wxgeometrie-0.133.2.orig/0000755000175000017500000000000012014170670015352 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/tools/0000755000175000017500000000000012014170666016517 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/tools/pytest.py0000644000175000017500000000755412014170666020434 0ustar georgeskgeorgesk"""py.test hacks to support XFAIL/XPASS""" # XXX this should be integrated into py.test # XXX but we can't force everyone to install py-lib trunk import sys try: # functools is not available in Python 2.4 import functools except ImportError: has_functools = False else: has_functools = True try: # tested with py-lib 0.9.0 from py.__.test.outcome import Outcome, Passed, Failed, Skipped from py.__.test.terminal.terminal import TerminalSession from py.test import skip USE_PYTEST = True except ImportError: USE_PYTEST = False def raises(ExpectedException, code): assert isinstance(code, str) frame = sys._getframe(1) loc = frame.f_locals.copy() try: exec code in frame.f_globals, loc except ExpectedException: return raise AssertionError("DID NOT RAISE") if not USE_PYTEST: class XFail(Exception): pass class XPass(Exception): pass class Skipped(Exception): pass def XFAIL(func): def wrapper(): try: func() except Exception: raise XFail() raise XPass() if has_functools: wrapper = functools.update_wrapper(wrapper, func) return wrapper def skip(str): raise Skipped(str) else: from time import time as now __all__ = ['XFAIL'] class XFail(Outcome): pass class XPass(Outcome): pass TerminalSession.typemap[XFail] = 'f' TerminalSession.typemap[XPass] = 'X' TerminalSession.namemap[XFail] = 'XFAIL' TerminalSession.namemap[XPass] = '*** XPASS ***' def footer(self, colitems): super(TerminalSession, self).footer(colitems) self.endtime = now() self.out.line() self.skippedreasons() self.failures() self.xpasses() self.summaryline() def xpasses(self): """report unexpectedly passed tests""" texts = {} for colitem, outcome in self.getitemoutcomepairs(XPass): raisingtb = self.getlastvisible(outcome.excinfo.traceback) fn = raisingtb.frame.code.path lineno = raisingtb.lineno #d = texts.setdefault(outcome.excinfo.exconly(), {}) d = texts.setdefault(outcome.msg, {}) d[(fn,lineno)] = outcome if texts: self.out.line() self.out.sep('_', '*** XPASS ***') for text, dict in texts.items(): #for (fn, lineno), outcome in dict.items(): # self.out.line('Skipped in %s:%d' %(fn, lineno+1)) #self.out.line("reason: %s" % text) self.out.line("%s" % text) self.out.line() def summaryline(self): outlist = [] sum = 0 for typ in Passed, XPass, XFail, Failed, Skipped: l = self.getitemoutcomepairs(typ) if l: outlist.append('%d %s' % (len(l), typ.__name__.lower())) sum += len(l) elapsed = self.endtime-self.starttime status = "%s" % ", ".join(outlist) self.out.sep('=', 'tests finished: %s in %4.2f seconds' % (status, elapsed)) # SymPy specific if self.getitemoutcomepairs(Failed): self.out.line('DO *NOT* COMMIT!') TerminalSession.footer = footer TerminalSession.xpasses = xpasses TerminalSession.summaryline = summaryline def XFAIL(func): """XFAIL decorator""" def func_wrapper(): try: func() except Outcome: raise # pass-through test outcome except: raise XFail('XFAIL: %s' % func.func_name) else: raise XPass('XPASS: %s' % func.func_name) if has_functools: func_wrapper = functools.update_wrapper(func_wrapper, func) return func_wrapper wxgeometrie-0.133.2.orig/tools/runtests.py0000644000175000017500000010424712014170666020770 0ustar georgeskgeorgesk# Copyright (c) 2006, 2007, 2008, 2009, 2010 SymPy Development Team # Copyright (c) 2010 Nicolas Pourcelot """ This is our testing framework. Goals: * it should be compatible with py.test and operate very similarly (or identically) * doesn't require any external dependencies * preferably all the functionality should be in this file only * no magic, just import the test file and execute the test functions, that's it * portable """ import os import sys import inspect import traceback import pdb from fnmatch import fnmatch from timeit import default_timer as clock import doctest as pdoctest # avoid clashing with our doctest() function from doctest import DocTestFinder import re as pre ROOT_PATH = '..' # WxGeometrie path def sys_normcase(f): if sys_case_insensitive: return f.lower() return f def convert_to_native_paths(lst): """ Converts a list of '/' separated paths into a list of native (os.sep separated) paths and converts to lowercase if the system is case insensitive. """ newlst = [] for i, rv in enumerate(lst): rv = os.path.join(*rv.split("/")) # on windows the slash after the colon is dropped if sys.platform == "win32": pos = rv.find(':') if pos != -1: if rv[pos+1] != '\\': rv = rv[:pos+1] + '\\' + rv[pos+1:] newlst.append(sys_normcase(rv)) return newlst def get_root_dir(): """ Returns the root directory and set the global value indicating whether the system is case sensitive or not. """ global sys_case_insensitive this_file = os.path.abspath(__file__) root_dir = os.path.join(os.path.dirname(this_file), ROOT_PATH) root_dir = os.path.normpath(root_dir) sys_case_insensitive = (os.path.isdir(root_dir) and os.path.isdir(root_dir.lower()) and os.path.isdir(root_dir.upper())) return sys_normcase(root_dir) def isgeneratorfunction(object): """ Return true if the object is a user-defined generator function. Generator function objects provides same attributes as functions. See isfunction.__doc__ for attributes listing. Adapted from Python 2.6. """ CO_GENERATOR = 0x20 if (inspect.isfunction(object) or inspect.ismethod(object)) and \ object.func_code.co_flags & CO_GENERATOR: return True return False def setup_pprint(): from sympy import pprint_use_unicode, init_printing # force pprint to be in ascii mode in doctests pprint_use_unicode(False) # hook our nice, hash-stable strprinter init_printing(pretty_print=False) def test(*paths, **kwargs): """ Run all tests in test_*.py files which match any of the given strings in `paths` or all tests if paths=[]. Notes: o if sort=False, tests are run in random order (not default). o paths can be entered in native system format or in unix, forward-slash format. Examples: >> import sympy Run all tests: >> sympy.test() Run one file: >> sympy.test("sympy/core/tests/test_basic.py") >> sympy.test("_basic") Run all tests in sympy/functions/ and some particular file: >> sympy.test("sympy/core/tests/test_basic.py", "sympy/functions") Run all tests in sympy/core and sympy/utilities: >> sympy.test("/core", "/util") """ verbose = kwargs.get("verbose", False) tb = kwargs.get("tb", "short") kw = kwargs.get("kw", "") blacklist = kwargs.get("blacklist", []) blacklist = convert_to_native_paths(blacklist) post_mortem = kwargs.get("pdb", False) colors = kwargs.get("colors", True) sort = kwargs.get("sort", True) r = PyTestReporter(verbose, tb, colors) t = SymPyTests(r, kw, post_mortem) test_files = t.get_test_files('.') not_blacklisted = [f for f in test_files if not any(b in f for b in blacklist)] if len(paths) == 0: t._testfiles.extend(not_blacklisted) else: paths = convert_to_native_paths(paths) matched = [] for f in not_blacklisted: basename = os.path.basename(f) for p in paths: if p in f or fnmatch(basename, p): matched.append(f) break t._testfiles.extend(matched) return t.test(sort=sort) def doctest(*paths, **kwargs): """ Runs doctests in all *py files in the sympy directory which match any of the given strings in `paths` or all tests if paths=[]. Note: o paths can be entered in native system format or in unix, forward-slash format. o files that are on the blacklist can be tested by providing their path; they are only excluded if no paths are given. Examples: >> import sympy Run all tests: >> sympy.doctest() Run one file: >> sympy.doctest("sympy/core/basic.py") >> sympy.doctest("polynomial.txt") Run all tests in sympy/functions/ and some particular file: >> sympy.doctest("/functions", "basic.py") Run any file having polynomial in its name, doc/src/modules/polynomial.txt, sympy\functions\special\polynomials.py, and sympy\polys\polynomial.py: >> sympy.doctest("polynomial") """ normal = kwargs.get("normal", False) verbose = kwargs.get("verbose", False) blacklist = kwargs.get("blacklist", []) blacklist.extend([ "sympy/thirdparty/pyglet", # segfaults "doc/src/modules/mpmath", # needs to be fixed upstream "sympy/mpmath", # needs to be fixed upstream "doc/src/modules/plotting.txt", # generates live plots "sympy/plotting", # generates live plots "sympy/utilities/compilef.py", # needs tcc "sympy/utilities/autowrap.py", # needs installed compiler "sympy/galgebra/GA.py", # needs numpy "sympy/galgebra/latex_ex.py", # needs numpy "sympy/conftest.py", # needs py.test "sympy/utilities/benchmarking.py", # needs py.test "doc/src/modules/polys", # very time consuming ]) blacklist = convert_to_native_paths(blacklist) r = PyTestReporter(verbose) t = SymPyDocTests(r, normal) test_files = t.get_test_files('wxgeometrie') # print test_files, 'voila!' # exit() not_blacklisted = [f for f in test_files if not any(b in f for b in blacklist)] if len(paths) == 0: t._testfiles.extend(not_blacklisted) else: # take only what was requested...but not blacklisted items # and allow for partial match anywhere or fnmatch of name paths = convert_to_native_paths(paths) matched = [] for f in not_blacklisted: basename = os.path.basename(f) for p in paths: if p in f or fnmatch(basename, p): matched.append(f) break t._testfiles.extend(matched) # run the tests and record the result for this *py portion of the tests if t._testfiles: failed = not t.test() else: failed = False # test *txt files only if we are running python newer than 2.4 if sys.version_info[:2] > (2,4): # N.B. # -------------------------------------------------------------------- # Here we test *.txt files at or below doc/src. Code from these must # be self supporting in terms of imports since there is no importing # of necessary modules by doctest.testfile. If you try to pass *.py # files through this they might fail because they will lack the needed # imports and smarter parsing that can be done with source code. # test_files = t.get_test_files('doc/src', '*.txt', init_only=False) test_files.sort() not_blacklisted = [f for f in test_files if not any(b in f for b in blacklist)] if len(paths) == 0: matched = not_blacklisted else: # Take only what was requested as long as it's not on the blacklist. # Paths were already made native in *py tests so don't repeat here. # There's no chance of having a *py file slip through since we # only have *txt files in test_files. matched = [] for f in not_blacklisted: basename = os.path.basename(f) for p in paths: if p in f or fnmatch(basename, p): matched.append(f) break setup_pprint() first_report = True for txt_file in matched: if not os.path.isfile(txt_file): continue old_displayhook = sys.displayhook try: out = pdoctest.testfile(txt_file, module_relative=False, optionflags=pdoctest.ELLIPSIS | \ pdoctest.NORMALIZE_WHITESPACE) finally: # make sure we return to the original displayhook in case some # doctest has changed that sys.displayhook = old_displayhook txtfailed, tested = out if tested: failed = txtfailed or failed if first_report: first_report = False msg = 'txt doctests start' lhead = '='*((80 - len(msg))//2 - 1) rhead = '='*(79 - len(msg) - len(lhead) - 1) print ' '.join([lhead, msg, rhead]) print # use as the id, everything past the first 'sympy' file_id = txt_file[txt_file.find('sympy') + len('sympy') + 1:] print file_id, # get at least the name out so it is know who is being tested wid = 80 - len(file_id) - 1 #update width test_file = '[%s]' % (tested) report = '[%s]' % (txtfailed or 'OK') print ''.join([test_file,' '*(wid-len(test_file)-len(report)), report]) # the doctests for *py will have printed this message already if there was # a failure, so now only print it if there was intervening reporting by # testing the *txt as evidenced by first_report no longer being True. if not first_report and failed: print print("DO *NOT* COMMIT!") return not failed class SymPyTests(object): def __init__(self, reporter, kw="", post_mortem=False): self._post_mortem = post_mortem self._kw = kw self._count = 0 self._root_dir = root_dir self._reporter = reporter self._reporter.root_dir(self._root_dir) self._testfiles = [] def test(self, sort=False): """ Runs the tests returning True if all tests pass, otherwise False. If sort=False run tests in random order. """ if sort: self._testfiles.sort() else: from random import shuffle shuffle(self._testfiles) self._reporter.start() for f in self._testfiles: try: self.test_file(f) except KeyboardInterrupt: print " interrupted by user" break return self._reporter.finish() def test_file(self, filename): name = "test%d" % self._count name = os.path.splitext(os.path.basename(filename))[0] self._count += 1 gl = {'__file__':filename} try: execfile(filename, gl) except (ImportError, SyntaxError): self._reporter.import_error(filename, sys.exc_info()) return pytestfile = "" if "XFAIL" in gl: pytestfile = inspect.getsourcefile(gl["XFAIL"]) disabled = gl.get("disabled", False) if disabled: funcs = [] else: # we need to filter only those functions that begin with 'test_' # that are defined in the testing file or in the file where # is defined the XFAIL decorator funcs = [gl[f] for f in gl.keys() if f.startswith("test_") and (inspect.isfunction(gl[f]) or inspect.ismethod(gl[f])) and (inspect.getsourcefile(gl[f]) == filename or inspect.getsourcefile(gl[f]) == pytestfile)] # Sorting of XFAILed functions isn't fixed yet :-( funcs.sort(key=lambda x: inspect.getsourcelines(x)[1]) i = 0 while i is not len(funcs): if isgeneratorfunction(funcs[i]): # some tests can be generators, that return the actual # test functions. We unpack it below: f = funcs.pop(i) for fg in f(): func = fg[0] args = fg[1:] fgw = lambda: func(*args) funcs.insert(i, fgw) i += 1 else: i += 1 # drop functions that are not selected with the keyword expression: funcs = [x for x in funcs if self.matches(x)] if not funcs: return self._reporter.entering_filename(filename, len(funcs)) for f in funcs: self._reporter.entering_test(f) try: f() except KeyboardInterrupt: raise except: t, v, tr = sys.exc_info() if t is AssertionError: self._reporter.test_fail((t, v, tr)) if self._post_mortem: pdb.post_mortem(tr) elif t.__name__ == "Skipped": self._reporter.test_skip() elif t.__name__ == "XFail": self._reporter.test_xfail() elif t.__name__ == "XPass": self._reporter.test_xpass(v) else: self._reporter.test_exception((t, v, tr)) if self._post_mortem: pdb.post_mortem(tr) else: self._reporter.test_pass() self._reporter.leaving_filename() def matches(self, x): """ Does the keyword expression self._kw match "x"? Returns True/False. Always returns True if self._kw is "". """ if self._kw == "": return True return x.__name__.find(self._kw) != -1 def get_test_files(self, dir, pat = 'test_*.py'): """ Returns the list of test_*.py (default) files at or below directory `dir` relative to the sympy home directory. """ dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0]) g = [] for path, folders, files in os.walk(dir): g.extend([os.path.join(path, f) for f in files if fnmatch(f, pat)]) return [sys_normcase(gi) for gi in g] class SymPyDocTests(object): def __init__(self, reporter, normal): self._count = 0 self._root_dir = root_dir self._reporter = reporter self._reporter.root_dir(self._root_dir) self._normal = normal self._testfiles = [] def test(self): """ Runs the tests and returns True if all tests pass, otherwise False. """ self._reporter.start() for f in self._testfiles: try: self.test_file(f) except KeyboardInterrupt: print " interrupted by user" break return self._reporter.finish() def test_file(self, filename): import unittest from StringIO import StringIO rel_name = filename[len(self._root_dir)+1:] module = rel_name.replace(os.sep, '.')[:-3] setup_pprint() try: module = pdoctest._normalize_module(module) tests = SymPyDocTestFinder().find(module) except: self._reporter.import_error(filename, sys.exc_info()) return tests = [test for test in tests if len(test.examples) > 0] # By default (except for python 2.4 in which it was broken) tests # are sorted by alphabetical order by function name. We sort by line number # so one can edit the file sequentially from bottom to top...HOWEVER # if there are decorated functions, their line numbers will be too large # and for now one must just search for these by text and function name. tests.sort(key=lambda x: -x.lineno) if not tests: return self._reporter.entering_filename(filename, len(tests)) for test in tests: assert len(test.examples) != 0 runner = pdoctest.DocTestRunner(optionflags=pdoctest.ELLIPSIS | \ pdoctest.NORMALIZE_WHITESPACE) old = sys.stdout new = StringIO() sys.stdout = new # If the testing is normal, the doctests get importing magic to # provide the global namespace. If not normal (the default) then # then must run on their own; all imports must be explicit within # a function's docstring. Once imported that import will be # available to the rest of the tests in a given function's # docstring (unless clear_globs=True below). if not self._normal: test.globs = {} # if this is uncommented then all the test would get is what # comes by default with a "from sympy import *" #exec('from sympy import *') in test.globs try: f, t = runner.run(test, out=new.write, clear_globs=False) finally: sys.stdout = old if f > 0: self._reporter.doctest_fail(test.name, new.getvalue()) else: self._reporter.test_pass() self._reporter.leaving_filename() def get_test_files(self, dir, pat='*.py', init_only=True): """ Returns the list of *py files (default) from which docstrings will be tested which are at or below directory `dir`. By default, only those that have an __init__.py in their parent directory and do not start with `test_` will be included. """ def importable(x): """ Checks if given pathname x is an importable module by checking for __init__.py file. Returns True/False. Currently we only test if the __init__.py file exists in the directory with the file "x" (in theory we should also test all the parent dirs). """ init_py = os.path.join(os.path.dirname(x), "__init__.py") return os.path.exists(init_py) dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0]) g = [] for path, folders, files in os.walk(dir): g.extend([os.path.join(path, f) for f in files if not f.startswith('test_') and fnmatch(f, pat)]) if init_only: # skip files that are not importable (i.e. missing __init__.py) g = [x for x in g if importable(x)] return [sys_normcase(gi) for gi in g] class SymPyDocTestFinder(DocTestFinder): """ A class used to extract the DocTests that are relevant to a given object, from its docstring and the docstrings of its contained objects. Doctests can currently be extracted from the following object types: modules, functions, classes, methods, staticmethods, classmethods, and properties. Modified from doctest's version by looking harder for code in the case that it looks like the the code comes from a different module. In the case of decorated functions (e.g. @vectorize) they appear to come from a different module (e.g. multidemensional) even though their code is not there. """ def _find(self, tests, obj, name, module, source_lines, globs, seen): """ Find tests for the given object and any contained objects, and add them to `tests`. """ if self._verbose: print 'Finding tests in %s' % name # If we've already processed this object, then ignore it. if id(obj) in seen: return seen[id(obj)] = 1 # Find a test for this object, and add it to the list of tests. test = self._get_test(obj, name, module, globs, source_lines) if test is not None: tests.append(test) # Look for tests in a module's contained objects. if inspect.ismodule(obj) and self._recurse: for rawname, val in obj.__dict__.items(): # Recurse to functions & classes. if inspect.isfunction(val) or inspect.isclass(val): in_module = self._from_module(module, val) if not in_module: # double check in case this function is decorated # and just appears to come from a different module. pat = r'\s*(def|class)\s+%s\s*\(' % rawname PAT = pre.compile(pat) in_module = any(PAT.match(line) for line in source_lines) if in_module: try: valname = '%s.%s' % (name, rawname) self._find(tests, val, valname, module, source_lines, globs, seen) except: pass # Look for tests in a module's __test__ dictionary. if inspect.ismodule(obj) and self._recurse: for valname, val in getattr(obj, '__test__', {}).items(): if not isinstance(valname, basestring): raise ValueError("SymPyDocTestFinder.find: __test__ keys " "must be strings: %r" % (type(valname),)) if not (inspect.isfunction(val) or inspect.isclass(val) or inspect.ismethod(val) or inspect.ismodule(val) or isinstance(val, basestring)): raise ValueError("SymPyDocTestFinder.find: __test__ values " "must be strings, functions, methods, " "classes, or modules: %r" % (type(val),)) valname = '%s.__test__.%s' % (name, valname) self._find(tests, val, valname, module, source_lines, globs, seen) # Look for tests in a class's contained objects. if inspect.isclass(obj) and self._recurse: for valname, val in obj.__dict__.items(): # Special handling for staticmethod/classmethod. if isinstance(val, staticmethod): val = getattr(obj, valname) if isinstance(val, classmethod): val = getattr(obj, valname).im_func # Recurse to methods, properties, and nested classes. if (inspect.isfunction(val) or inspect.isclass(val) or isinstance(val, property)): in_module = self._from_module(module, val) if not in_module: # "double check" again pat = r'\s*(def|class)\s+%s\s*\(' % valname PAT = pre.compile(pat) in_module = any(PAT.match(line) for line in source_lines) if in_module: valname = '%s.%s' % (name, valname) self._find(tests, val, valname, module, source_lines, globs, seen) def _get_test(self, obj, name, module, globs, source_lines): """ Return a DocTest for the given object, if it defines a docstring; otherwise, return None. """ # Extract the object's docstring. If it doesn't have one, # then return None (no test for this object). if isinstance(obj, basestring): docstring = obj else: try: if obj.__doc__ is None: docstring = '' else: docstring = obj.__doc__ if not isinstance(docstring, basestring): docstring = str(docstring) except (TypeError, AttributeError): docstring = '' # Find the docstring's location in the file. lineno = self._find_lineno(obj, source_lines) if lineno is None: # if None, then it wasn't really in this source return None # Don't bother if the docstring is empty. if self._exclude_empty and not docstring: return None # Return a DocTest for this object. if module is None: filename = None else: filename = getattr(module, '__file__', module.__name__) if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] return self._parser.get_doctest(docstring, globs, name, filename, lineno) class Reporter(object): """ Parent class for all reporters. """ pass class PyTestReporter(Reporter): """ Py.test like reporter. Should produce output identical to py.test. """ def __init__(self, verbose=False, tb="short", colors=True): self._verbose = verbose self._tb_style = tb self._colors = colors self._xfailed = 0 self._xpassed = [] self._failed = [] self._failed_doctest = [] self._passed = 0 self._skipped = 0 self._exceptions = [] # this tracks the x-position of the cursor (useful for positioning # things on the screen), without the need for any readline library: self._write_pos = 0 self._line_wrap = False def root_dir(self, dir): self._root_dir = dir def write(self, text, color="", align="left", width=80): """ Prints a text on the screen. It uses sys.stdout.write(), so no readline library is necessary. color ... choose from the colors below, "" means default color align ... left/right, left is a normal print, right is aligned on the right hand side of the screen, filled with " " if necessary width ... the screen width """ color_templates = ( ("Black" , "0;30"), ("Red" , "0;31"), ("Green" , "0;32"), ("Brown" , "0;33"), ("Blue" , "0;34"), ("Purple" , "0;35"), ("Cyan" , "0;36"), ("LightGray" , "0;37"), ("DarkGray" , "1;30"), ("LightRed" , "1;31"), ("LightGreen" , "1;32"), ("Yellow" , "1;33"), ("LightBlue" , "1;34"), ("LightPurple" , "1;35"), ("LightCyan" , "1;36"), ("White" , "1;37"), ) colors = {} for name, value in color_templates: colors[name] = value c_normal = '\033[0m' c_color = '\033[%sm' if align == "right": if self._write_pos+len(text) > width: # we don't fit on the current line, create a new line self.write("\n") self.write(" "*(width-self._write_pos-len(text))) if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty(): # the stdout is not a terminal, this for example happens if the # output is piped to less, e.g. "bin/test | less". In this case, # the terminal control sequences would be printed verbatim, so # don't use any colors. color = "" if sys.platform == "win32": # Windows consoles don't support ANSI escape sequences color = "" if self._line_wrap: if text[0] != "\n": sys.stdout.write("\n") if color == "": sys.stdout.write(text) else: sys.stdout.write("%s%s%s" % (c_color % colors[color], text, c_normal)) sys.stdout.flush() l = text.rfind("\n") if l == -1: self._write_pos += len(text) else: self._write_pos = len(text)-l-1 self._line_wrap = self._write_pos >= width self._write_pos %= width def write_center(self, text, delim="="): width = 80 if text != "": text = " %s " % text idx = (width-len(text)) // 2 t = delim*idx + text + delim*(width-idx-len(text)) self.write(t+"\n") def write_exception(self, e, val, tb): t = traceback.extract_tb(tb) # remove the first item, as that is always runtests.py t = t[1:] t = traceback.format_list(t) self.write("".join(t)) t = traceback.format_exception_only(e, val) self.write("".join(t)) def start(self): self.write_center("test process starts") executable = sys.executable v = tuple(sys.version_info) python_version = "%s.%s.%s-%s-%s" % v self.write("executable: %s (%s)\n\n" % (executable, python_version)) self._t_start = clock() def finish(self): self._t_end = clock() self.write("\n") global text, linelen text = "tests finished: %d passed, " % self._passed linelen = len(text) def add_text(mytext): global text, linelen """Break new text if too long.""" if linelen + len(mytext) > 80: text += '\n' linelen = 0 text += mytext linelen += len(mytext) if len(self._failed) > 0: add_text("%d failed, " % len(self._failed)) if len(self._failed_doctest) > 0: add_text("%d failed, " % len(self._failed_doctest)) if self._skipped > 0: add_text("%d skipped, " % self._skipped) if self._xfailed > 0: add_text("%d expected to fail, " % self._xfailed) if len(self._xpassed) > 0: add_text("%d expected to fail but passed, " % len(self._xpassed)) if len(self._exceptions) > 0: add_text("%d exceptions, " % len(self._exceptions)) add_text("in %.2f seconds" % (self._t_end - self._t_start)) if len(self._xpassed) > 0: self.write_center("xpassed tests", "_") for e in self._xpassed: self.write("%s:%s\n" % (e[0], e[1])) self.write("\n") if self._tb_style != "no" and len(self._exceptions) > 0: #self.write_center("These tests raised an exception", "_") for e in self._exceptions: filename, f, (t, val, tb) = e self.write_center("", "_") if f is None: s = "%s" % filename else: s = "%s:%s" % (filename, f.__name__) self.write_center(s, "_") self.write_exception(t, val, tb) self.write("\n") if self._tb_style != "no" and len(self._failed) > 0: #self.write_center("Failed", "_") for e in self._failed: filename, f, (t, val, tb) = e self.write_center("", "_") self.write_center("%s:%s" % (filename, f.__name__), "_") self.write_exception(t, val, tb) self.write("\n") if self._tb_style != "no" and len(self._failed_doctest) > 0: #self.write_center("Failed", "_") for e in self._failed_doctest: filename, msg = e self.write_center("", "_") self.write_center("%s" % filename, "_") self.write(msg) self.write("\n") self.write_center(text) ok = len(self._failed) == 0 and len(self._exceptions) == 0 and \ len(self._failed_doctest) == 0 if not ok: self.write("DO *NOT* COMMIT!\n") return ok def entering_filename(self, filename, n): rel_name = filename[len(self._root_dir)+1:] self._active_file = rel_name self._active_file_error = False self.write(rel_name) self.write("[%d] " % n) def leaving_filename(self): if self._colors: self.write(" ") if self._active_file_error: self.write("[FAIL]", "Red", align="right") else: self.write("[OK]", "Green", align="right") self.write("\n") if self._verbose: self.write("\n") def entering_test(self, f): self._active_f = f if self._verbose: self.write("\n"+f.__name__+" ") def test_xfail(self): self._xfailed += 1 self.write("f") def test_xpass(self, fname): self._xpassed.append((self._active_file, fname)) self.write("X") def test_fail(self, exc_info): self._failed.append((self._active_file, self._active_f, exc_info)) self.write("F") self._active_file_error = True def doctest_fail(self, name, error_msg): # the first line contains "******", remove it: error_msg = "\n".join(error_msg.split("\n")[1:]) self._failed_doctest.append((name, error_msg)) self.write("F") self._active_file_error = True def test_pass(self): self._passed += 1 if self._verbose: self.write("ok") else: self.write(".") def test_skip(self): self._skipped += 1 self.write("s") def test_exception(self, exc_info): self._exceptions.append((self._active_file, self._active_f, exc_info)) self.write("E") self._active_file_error = True def import_error(self, filename, exc_info): self._exceptions.append((filename, None, exc_info)) rel_name = filename[len(self._root_dir)+1:] self.write(rel_name) self.write("[?] Failed to import") if self._colors: self.write(" ") self.write("[FAIL]", "Red", align="right") self.write("\n") root_dir = get_root_dir() wxgeometrie-0.133.2.orig/tools/test.py0000755000175000017500000001221212014170666020051 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division, absolute_import, print_function, unicode_literals # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """Utilitaires et tests à effectuer avant de publier une nouvelle version de WxGéométrie. La suite de tests suivante est librement inspirée de Sympy. python test.py audit -> will run pyflakes checker on source code python test.py bench -> will run the complete benchmark suite python test.py clean -> will clean all trash (*.pyc and stuff) python test.py test -> will run the complete test suite """ import sys, os #from testlib import ROOTDIR, WXGEODIR _module_path = os.path.split(os.path.realpath(sys._getframe().f_code.co_filename))[0] ROOTDIR = os.path.abspath(_module_path + '/..') # /.../nom_du_projet/ WXGEODIR = ROOTDIR + '/wxgeometrie' SKIP_DIRS = ['sympy/', 'sympy_OLD/', 'sympy_old/', 'pylib/decorator.py', # already tested by sympy team. 'modules/OLD'] sys.path.insert(0, WXGEODIR) sys.path.insert(0, ROOTDIR) print(ROOTDIR) from wxgeometrie import param # Make sure I have the right Python version. if sys.version_info[:2] < param.python_min: print("Python %d.%d or newer is required. Python %d.%d detected." % \ (param.python_min + sys.version_info[:2])) sys.exit(-1) #TODO: use argparse once python 2.6 is deprecated for WxGéométrie. actions = {'u': 'audit', 'c': 'clean', 't': 'test', 'd': 'doctest', 'a': 'all', 'h': 'help'} args = sys.argv[1:] def audit(): """Audit WxGeometrie's source with PyFlakes. Audit WxGeometrie source code for following issues: - Names which are used but not defined or used before they are defined. - Names which are redefined without having been used. """ os.chdir(WXGEODIR) try: import pyflakes.scripts.pyflakes as flakes except ImportError: print("""In order to run the audit, you need to have PyFlakes installed.""") sys.exit(-1) print('\n == Auditing files... ==') warns = 0 for dirpath, dirnames, filenames in os.walk('.'): if not any((dirpath.startswith('./' + dir) or dir + '/' == dirpath) for dir in SKIP_DIRS): print('\nAuditing ' + dirpath + '...') print([('./' + dir, dirpath.startswith('./' + dir)) for dir in SKIP_DIRS]) for filename in filenames: if filename.endswith('.py') and filename != '__init__.py': warns += flakes.checkPath(os.path.join(dirpath, filename)) if warns > 0: print("Audit finished with total %d warnings" % warns) else: print("Audit finished without any warning. :)") def clean(): """Cleans *.pyc and debian trashs.""" os.chdir(ROOTDIR) os.system("py.cleanup --remove=.py~,.bak,.pyc,.txt~") def test(*args): "Run all unit tests." os.chdir(ROOTDIR) param.debug = False param.verbose = False from runtests import test test(*args, blacklist = SKIP_DIRS) def doctest(*args): "Run all doctests." os.chdir(WXGEODIR) param.debug = False param.verbose = False sys.argv = [sys.argv[0], '--defaut'] from runtests import doctest doctest(*args, blacklist = SKIP_DIRS) def all(*args): "Run all tests and doctests." test(*args) doctest(*args) def help(): print(u"""\n === Usage ===\n - Launch all unit tests: $ ./tools/test.py - Launch all doctests: $ ./tools/test.py --doctest - Launch all unit tests and doctests: $ ./tools/test.py --all - Clear working wxgeometrie directories: $ ./tools/test.py --clear - Audit code using PyFlakes: $ ./tools/test.py --audit Note that you may test only a few modules. For example: - Launch all unit tests concerning geolib: $ ./tools/test.py geolib - Launch all unit tests contained in geolib/test_objets.py $ ./tools/test.py objets """) sys.exit() # Actions are launched if args: if args[0].startswith('--'): action = args[0][2:] args = args[1:] elif args[0].startswith('-'): act = args[0][1:] args = args[1:] if act in actions: action = actions[act] else: help() else: action = 'test' if action in actions.itervalues(): locals()[action](*args) else: help() else: test() wxgeometrie-0.133.2.orig/tools/scriptlib.py0000644000175000017500000000632712014170666021074 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ###################################### # # Scriptlib # ###################################### # # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################### import sys, shutil, os, glob, subprocess _path_7zip = r"C:\Program Files\7-Zip\7z.exe" def norm(path): return os.path.realpath(os.path.normpath(os.path.expanduser(path))) def cp(src, dst): for pth in glob.glob(norm(src)): if os.path.isfile(pth): shutil.copy(pth, norm(dst)) elif os.path.isdir(pth): shutil.copytree(pth, norm(dst)) def mv(src, dst): for pth in glob.glob(norm(src)): shutil.move(pth, norm(dst)) def rm(*paths, **kw): quiet = kw.get('quiet', False) for path in paths: pths = glob.glob(norm(path)) if not (pths or quiet): print "Warning: %s not found, couldn't be removed." %path for pth in pths: if os.path.isfile(pth): os.remove(pth) def rename(src, dst): os.rename(norm(src), norm(dst)) def rmdir(*paths, **kw): quiet = kw.get('quiet', False) for path in paths: pths = glob.glob(norm(path)) if not (pths or quiet): print "Warning: %s not found, couldn't be removed." %path for pth in pths: if os.path.isdir(pth): shutil.rmtree(norm(pth)) def mkdir(path): os.mkdir(norm(path)) def cd(path): return os.chdir(norm(path)) def command(string, quiet=False): out = subprocess.Popen(string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout output = out.read() sys.stdout.write(output) out.close() if not quiet: print "Commande '%s' executee." %string return output def zip7(string): command('"%s" %s' %(_path_7zip, string)) def compil(script): command(sys.executable + ' -O ' + script + ' py2exe') def append(srcs, dst): with open(dst, 'wb') as dest: for src in srcs: with open(src, 'rb') as source: dest.write(source.read()) def pause(string = "\n-- pause --\n"): raw_input(string) def ls(path = '.'): if '*' in path: return glob.glob(path) return os.listdir(norm(path)) wxgeometrie-0.133.2.orig/tools/search.py0000755000175000017500000002202512014170666020342 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # Global search utility # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import sys import re import subprocess # ----- User config ----- IGNORE = ('*tmp_*', '*(OLD|BAZAR)*', '*sympy/*', '*modules/traceur/tableau.py', '*/(pyshell|idle).pyw') DEFAULT_EDITOR = 'geany' # XXX: move this outside the script # ------------------------ #TODO: provide default IGNORE by autodetecting .gitignore content, if any. patterns = filter(None, ('(' + pattern.replace('*', '.*').strip() + ')' for pattern in IGNORE)) IGNORE_RE = re.compile('|'.join(patterns)) SUPPORTED_EDITORS = ('geany', 'gedit', 'nano', 'vim', 'emacs', 'kate') def gs(chaine='', case=True, exclude_comments=True, extensions=(".py", ".pyw"), maximum=100, codec="latin1", statistiques=False, replace=None, color=None, edit_with=None): u"""Parcourt le rpertoire courant et les sous-rpertoire, la recherche des fichiers dont l'extension est comprise dans 'extensions', mais passe les rpertoires et les fichiers dont le nom commence par un prfixe de 'exclude_prefixe', ou finit par un suffixe de 'exclude_suffixe'. Pour chaque fichier trouv, renvoie toutes les lignes o 'chaine' se trouve. (Par dfaut, la casse est prise en compte, sinon, il suffit de modifier la valeur de 'case'.) Le nombre maximal de lignes renvoyes est fix par 'maximum', afin d'viter de saturer le systme. Si ce nombre est dpass (ie. toutes les occurences de 'chaine' ne sont pas affiches), la fonction renvoie False, sinon, True. """ if color is None: color = sys.platform.startswith('linux') if color: def blue(s): return '\033[0;36m' + s + '\033[0m' def blue2(s): return '\033[1;36m' + s + '\033[0m' def red(s): return '\033[0;31m' + s + '\033[0m' def green(s): return '\033[0;32m' + s + '\033[0m' def green2(s): return '\033[1;32m' + s + '\033[0m' def yellow(s): return '\033[0;33m' + s + '\033[0m' def white(s): return '\033[1;37m' + s + '\033[0m' else: green = blue = white = blue2 = red = green2 = yellow = (lambda s:s) if not chaine: statistiques = True if not case: chaine = chaine.lower() if replace is not None: assert case cwd = os.getcwd() repertoires = os.walk(cwd) print ("Searching in " + green(cwd) + "...") end_root_pos = len(cwd) + 1 print('') fichiers = [] for root, dirs, files in repertoires: files = [f for f in files if f[f.rfind("."):] in extensions] fichiers += [root + os.sep + f for f in files] # nombre de lignes de code au total N = 0 # nombre de lignes de commentaires au total C = 0 # nombre de fichiers F = 0 # nombre de lignes vides B = 0 # nombre de lignes contenant l'expression recherche n_lignes = 0 # Nombre d'occurences trouves. occurences = 0 for f in fichiers: if re.search(IGNORE_RE, f): continue F += 1 with open(f, "r") as fichier: lignes = [] results = [] for n, s in enumerate(fichier): if replace is not None: lignes.append(s) if statistiques: s = s.strip() if s: if s[0] != '#': N += 1 elif s.strip('#'): C += 1 else: B += 1 else: B += 1 continue if (exclude_comments and s.lstrip().startswith("#")): # comment line continue if not case: s = s.lower() pos = s.find(chaine) if pos != -1: if exclude_comments: substr = s[:pos] if '#' in substr: # test if the substring found was inside a comment # at the end of the line. # You have to be carefull, because `#` may be # inside a string... # TODO: handle triple quotes. mode = None for c in substr: if c in "'\"#": if mode is None: mode = c if c == '#': continue elif mode == c: mode = None if mode == '#': # substring found inside a comment continue occurences += 1 if replace is not None: lignes[-1] = s.replace(chaine, replace) s = s[:pos] + blue2(s[pos:pos+len(chaine)]) + s[pos+len(chaine):] results.append(u" \u25E6 line " + white(unicode(n + 1)) + ": " + s.decode(codec)) if edit_with is not None: if edit_with not in SUPPORTED_EDITORS: print(edit_with + ' is currently not supported.') print('Supported editors : ' + ','.join(SUPPORTED_EDITORS)) elif edit_with in ('geany', 'kate'): command = '%s -l %s %s' %(edit_with, n + 1, f) else: command = '%s +%s %s' %(edit_with, n + 1, f) subprocess.call(command, shell=True) n_lignes += 1 if n_lignes > maximum: return red("Maximum output exceeded...!") if results: print u"\u2022 in " + green(f[:end_root_pos]) + green2(f[end_root_pos:]) for result in results: print(result.rstrip()) if replace is not None: with open(f, 'w') as fichier: for l in lignes: fichier.write(l) if statistiques: # C - 20*F : on dcompte les prambules de tous les fichiers return (blue(str(N) + " lignes de code\n") + str(C) + " lignes de commentaires (" + str(C - 20*F) + " hors licence)\n" + str(B) + " lignes vides\n" + str(F) + " fichiers") if replace is None: return blue(u"\n-> %s occurence(s) trouve(s)." %occurences) else: return blue(u"%s occurence(s) de %s remplace(s) par %s." %(occurences, repr(chaine), repr(replace))) def usage(): print u"""\n === Usage ===\n - Rechercher la chane 'hello' dans le code : $ ./tools/search.py "hello" - Remplacer partout la chane 'hello' par la chane 'world': $ ./tools/search.py -r "hello" "world" """ exit() if __name__ == "__main__": args = sys.argv[1:] kw = {} if not args or args[0] in ('-h', '--help'): usage() if '-r' in args: i = args.index('-r') # L'argument suivant est la chane de substitution. if len(args) < i + 2: usage() kw['replace'] = args.pop(i + 1) args.pop(i) # on supprimer le '-r' if '-e' in args: args.remove('-e') kw['edit_with'] = DEFAULT_EDITOR if '-c' in args: args.remove('-c') kw['color'] = True if '-s' in args: args.remove('-s') args.insert(0, '') kw['statistiques'] = True options = (arg.split('=', 1) for arg in args[1:]) kw.update((key, eval(val)) for key, val in options) ##print kw title = "\n=== Recherche de %s ===\n" %repr(args[0]) if sys.platform.startswith('linux'): title = '\033[1;37m' + title + '\033[0m' print title print gs(args[0], **kw) wxgeometrie-0.133.2.orig/tools/testlib.py0000644000175000017500000000266212014170666020545 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random, randint as _randint from os.path import split, realpath, abspath import sys from wxgeometrie.param import tolerance as EPSILON _module_path = split(realpath(sys._getframe().f_code.co_filename))[0] ROOTDIR = abspath(_module_path + '/..') # /.../nom_du_projet/ WXGEODIR = ROOTDIR + '/wxgeometrie' def randint(a, b = None): if b is None: b = a a = 0 return _randint(a, b) def rand(): return randint(50) - randint(50) + random() def assertAlmostEqual(x, y): if isinstance(x, tuple) and isinstance(y, tuple): for x_elt, y_elt in zip(x, y): TEST = abs(y_elt - x_elt) < EPSILON if not TEST: print x_elt, "!=", y_elt assert TEST else: TEST = abs(y - x) < EPSILON if not TEST: print x, "!=", y assert TEST def assertNotAlmostEqual(x, y): # TODO: define test for tuple TEST = abs(y - x) > EPSILON if not TEST: print x, "==", y assert TEST def assertEqual(x, y): if x != y: print "ERREUR: ", x, " != ", y assert (x == y) assertEq = assertEqual def assertRaises(error, f, *args, **kw): try: f(*args, **kw) except Exception as e: assert type(e) == error else: raise AssertionError, '%s should be raised.' %type(e).__name__ wxgeometrie-0.133.2.orig/tools/__init__.py0000644000175000017500000000004712014170666020631 0ustar georgeskgeorgeskfrom search import gs import scriptlib wxgeometrie-0.133.2.orig/tools/release.py0000755000175000017500000001331212014170666020514 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement, print_function # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os, sys, time, re, types from optparse import OptionParser import scriptlib as s _module_path = os.path.split(os.path.realpath(sys._getframe().f_code.co_filename))[0] s.cd(_module_path + '/../wxgeometrie') parser = OptionParser(prog='release.py', usage="usage: %prog [options] release_number") parser.add_option("-o", "--output", dest="output", default='..', help="store output archive as FILE", metavar="FILE") parser.add_option("-m", "--message", dest="message", help="set release message to MSG (default: 'Version VERSION')", metavar="MSG") parser.add_option("-n", "--dry-run", action="store_true", dest="fake", default=False, help="simulate only") parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="don't ask for confirmation") (options, args) = parser.parse_args() if len(args) != 1: s.cd('..') sys.path.append(os.getcwd()) from wxgeometrie.param import version parser.error("fournir un (et un seul) argument (numero de version).\nVersion actuelle: " + version) version = args[0] def version_interne(version): version = version.replace("alpha", "-3").replace("beta", "-2").replace("rc", "-1").replace(".", " ").replace("_", " ") return [int(n) for n in version.split(' ')] def test_version(version): version = version.replace(' ', '_') reg='[0-9]+[.][0-9]+(([.][0-9]+([.][0-9]+)?)|(_(beta|alpha|rc)_[0-9]+))?$' if re.match(reg, version): return version sys.path.insert(0, os.getcwd()) # Option --dry-run if options.fake: for nom, val in s.__dict__.items(): if isinstance(val, types.FunctionType): setattr(s, nom, eval("lambda s, *args, **kw:print('@%s: ' + s)" %nom)) # Mise jour de la version et de la date dans param.__init__.py t=time.localtime() date = str((t.tm_year, t.tm_mon, t.tm_mday)) contenu = [] with open('param/version.py', 'r') as f: for line in f: if line.startswith('date_version = '): contenu.append('date_version = ' + date) elif line.startswith('version = '): version_precedente = line[11:].split('#')[0].strip()[:-1] # Changement du numro de version contenu.append('version = ' + repr(version.replace('_', ' ')) + '\n') else: contenu.append(line) # Quelques tests sur le numro de version: while True: modifier = False print('\n-------------------') print(u"Version prcdente: " + version_precedente) version = test_version(version) if version is None: print('Numero de version incorrect: ' + args[0]) modifier = True elif version_interne(version) <= version_interne(version_precedente): print('Les numeros de version doivent etre croissants: ' + args[0]) modifier = True else: print(u"Nouvelle version: " + version) if options.quiet: break rep = raw_input(u"Est-ce correct ? [y(es)/n(o)/(q)uit]") if not rep: continue if rep in 'yYoO': break elif rep in 'qQ': sys.exit() elif rep in 'nN': modifier = True if modifier: version = raw_input(u"Entrez un nouveau numero de version:") print(u'\nCration de la version ' + version + '...') if not options.fake: # Mise jour de param/version.py with open('param/version.py', 'w') as f: f.write(''.join(contenu).strip()) # Commit correspondant s.command('git add param/version.py') s.command('git commit -m %s' %repr('Version ' + version)) archive_tar = "wxgeometrie_%s.tar" %version archive_gz = archive_tar + '.gz' print(u'\nCration du paquet...') # Nettoyage (inutile, sauf plantage prcdent) s.cd('..') s.rmdir('build_', quiet=True) s.rm(archive_gz, quiet=True) # Cration d'un rpertoire temporaire build_/ s.mkdir('build_') s.mkdir('build_/wxgeometrie') # Cration du tag de release tag = 'v' + version s.command('git tag -am %s %s' %(repr(options.message or 'Version ' + version), tag)) # Rcupration des fichiers via git s.command('git archive %s -o build_/wxgeometrie.tar' %tag) s.cd('build_') s.command('tar -xf wxgeometrie.tar --directory wxgeometrie') s.rm('wxgeometrie.tar') # Personnalisation du contenu s.cd('wxgeometrie') s.rename('README.md', 'README') s.rm('MANIFEST.in') s.rm('.gitignore') s.rename('wxgeometrie/param/personnaliser_.py', 'wxgeometrie/param/personnaliser.py') s.cd('..') # Cration de l'archive .tar.gz s.command('tar -cf %s wxgeometrie' %archive_tar) s.command('gzip %s' %archive_tar) s.mv(archive_gz, options.output) print(u'\nPaquet cr dans %s.\n' %os.path.abspath(options.output)) # Nettoyage s.cd('..') s.rmdir('build_') wxgeometrie-0.133.2.orig/LICENSE0000644000175000017500000003564712014170666016403 0ustar georgeskgeorgeskCopyright (c) 2005, 2006, 2007, 2008, 2009, 2010 Nicolas Pourcelot WxGéométrie est distribué selon les termes ci-dessous: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS wxgeometrie-0.133.2.orig/INSTALL0000644000175000017500000000124512014170666016412 0ustar georgeskgeorgeskWxGeometrie =========== Plateformes supportées ---------------------- Windows XP, Vista, Seven GNU/Linux, FreeBSD MacOS X Prérequis ----------- Les programmes suivants doivent être déjà installés : Python : version 2.5, 2.6 ou 2.7 WxPython : version 2.6 ou 2.8 Matplotlib : version 1.0 Numpy : version 1.2, 1.3, 1.4 ou 1.5 Sous GNU/Linux Debian: `$ sudo apt-get install python-wxgtk2.8 python-matplotlib python-numpy` Installation ------------ WxGéometrie ne nécessite pas d'installation à proprement parler. Il suffit de lancer le script *wxgeometrie.pyw*. Au besoin, changer au préalable les permissions. Sous GNU/Linux : `$ chmod u+x wxgeometrie.pyw` wxgeometrie-0.133.2.orig/wxgeometrie/0000755000175000017500000000000012014170666017716 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/pylib/0000755000175000017500000000000012014170666021035 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/pylib/rapport.py0000644000175000017500000000536412014170666023106 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import time from ..pylib import uu, print_error from .. import param class Rapport(list): def __init__(self, fichier_log = None, frequence_archivage = 100): list.__init__(self) self.fichier_log = fichier_log self.frequence_archivage = frequence_archivage try: # Crer un fichier vierge. f = None f = open(self.fichier_log, 'w') f.write(time.strftime("%d/%m/%Y - %H:%M:%S") + '\n') f.close() except: # Impossible de crer le fichier (problme de permissions, etc.) self.fichier_log = None print_error() finally: if f is not None: f.close() def append(self, valeur): if param.debug: print '' print valeur print '' list.append(self, valeur) if len(self) > self.frequence_archivage: self.archiver() def extend(self, liste): list.extend(self, liste) if len(self) > self.frequence_archivage: self.archiver() def _contenu(self): u"Rcupre le contenu rcent (c--d. non archiv)." return '\n'.join(self) + '\n' def archiver(self): u"Copie les derniers enregistrements vers le fichier log." if self.fichier_log is not None: with open(self.fichier_log, 'a') as f: f.write(uu(self._contenu()).encode('utf8')) self[:] = [] def contenu(self): u"Rcupre le contenu complet, y compris ce qui a dj t archiv." if self.fichier_log is None: return self._contenu() else: self.archiver() with open(self.fichier_log, 'r') as f: return uu(f.read()) wxgeometrie-0.133.2.orig/wxgeometrie/pylib/decorator.py0000644000175000017500000002273412014170666023401 0ustar georgeskgeorgesk########################## LICENCE ############################### ## ## Copyright (c) 2005, Michele Simionato ## All rights reserved. ## ## Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## Redistributions in bytecode form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in ## the documentation and/or other materials provided with the ## distribution. ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ## DAMAGE. """ Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ __all__ = ["decorator", "FunctionMaker", "partial", "deprecated", "getinfo", "new_wrapper"] import sys, re, inspect, warnings from functools import partial DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') # basic functionality class FunctionMaker(object): """ An object with the ability to create functions with a given signature. It has attributes name, doc, module, signature, defaults, dict and methods update and make. """ def __init__(self, func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None): if func: # func can be a class or a callable, but not an instance method self.name = func.__name__ if self.name == '': # small hack for lambda functions self.name = '_lambda_' self.doc = func.__doc__ self.module = func.__module__ if inspect.isfunction(func): argspec = inspect.getargspec(func) self.args, self.varargs, self.keywords, self.defaults = argspec for i, arg in enumerate(self.args): setattr(self, 'arg%d' % i, arg) self.signature = inspect.formatargspec( formatvalue=lambda val: "", *argspec)[1:-1] self.dict = func.__dict__.copy() if name: self.name = name if signature is not None: self.signature = signature if defaults: self.defaults = defaults if doc: self.doc = doc if module: self.module = module if funcdict: self.dict = funcdict # check existence required attributes assert hasattr(self, 'name') if not hasattr(self, 'signature'): raise TypeError('You are decorating a non function: %s' % func) def update(self, func, **kw): "Update the signature of func with the data in self" func.__name__ = self.name func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) func.func_defaults = getattr(self, 'defaults', ()) callermodule = sys._getframe(3).f_globals.get('__name__', '?') func.__module__ = getattr(self, 'module', callermodule) func.__dict__.update(kw) def make(self, src_templ, evaldict=None, addsource=False, **attrs): "Make a new function from a given template and update the signature" src = src_templ % vars(self) # expand name and signature evaldict = evaldict or {} mo = DEF.match(src) if mo is None: raise SyntaxError('not a valid function template\n%s' % src) name = mo.group(1) # extract the function name reserved_names = set([name] + [ arg.strip(' *') for arg in self.signature.split(',')]) for n, v in evaldict.iteritems(): if n in reserved_names: raise NameError('%s is overridden in\n%s' % (n, src)) if not src.endswith('\n'): # add a newline just for safety src += '\n' try: code = compile(src, '', 'single') exec code in evaldict except: print >> sys.stderr, 'Error in generated code:' print >> sys.stderr, src raise func = evaldict[name] if addsource: attrs['__source__'] = src self.update(func, **attrs) return func @classmethod def create(cls, obj, body, evaldict, defaults=None, doc=None, module=None, addsource=True,**attrs): """ Create a function from the strings name, signature and body. evaldict is the evaluation dictionary. If addsource is true an attribute __source__ is added to the result. The attributes attrs are added, if any. """ if isinstance(obj, str): # "name(signature)" name, rest = obj.strip().split('(', 1) signature = rest[:-1] #strip a right parens func = None else: # a function name = None signature = None func = obj fun = cls(func, name, signature, defaults, doc, module) ibody = '\n'.join(' ' + line for line in body.splitlines()) return fun.make('def %(name)s(%(signature)s):\n' + ibody, evaldict, addsource, **attrs) def decorator(caller, func=None): """ decorator(caller) converts a caller function into a decorator; decorator(caller, func) decorates a function using a caller. """ if func is not None: # returns a decorated function return FunctionMaker.create( func, "return _call_(_func_, %(signature)s)", dict(_call_=caller, _func_=func), undecorated=func) else: # returns a decorator if isinstance(caller, partial): return partial(decorator, caller) # otherwise assume caller is a function f = inspect.getargspec(caller)[0][0] # first arg return FunctionMaker.create( '%s(%s)' % (caller.__name__, f), 'return decorator(_call_, %s)' % f, dict(_call_=caller, decorator=decorator), undecorated=caller, doc=caller.__doc__, module=caller.__module__) ###################### deprecated functionality ######################### @decorator def deprecated(func, *args, **kw): "A decorator for deprecated functions" warnings.warn( ('Calling the deprecated function %r\n' 'Downgrade to decorator 2.3 if you want to use this functionality') % func.__name__, DeprecationWarning, stacklevel=3) return func(*args, **kw) @deprecated def getinfo(func): """ Returns an info dictionary containing: - name (the name of the function : str) - argnames (the names of the arguments : list) - defaults (the values of the default arguments : tuple) - signature (the signature : str) - doc (the docstring : str) - module (the module name : str) - dict (the function __dict__ : str) >>> def f(self, x=1, y=2, *args, **kw): pass >>> info = getinfo(f) >>> info["name"] 'f' >>> info["argnames"] ['self', 'x', 'y', 'args', 'kw'] >>> info["defaults"] (1, 2) >>> info["signature"] 'self, x, y, *args, **kw' """ assert inspect.ismethod(func) or inspect.isfunction(func) regargs, varargs, varkwargs, defaults = inspect.getargspec(func) argnames = list(regargs) if varargs: argnames.append(varargs) if varkwargs: argnames.append(varkwargs) signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, formatvalue=lambda value: "")[1:-1] return dict(name=func.__name__, argnames=argnames, signature=signature, defaults = func.func_defaults, doc=func.__doc__, module=func.__module__, dict=func.__dict__, globals=func.func_globals, closure=func.func_closure) @deprecated def update_wrapper(wrapper, model, infodict=None): "A replacement for functools.update_wrapper" infodict = infodict or getinfo(model) wrapper.__name__ = infodict['name'] wrapper.__doc__ = infodict['doc'] wrapper.__module__ = infodict['module'] wrapper.__dict__.update(infodict['dict']) wrapper.func_defaults = infodict['defaults'] wrapper.undecorated = model return wrapper @deprecated def new_wrapper(wrapper, model): """ An improvement over functools.update_wrapper. The wrapper is a generic callable object. It works by generating a copy of the wrapper with the right signature and by updating the copy, not the original. Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module', 'dict', 'defaults'. """ if isinstance(model, dict): infodict = model else: # assume model is a function infodict = getinfo(model) assert not '_wrapper_' in infodict["argnames"], ( '"_wrapper_" is a reserved argument name!') src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict funcopy = eval(src, dict(_wrapper_=wrapper)) return update_wrapper(funcopy, model, infodict) wxgeometrie-0.133.2.orig/wxgeometrie/pylib/infos.py0000644000175000017500000001364012014170666022531 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # infos pylib # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode import sys import wx import matplotlib import os import platform import locale class dossier(object): def __init__(self, titre): self.__titre__ = titre def contenu(self): l = [] l.append("+ " + self.__titre__ + ":") if hasattr(self, "version"): l.append(" Version: " + self.version) for key in self.__dict__: if key not in ("__titre__", "version"): l.append(" %s: %s" %(key.capitalize(), getattr(self, key))) return "\n".join(l) + "\n\n" def informations_configuration(): dossier_os = dossier("Systeme") dossier_os.repertoire = os.getcwd() dossier_os.processeur = os.environ.get("PROCESSOR_IDENTIFIER", "?") dossier_os.version = platform.platform().replace("-", " ") if dossier_os.version.startswith("Windows"): dossier_os.distribution = "%s.%s build %s (%s) - %s" %tuple(sys.getwindowsversion()) # Il faut convertir en tuple sous Python 2.7 elif dossier_os.version.startswith("Linux"): try: f = open("/etc/lsb-release") s = f.read() f.close() dossier_os.distribution = " ".join(elt.split("=")[1] for elt in s.split("\n") if "=" in elt) except IOError: dossier_os.distribution = "?" except Exception: dossier_os.distribution = "#ERREUR#" # os.version = sys.platform # try: # __major__, __minor__, __build__, platform, __text__ = sys.getwindowsversion() # if platform is 0: # platform = "win32s" # elif platform is 1: # platform = "Windows 9x/ME" # elif platform is 2: # platform = "Windows NT/2000/XP" # else: # platform = "Unknown" # os.version += " %s version %s.%s %s build %s" %(platform, __major__, __minor__, __text__, __build__) # except AttributeError: # pass dossier_local = dossier("Localisation") dossier_local.langue = locale.getdefaultlocale()[0] dossier_local.encodage = locale.getpreferredencoding() dossier_python = dossier("Python") dossier_python.encodage = sys.getdefaultencoding() + " / Noms de fichiers: " + sys.getfilesystemencoding() dossier_python.version = sys.version dossier_python.executable = sys.executable # Pas tres utile : # dossier_python.api = sys.api_version # dossier_python.recursions = sys.getrecursionlimit() dossier_wxpython = dossier("WxPython") dossier_wxpython.portage = " ".join(wx.PlatformInfo[1:]) dossier_wxpython.version = wx.VERSION_STRING # Le code suivant provoque un Memory Leak de WxPython : # __p__ = wx.PlatformInformation() # dossier_wxpython.architecture = __p__.GetArchName() + " " + __p__.GetEndiannessName() # dossier_wxpython.os = "%s %s.%s" %(__p__.GetOperatingSystemFamilyName(), __p__.GetOSMajorVersion(), __p__.GetOSMinorVersion()) dossier_matplotlib = dossier("Matplolib") dossier_matplotlib.version = matplotlib.__version__ dossier_matplotlib.tex = matplotlib.rcParams["text.usetex"] if hasattr(matplotlib, "numerix"): # matplotlib <= 0.92 dossier_matplotlib.numerix = matplotlib.rcParams["numerix"] dossier_matplotlib.numerix += " (" + matplotlib.numerix.version + ")" else: # matplotlib 0.98+ dossier_matplotlib.numerix = "numpy" dossier_matplotlib.numerix += " (" + matplotlib.numpy.__version__ + ")" dossier_sympy = dossier("Sympy") try: import sympy dossier_sympy.version = sympy.__version__ except: dossier_sympy.version = "?" dossier_psyco = dossier("Psyco") try: import psyco dossier_psyco.version = ".".join(str(elt) for elt in psyco.version_info) try: import param if param.charger_psyco is False: dossier_psyco.utilisation = "unused" elif param.charger_psyco is None: dossier_psyco.utilisation = "profile" elif param.charger_psyco is True: dossier_psyco.utilisation = "full" else: dossier_psyco.utilisation = "inconnue" except ImportError: dossier_psyco.utilisation = "?" except ImportError: dossier_psyco.version = "Psyco non trouve." except Exception: dossier_psyco.version = "#ERREUR#" dossier_wxgeometrie = dossier("Wxgeometrie") try: import param dossier_wxgeometrie.version = param.version except Exception: dossier_wxgeometrie.version = "?" return (dossier_os.contenu() + dossier_local.contenu() + dossier_python.contenu() + dossier_wxpython.contenu() + dossier_matplotlib.contenu() + dossier_sympy.contenu() + dossier_psyco.contenu() + dossier_wxgeometrie.contenu()) wxgeometrie-0.133.2.orig/wxgeometrie/pylib/pylab_.py0000644000175000017500000000024012014170666022651 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) try: from pylab import * except: from numpy import * wxgeometrie-0.133.2.orig/wxgeometrie/pylib/bugs_report.py0000644000175000017500000000431212014170666023742 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode import urllib from .infos import informations_configuration from .. import param from .fonctions import uu def rapporter(titre = "", auteur = "", email = "", description = "", historique = "", log = ""): parametres = param.__dict__.copy() parametres.pop("__builtins__", None) parametres = "\n".join(str(key) + " = " + repr(val) for key, val in parametres.items()) data = { "version": param.version, "config": informations_configuration(), "titre": titre, "auteur": auteur, "email": email, "param": parametres, "description": description, "historique": historique, "log": log, } for key, value in data.items(): # data[key] = zlib.compress(uu(value.replace("\n", "\n
\n")).encode("utf-8"), 9).replace("\x01", "\x01\x03").replace("\x00", "\x01\x02") # php n'aime pas les caractres nuls dans une chane semble-t-il... data[key] = uu(value).replace("\n", "\n
\n").encode("iso-8859-1") try: f = urllib.urlopen("http://www.wxgeo.free.fr/bugs_report.php", urllib.urlencode(data)) if param.debug: print f.read() f.close() return True except IOError: return False wxgeometrie-0.133.2.orig/wxgeometrie/pylib/generic_wrapper.py0000644000175000017500000000334112014170666024564 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ########################################################################## # # Wrapper gnrique # ########################################################################## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Motivation: # http://docs.python.org/reference/datamodel.html#more-attribute-access-for-new-style-classes #TODO:this must wrap all special methods class GenericWrapper(object): slots = '__val' def __init__(self, val): object.__setattr__(self, '_GenericWrapper__val', val) def __getattribute__(self, name): val = object.__getattribute__(self, '_GenericWrapper__val') return getattr(val, name) def __setattr__(self, name, val): val = object.__getattribute__(self, '_GenericWrapper__val') return setattr(val, name, val) wxgeometrie-0.133.2.orig/wxgeometrie/pylib/fonctions.py0000644000175000017500000007373412014170666023427 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ########################################################################## # # Fonctions couramment utilisees, et non implementees en Python # (...du moins, ma connaissance !) # ########################################################################## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re import weakref import sys, zlib import os.path import warnings, traceback, linecache from .decorator import decorator from .. import param # paramtres du programme from sympy import sympify def is_in(element, _list): u"""Teste si l'lement est dans la liste, en effectuant un test d'identit (is) et non d'galit (==).""" test = False for elt in _list: if elt is element: test = True break return test # This is 'a lot' slower (2.4 times about) : ##def isin2(element, _list): ## u"""Teste si l'lement est dans la liste, en effectuant un test d'identit (is) et non d'galit (==).""" ## return id(element) in (id(elt) for elt in _list) # And this too... (2 times about on python 2.5) ##def isin3(element, _list): ## u"""Teste si l'lement est dans la liste, en effectuant un test d'identit (is) et non d'galit (==).""" ## return any(elt is element for elt in _list) def mreplace(main_string, list_of_strings, new_string = ""): u"""Remplace, dans "main_string", toutes les sous-chaines de "list_of_strings", par la chaine "new_string".""" for old_string in list_of_strings: main_string = main_string.replace(old_string, new_string) return main_string def recursive_replace(main_string, old_string, new_string = "", max_loops = 10000, max_len = 1000000): u"""Remplace, dans "main_string", la sous-chane "old_string" par "new_string", au besoin en plusieurs passes. En fin de processus, la sous-chane old_string ne subsiste plus dans la chane. Renvoie une erreur si le processus ne semble pas converger. (C'est en particulier le cas si old_string est strictement inclus dans new_string) La diffrence avec replace est claire sur cette exemple : >>> from wxgeometrie.pylib.fonctions import recursive_replace >>> "Hi HelloHello world world !".replace("Hello world", "") 'Hi Hello world !' >>> recursive_replace("Hi HelloHello world world !", "Hello world", "") 'Hi !' Il y a un cas particulier ou la sous-chane reste prsente : >>> recursive_replace("Hi HelloHello world world !", "Hello world", "Hello world") 'Hi HelloHello world world !' """ chaine = "" loops = 0 while chaine <> main_string: loops += 1 chaine = main_string main_string = main_string.replace(old_string, new_string) if loops > max_loops: raise RuntimeError, "Nombre de passes superieur au maximum autorise." if len(main_string) > max_len: raise OverflowError, "Taille de la chaine superieure au maximum autorise." return main_string def recursive_mreplace(main_string, list_of_strings, new_string = "", max_loops = 10000, max_len = 1000000): u"""Remplace, dans "main_string", toutes les sous-chaines de "list_of_strings" par "new_string", au besoin en plusieurs passes. En fin de processus, la sous-chane old_string ne subsiste plus dans la chane. Renvoie une erreur si le processus ne semble pas converger. Voir galement recursive_replace() et mreplace(). Remarque: recursive_mreplace n'est pas quivalent des appels successifs de recursive_replace(). >>> from wxgeometrie.pylib.fonctions import recursive_replace, recursive_mreplace >>> s = "tbtbaoao" >>> s = recursive_mreplace(s, ("to", "ba")) >>> s '' >>> s="tbtbaoao" >>> for i in ("to", "ba"): ... s = recursive_replace(s, i) >>> s 'tbtoao' """ chaine = "" loops = 0 while chaine <> main_string: loops += 1 chaine = main_string main_string = mreplace(main_string, list_of_strings, new_string) if loops > max_loops: raise RuntimeError, "Nombre de passes superieur au maximum autorise." if len(main_string) > max_len: raise OverflowError, "Taille de la chaine superieure au maximum autorise." return main_string def mfind(chaine, car): n = 0 l = [] while True: n = chaine.find(car, n) if n == -1: break l.append(n) n += 1 return l def msplit(main_string, list_of_separators): u"""Dcoupe la chaine "main_string", selon les sparateurs dfinis dans "list_of_separators".""" return mreplace(main_string, list_of_separators[1:], list_of_separators[0]).split(list_of_separators[0]) def removeend(main_string, *substrings): u"Enlve les ventuelles occurences de substring en fin de chaine." if substrings and True not in (sub == "" for sub in substrings): # pour viter une ventuelle boucle infinie. test = True while test: for sub in substrings: if main_string.endswith(sub): main_string = main_string[:-len(sub)] test = None if test is None: test = True else: test = False return main_string def removestart(main_string, *substrings): u"Enlve les ventuelles occurences de substring en dbut de chaine." if substrings and True not in (sub == "" for sub in substrings): # pour viter une ventuelle boucle infinie. test = True while test: for sub in substrings: if main_string.startswith(sub): main_string = main_string[len(sub):] test = None if test is None: test = True else: test = False return main_string def no_twin(liste): u"""Elimine les doublons dans une liste. Si tous les lements de la liste sont 'hashables', mieux vaut utiliser la fonction set.""" dico = {} for elt in liste:dico[id(elt)] = elt return dico.values() #def ntwin(l): return dict((id(elt), elt) for elt in l).values() # plus lgant, mais 50% plus lent ?!? def advanced_split(main_string, separator, keep_empty_str = False, symbols = "([{}])"): u"""Dcoupe la chaine "main_string" de manire intelligente, en ignorant les sparateurs compris dans un groupe entre parenthses, crochets, accolades, guillemets. Attention, separateur ne peut donc pas tre une parenthse, un crochet, une accolade ou un guillemet ! Par dfaut, supprime galement les chaines vides.""" in_string = False # est-on dans une chaine ? in_string_sep = "'" # caractere encadrant la chaine (" ou ') parentheses = 0 # tient le compte des parentheses ouvertes non fermees crochets = 0 # idem pour les crochets accolades = 0 # idem coupures = [-1] # endroits ou il faudra couper la chaine for i in xrange(len(main_string)): a = main_string[i] if a in ("'", '"'): if in_string: if in_string_sep == a: # attention, il y a 2 indicateurs de chaine (" et ') in_string = False else: in_string = True in_string_sep = a elif a in symbols: if a == "(" and not in_string: parentheses += 1 elif a == ")" and not in_string: parentheses -= 1 elif a == "[" and not in_string: crochets += 1 elif a == "]" and not in_string: crochets -= 1 elif a == "{" and not in_string: accolades += 1 elif a == "}" and not in_string: accolades -= 1 elif a == separator and not (in_string or parentheses or crochets or accolades) : coupures.append(i) coupures.append(None) # chaine[i:None] retourne la fin de chaine return [main_string[i+1:j] for i, j in zip(coupures[:-1], coupures[1:]) if main_string[i+1:j] or keep_empty_str] def regsub(regular_exp, main_string, action = ""): u"""Transforme la chaine "main_string" : Il applique aux parties vrifiant "regular_exp" le traitement "action". >>> from wxgeometrie.pylib.fonctions import regsub >>> regsub("[a-z]", "salut les amis !", "?") '????? ??? ???? !' >>> regsub("[a-z]+", "hello world !", lambda s: s[1:]) 'ello orld !' """ if isinstance(action, basestring): return re.sub(regular_exp, action, main_string) else: return re.sub(regular_exp, lambda x: action(x.group(0)), main_string) class WeakList(weakref.WeakValueDictionary): u"""Une 'liste' de rferences faibles. Le terme 'liste' est trompeur, la syntaxe des listes de python n'est pas implmente, excepte les mthodes append(), et remove(), et la conversion en liste. En outre, le contenu s'obtient par la mthode values(). Note: L'implmentation de remove est un peu diffrente : 'remove' utilise le comparateur 'is', et non "==", ce qui fait que remove([]) ne fera jamais rien, par exemple. (Il faut qu'il s'agisse du meme objet, et non d'un objet gal). Sinon, il faut utiliser compare_and_remove.""" def __init__(self): weakref.WeakValueDictionary.__init__(self) def append(self, valeur): u"Ajoute une valeur en fin de liste." self[max(self.keys() or [0]) + 1] = valeur def remove(self, valeur): u"""Supprime la valeur de la liste. Un test d'identit (et non d'galit) est effectu ('is' et non '=='). Si la valeur est prsente plusieurs fois, elle n'est supprime qu'une seule fois. Si la valeur n'est pas prsente, une erreur de type ValueError est mise. """ for key, value in self.items(): if value is valeur: del self[key] # il faut qu'il s'agisse du mme objet return raise ValueError, repr(valeur) + " is not in WeakList" def compare_and_remove(self, valeur): u"""Supprime la valeur de la liste. Un test d'galit est effectu ('==' et non 'is'). Si la valeur est prsente plusieurs fois, elle n'est supprime qu'une seule fois. Si la valeur n'est pas prsente, une erreur de type ValueError est mise. """ for key, value in self.items(): if value == valeur: del self[key] # un objet gal suffit return raise ValueError, repr(valeur) + " not in WeakList" def remove_all(self, valeur): u"""Supprime la valeur de la liste. Un test d'identit (et non d'galit) est effectu ('is' et non '=='). Toutes les occurences de la valeur sont supprimes. Si la valeur n'est pas prsente, aucune erreur n'est mise.""" for key, value in self.items(): if value is valeur: del self[key] # il faut qu'il s'agisse du mme objet def compare_and_remove_all(self, valeur): u"""Supprime la valeur de la liste. Un test d'galit est effectu ('==' et non 'is'). Toutes les occurences de la valeur sont supprimes. Si la valeur n'est pas prsente, aucune erreur n'est mise.""" for key, value in self.items(): if value == valeur: del self[key] # un objet gal suffit def __str__(self): return str(self.values()) + " (WeakList)" def __iter__(self): return self.itervalues() def __getitem__(self, n): return self.values()[n] def print_error(chaine = ''): u"""Affiche l'erreur sans interrompre le programme. C'est un alias de sys.excepthook, mais qui est plus souple avec les encodages. """ if chaine: print(chaine) typ, val, tb = sys.exc_info() tb = traceback.extract_tb(tb) print 'Traceback (most recent call last)' for fichier, ligne, fonction, code in tb: print ' File "' + uu(fichier) +'", line ' + str(ligne) + ', in ' + uu(fonction) if code is not None: print ' ' + uu(code) print uu(typ.__name__) + ": " + uu(val) print "Warning: this error was not raised." def rstrip_(s, end): u"""Supprime rcursivement 'end' de la fin de la chane 's'. >>> from wxgeometrie.pylib.fonctions import rstrip_ >>> rstrip_('blabla_suffixe_fixe_suffixe_suffixe', '_suffixe') 'blabla_suffixe_fixe' Nota : * ne pas confondre avec str.rstrip() : >>> 'blabla_suffixe_fixe_suffixe_suffixe'.rstrip('_suffixe') 'blabla' * si end == '', la chane de dpart est retourne : >>> rstrip_('bonjour', '') 'bonjour' """ if not end: return s i = -len(end) while s.endswith(end): s = s[:i] return s def split_geoname(name): u"""Tente de dcomposer un nom d'objet gomtrique en plusieurs noms. Ex: 1) "AB" -> ("A","B") 2) "A12B" -> ("A12","B") 3) "AB1" -> ("A","B1") 4) "A'B\"" -> ("A'", "B\"") 5) "ABC" -> ("A", "B", "C") """ return tuple(nom.strip() for nom in re.split("""([ ]*[A-Za-z][_]?[0-9"']*[ ]*)""", name) if nom) def convert_geoname(name, level = 0): u"""Convertit le nom entr par l'utilisateur en un nom rellement interprtable. Une conversion de niveau 1 est applique dans les botes de dialogue. Une conversion de niveau 0 est applique dans la console.""" if level > 0: if level > 1: if " " not in name: name = " ".join(split_geoname) name = name.replace('"', "''") name = name.replace("'''", "_tierce") name = name.replace("''", "_seconde") name = name.replace("'", "_prime") name = name.replace("```", "_tierce") name = name.replace("``", "_seconde") name = name.replace("`", "_prime") def split_around_parenthesis(main_string, position = 0, leftbracket = "("): u"""Coupe le premier groupe entre parentheses rencontr, en tenant compte des guillemets. 'leftbracket' peut prendre les valeurs "(", "[" ou "{" La parenthese ouvrante du groupe sera la premire trouve droite de 'position' Exemple: '1+4*(5+3*(2+7)+2-")")*7+(2-3)+4' -> ['1+4*', '(5+3*(2+7)+2-")")', '*7+(2-3)+4'] """ in_string = False # est-on dans une chaine ? in_string_sep = "'" # caractere encadrant la chaine (" ou ') position = main_string.find(leftbracket, position) if position is -1: return (main_string,) parentheses = 1 # tient le compte des parentheses ouvertes non fermees rightbracket = {"(": ")", "[": "]", "{": "}"}[leftbracket] prefixe = main_string[:position] chaine = main_string[position + 1:] for i in xrange(len(chaine)): a = chaine[i] if a in ("'", '"'): if in_string: if in_string_sep == a: # attention, il y a 2 indicateurs de chaine (" et ') in_string = False else: in_string = True in_string_sep = a elif a == leftbracket and not in_string: parentheses += 1 elif a == rightbracket and not in_string: parentheses -= 1 if parentheses is 0: chaine = chaine return (prefixe, leftbracket + chaine[:i + 1], chaine[i + 1:]) return (main_string,) # aucune parenthese fermante n'a t trouve pour ce groupe. def find_closing_bracket(expr, start = 0, brackets = '{}'): expr_deb = expr[:min(len(expr), 30)] # for debugging index = 0 balance = 1 # None if we're not presently in a string # Else, string_type may be ', ''', ", or """ string_type = None reg = re.compile('["' + brackets + "']") # ', ", { and } matched open_bracket = brackets[0] close_bracket = brackets[1] if start: expr = expr[start:] while balance: m = re.search(reg, expr) #~ print 'scan:', m if m is None: break result = m.group() i = m.start() if result == open_bracket: if string_type is None: balance += 1 elif result == close_bracket: if string_type is None: balance -= 1 # Brackets in string should not be recorded... # so, we have to detect if we're in a string at the present time. elif result in ("'", '"'): if string_type is None: if expr[i:].startswith(3*result): string_type = 3*result i += 2 else: string_type = result elif string_type == result: string_type = None elif string_type == 3*result: if expr[i:].startswith(3*result): string_type = None i += 2 i += 1 # counting the current caracter as already scanned text index += i expr = expr[i:] else: return start + index - 1 # last caracter is the searched bracket :-) raise ValueError, 'unbalanced brackets (%s) while scanning %s...' %(balance, repr(expr_deb)) def warning(message, type_warning = Warning, level=0): if param.warning: warnings.warn(message, type_warning, stacklevel = (level + 3)) def deprecation(message, level=0): warnings.warn(message, DeprecationWarning, stacklevel = (level + 3)) #def unicode2(string_or_unicode, encodage = None): # u"Convertit en unicode si besoin est, avec l'encodage de 'param.encodage' par dfaut." # if isinstance(string_or_unicode, str): # try: # return unicode(string_or_unicode, encodage or param.encodage) # except UnicodeDecodeError: ## try: ## print "chaine :\n", string_or_unicode ## print unicode(string_or_unicode, "cp1252") ## except Exception: ## pass # raise # elif isinstance(string_or_unicode, unicode): # return string_or_unicode # else: # try: # return unicode(string_or_unicode) # except UnicodeDecodeError: # print type(string_or_unicode) # raise def str2(string_or_unicode, encodage = None): u"Convertit en string si besoin est, avec l'encodage de 'param.encodage' par dfaut." if isinstance(string_or_unicode, str): return string_or_unicode elif isinstance(string_or_unicode, unicode): return string_or_unicode.encode(encodage or param.encodage) else: return str(string_or_unicode) def str3(unicode): dict = { 'a': (u'', u'', u'', ), 'e': (u'', u'', u'', u'', ), 'i': (u'', u'', ), 'o': (u'', u'', ), 'u': (u'', u'', u'', ), 'c': (u'', ), 'A': (u'', u'', u'', ), 'E': (u'', u'', u'', u'', ), 'I': (u'', u'', ), 'O': (u'', u'', ), 'U': (u'', u'', u'', ), 'C': (u'', ), } for key, liste in dict.items(): for item in liste: unicode = unicode.replace(item, key) return str(unicode) def universal_unicode(chaine): u"Convertit en unicode si besoin est, avec l'encodage de 'param.encodage' par dfaut." if not isinstance(chaine, basestring): chaine = unicode(chaine) if not isinstance(chaine, unicode): try: chaine = chaine.decode(param.encodage) except UnicodeError: try: chaine = chaine.decode('utf8') except UnicodeError: chaine = chaine.decode('iso-8859-1') return chaine uu = universal_unicode def path2(chemin): u"""Transforme le chemin en remplaant les / et \\ selon le sparateur utilis par le systme. % est remplac par l'emplacement du programme (contenu dans param.EMPLACEMENT). Exemple : path2("%/images/archives/old.png"). ~ fait rfrence au rpertoire personnel de l'utilisateur (ex: /home/SteveB/ sous Linux. """ return os.path.normpath(os.path.expanduser(uu(chemin).replace("%", uu(param.EMPLACEMENT)))) # L'ide de compiler en une fois pour toute les expressions regulires n'est pas avantageuse : le temps gagn ainsi est perdu rechercher les entres dans le dictionnaire. #~ def regsub(regular_exp, main_string, action = ""): #~ u"""Transforme la chaine "main_string" : #~ Il applique aux parties vrifiant "regular_exp" le traitement "action". #~ >>> regsub("[a-z]", "salut les amis !", "?") #~ '????? ??? ???? !' #~ >>> regsub("[a-z]+", "hello world !", lambda s: s[1:]) #~ 'ello orld !' #~ """ #~ if isinstance(regular_exp, basestring): #~ if isinstance(action, basestring): #~ return re.sub(regular_exp, action, main_string) #~ else: #~ return re.sub(regular_exp, lambda x: action(x.group(0)), main_string) #~ else: #~ if isinstance(action, basestring): #~ return regular_exp.sub(action, main_string) #~ else: #~ return regular_exp.sub(lambda x: action(x.group(0)), main_string) #~ class REStorageDict(dict): #~ u"""Un dictionnaire qui stocke les RE sous forme compile. #~ """ #~ def __getitem__(self, name): #~ try: #~ return dict.__getitem__(self, name) #~ except KeyError: #~ value = re.compile(name) #~ self.__setitem__(name, value) #~ return value class WeakRef(weakref.ref): u"""WeakRef surclasse weakref.ref en modifiant sa mthode '__eq__'. a == b <=> type(a) == type(b) == WeakRef and a() is b(). Le but est de ne pas appeler les mthodes __eq__ des objets rfrencs.""" def __eq__(self, y): if not (isinstance(self, WeakRef) and isinstance(y, WeakRef)): return False if self() is None or y() is None: return self is y return self() is y() class CustomWeakKeyDictionary(weakref.WeakKeyDictionary): """WeakKeyDictionary utilisant Weakref au lieu de weakref.ref. """ def __init__(self, dict=None): self.data = {} def remove(k, selfref=weakref.ref(self)): self = selfref() if self is not None: del self.data[k] self._remove = remove if dict is not None: self.update(dict) def __delitem__(self, key): del self.data[WeakRef(key)] def __getitem__(self, key): return self.data[WeakRef(key)] def __repr__(self): return "" % id(self) def __setitem__(self, key, value): self.data[WeakRef(key, self._remove)] = value def copy(self): new = CustomWeakKeyDictionary() for key, value in self.data.items(): o = key() if o is not None: new[o] = value return new def get(self, key, default=None): return self.data.get(WeakRef(key),default) def has_key(self, key): try: wr = WeakRef(key) except TypeError: return 0 return wr in self.data def __contains__(self, key): try: wr = WeakRef(key) except TypeError: return 0 return wr in self.data def pop(self, key, *args): return self.data.pop(WeakRef(key), *args) def setdefault(self, key, default=None): return self.data.setdefault(WeakRef(key, self._remove),default) def update(self, dict=None, **kwargs): d = self.data if dict is not None: if not hasattr(dict, "items"): dict = type({})(dict) for key, value in dict.items(): d[WeakRef(key, self._remove)] = value if len(kwargs): self.update(kwargs) def debug(*messages): u"""Affiche un (ou plusieurs) message(s) si le dboguage est actif.""" if param.debug: for message in messages: print(message) @decorator def trace(f, *args, **kw): if param.debug: print "Calling %s with args %s, %s" % (f.func_name, args, kw) return f(*args, **kw) @decorator def full_trace(f, *args, **kw): if param.debug: print '** Debugging info **' traceback.print_stack() print "Calling %s with args %s, %s" % (f.func_name, args, kw) print '-------------------\n' return f(*args, **kw) def deprecated(message = ''): "A decorator for deprecated functions" @decorator def _deprecated(func, *args, **kw): "A decorator for deprecated functions" warnings.warn('\n'.join(('La fonction %r est desuette.' %func.__name__, message)), DeprecationWarning, stacklevel = 3) return func(*args, **kw) return _deprecated ##@decorator ##def deprecated(func, *args, **kw): ## "A decorator for deprecated functions" ## warnings.warn( ## ('Calling the deprecated function %r\n' ## 'Downgrade to decorator 2.3 if you want to use this functionality') ## % func.__name__, DeprecationWarning, stacklevel=3) ## return func(*args, **kw) def traceit(frame, event, arg): u"""'Trace' (suit) une fonction python. Usage: import sys sys.settrace(traceit)""" if event == "line": lineno = frame.f_lineno filename = frame.f_globals["__file__"] if (filename.endswith(".pyc") or filename.endswith(".pyo")): filename = filename[:-1] name = frame.f_globals["__name__"] line = linecache.getline(filename, lineno) print "%s:%s: %s" % (name, lineno, line.rstrip()) return traceit def tracer_(booleen = True): if booleen: sys.settrace(traceit) else: sys.settrace(None) def property2(fonction): return property(fonction, fonction) # Permet de contourner un bug de exec() sous Python 2.5 lorsque with_statement est activ assert "with_statement" not in locals() assert "with_statement" not in globals() def exec_(s, globals, locals): exec(s, globals, locals) class CompressedList(list): def append(self, s): list.append(self, zlib.compress(s)) def __getitem__(self, i): return zlib.decompress(list.__getitem__(self, i)) def __setitem__(self, i, s): list.__setitem__(self, i, zlib.compress(s)) def remove(self, s): list.remove(self, zlib.compress(s)) def count(self, s): return list.count(self, zlib.compress(s)) def extend(self, iterable): list.extend(self, (zlib.compress(s) for s in iterable)) def index(self, s): list.index(self, zlib.compress(s)) def insert(self, i, s): list.insert(self, i, zlib.compress(s)) def pop(self, i = -1): return zlib.decompress(list.pop(self, i)) def pstfunc(chaine): args = [] dict_op = {'mul':'*','add':'+','exp':'**','div':'/','sub':'-'} dict_fn = {'ln':'ln'} def code_arg(s): return '(' + str(sympify(s)) + ')' for s in chaine.split(' '): if s in dict_op: args = [code_arg(dict_op[s].join(args))] elif s in dict_fn: assert len(args) == 1 args = [code_arg(dict_fn + '(' + args[0] + ')')] elif s: args.append(code_arg(s)) assert len(args) == 1 return args[0] class NoArgument(object): u'''Utilis comme valeur par dfaut, pour savoir si un argument optionnel a t pass. Une seule instance peut-tre cre.''' __instance = None def __new__(cls): if cls.__instance is None: cls.__instance = object.__new__(cls) return cls.__instance no_argument = NoArgument() class OrderedDict(dict): def __init__(self, seq = ()): self.__keys = [] dict.__init__(self) for key, val in seq: self[key] = val def __setitem__(self, key, value): if key not in self: self.__keys.append(key) dict.__setitem__(self, key, value) def __delitem__(self, key): dict.__delitem__(self, key) self.__keys.remove(key) def __iter__(self): return iter(self.__keys) def __repr__(self): return "MyOrderedDict(%s)"%repr(self.items()) def keys(self): return self.__keys[:] def values(self): return [self[key] for key in self.__keys] def items(self): return [(key, self[key]) for key in self.__keys] def copy(self): return self.__class__(self.iteritems()) def iterkeys(self): return iter(self) def iteritems(self): return ((key, self[key]) for key in self.__keys) def itervalues(self): return (self[key] for key in self.__keys) def update(self, E, **F): if hasattr(E, 'keys'): for k in E: self[k] = E[k] else: for (k, v) in E: self[k] = v for k in F: self[k] = F[k] def setdefaut(self, k, d = None): if k not in self: self[k] = d return self[k] def clear(self): del self.__keys[:] dict.clear(self) def pop(self, k, d=no_argument): try: v = dict.pop(self, k) self.__keys.remove(k) return v except KeyError: if d is no_argument: raise return d def __reversed__(self): return reversed(self.__keys) def popitem(self): if not self: raise KeyError('dictionary is empty') key = self.__keys.pop() value = dict.pop(self, key) return key, value wxgeometrie-0.133.2.orig/wxgeometrie/pylib/securite.py0000644000175000017500000002116612014170666023240 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ########################################################################## # # Gestion de la securite # ########################################################################## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import keyword, re, types import math # pour avoir droit aux fonctions mathematiques dans eval_restricted par defaut. import random as module_random# idem from ..pylib import advanced_split from .. import param def _avertissement(*arg, **kw): print u"Instruction interdite en mode securis." #fonctions_interdites = ["eval", "compile", "execfile", "file", "open", "write", "getattr", "setattr"] liste_blanche = set(('False', 'None', 'True', 'abs', 'all', 'any', 'basestring', 'bool', 'callable', 'chr', 'close', \ 'cmp', 'coerce', 'complex', 'dict', 'divmod', 'enumerate', 'filter', 'float', 'frozenset', 'globals', 'hash', 'hex', \ 'id', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'list', 'locals', 'long', 'map', 'max', 'min', 'object', 'oct', \ 'ord', 'pow', 'range', 'reduce', 'repr', 'reversed', 'round', 'set', 'slice', 'sorted', 'str', 'sum', \ 'tuple', 'type', 'unichr', 'unicode', 'xrange', 'zip'\ 'IndexError', 'SyntaxError', 'NameError', 'StandardError', 'UnicodeDecodeError', 'RuntimeWarning', \ 'Warning', 'FloatingPointError', 'FutureWarning', 'ImportWarning', 'TypeError', 'KeyboardInterrupt', \ 'UserWarning', 'SystemError', 'BaseException', 'RuntimeError', 'GeneratorExit', 'StopIteration', \ 'LookupError', 'UnicodeError', 'IndentationError', 'Exception', 'UnicodeTranslateError', 'UnicodeEncodeError', \ 'PendingDeprecationWarning', 'ArithmeticError', 'MemoryError', 'ImportError', 'KeyError', 'SyntaxWarning', \ 'EnvironmentError', 'OSError', 'DeprecationWarning', 'UnicodeWarning', 'ValueError', 'NotImplemented', \ 'TabError', 'ZeroDivisionError', 'ReferenceError', 'AssertionError', 'UnboundLocalError', 'NotImplementedError', \ 'AttributeError', 'OverflowError', 'WindowsError')) liste_noire = set(__builtins__.keys()) liste_noire.difference_update(liste_blanche) dictionnaire_builtins = {}.fromkeys(list(liste_noire), _avertissement) dictionnaire_modules = {}.fromkeys([key for key, objet in globals().items() if type(objet) == types.ModuleType]) ajouts_math = {"pi": math.pi, "e": math.e, "i": 1j} for key, obj in math.__dict__.items(): if type(obj) == types.BuiltinFunctionType: ajouts_math[key] = obj for key, obj in module_random.__dict__.items(): if type(obj) == types.BuiltinFunctionType: ajouts_math[key] = obj ##keywords = ('and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield') ### mots cls qui peuvent tre utiliss ##keywords_autorises = set('and', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'if', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield') keywords = set(keyword.kwlist) # mots cls dont il faut s'assurer qu'ils ne puissent PAS tre utiliss keywords_interdits = set(('exec', 'global', 'import')) # attention global ! # mots cls qui peuvent tre utiliss keywords_autorises = keywords.difference(keywords_interdits) ### ici, il s'agit de mots cls qui doivent rester en dbut de ligne ### (plus prcisment, il est impossible de rajouter devant une affectation, style 'variable = '). ##keywords_debut_ligne = ('break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'pass', 'for', 'if', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield') # Mots cls devant lesquels il est possible de rajouter une affectation, style 'variable = ...'. keywords_affectables = set(('lambda', 'not')) # Mots cls qui ne sont jamais en dbut de ligne. keywords_milieu = set(('or', 'and', 'as', 'in')) # Les autres, qui doivent rester en tte de ligne. keywords_non_affectables = keywords.difference(keywords_affectables).difference(keywords_milieu) def keywords_interdits_presents(chaine): return bool(re.search(r'(? 1 and s[0] == s[-1] and s[0] in ("'", '"'): # chaine # s est valu dans un contexte parfaitement vierge. return eval(s, {"__builtins__": None}, {"__builtins__": None}) if len(s) > 2 and s[0] == "u" and s[1] == s[-1] and s[1] in ("'", '"'): #unicode return eval(s, {"__builtins__": None}, {"__builtins__": None}) # idem if s == "None": return None if s == "True": # Eviter return eval(s), car True peut etre redefini. Exemple : True = False (!) return True if s == "False": return False if s[0] in ("(", "["): # tuple ou liste liste = [eval_safe(t) for t in advanced_split(s[1:-1], ",") if t] if s[0] == "(": liste = tuple(liste) return liste if s[0] == "{": # dictionnaire dict = {} liste = [advanced_split(t, ":") for t in advanced_split(s[1:-1], ",") if t] for key, val in liste: dict[eval_safe(key)] = eval_safe(val) return dict raise TypeError, "types int, str, float, bool, ou None requis (ou liste ou tuple de ces types)" wxgeometrie-0.133.2.orig/wxgeometrie/pylib/erreurs.py0000644000175000017500000001127312014170666023102 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Erreurs # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode # gnr par : # import exceptions;l = [" " + key + ": u\"\"\"" + val.__doc__ + "\"\"\"" for key, val in vars(exceptions).items() if not isinstance(val, basestring)];l.sort();print "messages = {\n" + ",\n".join(l) + "\n}" messages = { ArithmeticError: u"""Erreur de calcul.""", AssertionError: u"""Assertion failed.""", AttributeError: u"""L'attribut n'existe pas.""", BaseException: u"""Common base class for all exceptions""", DeprecationWarning: u"""Base class for warnings about deprecated features.""", EOFError: u"""Read beyond end of file.""", EnvironmentError: u"""Base class for I/O related errors.""", Exception: u"""Common base class for all non-exit exceptions.""", FloatingPointError: u"""Erreur de calcul (flottants).""", FutureWarning: u"""Base class for warnings about constructs that will change semantically in the future.""", GeneratorExit: u"""Request that a generator exit.""", IOError: u"""Impossible d'crire sur le priphrique.""", ImportWarning: u"""Base class for warnings about probable mistakes in module imports""", ImportError: u"""Module introuvable.""", IndentationError: u"""Indentation incorrecte.""", IndexError: u"""Indexage incorrect.""", KeyError: u"""Rfrence non trouve (cl).""", KeyboardInterrupt: u"""Program interrupted by user.""", LookupError: u"""Base class for lookup errors.""", MemoryError: u"""Manque de mmoire.""", NameError: u"""Nom inconnu.""", NotImplementedError: u"""Method or function hasn't been implemented yet.""", OSError: u"""OS system call failed.""", OverflowError: u"""Le rsultat est trop grand.""", PendingDeprecationWarning: u"""Base class for warnings about features which will be deprecated in the future.""", ReferenceError: u"""Rfrence non trouve (rfrence faible).""", RuntimeError: u"""Unspecified run-time error.""", RuntimeWarning: u"""Base class for warnings about dubious runtime behavior.""", StandardError: u"""Base class for all standard Python exceptions that do not represent interpreter exiting.""", StopIteration: u"""Fin d'itration.""", SyntaxError: u"""Erreur de syntaxe.""", SyntaxWarning: u"""Base class for warnings about dubious syntax.""", SystemError: u"""Internal error in the Python interpreter.""", SystemExit: u"""Request to exit from the interpreter.""", TabError: u"""Improper mixture of spaces and tabs.""", TypeError: u"""Type d'argument incorrect.""", UnboundLocalError: u"""Rfrence une variable non dfinie.""", UnicodeDecodeError: u"""Problme de dcodage (caractres spciaux).""", UnicodeEncodeError: u"""Problme d'encodage (caractres spciaux).""", UnicodeError: u"""Erreur unicode (caractres spciaux).""", UnicodeTranslateError: u"""Erreur unicode (caractres spciaux).""", UnicodeWarning: u"""Problme d'encodage.""", UserWarning: u"""Base class for warnings generated by user code.""", ValueError: u"""Valeur interdite.""", Warning: u"""Base class for warning categories.""", ZeroDivisionError: u"""Division par zro.""" } try: messages[WindowsError] = u"""MS-Windows OS system call failed.""" except NameError: # non dfini sous Linux par exemple pass def message(erreur): try: super(erreur) # si a marche, c'est une "classe", sinon, c'est une instance return messages.get(erreur, u"Erreur inconnue.") except TypeError: return messages.get(type(erreur), u"Erreur inconnue.") wxgeometrie-0.133.2.orig/wxgeometrie/pylib/__init__.py0000644000175000017500000000551212014170666023151 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .. import param # parametres du programme #from fonctions import * # librairie regroupant diverses fonctions "maison" #from infos import * # librairie servant a generer des infos sur la configuration (utile pour le debugage) from .fonctions import uu, is_in, WeakList, print_error, property2, str3, \ CompressedList, rstrip_, str2, no_twin, warning, \ CustomWeakKeyDictionary, debug, no_argument, path2, \ removeend, advanced_split, regsub, split_around_parenthesis,\ msplit, OrderedDict, find_closing_bracket # outils pour gerer la securite lors d'execution de code (tache delicate !) from .securite import eval_safe, eval_restricted #import bugs_report #import erreurs #import rapport from generic_wrapper import GenericWrapper from .decorator import decorator import matplotlib, matplotlib.mathtext matplotlib.rcParams['text.usetex'] = param.latex matplotlib.rcParams["text.latex.unicode"] = param.latex_unicode # A changer *avant* d'importer pylab ? matplotlib.rcParams['font.family'] ='serif' #matplotlib.rcParams['font.sans-serif'] ='STIXGeneral' matplotlib.rcParams['font.serif'] ='STIXGeneral' #matplotlib.rcParams['font.monospace'] ='STIXGeneral' matplotlib.rcParams['mathtext.fontset'] ='stix' import pylab_ as pylab # le fichier pylab_.py est modifie lors d'une "compilation" avec py2exe import numpy mathtext_parser = matplotlib.mathtext.MathTextParser("PS").parse def tex(txt): u"Rajoute des $ si l'expression LaTeX ainsi obtenue est correcte." try: mathtext_parser('$' + txt + '$') return '$' + txt + '$' except Exception: return txt def fullrange(a, b, pas): u'''Comme range(), mais avec des flottants, et contient en dernire valeur 'b'. quivalent de numpy.append(numpy.arrange(a, b, pas), b).''' return numpy.append(numpy.arange(a, b, pas), b) wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/0000755000175000017500000000000012014170666022433 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/0000755000175000017500000000000012014170666023547 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/distance_point_segment.odt0000644000175000017500000077453412014170666031030 0ustar georgeskgeorgeskPKs_:^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKs_:Configurations2/statusbar/PKs_:'Configurations2/accelerator/current.xmlPKPKs_:Configurations2/floater/PKs_:Configurations2/popupmenu/PKs_:Configurations2/progressbar/PKs_:Configurations2/menubar/PKs_:Configurations2/toolbar/PKs_:Configurations2/images/Bitmaps/PKs_:&:­­-Pictures/100000000000069800000489822DCE1E.pngPNG  IHDRCЈ pHYs.#.#x?vIDATxOL}?1Mlbc&ĝlX6!& Bb eӒ[6-V6%ZjբUTQT@%D8́8asS?03yD"/0|7牼4@@@@@@@@@@@@@@@@@@@@@@,..&IF)FKKKj +R233TP$hhh(( 4Hooo0+s{{[]D2۷#T(TD|>?==&''٬@#kuu3 \H;;;y]]]+w& fyhÇZ5P˼\.^p-"jWWW0H&JW/rRT, fyZՋyE2ROh42XjD*D"jR TTjpp0uuu)"\ȅ>;jW/Rc[捎f2Ka[[[y}}} !R#[Eѕ5|>P˼U(RS2(Rg fyߩPz=Q:\]]Ub]ϕ秧C-ݻe(Rg\^^9::RkZ2e^__r@u"Lftt4EE*D.OmW|O.SweP˼t:P|P˼M Ed26x<^(+re_P(̄1-\+zkkk===,/%I#g\L&y'r-_5͎i\׾w^4 fy###FB":;;ERTy˼D"al$妦B-fgg9x<j7::d Dh׻C-m.hL&C-EݻwHbeH:6fH#NOOO0+N@4 i@4!rP˼|>ohf9Е.-hO&:֝P˼m@ˋ4ݾ};vqq@"MwąBaff&MNNNZUI{}}=2opp0LQZRy=LM @4g26x<^( -$'P(fggCYޝ;w٬eDZ4Dggge5DZLC-:;;׍1- J'Bl߿4M-ztP722d 6+ҒgbX244H`0ˋF+++fisrw m2i3|a4 fyZ\"pۡy{{{fiLRy^4HjP{neT6:E[ z)"mx;;;,NV=Ohdd$EGyAИ"m{|~jj*2off~/ Muu+ i JD {zzY^WW8y'NZ=|P((@Bavv6MMMr9 >byy3  Kj2\#A)rDhy\Ac?>>V ,kkk]]],O< d2n-b2oaaAe2J=xe^6U 666Y^,S. bR@0\^^V. Nm7==K"ȫB4 fyoN*eUo{{;2gkkKe;A^M۷ClxPPHW|>P755%yj722rtt2ԅ nvvvz{{Y^wwP;A^=e2P˼h42HWgBazz:2orr2)U]P˼d22TGwYvwwY^WWƆPA%fcccmBAqAޥ{A(d2*@yWa}}+ ]X,P Ad2omyWP(ΆQ-8 $y*G2 +2]l6;11Q2/)!kFY@:VyokkG< kLfhh(6 }A^ӡ,ojjJ<5x<^2/L @5`Y|3A^#:::2A^rw eyZ'A^C[ZZ2yoww'2ommMeڊ d2-ڙ 9 P7::dJe^,2~, []]U&k>ǣmZ0A^yC666Y^ooΎA^sK&Ztj˼L&8KGjWWW0K&*py࠼eÇm.{ny˼l6WTYl'B@[bd2پS OB@Mvvvz{{NN@ty˼vy-P|~zz:2obbZ東ZvrѣGyT]3:AP#jR OTG@e26{ V*y@- _Pw^(˻sqk^Ey< .zggg0K&-x 'Q핷K$vU I\le\˴̻:;tBa~~>华f2Vy'pEDwww0k'R@d \T*łY^4]YYiG\YW*ݹs'ݻM2O\Y,ohh(N7@ \`ݽLOy  kL&B-bma,()שy[ +&E-,,KR{#"B!Z٠ r"ae^Çp z"H. eylp<rB4mؖy<ZhDݡyD/ey<lph{e^s7AP!+ϗoyWCG{ae^oomEG [__W}hcccm* AMP(ܻw/卌+y4`ŒɤW$r5>ҡh42u$ȃ*fdFFFBlgffũE3< J h^ h42/ɨ @ypWԶY^oo9<ɤm=.{֫R h=+++ o)h)VWWUh1ۅ =8TZÇA|A|f ݻw?0??GV?H$v~+J?d4 TZO<Dpjx2 OV)\XXXppoo/[|ޠ yhWz.cE^Spy88;;e"nuPUkgp0rpjjqK5 TZ%ݵP(*D"qa<|$ؚ?N¯rOP\]0(+ yhWzG݋y`tydNbT*uUmmmX˥Tkvv$ ^tCqm(Dgn~*4==z&//af9|>_ʤBҚD׸xPAZ!2A]J@Kh,sEyT͋Yl086[/#Ujm(m^*x‘ TZ>:{2X,G?eA4+{7/XhȠE `)@;;u`uݪ^*x5zkkk7nuﮊPGv>VSZ3xʻ y8xnsFb ?yP7+++[n޼L&Qbubq/⥧@3A=/RhO @:n$nU|=K5p7v޽FST^ZZ*^㋋ jT<zddD<E0b񠞃5n(۠p4]]]ccc+++Wx?*.^>PwB'믿n* 匞X56WXA K\s\WW׹Fo)\~:cg_qUmІFVK.Pl=99Y|eٺI2ЕR@/ȃKU߼y33ƍZ 7776811q~JI8XBkVR^NÇwwwz> F"|$Y4 8B/|B;1pjUŃm###'8)K5\YY ł67ꂼ7&ɲ/E"H"Hg]9A\-pcAXz`4(ԓdr\َکVth~yypES[moo+JkOP` Hx+ֶ熪sss}|$R83+-633-A\[n_^^Vz .nCeUqC- kllѣG&GGG͚*NNަYҡ= JrzԖy@:Vť k`)Оyp=vww?C?_x-E{bAkJ{V(qI&^Іypm/`/ήFpʻ O3ZUw6Zzb> : ~g{ZВP\yP<_C7_zE%hC<~-nݺe<TŃ>ַA^ ;fqmH awwST "hv>~y],㻻n)~(,ry I J<&wNq~W?::F'D  >\NX|T*e@= q~|W2WJkI:;;GGGWWWt077Wk1жyDqF|ӟ2*nxxxzzzuu5J |L6\whBA888xWB7n+T⣷x酅ړ_{_dOx'j %3A4l6{j˼{i:5]DF?lޔF777#9A4B0;;[37ްvүVwU(~ﭱoA4y===SVwR(qq1htff_/--)4L&366vFHhԏ޿8WIDM)MNNX={vo߾}pp@9A4nW;FxqJrt8 Fy{ܖ^6=uLSWEB@9A4?$X,V-,766 WvG]?z+-GyC޽OQR Zʍ7B3W_}U<>V8W.Ԡ$ŧ:ur^nqqQفAD"SO&XlooOq ܻw*9ByFR͛7C7nhUcF϶WCA{rof,H=̜xo. 'fz $_\\,oke2Mlnnq;mɣGNuu4.@ ?;;;/BhK/߯8L&s)^___}Аys/~-JBvxx800P 76NG*r@  Z:颳䙗xh4ZZ (rƍsX=Wnnn=I24"@ 8ny˼^<d>[cWCAOH$ P"Ndvtt,//+-Up~]govdw2vV}(X[[+oꫯi?Eӷ÷jٟ;88hAp_~94xgdbEӷ3xB` <"7'x\qhIBall4}'*bX)z54X'y1?'of6UZ\p_VH$"5K&8!.f}}g)c*RZjp;55unmmq[C?r߹s /H O~;677L&[br\飩Tj``Od2h F8!Q|.d{@Sf)nwww睈FǥY^^AN?-g|;2511[ІgNOO?:<<\ oppР'y@MDggghqM-hFYϟ;.m=88(.jOؔn 9APW^)m+Mdcc#ݹs-NZW#&N:8>>/p!M!LoG*B}]jcy@Wǯ~LFqhd\.x#ڮmoo/RWD Z__gCӎ_|qwwWqhXF-ϖN֖ݻ7===>>Wuc/.J} _<:::~_* (dnnP.K{{{DΝ;}n``cLNN&}Apw__i@CY__&hcccW3e)}ѥD:L$LMM޽{H ˳XG/Z K՞+NLLښ*!.'>P /h+ åijggޕ}5MM\/~-󖗗k477^W)}t:m8JK~y?Zw޽i'F[WcmmM<fpj:22rQQwwwBTN4-ok@2Loooi>s7(IR2R@y@#J$ZPB022P7()*'T*g>z)-8lpJV~y˼G hwwwK:99i muuƍ-x<>|x}}t7򀦷;;,Ύ .gr "p!<dٯ|+,޳%龾ܯ{~~t;%ZDPxw[MNNr9h۷KSh4,1Km pQ<=S,֭[dRqZLp,G5G*2VS{g,..gzx~``t|h"ZP&y7[yMmkk+xygg.Jx<|M-T& &:|>_:X,f@,Htvv7ojtpp^tStbL-.Jݺu+=SDB7XZZjS+Ԕ1.J|>oFq-j5t3NDfgg +pQ<]2_fR~ (.J>n*Rt||<00Puww6ϗNduu%˩-h"PFS(FFFӶETީh<~e&ݣЪŦ>ҹTAy_Wj~L&ckgg^eypdwwwiZ[vb wm-N-d .Z`#j>(Uq-ݻ{7Qq6<<#N W**T 4PAY^^.o/}I+8Ih <V-^|d28W#HDdlrre6IOt^ CL^ eyKKKpRTlenaatjxpU|D>˿yy'b t+|얗8PA)VWWzP788e%NZZ쀫!8/}x<t t Ӫ-]+J)/,,w~~k ͛7CY^4T3 .fggKeTGPBo~eLOONpSSSSN$^@uy5Y[[ w9}巇T!ô6zoo+ V&/'j\ NTGP7-ݻepsl6`z=U2 Ҟ7(IRsz&_~9hhvv*Y:q/ j8YXXPRA z,//GP799eФT[[[+P AI&/b({WWR .%|svvVAZS& eyhl68ɒRe\|>Z .033S4_-oi4Ƣ 'P/ykaa!8EQS핪4<< @y+;k-Kq벾.:44TBMMM)P#A@{'Bq޽{TznpRfgg  lll?cLP.F:;; xTj$hdW^ ey\T$HI[]]UFW%R R~,y@u2LF ]]]r!˥NOO+P;A@gV2b}}]M.* j'h) -xl68@ TQq&U-peeEA Z'>Pw֭t:8VWWhiqB,UjTv<f/'|׿g,FFFjåJ @y-wމyT8U6/MѨ-PA ZʓO>؟Bn`ݞP(~p0??&d2bvww+P<֗fQhss3x;wEJP.yw-o4\:fR=C?{g} < # k%Ř`RDbrfR'$`R38܃,9Zu 0Ɉ6sls'2ʍ317?Gn[_>yD?gw_?2z?ڷ|Y<.rĉ#ٳ,NZ-͆d2\0Ճ X<2??ƺ[J%@~Wpi|> Xp@բmr@բZ^^c]^:62@R><<XXEVs=YarrR8|۷o_ŲFFF"ւ"wsź}sjU8Xz}tt4|NRgϞKd0yEyyۑyp ;&HaJE jQp?ĺu?$MPHRKX paHh_92//7䘟 _wa FӔJ0l6+`)`Ǐuy~{V\RpQ*4333a###V"1??m۶XwW -O:}i4SPy\j:22:|pI0\.'`)$=ʑy'bd4ɓ'prÂ@UR t:򆇇 fZXXpEˍWm@U`u9Djm6#9p#333V"QV~/~}9"!lx!ԩRx≞XCA X#GNNNʤ#G| .Elfffƍ.#p`՝:uQTkd.ET*uyW^yܜp`---m޼9|ݾ}.Z]Px9٬@ե`MAo]92ꯄVر#|MάbxEFGG.Ek駟y{o㍇pI5BxE.Ek̙36luy_}R\\N&I]`(Xs7pC۴iә3g.FFFpuy4Co .`b:449 /MPy4ω'V̻;TV=baaA,1<<^YK@S5b]޵^(xW8qB&244^r,`u)h;wƺt:P8r\srrR&I&@AX]<Z^?+G}SNEuET*e2N@˼ +GݻZ J͛×;vxLe Q6yܖ-[b]5\>X8pNVpd*G&:EsrwƺF_% Lizz:LV"D׿rd<`]رc\.'y$ /nݺX722RՄ@w{n&Dx:E m۶XuƯ nSVo !p*V"djsO}@W _ Sԙ3gdp%s y$O?md,E_}Y$_& /YRy$TXuywuytT*߿_eikrUTvʹ9Щb\ڵZ-o Z&y$Zuy}}}w@YyE\K[ /@ }Z8#GD_q:6Ky~===:/-733g_+NRq}{[92odddyyY8$VlLD&YZZKT*K)`(B[nuy^{mׅ@耈t:mZ+^akD@cX72:p@رc2ikb1cccֈ"|eȼ'x!'NVՄ@;v,͆RX#<:_\^92w~wfff@4^qtk.a4+Ap=ĺgyR="8|А@#o U;"VUt|>^ƏEJ\L&酅t\.^ikG@Wc]M7TTũ###WSN9hEݮR޽; "<*޶T* X;<'=ʑyw$9}ttnȈ /͆W<kGwu{wbx<՗yJ֭[c]Ν;]-//GrE Z XS< jwwc]e]K/ w0::}pzi(OR XS<x=XOOO;zdx[ї\.'.1;;^1kJo_uyZM8D8q]P((pQo5??yXm۶r,Ldծ255^|>/`M)To=mذ<~ڵ+zܜXJ. @PyvdޗertĚ@5rԩ'ι뮻Z.e҅J%kJ\.uyW_}‚pͩSb\A .fe` kMVu].<= ݬq ֚".__# wݻwG0kA!kM̙3+GzFtÇ;sr5E\Rm۶Xe#:ѣGpAhvv6\ ccc֚".^^g?^xtgF޽{wZK7+ zyp&''߳yRl߾DpI4~,`)`9sfӦM.o3{FobQ,r9fRjrK|7:{st@3)`{wȼ|;h_>.x[###(J֚"Vً/Jbu?y#Q쀋l6 B 4&P+JW]uU۹soKZp188 BђW@X^^馛b]ƍ_}Uz><< ~  hE;Çw1$?ONNʄR.aM/[nȼ XсF333]!@kn~~>ĺk\.  p( "<hZwXxP8RT200P*J|'&S@E$KZ7ĺ+Kյ~\p&&&355%9y8zK_Ryg*b\<2ႌP(hE$3<ۻrd)hff&zpb4Mfgg4"knnZ92oyyY8T*m޼9... 544r,9yhj5ƺ7P.N>-.B:W':Q@￿'62駟K=*.BR WQ&4<h?O-2Jp!eYM1???00b\ٳ_p))\KFRĦ;5_'?pd@T P(i||\ @(ÇF_R###{ɓ'¥rrFmG?Ѻub۷'bprኚ4<hW Om۶zK8LOOGocccz],\B UX4<hcZm޽+G+(Jq׮]ۦXtk hE??uy===O=dnVTpZfj4MNϯO}jyyY8@w T2apuIh&7Ri֭.+|7tȑ#2aA.L&#y9jmRBq o`pu  h&EtƛՕ#&&&A쀋۷/--U4;;4":+qXя~<A{788 VWP=@fR@gT*c]ަM^ujћ^X njj*\cI+;3}yb\hXX#\.\fSSSIfOOO72$gΜpO]5x '*<|3336luypиm߾=zER kdtt4\lI]v׮]./NkZVf-JVXS2Lݢ^8p |4}lf2Lޤ4t}}}:o߾}Ah;O>dlLXSpe2M377wǺk歷FΞ==b޽ZM,r.aMnTTX~'͛\8& Wؘ@&S@ʑyO #$޵=*cjj*\x|^ @)ەJ*e2E +OELڛd<c]^(@ΦN{n\L+Lz~}g{L8@B,-- 7͛7W*L ,JL>r@AиENЋ#|<7[ƺ+rnnN8@ GKG ͗dέt:- y@\V;b]޺uZ8@K;v,zG:pLh[144$ w!o'y^L (VU|J%:wB @)^c]7l*4juǎ-hpppqqQ,l4"x'KKKc]ަMQ8ZCBpAkp)NLLh>E. {c]^__߷mkGy|IBSSSj|<|߽@8ZpgF'599.ˢ@S7\92oΝFT*m޼$D&Ţ@SR|^92W_Z]›.HYͧ.؃>?Vž}wӧO˄$f4:<bLOORأy`dpq\LL&\ZHo-[b]АOX,F`dd$Dd2ZB\һ[o5.ÛɎ;BBpq h Epy䑕#~i/ݻwGJa$JcAsttT @K(U/_>hw]ՄGo BA&$JcMs||\ @K(Q*Sٺukׅ#GDo\N&$Ԕ% "X5Au].oF`ff$_. WԔ@POOc#}Q+U*LfiiI,$~-V+aÆXwDAw^\FGGõjD\~{߻rdps&&&iXl6\8-JYnɓ';v,zs I=Щ'%yzgb{޽{pAA(Uy{W7n>OUU@pXHrl6+Uy@޷Cuy\s‚p>JΜ9#nvv6\ZE4I;bjjJ&$_P@VQM|/Vw}cA78qD*rm'χ6 hEl!zFAt:={8v¥"hJ8mڴ_tl6%.[("h/}K.矗 t\EF xffF @(VzgV>WՄ#vő#GdB{>OZ*"h7|sͱ.}{_RtǏGwؘmh;f;-ZZtM.oƍ?OmT*EfAt:-y@R<===:wMU*>H;jh-y@?_92瘠ݻ7O:%LGFF"H-[ĺn\. ȓO>ōʄ6U(C"H XN ©SRT3|>.\.'y@B=+G=cHpر\.|>/y@r ֭uyw}R=w :x @Rm۶Xd.Hn=}LU=33#y@X5аnݺ'DygtbbB&tl6jOSO=O>id$DXp122b{2L B<m?b}_^^@ At@R\.oٲ%  ZV޽;zXםpmgYLV;c]^*z-166ݏ2cΆk{ttT @k)o}/V=frAMMM9V(=>>.y@߸qcG?j24ӧpAgVչ\N @k)6VTnXw5הJ%Z+˃MNN|jjJ @k)F?~\2v޽{pAǛyPZ<أy_jp`-;n0::Y:DUS~|#T*uF;tLTR/ZKtjz-ĺW_}U8ZΞ=N\xd@R^:t(62رcKWT=Jg/ 7%ɓ֭=ݿAяRgϊVTf"Lry˖-.n\ >Llr<cUoqȼ uV:pG\xb1\ZNtzkx駅ovv6zŞ={pA7 }.r<MOORX7::QTo$d򧦦" R骫uy[n-wPGFF\E%s!| y@jGc]ކ ^z%o裏F̱cdB @SG]92G1V:qDcccc2 ߡ@(K/mذ!}FA`GvUVBW 4$p3Ҷmb]5\o;100 MxXs&<KA0::~bP(ȄnST-0<<, y@W{ꩧ̃\. ]T*`ttT @(n|ȼniyyY8t'OF8x^T,Í011! y^^^f.磌22nS*\4C`Z^<R׿/]VE큁R$V>Ԕ@$P[Xh!`߾}ѕI&&&P,$"7_~+GU*&''kG ]nll,@WTV̻+~_ t{ @,tpSeIx{{ow#:L쀋L&p_PVǏ_92o߾}F1 صkWŠX!N4PJ\FSN8!?XE6<w|7ƺ 6¡r2sfggí1::* !y^?C===y=phSgϞ ?9 )<wwGZ&K\޾}{􀋥%@hjjê@).UW]njdm^ٳ'zٳgQ< Sn#^8CEWVHP<cg}k!=]cccz],3::n H?я֭[.#H{\m ;T* HE[\\b]ޖ-[CT*\yjlp8HE%V{uy/pHz}(JEo}/4ݝDp'V=æ.cǎ~Jnl6+ 9yԩSׯuy{V¡ BtYl_FFF$"`ՔJ#r,Ze~~~`` JX,FOv<T>ĺu?-Y;vpQTljj*5\N @r(V__G۷/pcrr25|^ @r(믿<9vph|>]~SSS2111nB  9ykZx㍱.opp7kX,R)\E ̌@P/~.oo$Y\\d2fNO2)JC{#>#.EXjڮ]\87.Hw> (<f۴iS˻='u)F.MT.BAnt:- QyMRV?Ǻ+-3r:/i Z'pAr$"xl?}$sFVC'rԩccc> jvv6DElw{4K_d.qJ%\+ 6\ @(ZT*uyЇs."/tzaa 055n hZ;!JK9ORuXwB:u'˅[) HE@+}+_Y92%s>."gup7MOO HE@ׯ=V )Μ9=8.EYb( QyW.W۲epށ"RX^^0R4QJ@DQ$B^߻woK?τtyX3-+@DQ$7#/KmuywR9q%(h9. iy+lܸ1h'> #V"ozzr$" qXd|MD]J-.`Ά;kxxX @(^{ア./J .MUW*۷G2 VKn1IHs___{衇<~uEq#+ח={pk$χ+ HE@W\qyokd5ח<11] Ӗ\.|>/ iyI}͛7[]LxSSS5011aw Q@)ã>ws&Sg:IhNJRG>wm%ޯ LlJ@Q˫*tMY{=wz)t:A!X# $"jO~./7-.)}Q\@Am$"-b]^__߷ 'OF?O=>>n)J^fHЮ֭y32㋼v]V|X#333d$"뮻.ܹwvWTpMS(766& y^u].oZ]䍎FiXaMpr9 O/ yg?e]{4ﳟl%wj7==랹Bccc+ H9*X744ؑ_oGyRipp0zt.ipH E@G 3L|aE^쀋#͆T* H E@׿ +b\>}bI@[St_ׯ=O}VuaEdb=䓖14MR w_&L<cǎXu;+ T*R):R @2):\s6l/w1E^쀋]v-//[Lb1܃IF>|3_IݻKNM6==nqɤ @ѼNצ(26rI-j77|S8;СC2wԔ@dRt|0 4L -qp3%:?|?:OO$LRia۷o/bV?YɤFofs##vE*R@kڵ+ܒZu y]Vx㍱.oppȼ&~1@kEnÇ܁Rt~8Rډ H -d$"?VK===>dB쀋g[ZZ we6X<~_w~wb5V*ᬢrd\H`~~>Z H,E$[o5mܸ_j% H'O{sllL @b)W>`OOO흞̥pqq@B4rrKoxׯ_{4 _B^Eya@r9r$ܞ|^ @b)+JW_}ʑy¹+@, whPX<Fwuʑy\J=㥥%@8p ܤ333Kob#?7)H4b(HA4RI @b)x'J:=O|‡CG쀋#Gh`` ܧnn@)xJe.}{_ׅ`68p!7"NKHJ FGGc]e]?ۚ ڵkWZ $RU$"3F%;b``-HgφuxxX @)ʑyGkpBѧSӧeU, ;66& y\JcǎXwWWί_\@ 7Ą@$Sp طo_[~~.O&vؘ. &''=$"tرȼ[ T*cHÇvzzZ @)x/6l{4oΝEZ CHV$_teXd<.IRf.opp7ǣ  k޽{Ýi y??uy}}}!|>ڍق6}RH2EcȼÇwi'OLROFN$"US*l>OTN\ u|@h_St H8Eo\92^/Vڵ+2Sٳgh#sssp<V<z{{owؗ966}YKP@S&ovȼ{キcx]th;ǏwĄ@SVJ5\"wt:D^8p#7~, y Ε#~_d/ƯЎ:婩) `}_^92駟nǯ%{FqM8p bQ @)h3gkny/#<ֆ  y4I\kc]PRi/T*߿cɧyc]^:?@޻w.G6k&O?F?$|dۣu,..*J7o, yoqW'?g}pnnEv7;;aɧ5>Xwu%pd^.!}Y:@X }$"_c]^!\4ajj*ڏ?@Sb/b);'%V,tw199|>/ yێ̻kZ TJeg``I&&& ~qɧ ]b]͛-AgϞOJŢdtt4O|<^?#.oݺu?LQf=y[-(H_|qݺu:n!:Γdm^V$"y7V̛ vj>`-A@ ꭷ_kݾ}.p7v@ կDk###чt̿N5;;n{ h <G?QѼ??Zu\% Bt@ -[b]_ST?x.Eϴ9|@ |3.oӦM52T*Ef}ȑ#ڂ"=62wbPxg'''=:==jZ6pQ* m||<ǏyX~}Ѽ/~+?[V=k׮fCL&˕?y ?s@,-- ŊƯT*9znRm\.'apcO$"}.mذsQFGG] 2LE*ϯsX=/j`oo+fD/]a~oܸ=a~co;vܙNcܜHpo߾] @Pjڇ>-o;~ܠ=A7 7@vڅ"N0>>-߲eȅ;r<5y''' Em/hwA=6Ɉ:X?.](h{sss{O~ZqdaaA>bQ @P?'G.`/*tl6yB@;rH뮻"ѣG ,aJ"](h{{򡡡+{A=N h#<ڞ'W*͞fEmό<̄Emϩ+ fFy o -vL&#R`|>\N @Q w[l=7{<|^ @Q Ξ= z(/Ʉ1JJ%yBBA @Q!v9_n>i>DzKFGG-?33#(T*|~n?Q}/iӦ>~_Yܹ3:/"OH?mD@͝s_wuCCC3}}}_ܷo7a@Ri< 144477WV?_ \.< h/<:ѣG7o^*:t֯_Xfggڋ"TV|t}T&yWƧ~:۞/:@P@-..8qg瀕//[B?d`J0ڑ"VPR#]ޝwYVi||<<GGG#E oE, ڑ"V{zz"#~IVG>R$)` "e +-˅]\Ўyz̻fgg+*Ʉ̌@vUSO=O~IW#E4oH$_|Q2BGZ*Цy~___ּr,Jeڔ"V5 o2 W\nmJM{Hn:#`JЦy|㑑y{9( 522"M)%tM. @8pFGGêP(hS<hZ_r~&|><Ţ@63D5k֨jdЦyr~,}O24===-M)?>JEᄏZ hڔ"ZT7"] 7pi.}_2g3@+;|d U*оy}Gn۱cG*344$}) m޼9 U*£& h_<h?p˻k'''%P(CfddD @R@;˿˞.oۗ^zI2iFGGellL @R@ȭyַp`|>)bQ @R@j_#]ޯV*@D6  K_827ސ 4iKm֬YuwwRTxkhk<ho?nȭy?pzdR @[S@ tygΝ;':LxPimM1GD"oKNV.#"hk<7|sݺu:{dXb1<r@X97~^ T(! hk< D7p4###Q066&) .k'QC`||\ @[S@l?~|ڵ[|I9lK@8ٸqcҗddb`` \ښ"b^_^82Je_T5Et??ۿ[L&9M!yȼ{,KJ%\tZ @S@[#]wQVCpg2N^_ty7pyOT y.yЉwA'B!\###ڝ":[.rk޶mی#6FGGõ=66&)sU*~.o1Å],;Et{,_~bbB2l6jKE_|122[iwL&\ڝ"w}/rk׾z.T: Ā"z>88RphGde<@@ (Ow֮]{ ^*JĀ"z7ȼ{N2p EpΝ#ZM8R.\.' yx xΝo||<\###b@\ʟɟDk歷ޒ -nll,\Ā"7xcڵ:dhe|>\bQ @ (˛MR.¡5rpNLLEpE-["]ƍK8L&.i1zzz5kּkդpuE8޵^5g -%L~8S4xPVTnHww VPÕ900  yn۰a/nzz:\lV @<({##^~e\<LNN.cA X,qddD @<(UV7o.#hp) ǷHL&M2pEK/f͚.?dXe\.\rY @<(oܸ1rk#}믏ܚ;;APR G:3<=Ae˖Hw뭷;wN8|\.k#ENzHw5׼[a^T F63<;v,22oKB.|>/ fy@Hկ~ȼ7::bFz>88~W~X|>\ BA @(6suww7vyk׮52cep%J%1kpdޮ]Ͷe2p e1oHwwutT*.J" fy@ADӧO .qHYN@liddޚ5k^}UtJT*% ~y@;֭ܚJ&r3@Qq6JooTUXT ww6?< xG"]޵^ ' B@Q 22oۿL,;z@Qq611qFn}O>wqP?< nHw=j5I6 oT?<#ov=}dbc`` ܹGt^z˛oo$U\Tď" 6lܚ#A VL&Ē",Z;ty6mӾfff½NĒ"D;vty֭L*d2bItW_}u͚5]^ww$ӎbs@XR߿#}߬i/B!܃|^ @,)VHwmUUᴑp %EϿ/oJ] n||\ @,)~W_M$:oddD2m!͆{T* %E/;wotyLȼ700iX[ltyϟN+KRT*bIGG G=zT2^{*L +EE8qb:'L Q:W<p7/j5ᴔr8P @\).GtydZGX N.W<8x`OOOc7믿.166q'N[.rkޓO>)V0221q".\Hӑ.oppȼr)J7#]ކ &''%DCCCW<ŹȼHY}1==- yV.ȭy;wLS$p/uqXz~]wEsssYL&Ę"`醇O*U3==?00  yWW^YfMcYaCCCbLp>믿>rkC=d^*(a\N @)AL&|.\Ί c<eSOuww7vy֭;zdVHؘ@S,z+HDn(y#bLΟ?|A#VB6 C.JbL"~HqsIfy OOO 1EJ./HYFT*R1E :qڵk#uO?-eQTɤ@xSr-.j¹J333atZ @)VP˻N>-Q.<3@xS^x˛ÒYbě"`>}zÆ [x ,MPcMj]wι9,hchoۑ.wߕ̢0B  yq#8 +fJ@xS4;Svȭy +dr3Shnn|>w\V* CT*ͼZjDtOR(|uVT#<ϟ?/\.fR,|uVT#3twwGFK& )MVp@;vXdd^+|Cs0&_] `N5"h AЙ$0 \"2g  Eo>(*J@*jȽx<  G?cG0==n@󯮻 wygȼ'xs(Jg_]U8Ո]p!JE/| Z6||<_]XS#<֯_~Miյp@[;p@dd^OO/ _] `N5"hwׯܚ|'ƛ--Ϳ^jq@ T;#m޼9#2LrWWQ)+=] 6~~/mذarr2~[NmiՕq<"4vy?cd2zkE ӧȭy_cJ%ܮT*W׊<`N5"j}.ˇ~+Fe2Vp@\}ߍtyd2#JREl%y*jDc/5k"#ިBnN>oeZEpӅ>"=CA2%B=n6í( -qu2x""#mݺ6!JRյXS:=^6hk t~[f믿ٿ:j+JST\]U8Ոcd2rk^>g8yd"l6*rL&c'C>Ûn)z;77iӦ߿ /T*1ڃ@Pt |HwܹKVV۲eK$ɕ~Bai7;E /,/ڵkT*#;::v9y҉'{dmq[n.Y>˰׀ΡcZ[otsj5Ʌ}_={+f*Jv9yD=ÑnnÆ O_j.>W ezz:"8p@OOOc77__g2+;33/,JeE799yv-ս[Ֆ%ɤ}tEZ%wy>򾞙_tEs%wy^WR.gd2v Qy\~ŧ [Ə(3r9(<.c۶m]Wa``Z.+) y(<.e߾}]WmǎA\ sll:"OuرD"ѵwp||:";{l___2I$Nʗf',JQy\DZݲeKײڴiռٖ34ڂ"hmm߾jOUT&(:x`׊ٻw/bM@qNX,6[ }}}gϞ] !S@Qpǎۺuuy[lY찼8k>:"O511}D"]^Lݻw;@Qpw[.P(\ qtEWdnnnll+k׮5` 8zruyZJm*Jiy,ɓ'o߾,]ގ;/6SSSviy,T.K&W:tkӦM4<VZOH$'Oğs؁N`yjuy6m[gϞ-Y/m)XfG{m۶-Z߿ppFP*[(XSSS>EkKسg߿q"%ސ/z@(XAýW}+_YTN}- yjo߾+7uumuu%ޚwq9`AP(U'j]]Ůts_o<V[Xvu]PE{lbL@sLMMر#L^؍xk]v +ETT?YM^YplϞ=+U y4wobZc񼼑y'`;"JD Ē"&jhR]]⋼ZWU3g ď"&;|pC[|;>>.U ~y4پ}y#S߿_@(hц"otEO2RG@#J(h23"&WB@d2M.+~\R"bI@ 7y;;^{##bI@MNNv}¢nL$bI@Kl(*W5w_bI@K(ˉDurZ j5yUݻ\oWUxƹx7o>{z><m۶ښ"VTV.ZR={\t^Pft?I\)yg9r?uԥebb"J5vyd<)J244ottT2@ROfڔ"8mO<)(SNEF%B (Jul}  E!{Fm۶UUmA@9zhdd^: yt3gΤȭycccZ"SVwvYׅ,EDd2JE2@kRй&&&T*edКyt遁./H߿?-E@\.22o۶mjU8@P/:t(22opppffF2@P/;v,22bbB2@+PΞ={FF:tH2@) ۹sgVD<#ԔdfQŕJ./L9rD2@S(SU*[FfgϞ 2E\J{tyCCCsssV".oȼgJX5<"6mjdա+UVmyݻpE`׮].o֭JE8R d2R)+GK155N##/`(`j‘y;w @8SA#]АyS:|pooociӦ H̙3["GV۾}{r.)`AwD" J<Xf'Ok"ߙ3g./H:tH2)`Ej\.sN#Q :tP2l6o|511E7;;;44yy(`5;wF;vTUWBg||<22o```zzZ2e)`Uo^Մ\"X,FFm޼̙3R@3MMMȼ#GHP@Um۶EfgϞ yFFF"]PR EÇ'./Ne?W@KY82/LE<h-k׮z.d<hE{6H:"ZTXk濝 t&E3g 6vyDСCVVs\ddΝ;]8Qy"#Ϟ=+<hǎ72:"‘yAbOV-c# y~̃499[,%qv5;;7<+Ez><ym>@8yADcu֙@S@gHR]^ɓ'%-Ngvvv˖- @+S@' `xx8KV&EtB700`d&EtrNT*ed Etm۶Ef;::pu(_6CѣGdddޙ3g$@|lȼd2yq@)OV۷o72Z"={DF IE\̃>-["#9"X}<RАyt<2 ػw‘yJE8jy)JTKFQWjvvvpp022o||\2 y"۷GFy#`)E52V"X'O5vyTjbbB2By={v˖-FPKݻ#oj֡C"]޽kd,/E ?wIrQcvv6t}ؘd`Y(eA>tyFPý]̌dj(wԩT*?~\2d<`ET*#FGG%KVJ^ty;vVR+k||<22o```zzZ2(<`ŝ9s&N7vyF(077}lGFF \ Ezr\V \"XUǏKӧN \"Xm]^__ypi< jZ.32"h}%./VUB<&&&##&''%<fffd2yQ@#E|ZG۽{w^|DFFmݺunnN2sERJR*j6m455%PRlٲK$ Nzsȼg}<:"hQ۲eyt,Eкtc7d@<U*L&%SNIN䂖IENDB`PKs_:0%%-Pictures/10000000000009C7000007B8B23B0998.pngPNG  IHDR fo pHYs.#.#x?vIDATx h3x@bm-ljL6]W^|)u7;D$nV*T jS fN#jbt:LWT?t~wa׍???H؏ Q5JVP ++*TjffƋ eo͛6V׭[wE7Jlij^rSU-U7| %TUKO{A`u֭SSS^{ SU-y Y[SZ:<<@I0Ur4yylКգGz}Z=tӀ<2SU-kǏUVT֬x)YZ^Z:66@4jҶ-Ńʾ>/H"TU+Ӎ+y:qBe @j%kɪ7MkVSUp Y[SSszժdrpp;br߶1f+g2wס5{fdXWVT֬f2GTUqi(=pա5t)LU&/}BkV= `W7=ZgϞ|>8LU8~,f!:TU1mٵ55Op(b6C]( |TUjnGZBfkV Yrh*}gNZu_`5Ligҟmi,ApQ@̙JdBkV[ZZfgge*֬R@W: V >LUgVVTV٬ &LUox0Z~a'@Jf1`Ӏ{{{"eTUݮ&Z=9Jʘtg?җ*R\.4(Ww Ń5k֌8Pʒt7Mg3۶4V wSU.1}3OA`*@y3UujeEE`u֭֬SU^}ښ뇇/TUZ&/5ܑY__# Jf`uvvAPLUı#5TʚUf*-rà Ńꑑ @2UlfЧwww @1UC<=j>wSUii׬& ֬SUii}MCkV>TUZ5M;wX PLU%kVXd* 1U{Y2SUi LUSO|(LU2YZYQQC֬|SU)kV7Z p;LU6lh*2Uۍ+[ 0SU)ʊ kV>?;yFkVnTUҟ֬6a*|fSOZ b*~ZoʚUOJ 55֬|TU-<{W54U@y: SUI uH_eEE`u\ ćix0}Ńd2N@LJ&>mKcf]]]P LU%V7|`ڵkNRJ5 la 1SUIw;gԄ֬:Ore*鎛<ִsGӀ;;;@Y2Ut7]lk}<4Xݷo_>weTUbYwTU5 1Ud*BLU%Y SUIKf58jf*i׬:ui .SUI֬,TU2Ymiiu%TURAPpLU%gWsw5> P(8;b*׬v677[ SUIf5YUZ\m2UT]p>f5Liwp;LU%Ţl摇AZ |.SUI1 fuvve,TUR:yʊj*fX5rMYnYmڹ#q.0vcJǁ޽{(f*)Кպ1)SUIq5CCCn􇫹GnfP('SUIӚ՗.jKK5$7KVU֬- LU%)fuCmm`5LiĖ$fmi,Ax2U[Y=ӡ5{fbTU>G*+*Lb*I 555Ńꡡ!ć$}NǚvHܬ1a*If`evvEeTUnǎ֬R\..f*Iw`zCmmhȈʘ$Y]MO. .(Kt7z`5ϻWJݯYMVUVYc*Iw6oZ:44vrb*IfikVJ5BLU%ɚU`!kV7Z eTUc׬vvvZ TUYa*IK[ TUuJ$Y ,TUV`={YRa*I֬ 1Ue֬@)2Ue]mK5PZLU%iY1} kVJ tؑ Y`*I+Y`*I֬ 1U$kVJR׬gYD$Etjee~+8SUIJ7͚USUI֚ՎCս{Z +TUf5YUUdhn*,'SUI*5ŃT*533a*I%`zCmm`uݺu֬0U߽)i===n2XjTJsAX TUJ'^Yr4X"d*SUI*&>mK5 LU%T1}广f$v< Ywb1U7m,VWW[ TUʡcM;w֬vuu W#SUI*OM>Z TUʪGYe*IefuCm5XLU% fmif$g7tX wTUʹRYQa* SUI*FΝݼic`2N6JR,֬6a*SUI˚ՃO=i*SUIQ?{7YUe*SUIW Y[SZ_ҍTUbݻYd*I13OAPe*IqoK۶4_OJpc V]SUIgYmhhf.KbTU8&fupp} @JnjXOte[pק&Z Vۗ]Đ$3׬&uuuNTU{ΚU0U$}ΚݻY LU%If2U$L&*왪JiSܦ7SUI5SUI³BՖY*eTU8kVST.sP~LU%Iw{P[k*eTUkV_~e+TU$kV]w%Yǂ (Ս&SUI 4 5.f"TU2M|x1``5w3Qc*IZnL_io{"fu޽sssnhTUkV++*֬okV{(0U$EݻB/ 5+TU5/<{(4Xmmm.lV$)Z~D*full̝ J1U$E[ @tJt6͡Ofg*In׬ٳgvv r2U$E3'߬(RL& `٘JPzիCkVO:`yJJcЧX'Ys$zКՆl6F`阪JJsg7V pDLU%I]MO. v$T׬v677[ 3U$dUUhj.sLU%I CkVd:vXLU%I%t6Ń z{{Y`QJʤ.КY=TUT>wpLU%Ie^ YspGLU%I CkVJʼGnfP(x0U$bK?A`ŚUn$).qX*fu||ܻf*Iך Ńd2N!`x5lX$)kV=th={YLU%I1ʊj}}}&SUIR|}ښjuuАK.^8$)M^kڹ#q.ov݇=X$vjǁjKK7 '۞l]N;ZJr s9_k1%Ilx06fuddK[ښϜ`]ϦݻB](i֭[C_>|cg뒤xv虧C7ckkk> knn=ۏ~k:`]5ɪ⛱UxJ| (~|@}o뒤x6͛6֬ y70<<Z:sM%I]ڴs51kV aY$Yj*@<%W\WsKYf nYʗ.^8$)kV7Z C5VKkV;;;Y(o-׬%I}ޚUXI|ՎOMz.Ig'On*@|$n_mK1%I֬~fu``{ _:ښwx.If՚U2_Y$i5{f$7rj[WsKYf$֬>P_{.Ig__֬]kVW'=X$ųlf&kVO^~-׬nL_l]dͪ5!qkV}ŚUI5֬Ģ)\:ޯ=X$Yj*@K,t5o9$ɚUkVJZbkV?>$ɚUkVJWbq[Y}fkV%I֬~>zP*KrG$ɚOTVVWF$$Ͻ뒤xv57#-֬׬Aݘٺ$)kVCwYH,\[[[rZ>5$)udhY2ŏcKoMod2_hJ,_3::Zf(=$)Mg3w5Y P7nͪ$)?җ*YXj}G,~|@}G$)돯BkVS"_-׬9뒤xv}jr5&Q_-׬^%I]q`Ƿ5 Q*_\{{{r跬Y$Ŷ'^_>kVZܾКկ5|u뒤x߼yFkVT5kք֬u뒤xv57Ge*I=55uКտNVUY*I|YXD2rCkV_>$)}χ>Ρyvv[(oP(:t(s9[*Imo>ZqFkVB硟ZWY$Ŷ~yƛ>'?LܑD}?t:fuժKt6׬zp-e2T*ZW5xfܷo_>q&B=I%I]*ǽ7/oum[?}%Ils֬܎Dۋ/nܸUu뒤خYݽɚU% rT*U8W{.ImzҚU$b=۷/~kV%I֬Yd2*1SO֬6fUdjSNy7?<?55Zݘٺ$)`evv& '{ =>n܄$kV?Jr @I%I֬~.xĄuvv֬6=X$v5CCC3q`YMVU9뒤x6_U(mʛB2L?W{.Nyo|;H ?Υ]$I$IKȏC꿞;mxFQ>omm VZfUToIH$I$I0U.kV%uo5J$I$IWjt5kV_z(=`\$s›]I$I$iQfj_#}&F;>}δ~M*XY?GG]y I"F-U+UNxIzjA* B7BOԤ,(U׸DH*6*t;+7y:^ݺl#΋{TV]w_|.ۛ++ zD4rfu-?9n|Uus٠Kdcgg2>5ͻΈkUdUܼ`Ȱ:hXU Ϊ} n8 3m2JZ"8qbdXV6>v߸OʰjD|ң`UdU x<<yه{uO^mjAӀw<8C=&VUIV@{rrr;^TXU ;5^>|T)j˷~VUIVPkll,**|.׳`U%3cNVO~ UUU5КϬNքOCZU Ԧ=W=gUUU5Ϭ8{?B}* \/#*ɪjD,rflw>= ZggVGqUUU5/^9:lo,VU۞л-3x3UUU5vڕ*XU|fuT3߷ǃ`UdU Ç2$̪BB3OV.MV]+7$j555]3ʦ: VUjO=:>SJ[EEEdX-}Łw}!XU!P;V,6G+f}vh*ɪh;vpfqllqgV[wYv/|~o2>5ͻy$UUU5ܪY*pLϞ٭=gVw>t3`UJێ;Y*p̾JF slŰa)RjYdU #G\yάUH>ڷ}rPʰOu?JGJU5КoVgV zIEY3F5?VUI?ǝY*p+{vtKaj|?xVUIڮ] Y*p-G[>4eX|*IVP;xY*pܜ[o)꺫AdU GEάμe VUGt˞/zf?8VUIO<*XU3ݦDϬ*XU%ɪn[nˋYU[I+ 1zfup*IV@;|_*XUc>Usd]2lLG`U$j577O6͙U=y]gWzfudX#;`U$j-\ЙUǽa+zfJY$YUm׮]άU8iƕ\)bXʰάUUX\\*XU3k`^GGϬzCXU%ɪhD̙Uǭ쑋;>?oxУXU%ɪn/ άUBjuq7_= dU M68 VUwV33P*XU%ɪntfq~* kF_#XU%ɪhDbάUH>=7k~YMVUIYsssyy3`UϬ),H?Z;;άUUuּș7o VU-G=Z5^VUIڑ#GեK 7k~au6oxXU%YUngV ݞ Y-yr# Y˞ !Y!|hʰ桿߷#XU%YUh8p`򰚓e !Y=z7J*Nxfu-?mBgVYmgo`UdUU-^8rfxקUg{谺G#J*Ϭvŭ=yU´vKɈWV KV7 ۯy$-j<_Z AY.Ҕau͘^XVUIVU Ϭιk3`UϬvɚ7eX]5+pVUIVU[ՒW|^'rBgV gV_3C=UUUU~fo7^Ցbˆ Mw{VUIVUZլmW.{9XU!3igV]}_nVUIVU ϬΚq3`UgV#_<UUUU~fuܘoC3)ʧ: VUIVU[ݺxS;XU!Lo?fTg'~R XU%YUhgVVigVY$ѣ_};`U0{8 VUIVU) Fά8{<̓UƧ /Y$TuuuNNNڹSꭞ a[digVrQ șՖ^p'{B~cb3k*XU%YUnMMM'N NyǪjU0/8M}?xVUIVU\ՒW8Uժ S?)9s&8 VUIVU[uuu~~~OXUwj6;C6FϬA`UdUU-X rfu˜YŪ |fGe3uSGy$mӦMyyyjn]xeXU02l@bRϬn**XU%YUnEEEjNvڕOz)Ujf4řUJJ)%'FάμeC@V>\17ꞵK<|ș xeUo2>=<UUUU }=}RU#Z!XU%ɪpYqVU Ln|gA3;!ϏXU%ɪP۱cG~~~ڣz XU0۾m¸Qcˇ `JUUvÇ'Ymۮyr XU`-r[=:ؙUVUIVUO% gV$wɊYmgXU%ɪp1%~ @_٧ j]TgVJUUVWWڧ}SյW9 UU D"1qȧ:OC@6-!bgVJUUJ"'8O;Ī {fu@S Y*IVU)ꜜasNoiXU@o^=Cl3XU%ɪ*/jK?}ҋ @gV]2$YUhMMMau]>=|K @gVMVWlSJUUVQQV/a{VU 3{GXU%ɪP{U[VU 3#ά<(9 UU ș՜kVýUj[-qf$YU%'F> x֌ۜYŪ 8*VUIJnx*.άhTz<*ɪ*[EEEw'OTY-գ zfuC}GRUWW'{v/Vά&;[/{`*ɪ*Z՜lgVY~A)1߯$hD4rfuf;Upf5io|(q#VUIVU)Ț,XVj'{UjEFqf$nuuu3}׼v iocFYrq$h C Y]I/_!Y>9 rzR%'F> s |f57Ϭ[gVJJVQQǓߚ*VU X/{kL@|R 999ꠁYŪ 띚c/~fGR566EάnXƫ3: UUUU 3󏝑ê jʋ;Fά?ȃ?VUIVU)ϬSq3rXU@‹Ϭ֕O3XU%YUP۴iSjkW6XU0۾mR1V KVXݞJJX\\9~*/nY]3UUUU D"QVV9:km>%VU LUKt{齉#ΐ`UdUBmő3%8razc۳zH?Iά`UdUm׮]yyyjN^ܺ 3s7~'R544%Ymۮ\:XU`=4gV[EϬk9mY*ɪ*3VU*@%]g2: UUUU  DUgV!۾~ ϬV9#RUUU9jUvK#;ĖMVrɑ^ UUUU bgV7֛ztpʰft;gVJJvG> x-?9*@V>{^~O޽^#XU%YU@knn.//άZUՂnS Npfl`UJJu3jkH{faFuL;zCOw{BZU%YU@khh(**rfժ 1쪹{]b.uf$?t;jU~8KSU#ZϏVUIJpBgVƧsgY\dU.''ǙU*? x¸;V K=:#o*IVU)닋YpCsf k`gVVUIVUI*H9jU=rqG? x{YJUU ŋGUgV!YW/?Q١kdUmӦMάZU8njsڙ UUR5668ЙU*嶪2>uy[gVdU-H?ޙU*gVf49άXU%ɪ*Zsssyyy߫rʪ >gVkǙU$YUpۺukjάZUvK#:DϬVM}䭗=XU%ɪ*Z}}!CxZ{JUU 3Ɣ8jUspϬ拣w: `U$nάZUk_3bO+ SlŰaT`U$h'uUTdGf49#dUB-5W;/Ŭ`U%3];OVkf~O[VUIJv3{^{٫1*SCՑbˇY[dUBmϞ=]tY]2/ȬpNW%gV{e*Yumr*IVU)Nxfu-ӝY¹~\̟YgV}dUm֬YάZU\AU?J^=n8:8g3VUIJ~fsN/nřU΅Td-%#^[1,eX}n%}5OdVUIJ~f5my}fU*!Y.35֬\`UdUh'<άZU,Y픓5oӀ_lOgVUIVUI~fdWUqUղgVdUTվ}xejVUՑbˆ '~5$@;5O.rͪ _Ѥz |Ղ3kx=XU%YU% ϬξgV`UřȧynVUIVUI~f9jU*άF*UUUUR/ݬp'U*gѾƏ*UUUUR:kVyfU3fo3f53VUIVUIs'<YB&'U*=uOgVTyyy<OVǍ)8*XU ӫ.Q𝋢j]TgV­:^JΪ D ;>Us7vfىάXU%YU%[CCx3GkYy*ɪ*)КJKK#gV?;*|?]jU\VtIuf*ɪ*II-X rfxwyVU0} YJJRR6mKVw+YU JFv-K=ܔK~k(rfuú5^YUpάXU%YU%)D"1q3| >*@gV :Fάߗ*ɪ*)КϬ~W5U LׯSX*UUUURҥKڣU*@mv7!ܙU$$Çwfժ qOVR5צ'3`UdUh3gΌY}YGϚ/iάXU%YU%O<<SqcUմU{%3VUIVUIcǎș= U0o^}e~lŰ37sf*ɪ*)܎9rGά>yU`-}G Q`UdUhG6mZ̪Ug3ki fm_җN/I r4KM9$@YqoUs۞;cZEb'Ž=׷< ՍO,6 :4wg{*ɪ*)vؑ )'+ꞵKu`UdUnxnlk-S^ܺCW.{~/|~uS9_' : >ؓT&g73͛1JUUҹ^EEE<OVǍ)9aU/?b%}ƪZ~˂w|_=vMpIluRZ˿;6 O:ٱ3Oi>Nv>#wVлԂvfuZSNɖ)B'x+N:vO:{/7dr=_ɾ3~>ofg ߼zD3ݪjm6Y>sUժjUŪ*IVUI~f5ϬZUVUUL>?vfuѥ)꒜k7XUM&k~}nUZUXU%ɪ*)КJKK#`C~!*|wUƪz) i)>?vfu^uw}wUݛtoҽI>]UwUUuW$YU%ZEEEdX߫{u֭@x,STXНU}a+u3/͝t*pɶbU$^xᅼȇ&׬p*LުϬZU9ra .Ŏ|IwX37'mB&3?>7qw^bU$@[xq<Y{FΪʹo;RB~,KLb]#?U ?ZܫԂ;9 U$YU%֭[󓷜KUN ߼:mobbxT:vmpVά[*VUdU ޚUU.///yܩӮ*dlU=S'\V'+[ŗʙQOpf[/{w VUIJ -'m XU!c1p^6m vYWrV.MVWnx VUIJ D"QVV9Ǚ>#gU%U/IAΑ3UKY9ڥ ά^z3VUJUURUTTDά~wOBVol{vPqȟrsMάY9ZܫGYAtX}q΄/>:*XU%ɪ*)Ъ󓷜= xeU[^1|]5ϬSWΑ34_f UdUhgVs3vfժ -{wjQgVlz[UYMOz VUIJ D"QZZ9y?3VUHV'"gV;omþ2pY}άXU*IVUIܼ`ȰzU>9ߪ zE_8;gV /gV?; {Ǵ/VUIJ_]]]j>E5*d ߼:|q Ǐ~fu便}5VUIJ !CDά]U_`֭*|}32pVάmY-9޾dUhDbĉ.XUشŽ$'T-]+gj~oY}e,gV$$[EEE<Os&k?jU=ozܻ}e ^ZdQA3?Q*`UdUp{rrrA3VU2gVN67|2읚c/~fى]*ɪ*IXTT9a*dز{۶i8l@=|e9ftʻ9UUUU);3pFΪ _޿xC߉v_r⎹gVw/יU*ɪ*I~fuܘOUNeFάV+Ƨ /J?IάVUIVUI M6EάY9*v̼qrǏ*d޾&salŰak|jwv$$Zcccqqq5~FΪ ΋Yl׷<+woO?jDkgV$$["(++;SgVpvV.9;17'gO@U-]ҳ~?*`UdUp[xqw'O~*[-יKF MNlۦ¹wƶg~fJ?;߻<UUUUm׮]yyys%{^{٪JPΘv[3;O#Ysjդ޿}5oVUIVUI -s߼U5*Y63pV<4gV[Eά)ixJJR555;6sr̟%ϬZU)ׯ)?rfO?#8+gVeY}Gw8 XU%YU%)К#EoݪU5nȟr0dmaAeꑷ^Uh 'ufժU+:}yߌz: ߘ~fuWUhDgVXU"5?{k[[dy?}vH?{άXU*ɪ*Ixx{C7ֱfXIcYrfaFuL;zCOw{ VUIVUI ={\tENj,:V [Vqo6mծY`Wo[tiʰڙU*XU%YU%)䚚]3{u7ֱfK,3p.x2&nUbu!w_q۾|-^R1uµǴGKF |߿?bȼW7>=/>/>: #*XU%YU%)Z^E2nظNoyO>yBdX-=ҙU8+ߏƕ\=;`UdU$|h쪥KKV*xhάVKRϬqf VUIVUI yU8u`d[ufΊWt* ?HAܻh{Y8#6[3l1- UUt-]rxșiOgV!gVKG|[o`W/dΪzD՟(~VUIVUI nUmyxW^>]Aάq#1)ȼwV|][zffwjUdUV߷ٲa3y*mۦMZԣ`gj_ȼ{VRϬ>uygVjUdUWc_w#/=U S?3?}~_8+gV/>>O*XUgaq>2lO|߿W2- {RZ;;άUժ*ɪ*I!-R5^806wǗ=uȤvLQ$YU%)UvRșk/jaoCUo:yXЧ{hά٭LV׌n*XU%ɪ*I!-G`zo)YuȤ=Qеs۴{y}{άUUܪzw+tooC&Sa"|}e ^tqG*@w*IVUI nUmqW&k}Rۦ|ۙU8o^=CsS.qfdUW߷gw.I~xظm|)d޲{;/yX-[񕁳ufuѥ)jgV*IVUI sUmGvڙݷI/{2rfŽlZ^+G}wӀ_Y4˙UJUU[U:rfual:zI άoi~{@潱A}Ϭ4w3`U$$8J!rful߮ά1sgL#3S'\*d^Fv5]Y$YU%)UtՅ 1%ȏ@HH DH  8(oQ䗡 qs-*V-VZL#'Nz+{t̬Y}=`U$$EnUWgV |ጌfcU ӡgVS+<CΗ-6_mZ`U$YU%)rEݳ|HRSn^ H Y&rf_F;{; XU*IVUIZxbxJS㲼?d_*VՐ}vqaՙUHwc~M ߐ#NVUJUU"3gVݙU %mYn@Bάfij`3#.}uSUdUȭվWjv\Oձ;f; Mjd ؓs<`U$$EnU䮵3 ~4bUM__?HJZ2kWuf4gV*XU%ɪ*IVկսfuoU{;jXÜYxgo=zۜY`U$$EqUw)3#Y-*zWYUUUU r-O b7hY2!dVi?Hwfi)άVUJUU_;_W[Wޟ{dάBȎҧWZ^_;\2/˙U{~ѩ_Tg/VUIJU%4<l̐S{agVwrۻO[{^<љUJJ/^mS Y:<Ξ:)pfAY}0FgVwݔ*XU%YU%IQ\UvN8:k5K:)Ɂ3vl@;әUJUU|]n|ޱy֭ZyxW·lEϬ{rY`UdU$EnU+4jXndx#xϤ: D/Ѫ7vmjtfG<$$)rjN>6-/rn`Be֭Zi]@˼eҳ-JJ";WF}b :ҺvqfWw,eA3`UdU$YUv|C3rb+sfc{ u{3`UdU$YUvkdž] H!3gLi*4 pxK_zVUIVUIRVz~꽝%"gB[V/`B%)άBSrlgV*ɪ*I/4>zOOF;wTvQ s{tsfșմ3&jO*ɪ*Iܪz/Vˍ^Sa^֣`)x$ЩG^=ęUh TVd9 VUIVUIU?bxJS㲼^|U:Ξ:əUh N90a C;69 VUIVUIU/>93{KgV$jVYfm^<}=G?~Q XU%ɪ*IVտu=0ʌw;ԪJgVӺv Yݰ|WW+^oۀY ^dU$j~UC?5^U8[U GG8ZF~wt~V(ⓓVUJJ";sx3Ò?5(vmFǣxnU%33'6>a^/Ѫ oSlgV*XU%YU%IQ\U}vxeƛc7h{j֭e3}z9 lν9[ru*!YspgV)8w[nӂ~`U$$)rjڷ&5pfutV'_@jU%|s*4_t(hx ߐ8VUJJ"n tk Ho)άBSr-֑`U$$)rju?- fO HHeEߌtgV)ؽ~u64>zbj*XU%YU%I[U/'+4j4'vЁԪJrf/:d5>ϋ': XU*ɪ*IZǪ+kxGwo`ݪJfN. άBά5rhXyAauϭ9>`U$$YU#ⓓ/j\^G-{nU%dV8 M翨}S`Xv}GgV*XU%YU%ɪU/Ϭ>t{ψw;ԪJȎޒޣ3T1uAvۀi'eUdUܪzov+/lxYNl܈|_@jU%39 Mzu[ۀY`UdU$jDWz:~8pf,/vMzc{ynU%dN E@ލ1ClC~auN UUUUQ|j =[8jU%!gVS۴ Y={WجWpfUUUUVկ|ջc~˙U7gL^c{ά$'-HcwH}3UdU~ՕbcR#Ə6*$jnfiάVUJJUժyvdk3:Vo:lٜ{[$%wFP!љ<VUJJU5|T9)?U7oa5G7 ruHՙK$e]:$jQa3_t(*VU$$YUONVޖ8zS/`~f5GC;6{eY}W$YU%ɪEj=pCܻT~f5%9e^_;\P?gVJUU|7m.lxiN8 MrfufvgVJUU|3gV*43GtfF -ꮛ3Y*IVUIFytk{j!oMINVvTuWYm?5(pfol$YU%ɪEj_[81pCnrf+2Syx3-YDC6,vf)IDAT$YU%ɪ~k] #:?f?>8:{$gV!!萛kzzpFѹy*IVUIFQ[U#?5~jPltVc{ynU%gVG[_t[άֽ7XU%ɪ*IV(%9n-^UY+o9@[Ѧ ~>nq|ޛbڢ.$YU%ɪ jZV[0_@jU%gVS۴[_tY=~3qK{ XU%ɪ*IV'+4j ;v #`ݪJϬ3o9@B~ѡ_Fӂs<[XUXU%ɪ*IV(/^ygVGfv!IOhL3VU™ՙKeĜYMqWY=W7`U$YU%ɪE+SgVKhU)Y-*?}WBv11=%<UdU$j}I3g=X3i]To蕁9I"闾\VUBe6mgVexbკ Y}~ĥ^~֛UUdUuW̕ߕ[zTOխ$j'>蕁gV_*XU*IVUIF7_2pfhU%dsieSS=8?ޙUUUdUڷʯ8Zm[U ْYY}gN ӷTjo VUIJU5|__[e+ruөG8:sroW{q EϬs{VUJJQTV3׶Y! gV!|s*43SKY=>қUUUUdUs5P:0^)ׯ`BlEj6jZ.vl@ ׃{UUUUdU/;.pf؂wRٻvHY}b^ߡ{^ޮշ^gVJJ]gVd&0ܙUY_ oYZR|hBvjU-V^`X/>9VUIVUIU5jxi󈶁3|ƒuْYU{`W?zϤrf7y*ɪ*IFѹv: pfnYU<,%99~XMmye |k^гm-#?VUIVUIU5V/*4[7u?nj^HǬnY=~3XU%YU%IVՈ`άBS8ZT67-߇{Ej?$$ɪE5oTQΙUh =90; 9zǸ3{Jt7XU%YU%IV(:WѶqάBSe6m.޺+{zŒm[`X}Vά`UdU$YU#|]k ': M[JVS@Bά݃ Ϭ>[^#ά`UdU$YU#ˇ$9 whUEoZR |' +k|f<[VUIVUIU5jߪzfإάBS0ƔZ^qn0Xڿ7y*ɪ*IFyhY=c\TgCMcjΝ$DgVq3XU%YU%IVՈlͫ?.j\ojؒwGل@9ֵK")ѹ32W[٭XGy*ɪ*IF;8Pؔ}ϬZUiҍvxۀTrhBvܜ1W|˙/>9VUIVUIU5xkά^zѪJS6{yjW?zϤ؆gV'qo NW^^dU$*!KgV|ά^ޱ}֍^_y]8řUH n^dU$*9_W{h靁rŖμ˪ YM-pfze |vlyy}||0GtzVUJJQ겜ش#OgV|W:; M= w3_`UdU$YU捗*Fjvp솬˿7gV|:ռ~V@o߼gb=Խ7`U$$ɪE>Qy[^ۀVYU!dkca5k}ʼ2 uIi; VUJJٚ%auzÙU*97#=V@nֹbGY*XU%YU%IVՈ8Pؔ}ϬZU.K7aqcYDYsEpX=8\<U$j5>ZOU*]KfMo?^5h=^HșՂ '<U$j}~Į"gR%3VUv_ڦMzy/?啁yxAW l[9 VUJJgVψ=|*-}z-͹+;csNe _~6יUUUUUVՈ`gV=eG*ߏ3F YV:ӷ8V/ՙUUUUUVՈj|fuؘߡ3VU7?pjnVw1|Ϭ1nv~&̺`UdU$.Q;oԸ 6>ͫϭBϬvܩzF KzmXna: VUJJUGlk?i=2VUgٿoFzꚇxe |>kvg EάUdU%+?5^9 vU*3ά3ęU߉=yontfFțUUUUDQ/=[xC~d@c{YU!dKfMYjЀw@gRIaK; VUJJUʒsKϖ/>c\TgO~Ym*@X`U|*XU*ɪ*IVU"˳5U{3,O!dؕ/;pfl< $jfγ2֪άUUUUUDԉ+4jlG>ruG'!pfubάBB~ѡ 7{pX=43`UŪjUdU$*UK״jvplRA=CȖ͹7pf0ߙUߧoWO+\*s:\aE`U$$YU3we޻I!d6]ޱ}j֍^Hș՞m[*VUIVUIWf:$jJrxe !vMKyJUUDT3fZgV!oX|3D~a܉iaF9 `U$$YUgV7n*o٭[rfѪ;ƍM5Vwߒ<XU%ɪ*IVU[Ϭޖ*Yݷ+[.)-wfjUdU$*|[Ϭ$z!;Q'pfERRy^H/:tnVfۀgsz`U$$YUgV;v3ՙKgVǏ*$갂1W  sƜ؛$YU%ɪJ5>)?6o磻x!۰|QaAYnxfu|foz`U$$YUO5qPSظYT*4+ٶc ϬOݾ<XU%ɪ*IVU[Ϭn)gV<+޺1[}gVi7VUIJU C/ˉM=° !;sjZ{& E%݃sYJUUD~Jc OT`B%)Ɂ32BB~ajI5.|C~K_z`U$$YUO,9e&]/;>:wfZ.|Ў]?޺++[?} 9O b%n|Ҫ YM%~Xڹ3OXrXK<MKmj XU%ɪ*IVU"˳5fj|fuVUه{ά.sWrSiSdϬz3UժUUdU%ޫxfOĦorfժ o?jdqάBB~*VUJUUDԙ{*=άZUῪlIIάB}vԒ.LtfU$YU%ɪ \8zKv_ܖ=jU«ϭc{gVCZ%rYjUŪ*IVUIWV40$csf_@jUΉ=W 8ܙ^_֍;rfU$YU%ɪ |ظ䙆gV=3G%Ł3cegV4aOtf*XU%ɪ*IVU"x⚶O7J+άZUѹ3[j?^5hvye !PꮛҝY VUIJUS?/r̺zfժ o YMIN^+{bi)W tf*XU%ɪ*IVUCVgd4YU!39o;W<jׯ}EgV=9Ǜ`U$$YU/n:~Gxq duՠ7w4V_[*VUJUUDԅ3SoȏVUٲ9>ҎTVxe _tX|cn규Y=oUUdU%>;y|شWNs;˭}gVS۴ٽ~W· Y2WWzUdU$*Qt MقYVUj^lgVYzYʂlgVUUdUP겜؜QٖcU߷N}3ۻmPӂê3XU*IVUI]άKYcHƻv}W#+ȻϞ֣sgnʙUHO߮V:Clꋥ/y VUIVUIU(S,9S.9UgwMi3XU*IVUIW<[.: rsY&rfg4laү6-*XU%YU%IVU"O Ʀ*!ocXgVØ+bά}G{]Tg/VUIJU8:.=CȖ̚")əUH3G&!75)ߙU$YU%ɪ _ⓓ/LGf$Wy܃u٫ϭsfr;. Yݾ<XU%YU%IVU|]n ~`fe xɬ^ߡ{unW1#ҙU$$ɪJt+4j짓vGL+V'*$f-xf/>9UUUUdU%̰Y=gVٿ+YX|ce ovf*ɪ*IQ<^Y: Uwf]r!d/?ֵKzyάBByxAv<z|u<XU%YU%IVU7֕IjXn쾛sf opfc{ U=xf]*UUUUdU%jߪzWtfWx^VYi58ㇽyJJDOpkn'e9RY:uhw3lXgۖ+4խNZUUUUdU%V40lȬ=U}XwXA3D~ѡ{yY3Ǟ*UUUUdU%~Ɋ!/ˉMu.;3Yueniaa>UUUUdU%^奥 _T?}s: {bάB9:xpئgV_GJJ|-]8+_|rҁsgKm*`;wftlj`3CYJJDrU3VgiyC>zՠάBSp" YJJXU] [?5^9 vCT*4'\ngVh2s:\aE`U$$YU!UޟY-ˋf[9vjnV3G&X?*McXU%ɪ*IVUY-Mos}zjIqjUݙU`UdU$YUkg=Ȏ3cr{ۻ̓uYy)jAn3FP!əU`UdU$YUs5UNwf?K%~XMmӦr^S6_50xf{6xU$*]gVu/ Ϭά>:wWsfS}gV": UdUя߿۷3waX͞:)pfqcO9g^3_|rһ`UdU$U(~jՉC:l gV}X+YX|cA՟tf*XU%YU%ɪjU%|:%6y ?zyάBB,5=MY VUIVUIUƣ>7<9Yy[^3>΃uHծ; R/խY VUIVUIBD?~~gV<3-soW{q 3]Y VUIVUIZU;ov\"IŶh gVǏ*$e#8: UdU_奋73gVK zMjnV3yϬ9ÙUXUdUⓓ;oԸ v[#C|yj*$jgV[9 UUUUUQj_[81UCϬHJ?cDY?ÙU`UdU$*wf|HR}7@άv3_tVW=8Qt#oh ]*ɪ*IBjߪ6pfu=?:8 ?vpjozXU%ɪ*IVUOQ9IqYC6-gV_>`U$$YU!V40$?Μۀ)Y-[<+ 9ڽS{z EάVUIJU"۟<=sʃuH닜YDYK/xXU%ɪ*IVU?g,pfu\nw)Y훑~l6 $jvS /jo$YU%ɪ Qt&YLϯ`™}ʼ2_tgVo7UUdU(:_W;}<ά>_FH/:tnz36YdU$*D;V>$)pfGcYpfu2BB~An-gV|`U$$YU!W*?5~jPdp{=XY*ӷTꞒ?Ko$YU%ɪ Q''+'p|#CϬ$'^+ E)I3Emjxɛ*IVUIBG~Vgd&֪άVUIJU"{6T ixYNlȡ`BљK2B:zpF3UUdU 3™:?5.ˋvjn*~XMWsCB~aZgVwݔㇽdU$*DnUWgV>iPoH~)!3i]άnY+{b=۶ Y}~ĥ^~ֻ*IVUIBVտ|ufYcK:y<ɟ]Ł3/rfJUUU/n,eSrcwqhN-) EάBB~ѡ 7C; c$YU%ɪ ]VUugVs{ۻ̓uYy3}z9 E{&67VwxU{cXU%ɪ*IVUs5,=[Uo:l]?HJ^+۰|Q3&9 cXU%ɪ*IVU/ּI:Yo?c3˟JqvcOqf:JUUя^_>$)pf^#ѪgV}X׋!;uxQa~3=0\XVUIJUgmx؜n  Y훑~t fujIq3g_zcXU%ɪ*IVUs5P:0Uw夾܃u٫ϭڹS%^_y]8|Q=<>ցUUdU(~lgVl=U}4_v3^ߡvj[t3>ցUUdU~ͮuϬ.8ӷ=X0>r`COŁsՠϬV:JUUŏ5oyD[8Qǃu٣sgHJVӺv޺+YgRɷY{M|$YU%ɪ Q}棝|)άB^.CgV@6K@Ȫnݭs3/rf:JUUя^E7]*JբGxq dV3g{!*IVUIB?~׼R5O7&; [6^gV6>$O{;cXU%ɪ*IVUs5m)|K=XϬi?nʙUHW,ٶ媁 ~>>wu+gV|$YU%ɪ }[Y rs<ɾrfj0>[^#άXVUIVUIUS{v:Ў]8er o Y}~ĥάXU*ɪ*IBD}yc.:;YgRܺnge?"_wHņE#]Tg/VUIJUo`Y6zęUCgV'!0v3S挹[ά~Io$$ɪ Qc՛.j!?6 ݙU l|fЎ^=JrS/LlpfuBNJJE>oLvfw"ka5%9y{ l. Y*`UdU$YU!վpb`Xէ%s=USYڱfi¤`UdU$YU!ݺzq.;BB}40:ߋðݾgky$*DQ/mvijI.άB6,_87#ݙUH/:L+߾ =Խ7UUUUdU(m xR%?_ʃuOu)~Xmݪ+xe !% ~>>*`UdU$YU!<[s'rS xɬάBnyzXGY$*DeUG_*SKYufuas{XU%YU%IVU7^*x؍9ݜY-?*$jA D8JJE>_LrfЎ͗wl8er o oj`]ʙU*ɪ*IBD}y꼬NTѫ |ɥάBB~ѡ{v??"gsY$*s{v[p7+E jxigV7Jt}gN!om|f-^7vp D||fozXU%YU%IVUQݔ9\z5C>ֵKjǽ2W,ٶc ~>>?RgV$$ɪU5<[ozoFyˎ+ȋV[$%-5+{u];?#m/rf dR*IVUIU5aglx7: q-ayV@NT}s3=0\G<XVUIVUIU#zfm\8zl6!om|f_FߙUL*)nqf:JJ3Vd5[YCN_TkS23: ~: cXU%YU%IVU|_=2pf^w!d3'ά5c߻vtwf:JJUժ=yXJS㵃c OT`Beվvl@>}zZgVw gV|$$YU+ލOz!;{K^ije+23=۶\9U*ɪ*I7չVϻSu#F8𾻼2}z^n^Vۀi7>ցUUUU>3qfuuf7Ɂ3c~~ fu܉iaF9 cXU%YU%ɪ}0>~;mȏݔՙUm=ݽ+!;sqcsScey ݷdv7>ցUUUU3mάBS8ڦ͖˽25/rygV|$$YU~f'͖7Suh gVϘ╁\TNfe _~6|]>ցUUUU3,wgV!|gL άBά+sEZ=g̹ڏyUUUU3lʏeGߖS>Ҏ敁}vnxfu|fozcVU$$YUYvKNSظYgV!VΟݳmrY}yUUUU3fCgV'Uh nyv_mZuXUdU~ѩ_Tg/ï_XOĦaX-: ^jߌa/>9U$YU%ɪ D~JO7&:KYuwhԒk:]{J/ybU$$YUY:o_ܖoS#YjV*<-׳mYqolUUdU|]VmvCH.pfERҒYӽ23Yi]~'x VUIJU_3~4CϘ2wy\63{<UUdU7&5pfάB6,_*439 [Mjo*IVUIQtm?5./N޻I!dn뛑8Z2 KS[rϟ1+{ gVyu?UUdU轭Oά.ˉw[)! pH(Hk0HAQXPlOtR@BH 'CAƝtdW)Z8ʈS6cܹ<{=sݙ^z֬\)c*45&Y ;[{xbӂ7[ d*I@D]~nǸv֬BSPmiݻ@>y  _MfLU%TULUG|֬B8+wZ`5m۪-Ox25+RZYMfLU%TULU^1=0X}h'o׺X׬m*4!ekVTULU%T֬L5.75.!dK gyGy Y:ꦡwh*J$\ڊ eٱ{[ [Mrr`5wډ<_aA~5`*Id 7W/} 3E[ ;yxϠ~}mld |OZ֦ٓYg*J$/?}k@`uQ_[uh"kV<ܶo֬$J*UEij,E񣙱w5֬N˛d*n:G"ǚU0U$SUI2USUݕo5\Z}@wwMaj]U'!D2SYSUI2U$SU0U嚫?[0$Sү}uHՔmn@[ٿ}kV` J$j}yҫ%|`*DyjmV Y=<u`*Id u53b@ϟ<))~:iHkV!!_4xij›?蜗LU%TULU;kV+Y=߻\Cvm*!5p]`b[7sLU%TULU;kV/}/r ~-.!duUZ[ѓ=V{CkVpXd*IQ|d~`z붮~ȭ:rY&f[ 7ek9u`*Id TL54,V75.!dߚUh ޭ>1["`EYTULU%T#ꏧO YL!6֬B}|ffʲYTULU%TzvM]u5cJ$7֬Vޙù}uىÚUh [758ց$J*8~G+k׬hG֬B?j*4uU:ofԶ_^pSUI$SUpYfƏf-f9K6OJ Y4x@A՗e*XLU%I5os}`r |;6m?XM՚U'o.15+Y?:pSUI$SUp.ynʀO,\5[֭d |ū{kwnyV/cJ2U$B/_z{ r_Ȓn!dƏY]Z8OsC^tRV Y=YLUTU$TS=3[߽B]uGϟc*4V9xfZpZ`gxyLUTU$TOOX݌f96mk*4CaAXY5u^SU0Ud*I2U=7uP魏XUU5Y1c'Y ths{ J$'ʥ%J׭pY7)f ߚUH!WYSUI$SUkWcdRcCcMYhRVVAX M0CdžZ LU%IDFe ~vn`$Yn!dfԚ%YSUI$SUUE7:kV6;Bk?X?laO_:!7{ZkVTU$TSU揧֬fn=.!d;6 Yܱ55Sh*J2U$b _s{ 4^jMdjijOfj! ׬ie*Cw̯TULU%TLUI/\YenUwj*XLU%I8~[sψ7kV!kV18XfpSUI$SUg&vYL!-OX MDaA~VSY 8ց$SUI2U5UY7=3ָ"'6uUnUվXҚULU%J*XHӕn!d?ښUh:kVg*XLU%I8~CܚՊI5Ld֬B׬ V_^4<u`*TULUM]zqmkVe֬5[׬^9LU%J*8~E]8{ڠ9Ooc֬B׬n\k<u`*TULU񛈮Y}hz`tq_׬NsfFHȚ՞R5SV[ 8ց$SUI2Uo"O=3[!95csd !kVoi0X/._8ց$SUI2Uo_xb|J`̞f9#e%7fȚՁboj0X}>ק'k<uJ2U$SUp&^pߌmnա)Y}j2Ofoj!58RpTTU$7]ZfN`@f[~bMbS47$djN Y?MX 8aj*TULUXz f9֬N>[{ؓ׬i߰f<urd*IMD׬>3[7 1ˁYܱC`걊͞ 쓷kUx;_{y0U$SUI2Uo>4SS*]C9?fyRRɚ $dj6I5;ons<uJ$XW'=iykV~'oz8cx[O.f0UTULU%T kV̈ogĦO}:lCj2~5x憦fz֬J$Dwj%Y;2z9bBvpۦ6Ɂ5GJ<SY}>Ob*Id EtnO/XfwN Y}j2OWzڵY5>g<`*Id E_^_O ݪC.UOSo\zjjkV0U$SUI2UVm-mkhflnOԸXmXyRR`5s@? YxkEӬYTULU%T .UcBObBVkV_'YoVAfJ4`uw}zd*IQt{ ^-kwow!;yxO`j}k,d |ūY}67ɚULU%TULU"_kVO*]­:fuZޤ`Y]@ȎUlNqizpɟ=d*d*Im#֬ݛ/UX]wd dx?ȻzC/J$DWkVǧo#.!dm YMi۶?z2‚kV1LU%TULUM )p! YmabOWfEց5&_/?SUI2U$SU(ŗM Y}-?YV֬NV? {m_yN׬֬`*Id Q}~KՕ3'vuYѢyj{X ;SshMCYy[td*IQtǵ YkD35\CȎm?Xܱñ͞ fY׬7<`*Id Ο>˗^Z=o\:w`kV!|'jfuz2-Vj"fu|5<`*Id ^U{ 4M_^Xb5քkV!ݽo.׬ڶښULU%TULUTz֬BSaIIլO@5//p҇^0U$SUI2USU/Vܜk\ǚUmnHm?Xmd'Y=kV ^b0U$SUI2USU"fuW~?kV)x>7% =ߎk{kwii*8ց$J*8~5EwY M&w_ګs &W5Xd*IMD{a{$kV)Xda`~}9ߓ946'՚^LU%TULU(VMŘչ9i֬BN`jz4kV!!Čݾau`*Id Dѧ'k'5.͎suH;@kVU?s9#zY H.f5mۃ6y2 fLlժWNTU$T=wf~ތkV!|'J55+<_imZifu\xy:0Ud*I2Uo_^6/w!DʹIwX ݽ. ׬C֬cJ2U$7?w=cdRqؚyӬY&fL!Ofur`{F}v጗pSUI$SUp&xxŸ6[ rY ;V9fsѓY?nZ/XLU%Ip㕥[Vo5{"gѐv֬BB>u?VԓmYrpVOk-$SUI*@DR4=0X-d*$#‚`uZ$kV!|+)uŀ׭c*$SUI*@D{Sy5 =<'ߚU߆kVnOBvi=7{^TLU%JLU"f|l5ٱY|$ֽ5 =_gRkVb0Ud*I2U.9up{qYGrcs͓6,_@vl\۫]G3}9.wK<`*TUd Q/.NO*Gn!dY0cj`j&wBΧwJ}_׀xКULU%JLU"?/mkiXlY HMrr`uMC9ߓ95xFkV_s<`*TUd EWNY1!5ָ"5gjX׬v!fHY'A~߬kVw<`*TUd E]8oFV֬Bޭ>5Kz2V-&a >>d*LU%IʚAIVU--m*$dj][O.E/J2U$DoC`꣙Y֬BB>kղe`5k@kV!Q_ V~v጗LU%JLU7&v5:<6oT5cӺwސھj 쓷k̘x޼/J2U$UEY]=/~ѭ:fu͓g3oD!;Vg%?_n*LU%IBD=5CbVWZ:0XMv/!kVdMi0XKozC/J2U$b Qt;nN5.ˎ /B]u+;ܱC`GJ< SHm`Z5oM/` J2U$SUSULU!kV6\Z38.!5yG=cEK{khfq8RTU$ ֬Uः[w+, Vg۵ v=,MYMZoXLU%TTos/>S65s@B>kղe`uMC9ߓ׬קgpZ?:TU$: ֬~4;v-ìY|$Քm<@>>QSXs~!?O/_{y:0Ud*IރqHY޻GșѾ:9V-[>j'+YWC}9.J֬BB Eά8uٙCn?Xmղu+=_ɚi)gX u`*TULUvAv,kVgO{8Nb5X$SUI2Uo߽|dRq)f9 7OJdf9@?7~]7Z uJ2U$SUh~؆ii_>=Y[1!5ָ,;`|ƅjkV;wpb'oVAn5'\|E/`$J*] 3E_)sY<0~<)U<ߎkva >>%^TSUI2U$SU" 0X]u!PW={53oD!kV_+f*b*Id @DByYn[G3ckNfXձ9Y֬BB֬N˛4G"`Xa5` d*IDԕoM!ָ4;6cxﳵ]CȎUlޥS`ꑲOWh^Vj3kVLU%TULU௾|qс:=+ܪCޭ>?Xmղu+=ߑa[<J$Q?].kOܪC>>Q3-oR5=+;Գʁ5o=~5d*IDԹ֬ψ=Tg*d͊V-[V3ff;Gݝ+/d*IDѕoVLH Y=G]C>ֽk`m<ө ӓ^LU%TULU>{9{sn!d~ U<_imZfTULU%T#IKݪCkVgf*4H`?kVLU%TULUnpa'q֬Bd]քb9 s<J$EWNY:>%fuфLAB>f2Sbe ͚USUI2U$SU"vMOVBvPNfF׀.Y@B ϼUIVp{xy0U$SUI2U rw+2[ujaA~$kV!*4=fĆ֬J$QyvkV7d^޻ܪCJW֬N{~OBv SzX `*Id _,{ Дܩ:Z M;X MAaA~V֬J$W:4qW/}XY0,S ƁgݪCkVdY Mu+~43fM^LU%TULUT"vό.I_VB۵ fL VYDYݣ7YE/d*I` Qte#?[3w'o׺XmXyRR`j]U'YQ=kV_ggxyb0U$SUI2USU+,95fu-C|$ݽύ=)mZ q߬fof:0U$SUI2Uoښfùۿst[ujNfF׀9xu[<-:0U$SUI2UoW V notY=`uZ$_CB ~5'ЗW.yyLU%TULUTcό.x}F fU|$&9ٚUH uՓƌ-8X^0֚UTULU%T!kV&v Y};vfkVw>Q33oX:ٚUTU$T!>i?u8{>D}$5ॅ<ߖu+^p֬XLU%I K+'JsfyG[ ;qpW] {[cJ$7p{JvL Y]{]֬B+ZX:_{235&fOi0X}ikVqSUI$SUpSf*4*6w!~<) oiᬌkVqSUI2U$SUpz=ӇZ M#{}A Yڻ}dž׬PTU$T!^kVK׭p!Diy?@kVxjϚUTU$T!kV77^zߔoHԪe5ݻtff߰fyyLU%JLUOVo"ueY M6֬B֬f6Zz`J?kV1USUI$SUfuwAf禷>RVVHnlNV׀.Y@B֬Lip5$SUI*\ό.xIz]uMa1#p !kVg*`*TUd ׼kV5Zl?Xscx25v Y^0֚U,vSUI$SU"үl|JqivlFN_AYd' Y:<֬J2U$W>ނ!:\8$ҭ:$|jU rHԚ՟ nqkVLU%JLU/\^1=0X]lOUfuBnG=kʻwJ]ߚUSUI$SUU[F65^+#9HGrmY&fuJkVLU%JLU?;Uz ׬9bH.f5m#e% ?5LU%IpKkxqENmYD}$5sfy3Y0Ud*I2UkqO.+ݪCB> V5UH];ux5LU%Ip*G4 Y}`$ASX:_wd !_OkfkVLU%JLU19#z=bf;6d !_gĶo0X}~r}%/LU%ID+كRNVšբE|A w֏ mܤ3{7yy0Ud*I2U ^\5;S+5۾n!|K g5^Og׬H5LU%IDeG65.[>c)Y<} $ ۺ~Ú?k/LU%I=xxU߮b7*!fu]CNܕֽk`MrMŞ $dv75>7ݿVyy0Ud*I2UT/\z`hesnա)Y]Z8䐐/.\^/LU%TTSU ՗W~fuk2m]֬N8 f{ԅ֭֬s҇LU%J*@=|dR`~|:$|~}N@_i5 ~9/8ց$SUI2U5U+1ղؼ=vYԯO`5mۃ6y2OޮoVA5nd*u`*TULUMUq"vN{y}uGY-Z4ϓ|A޳M58ց$SUI*fՃ~ƌ9X=XcJ2U$ |D['~Xp'o׺Xnղe`j]U'MdK|v጗TU$7EKeSkV}QN Y[ѓ׬.1֬~ۥj7s5\4f5lY֬&ٻ`*Id ut`hP/w H.0X-,ȷfΧ貤p YLU%TULU ξX}d5sZ +Y"f5wډ<Gs23&w Y=rw5$J*DԟoKcr=bf5mc= fd|7tdTULU%TyYY?:~<)i9 |A7u5UnJ$@DR|7+}ȭ:IcFZ Wԥ5o=+<d*Iо=k@μ{Ə =<Avl\X9k=ٙCcsY=%^TU$Tze=5ӓ[u#֬N3Wv>=ύ ZXLU%I DcvL54,~|$Djjwod /'fOifŹ>w˃cJ2U$7E_m!ָ"5gjXUUu?XmղeijO·|1֬NyypSUI$SUp"f9YD}$X|OWZoj'5ln{<8ց$SUI*8~fV48^6(t ꐐIcF?~ԓy矶mkuxlިtkV!|*67^Ԫe hѼkV~'k<8ց$SUI*ߎ߀5`5Əw·d\zf&/u`*TUd7Qk kV48iU-150Xe*]C\1U:0Ud*I2U8_>8֬>{{H·eʔmYyL VܝG<$SUI*Dѕo>=.5ָ4;6wԀ]C*Yݵؓ-3#3%V`=/SU0Ud*I2UkVj^F>ǭ:l9Y_^ ji ~%^LUTU$TYCZݪC>yv͓ƌ9$djFVͱfb0Ud*I2UjfxH;ݪCJW;vݽݓ׬N͞-+/$SUI*DѕoO5.ˎ /B]u+;fE2Rb[7v}%/$SUI*DՋTNk\38n!ƏY]Z8듷k=ٖu+~4`ܤ*J2U$@D~~'[u_aA~`j*$jꀴKӃkVOlXK^SUI$SUSl|$&9ٚUh"kVY}aƐ?;0Ud*I2U(jf4;v-ìY|$;-~&9 쓷k,fuw~z`*TUd Qt҇?u8/}]UuHGr_^ظ6fuP>'d d=[pܿ`*TUd Qt;nY;j |ݟ9`MŞ fu)&^SUI$SU?9Պ؜p$76'+kK gy6-Y/$SUI*S_ՇifݪC-3yRR`5oǏz8cthop?i*`*TULUMUUE G65^邻ܪCn۔Ҷm`{NuU l ٷu Y^x5$SUI2U5UT rkVe.!d+O5fuoFO `XIVp'k<LU%TTSU(z}8uمkV?t'+YgJ]b0Ud*I&RDTՕJ [ M i=cͪcJ2U$ 5U=cdRq)]bBVRV{8˓?~4ooue[XLU%TToݿYS6]qYvl u.!duU Yݱq'oVAzS.:0Ud*I 5_)s H.fui,_Cvl\3M`곹I_LU%J*8~DT9zGrE֬:[{ؓ ܯw5MfձLU%J*8~Dԩ}[lkhflܩ>)+ifғ 7.c֬:aj*TULU ~t|jqivl><'s@j-KW{2/37fձLU%J*8~W_\:=+ܪCM8.fh<_CB O kVSUSUId ʺ{evm[uY֬BSΧ貰op[oͪc%J*8~Do5\>#PA |mJi֚Uh _}[oXم3^0U$SUI2Uo(j#?~:2k@kV)Xo`;z~z˃cd*I EW/}њECڽ}9OL˛d*46woӢx5uJ2U$SUpokV V',)v+Z4ϚUh jwo~֬:aj*TULUk۷y{5Ş\<ǭ:ݻtfB]u&L\z>w˃cd*I 5kVMȼPWbBnkVذ|qFJlfͪcd*I 5?(nGr֬BQfEZJkV߫x˃cd*I|S+pZ`"mU-?'fuҘ UUfqIXj$aޣ.vDRB;$$܄P*2(BA 7 (5%.rԜT4 - r<0; jWK?ω37^z:}JuK{^x^NcV·gի5n*$oנ{wfժUUdU^گ3 ԱMfꆒ yYbU$$YU>Zun:|,{[V{ܐ}Ydhv?ɝck_:$䮭WϙUHʭkڷ2gV8wƇJUU@}kzƿ5~.'e{ogV!,9nY}oc>ǙUJUU@Dά>u{7$ۿ; 0N5t!+[0]sjY]*XU%ɪ*IVU;.߬LIDAT%@ WzrX`X֥YUM}tL`X-䐐 os}ˢ6άUUdUoϬ_pV Y3{άBv./qauf{V@Ȏo_?UʪvfdU$*XU"?jifϬ~RËu뻴oyS&x2IE#3RY$YU%ɪ VU g>]}OW>-ݙUHșzG*oim>7xfVz5} ضqݘG\RgJUU`U|Y9t=c'Yp}gVjά$YU%ɪ ~osp+zufE ͛6ٹēYeά~$YU%ɪ ~CI}fX6/!_ YMJf<@Bά:WU?aU$$YU|3k |ᄬάBBάנբܿÁϬސQVvfՏuXU%ɪ*IVU7ܞgFpZk[uH39: rHԙ'_UgVXUUdU?~ztEXO<:o7V[hV2OrfuP̪밪JUUo^??J4Y}n}S'y2377WU?aU$$YU| ׆ϬmG!Iά>tEHԙg3~ê*IVUIv\<_aauzkxYo!ά)b'!{{OpeYcVUIJU .xnה<2[uHșME <Hș{YH+̪밪JUUoe~MgVG!|vo}f<_}C]gVXUUdU?~Puj7plnSgV!!gVG 3pf辡gTx8vɾά$YU%ɪ ~_O|aqvfx9q\jNvod 3-5yvfՏuXU%ɪ*IVU7@m_G]gV!9Ϭff' 9:֙ݣ ̪밪JUUoμwEάN{܋uHk+;ݓYHY|sau]7XVUIVUIUDџ?;Wo-ުC2YO3m>ر XVUIVUIU]pZo Y-b'YYϬlQRYbU$$YUB:&QՍ%əUJUUapU1Uc4xg*/!ά>2^gV!|+tf$*xjǓ럟:dӵϬ+@o.ƙUJJKWn)3FϙU[kάnlϪW=ى}y3:ẏJJ@}ήe}gVľ^CԱMfjɌ=jc/sfVUVUIVUIU?v|_u8kƁmXܿwp^3F  >8ً|~$$ɪ DU |3ٱg$)gV rYYj{c3oO*XU%YU%IVU u+zgV:o҅uU C}fuάUUUUVU μwi3niI/!d[fn?bfOd |Fd<:3`UdU$U+؉]ToŗefќFmY:Kr5(pf,p d-xwgVW-*XU%YU%ɪjU4x/ݪxj珆pzVVWڱM=7;f1xfbgV*ɪ*IVUVU"W8y %-U̓طa~3;~*XU%YU%ɪs0VU"̻{IY}n'bBvpΝ@<< gVv*IVUIU+jթC;p\n[u#6*lv>M%~$$YU @DϬ:z9q\j,?);o'!{{˞YŔU?aU$$YU ^l}Is9)/pꐐ35n?fn֚% {w3ʏuXUdU*Q/3 o!~XMŞ:ɓ͛2!qՍyg밪ZU%YU%ɪ VU"zfuyάBܰgV*<֥ 4oX XUժ*ɪ*IVUQ{Y:άB23y|3ʷy2of>a_;:^9JJUDፋV9 `ϪWgV7mRz2ӕ}fuOQOcVUIJUDQա}7ufedά.=͓?>uV}P:dU$*XU/ΝY@wgV!I~I辡3F =7Ȼ4obgV{x ~ê*IVUIUê3(/?umz-sYڽ{ Ϭ$YU%ɪ VUBǶrf֥ 3gV޸ғܿ>}x??:dU$*XUάB2p[ Jf<@L㺺o1n>:dU$*XUϬ*$*?p d;\ja؆~ê*IVUIU>:5;VKra ' =:N:dU$*XUsFoKS& tev/̏VScNd |n.5ά~ê*IVUIB?t%@H U|=Z9 Irfu¨άBBάvmQѲyVUdU$*@Dm+ç\^aOAޡݛ<ٱmY'u Y WYUJUUQttך5άЭΒcUU,ua5#=5K<HWs>xfun8~تUUdUy&3OsfeuVWttO7QM.ư7WfOoA$YU%ɪ _;nTWNҙUά>t'p de d6i0k }x*IVUIDԎi#kYݱhO7HKVs:u8V͓ܱSfqgV{sg|x*IVUID]k<:/Β=:+x}ڼiזy23{ xXU%ɪ*IVU(}fu)әUHșՂa553y' 9׸ni~au݀UUUdU/ΝY30UOh*쁊˞Y=نm.^>^XU%ɪ*IVU}fuzkYDYM[~׿9X31*JUUQttךҚgV_VgSEުC*^[ٺeuBO?:d@!gVՙUdU$*@DȻK6k\Rԫ';XUS6z29wɨM>?XVUIJU 8wf탷6~ojo!d+ˇ Vl2?U㟏kvSXU%ɪ*IVUWN[쏼UΝ~mzj?{'!ܺ6C7Ϭl3XU%ɪ*IVU:Ɗ=covK3{gTx!۹y&jX2OBvb*8}x*IVUID_ƫ :cjG.$$ɪUժ @޸G3sSf/AZy&jsy'aժ!x]>xfu8~؇$*VU*QTji~vsfrf ';m>VofRgV7><`UdU$YUZU UV |F7yM!;{b;kY/:@ȫj͋dfԟ5xf><`UdU$YU0xt3o7E][^ݛ<sUgV?xa3XU%YU%IVU u{ԍkB<ogV!%J4cV3VUIJUW^UPK3 O;U5I~07k\l3VUIJUQGG,S^~A$ə͋x2XUCvZ,wfժ*IVUIB/uI=HCJ{7Y}\> ;o~\XOj_=QϬn>:hQnάZU%ɪ*IVU g>]O_u8-/ݙUH3} >@V̟yCFgYJUUFpVN/Vjmo.d 39ڌkw3N`U$$YUK:Z2~o!ά^[^ $RV̪UUdU N%:*$ə):{ÁϬvjX3m*IVUIE!_u8[#gV!Iά˙UE Zן9m6,*IVUIEWmOƳrR,:$Ù-9 άbUdU$U=bo_2VjFz te~4YgVdU$* fOEO=}zg8 IrfOA^ۀ'*$jI3VUIJU_\:ݜY$%C}fտ 9zCF.o>|VUIJUQۋG*xnnlުCBά^[^ڱMWz2.ێtfժ*IVUIUN[άB\^8-7eBf떗T2gVpVUIVUIUEb[iᵁ3t:]Y:$Ù:ʷr4RUkX5܇$$ɪ VU?v;pJ~#{6ypfa';<MM3feر }xJJ`U{gVSw˂gUd8YŪ3oz]{]<_ÃUUUUdU*D{e/ά4ުC2YmbLOjBά2%]ʇ$$ɪ VUbq3w: Irfu¨άbUMnT5u[f$*XU .Z.:|kUd8:wv{8XU 2v Y=Z6χ$$ɪ VU谺_u83'uעyުC2Y&rZOjޔӡ͸ZgVuVUIVUIUU+H Y]ÇUd8m(`UMș{Y}VUIVUIUUk?XU%YU%IVU@yu̽f{OUm]ƍYŪp7eշsfժ*ɪ*I esI=ڱ7_+yM3RYDm9mo>~Xm*VՄ|5}4Y>3VUIVUIUݑw.pfsYl9{ Y-~|'U5|Fvn<:3VUIVUIUxށ:ܣ33' C='U5d-qY50vhTgV$*{WIuflii37y2h>yT Aֵ3[>Z1y3VUIVUIU׭+gV,G!Xߥ}a55+;ݓطa~3;~̪UUUUdU }~iߌէnG!IάΜ8ΓMyxT3nwfժ*ɪ*IUN- Ur8 IrfuEHWsjXZn**ɪ*IQsO>zpf5uˊזy27_L̪UUUUdU bi夼ުCBάvl?^[ފ3=MDn+$*QKtfd6x`ۀM@e78qP3*ɪ*IVU*QtԚnvf}=֥ ojQ }xJJUժ @D?7g=8 `37y2c sjY}{/Ν*ɪ*IVU*Qtx=cάB2x{-iiάBNWE_;άBl9aup^άBz2gV{x >:&5V; ۹䦦kY-۰ЪjUdU$*XUάM$YU%ɪ VU 8wfݨށ:㺣7x9q\`X_r^OƪJi`V36,JUU`U"jǴ33rRw2[uHȖ --~X&rZOƪJX)v3=[Ź3VUIJU@ݵfi3t*$jҾm -mC|OƪJ_=Zݣ=EVUIJU@}qo 3 ; rRcaTdl2?U㟏kvSUUdU*Q{ybάBB?AZZٺ3VU9ڌ)xfg3VUIVUIBG/$#oX3άB⶜Nii[.dľCok5]pUUdU(ݑwkxUAʌvpfrfup^jj,IU͜8mzWV7܇dU$*DΔ}?'U{d8 1Q3<߿`U%_ݦwgVWƎͳJUUQ4™UHJg]ڷ={'(K/"jz\^Ź3VUIJUșUHoo\ٱMfv\Oj!h<}x?gV=te3)5T4 u_VcXp{VXU%ɪ*IVU˞YUșUHFVIOjf>zlB$YU%ɪ Ub_|[uHȖ --~Xm޴Wz2ʭk;yEYJJU%Gxۿ ҜY|5{]JJ`UU)w: Irf{7hd/v1n+$*XU(Puj尜W>]ԙUH yY?ܻœm^EzigVoYlUdU$YU Dс3?qfd¹ -޸ғ?{ gV Y 9jUdU$*XU+yuL=@ܱ>Sa55+;ݓv-Sў?8jUdU$YU DeϬ>3-a~ۀU͜8.;#e5uygUUUUdU*E|W>٣?wb-sXU ن-?)xfJ$*XU6e3$6/Z8ڼi,df׏uf3Fsfժ*ɪ*IVU$#ſ5~)ΒUYҾm<*!;~Y;άZU%YU%ɪ VUd>gVu 3pfhU%|ŏ0$/xfVUIVUIUU UV>éM?~uo!dgTLyxTjO*vx8VUBb& ɪ5}ӎoYlUdU$YUZU^<:0Io!|J>sy'cU%d7ui;jUdU$*XU_\ޣn[7,4[u_ֵάZUI'6x)k o8jUdU$*XU;{N[)ܑ#$ۀ8нw,plãd Y;$*VU(Puj_u8ɯwV7g՜Nodf feo7XU%YU%IVUmZ땟xۺtaFzzjk<*YjgVߞ::jUdU$*XU0_*kj% vo*ɎVWϙU*:뺔U5cz~+gVdU*wG]'#qi~=Y,6x`j}C}e7YJJUW/ΝY~OW>oVW2kՋV9jU%|;d6̙[[U%YU%IVUa׍yp%@mὁauNnꛋYسM8jU%Z- Y̪UUUUm9K ƿ5i^ŏypάZUI3*oYcVUIVUIU賃XrKZ[U)ŷg,bBv@;jU%̙.xfr#ÙU$$YUّ7V,qM[㗺,8[upf|fo+ϬZU%YU%ɪ \7^ܧQ7rf#$əIE#}?>}̪UUUUBթ}?70N/tfcq3pfOkհ~qZgVYU%YU%ɪDIpVNK{ۼh3 *Ҿ蛂gV+&$$YU(:q҂o_^gVY:]Y>b蝃Z,ϯ1)*㉣VUIVUIQ_,xUAʼ#8 Q6augVΩVUIVUIQtT_utAӏ*8 U` <6K[U%YU%ɪ nucqI=o˖gVKŊUm( %[yh|w/Td[U%YU%ɪ ImVԯ_+T֕=boWg1ު3Wj^5lVdU*@t}zgάNgVիgU5 2N5oey)ɪ*IVUDُ>xoa4?sOW{!;w?ZU sUHiOMe8pG"%YU%ɪ VUxjɰU.ꥑO.,,tfUUU`UW s&ؔ+2N!>gV\{=Wծ=bU=vǓ2766zdU$*XU԰ "gV&9 'ϙ׶lSU̓+xf$$YU  X4۪jX;W:;+**OJJU^83prY1g/UsU}CάN4əUIVUIU \u;.YqHgVտ! USFjffCRUU@i]:zhgiۣ^̙3_6Vt{~чYתއYt)ɪ*IVU \k'^9:;Kgyrf3,wٙrdUUtiv3 2Ruwy~ϭ rf5ޣ>۪Yw]䣹 YdU$*?FY3:˙U8Iά{+7r9*ɪ*IVUVsfN3~ӹFT*'ժ3dU/l]ߓ=$gV|glU4'f/Fά odUUvD3/Nj'ə~r}:=އY4iRKKNIVUI:gœ견]; άBz{jzzzccGNIVUI_ }A>3ީWry3?Y|e;W:;VSSSJQ2/_|Ƭx}dXzArl//֠[N~}M_UM/Eά]SdU$YU (mfu^F)ec"gVWjwmwߊ|4w^^3UUdU*@ju?WSSTJXUzdթĝY51ș-ˬXUșՊ UUdUŪjU׳FάfL'~~7o9,=rfhꤏg֭W/:_,gĪIܿﲯ^jkkPɪ*IbUO9˯]hrfc;Y||f݊]ӧwXg{syozbU$<1몫rfUJXUzvجș*wӒs?w +eTGǨϲbU$֭w^?gUUdUŪk&oo겳:3y9ke3^88/m%Ū55>ʕ+JVUIU*zfșY}/^۲1rfbbSc{b;A2bUkiu=8*YU%IVU^œάIrf{\!{X2k_bX,GݙU8UTϤcV:}I~*z{_~a勒UUdUik;L~_ۿ#~~wOn>Ο}ޝ?y}>})~w/RcW3̿g]s\5>ˊ*붮@3'Ԛ79?PW~vO???PqMuyII͵?[< UUdU9qN]n8??>v:KrQ~ϝ}>woxߑoK.ϻ'o}wOn>Ο}>ϻngVo.Xǻu:DF~:x.MTqm㚊.@m߷7=S~Ӎ]]UKLVO[y㮋կ~TJXUVUdm}yj?u>̪UժjUzOq̝9fϧUUdUŪjUZUM&ksC\U475,=ϬZUVUWZUGș!Cڵ dU$YUqW]UwUUuoҽI&}>}]_ FάNOo\4]UwUU[Uꪚ{/*VտvHdKVʕ+=JVUIU׳-I;:/#e-ymY]4Фb?6bGϬ[᫊Uo݃ c&MrfUJUժ O-D˾6 Si%3ǿ?'1t_R`{S{Y=|Tɪ*IVU*wj\u~Ā3'Cɿw}=v۹3 Y*YU%ɪjUµaJ~3̽Tܿ/naUأsĪm^jEE7Uɪ*IVU*?oIvh6ObnޱXQ,v=u}-?񕥾XUOu۷OO;*YU%ɪ 5\9:E3-eoVoͩ=?scѣǃE}pq7RSSW\UJU \-IHV Kqf><;zx:3.êzxvգ#?O󛛛=JVUIߞ-*|OI~}{\*VՓP3ÇwfUJU \G-yά§kӏNٝg 8q/lªzڴnM^榥m۶sdU$*@* xf b=ϜX8~R|mOϗ߅gV*//*YU%ɪ uJ۟Y]7sU}ά^}" ZZZJVUI/l]4wӀg(XU\U7wșաC߿ӫdU$*@[rMzdX*U5UͺUGά xΜ9^h%$YUҟ`xǎ ƍsfUJU \YrE[V`M02>;dU$*@:=:5ʵj۰o>jZZZUUZɪ*IVUpuʵj6YVYUUdU#w\h7κ0άXUUs>?ęUYU%IVUE٧%,Yk^=:rf㰬$*@\=:7=Z0gfdXufUVUIU hM ec3#gV'f|-V`-/.ݫW]<˪*I7ʏYv_/4~XUgάʪ*Ip+4auޅҟ`ڼq9*$ɪ 1K.4Fg; `U SowfUVUIUciaάXUbi3JSDάyArl[U5Xwl?PgVeU$YU8zM)a)Kn3XUvc]~ άʪ*Ip̟_Z33VՠNϬ666zLUUVU@P[ry3$V`mZf@j~jjj'˪*IVU*@N>rfuFiU-%V`վDW\IYVUIZU#Y-JY=&c U5Xo7]|xSSeYU%ɪ @[3w֍VjشAՁ>䓞eU$*x#gVܲ|w'2%^eU$*ھfIIV3~f;ƺ"gVZZZ3˪*IVU/auiVÝYlIșCyjUUtL3VUm9VSSYVUInDJjժb; U5XY]vgYU%ɪ @^|dEI"=:&S U5XY-,,tfUVUI;MVY8 `U Y3鍍^eU$*jnjX8vxݣpfZUCjϲJUpmDauFFϧ~=߬XUxf5WTTxUUzݲjIf7XVU5XY4i3JUSΪgVg\=ҙU׈uu/xСάʪ*IVU8Ϳ)M;:/#l -U`}ؙVҲJU@*9:kEάVUB6{șÇ{UUꝆ13GmZ\*jMMiYU%ɪ @g33N}tgVSSS˽N˪*IVUU胋OMVdwx `U%XY-((pfUVUI7_ܜ3sFg9 XU Y3\rI}}gjYU%ɪ @[/FάN6VU={xUUTh7κ0u\ `U%Xάʪ*IVUzDܙU G9*$YU ay9GάN"ݙUJ:Y=|WkYU%ɪ @JfEάNJ xqgV*juukYU%ɪ @~eY&J:YޮeU$*z:Y]=gVJnw]IZZZ`˪*IVUu`9eYyf*!{3鍍eU$*:rn53GuygVjjjc˪*IVUµnR^dXR `U%X9vZOٲJUp|dEIVauSͺHXU [3yyyάʪ*IVUհ{œ3ɱVUB6{șCyӖUUȁc.|Qi kV+rfʳdU \k'߸<<|]?sMVxYYYkkmYU%ɪ @DάVUHv䉑a533ދUUdU P4.nD#{ʙUJ֭w^3۶mmU$YUך_ rfuzFϧ/2VUUӑ3m͛7ϻUUdU \U.)J9zMJުcdXoiimU$YU9YlxܙU$ɪ 8rn7xfu Jشa@jZZ͛[U%IVUµ΂剔auΰSY#O.))immnU$YU &zDά.w ޫYl3\rIccq$ɪ @JK>hkkș޽{;jU$YUoQꬌsm?U`sG"sXӀ<[U%IVUϬ>0f`U%Xl(&gV$*Sz>jy"_ufM3yX̬ZnU$YU; K:?rf΋zcF;`U%XOog'iii555̭$*0%jZZZUUx$YU \',TsSâ. pf]?ڙU$YU~XHIV ^d wNsfժ*IVU௫ya)Kn3wUvc]~ YJUx)gWjy"6ÜY*lҒԞ=?gVdU>Í"gV'%;gV ZA>gVdUOOYnXߺ| ':ү\>jU$*pܪ֕/lwfuAFJ|XU!Xl(U$YU#DάάU`̎gV$*p_^yAz3XU!Otxr>;yXׯ_MMUUdUp;vꌌӪV[ :_,yX+WJm+f$m)7 znգ#gV $*0{3qfY>|xSSUUdUp#gV9b`Up?as噃%|'$*k鏿9:-s c`U`ս;9,yXMMMJoYY8i?Uvc]~ 3yyy/gVdU>}<.͊5z3`U-)ǓF$ɪ z`ˇ: VU M3iiiVUIZU \;˲gVV xf5YU%ɪjUpmNάNTVUVgV [ZZdU@^ά'bqfNϬ۷Ϫ*IVU*p_f\șջG*XU!dϬ$^:˨Uժ jQQUU@~xAfauAF}aUBN3VUI'7^ì/rfu,gVZU!dϬ{wﶪJUԡ}.ߑ3 lXU?NϬn۶ͪ*IVU \%ה' .o]>ƆUVgVNjU$*'g*VU*pTgVoMϬZU%ɪ ljvvș;~3XUu<:tz$YU@{aΉY+ϫ;UWgV+++dUµ^nXqVܰjIIUU@^dq3eYշ~Uۍuy㮋 ---VUIڷWӀ}g *3gDά۷Ϫ*IVU Pu<:mĀ׷l0aUUgVdUµoG9N}|*wN$x̪*Igj?ՔLT<ܲaW㦼 ,Y}i/*1X7>ȧ}3VUIW=(5=hU[ڳg򰚛{!$=9* N7Ƕ͕3iiiUUUVUI:I\10ehʪ1_z즋#}7݃ ?A}]8jU$I$INMMM999;L~`u`x<9J$I$IgւȰ:@شa@ɿ8ׯcjU$I$I`k׮ݻwqF{_~ѫ:{}DjQQժ*I$I$IvޝvܻWU-@n~':477VUI$I$I*tKa%ժ*I$I$IZ[[Ǎy;kU_U`=a]3۶m_O$I$Iٯx@CFGWR$I$I$Q3={nZƫ:zv|a5??%˨UU$I$IBqά@E{3VUI$I$I ւx<*UgcZZ͛jU$I$IZ~3#Ot())immVUI$I$I gVWݷԫ:zG? yyy---VUI$I$I K.ęUHbiIjϞɿ8ZU%I$I$)Z[['MV/E=^ֶ͕oh$I$Io*:Ԫ*I$I$I:ֶm*V}m޸묪$I$Ic555 >ܙUH6wxhΜ9xÇ>ܙU$I$I$}h---ά`U$I$I$*))35>$I$I$Ik׮4gVJ$I$I:t誫rf$I$I$I#|1oz*I$I$I]6555yX=zXU%I$I$I={ 2$rfM`U$I$I$>*VUI$I$IJs3XU%I$I$I֯_߻wogVJ$I$I۷/==ݙU$I$I$}h˝Yy8 d0ACaJm/-hc% "X 2)Mi9]< VDC\aɊB,XaH6L1F IDAT v _<hǃ_y 8*IY$YU` άJEϬ?zē$ɪ MgVK}Y$YUi33S%IVUZϬrfUdU&gV%IVUXÇ3ê3$*all̙UIUevvۙUIU*cgV%IVUX3$*,UIU̪$ɪ KpfUdU9*I=:yﮇ~IU3+Y$YUi3_^?/I@zfuOg=KYf&KjB9z%O$*q:*I[%I244EQzXr/s6IU񎎎{ݓ$IVUj͙UIUFX̜Y=}1@dU`aaT*eR'&IUrlzU@dUR ޾ $*q޽;=FQl$*4 GQVKY$YUi||#=nܰa]#$ɪ twwwzXͷ];$ɪ A(tS$ɪ BT _b@dU\.{ݵHjB9kfIU8wޝV(:{@dU(}{ן$IVU;::lz AdU`zzzӦMauʕάJbMǏ1$H$I288VK}Y$YU\.ygV%IVUxZV(꺵kwn$ɪ q(Ξ:jiA$n=1g]$*\z5sfu-3SIAׯ\1HUƞ={2>q IVUipp03~3dUFGG[Ϭ۴A@zfu57MdU8bzX3VIA$άJUxrEQzXv9*IVUauʕv!IVU ;w̜Y={gvIXj3dU.\궭[f&dUJ~n[ IA^߼ysşFldUEϬ~˄$YU`dd$='$ɪ auuW׿o\OHU8I˝Y$*끁̙Ձz5P!IVU ͜Ym3S IAZ]~}zX]r/7o+$ɪ FX,(Ο;c$*,,,eά߷Ecn!IVU rau׎OgMdU37lpfU@ӢgV/4b$*$Iߟ9:wάJUh(=n%C@099Y(ꮮ[ IA===3OgHUZϬ:*IVU… \.sf IARɜYmo_̪$YU3^yC@Ձz5!IVU (˙3vli;$YUZfά S$YU bV(:C@$@jƜC@p|>Vm235a$*j̙ukTܲ|HU=dU$I8̪$YUwi=k5+$YU *oB$ɪ FǙUIo$@Emt3$YUի3[6:35a$*T*̙uk8*IVUhfά=u4"IVU HdhhhٛEc@"IVU (˹\.=~ٶ5$YUZvvv]];$dUz^,jn'YJ$ɪ I fά=?HU2gVwL$ɪ J%sf}EM$YU ;wfά>É$-NN-O.IENDB`PKs_: layout-cachecd`d(```p @S.@L^Ji00LA"P"PK;LPKs_:ObjectReplacements/Object 10 s qcd0dFids202109@! F KI;*T8 R5 O Ȳ[I~br2$7 l A4Y-4\ %EI9;0Cr-C#i@6i*~BMzSVZs!HÂ6v/lieʪXavT aGIgTv$C<22 . Vj8"$TϜ9eΩH`E0PK.rIPKs_:ObjectReplacements/Object 11 s qcd0dFidyBn#%- J(5(TMg@!$37X/\!(?71b2 @ 31gfv@ ddhbVghE@ !FҔ =`19 l b.I, MA hU J H2\`s-/@qCo"m@6@zV҂ dSa q r Ô8!6-f<&⺲p t*fGv|LeM``G =怕H#ɥ3g79Ib;PK_4PKs_:ObjectReplacements/Object 12 s qcd0dFidO20\dn@vPT8 R5 O Ȳ[I~brT8 R5 O Ȳ˛I.Ģܤ@%ALdn 5YhZ E@R@: #! lF ,ƀ0{,$?CHfnj_jBP~nb`]p1QPXnWF`G0iLeI1#A̰tMǧ@ǧ$">[0‚ZFāt!;uW`N30 -VeAZNe/r|d9He#R6IgPKX`nPKs_:ObjectReplacements/Object 15 s qcd0dFidwq00110X9@! F KIN*^T8 R5 O Ȳ˛I.Ģܤ?@%ALdn 5YhZ E@R@: #! lF ,ƀ0{,$?CHfnj_jBP~nbe`]p1Q@d m@6v 6èlLqT ,؎*氧Tv3r|d ODdCgFXP+v8nv .3fHZ` \PKCI\/PKs_:ObjectReplacements/Object 16 s qcd0dFidp00310X9@! F KIf*>T8 R5 O Ȳ˛I.Ģܤ@%ALdn 5YhZ E@R@: #! lF ,ƀ0{,$?CHfnj_jBP~nb`]p1QPXnWF`G0iLeI1#A̰tMǧ@ǧ$">[0‚ZFāt!;uW`N3T$-. PK~/PKs_:ObjectReplacements/Object 17 s qcd0dFidy8,n#%(5(TMg&@!$(27)?a#tP3-3;f121+5@mPA@ Hgq#342dfv%gM-VK-WM̃ 0 CnWF`qG0igSypW|cŧx|:1t|f!?#,ZldNH`2i0V0ePK5PKs_:ObjectReplacements/Object 20 s qcd0dFid20\dn@2PT8 R5 O Ȳ[I~br% A8:Cd381\7I,0PK߹PKs_:ObjectReplacements/Object 23 s qcd0dFid0301109@! F KIc*^T8 R5 O Ȳ˛I.Ģܤ~e A&BM 쀚Ĭ`uA-") x 6F ,ƀ0{,$?CHfnj_jBP~nbe`] s8|&J\3 qqsT$]dPKPKs_:ObjectReplacements/Object 24 s qcd0dFid130p0109@! F KIU*vT8 R5 O Ȳ˛I.Ģܤ@31L 5?30j#C;] tG0B1ؠ ! ~ Ayu(qN ^2wm@6/uSt. PKRZPKs_:ObjectReplacements/Object 25 s qcd0dFid1000LrBL@F KIg^@q V5 O Ȳ˛I.Ģܤ@e A&BM 쀚Ĭ`uA-") O Č@vXa`YI~br<@u8vÝVƀilPq ,h` 3$=8wؼAfS3V"¬Gt_*: ht\ w# 0E ,aR yuF\6Xt:! Z&c >-݀8Htez!NL@#Y#;kbx;ǰi9`2i"_l$2_е+cAyI1W)6bbnm||x[n*.XsPKi, PKs_:ObjectReplacements/Object 26 s qcd0dFid3n#%-J(5(TMg@!$37X/\!(?71br @ 31gfv@ ddhbVghE@@f)1?Nˀ4Jt N34+L$CpIbQpenR~"vP*) `s-aFױ3fe檳A`d\R PˉyZ;@"(q2SP4nXp w#E/7,ԬNlrEAKyaꄈPΟq|9)Ǒ?GHRBBJ!\HgȜV3ePKE=|PKs_:ObjectReplacements/Object 27 s qcd0dFid 20310$9@! F KIB*!T8 R5 O Ȳ[I~brD?ȟts!?aBJWHi0%0e#Y6<2"iU pYPKI|PKs_:ObjectReplacements/Object 29 s qcd0dFid?20310$9@! F KI{*!T8 R5 O Ȳ[I~br06IդJZR[|`GQ/ `;41s OA?ys|^OIuFrؼ-pv@Nh,-'4GV8 m"> f Owŧ,> ǁ/8x9)'1>ԩX 8<Ĩ(\ 6+p t*.IR͍_R)=HGdܠz-!7kMrHR݁V?( :H;?ϩH` pYPK^XPKs_:ObjectReplacements/Object 2 s qcd0dFid9,n#%(5(TMg@!$37X/\!(?71br @ 31gfv@ ddhbVghE@R@j Co2؄@6/C؃ h[h$K+ssZw%3ar-C#i@6ibÖA6%f<0ŧ8u`+wZ@G++"2*};i{Le=F#/Lp`eNG鈔9"( pYPK/U+PKs_:ObjectReplacements/Object 3 s qcd0dFidY€n@TT5(AUs3tYV`y \X\ o9#tP3-3;f121+5@mPA@ Hgq53\190K ZZ1x9X3uLNDJVʀi *Y#U 0paF@a(b)5D!BM$}B3=N(Hpn \U/TU"aWLfL0 |Nn3$ؘ|n|:>MبWJ./y? ,dqs*.X5ȽPKtDPKs_:ObjectReplacements/Object 4 s qcd0dFid8r9:n@ TT5(TMg&@!$(27)?h*Pd#tP3-3;f121+5@mPA@ Hg++3190K ZZ1xX3uLN:mvÝVƀil=Pq,',h`!;$=8wؼAfS@J+aV#:/ T_ vlINXQ-:9X` s`6:`鄈N',Nk-HDt: v{Htep!N,L@%~l W1(,FDkY XLe *K IjDe ;⁎^XYn pvDi>Ri>4IMTPK7A\ PKs_:ObjectReplacements/Object 5 s qcd0dFid 30T310rB@&@@UU@@q jT5'\ROnD3] [@q]gTw@Y7"̚ @30ö 梶JDެyo0]"0hs`,jsQUoQ;I /hZPK} I PKs_:ObjectReplacements/Object 6 s qcd0dFid'+@, t@I@x*PU 'dYM$CpIbQpenR~ľ A&BM 쀚Ĭ`uA-") ĽxpbF ,ƀ0{,$?CHfnj_jBP~nb`]p1:q+wZiSB. PKPPKs_:ObjectReplacements/Object 7 s qcd0dFidDn#%M(5(TMg&@!$(27)?P2F Y&g[fv@bdhbVk:ڠ@PF?f/gusT$]dPKb#PKs_:ObjectReplacements/Object 8 s qcd0dFidDn#%M(5(TMg&@!$(27)?AhrF Y&g[fv@bdhbVk:ڠ@PF?f@&Vۀl"&\PKIpPKs_:ObjectReplacements/Object 9 s qcd0dFidXY @!& #%M2PT8 R5 O Ȳ˛I.Ģܤ}ALdn 5YhZ E@R@: {!Č@vXa`YI~brct"iD&uL`cbpM f%1>9 YŊHh-akeKl0e#2g6<2"i pYPK?NP7BPKs_:ObjectReplacements/Object 34 s qcd0dFid130Tn@v;PT8 R5 O Ȳ[I~brtX+0p qthT'Ձ ԧ ɑKv0"w*,r.HZ`pPKI`  PKs_:ObjectReplacements/Object 35 s qcd0dFidX~Y:@N7P Hd i T5(TMg@!$37X/\!(?71b2 @ 31gfv@ ddhbVghE@@3VdiV`L$CpIbQpenR~U@;b UR *Ɍ0< a2@ŬLTtP!z) ^6D$MDn>, EZK@bJ6 v.Qj/#K<Ծ!Km}?vt1oPK RxCPKs_:ObjectReplacements/Object 38 s qcd0dFid10<@BL@F KI<'@US,@>H4jN>y. ,o $B2sSRs -Y Ĭ`>fM`f@Fv f;6 PT [@lFRx =`19 l b.I, MA hU J H1\  0{a pU;G44ӬKN( ;pU"BrSzVDJST$]<w6PKe1PKs_: content.xmlێ۸_!"Ʋ%,2l@bhIԒ\o})<})~I)YGMi(6dl:7 ymטqBe;]pqɯxQ7 pw]ۀ!ǝ38bwF#G͊3+m=\G6,`Ƣ+}8cFwϩ[wD(&VqqgѬ׻1oEϚN=ٛ/ᢄs{Y[8F%Ip6iPJ\ m^ DL[6$6{OWx't^md\F"L)RŀTAr~Ko0cVwws@E4zBLs;ؽ;Ωp]ma. y ek40# RhV L6` ,佼a;G.zɳTf#.ry k 1@uPtn&H<_#Fz1m#{ K57~oiB_a{WwNl OKꐆu ?5vAN~r v "|PfJ%<&.`B^z>D\6\Asht%Y:w4COb,BIbAa9u39Dęc^nttQᑏ4e~ׂ>IK_>D wYdf I^$wSm)Ly:'/Hzo%al8`fD<Ȏq%3)sݩX헭{I49 ?oCa+do떓WY;h")6FEa8 c{6z[1c}ƌˎ|Øq+ik1 Zt׎Moi}Cv"ӆ&1T?Uyj ejxRy.JÕJЏiSNDڊrP-LJ)lHVc&RB:We"*['(DҔa(?>?A|cHT"mҐouIm<iЫ|nRy(潣<[)o wXO=lcZce=K/R:|/LBtHO-#P)#LtK+gd4j(P5y@i@ Y-PZ0Xh+Pg?oE}4[1gI5VQ/㐹ݰcTZ1픢mڎ"#ivM)bCK Kuj"EZ$#@[xn;q ~"Nꄜ9uyER훓2ݦg[a[*[6rTTvt+QiM},յ#* km]v;Qh8RuXtNm?\@LS‰]V;u&n$-$kvTo=.Sqi! bk]@A*ZeIKh}:RsRd,)*MJ'U.ئekH*v;&p.Vzعǰv=cԜK!؜Tqd==b,O'sڟN'v 0"F^ |. F@QQhO0l1 ?E,RZe-OtGה0F pZűqfQ8&[% W``Oz4{TΟEu#ED8׭"}`O' ndBC;&VQ,kS9JSĚM юD55z[;hh?kO`T+V:b~6:k.H{0Tdލv&>0=fURIvG;M4ś2GWo>%5(#^v1G.+CUq){5vrH&H^Bөp{>*VIiއ?[ʾ3F_1SV1p3P} "(%9 |-WzhI><I F2la*NhUv|MݺS=b._ e=CLm 7H霋 =3c"5".FūB,K6=؇`#P4{g ^v}p-k S4t%D E[Siv Vi'ڪ&nč;yjG4Tp6훣VئUf@wiwv;Mf* o7jpfG2;kA) Vi'+j-}Vo;/PK.SŁAoPKs_:Object 10/content.xmlRj0S|˽D*Mũ)c]j&dDz3 ߛ{ڎy/;=.#HX53LPp1'h6lQ9O׋n~7_ 50!̒t9 54c0MGPNa:@]Q-B-^u1NJ[;f9.VŖМ*iyP :bqG"smǔ`Y5A)R"e GfC ھM#XQA4^8R&4A:~`$we}<Մ}C0* !M`9U^oـsc`^m KM wl?.…Au>PKt7PKs_:Object 10/settings.xmls:_v:;[tF-$vj< 999ͷET^QcJnyM &2rgʷ7t:jM1ƻ~;[[%H-CעsDַnee`r4MӋsz٬fW]=J,*E)݀ 2Q]VRY)kk?o߬ 3 TVB[#[)1":WWrί`”v+Rh$}6'quyQLlW~{|~$G<`(7шk\?*QkэeShhdCCKb.X=.^ɶ|q*m&FQe2P6$0 JaO@̳02!$(4Ki QSĨ0`0,FB<1bQZ%;l. ;Eg 4t$ߨwRoÐv"I5#ȵALFVI"Ո Նj;qkX1bۃAF{Al;a!1I._ډ˯>!Jyf(ЪJ*}`Ê}TY8ўF,~/r륊 J{_ /Qml݁-ﭾ/?Xn6.n]g86Qm8T 3}iu}@S0ZUf6?cևTP;OW.2ƃ7GH-LHD:끁/ע4Kɷ8PE;?v2}yfchw;jD<[_3k^TC-RT*ڇ ZjRlsQnq T7cg;84oןKЫƠhH\j=ggs?|z>oW{w <_en\@E\)J'p .JC p2e+ZG;8fx;k|qwU C_x(mCmG. 䜯a8; S(}8eAxWa[gC+ =.+R>L4?dWIf 0cY|ĩb[ 2)"9KEY&8 aB^h9>ډ_֔uRSК5-EClH); ((?ۤӺ(E ]0 %&FK|.u ťl9t~ۃū;nG/PKө8"PKs_:Object 11/content.xmlj0ٹLdT:Y2vj"Mf'`0 ~oZk :b7pw,B k$Z LBNG 7쎞>Q3m7. 90WU<7!2BX":xd5 ETH[Ԙy $II}5 p]5Qi9*VQsqza%FKrK,C%6ˈF{%,>{~6k(Qot)ͳ#);̎2ƥ!`E勳+y*% )AE?.{|dNTJeW)8]8tmE"CjjF,P%"K{PKVyPKs_:Object 11/settings.xmlr0s 2CZ hbk=O_ cCmZ6/EַZMEUz^|z\R(&/H΄[gAH‘!dvW>e}k-eft.V۵w.%]GE!'af'߅f!?4k}K! cԬyuM1"m$*yUe:4> I?Psئ #QpyQq&ޑa4tM3*#ՠkY1 IF57Q!o(j$Jbb?}yZR2zIu?^|%ۯ o C [j \*nlc j_v7THܔ8_yuAcڤ5M3OLs8_kLh@S۫vʯԡ Najvꂨ\k?"<η Ux|POGĪ} À2L7Ij(:VdY.a]CI΀YH\]M7XWqG3d{~B9Z@FS>$B-USl( 3UQE2.XG `9 <ѭ71,v3`}"PKvhFLdPKs_:Object 12/content.xml]Ak@۹ǩB P5PCq5.npjrüof^+{Ol͉1}[`+( ÎՒM_,2IQLy9!FiQheYwGx9 bUr(Ķmvy {T- ̍ƶ"p=;;sٌ!pu"A)=D6t.vljH*=NB=Ӽ ,btRty~bt|F燢Q 3Yy&qHw[@sB&6eZHr5'o9Oz|O`w:w(WNv:`jgu@\/.ιhV5K.Ă(x=FdឿrDP;*7#G0'bNy#S&ڰqA++to0DuƄROPjaȄd4F~΀61>MtgIL(¨osl#iWŪM., ``q8O*S\lpކSE%x;մ35ͪ`WvE$*&*"XհۀVQ">3c01_R2fI1 M?^|G_C$H-[j”*~ncL7د][*~^A1c֤5K@L 8a4U;WЅ~5;uAt|*I9ZQ*4t>dTu8EvO9 bJ8 X[(_tw9Չӹ'C"8 @z0LޏʚQqJV)ۺrw>9]GG!d&lagw;'?!m U4vkG#W }Ԝy<=dQPߡ\9fM[8ʂg:S,oj],3}m:Y, t]p% r wUf(g%mR$B-USl o#UQE2&"Q`xݎDl,r7[i'x M}p>tPK1MdPKs_:Object 14/content.xmlAk0Y(2PVZu0S2v iHf'el}}}}VAz%y$yg_f1ZD %\qT(m}tFOQ ~pA -L鎺(0H=;(0A|=F8CX!صwHE)EHVZ`kfd$PgT`@%*=n*p eKp0$ϼ"HIаX0zM>%+w֐p`ӥ#k~,|[Z>OLWہxV3y*_;{@T@ @е .B35b#?X_pn^ު=]khRPK0~6gPKs_:Object 14/settings.xml_w8Sxxӊ9#y 1;x TO? ]1g'@B{on.!zeG[v*D:$U{D!Nv|Usb>>fEC@# pHDnNh5 C 銕)c4`b[򑡡g),zhS>K[NGSH=$`<q1J˜ʀƐgfhs>Dia 4JZJjM+~M+P.o%!Џ| I 5Cj؉&Ռ 3QZ!&T# TP!o(aIv$LFDb)b=~hUS\~( Q-$S5CHVuPRTj,bwϗǟ/5L`PZTU?nd1]:mmk.PSu_䙵gtel[o P3k^TCBerAP{w&m3lkSQjs moJ60/sMqo7?kh'Fxj_.~O{U!n۵.^  ~'` ]aim~]k8n2@d-ףm2H5R: Ø(M]uqGɥ6$#l'p>NNy2sr9ǚ{ &E ״ze{Ij#Bc:Y,."~~IAhd~$xqLg Q)#zt<N%Ͱ .4brМE9*)i͊sЖ!6oR}m]uȌyY>,LpWyLp D7JBq){.ޟū[nGPKGJ8"PKs_: styles.xmlZ͎6)-7ٖglt& 3shH@Rm;>E^c&y$ϭ' 9 ڬ*}b/y?f I*eIc*#!S2‡tŐ bEQJ&+V`VJeZtgc͵o-A5V [?VSc.`oXȢ HПj:̈́4^.S-N*J)ΰLLI߃g@ wxrD{'*WK466S mg%Aڇ{"%C-="H yx舣89{ܪ>Z5loPl]rTH:];*8,{. WbRX(!MnGxl %d64%F;/:ʔ|qRItU!Gӧ%yE@.G #R*%SC@ 3bWqXs`Nr-(}3u# MT!ܠL@whV&nWoo6JXƠ L U@_x^Hݖ!-6<4v%CseX W9wSC=Vnz!j,14tQ4%DF{î _=`PG=t6K@u]+{UD>8>kf<\ K`t8۷VqIr *Є{hx(o |`?g?Yzt)EQ!:'G^\W8,_@ Lm2uۚIf9jR/%\2oNϹ )Rlb8yu29[^—ގ^KKUl5[bGvR )$8G2 Zfhq} -f -O3>0/_ityH4iֽj78 R,'w5<l4l͹8ԬSx^z7V g5n.fISm(-;uzY=`}fm.E:PMvG&D|=oݛ3wAѬG[jci+m軷?6e> >Hw&"eG7R_uT YsiօSO݇5gs{a_*H=R#pB"#BZ6$Zr)u=,b{54HW͕u{~]_W׫+kq~-ԯxw)p` ٖ\?1 +>c['+ ET ҷ1Hs7tA^!R{7|֠_4zCvR+#4=o8toQ G#ͳ/'7ށ*J xj _qأ:hɞúyiNrjY;}NAdCq8ߞwBb=a(Gj. Sb6ŋzң5k}%b6PRHd&L; MD+>:ݤv7hc5TXWMEo_97[k6#ĨZBod6N^h9U6QX,{r2UV_ ]n J1fwG"<'oJi, C>o8W6{O`E No i7PK/ pL.PKs_:Object 15/content.xmlN0}c/cЭ&&LH/&%ۑI7F|$`Л=}m?<`)D푚S% 0SL7i37G}s]e &mHBa*!O){[Fd.v(  0 #lKWi-l6hF9MkT")PIO2υ=,U~ "]:, j-k.PCu_䙍ejD|[ϯ @3KNTCRepA*@}, xԹ^ҵ(H]C\j?@'e`b Z^KCwǀ@?j/ ܺ zȓ 0Ci<@PZeW[`9[P~Fh' kT,.*a %ˡl<(T&d2Gx듡rŐ=ݶk*51*x lmohe{E"B#Ȭaqh?$ S/c<߃8S L{}Q)#4Cq 'f/49hY%wvVNj|JZl(e%T|[E2l^GCUpbx)RP\CGbg֟PK;8"PKs_:Object 17/content.xmlN0} bĄ euddd"ٍ+_|; ЛK9mݾ_h#-tUa%S3O'&nGqԛ܎hAaJ4v=+\@0^Uь<M<#-BWQǮG,RoG-B8ƍ-:;kD2\.|A%suȳ7O/0T̗d`x%zj`4 Mv;+[q`jz|w[L7sI&?- tkb) (fn^ҚSSi7bgh& W^r$;=?/u?PKTlGPKs_:Object 17/settings.xml_s:pxivuZw#[HEv^< 999e7^PcJniKj &ލd[K[k:cugMDI]DKa.cݷ,oY[9k-'aoW{|~$Gvb$(.EI5ZByê{l ;Y~rw[DQ'2Zy kȘjB̑:)_ Wo bӅ>/G}F}>aZ,FD3Tp/Zx[G.F&(W54CVtPS4{1NΑ&bo"/˘h%ƞ/*ԥl6kV,YRk$N6&Sͳ["n+#*{SXAoO  /^eL˖^"UuSKQ@凡}ɢ<5ӶA?MkqKM_X 0CDu 0TrϬAP$S>pǐ`'CE%J-ߔ^]fkRQޮ>'W`3CPu[\ZeWs?z|o`x \Of`,>O.@O ] ʧy=6˾24dsL 2>$)_]TP.cxpӣRuvw7r0#O)! 8mMUkbe6B*0A[e{M#BCvUd8 (/3-ĉ~c"" +>*MQed] B$ Ukg(˧5N-eClB)+mR}m]UdFټl:&8HWypU; kBZ6,kě;lhG/PKmA4"PKs_:Object 18/content.xmlmk0WfU*MĩchS DcߗVKh}KN[1 Il͞nD[۾蜕}ث4 I3xQNxA#\! (a{]9R6e:24$悅c^%"V$(jQZʄ$>@~% yV4&48e>b=P#H1*M[#!Fq%,JjLV/`$pW,-upBGθ"j:j9^DƬ׈PbOZMN:x)6x.}93bIb1":x«:r5p6ApFAD.?a[pnwg 4K޿9|i\F+7/xW.e55XچgwR_S q:\5jZϓ ÂqywO+ﲯ 0ٜ-S&ȇ zcFw0ԥ?꡼n:(T%id́2x畡r}"Nn۷{Sǚ{Fyv`#Lcw^Ѐ@a]5N<"K BK3qb7ıb<뤈0C芗:B`DSe8Y4CxP? H9g>ڑ_6MRSҚ Ж!6J6>.*2l^6WC}$A+R6e}LvK>244ႅ#^bV4(nPVʂ$C1A% yV448>=R H  *M W#%FaKXV՘<׬_Fiѯ@h[L n7]qAu"՚ @6YV+3 ~urؘ1zby0@ zψ'U8LOĈtj.^-ȥא)%xf(|ߊJC`iu{o7I>ҞB,}Ns/w4 R][('q˔1 /hAmE% m$z({7=J.UI:bo{s| 3!zer2SvT&^qoCD 2x`˻lpH=Dh_ 0PϮaK B +u/ıb<뤈"艗:B`H3p.i{BAUkG~[SIÇOIk֜ 1 |O벨Cf2ti``41y0^q֩/esϦ_k~on/ݿPKs 4"PKs_:Object 20/content.xmlmk0WnoU*M”Ц6Piun_jwwP—0xbLj3m/1qo?7K8H*]z ʬ\*q]@V|xCo X2ʘnHŸ :NN&clH:SiHC9:M[CN(?HfH-;y-Q(-Nt Uګױ &Yꔉ8a>[/Hi%I2-vwUx̀!qw!'\PKZuPKs_:Object 20/settings.xmls:_Ӫi֝h-vo#Boj~^< 999ͷE7^QcJniKj )&ޭd[+[:auMDI]ĝDCa;c;,oYJsNiz~J#پnfw]]Jf,*E)݀2պhuR't~X߽Y f(ijGv^1J7^=3DZ4w2w0aRuܕyC4c5}S6'}.)aoW狯WEi#12g="pKxN#\!)a{]}vl ty|)I G 4ݖ"V$(jPZʄ$>@~% yV448e>b۞s($Rw&h حWL%@j5& 5Q8E+PZ:8g}WgmFhP "ckDN 1Gꌧ\~&l_vs6?#Ox] eWZp qwHpQG=Mǃ%$L9sV|2TNyQmvoXc/(!"vVl z*.+R>L="FIGD^~IAha&NF8<8SlûN3#4EQ uI3tȿ s^ Cڇ/֬9 1PR~IɷuYTe  jL^915r0u kܳhxįoy/ݿPKH4"PKs_:Object 21/content.xmlRJ@۵AѶXx\7بAY?op4&`A^f79z$0Spl5w6贱GVg7n.04>?pqu286NƱi;z#*vB÷#`ь( SAz-ƭJc!cY5 upr$VNM!-] 1QrIvMlCrf G"9~) x9i]5qE/Tz9!2đ{ٟe}>ׄ ˏ~U8w&hqY(VxzfX](}I"͓{>Ah!gJg %t8\,k }:PK&RHcPKs_:Object 21/settings.xmls:_v:;[tF-$ v{ysrrћoK߫0”JTCġ3L[2gҷ7t>̨1ƻD58:[)IG}uӡ":{wRXֲ0yz=I✆nnMW9v?zGQJ @L k5.J7k@v: 7uPV+Fkҡ~~fA[&L6n>/uOx䶚v1w:_}Q,kCxv} \!)a]:Q6e: ]LK>148₅#^%g!wUhbF()5$:B!^) yVԇ4>"z)$Rw&h zةGL(@*5& QE+P&Z*8Sg|WemFh P "cVkDF 1GL~&l_vs:F&<9 b iPZ96uń?4QիoaٜmBR7Mr*r-@10|C5Ys/4~gfZe Uq{n ?+4\Nfע߽J晕8)Jd*Ǯ 4`r lȁ7Z34ߍ=M\* ?@g`' `fn^KC*wG Y ^DqKNT8.3h^|z:&؏ Wy U+*BJ6Lkџ{lG/PKwe^=4"PKs_:Object 22/content.xmlMj@ӽ7($_URzl6w%;5MOgp2 g9g&|R8*C{O &Vze!x,ƫvz4{k!.R/D l8YM`1|t0w 88}c S̢t^-ːm}<ϽSb^ +\jkR(KTVNɪ颵 L^ƅ\)d}F86dxU-eֱj n>!Er qrǩзۼϊ=YTd}U,Ri8:r3l]PKQ8PKs_:Object 22/settings.xml_s:pxivuZw#[HEv^< 999e7^PcJniKj &ލd[K[k:cugMDI]DKa.cݷ,oY[{y.'aoW4 I3vANxA#񑮐a؈)2FF&%JsB1gwSha5(eB C<) c@rN2iLs($Ro&h حWL%@j5& q8=+PZ:8Sg}WgmFhP "ckDN 1Gꌧ\~&l_vs`׮`M]`a:=*Pzxq01l,[zes_(ZꖖTU.cO_.E(=op&v~gZ/LV[7*q-7}U{`'/( R1P==Ci\LEē_Cna 9W+SxqvKExT^a 9C@Cm1«Pri)wt]z]%p=[8@^᳨<==,w)(JGp. p2e| ZG8f;k|qwQ C](MMK]Ff9_+Ìpw>*܇(nT}7UAxaogK 6=ly 5 > UY$#h/$ 4'v#@)ûN3#F4EQ uI3tȿ sV Clڇ/l8 PR~IɷuYTe  jL^9!V5r0u kܳhxįoyPK!}3"PKs_:Object 23/content.xmlMj@}6JA%XP0URz\6]ZҧSIL?L0ep*C{ PK( zQtOr;Aar4pأ^|t51R &xs>{eJP¢t^ʐmC΋^>/I5wBAY:fX;M&E) _k)dbT72E"SE8M˪Ŗr$=,uFkrOeؿ ֆmH,*3=*J8R!7;PK4:7PKs_:Object 23/settings.xmls:_v:;[tF-$v{ysrrћo0”JTCĥSL[ɶgҷ7t6.L1ƻD58:[)IGuۡ D6uecr+[t$IΓszfݮw7]]Jf,*E)݂2)h\ֳT[+k?lެ 3B[#;%[I B-6wj`¤n㦾/Rh*l~Hn;AZGDh!2g쀏="pGhNCq4W0B}JAWlDNMCÒO %`xY*uJ@DGH /B<;0OJ˜ژgZ$Ss>DΠAU1bi[¸HƤaU=6~B˽Rg,t|TQ戴-Ҩ<AdjZ!HɯԄҎw|cNhBߗC OpU \,^ƉK!1SJUz?7ua@r\dԷljPl--y\ F \:P *? {*PMK_䙭{٥nDU\[@ OSw/#yf%zҸ '; 8<BԿ\BO-MZEe&. 3py 05 T QA˥az;`vydly/%|*m~G3 k*A:'i˔1!ïhAU) u(2j(oG7= .UIbws |- 3d(@!dttڽ) cM ,ކx{[Zi!&)g˻hpI=Dh? 0]%5," BK3v"7ċ#S?v<0U&(L3A~.hwR~\s.J}赋bMRSК 'ז!6 O7>.3h^6WC}A+<8BXT:lYtq¯G~ݿPKo_43"PKs_:Object 24/content.xmlMj@ӽ7($?B#e3х+٩o}>C7DoY893k =1@-M&dg!x.&r;Aar5Nu8_Q/LIL|L!vLa b}go XRJYܐ΋2d[,wAW$^V;II۠,sfFUu+LٔƅBJ2*>U;N "+ɢqebKC:o5Q[bٿ"ֆmI,2=*J8Q!w;PKʹL7PKs_:Object 24/settings.xmls:_ӪةNN@t;ݷs 7]?W`s7@B8sNN!zm7F;yސjtw'VFNE uv!x'Q;}'!iSM`6st,|L^cv$yryNClZK{ǢQ H<).zVj+%rMg퇵 ;aڪYv'qd d5is??3AE[Liw%/uxfn.IG؛U{?dYCBGDnh5n FG 늵,Shd#C#.X=^ɶ|rJ!Fam2P&$1KaO@,̓06$(C :SG0AcnUbE0.R1i(YU _b'TC> UoT*n4DF4djmZ#2BV9Re)uq@r\xسljPl--y\ ܈:P *? {*PMM_䙍٥jDU\[@ OS Cyf)zҸ '{ 8\Կ\BW-MZEe&. py 01U Q/A˥e1:})h͚kKQr姛TG(eP_qPa8O!`cN}x%{k;kG/PKjfH3"PKs_:Object 25/content.xmlT=o0+f@>,:@ l2i˪%SNPJ,"{޽YưJDΠAU1bi[¸HƤaU=6~B˽Rg,t|TQ戴-Ҩ<AdjZ!HɯԄҎw|cNhBߗC OpU \,^ƉK!1SJUz?7ua@r뫃ɨoc2kBRMr.y*u T~T,k3[3m[Ko=U?3w/#yf%zҸ '; 8<BԿ\BO-MZEe&. 3py 05 T QA˥az;`vymz2w6p<Qyrzr\S>6?#Ox] eW^p qHk) u(2j(oG7= .UIbws \}2NyX2Sޔ&^aoCD-4]4פ"4|zhq̮Ay{Y|;ĩb; URD 芏*B`H ?Y4CxP?H9>E1&})h͆kKSr姛T|[EQ4/ qCpU!^,*BJ6,8ڣ?P?_PK.f5"PKs_:Object 26/content.xmlSMN@{qVcCL!1Nab;C:-1n\z7l)C6͗{{V;|JCi\A@#\6Mok gyܛ>M jL?`2z({s!BiQ<{`4 7CS U.VNxr mTjD("#oi͛JPF[% W̑*(2[HǦ~XBȢKR,76/)Ķ{\I ">s g:(hc J㻤zf;e/R.€\9mUdFMw+LY=N*nr<\K82 ӭxf"wϖy%sF-W,PKDX|PKs_:Object 26/settings.xmlw8:U1zZDK"`O-`@PtI@mǯv< ͗y^P`JniC!1&:ݓ+K:`O#DI]$ƤEaJZo"Zo{rX21vL,NSu=S2GQE(J$(agE+?PN0CMm,TU8Q빟 FС3eu-f&Li7n>.&L' C576SJb=~hlS8"8$\Hjb >wN>4gGڳڈE_P1BiS1zujsml;08w묛zg\3ۘ>4մBLG_] g}fs0tta.c4Xul\17#{\>]:, j-k.PCu_䙵w;elD|[o @3+^TCBepA*@}w, zxԙ^ұ(HC\j?@ge`l Z^KCwǀ@/j ܹzȓ 0Ci<APZeWY`9[ Q~Ah' kT,.+as%ˡn<(T&d2LGx듡r Ő=ݶk*51*x#lmie{I B#fȬAqh?$ 4S/c<ۃ8Sl[&ED=}8uI3Lȿ ӈ4л^;/GY%w_>%YqR6ĆMˢQ6/ۋȣ Hb*81`qg3S_(.esϡ#~x} }hPKq8"PKs_:Object 27/content.xmlSJ0)bYc~ڌ nW#ld"{ yS>99V-+ %܆q p_p>5`YI4U+7XCh|y̡ f0N8< WNkQAdӽ aV0}7~9.% ETrj~iVqx7? vr/gKwy%sF-W,PK&vPKs_:Object 27/settings.xmls:_Ӫi֝h-vo#Boj~W{np~眜B< k(N0%JVqɕ LZc"$A.I?NVqVIcҢ0I%- c[9h*Sfz=˲Ay}}]﮺LpYT=R erYqQ/JmOi2}P$|S[6 nlbzgF?tLYa SڍK t܋E9wӝ:_7w(FcV41d<DDᆾɔ\=~$U Rvb%Hٔ100-҄ jxh)ܭJ[iQ\ Ib J(Ɠ=$ iq8}f$* 7=Q"H *M0 /_LD%o䱼bDVrA> 7[3iR(rm9SAbH5Ha=rGba@1_L)Ub$bHL:FWqOxp!AGE:߷Zf=8xlih#n~y|yB =/`EשU1pVV_Sn]pݺpdnc8T 3}au}@*S1?(Ӂ嫇ѠbձkBstMs>:r%@4|C Us'ױ~gf ]{n ?3 "PD{Q 3 q0sT&eRg:zoJ2 /sw qioן k&h+Fxj_. ߅>=л~2pS{'`f.ʧy ?6ˮҳsL2N$X]V>JCyxܻQrM2ke>'C唇!{8mNUkbUG$_*1AOAFSUY4*"~~IAhn^xqL{}Q)#4Cq 'f59hy%v^Jj|JZl )e%T|[E2l^GCUpcx!fRP\CgGbg֟ PK>ي8"PKs_:Object 28/content.xmlSN@tAG &q]fSCK.x8n@L_EhxL( G9e-Ju 0 6 0NJ-5U Pn4d827kcr=".U@zK%|U~@Rvh\˺R[(kK?,o,3 B[#[+)۞+;l;0ݸoJ>0 bMɽ<&`Uv[|~ʆ,G< (\7҄kCߏ0E]JVW,E(2F#&&%ZrBw 峄Uik4K0JjeC ( yVԆ40>3R( $J{U& ؗȈ/Q*ג7TkX^d o_rl# ^UNjRN4f6)׈ 1IQڰR yGo K1Fj0D /cbbHL:FWqOxp.AGJD:ߵZf=8xlhh#y|yB&s=/`EיT1pVVg_Sn]pݺpdnc8T 3}nu}@.*S1?(ׁ嫇ѠbձkBstMs>:r%@4|C Usױ~gVf픗 ]{n ?3s "PD{Q 3sq0 T&RcQnq oJ2 -. w qioןskh+Fxj_. ߥ>=л~2pS{'`.{ʧy ?6˶ҳsL2N$\3g>JSCyxܹQqI2%0}OSb@F7pm8ǂXc¤>"Ɩv4Xgۻjp/HDh? 0uPˮ#5Ȣ2"Wdfv~C?3y2>QVY`2"X 4(puSFqUpQ^B .Z [j@ Jt>RS"<EC#i{㸦8(#RTȂ.$ҵbϞV+"Z)1bls,S.蟞nKgk'< WLk%QNdӽa'7}7~=.% ET|j)viVq4} "wO yJKfZ.w hYPKf cwPKs_:Object 29/settings.xmls:_ӪةNN@tA;ݷs 7^!{ysrro08(ӆRCģSLeNo?l=ԞR/ a' bwIjq7J6 N(i3M#D֏n簢e`|ze)zj%3U~n@Bvh\ԋR[)k:k?\3 VB#/e) b);l;0ӸoK1 bwɽl}?ߩysb>?fECC# OD7ӘkGߏ0A=JNWEf2FC>&%ZpB!Mg1whc׆(eC!àl<+ cj#BrNrJs) $Jg& ؓH'Q"גWTkX^d o_rb+L nUNjmRN4f6)׈ 1IQڰR yC9l #1Fb{0@ zψO)Ub$bHL:EWqk@t`)AE:߷f;pli h#.r녊 Jg_ [/qcb4-fi/?Xg=ǍDSM+tx]WOl'4v/_\eL1V[o/Xu.C__Dh=oqv~:/XuKyՁ%@fWߝZᙥ89T*Gn Zj3d^ҵO]C\j?@'f`j Z>#5/vGU>>_Ws'`f.ʧy=6ˮҷsL2V.N$X]VJCyxܻQrM2{ke'CŐ=ݶk*51*x omohe{E"BC}vUd0 8_gZةx1 {-""'>*ef(3d] F4$9QI×OIk֜  |o벨Cf2ti`b4 CC/%rQ$u ťl94:۽?׷P_PK$B8"PKs_:Object 1/content.xmlVj@){j)PPI"Z(*T)f qWTKO}w ]!X^dLQ= QTMg s ۭT#ڨ53E4;@}QB'a4w;U[U`H  + FH,Bk "m jJ)0on+Di??4CB\ꑞ^rcCl΄'FQ!C1Ԛ1a0ri'\"pwBt<55+pو Ę0BL'1 (ď&bDjl x:yۍd}M[IL~&i؅n&;vr -F!]\nWv9"Cclq\Nb>}|=4[_ Xȋ4c֢>PK=hӶF PKs_:Object 1/settings.xmlw8:uN@-tFOj;]1g'@B{on.!zuGq*5D:$Q?heC@CpHD7Dc?Wh0EJFW,E)2Fc &!&%zrB>w g wi`(?ʁ$B A(-$ h ~8*|f{Χ4B(1RT`2@##F\K^)R)byɒb~E"3|oT;bH ;ӤQ r\#JT+$jD jjJ5 e5,Ę(<#>3Cb \,w-˯#Jxf(|۪Jj=s{zQq= X3 ̕V?ď2Әi:tNtvwv5f__SN柜q:`h:yeGY1^>`1u ƈ~@Z\ư:ƕ}{:\3i\l\:mm[!PS.u_䙕gteh[o P迣3KNTC\erAP{&m9xqP7mqۦu~Oϵ9`dk)ڢ^qUcv 5n=mϢ|eGT8<'VwT6bN)A_b=)$X[#0YP^7{T\L2@ɹ8cpp%=mΡ Lm4fׁgq+ B3' -}?1xxˤp02BGs`whwLQNQI×OEkT l'bo모]fTZ?PK7}9"PKs_:Object 2/content.xmlj0Y@8u00e2j"Ně{=aVOo ;_|nyL2ѺsE„JOokM.7~ dqLnw=Bkԣ(RB:&}gHɐ<6z!u=%4G8!s+dE] qHI;ljBNWZ9%*1iYBpd4Z\ǒQ?fyCA# GD7ӈk\?*Qkэ#eSh`d#CCKb.X=.^{,nUM"eF{H /"<1J˜ʐgFRJ}ҞA?F >ve1i˵"՚,, `$pWlJ.28ȧ#F従x԰9M9@ "g5"'HBLF6l&TCP[âH\#jgާ2l1b1$&" z^;rէp:DpJO E"[ABIF2+}^oi 8B,y]幊 J{9_ /qm22î[Y}[_Misݺpbxaf|f`GO'4v/^eL1Vs_hc0m#}\ \:, j-[&PC_䙍ު ]s;n ?K <"PD;Q 3Kq03T=:RCQfq T7cg;4~OI%RcD},Fxj_.5߹=>==л2p;P,>O.L ]O͏#{^am~]g!8n2D>d֣3Hܭ5R(/\#]uqG6 Sn0}O)! {8mNYkbUGJ1AAS*ɬAqh? %NF8܃8SLQ]Q)#4EQ 'f/4'9hY)vVNj| Zh )egT|[E2h^-&&8H+Y{Jkg:->i˛=H o*Y-kT_GsaZ6>&! ?ƍy)7@TJ6SiZR6Er g[-`vIj@Z)0([ao{DPKx oPKs_:Object 3/settings.xmlw8:i-=b=ڎ_ =;y{ss 믋08(RAģLetϮ?t=ԚP/ ag bwI*q7J NZ(i1E#D֏nee`|Z|>?_د֛f5Q2GQy(J$ȕ`ZוJɟ\^am~zOgPj(zh﹟ FСÖ SڵꮄK) OlƗf1ѷ:k(ブ71d<DɌB|u)a{}8Q6e&}LK>164ႅ^lg1whcWh^ʆ$9@1A) yZT44e>3۞s) $J{ & ؓH'Q"גWTkX^d 寢_rb'!nNjmRN4f6)׈ 1IڰR yC9n 1Fb{0@ zψm2l11$& ł^;qէp2DpBIO "ZABIl}XoJ_}8'ڳڈF._T1Ri|S6a35:m;0;wM%׭ dž?ƚjZ`/׮` F g}f}x?@j󋋹;ªc6j 1"u+QAe|[j;<1s4һ_5ku-7}u`` /?Rw'{f)f2 [Z9xHԿY\BG/MXF'e!.5 pj`b Z>#5/vGU܏>]W3{'` ]#{~am~}g!8m2Dd֣0H5R\ C_x(Il]uG. /a8; S#CFwpJw۾ <ث0㭏翡cl{  UY4#~~AAhan8:8Sl[&EDAO|T>8ǓuA3Lȿ Ґ5л^(FY'w_>YsR4ĆMQ4/Х Pb91aEԩ/_l,^wC>?PK 3`9"PKs_:Object 4/content.xmlT=o0+6kR )P A`(& y$^2e)o R2`GwOe +a* BqK5S焜ooG,.\].?t( 5IEW9#"!]|ҋ/H э1&atZ€,(ͲWZ"ΗZd %̇ڵVkf As/Rԇ>o" K+yy갉/Lqj#yyx"ڊrFvfѠ`do[Ŏ|F[{tP{?oTTtGib:/$TrF{@iݱ/ 4lա? a}|AuY342tNLࣛ!@&c֫ "w`.fQ 6P=GGP\.Emnc|i%x}[O6"|PKJ:;PKs_:Object 4/settings.xmlo0WMkTЮtل:v _?ڒeI'Dbtkyi7ps-ЏLRzl\z !Tz ={OyG{;X|e(:kBu7m]]]PNEţߣ2bgvvy%qH:{@sF&6eZH5'k9z|_a$wkP^-nuKe]uv{@WLۗlhHS_V芛4}TTp TM *TkN|ZWEɨZ٘rDst;jVqJ#<ðWnKPKbPePKs_:Object 5/content.xmlŔJ@>ź4k[$+I[Ahmbɦ]H71>3?6A,aQO ܓSiA@ٔ48Vag|5ꁅ)?XEh6tj fu]0%6lt NA!;&6xjp.F( C%lZD e8dZ\/! v<&\A*LZ놊V)21<Ҡc@I'6%n/݆|z+n]eX B6׏b;-V;ly /y%C! &c0E|P"6BA83膖 *˩Q]Na%(W O6PKr҈PKs_:Object 5/settings.xmls:_ӊcZu' Z:b[PuM@mׯvwHw!Do΢SrNUOGOͿnx}Q?a')bwI+q6۷J)NF(m0AcD5n䰢ebrLt:=$֮eW1>*zGQJW @Ly+?PN0CMe,TU8t5es??3ACceyc~4՛ꦄK1 d ~YNdΗgEoi#1+22x Qo: ׸v+46%l+ʦȂIv\Pm3S( $Js & ؗȈ/Q*ג7TkX^do߁rl# !^UNjRN4f6)׈ 1IQڰR yGo K1f:a@j _[O)ǰLŐXt$}zKᨏp.AGJD:ߵJ ~epTIV?ОpX]幆 LJx_joEיi4Wvtjvv1f_Sz;\Nf!`c̭O` e]`2gcV0@Z\ưv kBwuuf9Map>r%@uo|CMMs+ױgVfn C{n ?k+sL"ЮD{Q 3sqriTܙLŢZQ-ߔmFA_nRn<oAG#[KAuW\Tv߅>kй~1phy'`Mw SO͏Aimmc>8lG!d-֣-2H5R۱} c4r(o;=J.U7I&as\ d8:!F dtsn۶s,51Lx"llhGu1AO{BFS Ż:Y,*"b~~IAh6ȼOpqLA }Q)#t<O%Ͱ .4"ל>Y92})i͒ז!֧oR}m]όyy0<Z($&&s7N}=bgƟPKGI8"PKs_:Object 6/content.xmlMj0gA[4i: ]kbtcfO3,Fw}9 ZUE+} >{-Țqe TUn}(MREHiŐXGdy-XO^9ahs!)+JE/ ڛsרq3,ʅC͛R  6EE~ldWk]#G[ {A{½ArJU3N:PK] PKs_:Object 6/settings.xml_s:pxiŶi֝h-vo#B7]W{np~眜BNg4 z~u5dϢQ5HP(T^ԕRɟ\Zae~f ('Xl*|h뾟 AХSeu- &Li7m Cc&A.y9w*_\^Q 燬hx } Mƍ\u(a;]}vl-|ddY s4ڔV,(Ѭ I J(=$ h q8}f6=S!HF)*M0p @LT%o䱼bgDVrB> 7[3iR(rm9SAbH5Ha=rdGb `@i ^L)UbbH,:BWC=rգp4@pDI#O %"[ABIcPsZO{d׭f詓ǡYv oU8h< gЁ~~c.cxXs=][9{i\l\:m?l{.PSu_䙵gtil[ P3+^TCBX94* LZtg֦FoJ60OsMqn<-@W#[KAuO\T߅C>ݐk]нA1ph}'` ]O͏aim~]k8n2@d-ףm2H5RZ Ø(MCmqGɥ6$#l0=W)S@Fp*wۮ 4HtW oRF %y&8KaA^ڑ_VURSҚ-eCl@);()?ߤӺ,e󲳈}Y8%&& o::R6\:=6PKZ,7"PKs_:Object 7/content.xmlMMj0=tB! C !t)d98RqC馧){_7b@}3SYXetCAjaRgv 'Nivk4[*$5ֱ~Z -n\kC\5/xQGWvPT^/# PKiXQPKs_:Object 7/settings.xmlo0WvkTЮt$XM|_?ڒeI'Dbtn6x.(:oZ `.z-ułxF0y&@J5D4t&v׊8s*Ft %Ӝ,){Z+)CǶn]__ۻPق.OEţߣbgvvՎ[BKuJtlڵ+!jVּs>2JmP&^c-nu i3*W,\e|1qQ/K_8*e>]2Grq\(D=2tm1Kʲ-̌H(n;/ ջňSXׁ&rH}N_ 8]@fT%-1r8 l(GnaVoA| CI|bDHUF1f thCՃfI'MhfTF 0AL"bQzj8l@BQH %>}ԡe(u;^|%ۯoCo [j \*nncj_avwTHܔ8224ႅC^bV4( QVʆ$C1a% yRF48>3o=Q"H *M0 /_LD%/䱼f_DVrC> 70iR(rm9SAbH5Ha3rGba@1!6Ta!1I._Ƒ˯J¥xf(|ߪJj`룚}{I:ҞF,}Ys/0>Ù]P6zCiZYG_MZkszhlnc0T 3}it}@S*3 ?8Ӂųbձ+BstEs>:t)@4}|C Us7ױ~g6f ];n ?3K "P/E;Q 3Kq0sT&gRzoJ2 /sw qioןKo&h+Fxj_. ߅>}п~2p3&!O. ]#{Aim~]o8n2B!d֣]0H5R/|$6.wߣRud  0O)sCFp*wۮ <ث0.mH㿡U`l{ .ﳫ"iTDġ,>Nď|O7 2)"9G y&8KaB]hF9gY9:)i͚sЖ!6oR}m]uȌy^F MLpFWyqD;KBq){ޟ[mGPK_7"PKs_:Object 9/content.xmlJ@>ŸWIRdS `x\6v-6QċOk >q`^,L0|ڬY._D%%axQ8ՠz`ގxV(M%Ζx 3HH;5̦`R!$$ HE{Iʘ,K~ kbÔK[F6@*P6$H0 +aO@̳06$(4Gi Q4`0ľ,FF|1RQZ%;lE [Eg4B$ߨwRo͐v"I5ȵALFVI"Ո Նj;akX1Rۇ!a&Al3Ta!1I._Ƒ˯>!cJ…yf(|ߪJj}`Ú}Iyv=KX]兊 LJ{x_ԋ3Cncd݁4ﭾ?Xg;uqf:|&]U6?c~0ÇLP;O__=e]_5cD:A/W4K˷8PE;?wr}yfmNq,5ߺ:0 ( JP+<3G\JE0P_&w^:Ј@ax]5Ȣ""dv~=c?6 OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-93102006-10-11T17:42:46Nicolas2009-07-05T13:59:36fr-FR25PT1H23M55SPKs_:Thumbnails/thumbnail.png{cpeAnlĶ983m۶mOv&ضm۶q|眪֭zz{u?^(/(#- m{ ZQuϾȁMXxx,/rFG,@K(+F"EG?XjŬLG7 8͐Q "qп _/Ls\f N |ZGivӉ@e ~}= L(=wzCz>Ӎba6}A]u]7u}0,;1\<4P=m}Yw}<ڶ]4Lo !>ak{]41>-u|}]A4\ Y 8]`Th5M>v<,j]fU5 /}{~)0>~2b&`$ PZcrبw4̱*g+.[la?BTI.[/y㲤| FѓbyiջRg~$$U"Ci7lZoNCz4@ LMIB!6U}! r6WƎJ|U~-ʉX&n|[6[I|ލ۴ bF+D |iCQߺ:zW&(X$a=9+m;Usռa/88M_t> g*^Il# M962|2o }$9:2٧|5b Cł!f(ZH^50 r ETXXI&pvzH76o^I24%bt-G]^na,.BA/JzKӥyI׫đŘCe?I1NS~Z OV#J" +Dا~#j3i0ùBv$r Hӌxf_MHzѺvp@C[V^ՁZ*qe'ŤNat]?'?`f 20tvl}t28w'Qٟ}|,cPt sxCaprXzJuRѹ iM)~kY ?q]g:Qk`v _wJ|3菊:[BCw\*>lk8IQ/mN.Sl+u7bxq n&XzOޯbq1-~C&Qj–?DO"'㲙B_I6_AZTy =G }->^Y8UOS^ y+4˘^EcB|?f)B=nZkhu?8JZ)RqI9u% #]Ҏs)Jsq (bg4ŪG0{3bJa~d:jU%aRU #V1+A3{Fˁ \+#Td]5j&^v0 ijU$m콤30{r)tXe؇h""ɘZ?4*S|; O Q":m,ـ#}ޖ|n("O%t=?%^<6|սf(RHC291G* 1ꓴu"¯-r+fc:m"|ENo%~+U$Gԋv0 ēCm>\ǯ.^-Lo(-Dp(^|ha3RkY?.(&6fCʡOxU_J.0o|Dp]v67qQ<]=QWN5 ?=^6 >Ou&y3^lw + vX!x&B%9c5#~5.đM&@3,+UGQ\ۙaW2:M" ҅0z8<0s^v^2@( J T;3pP5pQjMtlqp~@?fdžEt8 q묪6]o%6 ߝ9zl s/o7zի=0{&a|+hd4MXzO8ᚩT)3J3Q~Bg1YN?'jqDt;Mx$=@fPEԑo꾗H=R?>J_t FEv<8>:t0܂l 8WH? A,퍟8 ,mZ LbT? *7k{a=sUJk`bTꡟI&E)J?KPϤDG=f=af' GU#PJ}K{)wgl߁ߴeC}|s#s;螌5StTEkY6d򐭔h3KE~FҸy,xV@ 5#łx=~ҸQgED @ X #S4/.xV?Oc&uC 'ΐ@Θ9  H?DP;HOvB914'v";wx=5r _d<>7?il JYέeèλc^\NZ T x?lOh1V"T``8??]M`C[X!c_+ qLj+ μEd`qc ٠azEجX.:֖"pWM>R8^ Pٌ{Ei K2)4 iÎK4%ku9[275\FduZqsKp!^)]utx=]>Ti6Э{]uȢT~|>u!I-E /m6B\K6 GهVGcbd͎|+qY0VNx6&$  F//M.ߏjeSz#>QEL ,gTJIu:[ܷRC!˂3CJ`MjD!KIdID@(Z) ` RԞآ>fD58Ӫe*A DhP"6kYHgZ|%-2 ( yObsYpxPDdR .1L{dkߜҐqYjY @>~qhL$㔀˚= 1{ N\"2aWV /gfr^Lԁ# U.q~Qf#lS * 7V\ ,5 "}cr35|+k]yJz]4wLA~3G~&Iea;4Gϡ-X133sqXX=kvN VZ]?+^PvM0oH$ n~9NSaD4ÿao6p6Gb~ qnJg ƫe'RucEMӁ>tC{8>j ^ۺԱt%BhѳF N}F[x~<}>(d㋘Ƞ`SqH¢ST=z8|#2 2 I^TCRd[A=fzg`f5r=$3~: "BE@7_C c邘1R/WB1+KYUUN"YLzT U+[RBk$wM0dEGr4$. KtqR^=$K?˥0HۃI:7 E ?C6xKn^ m1Mb4S 3أRyBzٚ[toOq;I{v!Zi'l!g>0 nf%s.7dȂemWRn^d?}ڣ)~lћ6˖dqB5MWoAP²W%R M9v,^¨S^}ن3KFKbrny;i}K$9/,ƂŃ}ɼH2)_55a "'G=H0!Mc!E2]"3!Q~?\UWm MMv dٶZXP:M @\wp";ᇰ fe8b!J-M|QbbL#OQJg!'`1 >Xfh,~_R^znIj^*P,P8} r*c$ >YVCQHuE{x๰ouHExſsl7V=5IPI{P :{K-?_@:}TNHR(.7zj~[`*+`͇bZtvPcl}OW$|;Yj@]g7}ܣeglGQ酇u#G:t&dz/) W>>oD@<5ׁNDJ<tɈvB‰U-2]g ~%Bk⾟S(UW>q̏I c%y(/2yѸhU28+>H8#CRf]sHOc*j|IK.<BՈJ#Л&/Ψ/2 %;>XoӋ"Gp's h#OpCDϼ "A˔]s,"2q'ޤՑ18&f:_[)ʋ)l4r+ȾM Z!zv;822̬eo*n@w&W! )[)l*+)?r弪5Q,@, /EMU<3w4+ߐ޲쟹\ 8oM;4#kn岣\72e$6~6[gT_Dz8I[.Cyu$ؖd2$m׿6D`Ea(@`Ži W'k |@S3:8O29L} |tp}*]e[os?TYЯ)~e(R.=^L@?A,8v[bi*C 9ծ`ʩ6H2Q>٦w֏gNaa33.z[bgy6}!_M[mR|ަ;*m9G#8㭦0!na|_+ xvSqZfYM-\7e~Zy !ViuʲY 5FߝVF]iW*<WTtqnފ+P_E- Hs<{!Ote:2͔ eOK=7,0?:kW:%z!MMtH{y80P\^)thV^5. &V&3 Ig ldQѝ K քWBNN E s\o!eγvp6f9띪dx YC& l P6Q5A?uFn f QA/E#  jR5(n EH6 w1@~]Pc* !kLޯ^G퐇xArsL{z`:~KA~dbT @3g,rj- M*Ukm&`d#ژ~Yoa *.{~$ً&q,ee)*7V4 -:\0%<^9( ;GsB%-v3Ik BIXJŋ@MZ&V(Ϊ&+19\ +~P2RX9V i u)Dۖ,l+L`nCiV}dFh#QլbqTw8022(|ne9(va1#Q5%LA`2czN%4%LQc `f|ŔE )LIJ#{wȔJ"fy98oB2&:QL6=79l\cCh(a N P9ֽy]5w{C褘Ѫ7G=Tv$ĉ!x#deeQ,)AlZ9@8 ;^X1  (JZCm2`e0.Iu >5 ]uITh=: I_ gb( ɮ9Qgs)?So8p>)(C6%k8Za* iǺ-NRȊHzuҌ*wnÃb}RoHrcAgJŞ 㔕 §e@q$ y:# ;g | *>vN:=n|[rڭP)B8<$y%vi;۴!.Kqnp9n$^֔mQz/$yl?vTa J34 ح̆(0x#ֽ|Iߙ~/ (Oպj:u xT`I I,ջ|}O s(nI#&coA% FD s\W#,k|F{Ia3jK굅cYk^Z.iᏣ+e5Kg7Gcyxvd`j]qS~2[.' ؖ2:]T4C.CN$41tl*.YFLl!6LφL e-Q8>GN7 G8`FTP^[ܹ!6پ>Jɦ~NͺOLʰ@_t|$ Sv{6|^;v掏ag % ]Ch g~1nw0eJ !UQ9*4vC^\r0w:^VI:>h;bRŐ 2I;%[.KɿLn"Yomq]\_åx<(pSh#2hdLcmrDOh0}Kc/&D (!V~[F& ى  J0;*Y&AXJ^7e $?EXkpU$l41CyyH5`%,I;#tl!8:CЪ[V?<X(&xS"$]lB72È}cUh3o^0pFɫ"MbyKo9kv*wlyQP_oIXt'\Aμ H+*yrΎg[]?Ņux>F1o5q^-ϿGs| +"~܉s@kDXjsdSY&q ;n%;oE!=_tb%/G" EUܢL6VU35g$c<+ϒ|GP)z N֎ӲFDd|9+Pb_ I?"!?5+y]cRAs|0x\N>s SWMdMv[|b7Xo4LG^FceUJ-/Sl f}X*7[j4@7Z_SDGN-CNdͯ!JY3!j.kδ'qwĆpQbLR# ,42) r#F]VoL_/4L=OsPwb[XRp-CB~3:e1n\0]GIdzQӵQwjlZTlRU4qn; %¾dmuGPwfVwRHYyP)]?-!@ӚkIrq,Oe $ @׭()zDBzEWF.*x~Y6e.sIxmP-PRؒk`%˘ׁ:xʰ-mY@7TD0ĽM QNW_o350{wɘ.A~>ԁ$eO(텸o@ca)eKejx9{n帤V#ڬ56">X/=^X lz]xS9Ж!'c`=_>g:~Tƽ$19e/ "Ɠ(*TQXZz,`tz4ӷ6A0~_036[\2bIX߄$"~aɹc%^dEF3M^"D)d6ve0<&G:5LC>ޞ9WP& oHוjVԫ+U}pgr ˎvz)ZNķu"5XEXk~g$^ޕBwZX d%װ-l*);[`YYƥ1N?QJJd8;bHe.'~KU) N.!@0So4/ )fˈVx >GRn}!ەZ6tLR3U6FX,>٭MJ^W&f}V S ;ұu, l5yٙfZ<YE]Ni)`}1t&rHƾ˸  ^|@syUUB䱟y[`97m\`OI4|#c8]$vKh?y)A7sKt%G!-F(X|z|wH*<*ՙF]˴Yd8!Bf}Cz#*XvDygMϩ~a7d ,oď|/Jr@TE'-MFK1xZȜޣiGh^@#6Z{h|2t2t)u xeA0Sv9C4/H4.c{ì33+N<;G#AnKN xn$`<8|bJU>gӾB1H{RPrTE! .>w<]MpO^?(gCd{ qv܉V=6C`ӥD=/4:Efp=VZGfבGFv-46H Cc7rQy G$}tkmSaoPXcƆ|N)''h7 4ބL l"G&jj\yϔ'48PB&ۛ5+QNAXOSI=Sl"w_; (HӞŭU:,22-pXY{\!4Q݈L"rN8zyfbc[#Y_Nb¨}9n/X>hDPph{_g1nh$.TE°Uxt:kfs=Gz jR6I(ʇ=. DlW<|gٌBŚԩHn\F=Φа_/Gj_:A| bgWy/Xq@>\5*j#ZQfiH C]3q<& MvY9j&%Ff%v*Ue9|%ȐJkrxRT=0n~B;~m'+4zKcNwn/zT$\lΫ'E"?w:X_;&ϋ"צەF ݂7[&Un&j?YtY~;rwVfo+ V:`tQ[aWS4Yb/R+6#y̲X ,N>p.+Ҿ5PyxDζ5u 0ڰi 6o^C:m|;BgN@ &O [gD8h1J\`>OLcG( 1+(QdV "]C#m]ޘ> yR:%L>!Soe@Ԧ{QLf;r"va,bz5^+C# Ю2b!IV/b :Ij@Pmj(u MRHZ(t2'fC3py{Mt >:qc 5LL6%$ʷӇ- x8TtsՀ6>va _vya-i r p%+/y'ePJ *W3mͶ!b,\!#)'Q-IHn#&9(,X/zl [cCX`/늢N|pJ /eq_gB8Q QZjD$'vNU:Foȝ8:RKs5NyCVw4MOmc{Fl,1U`5d@({)nu/FwIMxad- shBң{yF]5L[kCP\ dXc!jH8 DP9o ILHz^> %o?w$MQ$wȯm4o@ ~,si?La6*^aT.#Mry=H5H7\夑$|ijɛV/\km p('cm<{,*(?8 yQ/en S=5O :A݉6/Mk+OczpF 04; ok"F/QiWn/x 0trf?u=S2%Zy~RWqG(Ni|QIR=X^X9lbamr¨!y[&Of!c@nKSLZnCԷXsXuvI yaAG"!,Z,kE3̦_Xн}aw=()Ee*?V$I.l: T d&[9Nk8:V8x=]o3_$PS#UH {()9"V4j]fjnR-ouVNq}]P]1g8GVG QMm0.fFl~nc_7.}tv\;Y(p)kUUt;˞}{PRTqtJGz7ҢU/ceadBB糤R@)oi@iʤ8ڒDC5X9Só" 62h z+jP:17[8n1b[, ,C2Ҟa"ҨJ:(p Ɨ ѯ:L1TnG48~ָ{Mf ? J"_ XAO@ӥPL%$#[I >c ka0iPK=Y2nAi*W NV'^4!oWk݀+~7W׆(FeYdjɦƲ 0& 69~ O$hi5Z[a?-i#pMc[ɥ_` Ӹӥ(O+I.h+`6qܶdὪ"W(Bz 5H#su]aRt>g\گ%ձAL%{J>ʹ\` d_$=ۇK_( '"Y;91yThݾf7|)q1~0?9tb " O2Ev Ovm'*6-wpjf]hC,hV>M#,ſ#&1106zJ?MlH7o.č(b@OyGHr7z2[ ~r(, , 1??{m{e |_748/ F4.>*ǘfBge@D RJC I7S_0JVNN6CuuIN˫+jc0ȍ_W$QHB;z'^-}!'6}6,+\jl1ps߇M.e o&FU-Df#維\ +;1]8mzȄ+]șƜ!t!X%+5HLKv7 Cƺd t{7?`1hbhyҾuC#ÐpPKeJ"g|7=\U3:SF {,Jsn\[Z8//F_}_OJ: xCIXD'yL{~rj%mJmYUW7#t`F$(qإ ȞQt*B}Wrv 0 rrE6Id1u =nw8Ah+:Y$aAM" )٩ ̭gvj"lZX۔Ӳ"ά N[ -Mq8lPфbz ǎ$&S'}zN<[|==n€88m ϰ[%y|s'0Ol@DcǨЌ[x7ltdeHzZc07CȎ2jX5mAvupvw|5]-NolX8x`q(L0\RaZJw%E[-U8s yG "kxH]K!?Ε5rp^z]Tm7x]Rϼq68رRmK"'̪|ڹd^-~{Ί[xCPz ]ֿGצ4[u0G'(|uϾ1wK7T(}rysr]"w,:}'x'+BD]g70:&PKiYBCPKs_:Object 30/content.xmlJ@}uޚ1-B[-T[0E<54ddW)3?]ShE oϚYX.mlW10OIϺpxR\-(Jf|.*F= UUx`FBLYyᲑ6N0a5$hqzTrRrQE˄hyqb'Z͈RL#Jǧ~-Xbd-\(]ΏMu?Azg/SuS6e{\ ?IoPKFe#XPKs_:Object 30/settings.xmls:_ӪةNN@t;ݷs 7^!{ysrro(8(ӆRCħLuz'Wʷt:>jOF1ƻ$58I%I'm#ߦsD֏n簢eb|,NSfժw]}J8,E)݀29ѸuR't~X߹^ f(j7 G_06^Sv=3c:t)u}[ДI'lKn*'`Sn;|~̊4G< (|o2qPazX>;R6eF&LvK>244ႅC^bV4( QVʆ$C1a% yVF48>3=Q"H *M0 /_LD%䱼fDVrC> 70iR(rm9SAbH5Ha3rGba@1{RHĐt$Cf׀ %R:r%@4|C Us7ױ~g6f ];n ?3K "PD;Q 3Kq0sT&gRzoJ2 /sw qioןKo&h+Fxj_. ߅>}п~2p3'`f.ʧy=6ˮҷsL2V.N$X]V>JCyxܻQrM2ke'C9!{8mNUkbU6@$* 0AEFSUY4*"~~IAha^xq;oRF h<N%0!.^h4L#rМJ@ze>|f9hKQJ7>.:dFټl/#&&8J#ɫ<8R"ǝϥN}=Ώv?7PKs8"PKs_:Object 31/content.xmlJ@>źo͘L*-"JL6B[1^3?]ShE oϚ|d)zd41<k/Oȅue1EYq]by7{p7㘅]U=U Ux`F"LYyᲑ6Nڌ0A5G!$hqzT,RrQUKFJ <81ufDur¤qbׯy>XO_l 8*PU.'MM?AjkRu[6eGLJ 7PKL"WPKs_:Object 31/settings.xmls:_ӪةNN@t;ݷs 7^!{ysrro(8(ӆRCħLuz'Wʷt:>jOF1ƻ$58I%I'm#ߦsD֏n簢eb|,NSfժw]}J8,E)݀29ѸuR't~X߹^ f(j7 G_06^Sv=3c:t)u}[ДI'lKnu[N/ϛEh#1+2g@"ɌǹB Qvb-Hٔ100-҄ jyh^>[F6DY(d 0P{ YISpHTGi Q:S&4`0ľ,FJ|1RZ%;lF [Eg 4B$ߨwRoÐv"I5ȵALFVI"Ո ՆjakX1ۇ!aF}J#Cb \-#_ '#'K z?TP,U$G5ީ ԇfH{V@wǗ_/TL`T:Ro g^J }mӴG0bRsFc#pVK#:w]WOlǙ4v/_ pkR'KwKZ)]ˈ$5ĥ]^.A&%?4;:ȯ ~ ޮA <@YC\(JGp.J# p2eB ZG8a[kbqwY C_(IlCuqGɥ6 oa8[ S(nT]8UAxWa\`kC 6=.+R>L]4gWEf ӨC%Y|zıb:R6:?۽?׷P_PK;}8"PKs_:Object 32/content.xmlT=o0+f@>,:@ l2i˪%SNPJ,"{޽YưJ%|U~@Rvh\˺R[(kK?,o,3 B[#[+)۞+;l;0ݸoJ>0 bM=ZMtv[|~ʆ,= 0\S8D|/T.%l/M  -K`׻xYݪ5%%ʏ!Iw@axJd<- cjCArN Juy=a* Cbd(kE5E,/Y@YWѯ@9h\dpFOC/D*}'5V a'rT3J\DΔkDIj$R(RmXM@췆%ف#}"jS1l1R1$&# ł|8pէp4մBLGo]˺ g}fs0u\hu}Zcf:F&R׹X |uY~XBš*SX3+3]Wv`=Ձ9OfW߽Z陹8T*ڇ Yjrl~T7cQ;4~95RSD#<5/vOUҏ^]?Ps)轈zȓ 0݂=Si<APYe[Y`9[ Q~Eh cT.]c3ȡmLHf }Y|yxO5ov }Q)#4GI 'f4fkQ@ze>|TfkKR*/6>gFռl#&&8"ɫ<&8\"ǍcS_(.esϡY}PKd9"PKs_: settings.xmlZr8}߯Hp ;P SLH$&.I۲p06z"nu#E$AiƫW3\NoQt}ku bD MI14Dt/QLsݐ,0AciBpv͌|>?_WWWzhr§ǺFtGvBYR"?5yX߼^9>J@dsszlv;&k޾yy6[1˘pif庼kx]}fKLپ}l«WU>RA{1& ˖¹G $KK\p7Y+,Cǥ>Z6kbCW\Dk@N'͒Z>usFzrYt P}g'b4"K۵6C?h~JNzaGуR(dRU')T.TW?-1pE5c1lpMT .8~1А>;}6!,vS 7FYMEG:٪P$]N9i_>$f2ဂ~e:ZGL{EӍh'K6# - EX#̮'v2Z)i8XוVf%&3ْ"k*B .ouV7Shm0nO]ݪ Kҙ3x6POQ(|]>CPK;vJ PKs_:Object 33/content.xmlN0} b `b„Dv4MB1f<`RX/YNg1JEbV>U1PǣJwtF14/;W̅D䢪 !QIzV]asD5So^ٔ>O ɲ7Z 98'5bHn*љ+#;9h\y(^v)E\(1$b)iW:Io  G,6Vz5VO~{) iO%ǴaRZ)x?,ޗ<)MfXRvgtxe2VG1Kv?fyCA# ODnh5 ƨC 늵)c4`c_'1,z/d[>[NGi!$0 JaO@̳02!$(k4Ki QZSĨ0d0,FB<1bQZ%;. ;Eg 4t$ߨwRoÐv"I5#ȵALFVI"Ո Նj;qkX1@vJ)3Cb \Ǐ-j˯Jyf(ЪJ*=04݃SigZD{V!bw˗_/5L`TZ}RU37l$FxhvS}Ǹ{b;[uGqkf9z`k̆lL Ʃt^]e֜q_#]]X4 .}_E=osv~ng:/K{=5%&Hfhע߽z8X4* LZ tSlksQns mT7mgۦu~M%`bk1ڢ>#5/U*wG@Ztk ܍ 辈z Â#Si<_Xe_`9N[ P~Eh og/Ja CyxE1:})h͚sԖ!6mR}m]ǔyy ]X0 %&FK|.u ťl9t~/zoPKx,`8"PKs_:Object 34/content.xml]J0{ am/Cna6I0&벊7'8W\i˔.r]8In2޶k9,)2A,O\m+jjW9houA 8>F\? EsLZٙ9z .j1iDzCeBtKǘa50b;ӟ 1x}=PK \1PKs_:Object 34/settings.xmlw8:U:' Zz{:o#f B'3{v$7_gQXyEq)U5OGtN?nx}Q?a' bwI*q4۷J& NF(i2I,kaE,V06mVYf4FQ.qYT=R erYvQ-JeOi-4uP$|SY4 nlbl{g?tTYa) SZꦄK1 dƟW$!L|Y.zKYѐƐ;cBk&s뇹B P¶b)@ٔ100.҄ jygh.ܭJKiQ\(d Gaw@̓02$(4\Gi QZc&4`0ľ,FJ|1QZ%;F fEg4B$ߨwRoŐv"I5ȵALFVI"Ո Նj;~kX1ۇ!aAl=#Cb \,-_= GGs z?TP,U$TzǓz@{@t ϕV?Tk5RCnmhԭﭞ̿Xg;uqf:|]U&6?#~0냇a Ws~Ūc BstMs>:r-@4}o|C Us;ױ~gVfi ]{n ?3s "PE{Q 3sq0sT:fRQ-ߔeDA_nRޮ?/S[#KM@WuW\j=W ?|oW{ @eP'"!O.v ]O͏Aim~mk8l2@!d-֣m0H5R< C(Il]mqGɥ& #oaz8 G[ b{e)ͥ`%lɌ,:yz?ޭb DVU`T{$O g%1] ]CWHJS(.BdHjc Dkg'?ϰ'sQ* /C)iQ?9bBRуf$r^EYxiPӕ: ;}2TmuԂz{gԘw+PKԭ' PKs_:Object 35/settings.xmlo0WVkTЮt$XM|_?ڒeI'Dbtn7x.(:oZ `.z-ułxF0y&@J5D4t&v׊8s*Ft %Ӝ,){Z+)CǶ}|innndlAQx 3;EyiO%qH:{@sF%:6eZRH5+k9j|ah%w6Tw(VٱN:4`zreվf'*e錋zN_BĉT/,_ ~)#טgAQwIQuc,>mr.% v8Si̡sjrG]y,Z,a iE^4l>Oɗ| 4?PKn"PKs_:Object 36/settings.xml_s:pxiEݩNN@t;ݷsB7b;w$;_qT{Fi)QRC$Ls{gWt:jOhLj 1ƻd58%OI gmcYЦ "گ{ Xٲ0~K|>?_47ZVP2GQe(J$()`MUuR't~X߹^f(j7 G1ozgƼ?Htie`”z]ߖq4e> ljog;un4/[ehCA+2g@C"ٌB| (a;}26<ゅC^RV<( (l F'aO@\Ӓ061$i4z9At0Pe`Yber-yH5Kv0}*(- iGHQކ!5DNjF kșr( RD%@ (ՐWְ4?cfN#HKa)NbA~hP\~ (P-%S5CHVuPR՜{6b#/\jttg~Mcy1{CmءkXXMo^rzhl:{eGY^>`)u Ƅ~<7gsaÚ-BtuaEapt%@uo|CMMs׵g6fz떗 C;n ?k+KLB"ЮD;Q 3Kq iTBܚ%I-ߔmaV\Rn<-A_[@uO\TߥC>k}пA1ph3.O.(JGp.J#P p2e"3ZG8c[krq$ c,s9ǽ$Lp?Pr 31d89>A)dt{n۵s*51Lx noh' 0AES%:Y<.#~~EAh~8ك8S L':Gi 'fX/t19hI@ze}Tf9hKQ*/6>:dFռ,cF&8cɫ<8R"KS_(.esϥY}tPKT'9"PKs_:Object 37/content.xmlXj@+n'`O`)I 64tHx@ib[mR ~@cɲJjf4s9sGw6 Qh01):T^_w>NZ烃#@M7u-W&8,JL{h 1>yM)Z&7N(/ 2p~sh4jvC=V_6b[{RrKyP{KsΩ"'joZ[HbGibĘ 0LÈdVq$#޲ťp.O&6~'`cq6KQ ^C};2{S`eS6pȒ4w!͈ "7 gyMg:uz1(PK)YPKs_:Object 37/settings.xmlo0Wڭ#*hW v$XM|_?ڒeI'Dbt7x.(:oZ `.z-{ułxF0y&@J5D4t&vϊ8s*Ft %Ӝ,){Y+)CǶnu:{w7"[婨x{"@zBn6/;!4$~wΨ@Ǧ]Y RXfe8Ip 3 ܆e7vVǰ>SO._^UrtyɶqQ_8*e>]2Grrwdf,풶QJ _RmdrDBn?wtޱ}UX Fo<Ԕ0CpJZ"2.i1 i.f#1D8rsD LIS#bF¬7Q5\NX@`E5M< >hFN42bY fЛĨ`TaRF$f$.aCN%-#=0`\wEWY#&@yIPKzOfPKs_:Object 38/content.xmlJ@O1IXП$ Bc %4 n >1&/,3&.2#rкc TC1Wr߶+H9ja6@D =_W! J~zf* )dݑ_CD)bV>d2J†kSH"tކllcc(ą"6MU'" iEaJg)O9Y^~R\__ϼ\)Mhn<^gU+#(I< _Ko^PK.1PKs_:Object 38/settings.xmlo0WJTЮt$XM|_?ڒeI'Dl|N:oYj70=ʖ]ipvcup.8QL Ru 5 'nZgAH‘!d󱷳O6>e]k%ez>__#_ڭvmZ.]{D!ē.+;o5^D~?gTB}?SZ QXfeǞ#@[_@ePs6fSr2su 6bL6`^T;w` ;ӌʈf5iVDL0Bo"bQ hTJɣć8q_A: ^1z`NjVd5BMxyVZ<;_ۘ 7د=07e/vD[!{[#T\4i}|7LWg)01+E62yi !W-U _  \nݎf.Mme&ts_hk(ػ=j|Xp(>*WUr)ߘSdmjm; Vye[ouc=JÊa]aǗ.ogԺQ~3|;r\vYh;*h0>Q6Xټl^V6Dټ*wlDټyI2#eF)7RoH2fUfʼBU(s eF'+ Vkؔ9+6VmPw;~wYU݇ա\hWC@m Wo,A1F=G;xascz,A=ƨ77sg?NTE9SzS M1$7èK$`KK!aHa0$a0G0A# I@ $ ac'5g a I` °Y$aXAX0@Q~ 1YBJƄdxF1 %cLObjectReplacements/Object 12PKs_:^O{e?ObjectReplacements/Object 13PKs_:X`n@ObjectReplacements/Object 14PKs_:CI\/qBObjectReplacements/Object 15PKs_:~/CObjectReplacements/Object 16PKs_:k@3cEObjectReplacements/Object 17PKs_:eFObjectReplacements/Object 18PKs_:5HObjectReplacements/Object 19PKs_:PeIObjectReplacements/Object 20PKs_:MuCJObjectReplacements/Object 21PKs_:߹XLObjectReplacements/Object 22PKs_:}MObjectReplacements/Object 23PKs_:RZNObjectReplacements/Object 24PKs_:i, OObjectReplacements/Object 25PKs_:E=|QObjectReplacements/Object 26PKs_:%|SObjectReplacements/Object 27PKs_:I|rUObjectReplacements/Object 28PKs_:\|ي8"0Object 27/settings.xmlPKs_:"O}fObject 28/content.xmlPKs_:8"&Object 28/settings.xmlPKs_:f cw^ Object 29/content.xmlPKs_:$B8" Object 29/settings.xmlPKs_:=hӶF NObject 1/content.xmlPKs_:7}9"FObject 1/settings.xmlPKs_:QjxQ{Object 2/content.xmlPKs_: 8"Object 2/settings.xmlPKs_:x oCObject 3/content.xmlPKs_: 3`9" Object 3/settings.xmlPKs_:J:;)&Object 4/content.xmlPKs_:bPe6(Object 4/settings.xmlPKs_:r҈+Object 5/content.xmlPKs_:GI8"-Object 5/settings.xmlPKs_:] 2Object 6/content.xmlPKs_:Z,7" 4Object 6/settings.xmlPKs_:iXQA9Object 7/content.xmlPKs_:Lc:Object 7/settings.xmlPKs_:.f} ,>Object 8/content.xmlPKs_:_7"?Object 8/settings.xmlPKs_:mlD4DObject 9/content.xmlPKs_:߀%8"1FObject 9/settings.xmlPKs_:FFdKmeta.xmlPKs_:iYBCOThumbnails/thumbnail.pngPKs_:Fe#XoObject 30/content.xmlPKs_:s8"ՓObject 30/settings.xmlPKs_:L"W Object 31/content.xmlPKs_:;}8"nObject 31/settings.xmlPKs_:q*Object 32/content.xmlPKs_:d9"ǡObject 32/settings.xmlPKs_:;vJ settings.xmlPKs_:nMObject 33/content.xmlPKs_:x,`8"Object 33/settings.xmlPKs_: \12Object 34/content.xmlPKs_:2}7"cObject 34/settings.xmlPKs_:ԭ' Object 35/content.xmlPKs_:^ XLdObject 35/settings.xmlPKs_:n"sObject 36/content.xmlPKs_:T'9"\Object 36/settings.xmlPKs_:)YObject 37/content.xmlPKs_:zOfObject 37/settings.xmlPKs_:.1OObject 38/content.xmlPKs_:AKdObject 38/settings.xmlPKs_:`/*RRMETA-INF/manifest.xmlPK#}wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/ellipse.png0000644000175000017500000006554612014170666025732 0ustar georgeskgeorgeskPNG  IHDRr6sRGBbKGD pHYsnu>tIME 8œ IDATxyxT{d&; YI $@X Vw]֥Vmֵj]j[RY-VHP'kȾ}lu53g=!3<eZ@}v=㝖1W;ѡ -o裏pBvi:sC۵kWB u-b;Fi)?֭[J'NTvvvec - Qկ_4a„NQh}ݧZqJII$5J!1ؼy~i;.X,7n;zzp8BPac -@{=Mlr)\@33B 1dZ#tÇw:Z@];vˣ٬ezE{~vvvS-"߯F%%%}7*??B 8>x -YD111JKK;Ǵh!B 8>:::ӟTK/=ǭ^Z!z8Z'жm4mڴ.)'g:RQQw2L]@"##ZqqwEsf͚SLZ >SKZz衇hƍ oc -z$IW]uUbz:Bk֯_O? e6u]wvFiʕ+)n:7$M2EF:mN/^Ltm۶颋.$%''/_i͛tY{y]yqYn< ?YW]u˃w!+.٬CnDEEE?>|N?PLL;X0aT.KK{3$FJ t -$9˝[LMLR^'$Mb;m[}oJz)pq7x$)9jۿ*o.$y&M p -H55JN=ZYº{۾g]$fo<&1O@ިU ;˜ٸS͸-PBwYIcuHn~$D\VWuoy-+>2gl_H ˓ɓJx|^R^7Tou[NNU?-%x*&<'B Pulf&LR~Z~2$_Yd1[zªj$)75WWOz_.qFA(rsB dVvbvHy4Q^q/'=zZ}߼QdFNIȰYT'չ$IWLB3 0 ]IOץ8zzRWN@z~z˔A(ZSZ-9ٶg$)*,J ?kDzt١PH@vhmڐf -uN&/5,ao[9˜ZWN8MI|)5* Ap|um/frs5?-_3 -?$?I&e%fI)d Z˔)zGSM(8npHr GC{ TP^ gS}P >Zjuju`yu9~K:eG@qZW.o|պJW (jݷ>9˜Z[Vn,a2Yiː|MK8"lS8 k1Y4>i|Hy4Aa0@ϊ R,*3!3NN[!B >Y̖kTZݯY++VY攳ܩy:nVbv-o藳~X{,Phާۮ5kBkQm !9˝5@ nz&X^T# -s<> k C]>.h!D܋k^ԪUC(Z\zmkjwlӤII򔕘%ASG~O{򨏝8*fS5N|$(N`߯jI%ٗh9Bzִ!ӴtYHy:EQaQIҖ-z'$Ip=r#B\^[j;7ݠ@qPp<~$鎙whxpBݢU,s@2*ծd؎xL愮z{ے!Ct̻B XCkֆM7mPS&ѭ5xRdX$PhjcFY̩uU낻wjOWc㹂紩v$izt}kB Ϻ\bhbrk|8ڶZ=O{)~yiy!M2i!uR$9lq%VC{$IWiڐiB2ILyySC0Q+,I ҃s$ -FX)gST#&<]N~Zf c_dҵo_+{-&'׼}@6Mi -_sGst9{{Tۜ7z{ۃ)(̩"?hnjQZy|:Y>%%y;B iZ]ZI 8?$_ىٔYZEuEZ{E&R^ssd =/?-_/zA# )SS**, &0 bt%=]*+ \pZ-G]::{'P#PU+WY攳.Iɓy$.UTVR`ҷ'=)D0Z#{5S<߫dVvbvp!4QppXjv7K˽NS& iu;FǏ)SR(2,+$ņS' -@>z]8BWnj9 nan~f2$I̾GCUE*VYTtxϽwk 1iY2I؄  vZ'gSeSͦhGNrQZ88pAy 3|!뭩\3N$0>OEE,sjmZ}.n ΆZ_2nvXVOkZLMHMC5!i,a n]̄C59eQ+ t,E"48bpHyMU=Z:$I11Gm2@%ڪr97yOn:mSf-<> k C -(/ 0(~ïuC5jw8٦e&d"Z/04!lr=ɬIWNr­=fd2iԠQ O)SE`Ky/Ӱaz`JJ! -ϙTRdRic@/pBo$Ik*h+e20a+iiRE"H}oujU*zZ5oq~ByڴreI=٣A;T+TsGZ|WLϭ YTau|/uS p<l$]09P(T\WR^VU9˝Z1~_$)GOP9a1@B!<_:E/UV}A0^_~O~_ ER$I%u0w]^5UG(ޕA|%+c, Vn:<\2=]7ސ$u]zpB06]jlɯf2 |VX~ 7d +ڳǧFVZ[-jk3|Ӽ2y{ܯ^Y^oCc+2ҧhC& dV\E(pjjgSC{~?Z%|گZCuu&54XgE^o(!ݧpl6CVfb ]yW }}_d v><IVky{ZQTzb`:t͕!CG}*yyM 3n7jm׻}#فe毌pun1,zWTo5+.άx)1QJJ u|dVϯ|^?;g28B ~J^UVT]SM:,jh:~/H&!//÷@}r8v_6 dWtt"""eu.]-OwXx59u-RTMQQbj[.kϞVӱXV}x7ުz pBoo6+%zQ` 0B O3 ~QWZKK}ڵ˯ݻ AJ l(::PN\R|o6%%+9١hL0ny͇򍽿 .xN FLY |tA-jhhWKYMMֽjl9pqrC7ت-[X,bcJI+=]>ܢ#Ȑ10@|/}׻v{ ::g&*s)r95k`4.P'poQzS#IO8UNP&z@7U55n56Z:&<ϕQZO`wIMe+K /龂cG;vxk_fUUTWgbRÛif+>ޭx<*>ޣA<تdJLtjݷKq8OPpןev|\ NHU!gMIzxi{R}}*+]|Z &`r:lZ͆|JO74lIÇތ|8}aHUURIIR\QqO%%VsUEDiಯNI1+-ͦDGEKƨS/I2$W-ԝ+/$ foT+VZ{w}-a{h&IJ ! f )CJ#GNoW1B R>_`Dڸѭ"JJ mnQii\/~Ucc=JH/ kjMGሒλȁ/ ٳ|G9fHZ))*/-QY6't^?PfC_ťVvkSmM655ocҮ]%-[vu}3F;֢1cLi̘tD T=v쐶n?ںue_۷vP|| Y IDATG ^%'5dH222.8ͅǓ3|["pf%9(ОiW%J O٬TBD'.544]>~Sk}ΞըQ~kVVUcQR8;@-p\ҶmjIyO%%v8փLfJLt+%C))n%'{!#qHbQ|}a[3|C]4k3E=Nnw(%š޶z׷,6UW[UY*i\eUGwM&C՘1RVUa3F1B|#@i.izP**jfm3nWRR:VFIÇ4r]Cڙ1ʷt{T06g$)L;N9ՅKƨ I'}qno6纺VSEE*+TYS23}a$B Cv^զMf8>%'+%ŭ)::z1B1uK'DPkzIiU&sK4I=Vx$)g eǽVUhVv2Lnھ0C#Fnv4i4yr].Ph>-0ںaC`uZ6lLL̾)ÆY +zl}DOmS0[$vUh~.8G;Z$IOyI|P+TYRIK۷{s 쪪 W{MkX&N4gQ^^rr(@z-[+Zڣ ٴ{wa"1ѭv ڮiȑv IaEW^?TYf5JNWVVTW?b$)>D a$\JvanmmQe;rymvUW>դI&Z4iR`Dw0i+*,0#0`TZuCkU\l;p5RtG.edthhƏ+++R{-[fICTfnj34+}R> WORFƁnKK<*- ΝvΝ᪪ aΝV)֗_/|rk$CSZ5mZ%#вª׷s-L(-Viժ}>J7v- )=C{G^;4zYCD0 i2d(O٢Aቝ;$l٬o. QٮlRZpBFR_ƍ.eI;wUZPGǡ__,Cc4y4uU'FsΆ8{e5[U|cFI0-OڴIs }_7WPJJ`W# efZ#2#$E0-0X-2\{XfװL]>&scMw4xN:ɡN nowN۶uhfC;wڃ^TTdUQ+QvG&˳iڴ0efJfIҝީvo$n"#sv6GmZ̫+-*,t n <5#íѣJHfeŀ߲Wt+e4r^N6?:ZIj4%c]#Iˌp2ac@tivmWII;vU^8CEGI'Y5}I' %$)w~/Ζ$%F$jO(K0ТohnV>ԣ+|ZzL]=Mc;֪,f7a`v_.^2VU%ION{W3<[h%]/uIL?fb\'@{K ڲŭ͛;õsC;wrFk OO&Nl6rE|T$=vr^ &ϥ/0b_EEf&I=:P`Gvix+ppL:2u=Y7VqU7]8:Bǀg;PJ4k!ծv&+ܲ%R{%%fH۞_'vh4N:ɪOWZ9씔) :a="]ڴ!|7>զ,&O)77BVtKƪ.ɪޠQc|u]%j)m#|^# 4i۶&]m#v:[pm1äSY}SC{2!,_f E0ZҊ冖/4*4 edF^4ɪL"" 87'$7eHLIS/ӢW5# ydRtt&MդIO%XbC[8TR͛ ylyUo!Ff('Љ'UyΑ6Xf/e-/pҢE>XaV{{ׯnãGmQbb6g5h|ܴ9#R`$\M'l)/wi[gh&n8):͝kg5v^X +ծiX0ǎaHEEx%Kƃ:FDx:ה)6My5z#)k&hZޑHw=)brWfyuf͛C=ֻ[ޕ$zuߜZt-[{n&-_nWMZLHnSNYE+:WB XٷrMM -[ڲš͛#vΆ hCt&%%%w_>[)2,`@ѫ5m>$\vth4aB PDDvNPh$ڵ-Z£U*.r7lN;ͬsM:d)p}nݪ)S9;Џ1B aIRAIQھ=Ba:H9kTΕ<3\'`RX9nV3(Lr^TnZ.B&rz-Com6MSx4cUӧG(2WB ?&7 "iSJK~Pd_[u&Ǔ#ݺfP@ŁUVJ(~(\.0E~͜iWZZ4Z -0mSqc6lRaa]xYgҴiL~(8Cצ_*/?-5iRu %&QbPhQmjj矷O-Z2FUUyty&v$(3`KoihRZ[<am1 F[Fpr)7!Ig-{ B;ll[&\. mԩ-6,\11XZǧܶ6i׮F}re6lycЌr{yرoUPhrC/خ_61DEy߮ٳM=;BѼ嶦A}SAA֬Uc7fK/5骫L@۫_Ow.>CsX4mZl6J, -]nhVqڽNnM78@< GcU{{]sFjլY:T؅@-MM{eKV֚5q**!~xϗڤOV(E%kYUWybzKgmѸqXZܖhٲX}iv]xa=O=@EٶM?|?߯m:G : N:)^6G嶱q6lix57w~ݦ.s+m>IWW\'ABnHoij"<Ђj2jܹ: )(p {zvY%%0hG\o̙ Phqjkg UVv0=ݥNڣyL7.QVf8@VRb>*3ӥ}]7XgZ ?^2#SA:qG99Qd4+-_wxBwI[4a/k*hrd/w1c>}q'&Su9ӡJ, - vY7l6=Q?G^8 7E[{c?P@퍚^驧 GiΜ:]|O&K1 -O6xv裏zCGmGv<cAVjҟ&icFIIW (IIwk|ZZB@&'3ju&ed$SdPhzUz~#i0O~O%Ci5+d6 ڞfҢE^=G~ha6E"U|c !Ph{S:M_yng>CMۣŋ7Q[F3~{5颋(]xӵxbIN~1 ZMUk׆w*n>;NaaYiǎbmVR\V\`edQVV.{^WZ̥ ST\rNN^p~՛Eo] I7\~Iv`@=n^yQ?PaaWKn+E8۰s7ܼG1"KaavԔkʏT: u5Rrr:On+|Ү RTRr)b{[Iv(lm۳M("xA? ( VDѺ;u%7(66>d=ϧ ?gjooSX]_N<Zl.mׂڶ-bnƤ3O]$2-r1{΀B{aZ>n}qF,#BڬUo7IҤI3() EV\ yb;j=@yZ.IoG++˯l2b-B,6~@KxmYܼ⫯.SO3G)>2 =E~ښrrN%ݧѝ[b{)I+e%Ia8sNqB{ۥkөZiSXpuzjJKK9e'=*+m2t뭏v;:\mٲwU7_kǮczYhڠBzᅈzXefQd Y6{0ᄣv[[Keunjau b֏,s_d B3700*kuu{Ka3'" d2骫~-ۯ|j=GLe5qOK0 mKK`o85]O=ՠ`)Л ھ}SȲw謤ݘ1ا@/1bDۯ;( VVZtꩆ~k|>2L]:p^/`|ꩍzey?c۶EG"|F||.x~ZM{>ߚ5k[wЯ aH4}-[u8|zHEG3* ђ%|GȲpν[RIN@bZ=D=֮K/-ؓdŊ0 IM7Inw̎ѪH^Ћ_:-><Α(*ZrN}FHz$~{|Z nIҞ=]pE?q~25]SNj۶yUJMK^iuZ>sYo|Ň\>~|>Ojܹ5F>hʕqgTu_eU}jD:D_FEyu[um&eddPf[N&I3fmƊwZf24e<zQ.YH>@-kxt>f1NNnSOUK)>>g?,!!E))Ce ڸiyfd :'@`Yut[pRN<ѣr\]^sTS(.=Drr>xueݷ+ESVN?=FEٸѦi|ڴ|>Sh?ȫ.UccѪ'hј1<{TVlqݶӲX]pωOgң*1CTVf̙~Xq}PhE2%02զ^Y苚tZ6dnZnyW\qb @dwT>/R\zt= $\Bb5]Ib$TjT,QEUR.H[Xݙ)v z>3gfyϷ95x^z$I6Ml飏hB,'ptŖqPRRb 'H$۾r_~|}9)<^AA}A%I.3r=o.T+9 /9Qz p"KNNmǓpǭܹϷgOJLL:^rCrJ$Ѧ΋i۶#X=v'Iq@EtQUWǻׯVn.3.55s}aqz57:u&OEЩt떫YB۷Qc]g9KX)KfNDK. kÆxKlAsOHΠ_a %%4gΓ;Dw )gą IDAT$[7+>|㵵ӭJNN?Yv+#%>%IJJԻ7-Y9n}P,mݗG /G>ؠߧuAY֡; g>*EA#H>8fͪѐ!!DƏDv{֯?IF^}Zt~{@7|x^Ng|^ZUtnsuRlh;g]wݟ1cƍ@'sv}_~u+.7B2ƍ7oaěfRQD}$nعNha"R]zif4hƌ_+!mZ:Ɔ \ں i_\^ (0.tEe; ͘>w~x${zf vd֬;%I}4?+=qYw?]hd^lT__-ǧo~@JMg)##G7`(3Y`K;|s1_R\h;~cI;l6K?iHh ݔ)W^$/~1]Pp _M7MQCC&OB((./??W7o[jhכz)͛Faq3,p̅v,K?>>Ϙv,N}%_fvhza;nq =ϴhч{k3f23)h˖"}{z 6ִi7䓇qb`hT7ԤS$I7ؤef|v?/I3Lf,f sEuqz:h+k,ւԒ%TVV*ve+33WCQ&jI9!kVd[ZСW TT.Iyt$1yYN|0 ]c4xIR ]Zi`y{ƒeJk5+_Oy資Z rLΙ#UUŻ0Uϧ>6V}:Z7NRA;߱+99>oU5v?:ٿD@/CEUDZj-D.8EQ͕|nr4eJ$)3ߛ>?71W\6ܤND@ۑkDPݻudNQ$ɔ7U|fo|L3|@Ǻ SG<>ظ$2}zɶ5LVf&qx狘Ky QقM9g74F5a>V~Y;VǶI۶IM@{ЂAܝNS^hpuAm{T2+ݭCoLZKz|_[4sBmn,q.F4]Y==WxT_^޽i]'W?Hƶ]}!ztjux#L1A=\-j=nɠ J~GO$)fEҦt'-S̊9<"@{|U7Dz>aܼuQKmueUz`uݧ=vذpY.+HЌ_iΤ"ۭ+Åui* ln)C]TQ$兘8ޞzW ?ydv+_V0͸w %'E(n388#3quʖ$f=>]1ҳpX CUSv;=oCe7캴 zcRS9xȊv:2S6S(hU8v["pIthh|ŭWn#M.yyQg |1%&ک>c>I^n\|ݥk^Uip#n/+YΠda4$Ԟ2Lg~;]A[Eh%i6mS#rۥ{_9vJ]=GQw$o'$Dpe$Ucۚre^G)*[daYGv+㷧M+ӝwR}8C&߾W>DJKokjϙ.Kw58McMIq;4IR34s>]J=ǿKmwpSy8UG8}qiLC0YܛWFf7VK?iZzi lJ?c}A6lH$-sTRߦ!1 {_=[ʼnYHvk=_7pˀߍ~K<}M|)//ӟiR~~|K=CvҠA74wn-g]Z7_:cMȽDqpfJJ|=#o V!ygb,-c岻۷W׺ l6 awu:4>vӦ}A#g8HhD=U]Kt!O]@=" C瞶g穤3k+ْ${( GҥJ#G[i۶nOϪ93~xz͟)IrM]iiڣ[߲ItIڸY,\X?1o۠# G'KTWtUTq=WJT4n5gA=&Ni22b+RB-oB!;b/3zt|Ga@=W^nL=CUU)@RSSD3gjU|Y̱6 sSJ/l =dS8r>+4sf@Kz%I>_L/Р=ŴiCC2MN2͚W_z<1_矟Nq@=C /uݍ)//=ti0kwK.a}h[W]eןoj:UV2]'{Këmm2Nq@=3f g?N-:[_UXVrYzMCSR-M9PfMn9UeF gٲJrK,:1ty8$]}]l(33Nme[?I^zijj 7t*-fd7 ]tN@+Ig%-[fIRs]}O&E Et~=@cdh2.'t= 暶5|z+WR]]3 UfVriӂZĩ~)$y /#1lqK$kc*,FG%JS?ܠW_FeY'_j$wҴi~|s|>g .**i\+VU륗W^ɓinݢzuiXe}iҴh4dHXd^}5E\_3 2;55bIz0;~|X+V8@W ԿC˖tKxgG*UQ,8>B=LD?@ZҺL͛RV5T$SzD-\XotM7%jB֬bET\cW/{4w6蒁}K㭵[ew8#կصe;~!ntm֮5X8H'PYDڨ u5eJӳID"NdֺZOt9 8=ZZ¡;0[پݫriJఋF#ZznIߟf=N˗aЩ[hwxt͖/6Z9.Q7SJ 3B B/֜9 <9թ~[W#zy)''&IFmzd]qٳk3'h0K/^,@=M:ifliJzt]sMϯ`(p89sgb.àVeIqqT?QXk3k4cFHg[41gY??=I7ҐFqo6 ۂaX:Z]}uDcdlh…z,-Z&l&ү~e$.qkGM=k#5}z@SBB  4sk+fg[64cvS+@{ǘ\{A^Z ̥N@7 *-u{,'̙nMJHVHO=o~cigπ.BPn.NUUB@olXAATw5HD$"4UTԾ6%%uQ$C6ECCFnЏ~UW2@LS?xqj kʔMTNp,Y/lن }74ۥ h]Q?_ӟLh`jjX'袋"/V[Z8#z[6t5yWh2fi˖}T Ұa~MR]Φhgۖ.ܽ[qVVD?A@?qh4>|oڷȦ5qb.(>}h@aƈ^=?ƵumQ}^\{ZSi3zRI#Fkʔb-- ܹA͝kӺuOG嗇tMzC!ZXLzxܹLiRRDZS:4h@=F--XR\XS$Ҿn4qbPi|x8lm~O[ھ}32:ZM JL$ ͙GyT[{~f͘|zZQѨI/`BmA[Sc0mht-y'sm*.vxjjT]:Dl\'ZuқoZzᅨs$Ӥct9 o @ Ӱ,KMM~*+ hd-^+4iRKW xRU%ٳcCaX8Q'7sͥb&%KlYwoDhfHT|hq+)^yҋ/ZҾv4yrXSI@p|F#*ʕZ$YKjt6yf"9'uٳM͞ӦMwc5^gnnh,˒עE-X`ג%)t.KcH6@DҒ%{.9s*/߽ˍf䓛tiMСL@ Z>4O?uhٲd=>w̘x2i@j@%b'Xz|Ӯz7Φq|JL$ 82!ܯSVHV(aiK_nӥJK ?[a_=vKn hĈ٠Ov)%%%hӴn]>,B֬IT,{?aԩ1]yC_l(- b/6lһZZУ=@jְa9QjWvv*@m j°/6WԸ??9ǐKhq,n@Ӽym۶Ԁ 1Ag) DZjlpa@ FlK۶=Eu%1MҸq@Ųbw޳'R8҈ 3&SNq(''Q_Z:!6lMMViժD^}|:f}tahqT46JIki\K۶kaX#4rO(//[@ F֬iԗ_ZZ:Q_ɜj Æ5kҤ=סr#-v4wߎЮht5//C#3ƮO*'p hBnmRaaLK:zu{}~7Ykg>-s~GGE`իeg7kР& ؤMtW ItS1Ӵqc/nʕ֬IPi?L}S M"I hq>҇`Uধտ h(LTb"LZGJ(T Р[gاb|{X/sY1wCgmh1[CyQ-\ԪU^ö>?994|xDF4hO>jĩ^+VDnW'olvKԩy9uE=hхBҢE҂TXhS w} ԯ_H}էOPݻ{rA^ ں%hvOÂޘN9%24q[ga(%Z {H˖I[㨾®[뤓ԷoPJC:Y. ]tʐ-hJC_V]:,^yh1͛曰lTZZ]vб|>S4bѣ>\2DJH@sRQQ<._ʕ֮i6~fQvvX99GCzԷWt_8PPMM ڱ#Mbڱýssԭrb1~0,aiC ݅{l6jZkۏ]Ru_$&Fx[zvG%$xAE @ՅTVRi뀻HMWi@C;5d[ILZ˫ܵk**o,mlWIK>a*++3FӼ3uIn338qkccªvʥ**+r&=Bew , hN[[32=h0˒jjo"*.jFif˝I$%%FڵvP^W))^hqkMMoʩ*]ڥ~-}ŌaGSҀr0t^LMoȐƌ$-Yڼ-GT\lj&C%%\_T}S{UZZTEkWn{vVWkhtکt56f-YӦ~n\k߾^;'nwB=maײxm G~͛mk)ߩ-[:h4wވ?-Gϗ$V_3Djj "fUTl554ҿKI1ճԫmL-[nvha6͕h}~KK=?myd^Ia*)ܥGCvef:hz3%& @dXeQE,&=RccoQ;[:oKm!fk ˖ vY!ð>w]n)11x MIj(-ͮ JMmвzZ8P|;~SQ~KMMRSSKwcgXx]-C v|vypٲ%%9#@NʲPhVLґH->}ͦBBB!SᰥHDF En-۶xw.6%h_VT,fnԳgs-[pr8r8lr8miky$t@t9 @@ @@ @@ @@ @@ @@ -@ -@ -@ -Z-Z@g)ɏK&jgIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/fig0.png0000644000175000017500000012670212014170666025112 0ustar georgeskgeorgeskPNG  IHDRCЈ pHYs.#.#x?vIDATxOL}?1Mlbc&ĝlX6!& Bb eӒ[6-V6%ZjբUTQT@%D8́8asS?03yD"/0|7牼4@@@@@@@@@@@@@@@@@@@@@@,..&IF)FKKKj +R233TP$hhh(( 4Hooo0+s{{[]D2۷#T(TD|>?==&''٬@#kuu3 \H;;;y]]]+w& fyhÇZ5P˼\.^p-"jWWW0H&JW/rRT, fyZՋyE2ROh42XjD*D"jR TTjpp0uuu)"\ȅ>;jW/Rc[捎f2Ka[[[y}}} !R#[Eѕ5|>P˼U(RS2(Rg fyߩPz=Q:\]]Ub]ϕ秧C-ݻe(Rg\^^9::RkZ2e^__r@u"Lftt4EE*D.OmW|O.SweP˼t:P|P˼M Ed26x<^(+re_P(̄1-\+zkkk===,/%I#g\L&y'r-_5͎i\׾w^4 fy###FB":;;ERTy˼D"al$妦B-fgg9x<j7::d Dh׻C-m.hL&C-EݻwHbeH:6fH#NOOO0+N@4 i@4!rP˼|>ohf9Е.-hO&:֝P˼m@ˋ4ݾ};vqq@"MwąBaff&MNNNZUI{}}=2opp0LQZRy=LM @4g26x<^( -$'P(fggCYޝ;w٬eDZ4Dggge5DZLC-:;;׍1- J'Bl߿4M-ztP722d 6+ҒgbX244H`0ˋF+++fisrw m2i3|a4 fyZ\"pۡy{{{fiLRy^4HjP{neT6:E[ z)"mx;;;,NV=Ohdd$EGyAИ"m{|~jj*2off~/ Muu+ i JD {zzY^WW8y'NZ=|P((@Bavv6MMMr9 >byy3  Kj2\#A)rDhy\Ac?>>V ,kkk]]],O< d2n-b2oaaAe2J=xe^6U 666Y^,S. bR@0\^^V. Nm7==K"ȫB4 fyoN*eUo{{;2gkkKe;A^M۷ClxPPHW|>P755%yj722rtt2ԅ nvvvz{{Y^wwP;A^=e2P˼h42HWgBazz:2orr2)U]P˼d22TGwYvwwY^WWƆPA%fcccmBAqAޥ{A(d2*@yWa}}+ ]X,P Ad2omyWP(ΆQ-8 $y*G2 +2]l6;11Q2/)!kFY@:VyokkG< kLfhh(6 }A^ӡ,ojjJ<5x<^2/L @5`Y|3A^#:::2A^rw eyZ'A^C[ZZ2yoww'2ommMeڊ d2-ڙ 9 P7::dJe^,2~, []]U&k>ǣmZ0A^yC666Y^ooΎA^sK&Ztj˼L&8KGjWWW0K&*py࠼eÇm.{ny˼l6WTYl'B@[bd2پS OB@Mvvvz{{NN@ty˼vy-P|~zz:2obbZ東ZvrѣGyT]3:AP#jR OTG@e26{ V*y@- _Pw^(˻sqk^Ey< .zggg0K&-x 'Q핷K$vU I\le\˴̻:;tBa~~>华f2Vy'pEDwww0k'R@d \T*łY^4]YYiG\YW*ݹs'ݻM2O\Y,ohh(N7@ \`ݽLOy  kL&B-bma,()שy[ +&E-,,KR{#"B!Z٠ r"ae^Çp z"H. eylp<rB4mؖy<ZhDݡyD/ey<lph{e^s7AP!+ϗoyWCG{ae^oomEG [__W}hcccm* AMP(ܻw/卌+y4`ŒɤW$r5>ҡh42u$ȃ*fdFFFBlgffũE3< J h^ h42/ɨ @ypWԶY^oo9<ɤm=.{֫R h=+++ o)h)VWWUh1ۅ =8TZÇA|A|f ݻw?0??GV?H$v~+J?d4 TZO<Dpjx2 OV)\XXXppoo/[|ޠ yhWz.cE^Spy88;;e"nuPUkgp0rpjjqK5 TZ%ݵP(*D"qa<|$ؚ?N¯rOP\]0(+ yhWzG݋y`tydNbT*uUmmmX˥Tkvv$ ^tCqm(Dgn~*4==z&//af9|>_ʤBҚD׸xPAZ!2A]J@Kh,sEyT͋Yl086[/#Ujm(m^*x‘ TZ>:{2X,G?eA4+{7/XhȠE `)@;;u`uݪ^*x5zkkk7nuﮊPGv>VSZ3xʻ y8xnsFb ?yP7+++[n޼L&Qbubq/⥧@3A=/RhO @:n$nU|=K5p7v޽FST^ZZ*^㋋ jT<zddD<E0b񠞃5n(۠p4]]]ccc+++Wx?*.^>PwB'믿n* 匞X56WXA K\s\WW׹Fo)\~:cg_qUmІFVK.Pl=99Y|eٺI2ЕR@/ȃKU߼y33ƍZ 7776811q~JI8XBkVR^NÇwwwz> F"|$Y4 8B/|B;1pjUŃm###'8)K5\YY ł67ꂼ7&ɲ/E"H"Hg]9A\-pcAXz`4(ԓdr\َکVth~yypES[moo+JkOP` Hx+ֶ熪sss}|$R83+-633-A\[n_^^Vz .nCeUqC- kllѣG&GGG͚*NNަYҡ= JrzԖy@:Vť k`)Оyp=vww?C?_x-E{bAkJ{V(qI&^Іypm/`/ήFpʻ O3ZUw6Zzb> : ~g{ZВP\yP<_C7_zE%hC<~-nݺe<TŃ>ַA^ ;fqmH awwST "hv>~y],㻻n)~(,ry I J<&wNq~W?::F'D  >\NX|T*e@= q~|W2WJkI:;;GGGWWWt077Wk1жyDqF|ӟ2*nxxxzzzuu5J |L6\whBA888xWB7n+T⣷x酅ړ_{_dOx'j %3A4l6{j˼{i:5]DF?lޔF777#9A4B0;;[37ްvүVwU(~ﭱoA4y===SVwR(qq1htff_/--)4L&366vFHhԏ޿8WIDM)MNNX={vo߾}pp@9A4nW;FxqJrt8 Fy{ܖ^6=uLSWEB@9A4?$X,V-,766 WvG]?z+-GyC޽OQR Zʍ7B3W_}U<>V8W.Ԡ$ŧ:ur^nqqQفAD"SO&XlooOq ܻw*9ByFR͛7C7nhUcF϶WCA{rof,H=̜xo. 'fz $_\\,oke2Mlnnq;mɣGNuu4.@ ?;;;/BhK/߯8L&s)^___}Аys/~-JBvxx800P 76NG*r@  Z:颳䙗xh4ZZ (rƍsX=Wnnn=I24"@ 8ny˼^<d>[cWCAOH$ P"Ndvtt,//+-Up~]govdw2vV}(X[[+oꫯi?Eӷ÷jٟ;88hAp_~94xgdbEӷ3xB` <"7'x\qhIBall4}'*bX)z54X'y1?'of6UZ\p_VH$"5K&8!.f}}g)c*RZjp;55unmmq[C?r߹s /H O~;677L&[br\飩Tj``Od2h F8!Q|.d{@Sf)nwww睈FǥY^^AN?-g|;2511[ІgNOO?:<<\ oppР'y@MDggghqM-hFYϟ;.m=88(.jOؔn 9APW^)m+Mdcc#ݹs-NZW#&N:8>>/p!M!LoG*B}]jcy@Wǯ~LFqhd\.x#ڮmoo/RWD Z__gCӎ_|qwwWqhXF-ϖN֖ݻ7===>>Wuc/.J} _<:::~_* (dnnP.K{{{DΝ;}n``cLNN&}Apw__i@CY__&hcccW3e)}ѥD:L$LMM޽{H ˳XG/Z K՞+NLLښ*!.'>P /h+ åijggޕ}5MM\/~-󖗗k477^W)}t:m8JK~y?Zw޽i'F[WcmmM<fpj:22rQQwwwBTN4-ok@2Loooi>s7(IR2R@y@#J$ZPB022P7()*'T*g>z)-8lpJV~y˼G hwwwK:99i muuƍ-x<>|x}}t7򀦷;;,Ύ .gr "p!<dٯ|+,޳%龾ܯ{~~t;%ZDPxw[MNNr9h۷KSh4,1Km pQ<=S,֭[dRqZLp,G5G*2VS{g,..gzx~``t|h"ZP&y7[yMmkk+xygg.Jx<|M-T& &:|>_:X,f@,Htvv7ojtpp^tStbL-.Jݺu+=SDB7XZZjS+Ԕ1.J|>oFq-j5t3NDfgg +pQ<]2_fR~ (.J>n*Rt||<00Puww6ϗNduu%˩-h"PFS(FFFӶETީh<~e&ݣЪŦ>ҹTAy_Wj~L&ckgg^eypdwwwiZ[vb wm-N-d .Z`#j>(Uq-ݻ{7Qq6<<#N W**T 4PAY^^.o/}I+8Ih <V-^|d28W#HDdlrre6IOt^ CL^ eyKKKpRTlenaatjxpU|D>˿yy'b t+|얗8PA)VWWzP788e%NZZ쀫!8/}x<t t Ӫ-]+J)/,,w~~k ͛7CY^4T3 .fggKeTGPBo~eLOONpSSSSN$^@uy5Y[[ w9}巇T!ô6zoo+ V&/'j\ NTGP7-ݻepsl6`z=U2 Ҟ7(IRsz&_~9hhvv*Y:q/ j8YXXPRA z,//GP799eФT[[[+P AI&/b({WWR .%|svvVAZS& eyhl68ɒRe\|>Z .033S4_-oi4Ƣ 'P/ykaa!8EQS핪4<< @y+;k-Kq벾.:44TBMMM)P#A@{'Bq޽{TznpRfgg  lll?cLP.F:;; xTj$hdW^ ey\T$HI[]]UFW%R R~,y@u2LF ]]]r!˥NOO+P;A@gV2b}}]M.* j'h) -xl68@ TQq&U-peeEA Z'>Pw֭t:8VWWhiqB,UjTv<f/'|׿g,FFFjåJ @y-wމyT8U6/MѨ-PA ZʓO>؟Bn`ݞP(~p0??&d2bvww+P<֗fQhss3x;wEJP.yw-o4\:fR=C?{g} < # k%Ř`RDbrfR'$`R38܃,9Zu 0Ɉ6sls'2ʍ317?Gn[_>yD?gw_?2z?ڷ|Y<.rĉ#ٳ,NZ-͆d2\0Ճ X<2??ƺ[J%@~Wpi|> Xp@բmr@բZ^^c]^:62@R><<XXEVs=YarrR8|۷o_ŲFFF"ւ"wsź}sjU8Xz}tt4|NRgϞKd0yEyyۑyp ;&HaJE jQp?ĺu?$MPHRKX paHh_92//7䘟 _wa FӔJ0l6+`)`Ǐuy~{V\RpQ*4333a###V"1??m۶XwW -O:}i4SPy\j:22:|pI0\.'`)$=ʑy'bd4ɓ'prÂ@UR t:򆇇 fZXXpEˍWm@U`u9Djm6#9p#333V"QV~/~}9"!lx!ԩRx≞XCA X#GNNNʤ#G| .Elfffƍ.#p`՝:uQTkd.ET*uyW^yܜp`---m޼9|ݾ}.Z]Px9٬@ե`MAo]92ꯄVر#|MάbxEFGG.Ek駟y{o㍇pI5BxE.Ek̙36luy_}R\\N&I]`(Xs7pC۴iә3g.FFFpuy4Co .`b:449 /MPy4ω'V̻;TV=baaA,1<<^YK@S5b]޵^(xW8qB&244^r,`u)h;wƺt:P8r\srrR&I&@AX]<Z^?+G}SNEuET*e2N@˼ +GݻZ J͛×;vxLe Q6yܖ-[b]5\>X8pNVpd*G&:EsrwƺF_% Lizz:LV"D׿rd<`]رc\.'y$ /nݺX722RՄ@w{n&Dx:E m۶XuƯ nSVo !p*V"djsO}@W _ Sԙ3gdp%s y$O?md,E_}Y$_& /YRy$TXuywuytT*߿_eikrUTvʹ9Щb\ڵZ-o Z&y$Zuy}}}w@YyE\K[ /@ }Z8#GD_q:6Ky~===:/-733g_+NRq}{[92odddyyY8$VlLD&YZZKT*K)`(B[nuy^{mׅ@耈t:mZ+^akD@cX72:p@رc2ikb1cccֈ"|eȼ'x!'NVՄ@;v,͆RX#<:_\^92w~wfff@4^qtk.a4+Ap=ĺgyR="8|А@#o U;"VUt|>^ƏEJ\L&酅t\.^ikG@Wc]M7TTũ###WSN9hEݮR޽; "<*޶T* X;<'=ʑyw$9}ttnȈ /͆W<kGwu{wbx<՗yJ֭[c]Ν;]-//GrE Z XS< jwwc]e]K/ w0::}pzi(OR XS<x=XOOO;zdx[ї\.'.1;;^1kJo_uyZM8D8q]P((pQo5??yXm۶r,Ldծ255^|>/`M)To=mذ<~ڵ+zܜXJ. @PyvdޗertĚ@5rԩ'ι뮻Z.e҅J%kJ\.uyW_}‚pͩSb\A .fe` kMVu].<= ݬq ֚".__# wݻwG0kA!kM̙3+GzFtÇ;sr5E\Rm۶Xe#:ѣGpAhvv6\ ccc֚".^^g?^xtgF޽{wZK7+ zyp&''߳yRl߾DpI4~,`)`9sfӦM.o3{FobQ,r9fRjrK|7:{st@3)`{wȼ|;h_>.x[###(J֚"Vً/Jbu?y#Q쀋l6 B 4&P+JW]uU۹soKZp188 BђW@X^^馛b]ƍ_}Uz><< ~  hE;Çw1$?ONNʄR.aM/[nȼ XсF333]!@kn~~>ĺk\.  p( "<hZwXxP8RT200P*J|'&S@E$KZ7ĺ+Kյ~\p&&&355%9y8zK_Ryg*b\<2ႌP(hE$3<ۻrd)hff&zpb4Mfgg4"knnZ92oyyY8T*m޼9... 544r,9yhj5ƺ7P.N>-.B:W':Q@￿'62駟K=*.BR WQ&4<h?O-2Jp!eYM1???00b\ٳ_p))\KFRĦ;5_'?pd@T P(i||\ @(ÇF_R###{ɓ'¥rrFmG?Ѻub۷'bprኚ4<hW Om۶zK8LOOGocccz],\B UX4<hcZm޽+G+(Jq׮]ۦXtk hE??uy===O=dnVTpZfj4MNϯO}jyyY8@w T2apuIh&7Ri֭.+|7tȑ#2aA.L&#y9jmRBq o`pu  h&EtƛՕ#&&&A쀋۷/--U4;;4":+qXя~<A{788 VWP=@fR@gT*c]ަM^ujћ^X njj*\cI+;3}yb\hXX#\.\fSSSIfOOO72$gΜpO]5x '*<|3336luypиm߾=zER kdtt4\lI]v׮]./NkZVf-JVXS2Lݢ^8p |4}lf2Lޤ4t}}}:o߾}Ah;O>dlLXSpe2M377wǺk歷FΞ==b޽ZM,r.aMnTTX~'͛\8& Wؘ@&S@ʑyO #$޵=*cjj*\x|^ @)ەJ*e2E +OELڛd<c]^(@ΦN{n\L+Lz~}g{L8@B,-- 7͛7W*L ,JL>r@AиENЋ#|<7[ƺ+rnnN8@ GKG ͗dέt:- y@\V;b]޺uZ8@K;v,zG:pLh[144$ w!o'y^L (VU|J%:wB @)^c]7l*4juǎ-hpppqqQ,l4"x'KKKc]ަMQ8ZCBpAkp)NLLh>E. {c]^__߷mkGy|IBSSSj|<|߽@8ZpgF'599.ˢ@S7\92oΝFT*m޼$D&Ţ@SR|^92W_Z]›.HYͧ.؃>?Vž}wӧO˄$f4:<bLOORأy`dpq\LL&\ZHo-[b]АOX,F`dd$Dd2ZB\һ[o5.ÛɎ;BBpq h Epy䑕#~i/ݻwGJa$JcAsttT @K(U/_>hw]ՄGo BA&$JcMs||\ @K(Q*Sٺukׅ#GDo\N&$Ԕ% "X5Au].oF`ff$_. WԔ@POOc#}Q+U*LfiiI,$~-V+aÆXwDAw^\FGGõjD\~{߻rdps&&&iXl6\8-JYnɓ';v,zs I=Щ'%yzgb{޽{pAA(Uy{W7n>OUU@pXHrl6+Uy@޷Cuy\s‚p>JΜ9#nvv6\ZE4I;bjjJ&$_P@VQM|/Vw}cA78qD*rm'χ6 hEl!zFAt:={8v¥"hJ8mڴ_tl6%.[("h/}K.矗 t\EF xffF @(VzgV>WՄ#vő#GdB{>OZ*"h7|sͱ.}{_RtǏGwؘmh;f;-ZZtM.oƍ?OmT*EfAt:-y@R<===:wMU*>H;jh-y@?_92瘠ݻ7O:%LGFF"H-[ĺn\. ȓO>ōʄ6U(C"H XN ©SRT3|>.\.'y@B=+G=cHpر\.|>/y@r ֭uyw}R=w :x @Rm۶Xd.Hn=}LU=33#y@X5аnݺ'DygtbbB&tl6jOSO=O>id$DXp122b{2L B<m?b}_^^@ At@R\.oٲ%  ZV޽;zXםpmgYLV;c]^*z-166ݏ2cΆk{ttT @k)o}/V=frAMMM9V(=>>.y@߸qcG?j24ӧpAgVչ\N @k)6VTnXw5הJ%Z+˃MNN|jjJ @k)F?~\2v޽{pAǛyPZ<أy_jp`-;n0::Y:DUS~|#T*uF;tLTR/ZKtjz-ĺW_}U8ZΞ=N\xd@R^:t(62رcKWT=Jg/ 7%ɓ֭=ݿAяRgϊVTf"Lry˖-.n\ >Llr<cUoqȼ uV:pG\xb1\ZNtzkx駅ovv6zŞ={pA7 }.r<MOORX7::QTo$d򧦦" R骫uy[n-wPGFF\E%s!| y@jGc]ކ ^z%o裏F̱cdB @SG]92G1V:qDcccc2 ߡ@(K/mذ!}FA`GvUVBW 4$p3Ҷmb]5\o;100 MxXs&<KA0::~bP(ȄnST-0<<, y@W{ꩧ̃\. ]T*`ttT @(n|ȼniyyY8t'OF8x^T,Í011! y^^^f.磌22nS*\4C`Z^<R׿/]VE큁R$V>Ԕ@$P[Xh!`߾}ѕI&&&P,$"7_~+GU*&''kG ]nll,@WTV̻+~_ t{ @,tpSeIx{{ow#:L쀋L&p_PVǏ_92o߾}F1 صkWŠX!N4PJ\FSN8!?XE6<w|7ƺ 6¡r2sfggí1::* !y^?C===y=phSgϞ ?9 )<wwGZ&K\޾}{􀋥%@hjjê@).UW]njdm^ٳ'zٳgQ< Sn#^8CEWVHP<cg}k!=]cccz],3::n H?я֭[.#H{\m ;T* HE[\\b]ޖ-[CT*\yjlp8HE%V{uy/pHz}(JEo}/4ݝDp'V=æ.cǎ~Jnl6+ 9yԩSׯuy{V¡ BtYl_FFF$"`ՔJ#r,Ze~~~`` JX,FOv<T>ĺu?-Y;vpQTljj*5\N @r(V__G۷/pcrr25|^ @r(믿<9vph|>]~SSS2111nB  9ykZx㍱.opp7kX,R)\E ̌@P/~.oo$Y\\d2fNO2)JC{#>#.EXjڮ]\87.Hw> (<f۴iS˻='u)F.MT.BAnt:- QyMRV?Ǻ+-3r:/i Z'pAr$"xl?}$sFVC'rԩccc> jvv6DElw{4K_d.qJ%\+ 6\ @(ZT*uyЇs."/tzaa 055n hZ;!JK9ORuXwB:u'˅[) HE@+}+_Y92%s>."gup7MOO HE@ׯ=V )Μ9=8.EYb( QyW.W۲epށ"RX^^0R4QJ@DQ$B^߻woK?τtyX3-+@DQ$7#/KmuywR9q%(h9. iy+lܸ1h'> #V"ozzr$" qXd|MD]J-.`Ά;kxxX @(^{ア./J .MUW*۷G2 VKn1IHs___{衇<~uEq#+ח={pk$χ+ HE@W\qyokd5ח<11] Ӗ\.|>/ iyI}͛7[]LxSSS5011aw Q@)ã>ws&Sg:IhNJRG>wm%ޯ LlJ@Q˫*tMY{=wz)t:A!X# $"jO~./7-.)}Q\@Am$"-b]^__߷ 'OF?O=>>n)J^fHЮ֭y32㋼v]V|X#333d$"뮻.ܹwvWTpMS(766& y^u].oZ]䍎FiXaMpr9 O/ yg?e]{4ﳟl%wj7==랹Bccc+ H9*X744ؑ_oGyRipp0zt.ipH E@G 3L|aE^쀋#͆T* H E@׿ +b\>}bI@[St_ׯ=O}VuaEdb=䓖14MR w_&L<cǎXu;+ T*R):R @2):\s6l/w1E^쀋]v-//[Lb1܃IF>|3_IݻKNM6==nqɤ @ѼNצ(26rI-j77|S8;СC2wԔ@dRt|0 4L -qp3%:?|?:OO$LRia۷o/bV?YɤFofs##vE*R@kڵ+ܒZu y]Vx㍱.oppȼ&~1@kEnÇ܁Rt~8Rډ H -d$"?VK===>dB쀋g[ZZ we6X<~_w~wb5V*ᬢrd\H`~~>Z H,E$[o5mܸ_j% H'O{sllL @b)W>`OOO흞̥pqq@B4rrKoxׯ_{4 _B^Eya@r9r$ܞ|^ @b)+JW_}ʑy¹+@, whPX<Fwuʑy\J=㥥%@8p ܤ333Kob#?7)H4b(HA4RI @b)x'J:=O|‡CG쀋#Gh`` ܧnn@)xJe.}{_ׅ`68p!7"NKHJ FGGc]e]?ۚ ڵkWZ $RU$"3F%;b``-HgφuxxX @)ʑyGkpBѧSӧeU, ;66& y\JcǎXwWWί_\@ 7Ą@$Sp طo_[~~.O&vؘ. &''=$"tرȼ[ T*cHÇvzzZ @)x/6l{4oΝEZ CHV$_teXd<.IRf.opp7ǣ  k޽{Ýi y??uy}}}!|>ڍق6}RH2EcȼÇwi'OLROFN$"US*l>OTN\ u|@h_St H8Eo\92^/Vڵ+2Sٳgh#sssp<V<z{{owؗ966}YKP@S&ovȼ{キcx]th;ǏwĄ@SVJ5\"wt:D^8p#7~, y Ε#~_d/ƯЎ:婩) `}_^92駟nǯ%{FqM8p bQ @)h3gkny/#<ֆ  y4I\kc]PRi/T*߿cɧyc]^:?@޻w.G6k&O?F?$|dۣu,..*J7o, yoqW'?g}pnnEv7;;aɧ5>Xwu%pd^.!}Y:@X }$"_c]^!\4ajj*ڏ?@Sb/b);'%V,tw199|>/ yێ̻kZ TJeg``I&&& ~qɧ ]b]͛-AgϞOJŢdtt4O|<^?#.oݺu?LQf=y[-(H_|qݺu:n!:Γdm^V$"y7V̛ vj>`-A@ ꭷ_kݾ}.p7v@ կDk###чt̿N5;;n{ h <G?QѼ??Zu\% Bt@ -[b]_ST?x.Eϴ9|@ |3.oӦM52T*Ef}ȑ#ڂ"=62wbPxg'''=:==jZ6pQ* m||<ǏyX~}Ѽ/~+?[V=k׮fCL&˕?y ?s@,-- ŊƯT*9znRm\.'apcO$"}.mذsQFGG] 2LE*ϯsX=/j`oo+fD/]a~oܸ=a~co;vܙNcܜHpo߾] @Pjڇ>-o;~ܠ=A7 7@vڅ"N0>>-߲eȅ;r<5y''' Em/hwA=6Ɉ:X?.](h{sss{O~ZqdaaA>bQ @P?'G.`/*tl6yB@;rH뮻"ѣG ,aJ"](h{{򡡡+{A=N h#<ڞ'W*͞fEmό<̄Emϩ+ fFy o -vL&#R`|>\N @Q w[l=7{<|^ @Q Ξ= z(/Ʉ1JJ%yBBA @Q!v9_n>i>DzKFGG-?33#(T*|~n?Q}/iӦ>~_Yܹ3:/"OH?mD@͝s_wuCCC3}}}_ܷo7a@Ri< 144477WV?_ \.< h/<:ѣG7o^*:t֯_Xfggڋ"TV|t}T&yWƧ~:۞/:@P@-..8qg瀕//[B?d`J0ڑ"VPR#]ޝwYVi||<<GGG#E oE, ڑ"V{zz"#~IVG>R$)` "e +-˅]\Ўyz̻fgg+*Ʉ̌@vUSO=O~IW#E4oH$_|Q2BGZ*Цy~___ּr,Jeڔ"V5 o2 W\nmJM{Hn:#`JЦy|㑑y{9( 522"M)%tM. @8pFGGêP(hS<hZ_r~&|><Ţ@63D5k֨jdЦyr~,}O24===-M)?>JEᄏZ hڔ"ZT7"] 7pi.}_2g3@+;|d U*оy}Gn۱cG*344$}) m޼9 U*£& h_<h?p˻k'''%P(CfddD @R@;˿˞.oۗ^zI2iFGGellL @R@ȭyַp`|>)bQ @R@j_#]ޯV*@D6  K_827ސ 4iKm֬YuwwRTxkhk<ho?nȭy?pzdR @[S@ tygΝ;':LxPimM1GD"oKNV.#"hk<7|sݺu:{dXb1<r@X97~^ T(! hk< D7p4###Q066&) .k'QC`||\ @[S@l?~|ڵ[|I9lK@8ٸqcҗddb`` \ښ"b^_^82Je_T5Et??ۿ[L&9M!yȼ{,KJ%\tZ @S@[#]wQVCpg2N^_ty7pyOT y.yЉwA'B!\###ڝ":[.rk޶mی#6FGGõ=66&)sU*~.o1Å],;Et{,_~bbB2l6jKE_|122[iwL&\ڝ"w}/rk׾z.T: Ā"z>88RphGde<@@ (Ow֮]{ ^*JĀ"z7ȼ{N2p EpΝ#ZM8R.\.' yx xΝo||<\###b@\ʟɟDk歷ޒ -nll,\Ā"7xcڵ:dhe|>\bQ @ (˛MR.¡5rpNLLEpE-["]ƍK8L&.i1zzz5kּkդpuE8޵^5g -%L~8S4xPVTnHww VPÕ900  yn۰a/nzz:\lV @<({##^~e\<LNN.cA X,qddD @<(UV7o.#hp) ǷHL&M2pEK/f͚.?dXe\.\rY @<(oܸ1rk#}믏ܚ;;APR G:3<=Ae˖Hw뭷;wN8|\.k#ENzHw5׼[a^T F63<;v,22oKB.|>/ fy@Hկ~ȼ7::bFz>88~W~X|>\ BA @(6suww7vyk׮52cep%J%1kpdޮ]Ͷe2p e1oHwwutT*.J" fy@ADӧO .qHYN@liddޚ5k^}UtJT*% ~y@;֭ܚJ&r3@Qq6JooTUXT ww6?< xG"]޵^ ' B@Q 22oۿL,;z@Qq611qFn}O>wqP?< nHw=j5I6 oT?<#ov=}dbc`` ܹGt^z˛oo$U\Tď" 6lܚ#A VL&Ē",Z;ty6mӾfff½NĒ"D;vty֭L*d2bItW_}u͚5]^ww$ӎbs@XR߿#}߬i/B!܃|^ @,)VHwmUUᴑp %EϿ/oJ] n||\ @,)~W_M$:oddD2m!͆{T* %E/;wotyLȼ700iX[ltyϟN+KRT*bIGG G=zT2^{*L +EE8qb:'L Q:W<p7/j5ᴔr8P @\).GtydZGX N.W<8x`OOOc7믿.166q'N[.rkޓO>)V0221q".\Hӑ.oppȼr)J7#]ކ &''%DCCCW<ŹȼHY}1==- yV.ȭy;wLS$p/uqXz~]wEsssYL&Ę"`醇O*U3==?00  yWW^YfMcYaCCCbLp>믿>rkC=d^*(a\N @)AL&|.\Ί c<eSOuww7vy֭;zdVHؘ@S,z+HDn(y#bLΟ?|A#VB6 C.JbL"~HqsIfy OOO 1EJ./HYFT*R1E :qڵk#uO?-eQTɤ@xSr-.j¹J333atZ @)VP˻N>-Q.<3@xS^x˛ÒYbě"`>}zÆ [x ,MPcMj]wι9,hchoۑ.wߕ̢0B  yq#8 +fJ@xS4;Svȭy +dr3Shnn|>w\V* CT*ͼZjDtOR(|uVT#<ϟ?/\.fR,|uVT#3twwGFK& )MVp@;vXdd^+|Cs0&_] `N5"h AЙ$0 \"2g  Eo>(*J@*jȽx<  G?cG0==n@󯮻 wygȼ'xs(Jg_]U8Ո]p!JE/| Z6||<_]XS#<֯_~Miյp@[;p@dd^OO/ _] `N5"hwׯܚ|'ƛ--Ϳ^jq@ T;#m޼9#2LrWWQ)+=] 6~~/mذarr2~[NmiՕq<"4vy?cd2zkE ӧȭy_cJ%ܮT*W׊<`N5"j}.ˇ~+Fe2Vp@\}ߍtyd2#JREl%y*jDc/5k"#ިBnN>oeZEpӅ>"=CA2%B=n6í( -qu2x""#mݺ6!JRյXS:=^6hk t~[f믿ٿ:j+JST\]U8Ոcd2rk^>g8yd"l6*rL&c'C>Ûn)z;77iӦ߿ /T*1ڃ@Pt |HwܹKVV۲eK$ɕ~Bai7;E /,/ڵkT*#;::v9y҉'{dmq[n.Y>˰׀ΡcZ[otsj5Ʌ}_={+f*Jv9yD=ÑnnÆ O_j.>W ezz:"8p@OOOc77__g2+;33/,JeE799yv-ս[Ֆ%ɤ}tEZ%wy>򾞙_tEs%wy^WR.gd2v Qy\~ŧ [Ə(3r9(<.c۶m]Wa``Z.+) y(<.e߾}]WmǎA\ sll:"OuرD"ѵwp||:";{l___2I$Nʗf',JQy\DZݲeKײڴiռٖ34ڂ"hmm߾jOUT&(:x`׊ٻw/bM@qNX,6[ }}}gϞ] !S@Qpǎۺuuy[lY찼8k>:"O511}D"]^Lݻw;@Qpw[.P(\ qtEWdnnnll+k׮5` 8zruyZJm*Jiy,ɓ'o߾,]ގ;/6SSSviy,T.K&W:tkӦM4<VZOH$'Oğs؁N`yjuy6m[gϞ-Y/m)XfG{m۶-Z߿ppFP*[(XSSS>EkKسg߿q"%ސ/z@(XAýW}+_YTN}- yjo߾+7uumuu%ޚwq9`AP(U'j]]Ůts_o<V[Xvu]PE{lbL@sLMMر#L^؍xk]v +ETT?YM^YplϞ=+U y4wobZc񼼑y'`;"JD Ē"&jhR]]⋼ZWU3g ď"&;|pC[|;>>.U ~y4پ}y#S߿_@(hц"otEO2RG@#J(h23"&WB@d2M.+~\R"bI@ 7y;;^{##bI@MNNv}¢nL$bI@Kl(*W5w_bI@K(ˉDurZ j5yUݻ\oWUxƹx7o>{z><m۶ښ"VTV.ZR={\t^Pft?I\)yg9r?uԥebb"J5vyd<)J244ottT2@ROfڔ"8mO<)(SNEF%B (Jul}  E!{Fm۶UUmA@9zhdd^: yt3gΤȭycccZ"SVwvYׅ,EDd2JE2@kRй&&&T*edКyt遁./H߿?-E@\.22o۶mjU8@P/:t(22opppffF2@P/;v,22bbB2@+PΞ={FF:tH2@) ۹sgVD<#ԔdfQŕJ./L9rD2@S(SU*[FfgϞ 2E\J{tyCCCsssV".oȼgJX5<"6mjdա+UVmyݻpE`׮].o֭JE8R d2R)+GK155N##/`(`j‘y;w @8SA#]АyS:|pooociӦ H̙3["GV۾}{r.)`AwD" J<Xf'Ok"ߙ3g./H:tH2)`Ej\.sN#Q :tP2l6o|511E7;;;44yy(`5;wF;vTUWBg||<22o```zzZ2e)`Uo^Մ\"X,FFm޼̙3R@3MMMȼ#GHP@Um۶EfgϞ yFFF"]PR EÇ'./Ne?W@KY82/LE<h-k׮z.d<hE{6H:"ZTXk濝 t&E3g 6vyDСCVVs\ddΝ;]8Qy"#Ϟ=+<hǎ72:"‘yAbOV-c# y~̃499[,%qv5;;7<+Ez><ym>@8yADcu֙@S@gHR]^ɓ'%-Ngvvv˖- @+S@' `xx8KV&EtB700`d&EtrNT*ed Etm۶Ef;::pu(_6CѣGdddޙ3g$@|lȼd2yq@)OV۷o72Z"={DF IE\̃>-["#9"X}<RАyt<2 ػw‘yJE8jy)JTKFQWjvvvpp022o||\2 y"۷GFy#`)E52V"X'O5vyTjbbB2By={v˖-FPKݻ#oj֡C"]޽kd,/E ?wIrQcvv6t}ؘd`Y(eA>tyFPý]̌dj(wԩT*?~\2d<`ET*#FGG%KVJ^ty;vVR+k||<22o```zzZ2(<`ŝ9s&N7vyF(077}lGFF \ Ezr\V \"XUǏKӧN \"Xm]^__ypi< jZ.32"h}%./VUB<&&&##&''%<fffd2yQ@#E|ZG۽{w^|DFFmݺunnN2sERJR*j6m455%PRlٲK$ Nzsȼg}<:"hQ۲eyt,Eкtc7d@<U*L&%SNIN䂖IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/interpolation.tex.backup0000644000175000017500000001630012014170666030424 0ustar georgeskgeorgesk\documentclass[a4paper,14pt,DIV22]{scrartcl} \RequirePackage[T1]{fontenc} \RequirePackage[latin1]{inputenc} \RequirePackage[frenchb]{babel} \RequirePackage{lmodern} \RequirePackage{amssymb,amsmath,latexsym, amsthm} \RequirePackage{pifont} \newcommand{\R}{\ensuremath{\mathbb{R}}} \newcommand{\SSI}{\Leftrightarrow} \newcommand{\note}{\ding{46}\,\,} \parindent=0cm\parskip=5pt \newenvironment{code}{\vspace{1em}\ttfamily\begin{minipage}{.045\linewidth}\hfill\end{minipage}\begin{minipage}{.95\linewidth} }{\rmfamily\end{minipage}\vspace{1em}} \newcommand{\s}{\phantom{xx}} %opening \title{Interpolation polynmiale par morceaux} \author{Nicolas Pourcelot} \begin{document} \maketitle \begin{abstract} Le but de ce document est d'offrir des interpolations polynmiales par morceaux utiles la ralisation d'exercices de lecture graphique, en particulier en classe de seconde. Il s'agit de gnrer des courbes passant par des points donns, et semblant le plus lgantes possible, c'est--dire sans artefacts. Ce document est distribu selon les termes de la GNU Free Documentation License. \end{abstract} \section{Interpolation par une courbe de fonction cubique par morceaux} \subsection{nonc du problme}\label{nonc} On cherche tracer la courbe ${\cal C}$ d'une \emph{fonction} $f$ passant simplement par un certain nombre de points (en particulier, l'utilisateur final ne fournit pas de conditions sur les drives). On supposera toujours ces points d'abscisses deux deux distinctes, et rangs par ordre (strictement) croissant d'abscisse. Soient trois points $A$, $B$, $C$ par laquelle notre courbe doit passer% \footnote{Si il n'y a que deux points $A$ et $B$, l'interpolation la plus naturelle est le segment $[AB]$.}% . La courbe doit en outre respecter les contraintes suivantes~: \begin{itemize} \item si $y_B>y_A$ et $y_B>y_C$, alors $y_B$ doit tre le maximum de $f$ sur l'intervalle $[x_A;x_C]$~; \item si $y_By_B>y_C$), la pente de la courbe ${\cal C}$ au point $B$ est celle de la droite $(AC)$. \end{itemize} Les deux premires contraintes permettent de raliser facilement des courbes d'extrema locaux choisis. La dernire contrainte est, elle, purement esthtique. % \subsection{Interpolation sur $[0;1]$} % % Soient $a,b,c,d\in \R $. % % Soit $f$ la fonction dfinie sur \R par $f(x)=ax^3+bx^2+cx+d$. % % On cherche dterminer $a,b,c,d$ en fonction de $f(0)$, $f'(0)$, $f(1)$ et $f'(1)$. % % $\forall x\in \R, f'(x)=3ax^2+2bx+c$. % % On obtient donc~: % % $ % \begin{aligned}[t]\begin{cases} % f(0)=d \\ % f'(0)=c \\ % f(1)=a+b+c+d \\ % f'(1)=3a+2b+c % \end{cases} % &\SSI % \begin{cases} % f(0)=d \\ % f'(0)=c \\ % 3f(1)-f'(1)=b+2c+3d \\ % f'(1)=3a+2b+c % \end{cases} \\ % &\SSI % \begin{cases} % f(0)=d \\ % f'(0)=c \\ % 3f(1)-f'(1)-2f'(0)-3f(0)=b\\ % \dfrac{f'(1)-2b-c}{3}=a % \end{cases}\\ % &\SSI % \begin{cases} % d=f(0) \\ % c=f'(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % a=\dfrac{f'(1)-2(3f(1)-f'(1)-2f'(0)-3f(0))-f'(0)}{3} % \end{cases}\\ % &\SSI % \begin{cases} % d=f(0) \\ % c=f'(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % a=\dfrac{3f'(1)-6f(1)+3f'(0)+6f(0)}{3} % \end{cases} \\ % &\SSI % \begin{cases} % a=f'(1)-2f(1)+f'(0)+2f(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % c=f'(0) \\ % d=f(0) % \end{cases} % \end{aligned}$ % % \note \emph{On aurait pu supposer que $f(0)=0$ et $f(1)=1$ pour simplifier encore un peu les calculs.} \subsection{Interpolation entre $(0;0)$ et $(1;1)$} Soient $a,b,c,d\in \R $. Soit $f$ la fonction dfinie sur \R{} par $f(x)=ax^3+bx^2+cx+d$. On cherche dterminer $a,b,c,d$ en fonction de $f(0)=0$, $f'(0)$, $f(1)=1$ et $f'(1)$. $\forall x\in \R, f'(x)=3ax^2+2bx+c$. On obtient donc~: $ \begin{aligned}[t]\begin{cases} 0=d \\ f'(0)=c \\ 1=a+b+c+d \qquad (L_3)\\ f'(1)=3a+2b+c\qquad (L_4) \end{cases} &\SSI \begin{cases} 0=d \\ f'(0)=c \\ 3-f'(1)=b+2c \qquad (3 L_3-L_4)\\ f'(1)=3a+2b+c \end{cases} \\ &\SSI \begin{cases} 0=d \\ f'(0)=c \\ 3-f'(1)-2f'(0)=b\\ \dfrac{f'(1)-2b-c}{3}=a \end{cases}\\ &\SSI \begin{cases} d=0 \\ c=f'(0) \\ b=3-f'(1)-2f'(0) \\ a=\dfrac{f'(1)-2(3-f'(1)-2f'(0))-f'(0)}{3} \end{cases}\\ &\SSI \begin{cases} d=0 \\ c=f'(0) \\ b=3-f'(1)-2f'(0) \\ a=\dfrac{3f'(1)-6+3f'(0)}{3} \end{cases} \\ &\SSI \begin{cases} a=f'(1)-2+f'(0) \\ b=3-f'(1)-2f'(0) \\ c=f'(0) \\ d=0 \end{cases} \end{aligned}$ \subsection{Cas gnral~: interpolation entre $A$ et $B$} On se ramne au cas prcdent par transformation affine (attention, les drives sont modifies~!). Puis, on obtient une interpolation entre $A$ et $B$ en appliquant la fonction solution trouve la transformation affine inverse. % \[\forall x\in \R, f(x)=k_y(a(k_xx-x_A)^3+b(k_xx-x_A)^2+c(k_xx-x_A)+d)+y_A\] avec~: % \[\begin{cases} % k_x = \dfrac{1}{x_B-x_A}\text{\qquad car $x_B-x_A\ne 0$} \\ % k_y = y_B-y_A \\ % a=f'(1)-2f(1)+f'(0)+2f(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % c=f'(0) \\ % d=0 % \end{cases} % \] \[\forall x\in \R, f(x)=k_y\left(a\left(\dfrac{x}{k_x}-x_A\right)^3+b\left(\dfrac{x}{k_x}-x_A\right)^2+c\left(\dfrac{x}{k_x}-x_A\right)\right)+y_A\] avec~: \[\begin{cases} k_x = x_B-x_A\text{\qquad ($k_x\ne 0$ car $x_A\ne x_B$)} \\ k_y = y_B-y_A \\ a=\dfrac{k_x}{k_y}f'(x_B)-2+\dfrac{k_x}{k_y}f'(x_A) \\ b=3-\dfrac{k_x}{k_y}f'(x_B)-2\dfrac{k_x}{k_y}f'(x_A) \\ c=f'(x_A) \\ \end{cases} \] \subsection{Pente en un point d'interpolation~:} Si le point est encadr par deux valeurs, on reprend les cas prcdents (cf. \ref{nonc})~: \begin{itemize} \item si $y_Ay_A$ et $y_B>y_C$, alors $y_B$ doit tre le maximum de $f$ sur l'intervalle $[x_A;x_C]$~; \item si $y_By_B>y_C$), la pente de la courbe ${\cal C}$ au point $B$ est celle de la droite $(AC)$. \end{itemize} Les deux premires contraintes permettent de raliser facilement des courbes d'extrema locaux choisis. La dernire contrainte est, elle, purement esthtique. % \subsection{Interpolation sur $[0;1]$} % % Soient $a,b,c,d\in \R $. % % Soit $f$ la fonction dfinie sur \R par $f(x)=ax^3+bx^2+cx+d$. % % On cherche dterminer $a,b,c,d$ en fonction de $f(0)$, $f'(0)$, $f(1)$ et $f'(1)$. % % $\forall x\in \R, f'(x)=3ax^2+2bx+c$. % % On obtient donc~: % % $ % \begin{aligned}[t]\begin{cases} % f(0)=d \\ % f'(0)=c \\ % f(1)=a+b+c+d \\ % f'(1)=3a+2b+c % \end{cases} % &\SSI % \begin{cases} % f(0)=d \\ % f'(0)=c \\ % 3f(1)-f'(1)=b+2c+3d \\ % f'(1)=3a+2b+c % \end{cases} \\ % &\SSI % \begin{cases} % f(0)=d \\ % f'(0)=c \\ % 3f(1)-f'(1)-2f'(0)-3f(0)=b\\ % \dfrac{f'(1)-2b-c}{3}=a % \end{cases}\\ % &\SSI % \begin{cases} % d=f(0) \\ % c=f'(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % a=\dfrac{f'(1)-2(3f(1)-f'(1)-2f'(0)-3f(0))-f'(0)}{3} % \end{cases}\\ % &\SSI % \begin{cases} % d=f(0) \\ % c=f'(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % a=\dfrac{3f'(1)-6f(1)+3f'(0)+6f(0)}{3} % \end{cases} \\ % &\SSI % \begin{cases} % a=f'(1)-2f(1)+f'(0)+2f(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % c=f'(0) \\ % d=f(0) % \end{cases} % \end{aligned}$ % % \note \emph{On aurait pu supposer que $f(0)=0$ et $f(1)=1$ pour simplifier encore un peu les calculs.} \subsection{Interpolation entre $(0;0)$ et $(1;1)$} Soient $a,b,c,d\in \R $. Soit $f$ la fonction dfinie sur \R{} par $f(x)=ax^3+bx^2+cx+d$. On cherche dterminer $a,b,c,d$ en fonction de $f(0)=0$, $f'(0)$, $f(1)=1$ et $f'(1)$. $\forall x\in \R, f'(x)=3ax^2+2bx+c$. On obtient donc~: $ \begin{aligned}[t]\begin{cases} 0=d \\ f'(0)=c \\ 1=a+b+c+d \qquad (L_3)\\ f'(1)=3a+2b+c\qquad (L_4) \end{cases} &\SSI \begin{cases} 0=d \\ f'(0)=c \\ 3-f'(1)=b+2c \qquad (3 L_3-L_4)\\ f'(1)=3a+2b+c \end{cases} \\ &\SSI \begin{cases} 0=d \\ f'(0)=c \\ 3-f'(1)-2f'(0)=b\\ \dfrac{f'(1)-2b-c}{3}=a \end{cases}\\ &\SSI \begin{cases} d=0 \\ c=f'(0) \\ b=3-f'(1)-2f'(0) \\ a=\dfrac{f'(1)-2(3-f'(1)-2f'(0))-f'(0)}{3} \end{cases}\\ &\SSI \begin{cases} d=0 \\ c=f'(0) \\ b=3-f'(1)-2f'(0) \\ a=\dfrac{3f'(1)-6+3f'(0)}{3} \end{cases} \\ &\SSI \begin{cases} a=f'(1)-2+f'(0) \\ b=3-f'(1)-2f'(0) \\ c=f'(0) \\ d=0 \end{cases} \end{aligned}$ \subsection{Cas gnral~: interpolation entre $A$ et $B$} On se ramne au cas prcdent par transformation affine (attention, les drives sont modifies~!). Puis, on obtient une interpolation entre $A$ et $B$ en appliquant la fonction solution trouve la transformation affine inverse. % \[\forall x\in \R, f(x)=k_y(a(k_xx-x_A)^3+b(k_xx-x_A)^2+c(k_xx-x_A)+d)+y_A\] avec~: % \[\begin{cases} % k_x = \dfrac{1}{x_B-x_A}\text{\qquad car $x_B-x_A\ne 0$} \\ % k_y = y_B-y_A \\ % a=f'(1)-2f(1)+f'(0)+2f(0) \\ % b=3f(1)-f'(1)-2f'(0)-3f(0) \\ % c=f'(0) \\ % d=0 % \end{cases} % \] \[\forall x\in \R, f(x)=k_y\left(a\left(\dfrac{x}{k_x}-x_A\right)^3+b\left(\dfrac{x}{k_x}-x_A\right)^2+c\left(\dfrac{x}{k_x}-x_A\right)\right)+y_A\] avec~: \[\begin{cases} k_x = x_B-x_A\text{\qquad ($k_x\ne 0$ car $x_A\ne x_B$)} \\ k_y = y_B-y_A \\ a=\dfrac{k_x}{k_y}f'(x_B)-2+\dfrac{k_x}{k_y}f'(x_A) \\ b=3-\dfrac{k_x}{k_y}f'(x_B)-2\dfrac{k_x}{k_y}f'(x_A) \\ c=f'(x_A) \\ \end{cases} \] \subsection{Pente en un point d'interpolation~:} Si le point est encadr par deux valeurs, on reprend les cas prcdents (cf. \ref{nonc})~: \begin{itemize} \item si $y_A Babel and hyphenation patterns for english, usenglishmax, dumylang, noh yphenation, arabic, farsi, croatian, ukrainian, russian, bulgarian, czech, slov ak, danish, dutch, finnish, basque, french, german, ngerman, ibycus, greek, mon ogreek, ancientgreek, hungarian, italian, latin, mongolian, norsk, icelandic, i nterlingua, turkish, coptic, romanian, welsh, serbian, slovenian, estonian, esp eranto, uppersorbian, indonesian, polish, portuguese, spanish, catalan, galicia n, swedish, ukenglish, pinyin, loaded. (/usr/share/texmf-texlive/tex/latex/koma-script/scrartcl.cls Document Class: scrartcl 2006/07/30 v2.95b KOMA-Script document class (article) (/usr/share/texmf-texlive/tex/latex/koma-script/scrkbase.sty Package: scrkbase 2006/07/30 v2.95b KOMA-Script package (basics and keyval use) (/usr/share/texmf-texlive/tex/latex/koma-script/scrlfile.sty Package: scrlfile 2006/03/28 v2.95 KOMA-Script package (loading files) Package scrlfile, 2006/03/28 v2.95 KOMA-Script package (loading files) Copyright (C) Markus Kohm ) (/usr/share/texmf-texlive/tex/latex/graphics/keyval.sty Package: keyval 1999/03/16 v1.13 key=value parser (DPC) \KV@toks@=\toks14 )) Package scrkbase Info: You've used the obsolete option `14pt'. (scrkbase) \KOMAoptions{fontsize=14pt} will be used instead. (scrkbase) You should do this change too on input line 594. (/usr/share/texmf-texlive/tex/latex/extsizes/size14.clo File: size14.clo 1999/11/11 v1.4a NON-Standard LaTeX file (size option) ) (/usr/share/texmf-texlive/tex/latex/koma-script/typearea.sty Package: typearea 2006/07/30 v2.95b KOMA-Script package (type area) Package typearea, 2006/07/30 v2.95b KOMA-Script package (type area) Copyright (C) Frank Neukam, 1992-1994 Copyright (C) Markus Kohm, 1994-2002 \ta@bcor=\skip41 \ta@div=\count79 Package scrkbase Info: You've used the obsolete option `a4paper'. (scrkbase) \KOMAoptions{paper=a4} will be used instead. (scrkbase) You should do this change too on input line 419. \ta@hblk=\skip42 \ta@vblk=\skip43 \ta@temp=\skip44 Package typearea Info: These are the values describing the layout: (typearea) DIV = 22 (typearea) BCOR = 0.0pt (typearea) \paperwidth = 597.50793pt (typearea) \textwidth = 516.02962pt (typearea) DIV-departure = -1/100 (typearea) \evensidemargin = -31.53084pt (typearea) \oddsidemargin = -31.53084pt (typearea) \paperheight = 845.04694pt (typearea) \textheight = 745.0pt (typearea) \topmargin = -80.60876pt (typearea) \headheight = 21.25pt (typearea) \headsep = 25.5pt (typearea) \topskip = 14.0pt (typearea) \footskip = 59.5pt (typearea) \baselineskip = 17.0pt (typearea) on input line 832. ) \c@part=\count80 \c@section=\count81 \c@subsection=\count82 \c@subsubsection=\count83 \c@paragraph=\count84 \c@subparagraph=\count85 \abovecaptionskip=\skip45 \belowcaptionskip=\skip46 \c@pti@nb@sid@b@x=\box26 \c@figure=\count86 \c@table=\count87 \bibindent=\dimen102 ) (/usr/share/texmf-texlive/tex/latex/base/fontenc.sty Package: fontenc 2005/09/27 v1.99g Standard LaTeX package (/usr/share/texmf-texlive/tex/latex/base/t1enc.def File: t1enc.def 2005/09/27 v1.99g Standard LaTeX file LaTeX Font Info: Redeclaring font encoding T1 on input line 43. )) (/usr/share/texmf-texlive/tex/latex/base/inputenc.sty Package: inputenc 2006/05/05 v1.1b Input encoding file \inpenc@prehook=\toks15 \inpenc@posthook=\toks16 (/usr/share/texmf-texlive/tex/latex/base/latin1.def File: latin1.def 2006/05/05 v1.1b Input encoding file )) (/usr/share/texmf-texlive/tex/generic/babel/babel.sty Package: babel 2005/11/23 v3.8h The Babel package (/home/nicolas/texmf/tex/latex/prof/frenchb.ldf Language: frenchb 2009/02/07 v2.3c French support from the babel system (/usr/share/texmf-texlive/tex/generic/babel/babel.def File: babel.def 2005/11/23 v3.8h Babel common definitions \babel@savecnt=\count88 \U@D=\dimen103 ) Package babel Info: Making : an active character on input line 121. Package babel Info: Making ; an active character on input line 122. Package babel Info: Making ! an active character on input line 123. Package babel Info: Making ? an active character on input line 124. \FB@Mht=\dimen104 \std@mcc=\count89 \dec@mcc=\count90 \parindentFFN=\dimen105 ************************************* * Local config file frenchb.cfg used * (/usr/share/texmf-texlive/tex/generic/babel/frenchb.cfg))) (/usr/share/texmf-texlive/tex/latex/carlisle/scalefnt.sty) (/usr/share/texmf/tex/latex/lm/lmodern.sty Package: lmodern 2007/01/14 v1.3 Latin Modern Fonts LaTeX Font Info: Overwriting symbol font `operators' in version `normal' (Font) OT1/cmr/m/n --> OT1/lmr/m/n on input line 13. LaTeX Font Info: Overwriting symbol font `letters' in version `normal' (Font) OML/cmm/m/it --> OML/lmm/m/it on input line 14. LaTeX Font Info: Overwriting symbol font `symbols' in version `normal' (Font) OMS/cmsy/m/n --> OMS/lmsy/m/n on input line 15. LaTeX Font Info: Overwriting symbol font `largesymbols' in version `normal' (Font) OMX/cmex/m/n --> OMX/lmex/m/n on input line 16. LaTeX Font Info: Overwriting symbol font `operators' in version `bold' (Font) OT1/cmr/bx/n --> OT1/lmr/bx/n on input line 17. LaTeX Font Info: Overwriting symbol font `letters' in version `bold' (Font) OML/cmm/b/it --> OML/lmm/b/it on input line 18. LaTeX Font Info: Overwriting symbol font `symbols' in version `bold' (Font) OMS/cmsy/b/n --> OMS/lmsy/b/n on input line 19. LaTeX Font Info: Overwriting symbol font `largesymbols' in version `bold' (Font) OMX/cmex/m/n --> OMX/lmex/m/n on input line 20. LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' (Font) OT1/cmr/bx/n --> OT1/lmr/bx/n on input line 22. LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' (Font) OT1/cmss/m/n --> OT1/lmss/m/n on input line 23. LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' (Font) OT1/cmr/m/it --> OT1/lmr/m/it on input line 24. LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' (Font) OT1/cmtt/m/n --> OT1/lmtt/m/n on input line 25. LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' (Font) OT1/cmr/bx/n --> OT1/lmr/bx/n on input line 26. LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' (Font) OT1/cmss/bx/n --> OT1/lmss/bx/n on input line 27. LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' (Font) OT1/cmr/bx/it --> OT1/lmr/bx/it on input line 28. LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' (Font) OT1/cmtt/m/n --> OT1/lmtt/m/n on input line 29. ) (/usr/share/texmf-texlive/tex/latex/amsfonts/amssymb.sty Package: amssymb 2002/01/22 v2.2d (/usr/share/texmf-texlive/tex/latex/amsfonts/amsfonts.sty Package: amsfonts 2001/10/25 v2.2f \@emptytoks=\toks17 \symAMSa=\mathgroup4 \symAMSb=\mathgroup5 LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold' (Font) U/euf/m/n --> U/euf/b/n on input line 132. )) (/usr/share/texmf-texlive/tex/latex/amsmath/amsmath.sty Package: amsmath 2000/07/18 v2.13 AMS math features \@mathmargin=\skip47 For additional information on amsmath, use the `?' option. (/usr/share/texmf-texlive/tex/latex/amsmath/amstext.sty Package: amstext 2000/06/29 v2.01 (/usr/share/texmf-texlive/tex/latex/amsmath/amsgen.sty File: amsgen.sty 1999/11/30 v2.0 \@emptytoks=\toks18 \ex@=\dimen106 )) (/usr/share/texmf-texlive/tex/latex/amsmath/amsbsy.sty Package: amsbsy 1999/11/29 v1.2d \pmbraise@=\dimen107 ) (/usr/share/texmf-texlive/tex/latex/amsmath/amsopn.sty Package: amsopn 1999/12/14 v2.01 operator names ) \inf@bad=\count91 LaTeX Info: Redefining \frac on input line 211. \uproot@=\count92 \leftroot@=\count93 LaTeX Info: Redefining \overline on input line 307. \classnum@=\count94 \DOTSCASE@=\count95 LaTeX Info: Redefining \ldots on input line 379. LaTeX Info: Redefining \dots on input line 382. LaTeX Info: Redefining \cdots on input line 467. \Mathstrutbox@=\box27 \strutbox@=\box28 \big@size=\dimen108 LaTeX Font Info: Redeclaring font encoding OML on input line 567. LaTeX Font Info: Redeclaring font encoding OMS on input line 568. \macc@depth=\count96 \c@MaxMatrixCols=\count97 \dotsspace@=\muskip10 \c@parentequation=\count98 \dspbrk@lvl=\count99 \tag@help=\toks19 \row@=\count100 \column@=\count101 \maxfields@=\count102 \andhelp@=\toks20 \eqnshift@=\dimen109 \alignsep@=\dimen110 \tagshift@=\dimen111 \tagwidth@=\dimen112 \totwidth@=\dimen113 \lineht@=\dimen114 \@envbody=\toks21 \multlinegap=\skip48 \multlinetaggap=\skip49 \mathdisplay@stack=\toks22 LaTeX Info: Redefining \[ on input line 2666. LaTeX Info: Redefining \] on input line 2667. ) (/usr/share/texmf-texlive/tex/latex/base/latexsym.sty Package: latexsym 1998/08/17 v2.2e Standard LaTeX package (lasy symbols) \symlasy=\mathgroup6 LaTeX Font Info: Overwriting symbol font `lasy' in version `bold' (Font) U/lasy/m/n --> U/lasy/b/n on input line 47. ) (/usr/share/texmf-texlive/tex/latex/amscls/amsthm.sty Package: amsthm 2004/08/06 v2.20 \thm@style=\toks23 \thm@bodyfont=\toks24 \thm@headfont=\toks25 \thm@notefont=\toks26 \thm@headpunct=\toks27 \thm@preskip=\skip50 \thm@postskip=\skip51 \thm@headsep=\skip52 \dth@everypar=\toks28 ) (/usr/share/texmf-texlive/tex/latex/psnfss/pifont.sty Package: pifont 2005/04/12 PSNFSS-v9.2a Pi font support (SPQR) LaTeX Font Info: Try loading font information for U+pzd on input line 63. (/usr/share/texmf-texlive/tex/latex/psnfss/upzd.fd File: upzd.fd 2001/06/04 font definitions for U/pzd. ) LaTeX Font Info: Try loading font information for U+psy on input line 64. (/usr/share/texmf-texlive/tex/latex/psnfss/upsy.fd File: upsy.fd 2001/06/04 font definitions for U/psy. )) (./interpolation.aux) \openout1 = `interpolation.aux'. LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 22. LaTeX Font Info: ... okay on input line 22. LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 22. LaTeX Font Info: ... okay on input line 22. LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 22. LaTeX Font Info: ... okay on input line 22. LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 22. LaTeX Font Info: ... okay on input line 22. LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 22. LaTeX Font Info: ... okay on input line 22. LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 22. LaTeX Font Info: ... okay on input line 22. LaTeX Font Info: Try loading font information for T1+lmr on input line 22. (/usr/share/texmf/tex/latex/lm/t1lmr.fd File: t1lmr.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Info: Redefining \degres on input line 22. Package frenchb.ldf Warning: The definition of \@makecaption has been changed, (frenchb.ldf) frenchb will NOT customise it; (frenchb.ldf) reported on input line 22. LaTeX Info: Redefining \dots on input line 22. LaTeX Info: Redefining \up on input line 22. LaTeX Font Info: Try loading font information for T1+lmss on input line 25. (/usr/share/texmf/tex/latex/lm/t1lmss.fd File: t1lmss.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Font Info: Try loading font information for OT1+lmr on input line 25. (/usr/share/texmf/tex/latex/lm/ot1lmr.fd File: ot1lmr.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Font Info: Try loading font information for OML+lmm on input line 25. (/usr/share/texmf/tex/latex/lm/omllmm.fd File: omllmm.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Font Info: Try loading font information for OMS+lmsy on input line 25. (/usr/share/texmf/tex/latex/lm/omslmsy.fd File: omslmsy.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Font Info: Try loading font information for OMX+lmex on input line 25. (/usr/share/texmf/tex/latex/lm/omxlmex.fd File: omxlmex.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Font Info: External font `lmex10' loaded for size (Font) <20.74> on input line 25. LaTeX Font Info: External font `lmex10' loaded for size (Font) <14.4> on input line 25. LaTeX Font Info: External font `lmex10' loaded for size (Font) <12> on input line 25. LaTeX Font Info: Try loading font information for U+msa on input line 25. (/usr/share/texmf-texlive/tex/latex/amsfonts/umsa.fd File: umsa.fd 2002/01/19 v2.2g AMS font definitions ) LaTeX Font Info: Try loading font information for U+msb on input line 25. (/usr/share/texmf-texlive/tex/latex/amsfonts/umsb.fd File: umsb.fd 2002/01/19 v2.2g AMS font definitions ) LaTeX Font Info: Try loading font information for U+lasy on input line 25. (/usr/share/texmf-texlive/tex/latex/base/ulasy.fd File: ulasy.fd 1998/08/17 v2.2e LaTeX symbol font definitions ) LaTeX Font Info: External font `lmex10' loaded for size (Font) <10> on input line 36. LaTeX Font Info: External font `lmex10' loaded for size (Font) <7> on input line 36. LaTeX Font Info: External font `lmex10' loaded for size (Font) <5> on input line 41. [1 {/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] LaTeX Font Info: External font `lmex10' loaded for size (Font) <17.28> on input line 114. [2] LaTeX Font Info: Try loading font information for T1+lmtt on input line 221. (/usr/share/texmf/tex/latex/lm/t1lmtt.fd File: t1lmtt.fd 2007/01/14 v1.3 Font defs for Latin Modern ) LaTeX Font Info: External font `lmex10' loaded for size (Font) <8> on input line 221. LaTeX Font Info: External font `lmex10' loaded for size (Font) <6> on input line 221. Underfull \hbox (badness 10000) in paragraph at lines 222--223 []\T1/lmtt/m/n/12 assert len([point.x for point in points]) == len(set(point.x for point in [] [3] Underfull \hbox (badness 10000) in paragraph at lines 245--260 [] [4] (./interpolation.aux) ) Here is how much of TeX's memory you used: 3022 strings out of 94074 39097 string characters out of 1165152 98548 words of memory out of 1500000 6227 multiletter control sequences out of 10000+50000 84016 words of font info for 90 fonts, out of 1200000 for 2000 645 hyphenation exceptions out of 8191 35i,16n,35p,240b,198s stack positions out of 5000i,500n,6000p,200000b,5000s {/usr/share/texmf/fonts/enc/dvips/lm/lm-ec.enc}{/usr /share/texmf/fonts/enc/dvips/lm/lm-mathex.enc}{/usr/share/texmf/fonts/enc/dvips /lm/lm-rm.enc}{/usr/share/texmf/fonts/enc/dvips/lm/lm-mathit.enc}{/usr/share/te xmf/fonts/enc/dvips/lm/lm-mathsy.enc} Output written on interpolation.pdf (4 pages, 180432 bytes). PDF statistics: 70 PDF objects out of 1000 (max. 8388607) 0 named destinations out of 1000 (max. 131072) 1 words of extra memory for PDF output out of 10000 (max. 10000000) wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/courbe_convexe_cas1.png0000644000175000017500000012124512014170666030177 0ustar georgeskgeorgeskPNG  IHDRpaʼnsRGBbKGD pHYsnu>tIME T IDATxy\T2by]J~Yf咖y-Sk.7󶸦-T25S P'uXey=s|Pl&bQe6.٬Pp8P @@ 4h@ 4hKsiܸqjР\\\-___mذ!\+))'4uT%$$,^X 4ƍ5sL]pAvܩ}/ 5nXrp(jtww͛պukM>]FRʕK.\Yp\bOLL5p@9R .,]v[[ζ@Γ/RӧOd҂ TzuǏ[-bcc%I7nP=駟jƌ6m=yںu:ukٲe<@1b$aÆ߿HL&zZj)**J=vءÇ߾I_Zj#Fhɒ%<P94~>@׮]$?_"""ԣGݾ}[zRPP:wK-[q3fj׮nݺT@0Y,KQ}sfY2 ͗׼qfbccpw}X,ݻ6oެ+ѣ)RaW Ty .ܫWinӦMY.]͛7K͛W³$[{Vtt"7 @+Vn?Y=}&L IW Rd?ה)S$IӢEx{ǎӑ#GY=znݺ%I4iL&Sl-[TΝa<@ΝuYնmL8p@~$Y+/=ߟZ`Aokvzi{ϺݥK/_zʺtRL(\퓟JRթSt ~-2-{%mٲźߩS|?W^ծ]ti%&&N:z'榸8\˗l2խ[WҪU裏jРA2eJZ5kfNec͚5ڿu?RD@W^]111}Ţ-ZԩS$ɤSN[ªL2[L,[uM7N+WfΜqƩlٲ2-s=gs{LʕQ޸q$ Qݺuy ALLz-رczG(-ruu$$$Xó$լYSjղ)2e())I򋵬*Us)=#Zr*T;vhĉ6Y^uEGGkÆ crtZj'aŋ6ǎ;F#uZIj߾}eCBBgݯZjom۶ ,_fMݾ}[ݥK/MQ0Ξ=atʯjw>|~jr}ߠ u]QQQCY^:WR%+>>>h]ld2Ğ?f=WRϞ={1bD]tɺKRbb;d Oz>}"""4~xڵk@n]ڵke+짞;;wG}d^_~edb *`-ZHժUCt񒔔X6ligI޲e"""5jP۶mvܹ67W9KƍSbbfΜ?Db Ǐ+&&ƺ!;W}M6Ӯ낂W_Yk֬ի3 j˗Wʕyw}oV͛7א!Cҝg&n]t$hf?<<ڵf7xCIII;3q\6mS'ɤyeX&mf&n]d+@WTI|X^ŋm5kS/^'N詧tzF;-h///ѣub(888k._X̙p+o{g&,,,8lEDDhrrrҜ9s2-WftiEV\\9bng<5%ݱ֐S5i$*UzY/{⢧z}ZiΡC]vOy>8z-[ *hڴiٖO;]hh(3q)'xJqpЫj߾}{xxxLeXg+={Ν;OdГ_ҥKӧ/_533111:zuĈ#G^GrvvV~d2c-X@:tڵk .k.^Qcƌч~hLޒm6%$$HZjnݺ烍7꧟~Uf3RGOGBt ]v+W+bkԩY^3bرCׯ9sX'ruuՌ3gL26_h۶mےkIL&< >>^o$i߿]:tH?1ZDvᎊ<`\ԩSUjUIFk[,o2_裏>Rf"jJSNӧm³$UZUcƌ*WAСC9!!!޻wo&' ,Й3gԺukó$իWO&3qśɒElVxxfBCCoO?-IP\^6lP߾}%Igd]=sZvfș/~?Յ <Ξ=KTln?쳹ϒњ;h Iu"y]VNNNZj9L0A111ٳgót17@.rmf|87.ϯb nZnСCTd>˗5l0L&-Y$]ݻw?fϞx衇l @%͜mriͪYvޭQF:HnЌ3K/DD2f/B l-f1^zK5RTT+VH2f7o^[jԨ.]szsÇkɒ%2e Os>u1988hҤI~|0ݱPph֭Ǐ[gٷ?cL&͘1C5j{W^]w&L='[i&+<ȑ#1c$EyP\tvA%‘zpɤ͛[G7oj開/*T5uTM}Ν;/OgϞtǂm&@.0:uR6m:uꔆ kjܸqZre'''M6MWTTTN*Ν;UV-|pM66zww^ZNҞ={4k,߿-Z'N dXsjݺu:Ծ}{=Zڵ[EL6M/dJw.GՆ |XլY3+K&I;vP&Mth U4ũ(9$H?1Q`X5cR%yR[}[a_$[ǷYN^GNTJiq_V:HzIc 4J˗åjդ_RǪU3\L} @ڹSjTZ\JHȼ\BQiS @Tnݤ+W]?hWJ}Jqq96.Nx @D2Ř4,""ߦQ]*yUh@F eK+!A@:бҺuxVΊY}sLʲ,Ik'v~-fe'4Mz M %]4v)3II7"]^z)bRkdb_}_ᒤhòW&kv2~OnhE(@eg[adz8{P;fX,B]9R1vڵ [Y#yaUGE٣vZ̟NYk5Q"yzs~6tEfI4p r5]2I: Թs s*o%$77-qvP$$7?O(ȋ00JhyhhnnR*6],Ԭi3<\JH> =Ϥ$#D"@Kt@2܅[b"1h S[ @&R@Ӆ@n4J,V$I~h@2 7e0OѴ@=pTHj,g9E 4J00?IGH ŋ ghFZЀX kA @9 t@2Anh :OΝ;qƩArqq|}}aÆ,rƏ,,۷k {=qΩN0Rq?/Y, t,^X 4ƍ5sL]pAvܩ}/ 5nXrpȸ5aO-[T TF u]~~~JLLuww͛պu\\ȑ#pL_?..NuM9OOO5oOvSJzlNدM"zj6md%iҥڼy$i޼y ǏgIJܻwo[:t,єʖ5i@Ɗ+ٍC>}&L IW {6zʔ)}iѢE<=R\qα4t;#GX}}},?zhݺuK4i$L\wei̴lSL˗y3<͛t֭[gvttT۶m3-{}$ggg/WLHHP@@uCruu-ϗcbbӝ&@_mݮW^A޳nwE˗=cs-@g~ꩧK.4sPPmۦI&rttC?ΪRԩ=zhĉ PRR0:uJ'Nh"Ӳ.]ҖTS4w)M=d2eϜ9իW>w)...Qjժ%h^lYi?Oޔxւ ǓO>/RGUPP*U EDD(::ZΝ5`jΜ9ܹԩk %^z6e֮]L 6dݲe\㟽լY3~``|}}UN 2D'Oo'|Rj_|~]Kk0]ͷ 7P4h:;]SʕU|yUZU:uҬYtkssiԨQVzuXn޼:uXϛL&>}ڦLLL5_ަlVYҁ]vnϚ5K:tɓ'իW/C+V|իW:@>|X!!!BnވIwgɴlm-^X~):_?\]]? :u|͚5UV-2*S/X˺e;viN=a͞=[|OmڴI[nգG=z`$K^ú4 ]BDž%mZm3㏫jժ6ǦMF.Ho߾}eCBBlr"㣏>ڵkaÆ,>LcڰaL~~I!@Ӻrͱ͛+k4ib~#@_}ajժKnѢBBB4}t}z衇23s|/,jӦM{ŅT M&SY,ڻwu&Lg)]Shk.+VOv6lXɽ2_?G3 9C]Wt|<=z<mt Ndd8`s~&NN-$$DFU?}{yJtNJJ6linnt&I/]ݹsG~Mrtoi7e0hggjUEZ?$k׮6:~<TRRԾ}{ 8P=ʗ/?OǏWL~Z*uNի97lZ6l͛uʕ+moW-Zcccm˗/ʕ+%).N\qU+Vw*&&&0T777;1b<<<):% 9?%%%Yu]tt}];{3x7mڴezfMi>c^=䷴[7oM6)!!AnRTTΟ?ÇkÆ :{bcc5i$mٲEg϶+=Xm2J*TL]zUwb2ԣG?i=gL=].^h߬Y3~k3Db@:zh|ҥKU^]>7n;j;w[/رfΜI.ڵku]kXl=SP]ݷ>_g}VJ>sXmkǎ&@32ݹsL;::j…6y,))ISN՜9s)..NGg7vj 뺳g̙3VZe{MTTz;wH|}}r\ԟEO=`-h`]I=X͘1#ݱ~&km^wk}}}eZHۿ~YOJJҠAt&=zh۶m*SL?C]v 7Ppnݺݻw{IVƍm%$$?&@㟟x +Aj]jLkd24zhm޼Y0tLL=j1bvg ?^qϨ7?L.&MѣG[[o;ӧOg{Mhhh?CZ~WM6irttg޶m\n̈́t}ƶ_-hWV-ۼE.vn{T\f"?Hnʲ|֭oVcƌ^xUV8q\\\+,,LovlKzak?mӭdHoZ SIcݮPwТEԷo_EFFj?~|ppPn ӕ$oo"12{/,=0uUFݷsɓ'nP"[>lfݻ d oFk׮VZ%OOO~kPmpt\\vܙxg+-۶m3:qVXӧO_СC?h6lL&,Ybxiyp+11Q;v9G}X|y kԨ'|b`?~\z\v<߫\rڼyj֬ݻwkԨQEbbbԳgOEDDhƌz饗́ڵyhn߾}N3CoFa.]̙3mCV\es*))Iz5j(+V$j޼yv_oooQFZtΝ{OΝ;ӧ>%Khʔ)Pr)AK uņ {Gڵkz'm&sttG}5 Щh֭Ǐ[gٷ?cL&͘1C5j{W^]w&L='[i&嶐3ݸoܐ2nݺ5k֤;~ՎլY3cժUƍꫯX%U3dRͭG͛75x`+T۷kԩ>>1cBBB&v\5 lVhh$ݻzG(ɤ~M9sV^qiΜ92ב#Gd-Y&۽{٣ &[.ƌ1WN{ZxnݪK.ɔ}NdT{-gxLHeH @F)7e0g'S$^ .IItb˷U\F}58, V3o+LKu#a~akQPD7nrIի?d@@i!Z%^rlXK;4Rjҋ/J)cժe._(M >P:)5m*-_nJLӦ5hZUrs3i@i ݺIW͕+5vRdJ}tuUo_c1^ R]#D%Ք)y,"Bzm4PD7n\WJw_gժ #!v~Ye"1[d=ao-]8mڴi~XiС,*oܨjw$y!9 mjtj~}lYf$.˷EْU%x5@gV6.NScV-gOꗲ,e)KYR%x_RB Qc %#eXVCjv7}+M kK/e[UꝽ{Is'nRT~3\([z˦nNMQ,e)KYRM*4Y% άW7'M#@cf$كl_e+W<= 4KYR,e)KْR|Bޙ,+[mFT5ڵ ,5@Aڳ؎1@qXN?3/7Hhd=j>mx^]5K ˟CJ^^i(nڻv?NSڰ@773Kg}ԩC@Ҳeŋݥ^F48T\\[qmil}&@Jfѭ%$@bHwKignhm<YQ{-}c; @hy8{ȯ]ԫgHJVlFp>z3FpL&_cGcԩҿ-%/VٲҐ!{!4#@D2{s?c;$ {'$ĘI{* s/U{w'Ƹo-)8Izl1~ӓ dKmR'(JG4PHח:}""KW؞ZXypchK4'>K?vҘ1RƜ; @E^EGC~Ws4oe9_?Ojӆ"@ @ Ü#Ho^Blϕ//\&uEJ8c#ҙ3Rb1J7˥ %R3ƒTUn{qb./'yvm:wN׿O?bblϵl)Կߜ^hK; PݴJJJ=׫;vd*4@ =zP'ݻ`b{\9iiX~ҋ H V,N=-꫒'uEi >J?->Xv\Æ2T>k>h UH+Jׯ P9bo^֘86.]ݺ1h [& ҅ ҭ[+PY,\2?a87mJ]!cTFRE{8P\I+VHæϕ*I'KU߄gdhXy]>9@KRP)@q!-Y"-^,]b{vmi8cVm77 h|)ǎQɓgϩ=M%GG h _m@eH`[؞sp3&kݚJ^{qt&b)ol6+<<\fY|[( W(EGK5jHS'EMRg2&M|y嗥ѣ5+2ڋ3t,-ZLLF7={%(HL_31c\u4P(52$Զ-up/etMesa~aTȝi2i\EsISHO>i"=DbVTG…vj]k8wHpX ޸xјQ㏍zKjՊz(2*UHZ ٳUg587lH=p %-Mm1i`n]99<;;K#GJNI}FxƽE 4JZQ#) _=F57n=^Ǎz(|^t24@Y,FŬY?؞\Y;VzuR% hXI=n5޽U|SzeF~$@L~-9sKG؞{Acb^0;IJ*Uq7. Қ5$`5nl,E5`D*(952tDt励2+-_.͝+۞kZ2ESr`M"'zqT<R7@Ʈ]fΔx=;ux~ ii> (hztPԹ3uei|iMsuLx@1(nݒ5[oIQQqGG`O5+ +trhPܾm>0J =;R:@ CIJ]ҬYŋ)M&i@CQOKA,_W`c&wOE;ҪU̙RXɔ}vH4` |FF3fիޓΟ=׻4mԴ)B֨aDNbv4ct=ӥ-'د zqbO=رcN]u98\֭'.@D/N4PJW2d?n{K#8oO=hWOrr2L( q1 ѣ:u2pwH=! gI@9S%IcR6R>}{i駟(’A߹#:E}wR)۴^ڽ[zqcmghb4((I?/5o.}m&M-[{] (&R8h"#%??~}icܳ$ը!}t萱4wD Z@~uK׿>SW,M")+G=h"kݹC 4ȝDi*cfS+'+M(UH=htA-$5h >,H3u$[o+z$sp^|XLJz@ ҨޕN5N@v&L=KfI^Y3iZc sǎ-[ACcMgY0 7K-[l}8f2I/,9;SOc 4(^yGZ\JJJ9ީ3]dh Ԭ)Udl @r4oT')V-믥~"<-( sv.].^V;^X-[7ސNJ9^رRrL} ~ v5&K&8Iry|E IDAThSOQ'w?c8^a$@EHa/O 4NBx4}tz0y׏%{tp=h4Pp1֎y(,m1ɔnnɒ]"AjBڹS 5^((a￷=>d4kzQؽ8S@Iݍ!)%5mj;tV"< @*@3 -\(խ+-Z$ݽkQCZNڵKjՊzPp (}x1WWc97ߔ\\#hXy!?o3 cml,C(NRҠA HM>;wJ&sq&NƏ7<(AH |kYcsPXU/dX,E͙fl6+44o ;O@zwJKJo-ݸrᇍeڶB`fk/qdD geJM͛ i'=4jTJxXԁgE]вo">,=(u$EFJo%-_n{|c0oo¨, PN8h$OMsӦҪUgE-u4rm;4cG (Mt~ܪmx4ȘdsX34P+'5jdltu(=,?yי::%(YS~_T_I!)ʔIC%J"d:e1e0ef gμ}̹},WvzioVyjUID@ȿøavCDD;}{w(U ^},١b$"JE;5$"""a 1mTC`(N"ZD ( ϖ &/tz+@d$*N"Rtih֬ag[PqQ-^S7hɓ&%DDD<6x”*_^1Q-E" Y̗{n]=?""rul'f^V` hR1Q-rnmh0JEDj:p~V< o SFq*<|y}+/- |Ӕ:s޻7L \(QJEs5?- ~E+oV06ntY0s&t(KCfp+H;{^zɬ}iX'aD(SF1'OTZ qƽn!""/* 65ssFf4JED@ZED :Bvgi1!DD$4[ AHA3<g[{YDDN="WHfCDDDC\t=z8xmg%""Jɟɰahذ!%K$88vڱ`,ow1NzzeرcIMMՓøO|&A^޵+ (o6 6d…L0C1f֬YC= x/&MO>RSSyG)[,Zb֭)SEo2DFG:HKAEDC=D`` 7f/^} VY l :HDD cmo׮]:3g0j(,WD-h߾#>zX/a}lڤx)и1.ҫiJT4+fm___nKyf{/}wUf&''ɲ^@AlfZa"s\Y1Q}}׎zQ*%+_z%vN?w7o&.n̙3Kv-m̓ӧOcǎ????ʖ-KժU O>L6_9=n6nt?$\tRDD@_Q{w7o9rŋ;۶m:r|Ǽ{4hЀZjÇsjԨAZ =k,9EՃ :KH 2+Wrq{c:uƍ'66e˖1d?Χ~3>*U/55{:~^fMjժvLR'==_\&.]Z[[v.&co.dpall YϗH~h;32rH .8J^D0/ӧ!ۍ9I#"nq~EAfrڵ/={p9~*U.7(X,t)׿#ߐW 1= ))r$"" W&- ]tv^z5qRcp萳alQCϕHaR$۰aC[gA΍8ؿkhԨe'111:s@ɒ^TːO?fZs\\LU:8Pr2 lewMg%"<}+@[,nq;˔)]r~n"sUbE ~s?u[[Nz_2Ub8|ի`Lg[}; G""J=2ep_؄◱NJ+2&?O8 T-"?~Lm/,bP#ŧOС&Y>p*eRX5kQbcc9rc?$$K}3&p큶X,yN/g.SzЫVAZb""r{}AcǎLmuԡRJvZh, fצ lO> >>znDD@{8{۾ow׮]ߎڵkuFΘK * 月'[]@'pm`/QLbrשEDD &ݻiIM믎bŊqmrf}R/-[ƍopTԩSXn[[y;o7[8nunL1TFq^ ^ sضmc?շ]w}mFLLLosNy!!!*Uʱ/걸>%KrwBZ[D$~G< L7:g__3\T,"Đf_h۶moۮ];~WNmqL tr)j*fΜ?;;wLҥuzya""sϝ;wٹq= h֯X1ZDD 897}||x'˗/mo޺u+{?&000ǿ/99.+ 4Hg `+|hərT}.-pn”2<͛MoHH+Um Ȍݱcl-[GѾ}{ƌíޚ#K-[p4|ŋ}z_]HNlݺ5ӂڵ/unF0ׇ_W^1"""E& *;?.cQFѯ_?}Y 㫯L/qtz9 ɽo?- L1sY,sfM7)""by@QJ\\V.>>J*9[uOLLaÆTX-v)O=#==PFIvr}{aÆs:iəX~6W)/("vڱf]vQ^Kf^֮u]w뜋""'K,{lٲ;v <j\`=z^cWe޼yo]?VGao٠J8z >^5FEDDŊ`/ \{8pǧÌ0r$9livHC=PgݻЗkɒ%̛7???>%Eۓͪ""ri+VpK÷ag\6^ S*y33|u8ё FQI;||6le9s™3gׯ_:ѣLKsn(""NӦMVT)NݡHJ2V+؉HI] X,4ḵ?tPӧaaSlٲ,_c / /\}4&Mb W8ED7,H-Z󊉈=VAӦaYDD@ȿ஻vR)*RSah ˴ҥRA⊑H~;v8z@NZ[D`D3L"u¢hqs睦'ڞ@{""ϡY3XkeP#""JEʖ50`zdvTLD;>mM[/0b)'""ZDa"low=i Q|DDD (pftiC T|DDs)"#ILI$x aaԩpYVD̟*}-Zuy;_[_wa~]D\R)޷ձvl%,Z}*f:VX;‰,a5Ѵ&c]J""?i$W5 @`@[=v_q吒ŋ|б:VzֱJD'qIqXJ/b]O N4x0-;+*4ԶN޺:-^o_#3FDģ\3~zu.?Ve0gFZ tX#z|y&:evfه+l?$b""-9 sm]?C /""X";^<ĘU]>}L{͚(ڙC`vDD2e#Q-"W@nŋ,ǎA׮eKSU|DDD \o?7l>~{Q-"WT͚ *ƍpb""W0rYQv5l:+hIp"#AE8ozIm3 hB@GDE0eN?Lϵ\~ ͚0`L KBp#""O!)|j2ۮ[gVޱ7V\DHJ2+jխkFĴh߱|rA-"y"#ILI$x aa Hs+֭ZMʕٸw/.gL_ӦO_^RLDm<{:2rJU7U*q>ED<5J\R@+b ""ZD ) Gм9llSa R|DDD b%KTDDr/11>m3~}Q-"# _NdtW?΀D8+VY:GM/t抏h) tDTSMǪa".p뭰i 4#s侶SD ;e!!,gúED._m[^Z ##ILI$x aazBDD<ޟfl6OV+qqqXVbccldG_})&"rq~ y>>0v, ~WҺ5J\R@+/"19ޟ!"^ud=dzsZ޸qW.y)-%.\j1(^vIt=0L R^vJ ,\)m7f mL˖5 }g%"ETm-"A60a6m`6 Q-R䄆µךA3D& ΅f`z/ ?5j(F"""JE 3馎-'Odvu/ ({$gDhXns}OزBB%"Ip"#ZkW}sA۝?FApi+_\H PDDD@d@GDE0eݔ"tnϚsAěn f>oo CO~ NHСC-$]l6ӻ3pi7 =s*$1%⁄ s`׫=j%..Jɕ^c10~b"- p^>̛-Z`5J0}Ƌpn/cvbb"b PkSxgJ="^{w ?QLD =&OvUh.)>"""WzEbbᆱxV;wM7'ϝ;JEDD@Hl/[&6̘7[b`@`*hf!ٳ1xI8wδ5j6} .""ZDף߿́TDY(lgSOMд#""ZD D* XX1TOC.pi Kat(YR1Q-R"# _Ndtd|۷CV0m`6󿈈\}*c%^KEoԮsDlwyS xq4X#Ox? /ٳaDEj;r3sn̓ƍGFGH`@Bt2PPH^:\AApS\ME6lU?~sa9"EDУ>~>P1Μ~reS=2h$"""zEF]6ĕ|vrsVx:@!-Z᫯+!=&Og\$,\YDDD xQۯjV;sAjik܌yq-""ZD|8wƎKjjpɤV-xojي) 3@cVѧߦaÆ,\ &p!ƌÚ5kѣau~\|4i҄| 72eʰh"BBBtK&Cbf{TSVJK3=-ZͦƍuQ#HDDD t!CP#a СCcMƱc.\`ݛ3mڴBXU?Lpp0!!!KS<>wƌQLh:p:t瞃ְIM7Q]|DDD`X:u*]Orl6O !!.]0{lƏOxxxzeʔaҥmۖ={2k,~U>FrGőD {ϔZƴY,0lnR1Q]8qAШQ#z^zB׮]Uiӆ+W2p@^|Biٲ% bƌ^@GDE0eT `?m61B1oӧOcǎ????ʖ-KժU O>L6+£kW8M[͚fH(YR1"ZzL<zg:f?t҅:uDtt4۷gŊX yǏӤIYd wqםH+qIqXӄޜ:w40-Ν A3SN+ԩSgrA~'f͚Ar܍7ި^/'0 o2eIbJ" S@DD脄V+OLJ8Ko޽;-\rl߾jժyœ`zA`` ׯ~zECMU{+?7oUVnmŋ'.. *\9 ~hV@s)>|wU gw+>""z.>1n2y9s&-`ɗ<=z zJݻw{$&&ү_?>>n2.5o޼L/XmE-Z=Jڵy衇Xzu?nٳgOYӦMۮCETU.cاxzǎ;2թSJ*)]к5cY|9|۱YVgt5Krr2_j}~g@oݺ={ 2:DFk^5 L\'Ni&[n%SE\@ӧ55^$رn+?*6ԩSݻױ_fMǐ_K|v]i_E^>,4LP>Bz~}&/-[zN?߿?Э-88!Cnv GAV:DDDD t.Xmtoݺ%ݳgΝsWR%t-xd|Ǚ1f"!!3 ))п?DE9W閜8bhsBB?IIIn>}:ʕ+LJ1c`tsq~&N%uyHS ,""zVI6lؐzkP2ꛬY4Ǿ}uvvʹiXr%qqqѰaCzSO=@paK/¾}v-̜ O>fcٲeg,o~z|A0$Ϟ•d  ζMa,sG< F )}G b[nt/SL7cve{SҤI~f̘Abb"}<4mm5RbEW)U {Ϲ?jdxH6bbb8~[[ƍܹs̡CbҤIk׎Pf͚,Y7ݺ9%LhO49e$㏤ۗwQ +F%A)!ƍc߾}tЁ8?CfLԁ+`\%"""@hBCC<>c)גVj*v`` -Z .tldM-C8vXb ʖ-Wd7Î0cbܹsONTV#GߟTbW^P\\elX $)!97 <A0UW_…PoQB""zV tPPP맺kl޽;y~6m2uތ+lu].oB;maz_+×_d7$~W5jP^|/OGah6mrdz{P]MDDC;wm۶9[}}@?NNNGn$K*v|Ƅb\IuNƪdɒ}zuHr D\ϹrQbŊL S\vsz0{YÆv-̜  !x?o}`111nC'h@v r, C=ۮYƱmX{xg\-00GR|̙l|}!J̥y)X̓$؆ fAb;u)SB2`R ={pyca(VLpaADDυ@g1C}||x'˗/m]j*U1_RJ c"ɓ'KJrr2۷ow4H 3f͂&M]裐^fwe%Sڵ tnݜɳbpcVpW,"""J=(T_}n?tPG̎;طo_IIIq|9V\YjoEo׸qc_ zۼHMMe˖qzee)]03!EItttk׮MPPPߗL 8y~\_icj:ϚzhK2װB =/%J`SD 6mԩSYr%YLڵc{뒴m6Goqhh(+VXٳoݻk0Wݺ;'NW^)=>>L?۾};&L 111_jO Y Z ?,&׫V%ǵ\Lٲe;{y뭷8qovy9rgΜa˖- :ԭGb֭똣oeʵO>yٱ ==`y"$ /^0ެqb迱cRbE7nRy9o+5%d~X\ǎಜ=c@ɒz-h!w>> 6ߜ9s ̙3/G KGyҤIfN##9p%J?̶g;+Gb0c tu灊{#ah~fq1oTT\+1%$%Ō"W ɷ/]2̝ C:EDDD 8a`ݻٵk`zS%Xh5kdڵ 2@ӴixgHHHCOĉy #$$$L׮]?~<+A^2`Z=, ճ5#X15 v6C}|)1ME qckؼ[ɳhHժ?c3-۷yVxiػz[g%""""yUr←R2ҥIEFGH`@BB$=^ƍ3e d+G> 'O ?S’%Kuh21B@?Wnc:a1Ô;qgժSOPb%WF Y '%*oR~0rYGLoSNg1r"}| ̟W!BC͜{5E< }Ќ7)52ѯ ŊD3'V- 1>ج]2 hj9ۓg??S[{W,"""r-" \  !""BYÆr9KK!%%1uǡjU34JHDDυzEЩYݽ۬]<(\ ii{Uyq ` M,)GB4".Aqh-aVyӎ{Ծ*L1Qe:6 `Ѐ!_ % )\"fd9O>}nWH~hiDkx٤sG_&n=Z;VEwruw $M, WTk^im̨]WDh<b *4@^@ZD?-INI))O>ҤI҈Ґ!ʹtΟ7(gd9;[nذ0i {|w%1u7oH2x1O~b,u'"jj")?_*(]?kh#'&J &P@8Ng_/]2fiBi\U%I:um^y)z&~^SRl9qs8c9[Z*\MUUR/UҞrd1 WNO/o $K6|xм5@PJg_-6Mv]6MEE ZK}Z- @@ iCbxpVb+Z,J@fG/~E@P? ZɓS^^F{w hӧ 6ܛp朼-׭O?7*&&FWր(ph9++K7nTAA r+p?^'NPLL "+WԻᆱr۷OO|.I:tFω ~{JFMP]tQ=(hagϞu5jO>p@3IKKӀlX@дiO*44T| J@ 2-ƌσe6:6oެիW>s{,<<\gΜ@' n:t']jυV.OFK?fө^zI)));vPlldb5ھ}>Р:_~aÆ)00B_Om6s]v>;hh/^TXXjkk%߼nٲ߲^EyZ?WWWsκt$ct-ffsN3<_}hÇ5}tW tRUTTP@#*++o>v ZW_}egɷoKtnw}]v)??_555뮻+88XQQQvLp ՝;>>ǧWttVZ#FT˗/ׂ j*mڴIs trɛFII uQ:tHyyyѣGp8ݻS+]PP/BNjU߾} +p?PVV/^-[SNJHHPdd[}㏵{n=쳒$ժ2p ##C%I3f̨%-v]SNէ~J!/[Np84bڵ|lghaڷo?fs^|EG?OS(33SΝd,JM2k, 8z 5k֨SNJOO-?4}t]pA| Oeddȵoϙ3GNSE}\۷Wtt;&á %&&_ȑ#)haGCꩧo_h@W4e8qB1o9/_֩S}矻_os^^mٚfӱc$Ih1Z޶m?3 2ģY.թS'4 hiӮ;.}/|BktK&ܹsv=^ܪ^{ b!@Ϯݷ%^nkONI^d'._^^sAAA?G纶C{gUF]:hիW歟kjj$!*22ңy~|D hDjjL&MRDDG6D2 Vq!I lm\_;ghyYYY*//7CBBΣssss>wQ<<7ZTUUiŊbo~v4 WϮŋ5amܸQQQQС۱uuC\&~U;99Y]t 6'&&-khi&={܏Ȳe܂&BsFd͚5KQQQ孮.K%IM^P ~틪zplX4uTΫիg}֫h@ZZq<:z={G4;tH/^pY۴i6nN:8{}p}y=Ο?={ѹk֬q=a„Fi)tk.cW3w\n<駮\֕RO[;Tˠ5n I$oͮ'Oy.\K/dhɒ%^hꨨp[b{mu)77wht:0UBB6U]O]'kHuu %1<hhy-Ǝy/w}g/hX4N>]wWǩ6ĉ3g~i cС _WEDD(((H Pjj9C{oFՒ88}W~v}iyE N:y};77W˗/7|Iބ.h~"𮭆W]oIJ%&&Zaaa1ݫ]viΜ9qZj̙9s}5juk׮ es&C^^[@u~ҥ*+++Ba@3),,TAA?x&)--%9f}@U Wozs\hׯf͚2h s;??_O=]XZ`$u7Z;>}MF=ޱcV\iͣU}C&M2;v6oެm_6!ڵkkܸqtC999WQRR_ZjKFi(/[LEEE}J k׺-JLAb3..N 0))IY ~I'N8Q۷obь3*((ȫˇ.hnfiӦu]bV~_b(==]:t֭[@8qB/_6)..֧~3gjҥ ߿n7mV9rz- >\z/*""BׯWll,,4{]P&QwU\\Yfi…z)dhW\i𸼼<%''+++K=z믿-lhll TbbС-Z6Xݻww;.$$D׿躍d޽zה3g( @sx =s;($,[V44~x5-Z^x٣ɓ'vZ 8P.\~zVXXOyy}3ʕ+zj>|Xm۶ULLq=3wٳ[o}zGh"ݛ`Zn4|S0Ÿo߮O>D_|v{[nz衇4f?opYO4 0 h4BuTVflI ^chZԣ~=_k Ѐm@/ xecQ~&@@< 4Kݝ+..P``kA/7y\REWUI۵<'}{]{%P?S?7%9KK:ymd^c@L܀66kqŪU``v]IRTMTRr[5*J6m(dP?S?cq:^mdeTTT]åKMRf'%'7}NRLspƅ ,]{''{~^c@Lf 4<IDATh @YN[q6Mv]6MEEEpc.]ʌ0cx5~Phy5~e 4h @@h @@  @@ 4h 4h @4h @@  @@ 4h@ 4hu)JM׺IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/triangle_rectangle.odt0000644000175000017500000021176612014170666030125 0ustar georgeskgeorgeskPK|_:^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK|_:Configurations2/statusbar/PK|_:'Configurations2/accelerator/current.xmlPKPK|_:Configurations2/floater/PK|_:Configurations2/popupmenu/PK|_:Configurations2/progressbar/PK|_:Configurations2/menubar/PK|_:Configurations2/toolbar/PK|_:Configurations2/images/Bitmaps/PK|_:C-Pictures/100002010000037800000185358AF3AF.pngPNG  IHDRxQsRGBbKGD pHYsnu>tIME ؋ IDATxwtTeLz'!$2Ek[bG@uYemtX˲.HPd{O&#F])ɛyd 06rgڵ \.QQQ <.#Fu￟\+L9,&<ϴiӘ;w.%%%$&&r3d 3w\5kÆ k0a'NS""""'r,OȐ!Cx:tO^vs3eOM7?c=db͚5tI WDDDDTD>1cƐC@@'Ofر}?̘1QFkHee%E f?8<a˻KFF/NSSiii8{XB!a3+_rqcY;wg*dQ9<W]u ,,>_.gEDDDDOh7n/d21oj׿MFMM 6SN9E `wߍbQ""""'r4 F?+~~~d/^g9ƍǘ1ckv/Rb(7tٿh˖-3`w} UDDDDڔ0 C1˗?rl6< z+fOdǎ>٬7EDDDc+X锊rZ3[~juͻU7@v Qa gݭ_%~}Aӹ)sdK3<ߒO}dBC,i$?:Wm45+TQ9֪toGF\dbp} .'׼(MacXId䥘L?ؚ%YgP_k(.^EDDDDOh[| 7=Ojx"+3q4`2Y%%9|}GINx<""""'ʩuynuŋjynn?& WWt{b;xtg ȑص崎Vk$aagBz/r9#g2 G>/ӿvCi 7w YYl>r)""""rLD)x,Έ?m0 "'i7'13EDDDDOرbɶ `Ge@y޽r{56:əHM:}Ez6""""'ҡ]ә @HoIKҦwy\\^. VGJxfssPQ|Ȭs痠;XDDDDTkX͗_̀˰b77rX%Iq5nGV] ̢t O|P`grlQmo 0p{tdc5,Kп[G&/m.PTJ뱄?Bju;]DDDDT{,\ }TfAm C\ONu _ǯMon**V"MMy^>>$%[ t7NUU6I1+3t*}ʙIXpL0݌4 ;- ƑV<ؚY&huSS'4c(Bmsqs1/}?a99z{[&ݻ_CjT """"'϶mQZ>>Q 1o!t UUcCfDDOz,zjQ8$ """"'Ҿ}b &#cQhᚳu^5 eT 5 )*]5NjT"#N#"""'x<0 '=zLlzMX; kg`5[Xpɳ K>6ϟASS׼Cr'@""""*x"Ǿ}oC-;ɗOX|2 WMb-+y[}Y6b'<סfdz46XB1ڙDDDDTD0*uĿӻ];1.q8&Q[7}ZS{Y}oS₺b5Hv\Z^] /ii?K; ihL:c؂1֕pn),YlbQ7ee?"y-It~v>K.wt)qJ}}HIyyEDDDTDq  Y@-#g30AJlrstx[$&hM<yhYqOX5]\nV HVG6A]v5o6{3zMliQ*++Q$$nc}s#, 94=NtXys3cu%ӱZôsIW?;o>0 K0\AaK\e^~~ɤ>GnhQfݺ4wtv.s7ngP/e-ECE'O"!!jM< jjiӠq~KR9W9f {OOina~ ;@n#9 f""""*xQm8ڍѷdo1ArYwsot!c"55^yӇѤ t$9[wey!}Tx367Wp0ˇxi~sLNѿnEEU\ y?T^ 2|=xDDDDT?R\/p22V*7׾Kw /FO޲JԔ5ӝGI; czݯV0?r2qv,@3;+FϣgpL-+o~4 -`ƑVQiϞq8&`6i8[[vooG$9Du3/L&ݻciNp U(|H,1@.YFnd*+?\̚jqr䂅7R0`6l&ᢰ_>s H[+)y/?02Vk9r. g_4 7ee?ByIIO{v"Qi+k&t 8$ӧ)_ݟrͲ{-|䷷s׉*.-EϠf346XBf&"""*x"VUz6o>uܯ[T0«[_ p]LKW%&:Ҧ`6),Q6lD]ЧϿxv<"fMfxY Ngd!"#/nDDDDOp44{W `a ii ?Ca J#8g ;Sfo}kɊQH?0ܔ.`6^6[ORR&ӽU KDDDTD}hJJ`FBicu \X6lWXOVGt`EϠz s+N|_G+o tmOYY R F- @FwEo9Lߐ;mO72I+o tMvI^ޔ/˟hʭ)d؂1pF qY}/t3xi%*2fbE(,Q@ѳ (l/@EcWOb(_e(/h:)3DDDDO:{[v6YX,Vs l,o/sл9)*KIݵ^HOEh) KDDDTsΎ* ""V(Ї2|o`(#`.JK"?..W׼o"))}<<7dfbbڢo:|2h9硲2q:sHN\ܟ򦈈IǷvmb ߐHK{^'37/ྏnycbޅ3y nD}^sfsc f\ >ca$jj6c+oFG_MZT%"""*xryXDt_n}.d`6ǒYF^3TV0\̚8TX"""'G!++hGYtYM\&n| OL&9\SVO|p"88Sa ?޽[v*,,@?s?c~r9NZV|Wq*HM}Io}yIIJ7'ZӇ=Xע硼|ijbHJz1 KDDDTʷ~NOXLx-¿/̅)g*^ jj6p -Fao~=OtMfF}[ @L`7VGRhig\:^|OO󄄜>~ KDDDO~ƍ}ا$+t7sp;)a =W)*7nw׼oӧ;%""'r/8wwu 5TrY޼sLB|N-znʖ?"y-^&%""'MMEYӃxHKIH rT2lj[™CxN.z՟,y[,KBZySDDDO[/l>>8](@Ec5KY'vpL+WGIM}OaIWrՓHJz L>+.~c,< g+&ItNgd[e[KHOMaIWc iJt!+~i)rr`\rsR^pb! DDDTxͅCJ$"<.~)a=DI8yLz-9dJTMfPX"""*x֭|0p ||eK`MfBzr?p:SwU;0CvsHQX"""*xҞ[Nc.cR(r.\tJ[RxoBN+zNP\:O|`` """*xlaӦA~/^R-þ<N+c5;#pSR\e^I>KTe KDDDOڋOvi"?iOE@I}9:_>ECEjtxt#!Anʛ"""*xr<56n]qz<3-E;7Sgyc2N'W[44|5g67'1UEDDT x,S(rXV;6r;hr70?V0]DCC$jj6pʛ&QQHOժc4EDDTpjm}q K=TpT( =+.WΠd O|P`grQ l=U\ܝ\`Wn.O d?ᡢbyySij&)ݢDDDTHeev}5w)9"Øg+ Qs-Ut_[ivz[,G򦈈 /{-;JFF#nf[a$!3ML&א65HahK?^P(fjl*@jx"+Fͥ[@‘?4pL uȬIO_O%""'?d)۷_㣿K*`]t˲+gl T8'?eeKx惃OnMP@%""'?fݺ^46=Dӧ+9*r z j[->;D]:_MA Ea)*zk?DDDOOW]/:) FoJph 0^Eȏ=?ByII{< QW;_Qy\X\NnSgߋdR8EϠf346XB1 LDDTih'W `lF}7n SooP0r]{7}ZRSj PX""']ǖ-PY -V(rLw[ϝc2P0rXΒ+o܇Z>-Za tn.W5a|^!M'<,%""*xyx<.0f#:z0 n[/ 'wGbPw"frSZhn.k11(,Qo}9AV+9\,kr0p3 =Ma /k&tr*iiSkn?>dR8ҦZV|9\^ {#G]cc99Yxj5Xa KDDTۼLZN@KtHfX\,Ò.WdzTT0JXDDDOkľS =UHd̜/_˲381v‘cXΦ <:Lل QX""'mk۶.jFF E:4 s-E1en__2ѭ% KDDTyCvv O#=zDlM F:r-=-+ӏe#fd P8r\54pLf#+o}ii/`(,Qm /&ldffA9|.ºRI<>ps*yʏ0 !fÇb/Aa \ "#/#)iBNk{n.Xt#}s.x QMP [I Od(`i礠`o{@z3 cdf~`S[KF ͙|=LnoʾcCV(t; Wڌa(.^Ha˭!?T^ 2|%""*xb͚xZVr =gt YKmx[1@z / gWc&OJ=+ϟFSS׼Ow#6F%""*x]YE*+P0e{2n1(odoU^jݛ!=ןĐ؟|PmAM8Oظkb gϿVh׷,8޽P˯)fCV緼;p.g/ԝc[F;ur54%'g"_6ltRS`),Q fÆ~}_?YHre6lc}Wl(F^m\nt Cz焘wW.55pLS{͈ IO_/Nz-~~B0 W緖[=\rhCz `OM|,2{+X\z_li9M(8$WX""*x*xYڵ1|ԙ`Cm)cW<'ǟ18wz'*f{ `Ũy+\0 Ea(*]5o'=}(,<,nD3pr"޶]^zDRkc}"{a1[~r͍\6>hywQ ꦀ=ee?ByZ%"'A@ˢIIyqsͲ{[Wӏp_*.Xtߔ`@T:G&WUHG,z}=^K(7fQfǎ(,s{0+C3t8jZ8ox*vD{M_bb'-Yf+"'!++p;=W0"?bW>-Jū'bŤs:q8&SU/ afVX""*xҞd%33K 3[kn2ux\:'$4zK@]aI{ֺZn#ILOΕok=='^C`p(,|pk63 ع# ea6aX w<Ɓ&u7*#ax(-}947{$9y11W+,<9֬ߑ2Y6/ޏjϹ1F@H',zkp85oWEDTx(+[.e)_5eZKch F:8ơ+oq#))򦈈 Kna'AAnPD~0 %XxʙУ‘N,$'g"k^yJT夥fPX""*xr4|ɦM}`D~%u>ۻVʊQsG&+\*rs} З >%"'GæM'QS?T[PDĈ7cqAYy UkQxo],./9n%rp렫x]L&#]U,+1Cf̈́>?D%"'?fϞp8&4Z<|Kc[oo`DIaLJJ{YDDT䇲Bpk1mosۊZ/ ;T`nRX2.W׼_/RSЭE KDDOrs{mFV l*c p Mf_4g)Ae*rsԔ5Eb#9Vau]}CssI"r^kZly 6~55[p8[99;HN~TQZJJf? f"#c #rx 7 Y@-Fa@T əHm&W޴}ii/w ^װn]*{ >CGn߾HpD~FSS9OSY p2k&"ڷ+ྒྷnU0"GTU} y!vll%"*x}r0 Fx}Y>u<{e(#r\z^|O/k 9}:V]DTٽnrslgР,"ҁܼ>~u<'Vi nw)*]5ߛiNa WнyB`l:O}O|V]:sNV0"m0<-'?E^6[zM & KDTq8cϞZ$lfp/> }$/o}@ =+6~UWNn46~5o hMQc/;; ߓ"A=n}^ ٻ(%) . 6?VW"])UD" "( ()I#O0$H$<}sy3y{]z,"Kp8v3u#M+oꉋT%XBIĩWT1[(Ơ$+k"[/V@Bh,nz!N TUp7!-M( B[+Bs>S>EvqZE^}Mڵ{B_ |ŋ}R IDATsHOc?99 Ҫ&::RSFH?G%%>ԑde}-Axog<뗍斳pW]u7qT?o ǟp$$ 7Tq}֛Y $XBfE7JPhEnĘ ?lO"8f=/Gݾ('g?w=Dr:yKǘ1S^ؽQgb۵6JJEAÆ 7j#qYY=RߠKy֬i/vL! ^sQV(߶!qs},;ЅKf L RSceȐ[N,[6nlݺ;?|&SHR^|q&W\qQKרڵ{nݾ'>~*cg֯ڵ锗KϽBfa·*ȫ$(BP(Iq|p $[_| }$i01? l|>:d<,pU #+kIIϢնinmgx ޖ !$;S<cbn!NZf֕qa2[%<@AM'ȍ|:?~Q̷ۃu>;XG&qս޽1=Su%HM^߱Qӹۇr{%,n99?isE4z^9mU 5a f^ȨQØ:?(7][[ǎ{.7~ϞOy|>fΜǖ-;yQƜafyts Nʛ;+=u0!$C^ޔvd#BaљdT[lC>E. 2s,?c~\}}~W\\h?33/=>[Fޜf`HSdf.b$Ul4SzO2x*T$&!N6(>5-EObg<^˖DG4`@og9͐VMJ$%*z ]Veem ZJOl{ l:BuYpTBJ_Yňeɼn $&<̈8۬]IצMT`[Pyϥ<;AޜfN!) #.^T*sV?n]?AE k *HJz*hjw?妛Fѹs{؛}ogڴ9yhFb:5EpbƬ|Uӌq#?~$wq% ^/mv$8XZpsx<Jp8XTZ㇓ ڍAi^W>c ! +vx0m2uC4z1cy 8-{3b1/sCؽ;Od!^UQ*SuK`Zna$yyEX,ރ>}nh҃R4R_$eϞz.C6Z)l %\LJTƋ;lv3Vo)$xxG`]{WVV#x䑉:-Ōp}֭W\w>d*C\ɞ=\qڕ{zL98^˗>r*qrcl޼OMz(z>u3C~_XXx<{ݛ/o^P$z ,t2NGHHWVt+ٳIV az(c}=m#CR~ЯpVXbb78ܳ{ɓd2RTTȑ1o83nz-O7?bs| L3LaŊu'|{Q))I<_`ݺMG;qc0^ >[o}_c2 2!!t61Nʛ^P6oT&$xH|jj~'&>٬omm`׮ Ǐ$% lbK,Y}r6nN~=` y_p')7 L3y _RRF^{)T*%ॗeٰa+Kf䷹;cJwP(Hax V_cƼʤI.o^jcHM}DE B6huSR2aÆ!")|>Opm؟%h4tY?Kׯ'r|bg^ۏw9ؓ7k';l޼H! Kb0](_8.)n׮Ok+AFF {nr;ԩyO(((!33ѣOqRX6%%sxjd6B  ի:@DDf|.] 7S(]z_jd߾oQC?z'_|O?BaN!=}k3Et,,!Z9KigtN׎ʲB y @rRYur3fj` SrخQRR@v6`ذr0 hUf_=st`m >AI]GV_yZv5pu덬ZInK2^IZCq6mnkwlݺ;?||[)~a_W\D r@LF~p]9\8! 9t4Kv]fժPv딠 ! ^w|>G~ђNe|[A|Xudgocɒx;`4uo/h+X( ! LѥB5:]z+Cزe(nwLIZWуQ*wvm`3j5|l{>ydW_ݛ; D3ǧ׾J 1x >WZFDD>Y*~mLIZix<5{*ڶmFrs 1s[`4yǝ_fE!13  AʛVrs_dJ#[ފ]-Bmժ wCQQ{tx*A.|ٓwIm-C6:9R.]#l(*BiiQ(ԁ6EQZNv90!$k~jjvjk kR9b0}Lu3C~_XXx<{ݛ/A@P0߿c/j]u\_쪐0!S-$''+bbTz5kM&$xڵx,t\T^JJ> _|u6;w2` r NGo1dD`9!Sy[n*זHp'DҒ8nVж탨akj/Y6$`B ,|trfώByybc1CHًR`Г־Yٳ?硇&k56z:JEII97e?~x<]kwpqa60gٸqƽΤI2l`9P2N6wJg!GE2_,hҮҡ3%Boٲ;b4Fѳ;RVVl`Ѷm Ujn0:JJJl'%bRR<H` NT!D73{>Oy_Ffr"mz~*N6Sݮoz&>>ÈqsX,^yÅh6/)(UԤ]CU%$xoϞYVJJp\|TǷہ,f8 B ?@P1ryEyl ٸ޽ZjjpǛ)ѡ]r3p`,))rg\UMnݤM Iı?? IJFcN~$y2'OL|>~7~Z#>?va+u[gC"Oj/ZjVcLnf:n!F=!ΰ伀պ7uis/T.B<'_…})+kN^o՞xR&s0|S,ؾ pnA税'vZ'epz^ڶm['NRS-Ņ~:gO%TWTeN99 |W 5 $5!gYBNVCZUDE]MZڛh1,! h3AAAGڴI!+k_O'F?w9?oaMhg;)_Zfgh[77ݺѣē{عSɮ]zvԳeSuȵta0 qVPV/^X,>1]%$~3o^&1X9r2'OjVw/m 5<7"+&)S;C:tORS$%%׺}֭Ӳz͛@޽d'>`P Dg㤨=4i73HMNxx/ ꫞ٳ"#r9?tN^۶1DJZi]}t;+pN.|qtL->'4̝|k+kO{Rw$wWtUjef3n}% ~or7iEڴ*FuuTTDPUMOvI5✘^BB 2O*ಏKz)hUc8 _` 8.9Yn{ػasLnS' BѿS?2&ju8OT&~{5۷/"??9=fڱc+z(7.]R B4{[JwrGwR>2f^JőO,)>tk'}^ݩL.اՌkyB4VVrs'bΡ7Jqqwұ$JKHג\6LE6t~11۷o7v-0TSzbMo ~n>8d O=bt7sΩ{/++k0 ƬYuk甃Df,d߾ TWHӥRDG&5  %;ظq1,\x1wގFc'*Jj !WW?/WT{yvCmX.h'RCnf1*,,孷,_~0Q50yr'D3vW2_ʛ%X"ƍ7Np?|}7*#Vk03OVWWayLL&!Bf/%B __ddVDDDѫQ1 ƍma'DsP(Nhhvk(V e·zh% ygٱcGPfÆq8z#P(tgyDFFS]ZIIIBP-ϟ% %%%ӿ2/#,̟q%al٢Ef*$ ;O.c6ѩ[G^K\ib˖vWJDM\.dر̛7/肒TWG*HN=(J%.{B Ncbj{[esX*~$`%x:۷o8#qՙNC9k2T*l \.!.a:~~%RڶrxbTaL%)^Rȑ1X2kBN6ӡ3de}Ol-( {5kڳ~TWO* %%PZjR'yҋ'Fz3_ N\t4ȭͿ>OM]MGRR$)p:nو#NJ@Ғ ݺ !ajuEkk7fMGJJNƌCUUU`0v=jV mŚ'IIh4BX50K(s^ы&pZe<;V^/eH)"D0Q(kׯINV۶Qݾ͛zu yy$`"7|6DT*f !DJh'_K.~%Sq[Wݺyq%JŢs2e^$D%z 9ii0\%y?+WkדxeoԨQk.h~GImmaaQ]s;Ћ0M!DP;Y|0l"j`V3e[xsK. {ԁ}|M(B,22搑)@SKnV4u0Z h _|7|ԩS aWorjj$'mSi !Z{2}8к|ͬUsZgՐ!:Gt8+B*v[m/TpԬc,֮M+ 8s ^II < > i&}=Auu$!!zڴY?_S룸L`!D{r@ )+?X\|q_쟓c+n=>"DPhde-}hqmlt%WQP0K&N7zhBBB3fLwz yyՊߠ֌ۭ!1 EAuj%%%r !ZɎW*|Y s~o.Q?T{Ɉ !Zg_ddf~Aj)]Bo+-39۴ifb„ L͹s!1;[_ᨡ&QMvloXDDJK!GP)Uwֹ˩1hOEBB$wpԩ )"D g6C.sȘt6+oVo3\i`۶;q$`%x#G[niC{b<6[NP(tDH#WZ<.;BH }< [],3Ҿ;U̜Y*Ё9礰p&Vٴi NgL>W_m,EVJJHt:IIw՛'V-YgbiB ]{xb TT!  tp}3e.VEJDVuJA/x~'Vf Gooy8z#mtB'Vĩ}:>}a]L4abݺX3Jĉ'x eee mZWJuuZ 7M S7o3wj 8c[ڲiB`<%NGARR2k[ҋ'DkRi$%Ci|mm#;R~-K$;>Ō?q ǣDTTR͙Z¤o2>tqFeƤ/ Q%5kunWANIZ r`ъ) еbRR^COntmۭ\޽$x橧"11#F:Ѓ'rZc;B+dZ)@TJP̻9sx:XrXgWjNZ^elB, c:wKHH+oVwS\%`^vv6o6SNEC/a <6"K`Dqu%LIк-O[\Ѿ}u(׺hSi !1SiXzѸ򦝂S*Jw#G2h s6΃UJ7Q Z6}ƚlR“u0 hvu \f2~*^{RR;}߿_/B4ƐYY5B۠Ci~1 N] X >+.XRF9|>Z>NXNr&JFGѡQЪh5 ~ ^ JBBTTJBT &B+.u2: hy;ŵG7ѧK/- <\9G8W QT.sxtpZ?a4v!= ,$h-w c\<\xtӦMcڴiYfqmW䮓L8DZ4*U 4 jRVDR/J^Cԫ5FF](M(ւJO&s޽F\J#YKüm_04co\1N"55fFosW>yiѯ;'aôx)(xM.I`$X4e"##c/FhV<CPs~~`Ԇ{)5fT*dž=wrxiݻ=$"۪;6v@MyiXbc{}MAM\h!Q~^nY69YȾ}/P]p3j%5u:Zm+iކ xwYjqi;T*8B q@Sxn{!5"j%XeXXXԹjsasas۱nv ۍbxqx|8>^8`}hˁ=y<)9LTL&u*RB>0WRmĨ51cИZхF EP&qlXBׁOJJ>daaIOA ȑ#߿?\pqqTTTB_yTZ,! XBN=NjZ{ ն"eXQ]u0'ABH&}8q JoÄ%'wd%I*Ea*뇺{(u*uIMdP?2&6~k4:MGaz}^Fkޥʚw" e7tmT ^bɼp͓!-E_EAݻlB4LǑoyx9Sew]LHHiiobc7w\VZEvv ^Kk eXH;BN|VP{(%XeX:{' eӊm'Nn'v{}BO*}8I_ tzOmBi/INIBR4N&NI:ikո1UJ ρI !pHB Qh!RG4;iYpTu6rP^eUj[<hxllse89|>TZFж}ϧpnwYjfÆ ړ_K}vnYguBwj1TW TTTv lјFm ԁpwm Rk/(,^N:W5uZBdȫDžqJ`2%yM(+n&os ^yWEm JsHQ=Fƈ^mĨ ?i&D"1"ШC«h΋NbCJ.71=-NVaC$x⤹뫱tLsnFsNP{#117PYWp8^6o&vFӶٜlZjjjN|%s'U'&D:sk`uRc/qQkUC殥Ym<;vc;Ch0 txߩJ(ס @)_UT}UKWs` &]WxULqh勝_c1_C4ispnLAFtqR|>ں9?iQ)Cxxjk73mk*e׮ٳGr(O<ׯg߾},\t:ꫯx8p ݻwh4ڵ"o{Gyyy裏xL&Shjі4I;-gwVSm+^Y]+x{?Pq_'v:gu.[ҟLC^^J?2 SCiivW3o7}>EEPT4𾤧@o'A; ~k5B4O4pIK'^gc|_'bmEXy*_2O(\Vn;6W}BqנO 9|WT'nH UZ! U}B6֯C хcԄj¨DҜB:Mn"52Us U^;YH~ey*5VQێzkO}/eâ\zMj_~$Og%cC>ոF0a>HaỸMBTT $xB}6-yz=% *_2sVPꨮZjO(6~kv9 =~ _G~[Y l Jk7b* RZBRQQhTZ4* یŤŠ1aІ7cԅSh4Tj JUTW{;oF76&ՑT')QQ t4ihbhڶUƧSNl߾ē7Lrr2=zK/O`ںiܿ~:GufЩu$:Qj/YC騪/r`ȫ檯qcw' *̡lڴ ~-`ڴix1e<|n&O6P~ӏ0Y\4]ADj(#iD.DDnD^԰96$j63y88'q o1{pݓ` EC3wa7&\ݒvݜ?iwX yN8&%iIDAT*xsq!AxEp^F<#ݲŶov~vxrpQ(D8C8+m6FGL4!0֦ mmmӧcØ;w.`MƀG~1<&`+(Q`˖Nk^Dn[ 0㘳p'$% Y!2T]PtAUM=4GѐGCZJP4@SoZ>9S<pYvԎ.s3Y?LO J7|N7 X(FЈy3bK/&""#,u ,uzʨAL]@TUH I d=ECP 4DEqAE,?7vO2F J&9ҜAiPsB? |>l7X|Wq[۷oǼy5f̘{ZׯXMKG"JĪ'?pG4Ł(S5alӉ܅|*qU4G$M#Zk<ՔǨDFS-K/ t-%;Ba?2520(`iwu8 4Cۜ?ity9~Wb%CrEծp7oL\;?tI8ԬAuj::lç^&< x!b˖쿺w>#}A0sAcf0哇v<'_a˸(Dy(C)ZVDvs\iDnD.D3F4gPj*i3(u A2ՔURiRMy@?LG] L qcN1Gx~^!^g"ܥؐQp n?YApwƦwU%g9PQ3AQڭϋOrc̘GQYy{^=wUW(f\˲̀G/>oOyWAORd:ߠgCI ()e#$h%G GBu=yv{n궵s5}&L`# ,-X7{ƇsQ(y\ PV8iHO{;O!t!*ATAL3F!j"$M4w)˫12DfLN ITf+P @(S8/jܝLP^f!OrRpو'5 Q<'#9`}8vA?(㟀 ryޝoԔYt:q-0@9޸ q ] 'gt]Z#Cbr';S{q}w(\7:.0p8sspYav#H`іEx;;v<<ۍ.rِ1ŜA&k(eG]S2u5ՔGgo픸@?L]¤*:Ikd ^!G+JgRڡ:K @*ń ])tumC"![뮮?OFALսrN<7i$ ?8}]zظq#0壷e@96(芬?Sf!]g{!jaĔD- @T Y]TWIOP7ɖdl0Oekj8N͟|Ik)b8yQć/GߋW:t~BC|S0qs(.Ѱ}.hllgϞ=oٲk֬:[p: xDDDDOsFpp (GS>$9Q a QQ1džČOa]}}Fl۶ /:::PYYn WFYYH$ Q~h8ـ/MG >4K#0MW;fΠ<|1(D՜AfT'@ (J5ҁOocsoI>TlE\8tk3^.)-))%Kp}7Qk^6ۈ{g˅AQ5lӊ>QӘA)5J92Ր'yU4ʺIͱ!FLܝƬZ47GgKG"!7vG.]k~}dYơCbϞ=lXp!6l؀:<"""|M=#Ml*hа}b bR"ِGAT9cΠ F@JpwD-9oߎn~װtso"ʕ+<׋_-b#"""7ǔ 6YӪqa(?E-?|͛ݻg/G<#a;a,\v-{1#lhnnFee9_4\y8|0c}0uT6lBDDDVJ`崕 w~`sm2-_,_;/Bzq * 뮻j*Xz5}6v """m[mŻ_C+xG(DÌ$Ihhh̙s^_gƌ;v@gg'Q>z?º~gP. 3{(| xDDDD`î h.Mn Cߝo1Z kr#ʁW?< E!555uٓqP[[k]s(A,]Nb͸kz{QN{p8`rGD~`h-mP xGk}{H$ގ[b;w.N:blڴ H$Q$ GϱjժvmXlB<|>Ə#"3f dYF"@<7BWD"{mxɈVZZztM[zGDDy)#M<\""_}t]t: PRR2Ν;3,XE!"!SWWwI5xDDW;ٳ(DD4b&啉'ѣÁ.pahD卶6+W\pGDD xDDD3(O| xDDĀGDDwl61ӧOIPZZʅ!""<""\w(G8蜈@oo/BƎ1h式?WΙ3BDD xDDDΉ(O7Xad#"FQTT]UUUhnnш<""i|`֬Y\""b#""E`rRXGDD9K$AQ@YY:::0DD4bqr޽{p\""b#""EGDDĀGDDyl6ш<""IX PXX. h#"k.+^E!""<.D"p8^zZ[[34R6f `PK)7PK|_:ObjectReplacements/Object 4 s qcd0dFidyn#% J(5(U 'dYYL &@!$(27)?g9#tjf%`f,Fv f ") 5yZ fg{b D:̚{Pxs ;H3d++&!:&J\1щ6 J#;āt&3,R1jibtc-3r eǨ,R:l0Q-ؚ9t>RObe`9vC,Cl@\Nâ5uFWN3tFٱ&Tvl#saH 1l=\PKvfPK|_:ObjectReplacements/Object 5 s qcd0dFid00Lda`hrBL@F KI/ @US,PiP՜p} ]@`$K+ss6+b 5?30j#C;]цdqPf?=h190K ZZ1x9X3uLNHG0B ddwZN@E 2:Ά;pE',O` A,6:]Ya鄈N',Nk-HDt:PQTHS)zN|0`)5CA46H`d.;͚ZN(ٰ2E%"STL!1˸ ѩ=ʸl2)qPѭ'hGj͇g lNDPK#˕ PK|_:ObjectReplacements/Object 6 s qcd0dFidꀜn#% (5(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T [#HiRLHu_*:A&Rp9p6 \X\0pW1*) `s@;̟yZ!6It2hX:s wNZISPcfx0 q 8P(R<:s r|:Z9lׁu{NKyXiirEfsQ=iFd TLib``aq4eJnDh~=E%;hKxK4l K,PKM PK|_: content.xml[n6ߧhʲ,I܉L t2bHV"$/> {HJlɎd s!x.t$0Km Ӏ.w7/sXOBd n*&f80$P`1\*D23B.울-lˬhxm{͚rhˬhUe1z"I6vq=2b-3׻pjᠤK3k0pq2z=-h,Q)hb4HWݬ5"f[Lo M^?l^?&Hη}۫xV]3UI+C׌+ԋ N$`'y⠴8Kt S0- !0 \\p~qVqbP!]Y&"8.S4O1' (ð0`*N*grJdi [ND q3siˌV.װŶWJHX\ I wmw8~  ڠ+2\Sl`>+ k:x1]X!Q}$D[DȲxeh[XG,ACsAm[¡mQ&A$`+uLG3y\'?!,\څpU8)D%HV3yB!֬e4/^Pb\`2MΕL"9urbw$Q{'e DLtؙ" P.y/Tl?*G}ɸ mD `+ [&$2d/dXsb&u^q- s70v\{.4}wn'Z4~]>Z6m将dC!J,uߩ5JKR{s! '0NWcf ǂa@~mI%ϩPLqVF31o?CIW$ƌ;Ϳ"I ;,E:Eh$!a_~b`9p +=ۄa1p(޲s+28.^ H mnmUUɖK OQxRXf1M:1iY Gla듖 HctX&u:~6Ə/jOaYCa7EChݵ[mnXV}  y,M=}M- ZГ E yzt@3]Q5 njOB8G*~L3}5#V tҳy K?k\ւ}}`n~o4PZC?N?[9.Lf3ot o1''8,P 3$kt^F`mQ+#O8"K!RczA C ㇿ`F:kE}~{O#hr?:'V~ț_PELXNtmA㦠;MSq ^aӵwꞠ1bo~( åřoq0kb\*}k"]Efr׶#aԺ#mI~ ?O"~͌z^GЙwސ.:6? c!ȯiZ^̇APK|1PK|_: styles.xml[]6~)XLQ"53L*UY'DB`haOklnG2GTMb FF)#悰>Lfq"e)vegvKRXZ帐GE…X}Xb͐ b]L׬ąZk=ʦkf_Z'9UXdfٗ̚8:LVS_|˦ ? mYDtx>KY0;͓j5++N5W1j21OfX)^ߤ7Ov xM݈k=cC3ɦ{9Lo&x>u.rUI9yۗgզ*A8$ǞdOMk|í#*L/m%Q[iRe8ᕉz80w|h@9c()LGĉ Ňb`u`kbp9rlӾŁqod(x |(8x3#B"<<`VMtXqs:+3ElMqTI:^;*9l:. :b2v@2zM vq#Qh8LGT.'i2T=-q r@ʃDi1$J2503smņc'ti9D}Hy$7( EUVVUp5PmЬ*Lٕͮjl2 ?]pU VD Ù =% {9|. 9wqHWQoc}-bx*OqvL#2l^*?6? wY8)̃m=\?nXvt%EǨ|0PRas}Nn}W8l_lX2؆IV87~}`y)ᒥ*1 C<{5!BQ'^5g'?6[3D?[A2ʖ#H:φYd9>(\ %&GpR6Fʑe Fk-HA>n'1P@e 7A,sV^_is? 0d8ЋCD^> 4"|9B- iVׇkt̀8:]O`? x2~Qg"D^jv״cS9lqŞS=[N6ƦaVUM׻Q4S8 ׹МUan+Xq1W~ǝK3c{RhQ<,ZiR= .89L}͑a;6G}6y]6ަHGf[?&]k3H;f5#؄xV\e^7=z#ix=6x^QID>|=Ҵz(J1i!pqjʐreNl bUp{=޸3fNE)3=YT,T©+SHV$?/~ Q/QfųzP/<,p0z11tИmG0ո3R&@VG %BZn z TprO=fZ\]7WjuwvR^^]+kuv%_oX[[Z3gKv݃{.ች'+V*;ERWy_곽۩M٘d@^y`h˾y裿VMQ2񂪍,)/HUN^5imc&uP9XYлz3Ge<[K"q G+b5{9Y;'#(Ȇ#"{HU խ5k1 FxK> Q\7405!B",h  @ [ljb\"`OP _xTs@[>jYF¿0!  xpڢ d[@uKOϙ(5N[tΉ;-'t>U 2:1{>JqJ;W P-D_UU9waIi؋^%8;Z0mCdMExQ/,ɐZ$vBPKF~PK|_:Object 21/settings.xmlQo0)B5*(}6^_d;&2RF=!~?/u vf[<)[W9ыC`Bjt&vێ9s*\FB\\Y8rոDp귷n:C6SQ}"@zBvU5mm MqHwZ[@sA%:6vm+Fa79j|bdw{P&Nd-nusi 2n٬3bu&tEYr!D tt+ȕgG< HM_ipD|g},a@ڗ\y a!*x\STNG SJ+k!a4hapbٝ9 & $ g3O%aVŨMB, aq8S:SaLpچ̋Jbg4v ikQ ]3͊HFUMbTD0a ٣<>3 '+ÒRK2B MyQVك c >݀?Uu9ϯ6. kקBe%>ƷBLo3NhPrY2̐4*@0Tf*5=“|[d;Ze!eRpN,[ReCtLWET{dD$K XO8 a6I0  2FD=C!+Өi(iQSRM)R6ƈP;i]U$l]3 F0 ڱw(2F)Fg<| p}'PKI!RiPK|_:Object 1/content.xml]k0+\ {&2t`0eJ6@Hs~hM8!yg$&_:4TȰH\Mj,-?<Aty)"|`W!KoPK09PK|_:Object 1/settings.xmlQo0)N#*(}6^_d;gB$)#}ϗsҺ[\Pdm~]-`-}z!0y%@J5DXj:nrmǜH.#!Wz.Fihw K^Rujuj\#_8flC=ds8>D!'$la7g'o[;'BS;iR\Q ]k QXfg{γ@ʤݩS [\0B}̲[o֚LXf|ۨgθW5K.ĜHx݀.#edf(R7F)1,q eX?5%Wa;}EckBFȰ 6rJJ<24YvN!@ݙ@@i@z1^fS&t238)O* 3QpyQI&ޞa4tM3*#ՠkY IHF57Q!b5bbA{y\R2zIF)/ {! Gl Sj \*|lc `v}*$aމ$y@ mީ%4`:pTҐl)?"P'/|Tr M1,m|ZE(['p2ơpT;1y"[_;nbt+';IPK _QiPK|_:Object 2/content.xml]K0\͋8v Fl5&vI+M8!y4!,S\ F=&rqrN`ط˭ۭHJu<;x'q{fbE<`NlJFYH^>͚ٔT+!v,{.>D!'$la7ڭ۝4NkH~P]֮mt(Q=Yr SDeZNV0̾P_.֛r],s}ܨιW5K.ĜHx݀.#edn(R7F)1,q eX?5%Wa;}EckBFȰ 6v0 Ȕd=cHe8 n zoavgN Ib{IUN1f)thcTg,M=hfTF0AL"Qzjo@B(j$/ #tlj Te uS^ԁk`C$,*.UenTH¼$/m S?#\,i|u:=RfHC^S~DN^w*[^SD^I -2g=2ؐ2x)8'M C2!:"YJ2\v'LxF'70Tz DD q|[ uI#!{ iT0jr})&j)bcDY(iН.*Q.O6 e4C+v,$|cEFvH#]0G%rbdf(ӍQJ /(˶\05" kGٱ}UXv~&!2u8%~% t~24YOFV AEM} nω/4/O=Syz/ J>(Fl:aN {z֩ cB6`^T;gM;]ӌʈf5iVDL0Bo"bQ hT%_QAL_=Cxo KJZB/g`n:z`6@2!7Jyvw>1_u[0/e/u1?1P>:=BeMlN'87a4U9G~e5;UAT"uF* (לsbޑz0L/SS*ګ8#%YjzMixѨ$Eg8$.&R`+J_K Y?,WU%OV5VBIڧjN)6B ݙO벨<exLRF(0Xbj79ahtkǍ4 <I} pN}'iPK8TiPK|_:Object 4/content.xmlj0gA/l2O[`S2" Դ=d/ÞdծP|; 'rinÂz>sJu`|XEML> s"qw*b.( / _xTRA&}Լ ¿鈮S.$GϢ3K* b3ve|"8n'xsb=F*Npk:> Y *%s?U48kj9Ͽjᐊp" mF .gAC :Z5`#kX餜sq-_{ /PKҏi[PK|_:Object 4/settings.xmlQo0)VkTPV hl^_d;gB )K=!~?/y|Zvf[]0G%rqX(D#2UA(%Ce[.PO#]ؾ*vKd VN:p:?PU'XY# +Ӡmq>fPЗħ)FvCOJP7kUD-dr?KLrʊ&#/jPVʩ`F%Xo]y=0JJvA/PKfă PK|_:Object 5/settings.xmlo0WDyoÏZ#jHM@g")~6!$"srNZwހ mׯk9e˶4}ju>ps ",5 7ݶ#\$ +=C`4xW6>em{%e:z^7/ -RT<;5jN߶Nnw8$;= kYֶ}>DΚg5ˁL1;r;ISk9i [B0Brel4˙t^ͶqQ/_8*e>]2'rrXd23FA(%#—e[.PۏԾ*vsdMVຜp8RUGX[c +Ӡ]if>fwP0ħ)F* )sbޓ0L/Ss*ګ8#%Yjf̈́ixѨ$Eg$$.&R`kJ_K ܣ,WNЇQ++$EÇOI5 'WK#\@I§uYTuy f(A,^1Gc[04F)^q}>8>tPK>)pUjPK|_:Object 6/content.xmlj0Y`t TVNa$kSIiuVÞd/ؓv]즜|$juLp 6K(7RML=OtVDڝLPĥ|bY̠g}0&8G 7BwŔ&Ԡ-A(%vv$ROWKf)A??&áRs3>ӈu_zTZq| Ɗe1B{G e=: @џfʪ_ͺ{S-eA.q`|/J" fpL}>?O krp# IdI!8\Z%\+. g8tٰm8M?i4qfC~7PKRŽ|PK|_:Object 6/settings.xmlr0' 2iHr-jlG+aL(>bW zen7oYۮ_l sʖmqzվ|hbA=pE0y%@J5DXj:n|mGH.#Wz.ihwl|^Juz}n\#_:f&C=d <>F!'`7G'o[{'BS;I\Q ]k RXfg{Γ@ ʤݩa! }sʲRo3rf=tEY|!D tt+Vȕ9gG#HL\h`Dl]$aO70?/ /Y„5BU\SWN9)Uz5ƀp @? ΂J3ň0bT.а(@Ma0&8ODmÙEű3xѴ5ͨ`VfE*&1*"p؀FQH]G|~iIB@% MyQV]؃ @R-r]ŝmW]+׮O$Kً2?O61P:䲦I6W0C#utSyز *ml:#hs9lrI}`Pad*ΈpIAD3q na6I0 2FD=C6wG+Өn(IQ{SRM)R6ƈP;i]U$l]l#hWQX@ 1 n}Fx8s#;NwPKSiPK|_:!_CCmeta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-93102006-10-11T17:42:46Nicolas2009-07-05T13:59:56fr-FR42PT3H2M44SPK|_:Thumbnails/thumbnail.pngUTO EBB'khqi"]R,xPw(Bq)ŝ@,h܇ù|ZY3{ޟhII_)a0Ob+XXܡ 9\*Jv} ?%QM7U2ßmZE7wpU]5ME%;bsϺ{nDjtޕHǸ_Z4ҟ%0PE*GL<(7ٝ%g菫PM`4htzB(;83m)A^;1K=P`>4M4v3zfv*a|prV;y iNᢠ?jn-wr]Q@4'~}xv4l!|do śX>6Zo+uFgkyS =fdtE7A|LkFX82np$gk&5.a=x Cd\ ^,v.Mz2RVK'Ce Qeo[;vW*OOIB_Dpbg5o]kFRJzkF@]jWpzB U r#]h X$2;&=#C-<0eN_%OCH:L} y,/H/'{}R#dszl.*/_i~NSn@WCV0ktt%IDʦt4懚ńNAv/CЛ6(dKҴţky28gGC#K%k%`0f=EL,N#qFa3ȺYBw5M+N!)`]I6C<UW^[ bub9]uu3a= Of|FsuH34:V}+pViy)2 \c;e!x>@6VyظxnF $ cgYďDo!މ]7ۣVt*|8R:#1ڷz>c:*rt'aF6tqkn]tVˊ-fwF7!Azh 6Z䖖yK bc4P #JT S2EEљҬ uL$?ÎeQH:sC QvtLz`$ :yk+aL ?Ȅ"&T,_sp~FQQ RA(D/+E2|³GY\||GA. ch_-䔔`+A)zS;&xz=mERK TT0n_׋)A&A+\#YEid[倠НרXMŷ3=eI+dؾ}y=(qVg%~~SuxE6 nJʹY"+jAa|"+٢"]vYG<ƃ+bI`ƔԐ]:9֠cǿ,&g7ُrʹ|QHݐXAPqT6kD_Z;}1<A X}5T|~@p*Bh k -%ɞS9󺦾ĥSAzf/P)OOqyRx8*JHeٕa &35p1f\obp_vDL[.SJ: Zr("/]kEV@vck7|H({R״}4~Jʋi'Ltck s*, (@"73VorzPJOwh_o'UlhȥZSZ1,g(R> =O0p=`f$)<aQ? e8U2(V%|{^j4|/k\Qsq^|ٵ2zs(6~T!]ǢL+ͷ$FKTjsw!torzf7]%V'SȖ0S*U%̏ϴ`,[ {³. |ఏQC^z@>Fkk_˒ b~V t2b=t{ըEf2zB-1}9f7a&ݱ|J7’DG)n' bD;)gyt_1J_A:ي^C _68gm`,I0@Q_chۮz|y(pdbW*7PhMǨ(t7@1Φx"UƟQq2,p^NWͣ$SψGlHsu{4aOV;n +U4z'?'ܖlc![POSn}B]'Fo>{)bnJ---?u#mqOOێ5 ֺ [&0:@6/Բ4Zȼ*"7yjC񯴗"t96x\0PEłwgI{K]X[&'C5Hf|CpW>, lk6dćkyh䈧 ;n)43`G7=REwA^pfVA[$'Wԟr$14ܳ迆N+Mے=g}!w{/?x,2Vk ;LTm 83zޮY"~yYHϋi A/a"[Ee( I+Eiaqq^ili5.im{-_fR4|| C>o㞺ntF8U}͝mJ\wҢGSCxW8 @]*]6y5]Jzs [jw"x:YBySZmd|3(,+GBVOxf mxP> Msmb+IC-$-No˸Y:-.>]>ƻ8.>Sфm҈=>K1PPN 9޽+:7aH!yu>\Tk'5bۦ;+TNhe1,tJT}<[tMΏgK =^[-{HMW\ʸ~A!],2x~(Xh7@Dp Ə\ .QM2it%{k0$A-ɻ\lݻZk1uW?Dyڢ--\;YV @U %=i]`UBܦ!ϔ M`Y/{p輠B#¶%Nd8阀1Pϰ\.Vz^ r{b/~W]jx$lG6!} B7ݳQ{,Nnr['o_#Hmpí."A+i7-(,EHISN&+TYA9L$Noh8O$Pb>|`2 )^] n,CM9=ӥd8SCRɩNڡE[Ovl{41[}~GyctrlDo E 5ЊuQ_gnU'& f7)6p]y$hoVě5(w;W^rdO(z4r/`3 ,[C%}gp^}ZԸ]cy{ꂔOKCQvo^!ޥP_~Jh}FЄh-VW@KުC: h4/ 2pul(L@ \ ry2nk޲ {}\C&*[ft܇٥6I/l=l+FI9*-@~_;==RxemD/ $Tckj'_IZS.j ZQp/;miKi@r CjGysFBoK%Gi,< ph.@D[uH [7a{]k W2r-,Ca5tj-.E]!}KZqḙ<^TY5t2`7ĸMfi_$@EQ]>Y2$CDkiw[nh>RjYytx+𯮻S%&@,]0|MxԢ@9xW=V:RF4XNFv\Ad0aǦXoːoοm4p#A>[̒OD * }h@o0]C=.:_CG"|rAh1s-Pz"E{J;vy$=ILߨ2ՆFXDxZvZ/L^Ж=4Oug"@^ˆ/ڃ,6 Rq@Itziq al5+.i/o3qEM<+' C&ԧFgF; Md.}0j i6fm~[ ɴr NkA囝l|F̿7?^h ?⽅Ká&ϝ>zgMEHeu3˯*5ȒCt{ܫە滕cۅ%}dG(¿>v]nc5"a$Ԡط1A#-NINHe B{܆`0$o -zjd}Ѽsprx,+D1{~@1ַ7߸TgO-xS;N0?b6Fi.ۧrD '=Lw$r 9#ȁOP8~3ZN{v@ ᨦmC >h ~=t0wE*1uԖ"Vc}g2Bi{fXUtLϺ+Qp;yVmJP"e5*SqėB\n \)TgB5 w# 'yR5ʪE]7KcX/Ph{$\̗Q(dv {1B ȈW-Tq-R- 2.q(5*4UXZWKg<"5KeI$q 5{V ^(J acb⟸(7Q'S(+%+Z #^ԁr ^GHi(7ᵞiN|1[,]I x_Y8pY2S.{ _rk J_ec8z:n3}?6<(Yބ`.ߎ+k[T*(b;ir!RK7ӎS:>ިO_F1JU>)]xyJSJdIzPnZMiٽn,SH`҇DN0AS໛+Rz>ΟdGnk{}14:Ey(&"J[eTcjPj :$qt;Ӆܾ_IfSrc![~w13_%I8X@%I( ÌL?lL{'qo'1W&@4A˘q(l{_tn$Q #3.fBE澾 L.2'^8N.sXnHe;)K/*=):8 vőr`;^4SsuMM0+s2¯y=O>uʭD:j^؄f/z;k *^=HA.g֙~| lxl(mg(W,5OJYT½b#>QfFxsPmm{856F'A%xd٤XU#|>rFO/τR&fRe=On3Nm[Yl<={&Ya!11_i+H_Cv/Ԇ9w\a?3s=aD/Zǩ-X݃R" @@Kˏ uT P< PЀl+,rര"1>lzE={< ɇ~RR>PK1%'PK|_: settings.xmlZ]S";}šwJ)[^V P[i אJ2v2@|@Й;>$pm6'4GyOK c.GwKQUGP1J' MA14Dt+ ULs]lb & *GWLp~I*t:=777Ev94B9}]e?Bĕ#;![svV*]"?TyX_]8> d.. Uւm~JCI|c rid-m L،>?:;?G /\\3 1L@9 `2АISN5:(?pCeMP]0I PQL.`qY{8$2,URx; Q3#΀iqh}dtֻX(:V05ը'# /!󠶖bo:qM0)LDpWRqvsN|k!bbЏV-4^]Am0L("/2L1Ѡa7߰2gfQmY}u]M^C#wIuRW$o`AͿrw'^PF)bGRM-0*/xM%&Upش=Oegp;TY)}|8 6;11>̛bI[RZ}R m/"Zb䋠5cnpO~sq8~1 Mϐ>;~V!-,È.Q#clUvR̾|4K(N:֙@x_&-jwYStjÇs6)5E.X}̮'lfWGT&EAQA䊉.i|55X('ĆBH!+IlC/" WuԛhoFW 'QR .'ztZ@GWDa/c̎|bf `fuyLJOQ Ġ $-H^ZSMAJL5}``ez^+',%e8|)QWa[Qh8p`{dpgz]/R/ӝP\'ʪb&5\ ~4w _wϟ|uӃ-RߩP_ zz۳~wܯyNZGNeGO m u]E pLℳ_p1. Qutg&OY]tYt@7..<Iht+[6K7x3ӘPK9 1PK|_:^2 ''mimetypePK|_:MConfigurations2/statusbar/PK|_:'Configurations2/accelerator/current.xmlPK|_:Configurations2/floater/PK|_:Configurations2/popupmenu/PK|_:JConfigurations2/progressbar/PK|_:Configurations2/menubar/PK|_:Configurations2/toolbar/PK|_:Configurations2/images/Bitmaps/PK|_:C--Pictures/100002010000037800000185358AF3AF.pngPK|_:| ObjectReplacements/Object 21PK|_:*gWObjectReplacements/Object 1PK|_:c+gʘObjectReplacements/Object 2PK|_:)7>ObjectReplacements/Object 3PK|_:vfObjectReplacements/Object 4PK|_:#˕ }ObjectReplacements/Object 5PK|_:M bjectReplacements/Object 6PK|_:|1 Dcontent.xmlPK|_:1 styles.xmlPK|_:F~Object 21/content.xmlPK|_:I!RiwObject 21/settings.xmlPK|_:09 Object 1/content.xmlPK|_: _QiObject 1/settings.xmlPK|_:X'9Object 2/content.xmlPK|_:39:NQiObject 2/settings.xmlPK|_:n9f+Object 3/content.xmlPK|_:8TiObject 3/settings.xmlPK|_:ҏi[jObject 4/content.xmlPK|_: $UiBObject 4/settings.xmlPK|_:fă Object 5/content.xmlPK|_:>)pUjObject 5/settings.xmlPK|_:RŽ|8Object 6/content.xmlPK|_:SiObject 6/settings.xmlPK|_:!_CCmeta.xmlPK|_:1%'Thumbnails/thumbnail.pngPK|_: *P *settings.xmlPK|_:9 1~META-INF/manifest.xmlPK%% wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/triangle_rectangle.png0000644000175000017500000011143112014170666030107 0ustar georgeskgeorgeskPNG  IHDRxQsRGBbKGD pHYsnu>tIME ؋ IDATxwtTeLz'!$2Ek[bG@uYemtX˲.HPd{O&#F])ɛyd 06rgڵ \.QQQ <.#Fu￟\+L9,&<ϴiӘ;w.%%%$&&r3d 3w\5kÆ k0a'NS""""'r,OȐ!Cx:tO^vs3eOM7?c=db͚5tI WDDDDTD>1cƐC@@'Ofر}?̘1QFkHee%E f?8<a˻KFF/NSSiii8{XB!a3+_rqcY;wg*dQ9<W]u ,,>_.gEDDDDOh7n/d21oj׿MFMM 6SN9E `wߍbQ""""'r4 F?+~~~d/^g9ƍǘ1ckv/Rb(7tٿh˖-3`w} UDDDDڔ0 C1˗?rl6< z+fOdǎ>٬7EDDDc+X锊rZ3[~juͻU7@v Qa gݭ_%~}Aӹ)sdK3<ߒO}dBC,i$?:Wm45+TQ9֪toGF\dbp} .'׼(MacXId䥘L?ؚ%YgP_k(.^EDDDDOh[| 7=Ojx"+3q4`2Y%%9|}GINx<""""'ʩuynuŋjynn?& WWt{b;xtg ȑص崎Vk$aagBz/r9#g2 G>/ӿvCi 7w YYl>r)""""rLD)x,Έ?m0 "'i7'13EDDDDOرbɶ `Ge@y޽r{56:əHM:}Ez6""""'ҡ]ә @HoIKҦwy\\^. VGJxfssPQ|Ȭs痠;XDDDDTkX͗_̀˰b77rX%Iq5nGV] ̢t O|P`grlQmo 0p{tdc5,Kп[G&/m.PTJ뱄?Bju;]DDDDT{,\ }TfAm C\ONu _ǯMon**V"MMy^>>$%[ t7NUU6I1+3t*}ʙIXpL0݌4 ;- ƑV<ؚY&huSS'4c(Bmsqs1/}?a99z{[&ݻ_CjT """"'϶mQZ>>Q 1o!t UUcCfDDOz,zjQ8$ """"'Ҿ}b &#cQhᚳu^5 eT 5 )*]5NjT"#N#"""'x<0 '=zLlzMX; kg`5[Xpɳ K>6ϟASS׼Cr'@""""*x"Ǿ}oC-;ɗOX|2 WMb-+y[}Y6b'<סfdz46XB1ڙDDDDTD0*uĿӻ];1.q8&Q[7}ZS{Y}oS₺b5Hv\Z^] /ii?K; ihL:c؂1֕pn),YlbQ7ee?"y-It~v>K.wt)qJ}}HIyyEDDDTDq  Y@-#g30AJlrstx[$&hM<yhYqOX5]\nV HVG6A]v5o6{3zMliQ*++Q$$nc}s#, 94=NtXys3cu%ӱZôsIW?;o>0 K0\AaK\e^~~ɤ>GnhQfݺ4wtv.s7ngP/e-ECE'O"!!jM< jjiӠq~KR9W9f {OOina~ ;@n#9 f""""*xQm8ڍѷdo1ArYwsot!c"55^yӇѤ t$9[wey!}Tx367Wp0ˇxi~sLNѿnEEU\ y?T^ 2|=xDDDDT?R\/p22V*7׾Kw /FO޲JԔ5ӝGI; czݯV0?r2qv,@3;+FϣgpL-+o~4 -`ƑVQiϞq8&`6i8[[vooG$9Du3/L&ݻciNp U(|H,1@.YFnd*+?\̚jqr䂅7R0`6l&ᢰ_>s H[+)y/?02Vk9r. g_4 7ee?ByIIO{v"Qi+k&t 8$ӧ)_ݟrͲ{-|䷷s׉*.-EϠf346XBf&"""*x"VUz6o>uܯ[T0«[_ p]LKW%&:Ҧ`6),Q6lD]ЧϿxv<"fMfxY Ngd!"#/nDDDDOp44{W `a ii ?Ca J#8g ;Sfo}kɊQH?0ܔ.`6^6[ORR&ӽU KDDDTD}hJJ`FBicu \X6lWXOVGt`EϠz s+N|_G+o tmOYY R F- @FwEo9Lߐ;mO72I+o tMvI^ޔ/˟hʭ)d؂1pF qY}/t3xi%*2fbE(,Q@ѳ (l/@EcWOb(_e(/h:)3DDDDO:{[v6YX,Vs l,o/sл9)*KIݵ^HOEh) KDDDTsΎ* ""V(Ї2|o`(#`.JK"?..W׼o"))}<<7dfbbڢo:|2h9硲2q:sHN\ܟ򦈈IǷvmb ߐHK{^'37/ྏnycbޅ3y nD}^sfsc f\ >ca$jj6c+oFG_MZT%"""*xryXDt_n}.d`6ǒYF^3TV0\̚8TX"""'G!++hGYtYM\&n| OL&9\SVO|p"88Sa ?޽[v*,,@?s?c~r9NZV|Wq*HM}Io}yIIJ7'ZӇ=Xע硼|ijbHJz1 KDDDTʷ~NOXLx-¿/̅)g*^ jj6p -Fao~=OtMfF}[ @L`7VGRhig\:^|OO󄄜>~ KDDDO~ƍ}ا$+t7sp;)a =W)*7nw׼oӧ;%""'r/8wwu 5TrY޼sLB|N-znʖ?"y-^&%""'MMEYӃxHKIH rT2lj[™CxN.z՟,y[,KBZySDDDO[/l>>8](@Ec5KY'vpL+WGIM}OaIWrՓHJz L>+.~c,< g+&ItNgd[e[KHOMaIWc iJt!+~i)rr`\rsR^pb! DDDTxͅCJ$"<.~)a=DI8yLz-9dJTMfPX"""*x֭|0p ||eK`MfBzr?p:SwU;0CvsHQX"""*xҞ[Nc.cR(r.\tJ[RxoBN+zNP\:O|`` """*xlaӦA~/^R-þ<N+c5;#pSR\e^I>KTe KDDDOڋOvi"?iOE@I}9:_>ECEjtxt#!Anʛ"""*xr<56n]qz<3-E;7Sgyc2N'W[44|5g67'1UEDDT x,S(rXV;6r;hr70?V0]DCC$jj6pʛ&QQHOժc4EDDTpjm}q K=TpT( =+.WΠd O|P`grQ l=U\ܝ\`Wn.O d?ᡢbyySij&)ݢDDDTHeev}5w)9"Øg+ Qs-Ut_[ivz[,G򦈈 /{-;JFF#nf[a$!3ML&א65HahK?^P(fjl*@jx"+Fͥ[@‘?4pL uȬIO_O%""'?d)۷_㣿K*`]t˲+gl T8'?eeKx惃OnMP@%""'?fݺ^46=Dӧ+9*r z j[->;D]:_MA Ea)*zk?DDDOOW]/:) FoJph 0^Eȏ=?ByII{< QW;_Qy\X\NnSgߋdR8EϠf346XB1 LDDTih'W `lF}7n SooP0r]{7}ZRSj PX""']ǖ-PY -V(rLw[ϝc2P0rXΒ+o܇Z>-Za tn.W5a|^!M'<,%""*xyx<.0f#:z0 n[/ 'wGbPw"frSZhn.k11(,Qo}9AV+9\,kr0p3 =Ma /k&tr*iiSkn?>dR8ҦZV|9\^ {#G]cc99Yxj5Xa KDDTۼLZN@KtHfX\,Ò.WdzTT0JXDDDOkľS =UHd̜/_˲381v‘cXΦ <:Lل QX""'mk۶.jFF E:4 s-E1en__2ѭ% KDDTyCvv O#=zDlM F:r-=-+ӏe#fd P8r\54pLf#+o}ii/`(,Qm /&ldffA9|.ºRI<>ps*yʏ0 !fÇb/Aa \ "#/#)iBNk{n.Xt#}s.x QMP [I Od(`i礠`o{@z3 cdf~`S[KF ͙|=LnoʾcCV(t; Wڌa(.^Ha˭!?T^ 2|%""*xb͚xZVr =gt YKmx[1@z / gWc&OJ=+ϟFSS׼Ow#6F%""*x]YE*+P0e{2n1(odoU^jݛ!=ןĐ؟|PmAM8Oظkb gϿVh׷,8޽P˯)fCV緼;p.g/ԝc[F;ur54%'g"_6ltRS`),Q fÆ~}_?YHre6lc}Wl(F^m\nt Cz焘wW.55pLS{͈ IO_/Nz-~~B0 W緖[=\rhCz `OM|,2{+X\z_li9M(8$WX""*x*xYڵ1|ԙ`Cm)cW<'ǟ18wz'*f{ `Ũy+\0 Ea(*]5o'=}(,<,nD3pr"޶]^zDRkc}"{a1[~r͍\6>hywQ ꦀ=ee?ByZ%"'A@ˢIIyqsͲ{[Wӏp_*.Xtߔ`@T:G&WUHG,z}=^K(7fQfǎ(,s{0+C3t8jZ8ox*vD{M_bb'-Yf+"'!++p;=W0"?bW>-Jū'bŤs:q8&SU/ afVX""*xҞd%33K 3[kn2ux\:'$4zK@]aI{ֺZn#ILOΕok=='^C`p(,|pk63 ع# ea6aX w<Ɓ&u7*#ax(-}947{$9y11W+,<9֬ߑ2Y6/ޏjϹ1F@H',zkp85oWEDTx(+[.e)_5eZKch F:8ơ+oq#))򦈈 Kna'AAnPD~0 %XxʙУ‘N,$'g"k^yJT夥fPX""*xr4|ɦM}`D~%u>ۻVʊQsG&+\*rs} З >%"'GæM'QS?T[PDĈ7cqAYy UkQxo],./9n%rp렫x]L&#]U,+1Cf̈́>?D%"'?fϞp8&4Z<|Kc[oo`DIaLJJ{YDDT䇲Bpk1mosۊZ/ ;T`nRX2.W׼_/RSЭE KDDOrs{mFV l*c p Mf_4g)Ae*rsԔ5Eb#9Vau]}CssI"r^kZly 6~55[p8[99;HN~TQZJJf? f"#c #rx 7 Y@-Fa@T əHm&W޴}ii/w ^װn]*{ >CGn߾HpD~FSS9OSY p2k&"ڷ+ྒྷnU0"GTU} y!vll%"*x}r0 Fx}Y>u<{e(#r\z^|O/k 9}:V]DTٽnrslgР,"ҁܼ>~u<'Vi nw)*]5ߛiNa WнyB`l:O}O|V]:sNV0"m0<-'?E^6[zM & KDTq8cϞZ$lfp/> }$/o}@ =+6~UWNn46~5o hMQc/;; ߓ"A=n}^ ٻ(%) . 6?VW"])UD" "( ()I#O0$H$<}sy3y{]z,"Kp8v3u#M+oꉋT%XBIĩWT1[(Ơ$+k"[/V@Bh,nz!N TUp7!-M( B[+Bs>S>EvqZE^}Mڵ{B_ |ŋ}R IDATsHOc?99 Ҫ&::RSFH?G%%>ԑde}-Axog<뗍斳pW]u7qT?o ǟp$$ 7Tq}֛Y $XBfE7JPhEnĘ ?lO"8f=/Gݾ('g?w=Dr:yKǘ1S^ؽQgb۵6JJEAÆ 7j#qYY=RߠKy֬i/vL! ^sQV(߶!qs},;ЅKf L RSceȐ[N,[6nlݺ;?|&SHR^|q&W\qQKרڵ{nݾ'>~*cg֯ڵ锗KϽBfa·*ȫ$(BP(Iq|p $[_| }$i01? l|>:d<,pU #+kIIϢնinmgx ޖ !$;S<cbn!NZf֕qa2[%<@AM'ȍ|:?~Q̷ۃu>;XG&qս޽1=Su%HM^߱Qӹۇr{%,n99?isE4z^9mU 5a f^ȨQØ:?(7][[ǎ{.7~ϞOy|>fΜǖ-;yQƜafyts Nʛ;+=u0!$C^ޔvd#BaљdT[lC>E. 2s,?c~\}}~W\\h?33/=>[Fޜf`HSdf.b$Ul4SzO2x*T$&!N6(>5-EObg<^˖DG4`@og9͐VMJ$%*z ]Veem ZJOl{ l:BuYpTBJ_Yňeɼn $&<̈8۬]IצMT`[Pyϥ<;AޜfN!) #.^T*sV?n]?AE k *HJz*hjw?妛Fѹs{؛}ogڴ9yhFb:5EpbƬ|Uӌq#?~$wq% ^/mv$8XZpsx<Jp8XTZ㇓ ڍAi^W>c ! +vx0m2uC4z1cy 8-{3b1/sCؽ;Od!^UQ*SuK`Zna$yyEX,ރ>}nh҃R4R_$eϞz.C6Z)l %\LJTƋ;lv3Vo)$xxG`]{WVV#x䑉:-Ōp}֭W\w>d*C\ɞ=\qڕ{zL98^˗>r*qrcl޼OMz(z>u3C~_XXx<{ݛ/o^P$z ,t2NGHHWVt+ٳIV az(c}=m#CR~ЯpVXbb78ܳ{ɓd2RTTȑ1o83nz-O7?bs| L3LaŊu'|{Q))I<_`ݺMG;qc0^ >[o}_c2 2!!t61Nʛ^P6oT&$xH|jj~'&>٬omm`׮ Ǐ$% lbK,Y}r6nN~=` y_p')7 L3y _RRF^{)T*%ॗeٰa+Kf䷹;cJwP(Hax V_cƼʤI.o^jcHM}DE B6huSR2aÆ!")|>Opm؟%h4tY?Kׯ'r|bg^ۏw9ؓ7k';l޼H! Kb0](_8.)n׮Ok+AFF {nr;ԩyO(((!33ѣOqRX6%%sxjd6B  ի:@DDf|.] 7S(]z_jd߾oQC?z'_|O?BaN!=}k3Et,,!Z9KigtN׎ʲB y @rRYur3fj` SrخQRR@v6`ذr0 hUf_=st`m >AI]GV_yZv5pu덬ZInK2^IZCq6mnkwlݺ;?||[)~a_W\D r@LF~p]9\8! 9t4Kv]fժPv딠 ! ^w|>G~ђNe|[A|Xudgocɒx;`4uo/h+X( ! LѥB5:]z+Cزe(nwLIZWуQ*wvm`3j5|l{>ydW_ݛ; D3ǧ׾J 1x >WZFDD>Y*~mLIZix<5{*ڶmFrs 1s[`4yǝ_fE!13  AʛVrs_dJ#[ފ]-Bmժ wCQQ{tx*A.|ٓwIm-C6:9R.]#l(*BiiQ(ԁ6EQZNv90!$k~jjvjk kR9b0}Lu3C~_XXx<{ݛ/A@P0߿c/j]u\_쪐0!S-$''+bbTz5kM&$xڵx,t\T^JJ> _|u6;w2` r NGo1dD`9!Sy[n*זHp'DҒ8nVж탨akj/Y6$`B ,|trfώByybc1CHًR`Г־Yٳ?硇&k56z:JEII97e?~x<]kwpqa60gٸqƽΤI2l`9P2N6wJg!GE2_,hҮҡ3%Boٲ;b4Fѳ;RVVl`Ѷm Ujn0:JJJl'%bRR<H` NT!D73{>Oy_Ffr"mz~*N6Sݮoz&>>ÈqsX,^yÅh6/)(UԤ]CU%$xoϞYVJJp\|TǷہ,f8 B ?@P1ryEyl ٸ޽ZjjpǛ)ѡ]r3p`,))rg\UMnݤM Iı?? IJFcN~$y2'OL|>~7~Z#>?va+u[gC"Oj/ZjVcLnf:n!F=!ΰ伀պ7uis/T.B<'_…})+kN^o՞xR&s0|S,ؾ pnA税'vZ'epz^ڶm['NRS-Ņ~:gO%TWTeN99 |W 5 $5!gYBNVCZUDE]MZڛh1,! h3AAAGڴI!+k_O'F?w9?oaMhg;)_Zfgh[77ݺѣē{عSɮ]zvԳeSuȵta0 qVPV/^X,>1]%$~3o^&1X9r2'OjVw/m 5<7"+&)S;C:tORS$%%׺}֭Ӳz͛@޽d'>`P Dg㤨=4i73HMNxx/ ꫞ٳ"#r9?tN^۶1DJZi]}t;+pN.|qtL->'4̝|k+kO{Rw$wWtUjef3n}% ~or7iEڴ*FuuTTDPUMOvI5✘^BB 2O*ಏKz)hUc8 _` 8.9Yn{ػasLnS' BѿS?2&ju8OT&~{5۷/"??9=fڱc+z(7.]R B4{[JwrGwR>2f^JőO,)>tk'}^ݩL.اՌkyB4VVrs'bΡ7Jqqwұ$JKHג\6LE6t~11۷o7v-0TSzbMo ~n>8d O=bt7sΩ{/++k0 ƬYuk甃Df,d߾ TWHӥRDG&5  %;ظq1,\x1wގFc'*Jj !WW?/WT{yvCmX.h'RCnf1*,,孷,_~0Q50yr'D3vW2_ʛ%X"ƍ7Np?|}7*#Vk03OVWWayLL&!Bf/%B __ddVDDDѫQ1 ƍma'DsP(Nhhvk(V e·zh% ygٱcGPfÆq8z#P(tgyDFFS]ZIIIBP-ϟ% %%%ӿ2/#,̟q%al٢Ef*$ ;O.c6ѩ[G^K\ib˖vWJDM\.dر̛7/肒TWG*HN=(J%.{B Ncbj{[esX*~$`%x:۷o8#qՙNC9k2T*l \.!.a:~~%RڶrxbTaL%)^Rȑ1X2kBN6ӡ3de}Ol-( {5kڳ~TWO* %%PZjR'yҋ'Fz3_ N\t4ȭͿ>OM]MGRR$)p:nو#NJ@Ғ ݺ !ajuEkk7fMGJJNƌCUUU`0v=jV mŚ'IIh4BX50K(s^ы&pZe<;V^/eH)"D0Q(kׯINV۶Qݾ͛zu yy$`"7|6DT*f !DJh'_K.~%Sq[Wݺyq%JŢs2e^$D%z 9ii0\%y?+WkדxeoԨQk.h~GImmaaQ]s;Ћ0M!DP;Y|0l"j`V3e[xsK. {ԁ}|M(B,22搑)@SKnV4u0Z h _|7|ԩS aWorjj$'mSi !Z{2}8к|ͬUsZgՐ!:Gt8+B*v[m/TpԬc,֮M+ 8s ^II < > i&}=Auu$!!zڴY?_S룸L`!D{r@ )+?X\|q_쟓c+n=>"DPhde-}hqmlt%WQP0K&N7zhBBB3fLwz yyՊߠ֌ۭ!1 EAuj%%%r !ZɎW*|Y s~o.Q?T{Ɉ !Zg_ddf~Aj)]Bo+-39۴ifb„ L͹s!1;[_ᨡ&QMvloXDDJK!GP)Uwֹ˩1hOEBB$wpԩ )"D g6C.sȘt6+oVo3\i`۶;q$`%x#G[niC{b<6[NP(tDH#WZ<.;BH }< [],3Ҿ;U̜Y*Ё9礰p&Vٴi NgL>W_m,EVJJHt:IIw՛'V-YgbiB ]{xb TT!  tp}3e.VEJDVuJA/x~'Vf Gooy8z#mtB'Vĩ}:>}a]L4abݺX3Jĉ'x eee mZWJuuZ 7M S7o3wj 8c[ڲiB`<%NGARR2k[ҋ'DkRi$%Ci|mm#;R~-K$;>Ō?q ǣDTTR͙Z¤o2>tqFeƤ/ Q%5kunWANIZ r`ъ) еbRR^COntmۭ\޽$x橧"11#F:Ѓ'rZc;B+dZ)@TJP̻9sx:XrXgWjNZ^elB, c:wKHH+oVwS\%`^vv6o6SNEC/a <6"K`Dqu%LIк-O[\Ѿ}u(׺hSi !1SiXzѸ򦝂S*Jw#G2h s6΃UJ7Q Z6}ƚlR“u0 hvu \f2~*^{RR;}߿_/B4ƐYY5B۠Ci~1 N] X >+.XRF9|>Z>NXNr&JFGѡQЪh5 ~ ^ JBBTTJBT &B+.u2: hy;ŵG7ѧK/- <\9G8W QT.sxtpZ?a4v!= ,$h-w c\<\xtӦMcڴiYfqmW䮓L8DZ4*U 4 jRVDR/J^Cԫ5FF](M(ւJO&s޽F\J#YKüm_04co\1N"55fFosW>yiѯ;'aôx)(xM.I`$X4e"##c/FhV<CPs~~`Ԇ{)5fT*dž=wrxiݻ=$"۪;6v@MyiXbc{}MAM\h!Q~^nY69YȾ}/P]p3j%5u:Zm+iކ xwYjqi;T*8B q@Sxn{!5"j%XeXXXԹjsasas۱nv ۍbxqx|8>^8`}hˁ=y<)9LTL&u*RB>0WRmĨ51cИZхF EP&qlXBׁOJJ>daaIOA ȑ#߿?\pqqTTTB_yTZ,! XBN=NjZ{ ն"eXQ]u0'ABH&}8q JoÄ%'wd%I*Ea*뇺{(u*uIMdP?2&6~k4:MGaz}^Fkޥʚw" e7tmT ^bɼp͓!-E_EAݻlB4LǑoyx9Sew]LHHiiobc7w\VZEvv ^Kk eXH;BN|VP{(%XeX:{' eӊm'Nn'v{}BO*}8I_ tzOmBi/INIBR4N&NI:ikո1UJ ρI !pHB Qh!RG4;iYpTu6rP^eUj[<hxllse89|>TZFж}ϧpnwYjfÆ ړ_K}vnYguBwj1TW TTTv lјFm ԁpwm Rk/(,^N:W5uZBdȫDžqJ`2%yM(+n&os ^yWEm JsHQ=Fƈ^mĨ ?i&D"1"ШC«h΋NbCJ.71=-NVaC$x⤹뫱tLsnFsNP{#117PYWp8^6o&vFӶٜlZjjjN|%s'U'&D:sk`uRc/qQkUC殥Ym<;vc;Ch0 txߩJ(ס @)_UT}UKWs` &]WxULqh勝_c1_C4ispnLAFtqR|>ں9?iQ)Cxxjk73mk*e׮ٳGr(O<ׯg߾},\t:ꫯx8p ݻwh4ڵ"o{Gyyy裏xL&Shjі4I;-gwVSm+^Y]+x{?Pq_'v:gu.[ҟLC^^J?2 SCiivW3o7}>EEPT4𾤧@o'A; ~k5B4O4pIK'^gc|_'bmEXy*_2O(\Vn;6W}BqנO 9|WT'nH UZ! U}B6֯C хcԄj¨DҜB:Mn"52Us U^;YH~ey*5VQێzkO}/eâ\zMj_~$Og%cC>ոF0a>HaỸMBTT $xB}6-yz=% *_2sVPꨮZjO(6~kv9 =~ _G~[Y l Jk7b* RZBRQQhTZ4* یŤŠ1aІ7cԅSh4Tj JUTW{;oF76&ՑT')QQ t4ihbhڶUƧSNl߾ē7Lrr2=zK/O`ںiܿ~:GufЩu$:Qj/YC騪/r`ȫ檯qcw' *̡lڴ ~-`ڴix1e<|n&O6P~ӏ0Y\4]ADj(#iD.DDnD^԰96$j63y88'q o1{pݓ` EC3wa7&\ݒvݜ?iwX yN8&%iIDAT*xsq!AxEp^F<#ݲŶov~vxrpQ(D8C8+m6FGL4!0֦ mmmӧcØ;w.`MƀG~1<&`+(Q`˖Nk^Dn[ 0㘳p'$% Y!2T]PtAUM=4GѐGCZJP4@SoZ>9S<pYvԎ.s3Y?LO J7|N7 X(FЈy3bK/&""#,u ,uzʨAL]@TUH I d=ECP 4DEqAE,?7vO2F J&9ҜAiPsB? |>l7X|Wq[۷oǼy5f̘{ZׯXMKG"JĪ'?pG4Ł(S5alӉ܅|*qU4G$M#Zk<ՔǨDFS-K/ t-%;Ba?2520(`iwu8 4Cۜ?ity9~Wb%CrEծp7oL\;?tI8ԬAuj::lç^&< x!b˖쿺w>#}A0sAcf0哇v<'_a˸(Dy(C)ZVDvs\iDnD.D3F4gPj*i3(u A2ՔURiRMy@?LG] L qcN1Gx~^!^g"ܥؐQp n?YApwƦwU%g9PQ3AQڭϋOrc̘GQYy{^=wUW(f\˲̀G/>oOyWAORd:ߠgCI ()e#$h%G GBu=yv{n궵s5}&L`# ,-X7{ƇsQ(y\ PV8iHO{;O!t!*ATAL3F!j"$M4w)˫12DfLN ITf+P @(S8/jܝLP^f!OrRpو'5 Q<'#9`}8vA?(㟀 ryޝoԔYt:q-0@9޸ q ] 'gt]Z#Cbr';S{q}w(\7:.0p8sspYav#H`іEx;;v<<ۍ.rِ1ŜA&k(eG]S2u5ՔGgo픸@?L]¤*:Ikd ^!G+JgRڡ:K @*ń ])tumC"![뮮?OFALսrN<7i$ ?8}]zظq#0壷e@96(芬?Sf!]g{!jaĔD- @T Y]TWIOP7ɖdl0Oekj8N͟|Ik)b8yQć/GߋW:t~BC|S0qs(.Ѱ}.hllgϞ=oٲk֬:[p: xDDDDOsFpp (GS>$9Q a QQ1džČOa]}}Fl۶ /:::PYYn WFYYH$ Q~h8ـ/MG >4K#0MW;fΠ<|1(D՜AfT'@ (J5ҁOocsoI>TlE\8tk3^.)-))%Kp}7Qk^6ۈ{g˅AQ5lӊ>QӘA)5J92Ր'yU4ʺIͱ!FLܝƬZ47GgKG"!7vG.]k~}dYơCbϞ=lXp!6l؀:<"""|M=#Ml*hа}b bR"ِGAT9cΠ F@JpwD-9oߎn~װtso"ʕ+<׋_-b#"""7ǔ 6YӪqa(?E-?|͛ݻg/G<#a;a,\v-{1#lhnnFee9_4\y8|0c}0uT6lBDDDVJ`崕 w~`sm2-_,_;/Bzq * 뮻j*Xz5}6v """m[mŻ_C+xG(DÌ$Ihhh̙s^_gƌ;v@gg'Q>z?º~gP. 3{(| xDDDD`î h.Mn Cߝo1Z kr#ʁW?< E!555uٓqP[[k]s(A,]Nb͸kz{QN{p8`rGD~`h-mP xGk}{H$ގ[b;w.N:blڴ H$Q$ GϱjժvmXlB<|>Ə#"3f dYF"@<7BWD"{mxɈVZZztM[zGDDy)#M<\""_}t]t: PRR2Ν;3,XE!"!SWWwI5xDDW;ٳ(DD4b&啉'ѣÁ.pahD卶6+W\pGDD xDDD3(O| xDDĀGDDwl61ӧOIPZZʅ!""<""\w(G8蜈@oo/BƎ1h式?WΙ3BDD xDDDΉ(O7Xad#"FQTT]UUUhnnш<""i|`֬Y\""b#""E`rRXGDD9K$AQ@YY:::0DD4bqr޽{p\""b#""EGDDĀGDDyl6ш<""IX PXX. h#"k.+^E!""<.D"p8^zZ[[34> stream xZK6ϯ90T'4 =3r9%@`v/,YvÌ[%OV{:(юfo03=W÷^ޟ vQaxcMZqxI3P>){'cٟT |_/i99Go???}*"0h(jСPU?ӹ*Xz^vK^y7?Zi?&/EQ;DiԓknfyȇwTdJ:i-IpXMp($eQCXyy PUf? _Ԉ7a0!:j;$>#ݿ:Or v T`OH2_@:-_fQuulJw1[4#{휔FJs]@}F>HzBFңL+;%0PgSʗ򞟔2?dmsEZ[/pPrd!p*Y`9~)LuO!>5K j I]#X`+/&ԉng'g#*/g#F̵u<@-w8왏#r*G4(/*)waW~P>ܑq UvÊ\+j=ZbGJڒ}sc[0f+6Z)FO۬WS-e&&>Kd jGʌ]{16z-]72j-zLIKk-[k$[ISpaNo q2Iߴ0C˕q>OP(TבMUt^Rb/ Re_·BlUTvqv8 UsvCmoTҁlSrHPmVauȽ'LEE'[`QB)k$9㒩0/6s$j*“B(<3"V͕o6f|qaﺄRf?%xe6r3cQQ%)PPR:M+et&f;Bb謠Bġ#!1q+; N8CYQ T0bYjn`R@gȸ4aOb6a :s3ʕhL3b2;F!n&gE;Z;̕?PPu5H7 g]3H7mfR`=Rk0Ltb"Y4eBa1y й"AopbC +MrSbc1jml1~$ {H uf{^No w %Q%hyKlnC˘57t#╋#kL3oͳ)dLyy0:O$P䃀 H%fd IpMR}+ӂ SJ&J'lDQJb.Ԉ@7Vl[YtUtk TIA4v"8~lnH}V~#7J~Z^O_pw׍ endstream endobj 3 0 obj 1981 endobj 4 0 obj <> stream JFIFC     C   x" HgII<[y?m՜EOI;Y`._6*Ksn^?l;i+u{:a[?۰vT ZɲUv޷Ds+sϾ"Y 74-mt#;*XmˎHl+p} ѣjD zw]65&>u|ouik`跿3z9Q[*iۼǐ}iH׸5XR'N1%3;*oG򻇡@Ŏ: ]<;Q`XUgh{YB84DzFo`> 4܈*{c93j+Z+4ϖnXiS]dJ\0@ I̐MbH_`QV]S <1ϗ#KmM/4uzqGtӶJE/H) 5;f3fN{%|fk6u_%S?_Egȼ˸R`y~mM&|{ke[RYw^yOD@S8 \Qӧ.Wef |s "t䋧ygX3DGjΰLKme)St*vاJ*kƤ7?>iMu]Dݗ/ ﳱhqeec{& WGdẕ5Gkͷ2PGdEh*+uPamYI$Ebe)òZR'U[~8ˉeEpJM?scBSN2,j!=Yi2+=|>YΎqS&-/Ϧ^ ^yJaF4E"d!~g=V+;W@{!.ht)f bJ^JU[_n17|ֲ/h1F~'*Xd,O9kkIZjXiT%3t0pc's SrVAM!܌O.G+%S x棐eʄ!+52/j}E(bdeҎc+2q"%2X/c#~4*:Wz #Gu{2ڵ 2 ɖE4?M|6TM{#d9 i9rU;Rj_Y@g/HTxPx}H׮YdUC>6sKX[ n!oM|$Zuv yZ@*n!H"Q,=7h1vIVj+9o'E3'g-޽hnCek'q89pߧ1-MOqQT\#l`3%rbU *vK)Q?ET3cI>X$QJɮo=]?!l]T#.s\N'b{ܭr=C6b'u:CBjfՍ2 a@f SNYTfO &~Rט t2DZBfG*"גc6t-xYuK t3BGH棒ZfX8)6lE(C뷯%10ʭ,s>j ب|͚3&q}v v9.؂/>YQHC/c (JĀ'\O%aH9ai&n`0ѐ,G+&oȱckVMJee6{ʇAY=Y~aY&k㺔eo\ʆܨbکvȥ6c \՛GwD6CsQ-J#jPJ)И3˪{g]0'}r8li4 1:|=LR+M:ϊ/n\_AvU I$,ꦉDKǶF{S~ k [' pwMd$c^}v t9?tckoswk35]abDp.w X؍aQQvFdaZw~R:-':m_7g"S$̑v9;HDr;.*!`Jݲ^G%.fj=q4FRG6oNխgEv ޡll}%ƫGNѫhSvRޛJa.!C#`s#dĆ iqnG\qknf‰]'sA[xv17`17J%+$ nVKĵ Gv#rItc6rIdoQ= :eEOwo5u@zpt< zOpThW?Rv.J=" gbs^/LM gv)7?[ۉKlfxsq92Z׌=bƳ?ysq7?boӮ&PUJv*]W%k2b9׳PJ7w辝.Bt$D=8!@(1푽@_O{o%nQ p?'bW.?\~;Rv.J5"N^Ԯ7YE%ebwm66U{RlQ>S0ͳ3X;_Gї]G/@ !10AQR "2@BPa3q#4Sr`bC?[T4’&fW }!NMa+Œ[j>޳&V\=#`Y.[!I>K/UvlѻH4Y񻏼wʧhc>&EV$(PQu|/O Pd*S7YsQ4y̛jhЗUD&]kdGSnaP6q5#Ntl1yUT>\!8I,EIoiMvH%PID4ne mBmI"cQӮҪx| H$OΠ4V2j+XL̵(5>/4k2sn-j '#$cf `v|d D޳2.VңH749#+q?FO5pX510Q0?8M/(i8m6tD(NͰf$0 L`#:)Yپ11o\cQWNl )9euUVaJO>Q-<ՍWwT 3s&M4rh:# 2{"2F6`˞dʉ >0d#0d,PsCQ9Q ˿8:j[4unyUVM"RMQ !"1AQ#24@Raq 03BP`CSbr$5Tt%D6?.LL8eyKV.Q, G>HlrI ! @ДWoՖEJbu ;-[%ȷD0ͥ.t'XYM4]V凋oVeJE7%Ʈq`BK%WJ>H>AQ2.Y/ѱfJ mbB3ErWk,9EF#fd Tۉ B NX$a+A JA ;y?&4a{uE();Gjq[ԊóNo8waB ٮs$(s7$Ɗ-1yoHZFS BtNreZ*'L@(l1~UbW&*¹"]O<MY'֓ -T 1|RM|4'5 +lŧG +As\kb뛂+G4k9&rV8C!kZ?oS\VU5Eɔ!I=SZgdvvdZ@D:kZO o'-PXXEDo՚J|'248!/~uVWK+gia^oqjɫˍaWw7}o,-1;t< NyljDٮفsC̬-<=S/jc6~1R&3Ot(W~wGM rv8dV9ucw2,9Ϣ* FL|cPi߭ٴx5-ia6oHK\  ˰ QCH ,>"k304$=-79Yfl*յow DUDSW\Y֕;Ir3Rj@t2ldy6 hb MG&7%y:۵>S2aq•  !NY/+to!֔BUg/$<ýmvⶪI;bAwr]eă0{~ȓFtKObdNQH0HW1' 1D.楯О߯8X츦y(҆:Ru VBc&. aDm(&vqOOJ6G0ҧj3 ˒Ry'G.+4qBrKؿ4&\P*Uw}L"y~=S]8–!"J!V96yHu:@[k Q]>G1zsCLne*.< |Pj<t 9gMuMq{O{19eMLKk(ڏ*=piͮ|$3v?W_Y1>YNFw8J㫗}7hd*GLlyOɪ.M6Y^Qy N&,ZbCeStZL~Oxp|zjaW$YCd~oLP!?o3?LvEds;yY,gvsO6.oE7ui ~Q䘹8A/6J$<a1q{SN2Vcŷy&bE{&[8˼{p]sy-^PTZWjp:BPbٶ'ia\j䒤wZƢjPu}f;˖m_sg耔\`JyZt}'68)'ԅ- ):i]6G I88 _hW1Gїk#1tW%Zx9E[hiqgt{,Lü ƝQe+c7}wy&u닾+LODw,ĚmRh≜9+ERBpd~1cDD5֏˳֙5mLk_%|I3rH)HHySս5zkoҭ5p)KDYP:2cdJ'O{bf}D}߫•k"Η|u>=Q=H-+;uDa6l,FBEI:Y3&}TZvvdX#^̴S_v{<Ü+4⼻\EwVbNm O84uld%D&\Ivמ-IzUy6UA\m54ijUBiNO9!])d=-ϕ?Lcf2p_/0eipǏ_4=qsidfZs)re>~6YUe,zSeY~,z?*T[x- {a`&ؠ=3{}6o*e?DgLF,̏PvƇǩptꫲ8k~}OK10}С,G̍ bzUյzd%ĥT=KT{J^S*nyNw,Ľj)db~Uɋǹx"66A8+&h6,c9ґ˟ϕ c>[#7X)NW4z1`z`dfNͧC3-Ye4o≉4ڎi}ڑ{W3-mх*zS%*X~(ʗ= 3d:pV1c>ÚGM gٓ)I<\l|x#ƭ=-8j~#7O]324QfB\ȋ M.mO|ܽPI?6r)RIZx\9zI\鳓l m哊\xlEc9ҁKϕ c>_#7X#뫶8:_lxSОN,F]s#Zw3"ʖ:i99?$ӏn)f>‘I"аgTs_ɾUMSx2- } A2́%&OBԄhިSld|WxI)yfJJXt.gJZA*mҍ bZKJO:S#8[M+v~'O#QEWhSΞ->iEōd5r9U i $o˂> I.#qVV/(_'IбE|+!1AQaq@ 0P`?!I jAůbFKF Ʌɾ 8t8؝~KmU ~wF"*1\lτc hǛ mi}rH<#8g"D n3[b'E~CJH˯rfp[EEd Z2dTc<_y# 8L &P_|"pdP^D>u0(tq-Wl@u꘏"hS+j 1J14*va P#/ Ȋ@"鈜M90S0b}֣ypFH#f!mJ4B\~%e1DzJD5; Y~)~\$(5P/XzP{ *ǒUcW>6C`o2X~S0D f#N(6POO&b^rWCw pf ;K8sZ9vrS} XXSn^뀷wtYp#"A~ 3mDŽݘBN0<#c65@`HiMaFt&"aG:u9nӨ {@Y^*f )=8khSԠ 0/ʫ("Z2 wh _w $iE>] w9?` a 4LDFs$*t!F5=qH7YK!h"P"GB) غ1`"IB}z /C %g})if1{C kkVeqD2J$ օ14@ _|,R,ǕIGr D69T)3 Py&\cΘ l@r1"Eː11ƳN.V%&&P5{tWs`<Щ EqE؀-DFgS1҈bs"4vཕ3ddU͊>=Da rڐ ̦b8l\+4G@ ,ǪH;  52qy03g2[O- B%=ŁրQWf04sŘfM D%e xfF$1ymEvLJ;&f`V?ߦjB0tJ`+,a^ x0S48" aMehjXD 눸v5SB2b"h6a%`,X$`40RɮbgO{8P/=:W#ǟ+S6^K4<R,l%ɮJ&`H‡$60G 3 P<< 'q> *d^V@pr])X?]e?$;-3B}b1##̉͢8 <<﾿<<<<<<< k*<<<<<<<ᄉ_<<<<<<>~4K?<<<<<<>3<<<<<_ /w﾿=<<<<Ͼg^3<<<{<<;lx۟N ros_Sxz<<<<=<<<<׍0hӞy @P<<<<<<<7p},JPOxkH|L ;:<(G#$>NKJ-jmBR84~%s9(xbT5,-^R}$GRUdz`1O[(r0N6iZ$k*AK<@ܠ٢csuGpF=)ra*:.$K4@>N)9i AB$ѹKcHmx4ǻ'iriM׃,NGM*aAjs- "9xv~i,G% q68zU- (2?Y(cG<26LirioXٍ(Q BR[uBaԡv j6y#z3<'AGOEsӰ;1= dzt[=7-TX7QpkARPDá|M )C{TE笍NCQ>jWӉPF NuMnjc/PoV?v1Q|иa#5LJF'jqnI[#>r'xJ.ӘSztzo[oE6q7oP`փKz=ʃP`}59uG=1E# "f?qM`E+ٗ_+!1QaqA0 @P`?ݓbvC$`sS63Ҿsًҕ0ye00i3 <5{^iGn{y(Hɸp<-t9&&Tq{E';|ɝB .bcݻֻB8gN[Gw{>U~%{˞k%𼸦F&u ʼt֚cΛia澴CF0hv%Z d`s;kL_[} sZupwRngwbAvk2_ /t|s?A9vn(@$虖= *@GXp(ͷ9b$IxἳF(RssA##~x )zllķJUF:Hp3WOv!ּ@ 3uCKCw۴@ 2EQtssn5qˊbu7׿8X($hN.v䆰ZF2iDagҮ>e ;Or!ذMxOs:0?\V 26m9(uZGa֏ȉH#4@ 5 |) 6'DSSa*;K .Z'n%q 6jAY4GDa IJkٙSB[0L`@ (7?X\9Ji 8x֕ϰn K̩lËsaˉj<F3,%B\[Gњ* ;52xN*A ;EUαWGJI(=.5n`L$ h&*Ϩ|"lQw,pHڡo5]WZX@Ov[B`2;ˀ1'͗\p%f::7L''wPV89@k,3Rz%÷ x#E!ȌE-7Pq R@5!N≑f`p$r= BD47/n) EMGuʽDR%!4٩Pv2&SrnZfcE2q2z'q,qSA< h D:<9Z8D炁G@\Ҟ "Ks91ʕc"xAv"B嬻8lh>(SH`2-)t; & o} AA ǂ iSga )8 z#j󃎹~]hT17,l u] ~>#rN}ZY؃9!{-qbEG[lDpu$VX55q 9C5 "8ip-P6M.G^qrԈ?TQ5k$2$J,GqF|Z.( &.I=~hAHNqO!7< LdZ\bJ.թ ڨr B%'<]]M, [J!dFgC Lb&-a(\քEMI€nh ҅Wo]]OMDRP [0, X22'8)MtM<7\b3FE){tLi.TLѹ8h[7|[F$'I @_\n|*bH@B:LLn[CbMHܮa&^&Uy@'H֓R]x[sS t1Jĝ7omft 3I!3f&/z"/)</ٚ5,emY8a'jڞEou"A\"oEBk"mM}K5z9ƞStZnt]ݴOɠkmC}Pn?];&O6 yh=bRwj:z ##~P,+H"Rrz{toM/@9(QYnCQ@R f7>@"Hӫ=/x=wFrD@{aXCF棍yfTYˊ1X0S]qP궮QNLQ6|Fz?IjUs0b 0 HWUZ {'XfFbv/:aTOAXצ /U\|Av%gbws! b4ΰ] _i^GSu 5V&B!1dyaH />#&!$j5;R/HxLVef {!3+49zho $?+_lRXU^,{f{U*Z^ͩ> zb1<$vag/_P U5&my|+9v/m҄T@Cn_ ʧ|Q'!jN&f3R+3etK']v@S>*wQX,|K|^XTC06 1c. 3(m 2t9;S9&{l7IzQd=ԶL H 3+0XND$ j+SP%̙g^@T|}ܜYXL/}an-jHJß}k-taRqKZ=%_?*e!ofT1m&\pى8{ >YbYǘJ%%>2whA\AT^|V | {V~xA+.0stRjc sxuSZ@fe4[ӭ8La8˜=uj):!zj-^,:ğƿj9Pz{6^.ҙd}(u`u!wI$3ԔaLZZ@BK" iH& pKZPa F%)nRA{`yH7Xp3vz'F* \!P҅ "?&p">E(fy \PL޳j"6@h4JHHr)TJ6ٔH1 endstream endobj 5 0 obj <> stream x  OmE endstream endobj 6 0 obj 357 endobj 8 0 obj <> stream x|y|[ŵܫ͖lٲ-˛$K^dK,K:`;`g!m-G8&ЖPP(kN@@Z ^PJ ~4!@!l+̽v@YE7s̙3gΜmFHv#A)#!pEV"=ӟg!MB=}ݭ|p%BI7"u~!Xyqnٞ>,Fh{nо9탵 oо€_wO@ǫ #=iSDCA?E(0\c +*&!Q(r62CᶡLAy'[m7gU8DfA?AOhF{>ZxAEHD(b\14lE#nB=DO %W|T ^0ރ H{OK?ƧRc^ru\]F\2lG#D9^VVԆz~Ü /r0SW#105ybEQ=.B?&:p[h#Hz%݋A`3n%nn➩7Q ;EN$?U??>FW-]¿o%-B6h Xu'Xj}tЯ_# 'i~"Rm(mG%  d@7BAD@ٸ _Uz|\B%r7ppQ<=+GUn ~S:/'n=a3ЋV5x;_wRr> As+.yS QIoru,;k= ^x3q: :z>p/މ{+]'J&? wߒ7mu-7 \-vXŇuUTsxדNiҧ N}gx[|8>3#,R9\/iAg@h}&~8xU'7o5}Nš2 T፸wA]MŇ4~ ;q*Dd $~2Hv-g!0Xu2%&<(ww';E%5S 66=BS|pB<_W9|FJ*T-URL5CEBݍÿ$u7d?Ap;!Rʗ/Gqq62md x7(?lU#D.oi Ƿ#C=d+Ӄƽ ~sbߋoP>6'xBt;@5`W( G7q-Ȅ>p!7%7Sw `_m4y\J|'^ 2pj'Q> !̓oKeRCs%C*ӷQ;~})nXm:8&IAxWkG*XM Wɽ\7G~9 c҄sȮMoDM$O| +}YYbrS;&u[j!%!h/*,л0)#g(?FV{Qh7.rñq !H'3x "Kl?>F = ?En5U OyUZ,.*,pmV`2e3 )$61AV)ఖE$۝ DS!j-5-fKXBBjbnB#YVK^ns59ٹ9tŚfXm<0F4`XgM+YTGY:NŏL$zcܢl߲lUYc\D1HƠ^$갨&c78Vp\a"an=e:69?r:?q:?:ܔjIIU }K\NNPg:y2ߙi T `-tnڴ mB6)ӌ*KUj\UF@͛W2Zxs:аJRHJ*#UDt7Vj%]^EKC=/න}h!|ol[Գo'A1s/qP7Z6/܉'z_CSXuPka_:LYN^qlc8?=xɫ{}eσ~o"%"/j+3k+Sbjul8XlY_XXV\1V1K*&ky벗:zT\y EnGs=X#6+UF{yG6O t̳)I#Z㐜g.0M$Q)h8NizRh~HOg&B2/ϟ_ vj+ʅ܌TSңE3)ɋ]mmH6dU ﹙C۴hPM;h1V:15^Th!KC8YMw{L߽po~;-aSg, M[,R\+2ib]S*-~ĸEszmBRU>QSr9bSev볰U?#ǚKdz..Ècuo[YWDЉu{u.kǴ$J1Mtt.qjl߄MptђhXJ_iބa׆Ta0Z 1; @R-ۍͫr8)7ٺΊL>RwC.=VrT[zšBK&eq$4Sw4]g(/ESf-%Q8Yy$Zvbl56n)噵Sȴ3<0y{tnYv{v nE3/<+߬8 sՖW=j#_9VDR?,K95 8ohv'麎6-jp X4)HG3Sp$U9v՜ONp03$N65$b{'/rhEs`D<ެ|ݎu򿶙M6`{ Dg1ZR-ry锖JEK< ~<ýSpe6m.@dM{NNwe椤w ei$3cIOp1M$$&8#&ib/HcR֐-xqu4X\p a PIIՌ ؄S$ODD$ND͗D-N^+쮜kL 7K:x 7WpJlR;&L\Cd \4Y6MKS(+h|O!ICYT!&Ry>APk)ކ ,[QMj.s7'Nil2ՌΝyTs^=58KV(+Ac'RH1(ɽ#ʍ?ɣzq0TP|9swp:Ǿ! 'eއ1wcT<(-Αa BQ Q(?.JT(*ʰ91֠KNP]#É(>.ZMud8 mԇfK_2Qrr&HQ9T!+WU` o:fa&`2 cO07$`ߜrd[#`2 }]2 /`| }dpՉ&à4o"ӜeG^gZΟ0ʜ18 j2̣B{ S%F%v~ S4eSr 2L_dpŗr2 (z>- f#2L\F?!Ô0 SOl2 |\ފ~ S|?Ki&p"4f)= frA)W`fJ7V4u#Z@@iE ^hJDP#BӻF!ƻjbx߿=#BO n|2T*Cm}P1= CZPBh]0G3V@=hel}@9kb0gt-:Y%*ޔ&=a(0qgO8 tZzZb5Bh+B5A"@Dd@^`-JٛJHk+ss}Loa^* ЎS*?iJ7¤ꙙ7 GL)0BlUk `aLTlMw+z(I/ZYTR9BL{[ iikͶddz]&J50Y;NFxu14.?|1^& l I}Lwd~gr u,a2oʫND4Z}&8ð,۴F$0=خ9%}4Ĵgzr_(5`<:gEDr?"aIG38aX6,GY 0k 39%IA6vZ <<T!yЌ%ŘEs$/9ոs]w1 Kڥ9W1^cYyexf)G2؈0[iu̎>yސCsJK#nZF Jtݝ,XOΣPtX8^-3;eI]aT|ne4D5e2;WP_C ͱ4gYܺMFPv1 nbk\ŲltY \Fo)#]3#8,1OͲ~1F8Px%`i,fكTvf3l+<&rj3 e-d毷˞U@[otóN& l@"_ygּ4wt1MKf=?Pfsɗkei92ԩ`:睢 tb{g#5ۻ,)_A?Neӹ>e+:?oP:مgFi:UNA4}Ldg.y/ e:|Ob/si~-iy/84]w#iOi3t5["F`@Rφ^D"Ze|C-2eCW*ቂu8>M{3a蠿Z{@0(1 |@p@t M+ܔ67D1aa+)+%4 k=]~XX}"@4z(L=B@g(vG|65ֿjm<_i#ZC./Uv隅'C.!0 DtZa/"8n}aH/f8=!`l_h F=tl S* C(:#p/+<0BEuȴ]pD }tYag`Eo*a@g#z].4+&<oFg/UH0 @xw1фΐ0 ܨ OVF@YB?ANB;!:FO `f} 0P(8 v#p`S4 RQAEajm  eAs]B'{3DCa:r?4 D`瀤fQ!%#`^_c[$:$|c$(uwG;}(L<4D#H g,Q@ 9#fz|;!?D@ACC)/dd]`oTBv C]7uGFF\"]:H?B oPӁ?hDh8ȆXٺlƆe+W+ g/kl^YhXyyV].LPy4!V%0KR>`~a>R 8_~0z}``G\`(<rJ.v$㗜Zvf! kSY, Q2a_K)p=%H^I΄>!< |~hx 8CL,&T_? {:qH9zCG Au½t%\S ;DzSQ.̦dq78`C>S:TEJ7F &@1]O2gtB~O3y|ZJnml*ު"[VѬkdYy *j^eMe.Dݗ#meX/*BWf~.VځN|sFJ-`,Z>n;qўytҙJg^*ytҙJg^*ytҙJg^*ytҙJg^*?R om\߼7 }o`fƜz?KMx5"_9nNz<Eofeu^kCg7ռFΡmFΥuf|bWC eǁ,_UcR;qoߞ߉S5R|ƏpL47{N %K+ M{۟ ƹqr(\E̪¨ `4 52!SSbC" ٽݳ,j;^]WյzuKЫkГJH8:M$W{%t QAw ͂I5pBv/c\zǓ'#a< gJT$}5Hڸ*x#Q<5^=Px搙xi!>_P OQ(ۡ ?F嬾Soՠ &` ;\7^270'E|F .Bzymk>˷էmRN{'8]!u+u _gWQ4.-ѢƍRc~r`q2JtGlE>ө_ݛڨp*(|%[TNɵ] ܙv%ѿ)we~k&[:&?v-e6\344G8ֶD^߶h4;3M/Y?jàz:Hj0(vqNo H8F<|ÔJcW'rخ{YpV]/p^0i endstream endobj 9 0 obj 11635 endobj 10 0 obj <> endobj 11 0 obj <> stream x]j >E4%EhHt ..osd<ÚʳWgDNJK:tYiT*ob,aoӓ)KBonZ{q3}4}_` m} 7 4:K#a7HyE˶hq ,H9?dU$n##/'991j.ɋ\#> endobj 13 0 obj <> stream xy|ŵ(\U==fzf4h$,[m$y,Kƫl/`$/`c+$,6 +2`$]C 5$F3TȘ{{}VUުΩVpҢA iK3&!^Gm\/>v /[y7B[rͦKeF28UW,Y~S]Dм3jWA&Zu2>|ZvْCCʫ\;2s.^+#зv(T@+]9B۴> :V:aXN+UZ Mffwܞ2Op$/OT$S w'|*H _rW`i  {^BgqzDm4u t&bQ(})Ȏ9.CCtP;=6sZQ\p5'x *F*p޼pQ»P 4>BFE[ѷ ]~VzBKѣ ^awB16nmUCk`;>YGc+I7ۡLJIb}HD4DK=lUT.)GHЏo3sY=|7woh =ޣ tW*b'v\8 = ?Nv܍O癇t`)X ,P9Z=܋wi]U#\@ЛЏ?Gp|l-//|}Q"GBmDעU_@/DWmF%Nz6<{'P00z<_W]<~'fy-[qx yA4 |}w?z 80w/̭.KY6=X AJ|e,3t33a^a~$MB{DEY*_t#ځrڇCg@a݊c7~?_¯?/ApH-i"d YIn;y|xeVf=q=,[UiR_u>=,+lQ۠a> 0AvZ0xl5x:xsᘏ±/ū؊Mf| &[>=x ? ILa%$#m&SI'd-}dl =J̄$gv3O2/0o1d [Vp0}6<0 s5̝/,#f5sUf-G50+?s䏬!-Y4,WZٛ9[רl'KͅgQ!o"=Mm^d5ى5yw{3o{ЇL;>sƆ$^4QI;<0PYG >F k@=s {{XI'4j5te*Y(Ǣp(>or:6d zVV)y2^q8;FӦ%i=\;,BӔ_3,ʗ_R+W˕RJ•XQcBl ?o #x%-p ɰ`n[Za+OٸjGko AЇK*~?% -ź#2=Lz陓gs3n <>*{#ae¿A[W5 c.[wp>k J.a LK. υ[WNV>0n] OZ,6^ XnawZ1V7һJcnH|>kuO>gꯝjǎ)AqʎKF KqY,;N;SA IP6ϐȓ#!?> 5Oc9 98OH—#GBqqpc5,| YUoÐaЈ_/%k$?:ʴIUk&~gb1c*xL^6R8)bTFʌJTʧ" N@hПii*ݼInCXqE֘DnHb HPI 3)Idg!KNn==ȍӼFnTJF(V8oky[nͯmGG4“enV;xߙ(RCV**t+ۛe$%pV1=%Y 'LFd P[@ۂeqopK`U|r[A |(4j-)}ʽ7r<+㙲P/P|8Π'qJ Vyd+: Ɗ% @1|K9EL (ذ{0pp.N` xԃjf5Z6[fkH0DFDۢ n5M$?,ek7jU )U5`K>>'F -7zlePhrɪ ^&|/YW/;]X7mK||sjF >62@j5 Z6pU/RT[ܼOβ>:W]DJ!x{X1t$LNayC0`?dj$^ @Еhnn!w!;ݳH[8XHIew!S RT"|⾨:*v% kq)'+"FEDlS2Ȭd_T"`[7/fffu eO7*<1dȄvJ;xٚZ 2s StWm|cWny}vK޴2l;x7y=76ЭQIqUL)IirKva~;!Nͫcxo/3Q2#X^ӽ1bwBU|~(ITSqMrK.bAg{rWJ EԌJ+^ Tz䱕9ݪoҘXE[Egfquf>f>h*/zxWbGp0z65]u>|.,Dqѓgmdd u ΋RUN 6i&Mo?G3^CaFp䌺)++Fqo/:eh:JOҫ⌄1:pNc= ;W T>\RF?aQF59L3#jP7!ƢiS n>; F 3D}F+J1΃<o x<|*{^٫n:ryv Mjav&p,xH`ehg+n:g1љSŜ9,s"uE٩ΥBNV"`v™ >>l ksY &x ^aU&zY` 9F{tg%ޣk 9rBd1H5#fKKAc'HHL55fjȐ?reS找w9kVY:IvdI̿+ےxjoU;k|!7>A2iPFrpo/4:PWH4 /0q7U)<1=pw1q^W\ IZ gXCI:*-STҁ\HV`+VJ*m\nrNcN=U=O}hJdE$BS8-6 qO/k"JsT;5XˢoYnӰVW+*|Ƈ4~8WJ&kmvlC6b4ޥjhԬ9ռU- Pvc=`8?(,iT,d$Xe4QA)˹ eSO 0/@O"Vƞ`-4s1E.X̓mO6uD+Ƥos'5)E,Z9e^S;4;zW-}UרP@3]C"H j.xܯ%]^_oG ::-A!#FO(. # *#͒ӴEw)zrvH6ɓ)nnQFOrFn^Tq+m \h;2)]zg^vm X`c_V|=P'a0*:\kNU`T_tT0!Th 4VJtT`r|l /ʋjbM]䚤W嬂39(+6+R^2MԄ#xR/߶֤ǿ6jgL&~,{bZs5A$Kw#iی`r 6cǛˍs$3wAc=sAԞӁx zHZ+PM  7Tc_q`4*Nx-H)7ZDA-v3 vb@E|C>OăHœqbokƭ9.,сlr"0@=Vk?""_y#G3mV ηɴYwɤ|bJtA/V/~\m/Ow4d.lDOQ_%%Y:|o,҈pii"R;No8TFdޫl52nj00QUjZ cl}x/~&+d2uda3̂YYa f Fa+\ZV˃[( 9bE- ElhDdv~E*ԢYf>Z;+XvK̤ mVYa&^aq4#3B1,.#ecB.34c"nOSzl/Ytӣ\Ϩ0 (F A =ac,!-3`$hn{L? n]gjؓݓⱥ2U~iF;0l R&ZgGfb 6WW3i ڒ *LiUyfp3bk`FԴL" LkVjnUݢEccF|MH2jSd3؍4Ls|Ęzp4dmHjQ.6jYm &XX`*[ O7JK&I2HSjR'8FV[SyD!Y9CaV 0?CAr[A.rVA^we91<>.͓/A:7Cq6@ l˜}D%B2LN9s]}m=Qj%Qb(ef2 >+kDј<5(=T`{AtAFGu7@KBh -5R8}X\ŏkiqZhrQRPGۥɩXu1ZTCRn W qrV KZi{ N\|7gg7#2޻yIm7Zkcߊ|oEg˼\mttesUuBW 伡ĭWo`u|4:+zs)o &騛|(du]ˠl,8rEJqNm\nCo.vwǮI7jnݎݝ# ՚WO.:.њZ"Qe[m٘Z9ZW)LƤi>~.̄GI 1Ǧhx\•~zi IFæm]lʬbw3na$M]]A'q>K~HtF8VYxVEL`ҒCH]i&ͥ9xN12fW wb'cbll(HZ-W4ښE]LSD6loAՒywnJ3] b0]"4&qYVCp'zᢧuȏՇ@&HF~~̹*QVGhT8 n( s=4uaZKr;!ɶ8nnzEU*F19I#s<3A]D4kd]#5r4MmZ4&34p1\V Ec5g,{_Hֿp ?ĞW>p[5,&ֶ-5]3fL7,Lwgƿ}P]jN1o%Ee8ewͯu8—|ɶ̿d`ޅ|ycfA8uZƂ&dDVtSCvNgXӔZJ 5~Xˎ%{g ym˷pܫ-QdbEW鴥WvB͐wiqO;ݧ=崇m̊M_G@,Li Qqzg|SSʥ9\1#6rwn_mWFWYi\~!.GZ:e&YZĕ:2ZWiRfrL6{L8c8DI}EX[dej?(:/O%Xs򽑰]NK̟\|_{G6x+,^J̿QCʳzX!2X?Tן?U$6IXT:^cqQ2qǽⶣl45ƛmb F"Fv:B!5VcOWU1$RdW7ٗdCI2$O&O%do]qQ#)aw9=WMGEG8;vב L,7M6i)?*U_K6knJab0\J=6qw-:2y0syeMgsy U%!E|"yjS1- >2!_ ŽUHwjJ &;N@<;Er\B NJ;n߂1OO?.Jun6[ɡ3|]dctP?b]=B G[O\Ɲ|1 S&AR>*Ӛe^h/_[̼+lԵ ~uGeAaJjFN+:kN1%/w})_ 77m0%rG_JN$< ua;rO/,\۟rW!(9$jP0DM*J.ɫh1(te3ȰC1)1"R5#\13NF3AFO#@Jwg{~5a~"$>UaFFcj Ř +bgi!yq; qdmitlp9!TiE]-|=ffR;sUw*CO  :/]VJRT1qS&ҋmvz{\O00*-gI4*Mn0cčhi?.~y8fL Xa&ۥ۫#R[T 18h>c]RGT>qL_t3޹~j9QgF]6«EVh+ *VȔN.v]_<4Ld.,!VpEIBŽ$J>ߤMq7=]&ťg8=# O4ٝɷ@3Qܦn$TFt6aժ@+-s.Uh)/z[{'YYv/!7{ڶ׆gŅk[>;w<]C 0OCN r4f J (ndNs* rX)]{;+_vgy|MS|Kox`.y,ɭzWjm=d#W4j>ְZPsR0z`LjUW39rFsi:m6af.x<^LNp c' Qi 6fDʤI;iNsܪVb߫ܫ#1'UBbG?UG|A^s^5v. %=/=+#ø<1Ic(e0];[# ŲXRy5CzL' iF%ˬ1 G6!Lqշqll4cr|r8jEH%}'tTdJC-]]EZ^Ր5jX#c.q>X 5^{狤$ǧGPe)Y8q7$Yfsw.t#>@"Mr H"OVkqxBS%yf[81P_c'H%z!#EIcT*kEN6c:ӓ% 6;STEnQRן(3/lD;2dŚyXCzq=sV"_wӗOY- 0rQkE+eWzܟ~DrOoUnU *]]*U.>wTzʭn#  z86&+iZEEe=2 RD'N7Hp[rfܳ}}Y&@8 ms =zΌ sbNLGx8{%})~qXEOje"uwW7'ph"MͿ#?Z*=irlOhwY!jTǮ|l'O^i7.P@SxAѫ x|Zw,G@gY$ +ӭۺJ6Y[IJVw#!+:[t#8- VQmj%fu+Nw@QBWRn+=%V gK{"ë&]8X2SR t 3j]'愷PyyG;B*Zxn~q[d[;wGϨ : i4U󞆔aa-j⚄&c})I7Lt )as^aUUjjj+5>x&s`4TWW jajamWq8eњZfj;#5ٚL6le4bcVٴe[]^ n :éT.[<tdqCٰ% kmh:d2Z@CgaR*8ԌqC.,rj=n8 E5] B7GDf{XC9}T,r!u9od@ Rd͙fkV,Lg%5WbiK.ɨTlddUoӷC2澶߿⯝6㛙d>7lWfUTIT4QY6}Z kBUs3-J:mnI&oܑ'l5NcwT Ue0 <TńB0z^|q%g): ) ȇ&J)ѴѾrk,7;6~ulEruk_JM\fI[jm**4̓,bLk«PNȉt͍0s$ACƗ4e،蘢T﫪k!?f ڦZ#g>v=ɞeYv J:h/d4򒜝U=gd#x#6CWhG>Y*<@f1Y5A5^ݪYB` D:l:2 K9x*1eQsQɑ=AgO5'?{ }K!+b3ZS'FO> tW۟')'_%Wo~ 3'Nb֥4zh1 \=}xRgNɤr!R7/r:FYE[.q~{8!`^70GwDgKOZ-zlZBX\JLu)r!@r9h1ƪBş:Z*ݱ~a7p/k{SP.ВVC|;/?>t'pץg*a(R{ܯEU&nrZuMkŝʛjrWLPrQ?oE<4^X:VFp&0R908j#i< )dL1+$K8#{l?%`"S95Q;c_ 9;v]lG^|w6D*wFmD{"O_ JD`, /W9bةX79^ĵٚ tMtz}/?z]MݷuO^'L6+~ᝏ8p7x%9q%nߞ_];‡?qM;&lڅ1:6%Is:}R 90 >nUv&0,&SKXRav)M*|W%i( iAa1ДXcJ7[t 3H! =|DCMEe?: 2pA |h^ T&)PԉLBZti~~3v'ȣnԟE N &&L[å-ǝ9tP]|M6SVHNG%t\%G>H\IXd ^WRJ6ݸ wUwBЗ7#|r~6aℲ ܌/yFW׈緳-_>;^cVUTvJBx |![X^!֗or<z:o<ΨEr\tB,Z]K 4/#=_;B}7iHᑂe1VAmQXQ^k v)10ʉWKp]ŹJR)7u2u*Ť*pߓLk,#}47>O:&[.n̿bx=p,G28,,;x#>g$x؀Way JnWRex+KbJύvu DJOc o$8`RA<d~ie W$/n5B=.*-3}wz,|"XG~a\XHIpO|~uǸ: ׅ54,#_?[K=x c'n);Iӯ;{!tV$DCc|,Jf#{}9A9a"mCp݄wUoF{A pjm9BZt d:KxRwJ먯И_(mT2kdn_/bNإvxӡzP~;5n*SŒ~,;<x|ERgVFW~OwzxAtx؜\[-fc}݃ ̱o3<7.)ʮai"Ψ+F$bT[{缮|1v ;5ʍmQru=]j4'kiJ5rDGoU߭;E*yFciD Nz`bMIP7vE!⋐/*KvؗMP|i6׈X4-2 m$6|BD dTQEkdx(h@$dz H=Lw:E葯BQӥ 4 ǜM?Ώm|)9gY֝ޞVMkf7]oֆ//O W4#%|G5Hh C aRqAW֤nCr)ͱyjb &hk5pGsQҕ^sDzM}n pL*S{¤R)%mN)5eEeZI.}bSVHptMĀɴՌךYydxCmqF\l,a(>kӡ:%1⏩Op4ҋ@5cDB8b0.eN?~#$+Y$3dħ3] QZlV3',kխqEf_91mgCc:_h[]FcZ~oj&1:^.VŪÕ}/'c~D}=Vq}V 7R*y~vG$͗Q (Ж8Vw0QS+, ju:{#(&Hahh"cOVBq'coٞECȃu ԟ{.DYԩN@ce 36!)0IW->j۩ q:X{:='Щ}03ꂮ+اkζp~J̚"gqwL;Hm +Joe/]E *R~V[ j9~i 40DSnerC@ h#$y D[,*;tg* 9dڹy 9R͂7TNg‚Zl4iCv { Bya U|\q2Tuu/.3.,^6ZEYmst|nvcΠ3|}DYWS&/47KجA DӯDb!,H- *y cO f./,J0>%X%Gs%X"B`57kЀ%XVSU=R2ayU8^12% R3`sHkv V qo p V"SUTYikPZv׎3y%X,n/zrZQ9Jg + r{N2<]UF 4r/@#׆ 4r\FK0m.@#w%h `rxjt&P^+_ږ+k6F[<.rQ08 xKi_U;p'eQ,sW} hSVr)Ek֗:z(B?: wJ{HNREsh-^uϿwWEpXNj"buH@6Jr~خ([8gR([F-ny8^`;<6DQ]n [\ ,#Z}8p-(j*QpSJ s֥[,Swбӗy:Q4wdkӝ,xx!cϗc8r6 ;q!wb.w;v$exΜ,$W Wg}Q4;5r5KWp.~TyM,|fQqy)J(׺D F n]qk#i)qfשQ7Iql}|1aڵQq=㮮'"VP;{RE54YżdkQ$-KOqB&6VmCl@\5#խ:m̟66Wҋ,ae;YGGF8ŞąY K{{pmCՃ{3~aGᷳSV1,;0xQ-HV.TϷ.JwVMswL,\Wټ>N>%0+]e-4S8ugt\q]Z~n!wt[|i5S U(͖*y|ER툱͢Y %T/UA-pu3lq/ѫ8ntdUڍj8n^\YEqv({5{/H|-spPux?/IOyyѱ˗%B.X[=+HswB{O^?YНJ#jw;ێz;r q!:x} _lwbk+qX=i%E)! 3L4Yx&)QScZF`izJM(1g2c)IAɤb [njQvDdzMQ BStd8hZ'5,R#(m*L4i$&MZq\_aa.Ղ [BU)Q;hf5;r &ԔϺcfDb$beOõѸj iHS$6 ZxTK$h hDO4&Τ`2aLb$=ˉaX:>4ˉ SC81ьDbVl1J'i36Nkmvzw[Tk5d+B-n'mIm-i:ģIJ9pJKP$ý{{{({==z==)$ 9 :X Lt,֢6242jLr)pBOI0UIX)ZQ pUX[ U)$NI0˂^mLhNg " 5, `W)dI8VI5ᒢZfnU!#)U`Mn%D֢:B֕+"Uc1oL&ۖkzRw#(/-۩yiL@g" ݊<;pUzZqԵ ,Dx$MT3S LWofFjj:6[O|\zXY׏c}d"Ε2EMm>>hw-jgI99CY}\̊IE\&ygKXͅO ~˟^> 恛Z=O ~Suutm |^%/ޗ>@ͰP%,Ipuoa5Z0Q6OkwL]kj0o fλU=1$ ht @^DE^cH%Ҩ4Et ۽} ܏;8 9Y7?%rwixTҀ]ʀ4+"~/x.H!߶;dazl5k˫՞Ո:Yn;Z~_~x U}o}aְҾ, >ܖR+mqu=XGF<븭V|}#Ⱦ]vFUwPK\p<aW.*Яsw_6$].+ <|N &a8P_N 36-\*w8g7w^dn8ڗ7|e1}ٓ&4:rY|XA- 4؄ !bM瘭=ݳӏ^bB&Y1d geml"6ă#XjAbaUv7164Xf;0 dΨ4<(.4:<Su[h]_YSߋE{418d6TDQfcAJDU-_ʬ]%=#ѐ֝SCj_;7o\3:׷ h>::sGf˅.nsUȇcFWx WʦjW RK%L$d'֠%>@݆+.k]BPۣwYed2MhL-HںGd`O6t{_Dq zLTtR$Eww=cFLK𕑷B?2&OO7ϙ?:385e녨D/~ l JB>x}=H-RKOM36Qѩsv:U+h)_H)3NԎZ$B}z/j+E2m55 N@ZheZ4,C8?S̋ endstream endobj 14 0 obj 22477 endobj 15 0 obj <> endobj 16 0 obj <> stream x]n0E| &Dʢ5pR ,̥Est]o}?ahwnRwKhڻcCi^{j$yr? Uoq7'Jݶt-@:wօ]RiPfH-Cل51JpQ3g¤-s|!\JDl{%LμB]x üOl_ U޻hF7Ҥٝ endstream endobj 17 0 obj <> endobj 18 0 obj <> stream xTO[U?/E6R*f_hiHڵ(!s836)Z◾ז}mmˤQѩe[u Y4фh733o@?eQcdax^y1ޗ|{y'@%U-rTmbVg 7>eEBE[P!R iԁ1-;n5 =tc<~SikX ucOȁ'}|Jf50 z k3BQ) ..(*b,5Ȓ\ࠔ@~ D1w'Y? STm1YENmf{鑋??$MD r"')3v>]m-b !~4fE&y L0s?g` XQu=-->{h=\&2} 'I4%DG:c#P2aomnmYTp@ >>D0 ܻaeiHBrf!v؇r)%D% j!AHc#^9i=b<kQ^+㘛 > endstream endobj 19 0 obj 1076 endobj 20 0 obj <> endobj 21 0 obj <> stream x]n <ajwWcuCna$/Mz`fH^Z%yw 0r=R$Brw'fnLtY"s+= "VjĻq()*,`<3& ѵoOKĭqLJJ+\6M@bsdW&Jk98m~Liv |9)1p鱾z^~|OwG I7W< endstream endobj 22 0 obj <> endobj 23 0 obj <> stream x|y`[{e[[)#;B Ɗ-"eKrI0[(WKZ; 6)B)7BpmrX}GH|׿~H73޳ Okg=% #mǷ!}@gcp/0l9О?; `忚o2:+,7b9+|`yngK )X~] zn|jmXF? ¯ʫy{Os2u˔IY1ḇǰ ,H;c/h υ2q|LaO*ɅY) ,PdA#Oa$Ak~ȴу(a!llx| ˢ=/dXŦG/A9L:;7h.C~vωj G>gG@MX㮇qdah EA vQ uɃ vS2Jߖ6dU3*pfaj sUfCE(al0$L#kɏwsҽW!\،:]?_w"RDGvQBLBqȁ`]aGk.e5Eƞx?uF-dYCd;3cIt Ygw5?~O~{PwZWMbc9N>N:Χ4B?5^v#^JJ ҏ]r|X5hSD"_qA&m݋ڂ|x^MxH,I'd YM֐N$ג &K[C4}U3ب#Kuҹ&),]n(RՆ{ 75|.'_51yјh|tF(~ Fɭh;1:}fݛx F dBlM'y[]MVrEFI,y@^%?_Qʨ:h&unzz3>q}L#fBV-d~4MJEk/ wKKdՐd2 W96y-_"_*%*)STnU~hL6fY;I?H`gf482H!'R/]"F~)WJٽp-#" B ,~HFW1ZO0-;IgaNz 5!gz@ayh6yMl~ǤT"hLWk B.iWD3p CVM ,fv4'f ?I)rrE>11< 7VI/EIDUP>s$WivO6f?g"\.CB4L>##$#iV4z'!7gDbC.c2*Ab8n^]f}fe.Z˙^NhXU"ODmlꉜlGLp:ujLu kj3A/4KtbQFHZ1XRvcj2k|1y3ƌHjfNIZE2k"ZBey"+V6զ;yRӚ! #`1LD(bϧW; ^=j -ض6yMmc$qHꅇlEXӴmjk:U^ܦFlf}iv}`=}5heՈ)saf-萴pyv!NlltF3;apݣOliMЬ3ޢqS D;VMp2cDV5iĉT[+ ?{E e:C5SG><ƫ֏<8& qԠ#Q\y챪| 5+D;ܫWz`"[W6ie6-ex˴5exDL ߇zZ8kbMI!)_Vg6 2WE9s`}Z?2nݐZ3wafe=WGnItyAc>0%(c(Ez(,bm=>ˬ/b>T!m/s ΄lsc@8xW4Ϸ{L,R!^~M9L6Pann7+UMåpay#l$,.XwB @2<&D)vK}õLC Ɉ Fig&v[)b2<(CFFiFI|7nzIeo!ku}m V5֧utgSq^ 0DdI~#U˦1sMʫnrljl6̊+L(kP1O(=6r8BoU~/QnT(,.$8Z bxKi<1I *lf Xe",$ iQ*TvGI_Zr \kI 7CG뎯 Bs5 TmO9 8TeVLYV$}guܼ6~io ߌf_D ElȴIƳ8ja( 6fGa: +G4}X˔2 2LYk8GjQQKK9"4}y<~Č ˄IԀ-˪ɼf#&Ie,7 a/%+enz,tH!QkJ(ݽY|C_ÿ qqPsSF:EK'`B]Jec 1|\p^/R-FT&!?.ȹ<{e %&P̉i]W]ڝ7peN’EK>w\q1ѴKw}ci9YlF@"Y$œQx>`!É|Vl8'YÙ{fX]f\@-xY?=;ʲ<`78=oq(.\tWcPg Ő"KbҪ]%e!pN[nm*i}*)J>vj/^͍ȱ# ;yoT}|: *Eq%Vrqzq/X'DŽlFfӒS\9sVܲ2~P担`oM)).++';\/}*$6^TuK?K\xi3!iW.::TBiv]E~7_Ǝl<`2ʊbHFYM_advJgaVu~VG(ӆb6Ke1wI٩1qeR6d̊cƼb~-g!(gE+9+FSm6X)lFlFY$cT;$`cs WDK;7\CyU9rƋ+n跶W΂cg>Cf\J-#R/)ef.˕f́"-=+u26X{ e9=eؖcWpVzEyM6˝SPTatfS[l'ۘݜXnfr)]1VPQ%m ؐ봘Ksfkct&.`.4Ŭ3'!¼ݼ|ԬG/=e$  5hn9\suOCj 9d=$U}/\Zm*p4VH8S ),/זh"ZjKL:R8f޷gY1ym8Ȝ7`Ņw{3Yӵᚆ W,nZWPX຤7_|Tl=U/\2Dm[/lp\+0Dy  YSZh8m50d?{O2IS#U8;(Z RGv<)g'YAJȌ|Z/TW$:h$^e"#&)h5İ cZlKOKr$Fug]Ϙ0ɨD+R+JVGNYwrA+T051S>2~29R W$s?aѶW%ש>t3!rtWbzȷ윹{g]a$ˍVj|xh0ҳ<%+lt56:FlbMQSX֙j%(2KqZ$˻km"qkDD~FS5CEqE8&Z >o=k:=qg7`/J-{yb=5Yg8G4jHLZɤEigaM,m;*7 J疗9y~/9ס>cw'<)9;ٓ`:{7oX~?b6o0.J]tL&]D/bz_M&G"ӓ1te<.찐x~DY,REZ^D )rr%2B5^giS}zsşt~̷DTÍ3?rv{I7ߐEeҥ!Lbq;$9l dRE278lm)4Ő '(VkJaAr!Yz[yvkv))˒qoSd$itN{<[_/hѴ]ll[V<^'ejbMiL,jJȟ6&}S%,RRb̠$3mR|L|5 oyfKYbf\fbE˸xVrs+ɜL Jg<{Gc<'#LfدOYC)})L[RRг!Lُ'#=ߡ n+\Ĉ)l< *b6~X` KʳYh;Ky-yWgKwEs˓_f~56X@3[y1g's.ILdm1`/V>$ՇlvNQC@2">4ȯ/{seFQ<c޲7c+>8^E?e2`rrk8?q6JX%<,E`y}p 8(I+/ΔØqPʏ\k<_B5C\ԍ׮S|)-L#JLJ!FfNf3ɥ:mLOO?fq gt:F[f`t,u6x8rÄUX45ְN3(ni A:2:-C9V`6BRBN.aNPo­:m9IOa/I8vnrNCA*"qH=dA>V̲-ɶ@]*hvN/l|Ns9mg47-MQNZN4Lۮߴ_47uF47}~XѿAF:4yN47'"nٱ:+h3gni f_ X>4}O?XKm9:-AAf$n t{>#7>#i\c:mN4ni=,tNO=@y ly:-AV^yC:z= \Ns3AUq(?Aoi^ӱvOZ%N (4 U:ЍֹT̚"ͯ^ k:RT8 &xBX6 4uXVcNīF!,zBy! lkr݋ mBrXQ7} Ag|F\ 9O[Bo7+$N̟ySxnCyR sOXCQ' .u zQ{@ޟ o)^c^|(_ Nԃ_QV'1/b#taQ/j7{PE|kZ/5B݂\^=8 ۷-ƚ,Ѝ8~Y_Z{x7uHWQ70FztmB"Nٍ{ȫ'FSS< / S,mza"zN L&U5}MzrvGh7j+KB;rriZ]uLO&Q);uLg\x[7)gM^"W*Y{}!]#Y&ar _iѡ  u h=LnDd. \ =Di2z_B__>!OXA/֏[v El". O\wmeSk…5*Z'#MMXGY-r&o K1Ҭ5#bKq Ďƶy\-\S3lN]!R'R]Hu D)E h//"FMefFj08kP׳E}֬+Mu]*j-F@)bM։f1vIόgq?o71fDnyr?-1GsfH_DN䫵Yz.0ic2㹳";盈a}=x*{Bn1)G57%C.1-e䓣sb^q⨝Cz`d8>1̥eoШGX֯tU=qy&iOESNy)q;wv]Bx\N/Ixs9t&31u3b'{n~BfZW=)9$N}a8տ~wJ:ŝGZ'V!u<1lv'p~F>ƽg\?N sTߘ˹ElP{A) p{{|go+&Ɓ_էޫ6vԥ@Ԛ@'n@ SW:xMH]܍TV㥸@TW7vC*_k mⰷ:.zhP @jRk0 gĹv h獺FQsMjk'}/kSjY׬VWx,q@vΐ &$9ޞU>6wo}h|uU`^otzCy\z}m8bEqM;Ԑ 6mcw~l }jG_W[;Ao+N ۭb=҃B'E}_'Sa@4FEZѨp;!w+}UX:b(>o x> ;*uv")t8/S :}S-9G݂#؄bk9hsك }AډP|hn+{{z|hVύ.t:T;\FS7>^+SBRš>l_+)DT8L'Q/ܚ C=>x{P5diC{V+,/5U, ?p+ oء׉>ek/_.W^\SluZ}֪ubb;ЬV&>AEqaaS,11|ԁ@ʣ ,֑"Fѿݻ1H,P[ /#>A<|8tF?'. l ~ }ajpEM+4)&:hS7{;0¼! 5"fgs3W ZtNVz<&0*"a꠰X_RA_ )GQDŽڷ,]?g@ՂWЉ {,n^}^/ح -C>~_N>CO0M9Z"'}'յn?XD}poxgX79EsL5 XY4gܹx-/)WJ+K+bb}bB]=N֧OM>z08#95وh3S\کҾ Y"hw*髹=,m(=mݒXs<~ ︎㫹}r|g*Df} T'il=i@٩ ԈGWICZ !HeRΔJP4HX7 'q=jaS762R4NU< }&a<_u=kI?L\SϿ+ߦ6Cp+pn#%ŞQtxA G#u Q>WC1=]4$&kWlƖTu9uQ ,z;eg,7#fGjjev-J-)-p \"{ݔT\XdKi s^"<6Gk ",GhA}mÖ̞[OxɈZE؁.ʼnÞb/FWXmoa"&xV z砩/#%$vպڝ*x2ǟ Sxv\YYvs9W-\R,$\BdIβ~e`z%WcaoW/$ +vaasyN,g_pWx}fws$&saĜmO a-kGc#2)\NwH@b:NoʯίM)SI3{=zpSM! CS+N>`j?6P|p(?Z CC$wuCbM;.imƺ:DN 900"MBZh endstream endobj 24 0 obj 11373 endobj 25 0 obj <> endobj 26 0 obj <> stream x]Mn e8i$R&QEa@J]a1#kV+:#:tPZ:FI^PEq$ n=LLU-fV9K^H7Mn &О2RTyOEն! -bQ0[.q=iu O~܅<2VEc|8!e !AO\"" wWۤK6XB\#~@P~RR endstream endobj 27 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> /ProcSet[/PDF/Text/ImageC/ImageI/ImageB] >> endobj 1 0 obj <>/Contents 2 0 R>> endobj 30 0 obj <> endobj 31 0 obj < /Dest[1 0 R/XYZ 28.4 801.6 0]/Parent 30 0 R>> endobj 7 0 obj <> endobj 32 0 obj <> endobj 33 0 obj < /Producer /CreationDate(D:20090218123212+01'00')>> endobj xref 0 34 0000000000 65535 f 0000076621 00000 n 0000000019 00000 n 0000002071 00000 n 0000002092 00000 n 0000025583 00000 n 0000026121 00000 n 0000076982 00000 n 0000026141 00000 n 0000037861 00000 n 0000037883 00000 n 0000038092 00000 n 0000038440 00000 n 0000038660 00000 n 0000061224 00000 n 0000061247 00000 n 0000061447 00000 n 0000061948 00000 n 0000062301 00000 n 0000063463 00000 n 0000063485 00000 n 0000063676 00000 n 0000064015 00000 n 0000064208 00000 n 0000075668 00000 n 0000075691 00000 n 0000075895 00000 n 0000076245 00000 n 0000076459 00000 n 0000076522 00000 n 0000076764 00000 n 0000076820 00000 n 0000077081 00000 n 0000077195 00000 n trailer < <87EC36CB9AAB53896045EC8FD9907664> ] /DocChecksum /3257A41855B0C2C94AE2759EAADE67C8 >> startxref 77382 %%EOF wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/fig0.ggb0000644000175000017500000000230512014170666025055 0ustar georgeskgeorgeskPKœK5 geogebra.xmlXn6}~ZuY$/ $yhJ6Rc II(/e8}]ZPQ-l;<)Rgcze4ŌƂi!r"ǖkWOxA$C}[RTBZ,[+ؒ B%[SUQ_oxD$c)#\mV MBR-XxUOXstvK{*%`W"d%6,mqe%4&|m}+`Zi⨿ N3dJh# |_a]cMLzf&KpH;L*L%(H}KT%#Wshv_[ -ĺ;t;x`DҨpJ t8W2[De4d!(yti9h9zt YcBkF dB%paBf^Mz7AOϾNK*UCg/cl/"? ֥&r(t]k7;& 9ӬN -'<;ܽ 27.Bc,=g $_>Ưr{9ai1Ӄ޽O}y7&]S%u/PK';PKœK5'; geogebra.xmlPK:uwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/distance_point_ellipse.odt0000644000175000017500000052123712014170666031011 0ustar georgeskgeorgeskPKL~<^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKL~<Configurations2/statusbar/PKL~<'Configurations2/accelerator/current.xmlPKPKL~<Configurations2/floater/PKL~<Configurations2/popupmenu/PKL~<Configurations2/progressbar/PKL~<Configurations2/menubar/PKL~<Configurations2/toolbar/PKL~<Configurations2/images/Bitmaps/PKL~<$Object 40/Configurations2/statusbar/PKL~<"Object 40/Configurations2/floater/PKL~<$Object 40/Configurations2/popupmenu/PKL~<&Object 40/Configurations2/progressbar/PKL~<"Object 40/Configurations2/menubar/PKL~<"Object 40/Configurations2/toolbar/PKL~<)Object 40/Configurations2/images/Bitmaps/PKL~<Object 40/content.xmlMj0gvYO ڢ)T;Ye2hMĄ=^cϴXM|_ϦUD*ׅTb;vip[mN9\PiY\xY sR q@r/8(ȂY(sXYnUo6kL4n WVcJc;a+ĒF hFRr뺏\nJ؃hc˿ ׇPK2h^PKL~<Object 40/settings.xmlQo0)ukTPV hl&v &2RF=!~?/y  Zvf[<)WыC`Bjt&vˎ9s*\FB\\Y셔8ryu|oootlF秢(D܁ę Q];:No7JuleZVHr5;os2NuP&vd-nu3i "uQwE7_M\ p%bNJYN@L'ၿb\y\?/]" Ej0wQJ )˷|ffBn?t}UXv~&!2u8%A% tv22YFvN6."@ݞ@@iB_z1^fS$t2S4 2癨m8 $vFo0vj5Ӭ`T$FE$vШ=J31}1H r⽂<,)Uh $Cqݓu`ڙ=?# Jy~w>1@uo]0/c/ <`|[y Ę6Cyԏ%5K3_Npn* hH3۫rʏ Nakv\k%<ɷy戼UP/IJɽ%aH^DtU$KWIFK8 Q CI΀qD<]M7X1$ YJ@F%-j>|JI9ZʦQJ4t'>ˢdu8`H `J8 _tIME 8œ IDATxyxT{d&; YI $@X Vw]֥Vmֵj]j[RY-VHP'kȾ}lu53g=!3<eZ@}v=㝖1W;ѡ -o裏pBvi:sC۵kWB u-b;Fi)?֭[J'NTvvvec - Qկ_4a„NQh}ݧZqJII$5J!1ؼy~i;.X,7n;zzp8BPac -@{=Mlr)\@33B 1dZ#tÇw:Z@];vˣ٬ezE{~vvvS-"߯F%%%}7*??B 8>x -YD111JKK;Ǵh!B 8>:::ӟTK/=ǭ^Z!z8Z'жm4mڴ.)'g:RQQw2L]@"##ZqqwEsf͚SLZ >SKZz衇hƍ oc -z$IW]uUbz:Bk֯_O? e6u]wvFiʕ+)n:7$M2EF:mN/^Ltm۶颋.$%''/_i͛tY{y]yqYn< ?YW]u˃w!+.٬CnDEEE?>|N?PLL;X0aT.KK{3$FJ t -$9˝[LMLR^'$Mb;m[}oJz)pq7x$)9jۿ*o.$y&M p -H55JN=ZYº{۾g]$fo<&1O@ިU ;˜ٸS͸-PBwYIcuHn~$D\VWuoy-+>2gl_H ˓ɓJx|^R^7Tou[NNU?-%x*&<'B Pulf&LR~Z~2$_Yd1[zªj$)75WWOz_.qFA(rsB dVvbvHy4Q^q/'=zZ}߼QdFNIȰYT'չ$IWLB3 0 ]IOץ8zzRWN@z~z˔A(ZSZ-9ٶg$)*,J ?kDzt١PH@vhmڐf -uN&/5,ao[9˜ZWN8MI|)5* Ap|um/frs5?-_3 -?$?I&e%fI)d Z˔)zGSM(8npHr GC{ TP^ gS}P >Zjuju`yu9~K:eG@qZW.o|պJW (jݷ>9˜Z[Vn,a2Yiː|MK8"lS8 k1Y4>i|Hy4Aa0@ϊ R,*3!3NN[!B >Y̖kTZݯY++VY攳ܩy:nVbv-o藳~X{,Phާۮ5kBkQm !9˝5@ nz&X^T# -s<> k C]>.h!D܋k^ԪUC(Z\zmkjwlӤII򔕘%ASG~O{򨏝8*fS5N|$(N`߯jI%ٗh9Bzִ!ӴtYHy:EQaQIҖ-z'$Ip=r#B\^[j;7ݠ@qPp<~$鎙whxpBݢU,s@2*ծd؎xL愮z{ے!Ct̻B XCkֆM7mPS&ѭ5xRdX$PhjcFY̩uU낻wjOWc㹂紩v$izt}kB Ϻ\bhbrk|8ڶZ=O{)~yiy!M2i!uR$9lq%VC{$IWiڐiB2ILyySC0Q+,I ҃s$ -FX)gST#&<]N~Zf c_dҵo_+{-&'׼}@6Mi -_sGst9{{Tۜ7z{ۃ)(̩"?hnjQZy|:Y>%%y;B iZ]ZI 8?$_ىٔYZEuEZ{E&R^ssd =/?-_/zA# )SS**, &0 bt%=]*+ \pZ-G]::{'P#PU+WY攳.Iɓy$.UTVR`ҷ'=)D0Z#{5S<߫dVvbvp!4QppXjv7K˽NS& iu;FǏ)SR(2,+$ņS' -@>z]8BWnj9 nan~f2$I̾GCUE*VYTtxϽwk 1iY2I؄  vZ'gSeSͦhGNrQZ88pAy 3|!뭩\3N$0>OEE,sjmZ}.n ΆZ_2nvXVOkZLMHMC5!i,a n]̄C59eQ+ t,E"48bpHyMU=Z:$I11Gm2@%ڪr97yOn:mSf-<> k C -(/ 0(~ïuC5jw8٦e&d"Z/04!lr=ɬIWNr­=fd2iԠQ O)SE`Ky/Ӱaz`JJ! -ϙTRdRic@/pBo$Ik*h+e20a+iiRE"H}oujU*zZ5oq~ByڴreI=٣A;T+TsGZ|WLϭ YTau|/uS p<l$]09P(T\WR^VU9˝Z1~_$)GOP9a1@B!<_:E/UV}A0^_~O~_ ER$I%u0w]^5UG(ޕA|%+c, Vn:<\2=]7ސ$u]zpB06]jlɯf2 |VX~ 7d +ڳǧFVZ[-jk3|Ӽ2y{ܯ^Y^oCc+2ҧhC& dV\E(pjjgSC{~?Z%|گZCuu&54XgE^o(!ݧpl6CVfb ]yW }}_d v><IVky{ZQTzb`:t͕!CG}*yyM 3n7jm׻}#فe毌pun1,zWTo5+.άx)1QJJ u|dVϯ|^?;g28B ~J^UVT]SM:,jh:~/H&!//÷@}r8v_6 dWtt"""eu.]-OwXx59u-RTMQQbj[.kϞVӱXV}x7ުz pBoo6+%zQ` 0B O3 ~QWZKK}ڵ˯ݻ AJ l(::PN\R|o6%%+9١hL0ny͇򍽿 .xN FLY |tA-jhhWKYMMֽjl9pqrC7ت-[X,bcJI+=]>ܢ#Ȑ10@|/}׻v{ ::g&*s)r95k`4.P'poQzS#IO8UNP&z@7U55n56Z:&<ϕQZO`wIMe+K /龂cG;vxk_fUUTWgbRÛif+>ޭx<*>ޣA<تdJLtjݷKq8OPpןev|\ NHU!gMIzxi{R}}*+]|Z &`r:lZ͆|JO74lIÇތ|8}aHUURIIR\QqO%%VsUEDiಯNI1+-ͦDGEKƨS/I2$W-ԝ+/$ foT+VZ{w}-a{h&IJ ! f )CJ#GNoW1B R>_`Dڸѭ"JJ mnQii\/~Ucc=JH/ kjMGሒλȁ/ ٳ|G9fHZ))*/-QY6't^?PfC_ťVvkSmM655ocҮ]%-[vu}3F;֢1cLi̘tD T=v쐶n?ںue_۷vP|| Y IDATG ^%'5dH222.8ͅǓ3|["pf%9(ОiW%J O٬TBD'.544]>~Sk}ΞըQ~kVVUcQR8;@-p\ҶmjIyO%%v8փLfJLt+%C))n%'{!#qHbQ|}a[3|C]4k3E=Nnw(%š޶z׷,6UW[UY*i\eUGwM&C՘1RVUa3F1B|#@i.izP**jfm3nWRR:VFIÇ4r]Cڙ1ʷt{T06g$)L;N9ՅKƨ I'}qno6纺VSEE*+TYS23}a$B Cv^զMf8>%'+%ŭ)::z1B1uK'DPkzIiU&sK4I=Vx$)g eǽVUhVv2Lnھ0C#Fnv4i4yr].Ph>-0ںaC`uZ6lLL̾)ÆY +zl}DOmS0[$vUh~.8G;Z$IOyI|P+TYRIK۷{s 쪪 W{MkX&N4gQ^^rr(@z-[+Zڣ ٴ{wa"1ѭv ڮiȑv IaEW^?TYf5JNWVVTW?b$)>D a$\JvanmmQe;rymvUW>դI&Z4iR`Dw0i+*,0#0`TZuCkU\l;p5RtG.edthhƏ+++R{-[fICTfnj34+}R> WORFƁnKK<*- ΝvΝ᪪ aΝV)֗_/|rk$CSZ5mZ%#вª׷s-L(-Viժ}>J7v- )=C{G^;4zYCD0 i2d(O٢Aቝ;$l٬o. QٮlRZpBFR_ƍ.eI;wUZPGǡ__,Cc4y4uU'FsΆ8{e5[U|cFI0-OڴIs }_7WPJJ`W# efZ#2#$E0-0X-2\{XfװL]>&scMw4xN:ɡN nowN۶uhfC;wڃ^TTdUQ+QvG&˳iڴ0efJfIҝީvo$n"#sv6GmZ̫+-*,t n <5#íѣJHfeŀ߲Wt+e4r^N6?:ZIj4%c]#Iˌp2ac@tivmWII;vU^8CEGI'Y5}I' %$)w~/Ζ$%F$jO(K0ТohnV>ԣ+|ZzL]=Mc;֪,f7a`v_.^2VU%ION{W3<[h%]/uIL?fb\'@{K ڲŭ͛;õsC;wrFk OO&Nl6rE|T$=vr^ &ϥ/0b_EEf&I=:P`Gvix+ppL:2u=Y7VqU7]8:Bǀg;PJ4k!ծv&+ܲ%R{%%fH۞_'vh4N:ɪOWZ9씔) :a="]ڴ!|7>զ,&O)77BVtKƪ.ɪޠQc|u]%j)m#|^# 4i۶&]m#v:[pm1äSY}SC{2!,_f E0ZҊ冖/4*4 edF^4ɪL"" 87'$7eHLIS/ӢW5# ydRtt&MդIO%XbC[8TR͛ ylyUo!Ff('Љ'UyΑ6Xf/e-/pҢE>XaV{{ׯnãGmQbb6g5h|ܴ9#R`$\M'l)/wi[gh&n8):͝kg5v^X +ծiX0ǎaHEEx%Kƃ:FDx:ה)6My5z#)k&hZޑHw=)brWfyuf͛C=ֻ[ޕ$zuߜZt-[{n&-_nWMZLHnSNYE+:WB XٷrMM -[ڲš͛#vΆ hCt&%%%w_>[)2,`@ѫ5m>$\vth4aB PDDvNPh$ڵ-Z£U*.r7lN;ͬsM:d)p}nݪ)S9;Џ1B aIRAIQھ=Ba:H9kTΕ<3\'`RX9nV3(Lr^TnZ.B&rz-Com6MSx4cUӧG(2WB ?&7 "iSJK~Pd_[u&Ǔ#ݺfP@ŁUVJ(~(\.0E~͜iWZZ4Z -0mSqc6lRaa]xYgҴiL~(8Cצ_*/?-5iRu %&QbPhQmjj矷O-Z2FUUyty&v$(3`KoihRZ[<am1 F[Fpr)7!Ig-{ B;ll[&\. mԩ-6,\11XZǧܶ6i׮F}re6lycЌr{yرoUPhrC/خ_61DEy߮ٳM=;BѼ嶦A}SAA֬Uc7fK/5骫L@۫_Ow.>CsX4mZl6J, -]nhVqڽNnM78@< GcU{{]sFjլY:T؅@-MM{eKV֚5q**!~xϗڤOV(E%kYUWybzKgmѸqXZܖhٲX}iv]xa=O=@EٶM?|?߯m:G : N:)^6G嶱q6lix57w~ݦ.s+m>IWW\'ABnHoij"<Ђj2jܹ: )(p {zvY%%0hG\o̙ Phqjkg UVv0=ݥNڣyL7.QVf8@VRb>*3ӥ}]7XgZ ?^2#SA:qG99Qd4+-_wxBwI[4a/k*hrd/w1c>}q'&Su9ӡJ, - vY7l6=Q?G^8 7E[{c?P@퍚^驧 GiΜ:]|O&K1 -O6xv裏zCGmGv<cAVjҟ&icFIIW (IIwk|ZZB@&'3ju&ed$SdPhzUz~#i0O~O%Ci5+d6 ڞfҢE^=G~ha6E"U|c !Ph{S:M_yng>CMۣŋ7Q[F3~{5颋(]xӵxbIN~1 ZMUk׆w*n>;NaaYiǎbmVR\V\`edQVV.{^WZ̥ ST\rNN^p~՛Eo] I7\~Iv`@=n^yQ?PaaWKn+E8۰s7ܼG1"KaavԔkʏT: u5Rrr:On+|Ү RTRr)b{[Iv(lm۳M("xA? ( VDѺ;u%7(66>d=ϧ ?gjooSX]_N<Zl.mׂڶ-bnƤ3O]$2-r1{΀B{aZ>n}qF,#BڬUo7IҤI3() EV\ yb;j=@yZ.IoG++˯l2b-B,6~@KxmYܼ⫯.SO3G)>2 =E~ښrrN%ݧѝ[b{)I+e%Ia8sNqB{ۥkөZiSXpuzjJKK9e'=*+m2t뭏v;:\mٲwU7_kǮczYhڠBzᅈzXefQd Y6{0ᄣv[[Keunjau b֏,s_d B3700*kuu{Ka3'" d2骫~-ۯ|j=GLe5qOK0 mKK`o85]O=ՠ`)Л ھ}SȲw謤ݘ1ا@/1bDۯ;( VVZtꩆ~k|>2L]:p^/`|ꩍzey?c۶EG"|F||.x~ZM{>ߚ5k[wЯ aH4}-[u8|zHEG3* ђ%|GȲpν[RIN@bZ=D=֮K/-ؓdŊ0 IM7Inw̎ѪH^Ћ_:-><Α(*ZrN}FHz$~{|Z nIҞ=]pE?q~25]SNj۶yUJMK^iuZ>sYo|Ň\>~|>Ojܹ5F>hʕqgTu_eU}jD:D_FEyu[um&eddPf[N&I3fmƊwZf24e<zQ.YH>@-kxt>f1NNnSOUK)>>g?,!!E))Ce ڸiyfd :'@`Yut[pRN<ѣr\]^sTS(.=Drr>xueݷ+ESVN?=FEٸѦi|ڴ|>Sh?ȫ.UccѪ'hј1<{TVlqݶӲX]pωOgң*1CTVf̙~Xq}PhE2%02զ^Y苚tZ6dnZnyW\qb @dwT>/R\zt= $\Bb5]Ib$TjT,QEUR.H[Xݙ)v z>3gfyϷ95x^z$I6Ml飏hB,'ptŖqPRRb 'H$۾r_~|}9)<^AA}A%I.3r=o.T+9 /9Qz p"KNNmǓpǭܹϷgOJLL:^rCrJ$Ѧ΋i۶#X=v'Iq@EtQUWǻׯVn.3.55s}aqz57:u&OEЩt떫YB۷Qc]g9KX)KfNDK. kÆxKlAsOHΠ_a %%4gΓ;Dw )gą IDAT$[7+>|㵵ӭJNN?Yv+#%>%IJJԻ7-Y9n}P,mݗG /G>ؠߧuAY֡; g>*EA#H>8fͪѐ!!DƏDv{֯?IF^}Zt~{@7|x^Ng|^ZUtnsuRlh;g]wݟ1cƍ@'sv}_~u+.7B2ƍ7oaěfRQD}$nعNha"R]zif4hƌ_+!mZ:Ɔ \ں i_\^ (0.tEe; ͘>w~x${zf vd֬;%I}4?+=qYw?]hd^lT__-ǧo~@JMg)##G7`(3Y`K;|s1_R\h;~cI;l6K?iHh ݔ)W^$/~1]Pp _M7MQCC&OB((./??W7o[jhכz)͛Faq3,p̅v,K?>>Ϙv,N}%_fvhza;nq =ϴhч{k3f23)h˖"}{z 6ִi7䓇qb`hT7ԤS$I7ؤef|v?/I3Lf,f sEuqz:h+k,ւԒ%TVV*ve+33WCQ&jI9!kVd[ZСW TT.Iyt$1yYN|0 ]c4xIR ]Zi`y{ƒeJk5+_Oy資Z rLΙ#UUŻ0Uϧ>6V}:Z7NRA;߱+99>oU5v?:ٿD@/CEUDZj-D.8EQ͕|nr4eJ$)3ߛ>?71W\6ܤND@ۑkDPݻudNQ$ɔ7U|fo|L3|@Ǻ SG<>ظ$2}zɶ5LVf&qx狘Ky QقM9g74F5a>V~Y;VǶI۶IM@{ЂAܝNS^hpuAm{T2+ݭCoLZKz|_[4sBmn,q.F4]Y==WxT_^޽i]'W?Hƶ]}!ztjux#L1A=\-j=nɠ J~GO$)fEҦt'-S̊9<"@{|U7Dz>aܼuQKmueUz`uݧ=vذpY.+HЌ_iΤ"ۭ+Åui* ln)C]TQ$兘8ޞzW ?ydv+_V0͸w %'E(n388#3quʖ$f=>]1ҳpX CUSv;=oCe7캴 zcRS9xȊv:2S6S(hU8v["pIthh|ŭWn#M.yyQg |1%&ک>c>I^n\|ݥk^Uip#n/+YΠda4$Ԟ2Lg~;]A[Eh%i6mS#rۥ{_9vJ]=GQw$o'$Dpe$Ucۚre^G)*[daYGv+㷧M+ӝwR}8C&߾W>DJKokjϙ.Kw58McMIq;4IR34s>]J=ǿKmwpSy8UG8}qiLC0YܛWFf7VK?iZzi lJ?c}A6lH$-sTRߦ!1 {_=[ʼnYHvk=_7pˀߍ~K<}M|)//ӟiR~~|K=CvҠA74wn-g]Z7_:cMȽDqpfJJ|=#o V!ygb,-c岻۷W׺ l6 awu:4>vӦ}A#g8HhD=U]Kt!O]@=" C瞶g穤3k+ْ${( GҥJ#G[i۶nOϪ93~xz͟)IrM]iiڣ[߲ItIڸY,\X?1o۠# G'KTWtUTq=WJT4n5gA=&Ni22b+RB-oB!;b/3zt|Ga@=W^nL=CUU)@RSSD3gjU|Y̱6 sSJ/l =dS8r>+4sf@Kz%I>_L/Р=ŴiCC2MN2͚W_z<1_矟Nq@=C /uݍ)//=ti0kwK.a}h[W]eןoj:UV2]'{Këmm2Nq@=3f g?N-:[_UXVrYzMCSR-M9PfMn9UeF gٲJrK,:1ty8$]}]l(33Nme[?I^zijj 7t*-fd7 ]tN@+Ig%-[fIRs]}O&E Et~=@cdh2.'t= 暶5|z+WR]]3 UfVriӂZĩ~)$y /#1lqK$kc*,FG%JS?ܠW_FeY'_j$wҴi~|s|>g .**i\+VU륗W^ɓinݢzuiXe}iҴh4dHXd^}5E\_3 2;55bIz0;~|X+V8@W ԿC˖tKxgG*UQ,8>B=LD?@ZҺL͛RV5T$SzD-\XotM7%jB֬bET\cW/{4w6蒁}K㭵[ew8#կصe;~!ntm֮5X8H'PYDڨ u5eJӳID"NdֺZOt9 8=ZZ¡;0[پݫriJఋF#ZznIߟf=N˗aЩ[hwxt͖/6Z9.Q7SJ 3B B/֜9 <9թ~[W#zy)''&IFmzd]qٳk3'h0K/^,@=M:ifliJzt]sMϯ`(p89sgb.àVeIqqT?QXk3k4cFHg[41gY??=I7ҐFqo6 ۂaX:Z]}uDcdlh…z,-Z&l&ү~e$.qkGM=k#5}z@SBB  4sk+fg[64cvS+@{ǘ\{A^Z ̥N@7 *-u{,'̙nMJHVHO=o~cigπ.BPn.NUUB@olXAATw5HD$"4UTԾ6%%uQ$C6ECCFnЏ~UW2@LS?xqj kʔMTNp,Y/lن }74ۥ h]Q?_ӟLh`jjX'袋"/V[Z8#z[6t5yWh2fi˖}T Ұa~MR]Φhgۖ.ܽ[qVVD?A@?qh4>|oڷȦ5qb.(>}h@aƈ^=?ƵumQ}^\{ZSi3zRI#Fkʔb-- ܹA͝kӺuOG嗇tMzC!ZXLzxܹLiRRDZS:4h@=F--XR\XS$Ҿn4qbPi|x8lm~O[ھ}32:ZM JL$ ͙GyT[{~f͘|zZQѨI/`BmA[Sc0mht-y'sm*.vxjjT]:Dl\'ZuқoZzᅨs$Ӥct9 o @ Ӱ,KMM~*+ hd-^+4iRKW xRU%ٳcCaX8Q'7sͥb&%KlYwoDhfHT|hq+)^yҋ/ZҾv4yrXSI@p|F#*ʕZ$YKjt6yf"9'uٳM͞ӦMwc5^gnnh,˒עE-X`ג%)t.KcH6@DҒ%{.9s*/߽ˍf䓛tiMСL@ Z>4O?uhٲd=>w̘x2i@j@%b'Xz|Ӯz7Φq|JL$ 82!ܯSVHV(aiK_nӥJK ?[a_=vKn hĈ٠Ov)%%%hӴn]>,B֬IT,{?aԩ1]yC_l(- b/6lһZZУ=@jְa9QjWvv*@m j°/6WԸ??9ǐKhq,n@Ӽym۶Ԁ 1Ag) DZjlpa@ FlK۶=Eu%1MҸq@Ųbw޳'R8҈ 3&SNq(''Q_Z:!6lMMViժD^}|:f}tahqT46JIki\K۶kaX#4rO(//[@ F֬iԗ_ZZ:Q_ɜj Æ5kҤ=סr#-v4wߎЮht5//C#3ƮO*'p hBnmRaaLK:zu{}~7Ykg>-s~GGE`իeg7kР& ؤMtW ItS1Ӵqc/nʕ֬IPi?L}S M"I hq>҇`Uধտ h(LTb"LZGJ(T Р[gاb|{X/sY1wCgmh1[CyQ-\ԪU^ö>?994|xDF4hO>jĩ^+VDnW'olvKԩy9uE=hхBҢE҂TXhS w} ԯ_H}էOPݻ{rA^ ں%hvOÂޘN9%24q[ga(%Z {H˖I[㨾®[뤓ԷoPJC:Y. ]tʐ-hJC_V]:,^yh1͛曰lTZZ]vб|>S4bѣ>\2DJH@sRQQ<._ʕ֮i6~fQvvX99GCzԷWt_8PPMM ڱ#Mbڱýssԭrb1~0,aiC ݅{l6jZkۏ]Ru_$&Fx[zvG%$xAE @ՅTVRi뀻HMWi@C;5d[ILZ˫ܵk**o,mlWIK>a*++3FӼ3uIn338qkccªvʥ**+r&=Bew , hN[[32=h0˒jjo"*.jFif˝I$%%FڵvP^W))^hqkMMoʩ*]ڥ~-}ŌaGSҀr0t^LMoȐƌ$-Yڼ-GT\lj&C%%\_T}S{UZZTEkWn{vVWkhtکt56f-YӦ~n\k߾^;'nwB=maײxm G~͛mk)ߩ-[:h4wވ?-Gϗ$V_3Djj "fUTl554ҿKI1ճԫmL-[nvha6͕h}~KK=?myd^Ia*)ܥGCvef:hz3%& @dXeQE,&=RccoQ;[:oKm!fk ˖ vY!ð>w]n)11x MIj(-ͮ JMmвzZ8P|;~SQ~KMMRSSKwcgXx]-C v|vypٲ%%9#@NʲPhVLґH->}ͦBBB!SᰥHDF En-۶xw.6%h_VT,fnԳgs-[pr8r8lr8miky$t@t9 @@ @@ @@ @@ @@ @@ -@ -@ -@ -Z-Z@g)ɏK&jgIENDB`PKL~<[?-Pictures/10000201000003D0000002707AEE4682.pngPNG  IHDRpaʼnsRGBbKGD pHYsnu>tIME T IDATxy\T2by]J~Yf咖y-Sk.7󶸦-T25S P'uXey=s|Pl&bQe6.٬Pp8P @@ 4h@ 4hKsiܸqjР\\\-___mذ!\+))'4uT%$$,^X 4ƍ5sL]pAvܩ}/ 5nXrp(jtww͛պukM>]FRʕK.\Yp\bOLL5p@9R .,]v[[ζ@Γ/RӧOd҂ TzuǏ[-bcc%I7nP=駟jƌ6m=yںu:ukٲe<@1b$aÆ߿HL&zZj)**J=vءÇ߾I_Zj#Fhɒ%<P94~>@׮]$?_"""ԣGݾ}[zRPP:wK-[q3fj׮nݺT@0Y,KQ}sfY2 ͗׼qfbccpw}X,ݻ6oެ+ѣ)RaW Ty .ܫWinӦMY.]͛7K͛W³$[{Vtt"7 @+Vn?Y=}&L IW Rd?ה)S$IӢEx{ǎӑ#GY=znݺ%I4iL&Sl-[TΝa<@ΝuYնmL8p@~$Y+/=ߟZ`Aokvzi{ϺݥK/_zʺtRL(\퓟JRթSt ~-2-{%mٲźߩS|?W^ծ]ti%&&N:z'榸8\˗l2խ[WҪU裏jРA2eJZ5kfNec͚5ڿu?RD@W^]111}Ţ-ZԩS$ɤSN[ªL2[L,[uM7N+WfΜqƩlٲ2-s=gs{LʕQ޸q$ Qݺuy ALLz-رczG(-ruu$$$Xó$լYSjղ)2e())I򋵬*Us)=#Zr*T;vhĉ6Y^uEGGkÆ crtZj'aŋ6ǎ;F#uZIj߾}eCBBgݯZjom۶ ,_fMݾ}[ݥK/MQ0Ξ=atʯjw>|~jr}ߠ u]QQQCY^:WR%+>>>h]ld2Ğ?f=WRϞ={1bD]tɺKRbb;d Oz>}"""4~xڵk@n]ڵke+짞;;wG}d^_~edb *`-ZHժUCt񒔔X6ligI޲e"""5jP۶mvܹ67W9KƍSbbfΜ?Db Ǐ+&&ƺ!;W}M6Ӯ낂W_Yk֬ի3 j˗Wʕyw}oV͛7א!Cҝg&n]t$hf?<<ڵf7xCIII;3q\6mS'ɤyeX&mf&n]d+@WTI|X^ŋm5kS/^'N詧tzF;-h///ѣub(888k._X̙p+o{g&,,,8lEDDhrrrҜ9s2-WftiEV\\9bng<5%ݱ֐S5i$*UzY/{⢧z}ZiΡC]vOy>8z-[ *hڴiٖO;]hh(3q)'xJqpЫj߾}{xxxLeXg+={Ν;OdГ_ҥKӧ/_533111:zuĈ#G^GrvvV~d2c-X@:tڵk .k.^Qcƌч~hLޒm6%$$HZjnݺ烍7꧟~Uf3RGOGBt ]v+W+bkԩY^3bرCׯ9sX'ruuՌ3gL26_h۶mےkIL&< >>^o$i߿]:tH?1ZDvᎊ<`\ԩSUjUIFk[,o2_裏>Rf"jJSNӧm³$UZUcƌ*WAСC9!!!޻wo&' ,Й3gԺukó$իWO&3qśɒElVxxfBCCoO?-IP\^6lP߾}%Igd]=sZvfș/~?Յ <Ξ=KTln?쳹ϒњ;h Iu"y]VNNNZj9L0A111ٳgót17@.rmf|87.ϯb nZnСCTd>˗5l0L&-Y$]ݻw?fϞx衇l @%͜mriͪYvޭQF:HnЌ3K/DD2f/B l-f1^zK5RTT+VH2f7o^[jԨ.]szsÇkɒ%2e Os>u1988hҤI~|0ݱPph֭Ǐ[gٷ?cL&͘1C5j{W^]w&L='[i&+<ȑ#1c$EyP\tvA%‘zpɤ͛[G7oj開/*T5uTM}Ν;/OgϞtǂm&@.0:uR6m:uꔆ kjܸqZre'''M6MWTTTN*Ν;UV-|pM66zww^ZNҞ={4k,߿-Z'N dXsjݺu:Ծ}{=Zڵ[EL6M/dJw.GՆ |XլY3+K&I;vP&Mth U4ũ(9$H?1Q`X5cR%yR[}[a_$[ǷYN^GNTJiq_V:HzIc 4J˗åjդ_RǪU3\L} @ڹSjTZ\JHȼ\BQiS @Tnݤ+W]?hWJ}Jqq96.Nx @D2Ř4,""ߦQ]*yUh@F eK+!A@:бҺuxVΊY}sLʲ,Ik'v~-fe'4Mz M %]4v)3II7"]^z)bRkdb_}_ᒤhòW&kv2~OnhE(@eg[adz8{P;fX,B]9R1vڵ [Y#yaUGE٣vZ̟NYk5Q"yzs~6tEfI4p r5]2I: Թs s*o%$77-qvP$$7?O(ȋ00JhyhhnnR*6],Ԭi3<\JH> =Ϥ$#D"@Kt@2܅[b"1h S[ @&R@Ӆ@n4J,V$I~h@2 7e0OѴ@=pTHj,g9E 4J00?IGH ŋ ghFZЀX kA @9 t@2Anh :OΝ;qƩArqq|}}aÆ,rƏ,,۷k {=qΩN0Rq?/Y, t,^X 4ƍ5sL]pAvܩ}/ 5nXrpȸ5aO-[T TF u]~~~JLLuww͛պu\\ȑ#pL_?..NuM9OOO5oOvSJzlNدM"zj6md%iҥڼy$i޼y ǏgIJܻwo[:t,єʖ5i@Ɗ+ٍC>}&L IW {6zʔ)}iѢE<=R\qα4t;#GX}}},?zhݺuK4i$L\wei̴lSL˗y3<͛t֭[gvttT۶m3-{}$ggg/WLHHP@@uCruu-ϗcbbӝ&@_mݮW^A޳nwE˗=cs-@g~ꩧK.4sPPmۦI&rttC?ΪRԩ=zhĉ PRR0:uJ'Nh"Ӳ.]ҖTS4w)M=d2eϜ9իW>w)...Qjժ%h^lYi?Oޔxւ ǓO>/RGUPP*U EDD(::ZΝ5`jΜ9ܹԩk %^z6e֮]L 6dݲe\㟽լY3~``|}}UN 2D'Oo'|Rj_|~]Kk0]ͷ 7P4h:;]SʕU|yUZU:uҬYtkssiԨQVzuXn޼:uXϛL&>}ڦLLL5_ަlVYҁ]vnϚ5K:tɓ'իW/C+V|իW:@>|X!!!BnވIwgɴlm-^X~):_?\]]? :u|͚5UV-2*S/X˺e;viN=a͞=[|OmڴI[nգG=z`$K^ú4 ]BDž%mZm3㏫jժ6ǦMF.Ho߾}eCBBlr"㣏>ڵkaÆ,>LcڰaL~~I!@Ӻrͱ͛+k4ib~#@_}ajժKnѢBBB4}t}z衇23s|/,jӦM{ŅT M&SY,ڻwu&Lg)]Shk.+VOv6lXɽ2_?G3 9C]Wt|<=z<mt Ndd8`s~&NN-$$DFU?}{yJtNJJ6linnt&I/]ݹsG~Mrtoi7e0hggjUEZ?$k׮6:~<TRRԾ}{ 8P=ʗ/?OǏWL~Z*uNի97lZ6l͛uʕ+moW-Zcccm˗/ʕ+%).N\qU+Vw*&&&0T777;1b<<<):% 9?%%%Yu]tt}];{3x7mڴezfMi>c^=䷴[7oM6)!!AnRTTΟ?ÇkÆ :{bcc5i$mٲEg϶+=Xm2J*TL]zUwb2ԣG?i=gL=].^h߬Y3~k3Db@:zh|ҥKU^]>7n;j;w[/رfΜI.ڵku]kXl=SP]ݷ>_g}VJ>sXmkǎ&@32ݹsL;::j…6y,))ISN՜9s)..NGg7vj 뺳g̙3VZe{MTTz;wH|}}r\ԟEO=`-h`]I=X͘1#ݱ~&km^wk}}}eZHۿ~YOJJҠAt&=zh۶m*SL?C]v 7Ppnݺݻw{IVƍm%$$?&@㟟x +Aj]jLkd24zhm޼Y0tLL=j1bvg ?^qϨ7?L.&MѣG[[o;ӧOg{Mhhh?CZ~WM6irttg޶m\n̈́t}ƶ_-hWV-ۼE.vn{T\f"?Hnʲ|֭oVcƌ^xUV8q\\\+,,LovlKzak?mӭdHoZ SIcݮPwТEԷo_EFFj?~|ppPn ӕ$oo"12{/,=0uUFݷsɓ'nP"[>lfݻ d oFk׮VZ%OOO~kPmpt\\vܙxg+-۶m3:qVXӧO_СC?h6lL&,Ybxiyp+11Q;v9G}X|y kԨ'|b`?~\z\v<߫\rڼyj֬ݻwkԨQEbbbԳgOEDDhƌz饗́ڵyhn߾}N3CoFa.]̙3mCV\es*))Iz5j(+V$j޼yv_oooQFZtΝ{OΝ;ӧ>%Khʔ)Pr)AK uņ {Gڵkz'm&sttG}5 Щh֭Ǐ[gٷ?cL&͘1C5j{W^]w&L='[i&嶐3ݸoܐ2nݺ5k֤;~ՎլY3cժUƍꫯX%U3dRͭG͛75x`+T۷kԩ>>1cBBB&v\5 lVhh$ݻzG(ɤ~M9sV^qiΜ92ב#Gd-Y&۽{٣ &[.ƌ1WN{ZxnݪK.ɔ}NdT{-gxLHeH @F)7e0g'S$^ .IItb˷U\F}58, V3o+LKu#a~akQPD7nrIի?d@@i!Z%^rlXK;4Rjҋ/J)cժe._(M >P:)5m*-_nJLӦ5hZUrs3i@i ݺIW͕+5vRdJ}tuUo_c1^ R]#D%Ք)y,"Bzm4PD7n\WJw_gժ #!v~Ye"1[d=ao-]8mڴi~XiС,*oܨjw$y!9 mjtj~}lYf$.˷EْU%x5@gV6.NScV-gOꗲ,e)KYR%x_RB Qc %#eXVCjv7}+M kK/e[UꝽ{Is'nRT~3\([z˦nNMQ,e)KYRM*4Y% άW7'M#@cf$كl_e+W<= 4KYR,e)KْR|Bޙ,+[mFT5ڵ ,5@Aڳ؎1@qXN?3/7Hhd=j>mx^]5K ˟CJ^^i(nڻv?NSڰ@773Kg}ԩC@Ҳeŋݥ^F48T\\[qmil}&@Jfѭ%$@bHwKignhm<YQ{-}c; @hy8{ȯ]ԫgHJVlFp>z3FpL&_cGcԩҿ-%/VٲҐ!{!4#@D2{s?c;$ {'$ĘI{* s/U{w'Ƹo-)8Izl1~ӓ dKmR'(JG4PHח:}""KW؞ZXypchK4'>K?vҘ1RƜ; @E^EGC~Ws4oe9_?Ojӆ"@ @ Ü#Ho^Blϕ//\&uEJ8c#ҙ3Rb1J7˥ %R3ƒTUn{qb./'yvm:wN׿O?bblϵl)Կߜ^hK; PݴJJJ=׫;vd*4@ =zP'ݻ`b{\9iiX~ҋ H V,N=-꫒'uEi >J?->Xv\Æ2T>k>h UH+Jׯ P9bo^֘86.]ݺ1h [& ҅ ҭ[+PY,\2?a87mJ]!cTFRE{8P\I+VHæϕ*I'KU߄gdhXy]>9@KRP)@q!-Y"-^,]b{vmi8cVm77 h|)ǎQɓgϩ=M%GG h _m@eH`[؞sp3&kݚJ^{qt&b)ol6+<<\fY|[( W(EGK5jHS'EMRg2&M|y嗥ѣ5+2ڋ3t,-ZLLF7={%(HL_31c\u4P(52$Զ-up/etMesa~aTȝi2i\EsISHO>i"=DbVTG…vj]k8wHpX ޸xјQ㏍zKjՊz(2*UHZ ٳUg587lH=p %-Mm1i`n]99<;;K#GJNI}FxƽE 4JZQ#) _=F57n=^Ǎz(|^t24@Y,FŬY?؞\Y;VzuR% hXI=n5޽U|SzeF~$@L~-9sKG؞{Acb^0;IJ*Uq7. Қ5$`5nl,E5`D*(952tDt励2+-_.͝+۞kZ2ESr`M"'zqT<R7@Ʈ]fΔx=;ux~ ii> (hztPԹ3uei|iMsuLx@1(nݒ5[oIQQqGG`O5+ +trhPܾm>0J =;R:@ CIJ]ҬYŋ)M&i@CQOKA,_W`c&wOE;ҪU̙RXɔ}vH4` |FF3fիޓΟ=׻4mԴ)B֨aDNbv4ct=ӥ-'د zqbO=رcN]u98\֭'.@D/N4PJW2d?n{K#8oO=hWOrr2L( q1 ѣ:u2pwH=! gI@9S%IcR6R>}{i駟(’A߹#:E}wR)۴^ڽ[zqcmghb4((I?/5o.}m&M-[{] (&R8h"#%??~}icܳ$ը!}t萱4wD Z@~uK׿>SW,M")+G=h"kݹC 4ȝDi*cfS+'+M(UH=htA-$5h >,H3u$[o+z$sp^|XLJz@ ҨޕN5N@v&L=KfI^Y3iZc sǎ-[ACcMgY0 7K-[l}8f2I/,9;SOc 4(^yGZ\JJJ9ީ3]dh Ԭ)Udl @r4oT')V-믥~"<-( sv.].^V;^X-[7ސNJ9^رRrL} ~ v5&K&8Iry|E IDAThSOQ'w?c8^a$@EHa/O 4NBx4}tz0y׏%{tp=h4Pp1֎y(,m1ɔnnɒ]"AjBڹS 5^((a￷=>d4kzQؽ8S@Iݍ!)%5mj;tV"< @*@3 -\(խ+-Z$ݽkQCZNڵKjՊzPp (}x1WWc97ߔ\\#hXy!?o3 cml,C(NRҠA HM>;wJ&sq&NƏ7<(AH |kYcsPXU/dX,E͙fl6+44o ;O@zwJKJo-ݸrᇍeڶB`fk/qdD geJM͛ i'=4jTJxXԁgE]вo">,=(u$EFJo%-_n{|c0oo¨, PN8h$OMsӦҪUgE-u4rm;4cG (Mt~ܪmx4ȘdsX34P+'5jdltu(=,?yי::%(YS~_T_I!)ʔIC%J"d:e1e0ef gμ}̹},WvzioVyjUID@ȿøavCDD;}{w(U ^},١b$"JE;5$"""a 1mTC`(N"ZD ( ϖ &/tz+@d$*N"Rtih֬ag[PqQ-^S7hɓ&%DDD<6x”*_^1Q-E" Y̗{n]=?""rul'f^V` hR1Q-rnmh0JEDj:p~V< o SFq*<|y}+/- |Ӕ:s޻7L \(QJEs5?- ~E+oV06ntY0s&t(KCfp+H;{^zɬ}iX'aD(SF1'OTZ qƽn!""/* 65ssFf4JED@ZED :Bvgi1!DD$4[ AHA3<g[{YDDN="WHfCDDDC\t=z8xmg%""Jɟɰahذ!%K$88vڱ`,ow1NzzeرcIMMՓøO|&A^޵+ (o6 6d…L0C1f֬YC= x/&MO>RSSyG)[,Zb֭)SEo2DFG:HKAEDC=D`` 7f/^} VY l :HDD cmo׮]:3g0j(,WD-h߾#>zX/a}lڤx)и1.ҫiJT4+fm___nKyf{/}wUf&''ɲ^@AlfZa"s\Y1Q}}׎zQ*%+_z%vN?w7o&.n̙3Kv-m̓ӧOcǎ????ʖ-KժU O>L6_9=n6nt?$\tRDD@_Q{w7o9rŋ;۶m:r|Ǽ{4hЀZjÇsjԨAZ =k,9EՃ :KH 2+Wrq{c:uƍ'66e˖1d?Χ~3>*U/55{:~^fMjժvLR'==_\&.]Z[[v.&co.dpall YϗH~h;32rH .8J^D0/ӧ!ۍ9I#"nq~EAfrڵ/={p9~*U.7(X,t)׿#ߐW 1= ))r$"" W&- ]tv^z5qRcp萳alQCϕHaR$۰aC[gA΍8ؿkhԨe'111:s@ɒ^TːO?fZs\\LU:8Pr2 lewMg%"<}+@[,nq;˔)]r~n"sUbE ~s?u[[Nz_2Ub8|ի`Lg[}; G""J=2ep_؄◱NJ+2&?O8 T-"?~Lm/,bP#ŧOС&Y>p*eRX5kQbcc9rc?$$K}3&p큶X,yN/g.SzЫVAZb""r{}AcǎLmuԡRJvZh, fצ lO> >>znDD@{8{۾ow׮]ߎڵkuFΘK * 月'[]@'pm`/QLbrשEDD &ݻiIM믎bŊqmrf}R/-[ƍopTԩSXn[[y;o7[8nunL1TFq^ ^ sضmc?շ]w}mFLLLosNy!!!*Uʱ/걸>%KrwBZ[D$~G< L7:g__3\T,"Đf_h۶moۮ];~WNmqL tr)j*fΜ?;;wLҥuzya""sϝ;wٹq= h֯X1ZDD 897}||x'˗/mo޺u+{?&000ǿ/99.+ 4Hg `+|hərT}.-pn”2<͛MoHH+Um Ȍݱcl-[GѾ}{ƌíޚ#K-[p4|ŋ}z_]HNlݺ5ӂڵ/unF0ׇ_W^1"""E& *;?.cQFѯ_?}Y 㫯L/qtz9 ɽo?- L1sY,sfM7)""by@QJ\\V.>>J*9[uOLLaÆTX-v)O=#==PFIvr}{aÆs:iəX~6W)/("vڱf]vQ^Kf^֮u]w뜋""'K,{lٲ;v <j\`=z^cWe޼yo]?VGao٠J8z >^5FEDDŊ`/ \{8pǧÌ0r$9livHC=PgݻЗkɒ%̛7???>%Eۓͪ""ri+VpK÷ag\6^ S*y33|u8ё FQI;||6le9s™3gׯ_:ѣLKsn(""NӦMVT)NݡHJ2V+؉HI] X,4ḵ?tPӧaaSlٲ,_c / /\}4&Mb W8ED7,H-Z󊉈=VAӦaYDD@ȿ஻vR)*RSah ˴ҥRA⊑H~;v8z@NZ[D`D3L"u¢hqs睦'ڞ@{""ϡY3XkeP#""JEʖ50`zdvTLD;>mM[/0b)'""ZDa"low=i Q|DDD (pftiC T|DDs)"#ILI$x aaԩpYVD̟*}-Zuy;_[_wa~]D\R)޷ձvl%,Z}*f:VX;‰,a5Ѵ&c]J""?i$W5 @`@[=v_q吒ŋ|б:VzֱJD'qIqXJ/b]O N4x0-;+*4ԶN޺:-^o_#3FDģ\3~zu.?Ve0gFZ tX#z|y&:evfه+l?$b""-9 sm]?C /""X";^<ĘU]>}L{͚(ڙC`vDD2e#Q-"W@nŋ,ǎA׮eKSU|DDD \o?7l>~{Q-"WT͚ *ƍpb""W0rYQv5l:+hIp"#AE8ozIm3 hB@GDE0eN?Lϵ\~ ͚0`L KBp#""O!)|j2ۮ[gVޱ7V\DHJ2+jխkFĴh߱|rA-"y"#ILI$x aa Hs+֭ZMʕٸw/.gL_ӦO_^RLDm<{:2rJU7U*q>ED<5J\R@+b ""ZD ) Gм9llSa R|DDD b%KTDDr/11>m3~}Q-"# _NdtW?΀D8+VY:GM/t抏h) tDTSMǪa".p뭰i 4#s侶SD ;e!!,gúED._m[^Z ##ILI$x aazBDD<ޟfl6OV+qqqXVbccldG_})&"rq~ y>>0v, ~WҺ5J\R@+/"19ޟ!"^ud=dzsZ޸qW.y)-%.\j1(^vIt=0L R^vJ ,\)m7f mL˖5 }g%"ETm-"A60a6m`6 Q-R䄆µךA3D& ΅f`z/ ?5j(F"""JE 3馎-'Odvu/ ({$gDhXns}OزBB%"Ip"#ZkW}sA۝?FApi+_\H PDDD@d@GDE0eݔ"tnϚsAěn f>oo CO~ NHСC-$]l6ӻ3pi7 =s*$1%⁄ s`׫=j%..Jɕ^c10~b"- p^>̛-Z`5J0}Ƌpn/cvbb"b PkSxgJ="^{w ?QLD =&OvUh.)>"""WzEbbᆱxV;wM7'ϝ;JEDD@Hl/[&6̘7[b`@`*hf!ٳ1xI8wδ5j6} .""ZDף߿́TDY(lgSOMд#""ZD D* XX1TOC.pi Kat(YR1Q-R"# _Ndtd|۷CV0m`6󿈈\}*c%^KEoԮsDlwyS xq4X#Ox? /ٳaDEj;r3sn̓ƍGFGH`@Bt2PPH^:\AApS\ME6lU?~sa9"EDУ>~>P1Μ~reS=2h$"""zEF]6ĕ|vrsVx:@!-Z᫯+!=&Og\$,\YDDD xQۯjV;sAjik܌yq-""ZD|8wƎKjjpɤV-xojي) 3@cVѧߦaÆ,\ &p!ƌÚ5kѣau~\|4i҄| 72eʰh"BBBtK&Cbf{TSVJK3=-ZͦƍuQ#HDDD t!CP#a СCcMƱc.\`ݛ3mڴBXU?Lpp0!!!KS<>wƌQLh:p:t瞃ְIM7Q]|DDD`X:u*]Orl6O !!.]0{lƏOxxxzeʔaҥmۖ={2k,~U>FrGőD {ϔZƴY,0lnR1Q]8qAШQ#z^zB׮]Uiӆ+W2p@^|Biٲ% bƌ^@GDE0eT `?m61B1oӧOcǎ????ʖ-KժU O>L6+£kW8M[͚fH(YR1"ZzL<zg:f?t҅:uDtt4۷gŊX yǏӤIYd wqםH+qIqXӄޜ:w40-Ν A3SN+ԩSgrA~'f͚Ar܍7ި^/'0 o2eIbJ" S@DD脄V+OLJ8Ko޽;-\rl߾jժyœ`zA`` ׯ~zECMU{+?7oUVnmŋ'.. *\9 ~hV@s)>|wU gw+>""z.>1n2y9s&-`ɗ<=z zJݻw{$&&ү_?>>n2.5o޼L/XmE-Z=Jڵy衇Xzu?nٳgOYӦMۮCETU.cاxzǎ;2թSJ*)]к5cY|9|۱YVgt5Krr2_j}~g@oݺ={ 2:DFk^5 L\'Ni&[n%SE\@ӧ55^$رn+?*6ԩSݻױ_fMǐ_K|v]i_E^>,4LP>Bz~}&/-[zN?߿?Э-88!Cnv GAV:DDDD t.Xmtoݺ%ݳgΝsWR%t-xd|Ǚ1f"!!3 ))п?DE9W閜8bhsBB?IIIn>}:ʕ+LJ1c`tsq~&N%uyHS ,""zVI6lؐzkP2ꛬY4Ǿ}uvvʹiXr%qqqѰaCzSO=@paK/¾}v-̜ O>fcٲeg,o~z|A0$Ϟ•d  ζMa,sG< F )}G b[nt/SL7cve{SҤI~f̘Abb"}<4mm5RbEW)U {Ϲ?jdxH6bbb8~[[ƍܹs̡CbҤIk׎Pf͚,Y7ݺ9%LhO49e$㏤ۗwQ +F%A)!ƍc߾}tЁ8?CfLԁ+`\%"""@hBCC<>c)גVj*v`` -Z .tldM-C8vXb ʖ-Wd7Î0cbܹsONTV#GߟTbW^P\\elX $)!97 <A0UW_…PoQB""zV tPPP맺kl޽;y~6m2uތ+lu].oB;maz_+×_d7$~W5jP^|/OGah6mrdz{P]MDDC;wm۶9[}}@?NNNGn$K*v|Ƅb\IuNƪdɒ}zuHr D\ϹrQbŊL S\vsz0{YÆv-̜  !x?o}`111nC'h@v r, C=ۮYƱmX{xg\-00GR|̙l|}!J̥y)X̓$؆ fAb;u)SB2`R ={pyca(VLpaADDυ@g1C}||x'˗/m]j*U1_RJ c"ɓ'KJrr2۷ow4H 3f͂&M]裐^fwe%Sڵ tnݜɳbpcVpW,"""J=(T_}n?tPG̎;طo_IIIq|9V\YjoEo׸qc_ zۼHMMe˖qzee)]03!EItttk׮MPPPߗL 8y~\_icj:ϚzhK2װB =/%J`SD 6mԩSYr%YLڵc{뒴m6Goqhh(+VXٳoݻk0Wݺ;'NW^)=>>L?۾};&L 111_jO Y Z ?,&׫V%ǵ\Lٲe;{y뭷8qovy9rgΜa˖- :ԭGb֭똣oeʵO>yٱ ==`y"$ /^0ެqb迱cRbE7nRy9o+5%d~X\ǎಜ=c@ɒz-h!w>> 6ߜ9s ̙3/G KGyҤIfN##9p%J?̶g;+Gb0c tu灊{#ah~fq1oTT\+1%$%Ō"W ɷ/]2̝ C:EDDD 8a`ݻٵk`zS%Xh5kdڵ 2@ӴixgHHHCOĉy #$$$L׮]?~<+A^2`Z=, ճ5#X15 v6C}|)1ME qckؼ[ɳhHժ?c3-۷yVxiػz[g%""""yUr←R2ҥIEFGH`@BB$=^ƍ3e d+G> 'O ?S’%Kuh21B@?Wnc:a1Ô;qgժSOPb%WF Y '%*oR~0rYGLoSNg1r"}| ̟W!BC͜{5E< }Ќ7)52ѯ ŊD3'V- 1>ج]2 hj9ۓg??S[{W,"""r-" \  !""BYÆr9KK!%%1uǡjU34JHDDυzEЩYݽ۬]<(\ ii{Uyq ` M,)GB4".Aqh-aVyӎ{Ծ*L1Qe:6 `Ѐ!_ % )\"fd9O>}nWH~hiDkx٤sG_&n=Z;VEwruw $M, WTk^im̨]WDh<b *4@^@ZD?-INI))O>ҤI҈Ґ!ʹtΟ7(gd9;[nذ0i {|w%1u7oH2x1O~b,u'"jj")?_*(]?kh#'&J &P@8Ng_/]2fiBi\U%I:um^y)z&~^SRl9qs8c9[Z*\MUUR/UҞrd1 WNO/o $K6|xм5@PJg_-6Mv]6MEE ZK}Z- @@ iCbxpVb+Z,J@fG/~E@P? ZɓS^^F{w hӧ 6ܛp朼-׭O?7*&&FWր(ph9++K7nTAA r+p?^'NPLL "+WԻᆱr۷OO|.I:tFω ~{JFMP]tQ=(hagϞu5jO>p@3IKKӀlX@дiO*44T| J@ 2-ƌσe6:6oެիW>s{,<<\gΜ@' n:t']jυV.OFK?fө^zI)));vPlldb5ھ}>Р:_~aÆ)00B_Om6s]v>;hh/^TXXjkk%߼nٲ߲^EyZ?WWWsκt$ct-ffsN3<_}hÇ5}tW tRUTTP@#*++o>v ZW_}egɷoKtnw}]v)??_555뮻+88XQQQvLp ՝;>>ǧWttVZ#FT˗/ׂ j*mڴIs trɛFII uQ:tHyyyѣGp8ݻS+]PP/BNjU߾} +p?PVV/^-[SNJHHPdd[}㏵{n=쳒$ժ2p ##C%I3f̨%-v]SNէ~J!/[Np84bڵ|lghaڷo?fs^|EG?OS(33SΝd,JM2k, 8z 5k֨SNJOO-?4}t]pA| Oeddȵoϙ3GNSE}\۷Wtt;&á %&&_ȑ#)haGCꩧo_h@W4e8qB1o9/_֩S}矻_os^^mٚfӱc$Ih1Z޶m?3 2ģY.թS'4 hiӮ;.}/|BktK&ܹsv=^ܪ^{ b!@Ϯݷ%^nkONI^d'._^^sAAA?G纶C{gUF]:hիW歟kjj$!*22ңy~|D hDjjL&MRDDG6D2 Vq!I lm\_;ghyYYY*//7CBBΣssss>wQ<<7ZTUUiŊbo~v4 WϮŋ5amܸQQQQС۱uuC\&~U;99Y]t 6'&&-khi&={܏Ȳe܂&BsFd͚5KQQQ孮.K%IM^P ~틪zplX4uTΫիg}֫h@ZZq<:z={G4;tH/^pY۴i6nN:8{}p}y=Ο?={ѹk֬q=a„Fi)tk.cW3w\n<駮\֕RO[;Tˠ5n I$oͮ'Oy.\K/dhɒ%^hꨨp[b{mu)77wht:0UBB6U]O]'kHuu %1<hhy-Ǝy/w}g/hX4N>]wWǩ6ĉ3g~i cС _WEDD(((H Pjj9C{oFՒ88}W~v}iyE N:y};77W˗/7|Iބ.h~"𮭆W]oIJ%&&Zaaa1ݫ]viΜ9qZj̙9s}5juk׮ es&C^^[@u~ҥ*+++Ba@3),,TAA?x&)--%9f}@U Wozs\hׯf͚2h s;??_O=]XZ`$u7Z;>}MF=ޱcV\iͣU}C&M2;v6oެm_6!ڵkkܸqtC999WQRR_ZjKFi(/[LEEE}J k׺-JLAb3..N 0))IY ~I'N8Q۷obь3*((ȫˇ.hnfiӦu]bV~_b(==]:t֭[@8qB/_6)..֧~3gjҥ ߿n7mV9rz- >\z/*""BׯWll,,4{]P&QwU\\Yfi…z)dhW\i𸼼<%''+++K=z믿-lhll TbbС-Z6Xݻww;.$$D׿躍d޽zה3g( @sx =s;($,[V44~x5-Z^x٣ɓ'vZ 8P.\~zVXXOyy}3ʕ+zj>|Xm۶ULLq=3wٳ[o}zGh"ݛ`Zn4|S0Ÿo߮O>D_|v{[nz衇4f?opYO4 0 h4BuTVflI ^chZԣ~=_k Ѐm@/ xecQ~&@@< 4Kݝ+..P``kA/7y\REWUI۵<'}{]{%P?S?7%9KK:ymd^c@L܀66kqŪU``v]IRTMTRr[5*J6m(dP?S?cq:^mdeTTT]åKMRf'%'7}NRLspƅ ,]{''{~^c@Lf 4<IDATh @YN[q6Mv]6MEEEpc.]ʌ0cx5~Phy5~e 4h @@h @@  @@ 4h 4h @4h @@  @@ 4h@ 4hu)JM׺IENDB`PKL~< layout-cachecd`d(Hf``p 0]AVU!AB(S $;rf @A kqPK\ZHEgPKL~<ObjectReplacements/Object 40 s qcd0dFid'@L 1iNn(F Kɀ@x*P*O Ȳ̀$CpIbQpenR~D2F +ԔpKi @ vPOE@R@: fiS3=`1\PK(68PKL~<ObjectReplacements/Object 10 s qcd0dFid;X  7YH:B@<(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T [@lF& =`1괿 Nci ;͠4Ӱ%\PK*UPKL~<ObjectReplacements/Object 11 s qcd0dFidՙX  7YHԀB@<(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T @lFҢ =`1괿 TtZ wN,;4laPK7l)PKL~<ObjectReplacements/Object 12 s qcd0dFideX  7YHڤB@<(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T @lFҢ =`1괿 Tt wN(;4laPK|2PKL~<ObjectReplacements/Object 13 s qcd0dFid{9@! F KI[PPj*P*6 O Ȳ,$?CHfnj_jBP~nbDr @ 31gfv@ ddhbVghC(@|4 b ;4Ym3\ %EI9W퀻\WIA]qü-p{a D&JaV2a fl>0Ze`9vC`NeU0-;Gt_*:͏A rjdǩ,fg3Cr~ q@C>C1HQepPxi@ښ v&_;NXa JnXU; W[CZKr>%u0{ t;Rzӌ: xaQ4tEuo0i>rP`eZqf;hZr2> KnW["'6EbxlS,PK̘PKL~<ObjectReplacements/Object 14 s qcd0dFidg0300109@! F KIt*~T8 TlT5'\fM`f@Fv f;6$ۋ ātRZR1?Nˀ4J t N34+L$CpIbQpenR~"vP*)ac`Pc9Civ hrxETMg0%^;-qP84^Ӭ\YaND́OX)-)cPKqr%ePKL~<ObjectReplacements/Object 20 s qcd0dFid'00<n@6 PT8 TlT5'\fM`f@Fv f;6$ۋ ātb)-dTi9) a)5wYf$K+ssrWIA ` J 0q( L:hr#-uNKNf9z iփiND́;)-)cPK U$ePKL~<ObjectReplacements/Object 23 s qcd0dFid10<,n@v PT8 TlT5'\fM`f@Fv f;6$ۋ ātRZR1?Nˀ4J t N34+L$CpIbQpenR~"vP*)ac`Pc9Civ hrxETMg0e"Yp iֽpY ݁;- iIN"RZ,RJ; [9PK_/$ePKL~<ObjectReplacements/Object 25 s qcd0dFid'00<n@6 PT8 TlT5'\`9uPdpXls b l. \9"`1|EY3R:k ?HL+BEv.<PK/~de PKL~<ObjectReplacements/Object 27 s qcd0dFid10<,n@vPT8 TlT5'\q3ãWVhǁMN{ tÝw". PKCPKL~<ObjectReplacements/Object 3 s qcd0dFid8D|n#%-J(5(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T ، { 2zcP2œ wp5N{ Tw4p9p6 \X\0pW1*) 0[FlItQϩ,r x)+ t:hs۠o٨ 4lPKҜ%PKL~<ObjectReplacements/Object 4 s qcd0dFid-8D|n#%J(5(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T [@lFҊ= =`1 zaNS;M}P8MN@Evlq8 l b.I, MAud-C#a$: tX $bJ!ii6h%6*d4%vB) [w6PKeA%PKL~<ObjectReplacements/Object 5 s qcd0dFid00R6f `PK)7PKL~<ObjectReplacements/Object 8 s qcd0dFid,n#%J(5(U 'dYYL &@!$(27)?g9#tjf`f,Fv f ") 5yZ fg{b D:v6 ! ~ Ay`c``\׉C] +p2PidiL0Yfi3ѹ.L':a`u@bF``EdHs><ذ& pgPKq&PKL~<ObjectReplacements/Object 9 s qcd0dFidIa&Q (d2d i{T% gMg,&K ZZѸd13L 6Pځڐl/*Y 6#HiIĀ@&Cw :/(q' wN3;4laPKY8PKL~<ObjectReplacements/Object 30 s qcd0dFidkY!@N7P Hd izT5(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T w، ie =`19 l b.I, MA hU J Hbaރ=0Y"u00τHa6Ga-<`9ePD6`&l9\b#!i@z;i &_;NXa#t="Bw?8 wΝs9 *-xdPK72PKL~<ObjectReplacements/Object 31 s qcd0dFid[Y!@N7P Hd ikzT5(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T w، ie =`19 l b.I, MA hU J Hbaރ=0Y"u00τHa6Ga-<`9ePD6`&l9\b fHy3zv?8  l<PKǗ+.PKL~<ObjectReplacements/Object 32 s qcd0dFid030<n@PT8 TlT5'\y. bb ! ~ AyA301+n5YN "q f)M+diV`L$CpIbQpenR~U@;b UR@#U 6v7`+AY a_ffO@̀0LfDd6?`9Gda=,~pbaUGJa&J!/Ʋl=`.|9cYvR Lk̥H@ʙ8r&Ô_n@8f(CΚꉾs2uW`N0OZF7Y *OPohPK!ڞ PKL~< content.xml=ˎȑ BnD5efnYdJ1$IU՜o>{ԟ8")QRE5`J|EfDd22 }Өos˷??łvQ?܏S~xԾ¿$dO<^G5.0zt}T;cR_rY6.vWm !t XY<*^t.c>dm>a;o•{@]G#:?l[{wT(Ddz+"yC6.rk9žVh2Ϗ[^*,l[B-X̤u?<*vH69B(46k^Ҡ1}F6`q/?~W#t>È[$\D9beP@P#Ua;s]~sn$S!aG"^jл,Ą6R;snNٻTO¶ /X /MM"Bb-3y{Sqi /fs=0 q`| fIlk"*Rʒ;?3ATUNF{ȊۛV^\C{3-'q"f8Ks}oID *4E~B0jzǝ^6xޱԡ"b .m>qDM}?a 6  " s>ȉ. +KI@#h3!o6kl )M=v:_lQ0~.(lQՕdƟDElgvpq^{mϽ7M,,75z(2CyuAqWG "tfiY2.(;;mrV(S'EԿERHF{N~t!ĉi[f 'GĒ}.@mW(-S`G3eq!riJ%,zqFCD t![;>14] /W.;_LWsbمWxuϲQbĪU1qYMԛ|>"?upgmud]Krʀ:!n<9)AeLZ+6p隺)fbXw%X'_l^bLx``idLyZ`%Q@ }bHCr;u<Ϫykp 50eǔӗ(]D&uL}\pbl{Y흯TJqv$lj,eTh|?FC57].@ aCi6ac91ZS[i91l)K4KcW~94'1)LٻGD.gCϱEZQ,}@K@ .&a4|\hmpvB}dI$E\%pL MkFd)&PtL{Gb2N7M+L;\ 2U_~QV_qя!=6@*ÛIV%ȡ>)|GII$@;C+AOB'0CMpԻNVD(&-'2w.\ Od>f7W(cTźGX7Unv^{Y}QzΧ5eyDL'{J9]tcfp; rr` ?s(?FӘόota\QzP"Aqҹ$O̯ Z2!ikcs.'rd0c_׵Nx`&sg_XHT̓q i>){kEh'Da"6DMTOK2PNYK8R qOX 1}|XTFGN~^~-zs(vna!cBg#N:/Mԭ1Q;9mhN/WlՉiL+L|O̱ 1ǝɏ'>pVU@MŇ~+5X|TOͣa 6_ oŐμQfelUHBT6ʬE;ƴWT}hjUg Ҵ4 4+Eי:'C5T2-mI7xJ뱰$Ϫo

k :bp;_qB{7֔_w#|7w#|7w#|7w#|7w#|7w#|7w#|7w#}72Gw1/ZnAmf'w*<~+k.zZ'@=(_5NynHLBe GbQ}a 0e>ϦъE}a_U Tb#^7aR!yCir1(sN(Q VM#_c'O` \`s7P6]|k @NY^"ڨps@@s@@s`s*砵P@9%E H) #<PhO%%Pnr'P4h<1Y̎AQ#@*L Ha6Z e {7%s3Sܙ2凜|0cRoRfΗaόbVU\MB*+\U\9"Wr$Wr Wf8jģ0㖙q̸ef23ň[f-3㖙q̸ef23ň[f-3㖙q̸ef܊-d8FAzZ'}z;3f9ޣm|(4Cɇ8\M9P\UZB e+\ňt8ٖ閃 =P@&W\9 ַVXaj\+o1ԟB3A邲 EP\JFc4&f;11bGbl6i|dr`p8{=?yxeuY}rLwC{~`aPӠjΖj.?]E쩆!SHJPvf y}ÎOݣ<v=JgϞQ xx1{`Bs9.p<Lq0Q@`n󀴡Grw<` >sHrŷh{[f7:zbUS>~Xk~~T Ye $Q<$4 '9e w#sXfx|& z t n h bұ|n'd> >>>H|}_ y v!1ƦvL"PC^? A endstream endobj 13 0 obj 13392 endobj 14 0 obj <> endobj 15 0 obj <> stream x]Mo0 CEB%Ե q؇hbH#Dnv=rTkg5 It֙p %*_OmqvݐIkf8I bqjbܼ$dR@gyLrE\b^IΣW32ϟ qx } !IMZG}?i endstream endobj 16 0 obj <> endobj 17 0 obj <> stream x| xյ3Ųe"yeَ-$V%$N!&m-# [! a)Je/ BJ[%i71רk~WTƌ{&M}_ȳ~eq/CDM/U  "} gVFN6uQ>O(`=|k^RQg! o@|s6G>x)hGH+"8 P 5~|hg" oMHQ՛=0r@[brețpW$P $ ?QOB8 &f\kllG$+GNgWXqDF^'GoCo sDGo vCSp~ o`O9+*`h#xa|nDZc " |3\s-\z! !$2CB@P-@NBrq p#</8fEv+[ HsZU$$[gՂ G тE0 #HOux$ԓKQ>!M'3oF.|?n(y6jh=t\ס~wí"zF_ jF>r%:.9Lskx×N3ݪߪy=h4 gC 7ઇ`+j2z^wgAGbPcxU܉3|-:.}.;B&PݤzT靹 u),Lm\ٌ9\܉?v|B _h5JW"%KѾT6'wɯǜSsY\W-qx}{Osy;o_]W%VKu.Ú\ M֠=|2Xgffy,YqEyc凑W#oG>a>pMvh6X 5QrZz؃q:jy/0zk[WHD#D.^ȄjK8 Irw7||ˮGI$o"GQp.q%x5r-n^^Dx{= okf~78,?$5&.U%T[TVV}:~_֤j5G4hsSxg~~wp{?0"ۄ#X7 . q.>Z]Gؾ2sѯ/&퀻ۆ1}µ MdDgJToI|d ʅWbm`O2^se/wq䮋|,pQ[rrp? A0m#wsC[?rix F>"p??'>H~.=%smpm&v2 aRø8,'?V\i *l󑛄Fk:͠^Vr?Dˮk7A>w$p3 E혡G%> nz; 3??Re chOOUGp+o'C'M!CI 1d=x#?Zb33OcD 6fUK0wb@f0wH9FK0m3a:H.i.YQ̥VNg%u5Ue%EEy9lkE33RȘc!.Vj*4[[z`NoPȱXQHV"\A Q- iR/#R:r4JL霣$ fc#P9u* (K(IRE wB2u@3xQ{ 57A`/ĸf3P:ǜa,ۃ/4hHq)kl sX V>a5ŨsZư~Aܖ/-b{w=f{Ӟ=s{O6g/ΐ<\$_Y.Kjӫ趭sAKS`Bc)Ƴ^čsĴlf<hH-AC Y,8(bթavb(õu^gnA_ ={ZR˞=pd{U2X7Z4yƴ`MݸaRAÔ`Ivk?>`J;Bxhmƾa9,mIy==iYW~'v+0k 0vG?8ᯐm*L) q J'xi($ZYKg]WNH]k_۬Ov뾗ClgXf=2&2R/$d b5LJyk"[) (!0t|ZjjLL6Y'g{K1֎bK o l.O ' Ӹ$^b\خ".᪗HX] XUib8$ W[ziq >X4xvS'%&,xR55YQY@3;{ڙͺ*Ba!_ԝ|2B}z**ufyro4̋x-*#*኿BcRͦUvE33-GESr>זs1EMΨ2, IeK,12`S^n9'Ok&sٙSS;rr!/3r \n8ִ8l2뿟ծvf:@}y5NuLź ciTl7JPg_p0zcuqϴpw~u]]/_4Lr In!"C^}01zGSÑOUSnbqVkbtA*rrR+KCp}no'~֏f~p~r{mdoo[[9#7^Z`kE cˬ[?XN>t4y.r@ohED(y+:M04<έдV&$چ`37Wp[7rpOBzJI4}JRJEh}Tk >.'fKbVvϴeM Z6O[d3!b)1pZMJLLhf(n(Dļ0mgxLToR\\֜`5fl CO檞$QI%|Țz8>yM28O0M |]˝zOݞcNMA9ރgT3B=u̙)ˢ%/vc2N[$chlM 唚krFGG7H|֤nC0d5sA@#VK$'Zk/cf:yqpv_m9ih^qF]`_ElkbTNlu^y[klͶ O\Qis,UâtEcB#U;%.]Rv,\!yPAm<+ko5Vm5GͿHM# {V:S Rq՟TɸDg6+8fcv9LS#OCW wɝK,ΙvI%c-Y/FnYi\)m= ~j&%:b $Kb ;Q'CFg7abOĐ0 6Ii:| )9J4~ ds9SM2ا=uȗq0~{| ԡqEi6/1P3bz 813^WʊrsrmF24Ij[2ͤ}923֝k}drAxfM߯)ڔg.k.nlɕinia,3ɏkjթE܋=fwy{M&.*.pLlUoDc{hQ<ŸMtR\n^V_MÙD iTuqq_5޸`ܡ8u ͚,>u;~Cqu;;uh}VEӟ OPض4'azl&**Oi*RiԳqBiryYUݼ'en-mFwW_кxIiͬPk㣲 拊-әn~einKXY8W_fI訬\ 5W97|\)aGK8a?Dw$-wM՝o|/瑪9^y&&*K-ˮ˭\Ԗݞ^1+jcuW 97T}殺g\r/LI$&*Ǟ'&&|RM@jJsx1Oӽm(yTM#GVôEÊZ6e{hjMɌ/'7;݆3`ٔhpqemmΐSj}iQ\\P8q}lICiZ#9ÔY[Uݾﶚu%KLvGC>3閚ko9&!@K=rr>6`WgmO` T1t)0MR|Q_X` X N:)|UQGdHEhLQ08_3F38[P`iT;,28@ >P<` PUTΒ,R`JGL8_YX?W0kN)#ӿw0?avq,U`\ :a ~%xNfp;xa B%A#|҅x3jbx&9$X=#01GG\+|(T .#X1C(CZx` 8FNXoe4^ĹwFW4YIYHç60Is+ӕHPqy>oSd^8u -\{Y>9Q6#ABN7Hu=8ka4\ףJOK$ir =ȼJHǹ>p+:Lzq^*8N~.y>ʤףU!a1|lUkb60Kjrzx)ZFY7O.c9NE5Ǵ'^iz>۞;hiҏ<]dPYV&k?oa\IsA-$2㾏2d=0a5l+z15ʢ)pg5yZX_mV#Ն\lܲgI> `Zs3|/*0"QYװ1Eұ99,gJJ^q,Ƙ0%eɸq6vVmaYd\LP]}sqE˓svS(V}k6M2x˼S,;+O0ٳ.&Q?VfѹE},{{K43{۪4kGY[ ^~84]CҞggWo}JkDne' 3ˁ>?-I% *Sr1 /S`b)SJ|d*(Mc{?3sr=wKHn; Jjƽ>WG&WubLZ:WWb(Hk=Cw:=nڽUZu5xGրk?;Ii$< nN(.=>;XcadHň:R2eX<,u\Qo_n{@1W@ʑ:ۥ5Ekl@r[h*;sOG&kgl u؈{eyޱi?I\X^Q*o-fM=k|Rd䴦֕k\]n_43lK~И{HBJÈOݗM #R;ꖼcYmFҨO&s36sHVT4f&ܾӵy|~fu[,0J4L(N ]$߳ōtS"4RQQE[#[%'|8Gjn^(,5k/叺D0 QsR?{?D~:w&\W:F\c#=rRNLi]>EnO_ꛐpS4Dᾼ=2B<>&q$ KC^/z&D/ ѐ~O~&Шkȵ3^sc=t?!/F^F6񏏸&e/k-knZ4(]x80:R<xԿ)@MYD;qVD6d֖ekVKkZUͫ5K˖mnno^ݩu,v t1t3,[ڢ7靠#[X*]A;sIjܒkvSI8l؅\aahۊ+=eG# ZNɅxܲR΍C#|tdb*9ρ0JT17a52Rw.cDbLήפdBtBw{E>r H}|u xhcؖQ@>[KNj3Q<ѸL=![1AOxt%{]GSOJ*Z8G(dg`}c |܌?`xpC>S:TEJ7F '`1]Kzls1g<@ %Xnn*yUeU%e%%QQYxqYReEyuy^9QH[Ŋx,rP}ʻytt_<:Dt`fֳ ωTΉt6ngdGg籜??c,p`翂 v+`翂 v+`翂 v+`翂 v+s_&y@~OO:UXeԏسp.< % }>l?d |)}獜Mr6gr~> rt=3Z4G}3NqM$g.<{WJ$]_8ZB(TUSX" q 8s }w?埰ƴQŴqmfk<۳R_4vP7I<[qx~޸eTE"~gMun_=x5!оf3աE#x?ĽxυXϳ}w1,ب}yEOsO[շjˡť0RaYЋ8yl~ SĀC1Q&QuL<.r׎wo{#‘#G۟m+Qqp\O^?#e~/].w=ms73ig]iBCwFN6/eӞ!i/ Y7 u- VQ5꘠I5xS.i@f\bW .]5+%.յaˀxi?Z77hoo6{G.L&n#? ;/ endstream endobj 18 0 obj 10862 endobj 19 0 obj <> endobj 20 0 obj <> stream x]j0 E /I&-Ca >hpl%54QE~L[Jb+Z96 aJ#ڐJK/ a_=,lrǝᆰTqB> endobj 22 0 obj <> stream x x[ŵ8>sJ^$YtX^-Yx8qV7m9vxl$hRZ¾(t&t#-t/:^V\  \ <.A7ڎ!֐VS:Nt_'d=~c,3PkmSU`*Gu0Z3} /?Ư?}$= R^|~w  @c)DZ R{a_eO'E TWGa]DFq#s;.6q9[aWQ!c5d\+O?2ifC l`t=N7П$N0 7^s&d=#Dn?3:SۊuϢ?`۱(-ˀ&܅[}A|Os s9fl-{7%OhDIb_īIt*Tֵh}d~~ vw~Ϣ@ &p`uf,Aq7,c62W22Vb?ʋ LՋ8ꙺm@lQ{.--wA2 ,OU|MCf@r{h @r|%{W4~kBL>S,512t0%;U ccMb6fVU˖ȶ^ +_"_*$(G~g -{q[JrIVyqkʄ* ϙn }8a?b_0] hgb}̕jiBb/忖B/0i=ȁeˮ^g9(Cg&֣A'{@1yfX)t?sGQw 1>Nt\VVx~S2#yxU&'gנWُ=S6%9?q؃?ya}Avt^xgS!*S PϽxcw"~Ԁ%;%)Zt'ڋ)(}$؟xmt]~S:m'c~r܎7ZYm~y܏~4x/+H6zj0>|-7N4qŊ3q1XӤ|DOGsek@ jd"=y%z)Tlۋ@@r% C~>ވ~Nα>U@GЃ҉(16rf#~6k?s$(_yx>u 3EMx bT};Bt]{~a@B&fPԊD9>S!Bd_ #7jZO p#QX:kw-m|]~{O][-jj<5!M~hxưScJLED`w/n J-LCD@b'3pQրNJZ/3w{8 j[].jJtתfΣv{ (Fz&{R֑]=3<`ɇ&%3rIu8_t5U=ml^Tg$(T b,8L*ͺ,)Qwƕ*Jڂ(׶D[4.חO|@F0</bOe>a={4Ճڳi'v{xγ(W ԵMk48v=ZS ,keТ1~՘_(W6Դ-jB_Q!23Ɠ\cvُ 2@qhj 8#q >pK.GpNTEBgUB˞eF){#J!azds-8`wnA `L.S&d8]>ىO9evz6ͨIȾ)|G0ZA}^GEFSYdqVA-p[uf&&V܊SJ8%ՒC&1S+!1Bz͇:5՗H*`rD!SU?D}\T#M~%EWT2A,E #*$W)u8g]o[HotdYy_Ԍ*cߨ̮8z6B[dJgIs(.\Z̖VJBID\,_i,G. 7 nj\=7?˧^z|e"+{vݲ-W5ۿ# 7VQb.)wd}iWk 7󭭋 7aף;3R|hȎ"E'j'(o(mr\`m(3i;c6 6==4ƙWu[ފic /}Zϵj~'EC%a,>mO'JUh- "L#Fl5WrD-pfAA?Nܒ q̦8dg\˒ʩR g_U:5fʪd]8PqwzK^2ցF.COe-U&I6z_m*W[:~s `cr/qv7;G#Ǔx,'&SkH?΀N3d8R6li%2р ~,R v)G8 #eLgҋ)o:J*q7Jj[[ixa'Q:X1vxx0Ӣ7xK=nYDSf碟?uo=VW>QXEڥe sb(N{oU6[IWeH+_Nj !ݼdj3H8SpBu*nu yBF* @xؼR|Z $K {Ḩ崌r9jf U L<_0#-1Z@:ǭD``1)e#`|?F:d^/t?m/Q۪JaBn ={˨uv]nNV3x}{*-E2OH'IvZJL$u=9HRùI ȕPGϥg JA(qQ O):F' Rp FAKWW^Oy%?EX "`w5Noz7ٙSr 2H 3PN.4LNX#i45OLjD{/B43O|>Q.z"1VN]Us횕fUo$ؽǜn򍦥+LHJ[QBNg߀g)f6jR*I2rd3{;2s˹io}D{'=ur*'EX۠SZAǘĘxZ,hUwn~\ ¿N3-7KiqUlJg֚E{X/5#b\DZ1 T|Ҍ\X  (fʩbT !wZN6FqʶqS&[@PBR-H@ U`] Q7qg5Pp} Fǁ-- )c3*F)6,d[ApCg`L*"$9c#xc\,ASDwa\'OppnK'wX(hLUpP BEfKX*D|P 3lfw4$3ߎoc+nݮyPWzFT) t}j֕~|n6qH+TC)bSKjkRiˑ 9;coX̦U<Ë<2c : Ƌ̍bd$Srm^E %ԙr-͚Ɛ[&ɞSTY-d WnzDGpX0Ws+B'ފ*tĻPIM_1}oyS2.>gMZh3rZ|?e?>ˏWXs]s{&Tԯ俞z\oY^zbހ6mC̛0˄8UxXXPRl`C |<|)+J#_]S.KZfYZlR݊>#*]/392TTّVܖr?0i|RJm.Q1)f>9KqqJRy%/ae% KKQrs%/y%V2Y(%U.UH51TwTT{ZTlYZ>(t_ߎZ N&kn!ViO*q*1ϙWW'˫ͩ1f:B*Nj#z2r]PVT> NVݔ]cqeLY\da{2ErA$o䶅q<89xnVZVTUeB?G7ɝ?w4Y6 %T"h1SΚaePk }Q\>/ݣX6+S&jv{eĥXㆬ\6߁}p!/ݻnҊ Ar73%?Y*i^.ĢƗeRX%Ǯoǥ!:7-ݷt~պ}44xkm,-x=+>ͯm)0[S9uK\U?([i]Vp+XjnYN~%"[SKf!5?;%dMVfjbg~fzy=?W)Mg~vv*Z;RԹD*V~TUTS8ݗ[c\UR;ڃ_pO R VS)J+tW7w|x+椻%-fH)vÜ%1i\Y(7 :B2tZbg@V(NJKNu`}XK=bxG fP20}Ma[' '?7_~ϓ $~}=9%-< ESS4`;MNj:!%-.qs'a]&1>%΅ÑȊhtⅹkGT*tt.^S^ccX󚡽maTe8I"6] D$NώH8p!c%qF8\ k%9]€F_Qd5ZF?c*t~oܹ97ܐH {,T1Py6:2sg8y'veBcJҘGRrFϨ5ӵFوq$ Q+K簞J x/K189m> t96/+Ƥ|<g;%}өc3yA0}n[GY-kd2s|-ڜk\s<$o8,~Seyl1}Y̬ e3ڜ7ڞ!>ޚS{Q/r3gf[הt_}K{bQeu4^uj'-ww_izsǪ /MtOȻQ*՗ 6]Tds,J=Oɴ樕*]ǚYŸBo,HH@V`a€AĢ|t3niثzxB? b#-xnBM ~7naQqFz !|FYQ9 EH񃢊Ml*3YZ'^0 jw88k]232e1$ egoF+j\fU20)*PmжBB[܁|tj43*Y|D_yz%K6C_߭DƇQU*4'S"uLQ i$!ʇ,5R0zhX> #+KD'B>7F2KF%ZVNuKDHmgtcd4-`L%EsMM,Cɦk%Xt;%XRMJ~(*dΖ`51ϗ` ֢)IMnHU >peD[POa9kl-,C +]aYegJQ ]~LaloK >7%_S}~u 4c ^AH07=CAs~3 ͸GAI0#_ ^Ea Ui YNRX f]e؟OaY6`*Ma9!)Dd$ < ?yU 5R8ړ.` sRg%LaOH0sى /`U _.On ;)CLE}y *`p.OA| DOPP#iXE?]WN &>u"q &?0K$̛@k(@!ԅJ=i-p#G}$,@- 0ɃC1xhR g)騏J24nKZvkm RZ&i}!G NNu5(^*vuP QkEl<_A%ozgI+4#o)Hv^[,꧓BTj!*m Yz(Y;bk;wg7[¢$NfڈgޏG]Fw]#CQFb;-UъFDaiܧ(/-#u ]rmj_a*a0]8gE(A{p_i)i?\d͢^꺩ҼaߑE/?[y$SAA}]{%?r7*xZvMWnQ֗^'aTlNnx%D$C,e .ƀ!"Ik"-NSR?Z'ETBa*ix?Q2 5f[du )6^/RMD3#q;ĴO0 %kaC3WB+={:hY99-^Fں鑆 qf{/<:㙿\IˢmM{Qjs:i۬A>zr1U0b-ldwHz)r3LӶK쥞Y>pwRd-'~i]Db<|' ӝ?<1%̎/K>kGڣ/ЛjN>f?'a*qB{f="_{%Ej@ bW+`K?X("V#FվO )y_LOrR,!2{2$Ӌ4Q!)>XSoq{AʟC:KH8g?U#񦒍Qݒ|Ehj0w$ et\T 7 ^GOIo&u;B#?M|Mx?z; P 1~M0iK`ܜ|Ȋ ^~u: GBk{"v~u`ߢi>ǓyCnGr>y6.n۴&δS׆mV_6DBP'5|Spk]]|FBۻ`Hs88=:)׆{6=|~P74 <{"}y0< hKvDr7M볫'#A'%YpXNXQ_h{d 8 ]0ngHPg/⻡%2 |{[ zDý~[?0E"]ý5#25,msO_W᷃m~3p*}2̬Od8} oˆB},2D @ "!0YV"i}1nQ0'ACغ8Hqw pl BCAj+Ms(fQ씏 j; jC=}XO38@(:Buz{ɂ{F{z{Fa^@3o^חt@NF(Cۂ;zB*!CP-cX\"AF)ZgOd78*6vC=dCC o^MdNaжmC䯔nl"{ Y@:^btȊk/^t ~e=|iM݊5u|uuu+5z ;3 @u 10ٲtU=d1a2d 6Â|/ЃáXú Hw3ħmˇz&J\ =Կ9$)8PPLHeSKfD13 ?.%f.2: X |d &ٕ Eb`ggǰ4.0-%bg[dȐ蓉ࠇ{{"d%{$E3$tDTK.,Bpv: lPOZAX"GaC#=P |v4}*E7F` &]~AdaA'KY=4M #TCP.esEEjh,3'/[ZQZ]/7#J}H4rGcap./%x 3{=4c_4S_ W/zA ^}D_ W/zA ^}D y&Ӄ;OpDPO[pgG,%#VxھtD_K<ʗˆq"G3/s1vt'T~3[k_ދeNYlF6WV.d e /O|٧}0뿔D"9<Zft%2 z` f/t/g~7%\Y9chmbBƎ q(e%d,NYNhT;AE$`nz#C2 ^.(icv 1&'\2ƞ7& wAQv M@bP?! C9d"иP%srL$xR*!DǝD`7U'??c<ɾ̀y]0}~; !>@P>^18cٹ!%BqX? j-R6%Y8O}YI|\/Wc8 $5E 4n01C)v ,} ֒XxU0X\wx 1Gl%(PSS31j)?2fdKq]R 1N3 "q &SQdb@/E3΅5E H,(p.sPtAbfh|$(B\i& }IN0"wAB:͒gAmT;=G3&Eb@KOqP J)(dT0PBĠ]G{& ?Ih Z o"A?.A"x!y a~/ἼA^2Qǜ\.OQ.OStS@ H B^<}^[ o"1#?ƀg—ƌH}aHjb85fBkk\ EZ1 ERg1188SR ZPr<[Xĺ28XWICWX<~eO<eOkVm=p܀.r>8N,tawvR_ssK JV+MIy]?25i>-&@"!CL/(-Gq7[@OP%P#)و* oǠvRDZliT3PF=q\&Hh^K'̕R4+ARaWLW]/vt1_F>:Т x\7Us&\+?{t s \+tՋpCS -j(VjY Tҭ3e~LHo[&q@j?2OC,yZPV"tg/s[# /{ endstream endobj 23 0 obj 16278 endobj 24 0 obj <> endobj 25 0 obj <> stream x]n0 /} bKVQv \gQTrg|Q?cD"'Gz2'8'jG3b8O+f9q =C\s݃gq]%?Cg)XK>S%?%#2;b%9*z#`b}ߝ! endstream endobj 26 0 obj <> endobj 27 0 obj <> stream xUoSU?}/ڍ9},]W2f,e2-@\1C`1Ȣ`qE˶;+ύrs=s=xO*GڻV3@p23 @K{-Q lj;HyVCy:";Ҏɍᘿ}AH.Z 6zg>/;ѮX{f+yHr J(i9y uz5ti)H7FweUi{9` ᅴ!KQyN+0M/ E\-[;GoXr~v+PB<|#FcS'XԺt6*,0u< ~u޶/.t޻`S ]gov~Pa>|ȼ%02Mmp_r>a@$H+[}vL>(w xqNp'GB(*4j$Ki9^J,++ݮGx MRЎ~gYE$ W YcV 7,&Tj+5|:=luZYQNa3vשdh" Ih2]+Jׇ0*7 Kq5E MF dȄ0R'&?7;ܖ QvUW붹Bٽ`qŋV36,ۊTŐՄ+M&SSa ev݁WjpU;:T)-EȲ9Z^c b&GNlց ]3\PH()Gt[%KK<%쌡dF|pY+LʖJ Sz؋n knQ>ehu*U 4a\0Wv/elʌ QF0 C"6(alS\?j`!ƛ*XU[g9ūan8XU̓ *a!UDóg_ 'U c*KI<'T,C"oXlDDAO|JxbHS*&8bRrEz JF\b$n@F3y?,ME(HҌ,t7NNk糘OJ8]MhvnvTU-=QƐ?K$݁H¼>_+mIF|ps`{O=>Cx"ՙP(i3(i 8ɛ endstream endobj 28 0 obj 1428 endobj 29 0 obj <> endobj 30 0 obj <> stream x]n0R1!޾qJ=Y,iT5sGzciyWc!6j[WC4heahl?y4zCx +[}mۛs_0OiC=wZ7:ͼ/}q$"F ^!9/X^EV}\z*BE`F|S!o?C!?? /ɟ |9TH5OI[s_!j|Z⚴<-J>wY'S7*xCܞ{_7:T - endstream endobj 31 0 obj <> endobj 32 0 obj <> stream xy|չ0|hl%$k%[-y=Yq qI/v$8, -[  ; e N䶔]iQzRh(9gƎ~|OfsΜٟsH'ҢEB3E;BؾwE~_GHqiyn6!$Ѯ Wu/BݝX$Q Wg* ×f|@5WW!K88_y/%c$UC BWT48 ݄loÛo1o ݅BA#Ǩn\Q%pقdkϑKf͠觸gEh?݈mWQp)ze@f Mh:aS֠cGbi쓈YPZ˙h BD5]D?x[F6XS9؊ W33ݎ@A^m3o3݀~"|)3>Nf_0}V(:΢|#7,+*Yk(u>t܏~Ρ1؀$92[e(.Eס: -yexar^J({5cٯB +U {=N)-;'`r''3#COeޗiAC P#̥ z}7oUJ|7vKs- {;8;~\)SvuZE:s{l}'oC'36uR|9 ߌ2˙L?3b0{=r~&/!ʴf5pC {\[Qs@o׾.Fcl> pmo—4WQ3GF?s3%!?߲{{Il4Rdޣ7))+UL̷f^h3L8>suGOeeRIew_<\aT ӄVK`1t "\& "p/z =]ӻCXp Kނ.Ͻ.:d;eG\#)?&GB(+(QL( )Э ~0K9oe&i&,bx3z9q{)v52,0YM~ց~Y0KZ &s=:ٟ"bv:m Qٻ@&|dh!* I|;h n[ +zF1.z9P!+N'ۙC̉ B[:BſGOܾ9Orr ^ scD}s+S*Slfc/\ ˢ5@ϣV{DOۙag~_qaof7Ele_0|G + #9l@5\3{ q%N}VQv1Zdr;{Ы".ͭ06|+[)3uKbexl1Tfw/Tl-|EnO]t)#Vځ2M`9 Z=s ~9vvC X ڃQ֠#E[P[S]UYJ$EhaA$ >q繜j1}nNQ cb жq.Xi(H+6C h5šHMA)z?%~ o^ /, n-A޽{-kԋ;E1t\Pи-0p0[Cq)u0qg`Iø#` 6;׬԰qX4Ub:̸|ՠ㱳c7NmQmG#e8n%c0qsYܸxuߺر{Occ4<[[hn[ CHhDREuHɶ*(=s96֍&NtulZ6|@kzIq3[7:xDžobsr%@tνNusdFFql B*ɣWB5ij3ZmL_MIqYPE@.,IK%]D@sghtb1XG)揁= ZsP)m<&3țo,țoo e\DZ74ݼo&ᶩ傜rob]1.N2Wd6iǹ SNR(i 旎-jS6f4ǫk._0= BLS1[mKOeniRt*{ݸAk 7փg{}˦ 3-j=6h)3WJr<ɡ& >(+i-G h} #Z-è} ~gep@'\1t &$gZC)ϰSxŤ˿GԮҿS UN`ŏ&!=;iw'iSMIM!;8p9C zC+huIԁWꐤz^YymNokk1%?EE^kgVX"YIr9W_+Ge(;Ocl-I` ~@<;"xՊڢ EbYҊ j*p(`]pSW`uU3PVF%?|+r/nMf8]9nIPЭhֻXǹ `i_vE0}.&{5{-J]'5 (D pYq> !/)7{L d uB]̑PW8Cjҗa=,x-j5R^ 4\7vc5ː'L#qNiJ T]2^'nq:RDz)l>=JNF*6O3 Dgчi"#J}A% ֆ@/j#)/+O!áJId,#$TYE!R;w8!;Zov_u&ΒWԧ+B>ˢ{/YP-whM/;dmP2Չb_(ǡ%g[0]̟WA_CV5ga9O(-([M{uJp:  0'3җؗo;5Z]=.7.sG=#p4RJf6.5V)_=)|V(1c250v&ro=`cm,oK@kkl|WZVLAD3ԗc} Q`AJ.aה,aDP–L*}j8"̹C};ӘY<#^}6V$&W*YP4%U*\,j7X`<&ȏfwܚ:I)Ss+WT.C՝_ʘ߻kxAc*6 ָ7,dqL_9H&}!}"8)8䌕Y>%=(O)Nr5.$N*RE˂1G}tg|_ j~}sGjw$1ZMn#幑Aw"j3JJ7Te^1FRX "6>p=m0C#c:asXܞd5+ e ]ܹQ@4I0 qkޙnOӚ9rҏ8p_ֿ rm/[*][ڪp{;m/FL;` FL+s>T`_|: {V( t'~X]Ԗަ<\vX|;-{f&`4x{mWԄb3oP(i]Zgu"ߐyɶ|u%91''g0+pZM)UWWȰ~ ku9 B1+k덬grvb…)Pm{v~~xx"T}huhkz "Bq)GIk-< ^Y!XX8첲E$V?]p41ɝT?|!a{cEE1arXQoRbSa1(痥AM1)"g%Xz*\T:b'!ŶU~bhqY "֐J8JTC؂Yl0h;KphƛFq`IoŞ3@dm'm7&+~Ty+TM5G7ݼi"Ne[YيƣPX'#?R 6T}Ua%9O>,D&bj%?s_ w?4LnU^`[lxn{߹̗{ÏŲpp+G/*Ω.! yTwa0:~pqs^6Ƃg7M(jׄl;r5#[#&䡱'SIPZTҥvjz=__--[-ʸTfCu=-SxJ}}"9iA8_SXkjR6Z S @>y)Lj3)W!)Ně沪DU]0&"Eq0,!0,|^Q5لj-4v05HȤGS,;:fM.yޟԌ!L9|I j\02M lb}t9٫)L%󩍙P$ɷ]5LGEAw{ͼGvY?"j.,U<_rӃXZ^ϹE圷4^ VqO2fp}>Hn"0k҅JkH{WcaӔ^񥷟N'$gem|Q#sq>Ǥ+h*\J[ {+n 3fq|M7biP¶uou(1Z`9I4kр|Wpjm =K){]:34xYެ/Zo^* 7}y3x: ZbUgyveV۪x!\X $Tk+3Nݷ$\T[JeEmW2{ZX2GOuȁ*+W?0NXj_lou\c~t+FEڱHbRZCFd|Nc&&ɨ8ش>88u∂Q898Hx JF!"l%@2uh$-Q(Ev.ȃ6cfGAjd'd]{DZ꫏>r cܞٓn G|zi[{m݁"e??e>*n1g'JP@hlff+>hu,QoH%ܹ73  08t (A# hfL,H߿ҡSա5W.Ǒ!q"_ }_>n$0yupV k= \XY[Uk.2olnvúnSuglT;j{CCcɹŏ⣺+V&FjW^׬(XT|-gMO{qC4 h$4Y-Ѭ`'o72[Vr%':wn:{mj$1ywK*ui7 ~b :gN H rr+[ʼn  ׋ZistqYzBaw69̬Μ#(l4+a.ZustmC]]\cWjGUq`s(/F[tuGz_/+"^] G\^ %3%f.;Vb-K~ɉ`ڶ\N{8F*2?fo1m,St}~Ux/Wwn+ɲSQM6pl>\”-x$CUPo[mjc$VbWCoX Y`(dxi(hJ,2wP8G8%dXB/&=8uN^c),zݘyqA-KgBOYr$- { 'aycUȈ)7I7uGowif)W~/jњH=i4YS4W*$%?R%!'> {3? Uԑ|Li/&1}۠P6BP  (!JɾIJ$㳊y a[eVV,[!Z>߷zenZ֒ ayב%.blTVw_l-(YttI#8EX1W!3qݒWV';eJ<&\µh' .Ӷ _(6<"?`*bpI 5Z vkf!=`~Ě`!| ^,(9V |3F&}z-Êvub.&o]qcnLךBQ Z;-[WT,c>;3t&yꅛ*\Y~RA-w(NUt% Ƨ?QtdXs8toQۅ X.w/BتRaOkjV&T> z^\Dw~ثz_%EV^ף8o=OE$2;ۉ Δ[{oBo'ԥhvkHH ⁒JOJ_hmnF<*ʍw26TC%.:sm˶ؼVUeqbA}n_e`P]O۪ށW\߰rº̖3M]ݟf 8'B;QN P.$Fܖsֈ[o͠Km^P΃damm!OP)E531z:{"X',AT!`CE^b;Zɱ3p^P-GcDGݙd]?{ӑwPhqɝ~vɺ;.#\wxQ]"ƒc;p];edO#{r|np0F簹gobNP wDžyn]YDZ,7iB:V8z1Ll/)rr΀P5ZYu j/Zī޷RR$~YJvD&|(NkPEMAR /(=C!̖)C 2Vgq*3"\݄jaEu7/ +ף]!f}X8PiC1oQ8P)bǠ?qLpb ;Q";EbT$"iH I*So"M\w#]b,: b‚tXک?%mԋv.R" q8Ŵf~{B~A3TUz;~}K7^c!{ O,XtIm2Ǩ2ăK-MQZ8.ay 4nB}ڧdU&4geyygHΨis _v,AsJb]~IiNH-am\SQ!'OQ>0rAO"(r!v(_VN,#^@│P@L4,|J!xSXPQ (PUg W 0EN)M9  S.G,SO7J]oiS{ӳUCOjE;/)E#I/Th},mN4ZmfU@ݶ0ɾdu KzPz`"SedVj4\wQgL+}͆d_,@8ZVbPuOZZԲq gƝMѦlrsu+uu+r u+:=T5b&bboc|{`^9e(Q^anyycKU:[: Cin"I}^.8=LvZOb3%g?p=63]W4uz19t3B_7 " Q[j#j _@`iQl+,;m`5 Δ9k&F*꺲xW5.s%SK";M5lSwy;4|Lo0 Eda5hǴ,?\n颛^˚Ww?3\K7Z`W<rdB8dʾxby9Җ"֥3Ժ=u)z`SEJm<6фK> Y8:)h P(Ʀfvy o"f5i7n\ P!lD^'/5 Y4H^Lzh=Qb ~lЈ{FH}h3z.#2Qo$Mb)`Rw(@cV96(ՉOګunasGX`jfuH8NFCQF=Byә'5Yٺ&*q/e7}՜k6l^ %CWD8; -hzF/7XrKnuXrb8ǫVapuK((Sdi 3^$4_1~9{x3?sa%) aWn?_^r/|%wf@OFi&~M !I mV1젻SU˰~vAP\.ʧ^~Xd .W&˾;svnP_Ֆt_[y冺;Skel8SKW/; ⧇a?WrhÅQ@2+!5d J0Fކ4z a5Sx #aFԌZ`Ԇdz/ce%;8a| ՗33﵁3 ],aڃA$YO̓c?“'cwf0~6fd/.]A64 EU36d;$ +vs3KIJZ5, #}~>^_]\T?s^=(٫u Q?a".y^q^o 74Dz%yD^}yuUIbLJtd^0 &ͻD]9`fzfz.џ;h{/Զ}#mar/Cvj]pZO=#Ԛ>h6A!򀸃Q5R{B  )ޞδok\3eKm, Z )u/gO_T)_[aJ/]r-g,4e~y/7,wDj5(T2E3_Yd[z@"ߡ+sHjO@ OIpPC1YT\Z]hd30U"%cGYiЈҠj㲠Yȕye0 ƿR𩄠M ztQ\"| a1'Aue6Vm4 '!E<) KS<_%XEMk~OF\f-*m=U ֜Pkn)(݊8o'qb85d߂9N=^~qBN7pNى:fJXh َ!OR Ǝ3 lͭ}WP?wٿ@TLh2ؑ?WW</d!,n~}{9j/8 @R mbݨPzo}NH2'~xW u[H; *PRY7_ͺ𮅩iAߓ*3cs d佊rZP›1u}9+ {7.yKʲrum=m>tz0/'oy޶k(^¨|j|B_yroCQ*GAZ',P )2d/J%/$X~h%XB9 VkYe[$XoHuEa(~@$\C3Hn",4l`Y wK i ,Gvs@Jbz V3GKUYU2~ ֱ-OIp*v(`&#Xq$=LPXNxJVH0)3`(wPXE @_z = u]!5-@<}J$蛗`J uF `oIߤ$`ITWV P2:Da-̡D @2[ P$6MX&Z^IF3Uf ? E ;H?E 5QE`R iI &_^I(L)H0S,⭐o`RED#,I)LU &唾ZZ?n`((L_-@VIhu.Fq[P7Q?{XţŐ2ӔV ]PFf?3@uЅ<3,ìI{x\ cwQzv{['I~iZI}tV;$0C/Jx"Ơd#?Ly`nPేR`ph;홟yy%ZG8l~Z).q%oD>!3M+ְεaZ}u>yӿXHtKu@SZHC/vȵSҮyΡ&}3,N13Wzar9bD4mq"g>XxǼ0Ch'i'|~1,Oi\0#TDRKPđX6@bU䢽T+ Ai9 㹏ʗO'cQn"_xyR_avq5/054G Yԑtf]iuݔiiAIߑ Qʏ\ ?dDfH\)҃9 Jzt;ܽtvҊG{GtufDFEP0*h>N*b``i)uu Ko/СF.liA7@i2zAAYS? UL|,N 9L5 oQD88g#Z;>JYx~U %- K^ >WC)T{(YO-W3-mA:yq 3[=O HX̟ʞUDef(1Ɋ k{ KSi9O=MgIma[шtv{$%03,.y:p%KOW. 4~|I R%̷/ĔhK>GQ0y/%MD'K<6F6ectO(j4lE> ygyA1-c΋bsycj!罂Ywf?=qzyݒ/JՀ3Y_?:$[B1B2) l-qÒ-$9X#f{]߶K3,0؞HJU$"g VLҿLv6&fޖW@ QA[D&kng?9{2:ٕn[;a({b~Iz8O*IgR27AD~nXϯI!_U̧:ޡ}Px'V@N~`z_OҶSįNv{bƞA~ezoP\V3s!SNog384̧:Iue 5uHt.a_wO{73KC=;:;y, %C#ۇ:zG;NAľގ!~w?L`hshkNo8D`i;zҽ|!~ dG::?0LgbGq[8aQtG@ξNBUTE{{Jt BA]s9|`E!u?p s&#Pid\989<77t 9vCã@`Bo=C>qAyi{go/Yp/ޞQxd*wg\w¬/B |!+;A!/rhGDR9;Ou GŽ=d}wK,։w&E|aB:A"lp"mjuK [WW/W6.nX_l]CCsêZ3'0\3U[FGHT,K:Aua|+4N@ &C>\r@.@ywtLJ(;0<,]4%Ҥ@JP147;BUJzhsx~b~H$H*`M&&LC="_9X$/H!(!: M{`Ai޴Pw`0(>|R( r*"7F 0 R~daii]-\vo;g;qդ† D*|EIeQ"HT0QRL³(OUt#uPI..M!ݨtڻhn?Tm/k^OX_h7.,$;ɞfv١gJ*}v١gJ*}v١gJ*}v١gJ*}vC%unυSnWv`ڜEX ^ n_៯y~ VTvR{cbm_Ik6sJY 6K'xb?kwA5vvջ=wgm/MoTݚO[*~ΠYɆRa h1M'" gB,:1_~E^Yxi^|zꢭ&-J,,*B5;ކaӠiHqjC 7D)joB;^ܯ-L_6Y9ddCޯG.|7tr'Mw@`gqgmUJ~~\ZNz2sKTd)XBWJ3KڋK$]٠eͥN,%*AZVj .D+M6eFRbAYp*˾#X ť`(Y*ialFiew drx[ZY.fAtx" rϱW H{sdKƛ(UyK}\ ,ފc f? UO_JR?udI& h'H90ᡷ>㉖zZ4+[=@CwФURIեFSqSCe6HI_A.PY%ઐW:U ȅBboW#1;c_`_a~u3z8[Ϯf܅)fG%C~3+ziF ="'\Ll"`'i^gF ,`c R" 4Ѡj˘8Q:>+%85kI'u.-3)P9:EC:}Ϙuu_u_un'K󺭼n [u\O7y͹HRZL)<D9yֻȑG psR"/G1*!Zx#OO S)19J\h!3i<8(A)o" %B 0@dD^N z.w|ZXR~s؇< ɢQ NxlJ'? {{rs,9Oy)*>SB+><*%y7hn=epUtl.+]:]\]o]oOyy+tm^L Gᒣ ᗐ{=.EBPT*E §AoAg #fARЖPNQjj ]w1q/mF!Đ,xzh .&Г_Û'Ξ{3؋/˼ޛyɞmfX%c1e#,331],kTMWC:lPf'D^x=yeY-x/O??r*oSk[KӀڀ1(}ggыc!컲UNoT,za7k^~ y7y^mht34щڐVqZu 4b݇ {d.),GA!sr6I_1hS3+]`L2RhT]WHȝ"rs9厖;J~{.rd}6|z[[<~ln>HȞҹ72ͅLm9Ilq!<1ڒu='!|u"nn ~|"YkKpHw"N!Ƿ|7Yw܆`v;Zm4H(\ljcŢ+o]'ci˘IAUmڸZ:Pƒ8δ Vn)SZSO^4| % Z t@W%j endstream endobj 33 0 obj 19224 endobj 34 0 obj <> endobj 35 0 obj <> stream x]Mn0FtMFBHĢ?jצ_W;XDmaq[ov]}@(KViG Դ`s/ LUy`0ȵk?J+ 9sG| %r5r#?rB>䜙O%rLgyʖ<_!W'2`Fg1H0+7gyGO%DBl 2Tq 9J_aVgͿ^{eD{ֵ 5( vHo෇q} endstream endobj 36 0 obj <> endobj 37 0 obj <> stream xܼy|Ž>^+dI$rx8D&1gHĥ\J҃B IpGS(i_6JwFvH~α330pEHWs'o!~6޸Azc\ܷ ~!e7]|yC?kxh~dVCz6CpmŇd._zR.n[Xym>iʕW\4%ŋlE(uu6A2B{$}=P'p?JfXNUjNo0 &j;DR*DcD&esBӔ-2.!?F{}4|Zq .-cčP݇v:kKhE?FQBo=Ȁ67h&z19D C1ԁ>fh !;*?^>4;z_4$NwGGB{c*EPmAAft)z|zBc) ^t㶗/CS6mR>.~xQy}hIHB4VB=l\gwBc &ЏVolb-'%][]k{0{'!\kr8Z e;УQ܁{(~}T)e[2F]Ex): ak(%Cj~|R^Z~1E-@z]~_%t7QC7)NP{!}|}hw`&,(y|~Q2;̾W(MВA/-ak؆#F<%3 7[imcJ_#l0 vC_=bV`l.b{{W_p9'+K,woAJ(_QD9s1`eп> Fr7ڍq^Co@/_Xw+ I"~$SWgZVY =QaW[Aaq㸲ي;)cl~ƫ{?,t_KʛaTRӭ˝Săݿ}3X/ `CZ Չx\+*-x߄oƷo{Q|3p?Ÿ?/@bl3Q&`,f>s>\kp1FB1O1wX fkؕl?w%-+\kpk7_rr_+6:#neNXy i^w* ?a!yi*օ)0cJf{9{+$+vvozyW~E{1S9^|cwzQRkݬ8lƣPH1/K;P7S|.yZi}{}'}@5\)'cߋd, g|aFa-VzHq.$(įuɿV=LhZP_:$ޟ*PlŻ;Ͼ&( puPzpva@7@?4/Eij逾m~ag @;5X5b)k ֠"Fw+(~cBTzwB9wf2}. 2f#A`wqWFx y_CG/\(|Zգf"ƾ?-l>У0џw4ųh;-;o#G*0oQ--}>B ʏX֕/3(3|Gw̅a_M2_n];qͺ ĸYgZ&ni +)U4KBJأ OhccnJrN鶳a.t,Z}朲v V۷WW%!vok#gpX4co߾`o_틺1iѳ7e݇$4!$$$b݇di)G3hzF4O5'L1UdGh]}6ЅSC8;= x@fAgA!7zs|NIx&; d.2L`jnj~~v"RaI|I~ƝEE ._Pjqj)LӀy-@zghWM'h=ރQ{ 4vw=%99DYgtE4R8BX-Y̓XU0Gpde!mz|Ji#ka>җXM*w )6%˩7!ʤFS\gp;'bb856^(ceeY3LT°Y8,"ScX;5F"ʹnCh,#Y@g eVLD,6as0TieDBh+NoE4Plի \ ~:h,pc! 74` V)mVclVe*@4e}LDH4Dl9 as ݞwtgSrp9|8>p44EK?cvF+Q\VJl}Ӳ/~Vweu'}Ew:aqeڔ=VR:HBBAōO'-$8GX°li$f{a5)'\a^\:[uѨא?)k q>3 i|{KG%аf̪{S+6RۭN6{n/9Xˏ_bd?^7*'ږ9|]\/ qÜ\'Cۥctα"dc8De{u9{Y!]yqpˣ=?C.4 +[031 k"Lф*!/vIخH YlZ TW𳂤x d1`jju י_-^QVpUL76 `@AR*% i4 ߺl[޼n/_6cM+u,v#[\z;[_]3^-}?NKp^qҢ*fNL`Y">  Ubapl?;I18Hj"KR ?Z"NjRKF?Ȏ-6P(+eRK 84i@Yt;DHQXId!q6ĥ\!igG0}z Ų#n%C^ |9P[=I:33d- #;~R@r&e A:7@?8r`f"ApΡ BC'C )bdoOhTk28rrbg[ss 9u!J*o1kXxd_kD%^ĩr oQG8^=eJuuo9kZ[Sn5syblUE Ғ'r5/+MJNc0Zo{} {e3 W&HՉf3PF+,YFg!IBA5 8bэmX+=X ֺbBEJA1Ma!+䒞RL,g>BƏ,7>Ё aZ;M;/^8O8]^ͅWV**M&:],&սDo `.O:m/6vsIIuwxY,xЂE [F-G-,JKmE$BrOR'1(:M\͠~ցEĦ-ErEԦ`}l,0 LuWG{Z]Y_2;rw#^AZv/ֿb<\nMP:x{ɢjQ׻&c:ʏ:Njd4 +I 0 OJX4!6&rRӧpma~^9E=YˢW[MV kDCz>0Fb2%~)ʑeD)MJB?Y$'ԍgDJ.'=-kiE,,$t(P@Q2k(a$E-0Ѐaj }~Ot]iRWǿY>P#G@MtzuLQfdM/W*S-J#LV9qRPEHthV(10e_)%<( I iXJ 7++B3`y&ϭW3EUͲ͹#4#a2dNiw%宔M4RӔlPlL=܈((qeEtsfBHE7}Yu٭5~<|q\]ff7lX}u1~3̿ӦP|#b_?S5S(GTH$\#ZHb,IrKӔ7Hd KXg;kE {|HjZ&P |ac0V+`+`iN2C|Yu"h4AbP"UtϲIԊ) Q茖tt-B3IJN5-D aݖS-S:˓.M^dڔE(+>3;ۓD3*+Jq%A*DJnB9eSCqc+扷sW(F`l̷B#x{ #?Nu?tX &_zŕpiwM86Z.7Y20Θv: 2Lʌd)Ցrc-9rVjya_Xr^Wq[+CTBG:"8W_ *3Lu-_iiP+Ek||/lb/dYXFcΥ2P5(MÕ0Er [@Ț$c6dm!DknY!ថBkbxo/;U#X{Cpp7wB%?> ~45$+k6'wv6tzH鉅XêuJ&jkA[w'wwjnHkNcɹk&o3<ܓ=~U>~ }vɦj|s]XpI0sV͡^4RT!2o9.[i2 | #Uq8dd#^vF]Δ_U]QFY)2g@ގIa)'VHWxʩq ݲ& Z^4TŁs,VhJRs(CI'KIMPb*>c o$YbRu+jzbwiن\C!ΪjtŞbjt0#P衤C d,c5nwպ]GSHAVN[  w,J蠥-̕fx^a.$%3yl4B5c=il{PnDs?P1l #4Ѡ)K*1Ag@ ڗHwՋ.@Ȫ+KSE;tYw!K"KE2FDx5)2Ie'39&"%XGGo>RI㶈X"! aߜP+8G`T 2ުzg{1/f_ly\}PÚhs6tk7 --3O1O&Uk[yU/9C3G;'4gRZ[4h7ˊWET&,vҕGBGMyAץcdzuD:N,4-WE6-n[~gfW3Xa6qh ^D"+3,^B(Lh(?< srd @*eZlS}t ՝x|]Nk=c-"Sc a, PYi3H,N/ c JI BB: ж A2+?TXZQ1@T(ڂL^-FރSjT$i U4)=UܮHTWW?̺AŊF yl@Bc'lWGc 'DI[p!4)b$@X *aԒ<]JzƎ,`#QZ YO]q/K#L:N)at澋k34λ K[kg^$⪪dS.Wd(Sfg0{"w!ΤQeꔘǔb1eP ?'M:t|F@ P9`u*yd!J*I6HM\~32 etZJx !S)g"]SNUؒ)Pq}t9<8Rcq:٫=Gb0%kXvt9z}!n>~nѠ5nYgBx^7LetK{u}!nIB~IZq?&2V롚4^*\芙Iqӗ4z7}20(TU+HR:c/|㲅P\!̓n*S]jMY bX қ4hyb[:@D?H E|SKэUiV4hҤWT:EqEM. +`V"`$%5H3[R<%E9@uH0JD@Ř*<F=h8 &ip,%0UQ@Zfʥ iHz7p7(s=4/ J۫mŊŪEx~6tffi~4}2H,͗..\.]'BW%nYfڼQt;iΚ?I?#m@GlY}?w)3!%f啭 &@( Md?jup}%ѕcS `F[:R6Nw-|_2&4O5̾uh!Ű2A~Fh3y|c>_[C5+-r=W pDOes4Jd|lg|`Zh`klzKnߥƵ:Fajn*l5o «}5+|fGmиvmW1[2v(#$XM}0¼&a|~U&nu*vJj#<a> 4Ǥ˶StD0&yE#~b&(f֢!%&*6NH?[͇f'^7B鞀,WZe؟USV/vfBq +??͇F1]}"R gB#{b>QC>@f9 -mCew}dSןLuMvf 41 4)&9rY9nonfhfx=Na ᰾N`mJ곇1[&U[ˢ1& MPoS_`&+BtB3)z0^䦮jWѫ{3Wr6^Ey6ߎ`ӟr~:P%@0B,D?VY6Ux3!5о)O$|ez$jmly+*Sb}EE2M~Wv߷W~`vqvf?Z}#7_^Q̚ _Ҏinfo^thq>8^@2,ykk߽S !7_LPj]D#"\ iXgR"%j!VGGiPܦ;; ;L[#[shq}?6"6\4TEh15FZb\iv0MKK]MW[[---{].cAa&c/Md\w4껚 A%Ԉ@6q:ɉ|)J5&+] e.hH(:EsFeE睑\>͇;iyaλ45^d;8 Rlx49 v|(a=dulV_\ThةmLGD y{%)|l2)S\ ۱=$'v5syyɑYeAy\/`S(2/jb: kة1bH􏝚d 9,IKMl*j|x+ `\؜?m.EhRsxl"4o>|DUyTC4-O ;SV>ԢBHr!Ab0(Ȯd^@? qcvU׆jo>hvUȷ}wfMM;l.8:jݺ.7fŪJ#jPjp8/7Q&RXהO_kń2AK3Pcb{yI7FcCrd#1mh>YR}tjN< u&7ޘ7ha UWKk -gH # DfB{҆T=ax_֒3DR]L׵&4kbI/_e1xrVNj椢rdiq%oB F N*_q4CкW/Y&j2IT9y45ϓȎ@l.$b|{qCy֯y4yz϶hА3 C@tGZZfV4E/ wLE=9h_`6hKC=XRIq8œ_ k}ҝ#a0eFXyZpqs"♪xC\TZ|pRJNj͈ML@F}ҰĀҙ T"Ԙi} Lɳw.7 vu2Sd:Xs=#̲?"5RqD͕]-TImcDUτTe p0<`2'?38WPO?&6QF;ɦ{.,ZcY]rm]sv~j2%pe^W*5N:js}k5+XtCitgM:.LAf(d:jeod[VS OxM-J6A=Mf{,37.dP2<ki|}Ց;;ۧܶ:w);^]~t']-+Ts[溑jŪV 9L/HATZ!֗o=CJgTH"p!:%I-^K =8V<ïsz/n쳐#1VF"ZA |4Fū%2.RPW#KKy"0q~P?P=cT4& E H7c~ʔb%!бq<^{]Ҡ}է7nW9iݸ,捆-G)27-&9x1sn<6#Q?n63h]t!]yc/3m 3y5ؔJbpEC/伀k𼋿qꙟ{={>,}(ȡ8W`&a@$eF)+Y堭G \a՚mfSLꁋv<\zpvܠXSqݥk.^W>×\Yz?^EP0=vVuujx3`y,\ X~ #ct=9&7Tetx?%Y{d!v$.uկrv2D41l>7dmcl?a$_ףsuVf꜃ϖadz ?5}2 8buM-8DԜ(C$gf~^)K˹eee 懵34=}5ǫ{@LI_q_9l6 iw:"vhE7!aJ=dJ*pN?P]a #(\;xsq>fftmYoYaYob@e &diPb|HgWEz,zf yy bg]OO@e'= z~S͠bOM5lF8ٱm' ͇ 4ibp`6G|@<9}bcx%rͮHC~㯦U/mwaE0ɍ?+o ]Bm i gTI#XOHw`lVPD0r7;r,EL$۰G[)3 }3+9w\P0Tb f1sJKFD9V!ҵ [ u U*@pQM-9s[MVj BFt;7,}nnp[~/ M3s#'z{?~K'޻rI\8UԅaƂ)6Gf[MWx[Pl6Z Xv^s}KP&cyTJx[n_wXS9,}ٻseZ -NX~NxdAg gt}p)X,l묙uפ9J3߹{n-/dN\%|}4w@.OֳHp sApeaYT)XNb'6(`ĦzTO L5:g$MS,-4mL70yr?. |=V`Fz{\;Uotԯ-u ~p?ْ 3,G@_t+0F: bXC=,3]iD@8B@VKI 7)0%Xrx>ˑuEJV aJc f5s-kS>nKZؼtKy 4R,a gj ]@8)y@`#;iDAJf9;Y'L'E8u#=L \,u@Nb ݬ_34^Yj?|hGtJ+\yo.mTͻY%>uJ:%'ߧx@~ګIu25OʿlOYyb/Wx);Ӧ:IA/˔:x^/3<blu_j[_/rr USLti5RM^qVV+pNy}iA;U]ݹ, ;|΢3q,G|.lI{u%QT)[} ivve6~ܯݫfg޶0r* ]I[na|ѻ:Y>/֒V=pvCF#Ϩ^P1A_ƣNWqu3i GT.@?fFSϤR?c?,f4eO3WJs z+o d<[2lð,іd蝅*fžgГl' g^{9G  d&20hǿkKHp [!%*rgdNU۠hRhq/)pq@UwKUSPnPLw=88^W:w7fy<ҜۭC鹗?Va%31<*<^=SŃ( )m$L=yrnZUx ɣ)~/#] m@NV1U(W>Uo~9֞^5/_]n{sFu6ȭk[@ѿ@cT.f<0ڲn(T~ݵ: Ğ4E炮dKP?5}%WY2ާB5ƒЀNfj vQZiȐi-H''%AIS M%4Y6p^z7y~/[K{Zudg-ͺz')^+pussw_x<r?XX/<|)qgܽ7̻NzeHH|j2j -=jٝfL&՘LbѐYf$>Y=[r]v[6s"%ީ^]~1E4 ':dL>)F;3ב;,gEYTdwŖ{4ѳR8L!z3vDpX.Uܜḙ7g}۵W\[Eө˨݄8N/pZ=c'+I؎'mL.\֐LMH`)Ƞin@e,:ӎr1BvFr#&n"kFr 1@݆Ǖ1<.*Vnf c ij~6Jf3}[_͛fƬ)x#l.ǧ g6vy{xD].JE&l,@9tJ1`^ˁuay)d%v2_uŜr|Sjw~y⽣#{;T$S\W8|ˁsn%_sV" h2F,Qhobw䪊Naw7<Goeʡ]PwG'ʕ@oZ+* uEg;`ekJ_e}%WI=k =e{U.,-_-{]Uf?7͝r75D;\%~Qk)1!ZAwNSvA5v+*wY hz (OPZf c$`]zSnsw\dg qf skrtIЮ'K=5?: 1pOUO~lV#& Gw)6ʍy*]Nd3SO=,4 o~?~ CcZ?C?챓Xʗ H FFJQ|)v7;\NJX{;O]r,(.sF$![sZ5XFxF>* 0%Jx%4,3S3eyŋp#NWXZ#x!5IAeoiSa00RSr*0L=Ȫȡt߃l*CKagkXY,zeX&\ ^w}tLm/\m翌<5[zkdЩn}=aQ6\-ݿY{䞃IGE[/a/,{l03f.ϼ.U<#˴ :IiY-&ƒSb{9% z06-`Cm!X(0cʗ,=Uz^t.}t\Uuc/>Ugf,V a HJp;;ez<~\>7ΜE84F 2҅n!PQ3tְ58~1 d0i0Esgs+ &P@1+:&ͫr#_!/Y 9ZF &}za: =Psϓ-_j4lbyYS{Rc/޻?\7Yibaر䏞S'r[.Ws8\o7w[J|SMǐ_ pP7БSP單k9YTa(j7=Z$+.-bWt}N#b1Ym%3AUɾ̋~7GU<7dCAIӹڲ/^̓}_ jGL닱u#0 G~_ًKfܾy\q-Sp 崏s=Ͽ?ɽHJ ل|R2)++˔FfU+dfmR+bIwݠgpYRR$ v`M]ȭs3ygrrE!TQ8@tOIn2,#ʐrY ͔fi>q*!*ΝsŷxEpp^t߀zgdfp}6aImnU;=/;pJ '=)t9VK$UݺΙ2`3r~7;$tu1&Vfc?Uw*5}632 rjL+mJIg,;!<s r.uBPR*W5cB#yzzzG<]vI}S"]GWnqfC[y?7d)r>$W%$0vKvVYRTyRc_?n<Z%/*N(& { dڜjӳU(L+[&Y ==)#r/VkiMoCrP=$j[e,z5Zvc P!0jbMml5Q(mu:A JpU[25%8 0A~ \jRT D$ExsXo%g tJ7 Jva*-tDQh 6E^z|п-;:\(^H{ T\޷!g.Lζl-9uE& 3. XVj8㄃qOC7nqLDFR-`}c(8{#=_ +e~XJv+r@8r/ ˓vlϫ ;rg~OߵPZSf`yKꏿ-SG~E+}{pu7("Khwo@it[΢.0غ 1N7u$?L&XoK-#$օUi-.hCEu`j|z{֌ !ޕ3`%/ d l"=WByۘ𭖙~u)H? `Az;ޅ&A(z(c giYO`Zr?GvR6C,$/-00{ )u}Ց:wg5Kה^^՟4O#%{o5oBKae-aϾ>fg\u*p?T%%xTK'K.Ooxʙg}&&ܼ3˯_~L|^-I~ͤ]n>6a !Iaps/dEXl'",CwDX\̋"@_`ad {gEXVKV<&tnM莊0FZ}Coa[D8a R"RaOe DXV|"db"j"B[DXn#Tf| 0*OOa HR殺RXNf +:ExUGI$0Ȕ)#GG " <*p0EG?aHG{Exd;/9 +IJ u)1QXE40Mj gl(Y4N/i> P ѴQDi)l ;hoQ/QK_X.#{X ӺxhD~DŽ:8GA .Ӂ)EG+01xe5RcNx!C('NQ~D,ŸyE6@!p7A!ASm`bh>(#6;FD/P|>QMj+G#%jZ AW͓i$Π'-j@(z૗]5"K$b0yiHRᯟ2~ O{)eK 4E P:iھ xe SQ(IttY()y.i=4ˉ>ɿh4"b:Gz$x' hyv(HҴi R4J{х6Ahˎ-ph]ܾ:mmL**9\GRÈ؟_HqA(NR 9%i݅2h^$+m4siqM5n1(;R8|C%-.#JR^ -pxHG{ Q'i_+4FCin^7ÐFVGKi?iw "! . 1$rAG+y9iY[_d}B1J4_8+`2k,2Ð ^+hBY1ʉtXFh%׀Y s/i=khAl2 F&@};)Yw>9¤Q =6.*,rYt7N5]&Y^6ОaAsR,BtL$yXibC{8r 5ʤdcKRxIZR(-WDX肞4f?Y^ؓhO.="g/0|R*>YrXl}1x8+Hy1#WA')I Z*b/)_hUQQ>{ ej(ʹ\z,fv%F^Ցxދ9E\OKȼCX6 t~@- q,T;Ns,xG.R%k)F!q$Lؓ΁Rq ::kDk1Z!U"\kj1$Xa0vGC817DF" WGbH,GFPoLH>J8<kk^*!~Sx`07h#<CcppdSh 9 XqQ0/Ie~?EDEFAB7t\ |SBx" >Q7m;6e|p Ccl!'Pd / Xxd 3|{"82bxdMDb`/4VVt A93i^cFCAGZBš##}PX<b|?06 ~,BH ē=Ю$04z#!>2"BEbC}q~8ēx?9DQ{c!J8F GC|P8?⇁ |r/ EP8ꥌ& F#b ]ã!dph,BPFF '1H#I(p AqquF@Eār}|/;ٛH8IECdJPp$>2D>>cAH %½q')'~#t]ohhTxd'<NCD 'H$p [}!`d2.IO$3NwGBqA*b!h M U$CGgh( &>XٰH2`bx7 |ac27& II6v4i^ܺo]ïo^ݴojSSӆjZIB! `P M* Uj$I( %i C@B`e|'$ "= Pw2'ˇT&Yj'"!AH g0d hsHA+Y Bbhp(I`<J,M]o -e<] x4dy"6& ;FiK EIH'>HA I9@aIX1)taA#:8-Mo(6" &M##Ihh"> A;"PG@ H@+_1XPĺRBJg+H 0W5˽*_*k*keյյj_iuɗODCX,G2, "qaq-LAޤtXzOظccgo'c.\>X|r`]>X|r`僕+V.\>X|r`+~,ARa(M}a}00{p%jv=^lK(دPp} }O$>^ѽM=&ig㳅]C!``XN ݝ`c&v4n ajßhːXdxlyLhp /5z0ԁ̓iRS`@jNn0R~IHZ@d1&2T17Tl` .{(GwQlu>G9}riq[?s]#'ֳ5u_A]Gf+j W0:]`*Y10O$jF)$ 1a",TֆuvA![SiOQV1~o;n;CێtحU030Q0pvneC!4.϶8 zAǨ@jSЬSN:֩`0Զ2O7[ j-Yɦ-jovi ˪U'V.J+IWmWdQ;.j+aQϢjQX j*jRDlF-RG@P-YҲesCpzҲԂy-2zcavM%n^˸TAh} 7wL__45cv fv5m"xwcB4dc DC6 F155lBof 4ҀڈјBy9B AL43ZVhf$Ҍ QJ$L"8kh`S@9i9NIx1[R a/7uۛB`o4NOL/I?ͺ{z Mj73K࠽q6ul g`=9 ڳPKd6A2[Mz-$1RV )X1ZV˦Umی\}1Jh&k]t%mWX3 [i}մ 6xHNodVq|@ ҁ޾ y)ܸ?'I&=`'FꗀFkmo^smuuMӁN*)[-;QR[p$o K6Mۤ$mGulI&M$|&qAq0Bqg.eFO`8t_09zYo1HJNV+σ۴Qp*W*08? 0! [V̓vQ܃}҇ {hÄ܉ǃ/Zas#O" 0D}$Y 2 endstream endobj 38 0 obj 30227 endobj 39 0 obj <> endobj 40 0 obj <> stream x]ˎ@E|b]#Y<6H^xR >ܺD֡.d<nNMCss|v15>>kGNkpeͭd{zg;a˽<=];(:~kcZOqo4n_vv7?_- >ŮUnTWmtoj^/zZJRn+ 2pnXR5^so~K|=w| ;pIރ+#ӥ\zG 5LbxOw`Cr/NΎj9W ϒnB _?&W]_Z&s#ga9|w-,W˧/QgثSWg_RL ?y5WY3iհ/g?|4-el3izwa. endstream endobj 41 0 obj <> endobj 42 0 obj << /F1 16 0 R /F2 41 0 R /F3 36 0 R /F4 31 0 R /F5 26 0 R /F6 21 0 R >> endobj 43 0 obj <> endobj 44 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 8 0 obj <>/Contents 9 0 R>> endobj 45 0 obj <> endobj 46 0 obj < /Dest[1 0 R/XYZ 28.4 801.6 0]/Parent 45 0 R>> endobj 11 0 obj <> endobj 47 0 obj <> endobj 48 0 obj < /Producer /CreationDate (D:20061115224308+01'00') >> endobj xref 0 49 0000000000 65535 f 0000258682 00000 n 0000000019 00000 n 0000006234 00000 n 0000116037 00000 n 0000006255 00000 n 0000116014 00000 n 0000159008 00000 n 0000258826 00000 n 0000159030 00000 n 0000160984 00000 n 0000259236 00000 n 0000161006 00000 n 0000174485 00000 n 0000174508 00000 n 0000174711 00000 n 0000175084 00000 n 0000175322 00000 n 0000186271 00000 n 0000186294 00000 n 0000186507 00000 n 0000186832 00000 n 0000187035 00000 n 0000203400 00000 n 0000203423 00000 n 0000203627 00000 n 0000204036 00000 n 0000204306 00000 n 0000205820 00000 n 0000205842 00000 n 0000206032 00000 n 0000206410 00000 n 0000206636 00000 n 0000225947 00000 n 0000225970 00000 n 0000226179 00000 n 0000226630 00000 n 0000226946 00000 n 0000257260 00000 n 0000257283 00000 n 0000257482 00000 n 0000258049 00000 n 0000258458 00000 n 0000258547 00000 n 0000258591 00000 n 0000258970 00000 n 0000259026 00000 n 0000259342 00000 n 0000259408 00000 n trailer < <4CB40764750CBF03DEC083C1ACBCC224> ] >> startxref 259597 %%EOF wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/glisseur.odt0000644000175000017500000026330612014170666026126 0ustar georgeskgeorgeskPKi_:^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKi_:Configurations2/statusbar/PKi_:'Configurations2/accelerator/current.xmlPKPKi_:Configurations2/floater/PKi_:Configurations2/popupmenu/PKi_:Configurations2/progressbar/PKi_:Configurations2/menubar/PKi_:Configurations2/toolbar/PKi_:Configurations2/images/Bitmaps/PKi_:Object 40/content.xmlV]k0}߯d6 ;)cOCJ-c=o_Զvi%M/s Wlzx2l[DUA4Kd*芿ML<:a,U:k i hrV (f>A׺}>>\ł`p}< j"Z;nk% "\". ICBv')c׶sx`;i3 {QYmcl`'][ ߸!߻\#"!Ҿiou-t ,7^{^s@3['J׾oՃ4`i]qN$x;賎z ?iٍ#Bof0xb\+H r+r'm3)Y4F< t励qeXV]ߐ]+Z+pu &PSDR6N 3ҒӚj8 Zl$,ܜz  MIlP07ŨU,,aI4W[ѿ@ -${<2 hfTF0AL"2Qzj,@B(j$O*bFbQ0pM)Mh =%c /+vcȟ _ JN*I(jӈBfw2gvXoMw\"WX4/D8p-ؕ.x0}F!NYv 0LU-gp:2e4@_Ght F4( )ւy׌vBqIoLLMP%)ywƴ⣹щ Dh(kW[A}:KZ+ڔ=T}E@ѯfR#ř 1j-'UYReH]NSe%˰/㳠*"yIG$mPDʚqΓ@fZ ʤkv촅ӭ`! }\em5 ttc:^-q"U }d: +:B1 P$/JF)1,rܸ2~PcZ[8xu)a"0EdFUYb +Ӡ]̆bq>fP0ħ)F\Y%ojvL'=D\=jTq1y"j}0/*;0iFet4+"&U7Q14*XQIPL]C/ KJZB/=0`\7EWY!&@/ >(sr]AH=EPKV PKi_:Object 42/settings.xmlo0WvJTЮtل:v 쯟MmɏR |9'uj $Evu勶8?vn:Z8SQ\IPJ5=K/v"=$J|C4ho /mgTj:_]Xfn&C}s8~B=L.nߩND~?gTA`bS]6^)Qs}w NrGmB}rt-7mx+ fL-^5LXfh\ȶq,_Q:z]Fܤr¸|Z,zD=rqR XPmܸ6l~fau: *Pce+(a0@ydBuY2bj# ? g@ۘea䦈 w:s$&aԷňobU60(G@M`*.68ODo)YM=jڙfUF L"bUfXjo@BQ(˱Ot ⿀:,)UhK$C!/jۯ 9X. )Un_avwT*89?>H9Pޚ䲦I}>'Ue hj{UN>taߩ?==&''٬@#kuu3 \H;;;y]]]+w& fyhÇZ5P˼\.^p-"jWWW0H&JW/rRT, fyZՋyE2ROh42XjD*D"jR TTjpp0uuu)"\ȅ>;jW/Rc[捎f2Ka[[[y}}} !R#[Eѕ5|>P˼U(RS2(Rg fyߩPz=Q:\]]Ub]ϕ秧C-ݻe(Rg\^^9::RkZ2e^__r@u"Lftt4EE*D.OmW|O.SweP˼t:P|P˼M Ed26x<^(+re_P(̄1-\+zkkk===,/%I#g\L&y'r-_5͎i\׾w^4 fy###FB":;;ERTy˼D"al$妦B-fgg9x<j7::d Dh׻C-m.hL&C-EݻwHbeH:6fH#NOOO0+N@4 i@4!rP˼|>ohf9Е.-hO&:֝P˼m@ˋ4ݾ};vqq@"MwąBaff&MNNNZUI{}}=2opp0LQZRy=LM @4g26x<^( -$'P(fggCYޝ;w٬eDZ4Dggge5DZLC-:;;׍1- J'Bl߿4M-ztP722d 6+ҒgbX244H`0ˋF+++fisrw m2i3|a4 fyZ\"pۡy{{{fiLRy^4HjP{neT6:E[ z)"mx;;;,NV=Ohdd$EGyAИ"m{|~jj*2off~/ Muu+ i JD {zzY^WW8y'NZ=|P((@Bavv6MMMr9 >byy3  Kj2\#A)rDhy\Ac?>>V ,kkk]]],O< d2n-b2oaaAe2J=xe^6U 666Y^,S. bR@0\^^V. Nm7==K"ȫB4 fyoN*eUo{{;2gkkKe;A^M۷ClxPPHW|>P755%yj722rtt2ԅ nvvvz{{Y^wwP;A^=e2P˼h42HWgBazz:2orr2)U]P˼d22TGwYvwwY^WWƆPA%fcccmBAqAޥ{A(d2*@yWa}}+ ]X,P Ad2omyWP(ΆQ-8 $y*G2 +2]l6;11Q2/)!kFY@:VyokkG< kLfhh(6 }A^ӡ,ojjJ<5x<^2/L @5`Y|3A^#:::2A^rw eyZ'A^C[ZZ2yoww'2ommMeڊ d2-ڙ 9 P7::dJe^,2~, []]U&k>ǣmZ0A^yC666Y^ooΎA^sK&Ztj˼L&8KGjWWW0K&*py࠼eÇm.{ny˼l6WTYl'B@[bd2پS OB@Mvvvz{{NN@ty˼vy-P|~zz:2obbZ東ZvrѣGyT]3:AP#jR OTG@e26{ V*y@- _Pw^(˻sqk^Ey< .zggg0K&-x 'Q핷K$vU I\le\˴̻:;tBa~~>华f2Vy'pEDwww0k'R@d \T*łY^4]YYiG\YW*ݹs'ݻM2O\Y,ohh(N7@ \`ݽLOy  kL&B-bma,()שy[ +&E-,,KR{#"B!Z٠ r"ae^Çp z"H. eylp<rB4mؖy<ZhDݡyD/ey<lph{e^s7AP!+ϗoyWCG{ae^oomEG [__W}hcccm* AMP(ܻw/卌+y4`ŒɤW$r5>ҡh42u$ȃ*fdFFFBlgffũE3< J h^ h42/ɨ @ypWԶY^oo9<ɤm=.{֫R h=+++ o)h)VWWUh1ۅ =8TZÇA|A|f ݻw?0??GV?H$v~+J?d4 TZO<Dpjx2 OV)\XXXppoo/[|ޠ yhWz.cE^Spy88;;e"nuPUkgp0rpjjqK5 TZ%ݵP(*D"qa<|$ؚ?N¯rOP\]0(+ yhWzG݋y`tydNbT*uUmmmX˥Tkvv$ ^tCqm(Dgn~*4==z&//af9|>_ʤBҚD׸xPAZ!2A]J@Kh,sEyT͋Yl086[/#Ujm(m^*x‘ TZ>:{2X,G?eA4+{7/XhȠE `)@;;u`uݪ^*x5zkkk7nuﮊPGv>VSZ3xʻ y8xnsFb ?yP7+++[n޼L&Qbubq/⥧@3A=/RhO @:n$nU|=K5p7v޽FST^ZZ*^㋋ jT<zddD<E0b񠞃5n(۠p4]]]ccc+++Wx?*.^>PwB'믿n* 匞X56WXA K\s\WW׹Fo)\~:cg_qUmІFVK.Pl=99Y|eٺI2ЕR@/ȃKU߼y33ƍZ 7776811q~JI8XBkVR^NÇwwwz> F"|$Y4 8B/|B;1pjUŃm###'8)K5\YY ł67ꂼ7&ɲ/E"H"Hg]9A\-pcAXz`4(ԓdr\َکVth~yypES[moo+JkOP` Hx+ֶ熪sss}|$R83+-633-A\[n_^^Vz .nCeUqC- kllѣG&GGG͚*NNަYҡ= JrzԖy@:Vť k`)Оyp=vww?C?_x-E{bAkJ{V(qI&^Іypm/`/ήFpʻ O3ZUw6Zzb> : ~g{ZВP\yP<_C7_zE%hC<~-nݺe<TŃ>ַA^ ;fqmH awwST "hv>~y],㻻n)~(,ry I J<&wNq~W?::F'D  >\NX|T*e@= q~|W2WJkI:;;GGGWWWt077Wk1жyDqF|ӟ2*nxxxzzzuu5J |L6\whBA888xWB7n+T⣷x酅ړ_{_dOx'j %3A4l6{j˼{i:5]DF?lޔF777#9A4B0;;[37ްvүVwU(~ﭱoA4y===SVwR(qq1htff_/--)4L&366vFHhԏ޿8WIDM)MNNX={vo߾}pp@9A4nW;FxqJrt8 Fy{ܖ^6=uLSWEB@9A4?$X,V-,766 WvG]?z+-GyC޽OQR Zʍ7B3W_}U<>V8W.Ԡ$ŧ:ur^nqqQفAD"SO&XlooOq ܻw*9ByFR͛7C7nhUcF϶WCA{rof,H=̜xo. 'fz $_\\,oke2Mlnnq;mɣGNuu4.@ ?;;;/BhK/߯8L&s)^___}Аys/~-JBvxx800P 76NG*r@  Z:颳䙗xh4ZZ (rƍsX=Wnnn=I24"@ 8ny˼^<d>[cWCAOH$ P"Ndvtt,//+-Up~]govdw2vV}(X[[+oꫯi?Eӷ÷jٟ;88hAp_~94xgdbEӷ3xB` <"7'x\qhIBall4}'*bX)z54X'y1?'of6UZ\p_VH$"5K&8!.f}}g)c*RZjp;55unmmq[C?r߹s /H O~;677L&[br\飩Tj``Od2h F8!Q|.d{@Sf)nwww睈FǥY^^AN?-g|;2511[ІgNOO?:<<\ oppР'y@MDggghqM-hFYϟ;.m=88(.jOؔn 9APW^)m+Mdcc#ݹs-NZW#&N:8>>/p!M!LoG*B}]jcy@Wǯ~LFqhd\.x#ڮmoo/RWD Z__gCӎ_|qwwWqhXF-ϖN֖ݻ7===>>Wuc/.J} _<:::~_* (dnnP.K{{{DΝ;}n``cLNN&}Apw__i@CY__&hcccW3e)}ѥD:L$LMM޽{H ˳XG/Z K՞+NLLښ*!.'>P /h+ åijggޕ}5MM\/~-󖗗k477^W)}t:m8JK~y?Zw޽i'F[WcmmM<fpj:22rQQwwwBTN4-ok@2Loooi>s7(IR2R@y@#J$ZPB022P7()*'T*g>z)-8lpJV~y˼G hwwwK:99i muuƍ-x<>|x}}t7򀦷;;,Ύ .gr "p!<dٯ|+,޳%龾ܯ{~~t;%ZDPxw[MNNr9h۷KSh4,1Km pQ<=S,֭[dRqZLp,G5G*2VS{g,..gzx~``t|h"ZP&y7[yMmkk+xygg.Jx<|M-T& &:|>_:X,f@,Htvv7ojtpp^tStbL-.Jݺu+=SDB7XZZjS+Ԕ1.J|>oFq-j5t3NDfgg +pQ<]2_fR~ (.J>n*Rt||<00Puww6ϗNduu%˩-h"PFS(FFFӶETީh<~e&ݣЪŦ>ҹTAy_Wj~L&ckgg^eypdwwwiZ[vb wm-N-d .Z`#j>(Uq-ݻ{7Qq6<<#N W**T 4PAY^^.o/}I+8Ih <V-^|d28W#HDdlrre6IOt^ CL^ eyKKKpRTlenaatjxpU|D>˿yy'b t+|얗8PA)VWWzP788e%NZZ쀫!8/}x<t t Ӫ-]+J)/,,w~~k ͛7CY^4T3 .fggKeTGPBo~eLOONpSSSSN$^@uy5Y[[ w9}巇T!ô6zoo+ V&/'j\ NTGP7-ݻepsl6`z=U2 Ҟ7(IRsz&_~9hhvv*Y:q/ j8YXXPRA z,//GP799eФT[[[+P AI&/b({WWR .%|svvVAZS& eyhl68ɒRe\|>Z .033S4_-oi4Ƣ 'P/ykaa!8EQS핪4<< @y+;k-Kq벾.:44TBMMM)P#A@{'Bq޽{TznpRfgg  lll?cLP.F:;; xTj$hdW^ ey\T$HI[]]UFW%R R~,y@u2LF ]]]r!˥NOO+P;A@gV2b}}]M.* j'h) -xl68@ TQq&U-peeEA Z'>Pw֭t:8VWWhiqB,UjTv<f/'|׿g,FFFjåJ @y-wމyT8U6/MѨ-PA ZʓO>؟Bn`ݞP(~p0??&d2bvww+P<֗fQhss3x;wEJP.yw-o4\:fR=C?{g} < # k%Ř`RDbrfR'$`R38܃,9Zu 0Ɉ6sls'2ʍ317?Gn[_>yD?gw_?2z?ڷ|Y<.rĉ#ٳ,NZ-͆d2\0Ճ X<2??ƺ[J%@~Wpi|> Xp@բmr@բZ^^c]^:62@R><<XXEVs=YarrR8|۷o_ŲFFF"ւ"wsź}sjU8Xz}tt4|NRgϞKd0yEyyۑyp ;&HaJE jQp?ĺu?$MPHRKX paHh_92//7䘟 _wa FӔJ0l6+`)`Ǐuy~{V\RpQ*4333a###V"1??m۶XwW -O:}i4SPy\j:22:|pI0\.'`)$=ʑy'bd4ɓ'prÂ@UR t:򆇇 fZXXpEˍWm@U`u9Djm6#9p#333V"QV~/~}9"!lx!ԩRx≞XCA X#GNNNʤ#G| .Elfffƍ.#p`՝:uQTkd.ET*uyW^yܜp`---m޼9|ݾ}.Z]Px9٬@ե`MAo]92ꯄVر#|MάbxEFGG.Ek駟y{o㍇pI5BxE.Ek̙36luy_}R\\N&I]`(Xs7pC۴iә3g.FFFpuy4Co .`b:449 /MPy4ω'V̻;TV=baaA,1<<^YK@S5b]޵^(xW8qB&244^r,`u)h;wƺt:P8r\srrR&I&@AX]<Z^?+G}SNEuET*e2N@˼ +GݻZ J͛×;vxLe Q6yܖ-[b]5\>X8pNVpd*G&:EsrwƺF_% Lizz:LV"D׿rd<`]رc\.'y$ /nݺX722RՄ@w{n&Dx:E m۶XuƯ nSVo !p*V"djsO}@W _ Sԙ3gdp%s y$O?md,E_}Y$_& /YRy$TXuywuytT*߿_eikrUTvʹ9Щb\ڵZ-o Z&y$Zuy}}}w@YyE\K[ /@ }Z8#GD_q:6Ky~===:/-733g_+NRq}{[92odddyyY8$VlLD&YZZKT*K)`(B[nuy^{mׅ@耈t:mZ+^akD@cX72:p@رc2ikb1cccֈ"|eȼ'x!'NVՄ@;v,͆RX#<:_\^92w~wfff@4^qtk.a4+Ap=ĺgyR="8|А@#o U;"VUt|>^ƏEJ\L&酅t\.^ikG@Wc]M7TTũ###WSN9hEݮR޽; "<*޶T* X;<'=ʑyw$9}ttnȈ /͆W<kGwu{wbx<՗yJ֭[c]Ν;]-//GrE Z XS< jwwc]e]K/ w0::}pzi(OR XS<x=XOOO;zdx[ї\.'.1;;^1kJo_uyZM8D8q]P((pQo5??yXm۶r,Ldծ255^|>/`M)To=mذ<~ڵ+zܜXJ. @PyvdޗertĚ@5rԩ'ι뮻Z.e҅J%kJ\.uyW_}‚pͩSb\A .fe` kMVu].<= ݬq ֚".__# wݻwG0kA!kM̙3+GzFtÇ;sr5E\Rm۶Xe#:ѣGpAhvv6\ ccc֚".^^g?^xtgF޽{wZK7+ zyp&''߳yRl߾DpI4~,`)`9sfӦM.o3{FobQ,r9fRjrK|7:{st@3)`{wȼ|;h_>.x[###(J֚"Vً/Jbu?y#Q쀋l6 B 4&P+JW]uU۹soKZp188 BђW@X^^馛b]ƍ_}Uz><< ~  hE;Çw1$?ONNʄR.aM/[nȼ XсF333]!@kn~~>ĺk\.  p( "<hZwXxP8RT200P*J|'&S@E$KZ7ĺ+Kյ~\p&&&355%9y8zK_Ryg*b\<2ႌP(hE$3<ۻrd)hff&zpb4Mfgg4"knnZ92oyyY8T*m޼9... 544r,9yhj5ƺ7P.N>-.B:W':Q@￿'62駟K=*.BR WQ&4<h?O-2Jp!eYM1???00b\ٳ_p))\KFRĦ;5_'?pd@T P(i||\ @(ÇF_R###{ɓ'¥rrFmG?Ѻub۷'bprኚ4<hW Om۶zK8LOOGocccz],\B UX4<hcZm޽+G+(Jq׮]ۦXtk hE??uy===O=dnVTpZfj4MNϯO}jyyY8@w T2apuIh&7Ri֭.+|7tȑ#2aA.L&#y9jmRBq o`pu  h&EtƛՕ#&&&A쀋۷/--U4;;4":+qXя~<A{788 VWP=@fR@gT*c]ަM^ujћ^X njj*\cI+;3}yb\hXX#\.\fSSSIfOOO72$gΜpO]5x '*<|3336luypиm߾=zER kdtt4\lI]v׮]./NkZVf-JVXS2Lݢ^8p |4}lf2Lޤ4t}}}:o߾}Ah;O>dlLXSpe2M377wǺk歷FΞ==b޽ZM,r.aMnTTX~'͛\8& Wؘ@&S@ʑyO #$޵=*cjj*\x|^ @)ەJ*e2E +OELڛd<c]^(@ΦN{n\L+Lz~}g{L8@B,-- 7͛7W*L ,JL>r@AиENЋ#|<7[ƺ+rnnN8@ GKG ͗dέt:- y@\V;b]޺uZ8@K;v,zG:pLh[144$ w!o'y^L (VU|J%:wB @)^c]7l*4juǎ-hpppqqQ,l4"x'KKKc]ަMQ8ZCBpAkp)NLLh>E. {c]^__߷mkGy|IBSSSj|<|߽@8ZpgF'599.ˢ@S7\92oΝFT*m޼$D&Ţ@SR|^92W_Z]›.HYͧ.؃>?Vž}wӧO˄$f4:<bLOORأy`dpq\LL&\ZHo-[b]АOX,F`dd$Dd2ZB\һ[o5.ÛɎ;BBpq h Epy䑕#~i/ݻwGJa$JcAsttT @K(U/_>hw]ՄGo BA&$JcMs||\ @K(Q*Sٺukׅ#GDo\N&$Ԕ% "X5Au].oF`ff$_. WԔ@POOc#}Q+U*LfiiI,$~-V+aÆXwDAw^\FGGõjD\~{߻rdps&&&iXl6\8-JYnɓ';v,zs I=Щ'%yzgb{޽{pAA(Uy{W7n>OUU@pXHrl6+Uy@޷Cuy\s‚p>JΜ9#nvv6\ZE4I;bjjJ&$_P@VQM|/Vw}cA78qD*rm'χ6 hEl!zFAt:={8v¥"hJ8mڴ_tl6%.[("h/}K.矗 t\EF xffF @(VzgV>WՄ#vő#GdB{>OZ*"h7|sͱ.}{_RtǏGwؘmh;f;-ZZtM.oƍ?OmT*EfAt:-y@R<===:wMU*>H;jh-y@?_92瘠ݻ7O:%LGFF"H-[ĺn\. ȓO>ōʄ6U(C"H XN ©SRT3|>.\.'y@B=+G=cHpر\.|>/y@r ֭uyw}R=w :x @Rm۶Xd.Hn=}LU=33#y@X5аnݺ'DygtbbB&tl6jOSO=O>id$DXp122b{2L B<m?b}_^^@ At@R\.oٲ%  ZV޽;zXםpmgYLV;c]^*z-166ݏ2cΆk{ttT @k)o}/V=frAMMM9V(=>>.y@߸qcG?j24ӧpAgVչ\N @k)6VTnXw5הJ%Z+˃MNN|jjJ @k)F?~\2v޽{pAǛyPZ<أy_jp`-;n0::Y:DUS~|#T*uF;tLTR/ZKtjz-ĺW_}U8ZΞ=N\xd@R^:t(62رcKWT=Jg/ 7%ɓ֭=ݿAяRgϊVTf"Lry˖-.n\ >Llr<cUoqȼ uV:pG\xb1\ZNtzkx駅ovv6zŞ={pA7 }.r<MOORX7::QTo$d򧦦" R骫uy[n-wPGFF\E%s!| y@jGc]ކ ^z%o裏F̱cdB @SG]92G1V:qDcccc2 ߡ@(K/mذ!}FA`GvUVBW 4$p3Ҷmb]5\o;100 MxXs&<KA0::~bP(ȄnST-0<<, y@W{ꩧ̃\. ]T*`ttT @(n|ȼniyyY8t'OF8x^T,Í011! y^^^f.磌22nS*\4C`Z^<R׿/]VE큁R$V>Ԕ@$P[Xh!`߾}ѕI&&&P,$"7_~+GU*&''kG ]nll,@WTV̻+~_ t{ @,tpSeIx{{ow#:L쀋L&p_PVǏ_92o߾}F1 صkWŠX!N4PJ\FSN8!?XE6<w|7ƺ 6¡r2sfggí1::* !y^?C===y=phSgϞ ?9 )<wwGZ&K\޾}{􀋥%@hjjê@).UW]njdm^ٳ'zٳgQ< Sn#^8CEWVHP<cg}k!=]cccz],3::n H?я֭[.#H{\m ;T* HE[\\b]ޖ-[CT*\yjlp8HE%V{uy/pHz}(JEo}/4ݝDp'V=æ.cǎ~Jnl6+ 9yԩSׯuy{V¡ BtYl_FFF$"`ՔJ#r,Ze~~~`` JX,FOv<T>ĺu?-Y;vpQTljj*5\N @r(V__G۷/pcrr25|^ @r(믿<9vph|>]~SSS2111nB  9ykZx㍱.opp7kX,R)\E ̌@P/~.oo$Y\\d2fNO2)JC{#>#.EXjڮ]\87.Hw> (<f۴iS˻='u)F.MT.BAnt:- QyMRV?Ǻ+-3r:/i Z'pAr$"xl?}$sFVC'rԩccc> jvv6DElw{4K_d.qJ%\+ 6\ @(ZT*uyЇs."/tzaa 055n hZ;!JK9ORuXwB:u'˅[) HE@+}+_Y92%s>."gup7MOO HE@ׯ=V )Μ9=8.EYb( QyW.W۲epށ"RX^^0R4QJ@DQ$B^߻woK?τtyX3-+@DQ$7#/KmuywR9q%(h9. iy+lܸ1h'> #V"ozzr$" qXd|MD]J-.`Ά;kxxX @(^{ア./J .MUW*۷G2 VKn1IHs___{衇<~uEq#+ח={pk$χ+ HE@W\qyokd5ח<11] Ӗ\.|>/ iyI}͛7[]LxSSS5011aw Q@)ã>ws&Sg:IhNJRG>wm%ޯ LlJ@Q˫*tMY{=wz)t:A!X# $"jO~./7-.)}Q\@Am$"-b]^__߷ 'OF?O=>>n)J^fHЮ֭y32㋼v]V|X#333d$"뮻.ܹwvWTpMS(766& y^u].oZ]䍎FiXaMpr9 O/ yg?e]{4ﳟl%wj7==랹Bccc+ H9*X744ؑ_oGyRipp0zt.ipH E@G 3L|aE^쀋#͆T* H E@׿ +b\>}bI@[St_ׯ=O}VuaEdb=䓖14MR w_&L<cǎXu;+ T*R):R @2):\s6l/w1E^쀋]v-//[Lb1܃IF>|3_IݻKNM6==nqɤ @ѼNצ(26rI-j77|S8;СC2wԔ@dRt|0 4L -qp3%:?|?:OO$LRia۷o/bV?YɤFofs##vE*R@kڵ+ܒZu y]Vx㍱.oppȼ&~1@kEnÇ܁Rt~8Rډ H -d$"?VK===>dB쀋g[ZZ we6X<~_w~wb5V*ᬢrd\H`~~>Z H,E$[o5mܸ_j% H'O{sllL @b)W>`OOO흞̥pqq@B4rrKoxׯ_{4 _B^Eya@r9r$ܞ|^ @b)+JW_}ʑy¹+@, whPX<Fwuʑy\J=㥥%@8p ܤ333Kob#?7)H4b(HA4RI @b)x'J:=O|‡CG쀋#Gh`` ܧnn@)xJe.}{_ׅ`68p!7"NKHJ FGGc]e]?ۚ ڵkWZ $RU$"3F%;b``-HgφuxxX @)ʑyGkpBѧSӧeU, ;66& y\JcǎXwWWί_\@ 7Ą@$Sp طo_[~~.O&vؘ. &''=$"tرȼ[ T*cHÇvzzZ @)x/6l{4oΝEZ CHV$_teXd<.IRf.opp7ǣ  k޽{Ýi y??uy}}}!|>ڍق6}RH2EcȼÇwi'OLROFN$"US*l>OTN\ u|@h_St H8Eo\92^/Vڵ+2Sٳgh#sssp<V<z{{owؗ966}YKP@S&ovȼ{キcx]th;ǏwĄ@SVJ5\"wt:D^8p#7~, y Ε#~_d/ƯЎ:婩) `}_^92駟nǯ%{FqM8p bQ @)h3gkny/#<ֆ  y4I\kc]PRi/T*߿cɧyc]^:?@޻w.G6k&O?F?$|dۣu,..*J7o, yoqW'?g}pnnEv7;;aɧ5>Xwu%pd^.!}Y:@X }$"_c]^!\4ajj*ڏ?@Sb/b);'%V,tw199|>/ yێ̻kZ TJeg``I&&& ~qɧ ]b]͛-AgϞOJŢdtt4O|<^?#.oݺu?LQf=y[-(H_|qݺu:n!:Γdm^V$"y7V̛ vj>`-A@ ꭷ_kݾ}.p7v@ կDk###чt̿N5;;n{ h <G?QѼ??Zu\% Bt@ -[b]_ST?x.Eϴ9|@ |3.oӦM52T*Ef}ȑ#ڂ"=62wbPxg'''=:==jZ6pQ* m||<ǏyX~}Ѽ/~+?[V=k׮fCL&˕?y ?s@,-- ŊƯT*9znRm\.'apcO$"}.mذsQFGG] 2LE*ϯsX=/j`oo+fD/]a~oܸ=a~co;vܙNcܜHpo߾] @Pjڇ>-o;~ܠ=A7 7@vڅ"N0>>-߲eȅ;r<5y''' Em/hwA=6Ɉ:X?.](h{sss{O~ZqdaaA>bQ @P?'G.`/*tl6yB@;rH뮻"ѣG ,aJ"](h{{򡡡+{A=N h#<ڞ'W*͞fEmό<̄Emϩ+ fFy o -vL&#R`|>\N @Q w[l=7{<|^ @Q Ξ= z(/Ʉ1JJ%yBBA @Q!v9_n>i>DzKFGG-?33#(T*|~n?Q}/iӦ>~_Yܹ3:/"OH?mD@͝s_wuCCC3}}}_ܷo7a@Ri< 144477WV?_ \.< h/<:ѣG7o^*:t֯_Xfggڋ"TV|t}T&yWƧ~:۞/:@P@-..8qg瀕//[B?d`J0ڑ"VPR#]ޝwYVi||<<GGG#E oE, ڑ"V{zz"#~IVG>R$)` "e +-˅]\Ўyz̻fgg+*Ʉ̌@vUSO=O~IW#E4oH$_|Q2BGZ*Цy~___ּr,Jeڔ"V5 o2 W\nmJM{Hn:#`JЦy|㑑y{9( 522"M)%tM. @8pFGGêP(hS<hZ_r~&|><Ţ@63D5k֨jdЦyr~,}O24===-M)?>JEᄏZ hڔ"ZT7"] 7pi.}_2g3@+;|d U*оy}Gn۱cG*344$}) m޼9 U*£& h_<h?p˻k'''%P(CfddD @R@;˿˞.oۗ^zI2iFGGellL @R@ȭyַp`|>)bQ @R@j_#]ޯV*@D6  K_827ސ 4iKm֬YuwwRTxkhk<ho?nȭy?pzdR @[S@ tygΝ;':LxPimM1GD"oKNV.#"hk<7|sݺu:{dXb1<r@X97~^ T(! hk< D7p4###Q066&) .k'QC`||\ @[S@l?~|ڵ[|I9lK@8ٸqcҗddb`` \ښ"b^_^82Je_T5Et??ۿ[L&9M!yȼ{,KJ%\tZ @S@[#]wQVCpg2N^_ty7pyOT y.yЉwA'B!\###ڝ":[.rk޶mی#6FGGõ=66&)sU*~.o1Å],;Et{,_~bbB2l6jKE_|122[iwL&\ڝ"w}/rk׾z.T: Ā"z>88RphGde<@@ (Ow֮]{ ^*JĀ"z7ȼ{N2p EpΝ#ZM8R.\.' yx xΝo||<\###b@\ʟɟDk歷ޒ -nll,\Ā"7xcڵ:dhe|>\bQ @ (˛MR.¡5rpNLLEpE-["]ƍK8L&.i1zzz5kּkդpuE8޵^5g -%L~8S4xPVTnHww VPÕ900  yn۰a/nzz:\lV @<({##^~e\<LNN.cA X,qddD @<(UV7o.#hp) ǷHL&M2pEK/f͚.?dXe\.\rY @<(oܸ1rk#}믏ܚ;;APR G:3<=Ae˖Hw뭷;wN8|\.k#ENzHw5׼[a^T F63<;v,22oKB.|>/ fy@Hկ~ȼ7::bFz>88~W~X|>\ BA @(6suww7vyk׮52cep%J%1kpdޮ]Ͷe2p e1oHwwutT*.J" fy@ADӧO .qHYN@liddޚ5k^}UtJT*% ~y@;֭ܚJ&r3@Qq6JooTUXT ww6?< xG"]޵^ ' B@Q 22oۿL,;z@Qq611qFn}O>wqP?< nHw=j5I6 oT?<#ov=}dbc`` ܹGt^z˛oo$U\Tď" 6lܚ#A VL&Ē",Z;ty6mӾfff½NĒ"D;vty֭L*d2bItW_}u͚5]^ww$ӎbs@XR߿#}߬i/B!܃|^ @,)VHwmUUᴑp %EϿ/oJ] n||\ @,)~W_M$:oddD2m!͆{T* %E/;wotyLȼ700iX[ltyϟN+KRT*bIGG G=zT2^{*L +EE8qb:'L Q:W<p7/j5ᴔr8P @\).GtydZGX N.W<8x`OOOc7믿.166q'N[.rkޓO>)V0221q".\Hӑ.oppȼr)J7#]ކ &''%DCCCW<ŹȼHY}1==- yV.ȭy;wLS$p/uqXz~]wEsssYL&Ę"`醇O*U3==?00  yWW^YfMcYaCCCbLp>믿>rkC=d^*(a\N @)AL&|.\Ί c<eSOuww7vy֭;zdVHؘ@S,z+HDn(y#bLΟ?|A#VB6 C.JbL"~HqsIfy OOO 1EJ./HYFT*R1E :qڵk#uO?-eQTɤ@xSr-.j¹J333atZ @)VP˻N>-Q.<3@xS^x˛ÒYbě"`>}zÆ [x ,MPcMj]wι9,hchoۑ.wߕ̢0B  yq#8 +fJ@xS4;Svȭy +dr3Shnn|>w\V* CT*ͼZjDtOR(|uVT#<ϟ?/\.fR,|uVT#3twwGFK& )MVp@;vXdd^+|Cs0&_] `N5"h AЙ$0 \"2g  Eo>(*J@*jȽx<  G?cG0==n@󯮻 wygȼ'xs(Jg_]U8Ո]p!JE/| Z6||<_]XS#<֯_~Miյp@[;p@dd^OO/ _] `N5"hwׯܚ|'ƛ--Ϳ^jq@ T;#m޼9#2LrWWQ)+=] 6~~/mذarr2~[NmiՕq<"4vy?cd2zkE ӧȭy_cJ%ܮT*W׊<`N5"j}.ˇ~+Fe2Vp@\}ߍtyd2#JREl%y*jDc/5k"#ިBnN>oeZEpӅ>"=CA2%B=n6í( -qu2x""#mݺ6!JRյXS:=^6hk t~[f믿ٿ:j+JST\]U8Ոcd2rk^>g8yd"l6*rL&c'C>Ûn)z;77iӦ߿ /T*1ڃ@Pt |HwܹKVV۲eK$ɕ~Bai7;E /,/ڵkT*#;::v9y҉'{dmq[n.Y>˰׀ΡcZ[otsj5Ʌ}_={+f*Jv9yD=ÑnnÆ O_j.>W ezz:"8p@OOOc77__g2+;33/,JeE799yv-ս[Ֆ%ɤ}tEZ%wy>򾞙_tEs%wy^WR.gd2v Qy\~ŧ [Ə(3r9(<.c۶m]Wa``Z.+) y(<.e߾}]WmǎA\ sll:"OuرD"ѵwp||:";{l___2I$Nʗf',JQy\DZݲeKײڴiռٖ34ڂ"hmm߾jOUT&(:x`׊ٻw/bM@qNX,6[ }}}gϞ] !S@Qpǎۺuuy[lY찼8k>:"O511}D"]^Lݻw;@Qpw[.P(\ qtEWdnnnll+k׮5` 8zruyZJm*Jiy,ɓ'o߾,]ގ;/6SSSviy,T.K&W:tkӦM4<VZOH$'Oğs؁N`yjuy6m[gϞ-Y/m)XfG{m۶-Z߿ppFP*[(XSSS>EkKسg߿q"%ސ/z@(XAýW}+_YTN}- yjo߾+7uumuu%ޚwq9`AP(U'j]]Ůts_o<V[Xvu]PE{lbL@sLMMر#L^؍xk]v +ETT?YM^YplϞ=+U y4wobZc񼼑y'`;"JD Ē"&jhR]]⋼ZWU3g ď"&;|pC[|;>>.U ~y4پ}y#S߿_@(hц"otEO2RG@#J(h23"&WB@d2M.+~\R"bI@ 7y;;^{##bI@MNNv}¢nL$bI@Kl(*W5w_bI@K(ˉDurZ j5yUݻ\oWUxƹx7o>{z><m۶ښ"VTV.ZR={\t^Pft?I\)yg9r?uԥebb"J5vyd<)J244ottT2@ROfڔ"8mO<)(SNEF%B (Jul}  E!{Fm۶UUmA@9zhdd^: yt3gΤȭycccZ"SVwvYׅ,EDd2JE2@kRй&&&T*edКyt遁./H߿?-E@\.22o۶mjU8@P/:t(22opppffF2@P/;v,22bbB2@+PΞ={FF:tH2@) ۹sgVD<#ԔdfQŕJ./L9rD2@S(SU*[FfgϞ 2E\J{tyCCCsssV".oȼgJX5<"6mjdա+UVmyݻpE`׮].o֭JE8R d2R)+GK155N##/`(`j‘y;w @8SA#]АyS:|pooociӦ H̙3["GV۾}{r.)`AwD" J<Xf'Ok"ߙ3g./H:tH2)`Ej\.sN#Q :tP2l6o|511E7;;;44yy(`5;wF;vTUWBg||<22o```zzZ2e)`Uo^Մ\"X,FFm޼̙3R@3MMMȼ#GHP@Um۶EfgϞ yFFF"]PR EÇ'./Ne?W@KY82/LE<h-k׮z.d<hE{6H:"ZTXk濝 t&E3g 6vyDСCVVs\ddΝ;]8Qy"#Ϟ=+<hǎ72:"‘yAbOV-c# y~̃499[,%qv5;;7<+Ez><ym>@8yADcu֙@S@gHR]^ɓ'%-Ngvvv˖- @+S@' `xx8KV&EtB700`d&EtrNT*ed Etm۶Ef;::pu(_6CѣGdddޙ3g$@|lȼd2yq@)OV۷o72Z"={DF IE\̃>-["#9"X}<RАyt<2 ػw‘yJE8jy)JTKFQWjvvvpp022o||\2 y"۷GFy#`)E52V"X'O5vyTjbbB2By={v˖-FPKݻ#oj֡C"]޽kd,/E ?wIrQcvv6t}ؘd`Y(eA>tyFPý]̌dj(wԩT*?~\2d<`ET*#FGG%KVJ^ty;vVR+k||<22o```zzZ2(<`ŝ9s&N7vyF(077}lGFF \ Ezr\V \"XUǏKӧN \"Xm]^__ypi< jZ.32"h}%./VUB<&&&##&''%<fffd2yQ@#E|ZG۽{w^|DFFmݺunnN2sERJR*j6m455%PRlٲK$ Nzsȼg}<:"hQ۲eyt,Eкtc7d@<U*L&%SNIN䂖IENDB`PKi_:Object 43/content.xmlVAk0Wd9MFJ[``faرj"mf'vm?qdZ[Pۺ1.!M{^W3LpV22>2ppQyvGCwPJyFuǨ nm Խ.%4WSIU W[6H+J^CK%%ԐN. @DGor\PwtS5 4mKGkF+2?TZO/ VzAcł[c= ៽|lc+ G~yԳ,'-G &˜r#_\HGi"NkXs5d b.Qlk+$HZ8 >akQEbrzH fFcoPK\= PKi_:Object 43/settings.xmln0)P@6*hZ$讀& `DS>ڄЖ&^!{8ilx!):m: >)_vɕsłяDRzl\z !Tz ={OyG{[X|e(8+Bum]__ۻP.Eţ?q2bgf;BMw*LlƵ+>jNּs2IPߡ\9fM[8ʂg:W,WgL\e|yqm:Y, t]r q\(D=rt0Q) FD,)϶\25n# {}%tX-Fh<ԄpC JX- tR] h#; NwAʄ"6vSr²3$)lRm8c`_T;gXM;SӬʈv5iWDLl"bU hUJ%A ÒR4K29X0n,~ @ٛR@r]ݯmvkwG"OًqYƏRN;&=GiҀ46̐4j A0f.5}"|_{Zm!\pD;p (7tad*ΈoEAD3  aI10 o Ķ2FD?C6Ȣ9er^3KIڗjN)6FT ݑO모"U-!QN(Xbn",r7[ix M}p>tPKpJ#PdPKi_:Object 44/content.xmlj0g@Xu0Y" iHsf'`'Yjm6؍7ᄜ!ؤUF3H-L&οNhnq@4 J[ILt81%cX0qI)^1 ͢ddR繗NfӦJjkTVmYυ]|KFT2J }zYeLXIxj5A]9JVyld_q rt1*a"ϊG WI(<5~x<3/iPK6nc;PKi_:Object 44/settings.xmlo0Wn%*hW v$XM|_?ڒeI'Dl|NכoYj70=ʖ=qvwve]tq.8QL Ru 5 'nYgAH‘!d󾷳O6>e/=k%ez>__#_ڭNcZ.]{G!ēW;o5~D~?gTB}?SY RXfe8Ip 3 EnCBͮp1,ԓ,e5?.Wsl6Pk?8*e>]2G+\1$LW$%m,rи2 x%Wn7q :PSDi)kaȌ4&VA;c} //2a$O]Sz J(Fb9a {:jTr1y"j}0/*;0iFet4+"&U7Q14*XQIHL]/ SJZFB/=z`NjVd5FMxyVZ<;۟ۘ Wد-07e/vD[![[#T\4ieo]k%ez\_]"_ڭ[{w7"[幨x1  =!vfk7vj< M%qH:{@sA%:6eZRH5+ks^>2JmP&^c-ou i+*n}]UϭV&vE/Z|!Dtt8,Vȵb1 X$%m _RmGBn?tީ}UX=Fo<Ԕ0CsJZ"2,i1 i.fC1@4rsDz LJS#bF¬Q5\NX@Þ`E5M >hFN42bY fЛĨ`TarD)V#yT3S'ӒRK2F  yQjlFH 5/U%p]γ ~=P! sS@|- !G#T\4iEE| 2ΝG+2s&;gzsth86(@;(=e 6Q6QuB[mWk4zf~ 6GԒr1Xkf'zXxX2(¾$<$.* Oh]roT2JL//r*CRyR`TaXhWK X.f,L_1sfF&g?%IFhlr9|y)ș-#+Q9g6.fv3;u{Q8[qP ʶqVY,]Etn:l'1lE4nF,E4ql!Y9~~UM65tYmm#Q[嬷Wxt }gAyStqmXgw6 Uٴ1:ra(7ḳ R@KUt JiQVg1sg:)g^,vlhFL豜͸(mё[5{Ț훇|d)գkPKYPKi_:ObjectReplacements/Object 43 s qcd0dFidn9@! F KI[,PPj*P*6 O ȲL$CpIbQpenR~63rF ݬPS3-3;f121+5@mHH, `cK& ! ~ Ayu1]DC dbpb fpb:\sՂ b G0iv|J1#ǧ LfX|:"ŧg+">maf;Ćpؼ- 0Beٰ#'׃,u dwӚ  \O킣9> \ O_'D||ɇHs>IuU J0Pi*$"9v8>/s : `.l Dia#>H4Q\yI 2-Z:n҃9SXnMr92ʟ؆PKqFPKi_:ObjectReplacements/Object 44 s qcd0dFid30315tL,$mRS,PiP՜p} ]@`$K+ss3Lfn 5YhC@ Hgq#3X2dfv%gM-VK-WM̃:&J\7j C+m@6v 6èl a 6[_i%0MbYS; "'5AXaI)9"%5l. PK+5PKi_:ObjectReplacements/Object 45 s qcd0dFidp00ı009@!& #%m?PU,PT8 TlT5'\%` OG 5 [8th :@l,`p qH2$*3l RjT1NVe PKnPKi_:ObjectReplacements/Object 1 s qcd0dFids9Xn@ҖT T5(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T [xHiLМfHr1$W& \*r]%#ab`e`9̖Di3Sa 9bK&f<0E&u 218Bd38 t|arQ/ `;DìSYu)VX|:"ŧ#l-Ht:e ~C0*8X F\ud'AP ` RjT1NVe PKumPKi_:ObjectReplacements/Object 2 s qcd0dFidr00쁜n@6%PT8 TlT5'\miK*0vD$4'ٮl`N3. PKG:gPKi_:ObjectReplacements/Object 3 s qcd0dFidun#% J(5(U 'dYYL & ] %EI9=AXPS3-3;f121+5@mHH,yZ fg{b h;H3d++&!\ wʌ0U;Le44'rFP{ iWNV NSb* PGD:pzVD?ȟts`.8aFA;8mY`;'"5?`9y?7ٮE: xmn)iЖhV.8h3V!"nSx6xEi*:hb0U"RY(6xmK|A6Bդc -s!+3h;w6 x ,$?HN{ܹwݠ1* JEy0vTsAuЖhWTm+ڠIPew)Tp-V:8s"m. PK3O'PKi_:ObjectReplacements/Object 4 s qcd0dFidgs00ı009@!& #%mPU,PT8 TlT5'\E[|ɑNXj0 qAtXX9,FG`@麿pPA% 3HQ=`3; [VPKHHpPKi_:ObjectReplacements/Object 38 s qcd0dFid10<@BL@F KI<'@US,@>H4jN>y. ,o $B2sSRs -Y Ĭ`>fM`f@Fv f;6 PT [@lFRx =`19 l b.I, MA hU J H1\  0{a pU;G44ӬKN( ;pU"BrSzVDJST$]<w6PKe1PKi_:ObjectReplacements/Object 39 s qcd0dFid00lrBL@F KI'PC@q4jN>y. bb ! ~ AyA301+n5YN "q ħ@Jd2}ǀ4+m&@!$(27)?aP9^u>423$=sؼF0Tw4{i8ŒNiU봿 TtA89Ά;͑Ĕ 0VX104?gf)npؼ-Z u g i" s6 4a&N n.DJօ{ЖhF< Q9 L0eVQU"V9("TE * `9lpVQ * AYE]pеWEq͗VQEѭ{ѽԽCD'2PKfYPKi_: content.xml[n#SZ@fF3֒vdtun %1;CNIew o'!G3űMk<<9$F7DHwDX#ʖ~L_\9_,hH0儜)y>{>s,39Wᜧ\:G솸έȭeִ ^|m׹#׶̚Zg_p[[; ^OR=+ncޝVJs[zrlఢK3(HL2 Q>M[7e5֮ vU,qp56 qs{G:ojǞLK4\bA$4mU2s:?2U3j ñQ$_ 8+i@{@ rCv7$xKL;I3 J0B )To'z1@ g_LnqDoum෵3?}g-d>jڻar07T|a1]CLasIEGBW\@$=1 #?kyo[cT|@ж3휯2Wo{[}BP`]S)deT҆ȘVgKH$8|Ypț`Gϧ$rzNLJ!H8YA]qAằd:%$T3^ 0LPtM<ts>ӗB:ڐ QRjοI$B>&jO/SR`K,ZZNfGSsE1h2Op )l/cZi %X,)3+@ɠ6(Όڄ'V |I\R>h3k"NI{LV"LSh#")CUi;aIwۃ ET18URIuVɕ%P[DUPrcsmON??Zo狌SP!Љ {/%( C4z#b$(Tջꇦs b ., 5;j0D$ͭ-":6P'2uȺˁ$vֱQGfYe$h^'Bx22chG!͡qYٟu"`N:0B@'6ts 383w6 eu 7ǩ!Ѹ);|:Ŗwq\Ap&%E$Xǀw6wଣjcj?</#6fl sz->t du$`DE{eR[V{ up4rƢ?'W;p(8BGd>CsW >R|_j̿!eo:5P5k~/ PK^L 1<PKi_: styles.xmlZ͒6SQ"53Ljgw+S I؀ GyFM$ȋ)Ue}-t7F͗9 0a2)H?>yö[u*Džu,ۂ*夜|$TX0`r9 T2i VJ?sS涿F$2xBLOPŁq杖d( |,lx 舣8Gܪ> (n@evgt=[v{NJ+Kأ"_`=߇&?ǎPB H(&3N~UI'FW1מ@ 8-y]"4@>IO Z0L2f򹝗U"6R+FBqH5x65qǻp/i2@+v~2^ 5CɅ9!i2F(E=4 FC 19 [!3-! u$IQ`V$Rl+@rcQNl(Klb|8dأ/Ø:W` CIdyjvX$Ϸ#9jkg `rAY\y6F!SJeN&_th?S Ju4-n[Y}PK|agmPKi_:Object 1/settings.xmlՙQo0)P^6֮ Vc }qZbGS}ل0F xxAI>Շ4 [ag9m3Рg}ߞ\XorI0>ITR5-՝ 7{ܳN].EWb@nnkw !{֓k۫t=e5R-uClʘ,Դ._[Eɨg/тBIDWQj*Poqlt9Y\v{}KU'PK]hPKi_:Object 2/content.xmlJ0}cl aM n!M@:dz㝏蓘k;Pq7$9| ZǰIf4 H-L!}½F`k^@NoF@6RϢH dIx>2"%Co ߢtrK'Đxn^tz(Y!nzfYdvhNr5*dߔƾυQVUq)bɈB+AХǞU4H#Sx}/{WUv·? 9 ӯ?պD! GV'yl=T(R`515|;ZePKDMRPKi_:Object 2/settings.xmlo0WM#*hW hl&v 쯟Mmɏ$"srN7oYj]60=V=q~ݺrI]p]1'5rq\(D=2tm +ʲ-LH(n;/ տňSԁ&rHN_ 8]@TU%-1ŀr8 l$FnaVI| #I|bDHUF1f thC,ԓM'M#hfTF 0AL"bQzj8n@BQHČ%> |ȉԡe$Lu?^|%ۯ1o C [j \*nnc j_vwTHܔ8%O'[?f(oMznPqYӤ46̘4j A0f.5C|[GZm1e\pD0 (tad*ΈoEAE r 0Tz D q|_ uE![ʹiԮQçSjMe!}CwӺ*HFպ< ' ڱAcq#o7s_!۩v'_PKLfKdPKi_:Object 3/content.xmlj0YZ")mį@0eJ~hQSiEpOZ͎]& 'z'9$R[n;Ed( 5tZI2Nnװ/ި;yZf03XEh0LU,{PoC0L].0P jK1BT J7M\+6( :Z!Te[n swSuPV1'CNMMEfl$l('-)},OF@&^fRcRS-2)޽oIPʡKu~oR2:1Z^']1'k\1$LW$%mL_QmdlDBӾWS+Z8޸Ma"40edNUZbS +ӠFb>fP0ħ)F\Y%jLGBE5m*< >hFN42bY :gЛĨ`TqRF$f$f.aCNg)-#d0 ,Y~xS 2g`޿Uu:6kwK$MًQquAcZ5M1OLs8_kÌi@S۫vʯԥ Nn*vꂨXk?$<1U~|SOWĪ} À2LI*(VdY.a]CIYH\M78YW1! ޠPNЇ^T$IçSjMe!}Awi]U$j^hWQX@ ! n}=q#o7s_!۩/v7_PKfLePKi_:Object 4/content.xmlj0gLeVZu0S2v3iHrqOV1vS'97,&j5o0AD]֕q=ϳ1lZ~t9eM.1GjϙO!zƷ0? @)YF^Dt,MRj8f9b[^g$шmPF*qQrkmD!d&$lafw;'BrziR\PMcwٸu4{Gɚ'=/0r;-w(WNqO-ou e3 *nݼf*V-vE/Zr!D3& +r Uf,R풶Q) D,)϶\27nc {ؾ:NcAA4`]jJ!%ą̏.KFLc!pPGnȀp LBePF}[fIJ)Vls9eN{ù~ 6'm8g`_T;gXM;SӬHv5iWDBl"U hUXqIPN} @qICP%cj5K_#$HY. )Un_avwT*{I }ピSI*.)i}2| 3!=^S~F]xf.5"|_`#X5w;Rn<]5UEgߊ,`34AaI10o Ķ2D?C6ʹaJJ)iQSQM)R5&P;i]U$j]sdciWXHěEcYq+ o7s_!'_ܼo$?PKOePKi_:KEEmeta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-93102006-10-11T17:42:46Nicolas2009-07-05T13:59:17fr-FR51PT12H0M22SPKi_:Thumbnails/thumbnail.pngzUT\A%${pwn]Ӹ3Y3O]u着S޷N42(((dY 5H ^b}:|Zc@!>?A QBT,!: 'D)Yh0^4c?3UQwB4-ُ&F"=t^^wΝ'Y~9YN>g5mɍ X^JgPaUD)eح!*Z{(rQkz .Tvy[ɈۓU}ߛ~Lej|Ɗ}){Ǝ׻&R%l+ˇ uwFˮbN% ExnU4#6㷫43AT"hy|w$Ԛax便LOy\le`"sb¡"|]8ǢO269h:0vCMliQinK븪4B>hIuzo5q V xnxs<}/dq0c<}w[!_8@ xÒ\O \ȶk>6ćq9sKc4|_&T+Ռɤ }XwӦƛ'p؎tK3tM:C^|6ƚ􂫒oY fsA&] a%5t2Y?8YA:'f( eec奲cJg҉Ǚf mav+ќZ[0+Hr¤Qݙ;Υy|n49,exjq$D _gn$0N^f` !xfm;KuЛB0D}r=R|ʖ0^TYVP7!H4I-R:bд(TUvŹ@@=r񬚬Ch4DnqhUMB6ߟw[r b JG_ȢyZQCDCC% Y,ۈa]&#閐ydL M,3xuchǘ$R;+2' |9=1rQ/Ln#5 uϸKѬz>NwQ%?{GhSer!'ɠ`pQx%cQn $N oj!dm4.mUR |.حFʗ˲k+.b=P@m#&÷#e13T,!öȺĵg da|` 9*/ xa{I:W#;w p¦b8eW \$\YLsHnc rP_jl `-BkF— [J Dy?c(r`N/m!>bHqNfZDf [f#2tmubI}R^3}R+MP;%Fu?k;nXOc*<@ >,ȹ`s5ĦQ,Ѧ|M4&!Mw(R; $Rָ+ Ǚ{)qn;(lR% u6?ֆN_ +K7͜S?hl2BݿIgjIc}aJ4lll~'jxȺǏqU+}-y_7鱏;T`JSbj}I$I SȶI):+*Fm"$QkVGҼrM "@'*J~x`}Rl)p|<@S* 0Tlnp(ciBG4*ްLHaV,c7wWs쁉؄sFWOFЌռqgGOm15USvp0I:18 Nu_b&ONœ!)cϾwIgWrS> YneTڛAL+Og#TpThbhdj*S:bc }[GDF5| x)Cv)I|? eo Յ&(pY܊ &tOh#H6YV Y(wbCA`UDA߿"c&Ʉ> ؠńۛ!uT)cܚN4"QL625,$R'҆޵]hx1?491{Z f+9>M̈́{ i%&2نAwzhl 4&^x%ʯK"Á)2:: M܀Ͱ=+ lH .SjIWOFpI _϶ u]c iG8pQ^b~`qgLU6%`'"\WˎKIj 5q6qBplp'mFI)#Fxm}7AIuQHPRh^D&@G^Tsȶ+rK<"Jztx)~!#Tפ||6w<_@\zpd͍^n+ P.E aM;Gex6cU_w:@Փ}%|1U?|]wa5-O mWr3mT ,,D hGj $(1(ꑲZ5+0Bs8,a+A =Lv:—k, sSjA ѿӑχѐ~U22Pis@&X8[+dDXLja\Z"Z@Wԝ4E<)u7DqkxJtȝc;~3&8?Wbq>*B|=㕣K3YWl<^\6>&}9W23rO9 1Rޗa҄Xcr)h4n%o.jw´08ao@ͫt|3 pKgHt_-SBDiu"bvѹE)alp-gz"u}lC˄T.3&#m&#ڳIE\Qe;ر{guc,  u?c ytmX4!.CT\F/rkӺCsE]3_åw|=h 0&Ι`;6 $2|bEg3p_,ȏuGndd?װ?XZ,X臥r. ABD$JS8{zZVFL'A<B4gB^+4.^ή'$ .{`:7dt$\C. ۖs6QpkFl ˦$,+ !q"b[Œ{G1$ l%]?GA٥z,zU1nΚ77vD%= 6jMC []Ue5:p1zi:4 GF]8C.$zUi?5(K;p(=>kdʸwfxw1&2u#&%6/M#4-#tJ$cT t(%vڗgdr8z k^kqү%+!y8jl9EE7nix BLpr΂j)D:%pb=R$` E܎T٘1=&8VPb%K@.=oshI,M v*n φ!&^`t#l5SƭXw%UP쬕n@#;j;0H{Zx]֤֌h֩꿔(ŝGsJh5䂒ӵ5f@*:퉇/2`Q;eJT3t<`Y;3Z#ٴcDȢ ^2 lユVh)-C,Dn(Ȱ!޻42'uٴݡLWo}/̵=HD3ֺDФp1:w9AX-YYH{WC'-BuUA;%^zB8; Nm#>%u̔$c*Z(_01Y.U4bNu<@pI1'2LϖW:-/GǦedtRjY@V)ȓ9@XNFze!qmy|kB-lKrԊh+)(;2EYh޽(۱ϝ%M|HђN-NFlQThPDL]2}=KߛW 4 "ibXo,rr) U;7ȏՎO+`MD,@1^.^kíc3lNJ 1@kC B|*xw#< ;ќ {S  s)Z2ŸkbAU! bFlR.&ai7J-$kɅe2r~!QMrGEvgҧve D X1k{B{Xzc|(z7rcMEJx0DBPj U^'PTb}e c3vwtŪడH|]|P͊( -qᆫqr>\)ᅣ r[Ѝ,o E;˨HB7%HX?Y˲܀3%涂B9L,g&p6!u/daXQ 1?Sr?Da3 vE-H 1Z2kPׂrٓ~rA5Cs"B1VS,)K'  Z:><+dwms ~ڲ`VH,EDNo5I O)8uhMf|\vlD.(w3YQ1_2k~h+7L mq6l.&bQٹm 6ZM|Iي⵻G5u'O-\P^eWd 7|αԼr%"T9$zT4m<4,2gq?3N% Krv C/10)b^s*r}Q&W*jaRżg6Fp@al?:r/D&SYcCK7?NJQLw-y&8"*4+"qջ!&VyW_=} 񩴀7A5}DM $x2DW $ဿO-5*uC6Qж2j&*hOc| Z' ^z^Ґ #}Rt%]O) kw ©i;/Eۿᦹ %hy; 1?L(}<1gP+HkN CFH>Swa!H#$_nF>r\ûG/J~xJ > PF+nZYXOvBk(VP/pWN] ,tu/ ($E_#do^ZGbE/=e̊%r Dp -%㰕.;&?ۀڼy\lnVTq*m?ϜK^OX 'E.E~ DDPT5/R7Տ];i G!:J>.>YPB/ b_[e*,}&,ؘtИp{xzݗM/6}=x-Џ}̥"rHjde 8(:%\F189 8":+; ,zh;T]@F3o?Iaʵ=1=RZòpE\[pgtmdkJm#fFw{QסC夂Pi.SP»:NI3UD'msMȏ9`.2aZĥU?F BU 狇9p) ⃧]!ڡ$7L7L4/+icC=]n8@x W2"8Joy@ؔ$ CҧjC$@F+wȟoO Mw'SuvblO9zku($+f t tu\!i+HĂB~'u I=WݧN 8Xt=\jNxggN_Ԁ$0~+ '㍋F'gy?D#]ζdzصVLOBJq!by`\twu;̿pwv쳼>XOf]FP@oM15mz&(MN7X;kV3 JH>NO''qkujI@ǔtcYEDܳ+׽RE1wk|- yKlX'O 0_ԜQRD5:ݥu ǎVY 3g<^y7oXosG7bG3P:.)8*4m|`JH A͑%w| M4V؝~Rh357sPO&aC r:qÄd΄|h gؘMZ[B1)JY-7?C?ʞ#~7`p\sT<ЅsjfȢx%u UݘDؒ<{+aaamHr2kČMkܟ~IZd\HI {L0VP.M/]C2a?@ƻ>)󄘍MU`qe*t0kNvVV5k"[~ UfZ"IB= nu๱0}dZB݈ю{z? D+Es4~2RCqQ; *gӌe姃NG"{׼.ܓr g,6*_xVA1G[{1L:T 6@}K'ծb^ᆧ;aAn^d4b;Թ6k{,5d+xrZTpй2Gړ}\PKWFt@m<}Rbq֮1sE1AZ(*?.ED6hWR6\C]q9!P#Sω;*'U3U8j1&^_@le5.T Uj]S2ߧ/.;y -!KiکDk4LVboq^_2]WJʈ2")D dwLiFyB0ht_\n+jiUT ?=3 5_/ٸ_aAm`3[ B2b'E[>?aq&7/òY[. Ev(M̊ku_{ZjU%-#Z!z K %g]`-%j`hs)Pǻ绳 9-/Z˟ܡ3lZVe>NyR L+E!թd#hiH<:cjĄ'^jxQpP ,>Rb(o~Ihb"X9^]( *V8y+){+v B1d6h ' z cբR0Q~   @jB,P8 bs\(ۜ܊E4o1oC876;f/Z/i8%dLS B%q SRBhENԕq+h{ 9m b>e^ӽoIGN E}qPS xƻmo,p+<^N'\ퟃX"xšb'|]xCy_!eL* G'lgOӡOn/Qq4 oe* ,dyI.1k!or鸊:Z"FdЮP El2mR& ^ S7Of-ZM2aҳP=o=$.O-㙮CyOα* OC004+ p*_5Z "!_c4Cw0Ժsˡ0_Q!4XO5 6;'u9G>BَUDz{y78'wlO׻]דZ>hq-LRXGۘSXG77odoQ42(hN6Ef sODcbj߼OCHwv.[7p&Lׄ_w|x2 !d5rzOt9YާdB(l3b:Su]ǶY|Og*G_XqinGI{w R9bb-8Ӷ_I6;o,cr%p6hȡw+tbyA0DܟS›ŧb9 P{`/E˿ZR)`vOW+yj>Ynh_}\2@cpq6$K6E?hnJNm=A^cc{D,SQ V 'ac  bł=DA]ğc q76;AA)@}LoR.Q._bqo0I )yC\֢S(@c)8PvL  ?_Yd=.lD-iaҴˎXWhgm?I܀G.]-EH$aX:71'\ \$+(GeBCL3b./\2BZ?Yrt֛-P{SLÌLkE7@yP;/w17F˰!|Ӕt uM&X$URIhp~A-PDN4EbA1ÎoeeˆwxI/)P wN.CIbUO$`C?swG^_*Zb<6chlQ[`U_&LNyw)OP&SHlآbJCPCXWŢR9P""Zbk&Wa#X㐛DY]Fq"xc⛁!}vCZY&#@Go-tU!HH25u,8O|Idt18V_N6G /mWnLL477`e; 1p9Tc+}2pد+bK\sէ%ElT\d7hm0{n]ݪ Kҙ3x6PO{Q(o}]C/PKJ PKi_:Object 38/content.xmlJ@O1IXП$ Bc %4 n >1&/,3&.2#rкc TC1Wr߶+H9ja6@D =_W! J~zf* )dݑ_CD)bV>d2J†kSH"tކllcc(ą"6MU'" iEaJg)O9Y^~R\__ϼ\)Mhn<^gU+#(I< _Ko^PK.1PKi_:Object 38/settings.xmlo0WJTЮt$XM|_?ڒeI'Dl|N:oYj70=ʖ]ipvcup.8QL Ru 5 'nZgAH‘!d󱷳O6>e]k%ez>__#_ڭvmZ.]{D!ē.+;o5^D~?gTB}?SZ QXfeǞ#@[_@ePs6fSr2su 6bL6`^T;w` ;ӌʈf5iVDL0Bo"bQ hTJɣć8q_A: ^1z`NjVd5BMxyVZ<;_ۘ 7د=07e/vD[!{[#T\4iR61ppVڃ)m9=[m@U{<:Π]QA{ !jtH!MM=8 Zx58F . f:<& ͏t%ā0i}qr f9up7N3\&N21@ "DY 8>+bY@, }*eZ[VMfgZԴTɯ/_fK[嗼)}WC>ē%#hDJiiTfhԂ=PE6V d*ugR:%fZPKtPKi_:Object 39/settings.xmlmo0SoC;PAjHM@&9Zl>lBhKʒx}ϗsҽ~zVi5QYOomKK)Pәp=+A"p @8u0Lsޏv֧wZK:l.7K+ussc&C]dK:~B#HO'?!4k=K!W cԬy<dܑPݡLZfN[8ҀuU5?֙>9ng\Kp"NJYOWLb\\.C"̌Eb]6JeٖK]$a@ڗ\a)#l@9į.s sʒӘb@X9c6CO#@0$ʄ$>uM1"m$*yUe:41 Q?Psئ 3QpyQq&ޑa4tM3*#ՠkY1 IF57Q!(j$JbFb? ԡe$LuC^|%ۯ1o C Kj \*nnc j_vTHܔ8>j)#b>a@^{t$KWqF}+,Z0Ѯ$Eg,$.&R`JP\+ʘ ޡPNЧQT$EӇOE5 PK"B@ȨuUTuy O(A,^1Gc;04F9%nBS_o$PK$M9LePKi_:META-INF/manifest.xmlY]o0}߯[&MQ5>3r]'3r؅M-Rҗ:}}o:Z1'I%W懕͇ӝ}DŽhG7tdz#ӛLl\zGn#[/9+vcZ@kxid|BSCa^ӁC d o\Kylqs7S^"/qGt>}Ḓw{ֽ9ړn,~PK nq4PKi_:^2 ''mimetypePKi_:MConfigurations2/statusbar/PKi_:'Configurations2/accelerator/current.xmlPKi_:Configurations2/floater/PKi_:Configurations2/popupmenu/PKi_:JConfigurations2/progressbar/PKi_:Configurations2/menubar/PKi_:Configurations2/toolbar/PKi_:Configurations2/images/Bitmaps/PKi_:Bϰá# -Object 40/content.xmlPKi_:aObject 40/settings.xmlPKi_::?c Object 41/content.xmlPKi_:3ULe Object 41/settings.xmlPKi_:V p Object 42/content.xmlPKi_:@LefObject 42/settings.xmlPKi_:&:­­-Pictures/100000000000069800000489822DCE1E.pngPKi_:\= Object 43/content.xmlPKi_:pJ#PdObject 43/settings.xmlPKi_:6nc;sObject 44/content.xmlPKi_:KjLdObject 44/settings.xmlPKi_:7dnxObject 45/content.xmlPKi_:~bMe2Object 45/settings.xmlPKi_:SL'ObjectReplacements/Object 40PKi_:V3y4ObjectReplacements/Object 41PKi_:YObjectReplacements/Object 42PKi_:qFTObjectReplacements/Object 43PKi_:+5ObjectReplacements/Object 44PKi_:nObjectReplacements/Object 45PKi_:umObjectReplacements/Object 1PKi_:G:gRObjectReplacements/Object 2PKi_:3O'ObjectReplacements/Object 3PKi_:HHpEObjectReplacements/Object 4PKi_:e1ObjectReplacements/Object 38PKi_:fYYObjectReplacements/Object 39PKi_:^L 1< ucontent.xmlPKi_:´م"0 istyles.xmlPKi_:|agm&Object 1/content.xmlPKi_:]hObject 1/settings.xmlPKi_:DMRObject 2/content.xmlPKi_:LfKdMObject 2/settings.xmlPKi_:[NVObject 3/content.xmlPKi_:fLeObject 3/settings.xmlPKi_:o9sxJ Object 4/content.xmlPKi_:Oe Object 4/settings.xmlPKi_:KEEmeta.xmlPKi_:F062Thumbnails/thumbnail.pngPKi_:J +Fsettings.xmlPKi_:.1yKObject 38/content.xmlPKi_:AKdLObject 38/settings.xmlPKi_:t|PObject 39/content.xmlPKi_:$M9LecRObject 39/settings.xmlPKi_: nq4UMETA-INF/manifest.xmlPK44 Xwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/interpolation.pdf0000644000175000017500000054032012014170666027135 0ustar georgeskgeorgesk%PDF-1.4 % 3 0 obj << /Length 1930 /Filter /FlateDecode >> stream xYKsW6* 'AƮT&qʩ-'UN=PCpv}>B_D @7>t^\^-s|H Md'gD3TɻnIo˩;, ][KSCyp| E+$ ⥷纜?)X!NIqe8 -P~wGhvmZqEwʄy~JѝUBqW\> endobj 1 0 obj << /Font << /F22 4 0 R /F23 5 0 R /F19 6 0 R /F31 7 0 R /F57 8 0 R /F28 9 0 R /F58 10 0 R /F47 11 0 R /F25 12 0 R /F45 13 0 R >> /ProcSet [ /PDF /Text ] >> endobj 17 0 obj << /Length 1733 /Filter /FlateDecode >> stream xZKoFWV 7;;L4@AdFȒCh~~gERK-m}Ekgovw% J-DrIuLϒ/,Ecdz2<_ylZ ;9]zps,6xTȘeŀd1_gyj2/`z]+L1,D&L63VTSra갨DKML =U\`X\X6R +IeRm fZ^ -UAeBFs j LgBn<1DXT75BFAM6VxNcYF 'ug^Ȼ(.7A\glG0tfvҘo g1ޯƜuM2ÛV ivw>DIO=P9%96i j})(;@pxa6Crb0Xw77;Ö5hCDN1hːYJBωD0OM-hcK-2l8?v8,pԉ$ $Q*VkbpKz< x7aZC=0VJ Kk ywqf?P =E@QPDZGhÄ~gF),q8?2OeauYL̀E{-ĩ&PU@kңGG~&O 3nE"; .W!>mvub"8x̲ܲ,;>cycRMt^qv:#KS@X6O@X endstream endobj 16 0 obj << /Type /Page /Contents 17 0 R /Resources 15 0 R /MediaBox [0 0 595.276 841.89] /Parent 14 0 R >> endobj 15 0 obj << /Font << /F19 6 0 R /F22 4 0 R /F65 18 0 R /F28 9 0 R /F31 7 0 R /F38 19 0 R /F25 12 0 R /F45 13 0 R /F33 20 0 R >> /ProcSet [ /PDF /Text ] >> endobj 23 0 obj << /Length 2223 /Filter /FlateDecode >> stream xZK۸ϯzwR񸒪RM7Z8H,)9n| 4rb2ܽ9O!)O$%ʚ،h-MʈX,髼Y,6釷B tվM/w.}],޽+LhLklB\/hQ̒Lf-IΓ,a9,f>aVxҿ 1iS:߂`r4?ʎ5ʽ?ZpCG^}W-Kny.X6EiNzlMN i E&]0l1-`FF!Y?CԂT ;n(}30|Q仇vϥ%b0cvh"~@BiGRT~æqPw]jv5DWF~_7Ӂ>r^nxQ76Fr4D6C2 )ƉuGX~3jJ#:IQJbzf#@ZL=Y>`NߞD$ѹ8ǒ-hB ,a<.7Aq 1a2boEQ ݬLHnj_o4BTJ%Z5;VMDf8-Yfq7/7sY`ŖI9ʌ{d8耙hqa&sfsN !}XRRKZa,E~wbvn%pŮb5}5lq5KHU1R/43M8jgd )gyF6kt`‹ R5Ge䬃U^'Og;yʷ,xFVAlhuIRJߓݞؤY_w 3w'೑b8gHg*0r~^(UtO۩n=C,Ӭ7VP;OG @3V~]gn~f\r K\^P3wżu]qV !01*=q@5I FQ. wAU TSBU v9 ;bsR2]O8#< `#x0FC{=GDfVe0 ۙ();Ir"p!ɦȀ~egY r)8}S.a$KҸIJ0m%.4b:.ICs1vc ȖŘ'TcærϹ8+d]ٔav[ ruf_V_ X=𰸿S{|Dp53ygsWXRW<\`}. -3n !u~6\! @4A= _GA!vZY'\PS.x))lp/W$xve~_e45o >Qv l͆r:]fUq&tyۖ2ΠM.}Y07;CSOSf9ʋA#i/ DP! A@SM9Dlb 'lTL@־y*vގ85I ձ+_Tbr׻*AXzA`}O4״Z Ozi/U{: endstream endobj 22 0 obj << /Type /Page /Contents 23 0 R /Resources 21 0 R /MediaBox [0 0 595.276 841.89] /Parent 14 0 R >> endobj 21 0 obj << /Font << /F22 4 0 R /F28 9 0 R /F19 6 0 R /F31 7 0 R /F38 19 0 R /F25 12 0 R /F47 11 0 R /F33 20 0 R /F45 13 0 R /F73 24 0 R >> /ProcSet [ /PDF /Text ] >> endobj 27 0 obj << /Length 651 /Filter /FlateDecode >> stream xڭUAs0+HpA.61*8! PlfA~,VKBpY %N'8zw^Se!{^QHwB8qBTJB!qѿzkB 881w[Ly9s!ǀz7(ۡ1/y!_Q*, 4iЊ-[ qL---|pM(Q4QZS* O~ЮL(" $*3|un<|nh \951U98|c/]!|L6Ѽ́GN )"k͵)z\5$_ T4{d߾=ja_w>WKXAj35^ hn}63Pjrngd;uT"fߪ NOrvL+Ǵ#X$: //{Y徭aZ㇒~.(H]WyZXgssU@?c U~n{n1- BL|H%,¬w9f/Ed2Q`":U^\[OKڂ 8vW}ޥ endstream endobj 26 0 obj << /Type /Page /Contents 27 0 R /Resources 25 0 R /MediaBox [0 0 595.276 841.89] /Parent 14 0 R >> endobj 25 0 obj << /Font << /F73 24 0 R /F19 6 0 R >> /ProcSet [ /PDF /Text ] >> endobj 29 0 obj [514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514 514.6 514.6 514 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6 514.6] endobj 31 0 obj [791.7 791.7 583.3 583.3 638.9 638.9 638.9 638.9 805.6 805.6 805.6 805.6 1277.8 1277.8 811.1 811.1 875 875 666.7 666.7 666.7 666.7 666.7 666.7 888.9 888.9 888.9 888.9 888.9 888.9 888.9 666.7 875 875] endobj 32 0 obj [722.2] endobj 34 0 obj [354.1 354.1 458.6 719.9 249.7 301.9 249.7 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 249.7 249.7] endobj 35 0 obj [500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8 472.2 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 472 277.8] endobj 36 0 obj [380.8 380.8 489.6 761.6 272 326.4 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 272 761.6 462.4 462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.2 734 353.2 503 761.2 611.8 897.3 734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 461.6 272] endobj 38 0 obj [750 758.5 714.7 827.9 738.2 643.1 786.3 831.3 439.6 554.5 849.3 680.6 970.1 803.5 762.8 642 790.6 759.3 613.2 584.4 682.8 583.3 944.4 828.5 580.6 682.6 388.9 388.9 388.9 1000 1000 416.7 528.6 429.2 432.8 520.5 465.6 489.6 477 576.2 344.5 411.8 520.6 298.4 878 600.2 484.7 503.1 446.4 451.2 468.8 361.1 572.5 484.7 715.9 571.5 490.3] endobj 39 0 obj [277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 777.8 777.8 777.8 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 555.6 750 277.8 500 555.6 444.5 555.6 444.5 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6 500 555.6 527.8 391.7 394.5 388.9 555.6 527.8 722.2 527.8 527.8] endobj 40 0 obj [272 761.6 489.6 761.6 489.6 516.9 734 743.9 700.5 813 724.8 633.8 772.4 811.3 431.9 541.2 833 666.2 947.3 784.1 748.3 631.1 775.5 745.3 602.2 573.9 665 570.8 924.4 812.6 568.1 670.2 380.8 380.8 380.8 979.2 979.2 410.9 514 416.3 421.4 508.8 453.8 482.6 468.9 563.7 334 405.1 509.3 291.7 856.5 584.5 470.7 491.4 434.1 441.3 461.2 353.6 557.3 473.4 699.9 556.4 477.4] endobj 41 0 obj [450 500 450 300 450 500 300 325 450 250 800 550 500 500 450 412.5 400 325] endobj 43 0 obj [777.8 277.8 777.8 500 777.8 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 1000 500 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 1000 1000 777.8 777.8 1000 1000 500 500 1000 1000 1000 777.8 1000 1000 611.1 611.1 1000 1000 1000 777.8 275 1000 666.7 666.7 888.9 888.9 0 0 555.6 555.6 666.7 500 722.2 722.2 777.8 777.8 611.1 798.5 656.8 526.5] endobj 44 0 obj [489.6 979.2 0 382 272 299.2 571.2 544 544 816 816 489.6 272 358 816 489.6 816 761.6 272 380.8 380.8 489.6 761.6 272 326.4 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 761.6 761.6 761.6 462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.2 734 353.2 503 761.2 611.8 897.3 734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 500 272 555.6 734 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6 544 516.8 380.8 386.2 380.8 544 516.8 707.2 516.8 516.8 435.2 500 277.8 500 555.6 163.2 734 734 707.2 707.2 747.8 666.2 666.2 768.2 611.8 611.8 611.8 734 734 734 761.6 720.6 720.6 544 544 544 707.2 707.2 734 734 734 598.4 598.4 598.4 820.9 353.2 544 474.6 489.6 489.6 435.2 435.2 544 435.2 435.2 489.6 272 272 329.1 544 544 494.5 489.6 380.8 380.8 386.2 386.2 386.2 380.8 380.8 544 544 516.8 435.2 435.2 435.2 544 272 462.4 734 734 734 734 734 734 734 883.7 707.2 666.2 666.2 666.2 666.2 353.2 353.2 353.2 353.2 747.8 734 761.6 761.6 761.6 761.6 761.6 992.6 761.6 734 734 734 734 734 666.2 1088 489.6 489.6 489.6 489.6 489.6 489.6 707.2 435.2 435.2 435.2 435.2 435.2 272 272 272 272 489.6 544 489.6 489.6 489.6] endobj 45 0 obj [458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 249.7 249.7 761.6 719.8 761.6 432.5 719.8 693.3 654.3 667.6 706.6 628.2 602.1 726.3 693.3 327.6 471.5 719.4 576 850 693.3 719.8 628.2 719.8 680.4 510.9 667.6 693.3 693.3 954.5 693.3 693.3 563.1 249.7 500 249.7 555.6 693.3 249.7 458.6 510.9 406.4 510.9 406.4 275.8 458.6 510.9 249.7 275.8 484.7 249.7 772.1 510.9 458.6 510.9 484.7 354.1 359.4 354.1 510.9] endobj 46 0 obj [305.5 427.8 427.8 550 855.6 305.5 366.7 305.5 550 550 550 550 550 550 550 550 550 550 550 305.5 305.5 894.4 855.6 894.4 519.5 733.3 733.3 733.3 702.8 794.5 641.7 611.1 733.3 794.5 330.5 519.5 763.9 580.5 977.8 794.5 794.5 702.8 794.5 702.8 611.1 733.3 763.9 733.3 1038.9 733.3 733.3 672.2 343.1 575 343.1 555.5 733.3 305.5 525 561.1 488.9 561.1 511.1 336.1 550 561.1 255.5 286.1 530.6 255.5 866.7 561.1 550 561.1 561.1 372.2 421.7 404.2 561.1 500 744.5 500 500 476.4 575 319.5 575 555.5 183.3 733.3 733.3 702.8 702.8 794.5 641.7 641.7 733.3 580.5 580.5 672.2 794.5 794.5 794.5 794.5 702.8 702.8 611.1 611.1 611.1 733.3 733.3 763.9 763.9 733.3 672.2 672.2 672.2 817 330.5 561.1 566 525 525 488.9 488.9 561.1 511.1 511.1 550 255.5 255.5 381.4 561.1 561.1 561.1 550 372.2 372.2 421.7 421.7 421.7 404.2 404.2 561.1 561.1 500 476.4 476.4 476.4 516.1 366.7 519.5 733.3 733.3 733.3 733.3 733.3 733.3 733.3 947.2 702.8 641.7 641.7 641.7 641.7 330.5 330.5 330.5 330.5 794.5 794.5 794.5 794.5 794.5 794.5 794.5 1069.5 855.6 763.9 763.9 763.9 763.9 733.3 702.8 1222.2 525 525 525 525 525 525 794.5 488.9 511.1 511.1 511.1 511.1 255.5 255.5 255.5 255.5 550 561.1 550 550 550] endobj 47 0 obj << /Length1 1903 /Length2 2951 /Length3 0 /Length 4073 /Filter /FlateDecode >> stream xڝU 8/EQ 1d,1Ø e mBht%C'BTI]yϽݫ?0[E"xԚkVk!6s_Qѳ2+1atrli[VR0G;}JΎ$Fm;Ѹɏ2~!ڟ/g5L1ULsX,Ց̍޶ oa{˯I9t:7a4_* +yo84\11SWk.8ʛ wt'od|$4ILl:}×xqvtL:…ѴF*+d-!|ToV'-kp{,K񾔥q!.^5(yhK2aTqAE'k$I`)0hP=:nIC/m!JѤ{!_zۘcQ g7TkK%& ټT/q|/.]5GHx!j;jLÞܱ _L6Sr O+%ƿ7GD`_.kvX-Q hx£MG0LowKWxwGX)}Þփy)} `'48{>)G%q}^ky_pRh"V?i6dN,7ëӷ4= Q7g|#b%%Vζ'SBjɚ䭂/4,C L8%~{qFGyn]A_>GHU 9W<==h쎆pUcV*-k9dV({JB4eJK]_$[W_K Z\ (JmP B Wfθ)))Nqqand GY+ʯ}a^e7hMx1R}L,z˗G4Ķ2whe>ThF v9y aPĎ5)zW3F-o6e~[9Fڙ읒4TjWvЁְ^Z&\,ͥ^EA;gj72 }L؇ֻg"j~/ŒIr_"mADɎ2uN:7mx\.gp^zN=♺'k:-N I_DpS^f`KPO{7Ye{/}>MZw(g 4scyK;t5K=sfNte(?رs.s$u/# #w7M Ggo8"ah58U]P n /;_~|Ь*C)9QZHIJxK>/]8ߤ+T׍E3<{h7*k79>B0RpN3_ۖ+WyK@jaV:|{ QU(XE6jȺI[YPԘ:ZCBӳ8ū\=6ێf)]C3z{oO 1y| ].{?VLWTq|ô3\S, .fE5`.PQwL3.a xkmYqVBV&&W 殺BD6''`uS!u~̴)Fn;G{N^6\Ԛ*zb˟H#. %oܵnY[ Nr*ӟ欰 ?X2a2Km?;CJpLWr+e=^kEj5jȃ.fOpֿ2)e;xH-C!AG/#IM>}2&V/񮣿Qq6(~=|:]T[tQ o,Jd4`$8e~ PTp{"7R{6AmgƂ/[[m'&6xN>uwgc$hA]&U2} g_> endobj 49 0 obj << /Length1 1714 /Length2 2978 /Length3 0 /Length 4051 /Filter /FlateDecode >> stream xڝU 8Tl%K{Bc mln,%˘90f4","D0."YJ*QBY~3s}{1 I4uTEV4A76 ()R@@&h>ӢkP.ՖNE8 TS kJ)90Bi3: L4h2OP$ `jvd[K`;*I/G Aw X8ٻ88hM遁d p:#],3c;$] g$ |A N CqQlC;O 2`) DQ١$ Hhxp#`C@$*q2'od*Ӏ Be'iPA큧!Sid N#S!R!6V;gIȔ8(F66@@$ Ώѩφ}}S:)/ ]1$yl^POc?6M&Q Tu3 ` D3*g҆hǀF9ƔΓ*= w. Rb $ q!NA+?]*u8@2x`dp q,HC X}@F!~`#6[dԳS v+O D 0 VbG ۾u~:̜N$ڡ@ƨ1_s9y+ۑ)(OjN` ~31 GupJ8OhHZ dw@Mw2m"Hh2@:BA @ B`lc@LctZ(e/=g:Ęڔtߒ.1ӄ?D Lށ\]͆ ۔i?F0aWH!0=P(gzw&&df:\Pft?x7_7 g7/2Am^u# SUtawD̞2fW^+G k\W@ K ']wW Kxhf�upl;xAAunB'kk.S7~^]HS8^\VƵ~XN Q9(^fm8!>(mM;tn3JIff_dXM*Jk{VAE9]䛪¾Y+je{U1-Mt~^qw'b <fiŝP;Cʥb^pewVk7*Q{_}Sy[l)\s̭{Ш4V-v.ťKl}AHGk1x`9`en%~E'TN3Of} >ܪ;j^~קa7+ rRF|?S0H z Ey&]RcP 7#}!%RkI_P/MNlӷ[i0?80wk'Y-0$-(yJueڲ侹Y4&${mzꚪߛ3e527vxv⢀q9e`\W]U/+YƟ\ىiݖ^<Uv "{tgYÀsYJT-ͽ֎PnY U'34(%9'<~Qz5_μcb.BޯG6x2C a j׿%v]<4V.f#<1м}3{ E:gd.hRSyA$ϲhr\ѩ򁉽% ܷ{iN]_7}?wwLs;nDJQFTBR˷d\V8͂,3#;⯫zxut)S Nd䤇뫴<>&ZguJ 'WO2v6\O|SOKųB̉=qS^Mv v }pū[RSN; Y'>ħS{'W`JBX__NڗX>u1oϞ5OcLAfWqm/B5E%S`^E% r6p3dxu#^^vD~ㅋXK!nQW+lYtUE%M|wHY>K_rOŻc Oε<LV;C3QR"wy>Hlܭ'v"vj6.P/QP{I]s% bWcGF#\AKݱۮ*maz7\o! |t79)/nߊ6ccJֆEl9@[<~M9 `UF틘m)|D/?3KEz~`qqZqP]LiFzS/X>[7rYy_ڬxy'g{z01p;m>0_ñhH)fVW59Zv_:HhNR:^z0d`Ѡk~Sn;$&a1 mR&DɱMl:v w Z\bTw n݃(ujXH7p)U_i}k=GKXm :R3O(+fK%ss#u:T "Mg {5?or\_;]` =gJ05IGYrԋ݄$B+#4bE]71Q[ߎZJϡUg"4/.kCcS3ܒ  Jj+@ 3pcx5]sSR#nfeH?}lz\YOݔekiۈWy3&9Ï#Iѣ/ rx22;{Xޔ0 J{*9xYwʢ˂T//.4Ppokr[BlYǢD8U:, uk=Wi endstream endobj 50 0 obj << /Type /FontDescriptor /FontName /WOBYWM+LMMathItalic10-Italic /Flags 4 /FontBBox [-32 -250 1048 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 72 /XHeight 431 /CharSet (/A/B/C/x/y) /FontFile 49 0 R >> endobj 51 0 obj << /Length1 1870 /Length2 4761 /Length3 0 /Length 5915 /Filter /FlateDecode >> stream xڝuTS[Ӷ(уt$*HA:!  { RWQ7E@@" ((E}޻VIϞ51Wc\4^\Q xw]<IHzF@@&P$MbIDȰ2$n$") HIK,OIȀK"<1~8O$E=`0~D/(A.w(k9cflib."R ,'%04Ռ,cinA@]X71Ȃ"IHz_,∩h8 ?$}$ !HAژ_'x!Չ'D8{+~~~ 7`@(+11*LZL\B@WC\ x)ݹCq?K6011H4aP<8\.@Œf'kw*!nI0 q"W$ A G:H$PHW[$)" /%HROj//b89i"`=?ttE$i8lF !h~x |?LZ"-Ar5 xPtEhpP_!AiHp$ Ol}7$gv]+''9"cШpaMw62z!&CBA[!qHRW*5 KH RKT}l!IFQ'?!QUO<RDSJX7O#n,b4bHAN#1xbMġ' V#~YX%5䈘_oKÈ-`LI{UF )<S(DL &A~+ӖeO5N>oA D??=VWKI2ve9H?"aŸGl?mҭ WRGVSleݱ*Jf 5Tc %-y%kgPdz鵣_W ZjV <7U]E mʥR I0d^j.+hfâX0qur$gKN>V{{i¹8}Xnb4{8ZLҹ޾L 7D?;*9L%ݭcഝhKΟZ6"ܭ%UiSIU3N˸JdvvC{fTe{@+[A1Wcd7aidh2Q%Wu:g^><j@AHؿQ*ڏKBz/ ԇwuF:۷ɻLas I֧L3 9y!M_^v!}6 \alZEuF Q9rn&@BdZANc4M/$/3pV Rq:͠k=-oַPAR'\WlKi6kGlTOxח~8pJ=Mu1YҘ';{+$=!Z F[MXʊN7Q Кg kOr0?j!U]gnYSؑy=<,?aj x31.I~^N3?}GWo/)/i,Vߘ*lw_$G l߽#%h"؆x. HG?3pB=w ,= S`ǜNطZpO?@Jq3tAxScJ]=]BWYŸQ5Kj+SRGDsW{n*Q0:l”VkpDyPQٺ^S{ϴ~tZJQ을}vŽu:y8c"G7a/5B„1t_" r:bIlBUdl{ EQ{тaIk^Y>] ʰYhSD(%af򕳎Lƛ!x_XEY%eюK{/>]tƘL#YoBȪfj4SEwƩksBt:yyg<ϟ)ܰNர vاx+s扗 U9[Iʶ=^_d/ mMQ|,+鯏Ck k\O:φ xBץȃo|cR?ꊞX;B V!؞"}u.ӓ־4,H>G?{:Rgi('h?= O&'#$Qd-:bzF橛2,e[pߦ!'Y>CJPa縁BN^Uhv>=+dAGFǕo4s#6 zsWx:݊I0u'$ghsLk]wi'-G(7+w]}=MhtwY׵m_ot- ]^K^AMDD:1Frxz̷C8 _x8ÌSS-73{)ete<+k}}|s%"F[KqJN+,x="V8/ϪSlj88Oͭ0!YD7VS"^!>2c:dq! ~eri֝SW=xP;Gvp<1+$@\, I)n|cmONe#++r\w=%m}3.63<#?ru[=l9>JJ( "TND-^ZcXGr`X(ǐ]}Gm^v~'*79~Eklh-u!3L8!f>SvZudxʬ'B ju! kNˋ~'|`cĹ9XOmC5S 8>dt=Oh׷xUeM1jjjYSG/* :qc$zϰp8z[LL+TlYX@HlGiݸQNDc-U eU{T06>/?+U{#eT1p|+4qWn|h`|^"[0:<; lSڿs,7"!0^9PɴYjw1Y|rXKy4PO_f$lZyd]Vfaϼ&Bp|Tn_ ,"eJ3]2xQhV8L^O. ipaԢ4S*L,ⱂkKnB >4n)o,%Hr02idHHQG1o%6]rH$*M?a*S橾4vFwTuՏ%fFe4R^cDCt -oE}| 7&flj mkDM+b~|P^b)풂@3l/cc-Ǵ~W!2)KMF1Wu> qG(ַ={su+6ķPMS-MY2vCVY̓ {tp DO܍ULx<|K֝1;r^=UY{s"&ZtO!3};!3)nQj.B{N>  U(k U0>Ep9PծlEl`n7s1%+dӫG\L~%3)6U| Ao|&-7A7ZȿY,;}]]tYͅv\cU_o 8<|f m^ǢN܃Q>E&~KQ7=*ϚUXul2,]Wq\=B1}X?>ړ#8՛f~b̗hAS4L{ޭvRz |g!^bn{בbċ(s깑mEIZF{t%kW:8Rba.,џG|S*flpUL+OVk鼻5]6OwhGs܋b2%8_4-f<̏ă m,E.$V3;)rڷ-Q 0,OznF'dsM+v^לt;N" fq+1Pnuš1;B:sYՔ.؊AFÑVhd'?D,Z6vZ3 !j-)3p9KY=e:!~| 0Em]ֳ:b_DbJfbdJ<0KM6=e(t_ ؎]"<Θ_C?s%/;l[07>INd2c͵S}RګjS~ݫZM}5wJdQ@1څ'VU7#ymhanROjJjuu~mǨ򞦚)x:EE#VlOzU?6 :+Lug۳UT|gt#NQB.qvg]F'F `dK>ӳ{J3BMxw˟aO6 Ep%ŻAK|uyu)ΤdIWG=J97z`D`g K'3枘hP)lx}滊hc a更3<뷹"]Oc"]Y _29{^<-Z{p_>*vζ,y)0Ɲ!q1Y 5H2WtrFFI'F%ƯR=7|cCjvdyD,"&i}@M&%ISc~[N!֙`xdB$<~ވSci \Ϣ<<?pye6l<\'s+ KO,֌hҹO^O.,YB` s]vJ[莦–C0n8KEC }RV 6t3Bԥ=mɁ ]F=_H*ޮv ֢isF<Ųɦ}\܈W:qI!-JS2l@SX!el$ɥ$D|ښ-݅p\^JA3]eʦ nqڨTN-/0=m{WXi{"'@YRSD;14zJg[Mu#4;f2D zWLU?;CF RِzQn }]X%- ӵmϜ#%(1J?9Hso=Lc+_n6M&9_Yz?S endstream endobj 52 0 obj << /Type /FontDescriptor /FontName /PDGVIO+LMMathItalic12-Italic /Flags 4 /FontBBox [-31 -250 1026 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 65 /XHeight 431 /CharSet (/A/B/C/L/a/b/c/comma/d/f/greater/k/less/x/y) /FontFile 51 0 R >> endobj 53 0 obj << /Length1 2060 /Length2 23892 /Length3 0 /Length 25139 /Filter /FlateDecode >> stream xڴeX۶6)NpwNq!\(wwC(g>ʕr[9i98U ` Osk? ?b?׵q;,.6 p>S`ac:ݯ;`įL=^ `DaSN?rɸ۫@mtO2aK8Kg"c Pq5Wi%w=-"q}=rlYnҽN#/7k!kij*2߱Nlah! ,p|8^gװX.'7K0OC^l =o9+l毻6א<6KV<6@~?o5oj`G?ԯV"\L\3W.^rrQWҮ8o| iQsHu=>s kBv ;?L-4`ge ׿GxKH=}X,SJ|__g9R 'eql.bZ']0Y O#zT/>FL E A$'hXKg֜X1qenG!->ͪPApW=B%)y9}LiXڲubsxa73G|q4?DgWH [i;%DO.$P$VC|^Iq_\:'%鲃cX{"}ܳbG- 뭞j*)!( ;TW`vxwAyB_4EH$5B*OEskF[Y;o58IShin_U^b+Ң$q{Mx=MTדρ9fŀ A;i^.oXv}/i^Y*Q.ap _)gM1!"c|:!ߖXO 1!N; mEoG954Hcj$T3=U |@V3ע/֪.oi~B>|PYh9:U9m0rG&)q9HY9/^q3VC\mv3˥qMdŌN( /<#RP(x!qoN@U}KRRx* owkhK_}J>dIclj>tpK aE 73o6Nv,31\糧E)c㓵(9e~۪E>s 0 i $U[{|\ Ms#|y[Ws/a 3^(3'H|>$.+o#C(Tkt8`]S- |v[Kxi%9ޑ*( F ́xdG guZ237VU)dQ0O(6Lsh<ѡBbJxЈ^1rYۀnj0 $w"M7Y g=tpݳS{iLr{PK,K<>/ry4#g yE1q<\C#+[R-⒦8l @I0,F5j]h vkS_ba?m%Ke0;îmfs9WJ`BU9GQz}S6D:ۘHn$EGAaArH &6_p_g v\6YKyf8O>oK؂?@5֔"w&d7bn̾vퟶ v%%6Y(oчצ8imk ,S4к'#]_3+,mJғjN $OImC:17EI` f#,:m {2ܧ k)hZq+`ni L=hX?qܴXyoё .cSݤX"gc+t 18,v˲ C26W\A%}Aj4z5(g#XAq7!j$],$Qvyv3%Ajfg\bC-/.D1IUy;Cm}NMOJL#D ?Cέ}sRC[+ֈ55$BȉQBR1|dBBbŷ0߸f9,1‚ v>ev%Ie 8+%vK]wJ"k)L ToHtev:J6n[fu!ad[3ĆICU\R|NW>i~2z,x@%|a,B1>yN//(cG&Vݞ6"~0p%ϭ{C4{{ܕgWzmV)O`/d[n5<nڻǁ.w8rd%IB$u#zgnC㷦@[a Ny |O; VH|xfO&ljJ۸XT".m56,dDDT}xȫ&$eڲI-/@:K419Ĕ'*]`;"'kUや`Wl q: B\-+"#IȪϚ}Ř*{8q|y72z9=>lR.ꫧ ]%|% Tr'i SW} 5iv[$sMy;^8U3ʬɈȺ,z&/2f;vnQ蠘L,rtj-DJ=v\yE|uWNiFᙦ&&l;#MB\;ߎ [STzeSHsZjW8I\~SFɀգ͟^UV# 5>!M]苈ߺS4;=-i^o*MА6x.җSw-0WTj m(mmb9{䪣v܃o4X07@ԺD .r|'=E l=O2+|[6LOϲ8Mɼ+ӣdeا ~r# p22~$bS~?Eh{{L 8'p"ѿ/}(ߏƳ'g}LT .2K/D$=7 7߬D2zLx # Jn{*T<K\LÍZ C"K$8AB^ΣQ/ÌFuxY3yQ>W K!-F'^PT&Tg0F$@|x^kDw |n$J@zmK LY$xYkU@*0v݊A:U}NyCk|=w@kXz,ܙ*Dcw為zW񈟹%,SO? ueX$/pJ"F /%wL|B(b)NXqxٵ%*x F1RO- @ƯTY#3>ecp68fmy}z_OS,#hZihM8մ_KeqQ_L,Tp]><Eǎ+67jCFol%X(vs}ƗʌOzqNJ_rmS+RlВl7>! [y3]pc0Wfj:=6ґ~2o3}_V$1sSN`ۥ. _Z!-{ V$n //smi|C>|89gTaΞQUw>ߢsźLKV6_D)tI7d^,9|o EgV~2vkg\r}E K3Ӊ(1y#?9L ^U?N}@/ Jqvn.~w:1"p319gk=3S/8n;2vK FA($. )4wyTk7E3Ϣ:V8\Q2HU-f;]60E- ;Hu}V]"AiH8vYTCb!#4$SX?_+W.-4T|LO`* [ygL!f}4En{CNMDFQ^t_#!,{fJ 2 }0{ 9^@EF@PչQ/(w>#57 `7],E5#"p{M%q㮞@MG(4Usa% 3moY(+>X%I|rDzTO Ǧ p: -3 ꉿC!,saBh Kvo DgPhdždބ3if؍ V0:b>=R|9aZ9^mR.`ʛʣz*.bE8,irމlG<'0t}q=tTcꞂ^j=BrUG5R")gX3MPM(iEX#sn4`ˤIp9\}("99Jr1$,GlMMzCVC2"gܗ|\LARR} CU ={1^XFY)g2QD *!ƶfWmڲ+ҡ./ H,X{爙Dpy"\nF/SF} X3};a(NRV?Ɩ,1=6W=w )Sc˂Z'C9x,"%J c+c_,Iy5=FV&;Kh-ao=Q[=G09J}oyKtynp0t|GPOTfu0tDz݄kqv kNǞ꜃>y]j m);p uswlV:̆ڡb^&7;@PNi+ktʚ1>9,Q͞LAE| CY%fp"= ]EQ-bmwWz<FnH7Zaj#Xe[wbn}D+P{ Q{C3_ƨGj Ye|$2%ƥ]&K6Z -[̅,(R']+zW?ZbP<4PTy<_0KM$Iֿu1M#2([T6::U{JtbE+휭OLc1VYt0OȊ9Z?PeyTu7*)zw5Kz9Q!. h'24E^J "aH mZ&^ I ? ZI J<_CcJFDFm#ww?0ɨyx57 \urnmZ2yP=xS? X<*UL5jK{xD{+~cS`e&{z!z;!^KV>7ބ=WHx1)!l2RS~V*tHNjpK?`^z,NaFSxE;R⁝6,+j D6C+b;]+no='HDe AP%lN: 8Eɵޫg,PՏW }yhkVf0$k3Ƈ1z8U~0+M`#KSuse!rMRض&~r`WiBj+c@T鞱w1nHjkϢ*fZ'.3iBEjp3p`~I4& vk4$uu_KH0[r(5x_Xv|1imB7F$OܺNG!ݧl#>HN֐"5JKӱ>jPh|õCp>4f%Z哨cJUFCjnsf$֭ [YոVkshE7yB7oF;6ч 6yt'&^M M@56pk[ckBGvWw};omaI{+q_by8nX6 E"PcI$x9~ˮ'9\z M-BR,l*׼B#"Bhe$2l͝n{%?a T kAo qa},*n7 ӗ )&F_yϼX1g_t>Xe2nyͱ6}xcB#L]'M{AT}z"닔1 ZhmKlM3iNc{"T\[A;YT"<;M\"+Rxw_ڶaLF)oM ʾX T'VmvFŵP7VK=VoX!=2~jDib~&,"5 ߝ_&[=ںk'%ԍ珯B P~1=Tv,KGFv$F`x;;>9qNc@%M 6*Xo?$T|s'YXtqniѷ9Ze7km"?gCt] h8%g4#8B~p##c ϨE! ֎$8)O݇ ;- F:u#RtѠQ7teKAw21v&M/OpeܾENϖ:Zslh!0p?cф҃xt\cX4j<$"GfAu9ܛ ڳrU indKҟ* y8! c\!0 QɁAL$l5qClлKTYF@C9r?^{ M'Q /_Yi ai:FQ='g@lN挚j]*d[iTՠeFMV$3^OLp|(<}WW"Kzkezou[1rXUI2󭓍zc[^2ax:͸׵^S.:؊[pbE;5"RSxw~V3*^% 0~sMki֬Ǡ =ފfw?}5 fNܯmMq. :AegWwFZm\F1( G4 hdԎ))iGRƈB*nKUȺzQ³Norڈ  )b8{~Q]F:8BfK#lnuR9!mȓ5A@ݸW. 5;tTTwlRo"_xa52)^ ;|ϴl !X[t2]ړ @9e)r'JOĸZv^q[!nw#)2ёKmW<. E^; Ri^Z᠔S2v63qAԙ5@Cj`Uz~!|ܭ;% >w*oRIa3(Ç&FaRoWf]5n}%{g38tdT;Ѭ$S 5ߴ7m4ɶHzijǑDU|*|>H߂* XMF$yس9C34 SW)!,ɏH9Ip|;c ooI];Q`M2rk9>wr+'rL ɣ/TCٿ]͸{)UDf:!${ 4_rvMdx\VqnM:)(t%H탃8ęGQNP~IORBKe}=.a p&$wqc8Ul=b,o\g#,qjc^heW?=~?^ud9d,*YƧ[_3?Xyo>ZqJ5/>\ 3w@'+Ο<քW4$dO_|E6&2˽[+uU:WQ ٧^ RIyv9fͬҽZ넴HGӳ`ڏ9euTΔUeHV1vROGl!Oxbuy/Lǁ|S&p\ѯl2=%v(Z8WЫ!}Tx !$.JyzDh6 c G7{}E,p6L4yG;OU)!HuI}-±>v>>[LQĭ ɲyԗC; ޣߓ3Bnɗjba> s-神//H0 P_v7)Vcgh`҂@Sfo{!7{XSJ+ưf-ޗX$&D7]ۨF4YkoDZFy í H&}.>UHjH??m'0W.6(Ͼ"]MEY+JܺY9/ /'K?uS6)jJQ -&k`1#ކ,Sy旟d411]b 7 !b-nD@#nq1KL'3FRf dwMKl_Tzя[R.}su gA*>A1_RNhG*'l}ITelqsY ##NoΨDƣ.xU1ζ`ZjKzV$Q ͉ PIu=^oMTR`%1[F xܟWdV9'<%B~t%.(u_ h^=(ǵJ+; ȌS8jI 6gwFSNq+So`ghگϿ*t؛] =v+\>D`Z+ڃ|\i34Zdv&3金&yyhFΓ$d[٘ :,?uz?#i˦%X,F =LSˤA~HS's'0 äGuxU7g܍'Ջ@9M>n(kO$09{_u&KEP."b vU{m;8/yRUF8oKc}g;FR:UGRnbw sEmy@M-p:B]z>k_:O_'ܟÖv*nUr,BݭDf#YMTW7.O }(Py7{@1ހʤ2]q-B+hk !#ip@o]US9]`+ڟ=d6z^^JW/ @'RV+cXK[w(v>^ /L((ygMJrn(Bި'1ν01ynvi]pQ[;٬g9ZUӔ$F E)`h۩OӉ`OφKﵐlJ"rYES8g> HۏE|fE׍ Bv9^) δFH 6S2w[!tӶ,qeywdڅHAo$xƺG>=zSV-EkYJMlk#k8欨'3E܏l^jvR=tT!H_ͧŴ]9C4ٿ=_FyҤ 4 ɀ0+?|sD:ixKd ׺5ˏd#Iq?-vG,ѷ_t(nLBcV ާ~XE[`E.lJ+mlD!d[2Nnk<ޓ|뎞 {01I֟3V glKij_{, :t3Wp7mŵQ$7+#3SU:C$o(ٹ1Ԕ dn®CU*|eB~O> Air[5tIBȠf,ۍ`R?t%E獻 =6J@f۶m&[ldLmۜl4ٺާ72gYTyjwBYGB`=Md^؃StzhU:ԷL4 %ԪMPRFڍusލ}٘78yd෾.{^OK/.wvL7ƣCN̢@ dp!*'^=#bA@?qetck^.mI#vV"@ﻏ\loPcI: Sh F‹lݜ~Q^5g\$/iIa*?%bTs$"5\<Xh  MϠtyzf'՛4$>@2W.ѫ0?6w*Y,YzKJ:v 792oj.I͕m^cHhpgG@:9? N- C$L#wz9sA2bU[k@+ÈAhM@ &_LTEffH8# f *S&M;q H?N(gw]# yq"S *ojb"{yqAʣ(#j (qޡ[C2(US9:S1ШmvpcFQ[Ԫs9 L6ߌKN0O8X~>RYm]:XwX0nj~W hS}*FmI yida䯑xI-ф)] c d޶_ASqm+=QW'IJcIP|qDZM@'rJ3 ZF mVvK+OqSDR7Gژ3)|PfN6\}sL9ャc?L-{k9nn erdWpn2|ԇٲ]pMTI s{MNq#k{CLڊȃ ̫U0xb nDAU"^Wƕt*?kCjXW XyI)|2B^X3_sROom'݂ 1rꎽ~~&&A0EK=oY1*E5ŞyHQ}w;iZ7`lZG}$x?idW1cVgЮ4P<mH]_\6zT(}>DzM߲셰DcTH(HŨ/Mq4mҍ/Yg3IܤvS0wux0ËxePs)+E`:F2eit-rXmكkKTiR2~ wk4VVI ٵ}b(EoEoϵk˙8W]YJOh2^UZnjQo(﨨$;wü*?1AV]$;wѮRH_,؛-޺XsK(x @W֝)l7驘ݺqRIw8oPs PP「x=?@M~yKx}]Ǒ 9Dj5SkXQch~SuwWed@ڋ\wF2 t%!#SdWx&pʐ"$XZd4C]hI~|W[|Xaցt(v{40_.]TNIF l2 Ҵ?b5Ω&F*^9bC _%E "&!p%|]@` Eʾ5j( W=F"΍bew_#$\ėSTd]JZKg# :-7wwBTLPj+uLm0.J뙭gX @oljd^-ng0{<'UK0#_E5P=Ei/]XMͩhxm/b3wc@Su1)3j͔穕c4J9bxKr |*-"&T"jAꝶtU< Ӏ3ٜnN_4H|b &\jhi\/56v+ӘAhJ-;#SѳwDrs07u3Mg蜇w;K1ÆU-Sss6cv~, WfxfK!Uib*Jm\^NsgϚ\y78mbn8 Hg4Nf~ ^p8 -!t"fi}fL^M16Yͩ0vZDU`i ܕg g.!?&t-2m@5<ץ( [MFCD1s"bmQkY<|\#uE2r<!6ϭ["e=$kD?s m%k]1GSCZq%\ :}gXC"ÖK{gK |c*SАS6GbcNp=Ƕ=)-h˩,_Η @j?T]z+'N%\w:㴥RFs.Z񐦎4S c7 O+z")T9)w@ "Wm{)>P~^C ٢(gl3uERxŭ*sAf\e4+=](}Ŷ ۓc9+{p2͉)_[bڊ5JC' @|3Qu~{&6mY8"o=(qnbk`'GD,9RYqq~kNޠ3E50DDm{t'@x 5W=6p䑝AEGRٚ% ПO:r~T$jp N-6ǟv@=q]FK[Q;ad<^.W0z|l mcu]q h_W|.Nh:@!|g5IGS@}Q 8dNCgw1YG46MJH+VV9Vy`J5GP?!J٧s*A.^_Yb"]+45@_G{8mOzdS@@G• x,b<uiFu3[9.;7y =./K0ѽHC= 23884G]'?읩^ - @B@__𕂦3eyT #g?gdQaC۞O'j5뻸S87>h [MF Jgv&ǑAmuly[y@3'MLjdλg!Oɵhd_^uA|h' ?,:6Ć aomԓ °gk~_{.wdChd1hMVpwzEuh0V"z*On ˞ɛ*kQZk"sځ8tO|@v-d,l7ذwFcOrd qw CT|jKl[k9(zPP=> - P;y ]"a^j}SxT>|#r Ow6#[4Gȳ ?H[c20A fj"4 yEyT w ZJ7h"xYNadg׍F[$jYIN\4Ddt꺯V!E9n>KCޫ7l/B +\鞥|Ptjú@W 9 #^p=y8+J>ampQ|Ug選k4v`EK|C[ݥhƒyg(ŀrw e)|`D} K]fѯ;I44Nv&bQ/غm, _C\By Æ *ݚ`"d*oiИ>z_X`qyc2GJ~FF.aDdϱ˖kus|.uݰLlVlbdEӀԛLrV:ͤԿ?<ɗ .?;՛Ӑ 1x)-] aI1/ Pm`ł$֋?,/\ ѣx7ͧ Q [U@! @IG-RO~T~je]/9GO|r2M}w[ڞ[T72Y,8"nh or&R|Wm٥Xpݕ/],+Bg1cWg߳\Ʃ v]ˣϖC/bs98~ƜBw[$CBZ6@d̑1] !W7#yNS8>=swo%Yie]$R8 iFE2 qd{*51vMf<˦,\}G`{L[ t(X᫷ݲ[\qz.GӱB Y>8IpIu^,kbe(l+[P5eKqHPf-.eːS~n*&?_o2n7=DO4tAҝ6-|.]q(wXVfȽe%b.+Y6vsk4*W+B\W8 L0ƃ]o}|{P\oO1Sҝ߂;.OjES4ToK<9I 'sh 8U$ua:c[}a ?CMڸ@)@itDצfJI\&汝ō4H2D1xC,g 4 N{-bN3#rqnESaҠDdiȒzkm0N>8rn5t@FEkv@d?;*kE2xK4ZeqE,?cCf:>E 8i@_z;`m,!oI{7dk)!%d(ϪVC2,M,>ҫ)䦯n gL-i79T djviw.lDŽ g}pZ}?G_!W2&a"tjLf*j!~@) +RO:ϭŚ<%o6s[%/QN*\}5hIětiLթ`$F~ CͼJaP[ڨ-V"dyj5uc|; dӉY51bƒn{\bX%2zMh,xpm&Om4Y> M.O`!v_H"9 `e"4W(O3G2} hm/ʲHq|p:LstH>lU(0E(x䑞ABĉ*M#jbW.P9Viw|A*5 >Jg 7xGkKGV~J t' tmtr^+OdI(%6~%$z[ q^M/Wp7^nkсV^6|4%u4'8Xtu/qFr!/N~Pl]Toh(CZ[}ؙרQW91]1~zE;}rT;!ܹڙ/t+\Zsf ~MeX96;CLhxkVUzWo()e]Y-LAhj!5a[vyK¿,ڳmƭݲﲄX7 rsr!LP[HWؖտg\ꂨ-W]> endobj 55 0 obj << /Length1 2639 /Length2 27402 /Length3 0 /Length 28942 /Filter /FlateDecode >> stream xڴyeT\[-]BNO]wW9vt k"S`t6J;;ؘYJΎfNlL@k37XƊHE%4:;I6n @ʃH:ZK@ 2qh &s3wdmH8Zۀ~sp01f- &wr9Y䙕^`- `1s8[4- )u 3XrԒaH)kJڌ- ?5N@7kF&X;𷻒lO583VnΎڀ@.,,^^^ fg7kfӴux9n@_p@6 ~hktrvv[n% ,;1p#@96/_EUUEdd6<@K$<~Pa32C3x9}ћ,7#`e:%SSdy2M!&eq~/#8QwPX:YZn PN"?2k  6,"l\]Vf[+ yO|Bbsr-g/̝I>N>K"3ŒppP6swnfuZڂ,lnr9xŜ%} ~~dxKD {';/܈XTĵde'dlid ` w eK_`avr].rX$~F?"o `,2E(A`N?̩o T,E߈ٿ;;'Z^[A;9@V7Y8;/ xP`$X[CI hf#cb,l,<c[q{ang"Cz9[Wo 3$&Y)`@pBr`btXf? wH \?9.; 8;9l*\.f?f?4'8]q;%oWg?l!'tEz 4 {r,xqS(7>pT߳&@9ul-/_0Q2$oVfV`ߌ#՟[~L&vӀ| u Ŀ[z--٥5GJLR1U ',fLvKnE CZ3 eSBu8%WM\Yn*Jh3kf*-w+lOh 9쾏eŸH%7,o_Ƀ*akvs^@'B0zY"i~1a%3IXlAMvoBy0;T9ɥ,0؞o_$񆒝˖Yb4<@{X*_P &UuH[!sfų`2R0lHTR*i{'LCzФ`J.)OBWzsoʔ00O^FhYej“6S R%I`[IL <=JxհGJ5%ƀ=r1A%4#=tKIϑ5ck(PwC20 4kBnG׭ y>E9ב+B Q^mHnIPt$7jg̼(uC`xX ֆ0P ;D Mr(}5 5R6&( om#ݾ4%*}}-*PƲ\O2{~(W IS2k&NM%r?^lXЋ еP6%yP (C㽚-CTX(ԗ=ӑox OSsB64+PX]0eG|,=cF\axfL.?"żk_ä#G&;B*',}"Y}/,L֎s$W͢繂MB=,peߝ'4zs[97-JWq_T_ jcJσEaЉsKx* P+\0Y8XٛSDE2-V>* oes2(V_!k2n)!u Ӿa*9C6{XCp_/_Ò޼ ]qy 9M<|_u=A^, z *A^ꢉc`vA,c @raA4=ko_Ǎ=ٹĚ/ VPP> *_'QHb mz *,ZԶU,+qĮ3 ʬm N]V{~]n gP4 PVOz!) .7#_Y]8`#Tx@1'ьeC_:@Re } '4My/vC?@r5pAU$nzpoC?dEHTlF"kCl]dQuF`p8Y ( T%E-aIiwdMbz[ӡE \6jGFKC< E9no3Zy_)A,Et7}PllRdi6zR"C:0xPmWA~q* &(ݑD8Y8SMib}&"W2͏Pa0.aޘ#x׋#3&Kfm7=y?<pCnը H#<*s{I r,X[A?JX)Dc#!3$ GqA'sIwė*H 6PK*ो`o<@6D^A;rX0&YT6P{L9_T,w!]f7ubF22ï8)s 7睢]~eO%(af$y/ǔdA5AΈ"C1iž`Бcmecf(FK.7ƥdi{3JնdK|w!c@&?V9}yi`̧.%h`H0-Ol](UieFTթI\uBi5bϦ:e&׹\C}81ZhzM^ | Fas#&7L0Q5ƫ ڠvӜ#( 9:>*lJ$XބO)sF{x3JJQU]+LR</PK Mz8wS˔י'w:ږy`Qrb^O@XίpR"iPEo"4.+JR%&J|3j&߹e'{gk4idE|]]a@i[ <rLwשftu{8O(rwFɥ]Iv* EO?;$*)c/QK+ 9Ik8kcl|yXUͻy@V5_Xtb!ӧSu_[%VdHƯ-1h& 'B$Mn~rG 6Ez[DIvCqmycߗ:(͟Lx 8>QF2ҔUX(pGUbPn(.nC|bEsBLJ|hHŠG:B*@<[Xu% fXԑ/p[X3@F!Z\9cr2DCfXrl~k:b"X9~<>.晉zD"ѲX) @XsO³3Q 66wds9uԡ%x5|:DX ejeX1/K&@ډɛu֪B25` . [,OЎ̬?%]w] y#r LJ(Kl).EA9ī4?+\@Q!ԩ =8 ͆6="+lo]/26"ǹ1%)ćY1%Ɯ$T!b8Brʺ-?1֔$-M ] G'02s.c3*cע+Oͣw&؎:? d6'ٯz'\X*fdeՂULsZRtۻ3!@@ʋK_{Ód_*>ȗq'߸ٹ&43 -vqs#@'Hza*l2| pU;+)~$28 ykyW[MW8PTణb{U5(ՒBZrR&d̬<}D43x#L{hS:e1"!^p/$&AMSqw0=<; L3 n2#G#gVsdy>o_W x9w0#( jP,CXW/v tq4=fO02>g>5uB7eqUw˜@fEZ|w\tJIl-AoƭxTQ|9xО,Hm(b죳E;q+F|y+0dykD'[l%Eh1ټ+Lj\&un~ k)(4ڊCv%mipw"+ta%vh*NQ\t܆jHd^K|5w0V\:_2fSKT|=4C"U2 Z!V13,)$ۭT¯eiH氕jb~Vmeb+ ~heyQ+.WJ$p OzZR,3Y%X޴?<tT`*`嫈*}>,:1HtdP<#t}"GNg `4ݐ;\F.*ǰɹ[RGɱ/V/,*pk>(*ɺ^ʣ2认',_s}xvC  ֝Ŭxh'hcgEX\o@=Kwևc¶Q+h<)+1e@M,ECVv!&\?Rq+"Ub{u=%]gO=>٥>53zQi}DGo3cƎԜ?%LHfWTVv;X`MrK3n 8Ȟ,/&h a@nC#YG hKSd&˂k;y`*rIyrʅ>P9kʭk*zL:;K6 1ǵ33$#HvbqVsCL-1 CH`:d9؂1K)lC31c0WǭJ76OYB7>'\&P`L !(Rxn},3k2]E/сQZP B'f#;(7ĦM$=)SN3Zd15וֹXp sBiaX~ѷޅPTpJ;aY$8cp Mqܯ_] iF@2In cvb(!e4Fw߶ &fJZD$;=} Id#\ Ө?SdR m] WО,O?RYpc-.{Bf8&*xsS-y}HhbLd9lg"FuV̋3_012\)^#OE#] f\l\4Q/oNmś@mN= Soo~SBAXEm%s2 1)o?/#_c 0ǫ0&%P7RBH]xFFHGc0;}m/V#&pVbnnK-lt7vj`~Er-G1.R18"3 W6~:hNIݍxN[]+FP) 0_:ZEO[acQӿ/0fE{h}ǒl ^L+@LVv]OX+hGή:v&TQ}݅Wгv3&(Hҟ6m7[⬷P3 @܄(/4dPͰRy*IBm,¡n *5響 -P=VCG^Owh4ڒ6^*ilN6{ڧ<痼!/?nǚۊ4H 6CcYZ]Jf, G3\1c-S‚rIjsN,7eIS zj%)Q˙+ +D>4qÏT/ RQ]>Nt;8B!?<.E$oЪ )g6Tsiq)DYx?P+gyf1$8da<1G97]Jn^YEX:7warq ggizl>1pt& TU`3׽QV)duyD>m~>蟯{$)=F?f7h2>VD(Ʌ5gxT~{afHlN,Rk(: ~߲rAŇ妟=QƯPJ _M[F0|DLB><T;h1}ȵܨTލ/(hD FP!OiӟMrYIHER>@݄#km őF0\Hpg:JGr ( _XHCndX9;Ra=DZRV3ڀ8KȔ dmuXgK!`wxBa3@!2q3Wj t02jJJ2Dhs>^F@GJA $kЃQ_yxu<<9EEdoItmPqV9҂i 2V _KCm2IUrλw.j"nV^sFaoJ3L܅^gqqYeLCt 9W@]q(+Rk FuЭ;q%a6 _EZ̓a>a."@ l7a;Zr# ԊY 4p9\_r-ӃK WE r Lh+%u?ЦciX>em;q2YqAMbζBV+] fK[ :!d=[OD7A+Qo4-YR႔K?x Cf\562_4~U y>b *|m.I; UgSɋzu2XKX̳I+kvmDU)f`p?Mևbp< IӖz7-GR9vDž~Ӫeݠވ19q:>&O6=#f= 3񱔌zTӉU;vTڼ#D2׷c-=xL"J[;5xp% -{X*u&=b稽{NwVdja+ {sO?Dz#?ߙ1l:A[du'ȕ[o> M֦m1=3U"IP@ra(ofcTY »= +վwN!t؊NHS]kn~ӑ<>k4^sO"mƔ Rֽ[W(H}T9 ݶ$&!VMG_Kx(d"aJ.WE$"b9F{Su15> i2Oy$ˍ{![n\ ysﺿcS5Ql1O\dc,x1צ?s8yrǵ9K`IVWa!t)掂 eP=4Hn =*YKff( sUiLWAzC?2K1 $s[8r,b{i*ՕsL"tUWiR;is.:UvCKUu`bf s`%S8u~A„>͒ k&1i!ңbkr4988(O[̰|eT̹WC پ\.bpfzz~O *뤡WR]Hw5azsU;~_-\P{oj 0_ոn "&A4JYhPVh,c"1 U7?HV2fKQʊ*|j]6u 4;_1 `g &2Y%(DN|]O:ȇg>q4NcvbiT(+++Ò9/oXzI[ 7fXFzR1/)0Cjd_V(QחNo{L.[bwS0n3|XL/Sך[D~BDCɩ-M? 8~߶qx#*'{h|0J,S*, (F}%˰R{y{[)hiЕ?H.zD1WPQn;ݩ"2җ@f1_OFKb##r9hc.mt4V!/W22/Oz'|];-؈R[c6 uy.bfjŰfF%9 9h-dL:2vo2c=)iJ/p=$L pH[mH):"i^](Y? OPl$l\QRSD\i%5bx8jݟ<~ =Hg*.ԼkѲ֊K9 ^8mP,n4}t^Y[T)4*./7ՏjF1{\xq~ C&l?B ϰ2Tq~SQ$]T֓cc\Մ1t>jZ>cژʶj*GK;Xw(l$ ,iO? W5):)[7BA';zWZ iY_6$[пJɜ=${/> Zꮊ*h PgY^U|;U:~QR4%:[QF,&l5LjwaS{.o"|5+9-jyV%65Z|ʢdϴN㵇cڴYBлV5ђ B<`Yŋ'+6eJ֢v aHB#GY?odoZ^Nɥ?ϙLFne6<>?-^sVl VϘ}q\K"<>:p 0Ẕ݊h;qmmwg1>|g!6JnemF#*/:I6b!|D\"OLP0?z,S_B'^_85?\IqhmzcaM#.5ƸT"v{{IS}l-fA2!@B~|TڥbMP`V&jfϞo=# 6tЊ;E|0fCTB//% 9 3ff7E>{5tPIH;LQ]$֑W(cQz3kss.+%?$_|.\<[ݟUrk>/P.']Q;y %AJ)T?}Ĕٹ?wVGVg0ı Ж-iLoßv~}[R|94#b{\C 8 iH)R1ees^UI0 T#b\KI;#5fv-H8|?rE+'wvZ} wM1g$beKQħuJ}ȍXwh[F+=竔/9ߙ`%-~]||f܂ఎln)%RV|Y[e@. ]F.[lA/`LpF:I$ mNλHOkQ9<۷4b []mHwL;(H#EԜSS⽁AȊW`e "dLU:=Uyy{ڈͯmmS>}7Jz6Å !su:fntCT&_\?˵%t | IT&~>-CTȕڇ k^!AWmd_F&Ԩ;$k"jګ4/Yw,ag޽ ΃K .C*Ao@woitRYXOAX~7J}M6kj;}czۃjijezz5YXcٌKKkNqEؚӱPDv\Tלב TufQd!V]Zڧ&\&+nRݳ5_ mٌha9F놺*EE}PFqNN"]h${ͧ)ޑghWOU11K {V&1r; AxC*^b=X~f'3[ vlDiWaھ.=,ObD~@Bຐĕ$'C86oGUʓY4ӜE- p[PoI1p+D:6;WvST J?o+haن AJ^\Z*{O]aX6CZތ`4]%"H`VU,+#l?!U ub;a˾G5>PP_ZD kMb1*X+iב?I 1-ma~|9N*&:6.OM$)l(e_THmZX/n#V`HJ/$.R3D B^p{f[oocj٣.F~ݓo1O ?T>hMn3_CK %v^]CŲz[ɷ)n:F'^~tboC%y% Q:v7\GM`=ْ cTKSqƷ3;!obF&iG;otP~ ܽ>wׯU&'`~̫ h;͎pKq啥p$bf:=S@7G(L¢q~Ӎw>8Z"hxh8ÉKD_&E002M.3ȍށuĂ/MT4X8 9WwgSyUZCf]m+[̳K8UȿF(DG0w/Wy*&ݱ s궥Qz˛UePr?}v֤/!ɫx%D}A.P`~m!p|@`DkL/K l7 mߺ!.:uR i2$ 6Uk|3]KO A [27E5i)f8 E*cIKȿ5D/j9_{Io9%nTqxCN}h*—f, 1sLbFB̺= V;?]AA*gk):D1ze9Y{>ą!O.,9cօ;=ޚ%y r8 ײkmt&%]Q8۾jRTd~>pP1MX!*RA 3J(cJ3lvWk_Z2U1M~%.F5_2))n>i+bm^ێ[Q Gҝ +RG]$ٗjkdbkP?{mMqjK ~|5xΕnEgH*D׃QmG$Ua2fb#={NKULE^8A}Z=Kwi,ciqu5u;_?NkǙ碰9J Ʋ}}paÍ'0B_ܛv7t`\äY oK 5swy.#cԙ܄?wH8T#ydctMVZTrZ}bܬ׌"c%\o.Lޤm5MR&J:!TҐhC7WɤW!z(P=S0I C,L&p0C8 Y{AגPcbS,8/cFKĄMqmpQiLv g* )븐'bHΣtV;4i6|s[ 5~`31>pBB*'̌h+"oXG ] NpYm2;K-PJ*=~[bfЉ|f `UY07OEޱecqUF0T"_ f1?~QejMM,\gatѓcZF [i4 @~Di6V@0q4؜u_&NC9ؔ .UT~L$ !n ?v)UHD׌J&t@cw:EAOQ 2._ *ZFoPYt%aE/W懺G_}؂dNYD@KzWɆJKzYC -j{W~Ovnt`exy&Yyx8fsmBS_Ӌ3!d5A.guiF#)J{wPh7Fճ~tXy{ٌ祅a6h!ʎ~H{f.3̵RPJ{]⌐6!H߭ljT؝ w\, o8vFUhD^S6T&FT p\)p $:j+3(o(0&i[z|Ѳj.Ecv S2|&' aV2Uůnl[C-eÇv@p2eXoVO%%r `b$(\{c$Z ,lz">gV2G4+W(>%]mnuZOP:/sWewf˙[Gq$"cnRa:U ϫ!)/stw{*Oóhzɒgu'=SV x%Z*<}CAUB'+,rDl3O#i0Hx[up􎲠SGV+7|p^H=QW6/NژVg:.Z i3$KϘPiz끁Gg(Ng|Q#En02 vF>?_VSq lP]a񀝚dc4[Z†W\[X\\FyA*8J-G[s9=z¦^YM:35k7`hPVg 4P3fWN(dށu(6F\==.rcژ.h{clIBXDS%^L`A‰TDhG#"I]8ں&@%hK. k9Z[d !"!ڠ9G-{R(L8GUi5t"x5'ଛ=O/}\Agav.F=9x!Ml}%"u6@g2c9/LǞo\Z܈ 1dc1'y9;y^@E9egS]B4/*& !~o 2A9 SOu)];۴`=.Q0臌`12d@o$AαPF R a4 AD_ծ0H R>dM"w6g">#Y0F$| R#O t ~Ƶ%$pbCSAa`Ep -x2t4d YaԂȖ?6m:\nEDlmb-? mi-W5I _ xVNtW lÆ8 ؗϟ6YNs讁D*Zo(8al[`f?J)g97 ̍o+E[G0f+IaYٜX59y3҆F;=ɧK<?K.J { 9ܘ̬_DFaݭ?c_/kZ;oc$ªž;PHN3#Z$Vzw>,prR LУygx܎V0Ut'Gteck+ n| )LomOu h /zzXs"-]& ]>sRqz7 =; ŹЭC7`K|,ݿ>`GSV'g Kh8L0ܡw.dCKCNc hTDE}lơyL~u:E]@؉-ZЂ!@gd<@|M88 9ley~7iH[jxVLfϮ.xOmxp*zKdeH8)$O%&Rv\*źL}Iqva%*9L:b Vbw/6q )1Y!/[eӊU(x,B8kXjQ|#8U0qfYA;]fI%(PL*n^ HCZY+EOqCUVD4U$@$j!%{kI~0}{S}tKT'V"A)+'tSxUe'Կ\r2=\PoD )n \ۉH=`_Q3$p$?4^: !5K|V&3b4|aہ<ǞƎ3(҂(ߢƂgJ#Uz:/=./nQ Q$xwԕ9[Gh (05s}slofIui:Ze2O뾢2iqѶk!PhcnFb%YÒSlV-ty Yn mr7]t ]_ԬT$1'UķC K##G$'Fdopнg s2X8`^ԏӳdG%uJYn!/zq3g\1d$2,sA涞j#"_? [_,FKX UMlܼ]ړ14 i $'Z73'kJXZ*:ݱ2L !}av*- ΀ ‡XX`-4 ˟Dt2Òry;q'%1+X=yvd3_gK=! lүVÐFTڔ?US0S~,J`h'VWE~| uI4S 5UtPb/+S%kVd|2@X|/+Ɲ݄er5:ǢrrƒbeNV6}ۨޮeVHo./W;=ӭmu61(H7ǃj7ѐS1Qrz ^CZiFKr: wtXqB)!e<ɠo62V5j-_&sNTIKͅ~Ǣ,A $RY5,p‘ W'hଌƀ>~:[Ey뛨?=ݤ]|?C(! \06pk8s*TFjPXT%s'.!m6'cƝlxb$~#)'= |R3-<.(_ٛlɀKQr\δ?gWbj2I6h9ٯf,9Kk.![[5hڦ+nKD𰓱L)EcDNj2> nawnּ61p;D5]H$dkZ䩒^gp4W#J!%F6u@Uzwk|%^':E( W2ґ2+Ӻyq([eg6uH6o&n59(`B8d ?JDze'04pR:oB*}ߢ%V^ٜPY;'G \8W 0s]<%b7PFߣ47%BI2.u~Ζ6-ՃhXw-UhMϓcAUL=?&ZHd3K@ P=qc$K(pXP]Ց[Vf/8jR4W؈!T{o^KC1'W3ri6ҕ^|46=UvVioGd<83^Z˰#]=~<u[ؠL0֖;b1{%TRM$#e\){){ O ќUS` rpZ ^Zr7b'@+u2w~&ã=F(;7495$Fֹ zdn)}f(HY:TPzg˳?WQ44lCw#HO KDJꟗC'qt~K}HLQ 3Kp?_E][$͸FB/¹o=F-1dw?+ovr7]KC3#*Kt&-w)EH9qv'L`\>tkYqPN e@ve¹27kL"=v_Ojk;ּF9 _O!yh6ȳAz S|. hGV iq'<" L&bmUd_*92UEϠo62VrUjpA2g0/Is)Ww-TqyA#_2ya(=@0gB'vhu-G9A/lUg#rlz9vМWZ'(PU~av ! VAM>6!@J9sNօ/S+ :¶r?[ҔɅ9FL4a"0L{}[!6'~%ܚn>jo:r@kozCi?Nabze.xU/TE]hɍu_7Vi\oBcL N7(fp/w^f}pϙJBř9E}ʛv#wN5`&)$@N齢BFABj%F{) #;~am"$s>IB9gqV5ǯކ\icck9X`Nc FPse[w 'm{ u9x)e ):gE|*<. R릤(ŞTi#㘚QaM<6Tx^tEa'trJ[T-Fǧʃ8I[i]9#Ͱ=Y9T`#u?q(IF%n':oD=U*dmK04vEpL518aa |2.-]K1_gs B/|GpJ|CRE"(/šc1jkׯ1Qba2,M~UtZГ+S?ĿkoW$r(0ycI?ޞ}KSFLFf92H*7$EĴb zF=6[-XqQ1b ntq uO+ '|}uL>8\+uPcD%[.zX z9aQ߹{2P n ‘mf9hrRPU qV܀c[#k 3|*#]k xs(uY̗LEKHwAX BMlP6s@z v^gr:+;G.KD{*@8"ƨ iŻ/Yl,5os ALW,=2K]ItL*YPSj4xߏۼU@` ;H^7_x*t.,7(EN=t9K}4ѵ ۝N }!/MCYO.+*( NC$T DVՌ) tx:GSP躇! 7.w|UV!w&+%!Scn_,B["A~jofy9 tXʈ:㨵8Ra2mx̠C`l#W?NX:bcuwaRWRUxAwD)-yUFq&7 G#(G3.v6VTlߞ䩟Y%r|btJGy7b&(ăFկa -!!w5&ƜZ%e;Ŧ5?7)XEZuI3–sy`c(ęL@#*Qe,~{κ .8D4'q"?/ѱ9A۩@[\rn>e0 S2-$)I0 VR +Ķ2;'<-b*v@ -9mHg;^",_%^j͠IOеB ^vc) dvZ&d?CJrR5M-.J{ P'+R;tzw.H0s_dg@p.܁]T09rO:b*J40_8ǕiwdPExnkJ(K C`pHl8R߄/iM*4#.EO>?( LYʹKXL,c(f"P`[δ[i%^^7$st^Aje߼ ENvͩ6%AdԎ+o)gVV˿wH᳿4n?$XK&˫2>fRiJ."a1ܱs(@qk wjm}e֊Uԝ&ٮ2\mpwĹ1F^_qu41ډgf?#= fč/֞~g6i\+pE˼¢ KlsZ,DK,^pz8n|zSfļ$X08<@׾X p`y ^9F^U"r鼽OgS5ql[((b66}? 1^#5d/ldyUʱ w\/gKa[m/*:e<:竚qORI |vw0 uUUj٘c&VIbi*j}@>އa ў<{WkR4\ٹ`껳Xԛ=^{zۆ 4`AlPPd8|Oxrmh'@,qjm?:> endobj 57 0 obj << /Length1 1939 /Length2 22008 /Length3 0 /Length 23222 /Filter /FlateDecode >> stream xڴuT۶>ݝݡHR-Zxq}9&cDYAd 9202TA&,\ @K7;gBhj r7qX8]&n&^Vff. n5z&^@_2ŕ tvҼ-\`c`O({r3[5 ˨Py+ )jj)U% e5jn "!EQT5Rj^Ձ@gKzOw? ":,L`]/l@{pU@mhrdt l x:5 pXf@ IFV]rp_{kWٻ /hN7@ @O 59BL'N. bwF ˟Y;SQPSgttO qy^73b {w.:&nWKf[av0ns7G& k'7Y]if_f~_#`ab_|\L܁Wg7ϿSB`[} ][M3w꿶'49ýL wP=;[yhV9ۛEhljfwk˸S^>Tv;]ߏ?GlL4u87w^,K$@VN;X98>,\6zE=9#(7I// d`2`K`0YX`7ݿYޓ%AbGh~-\Ѳoo;pv[''\=@f~o?ߗ tCSsV#j [ؿ(:[{132GNy033S?b>Ot_=f 3POMS<؂ڲIPYS_vHBEA-ٔE yi^ mP,׍Ԫs] >Kb'ա4BZ×x hR"`mt#E|^8 ii|Z?Făy7T#%8^S{p-ХNPd9qBg; N㗳L"@EJs,R~<"m=`}=n\`εw(ANcDgb߯O`XhË /}9QW</ Ŏ=,cJ]*%Bpbdat#p`(#nsKGWh@]2RϨkz?>ʼpÍ.0YHT> 90{꬜EO_a%UU.5Q7T7!"=mVu2VOۄ!Υ|,vJrvSXjq1f1&u&Xϝo6' 7};xdoXH jgWՉm8HDįjɆO4ЩF;,A):[e~w!N|g#hk Ât ʬ~Js/_[Nmwhq$6 ݃㏅١NF EL>Y{79f›78djaSV]<r_;,`67I0Wm!*_8jXsZUD=>p8'lq;٨y.l}A6h7-56#c 7M ΢HCDɂ`rH2-8z39ևVɯ&lVrrIW)$ЍCnN ۚ{1E#0+ w=>I̓~NH:} Eo[0b<і 38%zq)~3OaY7IO0;s/]feJ6qg:92V )ƁH&.㳊"aS,ZwY&K9DX\8^h\Х0NUt|E4]fk8_M& :+ E]P`q{RR|LKy[bٳ:̇r$'[B7[pzѯ2>,SԚ_ q'n :]BI5CT3j=I-MQ(:!]&36ohא&nIU_ L*LN.900{]#QQ#)jz:{|dBaj1 V}Y'| ڧ^oa=q̝EvrA^ ʦP&pf^)9l*D)M {6 Դkn3sa"E'*MM"cϕ9gܼй|[Ӈ5\DvSM}!H-Yl3SJ]ST[x3bDsWbt%uNM2k>tKZUD”A=Z+% Ӄ%~Pd! ĹLq`|F jZW7]BR)`E;%ƚ41wJkLf&L=%%?M.t{LeȪ[2V%B̡-[eـ%3V]?dJ|'Wdϓ '?\Vl>Paɰ= Uہ/7k݀Tx+l`^ iG@ *_WlC8BWڠ˰rƒ?2xcC45rRɲchzTI^3l @/:+#eC4=cfѯ\#y^Bla%ۗ020#gMBr4榄6ϼ}T c:óAZMjZeiYHDwOJZu)fq%y洗~nx,;[Y2t ,@]osA/7gHHi2u]ffXEr Chx{vJsM5GB7K>b +h6C֕ǫ)@ }[~PXSz%Dؙ=^"&_Ue\)B9dG݀}5Xяܨˢz)O^^0/fH:^rpѭ)=v~&2nkK8_ ֝:W{1JIVٜ/\nJ50| D݊q3^O4|@\`]ɦEZ G:^9O.Z\|:ɟ9Q-xb\;.P6.`,;O+:[7'-m>rh494kx,>,ggt3溧RNBUka||H RU`g˾Y/*`tU;!Z1+ ݱ9ͿtG -6Q`r_DRҡ4ْ'B.L*<:Ճ@&$eP6~YZW=a!xD&z~(F*I}"2u̵@1"R?k#l ]s6YsˤdtT-^I]wK 3Wuc)Oڨ |ԁ=U.W9$)d%':@c"1'=^@>\7P=qLeyE:DlE6qaJdW9V]03l8tlaJ'"Dt'h>j77ʑhMG]F4~}QvlROҾ6R-d[Ak-m5mqZfS*]ߦgnZ@9Ff!4Y*<' (5#\㺞~nٸ\g Nf6j??5WE(+u 5{pL:i*,LՍ1|ú;OA'+ijThP|PWoG]+RϻWzY>xF ;Kܹ;>,3LNya!EiT J=1ZRPhB_)YdG6 ,͌M@4X0 $aQiDbU,Z߅GǡXѪx!R 9JdRKPO&dpڭ=VvKS!ҦC]}MMXyUc"eBfI>3x}3@ mhl!VˁnMN+v?J^u#2~ HyU_.]ͰV6EZ!P8׽le(TT ^& }0LGz~| nzὍܚ:fvmdr:<ŎZHF_3UHs7U}pPRػ?<'H<\v8F)ar=2\٬k0_ 4xJ}E>֗%cf+9&}#&۠%՗~)2GP=lo>a@AM#F%=b]I{:h Ft$X^^h,P*w [5.n7gH|H#<:GT3]24K& ^qM:0bsIB An[QbfY6<:>0q], ~Ablm$oWKzTH#߆oqB(c~~E\;? CO&̖8bVh66ŵLC8IaЎ9U;X)JXkDQ*< _% :@iB DzevVƈ>g]{ru\/\AM-m fZJgGmK hKInw.\C]p,z+J %$ēG+L-.دMww6'di Bb_1-9~oF%~]U=lE^y-d1\佄3eƴݚ>Hcm 2}+v-pN&g& O_.\x ץii%eXTQ 5@0gj%Bt&"f2¨MoY* ΎM5^q*& O(>dXd ~P&J*goZl?D@{e ,S~AN@d9X r8!4FԠ&IF@c)=l3Wmb'ɲs Jiү /*y/X@/)Y⊒q7el*(%͈:_r)SҖgϾئ+1Ge` :[gr%=alw8BRA* ?s6X|Q&=#82V.'"v'iIft)rIGsvzTڙɾ,(\(bu(]P ]b{5i> Dݕ?MQQLLn>vbH:j]'Cwj,۽axDHJkB[KJ+;`_5)ۺEOSqs,bX*;xhz.dN.cf脩l7ITrfk NJbR%/GzL̓ٓܤ~쪪uy3L}0SV#Sø]WI*$f!7HluղV J}֭`@N0JΫ k;N6I*E_Pm{ ~(n4mf '_dyUyAL6E*tx{W[iSNd+p8[ $/'+Iȑ9€"+7a ^)2]YscsXqI bT EOp{,8ZCv.ZWt.tkp-%(?d;B'E0_q E# BU"?`e o3M`<V Sbsf"rFE 1|T7_{`g #.fV[ %-D:1{׏ugt(7jXͣAKLI-^;$BnR<9QA V?f?lS nRY ܽmI34_< ioMG6[;k]m'w"ifzw|)дncySe1 r֩>ьxy5ʴdS{ bjS: Yv(_IV+Q{/NiS#켢@$ !kqlub⦌M@:  ΣaQ#RۢeM4Fd۱K5Ov^&ALg)RMu{R&.G"G+nr_Zp߈:xHĢY@H:}<4E'n+Pj2ڞ d0f1)GJX#ytm$44/~I]Iޮ\ܚ c|1ivm[+kɘuȉl|)<3zbrF7 AymXȯYT­m2XOҹta)Qb] A?^}QAg6#hmaښsD [2jdo$ۈsGa$HD^/T#8:{ǰe: :jLb_<*7rJY!?fpprmObh;2WTt6CtCOpwrx۪'!AT7L*@+-BCY4` >Y{A\ZEĊ(1t?쾴L#C[G3;-CKjJ Jg vVc9C{;!3YZg9(3 x0OC;oվA40v{ >D/b&)Ͳ9x[W ؍d]BBGVD) Ő* "9Sog* t8d5ZꏍGWJߦSē1vXb$aaYAˑ<*B\Xb 7ztv'1+(T(GѶطM]ftį]29VsgE)J]'CqP1n3V? `o1el0 ;^V7\w}8ܭt"h K ??Xۅݔ~TߔH+Ԃp]+"#|Sy4)Y gv.^[sS@o}2V uUϝ洏*60 #/R\+e5S)zVGTF#9 m +_0T W-jqiDJKNfr?d^5%4`kr'hɋQa}͏3rѧ LPQjIi)8+ 4eQXw%Iθ^i<lNQ;`.Lɸr<̈́V#B'`/졿((,gJ< <ӻ֌n8 ˵Nsa kJqS=BN#]jt U=iԋ'tT?0+d^| ?̛pڝq-IL;eL6-9p6ˁYt$ 6 Wog| 6I<祂ɸm%C?&)1-}D@RY}'EVX8qwkw ǫ.s[""ybqE6@23亣#㭱dۭ|%wiE@BF05&a娻3^Nި}Sm{ilAڛ|%_FCܯ}G T#9up0d4g)ODߴn-s>`m۹0aR=U{WB3~^m*_);a!촀p hR@ٝ+"m?2o2mg!fĥ\&9Mi%ձ]Q7vs mӼze)&ȥsСBSLbJ=N?STu  KqH#n?mya k[Ir~SMfT Ae0%kmMxG)Y<\Pj^Y-GjlsoQHYERf !#L=^nUgԹ4ʯ(>l*@7?? Zָ[(| ~T8cd,ke'u>3NAk5i}6zКᴻ]M=#ъ,AS,țQ]+MlJ`zX>JAIo<#(%0Q9Z97-_~fWsVDB5J&\HL;P3u9iVD7[񣟲QLDž =8c3 ]Su1.uٍd?Zc0Y-AT4&KSQvx6^đ: 21gr(\c0,4"[ ay]cx{ D;-o,mlNjx@30EZ8тS{b,+$G‡sM1cs7Vz+#3`v'Z*GTGެ_1SK|0"Rq'!&  F ׏1| 3qaL lxvO~**_U=)&oXlV>hrgjQXbZCB5d\7}a+<.J@ɯaBϱ@6y ! 8xD|tn-Uηɜ/:1ł ]nʮ EPJRG(]B+I|gwf dm/䆽o!_S)W֐F2kAc#֡~A:D>Zxp'Nԛ)& zª]#x 6>F*O$ڥ߇?\*j}\H)F7/}`,K {=w%lk%-U]9qmʶ˼cPԛgOj_fQjTzJq!~W{/\7'S>m,5li3ts]Ԍ$Um΄ޟ'Zm]?NH&QS[xvD1]5;wשuwJ#e' k_0 6\W[3bTP&Z{\j ؇G>pe']H_s3p >୛E[𐥫,R`O<ysb1KC4g},$I+:Ƒ밡q;9^񧏠~Ir~XL}'yiy[\L\$ER*O|b߹,dK:W46NSq9֠7Ř '3$"u_?ub. ?Zz1o)+E+vk~Kd .}_ܖC95.; raGYa)()'u2c޵q*e_Ge7?(5cT6!1n}bWSdeIWq@g|N1aE'1n=v'G",($̟ +S j/c6o^ɾ)ѤgG4貾PH[Mu{Rծ oZt<\zEJW.Heo7kj5 6KW bl Kl$P \ Q(3 ~UUۭ: '"C|B0/9քrt+Wˑ)՛Nf1D2ݴE ҁ"="?a \dD.3R_j s.)i(4 7q$سw:'ox3S-~=[kd5޾Aƚ5L0\'ؑ⽥QL_3|ܣBްNJlECL@*.Q㙊YF1G a KpRі\#cfI7p\ u}>qqvw%OJ|E44R 3 )JbMMgj"4n%bwkS_,(\T2A+Nq(g\^2 )~䬮KxT.&Ѵ[g-Us$ܴKpŬ-&ND}!D;꽔TVZ %"gY\8kZܶcgKz >¹-UumY_"],y*c/qӍL:5 0W7 $qr7vqQA l4^؃^il T/AzV=&|:rN61uAB*<7B"qgx)!(u.4 F8*+hUbE07<ǔJC>*oRx5VYA'}}- z'#B'Pl;E4=YN:/vd ke[^E?pIiIUZ^G~K mkK9Tu6H:dm107>bZ(g:QZRBxd@YЕ!'pɧA1L}pM7F~Q%hy, `aKPaٺ?ˆ'IA<)\= L8v+&ʣh~x)dHjLnH_iײ{v:~H=U7!in0d 5n8$Y@eU<{+f ᑧ!iUǧS"?6Yme2;G2" ? @t*{}ۢmł4pRN.,Y-t:/#5Sd`:̋;HBQj\o\qoI#J!/,ۖS"ʻ ^wkO7oHJ2cD2gڎDQAREBkѴ M6\M•Ld.8N0-- sOlj9.6 ;I/XWXT͇J_KɲO׺v6L KCfN}; ZWCA 9ܘ̬_DFaݭ?o{;c\!^bܖN '*6mAk˦$Q-녂߃gBK{Y}lU.誵ϵIl@I36 G*JRB>^WxYe{a@?!z[V2iW ,~A97p) UsU!Nmb| ~7r$`a_$YK7uĀUs֩/g}Mґ]uA+$GwPPHEpQ\.r{UL [lr]FE_ulE la#ZV]܋!Li$s&Srˣ8X%%[o{bJv{;^('QrϚ0KɯFDowf>Mk(ztjVkb7aL |- Ȣ˾V/6Ѵ./܎n'zA#g"JFZ%zôE~SMLSf>coym=J+=G2H5QT$;-',o CptCQVO+^ ~K˧oԾ{$o!M[}xk+/yh{Xu`y$3{\'8H r (̛#Y[ )n2sa?.xV h1B_M:rz+G oURLdw]od"9Lʱ/b,ty+h] Wkğd0Mۋz#_l~BX mUySoYL erjJ_R1hʏݳ{KK i]ԓ]NĥcWI Gހյ䅭Dva7mem`-tMh.iRq [i™+A5pLDWܖ;`_öոѠm*jWaMS Uw%d 磸{F\K>,rTt.K yNtXՊjTmrб2gS?+UMd.n՚)53"Aj.4ǃط+ LDp/ܝ:I\t_=KUVׅLX-QtӆQ ]o H Rp3EQ!3sJ8FGԔCܕ`k>!hRhFևƭJП IQNrb@8GWUOWUORh4:DU۪xnk9%c".i C,AS^PODƛPYe4oSJo9Rf ;#IܙGAWDSSQN+V$BO%ؘFd8Mfr{Wz':z1ӆ#x3ӡJ v&/Z ÁQL6(r8p6GQIòAA`HW "lxfD t.^m*t܊aR-1J1V3D-s]`ŇZ;x̅%v&9"ϖ<.?!˔u$Qjb=Z(Yȸ-D`jW;X6~x i-߿71]<3-h/W.|Hz+BFيMW.u-lNdG1D*$sDyzLCѿ'%YR]' 0h_cO bѿnk?42N9w2<\y cY鷿TgК>~sEΘMj^jRYM|oާSһi_?u[M]?`w xFH[2s 6{# mF  =]$NTfvKa˄ hS_HKmW{"W;`]Q dS+8p YuKYEXfhbR=nHbeW`$,G(tt~Iv/~#E7@ÿ9g/< 5 )_IK+yipCz [2s9id6'wprz(TdA ɠRlC|stA`h/(1k@ˣʦežd !3ܿ8_$bQTB0ȤaYRkIJM^nŨ@.Μ q:lKk]iLҸ;WgZ^}"+OédwFq WʴZj.ewD72tOj_I y[J.]*6A : ׇ,=t=-!w@<Š{AJbtKx/Ly U38qW;7\[?Rκٌcz}nDy>Kq0L" Ώ@]R;Չ,9{4R/4ǎ;Oqd4IGRopf@dbN"e`ĝTPSx@$5j(mbl I?RA*'JɪrmZŨ3jEH?BKxlyw(zb/lhf6T(  _A.ƣK?Z ͻ-50ˌxH~0"nY(8_ И{9g8izVNg, Q^=FKbu.Q5<^N}iᐨz汻-Zdrୋ0ЪRr}&‘餗SC8]҆?h4 @C%kHz11#bΌ",Ȅ *ɺ%qzEb˰,'׷M*ȯC g6m3*箖wh AV27?SUq/.\[gtjN+wG Zz։JpW6 aD9FS޷*/zt%CTwf|jdĮSW,YB:$1?38-!&/ݸZu`#ѐn('/ W17CEM@Ӷ$Dz+lD?DRI |ʣ ?ΊH׫]tG}'-Q'oK荛%V<ӝKiDΙE]]s,'kSG|s1g} 4 V+YU;BCYUi̛&)fWGb0F j_r4VZZU"2:р/.bCײ">RR3xrOvN[]])u 0 \V3"*k~B-b7AvL( ӀVB@ , BX+ec@Đ)(%eQec`?'pJ#WSHoOw(lUVnPUցEowOt8Q{6Ld0!>s#e_]LӑyxY+`V;GWph&4iʑEt9 8^6@EA߯, 2.ZZHʋ<ߞVio.^ ;cr/[jf6mUAe:[nߖ#h R5WN# OM̔{ _8өmM79M0nd_Z$˂[UG`Zޮr1%/bt2 ogzO$sOv1\|_z؛DZf1n]rYG<>:D2(J- 3ٍwcR*gq _ݨe 7V_V TlـLPPAi_"%F%[hlM.yS-;`3r965%L\(\ dVGݯ첏'';C߬BFGlKn=9vfdϧ4^Gը8aYxbM>47 lt6lA@럦EVY˒v8SpEZ'LrYҘkQb Rʴc78FX5M4 &L2MyRrBI-!SB ѭYF"|P78fBiI\LCQx:~_6T$\{P2I}NXB*n0W+sfi&L"Mgrn~n-4!.XG7g)\)7>#nvg^b_ܐ_r$C3<PgըݵR"6~ZE>!{bn { MbۛSM]֫'&73_`_n9thSG{@׹zmS qQF )\Q)KL!l]\߷n6׵!0* j D7^\RmZ9 k,r5m-$)L-KnH8mU<•-1^?LoD;"B7&>D]+;!rȘ#}`?Lt abw`]*H/Gs!W A#0`oԣc endstream endobj 58 0 obj << /Type /FontDescriptor /FontName /FVBGOJ+LMRoman17-Regular /Flags 4 /FontBBox [-400 -286 1338 1125] /Ascent 682 /CapHeight 682 /Descent -195 /ItalicAngle 0 /StemV 18 /XHeight 430 /CharSet (/N/P/a/c/e/five/i/l/m/o/one/parenleft/parenright/r/s/semicolon/t/two/u/zero) /FontFile 57 0 R >> endobj 59 0 obj << /Length1 1706 /Length2 10649 /Length3 0 /Length 11765 /Filter /FlateDecode >> stream xڵzeP6ŵP 8.-@`AwRKq+ZhŊ{M&eϻd&b34&PTR9@;9egCp`$ @@W' r9?k-f%b0(Y r;]$ `+k̿#gynn qwy%2 35htZRu-U FqԒyS֔d44j@Voʚy +IiiJ>rvNntj ` : X@Y V,viZ]g[30 gW](A.NҐϥ|vzơK12@WQUU`;@A@gC(0=?A$\P8dv޾@vy8\V>9u+"` fg`?& w1IE {0,$ D]~WL\(̲ٓ` Ŷpudr;$e Alan;ݟ OqX\@`K :|o `6>~.` ?3_]gϛiqX,X!`Z%jg 1ڃ<&:d!@.`*jnWe%`e0sqpIv#|_[z^螧=ğ{6UMNUBM3)s :;=؞< ?`eq@]P_%wW濡?;o"7 7 ` wϬdPg-Hl|D u{?Ϗ2ّyC<|vNngܾk=gȟ[o@ 94\0&1O`堒PXW>a.sXH09 (+`PKB`Ԓ\5za&S#Ŕf R (LŸ+Ak"55'y ,ʰu!ѽh ctvt {=i!pHɱoW$n aRz\2 .˧q˷;zCץ\چ]9G~;U#[P7O3_h96y`;7qYj~Mn%eeEqHCދgPM, u s[gNB_~>W.`ll#'a/ bEpo$W-#1{rDusd 钉ygg;ӮrLisjÕxST"7{:qVS')4GJ~SoӼھ6AԸub@!]# < yWG O @K}]mde^DÚ uģ xPTn1 a"Cw~ooY 0ȥt-&)`](O"zta}gN[]\fLJŽ '_"l(JԌ)[jj(1Y%|(DR-< #FLW 덥o&n4 ϷTrzz^[Ŏ[)IyM/G9 =0`/S٠?;zcI^G~,yTs= yDAq+ڠ!yҘѵQ`k]l!l %ˡdaj5Ip֭z~ (˻L8k6b{?%gT`>pW%\=zCl &UeVߚ.srC"]A>/`k@r:HNߞLAN^Yc< xSlO3m?DHԒ>B}%l`/ʒ15XzZYH|5-=!|%~nf@)Tw0BtAJq/ P#PauuĄ ;uNtEL;SA=/B6(tDj[J7u_h*. |'5uגZTW 4k)x1VDP K},[Jsg蓬;ii+B XzS}i2"'Q _.-:`upy2=O⺣b&CbbssyUUC]H-^jz, (awҎ(&{S{ᕗ{|5qK#኷=,_R{jWShɫj175ug^z_)qKe]iH]pj }f0P2Qmoz?ԞZ t84.Ib4/&XhL ͷ]з94wHfNq9"QW?-g@hRƜ{?#{?i(8 *'h7 vьa/즵rB=?8[Y6P?bM"Gxq|#)oF^!K^P0g%[vԸ|?d L`r>1q R il{ u6:ml`x}9Q\X/`pRh݈Pa}p49EaAh/#$Uiw< >ŧ V\Ey)2$ZJ%Q:x!A!W;jqF3TES !Q,v zBVwXv8\i30xwuaj 7ETYtr)o] }IɒoB[k `C&Ǘ˒hFL՞·+Q8=r.~)@B=#rDYZg{"7N22ԱozAY{TeE[0`וb;ZsF urMb,`z"yUQ_gol-S+gkDȌ{,^\XO'AjܨqR+ 媸ԙ5jBc6s=JM1אu%_e8~rfu$-@"_` A'+:) Uf3U+.cYHǹ]0(l 땀֢ֆDTGX-t4ߓb?]@턾>E A=Po/1z_= "`⏷ j(9+wcYF$omCgx]r:U-7jN8F"!粑H)j_TY"J ?E;j'Wݞ,ZxP6WYr}蟬説(,i٥-#k/L1^lZ0#/a(WFpkr'Plt?޻Mx*abRf&3<-m@Jna^u=8g] Ánr/|m7 ~C$rj/D1*g?r,-B$Z.;f#, L [mVr#˅NBj:ЦHR[QT-<> _h~dOjr)Ń {[x*YFrمz}/p4a7{B/ 2ɝ/ɣW㕸!8(_g={b?^s~;k3KKt.CTU{6}AF=87J}->ٸQAN rU0bm$v!IfVEzѓUx? !ZPI"x3ۖe nJ9o2ugӈ\rQ])DEq5托Te?34ge kDي];I=rBX>{K2]&\QĒv^ÌNC\` _7RS#=RλB͖Dq[9\Lh#/rT1u@gͺNo C ;6e1`!۵;0ɗ7%f+":i^y`[BЖtV^ R ֣cvhi]-Sdz?T{㗨\L@|'޳X8;'6IHu"V+p*UD牬v샢٬2<1aIVbNvSSlO\K(5Ӈ0 . 28~Q"O* Sɓ"Z ™ЖmUxcYGr퍮(M9X5,awFŧN"H΁Z~ȓ M ۬;4=?(#grʖ"|!SRғht`6<Wḩ *YuuV̽5bp,ʊcL;-fST_הq1uJ'S[,)Lo@Go+r1'hHy'_Nvlh~½25}h;)׻*a*#l{ ev4ؘa0>y;K HJp{<2cZ4^4TN"&$ Ͽ^wN¯5RhigTG'ן_bJ l:]7Y%ٝ=g0 TN }Zp /C|\(RkyWŨ~q;p7Kt"X:61WG/dm$,;|Վ +6G\_j=vR ! PrjsӥKN;b&5ǁmUVfgfo7lGw%~}mYeZ NQO|q]yI7!/^<}UFn9efHũʞC pP3Ri|ev jQqwhd8%˓دN'G/-8A|ع9,eI%aE *XLXT[_6@=&Sֽe MЇd*G!;lX]FNEdb7v9!Xɫ]V{368t~td!UhwJ49U~ߎX0WT<%T\0h`U9Vg2̎zh,1{Kop|8O8q4iS You pH\gh/2Y(4>2n$2\/xUVX|Z#Xs|=~#Y8|;/i;yŊD7g3uj+,=mx9<|q2Z̢EųKX30EPټnbga s6vWZJ,'h *N2H2`Ќ Xm짗N-|0)6eHI?{"s)gך>7}oLIqc֦J9H0x3@Km(R[9vDX ą仼ƻ9n@Qzs0s6f4W90pd7)]*l`V@߱#|jÎ SGd.+L7dڻc0 P'&*5g #haPW^⣊I"nmɡ Qۓ3ެBJ @~zJ"h> ,E( 4SK!%nJ 4}]MwsU@F[XBNKXe%=_y$N%vk"ynδNi +jJصhvYPV}x;V ŠI#z<0{;1G{rx%458!~04 ھN(Nl6OO&풊-_Ѿ7zc[JםIet!9N>FK^ O Iu;U?wwh6)9XlQ^%;c28Ě wh{v+>E<*O["?̈́c %y/e-v}? Tt#>&:w(kaZcxC8VXvb56(|eͣPSG.ޖFocGLk D!"Ex =FN_0jjmɬ8CLyW 槹sRH4vJ( HjXkP:`8rr.[*|"OfG']׀D<緼O=mx.zzK᫐TUQ%?<<:2\@秈i6+ɴ Wj̈j˅|3hZ&nޞTG8*񍷗.X rAZ~4!KeġW4j۸JMTHHQ8~LeKsvw:-7f0ylGѿ7eoel+tc?\|ȷ7Jk+ZXW`oyz)Kр -t w15N9J&q_l ;lχj:!rzrXk8@c4-3KʣudBZrTm41S5cG+3cJH*q@X犺$'W0kSd#rtJ=,+TX%>8^~mM}cԔI \&fz 2֒lHҩ3 fhJ& ~M1([Q {ʍǕ:)wQVe_K4?F^H-v6Vg+3_4jlѮE*`|opGQyjI?¸Wwe$VabSHK4KYR#J jj?,l0[0~w8\h'SZ/4<$w!#3N'٘yO%и"v7ʯ7tѵ)=^mJQͺnm} <[p9dEh j4{z_D[<L6+ !!G%<ܾQ&.ǰ:9?s}ӵq_*tK"eStN7⧌N U۔Dqy?a5 =e`Kѐ h:1a;e3`xrD&OM;'`C2) "Ζz<8"@U%K?LgABfΖ{8{'^r@2!鷖*5vhEQDN hŢt/4f ׈9j!a/PsgIxkkakx0 x:pK(\Lo?. 1`HX&藩[[F7n__p߿Rө3.XcD1jpb96#Y^@]5?5{P ,:  z4E׼19l"E~S 7WN|T$L݉赯a_G±*<#,n4*䈏>02Jw߿q{SC1{9 zv~<,!/|)[bg26Q((n8 ajD>j]'JFk ͒:o V\ -*!]@lOڷep}0R`:KVtl}^yr ѧB*20O"[}-Zd4KHJU`-V0ۀa-0Um2e6EP{>EBcC{LWςEy]+™ka+ ޣqk\>$+OG|ԒU -^뿱0<{]v'tLgGxKZ^E6@!qAD׎Ю/-:zםI*v!O\~c31GAQUC[1Q_OUt^7S!6t+‘m&qmT=ɑV6.~cfғ>;חE_+Sb&ᘸ5V<-9]k!՚H qQ]!Zzm&KnJH 7*%P$9&҃g\#_);'/(5/?FqbMcwG _wBf74:wKAa| ٦BF[eɛVsX׊{oS ?V܁=?pv*O׺\[> p{E\+OCSA;{C MIEb=ţʍ;< 7w:mf# 'ő3˔V1"<wAb`GU<r6E1{ !u2:3) pY*R Kg#d4Ly59?yu6Uk7v4xF19F~V] iYk'A(ܮ|kZc8lp [7:/YbW#DdSŷf}|hn [(UT(0m8e!yl%&@;Gx8Mj J}={pIأr̩̙XW *F '^jj踒sR~4o{3eTr8ew4Ŭ^̈́To#_^?(a({h_Y 27eTRTE>_o^9`^H-@#OޜhbcsQE׭ރڼ_9ILn;o\BC">*]޻msJê$(Jm9*.Nkn\s{)<j`_Oaf'jQ[%؂;׹#%.@gߠ:w+IϳeN>{rER򼏒c u>SSXuHt|_glgXX R#&awǍ =e2W"{pLƴv1!?)0B`h ǿ( dެ!Zil H\aaСdY .T+ ,kM7#Ѿm^ͶfHUhn Eug/͜ Qj"!Rw=V[qwW੡"^9 kG ^<: MɰhA ir u8{`G"y#bF owZavYZc"1.$+Q0.hv9G}_Qs# Z Zek稿LE<2HLf^&:w_N>1hwbt2e5K D.s"+|PIVMDcӃdkkSj_3!H"dcs:gi$$DpW+޿I>RIƚ>X9.Iba({'y.d*o4r%p0,z+Y<r=a{c&-z8OEOU{D q za6z?v]IFδ^!J^Ŵbw.. sݙ1CVHɈ;ʨ9c&aY^ endstream endobj 60 0 obj << /Type /FontDescriptor /FontName /QIPCQJ+LMRoman12-Italic /Flags 4 /FontBBox [-440 -289 1358 1125] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle -14 /StemV 52 /XHeight 431 /CharSet (/c/f/i/n/o/t) /FontFile 59 0 R >> endobj 61 0 obj << /Length1 2161 /Length2 23207 /Length3 0 /Length 24526 /Filter /FlateDecode >> stream xڴeT\[5=+ wwCww}}ox~QK^k}(IE@& {FV&>3+  .beAwX%]|V.K' :k&@cP90;V@wq˟쌌"cznjrwۛd wd0Zۚ@u6@CMRU FX\5&@JCMu ɂ$+5Xn@'g?i;3]͝@v%X8133Y:0,lni p9ޯN@[_q7:\, %y+S3Gпv|wz{!\Ĵ94++쌭]..dNNr([4.z_v}8Q^)_s+[zfeLATQ棤:_BJxX?)io&{'bVq9y2 ؃GhneoffV@3}!-X@GԒOF㏘}; 3~Av6v\\T7B`YO@+9/;4mK=iA.ݧgSOv@YzXhZ9)[Z˸ϸ-4l|?cQFVѽ= ^^?dr222=+Hڛ̬-l\c''cO`x 03ك\].sŸ.rqBl,fIcSWdf ﻷ߈l70qLA=?{꿳76vwӟ ]fww/s9ANpx70[|_ߐ?VN7=whjdjgn^+8ރjwt$L]A.m3cikq|ۘ󝬋y_;t=x{J=,ݦj.N -& .NVz,L,gg3bb oF.#rs)ס h2Nk ,eQ#-9E'G.EU 3K /զ }jOi"oG"):ɤPCF{*WS1Ց1~&1m &Lcڽd cpe+.ڸ_tnި0 g\ơ+xtoʦ=|VKP+SoH8%’D=A)14i/G0M$3]ڦǸ3m岣5@p4opȢO(t cT/]P%xODyW~̮!G'n3Ce;"V]/(z qA5gWC䳄Vq~sI.PC(H'EH &h{=wtc204wl>tkD >]4ArM/b-tMW:D\cX0gjvY!N Z/[׮UqDL㤜ump~7o h8Q[(v!+'=lB~m/+I 6.7q"'oEMS,ԩcӎ8}}{\ݡצnһ\>ӶLznotK R_Vہ`$K{6؆ȟ )a:CNxϗt^gJ HϜ;Z_8FډgROqAC<>P(A<4SпUUo={mx#U6Wk{dì$%ݥHy!!^Ua-JbQe^2*mm3rWV 5} jt;!t}|35br8ҧݦ{8,'' N)`zFL}q4L$d|] XWAp0/Dwi}\@tGiH\/}7KL϶=|W1G-U^1B>+z-i%Cs\ dCo! /շݟiHهĻ8Td'鉆Y|8'"c7])?GwX `'qZ|%%&|z1z Fi|Bj躬L@ӆ@D -svko,Bnl4 ޏ \)R[xa@N]I&,ޔ(0"{m]ZX-DrOL)ø,юQlHVҍN`/06u~{iJsy}@V+:U6o"f .NJzx95y;2)$4E ̺bCmr᤭xfm~6EO0{غdذf/M0@yRwe7RWԩ}擘ASyFܳY{5%csʼnHR͍==ޑM}v}KEBFQY$눹wJ0}b6ӈ}|9졧aCFliwW̖=s ?ݶi +>1$XuFW4eF9eo,a;ԄFO=|6_+@ +f|Wv}- Nƶsl8ݦoVU!Q+me^ek 4"?G%Ok/ˌƼ*>b^wR6w6pRzu=j1R'ʥ͞E:ē&go5`0aԉxQVIƝ#lUi8H]hтTne⣻@QEߝkT8t2i3FnQ5Xd_݇:&# ɇ];pt%B-qz)_ShmY5[Vk sJ3~O޽&VbPjHOyi(Hd9Ads`?3$k3ڰp!BiyqAiۗd;2~pS{[nCq݆Ϸ*Dė:]dWHTHƭFR@6 #\|MF}{X8Qgεn6[| U99E (6G6)YUk "HOK. 9T%q2jS+Lx>]Fm`WRG[> z"lI4 @P*m%>ԋ>:LÔ67PC]FYTLSltqC]fXe$۵ES_JwoM`Fs'_/=,wӞB̈m͇m}OI! ^/⤛5Zh y K;ơK#[c0- P%>0PV7g=gMM6aTbYˢ:3KYޯ7hlzܒA&k=v'zE-_chAy]P7Q =V|= 3gS=4+CZ t4+ sN+ѕIfQE+EAV&~d2B&8=cvInK9{cQyaԭfy{>|<5x$yca$``*%koI_f9m}T gHbM?n7I08 /3qƧؽ&?*K6[PP"~)kYjtOb?l& NR;m8fΔH8 m{ h3P<.)%{E#ʛ V{ri9móv #Y;rqeJxi.=j*ͯ3x EF:]k#Ӹ ~7` 9S5x#f#D,o6#` I6Yl B?|ujrycyNGmxq.ӥOsRT(GnIEZ]C&Nc;(ve+1B;aQƗ~dQj y^ǐk 1y=k_/ҏe4bm&G{Ym xK\Ȱ! g(\!\|AiZbs笣X= iL^$rt֔{Dz%^S*l ~w h} F"l@ux{/'QO}wxr1n M+ⱴM ea=\vG~6G;+ߍT4i{`Qd%2 ܤI4qhCw^"֧E8c1IקQPFo`!/\7g5勚\mUX q%R3ϿP䱺Q:`x~(GEC$C'\[Y*d[eN!xA\{9ԝy28!jVs?h69jiv.\=0ۡ K*QD]hBjr^o@"^='TyJrV%8|"EwM}ܧ PAψRX}?=~A;iQn~!a/3Z#vNFVlUK.+r}_+EVIoX Zՠnʸcl)=vF sR-FlcM%;1k?G[%q7>0;ر-M9ژE(u*|CRQ g3}#T?Y>O Bl!T,sEk+Oq@&r1WԬ6T:o,GXv~֑ƺruۉ:Vt_4}Bh7}T:nl"6# n2zghyM'޶#3+0T>xr5["٤U;_ #ek$acrrLt+ncVCoqV79Ml! L֣/wnp`ߏO4\%1BNN_ .f!R_gdkb¶/{h-cB _!"J8U eG9xCY;\kzP L&lʱ\kAz߃ӹ Kg}0}s=uzH'} 6|N:{0?7.>ʔMӁ;^`FN n4@/~TKX]̟<p]P DԫJ QÇ"?fW8[FqZom]QtZl'Vs嫰2ilRnW^l=J> Udjw6GU+{6Q–vBc ;vz$!Q ilE%ONW沽k^a,p&+MspzC!&򁊅eLQvDi7.On6eq6?Yp[=dB ]rJ`xw@A3™oպ7U-3xœ|9"nk]@ RISȁ<{{1'#5kӛ o[2+P)lW0;Kd ŘC+kDF3Vލc/}<ޮ𜁫:>H+cR6> ;'!҈}Qʸvn.\Lepz$yXs%LBQ{3Ńc 1`ۄrWs(F~Qu۹beh ?hFM!С5%3INe֋ZOΠnשfR["/ͻ_"/o'Ho&I߫{:]"9M,#;NJ+ | Ֆ}c%+/q214^0"-,0`ilk#?`Yw:}bUw |r\0wgL) ~ղ[ FkYEdQ *s@g*l2H39wq"TTm4Ո< E\@z6H;$}(H(My:H 4OܕΣ'_OjEnvCZ)moك !X gqCG|h,tUyElWT4K-LtLB &FX;;M-{t.^kӴ gr?^$Oa¸s겒un>̖=)2on eJA1U|cIp I(AXJ=ƈlAML1@ճ_;fqv?u\DtP0 ̋cOjɖy{}1>E,П c&LHDgKB2okeT+SB0 Ռaщ~.Z(,ijǸ~hY=-`v1Y Ƚ]M)OMS_e~l(EH_`1qPW0gqKnK+5!<#0ڭIe6kjF*o[iaq/ @<Mj i i2՟=ͲAhE$b#q9VhIQOu(3 ՞Nq] U-] aF6R;b'y0@gy.Ki*ʼǚS%帠l o;8D9js- );C[%7% z:H̱ח؛n40-M~$uԄ8ꤪ 7DW jݘ3,u:ŁC р 1Lwpa[_ W A0W:Ln9q"7A=!!*MHDY`%}dQ &>pfo1Ȗ3: KkfVata M&iOeICaf G (B#xfwM :Q%_겯܀߃U^4 7 neӌE)uèל9v.S4Vzf_<5@A[͒'jUZK*MP𔝩Z# >]ocn@9,S%Mpi E  3',vqZc;Ph =Ǐ:qu\;/;~q+ #b!ʏd.#65QV66\#DXTL)996INɻ|j 3(K; X^C[^TKs6aYCOV|$Cn/)%^(9$+2:#O2Fc~Mi;VpN_6S7L;Z*0V% J>h¤`DneY1 Hf6UPLi!N3S #C2`|[/g=k6P1˅#lH|),hsy!NpG֞1KGIφ7PHԭ:XE%+ĦEwy4>ݕr gL R}I{5#<ƶ^U) Vx`G=M*G?~7Xݰ^S,[k.x? ^7;N9_n( E#LTg|`"u:N+D?vZۃE-!Q$.۝aJoPve4ޒIOeӛY7gbȠY' 18aeMݱmuOf u~߼3Z+Q,;_m%OZE?᫐G-Sߙo7=G\>v%+OC dP 'yP?{h{!mba73<ָw>Ϳy:6w*8 1d]u 5%,[[hDA?{$!#z;LmX<2.Dqf4hajתEg|tYc-=f Fw]l9e"gJ- Q:MR3Gow\27ڳbPJyI@l,pܒt #ePpoLiT) ,s*y@l]y+ۄ] v|$t+ \Uڼy[l7_~WtKm_PVʆ$, ҇#v49Wfî*k>gOGzPtgE%8֑d›MD+˘kP|]ob$tb}nUi9Œ4+UJ4F? b ~R苈!evf?Mɞgs}_`L-A˂~0f'aAHձ&\`+#\W絒>:QF,ű>`w6E=3Ƣٕ-hR8"x,yo Kӧ_UoQH^5ZNg.fEyd֫G:=6 Ik"zjgQ"jX(c.{1KtJ\d7Y8>Đjb%D;؄&fn?3)oɓ/ l(&Φ~xeS.C;>JOddG!l8!7JoiVQ\,z)/N^fmiw6IAkO?7{38K^fJk}&'1G,Bst8r]ɒ`6QB7! O(4wFmz\3X>_́W-6JdFSN_za"ĿvsJMmzPh9iU#'ƹ&+qׂBd!,0x)%gly4M2A`r]dH!CK$]4:<%Ҩ kRqxnGh#D].+sFb,i" cτ2#;IWwr@bgk{z͕ot*VxsZ4oc mFRX<8'2FfDz&'%:Xr,`rJ_yF>@r۫Җy & XiYEs9X= rz 0 (zj;r>)I;m)dJօg')ՃE-IKOͲ~kf\8~1)뗽΍c.4 wT156 VN^h~zJ#X4+:ТTѦp %hfq7V8 1 bTTi2BE/佃'u3e$VjQh b~Ff2Q s{<,s-nSK n@qM‚0kV lB*Ox|%0!ϘMH0鬡#`D*@B7,痲`PڌX02 tZPRÏԽ J fǚ 9ifY QhVrz)oů8$>HEeQlDqm[ vXul|D0݃5"J%\gN1B{t#J0k(%~S, >Z a鶠@%Zl¶C s/ Jk@i#GH71pPۂMuXǀ` GH$&f _ϩT<-Zh`a c*e_s%ykXZm DR1 }95c 4W%OF/-1rYcpeQ %Qzƅwx h ?5aَ)>CJ9HA6ۅ1"KMzkF\pK6$l&/TF)a#9#G㶗Iu5Jpm*4n=!hj2 %1% FM}ћPS\2soۿO|E5K<rF2'26z-> Zg3;uѪUH E8'}^Gh-C(\{/l?*- !ϫk|A7(b 1}?0'䨎ܡHjWF=)]AY饭1'Wyd{[G2xʐy#?A'n08f=gʚ}̈́>JJ{?YyWz#lk,~ #Sq$$O~i?;4_Gyѣj=^6cqU=9Gď/<]_ O$tppcX+<5f$m!@gd5Oe1FXO78Mjy.3 K$ ^/X}F[O$ {/ V6 I8E, ,$XYiuw,(6?]Eۂ[8Z6tЬ! 9dzze׈rtA_OK%KOTeL[}.\>XF:a~Ivb䯓;0:#m,Wq\nYe۱1s']kΌrʓe1rM (qqn !]0h7&A H6n˚EҘU{@v R-"U J=4~[Fy,~Й⤥փRq  ap26r=x<Gwx(ؼH5T%tY~V.cU6zF}#[pK@:EL-k돘[T]-&|)3쭕 QU _ \߱mf֦leiGGT6)k#- "J:{'Ҝ>^0 -!&4#́R%Ֆie㒬Y;L1֤*`wN*sG蕽5`hE[($)`1b"2 BTcFP5S^}R ]k75c:vXp@=Xdwub%T?yZr3iW|Z)>%?{s''ۙ7̪9ܟHGM\/>y

Z?ꁙ)@Ye3@ЀQj)Sh! gTCY2"vBꯦ ykěC}z ΠLA5RCtc?r.5ܿN."BRuF#\2Ds Ԙr ^4c)L <Q>`Lj$ai,j{Unj_/( Mɩ#Ӄtsct)E>o,ĺg_=`{&kmNXW߮E)czx0"\QO31/= VE?Vd_yOd t<>8l<(p^b]w ka)#I\|i$$`23E%hA^xp8d_U9~}uq eMr{{VGlj{ m4{z #002st~©oNuI0g %r9 {rxd YNŲ;2~U긣i{AXN-OJYp>v frp8ShԵmW 7>HYllm$-M0MLPHx v-BbNoa׭ſDMɒ5_sW[#NAeA7Ȥjw&?0 0zg%Z?)@'O\wSձo6$]W`W.kHo`f41`35Iٞv8P fPX:\-u/w^'ꗊ9Lr$h۴r7 D*L-sE uP9;sNf[o8qU@{Q&%L%<'-5(\kvM ρ.v~ _{tJl,tK ^[/trpi!1bz\+*7Uww!dG(k_޾LaP[:KfruQO@?Si̟aSQx{fp|#M?Y9"&ٯҚNgQiZ.IF@c",c660|9xZh݈߼CU.\.oEdLNq}݅/ߠECwWs"i|DԫOW34h{ qaWk\:(UGG7ekEIJ~Hp`If`;;9Bv2s|S$+'V' 0sZӈ#( #/zn VqmIJXG~Җ=81Y)qH /_SsnOY_mCE\]8m~|D^ʉ.?iF3kdplacZ#?V^[#a=SgpeS"fS>AQ5\g8ܕ6ic W4:JM.Ve# wOp2yvmwxD U.x{LPe@wݳ_PqZ,Q]b0&ĥ ϥd0$"~6& vYv*x__?ڗ 99.5_6.C6yD0oClbJo/nKz/tۖ.Ě跆ӈ7WƼ#]{?󘝁Al1v_Z1 0 b]%LOtI3eؙ~R]:tIy̝Jy] cjl.j꧹ܬ(RubyHO#p*U:q$CRx+jXRB=~hyſ<*8(t4e6!eI"1Spwd8MxwEybQ"_}&7 8B4w>,pvk_Mi9r)w ?2ia ҶT-s nnDP loz /.H^Q#sEr*bPt:,x.UQ %^XA)iʎL O- oɆᄼ2Ӯɒ 8bi3i5HE <+E DV&C D!3X9>_EK2ϗT;LA*Ȗ, S8 u"> :L@g$Z~sp& i8L cxiYӣfQc/kwSvpy,'dz׊I2ITvD dB=$Tڃ{/`Aj/zˇ4@d?I\~ܟU]PTo",ѥ>}m>u#(xZ#(MbQv:BE"g~l>iųrfn6N~ѾƇ[u|*jNƽH$wAq`W#z?E,a>+x nTkv^6](|WWYżI恂1^!ypT^|OL0ߎ EgB~. ]LF1|c9vg_^DX#vĎj6#c.׬璖v_Kh~HU;b7e0)r3n`F}GV7ٺfҲ$` ".}[>N2 i ?OS|BIϨF"ڻ!|FwZnc,T.A&u:4e?u wg'rrV1<ŸH}Uda/h.3}a fdcހ 8Ri.HV}B/BjG`K@/biui]IĦm*f]V|m#-6LbG)p򷝅O:vtDx'֩Uw2뵤GN_Dc9NJA=..%!/uy/S3Bt`ȣ;V1xï(`epzЃ+-ȰFNʟB:Kk QpQqEpz/0i(hƟ=BE\2g CWX3s uս$7W{w񲖫wccpBrJ3Y/mϹHs'aVE $q- ǬOA"Y [&&Q>"@ S ߥ&Mkd7rǹtT 6G.4qaQ(_ڮpm62y;fYwYf|TU2~z`S'm5r[K}­@qȇtx3*?=Pg}I"D5pw0f6];d/OkY/lk< lImLtTu'8zJp)`v(.RTYAb}5 9krf($t#SC2 $v$b ckKB8 p1-_Hŕ7X/1LP%nLlFT9bv*_/oOo=]nhLDWA7x})4H99,R^ `BMdD= ,)Y6BkJpcYS{q"VꡩsC¾S.b&Oއ[79P0cfhwY*rIO`&O{\%8ʇD+7fFd Nv+a$M*C~(@]?9_yΔ[;VVLh' s+ܳqish¶gJbFɼ@B(//|<_nS c|%[Vhxa />+[pLلDʾÑhhC&!F4׵1$WGyR9:O/ snJCJ k[$7Eoo<"1~φHDsHOY*Wbpu#Q39[W9](w3QShx e.|uzEJ$SeV)&!0zcQ„n (ߘEamh O{=#gq&m0`.K<V[۬қ+fkivԦ Q?qB4qU3n}(*`t3 S3B]Zۜ;`/bk |GOlР6>vrlhZqLz8Y$KU9yDN_UoKhDD^xg:+ Jn͆[LMb8peeޖA\vNր#F,*$B"eR1it/<|N(&0БH[rskG`Kb\]w>f-NAB^ȺQLe#K4W]UT(aLe&w<d\xI UmANpk*HSkaBw͏ w_#y6GSv%5Gvpc%m*ˡ3aؽXhޡ᱈) rl\Dxq(I|z_D4FLGM<@_z4%]WW'!]y]k4g/a'6q.yݲ<Հv"E'AA 9ׅdL,+:k.$rI'K׿4×UiATBk*JRI r%^ 6`BijϠP$yf>nDa"[@1F?~?N#p拳GlnySKq'],]Cn3z/KAVBlgEP);GQSlRƩ0,Pd߳_t_p + WYme6WJ~Q¯9XM+@8vHM(]b0M&F}X :tVຯ`KΦW1s\+mTzB\LZn)NV@m9̅͝GwDO zI+Db%ވ`mnNQU9*;7m ^ %@jPOYjMsy/156ϰ8 "mlgr*l|!i 'y%/1(WOALQ:56J;$u!hduhȈ]\Y TW* 3xjy2&E2r Y endstream endobj 62 0 obj << /Type /FontDescriptor /FontName /DKIIIP+LMSans10-Bold /Flags 4 /FontBBox [-460 -297 1761 1134] /Ascent 694 /CapHeight 694 /Descent -194 /ItalicAngle 0 /StemV 136 /XHeight 458 /CharSet (/C/Eacute/I/P/a/b/c/colon/d/e/eacute/egrave/f/five/four/g/i/l/m/n/o/ocircumflex/one/p/period/q/quoteright/r/s/t/three/two/u/x/y) /FontFile 61 0 R >> endobj 63 0 obj << /Length1 1797 /Length2 2648 /Length3 0 /Length 3760 /Filter /FlateDecode >> stream xڝT 8TGJ)/ޜ(fcFc2}a1saF""dIJHDJr9s?$LaGV ǟX|bDYȡ X-?` 2@V`uA N hcqʀ1 aѨ>MSCO|B(7hȠ(\JCUat? L'f$ LŁ(dG3uƁ@Nꀙ#ف1| Uq@pVOc\ oD@6*AXh L0fs'!'@xs8L]4墨Al fQQL: FCei8hXYmD` *A|V$2|8 ' xېl\X,Ѭ@ba80g%` 346ht_HcfmdcaJ$;̋'CX-<$2(p` '[_'R AK30`.#P?1ю $F8v! ͛|3BX(f~ %! B~_b ͗4?nIO2A(( -u4N!.l0Fo??sUhlSh_ ;1tj0ȟl:$Me|ob-<!>R~ܕh_iLd dQ $8<"Nx]Q NL>mm7ͯp,F\FBD00LjЁ4dHDtk#¾wEZzr6KM+|ڨYuV5q7D"o ANk֜ʚVc^"|w\F+.z}!uΛ5"ѶkIG~P]qeVS MRLApMYWej@^JQ]OdeߏwQ \wϠ O>^-e* bpV VKx кK}mPP>z,ga2&ƌՖV\60loxWÍ&S_ i\r*H=yíF{ =KZ/Ӛqr=啋*O5"5oz' p!_uǿ8:&M+\URݡ|g@efp? s2^OyBiG{V8,+h|iDVeOI'.Jj=(t|Lod3uN!iB̯)Zf9~N4NrSsOpʅ?E.6F&4%aj>ǘ:㈓GiYA{zU_Ê+*?P2c.{Ɇ8^ Eqmv)!*T--_Mwjfl,2vf>x{B{ѱ>SIРLmB~v~ff)ȐpU] Y6sƸ?e|CjؕXv4-? Ͷァ?eK5.wh:Y7BmgbV~=VXfbw!v,kδ$ts:|)1G»NM{4RWI=4gPmj>?)ZrKڟW0!,3wY a{b-}[t#l˚&$uQ/bCTgĉG`$?=۸t C BkO}uH= ˻" zQ~<>i=!@5tv<:ќ۠Ӿ#[tГCG?Jk8t覷DGн5jg@Om5箛ۛ6,jnH;A-l*>AʮrKr/2\>4hz&sYrTHVbRFt{C:#]v=J7%f:Z%S%ʼncKd͢H^ p;|۱> endobj 65 0 obj << /Length1 2314 /Length2 21786 /Length3 0 /Length 23154 /Filter /FlateDecode >> stream xڴeP\۶6CAww44!!BpG}NW,c9WU79 1PDSp9YN,@s#' \ hl9@i#;#' '<9@htzך=r@џx*3] 3R;x8Y[`鏷{pk{7gkK)@A o.PF6f{3*P"PVPSTfx`?\DUT$U@u:ꟿ@;c9@^]'ϻw91UaU-E1f?k0\NΖ7wf9@e90218lja pw?6 bg t, 7YK3|wzM?1mep#_[#K;drq%{R  'ؿL;>Ξ.@ {?=K&',/%.re'gY''g0LEmm߉:gҀ<mmgfҚYڙ) PA&t7`a#f#~ci|{9 '?9&9+E3pKU3TmT]jjog03ۃm_]lllTwa/; TNF6tt*ZL,R ] lgn|_"?}O!?O K>&v@ggtQ+_3QZBF:c2373s<'>@F`gzw8|fN`#08F߈o#'KgM#wd h[o nɿ{0{*;',s&9?{n/hw?Ii% mPsu[`~4GؘGc{o{3M/orst?Q w?va{_? 3 *iow|wzB.{/rjX3D}ug >r-"bE>y|/qɿn]݁&s&V͡b UXPӝx8HEE0{f`+uN"bBLx}9agu.,s{%ޯbߩoɱY~xd]6b&c+Ԅuq=9Ŧ!HЁŧ/s9ƒMÅ{̜.d@g͹wcuE檔cgV3븶k;ykT')ߞAQBbq#U&Ǟ=;Cөk|pV-HgZwFjrc#|~@]46Mr(8] zM&l3gl$^52YhfQ[D _^? y]!j,(JR.lf( \~ CPhob/"jPvc)d#LrP b o웴/sHI7~cW8ݔ`~_ W7~~L kwIq֣aA-hu2/fls"Mh])0̇R{xi9KHm"Ja=mysN0I9,jhQ& ;6_M0 RF8d6oT0dLp.B,ϡH*.>7ݺsaഒjh{g;jdH m]Wc,r B]|:W5k7$rh6ȑO\&-xʝN,clqAO-j4FNklv5AI`GYK#i(Op(җWVufN>+0W+WnC(hCk&eF&5*Baڶ^z(&W2)J}Q6{%$[Kj6ͻ/HED;5g;gm' ːMu봐Ѓ)٨r2=t>&su+^Q`V \N8NYFx]ڋQ@$vmyePe}{4;7m&_4gNGJf;mDIcưc0cԭ#:G1I-$[CGxϑ&r ɚaBDBKV8Tf'p*>M!:׿(&}ܖZ4wf? NcZV Yznbgq# -gdV?}>0=NSI*6.Ij,4豿lQW_b侶']e$ex_C Dw{ 1á%ٜ=wPP6T<2J@]x3?raS^nPI;I@A|H˼aeBHX ܷysB["yst.0-:9~7+4zˍ[uӢǵqsRz5x`17Np"O7c*潉>u$9c(#9BLu4y`4ή(1>rgխaBz-<Ƈ\ƥZE,[tfl^y;WݖEnR&ݯNnzZr΋rt<ʘocC|u PH&R& K0c:[2{1hq G*aԹƷYmRBsaÐ*0+ Qiu0)'?˒ѪPXb7w!mc㵿&FzUgfLgv5Ar`6y|9i} sZ.%J(2h=bF*wCRymo]CTCs~nPdQW|[9`_vTY; E=m[DQ* Ijo@Syث%gJR.sw;#dvX $](o.'}R^$9NWɈS/|! 1EY[+8`6Cpsm`dabLnPr=jUީE'8zfqK$aSVF1}>umT۰N]xCl)pNGRo؎-3X]МW>X}U4x2d7`wREE_^MPv3aA,2=fmDc*[LMm| cq`gz뉼qzIFGW)g/ %邩Y@JDÖ Y~.I.AU--fJ->(w_~X #<)&W%Q 7(Z@Ҫ2⠛jp$>5f>Ʃ#up퇂7RJ`ADN],c)T^`USl `Qkk?xJ$Ԃ}1VGDZr $~2hWq5`;>;Ņx݄֬9v".S! Ljt!Nl ?: tOb{.dgdoџKXj~{%; /3AekKNi/Q(UxJy LZcmͫ'8Ue gYgCp:n:SPk=z׃wTp!kZ*%a2v73j:;ف<=?+ҘĜ_ %Ƭ5t рށ69ςloE;lKt3LC?\&#b6e pUTCT\kĦq/ǤBb~2MP UTkTZCNzb.UBwfLinw$…'7FfMZ4w9Z, 7{. `חj&LZSO\617gEmZ&ALϧ+4 O~$qØXɏz7D1Ϊ @Em_0QDdT]uKPS O:c£: JBҢKG Y1' Z@&lg3WԲS;?u|~ *Akj&ϴo@i~tx٠PK ;ޥ?pG˒Q)v ^7>Wcpc3$|.(U+ytNs+,e'%{v A =8^φ핪7 \҈j?zs~ѲA/t5܁n ym%|>oXbi;,%\-A YFסg㣽D!ʓdF或F19l} o brvg$u@fgi'$m{ɓ**_kqt3j9ՙn@ydk]dpҦfTR1)QD>ʹ6|u]VYɥ~,.P~1%dQ'b t4KE}sa!;>9^6 1)~mlcK=d5 ;iZ5#Ĕv}u= Do ʧ@QT~\[`%r;\ FdznWwQJp0r;^S>P[g>`j!6/-fclEBk!_=VHnmf#A?ZF3ԛӾzZZ׭8wҨzS*"sn$NeaǛɱitnvaUOs(ވ ޽4EF݇VJ'֨;tE;YcӕCbTCSq1d6rvK2&5T5j;+as\i:+7AxO0hI9S^J+潸\Wz}ۜWL6cGcYe?Hb(c%:9)-K7=]:Ho_h}ϬMS׬6<`H BсMboލzZ ֬>'|&,R!l4 9w5E> Aw_-GEk}&#CU4f߼w۞|o5-t> DKaQ_c{ 88\[Qn.zBJ,ߪD~c"7^%oO/ۻΪ?Kˣ_k.GGcHJvfn!>_V;hT d!+F>I?Ls`Pl/?Ȋ]%+.ğ,|V(p=%a[xwӰʬHnQ+'wTv !2nQ{Ȇz]&=iq lySk )De{!7 ;>6*L=,6P9՗;gp*Jg#”?W\6$^^;)(M'Mdδoqپw'yuiY}mdtk̃} a.K225JɂŵK* zB&v4c/:C9seMJJ yd)LL5chuNo?:$I˖yE잾{{:8h\ɻ@xjT}j[%TI Vm`FFx}ђ rwTΟSp'@rU{*Ul( ~Rd^ #$tfEįd1d㍛-GV"4( ;&pE MSh\S\*.IH]H&D_2V| tic -].ͨe`{3`ϕ#j&YĒܰ3a'L)ZkzڋbXr.8=Ys'쮘Fa]tٷ@TW߫m?}*[apƨHg- /gm~¦:ۊUvdt>#X%I:c ,N'O$l8YuFb} l=~4ja7i$O _#j|h%sj3 ` ;q:,. I%܅e P0O'{~Ά+ȸ,›BKK; x 3?]9)tAtfWnta@be]Y_y:6N}!-Ldsz Lo*7 Q AnO[Qd0?fӔu:gv R C4ϲ8gup11Qֻ #Aׯm*q o29bs, c<;tY βldvc<%鑂Bʔ ?eӛ7)G[Db[7b;i[rmQjq1>Bl~'ĉtl?ԻOn:UI; WWHH,<:t% l⛁zn-2EDcmIǷ(OUeP٤sXq B >]*{G$|Hs9+Ǹf_Z"-8?!h$1#OJ*0CYn>: rY@iuH sl y"xuL~*4!ieωvTWn۶ZCxpY V5ԛ!->hQkML?a6%}U6esc'}mEYCdEeܲ2ɔ5BW[Ⱛ2SiaR570°ճ)V[yQPL|I%LiIE;V|,՗AE ԟDz.W)-?(+4/5KڏՀo3oBH,ݠv?3O7wmؕ!N6hnzo 2vo7 -GgXgԆ&6PFYL~}2፼Z~k&JX}p%=IaNf5"''`jޛID@9~EI+0 Ѵ {Ϛ]Zx_yJG/]ϛo~fHɹi^bWPDe]clt닠W=)w4-x֢;[d^ >B49ԛ1y/O3aLIMKbF $ p1##4Eh 4nRn98JruV}Hs[GqQ%%ƦIhvY4yz}rUĨVzHOEAn0ȠUGz%lqn^J."\`iѯ$]-=af 3IYȬRh> o)74~ʨm!Ɖ#5bϜ87ѕml'P =.?0>[ewc"R y"id}/GwY"æn㺛О6Ԅ6J"C[ғr0Ko&--s%:[ֿlsa?bP ̀{=Qٙ,W?jq҆|rϩ+| o)7AF1532|~âq!Z1<h1@d+Fli">j+mg']3=5ϔm%NyCO'] n4YuYe[tn&z%JwSyxD YRM,F[?Z]yQh rLx~(K(^,- 3ΔOepji]gX]_IOl5_͂eSc7-0&M?-F_QӺmLQniKl tOCDnxeqalV6uNV0^o yLs ^.nY(Ӱ^S 6ڻ4Dc/ښx*H뭑uhb([޾~y5b" {k6?D%<A>\߉O0nܬ@6'6yU .$-OIص~'*_SO!yv^~;H,kU>SɒF ζR?7Ğl>{}5k"e.vg M+/gId|XJvNhLdSuC2rka!H1 6hO Y'8lW:>V;\#>Uo.Cz;ízd3%™dffZ'bUt |.G814KDx|3hb %}`Y3n`:K?Qn*%`&$<"s5T`=ʂ,eGX(B/O|GȬ_ꄱm*\W=[, ע6G5  6Xڦ$˧/_˖Zk۟_jΖ̤Fa3l#kzfa}KYGงK[h}7Nj.'6 AV@)d^x=jOd_=JvђRbk*zǺvޮƶ!D ) HIXc|?tPfWgm.6|c`lbcG݋VZo'H}kw6FFp(:ȪNwt]mE%ps 8ag)Yt%S(ecr ZGtvL4))d.]`Kj!񭓄@'ka6f@M0%H '=) aGv"?9*Og؀Ve6`}<߅aV,g">k4@ 26j)@:L ]cM)Y C+ $O?I+K,/W3+cfywxYqo8ƺEj| Pf#P.djB5eC,|-1{<+Fe).GXt'\?(kICuyW8h7'@Ƴɦ*T^a620b7穧!ź6\*yE 6U4K)٠xGW*sG-^~ߌ@Y :7"uA Uw}N%Rg[|w"ZH)GE_/;Up>泾ynFlY\15/56zyֵncVu)#C]0OIрV.Ð=VvYXaDŽUF/WK'`|YqN Dc aPV gIdE٩7gqȰC-hв0$2.RMܞؖS4%L/VԦs[O }J"g۽/L"D#Xj!A^Lp2xvTJXW3 JE[Vz-~ASȱ;wʪxFq#ZxX;hP+$7n]2'c| oa~G91'|lE\h95b7+- .QKf+&gR6B=%:΁(G[kb6?:0n a¸ rx|.G!ml_0Erݺhj5$dif# oW`z;47l[-XWJ7ւXM,hb+n\qzCmq$#Mm * /NiԺ_WzeHdEPNjTGt-IF,>S-t/sryN]$T gZc1qtɡUNz5 H%J"URkn4Oj}Khr;Aoc0 +Ңq2Ҝ{'VPA\TqHD  j$")S}%:rD6cn:o = ܼ?Z5;MG ;{iI\GuD(sHq>>bb̗n=c1!,s,jRJϗd\_r3HmJM0S^Zp[)Ic^Ƙ1+?yк=/4 [|'fe1E&DQ=](|ʈs(ϵLB,f,j49`F¤G Sx0죠xۛ5@Y*D0g`YL~AdVGՉ.c›ˑ:f(ڗq* \ZbWGR?o,Tl𴆘c̷Qnc )1 D&52!] |K][I٠<vM*P8TJ2}s G?=DXo`QuBF+SO7DmKLQ(*L}?0}> ׷ΞX釥N8ri#gt ֮Ti:b9Nmȝ/ir~8:@Tcdw+A;&IntO^‡_9]C'ǿJ g*cg'n^Uei 0=w6. 4klh1+6et'i;d:![oiG,zbd1a[ nx,ٙD@&Z= HԌD]kFJd=<4(-Ω-]KZKUY)[|/+5Cn^ !}Msmg q -!_z{?NT:Wʼn.žY`4=ܮ3 #TqWGʱ_ Mx~duqMghU`q6mWKhlU6ԾpB^Nd}Rlq'G$j0V"-_1+ך!Dz)tyʛa2qEaΗ] y}R.cu og¢;hwf#yE73h)ŬSUmv1; ,pMT yT`VAAE RǮɊ=G5O>{MsD*0*c^M2}iT~լrs ɘ?z6rl)*c*O$=80"k F$owtX2R#:a DhJN"@}I}vW($6@W-@ { ^*8`]#]n2Nh&q h S P~ȹ8,Ƿ}hɹLl}GCHZ=6{ܲe"#trڐ$ ; #]$NR7oMcv$ )oe" R_ayYNn~P5`#2E]vegц;fv!)#PpAvVhAڵd#O)5vT(gDe$ܿ2#q@(ďvrrw}I3Xֵ ;c:+ıyI?sƗ4c_ \ʹu2eVgg)|ҚY&Sgŕ\qOP.هEhtOX]?C 2 _]zS(S8H{Q߀ncИ(EWbp)g +-d/8aƣ'H|Ki;~F3^ a'mmLotC'4 K)5s aIVf,ˍ3')jb1kl?G(hqJ}Am*Q:SAa, fͽgc~ע{"Up+Wg=-g+TʍrbjNsrP(8 ~uAMHo./3tݭޫaal%ĸ9&:&D A4#>1C}ȼn`,:7) }GbLȚ$f3LJ>X?"a'{ڃT[1waN?b&LaFp G^9s~ƣK ͔?$>Hv [謡cQi7T\v99_A} $wU p^*0w143R >Z(Ϟjf JHԱgB2`ġsC¾S.X&l,J yAz@ud ;0^.N ',LK׋ia}lrj5bO@6 [PE8$}I+иyOr)4Jj2}͎tKWvټTARlt=NJ:F,Y9 [o0}#ogD<3{(|`>%p!U4 83UU "%{EZ oIx3yF  ]#dאV d<ـ݋;x`.™ic~6PgYa^y.Nuwsۂ>p( pE3H{%Xn`w}ғ0j+Yi+!o.&;/YfYIZV9ڠt24EeVgZ%z)C|:sa>ۋrgY0|>rGo@- U^ {0ƅh\y5e(ҎP{UEd^هmc.fGO=_G39!M(Dɒ-W <{d*U@n6#³04 xGRγuSLnMOP?jv\ ]4.E١)O ZrX|Thzۻb hɑo1y<_8[ڹZW4ldqA JhS#۹APD DBL4YcÐiq㳍obgU呃k*OVհ]@~-3` qFɤ/<~!o0ktU;[&@@ nj /rX 6VbD|&łzw\#s1WȜy?CZWJvVȵxdU,7ZFm@ &5kǍ'kiPm#zV 8`a&#V}!p [!m 3w# :l6$C^wW"!heԎ a֚Lʈ"FGX._k;)WQ U$NB!A;?(q;Q~XTM &EvI5465\"vؽnV<͉l ޵Ɍ^-xĄ9)Cz- 6OR/ʢ]rX 2Hiݔ8i_*+S;Vd|ёLu cyJ$/Ӹsd͸Y52#&.`v_{(˫PqL6,"OpXY]zHYIH@GѶEAcUs } [Z7G] m0ˡL#N;ĈXM^bj|Lu/ziE.]2,^B?F+(-r\ ¶^?NH3nA.M)m=WfqH 14D`9&H}[Y;!]+$2O]r(nL;2Xz4OnNnlTt(AӔi9"4]H5nL\Sk،q$Rg3~t_lgb-2RTR)4nA9hڣᓿIS-\A$2Y}[C&-5i\b%FGj \0; hB !0 oۃn!'8?ˬ|܄̩yQUJъrP4}:Ff=0-`3tO"5o8E'mPTkҟ~=7fcmga/h"5 (.J[KΫ[)Ko5'”In qzlv8A*1;rfO_#1wLjܧx /WPC=sB<}/OJeyg?l(FYp.QL5͝xvVH_aƨr96y+Ž "u*o@BiH1S'.^JcM]yxxEO"uc쒸KE<3Y{C 1b K{/~=f wl)3&8&Yz5dC乍d.ᔈ82-X$^etJ_0z t,1Ϩ&O*h<$!)Q`CÛ2mnǑʏ#Lx 4ԏphF}n8ZٕG1F>N(Lخ6?f zM1$2:@wL4#qKP#Az pk,CGxk8xc]D HΉvxߞ`JmJ34 4;vr .KXITl=q[ԭIR( endstream endobj 66 0 obj << /Type /FontDescriptor /FontName /JGKJDV+LMTypewriter12-Regular /Flags 4 /FontBBox [-444 -311 715 1019] /Ascent 619 /CapHeight 619 /Descent -222 /ItalicAngle 0 /StemV 65 /XHeight 431 /CharSet (/A/B/C/a/asterisk/b/bracketleft/bracketright/c/colon/comma/e/equal/f/g/hyphen/i/k/l/less/m/n/o/one/p/parenleft/parenright/period/plus/quotedbl/r/s/slash/t/three/two/u/x/y/zero) /FontFile 65 0 R >> endobj 67 0 obj << /Length1 801 /Length2 1236 /Length3 0 /Length 1785 /Filter /FlateDecode >> stream xڭR}^ u.iLln33g̜qfM ˴rK iC7*j#Mb[ˆ.Uw܃ϵs=}ڂD"lD=`y1$bO&X[Qˆh(\ ɮd+FaB6"x8PB8Zq6H)PV yChۮO)+ @hĵ'H6 +|D<2RJps nr\ '=ZOΐ ~_o`koB  W}x@ZKqYx@!w4Ua}a~" E`LjLoo=DƁN[A(89 SQdqJ'v!RˤrPoMWRzBq"#f#pPIWć{CI$2ej^շ>nJjbw] _VltLcl[{SIfKbCƛܢ>X\Ԙ 9{%\Ly|w~IͿg*`y{;^g ⣶δpBvm"Р(Kx Rbҵ"-Ke>:} #¦)BsyjyK4xqk|(_/=/dQMCʮM9Nݖఞ 쯚i,waOl٬"*kz^1 YԅIf&gY::moV?Sp{tԷiA2꡽TZ2/ӘFf#ц-*͖aWl\erm aaIYH WbQ[z-gNRܷ&('Ȗz_R ¡^7H"}oRlwf[l5gE\LFE'2c%z[sӘ> endobj 28 0 obj << /Type /Encoding /Differences [21/endash 27/ff/fi 30/ffi 33/exclam/quotedbl 39/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five 58/colon/semicolon/less/equal 65/A/B/C/D/E/F/G 73/I 76/L 78/N/O/P 83/S 85/U 91/bracketleft 93/bracketright 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v 120/x/y 201/Eacute 224/agrave 232/egrave/eacute/ecircumflex 244/ocircumflex] >> endobj 30 0 obj << /Type /Encoding /Differences [32/parenleftBigg/parenrightBigg 48/parenlefttp/parenrighttp 56/bracelefttp 58/braceleftbt 60/braceleftmid 62/braceex 64/parenleftbt/parenrightbt] >> endobj 37 0 obj << /Type /Encoding /Differences [59/comma/less 62/greater 65/A/B/C 76/L 97/a/b/c/d 102/f 107/k 120/x/y] >> endobj 42 0 obj << /Type /Encoding /Differences [0/minus 44/arrowdblboth 48/prime 50/element 54/negationslash 56/universal 67/C] >> endobj 33 0 obj << /Type /Encoding /Differences [40/parenleft/parenright 43/plus 48/zero/one/two/three/four 54/six 59/semicolon 61/equal 91/bracketleft 93/bracketright] >> endobj 10 0 obj << /Type /Font /Subtype /Type1 /BaseFont /SNXTLX+LMRoman10-Regular /FontDescriptor 54 0 R /FirstChar 39 /LastChar 121 /Widths 39 0 R /Encoding 28 0 R >> endobj 6 0 obj << /Type /Font /Subtype /Type1 /BaseFont /OUBVMG+LMRoman12-Regular /FontDescriptor 56 0 R /FirstChar 21 /LastChar 244 /Widths 44 0 R /Encoding 28 0 R >> endobj 5 0 obj << /Type /Font /Subtype /Type1 /BaseFont /FVBGOJ+LMRoman17-Regular /FontDescriptor 58 0 R /FirstChar 48 /LastChar 117 /Widths 45 0 R /Encoding 28 0 R >> endobj 8 0 obj << /Type /Font /Subtype /Type1 /BaseFont /QIPCQJ+LMRoman12-Italic /FontDescriptor 60 0 R /FirstChar 99 /LastChar 116 /Widths 41 0 R /Encoding 28 0 R >> endobj 4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /DKIIIP+LMSans10-Bold /FontDescriptor 62 0 R /FirstChar 39 /LastChar 244 /Widths 46 0 R /Encoding 28 0 R >> endobj 24 0 obj << /Type /Font /Subtype /Type1 /BaseFont /JGKJDV+LMTypewriter12-Regular /FontDescriptor 66 0 R /FirstChar 34 /LastChar 121 /Widths 29 0 R /Encoding 28 0 R >> endobj 20 0 obj << /Type /Font /Subtype /Type1 /BaseFont /KJPKVC+LMMathExtension10-Regular /FontDescriptor 48 0 R /FirstChar 32 /LastChar 65 /Widths 31 0 R /Encoding 30 0 R >> endobj 11 0 obj << /Type /Font /Subtype /Type1 /BaseFont /WOBYWM+LMMathItalic10-Italic /FontDescriptor 50 0 R /FirstChar 65 /LastChar 121 /Widths 38 0 R /Encoding 37 0 R >> endobj 9 0 obj << /Type /Font /Subtype /Type1 /BaseFont /PDGVIO+LMMathItalic12-Italic /FontDescriptor 52 0 R /FirstChar 59 /LastChar 121 /Widths 40 0 R /Encoding 37 0 R >> endobj 7 0 obj << /Type /Font /Subtype /Type1 /BaseFont /QHYENV+LMMathSymbols10-Italic /FontDescriptor 64 0 R /FirstChar 0 /LastChar 67 /Widths 43 0 R /Encoding 42 0 R >> endobj 19 0 obj << /Type /Font /Subtype /Type1 /BaseFont /QDTWCG+MSBM10 /FontDescriptor 68 0 R /FirstChar 82 /LastChar 82 /Widths 32 0 R >> endobj 13 0 obj << /Type /Font /Subtype /Type1 /BaseFont /SNXTLX+LMRoman10-Regular /FontDescriptor 54 0 R /FirstChar 50 /LastChar 93 /Widths 35 0 R /Encoding 33 0 R >> endobj 12 0 obj << /Type /Font /Subtype /Type1 /BaseFont /OUBVMG+LMRoman12-Regular /FontDescriptor 56 0 R /FirstChar 40 /LastChar 93 /Widths 36 0 R /Encoding 33 0 R >> endobj 18 0 obj << /Type /Font /Subtype /Type1 /BaseFont /FVBGOJ+LMRoman17-Regular /FontDescriptor 58 0 R /FirstChar 40 /LastChar 59 /Widths 34 0 R /Encoding 33 0 R >> endobj 14 0 obj << /Type /Pages /Count 4 /Kids [2 0 R 16 0 R 22 0 R 26 0 R] >> endobj 69 0 obj << /Type /Catalog /Pages 14 0 R >> endobj 70 0 obj << /Producer (pdfTeX-1.40.3) /Creator (TeX) /CreationDate (D:20100305134044+01'00') /ModDate (D:20100305134044+01'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX using libpoppler, Version 3.141592-1.40.3-2.2 (Web2C 7.5.6) kpathsea version 3.5.6) >> endobj xref 0 71 0000000000 65535 f 0000002136 00000 n 0000002024 00000 n 0000000015 00000 n 0000176779 00000 n 0000176444 00000 n 0000176276 00000 n 0000177638 00000 n 0000176612 00000 n 0000177466 00000 n 0000176107 00000 n 0000177293 00000 n 0000178117 00000 n 0000177949 00000 n 0000178453 00000 n 0000004235 00000 n 0000004120 00000 n 0000002307 00000 n 0000178285 00000 n 0000177809 00000 n 0000177117 00000 n 0000006815 00000 n 0000006700 00000 n 0000004397 00000 n 0000176943 00000 n 0000007835 00000 n 0000007720 00000 n 0000006989 00000 n 0000175050 00000 n 0000007916 00000 n 0000175482 00000 n 0000008458 00000 n 0000008674 00000 n 0000175935 00000 n 0000008698 00000 n 0000008836 00000 n 0000009085 00000 n 0000175680 00000 n 0000009392 00000 n 0000009742 00000 n 0000010209 00000 n 0000010591 00000 n 0000175803 00000 n 0000010683 00000 n 0000011071 00000 n 0000012267 00000 n 0000012699 00000 n 0000013880 00000 n 0000018072 00000 n 0000018429 00000 n 0000022599 00000 n 0000022842 00000 n 0000028876 00000 n 0000029152 00000 n 0000054411 00000 n 0000054744 00000 n 0000083806 00000 n 0000084306 00000 n 0000107648 00000 n 0000107952 00000 n 0000119837 00000 n 0000120079 00000 n 0000144725 00000 n 0000145078 00000 n 0000148957 00000 n 0000149250 00000 n 0000172524 00000 n 0000172932 00000 n 0000174835 00000 n 0000178532 00000 n 0000178583 00000 n trailer << /Size 71 /Root 69 0 R /Info 70 0 R /ID [<0E79A221EF4F87EC517C1CC2D11981E0> <0E79A221EF4F87EC517C1CC2D11981E0>] >> startxref 178854 %%EOF wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/projete.odt0000644000175000017500000032535512014170666025744 0ustar georgeskgeorgeskPK$8^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK$8Configurations2/statusbar/PK$8'Configurations2/accelerator/current.xmlPKPK$8Configurations2/floater/PK$8Configurations2/popupmenu/PK$8Configurations2/progressbar/PK$8Configurations2/menubar/PK$8Configurations2/toolbar/PK$8Configurations2/images/Bitmaps/PK$8&:­­-Pictures/100000000000069800000489822DCE1E.pngPNG  IHDRCЈ pHYs.#.#x?vIDATxOL}?1Mlbc&ĝlX6!& Bb eӒ[6-V6%ZjբUTQT@%D8́8asS?03yD"/0|7牼4@@@@@@@@@@@@@@@@@@@@@@,..&IF)FKKKj +R233TP$hhh(( 4Hooo0+s{{[]D2۷#T(TD|>?==&''٬@#kuu3 \H;;;y]]]+w& fyhÇZ5P˼\.^p-"jWWW0H&JW/rRT, fyZՋyE2ROh42XjD*D"jR TTjpp0uuu)"\ȅ>;jW/Rc[捎f2Ka[[[y}}} !R#[Eѕ5|>P˼U(RS2(Rg fyߩPz=Q:\]]Ub]ϕ秧C-ݻe(Rg\^^9::RkZ2e^__r@u"Lftt4EE*D.OmW|O.SweP˼t:P|P˼M Ed26x<^(+re_P(̄1-\+zkkk===,/%I#g\L&y'r-_5͎i\׾w^4 fy###FB":;;ERTy˼D"al$妦B-fgg9x<j7::d Dh׻C-m.hL&C-EݻwHbeH:6fH#NOOO0+N@4 i@4!rP˼|>ohf9Е.-hO&:֝P˼m@ˋ4ݾ};vqq@"MwąBaff&MNNNZUI{}}=2opp0LQZRy=LM @4g26x<^( -$'P(fggCYޝ;w٬eDZ4Dggge5DZLC-:;;׍1- J'Bl߿4M-ztP722d 6+ҒgbX244H`0ˋF+++fisrw m2i3|a4 fyZ\"pۡy{{{fiLRy^4HjP{neT6:E[ z)"mx;;;,NV=Ohdd$EGyAИ"m{|~jj*2off~/ Muu+ i JD {zzY^WW8y'NZ=|P((@Bavv6MMMr9 >byy3  Kj2\#A)rDhy\Ac?>>V ,kkk]]],O< d2n-b2oaaAe2J=xe^6U 666Y^,S. bR@0\^^V. Nm7==K"ȫB4 fyoN*eUo{{;2gkkKe;A^M۷ClxPPHW|>P755%yj722rtt2ԅ nvvvz{{Y^wwP;A^=e2P˼h42HWgBazz:2orr2)U]P˼d22TGwYvwwY^WWƆPA%fcccmBAqAޥ{A(d2*@yWa}}+ ]X,P Ad2omyWP(ΆQ-8 $y*G2 +2]l6;11Q2/)!kFY@:VyokkG< kLfhh(6 }A^ӡ,ojjJ<5x<^2/L @5`Y|3A^#:::2A^rw eyZ'A^C[ZZ2yoww'2ommMeڊ d2-ڙ 9 P7::dJe^,2~, []]U&k>ǣmZ0A^yC666Y^ooΎA^sK&Ztj˼L&8KGjWWW0K&*py࠼eÇm.{ny˼l6WTYl'B@[bd2پS OB@Mvvvz{{NN@ty˼vy-P|~zz:2obbZ東ZvrѣGyT]3:AP#jR OTG@e26{ V*y@- _Pw^(˻sqk^Ey< .zggg0K&-x 'Q핷K$vU I\le\˴̻:;tBa~~>华f2Vy'pEDwww0k'R@d \T*łY^4]YYiG\YW*ݹs'ݻM2O\Y,ohh(N7@ \`ݽLOy  kL&B-bma,()שy[ +&E-,,KR{#"B!Z٠ r"ae^Çp z"H. eylp<rB4mؖy<ZhDݡyD/ey<lph{e^s7AP!+ϗoyWCG{ae^oomEG [__W}hcccm* AMP(ܻw/卌+y4`ŒɤW$r5>ҡh42u$ȃ*fdFFFBlgffũE3< J h^ h42/ɨ @ypWԶY^oo9<ɤm=.{֫R h=+++ o)h)VWWUh1ۅ =8TZÇA|A|f ݻw?0??GV?H$v~+J?d4 TZO<Dpjx2 OV)\XXXppoo/[|ޠ yhWz.cE^Spy88;;e"nuPUkgp0rpjjqK5 TZ%ݵP(*D"qa<|$ؚ?N¯rOP\]0(+ yhWzG݋y`tydNbT*uUmmmX˥Tkvv$ ^tCqm(Dgn~*4==z&//af9|>_ʤBҚD׸xPAZ!2A]J@Kh,sEyT͋Yl086[/#Ujm(m^*x‘ TZ>:{2X,G?eA4+{7/XhȠE `)@;;u`uݪ^*x5zkkk7nuﮊPGv>VSZ3xʻ y8xnsFb ?yP7+++[n޼L&Qbubq/⥧@3A=/RhO @:n$nU|=K5p7v޽FST^ZZ*^㋋ jT<zddD<E0b񠞃5n(۠p4]]]ccc+++Wx?*.^>PwB'믿n* 匞X56WXA K\s\WW׹Fo)\~:cg_qUmІFVK.Pl=99Y|eٺI2ЕR@/ȃKU߼y33ƍZ 7776811q~JI8XBkVR^NÇwwwz> F"|$Y4 8B/|B;1pjUŃm###'8)K5\YY ł67ꂼ7&ɲ/E"H"Hg]9A\-pcAXz`4(ԓdr\َکVth~yypES[moo+JkOP` Hx+ֶ熪sss}|$R83+-633-A\[n_^^Vz .nCeUqC- kllѣG&GGG͚*NNަYҡ= JrzԖy@:Vť k`)Оyp=vww?C?_x-E{bAkJ{V(qI&^Іypm/`/ήFpʻ O3ZUw6Zzb> : ~g{ZВP\yP<_C7_zE%hC<~-nݺe<TŃ>ַA^ ;fqmH awwST "hv>~y],㻻n)~(,ry I J<&wNq~W?::F'D  >\NX|T*e@= q~|W2WJkI:;;GGGWWWt077Wk1жyDqF|ӟ2*nxxxzzzuu5J |L6\whBA888xWB7n+T⣷x酅ړ_{_dOx'j %3A4l6{j˼{i:5]DF?lޔF777#9A4B0;;[37ްvүVwU(~ﭱoA4y===SVwR(qq1htff_/--)4L&366vFHhԏ޿8WIDM)MNNX={vo߾}pp@9A4nW;FxqJrt8 Fy{ܖ^6=uLSWEB@9A4?$X,V-,766 WvG]?z+-GyC޽OQR Zʍ7B3W_}U<>V8W.Ԡ$ŧ:ur^nqqQفAD"SO&XlooOq ܻw*9ByFR͛7C7nhUcF϶WCA{rof,H=̜xo. 'fz $_\\,oke2Mlnnq;mɣGNuu4.@ ?;;;/BhK/߯8L&s)^___}Аys/~-JBvxx800P 76NG*r@  Z:颳䙗xh4ZZ (rƍsX=Wnnn=I24"@ 8ny˼^<d>[cWCAOH$ P"Ndvtt,//+-Up~]govdw2vV}(X[[+oꫯi?Eӷ÷jٟ;88hAp_~94xgdbEӷ3xB` <"7'x\qhIBall4}'*bX)z54X'y1?'of6UZ\p_VH$"5K&8!.f}}g)c*RZjp;55unmmq[C?r߹s /H O~;677L&[br\飩Tj``Od2h F8!Q|.d{@Sf)nwww睈FǥY^^AN?-g|;2511[ІgNOO?:<<\ oppР'y@MDggghqM-hFYϟ;.m=88(.jOؔn 9APW^)m+Mdcc#ݹs-NZW#&N:8>>/p!M!LoG*B}]jcy@Wǯ~LFqhd\.x#ڮmoo/RWD Z__gCӎ_|qwwWqhXF-ϖN֖ݻ7===>>Wuc/.J} _<:::~_* (dnnP.K{{{DΝ;}n``cLNN&}Apw__i@CY__&hcccW3e)}ѥD:L$LMM޽{H ˳XG/Z K՞+NLLښ*!.'>P /h+ åijggޕ}5MM\/~-󖗗k477^W)}t:m8JK~y?Zw޽i'F[WcmmM<fpj:22rQQwwwBTN4-ok@2Loooi>s7(IR2R@y@#J$ZPB022P7()*'T*g>z)-8lpJV~y˼G hwwwK:99i muuƍ-x<>|x}}t7򀦷;;,Ύ .gr "p!<dٯ|+,޳%龾ܯ{~~t;%ZDPxw[MNNr9h۷KSh4,1Km pQ<=S,֭[dRqZLp,G5G*2VS{g,..gzx~``t|h"ZP&y7[yMmkk+xygg.Jx<|M-T& &:|>_:X,f@,Htvv7ojtpp^tStbL-.Jݺu+=SDB7XZZjS+Ԕ1.J|>oFq-j5t3NDfgg +pQ<]2_fR~ (.J>n*Rt||<00Puww6ϗNduu%˩-h"PFS(FFFӶETީh<~e&ݣЪŦ>ҹTAy_Wj~L&ckgg^eypdwwwiZ[vb wm-N-d .Z`#j>(Uq-ݻ{7Qq6<<#N W**T 4PAY^^.o/}I+8Ih <V-^|d28W#HDdlrre6IOt^ CL^ eyKKKpRTlenaatjxpU|D>˿yy'b t+|얗8PA)VWWzP788e%NZZ쀫!8/}x<t t Ӫ-]+J)/,,w~~k ͛7CY^4T3 .fggKeTGPBo~eLOONpSSSSN$^@uy5Y[[ w9}巇T!ô6zoo+ V&/'j\ NTGP7-ݻepsl6`z=U2 Ҟ7(IRsz&_~9hhvv*Y:q/ j8YXXPRA z,//GP799eФT[[[+P AI&/b({WWR .%|svvVAZS& eyhl68ɒRe\|>Z .033S4_-oi4Ƣ 'P/ykaa!8EQS핪4<< @y+;k-Kq벾.:44TBMMM)P#A@{'Bq޽{TznpRfgg  lll?cLP.F:;; xTj$hdW^ ey\T$HI[]]UFW%R R~,y@u2LF ]]]r!˥NOO+P;A@gV2b}}]M.* j'h) -xl68@ TQq&U-peeEA Z'>Pw֭t:8VWWhiqB,UjTv<f/'|׿g,FFFjåJ @y-wމyT8U6/MѨ-PA ZʓO>؟Bn`ݞP(~p0??&d2bvww+P<֗fQhss3x;wEJP.yw-o4\:fR=C?{g} < # k%Ř`RDbrfR'$`R38܃,9Zu 0Ɉ6sls'2ʍ317?Gn[_>yD?gw_?2z?ڷ|Y<.rĉ#ٳ,NZ-͆d2\0Ճ X<2??ƺ[J%@~Wpi|> Xp@բmr@բZ^^c]^:62@R><<XXEVs=YarrR8|۷o_ŲFFF"ւ"wsź}sjU8Xz}tt4|NRgϞKd0yEyyۑyp ;&HaJE jQp?ĺu?$MPHRKX paHh_92//7䘟 _wa FӔJ0l6+`)`Ǐuy~{V\RpQ*4333a###V"1??m۶XwW -O:}i4SPy\j:22:|pI0\.'`)$=ʑy'bd4ɓ'prÂ@UR t:򆇇 fZXXpEˍWm@U`u9Djm6#9p#333V"QV~/~}9"!lx!ԩRx≞XCA X#GNNNʤ#G| .Elfffƍ.#p`՝:uQTkd.ET*uyW^yܜp`---m޼9|ݾ}.Z]Px9٬@ե`MAo]92ꯄVر#|MάbxEFGG.Ek駟y{o㍇pI5BxE.Ek̙36luy_}R\\N&I]`(Xs7pC۴iә3g.FFFpuy4Co .`b:449 /MPy4ω'V̻;TV=baaA,1<<^YK@S5b]޵^(xW8qB&244^r,`u)h;wƺt:P8r\srrR&I&@AX]<Z^?+G}SNEuET*e2N@˼ +GݻZ J͛×;vxLe Q6yܖ-[b]5\>X8pNVpd*G&:EsrwƺF_% Lizz:LV"D׿rd<`]رc\.'y$ /nݺX722RՄ@w{n&Dx:E m۶XuƯ nSVo !p*V"djsO}@W _ Sԙ3gdp%s y$O?md,E_}Y$_& /YRy$TXuywuytT*߿_eikrUTvʹ9Щb\ڵZ-o Z&y$Zuy}}}w@YyE\K[ /@ }Z8#GD_q:6Ky~===:/-733g_+NRq}{[92odddyyY8$VlLD&YZZKT*K)`(B[nuy^{mׅ@耈t:mZ+^akD@cX72:p@رc2ikb1cccֈ"|eȼ'x!'NVՄ@;v,͆RX#<:_\^92w~wfff@4^qtk.a4+Ap=ĺgyR="8|А@#o U;"VUt|>^ƏEJ\L&酅t\.^ikG@Wc]M7TTũ###WSN9hEݮR޽; "<*޶T* X;<'=ʑyw$9}ttnȈ /͆W<kGwu{wbx<՗yJ֭[c]Ν;]-//GrE Z XS< jwwc]e]K/ w0::}pzi(OR XS<x=XOOO;zdx[ї\.'.1;;^1kJo_uyZM8D8q]P((pQo5??yXm۶r,Ldծ255^|>/`M)To=mذ<~ڵ+zܜXJ. @PyvdޗertĚ@5rԩ'ι뮻Z.e҅J%kJ\.uyW_}‚pͩSb\A .fe` kMVu].<= ݬq ֚".__# wݻwG0kA!kM̙3+GzFtÇ;sr5E\Rm۶Xe#:ѣGpAhvv6\ ccc֚".^^g?^xtgF޽{wZK7+ zyp&''߳yRl߾DpI4~,`)`9sfӦM.o3{FobQ,r9fRjrK|7:{st@3)`{wȼ|;h_>.x[###(J֚"Vً/Jbu?y#Q쀋l6 B 4&P+JW]uU۹soKZp188 BђW@X^^馛b]ƍ_}Uz><< ~  hE;Çw1$?ONNʄR.aM/[nȼ XсF333]!@kn~~>ĺk\.  p( "<hZwXxP8RT200P*J|'&S@E$KZ7ĺ+Kյ~\p&&&355%9y8zK_Ryg*b\<2ႌP(hE$3<ۻrd)hff&zpb4Mfgg4"knnZ92oyyY8T*m޼9... 544r,9yhj5ƺ7P.N>-.B:W':Q@￿'62駟K=*.BR WQ&4<h?O-2Jp!eYM1???00b\ٳ_p))\KFRĦ;5_'?pd@T P(i||\ @(ÇF_R###{ɓ'¥rrFmG?Ѻub۷'bprኚ4<hW Om۶zK8LOOGocccz],\B UX4<hcZm޽+G+(Jq׮]ۦXtk hE??uy===O=dnVTpZfj4MNϯO}jyyY8@w T2apuIh&7Ri֭.+|7tȑ#2aA.L&#y9jmRBq o`pu  h&EtƛՕ#&&&A쀋۷/--U4;;4":+qXя~<A{788 VWP=@fR@gT*c]ަM^ujћ^X njj*\cI+;3}yb\hXX#\.\fSSSIfOOO72$gΜpO]5x '*<|3336luypиm߾=zER kdtt4\lI]v׮]./NkZVf-JVXS2Lݢ^8p |4}lf2Lޤ4t}}}:o߾}Ah;O>dlLXSpe2M377wǺk歷FΞ==b޽ZM,r.aMnTTX~'͛\8& Wؘ@&S@ʑyO #$޵=*cjj*\x|^ @)ەJ*e2E +OELڛd<c]^(@ΦN{n\L+Lz~}g{L8@B,-- 7͛7W*L ,JL>r@AиENЋ#|<7[ƺ+rnnN8@ GKG ͗dέt:- y@\V;b]޺uZ8@K;v,zG:pLh[144$ w!o'y^L (VU|J%:wB @)^c]7l*4juǎ-hpppqqQ,l4"x'KKKc]ަMQ8ZCBpAkp)NLLh>E. {c]^__߷mkGy|IBSSSj|<|߽@8ZpgF'599.ˢ@S7\92oΝFT*m޼$D&Ţ@SR|^92W_Z]›.HYͧ.؃>?Vž}wӧO˄$f4:<bLOORأy`dpq\LL&\ZHo-[b]АOX,F`dd$Dd2ZB\һ[o5.ÛɎ;BBpq h Epy䑕#~i/ݻwGJa$JcAsttT @K(U/_>hw]ՄGo BA&$JcMs||\ @K(Q*Sٺukׅ#GDo\N&$Ԕ% "X5Au].oF`ff$_. WԔ@POOc#}Q+U*LfiiI,$~-V+aÆXwDAw^\FGGõjD\~{߻rdps&&&iXl6\8-JYnɓ';v,zs I=Щ'%yzgb{޽{pAA(Uy{W7n>OUU@pXHrl6+Uy@޷Cuy\s‚p>JΜ9#nvv6\ZE4I;bjjJ&$_P@VQM|/Vw}cA78qD*rm'χ6 hEl!zFAt:={8v¥"hJ8mڴ_tl6%.[("h/}K.矗 t\EF xffF @(VzgV>WՄ#vő#GdB{>OZ*"h7|sͱ.}{_RtǏGwؘmh;f;-ZZtM.oƍ?OmT*EfAt:-y@R<===:wMU*>H;jh-y@?_92瘠ݻ7O:%LGFF"H-[ĺn\. ȓO>ōʄ6U(C"H XN ©SRT3|>.\.'y@B=+G=cHpر\.|>/y@r ֭uyw}R=w :x @Rm۶Xd.Hn=}LU=33#y@X5аnݺ'DygtbbB&tl6jOSO=O>id$DXp122b{2L B<m?b}_^^@ At@R\.oٲ%  ZV޽;zXםpmgYLV;c]^*z-166ݏ2cΆk{ttT @k)o}/V=frAMMM9V(=>>.y@߸qcG?j24ӧpAgVչ\N @k)6VTnXw5הJ%Z+˃MNN|jjJ @k)F?~\2v޽{pAǛyPZ<أy_jp`-;n0::Y:DUS~|#T*uF;tLTR/ZKtjz-ĺW_}U8ZΞ=N\xd@R^:t(62رcKWT=Jg/ 7%ɓ֭=ݿAяRgϊVTf"Lry˖-.n\ >Llr<cUoqȼ uV:pG\xb1\ZNtzkx駅ovv6zŞ={pA7 }.r<MOORX7::QTo$d򧦦" R骫uy[n-wPGFF\E%s!| y@jGc]ކ ^z%o裏F̱cdB @SG]92G1V:qDcccc2 ߡ@(K/mذ!}FA`GvUVBW 4$p3Ҷmb]5\o;100 MxXs&<KA0::~bP(ȄnST-0<<, y@W{ꩧ̃\. ]T*`ttT @(n|ȼniyyY8t'OF8x^T,Í011! y^^^f.磌22nS*\4C`Z^<R׿/]VE큁R$V>Ԕ@$P[Xh!`߾}ѕI&&&P,$"7_~+GU*&''kG ]nll,@WTV̻+~_ t{ @,tpSeIx{{ow#:L쀋L&p_PVǏ_92o߾}F1 صkWŠX!N4PJ\FSN8!?XE6<w|7ƺ 6¡r2sfggí1::* !y^?C===y=phSgϞ ?9 )<wwGZ&K\޾}{􀋥%@hjjê@).UW]njdm^ٳ'zٳgQ< Sn#^8CEWVHP<cg}k!=]cccz],3::n H?я֭[.#H{\m ;T* HE[\\b]ޖ-[CT*\yjlp8HE%V{uy/pHz}(JEo}/4ݝDp'V=æ.cǎ~Jnl6+ 9yԩSׯuy{V¡ BtYl_FFF$"`ՔJ#r,Ze~~~`` JX,FOv<T>ĺu?-Y;vpQTljj*5\N @r(V__G۷/pcrr25|^ @r(믿<9vph|>]~SSS2111nB  9ykZx㍱.opp7kX,R)\E ̌@P/~.oo$Y\\d2fNO2)JC{#>#.EXjڮ]\87.Hw> (<f۴iS˻='u)F.MT.BAnt:- QyMRV?Ǻ+-3r:/i Z'pAr$"xl?}$sFVC'rԩccc> jvv6DElw{4K_d.qJ%\+ 6\ @(ZT*uyЇs."/tzaa 055n hZ;!JK9ORuXwB:u'˅[) HE@+}+_Y92%s>."gup7MOO HE@ׯ=V )Μ9=8.EYb( QyW.W۲epށ"RX^^0R4QJ@DQ$B^߻woK?τtyX3-+@DQ$7#/KmuywR9q%(h9. iy+lܸ1h'> #V"ozzr$" qXd|MD]J-.`Ά;kxxX @(^{ア./J .MUW*۷G2 VKn1IHs___{衇<~uEq#+ח={pk$χ+ HE@W\qyokd5ח<11] Ӗ\.|>/ iyI}͛7[]LxSSS5011aw Q@)ã>ws&Sg:IhNJRG>wm%ޯ LlJ@Q˫*tMY{=wz)t:A!X# $"jO~./7-.)}Q\@Am$"-b]^__߷ 'OF?O=>>n)J^fHЮ֭y32㋼v]V|X#333d$"뮻.ܹwvWTpMS(766& y^u].oZ]䍎FiXaMpr9 O/ yg?e]{4ﳟl%wj7==랹Bccc+ H9*X744ؑ_oGyRipp0zt.ipH E@G 3L|aE^쀋#͆T* H E@׿ +b\>}bI@[St_ׯ=O}VuaEdb=䓖14MR w_&L<cǎXu;+ T*R):R @2):\s6l/w1E^쀋]v-//[Lb1܃IF>|3_IݻKNM6==nqɤ @ѼNצ(26rI-j77|S8;СC2wԔ@dRt|0 4L -qp3%:?|?:OO$LRia۷o/bV?YɤFofs##vE*R@kڵ+ܒZu y]Vx㍱.oppȼ&~1@kEnÇ܁Rt~8Rډ H -d$"?VK===>dB쀋g[ZZ we6X<~_w~wb5V*ᬢrd\H`~~>Z H,E$[o5mܸ_j% H'O{sllL @b)W>`OOO흞̥pqq@B4rrKoxׯ_{4 _B^Eya@r9r$ܞ|^ @b)+JW_}ʑy¹+@, whPX<Fwuʑy\J=㥥%@8p ܤ333Kob#?7)H4b(HA4RI @b)x'J:=O|‡CG쀋#Gh`` ܧnn@)xJe.}{_ׅ`68p!7"NKHJ FGGc]e]?ۚ ڵkWZ $RU$"3F%;b``-HgφuxxX @)ʑyGkpBѧSӧeU, ;66& y\JcǎXwWWί_\@ 7Ą@$Sp طo_[~~.O&vؘ. &''=$"tرȼ[ T*cHÇvzzZ @)x/6l{4oΝEZ CHV$_teXd<.IRf.opp7ǣ  k޽{Ýi y??uy}}}!|>ڍق6}RH2EcȼÇwi'OLROFN$"US*l>OTN\ u|@h_St H8Eo\92^/Vڵ+2Sٳgh#sssp<V<z{{owؗ966}YKP@S&ovȼ{キcx]th;ǏwĄ@SVJ5\"wt:D^8p#7~, y Ε#~_d/ƯЎ:婩) `}_^92駟nǯ%{FqM8p bQ @)h3gkny/#<ֆ  y4I\kc]PRi/T*߿cɧyc]^:?@޻w.G6k&O?F?$|dۣu,..*J7o, yoqW'?g}pnnEv7;;aɧ5>Xwu%pd^.!}Y:@X }$"_c]^!\4ajj*ڏ?@Sb/b);'%V,tw199|>/ yێ̻kZ TJeg``I&&& ~qɧ ]b]͛-AgϞOJŢdtt4O|<^?#.oݺu?LQf=y[-(H_|qݺu:n!:Γdm^V$"y7V̛ vj>`-A@ ꭷ_kݾ}.p7v@ կDk###чt̿N5;;n{ h <G?QѼ??Zu\% Bt@ -[b]_ST?x.Eϴ9|@ |3.oӦM52T*Ef}ȑ#ڂ"=62wbPxg'''=:==jZ6pQ* m||<ǏyX~}Ѽ/~+?[V=k׮fCL&˕?y ?s@,-- ŊƯT*9znRm\.'apcO$"}.mذsQFGG] 2LE*ϯsX=/j`oo+fD/]a~oܸ=a~co;vܙNcܜHpo߾] @Pjڇ>-o;~ܠ=A7 7@vڅ"N0>>-߲eȅ;r<5y''' Em/hwA=6Ɉ:X?.](h{sss{O~ZqdaaA>bQ @P?'G.`/*tl6yB@;rH뮻"ѣG ,aJ"](h{{򡡡+{A=N h#<ڞ'W*͞fEmό<̄Emϩ+ fFy o -vL&#R`|>\N @Q w[l=7{<|^ @Q Ξ= z(/Ʉ1JJ%yBBA @Q!v9_n>i>DzKFGG-?33#(T*|~n?Q}/iӦ>~_Yܹ3:/"OH?mD@͝s_wuCCC3}}}_ܷo7a@Ri< 144477WV?_ \.< h/<:ѣG7o^*:t֯_Xfggڋ"TV|t}T&yWƧ~:۞/:@P@-..8qg瀕//[B?d`J0ڑ"VPR#]ޝwYVi||<<GGG#E oE, ڑ"V{zz"#~IVG>R$)` "e +-˅]\Ўyz̻fgg+*Ʉ̌@vUSO=O~IW#E4oH$_|Q2BGZ*Цy~___ּr,Jeڔ"V5 o2 W\nmJM{Hn:#`JЦy|㑑y{9( 522"M)%tM. @8pFGGêP(hS<hZ_r~&|><Ţ@63D5k֨jdЦyr~,}O24===-M)?>JEᄏZ hڔ"ZT7"] 7pi.}_2g3@+;|d U*оy}Gn۱cG*344$}) m޼9 U*£& h_<h?p˻k'''%P(CfddD @R@;˿˞.oۗ^zI2iFGGellL @R@ȭyַp`|>)bQ @R@j_#]ޯV*@D6  K_827ސ 4iKm֬YuwwRTxkhk<ho?nȭy?pzdR @[S@ tygΝ;':LxPimM1GD"oKNV.#"hk<7|sݺu:{dXb1<r@X97~^ T(! hk< D7p4###Q066&) .k'QC`||\ @[S@l?~|ڵ[|I9lK@8ٸqcҗddb`` \ښ"b^_^82Je_T5Et??ۿ[L&9M!yȼ{,KJ%\tZ @S@[#]wQVCpg2N^_ty7pyOT y.yЉwA'B!\###ڝ":[.rk޶mی#6FGGõ=66&)sU*~.o1Å],;Et{,_~bbB2l6jKE_|122[iwL&\ڝ"w}/rk׾z.T: Ā"z>88RphGde<@@ (Ow֮]{ ^*JĀ"z7ȼ{N2p EpΝ#ZM8R.\.' yx xΝo||<\###b@\ʟɟDk歷ޒ -nll,\Ā"7xcڵ:dhe|>\bQ @ (˛MR.¡5rpNLLEpE-["]ƍK8L&.i1zzz5kּkդpuE8޵^5g -%L~8S4xPVTnHww VPÕ900  yn۰a/nzz:\lV @<({##^~e\<LNN.cA X,qddD @<(UV7o.#hp) ǷHL&M2pEK/f͚.?dXe\.\rY @<(oܸ1rk#}믏ܚ;;APR G:3<=Ae˖Hw뭷;wN8|\.k#ENzHw5׼[a^T F63<;v,22oKB.|>/ fy@Hկ~ȼ7::bFz>88~W~X|>\ BA @(6suww7vyk׮52cep%J%1kpdޮ]Ͷe2p e1oHwwutT*.J" fy@ADӧO .qHYN@liddޚ5k^}UtJT*% ~y@;֭ܚJ&r3@Qq6JooTUXT ww6?< xG"]޵^ ' B@Q 22oۿL,;z@Qq611qFn}O>wqP?< nHw=j5I6 oT?<#ov=}dbc`` ܹGt^z˛oo$U\Tď" 6lܚ#A VL&Ē",Z;ty6mӾfff½NĒ"D;vty֭L*d2bItW_}u͚5]^ww$ӎbs@XR߿#}߬i/B!܃|^ @,)VHwmUUᴑp %EϿ/oJ] n||\ @,)~W_M$:oddD2m!͆{T* %E/;wotyLȼ700iX[ltyϟN+KRT*bIGG G=zT2^{*L +EE8qb:'L Q:W<p7/j5ᴔr8P @\).GtydZGX N.W<8x`OOOc7믿.166q'N[.rkޓO>)V0221q".\Hӑ.oppȼr)J7#]ކ &''%DCCCW<ŹȼHY}1==- yV.ȭy;wLS$p/uqXz~]wEsssYL&Ę"`醇O*U3==?00  yWW^YfMcYaCCCbLp>믿>rkC=d^*(a\N @)AL&|.\Ί c<eSOuww7vy֭;zdVHؘ@S,z+HDn(y#bLΟ?|A#VB6 C.JbL"~HqsIfy OOO 1EJ./HYFT*R1E :qڵk#uO?-eQTɤ@xSr-.j¹J333atZ @)VP˻N>-Q.<3@xS^x˛ÒYbě"`>}zÆ [x ,MPcMj]wι9,hchoۑ.wߕ̢0B  yq#8 +fJ@xS4;Svȭy +dr3Shnn|>w\V* CT*ͼZjDtOR(|uVT#<ϟ?/\.fR,|uVT#3twwGFK& )MVp@;vXdd^+|Cs0&_] `N5"h AЙ$0 \"2g  Eo>(*J@*jȽx<  G?cG0==n@󯮻 wygȼ'xs(Jg_]U8Ո]p!JE/| Z6||<_]XS#<֯_~Miյp@[;p@dd^OO/ _] `N5"hwׯܚ|'ƛ--Ϳ^jq@ T;#m޼9#2LrWWQ)+=] 6~~/mذarr2~[NmiՕq<"4vy?cd2zkE ӧȭy_cJ%ܮT*W׊<`N5"j}.ˇ~+Fe2Vp@\}ߍtyd2#JREl%y*jDc/5k"#ިBnN>oeZEpӅ>"=CA2%B=n6í( -qu2x""#mݺ6!JRյXS:=^6hk t~[f믿ٿ:j+JST\]U8Ոcd2rk^>g8yd"l6*rL&c'C>Ûn)z;77iӦ߿ /T*1ڃ@Pt |HwܹKVV۲eK$ɕ~Bai7;E /,/ڵkT*#;::v9y҉'{dmq[n.Y>˰׀ΡcZ[otsj5Ʌ}_={+f*Jv9yD=ÑnnÆ O_j.>W ezz:"8p@OOOc77__g2+;33/,JeE799yv-ս[Ֆ%ɤ}tEZ%wy>򾞙_tEs%wy^WR.gd2v Qy\~ŧ [Ə(3r9(<.c۶m]Wa``Z.+) y(<.e߾}]WmǎA\ sll:"OuرD"ѵwp||:";{l___2I$Nʗf',JQy\DZݲeKײڴiռٖ34ڂ"hmm߾jOUT&(:x`׊ٻw/bM@qNX,6[ }}}gϞ] !S@Qpǎۺuuy[lY찼8k>:"O511}D"]^Lݻw;@Qpw[.P(\ qtEWdnnnll+k׮5` 8zruyZJm*Jiy,ɓ'o߾,]ގ;/6SSSviy,T.K&W:tkӦM4<VZOH$'Oğs؁N`yjuy6m[gϞ-Y/m)XfG{m۶-Z߿ppFP*[(XSSS>EkKسg߿q"%ސ/z@(XAýW}+_YTN}- yjo߾+7uumuu%ޚwq9`AP(U'j]]Ůts_o<V[Xvu]PE{lbL@sLMMر#L^؍xk]v +ETT?YM^YplϞ=+U y4wobZc񼼑y'`;"JD Ē"&jhR]]⋼ZWU3g ď"&;|pC[|;>>.U ~y4پ}y#S߿_@(hц"otEO2RG@#J(h23"&WB@d2M.+~\R"bI@ 7y;;^{##bI@MNNv}¢nL$bI@Kl(*W5w_bI@K(ˉDurZ j5yUݻ\oWUxƹx7o>{z><m۶ښ"VTV.ZR={\t^Pft?I\)yg9r?uԥebb"J5vyd<)J244ottT2@ROfڔ"8mO<)(SNEF%B (Jul}  E!{Fm۶UUmA@9zhdd^: yt3gΤȭycccZ"SVwvYׅ,EDd2JE2@kRй&&&T*edКyt遁./H߿?-E@\.22o۶mjU8@P/:t(22opppffF2@P/;v,22bbB2@+PΞ={FF:tH2@) ۹sgVD<#ԔdfQŕJ./L9rD2@S(SU*[FfgϞ 2E\J{tyCCCsssV".oȼgJX5<"6mjdա+UVmyݻpE`׮].o֭JE8R d2R)+GK155N##/`(`j‘y;w @8SA#]АyS:|pooociӦ H̙3["GV۾}{r.)`AwD" J<Xf'Ok"ߙ3g./H:tH2)`Ej\.sN#Q :tP2l6o|511E7;;;44yy(`5;wF;vTUWBg||<22o```zzZ2e)`Uo^Մ\"X,FFm޼̙3R@3MMMȼ#GHP@Um۶EfgϞ yFFF"]PR EÇ'./Ne?W@KY82/LE<h-k׮z.d<hE{6H:"ZTXk濝 t&E3g 6vyDСCVVs\ddΝ;]8Qy"#Ϟ=+<hǎ72:"‘yAbOV-c# y~̃499[,%qv5;;7<+Ez><ym>@8yADcu֙@S@gHR]^ɓ'%-Ngvvv˖- @+S@' `xx8KV&EtB700`d&EtrNT*ed Etm۶Ef;::pu(_6CѣGdddޙ3g$@|lȼd2yq@)OV۷o72Z"={DF IE\̃>-["#9"X}<RАyt<2 ػw‘yJE8jy)JTKFQWjvvvpp022o||\2 y"۷GFy#`)E52V"X'O5vyTjbbB2By={v˖-FPKݻ#oj֡C"]޽kd,/E ?wIrQcvv6t}ؘd`Y(eA>tyFPý]̌dj(wԩT*?~\2d<`ET*#FGG%KVJ^ty;vVR+k||<22o```zzZ2(<`ŝ9s&N7vyF(077}lGFF \ Ezr\V \"XUǏKӧN \"Xm]^__ypi< jZ.32"h}%./VUB<&&&##&''%<fffd2yQ@#E|ZG۽{w^|DFFmݺunnN2sERJR*j6m455%PRlٲK$ Nzsȼg}<:"hQ۲eyt,Eкtc7d@<U*L&%SNIN䂖IENDB`PK$8ObjectReplacements/Object 10 s qcd0dFid(10310Hy@! F KIn*?T8 TlT5'\nA[p\n0҂CU0[D֖ZYZE Y4lC pYPKPK$8ObjectReplacements/Object 11 s qcd0dFidX  7YH2B@<(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T [@lFҊ= =`1괿 TtYwN wZi’. PKTQPK$8ObjectReplacements/Object 12 s qcd0dFidkX  7YHjB@<(U 'dYL @!$37X/\!(?71q9bf f3m3;221+I3!^T ، { 2zc i40Ӱ%\PKg#(ֲPK$8ObjectReplacements/Object 13 s qcd0dFidwX10310\B@&@@U~@@q4jN>y. 3\ %EI9=AXPS3-3;f121+5@mHH, >R#،@vXa2 yZ``D ! ~ Ay100]DApU;Le. 2:͆;͉\Q5Bmӄ}PGD:pg7;"Gʟ͟aF@a&Ԩ4)0k~}9 \0U"fțma3h ۛ6B 60ca)@0jYPY ` ;Ds[dжk_ںsؠ;}!1E"sJU eiuOv0WQ3m:kUT 6*]4 H\EUUFRݢEuws=0M}жl[4HJM*7:j liv[g0umkIo08a0hs`,6Bih0kIf&9o *`̛iޔ@8̖lխYn sbl4l+PK3ϢW(PK$8ObjectReplacements/Object 14 s qcd0dFidy@! F KIPPj*P*6 O ȲL$CpIbQpenR~DrF +ԔpKY @ v PGE@R@: 1B1A Č@vXaDكf $B2sSRsc``\׉C]q]*t_*:m*P HlӜuUCjӮ&0veF(+,Bit˟~l9)'7ٮ%6H `Tw@Y8;"̚0<Ѕz\6q\Wț"oq օ{<6B xca7 ۵|5rQ5 ( ,YY `s ":mu@;Ex0םmvȠ;EmݹWlFjXwzI  u'}SA[EM)Szves%;hәܠdmU0h#4Iq0JjUUT2 )n2HP-[uESuDt )!ҙ`pAZEZ-@75kb#\gж6\ucDw06: c!a8Hm(0"z[9漹dVKLcތ5#7-c;PKqY&PK$8ObjectReplacements/Object 15ZKkQ>jؤɄM;MiMJADi|[Pb,FtU4uk)ή]OnDQE]33C a ܹwn|19cNϟ(C! i{'*D|<7x-RoX1+͹tйo!OY>Gh~xE;x];\*^n|jlL2.kqeg'sxDwutv/@?un_ʦCzd~;joj#2C ZahݦCsW幕ҹBaJr `+(G ,i`5ߐ,`FX̶YYUU 5 ;{VMﱵʁj.%&tF:i-ϺWܮ3 }5EVhSNc+.E!Z琰jCý)*Oa2&"1UaSToKΟqO֫3uFe'}5X  Bc27շ>[kpL &)½V╮q%y+t1.tUN -/>l[BJ$b5*D65``˿PK/\&PK$8ObjectReplacements/Object 16ZKkQ>LԶiĦ]tRSlRIm1BE[PLwХ)VdJgof g~99络dJq( DU%˷||SӮ [}`|_mܛoXY 癏vg& f/% ƃЯ} #C4i[1>Wy| K 4=ۑShJ !  Wv/M 3aܢstQr ,j`5jZ%Mj+8+C١BYgYК-XIhzrI$tohEg =dt+5 DLCFZ.%Aylo'D鄍]<+Qa ҃X8Ba%}AyFQdx>9蜉K:C)g?Q&g>~IJqjls9X6-rXM9'|97s`%}bn~|nhLJv dtVe<.yn2]\WEw-@=˖c,BK RǤ=/^N)MPb1 (N M:J"AЩB+кVï}%8&PPQEF.A5ǿm*b[ի# SVYVDnO@ur]guՙt>ZKNJ?tZ U:SFtse2F'uP33[i}#ZiËp Ri;٠:[6 lAm-SoD47Fdr` )JӌΏ%)3 RЅU2F!NTmG c%lcQ6fehS!ƌ-˓Ǭ9^ dwY.^ K~ԁNe}lQ6>7 F[#,nl|r!Oif9{tA=cshoRm퓳6Ef @{Mb،vgl>8K+ZTE[`:M>X ЊhQKu ֢eXtNIڄHmj(~y}9WjYtpu'3F5ngYﭰɄ"Y֩ju|Y׭Ѳ4˙) `Ύ,kdtpXtŠmt',b+-~˺N/PKe^t;PK$8ObjectReplacements/Object 18ZKhQ3`S56[,\%hSt4AJ- Q1YUIn t-+{wAB7v3f32owso^rvI2dŨ󐶷i _!UbluǕXmKLSrirJxɸ61kT5޽~Dk:$U[ :fZ0rߎz\C ЎA_)a<좊9 #ҜdXKffyٲIfObnUr"r[qs,'L:g3sk@йޑvsl|vCNZ&H"ӷWYk@c!W& !^/bxE?,*9]p lL`W0l #F~l":, #]'|6Eͧ1ؒG |}E'E[6^Hg; Z(3K1FVUlvjsv]bbq$?پȶ4U>9%*^Z2>\ܹ"d^+Y ,J$d=+Y0Ѳk%="gPn9:+φa,b ,_˿PK>M%PK$8ObjectReplacements/Object 1͙K/QA[MƆ`ClJTXyE%)(Rj#♲ -V6>EE| 2ΝG+2s&;gzsth86(@;(=e 6Q6QuB[mWk4zf~ 6GԒr1Xkf'zXxX2(¾$<$.* Oh]roT2JL//r*CRyR`TaXhWK X.f,L_1sfF&g?%IFhlr9|y)ș-#+Q9g6.fv3;u{Q8[qP ʶqVY,]Etn:l'1lE4nF,E4ql!Y9~~UM65tYmm#Q[嬷Wxt }gAyStqmXgw6 Uٴ1:ra(7ḳ R@KUt JiQVg1sg:)g^,vlhFL豜͸(mё[5{Ț훇|d)գkPKYPK$8ObjectReplacements/Object 2 s qcd0dFid00lrBL@F KI'PC@q4jN>y. bb ! ~ AyA301+n5YN "q ħ@Jd2}ǀ4+m&@!$(27)?aP9^u>423$=sؼF0Tw4{i8ŒNiU봿 TtA89Ά;͑Ĕ 0VX104?gf)npؼ-Z u g i" s6 4a&N n.DJօ{ЖhF< Q9 L0eVQU"V9("TE * `9lpVQ * AYE]pеWEq͗VQEѭ{ѽԽCD'2PKfYPK$8ObjectReplacements/Object 3ZKhQ3|&LXic\hצRkX6VQX BZA(sFERA[?⦌of23AT2g;sK}8j'(H{$z'%xkH.YfnBTٺN/JWh}:o(;p&vh`4vx898'OL69OMI^)?ˬHD瘁>$6ʷ#Жr BM6Mѡm2[tPL7>e8͡ k#JQӀVd8XT=V,Y Lm%Knu euXat~_ KUVyΠs";gY6:`l~5l AokMY3ͯnld9n5{.dukF!DQ4zdјlsq/lLpl=lȲq?`Κl:(G!d nPEE1X:5 (Ƭw BMLdfmެym>g ,b3b\h6'}ltώ}RLc:>[l"v+ + Х6D}؎,b4QeU5nm]oOMT~|p=HVfa$-D) ^ \ܽaZ§y ~k"қ#?jiChȆVK 4Am\yXCΝDVl` }Gۓ"~)zqX^PKj,h 60PK$8ObjectReplacements/Object 4 s qcd0dFid7p00310X9@! F KIz*>T8 TlT5'\mafa$ٮ҅0\a94l \PK]/PK$8ObjectReplacements/Object 5 s qcd0dFidH10310\rB@&@@UB@@q4jN>y. bb0\ %EI9=AXPS3-3;f121+5@mHÐb3B1A Č@vXa`YI~br<c"u@Z溊=pU0鴿 Tt PӜfÝDӨjO`6 i iJ,6BXaꈈPGN[\ϊȟ|nc;5U1hsg͝\ȝ;iDrXFÚaysJD`2ߠmom5-4HKWB'LtFUSNlN8*>Hsa -ٮE"I Y4l2 pYPK tPK$8ObjectReplacements/Object 6 s qcd0dFidDn#% (5(U 'dYYL &@!$(27)?AhrF ݬPS3-3;f121+5@mHH, `cm& ! ~ Ayu1]DC dbpd fpd[`2ePKxˎ>PK$8ObjectReplacements/Object 7 s qcd0dFidDn#%(5(U 'dYYL &@!$(27)?hrF ݬPS3-3;f121+5@mHH, `cW& ! ~ Ayu1]DC dbptb fpb[`2ePKysPK$8ObjectReplacements/Object 8 s qcd0dFide9@! F KIPPj*P*6 O ȲL$CpIbQpenR~DrF +ԔpKY @ v PGE@R@: fcރ ! ~ Ay100]Dā2#u{N` i4A;`9͆;͉\Q5Ԟ2BmӄӔXm "8F?#8'6 %v*rp3}C1Ǡ-8mjq"4]p<&ot!!-̖2of4Ae$A B1$/H4jN>y. ,o $B2sSRs -Y Ĭ`>fM`f@Fv f;6 PT [@lFRx =`19 l b.I, MA hU J H1\  0{a pU;G44ӬKN( ;pU"BrSzVDJST$]<w6PKe1PK$8 content.xml\_o TNe;׍^{n7};0m'*I}^9_C1E9Ù3C. -rviyB}cAD4큪|AcefRT= ۠`f[ArIr~q5) <(lraZ_+B0tXGק_ 𞘞&) fV`ʅ w1T` P 2+ٳL(ݲa^aC|meZKĐdfSF4]Z1yD5Z`jMo E>ߞ"ʇ}pr *ڏ[*KXL|`3~`@rT3 ڑ;Hk*;^=' )<%^y%\ 8РcW7=ѯ㑐=CKmVt#԰QR|H."a7i Y -ScOTtdczvH# [b^lZ a>pAL J54Q!dyCD|fGyk!,$L OyΧ&AZ*7aRQѺ72XPFxMlc]ðN4pMXmTۧ-`6 i4:AfC[6.\5$_``^P[P'EX)KW#(zStXso:bdřB.kHzӤ DE? Qh8w(|(j4ߏ^A.Ku1tru0iK$#z#J*l4d !4^߅a"RpT4sh1uMlw^:Z6ِ*Af:3>b4W{'|;Lpޥh[ ΀W%OҴ5{|F\ͶЅ+WYVg 3_;6$Jx6 AT]WȀ<|҂b#rYfudL%m&7$D f &+#i<y|O~^쐭 {0L!t1%DL(]JNu;1k0Z~S ( p@8mAB$HF]' ;m XX`P9N#j2p43XGaqEHT~&Yw<φ}pbߝ3C+r҂W, .Ԇ9áթy>#oN=o[Coz/ NgTwK^#zśȫCcn4Rlܒv>l޽(UŷwOq ?]zjl{M0tYC80hbq/kmLwp0ęM'bd^ޞ3WX L"Wՙa31?fS6i6cLzM\ ՘Ɯ yabi/볍)9>y1\qW!phvNL ODcdxc=W3 UM3sM.h 35ϼt(~΀kb668yC~>Wt"]H@Eרz$Fƕ g02ńgTEl_)eTr7vqEQJ^?5.6U}0^3VLcrЄ>bF_=8>rO= m&r啷M+Qnhlnxpx}fw^4\ɟNfv=pg3ytt|% c1e8zv9^KF%n7{CcrU̽ɘ k^g|1ۄz*M?9icr4+SsXּܡ߮2Ωޡ駪u~ <s yTH~0 VY5b񫆝^)>~-bXlM.܃݃w#PKym @PK$8Object 10/content.xmlŕj@}힒CPhnI M(TBVz}k/{:@h!l8"-F@ Y4ڧ&PKޖPK$8Object 10/settings.xmlՙQo8S@V%*ZNl '_%$Kę8|N \Pd}s޶Z<) ַٵy VG/3R!^g3a$HX?;iH"ez>_wϑvٛPيEeQ2g6v][f!?gTBc֮-t^)wQ;jY`lO[P&A.ZUVҀ'˗cv{L9nM=F‰To&oҀ,0>r3 >̀_Uu9/۳VLLn}=hLOFK _}QF5Hv'ȗY}e_ǽwi8ۿ_ 4Bf5KFwB3_GN*&̧^8Wa\Bkw Ůk9\s?"<7XPH5s)zޒ0ROM2] RMnU_ j t,i\h$Eg<&MkJP]kʘ&mnԺ(yQ{w&Tjb3DY iVpQ.ߢ%ShDWQj:PoqltkǍGC]9.jPKaPK$8Object 11/content.xml]Ak@ӹ1HA%Y4F'Y.4ɮM~~7ex o`?+Z-M*RSc2{ ^4jna@{n" Qz**YiODqCb%|"H!قDO^iJd(}!, /sHt95WFڡd}@N>vӎ{Rq;ex;7[3:%zcPK4ImPK$8Object 11/settings.xmlՙAs:oCh2'ЁL阴\rhjk=~JS7rؖj2ן(lYꜶ0}ʂc~wri}z%0y"@J5DL8㞕p T8D 9_svG;kXv' )ٳ^V)\]]PْEewQ2gְv][!߿'TBc֮,t^(Q;jclOkP&~.ZxUҀGCv/:,<t9lM=F‰T onҀ$W<#W!pL EnH(%F[>23n kM߷/ տS{X5&JHNI8]@T%-5ňpX 1܏1¬jƒ3H0Ũu., aIPCZ(.&8D-EEe3x[Ѵ5ͨ `VfEd*"1*"հ]FPHGB'҄S2A xUi~H)YjoTKຜTQ+Y$L&E\vTA@&j/>)#'z>PlΛ~΃t2=݋:Ps^`FTH¼¬fx6@eTک\\EҘ+v41K#Z(rSƠX:ۡ)5=C³|so[Zc02x8M i #U4 .Ր,fQ&K HgBx%f*MRtbmD ź Q;yzaJ9F@oF[77jrN)6Em;w뺨*u5Z`8FIdxeաF5FvqdߘK?ۅ_cߩPK {PK$8Object 12/content.xml]Ak@ӹiA%YĂ`@#⩬FdM(kx{_j٨} A):GS\5N|4dfҳPiUBxM{$˨(a7 d>Gx)Ux2<'W?M' Z\Yh?'%}=Õj 7[ءO4LlM0^{<PKOmPK$8Object 12/settings.xmlՙAs:oCh@H1i䒋GS[W˜R[ydljZOi guNV >eA6=h}꿻z%0y"@J5DL8㞕p T8D 9_svG;kXv' )޳^V)\^^PْoEewQ2gְv][!߿'TBc֮,t)Q;jclOKP&~.ZxUҀ˧Cv/Y 4x:ysz>eٍOnҀ$W23 k-M߷/ տƄS;X5&JHNI8]@T%-5ňpX 1܏1¬jƒ3H0Ũu., awIP@!-{"M-hfTF0AL"2Qzj.@Bv(j$OČ#! pȉ~IiBX)v괏l\$,|1WU%p]K樕,&yt_O>vTA@&j/)#'zSl֛;0^w/ Buy Q! y#!fʯRiVsqIcSd0.h5N:bl($+ Z*J FmP =CI7ѐHbԪKǷnFٳ)xU SӉ < F"2 m{m0~@oиi#ԝ+tcPj<:jA]܈ Hƅ( *Be嬒[TEBK8?TflPnňI G45_Z%/A=mS t,>#Q[A@·ʄ"kIh6l$UJsTǁw"^b:o3mpR!1UI.N% uh#-U %Bt*Q6/уJF1q?{X<oq0úp<޵Us+jlh|_gPK# PK$8Object 13/settings.xmlՙQo0)P^6Pj Vc }IZl a@%|>i guNV >eA6=yo}迹z%0y"@JDTw&qJ8s*F"ݜICʾ')cǶW{<;iCkQY]"nAC65~ggVk3\_m iߴ6zB:V[Y^p s|ʤo_E R0@}tn=g#/;MH8*f! ý'\1$nɃMWthBx@ac:ʰ)%Wnטp u&P3ҀS6NG sҒӚbDX5}6C =@ 0$ڄ$!L1e$*E1f9th]-V/PsH 瞨ewFo0v:j9Ӭ`T^$FEd ШJɓy$A9O)Mh =%q]_vEOR,9l%Ĺvᗓ6dhPŻ!eX$;G>N=&?nͧ8k$EG,&M+LPkʘ&+mGuQͧSnMe)uYݺ.LFݼ<{N(QL^GU@ rűѥn82Ǹ;/ؗOPK"PK$8Object 14/content.xmlVn@+i04JRMM $:uu~IV-m s|Ûf;X. аbp4b!A{x/u\&zҨջn"44VϤPښ¬·La??idJγs@M_[}hҪ[G$2[)^ol\Zdoܢ)o,<VǒTnb`@b +V1gbC^PK㙇 PK$8Object 14/settings.xmlՙQo0)P^6&( hĎl a@%|>i hrV (f>A׺}>>\ł`p}< j"Z;nk% "\". ICBv')c׶sx`;i3 {QYmcl`'][ ߸!߻\#"!Ҿiou-t ,7^{^s@3['J׾oՃ4`i]9=g i.SIn$I3{Tx\1@n{]>hIɢ1-WD(zطڗ\][X6"*p8քEVig#1`挅[P@mH`SbY%)Fլb9g Mڊji!# !hmFN422Y :gЋĨ `TfE)W#yR3SB#nJiBH)3 yY+_Chb`Uu:?ׇdP|?TTFNA&G*O"b`vRLxȞ=_oo{G:PsaDHDqaVpxa\W3GZU$Op8a<Bk-ueŪq+9Xs?@<7hPxK5z^@Y!ćt5$KYDR+i2 oa -2Ay)cN^0hN#?zS'?n>5R-uCl˜,Դ*߹[Eɨ/ќcBIDWQj*P86Gf,|=> PKW PK$8 styles.xmlZ͒6SQ"53Ljgw+S I؀ GyFM$ȋ)Ue}-t7F͗9 0a2)H?>yö[u*Džu,ۂ*夜|$TX0`r9 T2i VJ?sS涿F$2xBLOPŁq杖d( |,lx 舣8Gܪ> (n@evgt=[v{NJ+Kأ"_`=߇&?ǎPB H(&3N~UI'FW1מ@ 8-y]"4@>H(S O,])(1:pcZyߝy׈FVu݄lhqs{б CPwx?h`s)جPM(KSZ(rP;V,+-IxeZfq]/JoMy}'_݄#d|}z4^u4U?4f86珇O,GkYV+c\![\|--T`OlԦ@AC.E~m ~- 0卂<bU!>f17E='DZPKQ0 PK$8Object 15/settings.xmlՙo0WDN-U+j2%Me"ݖK_sFi!dYs) 3pA[ӶG߷7'Owl"8*OHN~[1C#"Y7go}Iȱz}2۝ {5k]>1`6n}fVk;?\3\n"!Ծim_-t w^Ͻj?,/%R_֠}i-ݪ +i1vWg Aw my9*fÃ'b0*"3ݭhI)>-W X(zط$ڗ\\[X7#* HCNP8Y@D%-5c!pDXp%c j V(P0( #X/#aVoQ5XX@npZ@K.&8H-eE3x;Ѱ9ͨ`VΙfE*"1*"հ[FQHWLA0yR2zJuE^V|u/!oc4x10o*UK:di+^Ty莾|TTEAƇg*F"b b$}={=|h{ ֝/݅Lpx< 4&B"sF !RaVsqIhRyc$|@}YlmJΦ *܏O)v`.PrP[( US4HWCTFD DMZADn3 &):z4.[dd]SaT0&H MLDH_$stZVIH|YĖQYb/YwsaT܅]rc~PVmi.G`bQU^@8=iX@3Ť8ԻAkp\<{MS KA%DqȇЬ GO_ H(S O,])(1:pcQZyߒyטZԐ2|ݘ%bhqs{б CPg:x?x`s شRBM)s"Cz (tQ;Q,+/euYq]/hO My}??٘#d|y|,Pfod[it E7(:G,mUKx-.sDnϹ嵅 EmJ JY0\8h^Uz>,<ғ' |P\*: ȿϗDWN^".+PKU}f PK$8Object 16/settings.xmlՙo0WDN-U+j2%Me"ݖK_sFi!dYs) 3pA[ӶG߷7'Owl"8*OHN~[1C#"Y7go}Iȱz}2۝ {5k]>1`6n}fVk;?\3\n"!Ծim_-t w^Ͻj?,/%R_֠}i-ݪ +i1v۫g3;ݳG^9x/ba@|`q=j!7ʣLw+fRpOq*ʰz! x%WnW,xM戊Ґ4NVQyIiXh5m|6#znXZ &L$ 6ň)HUbT&3а8\_䒋 =RpyQގa4tN3*#ՠsY) HHF5Q!{r511(a8?@&L)q]_veț ^ JNYq{ڊ1s3\}L  \FVmbۓ [c8R&SᤏV̩Ð ¡(Hhol`$ Gz2rl{^{v<͚bFW+*mb@C: n7 2?dW[@wB$7m=3kֱ~WЂEVDD 켅[ua% } |:fs=i;ݳmވ9*fÃ'b2*"3ݭhI)>-WX(zw$ڗ\\;X7#* HCNP8Y@D%-5c!pDXp%c j V(P0( #X/#aV/Q5XX@pZ@K.&8H-eE3x;Ѱ9ͨ`VΙfE*"1*"հ[FQHWLA0yR2zJuE^V|u/!oc4x10*UK:di+^Ty莾\t**" óRg#B?~ow>x̞=>N_o7w'{o':@saDHDqnVp,8p%& Ĝ:*Vj5W4&7qIHrIqʗїvl bM4\~5Ŏ%Ju{KeJxjjH*rӈ({QV$x)0'Q CIyM7D˻P- /MD8!8N)L`s)P'M[P{}xjR™Eh!ԉӃ--!)Nu9Ѐqs) CU{iں?&"˄#+U% -jz4jEԼԒ9\9DzS~^o{E\Qüa^c 3ub͹|Z3bݻrxwq[DaIc uB(׹f5P1g\ rWR^SnG˸J>ܣ'VXM)Oȩh#@&G}h9L:>7;PK]. PK$8Object 18/settings.xmlՙo0WNmUkTS6`K_s;Mcd,'/U>糹A yj#[wg7l"8*HN~[1C#"Y7go}Yȱz}3۝+{5k]TTzہtt0Xݾgod~jߴB:/;YAr@ Y/JkоNJ0H<|nu>:NwzNȆT177 Ou X<3\#$Qy~1)Y8E'q eX^=ߑC+Zsu&PsDEi a'ȂŴf,DN6> =d,D j&be$*E1fth}.^/P Hre wFo0v:j9Ӭ`T^$FEv Ш=J㊘c0F SJZ&BOɔy`ˊNb2yEj \,=m˘عuї9lTňP_Aڝ6@wmowxq髻{o':@s^`DHDqnVp|6 Ĝ0CuT*j.5dR-uClƘ,Դ)Oܭdpɂ)$C+ 5TAη(2G,p\xo~~PKi%]PK$8Object 1/content.xmlVMk@Wl٪T BK%M6ԍ$kc(B=_MbH$F-^6{숕t^i`NPPu:`w- /zSZ`ΌЎ  sPmD:hv pB; !LX !y-L m[ lT*" pAE2ebPa" ̵a10eHP'X " R3ee hLJŰ"I9oOǵr8W1ֽT*tQxmH:ϥrNaۨa;u?|v&PǫX֟(^aC_cIn<@}RoigȔLf|fKG{&m|c5ԲA5L_uxFz >/ >(sr]AH=EPKV PK$8Object 1/settings.xmlՙQo0)P^6PD J1nھ8Gj-#)>lB#$xxAI>$ ZaouNV (f~ߺ[ܜ\X.jE08qTR5-՝ '}ܷbNE!GbE@n~kgK$?֓ct=eܷ;^<͚bFW+*mb@C: `Vk;?\3\n jߴB:;Y^r@ Y'JkоnՅ4`x^ﬞ@ct.>}䦞OZz#Hħ: ,Cpè<-U|aD(/ NYٳ۽'Ƕ`gӟ7xq廻= דjnA9ϰ]Ac"$87i8q`bN~>*Vj5W4&7qIHrIqїvl bM4\~-Ŏ%Ju{KeJxjjH*rӈ({QV$x)0'Q oa o2Ay)cN\ in#W֭GɒګOM5TK1&K5o7uQe2KdPơrT ..}=p#Ǒ*c.|oE_PK1dPK$8Object 2/content.xmlTJ@zi)DҤVA`x*iiYMJ)]4nHx|߷34܇{"ʙ>R61ppVڃ)m9=[m@U{<:Π]QA{ !jtH!MM=8 Zx58F . f:<& ͏t%ā0i}qr f9up7N3\&N21@ "DY 8>+bY@, }*eZ[VMfgZԴTɯ/_fK[嗼)}WC>ē%#hDJiiTfhԂ=PE6V d*ugR:%fZPKtPK$8Object 2/settings.xmlmo0SoC;PAjHM@&9Zl>lBhKʒx}ϗsҽ~zVi5QYOomKK)Pәp=+A"p @8u0Lsޏv֧wZK:l.7K+ussc&C]dK:~B#HO'?!4k=K!W cԬy<dܑPݡLZfN[8ҀuU5?֙>9ng\Kp"NJYOWLb\\.C"̌Eb]6JeٖK]$a@ڗ\a)#l@9į.s sʒӘb@X9c6CO#@0$ʄ$>uM1"m$*yUe:41 Q?Psئ 3QpyQq&ޑa4tM3*#ՠkY1 IF57Q!(j$JbFb? ԡe$LuC^|%ۯ1o C Kj \*nnc j_vTHܔ8>j)#b>a@^{t$KWqF}+,Z0Ѯ$Eg,$.&R`JP\+ʘ ޡPNЧQT$EӇOE5 PK"B@ȨuUTuy O(A,^1Gc;04F9%nBS_o$PK$M9LePK$8Object 3/content.xmlWj@){2ݪTVJ饒Dd$c$$n%f΄ht,tzO}vڷ,eYi?j`$A}V\D9VpSU5YIFj CՔxV@:4§C]T/ Y2. dYVz|>\$1WSIhɠ bDMy]+C!$B#@U f3KՐbV"G3ucRD(u\ h4i[Q^W;UnتPݰy[Ə/بCpX#騚8{ y/Ł?U\TYwq4=t4&"a?br| Ѡ*pC'JI"awI[|J8 O܍(-׆?g3\}H  \FVmboۓ [c8R&SᤏV̩Ð ¡(Hhol`$ {z2rl{^{v<͚bFW-*mb@C: n7 2?dW[@wB$7m=3kֱ~WЂEVDD 켅[ua% } |:f{߽g#;ݳwmވ9*fÃ'b2*"3ݭhI)>-W X(zw$ڗ\\;X7#* HCNP8Y@D%-5c!pDXp%c j V(P0( #X/#aV/Q5XX@pZ@K.&8H-eE3x;Ѱ9ͨ`VΙfE*"1*"հ[FQHWLA0yR2zJuE^V|u/!oc4x10/*UK:di+^Ty莾\t**" óRg#B?~kw>x̞=޵NͧO0_O/u:4(F1ɂleDJo,EN!iP2D\ReVۡ9RרDZ](ea !52ɋ,2SƒՊz%0y&@JDTw&qJ8s*F"ݜICʾ)cǶw<;iC{QY}"@C6 n_ٵ r?7[@wF%D7m=+kֱ~?yTc+"b2i 7v>Q_v/4x9:_MgsH8*~e! ƒjĝjQW䦻mM (;nbh&B~P4оʭN`jN8ArJFdAUVbZ3Ӡ&b疈!f V$P0$)F0ӮX~HYf`ܟUu:?şgd08Wγ;|SQ9@QF5Hv'ȧY{e_`w[{':Ps^aTH¼¬fx.@9ftR Wh2UcF:blmJΦ *܏^Rh\^710TS4d"775YjA:O4>6Pm#`Oo&B5M&(O5eL[ 5z(yRSSM)R7fP~n]U&n^EK ( &*HU9_7rY`\vs}C?PKPK$8Object 5/content.xmlŔj0YZE*m”+T J-cwIV 9n|çw =Ӏ{>3p]aD;[x66q\藃I`vr sL6M\׳ tƒzA>Kx$0'&za<- bP )j5!!I9][Lx6P\TE݅zJDM/zPx{?Pv@Ǧ=S?P\%>|[5E *2skԲ;1K1_XB$ЕeDJ4JZQ'8-Lx{a _K>PK_v;MgsH8* C0߳:"ɣM_hhJx@q'm"a)%Wap &PsD i) aȂŴfvN61["@5XP@mDz 2fUe:4!j'/P H 瑨e ,vFo0vj5Ӭ`T^$FEdv Ш=Jɓ11H0q}yXR2zJj:/?# Uj \*=k%˄u;UDE[drx2ZiDo ƿ۝#_fmonOmϿENnzqZXgc+lWИ IW,/aܥ1?P~J;H0zŎ&ø4"8]ga;r6Q~Dxo.bGk ROɽ%a 1e,#@ds|s]CPKHְPK$8Object 6/content.xmlMMj0=tjB! CPkCJB#k7n{'u#7^08pUF{wR )q}Mpd}KptpXr:s%g=c bȕu 8kQQ0 -^DcUUy IuV+\C9 !5H7/7,] "^(QXYR)I.!漰iMOnqkZZmC/:ܻ#T&Rgj9#?PK~PK$8Object 6/settings.xmlՙs8`zMxɔM^"z$98+aL)7g[7s0ZVͧ4 [Eַ:mCo}]ܟ}> Պz%0y&@JDTw&qJ8s*F"ݜICʾ)cǶw<;iC{QY}"@C6 nk/~nJoZzh}K!W ׬c~9VD eo종[ua% }|9fꪦ@C^7}䦞OYv#D7s7 itW W#TsňG&"7ݭhhJx@q6ʰ)%Wnp &Ps ҐS6NW' ӚaDX5m|6# =D 0k"ڄ$!L1e$*I1f9thC-F_ #QpyQގa4tN3*#ՠsY HF5Q!{r5'11H0q}yR2zJ/+:嗋ğoEj \Yy{J sMƹl ҈\㔿cPG_,VXDŚY;1K<4JxjՐ,UfQ&K HRx'f*MRtcmDɺ)Q;yzaR9F@Z%Oj|j9Z Qjߔܭdh2%qT k]zF# +c>".|O}PK]46PK$8Object 7/content.xmlMMj0=tB! C !t)d98RqC{_7b@}3SYXetCAjaRgv 'Nivk4[*$5ֱ~Z -n\kC\5/xQGWvJE /Nm"PKOvcPK$8Object 7/settings.xmlՙs8`zMxɔM^"z$98+aL)7g[7syؖjZOi^ ouV >eA?> Պz%0y&@JDTw&sJ8s*F"ݜICʾ)cǶw<;kC{QY}"@C6 ng/~niߴB:;YQr |ʤ5hE J0D}rǫ^=˟/G|xz?eً0Ax0^\S#"!Y"&K HRx'f*MRtcmDɺ)Q;yzaR9F@Z{Ĝ'n>5R-uCl(K5owuQe2[pJ`8BTj5.}=p#ǑO^ۅ_cSS ~PKŝ1PK$8Object 8/content.xmlQo0)>郜,QQ,I2Ͳ' h-v,w7'YA.`qzqj?ڬ+ 8[J#ߥl}J͆'s6X2np`%ly!,FJGC4I| `|N+\9RޅJm C%lVہ;\&dc3A?,1ݹpV={ k*sfR=N|8؃gA'tp ''U?Hתg$prUʠfU)V嵺tPTB)oRj3 [QAA2эz- Yk5flu+#]IG:"ޚ`y,tIPK_oHq8PK$8Object 8/settings.xmlՙs8zM0;vL}s=M^"Úh ZF1__5 KjZi^ ouV >eA;d}qK"`Ljhיp}+A"p@8s0v6NRo=K;^׽sݹ7O ދFq/dl`v][f!?gTBc֮-t^(wQ;jY`lOkP&A.ZxUVҀGcv?v4x>Esz?gٍ2L0YxxF<.-#"<tD(%FSʎ[>15n kM?/ 5S{X7&JHCNI8]@TU%-5ÈpD0<1¬j&3H0ŨM., aIT;Z@Z(.&8D-eEe3x;Ѵ5ͨ `VfEd*"1*"հ[FQHGB;Ò҄S2E xUi~HYjﯪTKຜTY+Y&L&e䎾}ꜨhLOFK _~QF5Hv'ȗY{E_`w[8_O/N Buy S! E#!ʯRiVsqISd0.h5N;ubx؎MAT ,(|cAnroIc`i qd&7ˈ/5YjA:O4.Ata6I0A)T2D I*^=J^|j9Z QVjߴܭ뢪dԭh2%qT []q#Ǒ'|c.o~~PKM|PK$8Object 9/content.xmlJ0}fh3%+!8jn--Mf[D|CĴ[itzM8nqJ BMrReL=N;}6_uj`GԶ(Dh8`ms&B0#4jP\qu @ nPLB;\*edcP,EnP 7WmcĒJDlӛNrDZP} /7%*~ԕAѕTG͂~*fiVQհi(LHA .؎nba)\+'KQFq&9] )JB]֞j7*rg_8;PK)UPK$8Object 9/settings.xmlՙQs8S0~I %@B2c%/^MmGO_ cJ1vrغ%c[W\L\Pd=sڶZ<) zַɥ5.G/R!^g3a$HX;Yò;iH,ej:]uOv^?͇zȖ4x/*B-H9jm-4<;"vg)BauyPc+"_c2iv>R_>{yY=ӟ}>6}ঞYv#D /7it++K}C&"7}d(%F[>25n kM߷/ տS{X5&JHNI8]@TU%-5ňpX 1܏1¬jƒ3H0Ũu., aIP;CZ(.&8D-EEe3x[Ѵ5ͨ `VfEd*"1*"հ]FPHGB;҄S2A xUi~H)YjﯪTKຜTY+Y$L&E\vTA@&'j/> )#'ozӴ>PlΛ~םd=݋:Ps^`FTH¼¬fx6@aTک\\EҘ+v41K#Z(rSĠX:ۡ)5=C³|s[Zc02x8M i #U4 .Ր,fQ&K HgBx%f*MRtbmD ź Q;yzaJ9F@oF[77jrN)6Em;w뺨*u5Z`8FIdxeաF5FvqdߘK?ۅc?PKjsPK$8wPPmeta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-93102006-10-11T17:42:46Nicolas Pourcelot2008-04-27T22:50:48fr-FR56PT11H34M23SPK$8Thumbnails/thumbnail.pngzUTQ% =!{ иwwwwhݽì53/k}WU{S(/"%)  uK} 5OJTH%4õZvН8'4& #bHe;yA/QI x8%G@:KɈAe@SNd<>ߦ TwN3w+Yʐ௘n?``> !cu=DOu%R-JD"SB!4(߇x0pXE=2xM~{}ͨtXd|0 MERz|jwt-9 vռ9Cݚ1Wf8MݒyE/ "]/d뽔N+S V~[͵o"}N~p^cѲj.h}=^G&kb<-dzo]%NW%k֏B32LjY\v |w>PϦVP#>*VF,$])خ6H*w Gh9,xowb|_\Y a;.ܤzDN~]F:vng&W]wbn%V!u .Y-9m۹m׭؅_KL''n6ٝSo?gܮ{=[^=U4q_]PLr]tljKjaKB㻉6W{yӟ~hX/\>|S<8*=3h< MAa[3Ɲu˵t61Mo`/_mU]kg؈ͬB\P..'hM>ErNSL8Deկfl9Ⱬ2plr/p ,k:zɊG+UHDTL6{*3u6yVG|ŵWWD2^V7pJ̹M}V{r% f`ur-̭''q/ҹl̲ qe ^I m8pXO0Fk {Ĩ HLz-jy$>ziK8S _X9VVbuㄒR$uײjQznWipڰ\{=S!3ɨ+pVg\Oϻg\ gpS/ZLL˷=UZ}E8riB%MׇHXW&;%.M3IN$47KE1^3LAh$gB=T3.B1 Hd.ð1A#?j ?NFSi<NM5 eNӧPWcLrǏF/a3 >,+:zU#Vdy׌5nd (픟I_!Tբ̍.u/3ނ>}_RZQ Iv5+红3KݒL{B {xsSHgko+oh>bx|x*rS n4ѹ՞wb'S dZݔ9 xbxMxQ gnj,gYJY%DךAb YyZ` $o|Dz 9qCᘔY$;]bcQTefŃP:5J*Eև*2CSCLWI] cdeYL݃[/f!@i $P:=D':!K=k}a(䢊6]RY>!f?'ya I'쭘 |~G&YQ(/DH: Όy5Zr@)56(Tt;5"ɹ|_>3[˥Y[>_ظtcȊ<-Pv(VU`VfyD857v) QuS/MA/6!uv5&nO },uVEOJMgl{-eFS8"t:'NQתse.@'3> \A| }vM8 EP"|>m4ޤNz=@񱺐Omi!LBnsEu7lgwøq[[9L!1SBp/a?ɩ]U^tIUYEY^n""L>1_O6o!|4Nbc[/rGWb:Вi~l"̷A@{vwܡDYMU6mά w)M/9!$/NX&<2AIYkww<u|6SO1Z%y SxV9~_Dm'SF4bۥ i+"B:y+mgwm~RGc!HM _諟'z'%:>cRP#)O8q H|HJS SV$_yBtľЎ~02 E:TxP]]]){sN{A\VQԨc\\ t<l#  H"vDUmtGb5YTS۳e1&㩻 y<9wS@6 J8{M&Pj ံ~,R6BӺ=4Qna\dl şH0 ~}>$o~6~n/%=frfಣ@>>iCn"z GqGT (&j}&!b=Ԃ_+>KdIמ¢&zgGPɱBBY>8ޡj甜ЋǃE/ӤV oF' BIfڍْE0 S%e1E27D>)9z9_xl-Á/E`ʿn%u*PGQ"<~kP.1 k$-_/e^sC J0exk]ɣ DXmvNV)SĥВ}nѪ>U]s!@5(ϜNZdC~K݀#Mu)ՙS ^%h_&&TP^-JJ"([CH6GAoRYBỾ0"7 bLWãbVkg';; t2*wjYsōC ,HθN0.EfTHI_)1q6z] aϊ #M"KQ;!!g Ir3>ERˉZj:⦯e ^lnN ōg$ftxHЅTO^-^:J 6'ͬo>1+%$E/dº~ug *ƸH)1piTR 5{N_`vE2q|g%A?eט voL{AӎScYqzO/?*M@Z|"%flSRd0vLTiӀ/y %v .5zOid蒳56vX^dB08'n*^"^]x<%=^ݜ88NCdU>3,w%;b%O|n滟!FG"(Q]mMDݽcɂ/O E I 6Y_=M=n̼ENM3Y:rW%z9fH*I4`#h2eCznɿ('x|AQg)3xy(}V03O^8 $&(;=8Ea*wP M%MWO֜}]Qvq]:cdό&0.W3kklMwSӬP~`=ܪi.-Y0#2愙UQE. gi/Gw>HW31gieAa4% UՌ swgwlٻYea&rf-ʅ(4G&*+\fsu4]B^qjMYm{:j҈?"+kPӌ7a>4r[Ԍ o|Ky/KGifP]9܌WXI{\u|eDФ'S4q! zOW8QF mZژ;.R ex} Ƹ~Cc] B ̄;NW+0%sirݻFҹa1`m=fyi˂ՁVXu43%r Z*>gNE"K"Q=DdHB˓I!Gi 󘃨[*mxXB"P1 >/N)rcevxnwS$4>bLn҉njϜ>I!'9iLPo_ ^)6J(4n4EpA8-[ɼO~c*[NYr# &C~M>'L$ ߇Sگe`_T ?)Yb["~$a)m.\>i%|(3 ( \g^mz F.\c6WJ)(dž1LJd'21Uw%ɶ`atA`'QBUf` ^Ä0_o$oEnU} q EӔm]]Bye3uESOfC.o2$|3bdilQN5abi7Ica춃!fyDa;\щQE%L^vq|;or*\q|q 󼺖F7<^"KAt *׌J9 BY[ f!;Ê}MV61aG8d }Ku:>bYeK> gjk^]TtҐ8x; e3؄.6\IyheZ3o .Ir=TԗRݻ"Ay4ǁ0 ue_-E"q>[Wf3s8oMV%M#gMŒӬcAa`̳nOHTT/?=Ac@982F\t?S;JJYݽӘsRt1xkTjq^ݎ:*\myOdhFgFsfof]~־^ 21:W%F݀10\ts@}_í5Ӥ3ϫܾC0 ": ^PDcQc%]S( j{Qؐ.~obe[չCE3пKmb+)l=/!,X )]}la6Xp^vt\?D16v2,Ia3?;[0J-3Śf߼?YN(ک)vWgd#Ŏ׋"`VG Dƫ3(EsV}QhE0Vɥ;_anbQ9a}.sc} & 9n TTlm𪽗.ӆemR\\כYhֳ VdBIT\\,xTi1Xsmcb8:;t_t>! 1SI#DB\3̱ESf}]W[ p!x~f m֕"Ti֏cEM߷+%2h%3~#&-W@I೑L˩O8w3)-5ҏyat[A7R?IG@ɤ^1ҊY/8 f`?@)E%̀ŁK3'o9TSr^5.S/s> ɾ*|DB^.PcJ~1w(Q)Qf?6~"Gr֨ ,\twRD9OXd1Z+_1|t&ӾO~a6H)JF50Cfl ]!y ",z_d "K~ gQ)E :d1K"D]/›!7飳=I:w1Z%Pz+8ZOlyo'NPbHfF" 1qIܤ_׺v!JWZ&vu ] a+!8MzFm .V.&*j"QB@9\0ra6 2j!;h|E KhqC c ka✉nKZHf,/gX#IzɅ,rFzb`#8ۥ!GN]+UV62b{. in0]. I^~A!14+-۴õm`=F#Zl<\L䣉dozB, UeSȯCs[f{bW&aWIbRShVK7+#㞌FsLي0rrd43v1L{uk ?}5ח(-YjUK+*(!EK$oGS7 z*?ݑl0w[(5¶4}ؚQtI~C%;ޑS|\o.,xHdƹl؞_r35%_y_wsj+ D~z"~̥o9QeO2h6g6rjX Rv@P [B>'Z%|. m7G).BÄ*pbX~us]m 3bCWJۘ)6h1DbBɦHݐ)<=M7f䧻ek!ψ0liX4Tn4F`˨]DDk)лSTw+}*uɬx< WO9Tl[!:Ka9_zTgz3X1Ʉ sOH*W`=h?>l\ܡ;[.tʙyzÞ@u7;2`mˍi)tB]<7%%15|H`IRR(8jTt1/1-~p"2&01HZ\e_0 &F+Eff ` LݘUkVʗo/4:{7h; dL}]}"lWtMl:ÅQ>l?A Q%hOh7{ '@l}Y)tz nC͟q1'T)/~[Q4^ߋsQtHfޣnHM0S pnؔ,ćk_  E)[QH:ygJ~Z^Ydvϔ6um0XΞXiiKu*AVD5ܿHm鸊'fpXj 0]:+\G&%N_Ǘ0TJT:U 7k4{m: hNlQs'$&!Xq֬{֦ %$',ݟpTӓ/.nFvV1X?_z-7VE`~*)7jɈj]x_XM/uY P'eYd.ϧѥ7ߕM[;)&gǕ{ًdd̆،9(۬<:by TR^Aø;O>p@b&a C BU2Fھ"$q@+1鿣L+6=wP%:b蚬i2.m$`,B0A;^>D\ 5Y~Fcf9\\V@=GrSUǫHgpPΏJCܠ9aԋfP3NmSnŴ ߘ/;OTJQ3$8urt>Ei Od|_bl=zu|M""bZ\s=h>FKr?JKI"32`W=-Mn^o='p*T9cr8Փ%{tϻXĔcuH} VQHyKN2 lqd6TXΜSO{7ZGWZEB B2hT:coKwLiۑvً)I&ŵ}&մ[׮j[a@5ۻ}G.fq=\"ezL#٪(dIp:eluU*iUݪJysfjyl*%jCٴa,Wˬ,Ěv3N!Uxorj=6_AxBZY]Ϲu׻u&4ɍ9w\]_WKc-IUeuִ=f?ɷV~d:+Ri9k0"@\ٿη5ӐѶV'a$6 [ڧbg@a n4WڳTΗwQݷ2{TeLln8?2iq}y70kIHs>EE⢛"6M(COʒҺ5hz&p]CDDwY@h՞Sc/ɛ^vGiQM5$\Y ITO fp&c 7 8/x=BW35OfLjY&Xz'kX4 $uM'n,D6~7ggltoW.a ck%=D}Xu?Dt0x`cX&8 (<_`;6Ӯ'h>W)N8w︪AӋPҲ~N=@&Z1>51Ŀ6DÏzҿI*3v+O.&xΛj˿2 X4&VcZ 7x[԰*|Xw<nid);KJ*p 8!=^U`^W {QQ忢\L"-Sݫ#3χip{CᰑE3Dw>,HRC;_8,"E/Z0Z^alzUOaI7J :%=E_n-O?9臼E{~T E5j 7ig<(8Z ,0v<sYp.DY5w<Ơ-yO43 дE.,u%Gyܸecm}bAX m8=\lpo(-1rfK->!6f@`gJ1c) hF "s/ KLcЛXۤ 39'HD٭ޝlY:Eeѓ\m8zvotO94&]q3*E9?(}- U}|r{yʽɡiCHɋV PKz~12PK$8 settings.xmlZr8}߯HpI2;P SLH$&j$mنp06z"nu#\W=xg  xϣN5N&۵Eb>]xr%gN3\ )-cDLz 9- I̗,*qddZpk(*_i+U_:vYkT'X>uo'F[K~Yt O,m U\XTV+9 ! Er ^9bxO(>SlĨ ,ߨ8'P a,6{H%_"O| ` xpyBfC~՘!H"0EeO+L]˜MwR@[O0ř܆`%m@d|T/#OdIfJTJ=-L{#6y@[q}rO7 =6uGw<'I}`mp / &"J2Qg꽯5SA=6쌙/-.!,9a:J-NaƏ 5ꈢH Iv<meLiJ6A, })$sN|" BbOVeoIAmuLt)"'ҏ"z֠na7߱2e ;P=]u] ͊W'w!פ L }wh0柹ɿ;m#_ot/m_ UJE۱RM-0!*/xo ME&Vpآ7Odp'2 Y)|$6:lY1!,ͫbQORZ]R =f?-1pE5c1lpMD .8~1А>;}6!,rS 7FYMEG:٪PG$]N9i_\$>$f2ဂ~e:Zϙ /'#ֆOVm+7G&c&Z ؛2F^O8eRp>8X췕Vf3ْ"k*B .ou)X6U˃wbnUNr%Y`trIuOPHgǧ(w.@PK\I PK$8Object 38/content.xmlJ@O1IXП$ Bc %4 n >1&/,3&.2#rкc TC1Wr߶+H9ja6@D =_W! J~zf* )dݑ_CD)bV>d2J†kSH"tކllcc(ą"6MU'" iEaJg)O9Y^~R\__ϼ\)Mhn<^gU+#(I< _Ko^PK.1PK$8Object 38/settings.xmlo0WJTЮt$XM|_?ڒeI'Dl|N:oYj70=ʖ]ipvcup.8QL Ru 5 'nZgAH‘!d󱷳O6>e]k%ez>__#_ڭvmZ.]{D!ē.+;o5^D~?gTB}?SZ QXfeǞ#@[_@ePs6fSr2su 6bL6`^T;w` ;ӌʈf5iVDL0Bo"bQ hTJɣć8q_A: ^1z`NjVd5BMxyVZ<;_ۘ 7د=07e/vD[!{[#T\4i.rQdrB5["DBb 70qAjŖ"x.yɱ$@k^ (|ËKK=hB1z|E4A7 =o%TB7q<>u!2ō-sXӊH~m ~sTXpSmp^ɯ.qFt%hw F ,Z7L2N-;Lol^ZV}-+^JeײkYݝFˣsqқ0g]|AE*D-@/ M{["pz "zcpzc荇'kI.O7dA0()=75 z3z18z1 =px&An!Mp0?`a#y4;l[Lpx0ߙ``3X;pggushts͇E, I㓪V R ?\>lLXnM/1/PKK,PK$8^2 ''mimetypePK$8MConfigurations2/statusbar/PK$8'Configurations2/accelerator/current.xmlPK$8Configurations2/floater/PK$8Configurations2/popupmenu/PK$8JConfigurations2/progressbar/PK$8Configurations2/menubar/PK$8Configurations2/toolbar/PK$8Configurations2/images/Bitmaps/PK$8&:­­--Pictures/100000000000069800000489822DCE1E.pngPK$8:ObjectReplacements/Object 10PK$8TQObjectReplacements/Object 11PK$8g#(ֲObjectReplacements/Object 12PK$83ϢW(ObjectReplacements/Object 13PK$8qY&lObjectReplacements/Object 14PK$8/\&9ObjectReplacements/Object 15PK$8ӢS&CObjectReplacements/Object 16PK$8e^t;RObjectReplacements/Object 17PK$8>M%ObjectReplacements/Object 18PK$8YObjectReplacements/Object 1PK$8fYObjectReplacements/Object 2PK$8j,h 60ObjectReplacements/Object 3PK$8]/'ObjectReplacements/Object 4PK$8 tObjectReplacements/Object 5PK$8xˎ>ObjectReplacements/Object 6PK$8ysObjectReplacements/Object 7PK$8IKFObjectReplacements/Object 8PK$8NObjectReplacements/Object 9PK$8e1ObjectReplacements/Object 38PK$8ym @ mcontent.xmlPK$8ޖObject 10/content.xmlPK$8aObject 10/settings.xmlPK$84ImObject 11/content.xmlPK$8 {Object 11/settings.xmlPK$8OmObject 12/content.xmlPK$8"Object 12/settings.xmlPK$8# Object 13/content.xmlPK$8"Object 13/settings.xmlPK$8㙇 Object 14/content.xmlPK$8W Object 14/settings.xmlPK$8´م"0 styles.xmlPK$8Q0 Object 15/content.xmlPK$8_ Object 15/settings.xmlPK$8U}f Object 16/content.xmlPK$8=Object 16/settings.xmlPK$8ɎObject 17/content.xmlPK$8&(bObject 17/settings.xmlPK$8]. Object 18/content.xmlPK$8i%]Object 18/settings.xmlPK$8V  Object 1/content.xmlPK$81d"Object 1/settings.xmlPK$8t&Object 2/content.xmlPK$8$M9Le(Object 2/settings.xmlPK$8 W,Object 3/content.xmlPK$8Xru.Object 3/settings.xmlPK$83bLh2Object 4/content.xmlPK$83Object 4/settings.xmlPK$8Object 6/settings.xmlPK$8OvcBObject 7/content.xmlPK$8ŝ1,DObject 7/settings.xmlPK$8_oHq8 HObject 8/content.xmlPK$8M|IObject 8/settings.xmlPK$8)UMObject 9/content.xmlPK$8jsOObject 9/settings.xmlPK$8wPP|Smeta.xmlPK$8z~12WThumbnails/thumbnail.pngPK$8\I ȉsettings.xmlPK$8.1Object 38/content.xmlPK$8AKdObject 38/settings.xmlPK$8K,META-INF/manifest.xmlPKII&wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/fig1.png0000644000175000017500000045211612014170666025114 0ustar georgeskgeorgeskPNG  IHDR fo pHYs.#.#x?vIDATx h3x@bm-ljL6]W^|)u7;D$nV*T jS fN#jbt:LWT?t~wa׍???H؏ Q5JVP ++*TjffƋ eo͛6V׭[wE7Jlij^rSU-U7| %TUKO{A`u֭SSS^{ SU-y Y[SZ:<<@I0Ur4yylКգGz}Z=tӀ<2SU-kǏUVT֬x)YZ^Z:66@4jҶ-Ńʾ>/H"TU+Ӎ+y:qBe @j%kɪ7MkVSUp Y[SSszժdrpp;br߶1f+g2wס5{fdXWVT֬f2GTUqi(=pա5t)LU&/}BkV= `W7=ZgϞ|>8LU8~,f!:TU1mٵ55Op(b6C]( |TUjnGZBfkV Yrh*}gNZu_`5Ligҟmi,ApQ@̙JdBkV[ZZfgge*֬R@W: V >LUgVVTV٬ &LUox0Z~a'@Jf1`Ӏ{{{"eTUݮ&Z=9Jʘtg?җ*R\.4(Ww Ń5k֌8Pʒt7Mg3۶4V wSU.1}3OA`*@y3UujeEE`u֭֬SU^}ښ뇇/TUZ&/5ܑY__# Jf`uvvAPLUı#5TʚUf*-rà Ńꑑ @2UlfЧwww @1UC<=j>wSUii׬& ֬SUii}MCkV>TUZ5M;wX PLU%kVXd* 1U{Y2SUi LUSO|(LU2YZYQQC֬|SU)kV7Z p;LU6lh*2Uۍ+[ 0SU)ʊ kV>?;yFkVnTUҟ֬6a*|fSOZ b*~ZoʚUOJ 55֬|TU-<{W54U@y: SUI uH_eEE`u\ ćix0}Ńd2N@LJ&>mKcf]]]P LU%V7|`ڵkNRJ5 la 1SUIw;gԄ֬:Ore*鎛<ִsGӀ;;;@Y2Ut7]lk}<4Xݷo_>weTUbYwTU5 1Ud*BLU%Y SUIKf58jf*i׬:ui .SUI֬,TU2Ymiiu%TURAPpLU%gWsw5> P(8;b*׬v677[ SUIf5YUZ\m2UT]p>f5Liwp;LU%Ţl摇AZ |.SUI1 fuvve,TUR:yʊj*fX5rMYnYmڹ#q.0vcJǁ޽{(f*)Кպ1)SUIq5CCCn􇫹GnfP('SUIӚ՗.jKK5$7KVU֬- LU%)fuCmm`5LiĖ$fmi,Ax2U[Y=ӡ5{fbTU>G*+*Lb*I 555Ńꡡ!ć$}NǚvHܬ1a*If`evvEeTUnǎ֬R\..f*Iw`zCmmhȈʘ$Y]MO. .(Kt7z`5ϻWJݯYMVUVYc*Iw6oZ:44vrb*IfikVJ5BLU%ɚU`!kV7Z eTUc׬vvvZ TUYa*IK[ TUuJ$Y ,TUV`={YRa*I֬ 1Ue֬@)2Ue]mK5PZLU%iY1} kVJ tؑ Y`*I+Y`*I֬ 1U$kVJR׬gYD$Etjee~+8SUIJ7͚USUI֚ՎCս{Z +TUf5YUUdhn*,'SUI*5ŃT*533a*I%`zCmm`uݺu֬0U߽)i===n2XjTJsAX TUJ'^Yr4X"d*SUI*&>mK5 LU%T1}广f$v< Ywb1U7m,VWW[ TUʡcM;w֬vuu W#SUI*OM>Z TUʪGYe*IefuCm5XLU% fmif$g7tX wTUʹRYQa* SUI*FΝݼic`2N6JR,֬6a*SUI˚ՃO=i*SUIQ?{7YUe*SUIW Y[SZ_ҍTUbݻYd*I13OAPe*IqoK۶4_OJpc V]SUIgYmhhf.KbTU8&fupp} @JnjXOte[pק&Z Vۗ]Đ$3׬&uuuNTU{ΚU0U$}ΚݻY LU%If2U$L&*왪JiSܦ7SUI5SUI³BՖY*eTU8kVST.sP~LU%Iw{P[k*eTUkV_~e+TU$kV]w%Yǂ (Ս&SUI 4 5.f"TU2M|x1``5w3Qc*IZnL_io{"fu޽sssnhTUkV++*֬okV{(0U$EݻB/ 5+TU5/<{(4Xmmm.lV$)Z~D*full̝ J1U$E[ @tJt6͡Ofg*In׬ٳgvv r2U$E3'߬(RL& `٘JPzիCkVO:`yJJcЧX'Ys$zКՆl6F`阪JJsg7V pDLU%I]MO. v$T׬v677[ 3U$dUUhj.sLU%I CkVd:vXLU%I%t6Ń z{{Y`QJʤ.КY=TUT>wpLU%Ie^ YspGLU%I CkVJʼGnfP(x0U$bK?A`ŚUn$).qX*fu||ܻf*Iך Ńd2N!`x5lX$)kV=th={YLU%I1ʊj}}}&SUIR|}ښjuuАK.^8$)M^kڹ#q.ov݇=X$vjǁjKK7 '۞l]N;ZJr s9_k1%Ilx06fuddK[ښϜ`]ϦݻB](i֭[C_>|cg뒤xv虧C7ckkk> knn=ۏ~k:`]5ɪ⛱UxJ| (~|@}o뒤x6͛6֬ y70<<Z:sM%I]ڴs51kV aY$Yj*@<%W\WsKYf nYʗ.^8$)kV7Z C5VKkV;;;Y(o-׬%I}ޚUXI|ՎOMz.Ig'On*@|$n_mK1%I֬~fu``{ _:ښwx.If՚U2_Y$i5{f$7rj[WsKYf$֬>P_{.Ig__֬]kVW'=X$ųlf&kVO^~-׬nL_l]dͪ5!qkV}ŚUI5֬Ģ)\:ޯ=X$Yj*@K,t5o9$ɚUkVJZbkV?>$ɚUkVJWbq[Y}fkV%I֬~>zP*KrG$ɚOTVVWF$$Ͻ뒤xv57#-֬׬Aݘٺ$)kVCwYH,\[[[rZ>5$)udhY2ŏcKoMod2_hJ,_3::Zf(=$)Mg3w5Y P7nͪ$)?җ*YXj}G,~|@}G$)돯BkVS"_-׬9뒤xv}jr5&Q_-׬^%I]q`Ƿ5 Q*_\{{{r跬Y$Ŷ'^_>kVZܾКկ5|u뒤x߼yFkVT5kք֬u뒤xv57Ge*I=55uКտNVUY*I|YXD2rCkV_>$)}χ>Ρyvv[(oP(:t(s9[*Imo>ZqFkVB硟ZWY$Ŷ~yƛ>'?LܑD}?t:fuժKt6׬zp-e2T*ZW5xfܷo_>q&B=I%I]*ǽ7/oum[?}%Ils֬܎Dۋ/nܸUu뒤خYݽɚU% rT*U8W{.ImzҚU$b=۷/~kV%I֬Yd2*1SO֬6fUdjSNy7?<?55Zݘٺ$)`evv& '{ =>n܄$kV?Jr @I%I֬~.xĄuvv֬6=X$v5CCC3q`YMVU9뒤x6_U(mʛB2L?W{.Nyo|;H ?Υ]$I$IKȏC꿞;mxFQ>omm VZfUToIH$I$I0U.kV%uo5J$I$IWjt5kV_z(=`\$s›]I$I$iQfj_#}&F;>}δ~M*XY?GG]y I"F-U+UNxIzjA* B7BOԤ,(U׸DH*6*t;+7y:^ݺl#΋{TV]w_|.ۛ++ zD4rfu-?9n|Uus٠Kdcgg2>5ͻΈkUdUܼ`Ȱ:hXU Ϊ} n8 3m2JZ"8qbdXV6>v߸OʰjD|ң`UdU x<<yه{uO^mjAӀw<8C=&VUIV@{rrr;^TXU ;5^>|T)j˷~VUIVPkll,**|.׳`U%3cNVO~ UUU5КϬNքOCZU Ԧ=W=gUUU5Ϭ8{?B}* \/#*ɪjD,rflw>= ZggVGqUUU5/^9:lo,VU۞л-3x3UUU5vڕ*XU|fuT3߷ǃ`UdU Ç2$̪BB3OV.MV]+7$j555]3ʦ: VUjO=:>SJ[EEEdX-}Łw}!XU!P;V,6G+f}vh*ɪh;vpfqllqgV[wYv/|~o2>5ͻy$UUU5ܪY*pLϞ٭=gVw>t3`UJێ;Y*p̾JF slŰa)RjYdU #G\yάUH>ڷ}rPʰOu?JGJU5КoVgV zIEY3F5?VUI?ǝY*p+{vtKaj|?xVUIڮ] Y*p-G[>4eX|*IVP;xY*pܜ[o)꺫AdU GEάμe VUGt˞/zf?8VUIO<*XU3ݦDϬ*XU%ɪn[nˋYU[I+ 1zfup*IV@;|_*XUc>Usd]2lLG`U$j577O6͙U=y]gWzfudX#;`U$j-\ЙUǽa+zfJY$YUm׮]άU8iƕ\)bXʰάUUX\\*XU3k`^GGϬzCXU%ɪhD̙Uǭ쑋;>?oxУXU%ɪn/ άUBjuq7_= dU M68 VUwV33P*XU%ɪntfq~* kF_#XU%ɪhDbάUH>=7k~YMVUIYsssyy3`UϬ),H?Z;;άUUuּș7o VU-G=Z5^VUIڑ#GեK 7k~au6oxXU%YUngV ݞ Y-yr# Y˞ !Y!|hʰ桿߷#XU%YUh8p`򰚓e !Y=z7J*Nxfu-?mBgVYmgo`UdUU-^8rfxקUg{谺G#J*Ϭvŭ=yU´vKɈWV KV7 ۯy$-j<_Z AY.Ҕau͘^XVUIVU Ϭιk3`UϬvɚ7eX]5+pVUIVU[ՒW|^'rBgV gV_3C=UUUU~fo7^Ցbˆ Mw{VUIVUZլmW.{9XU!3igV]}_nVUIVU ϬΚq3`UgV#_<UUUU~fuܘoC3)ʧ: VUIVU[ݺxS;XU!Lo?fTg'~R XU%YUhgVVigVY$ѣ_};`U0{8 VUIVU) Fά8{<̓UƧ /Y$TuuuNNNڹSꭞ a[digVrQ șՖ^p'{B~cb3k*XU%YUnMMM'N NyǪjU0/8M}?xVUIVU\ՒW8Uժ S?)9s&8 VUIVU[uuu~~~OXUwj6;C6FϬA`UdUU-X rfu˜YŪ |fGe3uSGy$mӦMyyyjn]xeXU02l@bRϬn**XU%YUnEEEjNvڕOz)Ujf4řUJJ)%'FάμeC@V>\17ꞵK<|ș xeUo2>=<UUUU }=}RU#Z!XU%ɪpYqVU Ln|gA3;!ϏXU%ɪP۱cG~~~ڣz XU0۾m¸Qcˇ `JUUvÇ'Ymۮyr XU`-r[=:ؙUVUIVUO% gV$wɊYmgXU%ɪp1%~ @_٧ j]TgVJUUVWWڧ}SյW9 UU D"1qȧ:OC@6-!bgVJUUJ"'8O;Ī {fu@S Y*IVU)ꜜasNoiXU@o^=Cl3XU%ɪ*/jK?}ҋ @gV]2$YUhMMMau]>=|K @gVMVWlSJUUVQQV/a{VU 3{GXU%ɪP{U[VU 3#ά<(9 UU ș՜kVýUj[-qf$YU%'F> x֌ۜYŪ 8*VUIJnx*.άhTz<*ɪ*[EEEw'OTY-գ zfuC}GRUWW'{v/Vά&;[/{`*ɪ*Z՜lgVY~A)1߯$hD4rfuf;Upf5io|(q#VUIVU)Ț,XVj'{UjEFqf$nuuu3}׼v iocFYrq$h C Y]I/_!Y>9 rzR%'F> s |f57Ϭ[gVJJVQQǓߚ*VU X/{kL@|R 999ꠁYŪ 띚c/~fGR566EάnXƫ3: UUUU 3󏝑ê jʋ;Fά?ȃ?VUIVU)ϬSq3rXU@‹Ϭ֕O3XU%YUP۴iSjkW6XU0۾mR1V KVXݞJJX\\9~*/nY]3UUUU D"QVV9:km>%VU LUKt{齉#ΐ`UdUBmő3%8razc۳zH?Iά`UdUm׮]yyyjN^ܺ 3s7~'R544%Ymۮ\:XU`=4gV[EϬk9mY*ɪ*3VU*@%]g2: UUUU  DUgV!۾~ ϬV9#RUUU9jUvK#;ĖMVrɑ^ UUUU bgV7֛ztpʰft;gVJJvG> x-?9*@V>{^~O޽^#XU%YU@knn.//άZUՂnS Npfl`UJJu3jkH{faFuL;zCOw{BZU%YU@khh(**rfժ 1쪹{]b.uf$?t;jU~8KSU#ZϏVUIJpBgVƧsgY\dU.''ǙU*? x¸;V K=:#o*IVU)닋YpCsf k`gVVUIVUI*H9jU=rqG? x{YJUU ŋGUgV!YW/?Q١kdUmӦMάZU8njsڙ UUR5668ЙU*嶪2>uy[gVdU-H?ޙU*gVf49άXU%ɪ*Zsssyyy߫rʪ >gVkǙU$YUpۺukjάZUvK#:DϬVM}䭗=XU%ɪ*Z}}!CxZ{JUU 3Ɣ8jUspϬ拣w: `U$nάZUk_3bO+ SlŰaT`U$h'uUTdGf49#dUB-5W;/Ŭ`U%3];OVkf~O[VUIJv3{^{٫1*SCՑbˇY[dUBmϞ=]tY]2/ȬpNW%gV{e*Yumr*IVU)Nxfu-ӝY¹~\̟YgV}dUm֬YάZU\AU?J^=n8:8g3VUIJ~fsN/nřU΅Td-%#^[1,eX}n%}5OdVUIJ~f5my}fU*!Y.35֬\`UdUh'<άZU,Y픓5oӀ_lOgVUIVUI~fdWUqUղgVdUTվ}xejVUՑbˆ '~5$@;5O.rͪ _Ѥz |Ղ3kx=XU%YU% ϬξgV`UřȧynVUIVUI~f9jU*άF*UUUUR/ݬp'U*gѾƏ*UUUUR:kVyfU3fo3f53VUIVUIs'<YB&'U*=uOgVTyyy<OVǍ)8*XU ӫ.Q𝋢j]TgV­:^JΪ D ;>Us7vfىάXU%YU%[CCx3GkYy*ɪ*)КJKK#gV?;*|?]jU\VtIuf*ɪ*II-X rfxwyVU0} YJJRR6mKVw+YU JFv-K=ܔK~k(rfuú5^YUpάXU%YU%)D"1q3| >*@gV :Fάߗ*ɪ*)КϬ~W5U LׯSX*UUUURҥKڣU*@mv7!ܙU$$Çwfժ qOVR5צ'3`UdUh3gΌY}YGϚ/iάXU%YU%O<<SqcUմU{%3VUIVUIcǎș= U0o^}e~lŰ37sf*ɪ*)܎9rGά>yU`-}G Q`UdUhG6mZ̪Ug3ki fm_җN/I r4KM9$@YqoUs۞;cZEb'Ž=׷< ՍO,6 :4wg{*ɪ*)vؑ )'+ꞵKu`UdUnxnlk-S^ܺCW.{~/|~uS9_' : >ؓT&g73͛1JUUҹ^EEE<OVǍ)9aU/?b%}ƪZ~˂w|_=vMpIluRZ˿;6 O:ٱ3Oi>Nv>#wVлԂvfuZSNɖ)B'x+N:vO:{/7dr=_ɾ3~>ofg ߼zD3ݪjm6Y>sUժjUŪ*IVUI~f5ϬZUVUUL>?vfuѥ)꒜k7XUM&k~}nUZUXU%ɪ*)КJKK#`C~!*|wUƪz) i)>?vfu^uw}wUݛtoҽI>]UwUUuW$YU%ZEEEdX߫{u֭@x,STXНU}a+u3/͝t*pɶbU$^xᅼȇ&׬p*LުϬZU9ra .Ŏ|IwX37'mB&3?>7qw^bU$@[xq<Y{FΪʹo;RB~,KLb]#?U ?ZܫԂ;9 U$YU%֭[󓷜KUN ߼:mobbxT:vmpVά[*VUdU ޚUU.///yܩӮ*dlU=S'\V'+[ŗʙQOpf[/{w VUIJ -'m XU!c1p^6m vYWrV.MVWnx VUIJ D"QVV9Ǚ>#gU%U/IAΑ3UKY9ڥ ά^z3VUJUURUTTDά~wOBVol{vPqȟrsMάY9ZܫGYAtX}q΄/>:*XU%ɪ*)Ъ󓷜= xeU[^1|]5ϬSWΑ34_f UdUhgVs3vfժ -{wjQgVlz[UYMOz VUIJ D"QZZ9y?3VUHV'"gV;omþ2pY}άXU*IVUIܼ`ȰzU>9ߪ zE_8;gV /gV?; {Ǵ/VUIJ_]]]j>E5*d ߼:|q Ǐ~fu便}5VUIJ !CDά]U_`֭*|}32pVάmY-9޾dUhDbĉ.XUشŽ$'T-]+gj~oY}e,gV$$[EEE<Os&k?jU=ozܻ}e ^ZdQA3?Q*`UdUp{rrrA3VU2gVN67|2읚c/~fى]*ɪ*IXTT9a*dز{۶i8l@=|e9ftʻ9UUUU);3pFΪ _޿xC߉v_r⎹gVw/יU*ɪ*I~fuܘOUNeFάV+Ƨ /J?IάVUIVUI M6EάY9*v̼qrǏ*d޾&salŰak|jwv$$Zcccqqq5~FΪ ΋Yl׷<+woO?jDkgV$$["(++;SgVpvV.9;17'gO@U-]ҳ~?*`UdUp[xqw'O~*[-יKF MNlۦ¹wƶg~fJ?;߻<UUUUm׮]yyys%{^{٪JPΘv[3;O#Ysjդ޿}5oVUIVUI -s߼U5*Y63pV<4gV[Eά)ixJJR555;6sr̟%ϬZU)ׯ)?rfO?#8+gVeY}Gw8 XU%YU%)К#EoݪU5nȟr0dmaAeꑷ^Uh 'ufժU+:}yߌz: ߘ~fuWUhDgVXU"5?{k[[dy?}vH?{άXU*ɪ*Ixx{C7ֱfXIcYrfaFuL;zCOw{ VUIVUI ={\tENj,:V [Vqo6mծY`Wo[tiʰڙU*XU%YU%)䚚]3{u7ֱfK,3p.x2&nUbu!w_q۾|-^R1uµǴGKF |߿?bȼW7>=/>/>: #*XU%YU%)Z^E2nظNoyO>yBdX-=ҙU8+ߏƕ\=;`UdU$|h쪥KKV*xhάVKRϬqf VUIVUI yU8u`d[ufΊWt* ?HAܻh{Y8#6[3l1- UUt-]rxșiOgV!gVKG|[o`W/dΪzD՟(~VUIVUI nUmyxW^>]Aάq#1)ȼwV|][zffwjUdUV߷ٲa3y*mۦMZԣ`gj_ȼ{VRϬ>uygVjUdUWc_w#/=U S?3?}~_8+gV/>>O*XUgaq>2lO|߿W2- {RZ;;άUժ*ɪ*I!-R5^806wǗ=uȤvLQ$YU%)UvRșk/jaoCUo:yXЧ{hά٭LV׌n*XU%ɪ*I!-G`zo)YuȤ=Qеs۴{y}{άUUܪzw+tooC&Sa"|}e ^tqG*@w*IVUI nUmqW&k}Rۦ|ۙU8o^=CsS.qfdUW߷gw.I~xظm|)d޲{;/yX-[񕁳ufuѥ)jgV*IVUI sUmGvڙݷI/{2rfŽlZ^+G}wӀ_Y4˙UJUU[U:rfual:zI άoi~{@潱A}Ϭ4w3`U$$8J!rful߮ά1sgL#3S'\*d^Fv5]Y$YU%)UtՅ 1%ȏ@HH DH  8(oQ䗡 qs-*V-VZL#'Nz+{t̬Y}=`U$$EnUWgV |ጌfcU ӡgVS+<CΗ-6_mZ`U$YU%)rEݳ|HRSn^ H Y&rf_F;{; XU*IVUIZxbxJS㲼?d_*VՐ}vqaՙUHwc~M ߐ#NVUJUU"3gVݙU %mYn@Bάfij`3#.}uSUdUȭվWjv\Oձ;f; Mjd ؓs<`U$$EnU䮵3 ~4bUM__?HJZ2kWuf4gV*XU%ɪ*IVկսfuoU{;jXÜYxgo=zۜY`U$$EqUw)3#Y-*zWYUUUU r-O b7hY2!dVi?Hwfi)άVUJUU_;_W[Wޟ{dάBȎҧWZ^_;\2/˙U{~ѩ_Tg/VUIJU%4<l̐S{agVwrۻO[{^<љUJJ/^mS Y:<Ξ:)pfAY}0FgVwݔ*XU%YU%IQ\UvN8:k5K:)Ɂ3vl@;әUJUU|]n|ޱy֭ZyxW·lEϬ{rY`UdU$EnU+4jXndx#xϤ: D/Ѫ7vmjtfG<$$)rjN>6-/rn`Be֭Zi]@˼eҳ-JJ";WF}b :ҺvqfWw,eA3`UdU$YUv|C3rb+sfc{ u{3`UdU$YUvkdž] H!3gLi*4 pxK_zVUIVUIRVz~꽝%"gB[V/`B%)άBSrlgV*ɪ*I/4>zOOF;wTvQ s{tsfșմ3&jO*ɪ*Iܪz/Vˍ^Sa^֣`)x$ЩG^=ęUh TVd9 VUIVUIU?bxJS㲼^|U:Ξ:əUh N90a C;69 VUIVUIU/>93{KgV$jVYfm^<}=G?~Q XU%ɪ*IVտu=0ʌw;ԪJgVӺv Yݰ|WW+^oۀY ^dU$j~UC?5^U8[U GG8ZF~wt~V(ⓓVUJJ";sx3Ò?5(vmFǣxnU%33'6>a^/Ѫ oSlgV*XU%YU%IQ\U}vxeƛc7h{j֭e3}z9 lν9[ru*!YspgV)8w[nӂ~`U$$)rjڷ&5pfutV'_@jU%|s*4_t(hx ߐ8VUJJ"n tk Ho)άBSr-֑`U$$)rju?- fO HHeEߌtgV)ؽ~u64>zbj*XU%YU%I[U/'+4j4'vЁԪJrf/:d5>ϋ': XU*ɪ*IZǪ+kxGwo`ݪJfN. άBά5rhXyAauϭ9>`U$$YU#ⓓ/j\^G-{nU%dV8 M翨}S`Xv}GgV*XU%YU%ɪU/Ϭ>t{ψw;ԪJȎޒޣ3T1uAvۀi'eUdUܪzov+/lxYNl܈|_@jU%39 Mzu[ۀY`UdU$jDWz:~8pf,/vMzc{ynU%dN E@ލ1ClC~auN UUUUQ|j =[8jU%!gVS۴ Y={WجWpfUUUUVկ|ջc~˙U7gL^c{ά$'-HcwH}3UdU~ՕbcR#Ə6*$jnfiάVUJJUժyvdk3:Vo:lٜ{[$%wFP!љ<VUJJU5|T9)?U7oa5G7 ruHՙK$e]:$jQa3_t(*VU$$YUONVޖ8zS/`~f5GC;6{eY}W$YU%ɪEj=pCܻT~f5%9e^_;\P?gVJUU|7m.lxiN8 MrfufvgVJUU|3gV*43GtfF -ꮛ3Y*IVUIFytk{j!oMINVvTuWYm?5(pfol$YU%ɪEj_[81pCnrf+2Syx3-YDC6,vf)IDAT$YU%ɪ~k] #:?f?>8:{$gV!!萛kzzpFѹy*IVUIFQ[U#?5~jPltVc{ynU%gVG[_t[άֽ7XU%ɪ*IV(%9n-^UY+o9@[Ѧ ~>nq|ޛbڢ.$YU%ɪ jZV[0_@jU%gVS۴[_tY=~3qK{ XU%ɪ*IV'+4j ;v #`ݪJϬ3o9@B~ѡ_Fӂs<[XUXU%ɪ*IV(/^ygVGfv!IOhL3VU™ՙKeĜYMqWY=W7`U$YU%ɪE+SgVKhU)Y-*?}WBv11=%<UdU$j}I3g=X3i]To蕁9I"闾\VUBe6mgVexbკ Y}~ĥ^~֛UUdUuW̕ߕ[zTOխ$j'>蕁gV_*XU*IVUIF7_2pfhU%dsieSS=8?ޙUUUdUڷʯ8Zm[U ْYY}gN ӷTjo VUIJU5|__[e+ruөG8:sroW{q EϬs{VUJJQTV3׶Y! gV!|s*43SKY=>қUUUUdUs5P:0^)ׯ`BlEj6jZ.vl@ ׃{UUUUdU/;.pf؂wRٻvHY}b^ߡ{^ޮշ^gVJJ]gVd&0ܙUY_ oYZR|hBvjU-V^`X/>9VUIVUIU5jxi󈶁3|ƒuْYU{`W?zϤrf7y*ɪ*IFѹv: pfnYU<,%99~XMmye |k^гm-#?VUIVUIU5V/*4[7u?nj^HǬnY=~3XU%YU%IVՈ`άBS8ZT67-߇{Ej?$$ɪE5oTQΙUh =90; 9zǸ3{Jt7XU%YU%IV(:WѶqάBSe6m.޺+{zŒm[`X}Vά`UdU$YU#|]k ': M[JVS@Bά݃ Ϭ>[^#ά`UdU$YU#ˇ$9 whUEoZR |' +k|f<[VUIVUIU5jߪzfإάBS0ƔZ^qn0Xڿ7y*ɪ*IFyhY=c\TgCMcjΝ$DgVq3XU%YU%IVՈlͫ?.j\ojؒwGل@9ֵK")ѹ32W[٭XGy*ɪ*IF;8Pؔ}ϬZUiҍvxۀTrhBvܜ1W|˙/>9VUIVUIU5xkά^zѪJS6{yjW?zϤ؆gV'qo NW^^dU$*!KgV|ά^ޱ}֍^_y]8řUH n^dU$*9_W{h靁rŖμ˪ YM-pfze |vlyy}||0GtzVUJJQ겜ش#OgV|W:; M= w3_`UdU$YU捗*Fjvp솬˿7gV|:ռ~V@o߼gb=Խ7`U$$ɪE>Qy[^ۀVYU!dkca5k}ʼ2 uIi; VUJJٚ%auzÙU*97#=V@nֹbGY*XU%YU%IVՈ8Pؔ}ϬZU.K7aqcYDYsEpX=8\<U$j5>ZOU*]KfMo?^5h=^HșՂ '<U$j}~Į"gR%3VUv_ڦMzy/?啁yxAW l[9 VUJJgVψ=|*-}z-͹+;csNe _~6יUUUUUVՈ`gV=eG*ߏ3F YV:ӷ8V/ՙUUUUUVՈj|fuؘߡ3VU7?pjnVw1|Ϭ1nv~&̺`UdU$.Q;oԸ 6>ͫϭBϬvܩzF KzmXna: VUJJUGlk?i=2VUgٿoFzꚇxe |>kvg EάUdU%+?5^9 vU*3ά3ęU߉=yontfFțUUUUDQ/=[xC~d@c{YU!dKfMYjЀw@gRIaK; VUJJUʒsKϖ/>c\TgO~Ym*@X`U|*XU*ɪ*IVU"˳5U{3,O!dؕ/;pfl< $jfγ2֪άUUUUUDԉ+4jlG>ruG'!pfubάBB~ѡ 7{pX=43`UŪjUdU$*UK״jvplRA=CȖ͹7pf0ߙUߧoWO+\*s:\aE`U$$YU3we޻I!d6]ޱ}j֍^Hș՞m[*VUIVUIWf:$jJrxe !vMKyJUUDT3fZgV!oX|3D~a܉iaF9 `U$$YUgV7n*o٭[rfѪ;ƍM5Vwߒ<XU%ɪ*IVU[Ϭޖ*Yݷ+[.)-wfjUdU$*|[Ϭ$z!;Q'pfERRy^H/:tnVfۀgsz`U$$YUgV;v3ՙKgVǏ*$갂1W  sƜ؛$YU%ɪJ5>)?6o磻x!۰|QaAYnxfu|foz`U$$YUO5qPSظYT*4+ٶc ϬOݾ<XU%ɪ*IVU[Ϭn)gV<+޺1[}gVi7VUIJU C/ˉM=° !;sjZ{& E%݃sYJUUD~Jc OT`B%)Ɂ32BB~ajI5.|C~K_z`U$$YUO,9e&]/;>:wfZ.|Ў]?޺++[?} 9O b%n|Ҫ YM%~Xڹ3OXrXK<MKmj XU%ɪ*IVU"˳5fj|fuVUه{ά.sWrSiSdϬz3UժUUdU%ޫxfOĦorfժ o?jdqάBB~*VUJUUDԙ{*=άZUῪlIIάB}vԒ.LtfU$YU%ɪ \8zKv_ܖ=jU«ϭc{gVCZ%rYjUŪ*IVUIWV40$csf_@jUΉ=W 8ܙ^_֍;rfU$YU%ɪ |ظ䙆gV=3G%Ł3cegV4aOtf*XU%ɪ*IVU"x⚶O7J+άZUѹ3[j?^5hvye !PꮛҝY VUIJUS?/r̺zfժ o YMIN^+{bi)W tf*XU%ɪ*IVUCVgd4YU!39o;W<jׯ}EgV=9Ǜ`U$$YU/n:~Gxq duՠ7w4V_[*VUJUUDԅ3SoȏVUٲ9>ҎTVxe _tX|cn규Y=oUUdU%>;y|شWNs;˭}gVS۴ٽ~W· Y2WWzUdU$*Qt MقYVUj^lgVYzYʂlgVUUdUP겜؜QٖcU߷N}3ۻmPӂê3XU*IVUI]άKYcHƻv}W#+ȻϞ֣sgnʙUHO߮V:Clꋥ/y VUIVUIU(S,9S.9UgwMi3XU*IVUIW<[.: rsY&rfg4laү6-*XU%YU%IVU"O Ʀ*!ocXgVØ+bά}G{]Tg/VUIJU8:.=CȖ̚")əUH3G&!75)ߙU$YU%ɪ _ⓓ/LGf$Wy܃u٫ϭsfr;. Yݾ<XU%YU%IVU|]n ~`fe xɬ^ߡ{unW1#ҙU$$ɪJt+4j짓vGL+V'*$f-xf/>9UUUUdU%̰Y=gVٿ+YX|ce ovf*ɪ*IQ<^Y: Uwf]r!d/?ֵKzyάBByxAv<z|u<XU%YU%IVU7֕IjXn쾛sf opfc{ U=xf]*UUUUdU%jߪzWtfWx^VYi58ㇽyJJDOpkn'e9RY:uhw3lXgۖ+4խNZUUUUdU%V40lȬ=U}XwXA3D~ѡ{yY3Ǟ*UUUUdU%~Ɋ!/ˉMu.;3Yueniaa>UUUUdU%^奥 _T?}s: {bάB9:xpئgV_GJJ|-]8+_|rҁsgKm*`;wftlj`3CYJJDrU3VgiyC>zՠάBSp" YJJXU] [?5^9 vCT*4'\ngVh2s:\aE`U$$YU!UޟY-ˋf[9vjnV3G&X?*McXU%ɪ*IVUY-Mos}zjIqjUݙU`UdU$YUkg=Ȏ3cr{ۻ̓uYy)jAn3FP!əU`UdU$YUs5UNwf?K%~XMmӦr^S6_50xf{6xU$*]gVu/ Ϭά>:wWsfS}gV": UdUя߿۷3waX͞:)pfqcO9g^3_|rһ`UdU$U(~jՉC:l gV}X+YX|cA՟tf*XU%YU%ɪjU%|:%6y ?zyάBB,5=MY VUIVUIUƣ>7<9Yy[^3>΃uHծ; R/խY VUIVUIBD?~~gV<3-soW{q 3]Y VUIVUIZU;ov\"IŶh gVǏ*$e#8: UdU_奋73gVK zMjnV3yϬ9ÙUXUdUⓓ;oԸ v[#C|yj*$jgV[9 UUUUUQj_[81UCϬHJ?cDY?ÙU`UdU$*wf|HR}7@άv3_tVW=8Qt#oh ]*ɪ*IBjߪ6pfu=?:8 ?vpjozXU%ɪ*IVUOQ9IqYC6-gV_>`U$$YU!V40$?Μۀ)Y-[<+ 9ڽS{z EάVUIJU"۟<=sʃuH닜YDYK/xXU%ɪ*IVU?g,pfu\nw)Y훑~l6 $jvS /jo$YU%ɪ Qt&YLϯ`™}ʼ2_tgVo7UUdU(:_W;}<ά>_FH/:tnz36YdU$*D;V>$)pfGcYpfu2BB~An-gV|`U$$YU!W*?5~jPdp{=XY*ӷTꞒ?Ko$YU%ɪ Q''+'p|#CϬ$'^+ E)I3Emjxɛ*IVUIBG~Vgd&֪άVUIJU"{6T ixYNlȡ`BљK2B:zpF3UUdU 3™:?5.ˋvjn*~XMWsCB~aZgVwݔㇽdU$*DnUWgV>iPoH~)!3i]άnY+{b=۶ Y}~ĥ^~ֻ*IVUIBVտ|ufYcK:y<ɟ]Ł3/rfJUUU/n,eSrcwqhN-) EάBB~ѡ 7C; c$YU%ɪ ]VUugVs{ۻ̓uYy3}z9 E{&67VwxU{cXU%ɪ*IVUs5,=[Uo:l]?HJ^+۰|Q3&9 cXU%ɪ*IVU/ּI:Yo?c3˟JqvcOqf:JUUя^_>$)pf^#ѪgV}X׋!;uxQa~3=0\XVUIJUgmx؜n  Y훑~t fujIq3g_zcXU%ɪ*IVUs5P:0Uw夾܃u٫ϭڹS%^_y]8|Q=<>ցUUdU(~lgVl=U}4_v3^ߡvj[t3>ցUUdU~ͮuϬ.8ӷ=X0>r`COŁsՠϬV:JUUŏ5oyD[8Qǃu٣sgHJVӺv޺+YgRɷY{M|$YU%ɪ Q}棝|)άB^.CgV@6K@Ȫnݭs3/rf:JUUя^E7]*JբGxq dV3g{!*IVUIB?~׼R5O7&; [6^gV6>$O{;cXU%ɪ*IVUs5m)|K=XϬi?nʙUHW,ٶ媁 ~>>wu+gV|$YU%ɪ }[Y rs<ɾrfj0>[^#άXVUIVUIUS{v:Ў]8er o Y}~ĥάXU*ɪ*IBD}yc.:;YgRܺnge?"_wHņE#]Tg/VUIJUo`Y6zęUCgV'!0v3S挹[ά~Io$$ɪ Qc՛.j!?6 ݙU l|fЎ^=JrS/LlpfuBNJJE>oLvfw"ka5%9y{ l. Y*`UdU$YU!վpb`Xէ%s=USYڱfi¤`UdU$YU!ݺzq.;BB}40:ߋðݾgky$*DQ/mvijI.άB6,_87#ݙUH/:L+߾ =Խ7UUUUdU(m xR%?_ʃuOu)~Xmݪ+xe !% ~>>*`UdU$YU!<[s'rS xɬάBnyzXGY$*DeUG_*SKYufuas{XU%YU%IVU7^*x؍9ݜY-?*$jA D8JJE>_LrfЎ͗wl8er o oj`]ʙU*ɪ*IBD}y꼬NTѫ |ɥάBB~ѡ{v??"gsY$*s{v[p7+E jxigV7Jt}gN!om|f-^7vp D||fozXU%YU%IVUQݔ9\z5C>ֵKjǽ2W,ٶc ~>>?RgV$$ɪU5<[ozoFyˎ+ȋV[$%-5+{u];?#m/rf dR*IVUIU5aglx7: q-ayV@NT}s3=0\G<XVUIVUIU#zfm\8zl6!om|f_FߙUL*)nqf:JJ3Vd5[YCN_TkS23: ~: cXU%YU%IVU|_=2pf^w!d3'ά5c߻vtwf:JJUժ=yXJS㵃c OT`Beվvl@>}zZgVw gV|$$YU+ލOz!;{K^ije+23=۶\9U*ɪ*I7չVϻSu#F8𾻼2}z^n^Vۀi7>ցUUUU>3qfuuf7Ɂ3c~~ fu܉iaF9 cXU%YU%ɪ}0>~;mȏݔՙUm=ݽ+!;sqcsScey ݷdv7>ցUUUU3mάBS8ڦ͖˽25/rygV|$$YU~f'͖7Suh gVϘ╁\TNfe _~6|]>ցUUUU3,wgV!|gL άBά+sEZ=g̹ڏyUUUU3lʏeGߖS>Ҏ敁}vnxfu|fozcVU$$YUYvKNSظYgV!VΟݳmrY}yUUUU3fCgV'Uh nyv_mZuXUdU~ѩ_Tg/ï_XOĦaX-: ^jߌa/>9U$YU%ɪ D~JO7&:KYuwhԒk:]{J/ybU$$YUY:o_ܖoS#YjV*<-׳mYqolUUdU|]VmvCH.pfERҒYӽ23Yi]~'x VUIJU_3~4CϘ2wy\63{<UUdU7&5pfάB6,_*439 [Mjo*IVUIQtm?5./N޻I!dn뛑8Z2 KS[rϟ1+{ gVyu?UUdU轭Oά.ˉw[)! pH(Hk0HAQXPlOtR@BH 'CAƝtdW)Z8ʈS6cܹ<{=sݙ^z֬\)c*45&Y ;[{xbӂ7[ d*I@D]~nǸv֬BSPmiݻ@>y  _MfLU%TULUG|֬B8+wZ`5m۪-Ox25+RZYMfLU%TULU^1=0X}h'o׺X׬m*4!ekVTULU%T֬L5.75.!dK gyGy Y:ꦡwh*J$\ڊ eٱ{[ [Mrr`5wډ<_aA~5`*Id 7W/} 3E[ ;yxϠ~}mld |OZ֦ٓYg*J$/?}k@`uQ_[uh"kV<ܶo֬$J*UEij,E񣙱w5֬N˛d*n:G"ǚU0U$SUI2USUݕo5\Z}@wwMaj]U'!D2SYSUI2U$SU0U嚫?[0$Sү}uHՔmn@[ٿ}kV` J$j}yҫ%|`*DyjmV Y=<u`*Id u53b@ϟ<))~:iHkV!!_4xij›?蜗LU%TULU;kV+Y=߻\Cvm*!5p]`b[7sLU%TULU;kV/}/r ~-.!duUZ[ѓ=V{CkVpXd*IQ|d~`z붮~ȭ:rY&f[ 7ek9u`*Id TL54,V75.!dߚUh ޭ>1["`EYTULU%T#ꏧO YL!6֬B}|ffʲYTULU%TzvM]u5cJ$7֬Vޙù}uىÚUh [758ց$J*8~G+k׬hG֬B?j*4uU:ofԶ_^pSUI$SUpYfƏf-f9K6OJ Y4x@A՗e*XLU%I5os}`r |;6m?XM՚U'o.15+Y?:pSUI$SUp.ynʀO,\5[֭d |ū{kwnyV/cJ2U$B/_z{ r_Ȓn!dƏY]Z8OsC^tRV Y=YLUTU$TS=3[߽B]uGϟc*4V9xfZpZ`gxyLUTU$TOOX݌f96mk*4CaAXY5u^SU0Ud*I2U=7uP魏XUU5Y1c'Y ths{ J$'ʥ%J׭pY7)f ߚUH!WYSUI$SUkWcdRcCcMYhRVVAX M0CdžZ LU%IDFe ~vn`$Yn!dfԚ%YSUI$SUUE7:kV6;Bk?X?laO_:!7{ZkVTU$TSU揧֬fn=.!d;6 Yܱ55Sh*J2U$b _s{ 4^jMdjijOfj! ׬ie*Cw̯TULU%TLUI/\YenUwj*XLU%I8~[sψ7kV!kV18XfpSUI$SUg&vYL!-OX MDaA~VSY 8ց$SUI2U5UY7=3ָ"'6uUnUվXҚULU%J*XHӕn!d?ښUh:kVg*XLU%I8~CܚՊI5Ld֬B׬ V_^4<u`*TULUM]zqmkVe֬5[׬^9LU%J*8~E]8{ڠ9Ooc֬B׬n\k<u`*TULU񛈮Y}hz`tq_׬NsfFHȚ՞R5SV[ 8ց$SUI2Uo"O=3[!95csd !kVoi0X/._8ց$SUI2Uo_xb|J`̞f9#e%7fȚՁboj0X}>ק'k<uJ2U$SUp&^pߌmnա)Y}j2Ofoj!58RpTTU$7]ZfN`@f[~bMbS47$djN Y?MX 8aj*TULUXz f9֬N>[{ؓ׬i߰f<urd*IMD׬>3[7 1ˁYܱC`걊͞ 쓷kUx;_{y0U$SUI2Uo>4SS*]C9?fyRRɚ $dj6I5;ons<uJ$XW'=iykV~'oz8cx[O.f0UTULU%T kV̈ogĦO}:lCj2~5x憦fz֬J$Dwj%Y;2z9bBvpۦ6Ɂ5GJ<SY}>Ob*Id EtnO/XfwN Y}j2OWzڵY5>g<`*Id E_^_O ݪC.UOSo\zjjkV0U$SUI2UVm-mkhflnOԸXmXyRR`5s@? YxkEӬYTULU%T .UcBObBVkV_'YoVAfJ4`uw}zd*IQt{ ^-kwow!;yxO`j}k,d |ūY}67ɚULU%TULU"_kVO*]­:fuZޤ`Y]@ȎUlNqizpɟ=d*d*Im#֬ݛ/UX]wd dx?ȻzC/J$DWkVǧo#.!dm YMi۶?z2‚kV1LU%TULUM )p! YmabOWfEց5&_/?SUI2U$SU(ŗM Y}-?YV֬NV? {m_yN׬֬`*Id Q}~KՕ3'vuYѢyj{X ;SshMCYy[td*IQtǵ YkD35\CȎm?Xܱñ͞ fY׬7<`*Id Ο>˗^Z=o\:w`kV!|'jfuz2-Vj"fu|5<`*Id ^U{ 4M_^Xb5քkV!ݽo.׬ڶښULU%TULUTz֬BSaIIլO@5//p҇^0U$SUI2USU/Vܜk\ǚUmnHm?Xmd'Y=kV ^b0U$SUI2USU"fuW~?kV)x>7% =ߎk{kwii*8ց$J*8~5EwY M&w_ګs &W5Xd*IMD{a{$kV)Xda`~}9ߓ946'՚^LU%TULU(VMŘչ9i֬BN`jz4kV!!Čݾau`*Id Dѧ'k'5.͎suH;@kVU?s9#zY H.f5mۃ6y2 fLlժWNTU$T=wf~ތkV!|'J55+<_imZifu\xy:0Ud*I2Uo_^6/w!DʹIwX ݽ. ׬C֬cJ2U$7?w=cdRqؚyӬY&fL!Ofur`{F}v጗pSUI$SUp&xxŸ6[ rY ;V9fsѓY?nZ/XLU%Ip㕥[Vo5{"gѐv֬BB>u?VԓmYrpVOk-$SUI*@DR4=0X-d*$#‚`uZ$kV!|+)uŀ׭c*$SUI*@D{Sy5 =<'ߚU߆kVnOBvi=7{^TLU%JLU"f|l5ٱY|$ֽ5 =_gRkVb0Ud*I2U.9up{qYGrcs͓6,_@vl\۫]G3}9.wK<`*TUd Q/.NO*Gn!dY0cj`j&wBΧwJ}_׀xКULU%JLU"?/mkiXlY HMrr`uMC9ߓ95xFkV_s<`*TUd EWNY1!5ָ"5gjX׬v!fHY'A~߬kVw<`*TUd E]8oFV֬Bޭ>5Kz2V-&a >>d*LU%IʚAIVU--m*$dj][O.E/J2U$DoC`꣙Y֬BB>kղe`5k@kV!Q_ V~v጗LU%JLU7&v5:<6oT5cӺwސھj 쓷k̘x޼/J2U$UEY]=/~ѭ:fu͓g3oD!;Vg%?_n*LU%IBD=5CbVWZ:0XMv/!kVdMi0XKozC/J2U$b Qt;nN5.ˎ /B]u+;ܱC`GJ< SHm`Z5oM/` J2U$SUSULU!kV6\Z38.!5yG=cEK{khfq8RTU$ ֬Uः[w+, Vg۵ v=,MYMZoXLU%TTos/>S65s@B>kղe`uMC9ߓ׬קgpZ?:TU$: ֬~4;v-ìY|$Քm<@>>QSXs~!?O/_{y:0Ud*IރqHY޻GșѾ:9V-[>j'+YWC}9.J֬BB Eά8uٙCn?Xmղu+=_ɚi)gX u`*TULUvAv,kVgO{8Nb5X$SUI2Uo߽|dRq)f9 7OJdf9@?7~]7Z uJ2U$SUh~؆ii_>=Y[1!5ָ,;`|ƅjkV;wpb'oVAn5'\|E/`$J*] 3E_)sY<0~<)U<ߎkva >>%^TSUI2U$SU" 0X]u!PW={53oD!kV_+f*b*Id @DByYn[G3ckNfXձ9Y֬BB֬N˛4G"`Xa5` d*IDԕoM!ָ4;6cxﳵ]CȎUlޥS`ꑲOWh^Vj3kVLU%TULU௾|qс:=+ܪCޭ>?Xmղu+=ߑa[<J$Q?].kOܪC>>Q3-oR5=+;Գʁ5o=~5d*IDԹ֬ψ=Tg*d͊V-[V3ff;Gݝ+/d*IDѕoVLH Y=G]C>ֽk`m<ө ӓ^LU%TULU>{9{sn!d~ U<_imZfTULU%T#IKݪCkVgf*4H`?kVLU%TULUnpa'q֬Bd]քb9 s<J$EWNY:>%fuфLAB>f2Sbe ͚USUI2U$SU"vMOVBvPNfF׀.Y@B ϼUIVp{xy0U$SUI2U rw+2[ujaA~$kV!*4=fĆ֬J$QyvkV7d^޻ܪCJW֬N{~OBv SzX `*Id _,{ Дܩ:Z M;X MAaA~V֬J$W:4qW/}XY0,S ƁgݪCkVdY Mu+~43fM^LU%TULUT"vό.I_VB۵ fL VYDYݣ7YE/d*I` Qte#?[3w'o׺XmXyRR`j]U'YQ=kV_ggxyb0U$SUI2USU+,95fu-C|$ݽύ=)mZ q߬fof:0U$SUI2Uoښfùۿst[ujNfF׀9xu[<-:0U$SUI2UoW V notY=`uZ$_CB ~5'ЗW.yyLU%TULUTcό.x}F fU|$&9ٚUH uՓƌ-8X^0֚UTULU%T!kV&v Y};vfkVw>Q33oX:ٚUTU$T!>i?u8{>D}$5ॅ<ߖu+^p֬XLU%I K+'JsfyG[ ;qpW] {[cJ$7p{JvL Y]{]֬B+ZX:_{235&fOi0X}ikVqSUI$SUpSf*4*6w!~<) oiᬌkVqSUI2U$SUpz=ӇZ M#{}A Yڻ}dž׬PTU$T!^kVK׭p!Diy?@kVxjϚUTU$T!kV77^zߔoHԪe5ݻtff߰fyyLU%JLUOVo"ueY M6֬B֬f6Zz`J?kV1USUI$SUfuwAf禷>RVVHnlNV׀.Y@B֬Lip5$SUI*\ό.xIz]uMa1#p !kVg*`*TUd ׼kV5Zl?Xscx25v Y^0֚U,vSUI$SU"үl|JqivlFN_AYd' Y:<֬J2U$W>ނ!:\8$ҭ:$|jU rHԚ՟ nqkVLU%JLU/\^1=0X]lOUfuBnG=kʻwJ]ߚUSUI$SUU[F65^+#9HGrmY&fuJkVLU%JLU?;Uz ׬9bH.f5m#e% ?5LU%IpKkxqENmYD}$5sfy3Y0Ud*I2UkqO.+ݪCB> V5UH];ux5LU%Ip*G4 Y}`$ASX:_wd !_OkfkVLU%JLU19#z=bf;6d !_gĶo0X}~r}%/LU%ID+كRNVšբE|A w֏ mܤ3{7yy0Ud*I2U ^\5;S+5۾n!|K g5^Og׬H5LU%IDeG65.[>c)Y<} $ ۺ~Ú?k/LU%I=xxU߮b7*!fu]CNܕֽk`MrMŞ $dv75>7ݿVyy0Ud*I2UT/\z`hesnա)Y]Z8䐐/.\^/LU%TTSU ՗W~fuk2m]֬N8 f{ԅ֭֬s҇LU%J*@=|dR`~|:$|~}N@_i5 ~9/8ց$SUI2U5U+1ղؼ=vYԯO`5mۃ6y2OޮoVA5nd*u`*TULUMUq"vN{y}uGY-Z4ϓ|A޳M58ց$SUI*fՃ~ƌ9X=XcJ2U$ |D['~Xp'o׺Xnղe`j]U'MdK|v጗TU$7EKeSkV}QN Y[ѓ׬.1֬~ۥj7s5\4f5lY֬&ٻ`*Id ut`hP/w H.0X-,ȷfΧ貤p YLU%TULU ξX}d5sZ +Y"f5wډ<Gs23&w Y=rw5$J*DԟoKcr=bf5mc= fd|7tdTULU%TyYY?:~<)i9 |A7u5UnJ$@DR|7+}ȭ:IcFZ Wԥ5o=+<d*Iо=k@μ{Ə =<Avl\X9k=ٙCcsY=%^TU$Tze=5ӓ[u#֬N3Wv>=ύ ZXLU%I DcvL54,~|$Djjwod /'fOifŹ>w˃cJ2U$7E_m!ָ"5gjXUUu?XmղeijO·|1֬NyypSUI$SUp"f9YD}$X|OWZoj'5ln{<8ց$SUI*8~fV48^6(t ꐐIcF?~ԓy矶mkuxlިtkV!|*67^Ԫe hѼkV~'k<8ց$SUI*ߎ߀5`5Əw·d\zf&/u`*TUd7Qk kV48iU-150Xe*]C\1U:0Ud*I2U8_>8֬>{{H·eʔmYyL VܝG<$SUI*Dѕo>=.5ָ4;6wԀ]C*Yݵؓ-3#3%V`=/SU0Ud*I2UkVj^F>ǭ:l9Y_^ ji ~%^LUTU$TYCZݪC>yv͓ƌ9$djFVͱfb0Ud*I2UjfxH;ݪCJW;vݽݓ׬N͞-+/$SUI*DѕoO5.ˎ /B]u+;fE2Rb[7v}%/$SUI*DՋTNk\38n!ƏY]Z8듷k=ٖu+~4`ܤ*J2U$@D~~'[u_aA~`j*$jꀴKӃkVOlXK^SUI$SUSl|$&9ٚUh"kVY}aƐ?;0Ud*I2U(jf4;v-ìY|$;-~&9 쓷k,fuw~z`*TUd Qt҇?u8/}]UuHGr_^ظ6fuP>'d d=[pܿ`*TUd Qt;nY;j |ݟ9`MŞ fu)&^SUI$SU?9Պ؜p$76'+kK gy6-Y/$SUI*S_ՇifݪC-3yRR`5oǏz8cthop?i*`*TULUMUUE G65^邻ܪCn۔Ҷm`{NuU l ٷu Y^x5$SUI2U5UT rkVe.!d+O5fuoFO `XIVp'k<LU%TTSU(z}8uمkV?t'+YgJ]b0Ud*I&RDTՕJ [ M i=cͪcJ2U$ 5U=cdRq)]bBVRV{8˓?~4ooue[XLU%TToݿYS6]qYvl u.!duU Yݱq'oVAzS.:0Ud*I 5_)s H.fui,_Cvl\3M`곹I_LU%J*8~DT9zGrE֬:[{ؓ ܯw5MfձLU%J*8~Dԩ}[lkhflܩ>)+ifғ 7.c֬:aj*TULU ~t|jqivl><'s@j-KW{2/37fձLU%J*8~W_\:=+ܪCM8.fh<_CB O kVSUSUId ʺ{evm[uY֬BSΧ貰op[oͪc%J*8~Do5\>#PA |mJi֚Uh _}[oXم3^0U$SUI2Uo(j#?~:2k@kV)Xo`;z~z˃cd*I EW/}њECڽ}9OL˛d*46woӢx5uJ2U$SUpokV V',)v+Z4ϚUh jwo~֬:aj*TULUk۷y{5Ş\<ǭ:ݻtfB]u&L\z>w˃cd*I 5kVMȼPWbBnkVذ|qFJlfͪcd*I 5?(nGr֬BQfEZJkV߫x˃cd*I|S+pZ`"mU-?'fuҘ UUfqIXj$aޣ.vDRB;$$܄P*2(BA 7 (5%.rԜT4 - r<0; jWK?ω37^z:}JuK{^x^NcV·gի5n*$oנ{wfժUUdU^گ3 ԱMfꆒ yYbU$$YU>Zun:|,{[V{ܐ}Ydhv?ɝck_:$䮭WϙUHʭkڷ2gV8wƇJUU@}kzƿ5~.'e{ogV!,9nY}oc>ǙUJUU@Dά>u{7$ۿ; 0N5t!+[0]sjY]*XU%ɪ*IVU;.߬LIDAT%@ WzrX`X֥YUM}tL`X-䐐 os}ˢ6άUUdUoϬ_pV Y3{άBv./qauf{V@Ȏo_?UʪvfdU$*XU"?jifϬ~RËu뻴oyS&x2IE#3RY$YU%ɪ VU g>]}OW>-ݙUHșzG*oim>7xfVz5} ضqݘG\RgJUU`U|Y9t=c'Yp}gVjά$YU%ɪ ~osp+zufE ͛6ٹēYeά~$YU%ɪ ~CI}fX6/!_ YMJf<@Bά:WU?aU$$YU|3k |ᄬάBBάנբܿÁϬސQVvfՏuXU%ɪ*IVU7ܞgFpZk[uH39: rHԙ'_UgVXUUdU?~ztEXO<:o7V[hV2OrfuP̪밪JUUo^??J4Y}n}S'y2377WU?aU$$YU| ׆ϬmG!Iά>tEHԙg3~ê*IVUIv\<_aauzkxYo!ά)b'!{{OpeYcVUIJU .xnה<2[uHșME <Hș{YH+̪밪JUUoe~MgVG!|vo}f<_}C]gVXUUdU?~Puj7plnSgV!!gVG 3pf辡gTx8vɾά$YU%ɪ ~_O|aqvfx9q\jNvod 3-5yvfՏuXU%ɪ*IVU7@m_G]gV!9Ϭff' 9:֙ݣ ̪밪JUUoμwEάN{܋uHk+;ݓYHY|sau]7XVUIVUIUDџ?;Wo-ުC2YO3m>ر XVUIVUIU]pZo Y-b'YYϬlQRYbU$$YUB:&QՍ%əUJUUapU1Uc4xg*/!ά>2^gV!|+tf$*xjǓ럟:dӵϬ+@o.ƙUJJKWn)3FϙU[kάnlϪW=ى}y3:ẏJJ@}ήe}gVľ^CԱMfjɌ=jc/sfVUVUIVUIU?v|_u8kƁmXܿwp^3F  >8ً|~$$ɪ DU |3ٱg$)gV rYYj{c3oO*XU%YU%IVU u+zgV:o҅uU C}fuάUUUUVU μwi3niI/!d[fn?bfOd |Fd<:3`UdU$U+؉]ToŗefќFmY:Kr5(pf,p d-xwgVW-*XU%YU%ɪjU4x/ݪxj珆pzVVWڱM=7;f1xfbgV*ɪ*IVUVU"W8y %-U̓طa~3;~*XU%YU%ɪs0VU"̻{IY}n'bBvpΝ@<< gVv*IVUIU+jթC;p\n[u#6*lv>M%~$$YU @DϬ:z9q\j,?);o'!{{˞YŔU?aU$$YU ^l}Is9)/pꐐ35n?fn֚% {w3ʏuXUdU*Q/3 o!~XMŞ:ɓ͛2!qՍyg밪ZU%YU%ɪ VU"zfuyάBܰgV*<֥ 4oX XUժ*ɪ*IVUQ{Y:άB23y|3ʷy2of>a_;:^9JJUDፋV9 `ϪWgV7mRz2ӕ}fuOQOcVUIJUDQա}7ufedά.=͓?>uV}P:dU$*XU/ΝY@wgV!I~I辡3F =7Ȼ4obgV{x ~ê*IVUIUê3(/?umz-sYڽ{ Ϭ$YU%ɪ VUBǶrf֥ 3gV޸ғܿ>}x??:dU$*XUάB2p[ Jf<@L㺺o1n>:dU$*XUϬ*$*?p d;\ja؆~ê*IVUIU>:5;VKra ' =:N:dU$*XUsFoKS& tev/̏VScNd |n.5ά~ê*IVUIB?t%@H U|=Z9 Irfu¨άBBάvmQѲyVUdU$*@Dm+ç\^aOAޡݛ<ٱmY'u Y WYUJUUQttך5άЭΒcUU,ua5#=5K<HWs>xfun8~تUUdUy&3OsfeuVWttO7QM.ư7WfOoA$YU%ɪ _;nTWNҙUά>t'p de d6i0k }x*IVUIDԎi#kYݱhO7HKVs:u8V͓ܱSfqgV{sg|x*IVUID]k<:/Β=:+x}ڼiזy23{ xXU%ɪ*IVU(}fu)әUHșՂa553y' 9׸ni~au݀UUUdU/ΝY30UOh*쁊˞Y=نm.^>^XU%ɪ*IVU}fuzkYDYM[~׿9X31*JUUQttךҚgV_VgSEުC*^[ٺeuBO?:d@!gVՙUdU$*@DȻK6k\Rԫ';XUS6z29wɨM>?XVUIJU 8wf탷6~ojo!d+ˇ Vl2?U㟏kvSXU%ɪ*IVUWN[쏼UΝ~mzj?{'!ܺ6C7Ϭl3XU%ɪ*IVU:Ɗ=covK3{gTx!۹y&jX2OBvb*8}x*IVUID_ƫ :cjG.$$ɪUժ @޸G3sSf/AZy&jsy'aժ!x]>xfu8~؇$*VU*QTji~vsfrf ';m>VofRgV7><`UdU$YUZU UV |F7yM!;{b;kY/:@ȫj͋dfԟ5xf><`UdU$YU0xt3o7E][^ݛ<sUgV?xa3XU%YU%IVU u{ԍkB<ogV!%J4cV3VUIJUW^UPK3 O;U5I~07k\l3VUIJUQGG,S^~A$ə͋x2XUCvZ,wfժ*IVUIB/uI=HCJ{7Y}\> ;o~\XOj_=QϬn>:hQnάZU%ɪ*IVU g>]O_u8-/ݙUH3} >@V̟yCFgYJUUFpVN/Vjmo.d 39ڌkw3N`U$$YUK:Z2~o!ά^[^ $RV̪UUdU N%:*$ə):{ÁϬvjX3m*IVUIE!_u8[#gV!Iά˙UE Zן9m6,*IVUIEWmOƳrR,:$Ù-9 άbUdU$U=bo_2VjFz te~4YgVdU$* fOEO=}zg8 IrfOA^ۀ'*$jI3VUIJU_\:ݜY$%C}fտ 9zCF.o>|VUIJUQۋG*xnnlުCBά^[^ڱMWz2.ێtfժ*IVUIUN[άB\^8-7eBf떗T2gVpVUIVUIUEb[iᵁ3t:]Y:$Ù:ʷr4RUkX5܇$$ɪ VU?v;pJ~#{6ypfa';<MM3feر }xJJ`U{gVSw˂gUd8YŪ3oz]{]<_ÃUUUUdU*D{e/ά4ުC2YmbLOjBά2%]ʇ$$ɪ VUbq3w: Irfu¨άbUMnT5u[f$*XU .Z.:|kUd8:wv{8XU 2v Y=Z6χ$$ɪ VU谺_u83'uעyުC2Y&rZOjޔӡ͸ZgVuVUIVUIUU+H Y]ÇUd8m(`UMș{Y}VUIVUIUUk?XU%YU%IVU@yu̽f{OUm]ƍYŪp7eշsfժ*ɪ*I esI=ڱ7_+yM3RYDm9mo>~Xm*VՄ|5}4Y>3VUIVUIUݑw.pfsYl9{ Y-~|'U5|Fvn<:3VUIVUIUxށ:ܣ33' C='U5d-qY50vhTgV$*{WIuflii37y2h>yT Aֵ3[>Z1y3VUIVUIU׭+gV,G!Xߥ}a55+;ݓطa~3;~̪UUUUdU }~iߌէnG!IάΜ8ΓMyxT3nwfժ*ɪ*IUN- Ur8 IrfuEHWsjXZn**ɪ*IQsO>zpf5uˊזy27_L̪UUUUdU bi夼ުCBάvl?^[ފ3=MDn+$*QKtfd6x`ۀM@e78qP3*ɪ*IVU*QtԚnvf}=֥ ojQ }xJJUժ @D?7g=8 `37y2c sjY}{/Ν*ɪ*IVU*Qtx=cάB2x{-iiάBNWE_;άBl9aup^άBz2gV{x >:&5V; ۹䦦kY-۰ЪjUdU$*XUάM$YU%ɪ VU 8wfݨށ:㺣7x9q\`X_r^OƪJi`V36,JUU`U"jǴ33rRw2[uHȖ --~X&rZOƪJX)v3=[Ź3VUIJU@ݵfi3t*$jҾm -mC|OƪJ_=Zݣ=EVUIJU@}qo 3 ; rRcaTdl2?U㟏kvSUUdU*Q{ybάBB?AZZٺ3VU9ڌ)xfg3VUIVUIBG/$#oX3άB⶜Nii[.dľCok5]pUUdU(ݑwkxUAʌvpfrfup^jj,IU͜8mzWV7܇dU$*DΔ}?'U{d8 1Q3<߿`U%_ݦwgVWƎͳJUUQ4™UHJg]ڷ={'(K/"jz\^Ź3VUIJUșUHoo\ٱMfv\Oj!h<}x?gV=te3)5T4 u_VcXp{VXU%ɪ*IVU˞YUșUHFVIOjf>zlB$YU%ɪ Ub_|[uHȖ --~Xm޴Wz2ʭk;yEYJJU%Gxۿ ҜY|5{]JJ`UU)w: Irf{7hd/v1n+$*XU(Puj尜W>]ԙUH yY?ܻœm^EzigVoYlUdU$YU Dс3?qfd¹ -޸ғ?{ gV Y 9jUdU$*XU+yuL=@ܱ>Sa55+;ݓv-Sў?8jUdU$YU DeϬ>3-a~ۀU͜8.;#e5uygUUUUdU*E|W>٣?wb-sXU ن-?)xfJ$*XU6e3$6/Z8ڼi,df׏uf3Fsfժ*ɪ*IVU$#ſ5~)ΒUYҾm<*!;~Y;άZU%YU%ɪ VUd>gVu 3pfhU%|ŏ0$/xfVUIVUIUU UV>éM?~uo!dgTLyxTjO*vx8VUBb& ɪ5}ӎoYlUdU$YUZU^<:0Io!|J>sy'cU%d7ui;jUdU$*XU_\ޣn[7,4[u_ֵάZUI'6x)k o8jUdU$*XU;{N[)ܑ#$ۀ8нw,plãd Y;$*VU(Puj_u8ɯwV7g՜Nodf feo7XU%YU%IVUmZ땟xۺtaFzzjk<*YjgVߞ::jUdU$*XU0_*kj% vo*ɎVWϙU*:뺔U5cz~+gVdU*wG]'#qi~=Y,6x`j}C}e7YJJUW/ΝY~OW>oVW2kՋV9jU%|;d6̙[[U%YU%IVUa׍yp%@mὁauNnꛋYسM8jU%Z- Y̪UUUUm9K ƿ5i^ŏypάZUI3*oYcVUIVUIU賃XrKZ[U)ŷg,bBv@;jU%̙.xfr#ÙU$$YUّ7V,qM[㗺,8[upf|fo+ϬZU%YU%ɪ \7^ܧQ7rf#$əIE#}?>}̪UUUUBթ}?70N/tfcq3pfOkհ~qZgVYU%YU%ɪDIpVNK{ۼh3 *Ҿ蛂gV+&$$YU(:q҂o_^gVY:]Y>b蝃Z,ϯ1)*㉣VUIVUIQ_,xUAʼ#8 Q6augVΩVUIVUIQtT_utAӏ*8 U` <6K[U%YU%ɪ nucqI=o˖gVKŊUm( %[yh|w/Td[U%YU%ɪ ImVԯ_+T֕=boWg1ު3Wj^5lVdU*@t}zgάNgVիgU5 2N5oey)ɪ*IVUDُ>xoa4?sOW{!;w?ZU sUHiOMe8pG"%YU%ɪ VUxjɰU.ꥑO.,,tfUUU`UW s&ؔ+2N!>gV\{=Wծ=bU=vǓ2766zdU$*XU԰ "gV&9 'ϙ׶lSU̓+xf$$YU  X4۪jX;W:;+**OJJU^83prY1g/UsU}CάN4əUIVUIU \u;.YqHgVտ! USFjffCRUU@i]:zhgiۣ^̙3_6Vt{~чYתއYt)ɪ*IVU \k'^9:;Kgyrf3,wٙrdUUtiv3 2Ruwy~ϭ rf5ޣ>۪Yw]䣹 YdU$*?FY3:˙U8Iά{+7r9*ɪ*IVUVsfN3~ӹFT*'ժ3dU/l]ߓ=$gV|glU4'f/Fά odUUvD3/Nj'ə~r}:=އY4iRKKNIVUI:gœ견]; άBz{jzzzccGNIVUI_ }A>3ީWry3?Y|e;W:;VSSSJQ2/_|Ƭx}dXzArl//֠[N~}M_UM/Eά]SdU$YU (mfu^F)ec"gVWjwmwߊ|4w^^3UUdU*@ju?WSSTJXUzdթĝY51ș-ˬXUșՊ UUdUŪjU׳FάfL'~~7o9,=rfhꤏg֭W/:_,gĪIܿﲯ^jkkPɪ*IbUO9˯]hrfc;Y||f݊]ӧwXg{syozbU$<1몫rfUJXUzvجș*wӒs?w +eTGǨϲbU$֭w^?gUUdUŪk&oo겳:3y9ke3^88/m%Ū55>ʕ+JVUIU*zfșY}/^۲1rfbbSc{b;A2bUkiu=8*YU%IVU^œάIrf{\!{X2k_bX,GݙU8UTϤcV:}I~*z{_~a勒UUdUik;L~_ۿ#~~wOn>Ο}ޝ?y}>})~w/RcW3̿g]s\5>ˊ*붮@3'Ԛ79?PW~vO???PqMuyII͵?[< UUdU9qN]n8??>v:KrQ~ϝ}>woxߑoK.ϻ'o}wOn>Ο}>ϻngVo.Xǻu:DF~:x.MTqm㚊.@m߷7=S~Ӎ]]UKLVO[y㮋կ~TJXUVUdm}yj?u>̪UժjUzOq̝9fϧUUdUŪjUZUM&ksC\U475,=ϬZUVUWZUGș!Cڵ dU$YUqW]UwUUuoҽI&}>}]_ FάNOo\4]UwUU[Uꪚ{/*VտvHdKVʕ+=JVUIU׳-I;:/#e-ymY]4Фb?6bGϬ[᫊Uo݃ c&MrfUJUժ O-D˾6 Si%3ǿ?'1t_R`{S{Y=|Tɪ*IVU*wj\u~Ā3'Cɿw}=v۹3 Y*YU%ɪjUµaJ~3̽Tܿ/naUأsĪm^jEE7Uɪ*IVU*?oIvh6ObnޱXQ,v=u}-?񕥾XUOu۷OO;*YU%ɪ 5\9:E3-eoVoͩ=?scѣǃE}pq7RSSW\UJU \-IHV Kqf><;zx:3.êzxvգ#?O󛛛=JVUIߞ-*|OI~}{\*VՓP3ÇwfUJU \G-yά§kӏNٝg 8q/lªzڴnM^榥m۶sdU$*@* xf b=ϜX8~R|mOϗ߅gV*//*YU%ɪ uJ۟Y]7sU}ά^}" ZZZJVUI/l]4wӀg(XU\U7wșաC߿ӫdU$*@[rMzdX*U5UͺUGά xΜ9^h%$YUҟ`xǎ ƍsfUJU \YrE[V`M02>;dU$*@:=:5ʵj۰o>jZZZUUZɪ*IVUpuʵj6YVYUUdU#w\h7κ0άXUUs>?ęUYU%IVUE٧%,Yk^=:rf㰬$*@\=:7=Z0gfdXufUVUIU hM ec3#gV'f|-V`-/.ݫW]<˪*I7ʏYv_/4~XUgάʪ*Ip+4auޅҟ`ڼq9*$ɪ 1K.4Fg; `U SowfUVUIUciaάXUbi3JSDάyArl[U5Xwl?PgVeU$YU8zM)a)Kn3XUvc]~ άʪ*Ip̟_Z33VՠNϬ666zLUUVU@P[ry3$V`mZf@j~jjj'˪*IVU*@N>rfuFiU-%V`վDW\IYVUIZU#Y-JY=&c U5Xo7]|xSSeYU%ɪ @[3w֍VjشAՁ>䓞eU$*x#gVܲ|w'2%^eU$*ھfIIV3~f;ƺ"gVZZZ3˪*IVU/auiVÝYlIșCyjUUtL3VUm9VSSYVUInDJjժb; U5XY]vgYU%ɪ @^|dEI"=:&S U5XY-,,tfUVUI;MVY8 `U Y3鍍^eU$*jnjX8vxݣpfZUCjϲJUpmDauFFϧ~=߬XUxf5WTTxUUzݲjIf7XVU5XY4i3JUSΪgVg\=ҙU׈uu/xСάʪ*IVU8Ϳ)M;:/#l -U`}ؙVҲJU@*9:kEάVUB6{șÇ{UUꝆ13GmZ\*jMMiYU%ɪ @g33N}tgVSSS˽N˪*IVUU胋OMVdwx `U%XY-((pfUVUI7_ܜ3sFg9 XU Y3\rI}}gjYU%ɪ @[/FάN6VU={xUUTh7κ0u\ `U%Xάʪ*IVUzDܙU G9*$YU ay9GάN"ݙUJ:Y=|WkYU%ɪ @JfEάNJ xqgV*juukYU%ɪ @~eY&J:YޮeU$*z:Y]=gVJnw]IZZZ`˪*IVUu`9eYyf*!{3鍍eU$*:rn53GuygVjjjc˪*IVUµnR^dXR `U%X9vZOٲJUp|dEIVauSͺHXU [3yyyάʪ*IVUհ{œ3ɱVUB6{șCyӖUUȁc.|Qi kV+rfʳdU \k'߸<<|]?sMVxYYYkkmYU%ɪ @DάVUHv䉑a533ދUUdU P4.nD#{ʙUJ֭w^3۶mmU$YUך_ rfuzFϧ/2VUUӑ3m͛7ϻUUdU \U.)J9zMJުcdXoiimU$YU9YlxܙU$ɪ 8rn7xfu Jشa@jZZ͛[U%IVUµ΂剔auΰSY#O.))immnU$YU &zDά.w ޫYl3\rIccq$ɪ @JK>hkkș޽{;jU$YUoQꬌsm?U`sG"sXӀ<[U%IVUϬ>0f`U%Xl(&gV$*Sz>jy"_ufM3yX̬ZnU$YU; K:?rf΋zcF;`U%XOog'iii555̭$*0%jZZZUUx$YU \',TsSâ. pf]?ڙU$YU~XHIV ^d wNsfժ*IVU௫ya)Kn3wUvc]~ YJUx)gWjy"6ÜY*lҒԞ=?gVdU>Í"gV'%;gV ZA>gVdUOOYnXߺ| ':ү\>jU$*pܪ֕/lwfuAFJ|XU!Xl(U$YU#DάάU`̎gV$*p_^yAz3XU!Otxr>;yXׯ_MMUUdUp;vꌌӪV[ :_,yX+WJm+f$m)7 znգ#gV $*0{3qfY>|xSSUUdUp#gV9b`Up?as噃%|'$*k鏿9:-s c`U`ս;9,yXMMMJoYY8i?Uvc]~ 3yyy/gVdU>}<.͊5z3`U-)ǓF$ɪ z`ˇ: VU M3iiiVUIZU \;˲gVV xf5YU%ɪjUpmNάNTVUVgV [ZZdU@^ά'bqfNϬ۷Ϫ*IVU*p_f\șջG*XU!dϬ$^:˨Uժ jQQUU@~xAfauAF}aUBN3VUI'7^ì/rfu,gVZU!dϬ{wﶪJUԡ}.ߑ3 lXU?NϬn۶ͪ*IVU \%ה' .o]>ƆUVgVNjU$*'g*VU*pTgVoMϬZU%ɪ ljvvș;~3XUu<:tz$YU@{aΉY+ϫ;UWgV+++dUµ^nXqVܰjIIUU@^dq3eYշ~Uۍuy㮋 ---VUIڷWӀ}g *3gDά۷Ϫ*IVU Pu<:mĀ׷l0aUUgVdUµoG9N}|*wN$x̪*Igj?ՔLT<ܲaW㦼 ,Y}i/*1X7>ȧ}3VUIW=(5=hU[ڳg򰚛{!$=9* N7Ƕ͕3iiiUUUVUI:I\10ehʪ1_z즋#}7݃ ?A}]8jU$I$INMMM999;L~`u`x<9J$I$IgւȰ:@شa@ɿ8ׯcjU$I$I`k׮ݻwqF{_~ѫ:{}DjQQժ*I$I$IvޝvܻWU-@n~':477VUI$I$I*tKa%ժ*I$I$IZ[[Ǎy;kU_U`=a]3۶m_O$I$Iٯx@CFGWR$I$I$Q3={nZƫ:zv|a5??%˨UU$I$IBqά@E{3VUI$I$I ւx<*UgcZZ͛jU$I$IZ~3#Ot())immVUI$I$I gVWݷԫ:zG? yyy---VUI$I$I K.ęUHbiIjϞɿ8ZU%I$I$)Z[['MV/E=^ֶ͕oh$I$Io*:Ԫ*I$I$I:ֶm*V}m޸묪$I$Ic555 >ܙUH6wxhΜ9xÇ>ܙU$I$I$}h---ά`U$I$I$*))35>$I$I$Ik׮4gVJ$I$I:t誫rf$I$I$I#|1oz*I$I$I]6555yX=zXU%I$I$I={ 2$rfM`U$I$I$>*VUI$I$IJs3XU%I$I$I֯_߻wogVJ$I$I۷/==ݙU$I$I$}h˝Yy8 d0ACaJm/-hc% "X 2)Mi9]< VDC\aɊB,XaH6L1F IDAT v _<hǃ_y 8*IY$YU` άJEϬ?zē$ɪ MgVK}Y$YUi33S%IVUZϬrfUdU&gV%IVUXÇ3ê3$*all̙UIUevvۙUIU*cgV%IVUX3$*,UIU̪$ɪ KpfUdU9*I=:yﮇ~IU3+Y$YUi3_^?/I@zfuOg=KYf&KjB9z%O$*q:*I[%I244EQzXr/s6IU񎎎{ݓ$IVUj͙UIUFX̜Y=}1@dU`aaT*eR'&IUrlzU@dUR ޾ $*q޽;=FQl$*4 GQVKY$YUi||#=nܰa]#$ɪ twwwzXͷ];$ɪ A(tS$ɪ BT _b@dU\.{ݵHjB9kfIU8wޝV(:{@dU(}{ן$IVU;::lz AdU`zzzӦMauʕάJbMǏ1$H$I288VK}Y$YU\.ygV%IVUxZV(꺵kwn$ɪ q(Ξ:jiA$n=1g]$*\z5sfu-3SIAׯ\1HUƞ={2>q IVUipp03~3dUFGG[Ϭ۴A@zfu57MdU8bzX3VIA$άJUxrEQzXv9*IVUauʕv!IVU ;w̜Y={gvIXj3dU.\궭[f&dUJ~n[ IA^߼ysşFldUEϬ~˄$YU`dd$='$ɪ auuW׿o\OHU8I˝Y$*끁̙Ձz5P!IVU ͜Ym3S IAZ]~}zX]r/7o+$ɪ FX,(Ο;c$*,,,eά߷Ecn!IVU rau׎OgMdU37lpfU@ӢgV/4b$*$Iߟ9:wάJUh(=n%C@099Y(ꮮ[ IA===3OgHUZϬ:*IVU… \.sf IARɜYmo_̪$YU3^yC@Ձz5!IVU (˙3vli;$YUZfά S$YU bV(:C@$@jƜC@p|>Vm235a$*j̙ukTܲ|HU=dU$I8̪$YUwi=k5+$YU *oB$ɪ FǙUIo$@Emt3$YUի3[6:35a$*T*̙uk8*IVUhfά=u4"IVU HdhhhٛEc@"IVU (˹\.=~ٶ5$YUZvvv]];$dUz^,jn'YJ$ɪ I fά=?HU2gVwL$ɪ J%sf}EM$YU ;wfά>É$-NN-O.IENDB`T N]OpVru0U*š՛ol*P.kV6L_bjޜ_~D$U%""*GXgb8SS=kk_ Wsf5{勏Oh*IUH Y-)zj[خ=$W^S99كiM_-fud* U%"IU:|CWaCYg֬RU"T?Wa`ufjiOƬKY- ddmi* U%"*IU{[Q-zj ȚU |^_R`jF _]3@Ȝܗ5]S@JDR3_+rs:/Mps>޿{y:5lyr_VEyfֱ=\8\5>]5gpkVgL|"kVooRx G/aepJshF"_O׊\30;o&̉W5C8a0._^u8S_c{6bkçX:iOaϺ%׬㑻O?Wа;oBiF8L:f28_{W>ikV$[9\.kV;n5{ӆ]55 TL9a;a*[8a0WL Y>ri!;mzuD&ՏC֩a̩o-fuLjNWtͪnM&U%SbsXN0qX sL0+.^ڧn`[i//Ao[עi`zb9ӹ9>SRGKX5^55 TL9a;a*[8y9{=Lka&/O9cU{|y BvT^=w Y7b'9֬6m0wrNa}Yzu+fu翤UݚMJT՝aT-RU5 &sÕi>ukV፧M%C;7txCtzMg2S s8|=8Ս?,5hؤdJaKU &KU"UU`29\֬~b Wm5͗ d9ۀs5jI5KY : &8a*[PL0 ldURŧM%ٓWOLVtrr_W0CfU]iCU: TL9a;a*[jL0+%7d֬>OǘK[?wmR`EƯ}j!s*/w\o4lRU)1I{"sX VL03䬸-"Y50 roVOL|6m o0g޴I]֬nx4lRU)1I{"sX VL0/ ?ZsOr_ja=$7lЀu`.0Cf҅[rjƅu6TՔ$U=9,UsTU a&sqp/׬s̝p59992W%׬Mg4lRU#GSbTDT-RU5&sMWL,5p4^Xzu "j!I~ΰAJY}5':iؤdJa¹Ed#.9 oKf-kVϙa51kV__@cMJ&8a*[jL0>;ޛ_j//EK>$fua%yu} ׬W8iؤdJa¹Ed#N^j///fEDR<Ĵw!j0ܱwVܖΚ3|fVk{s8{[ostZVYsSqm%]/V7 kV4lUC|n9F]qL//IԌw!0׬fSkVX' dvȸȚ^:֡ sxIZqL7r$%U%"RU@fǴ{NW\ۖ.lڨatVdp !?7Ip\X*TH HUqgY~[n Sv$= lN׫[tZ=1ٴ)'ruz ֬χr U%"IUp-o\5Cŗe-Gz4f kV'a*P.kV;5WryRUHUDDRU8eW>ֱƮ0U3F`O.6s:.fΟ+BJD$U%"#Gw]5 nꥩcMՁ]Ej:u^_gry5f5;GGIUȧᄉ5b*P.YN7DԨ,}&gn -zA/VoH@JD$U%" _}vѷ^u8GckV$?'uu*/9@d>7E靂kV 6.<@JD$U%");*fu@dzvZjJ99̡ڷh:upO~iDDRU""*@~W__ϊG/S2?(|Luܹ>Ә>v/"m-pč>y[uy/ܼyng)^i/xי.\/>=9t|؞2k}{_%r]İ&K:1ԟERPb:FʵtT%<Ah _yì&vI_ER:/uCipCl-?9ƶw^>gɉu2^:NfgL/.E7bZSYo)Y×Cu,}55jDpJQu8PF_ο1Uy>(7?KqF^y|/f5'fח>t\:UtH:$ψU1}_\c"ԟkb9Rbo+E(PUP)冶ED/kۡ^o X|(Wa9,Uu'p8,UunTU 3p>yаIUMRUr'p:@ s8N׬.]7zj[˜i //pD)ɁϘ@E5JU4zD: ].VuWwk4lRUSbT 0-a&GNŷ plr`//p|Sr꠾wͪp,~E靂o ̓MjJL;TչRU5dpq:_rYaj6a50Sբ5ɭ;m0X͛3ښU TՔ8,U%w9LRUHU0aǩ[y|ejSN 8^^ኰfڤ3sX #Thj^7 Y}5_O"4lRUSbT 0IU[ UUL9޲S~:[}T^t g{]o;aa1{IՖt XÆgޣаIUMRUr'p$UunTU 3p<:V ;&߼\^^׬k X9䎳s9faYI5I X]+6)1qXJs-a&Sw<12ô/0p8|MXڥ aYUFY.f5޸ 6)1qXJߠ?ïf9LX2{ɚ2O?|5Cʜ'N.>~G s8~>iS !*Vd֮֬Uk҅LUw_֐$f^^B`+޿[*IUH |yUw'^u8cWsлj!p3U1c~z5JY D$U%""*@}ڈ1q[F=>M Y힒^VaEkV۵h:up꛳FZ *IUH eSg;'|ܽf@ݴEj:uv P"H+A}GY *IUH Ob*>sw|S`zb99όP7!Kpj U%"*IU/ ?Zuo׼}sS Y0jx3ghP;fuݭNl]yTDD$USvVJ#@d9%׬l 2y[2;i@+kV!U%"*IU5 {[tXl#>ݡ^޿[*IUH |YC^u֭vm0Ug¨5 8 Ws7O Y]+34TqJс`uFrݿf(,^:j-gm P.kVnp޴a֬BJDRx׊\3,QdMp'>PD!0a"8|d=GOݔ^(=--X#v\^ӢQ9kVw0 V$ 3Uժ{ `dp^buB>ddjz]ʰ3pꂙgz5o?vw5]MjJ, kUŝ0âUfd00G˖g)-vj<}75iY^kVv>FҮ]kV'U5%fX'aa`of}qi#&& 3pb|n?όm>/7w9ݻt<5 ; ;&MTu?Ъ3,ZU0̰VR5lhɤ)gd00 ; |ᨎM]4 'Ua) L6 6xN_j*ΰV5Lrƍ>9skVVUjJ̰hUO0ZU{VvIf COXn&1} 5ש[=+v4w떧 =3fpIfVՔX֪;aEj Uժ:$a>N'5a 0>.YvI0 kU_z5W]_-֬z`Ӫ ZUq'0̰hUAZUgd00ǜڹ5FS9#wuo[3֬KvkVQnb֪OZՏt(U.kV=iUMafXƠZU3L2fP֬۝׷}k Kp2 Pv!3O}^pSpԹi /U֬jqf3 kUCpZr֬~ZU1%fX'aa*E dp*޼dNiSGFz %`׬69)8ZՐ0﫯:-!]'bJ̰hUO0ZU{V T4ٖצw͈W5K:v|3 kUg~2j'UlZUSbaX*1VU fj֬}nomݰhR} wW/b﫫 kU% ?=KYZg 3={\,X[UYvyg793,֪l˛XW4c`T;SkB/6L f+QJ֬ٚUT/5F_ה2`~m׬./Q4cLɻ-oVЪP f׷ DN@t9# fZd`qӛ4xMҭYVUD""UVٷ,f  |rƍ>NkVUM Үg? ͇TUhUED*""ZUhU9*ٶYb3.LgWl|begZՐ0 ]urdNdY hUEDDc]]L5!T9^խ~`+Q |v]=6fU nTv꒫uKVUDD Tw홁:p<rwcXӳǮאj=> >ښUVUDD Cy}Xǭ:Aϓ-O;u˪E@2qj!gXUѪhUxvY9j OkqJb~zs'gU%?4ݑ{6ϴfU*"UѪvoZv d>,XսKk BfGʮYݳqVUDD T=={y:UF֬Uj+[ A mYժhUEDg֬ԯW/X= vN"fuø~ÃVUDD*"U@*'v\::P%,Oj(fuᔉ!5ݻt$kV""ZU*^}qFx~fdm?ٴ`+ʮY9d@ky j?ޚUVUDD o|4?|֬IfbAZ[UѪVQ~H5! 3,;gw\WO1ׄ00Hfa0\̪u`ͿV~1 kVkrӲ7eVة#qi.fAF ; =/zI5niךUlZUSbaX*1h3jВAO5!0`a5n B^j/apkV/4Ug$Y!5ش°VU 3,ZUcPV& 3p}}kVM% WCr{_]v1agíYi? 9=NhUMaa1(0`aٷ,55.mѤ1oxCcS'X=c fV-Hdͪ6)0Uw VTUuI 3\3yf֊O96kVM% (/ YMOK{|èyk&& 隫85۹VՔX֪;a֪B f8 X:s>/חhr׬f-֬:ǚakBdjˌEW4v )0Uw 3U5V& 3p*g;jìM}yD# f{bZՐyiNM<ئgXy-7\vjzN0Uff{ZUSbaX*1(0`aS϶6 5ٽU}FZ:|Pf֪&6 kOT'|ZVՔX֪;aEj 3L2fTЁ}s?/Qy!.wjnwahU_z~Yݖ7VՔX֪;aEj 3L2f-VW7Qۦ>I_b8|e׬}F V5d,m{wYAqBjJ, kUŝ0âU5ժjUa0 "87Sz`{/H1\%?$X8g2ЪVɚN Y]{{oqBjJ, kUŝ0âU5ժjUa0 "MqxNȈd:}F)w!jƚUgXZv[s:*Vۣ8U5%N`aѪjU0`aSo|4wG_yA_b8kVaju\zN5yk )0U믿C aa dK%|SFzt|Q+D_R錽d_ qI 3̾5Pk6H?} k]ZLY|pJ7:=-Yu5,;?!{^h{ Y}5)2DM kUEDyM,y+1C0f)w5!&V/Y}C':>.f5x**1w .9-ׄT^_s\ۼ5Kь1%M4rUѪZUpD|ݛ֬^o'kM] jb;ˮY]ҫ=s4>a [ judӲ-O+fu>A*"ZUѪ7kFK/.&e|\5#֬UٴhUCfzb۔lcYժVUDDHQ֌-Qڦ[ T yO_^`ey̤8{qfb nqJ5fU*"ZUѪ Ey5j@eբZ-VfM53@z}M.ˮY]uK[kV"U*R/55_ާIfu;'Ae׬.ҚUhUEDD T}gߔ;5fH5}z:UO;?/UhU{ʮY}qæ@2YmqJfK敻f֬jUED*""ZU(87SzjTɚճh[_hx%M "Wt}hUEDD Tvnգ5@t9}z|4X䪓l\A*"ZUV7v/BkVd^5!={\$2՗t9k4?T)1a;aaXj & 30pyS/ilͪ]:a ;_rZf׬>w U5%fX'aa`cP0 aS5g^s5a՞v50é,Yڵ=BjJ̰hUO0ZU2 If 3 ޚU} ɳf cճhQ|!0B\fiVg|5ZUSbE}֪jUA3K&& 30vNfU_p2eբhX8g20Bɦu7]s%M "W;o;=hUMIfX caCK&,ׄ8$a>Ydͪ$YڽKq v nQ%WdͪVՔaѪza0ժ&a0p>t`_MX/a8Isb{Yu&ޤmJY풶cVՔaѪza0ժ:0 aS_X:]'KS׫[8٦ey }t*&$?[V-9\Z8T^U5%fX'aa*Aΰ3L2`6m3;NP+ށ)gӲ[6xiN՝aׄ$1bsA+_4fRvͪVՔaѪza0ժ:0 aSߚ}Yة/hya1FbsAKiin;kVa/蘑O^@jhJ̰hUO0ZUjUaNHf 3ٷ;掁:ĚUUB"sqf+֪"cҜ3On8̚mybJ̰hUO0ZUjUa0f8E \v'0H1>˦? ##fu˪E kU1ޑ"M9kV~bbJ̰hUO0ZUjUa0f8 o[`VRkVg\vyFYd^S֬.ϝ°V!]ޥ'׬>u֬jUMoU뽉?+Eq:x`\OZq]ヒ#;v(yBQq]ׅ:?׵nGۖ5ׅGx?灛5GsvyC;{䞒gWx#?o}sYOCθ.G۾%C֌P>(?޿HZq](&|qU.T#y-B#T>B; 󜗕͛"$j;k9 EmuPQ!U:߶\_l}k _({V݄/ˏ؅j9 NҞ0oyh7o9soٙ70X_%ʽy7Y>6:`̬a͏"# ۗl\؅61xZHB[W-B_Т8 %9?@ڽK_} ?o'<{wIxüyCz{$aCf}ռεw,RQqth:4!4ZU_k|FUժjUZUmVUkUIת3wȕ5}iUZy׫[jӼIҪjUaQ>@˓yfp=ZUMCӡЪZk5ZUVUUժjMZU9ת\dzgfNpZUV5seբZ-V_g;ZUjj5]#!]ڦUth:4cժq6}ڸګjګjګjjRU=5ګjjMzt4aګj!U-f]PX}oCӡt+G'aa>AGn)ЪhUEDV"~gYIOK-V/݇Ѫ"duh]KyiVUDD*"U Ey[u8}5@0w-NifͪVUm:ӃkV|xHu_UVHv]۷}MH}qiS/f.眳b-ϝŒV!5~YȜշvέZUVHEFb3#,kV*YҮjzZڣcF2UEqgˌ.(U.6iUED""ZU =y?kÇ:5fnXڧgO6>ğ֬>%m[$VUDD (+ߚU IX3AFFlz^Ek2SUD4'oDg83kV~oܣUѪhUTĚU yXdg-Vdd,2 K.?)fueZUVHE(0ↁ5lya:~9d k?ѾT&RUѪhUT5f6f(V{ta*f?& ʮYݱxVUDD*"URkVmLՁ*r׫[Ԥ%BpsZߢZYժVUDD Pՙ#iY^N-V׫g*P%ݧgkV_ԪhUED@*R^u'z]{%OvRztZU1w}&a%s*yےeU}}TL5! 3 f 3kVekp|[oXSLd&AiijK+vIeo֠s98%'_r܇M kU+!{-!ׄ0̰?4Lx֍.& 3 >5Mϟho_D_6NrN Ym֤ߌa&J֬zzXР$\~ВÍOe93,֪t`a0 3ˮYqa$w |0\%kV[,Xw|_1ag׬v񲦑Y׬<CjJ, 3Uu'0, kU 0$ap*hìncs:Fնuԗ0\dӺv Y ;$w̨ӡTʦ{\U5%֪t`a0`8 ן[֬K|`~5td:raCf_7?ny?>%m0ZUw°VՐGYy-WL5!0`aQ ]:}ګ31Uf?-V[vj8$'ɚIw:OjJ, 3Uu'0, kU #6d}M3L2fa [LթԚէ/UM׬K8fM՛;y<30M\uo+[°VU 3̰VՐZUgd00 kVgF{vkV% kr}՟x]u_{ \9d@ˌO/U.[ °VU 3̰VՐZUgd00 oקmGg6^kV% ÷^+Pvh}{^&9dM䌺c Y )0Uw 3U5V& 30)jxe5VkV% $eկW/XmqJKt4 зg5!PՁ-kV~*YU5%fX'aa*Aΰ3L2`aw_;SةSj5( ox˪Em9+XMOK5DiU}]}EECܵMjJ, kUŝ0âU5V& 30RpkV_c]:> xH֬jU}#l 2Rsgj)0Uw VՐZUgd00HEh mux_|e sb hU}9OΨ;?/Ѫ ZUq'0̰hU UuI 35"_Zc^6Փ4zuLҴPfi/fuøYժ3,ZU0̰VƠZUg&a00ɷfuYNiS';Ԛy|e ޲jyZ֬Nh,oZU_(BMz^kVyYkV V$ 3U1VvIf 3p2?㌋֬fjku~ xßlZף[աZU"|v 5RU5%N`aѪiU0`aٷ{u8&k7W d_l.Ⱥ}P`j.?,XEV;y|Fh]K|aVՔX֪;aEjHUժ:$aF^}kX}mʩ%e׬4'C/!iYyZixkV V$ 3U1VvIf 3pOT;vj{CsYժ"֬kdVUD""UR϶6zS+|^o z_ݚU* >q.(U.Կn)ЪVUDD@*;3=;f5k7UdjzY-fᔉ{_]Lぐ)xѮYժVUDD >b1U99֬@;)<5ZUѪhU(ޜȬNiS/55@ɦu޵̚ՕέZUѪVHEkV{K嵎5&e oK{6;5ZUѪhU@k[>0S^}T fH'g{npkUED*"Uժȡ +xf: kV[lӲ-WL5! 3 f 3kV~g꯻TΚ^]qQn15wΚUgeCcP72kV&n<K#GSb%|ZJ޽{ H5! 3,l|*ofj۷n|eu1ׄ00Hfa0pҲhM:|mG9޾vi3S6tMa,a8|LnQS׬VH1FlY(9e׬l)1âU> kUaHԌ6d}M3L2fa0\iYbOu5s`|e S׬VUR/}zYXժ3,ZU0̰VtZUg&a00é_=[_]ǚU|e ֬&y Ū/~3ԯMU)1âU> kUaHUua 3kVg\wn`dթY5W0ljLD_WIZU_(Gsޤm{5r4%fX'aa* 鴪0'$3 f8Ey>ۦ~(_Ypt9ի1kV [>V5"1Fݥ];,q46r4%fX'aa* 鴪0'$3 f8uٶFfo,a8| /l{Y5yOЪ[jUq|^m_ :СC V$ 3U!V aNQk֩֬fPZG|e aY:o[ kUaHUuA2`aSUE`?n Fgᔉ 22bՓ4zuLVmU[EL MIfX C:3  3$wF2kVG 6W0>{iiiUժB~: 8pw}giJ0ZU 3U!V 30 (_lK:FF_||e !ɦu{_r~~u]U"Vu̱ ܌=z8xQ)1 kU 3̰VtZUg$30 3:ofY5W0\kV3۶ٲjVHA**U~̛5#~؛yEEE 3Uu'00ZUiUa0 3pts3k֬~(_Yp4'&bɼf5yZUتF[n{3֭[74%fa;a֪N dfaS+fd׬o,a8|,=U.:u?4ZVުFӏ{Ӏnͪ)1 kU1D_0Hᴱ%|$n(&J'{JZ}M3L2fa0\n c=ڼdGwM+b 3e׬#vq0b%Gߪ&caE6%^)17cvo2iJ0ZUmyKf1B#7D_PkV]6fu\%kVT V%տ䘻E_ߪ:'8zVuw\\ye׬n޼pRD""ZU@ /g*V'?n3S݁$Y8gVU0[f5==}ѢE"UѪZUe9;]j$ÚGnjԪ*VfìY=z5"UѪZUeϟ̹8f.bs;`kU.bVdXzhlUVЪFYbY`j˖-YV@J"{p9kV1aj\;%e[գgZX*"ZU*O+ffZdZ~a$Úh&e OVU*iU\wuϲkV{CKѪhU({n kV{5@Y隫]NZՒF5=z-ED*"U|oޭkV\ kL*3kVkr˪E!kz}Mњ$\jUQ}[fjQQѥhUEDRn YWg>b(Tɚ'6 Y}ujѪZ(ojݺuϟoz)"ZU*e󒙳:ZTZ~y<>֬kptn*{YGs:ԚUѪhU.q]3kVњU I֬MRUgVkV/첽{cVUDD EfiW YW^0ajf6T[(]ݤq՝;wdVUDD uYr5Omf@2Yml5UHV֬VUDD ey3IkVd^:~ĝI 4>UjUaͪhU_D_0ȖJ 4ͩt|۷}M d 3\.mymEkV{fGxÚk}Mnj." %߿K%~-fG)8&5%faj=?FkTV3ICM|*ob aa0`aǡf]wv`&jjEl_D_[ /LN*UǸjy%߹Vʬ勞]kV7olJ, 3Uu'0, kU 0$a0)jЁ}!fu O1W0$kV_?1UkE kU ZUC:00`a0p.z~Vnf5SGflj,a8I֬Nuw%~:w1KìY=zw}gJ, 3Uu'0, kU 0$a0)j˝ڭal:cޝYU0\֬2kUߌu`jvoJ, 3Uu'0, kU #l_;G&& 301ь YC_fUYpYݑaa(oj͋Lafa!*b԰%&& 30aϺ%Pk6w Y;唓v9OlvfekV֭ϛ ZUq'00ZUC:hUa0 3pʼ׬>١졆ÂUZX: ;Z0[(_} ]h5 3Uu'0, kUUuA2 aqm(f=;|^o,a8d>ٴ]M\XE/&aXzL0>fG4%N`atЪ:$a5{fi4{;dͪ*!gZ:l@ߒ$[X+/ϟXzRFQkUCVۛ=R͝>{lSbaX*!0`apy5#Ose]:֬NUu!󷽻ˮY>|xYjJ0ZUw°V ZUg$30flkS6Y}e\,ajbs]ε_rV1֪Vc{~SbaX*!0`ap>t`׷ Y\,a8I֬֯W/˹vGfUkU+u+5l f<LafXtZU3L2fa0Wf:?T:mzYU0$_2&kV ~;[kUCf;oufE ZUq'0̰hU 鴪ZUgd00`8u 0gf`jZ3nSّ ֬]oZN%`'֑kU>˻]ߛ ZUq'0̰hU 鴪ZUgd00`8E ykV￲}3mИ=-Y7-˻ߍ 22"%=-O/1֪  \_}5]jJ0ZUw°V ZUg$30fJ}gXzgFfUYp2ש}F-T̷%'ħ=>kB*G/#$gd00`vk:kV5D_6N5?#wHdp$(u+G'.첃 5ðVUDg[ĒgcL|Px0{L_ ?f^Rs*̝U=eE2cպu]֘TD*""ZUhU19jԚU ҷw[(F"% QsK>7n\5Z*"ZU*U@%^(u֬IZAFFLw6JNZJ7~XڳgϯڼTD*""ZUL^[1D_|Yy5@U}oLk4T-P$R(\UhU+ kkV[nc#Sj٪~{M\? _q>ku. 8p ߿? :t(?TZ.ׅG帮U-yh7oh<̛7s񩬒g~}ٟ>߹,߿'k%|B_}G~/NB>(?޿HZq](&|q[ pa w 79ӏw*МiOuW,ypתBQw[\Bo=܅? -}v^\_v>p1w .aVKek,Zp9/w/6쵦?4 cP{筇֔%1抯7p*8܅nyt݇ЧKsrw?k.{ }겲]_ Be9wW7:Yj1%]?2;(޼ _+ Eք/T[؅=C{+ =G"E.9b5==}ѢEM5Qhz8 }q_= _(ޯk|Abںuk_C% ؅6C]Zny7o|̓<4WV [.ޮ &|ގB_⪊8 [vW!kuۚʝ~pY`ޑ]۷&|k"s>x`u ErǠ _uλ_zqbj~qݼyf$ʾd\-u9#q}=p7ohny7ohny7ohnd8~ܛroj}w&| I0V՝3k֬>vwNZL-%|[uk[-\! 5kp*؄PSsk[$|u Kۄ^hr/·C[sd-vuc긒⮛{Wp+( {V@'|.}[P n&vryDoPV _9U>$)岞=.կ*j:4dk:ZkVUUժjUZUVUUժjU0v';G׬jUZjUKh:תjU8D3޲kV+HMCӡUժZkVUUժjUZUVUUժjU5Ua)W^UVUgUZՔjUcGՖ-[{Mzq6UW^U{UUW^U{UUW^U{UUWR.W֬NhIګjܫ:0VGoa!R؇z' b h:4jWEDg[Ēgc*Zœڱj;0<qΎ H+CHݒbӲf5Vj۝{5c2ĮYp4^/TmY2OhUᵵ|r`͛ TE""ZU*eEkVW>8z^?㪷{cZZ|B{EhUEDZU[θ0;fmpתeL/49pdꓱuoIhUCO?5FfUDZw}M K *y[?i1J_Y]L5! 3 f 3p2tX:yɼfuڥL-&Zprꂙii1iȧ?Ƕ8-Nia*&śZ|X9(}7`LVzzz-y8p^ǰV՝0U5B fao3wձo[XYbhȺ}PvˈDr"cw frkUe=۰aN mͪ)1 kU ZU#$00Hfa00Ȓ{o Y6}ӓ%(B=uDq="_8g2VOufnݺ ,0%6IfX C:0 aF޼lNnfZlpͪj7]sU$8YogSkUށt2%6IfX C:fd0 #E $Ue Վ n|bS~~uv:Z$Ν;5 kU ZU#$00Hfa00Iͷ_z͹ɼfUYpudkƏ3ZۧԤAZDcUMZV/[Q~ۼy 6ËV$ 3U! 3L2`a Օ9QplYÇríޓ{y8ZjA5̟?ߔ^'aa* fa 35yٜ׬>աVAu&٪_kVo@:jԨΔ^'aa* f$3 f)j-=5#i=uu&Yj1OLz8fu֟~)9hUO0ZU10Hf 30R_ɽ]XybYU00H֪V9˞{6fN{LE}֪aA2`aIӪjͪaAV5[n[ËV$ 3U! d0 #u oʛ2;VlUfUY0 Y$|G}oX:pC3U> kUaH0 afaoJYgfUY0 YTL0>f /<Ѯ)9===X֭mE""ZUHQ[ ZUy2׏-VO;?&"ZU*.yw]X:}ګ3IV5e)wO>ix+UѪ@r3KY}Z֬V5 ws`~!#\VR֬όdjg*hUS Ym׮޽{MqE""ZUHQٷ{խkVGg6f̺kVO<Ģ"\VRY5UMm]:|\VR93kOu5s`-hUS> kVߛhUEDfηf j= XDZ9x?3̰3KxbJg;?,zkBf$30faT̛yOf֎-VPk?{w^U}ʶ`:Szj1$1Z;CgVͱQTZmAZ>Pypx `T!"A &1((9$9\b^E^Bج doBH>urggy߰|o >m +,2O 7_]|peno7/ 999nUݨzcAV!6rͷqrKYyMqǂ(0EVXa*<{K7̾OՉI;1;W?M6]'bpK7St9qrBjFFF]]{+lU0D +LV8f_U:fKg c9JalUă̿1nUwPتVXd* +pt]<1ҿ~tc9JalUڕ]0dHŊuު +U-$*TXaVX9 u̞K7c9JalUUVVU"Sa G-+~XoXbR"[U#{9UNP [UB L +̰ 2{*qxKy̶aR"[U#{9aucV݇w +lUu +,2VXaVGn N?% s٪ˉW~s>UVXa[HTXa kUVVU"Sa ?~=YS9JalUĊ Yuު + [H +TXaQ/:;s (E٪ˉO1cǎmllts}x*|'(0n!)LE +LhzY:;i7{Ydjd/'fMV>UVVU"Sa Gp}]Mᨌ1 ss"lUĢ} c݇w êTXd*TX~cVs" bUĎm[yYuުcp%\aњ?Mj>gKv?V5|, Sd ++<9oꌋzhGB~T6|,"Q+1ޛ:'rn1wVU8張k .H$I;w~JhqNzkKw\ݧ_Ut3`U%I$IiaދJVHxAcVaUXUI$IGBǬ>palF֊_0dcVaUXUI$IG,}z/99FOkrsF;fVUU$I$y0jQVUFq? *J$Il/5\u^Yg1$iU˞M~g:fVUU$I$y~>fuJzlâFwܞ11$I$#z{λ;:BVzhͣs**I$IܹmncV/Y%IjcZ[[f2`U$IdDK;:nw-7U5/{ *XU$I$]wsI_,yb$u߮F ˊVcؒ%KRUVU$Ioxy=x$ڪ.:f5//ލeXUj$I$#./tJVzh}ƅY>|x]]{˰U$I$Q?jwY}06U5+^qA_yaUV4666&XF?Mj>g+S?gsmUǂ(0EVXa*W{[z_4=cG~}m#EfkW}c7uˉ=YCՔKNRتTWWo=L +gk89%Z[|&QXaTXa% VǬgǒY&G >Ñ[7uˉOkrsFYihhpUwPتꚏ +,2VXaVG;=zٗ=zxԎY5G),2.'❟?3tȑ#8>ê-$VXd S.aqAV,|;QTXdZU#{9qcVǎYuު + k>TXa 5ggVzL>jRXdZU]NǬֺVتJV"+LV Gp}]QCǬǬVUǬVتJV"+LV Gwe5NJ(*,UժˉǬbKVتJV"+LV Gp鲅s3{=d9 lUFrǬ?K>UVVU| Sa Sa>5QΛ%Ytw̪9JaiUu9v[q-VU_'(Un!)0EVXaV GgW͹>+ta^9 lUed/'Z:xN~̪VUNPXaXU])LE +LVԺj z]t䛣VUFr ҅Yuު + k>TXa r^Y޳pm(*,UcV݇tXU-R +Un!)0EVXaV Gp%s :eԥUs"Ӫ.i>tՑ#GêjR [U Sd S.{G?J=cQTXd*#{9qcV:1[U;AaaUuͧ0 +0VXGo~z1ӮǬVUovecV/p}x*|'(0"Sa +|zGcVeQTXd*#{9qcVw>êz9x`aQXaxc}-|[gݭpopd +LV +0#U{o/:jXrQlâ]h,4|l=RXd/ly{񶋽78UAa*rZp5_Yh$I$ bUlmqNzKKw\ꕟ'U$I$.oM_ :;i]Ul}ȑ#U$I$i :>]/?g!iUed=1n\ê VU$Idt} 3V'qodHZUY[Ŗ.]5`U%I$IFם/='*CҪimUM7Y?~|CC;ذU$I$Q?sm!iUe V[[&6`U%I$IF>1.N}oJ I*#kcVSSSdžU$I$l|NhX}(֥,4$v:f5%%gu+VU$I$/,.?eS1Ґ2`W]:f5''1U$I$]k*:f4$S?tȑ#86`U%I$IF>ZpEKR+7h!iUed]} ZRR6`U%I$IFg'ܲ(g:ihVUF۶X,VXX6`U%I$IF/ +?>:{fCҪI>Ǭª VU$I$ff:eԥܐ2wՑ#Gֺ *XUI$IkBǬ>p۰xCҪҊe?߿]nXUۇ] >Dab_\m~N&n[75|, Sd +L Pu԰c3Ðno~p^^^}}Q^:)U5VXd* +0#]͒K<ǬVUov& IBw;Nƶ?RI?Ё[c%@ ?RQQu >n/%zr}}}inmc[N>yUN?C߼9o{'auSl{[;o%}m[ڪDrwB|~[?r|<|r&T~(&@)uy$'x 9;y~y;y:r}Fy~vҏyCoR'oR 'zً|U?ϓ{cV7md#1%AI?PeeeB_~DI?P#Ю]{DgRn=PP#B$ vy5'aMyc0at}cDH'6ѫݲm =PI?VBwmONNݱuSҏ=(t턫jtM<'?y}Fy~ sv|':%&ՑQ;1s?ttӿ5tC7&󶺖 )QC{ WK'@?јIВ~mˊo(5 -|oKc%I'@.UC:Ne:f5`ƌ.VUUkUժyVUVUsɪjU*TXaVXa*9byYBǬ.(EzV<аs_s>UVVU| Sa Sa־~GY5GD٪.RWYMKK; v᭪𝠰°S LV +p7/-4rؗ*i:2GDU՛]WtաcVvZ᭪H)0VXa San~0ǩ8feVUov]w_0dHoUEJaaUuͧ LV +ptSlY:nfj2L7'rn1 [UaRXaXU])LE +LV8~sی/~#d1(ȴzN<)t_^[[>U)U5TXd*TXak>f2GD٪.nX]:fo߾-ǬoUU5TXd*TXa:fuJzlâ("[UEwܞ14ۀ݇"°S LV +ptݶ|qcV;G(ȂXUECkǬVVVoUEJaaUuͧ0YaV +pDQ:3բ j2L7n?^p+VpުdRXaXU])LEVXa +QRo5Y}/a9'2"kr{1᭪\Lp=A$I~[u԰:9#曂H9-///qW]v谧"tj,k:f]*XUI$Ilb̞ONDҪIG]:fuر [UVU$IdD/:fu't*IZU|`X,?fddչynUXUI$I^1٩>g"iUedݰAs9s*J$IO{cVOZHZU1n[UVU$Idt-] cVUVUFCkа[__FU`U%I$IFfUVU293I;~X>|cV*I$I2~qլ+1$d%=ka555uӦMn[UjU%I$IFץwE3 E$=bXVnUU$I$][:;VDҪzhMnаzw744nUU$I$Q,yȵYU2.:fuȐ!|[VUP I$IF>w]zhX}8{WHZUY7.z9رu*XUI$I=t괡glM݈UbcVcX~~VU$I$]_{r^C{.{触#VUFOkrn1tjNNN}}VU$I$Qvκa(JҪ(;}X,?fddTWWnU444_:%jVUovQ.\Ǭ^ڽwVتJ| +L +Ln''ܶ0a߇a4OdU՛]d Wl=tji݁:AatͧYa*TX~Y1>cÒOdU՛]d RQׅ՛n1VU_'(UV"+LV +U;J:fuٖ ~"[U\xOdžÇoUu [UOa)TXa-Us0toݸd٪Jov-Ҋe?X:NPXa*])0EVXaV +ON0g:) ~"[U.Y~%#X,VXXh鰪:)VUSXa +0V8|"t̞'U"[oˋ1VU_'(UV"+LV +1Y]cƍ٦&VUzrX:)VتJ| +LVXa Gp}]͌e:f'UժNx_Zs9W^tXU-R +UV"+& +Ln? 3V ='ڜ ~"[U.Y~!cVg̘a鰪ZVUSXaTXa*pt o_̣YǬDқ] _3:t1c-VUPتJ| +LV +Lh77~X-1,O?f³MN;~XMKKmVتJ| +L +LV̾:-tC;ǬDқ] +^ѷOaeeeEJa(lUk> +LVXa[xϾ:fu/:f'U\7ge ZTTd鰪ZVUW$V"+0VXanן[au/,t'U"[ڪ?#tرc-VUPت5 Sd +-w :c%(VUzrEscX:rX: [ vOT4|, Sd +L#XZ? 9-/nyǂ]976+7;g?]aU[n++$I$Snn}E-RdW8';.rGݪ=cVo>EXUJ$Id3z3~?7JVU2_wa5''oU$I$f:fu#>*]o"dtÂ޽Ռ:`U%I$I2f_U>f/Y:EZUȺx͗CǬoU*I$Ir^_u԰pFʶH*.?b"w`U%I$I2nh^QǬQVU2~Z[3:t1cϷU$I$ɈZS}eg 3{LbǬVU2ΙH,VGU*XUI$Iu5^>fA[n"ddݴvYY-))qWߪ VU$I${o /n||G ˊVSRR.]ƾU$I$IF-v1s3z>y&+ҪJFCkjU*I$I<ʪ7K83~X]cjEZU(xAA>}jU*I$Il5f^uAՉ{VڮH*Y^{AjU*I$I<3޾0a_|mIu߮2:fժ VU$I$ym:=MǂEZUzhjU*I$Il͒բS2bVU2.^PһcVLmmmaQXax-kMSظcAV"+0VXa*pB~qܫ:f^lvlMD.7 N䕵_9ܮrj,VvzaQXafL-=fرVUXVVUW$TXa SapD W(6OՉIt"[U3xjUHAatͧYa*TXa;ԃM :n]/?gVU/ŌlWCǬرê êꊄ +,2VXa*0n~Ǭ=c ~٪꥘-YC;1VUVتJ| +L +LVXG3cꌋzhEz)fd R/;1VUVتJ| +L +LVۦm^=kj٪Jov ӧSjUHAatͧYa*TXaOu5^sQhXt̪Od*)ضSjUHAatͧYa*TXaOO¬VϟjVU/Ōl=#eucV)(UV"+LV +pŧfVsOs ~٪꥘-imUnp̪U"5 Sd Sa4QΎҩEY=&wh'?f'U>.^Pһ=fժj [UOa)TXa w"#9?t^Ϫ*UK1#[x :ǬZU-RPXa*](0EV +0VXNgя0auJzlâ?VU/Ōl=楧UE +lU+ Sa +-YV4+cVgwwϪ*UK1#[?jUHAatEYa*TXa;dgYtcV ~"[UNtִɭY"°"Q +LV-|?_uaqnϪJbFp+krjʬ) êDa*, +0VXaF?(auRKkҪ꥘-oXlɒ%VUEJaaUuE0YaV +0[x2{; ~VUZU3?uաcV뭪0B+]i4. y덍 >Da) Sa {cK:fu٧]//ۼt~9oFw X.Y>|x]]][:pyk-|e\@$I$ٍ`mXi#O練K ~g?8W^Z`U%I$I~7ti/n#ddS14~XMIIYtU`U%I$I2QΒ9_z1ҪJFOkrsFY9ǬZUJ$I${K7L'~X]cVI*X,~XMKK;E'ZUJ$I$fo߾/ZVXa*](0EVXaV +pt].fڳ~ϪjUb#[p_?߿ԪjR [UDa) Sa GE?nhX6-E ~VU*++ЋX,VXX|rtV'X =PCCCTTT,Rm||BUWWߨ6O^&X[[օ =<]y=z2߼nn3|r3J/;~(&@)o)|uy%N聂1mÞ蛷##w7oDj'XM^(<&uB&պp瑽k7_%\VNh" >?J\[#(ye >nz=>|筄O =V֖?uFBt2૜Bomcrob^[YYy2KG5%QZONdBFI?V[]k׮x]$I$I|yVUUժږU5p߮.dDګWqmٲ%UչUusUzvG~kMsqm{Zp}m[[+V01 >-lP4RE@''zc} Eҏ_֤(x:-<]{n< =P:s-v}vu/Ay E|ޤNhߤZN<cm='*y?tt:g =~N̍6njdtǥm4!o >?JV~I l(]cF?m#I豒~UZQK:wɷ^\U:WVզޖh~sU'MCC$Aa Ca + VXv.;;;t?po7]*ۤ +L;aY&b7Ǒ#G&&ZU[ؘVG]{NW5`@cjUnȳ>۷o{i%I;Ybİ7X,ߖ7V*tO***RSSNVf5weONT{KhXmhhZU *>1$Iƻhn~>}̪U"Dccc^^^,s*I-كſ9] Ԫ cY%I2ު=#e~CAAAcc[U >fNV~khXihh@DJd8rH*DLJo}?VvW$Y7]}րG;hjժǬ$;/2Ī 8¦MU$[z_M7ZUۍꭇ >Da Ca Ca]p]];7)n2XݩɓbUePXa"CaVXa 7Аժ=nKV Gp׭. +  + + V(BǬ~sj/QXa*VU} Ca VXa +f݃'ܸ(0e +PXa +PXa9rdϚ61VUSXaV +,2VXa(0VX#兆Q^KV [UOaPXa Ca 73mڴX,?]U^Tت } +  V + VҁYi% +Lp٧PXa(PXaPXaC j/QXa*lU> CaE + VXan1VUSXaV +,2VXaV + X|yJJJ~KV [UOa +PXa +رcY(0e Ca"Ca Ca\<8|pǬKV [UOaPXa Ca . cǎu̪DaU.V +  +0VXaVxb1en>V [UOa Ca Ca 7cǎ(j/QXa*lU> +"Ca + \s5=f^Tت } + VXaE + VXan=PlJJJz۱mTXaF] >Da Ca CaQ.{Y]WͯSc& +L_a*9x`Y%Iv?S?cVIVUcVIVU`޽iii:f$iU 1 +"Ca + 1VUSXa( +,2VXa( +顫j/QXa*lU> +"Ca + 61VUSXa( +,2VXa( +ij/QXa*lU> +"Ca + ~ym[%*0.J Ca Ca + i}j~gvcV% +Lp٧PXaVXd(PXaVX1Y}`Ck%*VU% +PXa! +PXap3Yu?k/Pa(QXa + VXa + i}jzڅ^Tت } + VXaE + VXaTTTY-^^BVتDaV +0DVXa(0VXa(L]]ȑ#O1VUSXa( +,2VXa( +p祱bͣ^b/ +lUuQ0V +0DVXa(0VXa(̪U?~wlb/Pa(Q + V"+0V +0nf݃V1ǬKV [UDa +PXa +]1MK [U]( VXa + VXa444憆ܜџTﳗK [U]( VXa + VXa,Yo߾_?7K%TXaƆ PXa Ca CaO;%%%j~g+^q*n_ڪ& +L_a*{RWWwWXl~LIҪ Ə1cVIVUV߿sݱm$iUݻw<8~Xۧϊ$4S[[h>t$iUаz`@̒%K?~+n3 IZUh"555tfUfꮸa5͙$iUǏbkz$iUUV?~X=vIҪ @3w>ճ p*IҪ t!$iUƱcdžܜюY%IZU獶0ǂ(0VXa(,PXa(PX˒%K1cC [|, Ca"+  + TTT7_.}ujYyMqvWai/lUuQ0VXa(PXaVXa(SWW7ra5͙1({EJa*lU +"Ca + GǷ>f*{EJa*lU CaE +  +f/_:fuİeo/H)LpQPXaVXd(PXaVXH~,^"0E Ca"Ca CaF3W^yeϚ6^bR [UDaPXa Ca cǎ ctǧUTت % + VXaE + VXaF3Y-۲^bR [UDa +PXa +h1g*=^bR [UDa +PXa +h.;;;~Xիׄb/H)LpQPXaVXd(PXaVXa48f̘1_};e)U.JV +0 +0VXa(0YdI,V/?VpXVU(QXa(PXd(PXahK_Rzր/XfH)LpQPXaPXa CaLiiE]?b939aR [UDa Ca Ca E޲e˕W^:f57gUTت % + V V +0VX&Lݻw:bX +lU + E + V +,rK˗s9͗K_]oH)VU.JV +0YaV +0VXcV/(0{XVXaK% + V + V +,rs1cƄY;>gH) VՈr PXa Ca CaE(((bjzڅU{*Z[ݲmM VUN;v8p`zAV@HkiURWW7|a5woǬU>///tj_c I*h0texg$s9'~X=kMkW+HҪ ΎVcآ ԫ''@H=)+jhhH =V]]]rC'@I6ѫ(<*'X]ya߼;MŘ1ss?tŘ1sssc.ƺX'䄎Y+靲m;wJhSD?V%XI?PýI?VBGVN/|~ҏ_֤((c_#?cnya߼;MŘ1ss?tŘ1sssc.ƺXAAAJJJ:4—zuZ &:%@;nJtJZON-ۖ_*+ѭ.Jt =PG>;fW^~UժjUZU;ת;(#Ҏ߼~oGx{;TIs1yy?ts1yyybX |͡cVo%-Ǭ:WչUusU;\X,?fe Jpw!I"t vؑ?=hP$iU ><~XbMgU40f̘19?MdDUsq$z:VXa(0Ya(PXaVXY}3j+w[c [Uہꭇ >Da Ca Ca |򔔔YhZ|& H +̎/lUuQ0VXa(PXaVXa(褑[ڷO?f/ Ӫ +0VXa0VXa(_{wTq! .!-GPjMDJW̐TS ::Z"ġtP/48t|3ۂYZ?_OYC]j٩&쬪K&LX d"L&,cnڏwFaΪa„E0a 0a"L0a\eoVOGz/E0a&LX 0a"LCz>9Jw%0aVU&,„ 2a„ a„Er,$IG^&lU^&L&,Ȅ &LX aȱau͝j,R ;R a„ C&,„ 0a„ CAեŅp^&lU^&L&,Ȅ &LX aȱ+Y^&lU^&L&,Ȅ 0a&LX!Bfj"LYU"L&L2a&LX &L?uՍ^&lU^a"L &,„E0ao/q z/ 0a&LX a"LCy^n7)„U{)„ 0a„!a„E0a„!OB$Rir[nm]Ka„K"L0a„0a&L0a" 9Wfu8„ [U%I$I$IRa sVUI$I$I˲RLjvo&q9$I$I$IFz>9gf޾|a)q9$I$I$Ivщ9笪$I$I$) ߬n|f2q9$I$I$IiZfunvzз8UU$I$I$q(|ˡ9箼_| +0_IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/documentation format de fichier.odt0000644000175000017500000007313712014170666031243 0ustar georgeskgeorgeskPKao5^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKao5Configurations2/statusbar/PKao5'Configurations2/accelerator/current.xmlPKPKao5Configurations2/floater/PKao5Configurations2/popupmenu/PKao5Configurations2/progressbar/PKao5Configurations2/menubar/PKao5Configurations2/toolbar/PKao5Configurations2/images/Bitmaps/PKao5 layout-cachecd`d(g``d``Q1l`(p ``!pf!H1/i -e3BU}0M@NPK\CUPKao5ObjectReplacements/Object 1[}pTWI`(M8 nb:4 &D@}y>;V?g"3VLmjuTQ+XpJ;c]~$w_@5 lλιl\`L|D1cj'nfg#Rp5.}~[GcAkի'X"Xj'xx"w׿U.rF2/R-gU0H<_dѢ '%]'5,T㟩kK͟dJdcRojӎv\:6u/H~/n8axlI[WiEclC{k*1󂝙`y /(39?}X)F>ڰޥ؊=hawy'lSݠ[Ѯ~6oLg 퍊ˀc\ޛ#M2 N Uvr'htW ReB>΃Zns[mLj nCU٬c 9.Hy74Avݚl`V,fAW&A fʒx?OHVmXg|8ll(mC0V,Xfe)Kd1h[=g6u9dE"HV --B7 #!tZF6򦑵#')>i;(wO YO6K\z4: Djms (oЁ&Ʋ (ƾ [!w-cN73j"@ _70d;p/;t6 ;nNE7B/ <,Q;\[ oJ#hx66aWP 0a'@`"L& &x ] 9&Bm)Zq`G(\1p׃|a"Qqс[Vz?DUج2Pجa1A!P*P<(xֱAߴWY&b1,˘捹&,>Stng=Z'TVs_!tĝBIa5_tCܨ;۾K7jPJ!p1,ߐNg@}tc) ;@IcEݏ,)ۗAĠ~ Ӡc춴3uIu)E#>aЕ KP9 yVrU^U&K1к<[=I3F:hH8'2Ր`a+x )o,z0`֤Q*OTJ YEhل\Ua>)UBcT0x\0\ImNM LNxKחܭs"U- '&:F3*9ψu("h[BT:&mATTm qľW7VtrdḢe^i%(T̰;":VκJNBI 9~iԦGttstDhG dȑN$yp@U(Y(!]-El/ðy=t!6 jnM}uU]z{Ee+ϡko,. 亢 LeF4\xT&˞Ȝ"@0:=rhJkzZdj]0eE%g]Qg|Jges9DW=nN 5*.uD=Yj++nyu '9KZQcy mw.$#UlkܼxgLz y>ƣ<0x.B?S'ǤRLgS8;-*[V6y`||"\5$&􈮧 dऌ{"^ o1ƖhBOR tG6ھ熆o|/*R{ w[PKܶ2G 8PKao5 content.xml]rƱf\RBx-vV뭬s\.RXVUy;*OM${fF) -ts_]M:a[ UVt{bXߴNK{:5tz4uN-ۖ%xrXG6q "syVQkq3pm^{y_Ʋw8ˬpC}קvޗ]5xFkӰ~:n\z~#άF6{cRMMmUVA9H^l$˟⑔TݫYnF$Nn`L3;' gJy޶l *1䥣۶/בo7u"ub!< 4(D^@k^qX؝Og/^tNV[zBdIѓhrvxc Ƴ5%:mMn'_roKxx|l =(57̛U%.ni{s#Ҟ5dԑ^ҷu|NE #LlbFFZ3jQ}X" ӡMk:F _7&DzM,W2ѵtg͠_~E6^Kg_k KޘZRE*jr(SRŽ4iOXV o}k:O|8 {9oWjؖ qA2s2x70b.XLAKv @|jELcv ]Ϙ޴\KڷrJL7-{9^cXDl189k^%>>YYy(NNyl^hʅq2EFЈw#|>nТc.CMJMl{#~9D&zs7m߸ '[oW: B֯QI @{lOnյÝy p^+66*wrgs˥ oS;qFB~{+_{R@}CO1.gEzzZʉҧS[l[0}*j) *6m*E) ơd~rJ"%pIKdrM+GffS_B\SS~xcS~&-rԸfَ_C#G c e1P*5R>=u*N_*N_ *N߰*Nj9 C3Qdu*L qhXM;fN/eP)ka,lK 'N$In/xKfHbdx&/yFmQb}23qC_Z4t$'}VspeN4Eo'avnPǎ1Q~Ds{bLY% TX;h H߾^ơTtrgԱJÏ%2a"zfR+b[R{r)yk=ɲ"'y(B9U-{`=_+a/Gnpy_O跪oք^<Ãw=:4%эRg#Ȫtsh:Im*7.Q вv#y?~tMndUgܵ'3dD>^hfЌ/4 XYScbtFrQvņ4zIzaDݓ+b U$Hl! qҡoT9Zßd(L"(pIkCUDNrqza䯳<caP7TxjEPSc a1m)qj* j uwcAWxrL6[;+y /vGP]p)1P:7JiYUyCUy|(}}PH%UI?Г9EUesWjnQ_)1 QJ=9W1-A7<6ϷԞS02rYޙFoCrph p}0E({3:5,vY;STq#.黺ܸ+ XasӻլAt\s9ȩ3]]bA̞ ˜Mg/]=ā)3jXYѸa^H,/)MM Ib@ ͉Oݢ퟼ `&霥zοC]O}.oWki"Ȃ@ wy D ER~>'pY&_PgjT^ܠmy !/bϠCdC6V H1SW̅R"dڅxYp0BEЖ 2O-.qÄzJ@%s:@ w.en5o[{rr du;`OYJ`G@VE]Πh3D)X~u q xP¡:ּECV6!&Cu!.)Y7@)[E"_{SUPL$C!s(apQ*_q\ʻ{<,ufQ OOੁi?P` OMF~i n$<+Ã:m^_'L{FJFMXkKfPRd_PCl,u212e1y+ä/ZiICW\8x6XE6AcFSE "ala}Lc/X/+~bXY#p:,oDŽУ ToQ XɽqL#ngWJ60_]@Xx gڋ/O4)â_Ԕ`|fA))6d»‡A9.efkcl0Cwobo;Rߗܞ-?X½)  Ƀw@2sdGz;KW7ԋ{B@ ځ:ְ`,& Mc\ dPZNDskO+o5,o3vo(UtN虠\9CWP40ʳ汿si:x<{edTb*"ujf 9d7-61p.%m@@SL5 xLTdIv^ #keE`YY'nؗ4J :o&D.ZмE$`QM+&3&u1:](&eQ@Ƽ%^@ H:8aeaL]fy+R=.M! YpP&"nW0 ru/[ NⰋ?w'͢p(c*>V82~?#: !6qqgnCpWb c:)7|ſ/4߱yx!_kcum_z0*8e6$k55Vd)i,]tutm tbؒDtA V2)gNw:OO8aJGϯ|æʊLq5?|et}zl3y[y b]*( I\ƯK'JBNUHv`ey%0 S?vEV<x4sF(1YRٿJi) 0FQ :9L7J78a@g&"Pb!ʐ_0kL pȽ7j(:Og_77H`ncuYh1K‘`0{,%45[vPio|p.T0ń8&V0ǖ4l@b [4 |sr?+y<5fKypVX:`C]Lr%lq"2%2vlFo NDJȎsLxN eWL$t *3Ozd(<6I:_ckvp}4~x\aHQ^;,&WK L,-+4^?,8ޢA"b~Jg_Ƙ,#aIY{[paAa Sg"+7·2[JqNqU^aIKTaMB>5!M場l e] ) w%HBqnRm=d=f.ۢ w(> iu˓[mâiZsF.R ,V' #r^MDwa,:P.Ϡ(|38^ f&nvaj~6+=|Wꖋ՜~, sRWa)Ews&jCV` =|.䘧PLRvqEи}jQ,ĕɩ0Y]|PӬ&3vIl ){)?N*C; qhON. 2\5cuglŇX;l|mu8 ϏjPE/ +@ln6mFbgya'49%R䍍cؠ{6ob ]3wϺ#a¹X3&!B@ԴͨhoBѷ[}GiG<"KMo v`+ĀncHR`=i%7Z4JS"l7`Wڈ N4\PiUa; !o=V3Zv ~_7~Cã_N?gY\6$jvtߧR~goZآ:G%G$k;I?7$ G:jcñHAc: nă&A cԲeIWcWrXW֜U^sŴ4e~%Ů]P7&b+\F"qXl/Ns0&Nf bA.61UIPm0 OћgTƭm slԙ}ļa3U y7 Hw. _^y;c̠ rlJSRKY{m>S_EtI$n. oNx+X`GmbנTpͅCԩV?myۛၚ;z'Fu'{ Lw)L!Qݩ)G+_fXa\dY,67nԻR GaS悔+>Q[8ˊtAg=l[7 TEvGͿ;z^l:A)~EJ/#0M'V/<3ɘJu^P"Mw s#-sګqnpj\o~wg'ʃ~LH+r3U7j*!^Rz<Ԇ`8{@z?Zz^7)=M:iþ^G:}]^%I E-gWf6Sцa_%ħד{aoQ6-#-*!a:v::=n6 =kUBxjpzFɁjW -r73ۢ?ugYy6h ,VN&^构d #6w!(ޝhs8 zΙ<ӚgZ$+Mm?(?A-*CQ%qh-Vne0v@uxS9P/UGGġν⨝{q1Il9ڝa:}g\Jr5o~~U5i\KSycyO!b;RZA}U~7;ӕ{afaFr.,Nw>N]PJ]ww_YYC3V-O;DkP6 @mjP6 @mjP6w1;YNdM=c|@X޶?ɧ?bX`VWY .XY)v=\^(.Wv5o@ؙzӑw SqGinNZywv*oK;Vyw쇴͕q3kO8^!79:gGI9~&6(YJq{ =$ukBEu0ŪҪ\(lsxDED_k3Ԏ >XČ MN[Tn$BU[I*%>IBCdmZC,-cʯǤLZOTOVIC_YA3cxj8Ù:p^xNMTZLNTO60zZ;JY-1P>]5L}XCKZOEo+(?+/U!+xq80K]i{o,Zj45̈́;Tާ)xC@@WxC Fh">);J4xR}kWݧk4ɧ갊>9~Mgxœt@<%6 Dp?\vg\͋gӕ>N\uT+}+N}y#J)~<*/PC_ǘJQe?>YP >Oe춴Ɖ*x=&"J̫eJ~ 09[둨;lá>Z[*L<PxD=0%'3gy+ͩ+A d%lJZ8TmG24) 4@Xx~s?d&4|/-olӧ]sxn @E3bRb O"'-t:^_aP€IxHyHc_ޡ0] @tsmx(Jmy].ĻH*Zb&Tq J.\>, J~oht R/;HW2Yܶqa4=_]Mt֮wPKS\%$HPKao5 styles.xml\6~O!8}-ϮMq\&ׂh$ ^}%Kl^\ 9g73H)GrB?NCtu?w>_咄.&i.ƹNv6,(']pwb*".~*洕hfAliA%;)%uCd 5)b~"p;RAD84tنł* G8||MpimMjP?z{E5޾!D; *-6>BLJXw.N[QUH{O)5BܱMG 3<$LMZ'…<O\KU0HFǃjY5Nr+d,#~O6iXl$-tCv:,nDz)g~[f'e ~wqQSy8TC{>W= Pw/DRQie|]#9bh."w ʸs ,{.R73+\6kb{BSbѠ5h(cD>/mA bsK n),-UWJhP-hQ] Q ];o"+BG!I'7KT2e3#uSTp:V=&+!!Y\Xb[ $PørV Ԭ Jd)%DyNNc9o ,{ r# RfS3|(Oj'ño_dr(p <<9 y@ĵ:Y{:@hNh-$[pbLP x^n| yq V`!,{%5_Z[g  TD}YAHsRpVbeGc_.↥TK@g(_#C#Ӆz[3ށ݆}D yz> e)R&Vp85끄sk; Eo7_oV!3F"ϑ~I'ZW- 5s)kmhPS_issVi2s;'Uw-p|qpPM1=qWՀ˕5[Nq5O:y.k^ƢuYe;o,z~,[TXN;,:E}-z &:,:EkMEo.h1`\Eo;,z{QdyE0:EgWeѠâE-WE=5Y:LZ\S@m:ίݦ}va%  ~U(oOŽ>n^}VuuM{}e)>C)8S%^RWy$ҩ8E<$ k9M$alKE8q9=}(0K&i*&9@B_} ï&ljB#MГY ^ A҅7|H\#]`X4}РeHPğI[KP%#+mIelNuV?Wj53*nTGr̦9o12j{*PK^( RPKao5Object 1/content.xml[n6\؀8Z(`ŒJSg(%$e'9b;R-+Ŏn+"wwCFFu.1#2Nߦ`&:ll˶fSϴ޾L7zz/::ɲTbbJX0euQf5 wZ&P4''{$e鞬d}! ݘoaDW.p9i3srٓI3} vnkyKWk$ve:&<ٓT$s E&U6Ϡ(nnm\YJ$܏M>+!2o) ^ĦγX~vHWZ5Nl!\#1ii(ع,.,Du^n=PhmHDY̳;tZME`w;*@e4ңc(nׁ zK<}|5Qd:xx>2O@+Mޮ_^Op>.8w@[*aлDn}Nz9X< JfC.,鷿ٮ ;Hf>-Bo8ñd $eְ4ҰY̊:Ր׭ 句^ybFy&Sk_ <W`b1 W|;Ʀ3/;?VdTۥ7molH.4&Amz׆lLxQA[ؗ&ξjːrNQZ;I3Lٝ"QU'Q׬6kޝ-~` N࿖B"w/ i"r@6B:D̎Ժcވb $V"Gy6R@zdU Ku!c lBК 8ٝ`8#{ oaqgҘJ["l™ڔٿZ{GKBxI;Ԥ]$g{&¢dZv8ZξwsfwlA6{uӭ9v & Q5rVoğݣy -Vp%1ļHl[SDz6Cqrg>XiRvTADص[ I\ővFn>ng^PKM0@:PKao5Object 1/styles.xmlYn6hɒ%X؏v(R@Rv{=՞dGJ%KrfCER?~wۻǂy;"$ƏMp%,S낔*frע\s$\ rWF.zmjFܵVQ5؞-zwSs54g|dA̋ )z*Up/ .0^VuU`0aC-BsilRYD̖)43bOHH 2˴k[ M&͏wo kilO*,h5{ k9wTAs@ e6,|/"g1ŘhC@d+wm%8O$H f2mr {ͳh$(b)dexT9)%RUC` ~[c"7TJcI%#D^#f<ŃTxh 4BM(- E_@@vhaq^) ŋ.ֺ z95Ce^2a0K%`3oS6*`? ^ֱsPFOc|W7~uLG6E 6pO-Z*]Js \Yhħk!9-G#gP:tDMH <=; ^W2C^I=!CL:'N̟JpCȲ> 7S2Tb5yE^o\⡪bA/1X56R z8d ;X9D$^V޶1?^q$rb$A\)G )\MF8(<<=EEts Ed>Q 5C.ej ?NZ 2eA*W{:(t.=2ӹ{\>5Uo%'www`;RhpzF[n0x4nz+ftnIl (XW=R2>=8+r .agjlp*օ4ϯ:V t:4RdŃwD]qW~_qSnYh`g eґ˞>R^d)A7PKavpPKao5Object 1/settings.xmlXYs8~_Aup@%L@blMd[1eϯ_C&C !GZׇZn>/gQicNUjgU|B[ű;ks67}%3LEc!^)oݷJ&C&E3̛k9i͗+feV 7+4M3ZѨz7C=F$x/bKVgFBzzQ)Ji-wimQufPDYnҚvHՔ]3!#͕M沓P7 ?MP`XUZ"Na㏘8t7&sTE,ނX y~n}-= *7ǗXe 3CrC\Z%1K{2_~ 1r3wg"nt&Esxۇ(2( '0\wRyBT?' $:<, 21+>AD0 E^Pq|{ylJ=FDe4Kޝ]Z~ l`.B%řJuFbKթN,32zr'kY$nBֱ#pDSfbhl*@hɿϰ6:h..Q!m GBsrh7H]>]mL i_MioJ忽6&<8z]T5 yf_j"/0 0\7Jzpʐ@*@]@TkMTyɔmoJ4f_UWnyUj*ʡ2󶓯pJ_.UZaŽ\n j.!tH7ʻ!4ʮ5a =]y9m$.-D1d< OpenOffice.org/2.0$Win32 OpenOffice.org_project/680m7$Build-9044Nicolas Pourcelot2006-02-14T23:13:372006-11-15T20:35:03fr-FR221PT15H19M20SPKao5Thumbnails/thumbnail.png]Ww4\]D !2:Hщ(FID 1LDg D]^37}Y랻κku>{RRZ uķ'ɧ O٣L9 eE\ǏxKVla& 2| >Em rBh[Rq{6 3/T TU "U~#&Ov=N'9o-mӫeŵݰQ 1Wc7q).դq2^0k u&Ec?N*|>W8``$6ܦOsȹIAO2AqE,q5a8}veas9 .>s8C^m2W>jBT$z#u=8#r_Uqo61Gi cWy37!/о928OϳRB쥢_?ݰRqp߄Y~f$P0G6z4Ű]`_44`Е"=M\\g L9CO\죢4}4'C(;kxP}JyUP+ b @"ɚךA)Qp`r DHnju|ڋl˄Nέڃsf/͊WӔ0riԜRhf*Ns_G1$vK'`c!/RAu17꬧ v ~nd +7yl1\[xHII:R2aSa?yk0T~Wi~%ZT902 \eoI˰e8-?:0 o9~ʐM11R×Q#B{<F+{ Q`kH"& m6Uŭ9\r#Za+[W6=%]=>*ioyORuU;4HkkoӧFhWՖ!l|m?cԇ<'\(_<_5_q 0^%w4'o22ݳn{׆kLS8OyrYYŦ/fZVhqn4LgˠH") Cx%&!%\%gO. Wqq :kFҢ%P0ojWi)DYPHQq@YG_+Ax?syZbEI*U{H87Ugi/s1ZcDEJ!s>46Xp _j-PM]MOU ; \w瀽N,nmg+i2\Dxo̎5FL%Iv'zm,u`dpS % ʺ#Xt_#O2.-bZ_f$g59qW>:c(면Ud6;Jd#qLf\e첥ӖO͠ոjkbX;=-UTX톅ROo{=%M~Txjrdera{3QP =ʃGJj9:*[ 7*8[v :&21&# о]i5p9v}\kdr!@>홺)nqr {uY&ndOHOX !hjTy诩O/&T=Frz+en#~=4^aA;5vhɘH*HI$~F ~Y6U^t&l{kD+7zHY*_+Ł+EVm=%yAN5oNܰI :JQ%n/Xbzd1f!RaˍӬXH"m6"gƕ-}E’6ZA&;vpk o-7<vA +G5x %1;Nnyh?0B6+tߋovys}RZ[Q3V5޿1+x-|=cA,7O_@SFÇa4(~{-|yEf|(l 4C3=m vZr/-0MnXt!? S+_ݝ⋅d=cTu-ro}u+uiFJMLP-4GZu脮vf{OBBA}n YV J@hS)'$` E+-?ed4ޑJ@&e*L)B ܁.5ܞkofcHiN×zg86CVBvhdiEspE2-wu.*F$ho`[uY+ Ԯ?hv(NcMԤ cGqQv0|,2A}!ؽm%Y<q4Irj W{;;_F  Kr?5l-񁄤/\Α zszL%/aw@t"r6l"ؤu|Pۯ0yU%ИΊ *#M;tM쟽 WF\>TzʠnVmPJH񔧮hfom?n)wkP8ӸY v̈́Ms<:o׻'FvlX.J?i &٤<)R N-JFrǦs|csWM3h{$i]fPINs!ven F?LA C}CCf mWYt/o#Y 1+l["$KjmnPV \܉M+Ջ1 NmMgBΏ|Q"'+HUvHvu @=őPq~tq4o@%J̸ZaUkY${ L40QW%c(g $6uбn-j/{+F@5S+0, z2ĕ"]Bg0#f}򩼺m6s84Tt+ VPKV_srPKao5 settings.xmlY[s:~?"wBȭ 1i7a/Y+2 j'[ڛvoWH+(Qӳd1ӻiԮ|4ɄGP1ZOh])YGK)u,\3cz:OMս]/PNPUMݐ㔝]VBXa~v p5. W}և `,Szå 7絳궔%wabv.)fr>4v}u )Ǡ5cr zCQAè Ȧ¹>&+ K+\ưx;VP"މ7LFQ Or_]^ܔȽ=rST^'NvJE)M4GwI̵AB,2vCۊ׫Z߽xp&d?0LT,XptĘXڅM)2cK?Dd 7Δ֥)م\,Ij}懳҆܀%43\ĿT TѱnH㽁+F~mEI*oJ}`MB9ytɄGc=+4۔P4\_6djT4{m 03g'm! ;v&9G eL6* F-&L8󕒡h]JԬEej &]_tܒ.y%T,4{fŷm~=}ЁUM =|vG?y3dd2_e >Ci5x+x#6ϊX:VMj w&{|Db+6=iyՃU8xhq{:<NF-J !<5yV,I +/]67j N )(A{si\lZh[mrTP5㑏C;4ŵ@x Mcidc$-s#{ ,mgnf?̘h*`/>˔Nͮ~{~ [bs`=XShe0{vGsZb!Cܬn}x$PKw\PKao5META-INF/manifest.xmln y ˇl*g*UU*=Wx@!Y}![es4ݨimYSfhWr}} mi,԰lCYF4$8 JcTa)xrؖV8)nLW߱ꇈqqv-E)Nͫ|WCwI;A h>r/9~!^BxCO#tE{cեk | ƀ$x߬_?*J<PK |lS PKao5^2 ''mimetypePKao5MConfigurations2/statusbar/PKao5'Configurations2/accelerator/current.xmlPKao5Configurations2/floater/PKao5Configurations2/popupmenu/PKao5JConfigurations2/progressbar/PKao5Configurations2/menubar/PKao5Configurations2/toolbar/PKao5Configurations2/images/Bitmaps/PKao5\CU -layout-cachePKao5ܶ2G 8ObjectReplacements/Object 1PKao5S\%$H content.xmlPKao5^( R 2styles.xmlPKao5M0@:>Object 1/content.xmlPKao5avpFObject 1/styles.xmlPKao5QIZKObject 1/settings.xmlPKao5-ehhYPmeta.xmlPKao5V_srTThumbnails/thumbnail.pngPKao5w\ (jsettings.xmlPKao5 |lS oMETA-INF/manifest.xmlPK7qwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/README.txt0000644000175000017500000000056212014170666024134 0ustar georgeskgeorgesk IMPORTANT : ----------- La structure interne de WxGéométrie a été largement modifiée entre la version 0.112 et la version 0.120. La documentation pour développeurs n'a pas été mise à jour. En particulier, le document "documentation sur geolib" est largement périmé. Si vous souhaitez davantage de renseignements, contactez-moi à wxgeo@users.sourceforge.net wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/documentation de l'API.odt0000644000175000017500000011615212014170666027210 0ustar georgeskgeorgeskPK{o5^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK{o5Configurations2/statusbar/PK{o5'Configurations2/accelerator/current.xmlPKPK{o5Configurations2/floater/PK{o5Configurations2/popupmenu/PK{o5Configurations2/progressbar/PK{o5Configurations2/menubar/PK{o5Configurations2/toolbar/PK{o5Configurations2/images/Bitmaps/PK{o5Ó{8LL-Pictures/100000000000037B000002BF8B07A35D.pngPNG  IHDR{_CsBITO IDATx1}' q)8;ZWTc4bkH˫RU8*`D!9!9Cg>]C.gȏ}||w7n&nʎnT?w7z>>>T:PRBX1N$NuB'@.'@ #t'ql:N8 gHIN$NTB'1'B'r b%YHsc!3DYuʒ8(K.d? qP4O16#~coqG'_f S k 9oot<' 'nVT4YpAScgZ1]p|04'r#Y$NȐVs5էEmx-Bmhx:vW`P< 0MggmYDܵ?" :0tư4Cʐĩ #'CBsޤ h6>>>^uB=&##f/x% <5Z;NW|9ϚG msH$qһz'"q:eI%qP @Y'eI%qP @Y'eI%qP @Y'eI%qP @Y'eI%qP @Y'eI뿯|p1ޖJ℠eM"9,8aVHf'B℃ؐJGft㳉3\}XVl]}6t:>b[ pW8kȸ9262Sd3Y6+dSFdl33|9~$R Zjٴ9ЋmuǺ-mن^ن&q†S>Rm@"{TzsMZ.qһH?Yv?9lAH*.+dhHRfr8x CLKN(Gk-Z: p4Y?]>^'\J -yo OgݍgyqNc/5N>^'eO[' O+qһP7;T'$N*}>fHplƔ&>G=wi/Y2krJHpq~ < 7'ݫ,}Ւ8jŠ{p} TEȚw6wOv'\Dܼ%}788A*ގ \FℲ u~} %qBA^ rg d'qBJOᝊ>,$NOyoY 8Lℜr}ElȝH&25#w&}$N8Klw$Ip5OBIp '>C'!nvH,ANH9;@9'4L ҤOh I$ fd 8aIy1JY[H 'b 8aKs5>B'̩TqY*!qgMr;k#}$NI :Ip1^/`5>' }B!']7 'd$q/?.~<5×2Ͳs#~P0g;M8iPNkz8tyӧ/4L5"I6dIB'8iMx-nR9hKO8igW\n@$N8ް 8iGPrEYKvN.L{NO>&qq2Uq:߽&q҂/!9 ;o@Cvݿݐ>ʆ?p']M0]U/ߤ;#\Þֳ&7A >ԼE^Ӷ}ݿSְK7@~߾Z/o'iJgnq\bɴR *8'RZ 8:_:w%'оBr'&t6Ca;iEo%w 9t=\2iy>i /N_fL S3)jo=usY)3\Ym˲ʍir[aCjojEs|p%bd&˫=v~MNtv9e9IhlrɑCS9*4rhր入fOJ[&U^Vjg6jKg$Fɼ=Myrg$qfaYKqVˋjвi7gճ${T꽝8ܔ#qftA8;X썳dIv#D\"qR#)k;~v~kDx(Z!qPJ+w -1rr'gA8:'qpe1pǡy'~*vB'$Nn$;m8Ν;) 8j;Nx"Z8N?z%N*ҹih @u6;w <]o8WJ C18Z$hq@]B†GsV.iLW8_jT9~݄Z$PHbG7'b_]Ɵfj>toٶ'v[/~;T6ppBˊa{:QW7N׋D"jfqVa]| ?l/ 0/z>>> @\9+E. @#?qZ>çC<i||[ݼuhCh󍰫B|'̻^Wlg,ΪL|v4K)BFdћ-_.z^Vd#*Tm9UdX37=n}}tRTՉ3T }NQu35uPhVݱnl;Ln;6'bmg UuIW Uz :)Ʊ껊FW.5eu`Y[vv̙Ƨw>ی^/q#f 5b9a/cdY%:ܤhٲvx'\]Vāf6oGʆJiIh*5g:򗿜Cߙ+6r O4sIZK/j<uzTqyYu2"q@^\/q@6ɣg\/-]Sp[l{It>+dgv&kr}y߅<־r]Ym5lz]u嬴M\%~jLUO7$LaʲƛFvsw\?"?P'o %{+CwFneuҷj#Wv-d9~=xޮY].'LjzjlSK1/hjݵ#:`UW9}T&eG^dvlY[#o 7.2ndk8kqByY/ҕ3_Pbjv-p'Ђ_ Җ"[Vd3;ز5hXb,~xP%~jDc2?ygNw:^O8,;mY@jj\jeqZbPtSPo eBrlWN5ڵ\NN',LcL ]|5/F_/hRBo㬱 r,x儫^y.+|$x{}ms繜*ㆊLن ٌ uQ=VFYT)ڜ0ʫl{Unkx_?#5Ȅ{qYBo8MI#qB<,߫2W_pt,lQy'/RH%vVƑC#4@$Nv=ΛϪ3@nq}`~Bw8Дۥ%Zo8%Nr=N4.aj<[oXx"jq@NqH].= SzCIWӏrҏG!Mc'OgK~t/yw a'է_/vcnLN/d\ )BSvP$N-ٮd AItOc1oǧվYl=8}2N n6#г67[w/uT709Z2콡/H3F< E^yMiF|]Rʺ+r/gk-LaukXc/gqRH"aq5ID_:Α03E8>B-~z+Fo5 #!MiaVu^߯'ru$j7ԉ.>t*c *1R5z HTmL{yS>>'&q<&Iɓ8\^`~ƪy:0ЭϪwj3=.n|XY>} Ћoz}|| U8Ef< tv=NI`I2:6v=NO`J:"C` q I@:fz].+ 7c,dȣ#U8Ous @YjTg)p@ z)q2H}>d!)P3g)+ `8ᬡH9$N;mY #K tese>{M8fqƅLY8)b<~{3Rt!J9N>ZC\)erx곫1A3q~razإ~'p`[ԏjd́~%q\c8  2 Su4XoW qg}2R`+uhGWQ]oWGRT:BVmO%q!B'H@YqD< tf''DH?NuB'HBgtK`pKBBfa6߽[h7_}5j@kmmrF3ǖ]LG69Q{kM:9=Ia^}z Qo?Y{L>1P3Y /E1,b%>$6~%/g_f<;{愛s`CAS7tCeފ‡ɧGYh 0RӍ0bo/[~9-g_˙,l|dK%ڔ-[%~,Jl4D&΁ IXUg#B)~TF&u(;-Uތde5P CǢc3G\_:'7lRܡDџjfgZnlFSE3!5l(~ϸxg:l.ڐ2a-~&VE&N~*/v(4g(Ք9tWO̺i|->.Ξ p:FLOWNdS<i^ʲ^[;@6T6Q{ǜ$cUq $rHO?>G̓L/Oe>t,8q2JƍJd9\v6J~/cI?N5_l=|:\J'*pyΪ7Vu&j93Lۇ%5Ng?)^.5NJѯ.<8{1?3ٻhWԳ*iC}|| ]9;!qO?0`3PoIvf[e#6s=N֏(Vo6/Y^3 MscTH*ps ٦= 89U]N[r.'|Z],x=;ξ#IeW\ahnS6X>X7Fߵ 5#evmVCj(~CO<)o]{TWk9zLۇϪ:tmhx'Ȳ.Z7ka/[}mXUoZc}xo˪6/c|eQ*ѕx򕞻yjOus`AAќ񸙟gV[ sg~{ث~g H2Rf62 ff uL_VdMhG͞Zڜr0MhCş "-\O|6rH?NB|Oj,wm8q4A[#.ˊf.qRn}Rck$;܄5 uQM<}k;9[M"R{7c)wfHO=x鼺 s8ЩIY8Nyzbh5{]zI٬Ƶ3Y?x %q)'ٟ>>Nf#7J-t9(:Z"  I$[ټOus qZu~m$ 0'Ys)=?*8.*rZPȫĹrLtgy1>$r㟑eEZ+kKܫldYӏ}~<9 dt;/҇rgOoC_dY,c| ;ŕPYV Stϡ(_,"^|6?8`j,t/YeB}w'ҳ~9H,0?QX:CQq34zT !=}r˓z])gγӆ2vf^-2da3tU@QΑW3<8N(ꣷ @H?A#{:7gz6t?eyRo8%N6'ݒ8O-q:zK\F ˓8Еvn9ՑtFV @!)-vt@v} gwR/ǏG?U@$ 3Aj06YNVvzzvz` IDATUjR-˙>}$?DXPJ'4)22;U>/>|ꛯZ굸柑%8{'tB3j2L%z4sp/;B'<]AsRy-Bh #ϽM%tUU%RȌiS>9 $}L6sme3GCOET:ӟ`֯%ꂖѮECJhrF =5fpưm}_[?N8@=j8oNzKqԥ Z 4ȡt`65<p_n ?E}q$-~J%v˜t 9zbtXz~ٳȥP#r޼[WUO ]_-T d?v=4г)3\굶Fvxxc}syki$,#]jvH2k9вV:6iB-i>: 8zwWB0rhW?θ|)@ҧX 2z44YBzTKoOgfZ̻Q2a o\0׏eo=D\w=Y'^<\Nxl&%NR#^. 8& x7H)#ÃV] XWWd氡ٳ6O{7Qr\i9'96e'p[AME8#'hdz^½ݣ>y[?N5Z~>e9@i] 6&pގ6j@_tO1h Tsj@MH@ՎAՒj)ȝ@Q)hRݭz;v)wMG'νrY=42'gV3VB2 vZ:x!ꀌMKcz;qa+a}{nwS|pWlv"߆4UwM(7?zh;:;h \c.ި-t!tKoW5?*l:!Ց4:E ^"@Q T:f>YM7_}5McqV}_3GN_ݧO㬦>;UJf˚=%+6*Pӧ~*(qiY]99}vu&E $tB ]ή\8#}(qT,%ҿSV PB'U[uYy_C:NBgWjgxI/zMC~ qmiz}~^~RqutVgW0ؼ< %4:-rz A͠]v]#>8zTo9C>btt!q:T⌜lPFΧà guF tT-ǯԜ 7G{*qB; 7M?gAS8 "O>&FPpڶ˄:Ô] ޮYՑWͬm)K|\tuů4;OwY$.:PO!&qVp,S7WG.[!%΋jӴ"b? MJK&#*1ڞ3 U.5+1#ގ5W$ibs"˲w^V Q4| 3X|نkZUo5=WrZOudjtv]C_m=0GnH6y6SoaOfh!pL#ŽP+"p=mU^Ϊ_`)-=MGO,ilEg#g#e:eG7˒gdlS`7'Æf04ղ=[=>Bgɧ,KҊ4(C-\x3vY^ mcW|O[jrҘǪѲhzK35`zpf* @%'YNkau7Oh52Bf/ƛ3Ҍٿϴe+ ߯Mi 80q~f4F iKhN3˞f,ZԼ ~4}ZuGo֣W<ڴۏ %Gp/̥3 U}NmZ8>O׶jgV=qml ÉMSsL+X'kѦTޔznBG~߼NI9sI%L'Wr)+pN)v֩w$wO11>m!νZNd$tR'W5ʥ5+n䶭ñ TB93>ۺs.H9Χ/9^>%ή}ɵfضmS$ # O=^ `S UK=j 4`pY q*]1/~(qـͻˣDHuD(vRǪWn tEj9'ҳގgqB'tµ3bby,ޔ\(޶ys7?B()%; 'J["%e I tEdUW1I̜:zyxpkQe¼uД5Yf4>z#)vJ(vF5'YP=ĵгً9Ii3h&8k`;qF\,v s98>tCv໊ZKۉ3w[xe3\)grJo0|ϡ,u3TzMtWxaq-d fcnCpVOӯFLg8f8mޖ£oʙrcLM'49c]%:+fo{NE&e2+7Pw r;{ToLcEz; q~F8O9ٻ.ة\'ɕUIm)Nj(otɪȫ]b߿uGm&8ZMsSܼG<:gb#͛35G SpxeFjc$Nz^d]"N]s xpߛ` }LVRoXuhJgrVo_^ .q0H6oK8}c ]v=<ŃLGS q6CSdyG$Nj%2K[`d* "rz >d1 ~?ERB4[2߰#Џ mNHpzt(qFuU4S/E3Y崡QXvً6lF̻_Q)?:'qfs|p`WX>wd%i=dг;9;e?3egVsǬ9^>}/Eg3>:Ôvj*;x3$NM(-˟ßlIsX+ 5c\ ,_fӯߎ˒S)szJs*M?{C֜GW<,fwlUꝫ$NM$#VBX P%p0HɓUEl žY4KмgSSℛE.ҌHQyp98={ 2ӗ"bF R sQIPP9N9S?-ks {2~> w^2/<1eyJ:exj{v{6ᱧ0l(Ű;䮂ڐP##{F$ENGF4ZѮ&{~x E$Nz#qAL8#q.^DQ'e9xq8(Kx<=ް!{E9>nG%q<^K6e/{HZ"qJ8>\^d#!Rx<%γQ'eIJg/(x<=8^DQj%q<xg/(x<=8^DQj xyy"]BQJ}_2w2J+'URd̈YKgB>Wb/gI-_jÿomW͹mxpuD;-oITad&*@Q'UWRS)Oϫα.ONX'Ym (q~z ǵ_!1}ss?;{ M8yPïH*2:*mNWjƦP33\m+m>ٌ/۰\iͱA)zmx|ttXcl7DA"ā2!q6Ld1vg7M, u&n虱4Ӹ5F@(5Nʒ88)K;Uүfdzq/8^s:{gv{+8xtq3,.e&, srI'P!VC|cuƐ<)w8 PqsWgz4Z}FVyU;~[/A)oYT~jEgf;-B=f/.]?\ܙgڳՁ,w")t> ^Jb˺y._ 'Y:[H"#hD!ƛs`ע w2nas'pi4ԨHc}fCIk@r9.'^۴ڌz9+gyB;#oȲVΝOCڻ5 %r6mÍ*3Eѭ4͝ވ Vr9?1 G2RѬ+wFl~ ?N}$Hi&H3Blx3QmxΚCk/g =^G3"w\wN,9rU7 ݤ;ԌȢ7zCa2RZrwޥΊx=3g[fLXiBϾjܯP96SM%dxhÃ"2][0Y3Ҭ^п_}{:.I˄j-PHU;PʽW9;{}v`:PW[A13>2YӏDq:\f6&qPdZʱC =;4xs:UH?=i|g+dC9.'wfI^fGT'Δ?CT?݄zW"Y1#qqBkҚ03 nc85ΧC?SJEQj$cND):?>>DXʑ8yT:y8,ө'EqStj\_\Lq5PNJ)Eq҂iw?孁BT:M[Mu ?)Yul5ol ﷸ '1.'4;h0Blt>G7qRȚ :J@1dօN@(k~|$)!fED!';8RI3RnY9ʇ$EInz5 tYu4zÜ^gp8 wlL}L?z>Ifw6=%07%MjI]5zIdZ\>;}KN4c8f3lFᓵrnz%3A$N*ݺG ʙ$n$RgSM˫auP3/eir]i. W8)uC IDAT.%LȚҡt_|e͖p`#gO\  'e\cCINR&KֶQtb G7Ã^UP<3x[* :WɪWm vՑVsr4_{Fm.(Y3B"^_'HVpuSM"ΞEgKٗ8kRyYvuXn+iuJ -ԏRV1=V튳7g\}|8654ȿ+'EX5 +z'ݐ3߉>URlpƱ#-'Wx2'15'D'E>2)\/ h]Z'5p$tV^V^r,y%w7 j:&mh%T:.wFϳzlIʋw."tC'sr];@es>/X{AfÍ.x<{$"etur !h^B'WW0=4y ߲;<@.%'xB'Џ:4cY~$·l%Ȯ\lsQhh[bKR.(H^'0Y1nv̄KBjlߛ/,"E) ڜV󬯿vBI%fdY\L,HYBέF󔯿v(?N?r8ŝefルO"G&YSEgdk=oHduF9P!G WW(EY54٦,yHJ|*x#C}%wfS''qμ_{7qLg[1v5 ByiU*Y ,L3YTL&߯Xڼ9rviC qxPh ̗q9!2?#OEoF|Nە3Լ`em>bˬM۟cMLg[1g!n3qҘҝ$ "E949tJ0\P)&@$N22-IL1f:Р-ChVbI@fzz2mE ;C?tagЏ*%nR3'}fn߿MwBFj^n< $8{1~g$]E)n:c$~MK&p@o8۷ML:Eo8Fl Oԏ80=D@vN8auVH1,(Щ'5ћN^HQlji%ne:}nYv-q'(jW|5.rXnty=|ޙ>>6'Ӈ?/lH~yc!3$GgYYǢgMs)y|ތM%Pׄ3_ǫ}[,P68{$hW eVCߔlj0" -4Ҙ0rAC8.JѡJ||٧r{W|l~qt2\FwWG8}i{I lb|9,₥7qBOSV{R9BK4V=j;6qvaW_\Yߵ62`muƏ,+W3}r;<yg&잝^5#"}kdror|C8?~rCr,-]Qhߡק<Z4*F2 IV8I(ͦWW' 5cYT&埑f,^80";16z'LP%,>IޙڳLZ]V{W'E%C36c;;RNzmbISz ],{kˍ*y7Ct 52_fK,̳^_I51"‹[[7Y@a:S_l*'=vu4*Eg#gա-:kh\Qa|/ S6TǞlƪHU-hyms֖8i2q:#%;O gY3 Yu:<{Q'-:+7T@ Tt{nl;d:3zfΧ~>=ruBcMfsF#QΕΚ4$q)TtTˌ8jFd!X\58Ϩl=-8FMUBFm\% +jqV.87rV$Nʒ8(85NT]*IENDB`PK{o5 layout-cachecd`d(c``d``Q2ܤl "Z ƊP5F ż @3 d(i1PK@bPK{o5 content.xml][oƕ~_m#Q-i4-%ޝOXFqEώWPN6%ҧR}Ep۔5$s.Ŭ1G\NjH^0٘7p~yw˻u,Y/%勌i_X6G*WYiv0 ӡb%4޶m[JK;|7x8"nC>D6M Tmiȫm_x^) Ņ"b eJ4g\G'*O;7.qM䛛y`tSj*ˬG\#v{~'"aE͢D .W,>ʨ!3/)+X7j?iz\"3 rcqͽE}cD{k]QbK&ZExy)"6-zgz kw(\R;h)M\Ŝ+,u{KJDѢY?gI 3ۧvR)z8cM$]Qz!zR&AX!A>+F&Q,7` 4{JАcGir=Fl" `{4>NK A-!%VPVk9bվOK4':sDg ΡpVz`: #+~8NM zbrN Yp/cEw[XF=o 鶿*FMnOV ``¨fwXDB/2 SWq4zaTkTR:̹^g]_`yrzzÝ6̲åPf+xz,r-3N`)m#R00揽Kf&i> |s[ =S= X{dJ>\X v \h #Q!VGb4ߋ|xe;Z'm:~&uU=j{~2PMӞ͍De)z ^.wn%ղd;.?-VC~ VuZRk Q=MQimI'v7pgM%}fm޿9-yˌw<,y=JT /#QP!gPT(GJLx\"&Qt"WD36)~(Qbf˘=zJ]2L&y}9wP{3؟Ž&>1(trImkr$w 2o|Zv%ZP qN{E c6\L-1ƲLgT8_QJ2]u.|@p+ܯHBJԏAp"qOr7qf,#??g/Y[$_y`>M jtu_iRrYK1egME5̏$7He4^702EF]^ Pj*i@.JUz6a@w =ROʼnPB.&C{Y_r_syF'ԓ'`x:Pp 7X)?yPS`xJcUi 7tiMaQ$K'4k3Y&4#MQ)n⍜rjn7x$&V4v Sƒ凊G<|v.lxY-7Ddthe-;5^sFJuڬ,ֿÊ'6RpȬ׺2/`ŧ< pCE0.Pn12''c@FykVcں('E$;w||{+ɹ fC@,#n/޴ =)i=]TJZ>_{8OOF{G{CAJ ScQ ;i2ܕM҅peVQwdt%qG* Y^[kXP7Sgc9hU5Z7JG.2nZ:^ݮx*?]geF`?7A* Dr Ziehj灗]UW;?ݫOůwtWEld{hTlzISIKM_/ibR6izISIs%Me)w>xSLY)zz?C 4;);CsE.Yta!+s 酈~]H>>}4-f )2U]op;s Ify& >m80d)_fj֣A>Xuq&w,0±w ] "Gh)  >Ԍw0t^|@>q RŹdBhR,I\$.T;tu}Eܹ"EA9β 4.tp?S\<=ZQIܓX"埽Q)Y0A~>kmFkmRqM[:,Tg+V硨%ucI%8 SqϖޠM/kAʻ a"G)1xiXB.G>.N})Sy,vwnr] k[y!8(- <3t!-|vՁF;6Aljfs;gO8Pogv I,}{93?ы݅"$d-ֹN$Pq|dxy}_f8 *D* F<$qLt>w,mǡtnQOٵ&S[EDqH[ P8 `rQw?Ce>En0n݋0rP*"̘px`*iɫt[` 7¼L;(D=;/UoTƉjyldx Xl&&, (y0 xR28V#>=\}'.@榧ZץhX+*J9ڟϾl`grqLpopxHOw}GGCׂmw|N}w1a]ڎ.«0(#MJobgDibxA\SA&N ~-9GZ fFyFN@haHp!I)3I(yFd4sl6 5?פ7o,10@Z=l#hJ",|Pq:]z'_VHtЍz'9P5 c2<*mb0*#0ܻ"(O%s9h~;!EiyΣ{R!Y37ApcSEA=9]@./(h\+.db]ZU)ϝ;Eq]n>\fFD$l.]z'RijBc<+z*p >gp,sɫs \M˭6zdx[hH@yzknӒ`3SZ838iOb+O *s(Ά,5)dCi)65Ffi@0k:A CrTFL,-G}]%k4=[랶r3@+aw$"j4 e:R:?bSjq9 wwj62R|s&p}8!}-;1bq>킷VAb4yT-6ШjOE1H*pƹqZN:x& Y)DqK*vT%X•Y4 4CjPQ Eyt{C D`9IK^ 0e^XX~+寿ߖ3 ;ɿfoOg# PSVG9n.,%/\_gXdVMs#*,yf?4Wayզ̏KS{iviò%YMN c @t.I y"VH˗>m'Lax}Q}~_x;|lD rwFN .Hz绗b, E] 妼sfv< 0YXAbHb+rz^dADndQ)K?%`F‚ZWi> hu9N@w,)hձ WI-*GEAʍơɘŸMtqa܈AxP{/Y) 1STukHߐe =:fX0?w$j:bcDQ<>\hpO6u HNꘚ> xLh' ާ+cGLٙ 6[I&:ǚ۟[ ~\Y=i:ph:n,0qLsj~*Ls>R7D?׎QUi Wn-cX෺ ;*'ՔhJ)K4PeyZIϤ[#jIE80WT I fܠac,Lmmٝ2.ftti6J:XS4sKa*XCULZlS/tt0ČiruC! ɎƲK2SqOZqnB&YŏLf9|ypa 9"`NY[R˳"c0vYԨ'H u'a4w'wTYI8\Fh ~a{*8Hp2Þ;Z!h{G+#Cl@)1|:>7+Hʹif/xwThr jH 68nj2`X Rk9}?1I[N^/).k8n_L׹&cRWSLɻZ 3zrG>0qhFu-Y)aؒ׼A xm^[.`4|!B8( C?Aur$ W#>f=TC2K ؑjȾirv#I?@uE$Tؚ8jFГx^n<,ժ$O0W7:BE_m>M]b0Cν*:M)t,!WԨ$ѤXM60fb u^1OvPQg+(H .yIG4$ژ!@26O e{(8vA%e<*+%DP^ISxoBG/+fp̀W7X U.*ӶH&EAMvI2ȣt?VyR {(MtQc~@ a(pNz })KOM’'rg!E@5R)֕hmRHtC'Pu4 jqyLOsc{T;,N@d9'c;]% [7;B,u$(9=76'ٹZԝ.eTK@惏X'C+ۅ{3݇Bɉ^[/BY@\' ]?S^1.;翰=C;f3EQ> HNn 5w-kgp]]ҥN۰MM+[;2zLL\uqׂߣMǩA Z%P^qҡɧk5,oo^us/o^V^ǣG{ƣW|{TX=G[O |=㫇7k/:-JI3vk>xT?_uYt"ټOk_jzMsN 0L˺h8`zt˳YYN<ˑ˯g\N]~egBtAQ삛G!<舥Lvz,:Z\#/5ͷz lG~`,SlwG7ͮ1qÀ. Zgy`Ԯ2^ (p4EK<^Az~P>^Tك5P+&:]B#ɦfigjWbR7YǶ)VL9VZkk{ϱ<5n&ybr Z<7ykk9ʠ:JHc~4U4L']w0BZ4Lh4A6Nj~:fx:t$MتK|wj _@AӔU Y;?t=r{w~b"/C]" ~ٷ4+~wߗ ǡK4NhN٧0c˝zS["al<q)BJ#ìVu?Q:7j{GPK38K >PK{o5aKddmeta.xml OpenOffice.org/2.0$Win32 OpenOffice.org_project/680m7$Build-9044Nicolas Pourcelot2006-02-14T23:13:372006-11-15T16:24:50fr-FR74PT4H53M0SPK{o5Thumbnails/thumbnail.pngWW4\&!-(5 2jeB%e0D1e"]e%3_\:g_)o1X222:) =I*JRU`EFF02#҅Qڭf˓_C׏bJZ4W@ތZzƉ[qOmDSE f{x7] RWBm)// <ݿh.r/3*JGS"S7k)*C{ Wy|V)M .8LG3Є$F%*άٗ-SSuoiRĭv70.S#pes(@3N&@+|3 wèCt7ǍvKhJS o t6e8~ܢGX6/Wd 2f>Wt3X쾁8z,x׏}sv1)f5YnHBEYf#?t RuyʽlfW!1=ve`iwW% Du6 @ӻp+?jx矶?W?XgunZ%\KP AI쨜.Aza{eȻ[F_` hԩ3{tvTdgPyD$'m}t6}B3aaѴv̐؝G]]/,\@zb+wPsd$k!(ڸ0&'w4a|W'wy~Kv!ZBGj ˌd4|\43[y6Ubks FP&KCV6hV!gP-nƧَ?1t.j{Θ! QjpYa[udv}|h$=+n*Yi.E,>;3T^8&}Wx{W><. )>T~%&Mߒ?FuPwkDnYVD|"픉x"`lF8'GCxyQlBpp w%T厄`iF H#AhPfy|2ll`rbқI8-k(7\>\‘?{BvJ`mn52ި bb%0SӮ2 \(m!"qd޽eHaF?DHvYMGJA:o7{@ ̈'1Fu7}8_&vxvMbRuw8]tE8O3e̓kU [kk18$B)ݑ#8qCDC+#vef&TPء{[ Ry^:F,>?C2[dqЮwf:9n!2D]CNZymUQT@#8]n.&~oV`3K|DL0c(ϺGwuP\9,;*뷒ʺG Xs995][Ldbf]9A|#|Zՙz8{9q")E( $Eך[5|QR!=l%Mxt=ם?.;wɂw/a>7=}#1N}MUT5|b5qtJ R a_ M r0s8F[LZQ G`zuZ,ϕlzR%9cIB#Yt~^ uSV߰~)M䦸m{L 8_aLcI H&Q'c[e!hLϛ$ּ z?oSun +NAUYJ)p4'jfdƛe'ddc6:Ҕ(F!ҟx<]sHa}nM)ʞ^CNlinRi˅tQN#^91>:?pkU!Z݂e dTGV@7#K˂"su8vR*dAxC /oՋnZtMK kw'w䎊AM'X${L,# gb՚h!UMLi;4_= B/]g4Pr\V1F;v5ݺ*RBfP}R8+A9A#t`K ȫ:SCMZE Kji IzLVTg58 ,jJ8QAf*9_1E9$%$wPZsZDšK[xٻqʝz|W"ʯߨvOoEZ-d8yuM`5W@!|qvߑqB ރ y]b! P1iL:h.%(m Pr%U6JiTwu颟E,5zVzfķ:yx @5XrglxYh2e%ږ"kf5#-&i&逵+5_PKPK{o5 settings.xmlYms8 ~wJ_-S tcvDd:-=6O~,YzɹLD(yPa<;w81FYT5CKtK]ϧAd.Yn: 7Wם|d)|isczX,Wgfۚ,PNPUMݐ)ɽ>N_g󽇾\Nh!_О39`(ɠiTtteKBH }j*1,!r{(-0w㭣j(r7Yx7$͟Wej>p4qRON(;lIQ敼1I9B;,28/)G 2w H=shzN@@!:& x3 Q" 'e[؆r(Q~?3{AA1K&~azT<13@G8kՀD!3K(MWtQ$UmurlpqGxGA ۃgƀlLNr]:%,gWOa 60 7Z\2 j0af0"OFă:qK0;,2WHRq$C`1JYk6M.%=KIX=iP̰Ӌ*g+_-P [{~5grd0|&)xGj+5Gi*PF?+Z:bχۙRtM6-0f*>/M&Spآ?WUw2cM#Mp2:lybJcPcXgҾ$R8JzcxCKOx<ŽoEC32T9io&m" c!PmM Qڡv^*o b_򿑿-zU! 7k;jE_"PKPK{o5META-INF/manifest.xml_O  -hu7vH(8&.jK!z\/,VyitŌeITu߳jт5z*,8Xp4/5KwF5חiyTDžN2IȩX1VIozW \Q;RW3>Jvht- 5\< 8.s',&w%a"56X!ޙơOw}dJ-4ZR 'ud>),Ĺw1!s)(ĊssShGR.t&P.@qڼkogN"dMzVTa8YEM,1 PK}vPK{o5^2 ''mimetypePK{o5MConfigurations2/statusbar/PK{o5'Configurations2/accelerator/current.xmlPK{o5Configurations2/floater/PK{o5Configurations2/popupmenu/PK{o5JConfigurations2/progressbar/PK{o5Configurations2/menubar/PK{o5Configurations2/toolbar/PK{o5Configurations2/images/Bitmaps/PK{o5Ó{8LL--Pictures/100000000000037B000002BF8B07A35D.pngPK{o5@b \Olayout-cachePK{o5\İ" Ocontent.xmlPK{o538K > rstyles.xmlPK{o5aKddB}meta.xmlPK{o5́Thumbnails/thumbnail.pngPK{o5 /settings.xmlPK{o5}vMETA-INF/manifest.xmlPKїwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/description.py0000644000175000017500000000074612014170666025337 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Documentation pour dveloppeurs.", "description": u"Documentation avance pour ajouter de nouvelles fonctionnalits WxGomtrie.", "groupe": u"Divers", "defaut": False, } wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/schema.dia0000644000175000017500000001010412014170666024346 0ustar georgeskgeorgesk ]n6SFq3E;Nh dv%W3}ȋ-I9ؖlY4$ئut~y݈ 8_}f$^fy4/B<.닋>䶈,I_$х\tqfy/0H}4*<-K\\<[jj8Kܻ˳uvuv\{(͗.^|EVrIyZpGkV ([UuKUwDyt|6I BaPrFK%K..Y^Q\neY"ZKѝN1ve4.lOh@i;Trgy<-k+!W-=귖~(uw.{ou~nPPA2b6[_pղ}O}s]S}֚D݊|uog{ƚ&ں Q}>g313%s-qrNnzzYZݎܛF8mT),Q o?›tz["JE e$-cT Y:k:!S B!-n$RQb_M(2媕B mC?? *;EJ"Y WXGl(Xz7("BBLC 9o 0:`Sy@׃Q2^&\7Xz sP:c?* mhHDHT, YCZ `^^FE$g0{|0$}›,}R8deZ!-֡B bX 2aƠVňV%3a^2%b3èjOiy'RJ7QOws= /[-x>}J\/T?T)s 鲢0G_rDDҏRԀR` jg3& 1NBE p%6q_b/5l^a"dxD]HjJbַroI9(ZJJ떙<5ߒԍ:l3Ri W۞AuX}-_Gl( ̔mȡendf)6(4wgW} 5B|8O"U>-u<.Ԥ*IS lhCwFqZWU imICtUt,gxj) bh3矀##E?J]hO6Kaw ^ $\YTOQhM|rN 9s@D(!S8!Ruk4Y)}?n=69% mOK>&rR= 'MՓ(H6:J\*}4dS5}Qw:4Kӌx]a͈q R%`3:#j͌61bGcN*$ct=U/RC2-2J3^ZZ҈*CUQyCZdxjh8tqH%OC{=t鰣(v2nѽ>V!6֒PSB!V(kY*0ejv D]iJq_A`@Xm, $г940 qB"f]P p{"4+@ s=Ӧ ȴa'ա<=! < ݈=Lxs`?C|m PWC0Ѽ%TڛVp=js0D#\"%\"TgRA`;T)1҉v2ʐ68]:dq !EnGWfs>#gϱ`y}D5%DrWm{ [mӇrWn \ƝwVU=i̕=As |,-2Wz+ÁGOepw^v۷ QuMT 7Uw-h?kU#czQ\CfP"j4(̷-ߍ\.ԤIpv" 7@8 ̥19*yG*O-F.k(y$J&D"Nc6bF8Čr*ۥLq }nIIpH{Uv~ؔpM q8$>t%\2mof)Nʝ?Gj#0|Z@Uv3DTz:Qj9sW*)XJQo?›VYۋ\*׃W2fՙki)A.kW3k}9wMu~y8$r*x:r3U[?vk[&Sc2%qvt8rp3=L7n`*CK+7LꝊ GU@̦n\++=`ҪU|=}] ,,nt3P6A*>[E#&4>uD -+A X[VbXʃ?To-PXW{gc4.0ǯl N ƾ僓"q{&%_"nu.-)aaJۮ^nd6r9ɗ&FU'n- `m> lzj<@u!~Pl5KqUaS;1bOTJU!FsCIB)x(!!KaݔYzqm Նs-|q䕥4Q)V̵KvQHڝTOx*[7vipܧGwCiR򑩗qM'/^}NjSmSvA]P֓ *I!QA^&uA+~?nk:-F>(BdH&W"z1ɸ}& =;8"b Pg4ٷ4{YɗTs6nEfPhfGdwxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/documentation format de fichier.pdf0000644000175000017500000103217012014170666031217 0ustar georgeskgeorgesk%PDF-1.4 %äüöß 2 0 obj <> stream x]ɎWه47Q00Yy=|0MJ\B|TL0 $qx2T0Ww_tכJa2Lu W=Ć:_Foz}lf n:]U>\oo }O}1OJo:⻟YOćq<CT'' 4ȷL(MޡZBheBûz߮tqWFW[4]M%$x;]B`1 43ޫ4:s˿$F.Lf(J`GW9{Ϯd,2^ݢ6F< uDcxfvY yӶ5, 󫈬?7iI_庑HB']L k|>. \dh`*(U;4^t@粷B( #^V:9Q!@ q^zDQޙ T 0g%IlYԸ ֚G:J1vPfzQ7]$2$âW"ʑXe9o}4zzE۽(šѤۙ ÄPm%Mv:$Z?u > m/{oV-iY6+G/Pcq#{;#̧Q%8OvUTUKmRRsM- e-2Yb}uPvlfhgLF_]\fAoUcld] }y見6N7M4&blUX*(l ,:rօd kQ,epFŔG`!l21$G?֤q9ِ:+[$Ƿ8 zbM7wF5ϯ+!Ԉ1.ɢ :M-ch3u* -ׁ]KXBet1Puc~FOnId3gG hPξ~h3i A9z=k \ iP{K4(_zT>͛'f,9̠ADʰ[ \ŤBn?D;n>IDy9(؍QRFGFNG46sjˢQ}kG n9z7Y!ָp|@t9bK 1Ư2RdÏ[rx13A2R~_RQ+)wz6);N&4Q;$zV)RRe)f99$&qvE>"49mu6X#V;}g7=34~+#k՜Oo4JPQRZscnSݾdpK`>'# [.qQ>e.k ]1с{CC0il~9'aV:91m!4\/+Ms j]M, 5r@ښ_ۚߚ;p'}k;"bVa$U XRY ip)O?7^J|Nm(OևC*|wVѫJ*đzTu@*QGXj:>nu"RB)gHf)=TLюNDrDh̝iǼ6ܝE1އjRzډ1ewq =9#3rJ^,MOXOiU ` Ӌ)Cb%K.MA*fl%-nΟpG#o"EJGOvRA:1 R!1Ʉ?cU Qv (zKw*áȄ;u~lCbL19"1C̶ Ss [: d(NQKY*oGK>Ӯ%L:SYFjl|<啓=x=!gMZ9CĹ#;2񘂜Ϛf3#S;~=~VFYjTۜ$_s;蓁昚CI)Ͷ$T[+܄pv5_yDzw B1 1q_!-l~z rUiJlHLOIb=Sia$V-bdydETюP9A{ˌU\k/>!ݟ1z%~WהHǰ94-7v 7W܌Sk5p͆αSԆIѫ|u: *=hDUfi8KqeM0#4|Q _ ~QĈFJ;HJAmZ@ CY-Nh4oK\z4,',3`>m$^DױgBN!)bˤSJcSDx4^@}]o1i-f7W53r 9WmOD'LyeSz*nnr]G.:,wgzrfW B%H{ Q2,mw]#dGr]ٺGҭ?skGb89A6k)5ͩc[L 6 u`Kڛ>;=I\)2 l2ȓCgi5O0ub鵵uzEUn2m[k+RV#WWͲc/ twkAc` us391>'P;c,\| n dTu֨ AȕYl,َ[ښߨ<6lR( ,Tqt!tWR%雲:i-,3nXG }Ze D5K̖hұҶ o LI$cstS]L;y^k hS4J :u08 -krm#՛ sMM-[%X"V`p" 8+H#!/Y{:'hߥ{lFL=NT(lKjFaes/H]UzlºQVo`|q "@KX\Oڊv `2] ؄ IL=s4QZ?mUQ͎}o M|HN KW (o+.x8v=H0!- ʉ ;gp΀)W %3Iꐁ0BH|SR$XRKfr;6<cFa]|DşSO1xڷGLewPX(_^'hoG94<.Q3 Q9'~8$y&U&ą&jtցDExi-uJxү.[lР/2Ri[1~yl_2!^k [Gʭ?ɿ62),Ao_XYl]Eϗg endstream endobj 3 0 obj 4011 endobj 5 0 obj <> stream x]ɮ+ݿ c@@Ox@~ q,$~n>ӡ-߷:i{4=r8h%6;'oR(ݵ4ya]u;﮺}(Nhf8i6{kODt<'Q[ˬxm,[+xF^7Ehe~6ԑo rǷ\CaD<Sϴb3zQ* %;2n#@"% t>#H.)L3w$ ±Tqd#;!53$ҨrA?$Yn ,֮M_~RLg"0W75sU,p[H}mn6nyF ǗegΡc E:`88D=msa1aqvkm&KtoViтMTh6-Sδ%Q^*12Xh>Cȇ@Y#S5Y cSxAbBdC>>x^]{fU*T_jEw#Ig.?G+|жx K/IKʴIׂUZY!$LBC,B6nٓ'*پ6\~>~W t( do1w:(|jrr1M"'ue7=KD(S6nѾux 8N ]V27<ղR @XzA` MLlE=kyqP ܏O`:N)cj_&@Ӻ//_6 єXmy#@[%7YeJh_Z[u8 lzin7PvRa{ր D~;ϹW 9zS%C$limƕ &ց|$n0luddggF<$t&sϭaY1,@y¯@37nYOW92g;9>t$(:f22lj _fMAZm.RZ?!zjٽ:uAOLÕ1s1$AǤ;g/Z+&P}kʾ}·}R78:xT#*ӵЪrq| )/3_B 9"?rĸh]ِS>0νJ?xe|ĩ\"5.C[hàJyֶQgl%fC.r;<ڑIbnG8 # Q|DXt`:ե̌LA$nFs5WA:@@aD8oJ3M r`V*ԩ㘎| ˳4;]뤭0&X1!z gq̫,Qq.u^kaPL1W0DꜜCM_'eJt 9MƟ촡cl=Bnwʵ ϻ*N.YнC1[Ky%H|u@Z\uN G )3v!+VA$4q~5`JDPA4{b9 Im9Ny5=9w,ܷDqOk]c^$cU[MޘlMC3H홬ޕkxu. l 膦QXjN5z. m/rjK3ДGy\4\mfS<8h3 ?gMD#&xGJV[WS?DP/<?} J9&)-eHm&ʃ1S ͸,0šDx~AuV{G[GT'{׳.IJ3Nѻpk}QY]#y=vk6[jwmG{?L/:緀O[%!aF_ )cakιVm~^l,FDjvR =ߩ=.&9pG}!{M}DZ,Iy]n7w[$>Ń J1oBmtNSa.m:ƇWW/ ,+8=42َ=5d.8M-1f##4S 0G7Pd\ᩬQ?-3lhlJ"arD# D-ƶTQ\ⱐjFwK 6J꧰[XdΊJ HD}ٻw?=)뫓",>Рv'XQ)L+$uT VuǍIpLCb=ps7w|; ׃ ?=hߤ9ȁrD|ŕ y 4Z4\?^wF+\usD⵫)4[@4/z!*2+f˧in+rǜ*`4W042o+7yeqzִXXjU Nٿj)_}Uٔu זU̪G}nM|[/OBY#Z]@[ŗ*"R<)pD}ɷ'QRR4|=!E"-{i`$ȅCC7OyEJ2m#eAw5 O'ȲMA9IJ= k5 Ӓ4E-QQ|%_δV ȷuN~ʭ':C=zE0")G5[H\'>-ܝ# wK=6"Xw` dA ߰eLH0_h>pܵ0@ڜT8(xN21ׯR&=-rdOǤ3GTۉۧ_th 9~̭2d+[l)E<ǶLN'\;k+ϢvΆ!jl߆콅B7![PBtd@@Xƒ'21&`?&x9mȓ 1#lT>7'!!ü1Xe2j"QL",|xB;eKMCs4΅ [}D-LƧPށbxHqN6(:h dN`k.Xog{DxykQ4|AEE/:rn'u,>bЅ߱ŕb"1*aW U@%_#pч24Y1}sML`$e8t'Hd&ok.+?QY DZFn," "GV%0/:"I~Q05ΐƛ46,h"OXq5mos#1ݞzȬ)k1i\I; }ƈ5Hni ໞ*{A g?;ΜMNh첗},CcA/q]*1a"5TtXSE`FmyT?WNǟpw05e"R/w=aI$t?ӣeMR3Q^jlKK>EYOOS3` :||,$dg6vkrϗC# endstream endobj 6 0 obj 4457 endobj 8 0 obj <> stream xɮ#BgQXܺ0HzRN$q _![\kaޛ؞bis'%i?O78ɜӯzمهSp9?P 5Gm#)]&1XGʴ3)garJ 8=ݸ'u˛no|9duKb99ϿWGK\Qv-, \CBV dA xفtH! ,0M>(9r#H?$jzPש'~0o XHps6vGv1ٳN%I?c"S9xk=0'":'(lxp:\z5T)O cT_(s3Lcp,9CQCXžV1k(srPZ^iњ نbS1}F,޻4"K{)xNaK3(0]07h.O?E[dG1WjuU,"KHFnB2Ras7SΤG3P_El|Ku3q')MaQ.fvyH )Vl益vAbIĂK+NJ bZʡ⎱@:\kNxPHwdo Q43(07w>hh$a7Ѳ =ŴM@~_ȑ>z^x|(9HNHkxt t:c³k*tS2s_ef4s6[\jb$ll-wە52rASLow4:!'tB!f%.Xy,"bVts$~X[/:$2ūD2~Uxa| +muJ8Gv6E?RpيWgS])[8Rư:~$}.uˢ^h8."ni*O^;%2}D׶5>P wpÎ_`6=ZU7j=aX^y>X 0@hZok/1.:U.̹E]iϾr4% 4N r7-D*_ܴ(w3GZZ/|T/ w^z3m͐`{tD h́͘NL ߟpx 8QںNcRx ~7߹^jƒ (V4)at7"ةmMuxcyĨ;8sK5WX0 Yoi3 Se>;Қb 0+UP0Κ߷zP%m}3iP B{0{Wfa ͆/|bo_bng">E8x+Qҧyk`mP>v/ґ89ݜq ,,wo$fl*3ԤF;ˡ>eW& |YG,5K?0s??IqL X2fAۿs4.0H\7s0l'SDUhl2tz3ƨRt}Vr%]ϙ%Yd",6ɇQs8#fc_e%2Pg >̉#ʳ/*p$![N@{n +\#ֺG[uZQ"Okȓy!t*Œ^ꊐKS52F BM#e2^wCfyujEA!-')\:1hio{=ժXZUvOEk;T)]sp1m^-=XFePß]l&\aɦ%NݥÉ"]-"k%vf[oH4>O S+N OՕ[u8\ oQ4IfV{6@~+~iN~4A|[̕L>ะb "0@f,pi}# < *3&ٖ}Tsa-V_5/fy.R).`=7Pqoa\4}=L 'G݋C% *% b:yH<]\x,"'#v1r1> 1Mòr}Zǀ[ 0|W*@ < *zU, 9WTT9Us+ĐUB.0nKOLw^-eu ƞLZC}gS4[1k3.މ☦:'oNLX)}A7_>t?  endstream endobj 9 0 obj 4245 endobj 11 0 obj <> stream xZn7 }߯݊@P`/}Nk6@/idfǍ4ױFx?btg:J>>fLhIMV٣w<4Ve?URu)@(# ZRCmP[5V UCmQ ǒ3[PdHjhQQ@dv 2ꋌ@ rdeVryo7uzz?ZANX80=yZ 5a yfFXqbcػ#0'vYX.c\,ye_X1oKak%I 5azXG 6=8&rRmYE;Y/Kx-?!22T:m瀼=Զ ܙ@br h5o9(,#%l#TR,'!.K\udt.6UTDJi4JS2*RIRڥҹd]݃d2;aZP=뇃~<;qtj0 _ϝ@)<)7 w>'NaEKru5rs+3L(eFQZ㢠f4?YP؞>U), - wЫus"aPVi5@O m)8remrI*T6baJpʛ8,Ľ(b&`kWdpY/ȃ9ku.۾KrfFgDUUuYX!f{"PwЩi:wnTWQ]e0 )צ = E~دȩ>;U\j u:5>(N${4Oc#{q!8CtD4MPQTl}UXbcySxj`u̾yL.DDyjI&=g@{X NRU#=&Y!l&&#> stream x]٪5qO Y< 1N~qB 7~hJI{@0ݫT*\^6ڧyǿ?/!>dOeV{H@Wn&>WmaR;D- IC*'Eۯ jއCǯ?=~/_37>>ҕ>Ďσ3c0j&H\@b L8~ e\zD"y` A'jĊs^eQ3VwE̲3I=|6׊.d>dE5̧*clgIAP| InlL2"O=""SqR5-uoBpg ƥHB-{UςRф?fY+ҍ1*^-@ AV8n;soU\ZcG՚[Ƶ1"̃%;}r^\A-.hǗaי/kkѤo0e`R/UqbCմ|[CRakf9d:k;C%$}N[5r^eyLzǾ8J9w.k5g7)_끑C:~NJͷ4l F\o^E+Vmˡ‡4]c}LJ !V,}nzYvvY~8u RG2lBjf:l[\oţj1#zz7gfGsC8 m{EʱiS!{}H~RR$?~.~1&:W?ry69iJd2H=tȢ:3P:S\oYNO$ts2lcA dAx`(!+dGS> VqV3_/oK.cdF~k/2XAs /2p\:_d005S4ҷBVUɾo/({LC˛dwu8.)ôϪ0DӒ3 Y91:L*"jL$5Y7 #cv2dsaa*(͵+W4vvd%Z#1mwyI;2RALQ>颧 d*1(bh ^P5Ck7.Bca<>!PB@JV`VTZ};ilП+Kdo*UF¤Hû#e"oJj%HSUكT0Yq[9GeWWڥy|pe  !"(q#5؞GZXph2-&ʴGHqV`Lio%| m|.,AtMK)uM/>FOBfzQI׆SZ9IG& K&-tH(FS˚T1,MB6&pYSz_P Mnr& o\ 5Q'%$LԼȅdΊ)Bd%X9TNϒ8,&%Xo)}Ag2Fs"&еAXM5sf*ʵo8F^B w%0bud."2E?RkѢ=n-JXO>[yvEЬ}(-3ͫWv^ྦྷ`#LhVygǙк}?,HZ>gq bfȽ͎aucZ7;&fnv @fnv0BFqȵ1ffv͎AJN=O(Jxt\㕧o IR}T=US(@nud0 @Ƴ7TM?}Q>\. mZߥ=MhtXF-u-4 x 4DK0)C$ҦVP |~咞 vV&B,001P1F6R;ɇ.]Nr H6X~{sCu=͑ZƥuC8UCSBuC1b6PB !F!TBP7 !FmsC㷉|Fa0i1_BMMC!ı2nq. !K!q4.Նåb\lSBԆcq!Ĥ!U!4Ln1 !F$!ĉ$s/^m qTR0's0P a`(Oer6%aB: l?q5 9bSYbP:ZE1ӕn7!Ƙ[br " hkW {:mJn4?pW\j`Gk4Rat 8TA` ]s=+[LW0p}ܕ &}\rE3R`LNBxM"ɄIG>nP`;"Pa%&LO0ԃTaᧈ** #(Z08bݧ0> ͺ;6}wM~֧@>^^O߁6}Ddӧ+Hwv #5 X |(Qr͇c[czZLg|,h1!\%F1QEi&jpyq$VThdxWuF ]1n=ap}X : j-f %EQfOi ];o z|>.=GA3Eo,4qgR"'!pzKSNg]- @K5nM5nQjuU'XgT*DsNe/}F2ʩ}ڄ֧ʩ}n:Ht*#c3%vOJ8plkedW(OV0kmp<Ј(ݓu6fX7=^[m{µR͹ hh[W u|(-=֚A;#xm҆p)Kd=;|q b ⩦ÌSRmHSd4.(9 iІ~2 ~freULf%9rRJfMT̎f@_d:̺9aYS'a7x;;Y oh3hZh}@{Zfxh3"/do/8۝˽=+ދhd7TvǴd;oUmo36~{o3lP mhr{?۲<7jD,طXkgRS 9bz%kO{zlѫDgh@=Qpc_cF~9,lqu]GFsaZ#Ìq_Gq8J0"8:0J\#ǀq8L% 1q8F:M8,P"yHz![8c,i澳G&5mPߔ@.'w^p܍p%W Z+ xHѻ'?!5k@VW$ ].+% Ҝ^JeEȋ 棦;oۑ>Ѐo Ń$xQ%@\.G뙵i%CmQ$ %|qsFrie8vs*Sr^: Wv;v9Y@ 녦 : A#]s%qsCqMGE u ~89(۲z9r@ɾb+{}4l:ܠF f$|pȂ 똠Չ{W'v4MÎqJ:QvWbY=`Xfw~&A+ɀ ? 7X‚ +(VO7*n ʋnTV78NQK 7HNVol7܋ҌX,{n)2]xa:cȢQ 5혠G~;t9RP֖MLwOxt;&eWNؒv rjKuL5@П˘}:8]uLp@{MSvH`> oӄf5!@Л @9 ~rz U(d"xz)za;)'a?gd0-;VB޳K]{K: .U~!P}v fg[!?rY\ )%v |BA@w,:WQH'"iT0t*1 AVVΠpSգ #u9vOG)~`.P8F> stream xYˎ#7 WyI $ K~?EK-Kع80n[HTLτKnghegiLw͑hfgl߾\C;=dG]u;=jc (ʇ5@y[Ĵ@QjrorQ Pι(g ۃl[@mP6(L ["5-[nR&5nBƷn\ƴ.iVb=+yŚb*YMdqHR!<|T$oO3{G_?p爓_/=>(tˡ`) ({0w y1W \CR1h\hL9<8hE Q|<#4GJ^Ch1ÈJ>"ȡC@ 5*L>rh3C-MŠD)fY1|,v|z񕠗Vu/sR*#}UEsU Zztq#D[X-M_0Y$K84 pѾMfUT 젫](%q?_8.ݡ٭ 0ҴMTCARbm,Z\р\Y_\ CŌ+@~)0>t}zͫk׃Lʲ|cj~RBcRY(ibm `Va# ]q6wN(DO-[esrUI&>dyOdo'e 3%zKXB%y^P UQ2]Mb?]Т6^rPW?\-H=`ن+#F]хqhh\2vu'4)e0k♖^>ϻ;(ؓ0]b,|t` X ?h[e.эxobOaz͕JoO!qel*{ J_up3}l!~ GFj*'wgzͧpyzoSy&+΅D@zB^7t]noX ϲ`0n r TQ!ӏ}:2b:pW UOď%o b'ZlkތU'W})7|`Kiדp~iUֵQdKWR'<GS_O"|śr^zTW;s=0F* :qSNz]w/r{&]Lť#waj~GU4eZsڪVi> stream xZɎ7W@:pH$?UER0n֌X{UeͿ-Fۙ6j?[BφBǶuƶCZ]jh5s$G+.,Z3ݲE t[ +eP (aX דJeEnaeX q̭V@IAaP (t -,Bvk@A\ &ͅDvM1,F~ d\>LuЦߔqnl2E$/ A,,WyrXOw8 }CcWs}mޯPNPY@R`,։KAZĸxw/@X٦`ANc58 "!JY Z 9OWdmP 7L"dL8à*fgvZS]':p')ˋ?D|SEHBhQEՈW> B4ifNlWrB]Q($sΥ|VI i ~M^h 5gTy!&d柖X ˫rG2.E)+33_}+IE%6irjRв@ZNu(R=Jt.J`&P8@3.5ɑX~0y(TD\aKjAZ`S cV?aSɯdXQe7,ñj/WI7hJ m6fSْC(Ħ&zt[P`Ɗkm$.М@{irp pUMEޅ[E p7_zUΥ&9\=&ypsbNUQ Gdgm;s\ϯQhurȐf8oS'0 ~Chz&v4OߜqkT ՎC?@tѶ[ ##䋂MdyKm\n1&"~WTMF*"' TإT&ti2N GwjB0vyJ75։]:ڤz'M 60_HUa*+09*U`YoSAƅ=Iَ>ZצM[Oٮ%G]Zpf˧4t_'1?i! Gvz݁EVBeއС+P6 lѲ&;~ nȕ0kG,YgGyzc<_^Q endstream endobj 21 0 obj 1469 endobj 23 0 obj <> stream xjAz9nrė~F#wVB GS)}[=P~/u4|)vX4Ir ͇lJS[^UhyUtUHbSVkbLMBb2Sę)Ĕ-F)RĔ f"JflJZMLIĔXHp`@:{: t t t tt t tjd $Dбfed#g#%#d{ZQ14ӆ>tr|(BJ_ qw,XrPCٓ=5zIUu'`HkX_#WhXhv,6 I`/"6ޯpc.a̡`{i~Й"M`F6K[j ֟y:Vq]`y+~Í2Y1̈́h_9qIl3X<k'6.AN(+a055pLup3DG/jT+#y;n=6AfAzHE {8T8Fa{%3>lעn>R>Q׷} x"Y endstream endobj 24 0 obj 662 endobj 26 0 obj <> stream xMo0 :C$`˽@}; ."hZW#Rg+]Rd!G  02$A8O=`:DS^2hIGS=SSZ1)9T)pL)g31%EMI&ǔhvLcJ"zcygp\6srLĎ1zG@:'tVttft&tFt*=I=I)zyzxcv@䁎ꁎ\=ŃASs}T>BHc{NTn2 1dLp#2x*W!9 (҈Z[2,,P;xt2k$n[(, $* vV3vB/onS'ہS^vऔNX;22:]59a8r ]߀\,ЉE;wHَ\ג}+x#D|O@GŤ- Pj X_ tdmMq Q7uDW̱BxXV텯d'fP"1;K[uptdb𩊐Av]&_(8 iKGz.yMpeixz~ R(mzcgV+%n.:\yHN1\r=zdu_tŵC] Ex}Jai|bZr9mjn&1tzaMf;^!Z!&ShmXIGװK#F1u 𾟓Χm;`˽~R=Tσ$.ZC7P^>wѭzvR0)! endstream endobj 27 0 obj 911 endobj 29 0 obj <> stream xZˎG Wه 'Vr0_)V+hYذ4GVu.v􄿡ٻ۟Ӑou.L4>Hbςӛ18ikrC2iC \Pgg)>ZG/!sToF9/VJ9<xƱ08vqssh9Ϡe?im 31I6'}[.+GOtWw/D׿kb%^:J;@yI ʼ8 G:xJz@7 .Dg8PQG3gڲȖ f I^X^ en' Nc%('L,/yC_ke>~n Gr38J]b#[1 -wvz+ |TkNs`*p1wÀ"^IhBSCIp-I0cR'N@)6p D@ pEcċOGܼL׏p{KX-{AP!T[5BoV^}Agdr[1_ᵜpxh·UuBl =r26H#U3FbrGխ ThA *8 8@q]\ʠeӠPJwYR&ޢ43__ް`i`6?P[ UؿyuG᭢#j75ЁxBmaӘo:30M=,L ש|'q-=]]QӽҫдLJi=%bCx>zރ;ji6͜X3A4pjK?b{ PFR`>΀VjbRaX*8BY g+ǣXң3d__;z;3V*nJ `L'X3IS w|) +7. r e +ʶnZhZ cr0ƅw92Ԙ gBJ=4N Pɨ Ym-)dXh7df e1+{roh s[Ah&dYV4J{ O =, p5*Q[dM%-km.ZB-4 ?9 8Hw9`h.G? :~],<-ZRoUC=gx <-c?F[p7-ZBҁwK 3+Dު+1'% 4-ǎ1615Pկg])$OAwKN٘=[M04ZA=]F/~EI[hü߀4~vY.n< endstream endobj 30 0 obj 1407 endobj 32 0 obj <> stream xZɎ6+tAh ?Ld.%QE#;=$Xc?-Io`-tgCczδ}g[d#4w:1MF]/HS^ozn^㩓^5W%W[r]{11g7L""VYvRU:TH*[4S5A{`Zk Y `=_=W.Ƒttiᄢ=V/`y@ND.:fǂ(L`/,-0cb'Tc=b?D#p)ǩp*1L0@Lj6+ pV$2T6EYF)3ءCrC2D'5.p%]ACQ[kl# #\\H7:; ̆ 垏 z1$Z/r}(A |pk ._2xFlT\Cv_rp zK^,<5rْ9;2Ҁi$)5Fz5CTKh*-wc$[ԋQIY1_#3-eVSAu{̒'%$%~24553qEEe,fR!:lXS%d gU1 OϹ%@oi|9T0mBw/S7=(L'6(ߎA4~X_q8q(Z=T?T!&?c(G,֞PjVS,~/d9 @0 3* !)BUFg;xBy@Tdj//u@DMf)Eh\<-]y BE8v ;W"}e/H}@6Y,Mʔk,> stream xXn0 W蜃Kj$ (d<@sWeDI3A #OͤR|ߞoyz ŗ(]on #n+) %Irx|WB%iߝ;N—sŨЗYcX>?+6 Ѡ;D~6sCKl1)Xub&|l3˓wSށfTv%tl@Zȵ e] hX܍BblS29 8R~6p`>~}@߿L2W8|`9(P2v_F{+(ש9rh ζb6e p!2:ጀlxӓ4š endstream endobj 36 0 obj 879 endobj 64 0 obj <> stream xԼwE?\UݓSwOOwgfw6˦DX$.U =E0b'wS Kt0 b8E=+;>3,xw_NuUu'|P"+Z^4sO~kaaKYA WE׺F"d.; 꼹30Mu tpyqW#zhovlޢ,Bh߽p왫[v˗-f | 8%[:%rݿܹK67A?z& &j;8^p.+`(,+h,HҙlEeUuMm݀M-mC;e#FaM3 (~?]^؟ph8.@KZmD?`82y Q3fh{z`#Ə Xg/%\0õѿczEoØ2Xi<Oŗ f~?&:0+دs;Qɨf: /3M$`ړUO/?EQmFCG pЕhz}>GQb 02|waBg9Wns\>1ߕS5m~w`:t6cwGЏz{G?9Qguˬޞ[=%_ P j&Ѽ=3=-3D<Ë܉/¨>]=-a`d6'[^9 f"\¬g2o2_a+Q v{1ϱE=zS<7$wNܳr-ϐU=NBg=^5 ' 50 6aqX6Qp$| `Wx3~ m`rk.O΂gl$]#'`2LSʹ03iV1||VgunV]t_9_0gf!ڀ |E&M2 ~kAi'm | wBDfk;H3M0V j2=B@i0 d:s{ۂG7 4ܽ:aʘ'7uFnW_6fP0n'nFrBÇa{*pw =\GF@B? ϸ -$`^n];a4r3%$<_]Yeld6O&R/:]kP=5r#sgI=%;71& 22aJhVPB18Sa@5HG2@ OC<_Jvo2As0|kGvS #ItM [3jj<!< Zga't=Fg8HkOc<g [&˸3oc§=&t'z0hN3tDBt-h0HSa_;: gߠNG&P#a}ȼȨL].Gg`puwJ p)}{?=~~0Z߆:h8~@mMuUeE,N$X4P0I^%:smVd4u,C0 !w%fte 3u蒡k؉t3TȳN:R-9 5eQ!QO;NkQZ}VA]Qyw ϐv `ޚ36YmѶ d@.Ot&iZxD7%E;bCgj;qL*tY](:ˑAmtۺ ӠkM=kЬi蜙S'v13'C<oŅ3kz˴f*k؉*4 kO_8 ~\5ib ~ROB|sCiόr):8:o͂055]efOݑ?|C5&FVt!M"Zs-*K')ln;en>Nk#OYL(z*D<[;gzfv=I326c 7tq.*Dsbb>hIzW:ݕJQ1=hڲd~t 'Μ40 ï(tV,ht;Ж,ffӓ gOxgEgDj˘wpny?[?ȱ'C(q' k]ζkh{(L]lQ6*,f /̊_ԝ?B6O+f'ܞu 7&q׬1oH5kEakfٝ_1+*s5;x$,:wF;w n<<<T|';Qfif )&AjdB#1Pf@t ֥:n>coFISb j@6 غzu,0ȬAe;@c$\M6i4iBM=M[Y ?&3{: =HA(-TCh2Ɍyo#[y ?ȄAfJVr+Ȉڬ߁F՜kJۍW^Q$8(V˸j *XG#I:"~bL)/9sv'T0'$ Q%zF4Ĉ'6lL^}Ζ(ʴ|w|i8B l&`,ƍ3[ښQ|)^꾧\EGzaQy!{0uF9J{OCeE25m| $Il%8i8)^wt=wn@]mM"hDžM^K{}W >)x.=W^inMK72>;lȒ{CgjVKwL1wzs:Ԝ*k1M-a>,? GfEbeqtF۟77עU=W\È mQ.@'Xr'*㪚6AdFT҈\(4*r1mL nlkmilTZ[dyyuolAՏΪ(nU;: [w&8$G0\!fϜ7?߉ ȋ3@zTi!/LpߐK'=;NQ; ݪ4HȿEjx-b E[fSKZLZuU)p5\NT83i\B6w+;K;Mz6n6ksTw [:;PGvcOU`iCTf^hiZ1-VL~jPB1UoDZ՝f lⶭ;w '`ت6DPhӿà FFո uIvyh_>J6 ~|ks;r.s8xe9!?4~> ^`:nȽH+=ГtuZF*pnfFRjchr4;TGcIHX[3la2>00+԰43Ttq~` 8evd o #NPFf=⊇!pD¡HYUalnJ;hmZHYVJzlH P0}Ja|..{j½MLvbE$ڢi-Zcinۀ2J& ,'4fh^wΓalqjlkۃye  Rd:KMN/=a\K cTT|\::.#be9-bk!wg3; [z'apUWR4_P ' :+_p,SN9]4L,4iݟҢp3kyC xmm긡s{7M)4(ԁw`{QZ3uJ0Y'r\DF뺅#^1dp9 4A)h0&ĝ8JVz܈GPҧ#~~ˢ[e[[{m~lsQ<]܈7 :,[dlCF\/ tEOA85e 8[F6G@H"tFa #H LƶzT3];Zz]b/:y,(Q(:`cd4F%h6飍b7^Z1˷O /@NlRm+ 4~p+w~~0w0 3Xڔͮҕ6^d;F'5OMED{E+O!]7P n嵸&5 TR L"͂lTwHLN' +҇‡G~_M 5>4Zxrxaru;w9w e /^>YFAc`bk܀na6x['YQRRRhHs::B[PAwD(*6EGp, NXqzW֠*dHhb-;GgO{cyW~;/wׯbKCyj$F̅(]xgLc ejV=NC-ᗊw$H8=bLXII<I[jl؉D;gQZonLovWD9戶GWDE7DDQ)sqI0t 6  zp?AҟW~6|q69E[i+n daO?}:r4۝i]RM- wђs}N&nPxy(E09!{50IhGG5˃|^_ n74n ti4Q67em@5UQa2fRcs3؎mn\qy7U $57(cBCCL7r\ԥTCjU'-.ť6O˴aEأzz#:PvLphs.̪9w=-0j7 a04ݭ0x[klׅ8foiEi>>[sȓ E@AkL*k4Pán#ECDh Ѓ-iqR[[/%5=nW)`PGDmM2+ *Rjz@SW]?qXO 9_\ntD<;߿a9mǙt(u]疚 ڦ^:FD/<\z٬\3ir* A\ME*INfKK۸魦:]H\<7omVt8fgY#)4l0+Z`ݥO?R|c?30|nhA<\ܬylyE]|%s\YKa VQtGgnJ[uMV^Aѫp^cwun/f{UlttRJF`" BQ$nDٙ9Pj TE*҂Tu:B  -xJA0 "XZE"IvH 8M8N&aau٦s'6|B=#߽# &Uw^^#ϸYtVl?s=$o%L Lf , +"a"BiVwL"R[4>l[$R\ħ*xDWN[ڪHUcg (ҒtfYMSa*r床 Bj8ef?bqC(h̞Ah0DЁq7&!{ aN&XP@KMIԯ,-&0I3OlO(bwNoi9h,44t=q+3钶z?װ+!?g9,/Ȏ0; (:Q\l~jBQ=?[a?NeNhj7u3-ɓ46C!?!k`Qz܄_k)2 PF^z(^h>?az+z%E!/ٟ 7mϪ#ɯۅv?kr$#+K5qV{d$G=~X xrHg2Z%""+< Ռy|604Y n쇞= S*~m4V3nYe:=ڨb9hrHf>cL;lFOfha?HE;py >[m@'&o;ߏ8~r>k}Y;? &tst % ar=`7ٍv33?ljTa1P9Nl]i2:AA2l*~ 2Sn ['r.<[rIAd$QH%XjF3S8ͪ8PT엠HX3**'PP1]F)W^p!*xO+O w($F*TbA!9[HnԠ`D8 n)3Cᦵ P"ߨ8DOn%fo_3z M sJR![86x$1bv)q[5Rjla?xh!ׅIؗ0nH`G"X`Rz LW`R $8%JHA#E` ψiaTys=2]{K~Wel =̼*?ho0ޟl?55/EuX\*{{=)1ue3{K Z(bY-,z&yؗ++@u붤S+zXgq|z@OSCWŕ#E}nTwfYLRy%jIǑ[z#\{Wyz?"K='羵l[}t^-~|{e//:҆>p^(9qH*.G;oC_19fggW_lXtCΐҦMBJydlN+h}`l$z%Af3g`21t}&W[]Qvwt_`HJ5'8\9í('΋6~`O@nݜ1VMvI4rׁk\NzᏇR~-+R[#TW fTuƼKҀ#.LW/äZkhG,EfH*]b86L9n(+(ixáhjzQa,M1e>6t㯷2LTtP R*dHЋaDjwi߀ O>5N҉pYCG_D=F.ZN6|ҜןB`ARA#{ʠ/~l~6sẇ̺ Wo?hE1b\edY19iv>5bFުH?$'P ȁ%]|X(l|ܼ= TNBe0vlLad[ # J~kxC`e?>/iؽ/ m&s E>3KNU-(SF9di :S$W+++7X7[KO=@֗h|UnFb%(U T^՛Hr\2X--R]K\oXT{Z9vҬ&s o(p /f*0JK  {O>?&%\cIkسnGopU+ͅxfzN9Zn-ƳpNb`d9bO=tDbgo|5;.u{,"8 _h8J"<y}>6JMH%:wf|N%֝ͯ mDC7WDw?y+o,)ѾTId'0qeh7ʕD3󉯹=M@V-r3I$U%?[EY?̻XcHU0W*A.(ʌYU>02l.Լ[ƒ|0ݪu^hPôZhmuyps>;NE44ޚ^OӜu9asLM |, `䚌MZX){CZΉ< YX l.B,oF@#4c 6IcȦ6^82&3ګLm4;-Ǔ<՟c[/I[n~φ?>LsgfdK 1w02zw@R6(I`]ñ/t3(-`[i 4gTY 7 aF_u吣iq*4!PQlO ;X+vS?5mɘF *oJFǹh((dot N|K>/'׽͕]:Idy;̬;L]m9}lt\w@A2|_4 ./q{tzOVbWiH-&kQYWX[_X}gn4f8}snΣU%~OH C%:R #܃<D5?z^&/?6'<V8.%I{C19$FPތbJbdIP)AVva ôt):ɤ:VCDA%/sָXFu,r"B^Iz% C^=! p$<5M WaɄL gpwCC'ᰄ%5U+5u5Ҋ,TIM$k( L/Y^ޒ}%ߕKvejQ=n8ͭf U_&n%j:4i 1}:V4-ECb M<-Mz= y?N:;^/,PKpabP°R. .ǎBȮp\ 8I<>B`]>LMnS阤D&ʜ2tV;'1ۿҰ&멫gYEю0zY7.q,dxB˧} 5Z|L\+7cNi鰞{`)Nz k2ua(XW[UuUU`NreNx[.KuDY`f(q;UY$gMB`Z 1l$Fy2Qh \%`gib3 " }~`*z n AAfā١J[j[qG'G"Z@fM A|-R/ M]Az1 n;xֿu]c0+&hws4ޜ}w5+E/LX/{Cw\h>"U`!)-*#<"j"tH %v󪽖m4#F};W#ۈրE_5CG)3hre"+l˜r46Źy.؇mN  >!9br\+V)J.ٕzs0u(/N1 "(f- 3Ȓ [b,5S:nUnf1UuE]d~oF).%*Rli:-7|2\avr+l()jif< QYawFoYlDaS*nB sLEX:Ӻ2+0cq[RE(6#MSwjA1-#fZ @Ǔ?<ps~=]ZK{ݼM(mSno{7̝98́o>ﮙ܈Cgϱ;&Vv\47gΗ"m5L6- r #,I=!{f֠i3ta9.hRH$.sT+HE&XF:FAry>(%r43hBݒ@~$&^O i0nܢfb( .@~OouyÈ[p,' 308j6Qh! MW-|ӄ4?Iw݅}U~q|6W>|w!#5iܠ2\-L'L\JF)0QT^ !vp_ߑ6dw[;[IS[&Mf ] r |ЛDQ(.8b1yMFci_ xN*iYuQujZc=PX_&gO r(tk>:y|~_N>?㾑\{-=^[=x9Zf|]%I R$ٝ'I̝9[ $U4HQ sJ#E"cf0Ky ȁ^wIa7K&ݰ&Jsh:3YG )gx]wx&F~mĭWG+]f,foKהU}cV~Vi,bztzJTWj)SG$Nה sXL %Vkx7 t ɲBN9UZ1t^ er@1*z EO/߫߭?gҀԮN\sŹth QAM?T _)t`,7%t'Q g~Ool\ "^M*~o5Qy37o{bŬMnws!>y ]k;X|vwг`C={S16otZsXzQAr+q/_"^1UpR3YsuCGlg@ ьݚ,ײxzÒɞ0P @QVVRêe'Z{k!׋;nb  ls=4м8'$ (:),g&,AA{;s3Tm]w ϟ2mUW}M6[4).=}O從6=٩Q7Nx)fjH)T:j[2NNV.g~N9(ʔcB m[kFذx!ZD:HTz}uȑgGkxWׅ78\`eR'>CLErR^n2QtJivִ/ jf{Ie a'nA|~Ls5:x1w|g3gd.K^أ4jOq햖Lh:GUS;55ѯ$ַ И(\A5-(?~9H~_?rU~yW=#L4@ITR~"1yfԚ=]{ӏĖK_ּV66n喛pK%am_wtӇ~ ۛ;W{ޏ?f bYOO04~z?  (L&ڏAjy/ Qt8g֌oE#$RD`8ߖ꺪ں`֢jhԱA ]lK/Ņ嶥ŠHI4N$k@#9 01-CЅIPp8 Gq% ԅ*ڇ',IB#6f`uOTW0cc6` adv +1}Sjxnq1Ol>;L_EQfG:>QmRîG=MZh ޅ4Ƌow tj&ʹZ1e}x=iIeW뵴|74-su^w x:9pܰ/ |Eڑ;<2Əa).IGV ~3mo `B8=jf+ul5%GZJp92jzDGY6SvdFp͋ʼGYOHQ|Pշ,w,/FH>I{ܜNa6àx8P';tCZ6hr`>[@!}PM\>X*h m|\~ݾ]1td DjBPWED#r^.3EmX7nCq WUC.t}T;e}'i!r)nKmtլN _%  ʓlN#<+fOS{t8f jlvnVf/Ln>I{*܏}&(fOt: <~zhD?0vjlD\_\] jy/Ώ=8דm"e;]FKgJEhI!$ EL7~KmB>\׍m o`v'YEj{/s o+D<ŝ"DUԹTMXdr%l;:Q}g_I8f5}j* \z ,wx8N뱛#䓤tM YdcͶY}18'\wZ3eںJ[s,}[ :RåK^ɺԙgG^bmAs,[fh P|&[K[\U_VVƐIb5E`ʧ$nʨ.3:S̭ƒ)S2zi-xj~ZGQ?O:m*8<94*瞜jv E1ɾ,^Oaq bЛ17xe3N|E/Y8"xȐH 5(<(2_6GObAO͵}KNgV/_pd[D[%:hmnyѷ{8S{Yd.>b9hﱿi;hwOq4 _`hUT߹gl7v6Km?YQ+{hAjR#>S1K⽲z}-WB.S{cX:S|qWde5^aJ0EغhU'4}XP]h{ j*=EhJ 0T8َ.jSak[miq/MD/~c*7%ݬ+VdMkNi{<!IVs/ʴy#Xdo95<5<S[Gӑ"z|Zv؅ׯ7 ~҅ [u />0|FSx8+o?mцmSM6 I/"f /"4M--)yl4/[+U \h 55wD:>Ia3 Kg K>>tl ?$A]|1MWw*sSf:k*UӮ<qD~˄S rq|a7'f @ MZ50Yki7E1j)!(E0Sh=x;tTU d3#XXx"p ` -Nba$+a>h>bf̾ʔBC]av_`H K}--4E'963љuٖɺزKl)';! C@LٖmlI76 iH)B[C! leYzP(C/@cw%#<3̼;3sf^p:T)01'a/;YU{f]0 /\69L/3@;5b%;}ZO<95+: ˵ A"-r+e܋VRl)tۭq*\VURBQdL&JrB(r;RpyY"c<nRKQ%V)X(+"z`GpG0<|+X,rC)A7vSƃƏ5o[֑i<,v/ާ?Q+,䦪6ҕY]WPiW<{nsovK{^t@{_qI~ /d럗B 9a sgDdN`RS7aPE jS_ 7{"ޥHFӍnU \f}/_䋽C7zn ?fz] ˠ[oTiFIU"NL(ViB}P}}c&{U -ō_۽yEA _o>oEzB|7!M)69h.>5 㠨{ VNv- 9:;ghhLS˧-o4ny]DWb0[,6ɋϵ0(Q?S<oU33;,75-źFCeM^u:k-dh q]~\dž7Q:5#{w[k5km4څ&smv W)ErXiX"7j5$z% W{Q$E&~d-L1^#ۉlHV|q$ѳEYd6"T,X *SvqS*q5W' Z3h17fAcXf, ˺F{D\:sD\Yp1X ]N5UgX!=,44AѾ'_}oϞ~G&>p1-G'}gxKzo Nz Ŀ cJDI0וՕ/6MHY|t:jזu8:͹m 62'r?Vi^κ9}vzڍɵ>L].w. u Γ5;4KcyHus͜rDO }ڭ-}ܝAMʤ2q7"OIAaYKi *Dpr{\\(HyBz%Uھr\F"d$b @9d!jży-r6L2_pEf lTیt"`]Y8C BΫt'l'w x&gTY5W7m|t\K%GYL~jUUw.$:%hIOԏjz,9Z7#ٌ V\JZuoE{Ua_p:t,B7[/s0N bv~Oeo߸0]"IG׏?m:ʡ9s --!]m[UgG#NmϞ:P-$ }|;}$>?Ul)&R29*$rhs86f5sCZ@j3Y6WaL>5SU\jCXNϾzۘcF μ|̛jʊgm , sGTUTSuenA/U=gp_ `EmeaW;a4F~<2j5i4&+n5FEGm&"эJ)jY!'f*ӹe/GM^,uz՞^_iyGUJS2+Jċjj`2i„=c4FQw.A=Ƞ G za+2ϺR,YNZytA*ziTQ/Noв/6虀ZiΫ"F¶j|P>(.Yu mJs6+s_wl_׊FΦ/ήAŮ?6]oӪA]DϟE[`wnX{ܬۋk \OR ''j )C/ge?RO*-wTQu᪇OOW+4%^T-*ڤ"G4>|ndTszS(}_@ BTLNӴ& \~UåP B(-e\ǦçDߧwVrg`xe"[V) u2rj7že\;G 5".qYk}rs1H魉3~ۺkt~rk?L tT%uM߾8Nꇈ}KVF}zU'58*NGS+7(y?V8}̾qޘ_f5D@ϑGۋ0;:A"1?Ҟ4΍pqBJF餃0n4ϠwNYD;0& 7$܉ 7Xv8wIފUBGן\K6nInfS6&UAXӱ|&*>\(M-VrC(+o?T{ξZ_&6|?_۶dUz9PpXFQzy_=-k&KW]u>'w>y)C{BN3Gkbӿ LIPvߋ-afaP?FUY+Yt%Q.:1t+:ڊ@g;{At ^+:Nituo`k=@!uS4 UΡJ5z[[`dقE0nޗ0l5wJQ \ͥIJ/rˏ(W_ iI*q:iX?K.x)G9(N@,M1A{h{|Jrh1Meќ/^9mJu7!Fy)ߙ+sSN{]dPj˦]K!vϩ f$bO9Mް+չdq CNb!'A0.[|ٿ)ANr[fF5CNcبCnJ7CS#POAت|t }Yc5-ܚ#&o8+8vag{2)_lEт~~&I[jcͭG[b ێs5뾿*\Bct3_ar,=9 'D暋`KુcL="+01!vĄ+&B9'1_7G\\,XO\8&tąuTp*&RCpVhec6#$$#47׀ZIac 63xH:O #X:G|qA]t$P\/TKST__sm u2ߣBg:oR}_Z!OdtWH =t6FPHej/BSRt6&x&?2BL (RiauA!Q@KDžtltH-!ae8O_6œB#VB=K }ٞIFSCqR82X7%G˗g PȦ TE6^Hvx$Ksu*FSSB7M Ƈ⺺Aƺڞ`@v0Y7 3;zDv GkIW|k48}e}GꕫWvX/t^}vuu;5Js EA`tjy7y/;ڝ!ovQ): !8aF vŅT7)!R r zin/TB6(9^/m:=ҐMR2gE]H; d·kCx&C OKeb&&d=>9%CP?}7ۛ rrA)o!L%([F-*40Mgx; tnU50nC3X7]nx&CHz M3d/]('xPyb|![@6֓cRudi/@"}Ov1AؼUBӂE5zbZ_ IhZn7kiu_eCX(L a7s# ?A+dB'90gOl]Ll]Lve3fڕʹ+iW6ӮlLꙆcgLR /K% $k%$Kn/Ge=m3K?yDߙfФ:y΢Ω77F'YLSŞUi;hϣa0'Fv v =HO10σy 9 !g!,?0 I>e4|ZŸBS`8 ܃V{sǗ8 cSD.U g(Bùç j\ru?#1P= !0ᇉr 3R \ka!*.ZX9-̽vyu'HQBjwP{CSP8`bNjko7AcvkoG^p;OjWQj WRj{_9o =yh<HA0GECv+`x6xVʷQ((<x f).;ʷ2V j-P=-P=-HƷ-aT& f`@'!_AH! rqw"s~Tn9mUp0; ˝&#u`:0'QD;^?@ƅ[]uI5M~t YCPԜ ѩD-0J`F%ޯXE#0S`xJ?GJv+BB |UNVA[`c s, s U;B!-NbnqX|"!iua#㇧*x*q#5ȗCAxxg?ԆD\NOυeOs1xvr;Jd@hKZumC9S;M(ђm 4Ms6=4[ioӬܦۦQk@ˀ殀ڀfa@h@Ӫ]:#rj7P|ݸ)! ߸&$xykB}[Eg |Ywx\Jڌd8 ^EeͲZYJV)s˜2 ɋjR.%rNz4@nt)[BaGlN<a9֠1#[i9^;|Z-a{+~L^ k嶱 ƱEc [;]5Q WVZt|sBoL`}4teDe5M<eq?3 ˌ`&6 @L2*?x endstream endobj 65 0 obj 26555 endobj 66 0 obj <> endobj 67 0 obj <> stream x]K0 )Cʢ5@I@YV"ѱ}qw6).9Ǐ9^!36vёfʊ{zܗx;qʊȉiӍ)+]?؞1M-K^fuK󹙾4XHKxN)S̭ Qڱi טru:0|i6s 5),v>@; ]Wm ]3~aL~弅P;7ƼBoz'=W)9ooo<z/u_((?bo`!K2䯅}-h#גK~A%,RĒ˼㌬/{?-#M~&ώVb##žߏ?Hrʏ3rPǫГ߃ߓ߃+?ȓ?+(?''?|N.F\W'ȣ#wwi'd7/g^ endstream endobj 68 0 obj <> endobj 69 0 obj <> stream xy|[ŵ8>sj-Wm-tX^$Y-8NbvN$rl'!,){&[_ m ayRA١yKڗRbwft児~~$399sk{h`K i0bnSW7]7 Bظn될?O7lʨ#r#zU !TAPQ2+!ݸib,>Z_ו_s$_B !Ma=$ BIR?{3DDaP9I3,'+*uFɜmsrN} p3C`"B1 0gNN ļIGpvsA૱ނbt%].) 6(*W2Ph>z!H/cr'b!.ޏk@ay!وЏcȇD ]nB؝K~( x7$Otʶr%+P=NՉ?N^'Y=3 9EyL$'?N~T.7_7џ6瓡It&_O~h /ď0ߑ Y"h} (Ѓ}zy Gp^,`:kۘ[˸2gA9\E܌WG[-eH֣tzC41`==x/>ϼĮs_$'DdF/C@a%ch?:R9FV\Rv{?wddW%K>|/83 7 f镨 ]+w Pz{|܇o?$ea[ا8̉m˓ɟNNK6$ۓ`|kZ@jcx> /ëq'P_/?1s$c7'\b]Ɏ[&$AU&$mEA/9*Fi,a'tp÷σQ!| `艟)g2`=c^bf~Ϯdecf沸%*dHf*sIf`#Hړs'_L~oع \.=u)R3 +:H>| 3}5O0Hx ~f}|'2>&=>Y\<<ϼL|ְ1k` of8r1f܇ײE%mrnIsp ny Ž8:W O_ߝ3G0(HOpǰL D{}= t"وІ71A2~3Ct=_D[_^y>aٹo7s>1>߀^\vgƸry҅KmP{9#A6pAe}ϖ®v^'u|42_ u μzOka 1aНнȋ~!7X~~bOppbrd Ifr- g?A7p'ٛ4JC?D*YڸEP"˱bYlYV/ d.Ucܛܿq?~{4'?w׳qv e@&sXos?8]]dm"i<9cL6ΉN}|SNݏ/k_%:yVKo-nc.Ы 4,)'@f,XZ:NڗУU@c,2o`a(LOx/>=$?NZl>U+W,_ڲ1:fnuUeEY8* y\9vՒi6 ^dUJ\Ʊ FN!Lp>AtAF׌΄Y gI05)NļPj#$-x6o\i_BqGZH\Bhl\ $pАXu㮆@o_SSh: ٞ}8{PAJ-*a,hHX= HlnCWwuY[ $p:O lzp¾]7hm_:-v?]Ⱦe: čm,,=Iuػmf@2 ;w-7Y#d(A< $!luA',mW- Zq%vO{ׂ9h`] ܧKF;MQV'XĤGFNy` $U]*|1Jt2$TjO'dGu{4;KʑIDfAyOB"zXH<. 2<Ljmf!s"Z TZ@k#H L')9.\EJ%S;= OP%3M,SűTy OmBîNinWJ+$,aoc팄1v$7U$4 .~TGJE sq*lW\߱h8iEfR7鹳ҳB9Ӽr]]_Ohr\67)w$K/$t@S! 0]h+ AX$Za'2rA^I(4R\ kNTՆ,j\^OCenM(;b5(oTHtZH=MXs-QBR_~H䷶uڻ#?Um 9^Q|( Ȯ}hJ5 s`b?CRmq&D `:sRPObE!) vPvhB݅pڵ#,չk49#],6kWCgz&ޞXxC;F\ Ay ׮l[s~>/i.3KRIf 0(i0-hMň)yeRy<̓O @G`(\!W<wEj826;U|ſ?Qdf)e~Ek&j]!8tJ`ek$p;P!B+~u4?+:HgFKE#ZpGH"Bx ]N׽1QW $\rh,s2;D0lJƦԏ&w"pI^2mOj >M|Ҙ ?ô!chP"~#c\9[T0Lltn: /ǍU~?Fqwlt \iKT~f *8\]-fS)˔mE>e}^lԋ)Ey^u{N{~ wě+ ,R^*KȂP!WW*TZRQ^y`EKBS40#e]]MQq['OⵗݽgχXwUQQա뷖T߳vi}(XV[P['s".= w' V,+pg{ʣQ4PܤT  ȏȿ,W"4eFh4…n.Q +BWQ=fԮ5@Y D1#r( qqZ4ʜkIcV}MxY%b.TUX+OVV{VOEE7G΢<UtD(7(A~Gc.ř4I5Uƪ*C' BpԱopds+RRXiC)]npIY9;u.y+>븸%~M厂*/!K~lc v~l..j||ݓ-y9FWu 슝ϒ3?$M\҈6/¢Y09j]3i-E!SVqK]~Зd|HbIf926Ha$,^<0,KvC#V}!m@ɃH 4Y̴g3sܔU;CLiaXBk*b`/jԲg!׌|GC)N3k'(r4D7;Rsс~);$N`['/K Hq51TFy՘U>|uϊ(z=;>->2dPcLRC ^2(!P*Vz*мHBo~Yhah5woQjT!M8;ìɱ8oe.;ZBlHF_ueiaH]&E%?R]NE*U 1)ߺ"bZԒ*^DZ?ea`塆jE-/R]jQWl"Up6\p740 zr# byY{5ld_Ļ]],qgS"hΑ~ 0Oü5*eVRe: GRgP.eK^sy"-;q!'גD8֐h`sGt]2%D&c1?3`MJ6SDT jm I@$ u?^)EHXR'@zkT;d?Z 06$&4l@ɉ I q97w`ߔb$_z(®KU9JHvloYԀ]PWmPgO0l]ʳo\[V_WEWԘ\J^ȭd~t~Μܛ5]thuѦhAI~A ˒omM5v蠨RT{؋9ȋգQ_9-9H7k}?3fs~їcrzϭ̽=G?ݚ&+DVGH,jȰM%s\ 8IC)s"},:nvbXDv 9eN:4G@ѺDj yre)8 e߳0Jf93l>68:EI9M&$j5Wp8P_rb7g1 oF O:D+Jưߜ4i<>|Tz:|GT @L!w`WJV9E.8\ϩ^?RGA#_N~o^!Ꮱ;{6緮-6W?ҦMguom;6g)\6QÀoa+7|#QZ2v86ٳ Gc+@|` &'ܮ1O^v}P~𳂣ABvH@@vP8g*jۭiVh~WE^¡#DU` pXlm6Qj@<1c|WcZR*'WSsXD`6wF$x4z&>9SID pm64_ܾBGz w`a89ܹ5.\_WsCx/E9"# ́r-UN*S6d8T QwE$,":ܑ{&4(3$2I+Eb,⍗`6b{R⻗閻ܵ?xld *[XWjli\~ 鉴0S"Vxqq9brifITf⦯gٗyVGF4`(bAʍt6ғ`0\F?eFp.c.SmV5R ʰU٩W66%®VW'rp}Ne<:A%M~jO5?G2L j i) 3:L<70'4%@!lE>j  $@TT\gkvV}Xt^j)F‹H01$=wopJ %z)43*O#㻙Y޵~d`Χ2_ɵ/[_3qӑ̣/G||*?BQm`Ud\^ì.0vg.O! 0гIlfNX~%<99p(6!awUQ# 2١Z!X:܁D{UZe)2L+&'n!6y Т_sz?+v~ku+Gt> ˓sC o̳xs#D؃A1YB- VseͿ~gWFE^LU2p ZB,4 Aܻ.y5O{GkY%\Jks"JAuV)3xG\l4\wǂPUݩfգaUސ㢥%Ƚ%Dr#ΒP S2B,ą2G6Q)mudBS?I)hGuI" A;Jhl98`H!g]F?D͉`o'dwl쇌 0GP0eK(gbm;h~LSrږ$d#% H LYܔA]PUU[lv][eym=Z'j7'+ː3tV|gY}Ǫ"1ڸ)k睢Wۜm{;n͋66E6Tu~s/* 49`zZ)n)!Plu +Uvrje>}bT[+fY4F fq#53iG(j(~XTN:\ "acm~׳ ِ`6b-RJx3 VZ Hx"Z3Ԑ4dF[ (~y] PpEP.{ JKKJBIO5*ptpډ8|naj{ k/~QYGd2g>A=L3t6ԗhkN 0"&w Y_we?|h| nJG3_* sFrhqGeUɄXry)GwNymOoW)-k2vg{Nl75#K5zYuWWw5gVgW./ޥWg,o6򠲹,^T+x zsrIsӏu֗d9./稪m.D/aKp=apa{"FԵk{  !?P6o&'}]SCTJGZPD\j }Hw@zAG@QfPv8Y*m<Ŭ$M]1+X+*+M7Eyw(T0Ӧhe%OOYE $.kV&OU q*W^)gŚ\U!n+%U {#Ȏ> "EXbV.fef .m9:ghU PdK\&|JqSRs3p'k*A]VddEJnVbgVfP j+ %lӈ+d.]n v@w'|G."(ARL.gaTcNI1N&&@?XTS~Q<(Nc&-$>* G8=|erpy^+IJ)S}c-W=:b*U8vpZ>.T2yQ Q ueS M] ԲRw! t7䧊״Gx>$ tMDDcl ,Əg X|'~o᜔4I+Y:?&RgICR R1z?)z|JCWi PM?l'r'X4^'hr,Fo FY#[Hia/?  =uJ;Kr> 2l6x9,-@\vg(F?VW'+G'/0薾 zzGz3^WU3%F-3$3\ԋ2[ԍ#O2j#+*';fWL'M4ML8{m C CKDžmϳWع|Cc)"-r' r"gjL bᨤ"7}BZ+6,x%+ ( X]9,E@-i-r}Jt+>)%jLe+c]yf>M/[!zsɉ#|͸j#G;ͩ;xZ5 <!gx)]KhEz,k_}aX|%FK~>}@hZ\*6l?:&.}yz?G4x[YpV~w6鱦hvv0;XĥWX&7` W_$Ǹa8P!#JaRTwdxB y/ OO IK|W_wy.rn)ߠZ/cx-F+h*@nzuKN)R=xQѪ'+(o:Pl s6YodeJf)YjYVޕ_zDq5NYCfP]S[i L9@ w;q2څ|X_^Ƨpyaya#PJ_IN(Dn2=8 w7.Ο<;~g]5sxd7{2R\<ћ/᰾pX߬$7q z]a}-K8uZpX_!Nq5+u >x&BR9T2P\C+W=uD]$CA 'tRi"sz_a]SLZ¡?n3!I8,RpR2[iJ84Dp8sH Vˁ a7H<-x_i}>C}aE5;<ң!!P>>@ z[O)}zۥZLg~Z.7]2ݐH_+%@.KK=!2k?*4Qz59cj@p쇞m3c$}&ԛ|{)Y>{]o"i(!i}=Г4ޙkM6@w>mY u]4o!)Y0k&"1u,9LIezFJ5wRJz/FOGF?I6A{_;g_zCHdi 1Dy}pzN(8A!ڗ%9MwFHML7Z6AY":Z¹Joь9Ju 7CPR%AFtqM6*)\# k uo\…E gD_Dki=TN; )O5WO{WG[v:id[g-mJI&Z?{L)y(ؔdnm^Ri>̤wJz6@毣j;6_qi\q1lh~nI9\KTk( Q͞9]*N3Yҽ^i)3.u}4MQ? }5$YZR|iMg:/Rd`=0CnS3EzI҃:s^Z Zrf\:u]KR5S O!5]/GZJ)ChDH~ʑ&7C~Nfz{ |c)Y-[6C|OKIWJ1N_lF0Duзu}cvzON1J=J=Cp)O "g% 9!;2FS\6UfJ$"*Ge`} κɘ. }cÕcńc’x_|@|k''+t uJ!BLXBr>hW\UBPR$ {6lc[c-=ailxowr|To 6%EUa!IϺ`|P7+=TE+J)ʁئ 8(a gp(6zu.Ƿ 0Ƿu t ͱ`φ>h5(t 궁 (y_/ lj1{k[vP=',Y@Z+buЅuL% =z=un? l"0:]}`oZJb=_o] Cq!iKoPlf[ ў `PZݏÜCuhgl 9-84_ m۶h4uEB66 ?4f]j:bۊHwl- 1diƅu+[ - h-ohXҰtVU3HF ~XD6eyz@ mo!-ŷR) sD\m$Dޞu>޵a #Q$C][cB|-)2۳:C(&z؀3 ޻]X?4/X jNYeHC7P`S ;SS1ո֮-]k{ۃU}A:x: I@hغ \Y@vuw9!@ovgSPJ2i="o)ѢI3uHto.@aٌ|4\Wva eC@4ߴn[{@!8}d_jH1BP׺5&z.O5Xcm,M螡jRaՊ:!(WF* `8T!3\\@XQZ!TUUigup3TH݇%uTL6k -X g?]c=: .DK3Ւkٗ w| %_~ %_~ %_pNZY$7Ns5Qm2x~ygfKOR1gSLSJ}3ם]U:щIglmug3qszDk[^tp.RdFp1?S͒m{?:؀z@Pw#_b}G4ҟg9Ƀ5%o֙ٓ=4Cx@DĈJSrڝ.S ՠ0+ eKuȈ7d@@rp88Fr_Fʫ),]YR`J/m)~J"WKUR'l)^!VkD8WR,HYQgNs0̒Z!`/@ *pR)ϠgP3JY9{$~62 u"{% VwCn<ZP>t}` m{j ߆oL8Ϙng`||~}hCiށ@w;teWQ#$~WS_WzzP=yt |(DC0H.b-k us!Mj6@*)d#e͆J0 ׳yʃTMy!兔 - . ҰˆJ%VqJHQIY* J:299<֎`>R\BG.e+J l6Kye2'A䜬MRqwuL9lk`40 Gl5  ȑH9(ꈷgW1q#c2^c{c Gkqn̩*( Y-L - aw.~|M]s!,?f+ k ̇! a?ņ!C&(v&mijGj p!̇L/&0e>`<>}@0 13w:1QX=BXNݑY7˜Mӿ} P=^,B~'@?2`l{@P _C^` Z!F3}r"\rf` Vm4`3 9z.r#FyāGxāG88qq#<#q>q|'|'NāOāO88aF,biN.4\:Χ99+V%!\@8D9Z(ZS ZS pjZS pjN-S pj' |:nzZ `-9<i9mi ΦrR!!tĵ #2֏!?⪇ Ww(E[&gU式8Es:S礢z=u= rU&IgK{~өV+*{=NŞYiTJRT+JS2J4 e$s$(3$d/|ѿeP3L- mfW͉PZ! (V/[yㄱ54'++nej $i^""> " "g,rߥ&JYz?)BhP#W'4yEl "^0ǀ4>@E l >S"z!viǃC["I endstream endobj 70 0 obj 18121 endobj 71 0 obj <> endobj 72 0 obj <> stream x]n0 mn45,m`o΀Trjf B[vc᛿͡//۫|׷i"(D L!emϭuS d30O+y""\:8*}h%𬈵DKyg19#VGD{f 8:%P#s|TbF7&t %?!Iה:CfT#F 1kb_5 +׊t gg9OG}ǎ~gr'̢ endstream endobj 73 0 obj <> endobj 74 0 obj <> stream xkAƟݤI+j$'-HH=J@ I#& .W&ē yk{$)@ev~33 B˧>Ŕd%ӧ#>#>Ŏd%ӧxXmG_TE>8  B [P*Z|g#]Si0 sǍը0JמccX|\ѫ__q0Q"}ٷJ-ۯv] Gˆjv9ϕmEzwܘ+O endstream endobj 75 0 obj 565 endobj 76 0 obj <> endobj 77 0 obj <> stream x]j0z= ۹CI diX}֊Bf>iVܾ`;L0xrsX"8zRe۴D9Zxs.;;dO#Νn'jp8=& uۧ(_sU壊 h,U] ԗK?o' KdUO7j ؅YsqO=1č~my endstream endobj 78 0 obj <> endobj 79 0 obj <> stream xy@8>AZ@$I8 h1; 1&>c;njr'$4$}4snsisWζvR7Mиm^~ofW8~+P/ڊ.A&|!{^"O-j@@P=c":k;L VrsG;'%0T4Э~ 4p> z ~ 3!f;0&Qc`TA݁DϠ?gXm؅w|~K@EQ3E(FBm>c } R -lϕf&hx3rVǸ&wN54}ZvJanFۡG#W]̯0&+p+Ŀ`2ǘV6e7{؏ؿs6n1oŷǟx}Iq=?Ep,m?c?@W/Ячh3@}G[=nˠ'k%GA|/_013%"fyyy=!쿹 Q Ebboȝhno'Raź3a\DhjG]a%^ +Uc-pg`7#8gKjN_/|x/Y!0~cL:j.S0eL%S3EL#s|Z L/ss5s s+s/,+o?Xl kae fv3{!{9eٟe9\/^>TŊVEۊ)nWW.Q]3+ `90-^P^ =;6s]\_<_Q<f딹;a742C{q$_'bXnis#HiY>Qf>XO|BiY BO1? kZ.Ól>] +g29̟P!HGsd"bN_B$>sĦRleч8}򷓽lBt}ރro=H\"5>21"[S2Ja (s=gc/ȉjȄeb"+/'=ﳿO`?aB#ct!a&t;-e. m f~ZbyY˖66,U*JcEH8?/ d|^p9v5b6y>5EըUJ2շ ٭\o޼|ABیaO/3,b%E(TR*yU s|u>at1 /0M#x0ǾNƭœuCsZ렽}:mSiuza[5cSATj8W7g#$ 9mÍKWͩszGjZ ǝ:> ZChd %u&9G!Z@ M '& P)#24^CaMm_AIa C"vG)J?)" a)$< PD4^70}=/>Ը 5WD`=2Wh DZ܏Hyi%99iI`2gz1 xyκalN)YK^%jvai1)l*O͵X'#CL&U)Ü))'w4 |<٬xfɓ}MWuZ4RX f6=4=-POjj\x({ {PϜ䌎L9\u3tbu~LTa}56oG|3TY^?IW0ظLSVNxNM) m1/#U-٘?+?B^hf9}V"Z /V@Z Kr',fƒKBd TOsr4 f{d_;=0jJ̟o.y\tNKS?)n p#gU:iP?d}>|}"⬳W`\Ѵj?heAުQ6RL@bh! ϨisTDhr4G0idF#4' C5hʠ!]J`Vxet{!Q|T.\Ÿ\̏Wxx% QcBLT ;L<k8 Yе܅Z|9z@OaoZBZE,$` OpTY8J07$t+WH=ǀ'ZP|QF=n (-c+(!J7㒢BUJ,ȱ5:-LGjfn'!f3"YMq( tûE. e2+SاRM Py8F~;?#& dG&qCa2Q s㫽؛ 3qeSc'-c8?=ۡ}Ji(L~,iX g%78VZZRRy*Ռ$: JL!acٹ[VW(/=2rOp"4?V_ VoΊgVmZRsVzz|)^7ϩ 8Nh {5bb)DV"[y%[X$5%gl/yѝrNPS^;j[ =GGGT:BFcUDpUEanc8r ?b^a^e"/Z1yx?d%e*i%=d^h]@GHޕFwƢuѭQ.:ºEsRUeaS(<+0-̅Gb`ȦDxaE *O8+:XRZ (5jݡ5*[TUaBUg'do*bOQ7>fJ{y@WC67"7Tθ[48ࡷ΁Cs!| a{DR^3:ѲVU xi"JރS,ȈJ>WR .ׇC!_|[<\0;.xIa DGYA֮.%(-ub+B(/:2 G~,-t]*gIo-w|Q(5_9;TR_y9q5y=WX$ SЛƑOH22  !hLZ󂎏E:xtX[f7]X qt$+M@l GkIi8ɖ֭pm3bJ9QW9úYd UsTW0? ~\W{eCus2`Z(;wKٮZp{l2h e_w Cɿvl|I #.^QdYBPyU~%-?iy=/C&4 cj{=5V6뷱?]j& oKcVB ̛[?DcޕzHna3EMnȒB$UE\[1Uf. mܺZ&ZSQhcl0ʅqA;I<, &6ZXZ;Xڣڷil\[v=P7B|%Yx%V;yJZ%L.kEB4 j<%BBƟ@bV'N@nj+!"n@ڨ[! v%"  +֮ՙ!P"2ȤoRIPHXZ--dчh仢&5RLO|3V&o*P^fim嫒PVtMm֊YYqNםW*u9|Q,?2 0{e^K(eP`.-v~]-N b?G? IxKh쓖t}LKȂ'x5ߒ\IM6j 2RmƲ98Eo] ljy8Dn('&Y9E h#_?"(؛#1'M2Gҕ>V\Ք |uu{',$p-ʨz,? 8)\7=eM^/2j0!bnJMV!3Nb_:6ޭfU۠UFAh;z#= Q!cr/&g y:"C1Sy$Բwj$bVlD9住/'6WbS9L'aPZ eGQ6eIkt*b$q}zۓGxp=?˺mh/|E _*lo\E r 1ǁ㢘gcR|={y{(W]qC[%XzSG$%&lr9&B,+u!te_5B~Z2"RU| ^yVw0yQbV=R 8Dc! zNRAVȾٲL%N9e&Q겆:\uT^PWX.g=e鎋HEBַ Z[OrR|,u3԰4[c-!.Gu̱:R8KiղmY,^Gyy5RdFaQ/s ܎){ ¯c| U shW7TfN;0o~3~7hyC,-?sA1čTPh3Q,;IX!bRѢ_Qю@^Ⱦ#d2E@iMvlafAhѡ"F,j,:ZĶ .FTJpY.">"t"YR~MJbSk'Z,3;q',S?@r|>Ouʤ>rvˍ9 $v"ހͽ!YT*ȁ50+5s;Ke]Ke@uՙg7Zsbyr &.a_lO˱KΞ, *IcAcA&@NQ1rT&C`?|Ľiȵ̳%OƩ0!ڋ*b樯0fvfLɁd D0) uL&ٵ|hG &Ŕ0]Dj4 vX|"T6#lT%{C:85/}ǡo1RQo/j"j66 ?TVgt3+%hdqf+lRqNex?on^: ]R7h~&N޳͢ 1u6~-Kj+uY6N7z&BuT[NaB i94VSup RKc'y̏Oc􍊞LOǭxGqkV8:q׏${Bc!3OэZ!@2KURD-^[o֦%TZ&bJ%UP9Gv{ K|TZM_J\wbc*<땛vaw۲5t-:.+{mI`a}jMW+\# ݒC=ЍwBags <w'Ї˜i+*+ oo l] +cPqm>ԌDnD a5Đw6 @=ȷ=Ne .VA5ZlXtu&^-贼^|RkmuX4*AQ|)\AtbN$Uvhk&sK2TI6>F&W:W^rXDzR\NONX[k/^S)娨TX9 9DnƖ@Ө߰౷oǾ[k'z5C]v$?;Б;dz^eiIz3ͦ|W^IS(*&ˑ4ϴ0tp\Յ].g++koumudL=Lh't5iֆT AI&i^0*;5XE+}rӲĄ|s7j@NP(?O;ÈFde'ƃd1S{?.btAM@ɒ2, Y8 C`d hh4z ݆>Bnl7`^2tYf"6Ctƈ'peH'R :A $KGHu?vf(OL<_˗X(-oIOv o$)u#lRͺҀ?wK=z/ILߝΦ0(դK#طY>_كE-E' xe%?F&JQYސOm@EN :k~if*=pmוgx^_jFrA07 H948p`Y: -6J% $#R\!FVq.s||=e~h~ ed7WȆf0`"т"̓1e*Ӛŏf^S3V"H"(] SXY 0J2VtO- s) =2˜-72x0Hʴ=D6M2]"LOYQ(d6cc-ɍc1KW/M+_q O|_P775r0o-_yƈ+l`[rţpVb*rDLe0ZƦ+F07@'jWҾxJ=܎O;s,BlJk!=5N_~۰pA'OSVE֟濆мn}=O朻ѽ<y^+|H3;sX`qQ]*u람?t2r2J2,P0 4(䔠zsO3R G&_!!1>R`\oH' ܱ`6%Ja!Vra\$sApRV>9/w|A+咔\CavnټLSpgkM&Z#xq"oļAQҚ DMhz36^'Ĝ`1d[>rH2.`|WR&Œv|E9~lK yOM_qO4 Pڍ.@` @]z k@O㵫DMĒnZ7"b6s9C*Kv|y8tT /RMQ{Z GHݴm_0P(^rOe Am ȩ&ujH}&)mڡa#<0Hi(FLFLRM kצMCo-&t|iMZ1g' VBr|&^xYy4֗`kϬ'xsl?0ُٹ6Lw>*D" w]E36ūD2ݚd4|+Yׄ7K0v*]W49VM7e2Y1Y1wO؊cU߀9dQqq$Z4dƫEj\,o` "E>f(#S5kT ?ռZsiLl݆9#n1C-Fc1vwoWR>VuwJOtOrsh1D?-!x;@5I'7@DBOƉ ןQQ9G {P{헫ԗes,JZ%] Φ%!BlԡTzsOͺ-\wn{WY  ejwVli_pͭ;_̡Y7cJ{^-ן/]]M/L~(D>GN=l&3Q.^ n/ף#9.c\LQrBf׽.:ףViTUȝpuEsVx9Ӆp'30fF3W|*A\)4>Wuh{6eM2 Ƚ-z)OAQŨdȤ㢎nߖ Nw[d^W&Ф _&4ށx5CU銑@Lqc/H6oوN;lO`AhN҃^ ![ɈL2}T W:CQY$9B-II9'.I,\vAI)_=g@dd=wO{ΖwmbS|eG>gY=߶h~3+5/fUl7 ?3||y롒W*?UkjU'j 4zoO:'02Z=(>W. Ѓ[F? r^~t?Q7>ڋg!tԎq V+c*jD}[:'5=nje O5߷򏴗;.qveܒO~Cр8Q3'հHn ͟~ڊJc,e%>B 02@v3VB нLe3eX\q2g2C2p1G2G읚)0F2| <{2![dXRRߑa%rɰ Wʰ2Azb|֡ÕSYd8$éنɰd0meC%)t+2̡BVBŒ.ʳ)"b9[a.,=Vv,0c̯CM{_ Zdڴ^&0d Zua~mw0̯g2 k{Oa~9Mad3Da-+{2 c =[!Kw2 }J4i{2 >DaҽBPf2&0O]_0bP8Гa'VBE2 gzMo?)$t eK%|>QF$@ ^u 2hB/ga ĺi{+MuP,' =8Cm +(^)PN~?t1- 9}^lZ? kS˦kxtu@|#{zH#Tϯ03@q?i)dPD~RE$U(ێA|+gmDcr d[)~yWI@IߙUn4mYmhZ=5YvyJhk [OaM;@3҃S##'W@7f]3/Ag')[NuP$Oq}'ҖG(=i2&cb#ڡFDHi`Y'| 7@zx.s ,x^ s|&4UM G2] =Tyzwze rQr mIz<0Clu%>J#FZ^vIuҽtNqf:@GRJ%y#g0i3wҶGzܞ9It/*wΐ_gIky6X/Σ4ZR uύuE(Ccc3)J=$[Xhe(JAY|)cؑAJF!rb2\ Z%4QH^3&"i{OgW[{дSXؔ$6ۓmNlz6P4&ذ 7Ah>< B͆ ²e}-ݝ’έ:mۓ*fd 3+:{pyT,nM%g(bFqh<+Q6 Mmz ۹C$wwb˦~h/M EvCD0b#$ұ|'jC~3i9o}kQ X:=[l[G@[pol.0U=Mx\ۦ-} ]#t}[@"vtt">${٧؇(WO7o rA66nc%Dv(ҧ@TVHf{tq'!f7k8FfB¯ pvC`DZ!(؄Lկ(=z:Jσ_CP'SկA`|+mE!l+{uw%ܦ8g<κ(Qj4l& 2L n|A|.:SFw1Ԙ8$6T^Scd0v;;4h;Lc{wMkE{!@Y! 5!ZWnA4A444r @ `6,P%-0h5,HZH֚ZV*`A`JSBM%(a 4jܘ] 6&ww|%7I~B   >jࣄI˃m`(a9؝!f)z!,Jx's‹~ly#u'!C4B4wB 4VA>S0%S0%(`JPL L ))A1+C])B 05@1505@15(SS`jS0S0)8`SLqLq))N1S0Sb8`8")")B1ESb`LL)Q3 ;!nv+ :\ a7=n)C Xc`9F,1rb998c1q p(_?, ['$o~^0^^eb". ^ق90U0_AxE8^#L³}/a0ڢfSO8 +|AyEO/"6SfO_f~~cռ?XHp > endobj 82 0 obj <> stream x]Mn0Ft DBH) RQi@̐" YpzfV"3yKjG]"޴n5+zH%^/~Ezh t:/0\L7yy Ab[GYu}/," Bй<5s3@HUKeݺ:P`s BUU`{q%N6EFQ\sg{8=!)9|9F~d'3}oŽ22G)?ASzx $x&c ղ%?#a_ ?쥼?*Wx&KPsx8{?##Z7.44'8!  endstream endobj 83 0 obj <> endobj 84 0 obj <> stream xܼy|SU8~ι7s͞4mI۴tRveD ( ]q)-`@TFQu^wq;vg7iy߿$眻sɺ5W/A*Q_tՋZVA/fM>?Eٰt+zb޽g"$z " 4.|ɂ1EP$Grh;v_nҜ~rբtͺW]`u sPZp咆eppNRzu}B95KVe;u!І+|TJ:hJZeuzdXmv|@0DKbeDEeU2mk\GQ1dwZa 6c~5́ކAϣw;؃85_\+AףW/Xg $K5WxN  o :NN>.< ߋDTm"iޜ B_*<^C3{LcH|LiG$_ӥ;&f!ݡ >ʙr͹3????)J*F 9h< O;Н}aF~}/W@ow]wb?~sBƛ>|?~WI6%5 {''5%,!fry>@#/tT<-kRtt@+FD6f4AR@J/=d6¯nsO ›(DRNAq;@oGjd,pR\|8MKڄA܊yَ`#Cw@G$jI~-ZFz  /@;hB{G1G,t?(E6TBΟ&} zo=#S%ȃA/@&$;ZNJ jL-oG7$MfQtEI@P~|{s Cy2D&[LbJ /П$,#5 GR/R 5wt'u\C.PiTm~Sܯͨwʡ)^@ R23HZ*6f-:JYb{0e2ȟwh 7 Zs%` I] z(:~vYvf9X Z7mjz>ܒƆQtmM*YUYJp(r:6l2:VQ Jh`T3 C2_ .jAӘK拧qÙKLp&?r&f ʔr~-~.gM -.oP;EV\uy ׇs}cY:nwHh7/QC %J,Շ"HN-}6Ѓ>*غ`qߤ3[[^oWYin^_؇M}ژx j'mcp ovrJOޖe1bsfQ gbܖ>˦3ps}muP9ۻ{t̋z}W%1{ohCG Rx%Ve Oo/]1aCS6zvXSdoz{-CF;e」l)+= <b`1O)#B}"z2/R+ԢEp|0\շpyy~/[' I^ڲ" !c0|4! b=UVzMɿI3Ხ83ˣP2yfΡ~c]}dptȖ###S,4ӲfC>l^R811yLw~.׎+B}晔!ģsFN*3U}t~Rg؂1}¾K?^YJ,~;إKtOKA6Wqɱ1vz{1{d[9{ |խ)s[r\JP!?>O5 i3 &cBJFZ'PN'2(-6EY6pFƊm)l>//x[ jL+w 5xW2" I G~+Vlkm=OjCm6Cv~QT$KVV^l阚d4v3{9xp )WN [bG^w^wH-q0fĝnԍ<_(*͌)S{sgnq9}Ono;tOwލ#_ӽ(7yBDJB1b7#®X<Ҁp!#OxىIaODzKo Rf .ORba ^OBMaW@8F(`RնzT\ݨj%>n)jNΓ[J'K%R[4vs="rB98x5b>fOw=ݨ;Kȓ _vJ?dd JRF*"r,]{a+WQOTW\'[[y܏mȱM &w,]߹HS@ YVg#T؊i捦M7?MWȩ44DUx#4'Pcdbd^;ywiXP6]gHLC(U^UqpF1p|)c UtFE8G*z,xGx[mIoJy  { C$yUU Gh]8],BYK$rFXz@ @G_(@CtAf3 kE'3u1(T4PgOoӰS n&2mȧJÖ [15Lo H*/`0ho 55ԩj.:=qjMUTv!D ]iF1w Y4dXj ʑaE`R~CM'n1jެT]eewMX^* *t׶yVJҖ{7Llwz?9fe"uڑwu_Y11+milvF` mm N 7UAx= 9vD9Z*VTd9Kzw.k)ffӃwӏOA6,pǨPJse>P\2L.q ġ^/S4.GTCIX&aNgрlLjp ށY8# `8L\ rr0`luFYdRjP𣮻2RA9Ymd]?b+6xnt_{mbxc%oOhTԮZ€jc2!F#p,nFӓ))F|m'#rySܳ8rkCJ&V@Waϖ21)ŐXZ6+JTw"/Ũl+h1JU(c2c)1UXV{ Q1ATO*(ھ9`Ȟ8bBR)-,-RQ$beSl 6{l).8,G͏ b}2x̹uHΌh(5.Ft6Vs)YF}UtՂ#<SK YŖoDAQAO[s*G6XJjtO+֙gxܣ]qO=G9]IAה,ng5ue[Pj|WGf-{K"kz,f/- E[K! J6^~Rr;zyL\uhr.lrč4ZLKn;]nFc2m6F,5( %:S&O %8iSi|F822BJ`UoK3&U$lC!Jm+CA3]a*r1DSh2/ YWT Xue܌h?z1t$TAIdbM u CӖ)d*"=rL'.‡I(Ὦ.⪬6YLՊʂ:,n.i.JknJm:"Bevu~}.&(E`06ƬKA'g 7\bFJ/l.e.T9]*lc%ǰSVKC]DTZUTq{nסtDn B|X+_Mxl?5_+nޑr O:Z(y9(i7 5LwVⶸKo# L+݂y5섛bYw2-jpeENȱQ! ^jKK[, @#.)vI]}4Z_WѠ֚u9- ѤF0Ju%hI]nPX kd˦ZMUW9NehwQ?Gx*1߮zXu@uB-I:S[t՚cc%f$tJGV**5ÖgxMėw0r#AxJJkΖafF EX,xbxOj"yfy9HT=BT+SvAm0wBJcnW)Tݡ /~W8;8Tb&$qbx$&" d8?&H.Xa 45W1^A DT1o^L-Ny.x8W˓+M^Ԏ/,wٙˍå ʩF խIӿZB4P;&yaSssSS[oc~=ZD#G<{j⽼J1pyINɏA,= (9>9*) 㝮$0rrsRжqJ+' hT4AN]|`l Aj$* x~Ħe뎃'WyC>kKFi J%Rc5Rksv[6eT& jrre^I W3>bׅƠVXr]AJ` LkK ͠CvϛT UqX E,qɮfuF]2ϭPXM=Vg0AAėE>P`eJZkUuaIDOQq܈'yYL7Q-IӛV NIzћ|}S $GsB{콶7"j)+K>j=i%*Ycw\wH1+93Xw$&dr!FC,OAS~CYAmp*-B|9*(Yqzeਠt͢]sU]LsA[7$]wq}7Lo2H-UB-0+EpIn`)i(sī*~.)F1kO}ޣWJM|ZQ(\Ro6t!u"JlMfQ\P9Uy<:D:7G#3hl(|,pXŌXT⢢&zQ . 0.&*U(Xu**#yxtldx/(-}:>'+`4`=zWth܈*,a(f!~܅{޹2TT1rħ7{ZXr - $ܸ쵍ܮKi0E%C\/XrN$$K@ZA|ӓ}m/'+9motS4CRє{}|+MͩDþ,$ *l$)XUeMDd(CLQ"Kx36X]46[,vD-,{8QekR{]+f:к 0q]:)8ڂtFtb+ydEAvw W ÒZ9/pGOWU$ X bE#dP*0^={!<*DԾrod jʖ;rtWwYq 6kVJx1X6K/)cx͝~ܻ]5Ϟ}tlN?nxSNb.ӁeGd9r#wVC{eTʎK_VY7Zr@6d&ef K) m2KJl/"T "ȍ؋\<>33B:i:E`ryR.}wz)4؆G5UnB? Nlթ5[ߴݺ 3;OZB,)7mkc=iYbbURyJBCDZ H1o+(f1f#̈f86u9]x:fbk2K/-w}W5I}ܯ|Yd%}XrWt`D>ު'V!=3ޒv,i25QMM=w)b{$uPy051p2eZ.6YLvf(R+F9=r"xJX>{O{ ecv~PDUUbP &$Ȅ,qBáܴ{ZC9R*@&Bw:n9YpTח r\-#l)0#fH^r!#NwR(y0: 8YZ5N{XPAգj7N{4>c(`Df@25y!3 ',8B(T N$%!/Rj "#AETdP18< XܙA`E)\([;k0UT 3E ( -{℟bgsvCwHks񸻮,ڲdW?ي+|-0hi8ǯ0naXEۨm)lJ:T;5J**V]yr'+yUթvw{m{LO88:Hi!o)© |Q|05pPPȰRx.CDoJ'Bj2tPBF!Tt6q;2JU4d"!5:hP TTC?+I*{BvzHWPg{5C!l8Njxt: "NЕ'ga,K9s3CcA!$|I687Ov<" 5ñ4L_R(x ujonW߹~JWvYB%֒rKߐ;TAxx[W[_(MϿ귭ۃ^ZIcCk tLį͍N5hVY#%FEcR2ҔFD~n'V`V 6pA[,:U?T^nm~+x6@Ҫ=eȵLLIc^_8Hsni'l .+\] ]yZ"a[]HdIR' sc(*;V!`̏Zi-mNiNm4'htf˸ 16|e®aO]pH E/0BP-|M(bHU.8gNStM,#7jFfjSMi}')xvXٜrQ=M'|2}t>٠a%%ߐ,64uuuiE$SG~w? J5zo:mLWW/So,&vQtD~ÈuveOT>ѧ=!$KTUU4rG7X("%FQ~lG2f1$_sN֘vn2dMOiHrP~ѴrTgW<L`jXEQA$- ܗ;ï]5k^2ש$IE.xdzo8ƝW6SrEnzG`ό}?{pw[,`puܘ{>ԇGqF`)rS?^Q%z y ND+?:~LOLUexZ_X`)Kp*]nHrwIzQ70FD@}*RVbd}!10 ߅BZqSJuy\qŅ]B'!''D'EyȭF]]5 ΂7/p2>ϹPwP\Z70Wڢ%,6^8OxUjP }`.{x\R#;%З6ܲ+^ֹ[E>{!*@[_c %rC_xMuw ÊDEX}~^Æ,*&BRq=g>B#du@DOn>FPp;'崏K6g61A$類DjRy 0 pO $eD1v E ̖?&Wr%rޫ2 zcHOgbǘtA~z%X1rx¡8vHLBu#}~,o9oS,6lrB⸿$%YkJ8+obUbG<jc%1z5DNP~ar ℩Ipq&9擸-ܣq (.K o.* (3͋3ֆt5mbHQClGCKHDϢ{=NzѫVvWKx־Ax[h O*v[J֪f- ֯&!Ymed^VKdHZEK˴,1X:Zk  6o@8Ba/02琝RrId ᗨb(.# HP1/P3>l, S/#S.Ԍ+Ϊ!{D^Ɠ%ػ4(?)T]i4#\U;p@OUR>Meb\p$S҅L#]H(S(}3@cla~'xfʭ$ :>E!χ|4h|> 4Y+_Ԩ6{KDOu l, *1d.N91 /BVPa7sw<>k=]>8bW+KQ2gW+͔U)R4#?qAm裁Y1 nA'!0:ZQZSIT*F[3G_G&_uhVW+:6;!iqƆq്PWKRwUR5`u6eDuW{ T%rT/+Tjh&"*m(d(|ghz +mdd=Vrh6mIR B>ç67 K- Iv8! Qkq@t-%>a޴0(8X:ފs=cXƊ1cɪ_~ݻ31 oj?_sԢH;yj沧wӱX^y2%W^]Y(j~q*6 "Mf=4( s'IH l G~x{F܊YC B>͗MEE(L&тhxDjHS ,t]趛i)eJ<"1ME+ե\a@S]jtܞ+ ߅+gZ t޷ mrH]>PqA^ &paaƈzH:%9Ur@7?!`CM k:Mryq܊׏E4Aoj,L#<_]- LI"Fb*2S6D7I 7 =yܐ7jsg :(ƛ8M;*rУ3``9Ȓ?8-˗bPn y~o!psyEIie]P)\W\GE1cQL\nB.L3J9FQVZ[o08C0 0g Sv2ⰻ=,OC(ibgvqv+)ubߘ*5$glN_&;QHT'*fTyxV*uSxCup' vbT*2# vHaόLq\!QXzTtqHFu6Ua_LC:@{vH !ãFHBJWFa1K]mzʀ;V5fGhU>1>(בT/WF+J9?{3?[ “ ٮ)]o>+#̼ʟ;=oU$э)()zS^Zܘi?/[n)'ss5s+ԫ4՛5ײ[ʶ?~X󐖍ulZ2dj֔Zrkك4{ET5٧ʟïNh^eʏϕ哕TSճ˦ťR8Nզ\.Ֆi&Rn^YlzMy #ШcI$gcz~.HzĿmeqS/f ŀ͑/{[>*_ϗ^Lً>='Dp"-)tW >t}1H ;݃P0-+d֠ۂ.s NPUt*/d?s)eGiҘ͹+1$D!DPoLL[)̧K Sq._r9+K o/hut-- \cӯ0| cTᢣw]t%C.iƀS.?u\r,sɝ&7^~]ʁ17>DNB.|:!d PB\2qr@Y4e7Ib/K5AB * `^y.q<S`>& wEFI1`ع/b:1Hwh&aTQ1,o}{ɴ)F"LʌsB"1ka|[F,IG/Y&Fw*X]&3*帞Txh\`i(Rc+' }=!705hory̢` g5xktBPA#/4fKS~$~0uR:A1hm|KFz,!יc M]G+Arw J8+sTٸ2Z\:e$?{Nteʏ{BsPKmTm2Lc0U(W{s^V* `*x.q:絖oaN0r 1^#zcʤT5l"eY${vWٳ,aLM|6/ ١Jb0/SVޑL6<@ÃB)X/=- JBaHaF3셕#㥖{BGHk;cf rΫXh U{o^3yf9 M6'Fu^0{&-^*ɵ-WNyb޽xW?O91enӉW@1/R IĔQ@'sKumkY]=m{+U%SmIIIooDMRMcD:SBԛpyyHI{}8&gZ㱈ǁ~ajvH6TCXNͫwi66~__t Y/ J̀h # v24#c3#Pw7ctqr~q4`x6p8$ n^) c1*lL:&kz.bMΚqAMkjzhtk-帀]25H,,njG2 VQ ilr֙ܓ{vR>N̈́02ezK9 P7j(;?7([ +p.%mQ(09n?VIIm{ۮnwt9.wPtE"bn{]G ;82o,tM#bca֛' ':"-Bjˣ2ɲg8K(K{]m5fbͼ&Ky5yʩBGq$&xl5bK*|U!éBj[pI".82XWL6 ~8A\{A*I ¤B8]YP) ӗk٪kPayϮ.ͬ\jÉ@iܻ'>:dT 2 #ִ/6Ԏ%?$)5*oNn0/eo8c? jc&ۢ8i*9NZ#HQ( HMblҢhT=KNDǜ{ι}l%-@s wڞsO۳4I&W4}w#>9>#4Ky9GhߦFs"PO9giގ {sٿ2GhNzZNBr~_T`Yf$YF^6R4?,J,N su INdJ*8auc`r!h?Pa<4 KT灟fs'`0 I4W߻-eӮNEKFVhI;|[?2a~lھB5|Jl60Kz_8\#+^y9Hi3Пo )-tDlY/ʔbӈ'+2c)F]! MZ!6O.9;MCKfH'(xh*+l8trd=NાŽe벷=.xMHeȒԉM$y5=u ː!aII2z|̄d)S$WFɪȓ if)w\5Nqt8QliN;,NI,GV3.7Lʈ)¾y Xj e +L!U`Re[sӔy(l$`Y-[UDUJNm]L%V旰ín"GĵU_ĥCFZKEt‚St{/\+NMfM4Y Dᐦiu).eC|&PZ̸ RSKWaTU-J+*Ne_"կ@Z5+eyD>Va:e䮓"0'nsOQ6%<˔\2j`ur#{YR/QSI9z$6e~R1gWZKduTٔ9r:ݟ/ߥtNTn7Z<#řRX {BQ u(K5es:>]/֎YZ?U]/T55}_t( 5o\q##TE!Wut*)/*+c4Za}gs'MY{9~%YJ܊DN<=VQXKhʳFy\ےvʫw)yU-!OBΞ2|;Cu14}'\{.- +]r !vQ`Cw}񓱼^ay8^I=nxgeiex6A@ #3>fsqp:h.4?oN\7Þ3Ff̰xU.{7luCg̸m9V=|cjlZkm=a 7ՙƴ鸆m$.^㣺˃| ?LIp#*ZִgSjm=ofǴ1WϤ`iqײiNLLdAݲ_\f ]ʈ8ZBqIF#da$O7]0s:c$B,SqrSHdHf ne 䇆[v:Y pr8\Dq : զ͌ `,> nL°WZnȴ89b&~B`(L38t G]#s(iWa-L4gɜ 6*4+ d`[[l 2d `sqrRvps:hZϸ&f,p+[S-ps͸çr?zB"GӨhHQ4蔙6l˦)o)>mY 5[f& pdq2eY3 4O동Qa.\X"BV<'TDd=X0lZ/`bΰ]u \7Ь4dB'rgYQ4sTRt1j R(k:JAs'kM՚s"4ꉄy oІ`lKdPis֔Nx+j2F<TtR枅Ua*-Tc8f!;vFj`K IY9HVۘ3@X>' SWĂ \eb:yc$r m(3}t.ár0iظ;:;v{;z̿sɈWa)!}Paˏ5?ŞǷD[=S(mU?R/ 5Zt+p*?TZPiCJ*?TZPiCJ=Tv>ŮŢh'0w"oT+wM2x]am-ڭ 'UO Y٘F[ں`{xOxc~o7e m} XmtTbbf-Qf):U-ا;Cud?Z_&トO_&OY?^%ow CC"!KGž`u<+ރיͣ^\4 KT9GLGߤ.OyETyZ8|ڿ })TOz֠6 - hu {'ЖyYzio+3om;3W==E,*NF { ?R\)uFc0hX[bXjػ?zRʫjL6ׅțz}U-x0^z0?];`> &y;4}Dx©3;I'c" "MHQG,oѦ" `W.p$_\@{tKo87ný=jS`0|P'}S}_,cmkjZG~_LZdzdݕ:ρKaoc^DGK4@̗9pכ[f0kE"h`l'@@w2Ҍc7҈BQ$/څ#&b[E;"hئ l1w`+E b![ թ{x>^QVS`=VsꤺakQI蟇gV>h]]R9#XYF1LXPԒZ| "KlO:QQ0۵ *@6_+ǩ~olO.w.D]..(g/P'ԏRr[k__~'ʽJܚ}+Vm3W;Y vi`NֿSǚ} nvW*ӼWX'VYr?/GEpyT. R).NWSjIoпQy&A6+C.uZ6eDp$!$YK9Me?k%{%X>@IO)iypsSpQ;*Yw~OGyMڻE/ I/K:~#郄0v۠RC- l-`Gt5p.JAd];9 ^Nt֫5+BGA9peڅ7?eZcGdH}w} ;|[||>_o1N[:/^7x*Q|UVܮxFݭ#K=#ō\(:߉)^Ee}T2G@ =R2v⾉b'K&Fy|ch8L\<>t 7 m5#D<=$4vi) ^ֆjx2\fNp>>"64vA@<(W}-bq8-gbE",0̱edU8lKqO}CKuee56zݖb!W7}k^&G-˥~\pƥ;U.5}+aKr`P0 N!s\.8n 86R> endobj 87 0 obj <> stream x]͎0<| IF2菚pRCyR>̹so0'?K?tGh9k?dVL׷sLYkOo2Y=޻i^6xk|臫y=c~fSdum:}>7ӗsz=vv??_cɿsFR;ևfl]Yu{U*9_ڟMQEQudQ^W\߁d]_!ߕoWޑ> endobj 89 0 obj <> stream x|y|[{V[ҕ7Iv]EXb;vLKb;쀉eYElˑd;( -)taIAΆ(I[(ci a++Y&=s9s\  vnogȖ? )+{Oc}ǟ=`. >OW{F#d"4X z;&f#X/ x=X*{v >. x}=/(zz0 اּE{ujd#x։9^P5Z>)~TC)b}??6mDO>q8 188D.>۰섃H?F $ їa@ )cx6þ$5IѷI=6ImUVU(Z*yn mthQPק$WU}8(Yr;ㆸ3MoDqI 9P~ |xI*>y wO?Q Z`Z$<d\\ ?(hF8<J`t`"vs[;F#Wj]-b&6Md#cNUrr*>r=2Z!NFDL kp U#p-\70p ^?wb$%"REIAQ"yosN~ }PVX'M[ǧ_GOGߊN!v<-V7>:x^;3"K$iBEr'[H Qr%_'w9;;r?y)z{*{݂ p$8ƌqHez 5sWy=f5pyWR9Ʌfn>lVh$uz +Z/hYҊ%e% ]yrd/pwHysdeڬh2:FB/ȯswH\֝dx Ye"RfKQnY$TURS<_&ȖHZl"gh!U Xq8Tg"CuxIgO_$$XIJ0-@k@"ںYKUuȺuYG[A~xp\Lj4uMDæY?3v˄.gis]8omIJULOiݓؚŏY"׷&:hֆc`_.ch-BE(r:":Jg̱ؕlutOF_:iTg9>X׊ږ!nJdV.AgQp"\m9kID[vZ;ّ0=͉3u"$3}r{Fg-RXmcˬܾ4ަPԚV>S(.gDži59"d?5 CqT;Ve&^1MQ35~Ѭ,xTX[gcwJccN$:&:b9+RKKarrIn\?&7n:)AƖ֣j:V/I 3-rq.I#(eMYnݬU` VN`wHm۶ *:˗36U|_#_ё}w 6ncEzo,e7|JIwvNP2 O ɫs\a))V MDc4q7I -$zůh!ՎX輔j+btʓtѶo/M-ՔS5k`=^Rt3[__5?>;$lI(G{e$[L_F.!"mRGC(Krs 9s]|A_r Նv LM'YZM$c=HR甛y*:3:mN)y<~ױXz #U<;GRSrdUIo)J'l *0]-0h2H0ixuzřVk\zr^乘l$z4ҿN>j]4'Q9v{Jgˡ/o6$^zA]+7 N㚵%WM躢rM=i16_g,\aڒͤZ^'4UBɕ^!TT"Q1̑h ֛]fI^JVת:HZ8Y^Q4Ub7&s2!sk&99 @m6F͞Fsi|sB; vę@ʻ4ƢSʢXPcQɲ'rl8꧅by!>`ocמpiq-xk;}+}MsSGѰg.=J#!dӑlm^Ѿ`hvjw2?}̬#orKNf02`d!ՠ]DuEj.ËA45l5 0].Ȥ [r b6рp,)oQٵ(A]RȹHz@.'H)ᝩ u\|-)91;r,]bյG}y[UtspWn宿eAʞcOO/Ŝq1kH[# C^єhiZnK:ޮ+ݧ{DwZթ(rS iتUjM@sF3 DZ 8n%I2aH VPS@sm{=fuUT ٬v1GW8"Xb9H~ZQ]ՎrN}re,3 S"svvRQ!c؆2oN%l7e~i.?=0ң̌Ҹ.+A=en)S˘wcZ q iXͲ4\:%&Hw8Q7h &QŵVEYR刎\(ZD>g@bhEʎKNBڷ^vcvz% XSYҡCJErS="pǧp-mğr[/pﭟ4og'9'}7*=VSՇZ}| ӱrS~c4˧&ߐ?jRmRZJ,"oⳖ< 1o,$\CpVUꔺGc_T-{G*k&Fl郡Ly [ xCB0 ч اu^pAƲ Asּ,ANzi2ӓOGŭP$qL[BxwcEFNƙ/- mG.i ytO$RWG1Neʨ_c%a5affƠycG˲f]lƕW|ݠ#,VЖG b͘nlo c2sp\z2 [ȲS?JԇT?RC>V V wga#fiL߂{b=5y^\c됳Kp'ëq[o[na:OIqNdDgbb3e䘟;Y(s3Qb3Lׯ_ə!eMQ|HWk2]J.F@&;Gɧ8_<,ܭB/aU||]H#eFYN3Bfl|nT;s,,agUڧRrȧM߄<;z/|)#%goh!WN: -6/$] i.L8Kǥ q;w6RT~6~,aƼx'!87nWbt+U_^UJ|d1YG\^t)'4e^{f;I ${ *?NtSܹ~њ9z٘oT {":17D9!a-,.PZ'mC_u=} aOuDP'(ɍ6!:MIae@wx,, %0~2r =(=]~OpLK .? QtFi',H-MB3%B^+zDO zF=.6:qf7 у~#m t)Ғ@Ou J!._3/{Q@'{P} zhVmG7 >/ t ?P42.ڛҨv"/<}(Eb(8Z(1þ<~lC!:/TCp@P(L G|H Y|~;{z}1z}}H@[}݀ߋA8$2~ ہF%-G1x )yGO aH14}ۇC^@qD4*q}0)jdz?C|4 yFo$4DP U Ct`*> hಢ~%` p_QSQh^HÈ>֥ymKC}C͊ziMCM]:iŪ uuMu-AҋPShA!z%ƌLmFCF֑,FѿPh$Jmح׃a{g)CsH}0qы0c"~Ρ0jpE%0S 9E36i70B{JX̎Ƭ@̅B>ιK61QdY: [?TO IH -$)G `Bz<8 w?*ꏮU=ã{8Bl{^_p@ ̈́C.\C~߈1ʡ'}fR\FT%Vox0ue*;(^QMW&bTxiAqYqNŋaYQZ!U/\RiʪHkEzlu1}Iev6V gϑݲ-׹R2Sd~1a?ɏ'_ O~ą .q' ?A\ O~ą .q'J$~`o3orT-Vn%%(oسuLlј8緅|oE(I-Ԏ?*α0"d 5[X!,Jf?-CRw[,: }v/~ f|X[}8]|W<]+!xxX{+4{n,.rJr}Avɮ"wNLKExW o'?x,}MF8Ub:@S[)kk5;+*ț8}ʭfe+MJ_ĝOv2It$h}IrW { ޥ*{>>x_^#^Ap\`kRZy==Myk+tdtxoh^X@\0A6>%G'8N/8}q>aPjwml߀ʽjaHρw݋mJtfͲʎÜacm!'nMֵv=i_l l|yEnqҾ'fo2 }Pf5+4{/-][[읫IӦhEQkZVrZЦѿRrѿHSh)0Zh-@$o7$3^h" ["*JIiƖRW&!Rjh]:Nmmȍp7Nhi 6ʺ!?x&ѫn͢ Ap:e_ֈarG@9/ALN ar83C%7|Y^yLnSκq36)&sJ+RDvqؙ.;ψ("qB6Ofd첌ALt4Vֵkae[3C\n-?\mse$ɹ.#jdi9d<ȤmP VMHwQJYȃJl3Αg8<kBgHy11R~KFSqwԶ!8KJȬL y:"h\OI *8Q"M/c, endstream endobj 90 0 obj 9829 endobj 91 0 obj <> endobj 92 0 obj <> stream x]n E|t;qe)uɋ>Ta"Հ0^ CJ];iV>{uFt頴t0 =JR $ n=LLU-fVH^H7Mn &О2RT > endobj 94 0 obj <> stream xyxř8\U=sLhF3#͌ն,_d3X>sŒ˱!X!0 l,` g!v%^~,H{F2>}NOwu^V_;x%ȀWMBغ~og?]}iDHpU7\z璶2o3B 5mN BʣƫhByګ6_q\?z:PW_e%/C(s]k݀~3?xI7 mA 0xA#JZ`4et=^_ R5h,M֥l1_(65Zg7s9s_kt_peW]}OE(p8p%L^Q9FQt 7ZGP7CoȄno E$"h.raB :jQ-B=9Q<НÐK:s*e?q ޼2\r.}h>>@@[ב]^|-u |3Q/;*W zwo1AxW{]]xrGcK;p8:BM=l ZITfWvA"r"#5k0oX[)~" mBסam} F 4:pq %rHۉ?.܋KcBnb8*TPF/;N7p5ܵ|Vhz=_@;~ 7w\Wʓ?@[$B- mBїw`V_FGпψrɿ"( cGK R.hw@/ZB|މǣ=ѐ0 F7MPi(h#W`>^Aaz;PS2́M[vn'p?O~VفDy0ס(; I||LE"7[rwrq?~Okĵ_E pڕ@iT@?4] k݌nA;=/=)56z} 3p|9j=pO+5;)H \2\Fn>rC>zn+7ף!=<_/%>Q7;I4\=yK폡znV| $Bk'` ލ# iv</k1pīZpmCV|&=,~q9F$KJ2,!uW?$a ϐŸzn-7~Ƚ̽'|m 2VMgBH6 /k|f ̓5[\',%V`@75umx9,Ek]n.; /.$rQ?E~GN?|kg&h^%o>D -xZy £HO:zy \NB=|A ]}zN\ǽ?E5|\LJ)8D*~GOrOE5BHQ.tljwSd9#11`A7bvK# iM~=(86F*7P+?Ճ@9;Q5v?ChLyݕƣFhPѓjOOUr@ *OTBX6V#(BA1]^Lmy^یRKsSole3T]6E#5a% }^۬l2:$j#;#sx̟_OÑ枛gDcٔssSTIJ҆JgD9:'Uks"8/fa7?J{e)#s7o7ۧuD:.է>z"k&flGdFx#s:G<9#\s톑 z:XY7"G)u׌h:FDrt/=QK6D6g[KaI{猸n<"[;zvt/WhpǎmȞ zN go/1Bbsv̅ CصTw{{FBڻK"4 eDٸ>t ^zry;z"v_w>;q xTsnJ}zl>yc0LuqŴE#zZ>%-h^ F6|\>!BLˏ19|nکML^ %g ҧ#H]f8ͣd$/+n۵YpN]*Z zaGj6;Bhtc9MN9S/p|$)~o΍#|I5kiU=J玾ZvNr&mʇ 0#| FjA@U=4BlnԠ#Gz>XUz .>aaTY V}^]8h-Ŝ/Mi5unx9sgA8Zj9isY17ѷcheh]D#;s=\ώξ'?W$/G?J~|C:zb4‹NH#wJm|t6~sx4–% 7*π0JH~"}PP@cꒈ?ș%3B%3PZʖ;Z<>ql;rD!S}l6ʺ686k|~w@LTh%^.V0PyɼAA"-LIiZHNH0x ⃪A͜$jPΒD(+bfwE=xB,g5HRrC y ZjYV&hI4 h:OjkPNowʲfݦQŪ!,`< h?f}Q ZR]{Vݯ/PZ(=voD[ [kԭxokEOw@;G,^.="~owѓcx}cYs}c5o?}[[[i+07Puɪ;o^M$n$Ru-檪_0܌ nVnnѶJ]] mwopnbsۃ6SȪ;ԴQONk:Jc)/ Y h{TuQn![:AEŗK?8isy~_ 5[ ZnOyPD1-ю5x:̶BGNO3N?O{ǩ;k -ҶLDW z_.!q=Ã:ilF+> 6/G+'J:pT$)n Lo ;"5`\6] xvh@>OןH:~%k 8UG'';kCFX\&50izX, MYY+^Ps{A:[0vh$U,wLLƐ![[dyI@dyN|,o$2WPRP$GA> *5}Z 0ZyK LՍ,rXPlK(;>~$* #u4;V(B -X}Oݤ&lUB'ap֔u ڒݥ XrP;/i:ΚEj3 Yt ] [L6mz2Nj_N<*e d@Ӗ Z £)l mI}i{{˺+!6'EQnjz\CyP(~"2E1FS BpWcbd(c2{tk*9 Q5[9YħD-bcfǗ)`SD Tuu.ɲ<>>>8+RyRl {at5:nPg#hN"pRC4E}IA͏6s5BɏZ/6?@#`[n4Qg)]d2;,P>}BW_T6RW\-J~`FjBsSSss٫qMFt :h|e(m7y_6`W.mI6ˎ~e KZ%sV,jWt4̹~Et55Lc~p24;ukΰƶ|<5NAXy˩u*tQ t-7I2;#?II+Z*4=@s?U-D~{U۠ji{p)QsQFJS W}0Gᖏj<1bT z}Æ*V΀F~EDlURطny1iL?& ĐQ):qHAw\ꇪXoavT?>,0 [ujXK4f cG銜8V.G#*R';ЀY..ϋ=K Kk ks 8 7t=^ӇݷsTkNPv8s\b GjZ1`C& ˜煨ۆldۈmvv¦) *.tҾWDp[aQ2P2i;"ߛEK$*-||mx%z5$/Nwfvdcy%{/C@oW" 64m&hq7x>5 !~*څ ڀ'eWSca3z!RB9 T"5SxF]ut :TMa dPfS(OhPp)+oxTLM109ba׀dz iS o' B0j|~Y.7t3Zu 4m XmJhh@Z)|U슫 {`݌@%yZnH]+,WnܐMzLH֘ѓ-\U8+qIMI=wؒAw"% CZMM!֑>ݐnY!6ʸ;&Carx$<>}/wM0 gơ[VLrjUE( qC<+ 6)AQ,*ȖB5\,hb,N*ا؟i*Tnc3d(CD7>o MRפiNr$t 3/He\i^u[ou'?8ŰS߰[כsN`SKnj,S\[ YrY IC@ƐXBLhQP@)14 z˜yۍΦ%M .Q+ l3&!yuZ]nnQ*pk --TH*Q_E! 5duG$PfŃ9H!-֖d@dDKFU dBuYPHCʰB"+#ʘrLgdO_2$]a%4 tP2`3/(7Ϣx574/D#+VG}f=s25ƣo(nޑN4uړO.X2uK7^:^&ntd0Lަ^ Y LLAhNjuȁpr_ XΕ|bHm,!v׼D tk>Gx ADհ@ .6T6,SkviH yJÈ;TkiirGv##YA SԄ֘)Nb.[vWN-"lFY>KALLƤA C=:rFݍ;s_ʽe/I˖n >=z~0\jV?6#W7olϖkr6ʀLᦑD)a֔oUbYqr`L>oxz~\ʂ Nk;QУoJ;6 s5j<._)$!V' k$1m'ٜoJ&&%I$S4H=].[>r.a:h6I5Ki^ê}QA'(s~+67oD~y'^яzk{RNC!ߐ{ΌьDi .Ӛ"sڭ~gwiwVFToMk Kk'Oaz27!ߐ!e~Bf{UK;yX*0r4&XшQϛ'(k@+{,X3+7 sQή͸o6sQܤz^O&$a);$r x++L䚠J{KYX+-tX=)W0/jۍF=^7?>=^y@t he_ )E#,a(-$ ~\e3n*hLTY-_ŃhsTnv2;þa" Sxdg#]釣r/]ޖZSzJXՕ>6֕yt D٭-䠵ݤs<(Jc琭v۪uYu +ZJ+VZj6C6s#j5{nUtW5L ND(aUX&MT /B [vU2Gu\;ojZVw߸ba}Km23ow x\PxrAI (sVm-j-ޭ +n(ſri(dfz=3YSqa#rTsqpȭft"Y6|9xAK@#39!OT%XJ¡@d2bμ3o#Q|UG ؔrQ,tC&d-l.ݻII0F^]4IZ:1϶ I,F 7WCy`8V%uSLVkZˆ&)#wg xB>hC>K8bP+= D)E=5~iH:!q nOHc11%Du ,x&}VO\; A|^yoTӥ0 xnS0'F](ˑΆqQrXA결fc~:BCp%j!+UiLCf%h-xz5555>`LnVZfs?ԟmo3n3fޖz2/[ycX @1WJ(3I;䂹P.|^|eƕT Cė}M<˼/*\TiU6/+3Am{Aܓٱڗ^Mj/&ۋұshRŇ/|.@c u0`0 u&>e&'6pZ<`MPGqD-+2J=z:B3_3 Q̛PF2c>#<\Z TS qƳE(IC2C_e9Vקy48jސGzC:lJŬ<<TS}iXx>_Rh79s)7cf4 )$J*EAREx4~6"垊YO_̏~|W_4g' bޞWmzskb{Xt⟮ +'S\-^} ,Ž_mU?_{{Y􄲨RA@Y*|đQu}!Q =Ö)C?$),p#ȎܖiiWr`ZsLL~19r* L6+clvEibbHDLKSAO!Vg<۩K!*]‚D'|e >GcعƵʽ+bNn6wS{_˧l>Om׾$|Jx,o# P6iy%edv$m=l~O7*Ԏ~BHNN1#HKnm-Tpk[%a*?|Έ3Y+xsssۇ(HN)8YRK#@|4LmpťUkDmM8dM;e&ci:#?>\<1828偔Lq  O4EUI=ʨY;zh&dm<2u2;Dx>R2%#ejKbPW iS!]5,%)yKɨ0^ a讽'\T^$ł0bxoذm!>?ЫYߴV2g^zaޟZ{--@ ?(| KI5#"&Ex w(>ܩ89,LًqF\.|i K\ A 蛶 ^ k0} 3f֊}TfXp`ye"(R~PK}k (B˸R ~2~TL˄ SN?f<p/݃{lÖa{doGIح! +wR""E?դ؏ U99!Tq0?<3D=>6/ 'ΘV P{:jԇ2%b ~4 m @ZA%^ۋA3S4F4Ftt&_Ql`I- \< WJwsQ$Dr AKLwɥ) x JѴ-A-KfOs cȇÆEp޾?cwix6 .\}֑zI LٗFQ(i^a3]Gt'B:h-74(V[`[TN[vg<ǜ~GUAU 7շ2h;yn|\45#c3Nٿ=+0gTܢLJjhud?nN}Kfk)??r{PՓ)(g&5nF*ifd)Yٖ¬qԎU öeA(RD)T,Q H^:1{;{j `lfڃU/B)9 @H Nl Z Bd%IlP.rur!簓80!S`h^QӑZj'+elNm1SťR<{ʼeU'/Yiy XZ/UA6KI LߴE ԴY*imv(˖ ,(l+)MX\y bYjyR%=t{F7Wmܳv4rѴEz{SXSc;XY#Ʈe=/Q'YszRTAA3) UYͯVURrL,O6&={?=qz\u@3ϳݵSB\^1r"h̦GZj7SQ.dӃw{z9.8g01GT#~mmmbQӥ^ʐ){=[cĪj_C6d'yIxB{0l?_.|Z^̌^)[ T*xv}SK>*ušz D) -y ԶM1Qu ",SN0."JhHSKaf5k6c&^wʹ^,L~ó߿e};,\A͗岡ͻ5zU/Ӆp79w~wiyNkL< z9!+b.$}CCBãAKjDQnFbj!q^ Y-U8mS 뫼Fyp1O> ǧwתF Ƽq}e/FLŶqحS\Q5Z"zo{Х{x!7vY}v^r2odxI'˞rN=Q ; ! %tK+|Fy~ig~@rBU͙ &Q4S>ZӦ&wLWLnM m )TtjD)chS9,{Xe`9IWnS4ZUt?:/x{u̾*Iq+*Ck?ad:kFDlz 9ʔGԜX>*3RDtR J`>I}sU^ɶZ:cĠ4G(Tm:=NLx6͸ob%1Zm62G)}/s-N)tPrX=I[~FIG'A*F23BJ Z,!(rAB%L|s⁴y>۞oL6~yhvCx׬FM<<ݴ7F[O`׹ qC,m2i!Thdziر1fƝmmw~u^ku1}0NAo͘W5v]nkK\yenpA|QmWAm]YnNܪ*olunu}S-<~>4 fl1CrJ'uIWU'.׮jn姠8 ЎlD:eh@ɴ \ vv2{>}'v{H EpyN+xXc֝ά[ OwMO,)?I&n-E\1h1f/cg"([kp1%w8CA,f4b'3|=:MNFŃ=T83E399i=y4<e(AyJ,~&|t;OOʩ'Hf]TA2frd;춖d'tO2,(1DInJm۶9B# b{}6 0V_9b2ڒ́guPH뷴UjsjK,D}TnLk;u\)fzHf*4BP[Bo Yhw֪cK2 nj-ɲd;:J~GYuԒjs$G6g/%HΒU,%U ܎R#.vi}Yg}>'6룇%ΜmU{9RhM#z笮5qmXzMs'ߎ~b~Qٸq8w74pѨrYmq'H4*_|~U3=wT[q#^t`h t46CiS7WUÿdKR-,(ND3Gv<"ԂS(,B@?ʶclb[ %J= D=q\KHDxlȦM&rMesx`SسWr;z[c{Ro"'k7mhʹ.^IǎAZB>S8 Jұh`;!84QX|],v}"GUDDQuXd!Yfz\RDa$'L&67k kq 8l"̰hqaAS[ )kM?׾[[ד ƓBX%t-w-ɛ1_ݺFt\_hZzԯZw5q^^M6m^JP&.oo^zSu;­;;fڹ%t}ӽk(X8:~ZcO>Uyho 6k /!Olv4=bo*N]UDtwlo ȣ{RCa P[HZwHOT0!^RmBJێM jjfWTg.ݞ!@8R-AN#z.?r(,\N 9)3;tz>W,eex<[&,YgqeY,Tkΰ[Y׎kM4g>5S{:Z]\XMjs'6;o:9'=N9 tzrI%ۮvސ9a>5\RgiW~_XvAR`I95) Bj@> [ 4Rr2;zC"ܛ<=N?oCcbc3H~挎f7vl XcCifŗ32`{gXAq~]Z9~<ҏW oZ԰P] M*P~4y:]~xiީG ̎UUF~>]ﴽnUS ULY7lu*P\2_SDkAl=2q3r9ҡpʛx䳗n[B 7)t/Zי{6׺O FGTAuyuf~!{d(" Zc]vfkׇBɶ7>rW|O,u~50!D@VVʧU++A)۫l%)dF tzv|^~$6dmvUK+w@DT3 g/6f+eg[6(@j*v+|Uw1+`.[橚> .9߿hgaט딋s1ꪭoªw @Fѐ kp2ID ,[>$jtGj)( p Æ=SyRm_+2ft{@&Oad{{k |6kEKY#qN:== sDQ/^LH~LY=o8s`pO6d93V~+Y|s~ӓ*82lPlFlFis(/`>:7q=!AO!^ӭl%~?=%D*MOe@ωd o-i 'D5dc8l1bQ6*xnjnjʞmEc_3ڣpFGb\ ~q ~ B!>`GdOMOT10:_XdX_4;:Wqڮ/psN~MUڪ ;Zm4 kb!_*Ց6BZ_!Lc0!!O>#&9G2 QыR )?}$ӒyctA]ݬ͙өP}^}Z>η_SHf,̸]rM,X(28ha}ĒS 0UON|J)e?g} 1H[ OS'z%0[2yʗ~R ;ӸDAL O#׶|pY}\.e9aN8򍵗dHHU eKVskcaaR6"2_N#6NjT33`sZAyވxVܣدZE. ǏǦǻU5ZI` ^5sx78?T7ngJ 3g֨"=vژ4ݶ4@h*_6KXmO fAAX"e*{vK]$klI-lj6$$8`5c9^8eĴla)& -;% )+3sl'@_@̜;wܹgfΜ3sL%0& =-ħoދ׍לy`b-߁6EqTlSkNtwg=oFp=y_?%jжHe?TX#G\8Ud'PAB?rh9eb0 bnNׅ-; qH_ƳVەڢTl^RȝG|%xMakmhXdͲ&UBoԙ {o*rP+6t*Zfk*l^}wWh폓M_IYm5sBrŨ2+dr6VVjlncQb#t7"2hm% f``}C<8g> |_փޔ\}5ť?,rc;αxVo@*]bu ^(Kϐ-fdyWcr-Z*IDr%Dѓ2ܬ>O]jZ<ߐF^rnF Py8㤪rwr*#o[YMHw"i&<00"D5m EVu*C$yu~oO_,~*6/ΦMOowͺξg/ U/Cg{R_lˣ36~m% 3{@ҁSKctok8y4]hIi8`^аaRV !-?t w]qߗUvwFb7C!\$}bϚln%}Y^kR!F.yG-%hjZ$"쯨`?Gd?_Bm5[[+hJ4g-@fɾ ࢍBSA1Gxh?IMo)IJJIԇ7F* S)L]i)l%sގj~XUB>rT*)WM dXT!Ɛ1|b[_yE1Km*vgM9&ǁW:4F/?9{{3(rQ.*QqXԫݪ('11[iV)`mKP0|?"(4.nҧ*0Py(e7HZo4mhӅ}"|OHw$/٘%d\'rdj>;()( K@"CSGdkЄؿ胮y]WLqq(jʞAζAk{Kb-J-7?8#(;x'#TUU[~֢ݎ}}N]IrFJnWqy UB5ԋo3`N:HA K92Z t_"٫$HL&n3c47d;̜z,s'd#Qk ccfd'لlxQer] ,y+БZ;A%hRKNX!U:"EV̙ooRŵ%H 3&,Ds"uDmnMԢ@I9e ̈́_ndރj_9č[| 3z;[^gպwdHbq ٕc 䟿=zG8oZW+Bmj~Վ5XN=?"֮k5kfC:7V~T[ Y Acx?-PHFȮ =|8]j TZک4Rp޵1/=D F9, -|0^ t$:cP Nurޡ=r=5La$7/X!uT-ư[w19bjUkUa^4gNoBg^|$`h=XաEܹ-Y p-FU?1:~9׹!YFu2,p^!.T+!P{Ι8¼XObxF;({KVDOХӄu'u7*$E!Ő:uJNfBPhhFMߑʦ'^r'ZuD}eF DUVo'J퓚#8V? -ID)g~vO }NC)\*u)ErKtv;6}n7M_ǿeR(+6ܺ;_-XQ/yd/*+u>7oE =lHk4cBs,@pEtOJ! 9 ?UwO .x2!Uœ,9A|(?5zVaov[rdm–Msg*?xD5CSLrx1;e?f0Td{ |s{Ⱦ_{|lN=ߜ\7l4[_BY>_-Ef36˜+w1\SblͼɜcT@Zs}n?h60́|9Dc h0vrOy$)^YX2 |N%%yԄ:$/11K>h( P, -ãg[9jײ.nD OQCv: tMEF(mnDks]ifދO??3pdxnIU9( dM_o&u69p3N6sTe"i1dk >z:?͈SrtcxrBVNٻq?;jJ RxGRhrum3KϪXC9{+y d#OA˩g%|ĩnVɏ\YÄZڮT,x/?bz&\-~G1|"vVӶ@|[ɽ{K& >z.|p+KWG~eTǜs@(GL>eN 3UT|қQАj8\JN^ߙě& f7,q͉( \?( p1f ''8wRpM!K/$ T5$$. I>`zKJ^V%RuGsnHZng8@rMVkJ<%$A2KH~⨇"NRqDT/ǭGq| k8h+:V(Cgp()*Y>k&]5̐JAߺpYlK3{L&1iNHO S䀞߼+ZA.\hKS6-vl|K;x3II`NאkLG, RY7~Z#V+ƹ$u +iVUcr!L"R5Gj=VFz'{\4+_7 6HK%ϘXJVsv un7-goX"6RK/÷tdKV+o5a2ڗ^,SGF6 G1cRgfGw;6&Gw{?q.~ߡ;֮LOUkKEt(Rv;G1jxT}YcY7چ6;FCN{]XپW?r-ce'/'B "l_ҾԯNx;e֏2j>w`|n?(( NUd~aO%Ic3:!JEe 8 L/+獂DւJI-ꀵ̒~Eآɩ~pHƵ ~n yXacai{ڞOcYqJ6a<ۘ?qjA %+(d!cm͓c5тH8zy7 {}FэxThS Xy@@wc#O.Ìn D֢ ځv1`#JeTi_߲S`aQ2ŌR ^7!];3#Վ *B;v3vZۧpQWTeK|^UY%O2q/ڿ?T5M ӱڹ +ō7y)w?m ~)7ewW#L6VcousTgTajVu6Dϸ4B "%&\-ҡC|eMJ2.~W&^-/?v~ӑw)v?%7]BU-,>b#!Dɝv"giI,}rgGE =)c(=m#Gɓ /_mA__&}.t83+*1hX\)h!Xu!uOR^bqin!H#7X0=ʢH2%%.dqdXT1|)>n"Jaݚ>~nh;Pf5dYHҴƲyMbP)_s6N4a_I00`,y>DcXlj(4qJZf*Ս2v٥~w$lSvEpv'fez2v)3hnv1اPr r\GjJʚ2S@dξMgG$E?W(^s0Pƚ:A:Eڪ8w"r]D*]DMOt]U$ݘi@igRde^!Dssv{=܉xˀBz8(k":Yyىb78g,5nZtp[õF:Ǿ=8r3.-B.}1?yS8Y{/>{1ARl@y"ѭ5zWEy5^ѽ{Wq%kΪAl%9Mկ;CNh**{EI W0zU\ }5:#++&jrjҪ& ʺ9b&w̨%(f6yK*nR*E*O9׈x1t>hTTK V@Рy%Ee !WBgY1zI)}_kfYȀ8fXŦISzQ߯ghgy ]%b0]̧O--NᏜx=[T_t#~6PBKA==w b?A0$"6aXh ց6Ŷ{U'p8M*uH1$THfVڄW*cSz&-^hq>d>T!ShϾK S.Nq~UgQ :~Q"EPHCqSU**G\(""ETH:4tW(3H|n?PDp0BQ(6~/ӱɝ|:6 FjRPԌ'uҁ@s+OO, ,0wXpƂ-!z:1dq&OO+LMP*nR)&-NV"䢑j``D)0Jc(*4%7b%XՅy'gPBV E+H-J2\5sE8v:T/h`#`<^'Rdyc+pųP6ڔ]YqOOSe'NK3z\Iod֐_lG>UA;./jG s8{ɴrVҎ&H`hP6y(ąt5ɢLߝWzߗ^/[֍VVhuc5׍Kpuvdmp_noٲ;Zm6[3@KL7a_[' n٠\Pqgmn%W sEL,̉Z=ֲeC3+嬲'O)a4^*fyg\q$TrO8>Ҡєލ1FEjY8C ~jRBq V~?fB[1/?-]q|hqӤZ+?1>֭uf; Ο:t٩u:/N2&%e<4DY#g8MCf[;QVfy+FjKG&jmUFz=Sb;r(qH¢F_ %Dzd)JQ*ZCVX8#lX<9̼Xp\1!*p"ءUp c)D[*#[hqW0H!(ft6dTS2kLFC(/W]zD{Y*lfa-N.D,q:bV l;{W㟯e\CqPL9Q@v%g$V) Dd)&"8wQcč!׻e@vJ(/9!Fc٣ӄ8(Ї_JD K"'a\I3{ζq̭*5|e"_ ̸?@?Oh & D^ pi^GQcUԓރ*e@>K{r-\.I7Fd¦ݞzCqljz2LLj:LәIF15xڒζW*KkTqY_[[zIGw8XQ3T~+hXzQ^^ۂGT7T>XkTq$Yn@,` LuF0#$x`m$ ivRW6J;Jp  ^/Nmމj[b*yTMH5e}.]g3뾊o%~_?u#]N(b5y/ L13 Mء!(5O&%^4wŹt˓/L}]QK#Pf>RptQIIUԷPUقteܽLz= PDHMhދ~(GTt}D"YUtA>ip:|#Gj]:҈eE#ˢb7FQӭ7FġgCF_Hz%F豄)8i9&hX}?ԤXqqlncsgMфF ì^ӌ[L 0BZb{+"5^o B jZ*Ec(P$jCZm(XXr8g, ]QP z}h"W<2D>DbT I0R:WWEcTFuYcXhZEwɉǜ`_+L:Ʊ"OR 93{:$H$WS @TXVˢSNVNXAZVM?xC c/Gؿ:ٿ-coq^x/e։3U9>hYTN&Ѿs'nv!;uX__9d9(ZYQS:ferxZ$ٳ;o2}z/j'f'ah̖٤<|CbѰfvBJ%۸m\:TAgԩdjs to=& $F=- ʏ΅"F,j-+:^9pKݖ1MmY6qB9-9UslNV2t pŞ\z)4voPnЎyBl$. !XՔHTa.*ǒE%{u|{C~W} ?0yEw޴=Z7~;;{.? K'޸~bin@*e! ),ëane"=@- F`u%Qew}71 8՚q\zsIOKǯ /wM~+85g+U MsfEHUQc*ov 5<.E~ *Ϟ~ZUW.:w}hEpX~U ooDnnD]M%o~IJEZ ػb "G Jqw E9&ʄ"/.2(FrnGz@.l|FڰŚqP#0Gck \ d-uFϳzR2:!YwO'%)Q*rMya> >: AmnxMh:Io+I2@p"l?}sǮW]#[Sn=憵/yB0.|ޛ?ȗdfy[[ŖUsv>g u.HzddQ$ԾLg$;YMo~f=V!=5TAL֛ XA *m7i!: a>0jɑp%%Ptv!WGDL£eP=YO'_f'i,>~67/[ple @s<{7҉o?KJ{?=kLN$JJ<l 0bB qF]G`^(PTZT?h/OM7 ms̍4?x$\~?lkx*̥&Z_q*5|jE9q3U:0>|]7tcj9fcڎ Ӓm_& rY}]8cݟU׀j5)`̭hW7[{̈u)Ze߃Yx D#ՊtZ )j Dm<isD&&LԈ > tFZ UIr2+um(/hy_Zp*͉{wR|1CZȻrlA|cl*C,TDV&3~lS{muP[++hXx=bkcb gU(>bS|C۸|m_cq#QbmߥQ).S5/^ah*DZɮVP*Wc!eLUGQ?WSad ZVdcm*=Dxog  Jl&#H>F+6ԐgAQ4TJT;^T%J>`@@+5gMqwSVdg=eUVkuYǙ ~y'-!5s&u)>WLJ$Y'jÜ旞wOb%Ö6*7MsXʊȴ%9}H; Cl$"s,芚nbv"O4v5"$ ˮ<6k*)aj@SGv]i'#^ciĮZ~ѵƺjG葫+vT<:jNעLc⇈2E1iAʂ71#]zu p}`W7,E!عzRHB>Hs)\? rAߋ ?Q<LP7`m 򇪿}Pih3iϘ߷[c@N|ow@"X;O"ƴ]]l(e??MZeQ[~/jO:fͺB\$v5x4lkxab_}h v,/l\<%/yc闖bvkoT7zw6mڂZh10F$*I~lkk ,Y 2` h0>VyD(.*:>-jf^a kح2C?ך%9BKK_+nZm{dzGny\Ikh|wu5+vpt#SZ+崅馹eOrlg$\t #2ӓvփFӐ2V.Cy4j1siuR:9/e'm^t?$/AN'\ 4oIq`rLL>[hMh[C/Y ~>9bIO1+'2-wq#L.{-藱<2IQvzZV{'ӸT6W?m6 >96Z.:J-fmwn;=%0ɶ#.K=ht*/urk,|e":)S')F\3ϦcJK>*> 6ʇOq)mttMm}TXD5 %npH#Mվ `|OL/CyNy,!|$R>%珋>]/ǹkW!ou#=Ԁ$I%jP9RJ@:2"5-Sn)ܩj!SU$ H|dK^pH׺.aKX A0'3Пh  anP(I2VdzI̠+MS%%^C]]:Wlwm.ll뻸kpo@K]1̬]~z2z M+ ʁήm̺@NOچrиn]BhztCdNQ]܁-=}ɳ=P%Šޮ(@`XX1 tv ZVv BYH{\]2$$%}8|L_'|Q_זbaaKwOG3$li:{uu&aѐ 1] #B{WGfc"Dl v 3PᎎuýhB@ F W蔾~P6B5}]b!>r!N&O*02#@fx}7ԋеuogs|dU2͙ͤ& 6 56ΐP>hrsB{c 'h[imz -rDv C#Pmm,61(KOrwtm% mgh^< u 3hP(.Ag2i6o5(.CpZhgcXDw0Cu Hzȷ&k-[$6ʈL@IvmMnkؕ8vT#^#W.hNE˅Es-_1OhXpy-_굴LvwVU]~U|2`40y#ɒ|6& y. uAeeڇP{P-q&2U.Pf}HIN>04MbʽsZ d&ln$mpkh SFr_$SBhm`WG4OX$m|=}ctL(&\Pޞ=rKHh2iy42p{o`7y%{#4I(?TU 5SC翈cѺ# } t> răݙa]{`@!mӟOAMvA?"I7P,x:&&zggK<@з\F𞶡Z 0Dk*cBui 1Őh.YҊ ˫teZ^O;#Jţ 3X@J`AޣK^nScee_,{}⋅/VXXba} +_,| +_,| +_,?rFֽ.xy<{i vyRn ~7y,}>hhTt#e>7Gg玲o7L0hYrS]- )|ÁٳeF٠e a ->u0(;ՠ~ 1F>4 pٞWAYc?:7A?dAfcOw>h0AVQ;8eGp=CF}I 88!~\.pZ>>^-6_aN!tA-B\p| {{rxĻ!Ix| ;xp_t;@n'U >fe{CXF)tm?:~,nommGܺ:j)M {5\ iAx >N$~GׁbcP>hdŲuj]wYPkJ% BI.z&vtH!``;U~! psqlǁ¤mT#`wp;\j.6&5&ifKP$VMfT7)i(2v8l#UdemnݘnNOyTyLy\yJ7)[QnR[[Ŵu: N5>km' xZ+$\|B\!!FE;MZwwrϐpkX}8n1\JWzHu9 %5ciqpjK[)S4MHeΊm1wǰXWP&3kkCkk2L(<5CƇ`}>Z &Ch!|QCܮO-}aOrkfXVm2B$|UVml<="Ip28)}̓$>'$||D3gß8P[ذHZp{p Z?Nc4~J@[N~ʮFo8]̓[,K>da6Ƀ6ǏRn~= Eb[a>!Mo_55=fG~gWO/ogsz&:;aQӿӯ}=l{&>!\#y\/jU ᤉ←Wߐ/J+XK9 9V3xZ奼Խ)'"XqqDĸ;vUjFA$NH|e|r ?- T`!Gp2)Z⌥XTŧGw4rL.1*%cYZlc-*-iC|kR=,g*@?G&X#-}.ZG42kZɲ/:\/O;\]D'5aζ6y7AoQO#04b" BV8m`*UV" #N r@r #M8Eb> endobj 97 0 obj <> stream x]ˎ@=_r[y(|xn%ѥ]]}hZOSɷiha]N1<&ėp(3q5>^Q=sD^c> endobj 99 0 obj <> stream x x[ŵ8>st%klK{ږ,ˋƱ,B!nrlxw℥@ҲM(PipQ}-c mۿ3sh#ef̜9sΙ3gflgtx, h7bԹc˶ ^F[:wu%'jnq B o ~ukxzD;PEQs@۳}u_q G:;ǸZYTm /߱=~$w߄P#!>t7НYԆaQ=E ۑ1 5bِPFԂj؈|n@{E@nԌRP5 :W̿_^~K(+OueIe.t/6!jt3:0n@O_yt [}ziA'&3| t>Bjt7=NJ=ߎ45؊kAW?;LP0.*< ۏ@GoȄ"0Vdƕ&S |5?)Ahėiƥ~U}f~"@^ !z 5fW- z:ep_#l. z!~g=?N,܋fk{濏0C X.F}q~\LGߪT͗?8kУ0C@ps5;@o,CAGX'ax`~Yrzڌx:6~/&~30_c13Ǚ6 u~eK5sν7kAP-h6ס}藠w':2;@@{p&΁]7x/?_çG b Ylbaa>buf/gb~W1gS}}9w|"g5A5sA]`0ځvvǿs1c-|IEh3 O5W=$S Ҏ [{߄}[~/ > 1!Y3ja.e໙d073o̯ט3< l&[neod10/U~UjjE`+իԛ՝OԧF)ִhkn|WsL[ͼ6 ^b>z7f;9jU3;rQnE&{mXףA7fF?#XPͩ9}&a D>:F% KsuQZS Q˽Xcw"vԄ&+mS. ݎbgWIv zاt^8ǹP)*[%jV"v!E{ԭ 7'gzفwNaXf49ׅa_c.m:ޡާ~T}XO|t9@Ggaq'-WX=9~13AY`k$Gk-{ȿ3ǛЫrl;a|-ӈ.:^' 9Q!6Rf#vN@orSr2\D%kF(F-GPu?vXBv @e?`5ϗ238vChzٗ! Ex-*[=EzTUbYyYiIQ ?/ 2>gFzZnKINZyhNbr= bS=+WHK c"5)x>ÔdLib1:8/Y[}D@' p,DE4 SB9;A^؇Za`E|󔄶@&{F9/-iq$1L/$_Djv/,6o&ֿ̧X{c8TG =.(mWx۸\_X@1kF6Q &If!?*uהF ZIKWqϐV49L!3VQl=P1[̟h@UAJt% *N$ߑ&KPQEח%p845?-6Z|kW/SdH'}P0.>x<$`b^~u^{XU=^!Dl6jVI›'g0Sߗ6؋8BAN'$@rr䪮Jt4&>gKi|E$\㲵u|Wu};]es벯}j*}W"/!3^&fVTe{ӿ>zOqKzcE"Y5oUy P{L_7r#W$v]}F5;h)ӝwyZ4NvMa_[*R2e*/+*"7ݡ$Dw4)Wk =^dЫWB1P׬*h~W| 5xw={CWʖΔa۝wʟ'+uڔeJ-lIfQ~d+{o.(±=s+ +SoQxP?X %i]ڨvLb˴k7ikXkm^ԲI-k7 ^) Ì]Fv>`o?h֮9a%3ћspNU*.|- U  &DH'0 dRJFld*FfՒU5ӂ/aJ ԒPQ3yjIݢnWԎ٧pʺi:=tv(6xLmD?>8R C=Qi~)t ֓Dee8RVƿ+{BmA<|cjoIdLMslkD7,콫wne%{ʯ_Sچ]s_\Ͼlź\OO1z_ m%U*ޘgd|/Ϳd@)_Au׽3?ѫ j6;ٰBh$d`,0&)pH/A/MHM%KũSxqܗEj,NEx%9/ b\{ӛ#rMMn.c Rl D&"#\b ZF+Arfg-8|Y8:e9 KdO /$4JI*mEdtťGcaPTK'%hM(h }!i|`)X)LOOdȘ"# Ir:*'0HtH"5,v X@8O=w$Ò.*,%!4$c}! M?5?3)0U8qBp/|!84 $NZ:ahqkE] lB׀jρa :gl1B_220Rql i6so6au-%EtO+?g-ffz KЄ.: Y81[ek#4Of)<_b=w?_S,d2>W|ݬAO 4"^/_LřlXk1>CRS?b䇍RU$CrREɲJެ`d3aG{~1[ cfX&i ]$ }>YDؗEcl*U0z'8E'%Y S,g6Z2aN`q"&#'Lɚت9QږI&e| ',\WUA=M&AnvnǏ݄f8h߮mmݮCLP+uM,X Omn.nO-&W~qㅫl&ܐ3Ә7ps/?d[ۊDF2`"Q:zMJqNU Ol^n|zc ȃ8Sb8rÜ't8ҡTe$6`0#c mto#./"ɤjEb6| #oclWH'#s*L20 mx@N",Jd_\_UNť:^)-׫.Ũ[RS>LU^ ]n#ƣ'STK9y'cB Z;yY.:9̥Oj Z07K۸˟"cd=lL ܵ԰հrK 3a&,_w^ɤ)V*ޑ[x-1Xȹ RN,wh<>23s[(`ȗę )zdtb07hMx}ٶ7trJy,nM :Z{oPXAi6hMvߩ=EV `u\ sN*[10ٳ== y57‘-kcz_ޞUm;gNd4 :=?N:3;=UAq{aC'Uk'0 ݄0S}o'.gj< !Q;|p-폶z5;sktB|[pU~qf]C37yQwYse?JK995'I> 5!W[F9L b;a#anyqISSIGOyw%"lE)}'1IpsYDݯ׸VXa!IKAԃ1 LzF#f4Xp8ʍy-X b106LoOpOs}h^NùG`,55,YIy~+^c˚8MsG֯*i'8gr𫗔5wU9MӲ`«Y)|rvXQ70LKL.QϩFF9w9Yg `_25;+KuzBǃӠ)w4nd2],J4v*-/J!QfDDy[ގZيEq+[\Xse¹LTYܑ*wʥhr)ܙ ɑ\$4Ùә3lr9\f*E,d:Bw%b }F:E 7m|E%^L|_LED^LE1` m֭p?i؋XWeg-8,`Z֚uٜ>úڮݱ>Re9}íb#֕AsUa g\  d !(âe'7hrS6y5fs >[Z[Rdly{L3w pDzYi|R='<]Ā5MR>ص tnmY~Ц'mx~VՁ.0.d.͵GV왹Vt\.p;HRUnj..K~ƯeIaRտq>#/rc>=cRO&ªm2; O17ĝK/5]|.W-#ʹ4KR0#1hK iYp9jb} tܞxl̴ի\UUs(+aG_:!y9]gw; 6BERIPA/[Vt) qN!߳8e@f-Z8/j;@y5wj4ΐ\vq׊ 7wON M5]׏:Ya'؇Q9#13IMN\n<ꛌ µ#?07{1܁/$Q(,*6,P* kEQ\p;1*+ Isb,Oal]xx_3؉BN U0 ?g?="F  @ʆ7rm3$ߋd+c8!9MʒSqriQؗLv "j~ 4m .%euD&9IDt+2oW•ᶠ@} J- P~܇Q_ou<*=\#"VyO4kV么`}~Zx3uaNku}ny ѪY*'_V,{bM;Zo8χ-k7$g}>fE7D# ,B JG"2%#SIT`z[hX+6 l'<&߲(  9q3HFYTآ*eV#C ̡ćXV`-Z8Z)yrQYrou&/fd/IP=M V\Gj(9ZXN sP9nU`;qTARX Ǜ B9)@R`oOr+0vHAiV`oڟ䛞 @i ͸_A*0ӫ _q*pBWC%*T Q@ܬ@p+MD+ SW`(l%<ϱ*0*o )P-}^m QcLD͐8Pq>A{g[ tuyQ s{\chɜXOPtA)F۠P/WJ ^u΁A<Uzhb^ZʗPHEJ{[$Kh:LvR.۫QJC+|"@?JEpΉj!*c/.w-gqs1L'ҹڎqJ\Jo"~賃;Lgҭa'b(-}u>Jt 5JpzX[GyrT~%^;) }EP8@zo7pM+DG8"Km+SmuGزfT>]\Rs]T8K\"P' rXQBib,cAU֡#l]બE;ر&%mQ>d>|5&Ϯjz)_s\ֹ.*}Y(1[h_Q(XbWZ`^e=Ͳ#t}P9v(+nJ~C%+nF "JYdޝ-JO[ QƕQ[++NE6ۡ0h=BnSh9D8.C4e .Jy6yR\蹃%uQ RE94Ly \ߏ~9aRT#sۡ0ģHnm}˫NË{dbgGI%`ύ d=x-@) !&^ DK##pnP޳ *\_̟o='^Eʲn-Xq cX, i=Y;(QFUBws#,i;%6pWR-Ưe^c,šx]%+lblQ$tt |>Zɑ{5*:?~KDڥOp"eOB>l ;(E<2]\[KlG崼˻B"=sNQ1~r9`\ك',GkvoWyU *qΚClFc'%ʞȒ;M'jd01~or]^B`>dTOen3UO[ GԞ'GT-Jn)EQe'E=S5O*YEP -E!sb EQ̃|(p !JhbT' H~g\ {hwGgT'*6 BX;0<801;/uu&􍑒qU?/+ AT+Vn/DwDnѝ;kz/XH*7DGpyybVSo@h!/ťPMk[. ё#Ꭾm@?8;2v(^t1*M\KDwZbO=Kbpm/$$^8{Gs ⚎h(`}OBHG;<2*v F; :Iɴ w;G;9b7Gwv]ёޭѮ\Q\5*@ؖ7.nvlQa΁ᾮq02룤QS'vɳw q+:q.dAN*- `q~2@Q?tOၱ= 1zhwG&%Rhpx ,1зH{lZ m#[)j:F%;c46BZDcTWZ:G{Aν2A#ESqdtD1mq˘,A耸#zygLttKo_8 <6H;{G{ĭ@qޮ(rlD֓-F(A;v폎Z10 YC:)䎾;21.v;wݮ02T'3/}ͣDtdEpgh"mҼvUWm6kV7_X/VyQgѵ`CDe%Kg Sn#-;vPS ,䴝[Q°\tvAzClNXbj n`9ۣ[.!@@:(BEV,6C7FMJHtti\"XRfsR,!(a82E"mGWW/Yǰ龐C)o-Q}{Mxd]6h- zlK_HٽTQ *: ʏU&GV!: ,p2an<30u86>IFa*k-ȂFa1XBuwKI^l mKt#c \ta5l*Yl$4KH #K KĒ⢲2I.G!4l}أh lw}`5qF?[coda> j1/x /x /x /x ?- RYhgdԺ|f&P }} e}s>B?%| }V;k%;g9Eƨ*[,gZ??b' )H4ge%gT,sxYxJA(.@DN{RaҴdVng!9dƘMI0;;iH<7A1}4P>=83!fӼ7iM!WܜH۝Ed`T'z^b s[ A`qC/IA,L!ⅶ,s/|]A))[)xEbdzxH&$Nj+/4Ob%7yaZǬF^(>C7i|d_5>5@npIn6>6>66nr׹kܤ,LLh.DGw_EEU4 wѸɈQ3"ƧLҸ%c|)2xRbh¸M>HMՐś.$54 5tP!HƄiIj@^N Qe<=ޝɇn'$Ļ#O_P7?ŻP3A:o'aT}PG?qx /$ɷݹ|+}$ߌwxVEi:oJxa0d TɶxO!W"MC4w# UHʢ՛PyEtRmDp-qp >D{<@,9WBRxַsD>Oc/A:Άﮇ$ʨTIJuuȏ="B‡ҔDžfArDxiv[;"+o׺O! /rT1a)b[ݴ',.<5`h}{pw ("4|o c@Gƅk{1PѦ@0%,2M \ DBhV#p^D)R…EttF*i *HP #K$i-Z^k:ViUZFI">H~[>I©H0ϐ ZbVi&Vl_+ 64-n1McɣLsY}QfIZ7=L_Ե2*mHh&TIР|4|UVHk!f-EDF]ތk)R-{3E<`%uEC~?'( p_@םΒ'WTOa|/St?8?њFxr?mG!nc"w(S}KgI;h]S'ZS7垺Chc6JѺriyuyOycݴ8VOl鬔<)C5D"c5Kt jpcCZTCM'VK{&Kg~uS*A`k੉!PuT&U&*UҞ(U<'zjШgtt >vb4XOaQLIJ(; e\4x[GIw[GP0( "fM빔_6gM4g 4x3Nd+g*OVM3M'ONfB'ClBc8l!; )/rARJ B/Ŷs\9Fȥ#+ endstream endobj 100 0 obj 17977 endobj 101 0 obj <> endobj 102 0 obj <> stream x]n0 ʼnTn_`jE9anހsvfofP[/ }!ˢ-M_` RUH295yC%8%1SN¼ENV!?r|gNS"hǩR㘣$3WwGdOAR Gb'?z*O(JgY%oyFHfSqol endstream endobj 103 0 obj <> endobj 104 0 obj <> stream x| x[虹WȖwyՕe;}_[^1NL;bْmr$9a  hC[? { (bB RO (-hm@Cޙ[~ދ̙39s%x\v w: +$wG{. }#Cx6ՁH՝"Jj[ɒQQ65@CxwXtZp)\q|3d8~a1DUy`;Y$>M8ZKh$>'  ^EvZ/21jIP?9»( | )hI <P Urh&p\?C! -*?J!r!<|+ȅߒdQ p'u"z$-wjV-X/v rPjA5p<~PCx%!B9Џ)U0 O~x vze-S;!6x ~"2RNzԐ{ ю?$#9,B"{âGU @@J؍p<W3@ҬE\^{$Ģ?&o+EQL\z5m0H:|H&%}C+UO4?hЉk]q{yxrc៸䐋Nr/y|MtBpqxTTmR9hse" P~CKzN?%_ !$ -VW%jBɧt!m>.(d VvX;G7T7ݓwEàGעu`_ /wO߆wBzkZDCI q!'Hn%u#hMe:M_-̂[YxZx^!^EZTVmAwmSS4|{' va,yL9:S2f3= Kb1K.C_߃;;p`$ ?CB4$DaL+q߭wz !2Nn"Nd/ُ$):CS|ZWmn9.U]HNThW$(l;OUdx+^w-bo|( B5MɧdOq2ZkЍprg`N4;Q˴El$F-P! `"P* no C%%B*<'ޭH9 |&j_`2^>} BM6"Q'wӿJ §OL%`/yOI:.[(U2-}-d=&\2V]Ll#U'A`7y@H=Qa$ En5$N]Υ?C.*͝O $CVchpV&qC<1Ox>~IŸHMoeB/|qLxlS}'/p^.Ǹ}^9NH:ߢ>M32,#E0 4 H[H!G;~*dc=rPr1f%+ OH)F &V{5VN%a ٪kil,\P]UYQ^ZR\TXcΞ5/3#ݔf )I ؘH]D6,4$XR%dj^1t9ll": <+us2i.)ȔiJ$y4$?Yy~'·4$aqx3 5%/閚&QАS=$7v"744ZTʛhjl&^!jԘd4vxIC zo@_Ue$ $?q_=0f+Xf\~f£:o=$L4NL y9{ڮ.4{Q0VMMM } 6՛&.F$Nxaїh8MDGM2uYw,RܙܜHٚ;# L;Oq3u9 ԂQz%Ԥӄ`&z+ ?]6t=B{U:4%M:cU0A/,8 `fqn@G5|\O7$|މl]Uhsy&zpp~<'|svS3̆in[DW9nSLeWvJM݊m[;9F7t IThg1/&f0ASI-|'q)^?q:O.?ƙUx3vRD@*O" D̀' $hTp@!H z.X;P(6Hcd6|J|p 6Kiwچi^KSAքVExPmqHŁتPKqiɖ}4d4 ͨ*3@y2Zx1r%>ST?]mZЋ 8j., V8@ ɿ}_%P1r}T=~=%]a4iВEdQʐ%!H\&? =ax$=fuH*(%!O E}.>QSK"BŰ늲 W: ifNWf MI ?7Kj֬GUA_{pfd8y#+͑QTV2']Uܷ5O>|<:F/Űfh:"?]D-.g.//R)`t:BMN顚E T-am.(,|ۙ,{; KS[\g{/(^A6Ek31o1o@dQjNu515-Ьjhugm{Wͱ·lxc!XGY 921ţu7bH M1(:4T;/KrԌF}u@M%uzzzz}Z{p%T0Ob^fyv˷6nt6ϧZgWFse%VQrzAND(++.թ^<0Pt QMoxl%5c$Wœ{3K+d7|N{bFk>y~IZlYʠMkW+z2aL (6FYNg8Z X~فȨڴ;k)&%TkV,\ݓ%V.ȰLOKEF%yFʙM 1o*6IJ`BYofZuL$Hڤ1k 5a)i47OD5U~Ra ռaX4V`]Ms"RzK}`07]ϑxb̚y qH,qYq,H*#fvF$X_x8rUa, `?JJlt1XOAj%~Ic-ͷJu H˃+C-NF`0n]ٸî_gRUH )eQV|APAV`5M5)(p0\/(pj+8Là/J=|H"ݝӖJwD DDBh5 ,[X_( "A7V`V55uQ*p(TƮ~KTkRpK&DdVOMV`jn P`q81 ,BIb/} H&'ѧ('3&)0IQNr+e&S@ѿ)STSWѿQ MߨOk!Vmp(sV(99sp8F)0ss8C|ZnP&P`CA >9/NEX'"7O8{SQ#N`)0$g z{9b ȟ"\3= Q`Գ@g6G9*AaSF 0W 38LP_$Fq_E0v+b/xw x{* pBV;8AC5` 4q-c@E[/E~ù<7.X pI1NDum|5A=n'U"DNw4g;鯍Ӹpƍw79*3dY34ZyԆs 5c{Pҿ bG}:8͔娷3~ 2Mڰ]k`2>;Jusiy'I@'tARy31U%ry${WbRs{8^%XYV9\l.Yn&.n=ZT6Gܒnir]|'}Ƹ2rY6.0׃yH] e7}Zd; rਗǝ5lQr˸,I<Э6ekNVk{7_[,ơQn5;˩cab!;+#z8D΀7²Mq))uZe:;D\#ǹfrv*Q7hUu)=?:',td kഇ:ڃ n\(1>39kf!m} ㊍fדλFU@?9 ٵź|xj5:NjS;gIqpϡq mǟ^ ^l\ur}tMyӜgW㞘3,,oƻ,{y`&,Ö=;- 6-{9̪#ǧ+󙝲3r(^ckxZxݬUvJ9faV rd +ҭ\ ;?ScqޥƲsR{xv(',37,j*/gR-'S}N%׳)3tb(}vϵ|ǯPrTBYSSv^N6r匔ϔ|!i3\ \nYRESuֺvظX>]eLSfsɩ-5ȫ㘚>#oty*y3NGřz͎!JjDj:7Nh{?sSgd瞵^>c=#ɟlYž;o(SuL?%OIr=GZ\al; 9<Ҙ-nGݖ'uĸG{cu%}fcNנ- 9QhoI.;1EpkadwXA]Za*p{#zƏ :`z¨0 f9?~zn:;nμЈTEscu!mh ܔPx N&m: c(lR/{׃Dnnw=V+a͌9.v{ѵVyQzFeXm&>ن1F{8.<:2Dcπtbd.Ρq͎uqtqsa[ 36g屮EFlt;9H:`{xFCax<W{0],#2Y$rK:Z[:Z,4K44-^$-ZִC 30 QCa0 ee q(u@Y&43*a`K~ 'u!ۀX9ʰ7+<G'YfBk{v9Hg CEJv `E)̒iSL3#lYGyIvl%ױzeuxdUϗ +r J "rRr;e2QC{QFٷjn-}3kz8|FrO٧<'餝̞[I'6 / U#<*<.i䝒[oξ;[oξ;[oξ;[oξ;`ӿ&9@-a/GS8Þn3W(rN9cp'#sWsj5L?3V(gȌ'{:':[d-_T(O_ y831;:?P2>%hkjA,+DPl+AcWt:g$AEW=9g2B6O sgieUYgS&rN?QW|s#Nٱ.D[ށxǛ,~ye/&|EKo@ij$>󌯾^*dܢQ3TxFx8YyEg$6/=\\K[lwMAv+{ }ɲM @Eu O^32 6ǰ ChP21=6zӁq`mk7gĶīT*z@# [>A\"UT'^Dy⻂hZa ^U*'oWv oky[o#xg|t.v --E[ N>B_Fhl xoxV!@ X"n-Y8R'\mjVKhrmn6\맱OD4F@ (x-U6v徶l^ZmmK|iHد>4X4PT(_JKa3䧾Tھݳd3DJ>(cN(#?+ۃ\;|eQ!:a bA1Kg^b\(K\KơK^%ވW~DYщ=ïD}`^"_[ӼaFW t;UPǕTgՋȥЀ^D>""}Ʊ c򕹰]H%fĢ!hg9OQay G' 30I2FÛF?Y3wxJ+X|w^3؏ /6ý~CK]hKa1 U)eSm èAs  K +0@'2\hȓ%jmh-E>CK2%ϩ>`h2fP^%P߶Pְh,4 97L, yLay^zɛx`Sަ[B].Q3jI40MF ҈M?pВ%rLuA"kE(k)P8eJeަLk:7~> endobj 107 0 obj <> stream x]j } /&n(H`v!i$ןm Pa=CgM`Tjŭ0Kjµʷ',zm 0wvtB{KN pC j@c'8W`('mK5qΓr]NǶ >ZZ*_QNv"8o\ZV51SbTVQ1j37ķkhć)߼NOjEsX]w>{o endstream endobj 108 0 obj <> endobj 109 0 obj <> stream xy|TE0\Uޗ۝NIߤNH\ I4$}aq\(Έ. "Pe7f\Qי(0@Suo'yyLꞪ[9Ns:l"Fk"%yW5zh]dZnB:+ׯx,M쫏Fn/ 4g,12T VHg7u9CxcKmdΓz[UMu>i9u>P-QNփ~'2@>?&54M8^huzd6{#r{|Ԁ ee ]P#D^CȍPg!)}Os(ܧv'qzBSPk7:zыȅ{ѕhҠ%s=^^Ag{ jmDf&9݄/w-*AfԊ7o?~p/yQ-|ƿ; 5nGw6SH^/P;q|e;AZ cLt&ah=>n|%7Zy0?գA\4ai|f(rBջ^>}Y66 O!E3`>0+,@:7E0 ,\9hFM5 $dJ~>^gEdi!qH=Oj| x?1c܃YMJ(B_`3TM!L{_F`֗&tz}x,/J ߊGq)HKk'g>_+\/ܨ48v$h.>t /XFlB\o]Q ?Y!>F$d-9q7-ҹ0W̕qU\ j >Oq^<ۅ.q9Ƥ^=@{1^B#YM;/%exn/,  c6?(RL&DIFn#M匜Kri\5:v{{ w;8o|:4~",^>4M5}kh'hhjhk_w>B{ L y==2T$dL½$CXOY\@v3d<7Whq~-ӘK ňB0 z;ϼp?y\,?AXҸ{ѯ6|zT> |< ?ra.pqđYE%܇Z7u݁fTDaU59diැ$܋(̮g`Npp5wKBxz{FɟzXWQ[|Z/,W"/B ݮ 4x^Re)ȴ̈́7p% AB ;ANA /){ԫY@JA ĿFwWmh$ȃM+]#t څ7~ZQ*%TrLI||3}_Ab4 G7AޅIt0*"{SV 47H< >ވfg/h &.*?tlIqQaQ#s9#B4)y=n3ّdVd4uZs܊'TÇӧ`2"2jz$Țz~/)C`I,JeldTzN J}x4%X%3x&1 pZT*S\#UL]Sf 4h5E{ F{kfqUC TxSz̊H]Ϝ+ҪFɵ=(8fEdMfru#5٠=l򚰩.XYT>lawJ늓$4nx>nKA-[6I=;.6UU%SkL+KXo.%::+e~`ͩY%胓[Vi[zмi{^@}䭐,XL)"S{h˼<9=MAUL@tXq U,# j% i,cіڱP ~04'l|ZGҖ"`ω9L񟈂OY 'p''v28G#`(Їn#Uii7h9$z.VZۋpUo'$/oo{ܣ ZEgRE7ʹKK[jTV.8/;Nz&/|Dco)Ŧ>~5:J=bt%2h-g\ ϴ6X`ɖ-)Ph4irZ+3~Pei?%KMWЧUCsdTt[L JSlŻ%1y<&8}7zn\q('7#,>ƙy5d$dKh.ͤ &P%I%:Vw@FYKat* @b#(Mjys@u>sxa,kOm&liѪ=%hD3n@_7_~LM¨p*ȦQn̖JK7GX$(8'r:APS۪' qKҸPFˑ?>>po55_(n*~&6,F+I@n5f9աVO'#{Z=5?jyau(g{E):3g 8I9̟ %8x.&K]Հ?@{9@MC Ue OB.no '&sB^ǎMCg ͉/C,XՈe4$xߨuyu::P\kՊt<:#4&ON욟4UT㺇m~H|kҙ=U[%tZMO2MΒbYqyL^j`XN>:j51aE%0֘.~(,y@'N1O8m9 -5BZ;-+:_Awu* L7v6w{i4O> Tܶ*hVdt[2t8 pe{R۱?w=,C׭otN'0ٰ~_{Rm̈́z8r9a&@B/[2by5)rl&0h0e1Eq=>NR)SfgAMQJc꽴䞁 %bIO%7eNg[,'!1E=SNqƝ82E)I8P" 4&h׈W%wI Y0K[RnTh,Lf%0BX5hd[VDIm_Uvsn~ށeM?UO,8)uJH9Woxޜ朜R߸9U憜-G99/i/iB{y:XMqT (M|!;STʗ.UWkLL/5Y0/e e#ZFt9Ar!NE@!L^핸b5SzoQJZ~eiASCkN}| 7#<6wۍrQNxXn_̞7}rp\c4 \ccCOC`qbT"bc;?-Y.ږ3W;&m̈ܩƩi#-ѠMQ۝v֧i_̲>7۟e_P>0 `/r\ VCEdp&f2.,dWBBʔ.;6FyKCdFKih a.^LL8d=f=a[:6&KyŚN[iKV4lX=δ"OY,3xF.āg`? R}>tg l#qY3b`rUϧp3W<rדW۵;7nIIύ;1zgU[8Ç^}AD R(ϗ]OZ/4FB>j%Jj@G?o 9xqމyӬK>CθEV5YlurNuS$-:H{,mD"'~ z)͔lk3TfZ^Scfo"'Rg3r?[i=lkLQT?S}'%&9prlMτ(d@u@[SLc' CDi!ܹ~qؗ؂}jػvdi|/r=؋&{/(>Xo~rڒ]iBmI>dLmIA%5KWA^$`A%?K &ؒ_~bs >\VPTTȍʹQj+9x)j ;,7tpHKGJ^ ^YbvgzBN5AL Ed=4e@3*! X!T- BK vq۪ی}՗.vpboz:60^d36*^dC/ _O+N^:9ɦ7#N~qަút) 'tۿ֙ILȶa:LtTӪڤaH)j}ڑMn? (1Ӗq/_d{;&XZP6>0'L4rG*mG״j`!wawdf-42I$ D\:)w$=Sd*^=A 3̝$Z2ٰ)cؘjAbsPب:H6ŋ)>[Mm>c l^cFug ڊD0#L@h6XXN YL01NQȌ@TMǨ&!&qXN0uz`VEtm` ?n;I!vsrB5VX.) 2ll5h[tLkum[M#ݩjtL+2Y L148%)hRe*c.OԀ:y$T0ڀ%lKtpjboبAw0"z/)[ޘ{uz*)zqBRkc7m\/QySnȃ|%Ow_(=~fu'=gֻnW~e.G"`{n~aqiyn~aqi7ԛe edePe ew;33~ft[?dx`CB 9(=@F`e(@0d${=t.+dR(7JR5ݓKsSY9X=O焇xnϳ֓a`ڎE,c"92p)ZV))$şs>N>(y(c .*GϭĔe'a4lQ]uRSBY@ü! ~¬~r̟pcsnճKl=+W9yݱᅫ/nN(,aִفt[:$OI2dNr>AuNOg;`LsyY(v/% +I:j o$(#Ǘz>Jy?8YHys)6W T E48ç-"N,Fъ|~ֆ "݅eڬx܆Elu_gcfm̳13\l緰?a=_6N{!1 m\',aAdc;-E0gE{e'$F9imը-X{Vh :nl5otzښy'rO}[>s[N$/慷_=Ben (a rdvNDx8!Դ/kpA%cسXyV)g&B@!*t q@8E-16h‡)lS#EerAE{xBWhg*guM=}ҳkߧ7 KHEVE$Krp 8:3.[2S]vltu43Bq;8TIu1E=f: va,/uzOyIw^S~4cy}A@,=s#U?sÔ!626`bˋL4ZF'8ț|Ȭrr6C]۳@u(>\o\lk5ϝ{{{7. ioLJϾ 9$ڄgfnR,U kn;d,x|t~!pGRDHslYu/xv&%uSuk Z&Dl}^"kI a0]P+jɉ%'ތn'4$6 кjHA H@injWCuaB*33Na<f L 6湺iO^~==x36>S˟Îҽ{d y?TIST!b=12iQz0"Upu;5\ w ZP &9m<{/E8=nu3;){oޡZwSWm5iSQ*^.lG"_.H$ 0S R&J$88Ů}ULK]K}tM bk]RO:N/%g "'wKďMFs[8nA6 &IVI1r'Ht(݈Tfgj;qR!)g"tމ{)p99LYq#f*f ƎhQ1;(s~yJx8S)F8Pu3oA)Ԗ0BWusmzhm:%=ftv5n;wkcgodYG^y㕗Di8x/ hhFz3)aX)f:N^صifnJRӠ ^p/jF6#JeS=a+`c(, a9)#(_V[ӎ>>?!d :L1f'϶9~T|@N$u 4K]vCڬ>S=f!7&/], ˅zWW13I,R"ED]SΟ{7]wM{IκyM상)NW_y}eXx|I{<$MrOg[T(W:%io~|_mR&;u5>w#H)\JM3T@23ǾsĊy ڡE\BDuC+mS@*#AtCS)b:) k/ginyez?qJ~ }}BmئQi9w,|6ekW큁'u<{M׃? ڝkܓG>+`[#RkOՒU)ԠT]T($s-jE)躔mnq\wd?Rl{-%drR`yEzauO7߅"loX`>Rf.TKbz1 I`z:&tڥʎW=iV/6j6QMGaL~Wd(,U L#;E?xs ٣.mB/7#py/o}:ٖgD$R4,n,BP!偞 I~jh%Q#%j]yyw\omv6{2z󗤿YFIȳ9r,\b#N'h- @;l1vy1Y#JF*)3ɘRxq4͑(;PUy&_ LB~x%N`i؆c?o 6N+9Op ,ʇl%y0{ELG>۾g69ճϬ&E o]/ CZl.XS^z(rF5FMlJF$YO {z="}~ښَEN6VbDiMzgLY11b]6c=;i^T`oHjH^Yc^oqEFV֤wvt|nO#OѰQ;~obY=WJ̚%VIyIIvVf4p5$KF mE?) }dl,v~N}x~+NG>}Ű%K|l77ؗgܐ^t w7@1z[<}#u Bnj3PƣuR0`&,bYHee~e{O#SSdǎ 䈿Ԑ^Rj5Tr-=KQ[uT,>0ST5Z][6e Xs{c3\T[[mMp싻'UO܄4 'JN8M%r$$ixeFsz)wyI8v@uvl1Y2Zw3Ulv#eedrҘJ ZFfC[r7TY{Ffr욕ïq8P}MZ;=n͑d'ٔנ \ ūò#Y.<%2TƔ(Ʀ7 Z!V]=@$H2h]?G4欞cwÊ:Mo{uXX2 Gru*lb@%ce3iFA<=@?ʼ:!IghX^Be02).^kΎYWztcl.+f8ثKǎy${4R=90OOZ:y>3+Ofh/ILɘ5.M4$KZE,L JaLEy*͡ eedV-f6WW[V8+WX2:2o0o$n̸66vTUL }!>4C( cw2摩Y8Sp [#Na*G~*]y'̰Bp~P483#`ʔinbod:Xsp nIp4vI_xB:437[s!;u(W/PFL9˘yvT aqTOZ̮XnmXI%9(ed% x5X-yًW<6ƹ +~{p=o-]w?7]:cJʠ+.y0򛺆W7Xnye WgjMױϨݎd%z_Qe))ҝiT''-ۨ]:q_R@21J VGF aR^T3."LNAoeǏ8%F>_fy z+9ȜL؉, }`R$H|#(I2؋,L#0CRN5 OEZ$U Ox2\NrGe~Y2je6G9 b9Oǯ1]ozPiaaF\b2~yeYg$<2TrSnyp'ۮݮ=؉bCl|A4:w[Z,"Sd2{IׇG ɦk`S=`#!}( "Ȣ_KB-pBٵF}XGnA3``d5Cq+{l}c2gA: <&"7ٷ:*{L.] 7ǿc1\&O+䦕mOܑ ʫکl*$0Upl8mw |YSaآݱ_:}=ܹ-?+!m+33q"?5("ءNiv µFMSϹrR)z}=5'gTҹC],deJaeujY2i eb5-e榤/q/ܨLv;a\`K  ݁`Vlû DЮW:Vx˥ݿh؋+؟qK-}kI`Ձf=ӽrOhn͹b挖gwjMcWO~4`^zs \\t1 A02(&Gw qAu1߯mjmzPbt4>xbiiE("M֧g!'Dz[:{T Fl}P 4"TWAנ_dn~aDnnmݩz,v%[ Ї!cp#! %H6,@Q.RC[CĎU).hsRS\"&#Fnphh啗3]N78zځ1 Ā z08 zB u,D߇}O )ӍHj0NV+W8Kb1CEq[C?*m5̀;f>4pʖkf]= Q:>'ٍl_Xk zv[Bb^O9Y,G@>gh:%HTV {_JHyEI3ȴ\Xo\oY[76[0?j~j*%٬j#i^Acf.'E5' 3,~.5dWKI =WKgEDJњѝeSFsCdwAÞA:3)w Fr ,Qad*prӆݠVq>Ju \ dOE&JH rAvy'[z˯^xIs /VYw<~qodfꊵmktq%_O%!6W~#Ž^)ϑ3{ *GRR۫"I&8N >}_:NJSViH\dzjDFvHA+s`T ZH/P,bdǭ$ϺJ/Ncl=@T!d-v9y]m}*^t'EipKL ^yכieo zN _ %?g {zʟR6: zYbp\>ϋ2G]-/X8#Rl4%{ m˒y/躋T!IMB>ۇ?ON}')'$)8h SN=_qW'>sz'2n¿x.wNv>"^_cpƟ>-NE:9oF{,F}d)b!KL ,de<:-, G8p*#`ZzZY(KOR͆]2% =tȾZ<S,ϔ T8Q7wZ}n<wwT茧>nׯ %.%}GqC}qÿ\>aǶG:g]\(a nTNwgSTM$ꦛ'eL LMLF4Dxbe`@媧4àK: %"8gG9:ܭۦ4Ʋ٥ $H>ۄ)A>c.Eo; 0 nz.gO܊n}/Zz;0ǎPl S31^ 18U#@,SwuѕLDɆ,0]DV'}pai6fq_leL3p h8Dl; 5Zz_ . 0}-_=pxՖO? {,E4)k92P ^ 4cf#N$Fvl)zI iL+ucҒG\ZP:;zLOM$[$zdܰE;-T9Tq}#M hB,OUPiq~ B)J l7+6۷,ف}ӱ$!tˬet/=aVNbs5!m.zQ",4y]'47OVaQ|$;Gh߁6A$Jty m\ApAfs#Bfvj]W;V 5qj-Y^.vYwۧ?H:r4W[]-_z4ʔgSKS̗LEh,Ц CfCRh?1s(LTCc :UX@n| kv֢#!֡Wa=BnVaVa#Z}[MhL͚^*lAK^Ei1*̡<"L $_Z\Nu(IXBV $bF4:i_,L:fnSa 媆`b云kX# ֲ} 1y)\0@#g* 4LSag < * 4Uaw y@* 4* 4Th5: 6ҹd`˿+mt.Y=w v2o18, к٘>Z&[[ -`pag% apGҕ=:6~f}e/I_`6hZZQ@T O = agL!t$z-`G h ˏ?l)opdoQ`țOѨ>`(P˝59ꬄ1tZh uPG$}hTF;nXI邲@:;gP#ȇg;逰Z1UsLPW _uPh ΨFFDq#AT[]tأ%b̈́x6<i(Jդ1)m~ZY*[FrkG2g7s"07 CCFAᦱCH:xkHP*I u(5KVlb-qCMEuWRCyCGݡ#[?H[U Fn:)KP(&*Ay^ڮ l kK;`SHʩ+ mFVੴ]t1LSbFY}:RJ&VZVk:SeF5w *v[ԙ4],5Dƥlt? 187 FT2i'4:Jԧ+Aa=[w*@LWJ CR>hppXοQiekOfVSpn,שibF][iGK qo;QUXZb;7 bs91%n;utzT:T.&FWFMۭeo8M齎aHd&}ahWkHb'qVCMLn&6yny.bKQL:5AzjQ(R`F)>j=eeHO08³!S!W@|)oN>3Ydˀ:~פ|e(mUq>ģ.6DDNy9{w Y;(~ڏKKEr Qe62C]+Y+AHWk]kTY|p7R7Iε) O3ѩ*?b,:Urr&Q/W)ӬCb:SD>W|lR,tڨbC!o3$gQUs);F0۠j: %ɶDT1L7 Eڇȹۇ1EGOUye_ͨ9\Mǡ-PVPiQ5T_YU* Iyh?fr ݋9QUCSf{Р|UԩEõUAu~H>l i"zjCX~(j$ZDX٠$GCj$XUE̶^R-/A|lVk *[n.~`}ktE6*=*-J3[[:!KlhiZkGIS"PmLEs:PotiiH FIy +;;yюhhHʮH{q,SRs-w GK3j[:ZVt`d3.iA{.i_-ڣ+:::Yꄢ Ks"RHZ0Sb()\'E;kب`-+#gE)푵 +i@Hi^rhzVCm}Kc#PG`[;Mtl륎`԰Bv4lΕBHljiJ]MfT[i4 P4Kn=&vt@wtBh^jPjJk:ZZhm ð;a ԎD^hsgCJվ~0ݲ&ZwG#MVzw(l+dcZ溮N6ՎᘠA{754- :RXYB߯KVHaM`,RSpP #Q@csm:Q@%Ed%[N#mS]DjPcyTb،uvRK+Z`"L L= t2u{B)2rEC34U:Z#ivstmGkE` aZ6u\^ڵkG5 ;)1e:Q4?6QVe3ΘcjC|Q#*muYA]?я.]?tѥKG.]?tK<? D 0hq4ʏ+iEYlP٣̾9ſ]^ϢkϞ։ } !C ̆ -v@  bodεB7ǾU,QKYrߥUs\9eRlRlt=jÛn4 OtrNt/[!bh'z NȜ}_F`!G#9l+h q%+oH>`ċ_n p/|!#D!p1_BАs> CXaC%B,wu`S!@,?ô mk{KJ 0 LpT,#@p윘O^G=t:4: ́P7zuCa'&y Uo|29t^規$'C.@Q"{J^`Wo%xeh}@| ΃Pa6en!H޺yCPr/=F萼* &I4  vH;BDm 4 |@4 ] BkQn@4 -YBQY٫4J"-OvޜrxDN ~w(woer~ܝe4 ryRٍ_Oݙ;wKD#i{gG{HlH?QOn& 6yoS}ν(å(39ut1,_լ{CBk|?GtORހ^x)O9τ0<J/{WUi~"R50/$0ڛ_;r2T1?C+` vuLe .,rvvvv@M)Z֡DEgt:FKa F Oc"k$TaA$TΟ+{עRϙ>lGN=JT`Rpe6>$\٣s=\=dsF 8ض&}fo k ҩS~ Qa5vl籔 S*{~F>U1;}T->M_Ụ܄)UU}x+$w(wVN$ZITJL2(Y9r{:2*`e\{2.ix3Lf&+F/2/;i ER"?+^VdP6D3ijSҥ)!APIKk `"BԎ!kx֥KJI|}SAYW!\IcΊ殌i F NSBBfy?,dT -(V7ս=X)4z VfwZWZwOc.66@-GCkH;ZZ$4= & դC~Gu>yloaA!Jّb&):|𜒗z6j|D(!h[P{5ˀ11iZcذ$~(Kbb}WL0:|QuƓ< &<Kz(t0T!*Oz ߲ ?s_Ԃ*Q9(/H endstream endobj 110 0 obj 19609 endobj 111 0 obj <> endobj 112 0 obj <> stream x]ͮ0F<+` WnI"e5pRCyR5N8K5L/2}:o:ݲsqLm~{%<{?$zj^~4>=q1YRצXS;no>լcS|ވ-U|hǫOYVP'~s/6PC,oȢ,8W.K# r.pE  ;ڃ?*QcWkc\d`/c_j vG|,>~ZWZL Z;Yы>wOYQ__AGп@MB;%> NgѿQC/B\֡;KcI¨-a=BF;Y %2 endstream endobj 113 0 obj <> endobj 114 0 obj <> stream xy|[ŵ8>3^]ek]Y[9_'Ib8+ [N-Ĭ/m e f(f)QRh# ;y})X]/߾>H;gfΙ9sYDCH֮`-o!oaKΈ[EH\޻!MBowtnQcO"\ cqT.rfGW䢻-wBΞλP%(v/BYvV_e੨g [G(4E{C9k {×~hpNo0,)Vt/pKp0J}?6b1Fs1+=z ` !;;B 4 t;LdCj]BEǟƗBMe%p<;tD?p^2 ȊZ ~^(n,~_G`>$َ@ϣWџ_Ȟ mӱ} 7\J.@0fvGQ!t= =:)8/DO=ܯy?,Q=~  p%Q)WW_hu"rshh=~~ }x (>?%A֓^r;y[=ϗ+ W ׉A1v-~:K#C *Dϡ7;~ex+2_a}32R ~r+:|ߑw'䯜ep>>.MpGy3 z~+͔g £ UMիP\RoyCX46K$}t?DE'A .w9ux->Cr߉ `Dl"A"W|Wɛ-r8s^-Vs[nC${3w{{;Z Ab.a 9B|_ S*rTE TTUb^V_^si~ N IPydA+ Ur1ЋoV)J@Q) ڣ"Db( GY7;ngăh}7߁4n>ŏa!)D7Q`W!?:!t^GOE,S-/F'C`Q+ ~ /wlJ2-XR^]6t D+=@7G#&7ugVXBc%Dž>c5B?5kacVn(%{#$Mܳh%v^+ @/~|ĉя[`;̨WC?u mh <5m'0o#^B"0 =Qk xzg|C\[/й FX(h)T7w&68?t-Fʅ?acKH{֘8ԏꕊ} 1x=*m@Hj+UliEܜl_V7#͚lI2NQ*F5)k>U BEp^EKTqR CNǔ+rSfUK5^)ZW[74|CIgZc`GG-RMvgpMK5t7Ӯ i ѨV;1f(Aj0uykNo5 eۢkS=(^EQljeTdHa:t4?5|moۼm\q9йeep#,^):q~>%Y-õ0 ĺMFjj`H΄*1ִ\ E5ގ Z@5(ڸ3rɓU# 74z=ToS:m4 o=%-椄`G&61t m,yWADV 8iœGh n]hi@m-楴G,W+ R2QɬA yyDĕSq9+ f 2=&d ѡ !%-ejź ʹ̒x`kTg2ےk:F4uu6J5-lN+%ڗ̶)P4ye#JrYdZhG,bF(YjUg4zDbetʧq_GkOk4<\j[^${kZf:?t]j&D^ JЊQ/fèٴq k&+[V4fB[$lEdVKfkiI%TLj5N VUrFN=SQIԙY| `B/V}xJ r2"ȩV 1=}H]r͟WLW3X;]*6bO') 6$n,#)98gH&K֔T9'S&4zO6#yBH $ĵ 9&j  /j0l؟4$&F~'U߱xqt鄕A1s T}f 0%{0~:րԮ2=1N%O]4!Z*mu&i;kڝ;|Wk2OS*jS9m٪\ks==/B2ԚXӒ*=rW3A"Oޠqhc940CSt ԹC[_Ozclh[c(׳͎ߵm;gw. W%}Ǜי?'Jh87A;3 s+J}MIz:5=АٖIMb8#xMUhg/^fr)6ǗVbҀϛ*@R3u\jVcYV=[OΩkZqfYc95:»omWVT֎],)mCͲد,%e[J|e!aM` 6_v5?:`VW>2 **$hPu:lH2!C'.ծ-#31 ;:8s =0Ɉ8L}f0x u笂Eg*/,F <')QY!X{ 7>P:}h{,x,ʱʎ+Mc;!Y^s%$a6״ԸdVlVd$``1lB{ε@6631+N#+]w"Qi*r-)SOZRRI9"sR§8]X_] b2CSSK*ҵdsrTgJZdXU3zRlXԒJJS59r˰5\OK]ޢ 5 oï}ص *%e9+xZ0# C2}yfTtyaq2Fb`Wf $J&Wn(OVs S;UEEdbhJ\T5F*HtLeNg2G]$& ΕuhFrW&. :L.VSЀgfi5㥌_g<zϻe(ק)8/ }<+GM/Ylg>`ObJwvOrR]֮c]~I{4~goO"9iE5%nWmִ5uWWQz&, *Uo{ P/ G-   6( DtJ<YK D"3).z#[-q v,@igR}(#뗪%Kk,kl&}X/..aipZöIS6YKjOZREeܹ rSRp#%Id@E2E|&.bZ [" J=)m1OC:|kI6,&$˨21R~ac%{.ok>/G>-pg=/`l*D0T&QD&52j4G4iEG3s&erIP7ۻy2S+#&*8Lى~?=b'cw]>؏ M%yKf)rj9ڒHŖdL6!1h&ަ눎:VL6M)-.tJNjU׫՜:\-$c#)ЈmFl4h~$ 'G[Mj5j^r7iZrr>zX|HI}4H\#-ʭrUғQ%X zxѕnc^4'%e)Lf%IZڒ{ XMZJA]OnIiPDϗM}k lLKsy1 r HفlyY׳LleKl>ۙ󧊙T.Y>BfI5*}] z4`20 lNszR]7~{qޟ ={ò *Wu>}7?Ұysön"۾_Xc޳5ʻOjp;BvWpGoɕpy^jT9,m]o%ǭjMfdYRJF$5W5"^kt^ʅ-C G Raq!)Lɐ\L'u ]v+)v[|{)^3ϰƇiHslhi$oLJZD="8E%JT p 8&^.ɣ|ɚ=wk϶5߷sM콘üsέ[˃2_I3Pwm?=mԄ9CAs՛5d"y݊iܛةVΥ6aa#ga*X9ئp>fVhf}mVd\o|l?7iGQ9U"%}@D$^gCZk ,VD'rkQ^׫ 5V~]?>qbr n s:x;9<[*^BsFzu:Χ8؅I[JK4iP FS-ظVLxƭ|^iw>@\?9}RsHoZ~~feͫߒ75ok? i>vi.^A\GF]\thwݼXMjM\Fd),VEyFo׈1MxNX2K5;!6KP쀎>^L<&IrmEͿ>N+R'EM ǧpOtZm G$ kv8 %rĥ}SC@(eqX³{@ $NKd ,2 "P ưpk9+\r*\3[Pa>և4++3+7AXPǏ$}1A@7X {gbcw)j~)M`S·`Bo\rm?Sܯu4Y}!7%6( jbh'$.ͫOtB%^ h?Xԑؔh 㭲QaɶuLx d#`9r@˙>3nA!֕-kM[,9xJ;mo2M%N$4'|$^N!ɮ穒J^{bq~zp~ӑIy'>7᭸߶[+?-ӝo 1[$ ~?7"2kč˘+WU"sr ƺ <$9q6Z=}|*RcZR |nI=9B9ysfOAi])?R$!֕"fS4mh cxݱS' 46vV8ÜoՓȍȋr yr*mmB}qԻu;.ɖ,Y~\Ģ^RMC,y=ēd\l&fBJg\!gǙk)OfXSDz rB4Xn%O\6RP־LsN٪m?)JO^1Z_8UjlO[4#Oo闽EbXj44 $爈0 'J$XOzGxTO?nh%2} d2kB6x) \걂[[ǿ_\c%' Îv>T!ؠ8v- 7*3A*>,Ōq٢39H[[ +f _%mAJYgƺ -k+[(Yp9;ϔwNxҟH@׿|4)JD;l)Ob{K9)R~.jR܌ft('QΌOа]@/: Id'<,J'ơ@1('jJ y3_boڕY+SasEo9yߴl~VF M/)9VCȽWWWܹ\U̕gE. gpkt !'л/9xA\R=B5DJ&mT͹z3a0WHp|p9xsrxkwpE{fZ~-(V1+?2LU;짭SoѠ՝j`Q~7щcʹPl{TP%5tVB7 itVLn-h U!"7z1] }NAt3PH?8'w_O KTUf\ݭUsڰnnL?a t¤l&{fEu@"f=6bD" =9Fe% ;P#6+0Rp ȁ*oU`S`5^֠ark8IuhG֣vuTO*o֬)0\ ̡| ܭқ/R`W*)%T$((-L~t7%A Q= `JhCy?`G6 VAb"Ջ ] VCޑp 0GLTnmGBe.Am"7*# '!zF/!o-,3ۖ \d;.2OZ'=D7 (V6=]L#At1OpMmesIyY̌b&m2I+cPJH1g8齗P3/mKaM-~鷐a͏,:uFNf l YwG,&7: BZ GPKWZxkfg*6|RZi}{{nB]V8̹gGcTAi icvz]38O{Ҧ`wLDdeOw$Ey- A pΗso`zCR`WؗZ;V­0`m ^`540 )tE'?v#L ]==mviEv#`0ؿPb#` (A |Fg,vvxz`pw@Mu 34_Z(p7 0PBmھd.u:{A"=ҎC`f:ARWdn`ooASaI`2]mld(4 Cfo2;J/GT$BG4@M 2fP ^CЀ-<Mݡ]^` PڀHxvL{{zXoHҢ]vv)[U,пW5-H'^H+E]N 1uV׮^Yհz:i}t56HUgoY[5h:@3R":Fa&3 5d:=ZșQ,8~=?X(5YG̠g;u# ]ԜBt5zn9EUس#Pg@5`#55oB3L!ϊbZ39 O](mf6{f0'%ryPkg.Q`[[Xe?ɖyW w`2{d= n tqPPUn)aNcuh a +|3䁎6ЮD)h2m.b52c:ue,(~t#K)MU, Je 5uPYpa ϲEeRrx?uFZ*Rc~ՐI[,D 6pjܒRS\zoQkg9:o^|߼67 ymko^|߼67 yman-@V3x[fQc4Z/DGPwm34338׺Aq5Xi'8zedg>_R=P=_/W%̟pگ'QSGKx!oS~y8 kW_Ϳ?+Q<cݍL)qsJ<5nJ.mAQn-DPw3zX’I k%fI qheHd1SXq %U)Es!yBv "S7K`J@\gC%Ws.njqrJJPL⑛SsX[:?,s׌ktk֒g8`ݦg9-*Dg01s 0?=e{ :j4d td[0fuOnahߥxԋh6n0LUi8QFl}%%bH QfnaP0fT3 \ #hZhpQ/ 탴`'S299H|d51RcdK*zH%=<6qG*%At.Fu,-`HgVs#L~FP7o~+kJD"GayG{6!ɋԸ傼M&Q%oA IA~hS xg`%/TJ y]Lȟ'S( w@>E"觐?IJ2ȟP3Ԧ)X3d|HY4{|LED yc>eM?L$tJKÍ$ h,2ɾg$$GɎ29K.⊳ ,@*F$au@!#׎eѪiACaP <{im=JrZ@BieŐ ;.a5HvA^^F @ >RE PE hhFAmFQ@Q(ꁢ(E=PE=B (dF! 2PȌB (dFQ @Q Ō((E1PE1B ($F!PHB ($Fa 3P(@a 3Pg8 G(PeG(P(Pd(w'@rHFrH 9H 9L=„Al.$J;S@;Sv $J(PD"(@(PDE(@e#@1#@1(Fb(FbQ0D)F?V 7aq%C8{Ч,K(˿b,|cyx]fAXiH!=9H"^.8)3x^/>.>' GEbRWW=zN%<:"RU*18 Ğ{$XDYɠJqgK 9Y~=?7* 9,NQ߂T^Ƨ>|D+!( ] R HYܬ. g eC@fݍ%I-O~h'DXv.lb,{=dOeowWiS(n𓠹G!|} s;YXv!de2S%1@0΅?y0PFt ,*31w̽ 1w9VlxB=͹q`IcY>)x[!{=kog U*-ŇaTɣ4Pֵ{/.wƂ 5T|_ˆs_.MGd)8pqYP?> e5FSzp5"k}vcֽ-3ThI߲gPIb|o^e8IO2+f t1EmQF^U*5&jN^ xl&IiJ5(ՑM+p]tmoN`톭QG-uaEtnBobyQrF 8NJ(FWݐ:0v^uCSrvV:*-˓khQc>nSc`zSe$&b$F55NTMv5 e +D hC3c7TS=*I ' [YhX V| +Fc,uvJ:rc6Xh%KA)E)ecqxǝIəI'^"|Z5!H-vv8C%iAloy0􆪣zх/Eڼ[=^ih}QU-xMUuXg謂vHǪ:CsmcUѱXr%&LqTVg8i[R=M+lԠ'y>t޻"jD hm2Iir\̓zPP]fD(uQϦTr:@5jK𝏉3888@~8%E6Eӿ$0TKuqhj&S&pG!?}M©K$#УBdܕ^,{ 9+bek<#_"EΧ4syJ(Rg%r9}Y d蟰{*ѥt!E30҄ƻo,- > endobj 117 0 obj <> stream x]n0 U(Jd9&g; 99#O{Gȏ1kk#w%{@?1S3qJ5e=1.?8g/W:KOSV! BDfbeICY>؅;GNfS C;Z( endstream endobj 118 0 obj <> endobj 119 0 obj <> stream x}y|U%˻/ yJ0졤$$D'lJjuVhh#B njm(Uߙ7!}>93sf̙3眙/v/kBqHmh|wz!lXޫj<c?_ܹmGEȰ!%8XBkno|ԫv6!B Nmn]HgAzkkGC1}q3կ|ƺG+mMwAyu 4-n/lDh0|HӄQ2MffNWLOHL+Դ`G؋Yx(軃!4.-F%F ؉DQ#[t+=3=B' 0p֢h'ڰ9Zr|?,= Xh9t= 8'*DQI&hZC}ԏnLs+0e|=^ysN_s/rga$.=Lzt%zF{#Om.P= /ū} |A$_$Lqqܕ,=˽;q|% ~7-#D.ѧD?FFd RPj^w'!:zA!lO3l܊mv|Ē*RM!r+. >K^&!ӄ[6p^t 4GG5EmѤh^%z ( f- <Q'gWt(18{@J|^k'3%| c|pK$BL&3rD99LIrd.2n,7=cy7p__ w R,RE/dc1]lo׉/Iҽ/?b )-'`؋}E۹+p ^ga+ވkP _]d*sr;$PLӘ{ab;.ehVL?;A&n!1|?qA' *#$4(\7FY3_x TǨ}p'`U}"Wc]N.Cg ~ dHi$)WsI Z ,258ESx#_NwEȹ1ccķrugx=!_|=iWB.7^%މ3@'X#E:q?{$r%y ˠDt%ً?E>} :7|1ŵ\ g3t-h$&*:%.w;l8[ :@+/>FcǑFGxX/{A/Sxtx 4ʵA[`N~pCan}ū5NB;{j@i4 l@{P> sFH.q OѥJ Gdg2GdRS?)1!bc\NlY-f AY)pz]OO<9S!~XF]Xℕ:\`:e eg))J )<ṘƝ^m(4qz%yKYSm@]6nDVEO.BɧCՔRAsRƔq) m >_qּ@<>~BŽqƪ^U^ZCvha:h*cCP!vbJQҠ%R` i0 SV%l_QMi&("S4^䳈T8 p(̤r!ǰtQv2'SV yPft.<zˀA"yZZA :Z$v6-Y;X2T.1_dž Cryt}GqV^93󔊍u:o+g]G  a> 85%$ny4 iS*Z& îxRA$cM.j&Yh[|ľ1́ز L ug):aXiZEC ]%Y6r@/N*g߸tIDQ7NLQ&nX?](ES6opΊ%><D3 Mи)xÕ;Tay{`ˤl5ujvBټ= B*%C4İ*QZJy <`F D˓Y|"TR_Kc?.JA8L8F^(,~yY+m` ΍~c- uSFeq&.37w*5Fywݶon̒)cȜIB1ͱ.^-t,k'}Æ7 GLo?>1|!}nW̗ gq!'_S } ]r>QjL1V0Ƣ31蒌/weX&Vp#lP G aLR bb66$?=qJ6YILEvٮOys,Ԟ:E]gN 8 :lghqIԜN7Q*JTJ s.vag).TQwZTj=J ZRF-eR6a+E!SSblL\A~ )ɨ8OxE)ɢ/Wͽ8\_Y{^{/}k{|y˴|M7 h%v@xypk1Ǥ(>-G7{A^A_~to#0WjY⊍E (tA1NpWYrE,Ii';.Ѕ.|tmkܰ<9XH`.⨈ x%rH#1^~p)] ^zQK@Uo9'<Od^?9/KqϜ9!_8.}9v`rAפ %v8K% TV<B0ᴙApdQ5 ]],pWJIAIqIqqA[DI⊳^ߒŝM^߶#=C_>(7嶛rʼR1+A?հ_oA>[pH—} }d 2A$oub~wtYR,SR>9eMp60I#23.V9beb,+ȉOHPbpfB1̐ɝi2dq$%,Y S 3rp"N|Odp]WB$t[ÝȞ{  0Li'{}Hzu9aLK(Ĭ=z_.`U|iB.TȥBe(MX"fL$" +mvΒ?v-aCoQY"or6V[SnS %/ 7_-gt]H#Yp\>}9uʴ}7NllOisۖV䵾f37rxRsƔGD_~ -M9EyUQa7j6]ȯ31KCrl$>j%۬>3q ml &WP0fށf>k11p`#$9Uܜ6w{KpYZKB0t0;PFyEy/ `mɱUH`g] x +^[ڃKzfM_+UODϷ/u]NSmV8QZݨ's}-#F1{8b'v킇[CAxN>E5axxtƂCF.L~;BpO8- @1""`pkQT$Cs-\8BnKSm<<ԒLHA//Rk|j@9W$fuw nsa .˨:BbnxG'a_#Dž}_E;X<ڭǙR-%Kk5Bga{p|?Ew]!(2p؋lN;PA}#t r܁1b *|;ތa'g%ƥ7$E:-һ [vMA톇&Mln,<#g*x1qN͓lOL5gM[PQe}_ .q`Kbͬ՟pq# G>C_7l QO^s$579cEoZwݝ G;t9 J ?#$Y0՜D)V}[~bG?y)'S\ awR׻o%)ӭ3twFHTy=I񧋹k5.^Y.i}0:gRĤY;%q>veWљ6i9!aHeغ:nv[p'sKM[õ9x4~JFd6Іsk ց1W?X=UϯnxyRc#MJ %9ӯk\3:1X_6s]}8eI:5ZXBCPj'y87''NC°3/DAmlZM rM,OA^%lSTgR?"JNiȫ ^X 2R+4B4ŕrB8(w/|\b^Ҏ0VNBhDkƿ9[$c$02Xk]*T5.fǧqĴN0g3J8TrsŸ"G_7_ܿGAOG>^sޯT*T;/1d L`SWCs-wymUUًdrL 公ѼetMB9|=:%b%QMNK2TZ8`ilڙPoZyZͽ.pvй)(EA[oM4ߚD~Iȟ^"G=7wv3D,% ߉~7p Tu%1ȥ/fs L31x<@%a3aK‘J&0'AmXl]lkt lCѸ}@︜]Rfw:~l=sȲbwN vYt8MRfaw"XyIrgtŹs7<ϳ'C|Ý|[r%^vŘfŗo=|%#]>TO)Vro~}[9܉ezqCߴ3q/Bȓ7"YY z'~pæ @YU1x ;sr{^%WTVukkgiyכcwuq;velm툍 T'"; \r[vX%{I7J %]NnWۥDVjQygNف{tZ-8XjYz~B[mt S>UY]tݾ2dw:vhWRȢEV-i *찔N"= `B3豓ɟ?҇g\937/>?X8w?3_|^7Dh}fwL]8um}^&R4N*OqLo6mĆ}xasfw_O޲q(XpU&Z*SF<=_EN'O3WY'&5C?"2PJReȱ8֗aǛQDj/oԱ 퀁z{I€1犃<*ٖ>z͖3n/a A#ZУ2=LOlG5}w{s^KU /rrKP][t8y' /OuuVS5KO|jqpSMtXʠ5@rj/Ǻ#-Amhȑ[/T,w_U96;pG}W9-ƗȜ6OGNh+^n&Y VZKtIZg58A0uMedn-6h38泅t)ՐmRŤ+6h Ѧs|0M,M7֠p5ƥ/F^V7zftx>{\'u܋{h2L03fh4fl@FAMf3M:6%aT8cK~cdP-u),헎Innz<8'Nd!D!d9LOgęS2hxR&C{')2Czj(ā] UvakSmv0RU GvKbznG)gp^4+)L=r) f! AX=eG"#V\qƙ׀cbO8›>C{^>w?fyzH:h76=fMZ4wZçTS{M͎6>lVIWD$\G}n4 c0=2ǣxQ׋krf nA^<&Y'(bZ-bYc!vQ|O~o~9{}]zA']_T,d6M=WO[B,',ޱ膻?6aMYJ%yw _5w̾0q\> KT<Ću|KQ s|- C`1YX8)Nd5AUk236&#u4r#VwxxnDaIc'5zyU$:')B_j|j. o7L6ju9g>w4/ԾW{ѮP;޲Ln}j;>8d40̺^]3v9j.y^'nYy1#_׬/ߴv^/xM`uKefWs?ȟՆƮ\IfQjrXv Em/F7bNo[Wϻ{T:z:Ԩ'Cwq9ccDJYTȞy⭧/^y*W/F^1]8GHX؋P_^c /':$I8,LNiM]f:Q2MN碹x.kxv&=C====so[ߒtksK_J;CG}Moܼ[3ͰҞ=rv vz۰ 4mO}Dձ$6neZ߶Q?ag\ eܲ -iԲW(=ˏd s D;07D^䖭O肦Ց'c&^fyL[1p&~4j,62ѫ'Tg -3:7s͠@*Q(xvin" Gd ay^s5 `h-; w4z;3/]Y2U`A_QA,=+=7cT>mND?#毾y'WYw*U& S[)UV2,1R;;Oo`Qn)f=+G0c<7dqꬣF OtZUU>ie9]&hD)RjP-"2%l2qd$#qC7 pVgYHZ: {'p,lz91&?x'9Fx2@vvdT#F8F?y^:Qj=95DTɧ9T岲4P[h0Z @ m}xBIzMF ٿ 3,eRk3D PmxQ  x YU}^xnpFasC#xduh oC[l\0!^s$blLD \ ξlV̜A{N+g%3\G^qjN줗9X|F52Ve7Uvȧ"B˴pt>{*6b>=21n_an4]׀s yů]Aע5:@>"0Pg*3OS G|WJJ}4JӚ]gf+ 󢺬<)5<[paTSn9{Ƹ+8)K;c.\M'ݕtqٙD$E"r{n}_VHК4?Ze%y3ޜtr?z<¿F^NO&$+DHPXS3vݪQ0.tbg4Ƒ LD,BvGQ&_~=Z$:3TFLHfjcvF@%lڝL$T v##v_D'IΡ[\SZ p"%kǹ%%n[پ=r'^WF" ?~?降[UF~kڻ#O?gGI[/RDU>%;p{,}8dpfFrt'$},8$nXmnKj&A ݃$w $VVhǰר]s伔֌%c8s./ _FAb ,2j&V:yJ!VˊnLJQq.^9 7CQ13hT~Ox9̄ f]* >S_UŃPݙ cRRص; I<2 l.$$(9-FCaLWg}q5i/9zdb~7=JӪ\J<1X) ˥@;^EFUjR+c}}/a|{-5x ˿r_o~FAAIc"~da@ 6\"'%)6A*(k|U Lt5eva'ğ P֧筄x0]D_m@l|ȟ=zF?{Z,E*@?ÏH?a/-ߛvZZZnqUkM1c]&OnjߣP&*WӔfZ_s_X=KQ8la-I:r\uXD)76t2cDɋ:l-2xt؂i -1$ku#AaW9T a9~E~FE밄u؀\:lD9:l"{u،Flrct؆rmS'O02="ytG鞩 !?өv|: Цoa_[: 0o oIMa@ay3Dy5¢ 6C~Y:̣+lcUa#4zl,C~&a ə:̣L.ʓ̕: |ȼ1aedXJOzBQCu-fa7xJg/He Ka,~daf28ҙէ@g֣ dagSM-0?`?;l\t/Eߢ4)y>0fU5Ũ5@!B :P;^K-߁zoa B&ǖr(SL(iEˆpz o Z#Q)|PܱPPg j̀z tln(qFr/iJa5o3 RD)TPHqb7?hsηxfPK5 l>(=RȣtϔMoa8zai}hY}/f3V^JhI[4~)M[TR%C2d-ҩJG끚!ڻYI \ƨfGQ0;%˃|Z%XF&TRY_%AoFk)kQ¨g5賡P{gc}t3xT /c)T < M`m7׸ݫsAԞBM+-km79D]}MZQJM5C\ERi 0ikZEGYaRʨf=CcemmPMT^ӕJy^rts].#+zm6͕& a>6k =MYMm$åeH_L.Nm,uQz{bz2EeӍfARnb& =Lz6vmZC[M=]ԞZizt5QMm`%M뽑qUC컝LZ^8ѭAא^V; 19H[P6,q.K9 kaک 0Zj v6CM,Ճ2fL K5I#t0+=&mwsRK[)~~q<4ݦkTڪTkDc gn6Iдh^AW}.7Z{W׭-N雼ϣƭkkxp#j/r=\j/Ib{ @'cL'ٰ*lTcҿ:J u@Qhle, UMYMʴ^Rwtwvtt+ 9ʄ)6h]Fsz)Podii^6B;iFEŴ鳬&iV3uktNPA/7,16*t̋V):њ Tڀlib d_;/nj@zEtAKҹSL\twSC/bEv,ib(lԀ.Z M (3H+*SiS׷._V2v2Qt]t65b;6ZHe7SY4B0K{4!e2;VB]minAa:W)#Ə)/jeM={ MurOs2M[Vhkçx0M/1YL6^c:z,#ya̱`2FPJF+3gWBfȑ,)(QJJJoYu߹i*W'C6Su-Odwce ^P\N}oZ{_K:7OZ𻾹7bpq}܁n (auFnFnFnFndi{7c^& ;<~p-};DsںWBNWQ kA߮ }i&:{/rZm|3dV}׉wU^2մUlo?.:5LϏ/(^/+R>Q?{~w}yݷcMoM啴 : K}NOXxg+\.vfZЇ}7.V߆W~=!^G1K-ȧq%Y5p3F|XnZ*g!D!Ȯ8_;ҕrSvf7mprQSviqH-89 :z95VBTvxpQ@(B:rZ*7uѴ4cen24<*_('*ɬF9߹'ӯ׀2ug?B<R!TCp1!Hah/L^2`y c`Eyaˡմc'vqD6%R%VI.cdGh֢xu:_@V@h0CBiLacd 0O8 9rs ՟FC& |s\rG-ɽEbq{,j٧_ַ詬7yʽW݇~l/#!7ދ @@/`@ L 8 Mmi{vs+`I R L 3/N ]/Ll?^dBaV0h*)WR7f/IfC endstream endobj 120 0 obj 18937 endobj 121 0 obj <> endobj 122 0 obj <> stream x]n0 rN|8#`JcGrbO%dr?_dLR=%Sb?sV G?Wox&.}]~ϟZ7R44K8E9 %q endstream endobj 123 0 obj <> endobj 124 0 obj << /F1 123 0 R /F2 88 0 R /F3 98 0 R /F4 118 0 R /F5 68 0 R /F6 78 0 R /F7 103 0 R /F8 108 0 R /F9 73 0 R /F10 83 0 R /F11 113 0 R /F12 93 0 R >> endobj 125 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 16 0 obj <>/Contents 17 0 R>> endobj 19 0 obj <>/Contents 20 0 R>> endobj 22 0 obj <>/Contents 23 0 R>> endobj 25 0 obj <>/Contents 26 0 R>> endobj 28 0 obj <>/Contents 29 0 R>> endobj 31 0 obj <>/Contents 32 0 R>> endobj 34 0 obj <>/Contents 35 0 R>> endobj 126 0 obj <> endobj 127 0 obj < /Dest[1 0 R/XYZ 28.4 330.5 0]/Parent 126 0 R/Next 128 0 R>> endobj 128 0 obj < /Dest[4 0 R/XYZ 28.4 738.2 0]/Parent 126 0 R/Prev 127 0 R/Next 135 0 R>> endobj 129 0 obj < /Dest[4 0 R/XYZ 28.4 629.1 0]/Parent 128 0 R/Next 130 0 R>> endobj 130 0 obj < /Dest[4 0 R/XYZ 28.4 398.9 0]/Parent 128 0 R/Prev 129 0 R/Next 131 0 R>> endobj 131 0 obj < /Dest[7 0 R/XYZ 28.4 734.9 0]/Parent 128 0 R/Prev 130 0 R>> endobj 132 0 obj < /Dest[7 0 R/XYZ 28.4 693.6 0]/Parent 131 0 R/Next 133 0 R>> endobj 133 0 obj < /Dest[7 0 R/XYZ 28.4 320.6 0]/Parent 131 0 R/Prev 132 0 R/Next 134 0 R>> endobj 134 0 obj < /Dest[10 0 R/XYZ 28.4 632.2 0]/Parent 131 0 R/Prev 133 0 R>> endobj 135 0 obj < /Dest[13 0 R/XYZ 28.4 799.4 0]/Parent 126 0 R/Prev 128 0 R/Next 141 0 R>> endobj 136 0 obj < /Dest[13 0 R/XYZ 28.4 723.9 0]/Parent 135 0 R/Next 137 0 R>> endobj 137 0 obj < /Dest[13 0 R/XYZ 28.4 592.7 0]/Parent 135 0 R/Prev 136 0 R/Next 138 0 R>> endobj 138 0 obj < /Dest[13 0 R/XYZ 28.4 333.9 0]/Parent 135 0 R/Prev 137 0 R>> endobj 139 0 obj < /Dest[13 0 R/XYZ 28.4 223.6 0]/Parent 138 0 R/Next 140 0 R>> endobj 140 0 obj < /Dest[19 0 R/XYZ 28.4 319.4 0]/Parent 138 0 R/Prev 139 0 R>> endobj 141 0 obj < /Dest[31 0 R/XYZ 28.4 182.4 0]/Parent 126 0 R/Prev 135 0 R>> endobj 142 0 obj < /Dest[31 0 R/XYZ 28.4 106.9 0]/Parent 141 0 R/Next 143 0 R>> endobj 143 0 obj < /Dest[34 0 R/XYZ 28.4 707.5 0]/Parent 141 0 R/Prev 142 0 R>> endobj 63 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 45 0 obj <> endobj 46 0 obj <> endobj 47 0 obj <> endobj 48 0 obj <> endobj 49 0 obj <> endobj 50 0 obj <> endobj 51 0 obj <> endobj 52 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 55 0 obj <> endobj 56 0 obj <> endobj 57 0 obj <> endobj 58 0 obj <> endobj 59 0 obj <> endobj 60 0 obj <> endobj 61 0 obj <> endobj 62 0 obj <> endobj 144 0 obj <> endobj 145 0 obj < /Creator /Producer /CreationDate (D:20061115202756+01'00') >> endobj xref 0 146 0000000000 65535 f 0000263390 00000 n 0000000019 00000 n 0000004101 00000 n 0000263672 00000 n 0000004122 00000 n 0000008650 00000 n 0000263849 00000 n 0000008671 00000 n 0000012987 00000 n 0000264033 00000 n 0000013008 00000 n 0000014903 00000 n 0000264180 00000 n 0000014925 00000 n 0000021850 00000 n 0000264345 00000 n 0000021872 00000 n 0000023352 00000 n 0000264492 00000 n 0000023374 00000 n 0000024916 00000 n 0000264639 00000 n 0000024938 00000 n 0000025673 00000 n 0000264786 00000 n 0000025694 00000 n 0000026678 00000 n 0000264933 00000 n 0000026699 00000 n 0000028179 00000 n 0000265080 00000 n 0000028201 00000 n 0000029809 00000 n 0000265227 00000 n 0000029831 00000 n 0000030783 00000 n 0000269082 00000 n 0000269202 00000 n 0000269318 00000 n 0000269436 00000 n 0000269556 00000 n 0000269676 00000 n 0000269796 00000 n 0000269912 00000 n 0000270030 00000 n 0000270150 00000 n 0000270270 00000 n 0000270389 00000 n 0000270506 00000 n 0000270624 00000 n 0000270744 00000 n 0000270864 00000 n 0000270983 00000 n 0000271100 00000 n 0000271214 00000 n 0000271328 00000 n 0000271446 00000 n 0000271562 00000 n 0000271680 00000 n 0000271798 00000 n 0000271916 00000 n 0000272034 00000 n 0000268905 00000 n 0000030804 00000 n 0000057446 00000 n 0000057469 00000 n 0000057663 00000 n 0000058279 00000 n 0000058741 00000 n 0000076949 00000 n 0000076972 00000 n 0000077171 00000 n 0000077604 00000 n 0000077899 00000 n 0000078550 00000 n 0000078571 00000 n 0000078761 00000 n 0000079053 00000 n 0000079214 00000 n 0000099604 00000 n 0000099627 00000 n 0000099836 00000 n 0000100288 00000 n 0000100605 00000 n 0000123281 00000 n 0000123304 00000 n 0000123513 00000 n 0000124031 00000 n 0000124407 00000 n 0000134323 00000 n 0000134345 00000 n 0000134542 00000 n 0000134895 00000 n 0000135109 00000 n 0000173884 00000 n 0000173907 00000 n 0000174106 00000 n 0000174769 00000 n 0000175278 00000 n 0000193343 00000 n 0000193367 00000 n 0000193572 00000 n 0000193984 00000 n 0000194265 00000 n 0000204520 00000 n 0000204544 00000 n 0000204759 00000 n 0000205085 00000 n 0000205291 00000 n 0000224989 00000 n 0000225013 00000 n 0000225204 00000 n 0000225713 00000 n 0000226063 00000 n 0000242184 00000 n 0000242208 00000 n 0000242404 00000 n 0000242834 00000 n 0000243121 00000 n 0000262147 00000 n 0000262171 00000 n 0000262376 00000 n 0000262845 00000 n 0000263170 00000 n 0000263334 00000 n 0000265374 00000 n 0000265433 00000 n 0000265574 00000 n 0000265820 00000 n 0000266005 00000 n 0000266239 00000 n 0000266476 00000 n 0000266673 00000 n 0000266823 00000 n 0000267029 00000 n 0000267256 00000 n 0000267414 00000 n 0000267649 00000 n 0000267867 00000 n 0000268129 00000 n 0000268383 00000 n 0000268645 00000 n 0000268771 00000 n 0000272153 00000 n 0000272221 00000 n trailer < <0942D87F1A50D02C81260A22AFC52EC3> ] >> startxref 272493 %%EOF wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/documentation de l'API.pdf0000644000175000017500000067671312014170666027211 0ustar georgeskgeorgesk%PDF-1.4 %äüöß 2 0 obj <> stream x\ٮ#}Wt {%?۹@> qſͥ*R-i&f{lv9-1q$YO}7 f#j=Hcfe&g:_;h,rkVzR῭3LyfpY0|)???_0}2}/_@Dx"K8?Ԣ;fk-vkYgۗrU%6,~a } Ï"ߍ&yū=/0/Hz:mNΥUnM" ě}{$'qN~d}pSq!#Kɉ 4G`ۆ ]Yꩻt+$ BI1"A}ʫ. i$4݅S;KPKL9u*.m'ҦũT.z?д$"wJzp_ھ7Υޗ~ELjKBJ_SPFE{^lf>}.#剐$ BbHcイ-k3/qdŐ71lР5_ W8^w D^w@B0@{P940=IXX݁];i hMhQPn O'No`})i78Ζ8-9sRz oYdgPOG)F5o)m5Xݸ 0ĿYcQb r=}?蚜xuLQοgżI8b~l__;ZD"2>$"aa˴68sHD% 3ؕ{Fܶ3$ е.8V#/Շ˨N~v&y0,N6(G1]2&bi^Z%ؽ['%tj!Pfz$(sE4 姴zg~uPv20["S !ɱ 5+[~ F^ ;kmC*k`gw`Ygs~ S"V>P'9^c 1>åT1Py%`BD>HPEtժ2_b mGV:NDi ͏>eԌORL3$DPRT@s yU)}vV uqFJ أu9 @@]R]S_wD-cן< lv,F9KLJgG^F՘nJbˈ\Q,GI e9>O$Ahd-R.$̋xg;.-n^;izL(d-ZKWwwjV|&(X`P"{Ao$x34&(#)ML+'D-]bX9Kv^wY#D'}E{:9K{'T PsWG+]sދSk$x R=h9|!`|8f7qxBbLQRCUKeh%" m̹v Lɞhz@z] _'>g=p9S_8[Ph1.؃ LvR\9fgZ{,ejwg,Eu9jZtiP]ɛKt#Y$w)i>?G]$]y! +u$0Gd-1T`+O:jA{#Xξ N7I&ѣx%2ƆبV/ˤ4(%xޙv*\vsR)x_ %9UyE._HU A~FaWQnS;;Ʈp s-a'w[~ai(G*#ھr'P*Qpr+ fI쾮hPDvt#Ѝ!^km8OwPmvi=$H}̀CG8Jf(uT,ǘ-#>Pl*k%E~,rplz(~, ~}~*,@^?!i8GQ8 endstream endobj 3 0 obj 2630 endobj 5 0 obj <> stream xZ˪7WhE[*Z`sY^$q {ߏ[*{n/4RN `'#fO⯴.ŏ?Nog7nx|_OM*?}p j]Pv ye^W۶&Wۊzl+Jbv]RDJ C/=FWc2- [6U({ 8sɠRA o#iǥJV֕Pit*J6ϩg/zn7CTC|٦qv2ustQg%M&Nw)W2CYp.^r4S$gJX4CI9ɴ8Yt ѱ*񸎡߮ӪLebk?4JkSL m*[B%;Ͷ&"b߆5 Ov_G *60Sm61vtD#bVWPAwg,Mts I-)GмV oQxPhk+gl|` Xh ){a Fo] OP5/~^Tn{P.4޷K=@0e0%K<Ȱ,pɶm~`/t ⠖e$U:yogExDw|0rJ6W0#ISfQѦΆ%^ϜQ/@G*Q >.H2Úy3mX\v]1']QSmK6E)$™dA2[ٰ1*b;Atm eZux}OlkgpL&tG~jcM!HXډy1*D%s[bPl 65\ba$t$wlb=jtS` fg04bfSAieȇ^L` go#Hv޼p"_UP6IdtiiIˈUrpn ! >W^q2FhasZ 0ɏqHQ DSnk 22i)M>^0qm6 w /ާsLf@4/1~Q33u{X81f3FS~4o=3fNux`HnKj ]XCyIЉ5ȵ( v%\{$P ˋ(]#f2=A`#\o0~rΑ:60٨P2ri㖣#?J* pS+tO4r@<7 |B]˼/;nJw1}մA> stream x+s m p2<@B=NW?{B Nu4{=pk0zssi8y VXW# gs#_aRSY6{<멪~.˚ҳd(KxCAN ʆu"_czOȂɆ3M$'L6Uۺnl&1pǹB>7OpF'`8QE'>`8QC'@lJ{=+3rij`3- D*3G?⁅HTW6pa>@ Va Y0s-ΘO8 d[}+67]OM`7)|mL _`>]38O%`^fJ `4`><..*0&`l85jTُ`>R0 i/O ; =qsHq`>~0OȄ`>`CFhO8( 001 +† v<CG9~|Cؒ8o`>6 y'̇By a9`f aC`c a+p/1|`2| !WlvyծN_I&P't15:8D$ pX5Ud*M݆o|)PkXr8b]05E#\y7;3ÉMl[ɍ0 'Bh"նsb8c+ p"F*2m[Qm'32a&Nh݋nV_1U`i p"FQd6ܶN gl &a>NhEfm+pƖ<Lp"F+[dݶxN gl:ÉRyZzpnElp"FK(2뚮ùX!4ZrYqudEۉ\ *É-oV>Nzj03BhgQOVa ǁ.CpBN(oG0-+w2rC 'B\_W3[~3-9Nؿ͆Sxy?uq0YLLԜ(+Ӱ3 ZNWu2 XՙԞTbsϪmPn*O?{]ew -x}t߶:9^|7ݩ3xPeKKǵ}H;YCM^D]te8_їg54|\l8̂Z0;(;+]qM(MM]ziؿxC gڸpjSm*{jۿ9؂ 6_S[UAke((zNp9^Of RHs>HWm]B(H#Sd6߇|^9?l8n9lݠN3㚃^oIo4) ~%3z3VbBl;hLe(ؿynCsO` &ܣpDsPU"G&sCd[ >]6KRP.Ll[ܑR ' ~G&N bBNyƉ/ on{X 7n4G_#g;1+؂ 6%Bc<-`qg!(fpD8|A,  'B\Xi`;D?l'`N(&9`N<`;`Np; ; i? 'B\|`;aOH9`8BA9/É2)*0!s^"ΔAH9`8B໥d;2yN(I=z`8B·,Q_N-xsp"E_ <'D!d. Bp"̅cYs+ ,= #v9V Bas9sgxa)!>ț&|}̴UD~g_7JbYYm N.1`Yv,ɜ-bYYʩ!t3;ĸi>{>y#m'2u6GBka֎YB;@eoZ޴B@`Y >.33@(սYOMy?M7bͩf5hWó^۩=:UfҘ5t9m)6.?{9h :וm7y\ zs {}X ݫ T,7-PmB!Y̭B̼̜E0aL @Jf_6X4y} gCOiUe g/p"f89!6WR]|i q!0m89gA k#^V:uFYFyYqU=jmϞz?-PmݸԈqW!Y̭B̼ Μձ'Dxl(uDOpsB0(uDOpsB0hiw a8S6#!s`F ÉpsÉ2?OCsV @_0!sa8 p̢ê'2(!0# spz1tC' l}3;5Q&xt \o߱.-餤c8][#1P=lxָh6E9,twi\)@T n=Sj%<=Tۆ.ett!l]Y+՜lm=|̢gV#tZرF!s7كYm\ޘgWa\>+O ݫk_ƥ<ٻ/aưY2m,zau#JgfEz\؉ׇ~Ӱu6HsV3/pgIW^guFnp%p>-`s>pºy'd5;8ÙhKmk(43˳:FCy.uùgS6jJ:+H7yȞz"MGgQng(F!ʯ 엩~6U{nh%EC̢gV g]w} sY a8*X#X 'B\εx@1=~PNX 'B\N4pÉ2!F# sp"̅DH# !d. 'Ba8a]0!sa89:3s@A3W>il-Y̝B RA puEB`8Ѻ؎ÉvD` &<`8ѺPa-0h]Qo`[(N# KRpuEQÉˁD늒 {BpuEQÉ%wD9NX 'ZW\J+.lVÉUwD9,RX 'ZWTU8aQV1KtMUv\(DݠH`0u 5;D3PXI>NRz?EQp ,f/Hޯ>׭كrB7g)l_׎Æ@iK8|F$(z.zϲZxx!}km7am0m9{wLeQ`]^ >J>iSF)=N~f\Y;'GFxJ.m^z{2:[^C NH#Et׫K{cxHaLe# N` +NZcڸfPrz+p_)bs7iţB[sq){ЍQ˱ʺ&z:4*=X:Jep0 GunhNJގ!5X Oa8v ap:ULFi)& A[ mB 0h]QxG@T\ۄ`8Ѻx puEmBJ`X(wC V%- 6E0`S><νw9P0cDhx j 'Bu8<'ɺV6ÉdW@" 'BXqxN*d 0MV 'B]qxN *8<'@ 1N&KXqxNT`8,y9 N&a]Dț0`8&+%1N p"MNs]Uw pAI&Q#fG_LDu$ik*J_  p?d_{ 5;.=?i`" MEWR_G^D#$ih*MV9L^czs`$ ME_ gmPQ] NCS!zvfI} a8 NCGosɳ%#0σz~n[>xB8|L=q`Wkh-\y]!?ZjMvq> umOquVr)eW5+}GR=c,Pp@WZgLwY=wOF׉a-u.C.pOùBZ]É8Xɪ#JQ:slmhWd2g"g+2QuB-E% l`H"MEqPKQI30I)ju:j)*)v(Hc!:bg>d2|IoI9jxJ)NXzؙ?d>LCO F ;'@c@,Շ7z|0$Nؙ9݋s/wev-&{6*LkUӚЈYmM6ܺ6'NjWG3,s"y$WkkWkA+! Yᩊa[7Aٰ>W@Zh`8NTR̟ 6EKyd_}oF}ǔw uI?hbXz< SŕrQEq(bg `(}]f+Ķ+f8pvцjG*2QuD%|Gm87NjEjYS:mЌH|f8H/ Ċ6eΠ"yn[`UnMM('4__{E Ŋȶ 6pɭekvi Vd [5œ>>d'={0oU1hYNl``8ti Vd6Ai8_*NSkd-~G g)̎iElq\V5S_)bE ?<>+ӅvkKʶ=PFSٯkU mjN W>BNp RQo8eS{ Q%Oxpp* 'VNoOU_  pC%>m8p 9`: 9{ %sl7s amC|d~8i9@Tڅ#ùx ) wT[ Ap|څ}p#O"g>.6]ȡD>m1;&8$5ʵ/lUM;G smۄ؁fhA'> J &3mJ'Yyݘ})*ݕ}Ux"j{AryQ0>Ѧ\Oe QzKvJбC G s Ƕ:#|"ܒrtG si߼A *é<# hù/"5L)UJ;4HsewbNFҩ~{N/>m F?CLf҄#É8|tƼ4!2ONX '`+yAyG%RX hއ>S@fg^PQIu- [YUW_] qm3IMu秢"oK@(U&ׯ" ',3/0%!Tlׯ" ',g2`8QI%3@6>26m~PGtpV^n]ˆDÄqQ@zq$Xtw3ta`g X2%3UuCi&">oGG 803h gW=(ոM]BDz#0. 6mad^ gd86R91pv@Oj2Y>{m8G43N84fUNO3.S՜qQ6^gr m(Ϋk:<w 5L wCopĸ+ #2x|H~fC;Lx&>j CHلgBd B4)."aCR*ZHeۜ2p gm!LÉp`2a0aD̈́3a($B4 #τ9]P 93F[B3a>ix}p[3*!SI2L)GZ1aD?( Nl!_+93ϹL"M9r6I^.# gm afO?Hr\Մ #$Rڲ!N^+f }E >tfM/V,66nX O4`8p4-@w F%z5 @Gt17K軨@z5d BYBE眣o|東|p  UR^W3lb8K!O #J4: $ 80aDpٿ1 3hfIcC $92`[w  Kd gdwP:"sΑJ/3=;B޴ʂ96q Y,v`8 ҞÉjPp_7?ӗGRJ^p ùp":3sѕ&˫8C|ì{P?`8ο91ȭ\qe5j8WrtGB5a gYs/3b8[3ssPN*_ s"0%|Éꑙk,YzzYp 3{D})?Jb*0ȭ\kqxh,Y,?Ne>MI?\^Qn*=}|rΑCw-pή[ Re3ښNk(~,n+?NZh`8[23Z0aĢӡZ8NVBf9ĉ,&jchP tͼNVB  #d'89^@VY8NZh0 gɼ,g|17~SfWv jƛDP -4 36yluBlߔgɘɾܖmkna{7SC}m,4^Uٰ>v%>s^ 5Ee[%Q/ݫm0 7({Q 43k{hpa8_rvNNxXeGd3L`sA_oysο6>H,gsdn] -ygWj60k%'Yzésl8hv5ԍ`Ùl8/Sr#GmwYՠ[t>=ᄓs-ksv  ̫K5QzU-(ta g"Bl8s04H`8j#_wzUOMvG5~m(~<.9r7Vz %g%],gsdn] - Mэ`0b9P - p40aD'x:5T]57NH{ulmhWz u#pp,"3+ASCUsPZh03.3@l0h]C3Xpul 69y ̰N(Ź͝B C4l :3E+kC K`d.8#zn4&7$i3No^;6p |is{Zj7hK~xُUpZ>BɡcY+\NNA`Qyq-=(Tr`Al>D9\:pI9׭)؊&&a`1A\;gT d򨃅`]X5DD5#&. ̣ =}vB~"”ŀy  s òϼXQZ\,:lIDb%6Rl5G..VJ/9ϰfZ_YxγLZdW3h6Yba^4IИU`"]Y̠9n9 3K\9KZ*/.X9@OHr/I#Uή6 *yկ| Ϩ2naRb7po٤0"-" hsb8j 'LM2C0 gGωt(J3bĞwr{9pa g ù+HL 4s >-̝ثO3HWS C'kL  LsNqʹs޽P{c )*%Xs9қ`zpoߩVyYMi;5~pnőúS0=X-zxYsL  N gé4f֫Al8%Gù"̭6$b c8mT|qՕ=n)yN}٦eoLvù"GNp2ub@5w_C}yNd݊*`27g挓]wXnŏRԭC gQZ]τ4{AE{'tÉɄKa1[pd'8ݯ1݊e~&{%Xἲ(=X:/Nά'XֆpnEL&2?%C*4'o+?p%Yqa8B:|&(1 V.NΕN6ZY`#u,Ny\ UX% ɩ`PNͩ*Y74Uh8!#^h$UH*XL(ضA͸0"ټ6p '`LB6slӉdaQc8S8' e >fyRw ԇpZQd8Ӱ??N(^ ' L#<>ABl8`81ͩɄ87'0~& ƢÉiHE|&X= $GY/&`=;<$*kaR|!AWF-aEeQ tw$(dC zw!(JvȒpN *M %p~zrPauA#zD-0 X.[H0V,yό-^RX|1Kc;#(Tc]6`fs? Sgd1X~S+q?׿"?VW^=s.ql s_0I`f ?>-ASL'/Ul'N\q9I`8Q0 T3ws s8UG9' NLr>95. d̄xA9ƭ g!ODѨ ґ9'(9׭Di-u?pf#6~fXxSHILÉvӊk g6bnj?`8+MLÉv$_>bn} kM|\t hڌe鳰ib8KaA[bsZW6Wѫ:Ŋʫ8tx/"abiܔ X^l Q:r..sDي4 З/(wL=|]pPfOr4109j!=ጧMgL?\ Kg'KcpTNW.j8@9q.:sGW4G?/BNebf\_vpϩpk1SrzqlKS]_ |p8W{!JO JGO}O<`8K*_Kȧ67tjxx$^1S]UvWyp9WԨI}}Ѝ?aFY 4|ߎ6P9e8+/c?ih8n.;^|V|vp_LO۳0(9p6U2pVL݈QxÙ 8YZpV=՞|`zV*7X2g 1?xUv?7X 'L\[^ҦnGW^gAi|Z r9jϲeO=64XQh 8BnF4-zF$C0h7܌= i9|>׾4_x/ndOM%{k)DCh\ &%Y32:}Z%y{zk%Z+y+m\' qh&rzmچL 3{a`a&U∲_W,mBNFTn*f9;#T =я3.̬^Qסּ LDMъT9.IcǥɂT Z$yF04Jxε``&Nd" 't'ׄ(a;W9,a8k3E>d1H1.g8k4fفM56y,efXYP`8!*Nax#f 'DÉv56 i8*2ǖǩ9~A<tɢƆapѫsjt}G|NDſtɢF"y|fjfW?R;p79FD'L6RKY*TT6ٯB7G!^yЍt9[3}-DMqvj;oLZzkA_w };ŚޢQcmvg8pFwg2@T~C/}mQwi;G;ܦρz8̬ m`qnt?uUcLu fpc8\&?c'cK O⟾Cm!"1p>ڟi88Ù{FxĞ |XD2@FkO?~2WMSٖ7R:9. QgHWbdXO2@$|3P{5,$yWy '7sQck9#F% B}[R s!ւɐ 0,MZ04H &jl-N\s:̄d`8nւsk4$Év5 g910frl[3bγ4HM +o%Έ:g9XA2@0Hi05f8{yξU$wJaj@pvN1j.s`8*9a1dj32Nd '̄duWWp^TP6.O;\ "Xͥa@9p*߂b8a$V3 pv7Y9gdjF, jۍke}>w͎ º X͐0pBHHpNBBYx +[_5slNxd`57;|{H1` j ͆{\aه ݸJ6 Ԝ)o,f.d[Z&ّ-~ GRXWy/Ô4iNp/ lSU +E $LB0`{'抍Cw/`$pL3Uϭ2p6rP?q PXMBJl`8H9Kj *-9p^JԈ 0{;UKk,?=ͱP6wVVt!ۂph0 ɬ:i'CĺwdOv0Wpl*,em;N>OZB<fOv0gɏk&`8`8asJn=$̆`810ى^Ъv03$@ N M6##v08( Q݊s=^q} xs;𓖞@`76Du l0g1'G? 紓#DžΔ/~3g_!\0M豞ń6b!(` PXwQ!0JgPCHۂjB5`8Qlu,Պ4hkp*ݳB ~z i1׀DUzd ŭ; !dY?Qdp U) Rw)\-ɝy}U2É Rw's,p> p* !9в??>?.x/~|XɠAc8w_p6{(tR6rU6FMRٱ{kyLTA!VpA2hܑ$n8_`8\YmXGC80#i`8`ɠA~~ԍцSc}e8}fra)djuMGgO !KY^vQ|ˬz#Ik)XG ACA4zYwsB/H= .H ݄Adb8B As}ȜgYud^&S0\ 0Xwss= .H NW`8/db$ '+|0aɠÉug`2 4?A -ny+ 4ؔr]~ VpA2 M;{NPBlp"='h =d߿Z~Dnuf憥c~XM`8[][j!VpA2ln;r`8[3JY 6!;p"zd&RH v ;KDnf&Ue1Xɰ.ɾ 'rlfP[#rT=<} DnUL* \`8=G)Qx|Dn%d&uNH ǖI`5ÉJL}Y.-aC|0`6WÉܪ뉾,Z!*Ь5G q)S.)"3en-jPgEʤ~O>^uomU Vu:E2DEv%pu0T`~e?^7=F 'gÉգԄEvۄiZwR.XjԔP3iLھ9 6W)4-ZgZVG5c)]Sj'Rg^vx9T0 9En\W;#շl;#?h8AAqqkJ^αԔ/9(3y|kQ"jfgƵ$Zp)9 t?|mA}W>,ARKS]|0VQ =1[g]Oىւfd.qCeéϲpv W Q+x\s(ݕ>TTڍn:YB4x"*Tm+)dq_X4n;JgJ^=RJ!53J!20SmgUkpE1ŢɀDU9ߨϒS z^64(9NE^^֦)ɴ?bѰ6w8ȕ);oDs׎Z=j{o_Zս_!Y1*dl8I,p 'JHdl3{ g:Fl]?VRTg8_}G1UX4N*MD0үGR㥳ƒJg%k7^Oqi"iVЄ(5Lˁ=}Db 6w8ȕ)z%oKk1nta#;{hx1(U)%.#N]wЋ% g }ލEDX`81 Etdk5M[(ÉR)Jlju⊃8ρo5Jmd(D[Wm )\ nAÉbc8ѺZqA_WWɠÉbc8ѺZqA__Jb8\& 'jЊٞy`-X[{`8Mۃ+f;Pubd_j1]J/V6%O󥎭r:%9j-]_S%۠q򳄦Jέ`zCq9jǥaAN'd딬j\$Ƹv0ù0܌9Z=:y` 14NNЃD p 'j`8Q0G|xb֞:y6Ӝ endstream endobj 8 0 obj 21748 endobj 10 0 obj <> stream x\ٮ}гtX$^d|@?E[!%u$.03W-ԩ"{߃7&>_'hPi'=uggx^^)y{ҧdgcGe0)>˚gd*Hpd?4d~4*|2<"g~y&=Յ;\9gsܘIh}Ǘy8Rii,_U C)?I="yUi:VTO)>UyJWQkwtV,RVKˢkZte*hE.`5Ն2 ]6}HnIiN8-w'鞶)jCU Hwa=.bSJFz8<~hZpޘ)by_z*B<͆e)N7]kc[+?1,#Mz hNbPCppwuy NɅv&6!+*ϛ9Z5$H$XH߄x~rҎ$g%BEٺIPSto(F4IVY6e!%Յdݠ$l5!VL4lɝ'>@f*m{d>R[vߔNEߥ&o^ ]B- AE^+>j¤)+[x1`ςQ;w.]{ԁp()Fy=2 9pya+?ʮ_.-zsM vb!qf {mbֺd#o% ܘ{u)኷hޤ@@/㽤UסBdM_@YIOjuPE 5 ㈬~X)P?37j#9z^jurPOEa~ur6ԑ=mXZڽunRШTƢaŰuwk}La#f4Kɯ$ >Ծ&f6+Uh)ݗfKQj4b"0Vñף!^gZ壁kjzv3*l kΫP7{cR0W6APg3ɉ^okm9J\T:蘥 2˹Ԟ?$^]7#uJ=uCuEթe\*8۶5k]U8+ܬϠgJ~r 4Ð gm0҈Cl3.^Jf\rG''sl|>!\h&nc5X[Bi/oH4uE )*0-37.]Mt&!dU~Ymqǔ Qi=qв{h_>Y~=qwqoO'5S~<_ރv"R 6qqkfar7=}C}kz(`b@/.Ls%:7;[Sߞm+riK'ฉk;LtnK@K/P觵6&M[*Va]'~=Ьˆc"תc55τobLJ:Jǹ$bJ[C0Ct4k,:A5|4YלV z D@P*п2gzĦa\X'R]A-lU޲Zf>NEM҇nNu9m"*ņ#Ʒ6.ܽBрb͌9*q\1)җ#.Zuu%m -6h&$@8jHRԋWuvQk޴[})hA)F8œxGg;?]!Mʅ$Olx4Ws0?)s4S,~zVȯC7fy4o^D"x,7ļaϣhIɮvFW*>UaY^Vז; *?{8rpŖ^eOY")H#1MbŴFџdS NA / ˄#ak=p&|mjƈ"Xg.Q67'w\]h=wKt}gL)>Dx,Lr-4;aX%*xˢX ,pM{r$ĖhaDqKwcc]O_lʯnV]G&:[57-uIqh}3!M70${ GhkkܗS#@jv̷lӢm*1-2N<R8Y'hhnXb;/!T2gѷeymضl_K{O> stream x\Y+~s uI.$@2/#TWIj3 vKJ*b??'8Y]/i jUOA 7gӜ[UbzYgFuEE/Zr,_hIN MJFggf __X./E?vô޴ ƒu7?μvk ObZͭNaCȨH=km4j4xlsS5ed!͌(uPduuIƋ44sc [ ܚ5m?^'1[U8K4ԑYiqΎF3?+p2DŹCF!,%}~~ kH0z%ƍH8 $*Uz'TcZǩ՗j@oQ&QG=EP a8g 5UjU:TDcc~ ľ[4JoY'|PU [2ddL>IKE4? T0tj*lm׹7^)_ ԟsbGb%.Je !y`*#T5$fx~3,Hl~n \%lHdL#]T[8f_\UsO>8_Ko04 ~EiHrYX4*VS-H~5IpOQS&Ĕ+d] `3iNH:Ӕ=XŃ^U-;Uqv$%h9HWVӄXR3 L WڒdVM=1=-:<4:]ٵH/ԥ>$ETXʝM?PA7^o+7c({a ƒcr'Ѥp73+}|ңI…R/L6;nd'ӭҼ0Vi4~,; bC9!G\icӀ|g{h~ZGXSlީ4;4MdzR4WD)fode\39 F|C;RĞW{y^=PI_.C=-3Fxဲ9uYp5Ou䘸3]9Z[ghzsM_՜ju MѮkj2Ż QI;5>x rao4ڭyaY,3pTCL QuO >f Cn7GSGۘ'y[X[{ 5A\KލTKčVST ts(pNYV)yNKOgeriDL#3ZS)Вg,wY{f9s Q_R{aB?aVR=x灯lPnaB4m@#dRi@R%l᪂>}I,pЩ{Hvcg;EYgAbҲ?AOB%7zSa'*v߾uOE;Λ}zdm <M~ ͻK?krﴙjM{Jj!*;vO<HX(g Žwt_l}S咶Z#3K<^D~At9UWv$ʦK :۾a";G4CQn$j뗎N =,gf5z=+xiҎUX (6++r1")q+Tpumx3Gi/ CpV 7a?mt7z}l3/7hfEk:#¤Vǃ^>ZRz$W]+8 WN4ck.@Nm-!R۔$FaPvr8fF^{q/bXAOtdbN&{X lw2pegL+U-+̤?06.Gjl<@l0iKw!Z Qܰڠm]ӮIvb4\W8vGuOeN[x{1'L F0?sոdq:sTqN`NV휅3VcʊSreHŖ47[SZL3T$meKc!HH~fi=_)O'u~B>:yPsH[@wǯ?uWOkl%!<*68Et $]`{-|5O'&Fq@ָ S*>#`Dj>`1WPXnSS>'Ɣ sn-moDYg<ƃ<0Nj;[f͖Cۣ/!E<8؃6UM!Y,!7 ɎPi4kY|&jc8^xDy6N%*@,(LOU=Yi{tۻ(+"Q? '&)~c*U;/F؃sKk;.EkE\o\rC.zme ڴ0Ib1*SLdYXzK]ׇV#Bxap_Q\ba1'˲B߶&R(*9]KqgdD%Ďo)3w0`J /1a9~ OKyV)]ΧZi#3)]!sBh ߂yAKoy1]:[B/ʲڏL"ͭ endstream endobj 14 0 obj 3514 endobj 16 0 obj <> stream x\ bt*`@P`9:})P4~%[h=Ibd׶$Ǐ5.& K_pΗa 秫 t~w5:׷^BۻmiLo߿ˏm??;~v}ͳ<a}\ܔWiobY&ڄq}b.,dhnn|6ț27z|y؅#cJ>z~AMLRḷ0-R^$Β&L}Z<5:,ܩڼ}Y>Lv+dV6mD:6}G<>S7V)ެ,bMP4doLTUO!s/T=JrJlnzwdMNgU ʚe?f0L'WNWKɟMQ5+PP '=Urd 1//@#ȔK;j;#q=N≀r zì)&KR M6(S>m4꺖CZySW 'SLP3--qIPX[z1i M9Q{#ٻJcҰb4H Y\bϩ!X'+~.*'.ˇwpBGPPt2ĂvZɠlI*#Y,'Ӭl`)IQLDEFb꒳:*~G{W_G%Ox$.^|f>L&7:c\$9kU^MrE.QFouꁲhdV&Z߆ZRv\cPb?7?td$G"7t2@u! qj!^5'L!袩ӷ-܃{?/kLJDJS>YP๧".BvCP2߆"̹ˎ6綋[ ? ϴipԲG!\D4$MJ[B?^>AWds=3Cl.V URW$'Y튮>9xc`{٘'N5i eC,;Wy<l]'*qR6 xJ$Py*y,HS9']Q}ec&ZAYHkJ@4ik( b|ټUQBX 9XOK8_2~aI\t0Fm h[X*no:{Q7>xn]S輣i^Fa~ه|9="5f AA=,b応AӲfEZ&p5@O;jqW9":>j^9n#i6q'ݳZTǙbp &VhŚ-40L[wFPoT%*7_JVfCGA@r MϤY:ZJ>եyRŔQr Uj`!%P&کH[J,t=.$~G_Jv m~[ :LvBi (1D`: C}>#e 4Vq}[ L[C;@GApN A3VLѺN9`&Ӎ!NZWOX>|-+,XL 發aBMʭM1]ϻX۔l$c:6|VI!v#q^wIw/ NO=.X1mb?) O[>qc GBp 30ڻ޺e2NbMj-\ CG+v5 ';=߰d"]--&zq\ce7 #)>1@3ma`|uO)q~cKugڠ¯J8P& 0c~'p Hv9+GZQ{ӷgÙI9N&섀cH8n)d;bɾэ2Bte0+qeuy+ VoIۈaHftruzE@~Uj5B.q=CƄA(މ3Y:{WyGԪ[jJ7wڼN#نx8k^z몆ҬվvMiC-onOYw 9F44.<6& tn ;^alFխ%U=PP *{O{ZB?ӦAhzltE<́@ QDyW)W `ӎM./d.39 4r\GeZ!:&=6r~^u42iq?^|x>6X3*8䑦( C]ABƊU!7P#K5>Յekzo C,dnjŇ Mioe3VSX\+"aL%E!k6k=Հ*ect*$x"ܲ<,Jj.N3y,4띠b%\qX>^WNSԴ&{)JsL?ʼnOS&֤a-Ŧ|JVi5F=U;5[BZ%,K$M&/OqPӈ{1 ޼͡X(_ wXƤ=[:;78Z9Sk)#\t6Cu"ڵM{i|9nҎ''spjA2qIlyqx5ucpucv7it,WZF!˔zd3c`ĴRud7sGYd􎈧q ʽ?vgH^oFN$ D|sc嫾Uh e7F_םq+~/LCt3~jL-Uy V퐻[@Bի] bϾh^%@~Lط8 C Bێt[hᡮyK p(=(c =VKLJ&҃M卪nTu{}EZdu.׏­FhB+݂Թ?<~ FVF x +ָ Iń k)gQgH}ɔC*#|>HC.#!"$Iȩ,u=r}%)c1ZThuVi !SMTW]"7PkUcʥQ֒]b*%cef΀>dptKI< يYa7M ǝ1|w?\ 5b endstream endobj 17 0 obj 3983 endobj 19 0 obj <> stream x\[~_ttlX==y@~@69H.eUے|uߋnfٌZ~]"qݖ_~QUm.]㣿j]]ewMܕNvf]7vfiLߕՕզǦE۷߻o,? sRZ)X|WN()Mi++.[}Ta᧹oo|*'G\Ϋoqd?J ˆG(My_~H]zzMy&\t}Ʀ e[gK[tP\];OdDdzt34'Wx:UV *@Pc9o̹G4Z!!{ *劖eήZf+O!i=\T %:4@*T`0Tis[*fS 5yw^Taܼj`sh@~6LĊVq*ze{)W򘎨WFK,7 B 1,[`MҚW8ARlIWw4[l!LTyn^j;~I2 m~on/+ :CBr<3,X|F{G>-!-YyDCުV"!\S^m0>)޸4euzꙮ:z ;&a#ꎐ`P&EnP7({yyl[V0qv6ޑ;F1l`q0("H{ZA)vмiӈܷ8?7`)蛣leAP1 oJfS=0K86amzaȁ4oX(tH=J [(NcwHI0VddF|6JBhd`Jh,'ER2^`09>@aɓ'[r8aB v3Si=Њu"ں&޷CT+:Hl|ŏ7Y׺ _[hbCɘ'*vICQK^>$[ :X}TGWDT4,Fe# 9;霹5֐f2X%ŠRHf1y٤h>O!#ǐq =ӃO6 ST꽖k˄fYD&cÏŨp˙1 Lz%}ʹqМi\Ā5Cmm2<ҪG%!8T_MO{IHI-W@/λ[Q5PqI=)kQ%L#L8^ci}^Y"鲸-)S.iuC"wJJwޤzVzKm4\̣"8싞-ZJ 5Q0a@}kqbo邒hv1#e!fiZ9S[s7P(r9g]~KB?^;q}Qk.ftpMKYKuBʖ9>0w4mt6L\I4*`@Uh(cO̤v8EҤdgkRUaN~ʭm{"8jS].kW:^d3Q c$_Mf_I89G-HEv~(@INҥ6LWMxD̍ J5Tv:E~)Di(N J~+'m/XAwť}S:O Eٟ…N)B8n9ڱB|#:F.`&;uR`b,M9dwԛ dHUB79SMM8N%K(%*k`kTd`T -B b;̥@*ֹ[ngln $5)4>*!j6HMdqCI+ִ/5! 'pf:^HFf`BT#PB԰Pc—=ٮOT))|&Qe#&\w=DFux,#0BN@K#DZH;*"}zɉ̧Jΐa&iFҏh*?MHWT`Sc=rEx"-%$Ѡe( qa#_u36rwFKxnd[fKKX;tZwoЂ} nZz_"MYFG eJP9*\keWDq/5tSYH e7P)A1768JFUˁLJ KOְJ_ ?wATn){@5 ` zn'j^3~6fe}mAMxזɎ9_HL 2Ckܩܐzns K#ʷYZ=12@9MB;Sk"hawfem!}6DD_ 5\:1(8R֬{(bWNI*?+,UZ <"ǡg Rr/jmg"VܞM,ra*5覌QUTXIJ<֗ =Խ]j pkfh8o#q]" LN \*@L|{171-2a;5ꇗq endstream endobj 20 0 obj 3813 endobj 22 0 obj <> stream xˎ-7q?_Hv[fa  $cwrU$ ,Е{{r/Z~~-3O[E姿fu?|Sm3ot ϲ6L?¢򭹫h?^+TYi5U틽uj_:}x||GW/Rc`;ziVOYuzSLY&-oH*AwnkQf_}_fգ^yE'ݿ {q-HtW)s۔%rVY{z4ŠFK;YՃ1׏|8_pDv$BNK ~:fXU@걈\.ؙyK2koDQskF`Fc90ETij4H7\b"jTV(Q;Q(ȼh]ň"AN;E݂_~_`~1k˜ [ŤmfZ{g.T].[H>:QS&ɂݬC6|K pS! wվfO0\ .mL줋!ُsN vRbCLYIC,t&XԂUnέ9V`Vs ^:ZABg "Μ;8.s T8V;@X[=czSz;_EUVǞ=%p. ,J H +L& qV`/2;6*D 6GvAeo _slԛnq`5`q0k>mbqxK)_?Yc-(TvTIfF):3e?{ʭ(4)Dmq xۘ!e-xy=%pΜҁ'+r[vzW#$49A59Hk9cEꮽG.e*f9T(dG+PPqH  e<Uzn1w.b!_Õ&:fNmVhP~ 'p9"Aϵ##8TAZų%};U|&)zwf%pFLwJ.f>=B: ZP`sZŮ8<w-|k]P;^7Q@ယUݞ 8b Ԯwn5 m7rnAk+Ɏ"O&Y`=#hI~AZB@FiDl<탊uIa$Y`l%`fBJ+)s%VLʃkVQFΘ7s^sӲD KBmfr4i (k^fսUg Ԍ=ӜT5L=J!RBfI t́Zf}^zR_aSsҟWILӏE@V8ATB6NI6n qsGcIȯ&.'PIQ9 8TSOG=aי9ʑ ̀4Dވ!G\djS1oy鎓PTvv<Ҧ{KD-e>^U D)^>چX8^p2RoysT6\S _f@0f8 'z=T@&P$\PW=݆8 q'!H @?sB$S0&(_u܆9a%AB\vt>Dk sU*\7l'R)DO"7x8c)9 UU0y"VָG8:  Bi|zzz7Q7gC6Q)"|l'ͣ;6 b| ɞЈXZ0I+dh\p[pTelöXlV[xך6}"`}Rmw*յF4a{=(.sʶ5ZxMI-:4E35!Tq_hQ{tSO$*b=dK`NcZocb~Ո\ihH v4xCž??vt=s0h2om=Ӏ>贈;'OTv詐WdB_zhΞ@` 88]Ӣlt\3izEʔ )dI%2`FLFRٌ. :ʳ8kǔJ+0OdﺘSaUGH׎tG4vႨp[jW@|RP(PR'UҎ6%"jOB[a](s_k9?u.! V P`q8TPKI0> stream xkAƟݤI+j$'-HH=J@ I#& .W&ē yk{$)@ev~33 B˧>Ŕd%ӧ#>#>Ŏd%ӧxXmG_TE>8  B [P*Z|g#]Si0 sǍը0JמccX|\ѫ__q0Q"}ٷJ-ۯv] Gˆjv9ϕmEzwܘ+O endstream endobj 47 0 obj 565 endobj 48 0 obj <> endobj 49 0 obj <> stream x]j0z= ۹CI diX}֊Bf>iVܾ`;L0xrsX"8zRe۴D9Zxs.;;dO#Νn'jp8=& uۧ(_sU壊 h,U] ԗK?o' KdUO7j ؅YsqO=1č~my endstream endobj 50 0 obj <> endobj 51 0 obj <> stream xy|T8~>w̖l$Dr*$$d!RAE@T/Z@ TQ\ պ(M sMnoc99s:BȂV#ꦪ֒!*BYC]snk]ܴ#"dX~qcW]ǭ v#d[mU5/e9*C5|yNoX>ѿ=:R]_i k>gCj!hmixLJj[߷mz,~` X`iB9`4-V]r8]nD CSR3Y9?~~A\ F(Q7$+ 7F>"Y1WY©؇NǢSVp /@y$ G/c]O648DsЫ6MQ'gDC#QڄR|1%DrZތh4WwGD?=G2 _A*"M B26 DD3a܅t7:| Ah"*PzgsJA04=~_sZv>9\ cAhl!Z`x<ϻx#DB&t+6Qx5!߉ZRyp67}. #(4JC -C7MWh'zG2h7w>|}>z 9q I!  }~ +qq7C㰊\ o:Coh9GG%x% >oHBF_'RNg6K_opn 75{ONUL̋E@ # Q&j\V&Dϋm.z}>C؂p<ijq#N|?DLAH))#Z"9BKn>yo^qGL[$.i|xԫB'NE@#foWt f؃}ƙ[ux9x?4~?;J$$ YH*%$"O-rQ9t|Fѫ>ArQ]ur+p?s/.>au6'aGDX $*'VmH4l5Ua? x~mWrV#7 rmd2w?d'XMfoH1s]h7 F//VLt/=Hd<#9ztқax(&?#IZ.w`^@$Jn8];Pm'A*M +1$W~ xH$Iԏh:Y7AK4w%4 _З(vϡ_37#ӸrVx5i%7^G}4\U#ѡtxNzL%WyxKw h'KzGt4JA h:ُ@o>pt637II*~(OXpFà"'P< /A/_h@U77"  g@?x<Lf~= >6t2r{pbFsk3Aq2fR.6&:y=Qn!mVd4Q8$q|ܝRͥ$N҉UQ5([+j5YVS%y)$ݯK{c\bݧSUKQVH$$@ W?NƕrIJ*A;̦ckMYh nobUxKF `YuǕtDZ)t䒪JE'$gevՉQn{Pƪt cEu-&p{U-55U uӪr6#8黔Νc\M7dܰa;},/>-I_a< };â/&¦ϖ-6T^'w$oЍft% }7J %&tG'WFft+C],@@ 4e:1Q$`nZK`hC?Zuc+7H#Y>k'K/=T9B%b c~;`|!BGYK{ȜVI̓f#s (h$WOe(z'RrݤDf%KW&>Q݆v*ٍ=?R\O8eyrɆJSf]G  \2`jR"p܌X'4TN 9vΣѤ\H4U]03K̳dAen lf`y|T9Q{~MhԨ'rR_K<}efg@a\ 2k ƃڰa|<~C冪Eaxc7TS'"HlHP Z?kN6V)ߑe)j.e) RԢ} BRNP=y<{'ya 8 `_F=E^A!_*>s{)2^Kh_}AܨIgGM8 ,]G#qL{/(<:dŃg Z  ޥd!1PlP[{&..>6 Ŷ:PjC ⵆz7 Mb' ~F/q,DH&l@Q(9x'P⦔ ) h4'rndn5syj6#!woFxa~xHE,&bwBWt!%2vf!?%̩;Y&h;'C: ΆEѠuAg!pzvdyI:9&{'Q0,*?߇"V^'rzHxc)2E"KvYdE8XyKIE>;|=E8xr\*(N ./$\4R!N Cp{(|#G@b]3)PuJD.%2k`'fpEM :Q;^X:yBx `~NӞ\Ae=v^ġu!"y =^J3YꝝVp *3#r {="$|yftpʐzf>*&;|bpVW %|g ?㜋14#I(ڇHNWQUi":3;ƃdW @%xI$( //-q"?%Opoq(?`7!8^&|='3fQ_gLyE1ƅN Q˱Q2$  QP)eP?;e E2t'&Չu)Xr`É4Xf4Rdҽ{/b*GFήh듾 `GQE[I_U|r*F]p9Tc܆@m%ʩ)a(?))C&5dӚ3?av^=Pjx۔sP^y=g޷0=eS*Œq$85S Jp>R 1~+W Q~޹!x6z[s$Dc SGrAZqUƿDhrj qŗȦ9: qsy>-41k8jzkÿc{N27~q&*|esŋ .ä c4MuWA0zw% co%7̀K $l8j8cALPBlXi|5woHC=I7CI.e;g(!}}@zx,UC"yb&;ͭ{gX`B> 4UUMb Tf6 Ӂ P:jF01۟2j)"AB^ y&CʸP\]s O9[]>+/Ï;tW{wLK; )0V(+ŭ+-{qd ܠ`Ɂ&D2 煟>ȿ JW/-(TM9:9@I!͙/Ę%cWJG{+M5I;LP& ۅ#Y()l"I2 =xcp;Ibq EEOUOj;8 WPKB y( CRR`+Tl {Ÿm>yˮTڥe!?SoAhm0$վl8 ?5@4mpdΙ(?f8{)eYY$Q%|`y[bYdzd{39:96%=%X{0 <>9!zB\u6#GE=hp{n.=qY/,^y!0뽨cޓOx;4:%}u*3S_,WGHKw6=#du9HRiJp!':&Fs 9`$#&66#hfLvŢ$)9%CQ13rb#H,=@AAĝ3ss2K$1=dߎ}Lvx}RQR_(H0ה_ ؛|UČtphxt=˪[*DZ,x 3- (\9c)`,[o\~O-6ƬX>|ɬ+kEy95S-u\ʍ+k\Z2r )3mq䦖& #2{9 ]9z=w p.pJC_/pғFW 1 (xx%8m8.}?#8E'wL|p;$oq^A!|2SN1f0 "rMt+2=4Yq`8h%(}Nn.*ÁKa 4_EUmcc_&eQR(4`6,kp|ADk?|a~UU}Ut!w u"(׶9oO]r;]KE>H"t [m=.Ck-*b x")h1-& *"cA,5c7j/^{7ndU*P$ @g-~J6bV >\Q[qTbc+8 2w>c,(}\цr# 4`Xev9m  ïcݜ= @ǁ &QiyMѷlHdOqѓRnMx)~s)fEO=!eΗ$HgY޿ G?44ϒ)O:AqLVLEX1ۨU9T%;& U/(,( 3WQB|*|:_;?qp\gx9yrwL"ڷqr?f=~߳J]~ZyIWW7L9 Mp J2$H N^Kr4~e TrHvl4Fq: AJȄO2ol7;mGʁ0)hJzӱٴ@2};>x,7 |E9똷e;3ԖL8aN]^u}2[o{y'$5cs;g6N[3*?K֟hVuG ߪ^ 2gxcsj~;w475J/b`эGXߐ'p0&.DžfZҎ/Ϛn7<>DB|P۾1|prpLsJWo|2<{|n.l%,7  e[L>m5|'ѕGX!TvDd6ve\Ѝ#%Y8>ptOjsAkZ4y`+֊;0 4TfN=va)I)SR.aP4YܼؓpQUތkDŽ̟__y2ケ^<'o 7\j쑱RRڕW{{QSPb'Cp<5dbpFoEp\7yD?;u&ŪY4hwΕؽY  dx+W#+nH}5mx&\t2Ʒtc64]8fd$ (=FɆ3i ^):+[ jivPT#aX qWmp8 ApEPXXr6Qq2 i,h:x]Li&)A\J;+B>KB~Dy$BXnR e'I;N?aaeE{$ja)3W/0o#(f]F$ex PBqrTDxOILf`/뜭=>}6}}%tvX:en.$S]u2O6;&|arKL"h@BTpx}I!<&niBˠ-3O+x5]=~di5yMeW}Ջ2҃l!m *=Tz%{>ͯ>u͘fnhތu?>>cO\|_y떭j2< )v+!2Ē/R,&Cփ >e[̕r 8{@kl@' (0c&xmߝà>Ϟ-@A {A`ae.727eYa(f|1cGM{,{a"yx%;?>up.fs-V"*8(BC rg+&`:c ˶#mn˱"6NFlmB3J1m^_/MˤdZ&ѦbuC:n;D /7WmqsMD)8fN?cZKҵ8v7r!F"FjL=]&Os9\6Z`F,"`3 lIţo Us/?47Z&ElX*܂65%צ؎:E%dZ&-MAo Xm)(Øim"5SSi 3o5ϣ#æ/ZMYB {-z_ [-_+-w wwn5 J[Z%7 ,,-B(GXJEB :d'b4-nsVdE">Cրn!7!# *sLf9{o1;xrͥx'f) 1Lxƃ@G 4{)8 oTrrr WƵp8 ʁ%1`GH#NX^VR;cI Ki3̾4$DzU#N,v:)T;)x( \Mn_﨏}>3:>/4}vc)dXsllbClF<~]w-zsIݣIj #Ao2yh:v\s6͛p)ok{գ7 beg ? Z j3 oÏoj,w \J .hNfPWZ|ٝj68bq7HNӎҴ4.->6ʞJg/M6C XWҔ Y\[mM]ۚ3_x8zQD6橞/Ro]˜H0iaY7Y.$&}Mj5+veoYWOˬ)1"\Hi5i!;CFݩ Jx4\{kn%]ױO\ၣ1(%8pHa34)S1'F1"R^|\2Lh0u5:׾Kѫ(D iy S6p udq>e_㜟mZ1?U|uA_-pŨ5D jY$hѣh72<$v>4OrqKaf%!?{%ȈLȌ,Ȋ\B{-JҨR\]]\}{M^w}]y>u};N]zuÁcu<^GrXL1;9^O`w*@ÝbD$D!{PAbiHw vIՍ+0b5W ps6=TAU!d(b]2k/j/rA"Z:vȍĆO kNqE4|-껞+ucf_r䇿i|nہi C| p|5;%Qf3bdK=+tq-G-g,| Ҍs=&hDIh0٠Xd0d4#!-FĤƮDPCnR̲J$e1lC!z1œJŎxiQyLviOwG9U9LX \whFCp1hV )ϩ|zEm9镸 ?Ͷ2"s7GS5v0vXޣôwNVJ`KJ~?LNva? /햁&ϨL90mB[Q7☂?S@7.}`YZkui &f J#><,Y>7e/(ĹE'QD1!2EE BTjcqwl"09XPE%3Bo(v퇧rc(H$Θ\JX@Ƽ[:P86vF A(}z/ߖ_aCO^;W7|yNj/1iecn˥|S?=wSP}aٺzlc;RTbD4MAD>=WNF+F~[}ٯ8+LsP1(&CHΈZALJ!ʍ4$+J,>X%,2)T޻+(O'V9 mيm@/D9]o:疇a-D>Y~OvNO'ى(V;5EvotkPyڭ50Tڷi{m궗v{υ.qX~h.*9HvgU3V} !eR؄;.FQ8*c<>QysaEW}^"ᶣ',`WXnpɞK!)l/YRIIXv^vCOW x3:$TnH`A#T$2QEȱMQD}םULn:_=_-H&H}Qz?naCٳ''YѦmAv5j/#YMN9pv銓 mтWvOܚi/F,VBh.q)_zeJkSwp_@7?E)Àͼ&q&sfxigw6?3E[͔cw(Fv]YC3G`RI7DVr)ۋܒ[vSw <%x2ŚzImVٝc[ȶqޘIRKb{]q3}btV}߅ A* ‚KN\ |ywڹi}w=Q_J'TGlhY!'80#rL`8J4j( ݇[||w"> ZeMxܔR+$K2 n6Y7߃7A9YgO+/}\m5|`6ѓ#ߑ#B=o Džqի(y_־|ыb䕯{vVkWW%t_x~9鬙0a̽įC=~`7WwEJW(Iq%dxayNٓbOr$2FXy(kdLBSpee1%83W\4%WvRC^c>po~+|ŵ.I7>5 zy`6x{]x:]NQb(yM9h.YovlgGV\v4߱fߥ_bw*VS8PqԓOwH]VxAW}oxxi8' NjOcG}i?# [!FihIyP*w3\{ 2%J1M"{i,C o{2yYz['<ħpY8 M7p{V#@ Pw?K"2̥N=w:q&W/V?7ȆM+[ʶѶU猪Jsbjgsk_{x{{I1inot(n_ٰZ 4B 9zװ* LjyA)q:̡!x;<Ja:F&Y:GhTg蟛Uxp\mhnYe?Rd:t|is`axtXghtB FT☣&ROh_wg鰕wW e{W98iPOPa\Po 0d]|0§oP@Y?Tao O I: }07}07aotoiƄt0L upAi*lbJ0*=C͐>K9\-lt֞Ǧa *,A4PvsIrKH4^h&s`!ATgQG@L:T,-sV6Qqj:`RZ6=yMjeuUx:56vUv]oeU`vOSJ:0xVgaejmQװݡcZ>kU4[ZT18O2YkuMzKQjz:zr/An~DvK}?Ok&K}0MDmjUWʺF{g֠Ӱ^VGZ$zmK6Jj} *:iجV28Ir2MloRM4̣IM]ގoٱoZcCxA~?foWNmY*nU'5yе&M:]ҞZiJ*Bm͚[^4m[t fUg*!} &C;3U&Uo-[P6,q>j6[NMP^FjV)TB4ga1ZALM? lxcp)2 0W%37_+.T5wB ƾTnj^590ڪgVe5r?]Ps`ݦ%{4X[jAיL/V{ЉLZјt/uk#םS.q<RG.u:?~avP/q"U3j^SY(rLi\ݑubU[6nuͰ?r.ٮТV2\RgԪbAt:/6m2MRba 3j%bkR竖[J~h~Tjjqzy &]jפUKr]IڿK~ߋٜZCV{*UEз}v[c6o g=R>/t԰ucVS[?%,w3ս==*EiCd ? baElOe6d~( B_֪ X}Yg2| {pVWkm]Uu&]Z^m0kC B-`BKji;gj.5´TZqlaU뾿[u t;q:F g^F mİtp般aF);ta,/  /~@~TY*G*Suu;,6#5;P'W ,QKVõtkɠ^^ ~t=}t+=@Gw}Gjt6Og#?t6Og#?t6Og#?t6Og#;h@Z>#W[;,wpoI ѢV=pH-A砿O4![(?T~߮Ǿsyp9~9f5&K}zeWѩƴԥ9E Z~wSFsWpcnpWrS"$sQ /op_AYN cu?\k+Pcp.ՙ KT܀T}-,Xf4O5C>K6WG,|S{~vRz]9yst,%ګ65+V w9lZ63:3KsWdѴ#QQ!M)C/JMKekHQ,bGPij<8 *YA BjX(P ղU))>4Qz(7o|pę.ZrOX? C"C(ǁ`Y'ktUD` oX'"pW'u9`X1pCc\ߧv9:ψ&io}+&eXW*a.߷Fxf+-m6YK%^<&(2ϓ260awe1Cz?2>2>2n+C=ⓃչJ-JIehkpt#c9vٯA? H*D 5Z쑊e"h7,.-b~:A?SqK~ʩyŻ<@ᨀgKe464 n"y.#JDK|"{<2_;UOLsCsCN]]]=cWn]:Q'rt08{oGv7Q2S%3w202FȊyo ]ۛts&ORvIy[/޿&uy;yEx:/ꔜ*WSio#Rix&[ )ֹP#+<mfcy\$UUjMqrʻYY*z'Ok)6EPFN)V([ :ҥ]+r-gmJR]Zc״.Dn)Z$5$٦YeUWQ-ѲRrrI$$%)"t AҒ췒D )?J\#nlI d'V/ ?o5q2ۏ_IZ$钷3b Q)} 7yal@E8q3Du ܳ9Մ5v4> endobj 54 0 obj <> stream x]n0E|!yHY1!}=sVtlό!];O=MػeԁNKQ]Nq%܌IrD;Eoj%f}XEgrʒVCf|nΔJ t)﷑J;tt|N,VNulñl|!4ˊl9s.le o r\r-l#Kp{%~}ͼKb ,ΰϹU k\Slo> ;k^[%]ebe>߲d/]%ԁ(Ljg 2> endobj 56 0 obj <> stream x|y|Sǵܫ]dYf]Yƛ18lp$ah6k6,mLH6m//i${ג&ڒ-3s"19g9gΜ"AG@" !lnz- T :7\_#IGH;:_ۂwJ:PM]@3" c낆PjG6@/E.WB] * G~][?7]umTIJZ IFS9Œj9//{-qbCB!(+C*+DùX@#Ȇ>\!}Dw! jBwc3BV-Y-j ՠOgQ>ߧxS4 D{"L4D|7"/jAGOL>"j32 #']vnFauW7)6 ?xD4$"n! _/o+WUT0ij_$^Tx#y?0B_ NCsk!w&YHja>2zA_q6g 2yV!.ʍrǹ?&KU>)U\X8xBSieCٯ@uZMOgŻ< ;H<΀؍s@J\ -| |;?? dH\Kax! o7)y8W-6ps4{8*>w f}0kEx?x^1⬒(te2ATJ GFXvP5$~-8ʩ(GQTv) @1_ qv^/=h9F0"Ƀ=ѝx ')_+.[bkQUac ^O#}@~}ĕt2=_ i }CG8F27_hk} "HUt( " tTZ xDEv2t]v{%A.XR m@*z%$v$ЯÎ*2oEoὰ/4>vKa?RlSS<8e hZxvk! 6T |u9;Q?\啄aA{~~i716XQ;̯q@Q5xZ: j?ú|DnZ ;OB-^c}:`y <*!r&g_$*W&1 h+ ]FX$JūPy| BbMX肪 *WJKrsYLtmTK9dL2uZZTS&DmQY=hjh T.MThcd¹"Pv~R(iJlPUaPzQau37zZ)`>wAw Q&Eu յpt%%Ama:,j¶E!VAjuzjO- e:j‚(^EQ%lrITŦBt5hp`|Qwx:4G@ #FmWTap={Ӹ:{Hա=Bt|u^7}Kۆa@ k\] ]]&D5Ş4Ρ(Zs:űr CMw:M?dACkv8DqnOa!SCIFf#>1r5,yCDv$iGp>jdj%mCG&0 ܖܢ6}(Jdՠ ||"%`Sql%<&5n- @n75QmJtf.Mi1$ZO=z<ɇE 5N3)u] 5AaafnMmC95tES4siDHz)/&f}φJQ5`>jj[*=[nWԳF)3ls ϩ#~yy/ih04=PGj &vo&9@ ׵Mt4qdoZXD^JC|C"a1nhjL-n9}cLҚ@kLjuf E2t r.fqsZWi0$* 'J EP3cNsfل㔼f*"lN4J΅`7(5. .x p.m@+&n=Ӻ>.X'PHjũdseQդWG1׷窟k@eR%M͇5~|-%Ÿ![9D}୭>r9xcw )x3;/2/őCD y\g_EJP S ytjVKũ95Nm&*Խ#]5CkBgA\t~@i5iAil~ЮRF-0<*Diy4`%J尧u xŤ֮rQR,xRGH1‰ݢQ_`"ˡٰFWj=elu0:,c2Wbj|QPm^ż7ƛp l$/;ڛuB2oSo^nQvY]*:]au.O[,w5oy;{ow|94RiH*e^jc;<|ZR-*-I.0d,13 _,UAUTd_Q)# >#!Zsn1%yޠ-cEnNW5ĥ =֋IV޹ʏmKcm]^ehq6GYF [V:TJS>vSa?X֭>]bExk)2Ϧe:H;x  ,̛WVjr(Lo5`TbRLXJi^9&|yhPgRqK?p/jkh—02awl|.Jpc7l!=[H_]0s=jRoEVr+/U) .5?coUOJdUA0"Q? 8Ժ,>OvlQ3,z|lUW1oU0CX>e ,:`T}dG ڝ jCRY+E|bkr9O7^xt]mK\m}1O@h 'UTd笩fN`XӨ*}cшlök0>3X*VFP*=w"b^)!T7} (\;絙ץV[[pmݖ},[ˇ!Z1t!l:Ze:8էy'ˌjũd:zTy<;\1IYntEWT4vމxվVו܅_xA?5x a8QKEw1%+|5qW\„T n*?S%)``G}eƻ{L(/Ν4\2şV-3,KM{frż޺ޒijNhL :lndLłVܺ :!;iqkLmjfygYv/>=޻IMX9_6q{?x Ű^7TW&+=[s^=9wkTz 15%~Lea!hKw@^Uj3;{{,g^)u ř̬o]Y)R)dngffzTYbAXeG궼RͿ?Ù=VMˏ(mnbwZv7fߚME{?IOіn,q =Xj&zTgXc'}[im;P,WbV͜w7[>Zl#ӝIrS zc>WUd+K6*Jִ1Hn>MLɢWTw8Y]Ldˬϼpg2tg ~k֬M/R- 7t얽%97QE7V: 7)!hWPz-6?RFnńBR)n9!5ނ_M nFIǡƣ^dY*'RgZ,&8zI9T22/@`o`B`Sa @{UNrܖs駭+NΜ3' ӝ'O\UW|onP^{dv mCfEMD5㚏5 MK3 NB 3",2OP+$ITZݲu@غS芤KȀ&kXri83r Djs-( 46T5:j ^J.TkW9hxF%7o?W}f0SE I~c󩯦rT3dHn$scR[Ii|}).3n fIW(A{:.s@l 93۩dTȍR%/qNߜs9FZ4jG|]JQ@D-v1RmM;[=" 39`3J*tF;xX]FV6}ZJ&DP -CgU86ݔq+禡}^m>S?_–NarR,ސ]*ӷVc_sW]صdI֛^حo%oN[ieJn ۙɼΊVLxSSMPlZqr#jTO7n.ghмyV.x.õz@>v_&qDpx\L ]i>ܢlܫOF?g˹ZZ}]VtNv ԩ`"-/e9o .\5+ZqjO.=5}˥24▖a 9f2Y>g͡ ij4^{]Nb:EهWm3"Rܴ=cO1 %vp5&fNKbc%{}zp-ϓ?O1/4Y4j 6-p |!'jK-4!ga9el'%3%yKפn6*] T׺Tݡy@鼵GE-*r?#^NL] ' HI'8 &i~-1;T5aCPN_(m mꓛr01mI#@2&ר3rVdvq&nZ_pͷ>g~sO>}X?ǎ\V5vK>G ډ^m^8@p@Ч*z?W~O-Sk=z+[i۝?GO+GM%Fx>8pFIhty3ҡ/th;нC(G}P(s4GR%+ǟ#._jλ91Ǖ;C9BNqN"qj* ;ҧlm`7c`ZBꀏ~L})&}Z0=s'w]\%Bzast罷=ִn]Kjl!7j޻?&ς*^CNz͍ѽʗ4~dkr=_p?)*HטA~,Ҿ5nomB^ NJٟ԰шWQL8W4j9ILj ,; QR8(i$mZY真rB"is+J#-䛆2D_]|'~`GiG}W/o{]|$oF0ݍ )[QF(,B%b2N DV 2>Շ-rH9CJԺuk9=n)X0WFӏ7[D !\FFIY ?])Ř3ZM4h ~󥤸!0~?gt=dm{Žyrj _W6Q]@Kq ^S Tx^Ulޫ%J "mߢ{㫴wwh_ў8^9|m_5C@RE{S^M1(o(S~kU<" FqL^?aFkBݧu u^ P*Q5pSi:upaa8Lt|ZJ+Ypvis CER.eoU6mRZ+hl1ݧin>0 c75vs߆uE<~/1~ͷpwpV\3k,,bC?m>0cUj;j/1Q|^,4߷2>բJd揜G?N%4)^ #emq|/\}Ť(~N~3&ZTh'0Qh.+q|a/;K?Ԍhsu77xCzYb'*0]C^Eh?+M}F DeJ`OiYA'j&wɸ2NW'e\_5h-Z :IףNuՏxx-v8T)E2Ρb{WHo\ƕ_+*ɴO(WנdkI yPI] 'Hy$=2#+]k{^ydJhWޕqm1\EbLЮ8v5Ծ :eqT8qØ2u8+:(`_:_qoڷd+8d+|[qo] R]EAW9unu8k]& j'Q 8snb&t3<<'2:}*O[2Tha瑐p++q[poqJinqA;$ O޳ w1_8YN8@' ϧ78'_F<p543Qu寖qھz~= gvH鼏& DP@AiB] _P/L%@CgCBn X-kT4-BO7 C2|%ŨPJYk pt\l%yt.?ĬhޓqJ+ݡiϢ;r2=L%ŝi9zXm{#_8_X_<&6 t9D[}JԷۧW|[`>p9m]d:HVK="G i7e/RoH]%wӤ;.1{Y̤y$-FS3aqsJ@8S`\˨fGع,:EK݀,d0|@i3lg#gͲ"}PQ|:hY OIP 6EKy|Mn~Y3>b3SvzwӱMhv"LoHayOofc"ݭ-ltwocH35ۧSPqi`#"Nϧ]H55.| H=pԛd#B9lUjJ_/<h \4v1_-3qvǗl9L~ِ+6d_ۦ楑i:4+LSۙ5EaOU9mg¬9;3}@+eLtHr9JvU3\2/[n*gNPФH^;jl0}όf shˍm/$yΗ}َf2v6_uK-yss% YlΞI7\_8C-.rW}B@#"u~@ps( ;Puk@D M+Us@ow`}];f7ځPf k6+C]}݁p}  kP%}`m`AQN#m-U =}Ak' ]@;,*0#+@l}0]PlBPtAa{(A)bG@vPjx-= sm;2 Dz2t2jsP&+Lz;pdGwp&,P/@AH2`G(o ]~H9-nPځ<5aI!,!x9,'ؽCw=nވ|)( 6[TBg,FEE"O`A{\1jO`sP/ HJP;NA{~ H:@H(L}=}l]H۷vn{_OQW'BC'1@>6 ۃd,+W5-_i٪ªzeKVj.\SWneAk6uZFULm "Lbl1ԑ7v Rvmg$`>  ĹB u 6ms޹S GΝ 6 34w`1`GZPNxJ(piUL3SoasugwL$G.p |yhy ttOW\@nPݡ]L l KN5m:;X{QA~0UAr^YCNsfq4zm 4ڃ dqoжPp|J B qkX`mؘ., Kya y`8r+򄊒bf]4(*WW_v3Z,ۇp] IMufrnO b'ϡiD~--{69g<4o6ko6ko6ko6ko6ko6ko6ko6kB>Yz.(ykk<˽>j)Пs?u8og*;36FjYj(vlTW갏w~? r6yLk#4.}3 rn jdVtۿ7nί俗G;t׏Qw?2b6ܽ#&K87bL)kL] AQnBPwyCt"#ڤREP84 O"Jw$J&fLf|ߎ%dd/mp#^A.n'9fu@r#FSnȫTC.R1X4`,7F- 85y8aD3>]ǩve|Ӣ"(t%M#C=l8Szc0Wǥ#+m2P*znN,5~NGeJk4Q-l}#b( ]Ź!LC`!0H1`F)@v~y25cHVn QV&JfS}DTZF>2bR Fi?уl-Jma`ָs!L~ISԾ _K01N,( 5=_;rGMF-2 c_v 1.Ɉ+[Fli2bdtwL'2I9Z FQ&ϒgtX!SQTLIbH5'H:7e< a2\-y73@4ޤñ :Ⱦ15F}B GG9![(*G)@ya^xL{Pc|EfDEnx3 C4MfX5B`PvA ;P J(WA(!|G?pG?~~fB9ڀ 8ڀqGpGGhFhF8q!"C8D!"(b(bQ Q Ō8q!C8!0 8Lab&0 8L3rLpL 11AXq`9Xq`9,q`9,G2N(Byǁwxǁw3By(pDG8(G8c8c80p 0p 3a渃P()ۦ!j8\n.;ћ ^1x%zoU0y \+UP6B郲SPbثPޅ b&oTRW=z^xJ5"F*~S畊J"ԤZЭ CC &~q~O W||k>ѐ 1"d͢޻& o"L<sc}?rʣPRJ!l(.֖b<1(9PP:Z!1'1b܀菝rrh,h,ggc9\5 ʡi~,bC <sp0hpq,WC.6p-51z [sr:&ʆ<܌N̖<1B1W%VjxDL< 1cQ: 0x5{cWFK|8$(O;q뺥pT 77)bQ*vE O®宀k5cK\Ǩ7'q5€`1ׅ٣LzqU Ǩ~|i܊cTT=J}|](NUUT-H"!scH uc$1vN02$ C9z1(ēŔ l$yրΫ50:SCo ud#&y3͢C^/Rfc\@Rb$pss\M-!ɖIʧI\qI4)K._>LI­KE*B gFisp8=VdHf6DF%S cNw)y `0[br! .Pz]ң4V("" '`bla|-0gEU߁O+6,fG "D,ue9X؄v endstream endobj 57 0 obj 14415 endobj 58 0 obj <> endobj 59 0 obj <> stream x]n0 LT+܌d*Q_mqPQbukԄ;=I$QY ]Ժ綇TZc3 +xbVԠatDE2Kl}(4I2YNw98||ArMg=s|`m|= ?3S kb 3j%Ϸȋ?2ѳdB={2)%gEBݽA{ o U}Σ[ endstream endobj 60 0 obj <> endobj 61 0 obj <> stream xܼy|8>3+n,aYC#ƎӉ!!&wEb;I  PBK!!8%(} $mh mٵIھ~~s?jȊv!)._W4dBo ˶lZ~KBh\ww1!{߹lݶ=# $@hU+,_VnTl)n3d8O|7;.}W6,[27p%W;~8_r# B5mظ%E覹z r@CydXmvAt8]nK)T4S-U+_A~&p_E!F^qYZ$׌\h7Jԋ~@8ԁ+|~@7XD>fTd4u#~mBNt5NGMB7xj=w?F3w=4<&t# :Na_CNK] /!51O@N܅"WqG׍*n :>zlVQ\af; &2-o6e^E+5Z=DEB8wV܍ɛBv]EYCbGSuO%O/Z9pGx:_m!| OGOn"_%ϒWț$ʸܽ KFZF5F0B p|+jZ p7:nC_E~ |.F >ފ[{74>K8|HNkȟY$ ?ty\c!i|#.ŻGky<\M*/G@[Vv=9ГYh7 K6^y(q"&@O=6G{%Pe1^U?;5|'|~ſßɑId ̨\Bz᳈,#WQE~A~K4]]r?եttSuku~3__Qן֏C{mkY/ 4S>s_˓x C6q? _ŏ Ed{|KrOkMf'{=Dѿ^%A!ïrKw]ď&u8I%rZ ]tG7q=28y݅F=nǹ(>|DF.?FZy?79@_pޟgRN@G m }7(gN j.XF!ۗK6(dY3ȷclЍ'4$% rKz3YxA/Nsg^ q:t߾s{5F==Ku£o$θ0 O#{z=( :rY&:hU, Ş 㣧P#o|96{L.9F.v$D_ɕ*b V_ckNcӀ!ˢ02i=-]ˁ"M?Ort'8@5K>)~(Hd<Q1Ma-6We+Q(}p1(oRR85gzEKCO"%$cW<+Ʈ߾XN>ʂϠ15/^WǪA.PϸP1ghǾng=L>a s!A$ı ӓA]yˇ FJV/oŊ/nӆ9ؘ=gu)2c}] o_ܷxߒ]K(;Jz__1>sch*JPa_?簂p"ϝ$}q[\LꥄkzM_q\32 ٣^%V;g{ܫ<+܆a}_w{lW\—8 q"^A@]|uD\S0&A.R%F SvXQ`SXvsk 棖Jb,v ᆰ] -Ǭ鐠D_z.nuQ~.κh Si߀ҳO`*! {]aT@CPkЯ-I9zv8ۯ:ܦ)>l92e=/o:`uAdpZ1[R*J5JGBR!Kϫ TNZYނŷ[d%`s0#bχK!Ѹ{R2(a\-(VA)o.s&2|$lzNN8~F|W&UB#+CQV*Nq_'QRf}Lx2n Y]lVElkMUen[E;c]>RM4iBY-x`O(&fT%޼}d.1i}$O=4G|{YUGU*Ӑ:ii<L]gFT%[[syKtwr璥^dXBK)Lߪ(U6B4Ļ'Jp|e4,hXүd(#ԙ.SFn ~w9TvJ$ .C~#)@K7ZLo3#H8B5)P|ZJ UNj4A4zk":krj _{cn1ה 7\Kgo޹!Ut2+u9 3]8W 5ULW<(6bC#v9-%e|TR}_ɀ;_(WbO$Ry\W>9.$%p>@(oj54`E $ gQiB-`]iwih^^ `AT}~^LpWaO}3Z`uL#gN5t҃u ]8̓Za?A=̶WOh,"T,iI5#kq 8&"iᗦIh:C]Rdf7.̤2kj}ڪL{5C֧N* 5^4=wf-Tܽ) ݱzǿrLHҪ5So+xzpÔ9urjY/_?q„{LE^!ol}tŴ< p q<#y3@B$:tD<\;OY :`,ּ}Z*!A#x(pP"j֎ 8i5 se{i@#u`r_lp'4^J '?Wk[8Ej{eóVx;&4uxONQ5//mp[4L5w_uU(o90fv'N&YYu ֯ї _&i+蘕VX Bq8o?a?i:`pu)6 Sۏ,kLRdKҵ׶_we|+JK'Pt$֐{ڒk9rƈ+8Ѽ5t1ZԀsJ۱I0dJ愬ql.{Y eʞ)sTZ IuѪp8 Jgd A|ԓ Ɣ`K"l-~>֒ Fl޼|JNWn^> &ܥ9 Ld7ZNNBd:J/od*8H)d.-!9KOH%AݎB]yak tMKV Zd#2OiZZV|T awޡD#D, FtHkd?'""|x p@T|e ͪ0 yU19H.5i%`ECK]Ѧ_*bHhY= e$ 9&{JتmxR??.2blXWr|[Q7!Q͘XXfXyŢXZBw cB_X2?Cqy(KCR0H 49!Vw{tSY3rZTĉbbX"Գ8SN`$`AمE CTGT(̔Kq* L5[؀Le! F7wM) ]n=4wꍱ(LzJ1AKn:#+VB7|rliJ0X WmNDo@JhA'9kGѧ'G>&4ޛSYx!y$BFj+d#j S ɜN~H漶_$Z~mb_3zP?({! d^qެۻ,ո{)k&\lݡ tt0!uvK>ѯ3!hHߓ)|مQt!I(,IK N2`NHB% +OVʃRUF|҈ck '\9ZE-u5 4U"ms4fѬޞJShK_/;XywwvTPq'KO#CC*^L';0D{МnO7h9tKbUӐ1Tw0oX9spo_}eq18{t'g5[,U_G_yB_'%SV\{j4oȫyű)T ^1?tCqcsc#}~2^jN*.iݳs>Jom ]zF09 `u85aξc‹R8L?<$%31wgR]uwV5.D}mUMWfMFDP5'_ߞD [cǽ`ȡ)յ,M'$Sq;wgU—-=⡧{^@4oS_6-[8~ӻnؼuqЋxቖDR$ =DI-b' oG"(3Nrf߭ INˑ[-z[T2p[֠V f|Ђ[0b!9:\<<;L-* fĩ;{g|xC=} S=-{m)5Р QyEG!:47 SOʌ%DÆxG|\Eθp$(p׋z9[/W6?;[|;̎u};mwx.x3] 춪Zkj=Z;ԭ?O,\bZ},į~sl^kᒲc)>4X X8MJ7 <2 ċ2HOCB JHN dJ%tVTҨLKUJUi~lͩXqUv[OXOZ@~'g((j(j(dKd*Uj*i9M?DtXh $p*,g8CY0ƥ%qjF)N)ӿ33\ɽ3ܗޙI}]?8~.?5c>bzuJ,ʯ-0 *;Gt#fڼɷtW[Z'RʊIL-I-~ASk~+>ajG:Ad8|=ei08G)6aFmwz>s?#oq? rVI! ]C J I"PTBqsBrIB+9FnK-!P"GNp|yZ P͛%6y9hY=p޲'PWa%f79I"'ݚyJwdXljO&5ZoAeLi4Uɸnù-D[̖k4$-[DgklhQRB /T`z_hty;pp J@OlyCG*,mP36:Oo؏.[#n 8Odh !۰&1VRĂ(D;UH @ J,-ϓ\)钄P|9]~o횠2Rt0]sҎ#g!ftg_XDh}f ##Oʒ 5#Ȟt*Ce[hm5gڻ*_#@%0EP]BOZ5s2 ?턝"1~$Bpy_`+jb~ Ѓ"4v\77Y?Ƣ4QvdrYdM qmfLxp]tm%=F WϒLLUg9y芕oܿٳ*!f.\`Z[״7H"J\as Č{p-md; ׭/LYI&c|DYQ2~Ԁ{uFdC۱=Fȑ9Ulr)]>)͓.^X;/~1|X#MːiՁGCPCITfW/YBXt9Psf"~whZѴ1tAgL@K5 hZj `؅)0L@ -(S ;pX aQ FRUYeJet24bc:c4DjheJݙLUXr~&8 2hTZ:4opY,SJ_رx**9DD,fTJM2QeH3R,^^f\v$r.C#l05#~ u    Bm&3w #zI^l"&OEMB^&T yـe`Ddmf!SEaů$m2sJ :zR+#>,NI֜]wÝ(g[v4~!{*KPV̒s-TM~*flZxO1!鰶:״ vpN;;\Yں G;6O̤1ŀ/ƛ1=tݫ|v.X>|R.g=F(M'n Bk@? Q4pCإXJxt׹ !O%xtСNĺHyz*qlTTh9B~ɧS+z$ DC%%NTd/cp(B"]~$"|BHX_zd }&n[^X[؂]Ą&+DՏ}UUroTX2sF`(i`ފZIp3Xt?_ir/yk;js |„k%AS2F+5uR$|ZV#"I\].3q &2 ۠X[c;9̑+O> /#Ž ;A2P9PKJLLJI1M)n9Legg^6fG)lmY0b;OGlikisq <ƱT:qb"8qڥ.~g.2dOkveB1,[#Hyňej+I1π)R_jI<9x(q ;A6")2Jb30Q4 b/v"F*cJlQlWlN[TPdb9ńI`;l5]^Ig؎ZR!Ku]}CKOOsۊ AO*BWoS)k%B:9uЛCnC*z#M_)^W.Ňqtf [iXΜ̐ʌef3'2ݎ 2S[w=!b9Q]fe6VlI5ͨM3Z̹+rښx9o#N0铉T(X$)M&u4[P5`Oq17WL}ѝۍ2>3^cc";u;;w$xǺnA{ơ8C[nZo\;sKq9[wM5Ӫm@Gl?y]n9IMM W !4z-d/u' Nip#ŖYv JJNһ.]I&|3py[m"B͊ t%(`쁈 qJ|YWc6wi 4Ƀ@I3h 6XO>PYE5[{4D kp65Vd`ʮSH[_ldqh^ٗ#rN§.΃_OTRUZ}P8ɊU^6P=cQJR䩯O'$Oo;C'Q<𰯁,=X-?Dyjۗ-ȜHeK\kCgxqds%AQw;vyD}ah}#2o榙*C BSj%qqMf_Աx%/!b5okow'~hl6%%X ưŖx4 q {gj1~8%>,-T!x!1bFkL6ȆmWvX%(!N m8 43lӘgl@uL ]2J(42i;0TZ$B=vEwnFh̦be%* `/yU CYHtcQ< %FDw2f֥D;DM)"#p'b΀g.t_YZ0N<nic/AmY-˕ļXڵѢ7?m dl#hC<{wV$^{>Q'47VqY?y "VP(L|)nz ~I&i4CT "^?G iތX"sri]4:·r>jj,T,' c{q)EQ9EDLe`BG4ܡoso\ucZ\?>̿uvY.e!.?1]mCvhk~kd;?߇|.ЅܗlBLJ  >õP7Kw i@e\xU4γߡφopO|p&?uF~cBk)G>3ڽ Mϳ:49a(BluXȟuW>ԟПQo8l.Xcc:*g.har#;fRV6epnb5KX_YTֳqP QmVim6/eH:p 6ZưqZn0@{]0>+l9X2Zqn6j1<-a.?ٳUΊ2,gf /3/5 f},;G"3;*k#]?^G5fX`RQk s&*Ժ>vVU.4ǖqО=0N<'c|a|*_xX1rF}73 =mfsW=n%njMWͪFݱtuhFF-1Rn2TA罌u^ѥ]FMfk՞+Xoem.{T\_ 햳'lpt>Y]UePN9WWP^Wm&ʆtr_ϣXKNpNoMr9]+观is9B:yr=MLqѳh[:U?Ȋ;}VF1Sfo;8 Tz:Ps!4,z|X\dCf_u.Ӱm\3ZQY13錷eƣذ%LTZb*Q>ўFs2Z\s;̌i[O{uϽgѓEX{, Hz`BHkt{Wfw{W\xXSw@ZiM&^˜X3iӤLpI:@ڙ8;^X8#C {SF { P%xd.r#s.gsqzi5W<a=T8F4ch@3kq%&Gl y1 r*QF6%0kc 6!4jI=?yϚjІT8YE-ԙѐb3M%dd/ذA]b :X†8r=k8"w*l(e0N5P͔s3M aFFS0G0%L-l}){~`M]g~ s8b9ũD DÚ9 :2#3ZDD ;Ot TjYTL4t;C#z8eȈsf$bʈR|0DCup`6UHs0V=hX=h0!lEVdVc,9KǞ];wvwji&jr8 ڙY0>S!X+ .#Z,F C|+S< 1+r?,`UrLg*0fo1lO•Ug(2kmlfg0 fRC@4̮9 5 d&3̨+K-E1 ՜]%NJJ>ewB$" 5!E\FYs^%:OsaNbڠtN֥a{2<ީcN4c,;܃HI؏Jvf#4_Ǯslb3,g=HfƀbMC׻0*iv-2aXsSVGMacSAb608TH>ѷ;{-QnT,ݼMu'gŖRjﰋIXg-ޏ.gCn % ^&]W4L8<'؀u-S*-KB`)#jnVە!X }mIt]seN&uku !NbC8{$-$_n&Mՠz TkS4N̓L?\N'$"F<A :F`4=Ln~>b$Md0)s*OhǵoOI @DPDM5z KKKKA#?M5=O 2x uDX ^)O?!އ3+eYū8C+MY s%~$we8 p >t ̊"Izge"w;yn*6ߑA宓IvX evd5'iwÒvSʷ%Sk>+oefVKH1 0qUP?7Ozi'yO FDo&M.^hE9!m08\ӴzjPҀ }{H`&**"QHA%¨8JGF]+[o<*VxP'iM~ 3|Gnm✆-? 0Dss8..(9-r |nx]]/|PyB 搄jI#8PǩCմ -LR; P.  .KLɹs%gYgNoN"g,g2 ov/oPve ܐ#Ѝ0:vam[j墫\.Ip\8S~\LOS<ǭ<s\9E g9YНtgAj LZe'Qְxr׊_(\PR (į G3mMG l@e6Zo[8|f`x"BQa- ZW#V+=bQÖ :̔#H|תQ6 hFy(5 4/Ԋ`P$k /^W<)liҤmb(J<-^>˿gxY(t;v,?M}or;W|t&)eΟ9R2eʜw9w1QL:V Zk_dedglA.C)5/˭/:ZZ"v&^E^QЭ@X P tJBOH|HDWy /1\h<{?2˻]~ky@>>@3?$*BVI!&VBM~.|4 "f -Y[w6zJ@_U@OZ'ƊtĘ#|TplXR BLB UVh+A5 j| 2[/T^r1V.+B :#B 5 k@;[X"ɊnZoEۀJ'ZZLQ+-/rY^YPH\b(r8)mI&KEP_I\kwQ {ߒވIETJnň49)eB I/DoU͒{&EǢx}җ+& 1wDzⰔB*/Hw/=aJw4GF%Ի[|5iVC{{+hbvU[OJ%󚴿0HrO>ۗەی[s7]W[< ///'ϑ'<"fcߖ_b(JDDi^E}:_dսz_{: ARО >9>Pp=ef!]NڻK@??)nx4]~HD6=Rw>Şfq ٲu+|Zվ;ܺUiOo'}B2}Bcm{Y09ixiddo0։Fh0hp{lD18ѓB/C [#^л,JDE$E+Ʉ~>|=M6<)[+lk=k=_j$dv ŻTw[M(ID| iZ`XQCn5w;f;ܭ{;Z;;JkOpy03aLWǡutp]{<~ XX[y+ȗJ.}Aד^nN;l2XdCXlhC%߾exvȅfbioe)cðc]b6>5@:fM}|6-1|-P[}ݻ}=l>Nx/kE0g͖ 4~NE~ß "nTƋt*t&tϜxNUXIG`쿔H7o4Mc.O-eQ|>Hf+=,v1ni endstream endobj 62 0 obj 23142 endobj 63 0 obj <> endobj 64 0 obj <> stream x]n0<9ާ郯f%umz>>?W{T>lcJUn6]8YgYm}奖{C YWu`. JyΕwB2+aO« Yx};͗x9 n3e_ V>6Fـտ9XOW>V ?sտĻ[' SǻO%G8GsE| ?egB_IOFzO/q/v2Hqb0RjaLǐd?iP%?0> endstream endobj 65 0 obj <> endobj 66 0 obj <> stream xܼy|T8~ιwξdf2d&$!!DED! ⮨J PE-Z}kmk֔.hm%3ܙh߿L=9w;s;k]) ~W-l ~-\sr( ]dҫzbIg"$zo K޿R5-[<wOV#44ܣz ܝAˮ^sh<f{ I86_~VX́W/n|FsBi骕ݵ_uUkFXC {0|@19T&W(UjV7Mffw8]? #ѲX.zTd扞Th%pځ~exMiӂ7=S臀_b'b׈*o4IT R\em<^G;b I|̨D*lyPl]m9W=8Q5YAn$v {Bs @M@]@Kʡ??x>y υG DqO`p j\JAߠb;V܋w>;-O~<1?b1[.}"ߗNN4/xxNp=7.8Kd'g6g~,(ڑ =>sxF<@'Zo~ Frl^ƭT2 ODC,GvLs/0ɳ\T vQ|sܳ hyEޘoͿRYXWX_xl)AR]'>aTӎƣ+`W^t>wGiฟBEK ɂ  o>7a܇OS3 K |ѓ$!-d $B"a y'o?" <J2cn & eC/O7, [.eoa?EODE{X.^.> +sn,7{;@R{@J/{Gl% =r *܅72$8xlgADmȣd p7 R\|8U3ֈE܎yن`H.\`3Hƒ [p;>2? p̯o>O9$6A&zmRfm@i/2vt8+s#p Ak{1/CnG3a sp0Q<3k1KD"Y2㿲 &nA]X^[H>Ckp 22#66ŤP|lO"3#1d?~&z E,~ א 0efB {X9NU4`?Ča,5It iDKD_q# }EP>Glw7]O&!'3]wtLq-"MM`I`ւ9N]OY'#VC aD^k i~TqDC}]&H%XY4~v9vb6 zVV)rT‰E,C0G!ر χ N\~&g.3Xi@ {Yf|g7 |+zOeY^u^ \o^,KA@9@}f܈[$Q¤l>Π _7qە}?ONAc}ϕ۠Sw4hb93]<ϼbnkңvrv{{z4ң^{$8z^hx$LN~Zo#{Qe=l}h!?VZ=Sg}MvAꝼ{IjElTKBy)x S}0:1 ϳ3/RKwkQZ8 0\շpey^DA |d~iD|(HcQ0FIȟ4h}hL. 8z)Uwx:}[&,=h⓱>295t82tdy~`B`쓄uY]6OO5;k>}LNJ3Q9'LEɋrXQ}yc.xQgz\4;MOi:WvٱѠvz{G={,{4cj7D\{;K,u:&)fӀgmCycxa ҞP;N?D$!1-QV s c1HqL#_|B/x[j:Lwb.Gz$bcc*#L>KLm`*R^WjtÜ)@+ |C—|6ͰooqSȉ`4 F2^Rb( qf3NM$ x>*Laڀ 9h`6ˎp_K,i`=FǐX'`d 6] ط 4PSӀf`<&ڤ9mA ¾"cauF~oMWg!TU+ ccW)_Wv#zgώxܑkQ8xc/GgB,̤ׄB5.t$ߍGπ%xۑ"e2)KtLYij-]wXv[Pgx2c8wT:\ibz_ͯӺW7Շ~__}W?3+ kiMM,ޑVE!a ]iNw)wSC *2$5EȁpPpb‰ 71wVry{&u H25[n'݄+e:[0iW+~pEz)=?bj=|eEgW\4nd*#wTȕ ΪR6vפ{n hmM#m^j+ {@rl(|Z#Z4 $ ML,X+w$mұْi{'٧أAMrLjPFsh$>P\D*#Av^"kTڝ$@q\SFbQ&άؤF{TXe :q `ٚt$(sPWp(,LEJV$)mF?0ɻu&SWRvg/A]dԌWAg*|7Tz6/v1r7}qz GDmh̓e_Qm8ƻx)^M&7%I{MNvMl' ?9NsI̒r0W.wmnp]O/Y] >kFƤ,CzCukbV$@(Ǐ,8N(YHC1w98(FJx0^qAB!0뭶8[k^sQ\OΞ=): q-.a]UtZ hJp:ՂŒrCVsm*%dm=rsQ#Uh3M)uoɦw=ޕtWw)\!1pFegiTzetmp"Zi/3X-irme/&cHWg#߫i^-f:4[Ũ=N9!.RcVqpٱtYDVKf,s/z`^L6Y &XMҦf )0YA8u8ƈӢЙl1p=/sK!~q֏/zT)M|dl]&v@szg%KSA.EnfAЀC>u`(8d'<}FnZxLk=/YM?7 kB5ꕤ9Z/R ? oz$ Z?`)*f)Sl(@*_$Dz4;s̈VU1ӯB_L@L&s02*p24x3cvC| uC1xИ1fȍՑQU g3/^4Rt1H0ce0ДUř9A&i`C9'qVVjYeSGAҋ&FEbk)b.DV_4ڮǨ` ] bC^l҄9";hԣp~5OS@  @{_xI)|ҰE H:.1祰fF\QS^2woD4GZa2rM']d$cM~=-l5[)(l˲cъ[*6,߃/ʏ0-hkK}:m<彙 "(xxa5uӺ.21jOw)ՏMhwVZ%16QQ5\^U4^j*Fc199B%M]}UѴ奠]StʵE\2U&[eQd ^r╰73)D.2\S)%%Nı( u䐇FW~#~TG/*N(EYc;m[6MXVBDX7MW1)@EJz؜2A\2[9,RFv;y5B~f7lP@&6lmqfjy5ZlB=wg:9sU)Ngbb~\QAa^瀎ݙ>gvkl嘺Ӌ/~㗖f`h;cLXHp-9HHD|i~p.Za 45Uq^ևQ&S?W1П*;Rm&sCʺGn3}MK'lX f[|ǃlff҉Ӿ{zk!@REGԼMӘ+W&G1/YIobvAnZDS &X{x!(c.$5x<^[z4))/(e 8=(7g=|zD3>6Pp#I` 1xhێ AT¡/$ -]w5Yf/K8\e/s= ^c-36oñ-ਆ˭K$*~#\k\\H - k9Y1TBQnq@ȧY[[0N3r:3*#Ra;c!{ 0zA^ &_ƨ\.!tH ":ѤI}i dU- !$@&z`Ҧ?H 3 Ֆi2u6vDˤzɬ7&O+8Q?{iƇ~hwLn  IԙGJXW+\Bi2sCYk kH;{GzB)LJ-ޤ0{Y~j=jp2u\4_12 bћ\uU\0H:gF@C R8_9q'EX2ST9?̬#OyD9+wgE)gPd:A3:WĨ\§oY Z9Eoab3bU1EW,W1<<\/wyez¼us626: D^dJ-Z[1#bl؉MEtw P=RR,lS[}b z6"yK:o>0sWR~s],~Dlf}[Eϸ>/9O~O-"W^ X%,~D舅gƛ.93V3UQ(rF-]{?>z9|´u玘N8NMh3-l`FaңHdBa//OJD-uK}|yA8jM`ʆsۃBClTUJA1<`q#YTSKxMϿaE=k2gaSIT_\tYE$lr!-;%I*VfvM~JdƹiU|uL~Z}aCgQqNgTSa VXT=_ȟZ +dX&OC97>)O%;l*d=;[2 re4,XTr}C$נ唘jgciyfa͏A8ys65z/&nT av^{UBmy:}ΎY |r|෱ /K_M* 哆H&)tصڡX ]&/)KVt3 e%$\ةZR{/w>G_Z=!@) 5g7ύ*_ Z4%)-nHųndz5UxNUZ.8Z{'~o~O~4s/9PFMM5yH,bu|$q՟F6wW5⥗TSF;~NyI֦},3*ߞc62`x>I"e$O2nTWWGŸ X5IE2=wgя!RFo!Pf يY%55 eb*sEGǍ>h^nYT%X |ZkْA5qBr 8\U=IF((CdM6 6Z$g6(<5ʏiOG,#$BZS׃wSNj*<_$r0~z֠*ya?ZqԊKPQAD/I_| G_kB eТuv7^4YUiE^sc_~sXzK)9NsS>^Q%r NVQbb&W+orI2RG,L%pNd3R,a(+Kx|>` @HWkHW$ 6ޏ(lJ&N0¨L()V"i%Uԥ=e*iYg{rLӏEir%F5r)ԧܣ{_S%ͣ5Xڴ :UYb4(2b/Y@+sGS#+K͕{x)owdWsܢgKl/Nz`پ"/G]_[ F!Q;1 [uiʨ?lUU*Uj5J\39NSxY29? 2_ + "tNW[ syϠZ4s*,Gqd"6UdQG0>,ʰB,=^snܭKt.[hN&e=9g)X[XoYlw i uN O[=o`47xRxr-悢 <ybmlZs~JI"ypFA~O!c[oJT,Jywv>fx'ho3:zpD\v[K*99OɆGM3Dː8Nud R$AլD-~ #qȆ" QA a zQGH"s `ydcnG" @&4Fn5V0/`a3vȎBl7 ?5f}X0_?K.̌k+dɶ20!F'}ga<(?uLMn#6П+>]<)3Q4YYoS5aP8+Fd#^K T }IkCÑ\[[;wl6{J;VI3Uش,Xax+~Jbf3o8 >Thy.ֺDQ5SG.f2Ѽgf<1涺ڶ5fn Sc֜VP)r3wU<^qXx*R|SA쐣]tԻi:t8~P,B%{j _Mqe/PRFXLR,aB}6eښIr> WI#CD/qbuMۼZU(c^yG2ѣU*o|7?]Iw7nA#h8HSA' | 1"ka/p>%MN\J|둋"(VRVgoj"ߒ:Zs?Q@ )c=H:0oKx}]Z |mG;eN7G8twJF7^u~U #ACЯK}q:2T 5.JO3e>Z+rLDGPiQ󚺴'q'fpp$83E1A۟<]"Kj(e?d$__V‰h)KðKh҃kZ/+"Wu/#;GFL7iI7+zV1e{4U?i`СWJSSNAvB+Sxƅ=}jZ[W[!&RI."'J+RA*鴄@@Ȥ GV#Z]NJ7KwIE-X6/We|( Bl~ Dg8f<RrKf[x9_CFX(3JLJ؁,e ʀ=z .i)%]B{(-^d7IŃrP(~&.4\^wiEXu$@$/ZY\W6?j]L(; c ).WrS$m ia ?q[z+O%x gÀ3% ,g&V8Am(~p1 Ugh?^9;K셧hP˜cTí"Y=xuȪuh¯́hjtJb4|ӑ/,s|-q-a;֩t_J$KKrv8e"< YZsDtu /tEL%LSiݠIo2cfVKFSy Uf,&820~k3 :1f~eҤFwlDU:1}WxK=fq3@ Or=Sh;qubMhSbgӲ-W#c h.& k DDjE3&&`x4/Z# b8۟X;ΨP8dWOA$J;({ZBďHΨAI7vO1sRyv%6 "Է~g.fֆV@9S)1ɺH)]?}јU3ݳeƑͦ4:8km>3x+;Y9cOl~{WJD?]7nW?J0\:":$c\{Sv$K;:'|m#JEeJ]XJ0);ClYw$L ooR> -D|vʊHY'P.ɴN]MWy0Vy0q /ЗvaO~vtz# :Ibmm.FOQsl&wL }P Ȑ^imqc{@U<(NUpm+iw ikN+mfgfPƁ钋?CQ\D/["dKo% PܗV;Dw1nJ{!IJ>UgWM{k<ߛ2"Ʌ"^&Ϗ=~6Lm rr Og0ht=?v^8vUPH+Wcp3O~ 8؋?̯XKtTS)ՙ^^u}ժݲG#{RO^OGd''S,$qðx"Q%i&,חr_*S٧D/ZTNTM 8G0ܶr-6_MޞcZyF=>)DtIIٰ:jRVo.`hUVx:`FloK`|WA(U 1.:v)>4  +qȔf<s/۞}1l#?w@ 9EgV0lmtr@_,-9Lٍ'>5R^h-k:'F:?xm+F9-b,B ̝`0ɢЦI\ކNY`_x_l@;<la]]x ==w1O+?هù`kg6 [s̝lsGȾ)-B;#?"Wx~'t_KLOE.] ^)R>Y=S}VTh]ƝkKk6]8s]/vl[`_]h*xeAhBF %D߲׹ +T GJ0 J,B|Q sDQ%(DΖ`)`h:sW k%`ZB\J \;A5?)% vN fPvA fIP !E{sh,Az_Y7~}%XƑÿz^eX,%Xʭ*k0,"a,Xc 㟕`:qø-*RJ_۴ O)p}J0\k(@_kb۸á%Z6cv6E6%J"HVꈒ%ZXrjQFH03C)&dwtlE6-٘4IQ~) EIlE6iQGs%EJe') Q3̹{wȁ( C|h0]}G% 7.a゚H[% m.a\> $ > u7~Gx\^vތc {Y[uo/S {'ף]A~Dˆ6@_5?#aO$IߢJ-[ ލrZz$ rZKsFGW%o$' D9Ä0gnNyV#a`+7Fw )z2C6a rG"`WxYg$[W&qge~,I/]UqSoR(Ct@NrmdR0G&r]$+I22GhRƁX= C|7W ;q"#H4FY3T; 8𚄫t8ՙQԭ)Nɪ;,NI#C&HTTQ8OD_2RL&Gݐ.bG2Yi:>,*hl&׶\.GhsKjLXG#*/R!#|E%)"sa!)G5IJtWaU'g*E̓fipҴ̺QڲߡuE>WU?-V\IGNY)v'ץN>:GKsk9I4 ^g&dlfG@GdxC)8%̔^.WXU=4IbIN=ݪ$e)&*$y&ߖnh+B]2## HoK+lQu?=2R]q"QK*?qA,`zNP@`FwE'aGiŽ2?ciYÔ^^Η;K2eEn_&Z<[qWPeKztY)]'- SEaհ:gemʝKy)ݯkɬ聕]^TҤ̖eIcFU'-Trǘ;W՞{ʬX)kt?_Ki0/6z#Ş_ {B u(K5es>>U.$yZb+?4튼]/5e=}I^it( J=oZq#TWev.)/*+cVЍ,ZʏA}eJ{s*;MY{$}%^JLKDMr?KVQxKXʽ|:.EL_[v+WH yU!wB|;u0tp;?'Fِlvp'$xGJe+1' M\@㖝l5 ϦޯD(Xb>GZt3RaQspcf̠DI~Y.3lF̈́m9֤{huJji.Gm=i4&oh3)q Hr3] =?‡ui~vr2LiǘKY, eMz6D>ꙴlӱ2>3Z6?I#るcє.>6[i;.׳YC:"9h0lѓz%B;GF:!G'̴a\6 DsS X3yfҀ@'5B31…%24i%rD$ӎEdIɦ@&g 5HuݭsssNkʝIθ[[gqChcEFp&4f"9;:xbx3 ~j{3:N`NQ@c̫,Ye-LC΄5K@,ʁ8PZ $)0a>l)eM:xW 9(\n"!H%ۮ5e$Ȗ mBhPSVgEKJʮ(3Y=;VrGyH| IvBHB;Y#aB1ǧWO&Mc(fD[%˔J3tút\ѓ1iAMM',HIBsC ?'*f2P4 H l7;)+j&l(+G:u*k6Z Uc4LZO.T.3$M%Av#^Tvut]-mmm7d[{{G;c#G}NݰUGuHT#3}͏ꫩQMVSOWH9NA;t |KUze-e*?TZPiCJ*?TZPiCJ*^w+TSW~bQ6n2һj7N^+•&_N.鰶͕K]_]ylLP^Fm[{ǽqo]v>>R91_0c&SMko5//S5]|G~u` _1xPM Eݮ^gqLã^\4 oK\ ž> %[֩ 8_S,vk[zwWYZda8p|G (s'p,eԋG{K}^VB~}ϧ7k yϼ)wT(''ƞIP AğVvx.c"4\SbX ݱ n)j\ݡc7ouثZ<@ @~~5K$i:߆h={t3gm(~񍡓m!OplvnwGCњfXaMXw/Eл!xFuwu ] MI|-*=y6im1꟯jdb:k/zk.x_j.^v z >&h8N/6rñh#8kSgPsn<~ 0 XcD6%$%żx945߼/v ź`_Bѧ߂u_,ENF}s=y굀UVG=| pjs]]RY8D0Pf1͌/PԢZ| @J|OڔQQ0۵ :x&=+I=g::c/w;;#rxVFa:7pW_q^rfF}xV>{e-lgpWWkw2ͳ<;EZnO k)0Kpx2͋xcP`|:-(?%ٻSBȥkT0!Di}9^1šw, "@-"0.ZVeDt5=-Pl|NoPh%{%>`Q؂O)jѴT1O`EvoC*Q^2np !R#  =}O~μ߮}L>Բ &H!τPvdv"tI;vOA[kѦ F049~I;Vyp۱A;ǡƋg/<{ğz}CBgxhv\+г힭GOrzH+ǧa4+ȅ‹ Q N.\<@5c8]<>pʉ2$c@I gH=Od 2 ,]diE$4C++F4,D4YA$*h6Գ ."ij&$r 4{bq18MgcbE1"LH_[bS/\񱾱=[_=7;Կec@_aS4IiZB j@7|c^.G̀˩ޖ^tƩEN5|E9U議qdrzp!ca_980F BC f{忹AځB'L|pN ?) ;e<~sPJX6AXC(m1$SAD endstream endobj 67 0 obj 22725 endobj 68 0 obj <> endobj 69 0 obj <> stream x]n0y /{(%IP$Ebq.*dD*Nd‚?}Z gɇ9H:t#u3mT?tsZwwm,m*_iݏ'COa9>Mt%?"k9N+R80?+HYktcO(B٪(|[t!FusMd#\-2;d.e_wK%5d~6y-0&=D2_rk[o%[1I/yW쯓jf5doY ycoI2𯸏-yגI/|tR #_2 4?F-Mߋ㬺{q >4N\%S endstream endobj 70 0 obj <> endobj 71 0 obj <> stream x| x[ŵ{˖%ˋdyu-xbkY`'Xe[ĶIqB! Ihã}$-K(( (G +%B[m?3z HΜ9s̙{<#N{1<+>Cz,OnPu[cPk,ǐGI?"جl EnG(l<a;qq𚰧lc[r :dn(v{}7>3pi9<'QGBRi#ǟx/ މ! .ާ`babX' ⣑|K4+a-?@Ua0!HZ8IX 5 š!; A.T8 5P~| ZO!X&b L&dKD=}!W:B9$&9)(C_ Z 6^x p3)C>̃@CBυ~ (a%Tup<ArKC;qk^x!k>Ćp^س|&W+p#wؽ8D %4]7Q*5WO]#$yr|EuBwtyQcvg8f6ʶvYzchߐR]#Bbhh&VC? ftRC$+=+7L*owCGSQC+ v:e}o8 d+n# 9gWz/o+<&&[W>;zh!hA#PU(jҏz1/GgАX1^(B™~J>rK6};ox'CP c[MTΉ{Pѡм85uWh]8Aj4|>'J" RIѾT5ɍ6r&wcN)Y\ WUs\'^> {y?p"_Xp(PޣܫPWuWë́uaoΉC!GC+? ,v"p'Gt>H~>.=qF D̶T^ i)%?U\# TB=y B'h\y~m$K:}h5fV'v~Hp><9,"|xߑ\np(+>l$٫Hi"/w_~4rvp# =``)~> wEnyblɳ O<0?8rF%Ќ%7~֎;=w6by 3{+a:+EIbTdV*em`m naNkNIbANߴ[cg;59ٰ_`8B~ux?1VpƊ(lmh[~>_!!%='O껭]~u6Flޯbӈ..>mGP]k6?hsDpqit۶M5\"mn۶E^67riMۚpT<O"-im+EڿNyf)07Z۬)k!q l[>v .Ɵߓ_%isN"3T9OP bYq!ep2$_;Q4V߹M_x=WOnQ? R/쟄6?+ 2VvqN uX/bꃥm8"uB=h.l7/k"t%gksdOJڳygjxIWO+$R kmbNYͭ絤>G׷ q  QY׾?v妰%qf] Y$pI ' S+3 Zs(сT' !HtOQQ)jY' FDbMYչ3U-窠aY,DDaA{V䏟+ @&MH} NH$Q,*SKS <񕦲y-&f{ UrņbWAJ"ո" ]SRVR4HLJ:Jܜ`1f?TPo2'$)!$z\`S IA~\ 5%fnyyY`/7嚅+L6ʆjXr.Iٸ4EC͹kUsUo%6;F<3+; x\W)PcJ/.*),Vk+,(-+Zhҭ$YL#KJrJurlP3wv^Va,JETVVf#' &Oukm\hQy?-#$ +X4 F΍dNvKѾ+!UͩBX)[xf-5+}.MI.]~8;)y4KlIzjfHMP :MI?A!-`HK 2k~ 7 p; eiL+>!Bǟ.g mO$$Dkh(Xg saH ?I ڮ+l-6$OۘAM5vТ55Ԣ=Mu PNG rjб*eld1R2RiR%ARj/Țv{7V/I*))2clПq 1wwyBƆݨVz)-Td$,ߓy(ɘiL,X?`_ggпXdE}W8|&,[$z8]7<㩣ۦz?t DJ>+z@tL5:{r N_EXGR.~h\t)?#t#GiKqG-Cck7ԑYTϋs}Me-iu-P/}hPGE$),Yuu W]tFmz%zEJ4w$ĚQ$H <,NH Q87anbe!'bĎ^ky !k•FHch15vNn_#hpױ|FFChH8xMR əFf)fLZ S9nbmRaXndϙA;P^nL`hMfXA[E }⩤(A_yuM$2.6<,Sޞx!2pwmM1tN&-_B>k" IO:S'TGt'GTmL1&j)&=#N:QəXjO,͜O()-]<(HrX1vv6̔ԁ`qx M@!G˷Ј}ڒFBꆄ'Rn£ s/mh6߿5fjO玕$j#]5X\KZw<_BZc$_<*CًQռ1h5dͳZͺ;шqZ>O4tq;HX.xr'-TstY>׿3c j]Mf}gͱD7rwX?6: .R#ҍFYY11>ӂ^Gd5qq5_PWk32Z%[EiTvCx;U]}gUT*sxskizZI􂙝蹔=ZTmL NWsܖ'\Y}  14/8^ZV{VSubݹV?~/8ҚiϬYߙ!{Ԛl}4u 5RÈ5QaGQ5{0ՠz\!eK"HĭG }=1~0'(1J ()DŠ7,j&6.yS{/{WC;hţ7MH!TeL>,z[ftr=TvJOMWf>7s_yݫfLqwT5m޵dRwEk_1'eV~U5KanFWӱc%5-U>Uǫ7,A(}08R&Ჩ &?$rNLV(ĕC2xF|> c* n[TT; ln-c`oP@y@hD3 VȾR1/VTd٨8(5{ Аt0=MGR|A`=gʰu9[Mu3F=bp sdX\T!FyrqT7d=x:o^ y Nr)(gI y ޔ_ /dp*3zF9%}f1d94ɒaaV3O( fs Sߓa0?`v)a\WA zXޭp>Jzly.F!"f"MNyS{`dƋXKZ*`Z1rӇ2بϋ6`كs`DX(q#΁ؼ=l6J*5|*`;r*9]=-ƃ=^{qpekRx-R9hQ!ADN7u]8b4\W"JONjx`e^A%d^@&8/iiǾ<ҍ2uqk7ӌ^l К"9l`c{γ-yà5'7B!=#"7_L K:%ќ3r('\ŢnYkPґfc'*yE6LյG3euS>pq1&y)%0$k\f}ɻG%N#lҜK/чxnj`]rbcɲJ{#23WP_>/09ϊ,gpaf6;it;i\vJ!}>PeYcGY'"<,},Ph ko)#sFqYb2'Nϲ z<Qx bi4AY%=;mgW 0`XTf]vV.ʒoMf1毓soq*e'ip` y֐p=U0l4:7eosI|f&OpKc5#R$r!}uQ9ˤ1zLa?21d;/ה|+:KQ5`SN.<7JyBgB):DL.9\E'xߌyiacixȞs7kje חr4oT0)H+G)HrA9KJjXlz}ӊc[W-7J';H;M7Зl9YGKCzBcI~RL:}qW(iKZO/Ǔs|I׽Z Kn͔)>{&9ZT2 :E,`+^k2{`O 3D)UDAoq/Kڛ[džn[Cnzgq\!qx;WlpBG+# 99X䊵rW_+.wz ΞVנ+.v݃:@puONR%qM*K'/-3[\ cfadHňȡd_bWl8z:+A gb+WK>1]lm抎9u#Y'Tr Ǩkur>Ѐs elq^X,TdG׀SMD!HiM+-׸=ԃ+rzNO؋3EOux7Qb?b#]^a`Lrv{9Qg+QHw`'ӡQApi}!ǀ#+A482|Y (fNSF<~68qNjU=n**h{`DG{QM ep:uQ(,8jGFuthKG.uz! Ԍ9&Rд"7+vHq8)G[p^ @r |c8|bۍP\=N4W.{ 4slt 9Wx>l%qwHKĎ1 ٳZs}ኼAY:yAy޵>j:G\yGِKZ4-m]dI\qFvƖŭZVbg*`(ϼMCg@ȲUpɨ-~c:۽e) 0):CK:1NXtSkDpFԽfDЍ9q*(dK燅lFd9EEX%ZD7#m8d˥O>ѷ>!Z{ֱ0~Q|~d|N\7cov1MLfzn;8]p 07WK_.}WK_.}WK_.}WK_.}+$H)wQk݌7?m3\ y,CIoB&ϥ86_? >: Py 1x_u#'gͤgɵ\_aL[£3l`޷v4v|a'oi~S8Ƿ3-]9]8ZBP/e]+4 f؅Z| }w_?4S9OκouuLx&g ]i]i$Qoő3?_r3/B)coZ vWB Amd.8L;Kx @\y:PW'%ep +4|7??6@fn'ANNw8{)0^:XXM:ƛ)r7{ }/Ox{ Zf9mQ,X|-`Z, i -'}'xpĉjᶷD-BKKµ ZZUc> wgya w.n,*'H=^y| M{kY5c:VX1-@J6{rͣ.F] nŽQG:,-xo{z C#hD0DQ~-8ZrnK#- ڿޠpVC*vX-Té;Rקjݩ5ڦTT:h$#y$-s|Wٓ%k}/Yvڑdp֙]=qPKvK_jsJI AaGXh@U$KX* dYq¡n܀vKm m֫bx-IZ8 dd(踤@i(*.c Zz6r;VZNXLP҇Ax PB9PB(9JCd<)P )X(rNC(fSl#c8oƒimċ3`=,ƚIk"@ut^Hand@,*y=” ǰB.0%\R@cشa3^R12@ ԁ1cX)%|z0=qa#d>r NKH,',o1҃,FJ X~Yɪ-z(~F-/ X~Z -VZ+,],uwHms]4ikEi^ڶ_ uKu~yRsoJ8Bp[_c[렦dWf[_F8+#J$UMTI8"˸D Z+w.IYtMe νW#: 65|a5\#fhlh|#`yRĈ K| endstream endobj 72 0 obj 11623 endobj 73 0 obj <> endobj 74 0 obj <> stream x]n E|tGdYJF`H5 R܋ kV+ޜx:(-fqh$/T*[‚[gSSUJ7giz#IpJttk`iFJ9ܾ  cm Z`U0[.q=R,\- > > endobj 76 0 obj <> stream xԼy|ř7^U3s=wh44#ida-ˇda_`iIcq 6IA>!:$XȆd Ja!X>O~Ss"#BR]=z!l[:剷26GHx˯NrtO"͗<B"cåk?x'8 h{&7 =Y \wcޟBx憫7[3~57QzPSWùrk.xY߇Ptz-!.O^#Σ;aZzN8^D`4-fw8]n!%\dU:] -lUgn>w .\Խ%K-_Ӌ({*8Q: E&,ԎP =v(:kh uY=73Ȍ6"hzp4B #:*Q'sQ?rB#8v{Jx @~>)xr;p-G-E!ڊlJihiEO[G(н|Q @[r]hAt5cKB?1t;ZM)(.@5pf.ZN-%JK IW8ڑB je6<~_jށuM[߂{ƵFˍh\ۉGp'c%qMv(9K*PF/;N,ԁ7pu|NS7qp=&:~ ?g ߓ[Ҋғ?B[DBMh1Z6Mm/#?D5IsU8 m_6ҊE_/;x%Z&[A)5Ó\(h|[a }8УO 27︻iݓ'2Ei8\QvAJ%ar3s,n)=??F@Ff_:Kw"*%ЮF9sPUо~-6U= ~B 6_o.|?lK'U{)Hl62\NrM>:n+7ۣ!D%Ml5jо&T '&z'~7&&|iOJh6h.a{ (zd~ 8P _ `‹a[ 5x-Vke0bZ2|So w3n#^N^\H]C%4=9E;R係%6er׼C!BP3قO;;J/fͣ}ͣH;zzyn܋z t57x$*MQG.BOAj|\B i8_G*~GOrO_kp#\SR8N$˸iqyAJ ݄9ڙD<@ A!=䏨ּ1]H^Cݍs@,yRAw$h_2 m E*@~ ;_ XCHKWBtC E;JեJђғ 7MKkR|dܞ~(eg~ퟩy dgk[ Q# t +|n O^HrGKOBX6#3ǁv:{Ru-3 M \}]m6SNU%+X4RVBnnJhDA9Qu{dn2< ֜S7@(}r~Mj^jꙚXRZPKZi(#GDQrq<'ҫ| 0ܠ(#OiiÎ9}[R} k܈;ҿgb!&hԈ72}C[0׬^>Gpۺ=bI*fD6"(Wޠ{c;ھq}d{F5wΈSxg۹W}܎v رMٳܫazgܾsv.Q]ޞ|P}*H;-REfG6츲>whsx׫.GveҞHx]3ǿׁv\yGU<_IWakLes3r:u^tf\1mQdÈNDOMpiڱ _/FbDַCjr?Ie?|痬*Ƥ Trt~$"6dtQ2H`P7 ~8L?」*Z '#C{ ZۇLw+cW蕡+gn`6sDH.{|izH=J\zYzәkS9\>`D.ZC _io>qHo9G|{gLOzY|L 族pyh$%goHs睟<Iҕ;vϻ6Վs#};֌F)0o̽:7i4{o߳xYf=K{Lfµ gVJh)-' =\ﰊʳvn#V&Nanˤ2e|LeeJ=c4Cw%ycyq8h FQy$%HNIL\(jhANá618`?N+iU3(iFBՆCA Џ!Ǐ? 7z[$ g %"L)=v4i|LOڲ ڬOf$郺PwX%6AHA*9VLUyZIAH0x⃪Q,ETuO[m暔ftd[\#iIH%IeZE3V (9GSgiV J-ȫCgqx# }تآQͺVfo@7G.{x)} 'u|A!!Ys}coɟ?w0}KS4,DF"jez9K-h~-m;+tbN Wcw=6h/ڑO "Vu^"{OV?t[4Tp*'ONvUuNj)`Y ,4 I'^Psπ=u`Jj+Dv;Y 51` bFZCzPܪf#] $)i@խ/,5RS|:3:xQ  7 d4[e,RՐXPhQfuH*U#u43Vx3ug {MTʧn4wےlSB'g8[ʶmk7 \RP+/,Զ+ QoTw;ۚ\p.ݝ; ;\$Ժ(RL(yMW0V Fwos^2v >#d7& Ejye2o I8ۢvz(MyQnj 5cizј>@QrE1FsBpWc'cd(c{4pÄ NbA_P+"t C' ,lVN( OZ&Reh(ĉ4>:>8`-:T,TqFͶmVg#{cSCDH h󆂂얐ɏ+"34?js 6 84k[n6<DŶ}6 ʧBBOk(I,9d.4*w`}FW Ok(S6VTRJ F*\cCCc>Ts;e un媯- +Cuc}ho&_xŒXd.l鿬?Thw^c~e[G_VQQ\SK/Nf|b05ՓF_O~=J:k\ˎf⊌гH E"O\ptEm./2RAppMU.`7؃L4ӛP) zzGIV֙`=FD-:[MPKQ*_\Q1? tD;=PᑚdC]V[":2+b"+rP `,C ɳF2Y˲t*O[2{G EOmΫUy1g*6d|~8IXe!8kGd$`,DW2h," F;*d $QάDjU9~g=F"YvEa"7D^%/d} ZJv'I~(9Ir()%ICÕK德"O*N1QNxkPiq̰N<ۏ5ZYL _PTub2a[/[HhY"V;jՊl=9îY]7l2k;'g8ZB^‚)2r[&7yh"nYo2pd y{-~3G?I )>: FFFk,kg\`-}ƈNy "ƕvXmq9QG<[N4~d^eȴz܌r]<'(;꠹~6X|y/p`:ՏnkjkIkeղ g5=?9qXJGe';LЀYj..ϋ]=ŋLMrB\N0=8|{0N@bk}~ Aѕ)nSqNQ7qh 6`J4P TD"Έz-5:~2<Ӛ ͩJ$NwfrȆK^Hqߪ*D3X߰U`CDݸ=ւ];+i&kG WS1Kf ~!R|{BH&g0,X^KR/=`K%LAyB PLhTp)oz+Tkb^`1I0kҭq&_&Ip3JS}ՕNtVL"Ӭ^?ZHΈծ6ꯪYo ԪԚ{m mxTL@hvg-H Uv+w5Q:zU+$nnS|\H\g]8#rI]I=wWؓA9AD!T%H M\WI~HE!v |0Fa)<  k}/wN$ 3gơ[@ҔTR 1ѪdIx61^נZC9\J-j{{XAj8 ϴ|Քn]#3d( "sю]T< S,Y?sjO6 oo^so?iIO~tI}0.CznmoV6o|jcjB @˕%WV1Jֆ! 954`B#DiJ 9b:4V "8y[Fq gd#$u4Ȍ?#Lciýu[Zh)#g+\2ii#-z&_OGe Z-D=M*#eJXX->0D*#: :zR+ K4x_Xc\%xnuA)^̀Z2BRJI(G7ScǺ%!UFCjc>5^reDtFљr(˘_ )U+a0()$Tj 3n.hBTE%_ŃhsTnvav{}]Ev)<; H&zo#G5^JSUz X>6Ty(f`VB ,Od/DʉCBl/?V~+ljF9Z,PR$DpR=&lew(&0'"TQ.H"z@0ţiw5:p8~s+_}^vMѵ+L.~憮zd3rLkWv*5}{ˮދwB%ͅ-fdWJ'F22G:9S[ux@.wbQ*)Yfc*QFUS3kXQl<l@.P>y|\摬d,;B:U EGϐ_\P'|щF¡Al|ֲxgxϑ(>2~BjaJ't 6A`z sL8$I\4.l.{Lz%bM՗=6O?t 'xzvx eHK>j yۃshvY=PˁJI[L2:LtE(2rVh߾_Y]*zM!4> ]##}rlK/ ?|$Y9e-鰭WVt)J>+q| BM:}pxϔyw<|2l _xO]1M&[:i2EƝ2"~4F:tKxVXwRV*ڬVjmmW<18op`b؝q*:>>t T/ |)}-Qq48=BrL*_sI2'Alj$ؔ}lTn 0[$C6@y1r=\G;!zFjDf`c^ؘW}lĝB}eې~@vNQV%U B2yGn8YrC i fO/MM5Z6'&d>V-4{^+eH[ُ*aO{ (a TԺdϷ*QY'Ie\盗=9oLք9K̉g^fE2+Y,750+0+=ؐ٤9|tS֕S3MjU^D=ٖ%>4ď45kRZO&r2h-`B Jp2/Y`~V (j9=oy=u_( }4?N4Fi)=>}euvxQ WL;:[<|ֆ|aRwl஍Ϻg7LZBsWv8 s'/8dK̘Msڛڗ_>5|҅x΃dr&ԄQi9w{[ P)> 5m&F0Zj eNG}”ujU 6#|3ɔ3p`ݎO)G^ b#pkU?sTh(6hEbYOVJcӜ`Rّ>@gTf֬rF46jT8Rp`T;u:LYd2 E#@whyt@p",13avfUvRC efT 9dŬ$2[` 6/SOm׽$rR'$9@JZп dnQh)Y[Kvv.wuՏu#?SzxLH8&ұAށ.'mVvnuvN߯y _*l[ͷ_J_][];]1DaHN}D x8\+>evh;+Ze4Cfɬflf03^`/EHAAH| RJ Z &{ST!QZDMMtaBzDal40mH &EPPNs3}LT<b-fyf,eҖPQT kׯ߶t+ɳad]Fc_S=&6{{Hv] D6(T9WSS'T;^ֳM) KI9CcYh} / 1g AqbQܮ(xXz`+tʌF0~ym!89:LMj}񗷞? ti|:J%#~m< 8nM$?= XJȇfi,Y®P<[ڻ5oF^{5·c u7,$…$>tr 4霢xYIWl2)䩕/Q)LeƯЖ⧄gp(PLtBu5"稭4:MJ,T"܅[$2c .F.FB)0TH35IVu6S3"18Joz=zveʱ*Sfjr}[[4;3f2e\U22qiAA/`%Ө_jOF˜LEFE{HDLrV T=ae 8 ,00'j'P5q2|m>אr /n-Sy59@m5mebM'N 3S]P/&bq1J!*MIWJMԖKzҨޘ @gXΡjk 9c- u⬗*bGCpyήr8ö 40T5 c^qQPjumτ_=,[PW;p traG;F7x+ ]mmU}ƃ=Cy iB-pIZBCN߷p۷p i]YbF6O %z[#7:mdFZl5|f&g"jbxc;3:.T8PGYOҙbyzܽޖPw.L &[3n=K,_gڅ&\!$Au^*GZeͷLnZBѨQ N[FAYntlE%U=j5PoPodC-/*<` \IEX |_Ъ*~ʽRJz훆owY`Uͫߕ+󟻝N=sdC ީchMh2Sv4 (!`\j4ޝ z/_VMHEv>N׷z*C קB}ʣ׮yx|&^{^"gㇿ8ʛ×_탓'4t{Esb38C2\&eW;Y vf獮/s;\{93O^N}hkϡQ McԈQn&b2jơ#?@:Xqj%Vq]"c!>Pb_B3?xlolΟo]6yg8[_*5h7dxI',*`͵$hHDx;BȣI+ֺ)Cɿ$]uWәy'k,D0@[D~ w'Qwe* NM^k1I~݊+4N>{s}@M'S˹8FPu4]"M9A-+W&cr,4lNNNۆݿI^gcqP:\cY״jZVkS6/0,YVv녭VVVׅnYc,VKuH YCI}ҝiKt+)vonM#k9Yϣڇ`M Pi.L.V";>};.\.R/u|̶ӕbkHe ltq0!jj OD}7}@ᑮ঱Pp?N 3u~kII2-vUCfAC5SJ }CBLůPWqHZ<\0CWn2+}`vXg}`ݭ d)XaVe*'6FhTB,Tf$VUб I PGwxvz^k<]`yõhGTO@~&\:WqCmvٖK&zӓoE}c?X|@뤊 yX+\E]O7LKYC](-.r"؞[ZWO/O}};M63Q9ۺ8Am- \ptpS2R"FįxD|^+5VԗMNk"`y>-6l/"C4C~]>ӡ{tOqmJ6T+ޤ2Q۠-xsy9BaoG{=oOviՆ 7񛴛M 7oQ>sOگ _M}@3d=exOd gٟf??7|tl\P]7n ]/.7N [Y庅nq +SOjA7l3}Y7X#볉BF#OߴSu~Aiٽ2UڌD0Kj|R~A6((F}^?J9}~(gX8L-SzşZ-QYY(1(f<}>5_Ku3Kx:rjW'k?.2z y?UQcFDQr0A*jH'di):U٦24SfP9#n9Wz߿SuQ`-Ĭ*2fq˪ej ҃C7eF/y桿3c=؞ZƟ-ĔEi%?;HM]95i}T7Uc+dxI"EricRp/,s;x[w[eX,[uB W&d։-YwhQ8{Sr.sk'$,yҠ65(aψ<13 Pnoxo65p`'ÑPg*!SmF[+՜BS i2RZm6b-ҷI,̯S!93ՠocK<'qjI1q6m__c|(vM|ʤKqIcP -Ҵ/kSR$*DUј%ldR42E )HЦ!fNx*;/?рOP6U?>B-;9i#GK7xrboVhE07keZMv\5t^z<﹬5bL|btFJ5{A;,ߪL.~ /Ru˟6kj7[X$%#B2QHu-H,m1WB>OR)#Y&XIW-o|0.L IUcAT[_trȖCe,sZRgdGdz|60R)0RٶF;:~Sŀ=Sߞ2i;ffZ>2K{E xRoؙh-D,jV@qcCbiTiQ"SND2tήa t pSlGniXs]VBy}޻U 3眽.={eaawaᐐ{";!,Y 14ih5[^&64h5ڦoojjմiV=3,hK.)[忴q?:ϖ޳#^$5k/;'7o$&[>Q s۠F|BÉ0$9osؓ G' vig4j5BdaT x@f(r\7 &3tHUw[ PgIdT?%QkeEjmA~h+Cv'|}=,08xK R;j`~{=_%D{ d#)E 3fcvw9&ׄlƥ+~I ͉ɵA߹v !n[G/2p@[(f1z޻ݗ£oX~Y#@GCO^uZjTՎ33m"ˆc ⊿+R,\ Fr{*C}ោ~z#B8l(Y7^[&CK*u0fP{@õN\͈U7LpJڽMC~R bmsm'ɶ+ڕ6k= ި=N/k+Zo)X|J蠋_i:˱T_k0W  <3. 2 KHh:XS<@tk0Aruk:oITƖι B&:LD&98GHr7ېF^rnFn "QG$z#)BwQ ' {)-g+S#`u3WX%q8!?"{6YtErzEs%ydMt mAǞޘؓZ{G:9xuᶚ;S]C[yaKs3O{^}S`$=}M*Uo~MwD,מDq;k+aǐ{@dzS_QYǣpMh2p '{aWAQ҂:V<Ϡ%RA c `[aZAJ=`S U(Q=µ*nS_ 1>gOx )%aJI8`2JJ㨧1GLL1Y H!%+!Cj,5"{#فTB`:S8솑L0A^j{~_p6 _!L;o{[3[>_RڰR)iPG "+&K 绁_Nׁ_|,PsW{|%,G;;c\~~Ϻ{!<Β}p1lh@&U)[^'-As VT -20&4 XфتŒszqzG$dqhQU*J ~) ԨJbȝouŇcHt#8l:kbMJQUNutn"{,$CDzGJU@L$ AN ? gm#%b!ɻȈFJlP2}1R҅y┴0/*eQE2J#KV*I1iw1Ybw,72kx?K>=\%]I}X/DS:S__K\ޭݱ\2z{⃱m<;i{r;cB`x0?޼{OVXUnc"L {*v#H:'L; aVHx;R[̋SMju^I3+1s "6M\DC[#ͳE ۂ.'G%ؚ#ñ$(zF$P5+ ECUfGjIso,w ,;A1FӀZJɧnҳJ ]S[䰳vt :2"Tq4Mdk'aCADMHE9=W,l*i_3Rx9ݑ2KKrMTձS6V~m/rޤWUO{UZ/z9X3dqŬTtyw1/Eb2wL8DJ+[[+*h+W0b"f061*ΒSU;ɒsdTJquuItDNGo0 [#a[ ,DhSnBrQ&SYd})ٖ^sQݚZo0U}6:-(FJvSVp'8Bt"A(R:<س7o$9<d Sc@ cMy4aFA܊QCj;s;op۳aGa C_9ɳcy9 B1j`5shh76|yʜ502Qp8StL>2kP@ϧ-V-8:^qyD=x<)#gfi>j73ݓxbLajX{>+Sl2Z0ߏYA&E#ՊtZ )f0/jibv A.PS'&%wIn4NŐS~εmᚂԂx@ME[T1n^[Z( k ---o1+, aƥ>V~M=fՌD9?s_l(<Δ~^o%Y[>DNef˭ջN9g%Iy琝Tc#CsKT=NP}hdL9{ ֕9}=(D:;u=`a@| dzo>Mζ g)q^&EJN^a4s0S~})NðJ8.8]ϩR'a SVBAޡkuTDk^*/V5/73?1s"ޅʧ4k4b][ =e>g'$LN0dXNMsw>Jk`D䛙8OIaχ}E*wW_NKڧF}'kq-/q9Kc"&"n몺AqhS]MrEbpҽң:7:=%תwhw8זsއ':S2Ѩcb-w#9ɅJ%Jt|PѫbJKi%::(_THr@n &*N"ޯůiU?kYzMMjvɄL\ 'JaLU_Ègsk*extԿ/;s67` U)w)WV,b q<}|l~Ilo8f1kEL44Xd⢨HkM\;gfUX7hsJAaDŧk!ݐaȸ4 $ԟ55h{{<[ )CғTx*@^)'x_QQijsIgқ%WT\QȰxna-hM>ƝUV9V9WZʯN]]quU뫍NWsuBdmƒOu_ѯ;]{6J,7J3c,'*(+p{>{C|*ÔNo1}h 26%r~X*0dE3 c̬y79N;/qыB<?/w$D23IĤӭu3TDBK:/< %UyшDBE&* R} %B8%B2QlrDdeNF&3ƵU}:S 4դHAE29TӜgnu(2a-G96n]gWrzTk3}>[][}eg1k?96n^( ;>~%ޯ}f?td<2:>|͎gæNF)"Qo8`=le0r9 \4cH'c͜TLß)'>/+cw'û;̲,bbd_7\`k-SWO٨^V/ vӌpIw?,g9)") Wo0ߒV7l4[_\$R}0ʵ'ĄE9C!!8:d35[uٍys+LqgL*yŬ}Q_)p](ze p) #$EVjIwɧ5]oz5G!\xZqF)r0XÙh7p HJ 8m3ZsdD3G,aqh6A4 X+a7Y>D0Dp쟗ɨ!k-Z_03uQּ=]_sx*21hg<#jc`s-L"y jL㓿8C B jC~̊-:inǼb-/_ov-/⽮LT? EFlH]Pd`IZD@?|E b!d ?L8Quqzb@y09-8, >5fP|8?5.+Xo v98B/nM]zE,V^GD FVT1Jv׼˳;~0\d8-~}m(܎Cf W:S`0SvFS"ɩ\azJ[&9X֚>F?m^Sg>Ʀ22*z7Aޏmj]6LvHp*KKo;gOO:sR<+m*VlVRI6;Hg2Q)5۱Ih;`;olo&:b=oUv+98 $*2'pV]YLVTۊ%[؟Q*Z,jढA=רyD6y5ڨu7m!g'&}CaK•Ep+䝋0-XLWY>>TÚxoRO3ax-sbٽUOhxj/YSFGM QJ&IVdbOn#zƤi1M+1ͥȍ8_M5b'Em-EvZz]D0VmidOmX|a90ؙ};f_n2X2Q;|;{|dzg|IX&ƱWwUi8v$;{93sk5dojzbrjZj_ѠvۼFCzz(SՠDlxBfIֶH򘃜'+ sk3N ^jGXdqBIl|{/k~5 fleӒpv.?ikZ 2- =Y"H4xS4Y~ 4>M(?dg77U<阊ugH3Re7kp_oAfl]pB!ߕ6 suDڔ(^2-!u)ʈU6fV[UaIGcq)%/)lQ͂ZsTV'rD9t~/6Ӵŏ| xuTpܬ{es@J^yJQz Q'^:=#J Ϟxh-wfq?x JbfORa3?=&Gh)v\Er > +hY:>:=9 N&cZ䬦Bk< Df0>vنv!1gWh΋OzO77[Md\An[RUQEO缇{ds#Y=lX;ݲ|Jm[զRGZr 3{~5(Szp`Y^^188`GjwppYC֯ӷNWŴރ%Ak!km4lHE5p5BE]3=뽛ffgW)u?x̭bk#PA&&9-ip vc5cd󜉊. J4]6a/=DmI|:>&tJ,B+9Rᵮ$03U,:_'wd 0s:G~"ybDgtd<.ϷiՋHSb+dhUW,˾*T[.7]LAz9a88xloďEښ1'u!t^K;HƕD#|L/g=PQT0h^X"_~]b)Ck‘Bt:|3 ߂ GF/ Y3 =a +}K'K r.u%]ePoB[f PLn R?(2%P(|a2*.zvyv]趚U*="G *gESy)``R1R[lkm*i3g*,V Tb*ԔjJj.J=ެ+;Nb'lƷf6!$ё%:d1ss0S$bD7şԸ` B$1, s2o. &R VN܅Cy밓3s]oS˜\%lўO'vv=̞es);8NjIlɎPgdmQ?"`d#9A O+[/ټ`8v|=.J{}`z z34I@!h_P: ^ 1!4+xB,QMӜg5|%lImI,Iz9n#vfs;Ętu\Ua܇K>1?'[6r:'jlj.c@\H<6 G釈wr-yU-UToOU58۬u#ok5rӧOOoo{Dzn-4jnZjk56AF٣ee[[s?[zGoP_ B$Q3 UV~5^ 5;Xl^{IwZ]c}o>3פԙMyc}ʕjn_e^t<o%brsgX>Yç;EJ8Qb@u:=:kf͸JCB_ioa>eyf̖ϗBƤy!8~G\W*at:~9gg̀4n?CqJ$PP!OEs3Y51?Ɲ 9peQ^,;IIkh?o QCL}M@D(#+dA9<4|`0/j"`K4E5h$G?Cycd3]!/Vuth`$,y}ŖHs"hl|AЪ3،y~h= 0?:¨ 5Ϟ›gZf9p(dWs-tHM~S  <'P/,1JHTПOOLP C|Bf|VΩr/Zhq &m 0)p֜fAq}h{:KvK)~]j{v$9qGvh  d$#"%0G6ic'*4<Ճ thL{p}Z" V,A^5XS8glOA 2zܥViORSrrJA V \ zݏok&9d%eۖ?9x_SӾ`ix< Ϡ0t pQo!`Wa{E0xy P =t։F xr0 0q$nP=:  ů`Z_5WԒҽW&*ŭ /Z5W7ch3{Z A̓h%& Yσ_!Y -gįx F0W(k;*Wx+ 6|,SA0hWfS3 6AWw 7hyꗟ)10a"1VH+Hp;l# \ i9PH3,8 w)w6E}Xi0e>mf̵ෞo{z?}?3WCpa%j*+nLɟJI\,,[T~Kjn_Yec埪LU˫V?9׳#3O׽p9dI +]74+51 ~d GaɹeZbßd#5Ǿmʣ-dK ,Y>AF\ ,b?$ dXdX^dX"weX>U52CdX6jD6(<&Ft5~-O0F&s 3Hena՘0qnaқa%2ɰ GdX,2A, 2G,֡!%XR52l`[ɰ``]4SXAj顰RXEk(b kH9[dȵV\2 uA\a#w Cc2 u^.PGa:*X*PG?a@ Ca CQXKUlMa=`p5ɭyR|iN [i:i ۨn ;黷RMHy8OPGg(Bb ¥RߦXOc,iYb&B?(F4Fԁ:`V /GifH%yKcjSJLL@+! OJ+C5D2TN}>pW; C v|m~4N: }{|2(ApȗJK{<RߤR~Kzgfr~.BHS_^9$ΐjȡ$ (B&FZ$nHuGN-!yh( %y;mnJ$vMS[F)!>!!CKER-542?~Z&B)Q^ZX@hT%GbOe!1z9W[3k\e4]rSLB4 -F i^;&QVM~RCm8=rAhh]mHCxtM/gPv>H!9NAH[0N;)f:f )o9H⩃uIoK%0Z7BC4-bDjp/ʏpauki맵+C G}7UQmM\gogjh]޾$<ژTr$NO_K 2?uڗ{bXJi]f3M83J3'R{%欏1HK'S]ߑ Қܒ#RTܝMpG7飹!xZ)4?gvuޑpu/q4ln*a``4к !KxhKj1ruQ :qI. m^㴟0~H_rN'< 6$#SovZ98+V< |IY@>+&Уχw2Pmp{ ^0 cygu+ײD[9kf fr\~eFA[DYr4ݴO(y5:޽r%}a&7&.-3Ԓ6aJ"뾄%s mSc\33bJK>HrF\OR:h?M҇`GJ}ʎԅTO %NAs4@1+sI 2-%bZꏥ?3cP2;3n%S}k^ӧG973 q08Ig'Btyog&=8TtYhЀW]qaU{KGf)d7ug~a^)4w ariƸ%t vohCM3LM^RaPG_Cw0_"Jge!@k|UO dcC_0ˍ!c`C#N\R,Pe.(QLGw ]݃₰xH [!}; ݝ-BG=!Ý݃hքL7 F2E'tI-P pWwr,Bz3ݝ"6'PS2 9D Ioz & @&M (ږFjbpΐm&Cj >7cp&.!@rs]B'{s" 73CV:z{%4E}]ΞL v  )MvvnH{zM4P&%ezmoW7TD'̓4C[:6u" -`v;"}iwpcٵ;3KM$o"#2җ2߱;emTc8 P"}ʦU,װjqӕBay\9_hXb\eL5P*1H"H7;(+H;6e B 0ڻ$3m+tR H*i#e:_nHIN0!rArLbe;m}Ôt v|;. -eGP&vݝ@",X$4K14 Jw2׻Wt!'ʣ7@ZIBvC7NB`v3h:3r 2ri04L^P |$d7S-xSelO1)X, 6tt ՒW6@UQ]$T"F%5K3YVVQvuZ1hM<%v4fi$z\C%[dOG?Xhc壍6VG+m|G+m|G+m|/7V.Y;h et_."?<>J39/W-rW]sw\I =R{:o&NUߌ͟_.Sw4ZT~ ʿ9 =<^iȯGΕY0V\ZZ535P}k,/?`~0F>t X(R~Em[WڷF rHȂ| 915 !N}Y0p(~102D|#0; 0i0ph$o&68{ !. LCd}{/{ avp.Ia uw*7}K-wvvٝssA\/)e8C!$;$ ``0,}NG> `'v;rf4"4V#?nT1|FK\Hn=1Gr!#ۉà|C`*4PwmQ#fws\[^dQIZRT|mu]3٣aڴF4kiv7ezmc6E*kStu#Ӻ3:ňR!(JQ٬lW(((+5v$]7ۣcyKD]NS 70<0p6؏ihT|JLn )L&5/4i3 *BrIB5/8 yYOx2ba.By0`Xw P عFI80\; O(b]}CbmѶGt0NGӏrMpSQ>X?%p"x}a_(ٱe/.2mYzel5Qr4KS7&3NWya6s !0az0i0s l>O&0m`SÈ!F \B֦ms i4#`MrvzC[`ٵ502>0`]<Glh(ŬxF4`S>jv=Cq%>P.̀_5n045 R[Il6v7os~V ?yڮɧj/vD / k}jb:Km/}hB_Q#G| k';ZѺ7Zwy/L4hu__97 ^{M>pGF>I? ?_@5yL;S=4Zh~ђђ}1Z05už3ބB ۉ 2 >pJ/-!o5y2p I.AL? BzP&Q4nh (@]h𓐊=G M}{ʷ$Aר%8|sQq5X2g|#'|J6 G U}`p0<>Y<%^-%s|1uDWj{8^/HB H8M+xI4)1hb&=HaR&QW!p5 >2 KiAa0nx:NTT a'=us~wp~{p~7[8Fl]$iC܎_G $su;c~e^}SߚCMG ^J#ZJ|11+뎫ܖyWKB{hw[9q;>>!t}p *m(m A:IMrSq9osp.!Ʃ!bc` ;4Zʥ# ZnnnB6 ˿yDź1uS L[ 88Pם`7o qhx|`pl޺.y)} ` a0cć-eK;z[24[>,5nF]^r_)u pGS Nrm0?G0Ql9M|XږA4Ð}bX!L41X[r #0@b0{R`P#HEAJA4B!nU endstream endobj 77 0 obj 37545 endobj 78 0 obj <> endobj 79 0 obj <> stream x]ˎ@E|b<C0=Hc@/֭$R.EWr%wscSK׷S 9\>LvͬO1J>۱ u\lD S__|zg~Ө,6\:_[} du7?_ޟc> endobj 81 0 obj <> stream x|{\\{wٻ/X{ed$!@1Ca%A%$GQkԶ^$Z6_VQ5W+ܻ<cm~{wfΜ9s9g,FC`C7OM9b,D(7][{8@ôdkpם ~xi[( Or@ !⑉15ֿzζy\'1pG+<׋n6Z ގ4sZF>PGB%CǪz3T: *F$#ZH2o,Jn}r"^F J?i052 ^\ sA ;[Bk8l1 97ɇr '|v5 a? ?`eP(ı %Gl68Ϸ?_԰T[DOYvȄqMɥ›!rN^$gpDž6ğE\@?µq\ 7Ó+h g.yx'e s˹[-.yɟů,{2q"d Lp*ԒQ.D<:!<y{oaT|U|k5Т\>GON~#i%mGr'yoGO\6o#lrP eȫ`3Qo;|8B~ EEɍerq\ʍpr?>woʿ3wMv\LhGo!B ¿fc_@Dү =-8j+JJ9+85'rv.m庹sPq>K8 ^aP% ;URvM\;:y J2}Ep6U(=ހw/1ʩ!vC#hdM ɥ\M#c0ykM\vsFހn~=~ý<ϫZ{y?_U|>]N~7?]8KBum=T7PYfF.\r-j;/_#4r{\ y%91U~rYJ.l=M@Zܛ=rk'V ?H֢=qyXM~-t>?Dv-?VB=c69!V)8'WHꡌ|7.A}^@'K®]uoc4 h 3j~-0 >sH(Y+c)<ǿAs*rxiG_8 U9-w|ʅwώ; YYGU|),:p}ߞ}>cC}lõa"e.$; =OkL9r'w+ɋ(,H^K qWLB3ʿez ̏6PsH#_{◠#>ܟn*;FޫUT `z@`msӦ666lX__neK+,./[tbQao\o'-23R)jI&Aj*xVIܶY=ADg bVΦImLM@ʮ(2e`RTH5)|G'7!|UYgZ bŠۍ=j)FڤFjڪߘA³"/,1ABOIYF,@kBbiX5Xt FVtxc੊}Vab1 FӁ+Fgt4`3qc);9ܺҙH[ՑKmfi܌41AsL9';fe\ vsxE T7a~ԹMzxڱۻIKО~_sk-%ZiDT6xisf1Vmq$EsHnmԮ$Ռ)mU˦(f[ħs ĥ=q14cjɝ-"ieLl[-zkvD{b"flov}ɬ,#< ,rGFE֋NN8!! KNVQy{?TyD[l~}/%.S04> ƒ13MޘMRXB|#cDZ}S[z.cL/2#cĆWfۈ .V/. %喻 >kxI"zԆ;Rq7m4y1sm^6!5̥R:fSu>cJ**NU- 3bVAď}ndkKՉӳ~fkɤziF29Z,kBBe{X6Yjk# _pZoTU;_|X)ؿŧAO*)ژ6P3p?|LsL`;Ay<<= Ϸyd@Fk-ZpAi͕6)vO|ӂwe98b 7t\20{rt2jYVўſoسnV.^iIn\0ڪͻ;',]pS j ?e]f !YŎrhuz15"(X@-Ɠ )VhT! MYپuc}>1leB+)I r'9N~TpƻI 0w){_N~L%4%OxWyŹf]x PY d2  WX}ip&A/,' jZmؽc]8/z~.{~wLu/ty)2e\0.7ŻXo^8N2˃${Mu9{*뼭ްM[FVpwUjXu*+ x磾[{8qĉe8U8v\1/Z6|6Eof׸`z_٭9|y/ol^)XͶui/RG"?pܼ| ]-'=hJѿ҉R͕ Xp(Ϧ<~n6l 8_ZAIy͹䎬>(eeu?-zKDLc0N ]`.ՈuzXNp'z <8j5yT@r#yuyy2~t)H~[G?uюםlN w- O[|̸(,+GH00JJ1l-u"JZ`8  GDs$xW.umR-IVɷGUg#xo*7XlF4QVT }8p᭫-Iv}+7} -tZȅ:} Wq =33ˍ:fpFQ'DR$>5 rֹN_jAwg<: x zYY6w_/{23'AܘKϓпHm35R$ј̪z3K6'7g\;q5c'', q%سCe׮`j(O%>3ly첩 -eES@yeܫ M+?X'9^$ef_8yC%+=~Pk\Ve2`3 /q ^(>1̴ԥ[ґaHӎΗ,IA0f6sY0"n@xbe-4^=֧*D1ᗞ'Z5YՌn&hSh`;ht1;IO4hW +:D*hR"MjXl#|܉)Y1"B &ϊ-x!\ˉhBJq/"HZ,Ui3n&ӆaǷN,faЋrG\V(OOU;^vlP&tZxia)w&r-0ȄdՕJqe.2w ].SWRY'Y6zPHrЇm--JA=DTp{m ۺ=dYP$CgpK!*b#o';U 0ߎx}eRʷwe]]3n⾣p3U8׈ޮhV57kiʸ~}s2L9*sjgŪ+;/ C FcuWoU4w&Skmֈ9%>8%8͸AUҬ9l)pB˪F/jĒ"o)'mt+S:.y;r$]̟ O )slH²S5uM/WL,O. Rxi/6D '-!.ւu_C#(Vl])i5amt_7ǓOVޢ2'ZݞAaV;^X$.=.=C):QC4DVu99:^=*bN)!uzz( |N6F#mӐ| O9n0כ̤61j~2RGD6FII+UFٹNS6aBf ,]q83gKo[N_\tڊY,ٟ}x~ȝ*qݲޒ=gĝգ\piOI8ca0̨S9߾ۉ{:3I|w?6w>܏.&=C^g֞۲ǓNuObp,CqU=Wv17c:AHR9TFh;?&Nd Sg VT*‘1)CPMz{f.rJL>g~XٯZCey_wⵓ|?i7 ojqwq 6r1w7Kء^uz&E9F l,3,qgK(U 4Lg%z]yF$ \*܊U`5%O(~LZW`dpXeq/)z l.UR$b9=ƗQ9PRS `5ݤ*0U`58L  [:HJzbNZXh|% l7*pl( ֓lX\[U ?!Tun{U)mk)ޮS` QXHXh_Gc_/x[Ѿ F&)07yF&h}S =F+`mV`ԉ;+X&/T`18 Z# ,%E-`g(0⳽ S]e*0'[w)0 S_N=6Fz޳J`)0sa|T`G ?3F X?#}ӿ+gSz e!s]rF~<wA:.Lk! } +AAw3 1=ؿjrOI&A`)WCcأ g+e6 L؁y'ak ':,7Ƚ4}Y3+1+*R|Ii"2 Z\: }“f8ҝC߉^,#p.L$ĆP7Q&մuJ0TDJԇYө#ʾFt~lVc}L"]ƿqSF2SLpb#F}O|JRwmnFdbiG:uV",֩f@;P@u({iB#㚐Rww!|2~6ԍ왘?AFL# k _Cbv{n!E xQEƜ ec}L+,Iq3`G,2?YaOFamZ3HՁzHi-y1F?O "E~̣Hd\imZNxGgTc 1OiC3FYuF֠Ao]:JLALԗVa,g-6I\]̻ YWTzwzJld^XCL1ẅ%G yde$t˱/vme\BSA}YmyqhR >fq\G!"Eaf/WXWEi.C /SٚnW,ӧp2 Y֔wNSG;dG=E,~1i[vȑ&$gfѰk\R|E>MKwNi9^$2Q0ETST^%Ni~60+jMGiʰrnSo/~?^z}Ռc #>x'f3DXܞiILf{AW`qwhF:O$];kLS(kKAŀ藮ł'_vZ˧С1ڕL(= 1sD!sh"<{J`ϒ7J,l)UE0 &_1?I{Sap+pQҊp? F}ROGT 2=3 ~%Ņ+HnHBȎP`;օD= ދgHrj^Qy#wEJz J ARc$ FΕ]_99)= EBRwD E@TGBxk$ؿmxux(jChO(Rڇ݃RPCnd(1w#ҚRb6CH IP>,-tVK 5zB(B*Hrʽ##uuwG;Il  i =`_4lg,}(28a);f F$*+:P EHQo4Ա  ENE^EuE^ho7J;!/4TD[fPbC˺5+׬X޸nTR:s͊u 5Ujj֬k4MmljF_菄7ļlz̓!j duDz(CH(DQ$5cm!)N]j{0яBREgXꊄ{B#D[Cd)uFŤ1P;SMuGg0ރb 3{IzBll8'ePGwz)3P}oIň0xѓʞ訿 Ȯ!øt{qఄ54{ 5]ӓ KClF"} "܌x`[xWŎn JN>CK1tSsDph#:mc:"uחe"OuJ0'llX.JeHJ Ku,.))-|EҢ Yu_iͯ!^2{8ep+̦:w!~ 4m# ?v:Y4߼87/yq͋o^~߼87/yq67f^0un\Sgm^&=-1O?T 9n@Nd ;Db$_l5/;JLux0cކicp7"敘aa/u@4/0w7wovtq+yu$H##uGX{$Hב##<~d, XG7+C6JuFԨQH`foiF54E9 $ڌ7z? endstream endobj 82 0 obj 11087 endobj 83 0 obj <> endobj 84 0 obj <> stream x]n0E /E#4B$H,PI?TêN[ Й{GuTVIw` 0̫@{"IJ^?&vbaj0lfJ=<0RtQwV`eiLʒ ܞg_w[n{g6 4ugf S#"KZ4MI@$ ~8iqǥF>N> endobj 86 0 obj <> stream xԽy|E?^U3se{ΞN23 $dA (H"F¥}**(spiu>pWV]WUdT$$o&SWwTsyβ .Lhb2{%KZaqȹatמEw#^vn 4"9o9:ۂм;jAƾGzdޢe$?Bgl;|{ Z_4% B 8˿h?y^ d˦XK.dGCȰ 0Ez0F\dj;.9 GxqIi"*KWTVUohєQF߿s+@gf!?BC٫dz}yNR_S}N9E[hoG,~10mG?o5Gߠ$H@Q_uI_Jq9_R-Sȉ7 PA|^cfp\y;,Jn7w-8Ģskrsw"t!W[WME"t%`zd_n =^ HDٿAwt>[p1^5ovܬb4MDh(Ibbg?9?|t1]֢uh3z3@ȃF,ЧG+p5|=~\2}A.Yd-h#h+ڏBƔNl|ߌo%ѐ}2Nΐ+yJ24 R' Il*wJnyܻ(p4y E/µo7ѧ0J 6`Ba|:>_xWO ``[vfo\WU4:tJ];/w=cx;cNN</KR|F K ɵd=IwH36f)sɼ|l-g'eaccflYvtsw猹}OCyǫG7{?>~>C_Ϳ`n i yx oOgsC0 Sɹpwͤߓ11&T2UL w',al{}Ihhl׼BktĔy<61 &J '҈"?ᇉ?cZVL{!n6 u;b1Ƅ!rtS'28b `6M]~3D#qm(boop1"bέd?OGT*> Vj 70y ߍz0s#G m z^]A/n]h C m(DT|-s˘lt")W.Bkj}Y:3ǤbmX ľ¾BX`4A{(! db5HCOC_INA2MBs I #=;ڤY;CFCH;=\0;CS]3}lgs3f &D́ewFxiΈ'}o榟@k^nEwa6y8i0gsD9D5pwMhhSX+:i'hsѯRм6xם@0C]_npznLaVq͗iurצmiגhKaǟ!ïۺu2zWC[:] :4]˂new0rWOi 2pm6vH, =Jnn+ &܁cjI=Ɵ>0(|*0D<[i2]U]s"͝ᴝ^ߥay8 Жm"VDh }lR5d~x /CÇZalgOk4 *]+&2َtt#OGV3 S5(]\l;c Žpxn3'&YYSJ]6C %aԣgL+m.6 Zt8J-]||nˋsGUjvB7'ԇtϴ12~YWk zuKXnYݹzfwnŬ̇W[dLg?Es{xZnl4j[0yo8㬶<7LisTk-U[@+ɴc!nQVmP볻1R۸6fw|+b xC-۴nl dj^G۞H&]JL6Nk8kDƾF( A! Lf=hOHf-XiY0WהbjլtM&v M/|H_]*pE7a@(TF8芢1qv}Tri!ԍoT 9dvŜ+%]h7~=p 46Ҿ`A6 `c>/kL)*Mxd%<%8q5 J`(VS][U% T:P<i¹$nbMxKVV]6yUtDvAiCs崅f:%)b' U{:olQÁ@(z{1\.-ID'FPXdS'q V"2|<cΝB69b Wׅ7a)ws~iRn.aX7Bv˕mZA.3`pznvsړ퐫=V; ZUۅ6 ye2pv#g/>S8ܑ-jqRE*9Ym&3YS/2ᴳvQEC~L3m|K4_ɜ|4H\ފJQ~BI @3OS.uxdKUlupH~,$Pii 2a^kLD0ygp =TUb%piRϨMZmjӓdU3E*$Qyi  0opx=N=nOR6E$aR(#eez)1D{pM4Q, uO{pjgε o pq)(ƣbQP:Y;TRS߿̕Xy~ێiA zolrAj$ l'`0\,}#??6D'M?|Wfg :^If݃vM :X7QQOggwP*ƻs_+E߸I+ֻoRhJ&95X!ǹDZ']|\$QDH$s\I^w:/pYRhJ"JD'$KD &--*t :hi}=_Bfrm8.,cR)[6u?kƇˆ&_w[qiQ< øлJ*͖i&,d{ڛ7iLr{ƛO4bzmaԥZ"7|S!n-P!T:g $@Ԓ|p LWz 8`5 0%?} ՁiNn)t1tȋ+!RRӥ27|2kM6&drxyt۞j&{ Jjd$Iˏ>(u 2?1[ pp\ǩҥA4_|A\蝥WD0J) eX>5x*0#盗+J/(3A/ߏS'Nb cU)$/ȣ22.Җth( Xa]DE[c}R97Ȫ:hۘ# BosSa>^l*H5H5a2˘"Պ܇Td~^ 8K?vѕ0G7 Bۃ̬ ^v]ܓYj tο[nyH` 44'+J'6{ayۆn/zE;F:jα aަ tئL%~E7D"BD%^_D^+{xy5@Ov(ăQMߣ=(?*UHwpS1n}<ۼh)5p?c +X5Cm`/Xҩ/̼,s-UCyEGΪ͒`1'i:Qg(_ۼ|X~1iVZWؘ'9-”F:s";!p "qAFQ*Flh$D-XE*MT4jP* zMA>Ţh7(E&xdiT]S<DY*ƨPb쥧@4"TvBLOȔ8Ea)}5R|L|3Z"g;LgF2>yJǤgp"m- |5i:]UY*qemYeeWcq>e~ Tmi3AOc6=E lYIi-+KAvM pF\|\_Mwn*ݣ,(/E'<   i@w%s_qaTTFjjEJcC U_Q)eӝf|Iu":NNӮ=N=06={gMmZ5W,n3雺por1N١Z9E܏(q{'ӣE_{?z#ac \٨Պ.JOy;WIvKR*)-GRsl6}BIOU(JcXI+&b@|@P(EĂ&!:+%&g5zb)>*; =`FN+K}CA.w-j{V+{yw>7K RUS*}H :wG!re܀wtiATNUL#X-Ш ώ ʴ=o=&X1k睆w޻} 9&^r/k˟79wt.gvAJz|Oj8yfP>=;d w$OChu+f}N_G}i|1GF֡iҏ X{gԚ{-iֳ/hUmnn DI8Vhv,E:i,e"c 56`d2\֍P`5 N/3~wKŔpC=P"e%DӬc1CxΊCsfFŽ߹ps`ߘ?笪o:==vylORg*93>#i0,vd`w9H9^uҜɤBK{&:=3i="8"G"A9M6jN[6PMW% stX׍Q|7<l o?BUDz^ZM $jmy7/Fv#1s_{ ՛¼olV7."~Oy-!786I%EN'ɋE$oYzIٞ= W{nln?\_r/n}_Щc4ݰ %(ΐ܀6b>!k hzUyj5wav~i`Alj΄վæ#9ZĻy/{[^~I^sKMUIiʓX5kt6N&3qx} :}qM\T_+~8~ںG30Y`1nr ^$!7V* {~q!L(7ױR<@@SʍjWmK_V^%WJh'\l-/}'_Ձ_Va\.bErWsc(JG\8j1LiS44ôؤ3u_+%(xXu³[aሠƋ!# RոAmi }?tP?!Ɇ5?m5R)5S8)1RH*tD8pt*5XYWX) kt /o}pfߍ*(%3&}%ɗ6nMOSJawH}Δ5T׆ &]w1v0&IG yP+pS)O4"Athڛ zuSQD8$y_݃5ȅS}=j܀Su8TL6#1hT$J_sίtXd^]aK0^v?`A!<. X#=Fz['zD$7K{NBo 4ׂ_=xBEwGłt%xPom XbM5\Alo0>2IWc_޺YD  P#iŽŎ]F$y~Tܦ!`R#S#&ֺ̬ӷvRp5`tW*E=qjRBv`MZ] qy{np`_Q49 WPG&M`^q _ cw2\R޸#Qx/-"RjAw9 :tG'rT{Q)D-NDZg;\D:m6RkT680w?^j}{{w^ /;|ɥ.7}{?TȄw6 6MF6FiJ!?ݯ_;wว'qң*<: =П,w"Z}(*ד4h]OL 3ʰd<o^0]sF=Tt֊/1׷hgj^]-{g[ڽ , ,+ `nMrr{/k#׍eC"RdԺHh-r#܍g(! ̲7Ŭ߃w?"q;][i,,4d~u^]MsGm 9t5Y3$^u| ]Q;RyRu@ a9OCVzQ^\`xW<$ !A#9'~ʹ"ŭyr|Qs:kL|բ%IԢ-k?e݀c|}e0Wos:5آ7%xާz5SF%})`k+J X%$pLFH !^"z:ƨ׵5$qDP ydilFVi< 2_Y^a7 d-~!$p}zV}(@:^8*c?i)cNqt=|IW',`7E Ƣ4}q%g4pD  s9+ʭwSa'}t~|Q\},0JvDa%tM2l zس%ʜf_/DW/.5}}نߖ={,P@o,,o.aw?5N"yh`ө9.ODc(^k x"XK 0+J4sS+ 0\Gy|ATO/Eڱqb;jMĈڒݐݹ+_-E>TUFc/C%v5? ^ Sǧ:Jڗ"xwͻEb_mܣyDӫW9FM **mdqUFpJr wq$aJQ1HZ.>{ܲPzVף7:ẗݺqkčqs&e9?]J\-:0D8~Σp%5nif9"3ꅨ)RUFhҠKlQ|yuM-հW/jShAWb]J͓q&z";uH\_ȭ47 4p!?]6MV?$0'~*e̅МgLE,̂ Ab R:g#T+ <uO]Yt7^˾k^[_?'{Im켶Mُyd_x E:{4\F1gp)JO=XZ=L_z)\@Vq~fY|(Ӡ""Q[% m[GtoNDuA MkBpƵ0 g Bz }q ;lV`,#ladfڬ"{{ ׆lxb @ȊYqX$%x+(" ѧvNBP1wS.6D#C¸us3U}Q\p#EBS)yaa!%_ġ<M[ȭhZ.9ǭVի1`|D͘ ,S( K)GCv J4=@=Og,z9 k:sxN}Pn&IZq%AZt!`?F8OFaڸLugm}*:?>&]*3w1khfh"6q(̴m+-貤ѱɒ~g٤OoanܠXӿ*S2讲NwIp9U]SVC һ}FyMp,xcO)wQT%ͲB7CC`љ*ԒՆ:5%5u.Be'>eק$GdJ$F42M'F/_McL֗7sKdT Z ZE<^WB|zYc$f *J/n(-(=nEd[R8 ?#Lԩve5RҿOn9C7rXMXO}\ĝݹ9;&!7@.1p^4,3fa!"m{;em]?ȏ^WNnDoQ3ssƹ{F`vNy5 í,gh$3 㟠_]FHEWOR&b1_*Ѝ*ncp$A!#%V(fo9/dbH[ཥ??Ȯ&+C)>ERF#@UִxDdDv~3Ej^P}@zPqZM$~ơ@ނ?xՅ\.=4ԡƪgwgw)<=sm?yI y $XeO2bRۍ2%~7Gu u|v< wܿhv˘ٳǴf.̞m.r ,qzi[=޲[OF4ef& &w7N,s ##d1J!'|#Yz LC CH `~o "Q9fd vl # ,r楋/KuH]) m\U?Zԙ F-růz/8yDZg3%Hitѕ~xޤ]|缿z e?Jv ÕrպGFjjO Ny^pKW\A8rȽ#_ZPm՘iլ%mYsfgGrb~h~m[S~W_QUs5yJzOoQpȩu)R©ԺTWZ;,UGZՐMƁ 1^cb.PpdRfF*- `Eqy4+-)*B!9$1"uL`ХmOʣFUQ=(VtV,`PRZ8Ⱥ UA}|nlOsbzcuM8(8uK*^ueܽxy Q!!ߘAg (y}&׉&P"10J].EW yW]%4mAgk 3ziӠe#}6IBnD:Ӗ~s%~5}?M9;e#!RKw t@ ͆UUUkkK֖8N i>A%!35 e\¯?G"1 -.KaBEh(Aj/Fׄ,t?M砙Rb,F EhIZEHh!Re{ G-20aUViiiAi<8*6X W1|j ޿`k٤;6ۨ vwHQ'gB1g,05ߝ5js9qD{kԟ!tEpy7 O @ˀW--ѭit2 ,Ӏeػj $Z_rh|K(^wWP]GcXN1YĴP[xS=C#JO5pfx1Y1Aed!I.~m+&[;lFs Sއ}'}de^æq*%GL C2LfT1d 6Qx0EFGI 5\#_A lF>Q]__Sk@TK&r%P464 zGa*yk3Y|4wg9Q:L"ob!3b^4P_ gUc^M4sv+Gusխi0[ơ@"+T ?'x_YOj9{[UTP,/5jrF9R` O֔I\`#y񠳛vtrdcc!Gm"]a8Xeڧlm6\|>;~<+Kw:p>D6or"{n{IW/98Ǚ0:<I8Ε{/j^})6Bf=M%"rD0GhiS$"BH+}vƆH$^ #^P2CX$=o Xߠ뵚In%-du;q882Mòf-Zl.D0arB 3d> uLca%̢)3SXC!n fm)(fI؅]0}[ I)NTKJ4"TK J'^06'^8SR1=(PMÊY#wڱ6vACS^Cêj"_Qs5pVk{rZ AIc?tt {{$JSpz~J"W?Y}F0gu_7-L"OO䶓U1?=eW1'q,| c8sZUV m vwRB%39kOc[W>h}L>KL@4IcH8puO*V3r[&lhm HuOS8{{z2GP\LS34Pq$Q#Jiwv!1TV66l֨>'fԢ77PKmИa 0 DqR7^ PTdޛvzxY7]]=PzJ /'N 9I1}3y\}Őd?4ėdWy>P43E8-uKu 61S ao ZD. OA&3tCŶvؤoH>/Ԗz\=ݳD) X99ŏ*PEO͞p֌{iv(2B ޤlJɛ"xx4}2.3zlj0]ODjQ Ոc8,53D qP6&56:q5MէV㪭uM.j[gd_[Y7YS[tOéWΣG~կ[i;RGoɿQ:]PՍ=' nP1рҠwT7jkAL*F'v=#HgdE [i.n* Vri3'z~kq[K}˻lzՏZܪ"iЧLBx88M+~3ĠJ,6 bs`uU%VWyq fQ`-& 7 bCAnpW4T6D jL}}&3n\Sn+5`a2inˢY {L%+.[|C4>i6j:hpt+P"Mq ݚDqH ^xh>H̽CAw8S4l8 i1}'il*V$|.橯7efG \^fW^4w .;v "jǪr+zјF1g}`p8 65L:4iP&Y2&AOn@RiG#Z\x6ްK)˩Gy 8gkO=w>9=)qtaƚkކԧ'0P`47y]B} vO(nE 8Cr4.M#8@W wbM%# ܈BԀ?BѠィ ti*jG@KuRsվԣOVrs95ojh&ƌ-‡֯lͶl7 ;\&ָ?QW_U`lWŸEF^3]V|ZC%odWaFgP}rQF2uZz}zC2AEV(3,*YP1Wṱy񣅲=_,9# e= OF}P6.:ŒD~(*BCRE$S&A)ZY9&D `jیgڑ9L+M%)MJM}i'3C[3T )GIju=s=,1_yN]*J{tFoV5'تp)\W'JQ+|b<='V:`W{_1bB,,0D?L~EX"p5@O2%mbL7"I@ zoSY9.R+7.M:ZL#Ys95M8uZkQdނOIk3LŠfe-nB$umq+YtөHڥ玌o\7SddtbPtbhtD^o`Ujb1i ‘ Y"˔ۄK"ssIZr2n-q2 Wi2 i3-K(ۛFLB-IO-yJB\kd<8K[;9s!jR jpJ!w6\8Iɺdr\ j;閯n8l<[!Aɚ#vFY`+YrNRsl5I3E6lZ5n)d^, 1gQq/xTsgdשsgwԩ3gv]g;wV}aQDo,~A/ ²mqWi&LoQ3 `]daôibaôi;v~Oݧ{>T7E!=OD<1(Ay J&4oaG_=e5xo0dAǪZ}#T?%gl:8ڱHW7#wA E_A{DA]~LKU D([9(@U~ oBuտ_[=O >3܎r{go2n1n1x©!nkqu2W:Qчr6uwe0 cKP{4 |؏1N18(w` ڛ:sjz W?Ŋ^L^\=E^D۫wNU7$w#<Oh)Y@%`] j-z8YaGT}wKCwkX7H\|V+DkG(`p  z=_>+M;?}WQ&I|СrTnQC4'_ pFv1ߗgIy*yu)BFP1jĚF6ZWU5w8'q:m#h{Aҏ,Z.DQ&}mfz@ WF\qqI\~ \uGn!4f:Λw:?|5$x-W!EPOnsks+M=_L?>^Fk\+n~<ln65)#>n;=Qՠڻ"@k ZNx-Oѻ^j/Z<׼Dg &?o z_8T]/ eff}}>j}X}Z'.A}7<ާ ,xVo*a1!A0u'$ 1_Nwm[Esq3fYP<.R'c/d G!/q==z9]Mh5BTo>;Δ} RS Ys6};yeF✜ߡ0 1TR_0 endstream endobj 87 0 obj 22333 endobj 88 0 obj <> endobj 89 0 obj <> stream x]ˎ@E|b]#YC0=Hc@mߧoN"eatU*(qw9w[ݵ#i׷sLIbOn2.I3ܻ$w9Oit77iVi.!f\Q.k9TP;wfdty87tݫ,CΗa [VE\wRYrp\p\o7*_lx˜k5xϜ%<6[p􇃉2k1?2>LA-Άj1/7CQO KG_pп+/7`רW__?&uO槿O//ߣvK=Z[koQl?fGΖG8gѿ<B"Kۇatu0e~qB~8 endstream endobj 90 0 obj <> endobj 91 0 obj << /F1 55 0 R /F2 70 0 R /F3 80 0 R /F4 60 0 R /F5 50 0 R /F6 65 0 R /F7 90 0 R /F8 75 0 R /F9 85 0 R >> endobj 92 0 obj <> endobj 93 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 9 0 obj <>/Contents 10 0 R>> endobj 12 0 obj <>/Contents 13 0 R>> endobj 15 0 obj <>/Contents 16 0 R>> endobj 18 0 obj <>/Contents 19 0 R>> endobj 21 0 obj <>/Contents 22 0 R>> endobj 94 0 obj <> endobj 95 0 obj < /Dest[1 0 R/XYZ 28.4 328.7 0]/Parent 94 0 R/Next 96 0 R>> endobj 96 0 obj < /Dest[4 0 R/XYZ 28.4 811.3 0]/Parent 94 0 R/Prev 95 0 R/Next 97 0 R>> endobj 97 0 obj < /Dest[4 0 R/XYZ 28.4 288.1 0]/Parent 94 0 R/Prev 96 0 R>> endobj 98 0 obj < /Dest[9 0 R/XYZ 28.4 640.3 0]/Parent 97 0 R/Next 102 0 R>> endobj 99 0 obj < /Dest[9 0 R/XYZ 28.4 512 0]/Parent 98 0 R/Next 100 0 R>> endobj 100 0 obj < /Dest[9 0 R/XYZ 28.4 373.8 0]/Parent 98 0 R/Prev 99 0 R/Next 101 0 R>> endobj 101 0 obj < /Dest[12 0 R/XYZ 28.4 192.8 0]/Parent 98 0 R/Prev 100 0 R>> endobj 102 0 obj < /Dest[15 0 R/XYZ 28.4 555.1 0]/Parent 97 0 R/Prev 98 0 R/Next 107 0 R>> endobj 103 0 obj < /Dest[15 0 R/XYZ 28.4 413 0]/Parent 102 0 R/Next 104 0 R>> endobj 104 0 obj < /Dest[15 0 R/XYZ 28.4 274.8 0]/Parent 102 0 R/Prev 103 0 R/Next 105 0 R>> endobj 105 0 obj < /Dest[15 0 R/XYZ 28.4 176 0]/Parent 102 0 R/Prev 104 0 R/Next 106 0 R>> endobj 106 0 obj < /Dest[18 0 R/XYZ 28.4 586 0]/Parent 102 0 R/Prev 105 0 R>> endobj 107 0 obj < /Dest[21 0 R/XYZ 28.4 774.7 0]/Parent 97 0 R/Prev 102 0 R>> endobj 108 0 obj < /Dest[21 0 R/XYZ 28.4 733.4 0]/Parent 107 0 R/Next 109 0 R>> endobj 109 0 obj < /Dest[21 0 R/XYZ 28.4 424.6 0]/Parent 107 0 R/Prev 108 0 R>> endobj 45 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 26 0 obj <> endobj 27 0 obj <> endobj 28 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <> endobj 33 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> endobj 36 0 obj <> endobj 37 0 obj <> endobj 38 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 41 0 obj <> endobj 42 0 obj <> endobj 43 0 obj <> endobj 44 0 obj <> endobj 110 0 obj <> endobj 111 0 obj < /Creator /Producer /CreationDate (D:20061115191649+01'00') >> endobj xref 0 112 0000000000 65535 f 0000218371 00000 n 0000000019 00000 n 0000002720 00000 n 0000218631 00000 n 0000002741 00000 n 0000004382 00000 n 0000004403 00000 n 0000026315 00000 n 0000218775 00000 n 0000026337 00000 n 0000029608 00000 n 0000218920 00000 n 0000029630 00000 n 0000033217 00000 n 0000219084 00000 n 0000033239 00000 n 0000037295 00000 n 0000219255 00000 n 0000037317 00000 n 0000041203 00000 n 0000219419 00000 n 0000041225 00000 n 0000044539 00000 n 0000223597 00000 n 0000223716 00000 n 0000223834 00000 n 0000223952 00000 n 0000224072 00000 n 0000224190 00000 n 0000224308 00000 n 0000224426 00000 n 0000224542 00000 n 0000224662 00000 n 0000224782 00000 n 0000224901 00000 n 0000225016 00000 n 0000225131 00000 n 0000225250 00000 n 0000225369 00000 n 0000225488 00000 n 0000225607 00000 n 0000225724 00000 n 0000225841 00000 n 0000225954 00000 n 0000223457 00000 n 0000044561 00000 n 0000045212 00000 n 0000045233 00000 n 0000045423 00000 n 0000045715 00000 n 0000045876 00000 n 0000066324 00000 n 0000066347 00000 n 0000066550 00000 n 0000067043 00000 n 0000067389 00000 n 0000081891 00000 n 0000081914 00000 n 0000082108 00000 n 0000082509 00000 n 0000082765 00000 n 0000105994 00000 n 0000106017 00000 n 0000106221 00000 n 0000106720 00000 n 0000107075 00000 n 0000129887 00000 n 0000129910 00000 n 0000130119 00000 n 0000130626 00000 n 0000130994 00000 n 0000142704 00000 n 0000142727 00000 n 0000142940 00000 n 0000143290 00000 n 0000143514 00000 n 0000181146 00000 n 0000181169 00000 n 0000181368 00000 n 0000182019 00000 n 0000182520 00000 n 0000193694 00000 n 0000193717 00000 n 0000193921 00000 n 0000194279 00000 n 0000194504 00000 n 0000216924 00000 n 0000216947 00000 n 0000217141 00000 n 0000217711 00000 n 0000218125 00000 n 0000218247 00000 n 0000218280 00000 n 0000219590 00000 n 0000219646 00000 n 0000219784 00000 n 0000219998 00000 n 0000220251 00000 n 0000220477 00000 n 0000220770 00000 n 0000221006 00000 n 0000221247 00000 n 0000221560 00000 n 0000221856 00000 n 0000222095 00000 n 0000222376 00000 n 0000222644 00000 n 0000222949 00000 n 0000223139 00000 n 0000226069 00000 n 0000226136 00000 n trailer < <40A0E37DFAE1DD043DD5D24EC0382183> ] >> startxref 226408 %%EOF wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/schema.png0000644000175000017500000004634412014170666024414 0ustar georgeskgeorgeskPNG  IHDR{_CsBITO IDATx1}' q)8;ZWTc4bkH˫RU8*`D!9!9Cg>]C.gȏ}||w7n&nʎnT?w7z>>>T:PRBX1N$NuB'@.'@ #t'ql:N8 gHIN$NTB'1'B'r b%YHsc!3DYuʒ8(K.d? qP4O16#~coqG'_f S k 9oot<' 'nVT4YpAScgZ1]p|04'r#Y$NȐVs5էEmx-Bmhx:vW`P< 0MggmYDܵ?" :0tư4Cʐĩ #'CBsޤ h6>>>^uB=&##f/x% <5Z;NW|9ϚG msH$qһz'"q:eI%qP @Y'eI%qP @Y'eI%qP @Y'eI%qP @Y'eI%qP @Y'eI뿯|p1ޖJ℠eM"9,8aVHf'B℃ؐJGft㳉3\}XVl]}6t:>b[ pW8kȸ9262Sd3Y6+dSFdl33|9~$R Zjٴ9ЋmuǺ-mن^ن&q†S>Rm@"{TzsMZ.qһH?Yv?9lAH*.+dhHRfr8x CLKN(Gk-Z: p4Y?]>^'\J -yo OgݍgyqNc/5N>^'eO[' O+qһP7;T'$N*}>fHplƔ&>G=wi/Y2krJHpq~ < 7'ݫ,}Ւ8jŠ{p} TEȚw6wOv'\Dܼ%}788A*ގ \FℲ u~} %qBA^ rg d'qBJOᝊ>,$NOyoY 8Lℜr}ElȝH&25#w&}$N8Klw$Ip5OBIp '>C'!nvH,ANH9;@9'4L ҤOh I$ fd 8aIy1JY[H 'b 8aKs5>B'̩TqY*!qgMr;k#}$NI :Ip1^/`5>' }B!']7 'd$q/?.~<5×2Ͳs#~P0g;M8iPNkz8tyӧ/4L5"I6dIB'8iMx-nR9hKO8igW\n@$N8ް 8iGPrEYKvN.L{NO>&qq2Uq:߽&q҂/!9 ;o@Cvݿݐ>ʆ?p']M0]U/ߤ;#\Þֳ&7A >ԼE^Ӷ}ݿSְK7@~߾Z/o'iJgnq\bɴR *8'RZ 8:_:w%'оBr'&t6Ca;iEo%w 9t=\2iy>i /N_fL S3)jo=usY)3\Ym˲ʍir[aCjojEs|p%bd&˫=v~MNtv9e9IhlrɑCS9*4rhր入fOJ[&U^Vjg6jKg$Fɼ=Myrg$qfaYKqVˋjвi7gճ${T꽝8ܔ#qftA8;X썳dIv#D\"qR#)k;~v~kDx(Z!qPJ+w -1rr'gA8:'qpe1pǡy'~*vB'$Nn$;m8Ν;) 8j;Nx"Z8N?z%N*ҹih @u6;w <]o8WJ C18Z$hq@]B†GsV.iLW8_jT9~݄Z$PHbG7'b_]Ɵfj>toٶ'v[/~;T6ppBˊa{:QW7N׋D"jfqVa]| ?l/ 0/z>>> @\9+E. @#?qZ>çC<i||[ݼuhCh󍰫B|'̻^Wlg,ΪL|v4K)BFdћ-_.z^Vd#*Tm9UdX37=n}}tRTՉ3T }NQu35uPhVݱnl;Ln;6'bmg UuIW Uz :)Ʊ껊FW.5eu`Y[vv̙Ƨw>ی^/q#f 5b9a/cdY%:ܤhٲvx'\]Vāf6oGʆJiIh*5g:򗿜Cߙ+6r O4sIZK/j<uzTqyYu2"q@^\/q@6ɣg\/-]Sp[l{It>+dgv&kr}y߅<־r]Ym5lz]u嬴M\%~jLUO7$LaʲƛFvsw\?"?P'o %{+CwFneuҷj#Wv-d9~=xޮY].'LjzjlSK1/hjݵ#:`UW9}T&eG^dvlY[#o 7.2ndk8kqByY/ҕ3_Pbjv-p'Ђ_ Җ"[Vd3;ز5hXb,~xP%~jDc2?ygNw:^O8,;mY@jj\jeqZbPtSPo eBrlWN5ڵ\NN',LcL ]|5/F_/hRBo㬱 r,x儫^y.+|$x{}ms繜*ㆊLن ٌ uQ=VFYT)ڜ0ʫl{Unkx_?#5Ȅ{qYBo8MI#qB<,߫2W_pt,lQy'/RH%vVƑC#4@$Nv=ΛϪ3@nq}`~Bw8Дۥ%Zo8%Nr=N4.aj<[oXx"jq@NqH].= SzCIWӏrҏG!Mc'OgK~t/yw a'է_/vcnLN/d\ )BSvP$N-ٮd AItOc1oǧվYl=8}2N n6#г67[w/uT709Z2콡/H3F< E^yMiF|]Rʺ+r/gk-LaukXc/gqRH"aq5ID_:Α03E8>B-~z+Fo5 #!MiaVu^߯'ru$j7ԉ.>t*c *1R5z HTmL{yS>>'&q<&Iɓ8\^`~ƪy:0ЭϪwj3=.n|XY>} Ћoz}|| U8Ef< tv=NI`I2:6v=NO`J:"C` q I@:fz].+ 7c,dȣ#U8Ous @YjTg)p@ z)q2H}>d!)P3g)+ `8ᬡH9$N;mY #K tese>{M8fqƅLY8)b<~{3Rt!J9N>ZC\)erx곫1A3q~razإ~'p`[ԏjd́~%q\c8  2 Su4XoW qg}2R`+uhGWQ]oWGRT:BVmO%q!B'H@YqD< tf''DH?NuB'HBgtK`pKBBfa6߽[h7_}5j@kmmrF3ǖ]LG69Q{kM:9=Ia^}z Qo?Y{L>1P3Y /E1,b%>$6~%/g_f<;{愛s`CAS7tCeފ‡ɧGYh 0RӍ0bo/[~9-g_˙,l|dK%ڔ-[%~,Jl4D&΁ IXUg#B)~TF&u(;-Uތde5P CǢc3G\_:'7lRܡDџjfgZnlFSE3!5l(~ϸxg:l.ڐ2a-~&VE&N~*/v(4g(Ք9tWO̺i|->.Ξ p:FLOWNdS<i^ʲ^[;@6T6Q{ǜ$cUq $rHO?>G̓L/Oe>t,8q2JƍJd9\v6J~/cI?N5_l=|:\J'*pyΪ7Vu&j93Lۇ%5Ng?)^.5NJѯ.<8{1?3ٻhWԳ*iC}|| ]9;!qO?0`3PoIvf[e#6s=N֏(Vo6/Y^3 MscTH*ps ٦= 89U]N[r.'|Z],x=;ξ#IeW\ahnS6X>X7Fߵ 5#evmVCj(~CO<)o]{TWk9zLۇϪ:tmhx'Ȳ.Z7ka/[}mXUoZc}xo˪6/c|eQ*ѕx򕞻yjOus`AAќ񸙟gV[ sg~{ث~g H2Rf62 ff uL_VdMhG͞Zڜr0MhCş "-\O|6rH?NB|Oj,wm8q4A[#.ˊf.qRn}Rck$;܄5 uQM<}k;9[M"R{7c)wfHO=x鼺 s8ЩIY8Nyzbh5{]zI٬Ƶ3Y?x %q)'ٟ>>Nf#7J-t9(:Z"  I$[ټOus qZu~m$ 0'Ys)=?*8.*rZPȫĹrLtgy1>$r㟑eEZ+kKܫldYӏ}~<9 dt;/҇rgOoC_dY,c| ;ŕPYV Stϡ(_,"^|6?8`j,t/YeB}w'ҳ~9H,0?QX:CQq34zT !=}r˓z])gγӆ2vf^-2da3tU@QΑW3<8N(ꣷ @H?A#{:7gz6t?eyRo8%N6'ݒ8O-q:zK\F ˓8Еvn9ՑtFV @!)-vt@v} gwR/ǏG?U@$ 3Aj06YNVvzzvz` IDATUjR-˙>}$?DXPJ'4)22;U>/>|ꛯZ굸柑%8{'tB3j2L%z4sp/;B'<]AsRy-Bh #ϽM%tUU%RȌiS>9 $}L6sme3GCOET:ӟ`֯%ꂖѮECJhrF =5fpưm}_[?N8@=j8oNzKqԥ Z 4ȡt`65<p_n ?E}q$-~J%v˜t 9zbtXz~ٳȥP#r޼[WUO ]_-T d?v=4г)3\굶Fvxxc}syki$,#]jvH2k9вV:6iB-i>: 8zwWB0rhW?θ|)@ҧX 2z44YBzTKoOgfZ̻Q2a o\0׏eo=D\w=Y'^<\Nxl&%NR#^. 8& x7H)#ÃV] XWWd氡ٳ6O{7Qr\i9'96e'p[AME8#'hdz^½ݣ>y[?N5Z~>e9@i] 6&pގ6j@_tO1h Tsj@MH@ՎAՒj)ȝ@Q)hRݭz;v)wMG'νrY=42'gV3VB2 vZ:x!ꀌMKcz;qa+a}{nwS|pWlv"߆4UwM(7?zh;:;h \c.ި-t!tKoW5?*l:!Ց4:E ^"@Q T:f>YM7_}5McqV}_3GN_ݧO㬦>;UJf˚=%+6*Pӧ~*(qiY]99}vu&E $tB ]ή\8#}(qT,%ҿSV PB'U[uYy_C:NBgWjgxI/zMC~ qmiz}~^~RqutVgW0ؼ< %4:-rz A͠]v]#>8zTo9C>btt!q:T⌜lPFΧà guF tT-ǯԜ 7G{*qB; 7M?gAS8 "O>&FPpڶ˄:Ô] ޮYՑWͬm)K|\tuů4;OwY$.:PO!&qVp,S7WG.[!%΋jӴ"b? MJK&#*1ڞ3 U.5+1#ގ5W$ibs"˲w^V Q4| 3X|نkZUo5=WrZOudjtv]C_m=0GnH6y6SoaOfh!pL#ŽP+"p=mU^Ϊ_`)-=MGO,ilEg#g#e:eG7˒gdlS`7'Æf04ղ=[=>Bgɧ,KҊ4(C-\x3vY^ mcW|O[jrҘǪѲhzK35`zpf* @%'YNkau7Oh52Bf/ƛ3Ҍٿϴe+ ߯Mi 80q~f4F iKhN3˞f,ZԼ ~4}ZuGo֣W<ڴۏ %Gp/̥3 U}NmZ8>O׶jgV=qml ÉMSsL+X'kѦTޔznBG~߼NI9sI%L'Wr)+pN)v֩w$wO11>m!νZNd$tR'W5ʥ5+n䶭ñ TB93>ۺs.H9Χ/9^>%ή}ɵfضmS$ # O=^ `S UK=j 4`pY q*]1/~(qـͻˣDHuD(vRǪWn tEj9'ҳގgqB'tµ3bby,ޔ\(޶ys7?B()%; 'J["%e I tEdUW1I̜:zyxpkQe¼uД5Yf4>z#)vJ(vF5'YP=ĵгً9Ii3h&8k`;qF\,v s98>tCv໊ZKۉ3w[xe3\)grJo0|ϡ,u3TzMtWxaq-d fcnCpVOӯFLg8f8mޖ£oʙrcLM'49c]%:+fo{NE&e2+7Pw r;{ToLcEz; q~F8O9ٻ.ة\'ɕUIm)Nj(otɪȫ]b߿uGm&8ZMsSܼG<:gb#͛35G SpxeFjc$Nz^d]"N]s xpߛ` }LVRoXuhJgrVo_^ .q0H6oK8}c ]v=<ŃLGS q6CSdyG$Nj%2K[`d* "rz >d1 ~?ERB4[2߰#Џ mNHpzt(qFuU4S/E3Y崡QXvً6lF̻_Q)?:'qfs|p`WX>wd%i=dг;9;e?3egVsǬ9^>}/Eg3>:Ôvj*;x3$NM(-˟ßlIsX+ 5c\ ,_fӯߎ˒S)szJs*M?{C֜GW<,fwlUꝫ$NM$#VBX P%p0HɓUEl žY4KмgSSℛE.ҌHQyp98={ 2ӗ"bF R sQIPP9N9S?-ks {2~> w^2/<1eyJ:exj{v{6ᱧ0l(Ű;䮂ڐP##{F$ENGF4ZѮ&{~x E$Nz#qAL8#q.^DQ'e9xq8(Kx<=ް!{E9>nG%q<^K6e/{HZ"qJ8>\^d#!Rx<%γQ'eIJg/(x<=8^DQj%q<xg/(x<=8^DQj xyy"]BQJ}_2w2J+'URd̈YKgB>Wb/gI-_jÿomW͹mxpuD;-oITad&*@Q'UWRS)Oϫα.ONX'Ym (q~z ǵ_!1}ss?;{ M8yPïH*2:*mNWjƦP33\m+m>ٌ/۰\iͱA)zmx|ttXcl7DA"ā2!q6Ld1vg7M, u&n虱4Ӹ5F@(5Nʒ88)K;Uүfdzq/8^s:{gv{+8xtq3,.e&, srI'P!VC|cuƐ<)w8 PqsWgz4Z}FVyU;~[/A)oYT~jEgf;-B=f/.]?\ܙgڳՁ,w")t> ^Jb˺y._ 'Y:[H"#hD!ƛs`ע w2nas'pi4ԨHc}fCIk@r9.'^۴ڌz9+gyB;#oȲVΝOCڻ5 %r6mÍ*3Eѭ4͝ވ Vr9?1 G2RѬ+wFl~ ?N}$Hi&H3Blx3QmxΚCk/g =^G3"w\wN,9rU7 ݤ;ԌȢ7zCa2RZrwޥΊx=3g[fLXiBϾjܯP96SM%dxhÃ"2][0Y3Ҭ^п_}{:.I˄j-PHU;PʽW9;{}v`:PW[A13>2YӏDq:\f6&qPdZʱC =;4xs:UH?=i|g+dC9.'wfI^fGT'Δ?CT?݄zW"Y1#qqBkҚ03 nc85ΧC?SJEQj$cND):?>>DXʑ8yT:y8,ө'EqStj\_\Lq5PNJ)Eq҂iw?孁BT:M[Mu ?)Yul5ol ﷸ '1.'4;h0Blt>G7qRȚ :J@1dօN@(k~|$)!fED!';8RI3RnY9ʇ$EInz5 tYu4zÜ^gp8 wlL}L?z>Ifw6=%07%MjI]5zIdZ\>;}KN4c8f3lFᓵrnz%3A$N*ݺG ʙ$n$RgSM˫auP3/eir]i. W8)uC IDAT.%LȚҡt_|e͖p`#gO\  'e\cCINR&KֶQtb G7Ã^UP<3x[* :WɪWm vՑVsr4_{Fm.(Y3B"^_'HVpuSM"ΞEgKٗ8kRyYvuXn+iuJ -ԏRV1=V튳7g\}|8654ȿ+'EX5 +z'ݐ3߉>URlpƱ#-'Wx2'15'D'E>2)\/ h]Z'5p$tV^V^r,y%w7 j:&mh%T:.wFϳzlIʋw."tC'sr];@es>/X{AfÍ.x<{$"etur !h^B'WW0=4y ߲;<@.%'xB'Џ:4cY~$·l%Ȯ\lsQhh[bKR.(H^'0Y1nv̄KBjlߛ/,"E) ڜV󬯿vBI%fdY\L,HYBέF󔯿v(?N?r8ŝefルO"G&YSEgdk=oHduF9P!G WW(EY54٦,yHJ|*x#C}%wfS''qμ_{7qLg[1v5 ByiU*Y ,L3YTL&߯Xڼ9rviC qxPh ̗q9!2?#OEoF|Nە3Լ`em>bˬM۟cMLg[1g!n3qҘҝ$ "E949tJ0\P)&@$N22-IL1f:Р-ChVbI@fzz2mE ;C?tagЏ*%nR3'}fn߿MwBFj^n< $8{1~g$]E)n:c$~MK&p@o8۷ML:Eo8Fl Oԏ80=D@vN8auVH1,(Щ'5ћN^HQlji%ne:}nYv-q'(jW|5.rXnty=|ޙ>>6'Ӈ?/lH~yc!3$GgYYǢgMs)y|ތM%Pׄ3_ǫ}[,P68{$hW eVCߔlj0" -4Ҙ0rAC8.JѡJ||٧r{W|l~qt2\FwWG8}i{I lb|9,₥7qBOSV{R9BK4V=j;6qvaW_\Yߵ62`muƏ,+W3}r;<yg&잝^5#"}kdror|C8?~rCr,-]Qhߡק<Z4*F2 IV8I(ͦWW' 5cYT&埑f,^80";16z'LP%,>IޙڳLZ]V{W'E%C36c;;RNzmbISz ],{kˍ*y7Ct 52_fK,̳^_I51"‹[[7Y@a:S_l*'=vu4*Eg#gա-:kh\Qa|/ S6TǞlƪHU-hyms֖8i2q:#%;O gY3 Yu:<{Q'-:+7T@ Tt{nl;d:3zfΧ~>=ruBcMfsF#QΕΚ4$q)TtTˌ8jFd!X\58Ϩl=-8FMUBFm\% +jqV.87rV$Nʒ8(85NT]*IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/modules/0000755000175000017500000000000012014170666021366 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/surfaces/0000755000175000017500000000000012014170666023201 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/surfaces/description.py0000644000175000017500000000066412014170666026104 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Traceur de surfaces", "description": u"Traceur de surfaces (encore rudimentaire).", "groupe": u"Modules", "defaut": True, }wxgeometrie-0.133.2.orig/wxgeometrie/modules/surfaces/_param_/0000755000175000017500000000000012014170666024577 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/surfaces/_param_/__init__.py0000644000175000017500000000057012014170666026712 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) resolution = .025 resolution_minimale = .002 # une valeur trop faible peut faire planter l'ordinateur ( adapter selon la puissance de la machine) ! mode = ("plot_surface", "plot_wireframe", "contour3D", "contourf3D")[0] # cf axes3d.py de matplotlib epaisseur_grillage = .5 wxgeometrie-0.133.2.orig/wxgeometrie/modules/surfaces/__init__.py0000644000175000017500000003323612014170666025321 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Surfaces # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm from matplotlib.axes import Axes from matplotlib.colors import LinearSegmentedColormap from numpy import max as nmax, min as nmin, meshgrid from ...GUI.wxlib import BusyCursor from ...GUI import MenuBar, Panel_API_graphique from ...pylib import fullrange, eval_safe from ...pylib.securite import dictionnaire_builtins from ...mathlib import end_user_functions from ...mathlib.parsers import traduire_formule class SurfacesMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", [u"exporter"], [u"presse-papier"], [u"quitter"]) self.ajouter(u"Affichage", [u"onglet"])#, ["repere"], ["quadrillage"], ["orthonorme"], ["fenetre"], ["zoomer"], ["dezoomer"], ["orthonormaliser"], [u"zoom_auto"]) self.ajouter(u"Outils", [u"debug"], [u"options"]) self.ajouter(u"?") class MyAxes3D(Axes3D): def draw(self, renderer): # draw the background patch self.axesPatch.draw(renderer) self._frameon = False # add the projection matrix to the renderer self.M = self.get_proj() renderer.M = self.M renderer.vvec = self.vvec renderer.eye = self.eye renderer.get_axis_position = self.get_axis_position # Calculate projection of collections and zorder them zlist = [(col.do_3d_projection(renderer), col) \ for col in self.collections] zlist.sort(reverse=True) for i, (z, col) in enumerate(zlist): col.zorder = getattr(col, '_force_zorder', i) # Calculate projection of patches and zorder them zlist = [(patch.do_3d_projection(renderer), patch) \ for patch in self.patches] zlist.sort(reverse=True) for i, (z, patch) in enumerate(zlist): patch.zorder = i self.w_xaxis.draw(renderer) self.w_yaxis.draw(renderer) self.w_zaxis.draw(renderer) Axes.draw(self, renderer) class Surfaces(Panel_API_graphique): __titre__ = u"Surfaces" # Donner un titre a chaque module def __init__(self, *args, **kw): Panel_API_graphique.__init__(self, *args, **kw) self.canvas.fixe = True #self.couleurs = u"bgrmkcy" # self.couleurs = [(0, 1, 0), (.5, .5, 0), (1, 0, 0), # (.5, 0, .5), (0, 0, 1), (0, .5, .5)] self.couleurs = [ [0.0, 0.0, 0.5], #[0.0, 0.0, 0.68939393939393945], #[0.0, 0.0, 0.87878787878787878], #[0.0, 0.0, 1.0], #[0.0, 0.16666666666666663, 1.0], [0.0, 0.33333333333333326, 1.0], #[0.0, 0.5, 1.0], #[0.0, 0.66666666666666652, 1.0], [0.0, 0.83333333333333326, 1.0], [0.080645161290322731, 1.0, 0.88709677419354827], #[0.21505376344086025, 1.0, 0.75268817204301075], #[0.34946236559139776, 1.0, 0.61827956989247301], [0.4838709677419355, 1.0, 0.48387096774193528], #[0.61827956989247301, 1.0, 0.34946236559139776], #[0.75268817204301053, 1.0, 0.21505376344086025], [0.88709677419354827, 1.0, 0.080645161290322509], #[1.0, 0.90123456790123502, 0.0], [1.0, 0.82, 0.0], #[1.0, 0.74691358024691423, 0.0], [1.0, 0.59259259259259256, 0.0], #[1.0, 0.43827160493827177, 0.0], [1.0, 0.28395061728395099, 0.0], #[1.0, 0.12962962962962976, 0.0], [0.8787878787878789, 0.0, 0.0], [0.68939393939393945, 0.0, 0.0], [0.5, 0, 0], ] self._Z = None self.entrees = wx.BoxSizer(wx.VERTICAL) box = wx.StaticBox(self, -1, u"Equation") ligne = wx.StaticBoxSizer(box, wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, "Z = "), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.equation = wx.TextCtrl(self, size = (160, -1), style=wx.TE_PROCESS_ENTER) self.equation.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.equation, 0, wx.ALIGN_CENTRE|wx.ALL,5) self.entrees.Add(ligne, 0, wx.ALL, 5) box = wx.StaticBox(self, -1, u"Abscisse") liste = wx.StaticBoxSizer(box, wx.VERTICAL) ligne = wx.BoxSizer(wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, "Xmin"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.xmin = wx.TextCtrl(self, value = "-5", size = (160, -1), style=wx.TE_PROCESS_ENTER) self.xmin.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.xmin, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) ligne = wx.BoxSizer(wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, u"Xmax"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.xmax = wx.TextCtrl(self, value = "5", size = (160, -1), style=wx.TE_PROCESS_ENTER) self.xmax.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.xmax, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) ligne = wx.BoxSizer(wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, u"Pas"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.pasX = wx.TextCtrl(self, value = "", size = (160, -1), style=wx.TE_PROCESS_ENTER) self.pasX.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.pasX, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) self.entrees.Add(liste, 0, wx.ALL, 5) box = wx.StaticBox(self, -1, u"Ordonne") liste = wx.StaticBoxSizer(box, wx.VERTICAL) ligne = wx.BoxSizer(wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, u"Ymin"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.ymin = wx.TextCtrl(self, value = "-5", size = (160, -1), style=wx.TE_PROCESS_ENTER) self.ymin.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.ymin, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) ligne = wx.BoxSizer(wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, u"Ymax"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.ymax = wx.TextCtrl(self, value = "5", size = (160, -1), style=wx.TE_PROCESS_ENTER) self.ymax.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.ymax, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) ligne = wx.BoxSizer(wx.HORIZONTAL) ligne.Add(wx.StaticText(self, -1, u"Pas"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.pasY = wx.TextCtrl(self, value = "", size = (160, -1), style=wx.TE_PROCESS_ENTER) self.pasY.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.pasY, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) self.entrees.Add(liste, 0, wx.ALL, 5) box = wx.StaticBox(self, -1, u"Seuils") liste = wx.StaticBoxSizer(box, wx.VERTICAL) ligne = wx.BoxSizer(wx.HORIZONTAL) # ligne.Add(wx.StaticText(self, -1, u"Pas"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.seuils = wx.TextCtrl(self, size = (160, -1), style=wx.TE_PROCESS_ENTER) self.seuils.Bind(wx.EVT_CHAR, self.EvtChar) ligne.Add(self.seuils, 0, wx.ALIGN_CENTRE|wx.ALL,5) liste.Add(ligne, 0, wx.ALL, 5) # liste.Add(wx.StaticText(self, -1, u"Exemple : 0.2 (vitez des valeurs trop faibles)."), 0, wx.ALL, 5) self.entrees.Add(liste, 0, wx.ALL, 5) #self.dessiner = wx.Button(self, wx.ID_REFRESH) #self.entrees.Add(self.dessiner, 0, wx.ALL, 5) self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.sizer.Add(self.entrees, 0, wx.ALL, 5) self.SetSizer(self.sizer) self.Fit() self.ax3d = MyAxes3D(self.canvas.figure) self.plt = self.canvas.figure.axes.append(self.ax3d) self.initialisation_terminee = True def _sauvegarder(self, fgeo, feuille=None): Panel_API_graphique._sauvegarder(self, fgeo, feuille) # TODO: implmenter sauvegarde return fgeo.contenu[u"Courbe"] = [{"Y" : [self.equations[i].GetValue()], u"intervalle" : [self.intervalles[i].GetValue()], u"active" : [str(self.boites[i].GetValue())]} for i in range(self.nombre_courbes)] def _ouvrir(self, fgeo): Panel_API_graphique._ouvrir(self, fgeo) # TODO: implmenter sauvegarde return if fgeo.contenu.has_key(u"Courbe"): for i in range(min(len(fgeo.contenu[u"Courbe"]), self.nombre_courbes)): self.equations[i].SetValue(fgeo.contenu[u"Courbe"][i][u"Y"][0]) self.intervalles[i].SetValue(fgeo.contenu[u"Courbe"][i][u"intervalle"][0]) self.boites[i].SetValue(fgeo.contenu[u"Courbe"][i][u"active"][0] == u"True") self.affiche() def EvtChar(self, event): code = event.GetKeyCode() if code == 13: self.affiche() else: event.Skip() def _affiche(self): # if not hasattr(self, "initialisation_terminee"): # return if not self.equation.GetValue().strip(): return xmin = eval_safe(self.xmin.GetValue().strip()) xmax = eval_safe(self.xmax.GetValue().strip()) ymin = eval_safe(self.ymin.GetValue().strip()) ymax = eval_safe(self.ymax.GetValue().strip()) pasX = self.pasX.GetValue().strip() if not pasX: pasX = self._param_.resolution*(xmax - xmin) else: pasX = eval_safe(pasX) if pasX < self._param_.resolution_minimale*max(xmax - xmin, ymax - ymin): pasX = self._param_.resolution_minimale*max(xmax - xmin, ymax - ymin) self.canvas.message(u"Attention, le pas est trop petit !") pasY = self.pasY.GetValue().strip() if not pasY: pasY = self._param_.resolution*(xmax - xmin) else: pasY = eval_safe(pasY) if pasY < self._param_.resolution_minimale*max(xmax - xmin, ymax - ymin): pasY = self._param_.resolution_minimale*max(xmax - xmin, ymax - ymin) self.canvas.message(u"Attention, le pas est trop petit !") with BusyCursor(): X = fullrange(xmin, xmax, pasX) Y = fullrange(ymin, ymax, pasY) X, Y = meshgrid(X, Y) dico = vars(end_user_functions).copy() dico.update({'x': X, 'X': X, 'Y': Y, 'y': Y}) dico.update(dictionnaire_builtins) formule = traduire_formule(self.equation.GetValue(), dico) self._Z = Z = eval(formule, dico) + 0*X # conversion des constantes en numpy.ndarray seuils_txt = self.seuils.GetValue().strip() if seuils_txt: # On rcupre et on classe les valeurs seuils = sorted(float(seuil) for seuil in seuils_txt.split(' ')) cmap = self._creer_cmap(seuils) else: cmap = cm.jet self.ax3d.clear() self.polyc = self.ax3d.plot_surface(X, Y, Z, rstride = 1, cstride = 1, cmap = cmap) self.polyc.set_linewidth(self._param_.epaisseur_grillage) return if seuils_txt: # linestyles = 'dotted' self.cs = self.ax3d.contour(X, Y, Z, cmap = cmap, levels = seuils, linewidths = 2*self._param_.epaisseur_grillage + 1) for collection in self.cs.collections: collection._force_zorder = 100 def _creer_cmap(self, seuils): zmax = nmax(self._Z) zmin = nmin(self._Z) delta = zmax - zmin # On les ramne entre 0 et 1 par transformation affine if delta: a = 1/delta b = -zmin/delta seuils = [0] + [a*z + b for z in seuils if zmin < z < zmax] + [1] # NB: < et pas <= print seuils cdict = {'red': [], 'green': [], 'blue': []} def add_col(val, color1, color2): cdict['red'].append((val, color1[0], color2[0])) cdict['green'].append((val, color1[1], color2[1])) cdict['blue'].append((val, color1[2], color2[2])) n = len(self.couleurs) for i, seuil in enumerate(seuils): add_col(seuil, self.couleurs[(i - 1)%n], self.couleurs[i%n]) return LinearSegmentedColormap('seuils', cdict, 256) def _colors_from_cmap(cmap, n): "Retourne 'n' couleurs rgulirement espaces de cmap." cdict = cmap._segmentdata vals = fullrange(0, 1, 1/(n - 1)) l = [] for val in vals: l.append([]) for c in ('red', 'green', 'blue'): gradient = cdict[c] for i, triplet in enumerate(gradient): if triplet[0] > val: val0, tmp, x0 = gradient[i - 1] val1, x1, tmp = triplet a = (x1 - x0)/(val1 - val0) b = x0 - a*val0 x = a*val + b l[-1].append(x) break else: l[-1].append(gradient[-1][1]) return l # code pour gnrer la liste de couleur de la mthode __init__ # print '\n'.join(repr(i) + ',' for i in _colors_from_cmap(cm.jet,25)) wxgeometrie-0.133.2.orig/wxgeometrie/modules/statistiques/0000755000175000017500000000000012014170666024130 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/statistiques/description.py0000644000175000017500000000067212014170666027032 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Statistiques", "description": u"Cration de diagrammes et d'expriences statistiques.", "groupe": u"Modules", "defaut": True, } wxgeometrie-0.133.2.orig/wxgeometrie/modules/statistiques/_param_/0000755000175000017500000000000012014170666025526 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/statistiques/_param_/__init__.py0000644000175000017500000000033512014170666027640 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) mode_effectifs = 0 # 0: par dfaut, 1: en pourcentages, 2: en frquences reglage_auto_fenetre = True hachures = False wxgeometrie-0.133.2.orig/wxgeometrie/modules/statistiques/experience.py0000644000175000017500000003122212014170666026631 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##------------------------------------------####### # Experience # ##------------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from numpy.random import rand from numpy import sum # NB: numpy.sum est 100 fois plus rapide que __builtin__.sum ! from ...GUI.wxlib import MyMiniFrame from ...pylib import msplit ent = int def alea(n = None): u"""Nombre entier alatoire compris entre 0 et n-1. Si n = None, nombre dcimal entre 0 et 1.""" if n == None: return rand() return int(n*rand()) def de(k = 1): return sum(1+(6*rand(k)).astype(int)) def sondage(pourcentage = 50, k = 1000): pourcentage /= 100. return 100.*sum(rand(k) y def __ge__(self, y): return self.milieu() > y or self.milieu() == y def __lt__(self, y): return self.milieu() < y def __le__(self, y): return self.milieu() < y or self.milieu() == y def __nonzero__(self): return self.milieu() <> 0 def effectif(self): return float(sum([self.lien.valeurs[valeur] for valeur in self.lien.liste_valeurs() if self[0] <= valeur < self[1]])) def amplitude(self): return self[1] - self[0] def densite(self): return self.effectif()/self.amplitude() __radd__ = __add__; __rmul__ = __mul__ __float__ = milieu class StatMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter("Fichier", ["nouveau"], ["ouvrir"], ["ouvrir ici"], ["enregistrer"], ["enregistrer_sous"], ["exporter"], None, ["mise en page"], ["imprimer"], [u"presse-papier"], None, ["proprietes"], None, self.panel.doc_ouverts, None, ["fermer"], ["quitter"]) self.ajouter("Editer", ["annuler"], ["refaire"], ["modifier"], ["supprimer"]) self.ajouter("creer") self.ajouter("Affichage", ["onglet"], None, ["barre_outils"], ["console_geolib"], None, ["zoom_texte"], ["zoom_ligne"], ["zoom_general"]) self.ajouter("Outils", [u"Exprience", u"Simuler une exprience.", "Alt+Ctrl+E", self.panel.creer_experience], [u"Lancers de ds", u"Simuler des lancers d'un ou de plusieurs ds.", "Ctrl+Shift+D", self.panel.creer_lancer_des], [u"Sondage", u"Simuler un sondage simple.", "Ctrl+Shift+S", self.panel.creer_sondage], None, ["options"]) self.ajouter(u"avance1") self.ajouter("?") class Statistiques(Panel_API_graphique): __titre__ = u"Statistiques" # Donner un titre a chaque module types_diagrammes = ('barres', 'batons', 'histogramme', 'cumul_croissant', 'cumul_decroissant', 'bandes', 'circulaire', 'semi-circulaire', 'boite') noms_diagrammes = [u"diagramme en barres", u"diagramme en batons", u"histogramme", u"effectifs cumuls croissants", u"effectifs cumuls dcroissants", u"diagramme en bandes", u"diagramme circulaire", u"diagramme semi-circulaire", u"diagramme en boite"] _graph = None def __init__(self, *args, **kw): Panel_API_graphique.__init__(self, *args, **kw) self.couleurs = "bgrmcy" self.hachures = ('/', '*', 'o', '\\', '//', 'xx', '.', 'x', 'O', '..', '\\\\\\') self._valeurs = {} self.classes = [] self.legende_x = '' # axe des abscisses self.legende_y = '' # axe des ordonnees self.legende_a = '' # unite d'aire (histogramme) self.gradu_x = '' self.gradu_y = '' self.gradu_a = '' self.origine_x = '' self.origine_y = '' self.donnees_valeurs = '' self.donnees_classes = '' self.intervalle_fluctuation = None #test dico quantiles self.choix_quantiles = {"mediane": [True, [0.5], 'r', '-'], \ "quartiles": [True, [0.25, 0.75], 'b', '--'],\ "deciles": [True, [0.1, 0.9], 'g', ':']} self.entrees = wx.BoxSizer(wx.VERTICAL) self.entrees.Add(wx.StaticText(self, -1, u" Mode graphique :"), 0, wx.ALL,5) self.choix = wx.Choice(self, -1, (100, 50), choices = self.noms_diagrammes) self.graph = 'barres' # *APRES* que self.choix soit dfini. self.Bind(wx.EVT_CHOICE, self.EvtChoice, self.choix) self.entrees.Add(self.choix, 0, wx.ALL, 5) #self.entrees.Add(wx.StaticText(self, -1, "")) box = wx.StaticBox(self, -1, u"Mesures") bsizer = wx.StaticBoxSizer(box, wx.VERTICAL) #self.entrees.Add(bsizer, 1, wx.EXPAND|wx.ALL, 5) self.entrees.Add(bsizer, 0, wx.ALL, 5) self._effectif_total = wx.StaticText(self, -1, u" Effectif total:") self._moyenne = wx.StaticText(self, -1, u" Moyenne:") self._mediane = wx.StaticText(self, -1, u" Mdiane:") self._mode = wx.StaticText(self, -1, u" Mode:") self._etendue = wx.StaticText(self, -1, u" Etendue:") self._variance = wx.StaticText(self, -1, u" Variance:") self._ecart_type = wx.StaticText(self, -1, u" Ecart-type:" + 30*" ") bsizer.Add(self._effectif_total, 0, wx.TOP|wx.LEFT, 9) bsizer.Add(self._moyenne, 0, wx.TOP|wx.LEFT, 9) bsizer.Add(self._mediane, 0, wx.TOP|wx.LEFT, 9) bsizer.Add(self._mode, 0, wx.TOP|wx.LEFT, 9) bsizer.Add(self._etendue, 0, wx.TOP|wx.LEFT, 9) bsizer.Add(self._variance, 0, wx.TOP|wx.LEFT, 9) bsizer.Add(self._ecart_type, 0, wx.ALL, 9) haut = wx.BoxSizer(wx.HORIZONTAL) haut.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) haut.Add(self.entrees, 0, wx.ALL, 5) self.onglets_bas = OngletsStatistiques(self) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(haut, 1, wx.GROW) self.sizer.Add(self.onglets_bas, 0) self.finaliser(contenu = self.sizer) ################# # Debugage ################# #~ self.donnees_valeurs = '55 67 68 72 72 72.5 74 75.5 78 78.5 79 81.5 86 91 94.5' #~ self.donnees_classes = '[40;60[ [60;70[ [70;75[ [75;90[ [90;120[' #~ self.legende_x = "calibre des bananes (g)" #~ self.legende_y = "nombre de bananes" #~ self.legende_a = "bananes" #~ self.actualiser() ################# @property2 def graph(self, val=None): if val is not None: assert val in self.types_diagrammes, "Type de diagramme incorrect." self._graph = val self.choix.SetSelection(self.types_diagrammes.index(self._graph)) return self._graph def EvtChoice(self, event): self._graph = self.types_diagrammes[event.GetSelection()] self.actualiser() def EvtChar(self, event): code = event.GetKeyCode() if code in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): self.actualiser() else: event.Skip() def EvtCheck(self, event): self.param('hachures', self.onglets_bas.autres.hachures.GetValue()) self.param('mode_effectifs', self.onglets_bas.autres.mode.GetSelection()) self.param('reglage_auto_fenetre', self.onglets_bas.autres.auto.GetValue()) self.actualiser() def actualiser(self, afficher = True): try: self.legende_x = self.onglets_bas.legende.x.GetValue() self.legende_y = self.onglets_bas.legende.y.GetValue() self.legende_a = self.onglets_bas.legende.a.GetValue() self.gradu_x = self.onglets_bas.graduation.x.GetValue() self.gradu_y = self.onglets_bas.graduation.y.GetValue() self.gradu_a = self.onglets_bas.graduation.a.GetValue() self.origine_x = self.onglets_bas.graduation.origine_x.GetValue() self.origine_y = self.onglets_bas.graduation.origine_y.GetValue() self.donnees_valeurs = self.onglets_bas.donnees.valeurs.GetValue() self.onglets_classes = self.onglets_bas.donnees.classes.GetValue() # test choix quantiles self.choix_quantiles["mediane"][0] = self.onglets_bas.autresq.mediane.GetValue() self.choix_quantiles["quartiles"][0] = self.onglets_bas.autresq.quartiles.GetValue() self.choix_quantiles["deciles"][0] = self.onglets_bas.autresq.deciles.GetValue() self.classes = [] self._valeurs = {} # La chaine va tre dcoupe au niveau des espaces ; on supprime donc les espaces inutiles valeurs = regsub("[ ]*[*][ ]*", self.onglets_bas.donnees.valeurs.GetValue(), "*") # on supprime les espaces autour des '*' valeurs = regsub("[[][^]]*for[^]]*in[^]]*[]]", valeurs, lambda s:s.replace(' ','|')) # une expression du style "[i for i in range(7)]" ne doit pas tre dcoupe au niveau des espaces. classes = self.onglets_bas.donnees.classes.GetValue() for classe in advanced_split(classes.replace(";", ","), " ", symbols = "({})"): if classe.endswith("["): classe = classe[:-1] + "]" self.ajouter_classes(Classe(eval_restricted(classe)).lier(self)) for val in advanced_split(valeurs.replace(";", ","), " ", symbols = "({})"): if val.endswith("["): val = val[:-1] + "]" if re.match("[[][^]]*for[^]]*in[^]]*[]]", val): val = eval_restricted(val.replace('|',' ')) for v in val: if type(v) in (list, tuple): # syntaxe style "[(3,i) for i in range(7)]" o 3 est l'effectif self.ajouter_valeur(v[1], v[0]) else: self.ajouter_valeur(v) # syntaxe style "[i for i in range(7)]" else: val = [eval_restricted(x) for x in advanced_split(val, "*")] val.reverse() self.ajouter_valeur(*val) # par dfaut, si toutes les valeurs entres sont des classes, le dcoupage en classes suit les classes entres. if not self.classes and not [x for x in self._valeurs.keys() if not isinstance(x, Classe)]: self.classes = self._valeurs.keys() self.calculer() if afficher: self.affiche() self.canvas.message(u"Graphique fini.") except: self.canvas.message(u"Impossible de construire le graphique.") print_error() def calculer(self): e = self.effectif_total() if e == int(e): e = int(e) self._effectif_total.SetLabel(u" Effectif total: %s" %e) self._moyenne.SetLabel(u" Moyenne: %s" %self.moyenne()) self._mediane.SetLabel(u" Mdiane: " + str(self.mediane())) self._mode.SetLabel(u" Mode: %s" %self.mode()) self._etendue.SetLabel(u" Etendue: %s" %self.etendue()) self._variance.SetLabel(u" Variance: %s" %self.variance()) self._ecart_type.SetLabel(u" Ecart-type: %s" %self.ecart_type()) def ajouter_valeurs(self, *valeurs): for val in valeurs: self.ajouter_valeur(val) def ajouter_valeur(self, valeur, effectif = 1): if type(valeur) in (list, tuple): valeur = Classe(valeur).lier(self) if self._valeurs.has_key(valeur): self._valeurs[valeur] += effectif else: self._valeurs[valeur] = float(effectif) @property def valeurs(self): mode = self.param('mode_effectifs') valeurs = self._valeurs # mode = 0: valeurs # mode = 1: frquences # mode = 2: pourcentages if mode: k = (100 if mode == 1 else 1) valeurs = valeurs.copy() total = sum(valeurs.itervalues()) for val in valeurs: valeurs[val] *= k/total return valeurs def graduations(self, x, y): if x and self.gradu_x.strip(): x = float(self.gradu_x) if y and self.gradu_y.strip(): y = float(self.gradu_y) self.canvas.gradu = (x, y) def origine(self, x, y): if self.origine_x.strip(): x = float(self.origine_x) if self.origine_y.strip(): y = float(self.origine_y) self.canvas.origine_axes = (x, y) def fenetre(self, *args): if self.param('reglage_auto_fenetre'): self.canvas.fenetre = args def ajouter_classes(self, *classes): self.classes += classes self.classes.sort() def liste_valeurs(self): valeurs = self._valeurs.keys() valeurs.sort() return valeurs def liste_valeurs_effectifs(self): valeurs_effectifs = self._valeurs.items() valeurs_effectifs.sort() return valeurs_effectifs def intervalle_classes(self): return min([classe[0] for classe in self.classes]), max([classe[1] for classe in self.classes]) def experience(self, formule, n, val_possibles = ()): u"""Ralise 'n' fois l'exprience dcrite par 'formule'. Exemple: self.experience('int(6*rand())+1', 100) simule 100 lancers de ds.""" self.actualiser(False) self.ajouter_valeurs(*[eval(formule) for i in xrange(n)]) for val in val_possibles: self.ajouter_valeur(val, 0) self.calculer() self.affiche() def axes(self, x=False, y=False, a=False, classes=False, legende_x=False): self.onglets_bas.enable(x, y, a, classes, legende_x) vide = self.effectif_total() == 0 or (classes and not self.classes) if vide: x = y = a = 0 self.canvas.afficher_axes = True self.canvas.utiliser_repere = False liste_axes = [] if x: liste_axes.append(0) if y: liste_axes.append(1) n = len(liste_axes) if n < 2: self.canvas.afficher_quadrillage = False if n < 1: self.canvas.afficher_axes = False self.canvas.liste_axes = tuple(liste_axes) return vide def _affiche(self): # ('barres', 'batons', 'histogramme', 'cumul_croissant', 'cumul_decroissant', 'bandes', 'circulaire', 'semi-circulaire', 'boite') msg = '' if self.graph == 'barres': msg = self.diagramme_barre() elif self.graph == 'bandes': msg = self.diagramme_bande() elif self.graph == 'batons': msg = self.diagramme_baton(2) elif self.graph == 'circulaire': msg = self.diagramme_circulaire() elif self.graph == 'semi-circulaire': msg = self.diagramme_circulaire(180) elif self.graph == 'boite': msg = self.diagramme_boite(True) # Graphiques utilisant les classes : elif self.graph == 'histogramme': msg = self.histogramme() elif self.graph == 'cumul_croissant': msg = self.courbe_effectifs() elif self.graph == 'cumul_decroissant': msg = self.courbe_effectifs(-1) if msg: self.afficher_message(msg) def creer_experience(self, event = None): win = ExperienceFrame(self) win.Show(True) def creer_lancer_des(self, event = None): win = LancerDes(self) win.Show(True) def creer_sondage(self, event = None): win = Sondage(self) win.Show(True) def dessiner_intervalle_fluctuation(self): n = self.intervalle_fluctuation if n is None: return m = self.moyenne(); f = m/100. e = 200*sqrt(f*(1-f)/n) x0 = m - e; x1 = m + e y0 = self.canvas.fenetre[2] + 4*self.canvas.coeff(1) y1 = self.canvas.fenetre[3] - 6*self.canvas.coeff(1) if self.param('hachures'): self.canvas.dessiner_polygone([x0, x0, x1, x1], [y0, y1, y1, y0], facecolor='w', edgecolor='k',alpha = .3, hatch = '/') self.canvas.dessiner_ligne([x0, x0, x1, x1, x0], [y0, y1, y1, y0, y0], 'k', alpha = 1) else: self.canvas.dessiner_polygone([x0, x0, x1, x1], [y0, y1, y1, y0], facecolor='y', edgecolor='y',alpha = .3) self.canvas.dessiner_ligne([x0, x0, x1, x1, x0], [y0, y1, y1, y0, y0], 'y', alpha = 1) def afficher_message(self, msg): u"Affichage un message prcisant pourquoi le graphique ne s'affiche pas." self.canvas.dessiner_texte(0, 0, msg, va='center', ha='center') self.fenetre(-1, 1, -1, 1) #------------------------------------ # Differents types de diagrammes. #------------------------------------ def histogramme(self): u"Construit un histogramme ( ne pas confondre avec le diagramme en barres !)" if self.axes(x=True, a=True, classes=True): return u"Dfinissez des classes.\nExemple : [0;10[ [10;20[" m, M = self.intervalle_classes() l = min([classe[1] - classe[0] for classe in self.classes]) hmax = max([classe.densite() for classe in self.classes]) # Rglage de la fentre d'affichage self.fenetre(m - 0.1*(M-m), M + 0.4*(M-m), -0.1*hmax, 1.1*hmax) self.origine(m, 0) self.graduations(l, 0) i = 0 for classe in self.classes: h = classe.densite() xx = [classe[0], classe[0], classe[1], classe[1]] yy = [0, h, h, 0] if self.param('hachures'): self.canvas.dessiner_polygone(xx, yy, 'w', hatch = self.hachures[i%len(self.hachures)]) else: self.canvas.dessiner_polygone(xx, yy, self.couleurs[i%len(self.couleurs)]) i += 1 self.canvas.dessiner_texte(M + 0.3*(M-m)-5*self.canvas.coeff(0), -18*self.canvas.coeff(1), self.legende_x, ha = "right") if 'x' in self.gradu_a: lu, hu_ = (float(c) for c in self.gradu_a.split('x')) effectif = lu*hu_ lu *= l hu = effectif/lu elif '*' in self.gradu_a: lu, hu = (float(c) for c in self.gradu_a.split('*')) effectif = lu*hu lu *= l hu = effectif/lu else: # l'effectif que represente le carre effectif = float(self.gradu_a) if self.gradu_a else arrondir(sum([classe.effectif() for classe in self.classes])/20) # cote du carre en pixels cote = sqrt(effectif/(self.canvas.coeff(0)*self.canvas.coeff(1))) lu = cote*self.canvas.coeff(0) hu = cote*self.canvas.coeff(1) x = M + 0.1*(M-m) col = '0.85' if self.param('hachures') else 'b' self.canvas.dessiner_polygone([x, x + lu, x + lu, x, x], [.5*hmax, .5*hmax, .5*hmax + hu, .5*hmax + hu, .5*hmax], col) eff = str(effectif).replace('.', ',') if eff.endswith(',0'): eff = eff[:-2] legende = eff + " " + (self.legende_a or u"unit") if effectif > 1 and not self.legende_a: legende += "s" self.canvas.dessiner_texte(x, .5*hmax - 15*self.canvas.coeff(1), legende, va = "top") def courbe_effectifs(self, mode=1): u""" Courbe des effectifs cumuls croissants si mode = 1, dcroissants si mode = -1. """ if self.axes(x=True, y=True, classes=True): return u"Dfinissez des classes.\nExemple : [0;10[ [10;20[" valeurs = self.liste_valeurs() l = min([classe[1] - classe[0] for classe in self.classes]) m, M = self.intervalle_classes() hmax = self.total() self.fenetre(m - 0.1*(M - m), M + 0.2*(M - m), -0.1*hmax, 1.1*hmax) self.graduations(l, arrondir(hmax/10)) self.origine(m, 0) # Classe with cumulatives eff or freq 2-uple list: y_cum y_cum = [] couleur = 'k' if self.param('hachures') else 'b' for classe in self.classes: y_value = [sum([self.valeurs[valeur] for valeur in valeurs if mode*valeur <= mode*classe[i]]) for i in (0, 1)] self.canvas.dessiner_ligne(classe, y_value, color = couleur) y_cum.append((classe, y_value)) dx, dy = self.canvas.dpix2coo(-5, -18) self.canvas.dessiner_texte(M + 0.2*(M - m) + dx, dy, self.legende_x, ha = "right") dx, dy = self.canvas.dpix2coo(15, -5) # Ajout des quantiles for q in ["mediane", "quartiles", "deciles"]: # tracer si les quantiles sont activs if self.choix_quantiles[q][0]: freq = self.choix_quantiles[q][1] for a in freq: try: (c, y) = self.select_classe(y_cum, a, mode) self.quantile_plot(c, y, a, couleur=self.choix_quantiles[q][2], style=self.choix_quantiles[q][3]) except TypeError: # c peut tre vide si les classes commencent une # fcc trop grande. pass # Legende legende_y = self.legende_y if not legende_y: mode = self.param('mode_effectifs') if mode == 0: legende_y = u"Effectifs cumuls" elif mode == 1: legende_y = u"Pourcentages cumuls" else: legende_y = u"Frquences cumules" self.canvas.dessiner_texte(m + dx, 1.1*hmax + dy, legende_y, va='top') def quantile_plot(self, classe, y, a, couleur='r', style='-'): u""" Trace le a-quantile @type classe: classe @param classe: la classe dans laquelle tombe le a-quantile. @type y: list @param y: bornes des eff ou freq cumuls de classe. @type couleur: char @param couleur: couleur du trac, rouge par dfaut @type style: char @param style: style de ligne rgl en cas de N&B @rtype: None """ a_reel = a*self.total() m = (y[1] - y[0])/(classe[1] - classe[0]) x_reg = (a_reel - y[0])/m + classe[0] # coordonnes de l'origine x0, y0 = self.canvas.origine_axes dx, dy = self.canvas.dpix2coo(-5, -18) # tenir compte du mode N&B col = 'k' if self.param('hachures') else couleur self.canvas.dessiner_ligne([x0, x_reg], [a_reel, a_reel], color = col, linestyle = style) self.canvas.dessiner_ligne([x_reg, x_reg], [a_reel, y0], color = col, linestyle = style) self.canvas.dessiner_texte(x_reg, y0 + dy, format(x_reg, ".4g"), color = col) def select_classe(self, liste, a, mode=1): u""" selectionne la classe contenant le a-quantile @type a: real @param a: le paramtre dans [0.1[. Ne pas mettre a=1.0 pour viter un dpassement @type liste: list of 2-uple classe, list @param liste: contient les classes couples leurs effectifs cumuls. @type mode: int @param mode: 1 or -1 for increasing or decreasing cumulative eff/freq @rtype: 2-uple renvoie un 2-uple: classe, [y_0, y_1] ou **None** si la recherche choue. """ eff_total = self.total() if mode == 1: # chosen_s = [(c,v) for (c,v) in liste if \ # v[0]/eff_total <= a < v[1]/eff_total] for (c, v) in liste: if v[0]/eff_total <= a < v[1]/eff_total: return (c, v) elif mode == -1: # chosen_s = [(c,v) for (c,v) in liste if v[1]/eff_total \ # <= a < v[0]/eff_total] for (c, v) in liste: if v[1]/eff_total <= a < v[0]/eff_total: return (c, v) def diagramme_barre(self, ratio=.7): u"""Diagramme en barres ; ratio mesure le quotient largeur d'une barre sur largeur maximale possible. (nombre dcimal entre 0 et 1). Essentiellement pertinent pour des sries qualitatives.""" if self.axes(y=True, legende_x=True): return valeurs = self.liste_valeurs() lmax = 100./len(valeurs) l = ratio*lmax e = .5*(lmax - l) hmax = max(self.valeurs.values()) self.fenetre(-10, 110, -.15*hmax, 1.15*hmax) self.canvas.dessiner_ligne((0, 110), (0, 0), 'k') self.graduations(0, arrondir(hmax/10)) self.origine(0, 0) n = 0 for valeur in valeurs: h = self.valeurs[valeur] x0, x1 = (2*n + 1)*e + n*l, (2*n + 1)*e + (n+1)*l xx = [x0, x0, x1, x1] yy = [0, h, h, 0] if self.param('hachures'): self.canvas.dessiner_polygone(xx, yy, 'w', hatch=self.hachures[(n - 1)%len(self.hachures)]) else: self.canvas.dessiner_polygone(xx, yy, self.couleurs[(n - 1)%len(self.couleurs)]) self.canvas.dessiner_texte((x0 + x1)/2., - 18*self.canvas.coeff(1), str(valeur), ha='center') n += 1 self.canvas.dessiner_texte(110 - 5*self.canvas.coeff(0), -35*self.canvas.coeff(1), self.legende_x, ha='right') legende_y = self.legende_y if not legende_y: mode = self.param('mode_effectifs') if mode == 0: legende_y = u"Effectifs" elif mode == 1: legende_y = u"Pourcentages" else: legende_y = u"Frquences" self.canvas.dessiner_texte(15*self.canvas.coeff(0), 1.15*hmax - 5*self.canvas.coeff(1), legende_y, va = "top") # les donnees sont affichees entre 0 et 100 en abscisse def diagramme_baton(self, largeur=1): u"""Diagramme en batons (sries quantitatives discrtes). 'largeur' est la demi-largeur en pixel.""" if self.axes(x=True, y=True): return valeurs = self.liste_valeurs() m, M = valeurs[0], valeurs[-1] if m == M: l = 1 M += 1 m -= 1 else: l = arrondir((M - m)/20) # on evite d'avoir des valeurs trop fantaisistes pour le pas ! m = l*int(m/l) - l hmax = max(self.valeurs.values()) # reglage de la fenetre d'affichage self.fenetre(m - 0.1*(M-m), M + 0.1*(M-m), -0.1*hmax, 1.1*hmax) if int(m) == m: m = int(m) # pour des raisons esthetiques self.origine(m, 0) self.graduations(l, arrondir(hmax/10)) e = largeur*self.canvas.coeff(0) i = 0 for val, eff in self.valeurs.items(): couleur = 'k' if self.param('hachures') else self.couleurs[(i - 1)%len(self.couleurs)] self.canvas.dessiner_polygone([val - e, val - e, val + e, val + e], [0, eff, eff, 0], couleur) i+=1 self.canvas.dessiner_texte(M + 0.1*(M - m) - 5*self.canvas.coeff(0), -18*self.canvas.coeff(1), self.legende_x, ha = "right") legende_y = self.legende_y if not legende_y: mode = self.param('mode_effectifs') if mode == 0: legende_y = u"Effectifs" elif mode == 1: legende_y = u"Pourcentages" else: legende_y = u"Frquences" self.canvas.dessiner_texte(m + 15*self.canvas.coeff(0), 1.1*hmax - 5*self.canvas.coeff(1), legende_y, va = "top") self.dessiner_intervalle_fluctuation() def diagramme_bande(self): u"""Diagramme en bande.""" if self.axes(): return valeurs = self.liste_valeurs() l_unite = 100./self.total() n = 0 x = 0 self.fenetre(-10, 110, -1, 2) self.graduations(0, 0) for valeur in valeurs: l = self.valeurs[valeur]*l_unite if self.param('hachures'): self.canvas.dessiner_polygone([x, x, x + l, x + l, x], [0, 1, 1, 0, 0], 'w', hatch = self.hachures[(n - 1)%len(self.hachures)]) else: self.canvas.dessiner_polygone([x, x, x + l, x + l, x], [0, 1, 1, 0, 0], self.couleurs[(n - 1)%len(self.couleurs)]) self.canvas.dessiner_texte(x+l/2., - 18*self.canvas.coeff(1), str(valeur), ha="center") n += 1 x += l def diagramme_circulaire(self, angle = 360): if self.axes(): return valeurs = self.liste_valeurs() valeurs, effectifs = zip(*self.valeurs.items()) # petit raffinement pour eviter que 2 couleurs identiques se suivent n = len(effectifs) l = len(self.couleurs) if n%l == 1: couleurs = tuple(((n-1)//l)*self.couleurs + self.couleurs[1]) else: couleurs = tuple(self.couleurs) effectifs = angle/360.*array(effectifs)/sum(effectifs) labels = [str(valeur) for valeur in valeurs] patches, texts = self.canvas.axes.pie(effectifs, labels = labels, labeldistance = 1.16, shadow = False, colors = couleurs) if self.param('hachures'): for i, patch in enumerate(patches): patch.set_fc('w') patch.set_hatch(self.hachures[i%len(self.hachures)]) self.canvas.synchroniser_fenetre() # pour profiter du reglage effectue par matplotlib self.canvas.orthonormer() # pour avoir un vrai cercle # rafraichir produirait une recursion infinie ! def diagramme_boite(self, afficher_extrema = True): u"Appel aussi diagramme moustache." if self.axes(x=True): return med = self.mediane() q1 = self.quartile(1) q3 = self.quartile(3) d1 = self.decile(1) d9 = self.decile(9) vals = self.liste_valeurs() m = vals[0] if isinstance(m, Classe): m = m[0] M = vals[-1] if isinstance(M, Classe): M = M[1] if str in [type(i) for i in (med, q1, q3, d1, d9)]: # self.mediane() ou self.decile() ou... renvoie "calcul impossible." return if int(m) == m: m = int(m) # pour des raisons esthetiques self.origine(m, 0) l = arrondir((M - m)/20) self.graduations(l, 0) if m != M: self.fenetre(m - 0.1*(M - m), M + 0.1*(M - m), -0.1, 1.1) else: self.fenetre(m - 0.1, M + 0.1, -0.1, 1.1) def col(val): return 'k' if self.param('hachures') else val w = 1 if self.param('hachures') else 1.5 # Quartiles self.canvas.dessiner_ligne([q1, q1], [.2, .8], linewidth = w, color = col('b')) self.canvas.dessiner_ligne([q3, q3], [.2, .8], linewidth = w, color = col('b')) # Mdiane if self.choix_quantiles['mediane'][0]: self.canvas.dessiner_ligne([med, med], [.2, .8], linewidth = w, color = col('r')) # "Moustaches" if self.choix_quantiles['deciles'][0]: # Les "moustaches" du diagramme correspondent au 1er et 9e dcile self.canvas.dessiner_ligne([m, M], [.5, .5], linestyle="None", marker="o", color="k", markerfacecolor="w") self.canvas.dessiner_ligne([d1, q1], [.5, .5], color="k") self.canvas.dessiner_ligne([q3, d9], [.5, .5], color="k") self.canvas.dessiner_ligne([d1, d1], [.4, .6], linewidth=w, color=col('g')) self.canvas.dessiner_ligne([d9, d9], [.4, .6], linewidth=w, color=col('g')) else: # Les "moustaches" du diagramme correspondent au minimum et maximum self.canvas.dessiner_ligne([m, q1], [.5, .5], color="k") self.canvas.dessiner_ligne([q3, M], [.5, .5], color="k") self.canvas.dessiner_ligne([m, m], [.4, .6], linewidth=w, color='k') self.canvas.dessiner_ligne([M, M], [.4, .6], linewidth=w, color='k') # Bote self.canvas.dessiner_ligne([q3, q1], [.2, .2], color="k") self.canvas.dessiner_ligne([q3, q1], [.8, .8], color="k") self.canvas.dessiner_texte(M + 0.1*(M - m) - 5*self.canvas.coeff(0), -18*self.canvas.coeff(1), self.legende_x, ha = "right") #------------------------------------------------------------ # Infos sur la serie. # (Mesures de tendance centrales, de dispersion, etc...) #------------------------------------------------------------ def effectif_total(self): # self._valeurs : effectifs bruts (non convertis en frquences) return sum(self._valeurs.itervalues()) def total(self): u"Retourne soit l'effectif total, soit 100, soit 1, selon les paramtres en cours." return sum(self.valeurs.itervalues()) def mode(self): if not self.effectif_total(): return u"Calcul impossible." m = max(self.valeurs.values()) v = [str(key) for key in self.valeurs.keys() if self.valeurs[key] == m] if len(v) > 2: v = v[:2] + ["..."] return " ; ".join(v) def moyenne(self): u"""Moyenne de la srie. Si les donnes de la srie sont regroupes par classes, chaque classe est remplace par son centre de classe, pour calculer une approximation de la moyenne. """ try: return tst(1.*sum([eff*val for val, eff in self.valeurs.items()])/self.total()) except (ZeroDivisionError, TypeError): return u"Calcul impossible." def etendue(self): try: valeurs = self.liste_valeurs() return valeurs[-1] - valeurs[0] except (TypeError, IndexError): return u"Calcul impossible." def variance(self): try: m = self.moyenne() if isinstance(m, basestring): raise TypeError return tst(1.*sum([eff*(val - m)**2 for val, eff in self.valeurs.items()])/self.total()) except (ZeroDivisionError, TypeError): return u"Calcul impossible." def ecart_type(self): v = self.variance() if isinstance(v, basestring): return u"Calcul impossible." return tst(sqrt(v)) def mediane(self): u"""Correspond la 'valeur du milieu' quand on ordonne les donnes. Prcisement, la dfinition retenue ici est la suivante : * si l'effectif total est impair, la mdiane est la valeur centrale ; * sinon (effectif total pair), la mdiane est la demi-somme des deux valeurs centrales. Une bonne manire de visualiser la mdiane pour des effectifs non entiers est de tracer un diagramme en bande.""" somme = 0 old_val = None objectif = self.effectif_total()/2 for val, effectif in self.liste_valeurs_effectifs(): somme += effectif if somme > objectif: if isinstance(val, Classe): # On estime la valeur de la mdiane au sein de la classe, # en l'assimilant au 2e quartile, et en estimant sa # valeur au prorata de sa position dans la classe. a, b = val x = (somme - objectif)/effectif return x*a + (1 - x)*b else: if somme - effectif == objectif: # la mediane est cheval sur 2 valeurs try: return (old_val + val)/2 except TypeError: print_error() return u"Calcul impossible." return val old_val = val return u"Calcul impossible." def tile(self, k = 4, i = 1): u""" Renvoie la valeur x de la srie telle que au moins i/k des donnes de la srie soient infrieures ou gales x. Exemple : tile(4,1) -> premier quartile. tile(10,2) -> deuxime dcile. """ somme = 0 objectif = i/k*self.effectif_total() # objectif : position du quartile au sein de la srie. for val, effectif in self.liste_valeurs_effectifs(): somme += effectif if somme >= objectif: if isinstance(val, Classe): assert effectif # On estime la valeur du quartile au sein de la classe, # au prorata de la position du quartile dans la classe. a, b = val x = (somme - objectif)/effectif return x*a + (1 - x)*b else: return val def quartile(self, i = 1): u""" Donne Qi, le ime quartile. Qi est le plus petit element de la serie statistique tel qu'au moins i*25% des donnes soient infrieures ou gales Qi. Note: si l'effectif de la srie est pair, le 2me quartile ne correspond pas toujours la mdiane.""" return self.tile(4, i) def decile(self, i = 1): u""" Donne Di, le ime dcile. Di est le plus petit element de la serie statistique tel qu'au moins i*10% des donnes soient infrieures ou gales Di. Note: si l'effectif de la srie est pair, le 5me dcile ne correspond pas toujours la mdiane.""" return self.tile(10, i) def centile(self, i = 1): u""" Donne Ci, le ime centile. Ci est le plus petit element de la serie statistique tel qu'au moins i% des donnes soient infrieures ou gales Ci. Note: si l'effectif de la srie est pair, le 50me centile ne correspond pas toujours la mdiane.""" return self.tile(100, i) def _sauvegarder(self, fgeo, feuille = None): Panel_API_graphique._sauvegarder(self, fgeo, feuille) fgeo.contenu["Diagramme"] = [{ "serie" : [{"valeurs" : [self.donnees_valeurs], "classes" : [self.donnees_classes]}], "legende" : [{"x" : [self.legende_x], "y" : [self.legende_y], "a" : [self.legende_a]}], "graduation": [{"x" : [self.gradu_x], "y" : [self.gradu_y], "a" : [self.gradu_a]}], "mode_graphique" : [self.graph] }] def _ouvrir(self, fgeo): Panel_API_graphique._ouvrir(self, fgeo) if fgeo.contenu.has_key("Diagramme"): diagramme = fgeo.contenu["Diagramme"][0] serie = diagramme["serie"][0] valeurs = serie["valeurs"][0] classes = serie["classes"][0] legende = diagramme["legende"][0] legende_x = legende["x"][0] legende_y = legende["y"][0] legende_a = legende["a"][0] gradu = diagramme["graduation"][0] gradu_x = gradu['x'][0] gradu_y = gradu['y'][0] gradu_a = gradu['a'][0] mode_graphique = diagramme["mode_graphique"][0] self.onglets_bas.legende.x.SetValue(legende_x) self.onglets_bas.legende.y.SetValue(legende_y) self.onglets_bas.legende.a.SetValue(legende_a) self.onglets_bas.graduation.x.SetValue(gradu_x) self.onglets_bas.graduation.y.SetValue(gradu_y) self.onglets_bas.graduation.a.SetValue(gradu_a) self.onglets_bas.donnees.valeurs.SetValue(valeurs) self.onglets_bas.donnees.classes.SetValue(classes) print('mode_graphique', mode_graphique) self.graph = mode_graphique self.actualiser() wxgeometrie-0.133.2.orig/wxgeometrie/modules/readme.txt0000644000175000017500000000244412014170666023370 0ustar georgeskgeorgeskMODULES _______ Ce rpertoire contient tous les modules de WxGomtrie. L'activation d'un module au dmarrage de WxGomtrie dpend de sa prsence dans param.__init__.py Pour activer/dsactiver des modules, ditez ce fichier avec un diteur de texte, et modifiez la ligne : modules = ("geometre", "traceur", "statistiques", "calculatrice", "probabilites", "surfaces") Un fichier description.py permet d'intgrer le module dans l'installeur pour Windows. Depuis la version 0.131, un module n'est pas reconnu si ce fichier est absent. Format du fichier description.py : ---------------------------------- # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? # groupe : "Modules" pour tous les modules defaut peut valoir True, False ou None. (None signifie que le module est ncessairement install) Exemple de fichier 'description.py': # -*- coding: iso-8859-1 -*- description = { "titre": u"Calculatrice", "description": u"Calculatrice avance oriente mathmatiques destine au collge/lyce.", "groupe": u"Modules", "defaut": True, } Pour plus de dtails concernant la cration de nouveaux modules pour WxGomtrie, voir : doc/developpeurs/documentation de l'API.pdf wxgeometrie-0.133.2.orig/wxgeometrie/modules/calculatrice/0000755000175000017500000000000012014170666024021 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/calculatrice/description.py0000644000175000017500000000071112014170666026715 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Calculatrice", "description": u"Calculatrice avance oriente mathmatiques destine au collge/lyce.", "groupe": u"Modules", "defaut": True, }wxgeometrie-0.133.2.orig/wxgeometrie/modules/calculatrice/_param_/0000755000175000017500000000000012014170666025417 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/calculatrice/_param_/__init__.py0000644000175000017500000000161512014170666027533 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) calcul_exact = True # gerer les fractions et les racines de maniere exacte si possible. ecriture_scientifique = False # afficher les resultats en ecriture scientifique. changer_separateurs = False # appliquer les separateurs personnalises separateurs_personnels = (",", ";") # separateurs personnalises (separateur decimal, separateur de listes) # d'autres choix sont possibles, mais pas forcement heureux... copie_automatique = False # copie automatique de chaque resultat dans le presse-papier copie_automatique_LaTeX = False formatage_OOo = True formatage_LaTeX = True ecriture_scientifique_decimales = 2 precision_calcul = 60 precision_affichage = 18 forme_affichage_complexe = ("algebrique", "exponentielle")[0] # Fonction appliquer tout rsultat avant de le renvoyer : appliquer_au_resultat = None wxgeometrie-0.133.2.orig/wxgeometrie/modules/calculatrice/__init__.py0000644000175000017500000005570312014170666026144 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Calculatrice # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas from matplotlib.figure import Figure from ...GUI.ligne_commande import LigneCommande from ...GUI.wxlib import png from ...GUI.inspecteur import FenCode from ...GUI import MenuBar, Panel_simple from ...mathlib.interprete import Interprete from ...mathlib.end_user_functions import __classement__ from ...pylib import print_error, uu, debug, no_argument, eval_safe from ... import param class CalculatriceMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", ["quitter"]) self.ajouter(u"Affichage", ["onglet"]) for rubrique in __classement__: self.ajouter(rubrique, *(self.formater(contenu, rubrique != "Symboles") for contenu in __classement__[rubrique])) # pas de parenthese apres un symbole self.ajouter(u"Outils", [u"Mmoriser le rsultat", u"Copie le resultat du calcul dans le presse-papier, afin de pouvoir l'utiliser ailleurs.", "Ctrl+M", self.panel.vers_presse_papier], [u"Rinitialiser", u"Rinitialiser la calculatrice.", "Ctrl+I", self.panel.initialiser], # [u"LaTeX", # [u"Inverser les sommes", "Inverser l'ordre d'affichage des termes des sommes.", None, self.panel.inverser_sommes_LaTeX, self.panel.inverser_sommes_LaTeX] # ], [u"options"], ) self.ajouter(u"Avanc", [u"tat interne de l'interprte", u"tat de l'interprte de commandes.", u"Ctrl+H", self.panel.EtatInterne], [u"ligne_commande"], ["debug"], ) self.ajouter("?") def formater(self, contenu, parentheses = True): if contenu is None: return titre, nom, doc = contenu return [titre, doc, "", self.generer_fonction(nom, parentheses)] def generer_fonction(self, nom, parenthese = True): def f(event = None, panel = self.panel, nom = nom, parenthese = parenthese): deb, fin = panel.entree.GetSelection() if parenthese: panel.entree.SetInsertionPoint(fin) panel.entree.WriteText(")") panel.entree.SetInsertionPoint(deb) panel.entree.WriteText(nom + "(") panel.entree.SetFocus() if deb == fin: final = fin + len(nom) + 1 else: final = fin + len(nom) + 2 else: panel.entree.WriteText(nom) final = fin + len(nom) panel.entree.SetFocus() panel.entree.SetInsertionPoint(final) panel.entree.SetSelection(final, final) return f class Calculatrice(Panel_simple): __titre__ = u"Calculatrice" # Donner un titre a chaque module def __init__(self, *args, **kw): Panel_simple.__init__(self, *args, **kw) self.interprete = Interprete(calcul_exact = self.param("calcul_exact"), ecriture_scientifique = self.param("ecriture_scientifique"), changer_separateurs = self.param("changer_separateurs"), separateurs_personnels = self.param("separateurs_personnels"), copie_automatique = self.param("copie_automatique"), formatage_OOo = self.param("formatage_OOo"), formatage_LaTeX = self.param("formatage_LaTeX"), ecriture_scientifique_decimales = self.param("ecriture_scientifique_decimales"), precision_calcul = self.param("precision_calcul"), precision_affichage = self.param("precision_affichage"), simpify = True, ) ## self.entrees = wx.BoxSizer(wx.HORIZONTAL) ## self.entree = wx.TextCtrl(self, size = (550, -1), style = wx.TE_PROCESS_ENTER) ## self.entrees.Add(self.entree, 1, wx.ALL|wx.GROW, 5) ## self.valider = wx.Button(self, wx.ID_OK) ## self.entrees.Add(self.valider, 0, wx.ALL, 5) self.entree = LigneCommande(self, longueur = 550, action = self.affichage_resultat) self.entree.SetToolTip(wx.ToolTip(u"[Maj]+[Entre] pour une valeur approche.")) self.corps = wx.BoxSizer(wx.HORIZONTAL) self.gauche = wx.BoxSizer(wx.VERTICAL) self.resultats = wx.TextCtrl(self, size = (450,310), style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH) self.gauche.Add(self.resultats, 0, wx.ALL, 5) self.figure = Figure(figsize=(5,1.3),frameon=True, facecolor="w") self.visualisation = FigureCanvas(self, -1, self.figure) self.axes = self.figure.add_axes([0, 0, 1, 1], frameon=False) self.axes.axison = False self.pp_texte = self.axes.text(0.5, 0.5, "", horizontalalignment='center', verticalalignment='center', transform = self.axes.transAxes, size=18) self.gauche.Add(self.visualisation, 0, wx.ALL|wx.ALIGN_CENTER, 5) self.corps.Add(self.gauche, 0, wx.ALL, 5) ### Pave numerique de la calculatrice ### # On construit le pav de la calculatrice. # Chaque bouton du pav doit provoquer l'insertion de la commande correspondante. self.pave = wx.BoxSizer(wx.VERTICAL) self.corps.Add(self.pave, 0, wx.ALL, 5) boutons = ["2nde", "ans", "ouv", "ferm", "egal", "7", "8", "9", "div", "x", "4", "5", "6", "mul", "y", "1", "2", "3", "minus", "z", "0", "pt", "pow", "plus", "t", "rac", "sin", "cos", "tan", "exp", "i", "pi", "e", "abs", "mod"] inserer = ["", "ans()", "(", ")", "=", "7", "8", "9", "/", "x", "4", "5", "6", "*", "y", "1", "2", "3", "-", "z", "0", ".", "^", "+", "t", "sqrt(", ("sin(", "asin(", "sinus / arcsinus"), ("cos(", "acos(", "cosinus / arccosinus"), ("tan(", "atan(", "tangente / arctangente"), ("exp(", "ln(", "exponentielle / logarithme neperien"), ("i", "cbrt(", "i / racine cubique"), ("pi", "sinh(", "pi / sinus hyperbolique"), ("e", "cosh", "e / cosinus hyperbolique"), ("abs(", "tanh", "valeur absolue / tangente hyperbolique"), (" mod ", "log10(", "modulo / logarithme decimal")] self.seconde = False # indique si la touche 2nde est activee. def action(event = None): self.seconde = not self.seconde if self.seconde: self.message(u"Touche [2nde] active.") else: self.message("") self.actions = [action] for i in range(len(boutons)): # On aligne les boutons de la calculatrice par rangees de 5. if i%5 == 0: self.rangee = wx.BoxSizer(wx.HORIZONTAL) self.pave.Add(self.rangee, 0, wx.ALL,0) # Ensuite, on construit une liste de fonctions, parallelement la liste des boutons. if i > 0: def action(event = None, entree = self.entree, j = i): if type(inserer[j]) == tuple: entree.WriteText(inserer[j][self.seconde]) else: entree.WriteText(inserer[j]) n = entree.GetInsertionPoint() entree.SetFocus() entree.SetInsertionPoint(n) self.seconde = False self.message("") self.actions.append(action) bmp = png('btn_' + boutons[i]) bouton = wx.BitmapButton(self, -1, bmp, style=wx.NO_BORDER) bouton.SetBackgroundColour(self.GetBackgroundColour()) espace = 3 if param.plateforme == "Linux": espace = 0 self.rangee.Add(bouton, 0, wx.ALL, espace) # A chaque bouton, on associe une fonction de la liste. bouton.Bind(wx.EVT_BUTTON, self.actions[i]) if type(inserer[i]) == tuple: bouton.SetToolTipString(inserer[i][2]) self.pave.Add(wx.BoxSizer(wx.HORIZONTAL)) ### Liste des options ### # En dessous du pav apparait la liste des diffrents modes de fonctionnement de la calculatrice. # Calcul exact ligne = wx.BoxSizer(wx.HORIZONTAL) self.cb_calcul_exact = wx.CheckBox(self) self.cb_calcul_exact.SetValue(not self.param("calcul_exact")) ligne.Add(self.cb_calcul_exact, flag = wx.ALIGN_CENTER_VERTICAL) ligne.Add(wx.StaticText(self, -1, u" Valeur approche."), flag = wx.ALIGN_CENTER_VERTICAL) self.pave.Add(ligne) self.cb_calcul_exact.Bind(wx.EVT_CHECKBOX, self.EvtCalculExact) # Notation scientifique ligne = wx.BoxSizer(wx.HORIZONTAL) self.cb_notation_sci = wx.CheckBox(self) self.cb_notation_sci.SetValue(self.param("ecriture_scientifique")) ligne.Add(self.cb_notation_sci, flag = wx.ALIGN_CENTER_VERTICAL) self.st_notation_sci = wx.StaticText(self, -1, u" criture scientifique (arrondie ") ligne.Add(self.st_notation_sci, flag = wx.ALIGN_CENTER_VERTICAL) self.sc_decimales = wx.SpinCtrl(self, -1, size = (45, -1), min = 0, max = 11) self.sc_decimales.SetValue(self.param("ecriture_scientifique_decimales")) ligne.Add(self.sc_decimales, flag = wx.ALIGN_CENTER_VERTICAL) self.st_decimales = wx.StaticText(self, -1, u" dcimales).") ligne.Add(self.st_decimales, flag = wx.ALIGN_CENTER_VERTICAL) self.pave.Add(ligne) self.EvtCalculExact() self.cb_notation_sci.Bind(wx.EVT_CHECKBOX, self.EvtNotationScientifique) self.sc_decimales.Bind(wx.EVT_SPINCTRL, self.EvtNotationScientifique) # Copie du rsultat dans le presse-papier ligne = wx.BoxSizer(wx.HORIZONTAL) self.cb_copie_automatique = wx.CheckBox(self) self.cb_copie_automatique.SetValue(self.param("copie_automatique")) ligne.Add(self.cb_copie_automatique, flag = wx.ALIGN_CENTER_VERTICAL) ligne.Add(wx.StaticText(self, -1, u" Copie du rsultat dans le presse-papier."), flag = wx.ALIGN_CENTER_VERTICAL) self.pave.Add(ligne) self.cb_copie_automatique.Bind(wx.EVT_CHECKBOX, self.EvtCopieAutomatique) # En mode LaTeX ligne = wx.BoxSizer(wx.HORIZONTAL) self.cb_copie_automatique_LaTeX = wx.CheckBox(self) self.cb_copie_automatique_LaTeX.SetValue(self.param("copie_automatique_LaTeX")) ligne.Add(self.cb_copie_automatique_LaTeX, flag = wx.ALIGN_CENTER_VERTICAL) self.st_copie_automatique_LaTeX = wx.StaticText(self, -1, u" Copie au format LaTeX (si possible).") ligne.Add(self.st_copie_automatique_LaTeX, flag = wx.ALIGN_CENTER_VERTICAL) self.pave.Add(ligne) self.EvtCopieAutomatique() self.cb_copie_automatique_LaTeX.Bind(wx.EVT_CHECKBOX, self.EvtCopieAutomatiqueLatex) # Autres options self.options = [(u"Virgule comme sparateur dcimal.", u"changer_separateurs"), ## (u"Copie du rsultat dans le presse-papier.", u"copie_automatique"), (u"Accepter la syntaxe OpenOffice.org", u"formatage_OOo"), (u"Accepter la syntaxe LaTeX", u"formatage_LaTeX"), ] self.options_box = [] for i in range(len(self.options)): ligne = wx.BoxSizer(wx.HORIZONTAL) self.options_box.append(wx.CheckBox(self)) ligne.Add(self.options_box[i], flag = wx.ALIGN_CENTER_VERTICAL) self.options_box[i].SetValue(self.param(self.options[i][1])) def action(event, chaine = self.options[i][1], entree = self.entree, self = self): self.param(chaine, not self.param(chaine)) entree.SetFocus() self.options_box[i].Bind(wx.EVT_CHECKBOX, action) ligne.Add(wx.StaticText(self, -1, " " + self.options[i][0]), flag = wx.ALIGN_CENTER_VERTICAL) self.pave.Add(ligne) self.option1 = wx.BoxSizer(wx.HORIZONTAL) self.pave.Add(self.option1) #wx.CheckBox(self) self.option2 = wx.BoxSizer(wx.HORIZONTAL) self.pave.Add(self.option2) self.sizer = wx.BoxSizer(wx.VERTICAL) ## self.sizer.Add(self.entrees, 0, wx.ALL, 5) self.sizer.Add(self.entree, 0, wx.ALL, 5) self.sizer.Add(self.corps, 0, wx.ALL, 5) self.SetSizer(self.sizer) self.Fit() # historique des calculs ## self.entree.Bind(wx.EVT_KEY_UP, self.EvtChar) self.entree.texte.Bind(wx.EVT_RIGHT_DOWN, self.EvtMenu) ## #self.Bind(wx.EVT_CHAR, self.EvtChar, self.entree) ## self.valider.Bind(wx.EVT_BUTTON, self.affichage_resultat) self.visualisation.Bind(wx.EVT_RIGHT_DOWN, self.EvtMenuVisualisation) self.initialiser() def activer(self): # Actions effectuer lorsque l'onglet devient actif self.entree.SetFocus() def _sauvegarder(self, fgeo): fgeo.contenu["Calculatrice"] = [{}] fgeo.contenu["Calculatrice"][0]["Historique"] = [repr(self.entree.historique)] # fgeo.contenu["Calculatrice"][0]["Resultats"] = [repr(self.interprete.derniers_resultats)] fgeo.contenu["Calculatrice"][0]["Affichage"] = [self.resultats.GetValue()] fgeo.contenu["Calculatrice"][0]["Etat_interne"] = [self.interprete.save_state()] fgeo.contenu["Calculatrice"][0]["Options"] = [{}] for i in range(len(self.options)): fgeo.contenu["Calculatrice"][0]["Options"][0][self.options[i][1]] = [str(self.options_box[i].GetValue())] def _ouvrir(self, fgeo): if fgeo.contenu.has_key("Calculatrice"): calc = fgeo.contenu["Calculatrice"][0] self.initialiser() self.entree.historique = eval_safe(calc["Historique"][0]) # self.interprete.derniers_resultats = securite.eval_safe(calc["Resultats"][0]) self.resultats.SetValue(calc["Affichage"][0] + "\n") self.interprete.load_state(calc["Etat_interne"][0]) liste = calc["Options"][0].items() options = [option for aide, option in self.options] for key, value in liste: value = eval_safe(value[0]) self.param(key, value) if key in options: self.options_box[options.index(key)].SetValue(value) # il faudrait encore sauvegarder les variables, mais la encore, 2 problemes : # - pb de securite pour evaluer les variables # - pb pour obtenir le code source d'une fonction. # Pour remedier a cela, il faut envisager de : # - creer un module d'interpretation securisee. # - rajouter a chaque fonction un attribut __code__, ou creer une nouvelle classe. def modifier_pp_texte(self, chaine): u"""Modifier le rsultat affich en LaTeX (pretty print).""" if self.param("latex"): chaine = "$" + chaine + "$" else: chaine = chaine.replace("\\mapsto", "\\rightarrow") if chaine.startswith(r"$\begin{bmatrix}"): chaine = chaine.replace(r"\begin{bmatrix}", r'\left({') chaine = chaine.replace(r"\end{bmatrix}", r'}\right)') chaine = chaine.replace(r"&", r'\,') self.pp_texte.set_text(chaine) self.visualisation.draw() def vers_presse_papier(self, event = None, texte = None): if texte is None: texte = self.dernier_resultat Panel_simple.vers_presse_papier(texte) def copier_latex(self, event = None): self.vers_presse_papier(texte = self.interprete.latex_dernier_resultat.strip("$")) def initialiser(self, event = None): self.dernier_resultat = "" # dernier resultat, sous forme de chaine formatee pour l'affichage self.entree.initialiser() self.interprete.initialiser() self.resultats.Clear() def affichage_resultat(self, commande, **kw): # Commandes spciales: if commande in ('clear', 'clear()', 'efface', 'efface()'): self.initialiser() self.modifier_pp_texte(u"Calculatrice rinitialise.") return self.modifie = True try: try: if kw["shift"]: self.interprete.calcul_exact = False resultat, latex = self.interprete.evaluer(commande) if latex == "$?$": # provoque une erreur (matplotlib 0.99.1.1) latex = u"Dsol, je ne sais pas faire..." finally: self.interprete.calcul_exact = self.param('calcul_exact') aide = resultat.startswith("\n== Aide sur ") #LaTeX debug("Expression LaTeX: " + latex) try: self.modifier_pp_texte((latex or resultat) if not aide else '') except Exception: print_error() self.modifier_pp_texte("") #Presse-papier self.dernier_resultat = resultat if self.param("copie_automatique"): if self.param("copie_automatique_LaTeX"): self.copier_latex() else: self.vers_presse_papier() # TextCtrl numero = str(len(self.interprete.derniers_resultats)) # vite le dcalage entre la premire ligne et les suivantes (matrices) if "\n" in resultat and not aide: resultat = "\n" + "\n".join(20*" " + ligne for ligne in resultat.split("\n")) self.resultats.AppendText(u" Calcul n\xb0" + numero + " : " + uu(commande) + u"\n Rsultat :" + " "*(4+len(numero)) + resultat + "\n__________________\n\n") self.message(u"Calcul effectu." + self.interprete.warning) self.entree.Clear() self.resultats.SetInsertionPoint(len(self.resultats.GetValue())) self.resultats.SetFocus() self.resultats.ScrollLines(1) self.entree.SetFocus() except Exception: self.message(u"Calcul impossible.") self.entree.SetFocus() if param.debug: raise def EvtMenu(self, event): if not event.ControlDown(): event.Skip() return def generer_fonction(nom, parenthese = True, self = self): def f(event = None, panel = self, nom = nom, parenthese = parenthese): deb, fin = panel.entree.GetSelection() if parenthese: panel.entree.SetInsertionPoint(fin) panel.entree.WriteText(")") panel.entree.SetInsertionPoint(deb) panel.entree.WriteText(nom + "(") panel.entree.SetFocus() if deb == fin: final = fin + len(nom) + 1 else: final = fin + len(nom) + 2 else: panel.entree.WriteText(nom) final = fin + len(nom) panel.entree.SetFocus() panel.entree.SetInsertionPoint(final) panel.entree.SetSelection(final, final) return f menu = wx.Menu() menu.SetTitle(u"Fonctions mathmatiques") debut = True for rubrique in __classement__: if not debut: menu.AppendSeparator() debut = False for titre, nom, doc in __classement__[rubrique]: i = wx.NewId() menu.Append(i, titre, doc) if rubrique != "Symboles": menu.Bind(wx.EVT_MENU, generer_fonction(nom), id =i) else: menu.Bind(wx.EVT_MENU, generer_fonction(nom, False), id =i) # pas de parenthese apres un symbole self.PopupMenu(menu) menu.Destroy() # self.entree.SetFocus() def EvtMenuVisualisation(self, event): menu = wx.Menu() i = wx.NewId() menu.Append(i, "Copier LaTeX", "Copier le code LaTeX dans le presse-papier.") menu.Bind(wx.EVT_MENU, self.copier_latex, id=i) self.PopupMenu(menu) menu.Destroy() def param(self, parametre, valeur = no_argument, defaut = False): if valeur is not no_argument: setattr(self.interprete, parametre, valeur) return Panel_simple.param(self, parametre = parametre, valeur = valeur, defaut = defaut) def EvtCalculExact(self, event = None): valeur = self.cb_calcul_exact.GetValue() self.param("calcul_exact", not valeur) if valeur: self.cb_notation_sci.Enable() self.st_notation_sci.Enable() self.sc_decimales.Enable() self.st_decimales.Enable() else: self.cb_notation_sci.Disable() self.st_notation_sci.Disable() self.sc_decimales.Disable() self.st_decimales.Disable() def EvtNotationScientifique(self, event = None): self.param("ecriture_scientifique", self.cb_notation_sci.GetValue()) self.param("ecriture_scientifique_decimales", self.sc_decimales.GetValue()) def EvtCopieAutomatique(self, event = None): valeur = self.cb_copie_automatique.GetValue() self.param("copie_automatique", valeur) if valeur: self.cb_copie_automatique_LaTeX.Enable() self.st_copie_automatique_LaTeX.Enable() else: self.cb_copie_automatique_LaTeX.Disable() self.st_copie_automatique_LaTeX.Disable() def EvtCopieAutomatiqueLatex(self, event = None): self.param("copie_automatique_LaTeX", self.cb_copie_automatique_LaTeX.GetValue()) def EtatInterne(self, event): contenu = self.interprete.save_state() h = FenCode(self, u"tat interne de l'inteprte", contenu, self.interprete.load_state) h.Show(True) wxgeometrie-0.133.2.orig/wxgeometrie/modules/graphes/0000755000175000017500000000000012014170666023017 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/graphes/description.py0000644000175000017500000000100112014170666025704 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Graphes orients, pondrs, probabilistes...", "description": u"Reprsentation de graphes, matrices, et algorithmes courants (Dijkstra, Welsh & Powell...).", "groupe": u"Modules", "defaut": False, } wxgeometrie-0.133.2.orig/wxgeometrie/modules/graphes/barre_outils_graphes.py0000644000175000017500000001227212014170666027600 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # Barre d'outils pour la gomtrie # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #from LIB import * #from GUI.wxlib import png from ...GUI.barre_outils import BarreOutils from ...geolib import Arc_oriente, Arc_points, Point_generique class BarreOutilsGraphes(BarreOutils): def __init__(self, parent, couleur = None): BarreOutils.__init__(self, parent, couleur) def creer_boutons(self): self.add("F1", (u"Pointeur", u"fleche4", u"Dplacer ou modifier un objet.", self.curseur), (u"Zoomer", u"zoombox2", u"Recentrer la zone d'affichage.", self.zoombox), (u"Slectionner", u"selection", u"Slectionner une partie de la feuille.", self.selectionner)).display(True) self.add("F2", (u"Sommet", u"point2",u"Crer un point.", self.point)) self.add("F3", (u"Arte", u"segment2", u"Crer une arte droite.", self.segment)) self.add("F4", (u"Arte oriente", u"vecteur", u"Crer une arte oriente droite.", self.vecteur), ) self.add("F5", (u"Arte courbe", u"arc_points", u"Crer une arte courbe (dfinir 3 points).", self.arc_points), ) self.add("F6", (u"Arte oriente (courbe)", u"arc_oriente", u"Crer une arte oriente courbe (dfinir 3 points).", self.arc_oriente), ) self.add("F7", (u"Texte", u"texte", u"Crer un texte.", self.texte)) self.add("F8", (u"Masquer", u"masquer", u"Masquer des objets.", self.masque)) self.add("F9", (u"Gommer", u"gomme", u"Supprimer des objets.", self.gomme)) self.add("Shift+F2", (u"Copier", u"pinceau", u"Copier le style d'un objet.", self.pinceau)) def arc_points(self, event = False, **kw): if event is False: self.arc(Arc_points, nom_style='arc', **kw) else: self.interagir(self.arc_points, u"Choisissez ou crez 3 points.") def arc_oriente(self, event = False, **kw): if event is False: self.arc(Arc_oriente, nom_style='arcs_orientes', **kw) else: self.interagir(self.arc_oriente, u"Choisissez ou crez 3 points.") def arc(self, classe, nom_style = '', **kw): u"Cration d'un arc dfini par 3 points. Un style spcial est appliqu au point intermdaire." if self.test(True, **kw): self.cache = [obj for obj in self.cache if obj.nom and obj.__feuille__ is self.feuille_actuelle] selection = kw["selection"] n = len(self.cache) if n == 1: # Le point intermdiaire a un style diffrent, # et ne doit donc pas concider avec un point dj existant. point = self.point(nom_style='points_ancrage', editer=None, **kw) self.cache.append(point) style = self.style(nom_style) style["previsualisation"] = True self.feuille_actuelle.objet_temporaire(classe(*(tuple(self.cache) + (self.feuille_actuelle.point_temporaire(),)), **style)) elif isinstance(selection, Point_generique): self.cache.append(selection) nouveau_point = False else: self.cache.append(self.point(**kw)) nouveau_point = True if n == 2: self.feuille_actuelle.objet_temporaire(None) code = classe.__name__ + "(" + ",".join(obj.nom for obj in self.cache) + ", **%s)" %self.style(nom_style) if nouveau_point: # on edite le nom du nouveau point (dernier parametre de self.executer) self.executer(code, editer = self.cache[-1]) else: # si c'est un vieux point, pas besoin d'editer son nom self.executer(code) elif n > 3: # ne se produit que si l'execution a plante... self.initialiser() elif self.cache: style = self.style(nom_style) style["previsualisation"] = True self.feuille_actuelle.objet_temporaire(classe(*(tuple(self.cache) + (self.feuille_actuelle.point_temporaire(),)), **style)) else: self.feuille_actuelle.objet_temporaire(None) wxgeometrie-0.133.2.orig/wxgeometrie/modules/graphes/_param_/0000755000175000017500000000000012014170666024415 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/graphes/_param_/__init__.py0000644000175000017500000000105012014170666026522 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) orthonorme = True afficher_axes = False afficher_barre_outils = True afficher_console_geolib = True afficher_objets_caches = True points = { "style": "o", } points_ancrage = { "style": ".", "visible": False, "couleur": "b", } segments = { "couleur": "k", } arcs = { "couleur": "k", } vecteurs = { "position": .5, "couleur": "k", } arcs_orientes = { "position": .5, "couleur": "k", } wxgeometrie-0.133.2.orig/wxgeometrie/modules/graphes/__init__.py0000644000175000017500000001422412014170666025133 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Geometre # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from collections import defaultdict from random import randint from itertools import count from sympy import latex from ...GUI import MenuBar, Panel_API_graphique from ...mathlib.graphes import Graph, colors, colors_dict from .barre_outils_graphes import BarreOutilsGraphes from ...geolib import Arc_oriente class GraphesMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", [u"nouveau"], [u"ouvrir"], [u"ouvrir ici"], [u"enregistrer"], [u"enregistrer_sous"], [u"exporter"], [u"exporter&sauver"], None, [u"mise en page"], [u"imprimer"], [u"presse-papier"], None, [u"proprietes"], None, self.panel.doc_ouverts, None, [u"fermer"], [u"quitter"]) self.ajouter(u"Editer", [u"annuler"], [u"refaire"], [u"modifier"], [u"supprimer"]) self.ajouter(u"creer") self.ajouter("affichage") self.ajouter("autres") self.ajouter(u"Outils", # [u"Crer le graphe", u"(Entre supprimer).", "Ctrl+E", self.panel.creer_graphe], [u"Colorier le graphe", u"Coloriage par l'algorithme de Welsh & Powell.", None, self.panel.colorier], [u"Latex -> Presse-papier", [u"Dijkstra", u"Recherche d'un trajet minimal entre deux points.", None, self.panel.latex_Dijkstra], [u"Welsh & Powell", u"Coloriage par l'algorithme de Welsh & Powell.", None, self.panel.latex_WelshPowell], [u"Matrice", u"Matrice du graphe.", None, self.panel.latex_Matrix], ], [u"options"], ) ## self.ajouter(u"Avanc", [u"historique"], [u"securise"], [u"ligne_commande"], [u"debug"]) self.ajouter(u"avance1") self.ajouter(u"?") class Graphes(Panel_API_graphique): __titre__ = u"Graphes" # Donner un titre chaque module #_param_ = _param_ def __init__(self, *args, **kw): Panel_API_graphique.__init__(self, *args, BarreOutils = BarreOutilsGraphes, **kw) self.finaliser() def creer_graphe(self, event=None): aretes = list(self.feuille_actuelle.objets.segments) aretes_orientees = list(self.feuille_actuelle.objets.vecteurs) for arc in self.feuille_actuelle.objets.arcs: if isinstance(arc, Arc_oriente): aretes_orientees.append(arc) else: aretes.append(arc) def poids(arete): try: return float(arete.label().replace(',', '.')) except Exception: return 1 dic = {} # Ex: {"A": {"B":[1], "C":[2, 5]}, "B": {}, "C": {"A": [2], "C": [1]}} for sommet in self.feuille_actuelle.objets.points: if sommet.visible: d_sommet = defaultdict(list) for arete in aretes: A, B = arete.extremites if sommet is A: d_sommet[B.nom].append(poids(arete)) elif sommet is B: d_sommet[A.nom].append(poids(arete)) for arete in aretes_orientees: A, B = arete.extremites if sommet is A: d_sommet[B.nom].append(poids(arete)) dic[sommet.nom] = d_sommet self.graph = Graph(dic, oriented=bool(aretes_orientees)) def matrice(self, event=None, creer=True): if creer: self.creer_graphe() return self.graph.matrix def chaine_eulerienne(self, event=None, chaine=None): self.creer_graphe() return self.graph.eulerian_trail(chaine) def colorier(self, event=None): def rnd(): return randint(5, 250) # Range is (5, 250) so as to avoid pure colors (most are already used). def rgb(r, g, b): return (r/255., g/255., b/255.) symbs = ('o', 'D', '*', 's', '<', '>', 'H', '^', 'd', 'h', 'p', 'v') self.creer_graphe() for sommets, colorname, i in zip(self.graph.coloring(), colors(), count()): couleur = colors_dict.get(colorname, (rnd(), rnd(), rnd())) for sommet in sommets: self.feuille_actuelle.objets[sommet].style(couleur=rgb(*couleur), style=symbs[i%len(symbs)]) self.feuille_actuelle.interprete.commande_executee() self.latex_WelshPowell(creer=False) def latex_Dijkstra(self, event=None, creer=True, start=None, end=None): if creer: self.creer_graphe() if start is None: start = min(self.graph.nodes) if end is None: end = max(self.graph.nodes) latex_ = self.graph.latex_Dijkstra(start, end) self.vers_presse_papier(latex_) def latex_WelshPowell(self, event=None, creer=True, first_nodes=()): if creer: self.creer_graph() latex_ = self.graph.latex_WelshPowell(*first_nodes) self.vers_presse_papier(latex_) def latex_Matrix(self, event=None, creer=True): self.vers_presse_papier(latex(self.matrice(creer=creer))) wxgeometrie-0.133.2.orig/wxgeometrie/modules/geometre/0000755000175000017500000000000012014170666023175 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/geometre/description.py0000644000175000017500000000071012014170666026070 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Gomtrie dynamique", "description": u"Cration et manipulation de figures gomtriques dans le plan.", "groupe": u"Modules", "defaut": True, }wxgeometrie-0.133.2.orig/wxgeometrie/modules/geometre/_param_/0000755000175000017500000000000012014170666024573 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/geometre/_param_/__init__.py0000644000175000017500000000030712014170666026704 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) orthonorme = True afficher_axes = False afficher_barre_outils = True afficher_console_geolib = True wxgeometrie-0.133.2.orig/wxgeometrie/modules/geometre/__init__.py0000644000175000017500000000431212014170666025306 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Geometre # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #from GUI import * from ...GUI import MenuBar, Panel_API_graphique class GeometreMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", [u"nouveau"], [u"ouvrir"], [u"ouvrir ici"], [u"enregistrer"], [u"enregistrer_sous"], [u"exporter"], [u"exporter&sauver"], None, [u"mise en page"], [u"imprimer"], [u"presse-papier"], None, [u"proprietes"], None, self.panel.doc_ouverts, None, [u"fermer"], [u"quitter"]) self.ajouter(u"Editer", [u"annuler"], [u"refaire"], [u"modifier"], [u"supprimer"]) self.ajouter(u"creer") self.ajouter("affichage") self.ajouter("autres") self.ajouter(u"Outils", [u"options"]) ## self.ajouter(u"Avanc", [u"historique"], [u"securise"], [u"ligne_commande"], [u"debug"]) self.ajouter(u"avance1") self.ajouter(u"?") class Geometre(Panel_API_graphique): __titre__ = u"Gomtrie dynamique" # Donner un titre chaque module #_param_ = _param_ def __init__(self, *args, **kw): Panel_API_graphique.__init__(self, *args, **kw) self.finaliser() wxgeometrie-0.133.2.orig/wxgeometrie/modules/probabilites/0000755000175000017500000000000012014170666024045 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/probabilites/repetition.py0000644000175000017500000000635012014170666026605 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Probabilités # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA def repetition_experiences(_profondeur=3, _numeroter=True, **evenements): u"""Génère le code d'un arbre de probababilités correspondant à la répétition d'expériences aléatoires identiques et indépendantes. Typiquement, un schéma de Bernoulli. >>> from wxgeometrie.modules.probabilites import repetition_experiences >>> print(repetition_experiences()) >A_1:0.5 >>A_2:0.5 >>>A_3:0.5 >>>&A_3:0.5 >>&A_2:0.5 >>>A_3:0.5 >>>&A_3:0.5 >&A_1:0.5 >>A_2:0.5 >>>A_3:0.5 >>>&A_3:0.5 >>&A_2:0.5 >>>A_3:0.5 >>>&A_3:0.5 """ #FIXME: rajouter des tests unitaires. if not evenements: evenements = {'A': '1/2', '&A': '1/2'} elif not any(isinstance(proba, basestring) for proba in evenements.itervalues()): if len(evenements) == 1: # On rajoute l'évènement complémentaire nom, proba = evenements.items() nom = (nom[1:] if nom.startswith('&') else nom) proba = 1 - proba evenements[nom] = proba else: total_probas = sum(evenements.itervalues()) reste = 1 - total_probas if abs(reste) > 0.0000000001: # param.tolerance if reste > 0: evenements['?'] = reste else: raise ValueError, "La somme des probabilites depasse 1." def key(nom): u"""Classe les évènements par ordre alphabétique, mais en plaçant les évènements contraires en dernier.""" return nom.replace('&', '_') evenements_tries = sorted(evenements, reverse=True, key=key) lines = [''] for niveau in range(1, _profondeur + 1): prefixe = niveau*'>' suffixe = ('_' + str(niveau) if _numeroter else '') for i in range(len(lines), 0, -1): if lines[i - 1].startswith((niveau - 1)*'>'): for nom in evenements_tries: proba = evenements[nom] lines.insert(i, prefixe + nom + suffixe + ':' + str(proba)) assert len(lines) < 10000 return '\n'.join(lines).strip() wxgeometrie-0.133.2.orig/wxgeometrie/modules/probabilites/description.py0000644000175000017500000000066112014170666026745 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Arbres de probabilits", "description": u"Gnrateur d'arbres de probabilits.", "groupe": u"Modules", "defaut": True, }wxgeometrie-0.133.2.orig/wxgeometrie/modules/probabilites/_param_/0000755000175000017500000000000012014170666025443 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/probabilites/_param_/__init__.py0000644000175000017500000000022412014170666027552 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) afficher_axes = False afficher_quadrillage = False wxgeometrie-0.133.2.orig/wxgeometrie/modules/probabilites/__init__.py0000644000175000017500000003337012014170666026164 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Probabilits # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re import wx from ...GUI import MenuBar, Panel_API_graphique from ...GUI.proprietes_objets import Proprietes from ...geolib import Segment, Texte, Point, TEXTE from ... import param from .repetition import repetition_experiences class ProbaMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", [u"nouveau"], [u"ouvrir"], [u"ouvrir ici"], [u"enregistrer"], [u"enregistrer_sous"], [u"exporter"], [u"exporter&sauver"], None, [u"mise en page"], [u"imprimer"], [u"presse-papier"], None, [u"proprietes"], None, self.panel.doc_ouverts, None, ["fermer"], ["quitter"]) self.ajouter(u"Editer", ["annuler"], ["refaire"], ["modifier"], ["supprimer"]) self.ajouter(u"creer") self.ajouter(u"Affichage", ["onglet"], None, ["repere"], ["quadrillage"], ["orthonorme"], None, ["zoom_texte"], ["zoom_ligne"], ["zoom_general"], None, ["fenetre"], ["zoomer"], ["dezoomer"], ["orthonormaliser"], [u"zoom_auto"]) self.ajouter(u"Autres actions", [u"detecter"]) self.ajouter(u"Outils", [u"Style des sommets", u"Modifier le style des sommets de l'arbre.", None, self.panel.proprietes_sommets], [u"Style des artes", u"Modifier le style des artes de l'arbre.", None, self.panel.proprietes_aretes], [u"Style de la lgende", u"Modifier le style des titres de chaque niveau.", None, self.panel.proprietes_titres], None, [u"options"]) ## self.ajouter(u"Avanc", [u"historique"], [u"securise"], [u"ligne_commande"], [u"debug"]) self.ajouter(u"avance1") self.ajouter(u"?") class Probabilites(Panel_API_graphique): __titre__ = u"Arbre de probabilits" # Donner un titre a chaque module def __init__(self, *args, **kw): Panel_API_graphique.__init__(self, *args, **kw) self.couleurs = u"bgrmkcy" self.entrees = wx.BoxSizer(wx.VERTICAL) self.entrees.Add(wx.StaticText(self, -1, u" Instructions :"), 0, wx.ALL,5) self.instructions = wx.TextCtrl(self, size = (200, 300), style = wx.TE_MULTILINE) self.instructions.SetValue("""||Tirage 1|Tirage 2|Tirage 3 omega >A:0,7 >>B:0,2 >>C:0,8 >&A:0,3 >>E:0,1 >>>F >>>G >>>H >>&E:0,9""") self.entrees.Add(self.instructions, 0, wx.ALL,5) self.appliquer = wx.Button(self, label = u"Gnrer l'arbre") self.appliquer.Bind(wx.EVT_BUTTON, self.Appliquer) self.entrees.Add(self.appliquer, 0, wx.ALL,5) self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW) self.sizer.Add(self.entrees, 0, wx.ALL, 5) self.finaliser(contenu = self.sizer) def _sauvegarder(self, fgeo, feuille = None): Panel_API_graphique._sauvegarder(self, fgeo, feuille) fgeo.contenu[u"Instructions"] = [self.instructions.GetValue()] def _ouvrir(self, fgeo): Panel_API_graphique._ouvrir(self, fgeo) if fgeo.contenu.has_key(u"Instructions"): self.instructions.SetValue(fgeo.contenu[u"Instructions"][0]) def Appliquer(self, event): with self.canvas.geler_affichage(actualiser=True, sablier=True): self.creer_feuille() instructions = [instruction for instruction in self.instructions.GetValue().split("\n") if instruction.strip()] nbr_colonnes = 0 if instructions[0].startswith("|"): legende = instructions[0] instructions = instructions[1:] self.canvas.fenetre = -.1, 1.1, -.1, 1.2 else: legende = None self.canvas.fenetre = -.1, 1.1, -.1, 1.1 # Rptition d'expriences alatoires indpendantes # # Ex: # >> A: 1/3 # >> &A: 2/3 # # quivaut : # # > A_1: 1/3 # >> A_2: 1/3 # >> &A_2: 2/3 # > &A_1: 2/3 # >> A_2: 1/3 # >> &A_2: 2/3 if instructions[0].startswith('>>'): profondeur = len(instructions[0]) - len(instructions[0].lstrip('>')) evenements = {} for instruction in instructions: assert instruction.startswith(profondeur*'>') nom, proba = instruction[profondeur:].split(':') evenements[nom.strip()] = proba.strip() instructions = repetition_experiences(_profondeur=profondeur, _numeroter=True, **evenements).split('\n') for instruction in instructions: if instruction.startswith(">"): nbr_colonnes = max(nbr_colonnes, len(instruction) - len(instruction.lstrip(">"))) if instructions[0].startswith(">"): instructions = [""] + instructions nbr_colonnes += 1 nbr_lignes = [] for i in xrange(nbr_colonnes): nbr_lignes.append(len([instruction for instruction in instructions if instruction.startswith(i*">") and not instruction.startswith((i+1)*">")])) #intersection, union : \cap \cup # Interprtation des instructions sous forme de listes de listes # >A # >>B # >>C # >D # >>E # >>>F # >>>G # >>H # >E # devient : # [{'liste': [{'liste': [{'liste': [], 'texte': u'B'}, {'liste': [], 'texte': u'C'}], 'texte': u'A'}, {'liste': [{'liste': [{'liste': [], 'texte': u'F'}, {'liste': [], 'texte': u'G'}], 'texte': u'E'}, {'liste': [], 'texte': u'H'}], 'texte': u'D'}, {'liste': [], 'texte': u'E'}], 'texte': ''}] arbre = [] ligne_precedente = [-1 for i in xrange(nbr_colonnes)] # numro de ligne atteint pour chaque colonne for instruction in instructions: colonne = len(instruction) - len(instruction.lstrip(">")) branche = arbre for i in xrange(colonne): # on se dplace de branche en branche ;) branche = branche[ligne_precedente[i]]["liste"] branche.append({"texte": instruction.lstrip(">"), "liste": []}) ligne_precedente[colonne] += 1 for i in xrange(colonne + 1, nbr_colonnes): ligne_precedente[i] = -1 print arbre # on parcourt l'arbre pour compter le nombre de ramifications def compter_ramifications(branche): if len(branche["liste"]) > 0: return sum(compter_ramifications(tige) for tige in branche["liste"]) else: return 1 ramifications = sum(compter_ramifications(branche) for branche in arbre) print ramifications def formater_texte(texte): if texte: if param.latex: if texte.startswith("&"): texte = r"\overline{" + texte[1:] + "}" texte = texte.replace("&{", r"\overline{") texte = texte.replace("&", r"\bar ") else: if texte.startswith("&"): texte = r"\bar{" + texte[1:] + "}" texte = texte.replace("&", r"\bar ") texte = "$" + texte + "$" if texte[0] != '$' else texte if param.latex: texte = "$" + texte + "$" # passage en mode "display" de LaTeX texte = texte.replace(" inter ", r"\ \cap \ ").replace(" union ", r"\ \cup \ ").replace("Omega", r"\Omega").replace("omega", r"\Omega") if param.latex: # on remplace les fractions. Ex: "1/15" -> "\frac{1}{15}" texte = re.sub("[0-9]+/[0-9]+",lambda s:"\\frac{" + s.group().replace("/", "}{") + "}", texte) return texte def creer_point(x, y, texte): texte = formater_texte(texte) M = Point(x, y, legende = TEXTE, label = texte, style = "o", couleur = "w", taille = 0) M.etiquette.style(_rayon_ = 0, niveau = 15, alignement_vertical = "center", alignement_horizontal = "center", fond = "w") return M def creer_segment(point1, point2, texte): texte = formater_texte(texte) s = Segment(point1, point2, legende = TEXTE, label = texte) s.etiquette.style(_rayon_ = 0, niveau = 15, alignement_vertical = "center", alignement_horizontal = "center", fond = "w") return s ramification = [0] # astuce pour avoir un "objet modifiable" (a mutable object) def parcourir_branche(branche, n, ramification = ramification): txt = branche["texte"].rsplit(":", 1) if len(txt) == 2: txt_pt, txt_segm = txt else: txt_pt = txt[0] txt_segm = "" if len(branche["liste"]) > 0: l = [] for tige in branche["liste"]: l.append(parcourir_branche(tige, n + 1)) M = creer_point(n/(nbr_colonnes - 1), .5*(l[0][0].ordonnee+l[-1][0].ordonnee), txt_pt) self.feuille_actuelle.objets.add(M) for point, txt_s in l: s = creer_segment(M, point, txt_s) self.feuille_actuelle.objets.add(s) return M, txt_segm else: M = creer_point(n/(nbr_colonnes - 1), 1 - ramification[0]/(ramifications - 1), txt_pt) ramification[0] += 1 self.feuille_actuelle.objets.add(M) return M, txt_segm for branche in arbre: parcourir_branche(branche, 0) if legende is not None: decalage = -0.5 for i in legende: if i != "|": break decalage += .5 legende = legende.strip("|").split("|") for n in xrange(len(legende)): t = Texte(legende[n], (n + decalage)/(nbr_colonnes - 1), 1.1) self.feuille_actuelle.objets.add(t) self.feuille_actuelle.interprete.commande_executee() def info_proprietes(self, titre): dlg = wx.MessageDialog(self, u"Crez l'arbre au pralable.", titre, wx.OK) dlg.ShowModal() dlg.Destroy() def proprietes_sommets(self, event = None): objets = self.feuille_actuelle.objets.lister(type = Point) if not objets: self.info_proprietes(u'Aucun sommet.') return win = Proprietes(self, objets) win.Show(True) def proprietes_aretes(self, event = None): objets = self.feuille_actuelle.objets.lister(type = Segment) if not objets: self.info_proprietes(u'Aucune arte.') return win = Proprietes(self, objets) win.Show(True) def proprietes_titres(self, event = None): objets = self.feuille_actuelle.objets.lister(type = Texte) if not objets: self.info_proprietes(u'Aucun titre.') return win = Proprietes(self, objets) win.Show(True) def _affiche(self): pass def assistant(self, event = None, liste = None): """Cre un arbre en supposant les vnements de la liste tous indpendants entre eux. Exemple: self.assistant(liste = ["A:0.4", "B:0.7"]) On peut spcifier explicitement les vnements contraires: self.assistant(liste = [("G_1:0.4", "P_1:0.6"), ("G_2:0.5", "P_2:0.5")])""" lignes = [""] niveau = 0 for couple in liste: if isinstance(couple, str): evt1 = couple evt2 = None else: evt1, evt2 = couple niveau += 1 for i in xrange(len(lignes) - 1, -1, -1): if evt2 is None: if ":" in evt1: evt, proba = evt1.rsplit(":", 1) else: evt = evt1 proba = "" try: proba = str(1-float(proba)) except ValueError: proba = "1-" + proba evt2 = "&" + evt + ":" + proba lignes.insert(i, niveau*">" + evt2) lignes.insert(i, niveau*">" + evt1) print "test", "\n".join(lignes) self.instructions.SetValue("\n".join(lignes)) def bernouilli(self, event = None, n = 3, evt1 = "", evt2 = None): self.assistant(liste = n*[(evt1, evt2)]) wxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/0000755000175000017500000000000012014170666023033 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/description.py0000644000175000017500000000065112014170666025732 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Traceur de courbes", "description": u"Traceur de courbes dans le plan.", "groupe": u"Modules", "defaut": True, }wxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/suites.py0000644000175000017500000002024312014170666024722 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Suites # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from ...GUI.wxlib import MyMiniFrame from ...geolib import Point, Segment, Droite, RIEN, TEXTE from ...pylib import eval_safe class CreerSuite(MyMiniFrame): def __init__(self, parent): MyMiniFrame.__init__(self, parent, u"Reprsenter une suite") self.SetExtraStyle(wx.WS_EX_BLOCK_EVENTS ) self.parent = parent self._param_ = self.parent._param_ p = self.panel = wx.Panel(self, -1) self.sizer = sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(wx.StaticText(p, -1, u"Choisissez le mode de gnration de la suite :"), 0, wx.ALIGN_LEFT|wx.ALL,5) self.mode = wx.Choice(p, -1, (100, 50), choices = [u"u(n+1)=f(u(n))", u"u(n)=f(n)"]) self.mode.SetSelection(0) self.Bind(wx.EVT_CHOICE, self.EvtChoixMode, self.mode) sizer.Add(self.mode, 0, wx.ALIGN_LEFT|wx.ALL,5) f = wx.BoxSizer(wx.HORIZONTAL) f.Add(wx.StaticText(p, -1, u"Choisissez la fonction f :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.fonction = wx.Choice(p, -1, (100, 50), choices = ["Y" + str(i+1) for i in xrange(self.parent.nombre_courbes)]) self.fonction.SetSelection(0) self.Bind(wx.EVT_CHOICE, self.EvtChoixFonction, self.fonction) f.Add(self.fonction, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) sizer.Add(f) start = wx.BoxSizer(wx.HORIZONTAL) start.Add(wx.StaticText(p, -1, u"Commencer pour n ="), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.n0 = wx.SpinCtrl(p, -1, "", size = (50, -1)) self.n0.SetRange(0, 1000000) self.n0.SetValue(0) start.Add(self.n0, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) sizer.Add(start) self.terme = terme = wx.BoxSizer(wx.HORIZONTAL) terme.Add(wx.StaticText(p, -1, u"Terme initial :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.un0 = wx.TextCtrl(p, -1, "1", size=(100, -1)) terme.Add(self.un0, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) sizer.Add(terme) sizer.Add(wx.StaticLine(p, -1, style=wx.LI_HORIZONTAL), 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) nbr = wx.BoxSizer(wx.HORIZONTAL) nbr.Add(wx.StaticText(p, -1, u"Construire les"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL |wx.ALL,5) self.termes = wx.SpinCtrl(p, -1, "", size = (50, -1)) self.termes.SetRange(0, 100) self.termes.SetValue(5) nbr.Add(self.termes, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) nbr.Add(wx.StaticText(p, -1, u"premiers termes."), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) sizer.Add(nbr) sizer.Add(wx.StaticLine(p, -1, style=wx.LI_HORIZONTAL), 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) #p.SetSizer(sizer) boutons = wx.BoxSizer(wx.HORIZONTAL) fermer = wx.Button(p, -1, u"Fermer") boutons.Add(fermer, 0, wx.ALL, 5) lancer = wx.Button(p, -1, u"Crer") boutons.Add(lancer, 0, wx.ALL, 5) self.Bind(wx.EVT_BUTTON, self.OnCloseMe, fermer) self.Bind(wx.EVT_BUTTON, self.Creer, lancer) sizer.Add(boutons) p.SetSizerAndFit(sizer) self.SetClientSize(p.GetSize()) def Creer(self, event = None): ## self.parent.creer_feuille() # nouvelle feuille de travail # style des lignes : style, epaisseur = self._param_.style_suites_recurrentes kw_lignes = {"style": style, "epaisseur": epaisseur} # Les suites s'enregistrent auprs du module traceur # if not hasattr(self.parent, "suites"): # self.parent.suites = {} objets = self.parent.feuille_actuelle.objets i=self.fonction.GetSelection() nom_courbe = 'Cf' + str(i + 1) if objets.has_key(nom_courbe): courbe = objets[nom_courbe] fonction = courbe.fonction elif self.parent.boites[i].GetValue(): self.parent.EvtChar(i=i) courbe = objets[nom_courbe] fonction = courbe.fonction else: # TODO: afficher un vrai message d'erreur raise KeyError, "courbe inexistante : %s" %nom_courbe if self.mode.GetSelection() == 0: # cas des suites dfinies par rcurrence u0 = eval_safe(self.un0.GetValue()) n0 = self.n0.GetValue() objets.suiteDroited = Droite(Point(0, 0), Point(1, 1), legende = TEXTE, label = "$y\ =\ x$") M = objets.suitePointM0 = Point(u0, 0, legende = TEXTE, label = "$u_%s$" %(n0)) # self.parent.suites["u"] = [d, M] for i in xrange(self.termes.GetValue() - 1): # (Attention, a ne va pas marcher pour les fonctions dfinies par morceau) u1 = fonction(u0) N = Point(u0, u1, legende = RIEN, visible = self._param_.afficher_points_de_construction) s = Segment(M, N, **kw_lignes) P = Point(0, u1, legende = TEXTE, label = "$u_%s$" %(i + n0 + 1)) t = Segment(N, P, **kw_lignes) Q = Point(u1, u1, legende = RIEN, visible = self._param_.afficher_points_de_construction) r = Segment(P, Q, **kw_lignes) M = Point(u1, 0, legende = TEXTE, label = "$u_%s$" %(i + n0 + 1)) #self.parent.suites[u"u"].append([M, N, P, s, t]) setattr(objets, "SuitePointN" + str(i), N) setattr(objets, "suitePointP" + str(i), P) setattr(objets, "suitePointQ" + str(i), Q) setattr(objets, "suiteSegments" + str(i), s) setattr(objets, "suiteSegmentt" + str(i), t) setattr(objets, "suiteSegmentr" + str(i), r) setattr(objets, "suitePointM" + str(i + 1), M) a = Segment(Q, M, **kw_lignes) setattr(objets, "suiteSegmenta" + str(i), a) u0 = u1 self.parent.canvas.zoom_auto() else: # suites dfinies explicitement n0 = self.n0.GetValue() # self.parent.suites[u"u"] = [] for i in xrange(n0, n0 + self.termes.GetValue()): yi = fonction(i) M = Point(i, 0, legende = TEXTE, label = str(i)) N = Point(i, yi, legende = RIEN) P = Point(0, yi, legende = TEXTE, label = "$u_%s$" %i) s = Segment(M, N, **kw_lignes) t = Segment(N, P, **kw_lignes) setattr(objets, "suitePointM" + str(i), M) setattr(objets, "suitePointN" + str(i), N) setattr(objets, "suitePointP" + str(i), P) setattr(objets, "suiteSegments" + str(i), s) setattr(objets, "suiteSegmentt" + str(i), t) self.parent.canvas.zoom_auto() def EvtChoixMode(self, event): if event.GetSelection() == 1: self.terme.ShowItems(False) else: self.terme.ShowItems(True) def EvtChoixFonction(self, event): n = event.GetSelection() for i in xrange(self.parent.nombre_courbes): self.parent.boites[i].SetValue(i == n) def OnCloseMe(self, event): self.Close(True) wxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/tableau.py0000644000175000017500000001155012014170666025024 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) #import param #from pylib import * import wx.grid as gridlib import wx #--------------------------------------------------------------------------- class EditorsAndRenderersGrid(gridlib.Grid): def __init__(self, parent): gridlib.Grid.__init__(self, parent, -1) self.SetDefaultCellAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) self.CreateGrid(8, 2) self.EnableDragRowSize(False) self.SetColLabelValue(0, "X") self.SetColLabelValue(1, "Y") self.SetCellValue(0, 0, "Valeurs") self.SetCellValue(0, 1, "Valeur libre") self.liste = ["Valeur libre"] + ["Y%s"%(i+1) for i in range(param.nombre_courbes)] self.choix = gridlib.GridCellChoiceEditor(self.liste, False) self.SetCellEditor(0, 1, self.choix) self.SetReadOnly(0, 0, True) #self.SetCellAlignment(0, 1, wx.CENTER, wx.CENTER) self.SetRowLabelSize(30) self.SetColSize(0, 75) self.SetColSize(1, 75) self.AutoSizeRows(True) self.Bind(gridlib.EVT_GRID_CELL_LEFT_DCLICK, self.OnLeftDClick) self.Bind(gridlib.EVT_GRID_CELL_CHANGE, self.OnChange) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.SetGridCursor(1, 0) #self.SetScrollbars(0, 0, 0, 0) def OnKeyDown(self, evt): if evt.KeyCode() <> wx.WXK_DELETE: evt.Skip() else: print self.GetSelectedCells() for couple in self.GetSelectedCells(): self.SetCellValue(couple[0], couple[1], "") print self.GetSelectionBlockTopLeft() print self.GetSelectionBlockBottomRight() for (couple0, couple1) in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): i0, j0 = couple0 i1, j1 = couple1 couples = ((i,j) for i in range(i0, i1 + 1) for j in range(j0, j1 + 1)) for couple in couples: self.SetCellValue(couple[0], couple[1], "") self.SetCellValue(self.GetGridCursorRow(), self.GetGridCursorCol(), "") #self.SetScrollbars(0, 0, 0, 0) # I do this because I don't like the default behaviour of not starting the # cell editor on double clicks, but only a second click. def OnLeftDClick(self, event = None): if self.CanEnableCellControl(): self.EnableCellEditControl() #self.SetScrollbars(0, 0, 0, 0) def OnChange(self, event = None): #print help(event.__class__) l = event.GetRow() c = event.GetCol() print event.__class__.__name__ if c == 0 and l <> 0: try: choix_fonction = self.choix.GetValue() if choix_fonction <> self.liste[0]: i = int(choix_fonction[1:]) intervalles = self.intervalles[i].GetValue().split("|") equations = self.equations[i].GetValue().split("|") self.SetCellValue(l, 1, str(float(self.GetCellValue(l, 0))**2)) except: event.Veto() if l == self.GetNumberRows() - 1: self.AppendRows() self.MoveCursorDown(False) #self.SetScrollbars(0, 0, 0, 0) #--------------------------------------------------------------------------- class TableauValeurs(wx.MiniFrame): def __init__(self, parent): wx.MiniFrame.__init__(self, parent, -1, "Tableau de valeurs", style=wx.DEFAULT_FRAME_STYLE | wx.TINY_CAPTION_HORIZ, size=(250,300)) self.parent = parent p = self.panel = wx.Panel(self, -1) sizer = wx.BoxSizer(wx.VERTICAL) barre = wx.BoxSizer(wx.HORIZONTAL) creer = wx.Button(p, -1, "Creer les points") effacer = wx.Button(p, -1, "Effacer") barre.Add(creer, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5) barre.Add(effacer, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5) self.grid = grid = EditorsAndRenderersGrid(p) self.Bind(wx.EVT_BUTTON, self.efface, effacer) sizer.Add(barre, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5) sizer.Add(grid, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND , 5) #p.SetSizer(sizer) p.SetSizerAndFit(sizer) x, y = p.GetSize().GetWidth(), p.GetSize().GetHeight() self.SetClientSize(wx.Size(x + 15, y + 15)) grid.AdjustScrollbars() def efface(self, event = None): self.grid.ClearGrid() #--------------------------------------------------------------------------- if __name__ == '__main__': import sys app = wx.PySimpleApp() frame = TableauValeurs(None) frame.Show(True) app.MainLoop() #--------------------------------------------------------------------------- wxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/_param_/0000755000175000017500000000000012014170666024431 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/_param_/__init__.py0000644000175000017500000000050712014170666026544 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- nombre_courbes = 9 afficher_barre_outils = False afficher_console_geolib = False # Paramtres concernant les suites ################################## # Style et paisseur des traits (suite rcurrente) style_suites_recurrentes = ("-", .5) afficher_points_de_construction = False wxgeometrie-0.133.2.orig/wxgeometrie/modules/traceur/__init__.py0000644000175000017500000002446312014170666025155 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # :--------------------------------------------: # : Traceur : # :--------------------------------------------: # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from functools import partial import re import wx from ...GUI import MenuBar, Panel_API_graphique from ...geolib import Courbe, Fonction from . import suites class TraceurMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", [u"nouveau"], [u"ouvrir"], [u"ouvrir ici"], [u"enregistrer"], [u"enregistrer_sous"], [u"exporter"], [u"exporter&sauver"], None, [u"mise en page"], [u"imprimer"], [u"presse-papier"], None, [u"proprietes"], None, self.panel.doc_ouverts, None, ["fermer"], ["quitter"]) self.ajouter(u"Editer", ["annuler"], ["refaire"], ["modifier"], ["supprimer"]) self.ajouter(u"creer") self.ajouter("affichage") self.ajouter("autres") self.ajouter(u"Outils", #[u"Tableau de valeurs", u"Tableaux de valeurs des fonctions.", u"Ctrl+T", self.panel.tableau], [u"Reprsenter une suite", u"Reprsenter une suite numrique.", None, self.panel.suite], None, [u"options"]) self.ajouter(u"avance1") self.ajouter(u"?") class Traceur(Panel_API_graphique): __titre__ = u"Traceur de courbes" # Donner un titre a chaque module def __init__(self, *args, **kw): Panel_API_graphique.__init__(self, *args, **kw) self.couleurs = u"bgrmkcy" self.nombre_courbes = self._param_.nombre_courbes self.boites = [] self.equations = [] self.intervalles = [] self.entrees = wx.BoxSizer(wx.VERTICAL) self.entrees.Add(wx.StaticText(self, -1, u" Equations :"), 0, wx.ALL,5) for i in range(self.nombre_courbes): ligne = wx.BoxSizer(wx.HORIZONTAL) self.boites.append(wx.CheckBox(self, label='f%s:'%(i+1))) self.boites[-1].SetValue(True) # Par defaut, les cases sont cochees. self.boites[i].Bind(wx.EVT_CHECKBOX, self.synchronise_et_affiche) # Bug de WxGtk: on ne peut pas attacher simultanment une fonction # aux vnements EVT_CHECKBOX et EVT_ENTER_WINDOW #self.boites[i].Bind(wx.EVT_ENTER_WINDOW, partial(self.MouseOver, i=i)) self.boites[i].Bind(wx.EVT_LEAVE_WINDOW, self.MouseOver) ligne.Add(self.boites[i], 0, wx.ALIGN_CENTRE|wx.ALL,5) ligne.Add(wx.StaticText(self, -1, "Y ="), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.equations.append(wx.TextCtrl(self, size=(120, -1), style=wx.TE_PROCESS_ENTER)) self.equations[i].Bind(wx.EVT_CHAR, partial(self.EvtChar, i=i)) self.equations[i].Bind(wx.EVT_ENTER_WINDOW, partial(self.MouseOver, i=i)) self.equations[i].Bind(wx.EVT_LEAVE_WINDOW, self.MouseOver) ligne.Add(self.equations[i], 0, wx.ALIGN_CENTRE|wx.ALL,5) ligne.Add(wx.StaticText(self, -1, "sur"), 0, wx.ALIGN_CENTRE|wx.ALL,5) self.intervalles.append(wx.TextCtrl(self, size = (100, -1), style = wx.TE_PROCESS_ENTER)) self.intervalles[i].Bind(wx.EVT_CHAR, partial(self.EvtChar, i=i)) self.intervalles[i].Bind(wx.EVT_ENTER_WINDOW, partial(self.MouseOver, i=i)) self.intervalles[i].Bind(wx.EVT_LEAVE_WINDOW, self.MouseOver) ligne.Add(self.intervalles[i], 0, wx.ALIGN_CENTRE|wx.ALL,5) self.entrees.Add(ligne, 0, wx.ALL, 5) self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW, 0) self.sizer.Add(self.entrees, 0, wx.ALL|wx.GROW, 5) self.finaliser(contenu = self.sizer) self._changement_feuille() def activer(self): # Actions effectuer lorsque l'onglet devient actif self.equations[0].SetFocus() def _changement_feuille(self): u"""Aprs tout changement de feuille.""" if hasattr(self, 'nombre_courbes'): # initialisation termine self._synchroniser_champs() self.feuille_actuelle.lier(self._synchroniser_champs) def _synchroniser_champs(self): u"""On synchronise le contenu des champs de texte avec les courbes. Lors de l'ouverture d'un fichier, ou d'un changement de feuille, ou lorsqu'une commande est excute dans la feuille.""" print "Synchronisation des champs..." for i in xrange(self.nombre_courbes): nom_courbe = 'Cf' + str(i + 1) if self.feuille_actuelle.objets.has_key(nom_courbe): objet = self.feuille_actuelle.objets[nom_courbe] self.boites[i].SetValue(objet.style('visible')) expression = objet.fonction.expression if expression.strip(): self.equations[i].SetValue(expression) self.boites[i].Enable() else: self.boites[i].Disable() ensemble = objet.fonction.ensemble ensemble = re.sub(r"(?<=[][])\+(?=[][])", 'U', ensemble) extremites_cachees = (str(e) for e in objet.fonction.style('extremites_cachees')) parties = ensemble.split('|') j = 0 for partie, extremites in zip(parties, extremites_cachees): def f(m): a, b, c, d = m.groups() return (a if b not in extremites else ' ') + b + ';' \ + c + (d if c not in extremites else ' ') parties[j] = re.sub(r"([][])([^][;]+);([^][;]+)([][])", f, partie) j += 1 self.intervalles[i].SetValue('|'.join(parties)) else: self.boites[i].Disable() self.equations[i].SetValue('') self.intervalles[i].SetValue('') def _synchroniser_courbes(self): u"""Opration inverse : on synchronise les courbes avec le contenu des champs de texte. Aprs un changement dans les champs de textes/cases cocher.""" objets = self.feuille_actuelle.objets for i in xrange(self.nombre_courbes): nom_courbe = 'Cf' + str(i + 1) nom_fonction = 'f' + str(i + 1) expr = self.equations[i].GetValue() ensemble = self.intervalles[i].GetValue() visible = self.boites[i].GetValue() if not expr.strip(): visible = False # self.boites[i].Disable() if self.feuille_actuelle.objets.has_key(nom_fonction): objets[nom_fonction].modifier_expression_et_ensemble(expression = expr, ensemble = ensemble) else: objets[nom_fonction] = Fonction(expr, ensemble, 'x') if self.feuille_actuelle.objets.has_key(nom_courbe): objets[nom_courbe].style(visible = visible) else: f = objets[nom_fonction] objets[nom_courbe] = Courbe(f, protege = True, visible = visible, couleur = self.couleurs[i%len(self.couleurs)]) ## self.canvas.regenerer_liste = True ## def _sauvegarder(self, fgeo): ## Panel_API_graphique._sauvegarder(self, fgeo) ## fgeo.contenu[u"Courbe"] = [{"Y" : [self.equations[i].GetValue()], u"intervalle" : [self.intervalles[i].GetValue()], u"active" : [str(self.boites[i].GetValue())]} for i in range(self.nombre_courbes)] ## def _ouvrir(self, fgeo): ## Panel_API_graphique._ouvrir(self, fgeo) ## if fgeo.contenu.has_key(u"Courbe"): ## for i in range(min(len(fgeo.contenu[u"Courbe"]), self.nombre_courbes)): ## self.equations[i].SetValue(fgeo.contenu[u"Courbe"][i][u"Y"][0]) ## self.intervalles[i].SetValue(fgeo.contenu[u"Courbe"][i][u"intervalle"][0]) ## self.boites[i].SetValue(fgeo.contenu[u"Courbe"][i][u"active"][0] == u"True") ## self.affiche() def _ouvrir(self, fgeo): Panel_API_graphique._ouvrir(self, fgeo) # On synchronise le contenu des champs de texte avec les courbes * la fin*. self._synchroniser_champs() def EvtChar(self, event=None, i=None): assert (i is not None) code = (event.GetKeyCode() if event is not None else wx.WXK_RETURN) if code in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): self.boites[i].SetValue(event is None or not event.ShiftDown()) self.synchronise_et_affiche() elif code == wx.WXK_ESCAPE: self.boites[i].SetValue(False) self.synchronise_et_affiche() else: event.Skip() def MouseOver(self, event=None, i=None): if i is None: self.canvas.select = None else: nom_courbe = 'Cf' + str(i + 1) self.canvas.select = self.feuille_actuelle.objets.get(nom_courbe, None) self.canvas.selection_en_gras() event.Skip() def synchronise_et_affiche(self, event = None): self._synchroniser_courbes() self.action_effectuee(u'Courbes modifies.') self.affiche() #event.Skip() # def tableau(self, event = None): # self.parent.a_venir() # return # table = tableau.TableauValeurs(self) # table.Show(True) #table.SetSize(wx.Size(200,250)) #table.SetDimensions(-1, -1, -1, 300) def suite(self, event = None): suite = suites.CreerSuite(self) suite.Show(True) wxgeometrie-0.133.2.orig/wxgeometrie/modules/cryptographie/0000755000175000017500000000000012014170666024246 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/cryptographie/description.py0000644000175000017500000000065412014170666027150 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Cryptographie ", "description": u"Outils de cryptographie lmentaire.", "groupe": u"Modules", "defaut": False, } wxgeometrie-0.133.2.orig/wxgeometrie/modules/cryptographie/_param_/0000755000175000017500000000000012014170666025644 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/cryptographie/_param_/__init__.py0000644000175000017500000000014012014170666027750 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) wxgeometrie-0.133.2.orig/wxgeometrie/modules/cryptographie/__init__.py0000644000175000017500000002607712014170666026373 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Calculatrice # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from string import ascii_uppercase as majuscules from functools import partial from itertools import cycle, izip from random import shuffle import re import wx #from ...GUI.wxlib import MyFont from ...GUI import MenuBar, Panel_simple from ...pylib import print_error from ... import param dict_accents = { u"": "E", u"": "E", u"": "E", u"": "E", u"": "E", u"": "E", u"": "A", u"": "A", u"": "A", u"": "A", u"": "O", u"": "O", u"": "I", u"": "I", u"": "U", u"": "U", u"": "U", u"": "U", u"": "C", u"": "C", } class CryptographieMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", ["quitter"]) self.ajouter(u"Affichage", ["onglet"]) self.ajouter(u"Outils", [u"Coder un message", u"Code le message par substitution mono-alphabtique.", "Ctrl+K", panel.coder], [u"Coder avec espaces", u"Code le message en conservant les espaces (substitution mono-alphabtique).", "Ctrl+Shift+K", partial(panel.coder, espaces=True)], [u"Gnrer une nouvelle cl", u"Gnrer une nouvelle permutation de l'alphabet.", None, panel.generer_cle], [u"Modifier la cl", u"Gnrer une nouvelle permutation de l'alphabet.", None, panel.DlgModifierCle], None, [u"options"]) self.ajouter(u"avance2") self.ajouter("?") class Cryptographie(Panel_simple): __titre__ = u"Cryptographie" # Donner un titre chaque module def __init__(self, *args, **kw): Panel_simple.__init__(self, *args, **kw) self._freeze = False self._actualiser = None # La cl est la permutation de l'alphabet actuellement utilise self.cle = self.generer_cle() # Signe indiquant un caractre non dchiffr self.symbole = '-' # '.' self.sizer = wx.BoxSizer(wx.VERTICAL) font = self.GetFont() # italic = wx.Font(font.GetPointSize(), font.GetFamily(), wx.ITALIC, wx.NORMAL) bold = wx.Font(font.GetPointSize(), font.GetFamily(), wx.NORMAL, wx.BOLD) self.textes = wx.GridBagSizer(5, 5) size = (400, 300) TE_STYLE = wx.TE_MULTILINE|wx.TE_RICH2#|wx.TE_CHARWRAP txt_clair = wx.StaticText(self, -1, u"Texte en clair") txt_clair.SetFont(bold) self.clair = wx.TextCtrl(self, style=TE_STYLE, size=size) # self.clair.Bind(wx.EVT_LEFT_UP, self.clairLeft) self.clair.Bind(wx.EVT_TEXT, partial(self.formater, widget=self.clair)) self.clair.Bind(wx.EVT_LEFT_UP, partial(self.formater, widget=self.clair)) self.clair.Bind(wx.EVT_KEY_DOWN, partial(self.formater, widget=self.clair)) self.copier_clair = wx.Button(self, label=u'Copier le texte en clair') self.copier_clair.Bind(wx.EVT_BUTTON, partial(self.copier, widget=self.clair)) txt_code = wx.StaticText(self, -1, u"Texte cod") txt_code.SetFont(bold) self.code = wx.TextCtrl(self, style=TE_STYLE, size=size) # self.code.Bind(wx.EVT_LEFT_UP, self.codeLeft) self.code.Bind(wx.EVT_LEFT_UP, partial(self.formater, widget=self.code)) self.code.Bind(wx.EVT_TEXT, self.code_modifie) self.code.Bind(wx.EVT_KEY_DOWN, partial(self.formater, widget=self.code)) self.copier_code = wx.Button(self, label=u'Copier le texte cod') self.copier_code.Bind(wx.EVT_BUTTON, partial(self.copier, widget=self.code)) self.textes.Add(txt_clair, (0, 0), flag=wx.ALIGN_CENTER) self.textes.AddSpacer((50, 1), (0, 1)) self.textes.Add(txt_code, (0, 2), flag=wx.ALIGN_CENTER) self.textes.Add(self.clair, (1, 0), flag=wx.ALIGN_CENTER) self.textes.Add(self.code, (1, 2), flag=wx.ALIGN_CENTER) self.textes.Add(self.copier_code, (2, 2), flag=wx.ALIGN_CENTER) self.textes.Add(self.copier_clair, (2, 0), flag=wx.ALIGN_CENTER) self.table = wx.GridBagSizer(5, 5) self.cases = {} size = (30, -1) self.table.Add(wx.StaticText(self, -1, u"Cod:"), (0, 0), flag=wx.ALIGN_CENTER) self.table.Add(wx.StaticText(self, -1, u"Clair:"), (1, 0), flag=wx.ALIGN_CENTER) for i, l in enumerate(majuscules): txtctrl = wx.TextCtrl(self, value=l, size=size, style=wx.TE_READONLY|wx.TE_CENTRE) txtctrl.Disable() self.table.Add(txtctrl, (0, i + 1)) for i, l in enumerate(majuscules): self.cases[l] = wx.TextCtrl(self, size=size, style=wx.TE_CENTRE) self.cases[l].SetMaxLength(1) self.table.Add(self.cases[l], (1, i + 1)) self.cases[l].Bind(wx.EVT_LEFT_DOWN, partial(self.EvtLeftDown, l=l)) self.cases[l].Bind(wx.EVT_KEY_DOWN, partial(self.EvtKey, l=l)) self.cases[l].Bind(wx.EVT_TEXT, self.decoder) self.sizer.Add(self.textes, 0, wx.ALIGN_CENTER | wx.ALL, 10) self.sizer.Add(self.table, 0, wx.ALIGN_CENTER | wx.ALL, 10) self.SetSizer(self.sizer) self.Fit() couleur_position = wx.Color(255, 205, 179) couleur1 = wx.Color(90, 40, 190) couleur2 = wx.Color(200, 100, 0) black = wx.Color(0, 0, 0) white = wx.Color(255, 255, 255) self.special = wx.TextAttr(wx.NullColour, couleur_position) self.fond = wx.TextAttr(couleur1, wx.NullColour) #"sky blue" self.fond2 = wx.TextAttr(couleur2, wx.NullColour) # "Lime Green" self.defaut = wx.TextAttr(black, white) self.Bind(wx.EVT_IDLE, self.OnIdle) # DEBUG: # self.code.SetValue('WR IRAMXPZRHRDZ IK HRYYOVR AL IRYYBKY RYZ NOALWLZR POM WR NOLZ FKR W BD O VOMIR WRY YLVDRY IR PBDAZKOZLBD RZ WRY RYPOARY RDZMR WRY HBZY OWBMY FKR I QOELZKIR BD VMBKPR WRY WRZZMRY ALDF POM ALDF') def copier(self, evt=None, widget=None): self.vers_presse_papier(widget.GetValue()) def DlgModifierCle(self, evt=None): dlg = wx.TextEntryDialog(self, u"La cl doit tre une permutation de l'alphabet,\nou un chiffre qui indique de combien l'alphabet est dcal.", u"Modifier la cl", str(self.cle)) test = True while test: test = dlg.ShowModal() if test == wx.ID_OK: try: self.modifier_cle(dlg.GetValue()) break except: print_error() if test == wx.ID_CANCEL: break dlg.Destroy() @staticmethod def generer_cle(evt=None): l = list(majuscules) shuffle(l) return ''.join(l) def modifier_cle(self, cle): cle = cle.strip().upper() if cle.isdigit(): n = int(cle) cle = majuscules[n:] + majuscules[:n] # On teste qu'il s'agit bien d'une permutation de l'alphabet: assert ''.join(sorted(cle)) == majuscules self.cle = cle def coder(self, evt=None, cle=None, espaces=False): cle = (self.cle if cle is None else cle) clair = self.clair.GetValue().upper() for key, val in dict_accents.items(): clair = clair.replace(key, val) d = dict(zip(majuscules, cle)) code = ''.join(d.get(s, ' ') for s in clair) code = re.sub(' +', ' ', code) if not espaces: code = code.replace(' ', '') self.code.SetValue(code) @staticmethod def _vigenere(l1, l2): return chr((ord(l1) + ord(l2) - 130)%26 + 65) def coder_vigenere(self, evt=None, msg=None, cle=None): if msg is None: msg = self.clair.GetValue() if cle is None: pass return ''.join(self._vigenere(l1, l2) for l1, l2 in izip(cycle(cle), msg)) def decoder(self, evt=None): code = self.code.GetValue().upper() def f(s): if s in majuscules: return self.cases[s].GetValue() or self.symbole return s clair = ''.join(f(s) for s in code) self.clair.SetValue(clair) if evt is not None: evt.Skip() def EvtLeftDown(self, evt=None, l=None): self.cases[l].SetFocusFromKbd() self.cases[l].SelectAll() def EvtKey(self, evt, l): self.message(u'') n = evt.GetKeyCode() if 65 <= n <= 90 or 97 <= n <= 122: c = chr(n).upper() for case in self.cases.values(): if case.GetValue() == c: self.message(u'La lettre %s est dj utilise !' %c) return self.cases[l].SetValue(c) else: evt.Skip() def code_modifie(self, evt): self.decoder(evt) self.formater(evt, widget=self.code) def formater(self, evt, widget=None): evt.Skip() if self._freeze: return self._actualiser = widget def _formater(self, widget): self._freeze = True txt = widget.GetValue() pos = widget.GetInsertionPoint() if param.plateforme == "Windows": self.copier_clair.SetFocusFromKbd() for w in (self.code, self.clair): w.Freeze() last = w.GetLastPosition() w.SetStyle(0, last, self.defaut) if ' ' in txt: i = 0 fond = self.fond fond2 = self.fond2 while i < last: j = txt.find(' ', i) if j == -1: j = last w.SetStyle(i, j, fond) fond, fond2 = fond2, fond i = j + 1 w.SetStyle(pos, pos + 1, self.special) if param.plateforme == "Windows": wx.CallAfter(w.SetSelection, pos, pos) wx.CallAfter(w.Thaw) else: w.Thaw() widget.SetFocusFromKbd() self._freeze = False self._actualiser = None def OnIdle(self, evt): if self._actualiser is not None and not self.parent.parent.closing: self._formater(self._actualiser) wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/0000755000175000017500000000000012014170666023172 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tablatexlib.py0000644000175000017500000002410512014170666026041 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # tabvar # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re, functools, math import numpy from sympy import oo, nan from ...mathlib import universal_functions as maths from ...mathlib.parsers import traduire_formule, simplifier_ecriture from ...mathlib.custom_functions import nul, custom_str from ...pylib import find_closing_bracket, advanced_split from ...geolib.routines import strip_trailing_zeros from ... import param maths.oo = oo maths.nan = nan maths.num_oo = numpy.inf maths.num_nan = numpy.nan maths.pi = math.pi maths.e = math.e resoudre = functools.partial(nul, intervalle = False) # resoudre = sympy.solve def nice_str(x): "Convertit 8.0 en '8' par exemple." return strip_trailing_zeros(custom_str(x)).replace('.', ',') #TODO: dplacer autant que possibles ces fonctions vers le parser de mathlib. def traduire_latex(expression): return traduire_formule( expression, fonctions = maths.__dict__, ## variables = '[_A-Za-z][_A-Za-z0-9]*', OOo = False, LaTeX = True, changer_separateurs = True, separateurs_personnels = (',', ';')) def convertir_en_latex(chaine): u"Convertit une chaine reprsentant un calcul en code LaTeX." #TODO: problme avec les puissances qui contiennent des fractions #TODO: incorporer cette fonction mathlib, et mettre en place des tests unitaires #TODO: c'est assez lent, optimiser ? # (environ 0.5ms en conditions relles, avec 10 20 appels par tableau). ## time0 = time.time() chaine = chaine.replace("**", "^") chaine = re.sub(r'[ ]+', ' ', chaine) # inutile en LaTeX, mais a peut simplifier certaines expressions regulieres chaine = re.sub(r'[ ]?/[ ]?', '/', chaine) chaine = re.sub(r'[ ]?\^[ ]?', '^', chaine) fonctions = ('cos', 'sin', 'tan', 'ln', 'log', 'exp', 'sqrt', '^') # On traite maintenant le (dlicat) cas des fractions, # ie. 2/3, mais aussi (pi+3)/(5-e), ou cos(2)/3 securite = 1000 while "/" in chaine: securite -= 1 if securite < 0: raise RuntimeError, "Boucle probablement infinie." i = chaine.find("/") # analyse des caractres prcdents, pour localiser le numrateur k = i parentheses = 0 # indices correspondants au dbut et la fin du numrateur debut_numerateur = fin_numerateur = None # exposant ventuel du numrateur puissance = '' while k > 0: k -= 1 if chaine[k].isalnum(): if fin_numerateur is None: fin_numerateur = k elif chaine[k] == ")": if parentheses == 0: fin_numerateur = k parentheses += 1 elif chaine[k] == "(": parentheses -= 1 if parentheses == 0: debut_numerateur = k break elif chaine[k] == '^': puissance = chaine[k:fin_numerateur + 1] elif parentheses == 0 and fin_numerateur is not None: debut_numerateur = k+1 break if debut_numerateur is None: debut_numerateur = 0 assert fin_numerateur is not None, "Numerateur introuvable" # On dtecte la fonction qui prcde ventuellement la parenthse # par exemple, sqrt(2)/2 -> le numrateur est 'sqrt(2)', et pas '(2)' # TODO: rcrire tout a plus proprement for func in fonctions: n = len(func) if chaine[:debut_numerateur].endswith(func) and\ (debut_numerateur == n or not chaine[debut_numerateur-n-1].isalpha()): debut_numerateur -= n numerateur = chaine[debut_numerateur : fin_numerateur+1].strip() if numerateur[0] == "(": if puissance: numerateur += puissance else: numerateur = numerateur[1:-1] # analyse des caractres suivants, pour localiser le dnominateur k = i parentheses = 0 # indices correspondants au dbut et la fin du numrateur debut_denominateur = fin_denominateur = None # exposant ventuel du dnominateur puissance = '' while k < len(chaine) - 1: k += 1 if chaine[k].isalnum(): if debut_denominateur is None: debut_denominateur = k elif chaine[k] == "(": if parentheses == 0: debut_denominateur = k parentheses += 1 elif chaine[k] == ")": parentheses -= 1 if parentheses == 0: fin_denominateur = k break elif parentheses == 0 and debut_denominateur is not None: fin_denominateur = k-1 break if fin_denominateur is None: fin_denominateur = len(chaine) - 1 assert debut_denominateur is not None, "Denominateur introuvable" denominateur = chaine[i + 1:fin_denominateur + 1].strip() if chaine[fin_denominateur+1:].startswith('^'): m = re.match('[(][A-Za-z0-9.]+[)]|[A-Za-z0-9.]+', chaine[fin_denominateur + 2:]) if m is not None: puissance = '^' + m.group() fin_denominateur += m.end() + 1 if denominateur[0] == "(": if puissance: denominateur += puissance else: denominateur = denominateur[1:-1] if len(chaine) >= 10000: if param.verbose: print "Code en cours :" print chaine raise RuntimeError, 'Memory Leak probable.' # remplacement de la fraction python par une fraction LaTeX chaine = chaine[:debut_numerateur] + r"\frac{" + numerateur + "}{" + denominateur + "}" + chaine[fin_denominateur+1:] assert securite >= 0 # Autres remplacements : chaine = re.sub(r"(? 2^{27} #chaine = re.sub(r'\^\([-0-9.]+\)', lambda m: '^{' + m.group()[2:-1] + '}', chaine) chaine = re.sub(r'\^-?[0-9.]+', lambda m: '^{' + m.group()[1:] + '}', chaine) ## if param.debug: ## print 'Temps pour conversion LaTeX:', time.time()- time0 return "$" + chaine + "$" def test_parentheses(chaine): u"""Retourne True si le parenthsage est correct, False sinon. Note: il s'agit d'un test rapide (on ne tient pas compte des guillemets, crochets...)""" count = 0 n = 0 k = 0 while count >= 0: k += 1 i = chaine.find('(', n) j = chaine.find(')', n) if i == -1: n = j elif j == -1: n = i else: n = min(i, j) if n == -1: break parenthese = chaine[n] assert parenthese in '()' count += (1 if parenthese == '(' else -1) n += 1 return not count def _extraire_facteurs(chaine): # 1. On enlve les parenthses superflues while chaine[0] == '(' and chaine[-1] == ')' and test_parentheses(chaine[1:-1]): chaine = chaine[1:-1] # Premier test rapide if '*' not in chaine and '/' not in chaine: return [chaine] # 2. On regarde s'il s'agit d'une somme/diffrence # On commence par enlever les '+' ou '-' en dbut de chane (-2x n'est pas une diffrence) _chaine = chaine.lstrip('+-') for symbole in ('+', '-'): if len(advanced_split(_chaine, symbole)) > 1: return [chaine] # c'est une somme/diffrence, pas de dcomposition en facteurs # 3. On dcoupe autour des '*' (en tenant compte des parenthses) facteurs = advanced_split(chaine, '*') if len(facteurs) == 1: facteurs = advanced_split(chaine, '/') if len(facteurs) == 1: # Ce n'est ni un produit ni un quotient return facteurs # 4. On redcoupe rcursivement chacun des facteurs decomposition = [] for facteur in facteurs: decomposition.extend(extraire_facteurs(facteur)) return decomposition def extraire_facteurs(chaine): ## chaine = _ajouter_mult_manquants(chaine, fonctions = maths.__dict__) chaine = traduire_formule(chaine, fonctions = maths.__dict__).replace('**', '^') # Pour faciliter la dcomposition en produit, # il est important que la puissance ne soit pas note '**'. return [simplifier_ecriture(facteur) for facteur in _extraire_facteurs(chaine)] wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tabval.py0000644000175000017500000001264012014170666025020 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # tabvar # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from numpy import arange from .tablatexlib import traduire_latex, maths from ...pylib import print_error def tabval(chaine = "", icomma = True): u"""Syntaxe: fonction: [precision d'arrondi]: 1ere valeur,2e valeur..valeur finale Exemples: \\sin(x): -5,-4.9..5 h(x)=sin(x)+1: [0.01]: -5,-4.5..0 ; 0,1..3 Utilisez ; pour sparer plusieurs bloc de valeurs, et // pour indiquer un retour la ligne (si le tableau est trop long). """ # f(x)=x+4:-5,-4..0 ; 2 ; 5,7..10// 12,14..20 # f(x)=x+4:-5..-4..0; 2; 5..7..10// 12,14..20 # f(x)=x+4:-5 -4+1..0 chaine_originale = chaine = chaine.strip() chaine = chaine.replace(r'\\', '\n').replace('//', '\n') sequence = chaine.split(":", 2) legende = [txt.strip() for txt in sequence[0].split("=", 1)] if len(legende) == 2: fonction, expression = legende # On devine la variable (en principe, elle est entre parenthses) deb = fonction.find("(") fin = fonction.find(")") if deb == -1: variable = "x" else: variable = fonction[deb+1:fin].strip() else: fonction = expression = legende[0] # Reste deviner la variable. # On cherche les lettres isoles (sauf 'e', qui reprsente exp(1)) m = re.search('(? 1: raise ValueError, "Il y a plusieurs variables dans l'expression !" elif not variables: raise ValueError, "Il n'y a pas de variable dans l'expression !" var = variables.pop() # Rcupration de l'ensemble de dfinition ens_def *= ensemble_definition(expr, var) valeurs_interdites = [] xmin = ens_def.intervalles[0].inf if not ens_def.intervalles[0].inf_inclus: valeurs_interdites.append(xmin) sup = xmin for intervalle in ens_def.intervalles: inf = intervalle.inf if sup != inf: # Il y a un 'trou' dans l'ensemble de dfinition (ex: ]-oo;0[U]2;+oo[) raise NotImplementedError #TODO: utiliser || pour noter un intervalle interdit sup = intervalle.sup if not intervalle.sup_inclus: valeurs_interdites.append(sup) xmax = sup # On tudie la drive df = expr.diff(var) ens_def_deriv = ensemble_definition(df, var) solutions = [sol for sol in resoudre(df, var) if sol.is_real] # On liste toutes les valeurs remarquables pour la fonction # NB: on les convertit toutes au format Sympy, pour qu'il n'y ait pas # par exemple deux zros "diffrents" lists (int(0), et sympy.Integer(0)) valeurs = {sympify(xmin): None, sympify(xmax): None} for sol in solutions: if xmin <= sol <= xmax: valeurs[sympify(sol)] = 0 for val in valeurs_interdites: if xmin <= val <= xmax: valeurs[sympify(val)] = nan liste_valeurs = sorted(valeurs) ## def reel(val): ## return val.is_real and -oo < val < oo # On gnre le code, valeur aprs valeur code = str(var) + ';' + legende + ':' if param.debug and param.verbose: print "liste_valeurs", liste_valeurs, valeurs, set(valeurs), [type(val) for val in valeurs] for i, valeur in enumerate(liste_valeurs): code_point = '(' + nice_str2(valeur) + ';' if valeur == xmin: lim_plus = limit(expr, var, valeur, dir = '+') if valeur != -oo and valeur in valeurs_interdites: code_point += '|' # On n'affiche les limites que si 'limites == True' if limites or (valeur != -oo and valeur not in valeurs_interdites): code_point += nice_str2(lim_plus) elif valeur == xmax: lim_moins = limit(expr, var, valeur, dir = '-') # On n'affiche les limites que si 'limites == True' if limites or (valeur != +oo and valeur not in valeurs_interdites): code_point += nice_str2(lim_moins) if valeur != +oo and valeur in valeurs_interdites: code_point += '|' else: if valeur in valeurs_interdites: # On n'affiche les limites que si 'limites == True' lim_plus = limit(expr, var, valeur, dir = '+') lim_moins = limit(expr, var, valeur, dir = '-') if limites: code_point += nice_str2(lim_moins) + '|' + nice_str2(lim_plus) else: code_point += '|' else: # On calcule simplement l'image lim_moins = lim_plus = expr.subs(var, valeur) code_point += nice_str2(lim_moins) if valeur not in (-oo, oo) and valeur not in ens_def_deriv: # La drive n'est pas dfinie en cette valeur code_point += ';|' code_point += ')' if i > 0: #print lim_precedente, lim_moins if lim_precedente < lim_moins: code += ' << ' elif lim_precedente > lim_moins: code += ' >> ' else: code += ' == ' code += code_point lim_precedente = lim_plus if param.debug and param.verbose: print 'Code TABVar:', code return tabvar(code, derivee=derivee) + '% ' + chaine_initiale + '\n' def tabvar(chaine="", derivee=True, limites=True, decimales=3): u"""Indiquer les variations de la fonction. Exemples : f: (-oo;3) << (1;2;0) << (3;+oo|-oo) << (5;2) >> (+oo;-oo) \\sqrt{x};(\\sqrt{x})';x: 0;0;| << +oo;+oo""" chaine_originale = chaine = chaine.strip() #ligne_variable = ligne_derivee = ligne_fonction = "" if not ':' in chaine and not '>>' in chaine and not '==' in chaine and not '<<' in chaine: return _auto_tabvar(chaine, derivee=derivee, limites=limites, decimales=decimales) chaine = chaine.replace("-oo", "-\\infty").replace("+oo", "+\\infty") liste = chaine.split(":", 1) if len(liste) == 1 or len(liste[0].strip()) == 0: ligne_variable = "x" ligne_derivee = "f'(x)" ligne_fonction = "f" else: legende, chaine = liste legende = legende.split(";") if len(legende) > 1: ligne_variable = legende[0].strip() ligne_fonction = legende[1].strip() if len(legende) > 2: ligne_derivee = legende[2].strip() else: deb = ligne_fonction.find("(") if deb == -1: ligne_derivee = ligne_fonction[:].strip() + "'(" + ligne_variable + ")" else: ligne_derivee = ligne_fonction[:deb].strip() + "'(" + ligne_variable + ")" else: # un seul argument pour la legende: c'est alors la fonction ligne_fonction = legende[0].strip() deb = ligne_fonction.find("(") fin = ligne_fonction.find(")") if deb == -1: ligne_variable = "x" ligne_derivee = ligne_fonction + "'(x)" else: ligne_variable = ligne_fonction[deb+1:fin].strip() ligne_derivee = ligne_fonction[:deb].strip() + "'(" + ligne_variable + ")" # on dcoupe la chane, en une suite contenant soit les valeurs de x, f(x) (et ventuellement f'(x)), # soit le sens de variation entre ces valeurs. # ex: "-oo;3 << 1;2 >> 3;-oo|+oo << 5;2 << +oo;+oo" devient # ["-oo;3", "<<", "1;2", ">>", "3;-oo|+oo", "<<", "5;2", "<<", "+oo;+oo"] sequence = re.split(r"(>>|<<|==|\|\|)", chaine.strip()) if not sequence[0]: # en l'absence d'indication, x varie de -oo... sequence[0] = "-\\infty;" if not sequence[-1]: # ... +oo sequence[-1] = "+\\infty;" def formater(chaine): chaine = chaine.strip() if chaine not in ("<<", ">>", "==", '||'): # Les valeurs sont ventuellement encadres par des parenthses (facultatives) pour plus de lisibilit. # On enlve ici les parenthses. ex: (-2;0) devient -2;0 if chaine[0] == '(' and chaine[-1] == ')' and test_parentheses(chaine[1:-1]): chaine = chaine[1:-1] if ";" not in chaine: chaine += ";" # il faut au minimum un ";" pour indiquer l'absence de valeur return chaine sequence = [formater(elt) for elt in sequence] # On effectue un premier balayage uniquement pour detecter les niveaux. niveaux = [] # chaque element de la liste correspond a une portion de tableau de variation comprise entre deux valeurs interdites. # A chacune de ces portions va correspondre un doublon (niveau minimal atteint, niveau maximal atteint) qu'on ajoute a la liste. niveau = niveau_minimum = niveau_maximum = 0 for elt in sequence: if (";" in elt and "|" in elt.split(";")[1]) \ or elt == '||': # presence d'une valeur interdite niveaux.append((niveau_minimum, niveau_maximum)) niveau = niveau_minimum = niveau_maximum = 0 else: if elt == "<<": niveau += 1 elif elt == ">>": niveau -= 1 if niveau < niveau_minimum: niveau_minimum = niveau if niveau > niveau_maximum: niveau_maximum = niveau niveaux.append((niveau_minimum, niveau_maximum)) ecart_maximal = max(val[1] - val[0] for val in niveaux) # L'environnement tabvar ne permet pas de positionner un texte entre deux lignes. # Si, dans la 3e partie du tableau (la fonction elle-mme), # le nombre de lignes (c--d. ecart_maximal+1) est impair, # on dcale le texte lgrement vers le haut pour le centrer verticalement (via raisebox). if ecart_maximal%2: ligne_fonction = "\\niveau{" + str((ecart_maximal+2)//2) +"}{" + str(ecart_maximal+1) + "}\\raisebox{0.5em}{$" + ligne_fonction + "$}" else: ligne_fonction = "\\niveau{" + str((ecart_maximal+2)//2) +"}{" + str(ecart_maximal+1) + "}" + ligne_fonction #print "niveaux: ", niveaux colonnes = 'C|' # ex: 'CCCCC' pour 5 colonnes centres portion = 0 # indique la derniere portion traitee (les portions sont delimitees par les bornes de l'ensemble de definition et les valeurs interdites) debut = True def en_latex(chaine): return convertir_en_latex(chaine)[1:-1] # Deuxieme et dernier balayage : # on parcourt maintenant la liste pour construire colonne par colonne le tableau de variations. for i in xrange(len(sequence)): # on justifie apres chaque etape, ce qui rend une eventuelle relecture du tableau plus agreable n = max(len(ligne_variable), len(ligne_derivee), len(ligne_fonction)) ligne_variable = ligne_variable.ljust(n) ligne_derivee = ligne_derivee.ljust(n) ligne_fonction = ligne_fonction.ljust(n) ligne_variable += "&" if debut: debut = False ligne_fonction += "&\\niveau{" + str(1 - niveaux[portion][0]) +"}{" + str(ecart_maximal + 1) + "}" else: ligne_fonction += "&" ligne_derivee += "&" elt = sequence[i] if elt in (">>", "<<", "==", "||"): # il s'agit d'une variation colonnes += ('N' if elt == '||' else 'C') #ligne_variable += " " if elt == "<<": ligne_derivee += "+" ligne_fonction += r"\croit" elif elt == ">>": ligne_derivee += "-" ligne_fonction += r"\decroit" elif elt == "==": ligne_derivee += "0" ligne_fonction += r"\constante" else: ligne_variable += r"\hspace*{15mm}" else: # il s'agit des valeurs entre deux variations valeurs = elt.split(";") # valeurs = x, f(x), et eventuellement f'(x). # Si f(x) n'est pas defini (valeur interdite), il y a (en general) une valeur-double pour f(x) : # les limites a gauche et a droite, separees par un "|". (Idem pour f'(x)). largeur = max((3 if "|" in val else 1) for val in valeurs) # 3 si x est une valeur interdite pour f ou f' (ie. une valeur contient un "|"), 1 sinon. if largeur == 3: # x est une valeur interdite pour f(x) ou f'(x) ligne_variable += " &" + en_latex(valeurs[0]) + "& " vals_fonc = valeurs[1].split("|") if len(vals_fonc) == 2: # x est une valeur interdite pour f(x) portion += 1 # on change de portion ligne_fonction += en_latex(vals_fonc[0]) + "&\\dbarre&" \ + "\\niveau{" + str(1 - niveaux[portion][0]) +"}{" + str(ecart_maximal + 1) + "}" \ + en_latex(vals_fonc[1]) else: ligne_fonction += " &" + en_latex(vals_fonc[0]) + "&" if len(valeurs) < 3: # le nombre derive n'est pas specifie valeurs.append("|") # si la fonction n'est pas definie en x, sa derivee non plus vals_deriv = valeurs[2].split("|") if len(vals_deriv) == 2: ligne_derivee += en_latex(vals_deriv[0]) + "&\\dbarre&" + en_latex(vals_deriv[1]) else: ligne_derivee += " &" + en_latex(vals_deriv[0]) + "&" else: # x n'est pas une valeur interdite ligne_variable += en_latex(valeurs[0]) ligne_fonction += en_latex(valeurs[1]) if len(valeurs) < 3: # le nombre derive n'est pas specifie if 0 < i < len(sequence)-1 and sequence[i-1] != sequence[i+1]: # Changement de sens de variation en x : # la drive s'annule donc. valeurs.append("0") else: valeurs.append(" ") ligne_derivee += en_latex(valeurs[2]) colonnes += largeur*'C' code = "\\[\\begin{tabvar}{|" + colonnes + "|}\n\\hline\n" code += ligne_variable + "\\\\\n" if derivee: code += "\\hline\n" + ligne_derivee + "\\\\\n" code += "\\hline\n" + ligne_fonction + "\\\\\n" code += "\\hline\n\\end{tabvar}\\]\n% " + chaine_originale + "\n" return code wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/description.py0000644000175000017500000000070412014170666026070 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- # Informations utilise par l'installeur sous Windows. # titre : description sommaire du module # description : description dtaille # defaut : par dfaut, le module est-il install ou non ? description = { "titre": u"Tableaux LaTeX", "description": u"Assistant la cration de tableaux de variations sous LaTeX.", "groupe": u"Modules", "defaut": False, } wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tabsign.py0000644000175000017500000004133012014170666025174 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # tabvar # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from sympy import oo, nan, Symbol from .tablatexlib import convertir_en_latex, traduire_latex, test_parentheses,\ maths, extraire_facteurs, resoudre, nice_str from ...mathlib.intervalles import R, conversion_chaine_ensemble from ...mathlib.custom_functions import ensemble_definition, custom_str from ...mathlib.interprete import Interprete from ... import param def _auto_tabsign(chaine, cellspace = False): u"""Gnre le code du tableau de signe partir d'une expression variable relle. On suppose que l'expression est continue sur tout intervalle de son ensemble de dfinition. Par ailleurs, ses zros doivent tre calculables pour la librairie sympy. """ chaine_initiale = chaine # Ensemble de dfinition if ' sur ' in chaine: chaine, ens_def = chaine.split(' sur ') ens_def = conversion_chaine_ensemble(ens_def, utiliser_sympy = True) else: ens_def = R # Lgende de la dernire ligne if '=' in chaine: legende, chaine = chaine.split('=', 1) else: legende = chaine # Dcomposition en produit facteurs = extraire_facteurs(chaine)#.replace('/', '*')) # Conversion en expression sympy interprete = Interprete() interprete.evaluer(chaine) expr = interprete.ans() # Rcupration de la variable variables = expr.atoms(Symbol) if len(variables) > 1: raise ValueError, "Il y a plusieurs variables dans l'expression !" elif not variables: raise ValueError, "Il n'y a pas de variable dans l'expression !" var = variables.pop() # Rcupration de l'ensemble de dfinition ens_def *= ensemble_definition(expr, var) if param.debug and param.verbose: print '-> Ensemble de definition:', ens_def code = str(var) # chane retourne, respectant la syntaxe de tabsign() valeurs_interdites = [] xmin = ens_def.intervalles[0].inf if not ens_def.intervalles[0].inf_inclus: valeurs_interdites.append(xmin) sup = xmin for intervalle in ens_def.intervalles: inf = intervalle.inf if sup != inf: # Il y a un 'trou' dans l'ensemble de dfinition (ex: ]-oo;0[U]2;+oo[) raise NotImplementedError sup = intervalle.sup if not intervalle.sup_inclus: valeurs_interdites.append(sup) code += ': ' + ('' if (intervalle.inf_inclus or inf == -oo) else '!') + nice_str(inf) + ';' \ + ('' if (intervalle.sup_inclus or sup == oo) else '!') + nice_str(sup) xmax = sup # tude du signe de chaque facteur for facteur in facteurs: interprete.evaluer(facteur) f_expr = interprete.ans() f_ens_def = ensemble_definition(f_expr, var) valeurs = {xmin: None, xmax: None} solutions = [sol for sol in resoudre(f_expr, var) if sol.is_real and xmin <= sol <= xmax] for sol in solutions: valeurs[sol] = 0 for val in valeurs_interdites: if val not in f_ens_def and val not in (-oo, oo): valeurs[val] = nan liste_valeurs = sorted(valeurs) # On gnre le code de la ligne code += '// ' #print solutions, valeurs_interdites if solutions and all(sol in valeurs_interdites for sol in solutions): code += '!' code += facteur + ':' for i, valeur in enumerate(liste_valeurs): if valeurs[valeur] == 0: code += nice_str(valeur) elif valeurs[valeur] == nan: code += '!' + nice_str(valeur) if i != len(liste_valeurs) - 1: valeur_suivante = liste_valeurs[i + 1] if valeur == -oo: if valeur_suivante == +oo: val_intermediaire = 0 else: val_intermediaire = valeur_suivante - 1 elif valeur_suivante == +oo: val_intermediaire = valeur + 1 else: val_intermediaire = (valeur + valeur_suivante)/2 # On suppose la fonction continue sur tout intervalle de son ensemble de dfinition. if f_expr.subs(var, val_intermediaire) > 0: code += ' ++ ' else: code += ' -- ' code += '// ' + legende if param.debug and param.verbose: print 'Code TABSign:', code return tabsign(code, cellspace = cellspace) + '% ' + chaine_initiale + '\n' def tabsign(chaine = '', cellspace = False): u"""Indiquer ligne par ligne le signe de chaque facteur. La dernire ligne (produit ou quotient) est gnre automatiquement. Exemples: x:-pi;pi // sin(x):-pi -- 0 ++ pi // !cos(x):-- -pi/2 ++ pi/2 -- // tan(x) x:-2;2 // x+1:-- -1 ++ // !x-1:-- 1 ++ Le point d'exclamation avant une expression signifie qu'elle correspond un dnominateur.""" chaine_originale = chaine = chaine.strip() chaine = chaine.replace('//', '\n').replace(r'\\', '\n').replace("-oo", r"-\infty").replace("+oo", r"+\infty") lignes = [ligne.strip() for ligne in chaine.split('\n') if ligne.strip()] if len(lignes) == 1: if ':' in lignes: lignes = [''] + lignes else: return _auto_tabsign(lignes[0], cellspace = cellspace) # 'resultat' est la dernire ligne, sauf si elle contient ':' # (Dans ce cas, 'resultat' sera gnr automatiquement plus tard, partir des autres lignes). resultat = (lignes.pop() if ':' not in lignes[-1] else '') ligne_variable = lignes.pop(0) #print lignes if ":" in ligne_variable: variable, donnees_variable = ligne_variable.split(":", 1) elif ";" in ligne_variable: variable = "" donnees_variable = ligne_variable else: variable = ligne_variable donnees_variable = "" variable = variable.strip() if ';' not in donnees_variable: donnees_variable += ';' def _inter2tuple(s): inf, sup = s.split(';') inf = inf.strip() sup = sup.strip() if not inf: inf = '-oo' if not sup: sup = '+oo' return (inf, sup) intervalles = [_inter2tuple(inter) for inter in donnees_variable.split(':')] # Sparation de la lgende et des autres donnes pour chaque ligne expressions = [] donnees_expressions = [] for ligne in lignes: expression, signe_expression = ligne.split(":") expressions.append(expression.strip()) donnees_expressions.append(signe_expression.strip()) # Au besoin, on gnre la lgende de la dernire ligne (c.-d. le rsultat - produit ou quotient) if resultat == "": numerateur = [] denominateur = [] for expression in expressions: if expression[0] == "!": liste = denominateur expression = expression[1:] else: liste = numerateur if "+" in expression or "-" in expression[1:]: # il s'agit d'une somme ou d'une difference, il faut donc des parentheses liste.append("(" + expression + ")") else: liste.append(expression) if denominateur: if len(denominateur) == 1 and denominateur[0][0] == "(" and denominateur[0][-1] == ")": denominateur[0] = denominateur[0][1:-1] if len(numerateur) == 1 and numerateur[0][0] == "(" and numerateur[0][-1] == ")": numerateur[0] = numerateur[0][1:-1] if not numerateur: numerateur = ["1"] resultat = "\\frac{%s}{%s}" %("".join(numerateur), "".join(denominateur)) else: resultat = "".join(numerateur) resultat_genere_automatiquement = True else: resultat_genere_automatiquement = False # Cas particulier : 'produit' de 1 seul lment -> inutile d'afficher une ligne rsultat if len(expressions) == 1 and not expressions[0].startswith("!"): afficher_resultat = False else: afficher_resultat = True # Au besoin, on gnre la lgende de la premire ligne (c.-d. la variable) if not variable: # On cherche les lettres isoles (sauf 'e', qui reprsente exp(1)) m = re.search('(? 0: return '+' if not co%2 else '' elif val < 0: return '$-$' if not co%2 else '' else: return '0' dict_signes = {'++': 1, '--': -1, '00': 0} # On gnre maintenant le code LaTeX correspondant au tableau proprement dit. # On procde colonne par colonne. nbr_colonnes = 2*len(valeurs) for co in xrange(1, nbr_colonnes): colonne = ['' for i in xrange(len(donnees_expressions) + 2)] signe = 1 # (1 pour positif, -1 pour ngatif, nan pour valeur interdite -> cf. latex_signe()) # Premire ligne (valeurs de la variable) if co%2: # Il s'agit d'une valeur (et non d'un signe + ou -) valeur_num = valeurs[(co - 1)//2] valeur = correspondances[valeur_num] # On applique un formatage LaTeX certaines expressions : colonne[0] = convertir_en_latex(valeur) if valeur_num in valeurs_interdites: signe = nan # Autres lignes for li, donnees in enumerate(donnees_expressions): # quel endroit de la ligne sommes-nous ? (3 cas) valeurs_precedentes = [k for k, val in enumerate(donnees) if not isinstance(val, basestring) and val[0]<=valeur_num] if valeurs_precedentes: # position de la dernire valeur de la ligne position = valeurs_precedentes[-1] if co%2: # 1er cas: on est au niveau d'une valeur if donnees[position][0] == valeur_num: if donnees[position][1] == '|': colonne[li + 1] = '||' signe = nan else: colonne[li + 1] = donnees[position][1] signe *= 0 if li + 1 in indices_denominateurs: signe = nan else: # 2e cas: on est entre deux valeurs assert position + 1 < len(donnees), "Verifiez que les valeurs de la ligne sont bien rangees dans l'ordre croissant." signe_ = dict_signes[donnees[position + 1]] colonne[li + 1] += latex_signe(signe_, co) # le signe qui est juste apres la derniere valeur signe *= signe_ else: # 3e cas: on est en dbut de ligne if co%2 == 0: # on est entre deux valeurs signe_ = dict_signes[donnees[1]] colonne[li + 1] += latex_signe(signe_, co) signe *= signe_ # Dernire ligne : signe du produit ou du quotient colonne[-1] += latex_signe(signe, co) # On centre le texte dans la colonne pour que le code LaTeX gnr soit plus lisible. n = max(len(texte) for texte in colonne) + 2 for i, ligne in enumerate(colonne): colonne[i] = ligne.center(n) # Une fois la colonne entirement gnre, le texte de la colonne est rajout chaque ligne for num, text in enumerate(colonne): lignes[num] += '&' + text # Cas particulier : 'produit' de 1 seul lment if not afficher_resultat: lignes.pop(-1 if resultat_genere_automatiquement else -2) # NB: si le resultat n'a pas t gnr automatiquement, on garde la dernire ligne, # et on supprime l'avant dernire, pour garder le nom ventuel de la fonction # par exemple, si f(x)=x-1 # cela permet d'avoir f(x) | - 1 + dans le tableau, # au lieu de x-1 | - 1 + # Et on rassemble les lignes if cellspace: code = "\\begin{center}\n\\begin{tabular}{|Sc|" + (nbr_colonnes - 1)*"Sc" + "|}\n\\hline\n" else: code = "\\begin{center}\n\\begin{tabular}{|c|" + (nbr_colonnes - 1)*"c" + "|}\n\\hline\n" for ligne in lignes: code += ligne + "\\\\\n\\hline\n" code += "\\end{tabular}\n\\end{center}\n% " + chaine_originale + "\n" return code wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/_param_/0000755000175000017500000000000012014170666024570 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/_param_/__init__.py0000644000175000017500000000052012014170666026676 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) copie_automatique = False # copie automatique de chaque resultat dans le presse-papier mode = 0 # 0 -> Tableau de variation ; 1 -> Tableau de signe ; 2 -> Tableau de valeurs utiliser_cellspace = False derivee = True limites = True wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tests/0000755000175000017500000000000012014170666024334 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tests/tabtestlib.py0000644000175000017500000000153612014170666027050 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) def assert_tableau(func, chaine, code_latex, **options): code = func(chaine, **options) if code != code_latex: print "-------" print "ERREUR:" print "Actually result is:" print code print "While expected output was:" print code_latex for i, car in enumerate(code): if i >= len(code_latex): print 'Output too long:' print code[i:] break elif code_latex[i] != car: print 'Difference:' print 'char number:', i print 'result:', repr(code[i:i+10]) print 'expected:', repr(code_latex[i:i+10]) break print "-------" assert (code == code_latex) wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tests/test_tabvar.py0000644000175000017500000001727212014170666027235 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from wxgeometrie.modules.tablatex.tests.tabtestlib import assert_tableau from wxgeometrie.modules.tablatex.tabvar import tabvar def assert_tabvar(chaine, code_latex, **options): assert_tableau(tabvar, chaine, code_latex, **options) def test_mode_manuel(): s = "x;f(x);f'(x):0;2;|>>1;0;1<<2;3;0" tab = \ r"""\[\begin{tabvar}{|C|CCCCCCC|} \hline x & &0& & &1& &2\\ \hline f'(x) &&\dbarre& &- &1&+ &0\\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2} &2&&\decroit&0&\croit&3\\ \hline \end{tabvar}\] % x;f(x);f'(x):0;2;|>>1;0;1<<2;3;0 """ assert_tabvar(s, tab) s = "x;f(x);f'(x):(0;2;|) >> (1;0;1) << (2;3;0)" tab = \ r"""\[\begin{tabvar}{|C|CCCCCCC|} \hline x & &0& & &1& &2\\ \hline f'(x) &&\dbarre& &- &1&+ &0\\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2} &2&&\decroit&0&\croit&3\\ \hline \end{tabvar}\] % x;f(x);f'(x):(0;2;|) >> (1;0;1) << (2;3;0) """ assert_tabvar(s, tab) s = "x;f(x):-oo;+oo>>0;-oo|+oo>>+oo;-oo" tab = \ r"""\[\begin{tabvar}{|C|CCCCCCC|} \hline x &-\infty & & &0& & &+\infty\\ \hline f'(x) & &- &&\dbarre& &- & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2}+\infty&\decroit&-\infty&\dbarre&\niveau{2}{2}+\infty&\decroit&-\infty\\ \hline \end{tabvar}\] % x;f(x):-oo;+oo>>0;-oo|+oo>>+oo;-oo """ assert_tabvar(s, tab) s = "x;f(x): (-oo;+oo)>>(0;-oo|+oo)>>(+oo;-oo)" tab = \ r"""\[\begin{tabvar}{|C|CCCCCCC|} \hline x &-\infty & & &0& & &+\infty\\ \hline f'(x) & &- &&\dbarre& &- & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2}+\infty&\decroit&-\infty&\dbarre&\niveau{2}{2}+\infty&\decroit&-\infty\\ \hline \end{tabvar}\] % x;f(x): (-oo;+oo)>>(0;-oo|+oo)>>(+oo;-oo) """ assert_tabvar(s, tab) s = "x;f(x);f'(x):(0;2;|) >> (1;-oo) || (2;+oo) << (+oo;3)" tab = \ r"""\[\begin{tabvar}{|C|CCCCCNCCC|} \hline x & &0& & &1 &\hspace*{15mm}&2 & &+\infty\\ \hline f'(x) &&\dbarre& &- &0 & &0 &+ & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2} &2&&\decroit&-\infty& &+\infty&\croit&3\\ \hline \end{tabvar}\] % x;f(x);f'(x):(0;2;|) >> (1;-oo) || (2;+oo) << (+oo;3) """ assert_tabvar(s, tab) def test_mode_auto(): s = 'f(x)=(x+1)/(3x-2)' tab = \ r"""\[\begin{tabvar}{|C|CCCCCCC|} \hline x &-\infty & & &\frac{2}{3}& & &+\infty\\ \hline f'(x) & &- &&\dbarre& &- & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2}\frac{1}{3}&\decroit&-\infty&\dbarre&\niveau{2}{2}+\infty&\decroit&\frac{1}{3}\\ \hline \end{tabvar}\] % x;f(x):(-oo;1/3) >> (2/3;-oo|+oo;|) >> (+oo;1/3) % f(x)=(x+1)/(3x-2) """ assert_tabvar(s, tab) s = "(x+1)(x+2)" tab = \ r"""\[\begin{tabvar}{|C|CCCCC|} \hline x &-\infty & &-\frac{3}{2}& &+\infty\\ \hline f'(x) & &- &0 &+ & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f$}&\niveau{2}{2}+\infty&\decroit&-\frac{1}{4}&\croit&+\infty\\ \hline \end{tabvar}\] % x;f:(-oo;+oo) >> (-3/2;-1/4) << (+oo;+oo) % (x+1)(x+2) """ assert_tabvar(s, tab) s = "f(x)=5*ln(x)/x+3" tab = \ r"""\[\begin{tabvar}{|C|CCCCCCC|} \hline x & &0& & &\e & &+\infty\\ \hline f'(x) &&\dbarre& &+ &0 &- & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{1}{2}&\dbarre&\niveau{1}{2}-\infty&\croit&5 \exp{-1} + 3&\decroit&3\\ \hline \end{tabvar}\] % x;f(x):(0;|-oo;|) << (e;5*exp(-1) + 3) >> (+oo;3) % f(x)=5*ln(x)/x+3 """ assert_tabvar(s, tab) def test_intervalle(): s = "x^2 sur [1;+oo[" tab = \ r"""\[\begin{tabvar}{|C|CCC|} \hline x &1 & &+\infty\\ \hline f'(x) & &+ & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f$}&\niveau{1}{2}1&\croit&+\infty\\ \hline \end{tabvar}\] % x;f:(1;1) << (+oo;+oo) % x^2 sur [1;+oo[ """ assert_tabvar(s, tab) def test_latex(): s = "f(x)=3e^{2x}-12e^{x}+5" tab = \ r"""\[\begin{tabvar}{|C|CCCCC|} \hline x &-\infty & &\ln{2}& &+\infty\\ \hline f'(x) & &- &0 &+ & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2}5&\decroit&-7 &\croit&+\infty\\ \hline \end{tabvar}\] % x;f(x):(-oo;5) >> (ln(2);-7) << (+oo;+oo) % f(x)=3e^{2x}-12e^{x}+5 """ assert_tabvar(s, tab) def test_issue_194(): s = "<< -1/ln(2) >>" tab = \ r"""\[\begin{tabvar}{|C|CCCCC|} \hline x &-\infty & &-\frac{1}{\ln{2}}& &+\infty\\ \hline f'(x) & &+ &0 &- & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f$}&\niveau{1}{2}&\croit& &\decroit&\\ \hline \end{tabvar}\] % << -1/ln(2) >> """ assert_tabvar(s, tab) def test_issue_pi(): s = 'sin(x) sur [-pi;pi]' tab = \ r'''\[\begin{tabvar}{|C|CCCCC|} \hline x &-\pi & &\frac{\pi}{2}& &\pi\\ \hline f'(x) & &+ &0 &- & \\ \hline \niveau{1}{2}\raisebox{0.5em}{$f$}&\niveau{1}{2}0&\croit&1 &\decroit&0\\ \hline \end{tabvar}\] % x;f:(-pi;0) << (pi/2;1) >> (pi;0) % sin(x) sur [-pi;pi] ''' assert_tabvar(s, tab) def test_options(): s = 'f(x)=4 x^{2} - 24 x + 11' options = {'derivee': False, 'limites': False} tab = \ r'''\[\begin{tabvar}{|C|CCCCC|} \hline x &-\infty & &3 & &+\infty\\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{2}{2}&\decroit&-25&\croit&\\ \hline \end{tabvar}\] % x;f(x):(-oo;) >> (3;-25) << (+oo;) % f(x)=4 x^{2} - 24 x + 11 ''' assert_tabvar(s, tab, **options) def test_issue_189(): # Tableaux de signes et de variation avec des décimaux s = 'f(x) = (x -4)\e^{-0,25x+5} sur [4;20]' options = {'derivee': False, 'decimales': 3} tab = \ r'''\[\begin{tabvar}{|C|CCCCC|} \hline x &4 & &8 & &20\\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{1}{2}0&\croit&80,342&\decroit&16\\ \hline \end{tabvar}\] % x;f(x) :(4;0) << (8;80,342) >> (20;16) % f(x) = (x -4)\e^{-0,25x+5} sur [4;20] ''' assert_tabvar(s, tab, **options) options = {'derivee': False, 'decimales': 2} tab = \ r'''\[\begin{tabvar}{|C|CCCCC|} \hline x &4 & &8 & &20\\ \hline \niveau{1}{2}\raisebox{0.5em}{$f(x)$}&\niveau{1}{2}0&\croit&80,34&\decroit&16\\ \hline \end{tabvar}\] % x;f(x) :(4;0) << (8;80,34) >> (20;16) % f(x) = (x -4)\e^{-0,25x+5} sur [4;20] ''' assert_tabvar(s, tab, **options)wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tests/test_tabval.py0000644000175000017500000000460012014170666027216 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from wxgeometrie.modules.tablatex.tests.tabtestlib import assert_tableau from wxgeometrie.modules.tablatex.tabval import tabval def assert_tabval(chaine, code_latex): assert_tableau(tabval, chaine, code_latex) def test_mode_manuel(): s = "f(x)=exp(x+1): 0.25: -5, -4..0 ; 0.5 ; 1, 2..6 ; 7, 10..21" tab = \ r"""\begin{center} \begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|} \hline $x$ & $-5$ & $-4$ & $-3$ & $-2$ & $-1$ & $0$ & $0,5$ & $1$ & $2$ & $3$ & $4$ & $5$ & $6$ & $7$ & $10$ & $13$ & $16$ & $19$ & $21$ \\ \hline $f(x)$ & $0$ & $0$ & $0,25$ & $0,25$ & $1$ & $2,75$ & $4,5$ & $7,5$ & $20$ & $54,5$ & $148,5$ & $403,5$ & $1096,75$ & $2981$ & $59874,25$ & $1202604,25$ & $24154952,75$ & $485165195,5$ & $3584912846,25$ \\ \hline \end{tabular} \end{center} % f(x)=exp(x+1): 0.25: -5, -4..0 ; 0.5 ; 1, 2..6 ; 7, 10..21 """ assert_tabval(s, tab) s = "f(x)=exp(x+1): [0.25]: -5,-4..0 ; 0.5 ; 1,2..6 ; 7,10..21" tab = \ r"""\begin{center} \begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|} \hline $x$ & $-5$ & $-4$ & $-3$ & $-2$ & $-1$ & $0$ & $0,5$ & $1$ & $2$ & $3$ & $4$ & $5$ & $6$ & $7$ & $10$ & $13$ & $16$ & $19$ & $21$ \\ \hline $f(x)$ & $0$ & $0$ & $0,25$ & $0,25$ & $1$ & $2,75$ & $4,5$ & $7,5$ & $20$ & $54,5$ & $148,5$ & $403,5$ & $1096,75$ & $2981$ & $59874,25$ & $1202604,25$ & $24154952,75$ & $485165195,5$ & $3584912846,25$ \\ \hline \end{tabular} \end{center} % f(x)=exp(x+1): [0.25]: -5,-4..0 ; 0.5 ; 1,2..6 ; 7,10..21 """ assert_tabval(s, tab) def test_mode_auto(): s = "(2x+3)^2:-10..0// 1..10" tab = \ r"""\begin{center} \begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|c|} \hline $x$ & $-10$ & $-9$ & $-8$ & $-7$ & $-6$ & $-5$ & $-4$ & $-3$ & $-2$ & $-1$ & $0$ \\ \hline $(2x+3)^2$ & $289$ & $225$ & $169$ & $121$ & $81$ & $49$ & $25$ & $9$ & $1$ & $1$ & $9$ \\ \hline \end{tabular} \begin{tabular}{|c|c|c|c|c|c|c|c|c|c|c|} \hline $x$ & $1$ & $2$ & $3$ & $4$ & $5$ & $6$ & $7$ & $8$ & $9$ & $10$ \\ \hline $(2x+3)^2$ & $25$ & $49$ & $81$ & $121$ & $169$ & $225$ & $289$ & $361$ & $441$ & $529$ \\ \hline \end{tabular} \end{center} % (2x+3)^2:-10..0// 1..10 """ assert_tabval(s, tab) wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tests/test_tabsign.py0000644000175000017500000002050512014170666027376 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from wxgeometrie.modules.tablatex.tests.tabtestlib import assert_tableau from wxgeometrie.modules.tablatex.tabsign import tabsign def assert_tabsign(chaine, code_latex): assert_tableau(tabsign, chaine, code_latex) def test_mode_manuel(): s = "x: -oo;+oo// 2x+1: -- -1/2 ++// 3-x: ++ 3 --// f(x)" tab = \ r"""\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $-\frac{1}{2}$ & & $3$ & & $+\infty$ \\ \hline $2x+1$ & & $-$ & 0 & + & & + & \\ \hline $3-x$ & & + & & + & 0 & $-$ & \\ \hline $f(x)$ & & $-$ & 0 & + & 0 & $-$ & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// 2x+1: -- -1/2 ++// 3-x: ++ 3 --// f(x) """ assert_tabsign(s, tab) def test_mode_auto(): s = 'g(x)=(x-7/2)(x+7/2)' tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $-\frac{7}{2}$ & & $\frac{7}{2}$ & & $+\infty$ \\ \hline $x-\frac{7}{2}$ & & $-$ & & $-$ & 0 & + & \\ \hline $x+\frac{7}{2}$ & & $-$ & 0 & + & & + & \\ \hline $g(x)$ & & + & 0 & $-$ & 0 & + & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// x-7/2: -- 7/2 ++ // x+7/2: -- -7/2 ++ // g(x) % g(x)=(x-7/2)(x+7/2) ''' assert_tabsign(s, tab) def test_polynomes(): s= 'f(x)=x^3-30x^2+112' tab = \ r"""\begin{center} \begin{tabular}{|c|ccccccccc|} \hline $x$ & $-\infty$ & & $-6 \sqrt{7} + 14$ & & $2$ & & $14 + 6 \sqrt{7}$ & & $+\infty$ \\ \hline $f(x)$ & & $-$ & 0 & + & 0 & $-$ & 0 & + & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// x^3-30 x^2+112: -- -6*sqrt(7) + 14 ++ 2 -- 14 + 6*sqrt(7) ++ // f(x) % f(x)=x^3-30x^2+112 """ assert_tabsign(s, tab) s = '- 6 x^{2} - 12 x + 4' tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $-\frac{\sqrt{15}}{3} - 1$ & & $-1 + \frac{\sqrt{15}}{3}$ & & $+\infty$ \\ \hline $- 6 x^{2} - 12 x + 4$ & & $-$ & 0 & + & 0 & $-$ & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// -6 x^(2)-12 x+4: -- -sqrt(15)/3 - 1 ++ -1 + sqrt(15)/3 -- // - 6 x^{2} - 12 x + 4 % - 6 x^{2} - 12 x + 4 ''' assert_tabsign(s, tab) def test_quotients(): s = '(3x-2)/((x-1)^2)' tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $\frac{2}{3}$ & & $1$ & & $+\infty$ \\ \hline $3 x-2$ & & $-$ & 0 & + & & + & \\ \hline $(x-1)^{2}$ & & + & & + & 0 & + & \\ \hline $\frac{3x-2}{(x-1)^{2}}$ & & $-$ & 0 & + & || & + & \\ \hline \end{tabular} \end{center} % x: -oo;!1: !1;+oo// 3 x-2: -- 2/3 ++ // !(x-1)^2: ++ 1 ++ // (3x-2)/((x-1)^2) % (3x-2)/((x-1)^2) ''' assert_tabsign(s, tab) s = '(3x-2)/(x-1)^2' tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $\frac{2}{3}$ & & $1$ & & $+\infty$ \\ \hline $3 x-2$ & & $-$ & 0 & + & & + & \\ \hline $(x-1)^{2}$ & & + & & + & 0 & + & \\ \hline $\frac{3x-2}{(x-1)^{2}}$ & & $-$ & 0 & + & || & + & \\ \hline \end{tabular} \end{center} % x: -oo;!1: !1;+oo// 3 x-2: -- 2/3 ++ // !(x-1)^2: ++ 1 ++ // (3x-2)/(x-1)^2 % (3x-2)/(x-1)^2 ''' assert_tabsign(s, tab) def test_latex(): s = '\dfrac{3x-2}{(x-1)^2}' tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $\frac{2}{3}$ & & $1$ & & $+\infty$ \\ \hline $3 x-2$ & & $-$ & 0 & + & & + & \\ \hline $(x-1)^{2}$ & & + & & + & 0 & + & \\ \hline $\dfrac{3x-2}{(x-1)^{2}}$ & & $-$ & 0 & + & || & + & \\ \hline \end{tabular} \end{center} % x: -oo;!1: !1;+oo// 3 x-2: -- 2/3 ++ // !(x-1)^2: ++ 1 ++ // \dfrac{3x-2}{(x-1)^2} % \dfrac{3x-2}{(x-1)^2} ''' assert_tabsign(s, tab) s = "g(x)=\dfrac{-x+1}{\e^{x}}" tab = \ r'''\begin{center} \begin{tabular}{|c|ccccc|} \hline $x$ & $-\infty$ & & $1$ & & $+\infty$ \\ \hline $-x+1$ & & + & 0 & $-$ & \\ \hline $\e^{x}$ & & + & & + & \\ \hline $g(x)$ & & + & 0 & $-$ & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// -x+1: ++ 1 -- // e^(x): ++ // g(x) % g(x)=\dfrac{-x+1}{\e^{x}} ''' assert_tabsign(s, tab) s= "f'(x)=1-\e^{-x+2}" tab = \ r'''\begin{center} \begin{tabular}{|c|ccccc|} \hline $x$ & $-\infty$ & & $2$ & & $+\infty$ \\ \hline $f'(x)$ & & $-$ & 0 & + & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// 1-e^(-x+2): -- 2 ++ // f'(x) % f'(x)=1-\e^{-x+2} ''' assert_tabsign(s, tab) def test_intervalle(): s = "x^2 sur [1;+oo[" tab = \ r'''\begin{center} \begin{tabular}{|c|ccc|} \hline $x$ & $1$ & & $+\infty$ \\ \hline $x^{2}$ & & + & \\ \hline \end{tabular} \end{center} % x: 1;+oo// x^2: ++ // x^2 % x^2 sur [1;+\infty[ ''' assert_tabsign(s, tab) s = "u(x)=1-x sur ]0;+oo[" tab = \ r'''\begin{center} \begin{tabular}{|c|ccccc|} \hline $x$ & $0$ & & $1$ & & $+\infty$ \\ \hline $u(x)$ & || & + & 0 & $-$ & \\ \hline \end{tabular} \end{center} % x: !0;+oo// 1-x: ++ 1 -- // u(x) % u(x)=1-x sur ]0;+\infty[ ''' assert_tabsign(s, tab) s = "u(x)=x(1-x) sur ]-1;0[U]0;4[" tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-1$ & & $0$ & & $1$ & & $4$ \\ \hline $x$ & & $-$ & 0 & + & & + & \\ \hline $1-x$ & & + & & + & 0 & $-$ & \\ \hline $u(x)$ & || & $-$ & || & + & 0 & $-$ & || \\ \hline \end{tabular} \end{center} % x: !-1;!0: !0;!4// !x: -- 0 ++ // 1-x: ++ 1 -- // u(x) % u(x)=x(1-x) sur ]-1;0[U]0;4[ ''' assert_tabsign(s, tab) s = "u(x)=(1+x)(1-x)/x sur ]-3;2[U]2;4]" tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccccccc|} \hline $x$ & $-3$ & & $-1$ & & $0$ & & $1$ & & $2$ & & $4$ \\ \hline $1+x$ & & $-$ & 0 & + & & + & & + & & + & \\ \hline $1-x$ & & + & & + & & + & 0 & $-$ & & $-$ & \\ \hline $x$ & & $-$ & & $-$ & 0 & + & & + & & + & \\ \hline $u(x)$ & || & + & 0 & $-$ & || & + & 0 & $-$ & || & $-$ & \\ \hline \end{tabular} \end{center} % x: !-3;!0: !0;!2: !2;4// 1+x: -- -1 ++ // 1-x: ++ 1 -- // !x: -- 0 ++ // u(x) % u(x)=(1+x)(1-x)/x sur ]-3;2[U]2;4] ''' assert_tabsign(s, tab) def test_issue_173(): s = "(1 - x)\e^{ 2x}" tab = \ r'''\begin{center} \begin{tabular}{|c|ccccc|} \hline $x$ & $-\infty$ & & $1$ & & $+\infty$ \\ \hline $1-x$ & & + & 0 & $-$ & \\ \hline $\e^{2 x}$ & & + & & + & \\ \hline $(1 - x)\e^{ 2x}$ & & + & 0 & $-$ & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// 1-x: ++ 1 -- // e^(2 x): ++ // (1 - x)\e^{ 2x} % (1 - x)\e^{ 2x} ''' assert_tabsign(s, tab) def test_issue_200(): s = 'f(x)=x^2-3' tab = \ r'''\begin{center} \begin{tabular}{|c|ccccccc|} \hline $x$ & $-\infty$ & & $-\sqrt{3}$ & & $\sqrt{3}$ & & $+\infty$ \\ \hline $f(x)$ & & + & 0 & $-$ & 0 & + & \\ \hline \end{tabular} \end{center} % x: -oo;+oo// x^2-3: ++ -sqrt(3) -- sqrt(3) ++ // f(x) % f(x)=x^2-3 ''' assert_tabsign(s, tab) def test_issue_189(): # Tableaux de signes et de variation avec des décimaux s = '2-0.25x' tab = \ r'''\begin{center} \begin{tabular}{|Sc|ScScScScSc|} \hline $x$ & $-\infty$ & & $8$ & & $\oo$ \\ \hline $2-0.25x$ & & + & 0 & $-$ & \\ \hline \end{tabular} \end{center} % x: -oo;oo// 2-0.25 x: ++ 8 -- // 2-0.25x % 2-0.25x '''wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/tests/__init__.py0000644000175000017500000000000212014170666026435 0ustar georgeskgeorgesk wxgeometrie-0.133.2.orig/wxgeometrie/modules/tablatex/__init__.py0000644000175000017500000002321112014170666025302 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Calculatrice # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from ...GUI.ligne_commande import LigneCommande from ...GUI import MenuBar, Panel_simple from ... import param from ...pylib import warning from ...pylib.erreurs import message from .tabsign import tabsign from .tabval import tabval from .tabvar import tabvar class TabLaTeXMenuBar(MenuBar): def __init__(self, panel): MenuBar.__init__(self, panel) self.ajouter(u"Fichier", ["quitter"]) self.ajouter(u"Affichage", ["onglet"]) self.ajouter(u"Outils", [u"Mmoriser le rsultat", u"Copie le code LaTeX gnr dans le presse-papier, afin de pouvoir l'utiliser ailleurs.", "Ctrl+M", self.panel.vers_presse_papier], None, [u"options"]) self.ajouter(u"avance2") self.ajouter("?") class TabLaTeX(Panel_simple): __titre__ = u"Tableaux LaTeX" # Donner un titre a chaque module def __init__(self, *args, **kw): Panel_simple.__init__(self, *args, **kw) self.sizer = wx.BoxSizer(wx.VERTICAL) ## self.entrees = wx.BoxSizer(wx.HORIZONTAL) ## self.entree = wx.TextCtrl(self, size = (500, -1), style=wx.TE_PROCESS_ENTER) ## self.entrees.Add(self.entree, 1, wx.ALL|wx.GROW, 5) ## self.valider = wx.Button(self, wx.ID_OK) ## self.entrees.Add(self.valider, 0, wx.ALL, 5) ## ## self.sizer.Add(self.entrees, 0, wx.ALL, 5) self.entree = LigneCommande(self, longueur = 500, action = self.generer_code) self.sizer.Add(self.entree, 0, wx.ALL, 5) self.sizer_type = wx.BoxSizer(wx.HORIZONTAL) self.type_tableau = wx.Choice(self, choices = (u"Tableau de variations", u"Tableau de signes", u"Tableau de valeurs")) self.type_tableau.SetSelection(self._param_.mode) self.sizer_type.Add(wx.StaticText(self, label = u"Type de tableau gnrer :"), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.sizer_type.Add(self.type_tableau, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.utiliser_cellspace = wx.CheckBox(self, label = u"Utiliser le paquetage cellspace.") self.utiliser_cellspace.SetValue(self._param_.utiliser_cellspace) self.utiliser_cellspace.SetToolTipString(u"Le paquetage cellspace vite que certains objets (comme les fractions) touchent les bordures du tableaux.") self.sizer_type.AddSpacer((10,0)) self.sizer_type.Add(self.utiliser_cellspace, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.derivee = wx.CheckBox(self, label = u"Drive.") self.derivee.SetValue(self._param_.derivee) self.derivee.SetToolTipString(u"Afficher une ligne indiquant le signe de la drive.") self.sizer_type.AddSpacer((10,0)) self.sizer_type.Add(self.derivee, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.limites = wx.CheckBox(self, label = u"Limites.") self.limites.SetValue(self._param_.limites) self.limites.SetToolTipString(u"Afficher les limites dans le tableau de variations.") self.sizer_type.AddSpacer((10,0)) self.sizer_type.Add(self.limites, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.sizer.Add(self.sizer_type, 0, wx.ALL, 5) box = wx.StaticBox(self, -1, u"Code LaTeX permettant de de gnrer le tableau") self.bsizer = wx.StaticBoxSizer(box, wx.VERTICAL) #~ self.sizer_code = wx.BoxSizer(wx.HORIZONTAL) #~ self.sizer_code.Add(wx.StaticText(self, label = u"Code LaTeX permettant de de gnrer le tableau."), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) #~ self.copier_code = wx.Button(self, label = u"Copier dans le presse-papier") #~ self.sizer_code.Add(self.copier_code, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) #~ self.bsizer.Add(self.sizer_code, 0, wx.ALL, 5) self.code_tableau = wx.TextCtrl(self, size = (700, 200), style = wx.TE_MULTILINE | wx.TE_RICH) self.bsizer.Add(self.code_tableau, 0, wx.ALL, 5) self.copier_code = wx.Button(self, label = u"Copier dans le presse-papier") self.bsizer.Add(self.copier_code, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.bsizer.Add(wx.StaticText(self, label = u"Pensez rajouter dans l'entte de votre fichier LaTeX la ligne suivante :"), 0, wx.TOP|wx.LEFT, 5) self.sizer_entete = wx.BoxSizer(wx.HORIZONTAL) self.code_entete = wx.TextCtrl(self, size = (200, -1), value = u"\\usepackage{tabvar}", style = wx.TE_READONLY) self.sizer_entete.Add(self.code_entete, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.copier_entete = wx.Button(self, label = u"Copier cette ligne") self.sizer_entete.Add(self.copier_entete, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.bsizer.Add(self.sizer_entete, 0, wx.ALL, 5) self.sizer.Add(self.bsizer, 0, wx.ALL, 5) self.cb = wx.CheckBox(self, label = u"Copier automatiquement le code LaTeX dans le presse-papier.") self.cb.SetValue(self._param_.copie_automatique) self.sizer.Add(self.cb, 0, wx.ALL, 5) self.SetSizer(self.sizer) self.Fit() ## self.entree.Bind(wx.EVT_KEY_UP, self.EvtChar) ## self.valider.Bind(wx.EVT_BUTTON, self.generer_code) self.type_tableau.Bind(wx.EVT_CHOICE, self.EvtChoix) self.EvtChoix() def copier_code(event = None): self.vers_presse_papier(texte = self.code_tableau.GetValue()) self.copier_code.Bind(wx.EVT_BUTTON, copier_code) def copier_entete(event = None): self.vers_presse_papier(texte = self.code_entete.GetValue()) self.copier_entete.Bind(wx.EVT_BUTTON, copier_entete) def regler_mode_copie(event = None): self._param_.copie_automatique = self.cb.GetValue() self.cb.Bind(wx.EVT_CHECKBOX, regler_mode_copie) def regler_cellspace(event = None): self._param_.utiliser_cellspace = self.utiliser_cellspace.GetValue() if self._param_.utiliser_cellspace: self.code_entete.SetValue(u"\\usepackage{cellspace}") else: self.code_entete.SetValue(u"") self.utiliser_cellspace.Bind(wx.EVT_CHECKBOX, regler_cellspace) def regler_derivee(event = None): self._param_.derivee = self.derivee.GetValue() self.derivee.Bind(wx.EVT_CHECKBOX, regler_derivee) def regler_limites(event = None): self._param_.limites = self.limites.GetValue() self.limites.Bind(wx.EVT_CHECKBOX, regler_limites) def activer(self): # Actions effectuer lorsque l'onglet devient actif self.entree.SetFocus() def vers_presse_papier(self, event = None, texte = ""): Panel_simple.vers_presse_papier(texte) def generer_code(self, commande, **kw): self.modifie = True try: if self._param_.mode == 0: code_latex = tabvar(commande, derivee=self._param_.derivee, limites=self._param_.limites) elif self._param_.mode == 1: code_latex = tabsign(commande, cellspace=self._param_.utiliser_cellspace) elif self._param_.mode == 2: code_latex = tabval(commande) else: warning("Type de tableau non reconnu.") self.code_tableau.SetValue(code_latex) if self._param_.copie_automatique: self.vers_presse_papier(texte = code_latex) self.entree.SetFocus() self.message(u"Le code LaTeX a bien t gnr.") except BaseException, erreur: self.message(u"Impossible de gnrer le code LaTeX. " + message(erreur)) self.entree.SetFocus() if param.debug: raise def EvtChoix(self, event = None): self._param_.mode = self.type_tableau.GetSelection() if self._param_.mode == 0: self.code_entete.SetValue(u"\\usepackage{tabvar}") self.entree.SetToolTipString(tabvar.__doc__) self.utiliser_cellspace.Disable() self.derivee.Enable() self.limites.Enable() elif self._param_.mode == 1: self.utiliser_cellspace.Enable() self.derivee.Disable() self.limites.Disable() self.entree.SetToolTipString(tabsign.__doc__) if self._param_.utiliser_cellspace: self.code_entete.SetValue(u"\\usepackage{cellspace}") else: self.code_entete.SetValue(u"") elif self._param_.mode == 2: self.utiliser_cellspace.Disable() self.derivee.Disable() self.limites.Disable() self.entree.SetToolTipString(tabval.__doc__) self.code_entete.SetValue(u"") wxgeometrie-0.133.2.orig/wxgeometrie/modules/__init__.py0000644000175000017500000001177512014170666023512 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ########################################## # Modules ########################################## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os from ..GUI.menu import MenuBar from ..GUI.panel import Panel_simple from ..API.parametres import actualiser_module from .. import param from ..pylib import print_error, str2, path2 def importer_module(nom_module, forcer = False): u"Retourne le module si l'import a russi, None sinon." if param.modules_actifs[nom_module] or forcer: if param.verbose: print("Import du module '%s'..." %nom_module) try: wxgeometrie = __import__('wxgeometrie.modules.' + nom_module, level=2) module = getattr(wxgeometrie.modules, nom_module) if hasattr(module, '_menu_'): # Module dj import -> rien faire. return module module._nom_ = module.__name__.split('.')[-1] menus = [objet for objet in module.__dict__.itervalues() if isinstance(objet, type) and issubclass(objet, MenuBar) and objet.__name__ != "MenuBar"] if len(menus) > 1 and param.debug: print menus raise IndexError, str2(u"Plusieurs classes hritent de MenuBar dans le module %s." %nom_module) if len(menus) == 0 and param.debug: raise IndexError, str2(u"Aucune classe n'hrite de MenuBar dans le module %s." %nom_module) module._menu_ = menus[0] panels = [objet for objet in module.__dict__.itervalues() if isinstance(objet, type) and issubclass(objet, Panel_simple) and objet.__name__ not in ("Panel_simple", "Panel_API_graphique")] if len(panels) > 1 and param.debug: print panels raise IndexError, str2(u"Plusieurs classes hritent de Panel_simple dans le module %s." %nom_module) if len(panels) == 0 and param.debug: raise IndexError, str2(u"Aucune classe n'hrite de Panel_simple dans le module %s." %nom_module) panel = module._panel_ = panels[0] try: param_pth = 'wxgeometrie.modules.%s._param_' %nom_module wxgeometrie = __import__(param_pth, level=2) panel._param_ = eval(param_pth) path = path2(param.emplacements['preferences'] + "/" + nom_module + "/parametres.xml") if param.sauver_preferences and param.charger_preferences and os.path.exists(path): try: a_verifier = dict((dicname, getattr(param, dicname)) for dicname in param.a_mettre_a_jour) actualiser_module(panel._param_, path) # certains paramtres peuvent avoir besoin d'une mise jour # (en cas de changement de version de wxgomtrie par exemple) # cela concerne en particulier les dictionnaires, qui peuvent gagner de nouvelles cls. for dicname in param.a_mettre_a_jour: for key, val in a_verifier[dicname].iteritems(): if hasattr(panel._param_, dicname): # (pour l'instant) param.a_mettre_a_jour s'applique tout wxgomtrie, # mais tous les paramtres ne concernent pas tous les modules. getattr(panel._param_, dicname).setdefault(key, val) except: print_error(u"\n\nImpossible d'actualiser les prfrences du module '%s'" %nom_module) except ImportError: panel._param_ = None print_error(u"\n\nImpossible d'importer les paramtres du module '%s'" %nom_module) except: print_error(u"\n\nImpossible d'importer les paramtres du module '%s'" %nom_module) panel._param_ = None except: print_error(u"\nError: Impossible d'importer le module '%s'" %nom_module) # On dsactive les modules non chargs. param.modules_actifs[nom_module] = False else: return module wxgeometrie-0.133.2.orig/wxgeometrie/GUI/0000755000175000017500000000000012014170666020342 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/GUI/wxlib.py0000644000175000017500000001075112014170666022045 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os, math import wx, wx.lib.dialogs, wx.lib.newevent from .. import param from ..pylib import uu class PseudoEvent(object): u"Cette classe est destine maintenir en vie un vnement prim." _methodes = ("AltDown", "ControlDown", "ShiftDown", "GetPositionTuple", "RightIsDown", "RightIsUp", "LeftIsDown", "LeftIsUp", "GetWheelRotation") def __init__(self, event): self._dict = {} for methode in self._methodes: if hasattr(event, methode): self._dict[methode] = getattr(event, methode)() def __getattr__(self, name): try: return lambda:self._dict[name] except KeyError: print(u"La mthode " + name + " n'est actuellement pas dfinie pour les pseudo-vnements.") raise def Skip(self): pass TransmitEvent, EVT_TRANSMIT = wx.lib.newevent.NewEvent() #AutoSaveEvent, EVT_AUTOSAVE = wx.lib.newevent.NewEvent() class BusyCursor(object): compteur = 0 def __enter__(self): self.__class__.compteur += 1 def __exit__(self, type, value, traceback): self.__class__.compteur -= 1 if self.compteur == 0: wx.EndBusyCursor() class BusyCursor(object): def __enter__(self): wx.BeginBusyCursor() def __exit__(self, type, value, traceback): wx.EndBusyCursor() if wx.Platform == '__WXMSW__': # Le curseur disparat sinon sous Windows !! wx.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) if wx.Platform == '__WXGTK__': # Bug de wx.Miniframe lorsque Compiz est activ sous Linux. class MyMiniFrame(wx.Frame): def __init__(self, parent, titre): wx.Frame.__init__(self, parent, -1, titre, style= wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.FRAME_NO_TASKBAR) else: class MyMiniFrame(wx.MiniFrame): def __init__(self, parent, titre): wx.MiniFrame.__init__(self, parent, -1, titre, style=wx.DEFAULT_FRAME_STYLE) # La version de WxPython doit supporter l'unicode. assert (wx.PlatformInfo[2] == "unicode"), "La version de WxPython utilisee doit imperativement supporter l'unicode !" def png(nom): u"""Charge l'image .png depuis le repertoire 'images/'. L'image doit tre au format png, et son nom doit indiqu sans l'extension '.png'.""" return wx.Image(os.path.normpath(os.path.join(uu(param.EMPLACEMENT), 'images', nom + ".png")), wx.BITMAP_TYPE_PNG).ConvertToBitmap() def screen_dpi(diagonale, ratio = (16, 10), pixels = None): u"""diagonale : longueur (en inches) de la diagonale de l'cran. ratio : (4, 3) ou (16, 10) pixels : 1024x768 par exemple (la soit-disant "rsolution" de l'cran) """ if pixels is None: pixels = wx.ScreenDC().GetSizeTuple() l, h = pixels x, y = ratio d = math.hypot(x, y) k = diagonale/d x *= k y *= k return (l/x, h/y) class MyFont(wx.Font): u"""Cr une nouvelle police, hritant par dfaut ses attributs de 'widget'.""" def __init__(self, widget, size=None, family=None, style=None, weight=None, underline=None): font = widget.GetFont() if size is None: size = font.GetPointSize() if family is None: family = font.GetFamily() if style is None: style = font.GetStyle() if weight is None: weight = font.GetWeight() if underline is None: underline = font.GetUnderlined() wx.Font.__init__(self, size, family, style, weight, underline) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/aide.py0000644000175000017500000001574712014170666021634 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Aide # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx.html as html import wx from .. import param from .wxlib import png from ..pylib.infos import informations_configuration from .app import app class Help(wx.Frame): def __init__(self, parent, path): wx.Frame.__init__(self, parent, -1, size=wx.Size(700,610)) self.path = path self.SetBackgroundColour(wx.Colour(255, 225, 153)) self.html = html.HtmlWindow(self, -1, style=wx.NO_FULL_REPAINT_ON_RESIZE) self.html.SetRelatedFrame(self, u" %s ") #self.html.SetRelatedStatusBar(0) self.printer = html.HtmlEasyPrinting() self.box = wx.BoxSizer(wx.VERTICAL) subbox = wx.BoxSizer(wx.HORIZONTAL) icones = [(u"maison",u"Page d'accueil.", self.OnHome), (u"gauche", u"Page precedente.", self.OnBack), (u"droite", u"Page suivante.", self.OnForward), (u"print", u"Imprimer la page.", self.OnPrint)] for i in range(len(icones)): icone = icones[i] bmp = png(icone[0]) bouton = wx.BitmapButton(self, -1, bmp, style=wx.NO_BORDER) bouton.SetBackgroundColour(self.GetBackgroundColour()) subbox.Add(bouton, 0, wx.ALL,5) bouton.SetToolTipString(icone[1]) bouton.Bind(wx.EVT_BUTTON, icone[2]) self.box.Add(subbox, 0) self.box.Add(self.html, 1, wx.GROW) self.SetSizer(self.box) self.SetAutoLayout(True) self.OnHome(None) def OnHome(self, event): self.html.LoadPage(self.path) def OnBack(self, event): self.html.HistoryBack() def OnForward(self, event): self.html.HistoryForward() def OnPrint(self, event): self.printer.GetPrintData().SetPaperId(wx.PAPER_LETTER) self.printer.PrintFile(self.html.GetOpenedPage()) class Informations(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, -1, u"Configuration systeme") self.SetBackgroundColour(wx.WHITE) panel = wx.Panel(self, -1) panel.SetBackgroundColour(wx.WHITE) panelSizer = wx.BoxSizer(wx.VERTICAL) italic = wx.Font(panel.GetFont().GetPointSize(), panel.GetFont().GetFamily(), wx.ITALIC, wx.NORMAL) #logo = wx.StaticBitmap(panel, -1, wx.Image(name = "images"+os.sep+"logo2.png").ConvertToBitmap()) #panelSizer.Add(logo, 0, wx.ALIGN_CENTRE) textes = informations_configuration().split(u"\n") for texte in textes: t = wx.StaticText(panel, -1, texte) if texte.startswith("+ "): t.SetFont(italic) panelSizer.Add(t, 0, wx.ALIGN_LEFT) btnOK = wx.Button(panel, wx.ID_OK, u"OK") btnCopier = wx.Button(panel, -1, u"Copier") btnCopier.Bind(wx.EVT_BUTTON, self.copier) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(btnOK, 0, wx.RIGHT, 40) sizer.Add(btnCopier, 0, wx.LEFT, 40) panelSizer.Add(sizer, 0, wx.ALL | wx.ALIGN_CENTRE, 5) panel.SetAutoLayout(True) panel.SetSizer(panelSizer) panelSizer.Fit(panel) topSizer = wx.BoxSizer(wx.HORIZONTAL) topSizer.Add(panel, 0, wx.ALL, 10) self.SetAutoLayout(True) self.SetSizer(topSizer) topSizer.Fit(self) self.Centre() def copier(self, event): app.vers_presse_papier(informations_configuration()) class About(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, -1, u"A propos de WxGomtrie") self.SetBackgroundColour(wx.WHITE) panel = wx.Panel(self, -1) panel.SetBackgroundColour(wx.WHITE) panelSizer = wx.BoxSizer(wx.VERTICAL) italic = wx.Font(panel.GetFont().GetPointSize(), panel.GetFont().GetFamily(), wx.ITALIC, wx.NORMAL) bold = wx.Font(panel.GetFont().GetPointSize(), panel.GetFont().GetFamily(), wx.NORMAL, wx.BOLD) logo = wx.StaticBitmap(panel, -1, png(u"logo2")) panelSizer.Add(logo, 0, wx.ALIGN_CENTRE) date = "/".join(str(n) for n in reversed(param.date_version)) textes = [[u"WxGomtrie version %s" % param.version, bold]] textes.append([u"Version publie le " + date + "."]) textes.append([]) textes.append([u"De la gomtrie dynamique, un traceur de courbes, et bien plus..."]) textes.append([u"WxGomtrie est un logiciel libre, vous pouvez l'utiliser et le modifier comme vous le souhaitez."]) textes.append([u"Copyright 2005-" + unicode(param.date_version[0]) +" Nicolas Pourcelot (wxgeo@users.sourceforge.net)", italic]) textes.append([]) textes.append([u"WxGomtrie inclut dsormais SymPy : Python library for symbolic mathematics."]) textes.append([u"Copyright 2006-" + unicode(param.date_version[0]) + " The Sympy Team - http://www.sympy.org.", italic]) textes.append([]) textes.append([u" Sophie."]) textes.append([u"'Le rve est bien rel. Effleurant votre main, je peux toucher le ciel!' Alain Ayroles", italic]) textes.append([u"Tous mes remerciements la communaut du logiciel libre."]) textes.append([]) for texte in textes: l = len(texte) if l: txt = wx.StaticText(panel, -1, texte[0]) if l > 1: txt.SetFont(texte[1]) panelSizer.Add(txt, 0, wx.ALIGN_LEFT) else: panelSizer.Add((5, 5)) # Spacer. btnOK = wx.Button(panel, wx.ID_OK, u"OK") panelSizer.Add(btnOK, 0, wx.ALL | wx.ALIGN_CENTRE, 5) panel.SetAutoLayout(True) panel.SetSizer(panelSizer) panelSizer.Fit(panel) topSizer = wx.BoxSizer(wx.HORIZONTAL) topSizer.Add(panel, 0, wx.ALL, 10) self.SetAutoLayout(True) self.SetSizer(topSizer) topSizer.Fit(self) self.Centre() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/wxcanvas.py0000644000175000017500000010455212014170666022555 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re import wx from numpy import array from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg from ..API.canvas import Canvas from .proprietes_objets import Proprietes from .wxlib import PseudoEvent, BusyCursor from .. import param from ..pylib import print_error, debug from ..geolib.textes import Texte, Texte_generique from ..geolib.objet import Objet from ..geolib.points import Point_generique from ..geolib.constantes import NOM, FORMULE, TEXTE, RIEN class MiniEditeur: def __init__(self, parent): self.parent = parent self.objet = None def init(self, objet, mode = 0): u"""Edition d'un nouvel objet. mode = 0: dition du nom de l'objet mode = 1: dition de l'tiquette de l'objet""" self.close() # finalise l'ventuelle dition en cours self.texte = "" self.objet = objet # self.old_label = objet.label() self.mode = mode # self.display() def display(self): self.objet.label_temporaire = self.texte def cancel(self): self.objet.label_temporaire = None self.objet = None def ok(self): canvas = self.parent panel = canvas.parent # NB: si aucun nom n'est rentr, [ENTREE] est un quivalent de [ECHAP]. if self.texte: try: # si possible, on change le nom de l'objet ; sinon, on change son label. nom = self.objet.nom if isinstance(self.objet, Texte) or not re.match("""[A-Za-z_][A-Za-z0-9_'"`]*$""", self.texte): self.mode = 1 # label if self.mode: self.objet.label(self.texte) panel.action_effectuee(u"%s.label(%s)" %(nom, repr(self.texte))) else: self.objet.renommer(self.texte, legende = NOM) panel.action_effectuee(u"%s.renommer(%s, legende = %s)" %(nom, repr(self.texte), NOM)) except RuntimeError: # on reste en mode edition if not param.nom_multiple: self.display() raise self.objet.label(self.texte) # par dfaut, si A est rattribu un point, il sera trait comme tiquette. panel.action_effectuee(u"%s.label(%s)" %(nom, repr(self.texte))) canvas.message(u"Attention : ce nom est dj attribu.") except: self.display() raise self.cancel() def key(self, key): if self: if key == wx.WXK_BACK: self.texte = self.texte[:-1] self.display() elif key == wx.WXK_ESCAPE: self.cancel() elif key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER: self.ok() elif key == 10: # Ctrl + Entree ( tester sous Linux !) self.texte += "\n" self.display() elif key >= 32 and key <= 255 and key <> wx.WXK_DELETE: key = unichr(key) # a-z, A-Z, 0-9 ou _ self.texte += key self.display() def close(self): u"Ferme l'diteur. Ne renvoie pas d'erreur s'il est dj ferm." if self: try: self.ok() except: self.cancel() print_error() def __nonzero__(self): return self.objet is not None class WxCanvas(FigureCanvasWxAgg, Canvas): def __init__(self, parent, fixe = False): u"Si fixe = True, l'utilisateur ne peut pas zoomer ou recadrer la fentre d'affichage avec la souris." self.parent = parent # initialisation dans cet ordre (self.figure doit tre dfini pour initialiser FigureCanvas) Canvas.__init__(self, couleur_fond = self.param("couleur_fond")) FigureCanvasWxAgg.__init__(self, parent, -1, self.figure) if param.plateforme == "Linux": self.SetSize(wx.Size(10, 10)) elif param.plateforme == "Windows": self.SetWindowStyle(wx.WANTS_CHARS) self.Refresh() self.debut_zoom = None # Utilis pour zoomer avec Ctrl + Clic gauche (contiendra la position initiale) self.debut_select = None # Utilis pour slectionner simultanment plusieurs objets avec Alt + Clic gauche (pos. initiale) self.debut_shift = None # Utilis pour translater le contenu de la fentre (position initiale) self.redetecter = True # Rechercher les objets proximit du pointeur self.select_memoire = None # Objet devant tre prochainement slectionn (en cas de "litige" entre 2 objets) self.etiquette_selectionnee = None # tiquette couramment slctionne self.fixe = fixe self.interaction = None # Fonction lancer au prochain clic de souris (au lieu des actions par dfaut) self.interaction_deplacement = None # Fonction lancer au prochain dplacement de souris (au lieu des actions par dfaut) self.editeur = MiniEditeur(self) # edite les noms et etiquettes des points, textes, etc. # Paramtres temporaires d'affichage self._dessin_temporaire = False self.sel = [] # Liste ordonne (pour la dtection) des objets de la feuille actuelle. self.motion_event = None self.wheel_event_count = 0 self.wheel_ctrl_event_count = 0 self.Bind(wx.EVT_MOUSEWHEEL, self.EventOnWheel) self.Bind(wx.EVT_MOTION, self.EventOnMotion) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_CHAR, self.OnChar) #self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) self._pile_instructions = [] # instructions excuter lorsqu'aucune autre action n'est en cours. (Idle) self.Bind(wx.EVT_IDLE, self.OnIdle) timer=wx.Timer(self) self.Bind(wx.EVT_TIMER, self._actualiser_si_necessaire) timer.Start(150) @property def feuille_actuelle(self): return self.parent.feuille_actuelle def param(self, *args, **kw): return self.parent.param(*args, **kw) def message(self, txt, lieu = 0): self.parent.parent.parent.message(txt, lieu) # cf. geometrie.py def _curseur(self, sablier): if sablier: wx.BeginBusyCursor() else: wx.EndBusyCursor() if wx.Platform == '__WXMSW__': # Le curseur disparat sinon sous Windows !! wx.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) @property def dimensions(self): if self._dimensions is None: return self.GetSizeTuple() return self._dimensions def _affiche_module(self): u"Affichage spcifique au module en cours." self.parent._affiche() def exporter(self, *args, **kw): with BusyCursor(): Canvas.exporter(self, *args, **kw) # Gestion des evenements (essentiellement la souris). ############################### def coordonnees(self, event, dx = 0, dy = 0): u"""Renvoie les coordonnes correspondant l'vnement, converties en coordonnes de la feuille. Si [Maj] est enfonce, les coordonnes sont arrondies la graduation la plus proche. dx et dy correspondent au dcalage entre les coordonnes de l'objet, et le point o on l'a saisit. (Par exemple, un texte n'est pas forcment saisi au niveau de son point d'ancrage). """ x, y = self.pix2coo(*event.GetPositionTuple()) if event.ShiftDown() or self.grille_aimantee: a, b = self.gradu return a*round((x + dx)/a), b*round((y + dy)/b) else: return float(x + dx), float(y + dy) def interagir(self, fonction, aide = None, fonction_bis = None): u"""Permet l'interaction du canevas avec un module externe. A chaque clic de souris, la fonction indique est appele, avec un certains nombre de paramtres comme arguments : objet(s) proximit, position en coordonnes et en pixels... Une aide est ventuellement affiche dans la barre d'tat de la fentre principale. fonction_bis est ventuellement utilise lors de chaque dplacement de la souris. Elle reoit en argument la position en pixels uniquement (pour ne pas alourdir le traitement). Exemple de rcupration des arguments : si un module appelle self.canvas.interagir(ma_fonction, u"Cliquez n'importe o.", ma_2eme_fonction), le module doit dfinir : def ma_fonction(self, **kw): pixel = kw["pixel"] position = kw["position"] objet_selectionne = kw["selection"] autres_objets_a_proximite = kw["autres"] print u"clic!" def ma_2eme_fonction(self, **kw): pixel = kw["pixel"] print u"a bouge !" """ self.interaction = fonction self.interaction_deplacement = fonction_bis if fonction: self.SetCursor(wx.StockCursor(wx.CURSOR_QUESTION_ARROW)) else: self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) if aide is not None: self.message(aide) def signal(self, event = None): if self.interaction: pixel = event.GetPositionTuple() self.detecter(pixel) self.interaction(selection = self.select, autres = self.selections, position = self.coordonnees(event), pixel = pixel) self.detecter(pixel) # XXX: toujours utile ? def deplacable(self, obj): # indique si un objet est deplacable return isinstance(obj, Objet) and obj._deplacable def pointable(self, obj): # indique si un objet possede des coordonnees return isinstance(obj, Objet) and obj._pointable def infos(self): self.message(self.select.info if self.select is not None else '') def detecter(self, position = None): u"""Dtecte les objets proximit de la position indique. Typiquement, on utilise self.detecter(event.GetPositionTuple()).""" self.redetecter = False self.debut_zoom = None actuelle = self.feuille_actuelle # feuille courante if not self.affichage_gele: if position is None: position = self.ScreenToClient(wx.GetMousePosition()) if param.afficher_coordonnees: # affichage des coordonnees dans la barre d'etat self.message(str(self.pix2coo(*position)), 1) elif param.afficher_pixels: # pour dbogage self.message(str(position) + ' -> ' + str(self.pix2coo(*position)), 1) x, y = position # on place les objets 'modifiables' en premier (points libres, glisseurs, textes) self.sel = actuelle.liste_objets(tri = True) # liste des objets pres du pointeur de la souris : self.selections = [] for obj in self.sel: try: if obj.distance_inf(x, y, param.precision_selection): self.selections.append(obj) except: print_error() self.message(u"Erreur: les coordonnes de %s sont incalculables." %obj.nom) proximite = len(self.selections) if proximite: self.select = self.selections[0] if self.select_memoire and self.select_memoire in self.selections: self.select = self.select_memoire if self.interaction: self.SetCursor(wx.StockCursor(wx.CURSOR_QUESTION_ARROW)) else: if self.deplacable(self.select): self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) else: self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) self.infos() else: if self.interaction: self.SetCursor(wx.StockCursor(wx.CURSOR_QUESTION_ARROW)) else: self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) self.message("") self.select = None self.selection_en_gras() def OnIdle(self, event): u"Instructions excuter si aucun processus n'est en cours." if self.parent.parent.parent.closing: # L'application est en train d'tre arrte. # Tous les vnements restant passent la trappe... return if self.wheel_event_count != 0: try: self.OnWheel(self.wheel_event_count) finally: self.wheel_event_count = 0 elif self.wheel_ctrl_event_count != 0: try: self.OnCtrlWheel(self.wheel_ctrl_event_count) finally: self.wheel_ctrl_event_count = 0 elif self.motion_event is not None: try: self.OnMotion(self.motion_event) finally: self.motion_event = None elif self._pile_instructions: try: fonction, args, kwargs = self._pile_instructions[-1] fonction(*args, **kwargs) finally: self._pile_instructions.pop() self._actualiser_si_necessaire() def execute_on_idle(self, fonction, *args, **kwargs): u"Excuter une fois qu'aucun processus n'est actif." self._pile_instructions.append((fonction, args, kwargs)) def EventOnWheel(self, event): if event.ControlDown(): if event.GetWheelRotation() > 0: self.wheel_ctrl_event_count += 1 else: self.wheel_ctrl_event_count -= 1 else: if event.GetWheelRotation() > 0: self.wheel_event_count += 1 else: self.wheel_event_count -= 1 def OnWheel(self, wheel_event_count): u"Gestion du zoom par la roulette de la souris." if self.fixe: return if wheel_event_count > 0: self.zoomer(param.zoom_in**wheel_event_count) self.parent.action_effectuee("zoom_in", signature = 'zoom_in') # self.zoom_in() else: # self.zoom_out() self.zoomer(param.zoom_out**(-wheel_event_count)) self.parent.action_effectuee("zoom_out", signature = 'zoom_out') def OnCtrlWheel(self, wheel_ctrl_event_count): u"Gestion du zoom du texte par la roulette de la souris." if wheel_ctrl_event_count > 0: self.zoom_texte *= param.zoom_texte_in**wheel_ctrl_event_count self.zoom_ligne *= param.zoom_ligne_in**wheel_ctrl_event_count else: self.zoom_texte *= param.zoom_texte_out**(-wheel_ctrl_event_count) self.zoom_ligne *= param.zoom_ligne_out**(-wheel_ctrl_event_count) def EventOnMotion(self, event): self.motion_event = PseudoEvent(event) if event.LeftIsDown() and not self.HasCapture(): self.CaptureMouse() def OnMotion(self, event): #if self.FindFocus() in (self.parent, self.parent.parent, self.parent.parent.parent): if self.GetTopLevelParent().IsEnabled() and (event.LeftIsDown() or event.RightIsDown()): self.SetFocus() if self.redetecter: self.detecter() actuelle = self.feuille_actuelle if actuelle.objet_temporaire(): # Utilis pour la prvisualisation d'objets, avant leur construction avec la souris actuelle.point_temporaire().coordonnees = self.coordonnees(event) if event.LeftIsDown():# or self.interaction: self.editeur.close() if event.ControlDown(): # selection d'un zone pour zoomer if event.AltDown(): # selection simultane de tous les objets d'une zone self.selection_zone(event.GetPositionTuple()) elif not self.fixe: self.gestion_zoombox(event.GetPositionTuple()) elif event.AltDown(): # deplacement de l'etiquette d'un objet self.debut_zoom = None x, y = event.GetPositionTuple() if self.etiquette_selectionnee is None: for objet in actuelle.liste_objets(False): if objet.etiquette is not None: try: if objet.etiquette.distance_inf(x, y, param.precision_selection): self.etiquette_selectionnee = objet.etiquette x, y = self.etiquette_selectionnee.coordonnees x1, y1 = self.coordonnees(event) self.decalage_coordonnees = x - x1, y - y1 break except: print_error() self.message(u"Erreur: impossible de trouver l'tiquette de %s.." %objet.nom) if self.etiquette_selectionnee: self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) self.etiquette_selectionnee(*self.coordonnees(event, *self.decalage_coordonnees)) elif self.select is not None and not self.interaction: # deplacement d'un objet avec la souris self.debut_zoom = None if self.deplacable(self.select): self.select(*self.coordonnees(event, *self.decalage_coordonnees)) self.infos() elif event.RightIsDown() and self.debut_shift and not self.fixe: # deplacement de la feuille self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) self.fin_shift = self.pix2coo(*event.GetPositionTuple()) translation = array(self.fin_shift) - array(self.debut_shift) self.fenetre = self.fenetre[0] - translation[0], self.fenetre[1] - translation[0], self.fenetre[2] - translation[1], self.fenetre[3] - translation[1] if self.select is not None: self.select = None self.selection_en_gras() elif self.interaction_deplacement is not None: self.interaction_deplacement(pixel = event.GetPositionTuple()) elif not event.ControlDown(): # detection des objets a proximite du pointeur self.detecter(event.GetPositionTuple()) def OnSelect(self, x0, x1, y0, y1): x0, x1 = min(x0, x1), max(x0, x1) y0, y1 = min(y0, y1), max(y0, y1) objets_dans_la_zone = [] for objet in self.feuille_actuelle.liste_objets(): espace = objet.espace_vital if espace is not None: xmin, xmax, ymin, ymax = espace if x0 <= xmin <= xmax <= x1 and y0 <= ymin <= ymax <= y1: objets_dans_la_zone.append(objet) self.feuille_actuelle.objets_en_gras(*objets_dans_la_zone) def exporte(): actuelle = self.feuille_actuelle # feuille de travail courante ## if actuelle.sauvegarde["export"]: ## dir, fichier = os.path.split(actuelle.sauvegarde["export"]) # on exporte sous le mme nom qu'avant par dfaut ## elif actuelle.sauvegarde["nom"]: ## fichier = actuelle.sauvegarde["nom"] # le nom par defaut est le nom de sauvegarde ## dir = actuelle.sauvegarde["repertoire"] ## else: ## if param.rep_export is None: ## dir = param.repertoire ## else: ## dir = param.rep_export filename = self.parent.parent.ExportFile(exporter = False) # ne pas faire l'export, mais rcuprer juste le nom if filename: self.exporter(nom = filename, zone = (x0, x1, y0, y1)) actuelle.sauvegarde["export"] = filename if objets_dans_la_zone: dlg = wx.SingleChoiceDialog(self, u"Appliquer la slection :", u"Slection", [u"Supprimer les objets", u"Masquer les objets", u"Editer les objets", u"Exporter la zone comme image"], wx.CHOICEDLG_STYLE) if dlg.ShowModal() == wx.ID_OK: choix = dlg.GetSelection() if choix == 0: with self.geler_affichage(actualiser = True): for objet in objets_dans_la_zone: self.executer(u"del %s" %objet.nom) elif choix == 1: with self.geler_affichage(actualiser = True): for objet in objets_dans_la_zone: self.executer(u"%s.cacher()" %objet.nom) elif choix == 2: win = Proprietes(self, objets_dans_la_zone) win.Show(True) elif choix == 3: exporte() dlg.Destroy() with self.geler_affichage(): self.selection_en_gras() else: exporte() self.rafraichir_affichage() def OnLeftDown(self, event): # Patch pour l'utilisation avec un dispositif de pointage absolu (tablette graphique ou TNI) self.detecter(event.GetPositionTuple()) if self.HasCapture(): self.ReleaseMouse() self.SetFocus() if self.deplacable(self.select): x, y = self.select.coordonnees x1, y1 = self.coordonnees(event) self.decalage_coordonnees = x - x1, y - y1 if getattr(self.select, 'on_left_click', None) is not None: self.select.on_left_click() def OnLeftUp(self, event): if self.HasCapture(): self.ReleaseMouse() if self.etiquette_selectionnee: x, y = self.etiquette_selectionnee.coordonnees self.parent.action_effectuee(u"%s.etiquette(%s, %s)" %(self.etiquette_selectionnee.parent.nom, x, y)) self.etiquette_selectionnee = None return if self.debut_zoom and not self.fixe: #self.ReleaseMouse() try: if event.ControlDown(): (x0, y0), (x1, y1) = self.debut_zoom, self.fin_zoom self.executer("fenetre = " + str((x0, x1, y0, y1))) else: self.rafraichir_affichage() finally: self.debut_zoom = None elif self.debut_select and not self.fixe: #self.ReleaseMouse() try: if event.ControlDown() and event.AltDown(): (x0, y0), (x1, y1) = self.debut_select, self.fin_select self.OnSelect(x0, x1, y0, y1) else: self.rafraichir_affichage() finally: self.debut_select = None elif self.interaction: self.signal(event) self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) elif self.deplacable(self.select): self.parent.action_effectuee(self.select.nom + str(self.select.coordonnees)) # pas super precis : il se peut qu'on relache le bouton sans avoir deplace le point. # ca fait un enregistrement inutile dans l'historique... def OnRightDown(self, event): self.editeur.close() self.detecter(event.GetPositionTuple()) if self.select is not None and not event.ControlDown(): menu = wx.Menu() # Contournement d'un bug de wxGtk if wx.Platform == '__WXGTK__': menu.Append(wx.NewId(), u" \u2714 " + self.select.nom_complet) menu.AppendSeparator() else: menu.SetTitle(self.select.nom_complet) selections = [obj for obj in self.selections if obj is not self.select] # autres objets a proximite n = len(selections) ids = [wx.NewId() for i in xrange(n + 7)] for i in range(n): def select(event, obj = selections[i]): self.select = self.select_memoire = obj self.selection_en_gras() menu.Append(ids[i], u"Slectionner " + selections[i].nom_complet) menu.Bind(wx.EVT_MENU, select, id = ids[i]) if n: menu.AppendSeparator() menu.Append(ids[n], u"Supprimer") def supprimer(event, select = self.select): self.executer(u"%s.supprimer()" %select.nom) menu.Bind(wx.EVT_MENU, supprimer, id = ids[n]) if self.select.style("visible"): chaine = u"Masquer" else: chaine = u"Afficher" menu.Append(ids[n + 1], chaine) def masquer(event, select = self.select): self.executer(u"%s.style(visible = %s)" %(select.nom, not self.select.style("visible"))) menu.Bind(wx.EVT_MENU, masquer, id = ids[n + 1]) menu.Append(ids[n + 2], u"Renommer") def renommer(event, select = self.select): dlg = wx.TextEntryDialog(self, u"Note: pour personnaliser davantage le texte de l'objet,\nchoisissez \"Texte associ\" dans le menu de l'objet.", u"Renommer l'objet", select.nom_corrige) test = True while test: test = dlg.ShowModal() if test == wx.ID_OK: try: # on renomme, et on met l'affichage de la lgende en mode "Nom" self.executer(u"%s.renommer(%s, legende = %s)" %(select.nom, repr(dlg.GetValue()), NOM)) break except: print_error() if test == wx.ID_CANCEL: break dlg.Destroy() menu.Bind(wx.EVT_MENU, renommer, id = ids[n + 2]) msg = u"diter le texte" if isinstance(self.select, Texte_generique) else u"Texte associ" menu.Append(ids[n + 3], msg) def etiquette(event, select = self.select): old_style = select.style().copy() old_label = select.style(u"label") if old_label is None: # le style label n'existe pas pour l'objet return #dlg = wx.TextEntryDialog(self, "Note: le code LATEX doit etre entre $$. Ex: $\\alpha$", "Changer l'etiquette de l'objet", old_label, style = wx.TE_MULTILINE ) #dlg.FitInside() dlg = wx.Dialog(self, -1, u"Changer la lgende de l'objet (texte quelconque)") sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(wx.StaticText(dlg, -1, u"Note: le code LATEX doit etre entre $$. Ex: $\\alpha$"), 0, wx.ALIGN_LEFT|wx.ALL, 5) dlg.text = wx.TextCtrl(dlg, -1, old_label, size=wx.Size(300,50), style = wx.TE_MULTILINE) sizer.Add(dlg.text, 0, wx.ALIGN_LEFT|wx.ALL, 5) dlg.cb = wx.CheckBox(dlg, -1, u"Interprter la formule") dlg.cb.SetValue(select.style(u"legende") == FORMULE) sizer.Add(dlg.cb, 0, wx.ALIGN_LEFT|wx.ALL, 5) line = wx.StaticLine(dlg, -1, size=(20,-1), style=wx.LI_HORIZONTAL) sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5) box = wx.BoxSizer(wx.HORIZONTAL) btn = wx.Button(dlg, wx.ID_OK) #btn.SetDefault() box.Add(btn, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) btn = wx.Button(dlg, wx.ID_CANCEL, u" Annuler ") box.Add(btn, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) sizer.Add(box, 0, wx.ALIGN_RIGHT|wx.ALL, 5) dlg.SetSizer(sizer) sizer.Fit(dlg) dlg.CenterOnScreen() test = True while test: test = dlg.ShowModal() if test == wx.ID_OK: try: self.executer(u"%s.label(%s, %s)" %(select.nom, repr(dlg.text.GetValue()), dlg.cb.GetValue())) break except: select.style(**old_style) #~ print "Ancien style", old_style print_error() if test == wx.ID_CANCEL: #select.label(old_label) #~ select.style(**old_style) break dlg.Destroy() menu.Bind(wx.EVT_MENU, etiquette, id = ids[n + 3]) if self.select.label(): chaine = u"Masquer" else: chaine = u"Afficher" menu.Append(ids[n + 4], chaine + u" nom/texte") def masquer_nom(event, select = self.select): if self.select.label(): mode = RIEN else: if self.select.style(u"label"): mode = TEXTE else: mode = NOM self.executer(u"%s.style(legende = %s)" %(select.nom, mode)) menu.Bind(wx.EVT_MENU, masquer_nom, id = ids[n + 4]) menu.AppendSeparator() menu.Append(ids[n + 5], u"Redfinir") def redefinir(event, select = self.select): nom = select.nom dlg = wx.TextEntryDialog(self, u"Exemple: transformez une droite en segment.", u"Redfinir l'objet", select._definition()) test = True while test: test = dlg.ShowModal() if test == wx.ID_OK: try: # on redfinit l'objet self.executer(u"%s.redefinir(%s)" %(nom, repr(dlg.GetValue()))) break except: print_error() if test == wx.ID_CANCEL: break dlg.Destroy() menu.Bind(wx.EVT_MENU, redefinir, id = ids[n + 5]) menu.AppendSeparator() if isinstance(self.select, Point_generique): ids_relier = [wx.NewId(), wx.NewId(), wx.NewId()] relier = wx.Menu() relier.Append(ids_relier[0], u"aux axes") relier.Append(ids_relier[1], u" l'axe des abscisses") relier.Append(ids_relier[2], u" l'axe des ordonnes") def relier0(event, self = self, select = self.select): self.executer(u"%s.relier_axes()" %select.nom) relier.Bind(wx.EVT_MENU, relier0, id = ids_relier[0]) def relier1(event, self = self, select = self.select): self.executer(u"%s.relier_axe_x()" %select.nom) relier.Bind(wx.EVT_MENU, relier1, id = ids_relier[1]) def relier2(event, self = self, select = self.select): self.executer(u"%s.relier_axe_y()" %select.nom) relier.Bind(wx.EVT_MENU, relier2, id = ids_relier[2]) menu.AppendMenu(wx.NewId(), u"Relier le point", relier) menu.AppendSeparator() menu.Append(ids[n + 6], u"Proprits") def proprietes(event, select = self.select): win = Proprietes(self, [select]) win.Show(True) menu.Bind(wx.EVT_MENU, proprietes, id = ids[n + 6]) self.PopupMenu(menu) menu.Destroy() if self.select is not None: self.select = None self.selection_en_gras() self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) elif not self.fixe: self.debut_shift = self.pix2coo(*event.GetPositionTuple()) self.SetCursor(wx.StockCursor(wx.CURSOR_SIZING)) if not self.HasCapture(): self.CaptureMouse() def OnRightUp(self, event): if self.HasCapture(): self.ReleaseMouse() if self.fixe: return self.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) if self.debut_shift: self.debut_shift= None self.parent.action_effectuee(u"fenetre = " + str(self.fenetre)) def editer(self, mode = 0): if self.select is not None: self.editeur.init(self.select, mode) else: self.editeur.close() def OnChar(self, event): if self.redetecter: self.detecter() key = event.GetKeyCode() debug(u"key: ", key) if key == wx.WXK_DELETE and self.select: if event.ShiftDown(): self.executer(u"%s.cacher()" %self.select.nom) else: self.executer(u"%s.supprimer()" %self.select.nom) if (key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER) and self.editeur.objet is not self.select: self.editer(event.ShiftDown()) elif self.editeur: self.editeur.key(key) if key == wx.WXK_ESCAPE and self.interaction: print "ESCAPE !" self.interaction(special = "ESC") if key < 256:debug(unichr(key)) event.Skip() def OnPaint(self, event): self.graph.restaurer_dessin() if param.plateforme == "Windows": event.Skip() def OnSize(self, event): self.feuille_actuelle._rafraichir_figures() self.rafraichir_affichage(rafraichir_axes = True) event.Skip() def OnLeave(self, event): self.execute_on_idle(self.feuille_actuelle.objets_en_gras) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/proprietes_feuille.py0000644000175000017500000001476312014170666024630 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Proprietes de la feuille # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from .wxlib import MyMiniFrame from ..pylib import uu class ProprietesDescription(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.parent = parent self.feuille = parent.feuille self.sizer = gbs = wx.GridBagSizer(10, 10) self.infos = {} gbs.Add(wx.StaticText(self, -1, u"Titre : "), (1, 1)) self.titre = wx.TextCtrl(self, value = self.feuille.infos("titre"), size=wx.Size(300, -1)) self.Bind(wx.EVT_TEXT, self.EvtTitre, self.titre) gbs.Add(self.titre, (1, 2), flag = wx.EXPAND) gbs.Add(wx.StaticText(self, -1, u"Auteur : "), (2, 1)) self.auteur = wx.TextCtrl(self, value = self.feuille.infos("auteur"), size=wx.Size(300, -1)) self.Bind(wx.EVT_TEXT, self.EvtAuteur, self.auteur) gbs.Add(self.auteur, (2, 2), flag = wx.EXPAND) gbs.Add(wx.StaticText(self, -1, u"Version : "), (3, 1)) self.version = wx.TextCtrl(self, value = self.feuille.infos("version"), size=wx.Size(300, -1)) self.Bind(wx.EVT_TEXT, self.EvtVersion, self.version) gbs.Add(self.version, (3, 2), flag = wx.EXPAND) gbs.Add(wx.StaticText(self, -1, u"Resum : "), (4, 1)) self.resume = wx.TextCtrl(self, value = self.feuille.infos("resume"), size=wx.Size(300, 50), style=wx.TE_MULTILINE) self.Bind(wx.EVT_TEXT, self.EvtResume, self.resume) gbs.Add(self.resume, (4, 2), flag = wx.EXPAND) gbs.Add(wx.StaticText(self, -1, u"Notes : "), (5, 1)) self.notes = wx.TextCtrl(self, value = self.feuille.infos("notes"), size=wx.Size(300, 100), style=wx.TE_MULTILINE) self.Bind(wx.EVT_TEXT, self.EvtNotes, self.notes) gbs.Add(self.notes, (5, 2), flag = wx.EXPAND) boutons = wx.GridBagSizer(10, 10) ok = wx.Button(self, wx.ID_OK) ok.Bind(wx.EVT_BUTTON, self.EvtOk) appliquer = wx.Button(self, label = u"Appliquer") appliquer.Bind(wx.EVT_BUTTON, self.EvtAppliquer) effacer = wx.Button(self, label = u"Effacer") effacer.Bind(wx.EVT_BUTTON, self.EvtEffacer) annuler = wx.Button(self, label = u"Annuler") annuler.Bind(wx.EVT_BUTTON, self.EvtAnnuler) boutons.Add(ok, (1, 0)) boutons.Add(appliquer, (1, 1)) boutons.Add(effacer, (1, 2)) boutons.Add(annuler, (1, 3)) boutons.Add(wx.StaticText(self, -1, ""), (2, 1)) boutons.Add gbs.Add(boutons, (6, 2))#, (1, 2)) gbs.SetEmptyCellSize(wx.Size(10, 10)) boutons.SetEmptyCellSize(wx.Size(4, 4)) self.SetSizerAndFit(self.sizer) self.parent.parent.dim1 = self.sizer.CalcMin().Get() def EvtTitre(self, event): self.infos["titre"] = event.GetString() def EvtAuteur(self, event): self.infos["auteur"] = event.GetString() def EvtVersion(self, event): self.infos["version"] = event.GetString() def EvtResume(self, event): self.infos["resume"] = event.GetString() def EvtNotes(self, event): self.infos["notes"] = event.GetString() def EvtOk(self, event): self.EvtAppliquer(event) self.EvtAnnuler(event) def EvtAppliquer(self, event): self.feuille.infos(**self.infos) self.parent.parent.panel.rafraichir_titre() def EvtEffacer(self, event): self.titre.SetValue("") self.auteur.SetValue("") self.version.SetValue("") self.resume.SetValue("") self.notes.SetValue("") def EvtAnnuler(self, event): self.parent.parent.Close() # fermeture de la frame class ProprietesStatistiques(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.parent = parent self.feuille = parent.feuille self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(wx.StaticText(self, -1, u"Informations sur " + uu(self.feuille.nom) + " :"), 0, wx.ALL, 8) self.sizer.Add(wx.StaticText(self, -1, u"Date de cration : " + uu(self.feuille.infos("creation"))), 0, wx.ALL, 8) self.sizer.Add(wx.StaticText(self, -1, u"Dernire modification : " + uu(self.feuille.infos("modification"))), 0, wx.ALL, 8) self.sizer.Add(wx.StaticText(self, -1, u"Nombre d'objets : " + str(len(self.feuille.liste_objets(True)))), 0, wx.ALL, 8) self.SetSizerAndFit(self.sizer) self.parent.parent.dim2 = self.sizer.CalcMin().Get() class OngletsProprietesFeuille(wx.Notebook): def __init__(self, parent): self.parent = parent self.feuille = parent.feuille wx.Notebook.__init__(self, parent) self.description = ProprietesDescription(self) self.AddPage(self.description, u"Description") self.statistiques = ProprietesStatistiques(self) self.AddPage(self.statistiques, u"Statistiques") class ProprietesFeuille(MyMiniFrame): def __init__(self, parent, feuille): self.parent = parent self.feuille = feuille self.fenetre_principale = self while hasattr(self.fenetre_principale, "parent"): # detection de la fenetre principale de WxGeometrie. self.fenetre_principale = self.fenetre_principale.parent self.panel = self.fenetre_principale.onglets.onglet_actuel MyMiniFrame.__init__(self, parent, u"Proprits de " + uu(self.feuille.nom)) self.onglets = OngletsProprietesFeuille(self) self.SetSize(wx.Size(*(max(dimensions) + 50 for dimensions in zip(self.dim1, self.dim2)))) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/fenetre_principale.py0000644000175000017500000001677312014170666024570 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # fenetre principale # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import sys, thread, traceback import wx from wx import PyOnDemandOutputWindow from ..pylib import uu, print_error, path2, debug from . import Panel_API_graphique from .ligne_commande import LigneCommande from .onglets import Onglets from .gestion_session import GestionnaireSession from ..API.console import Console from .. import param NOMPROG = param.NOMPROG class ReceptionDeFichiers(wx.FileDropTarget): def __init__(self, window): wx.FileDropTarget.__init__(self) self.window = window def OnDropFiles(self, x, y, filenames): for filename in filenames: if filename.endswith(u".geo") or filename.endswith(u".geoz"): self.window.onglets.ouvrir(filename) class FenetrePrincipale(wx.Frame): def __init__(self, app, fichier_log=None): wx.Frame.__init__(self, parent=None, title=NOMPROG) self.SetBackgroundColour(wx.WHITE) self.application = app # pour acceder a l'application en interne # crer avant les onglets self.fenetre_sortie = PyOnDemandOutputWindow(title = NOMPROG + u" - messages.") self.fichier_log = fichier_log self.SetIcon(wx.Icon(path2(u"%/images/icone.ico"), wx.BITMAP_TYPE_ICO)) # Barre de statut self.barre = wx.StatusBar(self, -1) self.barre.SetFieldsCount(2) self.barre.SetStatusWidths([-3, -2]) self.SetStatusBar(self.barre) self.message(u" Bienvenue !", 1) self.message(NOMPROG + u" version " + param.version) #Ligne de commande de dbogage self.ligne_commande = LigneCommande(self, 300, action = self.executer_commande, \ afficher_bouton = False, legende = 'Ligne de commande :') self.ligne_commande.Show(param.ligne_commande) # Creation des onglets et de leur contenu self.onglets = Onglets(self) self.__sizer_principal = wx.BoxSizer(wx.VERTICAL) self.__sizer_principal.Add(self.ligne_commande, 0, wx.LEFT, 5) self.__sizer_principal.Add(self.onglets, 1, wx.GROW) self.SetSizer(self.__sizer_principal) self.Fit() x_fit, y_fit = self.GetSize() x_param, y_param = param.dimensions_fenetre self.SetSize(wx.Size(max(x_fit, x_param), max(y_fit, y_param))) self.console = Console(self) self.Bind(wx.EVT_CLOSE, self.OnClose) self.SetDropTarget(ReceptionDeFichiers(self)) self.SetFocus() self.Bind(wx.EVT_IDLE, self.OnIdle) # closing == True si l'application est en train d'tre ferme self.closing = False self.gestion = GestionnaireSession(self.onglets) def OnIdle(self, evt): self.gestion.autosave() def afficher_ligne_commande(self, afficher=None): u"Afficher ou non la ligne de commande." if afficher is not None: if isinstance(afficher, bool): param.ligne_commande = afficher else: param.ligne_commande = not param.ligne_commande self.ligne_commande.Show(param.ligne_commande) if param.ligne_commande: self.ligne_commande.SetFocus() self.SendSizeEvent() return param.ligne_commande def mode_debug(self, debug=None): u"Passer en mode dboguage." if debug is not None: if isinstance(debug, bool): param.debug = debug else: param.debug = not param.debug if not param.debug: self.fenetre_sortie.close() return param.debug def message(self, texte, lieu=0): self.barre.SetStatusText(texte, lieu) def titre(self, texte=None): titre = NOMPROG if texte: titre += '-' + uu(texte) self.SetTitle(titre) def executer_commande(self, commande, **kw): try: self.console.executer(commande) self.message(u"Commande interne excute.") self.ligne_commande.Clear() except Exception: self.message(u"Commande incorrecte.") if param.debug: raise def OnClose(self, event): self.closing = True if not param.fermeture_instantanee: # pour des tests rapides try: if param.confirmer_quitter: panel = self.onglets.onglet_actuel if hasattr(panel, u"canvas") and hasattr(panel.canvas, u"Freeze"): panel.canvas.Freeze() dlg = wx.MessageDialog(self, u'Voulez-vous quitter %s ?' %NOMPROG, u'Quitter %s ?' %NOMPROG, wx.YES_NO | wx.ICON_QUESTION) reponse = dlg.ShowModal() if hasattr(panel, u"canvas") and hasattr(panel.canvas, u"Thaw"): panel.canvas.Thaw() dlg.Destroy() if reponse != wx.ID_YES: self.closing = False return self.gestion.sauver_preferences() self.gestion.sauver_session() for onglet in self.onglets: try: if isinstance(onglet, Panel_API_graphique): if param.historique_log: onglet.log.archiver() onglet.fermer_feuilles() except: #print_error() debug(u"Fermeture incorrecte de l'onglet : ", uu(str(onglet))) raise except Exception: try: print_error() wx.lib.dialogs.ScrolledMessageDialog(self, traceback.format_exc(), u"Erreur lors de la fermeture du programme").ShowModal() except UnicodeError: wx.lib.dialogs.ScrolledMessageDialog(self, "Impossible d'afficher l'erreur.", u"Erreur lors de la fermeture du programme").ShowModal() sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ if hasattr(self, "fenetre_sortie"): self.fenetre_sortie.close() # Si le premier onglet n'est pas actif au moment de quitter, cela produit une "Segmentation fault" sous Linux. # Quant savoir pourquoi... if self.onglets.GetRowCount(): self.onglets.ChangeSelection(0) print "On ferme !" event.Skip() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/mode_script.py0000644000175000017500000000567612014170666023242 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # mode script # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from ..geolib.feuille import Feuille from ..API.canvas import Canvas from ..API.sauvegarde import ouvrir_fichierGEO from ..pylib import print_error from .. import param def _err(msg, *msgs): msg = "== Erreur: %s ==" %msg if msgs: msg += '\n---\n' + '\n'.join(msgs) + '\n---' return msg def mode_script(input = None, output = None): try: if input is None: input = raw_input(u'Adresse du fichier de script ou du fichier .geo :') if input.endswith('.geo') or input.endswith('.geoz'): fgeo, message = ouvrir_fichierGEO(input) if fgeo is None: return _err(message) try: commandes = fgeo.contenu["Figure"][0] except KeyError: return _err(u"Le fichier '%s' ne comporte pas de figure." %input) else: try: with open(input, 'r') as f: commandes = f.read() except IOError: print_error() return _err(u"Fichier introuvable: '%s'" % input) feuille = Feuille() canvas = Canvas(feuille = feuille) feuille.canvas = canvas try: feuille.charger(commandes) except Exception: print_error() return _err(u"Commandes incorrectes", commandes) if output is None: output = raw_input(u'Adresse du fichier de sortie (png/svg/...) :') try: print canvas.fenetre canvas.exporter(output, echelle = param.echelle_cm) except IOError: print_error() return _err(u"Impossible d'exporter dans '%s'. Vrifiez les permissions ou l'espace disque." % output) except Exception: print_error() return _err(u"Erreur d'excution du mode script.") wxgeometrie-0.133.2.orig/wxgeometrie/GUI/app.py0000644000175000017500000000273212014170666021500 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx class App(wx.PySimpleApp): def boucle(self): self.MainLoop() def nom(self, nom=''): self.SetAppName(nom) def vers_presse_papier(self, texte): clipBoard=wx.TheClipboard if clipBoard.Open(): clipBoard.AddData(wx.TextDataObject(texte)) clipBoard.Close() return True return False app = App() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/ligne_commande.py0000644000175000017500000001253112014170666023657 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # Widget LigneCommande # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx class LigneCommande(wx.Panel): u"Un TextCtrl muni d'un historique et associ un bouton pour valider." def __init__(self, parent, longueur = 500, texte = None, action = (lambda *args, **kw: True), afficher_bouton = True, legende = None): self.parent = parent self.action = action wx.Panel.__init__(self, parent, -1, style = wx.TAB_TRAVERSAL|wx.WANTS_CHARS) self.SetBackgroundColour(self.parent.GetBackgroundColour()) sizer = wx.BoxSizer(wx.HORIZONTAL) self.texte = wx.TextCtrl(self, size = wx.Size(longueur, -1), style = wx.TE_PROCESS_ENTER) self.texte.Bind(wx.EVT_KEY_UP, self.EvtChar) if texte is None: self.bouton = wx.Button(self, wx.ID_OK) else: self.bouton = wx.Button(self, label = texte) self.bouton.Bind(wx.EVT_BUTTON, self.EvtButton) self.bouton.Show(afficher_bouton) if legende is not None: sizer.Add(wx.StaticText(self, -1, legende), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) sizer.Add(self.texte, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 5) sizer.Add(self.bouton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.SetSizer(sizer) self.Fit() self.initialiser() def initialiser(self): self.historique = [] self.position = None self.SetFocus() self.Clear() def GetValue(self): return self.texte.GetValue() def SetValue(self, value): self.texte.SetValue(value) def SetFocus(self): self.texte.SetFocus() def Clear(self): self.texte.Clear() def GetSelection(self): return self.texte.GetSelection() def GetInsertionPoint(self): return self.texte.GetInsertionPoint() def SetInsertionPoint(self, num): return self.texte.SetInsertionPoint(num) def WriteText(self, texte): self.texte.WriteText(texte) def SetSelection(self, deb, fin): self.texte.SetSelection(deb, fin) def SetToolTip(self, tip): self.texte.SetToolTip(tip) def EvtButton(self, event): commande = self.GetValue() self.position = None if commande: self.historique.append(commande) elif self.historique: # Appuyer une deuxime fois sur [Entre] permet de rpter l'action prcdente. commande = self.historique[-1] kw = {} for modifier in ('shift', 'alt', 'meta', 'control'): kw[modifier] = getattr(event, modifier.capitalize() + 'Down', lambda: None)() self.action(commande, **kw) def EvtChar(self, event): code = event.GetKeyCode() commande = self.GetValue() if code in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): self.EvtButton(event) elif code == wx.WXK_UP: # On remonte dans l'historique (-> entres plus anciennes) if self.position is None: # cas d'une commande en cours d'dition : if commande: if commande != self.historique[-1]: # on enregistre la commande en cours self.historique.append(commande) self.position = len(self.historique) - 1 else: self.position = len(self.historique) if self.position > 0: self.position -= 1 self.texte.SetValue(self.historique[self.position]) elif code == wx.WXK_DOWN: # On redescend dans l'historique (-> entres plus rcentes) if self.position is None or self.position == len(self.historique) - 1: if commande and commande != self.historique[-1]: self.historique.append(commande) self.texte.Clear() self.position = len(self.historique) elif self.position < len(self.historique) - 1: self.position += 1 self.texte.SetValue(self.historique[self.position]) else: self.position = None event.Skip() def Command(*args, **kwargs): pass def Create(*args, **kwargs): pass def GetAlignment(*args, **kwargs): pass def GetLabelText(*args, **kwargs): pass wxgeometrie-0.133.2.orig/wxgeometrie/GUI/fenetre_options.py0000644000175000017500000001421712014170666024124 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################### from functools import partial import wx, wx.lib.newevent from ..param.options import Section, Parametre OptionsModifiedEvent, EVT_OPTIONS_MODIFIED = wx.lib.newevent.NewEvent() class FenetreOptions(wx.Frame): def __init__(self, parent, options): wx.Frame.__init__(self, parent, -1, options.titre, style = wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT) self.parent = parent self.onglets = wx.Notebook(self, -1, style=wx.NB_TOP) dimensions_onglets = [] self.widgets = {} for theme in options: panel = wx.Panel(self.onglets) sizer = wx.BoxSizer(wx.VERTICAL) self.onglets.AddPage(panel, theme.titre) for elt in theme: if isinstance(elt, Section): box = wx.StaticBox(panel, -1, elt.titre) bsizer = wx.StaticBoxSizer(box, wx.VERTICAL) bsizer.AddSpacer(3) for parametre in elt: if isinstance(parametre, Parametre): psizer = self.ajouter_parametre(parametre, panel, sizer) bsizer.Add(psizer, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 2) elif isinstance(parametre, basestring): bsizer.Add(wx.StaticText(panel, -1, parametre), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) else: raise NotImplementedError, repr(type(elt)) bsizer.AddSpacer(3) sizer.Add(bsizer, 0, wx.ALL, 8) elif isinstance(elt, Parametre): psizer = self.ajouter_parametre(elt, panel, sizer) sizer.Add(psizer, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) elif isinstance(elt, basestring): sizer.Add(wx.StaticText(panel, -1, elt), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) else: raise NotImplementedError, repr(type(elt)) boutons = wx.BoxSizer(wx.HORIZONTAL) ok = wx.Button(panel, wx.ID_OK) ok.Bind(wx.EVT_BUTTON, self.EvtOk) boutons.Add(ok, 0, wx.ALL, 5) defaut = wx.Button(panel, label = u"Dfaut") defaut.Bind(wx.EVT_BUTTON, self.EvtDefaut) boutons.Add(defaut, 0, wx.ALL, 5) annuler = wx.Button(panel, wx.ID_CANCEL) annuler.Bind(wx.EVT_BUTTON, self.EvtAnnuler) boutons.Add(annuler, 0, wx.ALL, 5) sizer.Add(boutons, 0, wx.ALL, 5) panel.SetSizer(sizer) dimensions_onglets.append(sizer.CalcMin().Get()) w, h = (max(dimensions) for dimensions in zip(*dimensions_onglets)) self.SetSize(wx.Size(w + 10, h + 50)) self.CenterOnParent() def ajouter_parametre(self, parametre, panel, sizer): psizer = wx.BoxSizer(wx.HORIZONTAL) if parametre.type is not bool: psizer.Add(wx.StaticText(panel, -1, parametre.texte + ' :'), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) if parametre.type is bool: widget = wx.CheckBox(panel, label=parametre.texte) elif parametre.type is file: widget = wx.TextCtrl(panel, -1, '', size=(200,-1)) elif parametre.type is str: widget = wx.TextCtrl(panel, -1, '') elif isinstance(parametre.type, tuple): min_, max_ = parametre.type widget = wx.SpinCtrl(panel, min = min_, max = max_) elif isinstance(parametre.type, list): ## widget = wx.Choice(panel, -1, choices = parametre.type) widget = wx.ComboBox(panel, choices = parametre.type, style = wx.CB_READONLY) else: print parametre.type raise NotImplementedError self.widgets[parametre.nom] = widget widget.parametre = parametre ## if insinstance(parametre.type, list): ## widget.SetSelection(parametre.type.index(parametre.valeur)) ## else: ## widget.SetValue(parametre.valeur) widget.SetValue(parametre.valeur) psizer.Add(widget, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) if parametre.type is file: parcourir = wx.Button(panel, -1, u'Parcourir') self.Bind(wx.EVT_BUTTON, partial(self.EvtParcourir, widget = widget), parcourir) psizer.Add(parcourir, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) return psizer def EvtOk(self, evt = None): modifs = set() for widget in self.widgets.itervalues(): new_val = widget.GetValue() if new_val != widget.parametre.valeur: widget.parametre.valeur = new_val modifs.add(widget.parametre.prefixe) wx.PostEvent(self.parent, OptionsModifiedEvent(options = modifs)) self.Close() def EvtDefaut(self, evt = None): for widget in self.widgets.itervalues(): widget.SetValue(widget.parametre.defaut) def EvtAnnuler(self, evt = None): self.Close() def EvtParcourir(self, evt = None, widget = None): dlg = wx.DirDialog(self, u"Choisissez un rpertoire :", style=wx.DD_DEFAULT_STYLE) if dlg.ShowModal() == wx.ID_OK: widget.SetValue(dlg.GetPath()) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/barre_outils.py0000644000175000017500000016725712014170666023430 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # Barre d'outils pour la gomtrie # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from math import pi import wx from .wxlib import png from ..pylib import is_in from ..geolib.routines import distance from ..geolib.textes import Texte_generique from ..geolib.points import Point_generique, Barycentre, Point_pondere, Milieu from ..geolib.cercles import Arc_generique, Cercle_generique, Cercle, Arc_points,\ Arc_oriente, Cercle_diametre, Cercle_points, Demicercle,\ Arc_cercle from ..geolib.lignes import Droite_generique, Segment, Demidroite, Ligne_generique,\ Droite, Tangente, Parallele, Perpendiculaire, Bissectrice,\ Mediatrice, DemiPlan from ..geolib.polygones import Polygone_generique, PrevisualisationPolygone from ..geolib.angles import Angle_generique, Angle, Angle_oriente, Secteur_angulaire from ..geolib.transformations import Rotation, Homothetie, Translation from ..geolib.vecteurs import Vecteur_generique, Vecteur from ..geolib.intersections import Intersection_cercles, Intersection_droite_cercle from ..geolib.objet import Objet, contexte from .. import param class MultiButton(wx.BitmapButton): def __init__(self, parent, raccourci, selectionnable, *liste): self.parent = parent self.raccourci = raccourci self.selectionnable = selectionnable self.selected = False self.liste = list(liste) self.items = [] for i in xrange(len(self.liste)): if self.liste[i] is None: self.items.append(None) else: self.items.append(wx.NewId()) self.parent.Bind(wx.EVT_MENU, self.OnPopup, id=self.items[-1]) self.liste[i] = list(self.liste[i]) # on cre 2 images, nomme segment.png et segment_.png par exemple, la 2e correspondant au bouton slectionn. self.liste[i][1] = [png(self.liste[i][1] + s) for s in ("", "_")] wx.BitmapButton.__init__(self, parent, style=wx.NO_BORDER) self.SetBackgroundColour(self.parent.GetBackgroundColour()) self.select(False, 0) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightclic) self.Bind(wx.EVT_LEFT_DCLICK, self.OnRightclic) self.Bind(wx.EVT_BUTTON, self.OnLeftclic) if self.raccourci is not None: self.parent.canvas.Bind(wx.EVT_CHAR, self.OnChar) def select(self, sel = True, i = None): u"Slectionne et affiche le bouton i" if i is not None: self.selection = i if self.raccourci is None: touche = "" else: touche = " (" + self.raccourci.replace("Shift", "Maj") +")" aide = self.liste[self.selection][2] + touche if len(self.liste) > 1: aide += "\n\nClic droit pour plus de choix." self.SetToolTipString(aide) self.display(sel) def display(self, selected = None): if self.selectionnable: if selected is not None: self.selected = selected if selected is True: if hasattr(self.parent, "selected_button") and self.parent.selected_button is not self: self.parent.selected_button.display(False) self.parent.selected_button = self #else: # self.selected = False self.SetBitmapLabel(self.liste[self.selection][1][self.selected]) def action(self, event = None): self.liste[self.selection][3](event) def OnLeftclic(self, event = None): self.display(True) self.action(event) def OnRightclic(self, event = None): if len(self.liste) > 1: menu = wx.Menu() for i in xrange(len(self.liste)): if self.liste[i] is None: menu.AppendSeparator() #item = wx.MenuItem(menu, id = wx.ITEM_SEPARATOR, kind = wx.ITEM_SEPARATOR) else: item = wx.MenuItem(menu, self.items[i], self.liste[i][0]) #item.SetBitmap(self.liste[i][1][0]) #item.SetBackgroundColour(self.parent.GetBackgroundColour()) #-> ces mthodes sont largement buggues dans WxPython menu.AppendItem(item) del item self.PopupMenu(menu) menu.Destroy() def OnPopup(self, event = None): self.select(True, self.items.index(event.GetId())) self.action(event) def OnChar(self, event = None): r = self.raccourci.split("+") if len(r) is 2: m = r[0].lower() shift = m == "shift" alt = m == "alt" ctrl = m == "ctrl" else: shift = alt = ctrl = False if (shift^event.ShiftDown()) or (alt^event.AltDown()) or (ctrl^event.ControlDown()): event.Skip() return keycode = event.GetKeyCode() if hasattr(wx, "WXK_" + r[-1]) and getattr(wx, "WXK_" + r[-1]) == keycode: self.OnLeftclic(event) elif 0 <= keycode < 256 and unichr(keycode) == r[-1]: self.OnLeftclic(event) else: event.Skip() class BarreOutils(wx.Panel): def __init__(self, parent, couleur = None): self.parent = parent wx.Panel.__init__(self, parent, -1, style = wx.TAB_TRAVERSAL|wx.WANTS_CHARS) self.SetBackgroundColour(couleur if couleur is not None else wx.NamedColor(u"WHITE")) self.debut_selection = None self.debut_zoombox = None self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.add2(u"ouvrir", u"Ouvrir un fichier .geo.", self.parent.parent.OpenFile) self.btn_sauver = self.add2(u"sauvegarde", u"Enregistrer le document.", self.parent.parent.SaveFile) self.add2(u"image", u"Exporter comme image.", self.parent.parent.ExportFile) self.add2(u"annuler3", u"Annuler l'action prcdente.", self.parent.annuler) self.add2(u"retablir3", u"Rtablir une action annule.", self.parent.retablir) if self.parent.param('afficher_boutons'): self.creer_boutons() self.SetSizer(self.sizer) self.Fit() def add(self, racc, *liste): u"Ajoute des boutons multiples, slectionnables." button = MultiButton(self, racc, True, *liste) i = 5 if param.plateforme == "Linux": i = 0 self.sizer.Add(button, 0, wx.ALL, i) return button def add2(self, *args): u"Ajoute des boutons simples, non slectionnables." button = MultiButton(self, None, False, ("",) + args) i = 5 if param.plateforme == "Linux": i = 0 self.sizer.Add(button, 0, wx.ALL, i) return button def creer_boutons(self): self.add("F1", (u"Pointeur", u"fleche4", u"Dplacer ou modifier un objet.", self.curseur), (u"Zoomer", u"zoombox2", u"Recentrer la zone d'affichage.", self.zoombox), (u"Slectionner", u"selection", u"Slectionner une partie de la feuille.", self.selectionner)).display(True) self.add("F2", (u"Point", u"point2",u"Crer un point.", self.point)) self.add("F3", (u"Milieu", u"milieu2", u"Crer un milieu ou un centre.", self.milieu)) self.add("F4", (u"Segment", u"segment2", u"Crer un segment.", self.segment), None, (u"Vecteur", u"vecteur", u"Crer un vecteur.", self.vecteur), (u"Reprsentant d'un vecteur", u"representant", u"Crer un reprsentant d'un vecteur.", self.representant), ) self.add("F5", (u"Droite", u"droite2", u"Crer une droite.", self.droite), (u"Demi-droite", u"demidroite", u"Crer une demi-droite.", self.demidroite), None, (u"Parallle", u"parallele", u"Crer une parallle.", self.parallele), (u"Perpendiculaire", u"perpendiculaire", u"Crer une perpendiculaire.", self.perpendiculaire), (u"Tangente", u"tangente", u"Crer une tangente.", self.tangente), None, (u"Mdiatrice", u"mediatrice", u"Crer une mdiatrice.", self.mediatrice), (u"Bissectrice", u"bissectrice", u"Crer une bissectrice.", self.bissectrice), None, (u"Demi-plan", 'demiplan', u"Crer un demi-plan", self.demiplan), ) self.add("F6", (u"Cercle", u"cercle", u"Crer un cercle.", self.cercle), (u"Cercle dfini par son diamtre", u"cerclediametre", u"Crer un cercle dfini par son diamtre.", self.cercle_diametre), (u"Cercle passant par 3 points", u"cercle3points", u"Crer un cercle passant par 3 points.", self.cercle_points), None, (u"Arc de cercle", u"arc", u"Crer un arc de cercle de centre donn.", self.arc), (u"Arc passant par 3 points", u"arc_points", u"Crer un arc de cercle passant par 3 points.", self.arc_points), (u"Arc de cercle orient", u"arc_oriente", u"Crer un arc de cercle orient.", self.arc_oriente), (u"Demi-cercle", u"demicercle", u"Crer un demi-cercle.", self.demicercle), None, (u"Disque", u"disque", u"Crer un disque.", self.disque), ) self.add("F7", (u"Polygone", u"polygone", u"Crer un polygone.", self.polygone), None, (u"Triangle", u"triangle", u"Crer un triangle.", self.triangle), (u"Triangle rectangle", u"triangle_rectangle", u"Crer un triangle rectangle d'hypothnuse donne.", self.triangle_rectangle), (u"Triangle isocle", u"triangle_isocele", u"Crer un triangle isocle.", self.triangle_isocele), (u"Triangle isocle rectangle", u"triangle_rectangle_isocele", u"Crer un triangle isocle rectangle d'hypothnuse donne.", self.triangle_isocele_rectangle), (u"Triangle quilatral", u"triangle_equilateral", u"Crer un triangle quilatral.", self.triangle_equilateral), None, (u"Paralllogramme", u"parallelogramme", u"Crer un paralllogramme.", self.parallelogramme), (u"Rectangle", u"rectangle", u"Crer un rectangle.", self.rectangle), (u"Losange", u"losange", u"Crer un losange.", self.losange), (u"Carr", u"carre", u"Crer un carr.", self.carre), ) self.add("F8", (u"Intersection", u"intersection", u"Crer une intersection.", self.intersection)) self.add("F9", (u"Angle", u"angle", u"Crer un angle non orient.", self.angle), (u"Angle orient", u"angle_oriente", u"Crer un angle orient.", self.angle_oriente), ) self.add("Shift+F1", (u"Symtrie centrale", u"symetrie_centrale", u"Crer l'image d'un objet par symtrie par rapport un point.", self.symetrie), (u"Rflexion", u"reflexion", u"Crer l'image d'un objet par symtrie axiale.", self.reflexion), (u"Translation", u"translation", u"Crer l'image d'un objet par translation.", self.translation), (u"Rotation", u"rotation", u"Crer l'image d'un objet par rotation autour d'un point.", self.rotation), (u"Homothtie", u"homothetie", u"Crer l'image d'un objet par homothtie.", self.homothetie), ) self.add("Shift+F2", (u"Texte", u"texte", u"Crer un texte.", self.texte)) self.add("Shift+F3", (u"Masquer", u"masquer", u"Masquer des objets.", self.masque)) self.add("Shift+F4", (u"Gommer", u"gomme", u"Supprimer des objets.", self.gomme)) self.add("Shift+F5", (u"Copier", u"pinceau", u"Copier le style d'un objet.", self.pinceau)) @property def feuille_actuelle(self): return self.parent.feuille_actuelle @property def canvas(self): return self.parent.canvas ## @property ## def commande(self): ## return self.parent.commande def rafraichir(self): u"Appel par le parent pour rafraichir la barre d'outils." self.btn_sauver.selected = not self.feuille_actuelle.modifiee self.btn_sauver.display() def initialiser(self): self.cache = [] # objets en memoire # S'il n'y a pas de feuille, on la cre. self.feuille_actuelle.objet_temporaire(None) self.canvas.liste_objets_en_gras.clear() self.canvas.selection_en_gras() if self.debut_zoombox is not None or self.debut_selection is not None: self.debut_zoombox = None self.debut_selection = None # on rafraichit l'affichage pour effacer le rectangle de slection ou de zoom self.canvas.rafraichir_affichage(dessin_temporaire = True) # pour ne pas tout rafraichir def dialogue(self, titre, question, defaut = ""): u"""Certaines constructions ont besoin d'une valeur numrique (rotations, homothties...) Cette bote de dialogue permet l'utilisateur de saisir cette valeur. Retourne 'None' si l'utilisateur a annul.""" dlg = wx.TextEntryDialog(self, question, titre, defaut) if dlg.ShowModal() == wx.ID_OK: valeur = dlg.GetValue() else: valeur = None dlg.Destroy() return valeur def executer(self, instruction, editer = "defaut", init = True): u"""Excute une instruction dans la console. Si editer != None, le nom de l'objet est dit (par dfaut, seulement les points, droites et textes), afin de permettre de le renommer dans la foulee. Si init = True, le cache des selections est initialise.""" with contexte(unite_angle='r'): self.canvas.executer("_ = " + instruction) if init: self.initialiser() if editer == "defaut": if isinstance(self.feuille_actuelle.objets["_"], (Texte_generique, Point_generique, Droite_generique, Cercle_generique)): editer = self.feuille_actuelle.objets["_"] else: editer = None if editer: self.canvas.select = editer self.canvas.editer() def interagir(self, *args, **kw): u"Marque le bouton n comme slectionn (et initialise le cache des slections)." self.initialiser() self.canvas.interagir(*args, **kw) def test(self, doublons_interdits = True, **kw): u"""On vrifie qu'il y ait encore une feuille, et que le cache ne contienne que des objets de la feuille (la feuille a pu changer, ou un objet tre supprim).""" if kw.get("special", None) == "ESC": self.initialiser() return False else: self.canvas.editeur.close() ## objets_feuille = self.feuille_actuelle.objets() objets_feuille = self.feuille_actuelle.liste_objets(True) for obj in self.cache: if not is_in(obj, objets_feuille): self.cache.remove(obj) # Utiliser l'objet set pour faire l'intersection entre self.cache et self.feuille_actuelle.objets.__dict__.values() ?? if doublons_interdits and self.cache and kw.get("selection", None) is self.cache[-1]: # on vite de rentrer 2 fois de suite le mme objet dans le cache (vite de se retrouver avec qqch du style Segment(A,A) par ex.) # depuis la version 0.101.3, recliquer sur un objet le supprime du cache. self.cache.pop() return False return True def style(self, type_objet): u"Retourne le style d'objet dfini ventuellement dans les paramtres du module." print 'coucou', type_objet if not type_objet: return {} val = getattr(self.parent._param_, type_objet, {}) assert isinstance(val, dict) print val return val #---------------------------------------------------------- # Fonctions interactives de creation d'objets #---------------------------------------------------------- def curseur(self, event = None): u"Revenir en mode standard (flche simple)." self.interagir(None) def zoombox(self, event = False, **kw): u"Mode zoom." if event is not False: self.debut_zoombox = None self.interagir(self.zoombox, u"Cliquez pour dlimiter le dbut de la zone afficher.", self.zoombox_onmotion) elif not self.canvas.fixe: if kw.get("special", None) == "ESC": self.initialiser() self.canvas.message(u"Cliquez pour dlimiter le dbut de la zone afficher.") elif self.debut_zoombox is None: self.canvas.message(u"Cliquez pour dlimiter la fin de la zone afficher.") self.debut_zoombox = kw["position"] else: (x0, y0), (x1, y1) = self.debut_zoombox, self.canvas.fin_zoom self.canvas.executer("fenetre = " + str((x0, x1, y0, y1))) self.canvas.message(u"Cliquez pour dlimiter le dbut de la zone afficher.") self.debut_zoombox = None def zoombox_onmotion(self, **kw): if self.debut_zoombox is not None: self.canvas.debut_zoom = self.debut_zoombox self.canvas.gestion_zoombox(kw["pixel"]) self.canvas.debut_zoom = None def selectionner(self, event = False, **kw): u"Slectionner une zone." if event is not False: self.debut_selection = None self.interagir(self.selectionner, u"Cliquez pour dlimiter le dbut de la zone slectionner.", self.selectionner_onmotion) else: if kw.get("special", None) == "ESC": self.initialiser() self.canvas.message(u"Cliquez pour dlimiter le dbut de la zone slectionner.") elif self.debut_selection is None: self.canvas.message(u"Cliquez pour dlimiter la fin de la zone slectionner.") self.debut_selection = kw["position"] else: (x0, y0), (x1, y1) = self.debut_selection, self.canvas.fin_select self.canvas.OnSelect(x0, x1, y0, y1) self.canvas.message(u"Cliquez pour dlimiter le dbut de la zone slectionner.") self.debut_selection = None def selectionner_onmotion(self, **kw): if self.debut_selection is not None: self.canvas.debut_select = self.debut_selection self.canvas.selection_zone(kw["pixel"]) self.canvas.debut_select = None def point(self, event = False, nom_style='points', editer='defaut', **kw): if event is not False: self.interagir(self.point, u"Cliquez sur un objet, ou dans l'espace vierge.") else: if kw.get("special", None) == "ESC": self.initialiser() else: selection = kw["selection"] position = kw["position"] if not selection: self.executer(u"Point(*%s, **%s)" % (position, self.style(nom_style)), editer=editer, init = False) else: snom = selection.nom if isinstance(selection, Cercle_generique): self.executer(u"Glisseur_cercle(%s, %s)" %(snom, position), editer=editer, init = False) elif isinstance(selection, Segment): self.executer(u"Glisseur_segment(%s, %s)" %(snom, position), editer=editer, init = False) elif isinstance(selection, Droite_generique): self.executer(u"Glisseur_droite(%s, %s)" %(snom, position), editer=editer, init = False) elif isinstance(selection, Demidroite): self.executer(u"Glisseur_demidroite(%s, %s)" %(snom, position), editer=editer, init = False) elif isinstance(selection, Arc_generique): self.executer(u"Glisseur_arc_cercle(%s, %s)" %(snom, position), editer=editer, init = False) else: self.executer(u"Point(%s, %s)" % position, editer=editer, init = False) # On retourne le nom de l'objet cr # ('_' fait rfrence au dernier objet cr de la feuille), # afin de pouvoir utiliser cette mthode 'point()' comme routine ailleurs. return self.feuille_actuelle.objets["_"] def milieu(self, event = False, **kw): if event is not False: self.interagir(self.milieu, u"Choisissez deux points, ou un segment, un cercle, un polygone...") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) elif isinstance(selection, Polygone_generique): self.executer(u"Centre_gravite(%s)" %(selection.nom)) elif isinstance(selection, Cercle_generique) and not self.cache: self.executer(u"Centre(%s)" %(selection.nom)) elif isinstance(selection, Segment) and not self.cache: self.executer(u"Milieu(%s.point1, %s.point2)" %(selection.nom, selection.nom)) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: self.feuille_actuelle.objet_temporaire(Milieu(self.cache[0], self.feuille_actuelle.point_temporaire())) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Milieu(%s, %s)" %(self.cache[0].nom, self.cache[1].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() elif self.cache: self.feuille_actuelle.objet_temporaire(Milieu(self.cache[0], self.feuille_actuelle.point_temporaire())) else: self.feuille_actuelle.objet_temporaire(None) # NB: event doit recevoir par defaut False et non None, # car event vaut dj None par dfaut dans MultiButton.action(). def segment(self, event=False, **kw): if event is False: self.npoints(Segment, nom_style='segments', **kw) else: self.interagir(self.segment, u"Choisissez ou cr\xe9ez deux points.") def vecteur(self, event=False, **kw): if event is False: self.npoints(Vecteur, nom_style='vecteurs', **kw) else: self.interagir(self.vecteur, u"Choisissez ou cr\xe9ez deux points.") def droite(self, event = False, **kw): if event is False: self.npoints(Droite, **kw) else: self.interagir(self.droite, u"Choisissez ou cr\xe9ez deux points.") def demidroite(self, event = False, **kw): if event is False: self.npoints(Demidroite, **kw) else: self.interagir(self.demidroite, u"Choisissez ou cr\xe9ez deux points.") def mediatrice(self, event = False, **kw): if event is not False: self.interagir(self.mediatrice, u"Choisissez un segment ou deux points.") elif self.test(**kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) if len(self.cache) == 1: self.feuille_actuelle.objet_temporaire(Mediatrice(selection, self.feuille_actuelle.point_temporaire())) elif isinstance(selection, Segment): self.executer("Mediatrice(%s.point1, %s.point2)" %(selection.nom, selection.nom)) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: self.feuille_actuelle.objet_temporaire(Mediatrice(self.cache[0], self.feuille_actuelle.point_temporaire())) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer("Mediatrice(%s, %s)" %(self.cache[0].nom, self.cache[1].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def bissectrice(self, event = False, **kw): # A REVOIR if event is not False: self.interagir(self.bissectrice, u"Choisissez un angle ou trois points.") elif self.test(**kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(Bissectrice(self.cache[0], self.cache[1], self.feuille_actuelle.point_temporaire())) elif isinstance(selection, Secteur_angulaire): self.executer("Bissectrice(%s.point1, %s.point2, %s.point3)" %(selection.nom, selection.nom, selection.nom)) else: self.cache.append(self.point(**kw)) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(Bissectrice(self.cache[0], self.cache[1], self.feuille_actuelle.point_temporaire())) if len(self.cache) == 3: self.feuille_actuelle.objet_temporaire(None) self.executer("Bissectrice(%s, %s, %s)" %(self.cache[0].nom, self.cache[1].nom, self.cache[2].nom)) if len(self.cache) > 3: # ne se produit que si l'execution a plante... self.initialiser() def perpendiculaire(self, event = False, **kw): if event is not False: self.interagir(self.perpendiculaire, u"Choisissez ou crez un point et une droite.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Point_generique): self.cache.append(selection) elif isinstance(selection, Ligne_generique): self.cache.append(selection) self.feuille_actuelle.objet_temporaire(Perpendiculaire(selection, self.feuille_actuelle.point_temporaire())) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(self.cache[0], Ligne_generique): if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif isinstance(selection, Ligne_generique): self.cache.append(selection) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) if isinstance(self.cache[0], Point_generique): self.cache.reverse() self.executer("Perpendiculaire(%s, %s)" %(self.cache[0].nom, self.cache[1].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def parallele(self, event = False, **kw): if event is not False: self.interagir(self.parallele, u"Choisissez ou crez un point et une droite.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Point_generique): self.cache.append(selection) elif isinstance(selection, Ligne_generique): self.cache.append(selection) self.feuille_actuelle.objet_temporaire(Parallele(selection, self.feuille_actuelle.point_temporaire())) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(self.cache[0], Ligne_generique): if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif isinstance(selection, Ligne_generique): self.cache.append(selection) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) if isinstance(self.cache[0], Point_generique): self.cache.reverse() self.executer(u"Parallele(%s, %s)" %(self.cache[0].nom, self.cache[1].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def demiplan(self, event = False, **kw): if event is not False: self.interagir(self.demiplan, u"Choisissez ou crez un point et une droite.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Point_generique): self.cache.append(selection) elif isinstance(selection, Ligne_generique): self.cache.append(selection) self.feuille_actuelle.objet_temporaire(DemiPlan(selection, self.feuille_actuelle.point_temporaire())) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(self.cache[0], Ligne_generique): if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif isinstance(selection, Ligne_generique): self.cache.append(selection) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) if isinstance(self.cache[0], Point_generique): self.cache.reverse() self.executer(u"DemiPlan(%s, %s)" %(self.cache[0].nom, self.cache[1].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def tangente(self, event = False, **kw): if event is not False: self.interagir(self.tangente, u"Choisissez ou cr\u00e9ez un point et un cercle.") elif self.test(**kw): selection = kw["selection"] position = kw["position"] if len(self.cache) == 0: if isinstance(selection, Point_generique): self.cache.append(selection) self.feuille_actuelle.objet_temporaire(Droite(selection, self.feuille_actuelle.point_temporaire())) elif isinstance(selection, Cercle_generique): self.memoire_position = position self.cache.append(selection) self.feuille_actuelle.objet_temporaire(Tangente(selection, self.feuille_actuelle.point_temporaire())) else: self.cache.append(self.point(**kw)) self.feuille_actuelle.objet_temporaire(Droite(self.cache[0], self.feuille_actuelle.point_temporaire())) elif len(self.cache) == 1: if isinstance(self.cache[0], Cercle_generique): if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif isinstance(selection, Cercle_generique): self.cache.append(selection) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) if isinstance(self.cache[0], Point_generique): self.cache.reverse() else: position = self.memoire_position x1, y1 = self.cache[0].centre # on choisit la tangente qui passe le plus prs du point du cercle slectionn x, y = position x2, y2 = self.cache[1] det = (x1 - x)*(y2 - y) - (x2 - x)*(y1 - y) self.executer(u"Tangente(%s, %s, %s)" %(self.cache[0].nom, self.cache[1].nom, det>0)) if len(self.cache) > 2: # ne se produit que si l'excution a plante... self.initialiser() def representant(self, event = False, **kw): if event is not False: self.interagir(self.representant, u"Choisissez ou crez un point et un vecteur.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Point_generique): self.cache.append(selection) elif isinstance(selection, Vecteur): self.cache.append(selection) M = self.feuille_actuelle.point_temporaire() self.feuille_actuelle.objet_temporaire(Vecteur(M, Translation(selection)(M))) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(self.cache[0], Vecteur): if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif isinstance(selection, Vecteur): self.cache.append(selection) if len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) if isinstance(self.cache[0], Point_generique): self.cache.reverse() self.executer(u"Representant(%s, %s)" %(self.cache[0].nom, self.cache[1].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def translation(self, event = False, **kw): if event is not False: self.interagir(self.translation, u"Choisissez ou crez un objet, puis indiquez le vecteur de la translation.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Objet): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(selection, Vecteur_generique): self.cache.append(selection) if len(self.cache) == 2: self.executer(u"Translation(%s)(%s)" %(self.cache[1].nom, self.cache[0].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def symetrie(self, event = False, **kw): if event is not False: self.interagir(self.symetrie, u"Choisissez ou crez un objet, puis indiquez ou crez le centre de symtrie.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Objet): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 2: self.executer(u"Symetrie_centrale(%s)(%s)" %(self.cache[1].nom, self.cache[0].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def reflexion(self, event = False, **kw): if event is not False: self.interagir(self.reflexion, u"Choisissez ou crez un objet, puis indiquez l'axe de la rflexion.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Objet): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(selection, Ligne_generique): self.cache.append(selection) if len(self.cache) == 2: self.executer(u"Reflexion(%s)(%s)" %(self.cache[1].nom, self.cache[0].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def rotation(self, event = False, **kw): if event is not False: self.interagir(self.rotation, u"Choisissez ou crez un objet, puis indiquez le centre de la rotation, et l'angle.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Objet): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(selection, (Point_generique, Angle_generique)): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if not isinstance(selection, Angle_generique): angle = self.dialogue(u"Angle", u"Indiquez l'angle de la rotation.", u"45") if angle is None: self.initialiser() else: self.executer(u"Rotation(%s,%s)(%s)" %(self.cache[1].nom, repr(angle), self.cache[0].nom)) elif len(self.cache) == 2: if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) self.executer(u"Rotation(%s,%s)(%s)" %(self.cache[2].nom, self.cache[1].nom, self.cache[0].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def homothetie(self, event = False, **kw): if event is not False: self.interagir(self.homothetie, u"Choisissez ou crez un objet, puis indiquez le centre de l'homothtie, et son rapport.") elif self.test(**kw): selection = kw["selection"] if len(self.cache) == 0: if isinstance(selection, Objet): self.cache.append(selection) else: self.cache.append(self.point(**kw)) elif len(self.cache) == 1: if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) k = self.dialogue(u"Rapport", u"Indiquez le rapport de l'homothtie.", u"2") if k is None: self.initialiser() else: self.executer(u"Homothetie(%s,%s)(%s)" %(self.cache[1].nom, repr(k), self.cache[0].nom)) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def cercle(self, event = False, **kw): if event is False: self.npoints(Cercle, **kw) else: self.interagir(self.cercle, u"Choisissez ou crez deux points.") def cercle_diametre(self, event = False, **kw): if event is False: self.npoints(Cercle_diametre, **kw) else: self.interagir(self.cercle_diametre, u"Choisissez ou crez deux points.") def cercle_points(self, event = False, **kw): if event is False: self.npoints(Cercle_points, 3, **kw) else: self.interagir(self.cercle_points, u"Choisissez ou crez 3 points.") def arc(self, event = False, **kw): if event is False: self.npoints(Arc_cercle, 3, **kw) else: self.interagir(self.arc, u"Choisissez ou crez 3 points.") def demicercle(self, event = False, **kw): if event is False: self.npoints(Demicercle, **kw) else: self.interagir(self.demicercle, u"Choisissez ou crez deux points.") def arc_points(self, event = False, **kw): if event is False: self.npoints(Arc_points, 3, **kw) else: self.interagir(self.arc_points, u"Choisissez ou crez 3 points.") def arc_oriente(self, event = False, **kw): if event is False: self.npoints(Arc_oriente, 3, nom_style="arcs_orientes", **kw) else: self.interagir(self.arc_oriente, u"Choisissez ou crez 3 points.") def disque(self, event = False, **kw): if event is not False: self.interagir(self.disque, u"Choisissez un cercle.") elif self.test(**kw): selection = kw["selection"] if isinstance(selection, Cercle_generique): self.executer(u"Disque(%s)" %selection.nom) def angle(self, event = False, **kw): if event is False: self.npoints(Angle, 3, **kw) else: self.interagir(self.angle, u"Choisissez ou crez trois points.") def angle_oriente(self, event = False, **kw): if event is False: self.npoints(Angle_oriente, 3, **kw) else: self.interagir(self.angle_oriente, u"Choisissez ou crez trois points.") def triangle_rectangle(self, event = False, **kw): if event is not False: self.interagir(self.triangle_rectangle, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: A = self.cache[0] B = self.feuille_actuelle.point_temporaire() I = Milieu(A, B) C = Rotation(I, pi/3, unite='r')(B) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Triangle_rectangle(%s,%s, pi/6)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def triangle_isocele(self, event = False, **kw): if event is not False: self.interagir(self.triangle_isocele, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: A = self.cache[0] B = self.feuille_actuelle.point_temporaire() C = Rotation(A, pi/5, unite='r')(B) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Triangle_isocele(%s,%s, pi/5)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def triangle_isocele_rectangle(self, event = False, **kw): if event is not False: self.interagir(self.triangle_isocele_rectangle, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: A = self.cache[0] B = self.feuille_actuelle.point_temporaire() I = Milieu(A, B) C = Rotation(I, pi/2, unite='r')(B) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Triangle_isocele_rectangle(%s,%s)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def triangle_equilateral(self, event = False, **kw): if event is not False: self.interagir(self.triangle_equilateral, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: A = self.cache[0] B = self.feuille_actuelle.point_temporaire() C = Rotation(A, pi/3, unite='r')(B) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Triangle_equilateral(%s,%s)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def rectangle(self, event = False, **kw): if event is not False: self.interagir(self.rectangle, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: A = self.cache[0] B = self.feuille_actuelle.point_temporaire() C = Homothetie(B, 1.4)(Rotation(B, -pi/2, unite='r')(A)) D = Barycentre((A, 1), (B, -1), (C, 1)) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, D, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Rectangle(%s,%s,1.4)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def losange(self, event = False, **kw): if event is not False: self.interagir(self.losange, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: B = self.cache[0] C = self.feuille_actuelle.point_temporaire() A = Rotation(B, pi/5, unite='r')(C) D = Barycentre((A, 1), (B, -1), (C, 1)) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, D, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Losange(%s,%s,pi/5)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def carre(self, event = False, **kw): if event is not False: self.interagir(self.carre, u"Choisissez ou crez deux points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 1: A = self.cache[0] B = self.feuille_actuelle.point_temporaire() C = Rotation(B, -pi/2, unite='r')(A) D = Rotation(A, pi/2, unite='r')(B) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, D, A)) elif len(self.cache) == 2: self.feuille_actuelle.objet_temporaire(None) self.executer(u"Carre(%s,%s)" %(self.cache[0].nom, self.cache[1].nom)) else: self.feuille_actuelle.objet_temporaire(None) def triangle(self, event = False, **kw): if event is not False: self.interagir(self.triangle, u"Choisissez ou crez trois points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if 1 <= len(self.cache) <= 2: points = self.cache + [self.feuille_actuelle.point_temporaire()] self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(*points)) elif len(self.cache) == 3: self.executer(u"Triangle(" + ",".join(obj.nom for obj in self.cache) + ")") elif len(self.cache): points = self.cache + [self.feuille_actuelle.point_temporaire()] self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(*points)) else: self.feuille_actuelle.objet_temporaire(None) def parallelogramme(self, event = False, **kw): if event is not False: self.interagir(self.parallelogramme, u"Choisissez ou crez trois points.") elif self.test(True, **kw): selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) == 2: A, B = self.cache C = self.feuille_actuelle.point_temporaire() D = Barycentre((A, 1), (B, -1), (C, 1)) self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(A, B, C, D)) elif len(self.cache) == 3: self.executer(u"Parallelogramme(" + ",".join(obj.nom for obj in self.cache) + ")") def polygone(self, event = False, **kw): if event is not False: self.interagir(self.polygone, u"Indiquez les sommets, puis cliquez sur le 1er sommet.") elif self.test(True, **kw): #self.cache = [obj for obj in self.cache if obj.nom and obj.__feuille__ == self.feuille_actuelle] selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) else: self.cache.append(self.point(**kw)) if len(self.cache) >= 1: self.canvas.liste_objets_en_gras.append(self.cache[0]) self.canvas.selection_en_gras() ## self.cache[0].affiche_en_gras(True) if len(self.cache) > 2 and self.cache[0] is self.cache[-1]: # On cre le polygone self.feuille_actuelle.objet_temporaire(None) # cache = self.cache # self.initialiser() self.executer(u"Polygone(" + ",".join(obj.nom for obj in self.cache[:-1]) + ")") elif len(self.cache) >= 1: # Le polygone n'est pas encore complet tmp = self.feuille_actuelle.objet_temporaire() # liste des objets temporaires if tmp and isinstance(tmp[-1], PrevisualisationPolygone): # on ne recre pas la prvisualisation, on se contente d'ajouter un sommet tmp = tmp[-1] tmp.points = tmp.points[:-1] + (self.cache[-1], self.feuille_actuelle.point_temporaire()) else: points = self.cache + [self.feuille_actuelle.point_temporaire()] self.feuille_actuelle.objet_temporaire(PrevisualisationPolygone(*points)) elif self.cache: # Recliquer sur un objet le supprime du cache (cf. self.test()) tmp = self.feuille_actuelle.objet_temporaire()[-1] tmp.points = tmp.points[:-2] + (self.feuille_actuelle.point_temporaire(),) # on supprime un sommet else: self.feuille_actuelle.objet_temporaire(None) def intersection(self, event = False, **kw): def inter_dte_cer(dte, cer, position): u"Sert detecter l'intersection la plus proche du pointeur." intersections = Intersection_droite_cercle(dte, cer, True).intersections if len(intersections) == 2: # 2 points d'intersection -> on regarde le plus proche du pointeur xy0, xy1 = intersections xy0 = self.canvas.coo2pix(*xy0) xy1 = self.canvas.coo2pix(*xy1) position = self.canvas.coo2pix(*position) test = distance(position, xy0) < distance(position, xy1) else: test = True self.executer(u"Intersection_droite_cercle(%s, %s, %s)" %(dte.nom, cer.nom, test)) if event is not False: self.interagir(self.intersection, u"Indiquez deux objets, ou le lieu de l'intersection.") elif self.test(**kw): selection = kw["selection"] autres = kw["autres"] # autres objets a proximite position = kw["position"] if isinstance(selection, (Segment, Demidroite, Droite_generique, Cercle_generique, Arc_generique)): self.cache.append(selection) if len(self.cache) == 1: # permet de construire une intersection en 1 clic. objets = [obj for obj in autres if obj is not selection and isinstance(obj,(Segment, Demidroite, Droite_generique, Cercle_generique, Arc_generique))] if len(objets) == 1: # il n'y a pas d'ambiguite sur le 2eme objet. self.cache += objets if len(self.cache) == 2: obj1, obj2 = self.cache #self.initialiser() #inutile if isinstance(obj1, (Droite_generique, Segment, Demidroite)) and isinstance(obj2, (Droite_generique, Segment, Demidroite)): self.executer(u"Intersection_droites(%s, %s, legende=2)" %(obj1.nom, obj2.nom)) elif isinstance(obj1, (Cercle_generique, Arc_generique)) and isinstance(obj2, (Cercle_generique, Arc_generique)): M = Intersection_cercles(obj1, obj2) if len(M.intersections) == 2: xy0, xy1 = M.intersections xy0 = self.canvas.coo2pix(*xy0) xy1 = self.canvas.coo2pix(*xy1) position = self.canvas.coo2pix(*position) if distance(position, xy0) < distance(position, xy1): # M est bien l'intersection la plus proche angle = M.angle_positif else: angle = not M.angle_positif else: angle = M.angle_positif self.executer(u"Intersection_cercles(%s, %s, %s)" %(obj1.nom, obj2.nom, angle)) elif isinstance(obj1, (Droite_generique, Segment, Demidroite)) and isinstance(obj2, (Cercle_generique, Arc_generique)): inter_dte_cer(obj1, obj2, position) else: inter_dte_cer(obj2, obj1, position) if len(self.cache) > 2: # ne se produit que si l'execution a plante... self.initialiser() def texte(self, event = False, **kw): if event is not False: self.interagir(self.texte, u"Cliquez l'emplacement souhait.") elif self.test(**kw): position = kw["position"] self.executer(u"Texte('', %s, %s)" % position, init = False) def masque(self, event = False, **kw): if event is False: selection = kw["selection"] if selection is not None: self.canvas.executer("%s.cacher()" %selection.nom) else: self.interagir(self.masque) def gomme(self, event = False, **kw): if event is False: selection = kw["selection"] if selection is not None: self.canvas.executer("%s.supprimer()" %selection.nom) else: self.interagir(self.gomme) def pinceau(self, event = False, **kw): if event is not False: self.interagir(self.pinceau, u"Slectionnez un objet pour en copier le style.") elif self.test(**kw): selection = kw["selection"] if selection is not None: if len(self.cache) == 0: self.cache.append(selection) else: self.canvas.executer("%s.copier_style(%s)" %(selection.nom, self.cache[0].nom)) if len(self.cache): self.canvas.liste_objets_en_gras.append(self.cache[0]) self.canvas.selection_en_gras() def npoints(self, classe, n=2, nom_style='', **kw): u"Cration d'un objet de classe 'classe' ayant 'n' points comme arguments. Le nom de l'objet sera compos de 'prefixe' + 1 numro." if self.test(True, **kw): self.cache = [obj for obj in self.cache if obj.nom and obj.__feuille__ is self.feuille_actuelle] selection = kw["selection"] if isinstance(selection, Point_generique): self.cache.append(selection) nouveau_point = False else: self.cache.append(self.point(**kw)) nouveau_point = True if len(self.cache) == n - 1: style = self.style(nom_style) style["previsualisation"] = True self.feuille_actuelle.objet_temporaire(classe(*(tuple(self.cache) + (self.feuille_actuelle.point_temporaire(),)), **style)) elif len(self.cache) == n: self.feuille_actuelle.objet_temporaire(None) code = classe.__name__ + "(" + ",".join(obj.nom for obj in self.cache) + ", **%s)" %self.style(nom_style) if nouveau_point: # on edite le nom du nouveau point (dernier parametre de self.executer) self.executer(code, editer = self.cache[-1]) else: # si c'est un vieux point, pas besoin d'editer son nom self.executer(code) elif len(self.cache) > n: # ne se produit que si l'execution a plante... self.initialiser() elif self.cache: style = self.style(nom_style) style["previsualisation"] = True self.feuille_actuelle.objet_temporaire(classe(*(tuple(self.cache) + (self.feuille_actuelle.point_temporaire(),)), **style)) else: self.feuille_actuelle.objet_temporaire(None) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/animer.py0000644000175000017500000001204212014170666022166 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Suites # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from operator import attrgetter from .wxlib import MyMiniFrame from ..geolib.variables import Variable class DialogueAnimation(MyMiniFrame): def __init__(self, parent): MyMiniFrame.__init__(self, parent, u"Crer une animation") self.SetExtraStyle(wx.WS_EX_BLOCK_EVENTS ) self.parent = parent self.feuille_actuelle = self.parent.onglet_actuel.feuille_actuelle p = self.panel = wx.Panel(self, -1) self.sizer = sizer = wx.BoxSizer(wx.VERTICAL) terme = wx.BoxSizer(wx.HORIZONTAL) terme.Add(wx.StaticText(p, -1, u"Variable :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.var = wx.TextCtrl(p, -1, "", size=(100, -1)) terme.Add(self.var, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.var.Bind(wx.EVT_MIDDLE_DOWN, self.Propositions) sizer.Add(terme) sizer.Add(wx.StaticLine(p, -1, style=wx.LI_HORIZONTAL), 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) terme = wx.BoxSizer(wx.HORIZONTAL) terme.Add(wx.StaticText(p, -1, u"Dbut :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.deb = wx.TextCtrl(p, -1, "0", size=(50, -1)) terme.Add(self.deb, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) terme.Add(wx.StaticText(p, -1, u"Fin :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.fin = wx.TextCtrl(p, -1, "1", size=(50, -1)) terme.Add(self.fin, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) terme.Add(wx.StaticText(p, -1, u"Pas :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.pas = wx.TextCtrl(p, -1, "0.05", size=(50, -1)) terme.Add(self.pas, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) sizer.Add(terme) terme = wx.BoxSizer(wx.HORIZONTAL) terme.Add(wx.StaticText(p, -1, u"Priode (s) :"), 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) self.periode = wx.TextCtrl(p, -1, "0.1", size=(100, -1)) terme.Add(self.periode, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL,5) sizer.Add(terme) sizer.Add(wx.StaticLine(p, -1, style=wx.LI_HORIZONTAL), 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) boutons = wx.BoxSizer(wx.HORIZONTAL) self.btn_lancer = lancer = wx.Button(p, -1, u"Animer") boutons.Add(lancer, 0, wx.ALL, 5) fermer = wx.Button(p, -1, u"Fermer") boutons.Add(fermer, 0, wx.ALL, 5) self.Bind(wx.EVT_BUTTON, self.Animer, lancer) self.Bind(wx.EVT_BUTTON, self.OnCloseMe, fermer) sizer.Add(boutons) p.SetSizerAndFit(sizer) self.SetClientSize(p.GetSize()) self.en_cours = False def Animer(self, event = None): if self.en_cours: self.feuille_actuelle.stop() else: self.en_cours = True self.btn_lancer.SetLabel('Stop') self.feuille_actuelle.animer(nom = self.var.GetValue(), debut=eval(self.deb.GetValue()), fin=eval(self.fin.GetValue()), pas=eval(self.pas.GetValue()), periode=eval(self.periode.GetValue())) self.en_cours = False self.btn_lancer.SetLabel('Animer') def Propositions(self, event = None): u"Liste des noms de variables de la feuille actuelle." self.var.SetFocus() liste_objets = self.feuille_actuelle.objets.lister(False, type = Variable) liste_objets.sort(key=attrgetter('nom')) # ordre alphabtique if not liste_objets: return ids = [wx.NewId() for obj in liste_objets] menu = wx.Menu() for i in xrange(len(liste_objets)): menu.Append(ids[i], liste_objets[i].nom_complet) def select(event, nom = liste_objets[i].nom, champ = self.var): champ.SetValue(nom) menu.Bind(wx.EVT_MENU, select, id = ids[i]) self.PopupMenu(menu) menu.Destroy() def OnCloseMe(self, event): self.Close(True) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/inspecteur.py0000644000175000017500000000563212014170666023103 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Fenetres # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .pythonSTC import PythonSTC from wxlib import MyMiniFrame import wx #################################################################################################### # Code de la feuille actuelle class FenCode(MyMiniFrame): def __init__(self, parent, titre, contenu, fonction_modif): MyMiniFrame.__init__(self, parent, titre) self.parent = parent self.fonction_modif = fonction_modif ## self.texte = wx.TextCtrl(self, -1, contenu, size=(300, 10), style=wx.TE_MULTILINE) self.texte = PythonSTC(self, -1, size=(300, 10)) self.texte.AddText(contenu) self.texte.Bind(wx.EVT_CHAR, self.EvtChar) ## self.texte.SetInsertionPointEnd() self.texte.SetFocus() tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT | wx.TB_TEXT ) self.a = wx.Button(tb, -1, u"Modifier - F5") tb.AddControl(self.a) tb.AddSeparator() tb.AddSeparator() self.b = wx.Button(tb, -1, u"Annuler - ESC") tb.AddControl(self.b) tb.Realize() self.Bind(wx.EVT_BUTTON, self.executer, self.a) self.Bind(wx.EVT_BUTTON, self.fermer, self.b) self.SetSize((400, 500)) self.CenterOnParent(wx.BOTH) def EvtChar(self, event): key = event.GetKeyCode() if key == wx.WXK_ESCAPE: self.fermer() elif key == wx.WXK_F5: self.executer() else : event.Skip() def fermer(self, event = None): self.Close(True) def executer(self, event = None): # On excute le code (de la feuille par ex.) ventuellement modifi ## self.fonction_modif(self.texte.GetValue()) self.fonction_modif(self.texte.GetText()) self.fermer() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/pythonSTC.py0000644000175000017500000001337412014170666022617 0ustar georgeskgeorgesk import keyword import wx import wx.stc as stc class PythonSTC(stc.StyledTextCtrl): if wx.Platform == '__WXMSW__': faces = { 'times': 'Times New Roman', 'mono' : 'Courier New', 'helv' : 'Verdana', 'other': 'Comic Sans MS', 'size' : 10, 'size2': 8, } elif wx.Platform == '__WXMAC__': faces = { 'times': 'Times New Roman', 'mono' : 'Monaco', 'helv' : 'Arial', 'other': 'Comic Sans MS', 'size' : 12, 'size2': 10, } else: faces = { 'times': 'Times', 'mono' : 'Courier', 'helv' : '!Bitstream Vera Sans', 'other': 'new century schoolbook', 'size' : 10, 'size2': 8, } fold_symbols = 2 def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style) self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) self.SetLexer(stc.STC_LEX_PYTHON) self.SetKeyWords(0, " ".join(keyword.kwlist)) self.SetProperty("tab.timmy.whinge.level", "1") self.SetViewWhiteSpace(False) self.SetBufferedDraw(False) self.SetUseAntiAliasing(True) self.SetEdgeMode(stc.STC_EDGE_BACKGROUND) self.SetEdgeColumn(78) self.SetMarginMask(2, stc.STC_MASK_FOLDERS) self.SetMarginWidth(2, 12) self.SetIndent(4) self.SetBackSpaceUnIndents(True) self.SetTabWidth(4) self.SetUseTabs(False) self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI) # Make some styles, The lexer defines what each style is used for, we # just have to define what each style looks like. This set is adapted from # Scintilla sample property files. # Global default styles for all languages self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % self.faces) self.StyleClearAll() # Reset all to be like the default # Global default styles for all languages self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % self.faces) self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % self.faces) self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % self.faces) self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold") self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold") # Python styles # Default self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % self.faces) # Comments self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % self.faces) # Number self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % self.faces) # String self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % self.faces) # Single quoted string self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % self.faces) # Keyword self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % self.faces) # Triple quotes self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % self.faces) # Triple double quotes self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % self.faces) # Class name definition self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % self.faces) # Function or method name definition self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % self.faces) # Operators self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % self.faces) # Identifiers self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % self.faces) # Comment-blocks self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % self.faces) # End of line where string is not closed self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % self.faces) self.SetCaretForeground("BLUE") # self.SetCaretLineVisible(True) self.SetCaretLineBack(wx.Colour(200, 220, 220,255)) self.SetCaretLineBackAlpha(200) def OnUpdateUI(self, evt): # check for matching braces braceAtCaret = -1 braceOpposite = -1 charBefore = None caretPos = self.GetCurrentPos() if caretPos > 0: charBefore = self.GetCharAt(caretPos - 1) styleBefore = self.GetStyleAt(caretPos - 1) # check before if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR: braceAtCaret = caretPos - 1 # check after if braceAtCaret < 0: charAfter = self.GetCharAt(caretPos) styleAfter = self.GetStyleAt(caretPos) if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR: braceAtCaret = caretPos if braceAtCaret >= 0: braceOpposite = self.BraceMatch(braceAtCaret) if braceAtCaret != -1 and braceOpposite == -1: self.BraceBadLight(braceAtCaret) else: self.BraceHighlight(braceAtCaret, braceOpposite) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/onglets.py0000644000175000017500000005014712014170666022376 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------## # Onglets # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import wx import matplotlib.backend_bases as backend_bases from .inspecteur import FenCode from .aide import Help, About, Informations from .dialogues_geometrie import EditerObjet, SupprimerObjet from .nouvelles_versions import Gestionnaire_mises_a_jour from . import dialogues_geometrie from ..API.sauvegarde import FichierGEO, ouvrir_fichierGEO from .proprietes_objets import Proprietes from .. import param, modules, geolib from ..pylib import print_error, debug, path2 from .animer import DialogueAnimation from .proprietes_feuille import ProprietesFeuille from ..param.options import options as param_options from .fenetre_options import FenetreOptions from .contact import Contact class Onglets(wx.Notebook): def __init__(self, parent): self.parent = parent wx.Notebook.__init__(self, parent) self.SetBackgroundColour(wx.WHITE) self._liste = [] # liste des onglets def terminer_initialisation(self): ############################### # Creation de fonctions associees aux entrees du menu "Creer" self.creer = {} DG = dialogues_geometrie.__dict__ dialogues = [(nom[8:], DG[nom]) for nom in DG.keys() if nom.startswith("Dialogue")] for dialogue in dialogues: def f(event = None, self = self, dialogue = dialogue[1]): self.creer_objet(dialogue) self.creer[dialogue[0]] = f ############################### self.gestionnaire_de_mises_a_jour = Gestionnaire_mises_a_jour(self) # Ajoute les differentes composantes : self.actualiser_liste_onglets() # adaptation du titre de l'application et du menu. self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.changer, self) import fenetre_options self.Bind(fenetre_options.EVT_OPTIONS_MODIFIED, self.OnOptionsModified) if self._liste: # affiche le titre et le menu du 1er onglet self.actualise_onglet(self._liste[0]) self._liste[0].activer() def OnOptionsModified(self, evt = None): self.actualiser_liste_onglets() for parametre in ('decimales', 'unite_angle', 'tolerance'): geolib.contexte[parametre] = getattr(param, parametre) def deplacer_onglet(self, i, j): u"Dplacer un onglet de la position 'i' la position 'j'." if i != j: panel = self._liste.pop(i) self._liste.insert(j, panel) self.RemovePage(i) self.InsertPage(j, panel, panel.__titre__) def nouvel_onglet(self, panel, i = None): u"Ajouter un nouvel onglet la position 'i'." if i is None: self._liste.append(panel) self.AddPage(panel, panel.__titre__) else: self._liste.insert(i, panel) self.InsertPage(i, panel, panel.__titre__) setattr(self, panel.module._nom_, panel) def fermer_onglet(self, i): u"Fermer l'onglet situ en position 'i'." panel = self._liste.pop(i) delattr(self, panel.module._nom_) self.DeletePage(i) def changer(self, event): event.Skip() # onglet selectionn: onglet = self._liste[event.GetSelection()] self.actualise_onglet(onglet) # Actions personnalises lors de la slection wx.CallLater(10, onglet.activer) def actualise_onglet(self, onglet): self.parent.SetMenuBar(onglet.menu) # change le menu de la fenetre onglet.changer_titre() # change le titre de la fenetre if param.plateforme == "Windows": if onglet.canvas is not None: onglet.canvas.execute_on_idle(onglet.canvas.graph.restaurer_dessin) def onglet(self, nom): u"nom : nom ou numro de l'onglet." if type(nom) == int: return self._liste[nom] return getattr(self, nom, None) @property def onglet_actuel(self): if self._liste: return self._liste[self.GetSelection()] def onglet_suivant(self, event): self.AdvanceSelection() def __nonzero__(self): return bool(self._liste) def __iter__(self): return iter(self._liste) def changer_onglet(self, onglet): u"onglet : l'onglet proprement dit, ou son nom, ou son numro." if type(onglet) in (str, unicode): onglet = self._liste.index(getattr(self, onglet.lower())) elif type(onglet) not in (int, long): onglet = self._liste.index(onglet) self.SetSelection(onglet) def actualiser_liste_onglets(self, evt = None): # Tous les onglets situs avant 'pos' sont classs pos = 0 for nom in param.modules: module = modules.importer_module(nom) if module is not None: absent = True for i, panel in enumerate(self._liste[pos:]): if panel.module is module: # Dplacer le panel en position pos self.deplacer_onglet(pos + i, pos) # InsertPage(self, n, page, text, select, imageId) # RemovePage(self, n) # Mettre jour la position pos += 1 absent = False if absent: # Crer un onglet en position pos self.nouvel_onglet(module._panel_(self, module), pos) # AddPage(self, page, text, select, imageId) uhk # Mettre jour la position pos += 1 # Supprimer tous les onglets qui sont situs aprs pos while pos < self.GetPageCount(): self.fermer_onglet(pos) ##################################################################### def NewFile(self, event = None): self.onglet_actuel.creer_feuille() def SaveFile(self, event = None): actuelle = self.onglet_actuel.feuille_actuelle # feuille de travail courante if actuelle and actuelle.sauvegarde["nom"]: self.onglet_actuel.sauvegarder() else: self.SaveFileAs(event) def SaveFileAs(self, event = None): actuelle = self.onglet_actuel.feuille_actuelle # feuille de travail courante if actuelle and actuelle.sauvegarde["nom"]: fichier = actuelle.sauvegarde["nom"] # le nom par defaut est le nom prcdent dir = actuelle.sauvegarde["repertoire"] else: fichier = "" if param.rep_save is None: dir = param.repertoire else: dir = param.rep_save if param.compresser_geo: wildcard = u"Fichiers WxGomtrie compresss(*.geoz)|*.geoz|Fichiers WxGomtrie (*.geo)|*.geo|Tous les fichiers (*.*)|*.*" else: wildcard = u"Fichiers WxGomtrie (*.geo)|*.geo|Fichiers WxGomtrie compresss(*.geoz)|*.geoz|Tous les fichiers (*.*)|*.*" dlg = wx.FileDialog( self, message = u"Enregistrer sous ...", defaultDir=dir, defaultFile=fichier, wildcard = wildcard, style=wx.SAVE | wx.OVERWRITE_PROMPT | wx.CHANGE_DIR ) # dlg.SetFilterIndex(0) # inutile (par defaut, la valeur est 0). if dlg.ShowModal() == wx.ID_OK: param.rep_save = os.getcwd() if param.rep_open is None: param.rep_open = param.rep_save if param.rep_export is None: param.rep_export = param.rep_save os.chdir(param.repertoire) path = dlg.GetPath() self.onglet_actuel.sauvegarder(path) dlg.Destroy() def OpenFile(self, event = None, detecter_module = True): # Attention : # "This dialog is set up to change the current working directory to the path chosen." # on enregistre donc le nouveau getcwd() - "repertoire_ouverture_fichiers = os.getcwd()", pour restaurer ensuite l'ancien if param.rep_open is None: dir = param.repertoire else: dir = param.rep_open dlg = wx.FileDialog( self, message = u"Choisissez un fichier", defaultDir=dir, defaultFile="", wildcard = u"Fichiers WxGomtrie (*.geo)|*.geo|Fichiers WxGomtrie compresss(*.geoz)|*.geoz|Tous les fichiers (*.*)|*.*", style=wx.OPEN | wx.MULTIPLE | wx.CHANGE_DIR ) # Show the dialog and retrieve the user response. If it is the OK response, # process the data. if dlg.ShowModal() == wx.ID_OK: param.rep_open = os.getcwd() if param.rep_save is None: param.rep_save = param.rep_open if param.rep_export is None: param.rep_export = param.rep_open os.chdir(param.repertoire) # This returns a Python list of files that were selected. paths = dlg.GetPaths() for path in paths: if detecter_module: # on detecte dans quel onglet le fichier doit etre ouvert self.ouvrir(path) else: # on l'ouvre dans l'onglet actuellement ouvert self.onglet_actuel.ouvrir(path) # ouvre tous les fichiers selectionnes dlg.Destroy() def OpenFileHere(self, event = None): u"""Ouvrir le fichier dans le module courant. Par dfaut, sinon, le fichier est ouvert dans le module qui l'a cre.""" self.OpenFile(detecter_module = False) def ouvrir(self, fichier, en_arriere_plan = False): u"""Ouvre un fichier dans l'onglet adquat. 'fichier' est soit l'adresse d'un fichier .geo, soit une instance de FichierGEO. """ if not isinstance(fichier, FichierGEO): fichier, message = ouvrir_fichierGEO(fichier) self.parent.message(message) module = self.onglet(fichier.module) if module is not None: if not en_arriere_plan: self.changer_onglet(module) # affiche cet onglet module.ouvrir(fichier) # charge le fichier dans le bon onglet #print_error() else: self.parent.message(u"Impossible de dterminer le module adquat.") def ExportFile(self, event = None, lieu = None, sauvegarde = False, exporter = True): u"""Le paramtre sauvegarde indique qu'il faut faire une sauvegarde simultane. (attention, on ne vrifie pas que le fichier .geo n'existe pas !). """ actuelle = self.onglet_actuel.feuille_actuelle # feuille de travail courante if actuelle and actuelle.sauvegarde["export"]: dir, fichier = os.path.split(actuelle.sauvegarde["export"]) # on exporte sous le mme nom qu'avant par dfaut elif actuelle and actuelle.sauvegarde["nom"]: fichier = actuelle.sauvegarde["nom"] # le nom par defaut est le nom de sauvegarde dir = actuelle.sauvegarde["repertoire"] else: fichier = "" if param.rep_export is None: dir = param.repertoire else: dir = param.rep_export lieu = lieu or self.onglet_actuel.nom # par defaut, le lieu est l'onglet courant description_formats = sorted(backend_bases.FigureCanvasBase.filetypes.iteritems()) formats_supportes = sorted(backend_bases.FigureCanvasBase.filetypes) format_par_defaut = '' if '.' in fichier: format_par_defaut = fichier.split('.', 1)[1] if format_par_defaut not in formats_supportes: format_par_defaut = 'png' def format(typ, description): return typ.upper() + ' - ' + description + ' (.' + typ + ')|*.' + typ filtre = '|'.join(format(k, v) for k, v in description_formats) dlg1 = wx.FileDialog( self, u"Exporter l'image", dir, fichier, filtre, wx.SAVE | wx.OVERWRITE_PROMPT | wx.CHANGE_DIR ) dlg1.SetFilterIndex(formats_supportes.index(format_par_defaut)) # print formats_supportes.index(format_par_defaut), format_par_defaut, fichier if "." in fichier: ext = fichier.rsplit(".",1)[1] try: dlg1.SetFilterIndex(formats_supportes.index(ext)) except ValueError: debug("L'extension n'est pas dans la liste.") try: while 1: if dlg1.ShowModal() == wx.ID_OK: param.rep_export = os.getcwd() if param.rep_save is None: param.rep_save = param.rep_export if param.rep_open is None: param.rep_open = param.rep_export if sauvegarde: param.rep_save = param.rep_export os.chdir(param.repertoire) FileName = dlg1.GetPath() filename = FileName.lower() # Check for proper exension if not any(filename.endswith(extension) for extension in formats_supportes): FileName += "." + formats_supportes[dlg1.GetFilterIndex()] filename = FileName.lower() #~ dlg2 = wx.MessageDialog(self, u"L'extension du fichier\n" #~ u'doit tre\n' #~ u'png, svg, ps, jpg, bmp, xpm ou eps', #~ u'Nom de fichier incorrect', wx.OK | wx.ICON_ERROR) #~ try: #~ dlg2.ShowModal() #~ finally: #~ dlg2.Destroy() #~ else: #~ break # now save file break else: # exit without saving return False finally: dlg1.Destroy() try: if sauvegarde: GeoFileName = '.'.join(FileName.split('.')[:-1]) + ('.geoz' if param.compresser_geo else '.geo') self.onglet(lieu).sauvegarder(GeoFileName) # Save Bitmap if exporter: self.onglet(lieu).exporter(FileName) except: print_error() self.parent.message("Erreur lors de l'export.") return FileName #return self.onglet(lieu).exporter(FileName) def ExportAndSaveFile(self, event = None, lieu = None): self.ExportFile(event = event, lieu = lieu, sauvegarde = True) def CloseFile(self, event = None): self.onglet_actuel.fermer_feuille() def PageSetup(self, event = None): self.a_venir() def Printout(self, event = None): self.a_venir() def a_venir(self, event = None): dlg = wx.MessageDialog(self, u"Fonctionnalit non prsente pour l'instant !", u"A venir !", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def CloseWindow(self, event): self.parent.Close(True) def supprimer(self, event = None): canvas = self.onglet_actuel.canvas dlg = SupprimerObjet(self) if (dlg.ShowModal() == wx.ID_OK): with canvas.geler_affichage(actualiser=True, sablier=True): for selection in dlg.GetValueString(): try: # Il est normal que des erreurs soient renvoyes # si un objet dpendant d'un autre est dj supprim. objet = selection.split()[1] canvas.feuille_actuelle.objets[objet].supprimer() except Exception: print_error() canvas.feuille_actuelle.interprete.commande_executee() dlg.Destroy() def editer(self, event = None): feuille = self.onglet_actuel.feuille_actuelle if feuille: objets = [] dlg = EditerObjet(self) val = dlg.ShowModal() if val == wx.ID_OK: objets = [feuille.objets[selection.split()[1]] for selection in dlg.GetValueString()] dlg.Destroy() if objets: win = Proprietes(self, objets) win.Show(True) def fermeture(event, win = win): win.Unbind(wx.EVT_CLOSE) win.Close() self.editer() win.Bind(wx.EVT_CLOSE, fermeture) #self.parent.Raise() def creer_objet(self, classe): dl = classe(self) # this does not return until the dialog is closed. while 1: canvas = self.onglet_actuel.canvas resultat = dl.affiche() if resultat == wx.ID_OK: try: canvas.executer(dl.commande(), parser = True) break except NameError: print_error() canvas.message(u'Erreur : nom dj utilis ou nom rserv.') except: print_error() canvas.message(u'Erreur : paramtres incorrects.') else: break dl.Destroy() def Animer(self, event): d = DialogueAnimation(self) d.CenterOnParent(wx.BOTH) d.Show(True) def Histo(self, event): contenu = self.onglet_actuel.feuille_actuelle.sauvegarder() h = FenCode(self, u"Contenu interne de la feuille", contenu, self.executer_dans_feuille_courante) h.Show(True) def executer_dans_feuille_courante(self, instructions): self.onglet_actuel.creer_feuille() self.onglet_actuel.feuille_actuelle.charger(instructions) def Proprietes(self, event): actuelle = self.onglet_actuel.feuille_actuelle # feuille courante ProprietesFeuille(self, actuelle).Show() def Options(self, evt = None): FenetreOptions(self, param_options).Show() def Aide(self, event): aide = Help(self, path2("%/doc/help.htm")) aide.Show(True) #~ def Verifier_version(self, event): #~ # from GUI.nouvelles_versions import verifier_version # deplacer ? #~ # verifier_version(self) #~ self.gestionnaire_de_mises_a_jour.verifier_version() def Notes(self, event): f = open(path2("%/doc/notes.txt"), "r") msg = f.read().decode("utf8") f.close() msg = msg.replace(u"WxGeometrie", u"WxGomtrie version " + param.version, 1) dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "Notes de version") dlg.ShowModal() def Licence(self, event): f = open(path2("%/doc/license.txt"), "r") msg = f.read().decode("utf8") f.close() msg = msg.replace(u"WxGeometrie", u"WxGomtrie version " + param.version, 1) dlg = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "Licence") dlg.ShowModal() def Contacter(self, event): formulaire = Contact(self) formulaire.Show() #~ val = dialog.ShowModal() #~ if val == wx.ID_OK: #~ dialog.rapporter() #~ dialog.Destroy() def About(self, event): dialog = About(self) dialog.ShowModal() dialog.Destroy() def Informations(self, event): dialog = Informations(self) dialog.ShowModal() dialog.Destroy() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/gestion_session.py0000644000175000017500000001166612014170666024141 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # gestionnaire de session # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os from threading import Thread from time import sleep from ..pylib import uu, print_error, path2, debug, warning from ..API.sauvegarde import FichierSession from ..API.parametres import sauvegarder_module from .. import param class GestionnaireSession(object): def __init__(self, onglets): self.onglets = onglets # Cration (si ncessaire) des rpertoires /log, /macro, etc., dfinis dans /param/__init__.py for repertoire in param.emplacements: repertoire = path2(repertoire) if not os.path.isdir(repertoire): try: os.makedirs(repertoire) except IOError: print_error() thread = Thread(target=self._autosave_timer) thread.daemon = True thread.start() def _autosave_timer(self): try: while True: if param.sauvegarde_automatique: self.__sauver_session = True sleep(max(10*param.sauvegarde_automatique, 2)) except AttributeError: print('Warning: closing thread...') # Si le programme est en train d'tre ferm, param peut ne # plus exister. def autosave(self): if self.__sauver_session: Thread(target=self.sauver_session, kwargs={'forcer': True}).start() self.__sauver_session = False def sauver_session(self, lieu=None, seulement_si_necessaire=True, forcer=False): if param.sauver_session or forcer: fichiers_ouverts = [] if seulement_si_necessaire and not any(onglet.modifie for onglet in self.onglets): return for onglet in self.onglets: fichiers_ouverts.extend(onglet._fichiers_ouverts()) if self.onglets.onglet_actuel is None: print("Warning: Aucun onglet ouvert ; impossible de sauver la session !") return session = FichierSession(*fichiers_ouverts, **{'onglet_actif': self.onglets.onglet_actuel.nom}) if lieu is None: lieu = path2(param.emplacements['session'] + "/session.tar.gz") for onglet in self.onglets: onglet.modifie = False session.ecrire(lieu, compresser = True) print(u"Session sauve : (%s)" %lieu) def charger_session(self, lieu=None, reinitialiser=True): if reinitialiser: self.reinitialiser_session() if lieu is None: lieu = path2(param.emplacements['session'] + "/session.tar.gz") session = FichierSession().ouvrir(lieu) for fichier in session: self.onglets.ouvrir(fichier, en_arriere_plan = True) try: self.onglets.changer_onglet(session.infos['onglet_actif']) except (IndexError, AttributeError): warning("Impossible de restaurer l'onglet actif (%s)." %session.infos['onglet_actif']) def reinitialiser_session(self): for onglet in self.onglets: onglet.reinitialiser() def sauver_preferences(self, forcer=True): if param.sauver_preferences or forcer: fgeo = sauvegarder_module(param) fgeo.ecrire(path2(param.emplacements['preferences'] + "/parametres.xml")) for onglet in self.onglets: try: onglet.sauver_preferences() except: debug(u"Fermeture incorrecte de l'onglet : ", uu(str(onglet))) raise else: # La prfrence 'sauver_preferences' doit tre sauve dans tous les cas, # sinon il ne serait jamais possible de dsactiver les prfrences depuis WxGomtrie ! fgeo = sauvegarder_module({'sauver_preferences': False}) fgeo.ecrire(path2(param.emplacements['preferences'] + "/parametres.xml")) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/panel.py0000644000175000017500000004631112014170666022020 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Panel # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os, time, thread import wx from .barre_outils import BarreOutils from .menu import RSSMenu from .console_geolib import ConsoleGeolib from .wxlib import BusyCursor from .wxcanvas import WxCanvas from .app import app from ..API.sauvegarde import ouvrir_fichierGEO, FichierGEO from ..API.parametres import sauvegarder_module from .. import param from ..pylib import debug, path2, print_error, property2, removeend, no_argument,\ eval_safe from ..pylib.rapport import Rapport from ..geolib.classeur import Classeur from ..geolib.feuille import Feuille class Panel_simple(wx.Panel): u"""Remplace la classe la classe wx.Panel pour les diffrents modules. Pour les modules ayant besoin des fonctions graphiques evolues de WxGomtrie, mieux vaut utiliser la classe Panel_API_graphique, plus evolue que celle-ci.""" feuille_actuelle = None # pas de feuille # Indique si des modifications ont eu lieu (et s'il faudra donc sauvegarder la session) modifie = False def __init__(self, parent, module, menu = True, style = wx.TAB_TRAVERSAL|wx.WANTS_CHARS): wx.Panel.__init__(self, parent, -1, style = style) self.module = module # wx.NamedColor ne peut pas tre appel avant que wx.App existe. self.SetBackgroundColour(wx.WHITE) self.parent = parent self.nom = self.__class__.__name__.lower() self.canvas = None path = path2(param.emplacements['log'] + "/" + self.nom + u"_historique.log") self.log = Rapport(path) # ._derniere_signature : sert pour les logs (en cas de zoom de souris essentiellement). # (cf. geolib/feuille.py pour plus de dtails.) self._derniere_signature = None if menu: # Cration de la barre de menus associe au panel self.menu = self.module._menu_(self) def message(self, texte = ''): self.parent.parent.message(texte) def changer_titre(self, texte = ''): self.parent.parent.titre(texte) def ouvrir(self, fichier): u"""Ouvre un un fichier .geo. 'fichier' est soit l'adresse d'un fichier .geo, soit une instance de FichierGEO. """ with BusyCursor(): if not isinstance(fichier, FichierGEO): fichier, message = ouvrir_fichierGEO(fichier) self.message(message) self._ouvrir(fichier) # instance de FichierGEO def sauvegarder(self, nom_fichier = 'sauvegarde', **kw): if nom_fichier: if nom_fichier.endswith(".geoz"): nom_fichier = removeend(nom_fichier.strip(), ".geo", ".geoz") + ".geoz" # le nom par defaut sera ainsi [nom_de_la_feuille].geo" else: nom_fichier = removeend(nom_fichier.strip(), ".geo", ".geoz") + ".geo" # le nom par defaut sera ainsi [nom_de_la_feuille].geo" try: fgeo = FichierGEO(module = self.nom) self._sauvegarder(fgeo, **kw) if nom_fichier is None: return fgeo fgeo.ecrire(nom_fichier, zip = nom_fichier.endswith(".geoz")) self.message(u"Sauvegarde effectue.") except Exception: self.message(u"Echec de la sauvegarde.") if param.debug: raise def _fichiers_ouverts(self): return [self.sauvegarder(None)] def _ouvrir(self, fgeo): u"""Ouverture d'un fichier. surclasser pour chaque module.""" def _sauvegarder(self, fgeo): u"""Sauvegarde dans un fichier. surclasser pour chaque module.""" def _changement_feuille(self): u"""Aprs tout changement de feuille. surclasser pour chaque module.""" def param(self, parametre, valeur = no_argument, defaut = False): u"""Recherche la valeur d'un paramtre d'abord dans les paramtres du module (paramtres locaux), puis dans ceux de wxgomtrie. Si defaut vaut True, la valeur par dfaut du paramtre est renvoye.""" if valeur is not no_argument: setattr(self._param_, parametre, valeur) if self._param_ is not None and hasattr(self._param_, parametre): if defaut: if self._param_._parametres_par_defaut.has_key(parametre): return self._param_._parametres_par_defaut[parametre] elif param._parametres_par_defaut.has_key(parametre): return param._parametres_par_defaut[parametre] return getattr(self._param_, parametre) elif hasattr(param, parametre): if defaut and param._parametres_par_defaut.has_key(parametre): return param._parametres_par_defaut[parametre] return getattr(param, parametre) else: debug(u"Module %s: Paramtre %s introuvable." %(self.__titre__, parametre)) def sauver_preferences(self, lieu = None): if self._param_ is not None: try: if lieu is None: lieu = path2(param.emplacements['preferences'] + "/" + self.nom + "/parametres.xml") fgeo = sauvegarder_module(self._param_, self.nom) fgeo.ecrire(lieu) except: self.message(u"Impossible de sauvegarder les prfrences.") print_error() def action_effectuee(self, log, signature = None): if self.log is not None: if signature is not None and signature == self._derniere_signature and self.log: self.log[-1] = ('ACTION EFFECTUEE: ' + log) else: self.log.append('ACTION EFFECTUEE: ' + log) self._derniere_signature = signature def activer(self, event = None): u"Actions effectuer lorsque l'onglet du module est slectionn. surclasser." pass def reinitialiser(self): u"""Rinitialise le module (ferme les travaux en cours, etc.). surclasser.""" pass @staticmethod def vers_presse_papier(texte): u"""Copie le texte dans le presse-papier. Retourne True si la copie a russi, False sinon.""" return app.vers_presse_papier(texte) class Panel_API_graphique(Panel_simple): u"""Pour les modules ayant besoin de TOUTE l'API de WxGomtrie (canvas, historique, gestion des commandes, etc...) et pas seulement des bibliothques, mieux vaut utiliser cette classe. Cela concerne essentiellement les modules qui ont besoin de tracer des objets gomtriques.""" def __init__(self, parent, module, BarreOutils = BarreOutils): extra = {'style': wx.WANTS_CHARS} if param.plateforme == "Windows" else {} Panel_simple.__init__(self, parent, module, menu=False, **extra) # IMPORTANT: contruire toujours dans cet ordre. self.feuilles = Classeur(self, log = self.log) # En particulier, l'initialisation du canvas ncessite qu'il y ait dj une feuille ouverte. self.canvas = WxCanvas(self) # La construction du menu ncessite que self.canvas et self.log # soient dfinis, ainsi que self.doc_ouverts. self.doc_ouverts = RSSMenu(parent, u"Documents ouverts", [], self.charger_feuille, u"Documents ouverts.") self.menu = self.module._menu_(self) self.barre_outils = BarreOutils(self, couleur='white') self.console_geolib = ConsoleGeolib(self, couleur='white') self.barre_outils.Show(self.param("afficher_barre_outils")) self.console_geolib.Show(self.param("afficher_console_geolib")) ## self.creer_feuille() self.canvas.initialiser() self.__sizer_principal = wx.BoxSizer(wx.VERTICAL) self.__sizer_principal.Add(self.barre_outils, 0, wx.ALL, 5) @property2 def modifie(self, val = None): if val is None: return self.feuilles.modifie self.feuilles.modifie = val def changer_titre(self, texte = None): texte = self.feuille_actuelle.nom_complet if (texte is None) else texte self.parent.parent.titre(texte) def ouvrir(self, fichier): Panel_simple.ouvrir(self, fichier) self.feuille_actuelle.modifiee = False if isinstance(fichier, FichierGEO): self.feuille_actuelle.sauvegarde["nom"] = fichier.nom self.feuille_actuelle.sauvegarde["repertoire"] = fichier.repertoire else: rep, fich = os.path.split(fichier) self.feuille_actuelle.sauvegarde["repertoire"] = rep self.feuille_actuelle.sauvegarde["nom"] = removeend(fich, ".geo", ".geoz") # nom sans l'extension self.rafraichir_titre() def sauvegarder(self, nom_fichier = '', feuille = None): if feuille is None: feuille = self.feuille_actuelle if nom_fichier == '': if feuille.sauvegarde["nom"]: nom_fichier = os.path.join(feuille.sauvegarde["repertoire"], feuille.sauvegarde["nom"]) else: nom_fichier = "sauvegarde" if nom_fichier is None: return Panel_simple.sauvegarder(self, nom_fichier, feuille = feuille) Panel_simple.sauvegarder(self, nom_fichier, feuille = feuille) feuille.modifiee = False rep, fich = os.path.split(nom_fichier) feuille.sauvegarde["repertoire"] = rep feuille.sauvegarde["nom"] = removeend(fich, ".geo") # nom sans l'extension self.rafraichir_titre() def _fichiers_ouverts(self): u"Retourne la liste des fichiers ouverts (feuilles vierges exceptes)." return [self.sauvegarder(None, feuille) for feuille in self.feuilles if not feuille.vierge] def finaliser(self, contenu = None): if contenu is None: contenu = self.canvas self.__sizer_principal.Add(contenu, 1, wx.LEFT | wx.TOP | wx.GROW) self.__sizer_principal.Add(self.console_geolib, 0, wx.ALL, 5) self.SetSizer(self.__sizer_principal) self.Fit() def action_effectuee(self, log, signature = None): Panel_simple.action_effectuee(self, log, signature = signature) self.feuille_actuelle.interprete.commande_executee(signature = signature) def _get_actuelle(self): return self.feuilles.feuille_actuelle def _set_actuelle(self, feuille): self.feuilles.feuille_actuelle = feuille self.update() def _del_actuelle(self): del self.feuilles.feuille_actuelle self.update() feuille_actuelle = property(_get_actuelle, _set_actuelle, _del_actuelle) def creer_feuille(self, nom = None): if not (self.feuille_actuelle.vierge and nom is None): self.feuilles.nouvelle_feuille(nom) self.update() return self.feuille_actuelle ## self.canvas.fenetre = self.param("fenetre") # sert en particulier orthonormaliser le repre au besoin def charger_feuille(self, feuille): # Utilis par la classe API.menu.RSSMenu if isinstance(feuille, wx.Event): feuille = feuille.numero if not isinstance(feuille, Feuille): feuille = self.feuilles[feuille] self.feuille_actuelle = feuille def fermer_feuille(self, feuille = None): if feuille is None: del self.feuille_actuelle else: if not isinstance(feuille, Feuille): feuille = self.feuilles[feuille] self.feuilles.remove(feuille) self.update() def fermer_feuilles(self): u"Ferme toute les feuilles." self.feuilles.vider() self.update() def rafraichir_titre(self): u"Actualise le titre de la fentre, et la liste des feuilles ouvertes dans le menu." self.changer_titre() self.doc_ouverts.update(self.feuilles.noms) # vite les conflits lors de l'initialisation: if getattr(self, "barre_outils", None) is not None: self.barre_outils.rafraichir() def update(self): u"Fait les actualisations ncessaires quand la feuille courante change." self.rafraichir_titre() ## self.canvas.rafraichir_axes = True self.affiche() def _sauvegarder(self, fgeo, feuille = None): if feuille is None: feuille = self.feuille_actuelle fgeo.contenu["Figure"] = [feuille.sauvegarder()] fgeo.contenu["Affichage"] = [{}] for parametre in self.canvas.parametres: fgeo.contenu["Affichage"][0][parametre] = [repr(getattr(self.canvas, parametre))] fgeo.contenu["Meta"] = [{}] feuille.infos(modification = time.strftime("%d/%m/%Y - %H:%M:%S",time.localtime())) for nom, info in feuille.infos().items(): fgeo.contenu["Meta"][0][nom] = [info] #fgeo.contenu["Meta"][0]["modification"] = [time.strftime("%d/%m/%Y - %H:%M:%S",time.localtime())] def _ouvrir(self, fgeo): if fgeo.contenu.has_key("Affichage"): if fgeo.contenu["Affichage"]: parametres = fgeo.contenu["Affichage"][0] for parametre in parametres.keys(): setattr(self.canvas, parametre, eval_safe(parametres[parametre][0])) if fgeo.contenu.has_key("Figure"): for figure in fgeo.contenu["Figure"]: feuille = self.creer_feuille() feuille.charger(figure, mode_tolerant = True) if fgeo.contenu.has_key("Meta"): # obligatoirement APRES la creation du document, donc aprs "Figure" infos = fgeo.contenu["Meta"][0] for key, value in infos.items(): self.feuille_actuelle.infos(key = value[0]) for macro in fgeo.contenu.get("Macro", []): code = macro["code"][0] autostart = (macro["autostart"][0].strip().capitalize() == "True") mode_avance = (macro["mode_avance"][0].strip().capitalize() == "True") print "mode avance", mode_avance nom = macro["nom"][0] self.feuille_actuelle.macros[nom] = {"code": code, "autostart": autostart, "mode_avance": mode_avance} if autostart: self.executer_macro(nom = nom, **self.feuille_actuelle.macros[nom]) def executer_macro(self, **kw): code = kw.get("code", "") if kw.get("mode_avance", False): code = ":" + code else: # On va maintenant rajouter des lignes "pause()" au dbut de chaque boucle "for" ou "while". # l'instruction pause() permet d'interrompre la boucle au besoin. lignes = code.splitlines() rajouter_pause = False for i in xrange(len(lignes)): if rajouter_pause: lignes[i] = (len(lignes[i]) - len(lignes[i].lstrip()))*" " + "pause()\n" + lignes[i] # on insere "pause()" avant la ligne l, en respectant son indentation. rajouter_pause = False if (lignes[i].startswith("for") or lignes[i].startswith("while")) and lignes[i].endswith(":"): rajouter_pause = True code = "\n".join(lignes) if param.multi_threading: thread.start_new_thread(self.canvas.executer, (code + "\nwx.Yield()",)) else: self.canvas.executer(code) def exporter(self, path): self.canvas.exporter(path) self.feuille_actuelle.sauvegarde["export"] = path def annuler(self, event = None): self.feuille_actuelle.historique.annuler() self.rafraichir_titre() def retablir(self, event = None): self.feuille_actuelle.historique.refaire() self.rafraichir_titre() def affiche(self, event = None): self.canvas.rafraichir_affichage(rafraichir_axes = True) ## try: ## self.canvas.actualiser() ## except ZeroDivisionError: ## # se produit aprs avoir rduit la fentre, en dessous d'une certaine taille. ## print_error(u"Warning: Fentrage incorrect.") ## # Il semble que WxPython mette du temps determiner la nouvelle taille ## # de la fentre, et GetSize() renvoit alors temporairement des valeurs nulles. ## except: ## print_error() ## self.message(u"Erreur d'affichage !") def _affiche(self): u"Mthode surclasser." def reinitialiser(self): u"""Rinitialise le module (ferme les travaux en cours, etc.). surclasser dans la majorit des cas.""" self.fermer_feuilles() def sauver_preferences(self, lieu = None): if self._param_ is not None: for parametre in self.canvas.parametres: # Permet de sauvegarder les paramtres du moteur d'affichage pour chaque module de manire indpendante. setattr(self._param_, parametre, getattr(self.canvas, parametre)) Panel_simple.sauver_preferences(self, lieu) def afficher_barre_outils(self, afficher = None): u"Afficher ou non la barre d'outils." if afficher is not None: if isinstance(afficher, bool): self._param_.afficher_barre_outils = afficher else: self._param_.afficher_barre_outils = not self.param("afficher_barre_outils") with self.canvas.geler_affichage(actualiser = True): self.barre_outils.Show(self.param("afficher_barre_outils")) self.Fit() return self.param("afficher_barre_outils") def afficher_console_geolib(self, afficher = None): u"Afficher ou non la ligne de commande de la feuille." if afficher is not None: if isinstance(afficher, bool): self._param_.afficher_console_geolib = afficher else: self._param_.afficher_console_geolib = not self.param("afficher_console_geolib") with self.canvas.geler_affichage(actualiser = True): self.console_geolib.Show(self.param("afficher_console_geolib")) if self.param("afficher_console_geolib"): self.console_geolib.ligne_commande.SetFocus() self.Fit() return self.param("afficher_console_geolib") def activer(self, event = None): u"Actions effectuer lorsque l'onglet du module est slectionn. surclasser." if self.param("afficher_console_geolib"): #if gettattr(self._param_, "afficher_console_geolib", False): self.console_geolib.SetFocus() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/console_geolib.py0000644000175000017500000000460712014170666023706 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # Console pour geolib # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from .ligne_commande import LigneCommande from ..pylib import print_error class ConsoleGeolib(wx.Panel): def __init__(self, parent, couleur = None): self.parent = parent wx.Panel.__init__(self, parent, -1, style = wx.TAB_TRAVERSAL|wx.WANTS_CHARS) self.SetBackgroundColour(couleur if couleur is not None else wx.NamedColor(u"WHITE")) vsizer = wx.BoxSizer(wx.VERTICAL) label = u"Tapez une commande ci-dessus, puis appuyez sur [Entre]." self.resultats = wx.StaticText(self, size = (500, -1), label = label) italic = wx.Font(self.GetFont().GetPointSize(), self.GetFont().GetFamily(), wx.ITALIC, wx.NORMAL) self.resultats.SetFont(italic) self.ligne_commande = LigneCommande(self, longueur = 500, action = self.action) vsizer.Add(self.ligne_commande, 1, wx.ALL|wx.EXPAND, 0) vsizer.Add(self.resultats, 1, wx.ALL|wx.EXPAND, 5) self.SetSizer(vsizer) self.Fit() def action(self, commande, **kw): try: resultat = self.parent.feuille_actuelle.executer(commande) self.resultats.SetLabel(resultat) self.ligne_commande.Clear() except: print_error() self.resultats.SetLabel('Erreur.') wxgeometrie-0.133.2.orig/wxgeometrie/GUI/menu.py0000644000175000017500000006511212014170666021665 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Menu # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from operator import attrgetter from functools import partial import wx #----------------------- # Menus #----------------------- class RSSMenu(wx.Menu): # parent : wxgeometrie (Frame principal) u"Menu dynamique." def __init__(self, parent, titre = "", liste = None, fonction = None, aide = ""): wx.Menu.__init__(self) self.parent = parent self.table_ids = {} self.fonction = None self.titre = titre self.update(liste, fonction, aide) def update(self, liste = None, fonction = None, aide = None): if fonction is not None: self.fonction = fonction if aide is not None: self.aide = aide self.liste = liste or [] for id in self.table_ids.keys(): if self.fonction: # bof: pas super precis (si on rajoute une fonction entre temps, erreur) self.parent.parent.Unbind(wx.EVT_MENU, id = id) self.Delete(id) self.table_ids = {} for i in xrange(len(self.liste)): id = wx.NewId() self.table_ids[id] = i self.Append(id, self.liste[i], self.aide) if self.fonction: self.parent.parent.Bind(wx.EVT_MENU, self.gerer_evenement, id = id) def gerer_evenement(self, event): # rajoute a l'evenement la propriete nom_menu, avant de le rediriger. event.numero = self.table_ids[event.GetId()] self.fonction(event) class Menu(wx.Menu): u"Remplace la classe wx.Menu." def __init__(self, menubar, liste): wx.Menu.__init__(self) IDEM = True self.parent = menubar.parent self.table_ids = {} for contenu in liste: id = wx.NewId() # pour tre sr que 2 menus (dans 2 modules diffrents) n'auront pas le mme id. if not contenu: self.AppendSeparator() elif isinstance(contenu, RSSMenu): self.AppendMenu(id, "&" + contenu.titre, contenu) else: if len(contenu) == 1: # menus predefinis contenu = menubar.menus[contenu[0]] self.table_ids[id] = contenu if isinstance(contenu[1], (list, tuple)): # le menu contient un sous-menu menu = Menu(menubar, contenu[1:]) self.AppendMenu(id, "&" + contenu[0], menu) else: if not contenu[1]: contenu[1] = "" # 0->titre, 1->description, 2->raccourci, 3->fonction associee[, 4->cocher ou non] titre = "&" + contenu[0] if contenu[2]: titre += "\t" + contenu[2] if len(contenu) == 5: # le menu contient une case a cocher self.Append(id, titre, contenu[1], wx.ITEM_CHECK) if contenu[4] == IDEM: contenu[4] = contenu[3] #elif isinstance(contenu[4], basestring): # def f(self = menubar): # return getattr(self.canvas # contenu[4] = f self.Check(id, contenu[4]()) else: # cas classique self.Append(id, titre, contenu[1]) if contenu[3]: self.parent.parent.Bind(wx.EVT_MENU, self.gerer_evenement, id = id) def gerer_evenement(self, event): # rajoute a l'evenement la propriete nom_menu, avant de le rediriger. contenu = self.table_ids[event.GetId()] event.nom_menu = contenu[0] contenu[3](event) class MenuBar(wx.MenuBar): u"""Remplace la classe wx.MenuBar pour crer une barre de menu propre chaque module. La mthode de base est 'ajouter(self, *menu)', o menu est une liste. Exemple1: menu = ["Outils", ["Options", "Parametres du programme", "Ctrl+O", fonction1], ["Memoriser le resultat", "Copie le resultat du calcul dans le presse-papier", None, fonction2], None, ["Autres", ["Rubrique 1", None, "Alt+Ctrl+R", fonction3], ["Rubrique 2", "Rubrique non active pour l'instant", None, None]]] Exemple2: ["Infos", ["Afficher les infos", "Affichage des infos en temps reel", "Ctrl+I", fonction1, fonction2]] La presence de deux fonctions (eventuellement identiques) indique qu'il s'agit d'un menu "cochable". L'etat (coch ou non coch) est dtermin par la valeur renvoye par la fonction 'fonction2'. Note: Pour un menu moins standard, on peut toujours utiliser directement wx.MenuBar. """ def ajouter(self, *contenu): if isinstance(contenu, RSSMenu): self.Append(contenu, "&" + contenu.titre) else: if len(contenu) == 1: # menus predefinis contenu = self.menus[contenu[0]] menu = Menu(self, contenu[1:]) self.Append(menu, "&" + contenu[0]) def actualiser(self, event): u"Met jour le menu en actualisant l'tat des menus contenant une case cocher." menu = event.GetMenu() if hasattr(menu, "table_ids"): for item in menu.GetMenuItems(): if item.IsCheckable(): item.Check(menu.table_ids[item.GetId()][4]()) def __init__(self, panel = None, *contenu): # parent : wxgeometrie (Frame principal) wx.MenuBar.__init__(self) self.panel = panel self.parent = panel.parent self.canvas = panel.canvas self.fenetre = self.parent.parent ## self.historique = panel.historique ## self.commande = panel.commande def canparam(parametre): return partial(attrgetter(parametre), self.canvas) IDEM = True # Menus predefinis: self.menus = { "nouveau": [u"Nouveau", u"Crer un nouveau fichier.", u"Ctrl+N", self.parent.NewFile], "ouvrir": [u"Ouvrir", u"Ouvrir un fichier.", u"Ctrl+O", self.parent.OpenFile], "ouvrir ici": [u"Ouvrir ici", u"Essayer d'ouvrir le fichier dans le module courant.", u"Alt+Ctrl+O", self.parent.OpenFileHere], "enregistrer": [u"Enregistrer", u"Enregistrer le document.", u"Ctrl+S", self.parent.SaveFile], "enregistrer_sous": [u"Enregistrer sous...", u"Enregistrer le document sous un nouveau nom.", u"Alt+Ctrl+S", self.parent.SaveFileAs], "exporter": [u"Exporter...", u"Exporter l'image.", u"Ctrl+E", self.parent.ExportFile], "exporter&sauver": [u"Exporter et sauver", u"Exporter l'image, et sauvegarder le document.", u"Alt+Ctrl+E", self.parent.ExportAndSaveFile], "mise en page": [u"Paramtres d'impression", u"Rgler les paramtres d'impression.", None, self.parent.PageSetup], "imprimer": [u"Imprimer", u"Imprimer la figure gomtrique courante.", u"Ctrl+P", self.parent.Printout], "proprietes": [u"Proprits", u"Modifier les informations relatives au document", None, self.parent.Proprietes], "fermer": [u"Fermer", u"Fermer la feuille courante.", u"Ctrl+W", self.parent.CloseFile], "quitter": [u"Quitter", u"Fermer le programme.", u"Alt+F4", self.parent.CloseWindow], "onglet": [u"Onglet suivant", u"Changer d'onglet.", u"Ctrl+TAB", self.parent.onglet_suivant], "debug": [u"Dboguer", u"Dboguer le programme (afficher les erreurs, ...).", None, self.fenetre.mode_debug, self.fenetre.mode_debug], "ligne_commande": [u"Afficher la ligne de commande", u"Afficher la ligne de commande.", None, self.fenetre.afficher_ligne_commande, self.fenetre.afficher_ligne_commande], "options": [u"Options", u"Paramtres du programme.", None, self.parent.Options], "aide": [u"Aide", u"Obtenir de l'aide sur le programme.", None, self.parent.Aide], "notes": [u"Notes de version", u"Consulter l'historique du projet.", None, self.parent.Notes], "licence": [u"Licence", u"Licence.", None, self.parent.Licence], "infos": [u"Configuration", u"Visualiser la configuration actuelle du systme.", None, self.parent.Informations], "contact": [u"Signaler un problme", u"Envoyer un rapport de bug.", None, self.parent.Contacter], "versions": [u"Rechercher des mises jour", u"Vrifier si une nouvelle version est disponible.", None, self.parent.gestionnaire_de_mises_a_jour.verifier_version], "about": [u"A propos...", u"WxGeometrie (c) 2005-2007 Nicolas Pourcelot - License : GPL version 2", None, self.parent.About], } self.menus["fichier"] = ["Fichier", ["nouveau"], ["ouvrir"], ["ouvrir ici"], ["enregistrer"], ["enregistrer_sous"], ["exporter"], ["exporter&sauver"], None, ["mise en page"], ["imprimer"], None, ["fermer"], ["quitter"]] self.menus["avance1"] = [u"Avanc", [u"historique"], [u"ligne_commande"], [u"debug"]] self.menus["avance2"] = [u"Avanc", [u"ligne_commande"], ["debug"]] self.menus["?"] = ["?", ["aide"], ["notes"], ["licence"], ["infos"], ["contact"], None, ["versions"], None, ["about"]] if self.canvas: self.menus.update({ "annuler": [u"Annuler", u"Annuler la dernire action.", u"Ctrl+Z", self.panel.annuler], "refaire": [u"Refaire", u"Refait la dernire action annule.", u"Ctrl+Y", self.panel.retablir], "historique": [u"Contenu interne de la feuille", u"dition du contenu interne de la feuille.", u"Ctrl+H", self.parent.Histo], "presse-papier": [u"Copier dans le presse-papier", u"Copier l'image dans le presse-papier.", None, self.canvas.Copy_to_Clipboard], "barre_outils": [u"Afficher la barre d'outils", u"Afficher la barre d'outils de dessin en haut de la fentre.", None, self.panel.afficher_barre_outils, IDEM], "console_geolib": [u"Afficher la ligne de commande", u"Afficher la ligne de commande en bas de la fentre.", None, self.panel.afficher_console_geolib, IDEM], "repere": [u"Afficher le repre", u"Afficher le repre et les axes.", None, self.canvas.gerer_parametre_afficher_axes, canparam("afficher_axes")], "quadrillage": [u"Afficher le quadrillage", u"Afficher le quadrillage.", None, self.canvas.gerer_parametre_afficher_quadrillage, canparam('afficher_quadrillage')], "orthonorme": [u"Repre orthonorm", u"Garder un repre toujours orthonorm.", None, self.canvas.gerer_parametre_orthonorme, canparam('orthonorme')], "aimanter": [u"Aimanter la grille", u"Forcer les points se placer sur la grille.", None, self.canvas.gerer_parametre_grille_aimantee, canparam('grille_aimantee')], "reperage": [u"Reprage", [u"par des points", u"Reprage par l'origine et 2 points.", None, self.canvas.repere_OIJ], [u"par des vecteurs", u"Reprage par l'origine et les 2 vecteurs de base.", None, self.canvas.repere_Oij], [u"par des valeurs numriques", u"Graduation numrique des axes", None, self.canvas.repere_011], [u"Personnaliser le repre", u"Personnaliser l'affichage du repre, et les graduations", "Ctrl+Alt+R", self.parent.creer["Reperage"]], ], "quadrillages": [u"Quadrillage", [u"Par dfaut", u"Rtablir le quadrillage par dfaut.", None, self.canvas.quadrillage_defaut], [u"Graduations intermdiaires", u"Ajouter un quadrillage intermdiaire entre deux graduations.", None, self.canvas.quadrillage_demigraduation], [u"Graduations intermdiaires (color)", u"jouter un quadrillage intermdiaire entre deux graduations (version colore).", None, self.canvas.quadrillage_demigraduation_colore], [u"Papier millimtr", u"Crer un papier millimtr.", None, self.canvas.quadrillage_millimetre], [u"Papier millimtr color", u"Crer un papier millimtr color.", None, self.canvas.quadrillage_millimetre_colore], ], "zoom_texte": [u"Zoom texte", [u"100 %", u"Afficher les textes leur taille par dfaut.", None, lambda event, self=self: self.canvas.zoom_text(event, 100)], None, [u"50 %", u"Rduire les textes 50 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 50)], [u"60 %", u"Rduire les textes 60 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 60)], [u"70 %", u"Rduire les textes 70 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 70)], [u"80 %", u"Rduire les textes 80 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 80)], [u"90 %", u"Rduire les textes 90 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 90)], None, [u"120 %", u"Agrandir les textes 120 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 120)], [u"140 %", u"Agrandir les textes 140 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 140)], [u"160 %", u"Agrandir les textes 160 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 160)], [u"180 %", u"Agrandir les textes 180 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 180)], [u"200 %", u"Agrandir les textes 200 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_text(event, 200)], ], "zoom_ligne": [u"Zoom ligne", [u"100 %", u"Afficher les lignes leur taille par dfaut.", None, lambda event, self=self: self.canvas.zoom_line(event, 100)], None, [u"50 %", u"Rduire les lignes 50 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 50)], [u"60 %", u"Rduire les lignes 60 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 60)], [u"70 %", u"Rduire les lignes 70 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 70)], [u"80 %", u"Rduire les lignes 80 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 80)], [u"90 %", u"Rduire les lignes 90 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 90)], None, [u"120 %", u"Agrandir les lignes 120 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 120)], [u"140 %", u"Agrandir les lignes 140 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 140)], [u"160 %", u"Agrandir les lignes 160 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 160)], [u"180 %", u"Agrandir les lignes 180 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 180)], [u"200 %", u"Agrandir les lignes 200 % de leur taille.", None, lambda event, self=self: self.canvas.zoom_line(event, 200)], ], "zoom_general": [u"Zoom gnral", [u"Mode normal", u"Affichage par dfaut.", None, self.canvas.zoom_normal], [u"Lger grossissement", u"Textes et lignes (un peu) grossis.", None, self.canvas.zoom_large], None, [u"Mode vidoprojecteur (grossissement)", u"Rglage adapt la vidoprojection (textes et lignes grossis).", None, self.canvas. zoom_videoprojecteur], [u"Mode vidoprojecteur accentu", u"Grossissement trs important des objets.", None, self.canvas. zoom_videoprojecteur_large], ], "fenetre": [u"Rglage de la fentre", u"Rglage de la fentre d'affichage.", u"Alt+Ctrl+F", self.parent.creer["Fenetre"]], "zoomer": [u"Zoomer", u"Se rapprocher de la figure.", u"Ctrl+PGUP", self.canvas.zoom_in], "dezoomer": [u"Dzoomer", u"S'loigner de la figure.", u"Ctrl+PGDN", self.canvas.zoom_out], "orthonormaliser": [u"Orthonormaliser", u"Obtenir un repre orthonormal.", u"Alt+Ctrl+O", self.canvas.orthonormer], "zoom_auto": [u"Zoom intelligent", u"Rglage automatique de la fentre d'affichage.", u"Alt+Ctrl+A", self.canvas.zoom_auto], "modifier": [u"Modifier", u"Editer les proprites d'un ou plusieurs objets gomtriques.", u"Ctrl+M", self.parent.editer], "supprimer":[u"Supprimer", u"Supprime un ou plusieurs objets gomtriques.", u"Ctrl+DEL", self.parent.supprimer], "coder": [u"Codage automatique", u"Codage automatique de la figure.", u"Alt+Ctrl+C", self.canvas.coder], "decoder": [u"Effacer le codage", u"Supprimer le codage de la figure.", u"Alt+Ctrl+D", self.canvas.decoder], "traces": [u"Effacer les traces", u"Supprimer toutes les traces de la figure (laisse les objets en mode Trace).", None, self.canvas.effacer_traces], ##"detecter": [u"Dtecter les objets cachs", u"Signaler la prsence des objets cachs au passage du pointeur.", None, self.canvas.detecter_caches, self.canvas.detecter_caches], "detecter": [u"Afficher les objets cachs", u"Afficher en semi-transparent les objets cachs.", None, self.canvas.gerer_parametre_afficher_objets_caches, canparam('afficher_objets_caches')], "nettoyer": [u"Supprimer les objets inutiles", u"Supprimer les objets cachs qui ne servent pas pour la construction.", None, self.canvas.nettoyer_feuille], "animer": [u"Crer une animation", u"Faire varier automatiquement une valeur.", None, self.parent.Animer], "affichage": [u"Affichage", ["onglet"], None, ["barre_outils"], ["console_geolib"], None, ["repere"], ["quadrillage"], ["orthonorme"], ["reperage"], ["quadrillages"], None, ["zoom_texte"], ["zoom_ligne"], ["zoom_general"], None, ["fenetre"], ["zoomer"], ["dezoomer"], ["orthonormaliser"], [u"zoom_auto"]], "autres": [u"Autres actions", [u"coder"], [u"decoder"], [u"traces"], None, [u"detecter"], [u"nettoyer"], None, [u"animer"], [u"aimanter"]], "creer": [u"Crer", [u"Points", [u"Point libre", u"Point quelconque.", u"Ctrl+L", self.parent.creer["Point"]], [u"Milieu", u"Milieu d'un segment.", None, self.parent.creer["Milieu"]], [u"Barycentre", u"Barycentre de n points.", u"Ctrl+B", self.parent.creer["Barycentre"]], [u"Point final", u"Point dfini par une relation vectorielle.", u"Ctrl+F", self.parent.creer["PointFinal"]], [u"Point sur droite", u"Point appartenant une droite.", None, self.parent.creer["GlisseurDroite"]], [u"Point sur segment", u"Point appartenant un segment.", None, self.parent.creer["GlisseurSegment"]], [u"Point sur cercle", u"Point appartenant un cercle.", None, self.parent.creer["GlisseurCercle"]], ], [u"Intersections", [u"Intersection de deux droites", u"Point dfini par l'intersection de deux droites (ou demi-droites, ou segments).", u"Ctrl+I", self.parent.creer["InterDroites"]], [u"Intersection d'une droite et d'un cercle", u"Point dfini par l'intersection d'une droite et d'un cercle.", u"Alt+Ctrl+I", self.parent.creer["InterDroiteCercle"]], [u"Intersection de deux cercles", u"Point dfini par l'intersection de deux cercles (ou arcs de cercles).", None, self.parent.creer["InterCercles"]], ], [u"Centres", [u"Centre d'un cercle", u"Centre d'un cercle.", None, self.parent.creer["Centre"]], [u"Centre de gravit", u"Centre de gravite d'un triangle (intersection des mdianes).", None, self.parent.creer["CentreGravite"]], [u"Orthocentre", u"Orthocentre d'un triangle (intersection des hauteurs).", None, self.parent.creer["Orthocentre"]], [u"Centre du cercle circonscrit", u"Centre du cercle circonscrit d'un triangle (intersection des mdiatrices).", None, self.parent.creer["CentreCercleCirconscrit"]], [u"Centre du cercle inscrit", u"Centre du cercle inscrit d'un triangle (intersection des bissectrices).", None, self.parent.creer["CentreCercleInscrit"]], ], [u"Lignes", [u"Segment", u"Segment dfini par deux points.", u"Ctrl+G", self.parent.creer["Segment"]], None, [u"Droite", u"Droite dfinie par deux points.", u"Ctrl+D", self.parent.creer["Droite"]], [u"Demi-droite", u"Demi-droite dfinie par son origine et un autre point.", None, self.parent.creer["Demidroite"]], None, [u"Vecteur", u"Vecteur dfini par deux points.", u"Ctrl+U", self.parent.creer["Vecteur"]], [u"Vecteur libre", u"Vecteur dfini par ses coordonnes.", None, self.parent.creer["VecteurLibre"]], [u"Representant", u"Reprsentant d'origine donne d'un vecteur.", None, self.parent.creer["Representant"]], None, [u"Parallle", u"Parallle une droite passant par un point.", None, self.parent.creer["Parallele"]], [u"Perpendiculaire", u"Perpendiculaire une droite passant par un point.", None, self.parent.creer["Perpendiculaire"]], [u"Mdiatrice", u"Mdiatrice d'un segment.", None, self.parent.creer["Mediatrice"]], [u"Bissectrice", u"Bissectrice d'un angle.", None, self.parent.creer["Bissectrice"]], [u"Tangente", u"Tangente un cercle.", None, self.parent.creer["Tangente"]], ], [u"Cercles", [u"Cercle dfini par son centre et un point", u"Cercle dfini par son centre et un autre point.", u"Ctrl+K", self.parent.creer["Cercle"]], [u"Cercle dfini par son centre et son rayon", u"Cercle dfini par son centre et la valeur de son rayon.", u"Ctrl+R", self.parent.creer["CercleRayon"]], [u"Cercle dfini par un diamtre", u"Cercle dfini par deux points diamtralement opposs.", None, self.parent.creer["CercleDiametre"]], [u"Cercle dfini par 3 points", u"Cercle dfini par trois points.", None, self.parent.creer["CerclePoints"]], None, [u"Arc de centre donn", u"Arc de sens direct, dfini par son centre, son origine, et un autre point.", None, self.parent.creer["ArcCercle"]], [u"Arc dfini par 3 points", u"Arc dfini par ses extrmits, et un point intermdiaire.", None, self.parent.creer["ArcPoints"]], [u"Arc orient", u"Arc orient, dfini par ses extrmits, et un point intermdiaire.", None, self.parent.creer["ArcOriente"]], [u"Demi-cercle", u"Demi-cercle de diamtre donn, de sens direct.", None, self.parent.creer["DemiCercle"]], None, [u"Disque", u"Disque circonscrit par un cercle donn.", None, self.parent.creer["Disque"]], ], [u"Polygones", [u"Triangle", u"Triangle dfini par ses sommets.", None, self.parent.creer["Triangle"]], [u"Polygone quelconque", u"Polygone quelconque, dfini par ses sommets.", None, self.parent.creer["Polygone"]], [u"Paralllogramme", u"Paralllogramme de sens direct dfini par 3 sommets.", None, self.parent.creer["Parallelogramme"]], [u"Polygone rgulier", u"Polygone rgulier de sens direct dfini par 2 sommets conscutifs.", None, self.parent.creer["PolygoneRegulier"]], [u"Polygone rgulier de centre donn", u"Polygone rgulier dfini son centre et un sommet.", None, self.parent.creer["PolygoneRegulierCentre"]], ], [u"Interpolation", [u"Interpolation linaire", u"Lign brise reliant les points dsigns.", None, self.parent.creer["InterpolationLineaire"]], [u"Interpolation quadratique", u"Courbe lisse (ie. de classe C1) reliant les points dsigns.", None, self.parent.creer["InterpolationQuadratique"]], ], [u"Angles", [u"Angle", u"Angle non orient dfini par trois points.", None, self.parent.creer["Angle"]], [u"Angle orient", u"Angle orient dfini par trois points.", None, self.parent.creer["AngleOriente"]], [u"Angle orient (non affich)", u"Angle orient (non affich) dfini par 2 vecteurs.", None, self.parent.creer["AngleVectoriel"]], [u"Angle libre (non affich)", u"Angle orient (non affich) dfini par 2 vecteurs.", None, self.parent.creer["AngleLibre"]], ], [u"Transformations", [u"Translation", u"Translation de vecteur donn.", None, self.parent.creer["Translation"]], [u"Symtrie centrale", u"Symtrie par rapport un point.", None, self.parent.creer["SymetrieCentrale"]], [u"Symtrie axiale", u"Symtrie par rapport une droite.", None, self.parent.creer["Reflexion"]], [u"Rotation", u"Rotation de centre et d'angle donns.", None, self.parent.creer["Rotation"]], [u"Homothtie", u"Translation de vecteur donn.", None, self.parent.creer["Homothetie"]], None, [u"Image par transformation", u"Crer l'image d'un objet par une transformation gomtrique.", None, self.parent.creer["Image"]], ], [u"Divers", [u"Texte", u"Champ de texte.", None, self.parent.creer["Texte"]], [u"Variable", u"Variable lie ou non.", None, self.parent.creer["Variable"]], ], ] }) for item in contenu: self.ajouter(*item) self.parent.parent.Bind(wx.EVT_MENU_OPEN, self.actualiser) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/contact.py0000644000175000017500000001563212014170666022356 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Contact # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import thread, sys import wx from wxlib import TransmitEvent, EVT_TRANSMIT from .. import param from ..pylib import path2 from ..pylib.bugs_report import rapporter class Contact(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, -1, u"Contacter l'auteur", style = wx.FRAME_FLOAT_ON_PARENT|wx.CLIP_CHILDREN|wx.CLOSE_BOX|wx.CAPTION) self.SetBackgroundColour(wx.WHITE) self.parent = parent panel = wx.Panel(self, -1) italic = wx.Font(panel.GetFont().GetPointSize(), panel.GetFont().GetFamily(), wx.ITALIC, wx.NORMAL) # bold_italic = wx.Font(panel.GetFont().GetPointSize(), panel.GetFont().GetFamily(), wx.ITALIC, wx.BOLD) panel.SetBackgroundColour(wx.WHITE) panelSizer = wx.BoxSizer(wx.VERTICAL) avant_propos = wx.StaticText(panel, -1, u"""Afin d'amliorer le fonctionnement de WxGomtrie, vous tes invits signaler tout problme rencontr.""") panelSizer.Add(avant_propos, 0, wx.ALL, 5) avant_propos.SetFont(italic) panelSizer.Add((5, 5)) rapport = wx.StaticBoxSizer(wx.StaticBox(panel, -1, u"Rapport d'incident"), wx.VERTICAL) #rapport.Add(wx.StaticText(panel, -1, u"Rsum :"), 0, wx.ALL, 5) self.titre = titre = wx.TextCtrl(panel, -1, u"Rsum", size = (300, -1)) titre.SelectAll() rapport.Add(titre, 0, wx.ALL, 5) sizer= wx.BoxSizer(wx.HORIZONTAL) self.modules = modules = wx.Choice(panel, choices = [self.parent.onglet(md).__titre__ for md in param.modules if hasattr(self.parent.onglet(md), "__titre__")]) sizer.Add(wx.StaticText(panel, -1, u"Module concern :"), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) modules.SetSelection(self.parent.GetSelection()) sizer.Add(modules, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) rapport.Add(sizer, 0, wx.ALL, 0) rapport.Add(wx.StaticText(panel, -1, u"Description du problme :"), 0, wx.ALL, 5) self.commentaire = commentaire = wx.TextCtrl(panel, size = (300,100), style = wx.TE_MULTILINE) rapport.Add(commentaire, 0, wx.ALL, 5) panelSizer.Add(rapport, 0, wx.ALL|wx.EXPAND, 5) sizer = wx.StaticBoxSizer(wx.StaticBox(panel, -1, u"Vos coordonnes (facultatif)"), wx.HORIZONTAL) sizer.Add(wx.StaticText(panel, -1, u"Nom :"), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.nom = nom = wx.TextCtrl(panel, size = (100, -1)) sizer.Add(nom, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) sizer.Add(wx.StaticText(panel, -1, u" E-mail :"), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) self.mail = mail = wx.TextCtrl(panel, size = (100, -1)) sizer.Add(mail, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) panelSizer.Add(sizer, 0, wx.ALL|wx.EXPAND, 5) options = wx.StaticBoxSizer(wx.StaticBox(panel, -1, u"Options"), wx.VERTICAL) self.histo = histo = wx.CheckBox(panel, -1, "Inclure l'historique du module courant.") histo.SetValue(True) options.Add(histo, 0, wx.ALL, 5) self.msg = msg = wx.CheckBox(panel, -1, "Inclure l'historique des commandes.") msg.SetValue(True) options.Add(msg, 0, wx.ALL, 5) panelSizer.Add(options, 0, wx.ALL|wx.EXPAND, 5) btnOK = wx.Button(panel, wx.ID_OK, u"Envoyer") btnOK.SetToolTipString(u"Envoyer les informations.") btnCancel = wx.Button(panel, wx.ID_CANCEL, u"Annuler") btnCancel.SetToolTipString(u"Quitter sans rien envoyer.") sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(btnOK, 0, wx.RIGHT, 40) sizer.Add(btnCancel, 0, wx.LEFT, 40) panelSizer.Add(sizer, 0, wx.ALL | wx.ALIGN_CENTRE, 15) panel.SetAutoLayout(True) panel.SetSizer(panelSizer) panelSizer.Fit(panel) topSizer = wx.BoxSizer(wx.HORIZONTAL) topSizer.Add(panel, 0, wx.ALL, 10) self.SetAutoLayout(True) self.SetSizer(topSizer) topSizer.Fit(self) self.Centre() self.Bind(EVT_TRANSMIT, self.onTransmit) btnOK.Bind(wx.EVT_BUTTON, self.rapporter) btnCancel.Bind(wx.EVT_BUTTON, self.annuler) def annuler(self, event): self.Close() def rapporter(self, event): titre = self.titre.GetValue() commentaire = self.commentaire.GetValue() nom = self.nom.GetValue() mail = self.mail.GetValue() module = self.parent.onglet(self.modules.GetSelection()) if self.histo.GetValue() and hasattr(module, "log"): histo = module.log.contenu() else: histo = "" if self.msg.GetValue(): sys.stdout.flush() filename = path2(param.emplacements['log'] + u"/messages.log") try: file = open(filename, 'r') msg = file.read() finally: file.close() else: msg = "" def f(): result = rapporter(titre = titre, auteur = nom, email = mail, description = commentaire, historique = histo, log = msg) wx.PostEvent(self, TransmitEvent(success = result)) self.Hide() thread.start_new_thread(f, ()) def onTransmit(self, event): if event.success: dlg = wx.MessageDialog(self, u"Le message a t envoy avec succs. Merci !", u"Message envoy", wx.OK | wx.ICON_INFORMATION ) else: dlg = wx.MessageDialog(self, u"Impossible d'envoyer le message !", u"Connexion impossible.", wx.OK | wx.ICON_INFORMATION #wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION ) dlg.ShowModal() dlg.Destroy() if event.success: self.Close() else: self.Show() wxgeometrie-0.133.2.orig/wxgeometrie/GUI/nouvelles_versions.py0000644000175000017500000001106612014170666024664 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import thread, urllib, webbrowser import wx from wxlib import TransmitEvent, EVT_TRANSMIT from ..pylib import print_error from .. import param class Progression: def __init__(self, parent): self.parent = parent self.initialiser = True self.titre = u"Vrification des nouvelles versions." def actualiser(self, blocs_finis = None, taille_bloc = None, taille_fichier = None): if blocs_finis != None: self.fini = min(taille_bloc*blocs_finis, taille_fichier) #print self.fini # la premiere fois, on cree la fenetre de progression. if self.initialiser: self.dlg = wx.ProgressDialog(self.titre, u"Vrification en cours.", maximum = taille_fichier, parent = self.parent, style = wx.PD_CAN_ABORT | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME | wx.PD_AUTO_HIDE | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME ) self.timer = wx.FutureCall(500, self.actualiser) # les autres fois, on l'actualise. else: if not self.dlg.Update(self.fini): self.fin() raise ValueError # de maniere a interrompre le telechargement en cours. self.timer.Restart(500) self.initialiser = False def fin(self): try: self.timer.Stop() del self.timer self.dlg.Destroy() except: pass class Gestionnaire_mises_a_jour(wx.EvtHandler): def __init__(self, parent): self.parent = parent self.derniere_version = None wx.EvtHandler.__init__(self) self.Bind(EVT_TRANSMIT, self.onTransmit) def onTransmit(self, event): if event.success: if event.update_available: self.derniere_version = event.version wx.MessageBox(u"La version %s de WxGomtrie est sortie.\nVous allez tre redirig vers la page de tlchargement." %event.version, u"Une mise jour a t trouve.") webbrowser.open("http://sourceforge.net/projects/wxgeometrie/files/WxGeometrie/") else: wx.MessageBox(u"Aucune mise jour n'est disponible actuellement.\nConsultez http://wxgeo.free.fr pour plus d'informations.", u"Aucune mise jour trouve.") else: wx.MessageBox(u"Impossible de vrifier si une nouvelle version existe.", u"Connexion impossible") def verifier_version(self, event = None): thread.start_new_thread(self._verifier_version, ()) def _verifier_version(self, event = None): # progression = Progression(self.parent) try: filename, headers = urllib.urlretrieve("http://wxgeo.free.fr/wordpress/version")#, None, progression.actualiser) f = open(filename) version = f.read(60) f.close() if len(version) > 50 or not version.replace(" ", "").replace(".", "").isalnum(): raise Exception, "Incorrect file format, unable to find current version." if version.split(".") > param.version.split('.'): wx.PostEvent(self, TransmitEvent(success = True, update_available = True, version = version)) else: wx.PostEvent(self, TransmitEvent(success = True, update_available = False)) except: print_error() wx.PostEvent(self, TransmitEvent(success = False, update_available = None)) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/__init__.py0000644000175000017500000000417512014170666022462 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import traceback from .. import param if not param.py2exe: import wxversion try: if wxversion.checkInstalled(param.version_wxpython): wxversion.select(param.version_wxpython) # version a utiliser de preference else: # ou bien la version n'est pas trouvee, ou bien on est dans py2exe print u"Attention : impossible de charger la version %s de WxPython." %param.version_wxpython except Exception: if param.debug: print traceback.format_exc() try: import matplotlib matplotlib.use(param.moteur_de_rendu, warn = False) #import pylab # cette ligne semble ncessaire sous Ubuntu Feisty (python 2.5 - matplotlib 0.87.7) ?? except Exception: print "Warning : Erreur lors de l'import de pylab.\n" if param.debug: print traceback.format_exc() import wx from .menu import MenuBar from .panel import Panel_simple, Panel_API_graphique # NB: Ne *PAS* importer modules.py ici (ou alors, modifier le script d'initialisation). # En effet, la mise jour des paramtres en fonction des prfrences de l'utilisateur # doit avoir lieu avant d'importer modules.py, qui lui-mme utilise param.modules_actifs. wxgeometrie-0.133.2.orig/wxgeometrie/GUI/dialogues_geometrie.py0000644000175000017500000011705012014170666024734 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Fenetres # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from operator import attrgetter import wx from ..pylib import regsub from ..geolib.textes import Texte from ..geolib.points import Point_generique, Barycentre, Point, Milieu, Centre,\ Orthocentre, Centre_cercle_circonscrit, Centre_cercle_inscrit,\ Glisseur_droite, Glisseur_cercle, Glisseur_segment,\ Centre_gravite, Point_final from ..geolib.cercles import Cercle_generique, Cercle, Arc_points,\ Arc_oriente, Cercle_diametre, Cercle_rayon, Demicercle,\ Arc_cercle, Disque, Cercle_points from ..geolib.lignes import Droite_generique, Segment, Demidroite, Ligne_generique,\ Droite, Tangente, Parallele, Perpendiculaire, Bissectrice,\ Mediatrice from ..geolib.polygones import Polygone_regulier, Triangle, Polygone,\ Parallelogramme, Polygone_regulier_centre from ..geolib.angles import Angle_generique, Angle, Angle_oriente,\ Angle_libre, Angle_vectoriel from ..geolib.transformations import Rotation, Homothetie, Translation, Reflexion,\ Symetrie_centrale from ..geolib.vecteurs import Vecteur_generique, Vecteur, Vecteur_libre, Representant from ..geolib.intersections import Intersection_cercles, Intersection_droite_cercle,\ Intersection_droites from ..geolib.interpolations import Interpolation_lineaire, Interpolation_quadratique from ..geolib.variables import Variable from ..geolib.objet import Objet from .. import param def repr_str(chaine): u'Force la chane a tre reprsente entoure de guillemets doubles (").' return repr(chaine + "'")[:-2] + '"' #################################################################################################### # Differentes boites de dialogue pour creer des objets geometriques # A REECRIRE EN UTILISANT wxGridBagSizer ?? class Dialogue(wx.Dialog): objet = None # classe associee (utilise pour les boites de dialogue de creation d'objets geometriques) def __init__(self, parent, titre = "", size = wx.DefaultSize): u"""S'il s'agit d'un dialogue pour la cration d'objets, le titre par dfaut est gnr automatiquement partir de l'attribut de classe 'objet'.""" if self.objet and not titre: titre = u"Crer " + self.objet.titre() provider = wx.SimpleHelpProvider() wx.HelpProvider_Set(provider) # Instead of calling wx.Dialog.__init__ we precreate the dialog # so we can set an extra style that must be set before # creation, and then we create the GUI dialog using the Create # method. pre = wx.PreDialog() pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP) pre.Create(parent, -1, titre, wx.DefaultPosition, size, wx.DEFAULT_DIALOG_STYLE) # si l'objet est nul, ce n'est pas une boite de creation d'objet # This next step is the most important, it turns this Python # object into the real wrapper of the dialog (instead of pre) # as far as the wxPython extension is concerned. self.PostCreate(pre) # Now continue with the normal construction of the dialog # contents ## self.parent = parent # En cas de changement d'onglet, cela permet de pointer toujours sur l'onglet initial self.onglet_actuel = parent.onglet_actuel self.sizer = wx.BoxSizer(wx.VERTICAL) self.champs = {} self.Bind(wx.EVT_CHAR, self.OnChar) if self.objet : self.ajoute([("Nom %s : " %self.objet.titre("du", False)), ("nom", 10)], u"Entrez le nom de l'objet. Exemples : A, AB, Cercle, M2...") def OnChar(self, event): if event.GetKeyCode() == wx.WXK_ESCAPE: self.Close() else: event.Skip() def ajoute(self, contenu, aide = ""): # ajoute une ligne de contenu dans la boite de dialogue # format du contenu : ("texte statique",("nom de champ", taille), etc...) # exemple : ("entrez l'abscisse :",("absc",10),"cm") # pour un champ, un 3e argument peut-tre indiqu pour donner le type d'objet, s'il s'agit d'un objet gomtrique (ou None) # un 4eme argument peut-etre saisi, pour donner le comportement en cas d'agrandissement, # et un 5eme, pour indiquer une valeur initiale du champ # le champ cree sera accessible via self.champ("abcs") # # L'argument type d'objet sert faire des propositions l'utilisateur lors d'un clic du milieu. # # le type d'objet peut-tre compris dans une liste d'un seul lement, par exemple : [Point_generique] # Cela signifie alors que le champ doit contenir une liste de points, et non un seul point # # A noter qu'un tuple, comme (Point_generique, Vecteur), par contre, correspond au comportement habituel de Python: l'objet est soit un Point_generique, soit un Vecteur self.box = wx.BoxSizer(wx.HORIZONTAL) for txt in contenu: if isinstance(txt, (str, unicode)): # texte statique texte = wx.StaticText(self, -1, txt) texte.SetHelpText(aide) self.box.Add(texte, 0, wx.ALIGN_CENTRE|wx.ALL, 5) else: # champ de saisie self.champs[txt[0]] = wx.TextCtrl(self,-1, (len(txt) >= 5) and str(txt[4]) or "", size = (10*txt[1],-1)) self.champs[txt[0]].SetHelpText(aide) self.box.Add(self.champs[txt[0]], (len(txt) >= 4) and txt[3] or 0, wx.ALIGN_CENTRE|wx.ALL, 5) if len(txt) >= 3 and txt[2] is not None: self.champs[txt[0]].Bind(wx.EVT_MIDDLE_DOWN, self.MiddleClicFunction(txt[2], self.champs[txt[0]])) self.sizer.Add(self.box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) # ajoute la ligne def finalise(self): # cette fonction est a appeler une fois tout le contenu ajoute. line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL) self.sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5) self.box = wx.BoxSizer(wx.HORIZONTAL) if wx.Platform != "__WXMSW__": btn = wx.ContextHelpButton(self) self.box.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) btn = wx.Button(self, wx.ID_OK, self.objet and u" Crer " or u"Enregistrer") btn.SetHelpText(self.objet and u"Crer l'objet." or u"Enregistrer les modifications.") btn.SetDefault() self.box.Add(btn, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) btn = wx.Button(self, wx.ID_CANCEL, " Annuler ") btn.SetHelpText(u"Quitter sans rien changer.") self.box.Add(btn, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) self.sizer.Add(self.box, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) self.SetSizer(self.sizer) self.SetAutoLayout(True) self.sizer.Fit(self) self.CenterOnScreen() if self.objet: self.champs['nom'].SetFocus() def affiche(self): return self.ShowModal() def commande(self): return self.champ("nom") + "=" + self.objet.classe() + "(" + self.parametres() + ")" def champ(self, etiquette): return self.champs[etiquette].GetValue() def parametres(self): # le parametre par defaut est None return ",".join([etiquette + "=" + (self.champ(etiquette).strip() or "None") for etiquette in self.champs if etiquette <> "nom"]) def MiddleClicFunction(self, type, champ): u"Retourne une fonction qui sera execute lors d'un clic avec le bouton du milieu sur le champ 'champ'." def f(event, self = self, type = type, champ = champ): champ.SetFocus() liste = isinstance(type, list) if liste: type = type[0] liste_objets = self.onglet_actuel.feuille_actuelle.objets.lister(False, type = type) liste_objets.sort(key = attrgetter('nom')) # ordre alphabtique if not liste_objets: return ids = [wx.NewId() for obj in liste_objets] menu = wx.Menu() for i in xrange(len(liste_objets)): menu.Append(ids[i], liste_objets[i].nom_complet) if liste: # le champ doit contenir non pas un objet, mais une liste d'objets def select(event, nom = liste_objets[i].nom, champ = champ): val = champ.GetValue().strip() if val: if not val.endswith(","): val += "," val += " " champ.SetValue(val + nom) champ.SetInsertionPointEnd() else: # le champ contient un seul objet def select(event, nom = liste_objets[i].nom, champ = champ): champ.SetValue(nom) menu.Bind(wx.EVT_MENU, select, id = ids[i]) self.PopupMenu(menu) menu.Destroy() return f class DialoguePoint(Dialogue): objet = Point def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Coordonnes du point :"], u"Entrez les coordonnes de votre nouveau point.") self.ajoute([u"Abscisse :", ("x", 10, Variable)], u"Entrez ici l'abscisse du point. Exemple : 3.25") self.ajoute([u"Ordonne :", ("y", 10, Variable)], u"Entrez ici l'ordonne du point. Exemple : -5") self.finalise() class DialogueSegment(Dialogue): objet = Segment def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Extrmits du segment :"], u"Entrez les extrmits du nouveau segment.") self.ajoute([u"Premier point :", ("point1", 10, Point_generique)], u"Entrez ici le premier point. Exemple : A") self.ajoute([u"Deuxime point :", ("point2", 10, Point_generique)], u"Entrez ici le deuxime point. Exemple : B") self.finalise() class DialogueDroite(Dialogue): objet = Droite def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Donnez deux points de la droite :"], u"Dfinissez la droite en entrant deux points de la droite.") self.ajoute([u"Premier point :", ("point1", 10, Point_generique)], u"Entrez ici le premier point. Exemple : A") self.ajoute([u"Deuxime point :", ("point2", 10, Point_generique)], u"Entrez ici le deuxime point. Exemple : B") self.finalise() class DialogueDemidroite(Dialogue): objet = Demidroite def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Donnez deux points de la demi-droite :"], u"Dfinissez la demi-droite en entrant son origine, et un autre point.") self.ajoute([u"Origine :", ("origine", 10, Point_generique)], u"Entrez ici son origine. Exemple : A") self.ajoute([u"Deuxime point :", ("point", 10, Point_generique)], u"Entrez ici un deuxime point. Exemple : B") self.finalise() class DialogueVecteur(Dialogue): objet = Vecteur def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Extremits du vecteur :"], u"Entrez les extremits du nouveau vecteur.") self.ajoute([u"Point de dpart :", ("point1", 10, Point_generique)], u"Entrez ici le premier point. Exemple : A") self.ajoute([u"Point d'arrive :", ("point2", 10, Point_generique)], u"Entrez ici le deuxieme point. Exemple : B") self.finalise() class DialogueVecteurLibre(Dialogue): objet = Vecteur_libre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Coordonnes du vecteur :"], u"Entrez les coordonnes de votre nouveau vecteur.") self.ajoute([u"Abscisse :", ("x", 10, Variable)], u"Entrez ici l'abscisse du vecteur. Exemple : 3.25") self.ajoute([u"Ordonne :", ("y", 10, Variable)], u"Entrez ici l'ordonne du vecteur. Exemple : -5") self.finalise() class DialogueRepresentant(Dialogue): objet = Representant def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un reprsentant du vecteur :", ("vecteur", 10, Vecteur_generique)], u"Entrez ici un nom de vecteur. Exemple : u") self.ajoute([u"ayant pour origine le point :", ("origine", 10, Point_generique)], u"Entrez ici le point origine du vecteur. Exemple : A") self.finalise() class DialogueMilieu(Dialogue): objet = Milieu def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Construire le milieu des points :"], u"Entrez les extrmits du segment.") self.ajoute([u"Premier point :", ("point1", 10, Point_generique)], u"Entrez ici le premier point. Exemple : A") self.ajoute([u"Deuxime point :", ("point2", 10, Point_generique)], u"Entrez ici le deuxime point. Exemple : B") self.finalise() class DialogueBarycentre(Dialogue): objet = Barycentre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Construire le barycentre :"], u"Entrez les points, puis les coefficients.") self.ajoute([u"des points :", ("points", 10)], u"Entrez ici les points spars par des virgules. Exemple : A,B,C") self.ajoute([u"avec les coefficients :", ("coeffs", 10)], u"Entrez ici les coefficients (de somme non nulle !) spars par des virgules. Exemple : 5,3,1.5") self.finalise() def parametres(self): return "*zip((" + self.champ("points") + "),(" + self.champ("coeffs") + "))" class DialoguePointFinal(Dialogue): objet = Point_final def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Construire le point tel qu'on ait :"], u"Entrez la relation vectorielle.") self.ajoute([("point1", 2, Point_generique), ">", ("point2", 2, Point_generique), "=", ("relation", 10, Point_generique, 1)], \ u"""Entrez ici la relation vectorielle. Exemple, si N est le point construire :\nA>N = 2 B>C + 5/3 D>E. Attention : le membre de droite ne doit contenir que des points dj existants.""") self.finalise() def commande(self): #~ relation = regexp("[A-Z_a-z][A-Z_a-z0-9 ]*>[ ]*[A-Z_a-z][A-Z_a-z0-9]*", self.champ("relation"), "'(' + x + ')'") relation = regsub("[A-Z_a-z][A-Z_a-z0-9 ]*>[ ]*[A-Z_a-z][A-Z_a-z0-9]*", self.champ("relation"), lambda x:'(' + x + ')') #~ relation = regexp("[0-9][ ]*[(]", relation, "x[:-1] + '*('") relation = regsub("[0-9][ ]*[(]", relation, lambda x:x[:-1] + '*(') autre_point = self.champ("point1") if autre_point == self.champ("nom"): relation = "-(%s)" %relation autre_point = self.champ("point2") return self.champ("nom") + "=" + autre_point + "+(" + relation + ")" class DialogueCercleRayon(Dialogue): objet = Cercle_rayon def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un cercle de centre :", ("centre", 5, Point_generique)], u"Entrez ici le centre du cercle. Exemple : M") self.ajoute([u"et de rayon :", ("rayon", 5, Variable)], u"Entrez ici le rayon. Exemple : 3") self.finalise() class DialogueCercle(Dialogue): objet = Cercle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un cercle de centre :", ("centre", 5, Point_generique)], u"Entrez ici le centre du cercle. Exemple : O") self.ajoute([u"auquel appartient :", ("point", 5, Point_generique)], u"Entrez ici un point du cercle. Exemple : M") self.finalise() class DialogueCercleDiametre(Dialogue): objet = Cercle_diametre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Donnez un diamtre du cercle :"]) self.ajoute([u"Crer un cercle de diamtre : [", ("point1", 5, Point_generique), ("point2", 5, Point_generique), "]"], u"Entrez les extrmits du diamtre. Exemple : A et B") self.finalise() class DialogueCerclePoints(Dialogue): objet = Cercle_points def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Premier point du cercle :", ("point1", 5, Point_generique)], u"Entrez ici un point du cercle. Exemple : A") self.ajoute([u"Deuxime point : ", ("point2", 5, Point_generique)], u"Entrez un 2e point du cercle. Exemple : B") self.ajoute([u"Troisime point : ", ("point3", 5, Point_generique)], u"Entrez ici un 3e point du cercle. Exemple : C") self.finalise() class DialogueArcCercle(Dialogue): objet = Arc_cercle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Centre du cercle :", ("centre", 5, Point_generique)], u"Entrez ici le centre du cercle. Exemple : O") self.ajoute([u"Premier point de l'arc : ", ("point1", 5, Point_generique)], u"Entrez le 1er point de l'arc. L'arc est parcouru dans le sens direct. Exemple A") self.ajoute([u"Deuxime point : ", ("point2", 5, Point_generique)], u"Entrez un 2e point. Il ne sera pas forcment sur l'arc. Exemple : B") self.finalise() class DialogueArcPoints(Dialogue): objet = Arc_points def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Premier point de l'arc :", ("point1", 5, Point_generique)], u"Entrez ici une extrmit de l'arc. Exemple : A") self.ajoute([u"Deuxime point : ", ("point2", 5, Point_generique)], u"Entrez un point de l'arc, distinct des extrmits. Exemple : B") self.ajoute([u"Troisime point : ", ("point3", 5, Point_generique)], u"Entrez ici l'autre extrmit de l'arc. Exemple : C") self.finalise() class DialogueArcOriente(Dialogue): objet = Arc_oriente def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Premier point de l'arc :", ("point1", 5, Point_generique)], u"Entrez ici l'origine l'arc orient. Exemple : A") self.ajoute([u"Deuxime point : ", ("point2", 5, Point_generique)], u"Entrez un point de l'arc, distinct des extrmits. Exemple : B") self.ajoute([u"Troisime point : ", ("point3", 5, Point_generique)], u"Entrez ici le point final de l'arc orient. Exemple : C") self.finalise() class DialogueDemiCercle(Dialogue): objet = Demicercle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Donnez un diametre du demi-cercle :"]) self.ajoute([u"Crer une demi-cercle de diametre : [", ("point1", 5, Point_generique), ("point2", 5, Point_generique), "]"], u"Entrez les extremits du diamtre, dans le sens direct. Exemple : A et B") self.finalise() class DialogueDisque(Dialogue): objet = Disque def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Donnez le cercle :"]) self.ajoute([u"Crer un disque de circonfrence :", ("cercle", 5, Cercle_generique)], u"Entrez le cercle dlimitant le disque. Exemple : C") self.finalise() class DialogueParallele(Dialogue): objet = Parallele def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la parallle a :", ("droite", 5, Ligne_generique)], u"Entrez une droite. Exemple : d") self.ajoute([u"Passant par :", ("point", 5, Point_generique)], u"Entrez un point. Exemple : M") self.finalise() class DialoguePerpendiculaire(Dialogue): objet = Perpendiculaire def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la perpendiculaire a :", ("droite", 5, Ligne_generique)], u"Entrez une droite. Exemple : d") self.ajoute([u"Passant par :", ("point", 5, Point_generique)], u"Entrez un point. Exemple : M") self.finalise() class DialogueMediatrice(Dialogue): objet = Mediatrice def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la mdiatrice du segment : [", ("point1", 5, Point_generique), ("point2", 5, Point_generique), "]"], u"Entrez les extremites du segment. Exemple : A et B") self.finalise() class DialogueBissectrice(Dialogue): objet = Bissectrice def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la bissectrice de l'angle :", ("point1", 5, Point_generique), ("point2", 5, Point_generique), ("point3", 5, Point_generique)], u"Entrez le nom de l'angle, nomm par 3 points. Exemple : B A C") self.finalise() class DialogueTangente(Dialogue): objet = Tangente def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la tangente au cercle :", ("cercle", 5, Cercle_generique)], u"Entrez le nom du cercle. Exemple : Cer") self.ajoute([u"Passant par :", ("point", 5)], u"Entrez un point. Exemple : M") self.finalise() class DialogueInterDroites(Dialogue): objet = Intersection_droites def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer le point d'intersection des droites :", ("droite1", 5, Ligne_generique), "et", ("droite2", 5, Ligne_generique)], u"Entrez les noms des deux droites. Exemple : d1 et d2 ou (A B) et (C D)") self.finalise() class DialogueInterDroiteCercle(Dialogue): objet = Intersection_droite_cercle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un point d'intersection entre :", u"la droite", ("droite", 5, Ligne_generique), u"et le cercle", ("cercle", 5, Cercle_generique)], u"Entrez les noms dela droite, et du cercle. Exemple : AB et Cer") self.finalise() class DialogueInterCercles(Dialogue): objet = Intersection_cercles def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un point d'intersection des cercles :", ("cercle1", 5, Cercle_generique), "et", ("cercle2", 5, Cercle_generique)], u"Entrez les noms des deux cercles. Exemple : C1 et C2") self.finalise() class DialogueGlisseurDroite(Dialogue): objet = Glisseur_droite def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un point sur la droite :", ("droite", 5, Droite_generique)], u"Entrez le nom de la droite. Exemple : d ou (A B)") self.finalise() class DialogueGlisseurCercle(Dialogue): objet = Glisseur_cercle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un point sur le cercle :", ("cercle", 5, Cercle_generique)], u"Entrez le nom du cercle. Exemple : Cer") self.finalise() class DialogueGlisseurSegment(Dialogue): objet = Glisseur_segment def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un point sur le segment :", ("segment", 5, Segment)], u"Entrez le nom du segment. Exemple : s ou [A B]") self.finalise() class DialoguePolygone(Dialogue): objet = Polygone def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un polygone de sommets :", ("points", 10, [Point_generique])], u"Entrez les sommets du polygone. Exemple : A,B,C,D") self.finalise() def commande(self): # a cause de l'initialisation speciale de Polygone : __init__(*points) return self.champ("nom") + "=Polygone(" + self.champ("points") + ")" class DialoguePolygoneRegulier(Dialogue): objet = Polygone_regulier def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un polygone rgulier ", ("n", 5), u"sommets."], u"Entrez le nombre de sommets du polygone. Exemple : 7") self.ajoute([u"Premiers sommets", ("point1", 5, Point_generique), "et", ("point2", 5, Point_generique), u"(sens direct)."], u"Entrez le nom de deux sommets conscutifs. Exemple : A et B") self.finalise() class DialoguePolygoneRegulierCentre(Dialogue): objet = Polygone_regulier_centre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un polygone rgulier ", ("n", 5), u"sommets."], u"Entrez le nombre de sommets du polygone. Exemple : 7") self.ajoute([u"Passant par le sommet", ("sommet", 5, Point_generique), "et de centre", ("centre", 5, Point_generique)], u"Entrez le nom d'un sommet et du centre. Exemple : A et I") self.finalise() class DialogueParallelogramme(Dialogue): objet = Parallelogramme def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un paralllogramme de trois premiers sommets :", ("point1", 5, Point_generique), ",", ("point2", 5, Point_generique), "et", ("point3", 5, Point_generique)], u"Entrez les 3 premiers sommets du paralllogramme (sens direct). Exemple : A,B et C") self.finalise() class DialogueTriangle(Dialogue): objet = Triangle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer un triangle de sommets :", ("point1", 5, Point_generique), ",", ("point2", 5, Point_generique), "et", ("point3", 5, Point_generique)], u"Entrez le nom des points. Exemple : A , B et C") self.finalise() class DialogueCentre(Dialogue): objet = Centre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer le centre du cercle :", ("cercle", 5, Cercle_generique)], u"Entrez le nom du cercle. Exemple : Cer") self.finalise() class DialogueOrthocentre(Dialogue): objet = Orthocentre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer l'orthocentre du triangle :", ("triangle", 5, Triangle)], u"Entrez le nom du triangle. Exemple : ABC") self.finalise() class DialogueCentreGravite(Dialogue): objet = Centre_gravite def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer le centre de gravit du polygone :", ("polygone", 5, Polygone)], u"Entrez le nom du polygone. Exemple : ABC") self.finalise() class DialogueCentreCercleCirconscrit(Dialogue): objet = Centre_cercle_circonscrit def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer le centre du cercle circonscrit :", ("triangle", 5, Triangle)], u"Entrez le nom du triangle. Exemple : ABC") self.finalise() class DialogueCentreCercleInscrit(Dialogue): objet = Centre_cercle_inscrit def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer le centre du cercle inscrit dans :", ("triangle", 5, Triangle)], u"Entrez le nom du triangle. Exemple : ABC") self.finalise() class DialogueAngle(Dialogue): objet = Angle def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer l'angle :", ("point1", 5, Point_generique), ("point2", 5, Point_generique), ("point3", 5, Point_generique)], u"Entrez les trois sommets de l'angle. Exemple : A B C") self.finalise() class DialogueAngleOriente(Dialogue): objet = Angle_oriente def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer l'angle :", ("point1", 5, Point_generique), ("point2", 5, Point_generique), ("point3", 5, Point_generique)], u"Entrez les trois sommets de l'angle. Exemple : A B C") self.finalise() class DialogueAngleLibre(Dialogue): objet = Angle_libre def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Valeur de l'angle :", ("valeur", 5)], u"Entrez la valeur en degr ou en radian de l'angle. Exemple : pi/2, 15") self.ajoute([u"Unit (facultatif) :", ("unite", 5)], u"Entrez ventuellement l'unit. Exemple : r, d, g (degr, radian ou grad). Radian par dfaut.") self.finalise() def commande(self): # gestion du symbole "" valeur = self.champ("valeur").strip() if valeur.endswith(u""): valeur = valeur[:-1] unite = "'d'" else: unite = repr(self.champ("unite").lower().strip()) return u"%s=Angle_libre(%s, %s)" %(self.champ("nom"), valeur, unite) class DialogueAngleVectoriel(Dialogue): objet = Angle_vectoriel def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer l'angle : (", ("vecteur1", 5, Vecteur_generique), ",", ("vecteur2", 5, Vecteur_generique), ")"], u"Entrez les 2 vecteurs. Exemple : u et v, ou A>B et C>D") self.finalise() class DialogueTexte(Dialogue): objet = Texte def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Texte :", ("texte", 10)], u"Entrez ici le texte. Exemple : Bonjour!") self.ajoute([u"Coordonnes du texte :"], u"Entrez les coordonnes de votre nouveau texte.") self.ajoute([u"Abscisse :", ("x", 10)], u"Entrez ici l'abscisse du texte. Exemple : 3.25") self.ajoute([u"Ordonnee :", ("y", 10)], u"Entrez ici l'ordonne du texte. Exemple : -5") self.finalise() def commande(self): # Le champ texte doit etre converti... return "%s=Texte(%s,%s,%s)" %(self.champ("nom"), repr_str(self.champ("texte")), (self.champ("x") or "None"), (self.champ("y") or "None")) class DialogueRotation(Dialogue): objet = Rotation def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la rotation d'angle ", ("angle", 5, Angle_generique), " et de centre ", ("centre", 5, Point_generique)], u"Entrez l'angle. Exemple : a, 60, pi/2. Puis le centre. Exemple : A") self.ajoute([u"Unit :", ("unite", 5)], u"Entrez ventuellement l'unit. Exemple : r, d, g (degr, radian ou grad). Radian par dfaut.") self.finalise() def commande(self): # gestion du symbole "" angle = self.champ("angle").strip() if angle.endswith(u""): angle = angle[:-1] unite = "'d'" else: unite = self.champ("unite") return u"%s=Rotation(%s, %s, %s)" %(self.champ("nom"), self.champ("centre"), angle, unite) class DialogueSymetrieCentrale(Dialogue): objet = Symetrie_centrale def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la symtrie de centre : ", ("centre", 5, Point_generique)], u"Entrez un point. Exemple : A") #self.ajoute([u"Unit :", ("unite", 5)], u"Entrez ventuellement l'unit. Exemple : r, d, g (degr, radian ou grad). Radian par dfaut.") self.finalise() class DialogueTranslation(Dialogue): objet = Translation def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la translation de vecteur : ", ("vecteur", 5, Vecteur_generique)], u"Entrez un vecteur. Exemple : u, (1, 0), A>B") self.finalise() class DialogueReflexion(Dialogue): objet = Reflexion def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la rflexion d'axe : ", ("droite", 5, Ligne_generique)], u"Entrez l'axe de la symtrie. Exemple : d, (A B)") self.finalise() class DialogueHomothetie(Dialogue): objet = Homothetie def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer l'homothtie de centre : ", ("centre", 5, Point_generique), u" et de rapport ", ("rapport", 5, Variable)], u"Entrez un point (exemple: A) et un nombre (exemple: k, 3).") self.finalise() class DialogueInterpolationLineaire(Dialogue): objet = Interpolation_lineaire def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Interpoler linairement les points :", ("points", 20, [Point_generique])], u"Entrez les points par lesquels la courbe doit passer. Exemple: A,B,C,D,E") self.ajoute([u"Extrmits comprises :", ("debut", 5), ("fin", 5)], u"Indiquez si l'extrmit de dbut et de fin sont comprises. Exemple: o (ou oui), n (ou non)") self.finalise() def commande(self): # a cause de l'initialisation speciale de Polygone : __init__(*points) commande = self.champ("nom") + "=Interpolation_lineaire(" + self.champ("points") debut = self.champ("debut").lower() if debut in ("o", "oui", "y", "yes", "true"): commande += ", debut=True" elif debut in ("n", "non", "no", "false"): commande += ", debut=False" fin = self.champ("fin").lower() if fin in ("o", "oui", "y", "yes", "true"): commande += ", fin=True" elif fin in ("n", "non", "no", "false"): commande += ", fin=False" return commande + ")" class DialogueInterpolationQuadratique(Dialogue): objet = Interpolation_quadratique def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Interpolation quadratique des points :", ("points", 20, [Point_generique])], u"Entrez les points par lesquels la courbe doit passer. Exemple: A,B,C,D,E") self.ajoute([u"Extrmits comprises :", ("debut", 5), ("fin", 5)], u"Indiquez si l'extrmit de dbut et de fin sont comprises. Exemple: o (ou oui), n (ou non)") self.finalise() def commande(self): # a cause de l'initialisation speciale de Polygone : __init__(*points) commande = self.champ("nom") + "=Interpolation_quadratique(" + self.champ("points") debut = self.champ("debut").lower() if debut in ("o", "oui", "y", "yes", "true"): commande += ", debut=True" elif debut in ("n", "non", "no", "false"): commande += ", debut=False" fin = self.champ("fin").lower() if fin in ("o", "oui", "y", "yes", "true"): commande += ", fin=True" elif fin in ("n", "non", "no", "false"): commande += ", fin=False" return commande + ")" class DialogueVariable(Dialogue): objet = Variable def __init__(self, parent): Dialogue.__init__(self, parent) self.ajoute([u"Crer la variable de valeur : ", ("valeur", 15, Objet)], u"Entrez une valeur, entre guillemets pour une valeur 'lie' (consultez l'aide). Exemple : 'A.x+1'") self.finalise() class DialogueImage(Dialogue): def __init__(self, parent): Dialogue.__init__(self, parent, u"Crer l'image d'un objet par une transformation") self.ajoute([u"Nom de l'objet image : ", ("nom", 15, Objet)], u"Entrez le nom de l'objet que vous voulez crer. Exemple : M, d.") self.ajoute([u"Objet de dpart : ", ("objet", 15, Objet)], u"Entrez le nom de l'antcdent. Exemple : A, d, [A B]") self.ajoute([u"Transformation : ", ("transformation", 15, Objet)], u"Entrez la transformation (rotation, symtrie, etc...). Exemple : r, Rotation(O,pi/2)") self.finalise() def commande(self): return self.champ("nom") + "=" + self.champ("transformation") + "(" + self.champ("objet") + ")" ############################################################################### class DialogueFenetre(Dialogue): def __init__(self, parent): Dialogue.__init__(self, parent, u"Rgler la fentre d'affichage") fen = self.onglet_actuel.canvas.fenetre self.ajoute([u"Entrez les valeurs extrmales de la fentre d'affichage."]) self.ajoute([u"Xmin :", ("xmin", 10, None, 1, round(fen[0], 4))], u"Abscisse minimale. Exemple : -5") self.ajoute([u"Xmax :", ("xmax", 10, None, 1, round(fen[1], 4))], u"Abscisse maximale. Exemple : 5") self.ajoute([u"Ymin :", ("ymin", 10, None, 1, round(fen[2], 4))], u"Ordonne minimale. Exemple : -5") self.ajoute([u"Ymax :", ("ymax", 10, None, 1, round(fen[3], 4))], u"Ordonne maximale. Exemple : 5") self.finalise() btn = wx.Button(self, -1, u" Dfaut ") btn.Bind(wx.EVT_BUTTON, self.EvtRestaurer) btn.SetHelpText(u"Restaurer le rglage par dfaut de la fentre.") self.box.Add(btn, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) self.sizer.Fit(self) def commande(self): return "fenetre = " + self.champs["xmin"].GetValue() + "," + self.champs["xmax"].GetValue() + "," + self.champs["ymin"].GetValue() + "," + self.champs["ymax"].GetValue() def EvtRestaurer(self, event = None): self.champs["xmin"].SetValue(str(round(param.fenetre[0], 4))) self.champs["xmax"].SetValue(str(round(param.fenetre[1], 4))) self.champs["ymin"].SetValue(str(round(param.fenetre[2], 4))) self.champs["ymax"].SetValue(str(round(param.fenetre[3], 4))) class DialogueReperage(Dialogue): def __init__(self, parent): Dialogue.__init__(self, parent, u"Personnaliser le repre") gradu = self.onglet_actuel.canvas.gradu repere = self.onglet_actuel.canvas.repere self.ajoute([u"Entrez le repre : (", ("origine", 5, None, 1, repere[0]), "; ", ("x", 5, None, 1, repere[1]), "; ", ("y", 5, None, 1, repere[2]), ")"], u"Entrez le repre qui sera affich. Exemples: (O;i;j), (O;I;J), (0;5;10)") self.ajoute([u"Choisissez les graduations :"]) self.ajoute([u"axe des abscisses :", ("xgradu", 10, None, 1, round(gradu[0], 4))], u"Ecart entre deux graduations en abscisse. Exemple : 2") self.ajoute([u"axe des ordonnes :", ("ygradu", 10, None, 1, round(gradu[1], 4))], u"Ecart entre deux graduations en ordonne. Exemple : 2.5") self.finalise() btn = wx.Button(self, -1, u" Dfaut ") btn.Bind(wx.EVT_BUTTON, self.EvtRestaurer) btn.SetHelpText(u"Restaurer les valeurs par dfaut.") self.box.Add(btn, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) self.sizer.Fit(self) def commande(self): return u"repere = " + ", ".join(repr_str(self.champs[nom].GetValue()) for nom in ("origine", "x", "y")) + \ u"\ngradu = " + ", ".join(self.champs[nom].GetValue() for nom in ("xgradu", "ygradu")) def EvtRestaurer(self, event = None): self.champs["origine"].SetValue(param.repere[0]) self.champs["x"].SetValue(param.repere[1]) self.champs["y"].SetValue(param.repere[2]) self.champs["xgradu"].SetValue(str(round(param.gradu[0], 4))) self.champs["ygradu"].SetValue(str(round(param.gradu[1], 4))) ################################################################################ class SupprimerObjet(wx.lib.dialogs.MultipleChoiceDialog): def __init__(self, parent): liste = parent.onglet_actuel.feuille_actuelle.inventaire() wx.lib.dialogs.MultipleChoiceDialog.__init__(self, parent, u"Slectionnez les objets supprimer", u"Supprimer", liste, size=(250,400)) class EditerObjet(wx.lib.dialogs.MultipleChoiceDialog): def __init__(self, parent): liste = parent.onglet_actuel.feuille_actuelle.inventaire() wx.lib.dialogs.MultipleChoiceDialog.__init__(self, parent, u"Slectionnez les objets editer", u"Editer", liste, size=(250,400)) wxgeometrie-0.133.2.orig/wxgeometrie/GUI/proprietes_objets.py0000644000175000017500000006565212014170666024474 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Fenetres # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import wx from wx.lib.colourselect import EVT_COLOURSELECT, ColourSelect from matplotlib.colors import colorConverter as colorConverter from .wxlib import MyMiniFrame from .. import param from ..pylib import print_error, debug, advanced_split from ..geolib.constantes import NOM, FORMULE, TEXTE, RIEN from ..geolib.routines import nice_display class ProprietesAffichage(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.parent = parent self.panel = self.parent.parent.panel self.canvas = self.panel.canvas self.islabel = self.parent.parent.islabel self.objets = parent.objets self.sizer = wx.BoxSizer(wx.VERTICAL) self.changements = {} # ce dictionnaire contiendra tous les styles modifis encadre = wx.StaticBoxSizer(wx.StaticBox(self, -1, u"Mode d'affichage"), wx.HORIZONTAL) if not self.islabel: objets = [objet for objet in self.objets if objet.style("fixe") is not None] if objets: cb1 = wx.CheckBox(self, -1, u"Objet fixe", style = wx.CHK_3STATE) cb1.Bind(wx.EVT_CHECKBOX, self.EvtFixe) encadre.Add(cb1, 0, wx.ALL, 5) fixe = [objet.style("fixe") is True for objet in objets] if not any(fixe): etat = wx.CHK_UNCHECKED elif all(fixe): etat = wx.CHK_CHECKED else: etat = wx.CHK_UNDETERMINED cb1.Set3StateValue(etat) objets = [objet for objet in self.objets if objet.style("visible") is not None] if objets: cb2 = wx.CheckBox(self, -1, u"Objet visible", style = wx.CHK_3STATE) cb2.Bind(wx.EVT_CHECKBOX, self.EvtVisible) encadre.Add(cb2, 0, wx.ALL, 5) visible = [objet.style("visible") is True for objet in objets] if not any(visible): etat = wx.CHK_UNCHECKED elif all(visible): etat = wx.CHK_CHECKED else: etat = wx.CHK_UNDETERMINED cb2.Set3StateValue(etat) objets = [objet for objet in self.objets if objet.style("trace") is not None] if objets: cb3 = wx.CheckBox(self, -1, u"Laisser une trace", style = wx.CHK_3STATE) cb3.Bind(wx.EVT_CHECKBOX, self.EvtTrace) encadre.Add(cb3, 0, wx.ALL, 5) trace = [objet.style("trace") is True for objet in objets] if not any(trace): etat = wx.CHK_UNCHECKED elif all(trace): etat = wx.CHK_CHECKED else: etat = wx.CHK_UNDETERMINED cb3.Set3StateValue(etat) encadre1 = wx.StaticBoxSizer(wx.StaticBox(self, -1, u"Etiquette"), wx.VERTICAL) if not self.islabel: ligne = wx.BoxSizer(wx.HORIZONTAL) if len(self.objets) == 1: etiquette = wx.TextCtrl(self, value = self.objets[0].style("label"), size=wx.Size(200, -1)) self.Bind(wx.EVT_TEXT, self.EvtEtiquette, etiquette) ligne.Add(etiquette, 0, wx.ALL, 5) if [objet for objet in self.objets if objet.etiquette is not None]: editer = wx.Button(self, label = u"Style") editer.Bind(wx.EVT_BUTTON, self.EvtLabelStyle) ligne.Add(editer, 0, wx.ALL, 5) encadre1.Add(ligne, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("legende") is not None] if objets: leg = objets[0].style("legende") legende = wx.BoxSizer(wx.HORIZONTAL) self.radio_nom = wx.RadioButton(self, -1, "Nom", style = wx.RB_GROUP) self.radio_nom.SetValue(0) self.radio_etiquette = wx.RadioButton(self, -1, u"Texte") self.radio_etiquette.SetValue(0) self.radio_formule = wx.RadioButton(self, -1, u"Formule") self.radio_formule.SetValue(0) self.radio_aucun = wx.RadioButton(self, -1, u"Aucun") self.radio_aucun.SetValue(0) if all(objet.style("legende") == leg for objet in objets): if leg == NOM: self.radio_nom.SetValue(1) elif leg == TEXTE: self.radio_etiquette.SetValue(1) elif leg == FORMULE: self.radio_formule.SetValue(1) elif leg == RIEN: self.radio_aucun.SetValue(1) self.Bind(wx.EVT_RADIOBUTTON, self.EvtLegende, self.radio_nom) self.Bind(wx.EVT_RADIOBUTTON, self.EvtLegende, self.radio_etiquette) self.Bind(wx.EVT_RADIOBUTTON, self.EvtLegende, self.radio_formule) self.Bind(wx.EVT_RADIOBUTTON, self.EvtLegende, self.radio_aucun) legende.Add(self.radio_nom, 0, wx.ALL, 5) legende.Add(self.radio_etiquette, 0, wx.ALL, 5) legende.Add(self.radio_formule, 0, wx.ALL, 5) legende.Add(self.radio_aucun, 0, wx.ALL, 5) encadre1.Add(wx.StaticText(self, -1, u"Afficher : "), 0, wx.ALL,5) encadre1.Add(legende, 0, wx.ALL, 5) encadre2 = wx.StaticBoxSizer(wx.StaticBox(self, -1, u"Styles"), wx.VERTICAL) objets = [objet for objet in self.objets if objet.style("style") is not None] # on ne peut regler les styles simultanement que pour des objets de meme categorie categorie = objets and objets[0].style("categorie") or None if objets and categorie and all(objet.style("categorie") == categorie for objet in objets): choix = wx.BoxSizer(wx.HORIZONTAL) choix.Add(wx.StaticText(self, -1, u"Style de l'objet : "), 0, wx.ALL,5) #categorie = objets[0].style("categorie") or "lignes" self.liste_styles = getattr(param, "styles_de_" + categorie, []) self.style = wx.Choice(self, -1, (100, 50), choices = self.liste_styles) self.Bind(wx.EVT_CHOICE, self.EvtStyle, self.style) style = objets[0].style("style") if style in self.liste_styles and all(objet.style("style") == style for objet in objets): self.style.SetSelection(self.liste_styles.index(style)) # on slectionne le style actuel choix.Add(self.style, 0, wx.ALL, 5) encadre2.Add(choix, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("hachures") is not None] if objets: choix = wx.BoxSizer(wx.HORIZONTAL) choix.Add(wx.StaticText(self, -1, u"Style des hchures : "), 0, wx.ALL,5) self.types_de_hachures = getattr(param, "types_de_hachures", []) self.hachures = wx.Choice(self, -1, (100, 50), choices = self.types_de_hachures) self.Bind(wx.EVT_CHOICE, self.EvtHachures, self.hachures) hachures = objets[0].style("hachures") if hachures in self.types_de_hachures and all(objet.style("hachures") == hachures for objet in objets): self.hachures.SetSelection(self.types_de_hachures.index(hachures)) # on slectionne les hachures actuelles choix.Add(self.hachures, 0, wx.ALL, 5) encadre2.Add(choix, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("famille") is not None] categorie = objets and objets[0].style("categorie") or None if objets and categorie and all(objet.style("categorie") == categorie for objet in objets): choix = wx.BoxSizer(wx.HORIZONTAL) choix.Add(wx.StaticText(self, -1, "Police : "), 0, wx.ALL,5) #categorie = self.objet.style("categorie") or "lignes" self.liste_familles = getattr(param, "familles_de_" + categorie, []) self.famille = wx.Choice(self, -1, (100, 50), choices = self.liste_familles) self.Bind(wx.EVT_CHOICE, self.EvtFamille, self.famille) famille = objets[0].style("famille") if famille in self.liste_familles and all(objet.style("famille") == famille for objet in objets): self.famille.SetSelection(self.liste_familles.index(famille)) # on slectionne la famille actuelle choix.Add(self.famille, 0, wx.ALL, 5) encadre2.Add(choix, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("couleur") is not None] if objets: couleur = objets[0].style("couleur") choix = wx.BoxSizer(wx.HORIZONTAL) choix.Add(wx.StaticText(self, -1, u"Couleur de l'objet : "), 0, wx.ALL,5) if all(objet.style("couleur") == couleur for objet in objets): couleur = colorConverter.to_rgb(couleur) couleur = tuple(int(255*i) for i in couleur) # conversion du format matplotlib au format wx else: couleur = self.GetBackgroundColour() b = ColourSelect(self, -1, colour = couleur) b.Bind(EVT_COLOURSELECT, self.OnSelectColour) choix.Add(b, 0, wx.ALL, 5) encadre2.Add(choix, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("epaisseur") is not None] if objets: epaiss = objets[0].style("epaisseur") epaisseur = wx.BoxSizer(wx.HORIZONTAL) epaisseur.Add(wx.StaticText(self, -1, u"Epaisseur (en 10e de pixels) : "), 0, wx.ALL,5) self.epaisseur = wx.SpinCtrl(self, -1, "", (30, 50)) self.epaisseur.SetRange(1,10000) if all(objet.style("epaisseur") == epaiss for objet in objets): self.epaisseur.SetValue(10*epaiss) else: self.epaisseur.SetValueString("") self.Bind(wx.EVT_TEXT, self.EvtEpaisseur, self.epaisseur) epaisseur.Add(self.epaisseur, 0, wx.ALL, 5) encadre2.Add(epaisseur, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("taille") is not None] if objets: tail = objets[0].style("taille") taille = wx.BoxSizer(wx.HORIZONTAL) taille.Add(wx.StaticText(self, -1, u"Taille (en 10e de pixels) : "), 0, wx.ALL,5) self.taille = wx.SpinCtrl(self, -1, "", (30, 50)) self.taille.SetRange(1,10000) if all(objet.style("taille") == tail for objet in objets): self.taille.SetValue(10*tail) else: self.taille.SetValueString("") self.Bind(wx.EVT_TEXT, self.EvtTaille, self.taille) taille.Add(self.taille, 0, wx.ALL, 5) encadre2.Add(taille, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("position") is not None] if objets: pos = objets[0].style("position") position = wx.BoxSizer(wx.HORIZONTAL) position.Add(wx.StaticText(self, -1, u"Position de la flche : "), 0, wx.ALL,5) self.position = wx.SpinCtrl(self, -1, "", (30, 50)) self.position.SetRange(0, 100) if all(objet.style("position") == pos for objet in objets): self.position.SetValue(100*pos) else: self.position.SetValueString("") self.Bind(wx.EVT_TEXT, self.EvtPosition, self.position) position.Add(self.position, 0, wx.ALL, 5) encadre2.Add(position, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("angle") is not None] if objets: ang = objets[0].style("angle") angle = wx.BoxSizer(wx.HORIZONTAL) angle.Add(wx.StaticText(self, -1, u"Angle (en degr) : "), 0, wx.ALL,5) self.angle = wx.SpinCtrl(self, -1, "", (30, 50)) self.angle.SetRange(0, 360) if all(objet.style("angle") == ang for objet in objets): self.angle.SetValue(ang) else: self.angle.SetValueString("") self.Bind(wx.EVT_TEXT, self.EvtAngle, self.angle) angle.Add(self.angle, 0, wx.ALL, 5) encadre2.Add(angle, 0, wx.ALL, 5) objets = [objet for objet in self.objets if objet.style("double_fleche") is not None] if objets: cb4 = wx.CheckBox(self, -1, u"Flche double", style = wx.CHK_3STATE) cb4.Bind(wx.EVT_CHECKBOX, self.EvtFlecheDouble) encadre.Add(cb4, 0, wx.ALL, 5) double = [objet.style("double_fleche") is True for objet in objets] if not any(double): etat = wx.CHK_UNCHECKED elif all(double): etat = wx.CHK_CHECKED else: etat = wx.CHK_UNDETERMINED cb4.Set3StateValue(etat) objets = [objet for objet in self.objets if objet.style("codage") is not None] # on ne peut regler les codages simultanement que pour des objets de meme categorie categorie = objets and objets[0].style("categorie") or None if objets and categorie and all(objet.style("categorie") == categorie for objet in objets): choix = wx.BoxSizer(wx.HORIZONTAL) choix.Add(wx.StaticText(self, -1, "Codage : "), 0, wx.ALL,5) #categorie = objets[0].style("categorie") or "lignes" self.liste_codages = getattr(param, "codage_des_" + categorie, []) self.codage = wx.Choice(self, -1, (100, 50), choices = self.liste_codages) self.Bind(wx.EVT_CHOICE, self.EvtCodage, self.codage) codage = objets[0].style("codage") if codage in self.liste_codages and all(objet.style("codage") == codage for objet in objets): self.codage.SetSelection(self.liste_codages.index(codage)) # on slectionne le codage actuel choix.Add(self.codage, 0, wx.ALL, 5) encadre2.Add(choix, 0, wx.ALL, 5) boutons = wx.BoxSizer(wx.HORIZONTAL) ok = wx.Button(self, wx.ID_OK) ok.Bind(wx.EVT_BUTTON, self.EvtOk) boutons.Add(ok, 0, wx.ALL, 5) appliquer = wx.Button(self, label = u"Appliquer") appliquer.Bind(wx.EVT_BUTTON, self.EvtAppliquer) boutons.Add(appliquer, 0, wx.ALL, 5) if not self.islabel: supprimer = wx.Button(self, label = u"Supprimer") supprimer.Bind(wx.EVT_BUTTON, self.EvtSupprimer) boutons.Add(supprimer, 0, wx.ALL, 5) annuler = wx.Button(self, label = u"Annuler") annuler.Bind(wx.EVT_BUTTON, self.EvtAnnuler) boutons.Add(annuler, 0, wx.ALL, 5) if encadre.GetChildren(): # ne pas afficher un cadre vide ! self.sizer.Add(encadre, 0, wx.ALL, 5) else: encadre.GetStaticBox().Destroy() if encadre1.GetChildren(): self.sizer.Add(encadre1, 0, wx.ALL, 5) else: encadre1.GetStaticBox().Destroy() if encadre2.GetChildren(): self.sizer.Add(encadre2, 0, wx.ALL, 5) else: encadre2.GetStaticBox().Destroy() self.sizer.Add(boutons, 0, wx.ALL, 5) self.SetSizerAndFit(self.sizer) self.parent.parent.dim1 = self.sizer.CalcMin().Get() def EvtLegende(self, event): radio = event.GetEventObject() if radio is self.radio_nom: self.changements["legende"] = NOM elif radio is self.radio_etiquette: self.changements["legende"] = TEXTE elif radio is self.radio_formule: self.changements["legende"] = FORMULE else: self.changements["legende"] = RIEN def EvtFixe(self, event): self.changements["fixe"] = event.IsChecked() def EvtVisible(self, event): self.changements["visible"] = event.IsChecked() def EvtFlecheDouble(self, event): self.changements["double_fleche"] = event.IsChecked() def EvtTrace(self, event): self.changements["trace"] = event.IsChecked() def EvtEtiquette(self, event): self.changements["label"] = event.GetString() def OnSelectColour(self, event): couleur = tuple(i/255 for i in event.GetValue().Get()) # conversion du format wx au format matplotlib self.changements["couleur"] = couleur def EvtStyle(self, event): self.changements["style"] = self.liste_styles[event.GetSelection()] def EvtHachures(self, event): self.changements["hachures"] = self.types_de_hachures[event.GetSelection()] def EvtCodage(self, event): self.changements["codage"] = self.liste_codages[event.GetSelection()] def EvtFamille(self, event): self.changements["famille"] = self.liste_familles[event.GetSelection()] def EvtOk(self, event): self.EvtAppliquer(event) self.EvtAnnuler(event) def EvtAppliquer(self, event): with self.canvas.geler_affichage(actualiser=True, sablier=True): try: for objet in self.objets: changements = self.changements.copy() for key in changements.copy(): if objet.style(key) is None: # le style n'a pas de sens pour l'objet changements.pop(key) if self.islabel: self.canvas.executer(u"%s.etiquette.style(**%s)" %(objet.parent.nom, changements)) else: self.canvas.executer(u"%s.style(**%s)" %(objet.nom, changements)) except: print_error() def EvtSupprimer(self, event): with self.canvas.geler_affichage(actualiser=True, sablier=True): for objet in self.objets: self.canvas.executer(u"del %s" %objet.nom) self.EvtAnnuler(event) def EvtAnnuler(self, event): # Ce qui suit corrige un genre de bug bizarre de wx: quand une fentre de slection de couleur a t affiche, la fentre principale passe au second plan la fermeture de la fentre de proprits ?!? (ce qui est trs dsagrable ds qu'un dossier est ouvert dans l'explorateur, par exemple !) self.parent.parent.fenetre_principale.Raise() self.parent.parent.Close() # fermeture de la frame def EvtLabelStyle(self, event): win = Proprietes(self.parent, [objet.etiquette for objet in self.objets if objet.etiquette is not None], True) win.Show(True) def EvtEpaisseur(self, event): self.changements["epaisseur"] = self.epaisseur.GetValue()/10 def EvtTaille(self, event): self.changements["taille"] = self.taille.GetValue()/10 def EvtAngle(self, event): self.changements["angle"] = self.angle.GetValue() def EvtPosition(self, event): self.changements["position"] = self.position.GetValue()/100 class UpdatableTextCtrl(wx.TextCtrl): def __init__(self, parent, attribut, editable = False): wx.TextCtrl.__init__(self, parent, value = "", size=wx.Size(300, -1)) self.parent = parent self.attribut = attribut self.SetEditable(editable) self.actualiser() def formater(self, valeur): if self.parent.objet.existe: if isinstance(valeur, (str, unicode)): return valeur elif valeur is None: return u"Valeur non dfinie." elif hasattr(valeur, '__iter__'): return " ; ".join(self.formater(elt) for elt in valeur) return nice_display(valeur) else: return u"L'objet n'est pas dfini." def actualiser(self): self.SetValue(self.formater(getattr(self.parent.objet, self.attribut))) class ProprietesInfos(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.parent = parent self.objets = parent.objets self.sizer = wx.BoxSizer(wx.VERTICAL) self.infos = wx.StaticBoxSizer(wx.StaticBox(self, -1, u"Informations"), wx.VERTICAL) if len(self.objets) == 1: self.objet = self.objets[0] else: self.objet = None # cela n'a pas vraiment de sens d'afficher une longueur pour 3 segments differents par exemple... self.textes = [] proprietes = ("aire", "centre", "coordonnees", "rayon", "longueur", "perimetre", "norme", "sens") for propriete in proprietes: try: self.ajouter(propriete) except: debug(u"Erreur lors de la lecture de la proprit '%s' de l'objet %s." %(propriete, self.objet.nom)) print_error() self.ajouter("equation_formatee", u"Equation cartsienne") if self.textes: self.sizer.Add(self.infos, 0, wx.ALL, 5) actualiser = wx.Button(self, label = u"Actualiser") actualiser.Bind(wx.EVT_BUTTON, self.EvtActualiser) self.sizer.Add(actualiser, 0, wx.ALL, 15) else: self.sizer.Add(wx.StaticText(self, -1, str(len(self.objets)) + u" objets slectionns."), 0, wx.ALL, 15) self.infos.GetStaticBox().Destroy() del self.infos self.SetSizerAndFit(self.sizer) self.parent.parent.dim2 = self.sizer.CalcMin().Get() def ajouter(self, propriete, nom = None): if nom is None: nom = propriete.replace("_", " ").strip().capitalize() nom += " : " if hasattr(self.objet, propriete): self.infos.Add(wx.StaticText(self, -1, nom), 0, wx.ALL, 5) txt = UpdatableTextCtrl(self, propriete) self.infos.Add(txt, 0, wx.ALL, 5) self.textes.append(txt) def EvtActualiser(self, event = None): for txt in self.textes: txt.actualiser() class ProprietesAvance(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) self.parent = parent self.objets = parent.objets self.panel = self.parent.parent.panel self.canvas = self.parent.parent.canvas self.islabel = self.parent.parent.islabel self.sizer = wx.BoxSizer(wx.VERTICAL) if len(self.objets) is 1: self.objet = self.objets[0] box = wx.StaticBoxSizer(wx.StaticBox(self, -1, u"Style de l'objet"), wx.VERTICAL) box.Add(wx.StaticText(self, label = u"Attention, ne modifiez ce contenu que si vous savez ce que vous faites."), 0, wx.ALL, 5) self.avance = wx.TextCtrl(self, size=wx.Size(350, 200), style = wx.TE_MULTILINE) self.actualiser() box.Add(self.avance, 0, wx.ALL, 5) self.sizer.Add(box, 0, wx.ALL, 5) ok = wx.Button(self, id = wx.ID_OK) appliquer = wx.Button(self, label = u"Appliquer") actualiser = wx.Button(self, label = u"Actualiser") ok.Bind(wx.EVT_BUTTON, self.EvtOk) appliquer.Bind(wx.EVT_BUTTON, self.EvtAppliquer) actualiser.Bind(wx.EVT_BUTTON, self.actualiser) boutons = wx.BoxSizer(wx.HORIZONTAL) boutons.Add(ok, 0, wx.ALL, 5) boutons.Add(appliquer, 0, wx.ALL, 5) boutons.Add(actualiser, 0, wx.ALL, 5) self.sizer.Add(boutons, 0, wx.ALL, 10) self.SetSizerAndFit(self.sizer) self.parent.parent.dim3 = self.sizer.CalcMin().Get() def EvtOk(self, event): self.EvtAppliquer(event) self.parent.parent.fenetre_principale.Raise() self.parent.parent.Close() # fermeture de la frame def EvtAppliquer(self, event): txt = self.avance.GetValue().split('\n') dico = "{" for ligne in txt: key, value = ligne.split(":", 1) key = "'" + key.strip() + "':" dico += key + value + "," dico += "}" if self.islabel: self.canvas.executer(u"%s.etiquette.style(**%s)" %(self.objet.parent.nom, dico)) else: self.canvas.executer(u"%s.style(**%s)" %(self.objet.nom, dico)) def actualiser(self, event = None): items = (txt.split(':', 1) for txt in advanced_split(str(self.objet.style())[1:-1], ",")) self.avance.SetValue('\n'.join(sorted(key.strip()[1:-1] + ':' + value for key, value in items))) class OngletsProprietes(wx.Notebook): def __init__(self, parent): self.parent = parent self.objets = parent.objets wx.Notebook.__init__(self, parent) self.affichage = ProprietesAffichage(self) self.AddPage(self.affichage, u"Affichage") self.infos = ProprietesInfos(self) self.AddPage(self.infos, u"Informations") self.avance = ProprietesAvance(self) self.AddPage(self.avance, u"Avanc") class Proprietes(MyMiniFrame): def __init__(self, parent, objets, islabel = False): u"Le paramtre 'label' indique si l'objet diter est un label" print "OBJETS:" print objets print unicode(objets[0]) print repr(objets[0]) print objets[0].__class__ print isinstance(objets[0], str) objets[0].titre_complet("du", False) self.parent = parent self.islabel = islabel self.fenetre_principale = self while hasattr(self.fenetre_principale, "parent"): # detection de la fenetre principale de WxGeometrie. self.fenetre_principale = self.fenetre_principale.parent self.panel = self.fenetre_principale.onglets.onglet_actuel self.canvas = self.panel.canvas self.objets = objets if self.islabel: titre = u"du label" else: if len(objets) == 0: self.Close() if len(objets) == 1: titre = objets[0].titre_complet("du", False) else: titre = u"des objets" # wx.MiniFrame.__init__(self, parent, -1, u"Proprits " + titre, style=wx.DEFAULT_FRAME_STYLE | wx.TINY_CAPTION_HORIZ) MyMiniFrame.__init__(self, parent, u"Proprits " + titre) self.SetExtraStyle(wx.WS_EX_BLOCK_EVENTS ) self.onglets = OngletsProprietes(self) self.SetSize(wx.Size(*(max(dimensions) + 50 for dimensions in zip(self.dim1, self.dim2, self.dim3)))) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/0000755000175000017500000000000012014170666021157 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/geolib/polygones.py0000644000175000017500000012340012014170666023550 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from random import uniform, normalvariate, randint from math import pi, cos, sin from cmath import log as clog, exp as cexp from sympy import arg as sarg, exp as sexp, I from .intersections import Intersection_droites from .labels import Label_polygone from .lignes import Segment, Perpendiculaire, Bissectrice, Droite from .objet import Arguments, Argument, ArgumentNonModifiable, Ref, Objet, \ contexte, issympy, FILL_STYLES, RE_NOM_OBJET, TYPES_ENTIERS from .points import Point_generique, Point, Point_equidistant, Point_pondere, \ Barycentre from .transformations import Rotation from .routines import point_dans_polygone, distance, radian from ..pylib import is_in, print_error from .. import param class Cote(Segment): u"""Un cot. Un cot d'un polygone, reliant le point numero 'n' au point numero 'n + 1'. Note: n commence 0. L'objet est cr automatiquement lors de la cration du polygone. De plus, si l'objet est supprim, le polygone est automatiquement supprim.""" _style_defaut = param.cotes _prefixe_nom = "c" polygone = __polygone = ArgumentNonModifiable("Polygone_generique") n = __n = ArgumentNonModifiable("int") def __init__(self, polygone, n, **styles): self.__polygone = polygone self.__n = n Segment.__init__(self, polygone._Polygone_generique__sommets[n], polygone._Polygone_generique__sommets[(n + 1)%len(polygone._Polygone_generique__sommets)], **styles) def _modifier_hierarchie(self, valeur = None): # Voir commentaires pour Sommet._modifier_hierarchie N = len(self.__polygone._Polygone_generique__points) Objet._modifier_hierarchie(self, self.__polygone._hierarchie + (self.__n + N + 2)/(2*N + 2)) def _update(self, objet): u"""Pseudo mise jour: seul un objet identique est accept. Cela sert pour les objets crs automatiquement, qui peuvent tre enregistrs deux fois dans la feuille.""" if isinstance(objet, Cote) and self.__polygone is objet._Cote__polygone and self.__n == objet._Cote__n : self.style(**objet.style()) else: raise RuntimeError class Sommet(Point_generique): u"""Un sommet. Le nime sommet d'un polygone. Note: n commence 0. L'objet est cr automatiquement lors de la cration du polygone. De plus, si l'objet est supprim, le polygone est automatiquement supprim.""" _prefixe_nom = "S" # Un sommet peut-tre li un point, c'est--dire avoir toujours les mmes coordonnes que ce point _point_lie = None polygone = __polygone = ArgumentNonModifiable("Polygone_generique") n = __n = ArgumentNonModifiable("int") def __init__(self, polygone, n, **styles): self.__polygone = polygone self.__n = n Point_generique.__init__(self, **styles) def _get_coordonnees(self): return self.__polygone._Polygone_generique__points[self.__n].coordonnees def _set_coordonnees(self, x, y): if self._point_lie is not None: self._point_lie._set_coordonnees(x, y) def _modifier_hierarchie(self, valeur = None): # Pour les sauvegardes par exemple, il est prfrable que les sommets, puis les cots, # apparaissent juste aprs la construction du polygone ; ils doivent occuper des places conscutives dans la hirarchie. # Par exemple, si le polygone a 4 sommets, et si sa place dans la hierarchie est 18, ses trois sommets # auront comme valeur hierarchique, dans l'ordre, 18.1, 18.2, 18.3 et 18.4, # et ses cots auront pour valeur hirarchique 18.6, 18.7, 18.8, 18.9. N = len(self.__polygone._Polygone_generique__points) Objet._modifier_hierarchie(self, self.__polygone._hierarchie + (self.__n + 1)/(2*N + 2)) def _update(self, objet): u"""Pseudo mise jour: seul un objet identique est accept. Cela sert pour les objets crs automatiquement, qui peuvent tre enregistrs deux fois dans la feuille.""" if isinstance(objet, Sommet) and self.__polygone is objet._Sommet__polygone and self.__n == objet._Sommet__n : self.style(**objet.style()) else: raise RuntimeError def _lier_sommet(self, point): u"""Lie le sommet un point, en le rendant dplaable.""" self._point_lie = point self.style(couleur = "m") def _deplacable(self, *args, **kw): return self._point_lie is not None _deplacable = _modifiable = property(_deplacable, _deplacable) class Polygone_generique(Objet): u"""Un polygone gnrique. Classe mre de tous les polygones.""" _style_defaut = param.polygones _prefixe_nom = "p" points = __points = Arguments("Point_generique") def __init__(self, *points, **styles): n = len(points) self.__points = points = tuple(Ref(obj) for obj in points) self.__centre = Barycentre(*(Point_pondere(point, 1) for point in points)) Objet.__init__(self, **styles) self.etiquette = Label_polygone(self) self.__sommets = tuple(Sommet(self, i) for i in xrange(n)) self.__cotes = tuple(Cote(self, i) for i in xrange(n)) def _affecter_coordonnees_par_defaut(self, points): u"""Affecte aux points des coordonnes par dfaut. Les coordonnes alatoires sont gnres manire ce que le polygone ait peu de chance d'tre crois, et occupe une bonne partie de la fentre d'affichage.""" xmin, xmax, ymin, ymax = self.__feuille__.fenetre x0 = (xmin + xmax)/2 y0 = (ymin + ymax)/2 rx = (xmax - xmin)/2 ry = (ymax - ymin)/2 r = min(rx, ry) liste_k = [uniform(0.5*r, 1*r) for pt in points] # Par dfaut, pour viter les polygones croiss, on construit les sommets successifs # en rayonnant dans le sens direct partir du centre de la fentre. # Nota: si toutes les valeurs de liste_t taient regroupes sur un intervalle # d'amplitude < pi, il se pourrait que le polygone soit malgr tout crois, d'o l'algorithme suivant : if len(points) == 3: liste_t = [uniform(0, pi/3), uniform(2*pi/3, pi), uniform(4*pi/3, 5*pi/3)] else: liste_t = [uniform(0, pi/2), uniform(pi/2, pi), uniform(pi, 3*pi/2), uniform(3*pi/2, 2*pi)] liste_t += [uniform(0, 2*pi) for i in xrange(len(points) - 4)] for (k, t, pt) in zip(liste_k, liste_t, points): pt._Point__x = x0 + k*cos(t) pt._Point__y = y0 + k*sin(t) def _set_feuille(self): n = len(self.__points) # On enregistre sur la feuille les arguments crs par dfauts qui doivent l'tre if hasattr(self, "_valeurs_par_defaut")\ and self._valeurs_par_defaut: # On essaie d'viter un polygone crois if len(self.__arguments__) == n: args = [arg for nom, arg in self._iter_arguments] if all(isinstance(arg, Point) for arg in args): self._affecter_coordonnees_par_defaut(args) if not self._style.has_key("_noms_"): noms = re.findall(RE_NOM_OBJET, self._nom) noms_args, args = zip(*self._iter_arguments) correspondance = True # On tente de dtecter via le nom de l'objet le nom que doit prendre chacun de ses arguments. # Par exemple, si un rectangle s'appelle ABCD, alors les points qui le constituent # doivent prendre pour noms A, B, C et D. if len(noms) == n: for i in xrange(n): if self.__points[i]._nom != "" and self.__points[i]._nom != noms[i]: correspondance = False break else: correspondance = False if correspondance: self._style["_noms_"] = {"sommets": n*[""], "cotes": n*("", )} mode = "points" for i in xrange(n): if mode == "points": if i < len(args) and is_in(args[i], self.__points): if "_" + self.__class__.__name__ + "__" + noms_args[i] in self._valeurs_par_defaut: self.__feuille__.objets[noms[i]] = args[i] else: mode = "sommets" if mode == "sommets": self._style["_noms_"]["sommets"][i] = noms[i] self._style["_noms_"]["sommets"] = tuple(self._style["_noms_"]["sommets"]) # chec du nommage intelligent : on se rabat sur des noms alatoires else: for nom_arg in self._valeurs_par_defaut: self.__feuille__.objets[''] = getattr(self, nom_arg) self._valeurs_par_defaut = [] # On rfrence automatiquement tous les cts et sommets du polygone dans la feuille. # (En particulier, en mode graphique, cela permet de faire apparaitre tous les sommets du polygone lorsque celui-ci est cr) points_feuille = self.__feuille__.objets.lister(Point_generique) noms = self._style.get("_noms_", {"sommets": n*("", ), "cotes": n*("", )}) for i in xrange(n): # On exclue les sommets qui seraient dj dans la feuille : if not is_in(self.__points[i], points_feuille): nom = noms["sommets"][i] self.__feuille__.objets[nom] = self.__sommets[i] nom = noms["cotes"][i] self.__feuille__.objets[nom] = self.__cotes[i] # Exceptionnellement, il ne faut pas faire appel la mthode Objet._set_feuille. def __repr__(self, *args, **kwargs): self.style(_noms_ = { "sommets" : tuple(sommet.nom for sommet in self.__sommets), "cotes": tuple(cote.nom for cote in self.__cotes)}) return Objet.__repr__(self, *args, **kwargs) @property def centre(self): return self.__centre centre_gravite = centre @property def sommets(self): return self.__sommets @property def cotes(self): return self.__cotes @property def equilateral(self): longueurs_cotes = tuple(cote.longueur for cote in self.__cotes) return max(longueurs_cotes) - min(longueurs_cotes) < contexte['tolerance'] @property def regulier(self): G = self.__centre # centre de gravit distance_centre = tuple(distance(G, sommet) for sommet in self.__points) return self.equilateral and self.convexe and max(distance_centre) - min(distance_centre) < contexte['tolerance'] @property def inscrit(self): u"Le polygone est-il inscrit dans un cercle ?" raise NotImplementedError @property def convexe(self): def sens_angle(A, B, C): return (C.x - B.x)*(A.y - B.y) - (A.x - B.x)*(C.y - B.y) > 0 sens_initial = None i = -2 j = -1 for k in range(len(self.__points)): A = self.__points[i] B = self.__points[j] C = self.__points[k] sens = sens_angle(A, B, C) if sens_initial == None: sens_initial = sens if sens != sens_initial: return False i = j j = k return True def _creer_figure(self): if not self._representation: self._representation = [self.rendu.polygone(), self.rendu.ligne(zorder = 1)] fill = self._representation[0] plot = self._representation[1] niveau = self.style("niveau") hachures = self.style("hachures") alpha = self.style("alpha") couleur = self.style("couleur") style = self.style("style") epaisseur = self.style("epaisseur") points = self.__points + (self.__points[0],) xy = [pt.coordonnees for pt in points] x, y = zip(*xy) fill.xy = xy plot.set_data(x, y) fill.set(alpha=alpha, hatch=hachures, edgecolor=couleur, facecolor=couleur, linewidth=epaisseur) plot.set(color=couleur, linestyle=style, linewidth=epaisseur) fill.set_linestyle(FILL_STYLES.get(self.style("style"), "solid")) fill.zorder = niveau - 0.01 plot.zorder = niveau def image_par(self, transformation): return Polygone(*(point.image_par(transformation) for point in self.__points)) def _distance_inf(self, x, y, d): xy = [self._pixel(pt) for pt in self.__points] return point_dans_polygone((x,y), xy) def _contains(self, M): for cote in self.cotes: if M in cote: return True return point_dans_polygone(tuple(M), [pt.coordonnees for pt in self.__points]) @property def aire(self): u"""D'aprs David Chandler, Area of a general polygon. http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf""" if self.existe: points = self.__points xy = [pt.coordonnees for pt in (points + (points[0],))] s1 = s2 = 0 for i in xrange(len(points)): s1 += xy[i][0]*xy[i+1][1] s2 += xy[i][1]*xy[i+1][0] return abs(s1 - s2)/2 return None @property def info(self): return self.nom_complet + u" d'aire " + str(self.aire) @property def perimetre(self): return sum(cote.longueur for cote in self.__cotes) def _espace_vital(self): points = self.__points x1 = min(pt.abscisse for pt in points) x2 = max(pt.abscisse for pt in points) y1 = min(pt.ordonnee for pt in points) y2 = max(pt.ordonnee for pt in points) return (x1, x2, y1, y2) class Polygone(Polygone_generique): u"""Un polygone. Un polygone dfini par ses sommets.""" _points_crees_automatiquement = False def __new__(cls, *points, **styles): if styles.get("points", None): points = styles.pop("points") n = len(points) if not n: n = styles.pop("n", 3+abs(int(normalvariate(0,4)))) if n == 1: if isinstance(points[0], (list, tuple)): points = tuple(points[0]) n = len(points) elif isinstance(points[0], TYPES_ENTIERS): n = points[0] points = () # Attention, pas de 'elif' ici, 'n' a pu changer de valeur ! if n == 2: newclass = Segment elif n == 3: newclass = Triangle elif n == 4: newclass = Quadrilatere elif n == 5 : newclass = Pentagone elif n == 6 : newclass = Hexagone elif n == 7 : newclass = Heptagone elif n == 8 : newclass = Octogone else: return object.__new__(cls) objet = newclass.__new__(newclass, *points, **styles) objet.__init__(*points, **styles) return objet points = __points = Arguments("Point_generique") def __init__(self, *points, **styles): self._points_crees_automatiquement = False if styles.get("points", None): points = styles.pop("points") if not points: points = (styles.pop("n", 3 + abs(int(normalvariate(0,4)))), ) if len(points) == 1: if isinstance(points[0], (list, tuple)): points = tuple(points[0]) elif isinstance(points[0], TYPES_ENTIERS): points = tuple(Point() for i in xrange(points[0])) self._points_crees_automatiquement = True self.__points = points = tuple(Ref(obj) for obj in points) Polygone_generique.__init__(self, *points, **styles) def _set_feuille(self): if self._points_crees_automatiquement: self._affecter_coordonnees_par_defaut(self.__points) # Nommage intelligent des sommets par dfaut noms = re.findall(RE_NOM_OBJET, self._nom) if "".join(noms) == self._nom and len(self.__points) == len(noms): for arg, nom in zip(self.__points, noms): if self.__feuille__.objets.has_key(nom): nom = '' self.__feuille__.objets[nom] = arg else: for point in self.__points: self.__feuille__.objets[''] = point Objet._set_feuille(self) class Triangle(Polygone_generique): u"""Un triangle.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Polygone_generique.__init__(self, point1, point2, point3, **styles) d1 = Perpendiculaire(Droite(point1, point2), point3) d2 = Perpendiculaire(Droite(point2, point3), point1) self.orthocentre = Intersection_droites(d1, d2) self.centre_cercle_circonscrit = Point_equidistant(point1, point2, point3) d1 = Bissectrice(point1, point2, point3) d2 = Bissectrice(point3, point1, point2) self.centre_cercle_inscrit = Intersection_droites(d1, d2) @property def rectangle(self): a, b, c = sorted(cote.longueur for cote in self._Polygone_generique__cotes) return abs(a**2 + b**2 - c**2) < contexte['tolerance'] @property def isocele(self): a, b, c = sorted(cote.longueur for cote in self._Polygone_generique__cotes) return (b - a) < contexte['tolerance'] or (c - b) < contexte['tolerance'] @property def inscrit(self): u"Le polygone est-il inscrit dans un cercle ?" return True @property def convexe(self): return True @property def regulier(self): return True class Quadrilatere(Polygone_generique): u"""Un quadrilatre.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) point4 = __point4 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, point4 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) self.__point4 = point4 = Ref(point4) Polygone_generique.__init__(self, point1, point2, point3, point4, **styles) #TODO: complter ces mthodes et les tester (tests unitaires) @property def carre(self): return self.losange and self.rectangle @property def losange(self): raise self.equilateral @property def rectangle(self): A, B, C, D = self._Polygone_generique__sommets zAB = B.z - A.z zDC = C.z - D.z zAC = C.z - A.z raise abs(zAB - zDC) < contexte['tolerance'] and abs((zAB*zAC.conjugate()).real) < contexte['tolerance'] @property def parallelogramme(self): A, B, C, D = self._Polygone_generique__sommets zAB = B.z - A.z zDC = C.z - D.z raise abs(zAB - zDC) < contexte['tolerance'] @property def trapeze(self): A, B, C, D = self._Polygone_generique__sommets zAB = B.z - A.z zDC = C.z - D.z zAC = C.z - A.z zDB = B.z - D.z raise abs((zAB*zDC).real) < contexte['tolerance'] or abs((zAC*zDB).real) < contexte['tolerance'] @property def croise(self): raise NotImplementedError class Pentagone(Polygone_generique): u"""Un pentagone.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) point4 = __point4 = Argument("Point_generique", defaut = Point) point5 = __point5 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, point4 = None, point5 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) self.__point4 = point4 = Ref(point4) self.__point5 = point5 = Ref(point5) Polygone_generique.__init__(self, point1, point2, point3, point4, point5, **styles) class Hexagone(Polygone_generique): u"""Un hexagone.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) point4 = __point4 = Argument("Point_generique", defaut = Point) point5 = __point5 = Argument("Point_generique", defaut = Point) point6 = __point6 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, point4 = None, point5 = None, point6 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) self.__point4 = point4 = Ref(point4) self.__point5 = point5 = Ref(point5) self.__point6 = point6 = Ref(point6) Polygone_generique.__init__(self, point1, point2, point3, point4, point5, point6, **styles) class Heptagone(Polygone_generique): u"""Un heptagone.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) point4 = __point4 = Argument("Point_generique", defaut = Point) point5 = __point5 = Argument("Point_generique", defaut = Point) point6 = __point6 = Argument("Point_generique", defaut = Point) point7 = __point7 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, point4 = None, point5 = None, point6 = None, point7 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) self.__point4 = point4 = Ref(point4) self.__point5 = point5 = Ref(point5) self.__point6 = point6 = Ref(point6) self.__point7 = point7 = Ref(point7) Polygone_generique.__init__(self, point1, point2, point3, point4, point5, point6, point7, **styles) class Octogone(Polygone_generique): u"""Un octogone.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) point4 = __point4 = Argument("Point_generique", defaut = Point) point5 = __point5 = Argument("Point_generique", defaut = Point) point6 = __point6 = Argument("Point_generique", defaut = Point) point7 = __point7 = Argument("Point_generique", defaut = Point) point8 = __point8 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, point4 = None, point5 = None, point6 = None, point7 = None, point8 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) self.__point4 = point4 = Ref(point4) self.__point5 = point5 = Ref(point5) self.__point6 = point6 = Ref(point6) self.__point7 = point7 = Ref(point7) self.__point8 = point8 = Ref(point8) Polygone_generique.__init__(self, point1, point2, point3, point4, point5, point6, point7, point8, **styles) class Parallelogramme(Quadrilatere): u"""Un paralllogramme.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Quadrilatere.__init__(self, point1, point2, point3, Barycentre(Point_pondere(point1, 1), Point_pondere(point2, -1), Point_pondere(point3, 1)), **styles) class Sommet_rectangle(Point_generique): u"""Un sommet d'un rectangle. (Usage interne).""" _style_defaut = param.points_deplacables point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) rapport = __rapport = Argument("Variable_generique", defaut = lambda:uniform(.4, .8)) def __init__(self, point1, point2, rapport, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__rapport = rapport = Ref(rapport) Point_generique.__init__(self, **styles) def _get_coordonnees(self): k = self.__rapport zA = self.__point1.z zB = self.__point2.z zBC = (zB - zA)*1j*k zC = zB + zBC return zC.real, zC.imag def _set_coordonnees(self, x, y): zA = self.__point1.z zB = self.__point2.z z = x + 1j*y try: zAB = zB - zA # vecteur normal A>B (sens direct) zn = zAB*1j if issympy(zAB): p = ((z - zB)*zn.conjugate()).expand(complex=True).as_real_imag()[1] self.__rapport = p/((zAB*zAB.conjugate()).expand(complex=True).as_real_imag()[1]) else: # produit scalaire p = ((z - zB)*zn.conjugate()).real # on divise par |zAB| self.__rapport = p/((zAB*zAB.conjugate()).real) except (OverflowError, ZeroDivisionError): if param.debug: print_error() class Rectangle(Parallelogramme): u"""Un rectangle.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) rapport = __rapport = Argument("Variable_generique", defaut = lambda:uniform(.4, .8) + randint(0, 1)) def __init__(self, point1 = None, point2 = None, rapport = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__rapport = rapport = Ref(rapport) Parallelogramme.__init__(self, point1, point2, Sommet_rectangle(point1, point2, rapport), **styles) # Hack infme, pour lier le 3e sommet l'objet 'Sommet_triangle_rectangle' self._Polygone_generique__sommets[2]._lier_sommet(self._Quadrilatere__point3) class Losange(Parallelogramme): u"""Un losange.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) angle = __angle = Argument("Angle_generique", defaut = lambda:radian(uniform(pi/6, pi/4) + randint(0, 1)*pi/2)) def __init__(self, point1 = None, point2 = None, angle = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) Parallelogramme.__init__(self, Sommet_triangle_isocele(point1, point2, angle), point1, point2, **styles) # Hack infme, pour lier le 3e sommet l'objet 'Sommet_triangle_isocele' self._Polygone_generique__sommets[0]._lier_sommet(self._Quadrilatere__point1) class Polygone_regulier_centre(Polygone_generique): u"""Un polygone rgulier. Un polygone rgulier dfini par son centre, un sommet, et le nombre de cts.""" def __new__(cls, centre = None, sommet = None, n = None, **styles): if n is None: n = 3 + abs(int(normalvariate(0,4))) if n == 3: newclass = Triangle_equilateral_centre elif n == 4: newclass = Carre_centre else: return object.__new__(cls) objet = newclass.__new__(newclass, centre, sommet, **styles) objet.__init__(centre, sommet, **styles) return objet centre = __centre = Argument("Point_generique", defaut = Point) sommet = __sommet = Argument("Point_generique", defaut = Point) n = __n = ArgumentNonModifiable("int") def __init__(self, centre = None, sommet = None, n = None, **styles): self.__centre = centre = Ref(centre) self.__sommet = sommet = Ref(sommet) if n is None: n = 3 + abs(int(normalvariate(0,4))) # il ne faut pas utiliser de rfrence (Ref), car n n'est pas modifiable : self.__n = n points = (Rotation(centre, '2*pi*' + str(i) + '/' + str(n), unite='r')(sommet) for i in xrange(1, n)) Polygone_generique.__init__(self, sommet, *points, **styles) class Triangle_equilateral_centre(Triangle): u"""Un triangle quilatral. Un triangle quilatral dfini par son centre et un sommet.""" centre = __centre = Argument("Point_generique", defaut = Point) sommet = __sommet = Argument("Point_generique", defaut = Point) def __init__(self, centre = None, sommet = None, **styles): self.__centre = centre = Ref(centre) self.__sommet = sommet = Ref(sommet) Triangle.__init__(self, sommet, Rotation(centre, '2*pi/3', unite='r')(sommet), Rotation(centre, '4*pi/3', unite='r')(sommet), **styles) class Carre_centre(Quadrilatere): u"""Un carr. Un carr dfini par son centre et un sommet.""" centre = __centre = Argument("Point_generique", defaut = Point) sommet = __sommet = Argument("Point_generique", defaut = Point) def __init__(self, centre = None, sommet = None, **styles): self.__centre = centre = Ref(centre) self.__sommet = sommet = Ref(sommet) Quadrilatere.__init__(self, sommet, Rotation(centre, 'pi/2', unite='r')(sommet), Rotation(centre, 'pi', unite='r')(sommet), Rotation(centre, '3*pi/2', unite='r')(sommet), **styles) class Polygone_regulier(Polygone_generique): u"""Un polygone rgulier. Un polygone rgulier dfini par deux points conscutif (sens direct).""" def __new__(cls, point1 = None, point2 = None, n = None, **styles): if n is None: n = 3 + abs(int(normalvariate(0,4))) if n == 3: newclass = Triangle_equilateral elif n == 4: newclass = Carre else: return object.__new__(cls) objet = newclass.__new__(newclass, point1, point2, **styles) objet.__init__(point1, point2, **styles) return objet point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) n = __n = ArgumentNonModifiable("int") def __init__(self, point1 = None, point2 = None, n = 6, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) if n is None: n = 3 + abs(int(normalvariate(0,4))) # il ne faut pas utiliser de rfrence (Ref), car n n'est pas modifiable : self.__n = n angle = '(2-' + str(n) + ')*pi/' + str(n) point3 = Rotation(point2, angle, unite='r')(point1) # Auparavant, tous les sommets taient construits ainsi rcursivement # mais la taille de str(Polygone_regulier.points[i]) # croissait alors exponentiellement avec i (elle doublait chaque itration) ! points = [point1, point2, point3] self.__centre = centre = Point_equidistant(point1, point2, point3) for i in xrange(3, n): angle = '2*pi*' + str(i) + '/' + str(n) points.append(Rotation(centre, angle, unite='r')(point1)) Polygone_generique.__init__(self, *points, **styles) @property def centre(self): return self.__centre class Triangle_equilateral(Triangle): u"""Un triangle quilatral. Un triangle quilatral dfini par deux points conscutif (sens direct).""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Triangle.__init__(self, point1, point2, Rotation(point1, 'pi/3', unite='r')(point2), **styles) class Carre(Quadrilatere): u"""Un carr. Un carr dfini par deux points conscutif (sens direct).""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) point3 = Rotation(point2, '-pi/2', unite='r')(point1) Quadrilatere.__init__(self, point1, point2, point3, Rotation(point1, 'pi/2', unite='r')(point2), **styles) class Sommet_triangle_isocele(Point_generique): u"""Un sommet d'un triangle isocle. Le 3e sommet du triangle isocele (un des sommets de la base). (Usage interne).""" _style_defaut = param.points_deplacables sommet_principal = __sommet_principal = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) angle = __angle = Argument("Angle_generique", defaut = lambda:radian(uniform(pi/6, pi/4) + randint(0, 1)*pi/2)) def __init__(self, sommet_principal, point2, angle, **styles): self.__sommet_principal = sommet_principal = Ref(sommet_principal) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) Point_generique.__init__(self, **styles) def _get_coordonnees(self): a = self.__angle.radian zA = self.__sommet_principal.z zB = self.__point2.z if contexte['exact'] and issympy(a, zA, zB): zC = (zB - zA)*sexp(1j*a) + zA return zC.expand(complex=True).as_real_imag() else: zC = (zB - zA)*cexp(1j*a) + zA return zC.real, zC.imag def _set_coordonnees(self, x, y): with contexte(unite_angle='r'): zA = self.__sommet_principal.z zB = self.__point2.z z = x + 1j*y try: if issympy(zA, zB): self.__angle = sarg((z - zA)/(zB - zA)) else: self.__angle = clog((z - zA)/(zB - zA)).imag except (OverflowError, ZeroDivisionError): if param.debug: print_error() class Triangle_isocele(Triangle): u"""Un triangle isocle. Un triangle isocle dfini par son sommet principal, un autre sommet, et son angle principal (sens direct).""" sommet_principal = __sommet_principal = Argument("Point_generique", defaut=Point) point2 = __point2 = Argument("Point_generique", defaut=Point) angle = __angle = Argument("Angle_generique", defaut = lambda:radian(uniform(pi/6, pi/4) + randint(0, 1)*pi/2)) def __init__(self, sommet_principal = None, point2 = None, angle = None, **styles): self.__sommet_principal = sommet_principal = Ref(sommet_principal) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) Triangle.__init__(self, sommet_principal, point2, Sommet_triangle_isocele(sommet_principal, point2, angle), **styles) # Hack infme, pour lier le 3e sommet l'objet 'Sommet_triangle_isocele' self._Polygone_generique__sommets[-1]._lier_sommet(self._Triangle__point3) class Sommet_triangle_rectangle(Point_generique): u"""Un sommet d'un triangle rectangle. Le sommet oppos l'hypothnuse du triangle rectangle. (Usage interne).""" _style_defaut = param.points_deplacables point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) angle = __angle = Argument("Angle_generique", defaut = lambda:radian(uniform(pi/6, pi/3))) def __init__(self, point1, point2, angle, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) Point_generique.__init__(self, **styles) def _get_coordonnees(self): a = self.__angle.radian zA = self.__point1.z zB = self.__point2.z zI = (zA + zB)/2 if contexte['exact'] and issympy(a, zI): zC = (zB - zI)*sexp(I*2*a) + zI return zC.expand(complex=True).as_real_imag() else: zC = (zB - zI)*cexp(1j*2*a) + zI return zC.real, zC.imag def _set_coordonnees(self, x, y): with contexte(unite_angle='r'): zA = self.__point1.z zB = self.__point2.z zI = (zA + zB)/2 z = x + 1j*y try: if issympy(zI): self.__angle = sarg((z - zI)/(zB - zI))/2 else: self.__angle = clog((z - zI)/(zB - zI)).imag/2 except (OverflowError, ZeroDivisionError): if param.debug: print_error() class Triangle_rectangle(Triangle): u"""Un triangle rectangle. Un triangle rectangle dfini par les extrmits de son hypothnuse (sens direct), et un de ses angles aigus.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) angle = __angle = Argument("Angle_generique", defaut = lambda:radian(uniform(pi/7, pi/5))) def __init__(self, point1 = None, point2 = None, angle = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) Triangle.__init__(self, point1, point2, Sommet_triangle_rectangle(point1, point2, angle), **styles) # Hack infme, pour lier le 3e sommet l'objet 'Sommet_triangle_rectangle' self._Polygone_generique__sommets[-1]._lier_sommet(self._Triangle__point3) class Triangle_isocele_rectangle(Triangle): u"""Un triangle isocle rectangle. Un triangle isocle rectangle dfini par les extrmits de son hypothnuse (sens direct).""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Triangle.__init__(self, point1, point2, Sommet_triangle_rectangle(point1, point2, 'pi/4'), **styles) class PrevisualisationPolygone(Polygone_generique): u"""Une forme de polygone utilise uniquement pour la prvisualisation. Usage interne.""" def _get_points(self): return self.__points def _set_points(self, points): for pt in self.__points: pt.vassaux.remove(self) for pt in points: pt.vassaux.append(self) # NOTE: self.__points doit tre de type tuple et surtout pas liste # (une ventuelle modification de la liste ne gererait pas correctement les vassaux) self.__points = points ## self.creer_figure() self.figure_perimee() # Exceptionnellement, on ne passe par un objet 'Arguments' ici (le but tant d'tre le plus rapide possible). points = property(_get_points, _set_points) # De mme, inutile de passer par un descripteur de type 'DescripteurFeuille' pour l'attribut '__feuille__' __feuille__ = None def __init__(self, *points): Objet.__init__(self) self.__points = () self.points = points def style(self, nom): return param.polygones.get(nom, None) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.polygone(), self.rendu.ligne(zorder = 1)] fill = self._representation[0] plot = self._representation[1] niveau = self.style("niveau") points = self.__points xy = [pt.coordonnees for pt in points] x, y = zip(*xy) xy.append(self.__points[0].coordonnees) fill.xy = xy plot.set_data(x, y) fill._alpha = self.style("alpha") fill._edgecolor = fill._facecolor = plot._color = self.style("couleur") fill.zorder = niveau - 0.01 plot._linestyle = self.style("style") fill._linestyle = FILL_STYLES.get(self.style("style"), "solid") plot._linewidth = fill._linewidth = self.style("epaisseur") plot.zorder = niveau def __repr__(self): return "PrevisualisationPolygone(%s)" %repr(self.__points) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/lignes.py0000644000175000017500000011117112014170666023014 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from random import uniform from math import pi, atan2, cos, sin from numpy import inf from .objet import Objet, Objet_avec_equation, Argument, ArgumentNonModifiable, \ Ref, contexte, RE_NOM_DE_POINT, G from .angles import Secteur_angulaire from .points import Point, Point_generique, Centre, Point_translation, Milieu from .routines import nice_display, distance, carre_distance, formatage, \ vect, produit_scalaire, norme, distance_segment from .vecteurs import Vecteur_unitaire, Vecteur, Vecteur_libre, Vecteur_generique from .labels import Label_droite, Label_demidroite, Label_segment from .transformations import Translation from .. import param from ..pylib import eval_restricted ########################################################################################## ## LIGNES class Ligne_generique(Objet_avec_equation): u"""Une ligne gnrique. Usage interne : la classe mre pour les droites, segments, demi-droites.""" _affichage_depend_de_la_fenetre = True _enregistrer_sur_la_feuille_par_defaut = True _marqueurs = "()" point1 = __point1 = Argument("Point_generique") point2 = __point2 = Argument("Point_generique") def __init__(self, point1, point2, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Objet.__init__(self, **styles) #~ self._initialiser(point1 = Point_generique, point2 = Point_generique) def _longueur(self): return norme(self.__point2.x - self.__point1.x, self.__point2.y - self.__point1.y) def _espace_vital(self): x1, y1 = self.__point1.coordonnees x2, y2 = self.__point2.coordonnees return (min(x1, x2), max(x1, x2), min(y1, y2), max(y1, y2)) def _get_equation(self): u"Retourne un triplet (a, b, c), tel que ax + by + c = 0 soit une quation de droite de la ligne." xA, yA = self.__point1.coordonnees xB, yB = self.__point2.coordonnees a, b, c = yA - yB, xB - xA, xA*yB - yA*xB while -1 < a < 1 and -1 < b < 1: a *= 10 b *= 10 c *= 10 return (a, b, c) @property def equation_reduite(self): u"""Retourne (a, b) si la droite a une quation de la forme y=ax+b ; et (c,) si la droite a une quation de la forme x=c. Ceci permet de comparer facilement deux droites, ce qui n'est pas le cas avec la proprit .quation (puisqu'une droite a une infinit d'quations) : si d1._equation_reduite() ~ d2._equation_reduite, d1 et d2 sont ( peu prs) confondues. """ eq = self.equation if eq is None: return a, b, c = eq if abs(b) > contexte['tolerance'] : return (-a/b, -c/b) else: return (-c/a, ) def _parallele(self, ligne): u"Indique si la ligne est parallle une autre ligne." if not isinstance(ligne, Ligne_generique): raise TypeError, "L'objet doit etre une ligne." a, b, c = self.equation a0, b0, c0 = ligne.equation return abs(a*b0 - b*a0) < contexte['tolerance'] def _perpendiculaire(self, ligne): u"Indique si la ligne est perpendiculaire une autre ligne." if not isinstance(ligne, Ligne_generique): raise TypeError, "L'objet doit etre une ligne." a, b, c = self.equation a0, b0, c0 = ligne.equation return abs(a*a0 + b*b0) < contexte['tolerance'] def __iter__(self): return iter((self.__point1, self.__point2)) def _creer_nom_latex(self): u"""Cre le nom format en LaTeX. Ex: M1 -> $M_1$.""" Objet._creer_nom_latex(self) nom = self.nom_latex[1:-1] if re.match("(" + RE_NOM_DE_POINT + "){2}", nom): self.nom_latex = "$" + self._marqueurs[0] + nom + self._marqueurs[1] + "$" else: nom = self.latex_police_cursive(nom) self.nom_latex = "$" + nom + "$" def xy(self, x = None, y = None): u"""Retourne les coordonnes du point de la droite d'abscisse ou d'ordonne donne. x ou y doivent tre dfinis, mais bien sr pas les deux. Nota: Si x est donn pour une droite verticale, un point d'abscisse infinie est retourn. l'inverse, si y est donn pour une droite horizontale, un point d'ordonne infinie est retourn. """ x1, y1 = self.__point1.coordonnees x2, y2 = self.__point2.coordonnees if y is None and x is not None: a = self.pente return (x, a*(x - x1) + y1) elif x is None and y is not None: a = self.copente return (a*(y - y1) + x1, y) raise TypeError, 'x ou y doivent etre definis.' @property def pente(self): x1, y1 = self.__point1.coordonnees x2, y2 = self.__point2.coordonnees return (y2 - y1)/(x2 - x1) if (x2 - x1) else inf @property def copente(self): x1, y1 = self.__point1.coordonnees x2, y2 = self.__point2.coordonnees return (x2 - x1)/(y2 - y1) if (y2 - y1) else inf def _points(self): u"Donne les points d'intersection de la droite avec les bords de la fentre." # Attention, il faut imprativement rcuprer la fentre sur le canevas, # dans le cas d'un repre orthonorm. xmin, xmax, ymin, ymax = self.__canvas__.fenetre eps = contexte['tolerance'] dx = eps*(xmax - xmin) dy = eps*(ymax - ymin) # points contient les intersections de la droite avec les bords de la fentre. points = [] Mxmin = self.xy(x=xmin) if ymin - dy <= Mxmin[1] <= ymax + dy: points.append(Mxmin) Mxmax = self.xy(x=xmax) if ymin - dy <= Mxmax[1] <= ymax + dy: points.append(Mxmax) Mymin = self.xy(y=ymin) if xmin - dx <= Mymin[0] <= xmax + dx: points.append(Mymin) Mymax = self.xy(y=ymax) if xmin - dx <= Mymax[0] <= xmax + dx: points.append(Mymax) # En principe, points ne contient que deux lments. # Il peut ventuellement en contenir 3 ou 4, dans le cas limite o la droite # coupe la fentre dans un coin. Dans ce cas, on peut trouver # deux points d'intersection (quasi) confondus. # Il faut alors bien s'assurer de ne pas retourner 2 points confondus. if len(points) == 4: return points[:2] # Mxmin et Mxmax ne peuvent tre confondus. elif len(points) == 3: if points[0] == Mxmin and points[1] == Mxmax: return points[:2] # Mxmin et Mxmax ne peuvent tre confondus. return points[1:] # Mymin et Mymax non plus. else: assert len(points) <= 2 return points class Segment(Ligne_generique): u"""Un segment. Un segment dfini par deux points""" _affichage_depend_de_la_fenetre = True # codage _style_defaut = param.segments _prefixe_nom = "s" _marqueurs = "[]" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Ligne_generique.__init__(self, point1 = point1, point2 = point2, **styles) self.etiquette = Label_segment(self) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne(), self.rendu.codage()] x1, y1 = self.__point1.coordonnees x2, y2 = self.__point2.coordonnees couleur = self.style("couleur") epaisseur = self.style("epaisseur") niveau = self.style("niveau") style = self.style("style") for elt_graphique in self._representation: elt_graphique.set_visible(True) plot, codage = self._representation plot.set_data(((x1, x2), (y1, y2))) plot.set(color=couleur, linestyle=style, linewidth=epaisseur) plot.zorder = niveau # Codages utiliss pour indiquer les segments de mme longueur if not self.style("codage"): codage.set_visible(False) else: codage.set(visible=True, style=self.style('codage'), position=(.5*(x1 + x2), .5*(y1 + y2)), direction=(x2 - x1, y2 - y1), taille=param.codage["taille"], angle=param.codage["angle"], color=couleur, linewidth=epaisseur, zorder=niveau + 0.01, ) def image_par(self, transformation): return Segment(self.__point1.image_par(transformation), self.__point2.image_par(transformation)) def _distance_inf(self, x, y, d): return distance_segment((x, y), self._pixel(self.__point1), self._pixel(self.__point2), d) def _contains(self, M): #if not isinstance(M, Point_generique): # return False A = self.__point1 B = self.__point2 xu, yu = vect(A, B) xv, yv = vect(A, M) if abs(xu*yv - xv*yu) > contexte['tolerance']: return False if abs(xu) > abs(yu): k = xv/xu elif yu: k = yv/yu else: # A == B return M == A return 0 <= k <= 1 @property def longueur(self): u"""Longueur du segment. Alias de _longueur, disponible pour indiquer que l'objet a vraiment une longueur au sens mathmatique du terme (pas comme une droite !)""" return self._longueur() @property def extremites(self): return self.__point1, self.__point2 @property def info(self): return self.nom_complet + u' de longueur ' + nice_display(self.longueur) @staticmethod def _convertir(objet): if hasattr(objet, "__iter__"): return Segment(*objet) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" class Demidroite(Ligne_generique): u"""Une demi-droite. Une demi-droite dfinie par son origine et un deuxime point""" _style_defaut = param.droites _prefixe_nom = "d" _marqueurs = "[)" origine = __origine = Argument("Point_generique", defaut = Point) point = __point = Argument("Point_generique", defaut = Point) def __init__(self, origine = None, point = None, **styles): self.__origine = origine = Ref(origine) self.__point = point = Ref(point) Ligne_generique.__init__(self, point1 = origine, point2 = point, **styles) self.etiquette = Label_demidroite(self) def image_par(self, transformation): return Demidroite(self.__point1.image_par(transformation), self.__point2.image_par(transformation)) def _conditions_existence(self): return carre_distance(self.__origine, self.__point) > contexte['tolerance']**2 # EDIT: les conditions d'existence en amont sont dsormais bien dfinies (ou devraient l'tre !!) # return [self.point1.coo is not None and self.point2.coo is not None and sum(abs(self.point1.coo - self.point2.coo)) > contexte['tolerance']] # si les conditions d'existence en amont sont mal definies, il se peut que les coordonnees d'un point valent None def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne()] x, y = self.__origine.coordonnees x0, y0 = self.__point.coordonnees points = self._points() if len(points) < 2: # La droite sous-jacente ne coupe pas la fentre (ou seulement e return (x1, y1), (x2, y2) = points plot = self._representation[0] if produit_scalaire((x1 - x, y1 - y), (x0 - x, y0 - y)) > produit_scalaire((x2 - x, y2 - y), (x0 - x, y0 - y)): plot.set_data((x1, x), (y1, y)) else: plot.set_data((x, x2), (y, y2)) plot.set(color = self.style("couleur"), linestyle = self.style("style"), linewidth = self.style("epaisseur"), zorder = self.style("niveau")) def _distance_inf(self, x, y, d): # cf. "distance_point_segment.odt" dans "doc/developpeurs/maths/" xA, yA = self._pixel(self.__origine) xB, yB = self._pixel(self.__point) x1 = min(xA, xB); x2 = max(xA, xB) y1 = min(yA, yB); y2 = max(yA, yB) if (xA == x1 and x > x1 - d or xA == x2 and x < x2 + d) and (yA == y1 and y > y1 - d or yA == y2 and y < y2 + d): return ((yA - yB)*(x - xA)+(xB - xA)*(y - yA))**2/((xB - xA)**2+(yB - yA)**2) < d**2 else: return False def _contains(self, M): #if not isinstance(M, Point_generique): # return False A = self.__origine B = self.__point xu, yu = vect(A, B) xv, yv = vect(A, M) if abs(xu*yv - xv*yu) > contexte['tolerance']: return False if abs(xu) > abs(yu): # (AB) est plutt horizontale k = xv/xu elif yu: # (AB) est plutt verticale k = yv/yu else: # A == B return M == A return 0 <= k @property def equation_formatee(self): u"Equation sous forme lisible par l'utilisateur." eq = self.equation if eq is None: return u"L'objet n'est pas dfini." # on ne garde que quelques chiffres aprs la virgule a, b, c = (nice_display(coeff) for coeff in eq) eps = contexte['tolerance'] if abs(a) < abs(b): # droite plutt horizontale if (self.__point.ordonnee - self.__origine.ordonnee) > eps: ajout = "y > " + nice_display(self.__origine.ordonnee) elif (self.__point.ordonnee - self.__origine.ordonnee) < eps: ajout = "y < " + nice_display(self.__origine.ordonnee) else: return u"Prcision insuffisante." else: # droite plutt verticale if (self.__point.abscisse - self.__origine.abscisse) > eps: ajout = "x > " + nice_display(self.__origine.abscisse) elif (self.__point.abscisse - self.__origine.abscisse) < eps: ajout = "x < " + nice_display(self.__origine.abscisse) else: return u"Prcision insuffisante." return formatage("%s x + %s y + %s = 0 et %s" %(a, b, c, ajout)) @staticmethod def _convertir(objet): if hasattr(objet, "__iter__"): return Demidroite(*objet) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" class Droite_generique(Ligne_generique): u"""Une droite gnrique. Usage interne : la classe mre pour toutes les droites.""" _style_defaut = param.droites _prefixe_nom = "d" parallele = Ligne_generique._parallele perpendiculaire = Ligne_generique._perpendiculaire point1 = __point1 = Argument("Point_generique") point2 = __point2 = Argument("Point_generique") def __init__(self, point1, point2, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Ligne_generique.__init__(self, point1 = point1, point2 = point2, **styles) self.etiquette = Label_droite(self) def image_par(self, transformation): return Droite(self.__point1.image_par(transformation), self.__point2.image_par(transformation)) def _conditions_existence(self): return carre_distance(self.__point1, self.__point2) > contexte['tolerance']**2 def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne()] points = self._points() if len(points) < 2: # La droite sous-jacente ne coupe pas la fentre (ou seulement e return (x1, y1), (x2, y2) = points plot = self._representation[0] plot.set_data((x1, x2), (y1, y2)) plot.set(color = self.style("couleur"), linestyle = self.style("style"), linewidth = self.style("epaisseur"), zorder = self.style("niveau")) def _distance_inf(self, x, y, d): # cf. "distance_point_segment.odt" dans "doc/developpeurs/maths/" xA, yA = self._pixel(self.__point1) xB, yB = self._pixel(self.__point2) return ((yA-yB)*(x-xA)+(xB-xA)*(y-yA))**2/((xB-xA)**2+(yB-yA)**2) < d**2 def _contains(self, M): #if not isinstance(M, Point_generique): # return False A = self.__point1 B = self.__point2 xu, yu = vect(A, B) xv, yv = vect(A, M) return abs(xu*yv-xv*yu) < contexte['tolerance'] @property def equation_formatee(self): u"Equation sous forme lisible par l'utilisateur." eq = self.equation if eq is None: return u"L'objet n'est pas dfini." a, b, c = (nice_display(coeff) for coeff in eq) # on ne garde que quelques chiffres aprs la virgule pour l'affichage return formatage("%s x + %s y + %s = 0" %(a, b, c)) @property def info(self): return self.nom_complet + u" d'quation " + self.equation_formatee def __eq__(self, y): if self.existe and isinstance(y, Droite_generique) and y.existe: eq1 = self.equation_reduite eq2 = y.equation_reduite if len(eq1) == len(eq2) == 1: return abs(eq1[0] - eq2[0]) < contexte['tolerance'] elif len(eq1) == len(eq2) == 2: return abs(eq1[0] - eq2[0]) < contexte['tolerance'] and abs(eq1[1] - eq2[1]) < contexte['tolerance'] return False def __ne__(self, y): return not self == y @staticmethod def _convertir(objet): if hasattr(objet, "__iter__"): return Droite(*objet) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" class Droite(Droite_generique): u"""Une droite. Une droite dfinie par deux points""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __new__(cls, *args, **kw): if len(args) == 1 and isinstance(args[0], basestring): newclass = Droite_equation elif len(args) == 2 and isinstance(args[1], Vecteur_generique): newclass = Droite_vectorielle else: return object.__new__(cls) droite = newclass.__new__(newclass, *args, **kw) droite.__init__(*args, **kw) return droite def __init__(self, point1 = None, point2 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Droite_generique.__init__(self, point1 = point1, point2 = point2, **styles) class Point_droite(Point_generique): u"""Un des deux points servant construire une droite d'quation donne. Usage interne. Ceci sert pour les droites qui ne sont pas dfinies l'aide de points, mais directement l'aide d'une quation. Comme l'implmentation actuelle des droites exige que la droite soit dfinie par 2 points, on gnre deux points de la droite partir de son quation.""" droite = __droite = Argument("Droite_generique") premier = __premier = ArgumentNonModifiable("bool") def __init__(self, droite, premier, **styles): self.__droite = droite = Ref(droite) self.__premier = premier Point_generique.__init__(self, **styles) def _get_coordonnees(self): a, b, c = self.__droite.equation if b: if self.__premier: return 0, -c/b else: return 1, -a/b - c/b else: if self.__premier: return -c/a, 0 else: return -c/a, 1 class Droite_vectorielle(Droite_generique): u"""Une droite dirige par un vecteur. Une droite dfinie par un point et un vecteur directeur.""" point = __point = Argument("Point_generique", defaut=Point) vecteur = __vecteur = Argument("Vecteur_generique", defaut=Vecteur_libre) def __init__(self, point = None, vecteur = None, **styles): self.__point = point = Ref(point) self.__vecteur = vecteur = Ref(vecteur) Objet.__init__(self) # pour pouvoir utiliser 'Point_droite(self, True)', l'objet doit dj tre initialis Droite_generique.__init__(self, Point_droite(self, True), Point_droite(self, False), **styles) def _get_equation(self): a, b = self.__vecteur.coordonnees x, y = self.__point.coordonnees return -b, a, b*x - a*y def _conditions_existence(self): return self.__vecteur.x**2 + self.__vecteur.x**2 > contexte['tolerance']**2 class Parallele(Droite_generique): u"""Une parallle. La parallle une droite passant par un point.""" droite = __droite = Argument("Droite_generique") point = __point = Argument("Point_generique", defaut = Point) def __init__(self, droite, point = None, **styles): self.__droite = droite = Ref(droite) self.__point = point = Ref(point) Objet.__init__(self) # pour pouvoir utiliser 'Point_droite(self, True)', l'objet doit dj tre initialis Droite_generique.__init__(self, Point_droite(self, True), Point_droite(self, False), **styles) def _get_equation(self): a, b, c = self.__droite.equation x, y = self.__point.coordonnees return a, b, -a*x-b*y def _conditions_existence(self): return True ##class Droite_rotation(Droite): # REDFINIR ## u"""Une image d'une droite par rotation. ## ## Une droite obtenue par rotation d'une autre droite, ou d'un bipoint, ou...""" ## ## droite = __droite = Argument("Droite") ## rotation = __rotation = Argument("Rotation") ## ## def __init__(self, droite, rotation, **styles): ## self.__droite = droite = Ref(droite) ## self.__rotation = rotation = Ref(rotation) ## warning("A redefinir, voir commentaires dans le code.") ## Droite.__init__(self, point1 = Point_rotation(droite._Droite__point1, rotation), point2 = Point_rotation(droite._Droite__point2, rotation), **styles) ## # BUG : droite._Droite__point1 ne va pas, car si l'argument droite est modifi, la modification ne va pas se rpercuter sur les arguments de Point_rotation ## ## def _get_coordonnees(self): ## raise NotImplementedError ## class Perpendiculaire(Droite_generique): u"""Une perpendiculaire. Une droite perpendiculaire une autre passant par un point.""" droite = __droite = Argument("Ligne_generique") point = __point = Argument("Point_generique", defaut = Point) def __init__(self, droite, point = None, **styles): self.__droite = droite = Ref(droite) self.__point = point = Ref(point) Objet.__init__(self) # pour pouvoir utiliser 'Point_droite(self, True)', l'objet doit dj tre initialis Droite_generique.__init__(self, Point_droite(self, True), Point_droite(self, False), **styles) def _get_equation(self): a, b, c = self.__droite.equation x, y = self.__point.coordonnees return -b, a, b*x - a*y def _conditions_existence(self): return True class Mediatrice(Perpendiculaire): u"""Une mdiatrice. La mdiatrice d'un segment (ou d'un bipoint, ...) >>> from wxgeometrie.geolib import Point, Mediatrice, Segment >>> A=Point(1,2); B=Point(3,4) >>> s=Segment(A,B) >>> Mediatrice(A, B) == Mediatrice(s) True """ point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): if isinstance(point1, Segment): point2 = point1._Segment__point2 point1 = point1._Segment__point1 self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Perpendiculaire.__init__(self, Droite(point1, point2), Milieu(point1, point2), **styles) def _conditions_existence(self): return carre_distance(self.__point1, self.__point2) > contexte['tolerance']**2 class Droite_equation(Droite_generique): u"""Une droite dfinie par une quation. Une droite d'quation donne sous forme d'un triplet (a, b, c). (ax + by + c = 0)""" a = __a = Argument("Variable_generique") b = __b = Argument("Variable_generique") c = __c = Argument("Variable_generique") @staticmethod def __coeff(match_object): if match_object is None: return 0 chaine = match_object.group() if chaine == "-": return -1 elif chaine == "+" or not chaine: return 1 elif chaine[-1] == "*": chaine = chaine[:-1] return eval_restricted(chaine) @classmethod def __extraire_coeffs(cls, chaine): a = cls.__coeff(re.search("[-+]*[^-+xy]*(?=x)", chaine)) b = cls.__coeff(re.search("[-+]*[^-+xy]*(?=y)", chaine)) c = cls.__coeff(re.search("[-+]*[^-+xy]+(?=$|[-+])", chaine)) return a, b, c def __init__(self, a = 1, b = -1, c = 0, **styles): if isinstance(a, basestring): membre_gauche, membre_droite = a.replace(" ", "").split("=") a, b, c = self.__extraire_coeffs(membre_gauche) a_, b_, c_ = self.__extraire_coeffs(membre_droite) a -= a_ b -= b_ c -= c_ self.__a = a = Ref(a) self.__b = b = Ref(b) self.__c = c = Ref(c) Objet.__init__(self) # pour pouvoir utiliser 'Point_droite(self, True)', l'objet doit dj tre initialis Droite_generique.__init__(self, point1 = Point_droite(self, True), point2 = Point_droite(self, False), **styles) # self.equation = self.__a, self.__b, self.__c ## def verifier_coeffs(self, a, b, c): ## if a == b == 0: ## self.erreur(u"les deux premiers coefficients sont nuls.") def _conditions_existence(self): return not self.__a == self.__b == 0 # def _get_equation(self): # # Retourne un triplet (a, b, c), tel que ax + by + c = 0 soit une equation de droite de la ligne. # # On peut aussi modifier l'equation de la droite. # xA, yA = self.__point1.coordonnees # xB, yB = self.__point2.coordonnees # return (yA - yB, xB - xA, xA*yB - yA*xB) def _set_equation(self, a = None, b = -1, c = 0): if a is not None: # self.verifier_coeffs(a, b, c) self.__a = a self.__b = b self.__c = c def _get_equation(self): return self.__a, self.__b, self.__c ## def a(self, valeur = None): ## return self.__a(valeur) ## ## a = property(a, a) ## ## def b(self, valeur = None): ## return self.__b(valeur) ## ## b = property(b, b) ## ## def c(self, valeur = None): ## return self.__c(valeur) ## ## c = property(c, c) class Bissectrice(Droite_vectorielle): u"""Une bissectrice. La bissectrice d'un angle dfini par 3 points. On peut, au choix, entrer un angle ou 3 points comme argument.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): if isinstance(point1, Secteur_angulaire): # Au lieu de 3 points, on peut entrer un angle. point2 = point1._Secteur_angulaire__point point1 = Point_translation(point2, Translation(point1._Secteur_angulaire__vecteur1)) point3 = Point_translation(point2, Translation(point1._Secteur_angulaire__vecteur2)) self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) v = Vecteur_unitaire(Vecteur(point2, point1)) + Vecteur_unitaire(Vecteur(point2, point3)) Droite_vectorielle.__init__(self, point2, v, **styles) def _conditions_existence(self): return carre_distance(self.__point1, self.__point2) > contexte['tolerance']**2 and \ carre_distance(self.__point2, self.__point3) > contexte['tolerance']**2 class Point_tangence(Point_generique): u"""Un point de tangence. Le point de tangence d'un cercle et d'une droite tangente au cercle passant par un point donn. Usage interne.""" cercle = __cercle = Argument("Cercle_generique") point = __point = Argument("Point_generique") def __init__(self, cercle, point, angle_positif = None, **styles): self.__cercle = cercle = Ref(cercle) self.__point = point = Ref(point) self.__angle_positif = angle_positif = Ref(angle_positif) self.__intersection = G.Intersection_cercles(cercle, G.Cercle_diametre(Centre(cercle), point), angle_positif) Point_generique.__init__(self, **styles) def _get_coordonnees(self): # on a deux cas : # si le point est sur le cercle, on prend le point # si le point n'est pas sur le cercle, on construit une intersection de cercles if self.__point in self.__cercle: return self.__point.coordonnees return self.__intersection.coordonnees def _conditions_existence(self): return self.__intersection.existe or self.__point in self.__cercle class Tangente(Perpendiculaire): # REDFINIR ? u"""Une tangente. Une des deux tangentes un cercle passant par un point extrieur au cercle. Le dernier paramtre (True/False) sert distinguer les deux tangentes. (Voir la classe Intersection_cercles pour plus d'infos).""" cercle = __cercle = Argument("Cercle_generique", defaut='Cercle') point = __point = Argument("Point_generique", defaut = Point) angle_positif = __angle_positif = Argument("bool", defaut = True) def __init__(self, cercle = None, point = None, angle_positif = None, **styles): self.__cercle = cercle = Ref(cercle) self.__point = point = Ref(point) self.__angle_positif = angle_positif = Ref(angle_positif) self.point_tangence = self.__point_tangence = Point_tangence(cercle, point, angle_positif) Perpendiculaire.__init__(self, Droite(Centre(cercle), self.__point_tangence), self.__point_tangence, **styles) def _conditions_existence(self): return self.__point_tangence.existe def _set_feuille(self): # si l'on cre 2 fois de suite un tangente de mmes cercle et point, # alors on doit obtenir les deux tangentes diffrentes possibles. if "_Tangente__angle_positif" in self._valeurs_par_defaut: for objet in self.__feuille__.objets.lister(type = Tangente): if objet._Tangente__cercle is self.__cercle and objet._Tangente__point is self.__point: # on cre l'autre tangente self.__angle_positif = not objet._Tangente__angle_positif break if "_Tangente__point" in self._valeurs_par_defaut: if distance(self.__cercle.centre, self.__point) < self.__cercle.rayon: r = self.__cercle.rayon*uniform(1, 2) a = uniform(0, 2*pi) self.__point.coordonnees = self.__cercle.centre.x + r*cos(a), self.__cercle.centre.y + r*sin(a) Objet._set_feuille(self) class DemiPlan(Objet_avec_equation): u"""Un demi-plan. Le demi-plan dlimit par la droite d et contenant le point M.""" _affichage_depend_de_la_fenetre = True _enregistrer_sur_la_feuille_par_defaut = True _marqueurs = "()" _style_defaut = param.polygones _prefixe_nom = "P" droite = __droite = Argument('Ligne_generique', defaut = Droite) point = __point = Argument('Point_generique', defaut = Point) droite_incluse = __droite_incluse = Argument(bool, defaut = True) def __init__(self, droite = None, point = None, droite_incluse = None, **styles): self.__droite = droite = Ref(droite) self.__point = point = Ref(point) self.__droite_incluse = droite_incluse = Ref(droite_incluse) Objet_avec_equation.__init__(self, **styles) def _get_equation(self): return self.__droite.equation @property def equation_formatee(self): u"Equation sous forme lisible par l'utilisateur." eq = self.equation if eq is None: return u"Le demi-plan n'est pas dfini." test = self._signe() if test < 0: symbole = '<' elif test > 0: symbole = '>' else: return u"Le demi-plan n'est pas dfini." a, b, c = (nice_display(coeff) for coeff in eq) # on ne garde que quelques chiffres aprs la virgule pour l'affichage if self.__droite_incluse: symbole += '=' return formatage("%s x + %s y + %s %s 0" %(a, b, c, symbole)) def _signe(self, xy = None): x, y = (xy if xy else self.__point.xy) a, b, c = self.equation return cmp(a*x + b*y + c, 0) def _contains(self, M): signe = self._signe(M) return signe == self._signe() or (self.__droite_incluse and abs(signe) < contexte['tolerance']) def _conditions_existence(self): return self.__point not in self.__droite def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne(), self.rendu.polygone()] plot, fill = self._representation (x1, y1), (x2, y2) = self.__droite._points() plot.set_data((x1, x2), (y1, y2)) couleur, niveau = self.style(('couleur', 'niveau')) plot.set(color = couleur, linestyle = self.style("style"), linewidth = self.style("epaisseur"), zorder = niveau + 0.01) xmin, xmax, ymin, ymax = self.__canvas__.fenetre fill.set(edgecolor = couleur, facecolor = couleur, alpha = self.style('alpha'), zorder = niveau) coins = [(xmin, ymin), (xmin, ymax), (xmax, ymin), (xmax, ymax)] sommets = [coin for coin in coins if (coin in self)] sommets.extend([(x1, y1), (x2, y2)]) x0, y0 = (x1 + x2)/2, (y1 + y2)/2 sommets.sort(key = lambda xy: atan2(xy[0] - x0, xy[1] - y0)) fill.xy = sommets class Axe(Droite): u"""Un axe orient. Un axe orient servant au reprage. Cette classe sert essentiellement construire les axes (Ox) et (Oy). """ _style_defaut = param.axes point1 = __point1 = Argument("Point_generique") point2 = __point2 = Argument("Point_generique") def __init__(self, point1, point2, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Droite.__init__(self, point1, point2, **styles) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.fleche()] fleche = self._representation[0] points = self._points() if len(points) < 2: # La droite sous-jacente ne coupe pas la fentre (ou seulement e return xy0, xy1 = points x1, y1 = self.__point1 x2, y2 = self.__point2 # On s'assure que l'axe soit bien orient if abs(x2 - x1) > contexte['tolerance']: if (xy1[0] - xy0[0])*(x2 - x1) < 0: xy0, xy1 = xy1, xy0 else: if (xy1[1] - xy0[1])*(y2 - y1) < 0: xy0, xy1 = xy1, xy0 fleche.set(xy0=xy0, xy1=xy1, linewidth=self.style("epaisseur"), angle=self.style("angle"), taille=self.style("taille"), color=self.style("couleur"), linestyle=self.style("style"), zorder=self.style("niveau"), ) ########################################################################################## ## EN TRAVAUX : class Tangente_courbe(Droite_generique): u"""Une tangente une courbe. Une tangente une courbe de fonction.""" def __init__(self, courbe, point): Droite_generique.__init__(self, ) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/widgets.py0000644000175000017500000001413712014170666023205 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Feuille # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .objet import Objet_avec_coordonnees_modifiables, Argument, Ref from .textes import Texte_generique from ..pylib import uu from .. import param class Bouton(Texte_generique, Objet_avec_coordonnees_modifiables): u"""Un bouton cliquable. Un bouton avec texte. Typiquement, on lui associe une action lorsque l'on clique dessus, via la mthode `onLeftClick`. """ _style_defaut = param.boutons texte = __texte = Argument("basestring") abscisse = x = __x = Argument("Variable_generique", defaut=0) ordonnee = y = __y = Argument("Variable_generique", defaut=0) def __init__(self, x, y, texte, **styles): x, y, styles = self._recuperer_x_y(x, y, styles) texte = uu(texte) if texte != "": styles["label"] = texte self.__texte = texte = Ref(texte) self.__x = x = Ref(x) self.__y = y = Ref(y) Objet_avec_coordonnees_modifiables.__init__(self, x, y, **styles) def _creer_figure(self): x, y = self.coordonnees if not self._representation: self._representation = [self.rendu.texte(), self.rendu.polygone(), self.rendu.ligne()] Texte_generique._creer_figure(self) text, fill, plot = self._representation fill.set_visible(True) can = self.__canvas__ box = text.get_window_extent(can.get_renderer()) w, h = can.dpix2coo(.5*box.width, .5*box.height) niveau = self.style("niveau") ##if av == "left": ##x += w ##elif av == "right": ##x -= w ##if ah == "top": ##y -= h ##elif ah == "bottom": ##y += h mx, my = can.dpix2coo(10, 10) # marge verticale et horizontale (en pixels) w += mx h += my xy = [(x - w, y - h), (x - w, y + h), (x + w, y + h), (x + w, y - h)] fill.xy = xy fond = self.style('couleur_fond') fill.set(facecolor=fond, edgecolor=self._foncer(fond)) fill.zorder = niveau xy.append(xy[0]) plot.set_data(*zip(*xy)) plot.set(color='k') # TODO : utiliser FancyBboxPatch # http://matplotlib.sourceforge.net/examples/pylab_examples/fancybox_demo.html def _creer_figure(self): x, y = self.coordonnees if not self._representation: self._representation = [self.rendu.texte(), self.rendu.rectangle()] Texte_generique._creer_figure(self) text, rect = self._representation rect.set_visible(True) can = self.__canvas__ box = text.get_window_extent(can.get_renderer()) w, h = can.dpix2coo(box.width, box.height) niveau = self.style("niveau") marge = self.style("marge") ##if av == "left": ##x += w ##elif av == "right": ##x -= w ##if ah == "top": ##y -= h ##elif ah == "bottom": ##y += h mx, my = can.dpix2coo(marge, marge) # marge verticale et horizontale (en pixels) rect.set_width(w + 2*mx) rect.set_height(h + 2*my) rect.set_x(self.x - .5*w - mx) rect.set_y(self.y - .5*h - my) fond = self.style('couleur_fond') rect.set(facecolor=fond, edgecolor=self._foncer(fond)) rect.zorder = niveau # TODO : utiliser FancyBboxPatch # http://matplotlib.sourceforge.net/examples/pylab_examples/fancybox_demo.html ##def _distance_inf(self, x, y, d): ##d += self.style("marge") ##xmin, xmax, ymin, ymax = self._boite() ##return xmin - d - 3 < x < xmax + d + 3 and ymin - d < y < ymax + d def _boite(self): # Note : ymin et ymax "permutent" souvent car les transformations appliques inversent l'orientation. can = self.__canvas__ l, h = can.dimensions box = self.figure[1].get_window_extent(can.get_renderer()) xmin = box.xmin ymax = h - box.ymin xmax = box.xmax ymin = h - box.ymax return xmin, xmax, ymin, ymax def _distance_inf(self, x, y, d): # Pour cliquer sur un bouton, il faut que la distance soit nulle. return Texte_generique._distance_inf(self, x, y, 0) ##d += self.style("marge") ##xmin, xmax, ymin, ymax = self._boite() ##return xmin - d - 3 < x < xmax + d + 3 and ymin - d < y < ymax + d def _en_gras(self, booleen): fond = self.style('couleur_fond') if booleen: self._representation[1].set_facecolor(self._claircir(fond)) else: self._representation[1].set_facecolor(fond) @staticmethod def _claircir(couleur): r, g, b = couleur r = 1 - .5*(1 - r) g = 1 - .5*(1 - g) b = 1 - .5*(1 - b) return r, g, b @staticmethod def _foncer(couleur): r, g, b = couleur r = .75*r g = .75*g b = .75*b return r, g, bwxgeometrie-0.133.2.orig/wxgeometrie/geolib/constantes.py0000644000175000017500000000236312014170666023716 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # constantes # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode NONE = RIEN = 0 NAME = NOM = 1 TEXT = TEXTE = 2 FORMULE = 3 DEBUT = 0 MILIEU = 0.5 FIN = 1 wxgeometrie-0.133.2.orig/wxgeometrie/geolib/wxwidgets.py0000644000175000017500000001273712014170666023570 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Feuille # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from variables import * class Widget(Objet): u"""Un accessoire. La classe mre de tous les widgets (usage interne).""" _affichage_depend_de_la_fenetre = True # # def __init__(self, **kw): # Objet.__init__(self) # # def supprimer(self): # Objet.supprimer(self) # if self.__canvas__().select is self: # self.__canvas__().select = None # self.Destroy() # # def _get_coordonnees(self): # return self.x(), self.y() # # def _set_coordonnees(self, x = None, y = None): # if x is not None: # if y is None: # _set_coordonnees((-1,2)) est accept # x, y = x # self.x(x) # self.y(y) # # def abscisse(self): # return self.coordonnees()[0] # # def ordonnee(self): # return self.coordonnees()[1] # # def _espace_vital(self): # x, y = self.coordonnees() # l, h = self.GetSize() # l *= self.coeff(0); h*= self.coeff(1) # return (x, x + l, y, y + h) # # # def _distance_inf(self, x, y, d): # x0, y0 = self.position() # l, h = self.GetSize() # return x0 - d < x < x0 + l + d and y0 - d < y < y0 + h + d # # # ## _call = Objet.coordonnees # # # # # # # # #class Champ(Widget, wx.TextCtrl): # u"""Un champ. # # Un champ de saisie de texte.""" # # def __init__(self, texte = "", x = None, y = None, taille = 60, **kw): # if x is None: # self.synchroniser_feuille() # if self.__canvas__(): # xmin, xmax, ymin, ymax = self.__canvas__().fen() # x = module_random.uniform(xmin,xmax) # y = module_random.uniform(ymin,ymax) # del xmin, xmax, ymin, ymax # evite d'enregistrer les variables xmin, xmax... # else: # x = y = 0 # # x0, y0 = self.__canvas__().XYcoo2pix((x, y), -1) # coordonnes en pixels # # x, y, taille = self.nb2var(x), self.nb2var(y), self.nb2var(taille) # # on convertit l'entree en Variable (voir la definition de la Classe Variable en fin de page) # Widget.__init__(self, **kw) # wx.TextCtrl.__init__(self, self.__canvas__(), value = texte, pos = (int(x0), int(y0)), size = (taille, -1)) # self.style(**param.widgets) # self.finaliser(basestring, Variable, Variable, Variable) # # def _affiche(self): # x, y = self.coordonnees() # x0, y0 = self.__canvas__().XYcoo2pix((x, y), -1) # coordonnes en pixels # wx.TextCtrl.SetPosition(self, (int(x0), int(y0))) # wx.TextCtrl.SetSize(self, (self.taille(), -1)) # # # def __getattribute__(self, nom): # if nom == "texte": # object.__getattribute__(self, "__dict__")["texte"] = self.GetValue() # return object.__getattribute__(self, nom) # # # def __setattr__(self, nom, valeur): # if nom == "texte": # self.SetValue(valeur) # object.__getattribute__(self, "__dict__")[nom] = valeur # # # # # #class Bouton(Widget, wx.Button): # u"""Un bouton. # # Un bouton cliquable, auquel on peut associer diffrentes actions.""" # # # def __init__(self, texte = "", x = None, y = None, macro = None, **kw): # if x is None: # self.synchroniser_feuille() # if self.__canvas__(): # xmin, xmax, ymin, ymax = self.__canvas__().fen() # x = module_random.uniform(xmin,xmax) # y = module_random.uniform(ymin,ymax) # del xmin, xmax, ymin, ymax # evite d'enregistrer les variables xmin, xmax... # else: # x = y = 0 # # x0, y0 = self.__canvas__().XYcoo2pix((x, y), -1) # coordonnes en pixels # # x, y = self.nb2var(x), self.nb2var(y) # # on convertit l'entree en Variable (voir la definition de la Classe Variable en fin de page) # Widget.__init__(self, **kw) # wx.Button.__init__(self, self.__canvas__(), label = texte, pos = self.__canvas__().XYcoo2pix((int(x), int(y)), -1)) # self.Bind(wx.EVT_BUTTON, self.action) # self.style(**param.widgets) # self.finaliser(basestring, Variable, Variable) # # def action(self, event = None): # if self.macro is not None: # self.macro() # # # def _affiche(self): # x, y = self.coordonnees() # x0, y0 = self.__canvas__().XYcoo2pix((x, y), -1) # coordonnes en pixels # wx.Button.SetPosition(self, (int(x0), int(y0))) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/labels.py0000644000175000017500000002651212014170666023001 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Labels # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from weakref import ref from math import cos, sin, hypot, pi, acos from .objet import Objet from .textes import Texte_generique from .routines import angle_vectoriel from .points import (Glisseur_arc_cercle, Glisseur_segment, Glisseur_droite, Glisseur_vecteur, Glisseur_cercle, Glisseur_demidroite,) from .. import param from ..pylib import property2 class Label_generique(Texte_generique): u"""Un label (tiquette accole l'objet) Le label est cre automatiquement lors de la cration de l'objet. Le label n'est pas un 'vrai' objet, il n'est pas enregistr sur la feuille.""" _style_defaut = param.labels # Les coordonnes exactes n'ont aucun intrt pour une tiquette. _utiliser_coordonnees_approchees = True _initialisation_minimale = True @property def __feuille__(self): return self.parent.__feuille__ def __init__(self, parent): Texte_generique.__init__(self) self._parent = ref(parent) # self._parent est une fonction qui renvoit parent si il existe encore. # Cela vite de le maintenir en vie artificiellement par une rfrence circulaire ! self._initialiser_coordonnees() def _initialiser_coordonnees(self): pass @property def parent(self): return self._parent() @property2 def visible(self, val = None): if val is not None: assert isinstance(val, bool) self.style(visible = val) return self.style('visible') and self.parent.style('visible') def _epix2coo(self, x, y): u"Conversion des carts de pixels en carts de coordonnes." return self.__canvas__.coeff(0)*x, self.__canvas__.coeff(1)*y def _ecoo2pix(self, x, y): u"Conversion des carts de coordonnes en carts de pixels." return x/self.__canvas__.coeff(0), y/self.__canvas__.coeff(1) def _get_coordonnees(self): raise NotImplementedError # sera implemente differemment pour chaque type de label def label(self): return self.parent.label() @property def label_temporaire(self): return self.parent.label_temporaire def _creer_trace(self): u"Pas de trace pour les tiquettes." pass def figure_perimee(self): # IMPORTANT: # il faut effacer manuellement le cache, car les coordonnes ne sont pas fonction # des arguments de l'tiquette. # (Pour les autres objets, les arguments sont encapsuls dans des objets Ref # qui vident eux-mme le cache en cas de modification). self._cache.clear() Objet.figure_perimee(self) @property def existe(self): # C'est l'existence de l'objet qui dtermine celle de son tiquette. return self.parent.existe def _set_rayon(self, rayon, x, y): xmin, xmax, ymin, ymax = self._boite() rayon_max = param.distance_max_etiquette + hypot(xmin - xmax, ymin - ymax)/2 self.style(_rayon_ = min(rayon, rayon_max)) # TODO: nouvel algorithme: # 1. Chercher le point le plus proche parmi les 4 coins du texte. # 2. Adapter les coordonnes de ce point pour que la distance ne dpasse pas le maximum autoris. # 3. En dduire les nouvelles coordonnes du point d'ancrage # 4. Conclure avec les nouvelles valeur de _rayon_ et _angle_ ## def _distance_boite(self, x, y): ## xmin, xmax, ymin, ymax = self._boite() ## d1 = hypot((x-xmin), (y-ymin)) ## d2 = hypot((x-xmax), (y-ymax)) ## d3 = hypot((x-xmin), (y-ymax)) ## d4 = hypot((x-xmax), (y-ymin)) ## return min(d1, d2, d3, d4) @property def __x(self): return self.coordonnees[0] x = __x @property def __y(self): return self.coordonnees[1] y = __y class Label_point(Label_generique): u"L'tiquette d'un point." def _initialiser_coordonnees(self): self.style(_angle_ = pi/4) # les noms de style sont entre "_" pour viter des conflits avec les styles de Texte. self.style(_rayon_ = 7) # distance au point en pixels def _get_coordonnees(self): parent = self.parent r = self.style("_rayon_"); a = self.style("_angle_") rx, ry = self._epix2coo(r*cos(a), r*sin(a)) return parent.abscisse + rx, parent.ordonnee + ry def _set_coordonnees(self, x = None, y = None): if x is not None: parent = self.parent rx, ry = self._ecoo2pix(x - parent.abscisse, y - parent.ordonnee) rayon = hypot(rx, ry) if rayon: self.style(_angle_ = acos(rx/rayon)*(cmp(ry, 0) or 1)) self.style(_rayon_ = min(rayon, 50)) # distance maximale entre le point et son etiquette : 25 pixels ########################### # Les classes suivantes sont bases sur les classes Glisseur de geolib. class Label_glisseur(Label_generique): u"""Classe mre de tous les labels utilisant un objet glisseur. `classe` doit contenir le type de glisseur utilis. """ defaut = 0.5 classe = NotImplemented def _initialiser_coordonnees(self): self.style(_angle_ = pi/4) # les noms de style sont entre "_" pour viter des conflits avec les styles de Texte. self.style(_rayon_ = 7) # distance au point en pixels self.style(_k_ = self.defaut) # Sur le segment [AB], l'tiquette sera relative au point fictif M = k*A + (1-k)*B self._M = None # il serait assez dlicat d'initialiser _M d'office (risque de rcursion infinie)... def _get_coordonnees(self): if self._M is None: # import objets self._M = self.classe(self.parent, k = self.style("_k_")) #~ print self._M.coordonnees x0, y0 = self._M.coordonnees r = self.style("_rayon_"); a = self.style("_angle_") rx, ry = self._epix2coo(r*cos(a), r*sin(a)); return x0 + rx, y0 + ry def _set_coordonnees(self, x = None, y = None): if self._M is None: # import objets self._M = self.classe(self.parent, k = self.style("_k_")) if x is not None: self._M.coordonnees = (x, y) x0, y0 = self._M.coordonnees # comme _M est un glisseur, ce n'est pas x0 et y0 en gnral self.style(_k_ = self._M.parametre) rx, ry = self._ecoo2pix(x - x0, y - y0) rayon = hypot(rx, ry) if rayon: self.style(_angle_ = acos(rx/rayon)*(cmp(ry, 0) or 1)) self.style(_rayon_ = min(rayon, 50)) # distance maximale entre le point et son etiquette : 25 pixels class Label_segment(Label_glisseur): u"""L'tiquette d'un segment.""" classe = Glisseur_segment class Label_vecteur(Label_glisseur): u"""L'tiquette d'un vecteur.""" classe = Glisseur_vecteur class Label_droite(Label_glisseur): u"""L'tiquette d'une droite.""" classe = Glisseur_droite class Label_demidroite(Label_glisseur): """L'tiquette d'une demi-droite.""" classe = Glisseur_demidroite class Label_cercle(Label_glisseur): """L'tiquette d'un cercle.""" defaut = 0 classe = Glisseur_cercle class Label_arc_cercle(Label_glisseur): """L'tiquette d'un arc de cercle.""" classe = Glisseur_arc_cercle # Autres classes # ########################### class Label_polygone(Label_generique): u"L'tiquette d'un polygone." def _initialiser_coordonnees(self): self.style(_angle_ = pi/4) # les noms de style sont entre "_" pour viter des conflits avec les styles de Texte. self.style(_rayon_ = 7) # distance au point en pixels def _set_coordonnees(self, x = None, y = None): if x is not None: parent = self.parent x1, x2, y1, y2 = parent._espace_vital() x = max(min(x, x2), x1) y = max(min(y, y2), y1) rx, ry = self._ecoo2pix(x - parent.centre.abscisse, y - parent.centre.ordonnee) rayon = hypot(rx, ry) if rayon: self.style(_angle_ = acos(rx/rayon)*(cmp(ry, 0) or 1)) self.style(_rayon_ = rayon) # distance maximale entre le point et son tiquette : 25 pixels def _get_coordonnees(self): parent = self.parent r = self.style("_rayon_"); a = self.style("_angle_") rx, ry = self._epix2coo(r*cos(a), r*sin(a)) return parent.centre.abscisse + rx, parent.centre.ordonnee + ry class Label_angle(Label_generique): u"L'tiquette d'un angle." def _initialiser_coordonnees(self): self.style(_k_ = 0.5) # les noms de style sont entre "_" pour viter des conflits avec les styles de Texte. self.style(_rayon_ = param.codage["rayon"] + 10) # distance au point en pixels def _get_coordonnees(self): parent = self.parent r = self.style("_rayon_"); k = self.style("_k_") #xx, yy = self.parent.point.position() u = self._epix2coo(*parent._Secteur_angulaire__vecteur1) v = self._epix2coo(*parent._Secteur_angulaire__vecteur2) i = (1, 0) a = angle_vectoriel(i, u) b = angle_vectoriel(i, v) if parent.sens == u"non dfini" and parent._sens() < 0: a, b = b, a if b < a: b += 2*pi c = k*b + (1 - k)*a rx, ry = self._epix2coo(r*cos(c), r*sin(c)) return parent._Secteur_angulaire__point.abscisse + rx, parent._Secteur_angulaire__point.ordonnee + ry def _set_coordonnees(self, x = None, y = None): if x is not None: parent = self.parent rx, ry = self._ecoo2pix(x - parent.point.abscisse, y - parent.point.ordonnee) rayon = hypot(rx, ry) if rayon: u = self._epix2coo(*parent._Secteur_angulaire__vecteur1) v = self._epix2coo(*parent._Secteur_angulaire__vecteur2) i = (1, 0) a = angle_vectoriel(i, u) b = angle_vectoriel(i, v) if parent.sens == u"non dfini" and parent._sens() < 0: a, b = b, a c = angle_vectoriel(i, (rx, ry)) if a <> b: if b < a: b += 2*pi if c < a: c += 2*pi self.style(_k_ = (c-a)/(b-a)) self.style(_rayon_ = min(rayon, param.codage["rayon"] + 25)) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/cercles.py0000644000175000017500000006776012014170666023171 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Cercles # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from random import uniform from math import sin, cos, pi, hypot, sqrt from cmath import phase, rect from numpy import arange, cos as ncos, sin as nsin from sympy import pi as PI from .angles import Angle_vectoriel from .labels import Label_cercle, Label_arc_cercle from .lignes import Segment from .objet import Objet, Objet_avec_equation, Ref, Argument, issympy, contexte, \ pi_, FILL_STYLES, TYPES_REELS, G from .points import Point, Milieu, Centre, Point_generique, Point_equidistant, Glisseur_cercle from .routines import nice_display, angle_vectoriel, distance, formatage, trigshift, \ carre_distance_point_ellipse, vect from .variables import Mul, Rayon from .vecteurs import Vecteur from .. import param from ..pylib import fullrange ########################################################################################## # Cercles class Cercle_Arc_generique(Objet_avec_equation): u"""La classe mre de tous les cercles et arcs de cercles.""" _affichage_depend_de_la_fenetre = True _enregistrer_sur_la_feuille_par_defaut = True # cause du codage des longueurs (arcs), et du nombre de points variable des cercles centre = __centre = Argument("Point_generique") def __init__(self, centre, **styles): self.__centre = centre = Ref(centre) Objet.__init__(self, **styles) def _get_equation(self): u"""Retourne un triplet (a,b,c) tel que x**2 + y**2 + ax + by + c = 0 soit une quation du cercle.""" xO, yO = self.__centre.coordonnees r = self.rayon return (-2*xO, -2*yO, xO**2 + yO**2 - r**2) @property def rayon(self): raise NotImplementedError @property def diametre(self): return 2*self.rayon def _distance_inf(self, x, y, d): # surclasser pour les arcs x0, y0 = self._pixel(self.__centre) rx, ry = self.__canvas__.dcoo2pix(self.rayon, self.rayon) rx = abs(rx) ; ry = abs(ry) if x0 - rx - d < x < x0 + rx + d and y0 - ry - d < y < y0 + ry + d: return carre_distance_point_ellipse((x0, y0), rx, ry, (x, y), epsilon = .000001) < d**2 return False class Arc_generique(Cercle_Arc_generique): u"""La classe mre de tous les arcs de cercles.""" _style_defaut = param.arcs _prefixe_nom = "a" _affichage_depend_de_la_fenetre = True # codage centre = __centre = Argument("Point_generique") point = __point = Argument("Point_generique") def __init__(self, centre, point, **styles): self.__centre = centre = Ref(centre) self.__point = point = Ref(point) Cercle_Arc_generique.__init__(self, centre, **styles) self.etiquette = Label_arc_cercle(self) ## @property ## def centre(self): ## return self.__centre @property def rayon(self): try: return distance(self.__centre, self.__point) except TypeError: return 0 @property def equation_formatee(self): u"Equation sous forme lisible par l'utilisateur." eq = self.equation if eq is None: return u"L'objet n'est pas dfini." a, b, c = (nice_display(coeff) for coeff in eq) # on ne garde que quelques chiffres aprs la virgule pour l'affichage xmin, xmax, ymin, ymax = (nice_display(extremum) for extremum in self._espace_vital()) return formatage(u"x + y + %s x + %s y + %s = 0 avec %s 10*contexte['tolerance']: phi = phase(zM - z0) if not (a <= trigshift(phi, a) <= b): # on est au niveau de "l'ouverture" de l'arc # NB: le code n'est pas parfait ; en particulier, si le repre n'est pas orthonormal, # et que l'ellipse qui porte le cercle est *trs* allonge, # le calcul de distance est passablement faux *l'intrieur* de l'ellipse zA = z0 + rect(self.rayon, a) zB = z0 + rect(self.rayon, b) # On travaille maintenant avec les coordonnes en pixel _xA, _yA = self.__canvas__.coo2pix(zA.real, zA.imag) _xB, _yB = self.__canvas__.coo2pix(zB.real, zB.imag) return min((_xA - x)**2 + (_yA - y)**2, (_xB - x)**2 + (_yB - y)**2) < d**2 return Cercle_Arc_generique._distance_inf(self, x, y, d) def _intervalle(self): u"Renvoie deux nombres a < b. L'arc est l'ensemble des points (r*cos(t), r*sin(t)) pour t apartenant [a, b]." raise NotImplementedError def _sens(self): return 1 def _longueur(self): a, b = self._intervalle() return (b - a)*self.rayon @property def longueur(self): try: return self._longueur() except TypeError: return 0 @property def extremites(self): u"Extrmits de l'arc." raise NotImplementedError @property def info(self): return self.nom_complet + u' de longueur ' + nice_display(self.longueur) def _t(self): u, v = self._intervalle() x, y = self.__centre.coordonnees xmin, xmax, ymin, ymax = self.__feuille__.fenetre w = 3*(xmax - xmin) h = 3*(ymax - ymin) if xmin - w < x < xmax + w and ymin - h < y < ymax + h: return [fullrange(u, v, self.__canvas__.pas())] else: # Optimisation dans le cas o le centre est trs loin de la fentre. A = xmin + 1j*ymin B = xmax + 1j*ymin C = xmax + 1j*ymax D = xmin + 1j*ymax O = x + 1j*y a = phase(A - O) b = phase(B - O) c = phase(C - O) d = phase(D - O) if x >= xmax and ymin <= y <= ymax: assert (a <= 0 and b <= 0 and c >= 0 and d >= 0) a += 2*pi b += 2*pi # On rcupre la portion du cercle afficher : a, b = min(a, b, c, d), max(a, b, c, d) # Maintenant, il faut trouver l'intersection entre cette portion de cercle, et l'arc. # On s'arrange pour que a appartienne l'intervalle [u; u + 2pi[ : k = trigshift(a, u) - a a += k b += k # L'intersection est constitue d'un ou deux morceaux intersection = [] if a < v: c = min(b, v) print a, c intersection.append(fullrange(a, c, self.__canvas__.pas())) u += 2*pi v += 2*pi if b > u: c = min(b, v) intersection.append(fullrange(u, c, self.__canvas__.pas())) return intersection def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne(), self.rendu.ligne(), self.rendu.codage()] # 2 lignes pour afficher l'arc lui-mme for plot in self._representation[1:]: plot.set_visible(False) x, y = self._Arc_generique__centre.coordonnees r = self.rayon niveau, couleur, style, epaisseur = self.style(('niveau', 'couleur', 'style', 'epaisseur')) for i, t in enumerate(self._t()): plot = self._representation[i] plot.set_data(x + r*ncos(t), y + r*nsin(t)) plot.set(color=couleur, linestyle=style, linewidth=epaisseur, zorder=niveau, visible=True) # Gestion du codage des arcs de cercle (utilis pour indiquer les arcs de cercles de mme longeur) if self.style("codage"): a, b = self._intervalle() c = .5*(a + b) x0 = x + r*cos(c) y0 = y + r*sin(c) self._representation[2].set(visible=True, style=self.style('codage'), position=(x0, y0), direction=(y0 - y, x - x0), # vecteur orthogonal au rayon taille=param.codage["taille"], angle=param.codage["angle"], color=couleur, linewidth=epaisseur, zorder=niveau + 0.01, ) def _espace_vital(self): x0, y0 = self.__centre.coordonnees a, b = self._intervalle() t = arange(a, b, .003) r = self.rayon u = x0 + r*ncos(t) v = y0 + r*nsin(t) return min(u), max(u), min(v), max(v) def _contains(self, M): O = self.__centre a, b = self._intervalle() vec = vect(O, M) if hypot(*vec) < contexte['tolerance']: # M et O sont (quasi) confondus return self.rayon < contexte['tolerance'] # alors M appartient (quasiment) l'arc ssi l'arc est de rayon (quasi) nul else: c = angle_vectoriel(G.vecteur_unite, vec) if c < a: c += 2*pi # print "Test tolerance arc cercle", abs(distance(O, M) - self.rayon), a < c < b return abs(distance(O, M) - self.rayon) < contexte['tolerance'] and a - contexte['tolerance'] < c < b + contexte['tolerance'] class Arc_cercle(Arc_generique): u"""Un arc de cercle. Un arc de cercle orient, dfini par son centre et ses extremits(*), dans le sens direct. (*) note : le troisime point n'appartient pas forcment l'arc de cercle, mais sert en dlimiter l'angle au centre.""" centre = __centre = Argument("Point_generique", defaut = Point) point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, centre = None, point1 = None, point2 = None, **styles): self.__centre = centre = Ref(centre) self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Arc_generique.__init__(self, centre, point1, **styles) self._angle1 = Angle_vectoriel(G.vecteur_unite, Vecteur(centre, point1)) self._angle2 = Angle_vectoriel(G.vecteur_unite, Vecteur(centre, point2)) def image_par(self, transformation): return Arc_cercle(self.__centre.image_par(transformation), self.__point1.image_par(transformation), self.__point2.image_par(transformation)) def _intervalle(self): a = self._angle1.valeur b = self._angle2.valeur if b < a: b += 2*(PI if issympy(b) else pi) return a, b @property def extremites(self): return self.__point1, self.__point2 class Arc_points(Arc_generique): u"""Un arc dfini par 3 points. Un arc de cercle, dfini par ses extrmits, et un autre point.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Arc_generique.__init__(self, Point_equidistant(point1, point2, point3), point1, **styles) centre = self._Arc_generique__centre self._angle1 = Angle_vectoriel(G.vecteur_unite, Vecteur(centre, point1)) self._angle2 = Angle_vectoriel(G.vecteur_unite, Vecteur(centre, point2)) self._angle3 = Angle_vectoriel(G.vecteur_unite, Vecteur(centre, point3)) def image_par(self, transformation): return Arc_points(self.__point1.image_par(transformation), self.__point2.image_par(transformation), self.__point3.image_par(transformation)) def _intervalle(self): # mesure des angles dans ]-pi;pi] a = self._angle1.valeur b = self._angle2.valeur c = self._angle3.valeur if b < a: b += 2*(PI if issympy(b) else pi) if c < a: c += 2*(PI if issympy(c) else pi) if b < c: return a, c return c, a + 2*(PI if issympy(a) else pi) def _sens(self): u"Sens de parcours de l'arc : direct (1) ou indirect (-1)" # mesure des angles dans ]-pi;pi] a = float(self._angle1) b = float(self._angle2) c = float(self._angle3) if b < a: b += 2*pi if c < a: c += 2*pi if b < c: return 1 return -1 def _conditions_existence(self): return self._Arc_generique__centre.existe @property def extremites(self): return self.__point1, self.__point3 class Arc_oriente(Arc_points): u"""Un arc de cercle orient. Un arc de cercle orient, dfini par ses extrmits, et un autre point.""" _style_defaut = param.arcs_orientes point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) # on ne peut pas utiliser le mot-clef "defaut", car le style par dfaut est dj dfini (param.arcs) Arc_points.__init__(self, point1, point2, point3, **styles) def image_par(self, transformation): return Arc_oriente(self.__point1.image_par(transformation), self.__point2.image_par(transformation), self.__point3.image_par(transformation)) @property def sens(self): if self._sens() is 1: return "direct" return "indirect" def _creer_figure(self): if not self._representation: self._representation = [self.rendu.fleche_courbe()] fleche = self._representation[0] fleche.set(centre=self._Arc_generique__centre.coordonnees, rayon=self.rayon, intervalle = self._intervalle(), position=self.style("position"), double=self.style("double_fleche"), taille=self.style("taille"), angle=self.style("angle"), zorder=self.style("niveau"), color=self.style("couleur"), linewidth = self.style("epaisseur"), linestyle = self.style("style"), sens=self._sens(), ) class Demicercle(Arc_cercle): u"""Un demi-cercle. Un demi-cercle orient, dfini par ses extrmits, dans le sens direct.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Arc_cercle.__init__(self, Milieu(point1, point2), point1, point2, **styles) class Cercle_generique(Cercle_Arc_generique): u"""Un cercle gnrique. Usage interne : la classe mre pour tous les types de cercles.""" _style_defaut = param.cercles _prefixe_nom = "c" centre = __centre = Argument("Point_generique") def __init__(self, centre, **styles): self.__centre = centre = Ref(centre) Cercle_Arc_generique.__init__(self, centre, **styles) self.etiquette = Label_cercle(self) def _t(self): x, y = self.__centre.coordonnees xmin, xmax, ymin, ymax = self.__feuille__.fenetre w = 3*(xmax - xmin) h = 3*(ymax - ymin) if xmin - w < x < xmax + w and ymin - h < y < ymax + h: return fullrange(0, 2*pi, self.__canvas__.pas()) else: # Optimisation dans le cas o le centre est trs loin de la fentre. A = xmin + 1j*ymin B = xmax + 1j*ymin C = xmax + 1j*ymax D = xmin + 1j*ymax O = x + 1j*y a = phase(A - O) b = phase(B - O) c = phase(C - O) d = phase(D - O) if x >= xmax and ymin <= y <= ymax: assert (a <= 0 and b <= 0 and c >= 0 and d >= 0) a += 2*pi b += 2*pi return arange(min(a, b, c, d), max(a, b, c, d), self.__canvas__.pas()) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne()] plot = self._representation[0] x, y = self.__centre.coordonnees r = self.rayon t = self._t() plot.set_data(x + r*ncos(t), y + r*nsin(t)) plot.set(color=self.style("couleur"), linestyle = self.style("style"), linewidth=self.style("epaisseur")) plot.zorder = self.style("niveau") def _longueur(self): rayon = self.rayon return 2*rayon*pi_() perimetre = property(_longueur) def _contains(self, M): O = self.__centre return abs(distance(O, M) - self.rayon) < contexte['tolerance'] def _espace_vital(self): x, y = self.__centre.coordonnees r = self.rayon return (x - r, x + r, y - r, y + r) @property def equation_formatee(self): u"Equation sous forme lisible par l'utilisateur." eq = self.equation if eq is None: return u"L'objet n'est pas dfini." a, b, c = (nice_display(coeff) for coeff in eq) # on ne garde que quelques chiffres aprs la virgule pour l'affichage return formatage(u"x + y + %s x + %s y + %s = 0" %(a, b, c)) def _creer_nom_latex(self): u"""Cre le nom format en LaTeX. Ex: M1 -> $M_1$.""" Objet._creer_nom_latex(self) nom = self.latex_police_cursive(self.nom_latex[1:-1]) self.nom_latex = "$" + nom + "$" @property def info(self): return self.nom_complet + u' de rayon ' + nice_display(self.rayon) class Cercle_rayon(Cercle_generique): u"""Un cercle de rayon fix. Un cercle dfini par son centre et son rayon.""" centre = __centre = Argument("Point_generique", defaut = Point) rayon = __rayon = Argument("Variable_generique", defaut = 1) def __init__(self, centre = None, rayon = None, **styles): self.__centre = centre = Ref(centre) self.__rayon = rayon = Ref(rayon) Cercle_generique.__init__(self, centre, **styles) def image_par(self, transformation): from transformations import Homothetie, Rotation, Translation, Reflexion if isinstance(transformation, Homothetie): return Cercle_rayon(self.__centre.image_par(transformation), Mul(Rayon(self), transformation.rapport)) elif isinstance(transformation, (Rotation, Translation, Reflexion)): return Cercle_rayon(self.__centre.image_par(transformation), Rayon(self)) raise NotImplementedError #return Cercle(self.__centre.image_par(transformation), Glisseur_cercle(self).image_par(transformation)) def _conditions_existence(self): return self.__rayon >= 0 def _set_feuille(self): if "_Cercle__rayon" in self._valeurs_par_defaut: xmin, xmax, ymin, ymax = self.__feuille__.fenetre self.__rayon = .5*uniform(0, min(abs(xmin - xmax), abs(ymin - ymax))) # self._valeurs_par_defaut.discard("_Cercle__rayon") if "_Cercle__centre" in self._valeurs_par_defaut: xmin, xmax, ymin, ymax = self.__feuille__.fenetre r = self.__rayon self.__centre.coordonnees = uniform(xmin + r, xmax - r), uniform(ymin + r, ymax - r) # self._valeurs_par_defaut.discard("_Cercle__centre") Objet._set_feuille(self) class Cercle(Cercle_generique): u"""Un cercle. Un cercle dfini par son centre et un point du cercle.""" centre = __centre = Argument("Point_generique", defaut = Point) point = __point = Argument("Point_generique", defaut = Point) def __new__(cls, *args, **kw): if len(args) == 1 and isinstance(args[0], basestring): newclass = Cercle_equation elif len(args) == 1 and isinstance(args[0], Segment): newclass = Cercle_diametre elif len(args) == 2 and (isinstance(args[1], TYPES_REELS) or isinstance(args[1], basestring)): newclass = Cercle_rayon elif len(args) == 3 and isinstance(args[0], Point_generique) \ and isinstance(args[1], Point_generique) \ and isinstance(args[2], Point_generique): newclass = Cercle_points else: return object.__new__(cls) objet = newclass.__new__(newclass, *args, **kw) objet.__init__(*args, **kw) return objet def __init__(self, centre = None, point = None, **styles): self.__centre = centre = Ref(centre) self.__point = point = Ref(point) Cercle_generique.__init__(self, centre, **styles) @property def rayon(self): return distance(self.__centre, self.__point) def image_par(self, transformation): return Cercle(self.__centre.image_par(transformation), self.__point.image_par(transformation)) class Cercle_diametre(Cercle_generique): u"""Un cercle dfini par un diamtre. Un cercle dfini par un diamtre [AB].""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): if isinstance(point1, Segment) and point2 is None: point2 = point1.point2 point1 = point1.point1 self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Cercle_generique.__init__(self, Milieu(point1, point2), **styles) @property def rayon(self): return distance(self._Cercle_generique__centre, self.__point1) def image_par(self, transformation): return Cercle_diametre(self.__point1.image_par(transformation), self.__point2.image_par(transformation)) class Cercle_points(Cercle_generique): u"""Un cercle dfini par 3 points. Un cercle dfini par la donne de 3 points du cercle.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Cercle_generique.__init__(self, Point_equidistant(point1, point2, point3), **styles) @property def rayon(self): try: return distance(self._Cercle_generique__centre, self.__point1) except TypeError: return 0 def _conditions_existence(self): return self._Cercle_generique__centre.existe def image_par(self, transformation): return Cercle_points(self.__point1.image_par(transformation), self.__point2.image_par(transformation), self.__point3.image_par(transformation)) class Cercle_equation(Cercle_generique): u"""Un cercle dfini par une quation. Un cercle d'quation donne sous forme d'un triplet (a, b, c). (x**2 + y**2 + ax + by + c = 0)""" a = __a = Argument("Variable_generique") b = __b = Argument("Variable_generique") c = __c = Argument("Variable_generique") def __init__(self, a = 0, b = 0, c = -1, **styles): self.__a = a = Ref(a) self.__b = b = Ref(b) self.__c = c = Ref(c) Objet.__init__(self) # pour pouvoir utiliser 'Centre(self)', l'objet doit dj tre initialis Cercle_generique.__init__(self, Centre(self), **styles) @property def rayon(self): return sqrt((self.__a**2 + self.__b**2)/4 - self.__c) def _get_equation(self): u"Retourne un triplet (a, b, c), tel que x**2 + y**2 + ax + by + c = 0 soit une quation du cercle." return self.__a, self.__b, self.__c def _set_equation(self, a = None, b = 0, c = -1): if a is not None: self.__a = a self.__b = b self.__c = c def _conditions_existence(self): return self.__a**2 + self.__b**2 - 4*self.__c >= 0 ## def a(self, valeur = None): ## return self.__a(valeur) ## ## a = property(a, a) ## ## def b(self, valeur = None): ## return self.__b(valeur) ## ## b = property(b, b) ## ## def c(self, valeur = None): ## return self.__c(valeur) ## ## c = property(c, c) def image_par(self, transformation): return Cercle_rayon(Centre(self).image_par(transformation), Glisseur_cercle(self).image_par(transformation)) class Disque(Cercle_generique): u"""Un disque. Un disque dfini par le cercle le dlimitant.""" _style_defaut = param.polygones _prefixe_nom = "d" cercle = __cercle = Argument("Cercle_generique", defaut = Cercle) def __init__(self, cercle = None, **styles): self.__cercle = cercle = Ref(cercle) Cercle_generique.__init__(self, Centre(cercle), **styles) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.polygone()] fill = self._representation[0] x, y = self._Cercle_generique__centre.coordonnees r = self.rayon t = fullrange(0, 2*pi , self.__canvas__.pas()) fill.xy = zip(x + r*ncos(t), y + r*nsin(t)) fill._alpha = self.style("alpha") fill._color = self.style("couleur") fill._linestyle = FILL_STYLES.get(self.style("style"), "solid") fill._linewidth = self.style("epaisseur") fill.zorder = self.style("niveau") @property def rayon(self): return self.__cercle.rayon def _distance_inf(self, x, y, d): x, y = self.__canvas__.pix2coo(x, y) return distance(self._Cercle_generique__centre, (x, y)) <= self.rayon def _contains(self, M): return distance(self._Cercle_generique__centre, M) - self.rayon <= contexte['tolerance'] @property def aire(self): return self.rayon**2*pi_() @property def info(self): return self.nom_complet + u" d'aire " + nice_display(self.aire) @property def equation_formatee(self): u"Equation sous forme lisible par l'utilisateur." eq = self.equation if eq is None: return u"L'objet n'est pas dfini." a, b, c = (nice_display(coeff) for coeff in eq) # on ne garde que quelques chiffres aprs la virgule pour l'affichage return formatage(u"x + y + %s x + %s y + %s <= 0" %(a, b, c)) def image_par(self, transformation): return Disque(self.__cercle.image_par(transformation)) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/polyedres.py0000644000175000017500000005042212014170666023542 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from cmath import exp as cexp, log as clog from math import pi from .objet import Ref, Argument, Arguments, ArgumentNonModifiable, Objet, \ RE_NOM_OBJET, FILL_STYLES from .points import Point_generique, Point, Barycentre, Point_pondere from .lignes import Segment from .routines import point_dans_enveloppe_convexe, segments_secants, point_dans_polygone from .transformations import Translation, Rotation from .vecteurs import Vecteur from ..pylib import is_in, print_error from .. import param ########################################################################################## # Perspectives cavalires class Arete(Segment): u"""Une arte. Une arte d'un polydre, reliant le point numero 'n' au point numero 'p'. Note: n et p commencent 0. L'objet est cr automatiquement lors de la cration du polydre. De plus, si l'objet est supprim, le polydre est automatiquement supprim.""" # _style_defaut = param.aretes _prefixe_nom = "a" polyedre = __polyedre = ArgumentNonModifiable("Polyedre_generique") n = __n = ArgumentNonModifiable("int") p = __p = ArgumentNonModifiable("int") def __init__(self, polyedre, n, p, **styles): self.__polyedre = polyedre self.__n = n self.__p = p self._cachee = False # _styles = styles # styles = dict(polyedre.style()) # styles.update(_styles) Segment.__init__(self, polyedre._Polyedre_generique__sommets[n], polyedre._Polyedre_generique__sommets[p], **styles) self.__polyedre._cache.remove('test_aretes') # Segment.__init__(self, polyedre._Polyedre_generique__sommets[n], polyedre._Polyedre_generique__sommets[(n + 1)%len(polyedre._Polyedre_generique__sommets)], **polyedre.style()) # self._enregistrer = False # l'objet ne doit pas tre sauvegard # def supprimer(self): # self.__polyedre.supprimer() def _modifier_hierarchie(self, valeur = None): # Voir commentaires pour Sommet_polyedre._modifier_hierarchie N = len(self.__polyedre._Polyedre_generique__points) Objet._modifier_hierarchie(self, self.__polyedre._hierarchie + (self.__n + self.__p/N + N + 2)/(3*N + 2)) def _update(self, objet): u"""Pseudo mise jour: seul un objet identique est accept. Cela sert pour les objets crs automatiquement, qui peuvent tre enregistrs deux fois dans la feuille.""" if isinstance(objet, Arete) and self.__polyedre is objet._Arete__polyedre and self.__n == objet._Arete__n and self.__p is objet._Arete__p: self.style(**objet.style()) else: raise RuntimeError def cachee(self, value = None): if value is True: self._cachee = True self.style(style = "--") elif value is False: self._cachee = False self.style(style = "-") return self._cachee cachee = property(cachee, cachee) # def style(self, nom_de_style = None, refresh = False, **kwargs): # self.__polyedre._cache.remove('test_aretes') # Objet.style(self, nom_de_style = nom_de_style, refresh = refresh, **kwargs) def _creer_figure(self): # On s'assure que le test des artes a bien eu lieu. self.__polyedre._cache.get('test_aretes', self.__polyedre._tester_aretes) Segment._creer_figure(self) class Sommet_polyedre(Point_generique): u"""Un sommet. Le nime sommet d'un polydre. Note: n commence 0. L'objet est cr automatiquement lors de la cration du polyedre. De plus, si l'objet est supprim, le polydre est automatiquement supprim.""" ## _style_defaut = {"legende" : wxgeo.RIEN} _prefixe_nom = "S" # Un sommet peut-tre li un point, c'est--dire avoir toujours les mmes coordonnes que ce point _point_lie = None polyedre = __polyedre = ArgumentNonModifiable("Polyedre_generique") n = __n = ArgumentNonModifiable("int") def __init__(self, polyedre, n, **styles): self.__polyedre = polyedre self.__n = n Point_generique.__init__(self, **styles) def _get_coordonnees(self): return self.__polyedre._Polyedre_generique__points[self.__n].coordonnees def _set_coordonnees(self, x, y): if self._point_lie is not None: self._point_lie._set_coordonnees(x, y) def _modifier_hierarchie(self, valeur = None): # Pour les sauvegardes par exemple, il est prfrable que les sommets, puis les artes, # apparaissent juste aprs la construction du polydre ; ils doivent occuper des places conscutives dans la hirarchie. # Par exemple, si le polydre a 4 sommets, et si sa place dans la hirarchie est 18, ses trois sommets # auront comme valeur hirarchique, dans l'ordre, 18.1, 18.2, 18.3 et 18.4, # et ses artes auront pour valeur hirarchique 18.6, 18.7, 18.8, 18.9. N = len(self.__polyedre._Polyedre_generique__points) Objet._modifier_hierarchie(self, self.__polyedre._hierarchie + (self.__n + 1)/(3*N + 2)) def _update(self, objet): u"""Pseudo mise jour: seul un objet identique est accept. Cela sert pour les objets crs automatiquement, qui peuvent tre enregistrs deux fois dans la feuille.""" if isinstance(objet, Sommet_polyedre) and self.__polyedre is objet._Sommet_polyedre__polyedre and self.__n == objet._Sommet_polyedre__n : self.style(**objet.style()) else: raise RuntimeError def _lier_sommet(self, point): u"""Lie le sommet un point, en le rendant dplaable.""" self._point_lie = point self.style(couleur = "m") def _deplacable(self, *args, **kw): return self._point_lie is not None _deplacable = _modifiable = property(_deplacable, _deplacable) class Polyedre_generique(Objet): u"""Un polydre gnrique. Classe mre de toutes les reprsentations de polydres.""" _style_defaut = param.polyedres _prefixe_nom = "p" points = __points = Arguments("Point_generique") def __init__(self, *points, **styles): n = len(points) self.__points = points = tuple(Ref(obj) for obj in points) self.__centre = Barycentre(*(Point_pondere(point, 1) for point in points)) Objet.__init__(self, **styles) # self.etiquette = Label_polyedre(self) self.__sommets = tuple(Sommet_polyedre(self, i) for i in xrange(n)) # 'aretes' contient la liste des artes sous la forme de couples de numros de sommets. # ex: [(0, 1), (0, 2), (0,3), (1, 2), (1, 3), (2, 3)] pour un ttradre. aretes = styles.pop("aretes", []) self.__aretes = tuple(Arete(self, i, j) for i, j in aretes) # 'faces' contient la liste des faces sous la forme de tuples de numros de sommets. # ex: [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)] pour un ttradre. faces = styles.pop("faces", []) self.__faces = tuple(faces) # Les faces 'principales' seront colories plus fonces. # Typiquement, il s'agit de la vue de face. faces_principales = styles.pop("faces_principales", []) self.__faces_principales = tuple(faces_principales) def _set_feuille(self): n = len(self.__points) p = len(self.__aretes) # On enregistre sur la feuille les arguments crs par dfauts qui doivent l'tre if hasattr(self, "_valeurs_par_defaut")\ and self._valeurs_par_defaut\ and not self._style.has_key("_noms_"): noms = re.findall(RE_NOM_OBJET, self._nom) noms_args, args = zip(*self._iter_arguments) correspondance = True # On tente de dtecter via le nom de l'objet le nom que doit prendre chacun de ses arguments. # Par exemple, si un rectangle s'appelle ABCD, alors les points qui le constituent # doivent prendre pour noms A, B, C et D. if len(noms) == n: for i in xrange(n): if self.__points[i]._nom != "" and self.__points[i]._nom != noms[i]: correspondance = False break else: correspondance = False if correspondance: self._style["_noms_"] = {"sommets": n*[""], "aretes": p*("", )} mode = "points" for i in xrange(n): if mode == "points": if i < len(args) and is_in(args[i], self.__points): if "_" + self.__class__.__name__ + "__" + noms_args[i] in self._valeurs_par_defaut: self.__feuille__.objets[noms[i]] = args[i] else: mode = "sommets" if mode == "sommets": self._style["_noms_"]["sommets"][i] = noms[i] self._style["_noms_"]["sommets"] = tuple(self._style["_noms_"]["sommets"]) print "SOMMETS", self._style["_noms_"]["sommets"] # chec du nommage intelligent : on se rabat sur des noms alatoires else: for nom_arg in self._valeurs_par_defaut: self.__feuille__.objets[''] = getattr(self, nom_arg) self._valeurs_par_defaut = [] # On rfrence automatiquement tous les cts et sommets du polydre dans la feuille. # (En particulier, en mode graphique, cela permet de faire apparaitre tous les sommets du polydre lorsque celui-ci est cr) points_feuille = self.__feuille__.objets.lister(Point_generique) noms = self._style.get("_noms_", {"sommets": n*("", ), "aretes": p*("", )}) for i in xrange(n): # On exclue les sommets qui seraient dj dans la feuille : if not is_in(self.__points[i], points_feuille): nom = noms["sommets"][i] self.__feuille__.objets[nom] = self.__sommets[i] for i in xrange(p): nom = noms["aretes"][i] self.__feuille__.objets[nom] = self.__aretes[i] # Exceptionnellement, il ne faut pas faire appel la mthode Objet._set_feuille. ## def _set_feuille(self): ## # On rfrence automatiquement tous les cts et sommets du polyedre dans la feuille. ## # (En particulier, en mode graphique, cela permet de faire apparaitre tous les sommets du polyedre lorsque celui-ci est cr) ## Objet._set_feuille(self) ## n = len(self.__points) ## p = len(self.__aretes) ## points_feuille = self.__feuille__.objets(Point_generique) ## noms = self._style.get("_noms_", {"sommets": n*("", ), "aretes": p*("", )}) ## for i in xrange(n): ## # On exclue les sommets qui seraient dj dans la feuille : ## if not is_in(self.__points[i], points_feuille): ## nom = noms["sommets"][i] ## self.__feuille__.objets[nom] = self.__sommets[i] ## for i in xrange(p): ## nom = noms["aretes"][i] ## self.__feuille__.objets[nom] = self.__aretes[i] def __repr__(self, *args, **kwargs): self.style(_noms_ = { "sommets" : tuple(sommet.nom for sommet in self.__sommets), "aretes": tuple(arete.nom for arete in self.__aretes)}) return Objet.__repr__(self, *args, **kwargs) @property def centre(self): return self.__centre centre_gravite = centre @property def sommets(self): return self.__sommets @property def aretes(self): return self.__aretes ## @property ## def faces(self): ## return self.__faces def _creer_figure(self): if not self._representation: self._representation = [self.rendu.polygone() for face in self.__faces] for face, fill in zip(self.__faces, self._representation): xy = [self.__points[n].coordonnees for n in face + (face[0],)] niveau = self.style("niveau") couleur = self.style("couleur") fill.xy = xy if face in self.__faces_principales: fill.set_alpha(min(2*self.style("alpha"), 1.)) else: fill.set_alpha(self.style("alpha")) hachures = self.style("hachures") if hachures is not None: fill.set_hatch(hachures) fill.set(edgecolor=couleur, facecolor=couleur) fill.zorder = niveau - 0.01 fill.set_linestyle(FILL_STYLES.get(self.style("style"), "solid")) ## def image_par(self, transformation): ## return Polyedre(*(point.image_par(transformation) for point in objet._Polyedre_generique__points)) def _distance_inf(self, x, y, d): xy = [self._pixel(pt) for pt in self.__points] return point_dans_enveloppe_convexe((x,y), xy) def _espace_vital(self): points = self.__points x1 = min(pt.abscisse for pt in points) x2 = max(pt.abscisse for pt in points) y1 = min(pt.ordonnee for pt in points) y2 = max(pt.ordonnee for pt in points) return (x1, x2, y1, y2) def _secantes(self, arete1, arete2): p1, p2 = arete1 p3, p4 = arete2 return segments_secants(self.__points[p1], self.__points[p2], self.__points[p3], self.__points[p4]) def _tester_aretes(self): u"Mthode surclasser." raise NotImplementedError class Tetraedre(Polyedre_generique): u"""Un ttradre. La projection d'un ttradre.""" point1 = __point1 = Argument("Point_generique", defaut = lambda:Point()) point2 = __point2 = Argument("Point_generique", defaut = lambda:Point()) point3 = __point3 = Argument("Point_generique", defaut = lambda:Point()) point4 = __point4 = Argument("Point_generique", defaut = lambda:Point()) def __init__(self, point1 = None, point2 = None, point3 = None, point4 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) self.__point4 = point4 = Ref(point4) styles["aretes"] = [(0, 1), (0, 2), (1, 2), (0,3), (1, 3), (2, 3)] styles["faces"] = [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)] styles["faces_principales"] = [(0, 1, 2)] Polyedre_generique.__init__(self, point1, point2, point3, point4, **styles) def _tester_aretes(self): print "Test aretes 1", self.__point1.coordonnees print "Test aretes 2", self.__point2.coordonnees print "Test aretes 3", self.__point3.coordonnees print "Test aretes 4", self.__point4.coordonnees # Ce qui suit doit tre gr *avant* l'affichage du polydre, et de ses artes. if point_dans_polygone(self.__point4.coordonnees, [self.__point1.coordonnees, self.__point2.coordonnees, self.__point3.coordonnees]): print "3 cachees -> ok" self._Polyedre_generique__aretes[3].cachee = True self._Polyedre_generique__aretes[4].cachee = True self._Polyedre_generique__aretes[5].cachee = True else: self._Polyedre_generique__aretes[3].cachee = self._secantes((1, 2), (0, 3)) self._Polyedre_generique__aretes[4].cachee = self._secantes((0, 2), (1, 3)) self._Polyedre_generique__aretes[5].cachee = self._secantes((0, 1), (2, 3)) class Sommet_cube(Point_generique): u"""Un sommet d'un rectangle. (Usage interne).""" _style_defaut = param.points_deplacables point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) angle = __angle = Argument("Angle_generique", defaut = pi/6) rapport = __rapport = Argument("Variable_generique", defaut = 0.6) def __init__(self, point1, point2, angle, rapport, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) self.__rapport = rapport = Ref(rapport) Point_generique.__init__(self, **styles) def _get_coordonnees(self): k = self.__rapport a = self.__angle.radian zA = self.__point1.z zB = self.__point2.z zE = k*cexp(1j*a)*(zB - zA) + zA print "HHeLiBeBCNOFNe", k*cexp(1j*a), (zB - zA), zA, zE return zE.real, zE.imag def _set_coordonnees(self, x, y): zA = self.__point1.z zB = self.__point2.z z = x + 1j*y try: if abs(zB - zA) > param.tolerance: # TODO: cas o l'angle n'est pas en radian self.__angle.val = clog((z - zA)/(zB - zA)).imag except (OverflowError, ZeroDivisionError): if param.debug: print_error() class Cube(Polyedre_generique): u"""Un cube. La projection d'un cube.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) angle = __angle = Argument("Angle_generique", defaut = pi/6) rapport = __rapport = Argument("Variable_generique", defaut = 0.6) def _tester_aretes(self): # Ce qui suit doit tre gr *avant* l'affichage du polydre, et de ses artes. pts = self._Polyedre_generique__points aretes = self._Polyedre_generique__aretes face_avant = pts[:4] # cas o un des sommets est cach par la face avant for i in (4, 5, 6, 7): if point_dans_polygone(pts[i], face_avant): for arete in aretes: if arete._Arete__n == i or arete._Arete__p == i: arete.cachee = True else: arete.cachee = False return Polyedre_generique._conditions_existence(self) # sinon, on cherche quelle arrte "coupe" la face avant. for i in (4, 5, 6, 7): j = i - 4 # sommet correspondant de la face avant if self._secantes((i, j), ((j + 1)%4, (j + 2)%4)) or self._secantes((i, j), ((j + 2)%4, (j + 3)%4)): for arete in aretes: if arete._Arete__n == i or arete._Arete__p == i: arete.cachee = True else: arete.cachee = False return Polyedre_generique._conditions_existence(self) def __init__(self, point1 = None, point2 = None, angle = None, rapport = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__angle = angle = Ref(angle) self.__rapport = rapport = Ref(rapport) styles["aretes"] = [(0, 1), (1, 2), (2, 3), (3, 0), # face avant (4, 5), (5, 6), (6, 7), (7, 4), # face arrire (0, 4), (1, 5), (2, 6), (3, 7)] styles["faces"] = [(0, 1, 2, 3), (4, 5, 6, 7), (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (3, 0, 4, 7)] styles["faces_principales"] = [(0, 1, 2, 3)] point3 = Rotation(point2, -pi/2)(point1) point4 = Rotation(point1, pi/2)(point2) point5 = Sommet_cube(point1, point2, angle, rapport) t = Translation(Vecteur(point1, point5)) point6 = t(point2) point7 = t(point3) point8 = t(point4) Polyedre_generique.__init__(self, point1, point2, point3, point4, point5, point6, point7, point8, **styles) self._Polyedre_generique__sommets[4]._lier_sommet(point5) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/angles.py0000644000175000017500000002722612014170666023013 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode from random import uniform from math import pi from sympy import pi as PI from .labels import Label_angle from .objet import Objet_numerique, Objet, Ref, Argument, issympy, contexte, pi_, FILL_STYLES from .points import Point from .routines import nice_display, angle_vectoriel from .variables import Variable from .vecteurs import Vecteur_libre, Vecteur from .. import param from ..pylib import warning class Angle_generique(Objet_numerique): u"""Un angle gnrique (en radian). Classe mre des angles orients ou non.""" _prefixe_nom = "a" def __init__(self, **styles): Objet.__init__(self, **styles) @property def radian(self): return self.val rad = radian @property def degre(self): if self.existe: val = self.val*180 return val/pi if isinstance(val, float) else val/pi_() deg = degre @property def grad(self): if self.existe: return self.val*200/pi_() @property def info(self): unite = contexte['unite_angle'] if unite == 'd': val = nice_display(self.degre) + u'\u00B0' elif unite == 'g': val = nice_display(self.grad) + " grad" else: val = nice_display(self.rad) + " rad" return self.nom_complet.rstrip() + u" de valeur " + val @staticmethod def _convertir(objet): unite = contexte['unite_angle'] if isinstance(objet, basestring) and objet[-1] == u'\u00B0': objet = objet[:-1] unite = 'd' if not isinstance(objet, Variable): objet = Variable._convertir(objet) return Angle_libre(objet, unite = unite) class Secteur_angulaire(Angle_generique): u"Classe mre de tous les angles 'affichables'." _style_defaut = param.angles _affichage_depend_de_la_fenetre = True point = __point = Argument("Point_generique", defaut = Point) vecteur1 = __vecteur1 = Argument("Vecteur_generique", defaut = Vecteur_libre) vecteur2 = __vecteur2 = Argument("Vecteur_generique", defaut = Vecteur_libre) def __init__(self, point = None, vecteur1 = None, vecteur2 = None, **styles): self.__point = point = Ref(point) self.__vecteur1 = vecteur1 = Ref(vecteur1) self.__vecteur2 = vecteur2 = Ref(vecteur2) Angle_generique.__init__(self, **styles) self.etiquette = Label_angle(self) def _get_valeur(self): return angle_vectoriel(self.__vecteur1, self.__vecteur2) def _sens(self): x1, y1 = self.__vecteur1 x2, y2 = self.__vecteur2 return cmp(x1*y2 - x2*y1, 0) @property def sens(self): if self._sens() < 0: return "indirect" return "direct" def _creer_figure(self): codage = self.style("codage") niveau = self.style("niveau") style = self.style("style") if param.codage_automatique_angle_droits and not codage: if abs(abs(self.radian) - pi/2) < contexte['tolerance']: codage = "^" if not self._representation: ang = self.rendu.angle() self._representation = [ang, self.rendu.codage_angle(angle_associe=ang)] u = self.__canvas__.dcoo2pix(*self.__vecteur1) v = self.__canvas__.dcoo2pix(*self.__vecteur2) i = (1, 0) a = angle_vectoriel(i, v) b = angle_vectoriel(i, u) if isinstance(self, Angle) and self._sens() < 0: a, b = b, a if b < a: b += 2*pi angle, codage_angle = self._representation angle.set(rayon=param.codage['rayon'], position=self.__point.xy, taille=param.codage['taille'], intervalle=(a, b), angle=param.codage['angle'], style=codage, zorder=niveau - 0.01, alpha=self.style('alpha'), linewidth=self.style('epaisseur'), facecolor=self.style('couleur'), linestyle = FILL_STYLES.get(style, 'solid'), ) codage_angle.set(visible=bool(codage), linewidth=self.style('epaisseur'), linestyle = FILL_STYLES.get(style, 'solid'), ) def _espace_vital(self): x, y = self.__point.coordonnees x1, y1, = self.__vecteur1.coordonnees x2, y2 = self.__vecteur2.coordonnees xmin = min(x, x + x1, x + x2) xmax = max(x, x + x1, x + x2) ymin = min(y, y + y1, y + y2) ymax = max(y, y + y1, y + y2) return xmin, xmax, ymin, ymax def _distance_inf(self, x, y, d): x0, y0 = self._pixel(self.__point) #print (x - x0)**2 + (y - y0)**2 if (x - x0)**2 + (y - y0)**2 > (param.codage["rayon"] + d)**2: return False x, y = self.__canvas__.pix2coo(x, y) x0, y0 = self.__point.coordonnees u = (x - x0, y - y0) if abs(u[0]) + abs(u[1]) < contexte['tolerance']: # Vecteur inexistant (le pointeur de la souris est au sommet de l'angle) return True elif isinstance(self, Angle) and self._sens() < 0: a = angle_vectoriel(self.__vecteur2, self.__vecteur1) b = angle_vectoriel(self.__vecteur2, u) else: a = angle_vectoriel(self.__vecteur1, self.__vecteur2) b = angle_vectoriel(self.__vecteur1, u) #print a, b if a < 0: return not a < b < 0 else: return 0 <= b <= a def _conditions_existence(self): return self.__vecteur1.norme > contexte['tolerance'] and self.__vecteur2.norme > contexte['tolerance'] def image_par(self, transformation): return Secteur_angulaire(self.__point.image_par(transformation), self.__vecteur1.image_par(transformation), self.__vecteur2.image_par(transformation)) class Angle_oriente(Secteur_angulaire): u"""Un angle orient. Un angle orient dfini par 3 points A, B, C -> angle (BA>, BC>).""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Secteur_angulaire.__init__(self, point2, Vecteur(point2, point1), Vecteur(point2, point3), **styles) class Angle(Secteur_angulaire): u"""Un angle. Un angle non orient, dfini par 3 points A, B, C -> angle /ABC\.""" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) point3 = __point3 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, point3 = None, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Secteur_angulaire.__init__(self, point2, Vecteur(point2, point1), Vecteur(point2, point3), **styles) def _get_valeur(self): return abs(Secteur_angulaire._get_valeur(self)) @property def sens(self): return u"non dfini" # attention en cas de modification : cette valeur est utilise dans la classe Label_angle def image_par(self, transformation): return Angle(self.__point1.image_par(transformation), self.__point2.image_par(transformation), self.__point3.image_par(transformation)) class Angle_libre(Angle_generique): u"""Un angle libre. Un angle dfini par sa valeur numrique (en radian).""" variable = __variable = Argument("Variable_generique", defaut = lambda: uniform(0, 2*pi)) def __init__(self, variable = None, unite = None, **styles): # Gestion des units d'angle (degr et grad) if unite is None: unite = contexte['unite_angle'] else: if unite == u'\u00B0' or unite.startswith('deg'): unite = 'd' elif unite.startswith('gr'): unite = 'g' if isinstance(variable, basestring): variable = variable.strip() if variable[-1] == u'\u00B0': unite = 'd' variable = variable[:-1] if variable[-1] == u'\u00c2': # Problme frquent d'encodage (utf8 interprt comme latin1) if param.debug: warning('Angle: encodage incorrect (utf8/latin1).') variable = variable[:-1] elif variable.endswith(' rad'): unite = 'r' variable = variable[:-4] elif variable.endswith(' grad'): unite = 'g' variable = variable[:-5] if unite in ('d', 'g'): coeff = 180 if unite == 'd' else 200 if isinstance(variable, Variable): variable = variable.contenu if isinstance(variable, basestring): variable = '(' + variable + ')*pi/%s' %coeff elif issympy(variable): variable *= PI/coeff else: variable *= pi/coeff self.__variable = variable = Ref(variable) # Quel que soit le contexte (radian ou degr), les angles sont # stocks en interne en radian. Pour que `repr(angle)` puisse # tre correctement valu si le contexte est en degr, il faut # qu'apparaisse dans les paramtres `unite='r'`. # Le plus simple, c'est de le faire via le dictionnaire `styles`. styles['unite'] = 'r' Angle_generique.__init__(self, **styles) def _creer_figure(self): pass # les angles libres ne sont pas affichs. def _set_valeur(self, val = None): self.__variable = val def _get_valeur(self): return self.__variable.valeur class Angle_vectoriel(Angle_generique): u"""Un angle dfini par des vecteurs. Un angle orient dfini par deux vecteurs (non affich).""" vecteur1 = __vecteur1 = Argument("Vecteur_generique", defaut = Vecteur_libre) vecteur2 = __vecteur2 = Argument("Vecteur_generique", defaut = Vecteur_libre) def __init__(self, vecteur1 = None, vecteur2 = None, **styles): self.__vecteur1 = vecteur1 = Ref(vecteur1) self.__vecteur2 = vecteur2 = Ref(vecteur2) Angle_generique.__init__(self, **styles) def _conditions_existence(self): return self.__vecteur1.norme > contexte['tolerance'] and self.__vecteur2.norme > contexte['tolerance'] def _get_valeur(self): return angle_vectoriel(self.__vecteur1, self.__vecteur2) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/feuille.py0000644000175000017500000015121112014170666023157 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Feuille # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Ce module contient essentiellement la classe Feuille. C'est la classe qui va accueillir tous les objets geometriques. # Elle fait l'intermediaire entre les objets et le Panel ou s'affichent les objets. from keyword import iskeyword from random import choice from string import letters from math import pi, e from types import MethodType, GeneratorType, TypeType from operator import attrgetter import re import time from sympy import Symbol, Wild, sympify, oo from ..pylib import uu, is_in, str3, property2, print_error, rstrip_, CompressedList from ..mathlib.intervalles import Union, Intervalle from .objet import Objet, contexte, souffler, G from .angles import Secteur_angulaire from .lignes import Segment from .fonctions import Fonction from .points import Point from .cercles import Arc_generique from .textes import Texte from .labels import Label_generique from .vecteurs import Vecteur_libre from .variables import Variable from .constantes import FORMULE, NOM, RIEN from .pseudo_canvas import _pseudocanvas from .. import param from .. import mathlib from ..pylib.securite import keywords_interdits_presents, keywords_interdits #assert geo.Objet is Objet def parse_equation(chaine): #XXX: bauche d'un parser d'quations left, right = chaine.split('=') chaine = left + '-(' + right + ')' chaine = mathlib.parsers.traduire_formule(chaine) expr = sympify(chaine) a = Wild('a',exclude=[x, y]) b = Wild('b',exclude=[x, y]) c = Wild('c',exclude=[x, y]) d = Wild('d',exclude=[x, y]) e = Wild('e',exclude=[x, y]) f = Wild('f',exclude=[x, y]) x = Symbol('x') y = Symbol('y') droite = a*x + b*y + c cercle = a*x**2 + a*y**2 + b*x + c*y + d class MethodesObjets(object): def __init__(self, nom_de_la_methode, *objets): self.methode = nom_de_la_methode self.objets = objets def __call__(self, *args, **kw): for objet in self.objets: getattr(objet, self.methode)(*args, **kw) class Liste_objets(object): def __init__(self, feuille, classe): self.__dict__['feuille'] = feuille self.__dict__['classe'] = classe def __iter__(self): classe = self.classe return (obj for obj in self.feuille.liste_objets() if isinstance(obj, classe)) def __setattr__(self, nom, valeur): for obj in self: setattr(obj, nom, valeur) def __delattr__(self, nom): for obj in self: delattr(obj, nom) def __getattr__(self, nom): if hasattr(self.classe, nom) and isinstance(getattr(self.classe, nom), MethodType): return MethodesObjets(nom, *self) return [getattr(obj, nom) for obj in self] def __str__(self): return self.classe.__name__.split("_")[0] + 's: ' + ', '.join(obj.nom for obj in self) __unicode__ = __repr__ = __str__ class ModeTolerant(object): u'''Mode d'excution tolrant aux erreurs. Cela sert essentiellement charger un fichier d'une ancienne version de WxGomtrie.''' def __init__(self, feuille, mode = True): self.feuille = feuille self.mode = mode def __enter__(self): object.__setattr__(self.feuille.objets, '_Dictionnaire_objets__renommer_au_besoin', self.mode) def __exit__(self, type, value, traceback): object.__setattr__(self.feuille.objets, '_Dictionnaire_objets__renommer_au_besoin', False) self.feuille.objets._Dictionnaire_objets__tmp_dict.clear() class Dictionnaire_objets(dict): u"""Cette classe est un conteneur pour les objets de la feuille, qui sont tous ses attributs, sauf ceux prcds de __ (attributs reserves pour tous les objets de la classe). Elle contient aussi tous les objets geometriques. Certaines methodes standard (comme __setattr__) sont aussi interceptees ou redefinies. Attributs spciaux: '_' fait rfrence au dernier objet enregistr dans la feuille. """ __slots__ = ('__feuille__', '__timestamp', '__renommer_au_besoin', '__tmp_dict') def __init__(self, feuille): object.__setattr__(self, '__feuille__', feuille) object.__setattr__(self, '_Dictionnaire_objets__timestamp', 0) object.__setattr__(self, '_Dictionnaire_objets__renommer_au_besoin', False) object.__setattr__(self, '_Dictionnaire_objets__tmp_dict', {}) self.__effacer() def __effacer(self): self.clear() # On ajoute au dictionnaire courant les objets gomtriques, et uniquement eux # (pas tout LIB.py !) self.update((key, val) for key, val in G.__dict__.iteritems() \ if isinstance(val, type) and issubclass(val, Objet)) # Les noms de classe peuvent aussi tre taps en minuscules (c'est plus rapide taper) self.update((key.lower(), val) for key, val in G.__dict__.iteritems() \ if isinstance(val, type) and issubclass(val, Objet)) # On ajoute au dictionnaire les fonctions mathmatiques courantes self.update((key, val) for key, val in mathlib.universal_functions.__dict__.iteritems() \ if key[0] != "_" and key != "division") self.update(pi = pi, e = e, oo = oo, \ Intervalle = Intervalle, Union = Union, \ x = Symbol("x"), y = Symbol("y"), z = Symbol("z"), \ t = Symbol("t")) self.update(pause = self.__feuille__.pause, erreur = self.__feuille__.erreur, effacer = self.__feuille__.effacer, coder = self.__feuille__.coder, effacer_codage = self.__feuille__.effacer_codage, nettoyer = self.__feuille__.nettoyer, ) dict.__setitem__(self, 'None', None) dict.__setitem__(self, 'True', True) dict.__setitem__(self, 'False', False) # NB: 'True = True' et 'False = False' : non compatible Py3k types = {'points': 'Point_generique', 'droites': 'Droite_generique', 'polygones': 'Polygone_generique', 'segments': 'Segment', 'cercles': 'Cercle_generique', 'arcs': 'Arc_generique', 'textes': 'Texte_generique', 'vecteurs': 'Vecteur_generique', 'variables': 'Variable'} d = {} for typ in types: d[typ] = Liste_objets(self.__feuille__, getattr(G, types[typ])) self.update(d) def add(self, valeur): self["_"] = valeur ## def _remove(self, valeur): ## u"Commande de bas niveau. Ne pas utiliser directement !" ## for (key, val) in self.items(): ## if val is valeur: ## self.pop(key) def _dereferencer(self, objet): u"Commande de bas niveau. Ne pas utiliser directement !" if objet._nom: self.pop(objet._nom) # Important: pour que l'objet soit bien considr non rfrenc # il faut qu'il n'ait pas de nom (on ne peut pas rfrencer 2 fois un objet). objet._nom = "" def __setitem__(self, nom, valeur): u"""Note: objets.__setattr__("a", 3) <-> objets.a = 3 Les objets d'une feuille, contrairement aux objets Python, ne peuvent pas tre redfinis comme ca... En gnral, quand on essaie d'attribuer un nom qui existe dj, ce n'est pas volontaire. Pour viter les erreurs, on impose de dtruire explicitement l'objet avant que le nom puisse tre redonn.""" ## # Stocke le rsultat de la dernire commande (objet gomtrique ou non) ## if nom == '__': ## self.__feuille__.__ = valeur ## return # Paramtres du repre -> gr directement par la feuille if nom in self.__feuille__._parametres_repere \ or nom in ('xmin', 'xmax', 'ymin', 'ymax'): return setattr(self.__feuille__, nom, valeur) # Ne pas oublier le 'return' ! nom = self.__verifier_syntaxe_nom(valeur, nom) # Pour certains types d'objets (points libres, textes, variables...), # le nom peut tre dja attribu. # Par exemple, A=Point(1,2) est valide mme si A existe dj. # L'objet A ne sera pas cras, mais actualis. # Dans cet exemple, les coordonnes de A sont actualises en (1,2) # (Autrement dit, A=(1,2) devient un alias de A(1,2) ou A.coordonnees = 1,2). # Bien sr, il faut en particulier que la valeur soit un objet de meme type. # (A = Variable(3) n'est pas valide si A est un point !) ## if self.has_key(nom): ## if hasattr(self[nom], "_update"): ## self[nom]._update(valeur) ## return # on quitte, car le nom doit toujours rfrer l'objet initial ! ## else: ## self.erreur(u"ce nom est deja utilis\xe9 : " + nom, NameError) if self.has_key(nom): try: # Pour que les variables puissent tre interprtes, il faut que la feuille soit donne if isinstance(valeur, Objet) and valeur.__feuille__ is None: valeur.__feuille__ = self.__feuille__ self[nom]._update(valeur) return # on quitte, car le nom doit toujours rfrer l'objet initial ! except Exception: print_error() if self.__renommer_au_besoin: new = self.__feuille__.nom_aleatoire(valeur, prefixe=nom) print("Warning: '%s' renomm en '%s'." %(nom, new)) nom = self.__tmp_dict[nom] = new else: self.erreur(u"Ce nom est d\xe9ja utilis\xe9 : " + nom, NameError) if not isinstance(valeur, Objet): # Permet de construire des points la vole : '=((i,sqrt(i)) for i in (3,4,5,6))' if isinstance(valeur, GeneratorType) and nom == "_": for item in valeur: self.__setitem__('', item) return # 'A = Point' est un alias de 'A = Point()' elif isinstance(valeur, TypeType) and issubclass(valeur, Objet): valeur = valeur() # Par convniance, certains types sont automatiquement convertis : # - Variable elif isinstance(valeur, (int, long, float, str, unicode)): # u=3 cree une variable valeur = Variable(valeur) # - Point elif isinstance(valeur, complex): valeur = Point(valeur.real, valeur.imag) elif hasattr(valeur, "__iter__"): valeur = tuple(valeur) # - Texte if len(valeur) in (1, 3) and isinstance(tuple(valeur)[0], basestring): # t=["Bonjour!"] cree un texte valeur = Texte(*valeur) elif len(valeur) == 2: # - Vecteur_libre if nom.islower(): # u=(1,2) cre un vecteur valeur = Vecteur_libre(*valeur) # - Point else: # A=(1,2) cree un point. valeur = Point(*valeur) if not isinstance(valeur, Objet): self.erreur("type d'objet incorrect :(%s,%s)"%(nom, valeur), TypeError) if valeur._nom: # L'objet est dj rfrenc sur la feuille ssi il a un nom. # On en fait alors une copie : ainsi, A = B est remplac par A = B.copy() valeur = valeur.copy() # On enregistre le nom (ventuellement provisoire) car la mthode '_set_feuille' de l'objet en a besoin. valeur._nom = nom valeur.__feuille__ = self.__feuille__ if nom == "_": # Attention, la feuille doit tre dj definie ! nom = valeur._nom_alea() # Pour les objets nomms automatiquement, le nom n'est pas affich par dfaut. if valeur.style("legende") == NOM: valeur.style(legende = RIEN) # les objets commencant par "_" ne sont pas affichs par dfaut (pure convention) : if nom[0] == "_": valeur.style(visible = False) ## if is_in(valeur, self.itervalues()): ## # si l'objet est deja reference sur la feuille, on en fait une copie. ## valeur = valeur.copy() # ainsi, A = B est remplace par A = B.copy() dict.__setitem__(self, nom, valeur) ## dict.__setattr__(self, "__derniere_valeur__", weakref.ref(valeur)) valeur._nom = nom valeur._timestamp = self.__timestamp object.__setattr__(self, "_Dictionnaire_objets__timestamp", self.__timestamp + 1) ## valeur.creer_figure(True) self.__feuille__._actualiser_liste_objets = True self.__feuille__.affichage_perime() def __getitem(self, nom): # renommage temporaire : nom = self.__tmp_dict.get(nom, nom) # (utilis en cas de chargement d'un fichier ancien lors d'un conflit de nom). if nom in self.__feuille__._parametres_repere \ or nom in ('xmin', 'xmax', 'ymin', 'ymax'): return getattr(self.__feuille__, nom) elif nom == "objets": return self() elif nom == "noms": return self.noms elif nom == "_": return self.__derniere_valeur() return dict.__getitem__(self, self.__convertir_nom(nom)) def __getitem__(self, nom): try: return self.__getitem(nom) except KeyError: self.erreur(u"Objet introuvable sur la feuille : " + nom, KeyError) def get(self, nom, defaut=None): try: return self.__getitem(nom) except: return defaut def __contains__(self, nom): return dict.__contains__(self, self.__convertir_nom(nom)) def __delitem__(self, nom): if nom in self.__feuille__._parametres_repere: return delattr(self.__feuille__, nom) # ne pas oublier le 'return' elif nom == "_": self.__derniere_valeur().supprimer() else: try: self[nom].supprimer() except KeyError: if param.debug: print_error() self.__feuille__._actualiser_liste_objets = True self.__feuille__.affichage_perime() __setattr__ = __setitem__ __delattr__ = __delitem__ __getattr__ = __getitem__ def lister(self, objets_caches = True, **kw): u"""Retourne la liste des objets gomtriques. Le paramtre objets_caches indique s'il faut retourner les objets cachs. kw: 'type' : types inclure 'sauf' : types exclure """ if kw: sauf = kw.get("sauf", ()) type = kw.get("type", Objet) return [obj for obj in self.values() if isinstance(obj, type) \ and not isinstance(obj, sauf) and (objets_caches or obj.style("visible"))] elif objets_caches: return [obj for obj in self.values() if isinstance(obj, Objet)] else: return [obj for obj in self.values() if isinstance(obj, Objet) and obj.style("visible")] @property def noms(self): u"""Retourne les noms de tous les objets gomtriques.""" return set(nom for nom, obj in self.items() if isinstance(obj, Objet)) @staticmethod def __convertir_nom(nom): u'''Convertit les noms contenant des `, ', ou " en noms python corrects.''' return nom.replace('`', '_prime').replace('"', '_prime_prime').replace("'", "_prime") def __verifier_syntaxe_nom(self, objet, nom, **kw): u"Vrifie que le nom est correct (ie. bien form) et le modifie le cas chant." def err(msg): if kw.get('skip_err'): return if self.__renommer_au_besoin: new = self.__feuille__.nom_aleatoire(objet) print(u"Warning: '%s' renomm en '%s'." %(nom, new)) return new else: self.erreur(msg, NameError) if nom == '': return '_' nom = self.__convertir_nom(nom) # Noms rservs en python (if, then, else, for, etc.): if iskeyword(nom): return err(u"Nom r\xe9serv\xe9 : " + nom) # Pas d'accent dans le code ici a cause de Pyshell ! # Les noms contenant '__' sont des noms rservs pour un usage futur ventuel (convention). if "__" in nom: return err(u'Un nom ne peut pas contenir "__".') # Vrifie que le nom n'est pas rserv. if nom in self.__class__.__dict__.keys(): return err(u"Nom r\xe9serv\xe9.") if not re.match("""[A-Za-z_][A-Za-z0-9_'"`]*$""", nom): return err(u"'%s' n'est pas un nom d'objet valide." %nom) # Certains noms sont rservs des usages spcifiques. # Les noms f1, f2... sont rservs aux fonctions (cf. module Traceur). if nom[0] == 'f' and not isinstance(objet, Fonction) and \ rstrip_(nom, '_prime')[1:].isdigit(): return err(u"Nom r\xe9serv\xe9 aux fonctions : " + nom) # Les noms Cf1, Cf2... sont rservs l'usage du module Traceur. if nom.startswith('Cf') and nom[2:].isdigit() and \ not(isinstance(objet, Objet) and objet.style('protege')): return err(u"Nom r\xe9serv\xe9 : " + nom) # Gestion des ' (qui servent pour les drives) if nom.endswith('_prime'): if isinstance(objet, Fonction): return err(u'Nom interdit : %s est r\xe9serv\xe9 pour la d\xe9riv\xe9e.' %nom) else: base = rstrip_(nom, '_prime') if isinstance(self.get(base, None), Fonction): return err(u'Nom interdit : %s d\xe9signe d\xe9j\xe0 la d\xe9riv\xe9e de %s.' %(nom, base)) elif isinstance(objet, Fonction): # Si la fonction doit s'appeller f, on vrifie que f', f'', f''', etc. ne correspondent pas dj des objets. for existant in self: if existant.startswith(nom) and rstrip_(existant, '_prime') == nom: return err(u'Ambiguit\xe9 : un objet %s existe d\xe9j\xe0.' %existant) return nom def _objet_renommable(self, objet, nom): u"Vrifie que le nom peut-tre attribu (c--d. qu'il est bien form, et non utilis)." nom = self.__verifier_syntaxe_nom(objet, nom) if self.has_key(nom): self.erreur(u"Ce nom est dj utilis.", NameError) return nom def __str__(self): return "Gestionnaire d'objets de la feuille '" + self.__feuille__.nom \ + "': " + str(self.noms) def __repr__(self): return "Gestionnaire d'objets de la feuille '" + self.__feuille__.nom \ + "': " + repr(self.noms) def __derniere_valeur(self): u"Dernier objet cr." return max(self.__feuille__.liste_objets(True), key = lambda obj:obj._timestamp) class Interprete_feuille(object): u"""Excute des commandes dans la feuille. Reformule galement les commandes avant de les excuter.""" def __init__(self, feuille): self.feuille = feuille def executer(self, commande, parser = True, signature = None): u"""Excute la commande dans la feuille. Si parser=False, les facilits de syntaxe (abrviations, etc.) sont dsactives pour plus de rapidit. Si signature != None, elle est utilise pour la gestion de l'historique.""" if parser: commande = self.parser(commande) if param.debug: self.feuille.save_log("REQUETE FEUILLE: " + commande) # mettre en toute fin, pour des raisons de scurit. if keywords_interdits_presents(commande): self.erreur("Mots-clefs interdits : " + ", ".join(sorted(keywords_interdits))) try: code = compile(commande, '', 'eval') val = eval(code, self.feuille.objets) if isinstance(val, Variable): if val._type == "simple": retour = unicode(val.val) else: retour = '"' + val.contenu + '" : ' + unicode(val.val) else: retour = unicode(val) except SyntaxError: exec(commande + '\n', self.feuille.objets) # Le + '\n' final contourne un bug de Python 2.5 avec with_statement retour = u'Commande excute.' finally: self.commande_executee(signature = signature) return retour def commande_executee(self, signature = None): u"""Mthode appele automatiquement aprs avoir excut une commande dans la feuille. Si l'on n'a pas utilis la mthode executer(), il faut alors l'appeler manuellement.""" self.feuille.historique.archiver(signature = signature) # TODO: A dplacer dans la console graphique d'excution ? # Redtection des objets proximit du pointeur self.feuille.canvas.redetecter = True if self.feuille.classeur is not None and self.feuille.classeur.parent is not None: self.feuille.classeur.parent.rafraichir_titre() for action in self.feuille._actions: action() def parser(self, commande): commande = commande.strip() while ' ' in commande: commande = commande.replace(' ', ' ') if commande.startswith("="): commande = "_" + commande if commande == "del": commande += " _" # Gestion des ' # NB: attention, \' a dj un sens en LaTeX commande = commande.replace("'", "_prime").replace("\\_prime", "\\'") # (A B) -> Droite(A,B) def f(m): return "Droite(%s, %s)" % m.groups() commande = re.sub(r"\([ ]?(" + mathlib.parsers.VAR + ")[ ](" + mathlib.parsers.VAR + r")[ ]?\)", f, commande) # [A B] -> Segment(A,B) def f(m): return "Segment(%s, %s)" % m.groups() commande = re.sub(r"\[[ ]?(" + mathlib.parsers.VAR + ")[ ](" + mathlib.parsers.VAR + r")[ ]?\]", f, commande) # ||u|| -> u.norme def f(m): return "%s.norme" % m.groups() commande = re.sub(r"\|\|[ ]?(" + mathlib.parsers.VAR + r")[ ]?\|\|", f, commande) # ||A>B|| -> (A>B).norme def f(m): return "(%s>%s).norme" % m.groups() commande = re.sub(r"\|\|[ ]?(" + mathlib.parsers.VAR + ")>(" + mathlib.parsers.VAR + r")[ ]?\|\|", f, commande) # 1,2 ou 1;2 ou 1 2 ou (1,2) ou (1;2) ou (1 2) *uniquement* -> Point(1,2) m = re.match("(\()?(?P" + mathlib.parsers.NBR_SIGNE + ")[ ]?[;, ][ ]?(?P" + mathlib.parsers.NBR_SIGNE + ")(?(1)\))$", commande) if m: dict = m.groupdict() commande = "Point(%s,%s)" % (dict["x"], dict["y"]) # `Bonjour !` -> Texte("Bonjour !") # NB: attention, \` a dj un sens en LaTeX def f(m): return "Texte(\"%s\")" % m.groups()[0] commande = re.sub(r"(? self.n: self.etats.pop(0) # plus rapide que "self.etats = self.etats[-self.n:]" self._derniere_signature = signature self.last_hash = hash(signature) self.feuille.vierge = False self.feuille.modifiee = True def annuler(self): if len(self.etats) > 1: etat_actuel = self.etats.pop() self.etats_annules.append(etat_actuel) if len(self.etats_annules) > self.n: self.etats_annules.pop(0) # plus rapide que "self.etats_annules = self.etats_annules[-self.n:]" self.restaurer(self.etats[-1]) self.feuille.message(u"Action annule.") self.feuille.modifiee = True else: self.feuille.message(u"Impossible d'annuler.") def refaire(self): if self.etats_annules: etat = self.etats_annules.pop() self.etats.append(etat) if len(self.etats) > self.n: self.etats.pop(0) # plus rapide que "self.etats = self.etats[-self.n:]" self.restaurer(etat) self.feuille.message(u"Action restaure.") self.feuille.modifiee = True else: self.feuille.message(u"Impossible de restaurer.") def restaurer(self, txt): self.feuille.effacer() self.feuille.charger(txt, archiver = False) class Feuille(object): u"""Feuille de travail. L'objet 'log' doit tre une liste destine contenir tous les messages. """ # Pour limiter les erreurs, on indique le(s) type(s) autoris # pour chaque paramtre. _parametres_repere = {"quadrillages": tuple, "afficher_quadrillage": bool, "afficher_axes": bool, "afficher_fleches": bool, "repere": tuple, "gradu": tuple, "utiliser_repere": bool, "liste_axes": tuple, "orthonorme": bool, "fenetre": tuple, "zoom_texte": (int, float), "zoom_ligne": (int, float), "afficher_objets_caches": bool, } def __hash__(self): return id(self) def __init__(self, classeur = None, titre = "", log = None, parametres = None, canvas = None): self.log = log self.classeur = classeur self.__canvas = canvas ## self._fenetre = self.param("fenetre") # Gestion des paramtres graphiques (repre essentiellement) self.__dict_repere = {} if parametres is None: parametres = {} for nom in self._parametres_repere: self.__dict_repere[nom] = parametres.get(nom, self.parametres_par_defaut(nom)) self.macros = {} self._cache_listes_objets = {} self._actualiser_liste_objets = True ## self._mettre_a_jour_figures = True self._affichage_a_actualiser = True self._repere_modifie = True self._objets_temporaires = [] self.__point_temporaire__ = None # Permet une optimsation de l'affichage en cas d'objet dplac self._objet_deplace = None # Verrou qui indique que des objets sont encore en cours de modification. self._verrou_affichage = None # On met ._stop True pour stopper toutes les animations en cours. self._stop = False ## self._afficher_objets_caches = False ## # Indique que l'arrire-plan doit tre redessin ## self._repere_modifie = True # Parametres permettant de gerer l'enregistrement: self.sauvegarde = { "_modifie": True, # modifications depuis dernire sauvegarde "repertoire": None, # rpertoire de sauvegarde "nom": None, # nom de sauvegarde "export": None, # nom complet utilis pour l'export } # ( crer *avant* l'historique de la feuille) self.objets = Dictionnaire_objets(self) self.historique = Historique_feuille(self) self.interprete = Interprete_feuille(self) # Informations sur le document self._infos = { "titre": titre, "auteur": param.utilisateur, "creation": time.strftime("%d/%m/%Y - %H:%M:%S",time.localtime()), "modification": time.strftime("%d/%m/%Y - %H:%M:%S",time.localtime()), "version": "", "resume": "", "notes": "", } # Actions effectuer aprs qu'une commande ait t excute. self._actions = [] # Objet.__feuille__ = self # les objets sont crees dans cette feuille par defaut # --------------------------------------- # Gestion des paramtres du repre # --------------------------------------- def lier(self, action): if not is_in(action, self._actions): self._actions.append(action) def affichage_perime(self): # NB: Utiliser une mthode au lieu d'un attribut permet de gnrer # une erreur en cas de faute de frappe. self._affichage_a_actualiser = True @property2 def modifiee(self, val = None): if val is None: return self.sauvegarde['_modifie'] self.sauvegarde['_modifie'] = val if val and self.classeur is not None: self.classeur.modifie = True def infos(self, _key_ = None, **kw): if kw: self._infos.update(kw) self.modifiee = True elif _key_ is None: return self._infos.copy() else: return self._infos[_key_] def _rafraichir_figures(self, tous_les_objets = False): u"""Recre les figures des objets sensibles la fentre d'affichage. Si tous_les_objets = True, les figure des objets non sensibles sont aussi rcres. Par ailleurs, le rafraichissement n'a pas lieu immdiatement, mais les figures sont simplement marques comme primes, et seront rafraichies la prochaine utilisation. En principe, cette mthode n'a pas tre appele directement. """ for objet in self.liste_objets(): # Il faut recalculer la position de toutes les tiquettes # quand la fentre d'affichage change. if objet._affichage_depend_de_la_fenetre: objet.figure_perimee() elif objet.etiquette: # Mme si tous les objets n'ont pas besoin d'tre rafraichis, # leurs tiquettes doivent l'tre objet.etiquette.figure_perimee() def _gerer_parametres_repere(self, item = None, **kw): if kw: self.__dict_repere.update(kw) self._repere_modifie = True if 'fenetre' in kw or 'orthonorme' in kw: self._rafraichir_figures() ## self._mettre_a_jour_figures = True if 'afficher_objets_caches' in kw: for objet in self.liste_objets(True): if not objet.style('visible'): objet.figure_perimee() self._actualiser_liste_objets = True self.affichage_perime() if item is not None: return self.__dict_repere[item] ## @property2 ## def afficher_objets_caches(self, valeur = None): ## if valeur is not None: ## self._afficher_objets_caches = valeur ## for objet in self.liste_objets(True): ## if not objet.style('visible'): ## objet.creer_figure() ## self._actualiser_liste_objets = True ## return self._afficher_objets_caches def __getattr__(self, nom): # Les parametres du repere if nom in self._parametres_repere: return self._gerer_parametres_repere(nom) return object.__getattribute__(self, nom) def __setattr__(self, nom, valeur): if nom in self._parametres_repere: # TODO: amliorer la dtection des valeurs incorrectes assert isinstance(valeur, self._parametres_repere[nom]) # tests personnaliss pour certains paramtres nom_test = '_test_valeur_' + nom if hasattr(self, nom_test): valeur = getattr(self, nom_test)(valeur) self._gerer_parametres_repere(**{nom: valeur}) else: object.__setattr__(self, nom, valeur) def __delattr__(self, nom): if nom in self._parametres_repere: self._gerer_parametres_repere(**{nom: self.parametres_par_defaut(nom)}) else: object.__delattr__(self, nom) def _test_valeur_fenetre(self, valeur): xmin, xmax, ymin, ymax = valeur xmin = xmin if xmin is not None else self.fenetre[0] xmax = xmax if xmax is not None else self.fenetre[1] ymin = ymin if ymin is not None else self.fenetre[2] ymax = ymax if ymax is not None else self.fenetre[3] if abs(xmax - xmin) < 10*contexte['tolerance'] or abs(ymax - ymin) < 10*contexte['tolerance']: self.erreur(u"Le rglage de la fentre est incorrect.", ValueError) # Les 'float()' servent contourner un bug de numpy 1.1.x et numpy 1.2.x (repr de float64) return float(min(xmin, xmax)), float(max(xmin, xmax)), float(min(ymin, ymax)), float(max(ymin, ymax)) @property2 def xmin(self, value = None): if value is None: return self.fenetre[0] self.fenetre = value, None, None, None @property2 def xmax(self, value = None): if value is None: return self.fenetre[1] self.fenetre = None, value, None, None @property2 def ymin(self, value = None): if value is None: return self.fenetre[2] self.fenetre = None, None, value, None @property2 def ymax(self, value = None): if value is None: return self.fenetre[3] self.fenetre = None, None, None, value ######################################################################################### def liste_objets(self, objets_caches = None, tri = False): u"""Liste des objets, tris ventuellement selon le style 'niveau'. NB: un systme de mise en cache est utilis si possible, contrairement .objets.lister().""" if self._actualiser_liste_objets: for key in self._cache_listes_objets: self._cache_listes_objets[key] = None if objets_caches is None: objets_caches = self.afficher_objets_caches # 4 caches, correspondants aux 4 situations possibles : # objets_caches = True, trier = True ; # objets_caches = True, trier = False ; etc. clef = 'c' if objets_caches else '' if tri: clef += 't' if self._cache_listes_objets.get(clef) is None: liste = self.objets.lister(objets_caches = objets_caches) if tri: # Exceptionnellement, on utilise '._style' au lieu de '.style' # car le gain de temps est significatif. liste.sort(key = lambda x:x._style["niveau"], reverse = True) self._cache_listes_objets[clef] = liste return self._cache_listes_objets[clef] ######################################################################################### @property2 def canvas(self, val = None): if val is None: if self.__canvas is not None: return self.__canvas elif getattr(getattr(self.classeur, 'parent', None), 'canvas', None) is not None: return self.classeur.parent.canvas return _pseudocanvas self.__canvas = val # TODO: rcrire # les paramtres par dfaut de geolib doivent tre contenus dans geolib lui-mme. def parametres_par_defaut(self, nom): if getattr(self.classeur, "parent", None) is not None: return self.classeur.parent.param(nom) else: return getattr(param, nom) @property def parametres(self): return self.__dict_repere.copy() @property def nom(self): u"Destin tre affich." nom = self.infos("titre") if self.sauvegarde["nom"]: nom += ' - ' + self.sauvegarde["nom"] return nom or "Feuille" @property def nom_complet(self): u"Destin tre affich en haut de la fentre." nom = self.modifiee and "* " or "" liste = self.sauvegarde["nom"], self.infos("titre") nom += " - ".join(s for s in liste if s) return nom def objet(self, nom): # retourne l'objet associe au nom "nom" return self.objets[nom] ####################################################################################### # Methodes se rapportant a la feuille elle-meme def redefinir(self, objet, valeur): nom = objet.nom # on rcupre la liste des arguments, et on la formate... args = valeur.strip()[valeur.find("("):-1] + ",)" # ...de manire obtenir un objet 'tuple' en l'valuant. args = eval(args, self.objets) # utiliser evalsafe la place ? heritiers = objet._heritiers() heritiers.add(objet) for arg in args: if isinstance(arg, Objet): for heritier in heritiers: if arg is heritier: self.erreur(u"Dfinition circulaire dans %s : l'objet %s se retrouve dpendre de lui-mme." %(valeur, nom)) #raise RuntimeError, "Definition circulaire dans %s : l'objet %s se retrouve dependre de lui-meme." %(valeur, nom) actuel = self.sauvegarder() valeur += "\n" + nom + ".copier_style(" + repr(objet) + ")" old = "\n" + nom + " = " + repr(objet) assert old in actuel nouveau = actuel.replace(old, "\n" + nom + "=" + valeur) if param.debug: print(nouveau) try: self.historique.restaurer(nouveau) except Exception: print_error() self.historique.restaurer(actuel) self.erreur(u"Erreur lors de la redfinition de %s." %nom) def inventaire(self): if param.debug: for obj in self.liste_objets(True): print "- " + obj.nom + " : " + repr(obj) + " (" + obj.type() + ")" liste = [uu(obj.nom_complet) + u" (" + uu(obj.titre(point_final = False)) + (not obj.style("visible") and u" invisible)" or u")") for obj in self.liste_objets(True)] liste.sort() return liste def nettoyer(self): u"Supprime les objets cachs inutiles." objets = sorted((obj for obj in self.liste_objets(True) if not obj.style('visible')), key = attrgetter("_hierarchie"), reverse = True) for obj in objets: if not any(heritier.nom for heritier in obj._heritiers()): obj.supprimer() def effacer_codage(self): u"Efface tous les codages sur les segments, angles et arcs de cercles." for obj in self.liste_objets(True): if obj.style("codage") is not None: obj.style(codage = "") ## obj.figure_perimee() ## self.affichage_perime() def coder(self): u"Codage automatique de la figure (longueurs gales, angles gaux, et angles droits)." def test(groupe, liste, i): if len(groupe) is 1: groupe[0]["objet"].style(codage = "") ## groupe[0]["objet"].creer_figure() return False else: try: for elt in groupe: elt["objet"].style(codage = liste[i]) ## elt["objet"].creer_figure() return True except IndexError: self.message(u"Le nombre de codages disponibles est insuffisant.") print_error(u"Le nombre de codages disponibles est insuffisant.") lignes = [{"longueur": obj._longueur(), "objet": obj} for obj in self.objets.lister(False, type = (Segment, Arc_generique))] if lignes: lignes.sort() # attention, le classement d'un dictionnaire se fait selon l'ordre alphabtique des clefs groupe = [lignes[0]] i = 1 for ligne in lignes[1:]: if abs(groupe[-1]["longueur"] - ligne["longueur"]) < contexte['tolerance']: groupe.append(ligne) else: resultat = test(groupe, param.codage_des_lignes, i) if resultat is None: break if resultat: i += 1 groupe = [ligne] test(groupe, param.codage_des_lignes, i) angles = [{"angle": obj.val, "objet": obj} for obj in self.objets.lister(False, type = Secteur_angulaire)] if angles: angles.sort() # attention, le classement d'un dictionnaire se fait selon l'ordre alphabtique des clefs groupe = [angles[0]] i = 2 for angle in angles[1:]: if abs(groupe[-1]["angle"] - angle["angle"]) < contexte['tolerance']: groupe.append(angle) else: # print abs(abs(groupe[-1]["angle"]) - pi/2) if abs(abs(groupe[-1]["angle"]) - pi/2) < contexte['tolerance']: for elt in groupe: elt["objet"].style(codage = "^") ## elt["objet"].creer_figure() else: resultat = test(groupe, param.codage_des_angles, i) if resultat is None: break if resultat: i += 1 groupe = [angle] if abs(abs(groupe[-1]["angle"]) - pi/2) < contexte['tolerance']: for elt in groupe: elt["objet"].style(codage = "^") ## elt["objet"].creer_figure() else: test(groupe, param.codage_des_angles, i) self.affichage_perime() def objet_temporaire(self, objet = False): if objet is not False: if self._objets_temporaires: # obj = self._objets_temporaires[0] self.affichage_perime() if objet is None: self._objets_temporaires = [] else: objet.__feuille__ = self self._objets_temporaires = [objet] return self._objets_temporaires def contient_objet(self, objet): """contient_objet(self, objet) -> bool Teste rapidement si l'objet est rpertori dans la feuille. (Ne cherche pas parmi les objets temporaires.)""" return is_in(objet, self.objets.values()) def contient_objet_temporaire(self, objet): """contient_objet_temporaire(self, objet) -> bool Teste rapidement si l'objet est rpertori comme objet temporaire dans la feuille.""" for obj in self._objets_temporaires: if obj is objet: return True return False def point_temporaire(self): if self.__point_temporaire__ is None: self.__point_temporaire__ = Point() self.__point_temporaire__.__feuille__ = self return self.__point_temporaire__ def start(self): u"Autorise le lancement d'animations." self._stop = False def stop(self): u"Arrte toutes les animations en cours." self._stop = True def animer(self, nom, debut = 0, fin = 1, pas = 0.02, periode = 0.03): u"Anime la variable nomme 'nom'." self.objets[nom].varier(debut, fin, pas, periode) ####################################################################################### # Gestion des message # def message(self, messg): # A REECRIRE # if self.canvas: # messg = "Feuille %s - %s" %(self.nom, messg) # self.canvas.message(messg) # elif param.afficher_messages and param.verbose: # print(messg) # if self.log is not None: # self.log.append(message) def message(self, messg): # A REECRIRE if contexte['afficher_messages'] and param.verbose: messg = "Feuille %s - %s" %(self.nom, messg) self.canvas.message(messg) print(messg) if self.log is not None: self.log.append(messg) def erreur(self, message, erreur = None): self.message(u"Erreur : " + uu(message)) if erreur is None: erreur = RuntimeError raise erreur, str3(message) def save_log(self, log): # Imprativement utiliser 'is not None' car le log peut tre vide. if self.log is not None: self.log.append(log) ####################################################################################### # Gestion de l'affichage # cf. API/affichage.py def lister_figures(self): u"""Renvoie deux listes de figures (artistes matplotlib). La seconde est celle des figures qui vont bouger avec l'objet deplac ; et la premire, des autres qui vont rester immobiles. S'il n'y a pas d'objet en cours de dplacement, la deuxime liste est vide. """ objet_deplace = self._objet_deplace if isinstance(objet_deplace, Label_generique): objet_deplace = objet_deplace.parent # TODO: pouvoir rafraichir uniquement l'tiquette ? ## # Rafraichit les figures s'il y a besoin: ## if self._mettre_a_jour_figures: ## self._rafraichir_figures() ## self._mettre_a_jour_figures = False # On liste tous les objets qui vont bouger avec 'objet_deplace': if objet_deplace is None: heritiers = [] else: heritiers = objet_deplace._heritiers() heritiers.add(objet_deplace) # objets non susceptibles d'tre modifis (sauf changement de fentre, etc.) liste1 = [] # objets susceptibles d'tre modifis liste2 = [] for objet in self.liste_objets(): liste = liste2 if is_in(objet, heritiers) else liste1 liste.extend(objet.figure) liste.extend(objet._trace) if objet.etiquette: liste.extend(objet.etiquette.figure) liste.extend(objet.etiquette._trace) for objet in self._objets_temporaires: liste2.extend(objet.figure) liste2.extend(objet._trace) if objet.etiquette: liste2.extend(objet.etiquette.figure) liste2.extend(objet.etiquette._trace) return liste1, liste2 def effacer_traces(self): u"Efface toutes les traces (sans enlever le mode trace des objets)." for objet in self.liste_objets(): objet.effacer_trace() self.affichage_perime() def objets_en_gras(self, *objets): u"""Met en gras les objets indiqus, et remet les autres objets en tat "normal" le cas chant.""" changements = False for objet in self.liste_objets(True): if is_in(objet, objets): val = objet.en_gras(True) else: val = objet.en_gras(False) if val is not None: changements = True if changements: self.affichage_perime() ######################################################################################### def sauvegarder(self): u"Renvoie l'ensemble des commandes python qui permettra de recrer la figure avec tous ses objets." objets = self.liste_objets(True) # on doit enregistrer les objets dans le bon ordre (suivant la _hierarchie) : objets.sort(key = attrgetter("_hierarchie_et_nom")) ## texte = "fenetre = " + repr(self.fenetre) + "\n" texte = '\n'.join(nom + ' = ' + repr(getattr(self, nom)) for nom in self._parametres_repere ) + '\n' a_rajouter_a_la_fin = "" for objet in objets: if isinstance(objet, Texte) and objet.style("legende") == FORMULE: # on fait un cas particulier pour les objets Texte, car ils peuvent contenir une formule # qui dpend d'autres objets. Leur style n'est alors appliqu qu'aprs. texte += objet.nom + " = " + objet.__repr__(False) + "\n" a_rajouter_a_la_fin += objet.nom + ".style(**" + repr(objet.style()) + ")\n" else: texte += objet.nom + " = " + repr(objet) + "\n" # Les tiquettes peuvent contenir des formules qui dpendent d'autres objets. for objet in objets: if objet.etiquette is not None: texte += objet.nom + ".etiquette.style(**" + repr(objet.etiquette.style()) + ")\n" return texte + a_rajouter_a_la_fin def effacer(self): self.objets._Dictionnaire_objets__effacer() self.affichage_perime() def charger(self, commandes, rafraichir = True, archiver = True, mode_tolerant = False): u"""Excute un ensemble de commandes dans la feuille. Usage: f = Feuille() ... commandes = f.sauvegarder() f.effacer() f.charger(commandes) """ with self.canvas.geler_affichage(actualiser=rafraichir, sablier=rafraichir): with ModeTolerant(self, mode_tolerant): try: exec(commandes, self.objets) except: print "Liste des commandes:" print "--------------------" print commandes print "--------------------" try: print_error() except: print u"Affichage de l'erreur impossible !" self.erreur(u"Chargement incomplet de la feuille.") finally: for action in self._actions: action() if archiver: self.historique.archiver() def executer(self, commande, parser = True): return self.interprete.executer(commande, parser = parser) ######################################################################################### # Gestion du zoom et des coordonnees, reglage de la fenetre d'affichage. # cf. API/affichage.py ######################################################################################### # Diverses fonctionnalites de la feuille, utilisees par les objets. # C'est un peu la boite a outils :-) def nom_aleatoire(self, objet, prefixe=None): u"""Gnre un nom d'objet non encore utilis. Si possible, le nom sera de la forme 'prefixe' + chiffres. Sinon, un prfixe alatoire est gnr.""" prefixe = (prefixe if prefixe else objet._prefixe_nom) existants = self.objets.noms for i in xrange(1000): n = len(prefixe) numeros = [int(s[n:]) for s in existants if re.match(prefixe + "[0-9]+$", s)] nom = prefixe + (str(max(numeros) + 1) if numeros else '1') nom = self.objets._Dictionnaire_objets__verifier_syntaxe_nom(objet, nom, skip_err=True) if nom is not None: return nom prefixe = ''.join(choice(letters) for i in xrange(8)) raise RuntimeError, "Impossible de trouver un nom convenable apres 1000 essais !" def pause(self): souffler() if self._stop: raise RuntimeError, "Interruption de la macro." wxgeometrie-0.133.2.orig/wxgeometrie/geolib/transformations.py0000644000175000017500000001745512014170666024776 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from math import pi from .objet import Ref, Argument, contexte, Objet, G from .points import Point_generique, Point, Point_rotation, Point_translation,\ Point_homothetie, Point_reflexion from .angles import Angle_generique, Angle_libre from .vecteurs import Vecteur_generique, Vecteur_libre from .routines import radian ########################################################################################## # Divers ##class Transformation_generique(Objet): ## u"""Une transformation. ## ## La classe mre de toutes les transformations (usage interne).""" ## def __init__(self, **styles): ## Objet.__init__(self, **styles) ## ## def __call__(self, objet): ## if isinstance(objet, Ref): ## if isinstance(objet.__objet__, Point_generique): ## # pour garder la rfrence ## return self._Point_image(objet, self) ## else: ## # dans les autres cas, ce serait assez compliqu de garder la rfrence... ## # TODO: garder (si c'est faisable) la rfrence dans les autres cas (au moins pour les variables) ## # cela viterait un certain nombre de bugs (par ex., si on modifie le rayon d'un cercle dfini par une variable, le rayon du cercle image n'est pas modifi !) ## return self.__call__(objet.__objet__) ## elif isinstance(objet, Point_generique): ## return self._Point_image(objet, self) ## elif isinstance(objet, Polygone_generique): ## # PATCH: problme avec les polygones ayant des sommets dplacables (le sommet image serait dplaable) ## return Polygone(*(self(point) for point in objet._Polygone_generique__points)) ## elif isinstance(objet, Objet) and not isinstance(objet, (Objet_numerique, Transformation_generique)): ## return objet.__class__(**dict((key, self(value)) for key, value in objet._iter_arguments)) ## else: ## return objet class Transformation_generique(Objet): u"""Une transformation. La classe mre de toutes les transformations (usage interne).""" def __init__(self, **styles): Objet.__init__(self, **styles) def __call__(self, objet): if isinstance(objet, Ref): if isinstance(objet.objet, Point_generique): # pour garder la rfrence return self._Point_image(objet, self) else: # dans les autres cas, ce serait assez compliqu de garder la rfrence... #TODO: Dans l'idal, il faudrait garder (si c'est faisable) la rfrence dans les autres cas (au moins pour les variables) # cela viterait un certain nombre de bugs (par ex., si on modifie le rayon d'un cercle dfini par une variable, le rayon du cercle image n'est pas modifi !) return self.__call__(objet.objet) elif hasattr(objet, "image_par"): return objet.image_par(self) raise NotImplementedError, "L'image de %s par %s n'est pas definie." %(objet.nom_complet, self.nom_complet) class Rotation(Transformation_generique): u"""Une rotation. Une rotation dfinie par son centre, et un angle en radian (r), grad (g) ou degr (d). L'unit par dfaut est le radian.""" _Point_image = Point_rotation centre = __centre = Argument("Point_generique", defaut='Point(0, 0)') angle = __angle = Argument("Angle_generique") def __init__(self, centre=None, angle=None, unite=None, **styles): if unite is None: unite = contexte['unite_angle'] if angle is None: angle = radian(pi/6) if not isinstance(angle, Angle_generique): angle = Angle_libre(angle, unite = unite) self.__centre = centre = Ref(centre) self.__angle = angle = Ref(angle) Transformation_generique.__init__(self, **styles) @property def radian(self): return self.__angle.radian rad = radian @property def degre(self): return self.__angle.degre deg = degre @property def grad(self): return self.__angle.grad ## conversion = {"r": 1., "g": pi/200., "d": pi/180.} ## ## @staticmethod ## def choix_unite(unite): ## u"Gestion des alias." ## if unite is None: ## return "r" ## if unite == u"" or unite.startswith("d"): ## return "d" ## if unite.startswith("g"): ## return "g" ## return "r" def __call__(self, objet): if objet is self.__centre: return objet return Transformation_generique.__call__(self, objet) class Translation(Transformation_generique): u"""Une translation. Une translation dfinie par un vecteur.""" _Point_image = Point_translation vecteur = __vecteur = Argument("Vecteur_generique", defaut=Vecteur_libre) def __init__(self, vecteur = None, **styles): self.__vecteur = vecteur = Ref(vecteur) Transformation_generique.__init__(self, **styles) @staticmethod def _convertir(objet): if isinstance(objet, Vecteur_generique): return Translation(objet) raise TypeError, "%s must be of type 'Vecteur_generique'." %objet class Reflexion(Transformation_generique): u"""Une symtrie axiale. Une symtrie axiale (rflexion) dfinie par une droite.""" _Point_image = Point_reflexion droite = __droite = Argument("Ligne_generique", defaut='Droite') def __init__(self, droite = None, **styles): self.__droite = droite = Ref(droite) Transformation_generique.__init__(self, **styles) def __call__(self, objet): if objet is self.__droite: return objet return Transformation_generique.__call__(self, objet) class Homothetie(Transformation_generique): u"""Une homothtie. Une homothtie dfinie par son centre et son rapport.""" _Point_image = Point_homothetie centre = __centre = Argument("Point_generique", defaut='Point(0, 0)') rapport = __rapport = Argument("Variable_generique") def __init__(self, centre = None, rapport = 2, **styles): self.__centre = centre = Ref(centre) self.__rapport = rapport = Ref(rapport) Transformation_generique.__init__(self, **styles) def __call__(self, objet): if objet is self.__centre: return objet return Transformation_generique.__call__(self, objet) class Symetrie_centrale(Homothetie): u"""Une symtrie centrale. Une symtrie centrale dfinie par un point.""" centre = __centre = Argument("Point_generique", defaut='Point(0, 0)') def __init__(self, centre = None, **styles): self.__centre = centre = Ref(centre) Homothetie.__init__(self, centre, -1, **styles) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/routines.py0000644000175000017500000003070712014170666023410 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Geolib # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## Cette librairie contient des fonctions mathmatiques usage interne from math import cos, sin, hypot, ceil, floor, sqrt, pi, log10 import re import numpy from sympy import sqrt as s_sqrt from .contexte import contexte from ..mathlib.universal_functions import arg as u_arg, sqrt as u_sqrt def radian(val): unite = contexte['unite_angle'] if unite == 'd': return 180*val/pi elif unite == 'g': return 200*val/pi assert unite == 'r' return val ## Fonctions rapides destines essentiellement golib def produit_scalaire(u, v): u"""Calcule le produit scalaire des vecteurs u et v. u et v doivent tre de type list, tuple, array, ou geolib.Vecteur.""" return sum(i*j for i, j in zip(u ,v)) #def angle_vectoriel(u, v): # u"""Renvoie une mesure sur ]-pi;pi] de l'angle form par les vecteurs u et v. # u et v doivent tre de type list, tuple, array, ou geolib.Vecteur, et de dimension 2.""" # return clog(complex(*v)/complex(*u)).imag def angle_vectoriel(u, v): u"""Renvoie une mesure sur ]-pi;pi] de l'angle form par les vecteurs u et v. u et v doivent tre de type list, tuple, array, ou geolib.Vecteur, et de dimension 2. Version compatible avec sympy.""" return u_arg((v[0] + v[1]*1j)/(u[0] + u[1]*1j)) #~ def _angle_vectoriel_bis_(u, v): #~ u"Autre implmentation du produit vectoriel (un peu plus lente)." #~ return (cmp(u[0]*v[1]-v[0]*u[1], 0) or 1)*acos(produit_scalaire(u, v)/(hypot(*u)*hypot(*v))) def norme(x, y): u"Implmentation rapide de la norme d'un vecteur." if isinstance(x, (int, float, long)) or isinstance(y, (int, float, long)): return hypot(x, y) return u_sqrt(x**2 + y**2) def distance(A, B): u"Distance entre les points A et B." xA, yA = A xB, yB = B return norme(xA - xB, yA - yB) def carre_distance(A, B): u"Carr de la distance entre les points A et B." xA, yA = A xB, yB = B return (xB - xA)**2+(yB - yA)**2 def vect(A, B): u"Coordonnes du vecteur A>B." xA, yA = A xB, yB = B return xB - xA, yB - yA ## Fonctions de formatage (pour l'affichage entre autres) #~ def old_nchiffres(x, n = 1): #~ u"Arrondi x en fournissant n chiffres significatifs. Ex: nchiffres(2345, 2)." #~ return x and round(x/10**floor(log10(abs(x))-n+1))*10**floor(log10(abs(x))-n+1) # Attention au cas x = 0 ! def array_zip(*args): return (numpy.array(item) for item in zip(*args)) # Les deux fonctions qui suivent sont bien plus rapides que la classe Polynome pour fournir des solutions approchees. # Elles servent comme routine dans geolib par exemple. def delta(a, b, c): return b**2 - 4*a*c def racines(a, b, c, exact=False): d = delta(a, b, c) if not d: return [-b/(2*a)] if d > 0: rac = (s_sqrt(d) if exact else sqrt(d)) return [(-rac-b)/(2*a) , (rac-b)/(2*a)] return [] def point_dans_polygone(couple, polygone): u"""Dtermine si un point est l'intrieur d'un polygone. D'aprs un algorithme de Paul Bourke.""" x, y = couple dedans = False n = len(polygone) p1x, p1y = polygone[0] for i in range(1, n + 1): p2x, p2y = polygone[i % n] if min(p1y, p2y) < y <= max(p1y, p2y) and x <= max(p1x, p2x): if p1y != p2y: xinters = (y - p1y)*(p2x - p1x)/(p2y - p1y) + p1x if p1x == p2x or x <= xinters: dedans = not dedans p1x, p1y = p2x, p2y return dedans def enveloppe_convexe(*points): u"""Donne l'enveloppe convexe d'un ensemble de points. D'aprs Dinu C. Gherman (Andrew's Monotone Chain Algorithm).""" def det(p, q, r): return (q[0]*r[1] + p[0]*q[1] + r[0]*p[1]) - (q[0]*p[1] + r[0]*q[1] + p[0]*r[1]) # Get a local list copy of the points and sort them. points = sorted(points) # Build upper half of the hull. upper = [points[0], points[1]] for p in points[2:]: upper.append(p) # Le dterminant est positif ssi l'on tourne gauche while len(upper) > 2 and det(*upper[-3:]) >= 0: del upper[-2] # Build lower half of the hull. points.reverse() lower = [points[0], points[1]] for p in points[2:]: lower.append(p) while len(lower) > 2 and det(*lower[-3:]) >= 0: del lower[-2] # Remove duplicates. del lower[0], lower[-1] # Concatenate both halfs and return. return upper + lower def point_dans_enveloppe_convexe(point, polygone): u"""Dtermine si un point est l'intrieur de l'enveloppe convexe d'un polygone.""" def det(p, q, r): return (q[0]*r[1] + p[0]*q[1] + r[0]*p[1]) - (q[0]*p[1] + r[0]*q[1] + p[0]*r[1]) xmin = min(pt[0] for pt in polygone) xmax = max(pt[0] for pt in polygone) ymin = min(pt[1] for pt in polygone) ymax = max(pt[1] for pt in polygone) if not(xmin < point[0] < xmax and ymin < point[1] < ymax): return False # Get a local list copy of the points and sort them. points = sorted(tuple(polygone) + (point,)) # Build upper half of the hull. upper = [points[0], points[1]] for p in points[2:]: upper.append(p) # Le dterminant est positif ssi l'on tourne gauche while len(upper) > 2 and det(*upper[-3:]) >= 0: del upper[-2] if point in upper: return False # Build lower half of the hull. points.reverse() lower = [points[0], points[1]] for p in points[2:]: lower.append(p) while len(lower) > 2 and det(*lower[-3:]) >= 0: del lower[-2] return point not in lower ''' def distance_point_ellipse(centre, rx, ry, point): u"""Distance approche entre un point et une ellipse oriente selon les axes. D'aprs http://www.spaceroots.org/documents/distance/node9.html""" a, b = centre x, y = point # L'ellipse est dj oriente selon les axes, on prend le centre de l'ellipse comme origine x -= a y -= b # On se ramne au premier cadran, (Ox) et (Oy) tant des axes de symtrie de l'ellipse x = abs(x) y = abs(y) # On s'arrange pour que rx soit le semi-grand axe if rx < ry: rx, ry = ry, rx x, y = y, x # Cas particulier : ellipse rduite un point if rx < param.tolerance: return x**2 + y**2 f = (rx - ry)/rx # Cas particulier : le point est confondu avec le centre de l'ellipse if x**2 + y**2 < param.tolerance**2: return ry # Cas gnral : http://www.spaceroots.org/documents/distance/node9.html s = sqrt(x**2 + y**2) cos0 = x/s sin0 = y/s t0 = sin0/(1 - cos0) a = ((1 - f)*cos0)**2 + sin0**2 b = (1 - f)**2*x*cos0 + y*sin0 c = (1 - f)**2*(x**2 - rx**2) + y**2 k0 = c/(b + sqrt(b**2 - a*c)) # Nouveau point : x0 = x - k*cos0 y0 = x - k*sin0 phi = atan2(y, x*(1 - f)**2) ''' def carre_distance_point_ellipse(centre, rx, ry, point, epsilon = None): u"""Distance approche entre un point et une ellipse oriente selon les axes. Algorithme naf, d'aprs moi-mme. ;-)""" if epsilon is None: epsilon = contexte["tolerance"] xO, yO = centre x, y = point # L'ellipse est dj oriente selon les axes, on prend le centre de l'ellipse comme origine x -= xO y -= yO # On se ramne au premier cadran, (Ox) et (Oy) tant des axes de symtrie de l'ellipse x = abs(x) y = abs(y) rx = abs(rx) ry = abs(ry) def f(t): return (rx*cos(t) - x)**2 + (ry*sin(t) - y)**2 a = 0 b = pi/2 while b - a > epsilon: i = (a + b)/2 fim = f(i - epsilon) fi = f(i) fip = f(i + epsilon) if fim < fi < fip: b = i elif fim > fi > fip: a = i else: break return fi def segments_secants(p1, p2, p3, p4): d1 = direction_droite(p3, p4, p1) d2 = direction_droite(p3, p4, p2) d3 = direction_droite(p1, p2, p3) d4 = direction_droite(p1, p2, p4) return ((d1>0 and d2<0) or (d1<0 and d2>0)) and ((d3>0 and d4<0) or (d3<0 and d4>0)) def direction_droite(pi, pj, pk): xi, yi = pi xj, yj = pj xk, yk = pk return (xk - xi)*(yj - yi) - (yk - yi)*(xj - xi) def trigshift(t, a = 0): u"Retourne le reprsentant de t[2pi] situ dans l'intervalle [a; a+2pi[." return t + 2*pi*ceil((a - t)/(2*pi)) def distance_segment(M, A, B, d): u"""Teste si la distance entre le point M et le segment [AB] est infrieure d. M, A et B sont des couples de rels, et d un rel. Cf. "distance_point_segment.odt" dans "developpeurs/maths/". """ x, y = M if A is None or B is None: if A is None and B is None: return False A = B = A or B xA, yA = A xB, yB = B x1 = min(xA, xB) - d; x2 = max(xA, xB) + d y1 = min(yA, yB) - d; y2 = max(yA, yB) + d if x1 < x < x2 and y1 < y < y2: norme2 = ((xB - xA)**2 + (yB - yA)**2) if norme2 > contexte['tolerance']: return ((yA - yB)*(x - xA) + (xB - xA)*(y - yA))**2/norme2 < d**2 else: # les extrmits du segment sont confondues return (x - xA)**2 + (y - yA)**2 < d**2 else: return False # ---------------------- # Fonctions de formatage # ---------------------- def nchiffres(x, n = 1): u"""Arrondi x en fournissant n chiffres significatifs. >>> from wxgeometrie.geolib.routines import nchiffres >>> nchiffres(2345, 2) 2300.0 """ if x: k = 10**floor(log10(abs(x))-n+1) return round(x/k)*k return x # Attention au cas x = 0 ! TRAILING_ZEROS = re.compile(r"\.[0-9]*0+(?![0-9])") def strip_trailing_zeros(s): u""""Supprime tous les zeros inutiles des nombres flottants. >>> from wxgeometrie.geolib.routines import strip_trailing_zeros >>> strip_trailing_zeros("4.0000") '4' >>> strip_trailing_zeros("4.200*x + 3.007*y + .010 = 0") '4.2*x + 3.007*y + .01 = 0' """ return re.sub(TRAILING_ZEROS, lambda m:m.group().rstrip('0').rstrip('.'), s) def nice_display(x): if isinstance(x, float): x = round(x, contexte["decimales"]) if abs(x - int(x)) < contexte["tolerance"]: x = int(x) elif hasattr(x, 'valeur'): return nice_display(x.valeur) return strip_trailing_zeros(str(x).replace('**', '^')) def arrondir(x): u"""Arrondi le nombre : un seul chiffre significatif, compris entre 1 et 5. Transforme automatiquement le nombre en entier selon le cas.""" n = nchiffres(x, sqrt(.5)) if int(n) == n: n = int(n) return n def formatage(eqn): u"""Amliore l'affichage des quations. >>> from wxgeometrie.geolib.routines import formatage >>> formatage('1 x + -1/3 y + 1 = 0') 'x - 1/3 y + 1 = 0' >>> formatage('-1 x + -1 y + -1 = 0') '-x - y - 1 = 0' >>> formatage('2 x + 0 y - 28 = 0') '2 x - 28 = 0' >>> formatage(u'x\xb2 + y\xb2 + -1 x + -4/3 y + -47/36 = 0') == u'x\xb2 + y\xb2 - x - 4/3 y - 47/36 = 0' True """ #FIXME: pour l'instant, a ne marche qu'avec x et y (il ne faut # pas qu'il y ait de xy dans l'quation, ni de t, etc.) if eqn.startswith('1 '): eqn = eqn[2:] return eqn.replace("+ -1", "- 1").replace('-1 x', '-x').replace('-1 y', '-y')\ .replace("+ -", "- ").replace('- 1 x', '- x').replace('- 1 y', '- y')\ .replace('+ 1 x', '+ x').replace('+ 1 y', '+ y')\ .replace('+ 0 x ', '').replace('+ 0 y ', '').replace('+ 0 ', '') wxgeometrie-0.133.2.orig/wxgeometrie/geolib/formules.py0000644000175000017500000001022712014170666023367 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Formule # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from weakref import ref from .routines import nice_display from ..pylib import uu from .. import param class Formule(object): # Le caractre d'erreur doit tre accept par le parser de matplotlib en mode normal *ET* en mode math. _caractere_erreur = "<.>" def _get_feuille(self): return self.__feuille def _set_feuille(self, value): self.__feuille = value liste = self._contenu[:] for i in xrange(1, len(liste), 2): self._contenu[i].__feuille__ = value __feuille__ = property(_get_feuille, _set_feuille) def __init__(self, parent, chaine = ""): from .variables import Variable if isinstance(chaine, Formule): chaine = eval(repr(chaine)) ## print "Initialisation formule:", chaine, type(chaine) self._parent = ref(parent) # self._parent est une fonction qui renvoit parent si il existe encore. Cela permet de ne pas le maintenir en vie artificiellement (pas de rfrence circulaire). #~ self._cache_repr = chaine #~ self._cache_str = "".join() if "{" not in chaine: liste = ["", "{" + chaine + "}", ""] else: liste = re.split("([{][^}]+[}])", chaine) for i in xrange(1, len(liste), 2): cache = liste[i][1:-1] # "{A.x}" -> "A.x" var = liste[i] = Variable(cache) var._cache_formule = cache ## # on va maintenant redfinir la mthode affiche de toutes les variables de la formule : au lieu d'tre inactive, la mthode affiche va actualiser l'affichage de l'objet contenant la formule. ## def affiche(self, actualiser = False, formule = self): ## formule.parent.creer_figure() ## var.affiche = new.instancemethod(affiche, var, var.__class__) self._contenu = liste # il faut faire un systme de cache pour chaque variable : # - si la variable est calculable, on renvoie la valeur de la variable (et on met jour son cache) # - sinon, on renvoie le cache s'il s'agit de repr, et s'il s'agit de str. self.__feuille__ = self.parent.__feuille__ @property def parent(self): return self._parent() def supprimer(self): for i in xrange(1, len(self._contenu), 2): self._contenu[i].supprimer() def __repr__(self): liste = self._contenu[:] for i in xrange(1, len(liste), 2): if liste[i].val is not None: liste[i]._cache_formule = str(liste[i]) liste[i] = "{" + liste[i]._cache_formule + "}" return repr(uu("".join(liste))) #~ return uu("".join(liste)) def __unicode__(self): liste = self._contenu[:] for i in xrange(1, len(liste), 2): if liste[i].val is None: s = self._caractere_erreur else: s = nice_display(liste[i]) #~ if s == "None": #~ s = "" liste[i] = s return uu("".join(liste)) def __str__(self): return unicode(self).encode(param.encodage) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/courbes.py0000644000175000017500000003213712014170666023201 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Interpolation # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from itertools import izip,chain from numpy import isnan, isinf, sign, arange, inf, append from sympy import oo from .objet import Objet, Argument, Ref from .contexte import contexte from .routines import distance_segment from .. import param from ..pylib import print_error def inf_or_nan(x): return isinf(x) or isnan(x) class Courbe_generique(Objet): u"""Classe mre de toutes les courbes.""" _affichage_depend_de_la_fenetre = True _style_defaut = param.courbes def __init__(self, **styles): Objet.__init__(self, **styles) def _distance_inf(self, x, y, d): if len(self.xarray) and len(self.yarray): u, v = self.__canvas__.coo2pix(self.xarray, self.yarray) u -= x v -= y m = min(u*u + v*v) # u*u est beaucoup plus rapide que u**2 (sic!) return m < d**2 return False def _espace_vital(self): if len(self.xarray) and len(self.yarray): return (min(self.xarray), max(self.xarray), min(self.yarray), max(self.yarray)) @property def xarray(self): if self._Objet__figure_perimee: self._creer_figure() return self._xarray @property def yarray(self): if self._Objet__figure_perimee: self._creer_figure() return self._yarray class Courbe(Courbe_generique): u"""Courbe d'une fonction. L'expression doit tre donne en fonction de 'x'. Exemple : '2x^2-1/x+1' """ _prefixe_nom = "C" __fonction = fonction = Argument("Fonction") def __init__(self, fonction, **styles): self.__fonction = fonction = Ref(fonction) Courbe_generique.__init__(self, **styles) def _creer_figure(self): ## self.__canvas__.graph.supprimer(self._representation) self._representation = [] fenetre = self.__canvas__.fenetre pas = self.__canvas__.pas() self._xarray = () self._yarray = () ancien_intervalle = None # derniere_valeur = None # derniere_fonction = None # ancien_x = None # ancien_y = None for fonction, union, e_cach in izip(self.__fonction._Fonction__fonctions, self.__fonction._Fonction__unions, self.__fonction.style('extremites_cachees')): for intervalle in union.intervalles: x = intervalle.asarray(fenetre[0], fenetre[1], pas)[0] if len(x): #TODO: cas o len(x) == 1 (et donc, x[1] n'existe pas) y = fonction(x) x0 = x[0] xN = x[-1] y0 = y[0] yN = y[-1] # Bug de wxAgg pour des valeurs trop importantes, ou pour NaN x, y = self.supprimer_valeurs_extremes(x, y, fonction) self._representation.append(self.rendu.ligne(x, y, couleur = self.style("couleur"), linestyle = self.style("style"), linewidth = self.style("epaisseur"), zorder = self.style("niveau"), )) # _xarray et _yarray ne servent pas pour la reprsentation graphique, # mais pour ._distance_inf() uniquement self._xarray = append(self._xarray, x) self._yarray = append(self._yarray, y) if fenetre[0] < intervalle.inf < fenetre[1]: if ancien_intervalle is None: self._creer_debut_morceau(x, y, intervalle, e_cach) else: print intervalle, y[0], abs(y[0] - ancien_y[-1]), abs(x[0] - ancien_x[-1]), contexte['tolerance'] , pas fusion = abs(x0 - ancien_xN) < contexte['tolerance'] \ and (abs(y0 - ancien_yN) < contexte['tolerance'] or (isnan(y0) and isnan(ancien_yN))) if fusion: #Fusion print 'Fusion', y0 if isnan(y0): print u'Fusion avance' for i in xrange(10, 70, 10): try: val1 = ancienne_fonction(ancien_xN - 8**(-i)) val2 = ancienne_fonction(x0 + 8**(-i)) if abs(val1 - val2) < contexte['tolerance']: self._append_point(x0, val1, plein = False) break except (ZeroDivisionError, ValueError): print_error() fusion = False break else: fusion = False elif not(ancien_intervalle.sup_inclus or intervalle.inf_inclus): print 'Fusion classique' self._append_point(x[0], y[0], plein = False) if not fusion: self._creer_fin_morceau(ancien_x, ancien_y, ancien_intervalle, e_cach) self._creer_debut_morceau(x, y, intervalle, e_cach) ancien_x = x ancien_y = y ancien_xN = xN ancien_yN = yN ancien_intervalle = intervalle ancienne_fonction = fonction if ancien_intervalle is not None and fenetre[0] < ancien_intervalle.sup < fenetre[1]: self._creer_fin_morceau(ancien_x, ancien_y, ancien_intervalle, e_cach) def _creer_debut_morceau(self, x, y, intervalle, e_cach): if len(y) == 0: return elif len(y) == 1: if not inf_or_nan(y[0]): self._append_point(x[0], y[0]) return if x[0] in e_cach: return if not(inf_or_nan(y[0]) or inf_or_nan(y[1])): if intervalle.inf_inclus: self._append_point(x[0], y[0]) else: vec = x[1] - x[0], y[1] - y[0] self._append_arc(x[0], y[0], vec) # TODO: cas o len(y) < 3 elif isnan(y[0]) and not (isnan(y[1]) or isnan(y[2])) : if not intervalle.inf_inclus: vec = x[2] - x[1], y[2] - y[1] self._append_arc(x[1], y[1], vec) def _creer_fin_morceau(self, x, y, intervalle, e_cach): if len(y) <= 1: return if x[-1] in e_cach: return if not(inf_or_nan(y[-1]) or inf_or_nan(y[-2])): if intervalle.sup_inclus: self._append_point(x[-1], y[-1]) else: vec = x[-2] - x[-1], y[-2] - y[-1] self._append_arc(x[-1], y[-1], vec) # TODO: cas o len(y) < 3 elif isnan(y[-1]) and not (isnan(y[-2]) or isnan(y[-3])) : if not intervalle.inf_inclus: vec = x[-3] - x[-2], y[-3] - y[-2] self._append_arc(x[-2], y[-2], vec) def _append_arc(self, x0, y0, vec): if self.style("extremites"): self._representation.append(self.rendu.arc(x0, y0, vec, color=self.style("couleur"), linewidth=self.style("epaisseur"))) def _append_point(self, x0, y0, plein = True): if self.style("extremites"): self._representation.append(self.rendu.point(x0, y0, plein=plein, color=self.style("couleur"), markeredgewidth=self.style("epaisseur"))) def _supprimer_valeurs_extremes(self, x, y, fonction, i, j): u"""Lorsque les valeurs aux bornes sont indtermines (NaN), infinies (+/-Inf) ou trs loignes de zro (2e200), on cherche les convertir en une valeur raisonnable pour la fentre d'affichage. La principale difficult est de dterminer **numriquement** la limite probable. On commence par regarder la valeur calcule par numpy la borne considre : * Si la valeur est +/-Inf, il faut tudier son signe. En effet, numpy ne peut gnralement par faire la diffrence entre des calculs du type 1/0+ et 1/0- (ex: 1/(x-3) en 3+ et 3-). L'ide est la suivante : si les valeurs diminuent en se rapprochant de la borne, alors la limite est -Inf. De mme, si elles augmentent, la limite est +Inf. On retourne alors une valeur en dehors de la fentre d'affichage, qui simule l'infini. * Si le rsultat est nombre trs loign de zro, on le tronque tout en restant en dehors de la fentre d'affichage, de manire simuler l'infini. En effet, le traceur de matplotlib ragit mal aux valeurs "extrmes". * Enfin si le rsultat est de type NaN, on s'loigne lgrement (puis de plus en plus vite) de la borne, et on reitre, dans une limite de 20 itrations. """ x0 = x[i]; y0 = y[i] x1 = x[j]; y1 = y[j] k = 2**arange(-20., 0.) entre = k*(x1 - x0) + x0 # (1 - k)*x0 + k*x1 xk = chain([x0], entre, [x1]) yk = chain([y0], fonction(entre), [y1]) y_finis = [] # dernires valeurs finies infini = False xi_infini = None for xi, yi in izip(xk, yk): if infini: if not inf_or_nan(yi): y_finis.append(yi) if len(y_finis) == 2: x0 = xi_infini y0 = self._rogner_valeur(sign(y_finis[0] - y_finis[1])*inf) break else: if isinf(yi): infini = True xi_infini = xi elif not isnan(yi): x0 = xi y0 = self._rogner_valeur(yi) break x[i] = x0 y[i] = y0 return x, y def _rogner_valeur(self, y0): u"Remplace -inf et +inf par des valeurs numriques dpassant la fentre." if isnan(y0): return y0 xmin, xmax, ymin, ymax = self.__feuille__.fenetre decalage = 100*(ymax - ymin) if isinf(y0): return (ymin - decalage) if (y0 < 0) else (ymax + decalage) # Bug de wxAgg pour des valeurs trop importantes return max(min(y0, ymax + decalage), ymin - decalage) # assert (ymin - decalage < y0 < ymax + decalage) def supprimer_valeurs_extremes(self, x, y, fonction): x, y = self._supprimer_valeurs_extremes(x, y, fonction, 0, 1) x, y = self._supprimer_valeurs_extremes(x, y, fonction, -1, -2) return x, y def _espace_vital(self): xmin = self.__fonction._Fonction__unions[0].intervalles[0].inf xmax = self.__fonction._Fonction__unions[-1].intervalles[-1].sup if xmin == -oo: xmin = None if xmax == oo: xmax = None return (xmin, xmax, min(self.yarray), max(self.yarray)) def _distance_inf(self, x, y, d): P = x, y xm = self.__canvas__.pix2coo(x - d, y)[0] xM = self.__canvas__.pix2coo(x + d, y)[0] xarray = self.xarray filtre = (xm < xarray) & (xarray < xM) xa, ya = self.__canvas__.coo2pix(xarray[filtre], self.yarray[filtre]) A = None for x, y in izip(xa, ya): B = A A = x, y if distance_segment(P, A, B, d): return True return False @staticmethod def _convertir(objet): u"Convertit un objet en fonction." return NotImplemented def _update(self, objet): if not isinstance(objet, Courbe): objet = self._convertir(objet) if isinstance(objet, Courbe): self.fonction = objet.fonction else: raise TypeError, "L'objet n'est pas une courbe." wxgeometrie-0.133.2.orig/wxgeometrie/geolib/pseudo_canvas.py0000644000175000017500000000424312014170666024366 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Pseudo-canvas # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .. import param class PseudoContexte(object): u"Contexte bidon ne faisant absolument rien." def __enter__(self): pass def __exit__(self, type, value, traceback): pass class PseudoCanvas(object): u"""Faux canvas utilis par dfaut par la feuille de travail. Permet de faire appel l'objet canvas et ses mthodes sans gnrer d'erreur lorsqu'il n'y a pas de canvas.""" def __getattr__(self, nom): if param.debug: print('Action %s non effectuee (pas de canevas).' %nom) return (lambda *args, **kw: NotImplemented) ## ## def __setattr__(self, nom, valeur): ## pass ## def geler_affichage(self, *args, **kw): return PseudoContexte() ## def detecter(self, *args, **kw): ## pass ## ## def actualiser(self, *args, **kw): ## pass def __nonzero__(self): return False def __bool__(self): return False ## def redessiner_et_actualiser(self, *args, **kw): ## pass _pseudocanvas = PseudoCanvas() wxgeometrie-0.133.2.orig/wxgeometrie/geolib/interpolations.py0000644000175000017500000002460512014170666024612 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Interpolation # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode from numpy import array, arange, append from .objet import Ref, Arguments#, Argument from .courbes import Courbe_generique from .contexte import contexte from ..pylib import fullrange from .. import param class Interpolation_generique(Courbe_generique): u"""Classe mre de toutes les interpolations.""" points = __points = Arguments("Point_generique") _style_defaut = param.interpolations def __init__(self, *points, **styles): self.__points = points = tuple(Ref(pt) for pt in points) Courbe_generique.__init__(self, **styles) def _affiche_extremites(self, vec_deb = None, vec_fin = None): couleur = self.style("couleur") niveau = self.style("niveau") epaisseur = self.style("epaisseur") debut = self.style("debut") fin = self.style("fin") # Dbut de la courbe plot = self._representation[-2] x, y = self.__points[0].coordonnees a, b = self.__points[1].coordonnees if vec_deb is None: vec_deb = (a - x, b - y) if debut is True: plot.set_data((x,), (y,)) plot.set(visible=True, color=couleur, marker="o", markeredgecolor=couleur, markersize=2*self.__canvas__.taille["o"], linewidth=epaisseur) plot.zorder = niveau elif debut is False: arc = self.rendu.arc(x, y, vec_deb) plot.set_data(*arc.get_data()) # TESTER !! plot.set(visible=True, color=couleur, linewidth=epaisseur, marker="") plot.zorder = niveau else: plot.set_visible(False) # Fin de la courbe plot = self._representation[-1] x, y = self.__points[-1].coordonnees a, b = self.__points[-2].coordonnees if vec_fin is None: vec_fin = (a - x, b - y) if fin is True: plot.set_data((x,), (y,)) plot.set(visible=True, color=couleur, marker="o", markeredgecolor=couleur, markersize=2*self.__canvas__.taille["o"], linewidth=epaisseur) plot.zorder = niveau elif fin is False: arc = self.rendu.arc(x, y, vec_fin) plot.set_data(*arc.get_data()) # TESTER !! plot.set(visible=True, color=couleur, linewidth=epaisseur, marker="") plot.zorder = niveau else: plot.set_visible(False) class Interpolation_lineaire(Interpolation_generique): u"""Une interpolation linaire. Interpolation entre les points donns par des segments joints (courbe de classe C0). """ points = __points = Arguments("Point_generique") def __init__(self, *points, **styles): if styles.get("points", None): points = styles.pop("points") self.__points = points = tuple(Ref(pt) for pt in points) Interpolation_generique.__init__(self, *points, **styles) def _creer_figure(self): n = len(self.__points) couleur = self.style("couleur") niveau = self.style("niveau") style = self.style("style") epaisseur = self.style("epaisseur") if not self._representation: self._representation = [self.rendu.ligne() for i in xrange(n + 1)] self._xarray = array([]) self._yarray = array([]) if n < 2: return pas = self.__canvas__.pas() for i in xrange(n - 1): plot = self._representation[i] x1, y1 = self.__points[i].coordonnees x2, y2 = self.__points[i+1].coordonnees plot.set_data(array((x1, x2)), array((y1, y2))) plot.set(color=couleur, linestyle=style, linewidth=epaisseur) plot.zorder = niveau # TODO: amliorer l'algo de dtection (notamment si l'chelle # sur les 2 axes n'est pas la mme). # Utiliser l'algorithme des segments. if abs(y2 - y1) < abs(x2 - x1): x_array = arange(x1, x2, pas if x1 < x2 else -pas) a = (y2 - y1)/(x2 - x1) b = y1 - a*x1 y_array = a*x_array + b elif abs(y2 - y1) > contexte['tolerance']: y_array = arange(y1, y2, pas if y1 < y2 else -pas) a = (x2 - x1)/(y2 - y1) b = x1 - a*y1 x_array = a*y_array + b else: continue self._xarray = append(self._xarray, x_array) self._yarray = append(self._yarray, y_array) self._affiche_extremites() def image_par(self, transformation): # FIXME: l'image n'est plus correcte si le nombre de points est modifi ensuite # Par exemple, interp = Interpolation_lineaire(A, B), puis interp.points = (A, B, C) return self.__class__(*(transformation(point) for point in self.__points)) class Interpolation_quadratique(Interpolation_generique): u"""Une interpolation quadratique. Interpolation des points donns par une courbe polynomiale par morceaux, et de classe C1. Pour chaque morceau, x(t)=at+bt+c et y(t)=dt+et+f, o t appartient [0;1]. """ points = __points = Arguments("Point_generique") def __init__(self, *points, **styles): if "points" in styles: points = styles.pop("points") self.__points = points = tuple(Ref(pt) for pt in points) Interpolation_generique.__init__(self, *points, **styles) def _creer_figure(self): n = len(self.__points) couleur = self.style("couleur") niveau = self.style("niveau") style = self.style("style") epaisseur = self.style("epaisseur") if not self._representation: self._representation = [self.rendu.ligne() for i in xrange(n + 1)] self._xarray = array([]) self._yarray = array([]) if n < 2: return pas = self.__canvas__.pas() t = fullrange(0, 1, pas) for i in xrange(n - 1): plot = self._representation[i] x0, y0 = self.__points[i].coordonnees x1, y1 = self.__points[i+1].coordonnees if i == 0: dx0, dy0 = x1 - x0, y1 - y0 a, b, c = x1 - dx0 - x0, dx0, x0 d, e, f = y1 - dy0 - y0, dy0, y0 u = (a*t + b)*t + c v = (d*t + e)*t + f plot.set_data(u, v) plot.set(color=couleur, linestyle=style, linewidth=epaisseur) plot.zorder = niveau dx0 = 2*a + b dy0 = 2*d + e self._xarray = append(self._xarray, u) self._yarray = append(self._yarray, v) self._affiche_extremites(vec_fin = (dx0, dy0)) class Interpolation_cubique(Interpolation_generique): u"""Une interpolation cubique. Interpolation des points donns par une courbe polynomiale par morceaux, de vecteur tangent horizontal aux sommets, et de classe C1 en gnral (ie. si x_n!=x_{n-1}). Pour chaque morceau, x(t)=at^3+bt^2+ct+d et y(t)=et^3+ft^2+gt+h, o t appartient [0;1], et y'(0)=y'(1)=0. """ points = __points = Arguments("Point_generique") def __init__(self, *points, **styles): if "points" in styles: points = styles.pop("points") self.__points = points = tuple(Ref(pt) for pt in points) Interpolation_generique.__init__(self, *points, **styles) def _creer_figure(self): n = len(self.__points) couleur = self.style("couleur") niveau = self.style("niveau") style = self.style("style") epaisseur = self.style("epaisseur") courbure = self.style("courbure") if courbure is None: courbure = 1 if not self._representation: self._representation = [self.rendu.ligne() for i in xrange(n + 1)] self._xarray = array([]) self._yarray = array([]) if n < 2: return pas = self.__canvas__.pas() t = fullrange(0, 1, pas) for i in xrange(n - 1): plot = self._representation[i] x0, y0 = self.__points[i].coordonnees x1, y1 = self.__points[i+1].coordonnees if i == 0: dx0 = x1 - x0 dy0 = y1 - y0 else: dx0 = courbure*abs(x1 - x0) if dx1 != 0: dy0 = dy1/dx1*dx0 else: dx0 = dx1 dy0 = dy1 if i < n - 2: x2, y2 = self.__points[i+2].coordonnees if cmp(y0, y1) == cmp(y1, y2): dy1 = y2 - y0 dx1 = x2 - x0 else: dy1 = 0 dx1 = courbure*abs(x1 - x0) else: dy1 = y1 - y0 dx1 = x1 - x0 a = 2*(x0 - x1) + dx0 + dx1 b = 3*(x1 - x0) -2*dx0 - dx1 c = dx0 d = x0 e = 2*(y0 - y1) + dy0 + dy1 f = 3*(y1 - y0) -2*dy0 - dy1 g = dy0 h = y0 u = ((a*t + b)*t + c)*t + d v = ((e*t + f)*t + g)*t + h plot.set_data(u, v) plot.set(color=couleur, linestyle=style, linewidth=epaisseur) plot.zorder = niveau self._xarray = append(self._xarray, u) self._yarray = append(self._yarray, v) self._affiche_extremites(vec_deb = (dx0, dy0), vec_fin = (dx1, dy1)) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/points.py0000644000175000017500000011522412014170666023052 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Points # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from random import uniform, normalvariate from math import cos, sin, pi from numpy import ndarray from sympy import cos as scos, sin as ssin from .objet import Objet_avec_coordonnees, Ref, Argument, Objet, Arguments, \ contexte, Objet_avec_coordonnees_modifiables, issympy from .routines import angle_vectoriel, vect, carre_distance, produit_scalaire, \ distance from .. import param ################################################################################ ## POINTS class Point_generique(Objet_avec_coordonnees): u"""Un point gnrique. Usage interne : la classe mre pour tous les types de points (libres, barycentres, intersection...).""" _style_defaut = param.points _prefixe_nom = "M" _enregistrer_sur_la_feuille_par_defaut = True def __init__(self, **styles): #~ self.__args = GestionnaireArguments() Objet_avec_coordonnees.__init__(self, **styles) from .labels import Label_point self.etiquette = Label_point(self) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.ligne()] x, y = self.coordonnees plot = self._representation[0] plot.set_data([x], [y]) couleur = self.style("couleur") plot.set_markeredgecolor(couleur) plot.set_markerfacecolor(couleur) plot.set_marker(self.style("style")) plot.set_markersize(self.style("taille")) plot.set_markeredgewidth(self.style("epaisseur")) plot.zorder = self.style("niveau") def _espace_vital(self): x, y = self.coordonnees return (x, x, y, y) def _get_coordonnees(self): return NotImplemented # sera implemente differemment pour chaque type de point @property def abscisse(self): return self.coordonnees[0] @property def ordonnee(self): return self.coordonnees[1] @property def affixe(self): coordonnees = self.coordonnees return coordonnees[0] + coordonnees[1]*1j x = abscisse y = ordonnee z = affixe # def _image(self, transformation): # if isinstance(transformation, Rotation): # return Point_rotation(self, transformation) # elif isinstance(transformation, Homothetie): # return Point_homothetie(self, transformation) # elif isinstance(transformation, Translation): # return Point_translation(self, transformation) # elif isinstance(transformation, Rotation): # return Point_rotation(self, transformation) def image_par(self, transformation): from .transformations import Translation, Homothetie, Reflexion, Rotation if isinstance(transformation, Rotation): return Point_rotation(self, transformation) elif isinstance(transformation, Translation): return Point_translation(self, transformation) elif isinstance(transformation, Homothetie): return Point_homothetie(self, transformation) elif isinstance(transformation, Reflexion): return Point_reflexion(self, transformation) raise NotImplementedError def __sub__(self, y): return self + (-y) def __add__(self, y): from .vecteurs import Vecteur_generique if isinstance(y, Vecteur_generique): return Point_final(self, [y]) raise TypeError, "vecteur attendu" # A>B est un alias de Vecteur(A,B) - attention aux parentheses pour 2*(A>B) ! # A ne pas utiliser en interne (code peu lisible) def __gt__(self, point2): from .vecteurs import Vecteur return Vecteur(self, point2) def _distance_inf(self, x, y, d): x0, y0 = self._pixel() return (x - x0)**2 + (y - y0)**2 < d**2 def __eq__(self, y): if self.existe: if isinstance(y, Point_generique) and y.existe: return abs(self.x - y.x) < contexte['tolerance'] and abs(self.y - y.y) < contexte['tolerance'] elif isinstance(y, (list, tuple, ndarray)) and len(y) == 2: return abs(self.x - y[0]) < contexte['tolerance'] and abs(self.y - y[1]) < contexte['tolerance'] return False def __ne__(self, y): return not (self == y) def relier_axe_x(self): if self.__feuille__ is not None: from .lignes import Segment with self.__canvas__.geler_affichage(actualiser = True): M = Point("%s.x" %self.nom, 0, fixe = True) M.label("${%s.x}$" %self.nom, formule = True) s = Segment(self, M, style = ":") self.__feuille__.objets.add(M) self.__feuille__.objets.add(s) def relier_axe_y(self): if self.__feuille__ is not None: from .lignes import Segment with self.__canvas__.geler_affichage(actualiser = True): M = Point(0, "%s.y" %self.nom, fixe = True) M.label("${%s.y}$" %self.nom, formule = True) s = Segment(self, M, style = ":") self.__feuille__.objets.add(M) self.__feuille__.objets.add(s) def relier_axes(self): if self.__feuille__ is not None: with self.__canvas__.geler_affichage(actualiser = True): self.relier_axe_x() self.relier_axe_y() @staticmethod def _convertir(objet): if hasattr(objet, "__iter__"): return Point(*objet) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" class Point(Objet_avec_coordonnees_modifiables, Point_generique): u"""Un point libre. >>> from wxgeometrie.geolib import Point >>> A = Point(7, 3) >>> print A Point(x = 7, y = 3) """ _style_defaut = param.points_deplacables abscisse = x = __x = Argument("Variable_generique", defaut = lambda: normalvariate(0, 10)) ordonnee = y = __y = Argument("Variable_generique", defaut = lambda: normalvariate(0, 10)) def __new__(cls, *args, **kw): if len(args) == 1: from .cercles import Arc_generique, Cercle_generique from .lignes import Segment, Droite_generique, Demidroite if isinstance(args[0], Droite_generique): newclass = Glisseur_droite elif isinstance(args[0], Segment): newclass = Glisseur_segment elif isinstance(args[0], Demidroite): newclass = Glisseur_demidroite elif isinstance(args[0], Cercle_generique): newclass = Glisseur_cercle elif isinstance(args[0], Arc_generique): newclass = Glisseur_arc_cercle else: return object.__new__(cls) else: return object.__new__(cls) glisseur = newclass.__new__(newclass, *args, **kw) glisseur.__init__(*args, **kw) return glisseur def __init__(self, x = None, y = None, **styles): x, y, styles = self._recuperer_x_y(x, y, styles) self.__x = x = Ref(x) self.__y = y = Ref(y) Objet_avec_coordonnees_modifiables.__init__(self, x, y, **styles) Point_generique.__init__(self, **styles) def _set_feuille(self): xmin, xmax, ymin, ymax = self.__feuille__.fenetre if "_Point__x" in self._valeurs_par_defaut: self.__x = uniform(xmin, xmax) # self._valeurs_par_defaut.discard("_Point__x") if "_Point__y" in self._valeurs_par_defaut: self.__y = uniform(ymin, ymax) # self._valeurs_par_defaut.discard("_Point__y") Objet._set_feuille(self) def _update(self, objet): if not isinstance(objet, Point): objet = self._convertir(objet) if isinstance(objet, Point): self.coordonnees = objet.coordonnees else: raise TypeError, "L'objet n'est pas un point." class Point_pondere(Objet): u"""Un point pondr. Usage interne : les points pondrs sont utiliss dans la dfinition des barycentres.""" point = __point = Argument("Point_generique", defaut = Point) coefficient = __coefficient = Argument("Variable_generique", defaut = 1) @staticmethod def _convertir(objet): if isinstance(objet, Point_generique): return Point_pondere(objet) elif hasattr(objet, "__iter__"): return Point_pondere(*objet) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" def __init__(self, point = None, coefficient = None, **styles): ## if coefficient is None: ## coefficient = 1 self.__point = point = Ref(point) self.__coefficient = coefficient = Ref(coefficient) Objet.__init__(self, **styles) def __iter__(self): return iter((self.__point, self.__coefficient)) class Barycentre(Point_generique): u"""Un barycentre. Un barycentre de n points.""" points_ponderes = __points_ponderes = Arguments("Point_pondere") _prefixe_nom = "G" def __init__(self, *points_ponderes, **styles): if styles.get("points_ponderes", None): points_ponderes = styles.pop("points_ponderes") self.__points_ponderes = __points_ponderes = tuple(Ref(obj) for obj in points_ponderes) Point_generique.__init__(self, **styles) def _get_coordonnees(self): u"Coordonnes du barycentre en fonction de celles des points." total = self._cache.get('somme_coeff', self.__somme_coeffs) return sum(coeff*point.x for point, coeff in self.__points_ponderes)/total, \ sum(coeff*point.y for point, coeff in self.__points_ponderes)/total def _conditions_existence(self): # on le stocke pour eviter de le calculer 2 fois. total = self._cache.get('somme_coeff', self.__somme_coeffs) return abs(total) > contexte['tolerance'] def __somme_coeffs(self): return sum(point_pondere._Point_pondere__coefficient.valeur for point_pondere in self.__points_ponderes) class Milieu(Barycentre): u"""Un milieu. Le milieu de 2 points""" _prefixe_nom = "I" point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __init__(self, point1 = None, point2 = None, **styles): from .lignes import Segment if isinstance(point1, Segment): point2 = point1._Segment__point2 point1 = point1._Segment__point1 self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Barycentre.__init__(self, Point_pondere(point1, 1), Point_pondere(point2, 1), **styles) class Point_final(Point_generique): u"""Un point dfini par une relation vectorielle. Point dfini par une relation vectorielle. -> -> -> Exemple : point N tel que AN = -3 AB + 2 BC N = Point_final(A, [A>B, B>C], [-3,2])""" depart = __depart = Argument("Point_generique", defaut = Point) vecteurs = __vecteurs = Arguments("Vecteur_generique") coeffs = coefficients = __coefficients = Arguments("Variable_generique") def __init__(self, depart = None, vecteurs = (), coefficients = None, **styles): if coefficients == None: coefficients = tuple(1 for vecteur in vecteurs) self.__depart = depart = Ref(depart) self.__vecteurs = vecteurs = tuple(Ref(obj) for obj in vecteurs) self.__coefficients = coefficients = tuple(Ref(obj) for obj in coefficients) Point_generique.__init__(self, **styles) def _get_coordonnees(self): return self.__depart.x + sum(coeff*vecteur.x for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)), \ self.__depart.y + sum(coeff*vecteur.y for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)) ## def _get_coordonnees(self, exact = True): ## x = self.__depart.x if exact else self.__depart.x.valeur_approchee ## y = self.__depart.y if exact else self.__depart.y.valeur_approchee ## for i in xrange(len(self.__coefficients)): ## coeff = self.__coefficients[i] if exact else self.__coefficients[i].valeur_approchee ## x += vecteur.x if exact else vecteur.x.valeur_approchee ## ## if exact: ## return self.__depart.x + sum(coeff*vecteur.x for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)), \ ## self.__depart.y + sum(coeff*vecteur.y for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)) ## return self.__depart.x.valeur_approchee + sum(coeff.valeur_approchee*vecteur.x.valeur_approchee \ ## for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)), \ ## self.__depart.y.valeur_approchee + sum(coeff.valeur_approchee*vecteur.y.valeur_approchee \ ## for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)) class Point_translation(Point_generique): u"""Une image d'un point par translation.""" point = __point = Argument("Point_generique") translation = __translation = Argument("Translation") def __init__(self, point, translation, **styles): self.__point = point = Ref(point) self.__translation = translation = Ref(translation) Point_generique.__init__(self, **styles) def _get_coordonnees(self): return self.__point.x + self.__translation._Translation__vecteur.x, self.__point.y + self.__translation._Translation__vecteur.y class Point_rotation(Point_generique): u"""Une image d'un point par rotation. Point construit partir d'un autre via une rotation d'angle et de centre donn.""" point = __point = Argument("Point_generique") rotation = __rotation = Argument("Rotation") def __init__(self, point, rotation, **styles): self.__point = point = Ref(point) self.__rotation = rotation = Ref(rotation) Point_generique.__init__(self, **styles) def _get_coordonnees(self): x0, y0 = self.__rotation.centre.coordonnees xA, yA = self.__point.coordonnees a = self.__rotation.radian if contexte['exact'] and issympy(a, x0, y0, xA, yA): sina = ssin(a) ; cosa = scos(a) else: sina = sin(a) ; cosa = cos(a) return (-sina*(yA - y0) + x0 + cosa*(xA - x0), y0 + cosa*(yA - y0) + sina*(xA - x0)) # def _get_coordonnees2(self): # un poil plus lent # x0, y0 = self.__rotation.centre.coordonnees # xA, yA = self.__point.coordonnees # z = ((xA - x0) + (yA - y0)*1j)*cexp(1j*self.__rotation.radian) + x0 + y0*1j # return z.real, z.imag class Point_homothetie(Point_generique): u"""Une image d'un point par homothtie. Point construit partir d'un autre via une homothtie de rapport et de centre donn.""" point = __point = Argument("Point_generique") homothetie = __homothetie = Argument("Homothetie") def __init__(self, point, homothetie, **styles): self.__point = point = Ref(point) self.__homothetie = homothetie = Ref(homothetie) Point_generique.__init__(self, **styles) def _get_coordonnees(self): x0, y0 = self.__homothetie.centre.coordonnees xA, yA = self.__point.coordonnees k = self.__homothetie._Homothetie__rapport return x0 + k*(xA - x0), y0 + k*(yA - y0) class Point_reflexion(Point_generique): u"""Une image d'un point par rflexion. Point construit partir d'un autre via une symtrie d'axe donn.""" point = __point = Argument("Point_generique") reflexion = __reflexion = Argument("Reflexion") def __init__(self, point, reflexion, **styles): self.__point = point = Ref(point) self.__reflexion = reflexion = Ref(reflexion) Point_generique.__init__(self, **styles) def _get_coordonnees(self): x0, y0 = self.__reflexion._Reflexion__droite._Ligne_generique__point1.coordonnees x1, y1 = self.__reflexion._Reflexion__droite._Ligne_generique__point2.coordonnees x, y = self.__point z = x1 - x0 + (y1 - y0)*1j M = (x - x0 + (y0 - y)*1j)*z/z.conjugate() + x0 + y0*1j if contexte['exact'] and issympy(M): return M.expand(complex=True).as_real_imag() return M.real, M.imag class Projete_generique(Point_generique): u"""Un projet gnrique. Classe mre des diffrents types de projets orthogonaux (sur droite, sur cercle ou sur segment).""" def __init__(self, point, objet, **styles): #~ self._initialiser(point = Point_generique, objet = Objet) Point_generique.__init__(self, **styles) @property def distance(self): return distance(self, self.__point) class Projete_droite(Projete_generique): u"""Un projet orthogonal sur une droite. Projet orthogonal d'un point sur une droite.""" point = __point = Argument("Point_generique") droite = __droite = Argument("Droite_generique") def __init__(self, point, droite, **styles): self.__point = point = Ref(point) self.__droite = droite = Ref(droite) Projete_generique.__init__(self, point = point, objet = droite, **styles) def _get_coordonnees(self): xA, yA = self.__droite._Droite_generique__point1.coordonnees xB, yB = self.__droite._Droite_generique__point2.coordonnees x, y = self.__point.coordonnees xu = xB - xA # u = vecteur A>B yu = yB - yA xu2 = xu*xu yu2 = yu*yu AB = xu2 + yu2 if AB > contexte['tolerance']: return (yu2*xA + xu2*x + xu*yu*(y - yA))/AB, (xu2*yA + yu2*y + xu*yu*(x - xA))/AB else: return xA, yA class Projete_cercle(Projete_generique): u"""Un projet orthogonal sur un cercle. Projet orthogonal d'un point sur un cercle.""" point = __point = Argument("Point_generique") cercle = __cercle = Argument("Cercle_generique") def __init__(self, point, cercle, **styles): self.__point = point = Ref(point) self.__cercle = cercle = Ref(cercle) Projete_generique.__init__(self, point = point, objet = cercle, **styles) def _conditions_existence(self): # cas de non existence : si le point est au centre du cercle return carre_distance(self.__cercle.centre, self.__point) > contexte['tolerance']**2 def _get_coordonnees(self): k = angle_vectoriel((1, 0), vect(self.__cercle.centre, self.__point)) x0, y0 = self.__cercle.centre.coordonnees r = self.__cercle.rayon if contexte['exact'] and issympy(r, x0, y0): return x0 + r*scos(k), y0 + r*ssin(k) else: return x0 + r*cos(k), y0 + r*sin(k) class Projete_arc_cercle(Projete_generique): u"""Un projet orthogonal sur un arc de cercle. Projet orthogonal d'un point sur un arc de cercle.""" point = __point = Argument("Point_generique") arc = __arc = Argument("Arc_generique") def __init__(self, point, arc, **styles): self.__point = point = Ref(point) self.__arc = arc = Ref(arc) Projete_generique.__init__(self, point = point, objet = arc, **styles) def _conditions_existence(self): # cas de non existence : si le point est au centre du cercle return carre_distance(self.__arc.centre.coordonnees, self.__point) > contexte['tolerance']**2 def _get_coordonnees(self): M = self.__point arc = self.__arc O = arc.centre; a, b = arc._intervalle() c = angle_vectoriel((1, 0), vect(O, M)) while c < a: c += 2*pi # La mesure d'angle c est donc dans l'intervalle [a; a+2*pi[ if c > b: # c n'appartient pas [a;b] (donc M est en dehors de l'arc de cercle) if c - b > 2*pi + a - c: # c est plus proche de a+2*pi c = a else: # c est plus proche de b c = b if b - a < contexte['tolerance']: return arc.point1.coordonnees k = (b - c)/(b - a) t = a*k + b*(1 - k) x0, y0 = arc.centre.coordonnees r = arc.rayon if contexte['exact'] and issympy(r, x0, y0): return x0 +r*scos(t), y0 + r*ssin(t) else: return x0 +r*cos(t), y0 + r*sin(t) class Projete_segment(Projete_generique): u"""Un projet orthogonal sur un segment. Projet orthogonal d'un point sur un segment.""" point = __point = Argument("Point_generique") segment = __segment = Argument("Segment") def __init__(self, point, segment, **styles): self.__point = point = Ref(point) self.__segment = segment = Ref(segment) Projete_generique.__init__(self, point = point, objet = segment, **styles) def _get_coordonnees(self): xA, yA = A = self.__segment._Segment__point1 xB, yB = B = self.__segment._Segment__point2 x, y = self.__point.coordonnees AB2 = carre_distance(A, B) if AB2 > contexte['tolerance']: k = produit_scalaire(vect((x, y), A), vect(B, A))/AB2 k = min(max(k, 0), 1) # on se restreint au segment return (1-k)*xA + k*xB, (1 - k)*yA + k*yB else: # A et B sont confondus return xA, yA class Projete_demidroite(Projete_generique): u"""Un projet orthogonal sur une demi-droite. Projet orthogonal d'un point sur une demi-droite.""" point = __point = Argument("Point_generique") demidroite = __demidroite = Argument("Demidroite") def __init__(self, point, demidroite, **styles): self.__point = point = Ref(point) self.__demidroite = demidroite = Ref(demidroite) Projete_generique.__init__(self, point = point, objet = demidroite, **styles) def _get_coordonnees(self): xA, yA = A = self.__demidroite._Ligne_generique__point1 xB, yB = B = self.__demidroite._Ligne_generique__point2 x, y = self.__point.coordonnees AB2 = carre_distance(A, B) if AB2 > contexte['tolerance']: k = produit_scalaire(vect((x,y), A), vect(B, A))/AB2 k = max(k, 0) # on se restreint la demi-droite return (1-k)*xA+k*xB, (1-k)*yA+k*yB else: # A et B sont confondus return xA, yA class Centre_polygone_generique(Point_generique): u"""Un centre d'un triangle. Classe mre des diffrents centres d'un triangle.""" def __init__(self, polygone, **styles): #~ self._initialiser(polygone = Polygone) Point_generique.__init__(self, **styles) class Centre_gravite(Centre_polygone_generique): u"""Un centre de gravit. Centre de gravit d'un polygone (l'intersection des mdianes dans le cas d'un triangle).""" _prefixe_nom = "G" polygone = __polygone = Argument("Polygone_generique") def __init__(self, polygone, **styles): self.__polygone = polygone = Ref(polygone) Centre_polygone_generique.__init__(self, polygone, **styles) def _conditions_existence(self): return self.__polygone.centre_gravite.existe def _get_coordonnees(self): return self.__polygone.centre_gravite.coordonnees class Orthocentre(Centre_polygone_generique): u"""Un orthocentre. Orthocentre d'un triangle (intersection des hauteurs).""" _prefixe_nom = "H" triangle = __triangle = Argument("Triangle") def __init__(self, triangle, **styles): self.__triangle = triangle = Ref(triangle) Centre_polygone_generique.__init__(self, triangle, **styles) def _conditions_existence(self): return self.__triangle.orthocentre.existe def _get_coordonnees(self): return self.__triangle.orthocentre.coordonnees class Centre_cercle_circonscrit(Centre_polygone_generique): u"""Un centre du cercle circonscrit. Centre du cercle circonscrit d'un triangle (intersection des mdiatrices).""" _prefixe_nom = "O" triangle = __triangle = Argument("Triangle") def __init__(self, triangle, **styles): self.__triangle = triangle = Ref(triangle) Centre_polygone_generique.__init__(self, triangle, **styles) def _conditions_existence(self): return self.__triangle.centre_cercle_circonscrit.existe def _get_coordonnees(self): return self.__triangle.centre_cercle_circonscrit.coordonnees class Centre_cercle_inscrit(Centre_polygone_generique): u"""Un centre du cercle inscrit. Centre du cercle inscrit d'un triangle (intersection des bissectrices).""" _prefixe_nom = "I" triangle = __triangle = Argument("Triangle") def __init__(self, triangle, **styles): self.__triangle = triangle = Ref(triangle) Centre_polygone_generique.__init__(self, triangle, **styles) def _conditions_existence(self): return self.__triangle.centre_cercle_inscrit.existe def _get_coordonnees(self): return self.__triangle.centre_cercle_inscrit.coordonnees class Centre(Point_generique): u"""Un centre de cercle.""" _prefixe_nom = "O" cercle = __cercle = Argument("Cercle_generique") def __init__(self, cercle, **styles): self.__cercle = cercle = Ref(cercle) Point_generique.__init__(self, **styles) # def _conditions_existence(self): # return [self.__cercle.existe] def _get_coordonnees(self): a, b, c = self.__cercle.equation return (-a/2, -b/2) class Point_equidistant(Point_generique): u"""Point quidistant de 3 points. Utilis surtout pour un usage interne, le calcul des coordonnes est plus rapide qu'en passant par les mdiatrices.""" point1 = __point1 = Argument("Point_generique") point2 = __point2 = Argument("Point_generique") point3 = __point3 = Argument("Point_generique") def __init__(self, point1, point2, point3, **styles): self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) self.__point3 = point3 = Ref(point3) Point_generique.__init__(self, **styles) def _conditions_existence(self): det2, xA, yA, xB, yB, xC, yC = self._cache.get('det2', self.__det2) return abs(det2) > contexte['tolerance'] \ or abs(xB - xC) + abs(yB - yC) < contexte['tolerance'] \ or abs(xA - xC) + abs(yA - yC) < contexte['tolerance'] \ or abs(xB - xA) + abs(yB - yA) < contexte['tolerance'] def _get_coordonnees(self): det2, xA, yA, xB, yB, xC, yC = self._cache.get('det2', self.__det2) if abs(det2) > contexte['tolerance']: a = xB - xA b = yB - yA c = xC - xB d = yC - yB p1 = a*d; p2 = b*c return (p1*(xA+xB) - p2*(xB+xC) - b*d*(yC-yA))/det2, \ (a*c*(xC-xA) - p2*(yA+yB) + p1*(yB+yC))/det2 elif abs(xB - xC) + abs(yB - yC) < contexte['tolerance']: return (.5*(xA+xB), .5*(yA+yB)) elif abs(xA - xC) + abs(yA - yC) < contexte['tolerance']: return (.5*(xA+xB), .5*(yA+yB)) elif abs(xB - xA) + abs(yB - yA) < contexte['tolerance']: return (.5*(xA+xC), .5*(yA+yC)) def __det2(self): xA, yA = self.__point1.coordonnees xB, yB = self.__point2.coordonnees xC, yC = self.__point3.coordonnees return 2*((xB - xA)*(yC - yB) - (yB - yA)*(xC - xB)), xA, yA, xB, yB, xC, yC ########################################################################################## # Glisseurs class Glisseur_generique(Point_generique): u"""Un glisseur gnrique. Classe mre des diffrents types de glisseurs""" _style_defaut = param.points_deplacables objet = __objet = Argument("Objet") k = __k = Argument("Variable_generique") def __init__(self, objet, k, **styles): self.__objet = objet = Ref(objet) self.__k = k = Ref(k) Point_generique.__init__(self, **styles) def _initialiser_k(self, k): if isinstance(k, (list, tuple)): k = self._conversion_coordonnees_parametre(*k) return k def _set_coordonnees(self, x = None, y = None): if x is not None: self.k = self._conversion_coordonnees_parametre(x, y) class Glisseur_vecteur(Glisseur_generique): u"""Un glisseur sur vecteur.""" vecteur = __vecteur = Argument("Vecteur") parametre = k = __k = Argument("Variable_generique", None, lambda obj, value: max(min(value, 1), 0)) def __init__(self, vecteur, k = None, **styles): if k is None: k = uniform(0, 1) self.__vecteur = vecteur = Ref(vecteur) self.__k = k = Ref(self._initialiser_k(k)) Glisseur_generique.__init__(self, vecteur, k, **styles) def _get_coordonnees(self): x1, y1 = self.__vecteur._Vecteur__point1.coordonnees x2, y2 = self.__vecteur._Vecteur__point2.coordonnees k = self.__k return (1 - k)*x1 + k*x2, (1 - k)*y1 + k*y2 def _conversion_coordonnees_parametre(self, x, y): A = self.__vecteur._Vecteur__point1; B = self.__vecteur._Vecteur__point2 return produit_scalaire(vect((x,y), A), vect(B, A))/carre_distance(A, B) class Glisseur_ligne_generique(Glisseur_generique): u"""Un glisseur gnrique sur ligne. Classe mre des diffrents types de glisseurs sur ligne (ie. droite, segment, demi-droite)""" ligne = __ligne = Argument("Ligne_generique") k = __k = Argument("Variable_generique") def __init__(self, ligne, k, **styles): self.__ligne = ligne = Ref(ligne) self.__k = k = Ref(k) Glisseur_generique.__init__(self, ligne, k, **styles) def _get_coordonnees(self): x1, y1 = self.__ligne._Ligne_generique__point1.coordonnees x2, y2 = self.__ligne._Ligne_generique__point2.coordonnees k = self.__k return (1 - k)*x1 + k*x2, (1 - k)*y1 + k*y2 class Glisseur_droite(Glisseur_ligne_generique): u"""Un point sur une droite. Point pouvant 'glisser' sur une droite. k est un coefficient barycentrique. Si A et B sont les deux points de rfrence de la droite, k le coefficient, les coordonnes du glisseur M seront determines par la formule M = kA + (1-k)B.""" droite = __droite = Argument("Droite_generique") parametre = k = __k = Argument("Variable_generique") def __init__(self, droite, k = None, **styles): if k is None: k = normalvariate(0.5, 0.5) self.__droite = droite = Ref(droite) self.__k = k = Ref(self._initialiser_k(k)) Glisseur_ligne_generique.__init__(self, ligne = droite, k = k, **styles) def _conversion_coordonnees_parametre(self, x, y): A = self.__droite._Ligne_generique__point1; B = self.__droite._Ligne_generique__point2 return produit_scalaire(vect((x,y), A), vect(B, A))/carre_distance(A, B) class Glisseur_segment(Glisseur_ligne_generique): u"""Un point sur un segment. Point pouvant 'glisser' sur un segment. k est un coefficient barycentrique. Si A et B sont les deux points de rfrence de la droite, k le coefficient, les coordonnes du glisseur M seront determines par la formule M = kA + (1-k)B.""" segment = __segment = Argument("Segment") parametre = k = __k = Argument("Variable_generique", None, lambda obj, value: max(min(value, 1), 0)) # la valeur du parametre doit rester comprise entre 0 et 1 (segment) def __init__(self, segment, k = None, **styles): if k is None: k = uniform(0, 1) self.__segment = segment = Ref(segment) self.__k = k = Ref(self._initialiser_k(k)) Glisseur_ligne_generique.__init__(self, ligne = segment, k = k, **styles) def _conversion_coordonnees_parametre(self, x, y): A = self.__segment._Ligne_generique__point1; B = self.__segment._Ligne_generique__point2 return produit_scalaire(vect((x,y), A), vect(B, A))/carre_distance(A, B) class Glisseur_demidroite(Glisseur_ligne_generique): u"""Un point sur une demi-droite. Point pouvant 'glisser' sur un segment. k est un coefficient barycentrique. Si A et B sont les deux points de rfrence de la droite, k le coefficient, les coordonnes du glisseur M seront determines par la formule M = kA + (1-k)B.""" demidroite = __demidroite = Argument("Demidroite") parametre = k = __k = Argument("Variable_generique", None, lambda obj, value: max(value, 0)) def __init__(self, demidroite, k = None, **styles): if k is None: k = abs(normalvariate(0.5, 0.5)) self.__demidroite = demidroite = Ref(demidroite) self.__k = k = Ref(self._initialiser_k(k)) Glisseur_ligne_generique.__init__(self, ligne = demidroite, k = k, **styles) def _conversion_coordonnees_parametre(self, x, y): A = self.__demidroite._Ligne_generique__point1; B = self.__demidroite._Ligne_generique__point2 return produit_scalaire(vect((x,y), A), vect(B, A))/carre_distance(A, B) class Glisseur_cercle(Glisseur_generique): u"""Un point sur un cercle. Point pouvant 'glisser' sur un cercle. k est un angle. Si O et r sont les centres et rayons du cercle, k l'angle, les coordonnes du glisseur M seront dtermines par la formule M = O + r*(cos(k), sin(k)).""" cercle = __cercle = Argument("Cercle_generique") parametre = k = __k = Argument("Variable_generique") def __init__(self, cercle, k = None, **styles): if k is None: k = uniform(0, 2*pi) self.__cercle = cercle = Ref(cercle) self.__k = k = Ref(self._initialiser_k(k)) Glisseur_generique.__init__(self, objet = cercle, k = k, **styles) def _get_coordonnees(self): cercle = self.__cercle k = self.__k x0, y0 = cercle.centre.coordonnees r = cercle.rayon if contexte['exact'] and issympy(r, x0, y0): return x0 + r*scos(k), y0 + r*ssin(k) else: return x0 + r*cos(k), y0 + r*sin(k) def _conversion_coordonnees_parametre(self, x, y): O = self.__cercle.centre M = (x, y) if O != M: return angle_vectoriel((1, 0), vect(O, M)) else: return 0 class Glisseur_arc_cercle(Glisseur_generique): u"""Un point sur un arc de cercle. Point pouvant 'glisser' sur un cercle. k est un angle. Si O et r sont les centres et rayons de l'arc, k un coefficient compris entre 0 et 1, et a et b les mesures d'angle marquant le dbut et la fin de l'arc, les coordonnes du glisseur M seront dtermines par la formule M = O + r*(cos(ka+(1-k)b), sin(ka+(1-k)b)), si l'arc est de sens direct, et M = O + r*(cos(kb+(1-k)a), sin(kb+(1-k)a)) sinon.""" arc = __arc = Argument("Arc_generique") parametre = k = __k = Argument("Variable_generique") def __init__(self, arc, k = None, **styles): if k is None: k = uniform(0, 1) self.__arc = arc = Ref(arc) self.__k = k = Ref(self._initialiser_k(k)) Glisseur_generique.__init__(self, objet = arc, k = k, **styles) def _get_coordonnees(self): arc = self.__arc a, b = arc._intervalle() k = self.__k if arc._sens() == 1: t = a*k + b*(1 - k) else: t = b*k + a*(1 - k) x0, y0 = arc.centre.coordonnees r = arc.rayon if contexte['exact'] and issympy(r, x0, y0): return x0 + r*scos(t), y0 + r*ssin(t) else: return x0 + r*cos(t), y0 + r*sin(t) def _conversion_coordonnees_parametre(self, x, y): M = (x, y) O = self.__arc.centre; if O != M: a, b = self.__arc._intervalle() c = angle_vectoriel((1, 0), vect(O, M)) while c < a: c += 2*pi # La mesure d'angle c est donc dans l'intervalle [a; a+2*pi[ if c > b: # c n'appartient pas [a;b] (donc M est en dehors de l'arc de cercle) if c - b > 2*pi + a - c: # c est plus proche de a+2*pi c = a else: # c est plus proche de b c = b if b - a: if self.__arc._sens() == 1: return (b - c)/(b - a) else: return (c - a)/(b - a) else: return 0.5 else: return 0 class Nuage_generique(Objet): u"""Un nuage de points generique. Usage interne : la classe mre de tous les nuages de points.""" _prefixe_nom = "n" def _distance_inf(self, x, y, d): return any(pt._distance_inf(x, y, d) for pt in self.points) @property def points(self): raise NotImplementedError def style(self, nom_style = None, **kwargs): if kwargs: for point in self.__points: point.style(**kwargs) else: Objet.style(self, nom_style, **kwargs) class Nuage(Nuage_generique): u"""Un nuage de points. Le nuage est dfini par la donne de ses points. """ __points = points = Arguments('Point_generique') #TODO: il n'est pas possible actuellement de modifier la taille du nuage de points # aprs cration. C'est une limitation de la classe Arguments(). def __init__(self, *points, **styles): if styles.get('points', None): points = styles.pop("points") self.__points = points = tuple(Ref(obj) for obj in points) Nuage_generique.__init__(self, **styles) class NuageFonction(Nuage_generique): u"""Un nuage de points de coordonnes (x; f(x)). Le nuage est dfini par la donne de la fonction f, et d'une liste d'abscisses. """ __fonction = fonction = Argument('Fonction') __abscisses = abscisses = Arguments('Variable_generique') def __init__(self, fonction, *abscisses, **styles): if styles.get('abscisses', None): points = styles.pop('abscisses') self.__points = points = tuple(Ref(obj) for obj in points) Nuage_generique.__init__(self, **styles) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/fonctions.py0000644000175000017500000002667012014170666023546 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Fonctions # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode import re from .objet import Objet_numerique, Objet, contexte, ArgumentNonModifiable, \ Argument, Ref #from .. import param from ..pylib import is_in, property2 from ..mathlib.intervalles import preformatage_geolib_ensemble, formatage_ensemble from ..mathlib.parsers import VAR_NOT_ATTR, traduire_formule from .variables import Variable class Fonction(Objet_numerique): u"""Une fontion. Une fonction numrique une variable; l'argument est une expression sous forme de chaine de caractres. Exemple: Fonction('2*x+1', variable = 'x', ensemble = 'R').""" _prefixe_nom = "f" # nom de variable (mais pas d'attribut) __re = re.compile('(' + VAR_NOT_ATTR + ')') __expression = Argument("basestring") __ensemble = Argument("basestring") variable = __variable = ArgumentNonModifiable("basestring") @property2 def expression(self, expression = None): if expression is None: if self.__liste_expression: # On regnre l'expression partir de l'expression compile. # C'est important, car certains objets de la feuille peuvent avoir chang de nom entre temps. expr = "" for elt in self.__liste_expression: if isinstance(elt, Objet): expr += elt.nom else: expr += elt return expr else: return self.__expression else: self.modifier_expression_et_ensemble(expression = expression) @property2 def ensemble(self, ensemble = None): if ensemble is None: if self.__liste_ensemble: # On regnre l'ensemble partir de l'ensemble compile. # C'est important, car certains objets de la feuille peuvent avoir chang de nom entre temps. ens = "" for elt in self.__liste_ensemble: if isinstance(elt, Objet): ens += elt.nom else: ens += elt return ens else: return self.__ensemble else: self.modifier_expression_et_ensemble(ensemble = ensemble) def modifier_expression_et_ensemble(self, expression = None, ensemble = None): u"""Si l'on modifie la fois l'expression et l'ensemble, il est beaucoup plus rapide d'utiliser cette mthode.""" # print "modification!" if expression is None: expression = self.__expression if ensemble is None: ensemble = self.__ensemble else: ensemble, extremites_cachees = preformatage_geolib_ensemble(ensemble) extremites_cachees = tuple([Variable(x) for x in partie] for partie in extremites_cachees) self.style(extremites_cachees = extremites_cachees) if ensemble == self.__ensemble and expression == self.__expression: # Aucune modification return args = self._test_dependance_circulaire(expression, ensemble) self.__expression = expression self.__ensemble = ensemble self._compile(*args) def __init__(self, expression, ensemble = "R", variable = "x", **styles): Objet.__init__(self, **styles) if variable not in 'xt': raise NotImplementedError # *Pr*formatage ensemble, extremites_cachees = preformatage_geolib_ensemble(ensemble) if self.style("extremites_cachees"): extremites_cachees = self.style("extremites_cachees") else: extremites_cachees = tuple([Variable(x) for x in partie] for partie in extremites_cachees) self.__liste_expression = [] self.__liste_ensemble = [] # Une fonction peut tre dfinie par morceaux # Liste des fonctions correspondant chaque morceau self.__fonctions = None # Liste des (unions d'intervalles correspondant chaque morceau self.__unions = None # Les arguments non modifiables ne sont pas encapsuls dans des rfrences (classe Ref) self.__expression = expression = Ref(expression) self.__ensemble = ensemble = Ref(ensemble) self.__variable = variable self.style(extremites_cachees = extremites_cachees) def _test_dependance_circulaire(self, expression, ensemble, deuxieme_essai = False): u"""Provoque une erreur si l'objet se retrouve dpendre de lui-mme avec la nouvelle valeur. Retourne deux listes (list) composes alternativement d'instructions et d'objets de la feuille, et un ensemble (set) constitu des objets de la feuille mis en jeu dans le code. (... documenter ...)""" if self.__feuille__ is not None: try: with contexte(afficher_messages = False): liste_expression = re.split(self.__re, expression) liste_ensemble = re.split(self.__re, ensemble) objets = set() for i in xrange(1, len(liste_expression), 2): obj = self.__feuille__.objets[liste_expression[i]] if isinstance(obj, Objet): liste_expression[i] = obj objets.add(obj) if self is obj or is_in(self, obj._tous_les_ancetres()): print self, raise RuntimeError, "Definition circulaire dans %s : l'objet %s se retrouve dependre de lui-meme." %(self, obj) for i in xrange(1, len(liste_ensemble), 2): obj = self.__feuille__.objets[liste_ensemble[i]] if isinstance(obj, Objet): liste_ensemble[i] = obj objets.add(obj) if self is obj or is_in(self, obj._tous_les_ancetres()): print self, raise RuntimeError, "Definition circulaire dans %s : l'objet %s se retrouve dependre de lui-meme." %(self, obj) return liste_expression, liste_ensemble, objets except KeyError: if deuxieme_essai: raise # L'erreur peut-tre due la prsence de code LaTeX dans la fonction ; # on tente un 2e essai aprs traduction du code LaTeX ventuel. expression = traduire_formule(expression, fonctions = self.__feuille__.objets) return self._test_dependance_circulaire(expression, ensemble, deuxieme_essai = True) return None, None, None def _compile(self, liste_expression, liste_ensemble, objets): u"""Compile l'expression stocke dans la variable ; les arguments sont les valeurs retournes par '_test_dependance_circulaire'. La compilation doit toujours avoir lieu la fin de la procdure de redfinition de la variable, car elle ne doit tre excute que si la redfinition de la variable va effectivement avoir lieu, c'est--dire si tout le processus prcdent s'est excut sans erreur.""" #print "compilation !" if self.__feuille__ is not None: self.__liste_expression = liste_expression self.__liste_ensemble = liste_ensemble self.__fonctions = [] self.__unions = [] expressions = self.__expression.split("|") ensembles = self.__ensemble.split("|") # TODO: Prvoir le cas o les deux listes ne sont pas de mme longueur n = min(len(expressions), len(ensembles)) for i in xrange(n): express = traduire_formule(expressions[i], fonctions = self.__feuille__.objets) # On force ensuite la variable apparaitre dans l'expression de la formule. # C'est important quand la fonction est constante : # l'image d'un tableau par la fonction doit tre un tableau, et non la constante. if self.__variable not in express: express += "+0.*" + self.__variable self.__fonctions.append(eval("lambda " + self.__variable + ":" + express, self.__feuille__.objets)) ensemb = formatage_ensemble(ensembles[i], preformatage = False) self.__unions.append(eval(ensemb, self.__feuille__.objets)) # on supprime la variable de la liste des vassaux pour les objets dont elle ne dpendra plus desormais: for objet in self._ancetres: objet.vassaux.remove(self) self._ancetres = objets self._modifier_hierarchie() for objet in self._ancetres: # l'objet est vassal de chacun des objets dont il depend objet.vassaux.append(self) else: for objet in self._ancetres: objet.vassaux.remove(self) self._ancetres = set() self._modifier_hierarchie() self.__liste_expression = [] self.__liste_ensemble = [] self.__fonctions = None self.__unions = None def _set_feuille(self): self._compile(*self._test_dependance_circulaire(self.__expression, self.__ensemble)) self._heritiers_a_recalculer(self._heritiers()) def _recenser_les_ancetres(self): # warning("'_recenser_les_ancetres' n'a aucun effet pour une variable.") self._modifier_hierarchie() ## def affiche(self, actualiser = False): ## if actualiser and self.__feuille__ is not None: ## canvas = self.__canvas__ ## if canvas: ## canvas.actualiser() @staticmethod def _convertir(objet): u"Convertit un objet en fonction." return Fonction(objet) def __call__(self, valeur): for i in xrange(len(self.__unions)): if valeur in self.__unions[i]: return self.__fonctions[i](valeur) raise ValueError, "math domain error" def _update(self, objet): if not isinstance(objet, Fonction): objet = self._convertir(objet) if isinstance(objet, Fonction): if objet.__feuille__ is not None: # ?? objet.__feuille__ = self.__feuille__ self.modifier_expression_et_ensemble(objet.expression, objet.ensemble) else: raise TypeError, "l'objet n'est pas une fonction." wxgeometrie-0.133.2.orig/wxgeometrie/geolib/objet.py0000644000175000017500000022323312014170666022641 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ##--------------------------------------####### # Objet # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re, math from types import FunctionType, BuiltinFunctionType, TypeType import numpy from matplotlib.pyparsing import ParseFatalException from sympy import I, pi as PI, Basic, Integer from ..mathlib.internal_objects import Reel # intgrer dans geolib ?? from ..pylib import property2, no_twin, uu, str2, print_error, mathtext_parser, \ is_in, WeakList, CustomWeakKeyDictionary, warning from .routines import nice_display from .constantes import FORMULE, TEXTE, NOM from .formules import Formule from .contexte import contexte from .. import param try: import wx souffler = wx.YieldIfNeeded except ImportError: wx = None def souffler(): pass class _(object): pass # G contient tous les objets de geolib la fin de l'initialisation. G = _() ########################################################################### # NOTE : les vassaux ne sont pas parfaitement bien geres : # Un objet ne doit pas etre a la fois vassal et suzerain d'un autre. # Une telle situation provoquerait un plantage lors de la suppression d'un des deux objets (boucle recursive infinie). # Ceci ne pose a priori pas de problemes, sauf un peu de gaspillage de memoire : # en effet, les objets geometriques intermediaires introduits pour construire un objet ne sont pas systematiquement detruits quand l'objet est supprime. TYPES_ENTIERS = (long, int, Integer,) TYPES_REELS = TYPES_ENTIERS + (float, Basic, ) # XXX: Basic n'est pas forcment rel TYPES_NUMERIQUES = TYPES_REELS + (complex, ) ALPHABET_GREC_MIN = frozenset(( 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega' )) ALPHABET_GREC_CAP = frozenset(('Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta', 'Iota', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Xi', 'Omicron', 'Pi', 'Rho', 'Sigma', 'Tau', 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega')) ALPHABET_GREC = ALPHABET_GREC_MIN.union(ALPHABET_GREC_CAP) FILL_STYLES = {'-': 'solid', '--' : 'dashed', '-.' : 'dashdot', ':' : 'dotted'} RE_NOM_OBJET = re.compile("[A-Z][^A-Z]*") RE_NOM_DE_POINT="[A-Z]((_[{][0-9]+[}])|(_[0-9]+)|([']+))?" ##PI = math.pi ##if sympy is not None: ## PI = sympy.pi def issympy(*expressions): return all(isinstance(e, Basic) for e in expressions) ## OUTILS DIVERS ############ class Nom(object): u"""Nom d'un objet. Affiche le nom de l'objet quand on le met sous forme dAe chane. Exemple : >>> from wxgeometrie.geolib import Nom, Feuille, Point >>> M = Feuille().objets.M = Point(1, 2) >>> nom = Nom(M) >>> nom 'M' >>> M.nom = 'A' >>> nom 'A' """ __slots__ = "__objet" def __init__(self, objet): self.__objet = objet def __repr__(self): return repr(self.__objet.nom) class Rendu(object): u"""Couche d'abstraction entre l'objet et le canvas. Son rle est de rendre l'objet plus indpendant du canvas, mais aussi de faciliter le dbogage, en indiquant quel objet a dessin quoi. """ def __init__(self, parent): self.parent = parent @property def feuille(self): return self.parent.__feuille__ @property def canvas(self): return self.feuille and self.feuille.canvas def ligne(self, *args, **kw): artiste = self.canvas.ligne(*args, **kw) # Pour le dbogage, il est pratique de savoir qui a cre quoi. artiste._cree_par = self.parent return artiste def polygone(self, *args, **kw): artiste = self.canvas.polygone(*args, **kw) artiste._cree_par = self.parent return artiste def texte(self, *args, **kw): artiste = self.canvas.texte(*args, **kw) artiste._cree_par = self.parent return artiste def arc(self, *args, **kw): artiste = self.canvas.arc(*args, **kw) artiste._cree_par = self.parent return artiste def point(self, *args, **kw): artiste = self.canvas.point(*args, **kw) artiste._cree_par = self.parent return artiste def cercle(self, *args, **kw): artiste = self.canvas.cercle(*args, **kw) artiste._cree_par = self.parent return artiste def fleche(self, *args, **kw): artiste = self.canvas.fleche(*args, **kw) artiste._cree_par = self.parent return artiste def fleche_courbe(self, *args, **kw): artiste = self.canvas.fleche_courbe(*args, **kw) artiste._cree_par = self.parent return artiste def codage(self, *args, **kw): artiste = self.canvas.codage(*args, **kw) artiste._cree_par = self.parent return artiste def angle(self, *args, **kw): artiste = self.canvas.angle(*args, **kw) artiste._cree_par = self.parent return artiste def codage_angle(self, *args, **kw): artiste = self.canvas.codage_angle(*args, **kw) artiste._cree_par = self.parent return artiste def rectangle(self, *args, **kw): artiste = self.canvas.rectangle(*args, **kw) artiste._cree_par = self.parent return artiste def _pixel(self, point = None): if point is None: point = self.parent return self.canvas.coo2pix(*point.coordonnees_approchees) class Cache(object): u"""Un 'dictionnaire' double dont la mthode get est sensiblement modifie. Pour chaque entre, il y a une valeur en mode exact, et une valeur en mode approch. Si le mot-cl n'existe pas, le dictionnaire est mis jour l'aide de la fonction et de ses arguments. noter que la fonction n'est *PAS* execute si le dictionnaire contient la cl, ce qui n'est pas le cas de dict.get(clef, fonction(*args, **kw)) bien sr. D'o l'intrt du cache. """ __slots__ = '__approche', '__exact' def __init__(self): # Cache pour le mode approch... self.__approche = {} # ...et cache pour le mode exact. self.__exact = {} @property def __dict(self): return self.__exact if contexte['exact'] else self.__approche def get(self, clef, methode, *args, **kw): dict = self.__dict if clef not in dict: dict[clef] = methode(*args, **kw) return dict[clef] def __setitem__(self, key, value): self.__dict[key] = value def __getitem__(self, key): return self.__dict[key] def remove(self, key): u"Note: ne renvoie *PAS* d'erreur si la cl est absente." # Si une des deux valeurs (exacte ou approche) n'est plus valable, # l'autre ne l'est *trs* probablement plus non plus. self.__exact.pop(key, None) self.__approche.pop(key, None) def clear(self): self.__approche.clear() self.__exact.clear() def pi_(): return PI if contexte['exact'] else math.pi def I_(): return I if contexte['exact'] else 1j class Ref(object): u"""Conteneur pour un argument d'un objet. Si l'objet est dj une instance de Reference, alors l'objet est renvoy directement (afin de ne pas crer de conteneur de conteneur !)""" __slots__ = '_utilisateurs', '__objet' def __new__(cls, objet): # si objet est dj une instance de Ref, alors objet est renvoy directement # (afin de ne pas crer de conteneur de conteneur !) if isinstance(objet, cls): return objet instance = object.__new__(cls) instance.__objet = objet instance._utilisateurs = WeakList() return instance def _changer_objet(self, nouvel_objet, premiere_definition = False): ancien_objet = self.__objet self.__objet = nouvel_objet # Tous les objets qui utilisent cette rfrence doivent maintenant tre mis jour. # Le verrou suivant indique si le code suivant est excut dans # un cadre plus gnral de rafraichissement de l'affichage. # Exemple : # Si A est un Point, alors le code A.coordonnees = 2, 3 # invoque successivement les actions A.x = 2, et A.y = 3 # Aucun rafraichissement intermdiaire de l'affichage ne doit avoir lieu aprs A.x = 2, # sous peine d'une nette dgradation des performances. if self._utilisateurs and not premiere_definition: # Attention, l'objet n'est pas forcment de type Objet (il peut-tre de type int, str...) feuille = self._utilisateurs[0].__feuille__ if feuille is not None and feuille._verrou_affichage is None: feuille._a_rafraichir = [] for user in self._utilisateurs: # 1. Il ne dpendent plus de l'ancien objet, mais du nouveau if isinstance(ancien_objet, Objet): ancien_objet.vassaux.remove(user) if isinstance(nouvel_objet, Objet): nouvel_objet.vassaux.append(user) # 2. Il faut mettre jour la liste de leurs anctres # (auquel cas, il ne s'agit pas vraiment d'une redfinition des arguments) user._recenser_les_ancetres() # 3. Tous les hritiers doivent galement subir une mise jour heritiers = user._heritiers() user._heritiers_a_recalculer(heritiers) if feuille is not None: feuille._a_rafraichir.extend(heritiers) feuille._a_rafraichir.append(user) if feuille is not None and feuille._verrou_affichage is None: a_rafraichir = no_twin(feuille._a_rafraichir) for obj in a_rafraichir: obj.figure_perimee() ## feuille.affichage_perime() @property def objet(self): return self.__objet class BaseArgument(object): u"""Classe mre des descripteurs 'Argument', 'ArgumentNonModifiable' et 'Arguments'.""" __compteur__ = 0 def __init__(self, types, get_method=None, set_method=None, defaut=None): self.__contenu__ = CustomWeakKeyDictionary() BaseArgument.__compteur__ += 1 self.__compteur__ = BaseArgument.__compteur__ self.types = types self.get_method = get_method self.set_method = set_method self.defaut = defaut # La classe de rattachement (ie. la classe laquelle l'argument se rapporte) sera complte dynamiquement. # cf. Objet.__init__() self.rattachement = None # Le nom est de la forme '_NomDeClasse__NomDArgument', et est complt dynamiquement # cf. geolib/__init__.py self.nom = None def _definir_type(self): # Les arguments sont crs en mme temps que les classes ; # il se peut donc qu'un type d'argument soit une classe qui n'existe pas encore. # On rentre donc le type d'argument sous la forme d'une chane : "Ma_classe", # et la premire utilisation de l'argument par une instance, # on transforme la chane "Ma_classe" en la classe Ma_classe elle-mme. if isinstance(self.types, str): def convert(chaine): if hasattr(G, chaine): return getattr(G, chaine) else: return __builtins__[chaine] self.types = tuple(convert(elt.strip()) for elt in self.types.split(",")) elif not hasattr(self.types, '__iter__'): self.types = (self.types,) def _verifier_type(self, objet): if isinstance(objet, G.Variable): objet = objet.copy() if not isinstance(objet, self.types): for _type in self.types: if hasattr(_type, "_convertir"): try: objet = _type._convertir(objet) break except Exception: if param.verbose: print_error("Conversion impossible : de %s en %s" %(type(objet), _type)) # Attention : penser vrifier ensuite le type mme si la conversion semble avoir russie ! # En effet, la mthode '_convertir()' peut tre celle d'une sous-classe du type voulu, et donc ne pas convertir exactement l'objet dans le bon type. # Par exemple, pour un Vecteur_libre, c'est la mthode Vecteur_generique._convertir() qui est utilise ; on obtient donc un objet de type 'Vecteur_generique', mais pas forcment de type 'Vecteur_libre'. if not isinstance(objet, self.types): raise TypeError, "%s should be of type %s, and not %s." %(objet, self.types, type(objet)) return objet def _definir(self, obj, value): self._definir_type() if not hasattr(obj, "_valeurs_par_defaut"): # indique les arguments qui ont t initialiss avec une valeur par dfaut obj._valeurs_par_defaut = [] if not isinstance(value, Ref): raise TypeError, "l'argument doit etre encapsule dans un objet 'Ref' lors d'une premiere definition." if value.objet is None and self.defaut is not None: # Du fait des dpendances circulaires, self.defaut est parfois rentr # sous forme de chaine. la premire utilisation, il est converti. if isinstance(self.defaut, basestring): self.defaut = eval(self.defaut, G) if isinstance(self.defaut, (FunctionType, BuiltinFunctionType, TypeType)): value._Ref__objet = self.defaut() else: value._Ref__objet = self.defaut obj._valeurs_par_defaut.append(self.nom) # Attention : on utilise l'attribut de bas niveau ._Ref__objet, # pour viter que la rfrence croit une redfinition. value._changer_objet(self._verifier_type(value.objet), premiere_definition = True) # if isinstance(value.__objet__, Objet) and value.__objet__.__feuille__ is None: # value.__objet__.__feuille__ = obj.__feuille__ if not is_in(obj, value._utilisateurs): value._utilisateurs.append(obj) return value def _redefinir(self, obj, value): if isinstance(value, Ref): raise TypeError, "l'argument ne doit pas etre encapsule dans un objet 'Ref' lors d'une redefinition." if self.rattachement is not type(obj): raise AttributeError, "on ne peut pas redefinir un argument d'une sous-classe" value = self._verifier_type(value) # La premire tape, c'est d'liminer les dpendances circulaires. # Par exemple, u = Variable(3); A = Point("u", "2*u"); u("A.x") # A deviendrait alors la fois hritier de u (ie. on a besoin de u pour calculer A), et anctre de u (ie. on a besoin de A pour calculer u !) if value is obj or is_in(value, obj._heritiers()): # self.erreur(u"Dfinition circulaire dans %s : l'objet %s se retrouve dpendre de lui-mme." %(obj, obj)) raise RuntimeError, "Definition circulaire dans %s : l'objet %s se retrouve dependre de lui-meme." %(obj, obj) # La valeur a t fixe par l'utilisateur, elle n'est donc plus dfinie par dfaut : if self.nom in obj._valeurs_par_defaut: obj._valeurs_par_defaut.remove(self.nom) if isinstance(value, Objet) and value.__feuille__ is None: value.__feuille__ = obj.__feuille__ return value def _set(self, obj, value, premiere_definition = False): if self.set_method is not None: if isinstance(value, Ref): value._changer_objet(self.set_method(obj, value.objet), premiere_definition = premiere_definition) return value else: return self.set_method(obj, value) return value def _get(self, obj, value): if self.get_method is not None: return self.get_method(obj, value) return value class Argument(BaseArgument): u"""Un descripteur pour un argument d'un objet.""" def __get__(self, obj, type=None): if obj is None: # c'est la classe (et non une instance) qui appelle l'argument ; on renvoie alors l'objet 'BaseArgument' lui-mme (et pas son contenu) return self return self._get(obj, self.__contenu__[obj].objet) # ---------------------- # NOTE : en cas de 'KeyError' ici, c'est probablement que la classe # qui possde l'argument ne l'initialise pas dans sa mthode __init__(). # Il faut donc rajouter dans MaClasse.__init__() une ligne style # 'self.__monargument = monargument = Ref(monargument)'. # ---------------------- def __set__(self, obj, value): # 1er cas : Redfinition d'un argument (self.__contenu__ contient dj une rfrence l'instance. if is_in(obj, self.__contenu__): value = self._set(obj, value) if isinstance(self.__contenu__[obj].objet, G.Variable): # Optimisation : par exemple, on effectue A.x = 2 o A est un Point. # Il est bien plus rapide de modifier la valeur de la variable A.x (A.x.val = 2), # que de crer une nouvelle Variable de valeur 2, # et de faire pointer A.x vers cette Variable (c.-d. de faire A.x = Variable(2)). self.__contenu__[obj].objet.val = value else: self.__contenu__[obj]._changer_objet(self._redefinir(obj, value)) ## self._rafraichir(obj) # 2me cas : Dfinition d'un nouvel argument else: value = self._set(obj, value, premiere_definition = True) self.__contenu__[obj] = self._definir(obj, value) class ArgumentNonModifiable(BaseArgument): u"""Un descripteur pour un argument non modifiable d'un objet. Note : un argument non modifiable n'est pas encapsul dans un objet 'Ref'.""" def __init__(self, types, get_method = None, set_method = None): BaseArgument.__init__(self, types, get_method = None, set_method = None) def _definir(self, obj, value): self._definir_type() # if value is None and self.defaut is not None: # if isinstance(self.defaut, (types.FunctionType, types.BuiltinFunctionType, types.TypeType)): # value = self.defaut() # else: # value = self.defaut value = self._verifier_type(value) if not isinstance(value, self.types): for _type in self.types: if hasattr(type, "_convertir"): try: value = _type._convertir(value) break except Exception: if param.verbose: print_error("Conversion impossible :") if not isinstance(value, self.types): raise TypeError, "%s should be of type %s, and not %s." %(value, self.types, type(value)) if isinstance(value, Objet) and value.__feuille__ is None: value.__feuille__ = obj.__feuille__ return value def __get__(self, obj, type=None): if obj is None: # c'est la classe (et non une instance) qui appelle l'argument ; on renvoie alors l'objet 'BaseArgument' lui-mme (et pas son contenu) return self return self._get(obj, self.__contenu__[obj]) def __set__(self, obj, value): value = self._set(obj, value, premiere_definition = True) # 1er cas : Redfinition d'un argument (self.__contenu__ contient dj une rfrence l'instance. if is_in(obj, self.__contenu__): raise AttributeError, "Argument non modifiable." # 2me cas : Dfinition d'un nouvel argument else: self.__contenu__[obj] = self._definir(obj, value) class Arguments(BaseArgument): # au pluriel ! u"""Un descripteur pour une liste d'arguments d'un objet.""" def __get__(self, obj, type=None): if obj is None: return self value = TupleObjets((elt.objet for elt in self.__contenu__[obj]), objet = obj, arguments = self.__contenu__[obj]) return self._get(obj, value) def __set__(self, obj, value): # 1er cas : Redfinition d'un argument (self.__contenu__ contient dj une rfrence l'instance. if is_in(obj, self.__contenu__): objets = tuple(self._redefinir(obj, self._set(obj, elt)) for elt in value) if len(objets) != len(self.__contenu__[obj]): raise RuntimeError, "Impossible (actuellement) de modifier la taille d'une liste d'arguments." for elt, objet in zip(self.__contenu__[obj], objets): elt._changer_objet(objet) ## self._rafraichir(obj) # 2me cas : Dfinition d'un nouvel argument else: self.__contenu__[obj] = tuple(self._definir(obj, self._set(obj, elt, premiere_definition = True)) for elt in value) class TupleObjets(tuple): u"""Un objet tuple personnalis, destin contenir des objets gomtriques. Usage interne.""" # __slots__ = "_arguments", "_obj" def __new__(cls, valeurs, objet, arguments): instance = tuple.__new__(cls, valeurs) instance._obj = objet instance._arguments = arguments return instance def __setitem__(self, index, value): self._arguments[index]._changer_objet(self._arguments.redefinir(self._obj, value)) class DescripteurFeuille(object): u"""Descripteur grant l'attribut '.__feuille __' de la classe 'Objet'. Usage interne.""" def __init__(self): self.__contenu__ = CustomWeakKeyDictionary() def __get__(self, obj, type=None): if obj is None: return self return self.__contenu__.get(obj) def __set__(self, obj, value): self.__contenu__[obj] = value for ancetre in obj._ancetres: if ancetre.__feuille__ is None: ancetre.__feuille__ = value label = obj._style.get("label") if isinstance(label, Formule): label.__feuille__ = value if hasattr(obj, "_set_feuille") and value is not None: obj._set_feuille() ## LA CLASSE Objet ET SES HERITIERS DIRECTES ############################## class Objet(object): u"""Un objet gomtrique. La classe Objet est la classe mre de tous les objets gomtriques. Note : elle n'est pas utilisable en l'tat, mais doit tre surclasse. """ __arguments__ = () # cf. geolib/__init__.py __feuille__ = DescripteurFeuille() __compteur_hierarchie__ = 0 _prefixe_nom = "objet" _utiliser_coordonnees_approchees = False _label_temporaire = None # Indique si l'objet doit tre rafraichi lorsque la fentre d'affichage change # Typiquement, c'est le cas des objets 'infinis' (comme les droites ou les courbes...), # et des objets dont la taille ne doit pas dpendre de la fentre : # les textes, les codages des segments, des angles et des arcs, la flche des vecteurs... _affichage_depend_de_la_fenetre = False # Certains types d'objets (par exemple, les points), doivent apparaitre sur la feuille # mme s'ils ont t gnrs comme valeur par dfaut (par exemple, _enregistrer_sur_la_feuille_par_defaut = False # Le dictionnaire 'contexte' sert partager des informations entre tous les objets __contexte = contexte @property def contexte(self): return self.__contexte # NOTES: # Apres l'importation du module, on peut ainsi changer la __feuille__ utilisee par defaut par les objets crees. # Pour changer la feuille par defaut, il suffit donc de modifier l'attribut de classe. # Si la __feuille__ est nulle (None), les objets ne sont pas enregistres. # Cela sert essentiellement a creer des objets temporaires, ou a faire de la geometrie sans figures (sic!) _style_defaut = param.defaut_objets # Un objet protg ne peut pas tre supprim protege = False # Les labels ont une initialisation minimale _initialisation_minimale = False def __init__(self, **styles): # --------------------------------------------------------------------- # PARTIE 1 de l'initialisation : # tout ce qui ne doit se faire qu'une seule fois, mme en cas d'hritage multiple. # --------------------------------------------------------------------- if not hasattr(self, "_style"): # CREATION DES ATTRIBUTS 'STANDARDS' self.etiquette = None # None pour 'tiquette non dfinie' self._pointable = hasattr(self, "_get_coordonnees") self._modifiable = hasattr(self, "_set_coordonnees") or hasattr(self, "_set_val") or hasattr(self, "_set_equation") self._deplacable = hasattr(self, "_set_coordonnees") # Lorque l'objet est enregistr sur une feuille sur une feuille de travail, celle-ci lui donne un nom. self.__nom = "" self.nom_latex = "" # Code LaTeX utilis pour l'affichage du nom # Valeurs mises en cache (coordonnes, etc.) self._cache = Cache() # gestion des styles par dfaut self._creer_style_par_defaut() # Parametres par defauts # TODO: renommer _representation en _representation self._representation = [] self._trace = [] self._trace_x = [] self._trace_y = [] self._gras = False # Indique que la figure devra tre rafrachie self.__figure_perimee = True # Indique que le label devra tre test # (ceci sert pour viter les plantages du parser de matplotlib en cas d'expression LaTeX incorrecte) self._label_correct = None # Interface avec le moteur d'affichage self.rendu = Rendu(self) # GESTION DES DEPENDANCES self.vassaux = WeakList() # lors de sa cration, l'objet n'a, lui, aucun vassal (aucun objet ne dpend de lui) # La cration d'une WeakList plutt que d'une liste permet d'viter les pertes de mmoire. # ATTENTION : ne pas utiliser un objet WeakSet. # En effet, il se peut qu'un objet apparaisse plusieurs fois comme vassal, si il apparait plusieurs fois comme argument. # C'est un comportement normal ; en cas de drfrencement de l'objet comme vassal, il ne doit tre drfrenc # qu'une fois si un seul argument est chang ! if self._initialisation_minimale: return # les labels ont une initialisation minimaliste self._ancetres = set() self._recenser_les_ancetres() for ancetre in self._ancetres: # l'objet est vassal de chacun des objets dont il depend ancetre.vassaux.append(self) # --------------------------------------------------------------------- # PARTIE 2 de l'initialisation : # ce qui suit peut tre excut plusieurs fois en cas d'initialisations multiples # --------------------------------------------------------------------- if styles: self.style(**styles) def __hash__(self): return id(self) def _nom_alea(self): u"""Retourne un nom disponible sur la feuille, adapt au type d'objet. Ex: M1 pour un point, s1 pour un segment, etc. """ return self.__feuille__.nom_aleatoire(self) # Fonctions d'initialisation de l'objet : ######################################### def _creer_style_par_defaut(self): self._style = {} classes = self.__class__.mro()[:-1] classes.reverse() for classe in classes: if issubclass(classe, Objet): self._style.update(classe._style_defaut) @property def _iter_arguments(self): """Retourne un iterateur vers les couples (argument, valeur) L'ordre des arguments est respect. """ return iter((arg, getattr(self, arg)) for arg in self.__arguments__) @property def _arguments(self): return dict((arg, getattr(self, arg)) for arg in self.__arguments__) def _set_feuille(self): if hasattr(self, "_valeurs_par_defaut") and self._valeurs_par_defaut: noms_args, args = zip(*self._iter_arguments) correspondance = False # On tente de dtecter via le nom de l'objet le nom que doit prendre chacun de ses arguments. # Par exemple, si un rectangle s'appelle ABCD, alors les points qui le constituent # doivent prendre pour noms A, B, C et D. if all(isinstance(arg, G.Point_generique) for arg in args): noms = re.findall(RE_NOM_OBJET, self._nom) if len(args) == len(noms): correspondance = True for i in xrange(len(args)): if args[i]._nom != "" and args[i]._nom != noms[i]: correspondance = False break if correspondance: for i in xrange(len(args)): if "_" + self.__class__.__name__ + "__" + noms_args[i] in self._valeurs_par_defaut: self.__feuille__.objets[noms[i]] = args[i] # chec du nommage intelligent : on se rabat sur des noms alatoires if not correspondance: for nom_arg in self._valeurs_par_defaut: self.__feuille__.objets[''] = getattr(self, nom_arg) self._valeurs_par_defaut = [] # Styles et informations sur l'objet #################################### def label(self, label = None, formule = False): u"""Affiche le label (ou etiquette) de l'objet. La chaine renvoye dpendra de la valeur du style legende: - si legende = 0, renvoie '' - si legende = 1, renvoie le nom de l'objet - si legende = 2, renvoie le label proprement dit de l'objet - si legende = 4, renvoie le label interprt comme une formule Si le paramtre label est spcifi, le label est modifi, et le style de lgende est fix 2.""" if label is not None: if formule: # on bascule l'affichage en mode formule en mme temps self.style(label = label, legende = FORMULE) else: # si on change le label, celui-ci s'affiche par dfaut en mme temps. self.style(label = label, legende = TEXTE) legende = self.style("legende") if legende == NOM: label = self.nom_latex elif legende == TEXTE: txt = self.style("label") label = uu(txt) elif legende == FORMULE: # Retourne le texte avec les expressions values label = unicode(self._style["label"]) else: return "" if self._label_correct is None: if '$' in label: # on teste si l'expression LaTeX convient au parser de matplotlib try: mathtext_parser(label) self._label_correct = True except Exception: print_error() self._label_correct = False else: # le parser ne sera pas utilis (ce n'est pas une expression LaTeX) self._label_correct = True if not self._label_correct: label = label.replace('$', r'$\$$') return label @property2 def label_temporaire(self, *val): if val: self._label_temporaire = val[0] self.figure_perimee() return self._label_temporaire def style(self, nom_style = None, **kwargs): u"""Renvoie le ou les styles demands, ou modifie les styles de l'objet. - nom_style est un nom de style, ou une liste de noms de styles: La proprit correspondante est recherche dans self._style. Ex: couleur, taille = A.style(('couleur', 'taille')) - **kw sert modifier des styles. Ex: A.style(couleur = 'blue') """ if kwargs:# or defaut: #~ if defaut: #~ defaut = defaut.copy() #~ defaut.update(kwargs) #~ kwargs = defaut if kwargs.has_key("label") or kwargs.has_key("legende"): # il faudra tester le label self._label_correct = None if isinstance(self._style["label"], Formule): label = self._style["label"] self._style["label"] = "" label.supprimer() # si la lgende passe en mode formule, ou si elle reste en mode formule : if kwargs.get("legende", None) == FORMULE or (not kwargs.has_key("legende") and self._style["legende"] == FORMULE): if kwargs.has_key("label"): kwargs["label"] = Formule(self, kwargs["label"]) else: self._style["label"] = Formule(self, self._style["label"]) # sinon, si elle n'est plus en mode formule : elif isinstance(self._style["label"], Formule): self._style["label"] = eval(repr(label)) self._style.update(kwargs) self.figure_perimee() if nom_style: def get_style(nom): if nom == 'label' and self._style['legende'] == FORMULE: return eval(repr(self._style['label'])) return self._style.get(nom) return get_style(nom_style) if isinstance(nom_style, basestring) else [get_style(nom) for nom in nom_style] return self._style ## @deprecated('Utiliser directement style desormais.') ## def modifier(self, **kwargs): ## u"Change le style et rafraichit l'affichage." ## self.style(**kwargs) def cacher(self): self.style(visible = False) self.message(self.nom_complet + u" cach\xe9.") def voir(self): self.style(visible = True) self.message(self.nom_complet + u" visible.") @property2 def visible(self, val = None): if val is not None: assert isinstance(val, bool) self.style(visible = val) return self.style('visible') #~ def fixer(self): #~ self.style(fixe = True) #~ def liberer(self): #~ self.style(fixe = False) def renommer(self, nom, **kw): u"Permet de renommer l'objet, et ventuellement de changer en mme temps son style." nom_actuel = self.nom if nom_actuel != nom: nom = self.__feuille__.objets._objet_renommable(self, nom) ## objet = self.__feuille__.objets.pop(nom_actuel) ## # Important: pour que l'objet soit bien considr non rfrenc ## # il faut qu'il n'ait pas de nom (on ne peut pas rfrencer 2 fois un objet). ## objet._nom = "" self.__feuille__.objets._dereferencer(self) self.__feuille__.objets[nom] = self self.style(**kw) self.figure_perimee() # Nom de l'objet ##############################"" def _nom(self, chaine = None): if chaine is None: return self.__nom else: self.__nom = chaine self._creer_nom_latex() _nom = property(_nom, _nom) def nom(self): if self.__feuille__: return self._nom return "" nom = property(nom, renommer) @property def _hierarchie_et_nom(self): # pour classer les objets return (self._hierarchie, self.nom) def _creer_nom_latex(self): u"""Renvoie le nom format en LaTeX. Ex: M1 -> $M_1$.""" nom = self.nom_corrige.rstrip('_') if not nom: # l'objet n'est pas enregistr dans une feuille self.nom_latex = "" return nom = re.sub("([A-Za-z']+)_?([0-9]+)", lambda m:"%s_{%s}" %m.groups(), nom) for lettre in ALPHABET_GREC: if nom.startswith(lettre) and (nom == lettre or not nom[len(lettre)].isalpha()): nom = "\\" + nom break self.nom_latex = "$" + nom + "$" # Test de nom_latex : try: mathtext_parser(self.nom_latex) except ParseFatalException: warning('"%s" can not be parsed by mathtext.' %self.nom_latex) self.nom_latex = self.nom except Exception: print_error() self.nom_latex = self.nom @staticmethod def latex_police_cursive(nom): def _police_cursive(m): s = m.group(0) return r"\mathscr{" + s + "}" if s[0] != '\\' else s return re.sub(r"\\?[A-Za-z]+", _police_cursive, nom) @property def nom_corrige(self): u"""Renvoie le nom en remplaant, le cas chant, _prime par un guillemet simple (').""" return self.nom.replace("_prime", "'") @property def nom_complet(self): return self.__class__.__name__.split("_")[0] + " " + self.nom_corrige @property def definition(self): u"""Dfinition en franais correct de l'objet. Exemple: 'Perpendiculaire d passant par M'""" return self.nom_complet # surclasser en gnral @classmethod def type(cls): return cls.__name__.lower() @classmethod def classe(cls): return cls.__name__ @classmethod def titre(cls, article = "un", point_final = True): u"""Affichage format du type de l'objet... (un peu de grammaire!). Article peut tre 'un', 'le', ou 'du', l'accord se faisant automatiquement. Le formatage est respect (essayez 'un', 'UN', 'Un'). >>> from wxgeometrie.geolib.vecteurs import Vecteur_libre >>> u = Vecteur_libre() >>> print u.titre() un vecteur libre. """ titre = cls.__doc__.split("\n")[0].rstrip(".").lower() if point_final: titre += "." if not (article.lower() == u"un"): titre = " ".join(titre.split()[1:]) if article.lower() == u"le": if titre[0] in "aeiouy": titre = u"l'" + titre elif cls.genre() == "m": titre = u"le " + titre else: titre = u"la " + titre if article.lower() == u"du": if titre[0] in "aeiouy": titre = u"de l'" + titre elif cls.genre() == "m": titre = u"du " + titre else: titre = u"de la " + titre if article.istitle(): titre = titre.capitalize() if article.isupper(): titre = titre.upper() return titre def titre_complet(self, article = "un", point_final = False): return self.titre(article, point_final) + " " + self.nom_corrige @classmethod def genre(cls): det = cls.__doc__.split()[0] if det.lower() == u"un": return "m" else: return "f" # Gestion des dpendances ###########################" def _recenser_les_ancetres(self): u"""Met jour l'ensemble des anctres (de la gnration prcdente), c--d. l'ensemble des objets dont dpend (directement) l'objet. L'ensemble est mis en cache dans self._ancetres. Pour obtenir tous les anctres (recherche rcursive), utiliser la mthode '_tous_les_ancetres'. """ self._ancetres.clear() for val in self._arguments.values(): if isinstance(val, (list, tuple)): for item in val: if isinstance(item, Objet): self._ancetres.add(item) elif isinstance(val, Objet): self._ancetres.add(val) self._modifier_hierarchie() def _tous_les_ancetres(self): u"""Retourne tous les objets dont dpend, de prs ou de loin, l'objet.""" ancetres = self._ancetres.copy() for ancetre in self._ancetres: ancetres.update(ancetre._tous_les_ancetres()) return ancetres ## def _heritiers(self, classer = False): ## u"Retourne l'ensemble des objets qui dpendent de cet objet, ventuellement classs selon l'attribut '_hierarchie'." ## heritiers = self.vassaux.values() ## vassaux = tuple(heritiers) ## for vassal in vassaux: ## heritiers += vassal._heritiers() ## heritiers = no_twin(heritiers) ## if classer: ## heritiers.sort(key = lambda x: getattr(x, "_hierarchie")) ## return heritiers def _heritiers(self): u"Retourne l'ensemble des objets qui dpendent de cet objet." heritiers = set(self.vassaux) for vassal in heritiers.copy(): heritiers.update(vassal._heritiers()) return heritiers ## def _modifier_hierarchie(self, valeur = None): ## # plus self._hierarchie est faible, plus l'objet est haut plac dans la hierarchie ## # au sommet, pour les objets libres ("suzerains"), self._hierarchie = 0 ## if valeur is None: ## valeur = max(-1, -1, *(objet._hierarchie for objet in self._ancetres)) + 1 ## self._hierarchie = valeur ## for obj in self.vassaux.values(): ## if obj._hierarchie <= self._hierarchie: ## obj._modifier_hierarchie(self._hierarchie + 1) def _modifier_hierarchie(self, valeur = None): # plus self._hierarchie est faible, plus l'objet est haut plac dans la hierarchie Objet.__compteur_hierarchie__ += 1 if valeur is None: valeur = self.__class__.__compteur_hierarchie__ self._hierarchie = valeur # Il peut arriver (trs rarement) que self.vassaux soit modifi # en mme temps. Mieux vaut donc transformer self.vassaux en tuple. for obj in tuple(self.vassaux): obj._modifier_hierarchie() # Acces a l'environnement exterieur ################################### @property def __canvas__(self): return self.__feuille__ and self.__feuille__.canvas def _pixel(self, point = None): if point is None: point = self return self.__canvas__.coo2pix(*point.coordonnees_approchees) def message(self, message): if self.__feuille__: self.__feuille__.message(message) else: print message def erreur(self, message): if self.__feuille__ is not None: self.__feuille__.erreur(message) else: raise RuntimeError, str2(message) @property def info(self): u""" surclasser, en donnant une information significative pour l'objet. Par exemple, pour un point ou un vecteur, ses coordonnes, pour un segment, sa longueur, pour un polygone, son aire... """ return self.nom_complet # API graphique (affichage et gestion des coordonnees) ###################################################### ## def _trace(self): ## if self.style("trace"): ## x, y = self.coordonnees_approchees ## if self.__trace__: ## self.__trace__[0]._x.append(x) ## self.__trace__[0]._y.append(y) ## else: ## self.__trace__ = [self.rendu.ligne([x], [y], color = self.style("couleur"), marker = ".", linestyle = "None", markersize = 10)] ## else: ## self.__trace__ = [] ## return self.__trace__ def _creer_figure(self): u"""Cette fonction est surclasser pour les objets ayant une reprsentation graphique. Sinon, l'objet sera invisible (exemple: `geolib.Variable`).""" return NotImplemented def figure_perimee(self): self.__figure_perimee = True if self.__feuille__ is not None: self.__feuille__.affichage_perime() # NB: si, par ex., un objet est dplac, son tiquette aussi. # On pourrait raffiner, mais dans le doute on rafraichit # toujours les coordonnes de l'tiquette avec l'objet. if self.etiquette is not None: self.etiquette.figure_perimee() @property def figure(self): u"""La reprsentation graphique associe l'objet. La figure est une liste d'objets graphiques pour matplotlib : tous les items de la liste sont du type `matplotlib.artist.Artist`. La figure bnficie d'un systme de cache : elle n'est pas recre chaque fois, mais seulement lorsqu'elle a t marque comme prime. La mthode interne ._creer_figure() """ if self.__figure_perimee and self.__feuille__ is not None: with contexte(exact = False): # Utiliser self.visible, et non self.style('visible'), # car self.visible est customis pour les tiquettes. visible = self.visible if self.existe and (visible or self.__feuille__.afficher_objets_caches): # Remet alpha 1 par dfaut : # la transparence a peut-tre t modifie si l'objet tait auparavant invisible for artist in self._representation: artist.set_alpha(1) self._creer_figure() # Styles supplmentaires (pour le dbugage essentiellement) extra = self.style("extra") if extra: for artist in self._representation: artist.set(**extra) if not visible: for artist in self._representation: artist.set_alpha(.4*artist.get_alpha()) self._creer_trace() else: self._representation = [] self.__figure_perimee = False return self._representation ## def creer_figure(self): ## u"Construit la reprsentation graphique de l'objet (mais ne rafraichit pas l'affichage par dfaut)." ## # si l'objet n'a pas de nom (''), c'est que c'est une construction intermediaire ## # il ne faut donc pas l'afficher. ## if self.__feuille__ is not None: ## #~ nom = self.nom ## canvas = self.__canvas__ ## ## with contexte(exact = False): ## if (self.__feuille__.contient_objet(self) or self.__feuille__.contient_objet_temporaire(self)) and canvas: ## if self.etiquette is not None: ## self.etiquette.creer_figure() ## visible = self.style("visible") ## if self.existe and (visible or self.__feuille__.afficher_objets_caches): ## # Remet alpha 1 par dfaut : ## # la transparence a peut-tre t modifie si l'objet tait auparavant invisible ## for representation in self._representation: ## representation.set_alpha(1) ## ## self._creer_figure() ## # Styles supplmentaires (pour le dbugage essentiellement) ## extra = self.style("extra") ## if extra: ## for representation in self._representation: ## representation.set(**extra) ## if not visible: ## for representation in self._representation: ## representation.set_alpha(.4*representation.get_alpha()) ## self._creer_trace() ## ## else: ## self._representation = [] ## self.__feuille__.affichage_perime() def effacer_trace(self): self._trace = [] self._trace_x = [] self._trace_y = [] def _creer_trace(self): u"Mthode surclasser." pass def en_gras(self, valeur = True): u"""Met en valeur un objet. Typiquement, il s'agit d'un objet slectionn. Si l'objet a t mis en gras, retourne True. S'il a t remis en tat "normal", retourne False. Si l'tat de l'objet n'a pas chang, retourne None. """ if valeur: if not self._gras: self._en_gras(valeur) self._gras = True return True else: if self._gras: self._en_gras(valeur) self._gras = False return False def _en_gras(self, booleen): e = self.style("epaisseur") t = self.style("taille") ## if e is not None: ## e *= param.zoom_ligne ## if t is not None: ## t *= param.zoom_ligne if booleen: for plot in self.figure: if hasattr(plot, "set_linewidth") and e is not None: plot.set_linewidth(2*e) if hasattr(plot, "set_markeredgewidth") and e is not None: plot.set_markeredgewidth(2*e) if t is not None: plot.set_markersize(1.15*t) # if self.etiquette is not None: # self.etiquette._en_gras(True) else: for plot in self.figure: if hasattr(plot, "set_linewidth") and e is not None: plot.set_linewidth(e) if hasattr(plot, "set_markeredgewidth") and e is not None: plot.set_markeredgewidth(e) if t is not None: plot.set_markersize(t) # if self.etiquette is not None: # self.etiquette._en_gras(False) def distance_inf(self, x, y, d): u"Teste si la distance ( l'ecran, en pixels) entre l'objet et (x, y) est infrieure d." if hasattr(self, "_distance_inf") and self.existe: with contexte(exact = False): return self._distance_inf(x, y, d) else: return False # Certains objets n'existent pas toujours. # Par exemple, l'intersection de 2 droites est parfois vide. # Un objet construit a partir du point d'intersection de 2 droites peut donc ne plus exister temporairement. # En particulier, il est important de verifier que l'objet existe avant de l'afficher. def _existe(self): # Les conditions d'existence sont toujours values de manire approche pour l'instant with contexte(exact = False): if all(obj.existe for obj in self._ancetres): return self._cache.get('conditions', self._conditions_existence) else: # /!\ self.conditions_existence() ne doit PAS etre evalue si un ancetre n'existe pas (resultat imprevisible) return False @property def existe(self): return self._cache.get('existence', self._existe) def _conditions_existence(self): u"""Conditions spcifiques pour que l'objet existe, definir pour chaque objet. Exemple: la mediatrice de [AB] existe ssi A != B. """ return True def _espace_vital(self): return None @property def espace_vital(self): if self.existe: return self._espace_vital() # Notes concernant le code prcdent : # - Un objet qui "n'existe pas" (au sens gomtrique) a des coordonnes fixes None. # - Un objet libre (c'est--dire qui accepte des arguments pour ses mthodes coordonnees() ou equation()) # ne doit pas avoir de conditions d'existence. # API non graphique ################### def __contains__(self, y): with contexte(exact = False): return self._contains(y) def _contains(self): raise NotImplementedError def supprimer(self): # if self.style('protege'): # self.cacher() # return # Le nom doit tre rcupr AVANT la suppression. nom = self.nom_complet if self.protege: self.erreur(u"%s est protg." %nom) else: self._supprime() if self.__feuille__: self.__feuille__.affichage_perime() self.message(u"%s supprim." %nom) def _supprime(self): for ancetre in self._ancetres: # L'objet est supprim de la liste des vassaux, pour chaque objet dont il dpend. ancetre.vassaux.remove_all(self) # NB: ne gnre jamais d'erreur, mme si self n'est pas dans la WeakList # (contrairement au ".remove()" d'une liste) for heritier in list(self.vassaux): try: heritier._supprime() except KeyError: pass # il se peut que l'objet n'existe dj plus. if self.__feuille__: self.__feuille__.objets._dereferencer(self) ## if self.__canvas__: ## self.__canvas__.graph.supprimer(self._representation) ## if self.etiquette is not None: ## self.__canvas__.graph.supprimer(self.etiquette._representation) if isinstance(self._style["label"], Formule): # il faut supprimer proprement la formule. self._style["label"].supprimer() def redefinir(self, valeur): self.__feuille__.redefinir(self, valeur) def __repr__(self, styles = True): u"Mthode utilise pour sauvegarder les objets." def formater(objet): if isinstance(objet, Objet): if self.__feuille__ and self.__feuille__.contient_objet(objet): return objet.nom else: return objet.__repr__(styles) #if isinstance(objet, Variable): return repr(objet.val()) if isinstance(objet, (list, tuple)): return "[" + ",".join([formater(item) for item in objet]) + "]" # Le 'float()' servent contourner un bug de numpy 1.1.x et numpy 1.2.x (repr de float64) : if isinstance(objet, numpy.floating): return repr(float(objet)) return repr(objet) if styles: return self.classe() + "(" + ",".join(key + "=" + formater(val) for key, val in self._iter_arguments) + ", **" + repr(self.style()) + ")" else: return self.classe() + "(" + ",".join(key + "=" + formater(val) for key, val in self._iter_arguments) + ")" def __str__(self): u"Mthode utilise pour l'affichage (ne retourne pas les styles)." def formater(objet): if isinstance(objet, Objet): if self.__feuille__ and self.__feuille__.contient_objet(objet): return objet.nom ## #if isinstance(objet, Variable): return repr(objet.val()) if isinstance(objet, (list, tuple)): return "[" + ", ".join(formater(item) for item in objet) + "]" return unicode(objet) return uu(self.classe() + "(" + ", ".join(key + " = " + formater(val) for key, val in self._iter_arguments) + ")") def _definition(self): u"""Utilis pour afficher la dfinition actuelle de l'objet avant de le redfinir. L'affichage est compact (styles, etc. non affichs). """ def formater(objet): if isinstance(objet, Objet): if self.__feuille__ and self.__feuille__.contient_objet(objet): return objet.nom else: return objet._definition() ## #if isinstance(objet, Variable): return repr(objet.val()) if isinstance(objet, (list, tuple)): return "[" + ",".join([formater(item) for item in objet]) + "]" # Le 'float()' servent contourner un bug de numpy 1.1.x et numpy 1.2.x (repr de float64) : if isinstance(objet, numpy.floating): return repr(float(objet)) return repr(objet) return self.classe() + "(" + ", ".join(formater(val) for key, val in self._iter_arguments) + ")" def copy(self): kwargs = self.style().copy() kwargs.pop("noms", None) ## #~ print "copie de %s :" % self ## #~ print self.style() kwargs.update(self._arguments) return self.__class__(**kwargs) def copier_style(self, objet): u"""Applique le style de 'objet' l'objet (self). Si les objets sont de type diffrent, seuls certains styles communs entrent en compte.""" style = self.style() for key, value in objet.style().items(): if style.has_key(key): if key in param.styles_a_signification_variable: #print "clef:", key, style["categorie"], objet.style("categorie") if style["categorie"] == objet.style("categorie"): style[key] = value elif key not in param.styles_a_ne_pas_copier: style[key] = value if objet.etiquette is not None and self.etiquette is not None: style_etiquette = objet.etiquette.style().copy() for key in param.styles_a_ne_pas_copier: style_etiquette.pop(key, None) self.etiquette.style(**style_etiquette) self.figure_perimee() # def _image(self, transformation): # raise NotImplementedError ### Routines rapides ### ### Les routines suivantes sont optimises pour la vitesse, afin d'viter de passer par des classes (Vecteur, Segment, ...) lors des calculs internes. ### Elles essaient de concilier ceci avec une grande souplesse d'utilisation, en acceptant des arguments de types trs divers. ### Par exemple, un point peut tre donn sous la forme d'une instance de la classe Point, d'un tuple, d'une liste, d'un array. ############################## ## ## ## @staticmethod ## def _distance(A, B): ## u"Distance entre les points A et B." ## pylib.deprecation("A eviter desormais.") ## xA, yA = A ## xB, yB = B ## return math.hypot(xA - xB, yA - yB) ## ## @staticmethod ## def _carre_distance(A, B): ## u"Carr de la distance entre les points A et B." ## pylib.deprecation("A eviter desormais.") ## xA, yA = A ## xB, yB = B ## return (xB-xA)**2+(yB-yA)**2 ## ## @staticmethod ## def _vec(A, B): ## u"Coordonnes du vecteur A>B." ## pylib.deprecation("A eviter desormais.") ## xA, yA = A ## xB, yB = B ## return xB - xA, yB - yA ## @staticmethod ## def _angle_oriente(vec1, vec2): # DESUET ! Utiliser la fonction angle_vectoriel() de pylib maintenant ## u"Valeur en radian de l'angle dfinit par les vecteurs vec1, et vec2." ## pylib.deprecation("Utiliser la fonction angle_vectoriel() de pylib maintenant") ## x1, y1 = vec1; x2, y2 = vec2 ## signe = cmp(x1*y2-x2*y1, 0) # signe du dterminant ## if signe is 0: ## signe = 1 # Peut importe le signe quand les vecteurs sont colineaires (0 = -0 et pi = -pi [2pi]) ## return signe*math.acos(produit_scalaire(vec1, vec2)/(math.hypot(*vec1)*math.hypot(*vec2))) # La gestion de l'affichage est complique... # L'ide est que, quand on modifie un objet (style except), on passe par 3 mthodes : # _set_val, _set_equation, et _set_coordonnees. # Une fois ces mthodes appeles, on doit rafraichir l'affichage de toutes les dpendances, aprs les avoir recalcules. # La difficult, c'est que quand on modifie, par exemple, les coordonnes d'un point, # on modifie aussi les variables x et y (arguments de ce point), et donc, # on rafraichit inutilement plusieurs fois l'affichage. # Pour viter cela, on marque simplement les objets rafficher, et on rafrachit l'affichage une seule fois " la fin". # Mais comment dtecter "la fin" ? La solution retenue est de crer un verrou que pose la premire mthode s'xcuter. def _heritiers_a_recalculer(self, heritiers): u"""Indique que l'objet a t modifi, et que les coordonnes de tous les objets qui en dpendent doivent tre recalcules.""" self._cache.clear() if self.etiquette is not None: self.etiquette._cache.clear() for objet in heritiers: objet._cache.clear() if objet.etiquette is not None: objet.etiquette._cache.clear() ############################################################# #### Types d'objets trs gnraux class Objet_avec_coordonnees(Objet): u"""Un objet ayant des coordonnes (ex: points, vecteurs, ...). Usage interne : permet aux objets en hritant d'offrir un accs facile pour l'utilisateur final aux coordonnes via __call__.""" _style_defaut = {} # en cas d'hritage multiple, cela vite que le style de Objet efface d'autres styles def _get_coordonnees(self): raise NotImplementedError ## def _get_coordonnees_approchees(self): ## return self._get_coordonnees() def __call__(self, *args): if args and not self.style("fixe"): if len(args) == 1: self.coordonnees = args[0] elif len(args) == 2: self.coordonnees = args else: raise TypeError, "Trop d'arguments." return self.coordonnees def __iter__(self): # definit tuple(objet) si l'objet renvoie des coordonnees if self.existe: return iter(self.coordonnees) raise TypeError, str2(u"Conversion impossible, l'objet n'est pas defini.") ## @property ## def coo(self): ## u"""Alias de numpy.array(self.coordonnees).""" ## pylib.deprecation("A eviter desormais.") ## coordonnees = self.coordonnees ## if coordonnees is None: ## self.message(u"L'objet %s n'est pas dfini" % self.nom) ## return None ## return numpy.array(coordonnees) @property def info(self): return self.nom_complet + u" de coordonnes ("+ nice_display(self.x) + ", " + nice_display(self.y) + ")" @property def coordonnees_approchees(self): with contexte(exact = False): return self.coordonnees @property2 def coordonnees(self, couple = None): if couple is None: d = ({'exact': False} if self._utiliser_coordonnees_approchees else {}) with contexte(**d): return self._cache.get('xy', self._get_coordonnees) if self.existe else None try: if self.__feuille__ is not None and self.__feuille__._verrou_affichage is None: self.__feuille__._verrou_affichage = self self.__feuille__._a_rafraichir = [] self._set_coordonnees(*couple) if self.__feuille__ is not None and self.__feuille__._verrou_affichage is self: for obj in no_twin(self.__feuille__._a_rafraichir): obj.figure_perimee() self.__feuille__._objet_deplace = self finally: if self.__feuille__ is not None and self.__feuille__._verrou_affichage is self: self.__feuille__._verrou_affichage = None xy = coordonnees def __getitem__(self, i): return self.coordonnees[i] def _creer_trace(self): if self.style("trace"): x, y = self.coordonnees if not self._trace: ligne = self.rendu.ligne() ligne.set_color(self.style("couleur")) ligne.set_marker(".") ligne.set_linestyle("None") ligne.set_markersize(10) self._trace = [ligne] self._trace_x.append(x) self._trace_y.append(y) self._trace[0].set_data(self._trace_x, self._trace_y) else: self._trace = [] self._trace_x = [] self._trace_y = [] class Objet_avec_coordonnees_modifiables(Objet_avec_coordonnees): u"""Un objet ayant des coordonnes (ex: points libres, vecteurs libres, textes...). Usage interne.""" _style_defaut = {} abscisse = x = __x = Argument("Variable_generique", defaut = 0) ordonnee = y = __y = Argument("Variable_generique", defaut = 0) def __init__(self, x = None, y = None, **styles): self.__x = x = Ref(x) self.__y = y = Ref(y) Objet_avec_coordonnees.__init__(self, **styles) @staticmethod def _recuperer_x_y(x, y, kw = None): if kw and kw.get("z", False): z = kw.pop("z") x = z.real y = z.imag if y is None: if isinstance(x, complex): # un nombre complexe comme -1+2j est accept comme argument x = x.real y = x.imag elif hasattr(x, "__iter__"): # un couple comme (-1,2) est accept comme argument x, y = x if kw is None: return x, y else: return x, y, kw def _get_coordonnees(self): return self.__x.valeur, self.__y.valeur def _set_coordonnees(self, x = None, y = None): if x is not None: x, y = self._recuperer_x_y(x, y) self.__x = x self.__y = y def z(self, valeur = None): if valeur is None: return self.__x + 1j*self.__y self.__x = valeur.real self.__y = valeur.imag affixe = z = property(z, z) def __setitem__(self, i, valeur): if i == 0: self.__x = valeur elif i == 1: self.__y = valeur else: raise IndexError, "L'objet n'a que deux coordonnees." class Objet_avec_equation(Objet): u"""Un objet contenant une quation (ex: droites, ...). Usage interne : permet aux objets en hritant d'offrir un accs facile pour l'utilisateur final cette valeur via __call__.""" _style_defaut = {} # en cas d'hritage multiple, cela vite que le style de Objet efface d'autres styles def _get_equation(self): raise NotImplementedError ## def _get_equation_approchee(self): ## return self._get_equation() def __call__(self, equation = None): if equation is not None and not self.style("fixe"): self.equation = equation return self.equation def __iter__(self): # definit tuple(objet) si l'objet renvoie une quation if self.existe: return iter(self.equation) raise TypeError, str2(u"Conversion impossible, l'objet n'est pas defini.") @property def equation_approchee(self): with contexte(exact = False): return self.equation @property2 def equation(self, coefficients = None): if coefficients is None: return self._cache.get('eq', self._get_equation) if self.existe else None try: if self.__feuille__ is not None and self.__feuille__._verrou_affichage is None: self.__feuille__._verrou_affichage = self self.__feuille__._a_rafraichir = [] self._set_equation(*coefficients) if self.__feuille__ is not None and self.__feuille__._verrou_affichage is self: # Une seule fois la fin for obj in no_twin(self.__feuille__._a_rafraichir): obj.figure_perimee() self.__feuille__._objet_deplace = self finally: if self.__feuille__ is not None and self.__feuille__._verrou_affichage is self: self.__feuille__._verrou_affichage = None class Objet_avec_valeur(Objet): u"""Un objet contenant une valeur numrique (ex: angles, variables, ...). Usage interne : permet aux objets en hritant d'offrir un accs facile pour l'utilisateur final cette valeur via __call__.""" _style_defaut = {} # en cas d'hritage multiple, cela vite que le style de Objet efface d'autres styles def _get_valeur(self): raise NotImplementedError def _set_valeur(self): raise NotImplementedError ## def _get_valeur_approchee(self): ## return self._get_valeur() def __call__(self, valeur = None): if valeur is not None and not self.style("fixe"): self.val = valeur return self.val @property def valeur_approchee(self): with contexte(exact = False): return self.valeur @property2 def valeur(self, valeur = None): if valeur is None: return self._cache.get('val', self._get_valeur) if self.existe else None try: if self.__feuille__ is not None and self.__feuille__._verrou_affichage is None: self.__feuille__._verrou_affichage = self self.__feuille__._a_rafraichir = [] self._set_valeur(valeur) if self.__feuille__ is not None and self.__feuille__._verrou_affichage is self: for obj in no_twin(self.__feuille__._a_rafraichir): obj.figure_perimee() self.__feuille__._objet_deplace = self finally: if self.__feuille__ is not None and self.__feuille__._verrou_affichage is self: self.__feuille__._verrou_affichage = None val = valeur class Objet_numerique(Reel, Objet_avec_valeur): u"Ensemble de mthodes propres aux angles, aux variables, et autres objets numriques." _style_defaut = {} # en cas d'hritage multiple, cela vite que le style de Objet efface d'autres styles def __init__(self, *args, **kw): Objet.__init__(self, *args, **kw) def __float__(self): return float(self.val) def __int__(self): return int(self.val) ## -- code gnr automatiquement -- (cf. creer_operations.py) def __add__(self, y): if isinstance(y, Objet_numerique): return self.val + y.val return self.val + y def __radd__(self, y): return y + self.val def __iadd__(self, y): self.val = self.val + (y.val if isinstance(y, Objet_numerique) else y) return self def __sub__(self, y): if isinstance(y, Objet_numerique): return self.val - y.val return self.val - y def __rsub__(self, y): return y - self.val def __isub__(self, y): self.val = self.val - (y.val if isinstance(y, Objet_numerique) else y) return self def __mul__(self, y): if isinstance(y, Objet_numerique): return self.val * y.val return self.val * y def __rmul__(self, y): return y * self.val def __imul__(self, y): self.val = self.val * (y.val if isinstance(y, Objet_numerique) else y) return self def __div__(self, y): if isinstance(y, Objet_numerique): return self.val / y.val return self.val / y def __rdiv__(self, y): return y / self.val def __idiv__(self, y): self.val = self.val / (y.val if isinstance(y, Objet_numerique) else y) return self def __truediv__(self, y): if isinstance(y, Objet_numerique): return self.val / y.val return self.val / y def __rtruediv__(self, y): return y / self.val def __itruediv__(self, y): self.val = self.val / (y.val if isinstance(y, Objet_numerique) else y) return self def __pow__(self, y): if isinstance(y, Objet_numerique): return self.val ** y.val return self.val ** y def __rpow__(self, y): return y ** self.val def __ipow__(self, y): self.val = self.val ** (y.val if isinstance(y, Objet_numerique) else y) return self def __mod__(self, y): if isinstance(y, Objet_numerique): return self.val % y.val return self.val % y def __rmod__(self, y): return y % self.val def __imod__(self, y): self.val = self.val % (y.val if isinstance(y, Objet_numerique) else y) return self def __floordiv__(self, y): if isinstance(y, Objet_numerique): return self.val // y.val return self.val // y def __rfloordiv__(self, y): return y // self.val def __ifloordiv__(self, y): self.val = self.val // (y.val if isinstance(y, Objet_numerique) else y) return self ## -- fin du code gnr automatiquement -- def __abs__(self): return abs(self.val) def __neg__(self): return 0 - self # def __pos__(self): # return 0 + self def __eq__(self, y): return self.val == y ## return hasattr(y, "__float__") and abs(float(self) - float(y)) < param.tolerance # def __ne__(self, y): # return not self == y def __nonzero__(self): return self != 0 def __gt__(self, y): return self.val > y ## return hasattr(y, "__float__") and float(self) > float(y) # def __ge__(self, y): # return self > y or self == y ### TEMPLATE ### ##class MonObjet(ObjetParent1, ObjetParent2): ## ## __slots__ = ("objet_lie", "__objet_interne") ## _style_defaut = param.mon_objet ## __objet1 = objet1 = Argument("Point_generique") ## __objet2 = objet2 = Argument("Droite, Segment, Demidroite") ## __objets = objets = Arguments("Point_generique") ## ## ## def __init__(self, objet1, objet2, *objets, **styles): ## self.__objet1 = objet1 = Ref(objet1) ## self.__objet2 = objet2 = Ref(objet2) ## self.__objets = objets = (Ref(objet) for objet in objets) ## ObjetParent1.__init__(self, objet1, objet2) ## ObjetParent2.__init__(self, *objets, **styles) ## self.objet_lie = AutreObjet(*objets) ## self.etiquette = MonLabel(self) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/classeur.py0000644000175000017500000000626412014170666023362 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Gestionnaire de feuilles # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .feuille import Feuille class Classeur(list): u"Gestionnaire de feuilles." # Indique si le contenu du classeur a t modifi depuis la dernire sauvegarde de *session* modifie = False def __init__(self, parent = None, liste = [], log = None): self.parent = parent self.log = log list.__init__(self, liste) self.nouvelle_feuille() def __setitem__(self, i, feuille): assert isinstance(feuille, Feuille) if feuille in self: self.remove(feuille) if i >= 0: list.insert(self, i, feuille) elif i == -1: list.append(self, feuille) else: list.insert(self, i + 1, feuille) self._classeur_modifie() def _get_actuelle(self): return self[-1] def _set_actuelle(self, feuille): self[-1] = feuille self._classeur_modifie() def _del_actuelle(self): self.pop() if not self: # Il faut qu'il y ait toujours une feuille pour travailler self.nouvelle_feuille() self._classeur_modifie() feuille_actuelle = property(_get_actuelle, _set_actuelle, _del_actuelle) def nouvelle_feuille(self, nom = None, parametres = None): # Par dfaut, les paramtres sont ceux de la feuille prcdente if parametres is None and self: parametres = self[-1].parametres if nom is None: noms = self.noms i = 1 while ("Feuille " + str(i)) in noms: i += 1 nom = "Feuille " + str(i) self.append(Feuille(self, titre = nom, log = self.log, parametres = parametres)) self._classeur_modifie() return self[-1] def _classeur_modifie(self): self.modifie = True if self.parent is not None: self.parent._changement_feuille() def vider(self): while self: self.pop() # Il faut qu'il y ait toujours une feuille pour travailler self.nouvelle_feuille() @property def noms(self): return [feuille.nom for feuille in self] wxgeometrie-0.133.2.orig/wxgeometrie/geolib/variables.py0000644000175000017500000002715412014170666023512 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Variable # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re, time from sympy import Symbol, Basic, sympify from .objet import Ref, Argument, Objet, Objet_numerique, souffler, TYPES_REELS,\ contexte from ..pylib import property2, print_error, fullrange, is_in from ..mathlib.parsers import VAR_NOT_ATTR, NBR_SIGNE from .. import param class Variable_generique(Objet_numerique): u"""Une variable gnrique. Usage interne : la classe mre pour tous les types de variables.""" _prefixe_nom = "k" _style_defaut = param.variables def __init__(self, **styles): Objet.__init__(self, **styles) @staticmethod def _convertir(objet): u"Convertit un objet en variable." ## if isinstance(objet, Variable): # Inutile (?) ## return objet.copy() return Variable(objet) class Variable(Variable_generique): u"""Une variable libre. Une variable numrique ; l'argument peut tre un nombre, ou une expression sous forme de chaine de caractres. Exemple: Variable(17.5), Variable('AB.longeur+1'). Dans ce dernier cas, il est ncessaire qu'une feuille de travail soit dfinie. Note : ne pas dfinir directement l'attribut __contenu !""" # RE correspondant un nom de variable (mais pas d'attribut) __re = re.compile('(' + VAR_NOT_ATTR + ')') def _set_contenu(self, value): if isinstance(value, Variable): if value.__feuille__ is not None: value.__feuille__ = self.__feuille__ if value._Variable__fonction is None: return value.contenu return value.val elif isinstance(value, basestring): value = value.replace(" ","") # Si c'est un nombre: if not "." in value: try: symp = sympify(value) if not symp.atoms(Symbol): value = symp except AttributeError: pass elif re.match(NBR_SIGNE + "$", value): value = eval(value, {}) elif isinstance(value, Basic): if not value.is_real: raise RuntimeError, "La variable doit etre reelle." return value __contenu = Argument(TYPES_REELS + (basestring,), None, _set_contenu, defaut = 0) def __init__(self, contenu = 0, **styles): Variable_generique.__init__(self, **styles) self.__liste = [] self.__fonction = None self.__contenu = contenu = Ref(contenu) def _test_dependance_circulaire(self, valeur): u"""Provoque une erreur si l'objet se retrouve dpendre de lui-mme avec la nouvelle valeur. Retourne une liste compose alternativement d'instructions et d'objets de la feuille, et un ensemble constitu des objets de la feuille mis en jeu dans le code. (... documenter ...)""" if isinstance(valeur, basestring) and self.__feuille__ is not None: liste = re.split(self.__re, valeur) ensemble = set() for i in xrange(1, len(liste), 2): obj = self.__feuille__.objets[liste[i]] if isinstance(obj, Objet): liste[i] = obj ensemble.add(obj) if self is obj or is_in(self, obj._tous_les_ancetres()): print self, raise RuntimeError, "Definition circulaire dans %s : l'objet %s se retrouve dependre de lui-meme." %(self, obj) return liste, ensemble return None, None def _compile(self, liste, ensemble): u"""Compile l'expression stocke dans la variable ; les arguments sont les valeurs retournes par '_test_dependance_circulaire'. La compilation doit toujours avoir lieu la fin de la procdure de redfinition de la variable, car elle ne doit tre excute que si la redfinition de la variable va effectivement avoir lieu, c'est--dire si tout le processus prcdent s'est excut sans erreur.""" if self._type == "compose" and self.__feuille__ is not None: ## re.findall(self.__re, self.valeur) self.__liste = liste self.__fonction = eval("lambda:" + self.__contenu, self.__feuille__.objets) # on supprime la variable de la liste des vassaux pour les objets dont elle ne dpendra plus desormais: for objet in self._ancetres: objet.vassaux.remove(self) self._ancetres = ensemble self._modifier_hierarchie() for objet in self._ancetres: # l'objet est vassal de chacun des objets dont il depend objet.vassaux.append(self) else: for objet in self._ancetres: objet.vassaux.remove(self) self._ancetres = set() self._modifier_hierarchie() self.__liste = [] self.__fonction = None @property2 def contenu(self, value = None): if value is None: if self.__liste: # variable contenant une expression compile # On regnre l'expression partir de l'expression compile. # C'est important, car certains objets de la feuille peuvent avoir chang de nom entre temps. valeur = "" for elt in self.__liste: if isinstance(elt, Objet): valeur += elt.nom else: valeur += elt return valeur else: return self.__contenu else: args = self._test_dependance_circulaire(value) self.__contenu = value self._compile(*args) def _get_valeur(self): # cf. self._conditions_existence return self.__val_cache if contexte['exact'] else self.__val_cache_approche def _set_valeur(self, valeur): self.contenu = valeur def _set_feuille(self): self._compile(*self._test_dependance_circulaire(self.__contenu)) self._heritiers_a_recalculer(self._heritiers()) @property def _type(self): return isinstance(self.__contenu, basestring) and "compose" or "simple" def _recenser_les_ancetres(self): # warning("'_recenser_les_ancetres' n'a aucun effet pour une variable.") self._modifier_hierarchie() def _conditions_existence(self): # conditions specifiques pour que l'objet existe, a definir pour chaque objet if self._type == "compose": try: self.__val_cache = self.__fonction() if isinstance(self.__val_cache, Variable): self.__val_cache = self.__val_cache.val except Exception: if param.verbose: print_error(u"Impossible de dterminer la valeur de la variable " + self.nom + repr(self)) return False else: self.__val_cache = self.contenu try: self.__val_cache_approche = float(self.__val_cache) except TypeError: print_error(u"Variable de type incorrect.") return False return True def __str__(self): return str(self.contenu) def _definition(self): if self._type == "compose": return repr(self.contenu) else: return str(self.contenu) def _update(self, objet): if not isinstance(objet, Variable): objet = self._convertir(objet) if isinstance(objet, Variable): if objet.__feuille__ is not None: objet.__feuille__ = self.__feuille__ if objet._Variable__fonction is None: self.contenu = objet.contenu else: self.val = objet.val else: raise TypeError, "l'objet n'est pas une variable." def varier(self, debut = 0, fin = 1, pas = 0.02, periode = 0.03): if self.__feuille__ is not None: self.__feuille__.start() for i in fullrange(debut, fin, pas): t = time.clock() self.val = i while time.clock() < t + periode: souffler() if self.__feuille__._stop: break souffler() if self.__feuille__._stop: break ### Addition et multiplication lies ### ------------------------------------------------ ### Est-ce encore bien utile ? ## def add(self, y): ## u"Addition lie (le rsultat est une variable qui reste toujours gale la somme des 2 valeurs)." ## if self._type == "simple": ## if isinstance(y, TYPES_NUMERIQUES) or (isinstance(y, Variable) and y._type == "simple"): ## return Variable(self + y) ## var = Variable("(%s)+(%s)" %(self, y)) ## var.__feuille__ = self.__feuille__ ## return var ## def mul(self, y): ## u"Multiplication lie (le rsultat est une variable qui reste toujours gale au produit des 2 valeurs)." ## if self._type == "simple": ## if isinstance(y, TYPES_NUMERIQUES) or (isinstance(y, Variable) and y._type == "simple"): ## return Variable(self * y) ## var = Variable("(%s)*(%s)" %(self, y)) ## var.__feuille__ = self.__feuille__ ## return var class Rayon(Variable_generique): u"""Le rayon d'un cercle. >>> from wxgeometrie.geolib import Cercle, Rayon >>> c = Cercle((0, 0), 1) >>> c.rayon 1 >>> r = Rayon(c) >>> r.val 1 >>> c.rayon = 2 >>> r.val 2 """ __cercle = cercle = Argument("Cercle_Arc_generique") def __init__(self, cercle, **styles): self.__cercle = cercle = Ref(cercle) Variable_generique.__init__(self, **styles) def _get_valeur(self): return self.__cercle.rayon class Mul(Variable_generique): u"""Le produit de deux variables.""" __var1 = var1 = Argument(Variable_generique) __var2 = var2 = Argument(Variable_generique) def __init__(self, var1, var2, **styles): self.__var1 = var1 = Ref(var1) self.__var2 = var2 = Ref(var2) Variable_generique.__init__(self, **styles) def _get_valeur(self): return self.__var1.val*self.__var2.val class Add(Variable_generique): u"""La somme de deux variables.""" __var1 = var1 = Argument(Variable_generique) __var2 = var2 = Argument(Variable_generique) def __init__(self, var1, var2, **styles): self.__var1 = var1 = Ref(var1) self.__var2 = var2 = Ref(var2) Variable_generique.__init__(self, **styles) def _get_valeur(self): return self.__var1.val + self.__var2.val wxgeometrie-0.133.2.orig/wxgeometrie/geolib/intersections.py0000644000175000017500000003453012014170666024427 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .lignes import Ligne_generique, Droite_generique, Tangente from .cercles import Cercle_generique, Arc_generique from .objet import Ref, Argument, contexte, Objet from .routines import vect, angle_vectoriel, produit_scalaire, racines from .points import Point_generique ########################################################################################## # Intersections def Intersection(objet1, objet2, **styles): def case(type1, type2): return (isinstance(objet1, type1) and isinstance(objet2, type2))\ or (isinstance(objet2, type1) and isinstance(objet1, type2)) if case(Ligne_generique, Ligne_generique): return Intersection_droites(objet1, objet2, **styles) elif case(Ligne_generique, (Cercle_generique, Arc_generique)): return Intersection_droite_cercle(objet1, objet2, **styles) elif case((Cercle_generique, Arc_generique), (Cercle_generique, Arc_generique)): return Intersection_cercles(objet1, objet2, **styles) else: raise TypeError, "Intersection d'objets non supportes." class Intersection_generique(Point_generique): u"""Une intersection gnrique. La classe mre des diffrents types d'intersections""" objet1 = __objet1 = Argument("Objet") objet2 = __objet2 = Argument("Objet") def __init__(self, objet1, objet2, **styles): self.__objet1 = objet1 = Ref(objet1) self.__objet2 = objet2 = Ref(objet2) Point_generique.__init__(self, **styles) def _intersections(self): return tuple(point for point in \ self._cache.get('intersections_possibles', self._intersections_possibles)\ if self._tester_solution(point)) @property def intersections(self): u"""Liste de coordonnes correspondant aux points d'intersections possibles. Le premier couple correspond aux coordonnes du point lui-mme. Attention, tous ces points ne sont pas forcment des points d'intersection. En effet, pour calculer ces intersections, on assimile les segments des droites, et les arcs des cercles. Il faut ensuite tester les solutions potentielles avec ._tester_solution(). """ return self._cache.get('intersections', self._intersections) @staticmethod def _tester_solution(solution, *ensembles): u"""Effectue des tests pour valider une solution potentielle. Plus prcisment, on vrifie que la solution appartient aux ensembles indiqus, mais seulement pour certains types d'ensemble: - segments et demi-droites, - arcs de cercle. Ils ne sont pas pratiqus pour les droites et les cercles. L'ide est la suivante : pour chercher l'intersection d'un segment et d'un arc de cercle, on cherche l'intersection entre la droite et le cercle qui les contiennent, et on teste les solutions trouves pour voir si elles appartiennent bien au segment et l'arc de cercle, l'aide de cette mthode. """ def appartient(sol, ens): if isinstance(sol, (Cercle_generique, Droite_generique)): # aucun test n'est pratiqu return True return sol in ens return all(appartient(solution, ensemble) for ensemble in ensembles) def _get_coordonnees(self): u"Ne pas appeler directement (erreurs possibles)." return self._cache.get('intersections_possibles', self._intersections_possibles)[0] def _conditions_existence(self): intersections = self._cache.get('intersections_possibles', self._intersections_possibles) if intersections: return self._tester_solution(intersections[0], self.__objet1, self.__objet2) return False class Intersection_droites(Intersection_generique): u"""Une intersection de droites. L'intersection de 2 droites, segments, demi-droites...""" droite1 = __droite1 = Argument("Ligne_generique") droite2 = __droite2 = Argument("Ligne_generique") def __init__(self, droite1, droite2, **styles): self.__droite1 = droite1 = Ref(droite1) self.__droite2 = droite2 = Ref(droite2) Intersection_generique.__init__(self, objet1 = droite1, objet2 = droite2, **styles) def _intersections_possibles(self): u"""Liste de coordonnes correspondant aux points d'intersections possibles. Le premier couple correspond aux coordonnes du point lui-mme. Attention, tous ces points ne sont pas forcment des points d'intersection. En effet, pour calculer ces intersections, on assimile les segments des droites, et les arcs des cercles. Il faut ensuite tester les solutions potentielles avec ._tester_solution(). """ a, b, c = self.__droite1.equation d, e, f = self.__droite2.equation determinant = a*e - b*d if abs(determinant) <= contexte['tolerance']: return () x = self.__x = (f*b - c*e)/determinant y = self.__y = (d*c - a*f)/determinant return (x, y), # liste (tuple) de couples class Intersection_droite_cercle(Intersection_generique): # ATTENTION, il y a des modifications faire avant de surclasser ! u"""Une intersection d'une droite et d'un cercle. Un des deux points M et N d'intersection d'une droite et d'un cercle. Supposons qu'on le note M, et que la droite soit (AB). L'argument premier_point (optionnel) sert diffrencier les deux points d'intersection. Il sert indiquer si M se trouve "prs" de A (1er point de (AB)) ou de B (2me point). (Plus prcisment, si premier_point == True, MA + NB est minimal). """ droite = __droite = Argument("Ligne_generique") cercle = __cercle = Argument("Cercle_generique, Arc_generique") premier_point = __premier_point = Argument("bool", defaut = True) def __init__(self, droite, cercle, premier_point = None, **styles): # l'intersection de la droite et du cercle (si elle existe) est deux points (ventuellement confondus). # lorsque l'utilisateur cr deux fois deux suite un point d'intersection, ce ne doit pas tre le mme. if isinstance(cercle, Ligne_generique): droite, cercle = cercle, droite self.__droite = droite = Ref(droite) self.__cercle = cercle = Ref(cercle) self.__premier_point = premier_point = Ref(premier_point) Intersection_generique.__init__(self, objet1 = droite, objet2 = cercle, **styles) def _set_feuille(self): if "_Intersection_droite_cercle__premier_point" in self._valeurs_par_defaut: for objet in self.__feuille__.objets.lister(type = Intersection_droite_cercle): if objet._Intersection_droite_cercle__droite is self.__droite and objet._Intersection_droite_cercle__cercle is self.__cercle: # une intersection existe dj, on va construire l'autre self.__premier_point = not objet._Intersection_droite_cercle__premier_point return Objet._set_feuille(self) def _intersections_possibles(self): u"""Liste de coordonnes correspondant aux points d'intersections possibles. Le premier couple correspond aux coordonnes du point lui-mme. Attention, tous ces points ne sont pas forcment des points d'intersection. En effet, pour calculer ces intersections, on assimile les segments des droites, et les arcs des cercles. Il faut ensuite tester les solutions potentielles avec ._tester_solution(). """ points_intersection = [] # Cas de l'intersection d'une tangente un cercle avec "son" cercle : # TODO: dtecter la tangence de manire gnrale if isinstance(self.__droite, Tangente) and self.__droite.cercle is self.__cercle: points_intersection = [self.__droite.point_tangence.coordonnees] else: a, b, c = self.__droite.equation d, e, f = self.__cercle.equation if abs(b) > contexte['tolerance']: # La droite n'est pas verticale m = -a/b; n = -c/b # equation de la droite : y = mx + n sols = racines(m**2 + 1, 2*n*m + d + e*m, n**2 + e*n + f, exact=contexte['exact']) points_intersection = [(x, m*x + n) for x in sols] elif abs(a) > contexte['tolerance']: # La droite est verticale x = -c/a sols = racines(1, e, x**2 + d*x + f, exact=contexte['exact']) points_intersection = [(x, y) for y in sols] # Le dernier cas correspond une droite d'quation ayant # des coefficients presque nuls => prcision insuffisante. if len(points_intersection) == 2: # La partie la plus complexe consiste distinguer # les deux points d'intersection l'aide du "point proche". # Pour comprendre ce qui s'y passe, il est conseill de faire un dessin ! if self.__premier_point: A = self.__droite.point1 B = self.__droite.point2 else: A = self.__droite.point2 B = self.__droite.point1 u = vect(A, B) M, N = points_intersection if produit_scalaire(u, vect(A, M)) > produit_scalaire(u, vect(A, N)): points_intersection.reverse() return points_intersection class Intersection_cercles(Intersection_generique): # ATTENTION, il y a des modifications faire avant de surclasser ! u"""Une intersection de cercles. Un des deux points M et N d'intersection des deux cercles. L'argument angle_positif permet de distinguer les deux points. Si on note A et B les centres respectifs des 2 cercles, l'angle orient (AB>, AM>) doit tre de signe constant. """ cercle1 = __cercle1 = Argument("Cercle_generique, Arc_generique") cercle2 = __cercle2 = Argument("Cercle_generique, Arc_generique") angle_positif = __angle_positif = Argument("bool", defaut = True) def __init__(self, cercle1, cercle2, angle_positif = None, **styles): self.__cercle1 = cercle1 = Ref(cercle1) self.__cercle2 = cercle2 = Ref(cercle2) self.__angle_positif = angle_positif = Ref(angle_positif) Intersection_generique.__init__(self, objet1 = cercle1, objet2 = cercle2, **styles) def _set_feuille_(self): # L'intersection de deux cercles (si elle existe) est deux points # (ventuellement confondus). # Lorsque l'utilisateur cre deux fois de suite un point d'intersection, # ce ne doit pas tre le mme qui est cr deux fois de suite. if "_Intersection_cercles__angle_positif" in self._valeurs_par_defaut: for objet in self.__feuille__.objets.lister(type = Intersection_cercles): if objet._Intersection_cercles__cercle1 is self.__cercle1 and objet._Intersection_cercles__cercle2 is self.__cercle2: # pour ne pas crer le mme point. self.__angle_positif = not objet._Intersection_cercles__angle_positif Objet._set_feuille(self) def _intersections_possibles(self): u"""Liste de coordonnes correspondant aux points d'intersections possibles. Le premier couple correspond aux coordonnes du point lui-mme. Attention, tous ces points ne sont pas forcment des points d'intersection. En effet, pour calculer ces intersections, on assimile les segments des droites, et les arcs des cercles. Il faut ensuite tester les solutions potentielles avec ._tester_solution(). """ points_intersection = [] a, b, c = self.cercle1.equation d, e, f = self.cercle2.equation if (a, b, c) != (d, e, f): # On soustrait membre membre les 2 quations de cercle ; # on obtient ainsi une quation de droite : # (a-d)x + (b-e)y + (c-f) = 0 # On se ramne donc au cas de l'intersection d'un cercle et d'une droite a, b, c = a - d, b - e, c - f if abs(b) > contexte['tolerance']: # La droite n'est pas verticale m = -a/b; n = -c/b # equation de la droite : y = mx + n sols = racines(m**2 + 1, 2*n*m + d + e*m, n**2 + e*n + f, exact=contexte['exact']) points_intersection = [(x, m*x+n) for x in sols] elif abs(a) > contexte['tolerance']: # La droite est verticale x = -c/a sols = racines(1, e, x**2 + d*x + f, exact=contexte['exact']) points_intersection = [(x, y) for y in sols] # Dernier cas possible : la droite n'existe pas (cercles concentriques). # Pas d'intersection (ou alors, c'est tout le cercle -> non gr). if len(points_intersection) == 2: # L encore, la partie la plus complexe consiste distinguer # les deux points d'intersection, l'aide cette fois du signe de l'angle. # Pour comprendre ce qui s'y passe, il est conseill de faire un dessin ! A = self.__cercle1.centre.coordonnees B = self.__cercle2.centre.coordonnees M, N = points_intersection a = angle_vectoriel(vect(A, B), vect(A, M)) if (a < 0 and self.__angle_positif) or (a > 0 and not self.__angle_positif): points_intersection.reverse() return points_intersection wxgeometrie-0.133.2.orig/wxgeometrie/geolib/contexte.py0000644000175000017500000000767012014170666023374 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Geolib # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .. import param class Contexte(dict): u"""Gestionnaire de contexte. Exemple d'usage: >>> from wxgeometrie.geolib.contexte import Contexte >>> # Contexte global >>> contexte = Contexte(exact = False, decimales = 7) >>> # Contexte local >>> with contexte(exact = True): ... print contexte['exact'] True >>> print contexte['exact'] False """ __slots__ = '__local_dicts', '__no_direct_call', '__parent' def __init__(self, parent=None, **kw): dict.__init__(self, **kw) self.__local_dicts = [] self.__parent = parent # Surveille que la méthode .__call__() ne soit jamais appelée directement self.__no_direct_call = True def __getitem__(self, key): # On cherche d'abord dans les contextes locaux (en commençant par le dernier) for dico in reversed(self.__local_dicts): if dico.has_key(key): return dico[key] if dict.has_key(self, key): return dict.__getitem__(self, key) elif self.__parent is not None: return self.__parent[key] raise KeyError, key def get(self, key, default=None): try: return self[key] except KeyError: return default def new(self): u"""Crée un nouveau contexte-fils, qui hérite de celui-ci. Toutes les clés non trouvées du contexte-fils sont ensuite cherchées dans le contexte père.""" return self.__class__(parent=self) def __call__(self, **kw): u"""Cette méthode ne doit *JAMAIS* être appelée en dehors d'un contexte 'with'. Exemple d'usage: >>> from wxgeometrie.geolib.contexte import Contexte >>> contexte = Contexte(exact = False, decimales = 7) >>> with contexte(exact = True): ... print contexte['exact'] True """ # On ajoute un contexte local self.__local_dicts.append(kw) # On surveille que la méthode .__call__() ne soit jamais appelée directement # Cela conduirait à des memory leaks (empilement de contextes locaux jamais effacés) assert self.__no_direct_call, "Utilisation precedente en dehors d'un contexte 'with'." self.__no_direct_call = False return self def __enter__(self): # La méthode __enter__() est appelée juste après la méthode __call__() self.__no_direct_call = True def __exit__(self, type, value, traceback): # On supprime le dernier contexte local self.__local_dicts.pop() contexte = Contexte(exact = True, decimales = param.decimales, unite_angle = param.unite_angle, tolerance = param.tolerance, afficher_messages = param.afficher_messages, ) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/textes.py0000644000175000017500000003214212014170666023047 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Texte # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode from random import uniform, normalvariate from math import cos, sin from .objet import Objet_avec_coordonnees, Argument, Ref, Objet, \ Objet_avec_coordonnees_modifiables from .constantes import RIEN, TEXTE from ..pylib import uu, warning from .. import param class Texte_generique(Objet_avec_coordonnees): u"""Un texte gnrique. La classe mre de tous les objets Texte. (Usage interne).""" _style_defaut = param.textes _prefixe_nom = "txt" def __init__(self, **styles): Objet_avec_coordonnees.__init__(self, **styles) def _creer_figure(self): if not self._representation: self._representation = [self.rendu.texte(), self.rendu.polygone()] text = self._representation[0] fill = self._representation[1] texte = (self.label() if self.label_temporaire is None else self.label_temporaire + '...') if not texte: text.set_visible(False) fill.set_visible(False) return else: text.set_visible(True) x, y = self.coordonnees fond = self.style("fond") niveau = self.style("niveau") av = self.style("alignement_vertical") ah = self.style("alignement_horizontal") if av not in ('center', 'top', 'bottom', 'baseline'): av = 'bottom' if ah not in ('center', 'right', 'left'): ah = 'left' text.set_x(x) text.set_y(y) text.set_color(self.style("couleur")) text.set_alpha(self.style('alpha')) font = text.get_fontproperties() font.set_size(self.style("taille")) #font.set_stretch(self.style("largeur")) # mal gere par matploltib (version 0.87) if param.latex: # formatage gr par LaTeX style = self.style("style") if style == "italic": texte = "\\textit{" + texte + "}" elif style == "oblique": texte = "\\textsl{" + texte + "}" if self.style("epaisseur") == "gras": texte = "\\textbf{" + texte + "}" famille = self.style("famille") if famille == "serif": texte = "\\rmfamily{" + texte + "}" elif famille == "monospace": texte = "\\ttfamily{" + texte + "}" # elif famille == "cursive": # texte = "\\mathscr{" + texte + "}" elif famille != "sans-serif": warning("Famille de police non disponible en mode LaTeX.") else: # formatage gr par matplotlib # font.set_weight(self.style("epaisseur") == "gras" and "bold" or "normal") font.set_weight(self.style("epaisseur") > 55 and "bold" or "normal") font.set_style(self.style("style")) font.set_family(self.style("famille")) text._text = texte text.set_rotation(self.style("angle")) text.set_verticalalignment(av) text.set_horizontalalignment(ah) text.zorder = niveau + .001 if fond is None: fill.set_visible(False) else: fill.set_visible(True) can = self.__canvas__ box = text.get_window_extent(can.get_renderer()) w, h = can.dpix2coo(.5*box.width, .5*box.height) if av == "left": x += w elif av == "right": x -= w if ah == "top": y -= h elif ah == "bottom": y += h mx, my = can.dpix2coo(2, 2) # marge verticale et horizontale (en pixels) w += mx h += my fill.xy = [(x - w, y - h), (x - w, y + h), (x + w, y + h), (x + w, y - h)] fill.set(facecolor=fond, edgecolor=fond) fill.zorder = niveau #debug(font.__dict__) def _boite(self): # Note : ymin et ymax "permutent" souvent car les transformations appliques inversent l'orientation. can = self.__canvas__ l, h = can.dimensions box = self.figure[0].get_window_extent(can.get_renderer()) xmin = box.xmin ymax = h - box.ymin xmax = box.xmax ymin = h - box.ymax return xmin, xmax, ymin, ymax def _espace_vital(self): # Note : ymin et ymax "permutent" souvent car les transformations appliques inversent l'orientation. if not self.label(): return can = self.__canvas__ _xmin, _xmax, _ymin, _ymax = self._boite() xmin, ymin = can.pix2coo(_xmin, _ymax) xmax, ymax = can.pix2coo(_xmax, _ymin) return xmin, xmax, ymin, ymax def _distance_inf(self, x, y, d): xmin, xmax, ymin, ymax = self._boite() return xmin - d < x < xmax + d and ymin - d < y < ymax + d class Texte(Texte_generique, Objet_avec_coordonnees_modifiables): u"""Un texte. Un texte afficher""" ## _style_defaut = param.textes ## _prefixe_nom = "t" def _set_texte(self, value): if isinstance(value, basestring) and value != "" and hasattr(self, "_style"): self.style(label = value) return value def _get_texte(self, value): return self.style("label") texte = __texte = Argument("basestring", _get_texte, _set_texte) abscisse = x = __x = Argument("Variable_generique", defaut = lambda: normalvariate(0,10)) ordonnee = y = __y = Argument("Variable_generique", defaut = lambda: normalvariate(0,10)) def __init__(self, texte = "", x = None, y = None, **styles): x, y, styles = self._recuperer_x_y(x, y, styles) texte = uu(texte) if texte != "": styles["label"] = texte self.__texte = texte = Ref(texte) self.__x = x = Ref(x) self.__y = y = Ref(y) Objet_avec_coordonnees_modifiables.__init__(self, x, y, **styles) def style(self, *args, **kw): if kw.get("legende") == RIEN: kw["legende"] = TEXTE kw["visible"] = False return Objet_avec_coordonnees_modifiables.style(self, *args, **kw) @staticmethod def _convertir(objet): if isinstance(objet, basestring): return Texte(objet) if hasattr(objet, "__iter__"): return Texte(*objet) raise TypeError, "object is not iterable." def _update(self, objet): if not isinstance(objet, Texte): if hasattr(objet, "__iter__") and len(objet) == 1 and isinstance(objet[0], basestring): self.label(objet[0]) return else: objet = self._convertir(objet) if isinstance(objet, Texte): self.label(objet.texte) self.coordonnees = objet.coordonnees else: raise TypeError, "l'objet n'est pas un texte." def _set_feuille(self): xmin, xmax, ymin, ymax = self.__feuille__.fenetre if "_Texte__x" in self._valeurs_par_defaut: self.__x = uniform(xmin, xmax) # self._valeurs_par_defaut.discard("_Point__x") if "_Texte__y" in self._valeurs_par_defaut: self.__y = uniform(ymin, ymax) # self._valeurs_par_defaut.discard("_Point__x") Objet._set_feuille(self) def _en_gras(self, booleen): figure = self.figure if figure: # La figure est vide si l'objet est masqu. if booleen: figure[0]._bbox = {'alpha': 0.5, 'linewidth': 1, 'fill': False} else: figure[0]._bbox = None def image_par(self, transformation): from .transformations import Rotation, Translation, Homothetie, Reflexion if isinstance(transformation, Rotation): return Texte_rotation(self, transformation) elif isinstance(transformation, Translation): return Texte_translation(self, transformation) elif isinstance(transformation, Homothetie): return Texte_homothetie(self, transformation) elif isinstance(transformation, Reflexion): return Texte_reflexion(self, transformation) raise NotImplementedError class Texte_transformation_generique(Texte_generique): u"""Une image d'un texte par transformation. Classe mre de toutes les images de textes par transformation. (Usage interne).""" texte = __texte = Argument("Texte_generique") transformation = __transformation = Argument("Transformation_generique") def __init__(self, texte, transformation, **styles): self.__texte = texte = Ref(texte) self.__transformation = transformation = Ref(transformation) Texte_generique.__init__(self, **styles) def style(self, *args, **kw): if "visible" in args: return self._style["visible"] return self.__texte.style(*args, **kw) class Texte_rotation(Texte_transformation_generique): u"""Une image d'un texte par rotation. Texte construit partir d'un autre via une rotation d'angle et de centre donn.""" texte = __texte = Argument("Texte_generique") rotation = __rotation = Argument("Rotation") def __init__(self, texte, rotation, **styles): self.__texte = texte = Ref(texte) self.__rotation = rotation = Ref(rotation) Texte_transformation_generique.__init__(self, texte, rotation, **styles) def _get_coordonnees(self): x0, y0 = self.__rotation.centre.coordonnees xA, yA = self.__texte.coordonnees a = self.__rotation.radian sina = sin(a) ; cosa = cos(a) return (-sina*(yA - y0) + x0 + cosa*(xA - x0), y0 + cosa*(yA - y0) + sina*(xA - x0)) ## def style(self, *args, **kw): ## if "angle" in args: ## return Texte_transformation_generique.style(self, "angle") + 180*self.__rotation._Rotation__angle/math.pi ## return Texte_transformation_generique.style(self, *args, **kw) class Texte_translation(Texte_transformation_generique): u"""Une image d'un texte par translation. Texte construit partir d'un autre via une translation d'angle et de centre donn.""" texte = __texte = Argument("Texte_generique") translation = __translation = Argument("Translation") def __init__(self, texte, translation, **styles): self.__texte = texte = Ref(texte) self.__translation = translation = Ref(translation) Texte_transformation_generique.__init__(self, texte, translation, **styles) def _get_coordonnees(self): return self.__texte.x + self.__translation._Translation__vecteur.x, self.__texte.y + self.__translation._Translation__vecteur.y class Texte_homothetie(Texte_transformation_generique): u"""Une image d'un texte par homothetie. Texte construit partir d'un autre via une homothetie d'angle et de centre donn.""" texte = __texte = Argument("Texte_generique") homothetie = __homothetie = Argument("Homothetie") def __init__(self, texte, homothetie, **styles): self.__texte = texte = Ref(texte) self.__homothetie = homothetie = Ref(homothetie) Texte_transformation_generique.__init__(self, texte, homothetie, **styles) def _get_coordonnees(self): x0, y0 = self.__homothetie.centre.coordonnees xA, yA = self.__texte.coordonnees k = self.__homothetie._Homothetie__rapport return x0 + k*(xA-x0), y0 + k*(yA-y0) class Texte_reflexion(Texte_transformation_generique): u"""Une image d'un texte par reflexion. Texte construit partir d'un autre via une reflexion d'angle et de centre donn.""" texte = __texte = Argument("Texte_generique") reflexion = __reflexion = Argument("Reflexion") def __init__(self, texte, reflexion, **styles): self.__texte = texte = Ref(texte) self.__reflexion = reflexion = Ref(reflexion) Texte_transformation_generique.__init__(self, texte, reflexion, **styles) def _get_coordonnees(self): x0, y0 = self.__reflexion._Reflexion__droite._Droite_generique__point1 x1, y1 = self.__reflexion._Reflexion__droite._Droite_generique__point2 x, y = self.__texte z = x1 - x0 + (y1 - y0)*1j M = (x - x0 + (y0 - y)*1j)*z/z.conjugate() + x0 + y0*1j return M.real, M.imag wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/0000755000175000017500000000000012014170666022321 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_fonctions.py0000644000175000017500000000227612014170666025743 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from pytest import XFAIL from tools.testlib import assertAlmostEqual from wxgeometrie.geolib import Fonction, Feuille _VAL0 = -5.156557933 @XFAIL def test_Fonction(): u"Test sans feuille." g = Fonction('2x+7') assert g(17) == 41 # La fonction n'est compile que s'il y a une feuille. # -> tester lorsque chaque objet aura un feuille par dfaut. def test_base(): f = Feuille() H = f.objets.H = Fonction("2*t**3+5", variable = 't') assertAlmostEqual(H(_VAL0), 2*_VAL0**3+5) def test_reecriture(): f = Feuille() H = f.objets.H = Fonction("2x^2+3x(1+x)", 'R') def h(x): return 2*x**2+3*x*(1+x) assertAlmostEqual(H(17), h(17)) assertAlmostEqual(H(_VAL0), h(_VAL0)) def test_variables(): f = Feuille() o = f.objets a = o.a = 3 b = o.b = 5 g = o.g = Fonction("a*x+b") assertAlmostEqual(g(4), a*4+b) assertAlmostEqual(g(_VAL0), a*_VAL0+b) def test_intervalle(): f = Feuille() o = f.objets g = o.g = Fonction('x^2+2x+1', ']0;5') assert g.style('extremites_cachees') == ([5],) assert g.ensemble == ']0;5[' wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_transformations.py0000644000175000017500000000351012014170666027162 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random from math import pi from tools.testlib import assertAlmostEqual, randint from wxgeometrie.geolib import (Point, Droite, Droite_equation, Reflexion, Rotation, Translation, Homothetie, Symetrie_centrale, Milieu, Mediatrice, Vecteur_libre ) def test_Rotation(): A = Point(1.523, 45.35211) r = Rotation(A, pi/4) M = Point(1.4452, -1.2545) assertAlmostEqual(r(M).coordonnees, (34.423837071540447, 12.341247113306926)) assertAlmostEqual(r(A).coordonnees, A.coordonnees) d = Droite(A, M) d1 = Droite(A, r(M)) assertAlmostEqual(r(d).equation_reduite, d1.equation_reduite) assert(r(A) is A) def test_Translation(): v = Vecteur_libre(random()+randint(50)-randint(50), random()+randint(50)-randint(50)) t = Translation(v) M = Point(random()+randint(50)-randint(50), random()+randint(50)-randint(50)) assertAlmostEqual(M.x + v.x, t(M).x) assertAlmostEqual(M.y + v.y, t(M).y) def test_Reflexion(): d = Droite_equation(1, 6, -2) r = Reflexion(d) M = Point(random()+randint(50)-randint(50), random()+randint(50)-randint(50)) m = Mediatrice(M, r(M)) assertAlmostEqual(d.equation_reduite, m.equation_reduite) assert(r(d) is d) def test_Homothetie(): A = Point(1, -2) h = Homothetie(A, -3) M = Point(2, 7) assertAlmostEqual(h(M).coordonnees, (-2, -29)) assert(h(A) is A) def test_Symetrie_centrale(): M = Point(random()+randint(50)-randint(50), random()+randint(50)-randint(50)) A = Point(random()+randint(50)-randint(50), random()+randint(50)-randint(50)) s = Symetrie_centrale(A) assertAlmostEqual(Milieu(M, s(M)).coordonnees, A.coordonnees) assert(s(A) is A) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_cercles.py0000644000175000017500000001072312014170666025355 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from math import sqrt, sin, cos from random import random from pytest import XFAIL from tools.testlib import assertAlmostEqual, assertEqual from wxgeometrie.geolib import (Cercle_points, Cercle_diametre, Cercle_rayon, Demicercle, Arc_oriente, Arc_points, Label_arc_cercle, Disque, Label_cercle, Rayon, Point, Cercle, Milieu, Segment, Mediatrice, Arc_cercle, Cercle_equation, ) def test_Arc_cercle(): A = Point(-1.2561, 45.236) B = Point(251.2561, 41.256) O = Point(-42.25, 2.351) a = Arc_cercle(O, A, B) assert(isinstance(a.etiquette, Label_arc_cercle)) assert(A in a) assert(B not in a) assert(O not in a) def test_Arc_points(): A = Point(-1.2561, 45.236) B = Point(251.2561, 41.256) C = Point(-42.25, 2.351) a = Arc_points(A, B, C) assert(A in a) assert(B in a) assert(C in a) assertAlmostEqual(a.longueur, 1208.25931027) assertAlmostEqual(a.centre.coordonnees, (122.51970100911581, -114.11725498641933)) def test_Arc_oriente(): A = Point(-1.2561, 45.236) B = Point(251.2561, 41.256) C = Point(-42.25, 2.351) a = Arc_oriente(A, B, C) assert(A in a) assert(B in a) assert(C in a) assertAlmostEqual(a.longueur, 1208.25931027) assertAlmostEqual(a.centre.coordonnees, (122.51970100911581, -114.11725498641933)) assert(a.sens == "indirect") def test_Demicercle(): A = Point(-1.2561, 45.236) B = Point(251.2561, 41.256) c = Demicercle(A, B) assertAlmostEqual(Milieu(A, B).coordonnees, c.centre.coordonnees) assertAlmostEqual(c.diametre, Segment(A, B).longueur) assertAlmostEqual(c.rayon, Segment(A, B).longueur/2) def test_Cercle_rayon(): A = Point(-2.586, 7.541) c_1 = Cercle_rayon(A, -1) c0 = Cercle_rayon(A, 0) c2 = Cercle_rayon(A, 2) assert(isinstance(c_1.etiquette, Label_cercle)) assert(not c_1.existe) assert(c0.existe) assert(c2.existe) assertAlmostEqual(A.coordonnees, c0.centre.coordonnees) assert(A not in c_1) assert(A in c0) assert(A not in c2) assertEqual(c_1.rayon, -1) assertEqual(c0.rayon, 0) assertEqual(c2.rayon, 2) k = random() B = Point(A.x + 2*sin(k), A.y + 2*cos(k)) assert(B in c2) @XFAIL def test_cercle_defini_par_equation(): c = Cercle("x^2+y^2-2x+y-3=0") assertAlmostEqual(c.centre.xy, (1, -.5)) assertAlmostEqual(c.rayon, sqrt(4.25)) def test_Cercle(): A = Point(2.78841, -5.25) O = Point(27.8841, -0.525) c = Cercle(O, A) assert(c.centre is O) assert(c.point is A) assertAlmostEqual(c.rayon, 25.5366262763) # Test du typage dynamique c = Cercle(Point(1, 1), 1) assert(Point(1, 2) in c) c = Cercle(Point(1, 1), Point(0, 2), Point(-1, 1)) assert(Point(0, 0) in c) assertAlmostEqual(c.centre.coordonnees, (0, 1)) def test_Cercle_diametre(): A = Point(2.78841, -5.25) B = Point(27.8841, -0.525) c = Cercle_diametre(A, B) c1 = Cercle(A, B) assertAlmostEqual(c.diametre, c1.rayon) assertAlmostEqual(c.diametre, Segment(A, B).longueur) assertAlmostEqual(c.centre.coordonnees, Milieu(A, B).coordonnees) def test_Cercle_points(): A = Point(2.78841, -5.25) B = Point(27.8841, -0.525) C = Point(-42.25, 2.351) c = Cercle_points(A, B, C) assert(c.centre in Mediatrice(Segment(A, B))) assert(c.centre in Mediatrice(Segment(A, C))) def test_Cercle_equation(): c = Cercle_equation() assertAlmostEqual(c.centre.coordonnees, (0, 0)) assert(c.rayon == 1) c.a = -2 c.b = 4 c.c = -4 assertAlmostEqual(c.centre.coordonnees, (1, -2)) assert(c.rayon == 3) assert(c.existe) c.c = 6 assert(not c.existe) def test_Disque(): A = Point(2.78841, -5.25) B = Point(27.8841, -0.525) C = Point(-42.25, 2.351) c = Cercle_points(A, B, C) d = Disque(c) assertAlmostEqual(d.centre.coordonnees, c.centre.coordonnees) assertAlmostEqual(d.rayon, c.rayon) assert(A in d) assert(B in d) assert(C in d) assert(d.centre in d) assert(Point(-500, -500) not in d) def test_equation_formatee(): assert Cercle((10, 0), (1, 5)).equation_formatee == u'x\xb2 + y\xb2 - 20 x - 6 = 0' def test_Rayon(): c = Cercle((1, 1), (4, 5)) r = Rayon(c) assertAlmostEqual(r.val, 5) c.point.x = 1 assertAlmostEqual(r.val, 4) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_variables.py0000644000175000017500000000351212014170666025703 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random from tools.testlib import assertAlmostEqual, assertEqual, randint, assertRaises from wxgeometrie.geolib import Variable, Formule from wxgeometrie.pylib import mathtext_parser def test_operations(): for i in xrange(10): a = randint(50) - randint(50) b = randint(50) - randint(50) if i >= 5: a += random() b += random() u = Variable(a) v = Variable(b) # Test oprations : if i < 5: assertEqual(u+v, a+b) assertEqual(u-v, a-b) assertEqual(u*v, a*b) if a !=0: assertEqual(u**v, a**b) if b != 0: assertEqual(u/v, a/b) assertEqual(u//v, a//b) assertEqual(u%v, a%b) else: assertAlmostEqual(u+v, a+b) assertAlmostEqual(u-v, a-b) assertAlmostEqual(u*v, a*b) if a != 0: assertAlmostEqual(abs(u)**v, abs(a)**b) # Test assignations : u += v a += b assertEqual(u, a) u *= v a *= b assertEqual(u, a) def test_erreurs_mathematiques(): u = Variable(randint(50) - randint(50)+random()) def diviser(x, y): return x/y assertRaises(ZeroDivisionError, diviser, u, 0) def puissance(x, y): return x**y assertRaises(OverflowError, puissance, Variable(25.17), 10000) def test_simple_compose(): assert(Variable(5)._type == "simple") assert(Variable("-5.3")._type == "simple") assert(Variable("a")._type == "compose") def test_parser_matplotlib(): c = Formule._caractere_erreur c_math = '$%s$' %c assert(mathtext_parser(c)) assert(mathtext_parser(c_math)) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_interpolations.py0000644000175000017500000000165212014170666027010 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) #from tools.testlib import assertAlmostEqual from wxgeometrie.geolib.tests.geotestlib import rand_pt from wxgeometrie.geolib import Interpolation_lineaire, Translation def test_Interpolation_lineaire(): # cas gnral : polygone 11 cts : A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() E = rand_pt() F = rand_pt() G = rand_pt() H = rand_pt() I = rand_pt() J = rand_pt() K = rand_pt() i0 = Interpolation_lineaire(A, B, C, D, E, F, G, H, I, J, K) # Test keyword "points" i1 = Interpolation_lineaire(points = (A, B, C, D, E, F, G, H, I, J, K)) assert(len(i1.points) == len(i0.points)) assert("points" not in i1.style()) t = Translation((1, -2)) i2 = t(i1) assert i2.points[0].xy == (A.x + 1, A.y - 2) assert i2.points[-1].xy == (K.x + 1, K.y - 2) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_lignes.py0000644000175000017500000001417512014170666025223 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from math import sqrt from random import random from tools.testlib import assertAlmostEqual, assertEqual, assertRaises, randint from wxgeometrie.geolib.tests.geotestlib import rand_pt from wxgeometrie.geolib import (Tangente, Perpendiculaire, Parallele, Mediatrice, Droite_vectorielle, Point, Cercle, Droite, Bissectrice, Label_droite, Label_demidroite, Label_segment, Droite_equation, Milieu, Segment, Barycentre, Vecteur_libre, RIEN, Demidroite, DemiPlan, ) def test_Segment(): A = Point(4.5, 7.3) B = Point(4, 2.1) s = Segment(A, B) assert(isinstance(s.etiquette, Label_segment)) assertAlmostEqual(s.longueur, sqrt((B.x - A.x)**2 + (B.y - A.y)**2)) I = Milieu(s.point1, s.point2) assertEqual(I.coordonnees, ((A.x+B.x)/2, (A.y+B.y)/2)) M = Barycentre((A, 1), (B, -2)) N = Barycentre((A, -2), (B, 1)) assert(I in s) assert(M not in s) assert(N not in s) assert(s.style("legende") == RIEN) def test_Demidroite(): A = Point(4.5, 7.3) B = Point(4, 2.1) s = Demidroite(A, B) assert(isinstance(s.etiquette, Label_demidroite)) assertRaises(AttributeError, getattr, s, "longueur") I = Milieu(s.origine, s.point) assertEqual(I.coordonnees, ((A.x+B.x)/2, (A.y+B.y)/2)) M = Barycentre((A, 1), (B, -2)) N = Barycentre((A, -2), (B, 1)) assert(I in s) assert(M in s) assert(N not in s) assert(s.style("legende") == RIEN) def test_Droite(): A = Point(4.5, 7.3) B = Point(4, 2.1) d = Droite(A, B) assert(isinstance(d.etiquette, Label_droite)) assertRaises(AttributeError, getattr, d, "longueur") I = Milieu(d.point1, d.point2) assertEqual(I.coordonnees, ((A.x+B.x)/2, (A.y+B.y)/2)) M = Barycentre((A, 1), (B, -2)) N = Barycentre((A, -2), (B, 1)) assert(I in d) assert(M in d) assert(N in d) assert(isinstance(d.equation, tuple)) assert(d.style("legende") == RIEN) # Test du typage dynamique d = Droite("y=x+1") assert(Point(0, 1) in d) d = Droite(Point(1, 2), Vecteur_libre(1, 1)) assert(Point(1, 2) in d) assert(Point(2, 3) in d) d2 = Droite("y=-x+1") assert(Point(0, 1) in d2) assert(Point(1, 0) in d2) def test_Droite_vectorielle(): v = Vecteur_libre(1, 7) A = Point(-2, 3) d = Droite_vectorielle(A, v) assert(d.vecteur is v and d.point is A) assertAlmostEqual(v.y/v.x, -d.equation[0]/d.equation[1]) B = rand_pt() d1 = Droite_vectorielle(B, v) assert(d.parallele(d1)) def test_Parallele(): d0 = Droite_equation(2, 1, 7) A = Point(-2, 3) d = Parallele(d0, A) assert(d.parallele(d0)) assert(d.droite is d0 and d.point is A) assertAlmostEqual(d0.equation[:1], d.equation[:1]) # def test_Droite_rotation(): # r = Rotation(Point(1.45, -2.59), math.pi/3) # C = Point(1.458, -5.255) # D = Point(3.478, -2.14788) # d = Droite(C, D) # # Dans ce qui suit, d1, d2 et d3 doivent correspondre la mme droite. # d1 = Droite_rotation(d, r) # d2 = Droite(r(C), r(D)) # d3 = r(d) # a, b, c = d1.equation # assertAlmostEqual(d1.equation_reduite, d2.equation_reduite) # assertAlmostEqual(d1.equation_reduite, d3.equation_reduite) # assertAlmostEqual(d1.equation_reduite, (-a/b, -c/b)) # d = Droite_rotation(Droite_equation(1, -1, 1), Rotation(Point(0, 0), math.pi/2)) # a, b, c = d.equation # assertAlmostEqual(b/a, 1) # assertAlmostEqual(c/a, 1) def test_Mediatrice(): A = Point(4.5, 7.3) B = Point(-4.147, 2.1) s = Segment(A, B) d0 = Mediatrice(s) d1 = Mediatrice(A, B) I = Milieu(A, B) assert(I in d0) assert(I in d1) a, b, c = s.equation a0, b0, c0 = d0.equation assertAlmostEqual(a*a0 + b*b0, 0) assertAlmostEqual(d0.equation, d1.equation) def test_Perpendiculaire(): d = Droite_equation(-1, 2, 0) M = Point() d0 = Perpendiculaire(d, M) a, b, c = d.equation a0, b0, c0 = d0.equation assert(d.perpendiculaire(d0)) assertAlmostEqual(a*a0 + b*b0, 0) assert(M in d0) def test_Droite_equation(): a = randint(50) - randint(50) + 0.1 # afin que a ne soit pas nul b = randint(50) - randint(50) + random() c = randint(50) - randint(50) + random() d, e, f = Droite_equation(a, b, c).equation assertAlmostEqual((e/d, f/d), (b/a, c/a)) assertEqual(Droite_equation(a, 0, 0).equation[1:], (0, 0)) assertEqual((Droite_equation(0, a, 0).equation[0], Droite_equation(0, a, 0).equation[2]), (0, 0)) assert(not Droite_equation(0, 0, 0).existe) d = Droite_equation("y=-5/2x-3/2") assert(Point(0, -1.5) in d) assert(Point(-1, 1) in d) d = Droite_equation("x=2*10**2") assert(Point(200, -1000) in d) assert(Point(100, -1000) not in d) d = Droite_equation("2*x+2*y=1") assert(Point(0.5, 0) in d) assert(Point(1, -0.5) in d) d = Droite_equation("x+y=1") assert(Point(0, 1) in d) assert(Point(1, 0) in d) d = Droite_equation("x+2y=-2") assert(Point(0, -1) in d) assert(Point(-2, 0) in d) def test_Bissectrice(): A = Point(1, -5) B = Point(1.5, -5.3) C = Point(3, -4) d = Bissectrice(A, B, C) a, b, c = d.equation d, e = (0.0870545184921, -1.03861105199) assertAlmostEqual(b/a, d) assertAlmostEqual(c/a, e) def test_Tangente(): A = Point(4.75, -2.56887) O = Point(2.56874, -85.2541) M = Point(7.854, -552.444) c = Cercle(O, A) d = Tangente(c, A) assert(A in d) assert(M not in d) d1 = Tangente(c, M) assert(M in d1) assert(A not in d1) assert(not Tangente(c, O).existe) def test_equation_formatee(): assert Droite('y=x').equation_formatee == '-x + y = 0' def test_DemiPlan(): d = Droite('y=-x+1') P1 = DemiPlan(d, Point(0, 0), True) P2 = DemiPlan(d, Point(0, 0), False) assert Point(0, 0) in P1 assert Point(1, 0) in P1 assert Point(0, 0) in P2 assert Point(1, 0) not in P2 wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/geotestlib.py0000644000175000017500000000173512014170666025042 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from tools.testlib import rand #from wxgeometrie.geolib import (Point, Segment, Droite, Cercle, Vecteur, Variable, Polygone, # Vecteur_libre, Droite_equation, Cercle_equation, Angle, Mediatrice, # Arc_cercle, Demidroite, Milieu, Carre, Rectangle, Barycentre, # Reflexion, Homothetie, Rotation, Translation, Representant, # Triangle, Feuille, Fonction, Parallelogramme, contexte, Texte, # RIEN, TEXTE, FORMULE, NOM) from wxgeometrie.geolib import Point, Vecteur_libre, Droite_equation, Cercle_equation def rand_pt(): return Point(rand(), rand()) def rand_vec(): return Vecteur_libre(rand(), rand()) def rand_dte(): return Droite_equation(rand(), rand(), rand()) def rand_cercle(): a = rand() b = rand() c = (a**2+b**2-abs(rand()))/4 return Cercle_equation(a, b, c) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_points.py0000644000175000017500000002532512014170666025255 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random from sympy import sympify as symp from tools.testlib import assertAlmostEqual, assertEqual, randint from wxgeometrie.geolib.tests.geotestlib import rand_pt, rand_cercle from wxgeometrie.geolib import (Glisseur_arc_cercle, Glisseur_cercle, Glisseur_demidroite, Glisseur_droite, Point, Demidroite, Droite, Glisseur_segment, Point_equidistant, Centre, Centre_cercle_inscrit, Centre_cercle_circonscrit, Orthocentre, Projete_demidroite, Projete_segment, Projete_arc_cercle, Projete_cercle, Segment, Projete_droite, Centre_gravite, Label_point, Point_reflexion, Point_homothetie, Cercle, Point_rotation, Point_translation, Point_final, Arc_points, Arc_cercle, Triangle, Milieu, Barycentre, Mediatrice, Droite_equation, Cercle_equation, Polygone, Rotation, Translation, Vecteur, Vecteur_libre, Representant, Reflexion, Homothetie, NOM, ) def test_Point(): A = Point(1, 2) assert(isinstance(A.etiquette, Label_point)) assertEqual(A.x, 1) assertEqual(A.y, 2) assertEqual(type(A.coordonnees), tuple) A.x = 5 A.y = 7 assertEqual(A.coordonnees, (5, 7)) assert(A.style("legende") == NOM) # Test du typage dynamique d = Droite(rand_pt(), rand_pt()) B = Point(d) assert(isinstance(B, Glisseur_droite)) c = Cercle(A, 3) C = Point(c) assert(isinstance(C, Glisseur_cercle)) d = Demidroite(rand_pt(), rand_pt()) B = Point(d) assert(isinstance(B, Glisseur_demidroite)) s = Segment(rand_pt(), rand_pt()) B = Point(s) assert(isinstance(B, Glisseur_segment)) a = Arc_points(Point(), Point(1, 1), Point(1, 2)) B = Point(a) assert(isinstance(B, Glisseur_arc_cercle)) def test_Point_sympy(): A = Point('1/2', '2/3') assertEqual(A.x, symp('1/2')) assertAlmostEqual(A.coordonnees, (1/2, 2/3)) def test_Milieu(): A = Point(1,2) B = Point(2,4) I = Milieu(A,B) assert(I.x == (A.x+B.x)/2 and I.y == (A.y+B.y)/2) assertEqual(type(I.coordonnees), tuple) C = Point('1/3', '1') D = Point('1', '1') J = Milieu(C, D) assert(J.x == symp('2/3')) def test_Barycentre(): M = Point(3, 4) A = Point(1,2) B = Point(2,4) I = Milieu(A,B) K = Barycentre(M, A, B, (I, -4)) assertEqual(K.x, 0) assertEqual(K.y, 2) assert(not Barycentre(M, A, (B, -2)).existe) assertEqual(type(K.coordonnees), tuple) # Test keyword 'points_ponderes' G = Barycentre(points_ponderes = (M, A, B, (I, -4))) assert(K.coordonnees == G.coordonnees) assert("point_ponderes" not in G.style()) def test_Point_final(): A = Point(random(),random()) C = Point(random(),random()) u = Vecteur_libre(random(),random()) v = Vecteur(Point(random(), random()), Point(random(), random())) w = Representant(Vecteur(Point(random(), random()), Point(random(), random())), A) F = Point_final(C, (u, v, w), (-2, 1.5, 4)) assertEqual(type(F.coordonnees), tuple) assertAlmostEqual(F.x, C.x -2*u.x+1.5*v.x+4*w.x) assertAlmostEqual(F.y, C.y -2*u.y+1.5*v.y+4*w.y) def test_Point_translation(): u = Vecteur_libre(1.56, 2.78) v = Vecteur(Point(1.1457, 2.7895), Point(2.458, -8.25)) A = Point(5.256, -7.231) tu = Translation(u) tv = Translation(v) Au = Point_translation(A, tu) Av = Point_translation(A, tv) assertAlmostEqual(Au.coordonnees, (6.816, -4.451)) assertAlmostEqual(Av.coordonnees, (6.5683, -18.2705)) def test_Point_rotation(): A = Point(1, -4) r = Rotation(Point(-2, 7.56), 4.25) B = Point_rotation(A, r) assertAlmostEqual(r(A).coordonnees, B.coordonnees) assertAlmostEqual(B.coordonnees, (-13.684339450863803, 10.031803308717693)) A = Point('1', '0') r = Rotation(('0', '0'), 'pi/4') B = Point_rotation(A, r) C = Point('sqrt(2)/2', 'sqrt(2)/2') assert(C.x == B.x and C.y == B.y) def test_Point_homothetie(): A = Point(1, -2) h = Homothetie(A, -3) M = Point(2, 7) assertAlmostEqual(Point_homothetie(M, h).coordonnees, (-2, -29)) # assert(Point_homothetie(A, h) is A) -> mthode __new__ ? def test_Point_reflexion(): d = Droite_equation(1, 6, -2) r = Reflexion(d) M = Point(random()+randint(50)-randint(50), random()+randint(50)-randint(50)) m = Mediatrice(M, Point_reflexion(M, r)) assertAlmostEqual(d.equation_reduite, m.equation_reduite) def test_Projete_droite(): d = Droite_equation(1.25, -7.12, 2.15) A = Point(1.52, 2.14) M = Projete_droite(A, d) assert(M in d) assert(Droite(A, M)._perpendiculaire(d)) def test_Projete_cercle(): c = Cercle_equation(1, -2, -20) assert(c.existe) A = Point(randint(50) - randint(50) + random(), randint(50) - randint(50) + random()) P = Projete_cercle(A, c) assert(P.existe) assert(P in c) assert(P in Demidroite(c.centre, A)) A.coordonnees = c.centre.coordonnees assert(not P.existe) def test_Projete_arc_cercle(): O = Point(23.15, -12.75) A = Point(-12.5, 7.14) B = Point(7.15, 8.64) a = Arc_cercle(O, A, B) M = rand_pt() P = Projete_arc_cercle(M, a) assert(P in a) M.coordonnees = a.centre.coordonnees assert(not P.existe) M.coordonnees = -17.826266675199999, 11.760911186 assert(type(P.coordonnees) is tuple) assertAlmostEqual(A.coordonnees, P.coordonnees) assertEqual(A, P) def test_Projete_segment(): s = Segment(rand_pt(), rand_pt()) M = rand_pt() assert(Projete_segment(M, s) in s) A = Point(0, 1) B = Point(2, 1) s = Segment(A, B) M = Point(1, 7.15) P = Projete_segment(M, s) assertAlmostEqual(Milieu(A, B).coordonnees, P.coordonnees) M.x = .5 assertAlmostEqual(Barycentre((A, 3), (B, 1)).coordonnees, P.coordonnees) M.x = -1 assertAlmostEqual(A.coordonnees, P.coordonnees) M.x = 3 assertAlmostEqual(B.coordonnees, P.coordonnees) def test_Projete_demidroite(): d = Demidroite(rand_pt(), rand_pt()) M = rand_pt() assert(Projete_demidroite(M, d) in d) A = Point(0, 1) B = Point(2, 1) s = Demidroite(A, B) M = Point(1, 7.15) P = Projete_demidroite(M, s) assertAlmostEqual(Milieu(A, B).coordonnees, P.coordonnees) M.x = .5 assertAlmostEqual(Barycentre((A, 3), (B, 1)).coordonnees, P.coordonnees) M.x = -1 assertAlmostEqual(A.coordonnees, P.coordonnees) M.x = 3 assertAlmostEqual(Barycentre((A, -1), (B, 3)).coordonnees, P.coordonnees) def test_Centre_gravite(): A = rand_pt() B = rand_pt() C = rand_pt() I = Milieu(B, C) J = Milieu(A, C) K = Milieu(A, B) G = Centre_gravite(Triangle(A, B, C)) assertAlmostEqual(Segment(A, G).longueur, 2*Segment(I, G).longueur) assertAlmostEqual(Segment(B, G).longueur, 2*Segment(J, G).longueur) assertAlmostEqual(Segment(C, G).longueur, 2*Segment(K, G).longueur) def test_Orthocentre(): A, B, C = rand_pt(), rand_pt(), rand_pt() p = Polygone(A, B, C) H = Orthocentre(p) assert(Droite(A, H).perpendiculaire(Droite(B, C))) assert(Droite(B, H).perpendiculaire(Droite(A, C))) assert(Droite(C, H).perpendiculaire(Droite(B, A))) def test_Centre_cercle_circonscrit(): A, B, C = rand_pt(), rand_pt(), rand_pt() p = Polygone(A, B, C) O= Centre_cercle_circonscrit(p) assert(O == Point_equidistant(A, B, C)) def test_Centre_cercle_inscrit(): A, B, C = rand_pt(), rand_pt(), rand_pt() p = Polygone(A, B, C) I = Centre_cercle_inscrit(p) P = Projete_segment(I, Segment(B, C)) Q = Projete_segment(I, Segment(A, C)) R = Projete_segment(I, Segment(A, B)) c = Cercle(I, P) assert(P in c and Q in c and R in c) def test_Centre(): c = rand_cercle() O = Centre(c) M = Glisseur_cercle(c) assertAlmostEqual(Segment(O, M).longueur, c.rayon) def test_Point_equidistant(): A = rand_pt() B = rand_pt() C = rand_pt() P = Point_equidistant(A, B, C) assertAlmostEqual(Segment(A, P).longueur, Segment(B, P).longueur) assertAlmostEqual(Segment(A, P).longueur, Segment(C, P).longueur) assert(P in Mediatrice(A, B)) def test_Glisseur_droite(): A = rand_pt() B = rand_pt() d = Droite(A, B) M = Glisseur_droite(d) assert(M in d) M.k = 0 assertEqual(M.k, 0) assertAlmostEqual(M.coordonnees, A.coordonnees) P = Point(*M.coordonnees) M.k = 1 assertEqual(M.k, 1) assertAlmostEqual(M.coordonnees, B.coordonnees) M.k = 2 assertEqual(M.k, 2) M.k = -1 assertEqual(M.k, -1) Q = Point(*M.coordonnees) assertAlmostEqual(Droite(P, Q).equation_reduite, d.equation_reduite) M.k = 1.7 M(*M.coordonnees) assertAlmostEqual(M.k, 1.7) def test_Glisseur_segment(): A = rand_pt() B = rand_pt() s = Segment(A, B) M = Glisseur_segment(s) assert(M in s) M.k = 0 assertEqual(M.k, 0) assertAlmostEqual(M.coordonnees, A.coordonnees) P = Point(*M.coordonnees) M.k = 1 assertEqual(M.k, 1) assertAlmostEqual(M.coordonnees, B.coordonnees) M.k = 2 assertEqual(M.k, 1) # 0<=k<=1 pour un segment assert(M in s) M.k = -1 assertEqual(M.k, 0) # 0<=k<=1 pour un segment assert(M in s) M.k = 1 Q = Point(*M.coordonnees) assertAlmostEqual(Droite(P, Q).equation_reduite, s.equation_reduite) M.k = 1.7 M(*M.coordonnees) assertAlmostEqual(M.k, 1) def test_Glisseur_demidroite(): A = rand_pt() B = rand_pt() d = Demidroite(A, B) M = Glisseur_demidroite(d) assert(M in d) M.k = 0 assertEqual(M.k, 0) assertAlmostEqual(M.coordonnees, A.coordonnees) P = Point(*M.coordonnees) M.k = 1 assertEqual(M.k, 1) assertAlmostEqual(M.coordonnees, B.coordonnees) M.k = 2 assertEqual(M.k, 2) M.k = -1 assertEqual(M.k, 0) # k>=0 pour une demi-droite M.k = 1 Q = Point(*M.coordonnees) assertAlmostEqual(Droite(P, Q).equation_reduite, d.equation_reduite) M.k = 1.7 M(*M.coordonnees) assertAlmostEqual(M.k, 1.7) def test_Glisseur_cercle(): c = rand_cercle() M = Glisseur_cercle(c) assert(M in c) O = c.centre M.coordonnees = O.coordonnees # il faudrait complter un peu def test_Glisseur_arc_cercle(): A = rand_pt() B = rand_pt() C = rand_pt() a = Arc_cercle(A, B, C) M = Glisseur_arc_cercle(a) assert(M in a) O = a.centre M.coordonnees = O.coordonnees # il faudrait complter un peu wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_polygones.py0000644000175000017500000002233612014170666025757 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division, absolute_import # 1/2 == .5 (par defaut, 1/2 == 0) from math import pi, sin, cos from random import random from tools.testlib import assertAlmostEqual, assertEqual, randint from wxgeometrie.geolib.tests.geotestlib import rand_pt from wxgeometrie.geolib import (Point, Polygone, Milieu, Label_polygone, Barycentre, Droite, Triangle, Angle, Vecteur, Quadrilatere, Pentagone, Hexagone, Heptagone, Octogone, Segment, Parallelogramme, Triangle_isocele, Triangle_equilateral, Rectangle, Carre, Polygone_regulier, Carre_centre, Triangle_equilateral_centre, Polygone_regulier_centre, Losange, Mediatrice, Triangle_isocele_rectangle, Triangle_rectangle, contexte, ) # def test_Cote(): # pass # CRIRE # # def test_Sommet(): # pass # CRIRE def test_Polygone(): # cas gnral : polygone 11 cts : A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() E = rand_pt() F = rand_pt() G = rand_pt() H = rand_pt() I = rand_pt() J = rand_pt() K = rand_pt() p = p0 = Polygone(A, B, C, D, E, F, G, H, I, J, K) assert(isinstance(p.etiquette, Label_polygone)) assert(p.sommets[0] == A and p.sommets[10] == K) assert(Milieu(B, C) in p.cotes[1]) assertAlmostEqual(p.centre.coordonnees, Barycentre(A, B, C, D, E, F, G, H, I, J, K).coordonnees) # cas particuliers : t = Polygone(A, B, C) O = t.centre_cercle_circonscrit assertAlmostEqual(Segment(O, A).longueur, Segment(O, C).longueur) assert(Droite(t.orthocentre, C).perpendiculaire(Droite(A, B))) assert(t.__class__ is Triangle) p = Polygone(A, B, C, D) assert(p.__class__ is Quadrilatere) p = Polygone(A, B, C, D, E) assert(p.__class__ is Pentagone) p = Polygone(A, B, C, D, E, F) assert(p.__class__ is Hexagone) p = Polygone(A, B, C, D, E, F, G) assert(p.__class__ is Heptagone) p = Polygone(A, B, C, D, E, F, G, H) assert(p.__class__ is Octogone) assert(p._hierarchie < p.sommets[0]._hierarchie < p.sommets[1]._hierarchie < p.sommets[7]._hierarchie < p._hierarchie + 1) # Test keyword 'points' p = Polygone(points = (A, B, C, D, E, F, G, H, I, J, K)) assert(p.centre.coordonnees == p0.centre.coordonnees) assert("points" not in p.style()) # Syntaxe spciale : Polygone cr sans arguments, ou avec un entier comme argument. p = Polygone() p = Polygone(2) assert(isinstance(p, Segment)) p = Polygone(3) assert(isinstance(p, Triangle)) p = Polygone(n = 5) assert(isinstance(p, Pentagone)) p = Polygone(n = 12) assert(len(p.points) == 12) p = Polygone(23) assert(len(p.points) == 23) assert(len(str(p.points)) < 30000) def test_Triangle(): A = rand_pt() B = rand_pt() C = rand_pt() t = Triangle(A, B, C) O = t.centre_cercle_circonscrit assertAlmostEqual(Segment(O, A).longueur, Segment(O, C).longueur) def test_Quadrilatere(): A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() p = Quadrilatere(A, B, C, D) assertAlmostEqual(p.centre.coordonnees, Barycentre(A, B, C, D).coordonnees) def test_Pentagone(): A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() E = rand_pt() p = Pentagone(A, B, C, D, E) assertAlmostEqual(p.centre.coordonnees, Barycentre(A, B, C, D, E).coordonnees) def test_Hexagone(): A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() E = rand_pt() F = rand_pt() p = Hexagone(A, B, C, D, E, F) assertAlmostEqual(p.centre.coordonnees, Barycentre(A, B, C, D, E, F).coordonnees) def test_Heptagone(): A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() E = rand_pt() F = rand_pt() G = rand_pt() p = Heptagone(A, B, C, D, E, F, G) assertAlmostEqual(p.centre.coordonnees, Barycentre(A, B, C, D, E, F, G).coordonnees) def test_Octogone(): A = rand_pt() B = rand_pt() C = rand_pt() D = rand_pt() E = rand_pt() F = rand_pt() G = rand_pt() H = rand_pt() p = Octogone(A, B, C, D, E, F, G, H) assertAlmostEqual(p.centre.coordonnees, Barycentre(A, B, C, D, E, F, G, H).coordonnees) def test_Parallelogramme(): A = rand_pt() B = rand_pt() C = rand_pt() p = Parallelogramme(A, B, C) D = p.sommets[3] assertEqual(Vecteur(A, B), Vecteur(D, C)) def test_Rectangle(): A = rand_pt() B = rand_pt() r = Rectangle(A, B) M, N, O, P = r.sommets diagonale1 = Segment(M, O) diagonale2 = Segment(N, P) assertAlmostEqual(diagonale1.longueur, diagonale2.longueur) cote = Droite(M, N) cote_oppose = Droite(O, P) assert(cote.parallele(cote_oppose)) def test_Losange(): A = rand_pt() B = rand_pt() l = Losange(A, B) M, N, O, P = l.sommets diagonale1 = Droite(M, O) diagonale2 = Droite(N, P) assert(diagonale1.perpendiculaire(diagonale2)) cote = Droite(M, N) cote_oppose = Droite(O, P) assert(cote.parallele(cote_oppose)) def test_Polygone_regulier_centre(): O = rand_pt() M = rand_pt() p = Polygone_regulier_centre(O, M, 15) assert(len(p.cotes) == 15) assert(p.centre is O) for Mi in p.sommets: assertAlmostEqual(Segment(O, Mi).longueur, Segment(O, M).longueur) for i in xrange(10): coeffs = tuple(random() for i in xrange(15)) G = Barycentre(*zip(p.sommets, coeffs)) assert(G in p) G.points_ponderes[randint(11)].coefficient = -5 assert(G not in p) # cas particuliers : p = Polygone_regulier_centre(O, M, 3) assert(isinstance(p, Triangle)) p = Polygone_regulier_centre(O, M, 4) assert(isinstance(p, Quadrilatere)) assert(len(str(p.points)) < 30000) def test_Triangle_equilateral_centre(): O = rand_pt() M = rand_pt() p = Triangle_equilateral_centre(O, M) assert(p.centre.existe and p.centre is O) assert(p.centre_cercle_circonscrit.existe) assert(p.centre_cercle_inscrit.existe) assert(p.orthocentre.existe) assert(p.orthocentre == p.centre == p.centre_cercle_circonscrit == p.centre_cercle_inscrit) def test_Triangle_isocele_rectangle(): t = Triangle_isocele_rectangle((0, 0), (1, 1)) assertAlmostEqual(t.point3.xy, (0, 1)) assertAlmostEqual(t.aire, .5) def test_Carre_centre(): O = rand_pt() M = rand_pt() p = Carre_centre(O, M) assert(p.centre.existe and p.centre is O) assert(len(p.cotes) == 4) assertAlmostEqual(p.aire, p.cotes[0].longueur**2) def test_Polygone_regulier(): O = rand_pt() M = rand_pt() p = Polygone_regulier(O, M, 15) assert(len(p.cotes) == 15) assert(p.centre in Mediatrice(O, M)) for i in xrange(15): assertAlmostEqual(Segment(p.sommets[i%15], p.sommets[(i+1)%15]).longueur, Segment(p.sommets[(i+2)%15], p.sommets[(i+3)%15]).longueur) p = Polygone_regulier(O, M, 3) assert(isinstance(p, Triangle)) p = Polygone_regulier(O, M, 4) assert(isinstance(p, Quadrilatere)) # Test de rgression : # la taille de str(p.points) croissait exponentiellement. assert(len(str(p.points)) < 30000) def test_Triangle_equilateral(): O = rand_pt() M = rand_pt() p = Triangle_equilateral(O, M) assert(p.centre.existe) assert(p.centre_cercle_circonscrit.existe) assert(p.centre_cercle_inscrit.existe) assert(p.orthocentre.existe) assert(p.orthocentre == p.centre == p.centre_cercle_circonscrit == p.centre_cercle_inscrit) def test_Carre(): O = rand_pt() M = rand_pt() p = Carre(O, M) A, B, C, D = p.sommets assert(p.centre == Milieu(A, C) == Milieu(B, D)) assert(A == O and B == M) assert(len(p.cotes) == 4) assertAlmostEqual(p.aire, p.cotes[0].longueur**2) # Test redfinition d'un sommet c = Carre((3, 2), (7, 2)) M = Point(0, 2) c.point1 = M assert(c.point1 is M) assert(c.regulier) assertAlmostEqual(c.aire, 49) assertAlmostEqual(c.point4.coordonnees, (0, 9)) def test_Triangle_isocele(): A = rand_pt() B = rand_pt() tri = Triangle_isocele(A, B, 2*pi/13) C = tri.point3 a = Angle(B, A, C) assertAlmostEqual(a.radian, 2*pi/13) assertAlmostEqual(Segment(A, B).longueur, Segment(A, C).longueur) t1 = Triangle_isocele((0, 0), (1, 1), u'90') assertAlmostEqual(t1.point3.xy, (-1, 1)) t2 = Triangle_isocele((0, 0), (2, 0), pi/3) assertAlmostEqual(t2.point3.xy, (2*cos(pi/3), 2*sin(pi/3))) with contexte(unite_angle='d'): t2.point3.xy = (0, 9) assertAlmostEqual(t2.point3.xy, (0, 2)) with contexte(unite_angle='r'): t2.point3.xy = (0, 9) assertAlmostEqual(t2.point3.xy, (0, 2)) def test_Triangle_rectangle(): t = Triangle_rectangle(rand_pt(), rand_pt(), pi/7) a = Angle(t.point1, t.point3, t.point2) assertAlmostEqual(a.degre, 90) def test_issue_215(): # Quand les angles sont en degr, les valeurs par dfaut des triangles isocles sont incorrectes with contexte(unite_angle='d'): for i in range(10): t = Triangle_isocele() assert abs(t.angle.rad) > pi/6 wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_meta.py0000644000175000017500000001621612014170666024666 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) import re from os import walk, listdir from os.path import isfile, join from pytest import XFAIL from tools.testlib import WXGEODIR from wxgeometrie.geolib import G, objet def lister_classes(): classes = set(key.rsplit(".")[-1] for key, value in G.__dict__.iteritems() if type(value) is type and issubclass(value, G.Objet) and not key.endswith("_generique") ) classes_de_base = set(key.rsplit(".")[-1] for key, value in objet.__dict__.iteritems() if type(value) is type and issubclass(value, G.Objet) ) classes.difference_update(classes_de_base) return classes def test_toutes_classes(): u"On vrifie que toutes les classes de geolib soient bien testes." classes = lister_classes() classes_testees = set() path = join(WXGEODIR, 'geolib', 'tests') for name in listdir(path): if name.endswith('.py'): with open(join(path, name)) as f: for line in f: line = line.strip() if line.startswith('def test_'): classes_testees.add(line[9:-3]) assert 'Carre' in classes_testees, str(classes_testees) #TODO: tester ces classes restantes. skip_test = set(['Texte_translation', 'Tangente_courbe', 'Texte_rotation', 'Extremite', 'Point_tangence', 'NuageFonction', 'Mul', 'Cube', 'Cote', 'Sommet_triangle_isocele', 'Axe', 'Courbe', 'Sommet', 'Glisseur_vecteur', 'Add', 'Arete', 'Variable', 'Texte_homothetie', 'Label_vecteur', 'Sommet_polyedre', 'Sommet_rectangle', 'Nuage', 'Point_pondere', 'Sommet_triangle_rectangle', 'PrevisualisationPolygone', 'Texte_reflexion', 'Point_droite', 'Sommet_cube', 'Tetraedre']) non_testees = classes.difference(classes_testees, skip_test) if non_testees: print("\n" + 58*"-" + u"\nErreur: Certaines classes de `geolib` ne sont pas testes") print(' * ' + '\n * '.join(non_testees) + "\n" + 58*"-" + "\n") assert not non_testees a_maj = skip_test.intersection(classes_testees) if a_maj: print("\n" + 47*"-" + u"\nErreur: `skip_test` n'est pas jour.") print(u'Ces classes sont dsormais testes:') print(' * ' + '\n * '.join(a_maj) + "\n" + 47*"-" + "\n") assert not a_maj a_suppr = skip_test.difference(classes) if a_suppr: print("\n" + 47*"-" + u"\nErreur: `skip_test` n'est pas jour.") print(u"Ces classes n'existent plus:") print(' * ' + '\n * '.join(a_suppr) + "\n" + 47*"-" + "\n") assert not a_suppr def assert_heritage(classe, classe_parente): test = issubclass(classe, classe_parente) if not test: raise TypeError, "ERREUR: la classe %s N'herite PAS de %s" %(classe, classe_parente) def assert_not_heritage(classe, classe_parente): test = issubclass(classe, classe_parente) if test: raise TypeError, "ERREUR: la classe %s herite de %s" %(classe, classe_parente) def test_heritages(): u"""On vrifie que les objets ont une mthode '_get_coordonnees' ssi ils descendent de la classe 'Objet_avec_coordonnees'. De mme, les objets ont une mthode '_get_equation' et '_get_val' ssi ils descendent respectivement des classes 'Objet_avec_equation' et 'Objet_avec_valeur'.""" for classe in G.__dict__.itervalues(): if isinstance(classe, type) and issubclass(classe, G.Objet): # print classe if hasattr(classe, "_get_equation"): assert_heritage(classe, G.Objet_avec_equation) # assert("exact" in classe._get_equation.func_code.co_varnames) else: assert_not_heritage(classe, G.Objet_avec_equation) if hasattr(classe, "_get_coordonnees"): assert_heritage(classe, G.Objet_avec_coordonnees) # assert("exact" in classe._get_coordonnees.func_code.co_varnames) else: assert_not_heritage(classe, G.Objet_avec_coordonnees) if hasattr(classe, "_get_valeur"): assert_heritage(classe, G.Objet_avec_valeur) # assert("exact" in classe._get_valeur.func_code.co_varnames) else: assert_not_heritage(classe, G.Objet_avec_valeur) @XFAIL def test_methode_image_par(): classes = lister_classes() non_transformable = (G.Variable, G.Label_generique, G.Angle_libre, G.Angle_vectoriel, G.Point_pondere, G.Vecteur_libre, G.Vecteur_unitaire, G.Somme_vecteurs, G.Transformation_generique) non_transformable_actuellement = (G.Widget, G.Courbe, G.Interpolation_generique, G.Fonction, G.Texte) for classe in G.__dict__.itervalues(): if (isinstance(classe, type) and issubclass(classe, G.Objet) and classe.__name__.rsplit(".")[-1] in classes and not hasattr(classe, "image_par")): if not issubclass(classe, non_transformable) and not issubclass(classe, non_transformable_actuellement): raise AttributeError, "ATTENTION: " + str(classe) + " n'a pas d'attribut 'image_par' !" def test_arguments(): u"""On vrifie que l'attribut '.nom' des arguments correspondent bien leur noms rels. Le nom doit tre de la forme '_nomClasse__nomArgument'.""" for classe in G.__dict__.itervalues(): if isinstance(classe, type) and issubclass(classe, G.Objet): for key, value in vars(classe).iteritems(): if isinstance(value, G.BaseArgument) and key[0] == "_": assert(key == value.nom) @XFAIL def test_imports(): u"""Vrifie qu'il n'existe pas d'imports relatifs implicites.""" # On liste les modules locaux locaux = set() def test(line): assert not re.search('(from|import) (' + '|'.join(locaux) + ')[. ]', line) for root, dirs, files in walk(WXGEODIR): if 'sympy' in dirs: dirs.remove('sympy') if 'sympy_OLD' in dirs: dirs.remove('sympy_OLD') for name in files: if name.endswith('.py'): locaux.add(name[:-3]) for name in dirs: if isfile(join(root, name, '__init__.py')): locaux.add(name) assert 'sympy' not in locaux and 'trigonometry' not in locaux # on teste les imports for root, dirs, files in walk(WXGEODIR): for name in files: if name.endswith('.py'): with open(join(root, name)) as f: for n, line in enumerate(f): if 'from ' in line or 'import ' in line: assert test(line), join(root, name) + ' L' + str(n + 1) #def test_presence_methodes(): # attributs_ou_methodes_obligatoires = [ # ] # for classe in G.__dict__.itervalues(): # if isinstance(classe, type) and issubclass(classe, G.Objet): # for attr in attributs_ou_methodes_obligatoires: # if not hasattr(classe, attr): # print u"ERREUR: La classe %s doit possder l'attribut ou la mthode '%s' !" %(classe, attr) # assert(hasattr(classe, attr)) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_labels.py0000644000175000017500000000204212014170666025172 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from pytest import XFAIL #from tools.testlib import assertAlmostEqual from wxgeometrie.geolib.tests.geotestlib import rand_pt from wxgeometrie.geolib import NOM def test_Label_point(): A = rand_pt() B = rand_pt() A.label("Position de l'hirondelle d'Afrique.") B.label(u"Position de l'hirondelle europenne.") assert(A.label() == "Position de l'hirondelle d'Afrique.") assert(B.label() == u"Position de l'hirondelle europenne.") A.style(legende = NOM) assert(A.label() == "") @XFAIL def test_Label_segment(): raise NotImplementedError @XFAIL def test_Label_droite(): raise NotImplementedError @XFAIL def test_Label_demidroite(): raise NotImplementedError @XFAIL def test_Label_cercle(): raise NotImplementedError @XFAIL def test_Label_arc_cercle(): raise NotImplementedError @XFAIL def test_Label_polygone(): raise NotImplementedError @XFAIL def test_Label_angle(): raise NotImplementedError wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_routines.py0000644000175000017500000000101712014170666025601 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random import math from tools.testlib import assertAlmostEqual, assertEqual, assertNotAlmostEqual from wxgeometrie.geolib.routines import (strip_trailing_zeros,) from wxgeometrie.mathlib.universal_functions import sin as u_sin, cos as u_cos, tan as u_tan def test_strip_trailing_zeros(): assertEqual(strip_trailing_zeros('.0450*1.54556000+4.2003+a.e00+.003+4.000'), '.045*1.54556+4.2003+a.e00+.003+4') wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_calcul_formel.py0000644000175000017500000001216712014170666026550 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from numpy import array from sympy import S, Expr from wxgeometrie.geolib import (Point, Vecteur, Droite, Intersection, Cercle, Segment, Disque, Homothetie, Rotation, Translation, Reflexion, Variable, Variable_generique, contexte, TYPES_NUMERIQUES, Triangle ) l = locals def allsym(val): return (isinstance(val, Expr) or (isinstance(val, Variable_generique) and allsym(val.val)) or all(isinstance(x, Expr) for x in val)) def allnum(val): return (isinstance(val, TYPES_NUMERIQUES) or (isinstance(val, Variable_generique) and allnum(val.val)) or all(isinstance(x, TYPES_NUMERIQUES) for x in val)) def assert_eq_num(*vals): for val in vals: assert allnum(val) assert vals.count(vals[0]) == len(vals) def tofloat(x): if hasattr(x, '__iter__'): return x.__class__(tofloat(x) for x in x) else: return float(x) def eq(x, y): if contexte['exact']: return x == y else: if hasattr(x, '__iter__') and hasattr(y, '__iter__'): return all(eq(i, j) for i, j in zip(x, y)) else: return abs(x - y) < contexte['tolerance'] def assert_eq(*args): assert len(args) > 2 locals_ = args[-1] expected = S(args[-2]) args = args[:-2] for exact in (True, False): if not exact: expected = tofloat(expected) with contexte(exact=exact): for arg in args: val = eval(arg, locals_) TEST = (allsym(val) if exact else allnum(val)) if not TEST: print(repr(val) + ' should only contain %s.' %('exact values' if exact else 'floats')) assert TEST TEST = eq(val, expected) if not TEST: print("'%s' equals to '%s', not '%s'." %(arg, repr(val), repr(expected))) assert TEST def test_Segment(): A = Point('1/2', '3/4') B = Point('pi', 'pi') C = Point(S('1/2'), S('3/4')) assert_eq("A.coordonnees", "C.coordonnees", "(1/2, 3/4)", l()) assert_eq('B.coordonnees', '(pi, pi)', l()) s = Segment(A, B) assert_eq('s.longueur', '((pi - 1/2)**2 + (pi - 3/4)**2)**(1/2)', l()) def test_eqn_formatee(): c = Cercle(('1/2', '2/3'), 'sqrt(2)') assert c.equation_formatee == u'x\xb2 + y\xb2 - x - 4/3 y - 47/36 = 0' d = Droite(('0','0'),('1','1')) assert d.equation_formatee == '-x + y = 0' e=Droite(('1', '0'), ('2/3','1')) assert e.equation_formatee == '-x - 1/3 y + 1 = 0' def test_intersections(): c = Cercle(('1/2', '2/3'), 'sqrt(2)') assert_eq('c.diametre', '2*2**(1/2)', l()) d = Droite(('0','0'),('1','1')) assert_eq('d._longueur()', '2**(1/2)', l()) e=Droite(('1', '0'), ('2/3','1')) assert_eq('e._longueur()', '10**(1/2)/3', l()) c1 = Cercle(('1/3', '3'), 'pi') assert_eq('c1.diametre', '2*pi', l()) assert_eq('c1.perimetre', '2*pi**2', l()) d1 = Disque(c1) assert_eq('d1.aire', 'pi**3', l()) # Droite/droite M = Intersection(d, e) assert_eq('M.coordonnees', '(3/4, 3/4)', l()) # Cercle/droite I = Intersection(c, d) assert_eq('I.coordonnees', '(7/12 - 143**(1/2)/12, 7/12 - 143**(1/2)/12)', l()) assert_eq_num(I.coordonnees_approchees, (-0.41318839525844997, -0.41318839525844997)) # Cercle/cercle J = Intersection(c, c1) assert_eq('J.coordonnees', '(913/2364 - 98*(212563/12348 + (-913/1176 - 3*pi**2/98)**2' '- 394*pi**2/343 - 197*(125/56 - 3*pi**2/14)**2/49)**(1/2)/197' '+ 3*pi**2/197,' '2671/1182 - 7*(212563/12348 + (-913/1176 - 3*pi**2/98)**2' '- 394*pi**2/343 - 197*(125/56 - 3*pi**2/14)**2/49)**(1/2)/197' '- 42*pi**2/197)', l()) def test_transformations(): c = Cercle(('1/5', '4/5'), '1/3') h = Homothetie(('1', '1'), '5/7') v = Vecteur(c.centre, h.centre) c1 = h(c) v1 = Vecteur(c1.centre, h.centre) assert v1.coordonnees == tuple(h.rapport*array(v.coordonnees)) assert_eq('c1.centre.coordonnees', '(3/7, 6/7)', l()) assert_eq('c1.rayon', '5/21', l()) c.rayon = '2' assert_eq('c1.rayon', '10/7', l()) r = Rotation(('0', '1'), 'pi/3') c2 = r(c) t = Translation(('2/3', '-1/3')) c3 = t(c) m = Reflexion(Droite('y=x')) c4 = m(c) c.rayon = '3/7' assert_eq('c2.rayon', '3/7', l()) assert_eq('c2.centre.coordonnees', '(1/10 + 3**(1/2)/10, 9/10 + 3**(1/2)/10)', l()) assert_eq('c3.rayon', '3/7', l()) assert_eq('c3.centre.coordonnees', '(13/15, 7/15)', l()) assert_eq('c4.rayon', '3/7', l()) assert_eq('c4.centre.coordonnees', '(4/5, 1/5)', l()) def test_aire_diametre_perimetre(): p = Triangle(('0', '0'), ('0', '1/3'), ('1/3', '0')) assert_eq('p.aire', '1/18', l()) c = Cercle(('0', '0'), '5/3') assert_eq('c.rayon', '5/3', l()) assert_eq('c.diametre', '10/3', l()) assert_eq('c.perimetre', '10/3*pi', l()) D = Disque(c) assert_eq('D.aire', 'pi*25/9', l()) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_angles.py0000644000175000017500000000645012014170666025210 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random import math from tools.testlib import assertAlmostEqual, assertNotAlmostEqual from wxgeometrie.geolib import (Feuille, Angle_oriente, Angle_vectoriel, Angle_libre, Secteur_angulaire, Label_angle, Vecteur_libre, Point, Angle, Variable, contexte, ) from wxgeometrie.mathlib.universal_functions import sin as u_sin, cos as u_cos, tan as u_tan def test_Secteur_angulaire(): u = Vecteur_libre(5.458, -2.546) v = Vecteur_libre(-5.75, 12.6) P = Point(2.54, -5.68) a = Secteur_angulaire(P, u, v) assert(isinstance(a.etiquette, Label_angle)) assertAlmostEqual(a.val, 2.43538435941) assertAlmostEqual(a.degre, 139.537245287) def test_Angle_oriente(): A = Point(250.54, 612.78) B = Point(115.54, 168.24) C = Point(412.78, -254.23) a = Angle_oriente(A, B, C) assertAlmostEqual(a.rad, -2.2336365048) def test_Angle(): A = Point(250.54, 612.78) B = Point(115.54, 168.24) C = Point(412.78, -254.23) a = Angle(A, B, C) assertAlmostEqual(a.rad, +2.2336365048) def test_Angle_libre(): x = random() a = Angle_libre(x) assertAlmostEqual(a.deg, x*180/math.pi) assertAlmostEqual(a.grad, x*200/math.pi) assertAlmostEqual(a.rad, x) assertAlmostEqual(a.val, x) assertAlmostEqual(math.sin(a.val), math.sin(x)) assertAlmostEqual(math.cos(a.val), math.cos(x)) assertAlmostEqual(math.tan(a.val), math.tan(x)) y = x*180/math.pi a = Angle_libre(y, u"") assertAlmostEqual(a.deg, y) assertAlmostEqual(a.grad, x*200/math.pi) assertAlmostEqual(a.rad, x) assertNotAlmostEqual(a.val, a.deg) assertAlmostEqual(u_sin(a), math.sin(x)) assertAlmostEqual(u_cos(a), math.cos(x)) assertAlmostEqual(u_tan(a), math.tan(x)) a.unite = "g" assertNotAlmostEqual(a.val, a.grad) b = Angle_libre(u"45") assertAlmostEqual(b.rad, math.pi/4) f = Feuille() f.objets.A = Point(40, 20) f.objets.k = Variable("A.x+5") f.objets.c = Angle_libre(f.objets.k, "d") f.objets.d = Angle_libre("A.x+5", "d") assert(f.objets.c.rad is not None) assertAlmostEqual(b.rad, f.objets.c.rad) assertAlmostEqual(f.objets.d.rad, f.objets.c.rad) def test_Angle_vectoriel(): u = Vecteur_libre(5.458, -2.546) v = Vecteur_libre(-5.75, 12.6) a = Angle_vectoriel(u, v) assertAlmostEqual(a.val, 2.43538435941) def test_contexte_degre(): with contexte(unite_angle='d'): a = Angle_libre('pi rad') assertAlmostEqual(a.deg, 180) a = Angle_libre('100 grad') assertAlmostEqual(a.deg, 90) b = Angle_libre(30) assertAlmostEqual(b.rad, math.pi/6) # En interne, tout doit tre stock en radians assert float(b.val) == float(b.rad) # On doit avoir eval(repr(b)) == b assertAlmostEqual(eval(repr(b)).deg, 30) def test_info(): a = Angle_libre(u"30") assert str(a.deg) == '30' with contexte(unite_angle='d'): assert a.info == u'Angle de valeur 30' with contexte(unite_angle='r'): assert a.info == u'Angle de valeur pi/6 rad' with contexte(unite_angle='g'): assert a.info == u'Angle de valeur 100/3 grad' wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_vecteurs.py0000644000175000017500000000335412014170666025577 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from random import random from math import sqrt from tools.testlib import assertAlmostEqual, assertEqual from wxgeometrie.geolib import Point, Vecteur_unitaire, Vecteur, Vecteur_libre, Representant, Label_vecteur, Somme_vecteurs def setUp(): pass def test_Vecteur(): A=Point(1,2) B=Point(2,4) v=Vecteur(A, B) assert(isinstance(v.etiquette, Label_vecteur)) assert(v.x == 1 and v.y == 2) assertEqual(v.norme, sqrt(5)) assertEqual(type(v.coordonnees), tuple) # Test du typage dynamique : assert(isinstance(Vecteur(1, 3), Vecteur_libre)) assert(Vecteur(1, 3).coordonnees == (1, 3)) def test_Vecteur_libre(): u = Vecteur_libre(1, -2) u.y = -3 assertEqual(u.norme, sqrt(10)) assertEqual(type(u.coordonnees), tuple) def test_Representant(): A=Point(1,2) B=Point(2,4) v=Vecteur(A, B) w = Representant(v, B) assert(w.x == v.x and w.y == v.y and w.z == v.z) assert(w.origine == v.point2) assertEqual(type(w.coordonnees), tuple) assertAlmostEqual(w.extremite.coordonnees, (3, 6)) assert(w._hierarchie < w.extremite._hierarchie < w._hierarchie + 1) def test_Vecteur_unitaire(): u = Vecteur_unitaire(Vecteur_libre(random(), random())) assertAlmostEqual(u.norme, 1) assertEqual(type(u.coordonnees), tuple) def test_Somme_vecteurs(): A=Point(1,2) B=Point(2,4) v=Vecteur(A, B) w=Vecteur_libre(-4, 5) u=Representant(v, Point(1, 2)) vec = 2*u+1*v vec -= 5*w assert(tuple(vec.coordonnees) == (23, -19)) assertEqual(type(vec.coordonnees), tuple) assertEqual(vec.coordonnees, Somme_vecteurs((u, v, w), (2, 1, -5)).coordonnees) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_textes.py0000644000175000017500000000041312014170666025244 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from tools.testlib import rand from wxgeometrie.geolib import Texte def test_Texte(): t = Texte("spam & eggs", rand(), rand()) assert(t.texte == "spam & eggs") wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/tests_lignes.py~0000644000175000017500000001432612014170666025602 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from customtest import * class TestGeolibLignes(CustomTest): def test_Segment(self): A = Point(4.5, 7.3) B = Point(4, 2.1) s = Segment(A, B) self.assert_(isinstance(s.etiquette, Label_segment)) self.assertAlmostEqual(s.longueur, math.sqrt((B.x - A.x)**2 + (B.y - A.y)**2)) I = Milieu(s.point1, s.point2) self.assertEqual(I.coordonnees, ((A.x+B.x)/2, (A.y+B.y)/2)) M = Barycentre((A, 1), (B, -2)) N = Barycentre((A, -2), (B, 1)) self.assert_(I in s) self.assert_(M not in s) self.assert_(N not in s) self.assert_(s.style("legende") == param.RIEN) def test_Demidroite(self): A = Point(4.5, 7.3) B = Point(4, 2.1) s = Demidroite(A, B) self.assert_(isinstance(s.etiquette, Label_demidroite)) self.assertRaises(AttributeError, getattr, s, "longueur") I = Milieu(s.origine, s.point) self.assertEqual(I.coordonnees, ((A.x+B.x)/2, (A.y+B.y)/2)) M = Barycentre((A, 1), (B, -2)) N = Barycentre((A, -2), (B, 1)) self.assert_(I in s) self.assert_(M in s) self.assert_(N not in s) self.assert_(s.style("legende") == param.RIEN) def test_Droite(self): A = Point(4.5, 7.3) B = Point(4, 2.1) d = Droite(A, B) self.assert_(isinstance(d.etiquette, Label_droite)) self.assertRaises(AttributeError, getattr, d, "longueur") I = Milieu(d.point1, d.point2) self.assertEqual(I.coordonnees, ((A.x+B.x)/2, (A.y+B.y)/2)) M = Barycentre((A, 1), (B, -2)) N = Barycentre((A, -2), (B, 1)) self.assert_(I in d) self.assert_(M in d) self.assert_(N in d) self.assert_(isinstance(d.equation, tuple)) self.assert_(d.style("legende") == param.RIEN) # Test du typage dynamique d = Droite("y=x+1") self.assert_(Point(0, 1) in d) d = Droite(Point(1, 2), Vecteur_libre(1, 1)) self.assert_(Point(1, 2) in d) self.assert_(Point(2, 3) in d) d2 = Droite("y=-x+1") self.assert_(Point(0, 1) in d2) self.assert_(Point(1, 0) in d2) def test_Droite_vectorielle(self): v = Vecteur_libre(1, 7) A = Point(-2, 3) d = Droite_vectorielle(A, v) self.assert_(d.vecteur is v and d.point is A) self.assertAlmostEqual(v.y/v.x, -d.equation[0]/d.equation[1]) B = rand_pt() d1 = Droite_vectorielle(B, v) self.assert_(d.parallele(d1)) def test_Parallele(self): d0 = Droite_equation(2, 1, 7) A = Point(-2, 3) d = Parallele(d0, A) self.assert_(d.parallele(d0)) self.assert_(d.droite is d0 and d.point is A) self.assertAlmostEqual(d0.equation[:1], d.equation[:1]) ## def test_Droite_rotation(self): ## r = Rotation(Point(1.45, -2.59), math.pi/3) ## C = Point(1.458, -5.255) ## D = Point(3.478, -2.14788) ## d = Droite(C, D) ## # Dans ce qui suit, d1, d2 et d3 doivent correspondre la mme droite. ## d1 = Droite_rotation(d, r) ## d2 = Droite(r(C), r(D)) ## d3 = r(d) ## a, b, c = d1.equation ## self.assertAlmostEqual(d1.equation_reduite, d2.equation_reduite) ## self.assertAlmostEqual(d1.equation_reduite, d3.equation_reduite) ## self.assertAlmostEqual(d1.equation_reduite, (-a/b, -c/b)) ## d = Droite_rotation(Droite_equation(1, -1, 1), Rotation(Point(0, 0), math.pi/2)) ## a, b, c = d.equation ## self.assertAlmostEqual(b/a, 1) ## self.assertAlmostEqual(c/a, 1) def test_Mediatrice(self): A = Point(4.5, 7.3) B = Point(-4.147, 2.1) s = Segment(A, B) d0 = Mediatrice(s) d1 = Mediatrice(A, B) I = Milieu(A, B) self.assert_(I in d0) self.assert_(I in d1) a, b, c = s.equation a0, b0, c0 = d0.equation self.assertAlmostEqual(a*a0 + b*b0, 0) self.assertAlmostEqual(d0.equation, d1.equation) def test_Perpendiculaire(self): d = Droite_equation(-1, 2, 0) M = Point() d0 = Perpendiculaire(d, M) a, b, c = d.equation a0, b0, c0 = d0.equation self.assert_(d.perpendiculaire(d0)) self.assertAlmostEqual(a*a0 + b*b0, 0) self.assert_(M in d0) def test_Droite_equation(self): a = randint(50) - randint(50) + 0.1 # afin que a ne soit pas nul b = randint(50) - randint(50) + random() c = randint(50) - randint(50) + random() d, e, f = Droite_equation(a, b, c).equation self.assertAlmostEqual((e/d, f/d), (b/a, c/a)) self.assertEqual(Droite_equation(a, 0, 0).equation[1:], (0, 0)) self.assertEqual((Droite_equation(0, a, 0).equation[0], Droite_equation(0, a, 0).equation[2]), (0, 0)) self.assert_(not Droite_equation(0, 0, 0).existe) d = Droite_equation("y=-5/2x-3/2") self.assert_(Point(0, -1.5) in d) self.assert_(Point(-1, 1) in d) d = Droite_equation("x=2*10**2") self.assert_(Point(200, -1000) in d) self.assert_(Point(100, -1000) not in d) d = Droite_equation("2*x+2*y=1") self.assert_(Point(0.5, 0) in d) self.assert_(Point(1, -0.5) in d) d = Droite_equation("x+y=1") self.assert_(Point(0, 1) in d) self.assert_(Point(1, 0) in d) def test_Bissectrice(self): A = Point(1, -5) B = Point(1.5, -5.3) C = Point(3, -4) d = Bissectrice(A, B, C) a, b, c = d.equation d, e = (0.0870545184921, -1.03861105199) self.assertAlmostEqual(b/a, d) self.assertAlmostEqual(c/a, e) def test_Tangente(self): A = Point(4.75, -2.56887) O = Point(2.56874, -85.2541) M = Point(7.854, -552.444) c = Cercle(O, A) d = Tangente(c, A) self.assert_(A in d) self.assert_(M not in d) d1 = Tangente(c, M) self.assert_(M in d1) self.assert_(A not in d1) self.assert_(not Tangente(c, O).existe) if __name__ == '__main__': unittest.main() wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_courbes.py0000644000175000017500000000064312014170666025377 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from pytest import XFAIL @XFAIL def test_Interpolation_lineaire(): raise NotImplementedError @XFAIL def test_Interpolation_quadratique(): raise NotImplementedError @XFAIL def test_Interpolation_cubique_old(): raise NotImplementedError @XFAIL def test_Interpolation_cubique(): raise NotImplementedError wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_feuille.py0000644000175000017500000003276512014170666025374 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement import re from math import cos, pi, e, sqrt from tools.testlib import assertAlmostEqual, assertRaises, assertEqual from wxgeometrie.geolib.tests.geotestlib import rand_pt from wxgeometrie.geolib import (Triangle_rectangle, DescripteurFeuille, Point, Segment, Vecteur, Fonction, Variable, Feuille, Angle, contexte, Arc_cercle, Texte, Droite, Carre, Triangle, Polygone, Cercle, Parallelogramme, NOM, ) def test_abreviations(): f = Feuille(titre = u"Feuille de travail n1") o = f.objets assert(o.has_key("Point")) assert(o.has_key("Variable")) assert(o.has_key("Texte")) assert(o.has_key("point")) assert(o.has_key("variable")) assert(o.has_key("texte")) o.txt = ["salut"] assert(isinstance(o.txt, Texte)) assert(o.txt.texte == "salut") o.s = [u"H, a marche !"] assert(isinstance(o.s, Texte)) assert(o.s.texte == u"H, a marche !") o.A = (1, 2) o.k = 7 assert(isinstance(o.A, Point)) assert(isinstance(o.k, Variable)) o.h = 'A.x' o.A.x = 15 assert(o.h == 15) o.h = "A.x-10" assert(o.h == 5) assert(o.h - 3 == 2) o.h = pi/3 assertAlmostEqual(cos(o.h), 1/2) o.B = (-1, 3) o.u = o.A>o.B assert(isinstance(o.u, Vecteur)) assert(o.u.coordonnees == (o.B.x - o.A.x, o.B.y - o.A.y)) o.C = 2-3j assert(isinstance(o.C, Point)) assert(o.C.z == 2-3j) o.C.x = "A.x" #print 'o.C.x.val:', o.C.x.val, type(o.C.x.val) assert(isinstance(o.C.x.val, (float, int, long))) assert(o.C.x == o.A.x) o.A.coordonnees = -11, 3 assert(o.C.coordonnees[0] == -11) o.B.x = "A.x + 1" assert(isinstance(o.B.x.val, (float, int, long))) assert(o.B.x == o.A.x + 1) o.A.coordonnees = 30, -5 assert(o.B.coordonnees[0] == 31) o.A(-3.6, 0.4) assert(o.C.coordonnees[0] ==-3.6) # 'o.EFG = Triangle' doit tre accept comme alias de 'o.EFG = Triangle()' o.EFG = Triangle assert(isinstance(o.EFG, Triangle)) def test_nommage_automatique(): f = Feuille() M1 = f.objets._ = Point() assert("M1" in f.objets) M2 = f.objets._ = Point(1, 3) assert("M2" in f.objets) f.objets._ = Droite(M1, M2) assert("d1" in f.objets) f.objets._ = Cercle(M1, M2) assert("c1" in f.objets) f.objets._ = Segment(M1, M2) assert("s1" in f.objets) def test_noms_aleatoires(): f = Feuille() f.executer('A1=(1,2)') f.executer('A2=(1,0)') M = Point() s = Segment() g = Fonction('2x+7') assert f.nom_aleatoire(M) == 'M1' assert f.nom_aleatoire(s) == 's1' assert f.nom_aleatoire(g) == 'f1' assert f.nom_aleatoire(M, prefixe='A') == 'A3' # f0, f1, etc. sont rservs aux fonctions nom = f.nom_aleatoire(M, prefixe='f') assert re.match('[A-Za-z]{8}[0-9]+$', nom) def test_prime(): # Cf. issue 129 f = Feuille() f.executer('F = Fonction("2x+7")') assertRaises(NameError, f.executer, "F'' = (1, 4)") f.executer("G''' = (-3, 6)") assertRaises(NameError, f.executer, 'G = Fonction("3x+2")') assertRaises(NameError, f.executer, '''H' = Fonction("2x-4")''') assertRaises(NameError, f.executer, "f1' = (1, 2)") def test_nommage_intelligent(): f = Feuille() o = f.objets o.AB = Segment() assert(o.AB.point1.nom == "A") assert(o.AB.point2.nom == "B") del o.AB del o.B o.AB = Segment(o.A) assert(o.AB.point2.nom == "B") o.D = Point() o.CD = Segment(point2 = o.D) assert(o.CD.point1.nom == "C") o.clear() o.ABCDEFGHIJKLMNOPQRSTUVWXYZ = Polygone(26) assert( list(pt.nom for pt in o.ABCDEFGHIJKLMNOPQRSTUVWXYZ.points)\ == list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")) o.clear() o.EFG = Triangle() assert(o.EFG.point1.nom == "E") assert(o.EFG.point2.nom == "F") assert(o.EFG.point3.nom == "G") o.MNP = Triangle_rectangle() assert(o.MNP.point1.nom == "M") assert(o.MNP.point2.nom == "N") assert(o.MNP.sommets[2].nom == "P") assert(o.P.style("legende") == NOM) o.ABCD = Carre() assert(o.ABCD.point1.nom == "A") assert(o.ABCD.point2.nom == "B") assert(o.ABCD.sommets[2].nom == "C") assert(o.ABCD.sommets[3].nom == "D") def test_acces_objets(): f = Feuille() o = f.objets o.A = (1, 2) assert(o["A"] is o.A) o.A_prime = (7, -1.5) assert(o["A`"] is o.A_prime) exec("b=Vecteur_libre()", o) assert("b" in o.keys()) exec("del b", o) assert("b" not in o.keys()) def test_sauvegarde(): f = Feuille(titre = "Ma feuille") o = f.objets o.A = (1, 2) o.B = (-1, 3) o.k = 7 o.s = Segment(o.A, o.B) f.sauvegarder() def test_rattachement_objets(): A=Point() # assert(A.x == 0 and A.y == 0) x0, y0 = A.xy assert(isinstance(Point.__feuille__, DescripteurFeuille)) assert(A.__feuille__ is None) f = Feuille() f.objets.A = A assert(A.__feuille__ is f) assert(A.x != x0 and A.y != y0) xmin, xmax, ymin, ymax = f.fenetre assert(xmin <= A.x <= xmax and ymin <= A.y <= ymax) def test_variables_composees_1(): f = Feuille() A = f.objets.A = Point() B = f.objets.B = Point() A(-5, 13) B.x = A.x assert(B.x == B.coordonnees[0] == A.x == -5) A(1, 9) assert(B.x == B.coordonnees[0] == -5 and A.x == 1) B.x="A.x" A(17, 5) assert(B.coordonnees[0] == B.x == 17) def test_variables_composees_2(): f = Feuille() f.objets.M1 = Point(-1.27482678984, 1.69976905312, legende=2) f.objets.M2 = Point(2.42032332564, 1.25635103926, legende=2) f.objets.s1 = Segment(f.objets.M1,f.objets.M2) f.objets.M1(-2.77136258661, 2.91916859122) f.objets.M1(4.74826789838, -1.07159353349) f.objets.M5 = Point(-5.11778290993, 2.30946882217, legende=2) f.objets.M6 = Point(-1.86605080831, 3.25173210162, legende=2) f.objets.s4 = Segment(f.objets.M5,f.objets.M6) f.objets.M5(-5.59815242494, 2.34642032333) f.objets.M1(-2.42032332564, -1.60739030023) f.objets.M6(-1.86605080831, 3.25173210162) f.objets.M6.renommer('B', legende = 1) f.objets.M6 = Point(2.91916859122, 3.5103926097, legende=2) f.objets.M6.style(**{'legende': 3, 'label': u'B.x'}) f.objets.B(-1.18244803695, 1.25635103926) f.objets.M6.supprimer() f.objets.B(-2.21709006928, 2.64203233256) f.objets.M6 = Point(-6.6143187067, 0.443418013857, legende=2) f.objets.M6.renommer('C', legende = 1) f.objets.C.x=f.objets.B.x f.objets.B(-3.17782909931, 3.36258660508) f.objets.C.x="B.x" f.objets.B(-4.74826789838, 3.47344110855) f.objets.B(-1.99538106236, 3.63972286374) assert(f.objets.C.coordonnees[0] == f.objets.C.x == f.objets.B.coordonnees[0] == f.objets.B.x) def test_polygones_et_representants_de_vecteurs(): f = Feuille() f.objets.A = A = rand_pt() f.objets.B = B = rand_pt() f.objets.C = C = rand_pt() f.objets.p = Parallelogramme(A, B, C) f.objets.S1.renommer("D") s = repr(f.objets.p) del f.objets.p assert("D" not in f.objets) exec("p=" + s, f.objets) assert("D" in f.objets) assert(f.objets.D is f.objets.p.sommets[3]) def test_relier_point_axes(): f = Feuille() f.objets.M1 = Point(-1.27482678984, 1.69976905312, legende=2) f.objets.M2 = Point(2.42032332564, 1.25635103926, legende=2) f.objets.s1 = Segment(f.objets.M1, f.objets.M2) f.objets.M1.relier_axe_x() f.objets.M1.relier_axe_y() def test_noms_latex(): f = Feuille() f.objets.A = Point() assert(f.objets.A.nom == "A") assert(f.objets.A.nom_latex == "$A$") f.objets.B1 = Point() assert(f.objets.B1.nom == "B1") assert(f.objets.B1.nom_latex == "$B_{1}$") f.objets.C17 = Point() assert(f.objets.C17.nom == "C17") assert(f.objets.C17.nom_latex == "$C_{17}$") f.objets.objet5 = Point() assert(f.objets.objet5.nom == "objet5") assert(f.objets.objet5.nom_latex == "$objet_{5}$") f.objets.Delta = Point() assert(f.objets.Delta.nom == "Delta") assert(f.objets.Delta.nom_latex == "$\\Delta$") f.objets.delta = Point() assert(f.objets.delta.nom == "delta") assert(f.objets.delta.nom_latex == "$\\delta$") f.objets.phi15 = Point() assert(f.objets.phi15.nom == "phi15") assert(f.objets.phi15.nom_latex == "$\\phi_{15}$") f.objets.A_prime_prime = Point() assert(f.objets.A_prime_prime.nom == "A_prime_prime") assert(f.objets.A_prime_prime.nom_latex == "$A''$") f.objets["A'B'"] = Point() assert(f.objets.A_primeB_prime.nom == "A_primeB_prime") assert(f.objets.A_primeB_prime.nom_latex == "$A'B'$") f.objets.A_prime71 = Point() assert(f.objets.A_prime71.nom == "A_prime71") assert(f.objets.A_prime71.nom_latex == "$A'_{71}$") f.objets.A17B22 = Point() assert(f.objets.A17B22.nom == "A17B22") assert(f.objets.A17B22.nom_latex == "$A_{17}B_{22}$") f.objets.C_prime = Cercle() assert(f.objets.C_prime.nom_latex == "$\\mathscr{C}'$") f.objets.u = Vecteur() assert(f.objets.u.nom_latex == "$\\vec u$") f.objets.u_prime = Vecteur() assert(f.objets.u_prime.nom_latex == "$\\overrightarrow{u'}$") def test_formules(): f = Feuille() o = f.objets o.A = Point(e, 3) o.M = Point() o.M.label(u'{1/ln(A.x)}', True) assert(eval(o.M.label()) == 1) def test_constantes(): f = Feuille() assertAlmostEqual(f.objets.pi, 3.1415926535897931) assertAlmostEqual(f.objets.e, 2.7182818284590451) def test_modification_variable(): f = Feuille() o = f.objets o.a = Variable(1) o.fa = "5*sin(4/(a+.5))-1.5" o.A = Point(o.a, o.fa) o.fa = "-5*sin(4/(a+.5))+5.5" o.A = Point(o.a, o.fa) assertAlmostEqual(o.A.x, o.a) assertAlmostEqual(o.A.y, o.fa) def test_info(): f = Feuille() o = f.objets with contexte(decimales = 2): A = o.A = Point(5, 7) assert(A.info == u"Point A de coordonnes (5, 7)") B = o.B = Point(6.5, 9.3) assert(B.info == u"Point B de coordonnes (6.5, 9.3)") s = o.s = Segment(A, B) assert(s.info == u"Segment s de longueur 2.75") c = o.c = Cercle(s) assert(c.info == u"Cercle c de rayon 1.37") d = o.d = Droite(A, B) assert(d.info == u"Droite d d'quation -2.3 x + 1.5 y + 1 = 0") C = o.C = Point(-1.5, 2.7) a = o.a = Arc_cercle(A, B, C) assert(a.info == u'Arc a de longueur 7.5') alpha = o.alpha = Angle(A, B, C) assertEqual(alpha.info, u'Angle alpha de valeur 0.3 rad') with contexte(decimales = 3): assert(a.info == u'Arc a de longueur 7.505') def test_executer(): f = Feuille() o = f.objets f.executer("A = (1, 2)") f.executer("A.x += 1") assert(o.A.x == 2) f.executer("A' = 3, 4") f.executer("s = [A A']") f.executer("I = Milieu(s)") assertAlmostEqual(o.I.xy, (2.5, 3)) f.executer("del") assert("I" not in o.noms) assert("A_prime" in o.noms) f.executer("del") f.executer("del") assert("A_prime" not in o.noms) f.executer("= (1, 2)") assert(o.M1.coordonnees == (1, 2)) f.executer("txt0 = `Bonjour !`") f.executer(r"txt1 = `$P\`ere et m\`ere ont un accent grave.$`") f.executer("chaine_vide = ``") assert(o.txt0.texte == "Bonjour !") assert(o.txt1.texte == r"$P\`ere et m\`ere ont un accent grave.$") assert(o.chaine_vide.texte == "") f.executer("M = (5, 7)") f.executer("C = _") assert(o.C.x == 5) f.executer("=((i,sqrt(i)) for i in (3,4,5,6))") assert(o.M2.xy == (3, sqrt(3))) assert(o.M3.xy == (4, sqrt(4))) assert(o.M4.xy == (5, sqrt(5))) assert(o.M5.xy == (6, sqrt(6))) def test_nettoyer(): f = Feuille() o = f.objets ex = f.executer ex('A=(5,4)') ex('B=(6,5.3)') ex('s=Segment(A, B)') ex('I=Milieu(s)') ex('M=Point(s)') ex('d=Droite(A, B)') ex('C=(4, 8)') ex('d2=Droite(A, C)') ex('B.style(visible = False)') noms = o.noms assert(noms == set(("A", "B", "s", "I", "M", "d", "C", "d2", "B"))) f.nettoyer() assert(o.noms == noms) ex('s.style(visible = False)') f.nettoyer() assert(o.noms == noms) ex('M.style(visible = False)') f.nettoyer() noms -= set(("M", "s")) assert(o.noms == noms) ex('d.style(visible = False)') f.nettoyer() noms.remove("d") assert(o.noms == noms) ex('I.style(visible = False)') f.nettoyer() noms -= set(("B", "I")) assert(o.noms == noms) def test_feuille_modifiee(): f = Feuille() f.modifiee = True f.executer('A=(1,2)') assert(f.modifiee) f.modifiee = False f.executer('A.x = 3') assert(f.modifiee) f.modifiee = False f.historique.annuler() assert(f.modifiee) def test_issue_186(): f = Feuille() f.executer("c=Cercle") assertRaises(NameError, f.executer, "C_'=_") assert(f.objets.has_key("c")) def test_redefinir(): f = Feuille() A = f.objets.A = Point() B = f.objets.B = Point() f.objets.AB = Segment(A, B) f.objets.AB.redefinir('Vecteur(A, B)') assert isinstance(f.objets.AB, Vecteur) assert f.objets.AB == Vecteur(A, B) f.objets.txt = Texte('Hello', 2, 3) f.objets.txt.redefinir("Texte('Bonjour', 1, 4)") assert isinstance(f.objets.txt, Texte) assert f.objets.txt.texte == 'Bonjour' assert f.objets.txt.coordonnees == (1, 4) def test_issue_176(): f = Feuille() A = f.objets.A = Point() B = f.objets.B = Point() f.objets.s = Segment(A, B) del f.objets.A, f.objets.B, f.objets.s assert set(('A', 'B', 's')).isdisjoint(f.objets.noms) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/test_intersections.py0000644000175000017500000000550412014170666026627 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) #from tools.testlib import assertAlmostEqual from wxgeometrie.geolib.tests.geotestlib import rand_dte, rand_pt from wxgeometrie.geolib import (Intersection_droite_cercle, Intersection_cercles, Point, Droite, Intersection_droites, Parallele, Feuille, Cercle, Mediatrice, Segment, ) def test_Intersection_droites(): d1 = rand_dte() d2 = rand_dte() A = Intersection_droites(d1, d2) if not d1.parallele(d2): assert(A in d1 and A in d2) d3 = Parallele(d1, rand_pt()) assert(not Intersection_droites(d1, d3).existe) D = Point(-14.201335283549275, 1.5093204196583834) U = Point(-14.201335283549273, 17.644024286752096) d = Droite(U, D) s = Segment(U, D) V = Point(1.933368583544437, 7.5065025053891166) W = Point(7.1347038670937115, 8.3895493390615954) d2 = Droite(W, V) M1 = Intersection_droites(s, d2) M2 = Intersection_droites(d, d2) assert(M1.existe) assert(M2.existe) def test_Intersection_droite_cercle(): A = Point(-3.075, 2.0, legende=2) B = Point(0.0, 1.625, legende=2) c1 = Cercle(A, B) C = Point(-0.375, 4.425, legende=2) D = Point(3.25, 0.125, legende=2) d1 = Droite(C, D) assert(not Intersection_droite_cercle(d1, c1).existe) C(-5.675, 4.95) I = Intersection_droite_cercle(d1, c1, True) assert(I == (-4.87791007862, 4.51908023858)) J = Intersection_droite_cercle(d1, c1, False) assert(J == (0.0201000262814, 1.87113640036)) def test_Intersection_cercles(): A = Point(-4.4375, 1.95833333333, legende=2) B = Point(-2.10416666667, 0.875, legende=2) c1 = Cercle(A, B) C = Point(2.1875, 1.35416666667, legende=2) c2 = Cercle(C,B) D = Intersection_cercles(c2, c1, False, legende=2) assert(D == (-1.9466976004889973, 2.6017297602107377)) assert(Intersection_cercles(c2, c1, True, legende=2) == B) assert(Droite(A, C) == Mediatrice(B, D)) def test_intersection_et_feuille(): u"""On teste que par dfaut, le deuxime d'intersection soit diffrent du premier.""" f = Feuille() f.objets._ = Point(-5.11060948081, 0.144469525959) f.objets._ = Point(-3.97291196388, 0.794582392777) f.objets._ = Cercle(f.objets.M1, f.objets.M2) f.objets._ = Point(-3.26862302483, -1.10158013544) f.objets._ = Point(-5.79683972912, 2.41986455982) f.objets._ = Droite(f.objets.M3, f.objets.M4) f.objets._ = Intersection_droite_cercle(f.objets.d1, f.objets.c1, True) f.objets._ = Intersection_droite_cercle(f.objets.d1, f.objets.c1) # On vrifie qu'on a bien obtenu le 2e point d'intersection (et non deux fois de suite le mme) assert(f.objets.M6.premier_point == False) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/tests/__init__.py0000644000175000017500000000000012014170666024420 0ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/geolib/vecteurs.py0000644000175000017500000003245112014170666023376 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Vecteurs # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode from random import uniform, normalvariate from numpy import ndarray from .objet import Argument, Arguments, Ref, Objet_avec_coordonnees, Objet, G,\ contexte, TYPES_REELS, Objet_avec_coordonnees_modifiables from .points import Point, Point_generique, Point_final from .variables import Variable_generique from .routines import norme, vect from .. import param class Vecteur_generique(Objet_avec_coordonnees): u"""Un vecteur gnrique. Usage interne : la classe mre pour les diffrents vecteurs""" _prefixe_nom = "v" def __init__(self, **styles): # self.__args = GestionnaireArguments() Objet.__init__(self, **styles) # pas besoin de grer les styles (objet non affich), mais il vaut mieux que le code soit standardis... @property def abscisse(self): return self.coordonnees[0] @property def ordonnee(self): return self.coordonnees[1] @property def affixe(self): coordonnees = self.coordonnees return coordonnees[0] + coordonnees[1]*1j x = abscisse y = ordonnee z = affixe def _longueur(self): return norme(self.x, self.y) norme = property(_longueur) def __add__(self, y): if isinstance(y, Vecteur_generique): return Somme_vecteurs([self, y]) raise TypeError, "vecteur attendu" def __sub__(self, y): return self + (-y) def __mul__(self, y): return NotImplemented # if isinstance(y, Vecteur_generique): # return Produit_scalaire(self, y) # else: # return NotImplemented def __rmul__(self, y): return Somme_vecteurs([self], [y]) def __neg__(self, *args): return (-1)*self def __div__(self, y): return (1./y)*self def __truediv__(self, y): return self.__div__(y) def __eq__(self, y): if self.existe: if isinstance(y, Vecteur_generique) and y.existe: return abs(self.x - y.x) < contexte['tolerance'] and abs(self.y - y.y) < contexte['tolerance'] elif isinstance(y, (list, tuple, ndarray)) and len(y) == 2: return abs(self.x - y[0]) < contexte['tolerance'] and abs(self.y - y[1]) < contexte['tolerance'] return False def __nonzero__(self): return tuple(self.coordonnees) != (0, 0) def __ne__(self, y): return not (self == y) @staticmethod def _convertir(objet): if hasattr(objet, "__iter__"): a, b = objet if isinstance(a, Point_generique): return Vecteur(a, b) else: return Vecteur_libre(a, b) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" class Vecteur(Vecteur_generique): u"""Un vecteur. Un vecteur dfini par deux points.""" _style_defaut = param.vecteurs _affichage_depend_de_la_fenetre = True point1 = __point1 = Argument("Point_generique", defaut = Point) point2 = __point2 = Argument("Point_generique", defaut = Point) def __new__(cls, *args, **kw): if len(args) == 2 and isinstance(args[0], TYPES_REELS + (Variable_generique, basestring, )) \ and isinstance(args[1], TYPES_REELS + (Variable_generique, basestring, )): vecteur_libre = Vecteur_libre.__new__(Vecteur_libre, *args, **kw) vecteur_libre.__init__(*args, **kw) return vecteur_libre return object.__new__(cls) def __init__(self, point1 = None, point2 = None, **styles): # if point2 is None: # point2 = point1 # point1 = Point(0, 0) self.__point1 = point1 = Ref(point1) self.__point2 = point2 = Ref(point2) Vecteur_generique.__init__(self, **styles) self.etiquette = G.Label_vecteur(self) @property def extremites(self): return self.__point1, self.__point2 def _creer_figure(self): if not self._representation: self._representation = [self.rendu.fleche()] fleche = self._representation[0] fleche.set(xy0=self.__point1.coordonnees, xy1=self.__point2.coordonnees, taille=self.style("taille"), angle=self.style("angle"), position=self.style("position"), double=self.style("double_fleche"), linewidth=self.style("epaisseur"), color=self.style("couleur"), zorder=self.style("niveau"), linestyle=self.style("style"), ) def _get_coordonnees(self): return (self.__point2.x - self.__point1.x, self.__point2.y - self.__point1.y) def _espace_vital(self): x1, y1 = self.__point1.coordonnees x2, y2 = self.__point2.coordonnees return (min(x1, x2), max(x1, x2), min(y1, y2), max(y1, y2)) def _distance_inf(self, x, y, d): # cf. "distance_point_segment.odt" dans "doc/developpeurs/maths/" xA, yA = self._pixel(self.__point1) xB, yB = self._pixel(self.__point2) x1 = min(xA, xB) - d; x2 = max(xA, xB) + d y1 = min(yA, yB) - d; y2 = max(yA, yB) + d if x1 contexte['tolerance']: return ((yA-yB)*(x-xA)+(xB-xA)*(y-yA))**2/norme2 < d**2 else: # les extrmits du segment sont confondues return (x - xA)**2 + (y - yA)**2 < d**2 else: return False def _contains(self, M): #if not isinstance(M, Point_generique): # return False A = self.__point1 B = self.__point2 xu, yu = vect(A, B) xv, yv = vect(A, M) if abs(xu*yv-xv*yu) > contexte['tolerance']: return False if xu: k = xv/xu elif yu: k = yv/yu else: # A == B return M == A return 0 <= k <= 1 @staticmethod def _convertir(objet): if hasattr(objet, "__iter__"): return Vecteur(*objet) raise TypeError, "'" + str(type(objet)) + "' object is not iterable" def image_par(self, transformation): return Vecteur(self.__point1.image_par(transformation), self.__point2.image_par(transformation)) def _creer_nom_latex(self): u"""Cre le nom format en LaTeX. Ex: M1 -> $M_1$.""" Objet._creer_nom_latex(self) latex = self.nom_latex[1:-1] if len(latex) == 1: # petite flche nom = "\\vec " + self.nom_latex[1:-1] else: # grande flche nom = "\\overrightarrow{" + self.nom_latex[1:-1] + "}" self.nom_latex = "$" + nom + "$" class Vecteur_libre(Objet_avec_coordonnees_modifiables, Vecteur_generique): u"""Un vecteur libre. Un vecteur dfini par ses coordonnes.""" abscisse = x = __x = Argument("Variable_generique", defaut = lambda: normalvariate(0,10)) ordonnee = y = __y = Argument("Variable_generique", defaut = lambda: normalvariate(0,10)) def __init__(self, x = None, y = None, **styles): x, y, styles = self._recuperer_x_y(x, y, styles) self.__x = x = Ref(x) self.__y = y = Ref(y) Objet_avec_coordonnees_modifiables.__init__(self, x, y, **styles) Vecteur_generique.__init__(self, **styles) def _set_feuille(self): xmin, xmax, ymin, ymax = self.__feuille__.fenetre if "_Vecteur_libre__x" in self._valeurs_par_defaut: self.__x.val = uniform(xmin, xmax) # self._valeurs_par_defaut.discard("_Vecteur_libre__x") if "_Vecteur_libre__y" in self._valeurs_par_defaut: self.__y.val = uniform(ymin, ymax) # self._valeurs_par_defaut.discard("_Vecteur_libre__y") Objet._set_feuille(self) def _update(self, objet): if not isinstance(objet, Vecteur_libre): objet = self._convertir(objet) if isinstance(objet, Vecteur_libre): self.coordonnees = objet.coordonnees else: raise TypeError, "l'objet n'est pas un vecteur libre." class Vecteur_unitaire(Vecteur_generique): u"""Un vecteur unitaire. Un vecteur dfini en normalisant un autre vecteur. Il aura donc mme sens et mme direction, mais sera de norme 1.""" vecteur = __vecteur = Argument("Vecteur_generique", defaut = Vecteur_libre) def __init__(self, vecteur = None, **styles): self.__vecteur = vecteur = Ref(vecteur) Vecteur_generique.__init__(self, **styles) def _get_coordonnees(self): return (self.__vecteur.abscisse/self.__norme, self.__vecteur.ordonnee/self.__norme) def _conditions_existence(self): self.__norme = self.__vecteur.norme return self.__norme > contexte['tolerance'] class Somme_vecteurs(Vecteur_generique): u"""Une somme de vecteurs. Les arguments sont des couples coefficient, vecteur Exemple: Somme_vecteurs([u,v,A>B], [3,2,-5]).""" vecteurs = __vecteurs = Arguments("Vecteur_generique") coeffs = coefficients = __coefficients = Arguments("Variable_generique") def __init__(self, vecteurs, coefficients = None, **styles): if coefficients == None: coefficients = tuple(1 for vecteur in vecteurs) # le code suivant evite de multiplier les sommes de vecteurs imbriquees : _coefficients_ = [] _vecteurs_ = [] for i in range(len(vecteurs)): if isinstance(vecteurs[i], Somme_vecteurs): _coefficients_.extend(list(coefficients[i]*k for k in vecteurs[i].coefficients)) _vecteurs_.extend(vecteurs[i].vecteurs) else: _coefficients_.append(coefficients[i]) _vecteurs_.append(vecteurs[i]) coefficients, vecteurs = _coefficients_, _vecteurs_ self.__vecteurs = vecteurs = tuple(Ref(obj) for obj in vecteurs) self.__coefficients = coefficients = tuple(Ref(obj) for obj in coefficients) Vecteur_generique.__init__(self, **styles) def _get_coordonnees(self): return sum(coeff*vecteur.x for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)), \ sum(coeff*vecteur.y for coeff, vecteur in zip(self.__coefficients, self.__vecteurs)) class Extremite(Point_generique): u"""L'extrmit d'un reprsentant de vecteur.abscisse L'objet est cr automatiquement lors de la cration du reprsentant.""" representant = __representant = Argument("Representant") def __init__(self, representant, **styles): self.__representant = representant = Ref(representant) Point_generique.__init__(self, **styles) def _get_coordonnees(self): return self.__representant._Vecteur__point2.coordonnees def _modifier_hierarchie(self, valeur = None): # Voir commentaires pour Sommet._modifier_hierarchie dans polygones.py Objet._modifier_hierarchie(self, self.__representant._hierarchie + .5) def _update(self, objet): u"""Pseudo mise jour: seul un objet identique est accept. Cela sert pour les objets crs automatiquement, qui peuvent tre enregistrs deux fois dans la feuille.""" if isinstance(objet, Extremite) and self.__representant is objet._Extremite__representant: self.style(**objet.style()) else: raise RuntimeError class Representant(Vecteur): u"""Un reprsentant d'un vecteur. Un reprsentant d'un vecteur, ayant pour origine un point donn.""" vecteur = __vecteur = Argument("Vecteur_generique") origine = __origine = Argument("Point_generique", defaut = Point) def __init__(self, vecteur, origine = None, **styles): # if origine is None: # origine = Point(0, 0) self.__vecteur = vecteur = Ref(vecteur) self.__origine = origine = Ref(origine) point2 = Point_final(origine, [vecteur]) Vecteur.__init__(self, origine, point2, **styles) self.extremite = self.__extremite = Extremite(self) # self._autres_dependances.append(point2) def _set_feuille(self): nom = self._style.get("_noms_", {"extremite": ""})["extremite"] self.__feuille__.objets[nom] = self.__extremite def __repr__(self, *args, **kwargs): self.style(_noms_ = {"extremite": self.__extremite.nom}) return Objet.__repr__(self, *args, **kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/geolib/__init__.py0000644000175000017500000001311412014170666023270 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from .angles import Angle_generique, Secteur_angulaire, Angle_oriente, Angle, Angle_libre, Angle_vectoriel from .cercles import Cercle_Arc_generique, Arc_generique, Arc_cercle, Arc_points, Arc_oriente, Demicercle, Cercle_generique, Cercle_rayon, Cercle, Cercle_diametre, Cercle_points, Cercle_equation, Disque from .classeur import Classeur from .constantes import RIEN, TEXTE, NOM, FORMULE, MILIEU, DEBUT, MILIEU, FIN from .contexte import Contexte, contexte from .courbes import Courbe_generique, Courbe from .feuille import MethodesObjets, Liste_objets, ModeTolerant, Dictionnaire_objets, Interprete_feuille, Historique_feuille, Feuille from .formules import Formule from .fonctions import Fonction from .interpolations import Interpolation_generique, Interpolation_lineaire, Interpolation_quadratique, Interpolation_cubique from .intersections import Intersection_generique, Intersection_droites, Intersection_droite_cercle, Intersection_cercles, Intersection from .labels import Label_generique, Label_point, Label_segment, Label_vecteur, Label_droite, Label_demidroite, Label_cercle, Label_arc_cercle, Label_polygone, Label_angle from .lignes import Ligne_generique, Segment, Demidroite, Droite_generique, Droite, Point_droite, Droite_vectorielle, Parallele, Perpendiculaire, Mediatrice, Droite_equation, Bissectrice, Point_tangence, Tangente, DemiPlan, Axe, Tangente_courbe from .objet import Nom, Rendu, Cache, Ref, BaseArgument, Argument, ArgumentNonModifiable, Arguments, TupleObjets, DescripteurFeuille, Objet, Objet_avec_coordonnees, Objet_avec_coordonnees_modifiables, Objet_avec_equation, Objet_avec_valeur, Objet_numerique, G, TYPES_NUMERIQUES from .points import Point_generique, Point, Point_pondere, Barycentre, Milieu, Point_final, Point_translation, Point_rotation, Point_homothetie, Point_reflexion, Projete_generique, Projete_droite, Projete_cercle, Projete_arc_cercle, Projete_segment, Projete_demidroite, Centre_polygone_generique, Centre_gravite, Orthocentre, Centre_cercle_circonscrit, Centre_cercle_inscrit, Centre, Point_equidistant, Glisseur_generique, Glisseur_vecteur, Glisseur_ligne_generique, Glisseur_droite, Glisseur_segment, Glisseur_demidroite, Glisseur_cercle, Glisseur_arc_cercle, Nuage_generique, Nuage, NuageFonction from .polyedres import Arete, Sommet_polyedre, Polyedre_generique, Tetraedre, Sommet_cube, Cube from .polygones import Cote, Sommet, Polygone_generique, Polygone, Triangle, Quadrilatere, Pentagone, Hexagone, Heptagone, Octogone, Parallelogramme, Sommet_rectangle, Rectangle, Losange, Polygone_regulier_centre, Triangle_equilateral_centre, Carre_centre, Polygone_regulier, Triangle_equilateral, Carre, Sommet_triangle_isocele, Triangle_isocele, Sommet_triangle_rectangle, Triangle_rectangle, Triangle_isocele_rectangle, PrevisualisationPolygone #from .pseudo_canvas import PseudoContexte, PseudoCanvas from .textes import Texte_generique, Texte, Texte_transformation_generique, Texte_rotation, Texte_translation, Texte_homothetie, Texte_reflexion from .transformations import Transformation_generique, Rotation, Translation, Reflexion, Homothetie, Symetrie_centrale from .variables import Variable_generique, Variable, Rayon, Mul, Add from .vecteurs import Vecteur_generique, Vecteur, Vecteur_libre, Vecteur_unitaire, Somme_vecteurs, Extremite, Representant from .widgets import Bouton for _obj in vars().values(): if isinstance(_obj, type) and issubclass(_obj, Objet): prefixe = "_" + _obj.__name__ + "__" __arguments__ = [] for key, value in vars(_obj).iteritems(): if isinstance(value, BaseArgument) and key.startswith(prefixe): # Chaque argument récupère son nom... value.nom = key # ...et sa classe de rattachement : value.rattachement = _obj # Chaque classe recupère la liste de ses arguments... (en évitant d'utiliser 'inspect', qui n'est pas compatible avec psycho) # On cherche les entrées de la classe 'MaClasse' qui soient de type 'Argument' ou 'Arguments', # et qui commencent par '_MaClasse__'. # Exemple : '_MaClasse__monargument' qui est stocké comme 'monargument' (on enlève le préfixe). __arguments__.append((value.__compteur__, key[len(prefixe):])) # on trie les arguments par ordre de déclaration dans la classe __arguments__.sort() _obj.__arguments__ = tuple(key for compteur, key in __arguments__) # tuple pour éviter des bugs (partage d'1 même liste entre plusieurs classes par ex.) del _obj G.__dict__.update(locals()) vecteur_unite = G.vecteur_unite = Vecteur_libre(1, 0) feuille_par_defaut = G.feuille_par_defaut = Feuille() wxgeometrie-0.133.2.orig/wxgeometrie/param/0000755000175000017500000000000012014170670021011 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/param/version.py0000644000175000017500000000027512014170666023061 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # Numéro de version (et date de sa sortie) version = '0.133.2' date_version = (2012, 8, 19)wxgeometrie-0.133.2.orig/wxgeometrie/param/parametres.py0000644000175000017500000004364212014170666023544 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ###################################### # # FICHIER DE CONFIGURATION # ###################################### # # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################### # Note : # eviter d'utiliser des listes au maximum, preferer les tuples. # en effet, il est souvent utile de copier les parametres. # Mais par defaut, une liste n'est pas reellement copiee (il faut faire l2 = l1[:]). # Si de plus, on utilise des listes de listes, on a vite fait d'obtenir des bugs etranges... from locale import getdefaultlocale from math import pi python_min = (2, 6) # version minimale requise # debuguage (affichage des erreurs + diverses infos) debug = True # Le logiciel est-il install ? # Cela change les rpertoires par dfaut (session, etc.) install = False # affichage ou non des avertissements warning = debug verbose = 1 # 0, 1, 2 ou 3 # terme, verbose=0 doit couper *tous* les messages (pour ne pas parasiter les tests). # => crer une fonction print() personnalise. # Indique si geolib doit afficher les diffrents messages. Dans certaines consoles (ex: pyshell), cela provoque des comportements indsirables. afficher_messages = True #TODO: retravailler ces diffrents paramtres. fermeture_instantanee = False # Utile en mode dveloppement # affichage de la ligne de commande (utile pour le dveloppement) ligne_commande = debug NOMPROG = u"WxGomtrie" NOMPROG2 = 'WxGeometrie' # sans accent GUIlib = 'wx' version_wxpython = "2.8-unicode" # Les valeurs sont les noms des paquets sous Debian/Ubuntu. dependances = {'wx': 'python-wxgtk2.8', 'matplotlib': 'python-matplotlib', 'numpy': 'python-numpy'} latex = False latex_unicode = True # (sera ventuellement dsactiv ultrieurement, aprs l'import de wx) moteur_de_rendu = 'WXAgg' # ou 'Wx' pour supprimer l'anti-crnlage # jeu de caractre utiliser encodage = getdefaultlocale()[1] or "utf8" # Utiliser pysco si disponible (JIT compiler qui acclre le code python) # True -> tout compiler : psyco.full() # None -> compilation optimise : psyco.profile() # False -> ne pas essayer d'importer psyco charger_psyco = False # Modules activs par dfaut modules_par_defaut = ( "geometre", "traceur", "statistiques", "calculatrice", "probabilites", "surfaces", ) # Multi-threading pour les macros # ------------------------------- multi_threading = False # c'est assez instable... if multi_threading is None: multi_threading = (plateforme == 'Windows') # ca ne marche pas avec le serveur X (sous Linux par ex.) # Paramtres gnraux # -------------------- utilisateur = "" # nom de l'utilisateur, pour inclure dans les documents crs dimensions_fenetre = (890, 630) confirmer_quitter = True nbr_annulations = 50 # Crer un fichier .log (conseill) historique_log = True # Modifier ce fichier en temps rel (peu utile) historique_log_continu = False # Enregistrer les messages (notamment d'erreur) dans messages.log enregistrer_messages = True # Sauver les prfrences la fermeture du programme, pour les restaurer au prochain dmarrage sauver_preferences = True # Sauver la session en cours la fermeture du programme, pour la restaurer au prochain dmarrage sauver_session = True # Paramtre utilis essentiellement en interne (quand on lance WxGometrie avec l'option --defaut) charger_preferences = True # Intervalle de temps (en dizaines de secondes) entre 2 sauvegardes automatiques sauvegarde_automatique = 2 # (Mettre 0 pour la dsactiver) tolerance = 1e-8 # seuil de tolrance, utilis en particulier par geolib pour savoir si 2 points sont confondus # ce paramtre permet un compromis acceptable entre les faux negatifs # (2 points confondus considrs tort comme distincts, du fait d'imprcisions de calculs qui se cumulent) # et les faux positifs (deux points proches considers tort comme confondus). # Parametres d'affichage # ---------------------- orthonorme = False grille_aimantee = False # force les points se placer sur le quadrillage afficher_barre_outils = False afficher_console_geolib = False afficher_boutons = True # Possibilit de ne pas afficher les boutons, pour obliger une construction pas pas. zoom_texte = 1 zoom_ligne = 1 #bouger_curseur = None #if bouger_curseur is None: # bouger_curseur = (platform.system() in ('Windows', 'Linux')) #constantes RIEN = 0 NOM = 1 TEXTE = 2 FORMULE = 3 DEBUT = 0 MILIEU = 0.5 FIN = 1 #TODO: supprimer cette section une fois qu'une partie de param sera directement intgre geolib. # Styles lis la catgorie: styles_de_lignes = ['-', '--', '-.', ':', 'None'] styles_de_points = ['+', 'x', 'o', '.', ',', '1', '2', '3', '4', '<', '>', '^', 'v', 'D', 'H', '_', '|', 'd', 'h', 'p', 's'] styles_de_textes = ["normal", "italic", "oblique"] styles_de_angles = ['-', '--', '-.', ':', 'steps', 'None'] familles_de_textes = ["sans-serif", "serif", "cursive", "fantasy", "monospace"] codage_des_lignes = ['', '/', '//', '///', 'x', 'o'] codage_des_angles = ['', '^', ')', '))', ')))', '|', '||', 'x', 'o'] # Enumre les styles 'variables' : # - soit parce qu'ils ont des significations assez diffrentes selon les objets # - soit parce qu'ils ne peuvent pas prendre les mmes valeurs suivant les objets # Ces styles ne seront copis d'un objet l'autre que s'ils appartiennent la mme catgorie styles_a_signification_variable = ("style", "codage", "famille", "taille", "angle") # Ces styles ne seront pas copis, quelque soit la catgorie de la cible styles_a_ne_pas_copier = ("categorie", "niveau", "trace", "fixe", "label", "_rayon_", "_k_", "_angle_", "_noms_", "legende") types_de_hachures = [' ', '/', '//', '\\', '\\\\', '|', '-', '+', 'x', 'o', 'O', '.', '..', '*'] # en ralit, on peut aussi les panacher... defaut_objets = { "legende": RIEN, "visible": True, "label": "", "niveau": 0, } widgets = {"bidon":1, } variables = { "visible": False, } points = { "couleur": "b", "epaisseur": 1., "style": "+", "categorie": "points", "taille": 8, "visible": True, "legende": NOM, "niveau": 6, "trace": False, } points_deplacables = { "couleur": "r", "niveau": 10, "fixe": False, } segments = { "couleur": "g", "epaisseur": 1., "style": "-", "visible": True, "niveau": 3, "categorie": "lignes", "codage": codage_des_lignes[0], } interpolations = { "couleur": "g", "epaisseur": 1., "style": "-", "visible": True, "niveau": 3, "categorie": "lignes", "codage": codage_des_lignes[0], "debut": True, "fin": True, } droites = {"couleur": "b", "epaisseur": 1., "style": "-", "visible": True, "niveau": 2, "categorie": "lignes", } courbes = {"couleur": "b", "epaisseur": 1., "style": "-", "visible": True, "niveau": 2, "categorie": "lignes", "extremites": True, "extremites_cachees": (), } vecteurs = { "couleur": "g", "epaisseur": 1., "style": "-", "taille": 10, "visible": True, "niveau": 4, "categorie": "lignes", "angle": 60, "position": FIN, "double_fleche": False, } axes = { "couleur": "k", "epaisseur": 1., "style": "-", "taille": 10, "visible": True, "niveau": .1, "categorie": "lignes", "angle": 60, "position": FIN, "double_fleche": False, } cercles = { "couleur": "b", "epaisseur": 1., "style": "-", "visible": True, "niveau": 1, "categorie": "lignes", } arcs = { "couleur": "b", "epaisseur": 1., "style": "-", "visible": True, "niveau": 1, "categorie": "lignes", "codage": codage_des_lignes[0], } arcs_orientes = { "couleur": "g", "epaisseur": 1., "style": "-", "taille": 10, "visible": True, "niveau": 4, "categorie": "lignes", "angle": 60, "position": FIN, "double_fleche": False, } polygones = { "couleur": "y", "epaisseur": 1., "style": "-", "visible": True, "alpha": .2, "niveau": 0.1, "hachures": types_de_hachures[0], "categorie": "lignes", } cotes = { "couleur": "y", "epaisseur": 1., "style": "-", "visible": True, "alpha": 1, "niveau": 0, "categorie": "lignes", "codage": codage_des_lignes[0], } polyedres = { "couleur": "y", "epaisseur": 1., "style": "None", "visible": True, "alpha": .2, "niveau": 0, "hachures": types_de_hachures[0], "categorie": "lignes", } aretes = { "couleur": "y", "epaisseur": 1., "style": "-", "visible": True, "niveau": 0.5, "categorie": "lignes", } textes = { "couleur": "k", "epaisseur": 5, # 1 -> 9 #"largeur": ("normal", "narrow", "condensed", "wide")[0], # mal gere par matploltib (version 0.87) "taille": 18., "style": "normal", "famille": familles_de_textes[1], "visible": True, "angle": 0, "legende": TEXTE, "fixe": False, "categorie": "textes", "niveau": 7, "alignement_vertical": "center", "alignement_horizontal": "center", "alpha": 1, } boutons = { "couleur": 'k', "couleur_fond": (.3, .8, .9), "epaisseur": 1, "taille": 18., "style": "italic", "visible": True, "angle": 0, "legende": TEXTE, "fixe": True, "categorie": "widgets", "niveau": 10, "alignement_vertical": "center", "alignement_horizontal": "center", "marge": 3, } labels = { "couleur": "k", "epaisseur": 6, # 1 -> 9 #"largeur": ("normal", "narrow", "condensed", "wide")[0], # mal gere par matploltib (version 0.87) "taille": 18., "style": "normal", "famille": familles_de_textes[1], "visible": True, "angle": 0, "legende": TEXTE, "fixe": False, "categorie": "textes", "niveau": 7, "alignement_vertical": "bottom", "alignement_horizontal": "left", } angles = { "couleur": "b", "epaisseur": 1., "style": "-", "visible": True, "niveau": 5, "categorie": "angles", "codage": codage_des_angles[0], "alpha": .2, } # le parametre niveau est utilis pour dtecter l'objet sur la feuille : # en cas de conflit entre deux objets proches, il permet de savoir l'objet slectionner. # les petits objets (points, ...) doivent tre au dessus des plus gros, et avoir un niveau suprieur. # Rq: les objets modifiables devraient etre "au dessus" des autres, indpendamment de leur taille. # Attention, ce parametre n'influe pas sur l'affichage des objets (pour cela, c'est le parametre "zorder" de matplotlib) # le parametre "categorie" indique quelle liste de styles est appliquer. # par dfaut, on applique souvent 'styles_de_lignes'. del NOM, FORMULE, TEXTE, RIEN, DEBUT, MILIEU, FIN # Options de style pour les objets geometriques : # couleur: str # epaisseur: float # taille: float # style: str # visible: bool # fixe: bool # label: str # extra: dict # 'extra' est un dictionnaire d'options de matplotlib # attention, ces options seront appliquees a toutes les composantes (plot, fill, text) de la representation graphique codage = {"angle": 60, "taille": 6, "rayon": 20} codage_automatique_angle_droits = True # taille de differents elements graphiques taille = {"o" : 3, "(" : 5, ">" : 10, "|" : 8} # Distance maximale entre une etiquette et son objet associ : distance_max_etiquette = 50 # DSUET : ##chiffres_significatifs = 4 # intervient dans l'affichage des proprits des objets essentiellement. decimales = 2 # Note: en interne, ce sont toujours les radians qui sont utiliss. unite_angle = ("r", "d", "g")[0] # radian, degr ou grad liste_axes = (0, 1) # 0 -> axe des abscisses ; 1 -> axe des ordonnees afficher_axes = True afficher_fleches = True # fleches au bout des axes. # Pour se reperer, deux possibilites : # 1. Un repere proprement dit repere = ("O", "I", "J") # en majuscule, ce sont des points ; en minuscule, des vecteurs. # 2. Deux axes gradues origine_axes = (0, 0) # Pour choisir le mode : utiliser_repere = True # -> utiliser ou non un repre (mme origine sur chaque axe...) # -> si on n'utilise pas le repre, on affiche deux valeurs distinctes l'intersection des axes gradu = (1, 1) # espacement entre deux graduations pour chaque axe. (0 => pas de graduations). saturation = 0.3 # valeur de saturation pour le coefficient pas()/gradu # au dela du coefficient de saturation, les graduations ne sont plus affichees # cela evite de bloquer le programme... # Nombre de cm pour une unit en export echelle_cm = (1, 1) # Couleur de fond des figures. couleur_fond = "w" afficher_quadrillage = True # affiche un quadrillage a la place des graduations quadrillages = (((None, None), ":", .5, "k"),) # Chaque couple correspond a un quadrillage. # Le format est : ([espacement horizontal, espacement vertical], style, epaisseur, couleur). # On peut omettre des elements : # quadrillages = ((),) # Si un espacement vaut None, la valeur de gradu correspondante sera utilisee. # Valeurs possibles pour le style : (cf http://matplotlib.sourceforge.net/matplotlib.pylab.html#-plot) # "--" tirets # "-" lignes continues # ":" points # "-." alternance de points et de tirets # On peut superposer plusieurs quadrillages : # quadrillages = (((1, 1), "-", 0.25, "k"), ((0.25, 0.25), ":", .25, "k"),) # ou encore : # quadrillages = (((1, 1), ":", 1, "k"), ((0.2, 0.2), ":", .125, "k"),) couleur_papier_millimetre = '#aa7733' # couleur utiliser pour le papier millimtr entre autres resolution = 1000 # resolution utilisee pour le tracage des courbes (plus la valeur est importante, plus la courbe est lisse) fenetre = (origine_axes[0]-8, origine_axes[0]+8, origine_axes[1]-5, origine_axes[1]+5) # xmin, xmax, ymin, ymax zoom_in = 1.1 # coefficient d'agrandissement des objets lors d'un zoom. zoom_out = 0.9 # coefficient de reduction des objets lors d'un zoom. zoom_texte_in = 1.05 zoom_texte_out = 0.95 zoom_ligne_in = 1.07 zoom_ligne_out = 0.93 dpi_ecran = 80 # Tableau rcapitulatif des rsolutions les plus courantes : # http://forum.notebookreview.com/notebook-dummy-guide-articles/124093-guide-screen-sizes-dots-per-inch-dpi.html dpi_export = 200 # resolution utilisee lors de l'exportation des images compresser_geo = False # compresser les fichiers .geo par dfaut afficher_coordonnees = True # affiche en permanence les coordonnees afficher_pixels = False # pour dbogage (mettre afficher_coordonnes False prcdemment) afficher_objets_caches = False # affiche en gris les objets masqus precision_selection = 10 # plus la precision est importante, plus il faut etre pres d'un objet pour pouvoir le saisir # Si le chiffre est trop petit, ca devient fastidieux de selectionner un objet # A l'inverse, s'il est trop grand, il devient difficile de selectionner celui qu'on veut entre deux objets proches nom_multiple = False # autorise 'nommer' plusieurs objets avec le mme nom # (en fait, un seul objet aura le nom 'A' par exemple, les autres auront 'A' comme tiquette). # ------------------------------ # Code rcrire # Tranformation linaire applique au graphique : (EXPERIMENTAL !) transformation = None # Couple de la forme (a, b, c, d) tel que : # |x'| |a b| |x| # |y'| = |c d| |y| # Exemple (permutation des axes) : #transformation = (0, 1, 1, 1) # ------------------------------ afficher_barre_outils = False afficher_console_geolib = False # Tenter la virgule comme sparateur si le point a chou, et vice-versa. # ( dsactiver pour faciliter le dbogage.) adapter_separateur = True # Rpertoires par dfaut # ---------------------- # Rpertoire o on sauve les fichiers par dfaut rep_save = None # rep_save = repertoire # Rpertoire o on ouvre les fichiers par dfaut rep_open = None # rep_open = repertoire # Rpertoire o on exporte les fichiers par dfaut rep_export = None # rep_export = repertoire emplacements = {} taille_max_log = 10000 # taille max du fichier de log (en Ko) taille_max_log *= 1024 try: from .personnaliser import * # permet de gnrer un fichier personnaliser.py lors de l'installation, ou de la premire utilisation, et dont les parametres remplaceront ceux-ci. except ImportError: try: from .personnaliser_ import * except ImportError: pass if install: # Les prfrences, fichiers log, etc... sont stocks dans le dossier de l'utilisateur. # ~ se rfre au rpertoire de l'utilisateur (ex: /home/BillG/ sous Linux, ou C:\Documents and Settings\LTorvald\ sous Windows) emplacements.setdefault("log", "~/.wxgeometrie/log") emplacements.setdefault("preferences", "~/.wxgeometrie/preferences") emplacements.setdefault("macros", "~/.wxgeometrie/macros") emplacements.setdefault("session", "~/.wxgeometrie/session") else: # Utilisation sans installation. Tout est stock directement dans le dossier wxgeometrie/. # % se rfre au dossier contenant WxGeometrie (indiqu par param.EMPLACEMENT) emplacements.setdefault("log", "%/log") # dans log/ par dfaut emplacements.setdefault("preferences", "%/preferences") # dans preferences/ par dfaut emplacements.setdefault("macros", "%/macros") # dans macros/ par dfaut emplacements.setdefault("session", "%/session") # dans session/ par dfaut print(u'Import des paramtres termin.') wxgeometrie-0.133.2.orig/wxgeometrie/param/modules.py0000644000175000017500000000561212014170666023044 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ###################################### # # Dtection des modules # ###################################### # # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################### import os from .parametres import modules_par_defaut # Modules a importer # ---------------- _skip_dir = ('OLD',) def _detecter_modules(): modules_dir = os.path.normpath(os.path.join(__file__, '..', '..', 'modules')) modules = [] descriptions = {} def err(nom, msg): print("Warning: %s n'est pas un module valide (%s)." %(nom, msg)) for nom in os.listdir(modules_dir): if nom not in _skip_dir and os.path.isdir(os.path.join(modules_dir, nom)): description_file = os.path.join(modules_dir, nom, 'description.py') if os.path.isfile(description_file): try: compile(nom + '=0', '', 'single') # On teste si le nom est valide try: d = {} execfile(description_file, d) modules.append(nom) descriptions[nom] = d['description'] except: err(nom, u"fichier '%s' incorrect" %description_file) except Exception: err(nom, u"nom de module invalide") else: err(nom, u"fichier 'description.py' introuvable") return modules, descriptions try: modules, descriptions_modules = _detecter_modules() except OSError: print("Warning: impossible de dtecter les modules !") modules = [] descriptions_modules = {} modules_actifs = dict.fromkeys(modules, False) for nom in modules_par_defaut: modules_actifs[nom] = True def _key(nom): # les modules activs par dfaut apparaissent en premier, # les autres sont classs par ordre alphabtique. key = [1000000, nom] if nom in modules_par_defaut: key[0] = modules_par_defaut.index(nom) return key modules.sort(key = _key) wxgeometrie-0.133.2.orig/wxgeometrie/param/options.py0000644000175000017500000001111612014170666023063 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################### # bool -> CheckBox # file -> slectionner un rpertoire # str -> TextCtrl # (min, max) -> SpinCtrl # [bool] -> CheckListBox # ['item1', 'blabla2', ...] -> Choice from copy import deepcopy from .modules import modules as _modules, descriptions_modules class Rubrique(list): def __init__(self, titre): self.titre = titre list.__init__(self) def add(self, value): list.append(self, value) return value class Options(Rubrique): pass class Theme(Rubrique): pass class Section(Rubrique): pass class Parametre(object): def __init__(self, _texte, _get = (lambda x:x), _set = (lambda x:x), **kw): assert len(kw) == 1 self.nom, self.type = kw.popitem() if '__' in self.nom: self.prefixe, self.key = self.nom.split('__', 1) else: self.prefixe = self.nom self.key = None self._get = _get self._set = _set self.defaut = deepcopy(self.valeur) self.texte = _texte def _get_val(self): from .. import param if self.key is None: val = getattr(param, self.nom) else: val = getattr(param, self.prefixe)[self.key] return self._get(val) def _set_val(self, val): from .. import param val = self._set(val) if self.key is None: setattr(param, self.nom, val) else: getattr(param, self.prefixe)[self.key] = val valeur = property(_get_val, _set_val) P = Parametre options = Options(u'Prfrences') ## GENERAL general = options.add(Theme(u'Gnral')) general.add(P(u'Utilisateur', utilisateur = str)) general.add(P(u"Nombre maximal d'annulations", nbr_annulations = (0, 1000))) fermeture = general.add(Section(u' la fermeture')) fermeture.add(P(u'Demander confirmation avant de quitter.', confirmer_quitter = bool)) fermeture.add(P(u'Sauvegarder les prfrences.', sauver_preferences = bool)) fermeture.add(P(u'Sauvegarder la session en cours.', sauver_session = bool)) auto = general.add(Section(u'Sauvegarde automatique')) auto.add(P(u'Intervalle entre deux sauvegardes', sauvegarde_automatique = (0, 10000))) auto.add(u'Temps (en dizaine de s) entre deux sauvegardes automatiques.') auto.add(u'La valeur 0 dsactive la sauvegarde automatique.') ## MODULES modules = options.add(Theme(u'Modules')) liste = modules.add(Section(u'Activer les modules suivants')) for nom in _modules: d = {'modules_actifs__' + nom: bool} liste.add(P(descriptions_modules[nom]['titre'], **d)) modules.add(u'Nota: les modules non activs par dfaut peuvent tre non documents\net/ou encore exprimentaux.') #modules.add(P(u'Activer les modules suivants', modules_actifs = dict)) ## FORMAT format = options.add(Theme(u'Format')) format.add(P(u'Dcimales affiches', decimales = (0, 10))) format.add(P(u'Unit d\'angle', _get = (lambda k:{'d':u'degr', 'r':'radian', 'g':'grade'}[k]), _set = (lambda s:s[0]), unite_angle = [u'degr', 'radian', 'grade'] )) ## AVANC avance = options.add(Theme(u'Avanc')) export = avance.add(Section(u"Export")) export.add(P(u"Rsolution des images PNG", dpi_export = (10, 10000))) sauvegarde = avance.add(Section(u"Sauvegarde")) sauvegarde.add(P(u"Compresser les fichiers .geo par dfaut.", compresser_geo = bool)) empl_pref = avance.add(Section(u"Rpertoires d'enregistrement")) empl_pref.add(P(u"Prfrences", emplacements__preferences = file)) empl_pref.add(P(u"Session", emplacements__session = file)) empl_pref.add(P(u"Rapports d'erreur", emplacements__log = file)) wxgeometrie-0.133.2.orig/wxgeometrie/param/personnaliser.py0000644000175000017500000000247512014170666024264 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ############################################## # # FICHIER DE CONFIGURATION PERSONNALISE # ############################################## # # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ############################################## from math import pi # Placez ici les modifications que vous souhaitez apporter au fichier de paramétrage. debug = False #install = Truewxgeometrie-0.133.2.orig/wxgeometrie/param/__init__.py0000644000175000017500000000635312014170666023136 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ###################################### # # Paramtres du programme # ###################################### # # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ###################################### from time import mktime import sys, platform, os from .version import version, date_version from .modules import modules, modules_actifs, descriptions_modules from .parametres import * time_version = mktime(date_version + (0, 0, 0) + (0, 0, 0)) # Dernire vrification d'une ventuelle mise jour time_verification = time_version # Dtection de la configuration plateforme = platform.system() #'Windows' ou 'Linux' par exemple. repertoire = os.getcwd() # memorise le repertoire de lancement python_version = float(sys.version[:3]) python_version_info = sys.version_info py2exe = hasattr(sys, 'frozen') # le programme tourne-t-il en version "executable" ? # Paramtres dtects dynamiquement lors de l'excution : ecriture_possible = None EMPLACEMENT = '' # le rpertoire contenant wxgomtrie.pyw # Les valeurs suivantes ne doivent pas tre enregistres dans les prfrences de l'utilisateur : # - soit parce qu'il n'y aurait aucun sens les sauver (__builtins__ par exemple) # - soit parce qu'elles doivent tre gnres dynamiquement valeurs_a_ne_pas_sauver = ( "valeurs_a_ne_pas_sauver", "os", "platform", "__builtins__", "python_version", "python_min", "getdefaultlocale", "pi", "python_version", "version", "date_version", "time_version", "plateforme", "repertoire", "py2exe", "EMPLACEMENT", "emplacements" "a_mettre_a_jour", "modules", "descriptions_modules", "ecriture_possible", "charger_preferences", "types_de_hachures", "styles_de_lignes", "styles_de_points", "styles_de_textes", "styles_de_angles", "familles_de_textes", "codage_des_lignes", "codage_des_angles", "styles_a_signification_variable", "styles_a_ne_pas_copier", "dependances", "NOMPROG", "NOMPROG2", "GUIlib", ) # IMPORTANT ! # les dictionnaires pouvant comporter de nouvelles cls lors de la sortie d'une nouvelle version doivent tre mis jour : a_mettre_a_jour = ( "defaut_objets", "widgets", "points", "points_deplacables", "segments", "droites", "vecteurs", "cercles", "arcs", "arcs_orientes", "cotes", "polygones", "textes", "labels", "aretes", "polyedres", "courbes", "angles", "codage", "taille", "modules_actifs", ) del os, platform, sys print(u'Import des paramtres termin.') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/0000755000175000017500000000000012014170666021077 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/matrices/0000755000175000017500000000000012014170666022706 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/matrices/tests/0000755000175000017500000000000012014170666024050 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/matrices/tests/test_matrices.py0000644000175000017500000015272312014170666027302 0ustar georgeskgeorgeskfrom sympy import (symbols, Matrix, SparseMatrix, eye, I, Symbol, Rational, Float, wronskian, cos, sin, exp, hessian, sqrt, zeros, ones, randMatrix, Poly, S, pi, E, I, oo, trigsimp, Integer, block_diag, N, zeros, sympify, Pow, simplify, Min, Max, Abs) from sympy.matrices.matrices import (ShapeError, MatrixError, matrix_multiply_elementwise, diag, SparseMatrix, SparseMatrix, NonSquareMatrixError, _dims_to_nm, matrix_multiply_elementwise) from sympy.utilities.pytest import raises #from sympy.functions.elementary.miscellaneous import Max, Min #from sympy.functions.elementary.miscellaneous import Max, Min def test_division(): x, y, z = symbols('x y z') v = Matrix(1,2,[x, y]) assert v.__div__(z) == Matrix(1,2,[x/z, y/z]) assert v.__truediv__(z) == Matrix(1,2,[x/z, y/z]) assert v/z == Matrix(1,2,[x/z, y/z]) def test_sum(): x, y, z = symbols('x y z') m = Matrix([[1,2,3],[x,y,x],[2*y,-50,z*x]]) assert m+m == Matrix([[2,4,6],[2*x,2*y,2*x],[4*y,-100,2*z*x]]) def test_multiplication(): a=Matrix(( (1, 2), (3, 1), (0, 6), )) b = Matrix (( (1, 2), (3, 0), )) c= a*b assert c[0,0]==7 assert c[0,1]==2 assert c[1,0]==6 assert c[1,1]==6 assert c[2,0]==18 assert c[2,1]==0 h = matrix_multiply_elementwise(a, c) assert h == a.multiply_elementwise(c) assert h[0,0]==7 assert h[0,1]==4 assert h[1,0]==18 assert h[1,1]==6 assert h[2,0]==0 assert h[2,1]==0 raises(ShapeError, 'matrix_multiply_elementwise(a, b)') x = Symbol("x") c = b * Symbol("x") assert isinstance(c,Matrix) assert c[0,0] == x assert c[0,1] == 2*x assert c[1,0] == 3*x assert c[1,1] == 0 c2 = x * b assert c == c2 c = 5 * b assert isinstance(c,Matrix) assert c[0,0] == 5 assert c[0,1] == 2*5 assert c[1,0] == 3*5 assert c[1,1] == 0 def test_power(): A = Matrix([[2,3],[4,5]]) assert (A**5)[:] == [6140, 8097, 10796, 14237] A = Matrix([[2, 1, 3],[4,2, 4], [6,12, 1]]) assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433] assert A**0 == eye(3) assert A**1 == A assert (Matrix([[2]]) ** 100)[0,0] == 2**100 assert eye(2)**10000000 == eye(2) assert Matrix([[1, 2], [3, 4]])**Integer(2) == Matrix([[7, 10], [15, 22]]) A = Matrix([[33, 24], [48, 57]]) assert (A**(S(1)/2))[:] == [5, 2, 4, 7] A = Matrix([[0, 4], [-1, 5]]) assert (A**(S(1)/2))**2 == A def test_creation(): raises(ValueError, 'Matrix(5, 5, range(20))') x = Symbol("x") a = Matrix([[x, 0], [0, 0]]) m = a assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x,0,0,0] b = Matrix(2,2, [x, 0, 0, 0]) m = b assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x,0,0,0] assert a == b assert Matrix(b) == b c = Matrix(( Matrix(( (1, 2, 3), (4, 5, 6) )), (7, 8, 9) )) assert c.cols == 3 assert c.rows == 3 assert c[:] == [1,2,3,4,5,6,7,8,9] def test_tolist(): x, y, z = symbols('x y z') lst = [[S.One,S.Half,x*y,S.Zero],[x,y,z,x**2],[y,-S.One,z*x,3]] m = Matrix(lst) assert m.tolist() == lst def test_determinant(): x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix((1,)) assert M.det(method="bareis") == 1 assert M.det(method="berkowitz") == 1 M = Matrix(( (-3, 2), ( 8, -5) )) assert M.det(method="bareis") == -1 assert M.det(method="berkowitz") == -1 M = Matrix(( (x, 1), (y, 2*y) )) assert M.det(method="bareis") == 2*x*y-y assert M.det(method="berkowitz") == 2*x*y-y M = Matrix(( (1, 1, 1), (1, 2, 3), (1, 3, 6) )) assert M.det(method="bareis") == 1 assert M.det(method="berkowitz") == 1 M = Matrix(( ( 3, -2, 0, 5), (-2, 1, -2, 2), ( 0, -2, 5, 0), ( 5, 0, 3, 4) )) assert M.det(method="bareis") == -289 assert M.det(method="berkowitz") == -289 M = Matrix(( ( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16) )) assert M.det(method="bareis") == 0 assert M.det(method="berkowitz") == 0 M = Matrix(( (3, 2, 0, 0, 0), (0, 3, 2, 0, 0), (0, 0, 3, 2, 0), (0, 0, 0, 3, 2), (2, 0, 0, 0, 3) )) assert M.det(method="bareis") == 275 assert M.det(method="berkowitz") == 275 M = Matrix(( (1, 0, 1, 2, 12), (2, 0, 1, 1, 4), (2, 1, 1, -1, 3), (3, 2, -1, 1, 8), (1, 1, 1, 0, 6) )) assert M.det(method="bareis") == -55 assert M.det(method="berkowitz") == -55 M = Matrix(( (-5, 2, 3, 4, 5), ( 1, -4, 3, 4, 5), ( 1, 2, -3, 4, 5), ( 1, 2, 3, -2, 5), ( 1, 2, 3, 4, -1) )) assert M.det(method="bareis") == 11664 assert M.det(method="berkowitz") == 11664 M = Matrix(( ( 2, 7, -1, 3, 2), ( 0, 0, 1, 0, 1), (-2, 0, 7, 0, 2), (-3, -2, 4, 5, 3), ( 1, 0, 0, 0, 1) )) assert M.det(method="bareis") == 123 assert M.det(method="berkowitz") == 123 M = Matrix(( (x,y,z), (1,0,0), (y,z,x) )) assert M.det(method="bareis") == z**2 - x*y assert M.det(method="berkowitz") == z**2 - x*y def test_submatrix(): m0 = eye(4) assert m0[0:3, 0:3] == eye(3) assert m0[2:4, 0:2] == zeros(2) m1 = Matrix(3,3, lambda i,j: i+j) assert m1[0,:] == Matrix(1,3,(0,1,2)) assert m1[1:3, 1] == Matrix(2,1,(2,3)) m2 = Matrix([[0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]]) assert m2[:,-1] == Matrix(4,1,[3,7,11,15]) assert m2[-2:,:] == Matrix([[8,9,10,11],[12,13,14,15]]) def test_submatrix_assignment(): m = zeros(4) m[2:4, 2:4] = eye(2) assert m == Matrix(((0,0,0,0), (0,0,0,0), (0,0,1,0), (0,0,0,1))) m[0:2, 0:2] = eye(2) assert m == eye(4) m[:,0] = Matrix(4,1,(1,2,3,4)) assert m == Matrix(((1,0,0,0), (2,1,0,0), (3,0,1,0), (4,0,0,1))) m[:,:] = zeros(4) assert m == zeros(4) m[:,:] = ((1,2,3,4),(5,6,7,8),(9, 10, 11, 12),(13,14,15,16)) assert m == Matrix(((1,2,3,4), (5,6,7,8), (9, 10, 11, 12), (13,14,15,16))) m[0:2, 0] = [0,0] assert m == Matrix(((0,2,3,4), (0,6,7,8), (9, 10, 11, 12), (13,14,15,16))) def test_extract(): m = Matrix(4, 3, lambda i, j: i*3 + j) assert m.extract([0,1,3],[0,1]) == Matrix(3,2,[0,1,3,4,9,10]) assert m.extract([0,3],[0,0,2]) == Matrix(2,3,[0,0,2,9,9,11]) assert m.extract(range(4),range(3)) == m raises(IndexError, 'm.extract([4], [0])') raises(IndexError, 'm.extract([0], [3])') def test_reshape(): m0 = eye(3) assert m0.reshape(1,9) == Matrix(1,9,(1,0,0,0,1,0,0,0,1)) m1 = Matrix(3,4, lambda i,j: i+j) assert m1.reshape(4,3) == Matrix(((0,1,2), (3,1,2), (3,4,2), (3,4,5))) assert m1.reshape(2,6) == Matrix(((0,1,2,3,1,2), (3,4,2,3,4,5))) def test_applyfunc(): m0 = eye(3) assert m0.applyfunc(lambda x:2*x) == eye(3)*2 assert m0.applyfunc(lambda x: 0 ) == zeros(3) def test_expand(): x,y = symbols('x y') m0 = Matrix([[x*(x+y),2],[((x+y)*y)*x,x*(y+x*(x+y))]]) # Test if expand() returns a matrix m1 = m0.expand() assert m1 == Matrix([[x*y+x**2,2],[x*y**2+y*x**2,x*y+y*x**2+x**3]]) def test_random(): M = randMatrix(3,3) M = randMatrix(3,3,seed=3) M = randMatrix(3,4,0,150) def test_LUdecomp(): testmat = Matrix([[0,2,5,3], [3,3,7,4], [8,4,0,2], [-2,6,3,4]]) L,U,p = testmat.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-testmat == zeros(4) testmat = Matrix([[6,-2,7,4], [0,3,6,7], [1,-2,7,4], [-9,2,6,3]]) L,U,p = testmat.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-testmat == zeros(4) x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix(((1, x, 1), (2, y, 0), (y, 0, z))) L, U, p = M.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-M == zeros(3) mL = Matrix(( (1,0,0), (2,3,0), )) assert mL.is_lower() == True assert mL.is_upper() == False mU = Matrix(( (1,2,3), (0,4,5), )) assert mU.is_lower() == False assert mU.is_upper() == True # test FF LUdecomp M = Matrix([[1, 3, 3], [3, 2, 6], [3, 2, 2]]) P, L, Dee, U = M.LUdecompositionFF() assert P*M == L*Dee.inv()*U M = Matrix([[1, 2, 3, 4], [3, -1, 2, 3], [3, 1, 3, -2], [6, -1, 0, 2]]) P, L, Dee, U = M.LUdecompositionFF() assert P*M == L*Dee.inv()*U M = Matrix([[0, 0, 1], [2,3,0], [3, 1, 4]]) P, L, Dee, U = M.LUdecompositionFF() assert P*M == L*Dee.inv()*U def test_LUsolve(): A = Matrix([[2,3,5], [3,6,2], [8,3,6]]) x = Matrix(3,1,[3,7,5]) b = A*x soln = A.LUsolve(b) assert soln == x A = Matrix([[0,-1,2], [5,10,7], [8,3,4]]) x = Matrix(3,1,[-1,2,5]) b = A*x soln = A.LUsolve(b) assert soln == x def test_QRsolve(): A = Matrix([[2,3,5], [3,6,2], [8,3,6]]) x = Matrix(3,1,[3,7,5]) b = A*x soln = A.QRsolve(b) assert soln == x x = Matrix([[1,2],[3,4],[5,6]]) b = A*x soln = A.QRsolve(b) assert soln == x A = Matrix([[0,-1,2], [5,10,7], [8,3,4]]) x = Matrix(3,1,[-1,2,5]) b = A*x soln = A.QRsolve(b) assert soln == x x = Matrix([[7,8],[9,10],[11,12]]) b = A*x soln = A.QRsolve(b) assert soln == x def test_inverse(): A = eye(4) assert A.inv() == eye(4) assert A.inv("LU") == eye(4) assert A.inv("ADJ") == eye(4) A = Matrix([[2,3,5], [3,6,2], [8,3,6]]) Ainv = A.inv() assert A*Ainv == eye(3) assert A.inv("LU") == Ainv assert A.inv("ADJ") == Ainv def test_util(): v1 = Matrix(1,3,[1,2,3]) v2 = Matrix(1,3,[3,4,5]) assert v1.cross(v2) == Matrix(1,3,[-2,4,-2]) assert v1.norm() == sqrt(14) # cofactor assert eye(3) == eye(3).cofactorMatrix() test = Matrix([[1,3,2],[2,6,3],[2,3,6]]) assert test.cofactorMatrix() == Matrix([[27,-6,-6],[-12,2,3],[-3,1,0]]) test = Matrix([[1,2,3],[4,5,6],[7,8,9]]) assert test.cofactorMatrix() == Matrix([[-3,6,-3],[6,-12,6],[-3,6,-3]]) def test_jacobian_hessian(): x = Symbol('x') y = Symbol('y') L = Matrix(1,2,[x**2*y, 2*y**2 + x*y]) syms = [x,y] assert L.jacobian(syms) == Matrix([[2*x*y, x**2],[y, 4*y+x]]) L = Matrix(1,2,[x, x**2*y**3]) assert L.jacobian(syms) == Matrix([[1, 0], [2*x*y**3, x**2*3*y**2]]) f = x**2*y syms = [x,y] assert hessian(f, syms) == Matrix([[2*y, 2*x], [2*x, 0]]) f = x**2*y**3 assert hessian(f, syms) == Matrix([[2*y**3, 6*x*y**2],[6*x*y**2, 6*x**2*y]]) def test_QR(): A = Matrix([[1,2],[2,3]]) Q, S = A.QRdecomposition() R = Rational assert Q == Matrix([[5**R(-1,2), (R(2)/5)*(R(1)/5)**R(-1,2)], [2*5**R(-1,2), (-R(1)/5)*(R(1)/5)**R(-1,2)]]) assert S == Matrix([[5**R(1,2), 8*5**R(-1,2)], [0, (R(1)/5)**R(1,2)]]) assert Q*S == A assert Q.T * Q == eye(2) A = Matrix([[1,1,1],[1,1,3],[2,3,4]]) Q, R = A.QRdecomposition() assert Q.T * Q == eye(Q.cols) assert R.is_upper() assert A == Q*R def test_QR_non_square(): A = Matrix([[9,0,26],[12,0,-7],[0,4,4],[0,-3,-3]]) Q, R = A.QRdecomposition() assert Q.T * Q == eye(Q.cols) assert R.is_upper() assert A == Q*R A = Matrix([[1,-1,4],[1,4,-2],[1,4,2],[1,-1,0]]) Q, R = A.QRdecomposition() assert Q.T * Q == eye(Q.cols) assert R.is_upper() assert A == Q*R def test_nullspace(): # first test reduced row-ech form R = Rational M = Matrix([[5,7,2,1], [1,6,2,-1]]) out, tmp = M.rref() assert out == Matrix([[1,0,-R(2)/23,R(13)/23], [0,1,R(8)/23, R(-6)/23]]) M = Matrix([[-5,-1, 4,-3,-1], [ 1,-1,-1, 1, 0], [-1, 0, 0, 0, 0], [ 4, 1,-4, 3, 1], [-2, 0, 2,-2,-1]]) assert M*M.nullspace()[0] == Matrix(5,1,[0]*5) M = Matrix([[1,3,0,2,6,3,1], [-2,-6,0,-2,-8,3,1], [3,9,0,0,6,6,2], [-1,-3,0,1,0,9,3]]) out, tmp = M.rref() assert out == Matrix([[1,3,0,0,2,0,0], [0,0,0,1,2,0,0], [0,0,0,0,0,1,R(1)/3], [0,0,0,0,0,0,0]]) # now check the vectors basis = M.nullspace() assert basis[0] == Matrix([-3,1,0,0,0,0,0]) assert basis[1] == Matrix([0,0,1,0,0,0,0]) assert basis[2] == Matrix([-2,0,0,-2,1,0,0]) assert basis[3] == Matrix([0,0,0,0,0,R(-1)/3, 1]) # issue 1698; just see that we can do it when rows > cols M = Matrix([[1,2],[2,4],[3,6]]) assert M.nullspace() def test_wronskian(): x = Symbol('x') assert wronskian([cos(x), sin(x)], x) == cos(x)**2 + sin(x)**2 assert wronskian([exp(x), exp(2*x)], x) == exp(3*x) assert wronskian([exp(x), x], x) == exp(x) - x*exp(x) assert wronskian([1, x, x**2], x) == 2 w1 = -6*exp(x)*sin(x)*x + 6*cos(x)*exp(x)*x**2 - 6*exp(x)*cos(x)*x - \ exp(x)*cos(x)*x**3 + exp(x)*sin(x)*x**3 assert wronskian([exp(x), cos(x), x**3], x).expand() == w1 assert wronskian([exp(x), cos(x), x**3], x, method='berkowitz').expand() == w1 w2 = -x**3*cos(x)**2 - x**3*sin(x)**2 - 6*x*cos(x)**2 - 6*x*sin(x)**2 assert wronskian([sin(x), cos(x), x**3], x).expand() == w2 assert wronskian([sin(x), cos(x), x**3], x, \ method='berkowitz').expand() == w2 def canonicalize(v): """ Takes the output of eigenvects() and makes it canonical, so that we can compare it across platforms. It converts Matrices to lists, and uses set() to list the outer list in a platform independent way. """ def c(x): a, b, c = x return (S(a), S(b), tuple(c[0])) return tuple(set([c(x) for x in v])) def test_eigen(): x,y = symbols('x y') R = Rational assert eye(3).charpoly(x) == Poly((x-1)**3, x) assert eye(3).charpoly(y) == Poly((y-1)**3, y) M = Matrix([[1,0,0], [0,1,0], [0,0,1]]) assert M.eigenvals() == {S.One: 3} assert canonicalize(M.eigenvects()) == canonicalize( [(1, 3, [Matrix([1,0,0]), Matrix([0,1,0]), Matrix([0,0,1])])]) M = Matrix([[0,1,1], [1,0,0], [1,1,1]]) assert M.eigenvals() == {2*S.One: 1, -S.One: 1, S.Zero: 1} assert canonicalize(M.eigenvects()) == canonicalize( [( 2, 1, [Matrix([R(2,3), R(1,3), 1])]), (-1, 1, [Matrix([-1, 1, 0])]), ( 0, 1, [Matrix([ 0,-1, 1])])]) M = Matrix([ [1, -1], [1, 3]]) assert canonicalize(M.eigenvects()) == canonicalize( [[2, 2, [Matrix(1,2,[-1,1])]]]) M = Matrix([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]) a=R(15,2) b=3*33**R(1,2) c=R(13,2) d=(R(33,8) + 3*b/8) e=(R(33,8) - 3*b/8) def NS(e, n): return str(N(e, n)) r = [ (a - b/2, 1, [Matrix([(12 + 24/(c - b/2))/((c - b/2)*e) + 3/(c - b/2), (6 + 12/(c - b/2))/e,1])]), ( 0, 1, [Matrix([1,-2,1])]), (a + b/2, 1, [Matrix([(12 + 24/(c + b/2))/((c + b/2)*d) + 3/(c + b/2), (6 + 12/(c + b/2))/d,1])]), ] r1 = [(NS(r[i][0],2),NS(r[i][1],2),[NS(j,2) for j in r[i][2][0]]) for i in range(len(r))] r = M.eigenvects() r2=[(NS(r[i][0],2),NS(r[i][1],2),[NS(j,2) for j in r[i][2][0]]) for i in range(len(r))] assert sorted(r1) == sorted(r2) eps = Symbol('eps',real=True) M = Matrix([[abs(eps), I*eps ], [-I*eps, abs(eps) ]]) assert canonicalize(M.eigenvects()) == canonicalize( [( 2*abs(eps), 1, [ Matrix([[I*eps/abs(eps)],[1]]) ] ), ( 0, 1, [Matrix([[-I*eps/abs(eps)],[1]])]) ]) def test_sparse_matrix(): return def eye(n): tmp = SparseMatrix(n,n,lambda i,j:0) for i in range(tmp.rows): tmp[i,i] = 1 return tmp def zeros(n): return SparseMatrix(n,n,lambda i,j:0) # test element assignment a = SparseMatrix(( (1, 0), (0, 1) )) a[0, 0] = 2 assert a == SparseMatrix(( (2, 0), (0, 1) )) a[1, 0] = 5 assert a == SparseMatrix(( (2, 0), (5, 1) )) a[1, 1] = 0 assert a == SparseMatrix(( (2, 0), (5, 0) )) assert a.mat == {(0, 0): 2, (1, 0): 5} # test_multiplication a=SparseMatrix(( (1, 2), (3, 1), (0, 6), )) b = SparseMatrix (( (1, 2), (3, 0), )) c= a*b assert c[0,0]==7 assert c[0,1]==2 assert c[1,0]==6 assert c[1,1]==6 assert c[2,0]==18 assert c[2,1]==0 x = Symbol("x") c = b * Symbol("x") assert isinstance(c,SparseMatrix) assert c[0,0] == x assert c[0,1] == 2*x assert c[1,0] == 3*x assert c[1,1] == 0 c = 5 * b assert isinstance(c,SparseMatrix) assert c[0,0] == 5 assert c[0,1] == 2*5 assert c[1,0] == 3*5 assert c[1,1] == 0 #test_power A = SparseMatrix([[2,3],[4,5]]) assert (A**5)[:] == [6140, 8097, 10796, 14237] A = SparseMatrix([[2, 1, 3],[4,2, 4], [6,12, 1]]) assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433] # test_creation x = Symbol("x") a = SparseMatrix([x, 0], [0, 0]) m = a assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x,0,0,0] b = SparseMatrix(2,2, [x, 0, 0, 0]) m = b assert m.cols == m.rows assert m.cols == 2 assert m[:] == [x,0,0,0] assert a == b # test_determinant x, y = Symbol('x'), Symbol('y') assert SparseMatrix([ [1] ]).det() == 1 assert SparseMatrix(( (-3, 2), ( 8, -5) )).det() == -1 assert SparseMatrix(( (x, 1), (y, 2*y) )).det() == 2*x*y-y assert SparseMatrix(( (1, 1, 1), (1, 2, 3), (1, 3, 6) )).det() == 1 assert SparseMatrix(( ( 3, -2, 0, 5), (-2, 1, -2, 2), ( 0, -2, 5, 0), ( 5, 0, 3, 4) )).det() == -289 assert SparseMatrix(( ( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9, 10, 11, 12), (13, 14, 15, 16) )).det() == 0 assert SparseMatrix(( (3, 2, 0, 0, 0), (0, 3, 2, 0, 0), (0, 0, 3, 2, 0), (0, 0, 0, 3, 2), (2, 0, 0, 0, 3) )).det() == 275 assert SparseMatrix(( (1, 0, 1, 2, 12), (2, 0, 1, 1, 4), (2, 1, 1, -1, 3), (3, 2, -1, 1, 8), (1, 1, 1, 0, 6) )).det() == -55 assert SparseMatrix(( (-5, 2, 3, 4, 5), ( 1, -4, 3, 4, 5), ( 1, 2, -3, 4, 5), ( 1, 2, 3, -2, 5), ( 1, 2, 3, 4, -1) )).det() == 11664 assert SparseMatrix(( ( 2, 7, -1, 3, 2), ( 0, 0, 1, 0, 1), (-2, 0, 7, 0, 2), (-3, -2, 4, 5, 3), ( 1, 0, 0, 0, 1) )).det() == 123 # test_submatrix m0 = eye(4) assert m0[0:3, 0:3] == eye(3) assert m0[2:4, 0:2] == zeros(2) m1 = SparseMatrix(3,3, lambda i,j: i+j) assert m1[0,:] == SparseMatrix(1,3,(0,1,2)) assert m1[1:3, 1] == SparseMatrix(2,1,(2,3)) m2 = SparseMatrix([0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15]) assert m2[:,-1] == SparseMatrix(4,1,[3,7,11,15]) assert m2[-2:,:] == SparseMatrix([[8,9,10,11],[12,13,14,15]]) # test_submatrix_assignment m = zeros(4) m[2:4, 2:4] = eye(2) assert m == SparseMatrix((0,0,0,0), (0,0,0,0), (0,0,1,0), (0,0,0,1)) m[0:2, 0:2] = eye(2) assert m == eye(4) m[:,0] = SparseMatrix(4,1,(1,2,3,4)) assert m == SparseMatrix((1,0,0,0), (2,1,0,0), (3,0,1,0), (4,0,0,1)) m[:,:] = zeros(4) assert m == zeros(4) m[:,:] = ((1,2,3,4),(5,6,7,8),(9, 10, 11, 12),(13,14,15,16)) assert m == SparseMatrix(((1,2,3,4), (5,6,7,8), (9, 10, 11, 12), (13,14,15,16))) m[0:2, 0] = [0,0] assert m == SparseMatrix(((0,2,3,4), (0,6,7,8), (9, 10, 11, 12), (13,14,15,16))) # test_reshape m0 = eye(3) assert m0.reshape(1,9) == SparseMatrix(1,9,(1,0,0,0,1,0,0,0,1)) m1 = SparseMatrix(3,4, lambda i,j: i+j) assert m1.reshape(4,3) == SparseMatrix((0,1,2), (3,1,2), (3,4,2), (3,4,5)) assert m1.reshape(2,6) == SparseMatrix((0,1,2,3,1,2), (3,4,2,3,4,5)) # test_applyfunc m0 = eye(3) assert m0.applyfunc(lambda x:2*x) == eye(3)*2 assert m0.applyfunc(lambda x: 0 ) == zeros(3) # test_LUdecomp testmat = SparseMatrix([[0,2,5,3], [3,3,7,4], [8,4,0,2], [-2,6,3,4]]) L,U,p = testmat.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-testmat == zeros(4) testmat = SparseMatrix([[6,-2,7,4], [0,3,6,7], [1,-2,7,4], [-9,2,6,3]]) L,U,p = testmat.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-testmat == zeros(4) x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix(((1, x, 1), (2, y, 0), (y, 0, z))) L, U, p = M.LUdecomposition() assert L.is_lower() assert U.is_upper() assert (L*U).permuteBkwd(p)-M == zeros(3) # test_LUsolve A = SparseMatrix([[2,3,5], [3,6,2], [8,3,6]]) x = SparseMatrix(3,1,[3,7,5]) b = A*x soln = A.LUsolve(b) assert soln == x A = SparseMatrix([[0,-1,2], [5,10,7], [8,3,4]]) x = SparseMatrix(3,1,[-1,2,5]) b = A*x soln = A.LUsolve(b) assert soln == x # test_inverse A = eye(4) assert A.inv() == eye(4) assert A.inv("LU") == eye(4) assert A.inv("ADJ") == eye(4) A = SparseMatrix([[2,3,5], [3,6,2], [8,3,6]]) Ainv = A.inv() assert A*Ainv == eye(3) assert A.inv("LU") == Ainv assert A.inv("ADJ") == Ainv # test_cross v1 = Matrix(1,3,[1,2,3]) v2 = Matrix(1,3,[3,4,5]) assert v1.cross(v2) == Matrix(1,3,[-2,4,-2]) assert v1.norm(v1) == 14 # test_cofactor assert eye(3) == eye(3).cofactorMatrix() test = SparseMatrix([[1,3,2],[2,6,3],[2,3,6]]) assert test.cofactorMatrix() == SparseMatrix([[27,-6,-6],[-12,2,3],[-3,1,0]]) test = SparseMatrix([[1,2,3],[4,5,6],[7,8,9]]) assert test.cofactorMatrix() == SparseMatrix([[-3,6,-3],[6,-12,6],[-3,6,-3]]) # test_jacobian x = Symbol('x') y = Symbol('y') L = SparseMatrix(1,2,[x**2*y, 2*y**2 + x*y]) syms = [x,y] assert L.jacobian(syms) == Matrix([[2*x*y, x**2],[y, 4*y+x]]) L = SparseMatrix(1,2,[x, x**2*y**3]) assert L.jacobian(syms) == SparseMatrix([[1, 0], [2*x*y**3, x**2*3*y**2]]) # test_QR A = Matrix([[1,2],[2,3]]) Q, S = A.QRdecomposition() R = Rational assert Q == Matrix([[5**R(-1,2), (R(2)/5)*(R(1)/5)**R(-1,2)], [2*5**R(-1,2), (-R(1)/5)*(R(1)/5)**R(-1,2)]]) assert S == Matrix([[5**R(1,2), 8*5**R(-1,2)], [0, (R(1)/5)**R(1,2)]]) assert Q*S == A assert Q.T * Q == eye(2) # test nullspace # first test reduced row-ech form R = Rational M = Matrix([[5,7,2,1], [1,6,2,-1]]) out, tmp = M.rref() assert out == Matrix([[1,0,-R(2)/23,R(13)/23], [0,1,R(8)/23, R(-6)/23]]) M = Matrix([[1,3,0,2,6,3,1], [-2,-6,0,-2,-8,3,1], [3,9,0,0,6,6,2], [-1,-3,0,1,0,9,3]]) out, tmp = M.rref() assert out == Matrix([[1,3,0,0,2,0,0], [0,0,0,1,2,0,0], [0,0,0,0,0,1,R(1)/3], [0,0,0,0,0,0,0]]) # now check the vectors basis = M.nullspace() assert basis[0] == Matrix([[-3,1,0,0,0,0,0]]) assert basis[1] == Matrix([[0,0,1,0,0,0,0]]) assert basis[2] == Matrix([[-2,0,0,-2,1,0,0]]) assert basis[3] == Matrix([[0,0,0,0,0,R(-1)/3, 1]]) # test eigen x = Symbol('x') y = Symbol('y') eye3 = eye(3) assert eye3.charpoly(x) == (1-x)**3 assert eye3.charpoly(y) == (1-y)**3 # test values M = Matrix([(0,1,-1), (1,1,0), (-1,0,1) ]) vals = M.eigenvals() vals.sort() assert vals == [-1, 1, 2] R = Rational M = Matrix([ [1,0,0], [0,1,0], [0,0,1]]) assert M.eigenvects() == [[1, 3, [Matrix(1,3,[1,0,0]), Matrix(1,3,[0,1,0]), Matrix(1,3,[0,0,1])]]] M = Matrix([ [5,0,2], [3,2,0], [0,0,1]]) assert M.eigenvects() == [[1, 1, [Matrix(1,3,[R(-1)/2,R(3)/2,1])]], [2, 1, [Matrix(1,3,[0,1,0])]], [5, 1, [Matrix(1,3,[1,1,0])]]] assert M.zeros((3, 5)) == SparseMatrix(3, 5, {}) def test_subs(): x = Symbol('x') assert Matrix([[1,x],[x,4]]).subs(x, 5) == Matrix([[1,5],[5,4]]) y = Symbol('y') assert Matrix([[x,2],[x+y,4]]).subs([[x,-1],[y,-2]]) == Matrix([[-1,2],[-3,4]]) assert Matrix([[x,2],[x+y,4]]).subs([(x,-1),(y,-2)]) == Matrix([[-1,2],[-3,4]]) assert Matrix([[x,2],[x+y,4]]).subs({x:-1,y:-2}) == Matrix([[-1,2],[-3,4]]) def test_simplify(): x,y,f,n = symbols('x y f n') M = Matrix([ [ 1/x + 1/y, (x + x*y)/ x ], [(f(x) + y*f(x))/f(x), 2 * (1/n - cos(n * pi)/n)/ pi ] ]) M.simplify() assert M == Matrix([[(x + y)/(x * y), 1 + y ], [ 1 + y, 2*((1 - 1*cos(pi*n))/(pi*n)) ]]) M = Matrix([[(1 + x)**2]]) M.simplify() assert M == Matrix([[(1 + x)**2]]) M.simplify(ratio=oo) assert M == Matrix([[1 + 2*x + x**2]]) def test_transpose(): M = Matrix([[1,2,3,4,5,6,7,8,9,0], [1,2,3,4,5,6,7,8,9,0]]) assert M.T == Matrix( [ [1,1], [2,2], [3,3], [4,4], [5,5], [6,6], [7,7], [8,8], [9,9], [0,0] ]) assert M.T.T == M assert M.T == M.transpose() def test_conjugate(): M = Matrix([ [0,I,5], [1,2,0]]) assert M.T == Matrix([ [0,1], [I,2], [5,0]]) assert M.C == Matrix([ [0,-I,5], [1,2,0]]) assert M.C == M.conjugate() assert M.H == M.T.C assert M.H == Matrix([ [0,1], [-I,2], [5,0]]) def test_conj_dirac(): raises(ShapeError, "eye(3).D") M = Matrix([ [1,I,I,I], [0,1,I,I], [0,0,1,I], [0,0,0,1] ]) assert M.D == Matrix([ [1,0,0,0], [-I,1,0,0], [-I,-I,-1,0], [-I,-I,I,-1] ]) def test_trace(): M = Matrix([[1,0,0], [0,5,0], [0,0,8]]) assert M.trace() == 14 def test_shape(): x, y = symbols("x y") M = Matrix([[x,0,0], [0,y,0]]) assert M.shape == (2, 3) def test_col_row(): x, y = symbols("x y") M = Matrix([[x,0,0], [0,y,0]]) M.row(1,lambda r, j: r+j+1) assert M == Matrix([[x,0,0], [1,y+2,3]]) M.col(0,lambda c, j: c+y**j) assert M == Matrix([[x+1,0,0], [1+y,y+2,3]]) def test_issue851(): m = Matrix([1, 2, 3]) a = Matrix([1, 2, 3]) b = Matrix([2, 2, 3]) assert not (m in []) assert not (m in [1]) assert m != 1 assert m == a assert m != b def test_issue882(): class Index1(object): def __index__(self): return 1 class Index2(object): def __index__(self): return 2 index1 = Index1() index2 = Index2() m = Matrix([1, 2, 3]) assert m[index2] == 3 m[index2] = 5 assert m[2] == 5 m = Matrix([[1, 2, 3], [4, 5, 6]]) assert m[index1,index2] == 6 assert m[1,index2] == 6 assert m[index1,2] == 6 m[index1,index2] = 4 assert m[1, 2] == 4 m[1,index2] = 6 assert m[1, 2] == 6 m[index1,2] = 8 assert m[1, 2] == 8 def test_evalf(): a = Matrix([sqrt(5), 6]) assert abs(a.evalf()[0] - a[0].evalf()) < 1e-10 assert abs(a.evalf()[1] - a[1].evalf()) < 1e-10 def test_is_symbolic(): x = Symbol('x') a = Matrix([[x,x],[x,x]]) assert a.is_symbolic() == True a = Matrix([[1,2,3,4],[5,6,7,8]]) assert a.is_symbolic() == False a = Matrix([[1,2,3,4],[5,6,x,8]]) assert a.is_symbolic() == True a = Matrix([[1,x,3]]) assert a.is_symbolic() == True a = Matrix([[1,2,3]]) assert a.is_symbolic() == False a = Matrix([[1],[x],[3]]) assert a.is_symbolic() == True a = Matrix([[1],[2],[3]]) assert a.is_symbolic() == False def test_is_upper(): a = Matrix([[1,2,3]]) assert a.is_upper() == True a = Matrix([[1],[2],[3]]) assert a.is_upper() == False def test_is_lower(): a = Matrix([[1,2,3]]) assert a.is_lower() == False a = Matrix([[1],[2],[3]]) assert a.is_lower() == True def test_is_nilpotent(): a = Matrix(4, 4, [0,2,1,6,0,0,1,2,0,0,0,3,0,0,0,0]) assert a.is_nilpotent() a = Matrix([[1,0],[0,1]]) assert not a.is_nilpotent() def test_zeros_ones_fill(): n, m = 3, 5 a = zeros( (n, m) ) a.fill( 5 ) b = 5 * ones( (n, m) ) assert a == b assert a.rows == b.rows == 3 assert a.cols == b.cols == 5 assert a.shape == b.shape == (3, 5) def test_empty_zeros(): a = zeros(0) assert a == Matrix() a = zeros([0, 2]) assert a.rows == 0 assert a.cols == 2 a = zeros([2, 0]) assert a.rows == 2 assert a.cols == 0 def test_issue650(): x, y = symbols('x y') a = Matrix([[x**2, x*y],[x*sin(y), x*cos(y)]]) assert a.diff(x) == Matrix([[2*x, y],[sin(y), cos(y)]]) assert Matrix([[x, -x, x**2],[exp(x),1/x-exp(-x), x+1/x]]).limit(x, oo) == Matrix([[oo, -oo, oo],[oo, 0, oo]]) assert Matrix([[(exp(x)-1)/x, 2*x + y*x, x**x ], [1/x, abs(x) , abs(sin(x+1))]]).limit(x, 0) == Matrix([[1, 0, 1],[oo, 0, sin(1)]]) assert a.integrate(x) == Matrix([[Rational(1,3)*x**3, y*x**2/2],[x**2*sin(y)/2, x**2*cos(y)/2]]) def test_inv_iszerofunc(): A = eye(4) A.col_swap(0,1) for method in "GE", "LU": assert A.inv(method, iszerofunc=lambda x: x==0) == A.inv("ADJ") def test_jacobian_metrics(): rho, phi = symbols("rho phi") X = Matrix([rho*cos(phi), rho*sin(phi)]) Y = Matrix([rho, phi]) J = X.jacobian(Y) assert J == X.jacobian(Y.T) assert J == (X.T).jacobian(Y) assert J == (X.T).jacobian(Y.T) g = J.T*eye(J.shape[0])*J g = g.applyfunc(trigsimp) assert g == Matrix([[1, 0], [0, rho**2]]) def test_jacobian2(): rho, phi = symbols("rho phi") X = Matrix([rho*cos(phi), rho*sin(phi), rho**2]) Y = Matrix([rho, phi]) J = Matrix([ [cos(phi), -rho*sin(phi)], [sin(phi), rho*cos(phi)], [ 2*rho, 0], ]) assert X.jacobian(Y) == J def test_issue1465(): x, y, z = symbols('x y z') X = Matrix([exp(x + y + z), exp(x + y + z), exp(x + y + z)]) Y = Matrix([x, y, z]) for i in range(1, 3): for j in range(1, 3): X_slice = X[:i,:] Y_slice = Y[:j,:] J = X_slice.jacobian(Y_slice) assert J.rows == i assert J.cols == j for k in range(j): assert J[:,k] == X_slice def test_nonvectorJacobian(): x, y, z = symbols('x y z') X = Matrix([ [exp(x + y + z), exp(x + y + z)], [exp(x + y + z), exp(x + y + z)] ]) Y = Matrix([x, y, z]) raises(TypeError, 'X.jacobian(Y)') X = X[0,:] Y = Matrix([ [x, y], [x,z] ]) raises(TypeError, 'X.jacobian(Y)') def test_vec(): m = Matrix([ [1,3], [2,4] ]) m_vec = m.vec() assert m_vec.cols == 1 for i in xrange(4): assert m_vec[i] == i + 1 def test_vech(): m = Matrix([ [1,2], [2,3] ]) m_vech = m.vech() assert m_vech.cols == 1 for i in xrange(3): assert m_vech[i] == i + 1 m_vech = m.vech(diagonal=False) assert m_vech[0] == 2 x,y = symbols('x,y') m = Matrix([ [1, x*(x+y)], [y*x+x**2, 1] ]) m_vech = m.vech(diagonal=False) assert m_vech[0] == x*(x + y) x,y = symbols('x,y') m = Matrix([ [1, x*(x+y)], [y*x, 1] ]) m_vech = m.vech(diagonal=False, check_symmetry=False) assert m_vech[0] == y*x def test_vech_errors(): m = Matrix([ [1,3] ]) raises(ShapeError, 'm.vech()') m = Matrix([ [1,3], [2,4] ]) raises(ValueError, 'm.vech()') def test_diag(): x, y, z = symbols("x y z") a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) assert diag(a, b, b) == Matrix([ [1, 2, 0, 0, 0, 0], [2, 3, 0, 0, 0, 0], [0, 0, 3, x, 0, 0], [0, 0, y, 3, 0, 0], [0, 0, 0, 0, 3, x], [0, 0, 0, 0, y, 3], ]) assert diag(a, b, c) == Matrix([ [1, 2, 0, 0, 0, 0, 0], [2, 3, 0, 0, 0, 0, 0], [0, 0, 3, x, 0, 0, 0], [0, 0, y, 3, 0, 0, 0], [0, 0, 0, 0, 3, x, 3], [0, 0, 0, 0, y, 3, z], [0, 0, 0, 0, x, y, z], ]) assert diag(a, c, b) == Matrix([ [1, 2, 0, 0, 0, 0, 0], [2, 3, 0, 0, 0, 0, 0], [0, 0, 3, x, 3, 0, 0], [0, 0, y, 3, z, 0, 0], [0, 0, x, y, z, 0, 0], [0, 0, 0, 0, 0, 3, x], [0, 0, 0, 0, 0, y, 3], ]) a = Matrix([x, y, z]) b = Matrix([[1, 2], [3, 4]]) c = Matrix([[5, 6]]) assert diag(a, 7, b, c) == Matrix([ [x, 0, 0, 0, 0, 0], [y, 0, 0, 0, 0, 0], [z, 0, 0, 0, 0, 0], [0, 7, 0, 0, 0, 0], [0, 0, 1, 2, 0, 0], [0, 0, 3, 4, 0, 0], [0, 0, 0, 0, 5, 6], ]) def test_get_diag_blocks1(): x, y, z = symbols("x y z") a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) a.get_diag_blocks() == [a] b.get_diag_blocks() == [b] c.get_diag_blocks() == [c] def test_get_diag_blocks2(): x, y, z = symbols("x y z") a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) assert diag(a, b, b).get_diag_blocks() == [a, b, b] assert diag(a, b, c).get_diag_blocks() == [a, b, c] assert diag(a, c, b).get_diag_blocks() == [a, c, b] assert diag(c, c, b).get_diag_blocks() == [c, c, b] def test_inv_block(): x, y, z = symbols("x y z") a = Matrix([[1, 2], [2, 3]]) b = Matrix([[3, x], [y, 3]]) c = Matrix([[3, x, 3], [y, 3, z], [x, y, z]]) A = diag(a, b, b) assert A.inv(try_block_diag=True) == diag(a.inv(), b.inv(), b.inv()) A = diag(a, b, c) assert A.inv(try_block_diag=True) == diag(a.inv(), b.inv(), c.inv()) A = diag(a, c, b) assert A.inv(try_block_diag=True) == diag(a.inv(), c.inv(), b.inv()) A = diag(a, a, b, a, c, a) assert A.inv(try_block_diag=True) == diag( a.inv(), a.inv(), b.inv(), a.inv(), c.inv(), a.inv()) assert A.inv(try_block_diag=True, method="ADJ") == diag( a.inv(method="ADJ"), a.inv(method="ADJ"), b.inv(method="ADJ"), a.inv(method="ADJ"), c.inv(method="ADJ"), a.inv(method="ADJ")) def test_creation_args(): """ Check that matrix dimensions can be specified using any reasonable type (see issue 1515). """ raises(ValueError, 'zeros((3, -1))') raises(ValueError, 'zeros((1, 2, 3, 4))') assert zeros(3L) == zeros(3) assert zeros(Integer(3)) == zeros(3) assert zeros(3.) == zeros(3) assert eye(3L) == eye(3) assert eye(Integer(3)) == eye(3) assert eye(3.) == eye(3) assert ones((3L, Integer(4))) == ones((3, 4)) raises(TypeError, 'Matrix(1, 2)') def test_diagonal_symmetrical(): m = Matrix(2,2,[0, 1, 1, 0]) assert not m.is_diagonal() assert m.is_symmetric() assert m.is_symmetric(simplify=False) m = Matrix(2,2,[1, 0, 0, 1]) assert m.is_diagonal() m = diag(1, 2, 3) assert m.is_diagonal() assert m.is_symmetric() m = Matrix(3,3,[1, 0, 0, 0, 2, 0, 0, 0, 3]) assert m == diag(1, 2, 3) m = Matrix(2, 3, [0, 0, 0, 0, 0, 0]) assert not m.is_symmetric() assert m.is_diagonal() m = Matrix(((5, 0), (0, 6), (0, 0))) assert m.is_diagonal() m = Matrix(((5, 0, 0), (0, 6, 0))) assert m.is_diagonal() x, y = symbols('x y') m = Matrix(3,3,[1, x**2 + 2*x + 1, y, (x + 1)**2 , 2, 0, y, 0, 3]) assert m.is_symmetric() assert not m.is_symmetric(simplify=False) assert m.expand().is_symmetric(simplify=False) def test_diagonalization(): x, y, z = symbols('x y z') m = Matrix(3,2,[-3, 1, -3, 20, 3, 10]) assert not m.is_diagonalizable() assert not m.is_symmetric() raises(NonSquareMatrixError, 'm.diagonalize()') # diagonalizable m = diag(1, 2, 3) (P, D) = m.diagonalize() assert P == eye(3) assert D == m m = Matrix(2,2,[0, 1, 1, 0]) assert m.is_symmetric() assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D m = Matrix(2,2,[1, 0, 0, 3]) assert m.is_symmetric() assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D assert P == eye(2) assert D == m m = Matrix(2,2,[1, 1, 0, 0]) assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D m = Matrix(3,3,[1, 2, 0, 0, 3, 0, 2, -4, 2]) assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D m = Matrix(2,2,[1, 0, 0, 0]) assert m.is_diagonal() assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D assert P == eye(2) # diagonalizable, complex only m = Matrix(2,2,[0, 1, -1, 0]) assert not m.is_diagonalizable(True) raises(MatrixError, '(D, P) = m.diagonalize(True)') assert m.is_diagonalizable() (P, D) = m.diagonalize() assert P.inv() * m * P == D m = Matrix(2,2,[1, 0, 0, I]) raises(NotImplementedError, 'm.is_diagonalizable(True)') # !!! bug because of eigenvects() or roots(x**2 + (-1 - I)*x + I, x) # see issue 2193 # assert not m.is_diagonalizable(True) # raises(MatrixError, '(P, D) = m.diagonalize(True)') # (P, D) = m.diagonalize(True) # not diagonalizable m = Matrix(2,2,[0, 1, 0, 0]) assert not m.is_diagonalizable() raises(MatrixError, '(D, P) = m.diagonalize()') m = Matrix(3,3,[-3, 1, -3, 20, 3, 10, 2, -2, 4]) assert not m.is_diagonalizable() raises(MatrixError, '(D, P) = m.diagonalize()') # symbolic a, b, c, d = symbols('a b c d') m = Matrix(2,2,[a, c, c, b]) assert m.is_symmetric() assert m.is_diagonalizable() def test_jordan_form(): m = Matrix(3,2,[-3, 1, -3, 20, 3, 10]) raises(NonSquareMatrixError, 'm.jordan_form()') # diagonalizable m = Matrix(3, 3, [7, -12, 6, 10, -19, 10, 12, -24, 13]) Jmust = Matrix(3, 3, [1, 0, 0, 0, 1, 0, 0, 0, -1]) (P, J) = m.jordan_form() assert Jmust == J assert Jmust == m.diagonalize()[1] #m = Matrix(3, 3, [0, 6, 3, 1, 3, 1, -2, 2, 1]) #m.jordan_form() # very long # m.jordan_form() # # diagonalizable, complex only # Jordan cells # complexity: one of eigenvalues is zero m = Matrix(3, 3, [0, 1, 0, -4, 4, 0, -2, 1, 2]) Jmust = Matrix(3, 3, [2, 0, 0, 0, 2, 1, 0, 0, 2]) assert Jmust == m.jordan_form()[1] (P, Jcells) = m.jordan_cells() assert Jcells[0] == Matrix(1, 1, [2]) assert Jcells[1] == Matrix(2, 2, [2, 1, 0, 2]) #complexity: all of eigenvalues are equal m = Matrix(3, 3, [2, 6, -15, 1, 1, -5, 1, 2, -6]) Jmust = Matrix(3, 3, [-1, 0, 0, 0, -1, 1, 0, 0, -1]) (P, J) = m.jordan_form() assert Jmust == J #complexity: two of eigenvalues are zero m = Matrix(3, 3, [4, -5, 2, 5, -7, 3, 6, -9, 4]) Jmust = Matrix(3, 3, [1, 0, 0, 0, 0, 1, 0, 0, 0]) (P, J) = m.jordan_form() assert Jmust == J m = Matrix(4, 4, [6, 5, -2, -3, -3, -1, 3, 3, 2, 1, -2, -3, -1, 1, 5, 5]) Jmust = Matrix(4, 4, [2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2]) (P, J) = m.jordan_form() assert Jmust == J m = Matrix(4, 4, [6, 2, -8, -6, -3, 2, 9, 6, 2, -2, -8, -6, -1, 0, 3, 4]) Jmust = Matrix(4, 4, [2, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, -2]) (P, J) = m.jordan_form() assert Jmust == J m = Matrix(4, 4, [5, 4, 2, 1, 0, 1, -1, -1, -1, -1, 3, 0, 1, 1, -1, 2]) assert not m.is_diagonalizable() Jmust = Matrix(4, 4, [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 4, 1, 0, 0, 0, 4]) (P, J) = m.jordan_form() assert Jmust == J def test_Matrix_berkowitz_charpoly(): x, UA, K_i, K_w = symbols('x UA K_i K_w') A = Matrix([[-K_i - UA + K_i**2/(K_i + K_w), K_i*K_w/(K_i + K_w)], [ K_i*K_w/(K_i + K_w), -K_w + K_w**2/(K_i + K_w)]]) assert A.berkowitz_charpoly(x) == \ Poly(x**2 + (K_i*UA + K_w*UA + 2*K_i*K_w)/(K_i + K_w)*x + K_i*K_w*UA/(K_i + K_w), x, domain='ZZ(K_i,K_w,UA)') def test_exp(): m = Matrix([[3,4],[0,-2]]) assert m.exp() == Matrix([[exp(3),-4*exp(-2)/5 + 4*exp(3)/5],[0,exp(-2)]]) m = Matrix([[1,0],[0,1]]) assert m.exp() == Matrix([[E,0],[0,E]]) def test_SparseMatrix_transpose(): assert SparseMatrix((1,2),(3,4)).transpose() == SparseMatrix((1,3),(2,4)) def test_SparseMatrix_CL_RL(): assert SparseMatrix((1,2),(3,4)).row_list() == [(0, 0, 1), (0, 1, 2), (1, 0, 3), (1, 1, 4)] assert SparseMatrix((1,2),(3,4)).col_list() == [(0, 0, 1), (1, 0, 3), (0, 1, 2), (1, 1, 4)] def test_SparseMatrix_add(): assert SparseMatrix(((1,0), (0,1))) + SparseMatrix(((0,1), (1,0))) == SparseMatrix(((1,1), (1,1))) a = SparseMatrix(100, 100, lambda i, j : int(j != 0 and i % j == 0)) b = SparseMatrix(100, 100, lambda i, j : int(i != 0 and j % i == 0)) assert (len(a.mat) + len(b.mat) - len((a+b).mat) > 0) def test_has(): x, y, z = symbols('x,y,z') A = Matrix(((x,y),(2,3))) assert A.has(x) assert not A.has(z) assert A.has(Symbol) A = A.subs(x,2) assert not A.has(x) def test_errors(): # Note, some errors not tested. See 'XXX' in code. raises(ValueError, "_dims_to_nm([1, 0, 2])") raises(ValueError, "Matrix([[1, 2], [1]])") raises(ShapeError, "Matrix([[1, 2], [3, 4]]).copyin_matrix([1, 0], Matrix([1, 2]))") raises(TypeError, "Matrix([[1, 2], [3, 4]]).copyin_list([0, 1], set([]))") raises(NonSquareMatrixError, "Matrix([[1, 2, 3], [2, 3, 0]]).inv()") raises(ShapeError, "Matrix(1, 2, [1, 2]).row_join(Matrix([[1, 2], [3, 4]]))") raises(ShapeError, "Matrix([1, 2]).col_join(Matrix([[1, 2], [3, 4]]))") raises(ShapeError, "Matrix([1]).row_insert(1, Matrix([[1, 2], [3, 4]]))") raises(ShapeError, "Matrix([1]).col_insert(1, Matrix([[1, 2], [3, 4]]))") raises(NonSquareMatrixError, "Matrix([1, 2]).trace()") raises(TypeError, "SparseMatrix([[1, 2], [3, 4]]).submatrix([1, 1])") raises(TypeError, "Matrix([1]).applyfunc(1)") raises(ShapeError, "Matrix([1]).LUsolve(Matrix([[1, 2], [3, 4]]))") raises(MatrixError, "Matrix([[1,2,3],[4,5,6],[7,8,9]]).QRdecomposition()") raises(NonSquareMatrixError, "Matrix([1, 2]).LUdecomposition_Simple()") raises(ValueError, "Matrix([[1, 2], [3, 4]]).minorEntry(4, 5)") raises(ValueError, "Matrix([[1, 2], [3, 4]]).minorMatrix(4, 5)") raises(TypeError, "Matrix([1, 2, 3]).cross(1)") raises(TypeError, "Matrix([1, 2, 3]).dot(1)") raises(ShapeError, "Matrix([1, 2, 3]).dot(Matrix([1, 2]))") raises(NotImplementedError, "Matrix([[0,1,2],[0,0,-1], [0,0,0]]).exp()") raises(NonSquareMatrixError, "Matrix([1, 2, 3]).exp()") raises(ShapeError, "Matrix([[1, 2], [3, 4]]).normalized()") raises(NonSquareMatrixError, "Matrix([1, 2]).inverse_GE()") raises(ValueError, "Matrix([[1, 2], [1, 2]]).inverse_GE()") raises(NonSquareMatrixError, "Matrix([1, 2]).inverse_ADJ()") raises(ValueError, "Matrix([[1, 2], [1, 2]]).inverse_ADJ()") raises(NonSquareMatrixError, "Matrix([1,2]).is_nilpotent()") raises(ValueError, "hessian(Matrix([[1, 2], [3, 4]]), Matrix([[1, 2], [2, 1]]))") raises(ValueError, "hessian(Matrix([[1, 2], [3, 4]]), [])") raises(TypeError, "SparseMatrix(1.4, 2, lambda i, j: 0)") raises(ValueError, "SparseMatrix([1, 2, 3], [1, 2])") raises(ValueError, "SparseMatrix([[1, 2], [3, 4]])[(1, 2, 3)]") raises(ValueError, "SparseMatrix([[1, 2], [3, 4]]).rowdecomp(5)") raises(ValueError, "SparseMatrix([[1, 2], [3, 4]])[1, 2, 3] = 4") raises(TypeError, "SparseMatrix([[1, 2], [3, 4]]).copyin_list([0, 1], set([]))") raises(TypeError, "SparseMatrix([[1, 2], [3, 4]]).submatrix((1, 2))") raises(TypeError, "SparseMatrix([1, 2, 3]).cross(1)") raises(ValueError, "Matrix([[5, 10, 7],[0, -1, 2],[8, 3, 4]]).LUdecomposition_Simple(iszerofunc=lambda x:abs(x)<=4)") raises(NotImplementedError, "Matrix([[1, 0],[1, 1]])**(S(1)/2)") raises(NotImplementedError, "Matrix([[1, 2, 3],[4, 5, 6],[7, 8, 9]])**(0.5)") def test_len(): assert len(Matrix()) == 0 assert len(Matrix([[1, 2]])) == len(Matrix([[1], [2]])) == 2 assert len(Matrix(0, 2, lambda i, j: 0)) == len(Matrix(2, 0, lambda i, j: 0)) == 0 assert len(Matrix([[0, 1, 2], [3, 4, 5]])) == 6 assert Matrix([1]) assert not Matrix() def test_integrate(): x, y = symbols('x,y') A = Matrix(((1,4,x),(y,2,4),(10,5,x**2))) assert A.integrate(x) == Matrix(((x, 4*x, x**2/2), (x*y, 2*x, 4*x), (10*x, 5*x, x**3/3))) assert A.integrate(y) == Matrix(((y, 4*y, x*y),(y**2/2, 2*y, 4*y), (10*y, 5*y, y*x**2))) def test_limit(): x, y = symbols('x,y') A = Matrix(((1,4,sin(x)/x),(y,2,4),(10,5,x**2+1))) assert A.limit(x,0) == Matrix(((1,4,1),(y,2,4),(10,5,1))) def test_diff(): x, y = symbols('x,y') A = Matrix(((1,4,x),(y,2,4),(10,5,x**2+1))) assert A.diff(x) == Matrix(((0,0,1),(0,0,0),(0,0,2*x))) assert A.diff(y) == Matrix(((0,0,0),(1,0,0),(0,0,0))) def test_getattr(): x, y = symbols('x,y') A = Matrix(((1,4,x),(y,2,4),(10,5,x**2+1))) raises (AttributeError, 'A.nonexistantattribute') def test_hessenberg(): A = Matrix([[3, 4, 1],[2, 4 ,5],[0, 1, 2]]) assert A.is_upper_hessenberg() assert A.transpose().is_lower_hessenberg() A = Matrix([[3, 4, 1],[2, 4 ,5],[3, 1, 2]]) assert not A.is_upper_hessenberg() def test_cholesky(): A = Matrix(((25,15,-5),(15,18,0),(-5,0,11))) assert A.cholesky() * A.cholesky().T == A assert A.cholesky().is_lower() assert A.cholesky() == Matrix([[5, 0, 0], [3, 3, 0], [-1, 1, 3]]) def test_LDLdecomposition(): A = Matrix(((25,15,-5), (15,18,0), (-5,0,11))) L, D = A.LDLdecomposition() assert L * D * L.T == A assert L.is_lower() assert L == Matrix([[1, 0, 0], [ S(3)/5, 1, 0], [S(-1)/5, S(1)/3, 1]]) assert D.is_diagonal() assert D == Matrix([[25, 0, 0], [0, 9, 0], [0, 0, 9]]) def test_cholesky_solve(): A = Matrix([[2,3,5], [3,6,2], [8,3,6]]) x = Matrix(3,1,[3,7,5]) b = A*x soln = A.cholesky_solve(b) assert soln == x A = Matrix([[0,-1,2], [5,10,7], [8,3,4]]) x = Matrix(3,1,[-1,2,5]) b = A*x soln = A.cholesky_solve(b) assert soln == x def test_LDLsolve(): A = Matrix([[2,3,5], [3,6,2], [8,3,6]]) x = Matrix(3,1,[3,7,5]) b = A*x soln = A.LDLsolve(b) assert soln == x A = Matrix([[0,-1,2], [5,10,7], [8,3,4]]) x = Matrix(3,1,[-1,2,5]) b = A*x soln = A.LDLsolve(b) assert soln == x def test_matrix_norm(): # Vector Tests # Test columns and symbols x = Symbol('x', real=True) v = Matrix([cos(x), sin(x)]) assert trigsimp(v.norm(2)) == 1 assert v.norm(10) == Pow(cos(x)**10 + sin(x)**10, S(1)/10) # Test Rows y = Matrix([[5, Rational(3,2)]]) assert y.norm() == Pow(25 + Rational(9,4),S(1)/2) assert y.norm(oo) == max(y.mat) assert y.norm(-oo) == min(y.mat) # Matrix Tests # Intuitive test A = Matrix([[1,1], [1,1]]) assert A.norm(2)==2 assert A.norm(-2)==0 assert A.norm('frobenius')==2 assert eye(10).norm(2)==eye(10).norm(-2)==1 # Test with Symbols and more complex entries y = Symbol('y') A = Matrix([[3,y,y],[x,S(1)/2, -pi]]) assert (A.norm('fro') == (S(37)/4 + 2*abs(y)**2 + pi**2 + x**2)**(S(1)/2)) # Check non-square A = Matrix([[1,2,-3],[4,5,Rational(13,2)]]) assert A.norm(2) == sympify('(389/8 + 78665**(1/2)/8)**(1/2)') assert A.norm(-2) == S(0) assert A.norm('frobenius') == 389**Rational(1,2)/2 # Test properties of matrix norms # http://en.wikipedia.org/wiki/Matrix_norm#Definition # Two matrices A = Matrix([[1,2],[3,4]]) B = Matrix([[5,5],[-2,2]]) C = Matrix([[0,-I],[I,0]]) D = Matrix([[1,0],[0,-1]]) L = [A,B,C,D] alpha = Symbol('alpha', real=True) for order in ['fro', 2, -2]: # Zero Check assert zeros(3).norm(order) == S(0) # Check Triangle Inequality for all Pairs of Matrices for X in L: for Y in L: assert X.norm(order)+Y.norm(order) >= (X+Y).norm(order) # Scalar multiplication linearity for M in [A,B,C,D]: if order in [2,-2]: # Abs is causing tests to fail when Abs(alpha) is inside a Max # or Min. The tests produce mathematically true statements that # are too complex to be simplified well. continue; try: assert ((alpha*M).norm(order) == abs(alpha) * M.norm(order)) except NotImplementedError: pass; # Some Norms fail on symbolic matrices due to Max issue # Test Properties of Vector Norms # http://en.wikipedia.org/wiki/Vector_norm # Two column vectors a = Matrix([1,1-1*I,-3]) b = Matrix([S(1)/2, 1*I, 1]) c = Matrix([-1,-1,-1]) d = Matrix([3, 2, I]) e = Matrix([Integer(1e2),Rational(1,1e2),1]) L = [a,b,c,d,e] alpha = Symbol('alpha', real=True) for order in [1,2,-1, -2, S.Infinity, S.NegativeInfinity, pi]: # Zero Check assert Matrix([0,0,0]).norm(order) == S(0) # Triangle inequality on all pairs if order >= 1: # Triangle InEq holds only for these norms for v in L: for w in L: assert v.norm(order)+w.norm(order) >= (v+w).norm(order) # Linear to scalar multiplication if order in [1,2, -1, -2, S.Infinity, S.NegativeInfinity]: for vec in L: try: assert simplify( (alpha*v).norm(order) - (abs(alpha) * v.norm(order)) ) == 0 except NotImplementedError: pass; # Some Norms fail on symbolics due to Max issue def test_singular_values(): x = Symbol('x', real=True) A = Matrix([[0,1*I],[2,0]]) assert A.singular_values() == [2,1] A = eye(3); A[1,1] = x; A[2,2] = 5 vals = A.singular_values(); assert 1 in vals and 5 in vals and abs(x) in vals A = Matrix([[sin(x), cos(x)],[-cos(x), sin(x)]]) vals = [sv.trigsimp() for sv in A.singular_values()] assert vals == [S(1), S(1)] def test_condition_number(): x = Symbol('x', real=True) A = eye(3); A[0,0] = 10; A[2,2] = S(1)/10; assert A.condition_number() == 100 A[1,1] = x assert A.condition_number() == Max(10, Abs(x)) / Min(S(1)/10 , Abs(x)) M = Matrix([[cos(x), sin(x)], [-sin(x), cos(x)]]) Mc = M.condition_number() assert all(Float(1.).epsilon_eq(Mc.subs(x, val).evalf()) for val in \ [Rational(1,5), Rational(1, 2), Rational(1, 10), pi/2, pi, 7*pi/4 ]) def test_len(): assert len(Matrix()) == 0 assert len(Matrix([[1, 2]])) == len(Matrix([[1], [2]])) == 2 assert len(Matrix(0, 2, lambda i, j: 0)) == len(Matrix(2, 0, lambda i, j: 0)) == 0 assert len(Matrix([[0, 1, 2], [3, 4, 5]])) == 6 assert Matrix([1]) assert not Matrix() def test_equality(): A = Matrix(((1,2,3),(4,5,6),(7,8,9))) B = Matrix(((9,8,7),(6,5,4),(3,2,1))) assert A == A[:, :] assert not A != A[:, :] assert not A == B assert A != B assert A != 10 assert not A == 10 # A SparseMatrix can be equal to a Matrix C = SparseMatrix(((1,0,0),(0,1,0),(0,0,1))) D = Matrix(((1,0,0),(0,1,0),(0,0,1))) assert C == D assert not C != D wxgeometrie-0.133.2.orig/wxgeometrie/sympy/matrices/__init__.py0000644000175000017500000000055312014170666025022 0ustar georgeskgeorgesk"""A module that handles matrices. Includes functions for fast creating matrices like zero, one/eye, random matrix etc. """ from matrices import (Matrix, SparseMatrix, zeros, ones, eye, diag, hessian, randMatrix, GramSchmidt, wronskian, casoratian, list2numpy, matrix2numpy, DeferredVector, block_diag, symarray, ShapeError, NonSquareMatrixError) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/matrices/matrices.py0000644000175000017500000031120612014170666025072 0ustar georgeskgeorgeskfrom sympy import Basic, Symbol, Integer, C, S, Dummy, Rational, Add, Pow from sympy.core.numbers import Zero from sympy.core.sympify import sympify, converter, SympifyError from sympy.core.compatibility import is_sequence from sympy.polys import Poly, roots, cancel from sympy.simplify import simplify as sympy_simplify from sympy.utilities.iterables import flatten from sympy.functions.elementary.miscellaneous import sqrt, Max, Min from sympy.functions.elementary.complexes import re, Abs from sympy.printing import sstr from sympy.core.compatibility import callable, reduce import random class MatrixError(Exception): pass class ShapeError(ValueError, MatrixError): """Wrong matrix shape""" pass class NonSquareMatrixError(ShapeError): pass def _dims_to_nm(dims): """Converts dimensions tuple (or any object with length 1 or 2) or scalar in dims to matrix dimensions n and m.""" try: l = len(dims) except TypeError: dims = (dims,) l = 1 # This will work for nd-array too when they are added to sympy. for dim in dims: if dim < 0: raise ValueError("Matrix dimensions should be non-negative integers.") if l == 2: n, m = map(int, dims) elif l == 1: n = m = int(dims[0]) else: raise ValueError("Matrix dimensions should be a two-element tuple of ints or a single int!") return n, m def _iszero(x): """Returns True if x is zero.""" return x.is_zero class DeferredVector(object): def __init__(self,name): self.name=name def __getitem__(self,i): component_name = '%s[%d]'%(self.name,i) return Symbol(component_name) def __str__(self): return sstr(self) def __repr__(self): return sstr(self) class Matrix(object): # Added just for numpy compatibility # TODO: investigate about __array_priority__ __array_priority__ = 10.0 def __init__(self, *args): """ Matrix can be constructed with values or a rule. >>> from sympy import Matrix, I >>> Matrix( ((1,2+I), (3,4)) ) #doctest:+NORMALIZE_WHITESPACE [1, 2 + I] [3, 4] >>> Matrix(2, 2, lambda i,j: (i+1)*j ) #doctest:+NORMALIZE_WHITESPACE [0, 1] [0, 2] """ if len(args) == 3 and callable(args[2]): operation = args[2] self.rows = int(args[0]) self.cols = int(args[1]) self.mat = [] for i in range(self.rows): for j in range(self.cols): self.mat.append(sympify(operation(i, j))) elif len(args)==3 and is_sequence(args[2]): self.rows=args[0] self.cols=args[1] mat = args[2] if len(mat) != len(self): raise ValueError('List length should be equal to rows*columns') self.mat = map(lambda i: sympify(i), mat) elif len(args) == 1: mat = args[0] if isinstance(mat, Matrix): self.rows = mat.rows self.cols = mat.cols self.mat = mat[:] return elif hasattr(mat, "__array__"): # NumPy array or matrix or some other object that implements # __array__. So let's first use this method to get a # numpy.array() and then make a python list out of it. arr = mat.__array__() if len(arr.shape) == 2: self.rows, self.cols = arr.shape[0], arr.shape[1] self.mat = map(lambda i: sympify(i), arr.ravel()) return elif len(arr.shape) == 1: self.rows, self.cols = 1, arr.shape[0] self.mat = [0]*self.cols for i in xrange(len(arr)): self.mat[i] = sympify(arr[i]) return else: raise NotImplementedError("Sympy supports just 1D and 2D matrices") elif not is_sequence(mat, include=Matrix): raise TypeError("Matrix constructor doesn't accept %s as input" % str(type(mat))) mat = [] for row in args[0]: if isinstance(row, Matrix): mat.extend(row.tolist()) else: mat.append(row) self.rows = len(mat) if len(mat) != 0: if not is_sequence(mat[0]): self.cols = 1 self.mat = map(lambda i: sympify(i), mat) return self.cols = len(mat[0]) else: self.cols = 0 self.mat = [] for j in xrange(self.rows): if len(mat[j]) != self.cols: raise ValueError("Input %s inconsistant to form a Matrix." % args) for i in xrange(self.cols): self.mat.append(sympify(mat[j][i])) elif len(args) == 0: # Empty Matrix self.rows = self.cols = 0 self.mat = [] else: raise TypeError("Data type not understood") def key2ij(self,key): """Converts key=(4,6) to 4,6 and ensures the key is correct.""" if not (is_sequence(key) and len(key) == 2): raise TypeError("wrong syntax: a[%s]. Use a[i,j] or a[(i,j)]" %repr(key)) i,j=key if not (i>=0 and i=0 and j < self.cols): print self.rows, " ", self.cols raise IndexError("Index out of range: a[%s]"%repr(key)) return i,j def transpose(self): """ Matrix transposition. >>> from sympy import Matrix, I >>> m=Matrix(((1,2+I),(3,4))) >>> m #doctest: +NORMALIZE_WHITESPACE [1, 2 + I] [3, 4] >>> m.transpose() #doctest: +NORMALIZE_WHITESPACE [ 1, 3] [2 + I, 4] >>> m.T == m.transpose() True """ a = [0]*len(self) for i in xrange(self.cols): a[i*self.rows:(i+1)*self.rows] = self.mat[i::self.cols] return Matrix(self.cols,self.rows,a) T = property(transpose,None,None,"Matrix transposition.") def conjugate(self): """By-element conjugation.""" out = Matrix(self.rows,self.cols, lambda i,j: self[i,j].conjugate()) return out C = property(conjugate,None,None,"By-element conjugation.") @property def H(self): """ Hermite conjugation. >>> from sympy import Matrix, I >>> m=Matrix(((1,2+I),(3,4))) >>> m #doctest: +NORMALIZE_WHITESPACE [1, 2 + I] [3, 4] >>> m.H #doctest: +NORMALIZE_WHITESPACE [ 1, 3] [2 - I, 4] """ out = self.T.C return out @property def D(self): """Dirac conjugation.""" from sympy.physics.matrices import mgamma out = self.H * mgamma(0) return out def __getitem__(self,key): """ >>> from sympy import Matrix, I >>> m=Matrix(((1,2+I),(3,4))) >>> m #doctest: +NORMALIZE_WHITESPACE [1, 2 + I] [3, 4] >>> m[1,0] 3 >>> m.H[1,0] 2 - I """ if type(key) is tuple: i, j = key if type(i) is slice or type(j) is slice: return self.submatrix(key) else: # a2idx inlined if not type(i) is int: try: i = i.__index__() except AttributeError: raise IndexError("Invalid index a[%r]" % (key,)) # a2idx inlined if not type(j) is int: try: j = j.__index__() except AttributeError: raise IndexError("Invalid index a[%r]" % (key,)) if not (i>=0 and i=0 and j < self.cols): raise IndexError("Index out of range: a[%s]" % (key,)) else: return self.mat[i*self.cols + j] else: # row-wise decomposition of matrix if type(key) is slice: return self.mat[key] else: k = a2idx(key) if k is not None: return self.mat[k] raise IndexError("Invalid index: a[%s]" % repr(key)) def __setitem__(self, key, value): """ >>> from sympy import Matrix, I >>> m=Matrix(((1,2+I),(3,4))) >>> m #doctest: +NORMALIZE_WHITESPACE [1, 2 + I] [3, 4] >>> m[1,0]=9 >>> m #doctest: +NORMALIZE_WHITESPACE [1, 2 + I] [9, 4] """ if type(key) is tuple: i, j = key if type(i) is slice or type(j) is slice: if isinstance(value, Matrix): self.copyin_matrix(key, value) return if is_sequence(value): self.copyin_list(key, value) return else: # a2idx inlined if not type(i) is int: try: i = i.__index__() except AttributeError: raise IndexError("Invalid index a[%r]" % (key,)) # a2idx inlined if not type(j) is int: try: j = j.__index__() except AttributeError: raise IndexError("Invalid index a[%r]" % (key,)) if not (i>=0 and i=0 and j < self.cols): raise IndexError("Index out of range: a[%s]" % (key,)) else: self.mat[i*self.cols + j] = sympify(value) return else: # row-wise decomposition of matrix if type(key) is slice: raise IndexError("Vector slices not implemented yet.") else: k = a2idx(key) if k is not None: self.mat[k] = sympify(value) return raise IndexError("Invalid index: a[%s]"%repr(key)) def __array__(self): return matrix2numpy(self) def __len__(self): """ Return the number of elements of self. Implemented mainly so bool(Matrix()) == False. """ return self.rows * self.cols def tolist(self): """ Return the Matrix converted in a python list. >>> from sympy import Matrix >>> m=Matrix(3, 3, range(9)) >>> m [0, 1, 2] [3, 4, 5] [6, 7, 8] >>> m.tolist() [[0, 1, 2], [3, 4, 5], [6, 7, 8]] """ ret = [0]*self.rows for i in xrange(self.rows): ret[i] = self.mat[i*self.cols:(i+1)*self.cols] return ret def copyin_matrix(self, key, value): rlo, rhi = self.slice2bounds(key[0], self.rows) clo, chi = self.slice2bounds(key[1], self.cols) if value.rows != rhi - rlo or value.cols != chi - clo: raise ShapeError("The Matrix `value` doesn't have the same dimensions " + "as the in sub-Matrix given by `key`.") for i in range(value.rows): for j in range(value.cols): self[i+rlo, j+clo] = sympify(value[i,j]) def copyin_list(self, key, value): if not is_sequence(value): raise TypeError("`value` must be an ordered iterable, not %s." % type(value)) self.copyin_matrix(key, Matrix(value)) def hash(self): """Compute a hash every time, because the matrix elements could change.""" return hash(self.__str__() ) @property def shape(self): return (self.rows, self.cols) def __rmul__(self,a): if hasattr(a, "__array__") and a.shape != (): return matrix_multiply(a,self) out = Matrix(self.rows,self.cols,map(lambda i: a*i,self.mat)) return out def expand(self): out = Matrix(self.rows,self.cols,map(lambda i: i.expand(), self.mat)) return out def combine(self): out = Matrix(self.rows,self.cols,map(lambda i: i.combine(),self.mat)) return out def subs(self, *args): out = Matrix(self.rows,self.cols,map(lambda i: i.subs(*args),self.mat)) return out def __sub__(self,a): return self + (-a) def __mul__(self,a): if hasattr(a, "__array__") and a.shape != (): return matrix_multiply(self,a) out = Matrix(self.rows,self.cols,map(lambda i: i*a,self.mat)) return out def __pow__(self, num): if not self.is_square: raise NonSquareMatrixError() if isinstance(num, int) or isinstance(num, Integer): n = int(num) if n < 0: return self.inv() ** -n # A**-2 = (A**-1)**2 a = eye(self.cols) s = self while n: if n%2: a *= s n -= 1 s *= s n //= 2 return a elif isinstance(num, Rational): try: P, D = self.diagonalize() except MatrixError: raise NotImplementedError("Implemented only for diagonalizable matrices") for i in range(D.rows): D[i, i] = D[i, i]**num return P * D * P.inv() else: raise NotImplementedError("Only integer and rational values are supported") def __add__(self,a): return matrix_add(self,a) def __radd__(self,a): return matrix_add(a,self) def __div__(self,a): return self * (S.One/a) def __truediv__(self,a): return self.__div__(a) def multiply(self,b): """Returns self*b """ return matrix_multiply(self,b) def add(self,b): """Return self+b """ return matrix_add(self,b) def __neg__(self): return -1*self def __eq__(self, a): if not isinstance(a, (Matrix, Basic)): a = sympify(a) if isinstance(a, Matrix) and self.shape == a.shape: return all(self[i, j] == a[i, j] for i in xrange(self.rows) for j in xrange(self.cols)) else: return False def __ne__(self, a): if not isinstance(a, (Matrix, Basic)): a = sympify(a) if isinstance(a, Matrix) and self.shape == a.shape: return any(self[i, j] != a[i, j] for i in xrange(self.rows) for j in xrange(self.cols)) else: return True def __hash__(self): return super(Matrix, self).__hash__() def _format_str(self, strfunc, rowsep='\n'): # Handle zero dimensions: if self.rows == 0 or self.cols == 0: return '[]' # Build table of string representations of the elements res = [] # Track per-column max lengths for pretty alignment maxlen = [0] * self.cols for i in range(self.rows): res.append([]) for j in range(self.cols): string = strfunc(self[i,j]) res[-1].append(string) maxlen[j] = max(len(string), maxlen[j]) # Patch strings together for i, row in enumerate(res): for j, elem in enumerate(row): # Pad each element up to maxlen so the columns line up row[j] = elem.rjust(maxlen[j]) res[i] = "[" + ", ".join(row) + "]" return rowsep.join(res) def __str__(self): return sstr(self) def __repr__(self): return sstr(self) def cholesky(self): """ Returns the Cholesky Decomposition L of a Matrix A such that L * L.T = A A must be a square, symmetric, positive-definite and non-singular matrix >>> from sympy.matrices import Matrix >>> A = Matrix(((25,15,-5),(15,18,0),(-5,0,11))) >>> A.cholesky() [ 5, 0, 0] [ 3, 3, 0] [-1, 1, 3] >>> A.cholesky() * A.cholesky().T [25, 15, -5] [15, 18, 0] [-5, 0, 11] """ if not self.is_square: raise NonSquareMatrixError("Matrix must be square.") if not self.is_symmetric(): raise ValueError("Matrix must be symmetric.") return self._cholesky() def _cholesky(self): """ Helper function of cholesky. Without the error checks. To be used privately. """ L = zeros((self.rows, self.rows)) for i in xrange(self.rows): for j in xrange(i): L[i, j] = (1 / L[j, j]) * (self[i, j] - sum(L[i, k] * L[j, k] for k in xrange(j))) L[i, i] = (self[i, i] - sum(L[i, k] ** 2 for k in xrange(i))) ** (S(1)/2) return L def LDLdecomposition(self): """ Returns the LDL Decomposition (L,D) of matrix A, such that L * D * L.T == A This method eliminates the use of square root. Further this ensures that all the diagonal entries of L are 1. A must be a square, symmetric, positive-definite and non-singular matrix. >>> from sympy.matrices import Matrix, eye >>> A = Matrix(((25,15,-5),(15,18,0),(-5,0,11))) >>> L, D = A.LDLdecomposition() >>> L [ 1, 0, 0] [ 3/5, 1, 0] [-1/5, 1/3, 1] >>> D [25, 0, 0] [ 0, 9, 0] [ 0, 0, 9] >>> L * D * L.T * A.inv() == eye(A.rows) True """ if not self.is_square: raise NonSquareMatrixException("Matrix must be square.") if not self.is_symmetric(): raise ValueError("Matrix must be symmetric.") return self._LDLdecomposition() def _LDLdecomposition(self): """ Helper function of LDLdecomposition. Without the error checks. To be used privately. """ D = zeros((self.rows, self.rows)) L = eye(self.rows) for i in xrange(self.rows): for j in xrange(i): L[i, j] = (1 / D[j, j]) * (self[i, j] - sum( L[i, k] * L[j, k] * D[k, k] for k in xrange(j))) D[i, i] = self[i, i] - sum(L[i, k]**2 * D[k, k] for k in xrange(i)) return L, D def lower_triangular_solve(self, rhs): """ Solves Ax = B, where A is a lower triangular matrix. """ if not self.is_square: raise NonSquareMatrixException("Matrix must be square.") if rhs.rows != self.rows: raise ShapeError("Matrices size mismatch.") if not self.is_lower(): raise ValueError("Matrix must be lower triangular.") return self._lower_triangular_solve(rhs) def _lower_triangular_solve(self, rhs): """ Helper function of function lower_triangular_solve. Without the error checks. To be used privately. """ X = zeros((self.rows, 1)) for i in xrange(self.rows): if self[i, i] == 0: raise TypeError("Matrix must be non-singular.") X[i, 0] = (rhs[i, 0] - sum(self[i, k] * X[k, 0] for k in xrange(i))) / self[i, i] return X def upper_triangular_solve(self, rhs): """ Solves Ax = B, where A is an upper triangular matrix. """ if not self.is_square: raise NonSquareMatrixException("Matrix must be square.") if rhs.rows != self.rows: raise TypeError("Matrix size mismatch.") if not self.is_upper(): raise TypeError("Matrix is not upper triangular.") return self._upper_triangular_solve(rhs) def _upper_triangular_solve(self, rhs): """ Helper function of function upper_triangular_solve. Without the error checks, to be used privately. """ X = zeros((self.rows, 1)) for i in reversed(xrange(self.rows)): if self[i, i] == 0: raise ValueError("Matrix must be non-singular.") X[i, 0] = (rhs[i, 0] - sum(self[i, k] * X[k, 0] for k in xrange(i+1, self.rows))) / self[i, i] return X def cholesky_solve(self, rhs): """ Solves Ax = B using Cholesky decomposition, for a general square non-singular matrix. For a non-square matrix with rows > cols, the least squares solution is returned. """ if self.is_symmetric(): L = self._cholesky() elif self.rows >= self.cols: L = (self.T * self)._cholesky() rhs = self.T * rhs else: raise NotImplementedError("Under-determined System.") Y = L._lower_triangular_solve(rhs) return (L.T)._upper_triangular_solve(Y) def diagonal_solve(self, rhs): """ Solves Ax = B efficiently, where A is a diagonal Matrix, with non-zero diagonal entries. """ if not self.is_diagonal: raise TypeError("Matrix should be diagonal") if rhs.rows != self.rows: raise TypeError("Size mis-match") return self._diagonal_solve(rhs) def _diagonal_solve(self, rhs): """ Helper function of function diagonal_solve, without the error checks, to be used privately. """ return Matrix(rhs.rows, 1, lambda i, j: rhs[i, 0] / self[i, i]) def LDLsolve(self, rhs): """ Solves Ax = B using LDL decomposition, for a general square and non-singular matrix. For a non-square matrix with rows > cols, the least squares solution is returned. """ if self.is_symmetric(): L, D = self.LDLdecomposition() elif self.rows >= self.cols: L, D = (self.T * self).LDLdecomposition() rhs = self.T * rhs else: raise NotImplementedError("Under-determined System.") Y = L._lower_triangular_solve(rhs) Z = D._diagonal_solve(Y) return (L.T)._upper_triangular_solve(Z) def inv(self, method="GE", iszerofunc=_iszero, try_block_diag=False): """ Calculates the matrix inverse. According to the "method" parameter, it calls the appropriate method: GE .... inverse_GE() LU .... inverse_LU() ADJ ... inverse_ADJ() According to the "try_block_diag" parameter, it will try to form block diagonal matrices using the method get_diag_blocks(), invert these individually, and then reconstruct the full inverse matrix. Note, the GE and LU methods may require the matrix to be simplified before it is inverted in order to properly detect zeros during pivoting. In difficult cases a custom zero detection function can be provided by setting the iszerosfunc argument to a function that should return True if its argument is zero. """ if not self.is_square: raise NonSquareMatrixError() if try_block_diag: blocks = self.get_diag_blocks() r = [] for block in blocks: r.append(block.inv(method=method, iszerofunc=iszerofunc)) return diag(*r) if method == "GE": return self.inverse_GE(iszerofunc=iszerofunc) elif method == "LU": return self.inverse_LU(iszerofunc=iszerofunc) elif method == "ADJ": return self.inverse_ADJ() else: raise ValueError("Inversion method unrecognized") def __mathml__(self): mml = "" for i in range(self.rows): mml += "" for j in range(self.cols): mml += self[i,j].__mathml__() mml += "" return "" + mml + "" def row(self, i, f): """ Elementary row operation using functor >>> from sympy import ones >>> I = ones(3) >>> I.row(1,lambda i,j: i*3) >>> I [1, 1, 1] [3, 3, 3] [1, 1, 1] """ for j in range(0, self.cols): self[i, j] = f(self[i, j], j) def col(self, j, f): """ Elementary column operation using functor >>> from sympy import ones >>> I = ones(3) >>> I.col(0,lambda i,j: i*3) >>> I [3, 1, 1] [3, 1, 1] [3, 1, 1] """ for i in range(0, self.rows): self[i, j] = f(self[i, j], i) def row_swap(self, i, j): for k in range(0, self.cols): self[i, k], self[j, k] = self[j, k], self[i, k] def col_swap(self, i, j): for k in range(0, self.rows): self[k, i], self[k, j] = self[k, j], self[k, i] def row_del(self, i): self.mat = self.mat[:i*self.cols] + self.mat[(i+1)*self.cols:] self.rows -= 1 def col_del(self, i): """ >>> import sympy >>> M = sympy.matrices.eye(3) >>> M.col_del(1) >>> M #doctest: +NORMALIZE_WHITESPACE [1, 0] [0, 0] [0, 1] """ for j in range(self.rows-1, -1, -1): del self.mat[i+j*self.cols] self.cols -= 1 def row_join(self, rhs): """ Concatenates two matrices along self's last and rhs's first column >>> from sympy import Matrix >>> M = Matrix(3,3,lambda i,j: i+j) >>> V = Matrix(3,1,lambda i,j: 3+i+j) >>> M.row_join(V) [0, 1, 2, 3] [1, 2, 3, 4] [2, 3, 4, 5] """ if self.rows != rhs.rows: raise ShapeError("`self` and `rhs` must have the same number of rows.") newmat = self.zeros((self.rows, self.cols + rhs.cols)) newmat[:,:self.cols] = self[:,:] newmat[:,self.cols:] = rhs return newmat def col_join(self, bott): """ Concatenates two matrices along self's last and bott's first row >>> from sympy import Matrix >>> M = Matrix(3,3,lambda i,j: i+j) >>> V = Matrix(1,3,lambda i,j: 3+i+j) >>> M.col_join(V) [0, 1, 2] [1, 2, 3] [2, 3, 4] [3, 4, 5] """ if self.cols != bott.cols: raise ShapeError("`self` and `bott` must have the same number of columns.") newmat = self.zeros((self.rows+bott.rows, self.cols)) newmat[:self.rows,:] = self[:,:] newmat[self.rows:,:] = bott return newmat def row_insert(self, pos, mti): """ >>> from sympy import Matrix, zeros >>> M = Matrix(3,3,lambda i,j: i+j) >>> M [0, 1, 2] [1, 2, 3] [2, 3, 4] >>> V = zeros((1, 3)) >>> V [0, 0, 0] >>> M.row_insert(1,V) [0, 1, 2] [0, 0, 0] [1, 2, 3] [2, 3, 4] """ if pos is 0: return mti.col_join(self) if self.cols != mti.cols: raise ShapeError("`self` and `mti` must have the same number of columns.") newmat = self.zeros((self.rows + mti.rows, self.cols)) newmat[:pos,:] = self[:pos,:] newmat[pos:pos+mti.rows,:] = mti[:,:] newmat[pos+mti.rows:,:] = self[pos:,:] return newmat def col_insert(self, pos, mti): """ >>> from sympy import Matrix, zeros >>> M = Matrix(3,3,lambda i,j: i+j) >>> M [0, 1, 2] [1, 2, 3] [2, 3, 4] >>> V = zeros((3, 1)) >>> V [0] [0] [0] >>> M.col_insert(1,V) [0, 0, 1, 2] [1, 0, 2, 3] [2, 0, 3, 4] """ if pos is 0: return mti.row_join(self) if self.rows != mti.rows: raise ShapeError("self and mti must have the same number of rows.") newmat = self.zeros((self.rows, self.cols + mti.cols)) newmat[:,:pos] = self[:,:pos] newmat[:,pos:pos+mti.cols] = mti[:,:] newmat[:,pos+mti.cols:] = self[:,pos:] return newmat def trace(self): if not self.is_square: raise NonSquareMatrixError() trace = 0 for i in range(self.cols): trace += self[i,i] return trace def submatrix(self, keys): """ >>> from sympy import Matrix >>> m = Matrix(4,4,lambda i,j: i+j) >>> m #doctest: +NORMALIZE_WHITESPACE [0, 1, 2, 3] [1, 2, 3, 4] [2, 3, 4, 5] [3, 4, 5, 6] >>> m[0:1, 1] #doctest: +NORMALIZE_WHITESPACE [1] >>> m[0:2, 0:1] #doctest: +NORMALIZE_WHITESPACE [0] [1] >>> m[2:4, 2:4] #doctest: +NORMALIZE_WHITESPACE [4, 5] [5, 6] """ if not isinstance(keys[0], slice) and not isinstance(keys[1], slice): raise TypeError("At least one element of `keys` must be a slice object.") rlo, rhi = self.slice2bounds(keys[0], self.rows) clo, chi = self.slice2bounds(keys[1], self.cols) if not ( 0<=rlo<=rhi and 0<=clo<=chi ): raise IndexError("Slice indices out of range: a[%s]"%repr(keys)) outLines, outCols = rhi-rlo, chi-clo outMat = [0]*outLines*outCols for i in xrange(outLines): outMat[i*outCols:(i+1)*outCols] = self.mat[(i+rlo)*self.cols+clo:(i+rlo)*self.cols+chi] return Matrix(outLines,outCols,outMat) def extract(self, rowsList, colsList): """ Extract a submatrix by specifying a list of rows and columns Examples: >>> from sympy import Matrix >>> m = Matrix(4, 3, lambda i, j: i*3 + j) >>> m #doctest: +NORMALIZE_WHITESPACE [0, 1, 2] [3, 4, 5] [6, 7, 8] [9, 10, 11] >>> m.extract([0,1,3],[0,1]) #doctest: +NORMALIZE_WHITESPACE [0, 1] [3, 4] [9, 10] See also: .submatrix() """ cols = self.cols rows = self.rows mat = self.mat if not all(i < rows for i in rowsList): raise IndexError("Row indices out of range") if not all(j < cols for j in colsList): raise IndexError("Column indices out of range") return Matrix(len(rowsList), len(colsList), lambda i,j: mat[rowsList[i]*cols + colsList[j]]) def slice2bounds(self, key, defmax): """ Takes slice or number and returns (min,max) for iteration Takes a default maxval to deal with the slice ':' which is (none, none) """ if isinstance(key, slice): lo, hi = 0, defmax if key.start is not None: if key.start >= 0: lo = key.start else: lo = defmax+key.start if key.stop is not None: if key.stop >= 0: hi = key.stop else: hi = defmax+key.stop return lo, hi elif isinstance(key, int): if key >= 0: return key, key+1 else: return defmax+key, defmax+key+1 else: raise IndexError("Improper index type") def applyfunc(self, f): """ >>> from sympy import Matrix >>> m = Matrix(2,2,lambda i,j: i*2+j) >>> m #doctest: +NORMALIZE_WHITESPACE [0, 1] [2, 3] >>> m.applyfunc(lambda i: 2*i) #doctest: +NORMALIZE_WHITESPACE [0, 2] [4, 6] """ if not callable(f): raise TypeError("`f` must be callable.") out = Matrix(self.rows,self.cols,map(f,self.mat)) return out def evalf(self, prec=None, **options): if prec is None: return self.applyfunc(lambda i: i.evalf(**options)) else: return self.applyfunc(lambda i: i.evalf(prec, **options)) def reshape(self, _rows, _cols): """ >>> from sympy import Matrix >>> m = Matrix(2,3,lambda i,j: 1) >>> m #doctest: +NORMALIZE_WHITESPACE [1, 1, 1] [1, 1, 1] >>> m.reshape(1,6) #doctest: +NORMALIZE_WHITESPACE [1, 1, 1, 1, 1, 1] >>> m.reshape(3,2) #doctest: +NORMALIZE_WHITESPACE [1, 1] [1, 1] [1, 1] """ if len(self) != _rows*_cols: print "Invalid reshape parameters %d %d" % (_rows, _cols) return Matrix(_rows, _cols, lambda i,j: self.mat[i*_cols + j]) def print_nonzero (self, symb="X"): """ Shows location of non-zero entries for fast shape lookup :: >>> from sympy import Matrix, matrices >>> m = Matrix(2,3,lambda i,j: i*3+j) >>> m #doctest: +NORMALIZE_WHITESPACE [0, 1, 2] [3, 4, 5] >>> m.print_nonzero() #doctest: +NORMALIZE_WHITESPACE [ XX] [XXX] >>> m = matrices.eye(4) >>> m.print_nonzero("x") #doctest: +NORMALIZE_WHITESPACE [x ] [ x ] [ x ] [ x] """ s = "" for i in range(self.rows): s += "[" for j in range(self.cols): if self[i,j] == 0: s += " " else: s += symb + "" s += "]\n" print s def LUsolve(self, rhs, iszerofunc=_iszero): """ Solve the linear system Ax = b for x. self is the coefficient matrix A and rhs is the right side b. This is for symbolic matrices, for real or complex ones use sympy.mpmath.lu_solve or sympy.mpmath.qr_solve. """ if rhs.rows != self.rows: raise ShapeError("`self` and `rhs` must have the same number of rows.") A, perm = self.LUdecomposition_Simple(iszerofunc=_iszero) n = self.rows b = rhs.permuteFwd(perm) # forward substitution, all diag entries are scaled to 1 for i in range(n): for j in range(i): b.row(i, lambda x,k: x - b[j,k]*A[i,j]) # backward substitution for i in range(n-1,-1,-1): for j in range(i+1, n): b.row(i, lambda x,k: x - b[j,k]*A[i,j]) b.row(i, lambda x,k: x / A[i,i]) return b def LUdecomposition(self, iszerofunc=_iszero): """ Returns the decomposition LU and the row swaps p. Example: >>> from sympy import Matrix >>> a = Matrix([[4, 3], [6, 3]]) >>> L, U, _ = a.LUdecomposition() >>> L [ 1, 0] [3/2, 1] >>> U [4, 3] [0, -3/2] """ combined, p = self.LUdecomposition_Simple(iszerofunc=_iszero) L = self.zeros(self.rows) U = self.zeros(self.rows) for i in range(self.rows): for j in range(self.rows): if i > j: L[i,j] = combined[i,j] else: if i == j: L[i,i] = 1 U[i,j] = combined[i,j] return L, U, p def LUdecomposition_Simple(self, iszerofunc=_iszero): """ Returns A comprised of L,U (L's diag entries are 1) and p which is the list of the row swaps (in order). """ if not self.is_square: raise NonSquareMatrixError() n = self.rows A = self[:,:] p = [] # factorization for j in range(n): for i in range(j): for k in range(i): A[i,j] = A[i,j] - A[i,k]*A[k,j] pivot = -1 for i in range(j,n): for k in range(j): A[i,j] = A[i,j] - A[i,k]*A[k,j] # find the first non-zero pivot, includes any expression if pivot == -1 and not iszerofunc(A[i,j]): pivot = i if pivot < 0: # this result is based on iszerofunc's analysis of the possible pivots, so even though # the element may not be strictly zero, the supplied iszerofunc's evaluation gave True raise ValueError("No nonzero pivot found; inversion failed.") if pivot != j: # row must be swapped A.row_swap(pivot,j) p.append([pivot,j]) scale = 1 / A[j,j] for i in range(j+1,n): A[i,j] = A[i,j] * scale return A, p def LUdecompositionFF(self): """ Compute a fraction-free LU decomposition. Returns 4 matrices P, L, D, U such that PA = L D**-1 U. If the elements of the matrix belong to some integral domain I, then all elements of L, D and U are guaranteed to belong to I. **Reference** - W. Zhou & D.J. Jeffrey, "Fraction-free matrix factors: new forms for LU and QR factors". Frontiers in Computer Science in China, Vol 2, no. 1, pp. 67-80, 2008. """ n, m = self.rows, self.cols U, L, P = self[:,:], eye(n), eye(n) DD = zeros(n) # store it smarter since it's just diagonal oldpivot = 1 for k in range(n-1): if U[k,k] == 0: for kpivot in range(k+1, n): if U[kpivot, k] != 0: break else: raise ValueError("Matrix is not full rank") U[k, k:], U[kpivot, k:] = U[kpivot, k:], U[k, k:] L[k, :k], L[kpivot, :k] = L[kpivot, :k], L[k, :k] P[k, :], P[kpivot, :] = P[kpivot, :], P[k, :] L[k,k] = Ukk = U[k,k] DD[k,k] = oldpivot * Ukk for i in range(k+1, n): L[i,k] = Uik = U[i,k] for j in range(k+1, m): U[i,j] = (Ukk * U[i,j] - U[k,j]*Uik) / oldpivot U[i,k] = 0 oldpivot = Ukk DD[n-1,n-1] = oldpivot return P, L, DD, U def cofactorMatrix(self, method="berkowitz"): out = Matrix(self.rows, self.cols, lambda i,j: self.cofactor(i, j, method)) return out def minorEntry(self, i, j, method="berkowitz"): if not 0 <= i < self.rows or not 0 <= j < self.cols: raise ValueError("`i` and `j` must satisfy 0 <= i < `self.rows` " + "(%d)" % self.rows + "and 0 <= j < `self.cols` (%d)." % self.cols) return self.minorMatrix(i,j).det(method) def minorMatrix(self, i, j): if not 0 <= i < self.rows or not 0 <= j < self.cols: raise ValueError("`i` and `j` must satisfy 0 <= i < `self.rows` " + "(%d)" % self.rows + "and 0 <= j < `self.cols` (%d)." % self.cols) return self.delRowCol(i,j) def cofactor(self, i, j, method="berkowitz"): if (i+j) % 2 == 0: return self.minorEntry(i, j, method) else: return -1 * self.minorEntry(i, j, method) def jacobian(self, X): """ Calculates the Jacobian matrix (derivative of a vectorial function). *self* A vector of expressions representing functions f_i(x_1, ..., x_n). *X* The set of x_i's in order, it can be a list or a Matrix Both self and X can be a row or a column matrix in any order (jacobian() should always work). Examples:: >>> from sympy import sin, cos, Matrix >>> from sympy.abc import rho, phi >>> X = Matrix([rho*cos(phi), rho*sin(phi), rho**2]) >>> Y = Matrix([rho, phi]) >>> X.jacobian(Y) [cos(phi), -rho*sin(phi)] [sin(phi), rho*cos(phi)] [ 2*rho, 0] >>> X = Matrix([rho*cos(phi), rho*sin(phi)]) >>> X.jacobian(Y) [cos(phi), -rho*sin(phi)] [sin(phi), rho*cos(phi)] """ if not isinstance(X, Matrix): X = Matrix(X) # Both X and self can be a row or a column matrix, so we need to make # sure all valid combinations work, but everything else fails: if self.shape[0] == 1: m = self.shape[1] elif self.shape[1] == 1: m = self.shape[0] else: raise TypeError("self must be a row or a column matrix") if X.shape[0] == 1: n = X.shape[1] elif X.shape[1] == 1: n = X.shape[0] else: raise TypeError("X must be a row or a column matrix") # m is the number of functions and n is the number of variables # computing the Jacobian is now easy: return Matrix(m, n, lambda j, i: self[j].diff(X[i])) def QRdecomposition(self): """ Return Q,R where A = Q*R, Q is orthogonal and R is upper triangular. Examples This is the example from wikipedia:: >>> from sympy import Matrix, eye >>> A = Matrix([[12,-51,4],[6,167,-68],[-4,24,-41]]) >>> Q, R = A.QRdecomposition() >>> Q [ 6/7, -69/175, -58/175] [ 3/7, 158/175, 6/175] [-2/7, 6/35, -33/35] >>> R [14, 21, -14] [ 0, 175, -70] [ 0, 0, 35] >>> A == Q*R True QR factorization of an identity matrix >>> A = Matrix([[1,0,0],[0,1,0],[0,0,1]]) >>> Q, R = A.QRdecomposition() >>> Q [1, 0, 0] [0, 1, 0] [0, 0, 1] >>> R [1, 0, 0] [0, 1, 0] [0, 0, 1] """ if not self.rows >= self.cols: raise MatrixError("The number of rows must be greater than columns") n = self.rows m = self.cols rank = n row_reduced = self.rref()[0] for i in range(row_reduced.rows): if Matrix(row_reduced[i*m:(i+1)*m]).norm() == 0: rank -= 1 if not rank == self.cols: raise MatrixError("The rank of the matrix must match the columns") Q, R = self.zeros((n, m)), self.zeros(m) for j in range(m): # for each column vector tmp = self[:,j] # take original v for i in range(j): # subtract the project of self on new vector tmp -= Q[:,i] * self[:,j].dot(Q[:,i]) tmp.expand() # normalize it R[j,j] = tmp.norm() Q[:,j] = tmp / R[j,j] if Q[:,j].norm() != 1: raise NotImplementedError("Could not normalize the vector %d." % j) for i in range(j): R[i,j] = Q[:,i].dot(self[:,j]) return Q,R def QRsolve(self, b): """ Solve the linear system 'Ax = b'. 'self' is the matrix 'A', the method argument is the vector 'b'. The method returns the solution vector 'x'. If 'b' is a matrix, the system is solved for each column of 'b' and the return value is a matrix of the same shape as 'b'. This method is slower (approximately by a factor of 2) but more stable for floating-point arithmetic than the LUsolve method. However, LUsolve usually uses an exact arithmetic, so you don't need to use QRsolve. This is mainly for educational purposes and symbolic matrices, for real (or complex) matrices use sympy.mpmath.qr_solve. """ Q, R = self.QRdecomposition() y = Q.T * b # back substitution to solve R*x = y: # We build up the result "backwards" in the vector 'x' and reverse it # only in the end. x = [] n = R.rows for j in range(n-1, -1, -1): tmp = y[j,:] for k in range(j+1, n): tmp -= R[j,k] * x[n-1-k] x.append(tmp/R[j,j]) return Matrix([row.mat for row in reversed(x)]) # Utility functions def simplify(self, simplify=sympy_simplify, ratio=1.7): """Simplify the elements of a matrix in place. If (result length)/(input length) > ratio, then input is returned unmodified. If 'ratio=oo', then simplify() is applied anyway. See also simplify(). """ for i in xrange(len(self.mat)): self.mat[i] = simplify(self.mat[i], ratio=ratio) #def evaluate(self): # no more eval() so should be removed # for i in range(self.rows): # for j in range(self.cols): # self[i,j] = self[i,j].eval() def cross(self, b): if not is_sequence(b, include=Matrix): raise TypeError("`b` must be an ordered iterable or Matrix, not %s." % type(b)) if not (self.rows == 1 and self.cols == 3 or \ self.rows == 3 and self.cols == 1 ) and \ (b.rows == 1 and b.cols == 3 or \ b.rows == 3 and b.cols == 1): raise ShapeError("Dimensions incorrect for cross product.") else: return Matrix(1,3,((self[1]*b[2] - self[2]*b[1]), (self[2]*b[0] - self[0]*b[2]), (self[0]*b[1] - self[1]*b[0]))) def dot(self, b): if not is_sequence(b, include=Matrix): raise TypeError("`b` must be an ordered iterable or Matrix, not %s." % type(b)) m = len(b) if len(self) != m: raise ShapeError("Dimensions incorrect for dot product.") prod = 0 for i in range(m): prod += self[i] * b[i] return prod def multiply_elementwise(self, b): """Return the Hadamard product (elementwise product) of A and B >>> import sympy >>> A = sympy.Matrix([[0, 1, 2], [3, 4, 5]]) >>> B = sympy.Matrix([[1, 10, 100], [100, 10, 1]]) >>> print A.multiply_elementwise(B) [ 0, 10, 200] [300, 40, 5] """ return matrix_multiply_elementwise(self, b) def norm(self, ord=None): """Return the Norm of a Matrix or Vector. In the simplest case this is the geometric size of the vector Other norms can be specified by the ord parameter ===== ============================ ========================== ord norm for matrices norm for vectors ===== ============================ ========================== None Frobenius norm 2-norm 'fro' Frobenius norm - does not exist inf -- max(abs(x)) -inf -- min(abs(x)) 1 -- as below -1 -- as below 2 2-norm (largest sing. value) as below -2 smallest singular value as below other - does not exist sum(abs(x)**ord)**(1./ord) ===== ============================ ========================== >>> from sympy import Matrix, var, trigsimp, cos, sin >>> x = var('x', real=True) >>> v = Matrix([cos(x), sin(x)]) >>> print trigsimp( v.norm() ) 1 >>> print v.norm(10) (sin(x)**10 + cos(x)**10)**(1/10) >>> A = Matrix([[1,1], [1,1]]) >>> print A.norm(2)# Spectral norm (max of |Ax|/|x| under 2-vector-norm) 2 >>> print A.norm(-2) # Inverse spectral norm (smallest singular value) 0 >>> print A.norm() # Frobenius Norm 2 """ # Row or Column Vector Norms if self.rows == 1 or self.cols == 1: if ord == 2 or ord == None: # Common case sqrt() return Add(*(abs(i)**2 for i in self.mat))**S.Half elif ord == 1: # sum(abs(x)) return Add(*(abs(i) for i in self.mat)) elif ord == S.Infinity: # max(abs(x)) return Max(*self.applyfunc(abs)) elif ord == S.NegativeInfinity: # min(abs(x)) return Min(*self.applyfunc(abs)) # Otherwise generalize the 2-norm, Sum(x_i**ord)**(1/ord) # Note that while useful this is not mathematically a norm try: return Pow( Add(*(abs(i)**ord for i in self.mat)), S(1)/ord ) except: raise ValueError("Expected order to be Number, Symbol, oo") # Matrix Norms else: if ord == 2: # Spectral Norm # Maximum singular value return Max(*self.singular_values()) elif ord == -2: # Minimum singular value return Min(*self.singular_values()) elif (ord == None or isinstance(ord,str) and ord.lower() in ['f', 'fro', 'frobenius', 'vector']): # Reshape as vector and send back to norm function return self.vec().norm(ord=2) else: raise NotImplementedError("Matrix Norms under development") def normalized(self): if self.rows != 1 and self.cols != 1: raise ShapeError("A Matrix must be a vector to normalize.") norm = self.norm() out = self.applyfunc(lambda i: i / norm) return out def project(self, v): """Project onto v.""" return v * (self.dot(v) / v.dot(v)) def permuteBkwd(self, perm): copy = self[:,:] for i in range(len(perm)-1, -1, -1): copy.row_swap(perm[i][0], perm[i][1]) return copy def permuteFwd(self, perm): copy = self[:,:] for i in range(len(perm)): copy.row_swap(perm[i][0], perm[i][1]) return copy def delRowCol(self, i, j): # used only for cofactors, makes a copy M = self[:,:] M.row_del(i) M.col_del(j) return M def exp(self): """ Returns the exponent of a matrix """ if not self.is_square: raise NonSquareMatrixError("Exponentiation is valid only for square matrices") try: U, D = self.diagonalize() except MatrixError: raise NotImplementedError("Exponentiation is implemented only for diagonalizable matrices") for i in xrange(0, D.rows): D[i, i] = C.exp(D[i, i]) return U * D * U.inv() def zeros(self, dims): """Returns a dims = (d1,d2) matrix of zeros.""" n, m = _dims_to_nm( dims ) return Matrix(n,m,[S.Zero]*n*m) def eye(self, n): """Returns the identity matrix of size n.""" tmp = self.zeros(n) for i in range(tmp.rows): tmp[i,i] = S.One return tmp @property def is_square(self): return self.rows == self.cols def is_nilpotent(self): """ Checks if a matrix is nilpotent. A matrix B is nilpotent if for some integer k, B**k is a zero matrix. Example: >>> from sympy import Matrix >>> a = Matrix([[0,0,0],[1,0,0],[1,1,0]]) >>> a.is_nilpotent() True >>> a = Matrix([[1,0,1],[1,0,0],[1,1,0]]) >>> a.is_nilpotent() False """ if not self.is_square: raise NonSquareMatrixError("Nilpotency is valid only for square matrices") x = Dummy('x') if self.charpoly(x).args[0] == x**self.rows: return True return False def is_upper(self): """ Check if matrix is an upper triangular matrix. Example: >>> from sympy import Matrix >>> m = Matrix(2,2,[1, 0, 0, 1]) >>> m [1, 0] [0, 1] >>> m.is_upper() True >>> m = Matrix(3,3,[5, 1, 9, 0, 4 , 6, 0, 0, 5]) >>> m [5, 1, 9] [0, 4, 6] [0, 0, 5] >>> m.is_upper() True >>> m = Matrix(2,3,[4, 2, 5, 6, 1, 1]) >>> m [4, 2, 5] [6, 1, 1] >>> m.is_upper() False """ for i in xrange(1, self.rows): for j in xrange(0, i): if self[i,j] != 0: return False return True def is_lower(self): """ Check if matrix is a lower triangular matrix. Example: >>> from sympy import Matrix >>> m = Matrix(2,2,[1, 0, 0, 1]) >>> m [1, 0] [0, 1] >>> m.is_lower() True >>> m = Matrix(3,3,[2, 0, 0, 1, 4 , 0, 6, 6, 5]) >>> m [2, 0, 0] [1, 4, 0] [6, 6, 5] >>> m.is_lower() True >>> from sympy.abc import x, y >>> m = Matrix(2,2,[x**2 + y, y**2 + x, 0, x + y]) >>> m [x**2 + y, x + y**2] [ 0, x + y] >>> m.is_lower() False """ for i in xrange(0, self.rows): for j in xrange(i+1, self.cols): if self[i, j] != 0: return False return True def is_upper_hessenberg(self): """ Checks if the matrix is the upper hessenberg form. The upper hessenberg matrix has zero entries below the first subdiagonal. Example: >>> from sympy.matrices import Matrix >>> a = Matrix([[1,4,2,3],[3,4,1,7],[0,2,3,4],[0,0,1,3]]) >>> a [1, 4, 2, 3] [3, 4, 1, 7] [0, 2, 3, 4] [0, 0, 1, 3] >>> a.is_upper_hessenberg() True """ for i in xrange(2, self.rows): for j in xrange(0, i - 1): if self[i,j] != 0: return False return True def is_lower_hessenberg(self): r""" Checks if the matrix is in the lower hessenberg form. The lower hessenberg matrix has zero entries above the first superdiagonal. Example: >>> from sympy.matrices import Matrix >>> a = Matrix([[1,2,0,0],[5,2,3,0],[3,4,3,7],[5,6,1,1]]) >>> a [1, 2, 0, 0] [5, 2, 3, 0] [3, 4, 3, 7] [5, 6, 1, 1] >>> a.is_lower_hessenberg() True """ for i in xrange(0, self.rows): for j in xrange(i + 2, self.cols): if self[i, j] != 0: return False return True def is_symbolic(self): for element in self.mat: if element.has(Symbol): return True return False def is_symmetric(self, simplify=True): """ Check if matrix is symmetric matrix, that is square matrix and is equal to its transpose. By default, simplifications occur before testing symmetry. They can be skipped using 'simplify=False'; while speeding things a bit, this may however induce false negatives. Example: >>> from sympy import Matrix >>> m = Matrix(2,2,[0, 1, 1, 2]) >>> m [0, 1] [1, 2] >>> m.is_symmetric() True >>> m = Matrix(2,2,[0, 1, 2, 0]) >>> m [0, 1] [2, 0] >>> m.is_symmetric() False >>> m = Matrix(2,3,[0, 0, 0, 0, 0, 0]) >>> m [0, 0, 0] [0, 0, 0] >>> m.is_symmetric() False >>> from sympy.abc import x, y >>> m = Matrix(3,3,[1, x**2 + 2*x + 1, y, (x + 1)**2 , 2, 0, y, 0, 3]) >>> m [ 1, x**2 + 2*x + 1, y] [(x + 1)**2, 2, 0] [ y, 0, 3] >>> m.is_symmetric() True If the matrix is already simplified, you may speed-up is_symmetric() test by using 'simplify=False'. >>> m.is_symmetric(simplify=False) False >>> m1 = m.expand() >>> m1.is_symmetric(simplify=False) True """ if not self.is_square: return False if simplify: delta = self - self.transpose() delta.simplify() return delta == self.zeros((self.rows, self.cols)) else: return self == self.transpose() def is_diagonal(self): """ Check if matrix is diagonal, that is matrix in which the entries outside the main diagonal are all zero. Example: >>> from sympy import Matrix, diag >>> m = Matrix(2,2,[1, 0, 0, 2]) >>> m [1, 0] [0, 2] >>> m.is_diagonal() True >>> m = Matrix(2,2,[1, 1, 0, 2]) >>> m [1, 1] [0, 2] >>> m.is_diagonal() False >>> m = diag(1, 2, 3) >>> m [1, 0, 0] [0, 2, 0] [0, 0, 3] >>> m.is_diagonal() True See also: .is_lower(), is_upper() .is_diagonalizable() """ for i in xrange(self.rows): for j in xrange(self.cols): if i != j and self[i, j] != 0: return False return True def clone(self): return Matrix(self.rows, self.cols, lambda i, j: self[i, j]) def det(self, method="bareis"): """ Computes the matrix determinant using the method "method". Possible values for "method": bareis ... det_bareis berkowitz ... berkowitz_det """ if method == "bareis": return self.det_bareis() elif method == "berkowitz": return self.berkowitz_det() else: raise ValueError("Determinant method unrecognized") def det_bareis(self): """Compute matrix determinant using Bareis' fraction-free algorithm which is an extension of the well known Gaussian elimination method. This approach is best suited for dense symbolic matrices and will result in a determinant with minimal number of fractions. It means that less term rewriting is needed on resulting formulae. TODO: Implement algorithm for sparse matrices (SFF). """ if not self.is_square: raise NonSquareMatrixError() M, n = self[:,:], self.rows if n == 1: det = M[0, 0] elif n == 2: det = M[0, 0]*M[1, 1] - M[0, 1]*M[1, 0] else: sign = 1 # track current sign in case of column swap for k in range(n-1): # look for a pivot in the current column # and assume det == 0 if none is found if M[k, k] == 0: for i in range(k+1, n): if M[i, k] != 0: M.row_swap(i, k) sign *= -1 break else: return S.Zero # proceed with Bareis' fraction-free (FF) # form of Gaussian elimination algorithm for i in range(k+1, n): for j in range(k+1, n): D = M[k, k]*M[i, j] - M[i, k]*M[k, j] if k > 0: D /= M[k-1, k-1] if D.is_Atom: M[i, j] = D else: M[i, j] = cancel(D) det = sign * M[n-1, n-1] return det.expand() def adjugate(self, method="berkowitz"): """ Returns the adjugate matrix. Adjugate matrix is the transpose of the cofactor matrix. http://en.wikipedia.org/wiki/Adjugate See also: .cofactorMatrix(), .T """ return self.cofactorMatrix(method).T def inverse_LU(self, iszerofunc=_iszero): """ Calculates the inverse using LU decomposition. """ return self.LUsolve(self.eye(self.rows), iszerofunc=_iszero) def inverse_GE(self, iszerofunc=_iszero): """ Calculates the inverse using Gaussian elimination. """ if not self.is_square: raise NonSquareMatrixError() if self.det() == 0: raise ValueError("A Matrix must have non-zero determinant to invert.") big = self.row_join(self.eye(self.rows)) red = big.rref(iszerofunc=iszerofunc) return red[0][:,big.rows:] def inverse_ADJ(self): """ Calculates the inverse using the adjugate matrix and a determinant. """ if not self.is_square: raise NonSquareMatrixError() d = self.berkowitz_det() if d == 0: raise ValueError("A Matrix must have non-zero determinant to invert.") return self.adjugate()/d def rref(self,simplified=False, iszerofunc=_iszero, simplify=sympy_simplify): """ Take any matrix and return reduced row-echelon form and indices of pivot vars To simplify elements before finding nonzero pivots set simplified=True. To set a custom simplify function, use the simplify keyword argument. """ # TODO: rewrite inverse_GE to use this pivots, r = 0, self[:,:] # pivot: index of next row to contain a pivot pivotlist = [] # indices of pivot variables (non-free) for i in range(r.cols): if pivots == r.rows: break if simplified: r[pivots,i] = simplify(r[pivots,i]) if iszerofunc(r[pivots,i]): for k in range(pivots, r.rows): if simplified and k > pivots: r[k,i] = simplify(r[k,i]) if not iszerofunc(r[k,i]): break if k == r.rows - 1 and iszerofunc(r[k,i]): continue r.row_swap(pivots,k) scale = r[pivots,i] r.row(pivots, lambda x, _: x/scale) for j in range(r.rows): if j == pivots: continue scale = r[j,i] r.row(j, lambda x, k: x - scale*r[pivots,k]) pivotlist.append(i) pivots += 1 return r, pivotlist def nullspace(self,simplified=False): """ Returns list of vectors (Matrix objects) that span nullspace of self """ reduced, pivots = self.rref(simplified) basis = [] # create a set of vectors for the basis for i in range(self.cols - len(pivots)): basis.append(zeros((self.cols, 1))) # contains the variable index to which the vector corresponds basiskey, cur = [-1]*len(basis), 0 for i in range(self.cols): if i not in pivots: basiskey[cur] = i cur += 1 for i in range(self.cols): if i not in pivots: # free var, just set vector's ith place to 1 basis[basiskey.index(i)][i,0] = 1 else: # add negative of nonpivot entry to corr vector for j in range(i+1, self.cols): line = pivots.index(i) if reduced[line, j] != 0: if j in pivots: # XXX: Is this the correct error? raise NotImplementedError("Could not compute the nullspace of `self`.") basis[basiskey.index(j)][i,0] = -1 * reduced[line, j] return basis def berkowitz(self): """The Berkowitz algorithm. Given N x N matrix with symbolic content, compute efficiently coefficients of characteristic polynomials of 'self' and all its square sub-matrices composed by removing both i-th row and column, without division in the ground domain. This method is particularly useful for computing determinant, principal minors and characteristic polynomial, when 'self' has complicated coefficients e.g. polynomials. Semi-direct usage of this algorithm is also important in computing efficiently sub-resultant PRS. Assuming that M is a square matrix of dimension N x N and I is N x N identity matrix, then the following following definition of characteristic polynomial is begin used: charpoly(M) = det(t*I - M) As a consequence, all polynomials generated by Berkowitz algorithm are monic. >>> from sympy import Matrix >>> from sympy.abc import x, y, z >>> M = Matrix([ [x,y,z], [1,0,0], [y,z,x] ]) >>> p, q, r = M.berkowitz() >>> print p # 1 x 1 M's sub-matrix (1, -x) >>> print q # 2 x 2 M's sub-matrix (1, -x, -y) >>> print r # 3 x 3 M's sub-matrix (1, -2*x, x**2 - y*z - y, x*y - z**2) For more information on the implemented algorithm refer to: [1] S.J. Berkowitz, On computing the determinant in small parallel time using a small number of processors, ACM, Information Processing Letters 18, 1984, pp. 147-150 [2] M. Keber, Division-Free computation of sub-resultants using Bezout matrices, Tech. Report MPI-I-2006-1-006, Saarbrucken, 2006 """ if not self.is_square: raise NonSquareMatrixError() A, N = self, self.rows transforms = [0] * (N-1) for n in xrange(N, 1, -1): T, k = zeros((n+1,n)), n - 1 R, C = -A[k,:k], A[:k,k] A, a = A[:k,:k], -A[k,k] items = [ C ] for i in xrange(0, n-2): items.append(A * items[i]) for i, B in enumerate(items): items[i] = (R * B)[0,0] items = [ S.One, a ] + items for i in xrange(n): T[i:,i] = items[:n-i+1] transforms[k-1] = T polys = [ Matrix([S.One, -A[0,0]]) ] for i, T in enumerate(transforms): polys.append(T * polys[i]) return tuple(map(tuple, polys)) def berkowitz_det(self): """Computes determinant using Berkowitz method.""" poly = self.berkowitz()[-1] sign = (-1)**(len(poly)-1) return sign * poly[-1] def berkowitz_minors(self): """Computes principal minors using Berkowitz method.""" sign, minors = S.NegativeOne, [] for poly in self.berkowitz(): minors.append(sign*poly[-1]) sign = -sign return tuple(minors) def berkowitz_charpoly(self, x, simplify=sympy_simplify): """Computes characteristic polynomial minors using Berkowitz method.""" return Poly(map(simplify, self.berkowitz()[-1]), x) charpoly = berkowitz_charpoly def berkowitz_eigenvals(self, **flags): """Computes eigenvalues of a Matrix using Berkowitz method. """ return roots(self.berkowitz_charpoly(Dummy('x')), **flags) eigenvals = berkowitz_eigenvals def eigenvects(self, **flags): """Return list of triples (eigenval, multiplicity, basis).""" if 'multiple' in flags: del flags['multiple'] out, vlist = [], self.eigenvals(**flags) for r, k in vlist.iteritems(): tmp = self - eye(self.rows)*r basis = tmp.nullspace() # whether tmp.is_symbolic() is True or False, it is possible that # the basis will come back as [] in which case simplification is # necessary. if not basis: # The nullspace routine failed, try it again with simplification basis = tmp.nullspace(simplified=True) if not basis: raise NotImplementedError("Can't evaluate eigenvector for eigenvalue %s" % r) out.append((r, k, basis)) return out def singular_values(self): """ Compute the singular values of a Matrix >>> from sympy import Matrix, Symbol, eye >>> x = Symbol('x', real=True) >>> A = Matrix([[0, 1, 0], [0, x, 0], [-1, 0, 0]]) >>> print A.singular_values() [1, (x**2 + 1)**(1/2), 0] """ # Compute eigenvalues of A.H A valmultpairs = (self.H*self).eigenvals() # Expands result from eigenvals into a simple list vals = [] for k,v in valmultpairs.items(): vals += [sqrt(k)]*v # dangerous! same k in several spots! # If sorting makes sense then sort if all(val.is_number for val in vals): vals.sort(reverse=True) # sort them in descending order return vals def condition_number(self): """ Returns the condition number of a matrix. This is the maximum singular value divided by the minimum singular value >>> from sympy import Matrix, S >>> A = Matrix([[1, 0, 0], [0, 10, 0], [0,0,S.One/10]]) >>> print A.condition_number() 100 """ singularvalues = self.singular_values() return Max(*singularvalues) / Min(*singularvalues) def fill(self, value): """Fill the matrix with the scalar value.""" self.mat = [value]*len(self) def __getattr__(self, attr): if attr in ('diff','integrate','limit'): def doit(*args): item_doit = lambda item: getattr(item, attr)(*args) return self.applyfunc( item_doit ) return doit else: raise AttributeError("Matrix has no attribute %s." % attr) def integrate(self, *args): return Matrix(self.rows, self.cols, lambda i, j: self[i, j].integrate(*args)) def limit(self, *args): return Matrix(self.rows, self.cols, lambda i, j: self[i, j].limit(*args)) def diff(self, *args): return Matrix(self.rows, self.cols, lambda i, j: self[i, j].diff(*args)) def vec(self): """ Return the Matrix converted into a one column matrix by stacking columns >>> from sympy import Matrix >>> m=Matrix([ [1,3], [2,4] ]) >>> m [1, 3] [2, 4] >>> m.vec() [1] [2] [3] [4] """ return Matrix(len(self), 1, self.transpose().mat) def vech(self, diagonal=True, check_symmetry=True): """ Return the unique elements of a symmetric Matrix as a one column matrix by stacking the elements in the lower triangle. Arguments: diagonal -- include the diagonal cells of self or not check_symmetry -- checks symmetry of self but not completely reliably >>> from sympy import Matrix >>> m=Matrix([ [1,2], [2,3] ]) >>> m [1, 2] [2, 3] >>> m.vech() [1] [2] [3] >>> m.vech(diagonal=False) [2] """ c = self.cols if c != self.rows: raise ShapeError("Matrix must be square") if check_symmetry: self.simplify() if self != self.transpose(): raise ValueError("Matrix appears to be asymmetric; consider check_symmetry=False") count = 0 if diagonal: v = zeros( (c * (c + 1) // 2, 1) ) for j in xrange(c): for i in xrange(j,c): v[count] = self[i,j] count += 1 else: v = zeros( (c * (c - 1) // 2, 1) ) for j in xrange(c): for i in xrange(j+1,c): v[count] = self[i,j] count += 1 return v def get_diag_blocks(self): """Obtains the square sub-matrices on the main diagonal of a square matrix. Useful for inverting symbolic matrices or solving systems of linear equations which may be decoupled by having a block diagonal structure. Example: >>> from sympy import Matrix, symbols >>> from sympy.abc import x, y, z >>> A = Matrix([[1, 3, 0, 0], [y, z*z, 0, 0], [0, 0, x, 0], [0, 0, 0, 0]]) >>> a1, a2, a3 = A.get_diag_blocks() >>> a1 [1, 3] [y, z**2] >>> a2 [x] >>> a3 [0] >>> """ sub_blocks = [] def recurse_sub_blocks(M): i = 1 while i <= M.shape[0]: if i == 1: to_the_right = M[0, i:] to_the_bottom = M[i:, 0] else: to_the_right = M[0:i, i:] to_the_bottom = M[i:, 0:i] if any(to_the_right) or any(to_the_bottom): i += 1 continue else: sub_blocks.append(M[0:i, 0:i]) if M.shape == M[0:i, 0:i].shape: return else: recurse_sub_blocks(M[i:, i:]) return recurse_sub_blocks(self) return sub_blocks def diagonalize(self, reals_only = False): """ Return diagonalized matrix D and transformation P such as D = P^-1 * M * P where M is current matrix. Example: >>> from sympy import Matrix >>> m = Matrix(3,3,[1, 2, 0, 0, 3, 0, 2, -4, 2]) >>> m [1, 2, 0] [0, 3, 0] [2, -4, 2] >>> (P, D) = m.diagonalize() >>> D [1, 0, 0] [0, 2, 0] [0, 0, 3] >>> P [-1/2, 0, -1/2] [ 0, 0, -1/2] [ 1, 1, 1] >>> P.inv() * m * P [1, 0, 0] [0, 2, 0] [0, 0, 3] See also: .is_diagonalizable(), .is_diagonal() """ if not self.is_square: raise NonSquareMatrixError() if not self.is_diagonalizable(reals_only, False): self._diagonalize_clear_subproducts() raise MatrixError("Matrix is not diagonalizable") else: if self._eigenvects == None: self._eigenvects = self.eigenvects() diagvals = [] P = Matrix(self.rows, 0, []) for eigenval, multiplicity, vects in self._eigenvects: for k in range(multiplicity): diagvals.append(eigenval) vec = vects[k] P = P.col_insert(P.cols, vec) D = diag(*diagvals) self._diagonalize_clear_subproducts() return (P, D) def is_diagonalizable(self, reals_only = False, clear_subproducts=True): """ Check if matrix is diagonalizable. If reals_only==True then check that diagonalized matrix consists of the only not complex values. Some subproducts could be used further in other methods to avoid double calculations, By default (if clear_subproducts==True) they will be deleted. Example: >>> from sympy import Matrix >>> m = Matrix(3,3,[1, 2, 0, 0, 3, 0, 2, -4, 2]) >>> m [1, 2, 0] [0, 3, 0] [2, -4, 2] >>> m.is_diagonalizable() True >>> m = Matrix(2,2,[0, 1, 0, 0]) >>> m [0, 1] [0, 0] >>> m.is_diagonalizable() False >>> m = Matrix(2,2,[0, 1, -1, 0]) >>> m [ 0, 1] [-1, 0] >>> m.is_diagonalizable() True >>> m.is_diagonalizable(True) False """ if not self.is_square: return False res = False self._is_symbolic = self.is_symbolic() self._is_symmetric = self.is_symmetric() self._eigenvects = None #if self._is_symbolic: # self._diagonalize_clear_subproducts() # raise NotImplementedError("Symbolic matrices are not implemented for diagonalization yet") self._eigenvects = self.eigenvects() all_iscorrect = True for eigenval, multiplicity, vects in self._eigenvects: if len(vects) != multiplicity: all_iscorrect = False break elif reals_only and not eigenval.is_real: all_iscorrect = False break res = all_iscorrect if clear_subproducts: self._diagonalize_clear_subproducts() return res def _diagonalize_clear_subproducts(self): del self._is_symbolic del self._is_symmetric del self._eigenvects def jordan_form(self, calc_transformation = True): """ Return Jordan form J of current matrix. If calc_transformation is specified as False, then transformation P such that J = P^-1 * M * P will not be calculated. Note: Calculation of transformation P is not implemented yet Example: >>> from sympy import Matrix >>> m = Matrix(4, 4, [6, 5, -2, -3, -3, -1, 3, 3, 2, 1, -2, -3, -1, 1, 5, 5]) >>> m [ 6, 5, -2, -3] [-3, -1, 3, 3] [ 2, 1, -2, -3] [-1, 1, 5, 5] >>> (P, J) = m.jordan_form() >>> J [2, 1, 0, 0] [0, 2, 0, 0] [0, 0, 2, 1] [0, 0, 0, 2] See also: jordan_cells() """ (P, Jcells) = self.jordan_cells(calc_transformation) J = diag(*Jcells) return (P, J) def jordan_cells(self, calc_transformation = True): """ Return a list of Jordan cells of current matrix. This list shape Jordan matrix J. If calc_transformation is specified as False, then transformation P such that J = P^-1 * M * P will not be calculated. Note: Calculation of transformation P is not implemented yet Example: >>> from sympy import Matrix >>> m = Matrix(4, 4, [6, 5, -2, -3, -3, -1, 3, 3, 2, 1, -2, -3, -1, 1, 5, 5]) >>> m [ 6, 5, -2, -3] [-3, -1, 3, 3] [ 2, 1, -2, -3] [-1, 1, 5, 5] >>> (P, Jcells) = m.jordan_cells() >>> Jcells[0] [2, 1] [0, 2] >>> Jcells[1] [2, 1] [0, 2] See also: jordan_form() """ if not self.is_square: raise NonSquareMatrixError() _eigenvects = self.eigenvects() Jcells = [] for eigenval, multiplicity, vects in _eigenvects: geometrical = len(vects) if geometrical == multiplicity: Jcell = diag( *([eigenval] * multiplicity)) Jcells.append(Jcell) elif geometrical==0: raise MatrixError("Matrix has the eigen vector with geometrical multiplicity equal zero.") else: sizes = self._jordan_split(multiplicity, geometrical) cells = [] for size in sizes: cell = jordan_cell(eigenval, size) cells.append(cell) Jcells += cells return (None, Jcells) def _jordan_split(self, algebraical, geometrical): "return a list which sum is equal to 'algebraical' and length is equal to 'geometrical'" n1 = algebraical // geometrical res = [n1] * geometrical res[len(res)-1] += algebraical % geometrical assert sum(res) == algebraical return res def has(self, *patterns): """ Test whether any subexpression matches any of the patterns. Examples: >>> from sympy import Matrix, Float >>> from sympy.abc import x, y >>> A = Matrix(((1, x), (0.2, 3))) >>> A.has(x) True >>> A.has(y) False >>> A.has(Float) True """ return any(a.has(*patterns) for a in self.mat) def matrix_multiply(A, B): """ Matrix product A*B. A and B must be of appropriate dimensions. If A is an m x k matrix, and B is a k x n matrix, the product will be an m x n matrix. Example: >>> from sympy import Matrix >>> A = Matrix([[1, 2, 3], [4, 5, 6]]) >>> B = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> A*B [30, 36, 42] [66, 81, 96] >>> B*A Traceback (most recent call last): ... ShapeError >>> """ # The following implmentation is equivalent, but about 5% slower #ma, na = A.shape #mb, nb = B.shape # #if na != mb: # raise ShapeError() #product = Matrix(ma, nb, lambda i,j: 0) #for i in xrange(ma): # for j in xrange(nb): # s = 0 # for k in range(na): # s += A[i, k]*B[k, j] # product[i, j] = s #return product if A.shape[1] != B.shape[0]: raise ShapeError() blst = B.T.tolist() alst = A.tolist() return Matrix(A.shape[0], B.shape[1], lambda i, j: reduce(lambda k, l: k+l, map(lambda n, m: n*m, alst[i], blst[j]))) def matrix_multiply_elementwise(A, B): """Return the Hadamard product (elementwise product) of A and B >>> import sympy >>> A = sympy.Matrix([[0, 1, 2], [3, 4, 5]]) >>> B = sympy.Matrix([[1, 10, 100], [100, 10, 1]]) >>> print sympy.matrices.matrix_multiply_elementwise(A, B) [ 0, 10, 200] [300, 40, 5] """ if A.shape != B.shape: raise ShapeError() shape = A.shape return Matrix(shape[0], shape[1], lambda i, j: A[i,j] * B[i, j]) def matrix_add(A,B): """Return A+B""" if A.shape != B.shape: raise ShapeError() alst = A.tolist() blst = B.tolist() ret = [0]*A.shape[0] for i in xrange(A.shape[0]): ret[i] = map(lambda j,k: j+k, alst[i], blst[i]) return Matrix(ret) def zeros(dims): """Create zero matrix of dimensions dims = (d1,d2)""" n, m = _dims_to_nm(dims) return Matrix(n, m, [S.Zero]*m*n) def ones(dims): """Create all-one matrix of dimensions dims = (d1,d2)""" n, m = _dims_to_nm( dims ) return Matrix(n, m, [S.One]*m*n) def eye(n): """Create square identity matrix n x n See also: diag() """ n = int(n) out = zeros(n) for i in range(n): out[i, i] = S.One return out def diag(*values): """Create diagonal matrix from a list as a diagonal values. Arguments might be matrices too, in case of it they are fitted in result matrix Example: >>> from sympy.matrices import diag, Matrix >>> diag(1, 2, 3) [1, 0, 0] [0, 2, 0] [0, 0, 3] >>> from sympy.abc import x, y, z >>> a = Matrix([x, y, z]) >>> b = Matrix([[1, 2], [3, 4]]) >>> c = Matrix([[5, 6]]) >>> diag(a, 7, b, c) [x, 0, 0, 0, 0, 0] [y, 0, 0, 0, 0, 0] [z, 0, 0, 0, 0, 0] [0, 7, 0, 0, 0, 0] [0, 0, 1, 2, 0, 0] [0, 0, 3, 4, 0, 0] [0, 0, 0, 0, 5, 6] See also: eye() """ rows = 0 cols = 0 for m in values: if isinstance(m, Matrix): rows += m.rows cols += m.cols else: rows += 1 cols += 1 res = zeros((rows, cols)) i_row = 0 i_col = 0 for m in values: if isinstance(m, Matrix): res[i_row:i_row + m.rows, i_col:i_col + m.cols] = m i_row += m.rows i_col += m.cols else: res[i_row, i_col] = m i_row += 1 i_col += 1 return res def block_diag(matrices): """ Warning: this function is deprecated. See .diag() """ import warnings warnings.warn("block_diag() is deprecated, use diag() instead", DeprecationWarning) return diag(*matrices) def jordan_cell(eigenval, n): """ Create matrix of Jordan cell kind: Example: >>> from sympy.matrices.matrices import jordan_cell >>> from sympy.abc import x >>> jordan_cell(x, 4) [x, 1, 0, 0] [0, x, 1, 0] [0, 0, x, 1] [0, 0, 0, x] """ n = int(n) out = zeros(n) for i in range(n-1): out[i, i] = eigenval out[i, i+1] = S.One out[n-1, n-1] = eigenval return out def randMatrix(r,c,min=0,max=99,seed=[]): """Create random matrix r x c""" if seed == []: prng = random.Random() # use system time else: prng = random.Random(seed) return Matrix(r,c,lambda i,j: prng.randint(min,max)) def hessian(f, varlist): """Compute Hessian matrix for a function f see: http://en.wikipedia.org/wiki/Hessian_matrix """ # f is the expression representing a function f, return regular matrix if is_sequence(varlist): m = len(varlist) if not m: raise ShapeError("`len(varlist)` must not be zero.") elif isinstance(varlist, Matrix): m = varlist.cols if not m: raise ShapeError("`varlist.cols` must not be zero.") if varlist.rows != 1: raise ShapeError("`varlist` must be a row vector.") else: raise ValueError("Improper variable list in hessian function") if not getattr(f, 'diff'): # check differentiability raise ValueError("Function `f` (%s) is not differentiable" % f) out = zeros(m) for i in range(m): for j in range(i,m): out[i,j] = f.diff(varlist[i]).diff(varlist[j]) for i in range(m): for j in range(i): out[i,j] = out[j,i] return out def GramSchmidt(vlist, orthog=False): out = [] m = len(vlist) for i in range(m): tmp = vlist[i] for j in range(i): tmp -= vlist[i].project(out[j]) if tmp == Matrix([[0,0,0]]): raise ValueError("GramSchmidt: vector set not linearly independent") out.append(tmp) if orthog: for i in range(len(out)): out[i] = out[i].normalized() return out def wronskian(functions, var, method='bareis'): """Compute Wronskian for [] of functions | f1 f2 ... fn | | f1' f2' ... fn' | | . . . . | W(f1,...,fn) = | . . . . | | . . . . | | (n) (n) (n) | | D (f1) D (f2) ... D (fn) | see: http://en.wikipedia.org/wiki/Wronskian """ for index in xrange(0, len(functions)): functions[index] = sympify(functions[index]) n = len(functions) if n == 0: return 1 W = Matrix(n, n, lambda i,j: functions[i].diff(var, j) ) return W.det(method) def casoratian(seqs, n, zero=True): """Given linear difference operator L of order 'k' and homogeneous equation Ly = 0 we want to compute kernel of L, which is a set of 'k' sequences: a(n), b(n), ... z(n). Solutions of L are linearly independent iff their Casoratian, denoted as C(a, b, ..., z), do not vanish for n = 0. Casoratian is defined by k x k determinant: + a(n) b(n) . . . z(n) + | a(n+1) b(n+1) . . . z(n+1) | | . . . . | | . . . . | | . . . . | + a(n+k-1) b(n+k-1) . . . z(n+k-1) + It proves very useful in rsolve_hyper() where it is applied to a generating set of a recurrence to factor out linearly dependent solutions and return a basis. >>> from sympy import Symbol, casoratian, factorial >>> n = Symbol('n', integer=True) Exponential and factorial are linearly independent: >>> casoratian([2**n, factorial(n)], n) != 0 True """ seqs = map(sympify, seqs) if not zero: f = lambda i, j: seqs[j].subs(n, n+i) else: f = lambda i, j: seqs[j].subs(n, i) k = len(seqs) return Matrix(k, k, f).det() # Add sympify converters def _matrix_sympify(matrix): raise SympifyError('Matrix cannot be sympified') converter[Matrix] = _matrix_sympify del _matrix_sympify class SparseMatrix(Matrix): """Sparse matrix""" def __init__(self, *args): if len(args) == 3 and callable(args[2]): op = args[2] if not isinstance(args[0], (int, Integer)) or not isinstance(args[1], (int, Integer)): raise TypeError("`args[0]` and `args[1]` must both be integers.") self.rows = args[0] self.cols = args[1] self.mat = {} for i in range(self.rows): for j in range(self.cols): value = sympify(op(i,j)) if value != 0: self.mat[(i,j)] = value elif len(args)==3 and isinstance(args[0],int) and \ isinstance(args[1],int) and is_sequence(args[2]): self.rows = args[0] self.cols = args[1] mat = args[2] self.mat = {} for i in range(self.rows): for j in range(self.cols): value = sympify(mat[i*self.cols+j]) if value != 0: self.mat[(i,j)] = value elif len(args)==3 and isinstance(args[0],int) and \ isinstance(args[1],int) and isinstance(args[2], dict): self.rows = args[0] self.cols = args[1] self.mat = {} # manual copy, copy.deepcopy() doesn't work for key in args[2].keys(): self.mat[key] = args[2][key] else: if len(args) == 1: mat = args[0] else: mat = args if not is_sequence(mat[0]): mat = [ [element] for element in mat ] self.rows = len(mat) self.cols = len(mat[0]) self.mat = {} for i in range(self.rows): if len(mat[i]) != self.cols: raise ValueError("All arguments must have the same length.") for j in range(self.cols): value = sympify(mat[i][j]) if value != 0: self.mat[(i,j)] = value def __getitem__(self, key): if isinstance(key, slice) or isinstance(key, int): lo, hi = self.slice2bounds(key, len(self)) L = [] for i in range(lo, hi): m,n = self.rowdecomp(i) if (m,n) in self.mat: L.append(self.mat[(m,n)]) else: L.append(0) if len(L) == 1: return L[0] else: return L if len(key) != 2: raise ValueError("`key` must be of length 2.") if isinstance(key[0], int) and isinstance(key[1], int): i,j=self.key2ij(key) if (i, j) in self.mat: return self.mat[(i,j)] else: return 0 elif isinstance(key[0], slice) or isinstance(key[1], slice): return self.submatrix(key) else: raise IndexError("Index out of range: a[%s]"%repr(key)) def rowdecomp(self, num): nmax = len(self) if not (0 <= num < nmax) or not (0 <= -num < nmax): raise ValueError("`num` must satisfy 0 <= `num` < `self.rows*" + "*self.cols` (%d) and 0 <= -num < " % nmax + "`self.rows*self.cols` (%d) to apply redecomp()." % nmax) i, j = 0, num while j >= self.cols: j -= self.cols i += 1 return i,j def __setitem__(self, key, value): # almost identical, need to test for 0 if len(key) != 2: raise ValueError("`key` must be of length 2.") if isinstance(key[0], slice) or isinstance(key[1], slice): if isinstance(value, Matrix): self.copyin_matrix(key, value) if is_sequence(value): self.copyin_list(key, value) else: i,j=self.key2ij(key) testval = sympify(value) if testval != 0: self.mat[(i,j)] = testval elif (i,j) in self.mat: del self.mat[(i,j)] def row_del(self, k): newD = {} for (i,j) in self.mat.keys(): if i==k: pass elif i > k: newD[i-1,j] = self.mat[i,j] else: newD[i,j] = self.mat[i,j] self.mat = newD self.rows -= 1 def col_del(self, k): newD = {} for (i,j) in self.mat.keys(): if j==k: pass elif j > k: newD[i,j-1] = self.mat[i,j] else: newD[i,j] = self.mat[i,j] self.mat = newD self.cols -= 1 def toMatrix(self): l = [] for i in range(self.rows): c = [] l.append(c) for j in range(self.cols): if (i, j) in self.mat: c.append(self[i, j]) else: c.append(0) return Matrix(l) def row_list(self): """ Returns a Row-sorted list of non-zero elements of the matrix. >>> from sympy.matrices import SparseMatrix >>> a=SparseMatrix((1,2),(3,4)) >>> a [1, 2] [3, 4] >>> a.RL [(0, 0, 1), (0, 1, 2), (1, 0, 3), (1, 1, 4)] """ new=[] for i in range(self.rows): for j in range(self.cols): value = self[(i,j)] if value!=0: new.append((i,j,value)) return new RL = property(row_list,None,None,"Alternate faster representation") def col_list(self): """ Returns a Column-sorted list of non-zero elements of the matrix. >>> from sympy.matrices import SparseMatrix >>> a=SparseMatrix((1,2),(3,4)) >>> a [1, 2] [3, 4] >>> a.CL [(0, 0, 1), (1, 0, 3), (0, 1, 2), (1, 1, 4)] """ new=[] for j in range(self.cols): for i in range(self.rows): value = self[(i,j)] if value!=0: new.append((i,j,value)) return new CL = property(col_list,None,None,"Alternate faster representation") def transpose(self): """ Returns the transposed SparseMatrix of this SparseMatrix >>> from sympy.matrices import SparseMatrix >>> a = SparseMatrix((1,2),(3,4)) >>> a [1, 2] [3, 4] >>> a.T [1, 3] [2, 4] """ tran = SparseMatrix(self.cols,self.rows,{}) for key,value in self.mat.iteritems(): tran.mat[key[1],key[0]]=value return tran T = property(transpose,None,None,"Matrix transposition.") def __add__(self, other): if isinstance(other, SparseMatrix): return self.add(other) else: raise NotImplementedError("Only SparseMatrix + SparseMatrix supported") def __radd__(self, other): if isinstance(other, SparseMatrix): return self.add(other) else: raise NotImplementedError("Only SparseMatrix + SparseMatrix supported") def add(self, other): """ Add two sparse matrices with dictionary representation. >>> from sympy.matrices.matrices import SparseMatrix >>> A = SparseMatrix(5, 5, lambda i, j : i * j + i) >>> A [0, 0, 0, 0, 0] [1, 2, 3, 4, 5] [2, 4, 6, 8, 10] [3, 6, 9, 12, 15] [4, 8, 12, 16, 20] >>> B = SparseMatrix(5, 5, lambda i, j : i + 2 * j) >>> B [0, 2, 4, 6, 8] [1, 3, 5, 7, 9] [2, 4, 6, 8, 10] [3, 5, 7, 9, 11] [4, 6, 8, 10, 12] >>> A + B [0, 2, 4, 6, 8] [2, 5, 8, 11, 14] [4, 8, 12, 16, 20] [6, 11, 16, 21, 26] [8, 14, 20, 26, 32] """ if self.shape != other.shape: raise ShapeError() a, b = self.mat.keys(), other.mat.keys() a.sort() b.sort() i = j = 0 c = {} while i < len(a) or j < len(b): if j >= len(b) or (i < len(a) and a[i] < b[j]): c[a[i]] = self.mat[a[i]] i = i + 1 continue elif i >= len(a) or (j < len(b) and a[i] > b[j]): c[b[j]] = other.mat[b[j]] j = j + 1 continue else: c[a[i]] = self.mat[a[i]] + other.mat[b[j]] i = i + 1 j = j + 1 return SparseMatrix(self.rows, self.cols, c) # from here to end all functions are same as in matrices.py # with Matrix replaced with SparseMatrix def copyin_list(self, key, value): if not is_sequence(value): raise TypeError("`value` must be of type list or tuple.") self.copyin_matrix(key, SparseMatrix(value)) def multiply(self,b): """Returns self*b """ def dotprod(a,b,i,j): if a.cols != b.rows: raise ShapeError("`self.cols` must equal `b.rows`.") r=0 for x in range(a.cols): r+=a[i,x]*b[x,j] return r r = SparseMatrix(self.rows, b.cols, lambda i,j: dotprod(self,b,i,j)) if r.rows == 1 and r.cols ==1: return r[0,0] return r def submatrix(self, keys): if not isinstance(keys[0], slice) and not isinstance(keys[1], slice): raise TypeError("Both elements of `keys` must be slice objects.") rlo, rhi = self.slice2bounds(keys[0], self.rows) clo, chi = self.slice2bounds(keys[1], self.cols) if not ( 0<=rlo<=rhi and 0<=clo<=chi ): raise IndexError("Slice indices out of range: a[%s]"%repr(keys)) return SparseMatrix(rhi-rlo, chi-clo, lambda i,j: self[i+rlo, j+clo]) def reshape(self, _rows, _cols): if len(self) != _rows*_cols: print "Invalid reshape parameters %d %d" % (_rows, _cols) newD = {} for i in range(_rows): for j in range(_cols): m,n = self.rowdecomp(i*_cols + j) if (m,n) in self.mat: newD[(i,j)] = self.mat[(m,n)] return SparseMatrix(_rows, _cols, newD) def cross(self, b): if not is_sequence(b, include=Matrix): raise TypeError("`b` must be an ordered iterable or Matrix, not %s." % type(b)) if not (self.rows == 1 and self.cols == 3 or \ self.rows == 3 and self.cols == 1 ) and \ (b.rows == 1 and b.cols == 3 or \ b.rows == 3 and b.cols == 1): raise ShapeError("Dimensions incorrect for cross product") else: return SparseMatrix(1,3,((self[1]*b[2] - self[2]*b[1]), (self[2]*b[0] - self[0]*b[2]), (self[0]*b[1] - self[1]*b[0]))) def zeros(self, dims): """Returns a dims = (d1,d2) matrix of zeros.""" n, m = _dims_to_nm( dims ) return SparseMatrix(n,m,{}) def eye(self, n): tmp = SparseMatrix(n,n,lambda i,j:0) for i in range(tmp.rows): tmp[i,i] = 1 return tmp def list2numpy(l): """Converts python list of SymPy expressions to a NumPy array.""" from numpy import empty a = empty(len(l), dtype=object) for i, s in enumerate(l): a[i] = s return a def matrix2numpy(m): """Converts SymPy's matrix to a NumPy array.""" from numpy import empty a = empty(m.shape, dtype=object) for i in range(m.rows): for j in range(m.cols): a[i, j] = m[i, j] return a def a2idx(a): """ Tries to convert "a" to an index, returns None on failure. The result of a2idx() (if not None) can be safely used as an index to arrays/matrices. """ if hasattr(a, "__int__"): return int(a) if hasattr(a, "__index__"): return a.__index__() def symarray(prefix, shape): """Create a numpy ndarray of symbols (as an object array). The created symbols are named prefix_i1_i2_... You should thus provide a non-empty prefix if you want your symbols to be unique for different output arrays, as Sympy symbols with identical names are the same object. Parameters ---------- prefix : string A prefix prepended to the name of every symbol. shape : int or tuple Shape of the created array. If an int, the array is one-dimensional; for more than one dimension the shape must be a tuple. Examples -------- >> from sympy import symarray >> symarray('', 3) [_0 _1 _2] If you want multiple symarrays to contain distinct symbols, you *must* provide unique prefixes: >> a = symarray('', 3) >> b = symarray('', 3) >> a[0] is b[0] True >> a = symarray('a', 3) >> b = symarray('b', 3) >> a[0] is b[0] False Creating symarrays with a prefix: >> symarray('a', 3) [a_0 a_1 a_2] For more than one dimension, the shape must be given as a tuple: >> symarray('a', (2,3)) [[a_0_0 a_0_1 a_0_2] [a_1_0 a_1_1 a_1_2]] >> symarray('a', (2,3,2)) [[[a_0_0_0 a_0_0_1] [a_0_1_0 a_0_1_1] [a_0_2_0 a_0_2_1]] [[a_1_0_0 a_1_0_1] [a_1_1_0 a_1_1_1] [a_1_2_0 a_1_2_1]]] """ try: import numpy as np except ImportError: raise ImportError("symarray requires numpy to be installed") arr = np.empty(shape, dtype=object) for index in np.ndindex(shape): arr[index] = Symbol('%s_%s' % (prefix, '_'.join(map(str, index)))) return arr def _separate_eig_results(res): eigvals = [item[0] for item in res] multiplicities = [item[1] for item in res] eigvals = flatten([[val]*mult for val, mult in zip(eigVals, multiplicities)]) eigvects = flatten([item[2] for item in res]) return eigvals, eigvects wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/0000755000175000017500000000000012014170666022567 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/partitions_.py0000644000175000017500000000544312014170666025502 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import (fzero, from_man_exp, from_int, from_rational, fone, fhalf, bitcount, to_int, to_str, mpf_mul, mpf_div, mpf_sub, mpf_add, mpf_sqrt, mpf_pi, mpf_cosh_sinh, pi_fixed, mpf_cos) from sympy.core.numbers import igcd import math def A(n, j, prec): """Compute the inner sum in the HRR formula.""" if j == 1: return fone s = fzero pi = pi_fixed(prec) for h in xrange(1, j): if igcd(h,j) != 1: continue # & with mask to compute fractional part of fixed-point number one = 1 << prec onemask = one - 1 half = one >> 1 g = 0 if j >= 3: for k in xrange(1, j): t = h*k*one//j if t > 0: frac = t & onemask else: frac = -((-t) & onemask) g += k*(frac - half) g = ((g - 2*h*n*one)*pi//j) >> prec s = mpf_add(s, mpf_cos(from_man_exp(g, -prec), prec), prec) return s def D(n, j, prec, sq23pi, sqrt8): """ Compute the sinh term in the outer sum of the HRR formula. The constants sqrt(2/3*pi) and sqrt(8) must be precomputed. """ j = from_int(j) pi = mpf_pi(prec) a = mpf_div(sq23pi, j, prec) b = mpf_sub(from_int(n), from_rational(1,24,prec), prec) c = mpf_sqrt(b, prec) ch, sh = mpf_cosh_sinh(mpf_mul(a,c), prec) D = mpf_div(mpf_sqrt(j,prec), mpf_mul(mpf_mul(sqrt8,b),pi), prec) E = mpf_sub(mpf_mul(a,ch), mpf_div(sh,c,prec), prec) return mpf_mul(D, E) def npartitions(n, verbose=False): """ Calculate the partition function P(n), i.e. the number of ways that n can be written as a sum of positive integers. P(n) is computed using the Hardy-Ramanujan-Rademacher formula, described e.g. at http://mathworld.wolfram.com/PartitionFunctionP.html The correctness of this implementation has been tested for 10**n up to n = 8. """ n = int(n) if n < 0: return 0 if n <= 5: return [1, 1, 2, 3, 5, 7][n] # Estimate number of bits in p(n). This formula could be tidied pbits = int((math.pi*(2*n/3.)**0.5-math.log(4*n))/math.log(10)+1)*\ math.log(10,2) prec = p = int(pbits*1.1 + 100) s = fzero M = max(6, int(0.24*n**0.5+4)) sq23pi = mpf_mul(mpf_sqrt(from_rational(2,3,p), p), mpf_pi(p), p) sqrt8 = mpf_sqrt(from_int(8), p) for q in xrange(1, M): a = A(n,q,p) d = D(n,q,p, sq23pi, sqrt8) s = mpf_add(s, mpf_mul(a, d), prec) if verbose: print "step", q, "of", M, to_str(a, 10), to_str(d, 10) # On average, the terms decrease rapidly in magnitude. Dynamically # reducing the precision greatly improves performance. p = bitcount(abs(to_int(d))) + 50 np = to_int(mpf_add(s, fhalf, prec)) return int(np) __all__ = ['npartitions'] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/bbp_pi.py0000644000175000017500000000641512014170666024402 0ustar georgeskgeorgesk''' This implementation is a heavily modified fixed point implementation of BBP_formula for calculating the nth position of pi. The original hosted at: http://en.literateprograms.org/Pi_with_the_BBP_formula_(Python) # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sub-license, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Modifications: 1.Once the nth digit is selected the number of digits of working precision is calculated to ensure that the 14 Hexadecimal representation of that region is accurate. This was found empirically to be int((math.log10(n//1000))+18). This was found by searching for a value of working precision for the n = 0 and n = 1 then n was increased until the result was less precise, therefore increased again this was repeated for increasing n and an effective fit was found between n and the working precision value. 2. The while loop to evaluate whether the series has converged has be replaced with a fixed for loop, that option was selected because in a very large number of cases the loop converged to a point where no difference can be detected in less than 15 iterations. (done for more accurate memory and time banking). 3. output hex string constrained to 14 characters (accuracy assured to n = 10**7) 4. pi_hex_digits(n) changed to have coefficient to the formula in an array (perhaps just a matter of preference). ''' import math def Series(j, n): # Left sum from the bbp algorithm s = 0 D = dn(n) for k in range(0, n+1): r = 8*k+j s = (s + (pow(16,n-k,r)<<4*(D))//r) # Right sum. should iterate to infinty, but now just iterates to the point where # one iterations change is beyond the resolution of the data type used t = 0 for k in range(n+1, n+15): xp = int(16**(n-k) * (16**(D)) ) t = t + xp // (8*k+j) total = s+t return total def pi_hex_digits(n): # main of implementation arrays holding formulae coefficients n -= 1 a= [4,2,1,1] j = [1,4,5,6] #formulae x = + (a[0]*Series(j[0], n) - a[1]*Series(j[1], n) - a[2]*Series(j[2], n) - a[3]*Series(j[3], n)) & (16**(dn(n)) -1) s=("%014x" % x) #s is constrained between 0 and 14 return s[0:14] def dn(n): # controller for n dependence on precision if (n < 1000): f=16 else: f = int((math.log10(n//1000))+18) return f wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/factor_.py0000644000175000017500000012142712014170666024565 0ustar georgeskgeorgesk""" Integer factorization """ from sympy.core import Mul from sympy.core.evalf import bitcount from sympy.core.numbers import igcd from sympy.core.power import integer_nthroot, Pow from sympy.core.mul import Mul import random import math from primetest import isprime from generate import sieve, primerange, nextprime from sympy.core.singleton import S from operator import abs small_trailing = [i and max(int(not i % 2**j) and j for j in range(1,8)) \ for i in range(256)] def smoothness(n): """Return the B-smooth and B-power smooth values of n. The smoothness of n is the largest prime factor of n; the power- smoothness is the largest divisor raised to its multiplicity. >>> from sympy.ntheory.factor_ import smoothness >>> smoothness(2**7*3**2) (3, 128) >>> smoothness(2**4*13) (13, 16) >>> smoothness(2) (2, 2) """ if n == 1: return (1, 1) # not prime, but otherwise this causes headaches facs = factorint(n) return max(facs), max([m**facs[m] for m in facs]) def smoothness_p(n, m=-1, power=0, visual=None): """Return a list of [m, (p, (M, sm(p + m), psm(p + m)))...] where: o p**M is the base-p divisor of n o sm(p + m) is the smoothness of p + m (m = -1 by default) o psm(p + n) is the power smoothness of p + m The list is sorted according to smoothness (default) or by power smoothness if power=1. The smoothness of the numbers to the left (m = -1) or right (m = 1) of a factor govern the results that are obtained from the p +/- 1 type factoring methods. >>> from sympy.ntheory.factor_ import smoothness_p, factorint >>> smoothness_p(10431, m=1) (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) >>> smoothness_p(10431) (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) >>> smoothness_p(10431, power=1) (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) If visual=True then an annotated string will be returned: >>> print smoothness_p(21477639576571, visual=1) p**i=4410317**1 has p-1 B=1787, B-pow=1787 p**i=4869863**1 has p-1 B=2434931, B-pow=2434931 This string can also be generated directly from a factorization dictionary and vice versa: >>> factorint(17*9) {3: 2, 17: 1} >>> smoothness_p(_) 'p**i=3**2 has p-1 B=2, B-pow=2\\np**i=17**1 has p-1 B=2, B-pow=16' >>> smoothness_p(_) {3: 2, 17: 1} The table of the output logic is: _________________________________ | | visual= | | input + -----+--------+-------+ | | True | False | other | +-------+------+--------+-------+ | dict | str | tuple | str | | str | str | tuple | dict | | tuple | str | tuple | str | | n | str | tuple | tuple | | mul | str | tuple | tuple | +-------+------+--------+-------+ Note: recalculation of the input is done only for a Mul or dict, so smoothness_p({4: 2}, visual=False) == smoothness_p(16). """ from sympy.utilities import flatten if type(n) is str: if visual: return n d = {} for li in n.splitlines(): k, v = [int(i) for i in li.split('has')[0] .split('=')[1] .split('**')] d[k] = v if visual is not True and visual is not False: return d return smoothness_p(d, visual=False) elif type(n) is not tuple: facs = factorint(n, visual=False) if power: k = -1 else: k = 1 if type(n) is not tuple: rv = (m, sorted([(f, tuple([M] + list(smoothness(f + m)))) for f, M in [i for i in facs.items()]], key=lambda x: (x[1][k], x[0]))) else: rv = n if visual is False or (visual != True) and (type(n) in [int, Mul]): return rv lines = [] for dat in rv[1]: dat = flatten(dat) dat.insert(2, m) lines.append('p**i=%i**%i has p%+i B=%i, B-pow=%i' % tuple(dat)) return '\n'.join(lines) def trailing(n): """Count the number of trailing zero digits in the binary representation of n, i.e. determine the largest power of 2 that divides n.""" n = int(n) if not n: return 0 low_byte = n & 0xff if low_byte: return small_trailing[low_byte] # 2**m is quick for z up through 2**30 z = bitcount(n) - 1 if type(z) is int: if n == 1 << z: return z t = 0 p = 8 while not n & 1: while not n & ((1<>= p t += p p *= 2 p //= 2 return t def multiplicity(p, n): """ Find the greatest integer m such that p**m divides n. Example usage ============= >>> from sympy.ntheory import multiplicity >>> [multiplicity(5, n) for n in [8, 5, 25, 125, 250]] [0, 1, 2, 3, 3] """ p, n = int(p), int(n) if p == n: return 1 if p == 2: return trailing(n) m = 0 n, rem = divmod(n, p) while not rem: m += 1 if m > 5: # The multiplicity could be very large. Better # to increment in powers of two e = 2 while 1: ppow = p**e if ppow < n: nnew, rem = divmod(n, ppow) if not rem: m += e e *= 2 n = nnew continue return m + multiplicity(p, n) n, rem = divmod(n, p) return m def perfect_power(n, candidates=None, big=True, factor=True): """ Return ``(b, e)`` such that ``n`` == ``b**e`` if ``n`` is a perfect power; otherwise return ``False``. By default, the base is recursively decomposed and the exponents collected so the largest possible ``e`` is sought. If ``big=False`` then the smallest possible ``e`` (thus prime) will be chosen. If ``candidates`` for exponents are given, they are assumed to be sorted and the first one that is larger than the computed maximum will signal failure for the routine. If ``factor=True`` then simultaneous factorization of n is attempted since finding a factor indicates the only possible root for n. This is True by default since only a few small factors will be tested in the course of searching for the perfect power. """ n = int(n) if n < 3: return False logn = math.log(n, 2) max_possible = int(logn) + 2 # only check values less than this not_square = n % 10 in [2, 3, 7, 8] # squares cannot end in 2, 3, 7, 8 if not candidates: candidates = primerange(2 + not_square, max_possible) afactor = 2 + n % 2 for e in candidates: if e < 3: if e == 1 or e == 2 and not_square: continue if e > max_possible: return False # see if there is a factor present if factor: if n % afactor == 0: # find what the potential power is if afactor == 2: e = trailing(n) else: e = multiplicity(afactor, n) # if it's a trivial power we are done if e == 1: return False # maybe the bth root of n is exact r, exact = integer_nthroot(n, e) if not exact: # then remove this factor and check to see if # any of e's factors are a common exponent; if # not then it's not a perfect power n //= afactor**e m = perfect_power(n, candidates=primefactors(e), big=big) if m is False: return False else: r, m = m # adjust the two exponents so the bases can # be combined g = igcd(m, e) if g == 1: return False m //= g e //= g r, e = r**m*afactor**e, g if not big: e0 = primefactors(e) if len(e0) > 1 or e0[0] != e: e0 = e0[0] r, e = r**(e//e0), e0 return r, e else: # get the next factor ready for the next pass through the loop afactor = nextprime(afactor) # Weed out downright impossible candidates if logn/e < 40: b = 2.0**(logn/e) if abs(int(b + 0.5) - b) > 0.01: continue # now see if the plausible e makes a perfect power r, exact = integer_nthroot(n, e) if exact: if big: m = perfect_power(r, big=big, factor=factor) if m is not False: r, e = m[0], e*m[1] return int(r), e else: return False def pollard_rho(n, s=2, a=1, retries=5, seed=1234, max_steps=None, F=None): """Use Pollard's rho method to try to extract a nontrivial factor of ``n``. The returned factor may be a composite number. If no factor is found, ``None`` is returned. The algorithm generates pseudo-random values of x with a generator function, replacing x with F(x). If F is not supplied then the function x**2 + ``a`` is used. The first value supplied to F(x) is ``s``. Upon failure (if ``retries`` is > 0) a new ``a`` and ``s`` will be supplied; the ``a`` will be ignored if F was supplied. The sequence of numbers generated by such functions generally have a a lead-up to some number and then loop around back to that number and begin to repeat the sequence, e.g. 1, 2, 3, 4, 5, 3, 4, 5 -- this leader and loop look a bit like the Greek letter rho, and thus the name, 'rho'. For a given function, very different leader-loop values can be obtained so it is a good idea to allow for retries: >>> from sympy.ntheory.generate import cycle_length >>> n=16843009 >>> F=lambda x:(2048*pow(x,2,n) + 32767) % n >>> for s in range(5): ... cycle_length(F, s).next() ... (2489, 42) (78, 120) (1482, 99) (1482, 285) (1482, 100) \ \___loop \______________leader Here is an explicit example: >>> x=2 >>> for i in range(7): ... x=(x**2+12)%17 ... print x, ... 16 13 11 14 4 11 14 >>> cycle_length(lambda x: (x**2+12)%17, 2).next() (3, 2) >>> list(cycle_length(lambda x: (x**2+12)%17, 2, values=1)) [16, 13, 11, 14, 4] Instead of checking the differences of all generated values for a gcd with n, only the kth and 2*kth numbers are checked, e.g. 1st and 2nd, 2nd and 4th, 3rd and 6th until it has been detected that the loop has been traversed. Loops may be many thousands of steps long before rho finds a factor or reports failure. If ``max_steps`` is specified, the iteration is cancelled with a failure after the specified number of steps. Examples ======== >>> from sympy import pollard_rho >>> n=16843009 >>> F=lambda x:(2048*pow(x,2,n) + 32767) % n >>> pollard_rho(n, F=F) 257 Use the default setting with a bad value of ``a`` and no retries: >>> pollard_rho(n, a=n-2, retries=0) If retries is > 0 then perhaps the problem will correct itself when new values are generated for a: >>> pollard_rho(n, a=n-2, retries=1) 257 References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 229-231 - http://www.csh.rit.edu/~pat/math/quickies/rho/ """ n = int(n) if n < 5: raise ValueError('pollard_rho should receive n > 4') prng = random.Random(seed + retries) V = s for i in range(retries + 1): U = V if not F: F = lambda x: (pow(x, 2, n) + a) % n j = 0 while 1: if max_steps and (j > max_steps): break j += 1 U = F(U) V = F(F(V)) # V is 2x further along than U g = igcd(U - V, n) if g == 1: continue if g == n: break return int(g) V = prng.randint(0, n - 1) a = prng.randint(1, n - 3) # for x**2 + a, a%n should not be 0 or -2 F = None return None def pollard_pm1(n, B=10, a=2, retries=0, seed=1234): """ Use Pollard's p-1 method to try to extract a nontrivial factor of ``n``. Either a divisor (perhaps composite) or ``None`` is returned. The value of ``a`` is the base that is used in the test gcd(a**M - 1, n). The default is 2. If ``retries`` > 0 then if no factor is found after the first attempt, a new ``a`` will be generated randomly (using the ``seed``) and the process repeated. Note: the value of M is lcm(1..B) = reduce(ilcm, range(2, B + 1)). A search is made for factors next to even numbers having a power smoothness less than ``B``. Choosing a larger B increases the likelihood of finding a larger factor but takes longer. Whether a factor of n is found or not depends on ``a`` and the power smoothness of the even mumber just less than the factor p (hence the name p - 1). Although some discussion of what constitutes a good ``a`` some descriptions are hard to interpret. At the modular.math site referenced below it is stated that if gcd(a**M - 1, n) = N then a**M % q**r is 1 for every prime power divisor of N. But consider the following: >>> from sympy.ntheory.factor_ import smoothness_p, pollard_pm1 >>> n=257*1009 >>> smoothness_p(n) (-1, [(257, (1, 2, 256)), (1009, (1, 7, 16))]) So we should (and can) find a root with B=16: >>> pollard_pm1(n, B=16, a=3) 1009 If we attempt to increase B to 256 we find that it doesn't work: >>> pollard_pm1(n, B=256) >>> But if the value of ``a`` is changed we find that only multiples of 257 work, e.g.: >>> pollard_pm1(n, B=256, a=257) 1009 Checking different ``a`` values shows that all the ones that didn't work had a gcd value not equal to ``n`` but equal to one of the factors: >>> from sympy.core.numbers import ilcm, igcd >>> from sympy import factorint >>> M = reduce(ilcm, range(2, 256)) >>> set([igcd(pow(a, M, n) - 1, n) for a in range(2, 256) if ... igcd(pow(a, M, n) - 1, n) != n]) set([1009]) But does aM % d for every divisor of n give 1? >>> aM = pow(a, M, n) >>> [(d, aM%(1*d)) for d in factorint(n, visual=True).args] [(257**1, 1), (1009**1, 1)] No, only one of them. So perhaps the principle is that a root will be found for a given value of B provided that: 1) the power smoothness of the p - 1 value next to the root does not exceed B 2) a**M % p != 1 for any of the divisors of n. By trying more than one ``a`` it is possible that one of them will yield a factor. Example usage ============= With the default smoothness bound, this number can't be cracked: >>> from sympy.ntheory import pollard_pm1, primefactors >>> pollard_pm1(21477639576571) Increasing the smoothness bound helps: >>> pollard_pm1(21477639576571, B=2000) 4410317 Looking at the smoothness of the factors of this number we find: >>> from sympy.utilities import flatten >>> from sympy.ntheory.factor_ import smoothness_p, factorint >>> print smoothness_p(21477639576571, visual=1) p**i=4410317**1 has p-1 B=1787, B-pow=1787 p**i=4869863**1 has p-1 B=2434931, B-pow=2434931 The B and B-pow are the same for the p - 1 factorizations of the divisors because those factorizations had a very large prime factor: >>> factorint(4410317 - 1) {2: 2, 617: 1, 1787: 1} >>> factorint(4869863-1) {2: 1, 2434931: 1} Note that until B reaches the B-pow value of 1787, the number is not cracked; >>> pollard_pm1(21477639576571, B=1786) >>> pollard_pm1(21477639576571, B=1787) 4410317 The B value has to do with the factors of the number next to the divisor, not the divisors themselves. A worst case scenario is that the number next to the factor p has a large prime divisisor or is a perfect power. If these conditions apply then the power-smoothness will be about p/2 or p. The more realistic is that there will be a large prime factor next to p requiring a B value on the order of p/2. Although primes may have been searched for up to this level, the p/2 is a factor of p - 1, something that we don't know. The modular.math reference below states that 15% of numbers in the range of 10**15 to 15**15 + 10**4 are 10**6 power smooth so a B of 10**6 will fail 85% of the time in that range. From 10**8 to 10**8 + 10**3 the percentages are nearly reversed...but in that range the simple trial division is quite fast. References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 236-238 - http://modular.math.washington.edu/edu/2007/spring/ent/ent-html/ node81.html - http://www.math.mcgill.ca/darmon/courses/05-06/usra/charest.pdf - http://www.cs.toronto.edu/~yuvalf/Factorization.pdf """ n = int(n) if n < 4 or B < 3: raise ValueError('pollard_pm1 should receive n > 3 and B > 2') prng = random.Random(seed + B) # computing a**lcm(1,2,3,..B) % n for B > 2 # it looks weird, but it's right: primes run [2, B] # and the answer's not right until the loop is done. for i in range(retries + 1): aM = a for p in sieve.primerange(2, B + 1): e = int(math.log(B, p)) aM = pow(aM, pow(p, e), n) g = igcd(aM - 1, n) if 1 < g < n: return int(g) # get a new a: # since the exponent, lcm(1..B), is even, if we allow 'a' to be 'n-1' # then (n - 1)**even % n will be 1 which will give a g of 0 and 1 will # give a zero, too, so we set the range as [2, n-2]. Some references # say 'a' should be coprime to n, but either will detect factors. a = prng.randint(2, n - 2) def _trial(factors, n, candidates, verbose=False): """ Helper function for integer factorization. Trial factors ``n` against all integers given in the sequence ``candidates`` and updates the dict ``factors`` in-place. Returns the reduced value of ``n`` and a flag indicating whether any factors were found. """ if verbose: factors0 = factors.keys() nfactors = len(factors) for d in candidates: if n % d == 0: m = multiplicity(d, n) n //= d**m factors[d] = m if verbose: for k in sorted(set(factors).difference(set(factors0))): print factor_msg % (k, factors[k]) return int(n), len(factors) != nfactors def _check_termination(factors, n, limitp1, use_trial, use_rho, use_pm1, verbose): """ Helper function for integer factorization. Checks if ``n`` is a prime or a perfect power, and in those cases updates the factorization and raises ``StopIteration``. """ if verbose: print 'Check for termination' # since we've already been factoring there is no need to do # simultaneous factoring with the power check p = perfect_power(n, factor=False) if p is not False: base, exp = p if limitp1: limit = limitp1 - 1 else: limit = limitp1 facs = factorint(base, limit, use_trial, use_rho, use_pm1, verbose=False) for b, e in facs.items(): if verbose: print factor_msg % (b, e) factors[b] = exp*e raise StopIteration if isprime(n): factors[int(n)] = 1 raise StopIteration if n == 1: raise StopIteration trial_int_msg = "Trial division with ints [%i ... %i] and fail_max=%i" trial_msg = "Trial division with primes [%i ... %i]" rho_msg = "Pollard's rho with retries %i, max_steps %i and seed %i" pm1_msg = "Pollard's p-1 with smoothness bound %i and seed %i" factor_msg = '\t%i ** %i' fermat_msg = 'Close factors satisying Fermat condition found.' complete_msg = 'Factorization is complete.' def _factorint_small(factors, n, limit, fail_max): """ Return the value of n and either a 0 (indicating that factorization up to the limit was complete) or else the next near-prime that would have been tested. Factoring stops if there are fail_max unsuccessful tests in a row. If factors of n were found they will be in the factors dictionary as {factor: multiplicity} and the returned value of n will have had those factors removed. The factors dictionary is modified in-place. """ def done(n, d): """return n, d if the sqrt(n) wasn't reached yet, else n, 0 indicating that factoring is done. """ if d*d <= n: return n, d return n, 0 d = 2 m = trailing(n) if m: factors[d] = m n >>= m d = 3 if limit < d: if n > 1: factors[n] = 1 return done(n, d) # reduce m = 0 while n % d == 0: n //= d m += 1 if m == 20: mm = multiplicity(d, n) m += mm n //= d**mm break if m: factors[d] = m # when d*d exceeds maxx or n we are done; if limit**2 is greater # than n then maxx is set to zero so the value of n will flag the finish if limit*limit > n: maxx = 0 else: maxx = limit*limit dd = maxx or n d = 5 fails = 0 while fails < fail_max: if d*d > dd: break # d = 6*i - 1 # reduce m = 0 while n % d == 0: n //= d m += 1 if m == 20: mm = multiplicity(d, n) m += mm n //= d**mm break if m: factors[d] = m dd = maxx or n fails = 0 else: fails += 1 d += 2 if d*d > dd: break # d = 6*i - 1 # reduce m = 0 while n % d == 0: n //= d m += 1 if m == 20: mm = multiplicity(d, n) m += mm n //= d**mm break if m: factors[d] = m dd = maxx or n fails = 0 else: fails += 1 # d = 6*(i+1) - 1 d += 4 return done(n, d) def factorint(n, limit=None, use_trial=True, use_rho=True, use_pm1=True, verbose=False, visual=None): """ Given a positive integer ``n``, ``factorint(n)`` returns a dict containing the prime factors of ``n`` as keys and their respective multiplicities as values. For example: >>> from sympy.ntheory import factorint >>> factorint(2000) # 2000 = (2**4) * (5**3) {2: 4, 5: 3} >>> factorint(65537) # This number is prime {65537: 1} For input less than 2, factorint behaves as follows: - ``factorint(1)`` returns the empty factorization, ``{}`` - ``factorint(0)`` returns ``{0:1}`` - ``factorint(-n)`` adds ``-1:1`` to the factors and then factors ``n`` Algorithm ========= The function switches between multiple algorithms. Trial division quickly finds small factors (of the order 1-5 digits), and finds all large factors if given enough time. The Pollard rho and p-1 algorithms are used to find large factors ahead of time; they will often find factors of the order of 10 digits within a few seconds: >>> factors = factorint(12345678910111213141516) >>> for base, exp in sorted(factors.items()): ... print base, exp ... 2 2 2507191691 1 1231026625769 1 Any of these methods can optionally be disabled with the following boolean parameters: - ``use_trial``: Toggle use of trial division - ``use_rho``: Toggle use of Pollard's rho method - ``use_pm1``: Toggle use of Pollard's p-1 method ``factorint`` also periodically checks if the remaining part is a prime number or a perfect power, and in those cases stops. Partial Factorization ===================== If ``limit`` (> 3) is specified, the search is stopped after performing trial division up to (and including) the limit (or taking a corresponding number of rho/p-1 steps). This is useful if one has a large number and only is interested in finding small factors (if any). Note that setting a limit does not prevent larger factors from being found early; it simply means that the largest factor may be composite. Since checking for perfect power is relatively cheap, it is done regardless of the limit setting. This number, for example, has two small factors and a huge semi-prime factor that cannot be reduced easily: >>> from sympy.ntheory import isprime >>> a = 1407633717262338957430697921446883 >>> f = factorint(a, limit=10000) >>> f == {991: 1, 202916782076162456022877024859L: 1, 7: 1} True >>> isprime(max(f)) False This number has a small factor and a residual perfect power whose base is greater than the limit: >>> factorint(3*101**7, limit=5) {3: 1, 101: 7} Visual Factorization ==================== If ``visual`` is set to ``True``, then it will return a visual factorization of the integer. For example: >>> from sympy import pprint >>> pprint(factorint(4200, visual=True)) 3 1 2 1 2 *3 *5 *7 Note that this is achieved by using the evaluate=False flag in Mul and Pow. If you do other manipulations with an expression where evaluate=False, it may evaluate. Therefore, you should use the visual option only for visualization, and use the normal dictionary returned by visual=False if you want to perform operations on the factors. You can easily switch between the two forms by sending them back to factorint: >>> from sympy import Mul, Pow >>> regular = factorint(1764); regular {2: 2, 3: 2, 7: 2} >>> pprint(factorint(regular)) 2 2 2 2 *3 *7 >>> visual = factorint(1764, visual=True); pprint(visual) 2 2 2 2 *3 *7 >>> print factorint(visual) {2: 2, 3: 2, 7: 2} If you want to send a number to be factored in a partially factored form you can do so with a dictionary or unevaluated expression: >>> factorint(factorint({4: 2, 12: 3})) # twice to toggle to dict form {2: 10, 3: 3} >>> factorint(Mul(4, 12, **dict(evaluate=False))) {2: 4, 3: 1} The table of the output logic is: _______________________________ | | visual= | |input + -----+--------+------+ | | True | False | other| +------+------+--------+------+ |dict | mul | dict | mul | |n | mul | dict | dict | |mul | mul | dict | dict | +------+------+--------+------+ Miscellaneous Options ===================== If ``verbose`` is set to ``True``, detailed progress is printed. """ factordict = {} if visual and not isinstance(n, Mul) and not isinstance(n, dict): factordict = factorint(n, limit=limit, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose, visual=False) elif isinstance(n, Mul): factordict = dict([(int(k), int(v)) for k, v in n.as_powers_dict().items()]) elif isinstance(n, dict): factordict = n if factordict and (isinstance(n, Mul) or isinstance(n, dict)): # check it for k in factordict.keys(): if isprime(k): continue e = factordict.pop(k) d = factorint(k, limit=limit, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose, visual=False) for k, v in d.items(): if k in factordict: factordict[k] += v*e else: factordict[k] = v*e if visual or (type(n) is dict and visual is not True and visual is not False): if factordict == {}: return S.One return Mul(*[Pow(*i, **{'evaluate':False}) for i in sorted(factordict.items())], **{'evaluate':False}) elif isinstance(n, dict) or isinstance(n, Mul): return factordict assert use_trial or use_rho or use_pm1 n = int(n) if limit: limit = int(limit) # special cases if n < 0: factors = factorint(-n, limit=limit, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose, visual=False) factors[-1] = 1 return factors if limit: if limit < 2: if n == 1: return {} return {n: 1} elif n < 10: # doing this we are assured of getting a limit > 2 # when we have to compute it later return [{0: 1}, {}, {2: 1}, {3: 1}, {2: 2}, {5: 1}, {2: 1, 3: 1}, {7: 1}, {2: 3}, {3: 2}][n] factors = {} # do simplistic factorization if verbose: sn = str(n) if len(sn) > 50: print 'Factoring %s' % sn[:5] + \ '..(%i other digits)..' % (len(sn) - 10) + sn[-5:] else: print 'Factoring', n if use_trial: # this is the preliminary factorization for small factors small = 2**15 fail_max = 600 small = min(small, limit or small) if verbose: print trial_int_msg % (2, small, fail_max) n, next_p = _factorint_small(factors, n, small, fail_max) else: next_p = 2 if factors and verbose: for k in sorted(factors): print factor_msg % (k, factors[k]) if next_p == 0: if n > 1: factors[int(n)] = 1 if verbose: print complete_msg return factors # continue with more advanced factorization methods # first check if the simplistic run didn't finish # because of the limit and check for a perfect # power before exiting try: if limit and next_p > limit: if verbose: print 'Exceeded limit:', limit _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, verbose) if n > 1: factors[int(n)] = 1 return factors else: # Before quitting (or continuing on)... # ...do a Fermat test since it's so easy and we need the # square root anyway. Finding 2 factors is easy if they are # "close enough." This is the big root equivalent of dividing by # 2, 3, 5. sqrt_n = integer_nthroot(n, 2)[0] a = sqrt_n + 1 a2 = a**2 b2 = a2 - n for i in range(3): b, fermat = integer_nthroot(b2, 2) if fermat: break b2 += 2*a + 1 # equiv to (a+1)**2 - n a += 1 if fermat: if verbose: print fermat_msg if limit: limit -= 1 for r in [a - b, a + b]: facs = factorint(r, limit=limit, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose) factors.update(facs) raise StopIteration # ...see if factorization can be terminated _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, verbose) except StopIteration: if verbose: print complete_msg return factors # these are the limits for trial division which will # be attempted in parallel with pollard methods low, high = next_p, 2*next_p limit = limit or sqrt_n # add 1 to make sure limit is reached in primerange calls limit += 1 while 1: try: high_ = high if limit < high_: high_ = limit # Trial division if use_trial: if verbose: print trial_msg % (low, high_) ps = sieve.primerange(low, high_) n, found_trial = _trial(factors, n, ps, verbose) if found_trial: _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, verbose) else: found_trial = False if high > limit: if verbose: print 'Exceeded limit:', limit if n > 1: factors[int(n)] = 1 raise StopIteration # Only used advanced methods when no small factors were found if not found_trial: if (use_pm1 or use_rho): high_root = max(int(math.log(high_**0.7)), low, 3) # Pollard p-1 if use_pm1: if verbose: print (pm1_msg % (high_root, high_)) c = pollard_pm1(n, B=high_root, seed=high_) if c: # factor it and let _trial do the update ps = factorint(c, limit=limit-1, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose) n, _ = _trial(factors, n, ps, verbose=False) _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, verbose) # Pollard rho if use_rho: max_steps = high_root if verbose: print (rho_msg % (1, max_steps, high_)) c = pollard_rho(n, retries=1, max_steps=max_steps, \ seed=high_) if c: # factor it and let _trial do the update ps = factorint(c, limit=limit-1, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose) n, _ = _trial(factors, n, ps, verbose=False) _check_termination(factors, n, limit, use_trial, use_rho, use_pm1, verbose) except StopIteration: if verbose: print complete_msg return factors low, high = high, high*2 def primefactors(n, limit=None, verbose=False): """Return a sorted list of n's prime factors, ignoring multiplicity and any composite factor that remains if the limit was set too low for complete factorization. Unlike factorint(), primefactors() does not return -1 or 0. Example usage ============= >>> from sympy.ntheory import primefactors, factorint, isprime >>> primefactors(6) [2, 3] >>> primefactors(-5) [5] >>> sorted(factorint(123456).items()) [(2, 6), (3, 1), (643, 1)] >>> primefactors(123456) [2, 3, 643] >>> sorted(factorint(10000000001, limit=200).items()) [(101, 1), (99009901, 1)] >>> isprime(99009901) False >>> primefactors(10000000001, limit=300) [101] """ n = int(n) s = [] factors = sorted(factorint(n, limit=limit, verbose=verbose).keys()) s = [f for f in factors[:-1:] if f not in [-1, 0, 1]] if factors and isprime(factors[-1]): s += [factors[-1]] return s def _divisors(n): """Helper function for divisors which generates the divisors.""" factordict = factorint(n) ps = sorted(factordict.keys()) def rec_gen(n = 0): if n == len(ps): yield 1 else : pows = [1] for j in xrange(factordict[ps[n]]): pows.append(pows[-1] * ps[n]) for q in rec_gen(n + 1): for p in pows: yield p * q for p in rec_gen() : yield p def divisors(n, generator=False): """ Return all divisors of n sorted from 1..n by default. If generator is True an unordered generator is returned. The number of divisors of n can be quite large if there are many prime factors (counting repeated factors). If only the number of factors is desired use divisor_count(n). Examples:: >>> from sympy import divisors, divisor_count >>> divisors(24) [1, 2, 3, 4, 6, 8, 12, 24] >>> divisor_count(24) 8 >>> list(divisors(120, generator=True)) [1, 2, 4, 8, 3, 6, 12, 24, 5, 10, 20, 40, 15, 30, 60, 120] This is a slightly modified version of Tim Peters referenced at: http://stackoverflow.com/questions/1010381/python-factorization """ n = abs(n) if isprime(n): return [1, n] elif n == 1: return [1] elif n == 0: return [] else: rv = _divisors(n) if not generator: return sorted(rv) return rv def divisor_count(n): """Return the number of divisors of n. Reference: http://www.mayer.dial.pipex.com/maths/formulae.htm >>> from sympy import divisor_count >>> divisor_count(6) 4 """ n = abs(n) if n == 0: return 0 return Mul(*[v+1 for k, v in factorint(n).items() if k > 1]) def totient(n): """Calculate the Euler totient function phi(n) >>> from sympy.ntheory import totient >>> totient(1) 1 >>> totient(25) 20 """ if n < 1: raise ValueError("n must be a positive integer") factors = factorint(n) t = 1 for p, k in factors.iteritems(): t *= (p-1) * p**(k-1) return t wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/multinomial.py0000644000175000017500000000473412014170666025503 0ustar georgeskgeorgeskdef binomial_coefficients(n): """Return a dictionary containing pairs {(k1,k2) : C_kn} where C_kn are binomial coefficients and n=k1+k2.""" d = {(0, n):1, (n, 0):1} a = 1 for k in xrange(1, n//2+1): a = (a * (n-k+1))//k d[k, n-k] = d[n-k, k] = a return d def binomial_coefficients_list(n): """ Return a list of binomial coefficients as rows of the Pascal's triangle. """ d = [1] * (n+1) a = 1 for k in xrange(1, n//2+1): a = (a * (n-k+1))//k d[k] = d[n-k] = a return d def multinomial_coefficients(m, n, _tuple=tuple, _zip=zip): """Return a dictionary containing pairs ``{(k1,k2,..,km) : C_kn}`` where ``C_kn`` are multinomial coefficients such that ``n=k1+k2+..+km``. For example: >>> from sympy.ntheory import multinomial_coefficients >>> print multinomial_coefficients(2,5) {(3, 2): 10, (1, 4): 5, (2, 3): 10, (5, 0): 1, (0, 5): 1, (4, 1): 5} The algorithm is based on the following result: Consider a polynomial and its ``m``-th exponent:: P(x) = sum_{i=0}^m p_i x^k P(x)^n = sum_{k=0}^{m n} a(n,k) x^k The coefficients ``a(n,k)`` can be computed using the J.C.P. Miller Pure Recurrence [see D.E.Knuth, Seminumerical Algorithms, The art of Computer Programming v.2, Addison Wesley, Reading, 1981;]:: a(n,k) = 1/(k p_0) sum_{i=1}^m p_i ((n+1)i-k) a(n,k-i), where ``a(n,0) = p_0^n``. """ if m==2: return binomial_coefficients(n) symbols = [(0,)*i + (1,) + (0,)*(m-i-1) for i in range(m)] s0 = symbols[0] p0 = [_tuple(aa-bb for aa,bb in _zip(s,s0)) for s in symbols] r = {_tuple(aa*n for aa in s0):1} r_get = r.get r_update = r.update l = [0] * (n*(m-1)+1) l[0] = r.items() for k in xrange(1, n*(m-1)+1): d = {} d_get = d.get for i in xrange(1, min(m,k+1)): nn = (n+1)*i-k if not nn: continue t = p0[i] for t2, c2 in l[k-i]: tt = _tuple([aa+bb for aa,bb in _zip(t2,t)]) cc = nn * c2 b = d_get(tt) if b is None: d[tt] = cc else: cc = b + cc if cc: d[tt] = cc else: del d[tt] r1 = [(t, c//k) for (t, c) in d.iteritems()] l[k] = r1 r_update(r1) return r wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/residue_ntheory.py0000644000175000017500000000467612014170666026366 0ustar georgeskgeorgeskfrom sympy.core.numbers import igcd from primetest import isprime from factor_ import factorint def totient_(n): """returns the number of integers less than n and relatively prime to n""" if n < 1: raise ValueError("n must be a positive integer") tot = 0 for x in xrange(1, n): if igcd(x, n) == 1: tot += 1 return tot def n_order(a, n): """ returns the order of a modulo n Order of a modulo n is the smallest integer k such that a^k leaves a remainder of 1 with n. """ if igcd(a, n) != 1: raise ValueError("The two numbers should be relatively prime") group_order = totient_(n) factors = factorint(group_order) order = 1 if a > n: a = a % n for p, e in factors.iteritems(): exponent = group_order for f in xrange(0, e + 1): if (a ** (exponent)) % n != 1: order *= p ** (e - f + 1) break exponent = exponent // p return order def is_primitive_root(a, p): """ returns True if a is a primitive root of p """ if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p if n_order(a, p) == totient_(p): return True else: return False def is_quad_residue(a, p): """ returns True if a is a quadratic residue of p p should be a prime and a should be relatively prime to p """ if not isprime(p) or p == 2: raise ValueError("p should be an odd prime") if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p def square_and_multiply(a, n, p): if n == 0: return 1 elif n == 1: return a elif n % 2 == 1: return ((square_and_multiply(a, n // 2, p) ** 2) * a) % p else: return (square_and_multiply(a, n // 2, p) ** 2) % p return (square_and_multiply(a, (p - 1) // 2, p) % p) == 1 def legendre_symbol(a, p): """ return 1 if a is a quadratic residue of p else return -1 p should be an odd prime by definition """ if not isprime(p) or p == 2: raise ValueError("p should be an odd prime") if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p if is_quad_residue(a, p): return 1 else: return -1 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/modular.py0000644000175000017500000000244012014170666024604 0ustar georgeskgeorgeskfrom sympy.core.numbers import igcdex def crt(m, v, symmetric=False): """Chinese Remainder Theorem. The integers in m are assumed to be pairwise coprime. The output is then an integer f, such that f = v_i mod m_i for each pair out of v and m. """ mm = 1 for m_i in m: mm *= m_i result = 0 for m_i, v_i in zip(m, v): e = mm // m_i s, t, g = igcdex(e, m_i) c = v_i*s % m_i result += c*e result %= mm if symmetric: if result <= mm//2: return result else: return result - mm else: return result def crt1(m): """First part of chines remainder theorem, for multiple application. """ mm, e, s = 1, [], [] for m_i in m: mm *= m_i for m_i in m: e.append(mm // m_i) s.append(igcdex(e[-1], m_i)[0]) return mm, e, s def crt2(m, v, mm, e, s, symmetric=False): """Second part of Chinese Remainder Theorem, for multiple application. """ result = 0 for m_i, v_i, e_i, s_i in zip(m, v, e, s): c = v_i*s_i % m_i result += c*e_i result %= mm if symmetric: if result <= mm // 2: return result else: return result - mm else: return result wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/primetest.py0000644000175000017500000002460512014170666025164 0ustar georgeskgeorgesk""" Primality testing """ # prime list to use when number must be tested as a probable prime. #>>> list(primerange(2, 200)) _isprime_fallback_primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199] #>>> len(_) #46 # pseudoprimes that will pass through last mr_safe test _pseudos = set([ 669094855201, 1052516956501,2007193456621,2744715551581,9542968210729, 17699592963781,19671510288601, 24983920772821,24984938689453,29661584268781,37473222618541, 46856248255981,47922612926653,48103703944453,49110566041153, 49752242681221,91206655032481,91481980096033,119034193492321, 123645258399601,128928036060253,137364148720147,150753857310253, 153131886327421,155216912613121,185610214763821,224334357392701, 227752294950181,230058334559041,304562854940401,306001576998253, 335788261073821,377133492079081,379242177424951,389970770948461, 397319638319521,448114903362253,523235160050221,628999496281621, 699349238838253,746667678235753,790198268451301,794036495175661, 823820871230281,867739535711821,1039918661294761,1099127938585141, 1104388025338153,1173374598605653,1262797719066157,1265872947674653, 1325898212229667,1327034517143653,1418575746675583,1666122072463621, 1837400535259453,1857422490084961,1870756820971741,1914550540480717, 2018963273468221,2163829000939453,2206020317369221,2301037384029121, 2416062055125421,2435076500074921,2545656135020833,2594428516569781, 2669983768115821,2690937050990653,2758640869506607,2833525461416653, 2876662942007221,2932155806957821,2957010595723801,3183606449929153, 3220133449185901,3424103775720253,3625360152399541,3939300299037421, 3947917710714841,3980273496750253,4182256679324041,4450605887818261, 4727893739521501,4750350311306953,4755334362931153,5756440863559753, 5760976603475341,5794399356078761,5954850603819253,6125544931991761, 6320931714094861,6347593619672581,6406268028524101,6510632945054941, 6620082224794741,6627325072566061,6844056606431101,6989404981060153, 7144293947609521,7288348593229021,7288539837129253,7406102904971689, 7430233301822341,7576425305871193,7601696719033861,7803926845356487, 7892007967006633,7947797946559453,8207000460596953,8295064717807513, 8337196000698841,8352714234009421,8389755717406381,8509654470665701, 8757647355282841,8903933671696381,8996133652295653,9074421465661261, 9157536631454221,9188353522314541]) def _test(n, base): """Miller-Rabin strong pseudoprime test for one base. Return False if n is definitely composite, True if n is probably prime, with a probability greater than 3/4. """ from sympy.ntheory.factor_ import trailing n = int(n) if n < 2: return False # remove powers of 2 from n (= t * 2**s) s = trailing(n - 1) t = n >> s # do the Fermat test b = pow(base, t, n) if b == 1 or b == n-1: return True else: for j in xrange(1, s): b = (b**2) % n if b == n-1: return True return False def mr(n, bases): """Perform a Miller-Rabin strong pseudoprime test on n using a given list of bases/witnesses. Reference: Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 135-138 """ n = int(n) for base in bases: if not _test(n, base): return False return True def _mr_safe(n): """For n < 10**16, use the Miller-Rabin test to determine with certainty (unless the code is buggy!) whether n is prime. Although the primes 2 through 17 are sufficient to confirm that a number less than 341550071728322 (that is not prime 2 through 17) is prime, this range is broken up into smaller ranges with earlier ranges requiring less work. For example, for n < 1373653 only the bases 2 and 3 need be tested. What makes this a "safe" Miller-Rabin routine is that for n less than the indicated limit, the given bases have been confirmed to detect all composite numbers. What can potentially make this routine "unsafe" is including ranges for which previous tests do not removes prime factors of the bases being used. For example, this routine assumes that 2 and 3 have already been removed as prime; but if the first test were the one for n < 170584961 (that uses bases 350 and 3958281543) the routine would have to ensure that the primes 5, 7, 29, 67, 679067 are already removed or else they will be reported as being composite. For this reason it is helpful to list the prime factors of the bases being tested as is done below. The _mr_safe_helper can be used to generate this info-tag. References for the bounds: [1] http://primes.utm.edu/prove/prove2_3.html [2] http://www.trnicely.net/misc/mpzspsp.html [3] http://en.wikipedia.org/wiki/Miller-Rabin_primality_test# Accuracy_of_the_test [4] http://zdu.spaces.live.com/?_c11_BlogPart_pagedir= Next&_c11_BlogPart_handle=cns!C95152CB25EF2037! 138&_c11_BlogPart_BlogPart=blogview&_c=BlogPart [5] http://primes.utm.edu/glossary/xpage/Pseudoprime.html [6] http://uucode.com/obf/dalbec/alg.html#sprp """ if n < 1373653: return mr(n, [2, 3]) #[2, 3] stot = 1 clear == bases # these two (and similar below) are commented out since they are # more expensive in terms of stot than a later test. #if n < 9080191: return mr(n, [31, 73]) # ref [3] # [31, 73] stot = 4 clear == bases #if n < 25326001: return mr(n, [2, 3, 5]) # [2, 3, 5] stot = 3 clear == bases if n < 170584961: return mr(n, [350, 3958281543]) # [350, 3958281543] stot = 1 clear [2, 3, 5, 7, 29, 67, 679067] if n < 4759123141: return mr(n, [2, 7, 61]) # ref [3] # [2, 7, 61] stot = 3 clear == bases if n < 75792980677: return mr(n, [2, 379215, 457083754]) # [2, 379215, 457083754] stot = 1 clear [2, 3, 5, 53, 228541877] #if n < 118670087467: return n is not 3215031751 and mr(n, [2, 3, 5, 7]) # ref [3] # [2, 3, 5, 7] stot = 4 clear == bases if n < 1000000000000: return mr(n, [2, 13, 23, 1662803]) # [2, 13, 23, 1662803] stot = 4 clear == bases #if n < 2152302898747: return mr(n, [2, 3, 5, 7, 11]) # [2, 3, 5, 7, 11] stot = 5 clear == bases #if n < 3474749660383: return mr(n, [2, 3, 5, 7, 11, 13]) # [2, 3, 5, 7, 11, 13] stot = 7 clear == bases #if n < 21652684502221: return mr(n, [2, 1215, 34862, 574237825]) # [2, 1215, 34862, 574237825] stot = 8 clear [2, 3, 5, 7, 17431, 3281359] #if n < 341550071728321: return mr(n, [2, 3, 5, 7, 11, 13, 17]) # [2, 3, 5, 7, 11, 13, 17] stot = 11 clear == bases if n < 10000000000000000: return mr(n, [2, 3, 7, 61, 24251]) and n not in _pseudos # [2, 3, 7, 61, 24251] stot = 5 clear == bases raise ValueError("n too large") def isprime(n): """ Test if n is a prime number (True) or not (False). For n < 10**16 the answer is accurate; greater n values have a small probability of actually being pseudoprimes. Negative primes (e.g. -2) are not considered prime. The function first looks for trivial factors, and if none is found, performs a safe Miller-Rabin strong pseudoprime test with bases that are known to prove a number prime. Finally, a general Miller-Rabin test is done with the first k bases which, which will report a pseudoprime as a prime with an error of about 4**-k. The current value of k is 46 so the error is about 2 x 10**-28. Example usage ============= >>> from sympy.ntheory import isprime >>> isprime(13) True >>> isprime(15) False """ n = int(n) if n < 2: return False if n & 1 == 0: return n == 2 if n <= 23001: return pow(2, n, n) == 2 and n not in [341, 561, 645, 1105, 1387, 1729, 1905, 2047, 2465, 2701, 2821, 3277, 4033, 4369, 4371, 4681, 5461, 6601, 7957, 8321, 8481, 8911, 10261, 10585, 11305, 12801, 13741, 13747, 13981, 14491, 15709, 15841, 16705, 18705, 18721, 19951, 23001] try: return _mr_safe(n) except ValueError: return mr(n, _isprime_fallback_primes) def _mr_safe_helper(_s): """ Analyze a (new) mr_safe line for for total number of s's to be tested in _test along with the primes that must be cleared by a previous test. e.g. >>> from sympy.ntheory.primetest import _mr_safe_helper >>> print _mr_safe_helper("if n < 170584961: return mr(n, [350, 3958281543])") # [350, 3958281543] stot = 1 clear [2, 3, 5, 7, 29, 67, 679067] >>> print _mr_safe_helper('return mr(n, [2, 379215, 457083754])') # [2, 379215, 457083754] stot = 1 clear [2, 3, 5, 53, 228541877] """ def _info(bases): """ Analyze the list of bases, reporting the number of 'j-loops' that will be required if this list is passed to _test (stot) and the primes that must be cleared by a previous test. This info tag should then be appended to any new mr_safe line that is added so someone can easily see whether that line satisfies the requirements of mr_safe (see docstring there for details). """ from sympy.ntheory.factor_ import factorint, trailing factors = [] tot = 0 for b in bases: tot += trailing(b-1) f = factorint(b) factors.extend(f) factors = sorted(set(factors)) bases = sorted(set(bases)) if bases == factors: factors = '== bases' else: factors = str(factors) return ' # %s stot = %s clear %s' % tuple( [str(x).replace('L','') for x in (list(bases), tot, factors)]) _r = [int(_x) for _x in _s.split('[')[1].split(']')[0].split(',')] return _info(_r) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/tests/0000755000175000017500000000000012014170666023731 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/tests/test_ntheory.py0000644000175000017500000004405112014170666027036 0ustar georgeskgeorgeskfrom sympy import Sieve, binomial_coefficients, binomial_coefficients_list, \ multinomial_coefficients, Mul, S, Pow from sympy import factorial as fac from sympy.ntheory import isprime, n_order, is_primitive_root, \ is_quad_residue, legendre_symbol, npartitions, totient, \ factorint, primefactors, divisors, randprime, nextprime, prevprime, \ primerange, primepi, prime, pollard_rho, perfect_power, multiplicity, \ trailing, divisor_count, primorial, pollard_pm1 from sympy.ntheory.factor_ import smoothness, smoothness_p from sympy.ntheory.generate import cycle_length from sympy.ntheory.primetest import _mr_safe_helper, mr from sympy.ntheory.bbp_pi import pi_hex_digits from sympy.ntheory.modular import crt, crt1, crt2 from sympy.utilities.pytest import raises from sympy.utilities.iterables import capture def test_trailing(): assert trailing(0) == 0 assert trailing(1) == 0 assert trailing(-1) == 0 assert trailing(2) == 1 assert trailing(7) == 0 assert trailing(-7) == 0 for i in range(100): assert trailing((1< n: return 0 if p > n//2: return 1 q, m = n, 0 while q >= p: q //= p m += q return m def multiproduct(seq=(), start=1): """ Return the product of a sequence of factors with multiplicities, times the value of the parameter ``start``. The input may be a sequence of (factor, exponent) pairs or a dict of such pairs. >>> multiproduct({3:7, 2:5}, 4) # = 3**7 * 2**5 * 4 279936 """ if not seq: return start if isinstance(seq, dict): seq = seq.iteritems() units = start multi = [] for base, exp in seq: if not exp: continue elif exp == 1: units *= base else: if exp % 2: units *= base multi.append((base, exp//2)) return units * multiproduct(multi)**2 def test_factorint(): assert primefactors(123456) == [2, 3, 643] assert factorint(0) == {0:1} assert factorint(1) == {} assert factorint(-1) == {-1:1} assert factorint(-2) == {-1:1, 2:1} assert factorint(-16) == {-1:1, 2:4} assert factorint(2) == {2:1} assert factorint(126) == {2:1, 3:2, 7:1} assert factorint(123456) == {2:6, 3:1, 643:1} assert factorint(5951757) == {3:1, 7:1, 29:2, 337:1} assert factorint(64015937) == {7993:1, 8009:1} assert factorint(2**(2**6) + 1) == {274177:1, 67280421310721:1} assert multiproduct(factorint(fac(200))) == fac(200) for b, e in factorint(fac(150)).items(): assert e == fac_multiplicity(150, b) assert factorint(103005006059**7) == {103005006059:7} assert factorint(31337**191) == {31337:191} assert factorint(2**1000 * 3**500 * 257**127 * 383**60) == \ {2:1000, 3:500, 257:127, 383:60} assert len(factorint(fac(10000))) == 1229 assert factorint(12932983746293756928584532764589230) == \ {2: 1, 5: 1, 73: 1, 727719592270351: 1, 63564265087747: 1, 383: 1} assert factorint(727719592270351) == {727719592270351:1} assert factorint(2**64+1, use_trial=False) == factorint(2**64+1) for n in range(60000): assert multiproduct(factorint(n)) == n assert pollard_rho(2**64+1, seed=1) == 274177 assert pollard_rho(19, seed=1) is None assert factorint(3, limit=2) == {3: 1} assert factorint(12345) == {3: 1, 5: 1, 823: 1} assert factorint(12345, limit=3) == {4115: 1, 3: 1} # the 5 is greater than the limit assert factorint(1, limit=1) == {} assert factorint(12, limit=1) == {12: 1} assert factorint(30, limit=2) == {2: 1, 15: 1} assert factorint(16, limit=2) == {2: 4} assert factorint(124, limit=3) == {2: 2, 31: 1} assert factorint(4*31**2, limit=3) == {2: 2, 31: 2} p1 = nextprime(2**32) p2 = nextprime(2**16) p3 = nextprime(p2) assert factorint(p1*p2*p3) == {p1: 1, p2: 1, p3: 1} assert factorint(13*17*19, limit=15) == {13: 1, 17*19: 1} assert factorint(1951*15013*15053, limit=2000) == {225990689: 1, 1951: 1} assert factorint(primorial(17)+1, use_pm1=0) == \ {19026377261L: 1, 3467: 1, 277: 1, 105229: 1} # when prime b is closer than approx sqrt(8*p) to prime p then they are # "close" and have a trivial factorization a=nextprime(2**2**8) # 78 digits b=nextprime(a + 2**2**4) assert 'Fermat' in capture(lambda : factorint(a*b, verbose=1)) raises(ValueError, 'pollard_rho(4)') raises(ValueError, 'pollard_pm1(3)') raises(ValueError, 'pollard_pm1(10, B=2)') # verbose coverage n = nextprime(2**16)*nextprime(2**17)*nextprime(1901) assert 'with primes' in capture(lambda: factorint(n, verbose=1)) capture(lambda: factorint(nextprime(2**16)*1012, verbose=1)) n=nextprime(2**17) capture(lambda: factorint(n**3, verbose=1)) # perfect power termination capture(lambda: factorint(2*n, verbose=1)) # factoring complete msg # exceed 1st n=nextprime(2**17) n*=nextprime(n) assert '1000' in capture(lambda : factorint(n, limit=1000, verbose=1)) n*=nextprime(n) assert len(factorint(n)) == 3 assert len(factorint(n, limit=p1)) == 3 n*=nextprime(2*n) # exceed 2nd assert '2001' in capture(lambda : factorint(n, limit=2000, verbose=1)) assert capture(lambda : factorint(n, limit=4000, verbose=1)).count('Pollard') == 2 # non-prime pm1 result n=nextprime(8069) n*=nextprime(2*n)*nextprime(2*n, 2) capture(lambda: factorint(n, verbose=1)) # non-prime pm1 result # factor fermat composite p1 = nextprime(2**17) p2 = nextprime(2*p1) assert factorint((p1*p2**2)**3) == {p1: 3, p2: 6} def divisors_and_divisor_count(): assert divisors(-1) == [1] assert divisors(0) == [] assert divisors(1) == [1] assert divisors(2) == [1, 2] assert divisors(3) == [1, 3] assert divisors(17) == [1, 17] assert divisors(10) == [1, 2, 5, 10] assert divisors(100) == [1, 2, 4, 5, 10, 20, 25, 50, 100] assert divisors(101) == [1, 101] assert divisor_count(0) == 0 assert divisor_count(-1) == 1 assert divisor_count(1) == 1 assert divisor_count(6) == 4 assert divisor_count(12) == 6 def test_totient(): assert [totient(k) for k in range(1, 12)] == \ [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10] assert totient(5005) == 2880 assert totient(5006) == 2502 assert totient(5009) == 5008 def test_partitions(): assert [npartitions(k) for k in range(13)] == \ [1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42, 56, 77] assert npartitions(100) == 190569292 assert npartitions(200) == 3972999029388 assert npartitions(1000) == 24061467864032622473692149727991 assert npartitions(2000) == 4720819175619413888601432406799959512200344166 assert npartitions(10000) % 10**10 == 6916435144 assert npartitions(100000) % 10**10 == 9421098519 def test_residue(): assert n_order(2, 13) == 12 assert [n_order(a, 7) for a in range(1, 7)] == \ [1, 3, 6, 3, 6, 2] assert n_order(5, 17) == 16 assert n_order(17, 11) == n_order(6, 11) assert n_order(101, 119) == 6 assert is_primitive_root(2, 7) == False assert is_primitive_root(3, 8) == False assert is_primitive_root(11, 14) == False assert is_primitive_root(12, 17) == is_primitive_root(29, 17) assert is_quad_residue(3, 7) == False assert is_quad_residue(10, 13) == True assert is_quad_residue(12364, 139) == is_quad_residue(132, 139) assert is_quad_residue(207, 251) == True assert legendre_symbol(5, 11) == 1 assert legendre_symbol(25, 41) == 1 assert legendre_symbol(67, 101) == -1 def test_hex_pi_nth_digits(): assert pi_hex_digits(0) == '3243f6a8885a30' assert pi_hex_digits(1) == '243f6a8885a308' assert pi_hex_digits(10000) == '68ac8fcfb8016c' def test_crt(): def mcrt(m, v, r, symmetric=False): assert crt(m, v, symmetric) == r mm, e, s = crt1(m) assert crt2(m, v, mm, e, s, symmetric) == r mcrt([2, 3, 5], [0, 0, 0], 0) mcrt([2, 3, 5], [1, 1, 1], 1) mcrt([2, 3, 5], [-1, -1, -1], -1, True) mcrt([2, 3, 5], [-1, -1, -1], 2*3*5 - 1, False) def test_binomial_coefficients_list(): assert binomial_coefficients_list(0) == [1] assert binomial_coefficients_list(1) == [1, 1] assert binomial_coefficients_list(2) == [1, 2, 1] assert binomial_coefficients_list(3) == [1, 3, 3, 1] assert binomial_coefficients_list(4) == [1, 4, 6, 4, 1] assert binomial_coefficients_list(5) == [1, 5, 10, 10, 5, 1] assert binomial_coefficients_list(6) == [1, 6, 15, 20, 15, 6, 1] def test_binomial_coefficients(): for n in range(15): c = binomial_coefficients(n) l = [c[k] for k in sorted(c)] assert l == binomial_coefficients_list(n) def test_multinomial_coefficients(): assert multinomial_coefficients(1, 1) == {(1,): 1} assert multinomial_coefficients(1, 2) == {(2,): 1} assert multinomial_coefficients(1, 3) == {(3,): 1} assert multinomial_coefficients(2, 1) == {(0, 1): 1, (1, 0): 1} assert multinomial_coefficients(2, 2) == {(2, 0): 1, (0, 2): 1, (1, 1): 2} assert multinomial_coefficients(2, 3) == {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3} assert multinomial_coefficients(3, 1) == {(1, 0, 0): 1, (0, 1, 0): 1, (0, 0, 1): 1} assert multinomial_coefficients(3, 2) == {(0, 1, 1): 2, (0, 0, 2): 1, (1, 1, 0): 2, (0, 2, 0): 1, (1, 0, 1): 2, (2, 0, 0): 1} assert multinomial_coefficients(3, 3) == {(2, 1, 0): 3, (0, 3, 0): 1, (1, 0, 2): 3, (0, 2, 1): 3, (0, 1, 2): 3, (3, 0, 0): 1, (2, 0, 1): 3, (1, 2, 0): 3, (1, 1, 1): 6, (0, 0, 3): 1} def test_issue1257(): assert factorint(1030903) == {53: 2, 367: 1} def test_divisors(): assert divisors(28) == [1, 2, 4, 7, 14, 28] assert [x for x in divisors(3*5*7, 1)] == [1, 3, 5, 15, 7, 21, 35, 105] assert divisors(0) == [] def test_divisor_count(): assert divisor_count(0) == 0 assert divisor_count(6) == 4 def test_primorial(): assert primorial(1) == 2 assert primorial(1, nth=0) == 1 assert primorial(2) == 6 assert primorial(2, nth=0) == 2 assert primorial(4, nth=0) == 6 def test_smoothness_and_smoothness_p(): assert smoothness(1) == (1, 1) assert smoothness(2**4*3**2) == (3, 16) assert smoothness_p(10431, m=1) == \ (1, [(3, (2, 2, 4)), (19, (1, 5, 5)), (61, (1, 31, 31))]) assert smoothness_p(10431) == \ (-1, [(3, (2, 2, 2)), (19, (1, 3, 9)), (61, (1, 5, 5))]) assert smoothness_p(10431, power=1) == \ (-1, [(3, (2, 2, 2)), (61, (1, 5, 5)), (19, (1, 3, 9))]) assert smoothness_p(21477639576571, visual=1) == \ 'p**i=4410317**1 has p-1 B=1787, B-pow=1787\n' + \ 'p**i=4869863**1 has p-1 B=2434931, B-pow=2434931' def test_visual_factorint(): assert factorint(1, visual=1) == 1 forty2 = factorint(42, visual=True) assert type(forty2) == Mul assert str(forty2) == '2**1*3**1*7**1' assert factorint(1, visual=True) is S.One no = dict(evaluate=False) assert factorint(42**2, visual=True) == Mul(Pow(2, 2, **no), Pow(3, 2, **no), Pow(7, 2, **no), **no) assert Pow(-1, 1, **no) in factorint(-42, visual=True).args def test_visual_io(): sm = smoothness_p fi = factorint # with smoothness_p n = 124 d = fi(n) m = fi(d, visual=True) t = sm(n) s = sm(t) for th in [d, s, t, n, m]: assert sm(th, visual=True) == s assert sm(th, visual=1) == s for th in [d, s, t, n, m]: assert sm(th, visual=False) == t assert [sm(th, visual=None) for th in [d, s, t, n, m]] == [s, d, s, t, t] assert [sm(th, visual=0) for th in [d, s, t, n, m]] == [s, d, s, t, t] # with factorint for th in [d, m, n]: assert fi(th, visual=True) == m assert fi(th, visual=1) == m for th in [d, m, n]: assert fi(th, visual=False) == d assert [fi(th, visual=None) for th in [d, m, n]] == [m, d, d] assert [fi(th, visual=0) for th in [d, m, n]] == [m, d, d] # test reevaluation no = dict(evaluate=False) assert sm({4: 2}, visual=False) == sm(16) assert sm(Mul(*[Pow(k, v, **no) for k, v in {4: 2, 2: 6}.items()], **no), visual=False) == sm(2**10) assert fi({4: 2}, visual=False) == fi(16) assert fi(Mul(*[Pow(k, v, **no) for k, v in {4: 2, 2: 6}.items()], **no), visual=False) == fi(2**10) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/generate.py0000644000175000017500000002560212014170666024740 0ustar georgeskgeorgesk""" Generating and counting primes. """ import random from bisect import bisect from primetest import isprime from sympy.core.numbers import integer_nthroot # Using arrays for sieving instead of lists greatly reduces # memory consumption from array import array as _array def _arange(a, b): ar = _array('l', [0]*(b-a)) for i, e in enumerate(xrange(a, b)): ar[i] = e return ar class Sieve: """An infinite list of prime numbers, implemented as a dynamically growing sieve of Eratosthenes. When a lookup is requested involving a number that has not been sieved, the sieve is automatically extended up to that number.""" _list = _array('l', [2, 3, 5, 7, 11, 13]) def __repr__(self): return "" % \ (len(self._list), self._list[-2], self._list[-1]) def extend(self, N): """Grow the sieve to cover all numbers <= N.""" if N <= self._list[-1]: return # We need to sieve against all bases up to sqrt(n). If there # are too few, extend the list recursively. maxbase = int(N**0.5)+1 self.extend(maxbase) # Create a new sieve starting from N**0.5 begin = self._list[-1] + 1 newsieve = _arange(begin, N+1) # Now eliminate all multiples of primes in [2, N**0.5] for p in self.primerange(2, maxbase): # Start counting at a multiple of p, offsetting # the index to account for the new sieve's base index startindex = (-begin) % p for i in xrange(startindex, len(newsieve), p): newsieve[i] = 0 # Merge the sieves self._list += _array('l', [x for x in newsieve if x]) def extend_to_no(self, n): """Extend to include (at least) the nth prime number""" while len(self._list) < n: self.extend(int(self._list[-1] * 1.5)) def primerange(self, a, b): """Generate all prime numbers in the range [a, b).""" assert a <= b if b < 2: return a = max(2, a) self.extend(b) i = self.search(a)[1] maxi = len(self._list) + 1 while i < maxi: p = self._list[i-1] if p < b: yield p i += 1 else: return def search(self, n): """For n >= 2, return the tightest a, b such that self[a] <= n <= self[b]""" assert n >= 2 if n > self._list[-1]: self.extend(n) b = bisect(self._list, n) if self._list[b-1] == n: return b, b else: return b, b+1 def __contains__(self, n): if n < 2: return False a, b = self.search(n) return a == b def __getitem__(self, n): """Return the nth prime number""" self.extend_to_no(n) return self._list[n-1] # Generate a global object for repeated use in trial division etc sieve = Sieve() def prime(n): """ Return the nth prime, with the primes indexed as prime(1) = 2, prime(2) = 3, etc.... The nth prime is approximately n*log(n) and can never be larger than 2**n. Reference: http://primes.utm.edu/glossary/xpage/BertrandsPostulate.html """ assert n > 0 return sieve[n] def primepi(n): """ Return the value of the prime counting function pi(n) = the number of prime numbers less than or equal to n. The number n need not necessarily be an integer. """ if n < 2: return 0 else: n = int(n) return sieve.search(n)[0] def nextprime(n, i=1): """ Return the ith prime greater than n. Potential primes are located at 6*j +/- 1. >>> from sympy import nextprime >>> [(i, nextprime(i)) for i in range(10, 15)] [(10, 11), (11, 13), (12, 13), (13, 17), (14, 17)] >>> nextprime(2, i=2) # the 2nd prime after 2 5 """ if i > 1: pr = n j = 1 while 1: pr = nextprime(pr) j += 1 if j > i: break return pr n = int(n) if n < 2: return 2 if n < 7: return {2: 3, 3: 5, 4: 5, 5: 7, 6: 7}[n] nn = 6*(n//6) if nn == n: n += 1 if isprime(n): return n n += 4 elif n - nn == 5: n += 2 if isprime(n): return n n += 4 else: n = nn + 5 while 1: if isprime(n): return n n += 2 if isprime(n): return n n += 4 def prevprime(n): """ Return the largest prime smaller than n. Potential primes are located at 6*j +/- 1. >>> from sympy import prevprime >>> [(i, prevprime(i)) for i in range(10, 15)] [(10, 7), (11, 7), (12, 11), (13, 11), (14, 13)] """ n = int(n) if n < 3: raise ValueError("no preceding primes") if n < 8: return {3: 2, 4: 3, 5: 3, 6: 5, 7: 5}[n] nn = 6*(n//6) if n - nn <= 1: n = nn - 1 if isprime(n): return n n -= 4 else: n = nn + 1 while 1: if isprime(n): return n n -= 2 if isprime(n): return n n -= 4 def primerange(a, b): """ Generate a list of all prime numbers in the range [a, b). Some famous conjectures about the occurence of primes in a given range are [1]: - Twin primes: though often not, the following will give 2 primes an oo number of times: primerange(6*n - 1, 6*n + 2) - Legendre's: the following always yields at least one prime primerange(n**2, (n+1)**2+1) - Bertrand's (proven): there is always a prime in the range primerange(n, 2*n) - Brocard's: there are at least four primes in the range primerange(prime(n)**2, prime(n+1)**2) The average gap between primes is log(n) [2]; the gap between primes can be arbitrarily large since sequences of composite numbers are arbitrarily large, e.g. the numbers in the sequence n!+2, n!+3 ... n!+n are all composite. References: [1] http://en.wikipedia.org/wiki/Prime_number [2] http://primes.utm.edu/notes/gaps.html """ assert a <= b a -= 1 while 1: a = nextprime(a) if a < b: yield a else: return def randprime(a, b): """ Return a random prime number in the range [a, b). Bertrand's postulate assures that randprime(a, 2*a) will always succeed for a > 1. Reference: http://en.wikipedia.org/wiki/Bertrand's_postulate """ n = random.randint(a-1, b) p = nextprime(n) if p >= b: p = prevprime(b) if p < a: raise ValueError("no primes exist in the specified range") return p def primorial(n, nth=True): """ Returns the product of either a) the first n primes (default) or b) the primes less than or equal to n (when `nth`=False). >>> from sympy.ntheory.generate import primorial, randprime, primerange >>> from sympy import factorint, Mul, primefactors >>> primorial(4) # the first 4 primes are 2, 3, 5, 7 210 >>> primorial(4, nth=0) # primes <= 4 are 2 and 3 6 >>> primorial(1) 2 >>> primorial(1, nth=0) 1 One can argue that the primes are infinite since if you take a set of primes and multiply them together (e.g. the primorial) and then add or subtract 1, the result cannot be divided by any of the original factors, hence either 1 or more primes must divide this product of primes. >>> factorint(primorial(4) + 1) {211: 1} >>> factorint(primorial(4) - 1) {11: 1, 19: 1} >>> p = list(primerange(10, 20)) >>> sorted(set(primefactors(Mul(*p) + 1)).difference(set(p))) [2, 5, 31, 149] """ if n < 1: raise ValueError("primorial argument must be >= 1") p = 1 if nth: for i in range(1, n + 1): p *= prime(i) else: for i in primerange(2, n + 1): p *= i return p def cycle_length(f, x0, nmax=None, values=False): """For a given iterated sequence, return a generator that gives the length of the iterated cycle (lambda) and the length of terms before the cycle begins (mu); if ``values`` is True then the terms of the sequence will be returned instead. Note: more than the first lambda + mu terms may be returned and this is the cost of cycle detection with Brent's method; there are, however, generally less terms calculated than would have been calculated if the proper ending point were determined, e.g. by using Floyd's method. >>> from sympy.ntheory.generate import cycle_length >>> from random import Random This will yield successive values of i <-- func(i): >>> def iter(func, i): ... while 1: ... ii = func(i) ... yield ii ... i = ii ... A function is defined: >>> func = lambda i: (i**2 + 1) % 51 and given a seed of 2 and the mu and lambda terms calculated: >>> cycle_length(func, 4).next() (6, 2) We can see what is meant by looking at the output: >>> n = cycle_length(func, 4, values=True) >>> list(ni for ni in n) [17, 35, 2, 5, 26, 14, 44, 50, 2, 5, 26, 14] \_______________/ 6 values after the first 2 If a sequence is suspected of being longer than you might wish, ``nmax`` can be used to exit early (in which mu will be returned as None: >>> cycle_length(func, 4, nmax = 4).next() (4, None) >>> [ni for ni in cycle_length(func, 4, nmax = 4, values=True)] [17, 35, 2, 5] Code modified from: http://en.wikipedia.org/wiki/Cycle_detection. """ # main phase: search successive powers of two power = lam = 1 tortoise, hare = x0, f(x0) # f(x0) is the element/node next to x0. i = 0 while tortoise != hare and (not nmax or i < nmax): i += 1 if power == lam: # time to start a new power of two? tortoise = hare power *= 2 lam = 0 if values: yield hare hare = f(hare) lam += 1 if nmax and i == nmax: if values: return else: yield nmax, None return if not values: # Find the position of the first repetition of length lambda mu = 0 tortoise = hare = x0 for i in range(lam): hare = f(hare) while tortoise != hare: tortoise = f(tortoise) hare = f(hare) mu += 1 if mu: mu -= 1 yield lam, mu wxgeometrie-0.133.2.orig/wxgeometrie/sympy/ntheory/__init__.py0000644000175000017500000000110512014170666024675 0ustar georgeskgeorgesk""" Number theory module (primes, etc) """ from generate import nextprime, prevprime, prime, primepi, primerange,\ randprime, Sieve, sieve, primorial, cycle_length from primetest import isprime from factor_ import divisors, factorint, multiplicity, perfect_power,\ pollard_pm1, pollard_rho, primefactors, totient, trailing, divisor_count from partitions_ import npartitions from residue_ntheory import is_primitive_root, is_quad_residue, \ legendre_symbol, n_order, totient_ from multinomial import binomial_coefficients, binomial_coefficients_list,\ multinomial_coefficients wxgeometrie-0.133.2.orig/wxgeometrie/sympy/statistics/0000755000175000017500000000000012014170666023271 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/statistics/distributions.py0000644000175000017500000002562512014170666026557 0ustar georgeskgeorgeskfrom sympy.core import sympify, Lambda, Dummy, Integer, Rational, oo, Float, pi from sympy.functions import sqrt, exp, erf from sympy.printing import sstr import random class Sample(tuple): """ Sample([x1, x2, x3, ...]) represents a collection of samples. Sample parameters like mean, variance and stddev can be accessed as properties. The sample will be sorted. """ def __new__(cls, sample): s = tuple.__new__(cls, sorted(sample)) s.mean = mean = sum(s) / Integer(len(s)) s.variance = sum([(x-mean)**2 for x in s]) / Integer(len(s)) s.stddev = sqrt(s.variance) if len(s) % 2: s.median = s[len(s)//2] else: s.median = sum(s[len(s)//2-1:len(s)//2+1]) / Integer(2) return s def __repr__(self): return sstr(self) def __str__(self): return sstr(self) class ContinuousProbability(object): """Base class for continuous probability distributions""" def probability(s, a, b): """Calculate the probability that a random number x generated from the distribution satisfies a <= x <= b """ return s.cdf(b) - s.cdf(a) def random(s, n=None): """ random() -- generate a random number from the distribution. random(n) -- generate a Sample of n random numbers. """ if n is None: return s._random() else: return Sample([s._random() for i in xrange(n)]) def __repr__(self): return sstr(self) def __str__(self): return sstr(self) class Normal(ContinuousProbability): """ Normal(mu, sigma) represents the normal or Gaussian distribution with mean value mu and standard deviation sigma. Example usage: >>> from sympy.statistics import Normal >>> from sympy import oo >>> N = Normal(1, 2) >>> N.mean 1 >>> N.variance 4 >>> N.probability(-oo, 1) # probability on an interval 1/2 >>> N.probability(1, oo) 1/2 >>> N.probability(-oo, oo) 1 >>> N.probability(-1, 3) erf(2**(1/2)/2) >>> _.evalf() 0.682689492137086 """ def __init__(self, mu, sigma): self.mu = sympify(mu) self.sigma = sympify(sigma) mean = property(lambda s: s.mu) median = property(lambda s: s.mu) mode = property(lambda s: s.mu) stddev = property(lambda s: s.sigma) variance = property(lambda s: s.sigma**2) def pdf(s, x): """Return the probability density function as an expression in x""" x = sympify(x) return 1/(s.sigma*sqrt(2*pi)) * exp(-(x-s.mu)**2 / (2*s.sigma**2)) def cdf(s, x): """Return the cumulative density function as an expression in x""" x = sympify(x) return (1+erf((x-s.mu)/(s.sigma*sqrt(2))))/2 def _random(s): return random.gauss(float(s.mu), float(s.sigma)) def confidence(s, p): """Return a symmetric (p*100)% confidence interval. For example, p=0.95 gives a 95% confidence interval. Currently this function only handles numerical values except in the trivial case p=1. Examples usage: # One standard deviation >>> from sympy.statistics import Normal >>> N = Normal(0, 1) >>> N.confidence(0.68) (-0.994457883209753, 0.994457883209753) >>> N.probability(*_).evalf() 0.680000000000000 # Two standard deviations >>> N = Normal(0, 1) >>> N.confidence(0.95) (-1.95996398454005, 1.95996398454005) >>> N.probability(*_).evalf() 0.950000000000000 """ if p == 1: return (-oo, oo) assert p <= 1 # In terms of n*sigma, we have n = sqrt(2)*ierf(p). The inverse # error function is not yet implemented in SymPy but can easily be # computed numerically from sympy.mpmath import mpf, erfinv # calculate y = ierf(p) by solving erf(y) - p = 0 y = erfinv(mpf(p)) t = Float(str(mpf(float(s.sigma)) * mpf(2)**0.5 * y)) mu = s.mu.evalf() return (mu-t, mu+t) @staticmethod def fit(sample): """Create a normal distribution fit to the mean and standard deviation of the given distribution or sample.""" if not hasattr(sample, "stddev"): sample = Sample(sample) return Normal(sample.mean, sample.stddev) class Uniform(ContinuousProbability): """ Uniform(a, b) represents a probability distribution with uniform probability density on the interval [a, b] and zero density everywhere else. """ def __init__(self, a, b): self.a = sympify(a) self.b = sympify(b) mean = property(lambda s: (s.a+s.b)/2) median = property(lambda s: (s.a+s.b)/2) mode = property(lambda s: (s.a+s.b)/2) # arbitrary variance = property(lambda s: (s.b-s.a)**2 / 12) stddev = property(lambda s: sqrt(s.variance)) def pdf(s, x): """Return the probability density function as an expression in x""" x = sympify(x) if not x.is_Number: raise NotImplementedError("SymPy does not yet support" "piecewise functions") if x < s.a or x > s.b: return Rational(0) return 1/(s.b-s.a) def cdf(s, x): """Return the cumulative density function as an expression in x""" x = sympify(x) if not x.is_Number: raise NotImplementedError("SymPy does not yet support" "piecewise functions") if x <= s.a: return Rational(0) if x >= s.b: return Rational(1) return (x-s.a)/(s.b-s.a) def _random(s): return Float(random.uniform(float(s.a), float(s.b))) def confidence(s, p): """Generate a symmetric (p*100)% confidence interval. >>> from sympy import Rational >>> from sympy.statistics import Uniform >>> U = Uniform(1, 2) >>> U.confidence(1) (1, 2) >>> U.confidence(Rational(1,2)) (5/4, 7/4) """ p = sympify(p) assert p <= 1 d = (s.b-s.a)*p / 2 return (s.mean - d, s.mean + d) @staticmethod def fit(sample): """Create a uniform distribution fit to the mean and standard deviation of the given distribution or sample.""" if not hasattr(sample, "stddev"): sample = Sample(sample) m = sample.mean d = sqrt(12*sample.variance)/2 return Uniform(m-d, m+d) class PDF(ContinuousProbability): """ PDF(func, (x, a, b)) represents continuous probability distribution with probability distribution function func(x) on interval (a, b) If func is not normalized so that integrate(func, (x, a, b)) == 1, it can be normalized using PDF.normalize() method Example usage: >>> from sympy import Symbol, exp, oo >>> from sympy.statistics.distributions import PDF >>> from sympy.abc import x >>> a = Symbol('a', positive=True) >>> exponential = PDF(exp(-x/a)/a, (x,0,oo)) >>> exponential.pdf(x) exp(-x/a)/a >>> exponential.cdf(x) 1 - exp(-x/a) >>> exponential.mean a >>> exponential.variance a**2 """ def __init__(self, pdf, var): #XXX maybe add some checking of parameters if isinstance(var, (tuple, list)): self.pdf = Lambda(var[0], pdf) self.domain = tuple(var[1:]) else: self.pdf = Lambda(var, pdf) self.domain = (-oo, oo) self._cdf = None self._mean = None self._variance = None self._stddev = None def normalize(self): """ Normalize the probability distribution function so that integrate(self.pdf(x), (x, a, b)) == 1 Example usage: >>> from sympy import Symbol, exp, oo >>> from sympy.statistics.distributions import PDF >>> from sympy.abc import x >>> a = Symbol('a', positive=True) >>> exponential = PDF(exp(-x/a), (x,0,oo)) >>> exponential.normalize().pdf(x) exp(-x/a)/a """ norm = self.probability(*self.domain) if norm != 1: w = Dummy('w', real=True) return self.__class__(self.pdf(w)/norm, (w, self.domain[0], self.domain[1])) #self._cdf = Lambda(w, (self.cdf(w) - self.cdf(self.domain[0]))/norm) #if self._mean is not None: # self._mean /= norm #if self._variance is not None: # self._variance = (self._variance + (self._mean*norm)**2)/norm - self.mean**2 #if self._stddev is not None: # self._stddev = sqrt(self._variance) else: return self def cdf(self, x): x = sympify(x) if self._cdf is not None: return self._cdf(x) else: from sympy import integrate w = Dummy('w', real=True) self._cdf = integrate(self.pdf(w), w) self._cdf = Lambda(w, self._cdf - self._cdf.subs(w, self.domain[0])) return self._cdf(x) def _get_mean(self): if self._mean is not None: return self._mean else: from sympy import integrate w = Dummy('w', real=True) self._mean = integrate(self.pdf(w)*w,(w,self.domain[0],self.domain[1])) return self._mean def _get_variance(self): if self._variance is not None: return self._variance else: from sympy import integrate, simplify w = Dummy('w', real=True) self._variance = integrate(self.pdf(w)*w**2,(w,self.domain[0],self.domain[1])) - self.mean**2 self._variance = simplify(self._variance) return self._variance def _get_stddev(self): if self._stddev is not None: return self._stddev else: self._stddev = sqrt(self.variance) return self._stddev mean = property(_get_mean) variance = property(_get_variance) stddev = property(_get_stddev) def _random(s): raise NotImplementedError def transform(self,func,var): """Return a probability distribution of random variable func(x) currently only some simple injective functions are supported""" w = Dummy('w', real=True) from sympy import solve inverse = solve(func-w, var) newPdf = S.Zero funcdiff = func.diff(var) #TODO check if x is in domain for x in inverse: # this assignment holds only for x in domain # in general it would require implementing # piecewise defined functions in sympy newPdf += (self.pdf(var)/abs(funcdiff)).subs(var,x) return PDF(newPdf, (w, func.subs(var, self.domain[0]), func.subs(var, self.domain[1]))) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/statistics/tests/0000755000175000017500000000000012014170666024433 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/statistics/tests/test_statistics.py0000644000175000017500000000425212014170666030241 0ustar georgeskgeorgeskfrom sympy import sqrt, Rational, oo, Symbol, exp, pi from sympy.functions import erf from sympy.statistics import Normal, Uniform from sympy.statistics.distributions import PDF from operator import abs from sympy.mpmath import mp def test_normal(): dps, mp.dps = mp.dps, 20 N = Normal(0, 1) assert N.mean == 0 assert N.variance == 1 assert N.probability(-1, 1) == erf(1/sqrt(2)) assert N.probability(-1, 0) == erf(1/sqrt(2))/2 N = Normal(2, 4) assert N.mean == 2 assert N.variance == 16 assert N.confidence(1) == (-oo, oo) assert N.probability(1, 3) == erf(1/sqrt(32)) assert N.pdf(1).evalf() == (exp(Rational(-1,32)) / (4*sqrt(2*pi))).evalf() for p in [0.1, 0.3, 0.7, 0.9, 0.995]: a, b = N.confidence(p) assert abs(float(N.probability(a, b).evalf()) - p) < 1e-10 N = Normal(0, 2/sqrt(2*pi)) assert N.pdf(0) == Rational(1,2) mp.dps = dps def test_uniform(): U = Uniform(-3, -1) assert U.mean == -2 assert U.confidence(1) == (-3, -1) assert U.confidence(Rational(1,2)) == (Rational(-5,2), Rational(-3,2)) assert U.pdf(-4) == 0 assert U.pdf(-Rational(3,2)) == Rational(1,2) assert U.pdf(0) == 0 assert U.cdf(-4) == 0 assert U.cdf(-Rational(3,2)) == Rational(3,4) assert U.cdf(0) == 1 def test_fit(): import random random.seed(1234) n = Normal.fit(Uniform.fit(Normal(2, 1.5).random(1000))) #print n.mean #print n.stddev assert abs(n.mean - 2) < 0.3 assert abs(n.stddev - 1.5) < 0.3 def test_sample(): from sympy.statistics.distributions import Sample s = Sample([0,1]) assert s.mean == Rational(1,2) assert s.median == Rational(1,2) s = Sample([4,2,3]) assert s == Sample([2, 3, 4]) assert s.median == 3 s = Sample([4,2,3,1]) assert s.median == Rational(5,2) def test_PDF(): a = Symbol('a', positive=True) x = Symbol('x', real=True) exponential = PDF(exp(-x/a), (x,0,oo)) exponential = exponential.normalize() assert exponential.pdf(x) == 1/a*exp(-x/a) assert exponential.cdf(x) == 1 - exp(-x/a) assert exponential.mean == a assert exponential.variance == a**2 assert exponential.stddev == a wxgeometrie-0.133.2.orig/wxgeometrie/sympy/statistics/__init__.py0000644000175000017500000000011312014170666025375 0ustar georgeskgeorgesk""" SymPy statistics module """ from distributions import Normal, Uniform wxgeometrie-0.133.2.orig/wxgeometrie/sympy/conftest.py0000644000175000017500000000043112014170666023274 0ustar georgeskgeorgesk# The py library is part of the "py.test" testing suite (python-codespeak-lib on # Debian), see http://codespeak.net/py/ import py #this makes py.test put sympy directory into the sys.path, so that we can #"import sympy" from tests nicely rootdir = py.magic.autopath().dirpath() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/0000755000175000017500000000000012014170666022365 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/matrices/0000755000175000017500000000000012014170666024174 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/matrices/linalg.py0000644000175000017500000004553412014170666026027 0ustar georgeskgeorgesk""" Linear algebra -------------- Linear equations ................ Basic linear algebra is implemented; you can for example solve the linear equation system:: x + 2*y = -10 3*x + 4*y = 10 using ``lu_solve``:: >>> A = matrix([[1, 2], [3, 4]]) >>> b = matrix([-10, 10]) >>> x = lu_solve(A, b) >>> x matrix( [['30.0'], ['-20.0']]) If you don't trust the result, use ``residual`` to calculate the residual ||A*x-b||:: >>> residual(A, x, b) matrix( [['3.46944695195361e-18'], ['3.46944695195361e-18']]) >>> str(eps) '2.22044604925031e-16' As you can see, the solution is quite accurate. The error is caused by the inaccuracy of the internal floating point arithmetic. Though, it's even smaller than the current machine epsilon, which basically means you can trust the result. If you need more speed, use NumPy. Or choose a faster data type using the keyword ``force_type``:: >>> lu_solve(A, b, force_type=float) matrix( [[29.999999999999996], [-19.999999999999996]]) ``lu_solve`` accepts overdetermined systems. It is usually not possible to solve such systems, so the residual is minimized instead. Internally this is done using Cholesky decomposition to compute a least squares approximation. This means that that ``lu_solve`` will square the errors. If you can't afford this, use ``qr_solve`` instead. It is twice as slow but more accurate, and it calculates the residual automatically. Matrix factorization .................... The function ``lu`` computes an explicit LU factorization of a matrix:: >>> P, L, U = lu(matrix([[0,2,3],[4,5,6],[7,8,9]])) >>> print P [0.0 0.0 1.0] [1.0 0.0 0.0] [0.0 1.0 0.0] >>> print L [ 1.0 0.0 0.0] [ 0.0 1.0 0.0] [0.571428571428571 0.214285714285714 1.0] >>> print U [7.0 8.0 9.0] [0.0 2.0 3.0] [0.0 0.0 0.214285714285714] >>> print P.T*L*U [0.0 2.0 3.0] [4.0 5.0 6.0] [7.0 8.0 9.0] Interval matrices ----------------- Matrices may contain interval elements. This allows one to perform basic linear algebra operations such as matrix multiplication and equation solving with rigorous error bounds:: >>> a = matrix([['0.1','0.3','1.0'], ... ['7.1','5.5','4.8'], ... ['3.2','4.4','5.6']], force_type=mpi) >>> >>> b = matrix(['4','0.6','0.5'], force_type=mpi) >>> c = lu_solve(a, b) >>> c matrix( [[[5.2582327113062393041, 5.2582327113062749951]], [[-13.155049396267856583, -13.155049396267821167]], [[7.4206915477497212555, 7.4206915477497310922]]]) >>> print a*c [ [3.9999999999999866773, 4.0000000000000133227]] [[0.59999999999972430942, 0.60000000000027142733]] [[0.49999999999982236432, 0.50000000000018474111]] """ # TODO: # *implement high-level qr() # *test unitvector # *iterative solving from copy import copy from ..libmp.backend import xrange class LinearAlgebraMethods(object): def LU_decomp(ctx, A, overwrite=False, use_cache=True): """ LU-factorization of a n*n matrix using the Gauss algorithm. Returns L and U in one matrix and the pivot indices. Use overwrite to specify whether A will be overwritten with L and U. """ if not A.rows == A.cols: raise ValueError('need n*n matrix') # get from cache if possible if use_cache and isinstance(A, ctx.matrix) and A._LU: return A._LU if not overwrite: orig = A A = A.copy() tol = ctx.absmin(ctx.mnorm(A,1) * ctx.eps) # each pivot element has to be bigger n = A.rows p = [None]*(n - 1) for j in xrange(n - 1): # pivoting, choose max(abs(reciprocal row sum)*abs(pivot element)) biggest = 0 for k in xrange(j, n): s = ctx.fsum([ctx.absmin(A[k,l]) for l in xrange(j, n)]) if ctx.absmin(s) <= tol: raise ZeroDivisionError('matrix is numerically singular') current = 1/s * ctx.absmin(A[k,j]) if current > biggest: # TODO: what if equal? biggest = current p[j] = k # swap rows according to p ctx.swap_row(A, j, p[j]) if ctx.absmin(A[j,j]) <= tol: raise ZeroDivisionError('matrix is numerically singular') # calculate elimination factors and add rows for i in xrange(j + 1, n): A[i,j] /= A[j,j] for k in xrange(j + 1, n): A[i,k] -= A[i,j]*A[j,k] if ctx.absmin(A[n - 1,n - 1]) <= tol: raise ZeroDivisionError('matrix is numerically singular') # cache decomposition if not overwrite and isinstance(orig, ctx.matrix): orig._LU = (A, p) return A, p def L_solve(ctx, L, b, p=None): """ Solve the lower part of a LU factorized matrix for y. """ assert L.rows == L.cols, 'need n*n matrix' n = L.rows assert len(b) == n b = copy(b) if p: # swap b according to p for k in xrange(0, len(p)): ctx.swap_row(b, k, p[k]) # solve for i in xrange(1, n): for j in xrange(i): b[i] -= L[i,j] * b[j] return b def U_solve(ctx, U, y): """ Solve the upper part of a LU factorized matrix for x. """ assert U.rows == U.cols, 'need n*n matrix' n = U.rows assert len(y) == n x = copy(y) for i in xrange(n - 1, -1, -1): for j in xrange(i + 1, n): x[i] -= U[i,j] * x[j] x[i] /= U[i,i] return x def lu_solve(ctx, A, b, **kwargs): """ Ax = b => x Solve a determined or overdetermined linear equations system. Fast LU decomposition is used, which is less accurate than QR decomposition (especially for overdetermined systems), but it's twice as efficient. Use qr_solve if you want more precision or have to solve a very ill- conditioned system. If you specify real=True, it does not check for overdeterminded complex systems. """ prec = ctx.prec try: ctx.prec += 10 # do not overwrite A nor b A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() if A.rows < A.cols: raise ValueError('cannot solve underdetermined system') if A.rows > A.cols: # use least-squares method if overdetermined # (this increases errors) AH = A.H A = AH * A b = AH * b if (kwargs.get('real', False) or not sum(type(i) is ctx.mpc for i in A)): # TODO: necessary to check also b? x = ctx.cholesky_solve(A, b) else: x = ctx.lu_solve(A, b) else: # LU factorization A, p = ctx.LU_decomp(A) b = ctx.L_solve(A, b, p) x = ctx.U_solve(A, b) finally: ctx.prec = prec return x def improve_solution(ctx, A, x, b, maxsteps=1): """ Improve a solution to a linear equation system iteratively. This re-uses the LU decomposition and is thus cheap. Usually 3 up to 4 iterations are giving the maximal improvement. """ assert A.rows == A.cols, 'need n*n matrix' # TODO: really? for _ in xrange(maxsteps): r = ctx.residual(A, x, b) if ctx.norm(r, 2) < 10*ctx.eps: break # this uses cached LU decomposition and is thus cheap dx = ctx.lu_solve(A, -r) x += dx return x def lu(ctx, A): """ A -> P, L, U LU factorisation of a square matrix A. L is the lower, U the upper part. P is the permutation matrix indicating the row swaps. P*A = L*U If you need efficiency, use the low-level method LU_decomp instead, it's much more memory efficient. """ # get factorization A, p = ctx.LU_decomp(A) n = A.rows L = ctx.matrix(n) U = ctx.matrix(n) for i in xrange(n): for j in xrange(n): if i > j: L[i,j] = A[i,j] elif i == j: L[i,j] = 1 U[i,j] = A[i,j] else: U[i,j] = A[i,j] # calculate permutation matrix P = ctx.eye(n) for k in xrange(len(p)): ctx.swap_row(P, k, p[k]) return P, L, U def unitvector(ctx, n, i): """ Return the i-th n-dimensional unit vector. """ assert 0 < i <= n, 'this unit vector does not exist' return [ctx.zero]*(i-1) + [ctx.one] + [ctx.zero]*(n-i) def inverse(ctx, A, **kwargs): """ Calculate the inverse of a matrix. If you want to solve an equation system Ax = b, it's recommended to use solve(A, b) instead, it's about 3 times more efficient. """ prec = ctx.prec try: ctx.prec += 10 # do not overwrite A A = ctx.matrix(A, **kwargs).copy() n = A.rows # get LU factorisation A, p = ctx.LU_decomp(A) cols = [] # calculate unit vectors and solve corresponding system to get columns for i in xrange(1, n + 1): e = ctx.unitvector(n, i) y = ctx.L_solve(A, e, p) cols.append(ctx.U_solve(A, y)) # convert columns to matrix inv = [] for i in xrange(n): row = [] for j in xrange(n): row.append(cols[j][i]) inv.append(row) result = ctx.matrix(inv, **kwargs) finally: ctx.prec = prec return result def householder(ctx, A): """ (A|b) -> H, p, x, res (A|b) is the coefficient matrix with left hand side of an optionally overdetermined linear equation system. H and p contain all information about the transformation matrices. x is the solution, res the residual. """ assert isinstance(A, ctx.matrix) m = A.rows n = A.cols assert m >= n - 1 # calculate Householder matrix p = [] for j in xrange(0, n - 1): s = ctx.fsum((A[i,j])**2 for i in xrange(j, m)) if not abs(s) > ctx.eps: raise ValueError('matrix is numerically singular') p.append(-ctx.sign(A[j,j]) * ctx.sqrt(s)) kappa = ctx.one / (s - p[j] * A[j,j]) A[j,j] -= p[j] for k in xrange(j+1, n): y = ctx.fsum(A[i,j] * A[i,k] for i in xrange(j, m)) * kappa for i in xrange(j, m): A[i,k] -= A[i,j] * y # solve Rx = c1 x = [A[i,n - 1] for i in xrange(n - 1)] for i in xrange(n - 2, -1, -1): x[i] -= ctx.fsum(A[i,j] * x[j] for j in xrange(i + 1, n - 1)) x[i] /= p[i] # calculate residual if not m == n - 1: r = [A[m-1-i, n-1] for i in xrange(m - n + 1)] else: # determined system, residual should be 0 r = [0]*m # maybe a bad idea, changing r[i] will change all elements return A, p, x, r #def qr(ctx, A): # """ # A -> Q, R # # QR factorisation of a square matrix A using Householder decomposition. # Q is orthogonal, this leads to very few numerical errors. # # A = Q*R # """ # H, p, x, res = householder(A) # TODO: implement this def residual(ctx, A, x, b, **kwargs): """ Calculate the residual of a solution to a linear equation system. r = A*x - b for A*x = b """ oldprec = ctx.prec try: ctx.prec *= 2 A, x, b = ctx.matrix(A, **kwargs), ctx.matrix(x, **kwargs), ctx.matrix(b, **kwargs) return A*x - b finally: ctx.prec = oldprec def qr_solve(ctx, A, b, norm=None, **kwargs): """ Ax = b => x, ||Ax - b|| Solve a determined or overdetermined linear equations system and calculate the norm of the residual (error). QR decomposition using Householder factorization is applied, which gives very accurate results even for ill-conditioned matrices. qr_solve is twice as efficient. """ if norm is None: norm = ctx.norm prec = ctx.prec try: ctx.prec += 10 # do not overwrite A nor b A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() if A.rows < A.cols: raise ValueError('cannot solve underdetermined system') H, p, x, r = ctx.householder(ctx.extend(A, b)) res = ctx.norm(r) # calculate residual "manually" for determined systems if res == 0: res = ctx.norm(ctx.residual(A, x, b)) return ctx.matrix(x, **kwargs), res finally: ctx.prec = prec def cholesky(ctx, A, tol=None): r""" Cholesky decomposition of a symmetric positive-definite matrix `A`. Returns a lower triangular matrix `L` such that `A = L \times L^T`. More generally, for a complex Hermitian positive-definite matrix, a Cholesky decomposition satisfying `A = L \times L^H` is returned. The Cholesky decomposition can be used to solve linear equation systems twice as efficiently as LU decomposition, or to test whether `A` is positive-definite. The optional parameter ``tol`` determines the tolerance for verifying positive-definiteness. **Examples** Cholesky decomposition of a positive-definite symmetric matrix:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> A = eye(3) + hilbert(3) >>> nprint(A) [ 2.0 0.5 0.333333] [ 0.5 1.33333 0.25] [0.333333 0.25 1.2] >>> L = cholesky(A) >>> nprint(L) [ 1.41421 0.0 0.0] [0.353553 1.09924 0.0] [0.235702 0.15162 1.05899] >>> chop(A - L*L.T) [0.0 0.0 0.0] [0.0 0.0 0.0] [0.0 0.0 0.0] Cholesky decomposition of a Hermitian matrix:: >>> A = eye(3) + matrix([[0,0.25j,-0.5j],[-0.25j,0,0],[0.5j,0,0]]) >>> L = cholesky(A) >>> nprint(L) [ 1.0 0.0 0.0] [(0.0 - 0.25j) (0.968246 + 0.0j) 0.0] [ (0.0 + 0.5j) (0.129099 + 0.0j) (0.856349 + 0.0j)] >>> chop(A - L*L.H) [0.0 0.0 0.0] [0.0 0.0 0.0] [0.0 0.0 0.0] Attempted Cholesky decomposition of a matrix that is not positive definite:: >>> A = -eye(3) + hilbert(3) >>> L = cholesky(A) Traceback (most recent call last): ... ValueError: matrix is not positive-definite **References** 1. [Wikipedia]_ http://en.wikipedia.org/wiki/Cholesky_decomposition """ assert isinstance(A, ctx.matrix) if not A.rows == A.cols: raise ValueError('need n*n matrix') if tol is None: tol = +ctx.eps n = A.rows L = ctx.matrix(n) for j in xrange(n): c = ctx.re(A[j,j]) if abs(c-A[j,j]) > tol: raise ValueError('matrix is not Hermitian') s = c - ctx.fsum((L[j,k] for k in xrange(j)), absolute=True, squared=True) if s < tol: raise ValueError('matrix is not positive-definite') L[j,j] = ctx.sqrt(s) for i in xrange(j, n): it1 = (L[i,k] for k in xrange(j)) it2 = (L[j,k] for k in xrange(j)) t = ctx.fdot(it1, it2, conjugate=True) L[i,j] = (A[i,j] - t) / L[j,j] return L def cholesky_solve(ctx, A, b, **kwargs): """ Ax = b => x Solve a symmetric positive-definite linear equation system. This is twice as efficient as lu_solve. Typical use cases: * A.T*A * Hessian matrix * differential equations """ prec = ctx.prec try: ctx.prec += 10 # do not overwrite A nor b A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() if A.rows != A.cols: raise ValueError('can only solve determined system') # Cholesky factorization L = ctx.cholesky(A) # solve n = L.rows assert len(b) == n for i in xrange(n): b[i] -= ctx.fsum(L[i,j] * b[j] for j in xrange(i)) b[i] /= L[i,i] x = ctx.U_solve(L.T, b) return x finally: ctx.prec = prec def det(ctx, A): """ Calculate the determinant of a matrix. """ prec = ctx.prec try: # do not overwrite A A = ctx.matrix(A).copy() # use LU factorization to calculate determinant try: R, p = ctx.LU_decomp(A) except ZeroDivisionError: return 0 z = 1 for i, e in enumerate(p): if i != e: z *= -1 for i in xrange(A.rows): z *= R[i,i] return z finally: ctx.prec = prec def cond(ctx, A, norm=None): """ Calculate the condition number of a matrix using a specified matrix norm. The condition number estimates the sensitivity of a matrix to errors. Example: small input errors for ill-conditioned coefficient matrices alter the solution of the system dramatically. For ill-conditioned matrices it's recommended to use qr_solve() instead of lu_solve(). This does not help with input errors however, it just avoids to add additional errors. Definition: cond(A) = ||A|| * ||A**-1|| """ if norm is None: norm = lambda x: ctx.mnorm(x,1) return norm(A) * norm(ctx.inverse(A)) def lu_solve_mat(ctx, a, b): """Solve a * x = b where a and b are matrices.""" r = ctx.matrix(a.rows, b.cols) for i in range(b.cols): c = ctx.lu_solve(a, b.column(i)) for j in range(len(c)): r[j, i] = c[j] return r wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/matrices/calculus.py0000644000175000017500000004374612014170666026377 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange # TODO: should use diagonalization-based algorithms class MatrixCalculusMethods: def _exp_pade(ctx, a): """ Exponential of a matrix using Pade approximants. See G. H. Golub, C. F. van Loan 'Matrix Computations', third Ed., page 572 TODO: - find a good estimate for q - reduce the number of matrix multiplications to improve performance """ def eps_pade(p): return ctx.mpf(2)**(3-2*p) * \ ctx.factorial(p)**2/(ctx.factorial(2*p)**2 * (2*p + 1)) q = 4 extraq = 8 while 1: if eps_pade(q) < ctx.eps: break q += 1 q += extraq j = int(max(1, ctx.mag(ctx.mnorm(a,'inf')))) extra = q prec = ctx.prec ctx.dps += extra + 3 try: a = a/2**j na = a.rows den = ctx.eye(na) num = ctx.eye(na) x = ctx.eye(na) c = ctx.mpf(1) for k in range(1, q+1): c *= ctx.mpf(q - k + 1)/((2*q - k + 1) * k) x = a*x cx = c*x num += cx den += (-1)**k * cx f = ctx.lu_solve_mat(den, num) for k in range(j): f = f*f finally: ctx.prec = prec return f*1 def expm(ctx, A, method='taylor'): r""" Computes the matrix exponential of a square matrix `A`, which is defined by the power series .. math :: \exp(A) = I + A + \frac{A^2}{2!} + \frac{A^3}{3!} + \ldots With method='taylor', the matrix exponential is computed using the Taylor series. With method='pade', Pade approximants are used instead. **Examples** Basic examples:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> expm(zeros(3)) [1.0 0.0 0.0] [0.0 1.0 0.0] [0.0 0.0 1.0] >>> expm(eye(3)) [2.71828182845905 0.0 0.0] [ 0.0 2.71828182845905 0.0] [ 0.0 0.0 2.71828182845905] >>> expm([[1,1,0],[1,0,1],[0,1,0]]) [ 3.86814500615414 2.26812870852145 0.841130841230196] [ 2.26812870852145 2.44114713886289 1.42699786729125] [0.841130841230196 1.42699786729125 1.6000162976327] >>> expm([[1,1,0],[1,0,1],[0,1,0]], method='pade') [ 3.86814500615414 2.26812870852145 0.841130841230196] [ 2.26812870852145 2.44114713886289 1.42699786729125] [0.841130841230196 1.42699786729125 1.6000162976327] >>> expm([[1+j, 0], [1+j,1]]) [(1.46869393991589 + 2.28735528717884j) 0.0] [ (1.03776739863568 + 3.536943175722j) (2.71828182845905 + 0.0j)] Matrices with large entries are allowed:: >>> expm(matrix([[1,2],[2,3]])**25) [5.65024064048415e+2050488462815550 9.14228140091932e+2050488462815550] [9.14228140091932e+2050488462815550 1.47925220414035e+2050488462815551] The identity `\exp(A+B) = \exp(A) \exp(B)` does not hold for noncommuting matrices:: >>> A = hilbert(3) >>> B = A + eye(3) >>> chop(mnorm(A*B - B*A)) 0.0 >>> chop(mnorm(expm(A+B) - expm(A)*expm(B))) 0.0 >>> B = A + ones(3) >>> mnorm(A*B - B*A) 1.8 >>> mnorm(expm(A+B) - expm(A)*expm(B)) 42.0927851137247 """ A = ctx.matrix(A) if method == 'pade': return ctx._exp_pade(A) prec = ctx.prec j = int(max(1, ctx.mag(ctx.mnorm(A,'inf')))) j += int(0.5*prec**0.5) try: ctx.prec += 10 + 2*j tol = +ctx.eps A = A/2**j T = A Y = A**0 + A k = 2 while 1: T *= A * (1/ctx.mpf(k)) if ctx.mnorm(T, 'inf') < tol: break Y += T k += 1 for k in xrange(j): Y = Y*Y finally: ctx.prec = prec Y *= 1 return Y def cosm(ctx, A): r""" Gives the cosine of a square matrix `A`, defined in analogy with the matrix exponential. Examples:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> X = eye(3) >>> cosm(X) [0.54030230586814 0.0 0.0] [ 0.0 0.54030230586814 0.0] [ 0.0 0.0 0.54030230586814] >>> X = hilbert(3) >>> cosm(X) [ 0.424403834569555 -0.316643413047167 -0.221474945949293] [-0.316643413047167 0.820646708837824 -0.127183694770039] [-0.221474945949293 -0.127183694770039 0.909236687217541] >>> X = matrix([[1+j,-2],[0,-j]]) >>> cosm(X) [(0.833730025131149 - 0.988897705762865j) (1.07485840848393 - 0.17192140544213j)] [ 0.0 (1.54308063481524 + 0.0j)] """ B = 0.5 * (ctx.expm(A*ctx.j) + ctx.expm(A*(-ctx.j))) if not sum(A.apply(ctx.im).apply(abs)): B = B.apply(ctx.re) return B def sinm(ctx, A): r""" Gives the sine of a square matrix `A`, defined in analogy with the matrix exponential. Examples:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> X = eye(3) >>> sinm(X) [0.841470984807897 0.0 0.0] [ 0.0 0.841470984807897 0.0] [ 0.0 0.0 0.841470984807897] >>> X = hilbert(3) >>> sinm(X) [0.711608512150994 0.339783913247439 0.220742837314741] [0.339783913247439 0.244113865695532 0.187231271174372] [0.220742837314741 0.187231271174372 0.155816730769635] >>> X = matrix([[1+j,-2],[0,-j]]) >>> sinm(X) [(1.29845758141598 + 0.634963914784736j) (-1.96751511930922 + 0.314700021761367j)] [ 0.0 (0.0 - 1.1752011936438j)] """ B = (-0.5j) * (ctx.expm(A*ctx.j) - ctx.expm(A*(-ctx.j))) if not sum(A.apply(ctx.im).apply(abs)): B = B.apply(ctx.re) return B def _sqrtm_rot(ctx, A, _may_rotate): # If the iteration fails to converge, cheat by performing # a rotation by a complex number u = ctx.j**0.3 return ctx.sqrtm(u*A, _may_rotate) / ctx.sqrt(u) def sqrtm(ctx, A, _may_rotate=2): r""" Computes a square root of the square matrix `A`, i.e. returns a matrix `B = A^{1/2}` such that `B^2 = A`. The square root of a matrix, if it exists, is not unique. **Examples** Square roots of some simple matrices:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sqrtm([[1,0], [0,1]]) [1.0 0.0] [0.0 1.0] >>> sqrtm([[0,0], [0,0]]) [0.0 0.0] [0.0 0.0] >>> sqrtm([[2,0],[0,1]]) [1.4142135623731 0.0] [ 0.0 1.0] >>> sqrtm([[1,1],[1,0]]) [ (0.920442065259926 - 0.21728689675164j) (0.568864481005783 + 0.351577584254143j)] [(0.568864481005783 + 0.351577584254143j) (0.351577584254143 - 0.568864481005783j)] >>> sqrtm([[1,0],[0,1]]) [1.0 0.0] [0.0 1.0] >>> sqrtm([[-1,0],[0,1]]) [(0.0 - 1.0j) 0.0] [ 0.0 (1.0 + 0.0j)] >>> sqrtm([[j,0],[0,j]]) [(0.707106781186547 + 0.707106781186547j) 0.0] [ 0.0 (0.707106781186547 + 0.707106781186547j)] A square root of a rotation matrix, giving the corresponding half-angle rotation matrix:: >>> t1 = 0.75 >>> t2 = t1 * 0.5 >>> A1 = matrix([[cos(t1), -sin(t1)], [sin(t1), cos(t1)]]) >>> A2 = matrix([[cos(t2), -sin(t2)], [sin(t2), cos(t2)]]) >>> sqrtm(A1) [0.930507621912314 -0.366272529086048] [0.366272529086048 0.930507621912314] >>> A2 [0.930507621912314 -0.366272529086048] [0.366272529086048 0.930507621912314] The identity `(A^2)^{1/2} = A` does not necessarily hold:: >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) >>> sqrtm(A**2) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> sqrtm(A)**2 [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> A = matrix([[-4,1,4],[7,-8,9],[10,2,11]]) >>> sqrtm(A**2) [ 7.43715112194995 -0.324127569985474 1.8481718827526] [-0.251549715716942 9.32699765900402 2.48221180985147] [ 4.11609388833616 0.775751877098258 13.017955697342] >>> chop(sqrtm(A)**2) [-4.0 1.0 4.0] [ 7.0 -8.0 9.0] [10.0 2.0 11.0] For some matrices, a square root does not exist:: >>> sqrtm([[0,1], [0,0]]) Traceback (most recent call last): ... ZeroDivisionError: matrix is numerically singular Two examples from the documentation for Matlab's ``sqrtm``:: >>> mp.dps = 15; mp.pretty = True >>> sqrtm([[7,10],[15,22]]) [1.56669890360128 1.74077655955698] [2.61116483933547 4.17786374293675] >>> >>> X = matrix(\ ... [[5,-4,1,0,0], ... [-4,6,-4,1,0], ... [1,-4,6,-4,1], ... [0,1,-4,6,-4], ... [0,0,1,-4,5]]) >>> Y = matrix(\ ... [[2,-1,-0,-0,-0], ... [-1,2,-1,0,-0], ... [0,-1,2,-1,0], ... [-0,0,-1,2,-1], ... [-0,-0,-0,-1,2]]) >>> mnorm(sqrtm(X) - Y) 4.53155328326114e-19 """ A = ctx.matrix(A) # Trivial if A*0 == A: return A prec = ctx.prec if _may_rotate: d = ctx.det(A) if abs(ctx.im(d)) < 16*ctx.eps and ctx.re(d) < 0: return ctx._sqrtm_rot(A, _may_rotate-1) try: ctx.prec += 10 tol = ctx.eps * 128 Y = A Z = I = A**0 k = 0 # Denman-Beavers iteration while 1: Yprev = Y try: Y, Z = 0.5*(Y+ctx.inverse(Z)), 0.5*(Z+ctx.inverse(Y)) except ZeroDivisionError: if _may_rotate: Y = ctx._sqrtm_rot(A, _may_rotate-1) break else: raise mag1 = ctx.mnorm(Y-Yprev, 'inf') mag2 = ctx.mnorm(Y, 'inf') if mag1 <= mag2*tol: break if _may_rotate and k > 6 and not mag1 < mag2 * 0.001: return ctx._sqrtm_rot(A, _may_rotate-1) k += 1 if k > ctx.prec: raise ctx.NoConvergence finally: ctx.prec = prec Y *= 1 return Y def logm(ctx, A): r""" Computes a logarithm of the square matrix `A`, i.e. returns a matrix `B = \log(A)` such that `\exp(B) = A`. The logarithm of a matrix, if it exists, is not unique. **Examples** Logarithms of some simple matrices:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> X = eye(3) >>> logm(X) [0.0 0.0 0.0] [0.0 0.0 0.0] [0.0 0.0 0.0] >>> logm(2*X) [0.693147180559945 0.0 0.0] [ 0.0 0.693147180559945 0.0] [ 0.0 0.0 0.693147180559945] >>> logm(expm(X)) [1.0 0.0 0.0] [0.0 1.0 0.0] [0.0 0.0 1.0] A logarithm of a complex matrix:: >>> X = matrix([[2+j, 1, 3], [1-j, 1-2*j, 1], [-4, -5, j]]) >>> B = logm(X) >>> nprint(B) [ (0.808757 + 0.107759j) (2.20752 + 0.202762j) (1.07376 - 0.773874j)] [ (0.905709 - 0.107795j) (0.0287395 - 0.824993j) (0.111619 + 0.514272j)] [(-0.930151 + 0.399512j) (-2.06266 - 0.674397j) (0.791552 + 0.519839j)] >>> chop(expm(B)) [(2.0 + 1.0j) 1.0 3.0] [(1.0 - 1.0j) (1.0 - 2.0j) 1.0] [ -4.0 -5.0 (0.0 + 1.0j)] A matrix `X` close to the identity matrix, for which `\log(\exp(X)) = \exp(\log(X)) = X` holds:: >>> X = eye(3) + hilbert(3)/4 >>> X [ 1.25 0.125 0.0833333333333333] [ 0.125 1.08333333333333 0.0625] [0.0833333333333333 0.0625 1.05] >>> logm(expm(X)) [ 1.25 0.125 0.0833333333333333] [ 0.125 1.08333333333333 0.0625] [0.0833333333333333 0.0625 1.05] >>> expm(logm(X)) [ 1.25 0.125 0.0833333333333333] [ 0.125 1.08333333333333 0.0625] [0.0833333333333333 0.0625 1.05] A logarithm of a rotation matrix, giving back the angle of the rotation:: >>> t = 3.7 >>> A = matrix([[cos(t),sin(t)],[-sin(t),cos(t)]]) >>> chop(logm(A)) [ 0.0 -2.58318530717959] [2.58318530717959 0.0] >>> (2*pi-t) 2.58318530717959 For some matrices, a logarithm does not exist:: >>> logm([[1,0], [0,0]]) Traceback (most recent call last): ... ZeroDivisionError: matrix is numerically singular Logarithm of a matrix with large entries:: >>> logm(hilbert(3) * 10**20).apply(re) [ 45.5597513593433 1.27721006042799 0.317662687717978] [ 1.27721006042799 42.5222778973542 2.24003708791604] [0.317662687717978 2.24003708791604 42.395212822267] """ A = ctx.matrix(A) prec = ctx.prec try: ctx.prec += 10 tol = ctx.eps * 128 I = A**0 B = A n = 0 while 1: B = ctx.sqrtm(B) n += 1 if ctx.mnorm(B-I, 'inf') < 0.125: break T = X = B-I L = X*0 k = 1 while 1: if k & 1: L += T / k else: L -= T / k T *= X if ctx.mnorm(T, 'inf') < tol: break k += 1 if k > ctx.prec: raise ctx.NoConvergence finally: ctx.prec = prec L *= 2**n return L def powm(ctx, A, r): r""" Computes `A^r = \exp(A \log r)` for a matrix `A` and complex number `r`. **Examples** Powers and inverse powers of a matrix:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) >>> powm(A, 2) [ 63.0 20.0 69.0] [174.0 89.0 199.0] [164.0 48.0 179.0] >>> chop(powm(powm(A, 4), 1/4.)) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> powm(extraprec(20)(powm)(A, -4), -1/4.) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> chop(powm(powm(A, 1+0.5j), 1/(1+0.5j))) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] >>> powm(extraprec(5)(powm)(A, -1.5), -1/(1.5)) [ 4.0 1.0 4.0] [ 7.0 8.0 9.0] [10.0 2.0 11.0] A Fibonacci-generating matrix:: >>> powm([[1,1],[1,0]], 10) [89.0 55.0] [55.0 34.0] >>> fib(10) 55.0 >>> powm([[1,1],[1,0]], 6.5) [(16.5166626964253 - 0.0121089837381789j) (10.2078589271083 + 0.0195927472575932j)] [(10.2078589271083 + 0.0195927472575932j) (6.30880376931698 - 0.0317017309957721j)] >>> (phi**6.5 - (1-phi)**6.5)/sqrt(5) (10.2078589271083 - 0.0195927472575932j) >>> powm([[1,1],[1,0]], 6.2) [ (14.3076953002666 - 0.008222855781077j) (8.81733464837593 + 0.0133048601383712j)] [(8.81733464837593 + 0.0133048601383712j) (5.49036065189071 - 0.0215277159194482j)] >>> (phi**6.2 - (1-phi)**6.2)/sqrt(5) (8.81733464837593 - 0.0133048601383712j) """ A = ctx.matrix(A) r = ctx.convert(r) prec = ctx.prec try: ctx.prec += 10 if ctx.isint(r): v = A ** int(r) elif ctx.isint(r*2): y = int(r*2) v = ctx.sqrtm(A) ** y else: v = ctx.expm(r*ctx.logm(A)) finally: ctx.prec = prec v *= 1 return v wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/matrices/__init__.py0000644000175000017500000000000012014170666026273 0ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/matrices/matrices.py0000644000175000017500000007512512014170666026367 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange # TODO: interpret list as vectors (for multiplication) rowsep = '\n' colsep = ' ' class _matrix(object): """ Numerical matrix. Specify the dimensions or the data as a nested list. Elements default to zero. Use a flat list to create a column vector easily. By default, only mpf is used to store the data. You can specify another type using force_type=type. It's possible to specify None. Make sure force_type(force_type()) is fast. Creating matrices ----------------- Matrices in mpmath are implemented using dictionaries. Only non-zero values are stored, so it is cheap to represent sparse matrices. The most basic way to create one is to use the ``matrix`` class directly. You can create an empty matrix specifying the dimensions: >>> from mpmath import * >>> mp.dps = 15 >>> matrix(2) matrix( [['0.0', '0.0'], ['0.0', '0.0']]) >>> matrix(2, 3) matrix( [['0.0', '0.0', '0.0'], ['0.0', '0.0', '0.0']]) Calling ``matrix`` with one dimension will create a square matrix. To access the dimensions of a matrix, use the ``rows`` or ``cols`` keyword: >>> A = matrix(3, 2) >>> A matrix( [['0.0', '0.0'], ['0.0', '0.0'], ['0.0', '0.0']]) >>> A.rows 3 >>> A.cols 2 You can also change the dimension of an existing matrix. This will set the new elements to 0. If the new dimension is smaller than before, the concerning elements are discarded: >>> A.rows = 2 >>> A matrix( [['0.0', '0.0'], ['0.0', '0.0']]) Internally ``mpmathify`` is used every time an element is set. This is done using the syntax A[row,column], counting from 0: >>> A = matrix(2) >>> A[1,1] = 1 + 1j >>> A matrix( [['0.0', '0.0'], ['0.0', '(1.0 + 1.0j)']]) You can use the keyword ``force_type`` to change the function which is called on every new element: >>> matrix(2, 5, force_type=int) matrix( [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]) A more comfortable way to create a matrix lets you use nested lists: >>> matrix([[1, 2], [3, 4]]) matrix( [['1.0', '2.0'], ['3.0', '4.0']]) If you want to preserve the type of the elements you can use ``force_type=None``: >>> matrix([[1, 2.5], [1j, mpf(2)]], force_type=None) matrix( [[1, 2.5], [1j, '2.0']]) Convenient advanced functions are available for creating various standard matrices, see ``zeros``, ``ones``, ``diag``, ``eye``, ``randmatrix`` and ``hilbert``. Vectors ....... Vectors may also be represented by the ``matrix`` class (with rows = 1 or cols = 1). For vectors there are some things which make life easier. A column vector can be created using a flat list, a row vectors using an almost flat nested list:: >>> matrix([1, 2, 3]) matrix( [['1.0'], ['2.0'], ['3.0']]) >>> matrix([[1, 2, 3]]) matrix( [['1.0', '2.0', '3.0']]) Optionally vectors can be accessed like lists, using only a single index:: >>> x = matrix([1, 2, 3]) >>> x[1] mpf('2.0') >>> x[1,0] mpf('2.0') Other ..... Like you probably expected, matrices can be printed:: >>> print randmatrix(3) # doctest:+SKIP [ 0.782963853573023 0.802057689719883 0.427895717335467] [0.0541876859348597 0.708243266653103 0.615134039977379] [ 0.856151514955773 0.544759264818486 0.686210904770947] Use ``nstr`` or ``nprint`` to specify the number of digits to print:: >>> nprint(randmatrix(5), 3) # doctest:+SKIP [2.07e-1 1.66e-1 5.06e-1 1.89e-1 8.29e-1] [6.62e-1 6.55e-1 4.47e-1 4.82e-1 2.06e-2] [4.33e-1 7.75e-1 6.93e-2 2.86e-1 5.71e-1] [1.01e-1 2.53e-1 6.13e-1 3.32e-1 2.59e-1] [1.56e-1 7.27e-2 6.05e-1 6.67e-2 2.79e-1] As matrices are mutable, you will need to copy them sometimes:: >>> A = matrix(2) >>> A matrix( [['0.0', '0.0'], ['0.0', '0.0']]) >>> B = A.copy() >>> B[0,0] = 1 >>> B matrix( [['1.0', '0.0'], ['0.0', '0.0']]) >>> A matrix( [['0.0', '0.0'], ['0.0', '0.0']]) Finally, it is possible to convert a matrix to a nested list. This is very useful, as most Python libraries involving matrices or arrays (namely NumPy or SymPy) support this format:: >>> B.tolist() [[mpf('1.0'), mpf('0.0')], [mpf('0.0'), mpf('0.0')]] Matrix operations ----------------- You can add and subtract matrices of compatible dimensions:: >>> A = matrix([[1, 2], [3, 4]]) >>> B = matrix([[-2, 4], [5, 9]]) >>> A + B matrix( [['-1.0', '6.0'], ['8.0', '13.0']]) >>> A - B matrix( [['3.0', '-2.0'], ['-2.0', '-5.0']]) >>> A + ones(3) # doctest:+ELLIPSIS Traceback (most recent call last): ... ValueError: incompatible dimensions for addition It is possible to multiply or add matrices and scalars. In the latter case the operation will be done element-wise:: >>> A * 2 matrix( [['2.0', '4.0'], ['6.0', '8.0']]) >>> A / 4 matrix( [['0.25', '0.5'], ['0.75', '1.0']]) >>> A - 1 matrix( [['0.0', '1.0'], ['2.0', '3.0']]) Of course you can perform matrix multiplication, if the dimensions are compatible:: >>> A * B matrix( [['8.0', '22.0'], ['14.0', '48.0']]) >>> matrix([[1, 2, 3]]) * matrix([[-6], [7], [-2]]) matrix( [['2.0']]) You can raise powers of square matrices:: >>> A**2 matrix( [['7.0', '10.0'], ['15.0', '22.0']]) Negative powers will calculate the inverse:: >>> A**-1 matrix( [['-2.0', '1.0'], ['1.5', '-0.5']]) >>> A * A**-1 matrix( [['1.0', '1.0842021724855e-19'], ['-2.16840434497101e-19', '1.0']]) Matrix transposition is straightforward:: >>> A = ones(2, 3) >>> A matrix( [['1.0', '1.0', '1.0'], ['1.0', '1.0', '1.0']]) >>> A.T matrix( [['1.0', '1.0'], ['1.0', '1.0'], ['1.0', '1.0']]) Norms ..... Sometimes you need to know how "large" a matrix or vector is. Due to their multidimensional nature it's not possible to compare them, but there are several functions to map a matrix or a vector to a positive real number, the so called norms. For vectors the p-norm is intended, usually the 1-, the 2- and the oo-norm are used. >>> x = matrix([-10, 2, 100]) >>> norm(x, 1) mpf('112.0') >>> norm(x, 2) mpf('100.5186549850325') >>> norm(x, inf) mpf('100.0') Please note that the 2-norm is the most used one, though it is more expensive to calculate than the 1- or oo-norm. It is possible to generalize some vector norms to matrix norm:: >>> A = matrix([[1, -1000], [100, 50]]) >>> mnorm(A, 1) mpf('1050.0') >>> mnorm(A, inf) mpf('1001.0') >>> mnorm(A, 'F') mpf('1006.2310867787777') The last norm (the "Frobenius-norm") is an approximation for the 2-norm, which is hard to calculate and not available. The Frobenius-norm lacks some mathematical properties you might expect from a norm. """ def __init__(self, *args, **kwargs): self.__data = {} # LU decompostion cache, this is useful when solving the same system # multiple times, when calculating the inverse and when calculating the # determinant self._LU = None convert = kwargs.get('force_type', self.ctx.convert) if isinstance(args[0], (list, tuple)): if isinstance(args[0][0], (list, tuple)): # interpret nested list as matrix A = args[0] self.__rows = len(A) self.__cols = len(A[0]) for i, row in enumerate(A): for j, a in enumerate(row): self[i, j] = convert(a) else: # interpret list as row vector v = args[0] self.__rows = len(v) self.__cols = 1 for i, e in enumerate(v): self[i, 0] = e elif isinstance(args[0], int): # create empty matrix of given dimensions if len(args) == 1: self.__rows = self.__cols = args[0] else: assert isinstance(args[1], int), 'expected int' self.__rows = args[0] self.__cols = args[1] elif isinstance(args[0], _matrix): A = args[0].copy() self.__data = A._matrix__data self.__rows = A._matrix__rows self.__cols = A._matrix__cols convert = kwargs.get('force_type', self.ctx.convert) for i in xrange(A.__rows): for j in xrange(A.__cols): A[i,j] = convert(A[i,j]) elif hasattr(args[0], 'tolist'): A = self.ctx.matrix(args[0].tolist()) self.__data = A._matrix__data self.__rows = A._matrix__rows self.__cols = A._matrix__cols else: raise TypeError('could not interpret given arguments') def apply(self, f): """ Return a copy of self with the function `f` applied elementwise. """ new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] = f(self[i,j]) return new def __nstr__(self, n=None, **kwargs): # Build table of string representations of the elements res = [] # Track per-column max lengths for pretty alignment maxlen = [0] * self.cols for i in range(self.rows): res.append([]) for j in range(self.cols): if n: string = self.ctx.nstr(self[i,j], n, **kwargs) else: string = str(self[i,j]) res[-1].append(string) maxlen[j] = max(len(string), maxlen[j]) # Patch strings together for i, row in enumerate(res): for j, elem in enumerate(row): # Pad each element up to maxlen so the columns line up row[j] = elem.rjust(maxlen[j]) res[i] = "[" + colsep.join(row) + "]" return rowsep.join(res) def __str__(self): return self.__nstr__() def _toliststr(self, avoid_type=False): """ Create a list string from a matrix. If avoid_type: avoid multiple 'mpf's. """ # XXX: should be something like self.ctx._types typ = self.ctx.mpf s = '[' for i in xrange(self.__rows): s += '[' for j in xrange(self.__cols): if not avoid_type or not isinstance(self[i,j], typ): a = repr(self[i,j]) else: a = "'" + str(self[i,j]) + "'" s += a + ', ' s = s[:-2] s += '],\n ' s = s[:-3] s += ']' return s def tolist(self): """ Convert the matrix to a nested list. """ return [[self[i,j] for j in range(self.__cols)] for i in range(self.__rows)] def __repr__(self): if self.ctx.pretty: return self.__str__() s = 'matrix(\n' s += self._toliststr(avoid_type=True) + ')' return s def __get_element(self, key): ''' Fast extraction of the i,j element from the matrix This function is for private use only because is unsafe: 1. Does not check on the value of key it expects key to be a integer tuple (i,j) 2. Does not check bounds ''' if key in self.__data: return self.__data[key] else: return self.ctx.zero def __set_element(self, key, value): ''' Fast assignment of the i,j element in the matrix This function is unsafe: 1. Does not check on the value of key it expects key to be a integer tuple (i,j) 2. Does not check bounds 3. Does not check the value type ''' if value: # only store non-zeros self.__data[key] = value elif key in self.__data: del self.__data[key] def __getitem__(self, key): ''' Getitem function for mp matrix class with slice index enabled it allows the following assingments scalar to a slice of the matrix B = A[:,2:6] ''' # Convert vector to matrix indexing if isinstance(key, int) or isinstance(key,slice): # only sufficent for vectors if self.__rows == 1: key = (0, key) elif self.__cols == 1: key = (key, 0) else: raise IndexError('insufficient indices for matrix') if isinstance(key[0],slice) or isinstance(key[1],slice): #Rows if isinstance(key[0],slice): #Check bounds if (key[0].start is None or key[0].start >= 0) and \ (key[0].stop is None or key[0].stop <= self.__rows+1): # Generate indices rows = xrange(*key[0].indices(self.__rows)) else: raise IndexError('Row index out of bounds') else: # Single row rows = [key[0]] # Columns if isinstance(key[1],slice): # Check bounds if (key[1].start is None or key[1].start >= 0) and \ (key[1].stop is None or key[1].stop <= self.__cols+1): # Generate indices columns = xrange(*key[1].indices(self.__cols)) else: raise IndexError('Column index out of bounds') else: # Single column columns = [key[1]] # Create matrix slice m = self.ctx.matrix(len(rows),len(columns)) # Assign elements to the output matrix for i,x in enumerate(rows): for j,y in enumerate(columns): m.__set_element((i,j),self.__get_element((x,y))) return m else: # single element extraction if key[0] >= self.__rows or key[1] >= self.__cols: raise IndexError('matrix index out of range') if key in self.__data: return self.__data[key] else: return self.ctx.zero def __setitem__(self, key, value): # setitem function for mp matrix class with slice index enabled # it allows the following assingments # scalar to a slice of the matrix # A[:,2:6] = 2.5 # submatrix to matrix (the value matrix should be the same size as the slice size) # A[3,:] = B where A is n x m and B is n x 1 # Convert vector to matrix indexing if isinstance(key, int) or isinstance(key,slice): # only sufficent for vectors if self.__rows == 1: key = (0, key) elif self.__cols == 1: key = (key, 0) else: raise IndexError('insufficient indices for matrix') # Slice indexing if isinstance(key[0],slice) or isinstance(key[1],slice): # Rows if isinstance(key[0],slice): # Check bounds if (key[0].start is None or key[0].start >= 0) and \ (key[0].stop is None or key[0].stop <= self.__rows+1): # generate row indices rows = xrange(*key[0].indices(self.__rows)) else: raise IndexError('Row index out of bounds') else: # Single row rows = [key[0]] # Columns if isinstance(key[1],slice): # Check bounds if (key[1].start is None or key[1].start >= 0) and \ (key[1].stop is None or key[1].stop <= self.__cols+1): # Generate column indices columns = xrange(*key[1].indices(self.__cols)) else: raise IndexError('Column index out of bounds') else: # Single column columns = [key[1]] # Assign slice with a scalar if isinstance(value,self.ctx.matrix): # Assign elements to matrix if input and output dimensions match if len(rows) == value.rows and len(columns) == value.cols: for i,x in enumerate(rows): for j,y in enumerate(columns): self.__set_element((x,y), value.__get_element((i,j))) else: raise ValueError('Dimensions do not match') else: # Assign slice with scalars value = self.ctx.convert(value) for i in rows: for j in columns: self.__set_element((i,j), value) else: # Single element assingment # Check bounds if key[0] >= self.__rows or key[1] >= self.__cols: raise IndexError('matrix index out of range') # Convert and store value value = self.ctx.convert(value) if value: # only store non-zeros self.__data[key] = value elif key in self.__data: del self.__data[key] if self._LU: self._LU = None return def __iter__(self): for i in xrange(self.__rows): for j in xrange(self.__cols): yield self[i,j] def __mul__(self, other): if isinstance(other, self.ctx.matrix): # dot multiplication TODO: use Strassen's method? if self.__cols != other.__rows: raise ValueError('dimensions not compatible for multiplication') new = self.ctx.matrix(self.__rows, other.__cols) for i in xrange(self.__rows): for j in xrange(other.__cols): new[i, j] = self.ctx.fdot((self[i,k], other[k,j]) for k in xrange(other.__rows)) return new else: # try scalar multiplication new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i, j] = other * self[i, j] return new def __rmul__(self, other): # assume other is scalar and thus commutative assert not isinstance(other, self.ctx.matrix) return self.__mul__(other) def __pow__(self, other): # avoid cyclic import problems #from linalg import inverse if not isinstance(other, int): raise ValueError('only integer exponents are supported') if not self.__rows == self.__cols: raise ValueError('only powers of square matrices are defined') n = other if n == 0: return self.ctx.eye(self.__rows) if n < 0: n = -n neg = True else: neg = False i = n y = 1 z = self.copy() while i != 0: if i % 2 == 1: y = y * z z = z*z i = i // 2 if neg: y = self.ctx.inverse(y) return y def __div__(self, other): # assume other is scalar and do element-wise divison assert not isinstance(other, self.ctx.matrix) new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] = self[i,j] / other return new __truediv__ = __div__ def __add__(self, other): if isinstance(other, self.ctx.matrix): if not (self.__rows == other.__rows and self.__cols == other.__cols): raise ValueError('incompatible dimensions for addition') new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] = self[i,j] + other[i,j] return new else: # assume other is scalar and add element-wise new = self.ctx.matrix(self.__rows, self.__cols) for i in xrange(self.__rows): for j in xrange(self.__cols): new[i,j] += self[i,j] + other return new def __radd__(self, other): return self.__add__(other) def __sub__(self, other): if isinstance(other, self.ctx.matrix) and not (self.__rows == other.__rows and self.__cols == other.__cols): raise ValueError('incompatible dimensions for substraction') return self.__add__(other * (-1)) def __neg__(self): return (-1) * self def __rsub__(self, other): return -self + other def __eq__(self, other): return self.__rows == other.__rows and self.__cols == other.__cols \ and self.__data == other.__data def __len__(self): if self.rows == 1: return self.cols elif self.cols == 1: return self.rows else: return self.rows # do it like numpy def __getrows(self): return self.__rows def __setrows(self, value): for key in self.__data.copy(): if key[0] >= value: del self.__data[key] self.__rows = value rows = property(__getrows, __setrows, doc='number of rows') def __getcols(self): return self.__cols def __setcols(self, value): for key in self.__data.copy(): if key[1] >= value: del self.__data[key] self.__cols = value cols = property(__getcols, __setcols, doc='number of columns') def transpose(self): new = self.ctx.matrix(self.__cols, self.__rows) for i in xrange(self.__rows): for j in xrange(self.__cols): new[j,i] = self[i,j] return new T = property(transpose) def conjugate(self): return self.apply(self.ctx.conj) def transpose_conj(self): return self.conjugate().transpose() H = property(transpose_conj) def copy(self): new = self.ctx.matrix(self.__rows, self.__cols) new.__data = self.__data.copy() return new __copy__ = copy def column(self, n): m = self.ctx.matrix(self.rows, 1) for i in range(self.rows): m[i] = self[i,n] return m class MatrixMethods(object): def __init__(ctx): # XXX: subclass ctx.matrix = type('matrix', (_matrix,), {}) ctx.matrix.ctx = ctx ctx.matrix.convert = ctx.convert def eye(ctx, n, **kwargs): """ Create square identity matrix n x n. """ A = ctx.matrix(n, **kwargs) for i in xrange(n): A[i,i] = 1 return A def diag(ctx, diagonal, **kwargs): """ Create square diagonal matrix using given list. Example: >>> from mpmath import diag, mp >>> mp.pretty = False >>> diag([1, 2, 3]) matrix( [['1.0', '0.0', '0.0'], ['0.0', '2.0', '0.0'], ['0.0', '0.0', '3.0']]) """ A = ctx.matrix(len(diagonal), **kwargs) for i in xrange(len(diagonal)): A[i,i] = diagonal[i] return A def zeros(ctx, *args, **kwargs): """ Create matrix m x n filled with zeros. One given dimension will create square matrix n x n. Example: >>> from mpmath import zeros, mp >>> mp.pretty = False >>> zeros(2) matrix( [['0.0', '0.0'], ['0.0', '0.0']]) """ if len(args) == 1: m = n = args[0] elif len(args) == 2: m = args[0] n = args[1] else: raise TypeError('zeros expected at most 2 arguments, got %i' % len(args)) A = ctx.matrix(m, n, **kwargs) for i in xrange(m): for j in xrange(n): A[i,j] = 0 return A def ones(ctx, *args, **kwargs): """ Create matrix m x n filled with ones. One given dimension will create square matrix n x n. Example: >>> from mpmath import ones, mp >>> mp.pretty = False >>> ones(2) matrix( [['1.0', '1.0'], ['1.0', '1.0']]) """ if len(args) == 1: m = n = args[0] elif len(args) == 2: m = args[0] n = args[1] else: raise TypeError('ones expected at most 2 arguments, got %i' % len(args)) A = ctx.matrix(m, n, **kwargs) for i in xrange(m): for j in xrange(n): A[i,j] = 1 return A def hilbert(ctx, m, n=None): """ Create (pseudo) hilbert matrix m x n. One given dimension will create hilbert matrix n x n. The matrix is very ill-conditioned and symmetric, positive definite if square. """ if n is None: n = m A = ctx.matrix(m, n) for i in xrange(m): for j in xrange(n): A[i,j] = ctx.one / (i + j + 1) return A def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs): """ Create a random m x n matrix. All values are >= min and >> from mpmath import randmatrix >>> randmatrix(2) # doctest:+SKIP matrix( [['0.53491598236191806', '0.57195669543302752'], ['0.85589992269513615', '0.82444367501382143']]) """ if not n: n = m A = ctx.matrix(m, n, **kwargs) for i in xrange(m): for j in xrange(n): A[i,j] = ctx.rand() * (max - min) + min return A def swap_row(ctx, A, i, j): """ Swap row i with row j. """ if i == j: return if isinstance(A, ctx.matrix): for k in xrange(A.cols): A[i,k], A[j,k] = A[j,k], A[i,k] elif isinstance(A, list): A[i], A[j] = A[j], A[i] else: raise TypeError('could not interpret type') def extend(ctx, A, b): """ Extend matrix A with column b and return result. """ assert isinstance(A, ctx.matrix) assert A.rows == len(b) A = A.copy() A.cols += 1 for i in xrange(A.rows): A[i, A.cols-1] = b[i] return A def norm(ctx, x, p=2): r""" Gives the entrywise `p`-norm of an iterable *x*, i.e. the vector norm `\left(\sum_k |x_k|^p\right)^{1/p}`, for any given `1 \le p \le \infty`. Special cases: If *x* is not iterable, this just returns ``absmax(x)``. ``p=1`` gives the sum of absolute values. ``p=2`` is the standard Euclidean vector norm. ``p=inf`` gives the magnitude of the largest element. For *x* a matrix, ``p=2`` is the Frobenius norm. For operator matrix norms, use :func:`~mpmath.mnorm` instead. You can use the string 'inf' as well as float('inf') or mpf('inf') to specify the infinity norm. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> x = matrix([-10, 2, 100]) >>> norm(x, 1) mpf('112.0') >>> norm(x, 2) mpf('100.5186549850325') >>> norm(x, inf) mpf('100.0') """ try: iter(x) except TypeError: return ctx.absmax(x) if type(p) is not int: p = ctx.convert(p) if p == ctx.inf: return max(ctx.absmax(i) for i in x) elif p == 1: return ctx.fsum(x, absolute=1) elif p == 2: return ctx.sqrt(ctx.fsum(x, absolute=1, squared=1)) elif p > 1: return ctx.nthroot(ctx.fsum(abs(i)**p for i in x), p) else: raise ValueError('p has to be >= 1') def mnorm(ctx, A, p=1): r""" Gives the matrix (operator) `p`-norm of A. Currently ``p=1`` and ``p=inf`` are supported: ``p=1`` gives the 1-norm (maximal column sum) ``p=inf`` gives the `\infty`-norm (maximal row sum). You can use the string 'inf' as well as float('inf') or mpf('inf') ``p=2`` (not implemented) for a square matrix is the usual spectral matrix norm, i.e. the largest singular value. ``p='f'`` (or 'F', 'fro', 'Frobenius, 'frobenius') gives the Frobenius norm, which is the elementwise 2-norm. The Frobenius norm is an approximation of the spectral norm and satisfies .. math :: \frac{1}{\sqrt{\mathrm{rank}(A)}} \|A\|_F \le \|A\|_2 \le \|A\|_F The Frobenius norm lacks some mathematical properties that might be expected of a norm. For general elementwise `p`-norms, use :func:`~mpmath.norm` instead. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> A = matrix([[1, -1000], [100, 50]]) >>> mnorm(A, 1) mpf('1050.0') >>> mnorm(A, inf) mpf('1001.0') >>> mnorm(A, 'F') mpf('1006.2310867787777') """ A = ctx.matrix(A) if type(p) is not int: if type(p) is str and 'frobenius'.startswith(p.lower()): return ctx.norm(A, 2) p = ctx.convert(p) m, n = A.rows, A.cols if p == 1: return max(ctx.fsum((A[i,j] for i in xrange(m)), absolute=1) for j in xrange(n)) elif p == ctx.inf: return max(ctx.fsum((A[i,j] for j in xrange(n)), absolute=1) for i in xrange(m)) else: raise NotImplementedError("matrix p-norm for arbitrary p") if __name__ == '__main__': import doctest doctest.testmod() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/ctx_fp.py0000644000175000017500000001465412014170666024234 0ustar georgeskgeorgeskfrom .ctx_base import StandardBaseContext import math import cmath from . import math2 from . import function_docs from .libmp import mpf_bernoulli, to_float, int_types from . import libmp class FPContext(StandardBaseContext): """ Context for fast low-precision arithmetic (53-bit precision, giving at most about 15-digit accuracy), using Python's builtin float and complex. """ def __init__(ctx): StandardBaseContext.__init__(ctx) # Override SpecialFunctions implementation ctx.loggamma = math2.loggamma ctx._bernoulli_cache = {} ctx.pretty = False ctx._init_aliases() _mpq = lambda cls, x: float(x[0])/x[1] NoConvergence = libmp.NoConvergence def _get_prec(ctx): return 53 def _set_prec(ctx, p): return def _get_dps(ctx): return 15 def _set_dps(ctx, p): return _fixed_precision = True prec = property(_get_prec, _set_prec) dps = property(_get_dps, _set_dps) zero = 0.0 one = 1.0 eps = math2.EPS inf = math2.INF ninf = math2.NINF nan = math2.NAN j = 1j # Called by SpecialFunctions.__init__() @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] return f(ctx, *args, **kwargs) else: f_wrapped = f f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) setattr(cls, name, f_wrapped) def bernoulli(ctx, n): cache = ctx._bernoulli_cache if n in cache: return cache[n] cache[n] = to_float(mpf_bernoulli(n, 53, 'n'), strict=True) return cache[n] pi = math2.pi e = math2.e euler = math2.euler sqrt2 = 1.4142135623730950488 sqrt5 = 2.2360679774997896964 phi = 1.6180339887498948482 ln2 = 0.69314718055994530942 ln10 = 2.302585092994045684 euler = 0.57721566490153286061 catalan = 0.91596559417721901505 khinchin = 2.6854520010653064453 apery = 1.2020569031595942854 glaisher = 1.2824271291006226369 absmin = absmax = abs def is_special(ctx, x): return x - x != 0.0 def isnan(ctx, x): return x != x def isinf(ctx, x): return abs(x) == math2.INF def isnormal(ctx, x): if x: return x - x == 0.0 return False def isnpint(ctx, x): if type(x) is complex: if x.imag: return False x = x.real return x <= 0.0 and round(x) == x mpf = float mpc = complex def convert(ctx, x): try: return float(x) except: return complex(x) power = staticmethod(math2.pow) sqrt = staticmethod(math2.sqrt) exp = staticmethod(math2.exp) ln = log = staticmethod(math2.log) cos = staticmethod(math2.cos) sin = staticmethod(math2.sin) tan = staticmethod(math2.tan) cos_sin = staticmethod(math2.cos_sin) acos = staticmethod(math2.acos) asin = staticmethod(math2.asin) atan = staticmethod(math2.atan) cosh = staticmethod(math2.cosh) sinh = staticmethod(math2.sinh) tanh = staticmethod(math2.tanh) gamma = staticmethod(math2.gamma) rgamma = staticmethod(math2.rgamma) fac = factorial = staticmethod(math2.factorial) floor = staticmethod(math2.floor) ceil = staticmethod(math2.ceil) cospi = staticmethod(math2.cospi) sinpi = staticmethod(math2.sinpi) cbrt = staticmethod(math2.cbrt) _nthroot = staticmethod(math2.nthroot) _ei = staticmethod(math2.ei) _e1 = staticmethod(math2.e1) _zeta = _zeta_int = staticmethod(math2.zeta) # XXX: math2 def arg(ctx, z): z = complex(z) return math.atan2(z.imag, z.real) def expj(ctx, x): return ctx.exp(ctx.j*x) def expjpi(ctx, x): return ctx.exp(ctx.j*ctx.pi*x) ldexp = math.ldexp frexp = math.frexp def mag(ctx, z): if z: return ctx.frexp(abs(z))[1] return ctx.ninf def isint(ctx, z): if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 if z.imag: return False z = z.real try: return z == int(z) except: return False def nint_distance(ctx, z): if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 n = round(z.real) else: n = round(z) if n == z: return n, ctx.ninf return n, ctx.mag(abs(z-n)) def _convert_param(ctx, z): if type(z) is tuple: p, q = z return ctx.mpf(p) / q, 'R' if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 intz = int(z.real) else: intz = int(z) if z == intz: return intz, 'Z' return z, 'R' def _is_real_type(ctx, z): return isinstance(z, float) or isinstance(z, int_types) def _is_complex_type(ctx, z): return isinstance(z, complex) def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): coeffs = list(coeffs) num = range(p) den = range(p,p+q) tol = ctx.eps s = t = 1.0 k = 0 while 1: for i in num: t *= (coeffs[i]+k) for i in den: t /= (coeffs[i]+k) k += 1; t /= k; t *= z; s += t if abs(t) < tol: return s if k > maxterms: raise ctx.NoConvergence def atan2(ctx, x, y): return math.atan2(x, y) def psi(ctx, m, z): m = int(m) if m == 0: return ctx.digamma(z) return (-1)**(m+1) * ctx.fac(m) * ctx.zeta(m+1, z) digamma = staticmethod(math2.digamma) def harmonic(ctx, x): x = ctx.convert(x) if x == 0 or x == 1: return x return ctx.digamma(x+1) + ctx.euler nstr = str def to_fixed(ctx, x, prec): return int(math.ldexp(x, prec)) def rand(ctx): import random return random.random() _erf = staticmethod(math2.erf) _erfc = staticmethod(math2.erfc) def sum_accurately(ctx, terms, check_step=1): s = ctx.zero k = 0 for term in terms(): s += term if (not k % check_step) and term: if abs(term) <= 1e-18*abs(s): break k += 1 return s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/conftest.py0000644000175000017500000000043212014170666024563 0ustar georgeskgeorgesk# The py library is part of the "py.test" testing suite (python-codespeak-lib # on Debian), see http://codespeak.net/py/ import py #this makes py.test put mpath directory into the sys.path, so that we can #"import mpmath" from tests nicely rootdir = py.magic.autopath().dirpath() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/identification.py0000644000175000017500000007041712014170666025741 0ustar georgeskgeorgesk""" Implements the PSLQ algorithm for integer relation detection, and derivative algorithms for constant recognition. """ from .libmp.backend import xrange from .libmp import int_types, sqrt_fixed # round to nearest integer (can be done more elegantly...) def round_fixed(x, prec): return ((x + (1<<(prec-1))) >> prec) << prec class IdentificationMethods(object): pass def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False): r""" Given a vector of real numbers `x = [x_0, x_1, ..., x_n]`, ``pslq(x)`` uses the PSLQ algorithm to find a list of integers `[c_0, c_1, ..., c_n]` such that .. math :: |c_1 x_1 + c_2 x_2 + ... + c_n x_n| < \mathrm{tol} and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector exists, :func:`~mpmath.pslq` returns ``None``. The tolerance defaults to 3/4 of the working precision. **Examples** Find rational approximations for `\pi`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> pslq([-1, pi], tol=0.01) [22, 7] >>> pslq([-1, pi], tol=0.001) [355, 113] >>> mpf(22)/7; mpf(355)/113; +pi 3.14285714285714 3.14159292035398 3.14159265358979 Pi is not a rational number with denominator less than 1000:: >>> pslq([-1, pi]) >>> To within the standard precision, it can however be approximated by at least one rational number with denominator less than `10^{12}`:: >>> p, q = pslq([-1, pi], maxcoeff=10**12) >>> print(p); print(q) 238410049439 75888275702 >>> mpf(p)/q 3.14159265358979 The PSLQ algorithm can be applied to long vectors. For example, we can investigate the rational (in)dependence of integer square roots:: >>> mp.dps = 30 >>> pslq([sqrt(n) for n in range(2, 5+1)]) >>> >>> pslq([sqrt(n) for n in range(2, 6+1)]) >>> >>> pslq([sqrt(n) for n in range(2, 8+1)]) [2, 0, 0, 0, 0, 0, -1] **Machin formulas** A famous formula for `\pi` is Machin's, .. math :: \frac{\pi}{4} = 4 \operatorname{acot} 5 - \operatorname{acot} 239 There are actually infinitely many formulas of this type. Two others are .. math :: \frac{\pi}{4} = \operatorname{acot} 1 \frac{\pi}{4} = 12 \operatorname{acot} 49 + 32 \operatorname{acot} 57 + 5 \operatorname{acot} 239 + 12 \operatorname{acot} 110443 We can easily verify the formulas using the PSLQ algorithm:: >>> mp.dps = 30 >>> pslq([pi/4, acot(1)]) [1, -1] >>> pslq([pi/4, acot(5), acot(239)]) [1, -4, 1] >>> pslq([pi/4, acot(49), acot(57), acot(239), acot(110443)]) [1, -12, -32, 5, -12] We could try to generate a custom Machin-like formula by running the PSLQ algorithm with a few inverse cotangent values, for example acot(2), acot(3) ... acot(10). Unfortunately, there is a linear dependence among these values, resulting in only that dependence being detected, with a zero coefficient for `\pi`:: >>> pslq([pi] + [acot(n) for n in range(2,11)]) [0, 1, -1, 0, 0, 0, -1, 0, 0, 0] We get better luck by removing linearly dependent terms:: >>> pslq([pi] + [acot(n) for n in range(2,11) if n not in (3, 5)]) [1, -8, 0, 0, 4, 0, 0, 0] In other words, we found the following formula:: >>> 8*acot(2) - 4*acot(7) 3.14159265358979323846264338328 >>> +pi 3.14159265358979323846264338328 **Algorithm** This is a fairly direct translation to Python of the pseudocode given by David Bailey, "The PSLQ Integer Relation Algorithm": http://www.cecm.sfu.ca/organics/papers/bailey/paper/html/node3.html The present implementation uses fixed-point instead of floating-point arithmetic, since this is significantly (about 7x) faster. """ n = len(x) assert n >= 2 # At too low precision, the algorithm becomes meaningless prec = ctx.prec assert prec >= 53 if verbose and prec // max(2,n) < 5: print("Warning: precision for PSLQ may be too low") target = int(prec * 0.75) if tol is None: tol = ctx.mpf(2)**(-target) else: tol = ctx.convert(tol) extra = 60 prec += extra if verbose: print("PSLQ using prec %i and tol %s" % (prec, ctx.nstr(tol))) tol = ctx.to_fixed(tol, prec) assert tol # Convert to fixed-point numbers. The dummy None is added so we can # use 1-based indexing. (This just allows us to be consistent with # Bailey's indexing. The algorithm is 100 lines long, so debugging # a single wrong index can be painful.) x = [None] + [ctx.to_fixed(ctx.mpf(xk), prec) for xk in x] # Sanity check on magnitudes minx = min(abs(xx) for xx in x[1:]) if not minx: raise ValueError("PSLQ requires a vector of nonzero numbers") if minx < tol//100: if verbose: print("STOPPING: (one number is too small)") return None g = sqrt_fixed((4<> prec) s[k] = sqrt_fixed(t, prec) t = s[1] y = x[:] for k in xrange(1, n+1): y[k] = (x[k] << prec) // t s[k] = (s[k] << prec) // t # step 3 for i in xrange(1, n+1): for j in xrange(i+1, n): H[i,j] = 0 if i <= n-1: if s[i]: H[i,i] = (s[i+1] << prec) // s[i] else: H[i,i] = 0 for j in range(1, i): sjj1 = s[j]*s[j+1] if sjj1: H[i,j] = ((-y[i]*y[j])<> prec) for k in xrange(1, j+1): H[i,k] = H[i,k] - (t*H[j,k] >> prec) for k in xrange(1, n+1): A[i,k] = A[i,k] - (t*A[j,k] >> prec) B[k,j] = B[k,j] + (t*B[k,i] >> prec) # Main algorithm for REP in range(maxsteps): # Step 1 m = -1 szmax = -1 for i in range(1, n): h = H[i,i] sz = (g**i * abs(h)) >> (prec*(i-1)) if sz > szmax: m = i szmax = sz # Step 2 y[m], y[m+1] = y[m+1], y[m] tmp = {} for i in xrange(1,n+1): H[m,i], H[m+1,i] = H[m+1,i], H[m,i] for i in xrange(1,n+1): A[m,i], A[m+1,i] = A[m+1,i], A[m,i] for i in xrange(1,n+1): B[i,m], B[i,m+1] = B[i,m+1], B[i,m] # Step 3 if m <= n - 2: t0 = sqrt_fixed((H[m,m]**2 + H[m,m+1]**2)>>prec, prec) # A zero element probably indicates that the precision has # been exhausted. XXX: this could be spurious, due to # using fixed-point arithmetic if not t0: break t1 = (H[m,m] << prec) // t0 t2 = (H[m,m+1] << prec) // t0 for i in xrange(m, n+1): t3 = H[i,m] t4 = H[i,m+1] H[i,m] = (t1*t3+t2*t4) >> prec H[i,m+1] = (-t2*t3+t1*t4) >> prec # Step 4 for i in xrange(m+1, n+1): for j in xrange(min(i-1, m+1), 0, -1): try: t = round_fixed((H[i,j] << prec)//H[j,j], prec) # Precision probably exhausted except ZeroDivisionError: break y[j] = y[j] + ((t*y[i]) >> prec) for k in xrange(1, j+1): H[i,k] = H[i,k] - (t*H[j,k] >> prec) for k in xrange(1, n+1): A[i,k] = A[i,k] - (t*A[j,k] >> prec) B[k,j] = B[k,j] + (t*B[k,i] >> prec) # Until a relation is found, the error typically decreases # slowly (e.g. a factor 1-10) with each step TODO: we could # compare err from two successive iterations. If there is a # large drop (several orders of magnitude), that indicates a # "high quality" relation was detected. Reporting this to # the user somehow might be useful. best_err = maxcoeff<> prec) for j in \ range(1,n+1)] if max(abs(v) for v in vec) < maxcoeff: if verbose: print("FOUND relation at iter %i/%i, error: %s" % \ (REP, maxsteps, ctx.nstr(err / ctx.mpf(2)**prec, 1))) return vec best_err = min(err, best_err) # Calculate a lower bound for the norm. We could do this # more exactly (using the Euclidean norm) but there is probably # no practical benefit. recnorm = max(abs(h) for h in H.values()) if recnorm: norm = ((1 << (2*prec)) // recnorm) >> prec norm //= 100 else: norm = ctx.inf if verbose: print("%i/%i: Error: %8s Norm: %s" % \ (REP, maxsteps, ctx.nstr(best_err / ctx.mpf(2)**prec, 1), norm)) if norm >= maxcoeff: break if verbose: print("CANCELLING after step %i/%i." % (REP, maxsteps)) print("Could not find an integer relation. Norm bound: %s" % norm) return None def findpoly(ctx, x, n=1, **kwargs): r""" ``findpoly(x, n)`` returns the coefficients of an integer polynomial `P` of degree at most `n` such that `P(x) \approx 0`. If no polynomial having `x` as a root can be found, :func:`~mpmath.findpoly` returns ``None``. :func:`~mpmath.findpoly` works by successively calling :func:`~mpmath.pslq` with the vectors `[1, x]`, `[1, x, x^2]`, `[1, x, x^2, x^3]`, ..., `[1, x, x^2, .., x^n]` as input. Keyword arguments given to :func:`~mpmath.findpoly` are forwarded verbatim to :func:`~mpmath.pslq`. In particular, you can specify a tolerance for `P(x)` with ``tol`` and a maximum permitted coefficient size with ``maxcoeff``. For large values of `n`, it is recommended to run :func:`~mpmath.findpoly` at high precision; preferably 50 digits or more. **Examples** By default (degree `n = 1`), :func:`~mpmath.findpoly` simply finds a linear polynomial with a rational root:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> findpoly(0.7) [-10, 7] The generated coefficient list is valid input to ``polyval`` and ``polyroots``:: >>> nprint(polyval(findpoly(phi, 2), phi), 1) -2.0e-16 >>> for r in polyroots(findpoly(phi, 2)): ... print(r) ... -0.618033988749895 1.61803398874989 Numbers of the form `m + n \sqrt p` for integers `(m, n, p)` are solutions to quadratic equations. As we find here, `1+\sqrt 2` is a root of the polynomial `x^2 - 2x - 1`:: >>> findpoly(1+sqrt(2), 2) [1, -2, -1] >>> findroot(lambda x: x**2 - 2*x - 1, 1) 2.4142135623731 Despite only containing square roots, the following number results in a polynomial of degree 4:: >>> findpoly(sqrt(2)+sqrt(3), 4) [1, 0, -10, 0, 1] In fact, `x^4 - 10x^2 + 1` is the *minimal polynomial* of `r = \sqrt 2 + \sqrt 3`, meaning that a rational polynomial of lower degree having `r` as a root does not exist. Given sufficient precision, :func:`~mpmath.findpoly` will usually find the correct minimal polynomial of a given algebraic number. **Non-algebraic numbers** If :func:`~mpmath.findpoly` fails to find a polynomial with given coefficient size and tolerance constraints, that means no such polynomial exists. We can verify that `\pi` is not an algebraic number of degree 3 with coefficients less than 1000:: >>> mp.dps = 15 >>> findpoly(pi, 3) >>> It is always possible to find an algebraic approximation of a number using one (or several) of the following methods: 1. Increasing the permitted degree 2. Allowing larger coefficients 3. Reducing the tolerance One example of each method is shown below:: >>> mp.dps = 15 >>> findpoly(pi, 4) [95, -545, 863, -183, -298] >>> findpoly(pi, 3, maxcoeff=10000) [836, -1734, -2658, -457] >>> findpoly(pi, 3, tol=1e-7) [-4, 22, -29, -2] It is unknown whether Euler's constant is transcendental (or even irrational). We can use :func:`~mpmath.findpoly` to check that if is an algebraic number, its minimal polynomial must have degree at least 7 and a coefficient of magnitude at least 1000000:: >>> mp.dps = 200 >>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100, maxsteps=1000) >>> Note that the high precision and strict tolerance is necessary for such high-degree runs, since otherwise unwanted low-accuracy approximations will be detected. It may also be necessary to set maxsteps high to prevent a premature exit (before the coefficient bound has been reached). Running with ``verbose=True`` to get an idea what is happening can be useful. """ x = ctx.mpf(x) assert n >= 1 if x == 0: return [1, 0] xs = [ctx.mpf(1)] for i in range(1,n+1): xs.append(x**i) a = ctx.pslq(xs, **kwargs) if a is not None: return a[::-1] def fracgcd(p, q): x, y = p, q while y: x, y = y, x % y if x != 1: p //= x q //= x if q == 1: return p return p, q def pslqstring(r, constants): q = r[0] r = r[1:] s = [] for i in range(len(r)): p = r[i] if p: z = fracgcd(-p,q) cs = constants[i][1] if cs == '1': cs = '' else: cs = '*' + cs if isinstance(z, int_types): if z > 0: term = str(z) + cs else: term = ("(%s)" % z) + cs else: term = ("(%s/%s)" % z) + cs s.append(term) s = ' + '.join(s) if '+' in s or '*' in s: s = '(' + s + ')' return s or '0' def prodstring(r, constants): q = r[0] r = r[1:] num = [] den = [] for i in range(len(r)): p = r[i] if p: z = fracgcd(-p,q) cs = constants[i][1] if isinstance(z, int_types): if abs(z) == 1: t = cs else: t = '%s**%s' % (cs, abs(z)) ([num,den][z<0]).append(t) else: t = '%s**(%s/%s)' % (cs, abs(z[0]), z[1]) ([num,den][z[0]<0]).append(t) num = '*'.join(num) den = '*'.join(den) if num and den: return "(%s)/(%s)" % (num, den) if num: return num if den: return "1/(%s)" % den def quadraticstring(ctx,t,a,b,c): if c < 0: a,b,c = -a,-b,-c u1 = (-b+ctx.sqrt(b**2-4*a*c))/(2*c) u2 = (-b-ctx.sqrt(b**2-4*a*c))/(2*c) if abs(u1-t) < abs(u2-t): if b: s = '((%s+sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) else: s = '(sqrt(%s)/%s)' % (-4*a*c,2*c) else: if b: s = '((%s-sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) else: s = '(-sqrt(%s)/%s)' % (-4*a*c,2*c) return s # Transformation y = f(x,c), with inverse function x = f(y,c) # The third entry indicates whether the transformation is # redundant when c = 1 transforms = [ (lambda ctx,x,c: x*c, '$y/$c', 0), (lambda ctx,x,c: x/c, '$c*$y', 1), (lambda ctx,x,c: c/x, '$c/$y', 0), (lambda ctx,x,c: (x*c)**2, 'sqrt($y)/$c', 0), (lambda ctx,x,c: (x/c)**2, '$c*sqrt($y)', 1), (lambda ctx,x,c: (c/x)**2, '$c/sqrt($y)', 0), (lambda ctx,x,c: c*x**2, 'sqrt($y)/sqrt($c)', 1), (lambda ctx,x,c: x**2/c, 'sqrt($c)*sqrt($y)', 1), (lambda ctx,x,c: c/x**2, 'sqrt($c)/sqrt($y)', 1), (lambda ctx,x,c: ctx.sqrt(x*c), '$y**2/$c', 0), (lambda ctx,x,c: ctx.sqrt(x/c), '$c*$y**2', 1), (lambda ctx,x,c: ctx.sqrt(c/x), '$c/$y**2', 0), (lambda ctx,x,c: c*ctx.sqrt(x), '$y**2/$c**2', 1), (lambda ctx,x,c: ctx.sqrt(x)/c, '$c**2*$y**2', 1), (lambda ctx,x,c: c/ctx.sqrt(x), '$c**2/$y**2', 1), (lambda ctx,x,c: ctx.exp(x*c), 'log($y)/$c', 0), (lambda ctx,x,c: ctx.exp(x/c), '$c*log($y)', 1), (lambda ctx,x,c: ctx.exp(c/x), '$c/log($y)', 0), (lambda ctx,x,c: c*ctx.exp(x), 'log($y/$c)', 1), (lambda ctx,x,c: ctx.exp(x)/c, 'log($c*$y)', 1), (lambda ctx,x,c: c/ctx.exp(x), 'log($c/$y)', 0), (lambda ctx,x,c: ctx.ln(x*c), 'exp($y)/$c', 0), (lambda ctx,x,c: ctx.ln(x/c), '$c*exp($y)', 1), (lambda ctx,x,c: ctx.ln(c/x), '$c/exp($y)', 0), (lambda ctx,x,c: c*ctx.ln(x), 'exp($y/$c)', 1), (lambda ctx,x,c: ctx.ln(x)/c, 'exp($c*$y)', 1), (lambda ctx,x,c: c/ctx.ln(x), 'exp($c/$y)', 0), ] def identify(ctx, x, constants=[], tol=None, maxcoeff=1000, full=False, verbose=False): """ Given a real number `x`, ``identify(x)`` attempts to find an exact formula for `x`. This formula is returned as a string. If no match is found, ``None`` is returned. With ``full=True``, a list of matching formulas is returned. As a simple example, :func:`~mpmath.identify` will find an algebraic formula for the golden ratio:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> identify(phi) '((1+sqrt(5))/2)' :func:`~mpmath.identify` can identify simple algebraic numbers and simple combinations of given base constants, as well as certain basic transformations thereof. More specifically, :func:`~mpmath.identify` looks for the following: 1. Fractions 2. Quadratic algebraic numbers 3. Rational linear combinations of the base constants 4. Any of the above after first transforming `x` into `f(x)` where `f(x)` is `1/x`, `\sqrt x`, `x^2`, `\log x` or `\exp x`, either directly or with `x` or `f(x)` multiplied or divided by one of the base constants 5. Products of fractional powers of the base constants and small integers Base constants can be given as a list of strings representing mpmath expressions (:func:`~mpmath.identify` will ``eval`` the strings to numerical values and use the original strings for the output), or as a dict of formula:value pairs. In order not to produce spurious results, :func:`~mpmath.identify` should be used with high precision; preferably 50 digits or more. **Examples** Simple identifications can be performed safely at standard precision. Here the default recognition of rational, algebraic, and exp/log of algebraic numbers is demonstrated:: >>> mp.dps = 15 >>> identify(0.22222222222222222) '(2/9)' >>> identify(1.9662210973805663) 'sqrt(((24+sqrt(48))/8))' >>> identify(4.1132503787829275) 'exp((sqrt(8)/2))' >>> identify(0.881373587019543) 'log(((2+sqrt(8))/2))' By default, :func:`~mpmath.identify` does not recognize `\pi`. At standard precision it finds a not too useful approximation. At slightly increased precision, this approximation is no longer accurate enough and :func:`~mpmath.identify` more correctly returns ``None``:: >>> identify(pi) '(2**(176/117)*3**(20/117)*5**(35/39))/(7**(92/117))' >>> mp.dps = 30 >>> identify(pi) >>> Numbers such as `\pi`, and simple combinations of user-defined constants, can be identified if they are provided explicitly:: >>> identify(3*pi-2*e, ['pi', 'e']) '(3*pi + (-2)*e)' Here is an example using a dict of constants. Note that the constants need not be "atomic"; :func:`~mpmath.identify` can just as well express the given number in terms of expressions given by formulas:: >>> identify(pi+e, {'a':pi+2, 'b':2*e}) '((-2) + 1*a + (1/2)*b)' Next, we attempt some identifications with a set of base constants. It is necessary to increase the precision a bit. >>> mp.dps = 50 >>> base = ['sqrt(2)','pi','log(2)'] >>> identify(0.25, base) '(1/4)' >>> identify(3*pi + 2*sqrt(2) + 5*log(2)/7, base) '(2*sqrt(2) + 3*pi + (5/7)*log(2))' >>> identify(exp(pi+2), base) 'exp((2 + 1*pi))' >>> identify(1/(3+sqrt(2)), base) '((3/7) + (-1/7)*sqrt(2))' >>> identify(sqrt(2)/(3*pi+4), base) 'sqrt(2)/(4 + 3*pi)' >>> identify(5**(mpf(1)/3)*pi*log(2)**2, base) '5**(1/3)*pi*log(2)**2' An example of an erroneous solution being found when too low precision is used:: >>> mp.dps = 15 >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) '((11/25) + (-158/75)*pi + (76/75)*e + (44/15)*sqrt(2))' >>> mp.dps = 50 >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) '1/(3*pi + (-4)*e + 2*sqrt(2))' **Finding approximate solutions** The tolerance ``tol`` defaults to 3/4 of the working precision. Lowering the tolerance is useful for finding approximate matches. We can for example try to generate approximations for pi:: >>> mp.dps = 15 >>> identify(pi, tol=1e-2) '(22/7)' >>> identify(pi, tol=1e-3) '(355/113)' >>> identify(pi, tol=1e-10) '(5**(339/269))/(2**(64/269)*3**(13/269)*7**(92/269))' With ``full=True``, and by supplying a few base constants, ``identify`` can generate almost endless lists of approximations for any number (the output below has been truncated to show only the first few):: >>> for p in identify(pi, ['e', 'catalan'], tol=1e-5, full=True): ... print(p) ... # doctest: +ELLIPSIS e/log((6 + (-4/3)*e)) (3**3*5*e*catalan**2)/(2*7**2) sqrt(((-13) + 1*e + 22*catalan)) log(((-6) + 24*e + 4*catalan)/e) exp(catalan*((-1/5) + (8/15)*e)) catalan*(6 + (-6)*e + 15*catalan) sqrt((5 + 26*e + (-3)*catalan))/e e*sqrt(((-27) + 2*e + 25*catalan)) log(((-1) + (-11)*e + 59*catalan)) ((3/20) + (21/20)*e + (3/20)*catalan) ... The numerical values are roughly as close to `\pi` as permitted by the specified tolerance: >>> e/log(6-4*e/3) 3.14157719846001 >>> 135*e*catalan**2/98 3.14166950419369 >>> sqrt(e-13+22*catalan) 3.14158000062992 >>> log(24*e-6+4*catalan)-1 3.14158791577159 **Symbolic processing** The output formula can be evaluated as a Python expression. Note however that if fractions (like '2/3') are present in the formula, Python's :func:`~mpmath.eval()` may erroneously perform integer division. Note also that the output is not necessarily in the algebraically simplest form:: >>> identify(sqrt(2)) '(sqrt(8)/2)' As a solution to both problems, consider using SymPy's :func:`~mpmath.sympify` to convert the formula into a symbolic expression. SymPy can be used to pretty-print or further simplify the formula symbolically:: >>> from sympy import sympify >>> sympify(identify(sqrt(2))) 2**(1/2) Sometimes :func:`~mpmath.identify` can simplify an expression further than a symbolic algorithm:: >>> from sympy import simplify >>> x = sympify('-1/(-3/2+(1/2)*5**(1/2))*(3/2-1/2*5**(1/2))**(1/2)') >>> x (3/2 - 5**(1/2)/2)**(-1/2) >>> x = simplify(x) >>> x 2/(6 - 2*5**(1/2))**(1/2) >>> mp.dps = 30 >>> x = sympify(identify(x.evalf(30))) >>> x 1/2 + 5**(1/2)/2 (In fact, this functionality is available directly in SymPy as the function :func:`~mpmath.nsimplify`, which is essentially a wrapper for :func:`~mpmath.identify`.) **Miscellaneous issues and limitations** The input `x` must be a real number. All base constants must be positive real numbers and must not be rationals or rational linear combinations of each other. The worst-case computation time grows quickly with the number of base constants. Already with 3 or 4 base constants, :func:`~mpmath.identify` may require several seconds to finish. To search for relations among a large number of constants, you should consider using :func:`~mpmath.pslq` directly. The extended transformations are applied to x, not the constants separately. As a result, ``identify`` will for example be able to recognize ``exp(2*pi+3)`` with ``pi`` given as a base constant, but not ``2*exp(pi)+3``. It will be able to recognize the latter if ``exp(pi)`` is given explicitly as a base constant. """ solutions = [] def addsolution(s): if verbose: print("Found: ", s) solutions.append(s) x = ctx.mpf(x) # Further along, x will be assumed positive if x == 0: if full: return ['0'] else: return '0' if x < 0: sol = ctx.identify(-x, constants, tol, maxcoeff, full, verbose) if sol is None: return sol if full: return ["-(%s)"%s for s in sol] else: return "-(%s)" % sol if tol: tol = ctx.mpf(tol) else: tol = ctx.eps**0.7 M = maxcoeff if constants: if isinstance(constants, dict): constants = [(ctx.mpf(v), name) for (name, v) in constants.items()] else: namespace = dict((name, getattr(ctx,name)) for name in dir(ctx)) constants = [(eval(p, namespace), p) for p in constants] else: constants = [] # We always want to find at least rational terms if 1 not in [value for (name, value) in constants]: constants = [(ctx.mpf(1), '1')] + constants # PSLQ with simple algebraic and functional transformations for ft, ftn, red in transforms: for c, cn in constants: if red and cn == '1': continue t = ft(ctx,x,c) # Prevent exponential transforms from wreaking havoc if abs(t) > M**2 or abs(t) < tol: continue # Linear combination of base constants r = ctx.pslq([t] + [a[0] for a in constants], tol, M) s = None if r is not None and max(abs(uw) for uw in r) <= M and r[0]: s = pslqstring(r, constants) # Quadratic algebraic numbers else: q = ctx.pslq([ctx.one, t, t**2], tol, M) if q is not None and len(q) == 3 and q[2]: aa, bb, cc = q if max(abs(aa),abs(bb),abs(cc)) <= M: s = quadraticstring(ctx,t,aa,bb,cc) if s: if cn == '1' and ('/$c' in ftn): s = ftn.replace('$y', s).replace('/$c', '') else: s = ftn.replace('$y', s).replace('$c', cn) addsolution(s) if not full: return solutions[0] if verbose: print(".") # Check for a direct multiplicative formula if x != 1: # Allow fractional powers of fractions ilogs = [2,3,5,7] # Watch out for existing fractional powers of fractions logs = [] for a, s in constants: if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1)) for i in ilogs): logs.append((ctx.ln(a), s)) logs = [(ctx.ln(i),str(i)) for i in ilogs] + logs r = ctx.pslq([ctx.ln(x)] + [a[0] for a in logs], tol, M) if r is not None and max(abs(uw) for uw in r) <= M and r[0]: addsolution(prodstring(r, logs)) if not full: return solutions[0] if full: return sorted(solutions, key=len) else: return None IdentificationMethods.pslq = pslq IdentificationMethods.findpoly = findpoly IdentificationMethods.identify = identify if __name__ == '__main__': import doctest doctest.testmod() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/0000755000175000017500000000000012014170666023470 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/exec_py2.py0000644000175000017500000000010712014170666025556 0ustar georgeskgeorgeskdef exec_(string, globals, locals): exec string in globals, locals wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/backend.py0000644000175000017500000000551112014170666025433 0ustar georgeskgeorgeskimport os import sys #----------------------------------------------------------------------------# # Support GMPY for high-speed large integer arithmetic. # # # # To allow an external module to handle arithmetic, we need to make sure # # that all high-precision variables are declared of the correct type. MPZ # # is the constructor for the high-precision type. It defaults to Python's # # long type but can be assinged another type, typically gmpy.mpz. # # # # MPZ must be used for the mantissa component of an mpf and must be used # # for internal fixed-point operations. # # # # Side-effects # # 1) "is" cannot be used to test for special values. Must use "==". # # 2) There are bugs in GMPY prior to v1.02 so we must use v1.03 or later. # #----------------------------------------------------------------------------# # So we can import it from this module gmpy = None sage = None sage_utils = None try: xrange python3 = False except NameError: python3 = True BACKEND = 'python' if not python3: MPZ = long xrange = xrange basestring = basestring from .exec_py2 import exec_ else: MPZ = int xrange = range basestring = str from .exec_py3 import exec_ # Define constants for calculating hash on Python 3.2. if sys.version >= "3.2": HASH_MODULUS = sys.hash_info.modulus if sys.hash_info.width == 32: HASH_BITS = 31 else: HASH_BITS = 61 else: HASH_MODULUS = None HASH_BITS = None if 'MPMATH_NOGMPY' not in os.environ: try: try: import gmpy2 as gmpy except ImportError: try: import gmpy except ImportError: raise ImportError if gmpy.version() >= '1.03': BACKEND = 'gmpy' MPZ = gmpy.mpz except: pass if 'MPMATH_NOSAGE' not in os.environ: try: import sage.all import sage.libs.mpmath.utils as _sage_utils sage = sage.all sage_utils = _sage_utils BACKEND = 'sage' MPZ = sage.Integer except: pass if 'MPMATH_STRICT' in os.environ: STRICT = True else: STRICT = False MPZ_TYPE = type(MPZ(0)) MPZ_ZERO = MPZ(0) MPZ_ONE = MPZ(1) MPZ_TWO = MPZ(2) MPZ_THREE = MPZ(3) MPZ_FIVE = MPZ(5) try: if BACKEND == 'python': int_types = (int, long) else: int_types = (int, long, MPZ_TYPE) except NameError: if BACKEND == 'python': int_types = (int,) else: int_types = (int, MPZ_TYPE) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/libhyper.py0000644000175000017500000010732412014170666025667 0ustar georgeskgeorgesk""" This module implements computation of hypergeometric and related functions. In particular, it provides code for generic summation of hypergeometric series. Optimized versions for various special cases are also provided. """ import operator import math from .backend import MPZ_ZERO, MPZ_ONE, BACKEND, xrange, exec_ from .libintmath import gcd from .libmpf import (\ ComplexResult, round_fast, round_nearest, negative_rnd, bitcount, to_fixed, from_man_exp, from_int, to_int, from_rational, fzero, fone, fnone, ftwo, finf, fninf, fnan, mpf_sign, mpf_add, mpf_abs, mpf_pos, mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_min_max, mpf_perturb, mpf_neg, mpf_shift, mpf_sub, mpf_mul, mpf_div, sqrt_fixed, mpf_sqrt, mpf_rdiv_int, mpf_pow_int, to_rational, ) from .libelefun import (\ mpf_pi, mpf_exp, mpf_log, pi_fixed, mpf_cos_sin, mpf_cos, mpf_sin, mpf_sqrt, agm_fixed, ) from .libmpc import (\ mpc_one, mpc_sub, mpc_mul_mpf, mpc_mul, mpc_neg, complex_int_pow, mpc_div, mpc_add_mpf, mpc_sub_mpf, mpc_log, mpc_add, mpc_pos, mpc_shift, mpc_is_infnan, mpc_zero, mpc_sqrt, mpc_abs, mpc_mpf_div, mpc_square, mpc_exp ) from .libintmath import ifac from .gammazeta import mpf_gamma_int, mpf_euler, euler_fixed class NoConvergence(Exception): pass #-----------------------------------------------------------------------# # # # Generic hypergeometric series # # # #-----------------------------------------------------------------------# """ TODO: 1. proper mpq parsing 2. imaginary z special-cased (also: rational, integer?) 3. more clever handling of series that don't converge because of stupid upwards rounding 4. checking for cancellation """ def make_hyp_summator(key): """ Returns a function that sums a generalized hypergeometric series, for given parameter types (integer, rational, real, complex). """ p, q, param_types, ztype = key pstring = "".join(param_types) fname = "hypsum_%i_%i_%s_%s_%s" % (p, q, pstring[:p], pstring[p:], ztype) #print "generating hypsum", fname have_complex_param = 'C' in param_types have_complex_arg = ztype == 'C' have_complex = have_complex_param or have_complex_arg source = [] add = source.append aint = [] arat = [] bint = [] brat = [] areal = [] breal = [] acomplex = [] bcomplex = [] #add("wp = prec + 40") add("MAX = kwargs.get('maxterms', wp*100)") add("HIGH = MPZ_ONE<= 0:") add(" ZRE = xm << offset") add("else:") add(" ZRE = xm >> (-offset)") if have_complex_arg: add("offset = ye + wp") add("if offset >= 0:") add(" ZIM = ym << offset") add("else:") add(" ZIM = ym >> (-offset)") for i, flag in enumerate(param_types): W = ["A", "B"][i >= p] if flag == 'Z': ([aint,bint][i >= p]).append(i) add("%sINT_%i = coeffs[%i]" % (W, i, i)) elif flag == 'Q': ([arat,brat][i >= p]).append(i) add("%sP_%i, %sQ_%i = coeffs[%i]._mpq_" % (W, i, W, i, i)) elif flag == 'R': ([areal,breal][i >= p]).append(i) add("xsign, xm, xe, xbc = coeffs[%i]._mpf_" % i) add("if xsign: xm = -xm") add("offset = xe + wp") add("if offset >= 0:") add(" %sREAL_%i = xm << offset" % (W, i)) add("else:") add(" %sREAL_%i = xm >> (-offset)" % (W, i)) elif flag == 'C': ([acomplex,bcomplex][i >= p]).append(i) add("__re, __im = coeffs[%i]._mpc_" % i) add("xsign, xm, xe, xbc = __re") add("if xsign: xm = -xm") add("ysign, ym, ye, ybc = __im") add("if ysign: ym = -ym") add("offset = xe + wp") add("if offset >= 0:") add(" %sCRE_%i = xm << offset" % (W, i)) add("else:") add(" %sCRE_%i = xm >> (-offset)" % (W, i)) add("offset = ye + wp") add("if offset >= 0:") add(" %sCIM_%i = ym << offset" % (W, i)) add("else:") add(" %sCIM_%i = ym >> (-offset)" % (W, i)) else: raise ValueError l_areal = len(areal) l_breal = len(breal) cancellable_real = min(l_areal, l_breal) noncancellable_real_num = areal[cancellable_real:] noncancellable_real_den = breal[cancellable_real:] # LOOP add("for n in xrange(1,10**8):") add(" if n in magnitude_check:") add(" p_mag = bitcount(abs(PRE))") if have_complex: add(" p_mag = max(p_mag, bitcount(abs(PIM)))") add(" magnitude_check[n] = wp-p_mag") # Real factors multiplier = " * ".join(["AINT_#".replace("#", str(i)) for i in aint] + \ ["AP_#".replace("#", str(i)) for i in arat] + \ ["BQ_#".replace("#", str(i)) for i in brat]) divisor = " * ".join(["BINT_#".replace("#", str(i)) for i in bint] + \ ["BP_#".replace("#", str(i)) for i in brat] + \ ["AQ_#".replace("#", str(i)) for i in arat] + ["n"]) if multiplier: add(" mul = " + multiplier) add(" div = " + divisor) # Check for singular terms add(" if not div:") if multiplier: add(" if not mul:") add(" break") add(" raise ZeroDivisionError") # Update product if have_complex: # TODO: when there are several real parameters and just a few complex # (maybe just the complex argument), we only need to do about # half as many ops if we accumulate the real factor in a single real variable for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) for k in range(cancellable_real): add(" PIM = PIM * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) for i in noncancellable_real_num: add(" PIM = (PIM * AREAL_#) >> wp".replace("#", str(i))) for i in noncancellable_real_den: add(" PIM = (PIM << wp) // BREAL_#".replace("#", str(i))) if multiplier: if have_complex_arg: add(" PRE, PIM = (mul*(PRE*ZRE-PIM*ZIM))//div, (mul*(PIM*ZRE+PRE*ZIM))//div") add(" PRE >>= wp") add(" PIM >>= wp") else: add(" PRE = ((mul * PRE * ZRE) >> wp) // div") add(" PIM = ((mul * PIM * ZRE) >> wp) // div") else: if have_complex_arg: add(" PRE, PIM = (PRE*ZRE-PIM*ZIM)//div, (PIM*ZRE+PRE*ZIM)//div") add(" PRE >>= wp") add(" PIM >>= wp") else: add(" PRE = ((PRE * ZRE) >> wp) // div") add(" PIM = ((PIM * ZRE) >> wp) // div") for i in acomplex: add(" PRE, PIM = PRE*ACRE_#-PIM*ACIM_#, PIM*ACRE_#+PRE*ACIM_#".replace("#", str(i))) add(" PRE >>= wp") add(" PIM >>= wp") for i in bcomplex: add(" mag = BCRE_#*BCRE_#+BCIM_#*BCIM_#".replace("#", str(i))) add(" re = PRE*BCRE_# + PIM*BCIM_#".replace("#", str(i))) add(" im = PIM*BCRE_# - PRE*BCIM_#".replace("#", str(i))) add(" PRE = (re << wp) // mag".replace("#", str(i))) add(" PIM = (im << wp) // mag".replace("#", str(i))) else: for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) if multiplier: add(" PRE = ((PRE * mul * ZRE) >> wp) // div") else: add(" PRE = ((PRE * ZRE) >> wp) // div") # Add product to sum if have_complex: add(" SRE += PRE") add(" SIM += PIM") add(" if (HIGH > PRE > LOW) and (HIGH > PIM > LOW):") add(" break") else: add(" SRE += PRE") add(" if HIGH > PRE > LOW:") add(" break") #add(" from mpmath import nprint, log, ldexp") #add(" nprint([n, log(abs(PRE),2), ldexp(PRE,-wp)])") add(" if n > MAX:") add(" raise NoConvergence('Hypergeometric series converges too slowly. Try increasing maxterms.')") # +1 all parameters for next loop for i in aint: add(" AINT_# += 1".replace("#", str(i))) for i in bint: add(" BINT_# += 1".replace("#", str(i))) for i in arat: add(" AP_# += AQ_#".replace("#", str(i))) for i in brat: add(" BP_# += BQ_#".replace("#", str(i))) for i in areal: add(" AREAL_# += one".replace("#", str(i))) for i in breal: add(" BREAL_# += one".replace("#", str(i))) for i in acomplex: add(" ACRE_# += one".replace("#", str(i))) for i in bcomplex: add(" BCRE_# += one".replace("#", str(i))) if have_complex: add("a = from_man_exp(SRE, -wp, prec, 'n')") add("b = from_man_exp(SIM, -wp, prec, 'n')") add("if SRE:") add(" if SIM:") add(" magn = max(a[2]+a[3], b[2]+b[3])") add(" else:") add(" magn = a[2]+a[3]") add("elif SIM:") add(" magn = b[2]+b[3]") add("else:") add(" magn = -wp+1") add("return (a, b), True, magn") else: add("a = from_man_exp(SRE, -wp, prec, 'n')") add("if SRE:") add(" magn = a[2]+a[3]") add("else:") add(" magn = -wp+1") add("return a, False, magn") source = "\n".join((" " + line) for line in source) source = ("def %s(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs):\n" % fname) + source namespace = {} exec_(source, globals(), namespace) #print source return source, namespace[fname] if BACKEND == 'sage': def make_hyp_summator(key): """ Returns a function that sums a generalized hypergeometric series, for given parameter types (integer, rational, real, complex). """ from sage.libs.mpmath.ext_main import hypsum_internal p, q, param_types, ztype = key def _hypsum(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs): return hypsum_internal(p, q, param_types, ztype, coeffs, z, prec, wp, epsshift, magnitude_check, kwargs) return "(none)", _hypsum #-----------------------------------------------------------------------# # # # Error functions # # # #-----------------------------------------------------------------------# # TODO: mpf_erf should call mpf_erfc when appropriate (currently # only the converse delegation is implemented) def mpf_erf(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fzero if x == finf: return fone if x== fninf: return fnone return fnan size = exp + bc lg = math.log # The approximation erf(x) = 1 is accurate to > x^2 * log(e,2) bits if size > 3 and 2*(size-1) + 0.528766 > lg(prec,2): if sign: return mpf_perturb(fnone, 0, prec, rnd) else: return mpf_perturb(fone, 1, prec, rnd) # erf(x) ~ 2*x/sqrt(pi) close to 0 if size < -prec: # 2*x x = mpf_shift(x,1) c = mpf_sqrt(mpf_pi(prec+20), prec+20) # TODO: interval rounding return mpf_div(x, c, prec, rnd) wp = prec + abs(size) + 25 # Taylor series for erf, fixed-point summation t = abs(to_fixed(x, wp)) t2 = (t*t) >> wp s, term, k = t, 12345, 1 while term: t = ((t * t2) >> wp) // k term = t // (2*k+1) if k & 1: s -= term else: s += term k += 1 s = (s << (wp+1)) // sqrt_fixed(pi_fixed(wp), wp) if sign: s = -s return from_man_exp(s, -wp, prec, rnd) # If possible, we use the asymptotic series for erfc. # This is an alternating divergent asymptotic series, so # the error is at most equal to the first omitted term. # Here we check if the smallest term is small enough # for a given x and precision def erfc_check_series(x, prec): n = to_int(x) if n**2 * 1.44 > prec: return True return False def mpf_erfc(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fone if x == finf: return fzero if x == fninf: return ftwo return fnan wp = prec + 20 mag = bc+exp # Preserve full accuracy when exponent grows huge wp += max(0, 2*mag) regular_erf = sign or mag < 2 if regular_erf or not erfc_check_series(x, wp): if regular_erf: return mpf_sub(fone, mpf_erf(x, prec+10, negative_rnd[rnd]), prec, rnd) # 1-erf(x) ~ exp(-x^2), increase prec to deal with cancellation n = to_int(x)+1 return mpf_sub(fone, mpf_erf(x, prec + int(n**2*1.44) + 10), prec, rnd) s = term = MPZ_ONE << wp term_prev = 0 t = (2 * to_fixed(x, wp) ** 2) >> wp k = 1 while 1: term = ((term * (2*k - 1)) << wp) // t if k > 4 and term > term_prev or not term: break if k & 1: s -= term else: s += term term_prev = term #print k, to_str(from_man_exp(term, -wp, 50), 10) k += 1 s = (s << wp) // sqrt_fixed(pi_fixed(wp), wp) s = from_man_exp(s, -wp, wp) z = mpf_exp(mpf_neg(mpf_mul(x,x,wp),wp),wp) y = mpf_div(mpf_mul(z, s, wp), x, prec, rnd) return y #-----------------------------------------------------------------------# # # # Exponential integrals # # # #-----------------------------------------------------------------------# def ei_taylor(x, prec): s = t = x k = 2 while t: t = ((t*x) >> prec) // k s += t // k k += 1 return s def complex_ei_taylor(zre, zim, prec): _abs = abs sre = tre = zre sim = tim = zim k = 2 while _abs(tre) + _abs(tim) > 5: tre, tim = ((tre*zre-tim*zim)//k)>>prec, ((tre*zim+tim*zre)//k)>>prec sre += tre // k sim += tim // k k += 1 return sre, sim def ei_asymptotic(x, prec): one = MPZ_ONE << prec x = t = ((one << prec) // x) s = one + x k = 2 while t: t = (k*t*x) >> prec s += t k += 1 return s def complex_ei_asymptotic(zre, zim, prec): _abs = abs one = MPZ_ONE << prec M = (zim*zim + zre*zre) >> prec # 1 / z xre = tre = (zre << prec) // M xim = tim = ((-zim) << prec) // M sre = one + xre sim = xim k = 2 while _abs(tre) + _abs(tim) > 1000: #print tre, tim tre, tim = ((tre*xre-tim*xim)*k)>>prec, ((tre*xim+tim*xre)*k)>>prec sre += tre sim += tim k += 1 if k > prec: raise NoConvergence return sre, sim def mpf_ei(x, prec, rnd=round_fast, e1=False): if e1: x = mpf_neg(x) sign, man, exp, bc = x if e1 and not sign: if x == fzero: return finf raise ComplexResult("E1(x) for x < 0") if man: xabs = 0, man, exp, bc xmag = exp+bc wp = prec + 20 can_use_asymp = xmag > wp if not can_use_asymp: if exp >= 0: xabsint = man << exp else: xabsint = man >> (-exp) can_use_asymp = xabsint > int(wp*0.693) + 10 if can_use_asymp: if xmag > wp: v = fone else: v = from_man_exp(ei_asymptotic(to_fixed(x, wp), wp), -wp) v = mpf_mul(v, mpf_exp(x, wp), wp) v = mpf_div(v, x, prec, rnd) else: wp += 2*int(to_int(xabs)) u = to_fixed(x, wp) v = ei_taylor(u, wp) + euler_fixed(wp) t1 = from_man_exp(v,-wp) t2 = mpf_log(xabs,wp) v = mpf_add(t1, t2, prec, rnd) else: if x == fzero: v = fninf elif x == finf: v = finf elif x == fninf: v = fzero else: v = fnan if e1: v = mpf_neg(v) return v def mpc_ei(z, prec, rnd=round_fast, e1=False): if e1: z = mpc_neg(z) a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero: if e1: x = mpf_neg(mpf_ei(a, prec, rnd)) if not asign: y = mpf_neg(mpf_pi(prec, rnd)) else: y = fzero return x, y else: return mpf_ei(a, prec, rnd), fzero if a != fzero: if not aman or not bman: return (fnan, fnan) wp = prec + 40 amag = aexp+abc bmag = bexp+bbc zmag = max(amag, bmag) can_use_asymp = zmag > wp if not can_use_asymp: zabsint = abs(to_int(a)) + abs(to_int(b)) can_use_asymp = zabsint > int(wp*0.693) + 20 try: if can_use_asymp: if zmag > wp: v = fone, fzero else: zre = to_fixed(a, wp) zim = to_fixed(b, wp) vre, vim = complex_ei_asymptotic(zre, zim, wp) v = from_man_exp(vre, -wp), from_man_exp(vim, -wp) v = mpc_mul(v, mpc_exp(z, wp), wp) v = mpc_div(v, z, wp) if e1: v = mpc_neg(v, prec, rnd) else: x, y = v if bsign: v = mpf_pos(x, prec, rnd), mpf_sub(y, mpf_pi(wp), prec, rnd) else: v = mpf_pos(x, prec, rnd), mpf_add(y, mpf_pi(wp), prec, rnd) return v except NoConvergence: pass #wp += 2*max(0,zmag) wp += 2*int(to_int(mpc_abs(z, 5))) zre = to_fixed(a, wp) zim = to_fixed(b, wp) vre, vim = complex_ei_taylor(zre, zim, wp) vre += euler_fixed(wp) v = from_man_exp(vre,-wp), from_man_exp(vim,-wp) if e1: u = mpc_log(mpc_neg(z),wp) else: u = mpc_log(z,wp) v = mpc_add(v, u, prec, rnd) if e1: v = mpc_neg(v) return v def mpf_e1(x, prec, rnd=round_fast): return mpf_ei(x, prec, rnd, True) def mpc_e1(x, prec, rnd=round_fast): return mpc_ei(x, prec, rnd, True) def mpf_expint(n, x, prec, rnd=round_fast, gamma=False): """ E_n(x), n an integer, x real With gamma=True, computes Gamma(n,x) (upper incomplete gamma function) Returns (real, None) if real, otherwise (real, imag) The imaginary part is an optional branch cut term """ sign, man, exp, bc = x if not man: if gamma: if x == fzero: # Actually gamma function pole if n <= 0: return finf, None return mpf_gamma_int(n, prec, rnd), None if x == finf: return fzero, None # TODO: could return finite imaginary value at -inf return fnan, fnan else: if x == fzero: if n > 1: return from_rational(1, n-1, prec, rnd), None else: return finf, None if x == finf: return fzero, None return fnan, fnan n_orig = n if gamma: n = 1-n wp = prec + 20 xmag = exp + bc # Beware of near-poles if xmag < -10: raise NotImplementedError nmag = bitcount(abs(n)) have_imag = n > 0 and sign negx = mpf_neg(x) # Skip series if direct convergence if n == 0 or 2*nmag - xmag < -wp: if gamma: v = mpf_exp(negx, wp) re = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), prec, rnd) else: v = mpf_exp(negx, wp) re = mpf_div(v, x, prec, rnd) else: # Finite number of terms, or... can_use_asymptotic_series = -3*wp < n <= 0 # ...large enough? if not can_use_asymptotic_series: xi = abs(to_int(x)) m = min(max(1, xi-n), 2*wp) siz = -n*nmag + (m+n)*bitcount(abs(m+n)) - m*xmag - (144*m//100) tol = -wp-10 can_use_asymptotic_series = siz < tol if can_use_asymptotic_series: r = ((-MPZ_ONE) << (wp+wp)) // to_fixed(x, wp) m = n t = r*m s = MPZ_ONE << wp while m and t: s += t m += 1 t = (m*r*t) >> wp v = mpf_exp(negx, wp) if gamma: # ~ exp(-x) * x^(n-1) * (1 + ...) v = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), wp) else: # ~ exp(-x)/x * (1 + ...) v = mpf_div(v, x, wp) re = mpf_mul(v, from_man_exp(s, -wp), prec, rnd) elif n == 1: re = mpf_neg(mpf_ei(negx, prec, rnd)) elif n > 0 and n < 3*wp: T1 = mpf_neg(mpf_ei(negx, wp)) if gamma: if n_orig & 1: T1 = mpf_neg(T1) else: T1 = mpf_mul(T1, mpf_pow_int(negx, n-1, wp), wp) r = t = to_fixed(x, wp) facs = [1] * (n-1) for k in range(1,n-1): facs[k] = facs[k-1] * k facs = facs[::-1] s = facs[0] << wp for k in range(1, n-1): if k & 1: s -= facs[k] * t else: s += facs[k] * t t = (t*r) >> wp T2 = from_man_exp(s, -wp, wp) T2 = mpf_mul(T2, mpf_exp(negx, wp)) if gamma: T2 = mpf_mul(T2, mpf_pow_int(x, n_orig, wp), wp) R = mpf_add(T1, T2) re = mpf_div(R, from_int(ifac(n-1)), prec, rnd) else: raise NotImplementedError if have_imag: M = from_int(-ifac(n-1)) if gamma: im = mpf_div(mpf_pi(wp), M, prec, rnd) else: im = mpf_div(mpf_mul(mpf_pi(wp), mpf_pow_int(negx, n_orig-1, wp), wp), M, prec, rnd) return re, im else: return re, None def mpf_ci_si_taylor(x, wp, which=0): """ 0 - Ci(x) - (euler+log(x)) 1 - Si(x) """ x = to_fixed(x, wp) x2 = -(x*x) >> wp if which == 0: s, t, k = 0, (MPZ_ONE<>wp s += t//k k += 2 return from_man_exp(s, -wp) def mpc_ci_si_taylor(re, im, wp, which=0): # The following code is only designed for small arguments, # and not too small arguments (for relative accuracy) if re[1]: mag = re[2]+re[3] elif im[1]: mag = im[2]+im[3] if im[1]: mag = max(mag, im[2]+im[3]) if mag > 2 or mag < -wp: raise NotImplementedError wp += (2-mag) zre = to_fixed(re, wp) zim = to_fixed(im, wp) z2re = (zim*zim-zre*zre)>>wp z2im = (-2*zre*zim)>>wp tre = zre tim = zim one = MPZ_ONE< 2: f = k*(k-1) tre, tim = ((tre*z2re-tim*z2im)//f)>>wp, ((tre*z2im+tim*z2re)//f)>>wp sre += tre//k sim += tim//k k += 2 return from_man_exp(sre, -wp), from_man_exp(sim, -wp) def mpf_ci_si(x, prec, rnd=round_fast, which=2): """ Calculation of Ci(x), Si(x) for real x. which = 0 -- returns (Ci(x), -) which = 1 -- returns (Si(x), -) which = 2 -- returns (Ci(x), Si(x)) Note: if x < 0, Ci(x) needs an additional imaginary term, pi*i. """ wp = prec + 20 sign, man, exp, bc = x ci, si = None, None if not man: if x == fzero: return (fninf, fzero) if x == fnan: return (x, x) ci = fzero if which != 0: if x == finf: si = mpf_shift(mpf_pi(prec, rnd), -1) if x == fninf: si = mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) return (ci, si) # For small x: Ci(x) ~ euler + log(x), Si(x) ~ x mag = exp+bc if mag < -wp: if which != 0: si = mpf_perturb(x, 1-sign, prec, rnd) if which != 1: y = mpf_euler(wp) xabs = mpf_abs(x) ci = mpf_add(y, mpf_log(xabs, wp), prec, rnd) return ci, si # For huge x: Ci(x) ~ sin(x)/x, Si(x) ~ pi/2 elif mag > wp: if which != 0: if sign: si = mpf_neg(mpf_pi(prec, negative_rnd[rnd])) else: si = mpf_pi(prec, rnd) si = mpf_shift(si, -1) if which != 1: ci = mpf_div(mpf_sin(x, wp), x, prec, rnd) return ci, si else: wp += abs(mag) # Use an asymptotic series? The smallest value of n!/x^n # occurs for n ~ x, where the magnitude is ~ exp(-x). asymptotic = mag-1 > math.log(wp, 2) # Case 1: convergent series near 0 if not asymptotic: if which != 0: si = mpf_pos(mpf_ci_si_taylor(x, wp, 1), prec, rnd) if which != 1: ci = mpf_ci_si_taylor(x, wp, 0) ci = mpf_add(ci, mpf_euler(wp), wp) ci = mpf_add(ci, mpf_log(mpf_abs(x), wp), prec, rnd) return ci, si x = mpf_abs(x) # Case 2: asymptotic series for x >> 1 xf = to_fixed(x, wp) xr = (MPZ_ONE<<(2*wp)) // xf # 1/x s1 = (MPZ_ONE << wp) s2 = xr t = xr k = 2 while t: t = -t t = (t*xr*k)>>wp k += 1 s1 += t t = (t*xr*k)>>wp k += 1 s2 += t s1 = from_man_exp(s1, -wp) s2 = from_man_exp(s2, -wp) s1 = mpf_div(s1, x, wp) s2 = mpf_div(s2, x, wp) cos, sin = mpf_cos_sin(x, wp) # Ci(x) = sin(x)*s1-cos(x)*s2 # Si(x) = pi/2-cos(x)*s1-sin(x)*s2 if which != 0: si = mpf_add(mpf_mul(cos, s1), mpf_mul(sin, s2), wp) si = mpf_sub(mpf_shift(mpf_pi(wp), -1), si, wp) if sign: si = mpf_neg(si) si = mpf_pos(si, prec, rnd) if which != 1: ci = mpf_sub(mpf_mul(sin, s1), mpf_mul(cos, s2), prec, rnd) return ci, si def mpf_ci(x, prec, rnd=round_fast): if mpf_sign(x) < 0: raise ComplexResult return mpf_ci_si(x, prec, rnd, 0)[0] def mpf_si(x, prec, rnd=round_fast): return mpf_ci_si(x, prec, rnd, 1)[1] def mpc_ci(z, prec, rnd=round_fast): re, im = z if im == fzero: ci = mpf_ci_si(re, prec, rnd, 0)[0] if mpf_sign(re) < 0: return (ci, mpf_pi(prec, rnd)) return (ci, fzero) wp = prec + 20 cre, cim = mpc_ci_si_taylor(re, im, wp, 0) cre = mpf_add(cre, mpf_euler(wp), wp) ci = mpc_add((cre, cim), mpc_log(z, wp), prec, rnd) return ci def mpc_si(z, prec, rnd=round_fast): re, im = z if im == fzero: return (mpf_ci_si(re, prec, rnd, 1)[1], fzero) wp = prec + 20 z = mpc_ci_si_taylor(re, im, wp, 1) return mpc_pos(z, prec, rnd) #-----------------------------------------------------------------------# # # # Bessel functions # # # #-----------------------------------------------------------------------# # A Bessel function of the first kind of integer order, J_n(x), is # given by the power series # oo # ___ k 2 k + n # \ (-1) / x \ # J_n(x) = ) ----------- | - | # /___ k! (k + n)! \ 2 / # k = 0 # Simplifying the quotient between two successive terms gives the # ratio x^2 / (-4*k*(k+n)). Hence, we only need one full-precision # multiplication and one division by a small integer per term. # The complex version is very similar, the only difference being # that the multiplication is actually 4 multiplies. # In the general case, we have # J_v(x) = (x/2)**v / v! * 0F1(v+1, (-1/4)*z**2) # TODO: for extremely large x, we could use an asymptotic # trigonometric approximation. # TODO: recompute at higher precision if the fixed-point mantissa # is very small def mpf_besseljn(n, x, prec, rounding=round_fast): prec += 50 negate = n < 0 and n & 1 mag = x[2]+x[3] n = abs(n) wp = prec + 20 + n*bitcount(n) if mag < 0: wp -= n * mag x = to_fixed(x, wp) x2 = (x**2) >> wp if not n: s = t = MPZ_ONE << wp else: s = t = (x**n // ifac(n)) >> ((n-1)*wp + n) k = 1 while t: t = ((t * x2) // (-4*k*(k+n))) >> wp s += t k += 1 if negate: s = -s return from_man_exp(s, -wp, prec, rounding) def mpc_besseljn(n, z, prec, rounding=round_fast): negate = n < 0 and n & 1 n = abs(n) origprec = prec zre, zim = z mag = max(zre[2]+zre[3], zim[2]+zim[3]) prec += 20 + n*bitcount(n) + abs(mag) if mag < 0: prec -= n * mag zre = to_fixed(zre, prec) zim = to_fixed(zim, prec) z2re = (zre**2 - zim**2) >> prec z2im = (zre*zim) >> (prec-1) if not n: sre = tre = MPZ_ONE << prec sim = tim = MPZ_ZERO else: re, im = complex_int_pow(zre, zim, n) sre = tre = (re // ifac(n)) >> ((n-1)*prec + n) sim = tim = (im // ifac(n)) >> ((n-1)*prec + n) k = 1 while abs(tre) + abs(tim) > 3: p = -4*k*(k+n) tre, tim = tre*z2re - tim*z2im, tim*z2re + tre*z2im tre = (tre // p) >> prec tim = (tim // p) >> prec sre += tre sim += tim k += 1 if negate: sre = -sre sim = -sim re = from_man_exp(sre, -prec, origprec, rounding) im = from_man_exp(sim, -prec, origprec, rounding) return (re, im) def mpf_agm(a, b, prec, rnd=round_fast): """ Computes the arithmetic-geometric mean agm(a,b) for nonnegative mpf values a, b. """ asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if asign or bsign: raise ComplexResult("agm of a negative number") # Handle inf, nan or zero in either operand if not (aman and bman): if a == fnan or b == fnan: return fnan if a == finf: if b == fzero: return fnan return finf if b == finf: if a == fzero: return fnan return finf # agm(0,x) = agm(x,0) = 0 return fzero wp = prec + 20 amag = aexp+abc bmag = bexp+bbc mag_delta = amag - bmag # Reduce to roughly the same magnitude using floating-point AGM abs_mag_delta = abs(mag_delta) if abs_mag_delta > 10: while abs_mag_delta > 10: a, b = mpf_shift(mpf_add(a,b,wp),-1), \ mpf_sqrt(mpf_mul(a,b,wp),wp) abs_mag_delta //= 2 asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b amag = aexp+abc bmag = bexp+bbc mag_delta = amag - bmag #print to_float(a), to_float(b) # Use agm(a,b) = agm(x*a,x*b)/x to obtain a, b ~= 1 min_mag = min(amag,bmag) max_mag = max(amag,bmag) n = 0 # If too small, we lose precision when going to fixed-point if min_mag < -8: n = -min_mag # If too large, we waste time using fixed-point with large numbers elif max_mag > 20: n = -max_mag if n: a = mpf_shift(a, n) b = mpf_shift(b, n) #print to_float(a), to_float(b) af = to_fixed(a, wp) bf = to_fixed(b, wp) g = agm_fixed(af, bf, wp) return from_man_exp(g, -wp-n, prec, rnd) def mpf_agm1(a, prec, rnd=round_fast): """ Computes the arithmetic-geometric mean agm(1,a) for a nonnegative mpf value a. """ return mpf_agm(fone, a, prec, rnd) def mpc_agm(a, b, prec, rnd=round_fast): """ Complex AGM. TODO: * check that convergence works as intended * optimize * select a nonarbitrary branch """ if mpc_is_infnan(a) or mpc_is_infnan(b): return fnan, fnan if mpc_zero in (a, b): return fzero, fzero if mpc_neg(a) == b: return fzero, fzero wp = prec+20 eps = mpf_shift(fone, -wp+10) while 1: a1 = mpc_shift(mpc_add(a, b, wp), -1) b1 = mpc_sqrt(mpc_mul(a, b, wp), wp) a, b = a1, b1 size = mpf_min_max([mpc_abs(a,10), mpc_abs(b,10)])[1] err = mpc_abs(mpc_sub(a, b, 10), 10) if size == fzero or mpf_lt(err, mpf_mul(eps, size)): return a def mpc_agm1(a, prec, rnd=round_fast): return mpc_agm(mpc_one, a, prec, rnd) def mpf_ellipk(x, prec, rnd=round_fast): if not x[1]: if x == fzero: return mpf_shift(mpf_pi(prec, rnd), -1) if x == fninf: return fzero if x == fnan: return x if x == fone: return finf # TODO: for |x| << 1/2, one could use fall back to # pi/2 * hyp2f1_rat((1,2),(1,2),(1,1), x) wp = prec + 15 # Use K(x) = pi/2/agm(1,a) where a = sqrt(1-x) # The sqrt raises ComplexResult if x > 0 a = mpf_sqrt(mpf_sub(fone, x, wp), wp) v = mpf_agm1(a, wp) r = mpf_div(mpf_pi(wp), v, prec, rnd) return mpf_shift(r, -1) def mpc_ellipk(z, prec, rnd=round_fast): re, im = z if im == fzero: if re == finf: return mpc_zero if mpf_le(re, fone): return mpf_ellipk(re, prec, rnd), fzero wp = prec + 15 a = mpc_sqrt(mpc_sub(mpc_one, z, wp), wp) v = mpc_agm1(a, wp) r = mpc_mpf_div(mpf_pi(wp), v, prec, rnd) return mpc_shift(r, -1) def mpf_ellipe(x, prec, rnd=round_fast): # http://functions.wolfram.com/EllipticIntegrals/ # EllipticK/20/01/0001/ # E = (1-m)*(K'(m)*2*m + K(m)) sign, man, exp, bc = x if not man: if x == fzero: return mpf_shift(mpf_pi(prec, rnd), -1) if x == fninf: return finf if x == fnan: return x if x == finf: raise ComplexResult if x == fone: return fone wp = prec+20 mag = exp+bc if mag < -wp: return mpf_shift(mpf_pi(prec, rnd), -1) # Compute a finite difference for K' p = max(mag, 0) - wp h = mpf_shift(fone, p) K = mpf_ellipk(x, 2*wp) Kh = mpf_ellipk(mpf_sub(x, h), 2*wp) Kdiff = mpf_shift(mpf_sub(K, Kh), -p) t = mpf_sub(fone, x) b = mpf_mul(Kdiff, mpf_shift(x,1), wp) return mpf_mul(t, mpf_add(K, b), prec, rnd) def mpc_ellipe(z, prec, rnd=round_fast): re, im = z if im == fzero: if re == finf: return (fzero, finf) if mpf_le(re, fone): return mpf_ellipe(re, prec, rnd), fzero wp = prec + 15 mag = mpc_abs(z, 1) p = max(mag[2]+mag[3], 0) - wp h = mpf_shift(fone, p) K = mpc_ellipk(z, 2*wp) Kh = mpc_ellipk(mpc_add_mpf(z, h, 2*wp), 2*wp) Kdiff = mpc_shift(mpc_sub(Kh, K, wp), -p) t = mpc_sub(mpc_one, z, wp) b = mpc_mul(Kdiff, mpc_shift(z,1), wp) return mpc_mul(t, mpc_add(K, b, wp), prec, rnd) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/libintmath.py0000644000175000017500000003622112014170666026201 0ustar georgeskgeorgesk""" Utility functions for integer math. TODO: rename, cleanup, perhaps move the gmpy wrapper code here from settings.py """ import math from bisect import bisect from .backend import xrange from .backend import BACKEND, gmpy, sage, sage_utils, MPZ, MPZ_ONE, MPZ_ZERO def giant_steps(start, target, n=2): """ Return a list of integers ~= [start, n*start, ..., target/n^2, target/n, target] but conservatively rounded so that the quotient between two successive elements is actually slightly less than n. With n = 2, this describes suitable precision steps for a quadratically convergent algorithm such as Newton's method; with n = 3 steps for cubic convergence (Halley's method), etc. >>> giant_steps(50,1000) [66, 128, 253, 502, 1000] >>> giant_steps(50,1000,4) [65, 252, 1000] """ L = [target] while L[-1] > start*n: L = L + [L[-1]//n + 2] return L[::-1] def rshift(x, n): """For an integer x, calculate x >> n with the fastest (floor) rounding. Unlike the plain Python expression (x >> n), n is allowed to be negative, in which case a left shift is performed.""" if n >= 0: return x >> n else: return x << (-n) def lshift(x, n): """For an integer x, calculate x << n. Unlike the plain Python expression (x << n), n is allowed to be negative, in which case a right shift with default (floor) rounding is performed.""" if n >= 0: return x << n else: return x >> (-n) if BACKEND == 'sage': import operator rshift = operator.rshift lshift = operator.lshift def python_trailing(n): """Count the number of trailing zero bits in abs(n).""" if not n: return 0 t = 0 while not n & 1: n >>= 1 t += 1 return t if BACKEND == 'gmpy': if gmpy.version() >= '2': def gmpy_trailing(n): """Count the number of trailing zero bits in abs(n) using gmpy.""" if n: return MPZ(n).bit_scan1() else: return 0 else: def gmpy_trailing(n): """Count the number of trailing zero bits in abs(n) using gmpy.""" if n: return MPZ(n).scan1() else: return 0 # Small powers of 2 powers = [1<<_ for _ in range(300)] def python_bitcount(n): """Calculate bit size of the nonnegative integer n.""" bc = bisect(powers, n) if bc != 300: return bc bc = int(math.log(n, 2)) - 4 return bc + bctable[n>>bc] def gmpy_bitcount(n): """Calculate bit size of the nonnegative integer n.""" if n: return MPZ(n).numdigits(2) else: return 0 #def sage_bitcount(n): # if n: return MPZ(n).nbits() # else: return 0 def sage_trailing(n): return MPZ(n).trailing_zero_bits() if BACKEND == 'gmpy': bitcount = gmpy_bitcount trailing = gmpy_trailing elif BACKEND == 'sage': sage_bitcount = sage_utils.bitcount bitcount = sage_bitcount trailing = sage_trailing else: bitcount = python_bitcount trailing = python_trailing if BACKEND == 'gmpy' and 'bit_length' in dir(gmpy): bitcount = gmpy.bit_length # Used to avoid slow function calls as far as possible trailtable = [trailing(n) for n in range(256)] bctable = [bitcount(n) for n in range(1024)] # TODO: speed up for bases 2, 4, 8, 16, ... def bin_to_radix(x, xbits, base, bdigits): """Changes radix of a fixed-point number; i.e., converts x * 2**xbits to floor(x * 10**bdigits).""" return x * (MPZ(base)**bdigits) >> xbits stddigits = '0123456789abcdefghijklmnopqrstuvwxyz' def small_numeral(n, base=10, digits=stddigits): """Return the string numeral of a positive integer in an arbitrary base. Most efficient for small input.""" if base == 10: return str(n) digs = [] while n: n, digit = divmod(n, base) digs.append(digits[digit]) return "".join(digs[::-1]) def numeral_python(n, base=10, size=0, digits=stddigits): """Represent the integer n as a string of digits in the given base. Recursive division is used to make this function about 3x faster than Python's str() for converting integers to decimal strings. The 'size' parameters specifies the number of digits in n; this number is only used to determine splitting points and need not be exact.""" if n <= 0: if not n: return "0" return "-" + numeral(-n, base, size, digits) # Fast enough to do directly if size < 250: return small_numeral(n, base, digits) # Divide in half half = (size // 2) + (size & 1) A, B = divmod(n, base**half) ad = numeral(A, base, half, digits) bd = numeral(B, base, half, digits).rjust(half, "0") return ad + bd def numeral_gmpy(n, base=10, size=0, digits=stddigits): """Represent the integer n as a string of digits in the given base. Recursive division is used to make this function about 3x faster than Python's str() for converting integers to decimal strings. The 'size' parameters specifies the number of digits in n; this number is only used to determine splitting points and need not be exact.""" if n < 0: return "-" + numeral(-n, base, size, digits) # gmpy.digits() may cause a segmentation fault when trying to convert # extremely large values to a string. The size limit may need to be # adjusted on some platforms, but 1500000 works on Windows and Linux. if size < 1500000: return gmpy.digits(n, base) # Divide in half half = (size // 2) + (size & 1) A, B = divmod(n, MPZ(base)**half) ad = numeral(A, base, half, digits) bd = numeral(B, base, half, digits).rjust(half, "0") return ad + bd if BACKEND == "gmpy": numeral = numeral_gmpy else: numeral = numeral_python _1_800 = 1<<800 _1_600 = 1<<600 _1_400 = 1<<400 _1_200 = 1<<200 _1_100 = 1<<100 _1_50 = 1<<50 def isqrt_small_python(x): """ Correctly (floor) rounded integer square root, using division. Fast up to ~200 digits. """ if not x: return x if x < _1_800: # Exact with IEEE double precision arithmetic if x < _1_50: return int(x**0.5) # Initial estimate can be any integer >= the true root; round up r = int(x**0.5 * 1.00000000000001) + 1 else: bc = bitcount(x) n = bc//2 r = int((x>>(2*n-100))**0.5+2)<<(n-50) # +2 is to round up # The following iteration now precisely computes floor(sqrt(x)) # See e.g. Crandall & Pomerance, "Prime Numbers: A Computational # Perspective" while 1: y = (r+x//r)>>1 if y >= r: return r r = y def isqrt_fast_python(x): """ Fast approximate integer square root, computed using division-free Newton iteration for large x. For random integers the result is almost always correct (floor(sqrt(x))), but is 1 ulp too small with a roughly 0.1% probability. If x is very close to an exact square, the answer is 1 ulp wrong with high probability. With 0 guard bits, the largest error over a set of 10^5 random inputs of size 1-10^5 bits was 3 ulp. The use of 10 guard bits almost certainly guarantees a max 1 ulp error. """ # Use direct division-based iteration if sqrt(x) < 2^400 # Assume floating-point square root accurate to within 1 ulp, then: # 0 Newton iterations good to 52 bits # 1 Newton iterations good to 104 bits # 2 Newton iterations good to 208 bits # 3 Newton iterations good to 416 bits if x < _1_800: y = int(x**0.5) if x >= _1_100: y = (y + x//y) >> 1 if x >= _1_200: y = (y + x//y) >> 1 if x >= _1_400: y = (y + x//y) >> 1 return y bc = bitcount(x) guard_bits = 10 x <<= 2*guard_bits bc += 2*guard_bits bc += (bc&1) hbc = bc//2 startprec = min(50, hbc) # Newton iteration for 1/sqrt(x), with floating-point starting value r = int(2.0**(2*startprec) * (x >> (bc-2*startprec)) ** -0.5) pp = startprec for p in giant_steps(startprec, hbc): # r**2, scaled from real size 2**(-bc) to 2**p r2 = (r*r) >> (2*pp - p) # x*r**2, scaled from real size ~1.0 to 2**p xr2 = ((x >> (bc-p)) * r2) >> p # New value of r, scaled from real size 2**(-bc/2) to 2**p r = (r * ((3<> (pp+1) pp = p # (1/sqrt(x))*x = sqrt(x) return (r*(x>>hbc)) >> (p+guard_bits) def sqrtrem_python(x): """Correctly rounded integer (floor) square root with remainder.""" # to check cutoff: # plot(lambda x: timing(isqrt, 2**int(x)), [0,2000]) if x < _1_600: y = isqrt_small_python(x) return y, x - y*y y = isqrt_fast_python(x) + 1 rem = x - y*y # Correct remainder while rem < 0: y -= 1 rem += (1+2*y) else: if rem: while rem > 2*(1+y): y += 1 rem -= (1+2*y) return y, rem def isqrt_python(x): """Integer square root with correct (floor) rounding.""" return sqrtrem_python(x)[0] def sqrt_fixed(x, prec): return isqrt_fast(x<>= 1 if m < 250: _cache[m] = b return b MAX_FACTORIAL_CACHE = 1000 def ifac(n, memo={0:1, 1:1}): """Return n factorial (for integers n >= 0 only).""" f = memo.get(n) if f: return f k = len(memo) p = memo[k-1] MAX = MAX_FACTORIAL_CACHE while k <= n: p *= k if k <= MAX: memo[k] = p k += 1 return p def ifac2(n, memo_pair=[{0:1}, {1:1}]): """Return n!! (double factorial), integers n >= 0 only.""" memo = memo_pair[n&1] f = memo.get(n) if f: return f k = max(memo) p = memo[k] MAX = MAX_FACTORIAL_CACHE while k < n: k += 2 p *= k if k <= MAX: memo[k] = p return p if BACKEND == 'gmpy': ifac = gmpy.fac elif BACKEND == 'sage': ifac = lambda n: int(sage.factorial(n)) ifib = sage.fibonacci def list_primes(n): n = n + 1 sieve = list(xrange(n)) sieve[:2] = [0, 0] for i in xrange(2, int(n**0.5)+1): if sieve[i]: for j in xrange(i**2, n, i): sieve[j] = 0 return [p for p in sieve if p] if BACKEND == 'sage': # Note: it is *VERY* important for performance that we convert # the list to Python ints. def list_primes(n): return [int(_) for _ in sage.primes(n+1)] small_odd_primes = (3,5,7,11,13,17,19,23,29,31,37,41,43,47) small_odd_primes_set = set(small_odd_primes) def isprime(n): """ Determines whether n is a prime number. A probabilistic test is performed if n is very large. No special trick is used for detecting perfect powers. >>> sum(list_primes(100000)) 454396537 >>> sum(n*isprime(n) for n in range(100000)) 454396537 """ n = int(n) if not n & 1: return n == 2 if n < 50: return n in small_odd_primes_set for p in small_odd_primes: if not n % p: return False m = n-1 s = trailing(m) d = m >> s def test(a): x = pow(a,d,n) if x == 1 or x == m: return True for r in xrange(1,s): x = x**2 % n if x == m: return True return False # See http://primes.utm.edu/prove/prove2_3.html if n < 1373653: witnesses = [2,3] elif n < 341550071728321: witnesses = [2,3,5,7,11,13,17] else: witnesses = small_odd_primes for a in witnesses: if not test(a): return False return True def moebius(n): """ Evaluates the Moebius function which is `mu(n) = (-1)^k` if `n` is a product of `k` distinct primes and `mu(n) = 0` otherwise. TODO: speed up using factorization """ n = abs(int(n)) if n < 2: return n factors = [] for p in xrange(2, n+1): if not (n % p): if not (n % p**2): return 0 if not sum(p % f for f in factors): factors.append(p) return (-1)**len(factors) def gcd(*args): a = 0 for b in args: if a: while b: a, b = b, a % b else: a = b return a # Comment by Juan Arias de Reyna: # # I learn this method to compute EulerE[2n] from van de Lune. # # We apply the formula EulerE[2n] = (-1)^n 2**(-2n) sum_{j=0}^n a(2n,2j+1) # # where the numbers a(n,j) vanish for j > n+1 or j <= -1 and satisfies # # a(0,-1) = a(0,0) = 0; a(0,1)= 1; a(0,2) = a(0,3) = 0 # # a(n,j) = a(n-1,j) when n+j is even # a(n,j) = (j-1) a(n-1,j-1) + (j+1) a(n-1,j+1) when n+j is odd # # # But we can use only one array unidimensional a(j) since to compute # a(n,j) we only need to know a(n-1,k) where k and j are of different parity # and we have not to conserve the used values. # # We cached up the values of Euler numbers to sufficiently high order. # # Important Observation: If we pretend to use the numbers # EulerE[1], EulerE[2], ... , EulerE[n] # it is convenient to compute first EulerE[n], since the algorithm # computes first all # the previous ones, and keeps them in the CACHE MAX_EULER_CACHE = 500 def eulernum(m, _cache={0:MPZ_ONE}): r""" Computes the Euler numbers `E(n)`, which can be defined as coefficients of the Taylor expansion of `1/cosh x`: .. math :: \frac{1}{\cosh x} = \sum_{n=0}^\infty \frac{E_n}{n!} x^n Example:: >>> [int(eulernum(n)) for n in range(11)] [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] >>> [int(eulernum(n)) for n in range(11)] # test cache [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] """ # for odd m > 1, the Euler numbers are zero if m & 1: return MPZ_ZERO f = _cache.get(m) if f: return f MAX = MAX_EULER_CACHE n = m a = [MPZ(_) for _ in [0,0,1,0,0,0]] for n in range(1, m+1): for j in range(n+1, -1, -2): a[j+1] = (j-1)*a[j] + (j+1)*a[j+2] a.append(0) suma = 0 for k in range(n+1, -1, -2): suma += a[k+1] if n <= MAX: _cache[n] = ((-1)**(n//2))*(suma // 2**n) if n == m: return ((-1)**(n//2))*suma // 2**n wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/exec_py3.py0000644000175000017500000000001512014170666025555 0ustar georgeskgeorgeskexec_ = exec wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/libmpf.py0000644000175000017500000012526412014170666025325 0ustar georgeskgeorgesk""" Low-level functions for arbitrary-precision floating-point arithmetic. """ __docformat__ = 'plaintext' import math from bisect import bisect import sys # Importing random is slow #from random import getrandbits getrandbits = None from .backend import (MPZ, MPZ_TYPE, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, BACKEND, STRICT, HASH_MODULUS, HASH_BITS, gmpy, sage, sage_utils) from .libintmath import (giant_steps, trailtable, bctable, lshift, rshift, bitcount, trailing, sqrt_fixed, numeral, isqrt, isqrt_fast, sqrtrem, bin_to_radix) # We don't pickle tuples directly for the following reasons: # 1: pickle uses str() for ints, which is inefficient when they are large # 2: pickle doesn't work for gmpy mpzs # Both problems are solved by using hex() if BACKEND == 'sage': def to_pickable(x): sign, man, exp, bc = x return sign, hex(man), exp, bc else: def to_pickable(x): sign, man, exp, bc = x return sign, hex(man)[2:], exp, bc def from_pickable(x): sign, man, exp, bc = x return (sign, MPZ(man, 16), exp, bc) class ComplexResult(ValueError): pass try: intern except NameError: intern = lambda x: x # All supported rounding modes round_nearest = intern('n') round_floor = intern('f') round_ceiling = intern('c') round_up = intern('u') round_down = intern('d') round_fast = round_down def prec_to_dps(n): """Return number of accurate decimals that can be represented with a precision of n bits.""" return max(1, int(round(int(n)/3.3219280948873626)-1)) def dps_to_prec(n): """Return the number of bits required to represent n decimals accurately.""" return max(1, int(round((int(n)+1)*3.3219280948873626))) def repr_dps(n): """Return the number of decimal digits required to represent a number with n-bit precision so that it can be uniquely reconstructed from the representation.""" dps = prec_to_dps(n) if dps == 15: return 17 return dps + 3 #----------------------------------------------------------------------------# # Some commonly needed float values # #----------------------------------------------------------------------------# # Regular number format: # (-1)**sign * mantissa * 2**exponent, plus bitcount of mantissa fzero = (0, MPZ_ZERO, 0, 0) fnzero = (1, MPZ_ZERO, 0, 0) fone = (0, MPZ_ONE, 0, 1) fnone = (1, MPZ_ONE, 0, 1) ftwo = (0, MPZ_ONE, 1, 1) ften = (0, MPZ_FIVE, 1, 3) fhalf = (0, MPZ_ONE, -1, 1) # Arbitrary encoding for special numbers: zero mantissa, nonzero exponent fnan = (0, MPZ_ZERO, -123, -1) finf = (0, MPZ_ZERO, -456, -2) fninf = (1, MPZ_ZERO, -789, -3) # Was 1e1000; this is broken in Python 2.4 math_float_inf = 1e300 * 1e300 #----------------------------------------------------------------------------# # Rounding # #----------------------------------------------------------------------------# # This function can be used to round a mantissa generally. However, # we will try to do most rounding inline for efficiency. def round_int(x, n, rnd): if rnd == round_nearest: if x >= 0: t = x >> (n-1) if t & 1 and ((t & 2) or (x & h_mask[n<300][n])): return (t>>1)+1 else: return t>>1 else: return -round_int(-x, n, rnd) if rnd == round_floor: return x >> n if rnd == round_ceiling: return -((-x) >> n) if rnd == round_down: if x >= 0: return x >> n return -((-x) >> n) if rnd == round_up: if x >= 0: return -((-x) >> n) return x >> n # These masks are used to pick out segments of numbers to determine # which direction to round when rounding to nearest. class h_mask_big: def __getitem__(self, n): return (MPZ_ONE<<(n-1))-1 h_mask_small = [0]+[((MPZ_ONE<<(_-1))-1) for _ in range(1, 300)] h_mask = [h_mask_big(), h_mask_small] # The >> operator rounds to floor. shifts_down[rnd][sign] # tells whether this is the right direction to use, or if the # number should be negated before shifting shifts_down = {round_floor:(1,0), round_ceiling:(0,1), round_down:(1,1), round_up:(0,0)} #----------------------------------------------------------------------------# # Normalization of raw mpfs # #----------------------------------------------------------------------------# # This function is called almost every time an mpf is created. # It has been optimized accordingly. def _normalize(sign, man, exp, bc, prec, rnd): """ Create a raw mpf tuple with value (-1)**sign * man * 2**exp and normalized mantissa. The mantissa is rounded in the specified direction if its size exceeds the precision. Trailing zero bits are also stripped from the mantissa to ensure that the representation is canonical. Conditions on the input: * The input must represent a regular (finite) number * The sign bit must be 0 or 1 * The mantissa must be positive * The exponent must be an integer * The bitcount must be exact If these conditions are not met, use from_man_exp, mpf_pos, or any of the conversion functions to create normalized raw mpf tuples. """ if not man: return fzero # Cut mantissa down to size if larger than target precision n = bc - prec if n > 0: if rnd == round_nearest: t = man >> (n-1) if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): man = (t>>1)+1 else: man = t>>1 elif shifts_down[rnd][sign]: man >>= n else: man = -((-man)>>n) exp += n bc = prec # Strip trailing bits if not man & 1: t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t # Bit count can be wrong if the input mantissa was 1 less than # a power of 2 and got rounded up, thereby adding an extra bit. # With trailing bits removed, all powers of two have mantissa 1, # so this is easy to check for. if man == 1: bc = 1 return sign, man, exp, bc def _normalize1(sign, man, exp, bc, prec, rnd): """same as normalize, but with the added condition that man is odd or zero """ if not man: return fzero if bc <= prec: return sign, man, exp, bc n = bc - prec if rnd == round_nearest: t = man >> (n-1) if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): man = (t>>1)+1 else: man = t>>1 elif shifts_down[rnd][sign]: man >>= n else: man = -((-man)>>n) exp += n bc = prec # Strip trailing bits if not man & 1: t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t # Bit count can be wrong if the input mantissa was 1 less than # a power of 2 and got rounded up, thereby adding an extra bit. # With trailing bits removed, all powers of two have mantissa 1, # so this is easy to check for. if man == 1: bc = 1 return sign, man, exp, bc try: _exp_types = (int, long) except NameError: _exp_types = (int,) def strict_normalize(sign, man, exp, bc, prec, rnd): """Additional checks on the components of an mpf. Enable tests by setting the environment variable MPMATH_STRICT to Y.""" assert type(man) == MPZ_TYPE assert type(bc) in _exp_types assert type(exp) in _exp_types assert bc == bitcount(man) return _normalize(sign, man, exp, bc, prec, rnd) def strict_normalize1(sign, man, exp, bc, prec, rnd): """Additional checks on the components of an mpf. Enable tests by setting the environment variable MPMATH_STRICT to Y.""" assert type(man) == MPZ_TYPE assert type(bc) in _exp_types assert type(exp) in _exp_types assert bc == bitcount(man) assert (not man) or (man & 1) return _normalize1(sign, man, exp, bc, prec, rnd) if BACKEND == 'gmpy' and '_mpmath_normalize' in dir(gmpy): _normalize = gmpy._mpmath_normalize _normalize1 = gmpy._mpmath_normalize if BACKEND == 'sage': _normalize = _normalize1 = sage_utils.normalize if STRICT: normalize = strict_normalize normalize1 = strict_normalize1 else: normalize = _normalize normalize1 = _normalize1 #----------------------------------------------------------------------------# # Conversion functions # #----------------------------------------------------------------------------# def from_man_exp(man, exp, prec=None, rnd=round_fast): """Create raw mpf from (man, exp) pair. The mantissa may be signed. If no precision is specified, the mantissa is stored exactly.""" man = MPZ(man) sign = 0 if man < 0: sign = 1 man = -man if man < 1024: bc = bctable[int(man)] else: bc = bitcount(man) if not prec: if not man: return fzero if not man & 1: if man & 2: return (sign, man >> 1, exp + 1, bc - 1) t = trailtable[int(man & 255)] if not t: while not man & 255: man >>= 8 exp += 8 bc -= 8 t = trailtable[int(man & 255)] man >>= t exp += t bc -= t return (sign, man, exp, bc) return normalize(sign, man, exp, bc, prec, rnd) int_cache = dict((n, from_man_exp(n, 0)) for n in range(-10, 257)) if BACKEND == 'gmpy' and '_mpmath_create' in dir(gmpy): from_man_exp = gmpy._mpmath_create if BACKEND == 'sage': from_man_exp = sage_utils.from_man_exp def from_int(n, prec=0, rnd=round_fast): """Create a raw mpf from an integer. If no precision is specified, the mantissa is stored exactly.""" if not prec: if n in int_cache: return int_cache[n] return from_man_exp(n, 0, prec, rnd) def to_man_exp(s): """Return (man, exp) of a raw mpf. Raise an error if inf/nan.""" sign, man, exp, bc = s if (not man) and exp: raise ValueError("mantissa and exponent are undefined for %s" % man) return man, exp def to_int(s, rnd=None): """Convert a raw mpf to the nearest int. Rounding is done down by default (same as int(float) in Python), but can be changed. If the input is inf/nan, an exception is raised.""" sign, man, exp, bc = s if (not man) and exp: raise ValueError("cannot convert %s to int" % man) if exp >= 0: if sign: return (-man) << exp return man << exp # Make default rounding fast if not rnd: if sign: return -(man >> (-exp)) else: return man >> (-exp) if sign: return round_int(-man, -exp, rnd) else: return round_int(man, -exp, rnd) def mpf_round_int(s, rnd): sign, man, exp, bc = s if (not man) and exp: return s if exp >= 0: return s mag = exp+bc if mag < 1: if rnd == round_ceiling: if sign: return fzero else: return fone elif rnd == round_floor: if sign: return fnone else: return fzero elif rnd == round_nearest: if mag < 0 or man == MPZ_ONE: return fzero elif sign: return fnone else: return fone else: raise NotImplementedError return mpf_pos(s, min(bc, mag), rnd) def mpf_floor(s, prec=0, rnd=round_fast): v = mpf_round_int(s, round_floor) if prec: v = mpf_pos(v, prec, rnd) return v def mpf_ceil(s, prec=0, rnd=round_fast): v = mpf_round_int(s, round_ceiling) if prec: v = mpf_pos(v, prec, rnd) return v def mpf_nint(s, prec=0, rnd=round_fast): v = mpf_round_int(s, round_nearest) if prec: v = mpf_pos(v, prec, rnd) return v def mpf_frac(s, prec=0, rnd=round_fast): return mpf_sub(s, mpf_floor(s), prec, rnd) def from_float(x, prec=53, rnd=round_fast): """Create a raw mpf from a Python float, rounding if necessary. If prec >= 53, the result is guaranteed to represent exactly the same number as the input. If prec is not specified, use prec=53.""" # frexp only raises an exception for nan on some platforms if x != x: return fnan # in Python2.5 math.frexp gives an exception for float infinity # in Python2.6 it returns (float infinity, 0) try: m, e = math.frexp(x) except: if x == math_float_inf: return finf if x == -math_float_inf: return fninf return fnan if x == math_float_inf: return finf if x == -math_float_inf: return fninf return from_man_exp(int(m*(1<<53)), e-53, prec, rnd) def to_float(s, strict=False): """ Convert a raw mpf to a Python float. The result is exact if the bitcount of s is <= 53 and no underflow/overflow occurs. If the number is too large or too small to represent as a regular float, it will be converted to inf or 0.0. Setting strict=True forces an OverflowError to be raised instead. """ sign, man, exp, bc = s if not man: if s == fzero: return 0.0 if s == finf: return math_float_inf if s == fninf: return -math_float_inf return math_float_inf/math_float_inf if sign: man = -man try: if bc < 100: return math.ldexp(man, exp) # Try resizing the mantissa. Overflow may still happen here. n = bc - 53 m = man >> n return math.ldexp(m, exp + n) except OverflowError: if strict: raise # Overflow to infinity if exp + bc > 0: if sign: return -math_float_inf else: return math_float_inf # Underflow to zero return 0.0 def from_rational(p, q, prec, rnd=round_fast): """Create a raw mpf from a rational number p/q, round if necessary.""" return mpf_div(from_int(p), from_int(q), prec, rnd) def to_rational(s): """Convert a raw mpf to a rational number. Return integers (p, q) such that s = p/q exactly.""" sign, man, exp, bc = s if sign: man = -man if bc == -1: raise ValueError("cannot convert %s to a rational number" % man) if exp >= 0: return man * (1<= 0: return (-man) << offset else: return (-man) >> (-offset) else: if offset >= 0: return man << offset else: return man >> (-offset) ############################################################################## ############################################################################## #----------------------------------------------------------------------------# # Arithmetic operations, etc. # #----------------------------------------------------------------------------# def mpf_rand(prec): """Return a raw mpf chosen randomly from [0, 1), with prec bits in the mantissa.""" global getrandbits if not getrandbits: import random getrandbits = random.getrandbits return from_man_exp(getrandbits(prec), -prec, prec, round_floor) def mpf_eq(s, t): """Test equality of two raw mpfs. This is simply tuple comparion unless either number is nan, in which case the result is False.""" if not s[1] or not t[1]: if s == fnan or t == fnan: return False return s == t def mpf_hash(s): # Duplicate the new hash algorithm introduces in Python 3.2. if sys.version >= "3.2": ssign, sman, sexp, sbc = s # Handle special numbers if not sman: if s == fnan: return sys.hash_info.nan if s == finf: return sys.hash_info.inf if s == fninf: return -sys.hash_info.inf h = sman % HASH_MODULUS if sexp >= 0: sexp = sexp % HASH_BITS else: sexp = HASH_BITS - 1 - ((-1 - sexp) % HASH_BITS) h = (h << sexp) % HASH_MODULUS if ssign: h = -h if h == -1: h == -2 return int(h) else: try: # Try to be compatible with hash values for floats and ints return hash(to_float(s, strict=1)) except OverflowError: # We must unfortunately sacrifice compatibility with ints here. # We could do hash(man << exp) when the exponent is positive, but # this would cause unreasonable inefficiency for large numbers. return hash(s) def mpf_cmp(s, t): """Compare the raw mpfs s and t. Return -1 if s < t, 0 if s == t, and 1 if s > t. (Same convention as Python's cmp() function.)""" # In principle, a comparison amounts to determining the sign of s-t. # A full subtraction is relatively slow, however, so we first try to # look at the components. ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t # Handle zeros and special numbers if not sman or not tman: if s == fzero: return -mpf_sign(t) if t == fzero: return mpf_sign(s) if s == t: return 0 # Follow same convention as Python's cmp for float nan if t == fnan: return 1 if s == finf: return 1 if t == fninf: return 1 return -1 # Different sides of zero if ssign != tsign: if not ssign: return 1 return -1 # This reduces to direct integer comparison if sexp == texp: if sman == tman: return 0 if sman > tman: if ssign: return -1 else: return 1 else: if ssign: return 1 else: return -1 # Check position of the highest set bit in each number. If # different, there is certainly an inequality. a = sbc + sexp b = tbc + texp if ssign: if a < b: return 1 if a > b: return -1 else: if a < b: return -1 if a > b: return 1 # Both numbers have the same highest bit. Subtract to find # how the lower bits compare. delta = mpf_sub(s, t, 5, round_floor) if delta[0]: return -1 return 1 def mpf_lt(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) < 0 def mpf_le(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) <= 0 def mpf_gt(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) > 0 def mpf_ge(s, t): if s == fnan or t == fnan: return False return mpf_cmp(s, t) >= 0 def mpf_min_max(seq): min = max = seq[0] for x in seq[1:]: if mpf_lt(x, min): min = x if mpf_gt(x, max): max = x return min, max def mpf_pos(s, prec=0, rnd=round_fast): """Calculate 0+s for a raw mpf (i.e., just round s to the specified precision).""" if prec: sign, man, exp, bc = s if (not man) and exp: return s return normalize1(sign, man, exp, bc, prec, rnd) return s def mpf_neg(s, prec=None, rnd=round_fast): """Negate a raw mpf (return -s), rounding the result to the specified precision. The prec argument can be omitted to do the operation exactly.""" sign, man, exp, bc = s if not man: if exp: if s == finf: return fninf if s == fninf: return finf return s if not prec: return (1-sign, man, exp, bc) return normalize1(1-sign, man, exp, bc, prec, rnd) def mpf_abs(s, prec=None, rnd=round_fast): """Return abs(s) of the raw mpf s, rounded to the specified precision. The prec argument can be omitted to generate an exact result.""" sign, man, exp, bc = s if (not man) and exp: if s == fninf: return finf return s if not prec: if sign: return (0, man, exp, bc) return s return normalize1(0, man, exp, bc, prec, rnd) def mpf_sign(s): """Return -1, 0, or 1 (as a Python int, not a raw mpf) depending on whether s is negative, zero, or positive. (Nan is taken to give 0.)""" sign, man, exp, bc = s if not man: if s == finf: return 1 if s == fninf: return -1 return 0 return (-1) ** sign def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0): """ Add the two raw mpf values s and t. With prec=0, no rounding is performed. Note that this can produce a very large mantissa (potentially too large to fit in memory) if exponents are far apart. """ ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t tsign ^= _sub # Standard case: two nonzero, regular numbers if sman and tman: offset = sexp - texp if offset: if offset > 0: # Outside precision range; only need to perturb if offset > 100 and prec: delta = sbc + sexp - tbc - texp if delta > prec + 4: offset = prec + 4 sman <<= offset if tsign == ssign: sman += 1 else: sman -= 1 return normalize1(ssign, sman, sexp-offset, bitcount(sman), prec, rnd) # Add if ssign == tsign: man = tman + (sman << offset) # Subtract else: if ssign: man = tman - (sman << offset) else: man = (sman << offset) - tman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize1(ssign, man, texp, bc, prec or bc, rnd) elif offset < 0: # Outside precision range; only need to perturb if offset < -100 and prec: delta = tbc + texp - sbc - sexp if delta > prec + 4: offset = prec + 4 tman <<= offset if ssign == tsign: tman += 1 else: tman -= 1 return normalize1(tsign, tman, texp-offset, bitcount(tman), prec, rnd) # Add if ssign == tsign: man = sman + (tman << -offset) # Subtract else: if tsign: man = sman - (tman << -offset) else: man = (tman << -offset) - sman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize1(ssign, man, sexp, bc, prec or bc, rnd) # Equal exponents; no shifting necessary if ssign == tsign: man = tman + sman else: if ssign: man = tman - sman else: man = sman - tman if man >= 0: ssign = 0 else: man = -man ssign = 1 bc = bitcount(man) return normalize(ssign, man, texp, bc, prec or bc, rnd) # Handle zeros and special numbers if _sub: t = mpf_neg(t) if not sman: if sexp: if s == t or tman or not texp: return s return fnan if tman: return normalize1(tsign, tman, texp, tbc, prec or tbc, rnd) return t if texp: return t if sman: return normalize1(ssign, sman, sexp, sbc, prec or sbc, rnd) return s def mpf_sub(s, t, prec=0, rnd=round_fast): """Return the difference of two raw mpfs, s-t. This function is simply a wrapper of mpf_add that changes the sign of t.""" return mpf_add(s, t, prec, rnd, 1) def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False): """ Sum a list of mpf values efficiently and accurately (typically no temporary roundoff occurs). If prec=0, the final result will not be rounded either. There may be roundoff error or cancellation if extremely large exponent differences occur. With absolute=True, sums the absolute values. """ man = 0 exp = 0 max_extra_prec = prec*2 or 1000000 # XXX special = None for x in xs: xsign, xman, xexp, xbc = x if xman: if xsign and not absolute: xman = -xman delta = xexp - exp if xexp >= exp: # x much larger than existing sum? # first: quick test if (delta > max_extra_prec) and \ ((not man) or delta-bitcount(abs(man)) > max_extra_prec): man = xman exp = xexp else: man += (xman << delta) else: delta = -delta # x much smaller than existing sum? if delta-xbc > max_extra_prec: if not man: man, exp = xman, xexp else: man = (man << delta) + xman exp = xexp elif xexp: if absolute: x = mpf_abs(x) special = mpf_add(special or fzero, x, 1) # Will be inf or nan if special: return special return from_man_exp(man, exp, prec, rnd) def gmpy_mpf_mul(s, t, prec=0, rnd=round_fast): """Multiply two raw mpfs""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t sign = ssign ^ tsign man = sman*tman if man: bc = bitcount(man) if prec: return normalize1(sign, man, sexp+texp, bc, prec, rnd) else: return (sign, man, sexp+texp, bc) s_special = (not sman) and sexp t_special = (not tman) and texp if not s_special and not t_special: return fzero if fnan in (s, t): return fnan if (not tman) and texp: s, t = t, s if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast): """Multiply by a Python integer.""" sign, man, exp, bc = s if not man: return mpf_mul(s, from_int(n), prec, rnd) if not n: return fzero if n < 0: sign ^= 1 n = -n man *= n return normalize(sign, man, exp, bitcount(man), prec, rnd) def python_mpf_mul(s, t, prec=0, rnd=round_fast): """Multiply two raw mpfs""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t sign = ssign ^ tsign man = sman*tman if man: bc = sbc + tbc - 1 bc += int(man>>bc) if prec: return normalize1(sign, man, sexp+texp, bc, prec, rnd) else: return (sign, man, sexp+texp, bc) s_special = (not sman) and sexp t_special = (not tman) and texp if not s_special and not t_special: return fzero if fnan in (s, t): return fnan if (not tman) and texp: s, t = t, s if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] def python_mpf_mul_int(s, n, prec, rnd=round_fast): """Multiply by a Python integer.""" sign, man, exp, bc = s if not man: return mpf_mul(s, from_int(n), prec, rnd) if not n: return fzero if n < 0: sign ^= 1 n = -n man *= n # Generally n will be small if n < 1024: bc += bctable[int(n)] - 1 else: bc += bitcount(n) - 1 bc += int(man>>bc) return normalize(sign, man, exp, bc, prec, rnd) if BACKEND == 'gmpy': mpf_mul = gmpy_mpf_mul mpf_mul_int = gmpy_mpf_mul_int else: mpf_mul = python_mpf_mul mpf_mul_int = python_mpf_mul_int def mpf_shift(s, n): """Quickly multiply the raw mpf s by 2**n without rounding.""" sign, man, exp, bc = s if not man: return s return sign, man, exp+n, bc def mpf_frexp(x): """Convert x = y*2**n to (y, n) with abs(y) in [0.5, 1) if nonzero""" sign, man, exp, bc = x if not man: if x == fzero: return (fzero, 0) else: raise ValueError return mpf_shift(x, -bc-exp), bc+exp def mpf_div(s, t, prec, rnd=round_fast): """Floating-point division""" ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if not sman or not tman: if s == fzero: if t == fzero: raise ZeroDivisionError if t == fnan: return fnan return fzero if t == fzero: raise ZeroDivisionError s_special = (not sman) and sexp t_special = (not tman) and texp if s_special and t_special: return fnan if s == fnan or t == fnan: return fnan if not t_special: if t == fzero: return fnan return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] return fzero sign = ssign ^ tsign if tman == 1: return normalize1(sign, sman, sexp-texp, sbc, prec, rnd) # Same strategy as for addition: if there is a remainder, perturb # the result a few bits outside the precision range before rounding extra = prec - sbc + tbc + 5 if extra < 5: extra = 5 quot, rem = divmod(sman< sexp+sbc: return s # Another important special case: this allows us to do e.g. x % 1.0 # to find the fractional part of x, and it will work when x is huge. if tman == 1 and sexp > texp+tbc: return fzero base = min(sexp, texp) sman = (-1)**ssign * sman tman = (-1)**tsign * tman man = (sman << (sexp-base)) % (tman << (texp-base)) if man >= 0: sign = 0 else: man = -man sign = 1 return normalize(sign, man, base, bitcount(man), prec, rnd) reciprocal_rnd = { round_down : round_up, round_up : round_down, round_floor : round_ceiling, round_ceiling : round_floor, round_nearest : round_nearest } negative_rnd = { round_down : round_down, round_up : round_up, round_floor : round_ceiling, round_ceiling : round_floor, round_nearest : round_nearest } def mpf_pow_int(s, n, prec, rnd=round_fast): """Compute s**n, where s is a raw mpf and n is a Python integer.""" sign, man, exp, bc = s if (not man) and exp: if s == finf: if n > 0: return s if n == 0: return fnan return fzero if s == fninf: if n > 0: return [finf, fninf][n & 1] if n == 0: return fnan return fzero return fnan n = int(n) if n == 0: return fone if n == 1: return mpf_pos(s, prec, rnd) if n == 2: _, man, exp, bc = s if not man: return fzero man = man*man if man == 1: return (0, MPZ_ONE, exp+exp, 1) bc = bc + bc - 2 bc += bctable[int(man>>bc)] return normalize1(0, man, exp+exp, bc, prec, rnd) if n == -1: return mpf_div(fone, s, prec, rnd) if n < 0: inverse = mpf_pow_int(s, -n, prec+5, reciprocal_rnd[rnd]) return mpf_div(fone, inverse, prec, rnd) result_sign = sign & n # Use exact integer power when the exact mantissa is small if man == 1: return (result_sign, MPZ_ONE, exp*n, 1) if bc*n < 1000: man **= n return normalize1(result_sign, man, exp*n, bitcount(man), prec, rnd) # Use directed rounding all the way through to maintain rigorous # bounds for interval arithmetic rounds_down = (rnd == round_nearest) or \ shifts_down[rnd][result_sign] # Now we perform binary exponentiation. Need to estimate precision # to avoid rounding errors from temporary operations. Roughly log_2(n) # operations are performed. workprec = prec + 4*bitcount(n) + 4 _, pm, pe, pbc = fone while 1: if n & 1: pm = pm*man pe = pe+exp pbc += bc - 2 pbc = pbc + bctable[int(pm >> pbc)] if pbc > workprec: if rounds_down: pm = pm >> (pbc-workprec) else: pm = -((-pm) >> (pbc-workprec)) pe += pbc - workprec pbc = workprec n -= 1 if not n: break man = man*man exp = exp+exp bc = bc + bc - 2 bc = bc + bctable[int(man >> bc)] if bc > workprec: if rounds_down: man = man >> (bc-workprec) else: man = -((-man) >> (bc-workprec)) exp += bc - workprec bc = workprec n = n // 2 return normalize(result_sign, pm, pe, pbc, prec, rnd) def mpf_perturb(x, eps_sign, prec, rnd): """ For nonzero x, calculate x + eps with directed rounding, where eps < prec relatively and eps has the given sign (0 for positive, 1 for negative). With rounding to nearest, this is taken to simply normalize x to the given precision. """ if rnd == round_nearest: return mpf_pos(x, prec, rnd) sign, man, exp, bc = x eps = (eps_sign, MPZ_ONE, exp+bc-prec-1, 1) if sign: away = (rnd in (round_down, round_ceiling)) ^ eps_sign else: away = (rnd in (round_up, round_ceiling)) ^ eps_sign if away: return mpf_add(x, eps, prec, rnd) else: return mpf_pos(x, prec, rnd) #----------------------------------------------------------------------------# # Radix conversion # #----------------------------------------------------------------------------# def to_digits_exp(s, dps): """Helper function for representing the floating-point number s as a decimal with dps digits. Returns (sign, string, exponent) where sign is '' or '-', string is the digit string, and exponent is the decimal exponent as an int. If inexact, the decimal representation is rounded toward zero.""" # Extract sign first so it doesn't mess up the string digit count if s[0]: sign = '-' s = mpf_neg(s) else: sign = '' _sign, man, exp, bc = s if not man: return '', '0', 0 bitprec = int(dps * math.log(10,2)) + 10 # Cut down to size # TODO: account for precision when doing this exp_from_1 = exp + bc if abs(exp_from_1) > 3500: from .libelefun import mpf_ln2, mpf_ln10 # Set b = int(exp * log(2)/log(10)) # If exp is huge, we must use high-precision arithmetic to # find the nearest power of ten expprec = bitcount(abs(exp)) + 5 tmp = from_int(exp) tmp = mpf_mul(tmp, mpf_ln2(expprec)) tmp = mpf_div(tmp, mpf_ln10(expprec), expprec) b = to_int(tmp) s = mpf_div(s, mpf_pow_int(ften, b, bitprec), bitprec) _sign, man, exp, bc = s exponent = b else: exponent = 0 # First, calculate mantissa digits by converting to a binary # fixed-point number and then converting that number to # a decimal fixed-point number. fixprec = max(bitprec - exp - bc, 0) fixdps = int(fixprec / math.log(10,2) + 0.5) sf = to_fixed(s, fixprec) sd = bin_to_radix(sf, fixprec, 10, fixdps) digits = numeral(sd, base=10, size=dps) exponent += len(digits) - fixdps - 1 return sign, digits, exponent def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None, show_zero_exponent=False): """ Convert a raw mpf to a decimal floating-point literal with at most `dps` decimal digits in the mantissa (not counting extra zeros that may be inserted for visual purposes). The number will be printed in fixed-point format if the position of the leading digit is strictly between min_fixed (default = min(-dps/3,-5)) and max_fixed (default = dps). To force fixed-point format always, set min_fixed = -inf, max_fixed = +inf. To force floating-point format, set min_fixed >= max_fixed. The literal is formatted so that it can be parsed back to a number by to_str, float() or Decimal(). """ # Special numbers if not s[1]: if s == fzero: if dps: t = '0.0' else: t = '.0' if show_zero_exponent: t += 'e+0' return t if s == finf: return '+inf' if s == fninf: return '-inf' if s == fnan: return 'nan' raise ValueError if min_fixed is None: min_fixed = min(-(dps//3), -5) if max_fixed is None: max_fixed = dps # to_digits_exp rounds to floor. # This sometimes kills some instances of "...00001" sign, digits, exponent = to_digits_exp(s, dps+3) # No digits: show only .0; round exponent to nearest if not dps: if digits[0] in '56789': exponent += 1 digits = ".0" else: # Rounding up kills some instances of "...99999" if len(digits) > dps and digits[dps] in '56789' and \ (dps < 500 or digits[dps-4:dps] == '9999'): digits2 = str(int(digits[:dps]) + 1) if len(digits2) > dps: digits2 = digits2[:dps] exponent += 1 digits = digits2 else: digits = digits[:dps] # Prettify numbers close to unit magnitude if min_fixed < exponent < max_fixed: if exponent < 0: digits = ("0"*int(-exponent)) + digits split = 1 else: split = exponent + 1 if split > dps: digits += "0"*(split-dps) exponent = 0 else: split = 1 digits = (digits[:split] + "." + digits[split:]) if strip_zeros: # Clean up trailing zeros digits = digits.rstrip('0') if digits[-1] == ".": digits += "0" if exponent == 0 and dps and not show_zero_exponent: return sign + digits if exponent >= 0: return sign + digits + "e+" + str(exponent) if exponent < 0: return sign + digits + "e" + str(exponent) def str_to_man_exp(x, base=10): """Helper function for from_str.""" # Verify that the input is a valid float literal float(x) # Split into mantissa, exponent x = x.lower() parts = x.split('e') if len(parts) == 1: exp = 0 else: # == 2 x = parts[0] exp = int(parts[1]) # Look for radix point in mantissa parts = x.split('.') if len(parts) == 2: a, b = parts[0], parts[1].rstrip('0') exp -= len(b) x = a + b x = MPZ(int(x, base)) return x, exp special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan} def from_str(x, prec, rnd=round_fast): """Create a raw mpf from a decimal literal, rounding in the specified direction if the input number cannot be represented exactly as a binary floating-point number with the given number of bits. The literal syntax accepted is the same as for Python floats. TODO: the rounding does not work properly for large exponents. """ x = x.strip() if x in special_str: return special_str[x] if '/' in x: p, q = x.split('/') return from_rational(int(p), int(q), prec, rnd) man, exp = str_to_man_exp(x, base=10) # XXX: appropriate cutoffs & track direction # note no factors of 5 if abs(exp) > 400: s = from_int(man, prec+10) s = mpf_mul(s, mpf_pow_int(ften, exp, prec+10), prec, rnd) else: if exp >= 0: s = from_int(man * 10**exp, prec, rnd) else: s = from_rational(man, 10**-exp, prec, rnd) return s # Binary string conversion. These are currently mainly used for debugging # and could use some improvement in the future def from_bstr(x): man, exp = str_to_man_exp(x, base=2) man = MPZ(man) sign = 0 if man < 0: man = -man sign = 1 bc = bitcount(man) return normalize(sign, man, exp, bc, bc, round_floor) def to_bstr(x): sign, man, exp, bc = x return ['','-'][sign] + numeral(man, size=bitcount(man), base=2) + ("e%i" % exp) #----------------------------------------------------------------------------# # Square roots # #----------------------------------------------------------------------------# def mpf_sqrt(s, prec, rnd=round_fast): """ Compute the square root of a nonnegative mpf value. The result is correctly rounded. """ sign, man, exp, bc = s if sign: raise ComplexResult("square root of a negative number") if not man: return s if exp & 1: exp -= 1 man <<= 1 bc += 1 elif man == 1: return normalize1(sign, man, exp//2, bc, prec, rnd) shift = max(4, 2*prec-bc+4) shift += shift & 1 if rnd in 'fd': man = isqrt(man<> (20 + 6) # Khinchin's constant is relatively difficult to compute. Here # we use the rational zeta series # oo 2*n-1 # ___ ___ # \ ` zeta(2*n)-1 \ ` (-1)^(k+1) # log(K)*log(2) = ) ------------ ) ---------- # /___. n /___. k # n = 1 k = 1 # which adds half a digit per term. The essential trick for achieving # reasonable efficiency is to recycle both the values of the zeta # function (essentially Bernoulli numbers) and the partial terms of # the inner sum. # An alternative might be to use K = 2*exp[1/log(2) X] where # / 1 1 [ pi*x*(1-x^2) ] # X = | ------ log [ ------------ ]. # / 0 x(1+x) [ sin(pi*x) ] # and integrate numerically. In practice, this seems to be slightly # slower than the zeta series at high precision. @constant_memo def khinchin_fixed(prec): wp = int(prec + prec**0.5 + 15) s = MPZ_ZERO fac = from_int(4) t = ONE = MPZ_ONE << wp pi = mpf_pi(wp) pipow = twopi2 = mpf_shift(mpf_mul(pi, pi, wp), 2) n = 1 while 1: zeta2n = mpf_abs(mpf_bernoulli(2*n, wp)) zeta2n = mpf_mul(zeta2n, pipow, wp) zeta2n = mpf_div(zeta2n, fac, wp) zeta2n = to_fixed(zeta2n, wp) term = (((zeta2n - ONE) * t) // n) >> wp if term < 100: break #if not n % 10: # print n, math.log(int(abs(term))) s += term t += ONE//(2*n+1) - ONE//(2*n) n += 1 fac = mpf_mul_int(fac, (2*n)*(2*n-1), wp) pipow = mpf_mul(pipow, twopi2, wp) s = (s << wp) // ln2_fixed(wp) K = mpf_exp(from_man_exp(s, -wp), wp) K = to_fixed(K, prec) return K # Glaisher's constant is defined as A = exp(1/2 - zeta'(-1)). # One way to compute it would be to perform direct numerical # differentiation, but computing arbitrary Riemann zeta function # values at high precision is expensive. We instead use the formula # A = exp((6 (-zeta'(2))/pi^2 + log 2 pi + gamma)/12) # and compute zeta'(2) from the series representation # oo # ___ # \ log k # -zeta'(2) = ) ----- # /___ 2 # k # k = 2 # This series converges exceptionally slowly, but can be accelerated # using Euler-Maclaurin formula. The important insight is that the # E-M integral can be done in closed form and that the high order # are given by # n / \ # d | log x | a + b log x # --- | ----- | = ----------- # n | 2 | 2 + n # dx \ x / x # where a and b are integers given by a simple recurrence. Note # that just one logarithm is needed. However, lots of integer # logarithms are required for the initial summation. # This algorithm could possibly be turned into a faster algorithm # for general evaluation of zeta(s) or zeta'(s); this should be # looked into. @constant_memo def glaisher_fixed(prec): wp = prec + 30 # Number of direct terms to sum before applying the Euler-Maclaurin # formula to the tail. TODO: choose more intelligently N = int(0.33*prec + 5) ONE = MPZ_ONE << wp # Euler-Maclaurin, step 1: sum log(k)/k**2 for k from 2 to N-1 s = MPZ_ZERO for k in range(2, N): #print k, N s += log_int_fixed(k, wp) // k**2 logN = log_int_fixed(N, wp) #logN = to_fixed(mpf_log(from_int(N), wp+20), wp) # E-M step 2: integral of log(x)/x**2 from N to inf s += (ONE + logN) // N # E-M step 3: endpoint correction term f(N)/2 s += logN // (N**2 * 2) # E-M step 4: the series of derivatives pN = N**3 a = 1 b = -2 j = 3 fac = from_int(2) k = 1 while 1: # D(2*k-1) * B(2*k) / fac(2*k) [D(n) = nth derivative] D = ((a << wp) + b*logN) // pN D = from_man_exp(D, -wp) B = mpf_bernoulli(2*k, wp) term = mpf_mul(B, D, wp) term = mpf_div(term, fac, wp) term = to_fixed(term, wp) if abs(term) < 100: break #if not k % 10: # print k, math.log(int(abs(term)), 10) s -= term # Advance derivative twice a, b, pN, j = b-a*j, -j*b, pN*N, j+1 a, b, pN, j = b-a*j, -j*b, pN*N, j+1 k += 1 fac = mpf_mul_int(fac, (2*k)*(2*k-1), wp) # A = exp((6*s/pi**2 + log(2*pi) + euler)/12) pi = pi_fixed(wp) s *= 6 s = (s << wp) // (pi**2 >> wp) s += euler_fixed(wp) s += to_fixed(mpf_log(from_man_exp(2*pi, -wp), wp), wp) s //= 12 A = mpf_exp(from_man_exp(s, -wp), wp) return to_fixed(A, prec) # Apery's constant can be computed using the very rapidly convergent # series # oo # ___ 2 10 # \ n 205 n + 250 n + 77 (n!) # zeta(3) = ) (-1) ------------------- ---------- # /___ 64 5 # n = 0 ((2n+1)!) @constant_memo def apery_fixed(prec): prec += 20 d = MPZ_ONE << prec term = MPZ(77) << prec n = 1 s = MPZ_ZERO while term: s += term d *= (n**10) d //= (((2*n+1)**5) * (2*n)**5) term = (-1)**n * (205*(n**2) + 250*n + 77) * d n += 1 return s >> (20 + 6) """ Euler's constant (gamma) is computed using the Brent-McMillan formula, gamma ~= I(n)/J(n) - log(n), where I(n) = sum_{k=0,1,2,...} (n**k / k!)**2 * H(k) J(n) = sum_{k=0,1,2,...} (n**k / k!)**2 H(k) = 1 + 1/2 + 1/3 + ... + 1/k The error is bounded by O(exp(-4n)). Choosing n to be a power of two, 2**p, the logarithm becomes particularly easy to calculate.[1] We use the formulation of Algorithm 3.9 in [2] to make the summation more efficient. Reference: [1] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf [2] Jonathan Borwein & David Bailey, Mathematics by Experiment, A K Peters, 2003 """ @constant_memo def euler_fixed(prec): extra = 30 prec += extra # choose p such that exp(-4*(2**p)) < 2**-n p = int(math.log((prec/4) * math.log(2), 2)) + 1 n = 2**p A = U = -p*ln2_fixed(prec) B = V = MPZ_ONE << prec k = 1 while 1: B = B*n**2//k**2 A = (A*n**2//k + B)//k U += A V += B if max(abs(A), abs(B)) < 100: break k += 1 return (U<<(prec-extra))//V # Use zeta accelerated formulas for the Mertens and twin # prime constants; see # http://mathworld.wolfram.com/MertensConstant.html # http://mathworld.wolfram.com/TwinPrimesConstant.html @constant_memo def mertens_fixed(prec): wp = prec + 20 m = 2 s = mpf_euler(wp) while 1: t = mpf_zeta_int(m, wp) if t == fone: break t = mpf_log(t, wp) t = mpf_mul_int(t, moebius(m), wp) t = mpf_div(t, from_int(m), wp) s = mpf_add(s, t) m += 1 return to_fixed(s, prec) @constant_memo def twinprime_fixed(prec): def I(n): return sum(moebius(d)<<(n//d) for d in xrange(1,n+1) if not n%d)//n wp = 2*prec + 30 res = fone primes = [from_rational(1,p,wp) for p in [2,3,5,7]] ppowers = [mpf_mul(p,p,wp) for p in primes] n = 2 while 1: a = mpf_zeta_int(n, wp) for i in range(4): a = mpf_mul(a, mpf_sub(fone, ppowers[i]), wp) ppowers[i] = mpf_mul(ppowers[i], primes[i], wp) a = mpf_pow_int(a, -I(n), wp) if mpf_pos(a, prec+10, 'n') == fone: break #from libmpf import to_str #print n, to_str(mpf_sub(fone, a), 6) res = mpf_mul(res, a, wp) n += 1 res = mpf_mul(res, from_int(3*15*35), wp) res = mpf_div(res, from_int(4*16*36), wp) return to_fixed(res, prec) mpf_euler = def_mpf_constant(euler_fixed) mpf_apery = def_mpf_constant(apery_fixed) mpf_khinchin = def_mpf_constant(khinchin_fixed) mpf_glaisher = def_mpf_constant(glaisher_fixed) mpf_catalan = def_mpf_constant(catalan_fixed) mpf_mertens = def_mpf_constant(mertens_fixed) mpf_twinprime = def_mpf_constant(twinprime_fixed) #-----------------------------------------------------------------------# # # # Bernoulli numbers # # # #-----------------------------------------------------------------------# MAX_BERNOULLI_CACHE = 3000 """ Small Bernoulli numbers and factorials are used in numerous summations, so it is critical for speed that sequential computation is fast and that values are cached up to a fairly high threshold. On the other hand, we also want to support fast computation of isolated large numbers. Currently, no such acceleration is provided for integer factorials (though it is for large floating-point factorials, which are computed via gamma if the precision is low enough). For sequential computation of Bernoulli numbers, we use Ramanujan's formula / n + 3 \ B = (A(n) - S(n)) / | | n \ n / where A(n) = (n+3)/3 when n = 0 or 2 (mod 6), A(n) = -(n+3)/6 when n = 4 (mod 6), and [n/6] ___ \ / n + 3 \ S(n) = ) | | * B /___ \ n - 6*k / n-6*k k = 1 For isolated large Bernoulli numbers, we use the Riemann zeta function to calculate a numerical value for B_n. The von Staudt-Clausen theorem can then be used to optionally find the exact value of the numerator and denominator. """ bernoulli_cache = {} f3 = from_int(3) f6 = from_int(6) def bernoulli_size(n): """Accurately estimate the size of B_n (even n > 2 only)""" lgn = math.log(n,2) return int(2.326 + 0.5*lgn + n*(lgn - 4.094)) BERNOULLI_PREC_CUTOFF = bernoulli_size(MAX_BERNOULLI_CACHE) def mpf_bernoulli(n, prec, rnd=None): """Computation of Bernoulli numbers (numerically)""" if n < 2: if n < 0: raise ValueError("Bernoulli numbers only defined for n >= 0") if n == 0: return fone if n == 1: return mpf_neg(fhalf) # For odd n > 1, the Bernoulli numbers are zero if n & 1: return fzero # If precision is extremely high, we can save time by computing # the Bernoulli number at a lower precision that is sufficient to # obtain the exact fraction, round to the exact fraction, and # convert the fraction back to an mpf value at the original precision if prec > BERNOULLI_PREC_CUTOFF and prec > bernoulli_size(n)*1.1 + 1000: p, q = bernfrac(n) return from_rational(p, q, prec, rnd or round_floor) if n > MAX_BERNOULLI_CACHE: return mpf_bernoulli_huge(n, prec, rnd) wp = prec + 30 # Reuse nearby precisions wp += 32 - (prec & 31) cached = bernoulli_cache.get(wp) if cached: numbers, state = cached if n in numbers: if not rnd: return numbers[n] return mpf_pos(numbers[n], prec, rnd) m, bin, bin1 = state if n - m > 10: return mpf_bernoulli_huge(n, prec, rnd) else: if n > 10: return mpf_bernoulli_huge(n, prec, rnd) numbers = {0:fone} m, bin, bin1 = state = [2, MPZ(10), MPZ_ONE] bernoulli_cache[wp] = (numbers, state) while m <= n: #print m case = m % 6 # Accurately estimate size of B_m so we can use # fixed point math without using too much precision szbm = bernoulli_size(m) s = 0 sexp = max(0, szbm) - wp if m < 6: a = MPZ_ZERO else: a = bin1 for j in xrange(1, m//6+1): usign, uman, uexp, ubc = u = numbers[m-6*j] if usign: uman = -uman s += lshift(a*uman, uexp-sexp) # Update inner binomial coefficient j6 = 6*j a *= ((m-5-j6)*(m-4-j6)*(m-3-j6)*(m-2-j6)*(m-1-j6)*(m-j6)) a //= ((4+j6)*(5+j6)*(6+j6)*(7+j6)*(8+j6)*(9+j6)) if case == 0: b = mpf_rdiv_int(m+3, f3, wp) if case == 2: b = mpf_rdiv_int(m+3, f3, wp) if case == 4: b = mpf_rdiv_int(-m-3, f6, wp) s = from_man_exp(s, sexp, wp) b = mpf_div(mpf_sub(b, s, wp), from_int(bin), wp) numbers[m] = b m += 2 # Update outer binomial coefficient bin = bin * ((m+2)*(m+3)) // (m*(m-1)) if m > 6: bin1 = bin1 * ((2+m)*(3+m)) // ((m-7)*(m-6)) state[:] = [m, bin, bin1] return numbers[n] def mpf_bernoulli_huge(n, prec, rnd=None): wp = prec + 10 piprec = wp + int(math.log(n,2)) v = mpf_gamma_int(n+1, wp) v = mpf_mul(v, mpf_zeta_int(n, wp), wp) v = mpf_mul(v, mpf_pow_int(mpf_pi(piprec), -n, wp)) v = mpf_shift(v, 1-n) if not n & 3: v = mpf_neg(v) return mpf_pos(v, prec, rnd or round_fast) def bernfrac(n): r""" Returns a tuple of integers `(p, q)` such that `p/q = B_n` exactly, where `B_n` denotes the `n`-th Bernoulli number. The fraction is always reduced to lowest terms. Note that for `n > 1` and `n` odd, `B_n = 0`, and `(0, 1)` is returned. **Examples** The first few Bernoulli numbers are exactly:: >>> from mpmath import * >>> for n in range(15): ... p, q = bernfrac(n) ... print("%s %s/%s" % (n, p, q)) ... 0 1/1 1 -1/2 2 1/6 3 0/1 4 -1/30 5 0/1 6 1/42 7 0/1 8 -1/30 9 0/1 10 5/66 11 0/1 12 -691/2730 13 0/1 14 7/6 This function works for arbitrarily large `n`:: >>> p, q = bernfrac(10**4) >>> print(q) 2338224387510 >>> print(len(str(p))) 27692 >>> mp.dps = 15 >>> print(mpf(p) / q) -9.04942396360948e+27677 >>> print(bernoulli(10**4)) -9.04942396360948e+27677 .. note :: :func:`~mpmath.bernoulli` computes a floating-point approximation directly, without computing the exact fraction first. This is much faster for large `n`. **Algorithm** :func:`~mpmath.bernfrac` works by computing the value of `B_n` numerically and then using the von Staudt-Clausen theorem [1] to reconstruct the exact fraction. For large `n`, this is significantly faster than computing `B_1, B_2, \ldots, B_2` recursively with exact arithmetic. The implementation has been tested for `n = 10^m` up to `m = 6`. In practice, :func:`~mpmath.bernfrac` appears to be about three times slower than the specialized program calcbn.exe [2] **References** 1. MathWorld, von Staudt-Clausen Theorem: http://mathworld.wolfram.com/vonStaudt-ClausenTheorem.html 2. The Bernoulli Number Page: http://www.bernoulli.org/ """ n = int(n) if n < 3: return [(1, 1), (-1, 2), (1, 6)][n] if n & 1: return (0, 1) q = 1 for k in list_primes(n+1): if not (n % (k-1)): q *= k prec = bernoulli_size(n) + int(math.log(q,2)) + 20 b = mpf_bernoulli(n, prec) p = mpf_mul(b, from_int(q)) pint = to_int(p, round_nearest) return (pint, q) #-----------------------------------------------------------------------# # # # The gamma function (OLD IMPLEMENTATION) # # # #-----------------------------------------------------------------------# """ We compute the real factorial / gamma function using Spouge's approximation x! = (x+a)**(x+1/2) * exp(-x-a) * [c_0 + S(x) + eps] where S(x) is the sum of c_k/(x+k) from k = 1 to a-1 and the coefficients are given by c_0 = sqrt(2*pi) (-1)**(k-1) c_k = ----------- (a-k)**(k-1/2) exp(-k+a), k = 1,2,...,a-1 (k - 1)! As proved by Spouge, if we choose a = log(2)/log(2*pi)*n = 0.38*n, the relative error eps is less than 2^(-n) for any x in the right complex half-plane (assuming a > 2). In practice, it seems that a can be chosen quite a bit lower still (30-50%); this possibility should be investigated. For negative x, we use the reflection formula. References: ----------- John L. Spouge, "Computation of the gamma, digamma, and trigamma functions", SIAM Journal on Numerical Analysis 31 (1994), no. 3, 931-944. """ spouge_cache = {} def calc_spouge_coefficients(a, prec): wp = prec + int(a*1.4) c = [0] * a # b = exp(a-1) b = mpf_exp(from_int(a-1), wp) # e = exp(1) e = mpf_exp(fone, wp) # sqrt(2*pi) sq2pi = mpf_sqrt(mpf_shift(mpf_pi(wp), 1), wp) c[0] = to_fixed(sq2pi, prec) for k in xrange(1, a): # c[k] = ((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k) term = mpf_mul_int(b, ((-1)**(k-1) * (a-k)**k), wp) term = mpf_div(term, mpf_sqrt(from_int(a-k), wp), wp) c[k] = to_fixed(term, prec) # b = b / (e * k) b = mpf_div(b, mpf_mul(e, from_int(k), wp), wp) return c # Cached lookup of coefficients def get_spouge_coefficients(prec): # This exact precision has been used before if prec in spouge_cache: return spouge_cache[prec] for p in spouge_cache: if 0.8 <= prec/float(p) < 1: return spouge_cache[p] # Here we estimate the value of a based on Spouge's inequality for # the relative error a = max(3, int(0.38*prec)) # 0.38 = log(2)/log(2*pi), ~= 1.26*n coefs = calc_spouge_coefficients(a, prec) spouge_cache[prec] = (prec, a, coefs) return spouge_cache[prec] def spouge_sum_real(x, prec, a, c): x = to_fixed(x, prec) s = c[0] for k in xrange(1, a): s += (c[k] << prec) // (x + (k << prec)) return from_man_exp(s, -prec, prec, round_floor) # Unused: for fast computation of gamma(p/q) def spouge_sum_rational(p, q, prec, a, c): s = c[0] for k in xrange(1, a): s += c[k] * q // (p+q*k) return from_man_exp(s, -prec, prec, round_floor) # For a complex number a + b*I, we have # # c_k (a+k)*c_k b * c_k # ------------- = --------- - ------- * I # (a + b*I) + k M M # # 2 2 2 2 2 # where M = (a+k) + b = (a + b ) + (2*a*k + k ) def spouge_sum_complex(re, im, prec, a, c): re = to_fixed(re, prec) im = to_fixed(im, prec) sre, sim = c[0], 0 mag = ((re**2)>>prec) + ((im**2)>>prec) for k in xrange(1, a): M = mag + re*(2*k) + ((k**2) << prec) sre += (c[k] * (re + (k << prec))) // M sim -= (c[k] * im) // M re = from_man_exp(sre, -prec, prec, round_floor) im = from_man_exp(sim, -prec, prec, round_floor) return re, im def mpf_gamma_int_old(n, prec, rounding=round_fast): if n < 1000: return from_int(ifac(n-1), prec, rounding) # XXX: choose the cutoff less arbitrarily size = int(n*math.log(n,2)) if prec > size/20.0: return from_int(ifac(n-1), prec, rounding) return mpf_gamma(from_int(n), prec, rounding) def mpf_factorial_old(x, prec, rounding=round_fast): return mpf_gamma_old(x, prec, rounding, p1=0) def mpc_factorial_old(x, prec, rounding=round_fast): return mpc_gamma_old(x, prec, rounding, p1=0) def mpf_gamma_old(x, prec, rounding=round_fast, p1=1): """ Computes the gamma function of a real floating-point argument. With p1=0, computes a factorial instead. """ sign, man, exp, bc = x if not man: if x == finf: return finf if x == fninf or x == fnan: return fnan # More precision is needed for enormous x. TODO: # use Stirling's formula + Euler-Maclaurin summation size = exp + bc if size > 5: size = int(size * math.log(size,2)) wp = prec + max(0, size) + 15 if exp >= 0: if sign or (p1 and not man): raise ValueError("gamma function pole") # A direct factorial is fastest if exp + bc <= 10: return from_int(ifac((man< 5: size = int(size * math.log(size,2)) reflect = sign or (exp+bc < -1) wp = prec + max(0, size) + 25 # Near x = 0 pole (TODO: other poles) if p1: if size < -prec-5: return mpc_add_mpf(mpc_div(mpc_one, x, 2*prec+10), \ mpf_neg(mpf_euler(2*prec+10)), prec, rounding) elif size < -5: wp += (-2*size) if p1: # Should be done exactly! re_orig = re re = mpf_sub(re, fone, bc+abs(exp)+2) x = re, im if reflect: # Reflection formula wp += 15 pi = mpf_pi(wp), fzero pix = mpc_mul(x, pi, wp) t = mpc_sin_pi(x, wp) u = mpc_sub(mpc_one, x, wp) g = mpc_gamma_old(u, wp) w = mpc_mul(t, g, wp) return mpc_div(pix, w, wp) # Extremely close to the real line? # XXX: reflection formula if iexp+ibc < -wp: a = mpf_gamma_old(re_orig, wp) b = mpf_psi0(re_orig, wp) gamma_diff = mpf_div(a, b, wp) return mpf_pos(a, prec, rounding), mpf_mul(gamma_diff, im, prec, rounding) sprec, a, c = get_spouge_coefficients(wp) s = spouge_sum_complex(re, im, sprec, a, c) # gamma = exp(log(x+a)*(x+0.5) - xpa) * s repa = mpf_add(re, from_int(a), wp) logxpa = mpc_log((repa, im), wp) reph = mpf_add(re, fhalf, wp) t = mpc_sub(mpc_mul(logxpa, (reph, im), wp), (repa, im), wp) t = mpc_mul(mpc_exp(t, wp), s, prec, rounding) return t #-----------------------------------------------------------------------# # # # Polygamma functions # # # #-----------------------------------------------------------------------# """ For all polygamma (psi) functions, we use the Euler-Maclaurin summation formula. It looks slightly different in the m = 0 and m > 0 cases. For m = 0, we have oo ___ B (0) 1 \ 2 k -2 k psi (z) ~ log z + --- - ) ------ z 2 z /___ (2 k)! k = 1 Experiment shows that the minimum term of the asymptotic series reaches 2^(-p) when Re(z) > 0.11*p. So we simply use the recurrence for psi (equivalent, in fact, to summing to the first few terms directly before applying E-M) to obtain z large enough. Since, very crudely, log z ~= 1 for Re(z) > 1, we can use fixed-point arithmetic (if z is extremely large, log(z) itself is a sufficient approximation, so we can stop there already). For Re(z) << 0, we could use recurrence, but this is of course inefficient for large negative z, so there we use the reflection formula instead. For m > 0, we have N - 1 ___ ~~~(m) [ \ 1 ] 1 1 psi (z) ~ [ ) -------- ] + ---------- + -------- + [ /___ m+1 ] m+1 m k = 1 (z+k) ] 2 (z+N) m (z+N) oo ___ B \ 2 k (m+1) (m+2) ... (m+2k-1) + ) ------ ------------------------ /___ (2 k)! m + 2 k k = 1 (z+N) where ~~~ denotes the function rescaled by 1/((-1)^(m+1) m!). Here again N is chosen to make z+N large enough for the minimum term in the last series to become smaller than eps. TODO: the current estimation of N for m > 0 is *very suboptimal*. TODO: implement the reflection formula for m > 0, Re(z) << 0. It is generally a combination of multiple cotangents. Need to figure out a reasonably simple way to generate these formulas on the fly. TODO: maybe use exact algorithms to compute psi for integral and certain rational arguments, as this can be much more efficient. (On the other hand, the availability of these special values provides a convenient way to test the general algorithm.) """ # Harmonic numbers are just shifted digamma functions # We should calculate these exactly when x is an integer # and when doing so is faster. def mpf_harmonic(x, prec, rnd): if x in (fzero, fnan, finf): return x a = mpf_psi0(mpf_add(fone, x, prec+5), prec) return mpf_add(a, mpf_euler(prec+5, rnd), prec, rnd) def mpc_harmonic(z, prec, rnd): if z[1] == fzero: return (mpf_harmonic(z[0], prec, rnd), fzero) a = mpc_psi0(mpc_add_mpf(z, fone, prec+5), prec) return mpc_add_mpf(a, mpf_euler(prec+5, rnd), prec, rnd) def mpf_psi0(x, prec, rnd=round_fast): """ Computation of the digamma function (psi function of order 0) of a real argument. """ sign, man, exp, bc = x wp = prec + 10 if not man: if x == finf: return x if x == fninf or x == fnan: return fnan if x == fzero or (exp >= 0 and sign): raise ValueError("polygamma pole") # Reflection formula if sign and exp+bc > 3: c, s = mpf_cos_sin_pi(x, wp) q = mpf_mul(mpf_div(c, s, wp), mpf_pi(wp), wp) p = mpf_psi0(mpf_sub(fone, x, wp), wp) return mpf_sub(p, q, prec, rnd) # The logarithmic term is accurate enough if (not sign) and bc + exp > wp: return mpf_log(mpf_sub(x, fone, wp), prec, rnd) # Initial recurrence to obtain a large enough x m = to_int(x) n = int(0.11*wp) + 2 s = MPZ_ZERO x = to_fixed(x, wp) one = MPZ_ONE << wp if m < n: for k in xrange(m, n): s -= (one << wp) // x x += one x -= one # Logarithmic term s += to_fixed(mpf_log(from_man_exp(x, -wp, wp), wp), wp) # Endpoint term in Euler-Maclaurin expansion s += (one << wp) // (2*x) # Euler-Maclaurin remainder sum x2 = (x*x) >> wp t = one prev = 0 k = 1 while 1: t = (t*x2) >> wp bsign, bman, bexp, bbc = mpf_bernoulli(2*k, wp) offset = (bexp + 2*wp) if offset >= 0: term = (bman << offset) // (t*(2*k)) else: term = (bman >> (-offset)) // (t*(2*k)) if k & 1: s -= term else: s += term if k > 2 and term >= prev: break prev = term k += 1 return from_man_exp(s, -wp, wp, rnd) def mpc_psi0(z, prec, rnd=round_fast): """ Computation of the digamma function (psi function of order 0) of a complex argument. """ re, im = z # Fall back to the real case if im == fzero: return (mpf_psi0(re, prec, rnd), fzero) wp = prec + 20 sign, man, exp, bc = re # Reflection formula if sign and exp+bc > 3: c = mpc_cos_pi(z, wp) s = mpc_sin_pi(z, wp) q = mpc_mul_mpf(mpc_div(c, s, wp), mpf_pi(wp), wp) p = mpc_psi0(mpc_sub(mpc_one, z, wp), wp) return mpc_sub(p, q, prec, rnd) # Just the logarithmic term if (not sign) and bc + exp > wp: return mpc_log(mpc_sub(z, mpc_one, wp), prec, rnd) # Initial recurrence to obtain a large enough z w = to_int(re) n = int(0.11*wp) + 2 s = mpc_zero if w < n: for k in xrange(w, n): s = mpc_sub(s, mpc_reciprocal(z, wp), wp) z = mpc_add_mpf(z, fone, wp) z = mpc_sub(z, mpc_one, wp) # Logarithmic and endpoint term s = mpc_add(s, mpc_log(z, wp), wp) s = mpc_add(s, mpc_div(mpc_half, z, wp), wp) # Euler-Maclaurin remainder sum z2 = mpc_square(z, wp) t = mpc_one prev = mpc_zero k = 1 eps = mpf_shift(fone, -wp+2) while 1: t = mpc_mul(t, z2, wp) bern = mpf_bernoulli(2*k, wp) term = mpc_mpf_div(bern, mpc_mul_int(t, 2*k, wp), wp) s = mpc_sub(s, term, wp) szterm = mpc_abs(term, 10) if k > 2 and mpf_le(szterm, eps): break prev = term k += 1 return s # Currently unoptimized def mpf_psi(m, x, prec, rnd=round_fast): """ Computation of the polygamma function of arbitrary integer order m >= 0, for a real argument x. """ if m == 0: return mpf_psi0(x, prec, rnd=round_fast) return mpc_psi(m, (x, fzero), prec, rnd)[0] def mpc_psi(m, z, prec, rnd=round_fast): """ Computation of the polygamma function of arbitrary integer order m >= 0, for a complex argument z. """ if m == 0: return mpc_psi0(z, prec, rnd) re, im = z wp = prec + 20 sign, man, exp, bc = re if not im[1]: if im in (finf, fninf, fnan): return (fnan, fnan) if not man: if re == finf and im == fzero: return (fzero, fzero) if re == fnan: return (fnan, fnan) # Recurrence w = to_int(re) n = int(0.4*wp + 4*m) s = mpc_zero if w < n: for k in xrange(w, n): t = mpc_pow_int(z, -m-1, wp) s = mpc_add(s, t, wp) z = mpc_add_mpf(z, fone, wp) zm = mpc_pow_int(z, -m, wp) z2 = mpc_pow_int(z, -2, wp) # 1/m*(z+N)^m integral_term = mpc_div_mpf(zm, from_int(m), wp) s = mpc_add(s, integral_term, wp) # 1/2*(z+N)^(-(m+1)) s = mpc_add(s, mpc_mul_mpf(mpc_div(zm, z, wp), fhalf, wp), wp) a = m + 1 b = 2 k = 1 # Important: we want to sum up to the *relative* error, # not the absolute error, because psi^(m)(z) might be tiny magn = mpc_abs(s, 10) magn = magn[2]+magn[3] eps = mpf_shift(fone, magn-wp+2) while 1: zm = mpc_mul(zm, z2, wp) bern = mpf_bernoulli(2*k, wp) scal = mpf_mul_int(bern, a, wp) scal = mpf_div(scal, from_int(b), wp) term = mpc_mul_mpf(zm, scal, wp) s = mpc_add(s, term, wp) szterm = mpc_abs(term, 10) if k > 2 and mpf_le(szterm, eps): break #print k, to_str(szterm, 10), to_str(eps, 10) a *= (m+2*k)*(m+2*k+1) b *= (2*k+1)*(2*k+2) k += 1 # Scale and sign factor v = mpc_mul_mpf(s, mpf_gamma(from_int(m+1), wp), prec, rnd) if not (m & 1): v = mpf_neg(v[0]), mpf_neg(v[1]) return v #-----------------------------------------------------------------------# # # # Riemann zeta function # # # #-----------------------------------------------------------------------# """ We use zeta(s) = eta(s) / (1 - 2**(1-s)) and Borwein's approximation n-1 ___ k -1 \ (-1) (d_k - d_n) eta(s) ~= ---- ) ------------------ d_n /___ s k = 0 (k + 1) where k ___ i \ (n + i - 1)! 4 d_k = n ) ---------------. /___ (n - i)! (2i)! i = 0 If s = a + b*I, the absolute error for eta(s) is bounded by 3 (1 + 2|b|) ------------ * exp(|b| pi/2) n (3+sqrt(8)) Disregarding the linear term, we have approximately, log(err) ~= log(exp(1.58*|b|)) - log(5.8**n) log(err) ~= 1.58*|b| - log(5.8)*n log(err) ~= 1.58*|b| - 1.76*n log2(err) ~= 2.28*|b| - 2.54*n So for p bits, we should choose n > (p + 2.28*|b|) / 2.54. References: ----------- Peter Borwein, "An Efficient Algorithm for the Riemann Zeta Function" http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P117.ps http://en.wikipedia.org/wiki/Dirichlet_eta_function """ borwein_cache = {} def borwein_coefficients(n): if n in borwein_cache: return borwein_cache[n] ds = [MPZ_ZERO] * (n+1) d = MPZ_ONE s = ds[0] = MPZ_ONE for i in range(1, n+1): d = d * 4 * (n+i-1) * (n-i+1) d //= ((2*i) * ((2*i)-1)) s += d ds[i] = s borwein_cache[n] = ds return ds ZETA_INT_CACHE_MAX_PREC = 1000 zeta_int_cache = {} def mpf_zeta_int(s, prec, rnd=round_fast): """ Optimized computation of zeta(s) for an integer s. """ wp = prec + 20 s = int(s) if s in zeta_int_cache and zeta_int_cache[s][0] >= wp: return mpf_pos(zeta_int_cache[s][1], prec, rnd) if s < 2: if s == 1: raise ValueError("zeta(1) pole") if not s: return mpf_neg(fhalf) return mpf_div(mpf_bernoulli(-s+1, wp), from_int(s-1), prec, rnd) # 2^-s term vanishes? if s >= wp: return mpf_perturb(fone, 0, prec, rnd) # 5^-s term vanishes? elif s >= wp*0.431: t = one = 1 << wp t += 1 << (wp - s) t += one // (MPZ_THREE ** s) t += 1 << max(0, wp - s*2) return from_man_exp(t, -wp, prec, rnd) else: # Fast enough to sum directly? # Even better, we use the Euler product (idea stolen from pari) m = (float(wp)/(s-1) + 1) if m < 30: needed_terms = int(2.0**m + 1) if needed_terms < int(wp/2.54 + 5) / 10: t = fone for k in list_primes(needed_terms): #print k, needed_terms powprec = int(wp - s*math.log(k,2)) if powprec < 2: break a = mpf_sub(fone, mpf_pow_int(from_int(k), -s, powprec), wp) t = mpf_mul(t, a, wp) return mpf_div(fone, t, wp) # Use Borwein's algorithm n = int(wp/2.54 + 5) d = borwein_coefficients(n) t = MPZ_ZERO s = MPZ(s) for k in xrange(n): t += (((-1)**k * (d[k] - d[n])) << wp) // (k+1)**s t = (t << wp) // (-d[n]) t = (t << wp) // ((1 << wp) - (1 << (wp+1-s))) if (s in zeta_int_cache and zeta_int_cache[s][0] < wp) or (s not in zeta_int_cache): zeta_int_cache[s] = (wp, from_man_exp(t, -wp-wp)) return from_man_exp(t, -wp-wp, prec, rnd) def mpf_zeta(s, prec, rnd=round_fast, alt=0): sign, man, exp, bc = s if not man: if s == fzero: if alt: return fhalf else: return mpf_neg(fhalf) if s == finf: return fone return fnan wp = prec + 20 # First term vanishes? if (not sign) and (exp + bc > (math.log(wp,2) + 2)): return mpf_perturb(fone, alt, prec, rnd) # Optimize for integer arguments elif exp >= 0: if alt: if s == fone: return mpf_ln2(prec, rnd) z = mpf_zeta_int(to_int(s), wp, negative_rnd[rnd]) q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(z, q, prec, rnd) else: return mpf_zeta_int(to_int(s), prec, rnd) # Negative: use the reflection formula # Borwein only proves the accuracy bound for x >= 1/2. However, based on # tests, the accuracy without reflection is quite good even some distance # to the left of 1/2. XXX: verify this. if sign: # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_mul(mpf_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpf_sub(fone, s, 10*wp) a = mpf_gamma(y, wp) b = mpf_zeta(y, wp) c = mpf_sin_pi(mpf_shift(s, -1), wp) wp2 = wp + (exp+bc) pi = mpf_pi(wp+wp2) d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) return mpf_mul(a,mpf_mul(b,mpf_mul(c,d,wp),wp),prec,rnd) # Near pole r = mpf_sub(fone, s, wp) asign, aman, aexp, abc = mpf_abs(r) pole_dist = -2*(aexp+abc) if pole_dist > wp: if alt: return mpf_ln2(prec, rnd) else: q = mpf_neg(mpf_div(fone, r, wp)) return mpf_add(q, mpf_euler(wp), prec, rnd) else: wp += max(0, pole_dist) t = MPZ_ZERO #wp += 16 - (prec & 15) # Use Borwein's algorithm n = int(wp/2.54 + 5) d = borwein_coefficients(n) t = MPZ_ZERO sf = to_fixed(s, wp) ln2 = ln2_fixed(wp) for k in xrange(n): u = (-sf*log_int_fixed(k+1, wp, ln2)) >> wp #esign, eman, eexp, ebc = mpf_exp(u, wp) #offset = eexp + wp #if offset >= 0: # w = ((d[k] - d[n]) * eman) << offset #else: # w = ((d[k] - d[n]) * eman) >> (-offset) eman = exp_fixed(u, wp, ln2) w = (d[k] - d[n]) * eman if k & 1: t -= w else: t += w t = t // (-d[n]) t = from_man_exp(t, -wp, wp) if alt: return mpf_pos(t, prec, rnd) else: q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) return mpf_div(t, q, prec, rnd) def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False): re, im = s if im == fzero: return mpf_zeta(re, prec, rnd, alt), fzero # slow for large s if (not force) and mpf_gt(mpc_abs(s, 10), from_int(prec)): raise NotImplementedError wp = prec + 20 # Near pole r = mpc_sub(mpc_one, s, wp) asign, aman, aexp, abc = mpc_abs(r, 10) pole_dist = -2*(aexp+abc) if pole_dist > wp: if alt: q = mpf_ln2(wp) y = mpf_mul(q, mpf_euler(wp), wp) g = mpf_shift(mpf_mul(q, q, wp), -1) g = mpf_sub(y, g) z = mpc_mul_mpf(r, mpf_neg(g), wp) z = mpc_add_mpf(z, q, wp) return mpc_pos(z, prec, rnd) else: q = mpc_neg(mpc_div(mpc_one, r, wp)) q = mpc_add_mpf(q, mpf_euler(wp), wp) return mpc_pos(q, prec, rnd) else: wp += max(0, pole_dist) # Reflection formula. To be rigorous, we should reflect to the left of # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary # slowdown for interesting values of s if mpf_lt(re, fzero): # XXX: could use the separate refl. formula for Dirichlet eta if alt: q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), wp), wp) return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) # XXX: -1 should be done exactly y = mpc_sub(mpc_one, s, 10*wp) a = mpc_gamma(y, wp) b = mpc_zeta(y, wp) c = mpc_sin_pi(mpc_shift(s, -1), wp) rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im mag = max(rexp+rbc, iexp+ibc) wp2 = wp + mag pi = mpf_pi(wp+wp2) pi2 = (mpf_shift(pi, 1), fzero) d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) return mpc_mul(a,mpc_mul(b,mpc_mul(c,d,wp),wp),prec,rnd) n = int(wp/2.54 + 5) n += int(0.9*abs(to_int(im))) d = borwein_coefficients(n) ref = to_fixed(re, wp) imf = to_fixed(im, wp) tre = MPZ_ZERO tim = MPZ_ZERO one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) critical_line = re == fhalf ln2 = ln2_fixed(wp) pi2 = pi_fixed(wp-1) wp2 = wp+wp for k in xrange(n): log = log_int_fixed(k+1, wp, ln2) # A square root is much cheaper than an exp if critical_line: w = one_2wp // isqrt_fast((k+1) << wp2) else: w = exp_fixed((-ref*log) >> wp, wp) if k & 1: w *= (d[n] - d[k]) else: w *= (d[k] - d[n]) wre, wim = cos_sin_fixed((-imf*log)>>wp, wp, pi2) tre += (w * wre) >> wp tim += (w * wim) >> wp tre //= (-d[n]) tim //= (-d[n]) tre = from_man_exp(tre, -wp, wp) tim = from_man_exp(tim, -wp, wp) if alt: return mpc_pos((tre, tim), prec, rnd) else: q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp) return mpc_div((tre, tim), q, prec, rnd) def mpf_altzeta(s, prec, rnd=round_fast): return mpf_zeta(s, prec, rnd, 1) def mpc_altzeta(s, prec, rnd=round_fast): return mpc_zeta(s, prec, rnd, 1) # Not optimized currently mpf_zetasum = None def pow_fixed(x, n, wp): if n == 1: return x y = MPZ_ONE << wp while n: if n & 1: y = (y*x) >> wp n -= 1 x = (x*x) >> wp n //= 2 return y # TODO: optimize / cleanup interface / unify with list_primes sieve_cache = [] primes_cache = [] mult_cache = [] def primesieve(n): global sieve_cache, primes_cache, mult_cache if n < len(sieve_cache): sieve = sieve_cache#[:n+1] primes = primes_cache[:primes_cache.index(max(sieve))+1] mult = mult_cache#[:n+1] return sieve, primes, mult sieve = [0] * (n+1) mult = [0] * (n+1) primes = list_primes(n) for p in primes: #sieve[p::p] = p for k in xrange(p,n+1,p): sieve[k] = p for i, p in enumerate(sieve): if i >= 2: m = 1 n = i // p while not n % p: n //= p m += 1 mult[i] = m sieve_cache = sieve primes_cache = primes mult_cache = mult return sieve, primes, mult def zetasum_sieved(critical_line, sre, sim, a, n, wp): assert a >= 1 sieve, primes, mult = primesieve(a+n) basic_powers = {} one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) wp2 = wp+wp ln2 = ln2_fixed(wp) pi2 = pi_fixed(wp-1) for p in primes: if p*2 > a+n: break log = log_int_fixed(p, wp, ln2) cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) if critical_line: u = one_2wp // isqrt_fast(p<>wp, wp) pre = (u*cos) >> wp pim = (u*sin) >> wp basic_powers[p] = [(pre, pim)] tre, tim = pre, pim for m in range(1,int(math.log(a+n,p)+0.01)+1): tre, tim = ((pre*tre-pim*tim)>>wp), ((pim*tre+pre*tim)>>wp) basic_powers[p].append((tre,tim)) xre = MPZ_ZERO xim = MPZ_ZERO if a == 1: xre += one aa = max(a,2) for k in xrange(aa, a+n+1): p = sieve[k] if p in basic_powers: m = mult[k] tre, tim = basic_powers[p][m-1] while 1: k //= p**m if k == 1: break p = sieve[k] m = mult[k] pre, pim = basic_powers[p][m-1] tre, tim = ((pre*tre-pim*tim)>>wp), ((pim*tre+pre*tim)>>wp) else: log = log_int_fixed(k, wp, ln2) cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) if critical_line: u = one_2wp // isqrt_fast(k<>wp, wp) tre = (u*cos) >> wp tim = (u*sin) >> wp xre += tre xim += tim return xre, xim # Set to something large to disable ZETASUM_SIEVE_CUTOFF = 10 def mpc_zetasum(s, a, n, derivatives, reflect, prec): """ Fast version of mp._zetasum, assuming s = complex, a = integer. """ wp = prec + 10 have_derivatives = derivatives != [0] have_one_derivative = len(derivatives) == 1 # parse s sre, sim = s critical_line = (sre == fhalf) sre = to_fixed(sre, wp) sim = to_fixed(sim, wp) if a > 0 and n > ZETASUM_SIEVE_CUTOFF and not have_derivatives and not reflect: re, im = zetasum_sieved(critical_line, sre, sim, a, n, wp) xs = [(from_man_exp(re, -wp, prec, 'n'), from_man_exp(im, -wp, prec, 'n'))] return xs, [] maxd = max(derivatives) if not have_one_derivative: derivatives = range(maxd+1) # x_d = 0, y_d = 0 xre = [MPZ_ZERO for d in derivatives] xim = [MPZ_ZERO for d in derivatives] if reflect: yre = [MPZ_ZERO for d in derivatives] yim = [MPZ_ZERO for d in derivatives] else: yre = yim = [] one = MPZ_ONE << wp one_2wp = MPZ_ONE << (2*wp) ln2 = ln2_fixed(wp) pi2 = pi_fixed(wp-1) wp2 = wp+wp for w in xrange(a, a+n+1): log = log_int_fixed(w, wp, ln2) cos, sin = cos_sin_fixed((-sim*log)>>wp, wp, pi2) if critical_line: u = one_2wp // isqrt_fast(w<>wp, wp) xterm_re = (u * cos) >> wp xterm_im = (u * sin) >> wp if reflect: reciprocal = (one_2wp // (u*w)) yterm_re = (reciprocal * cos) >> wp yterm_im = (reciprocal * sin) >> wp if have_derivatives: if have_one_derivative: log = pow_fixed(log, maxd, wp) xre[0] += (xterm_re * log) >> wp xim[0] += (xterm_im * log) >> wp if reflect: yre[0] += (yterm_re * log) >> wp yim[0] += (yterm_im * log) >> wp else: t = MPZ_ONE << wp for d in derivatives: xre[d] += (xterm_re * t) >> wp xim[d] += (xterm_im * t) >> wp if reflect: yre[d] += (yterm_re * t) >> wp yim[d] += (yterm_im * t) >> wp t = (t * log) >> wp else: xre[0] += xterm_re xim[0] += xterm_im if reflect: yre[0] += yterm_re yim[0] += yterm_im if have_derivatives: if have_one_derivative: if maxd % 2: xre[0] = -xre[0] xim[0] = -xim[0] if reflect: yre[0] = -yre[0] yim[0] = -yim[0] else: xre = [(-1)**d * xre[d] for d in derivatives] xim = [(-1)**d * xim[d] for d in derivatives] if reflect: yre = [(-1)**d * yre[d] for d in derivatives] yim = [(-1)**d * yim[d] for d in derivatives] xs = [(from_man_exp(xa, -wp, prec, 'n'), from_man_exp(xb, -wp, prec, 'n')) for (xa, xb) in zip(xre, xim)] ys = [(from_man_exp(ya, -wp, prec, 'n'), from_man_exp(yb, -wp, prec, 'n')) for (ya, yb) in zip(yre, yim)] return xs, ys #-----------------------------------------------------------------------# # # # The gamma function (NEW IMPLEMENTATION) # # # #-----------------------------------------------------------------------# # Higher means faster, but more precomputation time MAX_GAMMA_TAYLOR_PREC = 5000 # Need to derive higher bounds for Taylor series to go higher assert MAX_GAMMA_TAYLOR_PREC < 15000 # Use Stirling's series if abs(x) > beta*prec # Important: must be large enough for convergence! GAMMA_STIRLING_BETA = 0.2 SMALL_FACTORIAL_CACHE_SIZE = 150 gamma_taylor_cache = {} gamma_stirling_cache = {} small_factorial_cache = [from_int(ifac(n)) for \ n in range(SMALL_FACTORIAL_CACHE_SIZE+1)] def zeta_array(N, prec): """ zeta(n) = A * pi**n / n! + B where A is a rational number (A = Bernoulli number for n even) and B is an infinite sum over powers of exp(2*pi). (B = 0 for n even). TODO: this is currently only used for gamma, but could be very useful elsewhere. """ extra = 30 wp = prec+extra zeta_values = [MPZ_ZERO] * (N+2) pi = pi_fixed(wp) # STEP 1: one = MPZ_ONE << wp zeta_values[0] = -one//2 f_2pi = mpf_shift(mpf_pi(wp),1) exp_2pi_k = exp_2pi = mpf_exp(f_2pi, wp) # Compute exponential series # Store values of 1/(exp(2*pi*k)-1), # exp(2*pi*k)/(exp(2*pi*k)-1)**2, 1/(exp(2*pi*k)-1)**2 # pi*k*exp(2*pi*k)/(exp(2*pi*k)-1)**2 exps3 = [] k = 1 while 1: tp = wp - 9*k if tp < 1: break # 1/(exp(2*pi*k-1) q1 = mpf_div(fone, mpf_sub(exp_2pi_k, fone, tp), tp) # pi*k*exp(2*pi*k)/(exp(2*pi*k)-1)**2 q2 = mpf_mul(exp_2pi_k, mpf_mul(q1,q1,tp), tp) q1 = to_fixed(q1, wp) q2 = to_fixed(q2, wp) q2 = (k * q2 * pi) >> wp exps3.append((q1, q2)) # Multiply for next round exp_2pi_k = mpf_mul(exp_2pi_k, exp_2pi, wp) k += 1 # Exponential sum for n in xrange(3, N+1, 2): s = MPZ_ZERO k = 1 for e1, e2 in exps3: if n%4 == 3: t = e1 // k**n else: U = (n-1)//4 t = (e1 + e2//U) // k**n if not t: break s += t k += 1 zeta_values[n] = -2*s # Even zeta values B = [mpf_abs(mpf_bernoulli(k,wp)) for k in xrange(N+2)] pi_pow = fpi = mpf_pow_int(mpf_shift(mpf_pi(wp), 1), 2, wp) pi_pow = mpf_div(pi_pow, from_int(4), wp) for n in xrange(2,N+2,2): z = mpf_mul(B[n], pi_pow, wp) zeta_values[n] = to_fixed(z, wp) pi_pow = mpf_mul(pi_pow, fpi, wp) pi_pow = mpf_div(pi_pow, from_int((n+1)*(n+2)), wp) # Zeta sum reciprocal_pi = (one << wp) // pi for n in xrange(3, N+1, 4): U = (n-3)//4 s = zeta_values[4*U+4]*(4*U+7)//4 for k in xrange(1, U+1): s -= (zeta_values[4*k] * zeta_values[4*U+4-4*k]) >> wp zeta_values[n] += (2*s*reciprocal_pi) >> wp for n in xrange(5, N+1, 4): U = (n-1)//4 s = zeta_values[4*U+2]*(2*U+1) for k in xrange(1, 2*U+1): s += ((-1)**k*2*k* zeta_values[2*k] * zeta_values[4*U+2-2*k])>>wp zeta_values[n] += ((s*reciprocal_pi)>>wp)//(2*U) return [x>>extra for x in zeta_values] def gamma_taylor_coefficients(inprec): """ Gives the Taylor coefficients of 1/gamma(1+x) as a list of fixed-point numbers. Enough coefficients are returned to ensure that the series converges to the given precision when x is in [0.5, 1.5]. """ # Reuse nearby cache values (small case) if inprec < 400: prec = inprec + (10-(inprec%10)) elif inprec < 1000: prec = inprec + (30-(inprec%30)) else: prec = inprec if prec in gamma_taylor_cache: return gamma_taylor_cache[prec], prec # Experimentally determined bounds if prec < 1000: N = int(prec**0.76 + 2) else: # Valid to at least 15000 bits N = int(prec**0.787 + 2) # Reuse higher precision values for cprec in gamma_taylor_cache: if cprec > prec: coeffs = [x>>(cprec-prec) for x in gamma_taylor_cache[cprec][-N:]] if inprec < 1000: gamma_taylor_cache[prec] = coeffs return coeffs, prec # Cache at a higher precision (large case) if prec > 1000: prec = int(prec * 1.2) wp = prec + 20 A = [0] * N A[0] = MPZ_ZERO A[1] = MPZ_ONE << wp A[2] = euler_fixed(wp) # SLOW, reference implementation #zeta_values = [0,0]+[to_fixed(mpf_zeta_int(k,wp),wp) for k in xrange(2,N)] zeta_values = zeta_array(N, wp) for k in xrange(3, N): a = (-A[2]*A[k-1])>>wp for j in xrange(2,k): a += ((-1)**j * zeta_values[j] * A[k-j]) >> wp a //= (1-k) A[k] = a A = [a>>20 for a in A] A = A[::-1] A = A[:-1] gamma_taylor_cache[prec] = A #return A, prec return gamma_taylor_coefficients(inprec) def gamma_fixed_taylor(xmpf, x, wp, prec, rnd, type): # Determine nearest multiple of N/2 #n = int(x >> (wp-1)) #steps = (n-1)>>1 nearest_int = ((x >> (wp-1)) + MPZ_ONE) >> 1 one = MPZ_ONE << wp coeffs, cwp = gamma_taylor_coefficients(wp) if nearest_int > 0: r = one for i in xrange(nearest_int-1): x -= one r = (r*x) >> wp x -= one p = MPZ_ZERO for c in coeffs: p = c + ((x*p)>>wp) p >>= (cwp-wp) if type == 0: return from_man_exp((r<> wp x += one p = MPZ_ZERO for c in coeffs: p = c + ((x*p)>>wp) p >>= (cwp-wp) if wp - bitcount(abs(x)) > 10: # pass very close to 0, so do floating-point multiply g = mpf_add(xmpf, from_int(-nearest_int)) # exact r = from_man_exp(p*r,-wp-wp) r = mpf_mul(r, g, wp) if type == 0: return mpf_div(fone, r, prec, rnd) if type == 2: return mpf_pos(r, prec, rnd) if type == 3: return mpf_log(mpf_abs(mpf_div(fone, r, wp)), prec, rnd) else: r = from_man_exp(x*p*r,-3*wp) if type == 0: return mpf_div(fone, r, prec, rnd) if type == 2: return mpf_pos(r, prec, rnd) if type == 3: return mpf_neg(mpf_log(mpf_abs(r), prec, rnd)) def stirling_coefficient(n): if n in gamma_stirling_cache: return gamma_stirling_cache[n] p, q = bernfrac(n) q *= MPZ(n*(n-1)) gamma_stirling_cache[n] = p, q, bitcount(abs(p)), bitcount(q) return gamma_stirling_cache[n] def real_stirling_series(x, prec): """ Sums the rational part of Stirling's expansion, log(sqrt(2*pi)) - z + 1/(12*z) - 1/(360*z^3) + ... """ t = (MPZ_ONE<<(prec+prec)) // x # t = 1/x u = (t*t)>>prec # u = 1/x**2 s = ln_sqrt2pi_fixed(prec) - x # Add initial terms of Stirling's series s += t//12; t = (t*u)>>prec s -= t//360; t = (t*u)>>prec s += t//1260; t = (t*u)>>prec s -= t//1680; t = (t*u)>>prec if not t: return s s += t//1188; t = (t*u)>>prec s -= 691*t//360360; t = (t*u)>>prec s += t//156; t = (t*u)>>prec if not t: return s s -= 3617*t//122400; t = (t*u)>>prec s += 43867*t//244188; t = (t*u)>>prec s -= 174611*t//125400; t = (t*u)>>prec if not t: return s k = 22 # From here on, the coefficients are growing, so we # have to keep t at a roughly constant size usize = bitcount(abs(u)) tsize = bitcount(abs(t)) texp = 0 while 1: p, q, pb, qb = stirling_coefficient(k) term_mag = tsize + pb + texp shift = -texp m = pb - term_mag if m > 0 and shift < m: p >>= m shift -= m m = tsize - term_mag if m > 0 and shift < m: w = t >> m shift -= m else: w = t term = (t*p//q) >> shift if not term: break s += term t = (t*u) >> usize texp -= (prec - usize) k += 2 return s def complex_stirling_series(x, y, prec): # t = 1/z _m = (x*x + y*y) >> prec tre = (x << prec) // _m tim = (-y << prec) // _m # u = 1/z**2 ure = (tre*tre - tim*tim) >> prec uim = tim*tre >> (prec-1) # s = log(sqrt(2*pi)) - z sre = ln_sqrt2pi_fixed(prec) - x sim = -y # Add initial terms of Stirling's series sre += tre//12; sim += tim//12; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= tre//360; sim -= tim//360; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre += tre//1260; sim += tim//1260; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= tre//1680; sim -= tim//1680; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) if abs(tre) + abs(tim) < 5: return sre, sim sre += tre//1188; sim += tim//1188; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= 691*tre//360360; sim -= 691*tim//360360; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre += tre//156; sim += tim//156; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) if abs(tre) + abs(tim) < 5: return sre, sim sre -= 3617*tre//122400; sim -= 3617*tim//122400; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre += 43867*tre//244188; sim += 43867*tim//244188; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) sre -= 174611*tre//125400; sim -= 174611*tim//125400; tre, tim = ((tre*ure-tim*uim)>>prec), ((tre*uim+tim*ure)>>prec) if abs(tre) + abs(tim) < 5: return sre, sim k = 22 # From here on, the coefficients are growing, so we # have to keep t at a roughly constant size usize = bitcount(max(abs(ure), abs(uim))) tsize = bitcount(max(abs(tre), abs(tim))) texp = 0 while 1: p, q, pb, qb = stirling_coefficient(k) term_mag = tsize + pb + texp shift = -texp m = pb - term_mag if m > 0 and shift < m: p >>= m shift -= m m = tsize - term_mag if m > 0 and shift < m: wre = tre >> m wim = tim >> m shift -= m else: wre = tre wim = tim termre = (tre*p//q) >> shift termim = (tim*p//q) >> shift if abs(termre) + abs(termim) < 5: break sre += termre sim += termim tre, tim = ((tre*ure - tim*uim)>>usize), \ ((tre*uim + tim*ure)>>usize) texp -= (prec - usize) k += 2 return sre, sim def mpf_gamma(x, prec, rnd='d', type=0): """ This function implements multipurpose evaluation of the gamma function, G(x), as well as the following versions of the same: type = 0 -- G(x) [standard gamma function] type = 1 -- G(x+1) = x*G(x+1) = x! [factorial] type = 2 -- 1/G(x) [reciprocal gamma function] type = 3 -- log(|G(x)|) [log-gamma function, real part] """ # Specal values sign, man, exp, bc = x if not man: if x == fzero: if type == 1: return fone if type == 2: return fzero raise ValueError("gamma function pole") if x == finf: if type == 2: return fzero return finf return fnan # First of all, for log gamma, numbers can be well beyond the fixed-point # range, so we must take care of huge numbers before e.g. trying # to convert x to the nearest integer if type == 3: wp = prec+20 if exp+bc > wp and not sign: return mpf_sub(mpf_mul(x, mpf_log(x, wp), wp), x, prec, rnd) # We strongly want to special-case small integers is_integer = exp >= 0 if is_integer: # Poles if sign: if type == 2: return fzero raise ValueError("gamma function pole") # n = x n = man << exp if n < SMALL_FACTORIAL_CACHE_SIZE: if type == 0: return mpf_pos(small_factorial_cache[n-1], prec, rnd) if type == 1: return mpf_pos(small_factorial_cache[n], prec, rnd) if type == 2: return mpf_div(fone, small_factorial_cache[n-1], prec, rnd) if type == 3: return mpf_log(small_factorial_cache[n-1], prec, rnd) else: # floor(abs(x)) n = int(man >> (-exp)) # Estimate size and precision # Estimate log(gamma(|x|),2) as x*log(x,2) mag = exp + bc gamma_size = n*mag if type == 3: wp = prec + 20 else: wp = prec + bitcount(gamma_size) + 20 # Very close to 0, pole if mag < -wp: if type == 0: return mpf_sub(mpf_div(fone,x, wp),mpf_shift(fone,-wp),prec,rnd) if type == 1: return mpf_sub(fone, x, prec, rnd) if type == 2: return mpf_add(x, mpf_shift(fone,mag-wp), prec, rnd) if type == 3: return mpf_neg(mpf_log(mpf_abs(x), prec, rnd)) # From now on, we assume having a gamma function if type == 1: return mpf_gamma(mpf_add(x, fone), prec, rnd, 0) # Special case integers (those not small enough to be caught above, # but still small enough for an exact factorial to be faster # than an approximate algorithm), and half-integers if exp >= -1: if is_integer: if gamma_size < 10*wp: if type == 0: return from_int(ifac(n-1), prec, rnd) if type == 2: return from_rational(MPZ_ONE, ifac(n-1), prec, rnd) if type == 3: return mpf_log(from_int(ifac(n-1)), prec, rnd) # half-integer if n < 100 or gamma_size < 10*wp: if sign: w = sqrtpi_fixed(wp) if n % 2: f = ifac2(2*n+1) else: f = -ifac2(2*n+1) if type == 0: return mpf_shift(from_rational(w, f, prec, rnd), -wp+n+1) if type == 2: return mpf_shift(from_rational(f, w, prec, rnd), wp-n-1) if type == 3: return mpf_log(mpf_shift(from_rational(w, abs(f), prec, rnd), -wp+n+1), prec, rnd) elif n == 0: if type == 0: return mpf_sqrtpi(prec, rnd) if type == 2: return mpf_div(fone, mpf_sqrtpi(wp), prec, rnd) if type == 3: return mpf_log(mpf_sqrtpi(wp), prec, rnd) else: w = sqrtpi_fixed(wp) w = from_man_exp(w * ifac2(2*n-1), -wp-n) if type == 0: return mpf_pos(w, prec, rnd) if type == 2: return mpf_div(fone, w, prec, rnd) if type == 3: return mpf_log(mpf_abs(w), prec, rnd) # Convert to fixed point offset = exp + wp if offset >= 0: absxman = man << offset else: absxman = man >> (-offset) # For log gamma, provide accurate evaluation for x = 1+eps and 2+eps if type == 3 and not sign: one = MPZ_ONE << wp one_dist = abs(absxman-one) two_dist = abs(absxman-2*one) cancellation = (wp - bitcount(min(one_dist, two_dist))) if cancellation > 10: xsub1 = mpf_sub(fone, x) xsub2 = mpf_sub(ftwo, x) xsub1mag = xsub1[2]+xsub1[3] xsub2mag = xsub2[2]+xsub2[3] if xsub1mag < -wp: return mpf_mul(mpf_euler(wp), mpf_sub(fone, x), prec, rnd) if xsub2mag < -wp: return mpf_mul(mpf_sub(fone, mpf_euler(wp)), mpf_sub(x, ftwo), prec, rnd) # Proceed but increase precision wp += max(-xsub1mag, -xsub2mag) offset = exp + wp if offset >= 0: absxman = man << offset else: absxman = man >> (-offset) # Use Taylor series if appropriate n_for_stirling = int(GAMMA_STIRLING_BETA*wp) if n < max(100, n_for_stirling) and wp < MAX_GAMMA_TAYLOR_PREC: if sign: absxman = -absxman return gamma_fixed_taylor(x, absxman, wp, prec, rnd, type) # Use Stirling's series # First ensure that |x| is large enough for rapid convergence xorig = x # Argument reduction r = 0 if n < n_for_stirling: r = one = MPZ_ONE << wp d = n_for_stirling - n for k in xrange(d): r = (r * absxman) >> wp absxman += one x = xabs = from_man_exp(absxman, -wp) if sign: x = mpf_neg(x) else: xabs = mpf_abs(x) # Asymptotic series y = real_stirling_series(absxman, wp) u = to_fixed(mpf_log(xabs, wp), wp) u = ((absxman - (MPZ_ONE<<(wp-1))) * u) >> wp y += u w = from_man_exp(y, -wp) # Compute final value if sign: # Reflection formula A = mpf_mul(mpf_sin_pi(xorig, wp), xorig, wp) B = mpf_neg(mpf_pi(wp)) if type == 0 or type == 2: A = mpf_mul(A, mpf_exp(w, wp)) if r: B = mpf_mul(B, from_man_exp(r, -wp), wp) if type == 0: return mpf_div(B, A, prec, rnd) if type == 2: return mpf_div(A, B, prec, rnd) if type == 3: if r: B = mpf_mul(B, from_man_exp(r, -wp), wp) A = mpf_add(mpf_log(mpf_abs(A), wp), w, wp) return mpf_sub(mpf_log(mpf_abs(B), wp), A, prec, rnd) else: if type == 0: if r: return mpf_div(mpf_exp(w, wp), from_man_exp(r, -wp), prec, rnd) return mpf_exp(w, prec, rnd) if type == 2: if r: return mpf_div(from_man_exp(r, -wp), mpf_exp(w, wp), prec, rnd) return mpf_exp(mpf_neg(w), prec, rnd) if type == 3: if r: return mpf_sub(w, mpf_log(from_man_exp(r,-wp), wp), prec, rnd) return mpf_pos(w, prec, rnd) def mpc_gamma(z, prec, rnd='d', type=0): a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero: # Imaginary part on negative half-axis for log-gamma function if type == 3 and asign: re = mpf_gamma(a, prec, rnd, 3) n = (-aman) >> (-aexp) im = mpf_mul_int(mpf_pi(prec+10), n, prec, rnd) return re, im return mpf_gamma(a, prec, rnd, type), fzero # Some kind of complex inf/nan if (not aman and aexp) or (not bman and bexp): return (fnan, fnan) # Initial working precision wp = prec + 20 amag = aexp+abc bmag = bexp+bbc if aman: mag = max(amag, bmag) else: mag = bmag # Close to 0 if mag < -8: if mag < -wp: # 1/gamma(z) = z + euler*z^2 + O(z^3) v = mpc_add(z, mpc_mul_mpf(mpc_mul(z,z,wp),mpf_euler(wp),wp), wp) if type == 0: return mpc_reciprocal(v, prec, rnd) if type == 1: return mpc_div(z, v, prec, rnd) if type == 2: return mpc_pos(v, prec, rnd) if type == 3: return mpc_log(mpc_reciprocal(v, prec), prec, rnd) elif type != 1: wp += (-mag) # Handle huge log-gamma values; must do this before converting to # a fixed-point value. TODO: determine a precise cutoff of validity # depending on amag and bmag if type == 3 and mag > wp and ((not asign) or (bmag >= amag)): return mpc_sub(mpc_mul(z, mpc_log(z, wp), wp), z, prec, rnd) # From now on, we assume having a gamma function if type == 1: return mpc_gamma((mpf_add(a, fone), b), prec, rnd, 0) an = abs(to_int(a)) bn = abs(to_int(b)) absn = max(an, bn) gamma_size = absn*mag if type == 3: pass else: wp += bitcount(gamma_size) # Reflect to the right half-plane. Note that Stirling's expansion # is valid in the left half-plane too, as long as we're not too close # to the real axis, but in order to use this argument reduction # in the negative direction must be implemented. #need_reflection = asign and ((bmag < 0) or (amag-bmag > 4)) need_reflection = asign zorig = z if need_reflection: z = mpc_neg(z) asign, aman, aexp, abc = a = z[0] bsign, bman, bexp, bbc = b = z[1] # Imaginary part very small compared to real one? yfinal = 0 balance_prec = 0 if bmag < -10: # Check z ~= 1 and z ~= 2 for loggamma if type == 3: zsub1 = mpc_sub_mpf(z, fone) if zsub1[0] == fzero: cancel1 = -bmag else: cancel1 = -max(zsub1[0][2]+zsub1[0][3], bmag) if cancel1 > wp: pi = mpf_pi(wp) x = mpc_mul_mpf(zsub1, pi, wp) x = mpc_mul(x, x, wp) x = mpc_div_mpf(x, from_int(12), wp) y = mpc_mul_mpf(zsub1, mpf_neg(mpf_euler(wp)), wp) yfinal = mpc_add(x, y, wp) if not need_reflection: return mpc_pos(yfinal, prec, rnd) elif cancel1 > 0: wp += cancel1 zsub2 = mpc_sub_mpf(z, ftwo) if zsub2[0] == fzero: cancel2 = -bmag else: cancel2 = -max(zsub2[0][2]+zsub2[0][3], bmag) if cancel2 > wp: pi = mpf_pi(wp) t = mpf_sub(mpf_mul(pi, pi), from_int(6)) x = mpc_mul_mpf(mpc_mul(zsub2, zsub2, wp), t, wp) x = mpc_div_mpf(x, from_int(12), wp) y = mpc_mul_mpf(zsub2, mpf_sub(fone, mpf_euler(wp)), wp) yfinal = mpc_add(x, y, wp) if not need_reflection: return mpc_pos(yfinal, prec, rnd) elif cancel2 > 0: wp += cancel2 if bmag < -wp: # Compute directly from the real gamma function. pp = 2*(wp+10) aabs = mpf_abs(a) eps = mpf_shift(fone, amag-wp) x1 = mpf_gamma(aabs, pp, type=type) x2 = mpf_gamma(mpf_add(aabs, eps), pp, type=type) xprime = mpf_div(mpf_sub(x2, x1, pp), eps, pp) y = mpf_mul(b, xprime, prec, rnd) yfinal = (x1, y) # Note: we still need to use the reflection formula for # near-poles, and the correct branch of the log-gamma function if not need_reflection: return mpc_pos(yfinal, prec, rnd) else: balance_prec += (-bmag) wp += balance_prec n_for_stirling = int(GAMMA_STIRLING_BETA*wp) need_reduction = absn < n_for_stirling afix = to_fixed(a, wp) bfix = to_fixed(b, wp) r = 0 if not yfinal: zprered = z # Argument reduction if absn < n_for_stirling: absn = complex(an, bn) d = int((1 + n_for_stirling**2 - bn**2)**0.5 - an) rre = one = MPZ_ONE << wp rim = MPZ_ZERO for k in xrange(d): rre, rim = ((afix*rre-bfix*rim)>>wp), ((afix*rim + bfix*rre)>>wp) afix += one r = from_man_exp(rre, -wp), from_man_exp(rim, -wp) a = from_man_exp(afix, -wp) z = a, b yre, yim = complex_stirling_series(afix, bfix, wp) # (z-1/2)*log(z) + S lre, lim = mpc_log(z, wp) lre = to_fixed(lre, wp) lim = to_fixed(lim, wp) yre = ((lre*afix - lim*bfix)>>wp) - (lre>>1) + yre yim = ((lre*bfix + lim*afix)>>wp) - (lim>>1) + yim y = from_man_exp(yre, -wp), from_man_exp(yim, -wp) if r and type == 3: # If re(z) > 0 and abs(z) <= 4, the branches of loggamma(z) # and log(gamma(z)) coincide. Otherwise, use the zeroth order # Stirling expansion to compute the correct imaginary part. y = mpc_sub(y, mpc_log(r, wp), wp) zfa = to_float(zprered[0]) zfb = to_float(zprered[1]) zfabs = math.hypot(zfa,zfb) #if not (zfa > 0.0 and zfabs <= 4): yfb = to_float(y[1]) u = math.atan2(zfb, zfa) if zfabs <= 0.5: gi = 0.577216*zfb - u else: gi = -zfb - 0.5*u + zfa*u + zfb*math.log(zfabs) n = int(math.floor((gi-yfb)/(2*math.pi)+0.5)) y = (y[0], mpf_add(y[1], mpf_mul_int(mpf_pi(wp), 2*n, wp), wp)) if need_reflection: if type == 0 or type == 2: A = mpc_mul(mpc_sin_pi(zorig, wp), zorig, wp) B = (mpf_neg(mpf_pi(wp)), fzero) if yfinal: if type == 2: A = mpc_div(A, yfinal, wp) else: A = mpc_mul(A, yfinal, wp) else: A = mpc_mul(A, mpc_exp(y, wp), wp) if r: B = mpc_mul(B, r, wp) if type == 0: return mpc_div(B, A, prec, rnd) if type == 2: return mpc_div(A, B, prec, rnd) # Reflection formula for the log-gamma function with correct branch # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0006/ # LogGamma[z] == -LogGamma[-z] - Log[-z] + # Sign[Im[z]] Floor[Re[z]] Pi I + Log[Pi] - # Log[Sin[Pi (z - Floor[Re[z]])]] - # Pi I (1 - Abs[Sign[Im[z]]]) Abs[Floor[Re[z]]] if type == 3: if yfinal: s1 = mpc_neg(yfinal) else: s1 = mpc_neg(y) # s -= log(-z) s1 = mpc_sub(s1, mpc_log(mpc_neg(zorig), wp), wp) # floor(re(z)) rezfloor = mpf_floor(zorig[0]) imzsign = mpf_sign(zorig[1]) pi = mpf_pi(wp) t = mpf_mul(pi, rezfloor) t = mpf_mul_int(t, imzsign, wp) s1 = (s1[0], mpf_add(s1[1], t, wp)) s1 = mpc_add_mpf(s1, mpf_log(pi, wp), wp) t = mpc_sin_pi(mpc_sub_mpf(zorig, rezfloor), wp) t = mpc_log(t, wp) s1 = mpc_sub(s1, t, wp) # Note: may actually be unused, because we fall back # to the mpf_ function for real arguments if not imzsign: t = mpf_mul(pi, mpf_floor(rezfloor), wp) s1 = (s1[0], mpf_sub(s1[1], t, wp)) return mpc_pos(s1, prec, rnd) else: if type == 0: if r: return mpc_div(mpc_exp(y, wp), r, prec, rnd) return mpc_exp(y, prec, rnd) if type == 2: if r: return mpc_div(r, mpc_exp(y, wp), prec, rnd) return mpc_exp(mpc_neg(y), prec, rnd) if type == 3: return mpc_pos(y, prec, rnd) def mpf_factorial(x, prec, rnd='d'): return mpf_gamma(x, prec, rnd, 1) def mpc_factorial(x, prec, rnd='d'): return mpc_gamma(x, prec, rnd, 1) def mpf_rgamma(x, prec, rnd='d'): return mpf_gamma(x, prec, rnd, 2) def mpc_rgamma(x, prec, rnd='d'): return mpc_gamma(x, prec, rnd, 2) def mpf_loggamma(x, prec, rnd='d'): sign, man, exp, bc = x if sign: raise ComplexResult return mpf_gamma(x, prec, rnd, 3) def mpc_loggamma(z, prec, rnd='d'): a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero and asign: re = mpf_gamma(a, prec, rnd, 3) n = (-aman) >> (-aexp) im = mpf_mul_int(mpf_pi(prec+10), n, prec, rnd) return re, im return mpc_gamma(z, prec, rnd, 3) def mpf_gamma_int(n, prec, rnd=round_fast): if n < SMALL_FACTORIAL_CACHE_SIZE: return mpf_pos(small_factorial_cache[n-1], prec, rnd) return mpf_gamma(from_int(n), prec, rnd) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/libmpi.py0000644000175000017500000006570212014170666025330 0ustar georgeskgeorgesk""" Computational functions for interval arithmetic. """ from .backend import xrange from .libmpf import ( ComplexResult, round_down, round_up, round_floor, round_ceiling, round_nearest, prec_to_dps, repr_dps, dps_to_prec, bitcount, from_float, fnan, finf, fninf, fzero, fhalf, fone, fnone, mpf_sign, mpf_lt, mpf_le, mpf_gt, mpf_ge, mpf_eq, mpf_cmp, mpf_min_max, mpf_floor, from_int, to_int, to_str, from_str, mpf_abs, mpf_neg, mpf_pos, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, mpf_div, mpf_shift, mpf_pow_int, from_man_exp, MPZ_ONE) from .libelefun import ( mpf_log, mpf_exp, mpf_sqrt, mpf_atan, mpf_atan2, mpf_pi, mod_pi2, mpf_cos_sin ) from .gammazeta import mpf_gamma, mpf_rgamma, mpf_loggamma, mpc_loggamma def mpi_str(s, prec): sa, sb = s dps = prec_to_dps(prec) + 5 return "[%s, %s]" % (to_str(sa, dps), to_str(sb, dps)) #dps = prec_to_dps(prec) #m = mpi_mid(s, prec) #d = mpf_shift(mpi_delta(s, 20), -1) #return "%s +/- %s" % (to_str(m, dps), to_str(d, 3)) mpi_zero = (fzero, fzero) mpi_one = (fone, fone) def mpi_eq(s, t): return s == t def mpi_ne(s, t): return s != t def mpi_lt(s, t): sa, sb = s ta, tb = t if mpf_lt(sb, ta): return True if mpf_ge(sa, tb): return False return None def mpi_le(s, t): sa, sb = s ta, tb = t if mpf_le(sb, ta): return True if mpf_gt(sa, tb): return False return None def mpi_gt(s, t): return mpi_lt(t, s) def mpi_ge(s, t): return mpi_le(t, s) def mpi_add(s, t, prec=0): sa, sb = s ta, tb = t a = mpf_add(sa, ta, prec, round_floor) b = mpf_add(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf return a, b def mpi_sub(s, t, prec=0): sa, sb = s ta, tb = t a = mpf_sub(sa, tb, prec, round_floor) b = mpf_sub(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf return a, b def mpi_delta(s, prec): sa, sb = s return mpf_sub(sb, sa, prec, round_up) def mpi_mid(s, prec): sa, sb = s return mpf_shift(mpf_add(sa, sb, prec, round_nearest), -1) def mpi_pos(s, prec): sa, sb = s a = mpf_pos(sa, prec, round_floor) b = mpf_pos(sb, prec, round_ceiling) return a, b def mpi_neg(s, prec=0): sa, sb = s a = mpf_neg(sb, prec, round_floor) b = mpf_neg(sa, prec, round_ceiling) return a, b def mpi_abs(s, prec=0): sa, sb = s sas = mpf_sign(sa) sbs = mpf_sign(sb) # Both points nonnegative? if sas >= 0: a = mpf_pos(sa, prec, round_floor) b = mpf_pos(sb, prec, round_ceiling) # Upper point nonnegative? elif sbs >= 0: a = fzero negsa = mpf_neg(sa) if mpf_lt(negsa, sb): b = mpf_pos(sb, prec, round_ceiling) else: b = mpf_pos(negsa, prec, round_ceiling) # Both negative? else: a = mpf_neg(sb, prec, round_floor) b = mpf_neg(sa, prec, round_ceiling) return a, b # TODO: optimize def mpi_mul_mpf(s, t, prec): return mpi_mul(s, (t, t), prec) def mpi_div_mpf(s, t, prec): return mpi_div(s, (t, t), prec) def mpi_mul(s, t, prec=0): sa, sb = s ta, tb = t sas = mpf_sign(sa) sbs = mpf_sign(sb) tas = mpf_sign(ta) tbs = mpf_sign(tb) if sas == sbs == 0: # Should maybe be undefined if ta == fninf or tb == finf: return fninf, finf return fzero, fzero if tas == tbs == 0: # Should maybe be undefined if sa == fninf or sb == finf: return fninf, finf return fzero, fzero if sas >= 0: # positive * positive if tas >= 0: a = mpf_mul(sa, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # positive * negative elif tbs <= 0: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sa, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # positive * both signs else: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf elif sbs <= 0: # negative * positive if tas >= 0: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # negative * negative elif tbs <= 0: a = mpf_mul(sb, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # negative * both signs else: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf else: # General case: perform all cross-multiplications and compare # Since the multiplications can be done exactly, we need only # do 4 (instead of 8: two for each rounding mode) cases = [mpf_mul(sa, ta), mpf_mul(sa, tb), mpf_mul(sb, ta), mpf_mul(sb, tb)] if fnan in cases: a, b = (fninf, finf) else: a, b = mpf_min_max(cases) a = mpf_pos(a, prec, round_floor) b = mpf_pos(b, prec, round_ceiling) return a, b def mpi_square(s, prec=0): sa, sb = s if mpf_ge(sa, fzero): a = mpf_mul(sa, sa, prec, round_floor) b = mpf_mul(sb, sb, prec, round_ceiling) elif mpf_le(sb, fzero): a = mpf_mul(sb, sb, prec, round_floor) b = mpf_mul(sa, sa, prec, round_ceiling) else: sa = mpf_neg(sa) sa, sb = mpf_min_max([sa, sb]) a = fzero b = mpf_mul(sb, sb, prec, round_ceiling) return a, b def mpi_div(s, t, prec): sa, sb = s ta, tb = t sas = mpf_sign(sa) sbs = mpf_sign(sb) tas = mpf_sign(ta) tbs = mpf_sign(tb) # 0 / X if sas == sbs == 0: # 0 / if (tas < 0 and tbs > 0) or (tas == 0 or tbs == 0): return fninf, finf return fzero, fzero # Denominator contains both negative and positive numbers; # this should properly be a multi-interval, but the closest # match is the entire (extended) real line if tas < 0 and tbs > 0: return fninf, finf # Assume denominator to be nonnegative if tas < 0: return mpi_div(mpi_neg(s), mpi_neg(t), prec) # Division by zero # XXX: make sure all results make sense if tas == 0: # Numerator contains both signs? if sas < 0 and sbs > 0: return fninf, finf if tas == tbs: return fninf, finf # Numerator positive? if sas >= 0: a = mpf_div(sa, tb, prec, round_floor) b = finf if sbs <= 0: a = fninf b = mpf_div(sb, tb, prec, round_ceiling) # Division with positive denominator # We still have to handle nans resulting from inf/0 or inf/inf else: # Nonnegative numerator if sas >= 0: a = mpf_div(sa, tb, prec, round_floor) b = mpf_div(sb, ta, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # Nonpositive numerator elif sbs <= 0: a = mpf_div(sa, ta, prec, round_floor) b = mpf_div(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # Numerator contains both signs? else: a = mpf_div(sa, ta, prec, round_floor) b = mpf_div(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf return a, b def mpi_pi(prec): a = mpf_pi(prec, round_floor) b = mpf_pi(prec, round_ceiling) return a, b def mpi_exp(s, prec): sa, sb = s # exp is monotonic a = mpf_exp(sa, prec, round_floor) b = mpf_exp(sb, prec, round_ceiling) return a, b def mpi_log(s, prec): sa, sb = s # log is monotonic a = mpf_log(sa, prec, round_floor) b = mpf_log(sb, prec, round_ceiling) return a, b def mpi_sqrt(s, prec): sa, sb = s # sqrt is monotonic a = mpf_sqrt(sa, prec, round_floor) b = mpf_sqrt(sb, prec, round_ceiling) return a, b def mpi_atan(s, prec): sa, sb = s a = mpf_atan(sa, prec, round_floor) b = mpf_atan(sb, prec, round_ceiling) return a, b def mpi_pow_int(s, n, prec): sa, sb = s if n < 0: return mpi_div((fone, fone), mpi_pow_int(s, -n, prec+20), prec) if n == 0: return (fone, fone) if n == 1: return s if n == 2: return mpi_square(s, prec) # Odd -- signs are preserved if n & 1: a = mpf_pow_int(sa, n, prec, round_floor) b = mpf_pow_int(sb, n, prec, round_ceiling) # Even -- important to ensure positivity else: sas = mpf_sign(sa) sbs = mpf_sign(sb) # Nonnegative? if sas >= 0: a = mpf_pow_int(sa, n, prec, round_floor) b = mpf_pow_int(sb, n, prec, round_ceiling) # Nonpositive? elif sbs <= 0: a = mpf_pow_int(sb, n, prec, round_floor) b = mpf_pow_int(sa, n, prec, round_ceiling) # Mixed signs? else: a = fzero # max(-a,b)**n sa = mpf_neg(sa) if mpf_ge(sa, sb): b = mpf_pow_int(sa, n, prec, round_ceiling) else: b = mpf_pow_int(sb, n, prec, round_ceiling) return a, b def mpi_pow(s, t, prec): ta, tb = t if ta == tb and ta not in (finf, fninf): if ta == from_int(to_int(ta)): return mpi_pow_int(s, to_int(ta), prec) if ta == fhalf: return mpi_sqrt(s, prec) u = mpi_log(s, prec + 20) v = mpi_mul(u, t, prec + 20) return mpi_exp(v, prec) def MIN(x, y): if mpf_le(x, y): return x return y def MAX(x, y): if mpf_ge(x, y): return x return y def cos_sin_quadrant(x, wp): sign, man, exp, bc = x if x == fzero: return fone, fzero, 0 # TODO: combine evaluation code to avoid duplicate modulo c, s = mpf_cos_sin(x, wp) t, n, wp_ = mod_pi2(man, exp, exp+bc, 15) if sign: n = -1-n return c, s, n def mpi_cos_sin(x, prec): a, b = x if a == b == fzero: return (fone, fone), (fzero, fzero) # Guaranteed to contain both -1 and 1 if (finf in x) or (fninf in x): return (fnone, fone), (fnone, fone) wp = prec + 20 ca, sa, na = cos_sin_quadrant(a, wp) cb, sb, nb = cos_sin_quadrant(b, wp) ca, cb = mpf_min_max([ca, cb]) sa, sb = mpf_min_max([sa, sb]) # Both functions are monotonic within one quadrant if na == nb: pass # Guaranteed to contain both -1 and 1 elif nb - na >= 4: return (fnone, fone), (fnone, fone) else: # cos has maximum between a and b if na//4 != nb//4: cb = fone # cos has minimum if (na-2)//4 != (nb-2)//4: ca = fnone # sin has maximum if (na-1)//4 != (nb-1)//4: sb = fone # sin has minimum if (na-3)//4 != (nb-3)//4: sa = fnone # Perturb to force interval rounding more = from_man_exp((MPZ_ONE<= 1: if sign: return fnone return fone return v ca = finalize(ca, round_floor) cb = finalize(cb, round_ceiling) sa = finalize(sa, round_floor) sb = finalize(sb, round_ceiling) return (ca,cb), (sa,sb) def mpi_cos(x, prec): return mpi_cos_sin(x, prec)[0] def mpi_sin(x, prec): return mpi_cos_sin(x, prec)[1] def mpi_tan(x, prec): cos, sin = mpi_cos_sin(x, prec+20) return mpi_div(sin, cos, prec) def mpi_cot(x, prec): cos, sin = mpi_cos_sin(x, prec+20) return mpi_div(cos, sin, prec) def mpi_from_str_a_b(x, y, percent, prec): wp = prec + 20 xa = from_str(x, wp, round_floor) xb = from_str(x, wp, round_ceiling) #ya = from_str(y, wp, round_floor) y = from_str(y, wp, round_ceiling) assert mpf_ge(y, fzero) if percent: y = mpf_mul(MAX(mpf_abs(xa), mpf_abs(xb)), y, wp, round_ceiling) y = mpf_div(y, from_int(100), wp, round_ceiling) a = mpf_sub(xa, y, prec, round_floor) b = mpf_add(xb, y, prec, round_ceiling) return a, b def mpi_from_str(s, prec): """ Parse an interval number given as a string. Allowed forms are "-1.23e-27" Any single decimal floating-point literal. "a +- b" or "a (b)" a is the midpoint of the interval and b is the half-width "a +- b%" or "a (b%)" a is the midpoint of the interval and the half-width is b percent of a (`a \times b / 100`). "[a, b]" The interval indicated directly. "x[y,z]e" x are shared digits, y and z are unequal digits, e is the exponent. """ e = ValueError("Improperly formed interval number '%s'" % s) s = s.replace(" ", "") wp = prec + 20 if "+-" in s: x, y = s.split("+-") return mpi_from_str_a_b(x, y, False, prec) # case 2 elif "(" in s: # Don't confuse with a complex number (x,y) if s[0] == "(" or ")" not in s: raise e s = s.replace(")", "") percent = False if "%" in s: if s[-1] != "%": raise e percent = True s = s.replace("%", "") x, y = s.split("(") return mpi_from_str_a_b(x, y, percent, prec) elif "," in s: if ('[' not in s) or (']' not in s): raise e if s[0] == '[': # case 3 s = s.replace("[", "") s = s.replace("]", "") a, b = s.split(",") a = from_str(a, prec, round_floor) b = from_str(b, prec, round_ceiling) return a, b else: # case 4 x, y = s.split('[') y, z = y.split(',') if 'e' in s: z, e = z.split(']') else: z, e = z.rstrip(']'), '' a = from_str(x+y+e, prec, round_floor) b = from_str(x+z+e, prec, round_ceiling) return a, b else: a = from_str(s, prec, round_floor) b = from_str(s, prec, round_ceiling) return a, b def mpi_to_str(x, dps, use_spaces=True, brackets='[]', mode='brackets', error_dps=4, **kwargs): """ Convert a mpi interval to a string. **Arguments** *dps* decimal places to use for printing *use_spaces* use spaces for more readable output, defaults to true *brackets* pair of strings (or two-character string) giving left and right brackets *mode* mode of display: 'plusminus', 'percent', 'brackets' (default) or 'diff' *error_dps* limit the error to *error_dps* digits (mode 'plusminus and 'percent') Additional keyword arguments are forwarded to the mpf-to-string conversion for the components of the output. **Examples** >>> from mpmath import mpi, mp >>> mp.dps = 30 >>> x = mpi(1, 2) >>> mpi_to_str(x, mode='plusminus') '1.5 +- 5.0e-1' >>> mpi_to_str(x, mode='percent') '1.5 (33.33%)' >>> mpi_to_str(x, mode='brackets') '[1.0, 2.0]' >>> mpi_to_str(x, mode='brackets' , brackets=('<', '>')) '<1.0, 2.0>' >>> x = mpi('5.2582327113062393041', '5.2582327113062749951') >>> mpi_to_str(x, mode='diff') '5.2582327113062[4, 7]' >>> mpi_to_str(mpi(0), mode='percent') '0.0 (0%)' """ prec = dps_to_prec(dps) wp = prec + 20 a, b = x mid = mpi_mid(x, prec) delta = mpi_delta(x, prec) a_str = to_str(a, dps, **kwargs) b_str = to_str(b, dps, **kwargs) mid_str = to_str(mid, dps, **kwargs) sp = "" if use_spaces: sp = " " br1, br2 = brackets if mode == 'plusminus': delta_str = to_str(mpf_shift(delta,-1), dps, **kwargs) s = mid_str + sp + "+-" + sp + delta_str elif mode == 'percent': if mid == fzero: p = fzero else: # p = 100 * delta(x) / (2*mid(x)) p = mpf_mul(delta, from_int(100)) p = mpf_div(p, mpf_mul(mid, from_int(2)), wp) s = mid_str + sp + "(" + to_str(p, error_dps) + "%)" elif mode == 'brackets': s = br1 + a_str + "," + sp + b_str + br2 elif mode == 'diff': # use more digits if str(x.a) and str(x.b) are equal if a_str == b_str: a_str = to_str(a, dps+3, **kwargs) b_str = to_str(b, dps+3, **kwargs) # separate mantissa and exponent a = a_str.split('e') if len(a) == 1: a.append('') b = b_str.split('e') if len(b) == 1: b.append('') if a[1] == b[1]: if a[0] != b[0]: for i in xrange(len(a[0]) + 1): if a[0][i] != b[0][i]: break s = (a[0][:i] + br1 + a[0][i:] + ',' + sp + b[0][i:] + br2 + 'e'*min(len(a[1]), 1) + a[1]) else: # no difference s = a[0] + br1 + br2 + 'e'*min(len(a[1]), 1) + a[1] else: s = br1 + 'e'.join(a) + ',' + sp + 'e'.join(b) + br2 else: raise ValueError("'%s' is unknown mode for printing mpi" % mode) return s def mpci_add(x, y, prec): a, b = x c, d = y return mpi_add(a, c, prec), mpi_add(b, d, prec) def mpci_sub(x, y, prec): a, b = x c, d = y return mpi_sub(a, c, prec), mpi_sub(b, d, prec) def mpci_neg(x, prec=0): a, b = x return mpi_neg(a, prec), mpi_neg(b, prec) def mpci_pos(x, prec): a, b = x return mpi_pos(a, prec), mpi_pos(b, prec) def mpci_mul(x, y, prec): # TODO: optimize for real/imag cases a, b = x c, d = y r1 = mpi_mul(a,c) r2 = mpi_mul(b,d) re = mpi_sub(r1,r2,prec) i1 = mpi_mul(a,d) i2 = mpi_mul(b,c) im = mpi_add(i1,i2,prec) return re, im def mpci_div(x, y, prec): # TODO: optimize for real/imag cases a, b = x c, d = y wp = prec+20 m1 = mpi_square(c) m2 = mpi_square(d) m = mpi_add(m1,m2,wp) re = mpi_add(mpi_mul(a,c), mpi_mul(b,d), wp) im = mpi_sub(mpi_mul(b,c), mpi_mul(a,d), wp) re = mpi_div(re, m, prec) im = mpi_div(im, m, prec) return re, im def mpci_exp(x, prec): a, b = x wp = prec+20 r = mpi_exp(a, wp) c, s = mpi_cos_sin(b, wp) a = mpi_mul(r, c, prec) b = mpi_mul(r, s, prec) return a, b def mpi_shift(x, n): a, b = x return mpf_shift(a,n), mpf_shift(b,n) def mpi_cosh_sinh(x, prec): # TODO: accuracy for small x wp = prec+20 e1 = mpi_exp(x, wp) e2 = mpi_div(mpi_one, e1, wp) c = mpi_add(e1, e2, prec) s = mpi_sub(e1, e2, prec) c = mpi_shift(c, -1) s = mpi_shift(s, -1) return c, s def mpci_cos(x, prec): a, b = x wp = prec+10 c, s = mpi_cos_sin(a, wp) ch, sh = mpi_cosh_sinh(b, wp) re = mpi_mul(c, ch, prec) im = mpi_mul(s, sh, prec) return re, mpi_neg(im) def mpci_sin(x, prec): a, b = x wp = prec+10 c, s = mpi_cos_sin(a, wp) ch, sh = mpi_cosh_sinh(b, wp) re = mpi_mul(s, ch, prec) im = mpi_mul(c, sh, prec) return re, im def mpci_abs(x, prec): a, b = x if a == mpi_zero: return mpi_abs(b) if b == mpi_zero: return mpi_abs(a) # Important: nonnegative a = mpi_square(a) b = mpi_square(b) t = mpi_add(a, b, prec+20) return mpi_sqrt(t, prec) def mpi_atan2(y, x, prec): ya, yb = y xa, xb = x # Constrained to the real line if ya == yb == fzero: if mpf_ge(xa, fzero): return mpi_zero return mpi_pi(prec) # Right half-plane if mpf_ge(xa, fzero): if mpf_ge(ya, fzero): a = mpf_atan2(ya, xb, prec, round_floor) else: a = mpf_atan2(ya, xa, prec, round_floor) if mpf_ge(yb, fzero): b = mpf_atan2(yb, xa, prec, round_ceiling) else: b = mpf_atan2(yb, xb, prec, round_ceiling) # Upper half-plane elif mpf_ge(ya, fzero): b = mpf_atan2(ya, xa, prec, round_ceiling) if mpf_le(xb, fzero): a = mpf_atan2(yb, xb, prec, round_floor) else: a = mpf_atan2(ya, xb, prec, round_floor) # Lower half-plane elif mpf_le(yb, fzero): a = mpf_atan2(yb, xa, prec, round_floor) if mpf_le(xb, fzero): b = mpf_atan2(ya, xb, prec, round_ceiling) else: b = mpf_atan2(yb, xb, prec, round_ceiling) # Covering the origin else: b = mpf_pi(prec, round_ceiling) a = mpf_neg(b) return a, b def mpci_arg(z, prec): x, y = z return mpi_atan2(y, x, prec) def mpci_log(z, prec): x, y = z re = mpi_log(mpci_abs(z, prec+20), prec) im = mpci_arg(z, prec) return re, im def mpci_pow(x, y, prec): # TODO: recognize/speed up real cases, integer y yre, yim = y if yim == mpi_zero: ya, yb = yre if ya == yb: sign, man, exp, bc = yb if man and exp >= 0: return mpci_pow_int(x, (-1)**sign * int(man<>= 1 return mpci_pos(result, prec) gamma_min_a = from_float(1.46163214496) gamma_min_b = from_float(1.46163214497) gamma_min = (gamma_min_a, gamma_min_b) gamma_mono_imag_a = from_float(-1.1) gamma_mono_imag_b = from_float(1.1) def mpi_overlap(x, y): a, b = x c, d = y if mpf_lt(d, a): return False if mpf_gt(c, b): return False return True # type = 0 -- gamma # type = 1 -- factorial # type = 2 -- 1/gamma # type = 3 -- log-gamma def mpi_gamma(z, prec, type=0): a, b = z wp = prec+20 if type == 1: return mpi_gamma(mpi_add(z, mpi_one, wp), prec, 0) # increasing if mpf_gt(a, gamma_min_b): if type == 0: c = mpf_gamma(a, prec, round_floor) d = mpf_gamma(b, prec, round_ceiling) elif type == 2: c = mpf_rgamma(b, prec, round_floor) d = mpf_rgamma(a, prec, round_ceiling) elif type == 3: c = mpf_loggamma(a, prec, round_floor) d = mpf_loggamma(b, prec, round_ceiling) # decreasing elif mpf_gt(a, fzero) and mpf_lt(b, gamma_min_a): if type == 0: c = mpf_gamma(b, prec, round_floor) d = mpf_gamma(a, prec, round_ceiling) elif type == 2: c = mpf_rgamma(a, prec, round_floor) d = mpf_rgamma(b, prec, round_ceiling) elif type == 3: c = mpf_loggamma(b, prec, round_floor) d = mpf_loggamma(a, prec, round_ceiling) else: # TODO: reflection formula znew = mpi_add(z, mpi_one, wp) if type == 0: return mpi_div(mpi_gamma(znew, prec+2, 0), z, prec) if type == 2: return mpi_mul(mpi_gamma(znew, prec+2, 2), z, prec) if type == 3: return mpi_sub(mpi_gamma(znew, prec+2, 3), mpi_log(z, prec+2), prec) return c, d def mpci_gamma(z, prec, type=0): (a1,a2), (b1,b2) = z # Real case if b1 == b2 == fzero and (type != 3 or mpf_gt(a1,fzero)): return mpi_gamma(z, prec, type), mpi_zero # Estimate precision wp = prec+20 if type != 3: amag = a2[2]+a2[3] bmag = b2[2]+b2[3] if a2 != fzero: mag = max(amag, bmag) else: mag = bmag an = abs(to_int(a2)) bn = abs(to_int(b2)) absn = max(an, bn) gamma_size = max(0,absn*mag) wp += bitcount(gamma_size) # Assume type != 1 if type == 1: (a1,a2) = mpi_add((a1,a2), mpi_one, wp); z = (a1,a2), (b1,b2) type = 0 # Avoid non-monotonic region near the negative real axis if mpf_lt(a1, gamma_min_b): if mpi_overlap((b1,b2), (gamma_mono_imag_a, gamma_mono_imag_b)): # TODO: reflection formula #if mpf_lt(a2, mpf_shift(fone,-1)): # znew = mpci_sub((mpi_one,mpi_zero),z,wp) # ... # Recurrence: # gamma(z) = gamma(z+1)/z znew = mpi_add((a1,a2), mpi_one, wp), (b1,b2) if type == 0: return mpci_div(mpci_gamma(znew, prec+2, 0), z, prec) if type == 2: return mpci_mul(mpci_gamma(znew, prec+2, 2), z, prec) if type == 3: return mpci_sub(mpci_gamma(znew, prec+2, 3), mpci_log(z,prec+2), prec) # Use monotonicity (except for a small region close to the # origin and near poles) # upper half-plane if mpf_ge(b1, fzero): minre = mpc_loggamma((a1,b2), wp, round_floor) maxre = mpc_loggamma((a2,b1), wp, round_ceiling) minim = mpc_loggamma((a1,b1), wp, round_floor) maxim = mpc_loggamma((a2,b2), wp, round_ceiling) # lower half-plane elif mpf_le(b2, fzero): minre = mpc_loggamma((a1,b1), wp, round_floor) maxre = mpc_loggamma((a2,b2), wp, round_ceiling) minim = mpc_loggamma((a2,b1), wp, round_floor) maxim = mpc_loggamma((a1,b2), wp, round_ceiling) # crosses real axis else: maxre = mpc_loggamma((a2,fzero), wp, round_ceiling) # stretches more into the lower half-plane if mpf_gt(mpf_neg(b1), b2): minre = mpc_loggamma((a1,b1), wp, round_ceiling) else: minre = mpc_loggamma((a1,b2), wp, round_ceiling) minim = mpc_loggamma((a2,b1), wp, round_floor) maxim = mpc_loggamma((a2,b2), wp, round_floor) w = (minre[0], maxre[0]), (minim[1], maxim[1]) if type == 3: return mpi_pos(w[0], prec), mpi_pos(w[1], prec) if type == 2: w = mpci_neg(w) return mpci_exp(w, prec) def mpi_loggamma(z, prec): return mpi_gamma(z, prec, type=3) def mpci_loggamma(z, prec): return mpci_gamma(z, prec, type=3) def mpi_rgamma(z, prec): return mpi_gamma(z, prec, type=2) def mpci_rgamma(z, prec): return mpci_gamma(z, prec, type=2) def mpi_factorial(z, prec): return mpi_gamma(z, prec, type=1) def mpci_factorial(z, prec): return mpci_gamma(z, prec, type=1) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/libmpc.py0000644000175000017500000006433312014170666025321 0ustar georgeskgeorgesk""" Low-level functions for complex arithmetic. """ import sys from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, BACKEND from .libmpf import (\ round_floor, round_ceiling, round_down, round_up, round_nearest, round_fast, bitcount, bctable, normalize, normalize1, reciprocal_rnd, rshift, lshift, giant_steps, negative_rnd, to_str, to_fixed, from_man_exp, from_float, to_float, from_int, to_int, fzero, fone, ftwo, fhalf, finf, fninf, fnan, fnone, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_div, mpf_mul_int, mpf_shift, mpf_sqrt, mpf_hypot, mpf_rdiv_int, mpf_floor, mpf_ceil, mpf_nint, mpf_frac, mpf_sign, mpf_hash, ComplexResult ) from .libelefun import (\ mpf_pi, mpf_exp, mpf_log, mpf_cos_sin, mpf_cosh_sinh, mpf_tan, mpf_pow_int, mpf_log_hypot, mpf_cos_sin_pi, mpf_phi, mpf_cos, mpf_sin, mpf_cos_pi, mpf_sin_pi, mpf_atan, mpf_atan2, mpf_cosh, mpf_sinh, mpf_tanh, mpf_asin, mpf_acos, mpf_acosh, mpf_nthroot, mpf_fibonacci ) # An mpc value is a (real, imag) tuple mpc_one = fone, fzero mpc_zero = fzero, fzero mpc_two = ftwo, fzero mpc_half = (fhalf, fzero) _infs = (finf, fninf) _infs_nan = (finf, fninf, fnan) def mpc_is_inf(z): """Check if either real or imaginary part is infinite""" re, im = z if re in _infs: return True if im in _infs: return True return False def mpc_is_infnan(z): """Check if either real or imaginary part is infinite or nan""" re, im = z if re in _infs_nan: return True if im in _infs_nan: return True return False def mpc_to_str(z, dps, **kwargs): re, im = z rs = to_str(re, dps) if im[0]: return rs + " - " + to_str(mpf_neg(im), dps, **kwargs) + "j" else: return rs + " + " + to_str(im, dps, **kwargs) + "j" def mpc_to_complex(z, strict=False): re, im = z return complex(to_float(re, strict), to_float(im, strict)) def mpc_hash(z): if sys.version >= "3.2": re, im = z h = mpf_hash(re) + sys.hash_info.imag * mpf_hash(im) # Need to reduce either module 2^32 or 2^64 h = h % (2**sys.hash_info.width) return int(h) else: try: return hash(mpc_to_complex(z, strict=True)) except OverflowError: return hash(z) def mpc_conjugate(z, prec, rnd=round_fast): re, im = z return re, mpf_neg(im, prec, rnd) def mpc_is_nonzero(z): return z != mpc_zero def mpc_add(z, w, prec, rnd=round_fast): a, b = z c, d = w return mpf_add(a, c, prec, rnd), mpf_add(b, d, prec, rnd) def mpc_add_mpf(z, x, prec, rnd=round_fast): a, b = z return mpf_add(a, x, prec, rnd), b def mpc_sub(z, w, prec=0, rnd=round_fast): a, b = z c, d = w return mpf_sub(a, c, prec, rnd), mpf_sub(b, d, prec, rnd) def mpc_sub_mpf(z, p, prec=0, rnd=round_fast): a, b = z return mpf_sub(a, p, prec, rnd), b def mpc_pos(z, prec, rnd=round_fast): a, b = z return mpf_pos(a, prec, rnd), mpf_pos(b, prec, rnd) def mpc_neg(z, prec=None, rnd=round_fast): a, b = z return mpf_neg(a, prec, rnd), mpf_neg(b, prec, rnd) def mpc_shift(z, n): a, b = z return mpf_shift(a, n), mpf_shift(b, n) def mpc_abs(z, prec, rnd=round_fast): """Absolute value of a complex number, |a+bi|. Returns an mpf value.""" a, b = z return mpf_hypot(a, b, prec, rnd) def mpc_arg(z, prec, rnd=round_fast): """Argument of a complex number. Returns an mpf value.""" a, b = z return mpf_atan2(b, a, prec, rnd) def mpc_floor(z, prec, rnd=round_fast): a, b = z return mpf_floor(a, prec, rnd), mpf_floor(b, prec, rnd) def mpc_ceil(z, prec, rnd=round_fast): a, b = z return mpf_ceil(a, prec, rnd), mpf_ceil(b, prec, rnd) def mpc_nint(z, prec, rnd=round_fast): a, b = z return mpf_nint(a, prec, rnd), mpf_nint(b, prec, rnd) def mpc_frac(z, prec, rnd=round_fast): a, b = z return mpf_frac(a, prec, rnd), mpf_frac(b, prec, rnd) def mpc_mul(z, w, prec, rnd=round_fast): """ Complex multiplication. Returns the real and imaginary part of (a+bi)*(c+di), rounded to the specified precision. The rounding mode applies to the real and imaginary parts separately. """ a, b = z c, d = w p = mpf_mul(a, c) q = mpf_mul(b, d) r = mpf_mul(a, d) s = mpf_mul(b, c) re = mpf_sub(p, q, prec, rnd) im = mpf_add(r, s, prec, rnd) return re, im def mpc_square(z, prec, rnd=round_fast): # (a+b*I)**2 == a**2 - b**2 + 2*I*a*b a, b = z p = mpf_mul(a,a) q = mpf_mul(b,b) r = mpf_mul(a,b, prec, rnd) re = mpf_sub(p, q, prec, rnd) im = mpf_shift(r, 1) return re, im def mpc_mul_mpf(z, p, prec, rnd=round_fast): a, b = z re = mpf_mul(a, p, prec, rnd) im = mpf_mul(b, p, prec, rnd) return re, im def mpc_mul_imag_mpf(z, x, prec, rnd=round_fast): """ Multiply the mpc value z by I*x where x is an mpf value. """ a, b = z re = mpf_neg(mpf_mul(b, x, prec, rnd)) im = mpf_mul(a, x, prec, rnd) return re, im def mpc_mul_int(z, n, prec, rnd=round_fast): a, b = z re = mpf_mul_int(a, n, prec, rnd) im = mpf_mul_int(b, n, prec, rnd) return re, im def mpc_div(z, w, prec, rnd=round_fast): a, b = z c, d = w wp = prec + 10 # mag = c*c + d*d mag = mpf_add(mpf_mul(c, c), mpf_mul(d, d), wp) # (a*c+b*d)/mag, (b*c-a*d)/mag t = mpf_add(mpf_mul(a,c), mpf_mul(b,d), wp) u = mpf_sub(mpf_mul(b,c), mpf_mul(a,d), wp) return mpf_div(t,mag,prec,rnd), mpf_div(u,mag,prec,rnd) def mpc_div_mpf(z, p, prec, rnd=round_fast): """Calculate z/p where p is real""" a, b = z re = mpf_div(a, p, prec, rnd) im = mpf_div(b, p, prec, rnd) return re, im def mpc_reciprocal(z, prec, rnd=round_fast): """Calculate 1/z efficiently""" a, b = z m = mpf_add(mpf_mul(a,a),mpf_mul(b,b),prec+10) re = mpf_div(a, m, prec, rnd) im = mpf_neg(mpf_div(b, m, prec, rnd)) return re, im def mpc_mpf_div(p, z, prec, rnd=round_fast): """Calculate p/z where p is real efficiently""" a, b = z m = mpf_add(mpf_mul(a,a),mpf_mul(b,b), prec+10) re = mpf_div(mpf_mul(a,p), m, prec, rnd) im = mpf_div(mpf_neg(mpf_mul(b,p)), m, prec, rnd) return re, im def complex_int_pow(a, b, n): """Complex integer power: computes (a+b*I)**n exactly for nonnegative n (a and b must be Python ints).""" wre = 1 wim = 0 while n: if n & 1: wre, wim = wre*a - wim*b, wim*a + wre*b n -= 1 a, b = a*a - b*b, 2*a*b n //= 2 return wre, wim def mpc_pow(z, w, prec, rnd=round_fast): if w[1] == fzero: return mpc_pow_mpf(z, w[0], prec, rnd) return mpc_exp(mpc_mul(mpc_log(z, prec+10), w, prec+10), prec, rnd) def mpc_pow_mpf(z, p, prec, rnd=round_fast): psign, pman, pexp, pbc = p if pexp >= 0: return mpc_pow_int(z, (-1)**psign * (pman< 0: aman <<= de aexp = bexp else: bman <<= (-de) bexp = aexp re, im = complex_int_pow(aman, bman, n) re = from_man_exp(re, int(n*aexp), prec, rnd) im = from_man_exp(im, int(n*bexp), prec, rnd) return re, im return mpc_exp(mpc_mul_int(mpc_log(z, prec+10), n, prec+10), prec, rnd) def mpc_sqrt(z, prec, rnd=round_fast): """Complex square root (principal branch). We have sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i where r = abs(a+bi), when a+bi is not a negative real number.""" a, b = z if b == fzero: if a == fzero: return (a, b) # When a+bi is a negative real number, we get a real sqrt times i if a[0]: im = mpf_sqrt(mpf_neg(a), prec, rnd) return (fzero, im) else: re = mpf_sqrt(a, prec, rnd) return (re, fzero) wp = prec+20 if not a[0]: # case a positive t = mpf_add(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) + a u = mpf_shift(t, -1) # u = t/2 re = mpf_sqrt(u, prec, rnd) # re = sqrt(u) v = mpf_shift(t, 1) # v = 2*t w = mpf_sqrt(v, wp) # w = sqrt(v) im = mpf_div(b, w, prec, rnd) # im = b / w else: # case a negative t = mpf_sub(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) - a u = mpf_shift(t, -1) # u = t/2 im = mpf_sqrt(u, prec, rnd) # im = sqrt(u) v = mpf_shift(t, 1) # v = 2*t w = mpf_sqrt(v, wp) # w = sqrt(v) re = mpf_div(b, w, prec, rnd) # re = b/w if b[0]: re = mpf_neg(re) im = mpf_neg(im) return re, im def mpc_nthroot_fixed(a, b, n, prec): # a, b signed integers at fixed precision prec start = 50 a1 = int(rshift(a, prec - n*start)) b1 = int(rshift(b, prec - n*start)) try: r = (a1 + 1j * b1)**(1.0/n) re = r.real im = r.imag re = MPZ(int(re)) im = MPZ(int(im)) except OverflowError: a1 = from_int(a1, start) b1 = from_int(b1, start) fn = from_int(n) nth = mpf_rdiv_int(1, fn, start) re, im = mpc_pow((a1, b1), (nth, fzero), start) re = to_int(re) im = to_int(im) extra = 10 prevp = start extra1 = n for p in giant_steps(start, prec+extra): # this is slow for large n, unlike int_pow_fixed re2, im2 = complex_int_pow(re, im, n-1) re2 = rshift(re2, (n-1)*prevp - p - extra1) im2 = rshift(im2, (n-1)*prevp - p - extra1) r4 = (re2*re2 + im2*im2) >> (p + extra1) ap = rshift(a, prec - p) bp = rshift(b, prec - p) rec = (ap * re2 + bp * im2) >> p imc = (-ap * im2 + bp * re2) >> p reb = (rec << p) // r4 imb = (imc << p) // r4 re = (reb + (n-1)*lshift(re, p-prevp))//n im = (imb + (n-1)*lshift(im, p-prevp))//n prevp = p return re, im def mpc_nthroot(z, n, prec, rnd=round_fast): """ Complex n-th root. Use Newton method as in the real case when it is faster, otherwise use z**(1/n) """ a, b = z if a[0] == 0 and b == fzero: re = mpf_nthroot(a, n, prec, rnd) return (re, fzero) if n < 2: if n == 0: return mpc_one if n == 1: return mpc_pos((a, b), prec, rnd) if n == -1: return mpc_div(mpc_one, (a, b), prec, rnd) inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd]) return mpc_div(mpc_one, inverse, prec, rnd) if n <= 20: prec2 = int(1.2 * (prec + 10)) asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b pf = mpc_abs((a,b), prec) if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec: af = to_fixed(a, prec2) bf = to_fixed(b, prec2) re, im = mpc_nthroot_fixed(af, bf, n, prec2) extra = 10 re = from_man_exp(re, -prec2-extra, prec2, rnd) im = from_man_exp(im, -prec2-extra, prec2, rnd) return re, im fn = from_int(n) prec2 = prec+10 + 10 nth = mpf_rdiv_int(1, fn, prec2) re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im def mpc_cbrt(z, prec, rnd=round_fast): """ Complex cubic root. """ return mpc_nthroot(z, 3, prec, rnd) def mpc_exp(z, prec, rnd=round_fast): """ Complex exponential function. We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) for the computation. This formula is very nice because it is pefectly stable; since we just do real multiplications, the only numerical errors that can creep in are single-ulp rounding errors. The formula is efficient since mpmath's real exp is quite fast and since we can compute cos and sin simultaneously. It is no problem if a and b are large; if the implementations of exp/cos/sin are accurate and efficient for all real numbers, then so is this function for all complex numbers. """ a, b = z if a == fzero: return mpf_cos_sin(b, prec, rnd) if b == fzero: return mpf_exp(a, prec, rnd), fzero mag = mpf_exp(a, prec+4, rnd) c, s = mpf_cos_sin(b, prec+4, rnd) re = mpf_mul(mag, c, prec, rnd) im = mpf_mul(mag, s, prec, rnd) return re, im def mpc_log(z, prec, rnd=round_fast): re = mpf_log_hypot(z[0], z[1], prec, rnd) im = mpc_arg(z, prec, rnd) return re, im def mpc_cos(z, prec, rnd=round_fast): """Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) - sin(a)*sinh(b)*i. The same comments apply as for the complex exp: only real multiplications are pewrormed, so no cancellation errors are possible. The formula is also efficient since we can compute both pairs (cos, sin) and (cosh, sinh) in single stwps.""" a, b = z if b == fzero: return mpf_cos(a, prec, rnd), fzero if a == fzero: return mpf_cosh(b, prec, rnd), fzero wp = prec + 6 c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(c, ch, prec, rnd) im = mpf_mul(s, sh, prec, rnd) return re, mpf_neg(im) def mpc_sin(z, prec, rnd=round_fast): """Complex sine. We have sin(a+bi) = sin(a)*cosh(b) + cos(a)*sinh(b)*i. See the docstring for mpc_cos for additional comments.""" a, b = z if b == fzero: return mpf_sin(a, prec, rnd), fzero if a == fzero: return fzero, mpf_sinh(b, prec, rnd) wp = prec + 6 c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(s, ch, prec, rnd) im = mpf_mul(c, sh, prec, rnd) return re, im def mpc_tan(z, prec, rnd=round_fast): """Complex tangent. Computed as tan(a+bi) = sin(2a)/M + sinh(2b)/M*i where M = cos(2a) + cosh(2b).""" a, b = z asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b if b == fzero: return mpf_tan(a, prec, rnd), fzero if a == fzero: return fzero, mpf_tanh(b, prec, rnd) wp = prec + 15 a = mpf_shift(a, 1) b = mpf_shift(b, 1) c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) # TODO: handle cancellation when c ~= -1 and ch ~= 1 mag = mpf_add(c, ch, wp) re = mpf_div(s, mag, prec, rnd) im = mpf_div(sh, mag, prec, rnd) return re, im def mpc_cos_pi(z, prec, rnd=round_fast): a, b = z if b == fzero: return mpf_cos_pi(a, prec, rnd), fzero b = mpf_mul(b, mpf_pi(prec+5), prec+5) if a == fzero: return mpf_cosh(b, prec, rnd), fzero wp = prec + 6 c, s = mpf_cos_sin_pi(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(c, ch, prec, rnd) im = mpf_mul(s, sh, prec, rnd) return re, mpf_neg(im) def mpc_sin_pi(z, prec, rnd=round_fast): a, b = z if b == fzero: return mpf_sin_pi(a, prec, rnd), fzero b = mpf_mul(b, mpf_pi(prec+5), prec+5) if a == fzero: return fzero, mpf_sinh(b, prec, rnd) wp = prec + 6 c, s = mpf_cos_sin_pi(a, wp) ch, sh = mpf_cosh_sinh(b, wp) re = mpf_mul(s, ch, prec, rnd) im = mpf_mul(c, sh, prec, rnd) return re, im def mpc_cos_sin(z, prec, rnd=round_fast): a, b = z if a == fzero: ch, sh = mpf_cosh_sinh(b, prec, rnd) return (ch, fzero), (fzero, sh) if b == fzero: c, s = mpf_cos_sin(a, prec, rnd) return (c, fzero), (s, fzero) wp = prec + 6 c, s = mpf_cos_sin(a, wp) ch, sh = mpf_cosh_sinh(b, wp) cre = mpf_mul(c, ch, prec, rnd) cim = mpf_mul(s, sh, prec, rnd) sre = mpf_mul(s, ch, prec, rnd) sim = mpf_mul(c, sh, prec, rnd) return (cre, mpf_neg(cim)), (sre, sim) def mpc_cos_sin_pi(z, prec, rnd=round_fast): a, b = z if b == fzero: c, s = mpf_cos_sin_pi(a, prec, rnd) return (c, fzero), (s, fzero) b = mpf_mul(b, mpf_pi(prec+5), prec+5) if a == fzero: ch, sh = mpf_cosh_sinh(b, prec, rnd) return (ch, fzero), (fzero, sh) wp = prec + 6 c, s = mpf_cos_sin_pi(a, wp) ch, sh = mpf_cosh_sinh(b, wp) cre = mpf_mul(c, ch, prec, rnd) cim = mpf_mul(s, sh, prec, rnd) sre = mpf_mul(s, ch, prec, rnd) sim = mpf_mul(c, sh, prec, rnd) return (cre, mpf_neg(cim)), (sre, sim) def mpc_cosh(z, prec, rnd=round_fast): """Complex hyperbolic cosine. Computed as cosh(z) = cos(z*i).""" a, b = z return mpc_cos((b, mpf_neg(a)), prec, rnd) def mpc_sinh(z, prec, rnd=round_fast): """Complex hyperbolic sine. Computed as sinh(z) = -i*sin(z*i).""" a, b = z b, a = mpc_sin((b, a), prec, rnd) return a, b def mpc_tanh(z, prec, rnd=round_fast): """Complex hyperbolic tangent. Computed as tanh(z) = -i*tan(z*i).""" a, b = z b, a = mpc_tan((b, a), prec, rnd) return a, b # TODO: avoid loss of accuracy def mpc_atan(z, prec, rnd=round_fast): a, b = z # atan(z) = (I/2)*(log(1-I*z) - log(1+I*z)) # x = 1-I*z = 1 + b - I*a # y = 1+I*z = 1 - b + I*a wp = prec + 15 x = mpf_add(fone, b, wp), mpf_neg(a) y = mpf_sub(fone, b, wp), a l1 = mpc_log(x, wp) l2 = mpc_log(y, wp) a, b = mpc_sub(l1, l2, prec, rnd) # (I/2) * (a+b*I) = (-b/2 + a/2*I) v = mpf_neg(mpf_shift(b,-1)), mpf_shift(a,-1) # Subtraction at infinity gives correct real part but # wrong imaginary part (should be zero) if v[1] == fnan and mpc_is_inf(z): v = (v[0], fzero) return v beta_crossover = from_float(0.6417) alpha_crossover = from_float(1.5) def acos_asin(z, prec, rnd, n): """ complex acos for n = 0, asin for n = 1 The algorithm is described in T.E. Hull, T.F. Fairgrieve and P.T.P. Tang 'Implementing the Complex Arcsine and Arcosine Functions using Exception Handling', ACM Trans. on Math. Software Vol. 23 (1997), p299 The complex acos and asin can be defined as acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1)) asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1)) where z = a + I*b alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2) These expressions are rewritten in different ways in different regions, delimited by two crossovers alpha_crossover and beta_crossover, and by abs(a) <= 1, in order to improve the numerical accuracy. """ a, b = z wp = prec + 10 # special cases with real argument if b == fzero: am = mpf_sub(fone, mpf_abs(a), wp) # case abs(a) <= 1 if not am[0]: if n == 0: return mpf_acos(a, prec, rnd), fzero else: return mpf_asin(a, prec, rnd), fzero # cases abs(a) > 1 else: # case a < -1 if a[0]: pi = mpf_pi(prec, rnd) c = mpf_acosh(mpf_neg(a), prec, rnd) if n == 0: return pi, mpf_neg(c) else: return mpf_neg(mpf_shift(pi, -1)), c # case a > 1 else: c = mpf_acosh(a, prec, rnd) if n == 0: return fzero, c else: pi = mpf_pi(prec, rnd) return mpf_shift(pi, -1), mpf_neg(c) asign = bsign = 0 if a[0]: a = mpf_neg(a) asign = 1 if b[0]: b = mpf_neg(b) bsign = 1 am = mpf_sub(fone, a, wp) ap = mpf_add(fone, a, wp) r = mpf_hypot(ap, b, wp) s = mpf_hypot(am, b, wp) alpha = mpf_shift(mpf_add(r, s, wp), -1) beta = mpf_div(a, alpha, wp) b2 = mpf_mul(b,b, wp) # case beta <= beta_crossover if not mpf_sub(beta_crossover, beta, wp)[0]: if n == 0: re = mpf_acos(beta, wp) else: re = mpf_asin(beta, wp) else: # to compute the real part in this region use the identity # asin(beta) = atan(beta/sqrt(1-beta**2)) # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a) # alpha + a is numerically accurate; alpha - a can have # cancellations leading to numerical inaccuracies, so rewrite # it in differente ways according to the region Ax = mpf_add(alpha, a, wp) # case a <= 1 if not am[0]: # c = b*b/(r + (a+1)); d = (s + (1-a)) # alpha - a = (1/2)*(c + d) # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a) # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d))) c = mpf_div(b2, mpf_add(r, ap, wp), wp) d = mpf_add(s, am, wp) re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1) if n == 0: re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp) else: re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp) else: # c = Ax/(r + (a+1)); d = Ax/(s - (1-a)) # alpha - a = (1/2)*(c + d) # case n = 0: re = atan(b*sqrt(c + d)/2/a) # case n = 1: re = atan(a/(b*sqrt(c + d)/2) c = mpf_div(Ax, mpf_add(r, ap, wp), wp) d = mpf_div(Ax, mpf_sub(s, am, wp), wp) re = mpf_shift(mpf_add(c, d, wp), -1) re = mpf_mul(b, mpf_sqrt(re, wp), wp) if n == 0: re = mpf_atan(mpf_div(re, a, wp), wp) else: re = mpf_atan(mpf_div(a, re, wp), wp) # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover # replace it with 1 + Am1 + sqrt(Am1*(alpha+1))) # where Am1 = alpha -1 # if alpha <= alpha_crossover: if not mpf_sub(alpha_crossover, alpha, wp)[0]: c1 = mpf_div(b2, mpf_add(r, ap, wp), wp) # case a < 1 if mpf_neg(am)[0]: # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a)) c2 = mpf_add(s, am, wp) c2 = mpf_div(b2, c2, wp) Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) else: # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a))) c2 = mpf_sub(s, am, wp) Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) # im = log(1 + Am1 + sqrt(Am1*(alpha+1))) im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp) im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp) else: # im = log(alpha + sqrt(alpha*alpha - 1)) im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp) im = mpf_log(mpf_add(alpha, im, wp), wp) if asign: if n == 0: re = mpf_sub(mpf_pi(wp), re, wp) else: re = mpf_neg(re) if not bsign and n == 0: im = mpf_neg(im) if bsign and n == 1: im = mpf_neg(im) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im def mpc_acos(z, prec, rnd=round_fast): return acos_asin(z, prec, rnd, 0) def mpc_asin(z, prec, rnd=round_fast): return acos_asin(z, prec, rnd, 1) def mpc_asinh(z, prec, rnd=round_fast): # asinh(z) = I * asin(-I z) a, b = z a, b = mpc_asin((b, mpf_neg(a)), prec, rnd) return mpf_neg(b), a def mpc_acosh(z, prec, rnd=round_fast): # acosh(z) = -I * acos(z) for Im(acos(z)) <= 0 # +I * acos(z) otherwise a, b = mpc_acos(z, prec, rnd) if b[0] or b == fzero: return mpf_neg(b), a else: return b, mpf_neg(a) def mpc_atanh(z, prec, rnd=round_fast): # atanh(z) = (log(1+z)-log(1-z))/2 wp = prec + 15 a = mpc_add(z, mpc_one, wp) b = mpc_sub(mpc_one, z, wp) a = mpc_log(a, wp) b = mpc_log(b, wp) v = mpc_shift(mpc_sub(a, b, wp), -1) # Subtraction at infinity gives correct imaginary part but # wrong real part (should be zero) if v[0] == fnan and mpc_is_inf(z): v = (fzero, v[1]) return v def mpc_fibonacci(z, prec, rnd=round_fast): re, im = z if im == fzero: return (mpf_fibonacci(re, prec, rnd), fzero) size = max(abs(re[2]+re[3]), abs(re[2]+re[3])) wp = prec + size + 20 a = mpf_phi(wp) b = mpf_add(mpf_shift(a, 1), fnone, wp) u = mpc_pow((a, fzero), z, wp) v = mpc_cos_pi(z, wp) v = mpc_div(v, u, wp) u = mpc_sub(u, v, wp) u = mpc_div_mpf(u, b, prec, rnd) return u def mpf_expj(x, prec, rnd='f'): raise ComplexResult def mpc_expj(z, prec, rnd='f'): re, im = z if im == fzero: return mpf_cos_sin(re, prec, rnd) if re == fzero: return mpf_exp(mpf_neg(im), prec, rnd), fzero ey = mpf_exp(mpf_neg(im), prec+10) c, s = mpf_cos_sin(re, prec+10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im def mpf_expjpi(x, prec, rnd='f'): raise ComplexResult def mpc_expjpi(z, prec, rnd='f'): re, im = z if im == fzero: return mpf_cos_sin_pi(re, prec, rnd) sign, man, exp, bc = im wp = prec+10 if man: wp += max(0, exp+bc) im = mpf_neg(mpf_mul(mpf_pi(wp), im, wp)) if re == fzero: return mpf_exp(im, prec, rnd), fzero ey = mpf_exp(im, prec+10) c, s = mpf_cos_sin_pi(re, prec+10) re = mpf_mul(ey, c, prec, rnd) im = mpf_mul(ey, s, prec, rnd) return re, im if BACKEND == 'sage': try: import sage.libs.mpmath.ext_libmp as _lbmp mpc_exp = _lbmp.mpc_exp mpc_sqrt = _lbmp.mpc_sqrt except (ImportError, AttributeError): print("Warning: Sage imports in libmpc failed") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/libelefun.py0000644000175000017500000012547012014170666026020 0ustar georgeskgeorgesk""" This module implements computation of elementary transcendental functions (powers, logarithms, trigonometric and hyperbolic functions, inverse trigonometric and hyperbolic) for real floating-point numbers. For complex and interval implementations of the same functions, see libmpc and libmpi. """ import math from bisect import bisect from .backend import xrange from .backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, BACKEND from .libmpf import ( round_floor, round_ceiling, round_down, round_up, round_nearest, round_fast, ComplexResult, bitcount, bctable, lshift, rshift, giant_steps, sqrt_fixed, from_int, to_int, from_man_exp, to_fixed, to_float, from_float, from_rational, normalize, fzero, fone, fnone, fhalf, finf, fninf, fnan, mpf_cmp, mpf_sign, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_div, mpf_shift, mpf_rdiv_int, mpf_pow_int, mpf_sqrt, reciprocal_rnd, negative_rnd, mpf_perturb, isqrt_fast ) from .libintmath import ifib #------------------------------------------------------------------------------- # Tuning parameters #------------------------------------------------------------------------------- # Cutoff for computing exp from cosh+sinh. This reduces the # number of terms by half, but also requires a square root which # is expensive with the pure-Python square root code. if BACKEND == 'python': EXP_COSH_CUTOFF = 600 else: EXP_COSH_CUTOFF = 400 # Cutoff for using more than 2 series EXP_SERIES_U_CUTOFF = 1500 # Also basically determined by sqrt if BACKEND == 'python': COS_SIN_CACHE_PREC = 400 else: COS_SIN_CACHE_PREC = 200 COS_SIN_CACHE_STEP = 8 cos_sin_cache = {} # Number of integer logarithms to cache (for zeta sums) MAX_LOG_INT_CACHE = 2000 log_int_cache = {} LOG_TAYLOR_PREC = 2500 # Use Taylor series with caching up to this prec LOG_TAYLOR_SHIFT = 9 # Cache log values in steps of size 2^-N log_taylor_cache = {} # prec/size ratio of x for fastest convergence in AGM formula LOG_AGM_MAG_PREC_RATIO = 20 ATAN_TAYLOR_PREC = 3000 # Same as for log ATAN_TAYLOR_SHIFT = 7 # steps of size 2^-N atan_taylor_cache = {} # ~= next power of two + 20 cache_prec_steps = [22,22] for k in xrange(1, bitcount(LOG_TAYLOR_PREC)+1): cache_prec_steps += [min(2**k,LOG_TAYLOR_PREC)+20] * 2**(k-1) #----------------------------------------------------------------------------# # # # Elementary mathematical constants # # # #----------------------------------------------------------------------------# def constant_memo(f): """ Decorator for caching computed values of mathematical constants. This decorator should be applied to a function taking a single argument prec as input and returning a fixed-point value with the given precision. """ f.memo_prec = -1 f.memo_val = None def g(prec, **kwargs): memo_prec = f.memo_prec if prec <= memo_prec: return f.memo_val >> (memo_prec-prec) newprec = int(prec*1.05+10) f.memo_val = f(newprec, **kwargs) f.memo_prec = newprec return f.memo_val >> (newprec-prec) g.__name__ = f.__name__ g.__doc__ = f.__doc__ return g def def_mpf_constant(fixed): """ Create a function that computes the mpf value for a mathematical constant, given a function that computes the fixed-point value. Assumptions: the constant is positive and has magnitude ~= 1; the fixed-point function rounds to floor. """ def f(prec, rnd=round_fast): wp = prec + 20 v = fixed(wp) if rnd in (round_up, round_ceiling): v += 1 return normalize(0, v, -wp, bitcount(v), prec, rnd) f.__doc__ = fixed.__doc__ return f def bsp_acot(q, a, b, hyperbolic): if b - a == 1: a1 = MPZ(2*a + 3) if hyperbolic or a&1: return MPZ_ONE, a1 * q**2, a1 else: return -MPZ_ONE, a1 * q**2, a1 m = (a+b)//2 p1, q1, r1 = bsp_acot(q, a, m, hyperbolic) p2, q2, r2 = bsp_acot(q, m, b, hyperbolic) return q2*p1 + r1*p2, q1*q2, r1*r2 # the acoth(x) series converges like the geometric series for x^2 # N = ceil(p*log(2)/(2*log(x))) def acot_fixed(a, prec, hyperbolic): """ Compute acot(a) or acoth(a) for an integer a with binary splitting; see http://numbers.computation.free.fr/Constants/Algorithms/splitting.html """ N = int(0.35 * prec/math.log(a) + 20) p, q, r = bsp_acot(a, 0,N, hyperbolic) return ((p+q)<> extraprec) # Logarithms of integers are needed for various computations involving # logarithms, powers, radix conversion, etc @constant_memo def ln2_fixed(prec): """ Computes ln(2). This is done with a hyperbolic Machin-type formula, with binary splitting at high precision. """ return machin([(18, 26), (-2, 4801), (8, 8749)], prec, True) @constant_memo def ln10_fixed(prec): """ Computes ln(10). This is done with a hyperbolic Machin-type formula. """ return machin([(46, 31), (34, 49), (20, 161)], prec, True) """ For computation of pi, we use the Chudnovsky series: oo ___ k 1 \ (-1) (6 k)! (A + B k) ----- = ) ----------------------- 12 pi /___ 3 3k+3/2 (3 k)! (k!) C k = 0 where A, B, and C are certain integer constants. This series adds roughly 14 digits per term. Note that C^(3/2) can be extracted so that the series contains only rational terms. This makes binary splitting very efficient. The recurrence formulas for the binary splitting were taken from ftp://ftp.gmplib.org/pub/src/gmp-chudnovsky.c Previously, Machin's formula was used at low precision and the AGM iteration was used at high precision. However, the Chudnovsky series is essentially as fast as the Machin formula at low precision and in practice about 3x faster than the AGM at high precision (despite theoretically having a worse asymptotic complexity), so there is no reason not to use it in all cases. """ # Constants in Chudnovsky's series CHUD_A = MPZ(13591409) CHUD_B = MPZ(545140134) CHUD_C = MPZ(640320) CHUD_D = MPZ(12) def bs_chudnovsky(a, b, level, verbose): """ Computes the sum from a to b of the series in the Chudnovsky formula. Returns g, p, q where p/q is the sum as an exact fraction and g is a temporary value used to save work for recursive calls. """ if b-a == 1: g = MPZ((6*b-5)*(2*b-1)*(6*b-1)) p = b**3 * CHUD_C**3 // 24 q = (-1)**b * g * (CHUD_A+CHUD_B*b) else: if verbose and level < 4: print(" binary splitting", a, b) mid = (a+b)//2 g1, p1, q1 = bs_chudnovsky(a, mid, level+1, verbose) g2, p2, q2 = bs_chudnovsky(mid, b, level+1, verbose) p = p1*p2 g = g1*g2 q = q1*p2 + q2*g1 return g, p, q @constant_memo def pi_fixed(prec, verbose=False, verbose_base=None): """ Compute floor(pi * 2**prec) as a big integer. This is done using Chudnovsky's series (see comments in libelefun.py for details). """ # The Chudnovsky series gives 14.18 digits per term N = int(prec/3.3219280948/14.181647462 + 2) if verbose: print("binary splitting with N =", N) g, p, q = bs_chudnovsky(0, N, 0, verbose) sqrtC = isqrt_fast(CHUD_C<<(2*prec)) v = p*CHUD_C*sqrtC//((q+CHUD_A*p)*CHUD_D) return v def degree_fixed(prec): return pi_fixed(prec)//180 def bspe(a, b): """ Sum series for exp(1)-1 between a, b, returning the result as an exact fraction (p, q). """ if b-a == 1: return MPZ_ONE, MPZ(b) m = (a+b)//2 p1, q1 = bspe(a, m) p2, q2 = bspe(m, b) return p1*q2+p2, q1*q2 @constant_memo def e_fixed(prec): """ Computes exp(1). This is done using the ordinary Taylor series for exp, with binary splitting. For a description of the algorithm, see: http://numbers.computation.free.fr/Constants/ Algorithms/splitting.html """ # Slight overestimate of N needed for 1/N! < 2**(-prec) # This could be tightened for large N. N = int(1.1*prec/math.log(prec) + 20) p, q = bspe(0,N) return ((p+q)<> 11 mpf_phi = def_mpf_constant(phi_fixed) mpf_pi = def_mpf_constant(pi_fixed) mpf_e = def_mpf_constant(e_fixed) mpf_degree = def_mpf_constant(degree_fixed) mpf_ln2 = def_mpf_constant(ln2_fixed) mpf_ln10 = def_mpf_constant(ln10_fixed) @constant_memo def ln_sqrt2pi_fixed(prec): wp = prec + 10 # ln(sqrt(2*pi)) = ln(2*pi)/2 return to_fixed(mpf_log(mpf_shift(mpf_pi(wp), 1), wp), prec-1) @constant_memo def sqrtpi_fixed(prec): return sqrt_fixed(pi_fixed(prec), prec) mpf_sqrtpi = def_mpf_constant(sqrtpi_fixed) mpf_ln_sqrt2pi = def_mpf_constant(ln_sqrt2pi_fixed) #----------------------------------------------------------------------------# # # # Powers # # # #----------------------------------------------------------------------------# def mpf_pow(s, t, prec, rnd=round_fast): """ Compute s**t. Raises ComplexResult if s is negative and t is fractional. """ ssign, sman, sexp, sbc = s tsign, tman, texp, tbc = t if ssign and texp < 0: raise ComplexResult("negative number raised to a fractional power") if texp >= 0: return mpf_pow_int(s, (-1)**tsign * (tman<> pbc)] if pbc > workprec: pm = pm >> (pbc-workprec) pe += pbc - workprec pbc = workprec n -= 1 if not n: break y = y*y exp = exp+exp bc = bc + bc - 2 bc = bc + bctable[int(y >> bc)] if bc > workprec: y = y >> (bc-workprec) exp += bc - workprec bc = workprec n = n // 2 return pm, pe # froot(s, n, prec, rnd) computes the real n-th root of a # positive mpf tuple s. # To compute the root we start from a 50-bit estimate for r # generated with ordinary floating-point arithmetic, and then refine # the value to full accuracy using the iteration # 1 / y \ # r = --- | (n-1) * r + ---------- | # n+1 n \ n r_n**(n-1) / # which is simply Newton's method applied to the equation r**n = y. # With giant_steps(start, prec+extra) = [p0,...,pm, prec+extra] # and y = man * 2**-shift one has # (man * 2**exp)**(1/n) = # y**(1/n) * 2**(start-prec/n) * 2**(p0-start) * ... * 2**(prec+extra-pm) * # 2**((exp+shift-(n-1)*prec)/n -extra)) # The last factor is accounted for in the last line of froot. def nthroot_fixed(y, n, prec, exp1): start = 50 try: y1 = rshift(y, prec - n*start) r = MPZ(int(y1**(1.0/n))) except OverflowError: y1 = from_int(y1, start) fn = from_int(n) fn = mpf_rdiv_int(1, fn, start) r = mpf_pow(y1, fn, start) r = to_int(r) extra = 10 extra1 = n prevp = start for p in giant_steps(start, prec+extra): pm, pe = int_pow_fixed(r, n-1, prevp) r2 = rshift(pm, (n-1)*prevp - p - pe - extra1) B = lshift(y, 2*p-prec+extra1)//r2 r = (B + (n-1) * lshift(r, p-prevp))//n prevp = p return r def mpf_nthroot(s, n, prec, rnd=round_fast): """nth-root of a positive number Use the Newton method when faster, otherwise use x**(1/n) """ sign, man, exp, bc = s if sign: raise ComplexResult("nth root of a negative number") if not man: if s == fnan: return fnan if s == fzero: if n > 0: return fzero if n == 0: return fone return finf # Infinity if not n: return fnan if n < 0: return fzero return finf flag_inverse = False if n < 2: if n == 0: return fone if n == 1: return mpf_pos(s, prec, rnd) if n == -1: return mpf_div(fone, s, prec, rnd) # n < 0 rnd = reciprocal_rnd[rnd] flag_inverse = True extra_inverse = 5 prec += extra_inverse n = -n if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)): prec2 = prec + 10 fn = from_int(n) nth = mpf_rdiv_int(1, fn, prec2) r = mpf_pow(s, nth, prec2, rnd) s = normalize(r[0], r[1], r[2], r[3], prec, rnd) if flag_inverse: return mpf_div(fone, s, prec-extra_inverse, rnd) else: return s # Convert to a fixed-point number with prec2 bits. prec2 = prec + 2*n - (prec%n) # a few tests indicate that # for 10 < n < 10**4 a bit more precision is needed if n > 10: prec2 += prec2//10 prec2 = prec2 - prec2%n # Mantissa may have more bits than we need. Trim it down. shift = bc - prec2 # Adjust exponents to make prec2 and exp+shift multiples of n. sign1 = 0 es = exp+shift if es < 0: sign1 = 1 es = -es if sign1: shift += es%n else: shift -= es%n man = rshift(man, shift) extra = 10 exp1 = ((exp+shift-(n-1)*prec2)//n) - extra rnd_shift = 0 if flag_inverse: if rnd == 'u' or rnd == 'c': rnd_shift = 1 else: if rnd == 'd' or rnd == 'f': rnd_shift = 1 man = nthroot_fixed(man+rnd_shift, n, prec2, exp1) s = from_man_exp(man, exp1, prec, rnd) if flag_inverse: return mpf_div(fone, s, prec-extra_inverse, rnd) else: return s def mpf_cbrt(s, prec, rnd=round_fast): """cubic root of a positive number""" return mpf_nthroot(s, 3, prec, rnd) #----------------------------------------------------------------------------# # # # Logarithms # # # #----------------------------------------------------------------------------# def log_int_fixed(n, prec, ln2=None): """ Fast computation of log(n), caching the value for small n, intended for zeta sums. """ if n in log_int_cache: value, vprec = log_int_cache[n] if vprec >= prec: return value >> (vprec - prec) wp = prec + 10 if wp <= LOG_TAYLOR_SHIFT: if ln2 is None: ln2 = ln2_fixed(wp) r = bitcount(n) x = n << (wp-r) v = log_taylor_cached(x, wp) + r*ln2 else: v = to_fixed(mpf_log(from_int(n), wp+5), wp) if n < MAX_LOG_INT_CACHE: log_int_cache[n] = (v, wp) return v >> (wp-prec) def agm_fixed(a, b, prec): """ Fixed-point computation of agm(a,b), assuming a, b both close to unit magnitude. """ i = 0 while 1: anew = (a+b)>>1 if i > 4 and abs(a-anew) < 8: return a b = isqrt_fast(a*b) a = anew i += 1 return a def log_agm(x, prec): """ Fixed-point computation of -log(x) = log(1/x), suitable for large precision. It is required that 0 < x < 1. The algorithm used is the Sasaki-Kanada formula -log(x) = pi/agm(theta2(x)^2,theta3(x)^2). [1] For faster convergence in the theta functions, x should be chosen closer to 0. Guard bits must be added by the caller. HYPOTHESIS: if x = 2^(-n), n bits need to be added to account for the truncation to a fixed-point number, and this is the only significant cancellation error. The number of bits lost to roundoff is small and can be considered constant. [1] Richard P. Brent, "Fast Algorithms for High-Precision Computation of Elementary Functions (extended abstract)", http://wwwmaths.anu.edu.au/~brent/pd/RNC7-Brent.pdf """ x2 = (x*x) >> prec # Compute jtheta2(x)**2 s = a = b = x2 while a: b = (b*x2) >> prec a = (a*b) >> prec s += a s += (MPZ_ONE<>(prec-2) s = (s*isqrt_fast(x<>prec # Compute jtheta3(x)**2 t = a = b = x while a: b = (b*x2) >> prec a = (a*b) >> prec t += a t = (MPZ_ONE<>prec # Final formula p = agm_fixed(s, t, prec) return (pi_fixed(prec) << prec) // p def log_taylor(x, prec, r=0): """ Fixed-point calculation of log(x). It is assumed that x is close enough to 1 for the Taylor series to converge quickly. Convergence can be improved by specifying r > 0 to compute log(x^(1/2^r))*2^r, at the cost of performing r square roots. The caller must provide sufficient guard bits. """ for i in xrange(r): x = isqrt_fast(x<> prec v4 = (v2*v2) >> prec s0 = v s1 = v//3 v = (v*v4) >> prec k = 5 while v: s0 += v // k k += 2 s1 += v // k v = (v*v4) >> prec k += 2 s1 = (s1*v2) >> prec s = (s0+s1) << (1+r) if sign: return -s return s def log_taylor_cached(x, prec): """ Fixed-point computation of log(x), assuming x in (0.5, 2) and prec <= LOG_TAYLOR_PREC. """ n = x >> (prec-LOG_TAYLOR_SHIFT) cached_prec = cache_prec_steps[prec] dprec = cached_prec - prec if (n, cached_prec) in log_taylor_cache: a, log_a = log_taylor_cache[n, cached_prec] else: a = n << (cached_prec - LOG_TAYLOR_SHIFT) log_a = log_taylor(a, cached_prec, 8) log_taylor_cache[n, cached_prec] = (a, log_a) a >>= dprec log_a >>= dprec u = ((x - a) << prec) // a v = (u << prec) // ((MPZ_TWO << prec) + u) v2 = (v*v) >> prec v4 = (v2*v2) >> prec s0 = v s1 = v//3 v = (v*v4) >> prec k = 5 while v: s0 += v//k k += 2 s1 += v//k v = (v*v4) >> prec k += 2 s1 = (s1*v2) >> prec s = (s0+s1) << 1 return log_a + s def mpf_log(x, prec, rnd=round_fast): """ Compute the natural logarithm of the mpf value x. If x is negative, ComplexResult is raised. """ sign, man, exp, bc = x #------------------------------------------------------------------ # Handle special values if not man: if x == fzero: return fninf if x == finf: return finf if x == fnan: return fnan if sign: raise ComplexResult("logarithm of a negative number") wp = prec + 20 #------------------------------------------------------------------ # Handle log(2^n) = log(n)*2. # Here we catch the only possible exact value, log(1) = 0 if man == 1: if not exp: return fzero return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) mag = exp+bc abs_mag = abs(mag) #------------------------------------------------------------------ # Handle x = 1+eps, where log(x) ~ x. We need to check for # cancellation when moving to fixed-point math and compensate # by increasing the precision. Note that abs_mag in (0, 1) <=> # 0.5 < x < 2 and x != 1 if abs_mag <= 1: # Calculate t = x-1 to measure distance from 1 in bits tsign = 1-abs_mag if tsign: tman = (MPZ_ONE< wp: t = normalize(tsign, tman, abs_mag-bc, tbc, tbc, 'n') return mpf_perturb(t, tsign, prec, rnd) else: wp += cancellation # TODO: if close enough to 1, we could use Taylor series # even in the AGM precision range, since the Taylor series # converges rapidly #------------------------------------------------------------------ # Another special case: # n*log(2) is a good enough approximation if abs_mag > 10000: if bitcount(abs_mag) > wp: return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) #------------------------------------------------------------------ # General case. # Perform argument reduction using log(x) = log(x*2^n) - n*log(2): # If we are in the Taylor precision range, choose magnitude 0 or 1. # If we are in the AGM precision range, choose magnitude -m for # some large m; benchmarking on one machine showed m = prec/20 to be # optimal between 1000 and 100,000 digits. if wp <= LOG_TAYLOR_PREC: m = log_taylor_cached(lshift(man, wp-bc), wp) if mag: m += mag*ln2_fixed(wp) else: optimal_mag = -wp//LOG_AGM_MAG_PREC_RATIO n = optimal_mag - mag x = mpf_shift(x, n) wp += (-optimal_mag) m = -log_agm(to_fixed(x, wp), wp) m -= n*ln2_fixed(wp) return from_man_exp(m, -wp, prec, rnd) def mpf_log_hypot(a, b, prec, rnd): """ Computes log(sqrt(a^2+b^2)) accurately. """ # If either a or b is inf/nan/0, assume it to be a if not b[1]: a, b = b, a # a is inf/nan/0 if not a[1]: # both are inf/nan/0 if not b[1]: if a == b == fzero: return fninf if fnan in (a, b): return fnan # at least one term is (+/- inf)^2 return finf # only a is inf/nan/0 if a == fzero: # log(sqrt(0+b^2)) = log(|b|) return mpf_log(mpf_abs(b), prec, rnd) if a == fnan: return fnan return finf # Exact a2 = mpf_mul(a,a) b2 = mpf_mul(b,b) extra = 20 # Not exact h2 = mpf_add(a2, b2, prec+extra) cancelled = mpf_add(h2, fnone, 10) mag_cancelled = cancelled[2]+cancelled[3] # Just redo the sum exactly if necessary (could be smarter # and avoid memory allocation when a or b is precisely 1 # and the other is tiny...) if cancelled == fzero or mag_cancelled < -extra//2: h2 = mpf_add(a2, b2, prec+extra-min(a2[2],b2[2])) return mpf_shift(mpf_log(h2, prec, rnd), -1) #---------------------------------------------------------------------- # Inverse tangent # def atan_newton(x, prec): if prec >= 100: r = math.atan((x>>(prec-53))/2.0**53) else: r = math.atan(x/2.0**prec) prevp = 50 r = MPZ(int(r * 2.0**53) >> (53-prevp)) extra_p = 50 for wp in giant_steps(prevp, prec): wp += extra_p r = r << (wp-prevp) cos, sin = cos_sin_fixed(r, wp) tan = (sin << wp) // cos a = ((tan-rshift(x, prec-wp)) << wp) // ((MPZ_ONE<>wp)) r = r - a prevp = wp return rshift(r, prevp-prec) def atan_taylor_get_cached(n, prec): # Taylor series with caching wins up to huge precisions # To avoid unnecessary precomputation at low precision, we # do it in steps # Round to next power of 2 prec2 = (1<<(bitcount(prec-1))) + 20 dprec = prec2 - prec if (n, prec2) in atan_taylor_cache: a, atan_a = atan_taylor_cache[n, prec2] else: a = n << (prec2 - ATAN_TAYLOR_SHIFT) atan_a = atan_newton(a, prec2) atan_taylor_cache[n, prec2] = (a, atan_a) return (a >> dprec), (atan_a >> dprec) def atan_taylor(x, prec): n = (x >> (prec-ATAN_TAYLOR_SHIFT)) a, atan_a = atan_taylor_get_cached(n, prec) d = x - a s0 = v = (d << prec) // ((a**2 >> prec) + (a*d >> prec) + (MPZ_ONE << prec)) v2 = (v**2 >> prec) v4 = (v2 * v2) >> prec s1 = v//3 v = (v * v4) >> prec k = 5 while v: s0 += v // k k += 2 s1 += v // k v = (v * v4) >> prec k += 2 s1 = (s1 * v2) >> prec s = s0 - s1 return atan_a + s def atan_inf(sign, prec, rnd): if not sign: return mpf_shift(mpf_pi(prec, rnd), -1) return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) def mpf_atan(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fzero: return fzero if x == finf: return atan_inf(0, prec, rnd) if x == fninf: return atan_inf(1, prec, rnd) return fnan mag = exp + bc # Essentially infinity if mag > prec+20: return atan_inf(sign, prec, rnd) # Essentially ~ x if -mag > prec+20: return mpf_perturb(x, 1-sign, prec, rnd) wp = prec + 30 + abs(mag) # For large x, use atan(x) = pi/2 - atan(1/x) if mag >= 2: x = mpf_rdiv_int(1, x, wp) reciprocal = True else: reciprocal = False t = to_fixed(x, wp) if sign: t = -t if wp < ATAN_TAYLOR_PREC: a = atan_taylor(t, wp) else: a = atan_newton(t, wp) if reciprocal: a = ((pi_fixed(wp)>>1)+1) - a if sign: a = -a return from_man_exp(a, -wp, prec, rnd) # TODO: cleanup the special cases def mpf_atan2(y, x, prec, rnd=round_fast): xsign, xman, xexp, xbc = x ysign, yman, yexp, ybc = y if not yman: if y == fzero and x != fnan: if mpf_sign(x) >= 0: return fzero return mpf_pi(prec, rnd) if y in (finf, fninf): if x in (finf, fninf): return fnan # pi/2 if y == finf: return mpf_shift(mpf_pi(prec, rnd), -1) # -pi/2 return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) return fnan if ysign: return mpf_neg(mpf_atan2(mpf_neg(y), x, prec, negative_rnd[rnd])) if not xman: if x == fnan: return fnan if x == finf: return fzero if x == fninf: return mpf_pi(prec, rnd) if y == fzero: return fzero return mpf_shift(mpf_pi(prec, rnd), -1) tquo = mpf_atan(mpf_div(y, x, prec+4), prec+4) if xsign: return mpf_add(mpf_pi(prec+4), tquo, prec, rnd) else: return mpf_pos(tquo, prec, rnd) def mpf_asin(x, prec, rnd=round_fast): sign, man, exp, bc = x if bc+exp > 0 and x not in (fone, fnone): raise ComplexResult("asin(x) is real only for -1 <= x <= 1") # asin(x) = 2*atan(x/(1+sqrt(1-x**2))) wp = prec + 15 a = mpf_mul(x, x) b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp) c = mpf_div(x, b, wp) return mpf_shift(mpf_atan(c, prec, rnd), 1) def mpf_acos(x, prec, rnd=round_fast): # acos(x) = 2*atan(sqrt(1-x**2)/(1+x)) sign, man, exp, bc = x if bc + exp > 0: if x not in (fone, fnone): raise ComplexResult("acos(x) is real only for -1 <= x <= 1") if x == fnone: return mpf_pi(prec, rnd) wp = prec + 15 a = mpf_mul(x, x) b = mpf_sqrt(mpf_sub(fone, a, wp), wp) c = mpf_div(b, mpf_add(fone, x, wp), wp) return mpf_shift(mpf_atan(c, prec, rnd), 1) def mpf_asinh(x, prec, rnd=round_fast): wp = prec + 20 sign, man, exp, bc = x mag = exp+bc if mag < -8: if mag < -wp: return mpf_perturb(x, 1-sign, prec, rnd) wp += (-mag) # asinh(x) = log(x+sqrt(x**2+1)) # use reflection symmetry to avoid cancellation q = mpf_sqrt(mpf_add(mpf_mul(x, x), fone, wp), wp) q = mpf_add(mpf_abs(x), q, wp) if sign: return mpf_neg(mpf_log(q, prec, negative_rnd[rnd])) else: return mpf_log(q, prec, rnd) def mpf_acosh(x, prec, rnd=round_fast): # acosh(x) = log(x+sqrt(x**2-1)) wp = prec + 15 if mpf_cmp(x, fone) == -1: raise ComplexResult("acosh(x) is real only for x >= 1") q = mpf_sqrt(mpf_add(mpf_mul(x,x), fnone, wp), wp) return mpf_log(mpf_add(x, q, wp), prec, rnd) def mpf_atanh(x, prec, rnd=round_fast): # atanh(x) = log((1+x)/(1-x))/2 sign, man, exp, bc = x if (not man) and exp: if x in (fzero, fnan): return x raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") mag = bc + exp if mag > 0: if mag == 1 and man == 1: return [finf, fninf][sign] raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") wp = prec + 15 if mag < -8: if mag < -wp: return mpf_perturb(x, sign, prec, rnd) wp += (-mag) a = mpf_add(x, fone, wp) b = mpf_sub(fone, x, wp) return mpf_shift(mpf_log(mpf_div(a, b, wp), prec, rnd), -1) def mpf_fibonacci(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if x == fninf: return fnan return x # F(2^n) ~= 2^(2^n) size = abs(exp+bc) if exp >= 0: # Exact if size < 10 or size <= bitcount(prec): return from_int(ifib(to_int(x)), prec, rnd) # Use the modified Binet formula wp = prec + size + 20 a = mpf_phi(wp) b = mpf_add(mpf_shift(a, 1), fnone, wp) u = mpf_pow(a, x, wp) v = mpf_cos_pi(x, wp) v = mpf_div(v, u, wp) u = mpf_sub(u, v, wp) u = mpf_div(u, b, prec, rnd) return u #------------------------------------------------------------------------------- # Exponential-type functions #------------------------------------------------------------------------------- def exponential_series(x, prec, type=0): """ Taylor series for cosh/sinh or cos/sin. type = 0 -- returns exp(x) (slightly faster than cosh+sinh) type = 1 -- returns (cosh(x), sinh(x)) type = 2 -- returns (cos(x), sin(x)) """ if x < 0: x = -x sign = 1 else: sign = 0 r = int(0.5*prec**0.5) xmag = bitcount(x) - prec r = max(0, xmag + r) extra = 10 + 2*max(r,-xmag) wp = prec + extra x <<= (extra - r) one = MPZ_ONE << wp alt = (type == 2) if prec < EXP_SERIES_U_CUTOFF: x2 = a = (x*x) >> wp x4 = (x2*x2) >> wp s0 = s1 = MPZ_ZERO k = 2 while a: a //= (k-1)*k; s0 += a; k += 2 a //= (k-1)*k; s1 += a; k += 2 a = (a*x4) >> wp s1 = (x2*s1) >> wp if alt: c = s1 - s0 + one else: c = s1 + s0 + one else: u = int(0.3*prec**0.35) x2 = a = (x*x) >> wp xpowers = [one, x2] for i in xrange(1, u): xpowers.append((xpowers[-1]*x2)>>wp) sums = [MPZ_ZERO] * u k = 2 while a: for i in xrange(u): a //= (k-1)*k if alt and k & 2: sums[i] -= a else: sums[i] += a k += 2 a = (a*xpowers[-1]) >> wp for i in xrange(1, u): sums[i] = (sums[i]*xpowers[i]) >> wp c = sum(sums) + one if type == 0: s = isqrt_fast(c*c - (one<> wp return v >> extra else: # Repeatedly apply the double-angle formula # cosh(2*x) = 2*cosh(x)^2 - 1 # cos(2*x) = 2*cos(x)^2 - 1 pshift = wp-1 for i in xrange(r): c = ((c*c) >> pshift) - one # With the abs, this is the same for sinh and sin s = isqrt_fast(abs((one<>extra), (s>>extra) def exp_basecase(x, prec): """ Compute exp(x) as a fixed-point number. Works for any x, but for speed should have |x| < 1. For an arbitrary number, use exp(x) = exp(x-m*log(2)) * 2^m where m = floor(x/log(2)). """ if prec > EXP_COSH_CUTOFF: return exponential_series(x, prec, 0) r = int(prec**0.5) prec += r s0 = s1 = (MPZ_ONE << prec) k = 2 a = x2 = (x*x) >> prec while a: a //= k; s0 += a; k += 1 a //= k; s1 += a; k += 1 a = (a*x2) >> prec s1 = (s1*x) >> prec s = s0 + s1 u = r while r: s = (s*s) >> prec r -= 1 return s >> u def exp_expneg_basecase(x, prec): """ Computation of exp(x), exp(-x) """ if prec > EXP_COSH_CUTOFF: cosh, sinh = exponential_series(x, prec, 1) return cosh+sinh, cosh-sinh a = exp_basecase(x, prec) b = (MPZ_ONE << (prec+prec)) // a return a, b def cos_sin_basecase(x, prec): """ Compute cos(x), sin(x) as fixed-point numbers, assuming x in [0, pi/2). For an arbitrary number, use x' = x - m*(pi/2) where m = floor(x/(pi/2)) along with quarter-period symmetries. """ if prec > COS_SIN_CACHE_PREC: return exponential_series(x, prec, 2) precs = prec - COS_SIN_CACHE_STEP t = x >> precs n = int(t) if n not in cos_sin_cache: w = t<<(10+COS_SIN_CACHE_PREC-COS_SIN_CACHE_STEP) cos_t, sin_t = exponential_series(w, 10+COS_SIN_CACHE_PREC, 2) cos_sin_cache[n] = (cos_t>>10), (sin_t>>10) cos_t, sin_t = cos_sin_cache[n] offset = COS_SIN_CACHE_PREC - prec cos_t >>= offset sin_t >>= offset x -= t << precs cos = MPZ_ONE << prec sin = x k = 2 a = -((x*x) >> prec) while a: a //= k; cos += a; k += 1; a = (a*x) >> prec a //= k; sin += a; k += 1; a = -((a*x) >> prec) return ((cos*cos_t-sin*sin_t) >> prec), ((sin*cos_t+cos*sin_t) >> prec) def mpf_exp(x, prec, rnd=round_fast): sign, man, exp, bc = x if man: mag = bc + exp wp = prec + 14 if sign: man = -man # TODO: the best cutoff depends on both x and the precision. if prec > 600 and exp >= 0: # Need about log2(exp(n)) ~= 1.45*mag extra precision e = mpf_e(wp+int(1.45*mag)) return mpf_pow_int(e, man<= 2 if mag > 1: # For large arguments: exp(2^mag*(1+eps)) = # exp(2^mag)*exp(2^mag*eps) = exp(2^mag)*(1 + 2^mag*eps + ...) # so about mag extra bits is required. wpmod = wp + mag offset = exp + wpmod if offset >= 0: t = man << offset else: t = man >> (-offset) lg2 = ln2_fixed(wpmod) n, t = divmod(t, lg2) n = int(n) t >>= mag else: offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) n = 0 man = exp_basecase(t, wp) return from_man_exp(man, n-wp, prec, rnd) if not exp: return fone if x == fninf: return fzero return x def mpf_cosh_sinh(x, prec, rnd=round_fast, tanh=0): """Simultaneously compute (cosh(x), sinh(x)) for real x""" sign, man, exp, bc = x if (not man) and exp: if tanh: if x == finf: return fone if x == fninf: return fnone return fnan if x == finf: return (finf, finf) if x == fninf: return (finf, fninf) return fnan, fnan mag = exp+bc wp = prec+14 if mag < -4: # Extremely close to 0, sinh(x) ~= x and cosh(x) ~= 1 if mag < -wp: if tanh: return mpf_perturb(x, 1-sign, prec, rnd) cosh = mpf_perturb(fone, 0, prec, rnd) sinh = mpf_perturb(x, sign, prec, rnd) return cosh, sinh # Fix for cancellation when computing sinh wp += (-mag) # Does exp(-2*x) vanish? if mag > 10: if 3*(1<<(mag-1)) > wp: # XXX: rounding if tanh: return mpf_perturb([fone,fnone][sign], 1-sign, prec, rnd) c = s = mpf_shift(mpf_exp(mpf_abs(x), prec, rnd), -1) if sign: s = mpf_neg(s) return c, s # |x| > 1 if mag > 1: wpmod = wp + mag offset = exp + wpmod if offset >= 0: t = man << offset else: t = man >> (-offset) lg2 = ln2_fixed(wpmod) n, t = divmod(t, lg2) n = int(n) t >>= mag else: offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) n = 0 a, b = exp_expneg_basecase(t, wp) # TODO: optimize division precision cosh = a + (b>>(2*n)) sinh = a - (b>>(2*n)) if sign: sinh = -sinh if tanh: man = (sinh << wp) // cosh return from_man_exp(man, -wp, prec, rnd) else: cosh = from_man_exp(cosh, n-wp-1, prec, rnd) sinh = from_man_exp(sinh, n-wp-1, prec, rnd) return cosh, sinh def mod_pi2(man, exp, mag, wp): # Reduce to standard interval if mag > 0: i = 0 while 1: cancellation_prec = 20 << i wpmod = wp + mag + cancellation_prec pi2 = pi_fixed(wpmod-1) pi4 = pi2 >> 1 offset = wpmod + exp if offset >= 0: t = man << offset else: t = man >> (-offset) n, y = divmod(t, pi2) if y > pi4: small = pi2 - y else: small = y if small >> (wp+mag-10): n = int(n) t = y >> mag wp = wpmod - mag break i += 1 else: wp += (-mag) offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) n = 0 return t, n, wp def mpf_cos_sin(x, prec, rnd=round_fast, which=0, pi=False): """ which: 0 -- return cos(x), sin(x) 1 -- return cos(x) 2 -- return sin(x) 3 -- return tan(x) if pi=True, compute for pi*x """ sign, man, exp, bc = x if not man: if exp: c, s = fnan, fnan else: c, s = fone, fzero if which == 0: return c, s if which == 1: return c if which == 2: return s if which == 3: return s mag = bc + exp wp = prec + 10 # Extremely small? if mag < 0: if mag < -wp: if pi: x = mpf_mul(x, mpf_pi(wp)) c = mpf_perturb(fone, 1, prec, rnd) s = mpf_perturb(x, 1-sign, prec, rnd) if which == 0: return c, s if which == 1: return c if which == 2: return s if which == 3: return mpf_perturb(x, sign, prec, rnd) if pi: if exp >= -1: if exp == -1: c = fzero s = (fone, fnone)[bool(man & 2) ^ sign] elif exp == 0: c, s = (fnone, fzero) else: c, s = (fone, fzero) if which == 0: return c, s if which == 1: return c if which == 2: return s if which == 3: return mpf_div(s, c, prec, rnd) # Subtract nearest half-integer (= mod by pi/2) n = ((man >> (-exp-2)) + 1) >> 1 man = man - (n << (-exp-1)) mag2 = bitcount(man) + exp wp = prec + 10 - mag2 offset = exp + wp if offset >= 0: t = man << offset else: t = man >> (-offset) t = (t*pi_fixed(wp)) >> wp else: t, n, wp = mod_pi2(man, exp, mag, wp) c, s = cos_sin_basecase(t, wp) m = n & 3 if m == 1: c, s = -s, c elif m == 2: c, s = -c, -s elif m == 3: c, s = s, -c if sign: s = -s if which == 0: c = from_man_exp(c, -wp, prec, rnd) s = from_man_exp(s, -wp, prec, rnd) return c, s if which == 1: return from_man_exp(c, -wp, prec, rnd) if which == 2: return from_man_exp(s, -wp, prec, rnd) if which == 3: return from_rational(s, c, prec, rnd) def mpf_cos(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1) def mpf_sin(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2) def mpf_tan(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 3) def mpf_cos_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 0, 1) def mpf_cos_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 1, 1) def mpf_sin_pi(x, prec, rnd=round_fast): return mpf_cos_sin(x, prec, rnd, 2, 1) def mpf_cosh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[0] def mpf_sinh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd)[1] def mpf_tanh(x, prec, rnd=round_fast): return mpf_cosh_sinh(x, prec, rnd, tanh=1) # Low-overhead fixed-point versions def cos_sin_fixed(x, prec, pi2=None): if pi2 is None: pi2 = pi_fixed(prec-1) n, t = divmod(x, pi2) n = int(n) c, s = cos_sin_basecase(t, prec) m = n & 3 if m == 0: return c, s if m == 1: return -s, c if m == 2: return -c, -s if m == 3: return s, -c def exp_fixed(x, prec, ln2=None): if ln2 is None: ln2 = ln2_fixed(prec) n, t = divmod(x, ln2) n = int(n) v = exp_basecase(t, prec) if n >= 0: return v << n else: return v >> (-n) if BACKEND == 'sage': try: import sage.libs.mpmath.ext_libmp as _lbmp mpf_sqrt = _lbmp.mpf_sqrt mpf_exp = _lbmp.mpf_exp mpf_log = _lbmp.mpf_log mpf_cos = _lbmp.mpf_cos mpf_sin = _lbmp.mpf_sin mpf_pow = _lbmp.mpf_pow exp_fixed = _lbmp.exp_fixed cos_sin_fixed = _lbmp.cos_sin_fixed log_int_fixed = _lbmp.log_int_fixed except (ImportError, AttributeError): print("Warning: Sage imports in libelefun failed") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/libmp/__init__.py0000644000175000017500000000734212014170666025607 0ustar georgeskgeorgeskfrom .libmpf import (prec_to_dps, dps_to_prec, repr_dps, round_down, round_up, round_floor, round_ceiling, round_nearest, to_pickable, from_pickable, ComplexResult, fzero, fnzero, fone, fnone, ftwo, ften, fhalf, fnan, finf, fninf, math_float_inf, round_int, normalize, normalize1, from_man_exp, from_int, to_man_exp, to_int, mpf_ceil, mpf_floor, mpf_nint, mpf_frac, from_float, to_float, from_rational, to_rational, to_fixed, mpf_rand, mpf_eq, mpf_hash, mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_ge, mpf_pos, mpf_neg, mpf_abs, mpf_sign, mpf_add, mpf_sub, mpf_sum, mpf_mul, mpf_mul_int, mpf_shift, mpf_frexp, mpf_div, mpf_rdiv_int, mpf_mod, mpf_pow_int, mpf_perturb, to_digits_exp, to_str, str_to_man_exp, from_str, from_bstr, to_bstr, mpf_sqrt, mpf_hypot) from .libmpc import (mpc_one, mpc_zero, mpc_two, mpc_half, mpc_is_inf, mpc_is_infnan, mpc_to_str, mpc_to_complex, mpc_hash, mpc_conjugate, mpc_is_nonzero, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_pos, mpc_neg, mpc_shift, mpc_abs, mpc_arg, mpc_floor, mpc_ceil, mpc_nint, mpc_frac, mpc_mul, mpc_square, mpc_mul_mpf, mpc_mul_imag_mpf, mpc_mul_int, mpc_div, mpc_div_mpf, mpc_reciprocal, mpc_mpf_div, complex_int_pow, mpc_pow, mpc_pow_mpf, mpc_pow_int, mpc_sqrt, mpc_nthroot, mpc_cbrt, mpc_exp, mpc_log, mpc_cos, mpc_sin, mpc_tan, mpc_cos_pi, mpc_sin_pi, mpc_cosh, mpc_sinh, mpc_tanh, mpc_atan, mpc_acos, mpc_asin, mpc_asinh, mpc_acosh, mpc_atanh, mpc_fibonacci, mpf_expj, mpf_expjpi, mpc_expj, mpc_expjpi, mpc_cos_sin, mpc_cos_sin_pi) from .libelefun import (ln2_fixed, mpf_ln2, ln10_fixed, mpf_ln10, pi_fixed, mpf_pi, e_fixed, mpf_e, phi_fixed, mpf_phi, degree_fixed, mpf_degree, mpf_pow, mpf_nthroot, mpf_cbrt, log_int_fixed, agm_fixed, mpf_log, mpf_log_hypot, mpf_exp, mpf_cos_sin, mpf_cos, mpf_sin, mpf_tan, mpf_cos_sin_pi, mpf_cos_pi, mpf_sin_pi, mpf_cosh_sinh, mpf_cosh, mpf_sinh, mpf_tanh, mpf_atan, mpf_atan2, mpf_asin, mpf_acos, mpf_asinh, mpf_acosh, mpf_atanh, mpf_fibonacci) from .libhyper import (NoConvergence, make_hyp_summator, mpf_erf, mpf_erfc, mpf_ei, mpc_ei, mpf_e1, mpc_e1, mpf_expint, mpf_ci_si, mpf_ci, mpf_si, mpc_ci, mpc_si, mpf_besseljn, mpc_besseljn, mpf_agm, mpf_agm1, mpc_agm, mpc_agm1, mpf_ellipk, mpc_ellipk, mpf_ellipe, mpc_ellipe) from .gammazeta import (catalan_fixed, mpf_catalan, khinchin_fixed, mpf_khinchin, glaisher_fixed, mpf_glaisher, apery_fixed, mpf_apery, euler_fixed, mpf_euler, mertens_fixed, mpf_mertens, twinprime_fixed, mpf_twinprime, mpf_bernoulli, bernfrac, mpf_gamma_int, mpf_factorial, mpc_factorial, mpf_gamma, mpc_gamma, mpf_loggamma, mpc_loggamma, mpf_rgamma, mpc_rgamma, mpf_gamma_old, mpc_gamma_old, mpf_factorial_old, mpc_factorial_old, mpf_harmonic, mpc_harmonic, mpf_psi0, mpc_psi0, mpf_psi, mpc_psi, mpf_zeta_int, mpf_zeta, mpc_zeta, mpf_altzeta, mpc_altzeta, mpf_zetasum, mpc_zetasum) from .libmpi import (mpi_str, mpi_from_str, mpi_to_str, mpi_eq, mpi_ne, mpi_lt, mpi_le, mpi_gt, mpi_ge, mpi_add, mpi_sub, mpi_delta, mpi_mid, mpi_pos, mpi_neg, mpi_abs, mpi_mul, mpi_div, mpi_exp, mpi_log, mpi_sqrt, mpi_pow_int, mpi_pow, mpi_cos_sin, mpi_cos, mpi_sin, mpi_tan, mpi_cot, mpi_atan, mpi_atan2, mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow, mpci_abs, mpci_pow, mpci_exp, mpci_log, mpci_cos, mpci_sin, mpi_gamma, mpci_gamma, mpi_loggamma, mpci_loggamma, mpi_rgamma, mpci_rgamma, mpi_factorial, mpci_factorial) from .libintmath import (trailing, bitcount, numeral, bin_to_radix, isqrt, isqrt_small, isqrt_fast, sqrt_fixed, sqrtrem, ifib, ifac, list_primes, isprime, moebius, gcd, eulernum) from .backend import (gmpy, sage, BACKEND, STRICT, MPZ, MPZ_TYPE, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_THREE, MPZ_FIVE, int_types, HASH_MODULUS, HASH_BITS) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/0000755000175000017500000000000012014170666024200 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/extrapolation.py0000644000175000017500000013236212014170666027452 0ustar georgeskgeorgesktry: from itertools import izip except ImportError: izip = zip from ..libmp.backend import xrange from .calculus import defun try: next = next except NameError: next = lambda _: _.next() @defun def richardson(ctx, seq): r""" Given a list ``seq`` of the first `N` elements of a slowly convergent infinite sequence, :func:`~mpmath.richardson` computes the `N`-term Richardson extrapolate for the limit. :func:`~mpmath.richardson` returns `(v, c)` where `v` is the estimated limit and `c` is the magnitude of the largest weight used during the computation. The weight provides an estimate of the precision lost to cancellation. Due to cancellation effects, the sequence must be typically be computed at a much higher precision than the target accuracy of the extrapolation. **Applicability and issues** The `N`-step Richardson extrapolation algorithm used by :func:`~mpmath.richardson` is described in [1]. Richardson extrapolation only works for a specific type of sequence, namely one converging like partial sums of `P(1)/Q(1) + P(2)/Q(2) + \ldots` where `P` and `Q` are polynomials. When the sequence does not convergence at such a rate :func:`~mpmath.richardson` generally produces garbage. Richardson extrapolation has the advantage of being fast: the `N`-term extrapolate requires only `O(N)` arithmetic operations, and usually produces an estimate that is accurate to `O(N)` digits. Contrast with the Shanks transformation (see :func:`~mpmath.shanks`), which requires `O(N^2)` operations. :func:`~mpmath.richardson` is unable to produce an estimate for the approximation error. One way to estimate the error is to perform two extrapolations with slightly different `N` and comparing the results. Richardson extrapolation does not work for oscillating sequences. As a simple workaround, :func:`~mpmath.richardson` detects if the last three elements do not differ monotonically, and in that case applies extrapolation only to the even-index elements. **Example** Applying Richardson extrapolation to the Leibniz series for `\pi`:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) ... for m in range(1,30)] >>> v, c = richardson(S[:10]) >>> v 3.2126984126984126984126984127 >>> nprint([v-pi, c]) [0.0711058, 2.0] >>> v, c = richardson(S[:30]) >>> v 3.14159265468624052829954206226 >>> nprint([v-pi, c]) [1.09645e-9, 20833.3] **References** 1. [BenderOrszag]_ pp. 375-376 """ assert len(seq) >= 3 if ctx.sign(seq[-1]-seq[-2]) != ctx.sign(seq[-2]-seq[-3]): seq = seq[::2] N = len(seq)//2-1 s = ctx.zero # The general weight is c[k] = (N+k)**N * (-1)**(k+N) / k! / (N-k)! # To avoid repeated factorials, we simplify the quotient # of successive weights to obtain a recurrence relation c = (-1)**N * N**N / ctx.mpf(ctx._ifac(N)) maxc = 1 for k in xrange(N+1): s += c * seq[N+k] maxc = max(abs(c), maxc) c *= (k-N)*ctx.mpf(k+N+1)**N c /= ((1+k)*ctx.mpf(k+N)**N) return s, maxc @defun def shanks(ctx, seq, table=None, randomized=False): r""" Given a list ``seq`` of the first `N` elements of a slowly convergent infinite sequence `(A_k)`, :func:`~mpmath.shanks` computes the iterated Shanks transformation `S(A), S(S(A)), \ldots, S^{N/2}(A)`. The Shanks transformation often provides strong convergence acceleration, especially if the sequence is oscillating. The iterated Shanks transformation is computed using the Wynn epsilon algorithm (see [1]). :func:`~mpmath.shanks` returns the full epsilon table generated by Wynn's algorithm, which can be read off as follows: * The table is a list of lists forming a lower triangular matrix, where higher row and column indices correspond to more accurate values. * The columns with even index hold dummy entries (required for the computation) and the columns with odd index hold the actual extrapolates. * The last element in the last row is typically the most accurate estimate of the limit. * The difference to the third last element in the last row provides an estimate of the approximation error. * The magnitude of the second last element provides an estimate of the numerical accuracy lost to cancellation. For convenience, so the extrapolation is stopped at an odd index so that ``shanks(seq)[-1][-1]`` always gives an estimate of the limit. Optionally, an existing table can be passed to :func:`~mpmath.shanks`. This can be used to efficiently extend a previous computation after new elements have been appended to the sequence. The table will then be updated in-place. **The Shanks transformation** The Shanks transformation is defined as follows (see [2]): given the input sequence `(A_0, A_1, \ldots)`, the transformed sequence is given by .. math :: S(A_k) = \frac{A_{k+1}A_{k-1}-A_k^2}{A_{k+1}+A_{k-1}-2 A_k} The Shanks transformation gives the exact limit `A_{\infty}` in a single step if `A_k = A + a q^k`. Note in particular that it extrapolates the exact sum of a geometric series in a single step. Applying the Shanks transformation once often improves convergence substantially for an arbitrary sequence, but the optimal effect is obtained by applying it iteratively: `S(S(A_k)), S(S(S(A_k))), \ldots`. Wynn's epsilon algorithm provides an efficient way to generate the table of iterated Shanks transformations. It reduces the computation of each element to essentially a single division, at the cost of requiring dummy elements in the table. See [1] for details. **Precision issues** Due to cancellation effects, the sequence must be typically be computed at a much higher precision than the target accuracy of the extrapolation. If the Shanks transformation converges to the exact limit (such as if the sequence is a geometric series), then a division by zero occurs. By default, :func:`~mpmath.shanks` handles this case by terminating the iteration and returning the table it has generated so far. With *randomized=True*, it will instead replace the zero by a pseudorandom number close to zero. (TODO: find a better solution to this problem.) **Examples** We illustrate by applying Shanks transformation to the Leibniz series for `\pi`:: >>> from mpmath import * >>> mp.dps = 50 >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) ... for m in range(1,30)] >>> >>> T = shanks(S[:7]) >>> for row in T: ... nprint(row) ... [-0.75] [1.25, 3.16667] [-1.75, 3.13333, -28.75] [2.25, 3.14524, 82.25, 3.14234] [-2.75, 3.13968, -177.75, 3.14139, -969.937] [3.25, 3.14271, 327.25, 3.14166, 3515.06, 3.14161] The extrapolated accuracy is about 4 digits, and about 4 digits may have been lost due to cancellation:: >>> L = T[-1] >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) [2.22532e-5, 4.78309e-5, 3515.06] Now we extend the computation:: >>> T = shanks(S[:25], T) >>> L = T[-1] >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) [3.75527e-19, 1.48478e-19, 2.96014e+17] The value for pi is now accurate to 18 digits. About 18 digits may also have been lost to cancellation. Here is an example with a geometric series, where the convergence is immediate (the sum is exactly 1):: >>> mp.dps = 15 >>> for row in shanks([0.5, 0.75, 0.875, 0.9375, 0.96875]): ... nprint(row) [4.0] [8.0, 1.0] **References** 1. [GravesMorris]_ 2. [BenderOrszag]_ pp. 368-375 """ assert len(seq) >= 2 if table: START = len(table) else: START = 0 table = [] STOP = len(seq) - 1 if STOP & 1: STOP -= 1 one = ctx.one eps = +ctx.eps if randomized: from random import Random rnd = Random() rnd.seed(START) for i in xrange(START, STOP): row = [] for j in xrange(i+1): if j == 0: a, b = 0, seq[i+1]-seq[i] else: if j == 1: a = seq[i] else: a = table[i-1][j-2] b = row[j-1] - table[i-1][j-1] if not b: if randomized: b = rnd.getrandbits(10)*eps elif i & 1: return table[:-1] else: return table row.append(a + one/b) table.append(row) return table @defun def sumap(ctx, f, interval, integral=None, error=False): r""" Evaluates an infinite series of an analytic summand *f* using the Abel-Plana formula .. math :: \sum_{k=0}^{\infty} f(k) = \int_0^{\infty} f(t) dt + \frac{1}{2} f(0) + i \int_0^{\infty} \frac{f(it)-f(-it)}{e^{2\pi t}-1} dt. Unlike the Euler-Maclaurin formula (see :func:`~mpmath.sumem`), the Abel-Plana formula does not require derivatives. However, it only works when `|f(it)-f(-it)|` does not increase too rapidly with `t`. **Examples** The Abel-Plana formula is particularly useful when the summand decreases like a power of `k`; for example when the sum is a pure zeta function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sumap(lambda k: 1/k**2.5, [1,inf]) 1.34148725725091717975677 >>> zeta(2.5) 1.34148725725091717975677 >>> sumap(lambda k: 1/(k+1j)**(2.5+2.5j), [1,inf]) (-3.385361068546473342286084 - 0.7432082105196321803869551j) >>> zeta(2.5+2.5j, 1+1j) (-3.385361068546473342286084 - 0.7432082105196321803869551j) If the series is alternating, numerical quadrature along the real line is likely to give poor results, so it is better to evaluate the first term symbolically whenever possible: >>> n=3; z=-0.75 >>> I = expint(n,-log(z)) >>> chop(sumap(lambda k: z**k / k**n, [1,inf], integral=I)) -0.6917036036904594510141448 >>> polylog(n,z) -0.6917036036904594510141448 """ prec = ctx.prec try: ctx.prec += 10 a, b = interval assert b == ctx.inf g = lambda x: f(x+a) if integral is None: i1, err1 = ctx.quad(g, [0,ctx.inf], error=True) else: i1, err1 = integral, 0 j = ctx.j p = ctx.pi * 2 if ctx._is_real_type(i1): h = lambda t: -2 * ctx.im(g(j*t)) / ctx.expm1(p*t) else: h = lambda t: j*(g(j*t)-g(-j*t)) / ctx.expm1(p*t) i2, err2 = ctx.quad(h, [0,ctx.inf], error=True) err = err1+err2 v = i1+i2+0.5*g(ctx.mpf(0)) finally: ctx.prec = prec if error: return +v, err return +v @defun def sumem(ctx, f, interval, tol=None, reject=10, integral=None, adiffs=None, bdiffs=None, verbose=False, error=False, _fast_abort=False): r""" Uses the Euler-Maclaurin formula to compute an approximation accurate to within ``tol`` (which defaults to the present epsilon) of the sum .. math :: S = \sum_{k=a}^b f(k) where `(a,b)` are given by ``interval`` and `a` or `b` may be infinite. The approximation is .. math :: S \sim \int_a^b f(x) \,dx + \frac{f(a)+f(b)}{2} + \sum_{k=1}^{\infty} \frac{B_{2k}}{(2k)!} \left(f^{(2k-1)}(b)-f^{(2k-1)}(a)\right). The last sum in the Euler-Maclaurin formula is not generally convergent (a notable exception is if `f` is a polynomial, in which case Euler-Maclaurin actually gives an exact result). The summation is stopped as soon as the quotient between two consecutive terms falls below *reject*. That is, by default (*reject* = 10), the summation is continued as long as each term adds at least one decimal. Although not convergent, convergence to a given tolerance can often be "forced" if `b = \infty` by summing up to `a+N` and then applying the Euler-Maclaurin formula to the sum over the range `(a+N+1, \ldots, \infty)`. This procedure is implemented by :func:`~mpmath.nsum`. By default numerical quadrature and differentiation is used. If the symbolic values of the integral and endpoint derivatives are known, it is more efficient to pass the value of the integral explicitly as ``integral`` and the derivatives explicitly as ``adiffs`` and ``bdiffs``. The derivatives should be given as iterables that yield `f(a), f'(a), f''(a), \ldots` (and the equivalent for `b`). **Examples** Summation of an infinite series, with automatic and symbolic integral and derivative values (the second should be much faster):: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> sumem(lambda n: 1/n**2, [32, inf]) 0.03174336652030209012658168043874142714132886413417 >>> I = mpf(1)/32 >>> D = adiffs=((-1)**n*fac(n+1)*32**(-2-n) for n in range(999)) >>> sumem(lambda n: 1/n**2, [32, inf], integral=I, adiffs=D) 0.03174336652030209012658168043874142714132886413417 An exact evaluation of a finite polynomial sum:: >>> sumem(lambda n: n**5-12*n**2+3*n, [-100000, 200000]) 10500155000624963999742499550000.0 >>> print(sum(n**5-12*n**2+3*n for n in range(-100000, 200001))) 10500155000624963999742499550000 """ tol = tol or +ctx.eps interval = ctx._as_points(interval) a = ctx.convert(interval[0]) b = ctx.convert(interval[-1]) err = ctx.zero prev = 0 M = 10000 if a == ctx.ninf: adiffs = (0 for n in xrange(M)) else: adiffs = adiffs or ctx.diffs(f, a) if b == ctx.inf: bdiffs = (0 for n in xrange(M)) else: bdiffs = bdiffs or ctx.diffs(f, b) orig = ctx.prec #verbose = 1 try: ctx.prec += 10 s = ctx.zero for k, (da, db) in enumerate(izip(adiffs, bdiffs)): if k & 1: term = (db-da) * ctx.bernoulli(k+1) / ctx.factorial(k+1) mag = abs(term) if verbose: print("term", k, "magnitude =", ctx.nstr(mag)) if k > 4 and mag < tol: s += term break elif k > 4 and abs(prev) / mag < reject: err += mag if _fast_abort: return [s, (s, err)][error] if verbose: print("Failed to converge") break else: s += term prev = term # Endpoint correction if a != ctx.ninf: s += f(a)/2 if b != ctx.inf: s += f(b)/2 # Tail integral if verbose: print("Integrating f(x) from x = %s to %s" % (ctx.nstr(a), ctx.nstr(b))) if integral: s += integral else: integral, ierr = ctx.quad(f, interval, error=True) if verbose: print("Integration error:", ierr) s += integral err += ierr finally: ctx.prec = orig if error: return s, err else: return s @defun def adaptive_extrapolation(ctx, update, emfun, kwargs): option = kwargs.get if ctx._fixed_precision: tol = option('tol', ctx.eps*2**10) else: tol = option('tol', ctx.eps/2**10) verbose = option('verbose', False) maxterms = option('maxterms', ctx.dps*10) method = option('method', 'r+s').split('+') skip = option('skip', 0) steps = iter(option('steps', xrange(10, 10**9, 10))) strict = option('strict') #steps = (10 for i in xrange(1000)) if 'd' in method or 'direct' in method: TRY_RICHARDSON = TRY_SHANKS = TRY_EULER_MACLAURIN = False else: TRY_RICHARDSON = ('r' in method) or ('richardson' in method) TRY_SHANKS = ('s' in method) or ('shanks' in method) TRY_EULER_MACLAURIN = ('e' in method) or \ ('euler-maclaurin' in method) last_richardson_value = 0 shanks_table = [] index = 0 step = 10 partial = [] best = ctx.zero orig = ctx.prec try: if 'workprec' in kwargs: ctx.prec = kwargs['workprec'] elif TRY_RICHARDSON or TRY_SHANKS: ctx.prec = (ctx.prec+10) * 4 else: ctx.prec += 30 while 1: if index >= maxterms: break # Get new batch of terms try: step = next(steps) except StopIteration: pass if verbose: print("-"*70) print("Adding terms #%i-#%i" % (index, index+step)) update(partial, xrange(index, index+step)) index += step # Check direct error best = partial[-1] error = abs(best - partial[-2]) if verbose: print("Direct error: %s" % ctx.nstr(error)) if error <= tol: return best # Check each extrapolation method if TRY_RICHARDSON: value, maxc = ctx.richardson(partial) # Convergence richardson_error = abs(value - last_richardson_value) if verbose: print("Richardson error: %s" % ctx.nstr(richardson_error)) # Convergence if richardson_error <= tol: return value last_richardson_value = value # Unreliable due to cancellation if ctx.eps*maxc > tol: if verbose: print("Ran out of precision for Richardson") TRY_RICHARDSON = False if richardson_error < error: error = richardson_error best = value if TRY_SHANKS: shanks_table = ctx.shanks(partial, shanks_table, randomized=True) row = shanks_table[-1] if len(row) == 2: est1 = row[-1] shanks_error = 0 else: est1, maxc, est2 = row[-1], abs(row[-2]), row[-3] shanks_error = abs(est1-est2) if verbose: print("Shanks error: %s" % ctx.nstr(shanks_error)) if shanks_error <= tol: return est1 if ctx.eps*maxc > tol: if verbose: print("Ran out of precision for Shanks") TRY_SHANKS = False if shanks_error < error: error = shanks_error best = est1 if TRY_EULER_MACLAURIN: if ctx.mpc(ctx.sign(partial[-1]) / ctx.sign(partial[-2])).ae(-1): if verbose: print ("NOT using Euler-Maclaurin: the series appears" " to be alternating, so numerical\n quadrature" " will most likely fail") TRY_EULER_MACLAURIN = False else: value, em_error = emfun(index, tol) value += partial[-1] if verbose: print("Euler-Maclaurin error: %s" % ctx.nstr(em_error)) if em_error <= tol: return value if em_error < error: best = value finally: ctx.prec = orig if strict: raise ctx.NoConvergence if verbose: print("Warning: failed to converge to target accuracy") return best @defun def nsum(ctx, f, *intervals, **options): r""" Computes the sum .. math :: S = \sum_{k=a}^b f(k) where `(a, b)` = *interval*, and where `a = -\infty` and/or `b = \infty` are allowed, or more generally .. math :: S = \sum_{k_1=a_1}^{b_1} \cdots \sum_{k_n=a_n}^{b_n} f(k_1,\ldots,k_n) if multiple intervals are given. Two examples of infinite series that can be summed by :func:`~mpmath.nsum`, where the first converges rapidly and the second converges slowly, are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nsum(lambda n: 1/fac(n), [0, inf]) 2.71828182845905 >>> nsum(lambda n: 1/n**2, [1, inf]) 1.64493406684823 When appropriate, :func:`~mpmath.nsum` applies convergence acceleration to accurately estimate the sums of slowly convergent series. If the series is finite, :func:`~mpmath.nsum` currently does not attempt to perform any extrapolation, and simply calls :func:`~mpmath.fsum`. Multidimensional infinite series are reduced to a single-dimensional series over expanding hypercubes; if both infinite and finite dimensions are present, the finite ranges are moved innermost. For more advanced control over the summation order, use nested calls to :func:`~mpmath.nsum`, or manually rewrite the sum as a single-dimensional series. **Options** *tol* Desired maximum final error. Defaults roughly to the epsilon of the working precision. *method* Which summation algorithm to use (described below). Default: ``'richardson+shanks'``. *maxterms* Cancel after at most this many terms. Default: 10*dps. *steps* An iterable giving the number of terms to add between each extrapolation attempt. The default sequence is [10, 20, 30, 40, ...]. For example, if you know that approximately 100 terms will be required, efficiency might be improved by setting this to [100, 10]. Then the first extrapolation will be performed after 100 terms, the second after 110, etc. *verbose* Print details about progress. *ignore* If enabled, any term that raises ``ArithmeticError`` or ``ValueError`` (e.g. through division by zero) is replaced by a zero. This is convenient for lattice sums with a singular term near the origin. **Methods** Unfortunately, an algorithm that can efficiently sum any infinite series does not exist. :func:`~mpmath.nsum` implements several different algorithms that each work well in different cases. The *method* keyword argument selects a method. The default method is ``'r+s'``, i.e. both Richardson extrapolation and Shanks transformation is attempted. A slower method that handles more cases is ``'r+s+e'``. For very high precision summation, or if the summation needs to be fast (for example if multiple sums need to be evaluated), it is a good idea to investigate which one method works best and only use that. ``'richardson'`` / ``'r'``: Uses Richardson extrapolation. Provides useful extrapolation when `f(k) \sim P(k)/Q(k)` or when `f(k) \sim (-1)^k P(k)/Q(k)` for polynomials `P` and `Q`. See :func:`~mpmath.richardson` for additional information. ``'shanks'`` / ``'s'``: Uses Shanks transformation. Typically provides useful extrapolation when `f(k) \sim c^k` or when successive terms alternate signs. Is able to sum some divergent series. See :func:`~mpmath.shanks` for additional information. ``'euler-maclaurin'`` / ``'e'``: Uses the Euler-Maclaurin summation formula to approximate the remainder sum by an integral. This requires high-order numerical derivatives and numerical integration. The advantage of this algorithm is that it works regardless of the decay rate of `f`, as long as `f` is sufficiently smooth. See :func:`~mpmath.sumem` for additional information. ``'direct'`` / ``'d'``: Does not perform any extrapolation. This can be used (and should only be used for) rapidly convergent series. The summation automatically stops when the terms decrease below the target tolerance. **Basic examples** A finite sum:: >>> nsum(lambda k: 1/k, [1, 6]) 2.45 Summation of a series going to negative infinity and a doubly infinite series:: >>> nsum(lambda k: 1/k**2, [-inf, -1]) 1.64493406684823 >>> nsum(lambda k: 1/(1+k**2), [-inf, inf]) 3.15334809493716 :func:`~mpmath.nsum` handles sums of complex numbers:: >>> nsum(lambda k: (0.5+0.25j)**k, [0, inf]) (1.6 + 0.8j) The following sum converges very rapidly, so it is most efficient to sum it by disabling convergence acceleration:: >>> mp.dps = 1000 >>> a = nsum(lambda k: -(-1)**k * k**2 / fac(2*k), [1, inf], ... method='direct') >>> b = (cos(1)+sin(1))/4 >>> abs(a-b) < mpf('1e-998') True **Examples with Richardson extrapolation** Richardson extrapolation works well for sums over rational functions, as well as their alternating counterparts:: >>> mp.dps = 50 >>> nsum(lambda k: 1 / k**3, [1, inf], ... method='richardson') 1.2020569031595942853997381615114499907649862923405 >>> zeta(3) 1.2020569031595942853997381615114499907649862923405 >>> nsum(lambda n: (n + 3)/(n**3 + n**2), [1, inf], ... method='richardson') 2.9348022005446793094172454999380755676568497036204 >>> pi**2/2-2 2.9348022005446793094172454999380755676568497036204 >>> nsum(lambda k: (-1)**k / k**3, [1, inf], ... method='richardson') -0.90154267736969571404980362113358749307373971925537 >>> -3*zeta(3)/4 -0.90154267736969571404980362113358749307373971925538 **Examples with Shanks transformation** The Shanks transformation works well for geometric series and typically provides excellent acceleration for Taylor series near the border of their disk of convergence. Here we apply it to a series for `\log(2)`, which can be seen as the Taylor series for `\log(1+x)` with `x = 1`:: >>> nsum(lambda k: -(-1)**k/k, [1, inf], ... method='shanks') 0.69314718055994530941723212145817656807550013436025 >>> log(2) 0.69314718055994530941723212145817656807550013436025 Here we apply it to a slowly convergent geometric series:: >>> nsum(lambda k: mpf('0.995')**k, [0, inf], ... method='shanks') 200.0 Finally, Shanks' method works very well for alternating series where `f(k) = (-1)^k g(k)`, and often does so regardless of the exact decay rate of `g(k)`:: >>> mp.dps = 15 >>> nsum(lambda k: (-1)**(k+1) / k**1.5, [1, inf], ... method='shanks') 0.765147024625408 >>> (2-sqrt(2))*zeta(1.5)/2 0.765147024625408 The following slowly convergent alternating series has no known closed-form value. Evaluating the sum a second time at higher precision indicates that the value is probably correct:: >>> nsum(lambda k: (-1)**k / log(k), [2, inf], ... method='shanks') 0.924299897222939 >>> mp.dps = 30 >>> nsum(lambda k: (-1)**k / log(k), [2, inf], ... method='shanks') 0.92429989722293885595957018136 **Examples with Euler-Maclaurin summation** The sum in the following example has the wrong rate of convergence for either Richardson or Shanks to be effective. >>> f = lambda k: log(k)/k**2.5 >>> mp.dps = 15 >>> nsum(f, [1, inf], method='euler-maclaurin') 0.38734195032621 >>> -diff(zeta, 2.5) 0.38734195032621 Increasing ``steps`` improves speed at higher precision:: >>> mp.dps = 50 >>> nsum(f, [1, inf], method='euler-maclaurin', steps=[250]) 0.38734195032620997271199237593105101319948228874688 >>> -diff(zeta, 2.5) 0.38734195032620997271199237593105101319948228874688 **Divergent series** The Shanks transformation is able to sum some *divergent* series. In particular, it is often able to sum Taylor series beyond their radius of convergence (this is due to a relation between the Shanks transformation and Pade approximations; see :func:`~mpmath.pade` for an alternative way to evaluate divergent Taylor series). Here we apply it to `\log(1+x)` far outside the region of convergence:: >>> mp.dps = 50 >>> nsum(lambda k: -(-9)**k/k, [1, inf], ... method='shanks') 2.3025850929940456840179914546843642076011014886288 >>> log(10) 2.3025850929940456840179914546843642076011014886288 A particular type of divergent series that can be summed using the Shanks transformation is geometric series. The result is the same as using the closed-form formula for an infinite geometric series:: >>> mp.dps = 15 >>> for n in range(-8, 8): ... if n == 1: ... continue ... print("%s %s %s" % (mpf(n), mpf(1)/(1-n), ... nsum(lambda k: n**k, [0, inf], method='shanks'))) ... -8.0 0.111111111111111 0.111111111111111 -7.0 0.125 0.125 -6.0 0.142857142857143 0.142857142857143 -5.0 0.166666666666667 0.166666666666667 -4.0 0.2 0.2 -3.0 0.25 0.25 -2.0 0.333333333333333 0.333333333333333 -1.0 0.5 0.5 0.0 1.0 1.0 2.0 -1.0 -1.0 3.0 -0.5 -0.5 4.0 -0.333333333333333 -0.333333333333333 5.0 -0.25 -0.25 6.0 -0.2 -0.2 7.0 -0.166666666666667 -0.166666666666667 **Multidimensional sums** Any combination of finite and infinite ranges is allowed for the summation indices:: >>> mp.dps = 15 >>> nsum(lambda x,y: x+y, [2,3], [4,5]) 28.0 >>> nsum(lambda x,y: x/2**y, [1,3], [1,inf]) 6.0 >>> nsum(lambda x,y: y/2**x, [1,inf], [1,3]) 6.0 >>> nsum(lambda x,y,z: z/(2**x*2**y), [1,inf], [1,inf], [3,4]) 7.0 >>> nsum(lambda x,y,z: y/(2**x*2**z), [1,inf], [3,4], [1,inf]) 7.0 >>> nsum(lambda x,y,z: x/(2**z*2**y), [3,4], [1,inf], [1,inf]) 7.0 Some nice examples of double series with analytic solutions or reductions to single-dimensional series (see [1]):: >>> nsum(lambda m, n: 1/2**(m*n), [1,inf], [1,inf]) 1.60669515241529 >>> nsum(lambda n: 1/(2**n-1), [1,inf]) 1.60669515241529 >>> nsum(lambda i,j: (-1)**(i+j)/(i**2+j**2), [1,inf], [1,inf]) 0.278070510848213 >>> pi*(pi-3*ln2)/12 0.278070510848213 >>> nsum(lambda i,j: (-1)**(i+j)/(i+j)**2, [1,inf], [1,inf]) 0.129319852864168 >>> altzeta(2) - altzeta(1) 0.129319852864168 >>> nsum(lambda i,j: (-1)**(i+j)/(i+j)**3, [1,inf], [1,inf]) 0.0790756439455825 >>> altzeta(3) - altzeta(2) 0.0790756439455825 >>> nsum(lambda m,n: m**2*n/(3**m*(n*3**m+m*3**n)), ... [1,inf], [1,inf]) 0.28125 >>> mpf(9)/32 0.28125 >>> nsum(lambda i,j: fac(i-1)*fac(j-1)/fac(i+j), ... [1,inf], [1,inf], workprec=400) 1.64493406684823 >>> zeta(2) 1.64493406684823 A hard example of a multidimensional sum is the Madelung constant in three dimensions (see [2]). The defining sum converges very slowly and only conditionally, so :func:`~mpmath.nsum` is lucky to obtain an accurate value through convergence acceleration. The second evaluation below uses a much more efficient, rapidly convergent 2D sum:: >>> nsum(lambda x,y,z: (-1)**(x+y+z)/(x*x+y*y+z*z)**0.5, ... [-inf,inf], [-inf,inf], [-inf,inf], ignore=True) -1.74756459463318 >>> nsum(lambda x,y: -12*pi*sech(0.5*pi * \ ... sqrt((2*x+1)**2+(2*y+1)**2))**2, [0,inf], [0,inf]) -1.74756459463318 Another example of a lattice sum in 2D:: >>> nsum(lambda x,y: (-1)**(x+y) / (x**2+y**2), [-inf,inf], ... [-inf,inf], ignore=True) -2.1775860903036 >>> -pi*ln2 -2.1775860903036 An example of an Eisenstein series:: >>> nsum(lambda m,n: (m+n*1j)**(-4), [-inf,inf], [-inf,inf], ... ignore=True) (3.1512120021539 + 0.0j) **References** 1. [Weisstein]_ http://mathworld.wolfram.com/DoubleSeries.html, 2. [Weisstein]_ http://mathworld.wolfram.com/MadelungConstants.html """ infinite, g = standardize(ctx, f, intervals, options) if not infinite: return +g() def update(partial_sums, indices): if partial_sums: psum = partial_sums[-1] else: psum = ctx.zero for k in indices: psum = psum + g(ctx.mpf(k)) partial_sums.append(psum) prec = ctx.prec def emfun(point, tol): workprec = ctx.prec ctx.prec = prec + 10 v = ctx.sumem(g, [point, ctx.inf], tol, error=1) ctx.prec = workprec return v return +ctx.adaptive_extrapolation(update, emfun, options) def wrapsafe(f): def g(*args): try: return f(*args) except (ArithmeticError, ValueError): return 0 return g def standardize(ctx, f, intervals, options): if options.get("ignore"): f = wrapsafe(f) finite = [] infinite = [] for k, points in enumerate(intervals): a, b = ctx._as_points(points) if b < a: return False, (lambda: ctx.zero) if a == ctx.ninf or b == ctx.inf: infinite.append((k, (a,b))) else: finite.append((k, (int(a), int(b)))) if finite: f = fold_finite(ctx, f, finite) if not infinite: return False, lambda: f(*([0]*len(intervals))) if infinite: f = standardize_infinite(ctx, f, infinite) f = fold_infinite(ctx, f, infinite) args = [0] * len(intervals) d = infinite[0][0] def g(k): args[d] = k return f(*args) return True, g # backwards compatible itertools.product def cartesian_product(args): pools = map(tuple, args) result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod) def fold_finite(ctx, f, intervals): if not intervals: return f indices = [v[0] for v in intervals] points = [v[1] for v in intervals] ranges = [xrange(a, b+1) for (a,b) in points] def g(*args): args = list(args) s = ctx.zero for xs in cartesian_product(ranges): for dim, x in zip(indices, xs): args[dim] = ctx.mpf(x) s += f(*args) return s #print "Folded finite", indices return g # Standardize each interval to [0,inf] def standardize_infinite(ctx, f, intervals): if not intervals: return f dim, [a,b] = intervals[-1] if a == ctx.ninf: if b == ctx.inf: def g(*args): args = list(args) k = args[dim] if k: s = f(*args) args[dim] = -k s += f(*args) return s else: return f(*args) else: def g(*args): args = list(args) args[dim] = b - args[dim] return f(*args) else: def g(*args): args = list(args) args[dim] += a return f(*args) #print "Standardized infinity along dimension", dim, a, b return standardize_infinite(ctx, g, intervals[:-1]) def fold_infinite(ctx, f, intervals): if len(intervals) < 2: return f dim1 = intervals[-2][0] dim2 = intervals[-1][0] # Assume intervals are [0,inf] x [0,inf] x ... def g(*args): args = list(args) #args.insert(dim2, None) n = int(args[dim1]) s = ctx.zero #y = ctx.mpf(n) args[dim2] = ctx.mpf(n) #y for x in xrange(n+1): args[dim1] = ctx.mpf(x) s += f(*args) args[dim1] = ctx.mpf(n) #ctx.mpf(n) for y in xrange(n): args[dim2] = ctx.mpf(y) s += f(*args) return s #print "Folded infinite from", len(intervals), "to", (len(intervals)-1) return fold_infinite(ctx, g, intervals[:-1]) @defun def nprod(ctx, f, interval, nsum=False, **kwargs): r""" Computes the product .. math :: P = \prod_{k=a}^b f(k) where `(a, b)` = *interval*, and where `a = -\infty` and/or `b = \infty` are allowed. By default, :func:`~mpmath.nprod` uses the same extrapolation methods as :func:`~mpmath.nsum`, except applied to the partial products rather than partial sums, and the same keyword options as for :func:`~mpmath.nsum` are supported. If ``nsum=True``, the product is instead computed via :func:`~mpmath.nsum` as .. math :: P = \exp\left( \sum_{k=a}^b \log(f(k)) \right). This is slower, but can sometimes yield better results. It is also required (and used automatically) when Euler-Maclaurin summation is requested. **Examples** A simple finite product:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> nprod(lambda k: k, [1, 4]) 24.0 A large number of infinite products have known exact values, and can therefore be used as a reference. Most of the following examples are taken from MathWorld [1]. A few infinite products with simple values are:: >>> 2*nprod(lambda k: (4*k**2)/(4*k**2-1), [1, inf]) 3.141592653589793238462643 >>> nprod(lambda k: (1+1/k)**2/(1+2/k), [1, inf]) 2.0 >>> nprod(lambda k: (k**3-1)/(k**3+1), [2, inf]) 0.6666666666666666666666667 >>> nprod(lambda k: (1-1/k**2), [2, inf]) 0.5 Next, several more infinite products with more complicated values:: >>> nprod(lambda k: exp(1/k**2), [1, inf]); exp(pi**2/6) 5.180668317897115748416626 5.180668317897115748416626 >>> nprod(lambda k: (k**2-1)/(k**2+1), [2, inf]); pi*csch(pi) 0.2720290549821331629502366 0.2720290549821331629502366 >>> nprod(lambda k: (k**4-1)/(k**4+1), [2, inf]) 0.8480540493529003921296502 >>> pi*sinh(pi)/(cosh(sqrt(2)*pi)-cos(sqrt(2)*pi)) 0.8480540493529003921296502 >>> nprod(lambda k: (1+1/k+1/k**2)**2/(1+2/k+3/k**2), [1, inf]) 1.848936182858244485224927 >>> 3*sqrt(2)*cosh(pi*sqrt(3)/2)**2*csch(pi*sqrt(2))/pi 1.848936182858244485224927 >>> nprod(lambda k: (1-1/k**4), [2, inf]); sinh(pi)/(4*pi) 0.9190194775937444301739244 0.9190194775937444301739244 >>> nprod(lambda k: (1-1/k**6), [2, inf]) 0.9826842777421925183244759 >>> (1+cosh(pi*sqrt(3)))/(12*pi**2) 0.9826842777421925183244759 >>> nprod(lambda k: (1+1/k**2), [2, inf]); sinh(pi)/(2*pi) 1.838038955187488860347849 1.838038955187488860347849 >>> nprod(lambda n: (1+1/n)**n * exp(1/(2*n)-1), [1, inf]) 1.447255926890365298959138 >>> exp(1+euler/2)/sqrt(2*pi) 1.447255926890365298959138 The following two products are equivalent and can be evaluated in terms of a Jacobi theta function. Pi can be replaced by any value (as long as convergence is preserved):: >>> nprod(lambda k: (1-pi**-k)/(1+pi**-k), [1, inf]) 0.3838451207481672404778686 >>> nprod(lambda k: tanh(k*log(pi)/2), [1, inf]) 0.3838451207481672404778686 >>> jtheta(4,0,1/pi) 0.3838451207481672404778686 This product does not have a known closed form value:: >>> nprod(lambda k: (1-1/2**k), [1, inf]) 0.2887880950866024212788997 A product taken from `-\infty`:: >>> nprod(lambda k: 1-k**(-3), [-inf,-2]) 0.8093965973662901095786805 >>> cosh(pi*sqrt(3)/2)/(3*pi) 0.8093965973662901095786805 A doubly infinite product:: >>> nprod(lambda k: exp(1/(1+k**2)), [-inf, inf]) 23.41432688231864337420035 >>> exp(pi/tanh(pi)) 23.41432688231864337420035 A product requiring the use of Euler-Maclaurin summation to compute an accurate value:: >>> nprod(lambda k: (1-1/k**2.5), [2, inf], method='e') 0.696155111336231052898125 **References** 1. [Weisstein]_ http://mathworld.wolfram.com/InfiniteProduct.html """ if nsum or ('e' in kwargs.get('method', '')): orig = ctx.prec try: # TODO: we are evaluating log(1+eps) -> eps, which is # inaccurate. This currently works because nsum greatly # increases the working precision. But we should be # more intelligent and handle the precision here. ctx.prec += 10 v = ctx.nsum(lambda n: ctx.ln(f(n)), interval, **kwargs) finally: ctx.prec = orig return +ctx.exp(v) a, b = ctx._as_points(interval) if a == ctx.ninf: if b == ctx.inf: return f(0) * ctx.nprod(lambda k: f(-k) * f(k), [1, ctx.inf], **kwargs) return ctx.nprod(f, [-b, ctx.inf], **kwargs) elif b != ctx.inf: return ctx.fprod(f(ctx.mpf(k)) for k in xrange(int(a), int(b)+1)) a = int(a) def update(partial_products, indices): if partial_products: pprod = partial_products[-1] else: pprod = ctx.one for k in indices: pprod = pprod * f(a + ctx.mpf(k)) partial_products.append(pprod) return +ctx.adaptive_extrapolation(update, None, kwargs) @defun def limit(ctx, f, x, direction=1, exp=False, **kwargs): r""" Computes an estimate of the limit .. math :: \lim_{t \to x} f(t) where `x` may be finite or infinite. For finite `x`, :func:`~mpmath.limit` evaluates `f(x + d/n)` for consecutive integer values of `n`, where the approach direction `d` may be specified using the *direction* keyword argument. For infinite `x`, :func:`~mpmath.limit` evaluates values of `f(\mathrm{sign}(x) \cdot n)`. If the approach to the limit is not sufficiently fast to give an accurate estimate directly, :func:`~mpmath.limit` attempts to find the limit using Richardson extrapolation or the Shanks transformation. You can select between these methods using the *method* keyword (see documentation of :func:`~mpmath.nsum` for more information). **Options** The following options are available with essentially the same meaning as for :func:`~mpmath.nsum`: *tol*, *method*, *maxterms*, *steps*, *verbose*. If the option *exp=True* is set, `f` will be sampled at exponentially spaced points `n = 2^1, 2^2, 2^3, \ldots` instead of the linearly spaced points `n = 1, 2, 3, \ldots`. This can sometimes improve the rate of convergence so that :func:`~mpmath.limit` may return a more accurate answer (and faster). However, do note that this can only be used if `f` supports fast and accurate evaluation for arguments that are extremely close to the limit point (or if infinite, very large arguments). **Examples** A basic evaluation of a removable singularity:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> limit(lambda x: (x-sin(x))/x**3, 0) 0.166666666666666666666666666667 Computing the exponential function using its limit definition:: >>> limit(lambda n: (1+3/n)**n, inf) 20.0855369231876677409285296546 >>> exp(3) 20.0855369231876677409285296546 A limit for `\pi`:: >>> f = lambda n: 2**(4*n+1)*fac(n)**4/(2*n+1)/fac(2*n)**2 >>> limit(f, inf) 3.14159265358979323846264338328 Calculating the coefficient in Stirling's formula:: >>> limit(lambda n: fac(n) / (sqrt(n)*(n/e)**n), inf) 2.50662827463100050241576528481 >>> sqrt(2*pi) 2.50662827463100050241576528481 Evaluating Euler's constant `\gamma` using the limit representation .. math :: \gamma = \lim_{n \rightarrow \infty } \left[ \left( \sum_{k=1}^n \frac{1}{k} \right) - \log(n) \right] (which converges notoriously slowly):: >>> f = lambda n: sum([mpf(1)/k for k in range(1,int(n)+1)]) - log(n) >>> limit(f, inf) 0.577215664901532860606512090082 >>> +euler 0.577215664901532860606512090082 With default settings, the following limit converges too slowly to be evaluated accurately. Changing to exponential sampling however gives a perfect result:: >>> f = lambda x: sqrt(x**3+x**2)/(sqrt(x**3)+x) >>> limit(f, inf) 0.992831158558330281129249686491 >>> limit(f, inf, exp=True) 1.0 """ if ctx.isinf(x): direction = ctx.sign(x) g = lambda k: f(ctx.mpf(k+1)*direction) else: direction *= ctx.one g = lambda k: f(x + direction/(k+1)) if exp: h = g g = lambda k: h(2**k) def update(values, indices): for k in indices: values.append(g(k+1)) # XXX: steps used by nsum don't work well if not 'steps' in kwargs: kwargs['steps'] = [10] return +ctx.adaptive_extrapolation(update, None, kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/quadrature.py0000644000175000017500000011260212014170666026731 0ustar georgeskgeorgeskimport math from ..libmp.backend import xrange class QuadratureRule(object): """ Quadrature rules are implemented using this class, in order to simplify the code and provide a common infrastructure for tasks such as error estimation and node caching. You can implement a custom quadrature rule by subclassing :class:`QuadratureRule` and implementing the appropriate methods. The subclass can then be used by :func:`~mpmath.quad` by passing it as the *method* argument. :class:`QuadratureRule` instances are supposed to be singletons. :class:`QuadratureRule` therefore implements instance caching in :func:`~mpmath.__new__`. """ def __init__(self, ctx): self.ctx = ctx self.standard_cache = {} self.transformed_cache = {} self.interval_count = {} def clear(self): """ Delete cached node data. """ self.standard_cache = {} self.transformed_cache = {} self.interval_count = {} def calc_nodes(self, degree, prec, verbose=False): r""" Compute nodes for the standard interval `[-1, 1]`. Subclasses should probably implement only this method, and use :func:`~mpmath.get_nodes` method to retrieve the nodes. """ raise NotImplementedError def get_nodes(self, a, b, degree, prec, verbose=False): """ Return nodes for given interval, degree and precision. The nodes are retrieved from a cache if already computed; otherwise they are computed by calling :func:`~mpmath.calc_nodes` and are then cached. Subclasses should probably not implement this method, but just implement :func:`~mpmath.calc_nodes` for the actual node computation. """ key = (a, b, degree, prec) if key in self.transformed_cache: return self.transformed_cache[key] orig = self.ctx.prec try: self.ctx.prec = prec+20 # Get nodes on standard interval if (degree, prec) in self.standard_cache: nodes = self.standard_cache[degree, prec] else: nodes = self.calc_nodes(degree, prec, verbose) self.standard_cache[degree, prec] = nodes # Transform to general interval nodes = self.transform_nodes(nodes, a, b, verbose) if key in self.interval_count: self.transformed_cache[key] = nodes else: self.interval_count[key] = True finally: self.ctx.prec = orig return nodes def transform_nodes(self, nodes, a, b, verbose=False): r""" Rescale standardized nodes (for `[-1, 1]`) to a general interval `[a, b]`. For a finite interval, a simple linear change of variables is used. Otherwise, the following transformations are used: .. math :: [a, \infty] : t = \frac{1}{x} + (a-1) [-\infty, b] : t = (b+1) - \frac{1}{x} [-\infty, \infty] : t = \frac{x}{\sqrt{1-x^2}} """ ctx = self.ctx a = ctx.convert(a) b = ctx.convert(b) one = ctx.one if (a, b) == (-one, one): return nodes half = ctx.mpf(0.5) new_nodes = [] if ctx.isinf(a) or ctx.isinf(b): if (a, b) == (ctx.ninf, ctx.inf): p05 = -half for x, w in nodes: x2 = x*x px1 = one-x2 spx1 = px1**p05 x = x*spx1 w *= spx1/px1 new_nodes.append((x, w)) elif a == ctx.ninf: b1 = b+1 for x, w in nodes: u = 2/(x+one) x = b1-u w *= half*u**2 new_nodes.append((x, w)) elif b == ctx.inf: a1 = a-1 for x, w in nodes: u = 2/(x+one) x = a1+u w *= half*u**2 new_nodes.append((x, w)) elif a == ctx.inf or b == ctx.ninf: return [(x,-w) for (x,w) in self.transform_nodes(nodes, b, a, verbose)] else: raise NotImplementedError else: # Simple linear change of variables C = (b-a)/2 D = (b+a)/2 for x, w in nodes: new_nodes.append((D+C*x, C*w)) return new_nodes def guess_degree(self, prec): """ Given a desired precision `p` in bits, estimate the degree `m` of the quadrature required to accomplish full accuracy for typical integrals. By default, :func:`~mpmath.quad` will perform up to `m` iterations. The value of `m` should be a slight overestimate, so that "slightly bad" integrals can be dealt with automatically using a few extra iterations. On the other hand, it should not be too big, so :func:`~mpmath.quad` can quit within a reasonable amount of time when it is given an "unsolvable" integral. The default formula used by :func:`~mpmath.guess_degree` is tuned for both :class:`TanhSinh` and :class:`GaussLegendre`. The output is roughly as follows: +---------+---------+ | `p` | `m` | +=========+=========+ | 50 | 6 | +---------+---------+ | 100 | 7 | +---------+---------+ | 500 | 10 | +---------+---------+ | 3000 | 12 | +---------+---------+ This formula is based purely on a limited amount of experimentation and will sometimes be wrong. """ # Expected degree # XXX: use mag g = int(4 + max(0, self.ctx.log(prec/30.0, 2))) # Reasonable "worst case" g += 2 return g def estimate_error(self, results, prec, epsilon): r""" Given results from integrations `[I_1, I_2, \ldots, I_k]` done with a quadrature of rule of degree `1, 2, \ldots, k`, estimate the error of `I_k`. For `k = 2`, we estimate `|I_{\infty}-I_2|` as `|I_2-I_1|`. For `k > 2`, we extrapolate `|I_{\infty}-I_k| \approx |I_{k+1}-I_k|` from `|I_k-I_{k-1}|` and `|I_k-I_{k-2}|` under the assumption that each degree increment roughly doubles the accuracy of the quadrature rule (this is true for both :class:`TanhSinh` and :class:`GaussLegendre`). The extrapolation formula is given by Borwein, Bailey & Girgensohn. Although not very conservative, this method seems to be very robust in practice. """ if len(results) == 2: return abs(results[0]-results[1]) try: if results[-1] == results[-2] == results[-3]: return self.ctx.zero D1 = self.ctx.log(abs(results[-1]-results[-2]), 10) D2 = self.ctx.log(abs(results[-1]-results[-3]), 10) except ValueError: return epsilon D3 = -prec D4 = min(0, max(D1**2/D2, 2*D1, D3)) return self.ctx.mpf(10) ** int(D4) def summation(self, f, points, prec, epsilon, max_degree, verbose=False): """ Main integration function. Computes the 1D integral over the interval specified by *points*. For each subinterval, performs quadrature of degree from 1 up to *max_degree* until :func:`~mpmath.estimate_error` signals convergence. :func:`~mpmath.summation` transforms each subintegration to the standard interval and then calls :func:`~mpmath.sum_next`. """ ctx = self.ctx I = err = ctx.zero for i in xrange(len(points)-1): a, b = points[i], points[i+1] if a == b: continue # XXX: we could use a single variable transformation, # but this is not good in practice. We get better accuracy # by having 0 as an endpoint. if (a, b) == (ctx.ninf, ctx.inf): _f = f f = lambda x: _f(-x) + _f(x) a, b = (ctx.zero, ctx.inf) results = [] for degree in xrange(1, max_degree+1): nodes = self.get_nodes(a, b, degree, prec, verbose) if verbose: print("Integrating from %s to %s (degree %s of %s)" % \ (ctx.nstr(a), ctx.nstr(b), degree, max_degree)) results.append(self.sum_next(f, nodes, degree, prec, results, verbose)) if degree > 1: err = self.estimate_error(results, prec, epsilon) if err <= epsilon: break if verbose: print("Estimated error:", ctx.nstr(err)) I += results[-1] if err > epsilon: if verbose: print("Failed to reach full accuracy. Estimated error:", ctx.nstr(err)) return I, err def sum_next(self, f, nodes, degree, prec, previous, verbose=False): r""" Evaluates the step sum `\sum w_k f(x_k)` where the *nodes* list contains the `(w_k, x_k)` pairs. :func:`~mpmath.summation` will supply the list *results* of values computed by :func:`~mpmath.sum_next` at previous degrees, in case the quadrature rule is able to reuse them. """ return self.ctx.fdot((w, f(x)) for (x,w) in nodes) class TanhSinh(QuadratureRule): r""" This class implements "tanh-sinh" or "doubly exponential" quadrature. This quadrature rule is based on the Euler-Maclaurin integral formula. By performing a change of variables involving nested exponentials / hyperbolic functions (hence the name), the derivatives at the endpoints vanish rapidly. Since the error term in the Euler-Maclaurin formula depends on the derivatives at the endpoints, a simple step sum becomes extremely accurate. In practice, this means that doubling the number of evaluation points roughly doubles the number of accurate digits. Comparison to Gauss-Legendre: * Initial computation of nodes is usually faster * Handles endpoint singularities better * Handles infinite integration intervals better * Is slower for smooth integrands once nodes have been computed The implementation of the tanh-sinh algorithm is based on the description given in Borwein, Bailey & Girgensohn, "Experimentation in Mathematics - Computational Paths to Discovery", A K Peters, 2003, pages 312-313. In the present implementation, a few improvements have been made: * A more efficient scheme is used to compute nodes (exploiting recurrence for the exponential function) * The nodes are computed successively instead of all at once Various documents describing the algorithm are available online, e.g.: * http://crd.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf * http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf """ def sum_next(self, f, nodes, degree, prec, previous, verbose=False): """ Step sum for tanh-sinh quadrature of degree `m`. We exploit the fact that half of the abscissas at degree `m` are precisely the abscissas from degree `m-1`. Thus reusing the result from the previous level allows a 2x speedup. """ h = self.ctx.mpf(2)**(-degree) # Abscissas overlap, so reusing saves half of the time if previous: S = previous[-1]/(h*2) else: S = self.ctx.zero S += self.ctx.fdot((w,f(x)) for (x,w) in nodes) return h*S def calc_nodes(self, degree, prec, verbose=False): r""" The abscissas and weights for tanh-sinh quadrature of degree `m` are given by .. math:: x_k = \tanh(\pi/2 \sinh(t_k)) w_k = \pi/2 \cosh(t_k) / \cosh(\pi/2 \sinh(t_k))^2 where `t_k = t_0 + hk` for a step length `h \sim 2^{-m}`. The list of nodes is actually infinite, but the weights die off so rapidly that only a few are needed. """ ctx = self.ctx nodes = [] extra = 20 ctx.prec += extra tol = ctx.ldexp(1, -prec-10) pi4 = ctx.pi/4 # For simplicity, we work in steps h = 1/2^n, with the first point # offset so that we can reuse the sum from the previous degree # We define degree 1 to include the "degree 0" steps, including # the point x = 0. (It doesn't work well otherwise; not sure why.) t0 = ctx.ldexp(1, -degree) if degree == 1: #nodes.append((mpf(0), pi4)) #nodes.append((-mpf(0), pi4)) nodes.append((ctx.zero, ctx.pi/2)) h = t0 else: h = t0*2 # Since h is fixed, we can compute the next exponential # by simply multiplying by exp(h) expt0 = ctx.exp(t0) a = pi4 * expt0 b = pi4 / expt0 udelta = ctx.exp(h) urdelta = 1/udelta for k in xrange(0, 20*2**degree+1): # Reference implementation: # t = t0 + k*h # x = tanh(pi/2 * sinh(t)) # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2 # Fast implementation. Note that c = exp(pi/2 * sinh(t)) c = ctx.exp(a-b) d = 1/c co = (c+d)/2 si = (c-d)/2 x = si / co w = (a+b) / co**2 diff = abs(x-1) if diff <= tol: break nodes.append((x, w)) nodes.append((-x, w)) a *= udelta b *= urdelta if verbose and k % 300 == 150: # Note: the number displayed is rather arbitrary. Should # figure out how to print something that looks more like a # percentage print("Calculating nodes:", ctx.nstr(-ctx.log(diff, 10) / prec)) ctx.prec -= extra return nodes class GaussLegendre(QuadratureRule): """ This class implements Gauss-Legendre quadrature, which is exceptionally efficient for polynomials and polynomial-like (i.e. very smooth) integrands. The abscissas and weights are given by roots and values of Legendre polynomials, which are the orthogonal polynomials on `[-1, 1]` with respect to the unit weight (see :func:`~mpmath.legendre`). In this implementation, we take the "degree" `m` of the quadrature to denote a Gauss-Legendre rule of degree `3 \cdot 2^m` (following Borwein, Bailey & Girgensohn). This way we get quadratic, rather than linear, convergence as the degree is incremented. Comparison to tanh-sinh quadrature: * Is faster for smooth integrands once nodes have been computed * Initial computation of nodes is usually slower * Handles endpoint singularities worse * Handles infinite integration intervals worse """ def calc_nodes(self, degree, prec, verbose=False): """ Calculates the abscissas and weights for Gauss-Legendre quadrature of degree of given degree (actually `3 \cdot 2^m`). """ ctx = self.ctx # It is important that the epsilon is set lower than the # "real" epsilon epsilon = ctx.ldexp(1, -prec-8) # Fairly high precision might be required for accurate # evaluation of the roots orig = ctx.prec ctx.prec = int(prec*1.5) if degree == 1: x = ctx.mpf(3)/5 w = ctx.mpf(5)/9 nodes = [(-x,w),(ctx.zero,ctx.mpf(8)/9),(x,w)] ctx.prec = orig return nodes nodes = [] n = 3*2**(degree-1) upto = n//2 + 1 for j in xrange(1, upto): # Asymptotic formula for the roots r = ctx.mpf(math.cos(math.pi*(j-0.25)/(n+0.5))) # Newton iteration while 1: t1, t2 = 1, 0 # Evaluates the Legendre polynomial using its defining # recurrence relation for j1 in xrange(1,n+1): t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1 t4 = n*(r*t1- t2)/(r**2-1) t5 = r a = t1/t4 r = r - a if abs(a) < epsilon: break x = r w = 2/((1-r**2)*t4**2) if verbose and j % 30 == 15: print("Computing nodes (%i of %i)" % (j, upto)) nodes.append((x, w)) nodes.append((-x, w)) ctx.prec = orig return nodes class QuadratureMethods: def __init__(ctx, *args, **kwargs): ctx._gauss_legendre = GaussLegendre(ctx) ctx._tanh_sinh = TanhSinh(ctx) def quad(ctx, f, *points, **kwargs): r""" Computes a single, double or triple integral over a given 1D interval, 2D rectangle, or 3D cuboid. A basic example:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> quad(sin, [0, pi]) 2.0 A basic 2D integral:: >>> f = lambda x, y: cos(x+y/2) >>> quad(f, [-pi/2, pi/2], [0, pi]) 4.0 **Interval format** The integration range for each dimension may be specified using a list or tuple. Arguments are interpreted as follows: ``quad(f, [x1, x2])`` -- calculates `\int_{x_1}^{x_2} f(x) \, dx` ``quad(f, [x1, x2], [y1, y2])`` -- calculates `\int_{x_1}^{x_2} \int_{y_1}^{y_2} f(x,y) \, dy \, dx` ``quad(f, [x1, x2], [y1, y2], [z1, z2])`` -- calculates `\int_{x_1}^{x_2} \int_{y_1}^{y_2} \int_{z_1}^{z_2} f(x,y,z) \, dz \, dy \, dx` Endpoints may be finite or infinite. An interval descriptor may also contain more than two points. In this case, the integration is split into subintervals, between each pair of consecutive points. This is useful for dealing with mid-interval discontinuities, or integrating over large intervals where the function is irregular or oscillates. **Options** :func:`~mpmath.quad` recognizes the following keyword arguments: *method* Chooses integration algorithm (described below). *error* If set to true, :func:`~mpmath.quad` returns `(v, e)` where `v` is the integral and `e` is the estimated error. *maxdegree* Maximum degree of the quadrature rule to try before quitting. *verbose* Print details about progress. **Algorithms** Mpmath presently implements two integration algorithms: tanh-sinh quadrature and Gauss-Legendre quadrature. These can be selected using *method='tanh-sinh'* or *method='gauss-legendre'* or by passing the classes *method=TanhSinh*, *method=GaussLegendre*. The functions :func:`~mpmath.quadts` and :func:`~mpmath.quadgl` are also available as shortcuts. Both algorithms have the property that doubling the number of evaluation points roughly doubles the accuracy, so both are ideal for high precision quadrature (hundreds or thousands of digits). At high precision, computing the nodes and weights for the integration can be expensive (more expensive than computing the function values). To make repeated integrations fast, nodes are automatically cached. The advantages of the tanh-sinh algorithm are that it tends to handle endpoint singularities well, and that the nodes are cheap to compute on the first run. For these reasons, it is used by :func:`~mpmath.quad` as the default algorithm. Gauss-Legendre quadrature often requires fewer function evaluations, and is therefore often faster for repeated use, but the algorithm does not handle endpoint singularities as well and the nodes are more expensive to compute. Gauss-Legendre quadrature can be a better choice if the integrand is smooth and repeated integrations are required (e.g. for multiple integrals). See the documentation for :class:`TanhSinh` and :class:`GaussLegendre` for additional details. **Examples of 1D integrals** Intervals may be infinite or half-infinite. The following two examples evaluate the limits of the inverse tangent function (`\int 1/(1+x^2) = \tan^{-1} x`), and the Gaussian integral `\int_{\infty}^{\infty} \exp(-x^2)\,dx = \sqrt{\pi}`:: >>> mp.dps = 15 >>> quad(lambda x: 2/(x**2+1), [0, inf]) 3.14159265358979 >>> quad(lambda x: exp(-x**2), [-inf, inf])**2 3.14159265358979 Integrals can typically be resolved to high precision. The following computes 50 digits of `\pi` by integrating the area of the half-circle defined by `x^2 + y^2 \le 1`, `-1 \le x \le 1`, `y \ge 0`:: >>> mp.dps = 50 >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) 3.1415926535897932384626433832795028841971693993751 One can just as well compute 1000 digits (output truncated):: >>> mp.dps = 1000 >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) #doctest:+ELLIPSIS 3.141592653589793238462643383279502884...216420198 Complex integrals are supported. The following computes a residue at `z = 0` by integrating counterclockwise along the diamond-shaped path from `1` to `+i` to `-1` to `-i` to `1`:: >>> mp.dps = 15 >>> chop(quad(lambda z: 1/z, [1,j,-1,-j,1])) (0.0 + 6.28318530717959j) **Examples of 2D and 3D integrals** Here are several nice examples of analytically solvable 2D integrals (taken from MathWorld [1]) that can be evaluated to high precision fairly rapidly by :func:`~mpmath.quad`:: >>> mp.dps = 30 >>> f = lambda x, y: (x-1)/((1-x*y)*log(x*y)) >>> quad(f, [0, 1], [0, 1]) 0.577215664901532860606512090082 >>> +euler 0.577215664901532860606512090082 >>> f = lambda x, y: 1/sqrt(1+x**2+y**2) >>> quad(f, [-1, 1], [-1, 1]) 3.17343648530607134219175646705 >>> 4*log(2+sqrt(3))-2*pi/3 3.17343648530607134219175646705 >>> f = lambda x, y: 1/(1-x**2 * y**2) >>> quad(f, [0, 1], [0, 1]) 1.23370055013616982735431137498 >>> pi**2 / 8 1.23370055013616982735431137498 >>> quad(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]) 1.64493406684822643647241516665 >>> pi**2 / 6 1.64493406684822643647241516665 Multiple integrals may be done over infinite ranges:: >>> mp.dps = 15 >>> print(quad(lambda x,y: exp(-x-y), [0, inf], [1, inf])) 0.367879441171442 >>> print(1/e) 0.367879441171442 For nonrectangular areas, one can call :func:`~mpmath.quad` recursively. For example, we can replicate the earlier example of calculating `\pi` by integrating over the unit-circle, and actually use double quadrature to actually measure the area circle:: >>> f = lambda x: quad(lambda y: 1, [-sqrt(1-x**2), sqrt(1-x**2)]) >>> quad(f, [-1, 1]) 3.14159265358979 Here is a simple triple integral:: >>> mp.dps = 15 >>> f = lambda x,y,z: x*y/(1+z) >>> quad(f, [0,1], [0,1], [1,2], method='gauss-legendre') 0.101366277027041 >>> (log(3)-log(2))/4 0.101366277027041 **Singularities** Both tanh-sinh and Gauss-Legendre quadrature are designed to integrate smooth (infinitely differentiable) functions. Neither algorithm copes well with mid-interval singularities (such as mid-interval discontinuities in `f(x)` or `f'(x)`). The best solution is to split the integral into parts:: >>> mp.dps = 15 >>> quad(lambda x: abs(sin(x)), [0, 2*pi]) # Bad 3.99900894176779 >>> quad(lambda x: abs(sin(x)), [0, pi, 2*pi]) # Good 4.0 The tanh-sinh rule often works well for integrands having a singularity at one or both endpoints:: >>> mp.dps = 15 >>> quad(log, [0, 1], method='tanh-sinh') # Good -1.0 >>> quad(log, [0, 1], method='gauss-legendre') # Bad -0.999932197413801 However, the result may still be inaccurate for some functions:: >>> quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') 1.99999999946942 This problem is not due to the quadrature rule per se, but to numerical amplification of errors in the nodes. The problem can be circumvented by temporarily increasing the precision:: >>> mp.dps = 30 >>> a = quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') >>> mp.dps = 15 >>> +a 2.0 **Highly variable functions** For functions that are smooth (in the sense of being infinitely differentiable) but contain sharp mid-interval peaks or many "bumps", :func:`~mpmath.quad` may fail to provide full accuracy. For example, with default settings, :func:`~mpmath.quad` is able to integrate `\sin(x)` accurately over an interval of length 100 but not over length 1000:: >>> quad(sin, [0, 100]); 1-cos(100) # Good 0.137681127712316 0.137681127712316 >>> quad(sin, [0, 1000]); 1-cos(1000) # Bad -37.8587612408485 0.437620923709297 One solution is to break the integration into 10 intervals of length 100:: >>> quad(sin, linspace(0, 1000, 10)) # Good 0.437620923709297 Another is to increase the degree of the quadrature:: >>> quad(sin, [0, 1000], maxdegree=10) # Also good 0.437620923709297 Whether splitting the interval or increasing the degree is more efficient differs from case to case. Another example is the function `1/(1+x^2)`, which has a sharp peak centered around `x = 0`:: >>> f = lambda x: 1/(1+x**2) >>> quad(f, [-100, 100]) # Bad 3.64804647105268 >>> quad(f, [-100, 100], maxdegree=10) # Good 3.12159332021646 >>> quad(f, [-100, 0, 100]) # Also good 3.12159332021646 **References** 1. http://mathworld.wolfram.com/DoubleIntegral.html """ rule = kwargs.get('method', 'tanh-sinh') if type(rule) is str: if rule == 'tanh-sinh': rule = ctx._tanh_sinh elif rule == 'gauss-legendre': rule = ctx._gauss_legendre else: raise ValueError("unknown quadrature rule: %s" % rule) else: rule = rule(ctx) verbose = kwargs.get('verbose') dim = len(points) orig = prec = ctx.prec epsilon = ctx.eps/8 m = kwargs.get('maxdegree') or rule.guess_degree(prec) points = [ctx._as_points(p) for p in points] try: ctx.prec += 20 if dim == 1: v, err = rule.summation(f, points[0], prec, epsilon, m, verbose) elif dim == 2: v, err = rule.summation(lambda x: \ rule.summation(lambda y: f(x,y), \ points[1], prec, epsilon, m)[0], points[0], prec, epsilon, m, verbose) elif dim == 3: v, err = rule.summation(lambda x: \ rule.summation(lambda y: \ rule.summation(lambda z: f(x,y,z), \ points[2], prec, epsilon, m)[0], points[1], prec, epsilon, m)[0], points[0], prec, epsilon, m, verbose) else: raise NotImplementedError("quadrature must have dim 1, 2 or 3") finally: ctx.prec = orig if kwargs.get("error"): return +v, err return +v def quadts(ctx, *args, **kwargs): """ Performs tanh-sinh quadrature. The call quadts(func, *points, ...) is simply a shortcut for: quad(func, *points, ..., method=TanhSinh) For example, a single integral and a double integral: quadts(lambda x: exp(cos(x)), [0, 1]) quadts(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) See the documentation for quad for information about how points arguments and keyword arguments are parsed. See documentation for TanhSinh for algorithmic information about tanh-sinh quadrature. """ kwargs['method'] = 'tanh-sinh' return ctx.quad(*args, **kwargs) def quadgl(ctx, *args, **kwargs): """ Performs Gauss-Legendre quadrature. The call quadgl(func, *points, ...) is simply a shortcut for: quad(func, *points, ..., method=GaussLegendre) For example, a single integral and a double integral: quadgl(lambda x: exp(cos(x)), [0, 1]) quadgl(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) See the documentation for quad for information about how points arguments and keyword arguments are parsed. See documentation for TanhSinh for algorithmic information about tanh-sinh quadrature. """ kwargs['method'] = 'gauss-legendre' return ctx.quad(*args, **kwargs) def quadosc(ctx, f, interval, omega=None, period=None, zeros=None): r""" Calculates .. math :: I = \int_a^b f(x) dx where at least one of `a` and `b` is infinite and where `f(x) = g(x) \cos(\omega x + \phi)` for some slowly decreasing function `g(x)`. With proper input, :func:`~mpmath.quadosc` can also handle oscillatory integrals where the oscillation rate is different from a pure sine or cosine wave. In the standard case when `|a| < \infty, b = \infty`, :func:`~mpmath.quadosc` works by evaluating the infinite series .. math :: I = \int_a^{x_1} f(x) dx + \sum_{k=1}^{\infty} \int_{x_k}^{x_{k+1}} f(x) dx where `x_k` are consecutive zeros (alternatively some other periodic reference point) of `f(x)`. Accordingly, :func:`~mpmath.quadosc` requires information about the zeros of `f(x)`. For a periodic function, you can specify the zeros by either providing the angular frequency `\omega` (*omega*) or the *period* `2 \pi/\omega`. In general, you can specify the `n`-th zero by providing the *zeros* arguments. Below is an example of each:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = lambda x: sin(3*x)/(x**2+1) >>> quadosc(f, [0,inf], omega=3) 0.37833007080198 >>> quadosc(f, [0,inf], period=2*pi/3) 0.37833007080198 >>> quadosc(f, [0,inf], zeros=lambda n: pi*n/3) 0.37833007080198 >>> (ei(3)*exp(-3)-exp(3)*ei(-3))/2 # Computed by Mathematica 0.37833007080198 Note that *zeros* was specified to multiply `n` by the *half-period*, not the full period. In theory, it does not matter whether each partial integral is done over a half period or a full period. However, if done over half-periods, the infinite series passed to :func:`~mpmath.nsum` becomes an *alternating series* and this typically makes the extrapolation much more efficient. Here is an example of an integration over the entire real line, and a half-infinite integration starting at `-\infty`:: >>> quadosc(lambda x: cos(x)/(1+x**2), [-inf, inf], omega=1) 1.15572734979092 >>> pi/e 1.15572734979092 >>> quadosc(lambda x: cos(x)/x**2, [-inf, -1], period=2*pi) -0.0844109505595739 >>> cos(1)+si(1)-pi/2 -0.0844109505595738 Of course, the integrand may contain a complex exponential just as well as a real sine or cosine:: >>> quadosc(lambda x: exp(3*j*x)/(1+x**2), [-inf,inf], omega=3) (0.156410688228254 + 0.0j) >>> pi/e**3 0.156410688228254 >>> quadosc(lambda x: exp(3*j*x)/(2+x+x**2), [-inf,inf], omega=3) (0.00317486988463794 - 0.0447701735209082j) >>> 2*pi/sqrt(7)/exp(3*(j+sqrt(7))/2) (0.00317486988463794 - 0.0447701735209082j) **Non-periodic functions** If `f(x) = g(x) h(x)` for some function `h(x)` that is not strictly periodic, *omega* or *period* might not work, and it might be necessary to use *zeros*. A notable exception can be made for Bessel functions which, though not periodic, are "asymptotically periodic" in a sufficiently strong sense that the sum extrapolation will work out:: >>> quadosc(j0, [0, inf], period=2*pi) 1.0 >>> quadosc(j1, [0, inf], period=2*pi) 1.0 More properly, one should provide the exact Bessel function zeros:: >>> j0zero = lambda n: findroot(j0, pi*(n-0.25)) >>> quadosc(j0, [0, inf], zeros=j0zero) 1.0 For an example where *zeros* becomes necessary, consider the complete Fresnel integrals .. math :: \int_0^{\infty} \cos x^2\,dx = \int_0^{\infty} \sin x^2\,dx = \sqrt{\frac{\pi}{8}}. Although the integrands do not decrease in magnitude as `x \to \infty`, the integrals are convergent since the oscillation rate increases (causing consecutive periods to asymptotically cancel out). These integrals are virtually impossible to calculate to any kind of accuracy using standard quadrature rules. However, if one provides the correct asymptotic distribution of zeros (`x_n \sim \sqrt{n}`), :func:`~mpmath.quadosc` works:: >>> mp.dps = 30 >>> f = lambda x: cos(x**2) >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) 0.626657068657750125603941321203 >>> f = lambda x: sin(x**2) >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) 0.626657068657750125603941321203 >>> sqrt(pi/8) 0.626657068657750125603941321203 (Interestingly, these integrals can still be evaluated if one places some other constant than `\pi` in the square root sign.) In general, if `f(x) \sim g(x) \cos(h(x))`, the zeros follow the inverse-function distribution `h^{-1}(x)`:: >>> mp.dps = 15 >>> f = lambda x: sin(exp(x)) >>> quadosc(f, [1,inf], zeros=lambda n: log(n)) -0.25024394235267 >>> pi/2-si(e) -0.250243942352671 **Non-alternating functions** If the integrand oscillates around a positive value, without alternating signs, the extrapolation might fail. A simple trick that sometimes works is to multiply or divide the frequency by 2:: >>> f = lambda x: 1/x**2+sin(x)/x**4 >>> quadosc(f, [1,inf], omega=1) # Bad 1.28642190869861 >>> quadosc(f, [1,inf], omega=0.5) # Perfect 1.28652953559617 >>> 1+(cos(1)+ci(1)+sin(1))/6 1.28652953559617 **Fast decay** :func:`~mpmath.quadosc` is primarily useful for slowly decaying integrands. If the integrand decreases exponentially or faster, :func:`~mpmath.quad` will likely handle it without trouble (and generally be much faster than :func:`~mpmath.quadosc`):: >>> quadosc(lambda x: cos(x)/exp(x), [0, inf], omega=1) 0.5 >>> quad(lambda x: cos(x)/exp(x), [0, inf]) 0.5 """ a, b = ctx._as_points(interval) a = ctx.convert(a) b = ctx.convert(b) if [omega, period, zeros].count(None) != 2: raise ValueError( \ "must specify exactly one of omega, period, zeros") if a == ctx.ninf and b == ctx.inf: s1 = ctx.quadosc(f, [a, 0], omega=omega, zeros=zeros, period=period) s2 = ctx.quadosc(f, [0, b], omega=omega, zeros=zeros, period=period) return s1 + s2 if a == ctx.ninf: if zeros: return ctx.quadosc(lambda x:f(-x), [-b,-a], lambda n: zeros(-n)) else: return ctx.quadosc(lambda x:f(-x), [-b,-a], omega=omega, period=period) if b != ctx.inf: raise ValueError("quadosc requires an infinite integration interval") if not zeros: if omega: period = 2*ctx.pi/omega zeros = lambda n: n*period/2 #for n in range(1,10): # p = zeros(n) # if p > a: # break #if n >= 9: # raise ValueError("zeros do not appear to be correctly indexed") n = 1 s = ctx.quadgl(f, [a, zeros(n)]) def term(k): return ctx.quadgl(f, [zeros(k), zeros(k+1)]) s += ctx.nsum(term, [n, ctx.inf]) return s if __name__ == '__main__': import doctest doctest.testmod() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/approximation.py0000644000175000017500000002115512014170666027450 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .calculus import defun #----------------------------------------------------------------------------# # Approximation methods # #----------------------------------------------------------------------------# # The Chebyshev approximation formula is given at: # http://mathworld.wolfram.com/ChebyshevApproximationFormula.html # The only major changes in the following code is that we return the # expanded polynomial coefficients instead of Chebyshev coefficients, # and that we automatically transform [a,b] -> [-1,1] and back # for convenience. # Coefficient in Chebyshev approximation def chebcoeff(ctx,f,a,b,j,N): s = ctx.mpf(0) h = ctx.mpf(0.5) for k in range(1, N+1): t = ctx.cospi((k-h)/N) s += f(t*(b-a)*h + (b+a)*h) * ctx.cospi(j*(k-h)/N) return 2*s/N # Generate Chebyshev polynomials T_n(ax+b) in expanded form def chebT(ctx, a=1, b=0): Tb = [1] yield Tb Ta = [b, a] while 1: yield Ta # Recurrence: T[n+1](ax+b) = 2*(ax+b)*T[n](ax+b) - T[n-1](ax+b) Tmp = [0] + [2*a*t for t in Ta] for i, c in enumerate(Ta): Tmp[i] += 2*b*c for i, c in enumerate(Tb): Tmp[i] -= c Ta, Tb = Tmp, Ta @defun def chebyfit(ctx, f, interval, N, error=False): r""" Computes a polynomial of degree `N-1` that approximates the given function `f` on the interval `[a, b]`. With ``error=True``, :func:`~mpmath.chebyfit` also returns an accurate estimate of the maximum absolute error; that is, the maximum value of `|f(x) - P(x)|` for `x \in [a, b]`. :func:`~mpmath.chebyfit` uses the Chebyshev approximation formula, which gives a nearly optimal solution: that is, the maximum error of the approximating polynomial is very close to the smallest possible for any polynomial of the same degree. Chebyshev approximation is very useful if one needs repeated evaluation of an expensive function, such as function defined implicitly by an integral or a differential equation. (For example, it could be used to turn a slow mpmath function into a fast machine-precision version of the same.) **Examples** Here we use :func:`~mpmath.chebyfit` to generate a low-degree approximation of `f(x) = \cos(x)`, valid on the interval `[1, 2]`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> poly, err = chebyfit(cos, [1, 2], 5, error=True) >>> nprint(poly) [0.00291682, 0.146166, -0.732491, 0.174141, 0.949553] >>> nprint(err, 12) 1.61351758081e-5 The polynomial can be evaluated using ``polyval``:: >>> nprint(polyval(poly, 1.6), 12) -0.0291858904138 >>> nprint(cos(1.6), 12) -0.0291995223013 Sampling the true error at 1000 points shows that the error estimate generated by ``chebyfit`` is remarkably good:: >>> error = lambda x: abs(cos(x) - polyval(poly, x)) >>> nprint(max([error(1+n/1000.) for n in range(1000)]), 12) 1.61349954245e-5 **Choice of degree** The degree `N` can be set arbitrarily high, to obtain an arbitrarily good approximation. As a rule of thumb, an `N`-term Chebyshev approximation is good to `N/(b-a)` decimal places on a unit interval (although this depends on how well-behaved `f` is). The cost grows accordingly: ``chebyfit`` evaluates the function `(N^2)/2` times to compute the coefficients and an additional `N` times to estimate the error. **Possible issues** One should be careful to use a sufficiently high working precision both when calling ``chebyfit`` and when evaluating the resulting polynomial, as the polynomial is sometimes ill-conditioned. It is for example difficult to reach 15-digit accuracy when evaluating the polynomial using machine precision floats, no matter the theoretical accuracy of the polynomial. (The option to return the coefficients in Chebyshev form should be made available in the future.) It is important to note the Chebyshev approximation works poorly if `f` is not smooth. A function containing singularities, rapid oscillation, etc can be approximated more effectively by multiplying it by a weight function that cancels out the nonsmooth features, or by dividing the interval into several segments. """ a, b = ctx._as_points(interval) orig = ctx.prec try: ctx.prec = orig + int(N**0.5) + 20 c = [chebcoeff(ctx,f,a,b,k,N) for k in range(N)] d = [ctx.zero] * N d[0] = -c[0]/2 h = ctx.mpf(0.5) T = chebT(ctx, ctx.mpf(2)/(b-a), ctx.mpf(-1)*(b+a)/(b-a)) for (k, Tk) in zip(range(N), T): for i in range(len(Tk)): d[i] += c[k]*Tk[i] d = d[::-1] # Estimate maximum error err = ctx.zero for k in range(N): x = ctx.cos(ctx.pi*k/N) * (b-a)*h + (b+a)*h err = max(err, abs(f(x) - ctx.polyval(d, x))) finally: ctx.prec = orig if error: return d, +err else: return d @defun def fourier(ctx, f, interval, N): r""" Computes the Fourier series of degree `N` of the given function on the interval `[a, b]`. More precisely, :func:`~mpmath.fourier` returns two lists `(c, s)` of coefficients (the cosine series and sine series, respectively), such that .. math :: f(x) \sim \sum_{k=0}^N c_k \cos(k m) + s_k \sin(k m) where `m = 2 \pi / (b-a)`. Note that many texts define the first coefficient as `2 c_0` instead of `c_0`. The easiest way to evaluate the computed series correctly is to pass it to :func:`~mpmath.fourierval`. **Examples** The function `f(x) = x` has a simple Fourier series on the standard interval `[-\pi, \pi]`. The cosine coefficients are all zero (because the function has odd symmetry), and the sine coefficients are rational numbers:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> c, s = fourier(lambda x: x, [-pi, pi], 5) >>> nprint(c) [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] >>> nprint(s) [0.0, 2.0, -1.0, 0.666667, -0.5, 0.4] This computes a Fourier series of a nonsymmetric function on a nonstandard interval:: >>> I = [-1, 1.5] >>> f = lambda x: x**2 - 4*x + 1 >>> cs = fourier(f, I, 4) >>> nprint(cs[0]) [0.583333, 1.12479, -1.27552, 0.904708, -0.441296] >>> nprint(cs[1]) [0.0, -2.6255, 0.580905, 0.219974, -0.540057] It is instructive to plot a function along with its truncated Fourier series:: >>> plot([f, lambda x: fourierval(cs, I, x)], I) #doctest: +SKIP Fourier series generally converge slowly (and may not converge pointwise). For example, if `f(x) = \cosh(x)`, a 10-term Fourier series gives an `L^2` error corresponding to 2-digit accuracy:: >>> I = [-1, 1] >>> cs = fourier(cosh, I, 9) >>> g = lambda x: (cosh(x) - fourierval(cs, I, x))**2 >>> nprint(sqrt(quad(g, I))) 0.00467963 :func:`~mpmath.fourier` uses numerical quadrature. For nonsmooth functions, the accuracy (and speed) can be improved by including all singular points in the interval specification:: >>> nprint(fourier(abs, [-1, 1], 0), 10) ([0.5000441648], [0.0]) >>> nprint(fourier(abs, [-1, 0, 1], 0), 10) ([0.5], [0.0]) """ interval = ctx._as_points(interval) a = interval[0] b = interval[-1] L = b-a cos_series = [] sin_series = [] cutoff = ctx.eps*10 for n in xrange(N+1): m = 2*n*ctx.pi/L an = 2*ctx.quadgl(lambda t: f(t)*ctx.cos(m*t), interval)/L bn = 2*ctx.quadgl(lambda t: f(t)*ctx.sin(m*t), interval)/L if n == 0: an /= 2 if abs(an) < cutoff: an = ctx.zero if abs(bn) < cutoff: bn = ctx.zero cos_series.append(an) sin_series.append(bn) return cos_series, sin_series @defun def fourierval(ctx, series, interval, x): """ Evaluates a Fourier series (in the format computed by by :func:`~mpmath.fourier` for the given interval) at the point `x`. The series should be a pair `(c, s)` where `c` is the cosine series and `s` is the sine series. The two lists need not have the same length. """ cs, ss = series ab = ctx._as_points(interval) a = interval[0] b = interval[-1] m = 2*ctx.pi/(ab[-1]-ab[0]) s = ctx.zero s += ctx.fsum(cs[n]*ctx.cos(m*n*x) for n in xrange(len(cs)) if cs[n]) s += ctx.fsum(ss[n]*ctx.sin(m*n*x) for n in xrange(len(ss)) if ss[n]) return s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/calculus.py0000644000175000017500000000014312014170666026363 0ustar georgeskgeorgeskclass CalculusMethods(object): pass def defun(f): setattr(CalculusMethods, f.__name__, f) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/optimization.py0000644000175000017500000007661412014170666027316 0ustar georgeskgeorgeskfrom copy import copy from ..libmp.backend import xrange class OptimizationMethods(object): def __init__(ctx): pass ############## # 1D-SOLVERS # ############## class Newton: """ 1d-solver generating pairs of approximative root and error. Needs starting points x0 close to the root. Pro: * converges fast * sometimes more robust than secant with bad second starting point Contra: * converges slowly for multiple roots * needs first derivative * 2 function evaluations per iteration """ maxsteps = 20 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if len(x0) == 1: self.x0 = x0[0] else: raise ValueError('expected 1 starting point, got %i' % len(x0)) self.f = f if not 'df' in kwargs: def df(x): return self.ctx.diff(f, x) else: df = kwargs['df'] self.df = df def __iter__(self): f = self.f df = self.df x0 = self.x0 while True: x1 = x0 - f(x0) / df(x0) error = abs(x1 - x0) x0 = x1 yield (x1, error) class Secant: """ 1d-solver generating pairs of approximative root and error. Needs starting points x0 and x1 close to the root. x1 defaults to x0 + 0.25. Pro: * converges fast Contra: * converges slowly for multiple roots """ maxsteps = 30 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if len(x0) == 1: self.x0 = x0[0] self.x1 = self.x0 + 0.25 elif len(x0) == 2: self.x0 = x0[0] self.x1 = x0[1] else: raise ValueError('expected 1 or 2 starting points, got %i' % len(x0)) self.f = f def __iter__(self): f = self.f x0 = self.x0 x1 = self.x1 f0 = f(x0) while True: f1 = f(x1) l = x1 - x0 if not l: break s = (f1 - f0) / l if not s: break x0, x1 = x1, x1 - f1/s f0 = f1 yield x1, abs(l) class MNewton: """ 1d-solver generating pairs of approximative root and error. Needs starting point x0 close to the root. Uses modified Newton's method that converges fast regardless of the multiplicity of the root. Pro: * converges fast for multiple roots Contra: * needs first and second derivative of f * 3 function evaluations per iteration """ maxsteps = 20 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if not len(x0) == 1: raise ValueError('expected 1 starting point, got %i' % len(x0)) self.x0 = x0[0] self.f = f if not 'df' in kwargs: def df(x): return self.ctx.diff(f, x) else: df = kwargs['df'] self.df = df if not 'd2f' in kwargs: def d2f(x): return self.ctx.diff(df, x) else: d2f = kwargs['df'] self.d2f = d2f def __iter__(self): x = self.x0 f = self.f df = self.df d2f = self.d2f while True: prevx = x fx = f(x) if fx == 0: break dfx = df(x) d2fx = d2f(x) # x = x - F(x)/F'(x) with F(x) = f(x)/f'(x) x -= fx / (dfx - fx * d2fx / dfx) error = abs(x - prevx) yield x, error class Halley: """ 1d-solver generating pairs of approximative root and error. Needs a starting point x0 close to the root. Uses Halley's method with cubic convergence rate. Pro: * converges even faster the Newton's method * useful when computing with *many* digits Contra: * needs first and second derivative of f * 3 function evaluations per iteration * converges slowly for multiple roots """ maxsteps = 20 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if not len(x0) == 1: raise ValueError('expected 1 starting point, got %i' % len(x0)) self.x0 = x0[0] self.f = f if not 'df' in kwargs: def df(x): return self.ctx.diff(f, x) else: df = kwargs['df'] self.df = df if not 'd2f' in kwargs: def d2f(x): return self.ctx.diff(df, x) else: d2f = kwargs['df'] self.d2f = d2f def __iter__(self): x = self.x0 f = self.f df = self.df d2f = self.d2f while True: prevx = x fx = f(x) dfx = df(x) d2fx = d2f(x) x -= 2*fx*dfx / (2*dfx**2 - fx*d2fx) error = abs(x - prevx) yield x, error class Muller: """ 1d-solver generating pairs of approximative root and error. Needs starting points x0, x1 and x2 close to the root. x1 defaults to x0 + 0.25; x2 to x1 + 0.25. Uses Muller's method that converges towards complex roots. Pro: * converges fast (somewhat faster than secant) * can find complex roots Contra: * converges slowly for multiple roots * may have complex values for real starting points and real roots http://en.wikipedia.org/wiki/Muller's_method """ maxsteps = 30 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if len(x0) == 1: self.x0 = x0[0] self.x1 = self.x0 + 0.25 self.x2 = self.x1 + 0.25 elif len(x0) == 2: self.x0 = x0[0] self.x1 = x0[1] self.x2 = self.x1 + 0.25 elif len(x0) == 3: self.x0 = x0[0] self.x1 = x0[1] self.x2 = x0[2] else: raise ValueError('expected 1, 2 or 3 starting points, got %i' % len(x0)) self.f = f self.verbose = kwargs['verbose'] def __iter__(self): f = self.f x0 = self.x0 x1 = self.x1 x2 = self.x2 fx0 = f(x0) fx1 = f(x1) fx2 = f(x2) while True: # TODO: maybe refactoring with function for divided differences # calculate divided differences fx2x1 = (fx1 - fx2) / (x1 - x2) fx2x0 = (fx0 - fx2) / (x0 - x2) fx1x0 = (fx0 - fx1) / (x0 - x1) w = fx2x1 + fx2x0 - fx1x0 fx2x1x0 = (fx1x0 - fx2x1) / (x0 - x2) if w == 0 and fx2x1x0 == 0: if self.verbose: print('canceled with') print('x0 =', x0, ', x1 =', x1, 'and x2 =', x2) break x0 = x1 fx0 = fx1 x1 = x2 fx1 = fx2 # denominator should be as large as possible => choose sign r = self.ctx.sqrt(w**2 - 4*fx2*fx2x1x0) if abs(w - r) > abs(w + r): r = -r x2 -= 2*fx2 / (w + r) fx2 = f(x2) error = abs(x2 - x1) yield x2, error # TODO: consider raising a ValueError when there's no sign change in a and b class Bisection: """ 1d-solver generating pairs of approximative root and error. Uses bisection method to find a root of f in [a, b]. Might fail for multiple roots (needs sign change). Pro: * robust and reliable Contra: * converges slowly * needs sign change """ maxsteps = 100 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if len(x0) != 2: raise ValueError('expected interval of 2 points, got %i' % len(x0)) self.f = f self.a = x0[0] self.b = x0[1] def __iter__(self): f = self.f a = self.a b = self.b l = b - a fb = f(b) while True: m = self.ctx.ldexp(a + b, -1) fm = f(m) if fm * fb < 0: a = m else: b = m fb = fm l /= 2 yield (a + b)/2, abs(l) def _getm(method): """ Return a function to calculate m for Illinois-like methods. """ if method == 'illinois': def getm(fz, fb): return 0.5 elif method == 'pegasus': def getm(fz, fb): return fb/(fb + fz) elif method == 'anderson': def getm(fz, fb): m = 1 - fz/fb if m > 0: return m else: return 0.5 else: raise ValueError("method '%s' not recognized" % method) return getm class Illinois: """ 1d-solver generating pairs of approximative root and error. Uses Illinois method or similar to find a root of f in [a, b]. Might fail for multiple roots (needs sign change). Combines bisect with secant (improved regula falsi). The only difference between the methods is the scaling factor m, which is used to ensure convergence (you can choose one using the 'method' keyword): Illinois method ('illinois'): m = 0.5 Pegasus method ('pegasus'): m = fb/(fb + fz) Anderson-Bjoerk method ('anderson'): m = 1 - fz/fb if positive else 0.5 Pro: * converges very fast Contra: * has problems with multiple roots * needs sign change """ maxsteps = 30 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if len(x0) != 2: raise ValueError('expected interval of 2 points, got %i' % len(x0)) self.a = x0[0] self.b = x0[1] self.f = f self.tol = kwargs['tol'] self.verbose = kwargs['verbose'] self.method = kwargs.get('method', 'illinois') self.getm = _getm(self.method) if self.verbose: print('using %s method' % self.method) def __iter__(self): method = self.method f = self.f a = self.a b = self.b fa = f(a) fb = f(b) m = None while True: l = b - a if l == 0: break s = (fb - fa) / l z = a - fa/s fz = f(z) if abs(fz) < self.tol: # TODO: better condition (when f is very flat) if self.verbose: print('canceled with z =', z) yield z, l break if fz * fb < 0: # root in [z, b] a = b fa = fb b = z fb = fz else: # root in [a, z] m = self.getm(fz, fb) b = z fb = fz fa = m*fa # scale down to ensure convergence if self.verbose and m and not method == 'illinois': print('m:', m) yield (a + b)/2, abs(l) def Pegasus(*args, **kwargs): """ 1d-solver generating pairs of approximative root and error. Uses Pegasus method to find a root of f in [a, b]. Wrapper for illinois to use method='pegasus'. """ kwargs['method'] = 'pegasus' return Illinois(*args, **kwargs) def Anderson(*args, **kwargs): """ 1d-solver generating pairs of approximative root and error. Uses Anderson-Bjoerk method to find a root of f in [a, b]. Wrapper for illinois to use method='pegasus'. """ kwargs['method'] = 'anderson' return Illinois(*args, **kwargs) # TODO: check whether it's possible to combine it with Illinois stuff class Ridder: """ 1d-solver generating pairs of approximative root and error. Ridders' method to find a root of f in [a, b]. Is told to perform as well as Brent's method while being simpler. Pro: * very fast * simpler than Brent's method Contra: * two function evaluations per step * has problems with multiple roots * needs sign change http://en.wikipedia.org/wiki/Ridders'_method """ maxsteps = 30 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx self.f = f if len(x0) != 2: raise ValueError('expected interval of 2 points, got %i' % len(x0)) self.x1 = x0[0] self.x2 = x0[1] self.verbose = kwargs['verbose'] self.tol = kwargs['tol'] def __iter__(self): ctx = self.ctx f = self.f x1 = self.x1 fx1 = f(x1) x2 = self.x2 fx2 = f(x2) while True: x3 = 0.5*(x1 + x2) fx3 = f(x3) x4 = x3 + (x3 - x1) * ctx.sign(fx1 - fx2) * fx3 / ctx.sqrt(fx3**2 - fx1*fx2) fx4 = f(x4) if abs(fx4) < self.tol: # TODO: better condition (when f is very flat) if self.verbose: print('canceled with f(x4) =', fx4) yield x4, abs(x1 - x2) break if fx4 * fx2 < 0: # root in [x4, x2] x1 = x4 fx1 = fx4 else: # root in [x1, x4] x2 = x4 fx2 = fx4 error = abs(x1 - x2) yield (x1 + x2)/2, error class ANewton: """ EXPERIMENTAL 1d-solver generating pairs of approximative root and error. Uses Newton's method modified to use Steffensens method when convergence is slow. (I.e. for multiple roots.) """ maxsteps = 20 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx if not len(x0) == 1: raise ValueError('expected 1 starting point, got %i' % len(x0)) self.x0 = x0[0] self.f = f if not 'df' in kwargs: def df(x): return self.ctx.diff(f, x) else: df = kwargs['df'] self.df = df def phi(x): return x - f(x) / df(x) self.phi = phi self.verbose = kwargs['verbose'] def __iter__(self): x0 = self.x0 f = self.f df = self.df phi = self.phi error = 0 counter = 0 while True: prevx = x0 try: x0 = phi(x0) except ZeroDivisionError: if self.verbose: 'ZeroDivisionError: canceled with x =', x0 break preverror = error error = abs(prevx - x0) # TODO: decide not to use convergence acceleration if error and abs(error - preverror) / error < 1: if self.verbose: print('converging slowly') counter += 1 if counter >= 3: # accelerate convergence phi = steffensen(phi) counter = 0 if self.verbose: print('accelerating convergence') yield x0, error # TODO: add Brent ############################ # MULTIDIMENSIONAL SOLVERS # ############################ def jacobian(ctx, f, x): """ Calculate the Jacobian matrix of a function at the point x0. This is the first derivative of a vectorial function: f : R^m -> R^n with m >= n """ x = ctx.matrix(x) h = ctx.sqrt(ctx.eps) fx = ctx.matrix(f(*x)) m = len(fx) n = len(x) J = ctx.matrix(m, n) for j in xrange(n): xj = x.copy() xj[j] += h Jj = (ctx.matrix(f(*xj)) - fx) / h for i in xrange(m): J[i,j] = Jj[i] return J # TODO: test with user-specified jacobian matrix, support force_type class MDNewton: """ Find the root of a vector function numerically using Newton's method. f is a vector function representing a nonlinear equation system. x0 is the starting point close to the root. J is a function returning the Jacobian matrix for a point. Supports overdetermined systems. Use the 'norm' keyword to specify which norm to use. Defaults to max-norm. The function to calculate the Jacobian matrix can be given using the keyword 'J'. Otherwise it will be calculated numerically. Please note that this method converges only locally. Especially for high- dimensional systems it is not trivial to find a good starting point being close enough to the root. It is recommended to use a faster, low-precision solver from SciPy [1] or OpenOpt [2] to get an initial guess. Afterwards you can use this method for root-polishing to any precision. [1] http://scipy.org [2] http://openopt.org """ maxsteps = 10 def __init__(self, ctx, f, x0, **kwargs): self.ctx = ctx self.f = f if isinstance(x0, (tuple, list)): x0 = ctx.matrix(x0) assert x0.cols == 1, 'need a vector' self.x0 = x0 if 'J' in kwargs: self.J = kwargs['J'] else: def J(*x): return ctx.jacobian(f, x) self.J = J self.norm = kwargs['norm'] self.verbose = kwargs['verbose'] def __iter__(self): f = self.f x0 = self.x0 norm = self.norm J = self.J fx = self.ctx.matrix(f(*x0)) fxnorm = norm(fx) cancel = False while not cancel: # get direction of descent fxn = -fx Jx = J(*x0) s = self.ctx.lu_solve(Jx, fxn) if self.verbose: print('Jx:') print(Jx) print('s:', s) # damping step size TODO: better strategy (hard task) l = self.ctx.one x1 = x0 + s while True: if x1 == x0: if self.verbose: print("canceled, won't get more excact") cancel = True break fx = self.ctx.matrix(f(*x1)) newnorm = norm(fx) if newnorm < fxnorm: # new x accepted fxnorm = newnorm x0 = x1 break l /= 2 x1 = x0 + l*s yield (x0, fxnorm) ############# # UTILITIES # ############# str2solver = {'newton':Newton, 'secant':Secant,'mnewton':MNewton, 'halley':Halley, 'muller':Muller, 'bisect':Bisection, 'illinois':Illinois, 'pegasus':Pegasus, 'anderson':Anderson, 'ridder':Ridder, 'anewton':ANewton, 'mdnewton':MDNewton} def findroot(ctx, f, x0, solver=Secant, tol=None, verbose=False, verify=True, **kwargs): r""" Find a solution to `f(x) = 0`, using *x0* as starting point or interval for *x*. Multidimensional overdetermined systems are supported. You can specify them using a function or a list of functions. If the found root does not satisfy `|f(x)^2 < \mathrm{tol}|`, an exception is raised (this can be disabled with *verify=False*). **Arguments** *f* one dimensional function *x0* starting point, several starting points or interval (depends on solver) *tol* the returned solution has an error smaller than this *verbose* print additional information for each iteration if true *verify* verify the solution and raise a ValueError if `|f(x) > \mathrm{tol}|` *solver* a generator for *f* and *x0* returning approximative solution and error *maxsteps* after how many steps the solver will cancel *df* first derivative of *f* (used by some solvers) *d2f* second derivative of *f* (used by some solvers) *multidimensional* force multidimensional solving *J* Jacobian matrix of *f* (used by multidimensional solvers) *norm* used vector norm (used by multidimensional solvers) solver has to be callable with ``(f, x0, **kwargs)`` and return an generator yielding pairs of approximative solution and estimated error (which is expected to be positive). You can use the following string aliases: 'secant', 'mnewton', 'halley', 'muller', 'illinois', 'pegasus', 'anderson', 'ridder', 'anewton', 'bisect' See mpmath.optimization for their documentation. **Examples** The function :func:`~mpmath.findroot` locates a root of a given function using the secant method by default. A simple example use of the secant method is to compute `\pi` as the root of `\sin x` closest to `x_0 = 3`:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> findroot(sin, 3) 3.14159265358979323846264338328 The secant method can be used to find complex roots of analytic functions, although it must in that case generally be given a nonreal starting value (or else it will never leave the real line):: >>> mp.dps = 15 >>> findroot(lambda x: x**3 + 2*x + 1, j) (0.226698825758202 + 1.46771150871022j) A nice application is to compute nontrivial roots of the Riemann zeta function with many digits (good initial values are needed for convergence):: >>> mp.dps = 30 >>> findroot(zeta, 0.5+14j) (0.5 + 14.1347251417346937904572519836j) The secant method can also be used as an optimization algorithm, by passing it a derivative of a function. The following example locates the positive minimum of the gamma function:: >>> mp.dps = 20 >>> findroot(lambda x: diff(gamma, x), 1) 1.4616321449683623413 Finally, a useful application is to compute inverse functions, such as the Lambert W function which is the inverse of `w e^w`, given the first term of the solution's asymptotic expansion as the initial value. In basic cases, this gives identical results to mpmath's built-in ``lambertw`` function:: >>> def lambert(x): ... return findroot(lambda w: w*exp(w) - x, log(1+x)) ... >>> mp.dps = 15 >>> lambert(1); lambertw(1) 0.567143290409784 0.567143290409784 >>> lambert(1000); lambert(1000) 5.2496028524016 5.2496028524016 Multidimensional functions are also supported:: >>> f = [lambda x1, x2: x1**2 + x2, ... lambda x1, x2: 5*x1**2 - 3*x1 + 2*x2 - 3] >>> findroot(f, (0, 0)) [-0.618033988749895] [-0.381966011250105] >>> findroot(f, (10, 10)) [ 1.61803398874989] [-2.61803398874989] You can verify this by solving the system manually. Please note that the following (more general) syntax also works:: >>> def f(x1, x2): ... return x1**2 + x2, 5*x1**2 - 3*x1 + 2*x2 - 3 ... >>> findroot(f, (0, 0)) [-0.618033988749895] [-0.381966011250105] **Multiple roots** For multiple roots all methods of the Newtonian family (including secant) converge slowly. Consider this example:: >>> f = lambda x: (x - 1)**99 >>> findroot(f, 0.9, verify=False) 0.918073542444929 Even for a very close starting point the secant method converges very slowly. Use ``verbose=True`` to illustrate this. It is possible to modify Newton's method to make it converge regardless of the root's multiplicity:: >>> findroot(f, -10, solver='mnewton') 1.0 This variant uses the first and second derivative of the function, which is not very efficient. Alternatively you can use an experimental Newtonian solver that keeps track of the speed of convergence and accelerates it using Steffensen's method if necessary:: >>> findroot(f, -10, solver='anewton', verbose=True) x: -9.88888888888888888889 error: 0.111111111111111111111 converging slowly x: -9.77890011223344556678 error: 0.10998877665544332211 converging slowly x: -9.67002233332199662166 error: 0.108877778911448945119 converging slowly accelerating convergence x: -9.5622443299551077669 error: 0.107778003366888854764 converging slowly x: 0.99999999999999999214 error: 10.562244329955107759 x: 1.0 error: 7.8598304758094664213e-18 1.0 **Complex roots** For complex roots it's recommended to use Muller's method as it converges even for real starting points very fast:: >>> findroot(lambda x: x**4 + x + 1, (0, 1, 2), solver='muller') (0.727136084491197 + 0.934099289460529j) **Intersection methods** When you need to find a root in a known interval, it's highly recommended to use an intersection-based solver like ``'anderson'`` or ``'ridder'``. Usually they converge faster and more reliable. They have however problems with multiple roots and usually need a sign change to find a root:: >>> findroot(lambda x: x**3, (-1, 1), solver='anderson') 0.0 Be careful with symmetric functions:: >>> findroot(lambda x: x**2, (-1, 1), solver='anderson') #doctest:+ELLIPSIS Traceback (most recent call last): ... ZeroDivisionError It fails even for better starting points, because there is no sign change:: >>> findroot(lambda x: x**2, (-1, .5), solver='anderson') Traceback (most recent call last): ... ValueError: Could not find root within given tolerance. (1 > 2.1684e-19) Try another starting point or tweak arguments. """ prec = ctx.prec try: ctx.prec += 20 # initialize arguments if tol is None: tol = ctx.eps * 2**10 kwargs['verbose'] = kwargs.get('verbose', verbose) if 'd1f' in kwargs: kwargs['df'] = kwargs['d1f'] kwargs['tol'] = tol if isinstance(x0, (list, tuple)): x0 = [ctx.convert(x) for x in x0] else: x0 = [ctx.convert(x0)] if isinstance(solver, str): try: solver = str2solver[solver] except KeyError: raise ValueError('could not recognize solver') # accept list of functions if isinstance(f, (list, tuple)): f2 = copy(f) def tmp(*args): return [fn(*args) for fn in f2] f = tmp # detect multidimensional functions try: fx = f(*x0) multidimensional = isinstance(fx, (list, tuple, ctx.matrix)) except TypeError: fx = f(x0[0]) multidimensional = False if 'multidimensional' in kwargs: multidimensional = kwargs['multidimensional'] if multidimensional: # only one multidimensional solver available at the moment solver = MDNewton if not 'norm' in kwargs: norm = lambda x: ctx.norm(x, 'inf') kwargs['norm'] = norm else: norm = kwargs['norm'] else: norm = abs # happily return starting point if it's a root if norm(fx) == 0: if multidimensional: return ctx.matrix(x0) else: return x0[0] # use solver iterations = solver(ctx, f, x0, **kwargs) if 'maxsteps' in kwargs: maxsteps = kwargs['maxsteps'] else: maxsteps = iterations.maxsteps i = 0 for x, error in iterations: if verbose: print('x: ', x) print('error:', error) i += 1 if error < tol * max(1, norm(x)) or i >= maxsteps: break if not isinstance(x, (list, tuple, ctx.matrix)): xl = [x] else: xl = x if verify and norm(f(*xl))**2 > tol: # TODO: better condition? raise ValueError('Could not find root within given tolerance. ' '(%g > %g)\n' 'Try another starting point or tweak arguments.' % (norm(f(*xl))**2, tol)) return x finally: ctx.prec = prec def multiplicity(ctx, f, root, tol=None, maxsteps=10, **kwargs): """ Return the multiplicity of a given root of f. Internally, numerical derivatives are used. This might be inefficient for higher order derviatives. Due to this, ``multiplicity`` cancels after evaluating 10 derivatives by default. You can be specify the n-th derivative using the dnf keyword. >>> from mpmath import * >>> multiplicity(lambda x: sin(x) - 1, pi/2) 2 """ if tol is None: tol = ctx.eps ** 0.8 kwargs['d0f'] = f for i in xrange(maxsteps): dfstr = 'd' + str(i) + 'f' if dfstr in kwargs: df = kwargs[dfstr] else: df = lambda x: ctx.diff(f, x, i) if not abs(df(root)) < tol: break return i def steffensen(f): """ linear convergent function -> quadratic convergent function Steffensen's method for quadratic convergence of a linear converging sequence. Don not use it for higher rates of convergence. It may even work for divergent sequences. Definition: F(x) = (x*f(f(x)) - f(x)**2) / (f(f(x)) - 2*f(x) + x) Example ....... You can use Steffensen's method to accelerate a fixpoint iteration of linear (or less) convergence. x* is a fixpoint of the iteration x_{k+1} = phi(x_k) if x* = phi(x*). For phi(x) = x**2 there are two fixpoints: 0 and 1. Let's try Steffensen's method: >>> f = lambda x: x**2 >>> from mpmath.optimization import steffensen >>> F = steffensen(f) >>> for x in [0.5, 0.9, 2.0]: ... fx = Fx = x ... for i in xrange(10): ... try: ... fx = f(fx) ... except OverflowError: ... pass ... try: ... Fx = F(Fx) ... except ZeroDivisionError: ... pass ... print '%20g %20g' % (fx, Fx) 0.25 -0.5 0.0625 0.1 0.00390625 -0.0011236 1.52588e-005 1.41691e-009 2.32831e-010 -2.84465e-027 5.42101e-020 2.30189e-080 2.93874e-039 -1.2197e-239 8.63617e-078 0 7.45834e-155 0 5.56268e-309 0 0.81 1.02676 0.6561 1.00134 0.430467 1 0.185302 1 0.0343368 1 0.00117902 1 1.39008e-006 1 1.93233e-012 1 3.73392e-024 1 1.39421e-047 1 4 1.6 16 1.2962 256 1.10194 65536 1.01659 4.29497e+009 1.00053 1.84467e+019 1 3.40282e+038 1 1.15792e+077 1 1.34078e+154 1 1.34078e+154 1 Unmodified, the iteration converges only towards 0. Modified it converges not only much faster, it converges even to the repelling fixpoint 1. """ def F(x): fx = f(x) ffx = f(fx) return (x*ffx - fx**2) / (ffx - 2*fx + x) return F OptimizationMethods.jacobian = jacobian OptimizationMethods.findroot = findroot OptimizationMethods.multiplicity = multiplicity if __name__ == '__main__': import doctest doctest.testmod() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/differentiation.py0000644000175000017500000004727712014170666027745 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .calculus import defun try: iteritems = dict.iteritems except AttributeError: iteritems = dict.items #----------------------------------------------------------------------------# # Differentiation # #----------------------------------------------------------------------------# @defun def difference(ctx, s, n): r""" Given a sequence `(s_k)` containing at least `n+1` items, returns the `n`-th forward difference, .. math :: \Delta^n = \sum_{k=0}^{\infty} (-1)^{k+n} {n \choose k} s_k. """ n = int(n) d = ctx.zero b = (-1) ** (n & 1) for k in xrange(n+1): d += b * s[k] b = (b * (k-n)) // (k+1) return d def hsteps(ctx, f, x, n, prec, **options): singular = options.get('singular') addprec = options.get('addprec', 10) direction = options.get('direction', 0) workprec = (prec+2*addprec) * (n+1) orig = ctx.prec try: ctx.prec = workprec h = options.get('h') if h is None: if options.get('relative'): hextramag = int(ctx.mag(x)) else: hextramag = 0 h = ctx.ldexp(1, -prec-addprec-hextramag) else: h = ctx.convert(h) # Directed: steps x, x+h, ... x+n*h direction = options.get('direction', 0) if direction: h *= ctx.sign(direction) steps = xrange(n+1) norm = h # Central: steps x-n*h, x-(n-2)*h ..., x, ..., x+(n-2)*h, x+n*h else: steps = xrange(-n, n+1, 2) norm = (2*h) # Perturb if singular: x += 0.5*h values = [f(x+k*h) for k in steps] return values, norm, workprec finally: ctx.prec = orig @defun def diff(ctx, f, x, n=1, **options): r""" Numerically computes the derivative of `f`, `f'(x)`, or generally for an integer `n \ge 0`, the `n`-th derivative `f^{(n)}(x)`. A few basic examples are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> diff(lambda x: x**2 + x, 1.0) 3.0 >>> diff(lambda x: x**2 + x, 1.0, 2) 2.0 >>> diff(lambda x: x**2 + x, 1.0, 3) 0.0 >>> nprint([diff(exp, 3, n) for n in range(5)]) # exp'(x) = exp(x) [20.0855, 20.0855, 20.0855, 20.0855, 20.0855] Even more generally, given a tuple of arguments `(x_1, \ldots, x_k)` and order `(n_1, \ldots, n_k)`, the partial derivative `f^{(n_1,\ldots,n_k)}(x_1,\ldots,x_k)` is evaluated. For example:: >>> diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (0,1)) 2.75 >>> diff(lambda x,y: 3*x*y + 2*y - x, (0.25, 0.5), (1,1)) 3.0 **Options** The following optional keyword arguments are recognized: ``method`` Supported methods are ``'step'`` or ``'quad'``: derivatives may be computed using either a finite difference with a small step size `h` (default), or numerical quadrature. ``direction`` Direction of finite difference: can be -1 for a left difference, 0 for a central difference (default), or +1 for a right difference; more generally can be any complex number. ``addprec`` Extra precision for `h` used to account for the function's sensitivity to perturbations (default = 10). ``relative`` Choose `h` relative to the magnitude of `x`, rather than an absolute value; useful for large or tiny `x` (default = False). ``h`` As an alternative to ``addprec`` and ``relative``, manually select the step size `h`. ``singular`` If True, evaluation exactly at the point `x` is avoided; this is useful for differentiating functions with removable singularities. Default = False. ``radius`` Radius of integration contour (with ``method = 'quad'``). Default = 0.25. A larger radius typically is faster and more accurate, but it must be chosen so that `f` has no singularities within the radius from the evaluation point. A finite difference requires `n+1` function evaluations and must be performed at `(n+1)` times the target precision. Accordingly, `f` must support fast evaluation at high precision. With integration, a larger number of function evaluations is required, but not much extra precision is required. For high order derivatives, this method may thus be faster if f is very expensive to evaluate at high precision. **Further examples** The direction option is useful for computing left- or right-sided derivatives of nonsmooth functions:: >>> diff(abs, 0, direction=0) 0.0 >>> diff(abs, 0, direction=1) 1.0 >>> diff(abs, 0, direction=-1) -1.0 More generally, if the direction is nonzero, a right difference is computed where the step size is multiplied by sign(direction). For example, with direction=+j, the derivative from the positive imaginary direction will be computed:: >>> diff(abs, 0, direction=j) (0.0 - 1.0j) With integration, the result may have a small imaginary part even even if the result is purely real:: >>> diff(sqrt, 1, method='quad') # doctest:+ELLIPSIS (0.5 - 4.59...e-26j) >>> chop(_) 0.5 Adding precision to obtain an accurate value:: >>> diff(cos, 1e-30) 0.0 >>> diff(cos, 1e-30, h=0.0001) -9.99999998328279e-31 >>> diff(cos, 1e-30, addprec=100) -1.0e-30 """ partial = False try: orders = list(n) x = list(x) partial = True except TypeError: pass if partial: x = [ctx.convert(_) for _ in x] return _partial_diff(ctx, f, x, orders, options) method = options.get('method', 'step') if n == 0 and method != 'quad' and not options.get('singular'): return f(ctx.convert(x)) prec = ctx.prec try: if method == 'step': values, norm, workprec = hsteps(ctx, f, x, n, prec, **options) ctx.prec = workprec v = ctx.difference(values, n) / norm**n elif method == 'quad': ctx.prec += 10 radius = ctx.convert(options.get('radius', 0.25)) def g(t): rei = radius*ctx.expj(t) z = x + rei return f(z) / rei**n d = ctx.quadts(g, [0, 2*ctx.pi]) v = d * ctx.factorial(n) / (2*ctx.pi) else: raise ValueError("unknown method: %r" % method) finally: ctx.prec = prec return +v def _partial_diff(ctx, f, xs, orders, options): if not orders: return f() if not sum(orders): return f(*xs) i = 0 for i in range(len(orders)): if orders[i]: break order = orders[i] def fdiff_inner(*f_args): def inner(t): return f(*(f_args[:i] + (t,) + f_args[i+1:])) return ctx.diff(inner, f_args[i], order, **options) orders[i] = 0 return _partial_diff(ctx, fdiff_inner, xs, orders, options) @defun def diffs(ctx, f, x, n=None, **options): r""" Returns a generator that yields the sequence of derivatives .. math :: f(x), f'(x), f''(x), \ldots, f^{(k)}(x), \ldots With ``method='step'``, :func:`~mpmath.diffs` uses only `O(k)` function evaluations to generate the first `k` derivatives, rather than the roughly `O(k^2)` evaluations required if one calls :func:`~mpmath.diff` `k` separate times. With `n < \infty`, the generator stops as soon as the `n`-th derivative has been generated. If the exact number of needed derivatives is known in advance, this is further slightly more efficient. Options are the same as for :func:`~mpmath.diff`. **Examples** >>> from mpmath import * >>> mp.dps = 15 >>> nprint(list(diffs(cos, 1, 5))) [0.540302, -0.841471, -0.540302, 0.841471, 0.540302, -0.841471] >>> for i, d in zip(range(6), diffs(cos, 1)): ... print("%s %s" % (i, d)) ... 0 0.54030230586814 1 -0.841470984807897 2 -0.54030230586814 3 0.841470984807897 4 0.54030230586814 5 -0.841470984807897 """ if n is None: n = ctx.inf else: n = int(n) if options.get('method', 'step') != 'step': k = 0 while k < n: yield ctx.diff(f, x, k, **options) k += 1 return singular = options.get('singular') if singular: yield ctx.diff(f, x, 0, singular=True) else: yield f(ctx.convert(x)) if n < 1: return if n == ctx.inf: A, B = 1, 2 else: A, B = 1, n+1 while 1: callprec = ctx.prec y, norm, workprec = hsteps(ctx, f, x, B, callprec, **options) for k in xrange(A, B): try: ctx.prec = workprec d = ctx.difference(y, k) / norm**k finally: ctx.prec = callprec yield +d if k >= n: return A, B = B, int(A*1.4+1) B = min(B, n) def iterable_to_function(gen): gen = iter(gen) data = [] def f(k): for i in xrange(len(data), k+1): data.append(next(gen)) return data[k] return f @defun def diffs_prod(ctx, factors): r""" Given a list of `N` iterables or generators yielding `f_k(x), f'_k(x), f''_k(x), \ldots` for `k = 1, \ldots, N`, generate `g(x), g'(x), g''(x), \ldots` where `g(x) = f_1(x) f_2(x) \cdots f_N(x)`. At high precision and for large orders, this is typically more efficient than numerical differentiation if the derivatives of each `f_k(x)` admit direct computation. Note: This function does not increase the working precision internally, so guard digits may have to be added externally for full accuracy. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = lambda x: exp(x)*cos(x)*sin(x) >>> u = diffs(f, 1) >>> v = mp.diffs_prod([diffs(exp,1), diffs(cos,1), diffs(sin,1)]) >>> next(u); next(v) 1.23586333600241 1.23586333600241 >>> next(u); next(v) 0.104658952245596 0.104658952245596 >>> next(u); next(v) -5.96999877552086 -5.96999877552086 >>> next(u); next(v) -12.4632923122697 -12.4632923122697 """ N = len(factors) if N == 1: for c in factors[0]: yield c else: u = iterable_to_function(ctx.diffs_prod(factors[:N//2])) v = iterable_to_function(ctx.diffs_prod(factors[N//2:])) n = 0 while 1: #yield sum(binomial(n,k)*u(n-k)*v(k) for k in xrange(n+1)) s = u(n) * v(0) a = 1 for k in xrange(1,n+1): a = a * (n-k+1) // k s += a * u(n-k) * v(k) yield s n += 1 def dpoly(n, _cache={}): """ nth differentiation polynomial for exp (Faa di Bruno's formula). TODO: most exponents are zero, so maybe a sparse representation would be better. """ if n in _cache: return _cache[n] if not _cache: _cache[0] = {(0,):1} R = dpoly(n-1) R = dict((c+(0,),v) for (c,v) in iteritems(R)) Ra = {} for powers, count in iteritems(R): powers1 = (powers[0]+1,) + powers[1:] if powers1 in Ra: Ra[powers1] += count else: Ra[powers1] = count for powers, count in iteritems(R): if not sum(powers): continue for k,p in enumerate(powers): if p: powers2 = powers[:k] + (p-1,powers[k+1]+1) + powers[k+2:] if powers2 in Ra: Ra[powers2] += p*count else: Ra[powers2] = p*count _cache[n] = Ra return _cache[n] @defun def diffs_exp(ctx, fdiffs): r""" Given an iterable or generator yielding `f(x), f'(x), f''(x), \ldots` generate `g(x), g'(x), g''(x), \ldots` where `g(x) = \exp(f(x))`. At high precision and for large orders, this is typically more efficient than numerical differentiation if the derivatives of `f(x)` admit direct computation. Note: This function does not increase the working precision internally, so guard digits may have to be added externally for full accuracy. **Examples** The derivatives of the gamma function can be computed using logarithmic differentiation:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> >>> def diffs_loggamma(x): ... yield loggamma(x) ... i = 0 ... while 1: ... yield psi(i,x) ... i += 1 ... >>> u = diffs_exp(diffs_loggamma(3)) >>> v = diffs(gamma, 3) >>> next(u); next(v) 2.0 2.0 >>> next(u); next(v) 1.84556867019693 1.84556867019693 >>> next(u); next(v) 2.49292999190269 2.49292999190269 >>> next(u); next(v) 3.44996501352367 3.44996501352367 """ fn = iterable_to_function(fdiffs) f0 = ctx.exp(fn(0)) yield f0 i = 1 while 1: s = ctx.mpf(0) for powers, c in iteritems(dpoly(i)): s += c*ctx.fprod(fn(k+1)**p for (k,p) in enumerate(powers) if p) yield s * f0 i += 1 @defun def differint(ctx, f, x, n=1, x0=0): r""" Calculates the Riemann-Liouville differintegral, or fractional derivative, defined by .. math :: \,_{x_0}{\mathbb{D}}^n_xf(x) \frac{1}{\Gamma(m-n)} \frac{d^m}{dx^m} \int_{x_0}^{x}(x-t)^{m-n-1}f(t)dt where `f` is a given (presumably well-behaved) function, `x` is the evaluation point, `n` is the order, and `x_0` is the reference point of integration (`m` is an arbitrary parameter selected automatically). With `n = 1`, this is just the standard derivative `f'(x)`; with `n = 2`, the second derivative `f''(x)`, etc. With `n = -1`, it gives `\int_{x_0}^x f(t) dt`, with `n = -2` it gives `\int_{x_0}^x \left( \int_{x_0}^t f(u) du \right) dt`, etc. As `n` is permitted to be any number, this operator generalizes iterated differentiation and iterated integration to a single operator with a continuous order parameter. **Examples** There is an exact formula for the fractional derivative of a monomial `x^p`, which may be used as a reference. For example, the following gives a half-derivative (order 0.5):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> x = mpf(3); p = 2; n = 0.5 >>> differint(lambda t: t**p, x, n) 7.81764019044672 >>> gamma(p+1)/gamma(p-n+1) * x**(p-n) 7.81764019044672 Another useful test function is the exponential function, whose integration / differentiation formula easy generalizes to arbitrary order. Here we first compute a third derivative, and then a triply nested integral. (The reference point `x_0` is set to `-\infty` to avoid nonzero endpoint terms.):: >>> differint(lambda x: exp(pi*x), -1.5, 3) 0.278538406900792 >>> exp(pi*-1.5) * pi**3 0.278538406900792 >>> differint(lambda x: exp(pi*x), 3.5, -3, -inf) 1922.50563031149 >>> exp(pi*3.5) / pi**3 1922.50563031149 However, for noninteger `n`, the differentiation formula for the exponential function must be modified to give the same result as the Riemann-Liouville differintegral:: >>> x = mpf(3.5) >>> c = pi >>> n = 1+2*j >>> differint(lambda x: exp(c*x), x, n) (-123295.005390743 + 140955.117867654j) >>> x**(-n) * exp(c)**x * (x*c)**n * gammainc(-n, 0, x*c) / gamma(-n) (-123295.005390743 + 140955.117867654j) """ m = max(int(ctx.ceil(ctx.re(n)))+1, 1) r = m-n-1 g = lambda x: ctx.quad(lambda t: (x-t)**r * f(t), [x0, x]) return ctx.diff(g, x, m) / ctx.gamma(m-n) @defun def diffun(ctx, f, n=1, **options): r""" Given a function `f`, returns a function `g(x)` that evaluates the nth derivative `f^{(n)}(x)`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> cos2 = diffun(sin) >>> sin2 = diffun(sin, 4) >>> cos(1.3), cos2(1.3) (0.267498828624587, 0.267498828624587) >>> sin(1.3), sin2(1.3) (0.963558185417193, 0.963558185417193) The function `f` must support arbitrary precision evaluation. See :func:`~mpmath.diff` for additional details and supported keyword options. """ if n == 0: return f def g(x): return ctx.diff(f, x, n, **options) return g @defun def taylor(ctx, f, x, n, **options): r""" Produces a degree-`n` Taylor polynomial around the point `x` of the given function `f`. The coefficients are returned as a list. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint(chop(taylor(sin, 0, 5))) [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333] The coefficients are computed using high-order numerical differentiation. The function must be possible to evaluate to arbitrary precision. See :func:`~mpmath.diff` for additional details and supported keyword options. Note that to evaluate the Taylor polynomial as an approximation of `f`, e.g. with :func:`~mpmath.polyval`, the coefficients must be reversed, and the point of the Taylor expansion must be subtracted from the argument: >>> p = taylor(exp, 2.0, 10) >>> polyval(p[::-1], 2.5 - 2.0) 12.1824939606092 >>> exp(2.5) 12.1824939607035 """ gen = enumerate(ctx.diffs(f, x, n, **options)) if options.get("chop", True): return [ctx.chop(d)/ctx.factorial(i) for i, d in gen] else: return [d/ctx.factorial(i) for i, d in gen] @defun def pade(ctx, a, L, M): r""" Computes a Pade approximation of degree `(L, M)` to a function. Given at least `L+M+1` Taylor coefficients `a` approximating a function `A(x)`, :func:`~mpmath.pade` returns coefficients of polynomials `P, Q` satisfying .. math :: P = \sum_{k=0}^L p_k x^k Q = \sum_{k=0}^M q_k x^k Q_0 = 1 A(x) Q(x) = P(x) + O(x^{L+M+1}) `P(x)/Q(x)` can provide a good approximation to an analytic function beyond the radius of convergence of its Taylor series (example from G.A. Baker 'Essentials of Pade Approximants' Academic Press, Ch.1A):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> one = mpf(1) >>> def f(x): ... return sqrt((one + 2*x)/(one + x)) ... >>> a = taylor(f, 0, 6) >>> p, q = pade(a, 3, 3) >>> x = 10 >>> polyval(p[::-1], x)/polyval(q[::-1], x) 1.38169105566806 >>> f(x) 1.38169855941551 """ # To determine L+1 coefficients of P and M coefficients of Q # L+M+1 coefficients of A must be provided assert(len(a) >= L+M+1) if M == 0: if L == 0: return [ctx.one], [ctx.one] else: return a[:L+1], [ctx.one] # Solve first # a[L]*q[1] + ... + a[L-M+1]*q[M] = -a[L+1] # ... # a[L+M-1]*q[1] + ... + a[L]*q[M] = -a[L+M] A = ctx.matrix(M) for j in range(M): for i in range(min(M, L+j+1)): A[j, i] = a[L+j-i] v = -ctx.matrix(a[(L+1):(L+M+1)]) x = ctx.lu_solve(A, v) q = [ctx.one] + list(x) # compute p p = [0]*(L+1) for i in range(L+1): s = a[i] for j in range(1, min(M,i) + 1): s += q[j]*a[i-j] p[i] = s return p, q wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/polynomials.py0000644000175000017500000001475012014170666027127 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .calculus import defun #----------------------------------------------------------------------------# # Polynomials # #----------------------------------------------------------------------------# # XXX: extra precision @defun def polyval(ctx, coeffs, x, derivative=False): r""" Given coefficients `[c_n, \ldots, c_2, c_1, c_0]` and a number `x`, :func:`~mpmath.polyval` evaluates the polynomial .. math :: P(x) = c_n x^n + \ldots + c_2 x^2 + c_1 x + c_0. If *derivative=True* is set, :func:`~mpmath.polyval` simultaneously evaluates `P(x)` with the derivative, `P'(x)`, and returns the tuple `(P(x), P'(x))`. >>> from mpmath import * >>> mp.pretty = True >>> polyval([3, 0, 2], 0.5) 2.75 >>> polyval([3, 0, 2], 0.5, derivative=True) (2.75, 3.0) The coefficients and the evaluation point may be any combination of real or complex numbers. """ if not coeffs: return ctx.zero p = ctx.convert(coeffs[0]) q = ctx.zero for c in coeffs[1:]: if derivative: q = p + x*q p = c + x*p if derivative: return p, q else: return p @defun def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10, error=False): """ Computes all roots (real or complex) of a given polynomial. The roots are returned as a sorted list, where real roots appear first followed by complex conjugate roots as adjacent elements. The polynomial should be given as a list of coefficients, in the format used by :func:`~mpmath.polyval`. The leading coefficient must be nonzero. With *error=True*, :func:`~mpmath.polyroots` returns a tuple *(roots, err)* where *err* is an estimate of the maximum error among the computed roots. **Examples** Finding the three real roots of `x^3 - x^2 - 14x + 24`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint(polyroots([1,-1,-14,24]), 4) [-4.0, 2.0, 3.0] Finding the two complex conjugate roots of `4x^2 + 3x + 2`, with an error estimate:: >>> roots, err = polyroots([4,3,2], error=True) >>> for r in roots: ... print(r) ... (-0.375 + 0.59947894041409j) (-0.375 - 0.59947894041409j) >>> >>> err 2.22044604925031e-16 >>> >>> polyval([4,3,2], roots[0]) (2.22044604925031e-16 + 0.0j) >>> polyval([4,3,2], roots[1]) (2.22044604925031e-16 + 0.0j) The following example computes all the 5th roots of unity; that is, the roots of `x^5 - 1`:: >>> mp.dps = 20 >>> for r in polyroots([1, 0, 0, 0, 0, -1]): ... print(r) ... 1.0 (-0.8090169943749474241 + 0.58778525229247312917j) (-0.8090169943749474241 - 0.58778525229247312917j) (0.3090169943749474241 + 0.95105651629515357212j) (0.3090169943749474241 - 0.95105651629515357212j) **Precision and conditioning** Provided there are no repeated roots, :func:`~mpmath.polyroots` can typically compute all roots of an arbitrary polynomial to high precision:: >>> mp.dps = 60 >>> for r in polyroots([1, 0, -10, 0, 1]): ... print r ... -3.14626436994197234232913506571557044551247712918732870123249 -0.317837245195782244725757617296174288373133378433432554879127 0.317837245195782244725757617296174288373133378433432554879127 3.14626436994197234232913506571557044551247712918732870123249 >>> >>> sqrt(3) + sqrt(2) 3.14626436994197234232913506571557044551247712918732870123249 >>> sqrt(3) - sqrt(2) 0.317837245195782244725757617296174288373133378433432554879127 **Algorithm** :func:`~mpmath.polyroots` implements the Durand-Kerner method [1], which uses complex arithmetic to locate all roots simultaneously. The Durand-Kerner method can be viewed as approximately performing simultaneous Newton iteration for all the roots. In particular, the convergence to simple roots is quadratic, just like Newton's method. Although all roots are internally calculated using complex arithmetic, any root found to have an imaginary part smaller than the estimated numerical error is truncated to a real number. Real roots are placed first in the returned list, sorted by value. The remaining complex roots are sorted by real their parts so that conjugate roots end up next to each other. **References** 1. http://en.wikipedia.org/wiki/Durand-Kerner_method """ if len(coeffs) <= 1: if not coeffs or not coeffs[0]: raise ValueError("Input to polyroots must not be the zero polynomial") # Constant polynomial with no roots return [] orig = ctx.prec weps = +ctx.eps try: ctx.prec += 10 tol = ctx.eps * 128 deg = len(coeffs) - 1 # Must be monic lead = ctx.convert(coeffs[0]) if lead == 1: coeffs = [ctx.convert(c) for c in coeffs] else: coeffs = [c/lead for c in coeffs] f = lambda x: ctx.polyval(coeffs, x) roots = [ctx.mpc((0.4+0.9j)**n) for n in xrange(deg)] err = [ctx.one for n in xrange(deg)] # Durand-Kerner iteration until convergence for step in xrange(maxsteps): if abs(max(err)) < tol: break for i in xrange(deg): if not abs(err[i]) < tol: p = roots[i] x = f(p) for j in range(deg): if i != j: try: x /= (p-roots[j]) except ZeroDivisionError: continue roots[i] = p - x err[i] = abs(x) # Remove small imaginary parts if cleanup: for i in xrange(deg): if abs(ctx._im(roots[i])) < weps: roots[i] = roots[i].real elif abs(ctx._re(roots[i])) < weps: roots[i] = roots[i].imag * 1j roots.sort(key=lambda x: (abs(ctx._im(x)), ctx._re(x))) finally: ctx.prec = orig if error: err = max(err) err = max(err, ctx.ldexp(1, -orig+1)) return [+r for r in roots], +err else: return [+r for r in roots] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/odes.py0000644000175000017500000002326412014170666025513 0ustar georgeskgeorgeskfrom bisect import bisect from ..libmp.backend import xrange class ODEMethods(object): pass def ode_taylor(ctx, derivs, x0, y0, tol_prec, n): h = tol = ctx.ldexp(1, -tol_prec) dim = len(y0) xs = [x0] ys = [y0] x = x0 y = y0 orig = ctx.prec try: ctx.prec = orig*(1+n) # Use n steps with Euler's method to get # evaluation points for derivatives for i in range(n): fxy = derivs(x, y) y = [y[i]+h*fxy[i] for i in xrange(len(y))] x += h xs.append(x) ys.append(y) # Compute derivatives ser = [[] for d in range(dim)] for j in range(n+1): s = [0]*dim b = (-1) ** (j & 1) k = 1 for i in range(j+1): for d in range(dim): s[d] += b * ys[i][d] b = (b * (j-k+1)) // (-k) k += 1 scale = h**(-j) / ctx.fac(j) for d in range(dim): s[d] = s[d] * scale ser[d].append(s[d]) finally: ctx.prec = orig # Estimate radius for which we can get full accuracy. # XXX: do this right for zeros radius = ctx.one for ts in ser: if ts[-1]: radius = min(radius, ctx.nthroot(tol/abs(ts[-1]), n)) radius /= 2 # XXX return ser, x0+radius def odefun(ctx, F, x0, y0, tol=None, degree=None, method='taylor', verbose=False): r""" Returns a function `y(x) = [y_0(x), y_1(x), \ldots, y_n(x)]` that is a numerical solution of the `n+1`-dimensional first-order ordinary differential equation (ODE) system .. math :: y_0'(x) = F_0(x, [y_0(x), y_1(x), \ldots, y_n(x)]) y_1'(x) = F_1(x, [y_0(x), y_1(x), \ldots, y_n(x)]) \vdots y_n'(x) = F_n(x, [y_0(x), y_1(x), \ldots, y_n(x)]) The derivatives are specified by the vector-valued function *F* that evaluates `[y_0', \ldots, y_n'] = F(x, [y_0, \ldots, y_n])`. The initial point `x_0` is specified by the scalar argument *x0*, and the initial value `y(x_0) = [y_0(x_0), \ldots, y_n(x_0)]` is specified by the vector argument *y0*. For convenience, if the system is one-dimensional, you may optionally provide just a scalar value for *y0*. In this case, *F* should accept a scalar *y* argument and return a scalar. The solution function *y* will return scalar values instead of length-1 vectors. Evaluation of the solution function `y(x)` is permitted for any `x \ge x_0`. A high-order ODE can be solved by transforming it into first-order vector form. This transformation is described in standard texts on ODEs. Examples will also be given below. **Options, speed and accuracy** By default, :func:`~mpmath.odefun` uses a high-order Taylor series method. For reasonably well-behaved problems, the solution will be fully accurate to within the working precision. Note that *F* must be possible to evaluate to very high precision for the generation of Taylor series to work. To get a faster but less accurate solution, you can set a large value for *tol* (which defaults roughly to *eps*). If you just want to plot the solution or perform a basic simulation, *tol = 0.01* is likely sufficient. The *degree* argument controls the degree of the solver (with *method='taylor'*, this is the degree of the Taylor series expansion). A higher degree means that a longer step can be taken before a new local solution must be generated from *F*, meaning that fewer steps are required to get from `x_0` to a given `x_1`. On the other hand, a higher degree also means that each local solution becomes more expensive (i.e., more evaluations of *F* are required per step, and at higher precision). The optimal setting therefore involves a tradeoff. Generally, decreasing the *degree* for Taylor series is likely to give faster solution at low precision, while increasing is likely to be better at higher precision. The function object returned by :func:`~mpmath.odefun` caches the solutions at all step points and uses polynomial interpolation between step points. Therefore, once `y(x_1)` has been evaluated for some `x_1`, `y(x)` can be evaluated very quickly for any `x_0 \le x \le x_1`. and continuing the evaluation up to `x_2 > x_1` is also fast. **Examples of first-order ODEs** We will solve the standard test problem `y'(x) = y(x), y(0) = 1` which has explicit solution `y(x) = \exp(x)`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = odefun(lambda x, y: y, 0, 1) >>> for x in [0, 1, 2.5]: ... print((f(x), exp(x))) ... (1.0, 1.0) (2.71828182845905, 2.71828182845905) (12.1824939607035, 12.1824939607035) The solution with high precision:: >>> mp.dps = 50 >>> f = odefun(lambda x, y: y, 0, 1) >>> f(1) 2.7182818284590452353602874713526624977572470937 >>> exp(1) 2.7182818284590452353602874713526624977572470937 Using the more general vectorized form, the test problem can be input as (note that *f* returns a 1-element vector):: >>> mp.dps = 15 >>> f = odefun(lambda x, y: [y[0]], 0, [1]) >>> f(1) [2.71828182845905] :func:`~mpmath.odefun` can solve nonlinear ODEs, which are generally impossible (and at best difficult) to solve analytically. As an example of a nonlinear ODE, we will solve `y'(x) = x \sin(y(x))` for `y(0) = \pi/2`. An exact solution happens to be known for this problem, and is given by `y(x) = 2 \tan^{-1}\left(\exp\left(x^2/2\right)\right)`:: >>> f = odefun(lambda x, y: x*sin(y), 0, pi/2) >>> for x in [2, 5, 10]: ... print((f(x), 2*atan(exp(mpf(x)**2/2)))) ... (2.87255666284091, 2.87255666284091) (3.14158520028345, 3.14158520028345) (3.14159265358979, 3.14159265358979) If `F` is independent of `y`, an ODE can be solved using direct integration. We can therefore obtain a reference solution with :func:`~mpmath.quad`:: >>> f = lambda x: (1+x**2)/(1+x**3) >>> g = odefun(lambda x, y: f(x), pi, 0) >>> g(2*pi) 0.72128263801696 >>> quad(f, [pi, 2*pi]) 0.72128263801696 **Examples of second-order ODEs** We will solve the harmonic oscillator equation `y''(x) + y(x) = 0`. To do this, we introduce the helper functions `y_0 = y, y_1 = y_0'` whereby the original equation can be written as `y_1' + y_0' = 0`. Put together, we get the first-order, two-dimensional vector ODE .. math :: \begin{cases} y_0' = y_1 \\ y_1' = -y_0 \end{cases} To get a well-defined IVP, we need two initial values. With `y(0) = y_0(0) = 1` and `-y'(0) = y_1(0) = 0`, the problem will of course be solved by `y(x) = y_0(x) = \cos(x)` and `-y'(x) = y_1(x) = \sin(x)`. We check this:: >>> f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) >>> for x in [0, 1, 2.5, 10]: ... nprint(f(x), 15) ... nprint([cos(x), sin(x)], 15) ... print("---") ... [1.0, 0.0] [1.0, 0.0] --- [0.54030230586814, 0.841470984807897] [0.54030230586814, 0.841470984807897] --- [-0.801143615546934, 0.598472144103957] [-0.801143615546934, 0.598472144103957] --- [-0.839071529076452, -0.54402111088937] [-0.839071529076452, -0.54402111088937] --- Note that we get both the sine and the cosine solutions simultaneously. **TODO** * Better automatic choice of degree and step size * Make determination of Taylor series convergence radius more robust * Allow solution for `x < x_0` * Allow solution for complex `x` * Test for difficult (ill-conditioned) problems * Implement Runge-Kutta and other algorithms """ if tol: tol_prec = int(-ctx.log(tol, 2))+10 else: tol_prec = ctx.prec+10 degree = degree or (3 + int(3*ctx.dps/2.)) workprec = ctx.prec + 40 try: len(y0) return_vector = True except TypeError: F_ = F F = lambda x, y: [F_(x, y[0])] y0 = [y0] return_vector = False ser, xb = ode_taylor(ctx, F, x0, y0, tol_prec, degree) series_boundaries = [x0, xb] series_data = [(ser, x0, xb)] # We will be working with vectors of Taylor series def mpolyval(ser, a): return [ctx.polyval(s[::-1], a) for s in ser] # Find nearest expansion point; compute if necessary def get_series(x): if x < x0: raise ValueError n = bisect(series_boundaries, x) if n < len(series_boundaries): return series_data[n-1] while 1: ser, xa, xb = series_data[-1] if verbose: print("Computing Taylor series for [%f, %f]" % (xa, xb)) y = mpolyval(ser, xb-xa) xa = xb ser, xb = ode_taylor(ctx, F, xb, y, tol_prec, degree) series_boundaries.append(xb) series_data.append((ser, xa, xb)) if x <= xb: return series_data[-1] # Evaluation function def interpolant(x): x = ctx.convert(x) orig = ctx.prec try: ctx.prec = workprec ser, xa, xb = get_series(x) y = mpolyval(ser, x-xa) finally: ctx.prec = orig if return_vector: return [+yk for yk in y] else: return +y[0] return interpolant ODEMethods.odefun = odefun if __name__ == "__main__": import doctest doctest.testmod() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/calculus/__init__.py0000644000175000017500000000024212014170666026307 0ustar georgeskgeorgeskfrom . import calculus # XXX: hack to set methods from . import approximation from . import differentiation from . import extrapolation from . import polynomials wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/ctx_mp.py0000644000175000017500000013405212014170666024236 0ustar georgeskgeorgesk""" This module defines the mpf, mpc classes, and standard functions for operating with them. """ __docformat__ = 'plaintext' import re from .ctx_base import StandardBaseContext from .libmp.backend import basestring from . import libmp from .libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, ComplexResult, to_pickable, from_pickable, normalize, from_int, from_float, from_str, to_int, to_float, to_str, from_rational, from_man_exp, fone, fzero, finf, fninf, fnan, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, mpf_hash, mpf_rand, mpf_sum, bitcount, to_fixed, mpc_to_str, mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, mpc_mpf_div, mpf_pow, mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, mpf_glaisher, mpf_twinprime, mpf_mertens, int_types) from . import function_docs from . import rational new = object.__new__ get_complex = re.compile(r'^\(?(?P[\+\-]?\d*\.?\d*(e[\+\-]?\d+)?)??' r'(?P[\+\-]?\d*\.?\d*(e[\+\-]?\d+)?j)?\)?$') from .ctx_mp_python import PythonMPContext as BaseMPContext from . import ctx_mp_python as _mpf_module from .ctx_mp_python import _mpf, _mpc, mpnumeric class MPContext(BaseMPContext, StandardBaseContext): """ Context for multiprecision arithmetic with a global precision. """ def __init__(ctx): BaseMPContext.__init__(ctx) ctx.trap_complex = False ctx.pretty = False ctx.types = [ctx.mpf, ctx.mpc, ctx.constant] ctx._mpq = rational.mpq ctx.default() StandardBaseContext.__init__(ctx) ctx.mpq = rational.mpq ctx.init_builtins() ctx.hyp_summators = {} ctx._init_aliases() # XXX: automate try: ctx.bernoulli.im_func.func_doc = function_docs.bernoulli ctx.primepi.im_func.func_doc = function_docs.primepi ctx.psi.im_func.func_doc = function_docs.psi ctx.atan2.im_func.func_doc = function_docs.atan2 except AttributeError: # python 3 ctx.bernoulli.__func__.func_doc = function_docs.bernoulli ctx.primepi.__func__.func_doc = function_docs.primepi ctx.psi.__func__.func_doc = function_docs.psi ctx.atan2.__func__.func_doc = function_docs.atan2 ctx.digamma.func_doc = function_docs.digamma ctx.cospi.func_doc = function_docs.cospi ctx.sinpi.func_doc = function_docs.sinpi def init_builtins(ctx): mpf = ctx.mpf mpc = ctx.mpc # Exact constants ctx.one = ctx.make_mpf(fone) ctx.zero = ctx.make_mpf(fzero) ctx.j = ctx.make_mpc((fzero,fone)) ctx.inf = ctx.make_mpf(finf) ctx.ninf = ctx.make_mpf(fninf) ctx.nan = ctx.make_mpf(fnan) eps = ctx.constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1), "epsilon of working precision", "eps") ctx.eps = eps # Approximate constants ctx.pi = ctx.constant(mpf_pi, "pi", "pi") ctx.ln2 = ctx.constant(mpf_ln2, "ln(2)", "ln2") ctx.ln10 = ctx.constant(mpf_ln10, "ln(10)", "ln10") ctx.phi = ctx.constant(mpf_phi, "Golden ratio phi", "phi") ctx.e = ctx.constant(mpf_e, "e = exp(1)", "e") ctx.euler = ctx.constant(mpf_euler, "Euler's constant", "euler") ctx.catalan = ctx.constant(mpf_catalan, "Catalan's constant", "catalan") ctx.khinchin = ctx.constant(mpf_khinchin, "Khinchin's constant", "khinchin") ctx.glaisher = ctx.constant(mpf_glaisher, "Glaisher's constant", "glaisher") ctx.apery = ctx.constant(mpf_apery, "Apery's constant", "apery") ctx.degree = ctx.constant(mpf_degree, "1 deg = pi / 180", "degree") ctx.twinprime = ctx.constant(mpf_twinprime, "Twin prime constant", "twinprime") ctx.mertens = ctx.constant(mpf_mertens, "Mertens' constant", "mertens") # Standard functions ctx.sqrt = ctx._wrap_libmp_function(libmp.mpf_sqrt, libmp.mpc_sqrt) ctx.cbrt = ctx._wrap_libmp_function(libmp.mpf_cbrt, libmp.mpc_cbrt) ctx.ln = ctx._wrap_libmp_function(libmp.mpf_log, libmp.mpc_log) ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) ctx.exp = ctx._wrap_libmp_function(libmp.mpf_exp, libmp.mpc_exp) ctx.expj = ctx._wrap_libmp_function(libmp.mpf_expj, libmp.mpc_expj) ctx.expjpi = ctx._wrap_libmp_function(libmp.mpf_expjpi, libmp.mpc_expjpi) ctx.sin = ctx._wrap_libmp_function(libmp.mpf_sin, libmp.mpc_sin) ctx.cos = ctx._wrap_libmp_function(libmp.mpf_cos, libmp.mpc_cos) ctx.tan = ctx._wrap_libmp_function(libmp.mpf_tan, libmp.mpc_tan) ctx.sinh = ctx._wrap_libmp_function(libmp.mpf_sinh, libmp.mpc_sinh) ctx.cosh = ctx._wrap_libmp_function(libmp.mpf_cosh, libmp.mpc_cosh) ctx.tanh = ctx._wrap_libmp_function(libmp.mpf_tanh, libmp.mpc_tanh) ctx.asin = ctx._wrap_libmp_function(libmp.mpf_asin, libmp.mpc_asin) ctx.acos = ctx._wrap_libmp_function(libmp.mpf_acos, libmp.mpc_acos) ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) ctx.asinh = ctx._wrap_libmp_function(libmp.mpf_asinh, libmp.mpc_asinh) ctx.acosh = ctx._wrap_libmp_function(libmp.mpf_acosh, libmp.mpc_acosh) ctx.atanh = ctx._wrap_libmp_function(libmp.mpf_atanh, libmp.mpc_atanh) ctx.sinpi = ctx._wrap_libmp_function(libmp.mpf_sin_pi, libmp.mpc_sin_pi) ctx.cospi = ctx._wrap_libmp_function(libmp.mpf_cos_pi, libmp.mpc_cos_pi) ctx.floor = ctx._wrap_libmp_function(libmp.mpf_floor, libmp.mpc_floor) ctx.ceil = ctx._wrap_libmp_function(libmp.mpf_ceil, libmp.mpc_ceil) ctx.nint = ctx._wrap_libmp_function(libmp.mpf_nint, libmp.mpc_nint) ctx.frac = ctx._wrap_libmp_function(libmp.mpf_frac, libmp.mpc_frac) ctx.fib = ctx.fibonacci = ctx._wrap_libmp_function(libmp.mpf_fibonacci, libmp.mpc_fibonacci) ctx.gamma = ctx._wrap_libmp_function(libmp.mpf_gamma, libmp.mpc_gamma) ctx.rgamma = ctx._wrap_libmp_function(libmp.mpf_rgamma, libmp.mpc_rgamma) ctx.loggamma = ctx._wrap_libmp_function(libmp.mpf_loggamma, libmp.mpc_loggamma) ctx.fac = ctx.factorial = ctx._wrap_libmp_function(libmp.mpf_factorial, libmp.mpc_factorial) ctx.gamma_old = ctx._wrap_libmp_function(libmp.mpf_gamma_old, libmp.mpc_gamma_old) ctx.fac_old = ctx.factorial_old = ctx._wrap_libmp_function(libmp.mpf_factorial_old, libmp.mpc_factorial_old) ctx.digamma = ctx._wrap_libmp_function(libmp.mpf_psi0, libmp.mpc_psi0) ctx.harmonic = ctx._wrap_libmp_function(libmp.mpf_harmonic, libmp.mpc_harmonic) ctx.ei = ctx._wrap_libmp_function(libmp.mpf_ei, libmp.mpc_ei) ctx.e1 = ctx._wrap_libmp_function(libmp.mpf_e1, libmp.mpc_e1) ctx._ci = ctx._wrap_libmp_function(libmp.mpf_ci, libmp.mpc_ci) ctx._si = ctx._wrap_libmp_function(libmp.mpf_si, libmp.mpc_si) ctx.ellipk = ctx._wrap_libmp_function(libmp.mpf_ellipk, libmp.mpc_ellipk) ctx._ellipe = ctx._wrap_libmp_function(libmp.mpf_ellipe, libmp.mpc_ellipe) ctx.agm1 = ctx._wrap_libmp_function(libmp.mpf_agm1, libmp.mpc_agm1) ctx._erf = ctx._wrap_libmp_function(libmp.mpf_erf, None) ctx._erfc = ctx._wrap_libmp_function(libmp.mpf_erfc, None) ctx._zeta = ctx._wrap_libmp_function(libmp.mpf_zeta, libmp.mpc_zeta) ctx._altzeta = ctx._wrap_libmp_function(libmp.mpf_altzeta, libmp.mpc_altzeta) # Faster versions ctx.sqrt = getattr(ctx, "_sage_sqrt", ctx.sqrt) ctx.exp = getattr(ctx, "_sage_exp", ctx.exp) ctx.ln = getattr(ctx, "_sage_ln", ctx.ln) ctx.cos = getattr(ctx, "_sage_cos", ctx.cos) ctx.sin = getattr(ctx, "_sage_sin", ctx.sin) def to_fixed(ctx, x, prec): return x.to_fixed(prec) def hypot(ctx, x, y): r""" Computes the Euclidean norm of the vector `(x, y)`, equal to `\sqrt{x^2 + y^2}`. Both `x` and `y` must be real.""" x = ctx.convert(x) y = ctx.convert(y) return ctx.make_mpf(libmp.mpf_hypot(x._mpf_, y._mpf_, *ctx._prec_rounding)) def _gamma_upper_int(ctx, n, z): n = int(ctx._re(n)) if n == 0: return ctx.e1(z) if not hasattr(z, '_mpf_'): raise NotImplementedError prec, rounding = ctx._prec_rounding real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding, gamma=True) if imag is None: return ctx.make_mpf(real) else: return ctx.make_mpc((real, imag)) def _expint_int(ctx, n, z): n = int(n) if n == 1: return ctx.e1(z) if not hasattr(z, '_mpf_'): raise NotImplementedError prec, rounding = ctx._prec_rounding real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding) if imag is None: return ctx.make_mpf(real) else: return ctx.make_mpc((real, imag)) def _nthroot(ctx, x, n): if hasattr(x, '_mpf_'): try: return ctx.make_mpf(libmp.mpf_nthroot(x._mpf_, n, *ctx._prec_rounding)) except ComplexResult: if ctx.trap_complex: raise x = (x._mpf_, libmp.fzero) else: x = x._mpc_ return ctx.make_mpc(libmp.mpc_nthroot(x, n, *ctx._prec_rounding)) def _besselj(ctx, n, z): prec, rounding = ctx._prec_rounding if hasattr(z, '_mpf_'): return ctx.make_mpf(libmp.mpf_besseljn(n, z._mpf_, prec, rounding)) elif hasattr(z, '_mpc_'): return ctx.make_mpc(libmp.mpc_besseljn(n, z._mpc_, prec, rounding)) def _agm(ctx, a, b=1): prec, rounding = ctx._prec_rounding if hasattr(a, '_mpf_') and hasattr(b, '_mpf_'): try: v = libmp.mpf_agm(a._mpf_, b._mpf_, prec, rounding) return ctx.make_mpf(v) except ComplexResult: pass if hasattr(a, '_mpf_'): a = (a._mpf_, libmp.fzero) else: a = a._mpc_ if hasattr(b, '_mpf_'): b = (b._mpf_, libmp.fzero) else: b = b._mpc_ return ctx.make_mpc(libmp.mpc_agm(a, b, prec, rounding)) def bernoulli(ctx, n): return ctx.make_mpf(libmp.mpf_bernoulli(int(n), *ctx._prec_rounding)) def _zeta_int(ctx, n): return ctx.make_mpf(libmp.mpf_zeta_int(int(n), *ctx._prec_rounding)) def atan2(ctx, y, x): x = ctx.convert(x) y = ctx.convert(y) return ctx.make_mpf(libmp.mpf_atan2(y._mpf_, x._mpf_, *ctx._prec_rounding)) def psi(ctx, m, z): z = ctx.convert(z) m = int(m) if ctx._is_real_type(z): return ctx.make_mpf(libmp.mpf_psi(m, z._mpf_, *ctx._prec_rounding)) else: return ctx.make_mpc(libmp.mpc_psi(m, z._mpc_, *ctx._prec_rounding)) def cos_sin(ctx, x, **kwargs): if type(x) not in ctx.types: x = ctx.convert(x) prec, rounding = ctx._parse_prec(kwargs) if hasattr(x, '_mpf_'): c, s = libmp.mpf_cos_sin(x._mpf_, prec, rounding) return ctx.make_mpf(c), ctx.make_mpf(s) elif hasattr(x, '_mpc_'): c, s = libmp.mpc_cos_sin(x._mpc_, prec, rounding) return ctx.make_mpc(c), ctx.make_mpc(s) else: return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs) def cospi_sinpi(ctx, x, **kwargs): if type(x) not in ctx.types: x = ctx.convert(x) prec, rounding = ctx._parse_prec(kwargs) if hasattr(x, '_mpf_'): c, s = libmp.mpf_cos_sin_pi(x._mpf_, prec, rounding) return ctx.make_mpf(c), ctx.make_mpf(s) elif hasattr(x, '_mpc_'): c, s = libmp.mpc_cos_sin_pi(x._mpc_, prec, rounding) return ctx.make_mpc(c), ctx.make_mpc(s) else: return ctx.cos(x, **kwargs), ctx.sin(x, **kwargs) def clone(ctx): """ Create a copy of the context, with the same working precision. """ a = ctx.__class__() a.prec = ctx.prec return a # Several helper methods # TODO: add more of these, make consistent, write docstrings, ... def _is_real_type(ctx, x): if hasattr(x, '_mpc_') or type(x) is complex: return False return True def _is_complex_type(ctx, x): if hasattr(x, '_mpc_') or type(x) is complex: return True return False def isnpint(ctx, x): """ Determine if *x* is a nonpositive integer. """ if not x: return True if hasattr(x, '_mpf_'): sign, man, exp, bc = x._mpf_ return sign and exp >= 0 if hasattr(x, '_mpc_'): return not x.imag and ctx.isnpint(x.real) if type(x) in int_types: return x <= 0 if isinstance(x, ctx.mpq): p, q = x._mpq_ if not p: return True return q == 1 and p <= 0 return ctx.isnpint(ctx.convert(x)) def __str__(ctx): lines = ["Mpmath settings:", (" mp.prec = %s" % ctx.prec).ljust(30) + "[default: 53]", (" mp.dps = %s" % ctx.dps).ljust(30) + "[default: 15]", (" mp.trap_complex = %s" % ctx.trap_complex).ljust(30) + "[default: False]", ] return "\n".join(lines) @property def _repr_digits(ctx): return repr_dps(ctx._prec) @property def _str_digits(ctx): return ctx._dps def extraprec(ctx, n, normalize_output=False): """ The block with extraprec(n): increases the precision n bits, executes , and then restores the precision. extraprec(n)(f) returns a decorated version of the function f that increases the working precision by n bits before execution, and restores the parent precision afterwards. With normalize_output=True, it rounds the return value to the parent precision. """ return PrecisionManager(ctx, lambda p: p + n, None, normalize_output) def extradps(ctx, n, normalize_output=False): """ This function is analogous to extraprec (see documentation) but changes the decimal precision instead of the number of bits. """ return PrecisionManager(ctx, None, lambda d: d + n, normalize_output) def workprec(ctx, n, normalize_output=False): """ The block with workprec(n): sets the precision to n bits, executes , and then restores the precision. workprec(n)(f) returns a decorated version of the function f that sets the precision to n bits before execution, and restores the precision afterwards. With normalize_output=True, it rounds the return value to the parent precision. """ return PrecisionManager(ctx, lambda p: n, None, normalize_output) def workdps(ctx, n, normalize_output=False): """ This function is analogous to workprec (see documentation) but changes the decimal precision instead of the number of bits. """ return PrecisionManager(ctx, None, lambda d: n, normalize_output) def autoprec(ctx, f, maxprec=None, catch=(), verbose=False): """ Return a wrapped copy of *f* that repeatedly evaluates *f* with increasing precision until the result converges to the full precision used at the point of the call. This heuristically protects against rounding errors, at the cost of roughly a 2x slowdown compared to manually setting the optimal precision. This method can, however, easily be fooled if the results from *f* depend "discontinuously" on the precision, for instance if catastrophic cancellation can occur. Therefore, :func:`~mpmath.autoprec` should be used judiciously. **Examples** Many functions are sensitive to perturbations of the input arguments. If the arguments are decimal numbers, they may have to be converted to binary at a much higher precision. If the amount of required extra precision is unknown, :func:`~mpmath.autoprec` is convenient:: >>> from mpmath import * >>> mp.dps = 15 >>> mp.pretty = True >>> besselj(5, 125 * 10**28) # Exact input -8.03284785591801e-17 >>> besselj(5, '1.25e30') # Bad 7.12954868316652e-16 >>> autoprec(besselj)(5, '1.25e30') # Good -8.03284785591801e-17 The following fails to converge because `\sin(\pi) = 0` whereas all finite-precision approximations of `\pi` give nonzero values:: >>> autoprec(sin)(pi) Traceback (most recent call last): ... NoConvergence: autoprec: prec increased to 2910 without convergence As the following example shows, :func:`~mpmath.autoprec` can protect against cancellation, but is fooled by too severe cancellation:: >>> x = 1e-10 >>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x) 1.00000008274037e-10 1.00000000005e-10 1.00000000005e-10 >>> x = 1e-50 >>> exp(x)-1; expm1(x); autoprec(lambda t: exp(t)-1)(x) 0.0 1.0e-50 0.0 With *catch*, an exception or list of exceptions to intercept may be specified. The raised exception is interpreted as signaling insufficient precision. This permits, for example, evaluating a function where a too low precision results in a division by zero:: >>> f = lambda x: 1/(exp(x)-1) >>> f(1e-30) Traceback (most recent call last): ... ZeroDivisionError >>> autoprec(f, catch=ZeroDivisionError)(1e-30) 1.0e+30 """ def f_autoprec_wrapped(*args, **kwargs): prec = ctx.prec if maxprec is None: maxprec2 = ctx._default_hyper_maxprec(prec) else: maxprec2 = maxprec try: ctx.prec = prec + 10 try: v1 = f(*args, **kwargs) except catch: v1 = ctx.nan prec2 = prec + 20 while 1: ctx.prec = prec2 try: v2 = f(*args, **kwargs) except catch: v2 = ctx.nan if v1 == v2: break err = ctx.mag(v2-v1) - ctx.mag(v2) if err < (-prec): break if verbose: print("autoprec: target=%s, prec=%s, accuracy=%s" \ % (prec, prec2, -err)) v1 = v2 if prec2 >= maxprec2: raise ctx.NoConvergence(\ "autoprec: prec increased to %i without convergence"\ % prec2) prec2 += int(prec2*2) prec2 = min(prec2, maxprec2) finally: ctx.prec = prec return +v2 return f_autoprec_wrapped def nstr(ctx, x, n=6, **kwargs): """ Convert an ``mpf`` or ``mpc`` to a decimal string literal with *n* significant digits. The small default value for *n* is chosen to make this function useful for printing collections of numbers (lists, matrices, etc). If *x* is a list or tuple, :func:`~mpmath.nstr` is applied recursively to each element. For unrecognized classes, :func:`~mpmath.nstr` simply returns ``str(x)``. The companion function :func:`~mpmath.nprint` prints the result instead of returning it. >>> from mpmath import * >>> nstr([+pi, ldexp(1,-500)]) '[3.14159, 3.05494e-151]' >>> nprint([+pi, ldexp(1,-500)]) [3.14159, 3.05494e-151] """ if isinstance(x, list): return "[%s]" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) if isinstance(x, tuple): return "(%s)" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) if hasattr(x, '_mpf_'): return to_str(x._mpf_, n, **kwargs) if hasattr(x, '_mpc_'): return "(" + mpc_to_str(x._mpc_, n, **kwargs) + ")" if isinstance(x, basestring): return repr(x) if isinstance(x, ctx.matrix): return x.__nstr__(n, **kwargs) return str(x) def _convert_fallback(ctx, x, strings): if strings and isinstance(x, basestring): if 'j' in x.lower(): x = x.lower().replace(' ', '') match = get_complex.match(x) re = match.group('re') if not re: re = 0 im = match.group('im').rstrip('j') return ctx.mpc(ctx.convert(re), ctx.convert(im)) if hasattr(x, "_mpi_"): a, b = x._mpi_ if a == b: return ctx.make_mpf(a) else: raise ValueError("can only create mpf from zero-width interval") raise TypeError("cannot create mpf from " + repr(x)) def mpmathify(ctx, *args, **kwargs): return ctx.convert(*args, **kwargs) def _parse_prec(ctx, kwargs): if kwargs: if kwargs.get('exact'): return 0, 'f' prec, rounding = ctx._prec_rounding if 'rounding' in kwargs: rounding = kwargs['rounding'] if 'prec' in kwargs: prec = kwargs['prec'] if prec == ctx.inf: return 0, 'f' else: prec = int(prec) elif 'dps' in kwargs: dps = kwargs['dps'] if dps == ctx.inf: return 0, 'f' prec = dps_to_prec(dps) return prec, rounding return ctx._prec_rounding _exact_overflow_msg = "the exact result does not fit in memory" _hypsum_msg = """hypsum() failed to converge to the requested %i bits of accuracy using a working precision of %i bits. Try with a higher maxprec, maxterms, or set zeroprec.""" def hypsum(ctx, p, q, flags, coeffs, z, accurate_small=True, **kwargs): if hasattr(z, "_mpf_"): key = p, q, flags, 'R' v = z._mpf_ elif hasattr(z, "_mpc_"): key = p, q, flags, 'C' v = z._mpc_ if key not in ctx.hyp_summators: ctx.hyp_summators[key] = libmp.make_hyp_summator(key)[1] summator = ctx.hyp_summators[key] prec = ctx.prec maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(prec)) extraprec = 50 epsshift = 25 # Jumps in magnitude occur when parameters are close to negative # integers. We must ensure that these terms are included in # the sum and added accurately magnitude_check = {} max_total_jump = 0 for i, c in enumerate(coeffs): if flags[i] == 'Z': if i >= p and c <= 0: ok = False for ii, cc in enumerate(coeffs[:p]): # Note: c <= cc or c < cc, depending on convention if flags[ii] == 'Z' and cc <= 0 and c <= cc: ok = True if not ok: raise ZeroDivisionError("pole in hypergeometric series") continue n, d = ctx.nint_distance(c) n = -int(n) d = -d if i >= p and n >= 0 and d > 4: if n in magnitude_check: magnitude_check[n] += d else: magnitude_check[n] = d extraprec = max(extraprec, d - prec + 60) max_total_jump += abs(d) while 1: if extraprec > maxprec: raise ValueError(ctx._hypsum_msg % (prec, prec+extraprec)) wp = prec + extraprec if magnitude_check: mag_dict = dict((n,None) for n in magnitude_check) else: mag_dict = {} zv, have_complex, magnitude = summator(coeffs, v, prec, wp, \ epsshift, mag_dict, **kwargs) cancel = -magnitude jumps_resolved = True if extraprec < max_total_jump: for n in mag_dict.values(): if (n is None) or (n < prec): jumps_resolved = False break accurate = (cancel < extraprec-25-5 or not accurate_small) if jumps_resolved: if accurate: break # zero? zeroprec = kwargs.get('zeroprec') if zeroprec is not None: if cancel > zeroprec: if have_complex: return ctx.mpc(0) else: return ctx.zero # Some near-singularities were not included, so increase # precision and repeat until they are extraprec *= 2 # Possible workaround for bad roundoff in fixed-point arithmetic epsshift += 5 extraprec += 5 if type(zv) is tuple: if have_complex: return ctx.make_mpc(zv) else: return ctx.make_mpf(zv) else: return zv def ldexp(ctx, x, n): r""" Computes `x 2^n` efficiently. No rounding is performed. The argument `x` must be a real floating-point number (or possible to convert into one) and `n` must be a Python ``int``. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> ldexp(1, 10) mpf('1024.0') >>> ldexp(1, -3) mpf('0.125') """ x = ctx.convert(x) return ctx.make_mpf(libmp.mpf_shift(x._mpf_, n)) def frexp(ctx, x): r""" Given a real number `x`, returns `(y, n)` with `y \in [0.5, 1)`, `n` a Python integer, and such that `x = y 2^n`. No rounding is performed. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> frexp(7.5) (mpf('0.9375'), 3) """ x = ctx.convert(x) y, n = libmp.mpf_frexp(x._mpf_) return ctx.make_mpf(y), n def fneg(ctx, x, **kwargs): """ Negates the number *x*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** An mpmath number is returned:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fneg(2.5) mpf('-2.5') >>> fneg(-5+2j) mpc(real='5.0', imag='-2.0') Precise control over rounding is possible:: >>> x = fadd(2, 1e-100, exact=True) >>> fneg(x) mpf('-2.0') >>> fneg(x, rounding='f') mpf('-2.0000000000000004') Negating with and without roundoff:: >>> n = 200000000000000000000001 >>> print(int(-mpf(n))) -200000000000000016777216 >>> print(int(fneg(n))) -200000000000000016777216 >>> print(int(fneg(n, prec=log(n,2)+1))) -200000000000000000000001 >>> print(int(fneg(n, dps=log(n,10)+1))) -200000000000000000000001 >>> print(int(fneg(n, prec=inf))) -200000000000000000000001 >>> print(int(fneg(n, dps=inf))) -200000000000000000000001 >>> print(int(fneg(n, exact=True))) -200000000000000000000001 """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) if hasattr(x, '_mpf_'): return ctx.make_mpf(mpf_neg(x._mpf_, prec, rounding)) if hasattr(x, '_mpc_'): return ctx.make_mpc(mpc_neg(x._mpc_, prec, rounding)) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fadd(ctx, x, y, **kwargs): """ Adds the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. The default precision is the working precision of the context. You can specify a custom precision in bits by passing the *prec* keyword argument, or by providing an equivalent decimal precision with the *dps* keyword argument. If the precision is set to ``+inf``, or if the flag *exact=True* is passed, an exact addition with no rounding is performed. When the precision is finite, the optional *rounding* keyword argument specifies the direction of rounding. Valid options are ``'n'`` for nearest (default), ``'f'`` for floor, ``'c'`` for ceiling, ``'d'`` for down, ``'u'`` for up. **Examples** Using :func:`~mpmath.fadd` with precision and rounding control:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fadd(2, 1e-20) mpf('2.0') >>> fadd(2, 1e-20, rounding='u') mpf('2.0000000000000004') >>> nprint(fadd(2, 1e-20, prec=100), 25) 2.00000000000000000001 >>> nprint(fadd(2, 1e-20, dps=15), 25) 2.0 >>> nprint(fadd(2, 1e-20, dps=25), 25) 2.00000000000000000001 >>> nprint(fadd(2, 1e-20, exact=True), 25) 2.00000000000000000001 Exact addition avoids cancellation errors, enforcing familiar laws of numbers such as `x+y-x = y`, which don't hold in floating-point arithmetic with finite precision:: >>> x, y = mpf(2), mpf('1e-1000') >>> print(x + y - x) 0.0 >>> print(fadd(x, y, prec=inf) - x) 1.0e-1000 >>> print(fadd(x, y, exact=True) - x) 1.0e-1000 Exact addition can be inefficient and may be impossible to perform with large magnitude differences:: >>> fadd(1, '1e-100000000000000000000', prec=inf) Traceback (most recent call last): ... OverflowError: the exact result does not fit in memory """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) y = ctx.convert(y) try: if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_add(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_add_mpf(y._mpc_, x._mpf_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_add_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_add(x._mpc_, y._mpc_, prec, rounding)) except (ValueError, OverflowError): raise OverflowError(ctx._exact_overflow_msg) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fsub(ctx, x, y, **kwargs): """ Subtracts the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** Using :func:`~mpmath.fsub` with precision and rounding control:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fsub(2, 1e-20) mpf('2.0') >>> fsub(2, 1e-20, rounding='d') mpf('1.9999999999999998') >>> nprint(fsub(2, 1e-20, prec=100), 25) 1.99999999999999999999 >>> nprint(fsub(2, 1e-20, dps=15), 25) 2.0 >>> nprint(fsub(2, 1e-20, dps=25), 25) 1.99999999999999999999 >>> nprint(fsub(2, 1e-20, exact=True), 25) 1.99999999999999999999 Exact subtraction avoids cancellation errors, enforcing familiar laws of numbers such as `x-y+y = x`, which don't hold in floating-point arithmetic with finite precision:: >>> x, y = mpf(2), mpf('1e1000') >>> print(x - y + y) 0.0 >>> print(fsub(x, y, prec=inf) + y) 2.0 >>> print(fsub(x, y, exact=True) + y) 2.0 Exact addition can be inefficient and may be impossible to perform with large magnitude differences:: >>> fsub(1, '1e-100000000000000000000', prec=inf) Traceback (most recent call last): ... OverflowError: the exact result does not fit in memory """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) y = ctx.convert(y) try: if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_sub(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_sub((x._mpf_, fzero), y._mpc_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_sub_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_sub(x._mpc_, y._mpc_, prec, rounding)) except (ValueError, OverflowError): raise OverflowError(ctx._exact_overflow_msg) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fmul(ctx, x, y, **kwargs): """ Multiplies the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** The result is an mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fmul(2, 5.0) mpf('10.0') >>> fmul(0.5j, 0.5) mpc(real='0.0', imag='0.25') Avoiding roundoff:: >>> x, y = 10**10+1, 10**15+1 >>> print(x*y) 10000000001000010000000001 >>> print(mpf(x) * mpf(y)) 1.0000000001e+25 >>> print(int(mpf(x) * mpf(y))) 10000000001000011026399232 >>> print(int(fmul(x, y))) 10000000001000011026399232 >>> print(int(fmul(x, y, dps=25))) 10000000001000010000000001 >>> print(int(fmul(x, y, exact=True))) 10000000001000010000000001 Exact multiplication with complex numbers can be inefficient and may be impossible to perform with large magnitude differences between real and imaginary parts:: >>> x = 1+2j >>> y = mpc(2, '1e-100000000000000000000') >>> fmul(x, y) mpc(real='2.0', imag='4.0') >>> fmul(x, y, rounding='u') mpc(real='2.0', imag='4.0000000000000009') >>> fmul(x, y, exact=True) Traceback (most recent call last): ... OverflowError: the exact result does not fit in memory """ prec, rounding = ctx._parse_prec(kwargs) x = ctx.convert(x) y = ctx.convert(y) try: if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_mul(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_mul_mpf(y._mpc_, x._mpf_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_mul_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_mul(x._mpc_, y._mpc_, prec, rounding)) except (ValueError, OverflowError): raise OverflowError(ctx._exact_overflow_msg) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def fdiv(ctx, x, y, **kwargs): """ Divides the numbers *x* and *y*, giving a floating-point result, optionally using a custom precision and rounding mode. See the documentation of :func:`~mpmath.fadd` for a detailed description of how to specify precision and rounding. **Examples** The result is an mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fdiv(3, 2) mpf('1.5') >>> fdiv(2, 3) mpf('0.66666666666666663') >>> fdiv(2+4j, 0.5) mpc(real='4.0', imag='8.0') The rounding direction and precision can be controlled:: >>> fdiv(2, 3, dps=3) # Should be accurate to at least 3 digits mpf('0.6666259765625') >>> fdiv(2, 3, rounding='d') mpf('0.66666666666666663') >>> fdiv(2, 3, prec=60) mpf('0.66666666666666667') >>> fdiv(2, 3, rounding='u') mpf('0.66666666666666674') Checking the error of a division by performing it at higher precision:: >>> fdiv(2, 3) - fdiv(2, 3, prec=100) mpf('-3.7007434154172148e-17') Unlike :func:`~mpmath.fadd`, :func:`~mpmath.fmul`, etc., exact division is not allowed since the quotient of two floating-point numbers generally does not have an exact floating-point representation. (In the future this might be changed to allow the case where the division is actually exact.) >>> fdiv(2, 3, exact=True) Traceback (most recent call last): ... ValueError: division is not an exact operation """ prec, rounding = ctx._parse_prec(kwargs) if not prec: raise ValueError("division is not an exact operation") x = ctx.convert(x) y = ctx.convert(y) if hasattr(x, '_mpf_'): if hasattr(y, '_mpf_'): return ctx.make_mpf(mpf_div(x._mpf_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_div((x._mpf_, fzero), y._mpc_, prec, rounding)) if hasattr(x, '_mpc_'): if hasattr(y, '_mpf_'): return ctx.make_mpc(mpc_div_mpf(x._mpc_, y._mpf_, prec, rounding)) if hasattr(y, '_mpc_'): return ctx.make_mpc(mpc_div(x._mpc_, y._mpc_, prec, rounding)) raise ValueError("Arguments need to be mpf or mpc compatible numbers") def nint_distance(ctx, x): r""" Return `(n,d)` where `n` is the nearest integer to `x` and `d` is an estimate of `\log_2(|x-n|)`. If `d < 0`, `-d` gives the precision (measured in bits) lost to cancellation when computing `x-n`. >>> from mpmath import * >>> n, d = nint_distance(5) >>> print(n); print(d) 5 -inf >>> n, d = nint_distance(mpf(5)) >>> print(n); print(d) 5 -inf >>> n, d = nint_distance(mpf(5.00000001)) >>> print(n); print(d) 5 -26 >>> n, d = nint_distance(mpf(4.99999999)) >>> print(n); print(d) 5 -26 >>> n, d = nint_distance(mpc(5,10)) >>> print(n); print(d) 5 4 >>> n, d = nint_distance(mpc(5,0.000001)) >>> print(n); print(d) 5 -19 """ typx = type(x) if typx in int_types: return int(x), ctx.ninf elif typx is rational.mpq: p, q = x._mpq_ n, r = divmod(p, q) if 2*r >= q: n += 1 elif not r: return n, ctx.ninf # log(p/q-n) = log((p-nq)/q) = log(p-nq) - log(q) d = bitcount(abs(p-n*q)) - bitcount(q) return n, d if hasattr(x, "_mpf_"): re = x._mpf_ im_dist = ctx.ninf elif hasattr(x, "_mpc_"): re, im = x._mpc_ isign, iman, iexp, ibc = im if iman: im_dist = iexp + ibc elif im == fzero: im_dist = ctx.ninf else: raise ValueError("requires a finite number") else: x = ctx.convert(x) if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): return ctx.nint_distance(x) else: raise TypeError("requires an mpf/mpc") sign, man, exp, bc = re mag = exp+bc # |x| < 0.5 if mag < 0: n = 0 re_dist = mag elif man: # exact integer if exp >= 0: n = man << exp re_dist = ctx.ninf # exact half-integer elif exp == -1: n = (man>>1)+1 re_dist = 0 else: d = (-exp-1) t = man >> d if t & 1: t += 1 man = (t<>1 # int(t)>>1 re_dist = exp+bitcount(man) if sign: n = -n elif re == fzero: re_dist = ctx.ninf n = 0 else: raise ValueError("requires a finite number") return n, max(re_dist, im_dist) def fprod(ctx, factors): r""" Calculates a product containing a finite number of factors (for infinite products, see :func:`~mpmath.nprod`). The factors will be converted to mpmath numbers. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fprod([1, 2, 0.5, 7]) mpf('7.0') """ orig = ctx.prec try: v = ctx.one for p in factors: v *= p finally: ctx.prec = orig return +v def rand(ctx): """ Returns an ``mpf`` with value chosen randomly from `[0, 1)`. The number of randomly generated bits in the mantissa is equal to the working precision. """ return ctx.make_mpf(mpf_rand(ctx._prec)) def fraction(ctx, p, q): """ Given Python integers `(p, q)`, returns a lazy ``mpf`` representing the fraction `p/q`. The value is updated with the precision. >>> from mpmath import * >>> mp.dps = 15 >>> a = fraction(1,100) >>> b = mpf(1)/100 >>> print(a); print(b) 0.01 0.01 >>> mp.dps = 30 >>> print(a); print(b) # a will be accurate 0.01 0.0100000000000000002081668171172 >>> mp.dps = 15 """ return ctx.constant(lambda prec, rnd: from_rational(p, q, prec, rnd), '%s/%s' % (p, q)) def absmin(ctx, x): return abs(ctx.convert(x)) def absmax(ctx, x): return abs(ctx.convert(x)) def _as_points(ctx, x): # XXX: remove this? if hasattr(x, '_mpi_'): a, b = x._mpi_ return [ctx.make_mpf(a), ctx.make_mpf(b)] return x ''' def _zetasum(ctx, s, a, b): """ Computes sum of k^(-s) for k = a, a+1, ..., b with a, b both small integers. """ a = int(a) b = int(b) s = ctx.convert(s) prec, rounding = ctx._prec_rounding if hasattr(s, '_mpf_'): v = ctx.make_mpf(libmp.mpf_zetasum(s._mpf_, a, b, prec)) elif hasattr(s, '_mpc_'): v = ctx.make_mpc(libmp.mpc_zetasum(s._mpc_, a, b, prec)) return v ''' def _zetasum_fast(ctx, s, a, n, derivatives=[0], reflect=False): if not (ctx.isint(a) and hasattr(s, "_mpc_")): raise NotImplementedError a = int(a) prec = ctx._prec xs, ys = libmp.mpc_zetasum(s._mpc_, a, n, derivatives, reflect, prec) xs = [ctx.make_mpc(x) for x in xs] ys = [ctx.make_mpc(y) for y in ys] return xs, ys class PrecisionManager: def __init__(self, ctx, precfun, dpsfun, normalize_output=False): self.ctx = ctx self.precfun = precfun self.dpsfun = dpsfun self.normalize_output = normalize_output def __call__(self, f): def g(*args, **kwargs): orig = self.ctx.prec try: if self.precfun: self.ctx.prec = self.precfun(self.ctx.prec) else: self.ctx.dps = self.dpsfun(self.ctx.dps) if self.normalize_output: v = f(*args, **kwargs) if type(v) is tuple: return tuple([+a for a in v]) return +v else: return f(*args, **kwargs) finally: self.ctx.prec = orig g.__name__ = f.__name__ g.__doc__ = f.__doc__ return g def __enter__(self): self.origp = self.ctx.prec if self.precfun: self.ctx.prec = self.precfun(self.ctx.prec) else: self.ctx.dps = self.dpsfun(self.ctx.dps) def __exit__(self, exc_type, exc_val, exc_tb): self.ctx.prec = self.origp return False if __name__ == '__main__': import doctest doctest.testmod() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/visualization.py0000644000175000017500000002177412014170666025653 0ustar georgeskgeorgesk""" Plotting (requires matplotlib) """ from colorsys import hsv_to_rgb, hls_to_rgb from .libmp import NoConvergence from .libmp.backend import xrange class VisualizationMethods(object): plot_ignore = (ValueError, ArithmeticError, ZeroDivisionError, NoConvergence) def plot(ctx, f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None, singularities=[], axes=None): r""" Shows a simple 2D plot of a function `f(x)` or list of functions `[f_0(x), f_1(x), \ldots, f_n(x)]` over a given interval specified by *xlim*. Some examples:: plot(lambda x: exp(x)*li(x), [1, 4]) plot([cos, sin], [-4, 4]) plot([fresnels, fresnelc], [-4, 4]) plot([sqrt, cbrt], [-4, 4]) plot(lambda t: zeta(0.5+t*j), [-20, 20]) plot([floor, ceil, abs, sign], [-5, 5]) Points where the function raises a numerical exception or returns an infinite value are removed from the graph. Singularities can also be excluded explicitly as follows (useful for removing erroneous vertical lines):: plot(cot, ylim=[-5, 5]) # bad plot(cot, ylim=[-5, 5], singularities=[-pi, 0, pi]) # good For parts where the function assumes complex values, the real part is plotted with dashes and the imaginary part is plotted with dots. .. note :: This function requires matplotlib (pylab). """ if file: axes = None fig = None if not axes: import pylab fig = pylab.figure() axes = fig.add_subplot(111) if not isinstance(f, (tuple, list)): f = [f] a, b = xlim colors = ['b', 'r', 'g', 'm', 'k'] for n, func in enumerate(f): x = ctx.arange(a, b, (b-a)/float(points)) segments = [] segment = [] in_complex = False for i in xrange(len(x)): try: if i != 0: for sing in singularities: if x[i-1] <= sing and x[i] >= sing: raise ValueError v = func(x[i]) if ctx.isnan(v) or abs(v) > 1e300: raise ValueError if hasattr(v, "imag") and v.imag: re = float(v.real) im = float(v.imag) if not in_complex: in_complex = True segments.append(segment) segment = [] segment.append((float(x[i]), re, im)) else: if in_complex: in_complex = False segments.append(segment) segment = [] if hasattr(v, "real"): v = v.real segment.append((float(x[i]), v)) except ctx.plot_ignore: if segment: segments.append(segment) segment = [] if segment: segments.append(segment) for segment in segments: x = [s[0] for s in segment] y = [s[1] for s in segment] if not x: continue c = colors[n % len(colors)] if len(segment[0]) == 3: z = [s[2] for s in segment] axes.plot(x, y, '--'+c, linewidth=3) axes.plot(x, z, ':'+c, linewidth=3) else: axes.plot(x, y, c, linewidth=3) axes.set_xlim([float(_) for _ in xlim]) if ylim: axes.set_ylim([float(_) for _ in ylim]) axes.set_xlabel('x') axes.set_ylabel('f(x)') axes.grid(True) if fig: if file: pylab.savefig(file, dpi=dpi) else: pylab.show() def default_color_function(ctx, z): if ctx.isinf(z): return (1.0, 1.0, 1.0) if ctx.isnan(z): return (0.5, 0.5, 0.5) pi = 3.1415926535898 a = (float(ctx.arg(z)) + ctx.pi) / (2*ctx.pi) a = (a + 0.5) % 1.0 b = 1.0 - float(1/(1.0+abs(z)**0.3)) return hls_to_rgb(a, b, 0.8) def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None, verbose=False, file=None, dpi=None, axes=None): """ Plots the given complex-valued function *f* over a rectangular part of the complex plane specified by the pairs of intervals *re* and *im*. For example:: cplot(lambda z: z, [-2, 2], [-10, 10]) cplot(exp) cplot(zeta, [0, 1], [0, 50]) By default, the complex argument (phase) is shown as color (hue) and the magnitude is show as brightness. You can also supply a custom color function (*color*). This function should take a complex number as input and return an RGB 3-tuple containing floats in the range 0.0-1.0. To obtain a sharp image, the number of points may need to be increased to 100,000 or thereabout. Since evaluating the function that many times is likely to be slow, the 'verbose' option is useful to display progress. .. note :: This function requires matplotlib (pylab). """ if color is None: color = ctx.default_color_function import pylab if file: axes = None fig = None if not axes: fig = pylab.figure() axes = fig.add_subplot(111) rea, reb = re ima, imb = im dre = reb - rea dim = imb - ima M = int(ctx.sqrt(points*dre/dim)+1) N = int(ctx.sqrt(points*dim/dre)+1) x = pylab.linspace(rea, reb, M) y = pylab.linspace(ima, imb, N) # Note: we have to be careful to get the right rotation. # Test with these plots: # cplot(lambda z: z if z.real < 0 else 0) # cplot(lambda z: z if z.imag < 0 else 0) w = pylab.zeros((N, M, 3)) for n in xrange(N): for m in xrange(M): z = ctx.mpc(x[m], y[n]) try: v = color(f(z)) except ctx.plot_ignore: v = (0.5, 0.5, 0.5) w[n,m] = v if verbose: print(n, "of", N) rea, reb, ima, imb = [float(_) for _ in [rea, reb, ima, imb]] axes.imshow(w, extent=(rea, reb, ima, imb), origin='lower') axes.set_xlabel('Re(z)') axes.set_ylabel('Im(z)') if fig: if file: pylab.savefig(file, dpi=dpi) else: pylab.show() def splot(ctx, f, u=[-5,5], v=[-5,5], points=100, keep_aspect=True, \ wireframe=False, file=None, dpi=None, axes=None): """ Plots the surface defined by `f`. If `f` returns a single component, then this plots the surface defined by `z = f(x,y)` over the rectangular domain with `x = u` and `y = v`. If `f` returns three components, then this plots the parametric surface `x, y, z = f(u,v)` over the pairs of intervals `u` and `v`. For example, to plot a simple function:: >>> from mpmath import * >>> f = lambda x, y: sin(x+y)*cos(y) >>> splot(f, [-pi,pi], [-pi,pi]) # doctest: +SKIP Plotting a donut:: >>> r, R = 1, 2.5 >>> f = lambda u, v: [r*cos(u), (R+r*sin(u))*cos(v), (R+r*sin(u))*sin(v)] >>> splot(f, [0, 2*pi], [0, 2*pi]) # doctest: +SKIP .. note :: This function requires matplotlib (pylab) 0.98.5.3 or higher. """ import pylab import mpl_toolkits.mplot3d as mplot3d if file: axes = None fig = None if not axes: fig = pylab.figure() axes = mplot3d.axes3d.Axes3D(fig) ua, ub = u va, vb = v du = ub - ua dv = vb - va if not isinstance(points, (list, tuple)): points = [points, points] M, N = points u = pylab.linspace(ua, ub, M) v = pylab.linspace(va, vb, N) x, y, z = [pylab.zeros((M, N)) for i in xrange(3)] xab, yab, zab = [[0, 0] for i in xrange(3)] for n in xrange(N): for m in xrange(M): fdata = f(ctx.convert(u[m]), ctx.convert(v[n])) try: x[m,n], y[m,n], z[m,n] = fdata except TypeError: x[m,n], y[m,n], z[m,n] = u[m], v[n], fdata for c, cab in [(x[m,n], xab), (y[m,n], yab), (z[m,n], zab)]: if c < cab[0]: cab[0] = c if c > cab[1]: cab[1] = c if wireframe: axes.plot_wireframe(x, y, z, rstride=4, cstride=4) else: axes.plot_surface(x, y, z, rstride=4, cstride=4) axes.set_xlabel('x') axes.set_ylabel('y') axes.set_zlabel('z') if keep_aspect: dx, dy, dz = [cab[1] - cab[0] for cab in [xab, yab, zab]] maxd = max(dx, dy, dz) if dx < maxd: delta = maxd - dx axes.set_xlim3d(xab[0] - delta / 2.0, xab[1] + delta / 2.0) if dy < maxd: delta = maxd - dy axes.set_ylim3d(yab[0] - delta / 2.0, yab[1] + delta / 2.0) if dz < maxd: delta = maxd - dz axes.set_zlim3d(zab[0] - delta / 2.0, zab[1] + delta / 2.0) if fig: if file: pylab.savefig(file, dpi=dpi) else: pylab.show() VisualizationMethods.plot = plot VisualizationMethods.default_color_function = default_color_function VisualizationMethods.cplot = cplot VisualizationMethods.splot = splot wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/ctx_mp_python.py0000644000175000017500000010752012014170666025637 0ustar georgeskgeorgesk#from ctx_base import StandardBaseContext from .libmp.backend import basestring, exec_ from .libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, ComplexResult, to_pickable, from_pickable, normalize, from_int, from_float, from_str, to_int, to_float, to_str, from_rational, from_man_exp, fone, fzero, finf, fninf, fnan, mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, mpf_hash, mpf_rand, mpf_sum, bitcount, to_fixed, mpc_to_str, mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, mpc_mpf_div, mpf_pow, mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, mpf_glaisher, mpf_twinprime, mpf_mertens, int_types) from . import rational from . import function_docs new = object.__new__ class mpnumeric(object): """Base class for mpf and mpc.""" __slots__ = [] def __new__(cls, val): raise NotImplementedError class _mpf(mpnumeric): """ An mpf instance holds a real-valued floating-point number. mpf:s work analogously to Python floats, but support arbitrary-precision arithmetic. """ __slots__ = ['_mpf_'] def __new__(cls, val=fzero, **kwargs): """A new mpf can be created from a Python float, an int, a or a decimal string representing a number in floating-point format.""" prec, rounding = cls.context._prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if type(val) is cls: sign, man, exp, bc = val._mpf_ if (not man) and exp: return val v = new(cls) v._mpf_ = normalize(sign, man, exp, bc, prec, rounding) return v elif type(val) is tuple: if len(val) == 2: v = new(cls) v._mpf_ = from_man_exp(val[0], val[1], prec, rounding) return v if len(val) == 4: sign, man, exp, bc = val v = new(cls) v._mpf_ = normalize(sign, MPZ(man), exp, bc, prec, rounding) return v raise ValueError else: v = new(cls) v._mpf_ = mpf_pos(cls.mpf_convert_arg(val, prec, rounding), prec, rounding) return v @classmethod def mpf_convert_arg(cls, x, prec, rounding): if isinstance(x, int_types): return from_int(x) if isinstance(x, float): return from_float(x) if isinstance(x, basestring): return from_str(x, prec, rounding) if isinstance(x, cls.context.constant): return x.func(prec, rounding) if hasattr(x, '_mpf_'): return x._mpf_ if hasattr(x, '_mpmath_'): t = cls.context.convert(x._mpmath_(prec, rounding)) if hasattr(t, '_mpf_'): return t._mpf_ if hasattr(x, '_mpi_'): a, b = x._mpi_ if a == b: return a raise ValueError("can only create mpf from zero-width interval") raise TypeError("cannot create mpf from " + repr(x)) @classmethod def mpf_convert_rhs(cls, x): if isinstance(x, int_types): return from_int(x) if isinstance(x, float): return from_float(x) if isinstance(x, complex_types): return cls.context.mpc(x) if isinstance(x, rational.mpq): p, q = x._mpq_ return from_rational(p, q, cls.context.prec) if hasattr(x, '_mpf_'): return x._mpf_ if hasattr(x, '_mpmath_'): t = cls.context.convert(x._mpmath_(*cls.context._prec_rounding)) if hasattr(t, '_mpf_'): return t._mpf_ return t return NotImplemented @classmethod def mpf_convert_lhs(cls, x): x = cls.mpf_convert_rhs(x) if type(x) is tuple: return cls.context.make_mpf(x) return x man_exp = property(lambda self: self._mpf_[1:3]) man = property(lambda self: self._mpf_[1]) exp = property(lambda self: self._mpf_[2]) bc = property(lambda self: self._mpf_[3]) real = property(lambda self: self) imag = property(lambda self: self.context.zero) conjugate = lambda self: self def __getstate__(self): return to_pickable(self._mpf_) def __setstate__(self, val): self._mpf_ = from_pickable(val) def __repr__(s): if s.context.pretty: return str(s) return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits) def __str__(s): return to_str(s._mpf_, s.context._str_digits) def __hash__(s): return mpf_hash(s._mpf_) def __int__(s): return int(to_int(s._mpf_)) def __long__(s): return long(to_int(s._mpf_)) def __float__(s): return to_float(s._mpf_) def __complex__(s): return complex(float(s)) def __nonzero__(s): return s._mpf_ != fzero __bool__ = __nonzero__ def __abs__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpf_ = mpf_abs(s._mpf_, prec, rounding) return v def __pos__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpf_ = mpf_pos(s._mpf_, prec, rounding) return v def __neg__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpf_ = mpf_neg(s._mpf_, prec, rounding) return v def _cmp(s, t, func): if hasattr(t, '_mpf_'): t = t._mpf_ else: t = s.mpf_convert_rhs(t) if t is NotImplemented: return t return func(s._mpf_, t) def __cmp__(s, t): return s._cmp(t, mpf_cmp) def __lt__(s, t): return s._cmp(t, mpf_lt) def __gt__(s, t): return s._cmp(t, mpf_gt) def __le__(s, t): return s._cmp(t, mpf_le) def __ge__(s, t): return s._cmp(t, mpf_ge) def __ne__(s, t): v = s.__eq__(t) if v is NotImplemented: return v return not v def __rsub__(s, t): cls, new, (prec, rounding) = s._ctxdata if type(t) in int_types: v = new(cls) v._mpf_ = mpf_sub(from_int(t), s._mpf_, prec, rounding) return v t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t - s def __rdiv__(s, t): cls, new, (prec, rounding) = s._ctxdata if isinstance(t, int_types): v = new(cls) v._mpf_ = mpf_rdiv_int(t, s._mpf_, prec, rounding) return v t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t / s def __rpow__(s, t): t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t ** s def __rmod__(s, t): t = s.mpf_convert_lhs(t) if t is NotImplemented: return t return t % s def sqrt(s): return s.context.sqrt(s) def ae(s, t, rel_eps=None, abs_eps=None): return s.context.almosteq(s, t, rel_eps, abs_eps) def to_fixed(self, prec): return to_fixed(self._mpf_, prec) def __round__(self, *args): return round(float(self), *args) mpf_binary_op = """ def %NAME%(self, other): mpf, new, (prec, rounding) = self._ctxdata sval = self._mpf_ if hasattr(other, '_mpf_'): tval = other._mpf_ %WITH_MPF% ttype = type(other) if ttype in int_types: %WITH_INT% elif ttype is float: tval = from_float(other) %WITH_MPF% elif hasattr(other, '_mpc_'): tval = other._mpc_ mpc = type(other) %WITH_MPC% elif ttype is complex: tval = from_float(other.real), from_float(other.imag) mpc = self.context.mpc %WITH_MPC% if isinstance(other, mpnumeric): return NotImplemented try: other = mpf.context.convert(other, strings=False) except TypeError: return NotImplemented return self.%NAME%(other) """ return_mpf = "; obj = new(mpf); obj._mpf_ = val; return obj" return_mpc = "; obj = new(mpc); obj._mpc_ = val; return obj" mpf_pow_same = """ try: val = mpf_pow(sval, tval, prec, rounding) %s except ComplexResult: if mpf.context.trap_complex: raise mpc = mpf.context.mpc val = mpc_pow((sval, fzero), (tval, fzero), prec, rounding) %s """ % (return_mpf, return_mpc) def binary_op(name, with_mpf='', with_int='', with_mpc=''): code = mpf_binary_op code = code.replace("%WITH_INT%", with_int) code = code.replace("%WITH_MPC%", with_mpc) code = code.replace("%WITH_MPF%", with_mpf) code = code.replace("%NAME%", name) np = {} exec_(code, globals(), np) return np[name] _mpf.__eq__ = binary_op('__eq__', 'return mpf_eq(sval, tval)', 'return mpf_eq(sval, from_int(other))', 'return (tval[1] == fzero) and mpf_eq(tval[0], sval)') _mpf.__add__ = binary_op('__add__', 'val = mpf_add(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_add(sval, from_int(other), prec, rounding)' + return_mpf, 'val = mpc_add_mpf(tval, sval, prec, rounding)' + return_mpc) _mpf.__sub__ = binary_op('__sub__', 'val = mpf_sub(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_sub(sval, from_int(other), prec, rounding)' + return_mpf, 'val = mpc_sub((sval, fzero), tval, prec, rounding)' + return_mpc) _mpf.__mul__ = binary_op('__mul__', 'val = mpf_mul(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_mul_int(sval, other, prec, rounding)' + return_mpf, 'val = mpc_mul_mpf(tval, sval, prec, rounding)' + return_mpc) _mpf.__div__ = binary_op('__div__', 'val = mpf_div(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_div(sval, from_int(other), prec, rounding)' + return_mpf, 'val = mpc_mpf_div(sval, tval, prec, rounding)' + return_mpc) _mpf.__mod__ = binary_op('__mod__', 'val = mpf_mod(sval, tval, prec, rounding)' + return_mpf, 'val = mpf_mod(sval, from_int(other), prec, rounding)' + return_mpf, 'raise NotImplementedError("complex modulo")') _mpf.__pow__ = binary_op('__pow__', mpf_pow_same, 'val = mpf_pow_int(sval, other, prec, rounding)' + return_mpf, 'val = mpc_pow((sval, fzero), tval, prec, rounding)' + return_mpc) _mpf.__radd__ = _mpf.__add__ _mpf.__rmul__ = _mpf.__mul__ _mpf.__truediv__ = _mpf.__div__ _mpf.__rtruediv__ = _mpf.__rdiv__ class _constant(_mpf): """Represents a mathematical constant with dynamic precision. When printed or used in an arithmetic operation, a constant is converted to a regular mpf at the working precision. A regular mpf can also be obtained using the operation +x.""" def __new__(cls, func, name, docname=''): a = object.__new__(cls) a.name = name a.func = func a.__doc__ = getattr(function_docs, docname, '') return a def __call__(self, prec=None, dps=None, rounding=None): prec2, rounding2 = self.context._prec_rounding if not prec: prec = prec2 if not rounding: rounding = rounding2 if dps: prec = dps_to_prec(dps) return self.context.make_mpf(self.func(prec, rounding)) @property def _mpf_(self): prec, rounding = self.context._prec_rounding return self.func(prec, rounding) def __repr__(self): return "<%s: %s~>" % (self.name, self.context.nstr(self)) class _mpc(mpnumeric): """ An mpc represents a complex number using a pair of mpf:s (one for the real part and another for the imaginary part.) The mpc class behaves fairly similarly to Python's complex type. """ __slots__ = ['_mpc_'] def __new__(cls, real=0, imag=0): s = object.__new__(cls) if isinstance(real, complex_types): real, imag = real.real, real.imag elif hasattr(real, '_mpc_'): s._mpc_ = real._mpc_ return s real = cls.context.mpf(real) imag = cls.context.mpf(imag) s._mpc_ = (real._mpf_, imag._mpf_) return s real = property(lambda self: self.context.make_mpf(self._mpc_[0])) imag = property(lambda self: self.context.make_mpf(self._mpc_[1])) def __getstate__(self): return to_pickable(self._mpc_[0]), to_pickable(self._mpc_[1]) def __setstate__(self, val): self._mpc_ = from_pickable(val[0]), from_pickable(val[1]) def __repr__(s): if s.context.pretty: return str(s) r = repr(s.real)[4:-1] i = repr(s.imag)[4:-1] return "%s(real=%s, imag=%s)" % (type(s).__name__, r, i) def __str__(s): return "(%s)" % mpc_to_str(s._mpc_, s.context._str_digits) def __complex__(s): return mpc_to_complex(s._mpc_) def __pos__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpc_ = mpc_pos(s._mpc_, prec, rounding) return v def __abs__(s): prec, rounding = s.context._prec_rounding v = new(s.context.mpf) v._mpf_ = mpc_abs(s._mpc_, prec, rounding) return v def __neg__(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpc_ = mpc_neg(s._mpc_, prec, rounding) return v def conjugate(s): cls, new, (prec, rounding) = s._ctxdata v = new(cls) v._mpc_ = mpc_conjugate(s._mpc_, prec, rounding) return v def __nonzero__(s): return mpc_is_nonzero(s._mpc_) __bool__ = __nonzero__ def __hash__(s): return mpc_hash(s._mpc_) @classmethod def mpc_convert_lhs(cls, x): try: y = cls.context.convert(x) return y except TypeError: return NotImplemented def __eq__(s, t): if not hasattr(t, '_mpc_'): if isinstance(t, str): return False t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return s.real == t.real and s.imag == t.imag def __ne__(s, t): b = s.__eq__(t) if b is NotImplemented: return b return not b def _compare(*args): raise TypeError("no ordering relation is defined for complex numbers") __gt__ = _compare __le__ = _compare __gt__ = _compare __ge__ = _compare def __add__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_add_mpf(s._mpc_, t._mpf_, prec, rounding) return v v = new(cls) v._mpc_ = mpc_add(s._mpc_, t._mpc_, prec, rounding) return v def __sub__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_sub_mpf(s._mpc_, t._mpf_, prec, rounding) return v v = new(cls) v._mpc_ = mpc_sub(s._mpc_, t._mpc_, prec, rounding) return v def __mul__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): if isinstance(t, int_types): v = new(cls) v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) return v t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_mul_mpf(s._mpc_, t._mpf_, prec, rounding) return v t = s.mpc_convert_lhs(t) v = new(cls) v._mpc_ = mpc_mul(s._mpc_, t._mpc_, prec, rounding) return v def __div__(s, t): cls, new, (prec, rounding) = s._ctxdata if not hasattr(t, '_mpc_'): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t if hasattr(t, '_mpf_'): v = new(cls) v._mpc_ = mpc_div_mpf(s._mpc_, t._mpf_, prec, rounding) return v v = new(cls) v._mpc_ = mpc_div(s._mpc_, t._mpc_, prec, rounding) return v def __pow__(s, t): cls, new, (prec, rounding) = s._ctxdata if isinstance(t, int_types): v = new(cls) v._mpc_ = mpc_pow_int(s._mpc_, t, prec, rounding) return v t = s.mpc_convert_lhs(t) if t is NotImplemented: return t v = new(cls) if hasattr(t, '_mpf_'): v._mpc_ = mpc_pow_mpf(s._mpc_, t._mpf_, prec, rounding) else: v._mpc_ = mpc_pow(s._mpc_, t._mpc_, prec, rounding) return v __radd__ = __add__ def __rsub__(s, t): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t - s def __rmul__(s, t): cls, new, (prec, rounding) = s._ctxdata if isinstance(t, int_types): v = new(cls) v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) return v t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t * s def __rdiv__(s, t): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t / s def __rpow__(s, t): t = s.mpc_convert_lhs(t) if t is NotImplemented: return t return t ** s __truediv__ = __div__ __rtruediv__ = __rdiv__ def ae(s, t, rel_eps=None, abs_eps=None): return s.context.almosteq(s, t, rel_eps, abs_eps) complex_types = (complex, _mpc) class PythonMPContext: def __init__(ctx): ctx._prec_rounding = [53, round_nearest] ctx.mpf = type('mpf', (_mpf,), {}) ctx.mpc = type('mpc', (_mpc,), {}) ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding] ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding] ctx.mpf.context = ctx ctx.mpc.context = ctx ctx.constant = type('constant', (_constant,), {}) ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding] ctx.constant.context = ctx def make_mpf(ctx, v): a = new(ctx.mpf) a._mpf_ = v return a def make_mpc(ctx, v): a = new(ctx.mpc) a._mpc_ = v return a def default(ctx): ctx._prec = ctx._prec_rounding[0] = 53 ctx._dps = 15 ctx.trap_complex = False def _set_prec(ctx, n): ctx._prec = ctx._prec_rounding[0] = max(1, int(n)) ctx._dps = prec_to_dps(n) def _set_dps(ctx, n): ctx._prec = ctx._prec_rounding[0] = dps_to_prec(n) ctx._dps = max(1, int(n)) prec = property(lambda ctx: ctx._prec, _set_prec) dps = property(lambda ctx: ctx._dps, _set_dps) def convert(ctx, x, strings=True): """ Converts *x* to an ``mpf`` or ``mpc``. If *x* is of type ``mpf``, ``mpc``, ``int``, ``float``, ``complex``, the conversion will be performed losslessly. If *x* is a string, the result will be rounded to the present working precision. Strings representing fractions or complex numbers are permitted. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> mpmathify(3.5) mpf('3.5') >>> mpmathify('2.1') mpf('2.1000000000000001') >>> mpmathify('3/4') mpf('0.75') >>> mpmathify('2+3j') mpc(real='2.0', imag='3.0') """ if type(x) in ctx.types: return x if isinstance(x, int_types): return ctx.make_mpf(from_int(x)) if isinstance(x, float): return ctx.make_mpf(from_float(x)) if isinstance(x, complex): return ctx.make_mpc((from_float(x.real), from_float(x.imag))) prec, rounding = ctx._prec_rounding if isinstance(x, rational.mpq): p, q = x._mpq_ return ctx.make_mpf(from_rational(p, q, prec)) if strings and isinstance(x, basestring): try: _mpf_ = from_str(x, prec, rounding) return ctx.make_mpf(_mpf_) except ValueError: pass if hasattr(x, '_mpf_'): return ctx.make_mpf(x._mpf_) if hasattr(x, '_mpc_'): return ctx.make_mpc(x._mpc_) if hasattr(x, '_mpmath_'): return ctx.convert(x._mpmath_(prec, rounding)) return ctx._convert_fallback(x, strings) def isnan(ctx, x): """ Return *True* if *x* is a NaN (not-a-number), or for a complex number, whether either the real or complex part is NaN; otherwise return *False*:: >>> from mpmath import * >>> isnan(3.14) False >>> isnan(nan) True >>> isnan(mpc(3.14,2.72)) False >>> isnan(mpc(3.14,nan)) True """ if hasattr(x, "_mpf_"): return x._mpf_ == fnan if hasattr(x, "_mpc_"): return fnan in x._mpc_ if isinstance(x, int_types) or isinstance(x, rational.mpq): return False x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isnan(x) raise TypeError("isnan() needs a number as input") def isinf(ctx, x): """ Return *True* if the absolute value of *x* is infinite; otherwise return *False*:: >>> from mpmath import * >>> isinf(inf) True >>> isinf(-inf) True >>> isinf(3) False >>> isinf(3+4j) False >>> isinf(mpc(3,inf)) True >>> isinf(mpc(inf,3)) True """ if hasattr(x, "_mpf_"): return x._mpf_ in (finf, fninf) if hasattr(x, "_mpc_"): re, im = x._mpc_ return re in (finf, fninf) or im in (finf, fninf) if isinstance(x, int_types) or isinstance(x, rational.mpq): return False x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isinf(x) raise TypeError("isinf() needs a number as input") def isnormal(ctx, x): """ Determine whether *x* is "normal" in the sense of floating-point representation; that is, return *False* if *x* is zero, an infinity or NaN; otherwise return *True*. By extension, a complex number *x* is considered "normal" if its magnitude is normal:: >>> from mpmath import * >>> isnormal(3) True >>> isnormal(0) False >>> isnormal(inf); isnormal(-inf); isnormal(nan) False False False >>> isnormal(0+0j) False >>> isnormal(0+3j) True >>> isnormal(mpc(2,nan)) False """ if hasattr(x, "_mpf_"): return bool(x._mpf_[1]) if hasattr(x, "_mpc_"): re, im = x._mpc_ re_normal = bool(re[1]) im_normal = bool(im[1]) if re == fzero: return im_normal if im == fzero: return re_normal return re_normal and im_normal if isinstance(x, int_types) or isinstance(x, rational.mpq): return bool(x) x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isnormal(x) raise TypeError("isnormal() needs a number as input") def isint(ctx, x, gaussian=False): """ Return *True* if *x* is integer-valued; otherwise return *False*:: >>> from mpmath import * >>> isint(3) True >>> isint(mpf(3)) True >>> isint(3.2) False >>> isint(inf) False Optionally, Gaussian integers can be checked for:: >>> isint(3+0j) True >>> isint(3+2j) False >>> isint(3+2j, gaussian=True) True """ if isinstance(x, int_types): return True if hasattr(x, "_mpf_"): sign, man, exp, bc = xval = x._mpf_ return bool((man and exp >= 0) or xval == fzero) if hasattr(x, "_mpc_"): re, im = x._mpc_ rsign, rman, rexp, rbc = re isign, iman, iexp, ibc = im re_isint = (rman and rexp >= 0) or re == fzero if gaussian: im_isint = (iman and iexp >= 0) or im == fzero return re_isint and im_isint return re_isint and im == fzero if isinstance(x, rational.mpq): p, q = x._mpq_ return p % q == 0 x = ctx.convert(x) if hasattr(x, '_mpf_') or hasattr(x, '_mpc_'): return ctx.isint(x, gaussian) raise TypeError("isint() needs a number as input") def fsum(ctx, terms, absolute=False, squared=False): """ Calculates a sum containing a finite number of terms (for infinite series, see :func:`~mpmath.nsum`). The terms will be converted to mpmath numbers. For len(terms) > 2, this function is generally faster and produces more accurate results than the builtin Python function :func:`sum`. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fsum([1, 2, 0.5, 7]) mpf('10.5') With squared=True each term is squared, and with absolute=True the absolute value of each term is used. """ prec, rnd = ctx._prec_rounding real = [] imag = [] other = 0 for term in terms: reval = imval = 0 if hasattr(term, "_mpf_"): reval = term._mpf_ elif hasattr(term, "_mpc_"): reval, imval = term._mpc_ else: term = ctx.convert(term) if hasattr(term, "_mpf_"): reval = term._mpf_ elif hasattr(term, "_mpc_"): reval, imval = term._mpc_ else: if absolute: term = ctx.absmax(term) if squared: term = term**2 other += term continue if imval: if squared: if absolute: real.append(mpf_mul(reval,reval)) real.append(mpf_mul(imval,imval)) else: reval, imval = mpc_pow_int((reval,imval),2,prec+10) real.append(reval) imag.append(imval) elif absolute: real.append(mpc_abs((reval,imval), prec)) else: real.append(reval) imag.append(imval) else: if squared: reval = mpf_mul(reval, reval) elif absolute: reval = mpf_abs(reval) real.append(reval) s = mpf_sum(real, prec, rnd, absolute) if imag: s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) else: s = ctx.make_mpf(s) if other is 0: return s else: return s + other def fdot(ctx, A, B=None, conjugate=False): r""" Computes the dot product of the iterables `A` and `B`, .. math :: \sum_{k=0} A_k B_k. Alternatively, :func:`~mpmath.fdot` accepts a single iterable of pairs. In other words, ``fdot(A,B)`` and ``fdot(zip(A,B))`` are equivalent. The elements are automatically converted to mpmath numbers. With ``conjugate=True``, the elements in the second vector will be conjugated: .. math :: \sum_{k=0} A_k \overline{B_k} **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> A = [2, 1.5, 3] >>> B = [1, -1, 2] >>> fdot(A, B) mpf('6.5') >>> list(zip(A, B)) [(2, 1), (1.5, -1), (3, 2)] >>> fdot(_) mpf('6.5') >>> A = [2, 1.5, 3j] >>> B = [1+j, 3, -1-j] >>> fdot(A, B) mpc(real='9.5', imag='-1.0') >>> fdot(A, B, conjugate=True) mpc(real='3.5', imag='-5.0') """ if B: A = zip(A, B) prec, rnd = ctx._prec_rounding real = [] imag = [] other = 0 hasattr_ = hasattr types = (ctx.mpf, ctx.mpc) for a, b in A: if type(a) not in types: a = ctx.convert(a) if type(b) not in types: b = ctx.convert(b) a_real = hasattr_(a, "_mpf_") b_real = hasattr_(b, "_mpf_") if a_real and b_real: real.append(mpf_mul(a._mpf_, b._mpf_)) continue a_complex = hasattr_(a, "_mpc_") b_complex = hasattr_(b, "_mpc_") if a_real and b_complex: aval = a._mpf_ bre, bim = b._mpc_ if conjugate: bim = mpf_neg(bim) real.append(mpf_mul(aval, bre)) imag.append(mpf_mul(aval, bim)) elif b_real and a_complex: are, aim = a._mpc_ bval = b._mpf_ real.append(mpf_mul(are, bval)) imag.append(mpf_mul(aim, bval)) elif a_complex and b_complex: #re, im = mpc_mul(a._mpc_, b._mpc_, prec+20) are, aim = a._mpc_ bre, bim = b._mpc_ if conjugate: bim = mpf_neg(bim) real.append(mpf_mul(are, bre)) real.append(mpf_neg(mpf_mul(aim, bim))) imag.append(mpf_mul(are, bim)) imag.append(mpf_mul(aim, bre)) else: if conjugate: other += a*ctx.conj(b) else: other += a*b s = mpf_sum(real, prec, rnd) if imag: s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) else: s = ctx.make_mpf(s) if other is 0: return s else: return s + other def _wrap_libmp_function(ctx, mpf_f, mpc_f=None, mpi_f=None, doc=""): """ Given a low-level mpf_ function, and optionally similar functions for mpc_ and mpi_, defines the function as a context method. It is assumed that the return type is the same as that of the input; the exception is that propagation from mpf to mpc is possible by raising ComplexResult. """ def f(x, **kwargs): if type(x) not in ctx.types: x = ctx.convert(x) prec, rounding = ctx._prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if hasattr(x, '_mpf_'): try: return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding)) except ComplexResult: # Handle propagation to complex if ctx.trap_complex: raise return ctx.make_mpc(mpc_f((x._mpf_, fzero), prec, rounding)) elif hasattr(x, '_mpc_'): return ctx.make_mpc(mpc_f(x._mpc_, prec, rounding)) raise NotImplementedError("%s of a %s" % (name, type(x))) name = mpf_f.__name__[4:] f.__doc__ = function_docs.__dict__.get(name, "Computes the %s of x" % doc) return f # Called by SpecialFunctions.__init__() @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] prec = ctx.prec try: ctx.prec += 10 retval = f(ctx, *args, **kwargs) finally: ctx.prec = prec return +retval else: f_wrapped = f f_wrapped.__doc__ = function_docs.__dict__.get(name, f.__doc__) setattr(cls, name, f_wrapped) def _convert_param(ctx, x): if hasattr(x, "_mpc_"): v, im = x._mpc_ if im != fzero: return x, 'C' elif hasattr(x, "_mpf_"): v = x._mpf_ else: if type(x) in int_types: return int(x), 'Z' p = None if isinstance(x, tuple): p, q = x elif hasattr(x, '_mpq_'): p, q = x._mpq_ elif isinstance(x, basestring) and '/' in x: p, q = x.split('/') p = int(p) q = int(q) if p is not None: if not p % q: return p // q, 'Z' return ctx.mpq(p,q), 'Q' x = ctx.convert(x) if hasattr(x, "_mpc_"): v, im = x._mpc_ if im != fzero: return x, 'C' elif hasattr(x, "_mpf_"): v = x._mpf_ else: return x, 'U' sign, man, exp, bc = v if man: if exp >= -4: if sign: man = -man if exp >= 0: return int(man) << exp, 'Z' if exp >= -4: p, q = int(man), (1<<(-exp)) return ctx.mpq(p,q), 'Q' x = ctx.make_mpf(v) return x, 'R' elif not exp: return 0, 'Z' else: return x, 'U' def _mpf_mag(ctx, x): sign, man, exp, bc = x if man: return exp+bc if x == fzero: return ctx.ninf if x == finf or x == fninf: return ctx.inf return ctx.nan def mag(ctx, x): """ Quick logarithmic magnitude estimate of a number. Returns an integer or infinity `m` such that `|x| <= 2^m`. It is not guaranteed that `m` is an optimal bound, but it will never be too large by more than 2 (and probably not more than 1). **Examples** >>> from mpmath import * >>> mp.pretty = True >>> mag(10), mag(10.0), mag(mpf(10)), int(ceil(log(10,2))) (4, 4, 4, 4) >>> mag(10j), mag(10+10j) (4, 5) >>> mag(0.01), int(ceil(log(0.01,2))) (-6, -6) >>> mag(0), mag(inf), mag(-inf), mag(nan) (-inf, +inf, +inf, nan) """ if hasattr(x, "_mpf_"): return ctx._mpf_mag(x._mpf_) elif hasattr(x, "_mpc_"): r, i = x._mpc_ if r == fzero: return ctx._mpf_mag(i) if i == fzero: return ctx._mpf_mag(r) return 1+max(ctx._mpf_mag(r), ctx._mpf_mag(i)) elif isinstance(x, int_types): if x: return bitcount(abs(x)) return ctx.ninf elif isinstance(x, rational.mpq): p, q = x._mpq_ if p: return 1 + bitcount(abs(p)) - bitcount(q) return ctx.ninf else: x = ctx.convert(x) if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): return ctx.mag(x) else: raise TypeError("requires an mpf/mpc") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/math2.py0000644000175000017500000004420112014170666023753 0ustar georgeskgeorgesk""" This module complements the math and cmath builtin modules by providing fast machine precision versions of some additional functions (gamma, ...) and wrapping math/cmath functions so that they can be called with either real or complex arguments. """ import operator import math import cmath # Irrational (?) constants pi = 3.1415926535897932385 e = 2.7182818284590452354 sqrt2 = 1.4142135623730950488 sqrt5 = 2.2360679774997896964 phi = 1.6180339887498948482 ln2 = 0.69314718055994530942 ln10 = 2.302585092994045684 euler = 0.57721566490153286061 catalan = 0.91596559417721901505 khinchin = 2.6854520010653064453 apery = 1.2020569031595942854 logpi = 1.1447298858494001741 def _mathfun_real(f_real, f_complex): def f(x, **kwargs): if type(x) is float: return f_real(x) if type(x) is complex: return f_complex(x) try: x = float(x) return f_real(x) except (TypeError, ValueError): x = complex(x) return f_complex(x) f.__name__ = f_real.__name__ return f def _mathfun(f_real, f_complex): def f(x, **kwargs): if type(x) is complex: return f_complex(x) try: return f_real(float(x)) except (TypeError, ValueError): return f_complex(complex(x)) f.__name__ = f_real.__name__ return f def _mathfun_n(f_real, f_complex): def f(*args, **kwargs): try: return f_real(*(float(x) for x in args)) except (TypeError, ValueError): return f_complex(*(complex(x) for x in args)) f.__name__ = f_real.__name__ return f # Workaround for non-raising log and sqrt in Python 2.5 and 2.4 # on Unix system try: math.log(-2.0) def math_log(x): if x <= 0.0: raise ValueError("math domain error") return math.log(x) def math_sqrt(x): if x < 0.0: raise ValueError("math domain error") return math.sqrt(x) except (ValueError, TypeError): math_log = math.log math_sqrt = math.sqrt pow = _mathfun_n(operator.pow, lambda x, y: complex(x)**y) log = _mathfun_n(math_log, cmath.log) sqrt = _mathfun(math_sqrt, cmath.sqrt) exp = _mathfun_real(math.exp, cmath.exp) cos = _mathfun_real(math.cos, cmath.cos) sin = _mathfun_real(math.sin, cmath.sin) tan = _mathfun_real(math.tan, cmath.tan) acos = _mathfun(math.acos, cmath.acos) asin = _mathfun(math.asin, cmath.asin) atan = _mathfun_real(math.atan, cmath.atan) cosh = _mathfun_real(math.cosh, cmath.cosh) sinh = _mathfun_real(math.sinh, cmath.sinh) tanh = _mathfun_real(math.tanh, cmath.tanh) floor = _mathfun_real(math.floor, lambda z: complex(math.floor(z.real), math.floor(z.imag))) ceil = _mathfun_real(math.ceil, lambda z: complex(math.ceil(z.real), math.ceil(z.imag))) cos_sin = _mathfun_real(lambda x: (math.cos(x), math.sin(x)), lambda z: (cmath.cos(z), cmath.sin(z))) cbrt = _mathfun(lambda x: x**(1./3), lambda z: z**(1./3)) def nthroot(x, n): r = 1./n try: return float(x) ** r except (ValueError, TypeError): return complex(x) ** r def _sinpi_real(x): if x < 0: return -_sinpi_real(-x) n, r = divmod(x, 0.5) r *= pi n %= 4 if n == 0: return math.sin(r) if n == 1: return math.cos(r) if n == 2: return -math.sin(r) if n == 3: return -math.cos(r) def _cospi_real(x): if x < 0: x = -x n, r = divmod(x, 0.5) r *= pi n %= 4 if n == 0: return math.cos(r) if n == 1: return -math.sin(r) if n == 2: return -math.cos(r) if n == 3: return math.sin(r) def _sinpi_complex(z): if z.real < 0: return -_sinpi_complex(-z) n, r = divmod(z.real, 0.5) z = pi*complex(r, z.imag) n %= 4 if n == 0: return cmath.sin(z) if n == 1: return cmath.cos(z) if n == 2: return -cmath.sin(z) if n == 3: return -cmath.cos(z) def _cospi_complex(z): if z.real < 0: z = -z n, r = divmod(z.real, 0.5) z = pi*complex(r, z.imag) n %= 4 if n == 0: return cmath.cos(z) if n == 1: return -cmath.sin(z) if n == 2: return -cmath.cos(z) if n == 3: return cmath.sin(z) cospi = _mathfun_real(_cospi_real, _cospi_complex) sinpi = _mathfun_real(_sinpi_real, _sinpi_complex) def tanpi(x): try: return sinpi(x) / cospi(x) except OverflowError: if complex(x).imag > 10: return 1j if complex(x).imag < 10: return -1j raise def cotpi(x): try: return cospi(x) / sinpi(x) except OverflowError: if complex(x).imag > 10: return -1j if complex(x).imag < 10: return 1j raise INF = 1e300*1e300 NINF = -INF NAN = INF-INF EPS = 2.2204460492503131e-16 _exact_gamma = (INF, 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, 362880.0, 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0, 121645100408832000.0, 2432902008176640000.0) _max_exact_gamma = len(_exact_gamma)-1 # Lanczos coefficients used by the GNU Scientific Library _lanczos_g = 7 _lanczos_p = (0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7) def _gamma_real(x): _intx = int(x) if _intx == x: if _intx <= 0: #return (-1)**_intx * INF raise ZeroDivisionError("gamma function pole") if _intx <= _max_exact_gamma: return _exact_gamma[_intx] if x < 0.5: # TODO: sinpi return pi / (_sinpi_real(x)*_gamma_real(1-x)) else: x -= 1.0 r = _lanczos_p[0] for i in range(1, _lanczos_g+2): r += _lanczos_p[i]/(x+i) t = x + _lanczos_g + 0.5 return 2.506628274631000502417 * t**(x+0.5) * math.exp(-t) * r def _gamma_complex(x): if not x.imag: return complex(_gamma_real(x.real)) if x.real < 0.5: # TODO: sinpi return pi / (_sinpi_complex(x)*_gamma_complex(1-x)) else: x -= 1.0 r = _lanczos_p[0] for i in range(1, _lanczos_g+2): r += _lanczos_p[i]/(x+i) t = x + _lanczos_g + 0.5 return 2.506628274631000502417 * t**(x+0.5) * cmath.exp(-t) * r gamma = _mathfun_real(_gamma_real, _gamma_complex) def rgamma(x): try: return 1./gamma(x) except ZeroDivisionError: return x*0.0 def factorial(x): return gamma(x+1.0) def arg(x): if type(x) is float: return math.atan2(0.0,x) return math.atan2(x.imag,x.real) # XXX: broken for negatives def loggamma(x): if type(x) not in (float, complex): try: x = float(x) except (ValueError, TypeError): x = complex(x) try: xreal = x.real ximag = x.imag except AttributeError: # py2.5 xreal = x ximag = 0.0 # Reflection formula # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0003/ if xreal < 0.0: if abs(x) < 0.5: v = log(gamma(x)) if ximag == 0: v = v.conjugate() return v z = 1-x try: re = z.real im = z.imag except AttributeError: # py2.5 re = z im = 0.0 refloor = floor(re) if im == 0.0: imsign = 0 elif im < 0.0: imsign = -1 else: imsign = 1 return (-pi*1j)*abs(refloor)*(1-abs(imsign)) + logpi - \ log(sinpi(z-refloor)) - loggamma(z) + 1j*pi*refloor*imsign if x == 1.0 or x == 2.0: return x*0 p = 0. while abs(x) < 11: p -= log(x) x += 1.0 s = 0.918938533204672742 + (x-0.5)*log(x) - x r = 1./x r2 = r*r s += 0.083333333333333333333*r; r *= r2 s += -0.0027777777777777777778*r; r *= r2 s += 0.00079365079365079365079*r; r *= r2 s += -0.0005952380952380952381*r; r *= r2 s += 0.00084175084175084175084*r; r *= r2 s += -0.0019175269175269175269*r; r *= r2 s += 0.0064102564102564102564*r; r *= r2 s += -0.02955065359477124183*r return s + p _psi_coeff = [ 0.083333333333333333333, -0.0083333333333333333333, 0.003968253968253968254, -0.0041666666666666666667, 0.0075757575757575757576, -0.021092796092796092796, 0.083333333333333333333, -0.44325980392156862745, 3.0539543302701197438, -26.456212121212121212] def _digamma_real(x): _intx = int(x) if _intx == x: if _intx <= 0: raise ZeroDivisionError("polygamma pole") if x < 0.5: x = 1.0-x s = pi*cotpi(x) else: s = 0.0 while x < 10.0: s -= 1.0/x x += 1.0 x2 = x**-2 t = x2 for c in _psi_coeff: s -= c*t if t < 1e-20: break t *= x2 return s + math_log(x) - 0.5/x def _digamma_complex(x): if not x.imag: return complex(_digamma_real(x.real)) if x.real < 0.5: x = 1.0-x s = pi*cotpi(x) else: s = 0.0 while abs(x) < 10.0: s -= 1.0/x x += 1.0 x2 = x**-2 t = x2 for c in _psi_coeff: s -= c*t if abs(t) < 1e-20: break t *= x2 return s + cmath.log(x) - 0.5/x digamma = _mathfun_real(_digamma_real, _digamma_complex) # TODO: could implement complex erf and erfc here. Need # to find an accurate method (avoiding cancellation) # for approx. 1 < abs(x) < 9. _erfc_coeff_P = [ 1.0000000161203922312, 2.1275306946297962644, 2.2280433377390253297, 1.4695509105618423961, 0.66275911699770787537, 0.20924776504163751585, 0.045459713768411264339, 0.0063065951710717791934, 0.00044560259661560421715][::-1] _erfc_coeff_Q = [ 1.0000000000000000000, 3.2559100272784894318, 4.9019435608903239131, 4.4971472894498014205, 2.7845640601891186528, 1.2146026030046904138, 0.37647108453729465912, 0.080970149639040548613, 0.011178148899483545902, 0.00078981003831980423513][::-1] def _polyval(coeffs, x): p = coeffs[0] for c in coeffs[1:]: p = c + x*p return p def _erf_taylor(x): # Taylor series assuming 0 <= x <= 1 x2 = x*x s = t = x n = 1 while abs(t) > 1e-17: t *= x2/n s -= t/(n+n+1) n += 1 t *= x2/n s += t/(n+n+1) n += 1 return 1.1283791670955125739*s def _erfc_mid(x): # Rational approximation assuming 0 <= x <= 9 return exp(-x*x)*_polyval(_erfc_coeff_P,x)/_polyval(_erfc_coeff_Q,x) def _erfc_asymp(x): # Asymptotic expansion assuming x >= 9 x2 = x*x v = exp(-x2)/x*0.56418958354775628695 r = t = 0.5 / x2 s = 1.0 for n in range(1,22,4): s -= t t *= r * (n+2) s += t t *= r * (n+4) if abs(t) < 1e-17: break return s * v def erf(x): """ erf of a real number. """ x = float(x) if x != x: return x if x < 0.0: return -erf(-x) if x >= 1.0: if x >= 6.0: return 1.0 return 1.0 - _erfc_mid(x) return _erf_taylor(x) def erfc(x): """ erfc of a real number. """ x = float(x) if x != x: return x if x < 0.0: if x < -6.0: return 2.0 return 2.0-erfc(-x) if x > 9.0: return _erfc_asymp(x) if x >= 1.0: return _erfc_mid(x) return 1.0 - _erf_taylor(x) gauss42 = [\ (0.99839961899006235, 0.0041059986046490839), (-0.99839961899006235, 0.0041059986046490839), (0.9915772883408609, 0.009536220301748501), (-0.9915772883408609,0.009536220301748501), (0.97934250806374812, 0.014922443697357493), (-0.97934250806374812, 0.014922443697357493), (0.96175936533820439,0.020227869569052644), (-0.96175936533820439, 0.020227869569052644), (0.93892355735498811, 0.025422959526113047), (-0.93892355735498811,0.025422959526113047), (0.91095972490412735, 0.030479240699603467), (-0.91095972490412735, 0.030479240699603467), (0.87802056981217269,0.03536907109759211), (-0.87802056981217269, 0.03536907109759211), (0.8402859832618168, 0.040065735180692258), (-0.8402859832618168,0.040065735180692258), (0.7979620532554873, 0.044543577771965874), (-0.7979620532554873, 0.044543577771965874), (0.75127993568948048,0.048778140792803244), (-0.75127993568948048, 0.048778140792803244), (0.70049459055617114, 0.052746295699174064), (-0.70049459055617114,0.052746295699174064), (0.64588338886924779, 0.056426369358018376), (-0.64588338886924779, 0.056426369358018376), (0.58774459748510932, 0.059798262227586649), (-0.58774459748510932, 0.059798262227586649), (0.5263957499311922, 0.062843558045002565), (-0.5263957499311922, 0.062843558045002565), (0.46217191207042191, 0.065545624364908975), (-0.46217191207042191, 0.065545624364908975), (0.39542385204297503, 0.067889703376521934), (-0.39542385204297503, 0.067889703376521934), (0.32651612446541151, 0.069862992492594159), (-0.32651612446541151, 0.069862992492594159), (0.25582507934287907, 0.071454714265170971), (-0.25582507934287907, 0.071454714265170971), (0.18373680656485453, 0.072656175243804091), (-0.18373680656485453, 0.072656175243804091), (0.11064502720851986, 0.073460813453467527), (-0.11064502720851986, 0.073460813453467527), (0.036948943165351772, 0.073864234232172879), (-0.036948943165351772, 0.073864234232172879)] EI_ASYMP_CONVERGENCE_RADIUS = 40.0 def ei_asymp(z, _e1=False): r = 1./z s = t = 1.0 k = 1 while 1: t *= k*r s += t if abs(t) < 1e-16: break k += 1 v = s*exp(z)/z if _e1: if type(z) is complex: zreal = z.real zimag = z.imag else: zreal = z zimag = 0.0 if zimag == 0.0 and zreal > 0.0: v += pi*1j else: if type(z) is complex: if z.imag > 0: v += pi*1j if z.imag < 0: v -= pi*1j return v def ei_taylor(z, _e1=False): s = t = z k = 2 while 1: t = t*z/k term = t/k if abs(term) < 1e-17: break s += term k += 1 s += euler if _e1: s += log(-z) else: if type(z) is float or z.imag == 0.0: s += math_log(abs(z)) else: s += cmath.log(z) return s def ei(z, _e1=False): typez = type(z) if typez not in (float, complex): try: z = float(z) typez = float except (TypeError, ValueError): z = complex(z) typez = complex if not z: return -INF absz = abs(z) if absz > EI_ASYMP_CONVERGENCE_RADIUS: return ei_asymp(z, _e1) elif absz <= 2.0 or (typez is float and z > 0.0): return ei_taylor(z, _e1) # Integrate, starting from whichever is smaller of a Taylor # series value or an asymptotic series value if typez is complex and z.real > 0.0: zref = z / absz ref = ei_taylor(zref, _e1) else: zref = EI_ASYMP_CONVERGENCE_RADIUS * z / absz ref = ei_asymp(zref, _e1) C = (zref-z)*0.5 D = (zref+z)*0.5 s = 0.0 if type(z) is complex: _exp = cmath.exp else: _exp = math.exp for x,w in gauss42: t = C*x+D s += w*_exp(t)/t ref -= C*s return ref def e1(z): # hack to get consistent signs if the imaginary part if 0 # and signed typez = type(z) if type(z) not in (float, complex): try: z = float(z) typez = float except (TypeError, ValueError): z = complex(z) typez = complex if typez is complex and not z.imag: z = complex(z.real, 0.0) # end hack return -ei(-z, _e1=True) _zeta_int = [\ -0.5, 0.0, 1.6449340668482264365,1.2020569031595942854,1.0823232337111381915, 1.0369277551433699263,1.0173430619844491397,1.0083492773819228268, 1.0040773561979443394,1.0020083928260822144,1.0009945751278180853, 1.0004941886041194646,1.0002460865533080483,1.0001227133475784891, 1.0000612481350587048,1.0000305882363070205,1.0000152822594086519, 1.0000076371976378998,1.0000038172932649998,1.0000019082127165539, 1.0000009539620338728,1.0000004769329867878,1.0000002384505027277, 1.0000001192199259653,1.0000000596081890513,1.0000000298035035147, 1.0000000149015548284] _zeta_P = [-3.50000000087575873, -0.701274355654678147, -0.0672313458590012612, -0.00398731457954257841, -0.000160948723019303141, -4.67633010038383371e-6, -1.02078104417700585e-7, -1.68030037095896287e-9, -1.85231868742346722e-11][::-1] _zeta_Q = [1.00000000000000000, -0.936552848762465319, -0.0588835413263763741, -0.00441498861482948666, -0.000143416758067432622, -5.10691659585090782e-6, -9.58813053268913799e-8, -1.72963791443181972e-9, -1.83527919681474132e-11][::-1] _zeta_1 = [3.03768838606128127e-10, -1.21924525236601262e-8, 2.01201845887608893e-7, -1.53917240683468381e-6, -5.09890411005967954e-7, 0.000122464707271619326, -0.000905721539353130232, -0.00239315326074843037, 0.084239750013159168, 0.418938517907442414, 0.500000001921884009] _zeta_0 = [-3.46092485016748794e-10, -6.42610089468292485e-9, 1.76409071536679773e-7, -1.47141263991560698e-6, -6.38880222546167613e-7, 0.000122641099800668209, -0.000905894913516772796, -0.00239303348507992713, 0.0842396947501199816, 0.418938533204660256, 0.500000000000000052] def zeta(s): """ Riemann zeta function, real argument """ if not isinstance(s, (float, int)): try: s = float(s) except (ValueError, TypeError): try: s = complex(s) if not s.imag: return complex(zeta(s.real)) except (ValueError, TypeError): pass raise NotImplementedError if s == 1: raise ValueError("zeta(1) pole") if s >= 27: return 1.0 + 2.0**(-s) + 3.0**(-s) n = int(s) if n == s: if n >= 0: return _zeta_int[n] if not (n % 2): return 0.0 if s <= 0.0: return 2.**s*pi**(s-1)*_sinpi_real(0.5*s)*_gamma_real(1-s)*zeta(1-s) if s <= 2.0: if s <= 1.0: return _polyval(_zeta_0,s)/(s-1) return _polyval(_zeta_1,s)/(s-1) z = _polyval(_zeta_P,s) / _polyval(_zeta_Q,s) return 1.0 + 2.0**(-s) + 3.0**(-s) + 4.0**(-s)*z wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/rational.py0000644000175000017500000001267212014170666024560 0ustar georgeskgeorgeskimport operator import sys from .libmp import int_types, mpf_hash, bitcount, from_man_exp, HASH_MODULUS new = object.__new__ def create_reduced(p, q, _cache={}): key = p, q if key in _cache: return _cache[key] x, y = p, q while y: x, y = y, x % y if x != 1: p //= x q //= x v = new(mpq) v._mpq_ = p, q # Speedup integers, half-integers and other small fractions if q <= 4 and abs(key[0]) < 100: _cache[key] = v return v class mpq(object): """ Exact rational type, currently only intended for internal use. """ __slots__ = ["_mpq_"] def __new__(cls, p, q=1): if type(p) is tuple: p, q = p elif hasattr(p, '_mpq_'): p, q = p._mpq_ return create_reduced(p, q) def __repr__(s): return "mpq(%s,%s)" % s._mpq_ def __str__(s): return "(%s/%s)" % s._mpq_ def __int__(s): a, b = s._mpq_ return a // b def __nonzero__(s): return bool(s._mpq_[0]) __bool__ = __nonzero__ def __hash__(s): a, b = s._mpq_ if sys.version >= "3.2": inverse = pow(b, HASH_MODULUS-2, HASH_MODULUS) if not inverse: h = sys.hash_info.inf else: h = (abs(a) * inverse) % HASH_MODULUS if a < 0: h = -h if h == -1: h = -2 return h else: if b == 1: return hash(a) # Power of two: mpf compatible hash if not (b & (b-1)): return mpf_hash(from_man_exp(a, 1-bitcount(b))) return hash((a,b)) def __eq__(s, t): ttype = type(t) if ttype is mpq: return s._mpq_ == t._mpq_ if ttype in int_types: a, b = s._mpq_ if b != 1: return False return a == t return NotImplemented def __ne__(s, t): ttype = type(t) if ttype is mpq: return s._mpq_ != t._mpq_ if ttype in int_types: a, b = s._mpq_ if b != 1: return True return a != t return NotImplemented def _cmp(s, t, op): ttype = type(t) if ttype in int_types: a, b = s._mpq_ return op(a, t*b) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return op(a*d, b*c) return NotImplementedError def __lt__(s, t): return s._cmp(t, operator.lt) def __le__(s, t): return s._cmp(t, operator.le) def __gt__(s, t): return s._cmp(t, operator.gt) def __ge__(s, t): return s._cmp(t, operator.ge) def __abs__(s): a, b = s._mpq_ if a >= 0: return s v = new(mpq) v._mpq_ = -a, b return v def __neg__(s): a, b = s._mpq_ v = new(mpq) v._mpq_ = -a, b return v def __pos__(s): return s def __add__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*d+b*c, b*d) if ttype in int_types: a, b = s._mpq_ v = new(mpq) v._mpq_ = a+b*t, b return v return NotImplemented __radd__ = __add__ def __sub__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*d-b*c, b*d) if ttype in int_types: a, b = s._mpq_ v = new(mpq) v._mpq_ = a-b*t, b return v return NotImplemented def __rsub__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(b*c-a*d, b*d) if ttype in int_types: a, b = s._mpq_ v = new(mpq) v._mpq_ = b*t-a, b return v return NotImplemented def __mul__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*c, b*d) if ttype in int_types: a, b = s._mpq_ return create_reduced(a*t, b) return NotImplemented __rmul__ = __mul__ def __div__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(a*d, b*c) if ttype in int_types: a, b = s._mpq_ return create_reduced(a, b*t) return NotImplemented def __rdiv__(s, t): ttype = type(t) if ttype is mpq: a, b = s._mpq_ c, d = t._mpq_ return create_reduced(b*c, a*d) if ttype in int_types: a, b = s._mpq_ return create_reduced(b*t, a) return NotImplemented def __pow__(s, t): ttype = type(t) if ttype in int_types: a, b = s._mpq_ if t: if t < 0: a, b, t = b, a, -t v = new(mpq) v._mpq_ = a**t, b**t return v raise ZeroDivisionError return NotImplemented mpq_1 = mpq((1,1)) mpq_0 = mpq((0,1)) mpq_1_2 = mpq((1,2)) mpq_3_2 = mpq((3,2)) mpq_1_4 = mpq((1,4)) mpq_1_16 = mpq((1,16)) mpq_3_16 = mpq((3,16)) mpq_5_2 = mpq((5,2)) mpq_3_4 = mpq((3,4)) mpq_7_4 = mpq((7,4)) mpq_5_4 = mpq((5,4)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/ctx_base.py0000644000175000017500000003637312014170666024543 0ustar georgeskgeorgeskfrom operator import gt, lt from .libmp.backend import xrange from .functions.functions import SpecialFunctions from .functions.rszeta import RSCache from .calculus.quadrature import QuadratureMethods from .calculus.calculus import CalculusMethods from .calculus.optimization import OptimizationMethods from .calculus.odes import ODEMethods from .matrices.matrices import MatrixMethods from .matrices.calculus import MatrixCalculusMethods from .matrices.linalg import LinearAlgebraMethods from .identification import IdentificationMethods from .visualization import VisualizationMethods from . import libmp class Context(object): pass class StandardBaseContext(Context, SpecialFunctions, RSCache, QuadratureMethods, CalculusMethods, MatrixMethods, MatrixCalculusMethods, LinearAlgebraMethods, IdentificationMethods, OptimizationMethods, ODEMethods, VisualizationMethods): NoConvergence = libmp.NoConvergence ComplexResult = libmp.ComplexResult def __init__(ctx): ctx._aliases = {} # Call those that need preinitialization (e.g. for wrappers) SpecialFunctions.__init__(ctx) RSCache.__init__(ctx) QuadratureMethods.__init__(ctx) CalculusMethods.__init__(ctx) MatrixMethods.__init__(ctx) def _init_aliases(ctx): for alias, value in ctx._aliases.items(): try: setattr(ctx, alias, getattr(ctx, value)) except AttributeError: pass _fixed_precision = False # XXX verbose = False def warn(ctx, msg): print("Warning:", msg) def bad_domain(ctx, msg): raise ValueError(msg) def _re(ctx, x): if hasattr(x, "real"): return x.real return x def _im(ctx, x): if hasattr(x, "imag"): return x.imag return ctx.zero def _as_points(ctx, x): return x def fneg(ctx, x, **kwargs): return -ctx.convert(x) def fadd(ctx, x, y, **kwargs): return ctx.convert(x)+ctx.convert(y) def fsub(ctx, x, y, **kwargs): return ctx.convert(x)-ctx.convert(y) def fmul(ctx, x, y, **kwargs): return ctx.convert(x)*ctx.convert(y) def fdiv(ctx, x, y, **kwargs): return ctx.convert(x)/ctx.convert(y) def fsum(ctx, args, absolute=False, squared=False): if absolute: if squared: return sum((abs(x)**2 for x in args), ctx.zero) return sum((abs(x) for x in args), ctx.zero) if squared: return sum((x**2 for x in args), ctx.zero) return sum(args, ctx.zero) def fdot(ctx, xs, ys=None, conjugate=False): if ys is not None: xs = zip(xs, ys) if conjugate: cf = ctx.conj return sum((x*cf(y) for (x,y) in xs), ctx.zero) else: return sum((x*y for (x,y) in xs), ctx.zero) def fprod(ctx, args): prod = ctx.one for arg in args: prod *= arg return prod def nprint(ctx, x, n=6, **kwargs): """ Equivalent to ``print(nstr(x, n))``. """ print(ctx.nstr(x, n, **kwargs)) def chop(ctx, x, tol=None): """ Chops off small real or imaginary parts, or converts numbers close to zero to exact zeros. The input can be a single number or an iterable:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> chop(5+1e-10j, tol=1e-9) mpf('5.0') >>> nprint(chop([1.0, 1e-20, 3+1e-18j, -4, 2])) [1.0, 0.0, 3.0, -4.0, 2.0] The tolerance defaults to ``100*eps``. """ if tol is None: tol = 100*ctx.eps try: x = ctx.convert(x) absx = abs(x) if abs(x) < tol: return ctx.zero if ctx._is_complex_type(x): #part_tol = min(tol, absx*tol) part_tol = max(tol, absx*tol) if abs(x.imag) < part_tol: return x.real if abs(x.real) < part_tol: return ctx.mpc(0, x.imag) except TypeError: if isinstance(x, ctx.matrix): return x.apply(lambda a: ctx.chop(a, tol)) if hasattr(x, "__iter__"): return [ctx.chop(a, tol) for a in x] return x def almosteq(ctx, s, t, rel_eps=None, abs_eps=None): r""" Determine whether the difference between `s` and `t` is smaller than a given epsilon, either relatively or absolutely. Both a maximum relative difference and a maximum difference ('epsilons') may be specified. The absolute difference is defined as `|s-t|` and the relative difference is defined as `|s-t|/\max(|s|, |t|)`. If only one epsilon is given, both are set to the same value. If none is given, both epsilons are set to `2^{-p+m}` where `p` is the current working precision and `m` is a small integer. The default setting typically allows :func:`~mpmath.almosteq` to be used to check for mathematical equality in the presence of small rounding errors. **Examples** >>> from mpmath import * >>> mp.dps = 15 >>> almosteq(3.141592653589793, 3.141592653589790) True >>> almosteq(3.141592653589793, 3.141592653589700) False >>> almosteq(3.141592653589793, 3.141592653589700, 1e-10) True >>> almosteq(1e-20, 2e-20) True >>> almosteq(1e-20, 2e-20, rel_eps=0, abs_eps=0) False """ t = ctx.convert(t) if abs_eps is None and rel_eps is None: rel_eps = abs_eps = ctx.ldexp(1, -ctx.prec+4) if abs_eps is None: abs_eps = rel_eps elif rel_eps is None: rel_eps = abs_eps diff = abs(s-t) if diff <= abs_eps: return True abss = abs(s) abst = abs(t) if abss < abst: err = diff/abst else: err = diff/abss return err <= rel_eps def arange(ctx, *args): r""" This is a generalized version of Python's :func:`~mpmath.range` function that accepts fractional endpoints and step sizes and returns a list of ``mpf`` instances. Like :func:`~mpmath.range`, :func:`~mpmath.arange` can be called with 1, 2 or 3 arguments: ``arange(b)`` `[0, 1, 2, \ldots, x]` ``arange(a, b)`` `[a, a+1, a+2, \ldots, x]` ``arange(a, b, h)`` `[a, a+h, a+h, \ldots, x]` where `b-1 \le x < b` (in the third case, `b-h \le x < b`). Like Python's :func:`~mpmath.range`, the endpoint is not included. To produce ranges where the endpoint is included, :func:`~mpmath.linspace` is more convenient. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> arange(4) [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0')] >>> arange(1, 2, 0.25) [mpf('1.0'), mpf('1.25'), mpf('1.5'), mpf('1.75')] >>> arange(1, -1, -0.75) [mpf('1.0'), mpf('0.25'), mpf('-0.5')] """ if not len(args) <= 3: raise TypeError('arange expected at most 3 arguments, got %i' % len(args)) if not len(args) >= 1: raise TypeError('arange expected at least 1 argument, got %i' % len(args)) # set default a = 0 dt = 1 # interpret arguments if len(args) == 1: b = args[0] elif len(args) >= 2: a = args[0] b = args[1] if len(args) == 3: dt = args[2] a, b, dt = ctx.mpf(a), ctx.mpf(b), ctx.mpf(dt) assert a + dt != a, 'dt is too small and would cause an infinite loop' # adapt code for sign of dt if a > b: if dt > 0: return [] op = gt else: if dt < 0: return [] op = lt # create list result = [] i = 0 t = a while 1: t = a + dt*i i += 1 if op(t, b): result.append(t) else: break return result def linspace(ctx, *args, **kwargs): """ ``linspace(a, b, n)`` returns a list of `n` evenly spaced samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)`` is also valid. This function is often more convenient than :func:`~mpmath.arange` for partitioning an interval into subintervals, since the endpoint is included:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> linspace(1, 4, 4) [mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] You may also provide the keyword argument ``endpoint=False``:: >>> linspace(1, 4, 4, endpoint=False) [mpf('1.0'), mpf('1.75'), mpf('2.5'), mpf('3.25')] """ if len(args) == 3: a = ctx.mpf(args[0]) b = ctx.mpf(args[1]) n = int(args[2]) elif len(args) == 2: assert hasattr(args[0], '_mpi_') a = args[0].a b = args[0].b n = int(args[1]) else: raise TypeError('linspace expected 2 or 3 arguments, got %i' \ % len(args)) if n < 1: raise ValueError('n must be greater than 0') if not 'endpoint' in kwargs or kwargs['endpoint']: if n == 1: return [ctx.mpf(a)] step = (b - a) / ctx.mpf(n - 1) y = [i*step + a for i in xrange(n)] y[-1] = b else: step = (b - a) / ctx.mpf(n) y = [i*step + a for i in xrange(n)] return y def cos_sin(ctx, z, **kwargs): return ctx.cos(z, **kwargs), ctx.sin(z, **kwargs) def cospi_sinpi(ctx, z, **kwargs): return ctx.cospi(z, **kwargs), ctx.sinpi(z, **kwargs) def _default_hyper_maxprec(ctx, p): return int(1000 * p**0.25 + 4*p) _gcd = staticmethod(libmp.gcd) list_primes = staticmethod(libmp.list_primes) isprime = staticmethod(libmp.isprime) bernfrac = staticmethod(libmp.bernfrac) moebius = staticmethod(libmp.moebius) _ifac = staticmethod(libmp.ifac) _eulernum = staticmethod(libmp.eulernum) def sum_accurately(ctx, terms, check_step=1): prec = ctx.prec try: extraprec = 10 while 1: ctx.prec = prec + extraprec + 5 max_mag = ctx.ninf s = ctx.zero k = 0 for term in terms(): s += term if (not k % check_step) and term: term_mag = ctx.mag(term) max_mag = max(max_mag, term_mag) sum_mag = ctx.mag(s) if sum_mag - term_mag > ctx.prec: break k += 1 cancellation = max_mag - sum_mag if cancellation != cancellation: break if cancellation < extraprec or ctx._fixed_precision: break extraprec += min(ctx.prec, cancellation) return s finally: ctx.prec = prec def mul_accurately(ctx, factors, check_step=1): prec = ctx.prec try: extraprec = 10 while 1: ctx.prec = prec + extraprec + 5 max_mag = ctx.ninf one = ctx.one s = one k = 0 for factor in factors(): s *= factor term = factor - one if (not k % check_step): term_mag = ctx.mag(term) max_mag = max(max_mag, term_mag) sum_mag = ctx.mag(s-one) #if sum_mag - term_mag > ctx.prec: # break if -term_mag > ctx.prec: break k += 1 cancellation = max_mag - sum_mag if cancellation != cancellation: break if cancellation < extraprec or ctx._fixed_precision: break extraprec += min(ctx.prec, cancellation) return s finally: ctx.prec = prec def power(ctx, x, y): r"""Converts `x` and `y` to mpmath numbers and evaluates `x^y = \exp(y \log(x))`:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> power(2, 0.5) 1.41421356237309504880168872421 This shows the leading few digits of a large Mersenne prime (performing the exact calculation ``2**43112609-1`` and displaying the result in Python would be very slow):: >>> power(2, 43112609)-1 3.16470269330255923143453723949e+12978188 """ return ctx.convert(x) ** ctx.convert(y) def _zeta_int(ctx, n): return ctx.zeta(n) def maxcalls(ctx, f, N): """ Return a wrapped copy of *f* that raises ``NoConvergence`` when *f* has been called more than *N* times:: >>> from mpmath import * >>> mp.dps = 15 >>> f = maxcalls(sin, 10) >>> print(sum(f(n) for n in range(10))) 1.95520948210738 >>> f(10) Traceback (most recent call last): ... NoConvergence: maxcalls: function evaluated 10 times """ counter = [0] def f_maxcalls_wrapped(*args, **kwargs): counter[0] += 1 if counter[0] > N: raise ctx.NoConvergence("maxcalls: function evaluated %i times" % N) return f(*args, **kwargs) return f_maxcalls_wrapped def memoize(ctx, f): """ Return a wrapped copy of *f* that caches computed values, i.e. a memoized copy of *f*. Values are only reused if the cached precision is equal to or higher than the working precision:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> f = memoize(maxcalls(sin, 1)) >>> f(2) 0.909297426825682 >>> f(2) 0.909297426825682 >>> mp.dps = 25 >>> f(2) Traceback (most recent call last): ... NoConvergence: maxcalls: function evaluated 1 times """ f_cache = {} def f_cached(*args, **kwargs): if kwargs: key = args, tuple(kwargs.items()) else: key = args prec = ctx.prec if key in f_cache: cprec, cvalue = f_cache[key] if cprec >= prec: return +cvalue value = f(*args, **kwargs) f_cache[key] = (prec, value) return value f_cached.__name__ = f.__name__ f_cached.__doc__ = f.__doc__ return f_cached wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/0000755000175000017500000000000012014170666024375 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/functions.py0000644000175000017500000004176212014170666026771 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange class SpecialFunctions(object): """ This class implements special functions using high-level code. Elementary and some other functions (e.g. gamma function, basecase hypergeometric series) are assumed to be predefined by the context as "builtins" or "low-level" functions. """ defined_functions = {} # The series for the Jacobi theta functions converge for |q| < 1; # in the current implementation they throw a ValueError for # abs(q) > THETA_Q_LIM THETA_Q_LIM = 1 - 10**-7 def __init__(self): cls = self.__class__ for name in cls.defined_functions: f, wrap = cls.defined_functions[name] cls._wrap_specfun(name, f, wrap) self.mpq_1 = self._mpq((1,1)) self.mpq_0 = self._mpq((0,1)) self.mpq_1_2 = self._mpq((1,2)) self.mpq_3_2 = self._mpq((3,2)) self.mpq_1_4 = self._mpq((1,4)) self.mpq_1_16 = self._mpq((1,16)) self.mpq_3_16 = self._mpq((3,16)) self.mpq_5_2 = self._mpq((5,2)) self.mpq_3_4 = self._mpq((3,4)) self.mpq_7_4 = self._mpq((7,4)) self.mpq_5_4 = self._mpq((5,4)) self.mpq_1_3 = self._mpq((1,3)) self.mpq_2_3 = self._mpq((2,3)) self.mpq_4_3 = self._mpq((4,3)) self.mpq_1_6 = self._mpq((1,6)) self.mpq_5_6 = self._mpq((5,6)) self.mpq_5_3 = self._mpq((5,3)) self._misc_const_cache = {} self._aliases.update({ 'phase' : 'arg', 'conjugate' : 'conj', 'nthroot' : 'root', 'polygamma' : 'psi', 'hurwitz' : 'zeta', #'digamma' : 'psi0', #'trigamma' : 'psi1', #'tetragamma' : 'psi2', #'pentagamma' : 'psi3', 'fibonacci' : 'fib', 'factorial' : 'fac', }) self.zetazero_memoized = self.memoize(self.zetazero) # Default -- do nothing @classmethod def _wrap_specfun(cls, name, f, wrap): setattr(cls, name, f) # Optional fast versions of common functions in common cases. # If not overridden, default (generic hypergeometric series) # implementations will be used def _besselj(ctx, n, z): raise NotImplementedError def _erf(ctx, z): raise NotImplementedError def _erfc(ctx, z): raise NotImplementedError def _gamma_upper_int(ctx, z, a): raise NotImplementedError def _expint_int(ctx, n, z): raise NotImplementedError def _zeta(ctx, s): raise NotImplementedError def _zetasum_fast(ctx, s, a, n, derivatives, reflect): raise NotImplementedError def _ei(ctx, z): raise NotImplementedError def _e1(ctx, z): raise NotImplementedError def _ci(ctx, z): raise NotImplementedError def _si(ctx, z): raise NotImplementedError def _altzeta(ctx, s): raise NotImplementedError def defun_wrapped(f): SpecialFunctions.defined_functions[f.__name__] = f, True def defun(f): SpecialFunctions.defined_functions[f.__name__] = f, False def defun_static(f): setattr(SpecialFunctions, f.__name__, f) @defun_wrapped def cot(ctx, z): return ctx.one / ctx.tan(z) @defun_wrapped def sec(ctx, z): return ctx.one / ctx.cos(z) @defun_wrapped def csc(ctx, z): return ctx.one / ctx.sin(z) @defun_wrapped def coth(ctx, z): return ctx.one / ctx.tanh(z) @defun_wrapped def sech(ctx, z): return ctx.one / ctx.cosh(z) @defun_wrapped def csch(ctx, z): return ctx.one / ctx.sinh(z) @defun_wrapped def acot(ctx, z): return ctx.atan(ctx.one / z) @defun_wrapped def asec(ctx, z): return ctx.acos(ctx.one / z) @defun_wrapped def acsc(ctx, z): return ctx.asin(ctx.one / z) @defun_wrapped def acoth(ctx, z): return ctx.atanh(ctx.one / z) @defun_wrapped def asech(ctx, z): return ctx.acosh(ctx.one / z) @defun_wrapped def acsch(ctx, z): return ctx.asinh(ctx.one / z) @defun def sign(ctx, x): x = ctx.convert(x) if not x or ctx.isnan(x): return x if ctx._is_real_type(x): if x > 0: return ctx.one else: return -ctx.one return x / abs(x) @defun def agm(ctx, a, b=1): if b == 1: return ctx.agm1(a) a = ctx.convert(a) b = ctx.convert(b) return ctx._agm(a, b) @defun_wrapped def sinc(ctx, x): if ctx.isinf(x): return 1/x if not x: return x+1 return ctx.sin(x)/x @defun_wrapped def sincpi(ctx, x): if ctx.isinf(x): return 1/x if not x: return x+1 return ctx.sinpi(x)/(ctx.pi*x) # TODO: tests; improve implementation @defun_wrapped def expm1(ctx, x): if not x: return ctx.zero # exp(x) - 1 ~ x if ctx.mag(x) < -ctx.prec: return x + 0.5*x**2 # TODO: accurately eval the smaller of the real/imag parts return ctx.sum_accurately(lambda: iter([ctx.exp(x),-1]),1) @defun_wrapped def powm1(ctx, x, y): mag = ctx.mag one = ctx.one w = x**y - one M = mag(w) # Only moderate cancellation if M > -8: return w # Check for the only possible exact cases if not w: if (not y) or (x in (1, -1, 1j, -1j) and ctx.isint(y)): return w x1 = x - one magy = mag(y) lnx = ctx.ln(x) # Small y: x^y - 1 ~ log(x)*y + O(log(x)^2 * y^2) if magy + mag(lnx) < -ctx.prec: return lnx*y + (lnx*y)**2/2 # TODO: accurately eval the smaller of the real/imag part return ctx.sum_accurately(lambda: iter([x**y, -1]), 1) @defun def _rootof1(ctx, k, n): k = int(k) n = int(n) k %= n if not k: return ctx.one elif 2*k == n: return -ctx.one elif 4*k == n: return ctx.j elif 4*k == 3*n: return -ctx.j return ctx.expjpi(2*ctx.mpf(k)/n) @defun def root(ctx, x, n, k=0): n = int(n) x = ctx.convert(x) if k: # Special case: there is an exact real root if (n & 1 and 2*k == n-1) and (not ctx.im(x)) and (ctx.re(x) < 0): return -ctx.root(-x, n) # Multiply by root of unity prec = ctx.prec try: ctx.prec += 10 v = ctx.root(x, n, 0) * ctx._rootof1(k, n) finally: ctx.prec = prec return +v return ctx._nthroot(x, n) @defun def unitroots(ctx, n, primitive=False): gcd = ctx._gcd prec = ctx.prec try: ctx.prec += 10 if primitive: v = [ctx._rootof1(k,n) for k in range(n) if gcd(k,n) == 1] else: # TODO: this can be done *much* faster v = [ctx._rootof1(k,n) for k in range(n)] finally: ctx.prec = prec return [+x for x in v] @defun def arg(ctx, x): x = ctx.convert(x) re = ctx._re(x) im = ctx._im(x) return ctx.atan2(im, re) @defun def fabs(ctx, x): return abs(ctx.convert(x)) @defun def re(ctx, x): x = ctx.convert(x) if hasattr(x, "real"): # py2.5 doesn't have .real/.imag for all numbers return x.real return x @defun def im(ctx, x): x = ctx.convert(x) if hasattr(x, "imag"): # py2.5 doesn't have .real/.imag for all numbers return x.imag return ctx.zero @defun def conj(ctx, x): x = ctx.convert(x) try: return x.conjugate() except AttributeError: return x @defun def polar(ctx, z): return (ctx.fabs(z), ctx.arg(z)) @defun_wrapped def rect(ctx, r, phi): return r * ctx.mpc(*ctx.cos_sin(phi)) @defun def log(ctx, x, b=None): if b is None: return ctx.ln(x) wp = ctx.prec + 20 return ctx.ln(x, prec=wp) / ctx.ln(b, prec=wp) @defun def log10(ctx, x): return ctx.log(x, 10) @defun def fmod(ctx, x, y): return ctx.convert(x) % ctx.convert(y) @defun def degrees(ctx, x): return x / ctx.degree @defun def radians(ctx, x): return x * ctx.degree def _lambertw_special(ctx, z, k): # W(0,0) = 0; all other branches are singular if not z: if not k: return z return ctx.ninf + z if z == ctx.inf: if k == 0: return z else: return z + 2*k*ctx.pi*ctx.j if z == ctx.ninf: return (-z) + (2*k+1)*ctx.pi*ctx.j # Some kind of nan or complex inf/nan? return ctx.ln(z) import math import cmath def _lambertw_approx_hybrid(z, k): imag_sign = 0 if hasattr(z, "imag"): x = float(z.real) y = z.imag if y: imag_sign = (-1) ** (y < 0) y = float(y) else: x = float(z) y = 0.0 imag_sign = 0 # hack to work regardless of whether Python supports -0.0 if not y: y = 0.0 z = complex(x,y) if k == 0: if -4.0 < y < 4.0 and -1.0 < x < 2.5: if imag_sign: # Taylor series in upper/lower half-plane if y > 1.00: return (0.876+0.645j) + (0.118-0.174j)*(z-(0.75+2.5j)) if y > 0.25: return (0.505+0.204j) + (0.375-0.132j)*(z-(0.75+0.5j)) if y < -1.00: return (0.876-0.645j) + (0.118+0.174j)*(z-(0.75-2.5j)) if y < -0.25: return (0.505-0.204j) + (0.375+0.132j)*(z-(0.75-0.5j)) # Taylor series near -1 if x < -0.5: if imag_sign >= 0: return (-0.318+1.34j) + (-0.697-0.593j)*(z+1) else: return (-0.318-1.34j) + (-0.697+0.593j)*(z+1) # return real type r = -0.367879441171442 if (not imag_sign) and x > r: z = x # Singularity near -1/e if x < -0.2: return -1 + 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r) # Taylor series near 0 if x < 0.5: return z # Simple linear approximation return 0.2 + 0.3*z if (not imag_sign) and x > 0.0: L1 = math.log(x); L2 = math.log(L1) else: L1 = cmath.log(z); L2 = cmath.log(L1) elif k == -1: # return real type r = -0.367879441171442 if (not imag_sign) and r < x < 0.0: z = x if (imag_sign >= 0) and y < 0.1 and -0.6 < x < -0.2: return -1 - 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r) if (not imag_sign) and -0.2 <= x < 0.0: L1 = math.log(-x) return L1 - math.log(-L1) else: if imag_sign == -1 and (not y) and x < 0.0: L1 = cmath.log(z) - 3.1415926535897932j else: L1 = cmath.log(z) - 6.2831853071795865j L2 = cmath.log(L1) return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2) def _lambertw_series(ctx, z, k, tol): """ Return rough approximation for W_k(z) from an asymptotic series, sufficiently accurate for the Halley iteration to converge to the correct value. """ magz = ctx.mag(z) if (-10 < magz < 900) and (-1000 < k < 1000): # Near the branch point at -1/e if magz < 1 and abs(z+0.36787944117144) < 0.05: if k == 0 or (k == -1 and ctx._im(z) >= 0) or \ (k == 1 and ctx._im(z) < 0): delta = ctx.sum_accurately(lambda: [z, ctx.exp(-1)]) cancellation = -ctx.mag(delta) ctx.prec += cancellation # Use series given in Corless et al. p = ctx.sqrt(2*(ctx.e*z+1)) ctx.prec -= cancellation u = {0:ctx.mpf(-1), 1:ctx.mpf(1)} a = {0:ctx.mpf(2), 1:ctx.mpf(-1)} if k != 0: p = -p s = ctx.zero # The series converges, so we could use it directly, but unless # *extremely* close, it is better to just use the first few # terms to get a good approximation for the iteration for l in xrange(max(2,cancellation)): if l not in u: a[l] = ctx.fsum(u[j]*u[l+1-j] for j in xrange(2,l)) u[l] = (l-1)*(u[l-2]/2+a[l-2]/4)/(l+1)-a[l]/2-u[l-1]/(l+1) term = u[l] * p**l s += term if ctx.mag(term) < -tol: return s, True l += 1 ctx.prec += cancellation//2 return s, False if k == 0 or k == -1: return _lambertw_approx_hybrid(z, k), False if k == 0: if magz < -1: return z*(1-z), False L1 = ctx.ln(z) L2 = ctx.ln(L1) elif k == -1 and (not ctx._im(z)) and (-0.36787944117144 < ctx._re(z) < 0): L1 = ctx.ln(-z) return L1 - ctx.ln(-L1), False else: # This holds both as z -> 0 and z -> inf. # Relative error is O(1/log(z)). L1 = ctx.ln(z) + 2j*ctx.pi*k L2 = ctx.ln(L1) return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2), False @defun def lambertw(ctx, z, k=0): z = ctx.convert(z) k = int(k) if not ctx.isnormal(z): return _lambertw_special(ctx, z, k) prec = ctx.prec ctx.prec += 20 + ctx.mag(k or 1) wp = ctx.prec tol = wp - 5 w, done = _lambertw_series(ctx, z, k, tol) if not done: # Use Halley iteration to solve w*exp(w) = z two = ctx.mpf(2) for i in xrange(100): ew = ctx.exp(w) wew = w*ew wewz = wew-z wn = w - wewz/(wew+ew-(w+two)*wewz/(two*w+two)) if ctx.mag(wn-w) <= ctx.mag(wn) - tol: w = wn break else: w = wn if i == 100: ctx.warn("Lambert W iteration failed to converge for z = %s" % z) ctx.prec = prec return +w @defun_wrapped def bell(ctx, n, x=1): x = ctx.convert(x) if not n: if ctx.isnan(x): return x return type(x)(1) if ctx.isinf(x) or ctx.isinf(n) or ctx.isnan(x) or ctx.isnan(n): return x**n if n == 1: return x if n == 2: return x*(x+1) if x == 0: return ctx.sincpi(n) return _polyexp(ctx, n, x, True) / ctx.exp(x) def _polyexp(ctx, n, x, extra=False): def _terms(): if extra: yield ctx.sincpi(n) t = x k = 1 while 1: yield k**n * t k += 1 t = t*x/k return ctx.sum_accurately(_terms, check_step=4) @defun_wrapped def polyexp(ctx, s, z): if ctx.isinf(z) or ctx.isinf(s) or ctx.isnan(z) or ctx.isnan(s): return z**s if z == 0: return z*s if s == 0: return ctx.expm1(z) if s == 1: return ctx.exp(z)*z if s == 2: return ctx.exp(z)*z*(z+1) return _polyexp(ctx, s, z) @defun_wrapped def cyclotomic(ctx, n, z): n = int(n) assert n >= 0 p = ctx.one if n == 0: return p if n == 1: return z - p if n == 2: return z + p # Use divisor product representation. Unfortunately, this sometimes # includes singularities for roots of unity, which we have to cancel out. # Matching zeros/poles pairwise, we have (1-z^a)/(1-z^b) ~ a/b + O(z-1). a_prod = 1 b_prod = 1 num_zeros = 0 num_poles = 0 for d in range(1,n+1): if not n % d: w = ctx.moebius(n//d) # Use powm1 because it is important that we get 0 only # if it really is exactly 0 b = -ctx.powm1(z, d) if b: p *= b**w else: if w == 1: a_prod *= d num_zeros += 1 elif w == -1: b_prod *= d num_poles += 1 #print n, num_zeros, num_poles if num_zeros: if num_zeros > num_poles: p *= 0 else: p *= a_prod p /= b_prod return p @defun def mangoldt(ctx, n): r""" Evaluates the von Mangoldt function `\Lambda(n) = \log p` if `n = p^k` a power of a prime, and `\Lambda(n) = 0` otherwise. **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> [mangoldt(n) for n in range(-2,3)] [0.0, 0.0, 0.0, 0.0, 0.6931471805599453094172321] >>> mangoldt(6) 0.0 >>> mangoldt(7) 1.945910149055313305105353 >>> mangoldt(8) 0.6931471805599453094172321 >>> fsum(mangoldt(n) for n in range(101)) 94.04531122935739224600493 >>> fsum(mangoldt(n) for n in range(10001)) 10013.39669326311478372032 """ n = int(n) if n < 2: return ctx.zero if n % 2 == 0: # Must be a power of two if n & (n-1) == 0: return +ctx.ln2 else: return ctx.zero # TODO: the following could be generalized into a perfect # power testing function # --- # Look for a small factor for p in (3,5,7,11,13,17,19,23,29,31): if not n % p: q, r = n // p, 0 while q > 1: q, r = divmod(q, p) if r: return ctx.zero return ctx.ln(p) if ctx.isprime(n): return ctx.ln(n) # Obviously, we could use arbitrary-precision arithmetic for this... if n > 10**30: raise NotImplementedError k = 2 while 1: p = int(n**(1./k) + 0.5) if p < 2: return ctx.zero if p ** k == n: if ctx.isprime(p): return ctx.ln(p) k += 1 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/theta.py0000644000175000017500000011071012014170666026054 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun def _jacobi_theta2(ctx, z, q): extra1 = 10 extra2 = 20 # the loops below break when the fixed precision quantities # a and b go to zero; # right shifting small negative numbers by wp one obtains -1, not zero, # so the condition a**2 + b**2 > MIN is used to break the loops. MIN = 2 if z == ctx.zero: if (not ctx._im(q)): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 s = x2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp s += a s = (1 << (wp+1)) + (s << 1) s = ctx.ldexp(s, -wp) else: wp = ctx.prec + extra1 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp-1) are = bre = x2re aim = bim = x2im sre = (1< MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp sre += are sim += aim sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) else: if (not ctx._im(q)) and (not ctx._im(z)): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp s = c1 + ((a * cn) >> wp) while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp s += (a * cn) >> wp s = (s << 1) s = ctx.ldexp(s, -wp) s *= ctx.nthroot(q, 4) return s # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp sre = c1 + ((are * cn) >> wp) sim = ((aim * cn) >> wp) while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp sre += ((are * cn) >> wp) sim += ((aim * cn) >> wp) sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) #c2 = (c1*c1 - s1*s1) >> wp c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) #s2 = (c1 * s1) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre = c1re + ((a * cnre) >> wp) sim = c1im + ((a * cnim) >> wp) while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre += ((a * cnre) >> wp) sim += ((a * cnim) >> wp) sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im prec0 = ctx.prec ctx.prec = wp # cos(z), sin(z) with z complex c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 n = 1 termre = c1re termim = c1im sre = c1re + ((are * cnre - aim * cnim) >> wp) sim = c1im + ((are * cnim + aim * cnre) >> wp) n = 3 termre = ((are * cnre - aim * cnim) >> wp) termim = ((are * cnim + aim * cnre) >> wp) sre = c1re + ((are * cnre - aim * cnim) >> wp) sim = c1im + ((are * cnim + aim * cnre) >> wp) n = 5 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 termre = ((are * cnre - aim * cnim) >> wp) termim = ((aim * cnre + are * cnim) >> wp) sre += ((are * cnre - aim * cnim) >> wp) sim += ((aim * cnre + are * cnim) >> wp) n += 2 sre = (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) s *= ctx.nthroot(q, 4) return s @defun def _djacobi_theta2(ctx, z, q, nd): MIN = 2 extra1 = 10 extra2 = 20 if (not ctx._im(q)) and (not ctx._im(z)): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if (nd&1): s = s1 + ((a * sn * 3**nd) >> wp) else: s = c1 + ((a * cn * 3**nd) >> wp) n = 2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if nd&1: s += (a * sn * (2*n+1)**nd) >> wp else: s += (a * cn * (2*n+1)**nd) >> wp n += 1 s = -(s << 1) s = ctx.ldexp(s, -wp) # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) cn = c1 = ctx.to_fixed(c1, wp) sn = s1 = ctx.to_fixed(s1, wp) c2 = (c1*c1 - s1*s1) >> wp s2 = (c1 * s1) >> (wp - 1) cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if (nd&1): sre = s1 + ((are * sn * 3**nd) >> wp) sim = ((aim * sn * 3**nd) >> wp) else: sre = c1 + ((are * cn * 3**nd) >> wp) sim = ((aim * cn * 3**nd) >> wp) n = 5 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp if (nd&1): sre += ((are * sn * n**nd) >> wp) sim += ((aim * sn * n**nd) >> wp) else: sre += ((are * cn * n**nd) >> wp) sim += ((aim * cn * n**nd) >> wp) n += 2 sre = -(sre << 1) sim = -(sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) x2 = (x*x) >> wp a = b = x2 prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) #c2 = (c1*c1 - s1*s1) >> wp c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) #s2 = (c1 * s1) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre = s1re + ((a * snre * 3**nd) >> wp) sim = s1im + ((a * snim * 3**nd) >> wp) else: sre = c1re + ((a * cnre * 3**nd) >> wp) sim = c1im + ((a * cnim * 3**nd) >> wp) n = 5 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre += ((a * snre * n**nd) >> wp) sim += ((a * snim * n**nd) >> wp) else: sre += ((a * cnre * n**nd) >> wp) sim += ((a * cnim * n**nd) >> wp) n += 2 sre = -(sre << 1) sim = -(sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = x2re aim = bim = x2im prec0 = ctx.prec ctx.prec = wp # cos(2*z), sin(2*z) with z complex c1, s1 = ctx.cos_sin(z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre = s1re + (((are * snre - aim * snim) * 3**nd) >> wp) sim = s1im + (((are * snim + aim * snre)* 3**nd) >> wp) else: sre = c1re + (((are * cnre - aim * cnim) * 3**nd) >> wp) sim = c1im + (((are * cnim + aim * cnre)* 3**nd) >> wp) n = 5 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre += (((are * snre - aim * snim) * n**nd) >> wp) sim += (((aim * snre + are * snim) * n**nd) >> wp) else: sre += (((are * cnre - aim * cnim) * n**nd) >> wp) sim += (((aim * cnre + are * cnim) * n**nd) >> wp) n += 2 sre = -(sre << 1) sim = -(sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) s *= ctx.nthroot(q, 4) if (nd&1): return (-1)**(nd//2) * s else: return (-1)**(1 + nd//2) * s @defun def _jacobi_theta3(ctx, z, q): extra1 = 10 extra2 = 20 MIN = 2 if z == ctx.zero: if not ctx._im(q): wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) s = x a = b = x x2 = (x*x) >> wp while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp s += a s = (1 << wp) + (s << 1) s = ctx.ldexp(s, -wp) return s else: wp = ctx.prec + extra1 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) sre = are = bre = xre sim = aim = bim = xim while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp sre += are sim += aim sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s else: if (not ctx._im(q)) and (not ctx._im(z)): s = 0 wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 s += (a * cn) >> wp while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp s += (a * cn) >> wp s = (1 << wp) + (s << 1) s = ctx.ldexp(s, -wp) return s # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 sre = (are * cn) >> wp sim = (aim * cn) >> wp while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp sre += (are * cn) >> wp sim += (aim * cn) >> wp sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) sre = (a * cnre) >> wp sim = (a * cnim) >> wp while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre += (a * cnre) >> wp sim += (a * cnim) >> wp sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim prec0 = ctx.prec ctx.prec = wp # cos(2*z), sin(2*z) with z complex c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) sre = (are * cnre - aim * cnim) >> wp sim = (aim * cnre + are * cnim) >> wp while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 sre += (are * cnre - aim * cnim) >> wp sim += (aim * cnre + are * cnim) >> wp sre = (1 << wp) + (sre << 1) sim = (sim << 1) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) return s @defun def _djacobi_theta3(ctx, z, q, nd): """nd=1,2,3 order of the derivative with respect to z""" MIN = 2 extra1 = 10 extra2 = 20 if (not ctx._im(q)) and (not ctx._im(z)): s = 0 wp = ctx.prec + extra1 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 if (nd&1): s += (a * sn) >> wp else: s += (a * cn) >> wp n = 2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp if nd&1: s += (a * sn * n**nd) >> wp else: s += (a * cn * n**nd) >> wp n += 1 s = -(s << (nd+1)) s = ctx.ldexp(s, -wp) # case z real, q complex elif not ctx._im(z): wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) c1 = ctx.to_fixed(c1, wp) s1 = ctx.to_fixed(s1, wp) cn = c1 sn = s1 if (nd&1): sre = (are * sn) >> wp sim = (aim * sn) >> wp else: sre = (are * cn) >> wp sim = (aim * cn) >> wp n = 2 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp if nd&1: sre += (are * sn * n**nd) >> wp sim += (aim * sn * n**nd) >> wp else: sre += (are * cn * n**nd) >> wp sim += (aim * cn * n**nd) >> wp n += 1 sre = -(sre << (nd+1)) sim = -(sim << (nd+1)) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) #case z complex, q real elif not ctx._im(q): wp = ctx.prec + extra2 x = ctx.to_fixed(ctx._re(q), wp) a = b = x x2 = (x*x) >> wp prec0 = ctx.prec ctx.prec = wp c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) if (nd&1): sre = (a * snre) >> wp sim = (a * snim) >> wp else: sre = (a * cnre) >> wp sim = (a * cnim) >> wp n = 2 while abs(a) > MIN: b = (b*x2) >> wp a = (a*b) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if (nd&1): sre += (a * snre * n**nd) >> wp sim += (a * snim * n**nd) >> wp else: sre += (a * cnre * n**nd) >> wp sim += (a * cnim * n**nd) >> wp n += 1 sre = -(sre << (nd+1)) sim = -(sim << (nd+1)) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) # case z and q complex else: wp = ctx.prec + extra2 xre = ctx.to_fixed(ctx._re(q), wp) xim = ctx.to_fixed(ctx._im(q), wp) x2re = (xre*xre - xim*xim) >> wp x2im = (xre*xim) >> (wp - 1) are = bre = xre aim = bim = xim prec0 = ctx.prec ctx.prec = wp # cos(2*z), sin(2*z) with z complex c1, s1 = ctx.cos_sin(2*z) ctx.prec = prec0 cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) snre = s1re = ctx.to_fixed(ctx._re(s1), wp) snim = s1im = ctx.to_fixed(ctx._im(s1), wp) if (nd&1): sre = (are * snre - aim * snim) >> wp sim = (aim * snre + are * snim) >> wp else: sre = (are * cnre - aim * cnim) >> wp sim = (aim * cnre + are * cnim) >> wp n = 2 while are**2 + aim**2 > MIN: bre, bim = (bre * x2re - bim * x2im) >> wp, \ (bre * x2im + bim * x2re) >> wp are, aim = (are * bre - aim * bim) >> wp, \ (are * bim + aim * bre) >> wp t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp cnre = t1 cnim = t2 snre = t3 snim = t4 if(nd&1): sre += ((are * snre - aim * snim) * n**nd) >> wp sim += ((aim * snre + are * snim) * n**nd) >> wp else: sre += ((are * cnre - aim * cnim) * n**nd) >> wp sim += ((aim * cnre + are * cnim) * n**nd) >> wp n += 1 sre = -(sre << (nd+1)) sim = -(sim << (nd+1)) sre = ctx.ldexp(sre, -wp) sim = ctx.ldexp(sim, -wp) s = ctx.mpc(sre, sim) if (nd&1): return (-1)**(nd//2) * s else: return (-1)**(1 + nd//2) * s @defun def _jacobi_theta2a(ctx, z, q): """ case ctx._im(z) != 0 theta(2, z, q) = q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=-inf, inf) max term for minimum (2*n+1)*log(q).real - 2* ctx._im(z) n0 = int(ctx._im(z)/log(q).real - 1/2) theta(2, z, q) = q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=n0, inf) + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n, n0-1, -inf) """ n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) e2 = ctx.expj(2*z) e = e0 = ctx.expj((2*n+1)*z) a = q**(n*n + n) # leading term term = a * e s = term eps1 = ctx.eps*abs(term) while 1: n += 1 e = e * e2 term = q**(n*n + n) * e if abs(term) < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 term = q**(n*n + n) * e if abs(term) < eps1: break s += term s = s * ctx.nthroot(q, 4) return s @defun def _jacobi_theta3a(ctx, z, q): """ case ctx._im(z) != 0 theta3(z, q) = Sum(q**(n*n) * exp(j*2*n*z), n, -inf, inf) max term for n*abs(log(q).real) + ctx._im(z) ~= 0 n0 = int(- ctx._im(z)/abs(log(q).real)) """ n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) e2 = ctx.expj(2*z) e = e0 = ctx.expj(2*n*z) s = term = q**(n*n) * e eps1 = ctx.eps*abs(term) while 1: n += 1 e = e * e2 term = q**(n*n) * e if abs(term) < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 term = q**(n*n) * e if abs(term) < eps1: break s += term return s @defun def _djacobi_theta2a(ctx, z, q, nd): """ case ctx._im(z) != 0 dtheta(2, z, q, nd) = j* q**1/4 * Sum(q**(n*n + n) * (2*n+1)*exp(j*(2*n + 1)*z), n=-inf, inf) max term for (2*n0+1)*log(q).real - 2* ctx._im(z) ~= 0 n0 = int(ctx._im(z)/log(q).real - 1/2) """ n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) e2 = ctx.expj(2*z) e = e0 = ctx.expj((2*n + 1)*z) a = q**(n*n + n) # leading term term = (2*n+1)**nd * a * e s = term eps1 = ctx.eps*abs(term) while 1: n += 1 e = e * e2 term = (2*n+1)**nd * q**(n*n + n) * e if abs(term) < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 term = (2*n+1)**nd * q**(n*n + n) * e if abs(term) < eps1: break s += term return ctx.j**nd * s * ctx.nthroot(q, 4) @defun def _djacobi_theta3a(ctx, z, q, nd): """ case ctx._im(z) != 0 djtheta3(z, q, nd) = (2*j)**nd * Sum(q**(n*n) * n**nd * exp(j*2*n*z), n, -inf, inf) max term for minimum n*abs(log(q).real) + ctx._im(z) """ n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) e2 = ctx.expj(2*z) e = e0 = ctx.expj(2*n*z) a = q**(n*n) * e s = term = n**nd * a if n != 0: eps1 = ctx.eps*abs(term) else: eps1 = ctx.eps*abs(a) while 1: n += 1 e = e * e2 a = q**(n*n) * e term = n**nd * a if n != 0: aterm = abs(term) else: aterm = abs(a) if aterm < eps1: break s += term e = e0 e2 = ctx.expj(-2*z) n = n0 while 1: n -= 1 e = e * e2 a = q**(n*n) * e term = n**nd * a if n != 0: aterm = abs(term) else: aterm = abs(a) if aterm < eps1: break s += term return (2*ctx.j)**nd * s @defun def jtheta(ctx, n, z, q, derivative=0): if derivative: return ctx._djtheta(n, z, q, derivative) z = ctx.convert(z) q = ctx.convert(q) # Implementation note # If ctx._im(z) is close to zero, _jacobi_theta2 and _jacobi_theta3 # are used, # which compute the series starting from n=0 using fixed precision # numbers; # otherwise _jacobi_theta2a and _jacobi_theta3a are used, which compute # the series starting from n=n0, which is the largest term. # TODO: write _jacobi_theta2a and _jacobi_theta3a using fixed-point if abs(q) > ctx.THETA_Q_LIM: raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) extra = 10 if z: M = ctx.mag(z) if M > 5 or (n == 1 and M < -5): extra += 2*abs(M) cz = 0.5 extra2 = 50 prec0 = ctx.prec try: ctx.prec += extra if n == 1: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta2(z - ctx.pi/2, q) else: ctx.dps += 10 res = ctx._jacobi_theta2a(z - ctx.pi/2, q) else: res = ctx._jacobi_theta2(z - ctx.pi/2, q) elif n == 2: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta2(z, q) else: ctx.dps += 10 res = ctx._jacobi_theta2a(z, q) else: res = ctx._jacobi_theta2(z, q) elif n == 3: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta3(z, q) else: ctx.dps += 10 res = ctx._jacobi_theta3a(z, q) else: res = ctx._jacobi_theta3(z, q) elif n == 4: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._jacobi_theta3(z, -q) else: ctx.dps += 10 res = ctx._jacobi_theta3a(z, -q) else: res = ctx._jacobi_theta3(z, -q) else: raise ValueError finally: ctx.prec = prec0 return res @defun def _djtheta(ctx, n, z, q, derivative=1): z = ctx.convert(z) q = ctx.convert(q) nd = int(derivative) if abs(q) > ctx.THETA_Q_LIM: raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) extra = 10 + ctx.prec * nd // 10 if z: M = ctx.mag(z) if M > 5 or (n != 1 and M < -5): extra += 2*abs(M) cz = 0.5 extra2 = 50 prec0 = ctx.prec try: ctx.prec += extra if n == 1: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta2a(z - ctx.pi/2, q, nd) else: res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) elif n == 2: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta2(z, q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta2a(z, q, nd) else: res = ctx._djacobi_theta2(z, q, nd) elif n == 3: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta3(z, q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta3a(z, q, nd) else: res = ctx._djacobi_theta3(z, q, nd) elif n == 4: if ctx._im(z): if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): ctx.dps += extra2 res = ctx._djacobi_theta3(z, -q, nd) else: ctx.dps += 10 res = ctx._djacobi_theta3a(z, -q, nd) else: res = ctx._djacobi_theta3(z, -q, nd) else: raise ValueError finally: ctx.prec = prec0 return +res wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/qfunctions.py0000644000175000017500000001666212014170666027153 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun def qp(ctx, a, q=None, n=None, **kwargs): r""" Evaluates the q-Pochhammer symbol (or q-rising factorial) .. math :: (a; q)_n = \prod_{k=0}^{n-1} (1-a q^k) where `n = \infty` is permitted if `|q| < 1`. Called with two arguments, ``qp(a,q)`` computes `(a;q)_{\infty}`; with a single argument, ``qp(q)`` computes `(q;q)_{\infty}`. The special case .. math :: \phi(q) = (q; q)_{\infty} = \prod_{k=1}^{\infty} (1-q^k) = \sum_{k=-\infty}^{\infty} (-1)^k q^{(3k^2-k)/2} is also known as the Euler function, or (up to a factor `q^{-1/24}`) the Dirichlet eta function. **Examples** If `n` is a positive integer, the function amounts to a finite product:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qp(2,3,5) -725305.0 >>> fprod(1-2*3**k for k in range(5)) -725305.0 >>> qp(2,3,0) 1.0 Complex arguments are allowed:: >>> qp(2-1j, 0.75j) (0.4628842231660149089976379 + 4.481821753552703090628793j) The regular Pochhammer symbol `(a)_n` is obtained in the following limit as `q \to 1`:: >>> a, n = 4, 7 >>> limit(lambda q: qp(q**a,q,n) / (1-q)**n, 1) 604800.0 >>> rf(a,n) 604800.0 The Taylor series of the reciprocal Euler function gives the partition function `P(n)`, i.e. the number of ways of writing `n` as a sum of positive integers:: >>> taylor(lambda q: 1/qp(q), 0, 10) [1.0, 1.0, 2.0, 3.0, 5.0, 7.0, 11.0, 15.0, 22.0, 30.0, 42.0] Special values include:: >>> qp(0) 1.0 >>> findroot(diffun(qp), -0.4) # location of maximum -0.4112484791779547734440257 >>> qp(_) 1.228348867038575112586878 The q-Pochhammer symbol is related to the Jacobi theta functions. For example, the following identity holds:: >>> q = mpf(0.5) # arbitrary >>> qp(q) 0.2887880950866024212788997 >>> root(3,-2)*root(q,-24)*jtheta(2,pi/6,root(q,6)) 0.2887880950866024212788997 """ a = ctx.convert(a) if n is None: n = ctx.inf else: n = ctx.convert(n) assert n >= 0 if q is None: q = a else: q = ctx.convert(q) if n == 0: return ctx.one + 0*(a+q) infinite = (n == ctx.inf) same = (a == q) if infinite: if abs(q) >= 1: if same and (q == -1 or q == 1): return ctx.zero * q raise ValueError("q-function only defined for |q| < 1") elif q == 0: return ctx.one - a maxterms = kwargs.get('maxterms', 50*ctx.prec) if infinite and same: # Euler's pentagonal theorem def terms(): t = 1 yield t k = 1 x1 = q x2 = q**2 while 1: yield (-1)**k * x1 yield (-1)**k * x2 x1 *= q**(3*k+1) x2 *= q**(3*k+2) k += 1 if k > maxterms: raise ctx.NoConvergence return ctx.sum_accurately(terms) # return ctx.nprod(lambda k: 1-a*q**k, [0,n-1]) def factors(): k = 0 r = ctx.one while 1: yield 1 - a*r r *= q k += 1 if k >= n: raise StopIteration if k > maxterms: raise ctx.NoConvergence return ctx.mul_accurately(factors) @defun_wrapped def qgamma(ctx, z, q, **kwargs): r""" Evaluates the q-gamma function .. math :: \Gamma_q(z) = \frac{(q; q)_{\infty}}{(q^z; q)_{\infty}} (1-q)^{1-z}. **Examples** Evaluation for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qgamma(4,0.75) 4.046875 >>> qgamma(6,6) 121226245.0 >>> qgamma(3+4j, 0.5j) (0.1663082382255199834630088 + 0.01952474576025952984418217j) The q-gamma function satisfies a functional equation similar to that of the ordinary gamma function:: >>> q = mpf(0.25) >>> z = mpf(2.5) >>> qgamma(z+1,q) 1.428277424823760954685912 >>> (1-q**z)/(1-q)*qgamma(z,q) 1.428277424823760954685912 """ if abs(q) > 1: return ctx.qgamma(z,1/q)*q**((z-2)*(z-1)*0.5) return ctx.qp(q, q, None, **kwargs) / \ ctx.qp(q**z, q, None, **kwargs) * (1-q)**(1-z) @defun_wrapped def qfac(ctx, z, q, **kwargs): r""" Evaluates the q-factorial, .. math :: [n]_q! = (1+q)(1+q+q^2)\cdots(1+q+\cdots+q^{n-1}) or more generally .. math :: [z]_q! = \frac{(q;q)_z}{(1-q)^z}. **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qfac(0,0) 1.0 >>> qfac(4,3) 2080.0 >>> qfac(5,6) 121226245.0 >>> qfac(1+1j, 2+1j) (0.4370556551322672478613695 + 0.2609739839216039203708921j) """ if ctx.isint(z) and ctx._re(z) > 0: n = int(ctx._re(z)) return ctx.qp(q, q, n, **kwargs) / (1-q)**n return ctx.qgamma(z+1, q, **kwargs) @defun def qhyper(ctx, a_s, b_s, q, z, **kwargs): r""" Evaluates the basic hypergeometric series or hypergeometric q-series .. math :: \,_r\phi_s \left[\begin{matrix} a_1 & a_2 & \ldots & a_r \\ b_1 & b_2 & \ldots & b_s \end{matrix} ; q,z \right] = \sum_{n=0}^\infty \frac{(a_1;q)_n, \ldots, (a_r;q)_n} {(b_1;q)_n, \ldots, (b_s;q)_n} \left((-1)^n q^{n\choose 2}\right)^{1+s-r} \frac{z^n}{(q;q)_n} where `(a;q)_n` denotes the q-Pochhammer symbol (see :func:`~mpmath.qp`). **Examples** Evaluation works for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qhyper([0.5], [2.25], 0.25, 4) -0.1975849091263356009534385 >>> qhyper([0.5], [2.25], 0.25-0.25j, 4) (2.806330244925716649839237 + 3.568997623337943121769938j) >>> qhyper([1+j], [2,3+0.5j], 0.25, 3+4j) (9.112885171773400017270226 - 1.272756997166375050700388j) Comparing with a summation of the defining series, using :func:`~mpmath.nsum`:: >>> b, q, z = 3, 0.25, 0.5 >>> qhyper([], [b], q, z) 0.6221136748254495583228324 >>> nsum(lambda n: z**n / qp(q,q,n)/qp(b,q,n) * q**(n*(n-1)), [0,inf]) 0.6221136748254495583228324 """ #a_s = [ctx._convert_param(a)[0] for a in a_s] #b_s = [ctx._convert_param(b)[0] for b in b_s] #q = ctx._convert_param(q)[0] a_s = [ctx.convert(a) for a in a_s] b_s = [ctx.convert(b) for b in b_s] q = ctx.convert(q) z = ctx.convert(z) r = len(a_s) s = len(b_s) d = 1+s-r maxterms = kwargs.get('maxterms', 50*ctx.prec) def terms(): t = ctx.one yield t qk = 1 k = 0 x = 1 while 1: for a in a_s: p = 1 - a*qk t *= p for b in b_s: p = 1 - b*qk if not p: raise ValueError t /= p t *= z x *= (-1)**d * qk ** d qk *= q t /= (1 - qk) k += 1 yield t * x if k > maxterms: raise ctx.NoConvergence return ctx.sum_accurately(terms) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/rszeta.py0000644000175000017500000013222212014170666026261 0ustar georgeskgeorgesk""" --------------------------------------------------------------------- .. sectionauthor:: Juan Arias de Reyna This module implements zeta-related functions using the Riemann-Siegel expansion: zeta_offline(s,k=0) * coef(J, eps): Need in the computation of Rzeta(s,k) * Rzeta_simul(s, der=0) computes Rzeta^(k)(s) and Rzeta^(k)(1-s) simultaneously for 0 <= k <= der. Used by zeta_offline and z_offline * Rzeta_set(s, derivatives) computes Rzeta^(k)(s) for given derivatives, used by z_half(t,k) and zeta_half * z_offline(w,k): Z(w) and its derivatives of order k <= 4 * z_half(t,k): Z(t) (Riemann Siegel function) and its derivatives of order k <= 4 * zeta_offline(s): zeta(s) and its derivatives of order k<= 4 * zeta_half(1/2+it,k): zeta(s) and its derivatives of order k<= 4 * rs_zeta(s,k=0) Computes zeta^(k)(s) Unifies zeta_half and zeta_offline * rs_z(w,k=0) Computes Z^(k)(w) Unifies z_offline and z_half ---------------------------------------------------------------------- This program uses Riemann-Siegel expansion even to compute zeta(s) on points s = sigma + i t with sigma arbitrary not necessarily equal to 1/2. It is founded on a new deduction of the formula, with rigorous and sharp bounds for the terms and rest of this expansion. More information on the papers: J. Arias de Reyna, High Precision Computation of Riemann's Zeta Function by the Riemann-Siegel Formula I, II We refer to them as I, II. In them we shall find detailed explanation of all the procedure. The program uses Riemann-Siegel expansion. This is useful when t is big, ( say t > 10000 ). The precision is limited, roughly it can compute zeta(sigma+it) with an error less than exp(-c t) for some constant c depending on sigma. The program gives an error when the Riemann-Siegel formula can not compute to the wanted precision. """ import math class RSCache: def __init__(ctx): ctx._rs_cache = [0, 10, {}, {}] from .functions import defun #-------------------------------------------------------------------------------# # # # coef(ctx, J, eps, _cache=[0, 10, {} ] ) # # # #-------------------------------------------------------------------------------# # This function computes the coefficients c[n] defined on (I, equation (47)) # but see also (II, section 3.14). # # Since these coefficients are very difficult to compute we save the values # in a cache. So if we compute several values of the functions Rzeta(s) for # near values of s, we do not recompute these coefficients. # # c[n] are the Taylor coefficients of the function: # # F(z):= (exp(pi*j*(z*z/2+3/8))-j* sqrt(2) cos(pi*z/2))/(2*cos(pi *z)) # # def _coef(ctx, J, eps): r""" Computes the coefficients `c_n` for `0\le n\le 2J` with error less than eps **Definition** The coefficients c_n are defined by .. math :: \begin{equation} F(z)=\frac{e^{\pi i \bigl(\frac{z^2}{2}+\frac38\bigr)}-i\sqrt{2}\cos\frac{\pi}{2}z}{2\cos\pi z}=\sum_{n=0}^\infty c_{2n} z^{2n} \end{equation} they are computed applying the relation .. math :: \begin{multline} c_{2n}=-\frac{i}{\sqrt{2}}\Bigl(\frac{\pi}{2}\Bigr)^{2n} \sum_{k=0}^n\frac{(-1)^k}{(2k)!} 2^{2n-2k}\frac{(-1)^{n-k}E_{2n-2k}}{(2n-2k)!}+\\ +e^{3\pi i/8}\sum_{j=0}^n(-1)^j\frac{ E_{2j}}{(2j)!}\frac{i^{n-j}\pi^{n+j}}{(n-j)!2^{n-j+1}}. \end{multline} """ newJ = J+2 # compute more coefficients that are needed neweps6 = eps/2. # compute with a slight more precision # that are needed # PREPARATION FOR THE COMPUTATION OF V(N) AND W(N) # See II Section 3.16 # # Computing the exponent wpvw of the error II equation (81) wpvw = max(ctx.mag(10*(newJ+3)), 4*newJ+5-ctx.mag(neweps6)) # Preparation of Euler numbers (we need until the 2*RS_NEWJ) E = ctx._eulernum(2*newJ) # Now we have in the cache all the needed Euler numbers. # # Computing the powers of pi # # We need to compute the powers pi**n for 1<= n <= 2*J # with relative error less than 2**(-wpvw) # it is easy to show that this is obtained # taking wppi as the least d with # 2**d>40*J and 2**d> 4.24 *newJ + 2**wpvw # In II Section 3.9 we need also that # wppi > wptcoef[0], and that the powers # here computed 0<= k <= 2*newJ are more # than those needed there that are 2*L-2. # so we need J >= L this will be checked # before computing tcoef[] wppi = max(ctx.mag(40*newJ), ctx.mag(newJ)+3 +wpvw) ctx.prec = wppi pipower = {} pipower[0] = ctx.one pipower[1] = ctx.pi for n in range(2,2*newJ+1): pipower[n] = pipower[n-1]*ctx.pi # COMPUTING THE COEFFICIENTS v(n) AND w(n) # see II equation (61) and equations (81) and (82) ctx.prec = wpvw+2 v={} w={} for n in range(0,newJ+1): va = (-1)**n * ctx._eulernum(2*n) va = ctx.mpf(va)/ctx.fac(2*n) v[n]=va*pipower[2*n] for n in range(0,2*newJ+1): wa = ctx.one/ctx.fac(n) wa=wa/(2**n) w[n]=wa*pipower[n] # COMPUTATION OF THE CONVOLUTIONS RS_P1 AND RS_P2 # See II Section 3.16 ctx.prec = 15 wpp1a = 9 - ctx.mag(neweps6) P1 = {} for n in range(0,newJ+1): ctx.prec = 15 wpp1 = max(ctx.mag(10*(n+4)),4*n+wpp1a) ctx.prec = wpp1 sump = 0 for k in range(0,n+1): sump += ((-1)**k) * v[k]*w[2*n-2*k] P1[n]=((-1)**(n+1))*ctx.j*sump P2={} for n in range(0,newJ+1): ctx.prec = 15 wpp2 = max(ctx.mag(10*(n+4)),4*n+wpp1a) ctx.prec = wpp2 sump = 0 for k in range(0,n+1): sump += (ctx.j**(n-k)) * v[k]*w[n-k] P2[n]=sump # COMPUTING THE COEFFICIENTS c[2n] # See II Section 3.14 ctx.prec = 15 wpc0 = 5 - ctx.mag(neweps6) wpc = max(6,4*newJ+wpc0) ctx.prec = wpc mu = ctx.sqrt(ctx.mpf('2'))/2 nu = ctx.expjpi(3./8)/2 c={} for n in range(0,newJ): ctx.prec = 15 wpc = max(6,4*n+wpc0) ctx.prec = wpc c[2*n] = mu*P1[n]+nu*P2[n] for n in range(1,2*newJ,2): c[n] = 0 return [newJ, neweps6, c, pipower] def coef(ctx, J, eps): _cache = ctx._rs_cache if J <= _cache[0] and eps >= _cache[1]: return _cache[2], _cache[3] orig = ctx._mp.prec try: data = _coef(ctx._mp, J, eps) finally: ctx._mp.prec = orig if ctx is not ctx._mp: data[2] = dict((k,ctx.convert(v)) for (k,v) in data[2].items()) data[3] = dict((k,ctx.convert(v)) for (k,v) in data[3].items()) ctx._rs_cache[:] = data return ctx._rs_cache[2], ctx._rs_cache[3] #-------------------------------------------------------------------------------# # # # Rzeta_simul(s,k=0) # # # #-------------------------------------------------------------------------------# # This function return a list with the values: # Rzeta(sigma+it), conj(Rzeta(1-sigma+it)),Rzeta'(sigma+it), conj(Rzeta'(1-sigma+it)), # .... , Rzeta^{(k)}(sigma+it), conj(Rzeta^{(k)}(1-sigma+it)) # # Useful to compute the function zeta(s) and Z(w) or its derivatives. # def aux_M_Fp(ctx, xA, xeps4, a, xB1, xL): # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE # See II Section 3.11 equations (47) and (48) aux1 = 126.0657606*xA/xeps4 # 126.06.. = 316/sqrt(2*pi) aux1 = ctx.ln(aux1) aux2 = (2*ctx.ln(ctx.pi)+ctx.ln(xB1)+ctx.ln(a))/3 -ctx.ln(2*ctx.pi)/2 m = 3*xL-3 aux3= (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) while((aux1 < m*aux2+ aux3)and (m>1)): m = m - 1 aux3 = (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) xM = m return xM def aux_J_needed(ctx, xA, xeps4, a, xB1, xM): # DETERMINATION OF J THE NUMBER OF TERMS NEEDED # IN THE TAYLOR SERIES OF F. # See II Section 3.11 equation (49)) # Only determine one h1 = xeps4/(632*xA) h2 = xB1*a * 126.31337419529260248 # = pi^2*e^2*sqrt(3) h2 = h1 * ctx.power((h2/xM**2),(xM-1)/3) / xM h3 = min(h1,h2) return h3 def Rzeta_simul(ctx, s, der=0): # First we take the value of ctx.prec wpinitial = ctx.prec # INITIALIZATION # Take the real and imaginary part of s t = ctx._im(s) xsigma = ctx._re(s) ysigma = 1 - xsigma # Now compute several parameter that appear on the program ctx.prec = 15 a = ctx.sqrt(t/(2*ctx.pi)) xasigma = a ** xsigma yasigma = a ** ysigma # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) xA1=ctx.power(2, ctx.mag(xasigma)-1) yA1=ctx.power(2, ctx.mag(yasigma)-1) # We compute various epsilon's (see II end of Section 3.1) eps = ctx.power(2, -wpinitial) eps1 = eps/6. xeps2 = eps * xA1/3. yeps2 = eps * yA1/3. # COMPUTING SOME COEFFICIENTS THAT DEPENDS # ON sigma # constant b and c (see I Theorem 2 formula (26) ) # coefficients A and B1 (see I Section 6.1 equation (50)) # # here we not need high precision ctx.prec = 15 if xsigma > 0: xb = 2. xc = math.pow(9,xsigma)/4.44288 # 4.44288 =(math.sqrt(2)*math.pi) xA = math.pow(9,xsigma) xB1 = 1 else: xb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) xc = math.pow(2,-xsigma)/4.44288 xA = math.pow(2,-xsigma) xB1 = 1.10789 # = 2*sqrt(1-log(2)) if(ysigma > 0): yb = 2. yc = math.pow(9,ysigma)/4.44288 # 4.44288 =(math.sqrt(2)*math.pi) yA = math.pow(9,ysigma) yB1 = 1 else: yb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) yc = math.pow(2,-ysigma)/4.44288 yA = math.pow(2,-ysigma) yB1 = 1.10789 # = 2*sqrt(1-log(2)) # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL # CORRECTION # See II Section 3.2 ctx.prec = 15 xL = 1 while 3*xc*ctx.gamma(xL*0.5) * ctx.power(xb*a,-xL) >= xeps2: xL = xL+1 xL = max(2,xL) yL = 1 while 3*yc*ctx.gamma(yL*0.5) * ctx.power(yb*a,-yL) >= yeps2: yL = yL+1 yL = max(2,yL) # The number L has to satify some conditions. # If not RS can not compute Rzeta(s) with the prescribed precision # (see II, Section 3.2 condition (20) ) and # (II, Section 3.3 condition (22) ). Also we have added # an additional technical condition in Section 3.17 Proposition 17 if ((3*xL >= 2*a*a/25.) or (3*xL+2+xsigma<0) or (abs(xsigma) > a/2.) or \ (3*yL >= 2*a*a/25.) or (3*yL+2+ysigma<0) or (abs(ysigma) > a/2.)): ctx.prec = wpinitial raise NotImplementedError("Riemann-Siegel can not compute with such precision") # We take the maximum of the two values L = max(xL, yL) # INITIALIZATION (CONTINUATION) # # eps3 is the constant defined on (II, Section 3.5 equation (27) ) # each term of the RS correction must be computed with error <= eps3 xeps3 = xeps2/(4*xL) yeps3 = yeps2/(4*yL) # eps4 is defined on (II Section 3.6 equation (30) ) # each component of the formula (II Section 3.6 equation (29) ) # must be computed with error <= eps4 xeps4 = xeps3/(3*xL) yeps4 = yeps3/(3*yL) # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE xM = aux_M_Fp(ctx, xA, xeps4, a, xB1, xL) yM = aux_M_Fp(ctx, yA, yeps4, a, yB1, yL) M = max(xM, yM) # COMPUTING NUMBER OF TERMS J NEEDED h3 = aux_J_needed(ctx, xA, xeps4, a, xB1, xM) h4 = aux_J_needed(ctx, yA, yeps4, a, yB1, yM) h3 = min(h3,h4) J = 12 jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) while jvalue > h3: J = J+1 jvalue = (2*ctx.pi)*jvalue/J # COMPUTING eps5[m] for 1 <= m <= 21 # See II Section 10 equation (43) # We choose the minimum of the two possibilities eps5={} xforeps5 = math.pi*math.pi*xB1*a yforeps5 = math.pi*math.pi*yB1*a for m in range(0,22): xaux1 = math.pow(xforeps5, m/3)/(316.*xA) yaux1 = math.pow(yforeps5, m/3)/(316.*yA) aux1 = min(xaux1, yaux1) aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) aux2 = math.sqrt(aux2) eps5[m] = (aux1*aux2*min(xeps4,yeps4)) # COMPUTING wpfp # See II Section 3.13 equation (59) twenty = min(3*L-3, 21)+1 aux = 6812*J wpfp = ctx.mag(44*J) for m in range(0,twenty): wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) # COMPUTING N AND p # See II Section ctx.prec = wpfp + ctx.mag(t)+20 a = ctx.sqrt(t/(2*ctx.pi)) N = ctx.floor(a) p = 1-2*(a-N) # now we get a rounded version of p # to the precision wpfp # this possibly is not necessary num=ctx.floor(p*(ctx.mpf('2')**wpfp)) difference = p * (ctx.mpf('2')**wpfp)-num if (difference < 0.5): num = num else: num = num+1 p = ctx.convert(num * (ctx.mpf('2')**(-wpfp))) # COMPUTING THE COEFFICIENTS c[n] = cc[n] # We shall use the notation cc[n], since there is # a constant that is called c # See II Section 3.14 # We compute the coefficients and also save then in a # cache. The bulk of the computation is passed to # the function coef() # # eps6 is defined in II Section 3.13 equation (58) eps6 = ctx.power(ctx.convert(2*ctx.pi), J)/(ctx.gamma(J+1)*3*J) # Now we compute the coefficients cc = {} cont = {} cont, pipowers = coef(ctx, J, eps6) cc=cont.copy() # we need a copy since we have # to change his values. Fp={} # this is the adequate locus of this for n in range(M, 3*L-2): Fp[n] = 0 Fp={} ctx.prec = wpfp for m in range(0,M+1): sumP = 0 for k in range(2*J-m-1,-1,-1): sumP = (sumP * p)+ cc[k] Fp[m] = sumP # preparation of the new coefficients for k in range(0,2*J-m-1): cc[k] = (k+1)* cc[k+1] # COMPUTING THE NUMBERS xd[u,n,k], yd[u,n,k] # See II Section 3.17 # # First we compute the working precisions xwpd[k] # Se II equation (92) xwpd={} d1 = max(6,ctx.mag(40*L*L)) xd2 = 13+ctx.mag((1+abs(xsigma))*xA)-ctx.mag(xeps4)-1 xconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*xB1*xB1)) /2 for n in range(0,L): xd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*xconst)+xd2 xwpd[n]=max(xd3,d1) # procedure of II Section 3.17 ctx.prec = xwpd[1]+10 xpsigma = 1-(2*xsigma) xd = {} xd[0,0,-2]=0; xd[0,0,-1]=0; xd[0,0,0]=1; xd[0,0,1]=0 xd[0,-1,-2]=0; xd[0,-1,-1]=0; xd[0,-1,0]=1; xd[0,-1,1]=0 for n in range(1,L): ctx.prec = xwpd[n]+10 for k in range(0,3*n//2+1): m = 3*n-2*k if(m!=0): m1 = ctx.one/m c1= m1/4 c2=(xpsigma*m1)/2 c3=-(m+1) xd[0,n,k]=c3*xd[0,n-1,k-2]+c1*xd[0,n-1,k]+c2*xd[0,n-1,k-1] else: xd[0,n,k]=0 for r in range(0,k): add=xd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) xd[0,n,k] -= ((-1)**(k-r))*add xd[0,n,-2]=0; xd[0,n,-1]=0; xd[0,n,3*n//2+1]=0 for mu in range(-2,der+1): for n in range(-2,L): for k in range(-3,max(1,3*n//2+2)): if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): xd[mu,n,k] = 0 for mu in range(1,der+1): for n in range(0,L): ctx.prec = xwpd[n]+10 for k in range(0,3*n//2+1): aux=(2*mu-2)*xd[mu-2,n-2,k-3]+2*(xsigma+n-2)*xd[mu-1,n-2,k-3] xd[mu,n,k] = aux - xd[mu-1,n-1,k-1] # Now we compute the working precisions ywpd[k] # Se II equation (92) ywpd={} d1 = max(6,ctx.mag(40*L*L)) yd2 = 13+ctx.mag((1+abs(ysigma))*yA)-ctx.mag(yeps4)-1 yconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*yB1*yB1)) /2 for n in range(0,L): yd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*yconst)+yd2 ywpd[n]=max(yd3,d1) # procedure of II Section 3.17 ctx.prec = ywpd[1]+10 ypsigma = 1-(2*ysigma) yd = {} yd[0,0,-2]=0; yd[0,0,-1]=0; yd[0,0,0]=1; yd[0,0,1]=0 yd[0,-1,-2]=0; yd[0,-1,-1]=0; yd[0,-1,0]=1; yd[0,-1,1]=0 for n in range(1,L): ctx.prec = ywpd[n]+10 for k in range(0,3*n//2+1): m = 3*n-2*k if(m!=0): m1 = ctx.one/m c1= m1/4 c2=(ypsigma*m1)/2 c3=-(m+1) yd[0,n,k]=c3*yd[0,n-1,k-2]+c1*yd[0,n-1,k]+c2*yd[0,n-1,k-1] else: yd[0,n,k]=0 for r in range(0,k): add=yd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) yd[0,n,k] -= ((-1)**(k-r))*add yd[0,n,-2]=0; yd[0,n,-1]=0; yd[0,n,3*n//2+1]=0 for mu in range(-2,der+1): for n in range(-2,L): for k in range(-3,max(1,3*n//2+2)): if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): yd[mu,n,k] = 0 for mu in range(1,der+1): for n in range(0,L): ctx.prec = ywpd[n]+10 for k in range(0,3*n//2+1): aux=(2*mu-2)*yd[mu-2,n-2,k-3]+2*(ysigma+n-2)*yd[mu-1,n-2,k-3] yd[mu,n,k] = aux - yd[mu-1,n-1,k-1] # COMPUTING THE COEFFICIENTS xtcoef[k,l] # See II Section 3.9 # # computing the needed wp xwptcoef={} xwpterm={} ctx.prec = 15 c1 = ctx.mag(40*(L+2)) xc2 = ctx.mag(68*(L+2)*xA) xc4 = ctx.mag(xB1*a*math.sqrt(ctx.pi))-1 for k in range(0,L): xc3 = xc2 - k*xc4+ctx.mag(ctx.fac(k+0.5))/2. xwptcoef[k] = (max(c1,xc3-ctx.mag(xeps4)+1)+1 +20)*1.5 xwpterm[k] = (max(c1,ctx.mag(L+2)+xc3-ctx.mag(xeps3)+1)+1 +20) ywptcoef={} ywpterm={} ctx.prec = 15 c1 = ctx.mag(40*(L+2)) yc2 = ctx.mag(68*(L+2)*yA) yc4 = ctx.mag(yB1*a*math.sqrt(ctx.pi))-1 for k in range(0,L): yc3 = yc2 - k*yc4+ctx.mag(ctx.fac(k+0.5))/2. ywptcoef[k] = ((max(c1,yc3-ctx.mag(yeps4)+1))+10)*1.5 ywpterm[k] = (max(c1,ctx.mag(L+2)+yc3-ctx.mag(yeps3)+1)+1)+10 # check of power of pi # computing the fortcoef[mu,k,ell] xfortcoef={} for mu in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): xfortcoef[mu,k,ell]=0 for mu in range(0,der+1): for k in range(0,L): ctx.prec = xwptcoef[k] for ell in range(0,3*k//2+1): xfortcoef[mu,k,ell]=xd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] xfortcoef[mu,k,ell]=xfortcoef[mu,k,ell]/((2*ctx.j)**ell) def trunc_a(t): wp = ctx.prec ctx.prec = wp + 2 aa = ctx.sqrt(t/(2*ctx.pi)) ctx.prec = wp return aa # computing the tcoef[k,ell] xtcoef={} for mu in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): xtcoef[mu,k,ell]=0 ctx.prec = max(xwptcoef[0],ywptcoef[0])+3 aa= trunc_a(t) la = -ctx.ln(aa) for chi in range(0,der+1): for k in range(0,L): ctx.prec = xwptcoef[k] for ell in range(0,3*k//2+1): xtcoef[chi,k,ell] =0 for mu in range(0, chi+1): tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*xfortcoef[chi-mu,k,ell] xtcoef[chi,k,ell] += tcoefter # COMPUTING THE COEFFICIENTS ytcoef[k,l] # See II Section 3.9 # # computing the needed wp # check of power of pi # computing the fortcoef[mu,k,ell] yfortcoef={} for mu in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): yfortcoef[mu,k,ell]=0 for mu in range(0,der+1): for k in range(0,L): ctx.prec = ywptcoef[k] for ell in range(0,3*k//2+1): yfortcoef[mu,k,ell]=yd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] yfortcoef[mu,k,ell]=yfortcoef[mu,k,ell]/((2*ctx.j)**ell) # computing the tcoef[k,ell] ytcoef={} for chi in range(0,der+1): for k in range(0,L): for ell in range(-2,3*k//2+1): ytcoef[chi,k,ell]=0 for chi in range(0,der+1): for k in range(0,L): ctx.prec = ywptcoef[k] for ell in range(0,3*k//2+1): ytcoef[chi,k,ell] =0 for mu in range(0, chi+1): tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*yfortcoef[chi-mu,k,ell] ytcoef[chi,k,ell] += tcoefter # COMPUTING tv[k,ell] # See II Section 3.8 # # a has a good value ctx.prec = max(xwptcoef[0], ywptcoef[0])+2 av = {} av[0] = 1 av[1] = av[0]/a ctx.prec = max(xwptcoef[0],ywptcoef[0]) for k in range(2,L): av[k] = av[k-1] * av[1] # Computing the quotients xtv = {} for chi in range(0,der+1): for k in range(0,L): ctx.prec = xwptcoef[k] for ell in range(0,3*k//2+1): xtv[chi,k,ell] = xtcoef[chi,k,ell]* av[k] # Computing the quotients ytv = {} for chi in range(0,der+1): for k in range(0,L): ctx.prec = ywptcoef[k] for ell in range(0,3*k//2+1): ytv[chi,k,ell] = ytcoef[chi,k,ell]* av[k] # COMPUTING THE TERMS xterm[k] # See II Section 3.6 xterm = {} for chi in range(0,der+1): for n in range(0,L): ctx.prec = xwpterm[n] te = 0 for k in range(0, 3*n//2+1): te += xtv[chi,n,k] xterm[chi,n] = te # COMPUTING THE TERMS yterm[k] # See II Section 3.6 yterm = {} for chi in range(0,der+1): for n in range(0,L): ctx.prec = ywpterm[n] te = 0 for k in range(0, 3*n//2+1): te += ytv[chi,n,k] yterm[chi,n] = te # COMPUTING rssum # See II Section 3.5 xrssum={} ctx.prec=15 xrsbound = math.sqrt(ctx.pi) * xc /(xb*a) ctx.prec=15 xwprssum = ctx.mag(4.4*((L+3)**2)*xrsbound / xeps2) xwprssum = max(xwprssum, ctx.mag(10*(L+1))) ctx.prec = xwprssum for chi in range(0,der+1): xrssum[chi] = 0 for k in range(1,L+1): xrssum[chi] += xterm[chi,L-k] yrssum={} ctx.prec=15 yrsbound = math.sqrt(ctx.pi) * yc /(yb*a) ctx.prec=15 ywprssum = ctx.mag(4.4*((L+3)**2)*yrsbound / yeps2) ywprssum = max(ywprssum, ctx.mag(10*(L+1))) ctx.prec = ywprssum for chi in range(0,der+1): yrssum[chi] = 0 for k in range(1,L+1): yrssum[chi] += yterm[chi,L-k] # COMPUTING S3 # See II Section 3.19 ctx.prec = 15 A2 = 2**(max(ctx.mag(abs(xrssum[0])), ctx.mag(abs(yrssum[0])))) eps8 = eps/(3*A2) T = t *ctx.ln(t/(2*ctx.pi)) xwps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-xsigma))*T) ywps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-ysigma))*T) ctx.prec = max(xwps3, ywps3) tpi = t/(2*ctx.pi) arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 U = ctx.expj(-arg) a = trunc_a(t) xasigma = ctx.power(a, -xsigma) yasigma = ctx.power(a, -ysigma) xS3 = ((-1)**(N-1)) * xasigma * U yS3 = ((-1)**(N-1)) * yasigma * U # COMPUTING S1 the zetasum # See II Section 3.18 ctx.prec = 15 xwpsum = 4+ ctx.mag((N+ctx.power(N,1-xsigma))*ctx.ln(N) /eps1) ywpsum = 4+ ctx.mag((N+ctx.power(N,1-ysigma))*ctx.ln(N) /eps1) wpsum = max(xwpsum, ywpsum) ctx.prec = wpsum +10 ''' # This can be improved xS1={} yS1={} for chi in range(0,der+1): xS1[chi] = 0 yS1[chi] = 0 for n in range(1,int(N)+1): ln = ctx.ln(n) xexpn = ctx.exp(-ln*(xsigma+ctx.j*t)) yexpn = ctx.conj(1/(n*xexpn)) for chi in range(0,der+1): pown = ctx.power(-ln, chi) xterm = pown*xexpn yterm = pown*yexpn xS1[chi] += xterm yS1[chi] += yterm ''' xS1, yS1 = ctx._zetasum(s, 1, int(N)-1, range(0,der+1), True) # END OF COMPUTATION of xrz, yrz # See II Section 3.1 ctx.prec = 15 xabsS1 = abs(xS1[der]) xabsS2 = abs(xrssum[der] * xS3) xwpend = max(6, wpinitial+ctx.mag(6*(3*xabsS1+7*xabsS2) ) ) ctx.prec = xwpend xrz={} for chi in range(0,der+1): xrz[chi] = xS1[chi]+xrssum[chi]*xS3 ctx.prec = 15 yabsS1 = abs(yS1[der]) yabsS2 = abs(yrssum[der] * yS3) ywpend = max(6, wpinitial+ctx.mag(6*(3*yabsS1+7*yabsS2) ) ) ctx.prec = ywpend yrz={} for chi in range(0,der+1): yrz[chi] = yS1[chi]+yrssum[chi]*yS3 yrz[chi] = ctx.conj(yrz[chi]) ctx.prec = wpinitial return xrz, yrz def Rzeta_set(ctx, s, derivatives=[0]): r""" Computes several derivatives of the auxiliary function of Riemann `R(s)`. **Definition** The function is defined by .. math :: \begin{equation} {\mathop{\mathcal R }\nolimits}(s)= \int_{0\swarrow1}\frac{x^{-s} e^{\pi i x^2}}{e^{\pi i x}- e^{-\pi i x}}\,dx \end{equation} To this function we apply the Riemann-Siegel expansion. """ der = max(derivatives) # First we take the value of ctx.prec # During the computation we will change ctx.prec, and finally we will # restaurate the initial value wpinitial = ctx.prec # Take the real and imaginary part of s t = ctx._im(s) sigma = ctx._re(s) # Now compute several parameter that appear on the program ctx.prec = 15 a = ctx.sqrt(t/(2*ctx.pi)) # Careful asigma = ctx.power(a, sigma) # Careful # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) A1 = ctx.power(2, ctx.mag(asigma)-1) # We compute various epsilon's (see II end of Section 3.1) eps = ctx.power(2, -wpinitial) eps1 = eps/6. eps2 = eps * A1/3. # COMPUTING SOME COEFFICIENTS THAT DEPENDS # ON sigma # constant b and c (see I Theorem 2 formula (26) ) # coefficients A and B1 (see I Section 6.1 equation (50)) # here we not need high precision ctx.prec = 15 if sigma > 0: b = 2. c = math.pow(9,sigma)/4.44288 # 4.44288 =(math.sqrt(2)*math.pi) A = math.pow(9,sigma) B1 = 1 else: b = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) c = math.pow(2,-sigma)/4.44288 A = math.pow(2,-sigma) B1 = 1.10789 # = 2*sqrt(1-log(2)) # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL # CORRECTION # See II Section 3.2 ctx.prec = 15 L = 1 while 3*c*ctx.gamma(L*0.5) * ctx.power(b*a,-L) >= eps2: L = L+1 L = max(2,L) # The number L has to satify some conditions. # If not RS can not compute Rzeta(s) with the prescribed precision # (see II, Section 3.2 condition (20) ) and # (II, Section 3.3 condition (22) ). Also we have added # an additional technical condition in Section 3.17 Proposition 17 if ((3*L >= 2*a*a/25.) or (3*L+2+sigma<0) or (abs(sigma)> a/2.)): #print 'Error Riemann-Siegel can not compute with such precision' ctx.prec = wpinitial raise NotImplementedError("Riemann-Siegel can not compute with such precision") # INITIALIZATION (CONTINUATION) # # eps3 is the constant defined on (II, Section 3.5 equation (27) ) # each term of the RS correction must be computed with error <= eps3 eps3 = eps2/(4*L) # eps4 is defined on (II Section 3.6 equation (30) ) # each component of the formula (II Section 3.6 equation (29) ) # must be computed with error <= eps4 eps4 = eps3/(3*L) # COMPUTING M. NUMBER OF DERIVATIVES Fp[m] TO COMPUTE M = aux_M_Fp(ctx, A, eps4, a, B1, L) Fp = {} for n in range(M, 3*L-2): Fp[n] = 0 # But I have not seen an instance of M != 3*L-3 # # DETERMINATION OF J THE NUMBER OF TERMS NEEDED # IN THE TAYLOR SERIES OF F. # See II Section 3.11 equation (49)) h1 = eps4/(632*A) h2 = ctx.pi*ctx.pi*B1*a *ctx.sqrt(3)*math.e*math.e h2 = h1 * ctx.power((h2/M**2),(M-1)/3) / M h3 = min(h1,h2) J=12 jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) while jvalue > h3: J = J+1 jvalue = (2*ctx.pi)*jvalue/J # COMPUTING eps5[m] for 1 <= m <= 21 # See II Section 10 equation (43) eps5={} foreps5 = math.pi*math.pi*B1*a for m in range(0,22): aux1 = math.pow(foreps5, m/3)/(316.*A) aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) aux2 = math.sqrt(aux2) eps5[m] = aux1*aux2*eps4 # COMPUTING wpfp # See II Section 3.13 equation (59) twenty = min(3*L-3, 21)+1 aux = 6812*J wpfp = ctx.mag(44*J) for m in range(0, twenty): wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) # COMPUTING N AND p # See II Section ctx.prec = wpfp + ctx.mag(t) + 20 a = ctx.sqrt(t/(2*ctx.pi)) N = ctx.floor(a) p = 1-2*(a-N) # now we get a rounded version of p to the precision wpfp # this possibly is not necessary num = ctx.floor(p*(ctx.mpf(2)**wpfp)) difference = p * (ctx.mpf(2)**wpfp)-num if difference < 0.5: num = num else: num = num+1 p = ctx.convert(num * (ctx.mpf(2)**(-wpfp))) # COMPUTING THE COEFFICIENTS c[n] = cc[n] # We shall use the notation cc[n], since there is # a constant that is called c # See II Section 3.14 # We compute the coefficients and also save then in a # cache. The bulk of the computation is passed to # the function coef() # # eps6 is defined in II Section 3.13 equation (58) eps6 = ctx.power(2*ctx.pi, J)/(ctx.gamma(J+1)*3*J) # Now we compute the coefficients cc={} cont={} cont, pipowers = coef(ctx, J, eps6) cc = cont.copy() # we need a copy since we have Fp={} for n in range(M, 3*L-2): Fp[n] = 0 ctx.prec = wpfp for m in range(0,M+1): sumP = 0 for k in range(2*J-m-1,-1,-1): sumP = (sumP * p) + cc[k] Fp[m] = sumP # preparation of the new coefficients for k in range(0, 2*J-m-1): cc[k] = (k+1) * cc[k+1] # COMPUTING THE NUMBERS d[n,k] # See II Section 3.17 # First we compute the working precisions wpd[k] # Se II equation (92) wpd = {} d1 = max(6, ctx.mag(40*L*L)) d2 = 13+ctx.mag((1+abs(sigma))*A)-ctx.mag(eps4)-1 const = ctx.ln(8/(ctx.pi*ctx.pi*a*a*B1*B1)) /2 for n in range(0,L): d3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*const)+d2 wpd[n] = max(d3,d1) # procedure of II Section 3.17 ctx.prec = wpd[1]+10 psigma = 1-(2*sigma) d = {} d[0,0,-2]=0; d[0,0,-1]=0; d[0,0,0]=1; d[0,0,1]=0 d[0,-1,-2]=0; d[0,-1,-1]=0; d[0,-1,0]=1; d[0,-1,1]=0 for n in range(1,L): ctx.prec = wpd[n]+10 for k in range(0,3*n//2+1): m = 3*n-2*k if (m!=0): m1 = ctx.one/m c1 = m1/4 c2 = (psigma*m1)/2 c3 = -(m+1) d[0,n,k] = c3*d[0,n-1,k-2]+c1*d[0,n-1,k]+c2*d[0,n-1,k-1] else: d[0,n,k]=0 for r in range(0,k): add = d[0,n,r]*(ctx.one*ctx.fac(2*k-2*r)/ctx.fac(k-r)) d[0,n,k] -= ((-1)**(k-r))*add d[0,n,-2]=0; d[0,n,-1]=0; d[0,n,3*n//2+1]=0 for mu in range(-2,der+1): for n in range(-2,L): for k in range(-3,max(1,3*n//2+2)): if ((mu<0)or (n<0) or(k<0)or (k>3*n//2)): d[mu,n,k] = 0 for mu in range(1,der+1): for n in range(0,L): ctx.prec = wpd[n]+10 for k in range(0,3*n//2+1): aux=(2*mu-2)*d[mu-2,n-2,k-3]+2*(sigma+n-2)*d[mu-1,n-2,k-3] d[mu,n,k] = aux - d[mu-1,n-1,k-1] # COMPUTING THE COEFFICIENTS t[k,l] # See II Section 3.9 # # computing the needed wp wptcoef = {} wpterm = {} ctx.prec = 15 c1 = ctx.mag(40*(L+2)) c2 = ctx.mag(68*(L+2)*A) c4 = ctx.mag(B1*a*math.sqrt(ctx.pi))-1 for k in range(0,L): c3 = c2 - k*c4+ctx.mag(ctx.fac(k+0.5))/2. wptcoef[k] = max(c1,c3-ctx.mag(eps4)+1)+1 +10 wpterm[k] = max(c1,ctx.mag(L+2)+c3-ctx.mag(eps3)+1)+1 +10 # check of power of pi # computing the fortcoef[mu,k,ell] fortcoef={} for mu in derivatives: for k in range(0,L): for ell in range(-2,3*k//2+1): fortcoef[mu,k,ell]=0 for mu in derivatives: for k in range(0,L): ctx.prec = wptcoef[k] for ell in range(0,3*k//2+1): fortcoef[mu,k,ell]=d[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] fortcoef[mu,k,ell]=fortcoef[mu,k,ell]/((2*ctx.j)**ell) def trunc_a(t): wp = ctx.prec ctx.prec = wp + 2 aa = ctx.sqrt(t/(2*ctx.pi)) ctx.prec = wp return aa # computing the tcoef[chi,k,ell] tcoef={} for chi in derivatives: for k in range(0,L): for ell in range(-2,3*k//2+1): tcoef[chi,k,ell]=0 ctx.prec = wptcoef[0]+3 aa = trunc_a(t) la = -ctx.ln(aa) for chi in derivatives: for k in range(0,L): ctx.prec = wptcoef[k] for ell in range(0,3*k//2+1): tcoef[chi,k,ell] = 0 for mu in range(0, chi+1): tcoefter = ctx.binomial(chi,mu) * la**mu * \ fortcoef[chi-mu,k,ell] tcoef[chi,k,ell] += tcoefter # COMPUTING tv[k,ell] # See II Section 3.8 # Computing the powers av[k] = a**(-k) ctx.prec = wptcoef[0] + 2 # a has a good value of a. # See II Section 3.6 av = {} av[0] = 1 av[1] = av[0]/a ctx.prec = wptcoef[0] for k in range(2,L): av[k] = av[k-1] * av[1] # Computing the quotients tv = {} for chi in derivatives: for k in range(0,L): ctx.prec = wptcoef[k] for ell in range(0,3*k//2+1): tv[chi,k,ell] = tcoef[chi,k,ell]* av[k] # COMPUTING THE TERMS term[k] # See II Section 3.6 term = {} for chi in derivatives: for n in range(0,L): ctx.prec = wpterm[n] te = 0 for k in range(0, 3*n//2+1): te += tv[chi,n,k] term[chi,n] = te # COMPUTING rssum # See II Section 3.5 rssum={} ctx.prec=15 rsbound = math.sqrt(ctx.pi) * c /(b*a) ctx.prec=15 wprssum = ctx.mag(4.4*((L+3)**2)*rsbound / eps2) wprssum = max(wprssum, ctx.mag(10*(L+1))) ctx.prec = wprssum for chi in derivatives: rssum[chi] = 0 for k in range(1,L+1): rssum[chi] += term[chi,L-k] # COMPUTING S3 # See II Section 3.19 ctx.prec = 15 A2 = 2**(ctx.mag(rssum[0])) eps8 = eps/(3* A2) T = t * ctx.ln(t/(2*ctx.pi)) wps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-sigma))*T) ctx.prec = wps3 tpi = t/(2*ctx.pi) arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 U = ctx.expj(-arg) a = trunc_a(t) asigma = ctx.power(a, -sigma) S3 = ((-1)**(N-1)) * asigma * U # COMPUTING S1 the zetasum # See II Section 3.18 ctx.prec = 15 wpsum = 4 + ctx.mag((N+ctx.power(N,1-sigma))*ctx.ln(N)/eps1) ctx.prec = wpsum + 10 ''' # This can be improved S1 = {} for chi in derivatives: S1[chi] = 0 for n in range(1,int(N)+1): ln = ctx.ln(n) expn = ctx.exp(-ln*(sigma+ctx.j*t)) for chi in derivatives: term = ctx.power(-ln, chi)*expn S1[chi] += term ''' S1 = ctx._zetasum(s, 1, int(N)-1, derivatives)[0] # END OF COMPUTATION # See II Section 3.1 ctx.prec = 15 absS1 = abs(S1[der]) absS2 = abs(rssum[der] * S3) wpend = max(6, wpinitial + ctx.mag(6*(3*absS1+7*absS2))) ctx.prec = wpend rz = {} for chi in derivatives: rz[chi] = S1[chi]+rssum[chi]*S3 ctx.prec = wpinitial return rz def z_half(ctx,t,der=0): r""" z_half(t,der=0) Computes Z^(der)(t) """ s=ctx.mpf('0.5')+ctx.j*t wpinitial = ctx.prec ctx.prec = 15 tt = t/(2*ctx.pi) wptheta = wpinitial +1 + ctx.mag(3*(tt**1.5)*ctx.ln(tt)) wpz = wpinitial + 1 + ctx.mag(12*tt*ctx.ln(tt)) ctx.prec = wptheta theta = ctx.siegeltheta(t) ctx.prec = wpz rz = Rzeta_set(ctx,s, range(der+1)) if der > 0: ps1 = ctx._re(ctx.psi(0,s/2)/2 - ctx.ln(ctx.pi)/2) if der > 1: ps2 = ctx._re(ctx.j*ctx.psi(1,s/2)/4) if der > 2: ps3 = ctx._re(-ctx.psi(2,s/2)/8) if der > 3: ps4 = ctx._re(-ctx.j*ctx.psi(3,s/2)/16) exptheta = ctx.expj(theta) if der == 0: z = 2*exptheta*rz[0] if der == 1: zf = 2j*exptheta z = zf*(ps1*rz[0]+rz[1]) if der == 2: zf = 2 * exptheta z = -zf*(2*rz[1]*ps1+rz[0]*ps1**2+rz[2]-ctx.j*rz[0]*ps2) if der == 3: zf = -2j*exptheta z = 3*rz[1]*ps1**2+rz[0]*ps1**3+3*ps1*rz[2] z = zf*(z-3j*rz[1]*ps2-3j*rz[0]*ps1*ps2+rz[3]-rz[0]*ps3) if der == 4: zf = 2*exptheta z = 4*rz[1]*ps1**3+rz[0]*ps1**4+6*ps1**2*rz[2] z = z-12j*rz[1]*ps1*ps2-6j*rz[0]*ps1**2*ps2-6j*rz[2]*ps2-3*rz[0]*ps2*ps2 z = z + 4*ps1*rz[3]-4*rz[1]*ps3-4*rz[0]*ps1*ps3+rz[4]+ctx.j*rz[0]*ps4 z = zf*z ctx.prec = wpinitial return ctx._re(z) def zeta_half(ctx, s, k=0): """ zeta_half(s,k=0) Computes zeta^(k)(s) when Re s = 0.5 """ wpinitial = ctx.prec sigma = ctx._re(s) t = ctx._im(s) #--- compute wptheta, wpR, wpbasic --- ctx.prec = 53 # X see II Section 3.21 (109) and (110) if sigma > 0: X = ctx.sqrt(abs(s)) else: X = (2*ctx.pi)**(sigma-1) * abs(1-s)**(0.5-sigma) # M1 see II Section 3.21 (111) and (112) if sigma > 0: M1 = 2*ctx.sqrt(t/(2*ctx.pi)) else: M1 = 4 * t * X # T see II Section 3.21 (113) abst = abs(0.5-s) T = 2* abst*math.log(abst) # computing wpbasic, wptheta, wpR see II Section 3.21 wpbasic = max(6,3+ctx.mag(t)) wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M1*X+1.3*M1*X*T)+wpinitial+1 wpbasic = max(wpbasic, wpbasic2) wptheta = max(4, 3+ctx.mag(2.7*M1*X)+wpinitial+1) wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 ctx.prec = wptheta theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) if k > 0: ps1 = (ctx._re(ctx.psi(0,s/2)))/2 - ctx.ln(ctx.pi)/2 if k > 1: ps2 = -(ctx._im(ctx.psi(1,s/2)))/4 if k > 2: ps3 = -(ctx._re(ctx.psi(2,s/2)))/8 if k > 3: ps4 = (ctx._im(ctx.psi(3,s/2)))/16 ctx.prec = wpR xrz = Rzeta_set(ctx,s,range(k+1)) yrz={} for chi in range(0,k+1): yrz[chi] = ctx.conj(xrz[chi]) ctx.prec = wpbasic exptheta = ctx.expj(-2*theta) if k==0: zv = xrz[0]+exptheta*yrz[0] if k==1: zv1 = -yrz[1] - 2*yrz[0]*ps1 zv = xrz[1] + exptheta*zv1 if k==2: zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2)+yrz[2]+2j*yrz[0]*ps2 zv = xrz[2]+exptheta*zv1 if k==3: zv1 = -12*yrz[1]*ps1**2-8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 zv = xrz[3]+exptheta*zv1 if k == 4: zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 zv = xrz[4]+exptheta*zv1 ctx.prec = wpinitial return zv def zeta_offline(ctx, s, k=0): """ Computes zeta^(k)(s) off the line """ wpinitial = ctx.prec sigma = ctx._re(s) t = ctx._im(s) #--- compute wptheta, wpR, wpbasic --- ctx.prec = 53 # X see II Section 3.21 (109) and (110) if sigma > 0: X = ctx.power(abs(s), 0.5) else: X = ctx.power(2*ctx.pi, sigma-1)*ctx.power(abs(1-s),0.5-sigma) # M1 see II Section 3.21 (111) and (112) if (sigma > 0): M1 = 2*ctx.sqrt(t/(2*ctx.pi)) else: M1 = 4 * t * X # M2 see II Section 3.21 (111) and (112) if (1-sigma > 0): M2 = 2*ctx.sqrt(t/(2*ctx.pi)) else: M2 = 4*t*ctx.power(2*ctx.pi, -sigma)*ctx.power(abs(s),sigma-0.5) # T see II Section 3.21 (113) abst = abs(0.5-s) T = 2* abst*math.log(abst) # computing wpbasic, wptheta, wpR see II Section 3.21 wpbasic = max(6,3+ctx.mag(t)) wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M2*X+1.3*M2*X*T)+wpinitial+1 wpbasic = max(wpbasic, wpbasic2) wptheta = max(4, 3+ctx.mag(2.7*M2*X)+wpinitial+1) wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 ctx.prec = wptheta theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) s1 = s s2 = ctx.conj(1-s1) ctx.prec = wpR xrz, yrz = Rzeta_simul(ctx, s, k) if k > 0: ps1 = (ctx.psi(0,s1/2)+ctx.psi(0,(1-s1)/2))/4 - ctx.ln(ctx.pi)/2 if k > 1: ps2 = ctx.j*(ctx.psi(1,s1/2)-ctx.psi(1,(1-s1)/2))/8 if k > 2: ps3 = -(ctx.psi(2,s1/2)+ctx.psi(2,(1-s1)/2))/16 if k > 3: ps4 = -ctx.j*(ctx.psi(3,s1/2)-ctx.psi(3,(1-s1)/2))/32 ctx.prec = wpbasic exptheta = ctx.expj(-2*theta) if k == 0: zv = xrz[0]+exptheta*yrz[0] if k == 1: zv1 = -yrz[1]-2*yrz[0]*ps1 zv = xrz[1]+exptheta*zv1 if k == 2: zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2) +yrz[2]+2j*yrz[0]*ps2 zv = xrz[2]+exptheta*zv1 if k == 3: zv1 = -12*yrz[1]*ps1**2 -8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 zv = xrz[3]+exptheta*zv1 if k == 4: zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 zv = xrz[4]+exptheta*zv1 ctx.prec = wpinitial return zv def z_offline(ctx, w, k=0): r""" Computes Z(w) and its derivatives off the line """ s = ctx.mpf('0.5')+ctx.j*w s1 = s s2 = ctx.conj(1-s1) wpinitial = ctx.prec ctx.prec = 35 # X see II Section 3.21 (109) and (110) # M1 see II Section 3.21 (111) and (112) if (ctx._re(s1) >= 0): M1 = 2*ctx.sqrt(ctx._im(s1)/(2 * ctx.pi)) X = ctx.sqrt(abs(s1)) else: X = (2*ctx.pi)**(ctx._re(s1)-1) * abs(1-s1)**(0.5-ctx._re(s1)) M1 = 4 * ctx._im(s1)*X # M2 see II Section 3.21 (111) and (112) if (ctx._re(s2) >= 0): M2 = 2*ctx.sqrt(ctx._im(s2)/(2 * ctx.pi)) else: M2 = 4 * ctx._im(s2)*(2*ctx.pi)**(ctx._re(s2)-1)*abs(1-s2)**(0.5-ctx._re(s2)) # T see II Section 3.21 Prop. 27 T = 2*abs(ctx.siegeltheta(w)) # defining some precisions # see II Section 3.22 (115), (116), (117) aux1 = ctx.sqrt(X) aux2 = aux1*(M1+M2) aux3 = 3 +wpinitial wpbasic = max(6, 3+ctx.mag(T), ctx.mag(aux2*(26+2*T))+aux3) wptheta = max(4,ctx.mag(2.04*aux2)+aux3) wpR = ctx.mag(4*aux1)+aux3 # now the computations ctx.prec = wptheta theta = ctx.siegeltheta(w) ctx.prec = wpR xrz, yrz = Rzeta_simul(ctx,s,k) pta = 0.25 + 0.5j*w ptb = 0.25 - 0.5j*w if k > 0: ps1 = 0.25*(ctx.psi(0,pta)+ctx.psi(0,ptb)) - ctx.ln(ctx.pi)/2 if k > 1: ps2 = (1j/8)*(ctx.psi(1,pta)-ctx.psi(1,ptb)) if k > 2: ps3 = (-1./16)*(ctx.psi(2,pta)+ctx.psi(2,ptb)) if k > 3: ps4 = (-1j/32)*(ctx.psi(3,pta)-ctx.psi(3,ptb)) ctx.prec = wpbasic exptheta = ctx.expj(theta) if k == 0: zv = exptheta*xrz[0]+yrz[0]/exptheta j = ctx.j if k == 1: zv = j*exptheta*(xrz[1]+xrz[0]*ps1)-j*(yrz[1]+yrz[0]*ps1)/exptheta if k == 2: zv = exptheta*(-2*xrz[1]*ps1-xrz[0]*ps1**2-xrz[2]+j*xrz[0]*ps2) zv =zv + (-2*yrz[1]*ps1-yrz[0]*ps1**2-yrz[2]-j*yrz[0]*ps2)/exptheta if k == 3: zv1 = -3*xrz[1]*ps1**2-xrz[0]*ps1**3-3*xrz[2]*ps1+j*3*xrz[1]*ps2 zv1 = (zv1+ 3j*xrz[0]*ps1*ps2-xrz[3]+xrz[0]*ps3)*j*exptheta zv2 = 3*yrz[1]*ps1**2+yrz[0]*ps1**3+3*yrz[2]*ps1+j*3*yrz[1]*ps2 zv2 = j*(zv2 + 3j*yrz[0]*ps1*ps2+ yrz[3]-yrz[0]*ps3)/exptheta zv = zv1+zv2 if k == 4: zv1 = 4*xrz[1]*ps1**3+xrz[0]*ps1**4 + 6*xrz[2]*ps1**2 zv1 = zv1-12j*xrz[1]*ps1*ps2-6j*xrz[0]*ps1**2*ps2-6j*xrz[2]*ps2 zv1 = zv1-3*xrz[0]*ps2*ps2+4*xrz[3]*ps1-4*xrz[1]*ps3-4*xrz[0]*ps1*ps3 zv1 = zv1+xrz[4]+j*xrz[0]*ps4 zv2 = 4*yrz[1]*ps1**3+yrz[0]*ps1**4 + 6*yrz[2]*ps1**2 zv2 = zv2+12j*yrz[1]*ps1*ps2+6j*yrz[0]*ps1**2*ps2+6j*yrz[2]*ps2 zv2 = zv2-3*yrz[0]*ps2*ps2+4*yrz[3]*ps1-4*yrz[1]*ps3-4*yrz[0]*ps1*ps3 zv2 = zv2+yrz[4]-j*yrz[0]*ps4 zv = exptheta*zv1+zv2/exptheta ctx.prec = wpinitial return zv @defun def rs_zeta(ctx, s, derivative=0, **kwargs): if derivative > 4: raise NotImplementedError s = ctx.convert(s) re = ctx._re(s); im = ctx._im(s) if im < 0: z = ctx.conj(ctx.rs_zeta(ctx.conj(s), derivative)) return z critical_line = (re == 0.5) if critical_line: return zeta_half(ctx, s, derivative) else: return zeta_offline(ctx, s, derivative) @defun def rs_z(ctx, w, derivative=0): w = ctx.convert(w) re = ctx._re(w); im = ctx._im(w) if re < 0: return rs_z(ctx, -w, derivative) critical_line = (im == 0) if critical_line : return z_half(ctx, w, derivative) else: return z_offline(ctx, w, derivative) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/zeta.py0000644000175000017500000010621712014170666025721 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .functions import defun, defun_wrapped, defun_static @defun def stieltjes(ctx, n, a=1): n = ctx.convert(n) a = ctx.convert(a) if n < 0: return ctx.bad_domain("Stieltjes constants defined for n >= 0") if hasattr(ctx, "stieltjes_cache"): stieltjes_cache = ctx.stieltjes_cache else: stieltjes_cache = ctx.stieltjes_cache = {} if a == 1: if n == 0: return +ctx.euler if n in stieltjes_cache: prec, s = stieltjes_cache[n] if prec >= ctx.prec: return +s mag = 1 def f(x): xa = x/a v = (xa-ctx.j)*ctx.ln(a-ctx.j*x)**n/(1+xa**2)/(ctx.exp(2*ctx.pi*x)-1) return ctx._re(v) / mag orig = ctx.prec try: # Normalize integrand by approx. magnitude to # speed up quadrature (which uses absolute error) if n > 50: ctx.prec = 20 mag = ctx.quad(f, [0,ctx.inf], maxdegree=3) ctx.prec = orig + 10 + int(n**0.5) s = ctx.quad(f, [0,ctx.inf], maxdegree=20) v = ctx.ln(a)**n/(2*a) - ctx.ln(a)**(n+1)/(n+1) + 2*s/a*mag finally: ctx.prec = orig if a == 1 and ctx.isint(n): stieltjes_cache[n] = (ctx.prec, v) return +v @defun_wrapped def siegeltheta(ctx, t, derivative=0): d = int(derivative) if (t == ctx.inf or t == ctx.ninf): if d < 2: if t == ctx.ninf and d == 0: return ctx.ninf return ctx.inf else: return ctx.zero if d == 0: if ctx._im(t): # XXX: cancellation occurs a = ctx.loggamma(0.25+0.5j*t) b = ctx.loggamma(0.25-0.5j*t) return -ctx.ln(ctx.pi)/2*t - 0.5j*(a-b) else: if ctx.isinf(t): return t return ctx._im(ctx.loggamma(0.25+0.5j*t)) - ctx.ln(ctx.pi)/2*t if d > 0: a = (-0.5j)**(d-1)*ctx.polygamma(d-1, 0.25-0.5j*t) b = (0.5j)**(d-1)*ctx.polygamma(d-1, 0.25+0.5j*t) if ctx._im(t): if d == 1: return -0.5*ctx.log(ctx.pi)+0.25*(a+b) else: return 0.25*(a+b) else: if d == 1: return ctx._re(-0.5*ctx.log(ctx.pi)+0.25*(a+b)) else: return ctx._re(0.25*(a+b)) @defun_wrapped def grampoint(ctx, n): # asymptotic expansion, from # http://mathworld.wolfram.com/GramPoint.html g = 2*ctx.pi*ctx.exp(1+ctx.lambertw((8*n+1)/(8*ctx.e))) return ctx.findroot(lambda t: ctx.siegeltheta(t)-ctx.pi*n, g) @defun_wrapped def siegelz(ctx, t, **kwargs): d = int(kwargs.get("derivative", 0)) t = ctx.convert(t) t1 = ctx._re(t) t2 = ctx._im(t) prec = ctx.prec try: if abs(t1) > 500*prec and t2**2 < t1: v = ctx.rs_z(t, d) if ctx._is_real_type(t): return ctx._re(v) return v except NotImplementedError: pass ctx.prec += 21 e1 = ctx.expj(ctx.siegeltheta(t)) z = ctx.zeta(0.5+ctx.j*t) if d == 0: v = e1*z ctx.prec=prec if ctx._is_real_type(t): return ctx._re(v) return +v z1 = ctx.zeta(0.5+ctx.j*t, derivative=1) theta1 = ctx.siegeltheta(t, derivative=1) if d == 1: v = ctx.j*e1*(z1+z*theta1) ctx.prec=prec if ctx._is_real_type(t): return ctx._re(v) return +v z2 = ctx.zeta(0.5+ctx.j*t, derivative=2) theta2 = ctx.siegeltheta(t, derivative=2) comb1 = theta1**2-ctx.j*theta2 if d == 2: def terms(): return [2*z1*theta1, z2, z*comb1] v = ctx.sum_accurately(terms, 1) v = -e1*v ctx.prec = prec if ctx._is_real_type(t): return ctx._re(v) return +v ctx.prec += 10 z3 = ctx.zeta(0.5+ctx.j*t, derivative=3) theta3 = ctx.siegeltheta(t, derivative=3) comb2 = theta1**3-3*ctx.j*theta1*theta2-theta3 if d == 3: def terms(): return [3*theta1*z2, 3*z1*comb1, z3+z*comb2] v = ctx.sum_accurately(terms, 1) v = -ctx.j*e1*v ctx.prec = prec if ctx._is_real_type(t): return ctx._re(v) return +v z4 = ctx.zeta(0.5+ctx.j*t, derivative=4) theta4 = ctx.siegeltheta(t, derivative=4) def terms(): return [theta1**4, -6*ctx.j*theta1**2*theta2, -3*theta2**2, -4*theta1*theta3, ctx.j*theta4] comb3 = ctx.sum_accurately(terms, 1) if d == 4: def terms(): return [6*theta1**2*z2, -6*ctx.j*z2*theta2, 4*theta1*z3, 4*z1*comb2, z4, z*comb3] v = ctx.sum_accurately(terms, 1) v = e1*v ctx.prec = prec if ctx._is_real_type(t): return ctx._re(v) return +v if d > 4: h = lambda x: ctx.siegelz(x, derivative=4) return ctx.diff(h, t, n=d-4) _zeta_zeros = [ 14.134725142,21.022039639,25.010857580,30.424876126,32.935061588, 37.586178159,40.918719012,43.327073281,48.005150881,49.773832478, 52.970321478,56.446247697,59.347044003,60.831778525,65.112544048, 67.079810529,69.546401711,72.067157674,75.704690699,77.144840069, 79.337375020,82.910380854,84.735492981,87.425274613,88.809111208, 92.491899271,94.651344041,95.870634228,98.831194218,101.317851006, 103.725538040,105.446623052,107.168611184,111.029535543,111.874659177, 114.320220915,116.226680321,118.790782866,121.370125002,122.946829294, 124.256818554,127.516683880,129.578704200,131.087688531,133.497737203, 134.756509753,138.116042055,139.736208952,141.123707404,143.111845808, 146.000982487,147.422765343,150.053520421,150.925257612,153.024693811, 156.112909294,157.597591818,158.849988171,161.188964138,163.030709687, 165.537069188,167.184439978,169.094515416,169.911976479,173.411536520, 174.754191523,176.441434298,178.377407776,179.916484020,182.207078484, 184.874467848,185.598783678,187.228922584,189.416158656,192.026656361, 193.079726604,195.265396680,196.876481841,198.015309676,201.264751944, 202.493594514,204.189671803,205.394697202,207.906258888,209.576509717, 211.690862595,213.347919360,214.547044783,216.169538508,219.067596349, 220.714918839,221.430705555,224.007000255,224.983324670,227.421444280, 229.337413306,231.250188700,231.987235253,233.693404179,236.524229666, ] def _load_zeta_zeros(url): import urllib d = urllib.urlopen(url) L = [float(x) for x in d.readlines()] # Sanity check assert round(L[0]) == 14 _zeta_zeros[:] = L @defun def oldzetazero(ctx, n, url='http://www.dtc.umn.edu/~odlyzko/zeta_tables/zeros1'): n = int(n) if n < 0: return ctx.zetazero(-n).conjugate() if n == 0: raise ValueError("n must be nonzero") if n > len(_zeta_zeros) and n <= 100000: _load_zeta_zeros(url) if n > len(_zeta_zeros): raise NotImplementedError("n too large for zetazeros") return ctx.mpc(0.5, ctx.findroot(ctx.siegelz, _zeta_zeros[n-1])) @defun_wrapped def riemannr(ctx, x): if x == 0: return ctx.zero # Check if a simple asymptotic estimate is accurate enough if abs(x) > 1000: a = ctx.li(x) b = 0.5*ctx.li(ctx.sqrt(x)) if abs(b) < abs(a)*ctx.eps: return a if abs(x) < 0.01: # XXX ctx.prec += int(-ctx.log(abs(x),2)) # Sum Gram's series s = t = ctx.one u = ctx.ln(x) k = 1 while abs(t) > abs(s)*ctx.eps: t = t * u / k s += t / (k * ctx._zeta_int(k+1)) k += 1 return s @defun_static def primepi(ctx, x): x = int(x) if x < 2: return 0 return len(ctx.list_primes(x)) # TODO: fix the interface wrt contexts @defun_wrapped def primepi2(ctx, x): x = int(x) if x < 2: return ctx._iv.zero if x < 2657: return ctx._iv.mpf(ctx.primepi(x)) mid = ctx.li(x) # Schoenfeld's estimate for x >= 2657, assuming RH err = ctx.sqrt(x,rounding='u')*ctx.ln(x,rounding='u')/8/ctx.pi(rounding='d') a = ctx.floor((ctx._iv.mpf(mid)-err).a, rounding='d') b = ctx.ceil((ctx._iv.mpf(mid)+err).b, rounding='u') return ctx._iv.mpf([a,b]) @defun_wrapped def primezeta(ctx, s): if ctx.isnan(s): return s if ctx.re(s) <= 0: raise ValueError("prime zeta function defined only for re(s) > 0") if s == 1: return ctx.inf if s == 0.5: return ctx.mpc(ctx.ninf, ctx.pi) r = ctx.re(s) if r > ctx.prec: return 0.5**s else: wp = ctx.prec + int(r) def terms(): orig = ctx.prec # zeta ~ 1+eps; need to set precision # to get logarithm accurately k = 0 while 1: k += 1 u = ctx.moebius(k) if not u: continue ctx.prec = wp t = u*ctx.ln(ctx.zeta(k*s))/k if not t: return #print ctx.prec, ctx.nstr(t) ctx.prec = orig yield t return ctx.sum_accurately(terms) # TODO: for bernpoly and eulerpoly, ensure that all exact zeros are covered @defun_wrapped def bernpoly(ctx, n, z): # Slow implementation: #return sum(ctx.binomial(n,k)*ctx.bernoulli(k)*z**(n-k) for k in xrange(0,n+1)) n = int(n) if n < 0: raise ValueError("Bernoulli polynomials only defined for n >= 0") if z == 0 or (z == 1 and n > 1): return ctx.bernoulli(n) if z == 0.5: return (ctx.ldexp(1,1-n)-1)*ctx.bernoulli(n) if n <= 3: if n == 0: return z ** 0 if n == 1: return z - 0.5 if n == 2: return (6*z*(z-1)+1)/6 if n == 3: return z*(z*(z-1.5)+0.5) if ctx.isinf(z): return z ** n if ctx.isnan(z): return z if abs(z) > 2: def terms(): t = ctx.one yield t r = ctx.one/z k = 1 while k <= n: t = t*(n+1-k)/k*r if not (k > 2 and k & 1): yield t*ctx.bernoulli(k) k += 1 return ctx.sum_accurately(terms) * z**n else: def terms(): yield ctx.bernoulli(n) t = ctx.one k = 1 while k <= n: t = t*(n+1-k)/k * z m = n-k if not (m > 2 and m & 1): yield t*ctx.bernoulli(m) k += 1 return ctx.sum_accurately(terms) @defun_wrapped def eulerpoly(ctx, n, z): n = int(n) if n < 0: raise ValueError("Euler polynomials only defined for n >= 0") if n <= 2: if n == 0: return z ** 0 if n == 1: return z - 0.5 if n == 2: return z*(z-1) if ctx.isinf(z): return z**n if ctx.isnan(z): return z m = n+1 if z == 0: return -2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 if z == 1: return 2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 if z == 0.5: if n % 2: return ctx.zero # Use exact code for Euler numbers if n < 100 or n*ctx.mag(0.46839865*n) < ctx.prec*0.25: return ctx.ldexp(ctx._eulernum(n), -n) # http://functions.wolfram.com/Polynomials/EulerE2/06/01/02/01/0002/ def terms(): t = ctx.one k = 0 w = ctx.ldexp(1,n+2) while 1: v = n-k+1 if not (v > 2 and v & 1): yield (2-w)*ctx.bernoulli(v)*t k += 1 if k > n: break t = t*z*(n-k+2)/k w *= 0.5 return ctx.sum_accurately(terms) / m @defun def eulernum(ctx, n, exact=False): n = int(n) if exact: return int(ctx._eulernum(n)) if n < 100: return ctx.mpf(ctx._eulernum(n)) if n % 2: return ctx.zero return ctx.ldexp(ctx.eulerpoly(n,0.5), n) # TODO: this should be implemented low-level def polylog_series(ctx, s, z): tol = +ctx.eps l = ctx.zero k = 1 zk = z while 1: term = zk / k**s l += term if abs(term) < tol: break zk *= z k += 1 return l def polylog_continuation(ctx, n, z): if n < 0: return z*0 twopij = 2j * ctx.pi a = -twopij**n/ctx.fac(n) * ctx.bernpoly(n, ctx.ln(z)/twopij) if ctx._is_real_type(z) and z < 0: a = ctx._re(a) if ctx._im(z) < 0 or (ctx._im(z) == 0 and ctx._re(z) >= 1): a -= twopij*ctx.ln(z)**(n-1)/ctx.fac(n-1) return a def polylog_unitcircle(ctx, n, z): tol = +ctx.eps if n > 1: l = ctx.zero logz = ctx.ln(z) logmz = ctx.one m = 0 while 1: if (n-m) != 1: term = ctx.zeta(n-m) * logmz / ctx.fac(m) if term and abs(term) < tol: break l += term logmz *= logz m += 1 l += ctx.ln(z)**(n-1)/ctx.fac(n-1)*(ctx.harmonic(n-1)-ctx.ln(-ctx.ln(z))) elif n < 1: # else l = ctx.fac(-n)*(-ctx.ln(z))**(n-1) logz = ctx.ln(z) logkz = ctx.one k = 0 while 1: b = ctx.bernoulli(k-n+1) if b: term = b*logkz/(ctx.fac(k)*(k-n+1)) if abs(term) < tol: break l -= term logkz *= logz k += 1 else: raise ValueError if ctx._is_real_type(z) and z < 0: l = ctx._re(l) return l def polylog_general(ctx, s, z): v = ctx.zero u = ctx.ln(z) if not abs(u) < 5: # theoretically |u| < 2*pi raise NotImplementedError("polylog for arbitrary s and z") t = 1 k = 0 while 1: term = ctx.zeta(s-k) * t if abs(term) < ctx.eps: break v += term k += 1 t *= u t /= k return ctx.gamma(1-s)*(-u)**(s-1) + v @defun_wrapped def polylog(ctx, s, z): s = ctx.convert(s) z = ctx.convert(z) if z == 1: return ctx.zeta(s) if z == -1: return -ctx.altzeta(s) if s == 0: return z/(1-z) if s == 1: return -ctx.ln(1-z) if s == -1: return z/(1-z)**2 if abs(z) <= 0.75 or (not ctx.isint(s) and abs(z) < 0.9): return polylog_series(ctx, s, z) if abs(z) >= 1.4 and ctx.isint(s): return (-1)**(s+1)*polylog_series(ctx, s, 1/z) + polylog_continuation(ctx, s, z) if ctx.isint(s): return polylog_unitcircle(ctx, int(s), z) return polylog_general(ctx, s, z) #raise NotImplementedError("polylog for arbitrary s and z") # This could perhaps be used in some cases #from quadrature import quad #return quad(lambda t: t**(s-1)/(exp(t)/z-1),[0,inf])/gamma(s) @defun_wrapped def clsin(ctx, s, z, pi=False): if ctx.isint(s) and s < 0 and int(s) % 2 == 1: return z*0 if pi: a = ctx.expjpi(z) else: a = ctx.expj(z) if ctx._is_real_type(z) and ctx._is_real_type(s): return ctx.im(ctx.polylog(s,a)) b = 1/a return (-0.5j)*(ctx.polylog(s,a) - ctx.polylog(s,b)) @defun_wrapped def clcos(ctx, s, z, pi=False): if ctx.isint(s) and s < 0 and int(s) % 2 == 0: return z*0 if pi: a = ctx.expjpi(z) else: a = ctx.expj(z) if ctx._is_real_type(z) and ctx._is_real_type(s): return ctx.re(ctx.polylog(s,a)) b = 1/a return 0.5*(ctx.polylog(s,a) + ctx.polylog(s,b)) @defun def altzeta(ctx, s, **kwargs): try: return ctx._altzeta(s, **kwargs) except NotImplementedError: return ctx._altzeta_generic(s) @defun_wrapped def _altzeta_generic(ctx, s): if s == 1: return ctx.ln2 + 0*s return -ctx.powm1(2, 1-s) * ctx.zeta(s) @defun def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs): d = int(derivative) if a == 1 and not (d or method): try: return ctx._zeta(s, **kwargs) except NotImplementedError: pass s = ctx.convert(s) prec = ctx.prec method = kwargs.get('method') verbose = kwargs.get('verbose') if a == 1 and method != 'euler-maclaurin': im = abs(ctx._im(s)) re = abs(ctx._re(s)) #if (im < prec or method == 'borwein') and not derivative: # try: # if verbose: # print "zeta: Attempting to use the Borwein algorithm" # return ctx._zeta(s, **kwargs) # except NotImplementedError: # if verbose: # print "zeta: Could not use the Borwein algorithm" # pass if abs(im) > 500*prec and 10*re < prec and derivative <= 4 or \ method == 'riemann-siegel': try: # py2.4 compatible try block try: if verbose: print("zeta: Attempting to use the Riemann-Siegel algorithm") return ctx.rs_zeta(s, derivative, **kwargs) except NotImplementedError: if verbose: print("zeta: Could not use the Riemann-Siegel algorithm") pass finally: ctx.prec = prec if s == 1: return ctx.inf abss = abs(s) if abss == ctx.inf: if ctx.re(s) == ctx.inf: if d == 0: return ctx.one return ctx.zero return s*0 elif ctx.isnan(abss): return 1/s if ctx.re(s) > 2*ctx.prec and a == 1 and not derivative: return ctx.one + ctx.power(2, -s) return +ctx._hurwitz(s, a, d, **kwargs) @defun def _hurwitz(ctx, s, a=1, d=0, **kwargs): prec = ctx.prec verbose = kwargs.get('verbose') try: extraprec = 10 ctx.prec += extraprec # We strongly want to special-case rational a a, atype = ctx._convert_param(a) if ctx.re(s) < 0: if verbose: print("zeta: Attempting reflection formula") try: return _hurwitz_reflection(ctx, s, a, d, atype) except NotImplementedError: pass if verbose: print("zeta: Reflection formula failed") if verbose: print("zeta: Using the Euler-Maclaurin algorithm") while 1: ctx.prec = prec + extraprec T1, T2 = _hurwitz_em(ctx, s, a, d, prec+10, verbose) cancellation = ctx.mag(T1) - ctx.mag(T1+T2) if verbose: print("Term 1:", T1) print("Term 2:", T2) print("Cancellation:", cancellation, "bits") if cancellation < extraprec: return T1 + T2 else: extraprec = max(2*extraprec, min(cancellation + 5, 100*prec)) if extraprec > kwargs.get('maxprec', 100*prec): raise ctx.NoConvergence("zeta: too much cancellation") finally: ctx.prec = prec def _hurwitz_reflection(ctx, s, a, d, atype): # TODO: implement for derivatives if d != 0: raise NotImplementedError res = ctx.re(s) negs = -s # Integer reflection formula if ctx.isnpint(s): n = int(res) if n <= 0: return ctx.bernpoly(1-n, a) / (n-1) t = 1-s # We now require a to be standardized v = 0 shift = 0 b = a while ctx.re(b) > 1: b -= 1 v -= b**negs shift -= 1 while ctx.re(b) <= 0: v += b**negs b += 1 shift += 1 # Rational reflection formula if atype == 'Q' or atype == 'Z': try: p, q = a._mpq_ except: assert a == int(a) p = int(a) q = 1 p += shift*q assert 1 <= p <= q g = ctx.fsum(ctx.cospi(t/2-2*k*b)*ctx._hurwitz(t,(k,q)) \ for k in range(1,q+1)) g *= 2*ctx.gamma(t)/(2*ctx.pi*q)**t v += g return v # General reflection formula # Note: clcos/clsin can raise NotImplementedError else: C1, C2 = ctx.cospi_sinpi(0.5*t) # Clausen functions; could maybe use polylog directly if C1: C1 *= ctx.clcos(t, 2*a, pi=True) if C2: C2 *= ctx.clsin(t, 2*a, pi=True) v += 2*ctx.gamma(t)/(2*ctx.pi)**t*(C1+C2) return v def _hurwitz_em(ctx, s, a, d, prec, verbose): # May not be converted at this point a = ctx.convert(a) tol = -prec # Estimate number of terms for Euler-Maclaurin summation; could be improved M1 = 0 M2 = prec // 3 N = M2 lsum = 0 # This speeds up the recurrence for derivatives if ctx.isint(s): s = int(ctx._re(s)) s1 = s-1 while 1: # Truncated L-series l = ctx._zetasum(s, M1+a, M2-M1-1, [d])[0][0] #if d: # l = ctx.fsum((-ctx.ln(n+a))**d * (n+a)**negs for n in range(M1,M2)) #else: # l = ctx.fsum((n+a)**negs for n in range(M1,M2)) lsum += l M2a = M2+a logM2a = ctx.ln(M2a) logM2ad = logM2a**d logs = [logM2ad] logr = 1/logM2a rM2a = 1/M2a M2as = rM2a**s if d: tailsum = ctx.gammainc(d+1, s1*logM2a) / s1**(d+1) else: tailsum = 1/((s1)*(M2a)**s1) tailsum += 0.5 * logM2ad * M2as U = [1] r = M2as fact = 2 for j in range(1, N+1): # TODO: the following could perhaps be tidied a bit j2 = 2*j if j == 1: upds = [1] else: upds = [j2-2, j2-1] for m in upds: D = min(m,d+1) if m <= d: logs.append(logs[-1] * logr) Un = [0]*(D+1) for i in xrange(D): Un[i] = (1-m-s)*U[i] for i in xrange(1,D+1): Un[i] += (d-(i-1))*U[i-1] U = Un r *= rM2a t = ctx.fdot(U, logs) * r * ctx.bernoulli(j2)/(-fact) tailsum += t if ctx.mag(t) < tol: return lsum, (-1)**d * tailsum fact *= (j2+1)*(j2+2) if verbose: print("Sum range:", M1, M2, "term magnitude", ctx.mag(t), "tolerance", tol) M1, M2 = M2, M2*2 if ctx.re(s) < 0: N += N//2 @defun def _zetasum(ctx, s, a, n, derivatives=[0], reflect=False): """ Returns [xd0,xd1,...,xdr], [yd0,yd1,...ydr] where xdk = D^k ( 1/a^s + 1/(a+1)^s + ... + 1/(a+n)^s ) ydk = D^k conj( 1/a^(1-s) + 1/(a+1)^(1-s) + ... + 1/(a+n)^(1-s) ) D^k = kth derivative with respect to s, k ranges over the given list of derivatives (which should consist of either a single element or a range 0,1,...r). If reflect=False, the ydks are not computed. """ #print "zetasum", s, a, n try: return ctx._zetasum_fast(s, a, n, derivatives, reflect) except NotImplementedError: pass negs = ctx.fneg(s, exact=True) have_derivatives = derivatives != [0] have_one_derivative = len(derivatives) == 1 if not reflect: if not have_derivatives: return [ctx.fsum((a+k)**negs for k in xrange(n+1))], [] if have_one_derivative: d = derivatives[0] x = ctx.fsum(ctx.ln(a+k)**d * (a+k)**negs for k in xrange(n+1)) return [(-1)**d * x], [] maxd = max(derivatives) if not have_one_derivative: derivatives = range(maxd+1) xs = [ctx.zero for d in derivatives] if reflect: ys = [ctx.zero for d in derivatives] else: ys = [] for k in xrange(n+1): w = a + k xterm = w ** negs if reflect: yterm = ctx.conj(ctx.one / (w * xterm)) if have_derivatives: logw = -ctx.ln(w) if have_one_derivative: logw = logw ** maxd xs[0] += xterm * logw if reflect: ys[0] += yterm * logw else: t = ctx.one for d in derivatives: xs[d] += xterm * t if reflect: ys[d] += yterm * t t *= logw else: xs[0] += xterm if reflect: ys[0] += yterm return xs, ys @defun def dirichlet(ctx, s, chi=[1], derivative=0): s = ctx.convert(s) q = len(chi) d = int(derivative) if d > 2: raise NotImplementedError("arbitrary order derivatives") prec = ctx.prec try: ctx.prec += 10 if s == 1: have_pole = True for x in chi: if x and x != 1: have_pole = False h = +ctx.eps ctx.prec *= 2*(d+1) s += h if have_pole: return +ctx.inf z = ctx.zero for p in range(1,q+1): if chi[p%q]: if d == 1: z += chi[p%q] * (ctx.zeta(s, (p,q), 1) - \ ctx.zeta(s, (p,q))*ctx.log(q)) else: z += chi[p%q] * ctx.zeta(s, (p,q)) z /= q**s finally: ctx.prec = prec return +z def secondzeta_main_term(ctx, s, a, **kwargs): tol = ctx.eps f = lambda n: ctx.gammainc(0.5*s, a*gamm**2, regularized=True)*gamm**(-s) totsum = term = ctx.zero mg = ctx.inf n = 0 while mg > tol: totsum += term n += 1 gamm = ctx.im(ctx.zetazero_memoized(n)) term = f(n) mg = abs(term) err = 0 if kwargs.get("error"): sg = ctx.re(s) err = 0.5*ctx.pi**(-1)*max(1,sg)*a**(sg-0.5)*ctx.log(gamm/(2*ctx.pi))*\ ctx.gammainc(-0.5, a*gamm**2)/abs(ctx.gamma(s/2)) err = abs(err) return +totsum, err, n def secondzeta_prime_term(ctx, s, a, **kwargs): tol = ctx.eps f = lambda n: ctx.gammainc(0.5*(1-s),0.25*ctx.log(n)**2 * a**(-1))*\ ((0.5*ctx.log(n))**(s-1))*ctx.mangoldt(n)/ctx.sqrt(n)/\ (2*ctx.gamma(0.5*s)*ctx.sqrt(ctx.pi)) totsum = term = ctx.zero mg = ctx.inf n = 1 while mg > tol or n < 9: totsum += term n += 1 term = f(n) if term == 0: mg = ctx.inf else: mg = abs(term) if kwargs.get("error"): err = mg return +totsum, err, n def secondzeta_exp_term(ctx, s, a): if ctx.isint(s) and ctx.re(s) <= 0: m = int(round(ctx.re(s))) if not m & 1: return ctx.mpf('-0.25')**(-m//2) tol = ctx.eps f = lambda n: (0.25*a)**n/((n+0.5*s)*ctx.fac(n)) totsum = ctx.zero term = f(0) mg = ctx.inf n = 0 while mg > tol: totsum += term n += 1 term = f(n) mg = abs(term) v = a**(0.5*s)*totsum/ctx.gamma(0.5*s) return v def secondzeta_singular_term(ctx, s, a, **kwargs): factor = a**(0.5*(s-1))/(4*ctx.sqrt(ctx.pi)*ctx.gamma(0.5*s)) extraprec = ctx.mag(factor) ctx.prec += extraprec factor = a**(0.5*(s-1))/(4*ctx.sqrt(ctx.pi)*ctx.gamma(0.5*s)) tol = ctx.eps f = lambda n: ctx.bernpoly(n,0.75)*(4*ctx.sqrt(a))**n*\ ctx.gamma(0.5*n)/((s+n-1)*ctx.fac(n)) totsum = ctx.zero mg1 = ctx.inf n = 1 term = f(n) mg2 = abs(term) while mg2 > tol and mg2 <= mg1: totsum += term n += 1 term = f(n) totsum += term n +=1 term = f(n) mg1 = mg2 mg2 = abs(term) totsum += term pole = -2*(s-1)**(-2)+(ctx.euler+ctx.log(16*ctx.pi**2*a))*(s-1)**(-1) st = factor*(pole+totsum) err = 0 if kwargs.get("error"): if not ((mg2 > tol) and (mg2 <= mg1)): if mg2 <= tol: err = ctx.mpf(10)**int(ctx.log(abs(factor*tol),10)) if mg2 > mg1: err = ctx.mpf(10)**int(ctx.log(abs(factor*mg1),10)) err = max(err, ctx.eps*1.) ctx.prec -= extraprec return +st, err @defun def secondzeta(ctx, s, a = 0.015, **kwargs): r""" Evaluates the secondary zeta function `Z(s)`, defined for `\mathrm{Re}(s)>1` by .. math :: Z(s) = \sum_{n=1}^{\infty} \frac{1}{\tau_n^s} where `\frac12+i\tau_n` runs through the zeros of `\zeta(s)` with imaginary part positive. `Z(s)` extends to a meromorphic function on `\mathbb{C}` with a double pole at `s=1` and simple poles at the points `-2n` for `n=0`, 1, 2, ... **Examples** >>> from mpmath import * >>> mp.pretty = True; mp.dps = 15 >>> secondzeta(2) 0.023104993115419 >>> xi = lambda s: 0.5*s*(s-1)*pi**(-0.5*s)*gamma(0.5*s)*zeta(s) >>> Xi = lambda t: xi(0.5+t*j) >>> -0.5*diff(Xi,0,n=2)/Xi(0) (0.023104993115419 + 0.0j) We may ask for an approximate error value:: >>> secondzeta(0.5+100j, error=True) ((-0.216272011276718 - 0.844952708937228j), 2.22044604925031e-16) The function has poles at the negative odd integers, and dyadic rational values at the negative even integers:: >>> mp.dps = 30 >>> secondzeta(-8) -0.67236328125 >>> secondzeta(-7) +inf **Implementation notes** The function is computed as sum of four terms `Z(s)=A(s)-P(s)+E(s)-S(s)` respectively main, prime, exponential and singular terms. The main term `A(s)` is computed from the zeros of zeta. The prime term depends on the von Mangoldt function. The singular term is responsible for the poles of the function. The four terms depends on a small parameter `a`. We may change the value of `a`. Theoretically this has no effect on the sum of the four terms, but in practice may be important. A smaller value of the parameter `a` makes `A(s)` depend on a smaller number of zeros of zeta, but `P(s)` uses more values of von Mangoldt function. We may also add a verbose option to obtain data about the values of the four terms. >>> mp.dps = 10 >>> secondzeta(0.5 + 40j, error=True, verbose=True) main term = (-30190318549.138656312556 - 13964804384.624622876523j) computed using 19 zeros of zeta prime term = (132717176.89212754625045 + 188980555.17563978290601j) computed using 9 values of the von Mangoldt function exponential term = (542447428666.07179812536 + 362434922978.80192435203j) singular term = (512124392939.98154322355 + 348281138038.65531023921j) ((0.059471043 + 0.3463514534j), 1.455191523e-11) >>> secondzeta(0.5 + 40j, a=0.04, error=True, verbose=True) main term = (-151962888.19606243907725 - 217930683.90210294051982j) computed using 9 zeros of zeta prime term = (2476659342.3038722372461 + 28711581821.921627163136j) computed using 37 values of the von Mangoldt function exponential term = (178506047114.7838188264 + 819674143244.45677330576j) singular term = (175877424884.22441310708 + 790744630738.28669174871j) ((0.059471043 + 0.3463514534j), 1.455191523e-11) Notice the great cancellation between the four terms. Changing `a`, the four terms are very different numbers but the cancellation gives the good value of Z(s). **References** A. Voros, Zeta functions for the Riemann zeros, Ann. Institute Fourier, 53, (2003) 665--699. A. Voros, Zeta functions over Zeros of Zeta Functions, Lecture Notes of the Unione Matematica Italiana, Springer, 2009. """ s = ctx.convert(s) a = ctx.convert(a) tol = ctx.eps if ctx.isint(s) and ctx.re(s) <= 1: if abs(s-1) < tol*1000: return ctx.inf m = int(round(ctx.re(s))) if m & 1: return ctx.inf else: return ((-1)**(-m//2)*\ ctx.fraction(8-ctx.eulernum(-m,exact=True),2**(-m+3))) prec = ctx.prec try: t3 = secondzeta_exp_term(ctx, s, a) extraprec = max(ctx.mag(t3),0) ctx.prec += extraprec + 3 t1, r1, gt = secondzeta_main_term(ctx,s,a,error='True', verbose='True') t2, r2, pt = secondzeta_prime_term(ctx,s,a,error='True', verbose='True') t4, r4 = secondzeta_singular_term(ctx,s,a,error='True') t3 = secondzeta_exp_term(ctx, s, a) err = r1+r2+r4 t = t1-t2+t3-t4 if kwargs.get("verbose"): print('main term =', t1) print(' computed using', gt, 'zeros of zeta') print('prime term =', t2) print(' computed using', pt, 'values of the von Mangoldt function') print('exponential term =', t3) print('singular term =', t4) finally: ctx.prec = prec if kwargs.get("error"): w = max(ctx.mag(abs(t)),0) err = max(err*2**w, ctx.eps*1.*2**w) return +t, err return +t @defun_wrapped def lerchphi(ctx, z, s, a): r""" Gives the Lerch transcendent, defined for `|z| < 1` and `\Re{a} > 0` by .. math :: \Phi(z,s,a) = \sum_{k=0}^{\infty} \frac{z^k}{(a+k)^s} and generally by the recurrence `\Phi(z,s,a) = z \Phi(z,s,a+1) + a^{-s}` along with the integral representation valid for `\Re{a} > 0` .. math :: \Phi(z,s,a) = \frac{1}{2 a^s} + \int_0^{\infty} \frac{z^t}{(a+t)^s} dt - 2 \int_0^{\infty} \frac{\sin(t \log z - s \operatorname{arctan}(t/a)}{(a^2 + t^2)^{s/2} (e^{2 \pi t}-1)} dt. The Lerch transcendent generalizes the Hurwitz zeta function :func:`zeta` (`z = 1`) and the polylogarithm :func:`polylog` (`a = 1`). **Examples** Several evaluations in terms of simpler functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> lerchphi(-1,2,0.5); 4*catalan 3.663862376708876060218414 3.663862376708876060218414 >>> diff(lerchphi, (-1,-2,1), (0,1,0)); 7*zeta(3)/(4*pi**2) 0.2131391994087528954617607 0.2131391994087528954617607 >>> lerchphi(-4,1,1); log(5)/4 0.4023594781085250936501898 0.4023594781085250936501898 >>> lerchphi(-3+2j,1,0.5); 2*atanh(sqrt(-3+2j))/sqrt(-3+2j) (1.142423447120257137774002 + 0.2118232380980201350495795j) (1.142423447120257137774002 + 0.2118232380980201350495795j) Evaluation works for complex arguments and `|z| \ge 1`:: >>> lerchphi(1+2j, 3-j, 4+2j) (0.002025009957009908600539469 + 0.003327897536813558807438089j) >>> lerchphi(-2,2,-2.5) -12.28676272353094275265944 >>> lerchphi(10,10,10) (-4.462130727102185701817349e-11 + 1.575172198981096218823481e-12j) >>> lerchphi(10,10,-10.5) (112658784011940.5605789002 + 498113185.5756221777743631j) Some degenerate cases:: >>> lerchphi(0,1,2) 0.5 >>> lerchphi(0,1,-2) -0.5 **References** 1. [DLMF]_ section 25.14 """ if z == 0: return a ** (-s) """ # Faster, but these cases are useful for testing right now if z == 1: return ctx.zeta(s, a) if a == 1: return z * ctx.polylog(s, z) """ if ctx.re(a) < 1: if ctx.isnpint(a): raise ValueError("Lerch transcendent complex infinity") m = int(ctx.ceil(1-ctx.re(a))) v = ctx.zero zpow = ctx.one for n in xrange(m): v += zpow / (a+n)**s zpow *= z return zpow * ctx.lerchphi(z,s, a+m) + v g = ctx.ln(z) v = 1/(2*a**s) + ctx.gammainc(1-s, -a*g) * (-g)**(s-1) / z**a h = s / 2 r = 2*ctx.pi f = lambda t: ctx.sin(s*ctx.atan(t/a)-t*g) / \ ((a**2+t**2)**h * ctx.expm1(r*t)) v += 2*ctx.quad(f, [0, ctx.inf]) if not ctx.im(z) and not ctx.im(s) and not ctx.im(a) and ctx.re(z) < 1: v = ctx.chop(v) return v wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/hypergeometric.py0000644000175000017500000014454412014170666030011 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .functions import defun, defun_wrapped def _check_need_perturb(ctx, terms, prec, discard_known_zeros): perturb = recompute = False extraprec = 0 discard = [] for term_index, term in enumerate(terms): w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term have_singular_nongamma_weight = False # Avoid division by zero in leading factors (TODO: # also check for near division by zero?) for k, w in enumerate(w_s): if not w: if ctx.re(c_s[k]) <= 0 and c_s[k]: perturb = recompute = True have_singular_nongamma_weight = True pole_count = [0, 0, 0] # Check for gamma and series poles and near-poles for data_index, data in enumerate([alpha_s, beta_s, b_s]): for i, x in enumerate(data): n, d = ctx.nint_distance(x) # Poles if n > 0: continue if d == ctx.ninf: # OK if we have a polynomial # ------------------------------ ok = False if data_index == 2: for u in a_s: if ctx.isnpint(u) and u >= int(n): ok = True break if ok: continue pole_count[data_index] += 1 # ------------------------------ #perturb = recompute = True #return perturb, recompute, extraprec elif d < -4: extraprec += -d recompute = True if discard_known_zeros and pole_count[1] > pole_count[0] + pole_count[2] \ and not have_singular_nongamma_weight: discard.append(term_index) elif sum(pole_count): perturb = recompute = True return perturb, recompute, extraprec, discard _hypercomb_msg = """ hypercomb() failed to converge to the requested %i bits of accuracy using a working precision of %i bits. The function value may be zero or infinite; try passing zeroprec=N or infprec=M to bound finite values between 2^(-N) and 2^M. Otherwise try a higher maxprec or maxterms. """ @defun def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs): orig = ctx.prec sumvalue = ctx.zero dist = ctx.nint_distance ninf = ctx.ninf orig_params = params[:] verbose = kwargs.get('verbose', False) maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(orig)) kwargs['maxprec'] = maxprec # For calls to hypsum zeroprec = kwargs.get('zeroprec') infprec = kwargs.get('infprec') perturbed_reference_value = None hextra = 0 try: while 1: ctx.prec += 10 if ctx.prec > maxprec: raise ValueError(_hypercomb_msg % (orig, ctx.prec)) orig2 = ctx.prec params = orig_params[:] terms = function(*params) if verbose: print() print("ENTERING hypercomb main loop") print("prec =", ctx.prec) print("hextra", hextra) perturb, recompute, extraprec, discard = \ _check_need_perturb(ctx, terms, orig, discard_known_zeros) ctx.prec += extraprec if perturb: if "hmag" in kwargs: hmag = kwargs["hmag"] elif ctx._fixed_precision: hmag = int(ctx.prec*0.3) else: hmag = orig + 10 + hextra h = ctx.ldexp(ctx.one, -hmag) ctx.prec = orig2 + 10 + hmag + 10 for k in range(len(params)): params[k] += h # Heuristically ensure that the perturbations # are "independent" so that two perturbations # don't accidentally cancel each other out # in a subtraction. h += h/(k+1) if recompute: terms = function(*params) if discard_known_zeros: terms = [term for (i, term) in enumerate(terms) if i not in discard] if not terms: return ctx.zero evaluated_terms = [] for term_index, term_data in enumerate(terms): w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term_data if verbose: print() print(" Evaluating term %i/%i : %iF%i" % \ (term_index+1, len(terms), len(a_s), len(b_s))) print(" powers", ctx.nstr(w_s), ctx.nstr(c_s)) print(" gamma", ctx.nstr(alpha_s), ctx.nstr(beta_s)) print(" hyper", ctx.nstr(a_s), ctx.nstr(b_s)) print(" z", ctx.nstr(z)) #v = ctx.hyper(a_s, b_s, z, **kwargs) #for a in alpha_s: v *= ctx.gamma(a) #for b in beta_s: v *= ctx.rgamma(b) #for w, c in zip(w_s, c_s): v *= ctx.power(w, c) v = ctx.fprod([ctx.hyper(a_s, b_s, z, **kwargs)] + \ [ctx.gamma(a) for a in alpha_s] + \ [ctx.rgamma(b) for b in beta_s] + \ [ctx.power(w,c) for (w,c) in zip(w_s,c_s)]) if verbose: print(" Value:", v) evaluated_terms.append(v) if len(terms) == 1 and (not perturb): sumvalue = evaluated_terms[0] break if ctx._fixed_precision: sumvalue = ctx.fsum(evaluated_terms) break sumvalue = ctx.fsum(evaluated_terms) term_magnitudes = [ctx.mag(x) for x in evaluated_terms] max_magnitude = max(term_magnitudes) sum_magnitude = ctx.mag(sumvalue) cancellation = max_magnitude - sum_magnitude if verbose: print() print(" Cancellation:", cancellation, "bits") print(" Increased precision:", ctx.prec - orig, "bits") precision_ok = cancellation < ctx.prec - orig if zeroprec is None: zero_ok = False else: zero_ok = max_magnitude - ctx.prec < -zeroprec if infprec is None: inf_ok = False else: inf_ok = max_magnitude > infprec if precision_ok and (not perturb) or ctx.isnan(cancellation): break elif precision_ok: if perturbed_reference_value is None: hextra += 20 perturbed_reference_value = sumvalue continue elif ctx.mag(sumvalue - perturbed_reference_value) <= \ ctx.mag(sumvalue) - orig: break elif zero_ok: sumvalue = ctx.zero break elif inf_ok: sumvalue = ctx.inf break elif 'hmag' in kwargs: break else: hextra *= 2 perturbed_reference_value = sumvalue # Increase precision else: increment = min(max(cancellation, orig//2), max(extraprec,orig)) ctx.prec += increment if verbose: print(" Must start over with increased precision") continue finally: ctx.prec = orig return +sumvalue @defun def hyper(ctx, a_s, b_s, z, **kwargs): """ Hypergeometric function, general case. """ z = ctx.convert(z) p = len(a_s) q = len(b_s) a_s = [ctx._convert_param(a) for a in a_s] b_s = [ctx._convert_param(b) for b in b_s] # Reduce degree by eliminating common parameters if kwargs.get('eliminate', True): i = 0 while i < q and a_s: b = b_s[i] if b in a_s: a_s.remove(b) b_s.remove(b) p -= 1 q -= 1 else: i += 1 # Handle special cases if p == 0: if q == 1: return ctx._hyp0f1(b_s, z, **kwargs) elif q == 0: return ctx.exp(z) elif p == 1: if q == 1: return ctx._hyp1f1(a_s, b_s, z, **kwargs) elif q == 2: return ctx._hyp1f2(a_s, b_s, z, **kwargs) elif q == 0: return ctx._hyp1f0(a_s[0][0], z) elif p == 2: if q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs) elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs) elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs) elif q == 0: return ctx._hyp2f0(a_s, b_s, z, **kwargs) elif p == q+1: return ctx._hypq1fq(p, q, a_s, b_s, z, **kwargs) elif p > q+1 and not kwargs.get('force_series'): return ctx._hyp_borel(p, q, a_s, b_s, z, **kwargs) coeffs, types = zip(*(a_s+b_s)) return ctx.hypsum(p, q, types, coeffs, z, **kwargs) @defun def hyp0f1(ctx,b,z,**kwargs): return ctx.hyper([],[b],z,**kwargs) @defun def hyp1f1(ctx,a,b,z,**kwargs): return ctx.hyper([a],[b],z,**kwargs) @defun def hyp1f2(ctx,a1,b1,b2,z,**kwargs): return ctx.hyper([a1],[b1,b2],z,**kwargs) @defun def hyp2f1(ctx,a,b,c,z,**kwargs): return ctx.hyper([a,b],[c],z,**kwargs) @defun def hyp2f2(ctx,a1,a2,b1,b2,z,**kwargs): return ctx.hyper([a1,a2],[b1,b2],z,**kwargs) @defun def hyp2f3(ctx,a1,a2,b1,b2,b3,z,**kwargs): return ctx.hyper([a1,a2],[b1,b2,b3],z,**kwargs) @defun def hyp2f0(ctx,a,b,z,**kwargs): return ctx.hyper([a,b],[],z,**kwargs) @defun def hyp3f2(ctx,a1,a2,a3,b1,b2,z,**kwargs): return ctx.hyper([a1,a2,a3],[b1,b2],z,**kwargs) @defun_wrapped def _hyp1f0(ctx, a, z): return (1-z) ** (-a) @defun def _hyp0f1(ctx, b_s, z, **kwargs): (b, btype), = b_s if z: magz = ctx.mag(z) else: magz = 0 if magz >= 8 and not kwargs.get('force_series'): try: # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric0F1/06/02/03/0004/ # We don't need hypercomb because the only possible singularity # occurs when the value is undefined. However, we should perhaps # still check for cancellation... # TODO: handle the all-real case more efficiently! # TODO: figure out how much precision is needed (exponential growth) orig = ctx.prec try: ctx.prec += 12 + magz//2 w = ctx.sqrt(-z) jw = ctx.j*w u = 1/(4*jw) c = ctx.mpq_1_2 - b E = ctx.exp(2*jw) H1 = (-jw)**c/E*ctx.hyp2f0(b-ctx.mpq_1_2, ctx.mpq_3_2-b, -u, force_series=True) H2 = (jw)**c*E*ctx.hyp2f0(b-ctx.mpq_1_2, ctx.mpq_3_2-b, u, force_series=True) v = ctx.gamma(b)/(2*ctx.sqrt(ctx.pi))*(H1 + H2) finally: ctx.prec = orig if ctx._is_real_type(b) and ctx._is_real_type(z): v = ctx._re(v) return +v except ctx.NoConvergence: pass return ctx.hypsum(0, 1, (btype,), [b], z, **kwargs) @defun def _hyp1f1(ctx, a_s, b_s, z, **kwargs): (a, atype), = a_s (b, btype), = b_s if not z: return ctx.one+z magz = ctx.mag(z) if magz >= 7 and not (ctx.isint(a) and ctx.re(a) <= 0): if ctx.isinf(z): if ctx.sign(a) == ctx.sign(b) == ctx.sign(z) == 1: return ctx.inf return ctx.nan * z try: try: ctx.prec += magz sector = ctx._im(z) < 0 def h(a,b): if sector: E = ctx.expjpi(ctx.fneg(a, exact=True)) else: E = ctx.expjpi(a) rz = 1/z T1 = ([E,z], [1,-a], [b], [b-a], [a, 1+a-b], [], -rz) T2 = ([ctx.exp(z),z], [1,a-b], [b], [a], [b-a, 1-a], [], rz) return T1, T2 v = ctx.hypercomb(h, [a,b], force_series=True) if ctx._is_real_type(a) and ctx._is_real_type(b) and ctx._is_real_type(z): v = ctx._re(v) return +v except ctx.NoConvergence: pass finally: ctx.prec -= magz v = ctx.hypsum(1, 1, (atype, btype), [a, b], z, **kwargs) return v def _hyp2f1_gosper(ctx,a,b,c,z,**kwargs): # Use Gosper's recurrence # See http://www.math.utexas.edu/pipermail/maxima/2006/000126.html _a,_b,_c,_z = a, b, c, z orig = ctx.prec maxprec = kwargs.get('maxprec', 100*orig) extra = 10 while 1: ctx.prec = orig + extra #a = ctx.convert(_a) #b = ctx.convert(_b) #c = ctx.convert(_c) z = ctx.convert(_z) d = ctx.mpf(0) e = ctx.mpf(1) f = ctx.mpf(0) k = 0 # Common subexpression elimination, unfortunately making # things a bit unreadable. The formula is quite messy to begin # with, though... abz = a*b*z ch = c * ctx.mpq_1_2 c1h = (c+1) * ctx.mpq_1_2 nz = 1-z g = z/nz abg = a*b*g cba = c-b-a z2 = z-2 tol = -ctx.prec - 10 nstr = ctx.nstr nprint = ctx.nprint mag = ctx.mag maxmag = ctx.ninf while 1: kch = k+ch kakbz = (k+a)*(k+b)*z / (4*(k+1)*kch*(k+c1h)) d1 = kakbz*(e-(k+cba)*d*g) e1 = kakbz*(d*abg+(k+c)*e) ft = d*(k*(cba*z+k*z2-c)-abz)/(2*kch*nz) f1 = f + e - ft maxmag = max(maxmag, mag(f1)) if mag(f1-f) < tol: break d, e, f = d1, e1, f1 k += 1 cancellation = maxmag - mag(f1) if cancellation < extra: break else: extra += cancellation if extra > maxprec: raise ctx.NoConvergence return f1 @defun def _hyp2f1(ctx, a_s, b_s, z, **kwargs): (a, atype), (b, btype) = a_s (c, ctype), = b_s if z == 1: # TODO: the following logic can be simplified convergent = ctx.re(c-a-b) > 0 finite = (ctx.isint(a) and a <= 0) or (ctx.isint(b) and b <= 0) zerodiv = ctx.isint(c) and c <= 0 and not \ ((ctx.isint(a) and c <= a <= 0) or (ctx.isint(b) and c <= b <= 0)) #print "bz", a, b, c, z, convergent, finite, zerodiv # Gauss's theorem gives the value if convergent if (convergent or finite) and not zerodiv: return ctx.gammaprod([c, c-a-b], [c-a, c-b], _infsign=True) # Otherwise, there is a pole and we take the # sign to be that when approaching from below # XXX: this evaluation is not necessarily correct in all cases return ctx.hyp2f1(a,b,c,1-ctx.eps*2) * ctx.inf # Equal to 1 (first term), unless there is a subsequent # division by zero if not z: # Division by zero but power of z is higher than # first order so cancels if c or a == 0 or b == 0: return 1+z # Indeterminate return ctx.nan # Hit zero denominator unless numerator goes to 0 first if ctx.isint(c) and c <= 0: if (ctx.isint(a) and c <= a <= 0) or \ (ctx.isint(b) and c <= b <= 0): pass else: # Pole in series return ctx.inf absz = abs(z) # Fast case: standard series converges rapidly, # possibly in finitely many terms if absz <= 0.8 or (ctx.isint(a) and a <= 0 and a >= -1000) or \ (ctx.isint(b) and b <= 0 and b >= -1000): return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs) orig = ctx.prec try: ctx.prec += 10 # Use 1/z transformation if absz >= 1.3: def h(a,b): t = ctx.mpq_1-c; ab = a-b; rz = 1/z T1 = ([-z],[-a], [c,-ab],[b,c-a], [a,t+a],[ctx.mpq_1+ab], rz) T2 = ([-z],[-b], [c,ab],[a,c-b], [b,t+b],[ctx.mpq_1-ab], rz) return T1, T2 v = ctx.hypercomb(h, [a,b], **kwargs) # Use 1-z transformation elif abs(1-z) <= 0.75: def h(a,b): t = c-a-b; ca = c-a; cb = c-b; rz = 1-z T1 = [], [], [c,t], [ca,cb], [a,b], [1-t], rz T2 = [rz], [t], [c,a+b-c], [a,b], [ca,cb], [1+t], rz return T1, T2 v = ctx.hypercomb(h, [a,b], **kwargs) # Use z/(z-1) transformation elif abs(z/(z-1)) <= 0.75: v = ctx.hyp2f1(a, c-b, c, z/(z-1)) / (1-z)**a # Remaining part of unit circle else: v = _hyp2f1_gosper(ctx,a,b,c,z,**kwargs) finally: ctx.prec = orig return +v @defun def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs): r""" Evaluates 3F2, 4F3, 5F4, ... """ a_s, a_types = zip(*a_s) b_s, b_types = zip(*b_s) a_s = list(a_s) b_s = list(b_s) absz = abs(z) ispoly = False for a in a_s: if ctx.isint(a) and a <= 0: ispoly = True break # Direct summation if absz < 1 or ispoly: try: return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) except ctx.NoConvergence: if absz > 1.1 or ispoly: raise # Use expansion at |z-1| -> 0. # Reference: Wolfgang Buhring, "Generalized Hypergeometric Functions at # Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992), # pp.145-153 # The current implementation has several problems: # 1. We only implement it for 3F2. The expansion coefficients are # given by extremely messy nested sums in the higher degree cases # (see reference). Is efficient sequential generation of the coefficients # possible in the > 3F2 case? # 2. Although the series converges, it may do so slowly, so we need # convergence acceleration. The acceleration implemented by # nsum does not always help, so results returned are sometimes # inaccurate! Can we do better? # 3. We should check conditions for convergence, and possibly # do a better job of cancelling out gamma poles if possible. if z == 1: # XXX: should also check for division by zero in the # denominator of the series (cf. hyp2f1) S = ctx.re(sum(b_s)-sum(a_s)) if S <= 0: #return ctx.hyper(a_s, b_s, 1-ctx.eps*2, **kwargs) * ctx.inf return ctx.hyper(a_s, b_s, 0.9, **kwargs) * ctx.inf if (p,q) == (3,2) and abs(z-1) < 0.05: # and kwargs.get('sum1') #print "Using alternate summation (experimental)" a1,a2,a3 = a_s b1,b2 = b_s u = b1+b2-a3 initial = ctx.gammaprod([b2-a3,b1-a3,a1,a2],[b2-a3,b1-a3,1,u]) def term(k, _cache={0:initial}): u = b1+b2-a3+k if k in _cache: t = _cache[k] else: t = _cache[k-1] t *= (b1+k-a3-1)*(b2+k-a3-1) t /= k*(u-1) _cache[k] = t return t * ctx.hyp2f1(a1,a2,u,z) try: S = ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), strict=kwargs.get('strict', True)) return S * ctx.gammaprod([b1,b2],[a1,a2,a3]) except ctx.NoConvergence: pass # Try to use convergence acceleration on and close to the unit circle. # Problem: the convergence acceleration degenerates as |z-1| -> 0, # except for special cases. Everywhere else, the Shanks transformation # is very efficient. if absz < 1.1 and ctx._re(z) <= 1: def term(kk, _cache={0:ctx.one}): k = int(kk) if k != kk: t = z ** ctx.mpf(kk) / ctx.fac(kk) for a in a_s: t *= ctx.rf(a,kk) for b in b_s: t /= ctx.rf(b,kk) return t if k in _cache: return _cache[k] t = term(k-1) m = k-1 for j in xrange(p): t *= (a_s[j]+m) for j in xrange(q): t /= (b_s[j]+m) t *= z t /= k _cache[k] = t return t sum_method = kwargs.get('sum_method', 'r+s+e') try: return ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), strict=kwargs.get('strict', True), method=sum_method.replace('e','')) except ctx.NoConvergence: if 'e' not in sum_method: raise pass if kwargs.get('verbose'): print("Attempting Euler-Maclaurin summation") """ Somewhat slower version (one diffs_exp for each factor). However, this would be faster with fast direct derivatives of the gamma function. def power_diffs(k0): r = 0 l = ctx.log(z) while 1: yield z**ctx.mpf(k0) * l**r r += 1 def loggamma_diffs(x, reciprocal=False): sign = (-1) ** reciprocal yield sign * ctx.loggamma(x) i = 0 while 1: yield sign * ctx.psi(i,x) i += 1 def hyper_diffs(k0): b2 = b_s + [1] A = [ctx.diffs_exp(loggamma_diffs(a+k0)) for a in a_s] B = [ctx.diffs_exp(loggamma_diffs(b+k0,True)) for b in b2] Z = [power_diffs(k0)] C = ctx.gammaprod([b for b in b2], [a for a in a_s]) for d in ctx.diffs_prod(A + B + Z): v = C * d yield v """ def log_diffs(k0): b2 = b_s + [1] yield sum(ctx.loggamma(a+k0) for a in a_s) - \ sum(ctx.loggamma(b+k0) for b in b2) + k0*ctx.log(z) i = 0 while 1: v = sum(ctx.psi(i,a+k0) for a in a_s) - \ sum(ctx.psi(i,b+k0) for b in b2) if i == 0: v += ctx.log(z) yield v i += 1 def hyper_diffs(k0): C = ctx.gammaprod([b for b in b_s], [a for a in a_s]) for d in ctx.diffs_exp(log_diffs(k0)): v = C * d yield v tol = ctx.eps / 1024 prec = ctx.prec try: trunc = 50 * ctx.dps ctx.prec += 20 for i in xrange(5): head = ctx.fsum(term(k) for k in xrange(trunc)) tail, err = ctx.sumem(term, [trunc, ctx.inf], tol=tol, adiffs=hyper_diffs(trunc), verbose=kwargs.get('verbose'), error=True, _fast_abort=True) if err < tol: v = head + tail break trunc *= 2 # Need to increase precision because calculation of # derivatives may be inaccurate ctx.prec += ctx.prec//2 if i == 4: raise ctx.NoConvergence(\ "Euler-Maclaurin summation did not converge") finally: ctx.prec = prec return +v # Use 1/z transformation # http://functions.wolfram.com/HypergeometricFunctions/ # HypergeometricPFQ/06/01/05/02/0004/ def h(*args): a_s = list(args[:p]) b_s = list(args[p:]) Ts = [] recz = ctx.one/z negz = ctx.fneg(z, exact=True) for k in range(q+1): ak = a_s[k] C = [negz] Cp = [-ak] Gn = b_s + [ak] + [a_s[j]-ak for j in range(q+1) if j != k] Gd = a_s + [b_s[j]-ak for j in range(q)] Fn = [ak] + [ak-b_s[j]+1 for j in range(q)] Fd = [1-a_s[j]+ak for j in range(q+1) if j != k] Ts.append((C, Cp, Gn, Gd, Fn, Fd, recz)) return Ts return ctx.hypercomb(h, a_s+b_s, **kwargs) @defun def _hyp_borel(ctx, p, q, a_s, b_s, z, **kwargs): if a_s: a_s, a_types = zip(*a_s) a_s = list(a_s) else: a_s, a_types = [], () if b_s: b_s, b_types = zip(*b_s) b_s = list(b_s) else: b_s, b_types = [], () kwargs['maxterms'] = kwargs.get('maxterms', ctx.prec) try: return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) except ctx.NoConvergence: pass prec = ctx.prec try: tol = kwargs.get('asymp_tol', ctx.eps/4) ctx.prec += 10 # hypsum is has a conservative tolerance. So we try again: def term(k, cache={0:ctx.one}): if k in cache: return cache[k] t = term(k-1) for a in a_s: t *= (a+(k-1)) for b in b_s: t /= (b+(k-1)) t *= z t /= k cache[k] = t return t s = ctx.one for k in xrange(1, ctx.prec): t = term(k) s += t if abs(t) <= tol: return s finally: ctx.prec = prec if p <= q+3: contour = kwargs.get('contour') if not contour: if ctx.arg(z) < 0.25: u = z / max(1, abs(z)) if ctx.arg(z) >= 0: contour = [0, 2j, (2j+2)/u, 2/u, ctx.inf] else: contour = [0, -2j, (-2j+2)/u, 2/u, ctx.inf] #contour = [0, 2j/z, 2/z, ctx.inf] #contour = [0, 2j, 2/z, ctx.inf] #contour = [0, 2j, ctx.inf] else: contour = [0, ctx.inf] quad_kwargs = kwargs.get('quad_kwargs', {}) def g(t): return ctx.exp(-t)*ctx.hyper(a_s, b_s+[1], t*z) I, err = ctx.quad(g, contour, error=True, **quad_kwargs) if err <= abs(I)*ctx.eps*8: return I raise ctx.NoConvergence @defun def _hyp2f2(ctx, a_s, b_s, z, **kwargs): (a1, a1type), (a2, a2type) = a_s (b1, b1type), (b2, b2type) = b_s absz = abs(z) magz = ctx.mag(z) orig = ctx.prec # Asymptotic expansion is ~ exp(z) asymp_extraprec = magz # Asymptotic series is in terms of 3F1 can_use_asymptotic = (not kwargs.get('force_series')) and \ (ctx.mag(absz) > 3) # TODO: much of the following could be shared with 2F3 instead of # copypasted if can_use_asymptotic: #print "using asymp" try: try: ctx.prec += asymp_extraprec # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric2F2/06/02/02/0002/ def h(a1,a2,b1,b2): X = a1+a2-b1-b2 A2 = a1+a2 B2 = b1+b2 c = {} c[0] = ctx.one c[1] = (A2-1)*X+b1*b2-a1*a2 s1 = 0 k = 0 tprev = 0 while 1: if k not in c: uu1 = 1-B2+2*a1+a1**2+2*a2+a2**2-A2*B2+a1*a2+b1*b2+(2*B2-3*(A2+1))*k+2*k**2 uu2 = (k-A2+b1-1)*(k-A2+b2-1)*(k-X-2) c[k] = ctx.one/k * (uu1*c[k-1]-uu2*c[k-2]) t1 = c[k] * z**(-k) if abs(t1) < 0.1*ctx.eps: #print "Convergence :)" break # Quit if the series doesn't converge quickly enough if k > 5 and abs(tprev) / abs(t1) < 1.5: #print "No convergence :(" raise ctx.NoConvergence s1 += t1 tprev = t1 k += 1 S = ctx.exp(z)*s1 T1 = [z,S], [X,1], [b1,b2],[a1,a2],[],[],0 T2 = [-z],[-a1],[b1,b2,a2-a1],[a2,b1-a1,b2-a1],[a1,a1-b1+1,a1-b2+1],[a1-a2+1],-1/z T3 = [-z],[-a2],[b1,b2,a1-a2],[a1,b1-a2,b2-a2],[a2,a2-b1+1,a2-b2+1],[-a1+a2+1],-1/z return T1, T2, T3 v = ctx.hypercomb(h, [a1,a2,b1,b2], force_series=True, maxterms=4*ctx.prec) if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,z]) == 5: v = ctx.re(v) return v except ctx.NoConvergence: pass finally: ctx.prec = orig return ctx.hypsum(2, 2, (a1type, a2type, b1type, b2type), [a1, a2, b1, b2], z, **kwargs) @defun def _hyp1f2(ctx, a_s, b_s, z, **kwargs): (a1, a1type), = a_s (b1, b1type), (b2, b2type) = b_s absz = abs(z) magz = ctx.mag(z) orig = ctx.prec # Asymptotic expansion is ~ exp(sqrt(z)) asymp_extraprec = z and magz//2 # Asymptotic series is in terms of 3F0 can_use_asymptotic = (not kwargs.get('force_series')) and \ (ctx.mag(absz) > 19) and \ (ctx.sqrt(absz) > 1.5*orig) #and \ #ctx._hyp_check_convergence([a1, a1-b1+1, a1-b2+1], [], # 1/absz, orig+40+asymp_extraprec) # TODO: much of the following could be shared with 2F3 instead of # copypasted if can_use_asymptotic: #print "using asymp" try: try: ctx.prec += asymp_extraprec # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric1F2/06/02/03/ def h(a1,b1,b2): X = ctx.mpq_1_2*(a1-b1-b2+ctx.mpq_1_2) c = {} c[0] = ctx.one c[1] = 2*(ctx.mpq_1_4*(3*a1+b1+b2-2)*(a1-b1-b2)+b1*b2-ctx.mpq_3_16) c[2] = 2*(b1*b2+ctx.mpq_1_4*(a1-b1-b2)*(3*a1+b1+b2-2)-ctx.mpq_3_16)**2+\ ctx.mpq_1_16*(-16*(2*a1-3)*b1*b2 + \ 4*(a1-b1-b2)*(-8*a1**2+11*a1+b1+b2-2)-3) s1 = 0 s2 = 0 k = 0 tprev = 0 while 1: if k not in c: uu1 = (3*k**2+(-6*a1+2*b1+2*b2-4)*k + 3*a1**2 - \ (b1-b2)**2 - 2*a1*(b1+b2-2) + ctx.mpq_1_4) uu2 = (k-a1+b1-b2-ctx.mpq_1_2)*(k-a1-b1+b2-ctx.mpq_1_2)*\ (k-a1+b1+b2-ctx.mpq_5_2) c[k] = ctx.one/(2*k)*(uu1*c[k-1]-uu2*c[k-2]) w = c[k] * (-z)**(-0.5*k) t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w t2 = ctx.j**k * ctx.mpf(2)**(-k) * w if abs(t1) < 0.1*ctx.eps: #print "Convergence :)" break # Quit if the series doesn't converge quickly enough if k > 5 and abs(tprev) / abs(t1) < 1.5: #print "No convergence :(" raise ctx.NoConvergence s1 += t1 s2 += t2 tprev = t1 k += 1 S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2], [a1],\ [], [], 0 T2 = [-z], [-a1], [b1,b2],[b1-a1,b2-a1], \ [a1,a1-b1+1,a1-b2+1], [], 1/z return T1, T2 v = ctx.hypercomb(h, [a1,b1,b2], force_series=True, maxterms=4*ctx.prec) if sum(ctx._is_real_type(u) for u in [a1,b1,b2,z]) == 4: v = ctx.re(v) return v except ctx.NoConvergence: pass finally: ctx.prec = orig #print "not using asymp" return ctx.hypsum(1, 2, (a1type, b1type, b2type), [a1, b1, b2], z, **kwargs) @defun def _hyp2f3(ctx, a_s, b_s, z, **kwargs): (a1, a1type), (a2, a2type) = a_s (b1, b1type), (b2, b2type), (b3, b3type) = b_s absz = abs(z) magz = ctx.mag(z) # Asymptotic expansion is ~ exp(sqrt(z)) asymp_extraprec = z and magz//2 orig = ctx.prec # Asymptotic series is in terms of 4F1 # The square root below empirically provides a plausible criterion # for the leading series to converge can_use_asymptotic = (not kwargs.get('force_series')) and \ (ctx.mag(absz) > 19) and (ctx.sqrt(absz) > 1.5*orig) if can_use_asymptotic: #print "using asymp" try: try: ctx.prec += asymp_extraprec # http://functions.wolfram.com/HypergeometricFunctions/ # Hypergeometric2F3/06/02/03/01/0002/ def h(a1,a2,b1,b2,b3): X = ctx.mpq_1_2*(a1+a2-b1-b2-b3+ctx.mpq_1_2) A2 = a1+a2 B3 = b1+b2+b3 A = a1*a2 B = b1*b2+b3*b2+b1*b3 R = b1*b2*b3 c = {} c[0] = ctx.one c[1] = 2*(B - A + ctx.mpq_1_4*(3*A2+B3-2)*(A2-B3) - ctx.mpq_3_16) c[2] = ctx.mpq_1_2*c[1]**2 + ctx.mpq_1_16*(-16*(2*A2-3)*(B-A) + 32*R +\ 4*(-8*A2**2 + 11*A2 + 8*A + B3 - 2)*(A2-B3)-3) s1 = 0 s2 = 0 k = 0 tprev = 0 while 1: if k not in c: uu1 = (k-2*X-3)*(k-2*X-2*b1-1)*(k-2*X-2*b2-1)*\ (k-2*X-2*b3-1) uu2 = (4*(k-1)**3 - 6*(4*X+B3)*(k-1)**2 + \ 2*(24*X**2+12*B3*X+4*B+B3-1)*(k-1) - 32*X**3 - \ 24*B3*X**2 - 4*B - 8*R - 4*(4*B+B3-1)*X + 2*B3-1) uu3 = (5*(k-1)**2+2*(-10*X+A2-3*B3+3)*(k-1)+2*c[1]) c[k] = ctx.one/(2*k)*(uu1*c[k-3]-uu2*c[k-2]+uu3*c[k-1]) w = c[k] * ctx.power(-z, -0.5*k) t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w t2 = ctx.j**k * ctx.mpf(2)**(-k) * w if abs(t1) < 0.1*ctx.eps: break # Quit if the series doesn't converge quickly enough if k > 5 and abs(tprev) / abs(t1) < 1.5: raise ctx.NoConvergence s1 += t1 s2 += t2 tprev = t1 k += 1 S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2, b3], [a1, a2],\ [], [], 0 T2 = [-z], [-a1], [b1,b2,b3,a2-a1],[a2,b1-a1,b2-a1,b3-a1], \ [a1,a1-b1+1,a1-b2+1,a1-b3+1], [a1-a2+1], 1/z T3 = [-z], [-a2], [b1,b2,b3,a1-a2],[a1,b1-a2,b2-a2,b3-a2], \ [a2,a2-b1+1,a2-b2+1,a2-b3+1],[-a1+a2+1], 1/z return T1, T2, T3 v = ctx.hypercomb(h, [a1,a2,b1,b2,b3], force_series=True, maxterms=4*ctx.prec) if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,b3,z]) == 6: v = ctx.re(v) return v except ctx.NoConvergence: pass finally: ctx.prec = orig return ctx.hypsum(2, 3, (a1type, a2type, b1type, b2type, b3type), [a1, a2, b1, b2, b3], z, **kwargs) @defun def _hyp2f0(ctx, a_s, b_s, z, **kwargs): (a, atype), (b, btype) = a_s # We want to try aggressively to use the asymptotic expansion, # and fall back only when absolutely necessary try: kwargsb = kwargs.copy() kwargsb['maxterms'] = kwargsb.get('maxterms', ctx.prec) return ctx.hypsum(2, 0, (atype,btype), [a,b], z, **kwargsb) except ctx.NoConvergence: if kwargs.get('force_series'): raise pass def h(a, b): w = ctx.sinpi(b) rz = -1/z T1 = ([ctx.pi,w,rz],[1,-1,a],[],[a-b+1,b],[a],[b],rz) T2 = ([-ctx.pi,w,rz],[1,-1,1+a-b],[],[a,2-b],[a-b+1],[2-b],rz) return T1, T2 return ctx.hypercomb(h, [a, 1+a-b], **kwargs) @defun def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs): an, ap = a_s bm, bq = b_s n = len(an) p = n + len(ap) m = len(bm) q = m + len(bq) a = an+ap b = bm+bq a = [ctx.convert(_) for _ in a] b = [ctx.convert(_) for _ in b] z = ctx.convert(z) if series is None: if p < q: series = 1 if p > q: series = 2 if p == q: if m+n == p and abs(z) > 1: series = 2 else: series = 1 if kwargs.get('verbose'): print("Meijer G m,n,p,q,series =", m,n,p,q,series) if series == 1: def h(*args): a = args[:p] b = args[p:] terms = [] for k in range(m): bases = [z] expts = [b[k]/r] gn = [b[j]-b[k] for j in range(m) if j != k] gn += [1-a[j]+b[k] for j in range(n)] gd = [a[j]-b[k] for j in range(n,p)] gd += [1-b[j]+b[k] for j in range(m,q)] hn = [1-a[j]+b[k] for j in range(p)] hd = [1-b[j]+b[k] for j in range(q) if j != k] hz = (-ctx.one)**(p-m-n) * z**(ctx.one/r) terms.append((bases, expts, gn, gd, hn, hd, hz)) return terms else: def h(*args): a = args[:p] b = args[p:] terms = [] for k in range(n): bases = [z] if r == 1: expts = [a[k]-1] else: expts = [(a[k]-1)/ctx.convert(r)] gn = [a[k]-a[j] for j in range(n) if j != k] gn += [1-a[k]+b[j] for j in range(m)] gd = [a[k]-b[j] for j in range(m,q)] gd += [1-a[k]+a[j] for j in range(n,p)] hn = [1-a[k]+b[j] for j in range(q)] hd = [1+a[j]-a[k] for j in range(p) if j != k] hz = (-ctx.one)**(q-m-n) / z**(ctx.one/r) terms.append((bases, expts, gn, gd, hn, hd, hz)) return terms return ctx.hypercomb(h, a+b, **kwargs) @defun_wrapped def appellf1(ctx,a,b1,b2,c,x,y,**kwargs): # Assume x smaller # We will use x for the outer loop if abs(x) > abs(y): x, y = y, x b1, b2 = b2, b1 def ok(x): return abs(x) < 0.99 # Finite cases if ctx.isnpint(a): pass elif ctx.isnpint(b1): pass elif ctx.isnpint(b2): x, y, b1, b2 = y, x, b2, b1 else: #print x, y # Note: ok if |y| > 1, because # 2F1 implements analytic continuation if not ok(x): u1 = (x-y)/(x-1) if not ok(u1): raise ValueError("Analytic continuation not implemented") #print "Using analytic continuation" return (1-x)**(-b1)*(1-y)**(c-a-b2)*\ ctx.appellf1(c-a,b1,c-b1-b2,c,u1,y,**kwargs) return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m+n':[c]}, x,y, **kwargs) @defun def appellf2(ctx,a,b1,b2,c1,c2,x,y,**kwargs): # TODO: continuation return ctx.hyper2d({'m+n':[a],'m':[b1],'n':[b2]}, {'m':[c1],'n':[c2]}, x,y, **kwargs) @defun def appellf3(ctx,a1,a2,b1,b2,c,x,y,**kwargs): outer_polynomial = ctx.isnpint(a1) or ctx.isnpint(b1) inner_polynomial = ctx.isnpint(a2) or ctx.isnpint(b2) if not outer_polynomial: if inner_polynomial or abs(x) > abs(y): x, y = y, x a1,a2,b1,b2 = a2,a1,b2,b1 return ctx.hyper2d({'m':[a1,b1],'n':[a2,b2]}, {'m+n':[c]},x,y,**kwargs) @defun def appellf4(ctx,a,b,c1,c2,x,y,**kwargs): # TODO: continuation return ctx.hyper2d({'m+n':[a,b]}, {'m':[c1],'n':[c2]},x,y,**kwargs) @defun def hyper2d(ctx, a, b, x, y, **kwargs): r""" Sums the generalized 2D hypergeometric series .. math :: \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{P((a),m,n)}{Q((b),m,n)} \frac{x^m y^n} {m! n!} where `(a) = (a_1,\ldots,a_r)`, `(b) = (b_1,\ldots,b_s)` and where `P` and `Q` are products of rising factorials such as `(a_j)_n` or `(a_j)_{m+n}`. `P` and `Q` are specified in the form of dicts, with the `m` and `n` dependence as keys and parameter lists as values. The supported rising factorials are given in the following table (note that only a few are supported in `Q`): +------------+-------------------+--------+ | Key | Rising factorial | `Q` | +============+===================+========+ | ``'m'`` | `(a_j)_m` | Yes | +------------+-------------------+--------+ | ``'n'`` | `(a_j)_n` | Yes | +------------+-------------------+--------+ | ``'m+n'`` | `(a_j)_{m+n}` | Yes | +------------+-------------------+--------+ | ``'m-n'`` | `(a_j)_{m-n}` | No | +------------+-------------------+--------+ | ``'n-m'`` | `(a_j)_{n-m}` | No | +------------+-------------------+--------+ | ``'2m+n'`` | `(a_j)_{2m+n}` | No | +------------+-------------------+--------+ | ``'2m-n'`` | `(a_j)_{2m-n}` | No | +------------+-------------------+--------+ | ``'2n-m'`` | `(a_j)_{2n-m}` | No | +------------+-------------------+--------+ For example, the Appell F1 and F4 functions .. math :: F_1 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b)_m (c)_n}{(d)_{m+n}} \frac{x^m y^n}{m! n!} F_4 = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b)_{m+n}}{(c)_m (d)_{n}} \frac{x^m y^n}{m! n!} can be represented respectively as ``hyper2d({'m+n':[a], 'm':[b], 'n':[c]}, {'m+n':[d]}, x, y)`` ``hyper2d({'m+n':[a,b]}, {'m':[c], 'n':[d]}, x, y)`` More generally, :func:`~mpmath.hyper2d` can evaluate any of the 34 distinct convergent second-order (generalized Gaussian) hypergeometric series enumerated by Horn, as well as the Kampe de Feriet function. The series is computed by rewriting it so that the inner series (i.e. the series containing `n` and `y`) has the form of an ordinary generalized hypergeometric series and thereby can be evaluated efficiently using :func:`~mpmath.hyper`. If possible, manually swapping `x` and `y` and the corresponding parameters can sometimes give better results. **Examples** Two separable cases: a product of two geometric series, and a product of two Gaussian hypergeometric functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> x, y = mpf(0.25), mpf(0.5) >>> hyper2d({'m':1,'n':1}, {}, x,y) 2.666666666666666666666667 >>> 1/(1-x)/(1-y) 2.666666666666666666666667 >>> hyper2d({'m':[1,2],'n':[3,4]}, {'m':[5],'n':[6]}, x,y) 4.164358531238938319669856 >>> hyp2f1(1,2,5,x)*hyp2f1(3,4,6,y) 4.164358531238938319669856 Some more series that can be done in closed form:: >>> hyper2d({'m':1,'n':1},{'m+n':1},x,y) 2.013417124712514809623881 >>> (exp(x)*x-exp(y)*y)/(x-y) 2.013417124712514809623881 Six of the 34 Horn functions, G1-G3 and H1-H3:: >>> from mpmath import * >>> mp.dps = 10; mp.pretty = True >>> x, y = 0.0625, 0.125 >>> a1,a2,b1,b2,c1,c2,d = 1.1,-1.2,-1.3,-1.4,1.5,-1.6,1.7 >>> hyper2d({'m+n':a1,'n-m':b1,'m-n':b2},{},x,y) # G1 1.139090746 >>> nsum(lambda m,n: rf(a1,m+n)*rf(b1,n-m)*rf(b2,m-n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 1.139090746 >>> hyper2d({'m':a1,'n':a2,'n-m':b1,'m-n':b2},{},x,y) # G2 0.9503682696 >>> nsum(lambda m,n: rf(a1,m)*rf(a2,n)*rf(b1,n-m)*rf(b2,m-n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 0.9503682696 >>> hyper2d({'2n-m':a1,'2m-n':a2},{},x,y) # G3 1.029372029 >>> nsum(lambda m,n: rf(a1,2*n-m)*rf(a2,2*m-n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 1.029372029 >>> hyper2d({'m-n':a1,'m+n':b1,'n':c1},{'m':d},x,y) # H1 -1.605331256 >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m+n)*rf(c1,n)/rf(d,m)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) -1.605331256 >>> hyper2d({'m-n':a1,'m':b1,'n':[c1,c2]},{'m':d},x,y) # H2 -2.35405404 >>> nsum(lambda m,n: rf(a1,m-n)*rf(b1,m)*rf(c1,n)*rf(c2,n)/rf(d,m)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) -2.35405404 >>> hyper2d({'2m+n':a1,'n':b1},{'m+n':c1},x,y) # H3 0.974479074 >>> nsum(lambda m,n: rf(a1,2*m+n)*rf(b1,n)/rf(c1,m+n)*\ ... x**m*y**n/fac(m)/fac(n), [0,inf], [0,inf]) 0.974479074 **References** 1. [SrivastavaKarlsson]_ 2. [Weisstein]_ http://mathworld.wolfram.com/HornFunction.html 3. [Weisstein]_ http://mathworld.wolfram.com/AppellHypergeometricFunction.html """ x = ctx.convert(x) y = ctx.convert(y) def parse(dct, key): args = dct.pop(key, []) try: args = list(args) except TypeError: args = [args] return [ctx.convert(arg) for arg in args] a_s = dict(a) b_s = dict(b) a_m = parse(a, 'm') a_n = parse(a, 'n') a_m_add_n = parse(a, 'm+n') a_m_sub_n = parse(a, 'm-n') a_n_sub_m = parse(a, 'n-m') a_2m_add_n = parse(a, '2m+n') a_2m_sub_n = parse(a, '2m-n') a_2n_sub_m = parse(a, '2n-m') b_m = parse(b, 'm') b_n = parse(b, 'n') b_m_add_n = parse(b, 'm+n') if a: raise ValueError("unsupported key: %r" % a.keys()[0]) if b: raise ValueError("unsupported key: %r" % b.keys()[0]) s = 0 outer = ctx.one m = ctx.mpf(0) ok_count = 0 prec = ctx.prec maxterms = kwargs.get('maxterms', 20*prec) try: ctx.prec += 10 tol = +ctx.eps while 1: inner_sign = 1 outer_sign = 1 inner_a = list(a_n) inner_b = list(b_n) outer_a = [a+m for a in a_m] outer_b = [b+m for b in b_m] # (a)_{m+n} = (a)_m (a+m)_n for a in a_m_add_n: a = a+m inner_a.append(a) outer_a.append(a) # (b)_{m+n} = (b)_m (b+m)_n for b in b_m_add_n: b = b+m inner_b.append(b) outer_b.append(b) # (a)_{n-m} = (a-m)_n / (a-m)_m for a in a_n_sub_m: inner_a.append(a-m) outer_b.append(a-m-1) # (a)_{m-n} = (-1)^(m+n) (1-a-m)_m / (1-a-m)_n for a in a_m_sub_n: inner_sign *= (-1) outer_sign *= (-1)**(m) inner_b.append(1-a-m) outer_a.append(-a-m) # (a)_{2m+n} = (a)_{2m} (a+2m)_n for a in a_2m_add_n: inner_a.append(a+2*m) outer_a.append((a+2*m)*(1+a+2*m)) # (a)_{2m-n} = (-1)^(2m+n) (1-a-2m)_{2m} / (1-a-2m)_n for a in a_2m_sub_n: inner_sign *= (-1) inner_b.append(1-a-2*m) outer_a.append((a+2*m)*(1+a+2*m)) # (a)_{2n-m} = 4^n ((a-m)/2)_n ((a-m+1)/2)_n / (a-m)_m for a in a_2n_sub_m: inner_sign *= 4 inner_a.append(0.5*(a-m)) inner_a.append(0.5*(a-m+1)) outer_b.append(a-m-1) inner = ctx.hyper(inner_a, inner_b, inner_sign*y, zeroprec=ctx.prec, **kwargs) term = outer * inner * outer_sign if abs(term) < tol: ok_count += 1 else: ok_count = 0 if ok_count >= 3 or not outer: break s += term for a in outer_a: outer *= a for b in outer_b: outer /= b m += 1 outer = outer * x / m if m > maxterms: raise ctx.NoConvergence("maxterms exceeded in hyper2d") finally: ctx.prec = prec return +s """ @defun def kampe_de_feriet(ctx,a,b,c,d,e,f,x,y,**kwargs): return ctx.hyper2d({'m+n':a,'m':b,'n':c}, {'m+n':d,'m':e,'n':f}, x,y, **kwargs) """ @defun def bihyper(ctx, a_s, b_s, z, **kwargs): r""" Evaluates the bilateral hypergeometric series .. math :: \,_AH_B(a_1, \ldots, a_k; b_1, \ldots, b_B; z) = \sum_{n=-\infty}^{\infty} \frac{(a_1)_n \ldots (a_A)_n} {(b_1)_n \ldots (b_B)_n} \, z^n where, for direct convergence, `A = B` and `|z| = 1`, although a regularized sum exists more generally by considering the bilateral series as a sum of two ordinary hypergeometric functions. In order for the series to make sense, none of the parameters may be integers. **Examples** The value of `\,_2H_2` at `z = 1` is given by Dougall's formula:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a,b,c,d = 0.5, 1.5, 2.25, 3.25 >>> bihyper([a,b],[c,d],1) -14.49118026212345786148847 >>> gammaprod([c,d,1-a,1-b,c+d-a-b-1],[c-a,d-a,c-b,d-b]) -14.49118026212345786148847 The regularized function `\,_1H_0` can be expressed as the sum of one `\,_2F_0` function and one `\,_1F_1` function:: >>> a = mpf(0.25) >>> z = mpf(0.75) >>> bihyper([a], [], z) (0.2454393389657273841385582 + 0.2454393389657273841385582j) >>> hyper([a,1],[],z) + (hyper([1],[1-a],-1/z)-1) (0.2454393389657273841385582 + 0.2454393389657273841385582j) >>> hyper([a,1],[],z) + hyper([1],[2-a],-1/z)/z/(a-1) (0.2454393389657273841385582 + 0.2454393389657273841385582j) **References** 1. [Slater]_ (chapter 6: "Bilateral Series", pp. 180-189) 2. [Wikipedia]_ http://en.wikipedia.org/wiki/Bilateral_hypergeometric_series """ z = ctx.convert(z) c_s = a_s + b_s p = len(a_s) q = len(b_s) if (p, q) == (0,0) or (p, q) == (1,1): return ctx.zero * z neg = (p-q) % 2 def h(*c_s): a_s = list(c_s[:p]) b_s = list(c_s[p:]) aa_s = [2-b for b in b_s] bb_s = [2-a for a in a_s] rp = [(-1)**neg * z] + [1-b for b in b_s] + [1-a for a in a_s] rc = [-1] + [1]*len(b_s) + [-1]*len(a_s) T1 = [], [], [], [], a_s + [1], b_s, z T2 = rp, rc, [], [], aa_s + [1], bb_s, (-1)**neg / z return T1, T2 return ctx.hypercomb(h, c_s, **kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/bessel.py0000644000175000017500000011134012014170666026224 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun def j0(ctx, x): """Computes the Bessel function `J_0(x)`. See :func:`~mpmath.besselj`.""" return ctx.besselj(0, x) @defun def j1(ctx, x): """Computes the Bessel function `J_1(x)`. See :func:`~mpmath.besselj`.""" return ctx.besselj(1, x) @defun def besselj(ctx, n, z, derivative=0, **kwargs): if type(n) is int: n_isint = True else: n = ctx.convert(n) n_isint = ctx.isint(n) if n_isint: n = int(ctx._re(n)) if n_isint and n < 0: return (-1)**n * ctx.besselj(-n, z, derivative, **kwargs) z = ctx.convert(z) M = ctx.mag(z) if derivative: d = ctx.convert(derivative) # TODO: the integer special-casing shouldn't be necessary. # However, the hypergeometric series gets inaccurate for large d # because of inaccurate pole cancellation at a pole far from # zero (needs to be fixed in hypercomb or hypsum) if ctx.isint(d) and d >= 0: d = int(d) orig = ctx.prec try: ctx.prec += 15 v = ctx.fsum((-1)**k * ctx.binomial(d,k) * ctx.besselj(2*k+n-d,z) for k in range(d+1)) finally: ctx.prec = orig v *= ctx.mpf(2)**(-d) else: def h(n,d): r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), -0.25, exact=True) B = [0.5*(n-d+1), 0.5*(n-d+2)] T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[],B,[(n+1)*0.5,(n+2)*0.5],B+[n+1],r)] return T v = ctx.hypercomb(h, [n,d], **kwargs) else: # Fast case: J_n(x), n int, appropriate magnitude for fixed-point calculation if (not derivative) and n_isint and abs(M) < 10 and abs(n) < 20: try: return ctx._besselj(n, z) except NotImplementedError: pass if not z: if not n: v = ctx.one + n+z elif ctx.re(n) > 0: v = n*z else: v = ctx.inf + z + n else: #v = 0 orig = ctx.prec try: # XXX: workaround for accuracy in low level hypergeometric series # when alternating, large arguments ctx.prec += min(3*abs(M), ctx.prec) w = ctx.fmul(z, 0.5, exact=True) def h(n): r = ctx.fneg(ctx.fmul(w, w, prec=max(0,ctx.prec+M)), exact=True) return [([w], [n], [], [n+1], [], [n+1], r)] v = ctx.hypercomb(h, [n], **kwargs) finally: ctx.prec = orig v = +v return v @defun def besseli(ctx, n, z, derivative=0, **kwargs): n = ctx.convert(n) z = ctx.convert(z) if not z: if derivative: raise ValueError if not n: # I(0,0) = 1 return 1+n+z if ctx.isint(n): return 0*(n+z) r = ctx.re(n) if r == 0: return ctx.nan*(n+z) elif r > 0: return 0*(n+z) else: return ctx.inf+(n+z) M = ctx.mag(z) if derivative: d = ctx.convert(derivative) def h(n,d): r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), 0.25, exact=True) B = [0.5*(n-d+1), 0.5*(n-d+2), n+1] T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[n+1],B,[(n+1)*0.5,(n+2)*0.5],B,r)] return T v = ctx.hypercomb(h, [n,d], **kwargs) else: def h(n): w = ctx.fmul(z, 0.5, exact=True) r = ctx.fmul(w, w, prec=max(0,ctx.prec+M)) return [([w], [n], [], [n+1], [], [n+1], r)] v = ctx.hypercomb(h, [n], **kwargs) return v @defun_wrapped def bessely(ctx, n, z, derivative=0, **kwargs): if not z: if derivative: # Not implemented raise ValueError if not n: # ~ log(z/2) return -ctx.inf + (n+z) if ctx.im(n): return nan * (n+z) r = ctx.re(n) q = n+0.5 if ctx.isint(q): if n > 0: return -ctx.inf + (n+z) else: return 0 * (n+z) if r < 0 and int(ctx.floor(q)) % 2: return ctx.inf + (n+z) else: return ctx.ninf + (n+z) # XXX: use hypercomb ctx.prec += 10 m, d = ctx.nint_distance(n) if d < -ctx.prec: h = +ctx.eps ctx.prec *= 2 n += h elif d < 0: ctx.prec -= d # TODO: avoid cancellation for imaginary arguments cos, sin = ctx.cospi_sinpi(n) return (ctx.besselj(n,z,derivative,**kwargs)*cos - \ ctx.besselj(-n,z,derivative,**kwargs))/sin @defun_wrapped def besselk(ctx, n, z, **kwargs): if not z: return ctx.inf M = ctx.mag(z) if M < 1: # Represent as limit definition def h(n): r = (z/2)**2 T1 = [z, 2], [-n, n-1], [n], [], [], [1-n], r T2 = [z, 2], [n, -n-1], [-n], [], [], [1+n], r return T1, T2 # We could use the limit definition always, but it leads # to very bad cancellation (of exponentially large terms) # for large real z # Instead represent in terms of 2F0 else: ctx.prec += M def h(n): return [([ctx.pi/2, z, ctx.exp(-z)], [0.5,-0.5,1], [], [], \ [n+0.5, 0.5-n], [], -1/(2*z))] return ctx.hypercomb(h, [n], **kwargs) @defun_wrapped def hankel1(ctx,n,x,**kwargs): return ctx.besselj(n,x,**kwargs) + ctx.j*ctx.bessely(n,x,**kwargs) @defun_wrapped def hankel2(ctx,n,x,**kwargs): return ctx.besselj(n,x,**kwargs) - ctx.j*ctx.bessely(n,x,**kwargs) @defun_wrapped def whitm(ctx,k,m,z,**kwargs): if z == 0: # M(k,m,z) = 0^(1/2+m) if ctx.re(m) > -0.5: return z elif ctx.re(m) < -0.5: return ctx.inf + z else: return ctx.nan * z x = ctx.fmul(-0.5, z, exact=True) y = 0.5+m return ctx.exp(x) * z**y * ctx.hyp1f1(y-k, 1+2*m, z, **kwargs) @defun_wrapped def whitw(ctx,k,m,z,**kwargs): if z == 0: g = abs(ctx.re(m)) if g < 0.5: return z elif g > 0.5: return ctx.inf + z else: return ctx.nan * z x = ctx.fmul(-0.5, z, exact=True) y = 0.5+m return ctx.exp(x) * z**y * ctx.hyperu(y-k, 1+2*m, z, **kwargs) @defun def hyperu(ctx, a, b, z, **kwargs): a, atype = ctx._convert_param(a) b, btype = ctx._convert_param(b) z = ctx.convert(z) if not z: if ctx.re(b) <= 1: return ctx.gammaprod([1-b],[a-b+1]) else: return ctx.inf + z bb = 1+a-b bb, bbtype = ctx._convert_param(bb) try: orig = ctx.prec try: ctx.prec += 10 v = ctx.hypsum(2, 0, (atype, bbtype), [a, bb], -1/z, maxterms=ctx.prec) return v / z**a finally: ctx.prec = orig except ctx.NoConvergence: pass def h(a,b): w = ctx.sinpi(b) T1 = ([ctx.pi,w],[1,-1],[],[a-b+1,b],[a],[b],z) T2 = ([-ctx.pi,w,z],[1,-1,1-b],[],[a,2-b],[a-b+1],[2-b],z) return T1, T2 return ctx.hypercomb(h, [a,b], **kwargs) @defun def struveh(ctx,n,z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/StruveH/26/01/02/ def h(n): return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], -(z/2)**2)] return ctx.hypercomb(h, [n], **kwargs) @defun def struvel(ctx,n,z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/StruveL/26/01/02/ def h(n): return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], (z/2)**2)] return ctx.hypercomb(h, [n], **kwargs) def _anger(ctx,which,v,z,**kwargs): v = ctx._convert_param(v)[0] z = ctx.convert(z) def h(v): b = ctx.mpq_1_2 u = v*b m = b*3 a1,a2,b1,b2 = m-u, m+u, 1-u, 1+u c, s = ctx.cospi_sinpi(u) if which == 0: A, B = [b*z, s], [c] if which == 1: A, B = [b*z, -c], [s] w = ctx.square_exp_arg(z, mult=-0.25) T1 = A, [1, 1], [], [a1,a2], [1], [a1,a2], w T2 = B, [1], [], [b1,b2], [1], [b1,b2], w return T1, T2 return ctx.hypercomb(h, [v], **kwargs) @defun def angerj(ctx, v, z, **kwargs): return _anger(ctx, 0, v, z, **kwargs) @defun def webere(ctx, v, z, **kwargs): return _anger(ctx, 1, v, z, **kwargs) @defun def lommels1(ctx, u, v, z, **kwargs): u = ctx._convert_param(u)[0] v = ctx._convert_param(v)[0] z = ctx.convert(z) def h(u,v): b = ctx.mpq_1_2 w = ctx.square_exp_arg(z, mult=-0.25) return ([u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], \ [b*(u-v+3),b*(u+v+3)], w), return ctx.hypercomb(h, [u,v], **kwargs) @defun def lommels2(ctx, u, v, z, **kwargs): u = ctx._convert_param(u)[0] v = ctx._convert_param(v)[0] z = ctx.convert(z) # Asymptotic expansion (GR p. 947) -- need to be careful # not to use for small arguments # def h(u,v): # b = ctx.mpq_1_2 # w = -(z/2)**(-2) # return ([z], [u-1], [], [], [b*(1-u+v)], [b*(1-u-v)], w), def h(u,v): b = ctx.mpq_1_2 w = ctx.square_exp_arg(z, mult=-0.25) T1 = [u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], [b*(u-v+3),b*(u+v+3)], w T2 = [2, z], [u+v-1, -v], [v, b*(u+v+1)], [b*(v-u+1)], [], [1-v], w T3 = [2, z], [u-v-1, v], [-v, b*(u-v+1)], [b*(1-u-v)], [], [1+v], w #c1 = ctx.cospi((u-v)*b) #c2 = ctx.cospi((u+v)*b) #s = ctx.sinpi(v) #r1 = (u-v+1)*b #r2 = (u+v+1)*b #T2 = [c1, s, z, 2], [1, -1, -v, v], [], [-v+1], [], [-v+1], w #T3 = [-c2, s, z, 2], [1, -1, v, -v], [], [v+1], [], [v+1], w #T2 = [c1, s, z, 2], [1, -1, -v, v+u-1], [r1, r2], [-v+1], [], [-v+1], w #T3 = [-c2, s, z, 2], [1, -1, v, -v+u-1], [r1, r2], [v+1], [], [v+1], w return T1, T2, T3 return ctx.hypercomb(h, [u,v], **kwargs) @defun def ber(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBer2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos, sin = ctx.cospi_sinpi(-0.75*n) T1 = [cos, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r T2 = [sin, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r return T1, T2 return ctx.hypercomb(h, [n], **kwargs) @defun def bei(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBei2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos, sin = ctx.cospi_sinpi(0.75*n) T1 = [cos, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r T2 = [sin, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r return T1, T2 return ctx.hypercomb(h, [n], **kwargs) @defun def ker(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKer2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos1, sin1 = ctx.cospi_sinpi(0.25*n) cos2, sin2 = ctx.cospi_sinpi(0.75*n) T1 = [2, z, 4*cos1], [-n-3, n, 1], [-n], [], [], [0.5, 0.5*(1+n), 0.5*(n+2)], r T2 = [2, z, -sin1], [-n-3, 2+n, 1], [-n-1], [], [], [1.5, 0.5*(3+n), 0.5*(n+2)], r T3 = [2, z, 4*cos2], [n-3, -n, 1], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r T4 = [2, z, -sin2], [n-3, 2-n, 1], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r return T1, T2, T3, T4 return ctx.hypercomb(h, [n], **kwargs) @defun def kei(ctx, n, z, **kwargs): n = ctx.convert(n) z = ctx.convert(z) # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKei2/26/01/02/0001/ def h(n): r = -(z/4)**4 cos1, sin1 = ctx.cospi_sinpi(0.75*n) cos2, sin2 = ctx.cospi_sinpi(0.25*n) T1 = [-cos1, 2, z], [1, n-3, 2-n], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r T2 = [-sin1, 2, z], [1, n-1, -n], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r T3 = [-sin2, 2, z], [1, -n-1, n], [-n], [], [], [0.5, 0.5*(n+1), 0.5*(n+2)], r T4 = [-cos2, 2, z], [1, -n-3, n+2], [-n-1], [], [], [1.5, 0.5*(n+3), 0.5*(n+2)], r return T1, T2, T3, T4 return ctx.hypercomb(h, [n], **kwargs) # TODO: do this more generically? def c_memo(f): name = f.__name__ def f_wrapped(ctx): cache = ctx._misc_const_cache prec = ctx.prec p,v = cache.get(name, (-1,0)) if p >= prec: return +v else: cache[name] = (prec, f(ctx)) return cache[name][1] return f_wrapped @c_memo def _airyai_C1(ctx): return 1 / (ctx.cbrt(9) * ctx.gamma(ctx.mpf(2)/3)) @c_memo def _airyai_C2(ctx): return -1 / (ctx.cbrt(3) * ctx.gamma(ctx.mpf(1)/3)) @c_memo def _airybi_C1(ctx): return 1 / (ctx.nthroot(3,6) * ctx.gamma(ctx.mpf(2)/3)) @c_memo def _airybi_C2(ctx): return ctx.nthroot(3,6) / ctx.gamma(ctx.mpf(1)/3) def _airybi_n2_inf(ctx): prec = ctx.prec try: v = ctx.power(3,'2/3')*ctx.gamma('2/3')/(2*ctx.pi) finally: ctx.prec = prec return +v # Derivatives at z = 0 # TODO: could be expressed more elegantly using triple factorials def _airyderiv_0(ctx, z, n, ntype, which): if ntype == 'Z': if n < 0: return z r = ctx.mpq_1_3 prec = ctx.prec try: ctx.prec += 10 v = ctx.gamma((n+1)*r) * ctx.power(3,n*r) / ctx.pi if which == 0: v *= ctx.sinpi(2*(n+1)*r) v /= ctx.power(3,'2/3') else: v *= abs(ctx.sinpi(2*(n+1)*r)) v /= ctx.power(3,'1/6') finally: ctx.prec = prec return +v + z else: # singular (does the limit exist?) raise NotImplementedError @defun def airyai(ctx, z, derivative=0, **kwargs): z = ctx.convert(z) if derivative: n, ntype = ctx._convert_param(derivative) else: n = 0 # Values at infinities if not ctx.isnormal(z) and z: if n and ntype == 'Z': if n == -1: if z == ctx.inf: return ctx.mpf(1)/3 + 1/z if z == ctx.ninf: return ctx.mpf(-2)/3 + 1/z if n < -1: if z == ctx.inf: return z if z == ctx.ninf: return (-1)**n * (-z) if (not n) and z == ctx.inf or z == ctx.ninf: return 1/z # TODO: limits raise ValueError("essential singularity of Ai(z)") # Account for exponential scaling if z: extraprec = max(0, int(1.5*ctx.mag(z))) else: extraprec = 0 if n: if n == 1: def h(): # http://functions.wolfram.com/03.07.06.0005.01 if ctx._re(z) > 4: ctx.prec += extraprec w = z**1.5; r = -0.75/w; u = -2*w/3 ctx.prec -= extraprec C = -ctx.exp(u)/(2*ctx.sqrt(ctx.pi))*ctx.nthroot(z,4) return ([C],[1],[],[],[(-1,6),(7,6)],[],r), # http://functions.wolfram.com/03.07.26.0001.01 else: ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airyai_C1(ctx) * 0.5 C2 = _airyai_C2(ctx) T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) else: if z == 0: return _airyderiv_0(ctx, z, n, ntype, 0) # http://functions.wolfram.com/03.05.20.0004.01 def h(n): ctx.prec += extraprec w = z**3/9 ctx.prec -= extraprec q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3 a1=q13; a2=1; b1=(1-n)*q13; b2=(2-n)*q13; b3=1-n*q13 T1 = [3, z], [n-q23, -n], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w a1=q23; b1=(2-n)*q13; b2=1-n*q13; b3=(4-n)*q13 T2 = [3, z, -z], [n-q43, -n, 1], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w return T1, T2 v = ctx.hypercomb(h, [n], **kwargs) if ctx._is_real_type(z) and ctx.isint(n): v = ctx._re(v) return v else: def h(): if ctx._re(z) > 4: # We could use 1F1, but it results in huge cancellation; # the following expansion is better. # TODO: asymptotic series for derivatives ctx.prec += extraprec w = z**1.5; r = -0.75/w; u = -2*w/3 ctx.prec -= extraprec C = ctx.exp(u)/(2*ctx.sqrt(ctx.pi)*ctx.nthroot(z,4)) return ([C],[1],[],[],[(1,6),(5,6)],[],r), else: ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airyai_C1(ctx) C2 = _airyai_C2(ctx) T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) @defun def airybi(ctx, z, derivative=0, **kwargs): z = ctx.convert(z) if derivative: n, ntype = ctx._convert_param(derivative) else: n = 0 # Values at infinities if not ctx.isnormal(z) and z: if n and ntype == 'Z': if z == ctx.inf: return z if z == ctx.ninf: if n == -1: return 1/z if n == -2: return _airybi_n2_inf(ctx) if n < -2: return (-1)**n * (-z) if not n: if z == ctx.inf: return z if z == ctx.ninf: return 1/z # TODO: limits raise ValueError("essential singularity of Bi(z)") if z: extraprec = max(0, int(1.5*ctx.mag(z))) else: extraprec = 0 if n: if n == 1: # http://functions.wolfram.com/03.08.26.0001.01 def h(): ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airybi_C1(ctx)*0.5 C2 = _airybi_C2(ctx) T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) else: if z == 0: return _airyderiv_0(ctx, z, n, ntype, 1) def h(n): ctx.prec += extraprec w = z**3/9 ctx.prec -= extraprec q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3 q16 = ctx.mpq_1_6 q56 = ctx.mpq_5_6 a1=q13; a2=1; b1=(1-n)*q13; b2=(2-n)*q13; b3=1-n*q13 T1 = [3, z], [n-q16, -n], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w a1=q23; b1=(2-n)*q13; b2=1-n*q13; b3=(4-n)*q13 T2 = [3, z], [n-q56, 1-n], [a1], [b1,b2,b3], \ [a1,a2], [b1,b2,b3], w return T1, T2 v = ctx.hypercomb(h, [n], **kwargs) if ctx._is_real_type(z) and ctx.isint(n): v = ctx._re(v) return v else: def h(): ctx.prec += extraprec w = z**3 / 9 ctx.prec -= extraprec C1 = _airybi_C1(ctx) C2 = _airybi_C2(ctx) T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w return T1, T2 return ctx.hypercomb(h, [], **kwargs) def _airy_zero(ctx, which, k, derivative, complex=False): # Asymptotic formulas are given in DLMF section 9.9 def U(t): return t**(2/3.)*(1-7/(t**2*48)) def T(t): return t**(2/3.)*(1+5/(t**2*48)) k = int(k) assert k >= 1 assert derivative in (0,1) if which == 0: if derivative: return ctx.findroot(lambda z: ctx.airyai(z,1), -U(3*ctx.pi*(4*k-3)/8)) return ctx.findroot(ctx.airyai, -T(3*ctx.pi*(4*k-1)/8)) if which == 1 and complex == False: if derivative: return ctx.findroot(lambda z: ctx.airybi(z,1), -U(3*ctx.pi*(4*k-1)/8)) return ctx.findroot(ctx.airybi, -T(3*ctx.pi*(4*k-3)/8)) if which == 1 and complex == True: if derivative: t = 3*ctx.pi*(4*k-3)/8 + 0.75j*ctx.ln2 s = ctx.expjpi(ctx.mpf(1)/3) * T(t) return ctx.findroot(lambda z: ctx.airybi(z,1), s) t = 3*ctx.pi*(4*k-1)/8 + 0.75j*ctx.ln2 s = ctx.expjpi(ctx.mpf(1)/3) * U(t) return ctx.findroot(ctx.airybi, s) @defun def airyaizero(ctx, k, derivative=0): return _airy_zero(ctx, 0, k, derivative, False) @defun def airybizero(ctx, k, derivative=0, complex=False): return _airy_zero(ctx, 1, k, derivative, complex) def _scorer(ctx, z, which, kwargs): z = ctx.convert(z) if ctx.isinf(z): if z == ctx.inf: if which == 0: return 1/z if which == 1: return z if z == ctx.ninf: return 1/z raise ValueError("essential singularity") if z: extraprec = max(0, int(1.5*ctx.mag(z))) else: extraprec = 0 if kwargs.get('derivative'): raise NotImplementedError # Direct asymptotic expansions, to avoid # exponentially large cancellation try: if ctx.mag(z) > 3: if which == 0 and abs(ctx.arg(z)) < ctx.pi/3 * 0.999: def h(): return (([ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),) return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True) if which == 1 and abs(ctx.arg(-z)) < 2*ctx.pi/3 * 0.999: def h(): return (([-ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),) return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True) except ctx.NoConvergence: pass def h(): A = ctx.airybi(z, **kwargs)/3 B = -2*ctx.pi if which == 1: A *= 2 B *= -1 ctx.prec += extraprec w = z**3/9 ctx.prec -= extraprec T1 = [A], [1], [], [], [], [], 0 T2 = [B,z], [-1,2], [], [], [1], [ctx.mpq_4_3,ctx.mpq_5_3], w return T1, T2 return ctx.hypercomb(h, [], **kwargs) @defun def scorergi(ctx, z, **kwargs): return _scorer(ctx, z, 0, kwargs) @defun def scorerhi(ctx, z, **kwargs): return _scorer(ctx, z, 1, kwargs) @defun_wrapped def coulombc(ctx, l, eta, _cache={}): if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: return +_cache[l,eta][1] G3 = ctx.loggamma(2*l+2) G1 = ctx.loggamma(1+l+ctx.j*eta) G2 = ctx.loggamma(1+l-ctx.j*eta) v = 2**l * ctx.exp((-ctx.pi*eta+G1+G2)/2 - G3) if not (ctx.im(l) or ctx.im(eta)): v = ctx.re(v) _cache[l,eta] = (ctx.prec, v) return v @defun_wrapped def coulombf(ctx, l, eta, z, w=1, chop=True, **kwargs): # Regular Coulomb wave function # Note: w can be either 1 or -1; the other may be better in some cases # TODO: check that chop=True chops when and only when it should #ctx.prec += 10 def h(l, eta): try: jw = ctx.j*w jwz = ctx.fmul(jw, z, exact=True) jwz2 = ctx.fmul(jwz, -2, exact=True) C = ctx.coulombc(l, eta) T1 = [C, z, ctx.exp(jwz)], [1, l+1, 1], [], [], [1+l+jw*eta], \ [2*l+2], jwz2 except ValueError: T1 = [0], [-1], [], [], [], [], 0 return (T1,) v = ctx.hypercomb(h, [l,eta], **kwargs) if chop and (not ctx.im(l)) and (not ctx.im(eta)) and (not ctx.im(z)) and \ (ctx.re(z) >= 0): v = ctx.re(v) return v @defun_wrapped def _coulomb_chi(ctx, l, eta, _cache={}): if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: return _cache[l,eta][1] def terms(): l2 = -l-1 jeta = ctx.j*eta return [ctx.loggamma(1+l+jeta) * (-0.5j), ctx.loggamma(1+l-jeta) * (0.5j), ctx.loggamma(1+l2+jeta) * (0.5j), ctx.loggamma(1+l2-jeta) * (-0.5j), -(l+0.5)*ctx.pi] v = ctx.sum_accurately(terms, 1) _cache[l,eta] = (ctx.prec, v) return v @defun_wrapped def coulombg(ctx, l, eta, z, w=1, chop=True, **kwargs): # Irregular Coulomb wave function # Note: w can be either 1 or -1; the other may be better in some cases # TODO: check that chop=True chops when and only when it should if not ctx._im(l): l = ctx._re(l) # XXX: for isint def h(l, eta): # Force perturbation for integers and half-integers if ctx.isint(l*2): T1 = [0], [-1], [], [], [], [], 0 return (T1,) l2 = -l-1 try: chi = ctx._coulomb_chi(l, eta) jw = ctx.j*w s = ctx.sin(chi); c = ctx.cos(chi) C1 = ctx.coulombc(l,eta) C2 = ctx.coulombc(l2,eta) u = ctx.exp(jw*z) x = -2*jw*z T1 = [s, C1, z, u, c], [-1, 1, l+1, 1, 1], [], [], \ [1+l+jw*eta], [2*l+2], x T2 = [-s, C2, z, u], [-1, 1, l2+1, 1], [], [], \ [1+l2+jw*eta], [2*l2+2], x return T1, T2 except ValueError: T1 = [0], [-1], [], [], [], [], 0 return (T1,) v = ctx.hypercomb(h, [l,eta], **kwargs) if chop and (not ctx._im(l)) and (not ctx._im(eta)) and (not ctx._im(z)) and \ (ctx._re(z) >= 0): v = ctx._re(v) return v def mcmahon(ctx,kind,prime,v,m): """ Computes an estimate for the location of the Bessel function zero j_{v,m}, y_{v,m}, j'_{v,m} or y'_{v,m} using McMahon's asymptotic expansion (Abramowitz & Stegun 9.5.12-13, DLMF 20.21(vi)). Returns (r,err) where r is the estimated location of the root and err is a positive number estimating the error of the asymptotic expansion. """ u = 4*v**2 if kind == 1 and not prime: b = (4*m+2*v-1)*ctx.pi/4 if kind == 2 and not prime: b = (4*m+2*v-3)*ctx.pi/4 if kind == 1 and prime: b = (4*m+2*v-3)*ctx.pi/4 if kind == 2 and prime: b = (4*m+2*v-1)*ctx.pi/4 if not prime: s1 = b s2 = -(u-1)/(8*b) s3 = -4*(u-1)*(7*u-31)/(3*(8*b)**3) s4 = -32*(u-1)*(83*u**2-982*u+3779)/(15*(8*b)**5) s5 = -64*(u-1)*(6949*u**3-153855*u**2+1585743*u-6277237)/(105*(8*b)**7) if prime: s1 = b s2 = -(u+3)/(8*b) s3 = -4*(7*u**2+82*u-9)/(3*(8*b)**3) s4 = -32*(83*u**3+2075*u**2-3039*u+3537)/(15*(8*b)**5) s5 = -64*(6949*u**4+296492*u**3-1248002*u**2+7414380*u-5853627)/(105*(8*b)**7) terms = [s1,s2,s3,s4,s5] s = s1 err = 0.0 for i in range(1,len(terms)): if abs(terms[i]) < abs(terms[i-1]): s += terms[i] else: err = abs(terms[i]) if i == len(terms)-1: err = abs(terms[-1]) return s, err def generalized_bisection(ctx,f,a,b,n): """ Given f known to have exactly n simple roots within [a,b], return a list of n intervals isolating the roots and having opposite signs at the endpoints. TODO: this can be optimized, e.g. by reusing evaluation points. """ assert n >= 1 N = n+1 points = [] signs = [] while 1: points = ctx.linspace(a,b,N) signs = [ctx.sign(f(x)) for x in points] ok_intervals = [(points[i],points[i+1]) for i in range(N-1) \ if signs[i]*signs[i+1] == -1] if len(ok_intervals) == n: return ok_intervals N = N*2 def find_in_interval(ctx, f, ab): return ctx.findroot(f, ab, solver='illinois', verify=False) def bessel_zero(ctx, kind, prime, v, m, isoltol=0.01, _interval_cache={}): prec = ctx.prec workprec = max(prec, ctx.mag(v), ctx.mag(m))+10 try: ctx.prec = workprec v = ctx.mpf(v) m = int(m) prime = int(prime) assert v >= 0 assert m >= 1 assert prime in (0,1) if kind == 1: if prime: f = lambda x: ctx.besselj(v,x,derivative=1) else: f = lambda x: ctx.besselj(v,x) if kind == 2: if prime: f = lambda x: ctx.bessely(v,x,derivative=1) else: f = lambda x: ctx.bessely(v,x) # The first root of J' is very close to 0 for small # orders, and this needs to be special-cased if kind == 1 and prime and m == 1: if v == 0: return ctx.zero if v <= 1: # TODO: use v <= j'_{v,1} < y_{v,1}? r = 2*ctx.sqrt(v*(1+v)/(v+2)) return find_in_interval(ctx, f, (r/10, 2*r)) if (kind,prime,v,m) in _interval_cache: return find_in_interval(ctx, f, _interval_cache[kind,prime,v,m]) r, err = mcmahon(ctx, kind, prime, v, m) if err < isoltol: return find_in_interval(ctx, f, (r-isoltol, r+isoltol)) # An x such that 0 < x < r_{v,1} if kind == 1 and not prime: low = 2.4 if kind == 1 and prime: low = 1.8 if kind == 2 and not prime: low = 0.8 if kind == 2 and prime: low = 2.0 n = m+1 while 1: r1, err = mcmahon(ctx, kind, prime, v, n) if err < isoltol: r2, err2 = mcmahon(ctx, kind, prime, v, n+1) intervals = generalized_bisection(ctx, f, low, 0.5*(r1+r2), n) for k, ab in enumerate(intervals): _interval_cache[kind,prime,v,k+1] = ab return find_in_interval(ctx, f, intervals[m-1]) else: n = n*2 finally: ctx.prec = prec @defun def besseljzero(ctx, v, m, derivative=0): r""" For a real order `\nu \ge 0` and a positive integer `m`, returns `j_{\nu,m}`, the `m`-th positive zero of the Bessel function of the first kind `J_{\nu}(z)` (see :func:`~mpmath.besselj`). Alternatively, with *derivative=1*, gives the first nonnegative simple zero `j'_{\nu,m}` of `J'_{\nu}(z)`. The indexing convention is that used by Abramowitz & Stegun and the DLMF. Note the special case `j'_{0,1} = 0`, while all other zeros are positive. In effect, only simple zeros are counted (all zeros of Bessel functions are simple except possibly `z = 0`) and `j_{\nu,m}` becomes a monotonic function of both `\nu` and `m`. The zeros are interlaced according to the inequalities .. math :: j'_{\nu,k} < j_{\nu,k} < j'_{\nu,k+1} j_{\nu,1} < j_{\nu+1,2} < j_{\nu,2} < j_{\nu+1,2} < j_{\nu,3} < \cdots **Examples** Initial zeros of the Bessel functions `J_0(z), J_1(z), J_2(z)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besseljzero(0,1); besseljzero(0,2); besseljzero(0,3) 2.404825557695772768621632 5.520078110286310649596604 8.653727912911012216954199 >>> besseljzero(1,1); besseljzero(1,2); besseljzero(1,3) 3.831705970207512315614436 7.01558666981561875353705 10.17346813506272207718571 >>> besseljzero(2,1); besseljzero(2,2); besseljzero(2,3) 5.135622301840682556301402 8.417244140399864857783614 11.61984117214905942709415 Initial zeros of `J'_0(z), J'_1(z), J'_2(z)`:: 0.0 3.831705970207512315614436 7.01558666981561875353705 >>> besseljzero(1,1,1); besseljzero(1,2,1); besseljzero(1,3,1) 1.84118378134065930264363 5.331442773525032636884016 8.536316366346285834358961 >>> besseljzero(2,1,1); besseljzero(2,2,1); besseljzero(2,3,1) 3.054236928227140322755932 6.706133194158459146634394 9.969467823087595793179143 Zeros with large index:: >>> besseljzero(0,100); besseljzero(0,1000); besseljzero(0,10000) 313.3742660775278447196902 3140.807295225078628895545 31415.14114171350798533666 >>> besseljzero(5,100); besseljzero(5,1000); besseljzero(5,10000) 321.1893195676003157339222 3148.657306813047523500494 31422.9947255486291798943 >>> besseljzero(0,100,1); besseljzero(0,1000,1); besseljzero(0,10000,1) 311.8018681873704508125112 3139.236339643802482833973 31413.57032947022399485808 Zeros of functions with large order:: >>> besseljzero(50,1) 57.11689916011917411936228 >>> besseljzero(50,2) 62.80769876483536093435393 >>> besseljzero(50,100) 388.6936600656058834640981 >>> besseljzero(50,1,1) 52.99764038731665010944037 >>> besseljzero(50,2,1) 60.02631933279942589882363 >>> besseljzero(50,100,1) 387.1083151608726181086283 Zeros of functions with fractional order:: >>> besseljzero(0.5,1); besseljzero(1.5,1); besseljzero(2.25,4) 3.141592653589793238462643 4.493409457909064175307881 15.15657692957458622921634 Both `J_{\nu}(z)` and `J'_{\nu}(z)` can be expressed as infinite products over their zeros:: >>> v,z = 2, mpf(1) >>> (z/2)**v/gamma(v+1) * \ ... nprod(lambda k: 1-(z/besseljzero(v,k))**2, [1,inf]) ... 0.1149034849319004804696469 >>> besselj(v,z) 0.1149034849319004804696469 >>> (z/2)**(v-1)/2/gamma(v) * \ ... nprod(lambda k: 1-(z/besseljzero(v,k,1))**2, [1,inf]) ... 0.2102436158811325550203884 >>> besselj(v,z,1) 0.2102436158811325550203884 """ return +bessel_zero(ctx, 1, derivative, v, m) @defun def besselyzero(ctx, v, m, derivative=0): r""" For a real order `\nu \ge 0` and a positive integer `m`, returns `y_{\nu,m}`, the `m`-th positive zero of the Bessel function of the second kind `Y_{\nu}(z)` (see :func:`~mpmath.bessely`). Alternatively, with *derivative=1*, gives the first positive zero `y'_{\nu,m}` of `Y'_{\nu}(z)`. The zeros are interlaced according to the inequalities .. math :: y_{\nu,k} < y'_{\nu,k} < y_{\nu,k+1} y_{\nu,1} < y_{\nu+1,2} < y_{\nu,2} < y_{\nu+1,2} < y_{\nu,3} < \cdots **Examples** Initial zeros of the Bessel functions `Y_0(z), Y_1(z), Y_2(z)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besselyzero(0,1); besselyzero(0,2); besselyzero(0,3) 0.8935769662791675215848871 3.957678419314857868375677 7.086051060301772697623625 >>> besselyzero(1,1); besselyzero(1,2); besselyzero(1,3) 2.197141326031017035149034 5.429681040794135132772005 8.596005868331168926429606 >>> besselyzero(2,1); besselyzero(2,2); besselyzero(2,3) 3.384241767149593472701426 6.793807513268267538291167 10.02347797936003797850539 Initial zeros of `Y'_0(z), Y'_1(z), Y'_2(z)`:: >>> besselyzero(0,1,1); besselyzero(0,2,1); besselyzero(0,3,1) 2.197141326031017035149034 5.429681040794135132772005 8.596005868331168926429606 >>> besselyzero(1,1,1); besselyzero(1,2,1); besselyzero(1,3,1) 3.683022856585177699898967 6.941499953654175655751944 10.12340465543661307978775 >>> besselyzero(2,1,1); besselyzero(2,2,1); besselyzero(2,3,1) 5.002582931446063945200176 8.350724701413079526349714 11.57419546521764654624265 Zeros with large index:: >>> besselyzero(0,100); besselyzero(0,1000); besselyzero(0,10000) 311.8034717601871549333419 3139.236498918198006794026 31413.57034538691205229188 >>> besselyzero(5,100); besselyzero(5,1000); besselyzero(5,10000) 319.6183338562782156235062 3147.086508524556404473186 31421.42392920214673402828 >>> besselyzero(0,100,1); besselyzero(0,1000,1); besselyzero(0,10000,1) 313.3726705426359345050449 3140.807136030340213610065 31415.14112579761578220175 Zeros of functions with large order:: >>> besselyzero(50,1) 53.50285882040036394680237 >>> besselyzero(50,2) 60.11244442774058114686022 >>> besselyzero(50,100) 387.1096509824943957706835 >>> besselyzero(50,1,1) 56.96290427516751320063605 >>> besselyzero(50,2,1) 62.74888166945933944036623 >>> besselyzero(50,100,1) 388.6923300548309258355475 Zeros of functions with fractional order:: >>> besselyzero(0.5,1); besselyzero(1.5,1); besselyzero(2.25,4) 1.570796326794896619231322 2.798386045783887136720249 13.56721208770735123376018 """ return +bessel_zero(ctx, 2, derivative, v, m) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/zetazeros.py0000644000175000017500000007322312014170666027004 0ustar georgeskgeorgesk""" The function zetazero(n) computes the n-th nontrivial zero of zeta(s). The general strategy is to locate a block of Gram intervals B where we know exactly the number of zeros contained and which of those zeros is that which we search. If n <= 400 000 000 we know exactly the Rosser exceptions, contained in a list in this file. Hence for n<=400 000 000 we simply look at these list of exceptions. If our zero is implicated in one of these exceptions we have our block B. In other case we simply locate the good Rosser block containing our zero. For n > 400 000 000 we apply the method of Turing, as complemented by Lehman, Brent and Trudgian to find a suitable B. """ from .functions import defun, defun_wrapped def find_rosser_block_zero(ctx, n): """for n<400 000 000 determines a block were one find our zero""" for k in range(len(_ROSSER_EXCEPTIONS)//2): a=_ROSSER_EXCEPTIONS[2*k][0] b=_ROSSER_EXCEPTIONS[2*k][1] if ((a<= n-2) and (n-1 <= b)): t0 = ctx.grampoint(a) t1 = ctx.grampoint(b) v0 = ctx._fp.siegelz(t0) v1 = ctx._fp.siegelz(t1) my_zero_number = n-a-1 zero_number_block = b-a pattern = _ROSSER_EXCEPTIONS[2*k+1] return (my_zero_number, [a,b], [t0,t1], [v0,v1]) k = n-2 t,v,b = compute_triple_tvb(ctx, k) T = [t] V = [v] while b < 0: k -= 1 t,v,b = compute_triple_tvb(ctx, k) T.insert(0,t) V.insert(0,v) my_zero_number = n-k-1 m = n-1 t,v,b = compute_triple_tvb(ctx, m) T.append(t) V.append(v) while b < 0: m += 1 t,v,b = compute_triple_tvb(ctx, m) T.append(t) V.append(v) return (my_zero_number, [k,m], T, V) def wpzeros(t): """Precision needed to compute higher zeros""" wp = 53 if t > 3*10**8: wp = 63 if t > 10**11: wp = 70 if t > 10**14: wp = 83 return wp def separate_zeros_in_block(ctx, zero_number_block, T, V, limitloop=None, fp_tolerance=None): """Separate the zeros contained in the block T, limitloop determines how long one must search""" if limitloop is None: limitloop = ctx.inf loopnumber = 0 variations = count_variations(V) while ((variations < zero_number_block) and (loopnumber 0): alpha = ctx.sqrt(u/v) b= (alpha*a+b2)/(alpha+1) else: b = (a+b2)/2 if fp_tolerance < 10: w = ctx._fp.siegelz(b) if abs(w)ITERATION_LIMIT)and(loopnumber>2)and(variations+2==zero_number_block): dtMax=0 dtSec=0 kMax = 0 for k1 in range(1,len(T)): dt = T[k1]-T[k1-1] if dt > dtMax: kMax=k1 dtSec = dtMax dtMax = dt elif (dtdtSec): dtSec = dt if dtMax>3*dtSec: f = lambda x: ctx.rs_z(x,derivative=1) t0=T[kMax-1] t1 = T[kMax] t=ctx.findroot(f, (t0,t1), solver ='illinois',verify=False, verbose=False) v = ctx.siegelz(t) if (t0400 000 000""" sb = sure_number_block(ctx, n) number_goodblocks = 0 m2 = n-1 t, v, b = compute_triple_tvb(ctx, m2) Tf = [t] Vf = [v] while b < 0: m2 += 1 t,v,b = compute_triple_tvb(ctx, m2) Tf.append(t) Vf.append(v) goodpoints = [m2] T = [t] V = [v] while number_goodblocks < 2*sb: m2 += 1 t, v, b = compute_triple_tvb(ctx, m2) T.append(t) V.append(v) while b < 0: m2 += 1 t,v,b = compute_triple_tvb(ctx, m2) T.append(t) V.append(v) goodpoints.append(m2) zn = len(T)-1 A, B, separated =\ separate_zeros_in_block(ctx, zn, T, V, limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) Tf.pop() Tf.extend(A) Vf.pop() Vf.extend(B) if separated: number_goodblocks += 1 else: number_goodblocks = 0 T = [t] V = [v] # Now the same procedure to the left number_goodblocks = 0 m2 = n-2 t, v, b = compute_triple_tvb(ctx, m2) Tf.insert(0,t) Vf.insert(0,v) while b < 0: m2 -= 1 t,v,b = compute_triple_tvb(ctx, m2) Tf.insert(0,t) Vf.insert(0,v) goodpoints.insert(0,m2) T = [t] V = [v] while number_goodblocks < 2*sb: m2 -= 1 t, v, b = compute_triple_tvb(ctx, m2) T.insert(0,t) V.insert(0,v) while b < 0: m2 -= 1 t,v,b = compute_triple_tvb(ctx, m2) T.insert(0,t) V.insert(0,v) goodpoints.insert(0,m2) zn = len(T)-1 A, B, separated =\ separate_zeros_in_block(ctx, zn, T, V, limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) A.pop() Tf = A+Tf B.pop() Vf = B+Vf if separated: number_goodblocks += 1 else: number_goodblocks = 0 T = [t] V = [v] r = goodpoints[2*sb] lg = len(goodpoints) s = goodpoints[lg-2*sb-1] tr, vr, br = compute_triple_tvb(ctx, r) ar = Tf.index(tr) ts, vs, bs = compute_triple_tvb(ctx, s) as1 = Tf.index(ts) T = Tf[ar:as1+1] V = Vf[ar:as1+1] zn = s-r A, B, separated =\ separate_zeros_in_block(ctx, zn,T,V,limitloop=ITERATION_LIMIT, fp_tolerance=fp_tolerance) if separated: return (n-r-1,[r,s],A,B) q = goodpoints[sb] lg = len(goodpoints) t = goodpoints[lg-sb-1] tq, vq, bq = compute_triple_tvb(ctx, q) aq = Tf.index(tq) tt, vt, bt = compute_triple_tvb(ctx, t) at = Tf.index(tt) T = Tf[aq:at+1] V = Vf[aq:at+1] return (n-q-1,[q,t],T,V) def count_variations(V): count = 0 vold = V[0] for n in range(1, len(V)): vnew = V[n] if vold*vnew < 0: count +=1 vold = vnew return count def pattern_construct(ctx, block, T, V): pattern = '(' a = block[0] b = block[1] t0,v0,b0 = compute_triple_tvb(ctx, a) k = 0 k0 = 0 for n in range(a+1,b+1): t1,v1,b1 = compute_triple_tvb(ctx, n) lgT =len(T) while (k < lgT) and (T[k] <= t1): k += 1 L = V[k0:k] L.append(v1) L.insert(0,v0) count = count_variations(L) pattern = pattern + ("%s" % count) if b1 > 0: pattern = pattern + ')(' k0 = k t0,v0,b0 = t1,v1,b1 pattern = pattern[:-1] return pattern @defun def zetazero(ctx, n, info=False, round=True): r""" Computes the `n`-th nontrivial zero of `\zeta(s)` on the critical line, i.e. returns an approximation of the `n`-th largest complex number `s = \frac{1}{2} + ti` for which `\zeta(s) = 0`. Equivalently, the imaginary part `t` is a zero of the Z-function (:func:`~mpmath.siegelz`). **Examples** The first few zeros:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> zetazero(1) (0.5 + 14.13472514173469379045725j) >>> zetazero(2) (0.5 + 21.02203963877155499262848j) >>> zetazero(20) (0.5 + 77.14484006887480537268266j) Verifying that the values are zeros:: >>> for n in range(1,5): ... s = zetazero(n) ... chop(zeta(s)), chop(siegelz(s.imag)) ... (0.0, 0.0) (0.0, 0.0) (0.0, 0.0) (0.0, 0.0) Negative indices give the conjugate zeros (`n = 0` is undefined):: >>> zetazero(-1) (0.5 - 14.13472514173469379045725j) :func:`~mpmath.zetazero` supports arbitrarily large `n` and arbitrary precision:: >>> mp.dps = 15 >>> zetazero(1234567) (0.5 + 727690.906948208j) >>> mp.dps = 50 >>> zetazero(1234567) (0.5 + 727690.9069482075392389420041147142092708393819935j) >>> chop(zeta(_)/_) 0.0 with *info=True*, :func:`~mpmath.zetazero` gives additional information:: >>> mp.dps = 15 >>> zetazero(542964976,info=True) ((0.5 + 209039046.578535j), [542964969, 542964978], 6, '(013111110)') This means that the zero is between Gram points 542964969 and 542964978; it is the 6-th zero between them. Finally (01311110) is the pattern of zeros in this interval. The numbers indicate the number of zeros in each Gram interval (Rosser blocks between parenthesis). In this case there is only one Rosser block of length nine. """ n = int(n) if n < 0: return ctx.zetazero(-n).conjugate() if n == 0: raise ValueError("n must be nonzero") wpinitial = ctx.prec try: wpz, fp_tolerance = comp_fp_tolerance(ctx, n) ctx.prec = wpz if n < 400000000: my_zero_number, block, T, V =\ find_rosser_block_zero(ctx, n) else: my_zero_number, block, T, V =\ search_supergood_block(ctx, n, fp_tolerance) zero_number_block = block[1]-block[0] T, V, separated = separate_zeros_in_block(ctx, zero_number_block, T, V, limitloop=ctx.inf, fp_tolerance=fp_tolerance) if info: pattern = pattern_construct(ctx,block,T,V) prec = max(wpinitial, wpz) t = separate_my_zero(ctx, my_zero_number, zero_number_block,T,V,prec) v = ctx.mpc(0.5,t) finally: ctx.prec = wpinitial if round: v =+v if info: return (v,block,my_zero_number,pattern) else: return v def gram_index(ctx, t): if t > 10**13: wp = 3*ctx.log(t, 10) else: wp = 0 prec = ctx.prec try: ctx.prec += wp x0 = (t/(2*ctx.pi))*ctx.log(t/(2*ctx.pi)) h = ctx.findroot(lambda x:ctx.siegeltheta(t)-ctx.pi*x, x0) h = int(h) finally: ctx.prec = prec return(h) def count_to(ctx, t, T, V): count = 0 vold = V[0] told = T[0] tnew = T[1] k = 1 while tnew < t: vnew = V[k] if vold*vnew < 0: count += 1 vold = vnew k += 1 tnew = T[k] a = ctx.siegelz(t) if a*vold < 0: count += 1 return count def comp_fp_tolerance(ctx, n): wpz = wpzeros(n*ctx.log(n)) if n < 15*10**8: fp_tolerance = 0.0005 elif n <= 10**14: fp_tolerance = 0.1 else: fp_tolerance = 100 return wpz, fp_tolerance @defun def nzeros(ctx, t): r""" Computes the number of zeros of the Riemann zeta function in `(0,1) \times (0,t]`, usually denoted by `N(t)`. **Examples** The first zero has imaginary part between 14 and 15:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nzeros(14) 0 >>> nzeros(15) 1 >>> zetazero(1) (0.5 + 14.1347251417347j) Some closely spaced zeros:: >>> nzeros(10**7) 21136125 >>> zetazero(21136125) (0.5 + 9999999.32718175j) >>> zetazero(21136126) (0.5 + 10000000.2400236j) >>> nzeros(545439823.215) 1500000001 >>> zetazero(1500000001) (0.5 + 545439823.201985j) >>> zetazero(1500000002) (0.5 + 545439823.325697j) This confirms the data given by J. van de Lune, H. J. J. te Riele and D. T. Winter in 1986. """ if t < 14.1347251417347: return 0 x = gram_index(ctx, t) k = int(ctx.floor(x)) wpinitial = ctx.prec wpz, fp_tolerance = comp_fp_tolerance(ctx, k) ctx.prec = wpz a = ctx.siegelz(t) if k == -1 and a < 0: return 0 elif k == -1 and a > 0: return 1 if k+2 < 400000000: Rblock = find_rosser_block_zero(ctx, k+2) else: Rblock = search_supergood_block(ctx, k+2, fp_tolerance) n1, n2 = Rblock[1] if n2-n1 == 1: b = Rblock[3][0] if a*b > 0: ctx.prec = wpinitial return k+1 else: ctx.prec = wpinitial return k+2 my_zero_number,block, T, V = Rblock zero_number_block = n2-n1 T, V, separated = separate_zeros_in_block(ctx,\ zero_number_block, T, V,\ limitloop=ctx.inf,\ fp_tolerance=fp_tolerance) n = count_to(ctx, t, T, V) ctx.prec = wpinitial return n+n1+1 @defun_wrapped def backlunds(ctx, t): r""" Computes the function `S(t) = \operatorname{arg} \zeta(\frac{1}{2} + it) / \pi`. See Titchmarsh Section 9.3 for details of the definition. **Examples** >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> backlunds(217.3) 0.16302205431184 Generally, the value is a small number. At Gram points it is an integer, frequently equal to 0:: >>> chop(backlunds(grampoint(200))) 0.0 >>> backlunds(extraprec(10)(grampoint)(211)) 1.0 >>> backlunds(extraprec(10)(grampoint)(232)) -1.0 The number of zeros of the Riemann zeta function up to height `t` satisfies `N(t) = \theta(t)/\pi + 1 + S(t)` (see :func:nzeros` and :func:`siegeltheta`):: >>> t = 1234.55 >>> nzeros(t) 842 >>> siegeltheta(t)/pi+1+backlunds(t) 842.0 """ return ctx.nzeros(t)-1-ctx.siegeltheta(t)/ctx.pi """ _ROSSER_EXCEPTIONS is a list of all exceptions to Rosser's rule for n <= 400 000 000. Alternately the entry is of type [n,m], or a string. The string is the zero pattern of the Block and the relevant adjacent. For example (010)3 corresponds to a block composed of three Gram intervals, the first ant third without a zero and the intermediate with a zero. The next Gram interval contain three zeros. So that in total we have 4 zeros in 4 Gram blocks. n and m are the indices of the Gram points of this interval of four Gram intervals. The Rosser exception is therefore formed by the three Gram intervals that are signaled between parenthesis. We have included also some Rosser's exceptions beyond n=400 000 000 that are noted in the literature by some reason. The list is composed from the data published in the references: R. P. Brent, J. van de Lune, H. J. J. te Riele, D. T. Winter, 'On the Zeros of the Riemann Zeta Function in the Critical Strip. II', Math. Comp. 39 (1982) 681--688. See also Corrigenda in Math. Comp. 46 (1986) 771. J. van de Lune, H. J. J. te Riele, 'On the Zeros of the Riemann Zeta Function in the Critical Strip. III', Math. Comp. 41 (1983) 759--767. See also Corrigenda in Math. Comp. 46 (1986) 771. J. van de Lune, 'Sums of Equal Powers of Positive Integers', Dissertation, Vrije Universiteit te Amsterdam, Centrum voor Wiskunde en Informatica, Amsterdam, 1984. Thanks to the authors all this papers and those others that have contributed to make this possible. """ _ROSSER_EXCEPTIONS = \ [[13999525, 13999528], '(00)3', [30783329, 30783332], '(00)3', [30930926, 30930929], '3(00)', [37592215, 37592218], '(00)3', [40870156, 40870159], '(00)3', [43628107, 43628110], '(00)3', [46082042, 46082045], '(00)3', [46875667, 46875670], '(00)3', [49624540, 49624543], '3(00)', [50799238, 50799241], '(00)3', [55221453, 55221456], '3(00)', [56948779, 56948782], '3(00)', [60515663, 60515666], '(00)3', [61331766, 61331770], '(00)40', [69784843, 69784846], '3(00)', [75052114, 75052117], '(00)3', [79545240, 79545243], '3(00)', [79652247, 79652250], '3(00)', [83088043, 83088046], '(00)3', [83689522, 83689525], '3(00)', [85348958, 85348961], '(00)3', [86513820, 86513823], '(00)3', [87947596, 87947599], '3(00)', [88600095, 88600098], '(00)3', [93681183, 93681186], '(00)3', [100316551, 100316554], '3(00)', [100788444, 100788447], '(00)3', [106236172, 106236175], '(00)3', [106941327, 106941330], '3(00)', [107287955, 107287958], '(00)3', [107532016, 107532019], '3(00)', [110571044, 110571047], '(00)3', [111885253, 111885256], '3(00)', [113239783, 113239786], '(00)3', [120159903, 120159906], '(00)3', [121424391, 121424394], '3(00)', [121692931, 121692934], '3(00)', [121934170, 121934173], '3(00)', [122612848, 122612851], '3(00)', [126116567, 126116570], '(00)3', [127936513, 127936516], '(00)3', [128710277, 128710280], '3(00)', [129398902, 129398905], '3(00)', [130461096, 130461099], '3(00)', [131331947, 131331950], '3(00)', [137334071, 137334074], '3(00)', [137832603, 137832606], '(00)3', [138799471, 138799474], '3(00)', [139027791, 139027794], '(00)3', [141617806, 141617809], '(00)3', [144454931, 144454934], '(00)3', [145402379, 145402382], '3(00)', [146130245, 146130248], '3(00)', [147059770, 147059773], '(00)3', [147896099, 147896102], '3(00)', [151097113, 151097116], '(00)3', [152539438, 152539441], '(00)3', [152863168, 152863171], '3(00)', [153522726, 153522729], '3(00)', [155171524, 155171527], '3(00)', [155366607, 155366610], '(00)3', [157260686, 157260689], '3(00)', [157269224, 157269227], '(00)3', [157755123, 157755126], '(00)3', [158298484, 158298487], '3(00)', [160369050, 160369053], '3(00)', [162962787, 162962790], '(00)3', [163724709, 163724712], '(00)3', [164198113, 164198116], '3(00)', [164689301, 164689305], '(00)40', [164880228, 164880231], '3(00)', [166201932, 166201935], '(00)3', [168573836, 168573839], '(00)3', [169750763, 169750766], '(00)3', [170375507, 170375510], '(00)3', [170704879, 170704882], '3(00)', [172000992, 172000995], '3(00)', [173289941, 173289944], '(00)3', [173737613, 173737616], '3(00)', [174102513, 174102516], '(00)3', [174284990, 174284993], '(00)3', [174500513, 174500516], '(00)3', [175710609, 175710612], '(00)3', [176870843, 176870846], '3(00)', [177332732, 177332735], '3(00)', [177902861, 177902864], '3(00)', [179979095, 179979098], '(00)3', [181233726, 181233729], '3(00)', [181625435, 181625438], '(00)3', [182105255, 182105259], '22(00)', [182223559, 182223562], '3(00)', [191116404, 191116407], '3(00)', [191165599, 191165602], '3(00)', [191297535, 191297539], '(00)22', [192485616, 192485619], '(00)3', [193264634, 193264638], '22(00)', [194696968, 194696971], '(00)3', [195876805, 195876808], '(00)3', [195916548, 195916551], '3(00)', [196395160, 196395163], '3(00)', [196676303, 196676306], '(00)3', [197889882, 197889885], '3(00)', [198014122, 198014125], '(00)3', [199235289, 199235292], '(00)3', [201007375, 201007378], '(00)3', [201030605, 201030608], '3(00)', [201184290, 201184293], '3(00)', [201685414, 201685418], '(00)22', [202762875, 202762878], '3(00)', [202860957, 202860960], '3(00)', [203832577, 203832580], '3(00)', [205880544, 205880547], '(00)3', [206357111, 206357114], '(00)3', [207159767, 207159770], '3(00)', [207167343, 207167346], '3(00)', [207482539, 207482543], '3(010)', [207669540, 207669543], '3(00)', [208053426, 208053429], '(00)3', [208110027, 208110030], '3(00)', [209513826, 209513829], '3(00)', [212623522, 212623525], '(00)3', [213841715, 213841718], '(00)3', [214012333, 214012336], '(00)3', [214073567, 214073570], '(00)3', [215170600, 215170603], '3(00)', [215881039, 215881042], '3(00)', [216274604, 216274607], '3(00)', [216957120, 216957123], '3(00)', [217323208, 217323211], '(00)3', [218799264, 218799267], '(00)3', [218803557, 218803560], '3(00)', [219735146, 219735149], '(00)3', [219830062, 219830065], '3(00)', [219897904, 219897907], '(00)3', [221205545, 221205548], '(00)3', [223601929, 223601932], '(00)3', [223907076, 223907079], '3(00)', [223970397, 223970400], '(00)3', [224874044, 224874048], '22(00)', [225291157, 225291160], '(00)3', [227481734, 227481737], '(00)3', [228006442, 228006445], '3(00)', [228357900, 228357903], '(00)3', [228386399, 228386402], '(00)3', [228907446, 228907449], '(00)3', [228984552, 228984555], '3(00)', [229140285, 229140288], '3(00)', [231810024, 231810027], '(00)3', [232838062, 232838065], '3(00)', [234389088, 234389091], '3(00)', [235588194, 235588197], '(00)3', [236645695, 236645698], '(00)3', [236962876, 236962879], '3(00)', [237516723, 237516727], '04(00)', [240004911, 240004914], '(00)3', [240221306, 240221309], '3(00)', [241389213, 241389217], '(010)3', [241549003, 241549006], '(00)3', [241729717, 241729720], '(00)3', [241743684, 241743687], '3(00)', [243780200, 243780203], '3(00)', [243801317, 243801320], '(00)3', [244122072, 244122075], '(00)3', [244691224, 244691227], '3(00)', [244841577, 244841580], '(00)3', [245813461, 245813464], '(00)3', [246299475, 246299478], '(00)3', [246450176, 246450179], '3(00)', [249069349, 249069352], '(00)3', [250076378, 250076381], '(00)3', [252442157, 252442160], '3(00)', [252904231, 252904234], '3(00)', [255145220, 255145223], '(00)3', [255285971, 255285974], '3(00)', [256713230, 256713233], '(00)3', [257992082, 257992085], '(00)3', [258447955, 258447959], '22(00)', [259298045, 259298048], '3(00)', [262141503, 262141506], '(00)3', [263681743, 263681746], '3(00)', [266527881, 266527885], '(010)3', [266617122, 266617125], '(00)3', [266628044, 266628047], '3(00)', [267305763, 267305766], '(00)3', [267388404, 267388407], '3(00)', [267441672, 267441675], '3(00)', [267464886, 267464889], '(00)3', [267554907, 267554910], '3(00)', [269787480, 269787483], '(00)3', [270881434, 270881437], '(00)3', [270997583, 270997586], '3(00)', [272096378, 272096381], '3(00)', [272583009, 272583012], '(00)3', [274190881, 274190884], '3(00)', [274268747, 274268750], '(00)3', [275297429, 275297432], '3(00)', [275545476, 275545479], '3(00)', [275898479, 275898482], '3(00)', [275953000, 275953003], '(00)3', [277117197, 277117201], '(00)22', [277447310, 277447313], '3(00)', [279059657, 279059660], '3(00)', [279259144, 279259147], '3(00)', [279513636, 279513639], '3(00)', [279849069, 279849072], '3(00)', [280291419, 280291422], '(00)3', [281449425, 281449428], '3(00)', [281507953, 281507956], '3(00)', [281825600, 281825603], '(00)3', [282547093, 282547096], '3(00)', [283120963, 283120966], '3(00)', [283323493, 283323496], '(00)3', [284764535, 284764538], '3(00)', [286172639, 286172642], '3(00)', [286688824, 286688827], '(00)3', [287222172, 287222175], '3(00)', [287235534, 287235537], '3(00)', [287304861, 287304864], '3(00)', [287433571, 287433574], '(00)3', [287823551, 287823554], '(00)3', [287872422, 287872425], '3(00)', [288766615, 288766618], '3(00)', [290122963, 290122966], '3(00)', [290450849, 290450853], '(00)22', [291426141, 291426144], '3(00)', [292810353, 292810356], '3(00)', [293109861, 293109864], '3(00)', [293398054, 293398057], '3(00)', [294134426, 294134429], '3(00)', [294216438, 294216441], '(00)3', [295367141, 295367144], '3(00)', [297834111, 297834114], '3(00)', [299099969, 299099972], '3(00)', [300746958, 300746961], '3(00)', [301097423, 301097426], '(00)3', [301834209, 301834212], '(00)3', [302554791, 302554794], '(00)3', [303497445, 303497448], '3(00)', [304165344, 304165347], '3(00)', [304790218, 304790222], '3(010)', [305302352, 305302355], '(00)3', [306785996, 306785999], '3(00)', [307051443, 307051446], '3(00)', [307481539, 307481542], '3(00)', [308605569, 308605572], '3(00)', [309237610, 309237613], '3(00)', [310509287, 310509290], '(00)3', [310554057, 310554060], '3(00)', [310646345, 310646348], '3(00)', [311274896, 311274899], '(00)3', [311894272, 311894275], '3(00)', [312269470, 312269473], '(00)3', [312306601, 312306605], '(00)40', [312683193, 312683196], '3(00)', [314499804, 314499807], '3(00)', [314636802, 314636805], '(00)3', [314689897, 314689900], '3(00)', [314721319, 314721322], '3(00)', [316132890, 316132893], '3(00)', [316217470, 316217474], '(010)3', [316465705, 316465708], '3(00)', [316542790, 316542793], '(00)3', [320822347, 320822350], '3(00)', [321733242, 321733245], '3(00)', [324413970, 324413973], '(00)3', [325950140, 325950143], '(00)3', [326675884, 326675887], '(00)3', [326704208, 326704211], '3(00)', [327596247, 327596250], '3(00)', [328123172, 328123175], '3(00)', [328182212, 328182215], '(00)3', [328257498, 328257501], '3(00)', [328315836, 328315839], '(00)3', [328800974, 328800977], '(00)3', [328998509, 328998512], '3(00)', [329725370, 329725373], '(00)3', [332080601, 332080604], '(00)3', [332221246, 332221249], '(00)3', [332299899, 332299902], '(00)3', [332532822, 332532825], '(00)3', [333334544, 333334548], '(00)22', [333881266, 333881269], '3(00)', [334703267, 334703270], '3(00)', [334875138, 334875141], '3(00)', [336531451, 336531454], '3(00)', [336825907, 336825910], '(00)3', [336993167, 336993170], '(00)3', [337493998, 337494001], '3(00)', [337861034, 337861037], '3(00)', [337899191, 337899194], '(00)3', [337958123, 337958126], '(00)3', [342331982, 342331985], '3(00)', [342676068, 342676071], '3(00)', [347063781, 347063784], '3(00)', [347697348, 347697351], '3(00)', [347954319, 347954322], '3(00)', [348162775, 348162778], '3(00)', [349210702, 349210705], '(00)3', [349212913, 349212916], '3(00)', [349248650, 349248653], '(00)3', [349913500, 349913503], '3(00)', [350891529, 350891532], '3(00)', [351089323, 351089326], '3(00)', [351826158, 351826161], '3(00)', [352228580, 352228583], '(00)3', [352376244, 352376247], '3(00)', [352853758, 352853761], '(00)3', [355110439, 355110442], '(00)3', [355808090, 355808094], '(00)40', [355941556, 355941559], '3(00)', [356360231, 356360234], '(00)3', [356586657, 356586660], '3(00)', [356892926, 356892929], '(00)3', [356908232, 356908235], '3(00)', [357912730, 357912733], '3(00)', [358120344, 358120347], '3(00)', [359044096, 359044099], '(00)3', [360819357, 360819360], '3(00)', [361399662, 361399666], '(010)3', [362361315, 362361318], '(00)3', [363610112, 363610115], '(00)3', [363964804, 363964807], '3(00)', [364527375, 364527378], '(00)3', [365090327, 365090330], '(00)3', [365414539, 365414542], '3(00)', [366738474, 366738477], '3(00)', [368714778, 368714783], '04(010)', [368831545, 368831548], '(00)3', [368902387, 368902390], '(00)3', [370109769, 370109772], '3(00)', [370963333, 370963336], '3(00)', [372541136, 372541140], '3(010)', [372681562, 372681565], '(00)3', [373009410, 373009413], '(00)3', [373458970, 373458973], '3(00)', [375648658, 375648661], '3(00)', [376834728, 376834731], '3(00)', [377119945, 377119948], '(00)3', [377335703, 377335706], '(00)3', [378091745, 378091748], '3(00)', [379139522, 379139525], '3(00)', [380279160, 380279163], '(00)3', [380619442, 380619445], '3(00)', [381244231, 381244234], '3(00)', [382327446, 382327450], '(010)3', [382357073, 382357076], '3(00)', [383545479, 383545482], '3(00)', [384363766, 384363769], '(00)3', [384401786, 384401790], '22(00)', [385198212, 385198215], '3(00)', [385824476, 385824479], '(00)3', [385908194, 385908197], '3(00)', [386946806, 386946809], '3(00)', [387592175, 387592179], '22(00)', [388329293, 388329296], '(00)3', [388679566, 388679569], '3(00)', [388832142, 388832145], '3(00)', [390087103, 390087106], '(00)3', [390190926, 390190930], '(00)22', [390331207, 390331210], '3(00)', [391674495, 391674498], '3(00)', [391937831, 391937834], '3(00)', [391951632, 391951636], '(00)22', [392963986, 392963989], '(00)3', [393007921, 393007924], '3(00)', [393373210, 393373213], '3(00)', [393759572, 393759575], '(00)3', [394036662, 394036665], '(00)3', [395813866, 395813869], '(00)3', [395956690, 395956693], '3(00)', [396031670, 396031673], '3(00)', [397076433, 397076436], '3(00)', [397470601, 397470604], '3(00)', [398289458, 398289461], '3(00)', # [368714778, 368714783], '04(010)', [437953499, 437953504], '04(010)', [526196233, 526196238], '032(00)', [744719566, 744719571], '(010)40', [750375857, 750375862], '032(00)', [958241932, 958241937], '04(010)', [983377342, 983377347], '(00)410', [1003780080, 1003780085], '04(010)', [1070232754, 1070232759], '(00)230', [1209834865, 1209834870], '032(00)', [1257209100, 1257209105], '(00)410', [1368002233, 1368002238], '(00)230' ] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/orthogonal.py0000644000175000017500000003737712014170666027144 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped def _hermite_param(ctx, n, z, parabolic_cylinder): """ Combined calculation of the Hermite polynomial H_n(z) (and its generalization to complex n) and the parabolic cylinder function D. """ n, ntyp = ctx._convert_param(n) z = ctx.convert(z) q = -ctx.mpq_1_2 # For re(z) > 0, 2F0 -- http://functions.wolfram.com/ # HypergeometricFunctions/HermiteHGeneral/06/02/0009/ # Otherwise, there is a reflection formula # 2F0 + http://functions.wolfram.com/HypergeometricFunctions/ # HermiteHGeneral/16/01/01/0006/ # # TODO: # An alternative would be to use # http://functions.wolfram.com/HypergeometricFunctions/ # HermiteHGeneral/06/02/0006/ # # Also, the 1F1 expansion # http://functions.wolfram.com/HypergeometricFunctions/ # HermiteHGeneral/26/01/02/0001/ # should probably be used for tiny z if not z: T1 = [2, ctx.pi], [n, 0.5], [], [q*(n-1)], [], [], 0 if parabolic_cylinder: T1[1][0] += q*n return T1, can_use_2f0 = ctx.isnpint(-n) or ctx.re(z) > 0 or \ (ctx.re(z) == 0 and ctx.im(z) > 0) expprec = ctx.prec*4 + 20 if parabolic_cylinder: u = ctx.fmul(ctx.fmul(z,z,prec=expprec), -0.25, exact=True) w = ctx.fmul(z, ctx.sqrt(0.5,prec=expprec), prec=expprec) else: w = z w2 = ctx.fmul(w, w, prec=expprec) rw2 = ctx.fdiv(1, w2, prec=expprec) nrw2 = ctx.fneg(rw2, exact=True) nw = ctx.fneg(w, exact=True) if can_use_2f0: T1 = [2, w], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 terms = [T1] else: T1 = [2, nw], [n, n], [], [], [q*n, q*(n-1)], [], nrw2 T2 = [2, ctx.pi, nw], [n+2, 0.5, 1], [], [q*n], [q*(n-1)], [1-q], w2 terms = [T1,T2] # Multiply by prefactor for D_n if parabolic_cylinder: expu = ctx.exp(u) for i in range(len(terms)): terms[i][1][0] += q*n terms[i][0].append(expu) terms[i][1].append(1) return tuple(terms) @defun def hermite(ctx, n, z, **kwargs): return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 0), [], **kwargs) @defun def pcfd(ctx, n, z, **kwargs): r""" Gives the parabolic cylinder function in Whittaker's notation `D_n(z) = U(-n-1/2, z)` (see :func:`~mpmath.pcfu`). It solves the differential equation .. math :: y'' + \left(n + \frac{1}{2} - \frac{1}{4} z^2\right) y = 0. and can be represented in terms of Hermite polynomials (see :func:`~mpmath.hermite`) as .. math :: D_n(z) = 2^{-n/2} e^{-z^2/4} H_n\left(\frac{z}{\sqrt{2}}\right). **Plots** .. literalinclude :: /modules/mpmath/plots/pcfd.py .. image :: /modules/mpmath/plots/pcfd.png **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> pcfd(0,0); pcfd(1,0); pcfd(2,0); pcfd(3,0) 1.0 0.0 -1.0 0.0 >>> pcfd(4,0); pcfd(-3,0) 3.0 0.6266570686577501256039413 >>> pcfd('1/2', 2+3j) (-5.363331161232920734849056 - 3.858877821790010714163487j) >>> pcfd(2, -10) 1.374906442631438038871515e-9 Verifying the differential equation:: >>> n = mpf(2.5) >>> y = lambda z: pcfd(n,z) >>> z = 1.75 >>> chop(diff(y,z,2) + (n+0.5-0.25*z**2)*y(z)) 0.0 Rational Taylor series expansion when `n` is an integer:: >>> taylor(lambda z: pcfd(5,z), 0, 7) [0.0, 15.0, 0.0, -13.75, 0.0, 3.96875, 0.0, -0.6015625] """ return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 1), [], **kwargs) @defun def pcfu(ctx, a, z, **kwargs): r""" Gives the parabolic cylinder function `U(a,z)`, which may be defined for `\Re(z) > 0` in terms of the confluent U-function (see :func:`~mpmath.hyperu`) by .. math :: U(a,z) = 2^{-\frac{1}{4}-\frac{a}{2}} e^{-\frac{1}{4} z^2} U\left(\frac{a}{2}+\frac{1}{4}, \frac{1}{2}, \frac{1}{2}z^2\right) or, for arbitrary `z`, .. math :: e^{-\frac{1}{4}z^2} U(a,z) = U(a,0) \,_1F_1\left(-\tfrac{a}{2}+\tfrac{1}{4}; \tfrac{1}{2}; -\tfrac{1}{2}z^2\right) + U'(a,0) z \,_1F_1\left(-\tfrac{a}{2}+\tfrac{3}{4}; \tfrac{3}{2}; -\tfrac{1}{2}z^2\right). **Examples** Connection to other functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> z = mpf(3) >>> pcfu(0.5,z) 0.03210358129311151450551963 >>> sqrt(pi/2)*exp(z**2/4)*erfc(z/sqrt(2)) 0.03210358129311151450551963 >>> pcfu(0.5,-z) 23.75012332835297233711255 >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) 23.75012332835297233711255 >>> pcfu(0.5,-z) 23.75012332835297233711255 >>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2)) 23.75012332835297233711255 """ n, _ = ctx._convert_param(a) return ctx.pcfd(-n-ctx.mpq_1_2, z) @defun def pcfv(ctx, a, z, **kwargs): r""" Gives the parabolic cylinder function `V(a,z)`, which can be represented in terms of :func:`~mpmath.pcfu` as .. math :: V(a,z) = \frac{\Gamma(a+\tfrac{1}{2}) (U(a,-z)-\sin(\pi a) U(a,z)}{\pi}. **Examples** Wronskian relation between `U` and `V`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a, z = 2, 3 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 0.7978845608028653558798921 >>> sqrt(2/pi) 0.7978845608028653558798921 >>> a, z = 2.5, 3 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 0.7978845608028653558798921 >>> a, z = 0.25, -1 >>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z) 0.7978845608028653558798921 >>> a, z = 2+1j, 2+3j >>> chop(pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)) 0.7978845608028653558798921 """ n, ntype = ctx._convert_param(a) z = ctx.convert(z) q = ctx.mpq_1_2 r = ctx.mpq_1_4 if ntype == 'Q' and ctx.isint(n*2): # Faster for half-integers def h(): jz = ctx.fmul(z, -1j, exact=True) T1terms = _hermite_param(ctx, -n-q, z, 1) T2terms = _hermite_param(ctx, n-q, jz, 1) for T in T1terms: T[0].append(1j) T[1].append(1) T[3].append(q-n) u = ctx.expjpi((q*n-r)) * ctx.sqrt(2/ctx.pi) for T in T2terms: T[0].append(u) T[1].append(1) return T1terms + T2terms v = ctx.hypercomb(h, [], **kwargs) if ctx._is_real_type(n) and ctx._is_real_type(z): v = ctx._re(v) return v else: def h(n): w = ctx.square_exp_arg(z, -0.25) u = ctx.square_exp_arg(z, 0.5) e = ctx.exp(w) l = [ctx.pi, q, ctx.exp(w)] Y1 = l, [-q, n*q+r, 1], [r-q*n], [], [q*n+r], [q], u Y2 = l + [z], [-q, n*q-r, 1, 1], [1-r-q*n], [], [q*n+1-r], [1+q], u c, s = ctx.cospi_sinpi(r+q*n) Y1[0].append(s) Y2[0].append(c) for Y in (Y1, Y2): Y[1].append(1) Y[3].append(q-n) return Y1, Y2 return ctx.hypercomb(h, [n], **kwargs) @defun def pcfw(ctx, a, z, **kwargs): r""" Gives the parabolic cylinder function `W(a,z)` defined in (DLMF 12.14). **Examples** Value at the origin:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a = mpf(0.25) >>> pcfw(a,0) 0.9722833245718180765617104 >>> power(2,-0.75)*sqrt(abs(gamma(0.25+0.5j*a)/gamma(0.75+0.5j*a))) 0.9722833245718180765617104 >>> diff(pcfw,(a,0),(0,1)) -0.5142533944210078966003624 >>> -power(2,-0.25)*sqrt(abs(gamma(0.75+0.5j*a)/gamma(0.25+0.5j*a))) -0.5142533944210078966003624 """ n, _ = ctx._convert_param(a) z = ctx.convert(z) def terms(): phi2 = ctx.arg(ctx.gamma(0.5 + ctx.j*n)) phi2 = (ctx.loggamma(0.5+ctx.j*n) - ctx.loggamma(0.5-ctx.j*n))/2j rho = ctx.pi/8 + 0.5*phi2 # XXX: cancellation computing k k = ctx.sqrt(1 + ctx.exp(2*ctx.pi*n)) - ctx.exp(ctx.pi*n) C = ctx.sqrt(k/2) * ctx.exp(0.25*ctx.pi*n) yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25)) yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25)) v = ctx.sum_accurately(terms) if ctx._is_real_type(n) and ctx._is_real_type(z): v = ctx._re(v) return v """ Even/odd PCFs. Useful? @defun def pcfy1(ctx, a, z, **kwargs): a, _ = ctx._convert_param(n) z = ctx.convert(z) def h(): w = ctx.square_exp_arg(z) w1 = ctx.fmul(w, -0.25, exact=True) w2 = ctx.fmul(w, 0.5, exact=True) e = ctx.exp(w1) return [e], [1], [], [], [ctx.mpq_1_2*a+ctx.mpq_1_4], [ctx.mpq_1_2], w2 return ctx.hypercomb(h, [], **kwargs) @defun def pcfy2(ctx, a, z, **kwargs): a, _ = ctx._convert_param(n) z = ctx.convert(z) def h(): w = ctx.square_exp_arg(z) w1 = ctx.fmul(w, -0.25, exact=True) w2 = ctx.fmul(w, 0.5, exact=True) e = ctx.exp(w1) return [e, z], [1, 1], [], [], [ctx.mpq_1_2*a+ctx.mpq_3_4], \ [ctx.mpq_3_2], w2 return ctx.hypercomb(h, [], **kwargs) """ @defun_wrapped def gegenbauer(ctx, n, a, z, **kwargs): # Special cases: a+0.5, a*2 poles if ctx.isnpint(a): return 0*(z+n) if ctx.isnpint(a+0.5): # TODO: something else is required here # E.g.: gegenbauer(-2, -0.5, 3) == -12 if ctx.isnpint(n+1): raise NotImplementedError("Gegenbauer function with two limits") def h(a): a2 = 2*a T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) return [T] return ctx.hypercomb(h, [a], **kwargs) def h(n): a2 = 2*a T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) return [T] return ctx.hypercomb(h, [n], **kwargs) @defun_wrapped def jacobi(ctx, n, a, b, x, **kwargs): if not ctx.isnpint(a): def h(n): return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),) return ctx.hypercomb(h, [n], **kwargs) if not ctx.isint(b): def h(n, a): return (([], [], [-b], [n+1, -b-n], [-n, a+b+n+1], [b+1], (x+1)*0.5),) return ctx.hypercomb(h, [n, a], **kwargs) # XXX: determine appropriate limit return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs) @defun_wrapped def laguerre(ctx, n, a, z, **kwargs): # XXX: limits, poles #if ctx.isnpint(n): # return 0*(a+z) def h(a): return (([], [], [a+n+1], [a+1, n+1], [-n], [a+1], z),) return ctx.hypercomb(h, [a], **kwargs) @defun_wrapped def legendre(ctx, n, x, **kwargs): if ctx.isint(n): n = int(n) # Accuracy near zeros if (n + (n < 0)) & 1: if not x: return x mag = ctx.mag(x) if mag < -2*ctx.prec-10: return x if mag < -5: ctx.prec += -mag return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs) @defun def legenp(ctx, n, m, z, type=2, **kwargs): # Legendre function, 1st kind n = ctx.convert(n) m = ctx.convert(m) # Faster if not m: return ctx.legendre(n, z, **kwargs) # TODO: correct evaluation at singularities if type == 2: def h(n,m): g = m*0.5 T = [1+z, 1-z], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) return (T,) return ctx.hypercomb(h, [n,m], **kwargs) if type == 3: def h(n,m): g = m*0.5 T = [z+1, z-1], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) return (T,) return ctx.hypercomb(h, [n,m], **kwargs) raise ValueError("requires type=2 or type=3") @defun def legenq(ctx, n, m, z, type=2, **kwargs): # Legendre function, 2nd kind n = ctx.convert(n) m = ctx.convert(m) z = ctx.convert(z) if z in (1, -1): #if ctx.isint(m): # return ctx.nan #return ctx.inf # unsigned return ctx.nan if type == 2: def h(n, m): cos, sin = ctx.cospi_sinpi(m) s = 2 * sin / ctx.pi c = cos a = 1+z b = 1-z u = m/2 w = (1-z)/2 T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ [-n, n+1], [1-m], w T2 = [-s, a, b], [-1, -u, u], [n+m+1], [n-m+1, m+1], \ [-n, n+1], [m+1], w return T1, T2 return ctx.hypercomb(h, [n, m], **kwargs) if type == 3: # The following is faster when there only is a single series # Note: not valid for -1 < z < 0 (?) if abs(z) > 1: def h(n, m): T1 = [ctx.expjpi(m), 2, ctx.pi, z, z-1, z+1], \ [1, -n-1, 0.5, -n-m-1, 0.5*m, 0.5*m], \ [n+m+1], [n+1.5], \ [0.5*(2+n+m), 0.5*(1+n+m)], [n+1.5], z**(-2) return [T1] return ctx.hypercomb(h, [n, m], **kwargs) else: # not valid for 1 < z < inf ? def h(n, m): s = 2 * ctx.sinpi(m) / ctx.pi c = ctx.expjpi(m) a = 1+z b = z-1 u = m/2 w = (1-z)/2 T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ [-n, n+1], [1-m], w T2 = [-s, c, a, b], [-1, 1, -u, u], [n+m+1], [n-m+1, m+1], \ [-n, n+1], [m+1], w return T1, T2 return ctx.hypercomb(h, [n, m], **kwargs) raise ValueError("requires type=2 or type=3") @defun_wrapped def chebyt(ctx, n, x, **kwargs): if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: return x * 0 return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs) @defun_wrapped def chebyu(ctx, n, x, **kwargs): if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1: return x * 0 return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs) @defun def spherharm(ctx, l, m, theta, phi, **kwargs): l = ctx.convert(l) m = ctx.convert(m) theta = ctx.convert(theta) phi = ctx.convert(phi) l_isint = ctx.isint(l) l_natural = l_isint and l >= 0 m_isint = ctx.isint(m) if l_isint and l < 0 and m_isint: return ctx.spherharm(-(l+1), m, theta, phi, **kwargs) if theta == 0 and m_isint and m < 0: return ctx.zero * 1j if l_natural and m_isint: if abs(m) > l: return ctx.zero * 1j # http://functions.wolfram.com/Polynomials/ # SphericalHarmonicY/26/01/02/0004/ def h(l,m): absm = abs(m) C = [-1, ctx.expj(m*phi), (2*l+1)*ctx.fac(l+absm)/ctx.pi/ctx.fac(l-absm), ctx.sin(theta)**2, ctx.fac(absm), 2] P = [0.5*m*(ctx.sign(m)+1), 1, 0.5, 0.5*absm, -1, -absm-1] return ((C, P, [], [], [absm-l, l+absm+1], [absm+1], ctx.sin(0.5*theta)**2),) else: # http://functions.wolfram.com/HypergeometricFunctions/ # SphericalHarmonicYGeneral/26/01/02/0001/ def h(l,m): if ctx.isnpint(l-m+1) or ctx.isnpint(l+m+1) or ctx.isnpint(1-m): return (([0], [-1], [], [], [], [], 0),) cos, sin = ctx.cos_sin(0.5*theta) C = [0.5*ctx.expj(m*phi), (2*l+1)/ctx.pi, ctx.gamma(l-m+1), ctx.gamma(l+m+1), cos**2, sin**2] P = [1, 0.5, 0.5, -0.5, 0.5*m, -0.5*m] return ((C, P, [], [1-m], [-l,l+1], [1-m], sin**2),) return ctx.hypercomb(h, [l,m], **kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/expintegrals.py0000644000175000017500000002627412014170666027467 0ustar georgeskgeorgeskfrom .functions import defun, defun_wrapped @defun_wrapped def _erf_complex(ctx, z): z2 = ctx.square_exp_arg(z, -1) #z2 = -z**2 v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2) if not ctx._re(z): v = ctx._im(v)*ctx.j return v @defun_wrapped def _erfc_complex(ctx, z): if ctx.re(z) > 2: z2 = ctx.square_exp_arg(z) nz2 = ctx.fneg(z2, exact=True) v = ctx.exp(nz2)/ctx.sqrt(ctx.pi) * ctx.hyperu((1,2),(1,2), z2) else: v = 1 - ctx._erf_complex(z) if not ctx._re(z): v = 1+ctx._im(v)*ctx.j return v @defun def erf(ctx, z): z = ctx.convert(z) if ctx._is_real_type(z): try: return ctx._erf(z) except NotImplementedError: pass if ctx._is_complex_type(z) and not z.imag: try: return type(z)(ctx._erf(z.real)) except NotImplementedError: pass return ctx._erf_complex(z) @defun def erfc(ctx, z): z = ctx.convert(z) if ctx._is_real_type(z): try: return ctx._erfc(z) except NotImplementedError: pass if ctx._is_complex_type(z) and not z.imag: try: return type(z)(ctx._erfc(z.real)) except NotImplementedError: pass return ctx._erfc_complex(z) @defun def square_exp_arg(ctx, z, mult=1, reciprocal=False): prec = ctx.prec*4+20 if reciprocal: z2 = ctx.fmul(z, z, prec=prec) z2 = ctx.fdiv(ctx.one, z2, prec=prec) else: z2 = ctx.fmul(z, z, prec=prec) if mult != 1: z2 = ctx.fmul(z2, mult, exact=True) return z2 @defun_wrapped def erfi(ctx, z): if not z: return z z2 = ctx.square_exp_arg(z) v = (2/ctx.sqrt(ctx.pi)*z) * ctx.hyp1f1((1,2), (3,2), z2) if not ctx._re(z): v = ctx._im(v)*ctx.j return v @defun_wrapped def erfinv(ctx, x): xre = ctx._re(x) if (xre != x) or (xre < -1) or (xre > 1): return ctx.bad_domain("erfinv(x) is defined only for -1 <= x <= 1") x = xre #if ctx.isnan(x): return x if not x: return x if x == 1: return ctx.inf if x == -1: return ctx.ninf if abs(x) < 0.9: a = 0.53728*x**3 + 0.813198*x else: # An asymptotic formula u = ctx.ln(2/ctx.pi/(abs(x)-1)**2) a = ctx.sign(x) * ctx.sqrt(u - ctx.ln(u))/ctx.sqrt(2) ctx.prec += 10 return ctx.findroot(lambda t: ctx.erf(t)-x, a) @defun_wrapped def npdf(ctx, x, mu=0, sigma=1): sigma = ctx.convert(sigma) return ctx.exp(-(x-mu)**2/(2*sigma**2)) / (sigma*ctx.sqrt(2*ctx.pi)) @defun_wrapped def ncdf(ctx, x, mu=0, sigma=1): a = (x-mu)/(sigma*ctx.sqrt(2)) if a < 0: return ctx.erfc(-a)/2 else: return (1+ctx.erf(a))/2 @defun_wrapped def betainc(ctx, a, b, x1=0, x2=1, regularized=False): if x1 == x2: v = 0 elif not x1: if x1 == 0 and x2 == 1: v = ctx.beta(a, b) else: v = x2**a * ctx.hyp2f1(a, 1-b, a+1, x2) / a else: m, d = ctx.nint_distance(a) if m <= 0: if d < -ctx.prec: h = +ctx.eps ctx.prec *= 2 a += h elif d < -4: ctx.prec -= d s1 = x2**a * ctx.hyp2f1(a,1-b,a+1,x2) s2 = x1**a * ctx.hyp2f1(a,1-b,a+1,x1) v = (s1 - s2) / a if regularized: v /= ctx.beta(a,b) return v @defun def gammainc(ctx, z, a=0, b=None, regularized=False): regularized = bool(regularized) z = ctx.convert(z) if a is None: a = ctx.zero lower_modified = False else: a = ctx.convert(a) lower_modified = a != ctx.zero if b is None: b = ctx.inf upper_modified = False else: b = ctx.convert(b) upper_modified = b != ctx.inf # Complete gamma function if not (upper_modified or lower_modified): if regularized: if ctx.re(z) < 0: return ctx.inf elif ctx.re(z) > 0: return ctx.one else: return ctx.nan return ctx.gamma(z) if a == b: return ctx.zero # Standardize if ctx.re(a) > ctx.re(b): return -ctx.gammainc(z, b, a, regularized) # Generalized gamma if upper_modified and lower_modified: return +ctx._gamma3(z, a, b, regularized) # Upper gamma elif lower_modified: return ctx._upper_gamma(z, a, regularized) # Lower gamma elif upper_modified: return ctx._lower_gamma(z, b, regularized) @defun def _lower_gamma(ctx, z, b, regularized=False): # Pole if ctx.isnpint(z): return type(z)(ctx.inf) G = [z] * regularized negb = ctx.fneg(b, exact=True) def h(z): T1 = [ctx.exp(negb), b, z], [1, z, -1], [], G, [1], [1+z], b return (T1,) return ctx.hypercomb(h, [z]) @defun def _upper_gamma(ctx, z, a, regularized=False): # Fast integer case, when available if ctx.isint(z): try: if regularized: # Gamma pole if ctx.isnpint(z): return type(z)(ctx.zero) orig = ctx.prec try: ctx.prec += 10 return ctx._gamma_upper_int(z, a) / ctx.gamma(z) finally: ctx.prec = orig else: return ctx._gamma_upper_int(z, a) except NotImplementedError: pass nega = ctx.fneg(a, exact=True) G = [z] * regularized # Use 2F0 series when possible; fall back to lower gamma representation try: def h(z): r = z-1 return [([ctx.exp(nega), a], [1, r], [], G, [1, -r], [], 1/nega)] return ctx.hypercomb(h, [z], force_series=True) except ctx.NoConvergence: def h(z): T1 = [], [1, z-1], [z], G, [], [], 0 T2 = [-ctx.exp(nega), a, z], [1, z, -1], [], G, [1], [1+z], a return T1, T2 return ctx.hypercomb(h, [z]) @defun def _gamma3(ctx, z, a, b, regularized=False): pole = ctx.isnpint(z) if regularized and pole: return ctx.zero try: ctx.prec += 15 # We don't know in advance whether it's better to write as a difference # of lower or upper gamma functions, so try both T1 = ctx.gammainc(z, a, regularized=regularized) T2 = ctx.gammainc(z, b, regularized=regularized) R = T1 - T2 if ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: return R if not pole: T1 = ctx.gammainc(z, 0, b, regularized=regularized) T2 = ctx.gammainc(z, 0, a, regularized=regularized) R = T1 - T2 # May be ok, but should probably at least print a warning # about possible cancellation if 1: #ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: return R finally: ctx.prec -= 15 raise NotImplementedError @defun_wrapped def expint(ctx, n, z): if ctx.isint(n) and ctx._is_real_type(z): try: return ctx._expint_int(n, z) except NotImplementedError: pass if ctx.isnan(n) or ctx.isnan(z): return z*n if z == ctx.inf: return 1/z if z == 0: # integral from 1 to infinity of t^n if ctx.re(n) <= 1: # TODO: reasonable sign of infinity return type(z)(ctx.inf) else: return ctx.one/(n-1) if n == 0: return ctx.exp(-z)/z if n == -1: return ctx.exp(-z)*(z+1)/z**2 return z**(n-1) * ctx.gammainc(1-n, z) @defun_wrapped def li(ctx, z, offset=False): if offset: if z == 2: return ctx.zero return ctx.ei(ctx.ln(z)) - ctx.ei(ctx.ln2) if not z: return z if z == 1: return ctx.ninf return ctx.ei(ctx.ln(z)) @defun def ei(ctx, z): try: return ctx._ei(z) except NotImplementedError: return ctx._ei_generic(z) @defun_wrapped def _ei_generic(ctx, z): # Note: the following is currently untested because mp and fp # both use special-case ei code if z == ctx.inf: return z if z == ctx.ninf: return ctx.zero if ctx.mag(z) > 1: try: r = ctx.one/z v = ctx.exp(z)*ctx.hyper([1,1],[],r, maxterms=ctx.prec, force_series=True)/z im = ctx._im(z) if im > 0: v += ctx.pi*ctx.j if im < 0: v -= ctx.pi*ctx.j return v except ctx.NoConvergence: pass v = z*ctx.hyp2f2(1,1,2,2,z) + ctx.euler if ctx._im(z): v += 0.5*(ctx.log(z) - ctx.log(ctx.one/z)) else: v += ctx.log(abs(z)) return v @defun def e1(ctx, z): try: return ctx._e1(z) except NotImplementedError: return ctx.expint(1, z) @defun def ci(ctx, z): try: return ctx._ci(z) except NotImplementedError: return ctx._ci_generic(z) @defun_wrapped def _ci_generic(ctx, z): if ctx.isinf(z): if z == ctx.inf: return ctx.zero if z == ctx.ninf: return ctx.pi*1j jz = ctx.fmul(ctx.j,z,exact=True) njz = ctx.fneg(jz,exact=True) v = 0.5*(ctx.ei(jz) + ctx.ei(njz)) zreal = ctx._re(z) zimag = ctx._im(z) if zreal == 0: if zimag > 0: v += ctx.pi*0.5j if zimag < 0: v -= ctx.pi*0.5j if zreal < 0: if zimag >= 0: v += ctx.pi*1j if zimag < 0: v -= ctx.pi*1j if ctx._is_real_type(z) and zreal > 0: v = ctx._re(v) return v @defun def si(ctx, z): try: return ctx._si(z) except NotImplementedError: return ctx._si_generic(z) @defun_wrapped def _si_generic(ctx, z): if ctx.isinf(z): if z == ctx.inf: return 0.5*ctx.pi if z == ctx.ninf: return -0.5*ctx.pi # Suffers from cancellation near 0 if ctx.mag(z) >= -1: jz = ctx.fmul(ctx.j,z,exact=True) njz = ctx.fneg(jz,exact=True) v = (-0.5j)*(ctx.ei(jz) - ctx.ei(njz)) zreal = ctx._re(z) if zreal > 0: v -= 0.5*ctx.pi if zreal < 0: v += 0.5*ctx.pi if ctx._is_real_type(z): v = ctx._re(v) return v else: return z*ctx.hyp1f2((1,2),(3,2),(3,2),-0.25*z*z) @defun_wrapped def chi(ctx, z): nz = ctx.fneg(z, exact=True) v = 0.5*(ctx.ei(z) + ctx.ei(nz)) zreal = ctx._re(z) zimag = ctx._im(z) if zimag > 0: v += ctx.pi*0.5j elif zimag < 0: v -= ctx.pi*0.5j elif zreal < 0: v += ctx.pi*1j return v @defun_wrapped def shi(ctx, z): # Suffers from cancellation near 0 if ctx.mag(z) >= -1: nz = ctx.fneg(z, exact=True) v = 0.5*(ctx.ei(z) - ctx.ei(nz)) zimag = ctx._im(z) if zimag > 0: v -= 0.5j*ctx.pi if zimag < 0: v += 0.5j*ctx.pi return v else: return z * ctx.hyp1f2((1,2),(3,2),(3,2),0.25*z*z) @defun_wrapped def fresnels(ctx, z): if z == ctx.inf: return ctx.mpf(0.5) if z == ctx.ninf: return ctx.mpf(-0.5) return ctx.pi*z**3/6*ctx.hyp1f2((3,4),(3,2),(7,4),-ctx.pi**2*z**4/16) @defun_wrapped def fresnelc(ctx, z): if z == ctx.inf: return ctx.mpf(0.5) if z == ctx.ninf: return ctx.mpf(-0.5) return z*ctx.hyp1f2((1,4),(1,2),(5,4),-ctx.pi**2*z**4/16) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/elliptic.py0000644000175000017500000011321712014170666026561 0ustar georgeskgeorgeskr""" Elliptic functions historically comprise the elliptic integrals and their inverses, and originate from the problem of computing the arc length of an ellipse. From a more modern point of view, an elliptic function is defined as a doubly periodic function, i.e. a function which satisfies .. math :: f(z + 2 \omega_1) = f(z + 2 \omega_2) = f(z) for some half-periods `\omega_1, \omega_2` with `\mathrm{Im}[\omega_1 / \omega_2] > 0`. The canonical elliptic functions are the Jacobi elliptic functions. More broadly, this section includes quasi-doubly periodic functions (such as the Jacobi theta functions) and other functions useful in the study of elliptic functions. Many different conventions for the arguments of elliptic functions are in use. It is even standard to use different parameterizations for different functions in the same text or software (and mpmath is no exception). The usual parameters are the elliptic nome `q`, which usually must satisfy `|q| < 1`; the elliptic parameter `m` (an arbitrary complex number); the elliptic modulus `k` (an arbitrary complex number); and the half-period ratio `\tau`, which usually must satisfy `\mathrm{Im}[\tau] > 0`. These quantities can be expressed in terms of each other using the following relations: .. math :: m = k^2 .. math :: \tau = -i \frac{K(1-m)}{K(m)} .. math :: q = e^{i \pi \tau} .. math :: k = \frac{\vartheta_2^4(q)}{\vartheta_3^4(q)} In addition, an alternative definition is used for the nome in number theory, which we here denote by q-bar: .. math :: \bar{q} = q^2 = e^{2 i \pi \tau} For convenience, mpmath provides functions to convert between the various parameters (:func:`~mpmath.qfrom`, :func:`~mpmath.mfrom`, :func:`~mpmath.kfrom`, :func:`~mpmath.taufrom`, :func:`~mpmath.qbarfrom`). **References** 1. [AbramowitzStegun]_ 2. [WhittakerWatson]_ """ from .functions import defun, defun_wrapped def nome(ctx, m): m = ctx.convert(m) if not m: return m if m == ctx.one: return m if ctx.isnan(m): return m if ctx.isinf(m): if m == ctx.ninf: return type(m)(-1) else: return ctx.mpc(-1) a = ctx.ellipk(ctx.one-m) b = ctx.ellipk(m) v = ctx.exp(-ctx.pi*a/b) if not ctx._im(m) and ctx._re(m) < 1: if ctx._is_real_type(m): return v.real else: return v.real + 0j elif m == 2: v = ctx.mpc(0, v.imag) return v @defun_wrapped def qfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic nome `q`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qfrom(q=0.25) 0.25 >>> qfrom(m=mfrom(q=0.25)) 0.25 >>> qfrom(k=kfrom(q=0.25)) 0.25 >>> qfrom(tau=taufrom(q=0.25)) (0.25 + 0.0j) >>> qfrom(qbar=qbarfrom(q=0.25)) 0.25 """ if q is not None: return ctx.convert(q) if m is not None: return nome(ctx, m) if k is not None: return nome(ctx, ctx.convert(k)**2) if tau is not None: return ctx.expjpi(tau) if qbar is not None: return ctx.sqrt(qbar) @defun_wrapped def qbarfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the number-theoretic nome `\bar q`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> qbarfrom(qbar=0.25) 0.25 >>> qbarfrom(q=qfrom(qbar=0.25)) 0.25 >>> qbarfrom(m=extraprec(20)(mfrom)(qbar=0.25)) # ill-conditioned 0.25 >>> qbarfrom(k=extraprec(20)(kfrom)(qbar=0.25)) # ill-conditioned 0.25 >>> qbarfrom(tau=taufrom(qbar=0.25)) (0.25 + 0.0j) """ if qbar is not None: return ctx.convert(qbar) if q is not None: return ctx.convert(q) ** 2 if m is not None: return nome(ctx, m) ** 2 if k is not None: return nome(ctx, ctx.convert(k)**2) ** 2 if tau is not None: return ctx.expjpi(2*tau) @defun_wrapped def taufrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic half-period ratio `\tau`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> taufrom(tau=0.5j) (0.0 + 0.5j) >>> taufrom(q=qfrom(tau=0.5j)) (0.0 + 0.5j) >>> taufrom(m=mfrom(tau=0.5j)) (0.0 + 0.5j) >>> taufrom(k=kfrom(tau=0.5j)) (0.0 + 0.5j) >>> taufrom(qbar=qbarfrom(tau=0.5j)) (0.0 + 0.5j) """ if tau is not None: return ctx.convert(tau) if m is not None: m = ctx.convert(m) return ctx.j*ctx.ellipk(1-m)/ctx.ellipk(m) if k is not None: k = ctx.convert(k) return ctx.j*ctx.ellipk(1-k**2)/ctx.ellipk(k**2) if q is not None: return ctx.log(q) / (ctx.pi*ctx.j) if qbar is not None: qbar = ctx.convert(qbar) return ctx.log(qbar) / (2*ctx.pi*ctx.j) @defun_wrapped def kfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic modulus `k`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> kfrom(k=0.25) 0.25 >>> kfrom(m=mfrom(k=0.25)) 0.25 >>> kfrom(q=qfrom(k=0.25)) 0.25 >>> kfrom(tau=taufrom(k=0.25)) (0.25 + 0.0j) >>> kfrom(qbar=qbarfrom(k=0.25)) 0.25 As `q \to 1` and `q \to -1`, `k` rapidly approaches `1` and `i \infty` respectively:: >>> kfrom(q=0.75) 0.9999999999999899166471767 >>> kfrom(q=-0.75) (0.0 + 7041781.096692038332790615j) >>> kfrom(q=1) 1 >>> kfrom(q=-1) (0.0 + +infj) """ if k is not None: return ctx.convert(k) if m is not None: return ctx.sqrt(m) if tau is not None: q = ctx.expjpi(tau) if qbar is not None: q = ctx.sqrt(qbar) if q == 1: return q if q == -1: return ctx.mpc(0,'inf') return (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**2 @defun_wrapped def mfrom(ctx, q=None, m=None, k=None, tau=None, qbar=None): r""" Returns the elliptic parameter `m`, given any of `q, m, k, \tau, \bar{q}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> mfrom(m=0.25) 0.25 >>> mfrom(q=qfrom(m=0.25)) 0.25 >>> mfrom(k=kfrom(m=0.25)) 0.25 >>> mfrom(tau=taufrom(m=0.25)) (0.25 + 0.0j) >>> mfrom(qbar=qbarfrom(m=0.25)) 0.25 As `q \to 1` and `q \to -1`, `m` rapidly approaches `1` and `-\infty` respectively:: >>> mfrom(q=0.75) 0.9999999999999798332943533 >>> mfrom(q=-0.75) -49586681013729.32611558353 >>> mfrom(q=1) 1.0 >>> mfrom(q=-1) -inf The inverse nome as a function of `q` has an integer Taylor series expansion:: >>> taylor(lambda q: mfrom(q), 0, 7) [0.0, 16.0, -128.0, 704.0, -3072.0, 11488.0, -38400.0, 117632.0] """ if m is not None: return m if k is not None: return k**2 if tau is not None: q = ctx.expjpi(tau) if qbar is not None: q = ctx.sqrt(qbar) if q == 1: return ctx.convert(q) if q == -1: return q*ctx.inf v = (ctx.jtheta(2,0,q)/ctx.jtheta(3,0,q))**4 if ctx._is_real_type(q) and q < 0: v = v.real return v jacobi_spec = { 'sn' : ([3],[2],[1],[4], 'sin', 'tanh'), 'cn' : ([4],[2],[2],[4], 'cos', 'sech'), 'dn' : ([4],[3],[3],[4], '1', 'sech'), 'ns' : ([2],[3],[4],[1], 'csc', 'coth'), 'nc' : ([2],[4],[4],[2], 'sec', 'cosh'), 'nd' : ([3],[4],[4],[3], '1', 'cosh'), 'sc' : ([3],[4],[1],[2], 'tan', 'sinh'), 'sd' : ([3,3],[2,4],[1],[3], 'sin', 'sinh'), 'cd' : ([3],[2],[2],[3], 'cos', '1'), 'cs' : ([4],[3],[2],[1], 'cot', 'csch'), 'dc' : ([2],[3],[3],[2], 'sec', '1'), 'ds' : ([2,4],[3,3],[3],[1], 'csc', 'csch'), 'cc' : None, 'ss' : None, 'nn' : None, 'dd' : None } @defun def ellipfun(ctx, kind, u=None, m=None, q=None, k=None, tau=None): try: S = jacobi_spec[kind] except KeyError: raise ValueError("First argument must be a two-character string " "containing 's', 'c', 'd' or 'n', e.g.: 'sn'") if u is None: def f(*args, **kwargs): return ctx.ellipfun(kind, *args, **kwargs) f.__name__ = kind return f prec = ctx.prec try: ctx.prec += 10 u = ctx.convert(u) q = ctx.qfrom(m=m, q=q, k=k, tau=tau) if S is None: v = ctx.one + 0*q*u elif q == ctx.zero: if S[4] == '1': v = ctx.one else: v = getattr(ctx, S[4])(u) v += 0*q*u elif q == ctx.one: if S[5] == '1': v = ctx.one else: v = getattr(ctx, S[5])(u) v += 0*q*u else: t = u / ctx.jtheta(3, 0, q)**2 v = ctx.one for a in S[0]: v *= ctx.jtheta(a, 0, q) for b in S[1]: v /= ctx.jtheta(b, 0, q) for c in S[2]: v *= ctx.jtheta(c, t, q) for d in S[3]: v /= ctx.jtheta(d, t, q) finally: ctx.prec = prec return +v @defun_wrapped def kleinj(ctx, tau=None, **kwargs): r""" Evaluates the Klein j-invariant, which is a modular function defined for `\tau` in the upper half-plane as .. math :: J(\tau) = \frac{g_2^3(\tau)}{g_2^3(\tau) - 27 g_3^2(\tau)} where `g_2` and `g_3` are the modular invariants of the Weierstrass elliptic function, .. math :: g_2(\tau) = 60 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-4} g_3(\tau) = 140 \sum_{(m,n) \in \mathbb{Z}^2 \setminus (0,0)} (m \tau+n)^{-6}. An alternative, common notation is that of the j-function `j(\tau) = 1728 J(\tau)`. **Plots** .. literalinclude :: /modules/mpmath/plots/kleinj.py .. image :: /modules/mpmath/plots/kleinj.png .. literalinclude :: /modules/mpmath/plots/kleinj2.py .. image :: /modules/mpmath/plots/kleinj2.png **Examples** Verifying the functional equation `J(\tau) = J(\tau+1) = J(-\tau^{-1})`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> tau = 0.625+0.75*j >>> tau = 0.625+0.75*j >>> kleinj(tau) (-0.1507492166511182267125242 + 0.07595948379084571927228948j) >>> kleinj(tau+1) (-0.1507492166511182267125242 + 0.07595948379084571927228948j) >>> kleinj(-1/tau) (-0.1507492166511182267125242 + 0.07595948379084571927228946j) The j-function has a famous Laurent series expansion in terms of the nome `\bar{q}`, `j(\tau) = \bar{q}^{-1} + 744 + 196884\bar{q} + \ldots`:: >>> mp.dps = 15 >>> taylor(lambda q: 1728*q*kleinj(qbar=q), 0, 5, singular=True) [1.0, 744.0, 196884.0, 21493760.0, 864299970.0, 20245856256.0] The j-function admits exact evaluation at special algebraic points related to the Heegner numbers 1, 2, 3, 7, 11, 19, 43, 67, 163:: >>> @extraprec(10) ... def h(n): ... v = (1+sqrt(n)*j) ... if n > 2: ... v *= 0.5 ... return v ... >>> mp.dps = 25 >>> for n in [1,2,3,7,11,19,43,67,163]: ... n, chop(1728*kleinj(h(n))) ... (1, 1728.0) (2, 8000.0) (3, 0.0) (7, -3375.0) (11, -32768.0) (19, -884736.0) (43, -884736000.0) (67, -147197952000.0) (163, -262537412640768000.0) Also at other special points, the j-function assumes explicit algebraic values, e.g.:: >>> chop(1728*kleinj(j*sqrt(5))) 1264538.909475140509320227 >>> identify(cbrt(_)) # note: not simplified '((100+sqrt(13520))/2)' >>> (50+26*sqrt(5))**3 1264538.909475140509320227 """ q = ctx.qfrom(tau=tau, **kwargs) t2 = ctx.jtheta(2,0,q) t3 = ctx.jtheta(3,0,q) t4 = ctx.jtheta(4,0,q) P = (t2**8 + t3**8 + t4**8)**3 Q = 54*(t2*t3*t4)**8 return P/Q def RF_calc(ctx, x, y, z, r): if y == z: return RC_calc(ctx, x, y, r) if x == z: return RC_calc(ctx, y, x, r) if x == y: return RC_calc(ctx, z, x, r) if not (ctx.isnormal(x) and ctx.isnormal(y) and ctx.isnormal(z)): if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z): return x*y*z if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z): return ctx.zero xm,ym,zm = x,y,z A0 = Am = (x+y+z)/3 Q = ctx.root(3*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z)) g = ctx.mpf(0.25) pow4 = ctx.one m = 0 while 1: xs = ctx.sqrt(xm) ys = ctx.sqrt(ym) zs = ctx.sqrt(zm) lm = xs*ys + xs*zs + ys*zs Am1 = (Am+lm)*g xm, ym, zm = (xm+lm)*g, (ym+lm)*g, (zm+lm)*g if pow4 * Q < abs(Am): break Am = Am1 m += 1 pow4 *= g t = pow4/Am X = (A0-x)*t Y = (A0-y)*t Z = -X-Y E2 = X*Y-Z**2 E3 = X*Y*Z return ctx.power(Am,-0.5) * (9240-924*E2+385*E2**2+660*E3-630*E2*E3)/9240 def RC_calc(ctx, x, y, r, pv=True): if not (ctx.isnormal(x) and ctx.isnormal(y)): if ctx.isinf(x) or ctx.isinf(y): return 1/(x*y) if y == 0: return ctx.inf if x == 0: return ctx.pi / ctx.sqrt(y) / 2 raise ValueError # Cauchy principal value if pv and ctx._im(y) == 0 and ctx._re(y) < 0: return ctx.sqrt(x/(x-y)) * RC_calc(ctx, x-y, -y, r) if x == y: return 1/ctx.sqrt(x) extraprec = 2*max(0,-ctx.mag(x-y)+ctx.mag(x)) ctx.prec += extraprec if ctx._is_real_type(x) and ctx._is_real_type(y): x = ctx._re(x) y = ctx._re(y) a = ctx.sqrt(x/y) if x < y: b = ctx.sqrt(y-x) v = ctx.acos(a)/b else: b = ctx.sqrt(x-y) v = ctx.acosh(a)/b else: sx = ctx.sqrt(x) sy = ctx.sqrt(y) v = ctx.acos(sx/sy)/(ctx.sqrt((1-x/y))*sy) ctx.prec -= extraprec return v def RJ_calc(ctx, x, y, z, p, r): if not (ctx.isnormal(x) and ctx.isnormal(y) and \ ctx.isnormal(z) and ctx.isnormal(p)): if ctx.isnan(x) or ctx.isnan(y) or ctx.isnan(z) or ctx.isnan(p): return x*y*z if ctx.isinf(x) or ctx.isinf(y) or ctx.isinf(z) or ctx.isinf(p): return ctx.zero if not p: return ctx.inf xm,ym,zm,pm = x,y,z,p A0 = Am = (x + y + z + 2*p)/5 delta = (p-x)*(p-y)*(p-z) Q = ctx.root(0.25*r, -6) * max(abs(A0-x),abs(A0-y),abs(A0-z),abs(A0-p)) m = 0 g = ctx.mpf(0.25) pow4 = ctx.one S = 0 while 1: sx = ctx.sqrt(xm) sy = ctx.sqrt(ym) sz = ctx.sqrt(zm) sp = ctx.sqrt(pm) lm = sx*sy + sx*sz + sy*sz Am1 = (Am+lm)*g xm = (xm+lm)*g; ym = (ym+lm)*g; zm = (zm+lm)*g; pm = (pm+lm)*g dm = (sp+sx) * (sp+sy) * (sp+sz) em = delta * ctx.power(4, -3*m) / dm**2 if pow4 * Q < abs(Am): break T = RC_calc(ctx, ctx.one, ctx.one+em, r) * pow4 / dm S += T pow4 *= g m += 1 Am = Am1 t = ctx.ldexp(1,-2*m) / Am X = (A0-x)*t Y = (A0-y)*t Z = (A0-z)*t P = (-X-Y-Z)/2 E2 = X*Y + X*Z + Y*Z - 3*P**2 E3 = X*Y*Z + 2*E2*P + 4*P**3 E4 = (2*X*Y*Z + E2*P + 3*P**3)*P E5 = X*Y*Z*P**2 P = 24024 - 5148*E2 + 2457*E2**2 + 4004*E3 - 4158*E2*E3 - 3276*E4 + 2772*E5 Q = 24024 v1 = g**m * ctx.power(Am, -1.5) * P/Q v2 = 6*S return v1 + v2 @defun def elliprf(ctx, x, y, z): r""" Evaluates the Carlson symmetric elliptic integral of the first kind .. math :: R_F(x,y,z) = \frac{1}{2} \int_0^{\infty} \frac{dt}{\sqrt{(t+x)(t+y)(t+z)}} which is defined for `x,y,z \notin (-\infty,0)`, and with at most one of `x,y,z` being zero. For real `x,y,z \ge 0`, the principal square root is taken in the integrand. For complex `x,y,z`, the principal square root is taken as `t \to \infty` and as `t \to 0` non-principal branches are chosen as necessary so as to make the integrand continuous. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprf(0,1,1); pi/2 1.570796326794896619231322 1.570796326794896619231322 >>> elliprf(0,1,inf) 0.0 >>> elliprf(1,1,1) 1.0 >>> elliprf(2,2,2)**2 0.5 >>> elliprf(1,0,0); elliprf(0,0,1); elliprf(0,1,0); elliprf(0,0,0) +inf +inf +inf +inf Representing complete elliptic integrals in terms of `R_F`:: >>> m = mpf(0.75) >>> ellipk(m); elliprf(0,1-m,1) 2.156515647499643235438675 2.156515647499643235438675 >>> ellipe(m); elliprf(0,1-m,1)-m*elliprd(0,1-m,1)/3 1.211056027568459524803563 1.211056027568459524803563 Some symmetries and argument transformations:: >>> x,y,z = 2,3,4 >>> elliprf(x,y,z); elliprf(y,x,z); elliprf(z,y,x) 0.5840828416771517066928492 0.5840828416771517066928492 0.5840828416771517066928492 >>> k = mpf(100000) >>> elliprf(k*x,k*y,k*z); k**(-0.5) * elliprf(x,y,z) 0.001847032121923321253219284 0.001847032121923321253219284 >>> l = sqrt(x*y) + sqrt(y*z) + sqrt(z*x) >>> elliprf(x,y,z); 2*elliprf(x+l,y+l,z+l) 0.5840828416771517066928492 0.5840828416771517066928492 >>> elliprf((x+l)/4,(y+l)/4,(z+l)/4) 0.5840828416771517066928492 Comparing with numerical integration:: >>> x,y,z = 2,3,4 >>> elliprf(x,y,z) 0.5840828416771517066928492 >>> f = lambda t: 0.5*((t+x)*(t+y)*(t+z))**(-0.5) >>> q = extradps(25)(quad) >>> q(f, [0,inf]) 0.5840828416771517066928492 With the following arguments, the square root in the integrand becomes discontinuous at `t = 1/2` if the principal branch is used. To obtain the right value, `-\sqrt{r}` must be taken instead of `\sqrt{r}` on `t \in (0, 1/2)`:: >>> x,y,z = j-1,j,0 >>> elliprf(x,y,z) (0.7961258658423391329305694 - 1.213856669836495986430094j) >>> -q(f, [0,0.5]) + q(f, [0.5,inf]) (0.7961258658423391329305694 - 1.213856669836495986430094j) The so-called *first lemniscate constant*, a transcendental number:: >>> elliprf(0,1,2) 1.31102877714605990523242 >>> extradps(25)(quad)(lambda t: 1/sqrt(1-t**4), [0,1]) 1.31102877714605990523242 >>> gamma('1/4')**2/(4*sqrt(2*pi)) 1.31102877714605990523242 **References** 1. [Carlson]_ 2. [DLMF]_ Chapter 19. Elliptic Integrals """ x = ctx.convert(x) y = ctx.convert(y) z = ctx.convert(z) prec = ctx.prec try: ctx.prec += 20 tol = ctx.eps * 2**10 v = RF_calc(ctx, x, y, z, tol) finally: ctx.prec = prec return +v @defun def elliprc(ctx, x, y, pv=True): r""" Evaluates the degenerate Carlson symmetric elliptic integral of the first kind .. math :: R_C(x,y) = R_F(x,y,y) = \frac{1}{2} \int_0^{\infty} \frac{dt}{(t+y) \sqrt{(t+x)}}. If `y \in (-\infty,0)`, either a value defined by continuity, or with *pv=True* the Cauchy principal value, can be computed. If `x \ge 0, y > 0`, the value can be expressed in terms of elementary functions as .. math :: R_C(x,y) = \begin{cases} \dfrac{1}{\sqrt{y-x}} \cos^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x < y \\ \dfrac{1}{\sqrt{y}}, & x = y \\ \dfrac{1}{\sqrt{x-y}} \cosh^{-1}\left(\sqrt{\dfrac{x}{y}}\right), & x > y \\ \end{cases}. **Examples** Some special values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprc(1,2)*4; elliprc(0,1)*2; +pi 3.141592653589793238462643 3.141592653589793238462643 3.141592653589793238462643 >>> elliprc(1,0) +inf >>> elliprc(5,5)**2 0.2 >>> elliprc(1,inf); elliprc(inf,1); elliprc(inf,inf) 0.0 0.0 0.0 Comparing with the elementary closed-form solution:: >>> elliprc('1/3', '1/5'); sqrt(7.5)*acosh(sqrt('5/3')) 2.041630778983498390751238 2.041630778983498390751238 >>> elliprc('1/5', '1/3'); sqrt(7.5)*acos(sqrt('3/5')) 1.875180765206547065111085 1.875180765206547065111085 Comparing with numerical integration:: >>> q = extradps(25)(quad) >>> elliprc(2, -3, pv=True) 0.3333969101113672670749334 >>> elliprc(2, -3, pv=False) (0.3333969101113672670749334 + 0.7024814731040726393156375j) >>> 0.5*q(lambda t: 1/(sqrt(t+2)*(t-3)), [0,3-j,6,inf]) (0.3333969101113672670749334 + 0.7024814731040726393156375j) """ x = ctx.convert(x) y = ctx.convert(y) prec = ctx.prec try: ctx.prec += 20 tol = ctx.eps * 2**10 v = RC_calc(ctx, x, y, tol, pv) finally: ctx.prec = prec return +v @defun def elliprj(ctx, x, y, z, p): r""" Evaluates the Carlson symmetric elliptic integral of the third kind .. math :: R_J(x,y,z,p) = \frac{3}{2} \int_0^{\infty} \frac{dt}{(t+p)\sqrt{(t+x)(t+y)(t+z)}}. Like :func:`~mpmath.elliprf`, the branch of the square root in the integrand is defined so as to be continuous along the path of integration for complex values of the arguments. **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprj(1,1,1,1) 1.0 >>> elliprj(2,2,2,2); 1/(2*sqrt(2)) 0.3535533905932737622004222 0.3535533905932737622004222 >>> elliprj(0,1,2,2) 1.067937989667395702268688 >>> 3*(2*gamma('5/4')**2-pi**2/gamma('1/4')**2)/(sqrt(2*pi)) 1.067937989667395702268688 >>> elliprj(0,1,1,2); 3*pi*(2-sqrt(2))/4 1.380226776765915172432054 1.380226776765915172432054 >>> elliprj(1,3,2,0); elliprj(0,1,1,0); elliprj(0,0,0,0) +inf +inf +inf >>> elliprj(1,inf,1,0); elliprj(1,1,1,inf) 0.0 0.0 >>> chop(elliprj(1+j, 1-j, 1, 1)) 0.8505007163686739432927844 Scale transformation:: >>> x,y,z,p = 2,3,4,5 >>> k = mpf(100000) >>> elliprj(k*x,k*y,k*z,k*p); k**(-1.5)*elliprj(x,y,z,p) 4.521291677592745527851168e-9 4.521291677592745527851168e-9 Comparing with numerical integration:: >>> elliprj(1,2,3,4) 0.2398480997495677621758617 >>> f = lambda t: 1/((t+4)*sqrt((t+1)*(t+2)*(t+3))) >>> 1.5*quad(f, [0,inf]) 0.2398480997495677621758617 >>> elliprj(1,2+1j,3,4-2j) (0.216888906014633498739952 + 0.04081912627366673332369512j) >>> f = lambda t: 1/((t+4-2j)*sqrt((t+1)*(t+2+1j)*(t+3))) >>> 1.5*quad(f, [0,inf]) (0.216888906014633498739952 + 0.04081912627366673332369511j) """ x = ctx.convert(x) y = ctx.convert(y) z = ctx.convert(z) p = ctx.convert(p) prec = ctx.prec try: ctx.prec += 20 tol = ctx.eps * 2**10 v = RJ_calc(ctx, x, y, z, p, tol) finally: ctx.prec = prec return +v @defun def elliprd(ctx, x, y, z): r""" Evaluates the degenerate Carlson symmetric elliptic integral of the third kind or Carlson elliptic integral of the second kind `R_D(x,y,z) = R_J(x,y,z,z)`. See :func:`~mpmath.elliprj` for additional information. **Examples** >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprd(1,2,3) 0.2904602810289906442326534 >>> elliprj(1,2,3,3) 0.2904602810289906442326534 The so-called *second lemniscate constant*, a transcendental number:: >>> elliprd(0,2,1)/3 0.5990701173677961037199612 >>> extradps(25)(quad)(lambda t: t**2/sqrt(1-t**4), [0,1]) 0.5990701173677961037199612 >>> gamma('3/4')**2/sqrt(2*pi) 0.5990701173677961037199612 """ return ctx.elliprj(x,y,z,z) @defun def elliprg(ctx, x, y, z): r""" Evaluates the Carlson completely symmetric elliptic integral of the second kind .. math :: R_G(x,y,z) = \frac{1}{4} \int_0^{\infty} \frac{t}{\sqrt{(t+x)(t+y)(t+z)}} \left( \frac{x}{t+x} + \frac{y}{t+y} + \frac{z}{t+z}\right) dt. **Examples** Evaluation for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> elliprg(0,1,1)*4; +pi 3.141592653589793238462643 3.141592653589793238462643 >>> elliprg(0,0.5,1) 0.6753219405238377512600874 >>> chop(elliprg(1+j, 1-j, 2)) 1.172431327676416604532822 A double integral that can be evaluated in terms of `R_G`:: >>> x,y,z = 2,3,4 >>> def f(t,u): ... st = fp.sin(t); ct = fp.cos(t) ... su = fp.sin(u); cu = fp.cos(u) ... return (x*(st*cu)**2 + y*(st*su)**2 + z*ct**2)**0.5 * st ... >>> nprint(mpf(fp.quad(f, [0,fp.pi], [0,2*fp.pi])/(4*fp.pi)), 13) 1.725503028069 >>> nprint(elliprg(x,y,z), 13) 1.725503028069 """ x = ctx.convert(x) y = ctx.convert(y) z = ctx.convert(z) if not z: x, z = z, x if not z: y, z = x, y if not z: return ctx.inf def terms(): T1 = 0.5*z*ctx.elliprf(x,y,z) T2 = -0.5*(x-z)*(y-z)*ctx.elliprd(x,y,z)/3 T3 = 0.5*ctx.sqrt(x*y/z) return T1,T2,T3 return ctx.sum_accurately(terms) @defun_wrapped def ellipf(ctx, phi, m): r""" Evaluates the Legendre incomplete elliptic integral of the first kind .. math :: F(\phi,m) = \int_0^{\phi} \frac{dt}{\sqrt{1-m \sin^2 t}} or equivalently .. math :: F(\phi,m) = \int_0^{\sin z} \frac{dt}{\left(\sqrt{1-t^2}\right)\left(\sqrt{1-mt^2}\right)}. The function reduces to a complete elliptic integral of the first kind (see :func:`~mpmath.ellipk`) when `\phi = \frac{\pi}{2}`; that is, .. math :: F\left(\frac{\pi}{2}, m\right) = K(m). In the defining integral, it is assumed that the principal branch of the square root is taken and that the path of integration avoids crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, the function extends quasi-periodically as .. math :: F(\phi + n \pi, m) = 2 n K(m) + F(\phi,m), n \in \mathbb{Z}. **Plots** .. literalinclude :: /modules/mpmath/plots/ellipf.py .. image :: /modules/mpmath/plots/ellipf.png **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipf(0,1) 0.0 >>> ellipf(0,0) 0.0 >>> ellipf(1,0); ellipf(2+3j,0) 1.0 (2.0 + 3.0j) >>> ellipf(1,1); log(sec(1)+tan(1)) 1.226191170883517070813061 1.226191170883517070813061 >>> ellipf(pi/2, -0.5); ellipk(-0.5) 1.415737208425956198892166 1.415737208425956198892166 >>> ellipf(pi/2+eps, 1); ellipf(-pi/2-eps, 1) +inf +inf >>> ellipf(1.5, 1) 3.340677542798311003320813 Comparing with numerical integration:: >>> z,m = 0.5, 1.25 >>> ellipf(z,m) 0.5287219202206327872978255 >>> quad(lambda t: (1-m*sin(t)**2)**(-0.5), [0,z]) 0.5287219202206327872978255 The arguments may be complex numbers:: >>> ellipf(3j, 0.5) (0.0 + 1.713602407841590234804143j) >>> ellipf(3+4j, 5-6j) (1.269131241950351323305741 - 0.3561052815014558335412538j) >>> z,m = 2+3j, 1.25 >>> k = 1011 >>> ellipf(z+pi*k,m); ellipf(z,m) + 2*k*ellipk(m) (4086.184383622179764082821 - 3003.003538923749396546871j) (4086.184383622179764082821 - 3003.003538923749396546871j) For `|\Re(z)| < \pi/2`, the function can be expressed as a hypergeometric series of two variables (see :func:`~mpmath.appellf1`):: >>> z,m = 0.5, 0.25 >>> ellipf(z,m) 0.5050887275786480788831083 >>> sin(z)*appellf1(0.5,0.5,0.5,1.5,sin(z)**2,m*sin(z)**2) 0.5050887275786480788831083 """ z = phi if not (ctx.isnormal(z) and ctx.isnormal(m)): if m == 0: return z + m if z == 0: return z * m if m == ctx.inf or m == ctx.ninf: return z/m raise ValueError x = z.real ctx.prec += max(0, ctx.mag(x)) pi = +ctx.pi away = abs(x) > pi/2 if m == 1: if away: return ctx.inf if away: d = ctx.nint(x/pi) z = z-pi*d P = 2*d*ctx.ellipk(m) else: P = 0 c, s = ctx.cos_sin(z) return s * ctx.elliprf(c**2, 1-m*s**2, 1) + P @defun_wrapped def ellipe(ctx, *args): r""" Called with a single argument `m`, evaluates the Legendre complete elliptic integral of the second kind, `E(m)`, defined by .. math :: E(m) = \int_0^{\pi/2} \sqrt{1-m \sin^2 t} \, dt \,=\, \frac{\pi}{2} \,_2F_1\left(\frac{1}{2}, -\frac{1}{2}, 1, m\right). Called with two arguments `\phi, m`, evaluates the incomplete elliptic integral of the second kind .. math :: E(\phi,m) = \int_0^{\phi} \sqrt{1-m \sin^2 t} \, dt = \int_0^{\sin z} \frac{\sqrt{1-mt^2}}{\sqrt{1-t^2}} \, dt. The incomplete integral reduces to a complete integral when `\phi = \frac{\pi}{2}`; that is, .. math :: E\left(\frac{\pi}{2}, m\right) = E(m). In the defining integral, it is assumed that the principal branch of the square root is taken and that the path of integration avoids crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, the function extends quasi-periodically as .. math :: E(\phi + n \pi, m) = 2 n E(m) + F(\phi,m), n \in \mathbb{Z}. **Plots** .. literalinclude :: /modules/mpmath/plots/ellipe.py .. image :: /modules/mpmath/plots/ellipe.png **Examples for the complete integral** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipe(0) 1.570796326794896619231322 >>> ellipe(1) 1.0 >>> ellipe(-1) 1.910098894513856008952381 >>> ellipe(2) (0.5990701173677961037199612 + 0.5990701173677961037199612j) >>> ellipe(inf) (0.0 + +infj) >>> ellipe(-inf) +inf Verifying the defining integral and hypergeometric representation:: >>> ellipe(0.5) 1.350643881047675502520175 >>> quad(lambda t: sqrt(1-0.5*sin(t)**2), [0, pi/2]) 1.350643881047675502520175 >>> pi/2*hyp2f1(0.5,-0.5,1,0.5) 1.350643881047675502520175 Evaluation is supported for arbitrary complex `m`:: >>> ellipe(0.5+0.25j) (1.360868682163129682716687 - 0.1238733442561786843557315j) >>> ellipe(3+4j) (1.499553520933346954333612 - 1.577879007912758274533309j) A definite integral:: >>> quad(ellipe, [0,1]) 1.333333333333333333333333 **Examples for the incomplete integral** Basic values and limits:: >>> ellipe(0,1) 0.0 >>> ellipe(0,0) 0.0 >>> ellipe(1,0) 1.0 >>> ellipe(2+3j,0) (2.0 + 3.0j) >>> ellipe(1,1); sin(1) 0.8414709848078965066525023 0.8414709848078965066525023 >>> ellipe(pi/2, -0.5); ellipe(-0.5) 1.751771275694817862026502 1.751771275694817862026502 >>> ellipe(pi/2, 1); ellipe(-pi/2, 1) 1.0 -1.0 >>> ellipe(1.5, 1) 0.9974949866040544309417234 Comparing with numerical integration:: >>> z,m = 0.5, 1.25 >>> ellipe(z,m) 0.4740152182652628394264449 >>> quad(lambda t: sqrt(1-m*sin(t)**2), [0,z]) 0.4740152182652628394264449 The arguments may be complex numbers:: >>> ellipe(3j, 0.5) (0.0 + 7.551991234890371873502105j) >>> ellipe(3+4j, 5-6j) (24.15299022574220502424466 + 75.2503670480325997418156j) >>> k = 35 >>> z,m = 2+3j, 1.25 >>> ellipe(z+pi*k,m); ellipe(z,m) + 2*k*ellipe(m) (48.30138799412005235090766 + 17.47255216721987688224357j) (48.30138799412005235090766 + 17.47255216721987688224357j) For `|\Re(z)| < \pi/2`, the function can be expressed as a hypergeometric series of two variables (see :func:`~mpmath.appellf1`):: >>> z,m = 0.5, 0.25 >>> ellipe(z,m) 0.4950017030164151928870375 >>> sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) 0.4950017030164151928870376 """ if len(args) == 1: return ctx._ellipe(args[0]) else: phi, m = args z = phi if not (ctx.isnormal(z) and ctx.isnormal(m)): if m == 0: return z + m if z == 0: return z * m if m == ctx.inf or m == ctx.ninf: return ctx.inf raise ValueError x = z.real ctx.prec += max(0, ctx.mag(x)) pi = +ctx.pi away = abs(x) > pi/2 if away: d = ctx.nint(x/pi) z = z-pi*d P = 2*d*ctx.ellipe(m) else: P = 0 def terms(): c, s = ctx.cos_sin(z) x = c**2 y = 1-m*s**2 RF = ctx.elliprf(x, y, 1) RD = ctx.elliprd(x, y, 1) return s*RF, -m*s**3*RD/3 return ctx.sum_accurately(terms) + P @defun_wrapped def ellippi(ctx, *args): r""" Called with three arguments `n, \phi, m`, evaluates the Legendre incomplete elliptic integral of the third kind .. math :: \Pi(n; \phi, m) = \int_0^{\phi} \frac{dt}{(1-n \sin^2 t) \sqrt{1-m \sin^2 t}} = \int_0^{\sin \phi} \frac{dt}{(1-nt^2) \sqrt{1-t^2} \sqrt{1-mt^2}}. Called with two arguments `n, m`, evaluates the complete elliptic integral of the third kind `\Pi(n,m) = \Pi(n; \frac{\pi}{2},m)`. In the defining integral, it is assumed that the principal branch of the square root is taken and that the path of integration avoids crossing any branch cuts. Outside `-\pi/2 \le \Re(z) \le \pi/2`, the function extends quasi-periodically as .. math :: \Pi(n,\phi+k\pi,m) = 2k\Pi(n,m) + \Pi(n,\phi,m), k \in \mathbb{Z}. **Plots** .. literalinclude :: /modules/mpmath/plots/ellippi.py .. image :: /modules/mpmath/plots/ellippi.png **Examples for the complete integral** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellippi(0,-5); ellipk(-5) 0.9555039270640439337379334 0.9555039270640439337379334 >>> ellippi(inf,2) 0.0 >>> ellippi(2,inf) 0.0 >>> abs(ellippi(1,5)) +inf >>> abs(ellippi(0.25,1)) +inf Evaluation in terms of simpler functions:: >>> ellippi(0.25,0.25); ellipe(0.25)/(1-0.25) 1.956616279119236207279727 1.956616279119236207279727 >>> ellippi(3,0); pi/(2*sqrt(-2)) (0.0 - 1.11072073453959156175397j) (0.0 - 1.11072073453959156175397j) >>> ellippi(-3,0); pi/(2*sqrt(4)) 0.7853981633974483096156609 0.7853981633974483096156609 **Examples for the incomplete integral** Basic values and limits:: >>> ellippi(0.25,-0.5); ellippi(0.25,pi/2,-0.5) 1.622944760954741603710555 1.622944760954741603710555 >>> ellippi(1,0,1) 0.0 >>> ellippi(inf,0,1) 0.0 >>> ellippi(0,0.25,0.5); ellipf(0.25,0.5) 0.2513040086544925794134591 0.2513040086544925794134591 >>> ellippi(1,1,1); (log(sec(1)+tan(1))+sec(1)*tan(1))/2 2.054332933256248668692452 2.054332933256248668692452 >>> ellippi(0.25, 53*pi/2, 0.75); 53*ellippi(0.25,0.75) 135.240868757890840755058 135.240868757890840755058 >>> ellippi(0.5,pi/4,0.5); 2*ellipe(pi/4,0.5)-1/sqrt(3) 0.9190227391656969903987269 0.9190227391656969903987269 Complex arguments are supported:: >>> ellippi(0.5, 5+6j-2*pi, -7-8j) (-0.3612856620076747660410167 + 0.5217735339984807829755815j) """ if len(args) == 2: n, m = args complete = True z = phi = ctx.pi/2 else: n, phi, m = args complete = False z = phi if not (ctx.isnormal(n) and ctx.isnormal(z) and ctx.isnormal(m)): if ctx.isnan(n) or ctx.isnan(z) or ctx.isnan(m): raise ValueError if complete: if m == 0: return ctx.pi/(2*ctx.sqrt(1-n)) if n == 0: return ctx.ellipk(m) if ctx.isinf(n) or ctx.isinf(m): return ctx.zero else: if z == 0: return z if ctx.isinf(n): return ctx.zero if ctx.isinf(m): return ctx.zero if ctx.isinf(n) or ctx.isinf(z) or ctx.isinf(m): raise ValueError if complete: if m == 1: return -ctx.inf/ctx.sign(n-1) away = False else: x = z.real ctx.prec += max(0, ctx.mag(x)) pi = +ctx.pi away = abs(x) > pi/2 if away: d = ctx.nint(x/pi) z = z-pi*d P = 2*d*ctx.ellippi(n,m) else: P = 0 def terms(): if complete: c, s = ctx.zero, ctx.one else: c, s = ctx.cos_sin(z) x = c**2 y = 1-m*s**2 RF = ctx.elliprf(x, y, 1) RJ = ctx.elliprj(x, y, 1, 1-n*s**2) return s*RF, n*s**3*RJ/3 return ctx.sum_accurately(terms) + P wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/factorials.py0000644000175000017500000001243412014170666027102 0ustar georgeskgeorgeskfrom ..libmp.backend import xrange from .functions import defun, defun_wrapped @defun def gammaprod(ctx, a, b, _infsign=False): a = [ctx.convert(x) for x in a] b = [ctx.convert(x) for x in b] poles_num = [] poles_den = [] regular_num = [] regular_den = [] for x in a: [regular_num, poles_num][ctx.isnpint(x)].append(x) for x in b: [regular_den, poles_den][ctx.isnpint(x)].append(x) # One more pole in numerator or denominator gives 0 or inf if len(poles_num) < len(poles_den): return ctx.zero if len(poles_num) > len(poles_den): # Get correct sign of infinity for x+h, h -> 0 from above # XXX: hack, this should be done properly if _infsign: a = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_num] b = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_den] return ctx.sign(ctx.gammaprod(a+regular_num,b+regular_den)) * ctx.inf else: return ctx.inf # All poles cancel # lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i) p = ctx.one orig = ctx.prec try: ctx.prec = orig + 15 while poles_num: i = poles_num.pop() j = poles_den.pop() p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i) for x in regular_num: p *= ctx.gamma(x) for x in regular_den: p /= ctx.gamma(x) finally: ctx.prec = orig return +p @defun def beta(ctx, x, y): x = ctx.convert(x) y = ctx.convert(y) if ctx.isinf(y): x, y = y, x if ctx.isinf(x): if x == ctx.inf and not ctx._im(y): if y == ctx.ninf: return ctx.nan if y > 0: return ctx.zero if ctx.isint(y): return ctx.nan if y < 0: return ctx.sign(ctx.gamma(y)) * ctx.inf return ctx.nan return ctx.gammaprod([x, y], [x+y]) @defun def binomial(ctx, n, k): return ctx.gammaprod([n+1], [k+1, n-k+1]) @defun def rf(ctx, x, n): return ctx.gammaprod([x+n], [x]) @defun def ff(ctx, x, n): return ctx.gammaprod([x+1], [x-n+1]) @defun_wrapped def fac2(ctx, x): if ctx.isinf(x): if x == ctx.inf: return x return ctx.nan return 2**(x/2)*(ctx.pi/2)**((ctx.cospi(x)-1)/4)*ctx.gamma(x/2+1) @defun_wrapped def barnesg(ctx, z): if ctx.isinf(z): if z == ctx.inf: return z return ctx.nan if ctx.isnan(z): return z if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)): return z*0 # Account for size (would not be needed if computing log(G)) if abs(z) > 5: ctx.dps += 2*ctx.log(abs(z),2) # Reflection formula if ctx.re(z) < -ctx.dps: w = 1-z pi2 = 2*ctx.pi u = ctx.expjpi(2*w) v = ctx.j*ctx.pi/12 - ctx.j*ctx.pi*w**2/2 + w*ctx.ln(1-u) - \ ctx.j*ctx.polylog(2, u)/pi2 v = ctx.barnesg(2-z)*ctx.exp(v)/pi2**w if ctx._is_real_type(z): v = ctx._re(v) return v # Estimate terms for asymptotic expansion # TODO: fixme, obviously N = ctx.dps // 2 + 5 G = 1 while abs(z) < N or ctx.re(z) < 1: G /= ctx.gamma(z) z += 1 z -= 1 s = ctx.mpf(1)/12 s -= ctx.log(ctx.glaisher) s += z*ctx.log(2*ctx.pi)/2 s += (z**2/2-ctx.mpf(1)/12)*ctx.log(z) s -= 3*z**2/4 z2k = z2 = z**2 for k in xrange(1, N+1): t = ctx.bernoulli(2*k+2) / (4*k*(k+1)*z2k) if abs(t) < ctx.eps: #print k, N # check how many terms were needed break z2k *= z2 s += t #if k == N: # print "warning: series for barnesg failed to converge", ctx.dps return G*ctx.exp(s) @defun def superfac(ctx, z): return ctx.barnesg(z+2) @defun_wrapped def hyperfac(ctx, z): # XXX: estimate needed extra bits accurately if z == ctx.inf: return z if abs(z) > 5: extra = 4*int(ctx.log(abs(z),2)) else: extra = 0 ctx.prec += extra if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)): n = int(ctx.re(z)) h = ctx.hyperfac(-n-1) if ((n+1)//2) & 1: h = -h if ctx._is_complex_type(z): return h + 0j return h zp1 = z+1 # Wrong branch cut #v = ctx.gamma(zp1)**z #ctx.prec -= extra #return v / ctx.barnesg(zp1) v = ctx.exp(z*ctx.loggamma(zp1)) ctx.prec -= extra return v / ctx.barnesg(zp1) @defun_wrapped def loggamma_old(ctx, z): a = ctx._re(z) b = ctx._im(z) if not b and a > 0: return ctx.ln(ctx.gamma_old(z)) u = ctx.arg(z) w = ctx.ln(ctx.gamma_old(z)) if b: gi = -b - u/2 + a*u + b*ctx.ln(abs(z)) n = ctx.floor((gi-ctx._im(w))/(2*ctx.pi)+0.5) * (2*ctx.pi) return w + n*ctx.j elif a < 0: n = int(ctx.floor(a)) w += (n-(n%2))*ctx.pi*ctx.j return w ''' @defun def psi0(ctx, z): """Shortcut for psi(0,z) (the digamma function)""" return ctx.psi(0, z) @defun def psi1(ctx, z): """Shortcut for psi(1,z) (the trigamma function)""" return ctx.psi(1, z) @defun def psi2(ctx, z): """Shortcut for psi(2,z) (the tetragamma function)""" return ctx.psi(2, z) @defun def psi3(ctx, z): """Shortcut for psi(3,z) (the pentagamma function)""" return ctx.psi(3, z) ''' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/functions/__init__.py0000644000175000017500000000046412014170666026512 0ustar georgeskgeorgeskfrom . import functions # Hack to update methods from . import factorials from . import hypergeometric from . import expintegrals from . import bessel from . import orthogonal from . import theta from . import elliptic from . import zeta from . import rszeta from . import zetazeros from . import qfunctions wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/ctx_iv.py0000644000175000017500000003720212014170666024237 0ustar georgeskgeorgeskimport operator from . import libmp from .libmp.backend import basestring from .libmp import ( int_types, MPZ_ONE, prec_to_dps, dps_to_prec, repr_dps, round_floor, round_ceiling, fzero, finf, fninf, fnan, mpf_le, mpf_neg, from_int, from_float, from_str, from_rational, mpi_mid, mpi_delta, mpi_str, mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub, mpi_mul, mpi_div, mpi_pow_int, mpi_pow, mpi_from_str, mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow, mpci_abs, mpci_pow, mpci_exp, mpci_log, ComplexResult) mpi_zero = (fzero, fzero) from .ctx_base import StandardBaseContext new = object.__new__ def convert_mpf_(x, prec, rounding): if hasattr(x, "_mpf_"): return x._mpf_ if isinstance(x, int_types): return from_int(x, prec, rounding) if isinstance(x, float): return from_float(x, prec, rounding) if isinstance(x, basestring): return from_str(x, prec, rounding) class ivmpf(object): """ Interval arithmetic class. Precision is controlled by iv.prec. """ def __new__(cls, x=0): return cls.ctx.convert(x) def __int__(self): a, b = self._mpi_ if a == b: return int(libmp.to_int(a)) raise ValueError @property def real(self): return self @property def imag(self): return self.ctx.zero def conjugate(self): return self @property def a(self): a, b = self._mpi_ return self.ctx.make_mpf((a, a)) @property def b(self): a, b = self._mpi_ return self.ctx.make_mpf((b, b)) @property def mid(self): ctx = self.ctx v = mpi_mid(self._mpi_, ctx.prec) return ctx.make_mpf((v, v)) @property def delta(self): ctx = self.ctx v = mpi_delta(self._mpi_, ctx.prec) return ctx.make_mpf((v,v)) @property def _mpci_(self): return self._mpi_, mpi_zero def _compare(*args): raise TypeError("no ordering relation is defined for intervals") __gt__ = _compare __le__ = _compare __gt__ = _compare __ge__ = _compare def __contains__(self, t): t = self.ctx.mpf(t) return (self.a <= t.a) and (t.b <= self.b) def __str__(self): return mpi_str(self._mpi_, self.ctx.prec) def __repr__(self): if self.ctx.pretty: return str(self) a, b = self._mpi_ n = repr_dps(self.ctx.prec) a = libmp.to_str(a, n) b = libmp.to_str(b, n) return "mpi(%r, %r)" % (a, b) def _compare(s, t, cmpfun): if not hasattr(t, "_mpi_"): try: t = s.ctx.convert(t) except: return NotImplemented return cmpfun(s._mpi_, t._mpi_) def __eq__(s, t): return s._compare(t, libmp.mpi_eq) def __ne__(s, t): return s._compare(t, libmp.mpi_ne) def __lt__(s, t): return s._compare(t, libmp.mpi_lt) def __le__(s, t): return s._compare(t, libmp.mpi_le) def __gt__(s, t): return s._compare(t, libmp.mpi_gt) def __ge__(s, t): return s._compare(t, libmp.mpi_ge) def __abs__(self): return self.ctx.make_mpf(mpi_abs(self._mpi_, self.ctx.prec)) def __pos__(self): return self.ctx.make_mpf(mpi_pos(self._mpi_, self.ctx.prec)) def __neg__(self): return self.ctx.make_mpf(mpi_neg(self._mpi_, self.ctx.prec)) def ae(s, t, rel_eps=None, abs_eps=None): return s.ctx.almosteq(s, t, rel_eps, abs_eps) class ivmpc(object): def __new__(cls, re=0, im=0): re = cls.ctx.convert(re) im = cls.ctx.convert(im) y = new(cls) y._mpci_ = re._mpi_, im._mpi_ return y def __repr__(s): if s.ctx.pretty: return str(s) return "iv.mpc(%s, %s)" % (repr(s.real), repr(s.imag)) def __str__(s): return "(%s + %s*j)" % (str(s.real), str(s.imag)) @property def a(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((a, a)) @property def b(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((b, b)) @property def c(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((c, c)) @property def d(self): (a, b), (c,d) = self._mpci_ return self.ctx.make_mpf((d, d)) @property def real(s): return s.ctx.make_mpf(s._mpci_[0]) @property def imag(s): return s.ctx.make_mpf(s._mpci_[1]) def conjugate(s): a, b = s._mpci_ return s.ctx.make_mpc((a, mpf_neg(b))) def overlap(s, t): t = s.ctx.convert(t) real_overlap = (s.a <= t.a <= s.b) or (s.a <= t.b <= s.b) or (t.a <= s.a <= t.b) or (t.a <= s.b <= t.b) imag_overlap = (s.c <= t.c <= s.d) or (s.c <= t.d <= s.d) or (t.c <= s.c <= t.d) or (t.c <= s.d <= t.d) return real_overlap and imag_overlap def __contains__(s, t): t = s.ctx.convert(t) return t.real in s.real and t.imag in s.imag def _compare(s, t, ne=False): if not isinstance(t, s.ctx._types): try: t = s.ctx.convert(t) except: return NotImplemented if hasattr(t, '_mpi_'): tval = t._mpi_, mpi_zero elif hasattr(t, '_mpci_'): tval = t._mpci_ if ne: return s._mpci_ != tval return s._mpci_ == tval def __eq__(s, t): return s._compare(t) def __ne__(s, t): return s._compare(t, True) def __lt__(s, t): raise TypeError("complex intervals cannot be ordered") __le__ = __gt__ = __ge__ = __lt__ def __neg__(s): return s.ctx.make_mpc(mpci_neg(s._mpci_, s.ctx.prec)) def __pos__(s): return s.ctx.make_mpc(mpci_pos(s._mpci_, s.ctx.prec)) def __abs__(s): return s.ctx.make_mpf(mpci_abs(s._mpci_, s.ctx.prec)) def ae(s, t, rel_eps=None, abs_eps=None): return s.ctx.almosteq(s, t, rel_eps, abs_eps) def _binary_op(f_real, f_complex): def g_complex(ctx, sval, tval): return ctx.make_mpc(f_complex(sval, tval, ctx.prec)) def g_real(ctx, sval, tval): try: return ctx.make_mpf(f_real(sval, tval, ctx.prec)) except ComplexResult: sval = (sval, mpi_zero) tval = (tval, mpi_zero) return g_complex(ctx, sval, tval) def lop_real(s, t): ctx = s.ctx if not isinstance(t, ctx._types): t = ctx.convert(t) if hasattr(t, "_mpi_"): return g_real(ctx, s._mpi_, t._mpi_) if hasattr(t, "_mpci_"): return g_complex(ctx, (s._mpi_, mpi_zero), t._mpci_) return NotImplemented def rop_real(s, t): ctx = s.ctx if not isinstance(t, ctx._types): t = ctx.convert(t) if hasattr(t, "_mpi_"): return g_real(ctx, t._mpi_, s._mpi_) if hasattr(t, "_mpci_"): return g_complex(ctx, t._mpci_, (s._mpi_, mpi_zero)) return NotImplemented def lop_complex(s, t): ctx = s.ctx if not isinstance(t, s.ctx._types): try: t = s.ctx.convert(t) except (ValueError, TypeError): return NotImplemented return g_complex(ctx, s._mpci_, t._mpci_) def rop_complex(s, t): ctx = s.ctx if not isinstance(t, s.ctx._types): t = s.ctx.convert(t) return g_complex(ctx, t._mpci_, s._mpci_) return lop_real, rop_real, lop_complex, rop_complex ivmpf.__add__, ivmpf.__radd__, ivmpc.__add__, ivmpc.__radd__ = _binary_op(mpi_add, mpci_add) ivmpf.__sub__, ivmpf.__rsub__, ivmpc.__sub__, ivmpc.__rsub__ = _binary_op(mpi_sub, mpci_sub) ivmpf.__mul__, ivmpf.__rmul__, ivmpc.__mul__, ivmpc.__rmul__ = _binary_op(mpi_mul, mpci_mul) ivmpf.__div__, ivmpf.__rdiv__, ivmpc.__div__, ivmpc.__rdiv__ = _binary_op(mpi_div, mpci_div) ivmpf.__pow__, ivmpf.__rpow__, ivmpc.__pow__, ivmpc.__rpow__ = _binary_op(mpi_pow, mpci_pow) ivmpf.__truediv__ = ivmpf.__div__; ivmpf.__rtruediv__ = ivmpf.__rdiv__ ivmpc.__truediv__ = ivmpc.__div__; ivmpc.__rtruediv__ = ivmpc.__rdiv__ class ivmpf_constant(ivmpf): def __new__(cls, f): self = new(cls) self._f = f return self def _get_mpi_(self): prec = self.ctx._prec[0] a = self._f(prec, round_floor) b = self._f(prec, round_ceiling) return a, b _mpi_ = property(_get_mpi_) class MPIntervalContext(StandardBaseContext): def __init__(ctx): ctx.mpf = type('ivmpf', (ivmpf,), {}) ctx.mpc = type('ivmpc', (ivmpc,), {}) ctx._types = (ctx.mpf, ctx.mpc) ctx._constant = type('ivmpf_constant', (ivmpf_constant,), {}) ctx._prec = [53] ctx._set_prec(53) ctx._constant._ctxdata = ctx.mpf._ctxdata = ctx.mpc._ctxdata = [ctx.mpf, new, ctx._prec] ctx._constant.ctx = ctx.mpf.ctx = ctx.mpc.ctx = ctx ctx.pretty = False StandardBaseContext.__init__(ctx) ctx._init_builtins() def _mpi(ctx, a, b=None): if b is None: return ctx.mpf(a) return ctx.mpf((a,b)) def _init_builtins(ctx): ctx.one = ctx.mpf(1) ctx.zero = ctx.mpf(0) ctx.inf = ctx.mpf('inf') ctx.ninf = -ctx.inf ctx.nan = ctx.mpf('nan') ctx.j = ctx.mpc(0,1) ctx.exp = ctx._wrap_mpi_function(libmp.mpi_exp, libmp.mpci_exp) ctx.sqrt = ctx._wrap_mpi_function(libmp.mpi_sqrt) ctx.ln = ctx._wrap_mpi_function(libmp.mpi_log, libmp.mpci_log) ctx.cos = ctx._wrap_mpi_function(libmp.mpi_cos, libmp.mpci_cos) ctx.sin = ctx._wrap_mpi_function(libmp.mpi_sin, libmp.mpci_sin) ctx.tan = ctx._wrap_mpi_function(libmp.mpi_tan) ctx.gamma = ctx._wrap_mpi_function(libmp.mpi_gamma, libmp.mpci_gamma) ctx.loggamma = ctx._wrap_mpi_function(libmp.mpi_loggamma, libmp.mpci_loggamma) ctx.rgamma = ctx._wrap_mpi_function(libmp.mpi_rgamma, libmp.mpci_rgamma) ctx.factorial = ctx._wrap_mpi_function(libmp.mpi_factorial, libmp.mpci_factorial) ctx.fac = ctx.factorial ctx.eps = ctx._constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1)) ctx.pi = ctx._constant(libmp.mpf_pi) ctx.e = ctx._constant(libmp.mpf_e) ctx.ln2 = ctx._constant(libmp.mpf_ln2) ctx.ln10 = ctx._constant(libmp.mpf_ln10) ctx.phi = ctx._constant(libmp.mpf_phi) ctx.euler = ctx._constant(libmp.mpf_euler) ctx.catalan = ctx._constant(libmp.mpf_catalan) ctx.glaisher = ctx._constant(libmp.mpf_glaisher) ctx.khinchin = ctx._constant(libmp.mpf_khinchin) ctx.twinprime = ctx._constant(libmp.mpf_twinprime) def _wrap_mpi_function(ctx, f_real, f_complex=None): def g(x, **kwargs): if kwargs: prec = kwargs.get('prec', ctx._prec[0]) else: prec = ctx._prec[0] x = ctx.convert(x) if hasattr(x, "_mpi_"): return ctx.make_mpf(f_real(x._mpi_, prec)) if hasattr(x, "_mpci_"): return ctx.make_mpc(f_complex(x._mpci_, prec)) raise ValueError return g @classmethod def _wrap_specfun(cls, name, f, wrap): if wrap: def f_wrapped(ctx, *args, **kwargs): convert = ctx.convert args = [convert(a) for a in args] prec = ctx.prec try: ctx.prec += 10 retval = f(ctx, *args, **kwargs) finally: ctx.prec = prec return +retval else: f_wrapped = f setattr(cls, name, f_wrapped) def _set_prec(ctx, n): ctx._prec[0] = max(1, int(n)) ctx._dps = prec_to_dps(n) def _set_dps(ctx, n): ctx._prec[0] = dps_to_prec(n) ctx._dps = max(1, int(n)) prec = property(lambda ctx: ctx._prec[0], _set_prec) dps = property(lambda ctx: ctx._dps, _set_dps) def make_mpf(ctx, v): a = new(ctx.mpf) a._mpi_ = v return a def make_mpc(ctx, v): a = new(ctx.mpc) a._mpci_ = v return a def _mpq(ctx, pq): p, q = pq a = libmp.from_rational(p, q, ctx.prec, round_floor) b = libmp.from_rational(p, q, ctx.prec, round_ceiling) return ctx.make_mpf((a, b)) def convert(ctx, x): if isinstance(x, (ctx.mpf, ctx.mpc)): return x if isinstance(x, ctx._constant): return +x if isinstance(x, complex) or hasattr(x, "_mpc_"): re = ctx.convert(x.real) im = ctx.convert(x.imag) return ctx.mpc(re,im) if isinstance(x, basestring): v = mpi_from_str(x, ctx.prec) return ctx.make_mpf(v) if hasattr(x, "_mpi_"): a, b = x._mpi_ else: try: a, b = x except (TypeError, ValueError): a = b = x if hasattr(a, "_mpi_"): a = a._mpi_[0] else: a = convert_mpf_(a, ctx.prec, round_floor) if hasattr(b, "_mpi_"): b = b._mpi_[1] else: b = convert_mpf_(b, ctx.prec, round_ceiling) if a == fnan or b == fnan: a = fninf b = finf assert mpf_le(a, b), "endpoints must be properly ordered" return ctx.make_mpf((a, b)) def nstr(ctx, x, n=5, **kwargs): x = ctx.convert(x) if hasattr(x, "_mpi_"): return libmp.mpi_to_str(x._mpi_, n, **kwargs) if hasattr(x, "_mpci_"): re = libmp.mpi_to_str(x._mpci_[0], n, **kwargs) im = libmp.mpi_to_str(x._mpci_[1], n, **kwargs) return "(%s + %s*j)" % (re, im) def mag(ctx, x): x = ctx.convert(x) if isinstance(x, ctx.mpc): return max(ctx.mag(x.real), ctx.mag(x.imag)) + 1 a, b = libmp.mpi_abs(x._mpi_) sign, man, exp, bc = b if man: return exp+bc if b == fzero: return ctx.ninf if b == fnan: return ctx.nan return ctx.inf def isnan(ctx, x): return False def isinf(ctx, x): return x == ctx.inf def isint(ctx, x): x = ctx.convert(x) a, b = x._mpi_ if a == b: sign, man, exp, bc = a if man: return exp >= 0 return a == fzero return None def ldexp(ctx, x, n): a, b = ctx.convert(x)._mpi_ a = libmp.mpf_shift(a, n) b = libmp.mpf_shift(b, n) return ctx.make_mpf((a,b)) def absmin(ctx, x): return abs(ctx.convert(x)).a def absmax(ctx, x): return abs(ctx.convert(x)).b def atan2(ctx, y, x): y = ctx.convert(y)._mpi_ x = ctx.convert(x)._mpi_ return ctx.make_mpf(libmp.mpi_atan2(y,x,ctx.prec)) def _convert_param(ctx, x): if isinstance(x, libmp.int_types): return x, 'Z' if isinstance(x, tuple): p, q = x return (ctx.mpf(p) / ctx.mpf(q), 'R') x = ctx.convert(x) if isinstance(x, ctx.mpf): return x, 'R' if isinstance(x, ctx.mpc): return x, 'C' raise ValueError def _is_real_type(ctx, z): return isinstance(z, ctx.mpf) or isinstance(z, int_types) def _is_complex_type(ctx, z): return isinstance(z, ctx.mpc) def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): coeffs = list(coeffs) num = range(p) den = range(p,p+q) #tol = ctx.eps s = t = ctx.one k = 0 while 1: for i in num: t *= (coeffs[i]+k) for i in den: t /= (coeffs[i]+k) k += 1; t /= k; t *= z; s += t if t == 0: return s #if abs(t) < tol: # return s if k > maxterms: raise ctx.NoConvergence wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/usertools.py0000644000175000017500000000572512014170666025007 0ustar georgeskgeorgesk def monitor(f, input='print', output='print'): """ Returns a wrapped copy of *f* that monitors evaluation by calling *input* with every input (*args*, *kwargs*) passed to *f* and *output* with every value returned from *f*. The default action (specify using the special string value ``'print'``) is to print inputs and outputs to stdout, along with the total evaluation count:: >>> from mpmath import * >>> mp.dps = 5; mp.pretty = False >>> diff(monitor(exp), 1) # diff will eval f(x-h) and f(x+h) in 0 (mpf('0.99999999906867742538452148'),) {} out 0 mpf('2.7182818259274480055282064') in 1 (mpf('1.0000000009313225746154785'),) {} out 1 mpf('2.7182818309906424675501024') mpf('2.7182808') To disable either the input or the output handler, you may pass *None* as argument. Custom input and output handlers may be used e.g. to store results for later analysis:: >>> mp.dps = 15 >>> input = [] >>> output = [] >>> findroot(monitor(sin, input.append, output.append), 3.0) mpf('3.1415926535897932') >>> len(input) # Count number of evaluations 9 >>> print(input[3]); print(output[3]) ((mpf('3.1415076583334066'),), {}) 8.49952562843408e-5 >>> print(input[4]); print(output[4]) ((mpf('3.1415928201669122'),), {}) -1.66577118985331e-7 """ if not input: input = lambda v: None elif input == 'print': incount = [0] def input(value): args, kwargs = value print("in %s %r %r" % (incount[0], args, kwargs)) incount[0] += 1 if not output: output = lambda v: None elif output == 'print': outcount = [0] def output(value): print("out %s %r" % (outcount[0], value)) outcount[0] += 1 def f_monitored(*args, **kwargs): input((args, kwargs)) v = f(*args, **kwargs) output(v) return v return f_monitored def timing(f, *args, **kwargs): """ Returns time elapsed for evaluating ``f()``. Optionally arguments may be passed to time the execution of ``f(*args, **kwargs)``. If the first call is very quick, ``f`` is called repeatedly and the best time is returned. """ once = kwargs.get('once') if 'once' in kwargs: del kwargs['once'] if args or kwargs: if len(args) == 1 and not kwargs: arg = args[0] g = lambda: f(arg) else: g = lambda: f(*args, **kwargs) else: g = f from timeit import default_timer as clock t1=clock(); v=g(); t2=clock(); t=t2-t1 if t > 0.05 or once: return t for i in range(3): t1=clock(); # Evaluate multiple times because the timer function # has a significant overhead g();g();g();g();g();g();g();g();g();g() t2=clock() t=min(t,(t2-t1)/10) return t wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/function_docs.py0000644000175000017500000103455012014170666025604 0ustar georgeskgeorgesk""" Extended docstrings for functions.py """ pi = r""" `\pi`, roughly equal to 3.141592654, represents the area of the unit circle, the half-period of trigonometric functions, and many other things in mathematics. Mpmath can evaluate `\pi` to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +pi 3.1415926535897932384626433832795028841971693993751 This shows digits 99991-100000 of `\pi`:: >>> mp.dps = 100000 >>> str(pi)[-10:] '5549362464' **Possible issues** :data:`pi` always rounds to the nearest floating-point number when used. This means that exact mathematical identities involving `\pi` will generally not be preserved in floating-point arithmetic. In particular, multiples of :data:`pi` (except for the trivial case ``0*pi``) are *not* the exact roots of :func:`~mpmath.sin`, but differ roughly by the current epsilon:: >>> mp.dps = 15 >>> sin(pi) 1.22464679914735e-16 One solution is to use the :func:`~mpmath.sinpi` function instead:: >>> sinpi(1) 0.0 See the documentation of trigonometric functions for additional details. """ degree = r""" Represents one degree of angle, `1^{\circ} = \pi/180`, or about 0.01745329. This constant may be evaluated to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +degree 0.017453292519943295769236907684886127134428718885417 The :data:`degree` object is convenient for conversion to radians:: >>> sin(30 * degree) 0.5 >>> asin(0.5) / degree 30.0 """ e = r""" The transcendental number `e` = 2.718281828... is the base of the natural logarithm (:func:`~mpmath.ln`) and of the exponential function (:func:`~mpmath.exp`). Mpmath can be evaluate `e` to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +e 2.7182818284590452353602874713526624977572470937 This shows digits 99991-100000 of `e`:: >>> mp.dps = 100000 >>> str(e)[-10:] '2100427165' **Possible issues** :data:`e` always rounds to the nearest floating-point number when used, and mathematical identities involving `e` may not hold in floating-point arithmetic. For example, ``ln(e)`` might not evaluate exactly to 1. In particular, don't use ``e**x`` to compute the exponential function. Use ``exp(x)`` instead; this is both faster and more accurate. """ phi = r""" Represents the golden ratio `\phi = (1+\sqrt 5)/2`, approximately equal to 1.6180339887. To high precision, its value is:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +phi 1.6180339887498948482045868343656381177203091798058 Formulas for the golden ratio include the following:: >>> (1+sqrt(5))/2 1.6180339887498948482045868343656381177203091798058 >>> findroot(lambda x: x**2-x-1, 1) 1.6180339887498948482045868343656381177203091798058 >>> limit(lambda n: fib(n+1)/fib(n), inf) 1.6180339887498948482045868343656381177203091798058 """ euler = r""" Euler's constant or the Euler-Mascheroni constant `\gamma` = 0.57721566... is a number of central importance to number theory and special functions. It is defined as the limit .. math :: \gamma = \lim_{n\to\infty} H_n - \log n where `H_n = 1 + \frac{1}{2} + \ldots + \frac{1}{n}` is a harmonic number (see :func:`~mpmath.harmonic`). Evaluation of `\gamma` is supported at arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +euler 0.57721566490153286060651209008240243104215933593992 We can also compute `\gamma` directly from the definition, although this is less efficient:: >>> limit(lambda n: harmonic(n)-log(n), inf) 0.57721566490153286060651209008240243104215933593992 This shows digits 9991-10000 of `\gamma`:: >>> mp.dps = 10000 >>> str(euler)[-10:] '4679858165' Integrals, series, and representations for `\gamma` in terms of special functions include the following (there are many others):: >>> mp.dps = 25 >>> -quad(lambda x: exp(-x)*log(x), [0,inf]) 0.5772156649015328606065121 >>> quad(lambda x,y: (x-1)/(1-x*y)/log(x*y), [0,1], [0,1]) 0.5772156649015328606065121 >>> nsum(lambda k: 1/k-log(1+1/k), [1,inf]) 0.5772156649015328606065121 >>> nsum(lambda k: (-1)**k*zeta(k)/k, [2,inf]) 0.5772156649015328606065121 >>> -diff(gamma, 1) 0.5772156649015328606065121 >>> limit(lambda x: 1/x-gamma(x), 0) 0.5772156649015328606065121 >>> limit(lambda x: zeta(x)-1/(x-1), 1) 0.5772156649015328606065121 >>> (log(2*pi*nprod(lambda n: ... exp(-2+2/n)*(1+2/n)**n, [1,inf]))-3)/2 0.5772156649015328606065121 For generalizations of the identities `\gamma = -\Gamma'(1)` and `\gamma = \lim_{x\to1} \zeta(x)-1/(x-1)`, see :func:`~mpmath.psi` and :func:`~mpmath.stieltjes` respectively. """ catalan = r""" Catalan's constant `K` = 0.91596559... is given by the infinite series .. math :: K = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2}. Mpmath can evaluate it to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +catalan 0.91596559417721901505460351493238411077414937428167 One can also compute `K` directly from the definition, although this is significantly less efficient:: >>> nsum(lambda k: (-1)**k/(2*k+1)**2, [0, inf]) 0.91596559417721901505460351493238411077414937428167 This shows digits 9991-10000 of `K`:: >>> mp.dps = 10000 >>> str(catalan)[-10:] '9537871503' Catalan's constant has numerous integral representations:: >>> mp.dps = 50 >>> quad(lambda x: -log(x)/(1+x**2), [0, 1]) 0.91596559417721901505460351493238411077414937428167 >>> quad(lambda x: atan(x)/x, [0, 1]) 0.91596559417721901505460351493238411077414937428167 >>> quad(lambda x: ellipk(x**2)/2, [0, 1]) 0.91596559417721901505460351493238411077414937428167 >>> quad(lambda x,y: 1/(1+(x*y)**2), [0, 1], [0, 1]) 0.91596559417721901505460351493238411077414937428167 As well as series representations:: >>> pi*log(sqrt(3)+2)/8 + 3*nsum(lambda n: ... (fac(n)/(2*n+1))**2/fac(2*n), [0, inf])/8 0.91596559417721901505460351493238411077414937428167 >>> 1-nsum(lambda n: n*zeta(2*n+1)/16**n, [1,inf]) 0.91596559417721901505460351493238411077414937428167 """ khinchin = r""" Khinchin's constant `K` = 2.68542... is a number that appears in the theory of continued fractions. Mpmath can evaluate it to arbitrary precision:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +khinchin 2.6854520010653064453097148354817956938203822939945 An integral representation is:: >>> I = quad(lambda x: log((1-x**2)/sincpi(x))/x/(1+x), [0, 1]) >>> 2*exp(1/log(2)*I) 2.6854520010653064453097148354817956938203822939945 The computation of ``khinchin`` is based on an efficient implementation of the following series:: >>> f = lambda n: (zeta(2*n)-1)/n*sum((-1)**(k+1)/mpf(k) ... for k in range(1,2*int(n))) >>> exp(nsum(f, [1,inf])/log(2)) 2.6854520010653064453097148354817956938203822939945 """ glaisher = r""" Glaisher's constant `A`, also known as the Glaisher-Kinkelin constant, is a number approximately equal to 1.282427129 that sometimes appears in formulas related to gamma and zeta functions. It is also related to the Barnes G-function (see :func:`~mpmath.barnesg`). The constant is defined as `A = \exp(1/12-\zeta'(-1))` where `\zeta'(s)` denotes the derivative of the Riemann zeta function (see :func:`~mpmath.zeta`). Mpmath can evaluate Glaisher's constant to arbitrary precision: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +glaisher 1.282427129100622636875342568869791727767688927325 We can verify that the value computed by :data:`glaisher` is correct using mpmath's facilities for numerical differentiation and arbitrary evaluation of the zeta function: >>> exp(mpf(1)/12 - diff(zeta, -1)) 1.282427129100622636875342568869791727767688927325 Here is an example of an integral that can be evaluated in terms of Glaisher's constant: >>> mp.dps = 15 >>> quad(lambda x: log(gamma(x)), [1, 1.5]) -0.0428537406502909 >>> -0.5 - 7*log(2)/24 + log(pi)/4 + 3*log(glaisher)/2 -0.042853740650291 Mpmath computes Glaisher's constant by applying Euler-Maclaurin summation to a slowly convergent series. The implementation is reasonably efficient up to about 10,000 digits. See the source code for additional details. References: http://mathworld.wolfram.com/Glaisher-KinkelinConstant.html """ apery = r""" Represents Apery's constant, which is the irrational number approximately equal to 1.2020569 given by .. math :: \zeta(3) = \sum_{k=1}^\infty\frac{1}{k^3}. The calculation is based on an efficient hypergeometric series. To 50 decimal places, the value is given by:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +apery 1.2020569031595942853997381615114499907649862923405 Other ways to evaluate Apery's constant using mpmath include:: >>> zeta(3) 1.2020569031595942853997381615114499907649862923405 >>> -psi(2,1)/2 1.2020569031595942853997381615114499907649862923405 >>> 8*nsum(lambda k: 1/(2*k+1)**3, [0,inf])/7 1.2020569031595942853997381615114499907649862923405 >>> f = lambda k: 2/k**3/(exp(2*pi*k)-1) >>> 7*pi**3/180 - nsum(f, [1,inf]) 1.2020569031595942853997381615114499907649862923405 This shows digits 9991-10000 of Apery's constant:: >>> mp.dps = 10000 >>> str(apery)[-10:] '3189504235' """ mertens = r""" Represents the Mertens or Meissel-Mertens constant, which is the prime number analog of Euler's constant: .. math :: B_1 = \lim_{N\to\infty} \left(\sum_{p_k \le N} \frac{1}{p_k} - \log \log N \right) Here `p_k` denotes the `k`-th prime number. Other names for this constant include the Hadamard-de la Vallee-Poussin constant or the prime reciprocal constant. The following gives the Mertens constant to 50 digits:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +mertens 0.2614972128476427837554268386086958590515666482612 References: http://mathworld.wolfram.com/MertensConstant.html """ twinprime = r""" Represents the twin prime constant, which is the factor `C_2` featuring in the Hardy-Littlewood conjecture for the growth of the twin prime counting function, .. math :: \pi_2(n) \sim 2 C_2 \frac{n}{\log^2 n}. It is given by the product over primes .. math :: C_2 = \prod_{p\ge3} \frac{p(p-2)}{(p-1)^2} \approx 0.66016 Computing `C_2` to 50 digits:: >>> from mpmath import * >>> mp.dps = 50; mp.pretty = True >>> +twinprime 0.66016181584686957392781211001455577843262336028473 References: http://mathworld.wolfram.com/TwinPrimesConstant.html """ ln = r""" Computes the natural logarithm of `x`, `\ln x`. See :func:`~mpmath.log` for additional documentation.""" sqrt = r""" ``sqrt(x)`` gives the principal square root of `x`, `\sqrt x`. For positive real numbers, the principal root is simply the positive square root. For arbitrary complex numbers, the principal square root is defined to satisfy `\sqrt x = \exp(\log(x)/2)`. The function thus has a branch cut along the negative half real axis. For all mpmath numbers ``x``, calling ``sqrt(x)`` is equivalent to performing ``x**0.5``. **Examples** Basic examples and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sqrt(10) 3.16227766016838 >>> sqrt(100) 10.0 >>> sqrt(-4) (0.0 + 2.0j) >>> sqrt(1+1j) (1.09868411346781 + 0.455089860562227j) >>> sqrt(inf) +inf Square root evaluation is fast at huge precision:: >>> mp.dps = 50000 >>> a = sqrt(3) >>> str(a)[-10:] '9329332814' :func:`mpmath.iv.sqrt` supports interval arguments:: >>> iv.dps = 15; iv.pretty = True >>> iv.sqrt([16,100]) [4.0, 10.0] >>> iv.sqrt(2) [1.4142135623730949234, 1.4142135623730951455] >>> iv.sqrt(2) ** 2 [1.9999999999999995559, 2.0000000000000004441] """ cbrt = r""" ``cbrt(x)`` computes the cube root of `x`, `x^{1/3}`. This function is faster and more accurate than raising to a floating-point fraction:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> 125**(mpf(1)/3) mpf('4.9999999999999991') >>> cbrt(125) mpf('5.0') Every nonzero complex number has three cube roots. This function returns the cube root defined by `\exp(\log(x)/3)` where the principal branch of the natural logarithm is used. Note that this does not give a real cube root for negative real numbers:: >>> mp.pretty = True >>> cbrt(-1) (0.5 + 0.866025403784439j) """ exp = r""" Computes the exponential function, .. math :: \exp(x) = e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}. For complex numbers, the exponential function also satisfies .. math :: \exp(x+yi) = e^x (\cos y + i \sin y). **Basic examples** Some values of the exponential function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> exp(0) 1.0 >>> exp(1) 2.718281828459045235360287 >>> exp(-1) 0.3678794411714423215955238 >>> exp(inf) +inf >>> exp(-inf) 0.0 Arguments can be arbitrarily large:: >>> exp(10000) 8.806818225662921587261496e+4342 >>> exp(-10000) 1.135483865314736098540939e-4343 Evaluation is supported for interval arguments via :func:`mpmath.iv.exp`:: >>> iv.dps = 25; iv.pretty = True >>> iv.exp([-inf,0]) [0.0, 1.0] >>> iv.exp([0,1]) [1.0, 2.71828182845904523536028749558] The exponential function can be evaluated efficiently to arbitrary precision:: >>> mp.dps = 10000 >>> exp(pi) #doctest: +ELLIPSIS 23.140692632779269005729...8984304016040616 **Functional properties** Numerical verification of Euler's identity for the complex exponential function:: >>> mp.dps = 15 >>> exp(j*pi)+1 (0.0 + 1.22464679914735e-16j) >>> chop(exp(j*pi)+1) 0.0 This recovers the coefficients (reciprocal factorials) in the Maclaurin series expansion of exp:: >>> nprint(taylor(exp, 0, 5)) [1.0, 1.0, 0.5, 0.166667, 0.0416667, 0.00833333] The exponential function is its own derivative and antiderivative:: >>> exp(pi) 23.1406926327793 >>> diff(exp, pi) 23.1406926327793 >>> quad(exp, [-inf, pi]) 23.1406926327793 The exponential function can be evaluated using various methods, including direct summation of the series, limits, and solving the defining differential equation:: >>> nsum(lambda k: pi**k/fac(k), [0,inf]) 23.1406926327793 >>> limit(lambda k: (1+pi/k)**k, inf) 23.1406926327793 >>> odefun(lambda t, x: x, 0, 1)(pi) 23.1406926327793 """ cosh = r""" Computes the hyperbolic cosine of `x`, `\cosh(x) = (e^x + e^{-x})/2`. Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> cosh(0) 1.0 >>> cosh(1) 1.543080634815243778477906 >>> cosh(-inf), cosh(+inf) (+inf, +inf) The hyperbolic cosine is an even, convex function with a global minimum at `x = 0`, having a Maclaurin series that starts:: >>> nprint(chop(taylor(cosh, 0, 5))) [1.0, 0.0, 0.5, 0.0, 0.0416667, 0.0] Generalized to complex numbers, the hyperbolic cosine is equivalent to a cosine with the argument rotated in the imaginary direction, or `\cosh x = \cos ix`:: >>> cosh(2+3j) (-3.724545504915322565473971 + 0.5118225699873846088344638j) >>> cos(3-2j) (-3.724545504915322565473971 + 0.5118225699873846088344638j) """ sinh = r""" Computes the hyperbolic sine of `x`, `\sinh(x) = (e^x - e^{-x})/2`. Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sinh(0) 0.0 >>> sinh(1) 1.175201193643801456882382 >>> sinh(-inf), sinh(+inf) (-inf, +inf) The hyperbolic sine is an odd function, with a Maclaurin series that starts:: >>> nprint(chop(taylor(sinh, 0, 5))) [0.0, 1.0, 0.0, 0.166667, 0.0, 0.00833333] Generalized to complex numbers, the hyperbolic sine is essentially a sine with a rotation `i` applied to the argument; more precisely, `\sinh x = -i \sin ix`:: >>> sinh(2+3j) (-3.590564589985779952012565 + 0.5309210862485198052670401j) >>> j*sin(3-2j) (-3.590564589985779952012565 + 0.5309210862485198052670401j) """ tanh = r""" Computes the hyperbolic tangent of `x`, `\tanh(x) = \sinh(x)/\cosh(x)`. Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> tanh(0) 0.0 >>> tanh(1) 0.7615941559557648881194583 >>> tanh(-inf), tanh(inf) (-1.0, 1.0) The hyperbolic tangent is an odd, sigmoidal function, similar to the inverse tangent and error function. Its Maclaurin series is:: >>> nprint(chop(taylor(tanh, 0, 5))) [0.0, 1.0, 0.0, -0.333333, 0.0, 0.133333] Generalized to complex numbers, the hyperbolic tangent is essentially a tangent with a rotation `i` applied to the argument; more precisely, `\tanh x = -i \tan ix`:: >>> tanh(2+3j) (0.9653858790221331242784803 - 0.009884375038322493720314034j) >>> j*tan(3-2j) (0.9653858790221331242784803 - 0.009884375038322493720314034j) """ cos = r""" Computes the cosine of `x`, `\cos(x)`. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> cos(pi/3) 0.5 >>> cos(100000001) -0.9802850113244713353133243 >>> cos(2+3j) (-4.189625690968807230132555 - 9.109227893755336597979197j) >>> cos(inf) nan >>> nprint(chop(taylor(cos, 0, 6))) [1.0, 0.0, -0.5, 0.0, 0.0416667, 0.0, -0.00138889] Intervals are supported via :func:`mpmath.iv.cos`:: >>> iv.dps = 25; iv.pretty = True >>> iv.cos([0,1]) [0.540302305868139717400936602301, 1.0] >>> iv.cos([0,2]) [-0.41614683654714238699756823214, 1.0] """ sin = r""" Computes the sine of `x`, `\sin(x)`. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sin(pi/3) 0.8660254037844386467637232 >>> sin(100000001) 0.1975887055794968911438743 >>> sin(2+3j) (9.1544991469114295734673 - 4.168906959966564350754813j) >>> sin(inf) nan >>> nprint(chop(taylor(sin, 0, 6))) [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333, 0.0] Intervals are supported via :func:`mpmath.iv.sin`:: >>> iv.dps = 25; iv.pretty = True >>> iv.sin([0,1]) [0.0, 0.841470984807896506652502331201] >>> iv.sin([0,2]) [0.0, 1.0] """ tan = r""" Computes the tangent of `x`, `\tan(x) = \frac{\sin(x)}{\cos(x)}`. The tangent function is singular at `x = (n+1/2)\pi`, but ``tan(x)`` always returns a finite result since `(n+1/2)\pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> tan(pi/3) 1.732050807568877293527446 >>> tan(100000001) -0.2015625081449864533091058 >>> tan(2+3j) (-0.003764025641504248292751221 + 1.003238627353609801446359j) >>> tan(inf) nan >>> nprint(chop(taylor(tan, 0, 6))) [0.0, 1.0, 0.0, 0.333333, 0.0, 0.133333, 0.0] Intervals are supported via :func:`mpmath.iv.tan`:: >>> iv.dps = 25; iv.pretty = True >>> iv.tan([0,1]) [0.0, 1.55740772465490223050697482944] >>> iv.tan([0,2]) # Interval includes a singularity [-inf, +inf] """ sec = r""" Computes the secant of `x`, `\mathrm{sec}(x) = \frac{1}{\cos(x)}`. The secant function is singular at `x = (n+1/2)\pi`, but ``sec(x)`` always returns a finite result since `(n+1/2)\pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> sec(pi/3) 2.0 >>> sec(10000001) -1.184723164360392819100265 >>> sec(2+3j) (-0.04167496441114427004834991 + 0.0906111371962375965296612j) >>> sec(inf) nan >>> nprint(chop(taylor(sec, 0, 6))) [1.0, 0.0, 0.5, 0.0, 0.208333, 0.0, 0.0847222] Intervals are supported via :func:`mpmath.iv.sec`:: >>> iv.dps = 25; iv.pretty = True >>> iv.sec([0,1]) [1.0, 1.85081571768092561791175326276] >>> iv.sec([0,2]) # Interval includes a singularity [-inf, +inf] """ csc = r""" Computes the cosecant of `x`, `\mathrm{csc}(x) = \frac{1}{\sin(x)}`. This cosecant function is singular at `x = n \pi`, but with the exception of the point `x = 0`, ``csc(x)`` returns a finite result since `n \pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> csc(pi/3) 1.154700538379251529018298 >>> csc(10000001) -1.864910497503629858938891 >>> csc(2+3j) (0.09047320975320743980579048 + 0.04120098628857412646300981j) >>> csc(inf) nan Intervals are supported via :func:`mpmath.iv.csc`:: >>> iv.dps = 25; iv.pretty = True >>> iv.csc([0,1]) # Interval includes a singularity [1.18839510577812121626159943988, +inf] >>> iv.csc([0,2]) [1.0, +inf] """ cot = r""" Computes the cotangent of `x`, `\mathrm{cot}(x) = \frac{1}{\tan(x)} = \frac{\cos(x)}{\sin(x)}`. This cotangent function is singular at `x = n \pi`, but with the exception of the point `x = 0`, ``cot(x)`` returns a finite result since `n \pi` cannot be represented exactly using floating-point arithmetic. >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> cot(pi/3) 0.5773502691896257645091488 >>> cot(10000001) 1.574131876209625656003562 >>> cot(2+3j) (-0.003739710376336956660117409 - 0.9967577965693583104609688j) >>> cot(inf) nan Intervals are supported via :func:`mpmath.iv.cot`:: >>> iv.dps = 25; iv.pretty = True >>> iv.cot([0,1]) # Interval includes a singularity [0.642092615934330703006419974862, +inf] >>> iv.cot([1,2]) [-inf, +inf] """ acos = r""" Computes the inverse cosine or arccosine of `x`, `\cos^{-1}(x)`. Since `-1 \le \cos(x) \le 1` for real `x`, the inverse cosine is real-valued only for `-1 \le x \le 1`. On this interval, :func:`~mpmath.acos` is defined to be a monotonically decreasing function assuming values between `+\pi` and `0`. Basic values are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> acos(-1) 3.141592653589793238462643 >>> acos(0) 1.570796326794896619231322 >>> acos(1) 0.0 >>> nprint(chop(taylor(acos, 0, 6))) [1.5708, -1.0, 0.0, -0.166667, 0.0, -0.075, 0.0] :func:`~mpmath.acos` is defined so as to be a proper inverse function of `\cos(\theta)` for `0 \le \theta < \pi`. We have `\cos(\cos^{-1}(x)) = x` for all `x`, but `\cos^{-1}(\cos(x)) = x` only for `0 \le \Re[x] < \pi`:: >>> for x in [1, 10, -1, 2+3j, 10+3j]: ... print("%s %s" % (cos(acos(x)), acos(cos(x)))) ... 1.0 1.0 (10.0 + 0.0j) 2.566370614359172953850574 -1.0 1.0 (2.0 + 3.0j) (2.0 + 3.0j) (10.0 + 3.0j) (2.566370614359172953850574 - 3.0j) The inverse cosine has two branch points: `x = \pm 1`. :func:`~mpmath.acos` places the branch cuts along the line segments `(-\infty, -1)` and `(+1, +\infty)`. In general, .. math :: \cos^{-1}(x) = \frac{\pi}{2} + i \log\left(ix + \sqrt{1-x^2} \right) where the principal-branch log and square root are implied. """ asin = r""" Computes the inverse sine or arcsine of `x`, `\sin^{-1}(x)`. Since `-1 \le \sin(x) \le 1` for real `x`, the inverse sine is real-valued only for `-1 \le x \le 1`. On this interval, it is defined to be a monotonically increasing function assuming values between `-\pi/2` and `\pi/2`. Basic values are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> asin(-1) -1.570796326794896619231322 >>> asin(0) 0.0 >>> asin(1) 1.570796326794896619231322 >>> nprint(chop(taylor(asin, 0, 6))) [0.0, 1.0, 0.0, 0.166667, 0.0, 0.075, 0.0] :func:`~mpmath.asin` is defined so as to be a proper inverse function of `\sin(\theta)` for `-\pi/2 < \theta < \pi/2`. We have `\sin(\sin^{-1}(x)) = x` for all `x`, but `\sin^{-1}(\sin(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: >>> for x in [1, 10, -1, 1+3j, -2+3j]: ... print("%s %s" % (chop(sin(asin(x))), asin(sin(x)))) ... 1.0 1.0 10.0 -0.5752220392306202846120698 -1.0 -1.0 (1.0 + 3.0j) (1.0 + 3.0j) (-2.0 + 3.0j) (-1.141592653589793238462643 - 3.0j) The inverse sine has two branch points: `x = \pm 1`. :func:`~mpmath.asin` places the branch cuts along the line segments `(-\infty, -1)` and `(+1, +\infty)`. In general, .. math :: \sin^{-1}(x) = -i \log\left(ix + \sqrt{1-x^2} \right) where the principal-branch log and square root are implied. """ atan = r""" Computes the inverse tangent or arctangent of `x`, `\tan^{-1}(x)`. This is a real-valued function for all real `x`, with range `(-\pi/2, \pi/2)`. Basic values are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> atan(-inf) -1.570796326794896619231322 >>> atan(-1) -0.7853981633974483096156609 >>> atan(0) 0.0 >>> atan(1) 0.7853981633974483096156609 >>> atan(inf) 1.570796326794896619231322 >>> nprint(chop(taylor(atan, 0, 6))) [0.0, 1.0, 0.0, -0.333333, 0.0, 0.2, 0.0] The inverse tangent is often used to compute angles. However, the atan2 function is often better for this as it preserves sign (see :func:`~mpmath.atan2`). :func:`~mpmath.atan` is defined so as to be a proper inverse function of `\tan(\theta)` for `-\pi/2 < \theta < \pi/2`. We have `\tan(\tan^{-1}(x)) = x` for all `x`, but `\tan^{-1}(\tan(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: >>> mp.dps = 25 >>> for x in [1, 10, -1, 1+3j, -2+3j]: ... print("%s %s" % (tan(atan(x)), atan(tan(x)))) ... 1.0 1.0 10.0 0.5752220392306202846120698 -1.0 -1.0 (1.0 + 3.0j) (1.000000000000000000000001 + 3.0j) (-2.0 + 3.0j) (1.141592653589793238462644 + 3.0j) The inverse tangent has two branch points: `x = \pm i`. :func:`~mpmath.atan` places the branch cuts along the line segments `(-i \infty, -i)` and `(+i, +i \infty)`. In general, .. math :: \tan^{-1}(x) = \frac{i}{2}\left(\log(1-ix)-\log(1+ix)\right) where the principal-branch log is implied. """ acot = r"""Computes the inverse cotangent of `x`, `\mathrm{cot}^{-1}(x) = \tan^{-1}(1/x)`.""" asec = r"""Computes the inverse secant of `x`, `\mathrm{sec}^{-1}(x) = \cos^{-1}(1/x)`.""" acsc = r"""Computes the inverse cosecant of `x`, `\mathrm{csc}^{-1}(x) = \sin^{-1}(1/x)`.""" coth = r"""Computes the hyperbolic cotangent of `x`, `\mathrm{coth}(x) = \frac{\cosh(x)}{\sinh(x)}`. """ sech = r"""Computes the hyperbolic secant of `x`, `\mathrm{sech}(x) = \frac{1}{\cosh(x)}`. """ csch = r"""Computes the hyperbolic cosecant of `x`, `\mathrm{csch}(x) = \frac{1}{\sinh(x)}`. """ acosh = r"""Computes the inverse hyperbolic cosine of `x`, `\mathrm{cosh}^{-1}(x) = \log(x+\sqrt{x+1}\sqrt{x-1})`. """ asinh = r"""Computes the inverse hyperbolic sine of `x`, `\mathrm{sinh}^{-1}(x) = \log(x+\sqrt{1+x^2})`. """ atanh = r"""Computes the inverse hyperbolic tangent of `x`, `\mathrm{tanh}^{-1}(x) = \frac{1}{2}\left(\log(1+x)-\log(1-x)\right)`. """ acoth = r"""Computes the inverse hyperbolic cotangent of `x`, `\mathrm{coth}^{-1}(x) = \tanh^{-1}(1/x)`.""" asech = r"""Computes the inverse hyperbolic secant of `x`, `\mathrm{sech}^{-1}(x) = \cosh^{-1}(1/x)`.""" acsch = r"""Computes the inverse hyperbolic cosecant of `x`, `\mathrm{csch}^{-1}(x) = \sinh^{-1}(1/x)`.""" sinpi = r""" Computes `\sin(\pi x)`, more accurately than the expression ``sin(pi*x)``:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sinpi(10**10), sin(pi*(10**10)) (0.0, -2.23936276195592e-6) >>> sinpi(10**10+0.5), sin(pi*(10**10+0.5)) (1.0, 0.999999999998721) """ cospi = r""" Computes `\cos(\pi x)`, more accurately than the expression ``cos(pi*x)``:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> cospi(10**10), cos(pi*(10**10)) (1.0, 0.999999999997493) >>> cospi(10**10+0.5), cos(pi*(10**10+0.5)) (0.0, 1.59960492420134e-6) """ sinc = r""" ``sinc(x)`` computes the unnormalized sinc function, defined as .. math :: \mathrm{sinc}(x) = \begin{cases} \sin(x)/x, & \mbox{if } x \ne 0 \\ 1, & \mbox{if } x = 0. \end{cases} See :func:`~mpmath.sincpi` for the normalized sinc function. Simple values and limits include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> sinc(0) 1.0 >>> sinc(1) 0.841470984807897 >>> sinc(inf) 0.0 The integral of the sinc function is the sine integral Si:: >>> quad(sinc, [0, 1]) 0.946083070367183 >>> si(1) 0.946083070367183 """ sincpi = r""" ``sincpi(x)`` computes the normalized sinc function, defined as .. math :: \mathrm{sinc}_{\pi}(x) = \begin{cases} \sin(\pi x)/(\pi x), & \mbox{if } x \ne 0 \\ 1, & \mbox{if } x = 0. \end{cases} Equivalently, we have `\mathrm{sinc}_{\pi}(x) = \mathrm{sinc}(\pi x)`. The normalization entails that the function integrates to unity over the entire real line:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> quadosc(sincpi, [-inf, inf], period=2.0) 1.0 Like, :func:`~mpmath.sinpi`, :func:`~mpmath.sincpi` is evaluated accurately at its roots:: >>> sincpi(10) 0.0 """ expj = r""" Convenience function for computing `e^{ix}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> expj(0) (1.0 + 0.0j) >>> expj(-1) (0.5403023058681397174009366 - 0.8414709848078965066525023j) >>> expj(j) (0.3678794411714423215955238 + 0.0j) >>> expj(1+j) (0.1987661103464129406288032 + 0.3095598756531121984439128j) """ expjpi = r""" Convenience function for computing `e^{i \pi x}`. Evaluation is accurate near zeros (see also :func:`~mpmath.cospi`, :func:`~mpmath.sinpi`):: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> expjpi(0) (1.0 + 0.0j) >>> expjpi(1) (-1.0 + 0.0j) >>> expjpi(0.5) (0.0 + 1.0j) >>> expjpi(-1) (-1.0 + 0.0j) >>> expjpi(j) (0.04321391826377224977441774 + 0.0j) >>> expjpi(1+j) (-0.04321391826377224977441774 + 0.0j) """ floor = r""" Computes the floor of `x`, `\lfloor x \rfloor`, defined as the largest integer less than or equal to `x`:: >>> from mpmath import * >>> mp.pretty = False >>> floor(3.5) mpf('3.0') .. note :: :func:`~mpmath.floor`, :func:`~mpmath.ceil` and :func:`~mpmath.nint` return a floating-point number, not a Python ``int``. If `\lfloor x \rfloor` is too large to be represented exactly at the present working precision, the result will be rounded, not necessarily in the direction implied by the mathematical definition of the function. To avoid rounding, use *prec=0*:: >>> mp.dps = 15 >>> print(int(floor(10**30+1))) 1000000000000000019884624838656 >>> print(int(floor(10**30+1, prec=0))) 1000000000000000000000000000001 The floor function is defined for complex numbers and acts on the real and imaginary parts separately:: >>> floor(3.25+4.75j) mpc(real='3.0', imag='4.0') """ ceil = r""" Computes the ceiling of `x`, `\lceil x \rceil`, defined as the smallest integer greater than or equal to `x`:: >>> from mpmath import * >>> mp.pretty = False >>> ceil(3.5) mpf('4.0') The ceiling function is defined for complex numbers and acts on the real and imaginary parts separately:: >>> ceil(3.25+4.75j) mpc(real='4.0', imag='5.0') See notes about rounding for :func:`~mpmath.floor`. """ nint = r""" Evaluates the nearest integer function, `\mathrm{nint}(x)`. This gives the nearest integer to `x`; on a tie, it gives the nearest even integer:: >>> from mpmath import * >>> mp.pretty = False >>> nint(3.2) mpf('3.0') >>> nint(3.8) mpf('4.0') >>> nint(3.5) mpf('4.0') >>> nint(4.5) mpf('4.0') The nearest integer function is defined for complex numbers and acts on the real and imaginary parts separately:: >>> nint(3.25+4.75j) mpc(real='3.0', imag='5.0') See notes about rounding for :func:`~mpmath.floor`. """ frac = r""" Gives the fractional part of `x`, defined as `\mathrm{frac}(x) = x - \lfloor x \rfloor` (see :func:`~mpmath.floor`). In effect, this computes `x` modulo 1, or `x+n` where `n \in \mathbb{Z}` is such that `x+n \in [0,1)`:: >>> from mpmath import * >>> mp.pretty = False >>> frac(1.25) mpf('0.25') >>> frac(3) mpf('0.0') >>> frac(-1.25) mpf('0.75') For a complex number, the fractional part function applies to the real and imaginary parts separately:: >>> frac(2.25+3.75j) mpc(real='0.25', imag='0.75') Plotted, the fractional part function gives a sawtooth wave. The Fourier series coefficients have a simple form:: >>> mp.dps = 15 >>> nprint(fourier(lambda x: frac(x)-0.5, [0,1], 4)) ([0.0, 0.0, 0.0, 0.0, 0.0], [0.0, -0.31831, -0.159155, -0.106103, -0.0795775]) >>> nprint([-1/(pi*k) for k in range(1,5)]) [-0.31831, -0.159155, -0.106103, -0.0795775] .. note:: The fractional part is sometimes defined as a symmetric function, i.e. returning `-\mathrm{frac}(-x)` if `x < 0`. This convention is used, for instance, by Mathematica's ``FractionalPart``. """ sign = r""" Returns the sign of `x`, defined as `\mathrm{sign}(x) = x / |x|` (with the special case `\mathrm{sign}(0) = 0`):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> sign(10) mpf('1.0') >>> sign(-10) mpf('-1.0') >>> sign(0) mpf('0.0') Note that the sign function is also defined for complex numbers, for which it gives the projection onto the unit circle:: >>> mp.dps = 15; mp.pretty = True >>> sign(1+j) (0.707106781186547 + 0.707106781186547j) """ arg = r""" Computes the complex argument (phase) of `x`, defined as the signed angle between the positive real axis and `x` in the complex plane:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> arg(3) 0.0 >>> arg(3+3j) 0.785398163397448 >>> arg(3j) 1.5707963267949 >>> arg(-3) 3.14159265358979 >>> arg(-3j) -1.5707963267949 The angle is defined to satisfy `-\pi < \arg(x) \le \pi` and with the sign convention that a nonnegative imaginary part results in a nonnegative argument. The value returned by :func:`~mpmath.arg` is an ``mpf`` instance. """ fabs = r""" Returns the absolute value of `x`, `|x|`. Unlike :func:`abs`, :func:`~mpmath.fabs` converts non-mpmath numbers (such as ``int``) into mpmath numbers:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> fabs(3) mpf('3.0') >>> fabs(-3) mpf('3.0') >>> fabs(3+4j) mpf('5.0') """ re = r""" Returns the real part of `x`, `\Re(x)`. Unlike ``x.real``, :func:`~mpmath.re` converts `x` to a mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> re(3) mpf('3.0') >>> re(-1+4j) mpf('-1.0') """ im = r""" Returns the imaginary part of `x`, `\Im(x)`. Unlike ``x.imag``, :func:`~mpmath.im` converts `x` to a mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> im(3) mpf('0.0') >>> im(-1+4j) mpf('4.0') """ conj = r""" Returns the complex conjugate of `x`, `\overline{x}`. Unlike ``x.conjugate()``, :func:`~mpmath.im` converts `x` to a mpmath number:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> conj(3) mpf('3.0') >>> conj(-1+4j) mpc(real='-1.0', imag='-4.0') """ polar = r""" Returns the polar representation of the complex number `z` as a pair `(r, \phi)` such that `z = r e^{i \phi}`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> polar(-2) (2.0, 3.14159265358979) >>> polar(3-4j) (5.0, -0.927295218001612) """ rect = r""" Returns the complex number represented by polar coordinates `(r, \phi)`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> chop(rect(2, pi)) -2.0 >>> rect(sqrt(2), -pi/4) (1.0 - 1.0j) """ expm1 = r""" Computes `e^x - 1`, accurately for small `x`. Unlike the expression ``exp(x) - 1``, ``expm1(x)`` does not suffer from potentially catastrophic cancellation:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> exp(1e-10)-1; print(expm1(1e-10)) 1.00000008274037e-10 1.00000000005e-10 >>> exp(1e-20)-1; print(expm1(1e-20)) 0.0 1.0e-20 >>> 1/(exp(1e-20)-1) Traceback (most recent call last): ... ZeroDivisionError >>> 1/expm1(1e-20) 1.0e+20 Evaluation works for extremely tiny values:: >>> expm1(0) 0.0 >>> expm1('1e-10000000') 1.0e-10000000 """ powm1 = r""" Computes `x^y - 1`, accurately when `x^y` is very close to 1. This avoids potentially catastrophic cancellation:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> power(0.99999995, 1e-10) - 1 0.0 >>> powm1(0.99999995, 1e-10) -5.00000012791934e-18 Powers exactly equal to 1, and only those powers, yield 0 exactly:: >>> powm1(-j, 4) (0.0 + 0.0j) >>> powm1(3, 0) 0.0 >>> powm1(fadd(-1, 1e-100, exact=True), 4) -4.0e-100 Evaluation works for extremely tiny `y`:: >>> powm1(2, '1e-100000') 6.93147180559945e-100001 >>> powm1(j, '1e-1000') (-1.23370055013617e-2000 + 1.5707963267949e-1000j) """ root = r""" ``root(z, n, k=0)`` computes an `n`-th root of `z`, i.e. returns a number `r` that (up to possible approximation error) satisfies `r^n = z`. (``nthroot`` is available as an alias for ``root``.) Every complex number `z \ne 0` has `n` distinct `n`-th roots, which are equidistant points on a circle with radius `|z|^{1/n}`, centered around the origin. A specific root may be selected using the optional index `k`. The roots are indexed counterclockwise, starting with `k = 0` for the root closest to the positive real half-axis. The `k = 0` root is the so-called principal `n`-th root, often denoted by `\sqrt[n]{z}` or `z^{1/n}`, and also given by `\exp(\log(z) / n)`. If `z` is a positive real number, the principal root is just the unique positive `n`-th root of `z`. Under some circumstances, non-principal real roots exist: for positive real `z`, `n` even, there is a negative root given by `k = n/2`; for negative real `z`, `n` odd, there is a negative root given by `k = (n-1)/2`. To obtain all roots with a simple expression, use ``[root(z,n,k) for k in range(n)]``. An important special case, ``root(1, n, k)`` returns the `k`-th `n`-th root of unity, `\zeta_k = e^{2 \pi i k / n}`. Alternatively, :func:`~mpmath.unitroots` provides a slightly more convenient way to obtain the roots of unity, including the option to compute only the primitive roots of unity. Both `k` and `n` should be integers; `k` outside of ``range(n)`` will be reduced modulo `n`. If `n` is negative, `x^{-1/n} = 1/x^{1/n}` (or the equivalent reciprocal for a non-principal root with `k \ne 0`) is computed. :func:`~mpmath.root` is implemented to use Newton's method for small `n`. At high precision, this makes `x^{1/n}` not much more expensive than the regular exponentiation, `x^n`. For very large `n`, :func:`~mpmath.nthroot` falls back to use the exponential function. **Examples** :func:`~mpmath.nthroot`/:func:`~mpmath.root` is faster and more accurate than raising to a floating-point fraction:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = False >>> 16807 ** (mpf(1)/5) mpf('7.0000000000000009') >>> root(16807, 5) mpf('7.0') >>> nthroot(16807, 5) # Alias mpf('7.0') A high-precision root:: >>> mp.dps = 50; mp.pretty = True >>> nthroot(10, 5) 1.584893192461113485202101373391507013269442133825 >>> nthroot(10, 5) ** 5 10.0 Computing principal and non-principal square and cube roots:: >>> mp.dps = 15 >>> root(10, 2) 3.16227766016838 >>> root(10, 2, 1) -3.16227766016838 >>> root(-10, 3) (1.07721734501594 + 1.86579517236206j) >>> root(-10, 3, 1) -2.15443469003188 >>> root(-10, 3, 2) (1.07721734501594 - 1.86579517236206j) All the 7th roots of a complex number:: >>> for r in [root(3+4j, 7, k) for k in range(7)]: ... print("%s %s" % (r, r**7)) ... (1.24747270589553 + 0.166227124177353j) (3.0 + 4.0j) (0.647824911301003 + 1.07895435170559j) (3.0 + 4.0j) (-0.439648254723098 + 1.17920694574172j) (3.0 + 4.0j) (-1.19605731775069 + 0.391492658196305j) (3.0 + 4.0j) (-1.05181082538903 - 0.691023585965793j) (3.0 + 4.0j) (-0.115529328478668 - 1.25318497558335j) (3.0 + 4.0j) (0.907748109144957 - 0.871672518271819j) (3.0 + 4.0j) Cube roots of unity:: >>> for k in range(3): print(root(1, 3, k)) ... 1.0 (-0.5 + 0.866025403784439j) (-0.5 - 0.866025403784439j) Some exact high order roots:: >>> root(75**210, 105) 5625.0 >>> root(1, 128, 96) (0.0 - 1.0j) >>> root(4**128, 128, 96) (0.0 - 4.0j) """ unitroots = r""" ``unitroots(n)`` returns `\zeta_0, \zeta_1, \ldots, \zeta_{n-1}`, all the distinct `n`-th roots of unity, as a list. If the option *primitive=True* is passed, only the primitive roots are returned. Every `n`-th root of unity satisfies `(\zeta_k)^n = 1`. There are `n` distinct roots for each `n` (`\zeta_k` and `\zeta_j` are the same when `k = j \pmod n`), which form a regular polygon with vertices on the unit circle. They are ordered counterclockwise with increasing `k`, starting with `\zeta_0 = 1`. **Examples** The roots of unity up to `n = 4`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint(unitroots(1)) [1.0] >>> nprint(unitroots(2)) [1.0, -1.0] >>> nprint(unitroots(3)) [1.0, (-0.5 + 0.866025j), (-0.5 - 0.866025j)] >>> nprint(unitroots(4)) [1.0, (0.0 + 1.0j), -1.0, (0.0 - 1.0j)] Roots of unity form a geometric series that sums to 0:: >>> mp.dps = 50 >>> chop(fsum(unitroots(25))) 0.0 Primitive roots up to `n = 4`:: >>> mp.dps = 15 >>> nprint(unitroots(1, primitive=True)) [1.0] >>> nprint(unitroots(2, primitive=True)) [-1.0] >>> nprint(unitroots(3, primitive=True)) [(-0.5 + 0.866025j), (-0.5 - 0.866025j)] >>> nprint(unitroots(4, primitive=True)) [(0.0 + 1.0j), (0.0 - 1.0j)] There are only four primitive 12th roots:: >>> nprint(unitroots(12, primitive=True)) [(0.866025 + 0.5j), (-0.866025 + 0.5j), (-0.866025 - 0.5j), (0.866025 - 0.5j)] The `n`-th roots of unity form a group, the cyclic group of order `n`. Any primitive root `r` is a generator for this group, meaning that `r^0, r^1, \ldots, r^{n-1}` gives the whole set of unit roots (in some permuted order):: >>> for r in unitroots(6): print(r) ... 1.0 (0.5 + 0.866025403784439j) (-0.5 + 0.866025403784439j) -1.0 (-0.5 - 0.866025403784439j) (0.5 - 0.866025403784439j) >>> r = unitroots(6, primitive=True)[1] >>> for k in range(6): print(chop(r**k)) ... 1.0 (0.5 - 0.866025403784439j) (-0.5 - 0.866025403784439j) -1.0 (-0.5 + 0.866025403784438j) (0.5 + 0.866025403784438j) The number of primitive roots equals the Euler totient function `\phi(n)`:: >>> [len(unitroots(n, primitive=True)) for n in range(1,20)] [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18] """ log = r""" Computes the base-`b` logarithm of `x`, `\log_b(x)`. If `b` is unspecified, :func:`~mpmath.log` computes the natural (base `e`) logarithm and is equivalent to :func:`~mpmath.ln`. In general, the base `b` logarithm is defined in terms of the natural logarithm as `\log_b(x) = \ln(x)/\ln(b)`. By convention, we take `\log(0) = -\infty`. The natural logarithm is real if `x > 0` and complex if `x < 0` or if `x` is complex. The principal branch of the complex logarithm is used, meaning that `\Im(\ln(x)) = -\pi < \arg(x) \le \pi`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> log(1) 0.0 >>> log(2) 0.693147180559945 >>> log(1000,10) 3.0 >>> log(4, 16) 0.5 >>> log(j) (0.0 + 1.5707963267949j) >>> log(-1) (0.0 + 3.14159265358979j) >>> log(0) -inf >>> log(inf) +inf The natural logarithm is the antiderivative of `1/x`:: >>> quad(lambda x: 1/x, [1, 5]) 1.6094379124341 >>> log(5) 1.6094379124341 >>> diff(log, 10) 0.1 The Taylor series expansion of the natural logarithm around `x = 1` has coefficients `(-1)^{n+1}/n`:: >>> nprint(taylor(log, 1, 7)) [0.0, 1.0, -0.5, 0.333333, -0.25, 0.2, -0.166667, 0.142857] :func:`~mpmath.log` supports arbitrary precision evaluation:: >>> mp.dps = 50 >>> log(pi) 1.1447298858494001741434273513530587116472948129153 >>> log(pi, pi**3) 0.33333333333333333333333333333333333333333333333333 >>> mp.dps = 25 >>> log(3+4j) (1.609437912434100374600759 + 0.9272952180016122324285125j) """ log10 = r""" Computes the base-10 logarithm of `x`, `\log_{10}(x)`. ``log10(x)`` is equivalent to ``log(x, 10)``. """ fmod = r""" Converts `x` and `y` to mpmath numbers and returns `x \mod y`. For mpmath numbers, this is equivalent to ``x % y``. >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> fmod(100, pi) 2.61062773871641 You can use :func:`~mpmath.fmod` to compute fractional parts of numbers:: >>> fmod(10.25, 1) 0.25 """ radians = r""" Converts the degree angle `x` to radians:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> radians(60) 1.0471975511966 """ degrees = r""" Converts the radian angle `x` to a degree angle:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> degrees(pi/3) 60.0 """ atan2 = r""" Computes the two-argument arctangent, `\mathrm{atan2}(y, x)`, giving the signed angle between the positive `x`-axis and the point `(x, y)` in the 2D plane. This function is defined for real `x` and `y` only. The two-argument arctangent essentially computes `\mathrm{atan}(y/x)`, but accounts for the signs of both `x` and `y` to give the angle for the correct quadrant. The following examples illustrate the difference:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> atan2(1,1), atan(1/1.) (0.785398163397448, 0.785398163397448) >>> atan2(1,-1), atan(1/-1.) (2.35619449019234, -0.785398163397448) >>> atan2(-1,1), atan(-1/1.) (-0.785398163397448, -0.785398163397448) >>> atan2(-1,-1), atan(-1/-1.) (-2.35619449019234, 0.785398163397448) The angle convention is the same as that used for the complex argument; see :func:`~mpmath.arg`. """ fibonacci = r""" ``fibonacci(n)`` computes the `n`-th Fibonacci number, `F(n)`. The Fibonacci numbers are defined by the recurrence `F(n) = F(n-1) + F(n-2)` with the initial values `F(0) = 0`, `F(1) = 1`. :func:`~mpmath.fibonacci` extends this definition to arbitrary real and complex arguments using the formula .. math :: F(z) = \frac{\phi^z - \cos(\pi z) \phi^{-z}}{\sqrt 5} where `\phi` is the golden ratio. :func:`~mpmath.fibonacci` also uses this continuous formula to compute `F(n)` for extremely large `n`, where calculating the exact integer would be wasteful. For convenience, :func:`~mpmath.fib` is available as an alias for :func:`~mpmath.fibonacci`. **Basic examples** Some small Fibonacci numbers are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for i in range(10): ... print(fibonacci(i)) ... 0.0 1.0 1.0 2.0 3.0 5.0 8.0 13.0 21.0 34.0 >>> fibonacci(50) 12586269025.0 The recurrence for `F(n)` extends backwards to negative `n`:: >>> for i in range(10): ... print(fibonacci(-i)) ... 0.0 1.0 -1.0 2.0 -3.0 5.0 -8.0 13.0 -21.0 34.0 Large Fibonacci numbers will be computed approximately unless the precision is set high enough:: >>> fib(200) 2.8057117299251e+41 >>> mp.dps = 45 >>> fib(200) 280571172992510140037611932413038677189525.0 :func:`~mpmath.fibonacci` can compute approximate Fibonacci numbers of stupendous size:: >>> mp.dps = 15 >>> fibonacci(10**25) 3.49052338550226e+2089876402499787337692720 **Real and complex arguments** The extended Fibonacci function is an analytic function. The property `F(z) = F(z-1) + F(z-2)` holds for arbitrary `z`:: >>> mp.dps = 15 >>> fib(pi) 2.1170270579161 >>> fib(pi-1) + fib(pi-2) 2.1170270579161 >>> fib(3+4j) (-5248.51130728372 - 14195.962288353j) >>> fib(2+4j) + fib(1+4j) (-5248.51130728372 - 14195.962288353j) The Fibonacci function has infinitely many roots on the negative half-real axis. The first root is at 0, the second is close to -0.18, and then there are infinitely many roots that asymptotically approach `-n+1/2`:: >>> findroot(fib, -0.2) -0.183802359692956 >>> findroot(fib, -2) -1.57077646820395 >>> findroot(fib, -17) -16.4999999596115 >>> findroot(fib, -24) -23.5000000000479 **Mathematical relationships** For large `n`, `F(n+1)/F(n)` approaches the golden ratio:: >>> mp.dps = 50 >>> fibonacci(101)/fibonacci(100) 1.6180339887498948482045868343656381177203127439638 >>> +phi 1.6180339887498948482045868343656381177203091798058 The sum of reciprocal Fibonacci numbers converges to an irrational number for which no closed form expression is known:: >>> mp.dps = 15 >>> nsum(lambda n: 1/fib(n), [1, inf]) 3.35988566624318 Amazingly, however, the sum of odd-index reciprocal Fibonacci numbers can be expressed in terms of a Jacobi theta function:: >>> nsum(lambda n: 1/fib(2*n+1), [0, inf]) 1.82451515740692 >>> sqrt(5)*jtheta(2,0,(3-sqrt(5))/2)**2/4 1.82451515740692 Some related sums can be done in closed form:: >>> nsum(lambda k: 1/(1+fib(2*k+1)), [0, inf]) 1.11803398874989 >>> phi - 0.5 1.11803398874989 >>> f = lambda k:(-1)**(k+1) / sum(fib(n)**2 for n in range(1,int(k+1))) >>> nsum(f, [1, inf]) 0.618033988749895 >>> phi-1 0.618033988749895 **References** 1. http://mathworld.wolfram.com/FibonacciNumber.html """ altzeta = r""" Gives the Dirichlet eta function, `\eta(s)`, also known as the alternating zeta function. This function is defined in analogy with the Riemann zeta function as providing the sum of the alternating series .. math :: \eta(s) = \sum_{k=0}^{\infty} \frac{(-1)^k}{k^s} = 1-\frac{1}{2^s}+\frac{1}{3^s}-\frac{1}{4^s}+\ldots The eta function, unlike the Riemann zeta function, is an entire function, having a finite value for all complex `s`. The special case `\eta(1) = \log(2)` gives the value of the alternating harmonic series. The alternating zeta function may expressed using the Riemann zeta function as `\eta(s) = (1 - 2^{1-s}) \zeta(s)`. It can also be expressed in terms of the Hurwitz zeta function, for example using :func:`~mpmath.dirichlet` (see documentation for that function). **Examples** Some special values are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> altzeta(1) 0.693147180559945 >>> altzeta(0) 0.5 >>> altzeta(-1) 0.25 >>> altzeta(-2) 0.0 An example of a sum that can be computed more accurately and efficiently via :func:`~mpmath.altzeta` than via numerical summation:: >>> sum(-(-1)**n / n**2.5 for n in range(1, 100)) 0.86720495150398402 >>> altzeta(2.5) 0.867199889012184 At positive even integers, the Dirichlet eta function evaluates to a rational multiple of a power of `\pi`:: >>> altzeta(2) 0.822467033424113 >>> pi**2/12 0.822467033424113 Like the Riemann zeta function, `\eta(s)`, approaches 1 as `s` approaches positive infinity, although it does so from below rather than from above:: >>> altzeta(30) 0.999999999068682 >>> altzeta(inf) 1.0 >>> mp.pretty = False >>> altzeta(1000, rounding='d') mpf('0.99999999999999989') >>> altzeta(1000, rounding='u') mpf('1.0') **References** 1. http://mathworld.wolfram.com/DirichletEtaFunction.html 2. http://en.wikipedia.org/wiki/Dirichlet_eta_function """ factorial = r""" Computes the factorial, `x!`. For integers `n \ge 0`, we have `n! = 1 \cdot 2 \cdots (n-1) \cdot n` and more generally the factorial is defined for real or complex `x` by `x! = \Gamma(x+1)`. **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for k in range(6): ... print("%s %s" % (k, fac(k))) ... 0 1.0 1 1.0 2 2.0 3 6.0 4 24.0 5 120.0 >>> fac(inf) +inf >>> fac(0.5), sqrt(pi)/2 (0.886226925452758, 0.886226925452758) For large positive `x`, `x!` can be approximated by Stirling's formula:: >>> x = 10**10 >>> fac(x) 2.32579620567308e+95657055186 >>> sqrt(2*pi*x)*(x/e)**x 2.32579597597705e+95657055186 :func:`~mpmath.fac` supports evaluation for astronomically large values:: >>> fac(10**30) 6.22311232304258e+29565705518096748172348871081098 Reciprocal factorials appear in the Taylor series of the exponential function (among many other contexts):: >>> nsum(lambda k: 1/fac(k), [0, inf]), exp(1) (2.71828182845905, 2.71828182845905) >>> nsum(lambda k: pi**k/fac(k), [0, inf]), exp(pi) (23.1406926327793, 23.1406926327793) """ gamma = r""" Computes the gamma function, `\Gamma(x)`. The gamma function is a shifted version of the ordinary factorial, satisfying `\Gamma(n) = (n-1)!` for integers `n > 0`. More generally, it is defined by .. math :: \Gamma(x) = \int_0^{\infty} t^{x-1} e^{-t}\, dt for any real or complex `x` with `\Re(x) > 0` and for `\Re(x) < 0` by analytic continuation. **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for k in range(1, 6): ... print("%s %s" % (k, gamma(k))) ... 1 1.0 2 1.0 3 2.0 4 6.0 5 24.0 >>> gamma(inf) +inf >>> gamma(0) Traceback (most recent call last): ... ValueError: gamma function pole The gamma function of a half-integer is a rational multiple of `\sqrt{\pi}`:: >>> gamma(0.5), sqrt(pi) (1.77245385090552, 1.77245385090552) >>> gamma(1.5), sqrt(pi)/2 (0.886226925452758, 0.886226925452758) We can check the integral definition:: >>> gamma(3.5) 3.32335097044784 >>> quad(lambda t: t**2.5*exp(-t), [0,inf]) 3.32335097044784 :func:`~mpmath.gamma` supports arbitrary-precision evaluation and complex arguments:: >>> mp.dps = 50 >>> gamma(sqrt(3)) 0.91510229697308632046045539308226554038315280564184 >>> mp.dps = 25 >>> gamma(2j) (0.009902440080927490985955066 - 0.07595200133501806872408048j) Arguments can also be large. Note that the gamma function grows very quickly:: >>> mp.dps = 15 >>> gamma(10**20) 1.9328495143101e+1956570551809674817225 """ psi = r""" Gives the polygamma function of order `m` of `z`, `\psi^{(m)}(z)`. Special cases are known as the *digamma function* (`\psi^{(0)}(z)`), the *trigamma function* (`\psi^{(1)}(z)`), etc. The polygamma functions are defined as the logarithmic derivatives of the gamma function: .. math :: \psi^{(m)}(z) = \left(\frac{d}{dz}\right)^{m+1} \log \Gamma(z) In particular, `\psi^{(0)}(z) = \Gamma'(z)/\Gamma(z)`. In the present implementation of :func:`~mpmath.psi`, the order `m` must be a nonnegative integer, while the argument `z` may be an arbitrary complex number (with exception for the polygamma function's poles at `z = 0, -1, -2, \ldots`). **Examples** For various rational arguments, the polygamma function reduces to a combination of standard mathematical constants:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> psi(0, 1), -euler (-0.5772156649015328606065121, -0.5772156649015328606065121) >>> psi(1, '1/4'), pi**2+8*catalan (17.19732915450711073927132, 17.19732915450711073927132) >>> psi(2, '1/2'), -14*apery (-16.82879664423431999559633, -16.82879664423431999559633) The polygamma functions are derivatives of each other:: >>> diff(lambda x: psi(3, x), pi), psi(4, pi) (-0.1105749312578862734526952, -0.1105749312578862734526952) >>> quad(lambda x: psi(4, x), [2, 3]), psi(3,3)-psi(3,2) (-0.375, -0.375) The digamma function diverges logarithmically as `z \to \infty`, while higher orders tend to zero:: >>> psi(0,inf), psi(1,inf), psi(2,inf) (+inf, 0.0, 0.0) Evaluation for a complex argument:: >>> psi(2, -1-2j) (0.03902435405364952654838445 + 0.1574325240413029954685366j) Evaluation is supported for large orders `m` and/or large arguments `z`:: >>> psi(3, 10**100) 2.0e-300 >>> psi(250, 10**30+10**20*j) (-1.293142504363642687204865e-7010 + 3.232856260909107391513108e-7018j) **Application to infinite series** Any infinite series where the summand is a rational function of the index `k` can be evaluated in closed form in terms of polygamma functions of the roots and poles of the summand:: >>> a = sqrt(2) >>> b = sqrt(3) >>> nsum(lambda k: 1/((k+a)**2*(k+b)), [0, inf]) 0.4049668927517857061917531 >>> (psi(0,a)-psi(0,b)-a*psi(1,a)+b*psi(1,a))/(a-b)**2 0.4049668927517857061917531 This follows from the series representation (`m > 0`) .. math :: \psi^{(m)}(z) = (-1)^{m+1} m! \sum_{k=0}^{\infty} \frac{1}{(z+k)^{m+1}}. Since the roots of a polynomial may be complex, it is sometimes necessary to use the complex polygamma function to evaluate an entirely real-valued sum:: >>> nsum(lambda k: 1/(k**2-2*k+3), [0, inf]) 1.694361433907061256154665 >>> nprint(polyroots([1,-2,3])) [(1.0 - 1.41421j), (1.0 + 1.41421j)] >>> r1 = 1-sqrt(2)*j >>> r2 = r1.conjugate() >>> (psi(0,-r2)-psi(0,-r1))/(r1-r2) (1.694361433907061256154665 + 0.0j) """ digamma = r""" Shortcut for ``psi(0,z)``. """ harmonic = r""" If `n` is an integer, ``harmonic(n)`` gives a floating-point approximation of the `n`-th harmonic number `H(n)`, defined as .. math :: H(n) = 1 + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n} The first few harmonic numbers are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(8): ... print("%s %s" % (n, harmonic(n))) ... 0 0.0 1 1.0 2 1.5 3 1.83333333333333 4 2.08333333333333 5 2.28333333333333 6 2.45 7 2.59285714285714 The infinite harmonic series `1 + 1/2 + 1/3 + \ldots` diverges:: >>> harmonic(inf) +inf :func:`~mpmath.harmonic` is evaluated using the digamma function rather than by summing the harmonic series term by term. It can therefore be computed quickly for arbitrarily large `n`, and even for nonintegral arguments:: >>> harmonic(10**100) 230.835724964306 >>> harmonic(0.5) 0.613705638880109 >>> harmonic(3+4j) (2.24757548223494 + 0.850502209186044j) :func:`~mpmath.harmonic` supports arbitrary precision evaluation:: >>> mp.dps = 50 >>> harmonic(11) 3.0198773448773448773448773448773448773448773448773 >>> harmonic(pi) 1.8727388590273302654363491032336134987519132374152 The harmonic series diverges, but at a glacial pace. It is possible to calculate the exact number of terms required before the sum exceeds a given amount, say 100:: >>> mp.dps = 50 >>> v = 10**findroot(lambda x: harmonic(10**x) - 100, 10) >>> v 15092688622113788323693563264538101449859496.864101 >>> v = int(ceil(v)) >>> print(v) 15092688622113788323693563264538101449859497 >>> harmonic(v-1) 99.999999999999999999999999999999999999999999942747 >>> harmonic(v) 100.000000000000000000000000000000000000000000009 """ bernoulli = r""" Computes the nth Bernoulli number, `B_n`, for any integer `n \ge 0`. The Bernoulli numbers are rational numbers, but this function returns a floating-point approximation. To obtain an exact fraction, use :func:`~mpmath.bernfrac` instead. **Examples** Numerical values of the first few Bernoulli numbers:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(15): ... print("%s %s" % (n, bernoulli(n))) ... 0 1.0 1 -0.5 2 0.166666666666667 3 0.0 4 -0.0333333333333333 5 0.0 6 0.0238095238095238 7 0.0 8 -0.0333333333333333 9 0.0 10 0.0757575757575758 11 0.0 12 -0.253113553113553 13 0.0 14 1.16666666666667 Bernoulli numbers can be approximated with arbitrary precision:: >>> mp.dps = 50 >>> bernoulli(100) -2.8382249570693706959264156336481764738284680928013e+78 Arbitrarily large `n` are supported:: >>> mp.dps = 15 >>> bernoulli(10**20 + 2) 3.09136296657021e+1876752564973863312327 The Bernoulli numbers are related to the Riemann zeta function at integer arguments:: >>> -bernoulli(8) * (2*pi)**8 / (2*fac(8)) 1.00407735619794 >>> zeta(8) 1.00407735619794 **Algorithm** For small `n` (`n < 3000`) :func:`~mpmath.bernoulli` uses a recurrence formula due to Ramanujan. All results in this range are cached, so sequential computation of small Bernoulli numbers is guaranteed to be fast. For larger `n`, `B_n` is evaluated in terms of the Riemann zeta function. """ stieltjes = r""" For a nonnegative integer `n`, ``stieltjes(n)`` computes the `n`-th Stieltjes constant `\gamma_n`, defined as the `n`-th coefficient in the Laurent series expansion of the Riemann zeta function around the pole at `s = 1`. That is, we have: .. math :: \zeta(s) = \frac{1}{s-1} \sum_{n=0}^{\infty} \frac{(-1)^n}{n!} \gamma_n (s-1)^n More generally, ``stieltjes(n, a)`` gives the corresponding coefficient `\gamma_n(a)` for the Hurwitz zeta function `\zeta(s,a)` (with `\gamma_n = \gamma_n(1)`). **Examples** The zeroth Stieltjes constant is just Euler's constant `\gamma`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> stieltjes(0) 0.577215664901533 Some more values are:: >>> stieltjes(1) -0.0728158454836767 >>> stieltjes(10) 0.000205332814909065 >>> stieltjes(30) 0.00355772885557316 >>> stieltjes(1000) -1.57095384420474e+486 >>> stieltjes(2000) 2.680424678918e+1109 >>> stieltjes(1, 2.5) -0.23747539175716 An alternative way to compute `\gamma_1`:: >>> diff(extradps(15)(lambda x: 1/(x-1) - zeta(x)), 1) -0.0728158454836767 :func:`~mpmath.stieltjes` supports arbitrary precision evaluation:: >>> mp.dps = 50 >>> stieltjes(2) -0.0096903631928723184845303860352125293590658061013408 **Algorithm** :func:`~mpmath.stieltjes` numerically evaluates the integral in the following representation due to Ainsworth, Howell and Coffey [1], [2]: .. math :: \gamma_n(a) = \frac{\log^n a}{2a} - \frac{\log^{n+1}(a)}{n+1} + \frac{2}{a} \Re \int_0^{\infty} \frac{(x/a-i)\log^n(a-ix)}{(1+x^2/a^2)(e^{2\pi x}-1)} dx. For some reference values with `a = 1`, see e.g. [4]. **References** 1. O. R. Ainsworth & L. W. Howell, "An integral representation of the generalized Euler-Mascheroni constants", NASA Technical Paper 2456 (1985), http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19850014994_1985014994.pdf 2. M. W. Coffey, "The Stieltjes constants, their relation to the `\eta_j` coefficients, and representation of the Hurwitz zeta function", arXiv:0706.0343v1 http://arxiv.org/abs/0706.0343 3. http://mathworld.wolfram.com/StieltjesConstants.html 4. http://pi.lacim.uqam.ca/piDATA/stieltjesgamma.txt """ gammaprod = r""" Given iterables `a` and `b`, ``gammaprod(a, b)`` computes the product / quotient of gamma functions: .. math :: \frac{\Gamma(a_0) \Gamma(a_1) \cdots \Gamma(a_p)} {\Gamma(b_0) \Gamma(b_1) \cdots \Gamma(b_q)} Unlike direct calls to :func:`~mpmath.gamma`, :func:`~mpmath.gammaprod` considers the entire product as a limit and evaluates this limit properly if any of the numerator or denominator arguments are nonpositive integers such that poles of the gamma function are encountered. That is, :func:`~mpmath.gammaprod` evaluates .. math :: \lim_{\epsilon \to 0} \frac{\Gamma(a_0+\epsilon) \Gamma(a_1+\epsilon) \cdots \Gamma(a_p+\epsilon)} {\Gamma(b_0+\epsilon) \Gamma(b_1+\epsilon) \cdots \Gamma(b_q+\epsilon)} In particular: * If there are equally many poles in the numerator and the denominator, the limit is a rational number times the remaining, regular part of the product. * If there are more poles in the numerator, :func:`~mpmath.gammaprod` returns ``+inf``. * If there are more poles in the denominator, :func:`~mpmath.gammaprod` returns 0. **Examples** The reciprocal gamma function `1/\Gamma(x)` evaluated at `x = 0`:: >>> from mpmath import * >>> mp.dps = 15 >>> gammaprod([], [0]) 0.0 A limit:: >>> gammaprod([-4], [-3]) -0.25 >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=1) -0.25 >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=-1) -0.25 """ beta = r""" Computes the beta function, `B(x,y) = \Gamma(x) \Gamma(y) / \Gamma(x+y)`. The beta function is also commonly defined by the integral representation .. math :: B(x,y) = \int_0^1 t^{x-1} (1-t)^{y-1} \, dt **Examples** For integer and half-integer arguments where all three gamma functions are finite, the beta function becomes either rational number or a rational multiple of `\pi`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> beta(5, 2) 0.0333333333333333 >>> beta(1.5, 2) 0.266666666666667 >>> 16*beta(2.5, 1.5) 3.14159265358979 Where appropriate, :func:`~mpmath.beta` evaluates limits. A pole of the beta function is taken to result in ``+inf``:: >>> beta(-0.5, 0.5) 0.0 >>> beta(-3, 3) -0.333333333333333 >>> beta(-2, 3) +inf >>> beta(inf, 1) 0.0 >>> beta(inf, 0) nan :func:`~mpmath.beta` supports complex numbers and arbitrary precision evaluation:: >>> beta(1, 2+j) (0.4 - 0.2j) >>> mp.dps = 25 >>> beta(j,0.5) (1.079424249270925780135675 - 1.410032405664160838288752j) >>> mp.dps = 50 >>> beta(pi, e) 0.037890298781212201348153837138927165984170287886464 Various integrals can be computed by means of the beta function:: >>> mp.dps = 15 >>> quad(lambda t: t**2.5*(1-t)**2, [0, 1]) 0.0230880230880231 >>> beta(3.5, 3) 0.0230880230880231 >>> quad(lambda t: sin(t)**4 * sqrt(cos(t)), [0, pi/2]) 0.319504062596158 >>> beta(2.5, 0.75)/2 0.319504062596158 """ betainc = r""" ``betainc(a, b, x1=0, x2=1, regularized=False)`` gives the generalized incomplete beta function, .. math :: I_{x_1}^{x_2}(a,b) = \int_{x_1}^{x_2} t^{a-1} (1-t)^{b-1} dt. When `x_1 = 0, x_2 = 1`, this reduces to the ordinary (complete) beta function `B(a,b)`; see :func:`~mpmath.beta`. With the keyword argument ``regularized=True``, :func:`~mpmath.betainc` computes the regularized incomplete beta function `I_{x_1}^{x_2}(a,b) / B(a,b)`. This is the cumulative distribution of the beta distribution with parameters `a`, `b`. .. note : Implementations of the incomplete beta function in some other software uses a different argument order. For example, Mathematica uses the reversed argument order ``Beta[x1,x2,a,b]``. For the equivalent of SciPy's three-argument incomplete beta integral (implicitly with `x1 = 0`), use ``betainc(a,b,0,x2,regularized=True)``. **Examples** Verifying that :func:`~mpmath.betainc` computes the integral in the definition:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> x,y,a,b = 3, 4, 0, 6 >>> betainc(x, y, a, b) -4010.4 >>> quad(lambda t: t**(x-1) * (1-t)**(y-1), [a, b]) -4010.4 The arguments may be arbitrary complex numbers:: >>> betainc(0.75, 1-4j, 0, 2+3j) (0.2241657956955709603655887 + 0.3619619242700451992411724j) With regularization:: >>> betainc(1, 2, 0, 0.25, regularized=True) 0.4375 >>> betainc(pi, e, 0, 1, regularized=True) # Complete 1.0 The beta integral satisfies some simple argument transformation symmetries:: >>> mp.dps = 15 >>> betainc(2,3,4,5), -betainc(2,3,5,4), betainc(3,2,1-5,1-4) (56.0833333333333, 56.0833333333333, 56.0833333333333) The beta integral can often be evaluated analytically. For integer and rational arguments, the incomplete beta function typically reduces to a simple algebraic-logarithmic expression:: >>> mp.dps = 25 >>> identify(chop(betainc(0, 0, 3, 4))) '-(log((9/8)))' >>> identify(betainc(2, 3, 4, 5)) '(673/12)' >>> identify(betainc(1.5, 1, 1, 2)) '((-12+sqrt(1152))/18)' """ binomial = r""" Computes the binomial coefficient .. math :: {n \choose k} = \frac{n!}{k!(n-k)!}. The binomial coefficient gives the number of ways that `k` items can be chosen from a set of `n` items. More generally, the binomial coefficient is a well-defined function of arbitrary real or complex `n` and `k`, via the gamma function. **Examples** Generate Pascal's triangle:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint([binomial(n,k) for k in range(n+1)]) ... [1.0] [1.0, 1.0] [1.0, 2.0, 1.0] [1.0, 3.0, 3.0, 1.0] [1.0, 4.0, 6.0, 4.0, 1.0] There is 1 way to select 0 items from the empty set, and 0 ways to select 1 item from the empty set:: >>> binomial(0, 0) 1.0 >>> binomial(0, 1) 0.0 :func:`~mpmath.binomial` supports large arguments:: >>> binomial(10**20, 10**20-5) 8.33333333333333e+97 >>> binomial(10**20, 10**10) 2.60784095465201e+104342944813 Nonintegral binomial coefficients find use in series expansions:: >>> nprint(taylor(lambda x: (1+x)**0.25, 0, 4)) [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] >>> nprint([binomial(0.25, k) for k in range(5)]) [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] An integral representation:: >>> n, k = 5, 3 >>> f = lambda t: exp(-j*k*t)*(1+exp(j*t))**n >>> chop(quad(f, [-pi,pi])/(2*pi)) 10.0 >>> binomial(n,k) 10.0 """ rf = r""" Computes the rising factorial or Pochhammer symbol, .. math :: x^{(n)} = x (x+1) \cdots (x+n-1) = \frac{\Gamma(x+n)}{\Gamma(x)} where the rightmost expression is valid for nonintegral `n`. **Examples** For integral `n`, the rising factorial is a polynomial:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(taylor(lambda x: rf(x,n), 0, n)) ... [1.0] [0.0, 1.0] [0.0, 1.0, 1.0] [0.0, 2.0, 3.0, 1.0] [0.0, 6.0, 11.0, 6.0, 1.0] Evaluation is supported for arbitrary arguments:: >>> rf(2+3j, 5.5) (-7202.03920483347 - 3777.58810701527j) """ ff = r""" Computes the falling factorial, .. math :: (x)_n = x (x-1) \cdots (x-n+1) = \frac{\Gamma(x+1)}{\Gamma(x-n+1)} where the rightmost expression is valid for nonintegral `n`. **Examples** For integral `n`, the falling factorial is a polynomial:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(taylor(lambda x: ff(x,n), 0, n)) ... [1.0] [0.0, 1.0] [0.0, -1.0, 1.0] [0.0, 2.0, -3.0, 1.0] [0.0, -6.0, 11.0, -6.0, 1.0] Evaluation is supported for arbitrary arguments:: >>> ff(2+3j, 5.5) (-720.41085888203 + 316.101124983878j) """ fac2 = r""" Computes the double factorial `x!!`, defined for integers `x > 0` by .. math :: x!! = \begin{cases} 1 \cdot 3 \cdots (x-2) \cdot x & x \;\mathrm{odd} \\ 2 \cdot 4 \cdots (x-2) \cdot x & x \;\mathrm{even} \end{cases} and more generally by [1] .. math :: x!! = 2^{x/2} \left(\frac{\pi}{2}\right)^{(\cos(\pi x)-1)/4} \Gamma\left(\frac{x}{2}+1\right). **Examples** The integer sequence of double factorials begins:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint([fac2(n) for n in range(10)]) [1.0, 1.0, 2.0, 3.0, 8.0, 15.0, 48.0, 105.0, 384.0, 945.0] For large `x`, double factorials follow a Stirling-like asymptotic approximation:: >>> x = mpf(10000) >>> fac2(x) 5.97272691416282e+17830 >>> sqrt(pi)*x**((x+1)/2)*exp(-x/2) 5.97262736954392e+17830 The recurrence formula `x!! = x (x-2)!!` can be reversed to define the double factorial of negative odd integers (but not negative even integers):: >>> fac2(-1), fac2(-3), fac2(-5), fac2(-7) (1.0, -1.0, 0.333333333333333, -0.0666666666666667) >>> fac2(-2) Traceback (most recent call last): ... ValueError: gamma function pole With the exception of the poles at negative even integers, :func:`~mpmath.fac2` supports evaluation for arbitrary complex arguments. The recurrence formula is valid generally:: >>> fac2(pi+2j) (-1.3697207890154e-12 + 3.93665300979176e-12j) >>> (pi+2j)*fac2(pi-2+2j) (-1.3697207890154e-12 + 3.93665300979176e-12j) Double factorials should not be confused with nested factorials, which are immensely larger:: >>> fac(fac(20)) 5.13805976125208e+43675043585825292774 >>> fac2(20) 3715891200.0 Double factorials appear, among other things, in series expansions of Gaussian functions and the error function. Infinite series include:: >>> nsum(lambda k: 1/fac2(k), [0, inf]) 3.05940740534258 >>> sqrt(e)*(1+sqrt(pi/2)*erf(sqrt(2)/2)) 3.05940740534258 >>> nsum(lambda k: 2**k/fac2(2*k-1), [1, inf]) 4.06015693855741 >>> e * erf(1) * sqrt(pi) 4.06015693855741 A beautiful Ramanujan sum:: >>> nsum(lambda k: (-1)**k*(fac2(2*k-1)/fac2(2*k))**3, [0,inf]) 0.90917279454693 >>> (gamma('9/8')/gamma('5/4')/gamma('7/8'))**2 0.90917279454693 **References** 1. http://functions.wolfram.com/GammaBetaErf/Factorial2/27/01/0002/ 2. http://mathworld.wolfram.com/DoubleFactorial.html """ hyper = r""" Evaluates the generalized hypergeometric function .. math :: \,_pF_q(a_1,\ldots,a_p; b_1,\ldots,b_q; z) = \sum_{n=0}^\infty \frac{(a_1)_n (a_2)_n \ldots (a_p)_n} {(b_1)_n(b_2)_n\ldots(b_q)_n} \frac{z^n}{n!} where `(x)_n` denotes the rising factorial (see :func:`~mpmath.rf`). The parameters lists ``a_s`` and ``b_s`` may contain integers, real numbers, complex numbers, as well as exact fractions given in the form of tuples `(p, q)`. :func:`~mpmath.hyper` is optimized to handle integers and fractions more efficiently than arbitrary floating-point parameters (since rational parameters are by far the most common). **Examples** Verifying that :func:`~mpmath.hyper` gives the sum in the definition, by comparison with :func:`~mpmath.nsum`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a,b,c,d = 2,3,4,5 >>> x = 0.25 >>> hyper([a,b],[c,d],x) 1.078903941164934876086237 >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)*x**n/fac(n) >>> nsum(fn, [0, inf]) 1.078903941164934876086237 The parameters can be any combination of integers, fractions, floats and complex numbers:: >>> a, b, c, d, e = 1, (-1,2), pi, 3+4j, (2,3) >>> x = 0.2j >>> hyper([a,b],[c,d,e],x) (0.9923571616434024810831887 - 0.005753848733883879742993122j) >>> b, e = -0.5, mpf(2)/3 >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)/rf(e,n)*x**n/fac(n) >>> nsum(fn, [0, inf]) (0.9923571616434024810831887 - 0.005753848733883879742993122j) The `\,_0F_0` and `\,_1F_0` series are just elementary functions:: >>> a, z = sqrt(2), +pi >>> hyper([],[],z) 23.14069263277926900572909 >>> exp(z) 23.14069263277926900572909 >>> hyper([a],[],z) (-0.09069132879922920160334114 + 0.3283224323946162083579656j) >>> (1-z)**(-a) (-0.09069132879922920160334114 + 0.3283224323946162083579656j) If any `a_k` coefficient is a nonpositive integer, the series terminates into a finite polynomial:: >>> hyper([1,1,1,-3],[2,5],1) 0.7904761904761904761904762 >>> identify(_) '(83/105)' If any `b_k` is a nonpositive integer, the function is undefined (unless the series terminates before the division by zero occurs):: >>> hyper([1,1,1,-3],[-2,5],1) Traceback (most recent call last): ... ZeroDivisionError: pole in hypergeometric series >>> hyper([1,1,1,-1],[-2,5],1) 1.1 Except for polynomial cases, the radius of convergence `R` of the hypergeometric series is either `R = \infty` (if `p \le q`), `R = 1` (if `p = q+1`), or `R = 0` (if `p > q+1`). The analytic continuations of the functions with `p = q+1`, i.e. `\,_2F_1`, `\,_3F_2`, `\,_4F_3`, etc, are all implemented and therefore these functions can be evaluated for `|z| \ge 1`. The shortcuts :func:`~mpmath.hyp2f1`, :func:`~mpmath.hyp3f2` are available to handle the most common cases (see their documentation), but functions of higher degree are also supported via :func:`~mpmath.hyper`:: >>> hyper([1,2,3,4], [5,6,7], 1) # 4F3 at finite-valued branch point 1.141783505526870731311423 >>> hyper([4,5,6,7], [1,2,3], 1) # 4F3 at pole +inf >>> hyper([1,2,3,4,5], [6,7,8,9], 10) # 5F4 (1.543998916527972259717257 - 0.5876309929580408028816365j) >>> hyper([1,2,3,4,5,6], [7,8,9,10,11], 1j) # 6F5 (0.9996565821853579063502466 + 0.0129721075905630604445669j) Near `z = 1` with noninteger parameters:: >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','41/8'], 1) 2.219433352235586121250027 >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], 1) +inf >>> eps1 = extradps(6)(lambda: 1 - mpf('1e-6'))() >>> hyper(['1/3',1,'3/2',2], ['1/5','11/6','5/4'], eps1) 2923978034.412973409330956 Please note that, as currently implemented, evaluation of `\,_pF_{p-1}` with `p \ge 3` may be slow or inaccurate when `|z-1|` is small, for some parameter values. When `p > q+1`, ``hyper`` computes the (iterated) Borel sum of the divergent series. For `\,_2F_0` the Borel sum has an analytic solution and can be computed efficiently (see :func:`~mpmath.hyp2f0`). For higher degrees, the functions is evaluated first by attempting to sum it directly as an asymptotic series (this only works for tiny `|z|`), and then by evaluating the Borel regularized sum using numerical integration. Except for special parameter combinations, this can be extremely slow. >>> hyper([1,1], [], 0.5) # regularization of 2F0 (1.340965419580146562086448 + 0.8503366631752726568782447j) >>> hyper([1,1,1,1], [1], 0.5) # regularization of 4F1 (1.108287213689475145830699 + 0.5327107430640678181200491j) With the following magnitude of argument, the asymptotic series for `\,_3F_1` gives only a few digits. Using Borel summation, ``hyper`` can produce a value with full accuracy:: >>> mp.dps = 15 >>> hyper([2,0.5,4], [5.25], '0.08', force_series=True) Traceback (most recent call last): ... NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. >>> hyper([2,0.5,4], [5.25], '0.08', asymp_tol=1e-4) 1.0725535790737 >>> hyper([2,0.5,4], [5.25], '0.08') (1.07269542893559 + 5.54668863216891e-5j) >>> hyper([2,0.5,4], [5.25], '-0.08', asymp_tol=1e-4) 0.946344925484879 >>> hyper([2,0.5,4], [5.25], '-0.08') 0.946312503737771 >>> mp.dps = 25 >>> hyper([2,0.5,4], [5.25], '-0.08') 0.9463125037377662296700858 Note that with the positive `z` value, there is a complex part in the correct result, which falls below the tolerance of the asymptotic series. """ hypercomb = r""" Computes a weighted combination of hypergeometric functions .. math :: \sum_{r=1}^N \left[ \prod_{k=1}^{l_r} {w_{r,k}}^{c_{r,k}} \frac{\prod_{k=1}^{m_r} \Gamma(\alpha_{r,k})}{\prod_{k=1}^{n_r} \Gamma(\beta_{r,k})} \,_{p_r}F_{q_r}(a_{r,1},\ldots,a_{r,p}; b_{r,1}, \ldots, b_{r,q}; z_r)\right]. Typically the parameters are linear combinations of a small set of base parameters; :func:`~mpmath.hypercomb` permits computing a correct value in the case that some of the `\alpha`, `\beta`, `b` turn out to be nonpositive integers, or if division by zero occurs for some `w^c`, assuming that there are opposing singularities that cancel out. The limit is computed by evaluating the function with the base parameters perturbed, at a higher working precision. The first argument should be a function that takes the perturbable base parameters ``params`` as input and returns `N` tuples ``(w, c, alpha, beta, a, b, z)``, where the coefficients ``w``, ``c``, gamma factors ``alpha``, ``beta``, and hypergeometric coefficients ``a``, ``b`` each should be lists of numbers, and ``z`` should be a single number. **Examples** The following evaluates .. math :: (a-1) \frac{\Gamma(a-3)}{\Gamma(a-4)} \,_1F_1(a,a-1,z) = e^z(a-4)(a+z-1) with `a=1, z=3`. There is a zero factor, two gamma function poles, and the 1F1 function is singular; all singularities cancel out to give a finite value:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> hypercomb(lambda a: [([a-1],[1],[a-3],[a-4],[a],[a-1],3)], [1]) -180.769832308689 >>> -9*exp(3) -180.769832308689 """ hyp0f1 = r""" Gives the hypergeometric function `\,_0F_1`, sometimes known as the confluent limit function, defined as .. math :: \,_0F_1(a,z) = \sum_{k=0}^{\infty} \frac{1}{(a)_k} \frac{z^k}{k!}. This function satisfies the differential equation `z f''(z) + a f'(z) = f(z)`, and is related to the Bessel function of the first kind (see :func:`~mpmath.besselj`). ``hyp0f1(a,z)`` is equivalent to ``hyper([],[a],z)``; see documentation for :func:`~mpmath.hyper` for more information. **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp0f1(2, 0.25) 1.130318207984970054415392 >>> hyp0f1((1,2), 1234567) 6.27287187546220705604627e+964 >>> hyp0f1(3+4j, 1000000j) (3.905169561300910030267132e+606 + 3.807708544441684513934213e+606j) Evaluation is supported for arbitrarily large values of `z`, using asymptotic expansions:: >>> hyp0f1(1, 10**50) 2.131705322874965310390701e+8685889638065036553022565 >>> hyp0f1(1, -10**50) 1.115945364792025420300208e-13 Verifying the differential equation:: >>> a = 2.5 >>> f = lambda z: hyp0f1(a,z) >>> for z in [0, 10, 3+4j]: ... chop(z*diff(f,z,2) + a*diff(f,z) - f(z)) ... 0.0 0.0 0.0 """ hyp1f1 = r""" Gives the confluent hypergeometric function of the first kind, .. math :: \,_1F_1(a,b,z) = \sum_{k=0}^{\infty} \frac{(a)_k}{(b)_k} \frac{z^k}{k!}, also known as Kummer's function and sometimes denoted by `M(a,b,z)`. This function gives one solution to the confluent (Kummer's) differential equation .. math :: z f''(z) + (b-z) f'(z) - af(z) = 0. A second solution is given by the `U` function; see :func:`~mpmath.hyperu`. Solutions are also given in an alternate form by the Whittaker functions (:func:`~mpmath.whitm`, :func:`~mpmath.whitw`). ``hyp1f1(a,b,z)`` is equivalent to ``hyper([a],[b],z)``; see documentation for :func:`~mpmath.hyper` for more information. **Examples** Evaluation for real and complex values of the argument `z`, with fixed parameters `a = 2, b = -1/3`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp1f1(2, (-1,3), 3.25) -2815.956856924817275640248 >>> hyp1f1(2, (-1,3), -3.25) -1.145036502407444445553107 >>> hyp1f1(2, (-1,3), 1000) -8.021799872770764149793693e+441 >>> hyp1f1(2, (-1,3), -1000) 0.000003131987633006813594535331 >>> hyp1f1(2, (-1,3), 100+100j) (-3.189190365227034385898282e+48 - 1.106169926814270418999315e+49j) Parameters may be complex:: >>> hyp1f1(2+3j, -1+j, 10j) (261.8977905181045142673351 + 160.8930312845682213562172j) Arbitrarily large values of `z` are supported:: >>> hyp1f1(3, 4, 10**20) 3.890569218254486878220752e+43429448190325182745 >>> hyp1f1(3, 4, -10**20) 6.0e-60 >>> hyp1f1(3, 4, 10**20*j) (-1.935753855797342532571597e-20 - 2.291911213325184901239155e-20j) Verifying the differential equation:: >>> a, b = 1.5, 2 >>> f = lambda z: hyp1f1(a,b,z) >>> for z in [0, -10, 3, 3+4j]: ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) ... 0.0 0.0 0.0 0.0 An integral representation:: >>> a, b = 1.5, 3 >>> z = 1.5 >>> hyp1f1(a,b,z) 2.269381460919952778587441 >>> g = lambda t: exp(z*t)*t**(a-1)*(1-t)**(b-a-1) >>> gammaprod([b],[a,b-a])*quad(g, [0,1]) 2.269381460919952778587441 """ hyp1f2 = r""" Gives the hypergeometric function `\,_1F_2(a_1,a_2;b_1,b_2; z)`. The call ``hyp1f2(a1,b1,b2,z)`` is equivalent to ``hyper([a1],[b1,b2],z)``. Evaluation works for complex and arbitrarily large arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a, b, c = 1.5, (-1,3), 2.25 >>> hyp1f2(a, b, c, 10**20) -1.159388148811981535941434e+8685889639 >>> hyp1f2(a, b, c, -10**20) -12.60262607892655945795907 >>> hyp1f2(a, b, c, 10**20*j) (4.237220401382240876065501e+6141851464 - 2.950930337531768015892987e+6141851464j) >>> hyp1f2(2+3j, -2j, 0.5j, 10-20j) (135881.9905586966432662004 - 86681.95885418079535738828j) """ hyp2f2 = r""" Gives the hypergeometric function `\,_2F_2(a_1,a_2;b_1,b_2; z)`. The call ``hyp2f2(a1,a2,b1,b2,z)`` is equivalent to ``hyper([a1,a2],[b1,b2],z)``. Evaluation works for complex and arbitrarily large arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a, b, c, d = 1.5, (-1,3), 2.25, 4 >>> hyp2f2(a, b, c, d, 10**20) -5.275758229007902299823821e+43429448190325182663 >>> hyp2f2(a, b, c, d, -10**20) 2561445.079983207701073448 >>> hyp2f2(a, b, c, d, 10**20*j) (2218276.509664121194836667 - 1280722.539991603850462856j) >>> hyp2f2(2+3j, -2j, 0.5j, 4j, 10-20j) (80500.68321405666957342788 - 20346.82752982813540993502j) """ hyp2f3 = r""" Gives the hypergeometric function `\,_2F_3(a_1,a_2;b_1,b_2,b_3; z)`. The call ``hyp2f3(a1,a2,b1,b2,b3,z)`` is equivalent to ``hyper([a1,a2],[b1,b2,b3],z)``. Evaluation works for arbitrarily large arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a1,a2,b1,b2,b3 = 1.5, (-1,3), 2.25, 4, (1,5) >>> hyp2f3(a1,a2,b1,b2,b3,10**20) -4.169178177065714963568963e+8685889590 >>> hyp2f3(a1,a2,b1,b2,b3,-10**20) 7064472.587757755088178629 >>> hyp2f3(a1,a2,b1,b2,b3,10**20*j) (-5.163368465314934589818543e+6141851415 + 1.783578125755972803440364e+6141851416j) >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10-20j) (-2280.938956687033150740228 + 13620.97336609573659199632j) >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10000000-20000000j) (4.849835186175096516193e+3504 - 3.365981529122220091353633e+3504j) """ hyp2f1 = r""" Gives the Gauss hypergeometric function `\,_2F_1` (often simply referred to as *the* hypergeometric function), defined for `|z| < 1` as .. math :: \,_2F_1(a,b,c,z) = \sum_{k=0}^{\infty} \frac{(a)_k (b)_k}{(c)_k} \frac{z^k}{k!}. and for `|z| \ge 1` by analytic continuation, with a branch cut on `(1, \infty)` when necessary. Special cases of this function include many of the orthogonal polynomials as well as the incomplete beta function and other functions. Properties of the Gauss hypergeometric function are documented comprehensively in many references, for example Abramowitz & Stegun, section 15. The implementation supports the analytic continuation as well as evaluation close to the unit circle where `|z| \approx 1`. The syntax ``hyp2f1(a,b,c,z)`` is equivalent to ``hyper([a,b],[c],z)``. **Examples** Evaluation with `z` inside, outside and on the unit circle, for fixed parameters:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp2f1(2, (1,2), 4, 0.75) 1.303703703703703703703704 >>> hyp2f1(2, (1,2), 4, -1.75) 0.7431290566046919177853916 >>> hyp2f1(2, (1,2), 4, 1.75) (1.418075801749271137026239 - 1.114976146679907015775102j) >>> hyp2f1(2, (1,2), 4, 1) 1.6 >>> hyp2f1(2, (1,2), 4, -1) 0.8235498012182875315037882 >>> hyp2f1(2, (1,2), 4, j) (0.9144026291433065674259078 + 0.2050415770437884900574923j) >>> hyp2f1(2, (1,2), 4, 2+j) (0.9274013540258103029011549 + 0.7455257875808100868984496j) >>> hyp2f1(2, (1,2), 4, 0.25j) (0.9931169055799728251931672 + 0.06154836525312066938147793j) Evaluation with complex parameter values:: >>> hyp2f1(1+j, 0.75, 10j, 1+5j) (0.8834833319713479923389638 + 0.7053886880648105068343509j) Evaluation with `z = 1`:: >>> hyp2f1(-2.5, 3.5, 1.5, 1) 0.0 >>> hyp2f1(-2.5, 3, 4, 1) 0.06926406926406926406926407 >>> hyp2f1(2, 3, 4, 1) +inf Evaluation for huge arguments:: >>> hyp2f1((-1,3), 1.75, 4, '1e100') (7.883714220959876246415651e+32 + 1.365499358305579597618785e+33j) >>> hyp2f1((-1,3), 1.75, 4, '1e1000000') (7.883714220959876246415651e+333332 + 1.365499358305579597618785e+333333j) >>> hyp2f1((-1,3), 1.75, 4, '1e1000000j') (1.365499358305579597618785e+333333 - 7.883714220959876246415651e+333332j) An integral representation:: >>> a,b,c,z = -0.5, 1, 2.5, 0.25 >>> g = lambda t: t**(b-1) * (1-t)**(c-b-1) * (1-t*z)**(-a) >>> gammaprod([c],[b,c-b]) * quad(g, [0,1]) 0.9480458814362824478852618 >>> hyp2f1(a,b,c,z) 0.9480458814362824478852618 Verifying the hypergeometric differential equation:: >>> f = lambda z: hyp2f1(a,b,c,z) >>> chop(z*(1-z)*diff(f,z,2) + (c-(a+b+1)*z)*diff(f,z) - a*b*f(z)) 0.0 """ hyp3f2 = r""" Gives the generalized hypergeometric function `\,_3F_2`, defined for `|z| < 1` as .. math :: \,_3F_2(a_1,a_2,a_3,b_1,b_2,z) = \sum_{k=0}^{\infty} \frac{(a_1)_k (a_2)_k (a_3)_k}{(b_1)_k (b_2)_k} \frac{z^k}{k!}. and for `|z| \ge 1` by analytic continuation. The analytic structure of this function is similar to that of `\,_2F_1`, generally with a singularity at `z = 1` and a branch cut on `(1, \infty)`. Evaluation is supported inside, on, and outside the circle of convergence `|z| = 1`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp3f2(1,2,3,4,5,0.25) 1.083533123380934241548707 >>> hyp3f2(1,2+2j,3,4,5,-10+10j) (0.1574651066006004632914361 - 0.03194209021885226400892963j) >>> hyp3f2(1,2,3,4,5,-10) 0.3071141169208772603266489 >>> hyp3f2(1,2,3,4,5,10) (-0.4857045320523947050581423 - 0.5988311440454888436888028j) >>> hyp3f2(0.25,1,1,2,1.5,1) 1.157370995096772047567631 >>> (8-pi-2*ln2)/3 1.157370995096772047567631 >>> hyp3f2(1+j,0.5j,2,1,-2j,-1) (1.74518490615029486475959 + 0.1454701525056682297614029j) >>> hyp3f2(1+j,0.5j,2,1,-2j,sqrt(j)) (0.9829816481834277511138055 - 0.4059040020276937085081127j) >>> hyp3f2(-3,2,1,-5,4,1) 1.41 >>> hyp3f2(-3,2,1,-5,4,2) 2.12 Evaluation very close to the unit circle:: >>> hyp3f2(1,2,3,4,5,'1.0001') (1.564877796743282766872279 - 3.76821518787438186031973e-11j) >>> hyp3f2(1,2,3,4,5,'1+0.0001j') (1.564747153061671573212831 + 0.0001305757570366084557648482j) >>> hyp3f2(1,2,3,4,5,'0.9999') 1.564616644881686134983664 >>> hyp3f2(1,2,3,4,5,'-0.9999') 0.7823896253461678060196207 .. note :: Evaluation for `|z-1|` small can currently be inaccurate or slow for some parameter combinations. For various parameter combinations, `\,_3F_2` admits representation in terms of hypergeometric functions of lower degree, or in terms of simpler functions:: >>> for a, b, z in [(1,2,-1), (2,0.5,1)]: ... hyp2f1(a,b,a+b+0.5,z)**2 ... hyp3f2(2*a,a+b,2*b,a+b+0.5,2*a+2*b,z) ... 0.4246104461966439006086308 0.4246104461966439006086308 7.111111111111111111111111 7.111111111111111111111111 >>> z = 2+3j >>> hyp3f2(0.5,1,1.5,2,2,z) (0.7621440939243342419729144 + 0.4249117735058037649915723j) >>> 4*(pi-2*ellipe(z))/(pi*z) (0.7621440939243342419729144 + 0.4249117735058037649915723j) """ hyperu = r""" Gives the Tricomi confluent hypergeometric function `U`, also known as the Kummer or confluent hypergeometric function of the second kind. This function gives a second linearly independent solution to the confluent hypergeometric differential equation (the first is provided by `\,_1F_1` -- see :func:`~mpmath.hyp1f1`). **Examples** Evaluation for arbitrary complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyperu(2,3,4) 0.0625 >>> hyperu(0.25, 5, 1000) 0.1779949416140579573763523 >>> hyperu(0.25, 5, -1000) (0.1256256609322773150118907 - 0.1256256609322773150118907j) The `U` function may be singular at `z = 0`:: >>> hyperu(1.5, 2, 0) +inf >>> hyperu(1.5, -2, 0) 0.1719434921288400112603671 Verifying the differential equation:: >>> a, b = 1.5, 2 >>> f = lambda z: hyperu(a,b,z) >>> for z in [-10, 3, 3+4j]: ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) ... 0.0 0.0 0.0 An integral representation:: >>> a,b,z = 2, 3.5, 4.25 >>> hyperu(a,b,z) 0.06674960718150520648014567 >>> quad(lambda t: exp(-z*t)*t**(a-1)*(1+t)**(b-a-1),[0,inf]) / gamma(a) 0.06674960718150520648014567 [1] http://www.math.ucla.edu/~cbm/aands/page_504.htm """ hyp2f0 = r""" Gives the hypergeometric function `\,_2F_0`, defined formally by the series .. math :: \,_2F_0(a,b;;z) = \sum_{n=0}^{\infty} (a)_n (b)_n \frac{z^n}{n!}. This series usually does not converge. For small enough `z`, it can be viewed as an asymptotic series that may be summed directly with an appropriate truncation. When this is not the case, :func:`~mpmath.hyp2f0` gives a regularized sum, or equivalently, it uses a representation in terms of the hypergeometric U function [1]. The series also converges when either `a` or `b` is a nonpositive integer, as it then terminates into a polynomial after `-a` or `-b` terms. **Examples** Evaluation is supported for arbitrary complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hyp2f0((2,3), 1.25, -100) 0.07095851870980052763312791 >>> hyp2f0((2,3), 1.25, 100) (-0.03254379032170590665041131 + 0.07269254613282301012735797j) >>> hyp2f0(-0.75, 1-j, 4j) (-0.3579987031082732264862155 - 3.052951783922142735255881j) Even with real arguments, the regularized value of 2F0 is often complex-valued, but the imaginary part decreases exponentially as `z \to 0`. In the following example, the first call uses complex evaluation while the second has a small enough `z` to evaluate using the direct series and thus the returned value is strictly real (this should be taken to indicate that the imaginary part is less than ``eps``):: >>> mp.dps = 15 >>> hyp2f0(1.5, 0.5, 0.05) (1.04166637647907 + 8.34584913683906e-8j) >>> hyp2f0(1.5, 0.5, 0.0005) 1.00037535207621 The imaginary part can be retrieved by increasing the working precision:: >>> mp.dps = 80 >>> nprint(hyp2f0(1.5, 0.5, 0.009).imag) 1.23828e-46 In the polynomial case (the series terminating), 2F0 can evaluate exactly:: >>> mp.dps = 15 >>> hyp2f0(-6,-6,2) 291793.0 >>> identify(hyp2f0(-2,1,0.25)) '(5/8)' The coefficients of the polynomials can be recovered using Taylor expansion:: >>> nprint(taylor(lambda x: hyp2f0(-3,0.5,x), 0, 10)) [1.0, -1.5, 2.25, -1.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] >>> nprint(taylor(lambda x: hyp2f0(-4,0.5,x), 0, 10)) [1.0, -2.0, 4.5, -7.5, 6.5625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [1] http://www.math.ucla.edu/~cbm/aands/page_504.htm """ gammainc = r""" ``gammainc(z, a=0, b=inf)`` computes the (generalized) incomplete gamma function with integration limits `[a, b]`: .. math :: \Gamma(z,a,b) = \int_a^b t^{z-1} e^{-t} \, dt The generalized incomplete gamma function reduces to the following special cases when one or both endpoints are fixed: * `\Gamma(z,0,\infty)` is the standard ("complete") gamma function, `\Gamma(z)` (available directly as the mpmath function :func:`~mpmath.gamma`) * `\Gamma(z,a,\infty)` is the "upper" incomplete gamma function, `\Gamma(z,a)` * `\Gamma(z,0,b)` is the "lower" incomplete gamma function, `\gamma(z,b)`. Of course, we have `\Gamma(z,0,x) + \Gamma(z,x,\infty) = \Gamma(z)` for all `z` and `x`. Note however that some authors reverse the order of the arguments when defining the lower and upper incomplete gamma function, so one should be careful to get the correct definition. If also given the keyword argument ``regularized=True``, :func:`~mpmath.gammainc` computes the "regularized" incomplete gamma function .. math :: P(z,a,b) = \frac{\Gamma(z,a,b)}{\Gamma(z)}. **Examples** We can compare with numerical quadrature to verify that :func:`~mpmath.gammainc` computes the integral in the definition:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> gammainc(2+3j, 4, 10) (0.00977212668627705160602312 - 0.0770637306312989892451977j) >>> quad(lambda t: t**(2+3j-1) * exp(-t), [4, 10]) (0.00977212668627705160602312 - 0.0770637306312989892451977j) Argument symmetries follow directly from the integral definition:: >>> gammainc(3, 4, 5) + gammainc(3, 5, 4) 0.0 >>> gammainc(3,0,2) + gammainc(3,2,4); gammainc(3,0,4) 1.523793388892911312363331 1.523793388892911312363331 >>> findroot(lambda z: gammainc(2,z,3), 1) 3.0 Evaluation for arbitrarily large arguments:: >>> gammainc(10, 100) 4.083660630910611272288592e-26 >>> gammainc(10, 10000000000000000) 5.290402449901174752972486e-4342944819032375 >>> gammainc(3+4j, 1000000+1000000j) (-1.257913707524362408877881e-434284 + 2.556691003883483531962095e-434284j) Evaluation of a generalized incomplete gamma function automatically chooses the representation that gives a more accurate result, depending on which parameter is larger:: >>> gammainc(10000000, 3) - gammainc(10000000, 2) # Bad 0.0 >>> gammainc(10000000, 2, 3) # Good 1.755146243738946045873491e+4771204 >>> gammainc(2, 0, 100000001) - gammainc(2, 0, 100000000) # Bad 0.0 >>> gammainc(2, 100000000, 100000001) # Good 4.078258353474186729184421e-43429441 The incomplete gamma functions satisfy simple recurrence relations:: >>> mp.dps = 25 >>> z, a = mpf(3.5), mpf(2) >>> gammainc(z+1, a); z*gammainc(z,a) + a**z*exp(-a) 10.60130296933533459267329 10.60130296933533459267329 >>> gammainc(z+1,0,a); z*gammainc(z,0,a) - a**z*exp(-a) 1.030425427232114336470932 1.030425427232114336470932 Evaluation at integers and poles:: >>> gammainc(-3, -4, -5) (-0.2214577048967798566234192 + 0.0j) >>> gammainc(-3, 0, 5) +inf If `z` is an integer, the recurrence reduces the incomplete gamma function to `P(a) \exp(-a) + Q(b) \exp(-b)` where `P` and `Q` are polynomials:: >>> gammainc(1, 2); exp(-2) 0.1353352832366126918939995 0.1353352832366126918939995 >>> mp.dps = 50 >>> identify(gammainc(6, 1, 2), ['exp(-1)', 'exp(-2)']) '(326*exp(-1) + (-872)*exp(-2))' The incomplete gamma functions reduce to functions such as the exponential integral Ei and the error function for special arguments:: >>> mp.dps = 25 >>> gammainc(0, 4); -ei(-4) 0.00377935240984890647887486 0.00377935240984890647887486 >>> gammainc(0.5, 0, 2); sqrt(pi)*erf(sqrt(2)) 1.691806732945198336509541 1.691806732945198336509541 """ erf = r""" Computes the error function, `\mathrm{erf}(x)`. The error function is the normalized antiderivative of the Gaussian function `\exp(-t^2)`. More precisely, .. math:: \mathrm{erf}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(-t^2) \,dt **Basic examples** Simple values and limits include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> erf(0) 0.0 >>> erf(1) 0.842700792949715 >>> erf(-1) -0.842700792949715 >>> erf(inf) 1.0 >>> erf(-inf) -1.0 For large real `x`, `\mathrm{erf}(x)` approaches 1 very rapidly:: >>> erf(3) 0.999977909503001 >>> erf(5) 0.999999999998463 The error function is an odd function:: >>> nprint(chop(taylor(erf, 0, 5))) [0.0, 1.12838, 0.0, -0.376126, 0.0, 0.112838] :func:`~mpmath.erf` implements arbitrary-precision evaluation and supports complex numbers:: >>> mp.dps = 50 >>> erf(0.5) 0.52049987781304653768274665389196452873645157575796 >>> mp.dps = 25 >>> erf(1+j) (1.316151281697947644880271 + 0.1904534692378346862841089j) Evaluation is supported for large arguments:: >>> mp.dps = 25 >>> erf('1e1000') 1.0 >>> erf('-1e1000') -1.0 >>> erf('1e-1000') 1.128379167095512573896159e-1000 >>> erf('1e7j') (0.0 + 8.593897639029319267398803e+43429448190317j) >>> erf('1e7+1e7j') (0.9999999858172446172631323 + 3.728805278735270407053139e-8j) **Related functions** See also :func:`~mpmath.erfc`, which is more accurate for large `x`, and :func:`~mpmath.erfi` which gives the antiderivative of `\exp(t^2)`. The Fresnel integrals :func:`~mpmath.fresnels` and :func:`~mpmath.fresnelc` are also related to the error function. """ erfc = r""" Computes the complementary error function, `\mathrm{erfc}(x) = 1-\mathrm{erf}(x)`. This function avoids cancellation that occurs when naively computing the complementary error function as ``1-erf(x)``:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> 1 - erf(10) 0.0 >>> erfc(10) 2.08848758376254e-45 :func:`~mpmath.erfc` works accurately even for ludicrously large arguments:: >>> erfc(10**10) 4.3504398860243e-43429448190325182776 Complex arguments are supported:: >>> erfc(500+50j) (1.19739830969552e-107492 + 1.46072418957528e-107491j) """ erfi = r""" Computes the imaginary error function, `\mathrm{erfi}(x)`. The imaginary error function is defined in analogy with the error function, but with a positive sign in the integrand: .. math :: \mathrm{erfi}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(t^2) \,dt Whereas the error function rapidly converges to 1 as `x` grows, the imaginary error function rapidly diverges to infinity. The functions are related as `\mathrm{erfi}(x) = -i\,\mathrm{erf}(ix)` for all complex numbers `x`. **Examples** Basic values and limits:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> erfi(0) 0.0 >>> erfi(1) 1.65042575879754 >>> erfi(-1) -1.65042575879754 >>> erfi(inf) +inf >>> erfi(-inf) -inf Note the symmetry between erf and erfi:: >>> erfi(3j) (0.0 + 0.999977909503001j) >>> erf(3) 0.999977909503001 >>> erf(1+2j) (-0.536643565778565 - 5.04914370344703j) >>> erfi(2+1j) (-5.04914370344703 - 0.536643565778565j) Large arguments are supported:: >>> erfi(1000) 1.71130938718796e+434291 >>> erfi(10**10) 7.3167287567024e+43429448190325182754 >>> erfi(-10**10) -7.3167287567024e+43429448190325182754 >>> erfi(1000-500j) (2.49895233563961e+325717 + 2.6846779342253e+325717j) >>> erfi(100000j) (0.0 + 1.0j) >>> erfi(-100000j) (0.0 - 1.0j) """ erfinv = r""" Computes the inverse error function, satisfying .. math :: \mathrm{erf}(\mathrm{erfinv}(x)) = \mathrm{erfinv}(\mathrm{erf}(x)) = x. This function is defined only for `-1 \le x \le 1`. **Examples** Special values include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> erfinv(0) 0.0 >>> erfinv(1) +inf >>> erfinv(-1) -inf The domain is limited to the standard interval:: >>> erfinv(2) Traceback (most recent call last): ... ValueError: erfinv(x) is defined only for -1 <= x <= 1 It is simple to check that :func:`~mpmath.erfinv` computes inverse values of :func:`~mpmath.erf` as promised:: >>> erf(erfinv(0.75)) 0.75 >>> erf(erfinv(-0.995)) -0.995 :func:`~mpmath.erfinv` supports arbitrary-precision evaluation:: >>> mp.dps = 50 >>> x = erf(2) >>> x 0.99532226501895273416206925636725292861089179704006 >>> erfinv(x) 2.0 A definite integral involving the inverse error function:: >>> mp.dps = 15 >>> quad(erfinv, [0, 1]) 0.564189583547756 >>> 1/sqrt(pi) 0.564189583547756 The inverse error function can be used to generate random numbers with a Gaussian distribution (although this is a relatively inefficient algorithm):: >>> nprint([erfinv(2*rand()-1) for n in range(6)]) # doctest: +SKIP [-0.586747, 1.10233, -0.376796, 0.926037, -0.708142, -0.732012] """ npdf = r""" ``npdf(x, mu=0, sigma=1)`` evaluates the probability density function of a normal distribution with mean value `\mu` and variance `\sigma^2`. Elementary properties of the probability distribution can be verified using numerical integration:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> quad(npdf, [-inf, inf]) 1.0 >>> quad(lambda x: npdf(x, 3), [3, inf]) 0.5 >>> quad(lambda x: npdf(x, 3, 2), [3, inf]) 0.5 See also :func:`~mpmath.ncdf`, which gives the cumulative distribution. """ ncdf = r""" ``ncdf(x, mu=0, sigma=1)`` evaluates the cumulative distribution function of a normal distribution with mean value `\mu` and variance `\sigma^2`. See also :func:`~mpmath.npdf`, which gives the probability density. Elementary properties include:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> ncdf(pi, mu=pi) 0.5 >>> ncdf(-inf) 0.0 >>> ncdf(+inf) 1.0 The cumulative distribution is the integral of the density function having identical mu and sigma:: >>> mp.dps = 15 >>> diff(ncdf, 2) 0.053990966513188 >>> npdf(2) 0.053990966513188 >>> diff(lambda x: ncdf(x, 1, 0.5), 0) 0.107981933026376 >>> npdf(0, 1, 0.5) 0.107981933026376 """ expint = r""" :func:`~mpmath.expint(n,z)` gives the generalized exponential integral or En-function, .. math :: \mathrm{E}_n(z) = \int_1^{\infty} \frac{e^{-zt}}{t^n} dt, where `n` and `z` may both be complex numbers. The case with `n = 1` is also given by :func:`~mpmath.e1`. **Examples** Evaluation at real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> expint(1, 6.25) 0.0002704758872637179088496194 >>> expint(-3, 2+3j) (0.00299658467335472929656159 + 0.06100816202125885450319632j) >>> expint(2+3j, 4-5j) (0.001803529474663565056945248 - 0.002235061547756185403349091j) At negative integer values of `n`, `E_n(z)` reduces to a rational-exponential function:: >>> f = lambda n, z: fac(n)*sum(z**k/fac(k-1) for k in range(1,n+2))/\ ... exp(z)/z**(n+2) >>> n = 3 >>> z = 1/pi >>> expint(-n,z) 584.2604820613019908668219 >>> f(n,z) 584.2604820613019908668219 >>> n = 5 >>> expint(-n,z) 115366.5762594725451811138 >>> f(n,z) 115366.5762594725451811138 """ e1 = r""" Computes the exponential integral `\mathrm{E}_1(z)`, given by .. math :: \mathrm{E}_1(z) = \int_z^{\infty} \frac{e^{-t}}{t} dt. This is equivalent to :func:`~mpmath.expint` with `n = 1`. **Examples** Two ways to evaluate this function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> e1(6.25) 0.0002704758872637179088496194 >>> expint(1,6.25) 0.0002704758872637179088496194 The E1-function is essentially the same as the Ei-function (:func:`~mpmath.ei`) with negated argument, except for an imaginary branch cut term:: >>> e1(2.5) 0.02491491787026973549562801 >>> -ei(-2.5) 0.02491491787026973549562801 >>> e1(-2.5) (-7.073765894578600711923552 - 3.141592653589793238462643j) >>> -ei(2.5) -7.073765894578600711923552 """ ei = r""" Computes the exponential integral or Ei-function, `\mathrm{Ei}(x)`. The exponential integral is defined as .. math :: \mathrm{Ei}(x) = \int_{-\infty\,}^x \frac{e^t}{t} \, dt. When the integration range includes `t = 0`, the exponential integral is interpreted as providing the Cauchy principal value. For real `x`, the Ei-function behaves roughly like `\mathrm{Ei}(x) \approx \exp(x) + \log(|x|)`. The Ei-function is related to the more general family of exponential integral functions denoted by `E_n`, which are available as :func:`~mpmath.expint`. **Basic examples** Some basic values and limits are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> ei(0) -inf >>> ei(1) 1.89511781635594 >>> ei(inf) +inf >>> ei(-inf) 0.0 For `x < 0`, the defining integral can be evaluated numerically as a reference:: >>> ei(-4) -0.00377935240984891 >>> quad(lambda t: exp(t)/t, [-inf, -4]) -0.00377935240984891 :func:`~mpmath.ei` supports complex arguments and arbitrary precision evaluation:: >>> mp.dps = 50 >>> ei(pi) 10.928374389331410348638445906907535171566338835056 >>> mp.dps = 25 >>> ei(3+4j) (-4.154091651642689822535359 + 4.294418620024357476985535j) **Related functions** The exponential integral is closely related to the logarithmic integral. See :func:`~mpmath.li` for additional information. The exponential integral is related to the hyperbolic and trigonometric integrals (see :func:`~mpmath.chi`, :func:`~mpmath.shi`, :func:`~mpmath.ci`, :func:`~mpmath.si`) similarly to how the ordinary exponential function is related to the hyperbolic and trigonometric functions:: >>> mp.dps = 15 >>> ei(3) 9.93383257062542 >>> chi(3) + shi(3) 9.93383257062542 >>> chop(ci(3j) - j*si(3j) - pi*j/2) 9.93383257062542 Beware that logarithmic corrections, as in the last example above, are required to obtain the correct branch in general. For details, see [1]. The exponential integral is also a special case of the hypergeometric function `\,_2F_2`:: >>> z = 0.6 >>> z*hyper([1,1],[2,2],z) + (ln(z)-ln(1/z))/2 + euler 0.769881289937359 >>> ei(z) 0.769881289937359 **References** 1. Relations between Ei and other functions: http://functions.wolfram.com/GammaBetaErf/ExpIntegralEi/27/01/ 2. Abramowitz & Stegun, section 5: http://www.math.sfu.ca/~cbm/aands/page_228.htm 3. Asymptotic expansion for Ei: http://mathworld.wolfram.com/En-Function.html """ li = r""" Computes the logarithmic integral or li-function `\mathrm{li}(x)`, defined by .. math :: \mathrm{li}(x) = \int_0^x \frac{1}{\log t} \, dt The logarithmic integral has a singularity at `x = 1`. Alternatively, ``li(x, offset=True)`` computes the offset logarithmic integral (used in number theory) .. math :: \mathrm{Li}(x) = \int_2^x \frac{1}{\log t} \, dt. These two functions are related via the simple identity `\mathrm{Li}(x) = \mathrm{li}(x) - \mathrm{li}(2)`. The logarithmic integral should also not be confused with the polylogarithm (also denoted by Li), which is implemented as :func:`~mpmath.polylog`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> li(0) 0.0 >>> li(1) -inf >>> li(1) -inf >>> li(2) 1.04516378011749278484458888919 >>> findroot(li, 2) 1.45136923488338105028396848589 >>> li(inf) +inf >>> li(2, offset=True) 0.0 >>> li(1, offset=True) -inf >>> li(0, offset=True) -1.04516378011749278484458888919 >>> li(10, offset=True) 5.12043572466980515267839286347 The logarithmic integral can be evaluated for arbitrary complex arguments:: >>> mp.dps = 20 >>> li(3+4j) (3.1343755504645775265 + 2.6769247817778742392j) The logarithmic integral is related to the exponential integral:: >>> ei(log(3)) 2.1635885946671919729 >>> li(3) 2.1635885946671919729 The logarithmic integral grows like `O(x/\log(x))`:: >>> mp.dps = 15 >>> x = 10**100 >>> x/log(x) 4.34294481903252e+97 >>> li(x) 4.3619719871407e+97 The prime number theorem states that the number of primes less than `x` is asymptotic to `\mathrm{Li}(x)` (equivalently `\mathrm{li}(x)`). For example, it is known that there are exactly 1,925,320,391,606,803,968,923 prime numbers less than `10^{23}` [1]. The logarithmic integral provides a very accurate estimate:: >>> li(10**23, offset=True) 1.92532039161405e+21 A definite integral is:: >>> quad(li, [0, 1]) -0.693147180559945 >>> -ln(2) -0.693147180559945 **References** 1. http://mathworld.wolfram.com/PrimeCountingFunction.html 2. http://mathworld.wolfram.com/LogarithmicIntegral.html """ ci = r""" Computes the cosine integral, .. math :: \mathrm{Ci}(x) = -\int_x^{\infty} \frac{\cos t}{t}\,dt = \gamma + \log x + \int_0^x \frac{\cos t - 1}{t}\,dt **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ci(0) -inf >>> ci(1) 0.3374039229009681346626462 >>> ci(pi) 0.07366791204642548599010096 >>> ci(inf) 0.0 >>> ci(-inf) (0.0 + 3.141592653589793238462643j) >>> ci(2+3j) (1.408292501520849518759125 - 2.983617742029605093121118j) The cosine integral behaves roughly like the sinc function (see :func:`~mpmath.sinc`) for large real `x`:: >>> ci(10**10) -4.875060251748226537857298e-11 >>> sinc(10**10) -4.875060250875106915277943e-11 >>> chop(limit(ci, inf)) 0.0 It has infinitely many roots on the positive real axis:: >>> findroot(ci, 1) 0.6165054856207162337971104 >>> findroot(ci, 2) 3.384180422551186426397851 Evaluation is supported for `z` anywhere in the complex plane:: >>> ci(10**6*(1+j)) (4.449410587611035724984376e+434287 + 9.75744874290013526417059e+434287j) We can evaluate the defining integral as a reference:: >>> mp.dps = 15 >>> -quadosc(lambda t: cos(t)/t, [5, inf], omega=1) -0.190029749656644 >>> ci(5) -0.190029749656644 Some infinite series can be evaluated using the cosine integral:: >>> nsum(lambda k: (-1)**k/(fac(2*k)*(2*k)), [1,inf]) -0.239811742000565 >>> ci(1) - euler -0.239811742000565 """ si = r""" Computes the sine integral, .. math :: \mathrm{Si}(x) = \int_0^x \frac{\sin t}{t}\,dt. The sine integral is thus the antiderivative of the sinc function (see :func:`~mpmath.sinc`). **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> si(0) 0.0 >>> si(1) 0.9460830703671830149413533 >>> si(-1) -0.9460830703671830149413533 >>> si(pi) 1.851937051982466170361053 >>> si(inf) 1.570796326794896619231322 >>> si(-inf) -1.570796326794896619231322 >>> si(2+3j) (4.547513889562289219853204 + 1.399196580646054789459839j) The sine integral approaches `\pi/2` for large real `x`:: >>> si(10**10) 1.570796326707584656968511 >>> pi/2 1.570796326794896619231322 Evaluation is supported for `z` anywhere in the complex plane:: >>> si(10**6*(1+j)) (-9.75744874290013526417059e+434287 + 4.449410587611035724984376e+434287j) We can evaluate the defining integral as a reference:: >>> mp.dps = 15 >>> quad(sinc, [0, 5]) 1.54993124494467 >>> si(5) 1.54993124494467 Some infinite series can be evaluated using the sine integral:: >>> nsum(lambda k: (-1)**k/(fac(2*k+1)*(2*k+1)), [0,inf]) 0.946083070367183 >>> si(1) 0.946083070367183 """ chi = r""" Computes the hyperbolic cosine integral, defined in analogy with the cosine integral (see :func:`~mpmath.ci`) as .. math :: \mathrm{Chi}(x) = -\int_x^{\infty} \frac{\cosh t}{t}\,dt = \gamma + \log x + \int_0^x \frac{\cosh t - 1}{t}\,dt Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> chi(0) -inf >>> chi(1) 0.8378669409802082408946786 >>> chi(inf) +inf >>> findroot(chi, 0.5) 0.5238225713898644064509583 >>> chi(2+3j) (-0.1683628683277204662429321 + 2.625115880451325002151688j) Evaluation is supported for `z` anywhere in the complex plane:: >>> chi(10**6*(1+j)) (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) """ shi = r""" Computes the hyperbolic sine integral, defined in analogy with the sine integral (see :func:`~mpmath.si`) as .. math :: \mathrm{Shi}(x) = \int_0^x \frac{\sinh t}{t}\,dt. Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> shi(0) 0.0 >>> shi(1) 1.057250875375728514571842 >>> shi(-1) -1.057250875375728514571842 >>> shi(inf) +inf >>> shi(2+3j) (-0.1931890762719198291678095 + 2.645432555362369624818525j) Evaluation is supported for `z` anywhere in the complex plane:: >>> shi(10**6*(1+j)) (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) """ fresnels = r""" Computes the Fresnel sine integral .. math :: S(x) = \int_0^x \sin\left(\frac{\pi t^2}{2}\right) \,dt Note that some sources define this function without the normalization factor `\pi/2`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> fresnels(0) 0.0 >>> fresnels(inf) 0.5 >>> fresnels(-inf) -0.5 >>> fresnels(1) 0.4382591473903547660767567 >>> fresnels(1+2j) (36.72546488399143842838788 + 15.58775110440458732748279j) Comparing with the definition:: >>> fresnels(3) 0.4963129989673750360976123 >>> quad(lambda t: sin(pi*t**2/2), [0,3]) 0.4963129989673750360976123 """ fresnelc = r""" Computes the Fresnel cosine integral .. math :: C(x) = \int_0^x \cos\left(\frac{\pi t^2}{2}\right) \,dt Note that some sources define this function without the normalization factor `\pi/2`. **Examples** Some basic values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> fresnelc(0) 0.0 >>> fresnelc(inf) 0.5 >>> fresnelc(-inf) -0.5 >>> fresnelc(1) 0.7798934003768228294742064 >>> fresnelc(1+2j) (16.08787137412548041729489 - 36.22568799288165021578758j) Comparing with the definition:: >>> fresnelc(3) 0.6057207892976856295561611 >>> quad(lambda t: cos(pi*t**2/2), [0,3]) 0.6057207892976856295561611 """ airyai = r""" Computes the Airy function `\operatorname{Ai}(z)`, which is the solution of the Airy differential equation `f''(z) - z f(z) = 0` with initial conditions .. math :: \operatorname{Ai}(0) = \frac{1}{3^{2/3}\Gamma\left(\frac{2}{3}\right)} \operatorname{Ai}'(0) = -\frac{1}{3^{1/3}\Gamma\left(\frac{1}{3}\right)}. Other common ways of defining the Ai-function include integrals such as .. math :: \operatorname{Ai}(x) = \frac{1}{\pi} \int_0^{\infty} \cos\left(\frac{1}{3}t^3+xt\right) dt \qquad x \in \mathbb{R} \operatorname{Ai}(z) = \frac{\sqrt{3}}{2\pi} \int_0^{\infty} \exp\left(-\frac{t^3}{3}-\frac{z^3}{3t^3}\right) dt. The Ai-function is an entire function with a turning point, behaving roughly like a slowly decaying sine wave for `z < 0` and like a rapidly decreasing exponential for `z > 0`. A second solution of the Airy differential equation is given by `\operatorname{Bi}(z)` (see :func:`~mpmath.airybi`). Optionally, with *derivative=alpha*, :func:`airyai` can compute the `\alpha`-th order fractional derivative with respect to `z`. For `\alpha = n = 1,2,3,\ldots` this gives the derivative `\operatorname{Ai}^{(n)}(z)`, and for `\alpha = -n = -1,-2,-3,\ldots` this gives the `n`-fold iterated integral .. math :: f_0(z) = \operatorname{Ai}(z) f_n(z) = \int_0^z f_{n-1}(t) dt. The Ai-function has infinitely many zeros, all located along the negative half of the real axis. They can be computed with :func:`~mpmath.airyaizero`. **Plots** .. literalinclude :: /modules/mpmath/plots/ai.py .. image :: /modules/mpmath/plots/ai.png .. literalinclude :: /modules/mpmath/plots/ai_c.py .. image :: /modules/mpmath/plots/ai_c.png **Basic examples** Limits and values include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airyai(0); 1/(power(3,'2/3')*gamma('2/3')) 0.3550280538878172392600632 0.3550280538878172392600632 >>> airyai(1) 0.1352924163128814155241474 >>> airyai(-1) 0.5355608832923521187995166 >>> airyai(inf); airyai(-inf) 0.0 0.0 Evaluation is supported for large magnitudes of the argument:: >>> airyai(-100) 0.1767533932395528780908311 >>> airyai(100) 2.634482152088184489550553e-291 >>> airyai(50+50j) (-5.31790195707456404099817e-68 - 1.163588003770709748720107e-67j) >>> airyai(-50+50j) (1.041242537363167632587245e+158 + 3.347525544923600321838281e+157j) Huge arguments are also fine:: >>> airyai(10**10) 1.162235978298741779953693e-289529654602171 >>> airyai(-10**10) 0.0001736206448152818510510181 >>> w = airyai(10**10*(1+j)) >>> w.real 5.711508683721355528322567e-186339621747698 >>> w.imag 1.867245506962312577848166e-186339621747697 The first root of the Ai-function is:: >>> findroot(airyai, -2) -2.338107410459767038489197 >>> airyaizero(1) -2.338107410459767038489197 **Properties and relations** Verifying the Airy differential equation:: >>> for z in [-3.4, 0, 2.5, 1+2j]: ... chop(airyai(z,2) - z*airyai(z)) ... 0.0 0.0 0.0 0.0 The first few terms of the Taylor series expansion around `z = 0` (every third term is zero):: >>> nprint(taylor(airyai, 0, 5)) [0.355028, -0.258819, 0.0, 0.0591713, -0.0215683, 0.0] The Airy functions satisfy the Wronskian relation `\operatorname{Ai}(z) \operatorname{Bi}'(z) - \operatorname{Ai}'(z) \operatorname{Bi}(z) = 1/\pi`:: >>> z = -0.5 >>> airyai(z)*airybi(z,1) - airyai(z,1)*airybi(z) 0.3183098861837906715377675 >>> 1/pi 0.3183098861837906715377675 The Airy functions can be expressed in terms of Bessel functions of order `\pm 1/3`. For `\Re[z] \le 0`, we have:: >>> z = -3 >>> airyai(z) -0.3788142936776580743472439 >>> y = 2*power(-z,'3/2')/3 >>> (sqrt(-z) * (besselj('1/3',y) + besselj('-1/3',y)))/3 -0.3788142936776580743472439 **Derivatives and integrals** Derivatives of the Ai-function (directly and using :func:`~mpmath.diff`):: >>> airyai(-3,1); diff(airyai,-3) 0.3145837692165988136507873 0.3145837692165988136507873 >>> airyai(-3,2); diff(airyai,-3,2) 1.136442881032974223041732 1.136442881032974223041732 >>> airyai(1000,1); diff(airyai,1000) -2.943133917910336090459748e-9156 -2.943133917910336090459748e-9156 Several derivatives at `z = 0`:: >>> airyai(0,0); airyai(0,1); airyai(0,2) 0.3550280538878172392600632 -0.2588194037928067984051836 0.0 >>> airyai(0,3); airyai(0,4); airyai(0,5) 0.3550280538878172392600632 -0.5176388075856135968103671 0.0 >>> airyai(0,15); airyai(0,16); airyai(0,17) 1292.30211615165475090663 -3188.655054727379756351861 0.0 The integral of the Ai-function:: >>> airyai(3,-1); quad(airyai, [0,3]) 0.3299203760070217725002701 0.3299203760070217725002701 >>> airyai(-10,-1); quad(airyai, [0,-10]) -0.765698403134212917425148 -0.765698403134212917425148 Integrals of high or fractional order:: >>> airyai(-2,0.5); differint(airyai,-2,0.5,0) (0.0 + 0.2453596101351438273844725j) (0.0 + 0.2453596101351438273844725j) >>> airyai(-2,-4); differint(airyai,-2,-4,0) 0.2939176441636809580339365 0.2939176441636809580339365 >>> airyai(0,-1); airyai(0,-2); airyai(0,-3) 0.0 0.0 0.0 Integrals of the Ai-function can be evaluated at limit points:: >>> airyai(-1000000,-1); airyai(-inf,-1) -0.6666843728311539978751512 -0.6666666666666666666666667 >>> airyai(10,-1); airyai(+inf,-1) 0.3333333332991690159427932 0.3333333333333333333333333 >>> airyai(+inf,-2); airyai(+inf,-3) +inf +inf >>> airyai(-1000000,-2); airyai(-inf,-2) 666666.4078472650651209742 +inf >>> airyai(-1000000,-3); airyai(-inf,-3) -333333074513.7520264995733 -inf **References** 1. [DLMF]_ Chapter 9: Airy and Related Functions 2. [WolframFunctions]_ section: Bessel-Type Functions """ airybi = r""" Computes the Airy function `\operatorname{Bi}(z)`, which is the solution of the Airy differential equation `f''(z) - z f(z) = 0` with initial conditions .. math :: \operatorname{Bi}(0) = \frac{1}{3^{1/6}\Gamma\left(\frac{2}{3}\right)} \operatorname{Bi}'(0) = \frac{3^{1/6}}{\Gamma\left(\frac{1}{3}\right)}. Like the Ai-function (see :func:`~mpmath.airyai`), the Bi-function is oscillatory for `z < 0`, but it grows rather than decreases for `z > 0`. Optionally, as for :func:`~mpmath.airyai`, derivatives, integrals and fractional derivatives can be computed with the *derivative* parameter. The Bi-function has infinitely many zeros along the negative half-axis, as well as complex zeros, which can all be computed with :func:`~mpmath.airybizero`. **Plots** .. literalinclude :: /modules/mpmath/plots/bi.py .. image :: /modules/mpmath/plots/bi.png .. literalinclude :: /modules/mpmath/plots/bi_c.py .. image :: /modules/mpmath/plots/bi_c.png **Basic examples** Limits and values include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airybi(0); 1/(power(3,'1/6')*gamma('2/3')) 0.6149266274460007351509224 0.6149266274460007351509224 >>> airybi(1) 1.207423594952871259436379 >>> airybi(-1) 0.10399738949694461188869 >>> airybi(inf); airybi(-inf) +inf 0.0 Evaluation is supported for large magnitudes of the argument:: >>> airybi(-100) 0.02427388768016013160566747 >>> airybi(100) 6.041223996670201399005265e+288 >>> airybi(50+50j) (-5.322076267321435669290334e+63 + 1.478450291165243789749427e+65j) >>> airybi(-50+50j) (-3.347525544923600321838281e+157 + 1.041242537363167632587245e+158j) Huge arguments:: >>> airybi(10**10) 1.369385787943539818688433e+289529654602165 >>> airybi(-10**10) 0.001775656141692932747610973 >>> w = airybi(10**10*(1+j)) >>> w.real -6.559955931096196875845858e+186339621747689 >>> w.imag -6.822462726981357180929024e+186339621747690 The first real root of the Bi-function is:: >>> findroot(airybi, -1); airybizero(1) -1.17371322270912792491998 -1.17371322270912792491998 **Properties and relations** Verifying the Airy differential equation:: >>> for z in [-3.4, 0, 2.5, 1+2j]: ... chop(airybi(z,2) - z*airybi(z)) ... 0.0 0.0 0.0 0.0 The first few terms of the Taylor series expansion around `z = 0` (every third term is zero):: >>> nprint(taylor(airybi, 0, 5)) [0.614927, 0.448288, 0.0, 0.102488, 0.0373574, 0.0] The Airy functions can be expressed in terms of Bessel functions of order `\pm 1/3`. For `\Re[z] \le 0`, we have:: >>> z = -3 >>> airybi(z) -0.1982896263749265432206449 >>> p = 2*power(-z,'3/2')/3 >>> sqrt(-mpf(z)/3)*(besselj('-1/3',p) - besselj('1/3',p)) -0.1982896263749265432206449 **Derivatives and integrals** Derivatives of the Bi-function (directly and using :func:`~mpmath.diff`):: >>> airybi(-3,1); diff(airybi,-3) -0.675611222685258537668032 -0.675611222685258537668032 >>> airybi(-3,2); diff(airybi,-3,2) 0.5948688791247796296619346 0.5948688791247796296619346 >>> airybi(1000,1); diff(airybi,1000) 1.710055114624614989262335e+9156 1.710055114624614989262335e+9156 Several derivatives at `z = 0`:: >>> airybi(0,0); airybi(0,1); airybi(0,2) 0.6149266274460007351509224 0.4482883573538263579148237 0.0 >>> airybi(0,3); airybi(0,4); airybi(0,5) 0.6149266274460007351509224 0.8965767147076527158296474 0.0 >>> airybi(0,15); airybi(0,16); airybi(0,17) 2238.332923903442675949357 5522.912562599140729510628 0.0 The integral of the Bi-function:: >>> airybi(3,-1); quad(airybi, [0,3]) 10.06200303130620056316655 10.06200303130620056316655 >>> airybi(-10,-1); quad(airybi, [0,-10]) -0.01504042480614002045135483 -0.01504042480614002045135483 Integrals of high or fractional order:: >>> airybi(-2,0.5); differint(airybi, -2, 0.5, 0) (0.0 + 0.5019859055341699223453257j) (0.0 + 0.5019859055341699223453257j) >>> airybi(-2,-4); differint(airybi,-2,-4,0) 0.2809314599922447252139092 0.2809314599922447252139092 >>> airybi(0,-1); airybi(0,-2); airybi(0,-3) 0.0 0.0 0.0 Integrals of the Bi-function can be evaluated at limit points:: >>> airybi(-1000000,-1); airybi(-inf,-1) 0.000002191261128063434047966873 0.0 >>> airybi(10,-1); airybi(+inf,-1) 147809803.1074067161675853 +inf >>> airybi(+inf,-2); airybi(+inf,-3) +inf +inf >>> airybi(-1000000,-2); airybi(-inf,-2) 0.4482883750599908479851085 0.4482883573538263579148237 >>> gamma('2/3')*power(3,'2/3')/(2*pi) 0.4482883573538263579148237 >>> airybi(-100000,-3); airybi(-inf,-3) -44828.52827206932872493133 -inf >>> airybi(-100000,-4); airybi(-inf,-4) 2241411040.437759489540248 +inf """ airyaizero = r""" Gives the `k`-th zero of the Airy Ai-function, i.e. the `k`-th number `a_k` ordered by magnitude for which `\operatorname{Ai}(a_k) = 0`. Optionally, with *derivative=1*, the corresponding zero `a'_k` of the derivative function, i.e. `\operatorname{Ai}'(a'_k) = 0`, is computed. **Examples** Some values of `a_k`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airyaizero(1) -2.338107410459767038489197 >>> airyaizero(2) -4.087949444130970616636989 >>> airyaizero(3) -5.520559828095551059129856 >>> airyaizero(1000) -281.0315196125215528353364 Some values of `a'_k`:: >>> airyaizero(1,1) -1.018792971647471089017325 >>> airyaizero(2,1) -3.248197582179836537875424 >>> airyaizero(3,1) -4.820099211178735639400616 >>> airyaizero(1000,1) -280.9378080358935070607097 Verification:: >>> chop(airyai(airyaizero(1))) 0.0 >>> chop(airyai(airyaizero(1,1),1)) 0.0 """ airybizero = r""" With *complex=False*, gives the `k`-th real zero of the Airy Bi-function, i.e. the `k`-th number `b_k` ordered by magnitude for which `\operatorname{Bi}(b_k) = 0`. With *complex=True*, gives the `k`-th complex zero in the upper half plane `\beta_k`. Also the conjugate `\overline{\beta_k}` is a zero. Optionally, with *derivative=1*, the corresponding zero `b'_k` or `\beta'_k` of the derivative function, i.e. `\operatorname{Bi}'(b'_k) = 0` or `\operatorname{Bi}'(\beta'_k) = 0`, is computed. **Examples** Some values of `b_k`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> airybizero(1) -1.17371322270912792491998 >>> airybizero(2) -3.271093302836352715680228 >>> airybizero(3) -4.830737841662015932667709 >>> airybizero(1000) -280.9378112034152401578834 Some values of `b_k`:: >>> airybizero(1,1) -2.294439682614123246622459 >>> airybizero(2,1) -4.073155089071828215552369 >>> airybizero(3,1) -5.512395729663599496259593 >>> airybizero(1000,1) -281.0315164471118527161362 Some values of `\beta_k`:: >>> airybizero(1,complex=True) (0.9775448867316206859469927 + 2.141290706038744575749139j) >>> airybizero(2,complex=True) (1.896775013895336346627217 + 3.627291764358919410440499j) >>> airybizero(3,complex=True) (2.633157739354946595708019 + 4.855468179979844983174628j) >>> airybizero(1000,complex=True) (140.4978560578493018899793 + 243.3907724215792121244867j) Some values of `\beta'_k`:: >>> airybizero(1,1,complex=True) (0.2149470745374305676088329 + 1.100600143302797880647194j) >>> airybizero(2,1,complex=True) (1.458168309223507392028211 + 2.912249367458445419235083j) >>> airybizero(3,1,complex=True) (2.273760763013482299792362 + 4.254528549217097862167015j) >>> airybizero(1000,1,complex=True) (140.4509972835270559730423 + 243.3096175398562811896208j) Verification:: >>> chop(airybi(airybizero(1))) 0.0 >>> chop(airybi(airybizero(1,1),1)) 0.0 >>> u = airybizero(1,complex=True) >>> chop(airybi(u)) 0.0 >>> chop(airybi(conj(u))) 0.0 The complex zeros (in the upper and lower half-planes respectively) asymptotically approach the rays `z = R \exp(\pm i \pi /3)`:: >>> arg(airybizero(1,complex=True)) 1.142532510286334022305364 >>> arg(airybizero(1000,complex=True)) 1.047271114786212061583917 >>> arg(airybizero(1000000,complex=True)) 1.047197624741816183341355 >>> pi/3 1.047197551196597746154214 """ ellipk = r""" Evaluates the complete elliptic integral of the first kind, `K(m)`, defined by .. math :: K(m) = \int_0^{\pi/2} \frac{dt}{\sqrt{1-m \sin^2 t}} \, = \, \frac{\pi}{2} \,_2F_1\left(\frac{1}{2}, \frac{1}{2}, 1, m\right). Note that the argument is the parameter `m = k^2`, not the modulus `k` which is sometimes used. **Plots** .. literalinclude :: /modules/mpmath/plots/ellipk.py .. image :: /modules/mpmath/plots/ellipk.png **Examples** Values and limits include:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipk(0) 1.570796326794896619231322 >>> ellipk(inf) (0.0 + 0.0j) >>> ellipk(-inf) 0.0 >>> ellipk(1) +inf >>> ellipk(-1) 1.31102877714605990523242 >>> ellipk(2) (1.31102877714605990523242 - 1.31102877714605990523242j) Verifying the defining integral and hypergeometric representation:: >>> ellipk(0.5) 1.85407467730137191843385 >>> quad(lambda t: (1-0.5*sin(t)**2)**-0.5, [0, pi/2]) 1.85407467730137191843385 >>> pi/2*hyp2f1(0.5,0.5,1,0.5) 1.85407467730137191843385 Evaluation is supported for arbitrary complex `m`:: >>> ellipk(3+4j) (0.9111955638049650086562171 + 0.6313342832413452438845091j) A definite integral:: >>> quad(ellipk, [0, 1]) 2.0 """ agm = r""" ``agm(a, b)`` computes the arithmetic-geometric mean of `a` and `b`, defined as the limit of the following iteration: .. math :: a_0 = a b_0 = b a_{n+1} = \frac{a_n+b_n}{2} b_{n+1} = \sqrt{a_n b_n} This function can be called with a single argument, computing `\mathrm{agm}(a,1) = \mathrm{agm}(1,a)`. **Examples** It is a well-known theorem that the geometric mean of two distinct positive numbers is less than the arithmetic mean. It follows that the arithmetic-geometric mean lies between the two means:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> a = mpf(3) >>> b = mpf(4) >>> sqrt(a*b) 3.46410161513775 >>> agm(a,b) 3.48202767635957 >>> (a+b)/2 3.5 The arithmetic-geometric mean is scale-invariant:: >>> agm(10*e, 10*pi) 29.261085515723 >>> 10*agm(e, pi) 29.261085515723 As an order-of-magnitude estimate, `\mathrm{agm}(1,x) \approx x` for large `x`:: >>> agm(10**10) 643448704.760133 >>> agm(10**50) 1.34814309345871e+48 For tiny `x`, `\mathrm{agm}(1,x) \approx -\pi/(2 \log(x/4))`:: >>> agm('0.01') 0.262166887202249 >>> -pi/2/log('0.0025') 0.262172347753122 The arithmetic-geometric mean can also be computed for complex numbers:: >>> agm(3, 2+j) (2.51055133276184 + 0.547394054060638j) The AGM iteration converges very quickly (each step doubles the number of correct digits), so :func:`~mpmath.agm` supports efficient high-precision evaluation:: >>> mp.dps = 10000 >>> a = agm(1,2) >>> str(a)[-10:] '1679581912' **Mathematical relations** The arithmetic-geometric mean may be used to evaluate the following two parametric definite integrals: .. math :: I_1 = \int_0^{\infty} \frac{1}{\sqrt{(x^2+a^2)(x^2+b^2)}} \,dx I_2 = \int_0^{\pi/2} \frac{1}{\sqrt{a^2 \cos^2(x) + b^2 \sin^2(x)}} \,dx We have:: >>> mp.dps = 15 >>> a = 3 >>> b = 4 >>> f1 = lambda x: ((x**2+a**2)*(x**2+b**2))**-0.5 >>> f2 = lambda x: ((a*cos(x))**2 + (b*sin(x))**2)**-0.5 >>> quad(f1, [0, inf]) 0.451115405388492 >>> quad(f2, [0, pi/2]) 0.451115405388492 >>> pi/(2*agm(a,b)) 0.451115405388492 A formula for `\Gamma(1/4)`:: >>> gamma(0.25) 3.62560990822191 >>> sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))) 3.62560990822191 **Possible issues** The branch cut chosen for complex `a` and `b` is somewhat arbitrary. """ gegenbauer = r""" Evaluates the Gegenbauer polynomial, or ultraspherical polynomial, .. math :: C_n^{(a)}(z) = {n+2a-1 \choose n} \,_2F_1\left(-n, n+2a; a+\frac{1}{2}; \frac{1}{2}(1-z)\right). When `n` is a nonnegative integer, this formula gives a polynomial in `z` of degree `n`, but all parameters are permitted to be complex numbers. With `a = 1/2`, the Gegenbauer polynomial reduces to a Legendre polynomial. **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> gegenbauer(3, 0.5, -10) -2485.0 >>> gegenbauer(1000, 10, 100) 3.012757178975667428359374e+2322 >>> gegenbauer(2+3j, -0.75, -1000j) (-5038991.358609026523401901 + 9414549.285447104177860806j) Evaluation at negative integer orders:: >>> gegenbauer(-4, 2, 1.75) -1.0 >>> gegenbauer(-4, 3, 1.75) 0.0 >>> gegenbauer(-4, 2j, 1.75) 0.0 >>> gegenbauer(-7, 0.5, 3) 8989.0 The Gegenbauer polynomials solve the differential equation:: >>> n, a = 4.5, 1+2j >>> f = lambda z: gegenbauer(n, a, z) >>> for z in [0, 0.75, -0.5j]: ... chop((1-z**2)*diff(f,z,2) - (2*a+1)*z*diff(f,z) + n*(n+2*a)*f(z)) ... 0.0 0.0 0.0 The Gegenbauer polynomials have generating function `(1-2zt+t^2)^{-a}`:: >>> a, z = 2.5, 1 >>> taylor(lambda t: (1-2*z*t+t**2)**(-a), 0, 3) [1.0, 5.0, 15.0, 35.0] >>> [gegenbauer(n,a,z) for n in range(4)] [1.0, 5.0, 15.0, 35.0] The Gegenbauer polynomials are orthogonal on `[-1, 1]` with respect to the weight `(1-z^2)^{a-\frac{1}{2}}`:: >>> a, n, m = 2.5, 4, 5 >>> Cn = lambda z: gegenbauer(n, a, z, zeroprec=1000) >>> Cm = lambda z: gegenbauer(m, a, z, zeroprec=1000) >>> chop(quad(lambda z: Cn(z)*Cm(z)*(1-z**2)*(a-0.5), [-1, 1])) 0.0 """ laguerre = r""" Gives the generalized (associated) Laguerre polynomial, defined by .. math :: L_n^a(z) = \frac{\Gamma(n+b+1)}{\Gamma(b+1) \Gamma(n+1)} \,_1F_1(-n, a+1, z). With `a = 0` and `n` a nonnegative integer, this reduces to an ordinary Laguerre polynomial, the sequence of which begins `L_0(z) = 1, L_1(z) = 1-z, L_2(z) = z^2-2z+1, \ldots`. The Laguerre polynomials are orthogonal with respect to the weight `z^a e^{-z}` on `[0, \infty)`. **Plots** .. literalinclude :: /modules/mpmath/plots/laguerre.py .. image :: /modules/mpmath/plots/laguerre.png **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> laguerre(5, 0, 0.25) 0.03726399739583333333333333 >>> laguerre(1+j, 0.5, 2+3j) (4.474921610704496808379097 - 11.02058050372068958069241j) >>> laguerre(2, 0, 10000) 49980001.0 >>> laguerre(2.5, 0, 10000) -9.327764910194842158583189e+4328 The first few Laguerre polynomials, normalized to have integer coefficients:: >>> for n in range(7): ... chop(taylor(lambda z: fac(n)*laguerre(n, 0, z), 0, n)) ... [1.0] [1.0, -1.0] [2.0, -4.0, 1.0] [6.0, -18.0, 9.0, -1.0] [24.0, -96.0, 72.0, -16.0, 1.0] [120.0, -600.0, 600.0, -200.0, 25.0, -1.0] [720.0, -4320.0, 5400.0, -2400.0, 450.0, -36.0, 1.0] Verifying orthogonality:: >>> Lm = lambda t: laguerre(m,a,t) >>> Ln = lambda t: laguerre(n,a,t) >>> a, n, m = 2.5, 2, 3 >>> chop(quad(lambda t: exp(-t)*t**a*Lm(t)*Ln(t), [0,inf])) 0.0 """ hermite = r""" Evaluates the Hermite polynomial `H_n(z)`, which may be defined using the recurrence .. math :: H_0(z) = 1 H_1(z) = 2z H_{n+1} = 2z H_n(z) - 2n H_{n-1}(z). The Hermite polynomials are orthogonal on `(-\infty, \infty)` with respect to the weight `e^{-z^2}`. More generally, allowing arbitrary complex values of `n`, the Hermite function `H_n(z)` is defined as .. math :: H_n(z) = (2z)^n \,_2F_0\left(-\frac{n}{2}, \frac{1-n}{2}, -\frac{1}{z^2}\right) for `\Re{z} > 0`, or generally .. math :: H_n(z) = 2^n \sqrt{\pi} \left( \frac{1}{\Gamma\left(\frac{1-n}{2}\right)} \,_1F_1\left(-\frac{n}{2}, \frac{1}{2}, z^2\right) - \frac{2z}{\Gamma\left(-\frac{n}{2}\right)} \,_1F_1\left(\frac{1-n}{2}, \frac{3}{2}, z^2\right) \right). **Plots** .. literalinclude :: /modules/mpmath/plots/hermite.py .. image :: /modules/mpmath/plots/hermite.png **Examples** Evaluation for arbitrary arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hermite(0, 10) 1.0 >>> hermite(1, 10); hermite(2, 10) 20.0 398.0 >>> hermite(10000, 2) 4.950440066552087387515653e+19334 >>> hermite(3, -10**8) -7999999999999998800000000.0 >>> hermite(-3, -10**8) 1.675159751729877682920301e+4342944819032534 >>> hermite(2+3j, -1+2j) (-0.07652130602993513389421901 - 0.1084662449961914580276007j) Coefficients of the first few Hermite polynomials are:: >>> for n in range(7): ... chop(taylor(lambda z: hermite(n, z), 0, n)) ... [1.0] [0.0, 2.0] [-2.0, 0.0, 4.0] [0.0, -12.0, 0.0, 8.0] [12.0, 0.0, -48.0, 0.0, 16.0] [0.0, 120.0, 0.0, -160.0, 0.0, 32.0] [-120.0, 0.0, 720.0, 0.0, -480.0, 0.0, 64.0] Values at `z = 0`:: >>> for n in range(-5, 9): ... hermite(n, 0) ... 0.02769459142039868792653387 0.08333333333333333333333333 0.2215567313631895034122709 0.5 0.8862269254527580136490837 1.0 0.0 -2.0 0.0 12.0 0.0 -120.0 0.0 1680.0 Hermite functions satisfy the differential equation:: >>> n = 4 >>> f = lambda z: hermite(n, z) >>> z = 1.5 >>> chop(diff(f,z,2) - 2*z*diff(f,z) + 2*n*f(z)) 0.0 Verifying orthogonality:: >>> chop(quad(lambda t: hermite(2,t)*hermite(4,t)*exp(-t**2), [-inf,inf])) 0.0 """ jacobi = r""" ``jacobi(n, a, b, x)`` evaluates the Jacobi polynomial `P_n^{(a,b)}(x)`. The Jacobi polynomials are a special case of the hypergeometric function `\,_2F_1` given by: .. math :: P_n^{(a,b)}(x) = {n+a \choose n} \,_2F_1\left(-n,1+a+b+n,a+1,\frac{1-x}{2}\right). Note that this definition generalizes to nonintegral values of `n`. When `n` is an integer, the hypergeometric series terminates after a finite number of terms, giving a polynomial in `x`. **Evaluation of Jacobi polynomials** A special evaluation is `P_n^{(a,b)}(1) = {n+a \choose n}`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> jacobi(4, 0.5, 0.25, 1) 2.4609375 >>> binomial(4+0.5, 4) 2.4609375 A Jacobi polynomial of degree `n` is equal to its Taylor polynomial of degree `n`. The explicit coefficients of Jacobi polynomials can therefore be recovered easily using :func:`~mpmath.taylor`:: >>> for n in range(5): ... nprint(taylor(lambda x: jacobi(n,1,2,x), 0, n)) ... [1.0] [-0.5, 2.5] [-0.75, -1.5, 5.25] [0.5, -3.5, -3.5, 10.5] [0.625, 2.5, -11.25, -7.5, 20.625] For nonintegral `n`, the Jacobi "polynomial" is no longer a polynomial:: >>> nprint(taylor(lambda x: jacobi(0.5,1,2,x), 0, 4)) [0.309983, 1.84119, -1.26933, 1.26699, -1.34808] **Orthogonality** The Jacobi polynomials are orthogonal on the interval `[-1, 1]` with respect to the weight function `w(x) = (1-x)^a (1+x)^b`. That is, `w(x) P_n^{(a,b)}(x) P_m^{(a,b)}(x)` integrates to zero if `m \ne n` and to a nonzero number if `m = n`. The orthogonality is easy to verify using numerical quadrature:: >>> P = jacobi >>> f = lambda x: (1-x)**a * (1+x)**b * P(m,a,b,x) * P(n,a,b,x) >>> a = 2 >>> b = 3 >>> m, n = 3, 4 >>> chop(quad(f, [-1, 1]), 1) 0.0 >>> m, n = 4, 4 >>> quad(f, [-1, 1]) 1.9047619047619 **Differential equation** The Jacobi polynomials are solutions of the differential equation .. math :: (1-x^2) y'' + (b-a-(a+b+2)x) y' + n (n+a+b+1) y = 0. We can verify that :func:`~mpmath.jacobi` approximately satisfies this equation:: >>> from mpmath import * >>> mp.dps = 15 >>> a = 2.5 >>> b = 4 >>> n = 3 >>> y = lambda x: jacobi(n,a,b,x) >>> x = pi >>> A0 = n*(n+a+b+1)*y(x) >>> A1 = (b-a-(a+b+2)*x)*diff(y,x) >>> A2 = (1-x**2)*diff(y,x,2) >>> nprint(A2 + A1 + A0, 1) 4.0e-12 The difference of order `10^{-12}` is as close to zero as it could be at 15-digit working precision, since the terms are large:: >>> A0, A1, A2 (26560.2328981879, -21503.7641037294, -5056.46879445852) """ legendre = r""" ``legendre(n, x)`` evaluates the Legendre polynomial `P_n(x)`. The Legendre polynomials are given by the formula .. math :: P_n(x) = \frac{1}{2^n n!} \frac{d^n}{dx^n} (x^2 -1)^n. Alternatively, they can be computed recursively using .. math :: P_0(x) = 1 P_1(x) = x (n+1) P_{n+1}(x) = (2n+1) x P_n(x) - n P_{n-1}(x). A third definition is in terms of the hypergeometric function `\,_2F_1`, whereby they can be generalized to arbitrary `n`: .. math :: P_n(x) = \,_2F_1\left(-n, n+1, 1, \frac{1-x}{2}\right) **Plots** .. literalinclude :: /modules/mpmath/plots/legendre.py .. image :: /modules/mpmath/plots/legendre.png **Basic evaluation** The Legendre polynomials assume fixed values at the points `x = -1` and `x = 1`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> nprint([legendre(n, 1) for n in range(6)]) [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] >>> nprint([legendre(n, -1) for n in range(6)]) [1.0, -1.0, 1.0, -1.0, 1.0, -1.0] The coefficients of Legendre polynomials can be recovered using degree-`n` Taylor expansion:: >>> for n in range(5): ... nprint(chop(taylor(lambda x: legendre(n, x), 0, n))) ... [1.0] [0.0, 1.0] [-0.5, 0.0, 1.5] [0.0, -1.5, 0.0, 2.5] [0.375, 0.0, -3.75, 0.0, 4.375] The roots of Legendre polynomials are located symmetrically on the interval `[-1, 1]`:: >>> for n in range(5): ... nprint(polyroots(taylor(lambda x: legendre(n, x), 0, n)[::-1])) ... [] [0.0] [-0.57735, 0.57735] [-0.774597, 0.0, 0.774597] [-0.861136, -0.339981, 0.339981, 0.861136] An example of an evaluation for arbitrary `n`:: >>> legendre(0.75, 2+4j) (1.94952805264875 + 2.1071073099422j) **Orthogonality** The Legendre polynomials are orthogonal on `[-1, 1]` with respect to the trivial weight `w(x) = 1`. That is, `P_m(x) P_n(x)` integrates to zero if `m \ne n` and to `2/(2n+1)` if `m = n`:: >>> m, n = 3, 4 >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) 0.0 >>> m, n = 4, 4 >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) 0.222222222222222 **Differential equation** The Legendre polynomials satisfy the differential equation .. math :: ((1-x^2) y')' + n(n+1) y' = 0. We can verify this numerically:: >>> n = 3.6 >>> x = 0.73 >>> P = legendre >>> A = diff(lambda t: (1-t**2)*diff(lambda u: P(n,u), t), x) >>> B = n*(n+1)*P(n,x) >>> nprint(A+B,1) 9.0e-16 """ legenp = r""" Calculates the (associated) Legendre function of the first kind of degree *n* and order *m*, `P_n^m(z)`. Taking `m = 0` gives the ordinary Legendre function of the first kind, `P_n(z)`. The parameters may be complex numbers. In terms of the Gauss hypergeometric function, the (associated) Legendre function is defined as .. math :: P_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(1+z)^{m/2}}{(1-z)^{m/2}} \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). With *type=3* instead of *type=2*, the alternative definition .. math :: \hat{P}_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(z+1)^{m/2}}{(z-1)^{m/2}} \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). is used. These functions correspond respectively to ``LegendreP[n,m,2,z]`` and ``LegendreP[n,m,3,z]`` in Mathematica. The general solution of the (associated) Legendre differential equation .. math :: (1-z^2) f''(z) - 2zf'(z) + \left(n(n+1)-\frac{m^2}{1-z^2}\right)f(z) = 0 is given by `C_1 P_n^m(z) + C_2 Q_n^m(z)` for arbitrary constants `C_1`, `C_2`, where `Q_n^m(z)` is a Legendre function of the second kind as implemented by :func:`~mpmath.legenq`. **Examples** Evaluation for arbitrary parameters and arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> legenp(2, 0, 10); legendre(2, 10) 149.5 149.5 >>> legenp(-2, 0.5, 2.5) (1.972260393822275434196053 - 1.972260393822275434196053j) >>> legenp(2+3j, 1-j, -0.5+4j) (-3.335677248386698208736542 - 5.663270217461022307645625j) >>> chop(legenp(3, 2, -1.5, type=2)) 28.125 >>> chop(legenp(3, 2, -1.5, type=3)) -28.125 Verifying the associated Legendre differential equation:: >>> n, m = 2, -0.5 >>> C1, C2 = 1, -3 >>> f = lambda z: C1*legenp(n,m,z) + C2*legenq(n,m,z) >>> deq = lambda z: (1-z**2)*diff(f,z,2) - 2*z*diff(f,z) + \ ... (n*(n+1)-m**2/(1-z**2))*f(z) >>> for z in [0, 2, -1.5, 0.5+2j]: ... chop(deq(mpmathify(z))) ... 0.0 0.0 0.0 0.0 """ legenq = r""" Calculates the (associated) Legendre function of the second kind of degree *n* and order *m*, `Q_n^m(z)`. Taking `m = 0` gives the ordinary Legendre function of the second kind, `Q_n(z)`. The parameters may complex numbers. The Legendre functions of the second kind give a second set of solutions to the (associated) Legendre differential equation. (See :func:`~mpmath.legenp`.) Unlike the Legendre functions of the first kind, they are not polynomials of `z` for integer `n`, `m` but rational or logarithmic functions with poles at `z = \pm 1`. There are various ways to define Legendre functions of the second kind, giving rise to different complex structure. A version can be selected using the *type* keyword argument. The *type=2* and *type=3* functions are given respectively by .. math :: Q_n^m(z) = \frac{\pi}{2 \sin(\pi m)} \left( \cos(\pi m) P_n^m(z) - \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} P_n^{-m}(z)\right) \hat{Q}_n^m(z) = \frac{\pi}{2 \sin(\pi m)} e^{\pi i m} \left( \hat{P}_n^m(z) - \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} \hat{P}_n^{-m}(z)\right) where `P` and `\hat{P}` are the *type=2* and *type=3* Legendre functions of the first kind. The formulas above should be understood as limits when `m` is an integer. These functions correspond to ``LegendreQ[n,m,2,z]`` (or ``LegendreQ[n,m,z]``) and ``LegendreQ[n,m,3,z]`` in Mathematica. The *type=3* function is essentially the same as the function defined in Abramowitz & Stegun (eq. 8.1.3) but with `(z+1)^{m/2}(z-1)^{m/2}` instead of `(z^2-1)^{m/2}`, giving slightly different branches. **Examples** Evaluation for arbitrary parameters and arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> legenq(2, 0, 0.5) -0.8186632680417568557122028 >>> legenq(-1.5, -2, 2.5) (0.6655964618250228714288277 + 0.3937692045497259717762649j) >>> legenq(2-j, 3+4j, -6+5j) (-10001.95256487468541686564 - 6011.691337610097577791134j) Different versions of the function:: >>> legenq(2, 1, 0.5) 0.7298060598018049369381857 >>> legenq(2, 1, 1.5) (-7.902916572420817192300921 + 0.1998650072605976600724502j) >>> legenq(2, 1, 0.5, type=3) (2.040524284763495081918338 - 0.7298060598018049369381857j) >>> chop(legenq(2, 1, 1.5, type=3)) -0.1998650072605976600724502 """ chebyt = r""" ``chebyt(n, x)`` evaluates the Chebyshev polynomial of the first kind `T_n(x)`, defined by the identity .. math :: T_n(\cos x) = \cos(n x). The Chebyshev polynomials of the first kind are a special case of the Jacobi polynomials, and by extension of the hypergeometric function `\,_2F_1`. They can thus also be evaluated for nonintegral `n`. **Plots** .. literalinclude :: /modules/mpmath/plots/chebyt.py .. image :: /modules/mpmath/plots/chebyt.png **Basic evaluation** The coefficients of the `n`-th polynomial can be recovered using using degree-`n` Taylor expansion:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(chop(taylor(lambda x: chebyt(n, x), 0, n))) ... [1.0] [0.0, 1.0] [-1.0, 0.0, 2.0] [0.0, -3.0, 0.0, 4.0] [1.0, 0.0, -8.0, 0.0, 8.0] **Orthogonality** The Chebyshev polynomials of the first kind are orthogonal on the interval `[-1, 1]` with respect to the weight function `w(x) = 1/\sqrt{1-x^2}`:: >>> f = lambda x: chebyt(m,x)*chebyt(n,x)/sqrt(1-x**2) >>> m, n = 3, 4 >>> nprint(quad(f, [-1, 1]),1) 0.0 >>> m, n = 4, 4 >>> quad(f, [-1, 1]) 1.57079632596448 """ chebyu = r""" ``chebyu(n, x)`` evaluates the Chebyshev polynomial of the second kind `U_n(x)`, defined by the identity .. math :: U_n(\cos x) = \frac{\sin((n+1)x)}{\sin(x)}. The Chebyshev polynomials of the second kind are a special case of the Jacobi polynomials, and by extension of the hypergeometric function `\,_2F_1`. They can thus also be evaluated for nonintegral `n`. **Plots** .. literalinclude :: /modules/mpmath/plots/chebyu.py .. image :: /modules/mpmath/plots/chebyu.png **Basic evaluation** The coefficients of the `n`-th polynomial can be recovered using using degree-`n` Taylor expansion:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(5): ... nprint(chop(taylor(lambda x: chebyu(n, x), 0, n))) ... [1.0] [0.0, 2.0] [-1.0, 0.0, 4.0] [0.0, -4.0, 0.0, 8.0] [1.0, 0.0, -12.0, 0.0, 16.0] **Orthogonality** The Chebyshev polynomials of the second kind are orthogonal on the interval `[-1, 1]` with respect to the weight function `w(x) = \sqrt{1-x^2}`:: >>> f = lambda x: chebyu(m,x)*chebyu(n,x)*sqrt(1-x**2) >>> m, n = 3, 4 >>> quad(f, [-1, 1]) 0.0 >>> m, n = 4, 4 >>> quad(f, [-1, 1]) 1.5707963267949 """ besselj = r""" ``besselj(n, x, derivative=0)`` gives the Bessel function of the first kind `J_n(x)`. Bessel functions of the first kind are defined as solutions of the differential equation .. math :: x^2 y'' + x y' + (x^2 - n^2) y = 0 which appears, among other things, when solving the radial part of Laplace's equation in cylindrical coordinates. This equation has two solutions for given `n`, where the `J_n`-function is the solution that is nonsingular at `x = 0`. For positive integer `n`, `J_n(x)` behaves roughly like a sine (odd `n`) or cosine (even `n`) multiplied by a magnitude factor that decays slowly as `x \to \pm\infty`. Generally, `J_n` is a special case of the hypergeometric function `\,_0F_1`: .. math :: J_n(x) = \frac{x^n}{2^n \Gamma(n+1)} \,_0F_1\left(n+1,-\frac{x^2}{4}\right) With *derivative* = `m \ne 0`, the `m`-th derivative .. math :: \frac{d^m}{dx^m} J_n(x) is computed. **Plots** .. literalinclude :: /modules/mpmath/plots/besselj.py .. image :: /modules/mpmath/plots/besselj.png .. literalinclude :: /modules/mpmath/plots/besselj_c.py .. image :: /modules/mpmath/plots/besselj_c.png **Examples** Evaluation is supported for arbitrary arguments, and at arbitrary precision:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> besselj(2, 1000) -0.024777229528606 >>> besselj(4, 0.75) 0.000801070086542314 >>> besselj(2, 1000j) (-2.48071721019185e+432 + 6.41567059811949e-437j) >>> mp.dps = 25 >>> besselj(0.75j, 3+4j) (-2.778118364828153309919653 - 1.5863603889018621585533j) >>> mp.dps = 50 >>> besselj(1, pi) 0.28461534317975275734531059968613140570981118184947 Arguments may be large:: >>> mp.dps = 25 >>> besselj(0, 10000) -0.007096160353388801477265164 >>> besselj(0, 10**10) 0.000002175591750246891726859055 >>> besselj(2, 10**100) 7.337048736538615712436929e-51 >>> besselj(2, 10**5*j) (-3.540725411970948860173735e+43426 + 4.4949812409615803110051e-43433j) The Bessel functions of the first kind satisfy simple symmetries around `x = 0`:: >>> mp.dps = 15 >>> nprint([besselj(n,0) for n in range(5)]) [1.0, 0.0, 0.0, 0.0, 0.0] >>> nprint([besselj(n,pi) for n in range(5)]) [-0.304242, 0.284615, 0.485434, 0.333458, 0.151425] >>> nprint([besselj(n,-pi) for n in range(5)]) [-0.304242, -0.284615, 0.485434, -0.333458, 0.151425] Roots of Bessel functions are often used:: >>> nprint([findroot(j0, k) for k in [2, 5, 8, 11, 14]]) [2.40483, 5.52008, 8.65373, 11.7915, 14.9309] >>> nprint([findroot(j1, k) for k in [3, 7, 10, 13, 16]]) [3.83171, 7.01559, 10.1735, 13.3237, 16.4706] The roots are not periodic, but the distance between successive roots asymptotically approaches `2 \pi`. Bessel functions of the first kind have the following normalization:: >>> quadosc(j0, [0, inf], period=2*pi) 1.0 >>> quadosc(j1, [0, inf], period=2*pi) 1.0 For `n = 1/2` or `n = -1/2`, the Bessel function reduces to a trigonometric function:: >>> x = 10 >>> besselj(0.5, x), sqrt(2/(pi*x))*sin(x) (-0.13726373575505, -0.13726373575505) >>> besselj(-0.5, x), sqrt(2/(pi*x))*cos(x) (-0.211708866331398, -0.211708866331398) Derivatives of any order can be computed (negative orders correspond to integration):: >>> mp.dps = 25 >>> besselj(0, 7.5, 1) -0.1352484275797055051822405 >>> diff(lambda x: besselj(0,x), 7.5) -0.1352484275797055051822405 >>> besselj(0, 7.5, 10) -0.1377811164763244890135677 >>> diff(lambda x: besselj(0,x), 7.5, 10) -0.1377811164763244890135677 >>> besselj(0,7.5,-1) - besselj(0,3.5,-1) -0.1241343240399987693521378 >>> quad(j0, [3.5, 7.5]) -0.1241343240399987693521378 Differentiation with a noninteger order gives the fractional derivative in the sense of the Riemann-Liouville differintegral, as computed by :func:`~mpmath.differint`:: >>> mp.dps = 15 >>> besselj(1, 3.5, 0.75) -0.385977722939384 >>> differint(lambda x: besselj(1, x), 3.5, 0.75) -0.385977722939384 """ besseli = r""" ``besseli(n, x, derivative=0)`` gives the modified Bessel function of the first kind, .. math :: I_n(x) = i^{-n} J_n(ix). With *derivative* = `m \ne 0`, the `m`-th derivative .. math :: \frac{d^m}{dx^m} I_n(x) is computed. **Plots** .. literalinclude :: /modules/mpmath/plots/besseli.py .. image :: /modules/mpmath/plots/besseli.png .. literalinclude :: /modules/mpmath/plots/besseli_c.py .. image :: /modules/mpmath/plots/besseli_c.png **Examples** Some values of `I_n(x)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besseli(0,0) 1.0 >>> besseli(1,0) 0.0 >>> besseli(0,1) 1.266065877752008335598245 >>> besseli(3.5, 2+3j) (-0.2904369752642538144289025 - 0.4469098397654815837307006j) Arguments may be large:: >>> besseli(2, 1000) 2.480717210191852440616782e+432 >>> besseli(2, 10**10) 4.299602851624027900335391e+4342944813 >>> besseli(2, 6000+10000j) (-2.114650753239580827144204e+2603 + 4.385040221241629041351886e+2602j) For integers `n`, the following integral representation holds:: >>> mp.dps = 15 >>> n = 3 >>> x = 2.3 >>> quad(lambda t: exp(x*cos(t))*cos(n*t), [0,pi])/pi 0.349223221159309 >>> besseli(n,x) 0.349223221159309 Derivatives and antiderivatives of any order can be computed:: >>> mp.dps = 25 >>> besseli(2, 7.5, 1) 195.8229038931399062565883 >>> diff(lambda x: besseli(2,x), 7.5) 195.8229038931399062565883 >>> besseli(2, 7.5, 10) 153.3296508971734525525176 >>> diff(lambda x: besseli(2,x), 7.5, 10) 153.3296508971734525525176 >>> besseli(2,7.5,-1) - besseli(2,3.5,-1) 202.5043900051930141956876 >>> quad(lambda x: besseli(2,x), [3.5, 7.5]) 202.5043900051930141956876 """ bessely = r""" ``bessely(n, x, derivative=0)`` gives the Bessel function of the second kind, .. math :: Y_n(x) = \frac{J_n(x) \cos(\pi n) - J_{-n}(x)}{\sin(\pi n)}. For `n` an integer, this formula should be understood as a limit. With *derivative* = `m \ne 0`, the `m`-th derivative .. math :: \frac{d^m}{dx^m} Y_n(x) is computed. **Plots** .. literalinclude :: /modules/mpmath/plots/bessely.py .. image :: /modules/mpmath/plots/bessely.png .. literalinclude :: /modules/mpmath/plots/bessely_c.py .. image :: /modules/mpmath/plots/bessely_c.png **Examples** Some values of `Y_n(x)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> bessely(0,0), bessely(1,0), bessely(2,0) (-inf, -inf, -inf) >>> bessely(1, pi) 0.3588729167767189594679827 >>> bessely(0.5, 3+4j) (9.242861436961450520325216 - 3.085042824915332562522402j) Arguments may be large:: >>> bessely(0, 10000) 0.00364780555898660588668872 >>> bessely(2.5, 10**50) -4.8952500412050989295774e-26 >>> bessely(2.5, -10**50) (0.0 + 4.8952500412050989295774e-26j) Derivatives and antiderivatives of any order can be computed:: >>> bessely(2, 3.5, 1) 0.3842618820422660066089231 >>> diff(lambda x: bessely(2, x), 3.5) 0.3842618820422660066089231 >>> bessely(0.5, 3.5, 1) -0.2066598304156764337900417 >>> diff(lambda x: bessely(0.5, x), 3.5) -0.2066598304156764337900417 >>> diff(lambda x: bessely(2, x), 0.5, 10) -208173867409.5547350101511 >>> bessely(2, 0.5, 10) -208173867409.5547350101511 >>> bessely(2, 100.5, 100) 0.02668487547301372334849043 >>> quad(lambda x: bessely(2,x), [1,3]) -1.377046859093181969213262 >>> bessely(2,3,-1) - bessely(2,1,-1) -1.377046859093181969213262 """ besselk = r""" ``besselk(n, x)`` gives the modified Bessel function of the second kind, .. math :: K_n(x) = \frac{\pi}{2} \frac{I_{-n}(x)-I_{n}(x)}{\sin(\pi n)} For `n` an integer, this formula should be understood as a limit. **Plots** .. literalinclude :: /modules/mpmath/plots/besselk.py .. image :: /modules/mpmath/plots/besselk.png .. literalinclude :: /modules/mpmath/plots/besselk_c.py .. image :: /modules/mpmath/plots/besselk_c.png **Examples** Evaluation is supported for arbitrary complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> besselk(0,1) 0.4210244382407083333356274 >>> besselk(0, -1) (0.4210244382407083333356274 - 3.97746326050642263725661j) >>> besselk(3.5, 2+3j) (-0.02090732889633760668464128 + 0.2464022641351420167819697j) >>> besselk(2+3j, 0.5) (0.9615816021726349402626083 + 0.1918250181801757416908224j) Arguments may be large:: >>> besselk(0, 100) 4.656628229175902018939005e-45 >>> besselk(1, 10**6) 4.131967049321725588398296e-434298 >>> besselk(1, 10**6*j) (0.001140348428252385844876706 - 0.0005200017201681152909000961j) >>> besselk(4.5, fmul(10**50, j, exact=True)) (1.561034538142413947789221e-26 + 1.243554598118700063281496e-25j) The point `x = 0` is a singularity (logarithmic if `n = 0`):: >>> besselk(0,0) +inf >>> besselk(1,0) +inf >>> for n in range(-4, 5): ... print(besselk(n, '1e-1000')) ... 4.8e+4001 8.0e+3000 2.0e+2000 1.0e+1000 2302.701024509704096466802 1.0e+1000 2.0e+2000 8.0e+3000 4.8e+4001 """ hankel1 = r""" ``hankel1(n,x)`` computes the Hankel function of the first kind, which is the complex combination of Bessel functions given by .. math :: H_n^{(1)}(x) = J_n(x) + i Y_n(x). **Plots** .. literalinclude :: /modules/mpmath/plots/hankel1.py .. image :: /modules/mpmath/plots/hankel1.png .. literalinclude :: /modules/mpmath/plots/hankel1_c.py .. image :: /modules/mpmath/plots/hankel1_c.png **Examples** The Hankel function is generally complex-valued:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hankel1(2, pi) (0.4854339326315091097054957 - 0.0999007139290278787734903j) >>> hankel1(3.5, pi) (0.2340002029630507922628888 - 0.6419643823412927142424049j) """ hankel2 = r""" ``hankel2(n,x)`` computes the Hankel function of the second kind, which is the complex combination of Bessel functions given by .. math :: H_n^{(2)}(x) = J_n(x) - i Y_n(x). **Plots** .. literalinclude :: /modules/mpmath/plots/hankel2.py .. image :: /modules/mpmath/plots/hankel2.png .. literalinclude :: /modules/mpmath/plots/hankel2_c.py .. image :: /modules/mpmath/plots/hankel2_c.png **Examples** The Hankel function is generally complex-valued:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> hankel2(2, pi) (0.4854339326315091097054957 + 0.0999007139290278787734903j) >>> hankel2(3.5, pi) (0.2340002029630507922628888 + 0.6419643823412927142424049j) """ lambertw = r""" The Lambert W function `W(z)` is defined as the inverse function of `w \exp(w)`. In other words, the value of `W(z)` is such that `z = W(z) \exp(W(z))` for any complex number `z`. The Lambert W function is a multivalued function with infinitely many branches `W_k(z)`, indexed by `k \in \mathbb{Z}`. Each branch gives a different solution `w` of the equation `z = w \exp(w)`. All branches are supported by :func:`~mpmath.lambertw`: * ``lambertw(z)`` gives the principal solution (branch 0) * ``lambertw(z, k)`` gives the solution on branch `k` The Lambert W function has two partially real branches: the principal branch (`k = 0`) is real for real `z > -1/e`, and the `k = -1` branch is real for `-1/e < z < 0`. All branches except `k = 0` have a logarithmic singularity at `z = 0`. The definition, implementation and choice of branches is based on [Corless]_. **Plots** .. literalinclude :: /modules/mpmath/plots/lambertw.py .. image :: /modules/mpmath/plots/lambertw.png .. literalinclude :: /modules/mpmath/plots/lambertw_c.py .. image :: /modules/mpmath/plots/lambertw_c.png **Basic examples** The Lambert W function is the inverse of `w \exp(w)`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> w = lambertw(1) >>> w 0.5671432904097838729999687 >>> w*exp(w) 1.0 Any branch gives a valid inverse:: >>> w = lambertw(1, k=3) >>> w (-2.853581755409037807206819 + 17.11353553941214591260783j) >>> w = lambertw(1, k=25) >>> w (-5.047020464221569709378686 + 155.4763860949415867162066j) >>> chop(w*exp(w)) 1.0 **Applications to equation-solving** The Lambert W function may be used to solve various kinds of equations, such as finding the value of the infinite power tower `z^{z^{z^{\ldots}}}`:: >>> def tower(z, n): ... if n == 0: ... return z ... return z ** tower(z, n-1) ... >>> tower(mpf(0.5), 100) 0.6411857445049859844862005 >>> -lambertw(-log(0.5))/log(0.5) 0.6411857445049859844862005 **Properties** The Lambert W function grows roughly like the natural logarithm for large arguments:: >>> lambertw(1000); log(1000) 5.249602852401596227126056 6.907755278982137052053974 >>> lambertw(10**100); log(10**100) 224.8431064451185015393731 230.2585092994045684017991 The principal branch of the Lambert W function has a rational Taylor series expansion around `z = 0`:: >>> nprint(taylor(lambertw, 0, 6), 10) [0.0, 1.0, -1.0, 1.5, -2.666666667, 5.208333333, -10.8] Some special values and limits are:: >>> lambertw(0) 0.0 >>> lambertw(1) 0.5671432904097838729999687 >>> lambertw(e) 1.0 >>> lambertw(inf) +inf >>> lambertw(0, k=-1) -inf >>> lambertw(0, k=3) -inf >>> lambertw(inf, k=2) (+inf + 12.56637061435917295385057j) >>> lambertw(inf, k=3) (+inf + 18.84955592153875943077586j) >>> lambertw(-inf, k=3) (+inf + 21.9911485751285526692385j) The `k = 0` and `k = -1` branches join at `z = -1/e` where `W(z) = -1` for both branches. Since `-1/e` can only be represented approximately with binary floating-point numbers, evaluating the Lambert W function at this point only gives `-1` approximately:: >>> lambertw(-1/e, 0) -0.9999999999998371330228251 >>> lambertw(-1/e, -1) -1.000000000000162866977175 If `-1/e` happens to round in the negative direction, there might be a small imaginary part:: >>> mp.dps = 15 >>> lambertw(-1/e) (-1.0 + 8.22007971483662e-9j) >>> lambertw(-1/e+eps) -0.999999966242188 **References** 1. [Corless]_ """ barnesg = r""" Evaluates the Barnes G-function, which generalizes the superfactorial (:func:`~mpmath.superfac`) and by extension also the hyperfactorial (:func:`~mpmath.hyperfac`) to the complex numbers in an analogous way to how the gamma function generalizes the ordinary factorial. The Barnes G-function may be defined in terms of a Weierstrass product: .. math :: G(z+1) = (2\pi)^{z/2} e^{-[z(z+1)+\gamma z^2]/2} \prod_{n=1}^\infty \left[\left(1+\frac{z}{n}\right)^ne^{-z+z^2/(2n)}\right] For positive integers `n`, we have have relation to superfactorials `G(n) = \mathrm{sf}(n-2) = 0! \cdot 1! \cdots (n-2)!`. **Examples** Some elementary values and limits of the Barnes G-function:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> barnesg(1), barnesg(2), barnesg(3) (1.0, 1.0, 1.0) >>> barnesg(4) 2.0 >>> barnesg(5) 12.0 >>> barnesg(6) 288.0 >>> barnesg(7) 34560.0 >>> barnesg(8) 24883200.0 >>> barnesg(inf) +inf >>> barnesg(0), barnesg(-1), barnesg(-2) (0.0, 0.0, 0.0) Closed-form values are known for some rational arguments:: >>> barnesg('1/2') 0.603244281209446 >>> sqrt(exp(0.25+log(2)/12)/sqrt(pi)/glaisher**3) 0.603244281209446 >>> barnesg('1/4') 0.29375596533861 >>> nthroot(exp('3/8')/exp(catalan/pi)/ ... gamma(0.25)**3/sqrt(glaisher)**9, 4) 0.29375596533861 The Barnes G-function satisfies the functional equation `G(z+1) = \Gamma(z) G(z)`:: >>> z = pi >>> barnesg(z+1) 2.39292119327948 >>> gamma(z)*barnesg(z) 2.39292119327948 The asymptotic growth rate of the Barnes G-function is related to the Glaisher-Kinkelin constant:: >>> limit(lambda n: barnesg(n+1)/(n**(n**2/2-mpf(1)/12)* ... (2*pi)**(n/2)*exp(-3*n**2/4)), inf) 0.847536694177301 >>> exp('1/12')/glaisher 0.847536694177301 The Barnes G-function can be differentiated in closed form:: >>> z = 3 >>> diff(barnesg, z) 0.264507203401607 >>> barnesg(z)*((z-1)*psi(0,z)-z+(log(2*pi)+1)/2) 0.264507203401607 Evaluation is supported for arbitrary arguments and at arbitrary precision:: >>> barnesg(6.5) 2548.7457695685 >>> barnesg(-pi) 0.00535976768353037 >>> barnesg(3+4j) (-0.000676375932234244 - 4.42236140124728e-5j) >>> mp.dps = 50 >>> barnesg(1/sqrt(2)) 0.81305501090451340843586085064413533788206204124732 >>> q = barnesg(10j) >>> q.real 0.000000000021852360840356557241543036724799812371995850552234 >>> q.imag -0.00000000000070035335320062304849020654215545839053210041457588 >>> mp.dps = 15 >>> barnesg(100) 3.10361006263698e+6626 >>> barnesg(-101) 0.0 >>> barnesg(-10.5) 5.94463017605008e+25 >>> barnesg(-10000.5) -6.14322868174828e+167480422 >>> barnesg(1000j) (5.21133054865546e-1173597 + 4.27461836811016e-1173597j) >>> barnesg(-1000+1000j) (2.43114569750291e+1026623 + 2.24851410674842e+1026623j) **References** 1. Whittaker & Watson, *A Course of Modern Analysis*, Cambridge University Press, 4th edition (1927), p.264 2. http://en.wikipedia.org/wiki/Barnes_G-function 3. http://mathworld.wolfram.com/BarnesG-Function.html """ superfac = r""" Computes the superfactorial, defined as the product of consecutive factorials .. math :: \mathrm{sf}(n) = \prod_{k=1}^n k! For general complex `z`, `\mathrm{sf}(z)` is defined in terms of the Barnes G-function (see :func:`~mpmath.barnesg`). **Examples** The first few superfactorials are (OEIS A000178):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(10): ... print("%s %s" % (n, superfac(n))) ... 0 1.0 1 1.0 2 2.0 3 12.0 4 288.0 5 34560.0 6 24883200.0 7 125411328000.0 8 5.05658474496e+15 9 1.83493347225108e+21 Superfactorials grow very rapidly:: >>> superfac(1000) 3.24570818422368e+1177245 >>> superfac(10**10) 2.61398543581249e+467427913956904067453 Evaluation is supported for arbitrary arguments:: >>> mp.dps = 25 >>> superfac(pi) 17.20051550121297985285333 >>> superfac(2+3j) (-0.005915485633199789627466468 + 0.008156449464604044948738263j) >>> diff(superfac, 1) 0.2645072034016070205673056 **References** 1. http://www.research.att.com/~njas/sequences/A000178 """ hyperfac = r""" Computes the hyperfactorial, defined for integers as the product .. math :: H(n) = \prod_{k=1}^n k^k. The hyperfactorial satisfies the recurrence formula `H(z) = z^z H(z-1)`. It can be defined more generally in terms of the Barnes G-function (see :func:`~mpmath.barnesg`) and the gamma function by the formula .. math :: H(z) = \frac{\Gamma(z+1)^z}{G(z)}. The extension to complex numbers can also be done via the integral representation .. math :: H(z) = (2\pi)^{-z/2} \exp \left[ {z+1 \choose 2} + \int_0^z \log(t!)\,dt \right]. **Examples** The rapidly-growing sequence of hyperfactorials begins (OEIS A002109):: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(10): ... print("%s %s" % (n, hyperfac(n))) ... 0 1.0 1 1.0 2 4.0 3 108.0 4 27648.0 5 86400000.0 6 4031078400000.0 7 3.3197663987712e+18 8 5.56964379417266e+25 9 2.15779412229419e+34 Some even larger hyperfactorials are:: >>> hyperfac(1000) 5.46458120882585e+1392926 >>> hyperfac(10**10) 4.60408207642219e+489142638002418704309 The hyperfactorial can be evaluated for arbitrary arguments:: >>> hyperfac(0.5) 0.880449235173423 >>> diff(hyperfac, 1) 0.581061466795327 >>> hyperfac(pi) 205.211134637462 >>> hyperfac(-10+1j) (3.01144471378225e+46 - 2.45285242480185e+46j) The recurrence property of the hyperfactorial holds generally:: >>> z = 3-4*j >>> hyperfac(z) (-4.49795891462086e-7 - 6.33262283196162e-7j) >>> z**z * hyperfac(z-1) (-4.49795891462086e-7 - 6.33262283196162e-7j) >>> z = mpf(-0.6) >>> chop(z**z * hyperfac(z-1)) 1.28170142849352 >>> hyperfac(z) 1.28170142849352 The hyperfactorial may also be computed using the integral definition:: >>> z = 2.5 >>> hyperfac(z) 15.9842119922237 >>> (2*pi)**(-z/2)*exp(binomial(z+1,2) + ... quad(lambda t: loggamma(t+1), [0, z])) 15.9842119922237 :func:`~mpmath.hyperfac` supports arbitrary-precision evaluation:: >>> mp.dps = 50 >>> hyperfac(10) 215779412229418562091680268288000000000000000.0 >>> hyperfac(1/sqrt(2)) 0.89404818005227001975423476035729076375705084390942 **References** 1. http://www.research.att.com/~njas/sequences/A002109 2. http://mathworld.wolfram.com/Hyperfactorial.html """ rgamma = r""" Computes the reciprocal of the gamma function, `1/\Gamma(z)`. This function evaluates to zero at the poles of the gamma function, `z = 0, -1, -2, \ldots`. **Examples** Basic examples:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> rgamma(1) 1.0 >>> rgamma(4) 0.1666666666666666666666667 >>> rgamma(0); rgamma(-1) 0.0 0.0 >>> rgamma(1000) 2.485168143266784862783596e-2565 >>> rgamma(inf) 0.0 A definite integral that can be evaluated in terms of elementary integrals:: >>> quad(rgamma, [0,inf]) 2.807770242028519365221501 >>> e + quad(lambda t: exp(-t)/(pi**2+log(t)**2), [0,inf]) 2.807770242028519365221501 """ loggamma = r""" Computes the principal branch of the log-gamma function, `\ln \Gamma(z)`. Unlike `\ln(\Gamma(z))`, which has infinitely many complex branch cuts, the principal log-gamma function only has a single branch cut along the negative half-axis. The principal branch continuously matches the asymptotic Stirling expansion .. math :: \ln \Gamma(z) \sim \frac{\ln(2 \pi)}{2} + \left(z-\frac{1}{2}\right) \ln(z) - z + O(z^{-1}). The real parts of both functions agree, but their imaginary parts generally differ by `2 n \pi` for some `n \in \mathbb{Z}`. They coincide for `z \in \mathbb{R}, z > 0`. Computationally, it is advantageous to use :func:`~mpmath.loggamma` instead of :func:`~mpmath.gamma` for extremely large arguments. **Examples** Comparing with `\ln(\Gamma(z))`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> loggamma('13.2'); log(gamma('13.2')) 20.49400419456603678498394 20.49400419456603678498394 >>> loggamma(3+4j) (-1.756626784603784110530604 + 4.742664438034657928194889j) >>> log(gamma(3+4j)) (-1.756626784603784110530604 - 1.540520869144928548730397j) >>> log(gamma(3+4j)) + 2*pi*j (-1.756626784603784110530604 + 4.742664438034657928194889j) Note the imaginary parts for negative arguments:: >>> loggamma(-0.5); loggamma(-1.5); loggamma(-2.5) (1.265512123484645396488946 - 3.141592653589793238462643j) (0.8600470153764810145109327 - 6.283185307179586476925287j) (-0.05624371649767405067259453 - 9.42477796076937971538793j) Some special values:: >>> loggamma(1); loggamma(2) 0.0 0.0 >>> loggamma(3); +ln2 0.6931471805599453094172321 0.6931471805599453094172321 >>> loggamma(3.5); log(15*sqrt(pi)/8) 1.200973602347074224816022 1.200973602347074224816022 >>> loggamma(inf) +inf Huge arguments are permitted:: >>> loggamma('1e30') 6.807755278982137052053974e+31 >>> loggamma('1e300') 6.897755278982137052053974e+302 >>> loggamma('1e3000') 6.906755278982137052053974e+3003 >>> loggamma('1e100000000000000000000') 2.302585092994045684007991e+100000000000000000020 >>> loggamma('1e30j') (-1.570796326794896619231322e+30 + 6.807755278982137052053974e+31j) >>> loggamma('1e300j') (-1.570796326794896619231322e+300 + 6.897755278982137052053974e+302j) >>> loggamma('1e3000j') (-1.570796326794896619231322e+3000 + 6.906755278982137052053974e+3003j) The log-gamma function can be integrated analytically on any interval of unit length:: >>> z = 0 >>> quad(loggamma, [z,z+1]); log(2*pi)/2 0.9189385332046727417803297 0.9189385332046727417803297 >>> z = 3+4j >>> quad(loggamma, [z,z+1]); (log(z)-1)*z + log(2*pi)/2 (-0.9619286014994750641314421 + 5.219637303741238195688575j) (-0.9619286014994750641314421 + 5.219637303741238195688575j) The derivatives of the log-gamma function are given by the polygamma function (:func:`~mpmath.psi`):: >>> diff(loggamma, -4+3j); psi(0, -4+3j) (1.688493531222971393607153 + 2.554898911356806978892748j) (1.688493531222971393607153 + 2.554898911356806978892748j) >>> diff(loggamma, -4+3j, 2); psi(1, -4+3j) (-0.1539414829219882371561038 - 0.1020485197430267719746479j) (-0.1539414829219882371561038 - 0.1020485197430267719746479j) The log-gamma function satisfies an additive form of the recurrence relation for the ordinary gamma function:: >>> z = 2+3j >>> loggamma(z); loggamma(z+1) - log(z) (-2.092851753092733349564189 + 2.302396543466867626153708j) (-2.092851753092733349564189 + 2.302396543466867626153708j) """ siegeltheta = r""" Computes the Riemann-Siegel theta function, .. math :: \theta(t) = \frac{ \log\Gamma\left(\frac{1+2it}{4}\right) - \log\Gamma\left(\frac{1-2it}{4}\right) }{2i} - \frac{\log \pi}{2} t. The Riemann-Siegel theta function is important in providing the phase factor for the Z-function (see :func:`~mpmath.siegelz`). Evaluation is supported for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> siegeltheta(0) 0.0 >>> siegeltheta(inf) +inf >>> siegeltheta(-inf) -inf >>> siegeltheta(1) -1.767547952812290388302216 >>> siegeltheta(10+0.25j) (-3.068638039426838572528867 + 0.05804937947429712998395177j) Arbitrary derivatives may be computed with derivative = k >>> siegeltheta(1234, derivative=2) 0.0004051864079114053109473741 >>> diff(siegeltheta, 1234, n=2) 0.0004051864079114053109473741 The Riemann-Siegel theta function has odd symmetry around `t = 0`, two local extreme points and three real roots including 0 (located symmetrically):: >>> nprint(chop(taylor(siegeltheta, 0, 5))) [0.0, -2.68609, 0.0, 2.69433, 0.0, -6.40218] >>> findroot(diffun(siegeltheta), 7) 6.28983598883690277966509 >>> findroot(siegeltheta, 20) 17.84559954041086081682634 For large `t`, there is a famous asymptotic formula for `\theta(t)`, to first order given by:: >>> t = mpf(10**6) >>> siegeltheta(t) 5488816.353078403444882823 >>> -t*log(2*pi/t)/2-t/2 5488816.745777464310273645 """ grampoint = r""" Gives the `n`-th Gram point `g_n`, defined as the solution to the equation `\theta(g_n) = \pi n` where `\theta(t)` is the Riemann-Siegel theta function (:func:`~mpmath.siegeltheta`). The first few Gram points are:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> grampoint(0) 17.84559954041086081682634 >>> grampoint(1) 23.17028270124630927899664 >>> grampoint(2) 27.67018221781633796093849 >>> grampoint(3) 31.71797995476405317955149 Checking the definition:: >>> siegeltheta(grampoint(3)) 9.42477796076937971538793 >>> 3*pi 9.42477796076937971538793 A large Gram point:: >>> grampoint(10**10) 3293531632.728335454561153 Gram points are useful when studying the Z-function (:func:`~mpmath.siegelz`). See the documentation of that function for additional examples. :func:`~mpmath.grampoint` can solve the defining equation for nonintegral `n`. There is a fixed point where `g(x) = x`:: >>> findroot(lambda x: grampoint(x) - x, 10000) 9146.698193171459265866198 **References** 1. http://mathworld.wolfram.com/GramPoint.html """ siegelz = r""" Computes the Z-function, also known as the Riemann-Siegel Z function, .. math :: Z(t) = e^{i \theta(t)} \zeta(1/2+it) where `\zeta(s)` is the Riemann zeta function (:func:`~mpmath.zeta`) and where `\theta(t)` denotes the Riemann-Siegel theta function (see :func:`~mpmath.siegeltheta`). Evaluation is supported for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> siegelz(1) -0.7363054628673177346778998 >>> siegelz(3+4j) (-0.1852895764366314976003936 - 0.2773099198055652246992479j) The first four derivatives are supported, using the optional *derivative* keyword argument:: >>> siegelz(1234567, derivative=3) 56.89689348495089294249178 >>> diff(siegelz, 1234567, n=3) 56.89689348495089294249178 The Z-function has a Maclaurin expansion:: >>> nprint(chop(taylor(siegelz, 0, 4))) [-1.46035, 0.0, 2.73588, 0.0, -8.39357] The Z-function `Z(t)` is equal to `\pm |\zeta(s)|` on the critical line `s = 1/2+it` (i.e. for real arguments `t` to `Z`). Its zeros coincide with those of the Riemann zeta function:: >>> findroot(siegelz, 14) 14.13472514173469379045725 >>> findroot(siegelz, 20) 21.02203963877155499262848 >>> findroot(zeta, 0.5+14j) (0.5 + 14.13472514173469379045725j) >>> findroot(zeta, 0.5+20j) (0.5 + 21.02203963877155499262848j) Since the Z-function is real-valued on the critical line (and unlike `|\zeta(s)|` analytic), it is useful for investigating the zeros of the Riemann zeta function. For example, one can use a root-finding algorithm based on sign changes:: >>> findroot(siegelz, [100, 200], solver='bisect') 176.4414342977104188888926 To locate roots, Gram points `g_n` which can be computed by :func:`~mpmath.grampoint` are useful. If `(-1)^n Z(g_n)` is positive for two consecutive `n`, then `Z(t)` must have a zero between those points:: >>> g10 = grampoint(10) >>> g11 = grampoint(11) >>> (-1)**10 * siegelz(g10) > 0 True >>> (-1)**11 * siegelz(g11) > 0 True >>> findroot(siegelz, [g10, g11], solver='bisect') 56.44624769706339480436776 >>> g10, g11 (54.67523744685325626632663, 57.54516517954725443703014) """ riemannr = r""" Evaluates the Riemann R function, a smooth approximation of the prime counting function `\pi(x)` (see :func:`~mpmath.primepi`). The Riemann R function gives a fast numerical approximation useful e.g. to roughly estimate the number of primes in a given interval. The Riemann R function is computed using the rapidly convergent Gram series, .. math :: R(x) = 1 + \sum_{k=1}^{\infty} \frac{\log^k x}{k k! \zeta(k+1)}. From the Gram series, one sees that the Riemann R function is a well-defined analytic function (except for a branch cut along the negative real half-axis); it can be evaluated for arbitrary real or complex arguments. The Riemann R function gives a very accurate approximation of the prime counting function. For example, it is wrong by at most 2 for `x < 1000`, and for `x = 10^9` differs from the exact value of `\pi(x)` by 79, or less than two parts in a million. It is about 10 times more accurate than the logarithmic integral estimate (see :func:`~mpmath.li`), which however is even faster to evaluate. It is orders of magnitude more accurate than the extremely fast `x/\log x` estimate. **Examples** For small arguments, the Riemann R function almost exactly gives the prime counting function if rounded to the nearest integer:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> primepi(50), riemannr(50) (15, 14.9757023241462) >>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(100)) 1 >>> max(abs(primepi(n)-int(round(riemannr(n)))) for n in range(300)) 2 The Riemann R function can be evaluated for arguments far too large for exact determination of `\pi(x)` to be computationally feasible with any presently known algorithm:: >>> riemannr(10**30) 1.46923988977204e+28 >>> riemannr(10**100) 4.3619719871407e+97 >>> riemannr(10**1000) 4.3448325764012e+996 A comparison of the Riemann R function and logarithmic integral estimates for `\pi(x)` using exact values of `\pi(10^n)` up to `n = 9`. The fractional error is shown in parentheses:: >>> exact = [4,25,168,1229,9592,78498,664579,5761455,50847534] >>> for n, p in enumerate(exact): ... n += 1 ... r, l = riemannr(10**n), li(10**n) ... rerr, lerr = nstr((r-p)/p,3), nstr((l-p)/p,3) ... print("%i %i %s(%s) %s(%s)" % (n, p, r, rerr, l, lerr)) ... 1 4 4.56458314100509(0.141) 6.1655995047873(0.541) 2 25 25.6616332669242(0.0265) 30.1261415840796(0.205) 3 168 168.359446281167(0.00214) 177.609657990152(0.0572) 4 1229 1226.93121834343(-0.00168) 1246.13721589939(0.0139) 5 9592 9587.43173884197(-0.000476) 9629.8090010508(0.00394) 6 78498 78527.3994291277(0.000375) 78627.5491594622(0.00165) 7 664579 664667.447564748(0.000133) 664918.405048569(0.000511) 8 5761455 5761551.86732017(1.68e-5) 5762209.37544803(0.000131) 9 50847534 50847455.4277214(-1.55e-6) 50849234.9570018(3.35e-5) The derivative of the Riemann R function gives the approximate probability for a number of magnitude `x` to be prime:: >>> diff(riemannr, 1000) 0.141903028110784 >>> mpf(primepi(1050) - primepi(950)) / 100 0.15 Evaluation is supported for arbitrary arguments and at arbitrary precision:: >>> mp.dps = 30 >>> riemannr(7.5) 3.72934743264966261918857135136 >>> riemannr(-4+2j) (-0.551002208155486427591793957644 + 2.16966398138119450043195899746j) """ primepi = r""" Evaluates the prime counting function, `\pi(x)`, which gives the number of primes less than or equal to `x`. The argument `x` may be fractional. The prime counting function is very expensive to evaluate precisely for large `x`, and the present implementation is not optimized in any way. For numerical approximation of the prime counting function, it is better to use :func:`~mpmath.primepi2` or :func:`~mpmath.riemannr`. Some values of the prime counting function:: >>> from mpmath import * >>> [primepi(k) for k in range(20)] [0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8] >>> primepi(3.5) 2 >>> primepi(100000) 9592 """ primepi2 = r""" Returns an interval (as an ``mpi`` instance) providing bounds for the value of the prime counting function `\pi(x)`. For small `x`, :func:`~mpmath.primepi2` returns an exact interval based on the output of :func:`~mpmath.primepi`. For `x > 2656`, a loose interval based on Schoenfeld's inequality .. math :: |\pi(x) - \mathrm{li}(x)| < \frac{\sqrt x \log x}{8 \pi} is returned. This estimate is rigorous assuming the truth of the Riemann hypothesis, and can be computed very quickly. **Examples** Exact values of the prime counting function for small `x`:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> iv.dps = 15; iv.pretty = True >>> primepi2(10) [4.0, 4.0] >>> primepi2(100) [25.0, 25.0] >>> primepi2(1000) [168.0, 168.0] Loose intervals are generated for moderately large `x`: >>> primepi2(10000), primepi(10000) ([1209.0, 1283.0], 1229) >>> primepi2(50000), primepi(50000) ([5070.0, 5263.0], 5133) As `x` increases, the absolute error gets worse while the relative error improves. The exact value of `\pi(10^{23})` is 1925320391606803968923, and :func:`~mpmath.primepi2` gives 9 significant digits:: >>> p = primepi2(10**23) >>> p [1.9253203909477020467e+21, 1.925320392280406229e+21] >>> mpf(p.delta) / mpf(p.a) 6.9219865355293e-10 A more precise, nonrigorous estimate for `\pi(x)` can be obtained using the Riemann R function (:func:`~mpmath.riemannr`). For large enough `x`, the value returned by :func:`~mpmath.primepi2` essentially amounts to a small perturbation of the value returned by :func:`~mpmath.riemannr`:: >>> primepi2(10**100) [4.3619719871407024816e+97, 4.3619719871407032404e+97] >>> riemannr(10**100) 4.3619719871407e+97 """ primezeta = r""" Computes the prime zeta function, which is defined in analogy with the Riemann zeta function (:func:`~mpmath.zeta`) as .. math :: P(s) = \sum_p \frac{1}{p^s} where the sum is taken over all prime numbers `p`. Although this sum only converges for `\mathrm{Re}(s) > 1`, the function is defined by analytic continuation in the half-plane `\mathrm{Re}(s) > 0`. **Examples** Arbitrary-precision evaluation for real and complex arguments is supported:: >>> from mpmath import * >>> mp.dps = 30; mp.pretty = True >>> primezeta(2) 0.452247420041065498506543364832 >>> primezeta(pi) 0.15483752698840284272036497397 >>> mp.dps = 50 >>> primezeta(3) 0.17476263929944353642311331466570670097541212192615 >>> mp.dps = 20 >>> primezeta(3+4j) (-0.12085382601645763295 - 0.013370403397787023602j) The prime zeta function has a logarithmic pole at `s = 1`, with residue equal to the difference of the Mertens and Euler constants:: >>> primezeta(1) +inf >>> extradps(25)(lambda x: primezeta(1+x)+log(x))(+eps) -0.31571845205389007685 >>> mertens-euler -0.31571845205389007685 The analytic continuation to `0 < \mathrm{Re}(s) \le 1` is implemented. In this strip the function exhibits very complex behavior; on the unit interval, it has poles at `1/n` for every squarefree integer `n`:: >>> primezeta(0.5) # Pole at s = 1/2 (-inf + 3.1415926535897932385j) >>> primezeta(0.25) (-1.0416106801757269036 + 0.52359877559829887308j) >>> primezeta(0.5+10j) (0.54892423556409790529 + 0.45626803423487934264j) Although evaluation works in principle for any `\mathrm{Re}(s) > 0`, it should be noted that the evaluation time increases exponentially as `s` approaches the imaginary axis. For large `\mathrm{Re}(s)`, `P(s)` is asymptotic to `2^{-s}`:: >>> primezeta(inf) 0.0 >>> primezeta(10), mpf(2)**-10 (0.00099360357443698021786, 0.0009765625) >>> primezeta(1000) 9.3326361850321887899e-302 >>> primezeta(1000+1000j) (-3.8565440833654995949e-302 - 8.4985390447553234305e-302j) **References** Carl-Erik Froberg, "On the prime zeta function", BIT 8 (1968), pp. 187-202. """ bernpoly = r""" Evaluates the Bernoulli polynomial `B_n(z)`. The first few Bernoulli polynomials are:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(6): ... nprint(chop(taylor(lambda x: bernpoly(n,x), 0, n))) ... [1.0] [-0.5, 1.0] [0.166667, -1.0, 1.0] [0.0, 0.5, -1.5, 1.0] [-0.0333333, 0.0, 1.0, -2.0, 1.0] [0.0, -0.166667, 0.0, 1.66667, -2.5, 1.0] At `z = 0`, the Bernoulli polynomial evaluates to a Bernoulli number (see :func:`~mpmath.bernoulli`):: >>> bernpoly(12, 0), bernoulli(12) (-0.253113553113553, -0.253113553113553) >>> bernpoly(13, 0), bernoulli(13) (0.0, 0.0) Evaluation is accurate for large `n` and small `z`:: >>> mp.dps = 25 >>> bernpoly(100, 0.5) 2.838224957069370695926416e+78 >>> bernpoly(1000, 10.5) 5.318704469415522036482914e+1769 """ polylog = r""" Computes the polylogarithm, defined by the sum .. math :: \mathrm{Li}_s(z) = \sum_{k=1}^{\infty} \frac{z^k}{k^s}. This series is convergent only for `|z| < 1`, so elsewhere the analytic continuation is implied. The polylogarithm should not be confused with the logarithmic integral (also denoted by Li or li), which is implemented as :func:`~mpmath.li`. **Examples** The polylogarithm satisfies a huge number of functional identities. A sample of polylogarithm evaluations is shown below:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> polylog(1,0.5), log(2) (0.693147180559945, 0.693147180559945) >>> polylog(2,0.5), (pi**2-6*log(2)**2)/12 (0.582240526465012, 0.582240526465012) >>> polylog(2,-phi), -log(phi)**2-pi**2/10 (-1.21852526068613, -1.21852526068613) >>> polylog(3,0.5), 7*zeta(3)/8-pi**2*log(2)/12+log(2)**3/6 (0.53721319360804, 0.53721319360804) :func:`~mpmath.polylog` can evaluate the analytic continuation of the polylogarithm when `s` is an integer:: >>> polylog(2, 10) (0.536301287357863 - 7.23378441241546j) >>> polylog(2, -10) -4.1982778868581 >>> polylog(2, 10j) (-3.05968879432873 + 3.71678149306807j) >>> polylog(-2, 10) -0.150891632373114 >>> polylog(-2, -10) 0.067618332081142 >>> polylog(-2, 10j) (0.0384353698579347 + 0.0912451798066779j) Some more examples, with arguments on the unit circle (note that the series definition cannot be used for computation here):: >>> polylog(2,j) (-0.205616758356028 + 0.915965594177219j) >>> j*catalan-pi**2/48 (-0.205616758356028 + 0.915965594177219j) >>> polylog(3,exp(2*pi*j/3)) (-0.534247512515375 + 0.765587078525922j) >>> -4*zeta(3)/9 + 2*j*pi**3/81 (-0.534247512515375 + 0.765587078525921j) Polylogarithms of different order are related by integration and differentiation:: >>> s, z = 3, 0.5 >>> polylog(s+1, z) 0.517479061673899 >>> quad(lambda t: polylog(s,t)/t, [0, z]) 0.517479061673899 >>> z*diff(lambda t: polylog(s+2,t), z) 0.517479061673899 Taylor series expansions around `z = 0` are:: >>> for n in range(-3, 4): ... nprint(taylor(lambda x: polylog(n,x), 0, 5)) ... [0.0, 1.0, 8.0, 27.0, 64.0, 125.0] [0.0, 1.0, 4.0, 9.0, 16.0, 25.0] [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] [0.0, 1.0, 1.0, 1.0, 1.0, 1.0] [0.0, 1.0, 0.5, 0.333333, 0.25, 0.2] [0.0, 1.0, 0.25, 0.111111, 0.0625, 0.04] [0.0, 1.0, 0.125, 0.037037, 0.015625, 0.008] The series defining the polylogarithm is simultaneously a Taylor series and an L-series. For certain values of `z`, the polylogarithm reduces to a pure zeta function:: >>> polylog(pi, 1), zeta(pi) (1.17624173838258, 1.17624173838258) >>> polylog(pi, -1), -altzeta(pi) (-0.909670702980385, -0.909670702980385) Evaluation for arbitrary, nonintegral `s` is supported for `z` within the unit circle: >>> polylog(3+4j, 0.25) (0.24258605789446 - 0.00222938275488344j) >>> nsum(lambda k: 0.25**k / k**(3+4j), [1,inf]) (0.24258605789446 - 0.00222938275488344j) It is also currently supported outside of the unit circle for `z` not too large in magnitude:: >>> polylog(1+j, 20+40j) (-7.1421172179728 - 3.92726697721369j) >>> polylog(1+j, 200+400j) Traceback (most recent call last): ... NotImplementedError: polylog for arbitrary s and z **References** 1. Richard Crandall, "Note on fast polylogarithm computation" http://people.reed.edu/~crandall/papers/Polylog.pdf 2. http://en.wikipedia.org/wiki/Polylogarithm 3. http://mathworld.wolfram.com/Polylogarithm.html """ bell = r""" For `n` a nonnegative integer, ``bell(n,x)`` evaluates the Bell polynomial `B_n(x)`, the first few of which are .. math :: B_0(x) = 1 B_1(x) = x B_2(x) = x^2+x B_3(x) = x^3+3x^2+x If `x = 1` or :func:`~mpmath.bell` is called with only one argument, it gives the `n`-th Bell number `B_n`, which is the number of partitions of a set with `n` elements. By setting the precision to at least `\log_{10} B_n` digits, :func:`~mpmath.bell` provides fast calculation of exact Bell numbers. In general, :func:`~mpmath.bell` computes .. math :: B_n(x) = e^{-x} \left(\mathrm{sinc}(\pi n) + E_n(x)\right) where `E_n(x)` is the generalized exponential function implemented by :func:`~mpmath.polyexp`. This is an extension of Dobinski's formula [1], where the modification is the sinc term ensuring that `B_n(x)` is continuous in `n`; :func:`~mpmath.bell` can thus be evaluated, differentiated, etc for arbitrary complex arguments. **Examples** Simple evaluations:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> bell(0, 2.5) 1.0 >>> bell(1, 2.5) 2.5 >>> bell(2, 2.5) 8.75 Evaluation for arbitrary complex arguments:: >>> bell(5.75+1j, 2-3j) (-10767.71345136587098445143 - 15449.55065599872579097221j) The first few Bell polynomials:: >>> for k in range(7): ... nprint(taylor(lambda x: bell(k,x), 0, k)) ... [1.0] [0.0, 1.0] [0.0, 1.0, 1.0] [0.0, 1.0, 3.0, 1.0] [0.0, 1.0, 7.0, 6.0, 1.0] [0.0, 1.0, 15.0, 25.0, 10.0, 1.0] [0.0, 1.0, 31.0, 90.0, 65.0, 15.0, 1.0] The first few Bell numbers and complementary Bell numbers:: >>> [int(bell(k)) for k in range(10)] [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147] >>> [int(bell(k,-1)) for k in range(10)] [1, -1, 0, 1, 1, -2, -9, -9, 50, 267] Large Bell numbers:: >>> mp.dps = 50 >>> bell(50) 185724268771078270438257767181908917499221852770.0 >>> bell(50,-1) -29113173035759403920216141265491160286912.0 Some even larger values:: >>> mp.dps = 25 >>> bell(1000,-1) -1.237132026969293954162816e+1869 >>> bell(1000) 2.989901335682408421480422e+1927 >>> bell(1000,2) 6.591553486811969380442171e+1987 >>> bell(1000,100.5) 9.101014101401543575679639e+2529 A determinant identity satisfied by Bell numbers:: >>> mp.dps = 15 >>> N = 8 >>> det([[bell(k+j) for j in range(N)] for k in range(N)]) 125411328000.0 >>> superfac(N-1) 125411328000.0 **References** 1. http://mathworld.wolfram.com/DobinskisFormula.html """ polyexp = r""" Evaluates the polyexponential function, defined for arbitrary complex `s`, `z` by the series .. math :: E_s(z) = \sum_{k=1}^{\infty} \frac{k^s}{k!} z^k. `E_s(z)` is constructed from the exponential function analogously to how the polylogarithm is constructed from the ordinary logarithm; as a function of `s` (with `z` fixed), `E_s` is an L-series It is an entire function of both `s` and `z`. The polyexponential function provides a generalization of the Bell polynomials `B_n(x)` (see :func:`~mpmath.bell`) to noninteger orders `n`. In terms of the Bell polynomials, .. math :: E_s(z) = e^z B_s(z) - \mathrm{sinc}(\pi s). Note that `B_n(x)` and `e^{-x} E_n(x)` are identical if `n` is a nonzero integer, but not otherwise. In particular, they differ at `n = 0`. **Examples** Evaluating a series:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> nsum(lambda k: sqrt(k)/fac(k), [1,inf]) 2.101755547733791780315904 >>> polyexp(0.5,1) 2.101755547733791780315904 Evaluation for arbitrary arguments:: >>> polyexp(-3-4j, 2.5+2j) (2.351660261190434618268706 + 1.202966666673054671364215j) Evaluation is accurate for tiny function values:: >>> polyexp(4, -100) 3.499471750566824369520223e-36 If `n` is a nonpositive integer, `E_n` reduces to a special instance of the hypergeometric function `\,_pF_q`:: >>> n = 3 >>> x = pi >>> polyexp(-n,x) 4.042192318847986561771779 >>> x*hyper([1]*(n+1), [2]*(n+1), x) 4.042192318847986561771779 """ cyclotomic = r""" Evaluates the cyclotomic polynomial `\Phi_n(x)`, defined by .. math :: \Phi_n(x) = \prod_{\zeta} (x - \zeta) where `\zeta` ranges over all primitive `n`-th roots of unity (see :func:`~mpmath.unitroots`). An equivalent representation, used for computation, is .. math :: \Phi_n(x) = \prod_{d\mid n}(x^d-1)^{\mu(n/d)} = \Phi_n(x) where `\mu(m)` denotes the Moebius function. The cyclotomic polynomials are integer polynomials, the first of which can be written explicitly as .. math :: \Phi_0(x) = 1 \Phi_1(x) = x - 1 \Phi_2(x) = x + 1 \Phi_3(x) = x^3 + x^2 + 1 \Phi_4(x) = x^2 + 1 \Phi_5(x) = x^4 + x^3 + x^2 + x + 1 \Phi_6(x) = x^2 - x + 1 **Examples** The coefficients of low-order cyclotomic polynomials can be recovered using Taylor expansion:: >>> from mpmath import * >>> mp.dps = 15; mp.pretty = True >>> for n in range(9): ... p = chop(taylor(lambda x: cyclotomic(n,x), 0, 10)) ... print("%s %s" % (n, nstr(p[:10+1-p[::-1].index(1)]))) ... 0 [1.0] 1 [-1.0, 1.0] 2 [1.0, 1.0] 3 [1.0, 1.0, 1.0] 4 [1.0, 0.0, 1.0] 5 [1.0, 1.0, 1.0, 1.0, 1.0] 6 [1.0, -1.0, 1.0] 7 [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] 8 [1.0, 0.0, 0.0, 0.0, 1.0] The definition as a product over primitive roots may be checked by computing the product explicitly (for a real argument, this method will generally introduce numerical noise in the imaginary part):: >>> mp.dps = 25 >>> z = 3+4j >>> cyclotomic(10, z) (-419.0 - 360.0j) >>> fprod(z-r for r in unitroots(10, primitive=True)) (-419.0 - 360.0j) >>> z = 3 >>> cyclotomic(10, z) 61.0 >>> fprod(z-r for r in unitroots(10, primitive=True)) (61.0 - 3.146045605088568607055454e-25j) Up to permutation, the roots of a given cyclotomic polynomial can be checked to agree with the list of primitive roots:: >>> p = taylor(lambda x: cyclotomic(6,x), 0, 6)[:3] >>> for r in polyroots(p[::-1]): ... print(r) ... (0.5 - 0.8660254037844386467637232j) (0.5 + 0.8660254037844386467637232j) >>> >>> for r in unitroots(6, primitive=True): ... print(r) ... (0.5 + 0.8660254037844386467637232j) (0.5 - 0.8660254037844386467637232j) """ meijerg = r""" Evaluates the Meijer G-function, defined as .. math :: G^{m,n}_{p,q} \left( \left. \begin{matrix} a_1, \dots, a_n ; a_{n+1} \dots a_p \\ b_1, \dots, b_m ; b_{m+1} \dots b_q \end{matrix}\; \right| \; z ; r \right) = \frac{1}{2 \pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j+s) \prod_{j=1}^n\Gamma(1-a_j-s)} {\prod_{j=n+1}^{p}\Gamma(a_j+s) \prod_{j=m+1}^q \Gamma(1-b_j-s)} z^{-s/r} ds for an appropriate choice of the contour `L` (see references). There are `p` elements `a_j`. The argument *a_s* should be a pair of lists, the first containing the `n` elements `a_1, \ldots, a_n` and the second containing the `p-n` elements `a_{n+1}, \ldots a_p`. There are `q` elements `b_j`. The argument *b_s* should be a pair of lists, the first containing the `m` elements `b_1, \ldots, b_m` and the second containing the `q-m` elements `b_{m+1}, \ldots b_q`. The implicit tuple `(m, n, p, q)` constitutes the order or degree of the Meijer G-function, and is determined by the lengths of the coefficient vectors. Confusingly, the indices in this tuple appear in a different order from the coefficients, but this notation is standard. The many examples given below should hopefully clear up any potential confusion. **Algorithm** The Meijer G-function is evaluated as a combination of hypergeometric series. There are two versions of the function, which can be selected with the optional *series* argument. *series=1* uses a sum of `m` `\,_pF_{q-1}` functions of `z` *series=2* uses a sum of `n` `\,_qF_{p-1}` functions of `1/z` The default series is chosen based on the degree and `|z|` in order to be consistent with Mathematica's. This definition of the Meijer G-function has a discontinuity at `|z| = 1` for some orders, which can be avoided by explicitly specifying a series. Keyword arguments are forwarded to :func:`~mpmath.hypercomb`. **Examples** Many standard functions are special cases of the Meijer G-function (possibly rescaled and/or with branch cut corrections). We define some test parameters:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> a = mpf(0.75) >>> b = mpf(1.5) >>> z = mpf(2.25) The exponential function: `e^z = G^{1,0}_{0,1} \left( \left. \begin{matrix} - \\ 0 \end{matrix} \; \right| \; -z \right)` >>> meijerg([[],[]], [[0],[]], -z) 9.487735836358525720550369 >>> exp(z) 9.487735836358525720550369 The natural logarithm: `\log(1+z) = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 0 \end{matrix} \; \right| \; -z \right)` >>> meijerg([[1,1],[]], [[1],[0]], z) 1.178654996341646117219023 >>> log(1+z) 1.178654996341646117219023 A rational function: `\frac{z}{z+1} = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 1 \end{matrix} \; \right| \; z \right)` >>> meijerg([[1,1],[]], [[1],[1]], z) 0.6923076923076923076923077 >>> z/(z+1) 0.6923076923076923076923077 The sine and cosine functions: `\frac{1}{\sqrt \pi} \sin(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{1}{2}, 0 \end{matrix} \; \right| \; z \right)` `\frac{1}{\sqrt \pi} \cos(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ 0, \frac{1}{2} \end{matrix} \; \right| \; z \right)` >>> meijerg([[],[]], [[0.5],[0]], (z/2)**2) 0.4389807929218676682296453 >>> sin(z)/sqrt(pi) 0.4389807929218676682296453 >>> meijerg([[],[]], [[0],[0.5]], (z/2)**2) -0.3544090145996275423331762 >>> cos(z)/sqrt(pi) -0.3544090145996275423331762 Bessel functions: `J_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} \end{matrix} \; \right| \; z \right)` `Y_a(2 \sqrt z) = G^{2,0}_{1,3} \left( \left. \begin{matrix} \frac{-a-1}{2} \\ \frac{a}{2}, -\frac{a}{2}, \frac{-a-1}{2} \end{matrix} \; \right| \; z \right)` `(-z)^{a/2} z^{-a/2} I_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} \end{matrix} \; \right| \; -z \right)` `2 K_a(2 \sqrt z) = G^{2,0}_{0,2} \left( \left. \begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} \end{matrix} \; \right| \; z \right)` As the example with the Bessel *I* function shows, a branch factor is required for some arguments when inverting the square root. >>> meijerg([[],[]], [[a/2],[-a/2]], (z/2)**2) 0.5059425789597154858527264 >>> besselj(a,z) 0.5059425789597154858527264 >>> meijerg([[],[(-a-1)/2]], [[a/2,-a/2],[(-a-1)/2]], (z/2)**2) 0.1853868950066556941442559 >>> bessely(a, z) 0.1853868950066556941442559 >>> meijerg([[],[]], [[a/2],[-a/2]], -(z/2)**2) (0.8685913322427653875717476 + 2.096964974460199200551738j) >>> (-z)**(a/2) / z**(a/2) * besseli(a, z) (0.8685913322427653875717476 + 2.096964974460199200551738j) >>> 0.5*meijerg([[],[]], [[a/2,-a/2],[]], (z/2)**2) 0.09334163695597828403796071 >>> besselk(a,z) 0.09334163695597828403796071 Error functions: `\sqrt{\pi} z^{2(a-1)} \mathrm{erfc}(z) = G^{2,0}_{1,2} \left( \left. \begin{matrix} a \\ a-1, a-\frac{1}{2} \end{matrix} \; \right| \; z, \frac{1}{2} \right)` >>> meijerg([[],[a]], [[a-1,a-0.5],[]], z, 0.5) 0.00172839843123091957468712 >>> sqrt(pi) * z**(2*a-2) * erfc(z) 0.00172839843123091957468712 A Meijer G-function of higher degree, (1,1,2,3): >>> meijerg([[a],[b]], [[a],[b,a-1]], z) 1.55984467443050210115617 >>> sin((b-a)*pi)/pi*(exp(z)-1)*z**(a-1) 1.55984467443050210115617 A Meijer G-function of still higher degree, (4,1,2,4), that can be expanded as a messy combination of exponential integrals: >>> meijerg([[a],[2*b-a]], [[b,a,b-0.5,-1-a+2*b],[]], z) 0.3323667133658557271898061 >>> chop(4**(a-b+1)*sqrt(pi)*gamma(2*b-2*a)*z**a*\ ... expint(2*b-2*a, -2*sqrt(-z))*expint(2*b-2*a, 2*sqrt(-z))) 0.3323667133658557271898061 In the following case, different series give different values:: >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2)) -0.06417628097442437076207337 >>> meijerg([[1],[0.25]],[[3],[0.5]],-2,series=1) 0.1428699426155117511873047 >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2,series=2)) -0.06417628097442437076207337 **References** 1. http://en.wikipedia.org/wiki/Meijer_G-function 2. http://mathworld.wolfram.com/MeijerG-Function.html 3. http://functions.wolfram.com/HypergeometricFunctions/MeijerG/ 4. http://functions.wolfram.com/HypergeometricFunctions/MeijerG1/ """ clsin = r""" Computes the Clausen sine function, defined formally by the series .. math :: \mathrm{Cl}_s(z) = \sum_{k=1}^{\infty} \frac{\sin(kz)}{k^s}. The special case `\mathrm{Cl}_2(z)` (i.e. ``clsin(2,z)``) is the classical "Clausen function". More generally, the Clausen function is defined for complex `s` and `z`, even when the series does not converge. The Clausen function is related to the polylogarithm (:func:`~mpmath.polylog`) as .. math :: \mathrm{Cl}_s(z) = \frac{1}{2i}\left(\mathrm{Li}_s\left(e^{iz}\right) - \mathrm{Li}_s\left(e^{-iz}\right)\right) = \mathrm{Im}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}), and this representation can be taken to provide the analytic continuation of the series. The complementary function :func:`~mpmath.clcos` gives the corresponding cosine sum. **Examples** Evaluation for arbitrarily chosen `s` and `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> s, z = 3, 4 >>> clsin(s, z); nsum(lambda k: sin(z*k)/k**s, [1,inf]) -0.6533010136329338746275795 -0.6533010136329338746275795 Using `z + \pi` instead of `z` gives an alternating series:: >>> clsin(s, z+pi) 0.8860032351260589402871624 >>> nsum(lambda k: (-1)**k*sin(z*k)/k**s, [1,inf]) 0.8860032351260589402871624 With `s = 1`, the sum can be expressed in closed form using elementary functions:: >>> z = 1 + sqrt(3) >>> clsin(1, z) 0.2047709230104579724675985 >>> chop((log(1-exp(-j*z)) - log(1-exp(j*z)))/(2*j)) 0.2047709230104579724675985 >>> nsum(lambda k: sin(k*z)/k, [1,inf]) 0.2047709230104579724675985 The classical Clausen function `\mathrm{Cl}_2(\theta)` gives the value of the integral `\int_0^{\theta} -\ln(2\sin(x/2)) dx` for `0 < \theta < 2 \pi`:: >>> cl2 = lambda t: clsin(2, t) >>> cl2(3.5) -0.2465045302347694216534255 >>> -quad(lambda x: ln(2*sin(0.5*x)), [0, 3.5]) -0.2465045302347694216534255 This function is symmetric about `\theta = \pi` with zeros and extreme points:: >>> cl2(0); cl2(pi/3); chop(cl2(pi)); cl2(5*pi/3); chop(cl2(2*pi)) 0.0 1.014941606409653625021203 0.0 -1.014941606409653625021203 0.0 Catalan's constant is a special value:: >>> cl2(pi/2) 0.9159655941772190150546035 >>> +catalan 0.9159655941772190150546035 The Clausen sine function can be expressed in closed form when `s` is an odd integer (becoming zero when `s` < 0):: >>> z = 1 + sqrt(2) >>> clsin(1, z); (pi-z)/2 0.3636895456083490948304773 0.3636895456083490948304773 >>> clsin(3, z); pi**2/6*z - pi*z**2/4 + z**3/12 0.5661751584451144991707161 0.5661751584451144991707161 >>> clsin(-1, z) 0.0 >>> clsin(-3, z) 0.0 It can also be expressed in closed form for even integer `s \le 0`, providing a finite sum for series such as `\sin(z) + \sin(2z) + \sin(3z) + \ldots`:: >>> z = 1 + sqrt(2) >>> clsin(0, z) 0.1903105029507513881275865 >>> cot(z/2)/2 0.1903105029507513881275865 >>> clsin(-2, z) -0.1089406163841548817581392 >>> -cot(z/2)*csc(z/2)**2/4 -0.1089406163841548817581392 Call with ``pi=True`` to multiply `z` by `\pi` exactly:: >>> clsin(3, 3*pi) -8.892316224968072424732898e-26 >>> clsin(3, 3, pi=True) 0.0 Evaluation for complex `s`, `z` in a nonconvergent case:: >>> s, z = -1-j, 1+2j >>> clsin(s, z) (-0.593079480117379002516034 + 0.9038644233367868273362446j) >>> extraprec(20)(nsum)(lambda k: sin(k*z)/k**s, [1,inf]) (-0.593079480117379002516034 + 0.9038644233367868273362446j) """ clcos = r""" Computes the Clausen cosine function, defined formally by the series .. math :: \mathrm{\widetilde{Cl}}_s(z) = \sum_{k=1}^{\infty} \frac{\cos(kz)}{k^s}. This function is complementary to the Clausen sine function :func:`~mpmath.clsin`. In terms of the polylogarithm, .. math :: \mathrm{\widetilde{Cl}}_s(z) = \frac{1}{2}\left(\mathrm{Li}_s\left(e^{iz}\right) + \mathrm{Li}_s\left(e^{-iz}\right)\right) = \mathrm{Re}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}). **Examples** Evaluation for arbitrarily chosen `s` and `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> s, z = 3, 4 >>> clcos(s, z); nsum(lambda k: cos(z*k)/k**s, [1,inf]) -0.6518926267198991308332759 -0.6518926267198991308332759 Using `z + \pi` instead of `z` gives an alternating series:: >>> s, z = 3, 0.5 >>> clcos(s, z+pi) -0.8155530586502260817855618 >>> nsum(lambda k: (-1)**k*cos(z*k)/k**s, [1,inf]) -0.8155530586502260817855618 With `s = 1`, the sum can be expressed in closed form using elementary functions:: >>> z = 1 + sqrt(3) >>> clcos(1, z) -0.6720334373369714849797918 >>> chop(-0.5*(log(1-exp(j*z))+log(1-exp(-j*z)))) -0.6720334373369714849797918 >>> -log(abs(2*sin(0.5*z))) # Equivalent to above when z is real -0.6720334373369714849797918 >>> nsum(lambda k: cos(k*z)/k, [1,inf]) -0.6720334373369714849797918 It can also be expressed in closed form when `s` is an even integer. For example, >>> clcos(2,z) -0.7805359025135583118863007 >>> pi**2/6 - pi*z/2 + z**2/4 -0.7805359025135583118863007 The case `s = 0` gives the renormalized sum of `\cos(z) + \cos(2z) + \cos(3z) + \ldots` (which happens to be the same for any value of `z`):: >>> clcos(0, z) -0.5 >>> nsum(lambda k: cos(k*z), [1,inf]) -0.5 Also the sums .. math :: \cos(z) + 2\cos(2z) + 3\cos(3z) + \ldots and .. math :: \cos(z) + 2^n \cos(2z) + 3^n \cos(3z) + \ldots for higher integer powers `n = -s` can be done in closed form. They are zero when `n` is positive and even (`s` negative and even):: >>> clcos(-1, z); 1/(2*cos(z)-2) -0.2607829375240542480694126 -0.2607829375240542480694126 >>> clcos(-3, z); (2+cos(z))*csc(z/2)**4/8 0.1472635054979944390848006 0.1472635054979944390848006 >>> clcos(-2, z); clcos(-4, z); clcos(-6, z) 0.0 0.0 0.0 With `z = \pi`, the series reduces to that of the Riemann zeta function (more generally, if `z = p \pi/q`, it is a finite sum over Hurwitz zeta function values):: >>> clcos(2.5, 0); zeta(2.5) 1.34148725725091717975677 1.34148725725091717975677 >>> clcos(2.5, pi); -altzeta(2.5) -0.8671998890121841381913472 -0.8671998890121841381913472 Call with ``pi=True`` to multiply `z` by `\pi` exactly:: >>> clcos(-3, 2*pi) 2.997921055881167659267063e+102 >>> clcos(-3, 2, pi=True) 0.008333333333333333333333333 Evaluation for complex `s`, `z` in a nonconvergent case:: >>> s, z = -1-j, 1+2j >>> clcos(s, z) (0.9407430121562251476136807 + 0.715826296033590204557054j) >>> extraprec(20)(nsum)(lambda k: cos(k*z)/k**s, [1,inf]) (0.9407430121562251476136807 + 0.715826296033590204557054j) """ whitm = r""" Evaluates the Whittaker function `M(k,m,z)`, which gives a solution to the Whittaker differential equation .. math :: \frac{d^2f}{dz^2} + \left(-\frac{1}{4}+\frac{k}{z}+ \frac{(\frac{1}{4}-m^2)}{z^2}\right) f = 0. A second solution is given by :func:`~mpmath.whitw`. The Whittaker functions are defined in Abramowitz & Stegun, section 13.1. They are alternate forms of the confluent hypergeometric functions `\,_1F_1` and `U`: .. math :: M(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} \,_1F_1(\tfrac{1}{2}+m-k, 1+2m, z) W(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} U(\tfrac{1}{2}+m-k, 1+2m, z). **Examples** Evaluation for arbitrary real and complex arguments is supported:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> whitm(1, 1, 1) 0.7302596799460411820509668 >>> whitm(1, 1, -1) (0.0 - 1.417977827655098025684246j) >>> whitm(j, j/2, 2+3j) (3.245477713363581112736478 - 0.822879187542699127327782j) >>> whitm(2, 3, 100000) 4.303985255686378497193063e+21707 Evaluation at zero:: >>> whitm(1,-1,0); whitm(1,-0.5,0); whitm(1,0,0) +inf nan 0.0 We can verify that :func:`~mpmath.whitm` numerically satisfies the differential equation for arbitrarily chosen values:: >>> k = mpf(0.25) >>> m = mpf(1.5) >>> f = lambda z: whitm(k,m,z) >>> for z in [-1, 2.5, 3, 1+2j]: ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) ... 0.0 0.0 0.0 0.0 An integral involving both :func:`~mpmath.whitm` and :func:`~mpmath.whitw`, verifying evaluation along the real axis:: >>> quad(lambda x: exp(-x)*whitm(3,2,x)*whitw(1,-2,x), [0,inf]) 3.438869842576800225207341 >>> 128/(21*sqrt(pi)) 3.438869842576800225207341 """ whitw = r""" Evaluates the Whittaker function `W(k,m,z)`, which gives a second solution to the Whittaker differential equation. (See :func:`~mpmath.whitm`.) **Examples** Evaluation for arbitrary real and complex arguments is supported:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> whitw(1, 1, 1) 1.19532063107581155661012 >>> whitw(1, 1, -1) (-0.9424875979222187313924639 - 0.2607738054097702293308689j) >>> whitw(j, j/2, 2+3j) (0.1782899315111033879430369 - 0.01609578360403649340169406j) >>> whitw(2, 3, 100000) 1.887705114889527446891274e-21705 >>> whitw(-1, -1, 100) 1.905250692824046162462058e-24 Evaluation at zero:: >>> for m in [-1, -0.5, 0, 0.5, 1]: ... whitw(1, m, 0) ... +inf nan 0.0 nan +inf We can verify that :func:`~mpmath.whitw` numerically satisfies the differential equation for arbitrarily chosen values:: >>> k = mpf(0.25) >>> m = mpf(1.5) >>> f = lambda z: whitw(k,m,z) >>> for z in [-1, 2.5, 3, 1+2j]: ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) ... 0.0 0.0 0.0 0.0 """ ber = r""" Computes the Kelvin function ber, which for real arguments gives the real part of the Bessel J function of a rotated argument .. math :: J_n\left(x e^{3\pi i/4}\right) = \mathrm{ber}_n(x) + i \mathrm{bei}_n(x). The imaginary part is given by :func:`~mpmath.bei`. **Plots** .. literalinclude :: /modules/mpmath/plots/ber.py .. image :: /modules/mpmath/plots/ber.png **Examples** Verifying the defining relation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> n, x = 2, 3.5 >>> ber(n,x) 1.442338852571888752631129 >>> bei(n,x) -0.948359035324558320217678 >>> besselj(n, x*root(1,8,3)) (1.442338852571888752631129 - 0.948359035324558320217678j) The ber and bei functions are also defined by analytic continuation for complex arguments:: >>> ber(1+j, 2+3j) (4.675445984756614424069563 - 15.84901771719130765656316j) >>> bei(1+j, 2+3j) (15.83886679193707699364398 + 4.684053288183046528703611j) """ bei = r""" Computes the Kelvin function bei, which for real arguments gives the imaginary part of the Bessel J function of a rotated argument. See :func:`~mpmath.ber`. """ ker = r""" Computes the Kelvin function ker, which for real arguments gives the real part of the (rescaled) Bessel K function of a rotated argument .. math :: e^{-\pi i/2} K_n\left(x e^{3\pi i/4}\right) = \mathrm{ker}_n(x) + i \mathrm{kei}_n(x). The imaginary part is given by :func:`~mpmath.kei`. **Plots** .. literalinclude :: /modules/mpmath/plots/ker.py .. image :: /modules/mpmath/plots/ker.png **Examples** Verifying the defining relation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> n, x = 2, 4.5 >>> ker(n,x) 0.02542895201906369640249801 >>> kei(n,x) -0.02074960467222823237055351 >>> exp(-n*pi*j/2) * besselk(n, x*root(1,8,1)) (0.02542895201906369640249801 - 0.02074960467222823237055351j) The ker and kei functions are also defined by analytic continuation for complex arguments:: >>> ker(1+j, 3+4j) (1.586084268115490421090533 - 2.939717517906339193598719j) >>> kei(1+j, 3+4j) (-2.940403256319453402690132 - 1.585621643835618941044855j) """ kei = r""" Computes the Kelvin function kei, which for real arguments gives the imaginary part of the (rescaled) Bessel K function of a rotated argument. See :func:`~mpmath.ker`. """ struveh = r""" Gives the Struve function .. math :: \,\mathbf{H}_n(z) = \sum_{k=0}^\infty \frac{(-1)^k}{\Gamma(k+\frac{3}{2}) \Gamma(k+n+\frac{3}{2})} {\left({\frac{z}{2}}\right)}^{2k+n+1} which is a solution to the Struve differential equation .. math :: z^2 f''(z) + z f'(z) + (z^2-n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. **Examples** Evaluation for arbitrary real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> struveh(0, 3.5) 0.3608207733778295024977797 >>> struveh(-1, 10) -0.255212719726956768034732 >>> struveh(1, -100.5) 0.5819566816797362287502246 >>> struveh(2.5, 10000000000000) 3153915652525200060.308937 >>> struveh(2.5, -10000000000000) (0.0 - 3153915652525200060.308937j) >>> struveh(1+j, 1000000+4000000j) (-3.066421087689197632388731e+1737173 - 1.596619701076529803290973e+1737173j) A Struve function of half-integer order is elementary; for example: >>> z = 3 >>> struveh(0.5, 3) 0.9167076867564138178671595 >>> sqrt(2/(pi*z))*(1-cos(z)) 0.9167076867564138178671595 Numerically verifying the differential equation:: >>> z = mpf(4.5) >>> n = 3 >>> f = lambda z: struveh(n,z) >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) + (z**2-n**2)*f(z) >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi >>> lhs 17.40359302709875496632744 >>> rhs 17.40359302709875496632744 """ struvel = r""" Gives the modified Struve function .. math :: \,\mathbf{L}_n(z) = -i e^{-n\pi i/2} \mathbf{H}_n(i z) which solves to the modified Struve differential equation .. math :: z^2 f''(z) + z f'(z) - (z^2+n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. **Examples** Evaluation for arbitrary real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> struvel(0, 3.5) 7.180846515103737996249972 >>> struvel(-1, 10) 2670.994904980850550721511 >>> struvel(1, -100.5) 1.757089288053346261497686e+42 >>> struvel(2.5, 10000000000000) 4.160893281017115450519948e+4342944819025 >>> struvel(2.5, -10000000000000) (0.0 - 4.160893281017115450519948e+4342944819025j) >>> struvel(1+j, 700j) (-0.1721150049480079451246076 + 0.1240770953126831093464055j) >>> struvel(1+j, 1000000+4000000j) (-2.973341637511505389128708e+434290 - 5.164633059729968297147448e+434290j) Numerically verifying the differential equation:: >>> z = mpf(3.5) >>> n = 3 >>> f = lambda z: struvel(n,z) >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) - (z**2+n**2)*f(z) >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi >>> lhs 6.368850306060678353018165 >>> rhs 6.368850306060678353018165 """ appellf1 = r""" Gives the Appell F1 hypergeometric function of two variables, .. math :: F_1(a,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c)_{m+n}} \frac{x^m y^n}{m! n!}. This series is only generally convergent when `|x| < 1` and `|y| < 1`, although :func:`~mpmath.appellf1` can evaluate an analytic continuation with respecto to either variable, and sometimes both. **Examples** Evaluation is supported for real and complex parameters:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf1(1,0,0.5,1,0.5,0.25) 1.154700538379251529018298 >>> appellf1(1,1+j,0.5,1,0.5,0.5j) (1.138403860350148085179415 + 1.510544741058517621110615j) For some integer parameters, the F1 series reduces to a polynomial:: >>> appellf1(2,-4,-3,1,2,5) -816.0 >>> appellf1(-5,1,2,1,4,5) -20528.0 The analytic continuation with respect to either `x` or `y`, and sometimes with respect to both, can be evaluated:: >>> appellf1(2,3,4,5,100,0.5) (0.0006231042714165329279738662 + 0.0000005769149277148425774499857j) >>> appellf1('1.1', '0.3', '0.2+2j', '0.4', '0.2', 1.5+3j) (-0.1782604566893954897128702 + 0.002472407104546216117161499j) >>> appellf1(1,2,3,4,10,12) -0.07122993830066776374929313 For certain arguments, F1 reduces to an ordinary hypergeometric function:: >>> appellf1(1,2,3,5,0.5,0.25) 1.547902270302684019335555 >>> 4*hyp2f1(1,2,5,'1/3')/3 1.547902270302684019335555 >>> appellf1(1,2,3,4,0,1.5) (-1.717202506168937502740238 - 2.792526803190927323077905j) >>> hyp2f1(1,3,4,1.5) (-1.717202506168937502740238 - 2.792526803190927323077905j) The F1 function satisfies a system of partial differential equations:: >>> a,b1,b2,c,x,y = map(mpf, [1,0.5,0.25,1.125,0.25,-0.25]) >>> F = lambda x,y: appellf1(a,b1,b2,c,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) + ... y*(1-x)*diff(F,(x,y),(1,1)) + ... (c-(a+b1+1)*x)*diff(F,(x,y),(1,0)) - ... b1*y*diff(F,(x,y),(0,1)) - ... a*b1*F(x,y)) 0.0 >>> >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) + ... x*(1-y)*diff(F,(x,y),(1,1)) + ... (c-(a+b2+1)*y)*diff(F,(x,y),(0,1)) - ... b2*x*diff(F,(x,y),(1,0)) - ... a*b2*F(x,y)) 0.0 The Appell F1 function allows for closed-form evaluation of various integrals, such as any integral of the form `\int x^r (x+a)^p (x+b)^q dx`:: >>> def integral(a,b,p,q,r,x1,x2): ... a,b,p,q,r,x1,x2 = map(mpmathify, [a,b,p,q,r,x1,x2]) ... f = lambda x: x**r * (x+a)**p * (x+b)**q ... def F(x): ... v = x**(r+1)/(r+1) * (a+x)**p * (b+x)**q ... v *= (1+x/a)**(-p) ... v *= (1+x/b)**(-q) ... v *= appellf1(r+1,-p,-q,2+r,-x/a,-x/b) ... return v ... print("Num. quad: %s" % quad(f, [x1,x2])) ... print("Appell F1: %s" % (F(x2)-F(x1))) ... >>> integral('1/5','4/3','-2','3','1/2',0,1) Num. quad: 9.073335358785776206576981 Appell F1: 9.073335358785776206576981 >>> integral('3/2','4/3','-2','3','1/2',0,1) Num. quad: 1.092829171999626454344678 Appell F1: 1.092829171999626454344678 >>> integral('3/2','4/3','-2','3','1/2',12,25) Num. quad: 1106.323225040235116498927 Appell F1: 1106.323225040235116498927 Also incomplete elliptic integrals fall into this category [1]:: >>> def E(z, m): ... if (pi/2).ae(z): ... return ellipe(m) ... return 2*round(re(z)/pi)*ellipe(m) + mpf(-1)**round(re(z)/pi)*\ ... sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) ... >>> z, m = 1, 0.5 >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) 0.9273298836244400669659042 0.9273298836244400669659042 >>> z, m = 3, 2 >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) (1.057495752337234229715836 + 1.198140234735592207439922j) (1.057495752337234229715836 + 1.198140234735592207439922j) **References** 1. [WolframFunctions]_ http://functions.wolfram.com/EllipticIntegrals/EllipticE2/26/01/ 2. [SrivastavaKarlsson]_ 3. [CabralRosetti]_ 4. [Vidunas]_ 5. [Slater]_ """ angerj = r""" Gives the Anger function .. math :: \mathbf{J}_{\nu}(z) = \frac{1}{\pi} \int_0^{\pi} \cos(\nu t - z \sin t) dt which is an entire function of both the parameter `\nu` and the argument `z`. It solves the inhomogeneous Bessel differential equation .. math :: f''(z) + \frac{1}{z}f'(z) + \left(1-\frac{\nu^2}{z^2}\right) f(z) = \frac{(z-\nu)}{\pi z^2} \sin(\pi \nu). **Examples** Evaluation for real and complex parameter and argument:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> angerj(2,3) 0.4860912605858910769078311 >>> angerj(-3+4j, 2+5j) (-5033.358320403384472395612 + 585.8011892476145118551756j) >>> angerj(3.25, 1e6j) (4.630743639715893346570743e+434290 - 1.117960409887505906848456e+434291j) >>> angerj(-1.5, 1e6) 0.0002795719747073879393087011 The Anger function coincides with the Bessel J-function when `\nu` is an integer:: >>> angerj(1,3); besselj(1,3) 0.3390589585259364589255146 0.3390589585259364589255146 >>> angerj(1.5,3); besselj(1.5,3) 0.4088969848691080859328847 0.4777182150870917715515015 Verifying the differential equation:: >>> v,z = mpf(2.25), 0.75 >>> f = lambda z: angerj(v,z) >>> diff(f,z,2) + diff(f,z)/z + (1-(v/z)**2)*f(z) -0.6002108774380707130367995 >>> (z-v)/(pi*z**2) * sinpi(v) -0.6002108774380707130367995 Verifying the integral representation:: >>> angerj(v,z) 0.1145380759919333180900501 >>> quad(lambda t: cos(v*t-z*sin(t))/pi, [0,pi]) 0.1145380759919333180900501 **References** 1. [DLMF]_ section 11.10: Anger-Weber Functions """ webere = r""" Gives the Weber function .. math :: \mathbf{E}_{\nu}(z) = \frac{1}{\pi} \int_0^{\pi} \sin(\nu t - z \sin t) dt which is an entire function of both the parameter `\nu` and the argument `z`. It solves the inhomogeneous Bessel differential equation .. math :: f''(z) + \frac{1}{z}f'(z) + \left(1-\frac{\nu^2}{z^2}\right) f(z) = -\frac{1}{\pi z^2} (z+\nu+(z-\nu)\cos(\pi \nu)). **Examples** Evaluation for real and complex parameter and argument:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> webere(2,3) -0.1057668973099018425662646 >>> webere(-3+4j, 2+5j) (-585.8081418209852019290498 - 5033.314488899926921597203j) >>> webere(3.25, 1e6j) (-1.117960409887505906848456e+434291 - 4.630743639715893346570743e+434290j) >>> webere(3.25, 1e6) -0.00002812518265894315604914453 Up to addition of a rational function of `z`, the Weber function coincides with the Struve H-function when `\nu` is an integer:: >>> webere(1,3); 2/pi-struveh(1,3) -0.3834897968188690177372881 -0.3834897968188690177372881 >>> webere(5,3); 26/(35*pi)-struveh(5,3) 0.2009680659308154011878075 0.2009680659308154011878075 Verifying the differential equation:: >>> v,z = mpf(2.25), 0.75 >>> f = lambda z: webere(v,z) >>> diff(f,z,2) + diff(f,z)/z + (1-(v/z)**2)*f(z) -1.097441848875479535164627 >>> -(z+v+(z-v)*cospi(v))/(pi*z**2) -1.097441848875479535164627 Verifying the integral representation:: >>> webere(v,z) 0.1486507351534283744485421 >>> quad(lambda t: sin(v*t-z*sin(t))/pi, [0,pi]) 0.1486507351534283744485421 **References** 1. [DLMF]_ section 11.10: Anger-Weber Functions """ lommels1 = r""" Gives the Lommel function `s_{\mu,\nu}` or `s^{(1)}_{\mu,\nu}` .. math :: s_{\mu,\nu}(z) = \frac{z^{\mu+1}}{(\mu-\nu+1)(\mu+\nu+1)} \,_1F_2\left(1; \frac{\mu-\nu+3}{2}, \frac{\mu+\nu+3}{2}; -\frac{z^2}{4} \right) which solves the inhomogeneous Bessel equation .. math :: z^2 f''(z) + z f'(z) + (z^2-\nu^2) f(z) = z^{\mu+1}. A second solution is given by :func:`~mpmath.lommels2`. **Plots** .. literalinclude :: /modules/mpmath/plots/lommels1.py .. image :: /modules/mpmath/plots/lommels1.png **Examples** An integral representation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> u,v,z = 0.25, 0.125, mpf(0.75) >>> lommels1(u,v,z) 0.4276243877565150372999126 >>> (bessely(v,z)*quad(lambda t: t**u*besselj(v,t), [0,z]) - \ ... besselj(v,z)*quad(lambda t: t**u*bessely(v,t), [0,z]))*(pi/2) 0.4276243877565150372999126 A special value:: >>> lommels1(v,v,z) 0.5461221367746048054932553 >>> gamma(v+0.5)*sqrt(pi)*power(2,v-1)*struveh(v,z) 0.5461221367746048054932553 Verifying the differential equation:: >>> f = lambda z: lommels1(u,v,z) >>> z**2*diff(f,z,2) + z*diff(f,z) + (z**2-v**2)*f(z) 0.6979536443265746992059141 >>> z**(u+1) 0.6979536443265746992059141 **References** 1. [GradshteynRyzhik]_ 2. [Weisstein]_ http://mathworld.wolfram.com/LommelFunction.html """ lommels2 = r""" Gives the second Lommel function `S_{\mu,\nu}` or `s^{(2)}_{\mu,\nu}` .. math :: S_{\mu,\nu}(z) = s_{\mu,\nu}(z) + 2^{\mu-1} \Gamma\left(\tfrac{1}{2}(\mu-\nu+1)\right) \Gamma\left(\tfrac{1}{2}(\mu+\nu+1)\right) \times \left[\sin(\tfrac{1}{2}(\mu-\nu)\pi) J_{\nu}(z) - \cos(\tfrac{1}{2}(\mu-\nu)\pi) Y_{\nu}(z) \right] which solves the same differential equation as :func:`~mpmath.lommels1`. **Plots** .. literalinclude :: /modules/mpmath/plots/lommels2.py .. image :: /modules/mpmath/plots/lommels2.png **Examples** For large `|z|`, `S_{\mu,\nu} \sim z^{\mu-1}`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> lommels2(10,2,30000) 1.968299831601008419949804e+40 >>> power(30000,9) 1.9683e+40 A special value:: >>> u,v,z = 0.5, 0.125, mpf(0.75) >>> lommels2(v,v,z) 0.9589683199624672099969765 >>> (struveh(v,z)-bessely(v,z))*power(2,v-1)*sqrt(pi)*gamma(v+0.5) 0.9589683199624672099969765 Verifying the differential equation:: >>> f = lambda z: lommels2(u,v,z) >>> z**2*diff(f,z,2) + z*diff(f,z) + (z**2-v**2)*f(z) 0.6495190528383289850727924 >>> z**(u+1) 0.6495190528383289850727924 **References** 1. [GradshteynRyzhik]_ 2. [Weisstein]_ http://mathworld.wolfram.com/LommelFunction.html """ appellf2 = r""" Gives the Appell F2 hypergeometric function of two variables .. math :: F_2(a,b_1,b_2,c_1,c_2,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b_1)_m (b_2)_n}{(c_1)_m (c_2)_n} \frac{x^m y^n}{m! n!}. The series is generally absolutely convergent for `|x| + |y| < 1`. **Examples** Evaluation for real and complex arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf2(1,2,3,4,5,0.25,0.125) 1.257417193533135344785602 >>> appellf2(1,-3,-4,2,3,2,3) -42.8 >>> appellf2(0.5,0.25,-0.25,2,3,0.25j,0.25) (0.9880539519421899867041719 + 0.01497616165031102661476978j) >>> chop(appellf2(1,1+j,1-j,3j,-3j,0.25,0.25)) 1.201311219287411337955192 >>> appellf2(1,1,1,4,6,0.125,16) (-0.09455532250274744282125152 - 0.7647282253046207836769297j) A transformation formula:: >>> a,b1,b2,c1,c2,x,y = map(mpf, [1,2,0.5,0.25,1.625,-0.125,0.125]) >>> appellf2(a,b1,b2,c1,c2,x,y) 0.2299211717841180783309688 >>> (1-x)**(-a)*appellf2(a,c1-b1,b2,c1,c2,x/(x-1),y/(1-x)) 0.2299211717841180783309688 A system of partial differential equations satisfied by F2:: >>> a,b1,b2,c1,c2,x,y = map(mpf, [1,0.5,0.25,1.125,1.5,0.0625,-0.0625]) >>> F = lambda x,y: appellf2(a,b1,b2,c1,c2,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) - ... x*y*diff(F,(x,y),(1,1)) + ... (c1-(a+b1+1)*x)*diff(F,(x,y),(1,0)) - ... b1*y*diff(F,(x,y),(0,1)) - ... a*b1*F(x,y)) 0.0 >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) - ... x*y*diff(F,(x,y),(1,1)) + ... (c2-(a+b2+1)*y)*diff(F,(x,y),(0,1)) - ... b2*x*diff(F,(x,y),(1,0)) - ... a*b2*F(x,y)) 0.0 **References** See references for :func:`~mpmath.appellf1`. """ appellf3 = r""" Gives the Appell F3 hypergeometric function of two variables .. math :: F_3(a_1,a_2,b_1,b_2,c,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a_1)_m (a_2)_n (b_1)_m (b_2)_n}{(c)_{m+n}} \frac{x^m y^n}{m! n!}. The series is generally absolutely convergent for `|x| < 1, |y| < 1`. **Examples** Evaluation for various parameters and variables:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf3(1,2,3,4,5,0.5,0.25) 2.221557778107438938158705 >>> appellf3(1,2,3,4,5,6,0); hyp2f1(1,3,5,6) (-0.5189554589089861284537389 - 0.1454441043328607980769742j) (-0.5189554589089861284537389 - 0.1454441043328607980769742j) >>> appellf3(1,-2,-3,1,1,4,6) -17.4 >>> appellf3(1,2,-3,1,1,4,6) (17.7876136773677356641825 + 19.54768762233649126154534j) >>> appellf3(1,2,-3,1,1,6,4) (85.02054175067929402953645 + 148.4402528821177305173599j) >>> chop(appellf3(1+j,2,1-j,2,3,0.25,0.25)) 1.719992169545200286696007 Many transformations and evaluations for special combinations of the parameters are possible, e.g.: >>> a,b,c,x,y = map(mpf, [0.5,0.25,0.125,0.125,-0.125]) >>> appellf3(a,c-a,b,c-b,c,x,y) 1.093432340896087107444363 >>> (1-y)**(a+b-c)*hyp2f1(a,b,c,x+y-x*y) 1.093432340896087107444363 >>> x**2*appellf3(1,1,1,1,3,x,-x) 0.01568646277445385390945083 >>> polylog(2,x**2) 0.01568646277445385390945083 >>> a1,a2,b1,b2,c,x = map(mpf, [0.5,0.25,0.125,0.5,4.25,0.125]) >>> appellf3(a1,a2,b1,b2,c,x,1) 1.03947361709111140096947 >>> gammaprod([c,c-a2-b2],[c-a2,c-b2])*hyp3f2(a1,b1,c-a2-b2,c-a2,c-b2,x) 1.03947361709111140096947 The Appell F3 function satisfies a pair of partial differential equations:: >>> a1,a2,b1,b2,c,x,y = map(mpf, [0.5,0.25,0.125,0.5,0.625,0.0625,-0.0625]) >>> F = lambda x,y: appellf3(a1,a2,b1,b2,c,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) + ... y*diff(F,(x,y),(1,1)) + ... (c-(a1+b1+1)*x)*diff(F,(x,y),(1,0)) - ... a1*b1*F(x,y)) 0.0 >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) + ... x*diff(F,(x,y),(1,1)) + ... (c-(a2+b2+1)*y)*diff(F,(x,y),(0,1)) - ... a2*b2*F(x,y)) 0.0 **References** See references for :func:`~mpmath.appellf1`. """ appellf4 = r""" Gives the Appell F4 hypergeometric function of two variables .. math :: F_4(a,b,c_1,c_2,x,y) = \sum_{m=0}^{\infty} \sum_{n=0}^{\infty} \frac{(a)_{m+n} (b)_{m+n}}{(c_1)_m (c_2)_n} \frac{x^m y^n}{m! n!}. The series is generally absolutely convergent for `\sqrt{|x|} + \sqrt{|y|} < 1`. **Examples** Evaluation for various parameters and arguments:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> appellf4(1,1,2,2,0.25,0.125) 1.286182069079718313546608 >>> appellf4(-2,-3,4,5,4,5) 34.8 >>> appellf4(5,4,2,3,0.25j,-0.125j) (-0.2585967215437846642163352 + 2.436102233553582711818743j) Reduction to `\,_2F_1` in a special case:: >>> a,b,c,x,y = map(mpf, [0.5,0.25,0.125,0.125,-0.125]) >>> appellf4(a,b,c,a+b-c+1,x*(1-y),y*(1-x)) 1.129143488466850868248364 >>> hyp2f1(a,b,c,x)*hyp2f1(a,b,a+b-c+1,y) 1.129143488466850868248364 A system of partial differential equations satisfied by F4:: >>> a,b,c1,c2,x,y = map(mpf, [1,0.5,0.25,1.125,0.0625,-0.0625]) >>> F = lambda x,y: appellf4(a,b,c1,c2,x,y) >>> chop(x*(1-x)*diff(F,(x,y),(2,0)) - ... y**2*diff(F,(x,y),(0,2)) - ... 2*x*y*diff(F,(x,y),(1,1)) + ... (c1-(a+b+1)*x)*diff(F,(x,y),(1,0)) - ... ((a+b+1)*y)*diff(F,(x,y),(0,1)) - ... a*b*F(x,y)) 0.0 >>> chop(y*(1-y)*diff(F,(x,y),(0,2)) - ... x**2*diff(F,(x,y),(2,0)) - ... 2*x*y*diff(F,(x,y),(1,1)) + ... (c2-(a+b+1)*y)*diff(F,(x,y),(0,1)) - ... ((a+b+1)*x)*diff(F,(x,y),(1,0)) - ... a*b*F(x,y)) 0.0 **References** See references for :func:`~mpmath.appellf1`. """ zeta = r""" Computes the Riemann zeta function .. math :: \zeta(s) = 1+\frac{1}{2^s}+\frac{1}{3^s}+\frac{1}{4^s}+\ldots or, with `a \ne 1`, the more general Hurwitz zeta function .. math :: \zeta(s,a) = \sum_{k=0}^\infty \frac{1}{(a+k)^s}. Optionally, ``zeta(s, a, n)`` computes the `n`-th derivative with respect to `s`, .. math :: \zeta^{(n)}(s,a) = (-1)^n \sum_{k=0}^\infty \frac{\log^n(a+k)}{(a+k)^s}. Although these series only converge for `\Re(s) > 1`, the Riemann and Hurwitz zeta functions are defined through analytic continuation for arbitrary complex `s \ne 1` (`s = 1` is a pole). The implementation uses three algorithms: the Borwein algorithm for the Riemann zeta function when `s` is close to the real line; the Riemann-Siegel formula for the Riemann zeta function when `s` is large imaginary, and Euler-Maclaurin summation in all other cases. The reflection formula for `\Re(s) < 0` is implemented in some cases. The algorithm can be chosen with ``method = 'borwein'``, ``method='riemann-siegel'`` or ``method = 'euler-maclaurin'``. The parameter `a` is usually a rational number `a = p/q`, and may be specified as such by passing an integer tuple `(p, q)`. Evaluation is supported for arbitrary complex `a`, but may be slow and/or inaccurate when `\Re(s) < 0` for nonrational `a` or when computing derivatives. **Examples** Some values of the Riemann zeta function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> zeta(2); pi**2 / 6 1.644934066848226436472415 1.644934066848226436472415 >>> zeta(0) -0.5 >>> zeta(-1) -0.08333333333333333333333333 >>> zeta(-2) 0.0 For large positive `s`, `\zeta(s)` rapidly approaches 1:: >>> zeta(50) 1.000000000000000888178421 >>> zeta(100) 1.0 >>> zeta(inf) 1.0 >>> 1-sum((zeta(k)-1)/k for k in range(2,85)); +euler 0.5772156649015328606065121 0.5772156649015328606065121 >>> nsum(lambda k: zeta(k)-1, [2, inf]) 1.0 Evaluation is supported for complex `s` and `a`: >>> zeta(-3+4j) (-0.03373057338827757067584698 + 0.2774499251557093745297677j) >>> zeta(2+3j, -1+j) (389.6841230140842816370741 + 295.2674610150305334025962j) The Riemann zeta function has so-called nontrivial zeros on the critical line `s = 1/2 + it`:: >>> findroot(zeta, 0.5+14j); zetazero(1) (0.5 + 14.13472514173469379045725j) (0.5 + 14.13472514173469379045725j) >>> findroot(zeta, 0.5+21j); zetazero(2) (0.5 + 21.02203963877155499262848j) (0.5 + 21.02203963877155499262848j) >>> findroot(zeta, 0.5+25j); zetazero(3) (0.5 + 25.01085758014568876321379j) (0.5 + 25.01085758014568876321379j) >>> chop(zeta(zetazero(10))) 0.0 Evaluation on and near the critical line is supported for large heights `t` by means of the Riemann-Siegel formula (currently for `a = 1`, `n \le 4`):: >>> zeta(0.5+100000j) (1.073032014857753132114076 + 5.780848544363503984261041j) >>> zeta(0.75+1000000j) (0.9535316058375145020351559 + 0.9525945894834273060175651j) >>> zeta(0.5+10000000j) (11.45804061057709254500227 - 8.643437226836021723818215j) >>> zeta(0.5+100000000j, derivative=1) (51.12433106710194942681869 + 43.87221167872304520599418j) >>> zeta(0.5+100000000j, derivative=2) (-444.2760822795430400549229 - 896.3789978119185981665403j) >>> zeta(0.5+100000000j, derivative=3) (3230.72682687670422215339 + 14374.36950073615897616781j) >>> zeta(0.5+100000000j, derivative=4) (-11967.35573095046402130602 - 218945.7817789262839266148j) >>> zeta(1+10000000j) # off the line (2.859846483332530337008882 + 0.491808047480981808903986j) >>> zeta(1+10000000j, derivative=1) (-4.333835494679647915673205 - 0.08405337962602933636096103j) >>> zeta(1+10000000j, derivative=4) (453.2764822702057701894278 - 581.963625832768189140995j) For investigation of the zeta function zeros, the Riemann-Siegel Z-function is often more convenient than working with the Riemann zeta function directly (see :func:`~mpmath.siegelz`). Some values of the Hurwitz zeta function:: >>> zeta(2, 3); -5./4 + pi**2/6 0.3949340668482264364724152 0.3949340668482264364724152 >>> zeta(2, (3,4)); pi**2 - 8*catalan 2.541879647671606498397663 2.541879647671606498397663 For positive integer values of `s`, the Hurwitz zeta function is equivalent to a polygamma function (except for a normalizing factor):: >>> zeta(4, (1,5)); psi(3, '1/5')/6 625.5408324774542966919938 625.5408324774542966919938 Evaluation of derivatives:: >>> zeta(0, 3+4j, 1); loggamma(3+4j) - ln(2*pi)/2 (-2.675565317808456852310934 + 4.742664438034657928194889j) (-2.675565317808456852310934 + 4.742664438034657928194889j) >>> zeta(2, 1, 20) 2432902008176640000.000242 >>> zeta(3+4j, 5.5+2j, 4) (-0.140075548947797130681075 - 0.3109263360275413251313634j) >>> zeta(0.5+100000j, 1, 4) (-10407.16081931495861539236 + 13777.78669862804508537384j) >>> zeta(-100+0.5j, (1,3), derivative=4) (4.007180821099823942702249e+79 + 4.916117957092593868321778e+78j) Generating a Taylor series at `s = 2` using derivatives:: >>> for k in range(11): print("%s * (s-2)^%i" % (zeta(2,1,k)/fac(k), k)) ... 1.644934066848226436472415 * (s-2)^0 -0.9375482543158437537025741 * (s-2)^1 0.9946401171494505117104293 * (s-2)^2 -1.000024300473840810940657 * (s-2)^3 1.000061933072352565457512 * (s-2)^4 -1.000006869443931806408941 * (s-2)^5 1.000000173233769531820592 * (s-2)^6 -0.9999999569989868493432399 * (s-2)^7 0.9999999937218844508684206 * (s-2)^8 -0.9999999996355013916608284 * (s-2)^9 1.000000000004610645020747 * (s-2)^10 Evaluation at zero and for negative integer `s`:: >>> zeta(0, 10) -9.5 >>> zeta(-2, (2,3)); mpf(1)/81 0.01234567901234567901234568 0.01234567901234567901234568 >>> zeta(-3+4j, (5,4)) (0.2899236037682695182085988 + 0.06561206166091757973112783j) >>> zeta(-3.25, 1/pi) -0.0005117269627574430494396877 >>> zeta(-3.5, pi, 1) 11.156360390440003294709 >>> zeta(-100.5, (8,3)) -4.68162300487989766727122e+77 >>> zeta(-10.5, (-8,3)) (-0.01521913704446246609237979 + 29907.72510874248161608216j) >>> zeta(-1000.5, (-8,3)) (1.031911949062334538202567e+1770 + 1.519555750556794218804724e+426j) >>> zeta(-1+j, 3+4j) (-16.32988355630802510888631 - 22.17706465801374033261383j) >>> zeta(-1+j, 3+4j, 2) (32.48985276392056641594055 - 51.11604466157397267043655j) >>> diff(lambda s: zeta(s, 3+4j), -1+j, 2) (32.48985276392056641594055 - 51.11604466157397267043655j) **References** 1. http://mathworld.wolfram.com/RiemannZetaFunction.html 2. http://mathworld.wolfram.com/HurwitzZetaFunction.html 3. http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf """ dirichlet = r""" Evaluates the Dirichlet L-function .. math :: L(s,\chi) = \sum_{k=1}^\infty \frac{\chi(k)}{k^s}. where `\chi` is a periodic sequence of length `q` which should be supplied in the form of a list `[\chi(0), \chi(1), \ldots, \chi(q-1)]`. Strictly, `\chi` should be a Dirichlet character, but any periodic sequence will work. For example, ``dirichlet(s, [1])`` gives the ordinary Riemann zeta function and ``dirichlet(s, [-1,1])`` gives the alternating zeta function (Dirichlet eta function). Also the derivative with respect to `s` (currently only a first derivative) can be evaluated. **Examples** The ordinary Riemann zeta function:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> dirichlet(3, [1]); zeta(3) 1.202056903159594285399738 1.202056903159594285399738 >>> dirichlet(1, [1]) +inf The alternating zeta function:: >>> dirichlet(1, [-1,1]); ln(2) 0.6931471805599453094172321 0.6931471805599453094172321 The following defines the Dirichlet beta function `\beta(s) = \sum_{k=0}^\infty \frac{(-1)^k}{(2k+1)^s}` and verifies several values of this function:: >>> B = lambda s, d=0: dirichlet(s, [0, 1, 0, -1], d) >>> B(0); 1./2 0.5 0.5 >>> B(1); pi/4 0.7853981633974483096156609 0.7853981633974483096156609 >>> B(2); +catalan 0.9159655941772190150546035 0.9159655941772190150546035 >>> B(2,1); diff(B, 2) 0.08158073611659279510291217 0.08158073611659279510291217 >>> B(-1,1); 2*catalan/pi 0.5831218080616375602767689 0.5831218080616375602767689 >>> B(0,1); log(gamma(0.25)**2/(2*pi*sqrt(2))) 0.3915943927068367764719453 0.3915943927068367764719454 >>> B(1,1); 0.25*pi*(euler+2*ln2+3*ln(pi)-4*ln(gamma(0.25))) 0.1929013167969124293631898 0.1929013167969124293631898 A custom L-series of period 3:: >>> dirichlet(2, [2,0,1]) 0.7059715047839078092146831 >>> 2*nsum(lambda k: (3*k)**-2, [1,inf]) + \ ... nsum(lambda k: (3*k+2)**-2, [0,inf]) 0.7059715047839078092146831 """ coulombf = r""" Calculates the regular Coulomb wave function .. math :: F_l(\eta,z) = C_l(\eta) z^{l+1} e^{-iz} \,_1F_1(l+1-i\eta, 2l+2, 2iz) where the normalization constant `C_l(\eta)` is as calculated by :func:`~mpmath.coulombc`. This function solves the differential equation .. math :: f''(z) + \left(1-\frac{2\eta}{z}-\frac{l(l+1)}{z^2}\right) f(z) = 0. A second linearly independent solution is given by the irregular Coulomb wave function `G_l(\eta,z)` (see :func:`~mpmath.coulombg`) and thus the general solution is `f(z) = C_1 F_l(\eta,z) + C_2 G_l(\eta,z)` for arbitrary constants `C_1`, `C_2`. Physically, the Coulomb wave functions give the radial solution to the Schrodinger equation for a point particle in a `1/z` potential; `z` is then the radius and `l`, `\eta` are quantum numbers. The Coulomb wave functions with real parameters are defined in Abramowitz & Stegun, section 14. However, all parameters are permitted to be complex in this implementation (see references). **Plots** .. literalinclude :: /modules/mpmath/plots/coulombf.py .. image :: /modules/mpmath/plots/coulombf.png .. literalinclude :: /modules/mpmath/plots/coulombf_c.py .. image :: /modules/mpmath/plots/coulombf_c.png **Examples** Evaluation is supported for arbitrary magnitudes of `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> coulombf(2, 1.5, 3.5) 0.4080998961088761187426445 >>> coulombf(-2, 1.5, 3.5) 0.7103040849492536747533465 >>> coulombf(2, 1.5, '1e-10') 4.143324917492256448770769e-33 >>> coulombf(2, 1.5, 1000) 0.4482623140325567050716179 >>> coulombf(2, 1.5, 10**10) -0.066804196437694360046619 Verifying the differential equation:: >>> l, eta, z = 2, 3, mpf(2.75) >>> A, B = 1, 2 >>> f = lambda z: A*coulombf(l,eta,z) + B*coulombg(l,eta,z) >>> chop(diff(f,z,2) + (1-2*eta/z - l*(l+1)/z**2)*f(z)) 0.0 A Wronskian relation satisfied by the Coulomb wave functions:: >>> l = 2 >>> eta = 1.5 >>> F = lambda z: coulombf(l,eta,z) >>> G = lambda z: coulombg(l,eta,z) >>> for z in [3.5, -1, 2+3j]: ... chop(diff(F,z)*G(z) - F(z)*diff(G,z)) ... 1.0 1.0 1.0 Another Wronskian relation:: >>> F = coulombf >>> G = coulombg >>> for z in [3.5, -1, 2+3j]: ... chop(F(l-1,eta,z)*G(l,eta,z)-F(l,eta,z)*G(l-1,eta,z) - l/sqrt(l**2+eta**2)) ... 0.0 0.0 0.0 An integral identity connecting the regular and irregular wave functions:: >>> l, eta, z = 4+j, 2-j, 5+2j >>> coulombf(l,eta,z) + j*coulombg(l,eta,z) (0.7997977752284033239714479 + 0.9294486669502295512503127j) >>> g = lambda t: exp(-t)*t**(l-j*eta)*(t+2*j*z)**(l+j*eta) >>> j*exp(-j*z)*z**(-l)/fac(2*l+1)/coulombc(l,eta)*quad(g, [0,inf]) (0.7997977752284033239714479 + 0.9294486669502295512503127j) Some test case with complex parameters, taken from Michel [2]:: >>> mp.dps = 15 >>> coulombf(1+0.1j, 50+50j, 100.156) (-1.02107292320897e+15 - 2.83675545731519e+15j) >>> coulombg(1+0.1j, 50+50j, 100.156) (2.83675545731519e+15 - 1.02107292320897e+15j) >>> coulombf(1e-5j, 10+1e-5j, 0.1+1e-6j) (4.30566371247811e-14 - 9.03347835361657e-19j) >>> coulombg(1e-5j, 10+1e-5j, 0.1+1e-6j) (778709182061.134 + 18418936.2660553j) The following reproduces a table in Abramowitz & Stegun, at twice the precision:: >>> mp.dps = 10 >>> eta = 2; z = 5 >>> for l in [5, 4, 3, 2, 1, 0]: ... print("%s %s %s" % (l, coulombf(l,eta,z), ... diff(lambda z: coulombf(l,eta,z), z))) ... 5 0.09079533488 0.1042553261 4 0.2148205331 0.2029591779 3 0.4313159311 0.320534053 2 0.7212774133 0.3952408216 1 0.9935056752 0.3708676452 0 1.143337392 0.2937960375 **References** 1. I.J. Thompson & A.R. Barnett, "Coulomb and Bessel Functions of Complex Arguments and Order", J. Comp. Phys., vol 64, no. 2, June 1986. 2. N. Michel, "Precise Coulomb wave functions for a wide range of complex `l`, `\eta` and `z`", http://arxiv.org/abs/physics/0702051v1 """ coulombg = r""" Calculates the irregular Coulomb wave function .. math :: G_l(\eta,z) = \frac{F_l(\eta,z) \cos(\chi) - F_{-l-1}(\eta,z)}{\sin(\chi)} where `\chi = \sigma_l - \sigma_{-l-1} - (l+1/2) \pi` and `\sigma_l(\eta) = (\ln \Gamma(1+l+i\eta)-\ln \Gamma(1+l-i\eta))/(2i)`. See :func:`~mpmath.coulombf` for additional information. **Plots** .. literalinclude :: /modules/mpmath/plots/coulombg.py .. image :: /modules/mpmath/plots/coulombg.png .. literalinclude :: /modules/mpmath/plots/coulombg_c.py .. image :: /modules/mpmath/plots/coulombg_c.png **Examples** Evaluation is supported for arbitrary magnitudes of `z`:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> coulombg(-2, 1.5, 3.5) 1.380011900612186346255524 >>> coulombg(2, 1.5, 3.5) 1.919153700722748795245926 >>> coulombg(-2, 1.5, '1e-10') 201126715824.7329115106793 >>> coulombg(-2, 1.5, 1000) 0.1802071520691149410425512 >>> coulombg(-2, 1.5, 10**10) 0.652103020061678070929794 The following reproduces a table in Abramowitz & Stegun, at twice the precision:: >>> mp.dps = 10 >>> eta = 2; z = 5 >>> for l in [1, 2, 3, 4, 5]: ... print("%s %s %s" % (l, coulombg(l,eta,z), ... -diff(lambda z: coulombg(l,eta,z), z))) ... 1 1.08148276 0.6028279961 2 1.496877075 0.5661803178 3 2.048694714 0.7959909551 4 3.09408669 1.731802374 5 5.629840456 4.549343289 Evaluation close to the singularity at `z = 0`:: >>> mp.dps = 15 >>> coulombg(0,10,1) 3088184933.67358 >>> coulombg(0,10,'1e-10') 5554866000719.8 >>> coulombg(0,10,'1e-100') 5554866221524.1 Evaluation with a half-integer value for `l`:: >>> coulombg(1.5, 1, 10) 0.852320038297334 """ coulombc = r""" Gives the normalizing Gamow constant for Coulomb wave functions, .. math :: C_l(\eta) = 2^l \exp\left(-\pi \eta/2 + [\ln \Gamma(1+l+i\eta) + \ln \Gamma(1+l-i\eta)]/2 - \ln \Gamma(2l+2)\right), where the log gamma function with continuous imaginary part away from the negative half axis (see :func:`~mpmath.loggamma`) is implied. This function is used internally for the calculation of Coulomb wave functions, and automatically cached to make multiple evaluations with fixed `l`, `\eta` fast. """ ellipfun = r""" Computes any of the Jacobi elliptic functions, defined in terms of Jacobi theta functions as .. math :: \mathrm{sn}(u,m) = \frac{\vartheta_3(0,q)}{\vartheta_2(0,q)} \frac{\vartheta_1(t,q)}{\vartheta_4(t,q)} \mathrm{cn}(u,m) = \frac{\vartheta_4(0,q)}{\vartheta_2(0,q)} \frac{\vartheta_2(t,q)}{\vartheta_4(t,q)} \mathrm{dn}(u,m) = \frac{\vartheta_4(0,q)}{\vartheta_3(0,q)} \frac{\vartheta_3(t,q)}{\vartheta_4(t,q)}, or more generally computes a ratio of two such functions. Here `t = u/\vartheta_3(0,q)^2`, and `q = q(m)` denotes the nome (see :func:`~mpmath.nome`). Optionally, you can specify the nome directly instead of `m` by passing ``q=``, or you can directly specify the elliptic parameter `k` with ``k=``. The first argument should be a two-character string specifying the function using any combination of ``'s'``, ``'c'``, ``'d'``, ``'n'``. These letters respectively denote the basic functions `\mathrm{sn}(u,m)`, `\mathrm{cn}(u,m)`, `\mathrm{dn}(u,m)`, and `1`. The identifier specifies the ratio of two such functions. For example, ``'ns'`` identifies the function .. math :: \mathrm{ns}(u,m) = \frac{1}{\mathrm{sn}(u,m)} and ``'cd'`` identifies the function .. math :: \mathrm{cd}(u,m) = \frac{\mathrm{cn}(u,m)}{\mathrm{dn}(u,m)}. If called with only the first argument, a function object evaluating the chosen function for given arguments is returned. **Examples** Basic evaluation:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> ellipfun('cd', 3.5, 0.5) -0.9891101840595543931308394 >>> ellipfun('cd', 3.5, q=0.25) 0.07111979240214668158441418 The sn-function is doubly periodic in the complex plane with periods `4 K(m)` and `2 i K(1-m)` (see :func:`~mpmath.ellipk`):: >>> sn = ellipfun('sn') >>> sn(2, 0.25) 0.9628981775982774425751399 >>> sn(2+4*ellipk(0.25), 0.25) 0.9628981775982774425751399 >>> chop(sn(2+2*j*ellipk(1-0.25), 0.25)) 0.9628981775982774425751399 The cn-function is doubly periodic with periods `4 K(m)` and `4 i K(1-m)`:: >>> cn = ellipfun('cn') >>> cn(2, 0.25) -0.2698649654510865792581416 >>> cn(2+4*ellipk(0.25), 0.25) -0.2698649654510865792581416 >>> chop(cn(2+4*j*ellipk(1-0.25), 0.25)) -0.2698649654510865792581416 The dn-function is doubly periodic with periods `2 K(m)` and `4 i K(1-m)`:: >>> dn = ellipfun('dn') >>> dn(2, 0.25) 0.8764740583123262286931578 >>> dn(2+2*ellipk(0.25), 0.25) 0.8764740583123262286931578 >>> chop(dn(2+4*j*ellipk(1-0.25), 0.25)) 0.8764740583123262286931578 """ jtheta = r""" Computes the Jacobi theta function `\vartheta_n(z, q)`, where `n = 1, 2, 3, 4`, defined by the infinite series: .. math :: \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} (-1)^n q^{n^2+n\,} \sin((2n+1)z) \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} q^{n^{2\,} + n} \cos((2n+1)z) \vartheta_3(z,q) = 1 + 2 \sum_{n=1}^{\infty} q^{n^2\,} \cos(2 n z) \vartheta_4(z,q) = 1 + 2 \sum_{n=1}^{\infty} (-q)^{n^2\,} \cos(2 n z) The theta functions are functions of two variables: * `z` is the *argument*, an arbitrary real or complex number * `q` is the *nome*, which must be a real or complex number in the unit disk (i.e. `|q| < 1`). For `|q| \ll 1`, the series converge very quickly, so the Jacobi theta functions can efficiently be evaluated to high precision. The compact notations `\vartheta_n(q) = \vartheta_n(0,q)` and `\vartheta_n = \vartheta_n(0,q)` are also frequently encountered. Finally, Jacobi theta functions are frequently considered as functions of the half-period ratio `\tau` and then usually denoted by `\vartheta_n(z|\tau)`. Optionally, ``jtheta(n, z, q, derivative=d)`` with `d > 0` computes a `d`-th derivative with respect to `z`. **Examples and basic properties** Considered as functions of `z`, the Jacobi theta functions may be viewed as generalizations of the ordinary trigonometric functions cos and sin. They are periodic functions:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> jtheta(1, 0.25, '0.2') 0.2945120798627300045053104 >>> jtheta(1, 0.25 + 2*pi, '0.2') 0.2945120798627300045053104 Indeed, the series defining the theta functions are essentially trigonometric Fourier series. The coefficients can be retrieved using :func:`~mpmath.fourier`:: >>> mp.dps = 10 >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4)) ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]) The Jacobi theta functions are also so-called quasiperiodic functions of `z` and `\tau`, meaning that for fixed `\tau`, `\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same except for an exponential factor:: >>> mp.dps = 25 >>> tau = 3*j/10 >>> q = exp(pi*j*tau) >>> z = 10 >>> jtheta(4, z+tau*pi, q) (-0.682420280786034687520568 + 1.526683999721399103332021j) >>> -exp(-2*j*z)/q * jtheta(4, z, q) (-0.682420280786034687520568 + 1.526683999721399103332021j) The Jacobi theta functions satisfy a huge number of other functional equations, such as the following identity (valid for any `q`):: >>> q = mpf(3)/10 >>> jtheta(3,0,q)**4 6.823744089352763305137427 >>> jtheta(2,0,q)**4 + jtheta(4,0,q)**4 6.823744089352763305137427 Extensive listings of identities satisfied by the Jacobi theta functions can be found in standard reference works. The Jacobi theta functions are related to the gamma function for special arguments:: >>> jtheta(3, 0, exp(-pi)) 1.086434811213308014575316 >>> pi**(1/4.) / gamma(3/4.) 1.086434811213308014575316 :func:`~mpmath.jtheta` supports arbitrary precision evaluation and complex arguments:: >>> mp.dps = 50 >>> jtheta(4, sqrt(2), 0.5) 2.0549510717571539127004115835148878097035750653737 >>> mp.dps = 25 >>> jtheta(4, 1+2j, (1+j)/5) (7.180331760146805926356634 - 1.634292858119162417301683j) Evaluation of derivatives:: >>> mp.dps = 25 >>> jtheta(1, 7, 0.25, 1); diff(lambda z: jtheta(1, z, 0.25), 7) 1.209857192844475388637236 1.209857192844475388637236 >>> jtheta(1, 7, 0.25, 2); diff(lambda z: jtheta(1, z, 0.25), 7, 2) -0.2598718791650217206533052 -0.2598718791650217206533052 >>> jtheta(2, 7, 0.25, 1); diff(lambda z: jtheta(2, z, 0.25), 7) -1.150231437070259644461474 -1.150231437070259644461474 >>> jtheta(2, 7, 0.25, 2); diff(lambda z: jtheta(2, z, 0.25), 7, 2) -0.6226636990043777445898114 -0.6226636990043777445898114 >>> jtheta(3, 7, 0.25, 1); diff(lambda z: jtheta(3, z, 0.25), 7) -0.9990312046096634316587882 -0.9990312046096634316587882 >>> jtheta(3, 7, 0.25, 2); diff(lambda z: jtheta(3, z, 0.25), 7, 2) -0.1530388693066334936151174 -0.1530388693066334936151174 >>> jtheta(4, 7, 0.25, 1); diff(lambda z: jtheta(4, z, 0.25), 7) 0.9820995967262793943571139 0.9820995967262793943571139 >>> jtheta(4, 7, 0.25, 2); diff(lambda z: jtheta(4, z, 0.25), 7, 2) 0.3936902850291437081667755 0.3936902850291437081667755 **Possible issues** For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`~mpmath.jtheta` raises ``ValueError``. This exception is also raised for `|q|` extremely close to 1 (or equivalently `\tau` very close to 0), since the series would converge too slowly:: >>> jtheta(1, 10, 0.99999999 * exp(0.5*j)) Traceback (most recent call last): ... ValueError: abs(q) > THETA_Q_LIM = 1.000000 """ eulernum = r""" Gives the `n`-th Euler number, defined as the `n`-th derivative of `\mathrm{sech}(t) = 1/\cosh(t)` evaluated at `t = 0`. Equivalently, the Euler numbers give the coefficients of the Taylor series .. math :: \mathrm{sech}(t) = \sum_{n=0}^{\infty} \frac{E_n}{n!} t^n. The Euler numbers are closely related to Bernoulli numbers and Bernoulli polynomials. They can also be evaluated in terms of Euler polynomials (see :func:`~mpmath.eulerpoly`) as `E_n = 2^n E_n(1/2)`. **Examples** Computing the first few Euler numbers and verifying that they agree with the Taylor series:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> [eulernum(n) for n in range(11)] [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] >>> chop(diffs(sech, 0, 10)) [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] Euler numbers grow very rapidly. :func:`~mpmath.eulernum` efficiently computes numerical approximations for large indices:: >>> eulernum(50) -6.053285248188621896314384e+54 >>> eulernum(1000) 3.887561841253070615257336e+2371 >>> eulernum(10**20) 4.346791453661149089338186e+1936958564106659551331 Comparing with an asymptotic formula for the Euler numbers:: >>> n = 10**5 >>> (-1)**(n//2) * 8 * sqrt(n/(2*pi)) * (2*n/(pi*e))**n 3.69919063017432362805663e+436961 >>> eulernum(n) 3.699193712834466537941283e+436961 Pass ``exact=True`` to obtain exact values of Euler numbers as integers:: >>> print(eulernum(50, exact=True)) -6053285248188621896314383785111649088103498225146815121 >>> print(eulernum(200, exact=True) % 10**10) 1925859625 >>> eulernum(1001, exact=True) 0 """ eulerpoly = r""" Evaluates the Euler polynomial `E_n(z)`, defined by the generating function representation .. math :: \frac{2e^{zt}}{e^t+1} = \sum_{n=0}^\infty E_n(z) \frac{t^n}{n!}. The Euler polynomials may also be represented in terms of Bernoulli polynomials (see :func:`~mpmath.bernpoly`) using various formulas, for example .. math :: E_n(z) = \frac{2}{n+1} \left( B_n(z)-2^{n+1}B_n\left(\frac{z}{2}\right) \right). Special values include the Euler numbers `E_n = 2^n E_n(1/2)` (see :func:`~mpmath.eulernum`). **Examples** Computing the coefficients of the first few Euler polynomials:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> for n in range(6): ... chop(taylor(lambda z: eulerpoly(n,z), 0, n)) ... [1.0] [-0.5, 1.0] [0.0, -1.0, 1.0] [0.25, 0.0, -1.5, 1.0] [0.0, 1.0, 0.0, -2.0, 1.0] [-0.5, 0.0, 2.5, 0.0, -2.5, 1.0] Evaluation for arbitrary `z`:: >>> eulerpoly(2,3) 6.0 >>> eulerpoly(5,4) 423.5 >>> eulerpoly(35, 11111111112) 3.994957561486776072734601e+351 >>> eulerpoly(4, 10+20j) (-47990.0 - 235980.0j) >>> eulerpoly(2, '-3.5e-5') 0.000035001225 >>> eulerpoly(3, 0.5) 0.0 >>> eulerpoly(55, -10**80) -1.0e+4400 >>> eulerpoly(5, -inf) -inf >>> eulerpoly(6, -inf) +inf Computing Euler numbers:: >>> 2**26 * eulerpoly(26,0.5) -4087072509293123892361.0 >>> eulernum(26) -4087072509293123892361.0 Evaluation is accurate for large `n` and small `z`:: >>> eulerpoly(100, 0.5) 2.29047999988194114177943e+108 >>> eulerpoly(1000, 10.5) 3.628120031122876847764566e+2070 >>> eulerpoly(10000, 10.5) 1.149364285543783412210773e+30688 """ spherharm = r""" Evaluates the spherical harmonic `Y_l^m(\theta,\phi)`, .. math :: Y_l^m(\theta,\phi) = \sqrt{\frac{2l+1}{4\pi}\frac{(l-m)!}{(l+m)!}} P_l^m(\cos \theta) e^{i m \phi} where `P_l^m` is an associated Legendre function (see :func:`~mpmath.legenp`). Here `\theta \in [0, \pi]` denotes the polar coordinate (ranging from the north pole to the south pole) and `\phi \in [0, 2 \pi]` denotes the azimuthal coordinate on a sphere. Care should be used since many different conventions for spherical coordinate variables are used. Usually spherical harmonics are considered for `l \in \mathbb{N}`, `m \in \mathbb{Z}`, `|m| \le l`. More generally, `l,m,\theta,\phi` are permitted to be complex numbers. .. note :: :func:`~mpmath.spherharm` returns a complex number, even the value is purely real. **Plots** .. literalinclude :: /modules/mpmath/plots/spherharm40.py `Y_{4,0}`: .. image :: /modules/mpmath/plots/spherharm40.png `Y_{4,1}`: .. image :: /modules/mpmath/plots/spherharm41.png `Y_{4,2}`: .. image :: /modules/mpmath/plots/spherharm42.png `Y_{4,3}`: .. image :: /modules/mpmath/plots/spherharm43.png `Y_{4,4}`: .. image :: /modules/mpmath/plots/spherharm44.png **Examples** Some low-order spherical harmonics with reference values:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> theta = pi/4 >>> phi = pi/3 >>> spherharm(0,0,theta,phi); 0.5*sqrt(1/pi)*expj(0) (0.2820947917738781434740397 + 0.0j) (0.2820947917738781434740397 + 0.0j) >>> spherharm(1,-1,theta,phi); 0.5*sqrt(3/(2*pi))*expj(-phi)*sin(theta) (0.1221506279757299803965962 - 0.2115710938304086076055298j) (0.1221506279757299803965962 - 0.2115710938304086076055298j) >>> spherharm(1,0,theta,phi); 0.5*sqrt(3/pi)*cos(theta)*expj(0) (0.3454941494713354792652446 + 0.0j) (0.3454941494713354792652446 + 0.0j) >>> spherharm(1,1,theta,phi); -0.5*sqrt(3/(2*pi))*expj(phi)*sin(theta) (-0.1221506279757299803965962 - 0.2115710938304086076055298j) (-0.1221506279757299803965962 - 0.2115710938304086076055298j) With the normalization convention used, the spherical harmonics are orthonormal on the unit sphere:: >>> sphere = [0,pi], [0,2*pi] >>> dS = lambda t,p: fp.sin(t) # differential element >>> Y1 = lambda t,p: fp.spherharm(l1,m1,t,p) >>> Y2 = lambda t,p: fp.conj(fp.spherharm(l2,m2,t,p)) >>> l1 = l2 = 3; m1 = m2 = 2 >>> print(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere)) (1+0j) >>> m2 = 1 # m1 != m2 >>> print(fp.chop(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere))) 0.0 Evaluation is accurate for large orders:: >>> spherharm(1000,750,0.5,0.25) (3.776445785304252879026585e-102 - 5.82441278771834794493484e-102j) Evaluation works with complex parameter values:: >>> spherharm(1+j, 2j, 2+3j, -0.5j) (64.44922331113759992154992 + 1981.693919841408089681743j) """ scorergi = r""" Evaluates the Scorer function .. math :: \operatorname{Gi}(z) = \operatorname{Ai}(z) \int_0^z \operatorname{Bi}(t) dt + \operatorname{Bi}(z) \int_z^{\infty} \operatorname{Ai}(t) dt which gives a particular solution to the inhomogeneous Airy differential equation `f''(z) - z f(z) = 1/\pi`. Another particular solution is given by the Scorer Hi-function (:func:`~mpmath.scorerhi`). The two functions are related as `\operatorname{Gi}(z) + \operatorname{Hi}(z) = \operatorname{Bi}(z)`. **Plots** .. literalinclude :: /modules/mpmath/plots/gi.py .. image :: /modules/mpmath/plots/gi.png .. literalinclude :: /modules/mpmath/plots/gi_c.py .. image :: /modules/mpmath/plots/gi_c.png **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> scorergi(0); 1/(power(3,'7/6')*gamma('2/3')) 0.2049755424820002450503075 0.2049755424820002450503075 >>> diff(scorergi, 0); 1/(power(3,'5/6')*gamma('1/3')) 0.1494294524512754526382746 0.1494294524512754526382746 >>> scorergi(+inf); scorergi(-inf) 0.0 0.0 >>> scorergi(1) 0.2352184398104379375986902 >>> scorergi(-1) -0.1166722172960152826494198 Evaluation for large arguments:: >>> scorergi(10) 0.03189600510067958798062034 >>> scorergi(100) 0.003183105228162961476590531 >>> scorergi(1000000) 0.0000003183098861837906721743873 >>> 1/(pi*1000000) 0.0000003183098861837906715377675 >>> scorergi(-1000) -0.08358288400262780392338014 >>> scorergi(-100000) 0.02886866118619660226809581 >>> scorergi(50+10j) (0.0061214102799778578790984 - 0.001224335676457532180747917j) >>> scorergi(-50-10j) (5.236047850352252236372551e+29 - 3.08254224233701381482228e+29j) >>> scorergi(100000j) (-8.806659285336231052679025e+6474077 + 8.684731303500835514850962e+6474077j) Verifying the connection between Gi and Hi:: >>> z = 0.25 >>> scorergi(z) + scorerhi(z) 0.7287469039362150078694543 >>> airybi(z) 0.7287469039362150078694543 Verifying the differential equation:: >>> for z in [-3.4, 0, 2.5, 1+2j]: ... chop(diff(scorergi,z,2) - z*scorergi(z)) ... -0.3183098861837906715377675 -0.3183098861837906715377675 -0.3183098861837906715377675 -0.3183098861837906715377675 Verifying the integral representation:: >>> z = 0.5 >>> scorergi(z) 0.2447210432765581976910539 >>> Ai,Bi = airyai,airybi >>> Bi(z)*(Ai(inf,-1)-Ai(z,-1)) + Ai(z)*(Bi(z,-1)-Bi(0,-1)) 0.2447210432765581976910539 **References** 1. [DLMF]_ section 9.12: Scorer Functions """ scorerhi = r""" Evaluates the second Scorer function .. math :: \operatorname{Hi}(z) = \operatorname{Bi}(z) \int_{-\infty}^z \operatorname{Ai}(t) dt - \operatorname{Ai}(z) \int_{-\infty}^z \operatorname{Bi}(t) dt which gives a particular solution to the inhomogeneous Airy differential equation `f''(z) - z f(z) = 1/\pi`. See also :func:`~mpmath.scorergi`. **Plots** .. literalinclude :: /modules/mpmath/plots/hi.py .. image :: /modules/mpmath/plots/hi.png .. literalinclude :: /modules/mpmath/plots/hi_c.py .. image :: /modules/mpmath/plots/hi_c.png **Examples** Some values and limits:: >>> from mpmath import * >>> mp.dps = 25; mp.pretty = True >>> scorerhi(0); 2/(power(3,'7/6')*gamma('2/3')) 0.4099510849640004901006149 0.4099510849640004901006149 >>> diff(scorerhi,0); 2/(power(3,'5/6')*gamma('1/3')) 0.2988589049025509052765491 0.2988589049025509052765491 >>> scorerhi(+inf); scorerhi(-inf) +inf 0.0 >>> scorerhi(1) 0.9722051551424333218376886 >>> scorerhi(-1) 0.2206696067929598945381098 Evaluation for large arguments:: >>> scorerhi(10) 455641153.5163291358991077 >>> scorerhi(100) 6.041223996670201399005265e+288 >>> scorerhi(1000000) 7.138269638197858094311122e+289529652 >>> scorerhi(-10) 0.0317685352825022727415011 >>> scorerhi(-100) 0.003183092495767499864680483 >>> scorerhi(100j) (-6.366197716545672122983857e-9 + 0.003183098861710582761688475j) >>> scorerhi(50+50j) (-5.322076267321435669290334e+63 + 1.478450291165243789749427e+65j) >>> scorerhi(-1000-1000j) (0.0001591549432510502796565538 - 0.000159154943091895334973109j) Verifying the differential equation:: >>> for z in [-3.4, 0, 2, 1+2j]: ... chop(diff(scorerhi,z,2) - z*scorerhi(z)) ... 0.3183098861837906715377675 0.3183098861837906715377675 0.3183098861837906715377675 0.3183098861837906715377675 Verifying the integral representation:: >>> z = 0.5 >>> scorerhi(z) 0.6095559998265972956089949 >>> Ai,Bi = airyai,airybi >>> Bi(z)*(Ai(z,-1)-Ai(-inf,-1)) - Ai(z)*(Bi(z,-1)-Bi(-inf,-1)) 0.6095559998265972956089949 """ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/0000755000175000017500000000000012014170666023527 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_basic_ops.py0000644000175000017500000003553312014170666027113 0ustar georgeskgeorgeskimport sympy.mpmath from sympy.mpmath import * from sympy.mpmath.libmp import * import random import sys try: long = long except NameError: long = int def test_type_compare(): assert mpf(2) == mpc(2,0) assert mpf(0) == mpc(0) assert mpf(2) != mpc(2, 0.00001) assert mpf(2) == 2.0 assert mpf(2) != 3.0 assert mpf(2) == 2 assert mpf(2) != '2.0' assert mpc(2) != '2.0' def test_add(): assert mpf(2.5) + mpf(3) == 5.5 assert mpf(2.5) + 3 == 5.5 assert mpf(2.5) + 3.0 == 5.5 assert 3 + mpf(2.5) == 5.5 assert 3.0 + mpf(2.5) == 5.5 assert (3+0j) + mpf(2.5) == 5.5 assert mpc(2.5) + mpf(3) == 5.5 assert mpc(2.5) + 3 == 5.5 assert mpc(2.5) + 3.0 == 5.5 assert mpc(2.5) + (3+0j) == 5.5 assert 3 + mpc(2.5) == 5.5 assert 3.0 + mpc(2.5) == 5.5 assert (3+0j) + mpc(2.5) == 5.5 def test_sub(): assert mpf(2.5) - mpf(3) == -0.5 assert mpf(2.5) - 3 == -0.5 assert mpf(2.5) - 3.0 == -0.5 assert 3 - mpf(2.5) == 0.5 assert 3.0 - mpf(2.5) == 0.5 assert (3+0j) - mpf(2.5) == 0.5 assert mpc(2.5) - mpf(3) == -0.5 assert mpc(2.5) - 3 == -0.5 assert mpc(2.5) - 3.0 == -0.5 assert mpc(2.5) - (3+0j) == -0.5 assert 3 - mpc(2.5) == 0.5 assert 3.0 - mpc(2.5) == 0.5 assert (3+0j) - mpc(2.5) == 0.5 def test_mul(): assert mpf(2.5) * mpf(3) == 7.5 assert mpf(2.5) * 3 == 7.5 assert mpf(2.5) * 3.0 == 7.5 assert 3 * mpf(2.5) == 7.5 assert 3.0 * mpf(2.5) == 7.5 assert (3+0j) * mpf(2.5) == 7.5 assert mpc(2.5) * mpf(3) == 7.5 assert mpc(2.5) * 3 == 7.5 assert mpc(2.5) * 3.0 == 7.5 assert mpc(2.5) * (3+0j) == 7.5 assert 3 * mpc(2.5) == 7.5 assert 3.0 * mpc(2.5) == 7.5 assert (3+0j) * mpc(2.5) == 7.5 def test_div(): assert mpf(6) / mpf(3) == 2.0 assert mpf(6) / 3 == 2.0 assert mpf(6) / 3.0 == 2.0 assert 6 / mpf(3) == 2.0 assert 6.0 / mpf(3) == 2.0 assert (6+0j) / mpf(3.0) == 2.0 assert mpc(6) / mpf(3) == 2.0 assert mpc(6) / 3 == 2.0 assert mpc(6) / 3.0 == 2.0 assert mpc(6) / (3+0j) == 2.0 assert 6 / mpc(3) == 2.0 assert 6.0 / mpc(3) == 2.0 assert (6+0j) / mpc(3) == 2.0 def test_pow(): assert mpf(6) ** mpf(3) == 216.0 assert mpf(6) ** 3 == 216.0 assert mpf(6) ** 3.0 == 216.0 assert 6 ** mpf(3) == 216.0 assert 6.0 ** mpf(3) == 216.0 assert (6+0j) ** mpf(3.0) == 216.0 assert mpc(6) ** mpf(3) == 216.0 assert mpc(6) ** 3 == 216.0 assert mpc(6) ** 3.0 == 216.0 assert mpc(6) ** (3+0j) == 216.0 assert 6 ** mpc(3) == 216.0 assert 6.0 ** mpc(3) == 216.0 assert (6+0j) ** mpc(3) == 216.0 def test_mixed_misc(): assert 1 + mpf(3) == mpf(3) + 1 == 4 assert 1 - mpf(3) == -(mpf(3) - 1) == -2 assert 3 * mpf(2) == mpf(2) * 3 == 6 assert 6 / mpf(2) == mpf(6) / 2 == 3 assert 1.0 + mpf(3) == mpf(3) + 1.0 == 4 assert 1.0 - mpf(3) == -(mpf(3) - 1.0) == -2 assert 3.0 * mpf(2) == mpf(2) * 3.0 == 6 assert 6.0 / mpf(2) == mpf(6) / 2.0 == 3 def test_add_misc(): mp.dps = 15 assert mpf(4) + mpf(-70) == -66 assert mpf(1) + mpf(1.1)/80 == 1 + 1.1/80 assert mpf((1, 10000000000)) + mpf(3) == mpf((1, 10000000000)) assert mpf(3) + mpf((1, 10000000000)) == mpf((1, 10000000000)) assert mpf((1, -10000000000)) + mpf(3) == mpf(3) assert mpf(3) + mpf((1, -10000000000)) == mpf(3) assert mpf(1) + 1e-15 != 1 assert mpf(1) + 1e-20 == 1 assert mpf(1.07e-22) + 0 == mpf(1.07e-22) assert mpf(0) + mpf(1.07e-22) == mpf(1.07e-22) def test_complex_misc(): # many more tests needed assert 1 + mpc(2) == 3 assert not mpc(2).ae(2 + 1e-13) assert mpc(2+1e-15j).ae(2) def test_complex_zeros(): for a in [0,2]: for b in [0,3]: for c in [0,4]: for d in [0,5]: assert mpc(a,b)*mpc(c,d) == complex(a,b)*complex(c,d) def test_hash(): for i in range(-256, 256): assert hash(mpf(i)) == hash(i) assert hash(mpf(0.5)) == hash(0.5) assert hash(mpc(2,3)) == hash(2+3j) # Check that this doesn't fail assert hash(inf) # Check that overflow doesn't assign equal hashes to large numbers assert hash(mpf('1e1000')) != hash('1e10000') assert hash(mpc(100,'1e1000')) != hash(mpc(200,'1e1000')) from sympy.mpmath.rational import mpq assert hash(mp.mpq(1,3)) assert hash(mp.mpq(0,1)) == 0 assert hash(mp.mpq(-1,1)) == hash(-1) assert hash(mp.mpq(1,1)) == hash(1) assert hash(mp.mpq(5,1)) == hash(5) assert hash(mp.mpq(1,2)) == hash(0.5) #FIXME-py3k: AssertionError, reported upstream. if sys.version >= "3.2": assert hash(mpf(1))*2**2000 == hash(2**2000) assert hash(mpf(1))/2**2000 == hash(mpq(1,2**2000)) # Advanced rounding test def test_add_rounding(): mp.dps = 15 a = from_float(1e-50) assert mpf_sub(mpf_add(fone, a, 53, round_up), fone, 53, round_up) == from_float(2.2204460492503131e-16) assert mpf_sub(fone, a, 53, round_up) == fone assert mpf_sub(fone, mpf_sub(fone, a, 53, round_down), 53, round_down) == from_float(1.1102230246251565e-16) assert mpf_add(fone, a, 53, round_down) == fone def test_almost_equal(): assert mpf(1.2).ae(mpf(1.20000001), 1e-7) assert not mpf(1.2).ae(mpf(1.20000001), 1e-9) assert not mpf(-0.7818314824680298).ae(mpf(-0.774695868667929)) def test_arithmetic_functions(): import operator ops = [(operator.add, fadd), (operator.sub, fsub), (operator.mul, fmul), (operator.truediv, fdiv)] a = mpf(0.27) b = mpf(1.13) c = mpc(0.51+2.16j) d = mpc(1.08-0.99j) for x in [a,b,c,d]: for y in [a,b,c,d]: for op, fop in ops: if fop is not fdiv: mp.prec = 200 z0 = op(x,y) mp.prec = 60 z1 = op(x,y) mp.prec = 53 z2 = op(x,y) assert fop(x, y, prec=60) == z1 assert fop(x, y) == z2 if fop is not fdiv: assert fop(x, y, prec=inf) == z0 assert fop(x, y, dps=inf) == z0 assert fop(x, y, exact=True) == z0 assert fneg(fneg(z1, exact=True), prec=inf) == z1 assert fneg(z1) == -(+z1) mp.dps = 15 def test_exact_integer_arithmetic(): # XXX: re-fix this so that all operations are tested with all rounding modes random.seed(0) for prec in [6, 10, 25, 40, 100, 250, 725]: for rounding in ['d', 'u', 'f', 'c', 'n']: mp.dps = prec M = 10**(prec-2) M2 = 10**(prec//2-2) for i in range(10): a = random.randint(-M, M) b = random.randint(-M, M) assert mpf(a, rounding=rounding) == a assert int(mpf(a, rounding=rounding)) == a assert int(mpf(str(a), rounding=rounding)) == a assert mpf(a) + mpf(b) == a + b assert mpf(a) - mpf(b) == a - b assert -mpf(a) == -a a = random.randint(-M2, M2) b = random.randint(-M2, M2) assert mpf(a) * mpf(b) == a*b assert mpf_mul(from_int(a), from_int(b), mp.prec, rounding) == from_int(a*b) mp.dps = 15 def test_odd_int_bug(): assert to_int(from_int(3), round_nearest) == 3 def test_str_1000_digits(): mp.dps = 1001 # last digit may be wrong assert str(mpf(2)**0.5)[-10:-1] == '9518488472'[:9] assert str(pi)[-10:-1] == '2164201989'[:9] mp.dps = 15 def test_str_10000_digits(): mp.dps = 10001 # last digit may be wrong assert str(mpf(2)**0.5)[-10:-1] == '5873258351'[:9] assert str(pi)[-10:-1] == '5256375678'[:9] mp.dps = 15 def test_monitor(): f = lambda x: x**2 a = [] b = [] g = monitor(f, a.append, b.append) assert g(3) == 9 assert g(4) == 16 assert a[0] == ((3,), {}) assert b[0] == 9 def test_nint_distance(): assert nint_distance(mpf(-3)) == (-3, -inf) assert nint_distance(mpc(-3)) == (-3, -inf) assert nint_distance(mpf(-3.1)) == (-3, -3) assert nint_distance(mpf(-3.01)) == (-3, -6) assert nint_distance(mpf(-3.001)) == (-3, -9) assert nint_distance(mpf(-3.0001)) == (-3, -13) assert nint_distance(mpf(-2.9)) == (-3, -3) assert nint_distance(mpf(-2.99)) == (-3, -6) assert nint_distance(mpf(-2.999)) == (-3, -9) assert nint_distance(mpf(-2.9999)) == (-3, -13) assert nint_distance(mpc(-3+0.1j)) == (-3, -3) assert nint_distance(mpc(-3+0.01j)) == (-3, -6) assert nint_distance(mpc(-3.1+0.1j)) == (-3, -3) assert nint_distance(mpc(-3.01+0.01j)) == (-3, -6) assert nint_distance(mpc(-3.001+0.001j)) == (-3, -9) assert nint_distance(mpf(0)) == (0, -inf) assert nint_distance(mpf(0.01)) == (0, -6) assert nint_distance(mpf('1e-100')) == (0, -332) def test_floor_ceil_nint_frac(): mp.dps = 15 for n in range(-10,10): assert floor(n) == n assert floor(n+0.5) == n assert ceil(n) == n assert ceil(n+0.5) == n+1 assert nint(n) == n # nint rounds to even if n % 2 == 1: assert nint(n+0.5) == n+1 else: assert nint(n+0.5) == n assert floor(inf) == inf assert floor(ninf) == ninf assert isnan(floor(nan)) assert ceil(inf) == inf assert ceil(ninf) == ninf assert isnan(ceil(nan)) assert nint(inf) == inf assert nint(ninf) == ninf assert isnan(nint(nan)) assert floor(0.1) == 0 assert floor(0.9) == 0 assert floor(-0.1) == -1 assert floor(-0.9) == -1 assert floor(10000000000.1) == 10000000000 assert floor(10000000000.9) == 10000000000 assert floor(-10000000000.1) == -10000000000-1 assert floor(-10000000000.9) == -10000000000-1 assert floor(1e-100) == 0 assert floor(-1e-100) == -1 assert floor(1e100) == 1e100 assert floor(-1e100) == -1e100 assert ceil(0.1) == 1 assert ceil(0.9) == 1 assert ceil(-0.1) == 0 assert ceil(-0.9) == 0 assert ceil(10000000000.1) == 10000000000+1 assert ceil(10000000000.9) == 10000000000+1 assert ceil(-10000000000.1) == -10000000000 assert ceil(-10000000000.9) == -10000000000 assert ceil(1e-100) == 1 assert ceil(-1e-100) == 0 assert ceil(1e100) == 1e100 assert ceil(-1e100) == -1e100 assert nint(0.1) == 0 assert nint(0.9) == 1 assert nint(-0.1) == 0 assert nint(-0.9) == -1 assert nint(10000000000.1) == 10000000000 assert nint(10000000000.9) == 10000000000+1 assert nint(-10000000000.1) == -10000000000 assert nint(-10000000000.9) == -10000000000-1 assert nint(1e-100) == 0 assert nint(-1e-100) == 0 assert nint(1e100) == 1e100 assert nint(-1e100) == -1e100 assert floor(3.2+4.6j) == 3+4j assert ceil(3.2+4.6j) == 4+5j assert nint(3.2+4.6j) == 3+5j for n in range(-10,10): assert frac(n) == 0 assert frac(0.25) == 0.25 assert frac(1.25) == 0.25 assert frac(2.25) == 0.25 assert frac(-0.25) == 0.75 assert frac(-1.25) == 0.75 assert frac(-2.25) == 0.75 assert frac('1e100000000000000') == 0 u = mpf('1e-100000000000000') assert frac(u) == u assert frac(-u) == 1 # rounding! u = mpf('1e-400') assert frac(-u, prec=0) == fsub(1, u, exact=True) assert frac(3.25+4.75j) == 0.25+0.75j def test_isnan_etc(): from sympy.mpmath.rational import mpq assert isnan(nan) == True assert isnan(3) == False assert isnan(mpf(3)) == False assert isnan(inf) == False assert isnan(mpc(2,nan)) == True assert isnan(mpc(2,nan)) == True assert isnan(mpc(nan,nan)) == True assert isnan(mpc(2,2)) == False assert isnan(mpc(nan,inf)) == True assert isnan(mpc(inf,inf)) == False assert isnan(mpq((3,2))) == False assert isnan(mpq((0,1))) == False assert isinf(inf) == True assert isinf(-inf) == True assert isinf(3) == False assert isinf(nan) == False assert isinf(3+4j) == False assert isinf(mpc(inf)) == True assert isinf(mpc(3,inf)) == True assert isinf(mpc(inf,3)) == True assert isinf(mpc(inf,inf)) == True assert isinf(mpc(nan,inf)) == True assert isinf(mpc(inf,nan)) == True assert isinf(mpc(nan,nan)) == False assert isinf(mpq((3,2))) == False assert isinf(mpq((0,1))) == False assert isnormal(3) == True assert isnormal(3.5) == True assert isnormal(mpf(3.5)) == True assert isnormal(0) == False assert isnormal(mpf(0)) == False assert isnormal(0.0) == False assert isnormal(inf) == False assert isnormal(-inf) == False assert isnormal(nan) == False assert isnormal(float(inf)) == False assert isnormal(mpc(0,0)) == False assert isnormal(mpc(3,0)) == True assert isnormal(mpc(0,3)) == True assert isnormal(mpc(3,3)) == True assert isnormal(mpc(0,nan)) == False assert isnormal(mpc(0,inf)) == False assert isnormal(mpc(3,nan)) == False assert isnormal(mpc(3,inf)) == False assert isnormal(mpc(3,-inf)) == False assert isnormal(mpc(nan,0)) == False assert isnormal(mpc(inf,0)) == False assert isnormal(mpc(nan,3)) == False assert isnormal(mpc(inf,3)) == False assert isnormal(mpc(inf,nan)) == False assert isnormal(mpc(nan,inf)) == False assert isnormal(mpc(nan,nan)) == False assert isnormal(mpc(inf,inf)) == False assert isnormal(mpq((3,2))) == True assert isnormal(mpq((0,1))) == False assert isint(3) == True assert isint(0) == True assert isint(long(3)) == True assert isint(long(0)) == True assert isint(mpf(3)) == True assert isint(mpf(0)) == True assert isint(mpf(-3)) == True assert isint(mpf(3.2)) == False assert isint(3.2) == False assert isint(nan) == False assert isint(inf) == False assert isint(-inf) == False assert isint(mpc(0)) == True assert isint(mpc(3)) == True assert isint(mpc(3.2)) == False assert isint(mpc(3,inf)) == False assert isint(mpc(inf)) == False assert isint(mpc(3,2)) == False assert isint(mpc(0,2)) == False assert isint(mpc(3,2),gaussian=True) == True assert isint(mpc(3,0),gaussian=True) == True assert isint(mpc(0,3),gaussian=True) == True assert isint(3+4j) == False assert isint(3+4j, gaussian=True) == True assert isint(3+0j) == True assert isint(mpq((3,2))) == False assert isint(mpq((3,9))) == False assert isint(mpq((9,3))) == True assert isint(mpq((0,4))) == True assert isint(mpq((1,1))) == True assert isint(mpq((-1,1))) == True assert mp.isnpint(0) == True assert mp.isnpint(1) == False assert mp.isnpint(-1) == True assert mp.isnpint(-1.1) == False assert mp.isnpint(-1.0) == True assert mp.isnpint(mp.mpq(1,2)) == False assert mp.isnpint(mp.mpq(-1,2)) == False assert mp.isnpint(mp.mpq(-3,1)) == True assert mp.isnpint(mp.mpq(0,1)) == True assert mp.isnpint(mp.mpq(1,1)) == False assert mp.isnpint(0+0j) == True assert mp.isnpint(-1+0j) == True assert mp.isnpint(-1.1+0j) == False assert mp.isnpint(-1+0.1j) == False assert mp.isnpint(0+0.1j) == False wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_special.py0000644000175000017500000000544612014170666026571 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_special(): assert inf == inf assert inf != -inf assert -inf == -inf assert inf != nan assert nan != nan assert isnan(nan) assert --inf == inf assert abs(inf) == inf assert abs(-inf) == inf assert abs(nan) != abs(nan) assert isnan(inf - inf) assert isnan(inf + (-inf)) assert isnan(-inf - (-inf)) assert isnan(inf + nan) assert isnan(-inf + nan) assert mpf(2) + inf == inf assert 2 + inf == inf assert mpf(2) - inf == -inf assert 2 - inf == -inf assert inf > 3 assert 3 < inf assert 3 > -inf assert -inf < 3 assert inf > mpf(3) assert mpf(3) < inf assert mpf(3) > -inf assert -inf < mpf(3) assert not (nan < 3) assert not (nan > 3) assert isnan(inf * 0) assert isnan(-inf * 0) assert inf * 3 == inf assert inf * -3 == -inf assert -inf * 3 == -inf assert -inf * -3 == inf assert inf * inf == inf assert -inf * -inf == inf assert isnan(nan / 3) assert inf / -3 == -inf assert inf / 3 == inf assert 3 / inf == 0 assert -3 / inf == 0 assert 0 / inf == 0 assert isnan(inf / inf) assert isnan(inf / -inf) assert isnan(inf / nan) assert mpf('inf') == mpf('+inf') == inf assert mpf('-inf') == -inf assert isnan(mpf('nan')) assert isinf(inf) assert isinf(-inf) assert not isinf(mpf(0)) assert not isinf(nan) def test_special_powers(): assert inf**3 == inf assert isnan(inf**0) assert inf**-3 == 0 assert (-inf)**2 == inf assert (-inf)**3 == -inf assert isnan((-inf)**0) assert (-inf)**-2 == 0 assert (-inf)**-3 == 0 assert isnan(nan**5) assert isnan(nan**0) def test_functions_special(): assert exp(inf) == inf assert exp(-inf) == 0 assert isnan(exp(nan)) assert log(inf) == inf assert isnan(log(nan)) assert isnan(sin(inf)) assert isnan(sin(nan)) assert atan(inf).ae(pi/2) assert atan(-inf).ae(-pi/2) assert isnan(sqrt(nan)) assert sqrt(inf) == inf def test_convert_special(): float_inf = 1e300 * 1e300 float_ninf = -float_inf float_nan = float_inf/float_ninf assert mpf(3) * float_inf == inf assert mpf(3) * float_ninf == -inf assert isnan(mpf(3) * float_nan) assert not (mpf(3) < float_nan) assert not (mpf(3) > float_nan) assert not (mpf(3) <= float_nan) assert not (mpf(3) >= float_nan) assert float(mpf('1e1000')) == float_inf assert float(mpf('-1e1000')) == float_ninf assert float(mpf('1e100000000000000000')) == float_inf assert float(mpf('-1e100000000000000000')) == float_ninf assert float(mpf('1e-100000000000000000')) == 0.0 def test_div_bug(): assert isnan(nan/1) assert isnan(nan/2) assert inf/2 == inf assert (-inf)/2 == -inf wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_hp.py0000644000175000017500000002432012014170666025550 0ustar georgeskgeorgesk""" Check that the output from irrational functions is accurate for high-precision input, from 5 to 200 digits. The reference values were verified with Mathematica. """ import time from sympy.mpmath import * precs = [5, 15, 28, 35, 57, 80, 100, 150, 200] # sqrt(3) + pi/2 a = \ "3.302847134363773912758768033145623809041389953497933538543279275605"\ "841220051904536395163599428307109666700184672047856353516867399774243594"\ "67433521615861420725323528325327484262075464241255915238845599752675" # e + 1/euler**2 b = \ "5.719681166601007617111261398629939965860873957353320734275716220045750"\ "31474116300529519620938123730851145473473708966080207482581266469342214"\ "824842256999042984813905047895479210702109260221361437411947323431" # sqrt(a) sqrt_a = \ "1.817373691447021556327498239690365674922395036495564333152483422755"\ "144321726165582817927383239308173567921345318453306994746434073691275094"\ "484777905906961689902608644112196725896908619756404253109722911487" # sqrt(a+b*i).real sqrt_abi_real = \ "2.225720098415113027729407777066107959851146508557282707197601407276"\ "89160998185797504198062911768240808839104987021515555650875977724230130"\ "3584116233925658621288393930286871862273400475179312570274423840384" # sqrt(a+b*i).imag sqrt_abi_imag = \ "1.2849057639084690902371581529110949983261182430040898147672052833653668"\ "0629534491275114877090834296831373498336559849050755848611854282001250"\ "1924311019152914021365263161630765255610885489295778894976075186" # log(a) log_a = \ "1.194784864491089550288313512105715261520511949410072046160598707069"\ "4336653155025770546309137440687056366757650909754708302115204338077595203"\ "83005773986664564927027147084436553262269459110211221152925732612" # log(a+b*i).real log_abi_real = \ "1.8877985921697018111624077550443297276844736840853590212962006811663"\ "04949387789489704203167470111267581371396245317618589339274243008242708"\ "014251531496104028712866224020066439049377679709216784954509456421" # log(a+b*i).imag log_abi_imag = \ "1.0471204952840802663567714297078763189256357109769672185219334169734948"\ "4265809854092437285294686651806426649541504240470168212723133326542181"\ "8300136462287639956713914482701017346851009323172531601894918640" # exp(a) exp_a = \ "27.18994224087168661137253262213293847994194869430518354305430976149"\ "382792035050358791398632888885200049857986258414049540376323785711941636"\ "100358982497583832083513086941635049329804685212200507288797531143" # exp(a+b*i).real exp_abi_real = \ "22.98606617170543596386921087657586890620262522816912505151109385026"\ "40160179326569526152851983847133513990281518417211964710397233157168852"\ "4963130831190142571659948419307628119985383887599493378056639916701" # exp(a+b*i).imag exp_abi_imag = \ "-14.523557450291489727214750571590272774669907424478129280902375851196283"\ "3377162379031724734050088565710975758824441845278120105728824497308303"\ "6065619788140201636218705414429933685889542661364184694108251449" # a**b pow_a_b = \ "928.7025342285568142947391505837660251004990092821305668257284426997"\ "361966028275685583421197860603126498884545336686124793155581311527995550"\ "580229264427202446131740932666832138634013168125809402143796691154" # (a**(a+b*i)).real pow_a_abi_real = \ "44.09156071394489511956058111704382592976814280267142206420038656267"\ "67707916510652790502399193109819563864568986234654864462095231138500505"\ "8197456514795059492120303477512711977915544927440682508821426093455" # (a**(a+b*i)).imag pow_a_abi_imag = \ "27.069371511573224750478105146737852141664955461266218367212527612279886"\ "9322304536553254659049205414427707675802193810711302947536332040474573"\ "8166261217563960235014674118610092944307893857862518964990092301" # ((a+b*i)**(a+b*i)).real pow_abi_abi_real = \ "-0.15171310677859590091001057734676423076527145052787388589334350524"\ "8084195882019497779202452975350579073716811284169068082670778986235179"\ "0813026562962084477640470612184016755250592698408112493759742219150452"\ # ((a+b*i)**(a+b*i)).imag pow_abi_abi_imag = \ "1.2697592504953448936553147870155987153192995316950583150964099070426"\ "4736837932577176947632535475040521749162383347758827307504526525647759"\ "97547638617201824468382194146854367480471892602963428122896045019902" # sin(a) sin_a = \ "-0.16055653857469062740274792907968048154164433772938156243509084009"\ "38437090841460493108570147191289893388608611542655654723437248152535114"\ "528368009465836614227575701220612124204622383149391870684288862269631" # sin(1000*a) sin_1000a = \ "-0.85897040577443833776358106803777589664322997794126153477060795801"\ "09151695416961724733492511852267067419573754315098042850381158563024337"\ "216458577140500488715469780315833217177634490142748614625281171216863" # sin(a+b*i) sin_abi_real = \ "-24.4696999681556977743346798696005278716053366404081910969773939630"\ "7149215135459794473448465734589287491880563183624997435193637389884206"\ "02151395451271809790360963144464736839412254746645151672423256977064" sin_abi_imag = \ "-150.42505378241784671801405965872972765595073690984080160750785565810981"\ "8314482499135443827055399655645954830931316357243750839088113122816583"\ "7169201254329464271121058839499197583056427233866320456505060735" # cos cos_a = \ "-0.98702664499035378399332439243967038895709261414476495730788864004"\ "05406821549361039745258003422386169330787395654908532996287293003581554"\ "257037193284199198069707141161341820684198547572456183525659969145501" cos_1000a = \ "-0.51202523570982001856195696460663971099692261342827540426136215533"\ "52686662667660613179619804463250686852463876088694806607652218586060613"\ "951310588158830695735537073667299449753951774916401887657320950496820" # tan tan_a = \ "0.162666873675188117341401059858835168007137819495998960250142156848"\ "639654718809412181543343168174807985559916643549174530459883826451064966"\ "7996119428949951351938178809444268785629011625179962457123195557310" tan_abi_real = \ "6.822696615947538488826586186310162599974827139564433912601918442911"\ "1026830824380070400102213741875804368044342309515353631134074491271890"\ "467615882710035471686578162073677173148647065131872116479947620E-6" tan_abi_imag = \ "0.9999795833048243692245661011298447587046967777739649018690797625964167"\ "1446419978852235960862841608081413169601038230073129482874832053357571"\ "62702259309150715669026865777947502665936317953101462202542168429" def test_hp(): for dps in precs: mp.dps = dps + 8 aa = mpf(a) bb = mpf(b) a1000 = 1000*mpf(a) abi = mpc(aa, bb) mp.dps = dps assert (sqrt(3) + pi/2).ae(aa) assert (e + 1/euler**2).ae(bb) assert sqrt(aa).ae(mpf(sqrt_a)) assert sqrt(abi).ae(mpc(sqrt_abi_real, sqrt_abi_imag)) assert log(aa).ae(mpf(log_a)) assert log(abi).ae(mpc(log_abi_real, log_abi_imag)) assert exp(aa).ae(mpf(exp_a)) assert exp(abi).ae(mpc(exp_abi_real, exp_abi_imag)) assert (aa**bb).ae(mpf(pow_a_b)) assert (aa**abi).ae(mpc(pow_a_abi_real, pow_a_abi_imag)) assert (abi**abi).ae(mpc(pow_abi_abi_real, pow_abi_abi_imag)) assert sin(a).ae(mpf(sin_a)) assert sin(a1000).ae(mpf(sin_1000a)) assert sin(abi).ae(mpc(sin_abi_real, sin_abi_imag)) assert cos(a).ae(mpf(cos_a)) assert cos(a1000).ae(mpf(cos_1000a)) assert tan(a).ae(mpf(tan_a)) assert tan(abi).ae(mpc(tan_abi_real, tan_abi_imag)) # check that complex cancellation is avoided so that both # real and imaginary parts have high relative accuracy. # abs_eps should be 0, but has to be set to 1e-205 to pass the # 200-digit case, probably due to slight inaccuracy in the # precomputed input assert (tan(abi).real).ae(mpf(tan_abi_real), abs_eps=1e-205) assert (tan(abi).imag).ae(mpf(tan_abi_imag), abs_eps=1e-205) mp.dps = 460 assert str(log(3))[-20:] == '02166121184001409826' mp.dps = 15 # Since str(a) can differ in the last digit from rounded a, and I want # to compare the last digits of big numbers with the results in Mathematica, # I made this hack to get the last 20 digits of rounded a def last_digits(a): r = repr(a) s = str(a) #dps = mp.dps #mp.dps += 3 m = 10 r = r.replace(s[:-m],'') r = r.replace("mpf('",'').replace("')",'') num0 = 0 for c in r: if c == '0': num0 += 1 else: break b = float(int(r))/10**(len(r) - m) if b >= 10**m - 0.5: raise NotImplementedError n = int(round(b)) sn = str(n) s = s[:-m] + '0'*num0 + sn return s[-20:] # values checked with Mathematica def test_log_hp(): mp.dps = 2000 a = mpf(10)**15000/3 r = log(a) res = last_digits(r) # Mathematica N[Log[10^15000/3], 2000] # ...7443804441768333470331 assert res == '44380444176833347033' # see issue 105 r = log(mpf(3)/2) # Mathematica N[Log[3/2], 2000] # ...69653749808140753263288 res = last_digits(r) assert res == '53749808140753263288' mp.dps = 10000 r = log(2) res = last_digits(r) # Mathematica N[Log[2], 10000] # ...695615913401856601359655561 assert res == '91340185660135965556' r = log(mpf(10)**10/3) res = last_digits(r) # Mathematica N[Log[10^10/3], 10000] # ...587087654020631943060007154 assert res == '54020631943060007154', res r = log(mpf(10)**100/3) res = last_digits(r) # Mathematica N[Log[10^100/3], 10000] # ,,,59246336539088351652334666 assert res == '36539088351652334666', res mp.dps += 10 a = 1 - mpf(1)/10**10 mp.dps -= 10 r = log(a) res = last_digits(r) # ...3310334360482956137216724048322957404 # 372167240483229574038733026370 # Mathematica N[Log[1 - 10^-10]*10^10, 10000] # ...60482956137216724048322957404 assert res == '37216724048322957404', res mp.dps = 10000 mp.dps += 100 a = 1 + mpf(1)/10**100 mp.dps -= 100 r = log(a) res = last_digits(+r) # Mathematica N[Log[1 + 10^-100]*10^10, 10030] # ...3994733877377412241546890854692521568292338268273 10^-91 assert res == '39947338773774122415', res mp.dps = 15 def test_exp_hp(): mp.dps = 4000 r = exp(mpf(1)/10) # IntegerPart[N[Exp[1/10] * 10^4000, 4000]] # ...92167105162069688129 assert int(r * 10**mp.dps) % 10**20 == 92167105162069688129 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_division.py0000644000175000017500000001235012014170666026765 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import * from sympy.mpmath import mpf, mp from random import randint, choice, seed all_modes = [round_floor, round_ceiling, round_down, round_up, round_nearest] fb = from_bstr fi = from_int ff = from_float def test_div_1_3(): a = fi(1) b = fi(3) c = fi(-1) # floor rounds down, ceiling rounds up assert mpf_div(a, b, 7, round_floor) == fb('0.01010101') assert mpf_div(a, b, 7, round_ceiling) == fb('0.01010110') assert mpf_div(a, b, 7, round_down) == fb('0.01010101') assert mpf_div(a, b, 7, round_up) == fb('0.01010110') assert mpf_div(a, b, 7, round_nearest) == fb('0.01010101') # floor rounds up, ceiling rounds down assert mpf_div(c, b, 7, round_floor) == fb('-0.01010110') assert mpf_div(c, b, 7, round_ceiling) == fb('-0.01010101') assert mpf_div(c, b, 7, round_down) == fb('-0.01010101') assert mpf_div(c, b, 7, round_up) == fb('-0.01010110') assert mpf_div(c, b, 7, round_nearest) == fb('-0.01010101') def test_mpf_divi_1_3(): a = 1 b = fi(3) c = -1 assert mpf_rdiv_int(a, b, 7, round_floor) == fb('0.01010101') assert mpf_rdiv_int(a, b, 7, round_ceiling) == fb('0.01010110') assert mpf_rdiv_int(a, b, 7, round_down) == fb('0.01010101') assert mpf_rdiv_int(a, b, 7, round_up) == fb('0.01010110') assert mpf_rdiv_int(a, b, 7, round_nearest) == fb('0.01010101') assert mpf_rdiv_int(c, b, 7, round_floor) == fb('-0.01010110') assert mpf_rdiv_int(c, b, 7, round_ceiling) == fb('-0.01010101') assert mpf_rdiv_int(c, b, 7, round_down) == fb('-0.01010101') assert mpf_rdiv_int(c, b, 7, round_up) == fb('-0.01010110') assert mpf_rdiv_int(c, b, 7, round_nearest) == fb('-0.01010101') def test_div_300(): q = fi(1000000) a = fi(300499999) # a/q is a little less than a half-integer b = fi(300500000) # b/q exactly a half-integer c = fi(300500001) # c/q is a little more than a half-integer # Check nearest integer rounding (prec=9 as 2**8 < 300 < 2**9) assert mpf_div(a, q, 9, round_down) == fi(300) assert mpf_div(b, q, 9, round_down) == fi(300) assert mpf_div(c, q, 9, round_down) == fi(300) assert mpf_div(a, q, 9, round_up) == fi(301) assert mpf_div(b, q, 9, round_up) == fi(301) assert mpf_div(c, q, 9, round_up) == fi(301) # Nearest even integer is down assert mpf_div(a, q, 9, round_nearest) == fi(300) assert mpf_div(b, q, 9, round_nearest) == fi(300) assert mpf_div(c, q, 9, round_nearest) == fi(301) # Nearest even integer is up a = fi(301499999) b = fi(301500000) c = fi(301500001) assert mpf_div(a, q, 9, round_nearest) == fi(301) assert mpf_div(b, q, 9, round_nearest) == fi(302) assert mpf_div(c, q, 9, round_nearest) == fi(302) def test_tight_integer_division(): # Test that integer division at tightest possible precision is exact N = 100 seed(1) for i in range(N): a = choice([1, -1]) * randint(1, 1< 10000.0 assert from_man_exp(0xf9, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1001 -> 10000.0 assert from_man_exp(0xe8, -4, 4, round_nearest)[:3] == (0, 7, 1) # 1110.1000 -> 1110.0 assert from_man_exp(0xe9, -4, 4, round_nearest)[:3] == (0, 15, 0) # 1110.1001 -> 1111.0 assert from_man_exp(-0xf0, -4, 4, round_nearest)[:3] == (1, 15, 0) assert from_man_exp(-0xf7, -4, 4, round_nearest)[:3] == (1, 15, 0) assert from_man_exp(-0xf8, -4, 4, round_nearest)[:3] == (1, 1, 4) assert from_man_exp(-0xf9, -4, 4, round_nearest)[:3] == (1, 1, 4) assert from_man_exp(-0xe8, -4, 4, round_nearest)[:3] == (1, 7, 1) assert from_man_exp(-0xe9, -4, 4, round_nearest)[:3] == (1, 15, 0) def test_rounding_bugs(): # 1 less than power-of-two cases assert from_man_exp(72057594037927935, -56, 53, round_up) == (0, 1, 0, 1) assert from_man_exp(73786976294838205979, -65, 53, round_nearest) == (0, 1, 1, 1) assert from_man_exp(31, 0, 4, round_up) == (0, 1, 5, 1) assert from_man_exp(-31, 0, 4, round_floor) == (1, 1, 5, 1) assert from_man_exp(255, 0, 7, round_up) == (0, 1, 8, 1) assert from_man_exp(-255, 0, 7, round_floor) == (1, 1, 8, 1) def test_rounding_issue160(): a = from_man_exp(9867,-100) b = from_man_exp(9867,-200) c = from_man_exp(-1,0) z = (1, 1023, -10, 10) assert mpf_add(a, c, 10, 'd') == z assert mpf_add(b, c, 10, 'd') == z assert mpf_add(c, a, 10, 'd') == z assert mpf_add(c, b, 10, 'd') == z def test_perturb(): a = fone b = from_float(0.99999999999999989) c = from_float(1.0000000000000002) assert mpf_perturb(a, 0, 53, round_nearest) == a assert mpf_perturb(a, 1, 53, round_nearest) == a assert mpf_perturb(a, 0, 53, round_up) == c assert mpf_perturb(a, 0, 53, round_ceiling) == c assert mpf_perturb(a, 0, 53, round_down) == a assert mpf_perturb(a, 0, 53, round_floor) == a assert mpf_perturb(a, 1, 53, round_up) == a assert mpf_perturb(a, 1, 53, round_ceiling) == a assert mpf_perturb(a, 1, 53, round_down) == b assert mpf_perturb(a, 1, 53, round_floor) == b a = mpf_neg(a) b = mpf_neg(b) c = mpf_neg(c) assert mpf_perturb(a, 0, 53, round_nearest) == a assert mpf_perturb(a, 1, 53, round_nearest) == a assert mpf_perturb(a, 0, 53, round_up) == a assert mpf_perturb(a, 0, 53, round_floor) == a assert mpf_perturb(a, 0, 53, round_down) == b assert mpf_perturb(a, 0, 53, round_ceiling) == b assert mpf_perturb(a, 1, 53, round_up) == c assert mpf_perturb(a, 1, 53, round_floor) == c assert mpf_perturb(a, 1, 53, round_down) == a assert mpf_perturb(a, 1, 53, round_ceiling) == a def test_add_exact(): ff = from_float assert mpf_add(ff(3.0), ff(2.5)) == ff(5.5) assert mpf_add(ff(3.0), ff(-2.5)) == ff(0.5) assert mpf_add(ff(-3.0), ff(2.5)) == ff(-0.5) assert mpf_add(ff(-3.0), ff(-2.5)) == ff(-5.5) assert mpf_sub(mpf_add(fone, ff(1e-100)), fone) == ff(1e-100) assert mpf_sub(mpf_add(ff(1e-100), fone), fone) == ff(1e-100) assert mpf_sub(mpf_add(fone, ff(-1e-100)), fone) == ff(-1e-100) assert mpf_sub(mpf_add(ff(-1e-100), fone), fone) == ff(-1e-100) assert mpf_add(fone, fzero) == fone assert mpf_add(fzero, fone) == fone assert mpf_add(fzero, fzero) == fzero def test_long_exponent_shifts(): mp.dps = 15 # Check for possible bugs due to exponent arithmetic overflow # in a C implementation x = mpf(1) for p in [32, 64]: a = ldexp(1,2**(p-1)) b = ldexp(1,2**p) c = ldexp(1,2**(p+1)) d = ldexp(1,-2**(p-1)) e = ldexp(1,-2**p) f = ldexp(1,-2**(p+1)) assert (x+a) == a assert (x+b) == b assert (x+c) == c assert (x+d) == x assert (x+e) == x assert (x+f) == x assert (a+x) == a assert (b+x) == b assert (c+x) == c assert (d+x) == x assert (e+x) == x assert (f+x) == x assert (x-a) == -a assert (x-b) == -b assert (x-c) == -c assert (x-d) == x assert (x-e) == x assert (x-f) == x assert (a-x) == a assert (b-x) == b assert (c-x) == c assert (d-x) == -x assert (e-x) == -x assert (f-x) == -x wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_pickle.py0000644000175000017500000000062712014170666026414 0ustar georgeskgeorgeskimport os import tempfile import pickle from sympy.mpmath import * def pickler(obj): fn = tempfile.mktemp() f = open(fn, 'wb') pickle.dump(obj, f) f.close() f = open(fn, 'rb') obj2 = pickle.load(f) f.close() os.remove(fn) return obj2 def test_pickle(): obj = mpf('0.5') assert obj == pickler(obj) obj = mpc('0.5','0.2') assert obj == pickler(obj) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/extratest_bessel.py0000644000175000017500000005042612014170666027470 0ustar georgeskgeorgesk# Extra stress testing for Bessel functions # Reference zeros generated with the aid of scipy.special # jn_zero, jnp_zero, yn_zero, ynp_zero from mpmath import * V = 15 M = 15 jn_small_zeros = \ [[2.4048255576957728, 5.5200781102863106, 8.6537279129110122, 11.791534439014282, 14.930917708487786, 18.071063967910923, 21.211636629879259, 24.352471530749303, 27.493479132040255, 30.634606468431975, 33.775820213573569, 36.917098353664044, 40.058425764628239, 43.19979171317673, 46.341188371661814], [3.8317059702075123, 7.0155866698156188, 10.173468135062722, 13.323691936314223, 16.470630050877633, 19.615858510468242, 22.760084380592772, 25.903672087618383, 29.046828534916855, 32.189679910974404, 35.332307550083865, 38.474766234771615, 41.617094212814451, 44.759318997652822, 47.901460887185447], [5.1356223018406826, 8.4172441403998649, 11.619841172149059, 14.795951782351261, 17.959819494987826, 21.116997053021846, 24.270112313573103, 27.420573549984557, 30.569204495516397, 33.7165195092227, 36.86285651128381, 40.008446733478192, 43.153453778371463, 46.297996677236919, 49.442164110416873], [6.3801618959239835, 9.7610231299816697, 13.015200721698434, 16.223466160318768, 19.409415226435012, 22.582729593104442, 25.748166699294978, 28.908350780921758, 32.064852407097709, 35.218670738610115, 38.370472434756944, 41.520719670406776, 44.669743116617253, 47.817785691533302, 50.965029906205183], [7.5883424345038044, 11.064709488501185, 14.37253667161759, 17.615966049804833, 20.826932956962388, 24.01901952477111, 27.199087765981251, 30.371007667117247, 33.537137711819223, 36.699001128744649, 39.857627302180889, 43.01373772335443, 46.167853512924375, 49.320360686390272, 52.471551398458023], [8.771483815959954, 12.338604197466944, 15.700174079711671, 18.980133875179921, 22.217799896561268, 25.430341154222704, 28.626618307291138, 31.811716724047763, 34.988781294559295, 38.159868561967132, 41.326383254047406, 44.489319123219673, 47.649399806697054, 50.80716520300633, 53.963026558378149], [9.9361095242176849, 13.589290170541217, 17.003819667816014, 20.320789213566506, 23.58608443558139, 26.820151983411405, 30.033722386570469, 33.233041762847123, 36.422019668258457, 39.603239416075404, 42.778481613199507, 45.949015998042603, 49.11577372476426, 52.279453903601052, 55.440592068853149], [11.086370019245084, 14.821268727013171, 18.287582832481726, 21.641541019848401, 24.934927887673022, 28.191188459483199, 31.42279419226558, 34.637089352069324, 37.838717382853611, 41.030773691585537, 44.21540850526126, 47.394165755570512, 50.568184679795566, 53.738325371963291, 56.905249991978781], [12.225092264004655, 16.037774190887709, 19.554536430997055, 22.94517313187462, 26.266814641176644, 29.54565967099855, 32.795800037341462, 36.025615063869571, 39.240447995178135, 42.443887743273558, 45.638444182199141, 48.825930381553857, 52.007691456686903, 55.184747939289049, 58.357889025269694], [13.354300477435331, 17.241220382489128, 20.807047789264107, 24.233885257750552, 27.583748963573006, 30.885378967696675, 34.154377923855096, 37.400099977156589, 40.628553718964528, 43.843801420337347, 47.048700737654032, 50.245326955305383, 53.435227157042058, 56.619580266508436, 59.799301630960228], [14.475500686554541, 18.433463666966583, 22.046985364697802, 25.509450554182826, 28.887375063530457, 32.211856199712731, 35.499909205373851, 38.761807017881651, 42.004190236671805, 45.231574103535045, 48.447151387269394, 51.653251668165858, 54.851619075963349, 58.043587928232478, 61.230197977292681], [15.589847884455485, 19.61596690396692, 23.275853726263409, 26.773322545509539, 30.17906117878486, 33.526364075588624, 36.833571341894905, 40.111823270954241, 43.368360947521711, 46.608132676274944, 49.834653510396724, 53.050498959135054, 56.257604715114484, 59.457456908388002, 62.651217388202912], [16.698249933848246, 20.789906360078443, 24.494885043881354, 28.026709949973129, 31.45996003531804, 34.829986990290238, 38.156377504681354, 41.451092307939681, 44.721943543191147, 47.974293531269048, 51.211967004101068, 54.437776928325074, 57.653844811906946, 60.8618046824805, 64.062937824850136], [17.801435153282442, 21.95624406783631, 25.705103053924724, 29.270630441874802, 32.731053310978403, 36.123657666448762, 39.469206825243883, 42.780439265447158, 46.06571091157561, 49.330780096443524, 52.579769064383396, 55.815719876305778, 59.040934037249271, 62.257189393731728, 65.465883797232125], [18.899997953174024, 23.115778347252756, 26.907368976182104, 30.505950163896036, 33.993184984781542, 37.408185128639695, 40.772827853501868, 44.100590565798301, 47.400347780543231, 50.678236946479898, 53.93866620912693, 57.184898598119301, 60.419409852130297, 63.644117508962281, 66.860533012260103]] jnp_small_zeros = \ [[0.0, 3.8317059702075123, 7.0155866698156188, 10.173468135062722, 13.323691936314223, 16.470630050877633, 19.615858510468242, 22.760084380592772, 25.903672087618383, 29.046828534916855, 32.189679910974404, 35.332307550083865, 38.474766234771615, 41.617094212814451, 44.759318997652822], [1.8411837813406593, 5.3314427735250326, 8.5363163663462858, 11.706004902592064, 14.863588633909033, 18.015527862681804, 21.16436985918879, 24.311326857210776, 27.457050571059246, 30.601922972669094, 33.746182898667383, 36.889987409236811, 40.033444053350675, 43.176628965448822, 46.319597561173912], [3.0542369282271403, 6.7061331941584591, 9.9694678230875958, 13.170370856016123, 16.347522318321783, 19.512912782488205, 22.671581772477426, 25.826037141785263, 28.977672772993679, 32.127327020443474, 35.275535050674691, 38.422654817555906, 41.568934936074314, 44.714553532819734, 47.859641607992093], [4.2011889412105285, 8.0152365983759522, 11.345924310743006, 14.585848286167028, 17.78874786606647, 20.9724769365377, 24.144897432909265, 27.310057930204349, 30.470268806290424, 33.626949182796679, 36.781020675464386, 39.933108623659488, 43.083652662375079, 46.232971081836478, 49.381300092370349], [5.3175531260839944, 9.2823962852416123, 12.681908442638891, 15.964107037731551, 19.196028800048905, 22.401032267689004, 25.589759681386733, 28.767836217666503, 31.938539340972783, 35.103916677346764, 38.265316987088158, 41.423666498500732, 44.579623137359257, 47.733667523865744, 50.886159153182682], [6.4156163757002403, 10.519860873772308, 13.9871886301403, 17.312842487884625, 20.575514521386888, 23.803581476593863, 27.01030789777772, 30.20284907898166, 33.385443901010121, 36.560777686880356, 39.730640230067416, 42.896273163494417, 46.058566273567043, 49.218174614666636, 52.375591529563596], [7.501266144684147, 11.734935953042708, 15.268181461097873, 18.637443009666202, 21.931715017802236, 25.183925599499626, 28.409776362510085, 31.617875716105035, 34.81339298429743, 37.999640897715301, 41.178849474321413, 44.352579199070217, 47.521956905768113, 50.687817781723741, 53.85079463676896], [8.5778364897140741, 12.932386237089576, 16.529365884366944, 19.941853366527342, 23.268052926457571, 26.545032061823576, 29.790748583196614, 33.015178641375142, 36.224380548787162, 39.422274578939259, 42.611522172286684, 45.793999658055002, 48.971070951900596, 52.143752969301988, 55.312820330403446], [9.6474216519972168, 14.115518907894618, 17.774012366915256, 21.229062622853124, 24.587197486317681, 27.889269427955092, 31.155326556188325, 34.39662855427218, 37.620078044197086, 40.830178681822041, 44.030010337966153, 47.221758471887113, 50.407020967034367, 53.586995435398319, 56.762598475105272], [10.711433970699945, 15.28673766733295, 19.004593537946053, 22.501398726777283, 25.891277276839136, 29.218563499936081, 32.505247352375523, 35.763792928808799, 39.001902811514218, 42.224638430753279, 45.435483097475542, 48.636922645305525, 51.830783925834728, 55.01844255063594, 58.200955824859509], [11.770876674955582, 16.447852748486498, 20.223031412681701, 23.760715860327448, 27.182021527190532, 30.534504754007074, 33.841965775135715, 37.118000423665604, 40.371068905333891, 43.606764901379516, 46.828959446564562, 50.040428970943456, 53.243223214220535, 56.438892058982552, 59.628631306921512], [12.826491228033465, 17.600266557468326, 21.430854238060294, 25.008518704644261, 28.460857279654847, 31.838424458616998, 35.166714427392629, 38.460388720328256, 41.728625562624312, 44.977526250903469, 48.211333836373288, 51.433105171422278, 54.645106240447105, 57.849056857839799, 61.046288512821078], [13.878843069697276, 18.745090916814406, 22.629300302835503, 26.246047773946584, 29.72897816891134, 33.131449953571661, 36.480548302231658, 39.791940718940855, 43.075486800191012, 46.337772104541405, 49.583396417633095, 52.815686826850452, 56.037118687012179, 59.249577075517968, 62.454525995970462], [14.928374492964716, 19.88322436109951, 23.81938909003628, 27.474339750968247, 30.987394331665278, 34.414545662167183, 37.784378506209499, 41.113512376883377, 44.412454519229281, 47.688252845993366, 50.945849245830813, 54.188831071035124, 57.419876154678179, 60.641030026538746, 63.853885828967512], [15.975438807484321, 21.015404934568315, 25.001971500138194, 28.694271223110755, 32.236969407878118, 35.688544091185301, 39.078998185245057, 42.425854432866141, 45.740236776624833, 49.029635055514276, 52.299319390331728, 55.553127779547459, 58.793933759028134, 62.02393848337554, 65.244860767043859]] yn_small_zeros = \ [[0.89357696627916752, 3.9576784193148579, 7.0860510603017727, 10.222345043496417, 13.361097473872763, 16.500922441528091, 19.64130970088794, 22.782028047291559, 25.922957653180923, 29.064030252728398, 32.205204116493281, 35.346452305214321, 38.487756653081537, 41.629104466213808, 44.770486607221993], [2.197141326031017, 5.4296810407941351, 8.5960058683311689, 11.749154830839881, 14.897442128336725, 18.043402276727856, 21.188068934142213, 24.331942571356912, 27.475294980449224, 30.618286491641115, 33.761017796109326, 36.90355531614295, 40.045944640266876, 43.188218097393211, 46.330399250701687], [3.3842417671495935, 6.7938075132682675, 10.023477979360038, 13.209986710206416, 16.378966558947457, 19.539039990286384, 22.69395593890929, 25.845613720902269, 28.995080395650151, 32.143002257627551, 35.289793869635804, 38.435733485446343, 41.581014867297885, 44.725777117640461, 47.870122696676504], [4.5270246611496439, 8.0975537628604907, 11.396466739595867, 14.623077742393873, 17.81845523294552, 20.997284754187761, 24.166235758581828, 27.328799850405162, 30.486989604098659, 33.642049384702463, 36.794791029185579, 39.945767226378749, 43.095367507846703, 46.2438744334407, 49.391498015725107], [5.6451478942208959, 9.3616206152445429, 12.730144474090465, 15.999627085382479, 19.22442895931681, 22.424810599698521, 25.610267054939328, 28.785893657666548, 31.954686680031668, 35.118529525584828, 38.278668089521758, 41.435960629910073, 44.591018225353424, 47.744288086361052, 50.896105199722123], [6.7471838248710219, 10.597176726782031, 14.033804104911233, 17.347086393228382, 20.602899017175335, 23.826536030287532, 27.030134937138834, 30.220335654231385, 33.401105611047908, 36.574972486670962, 39.743627733020277, 42.908248189569535, 46.069679073215439, 49.228543693445843, 52.385312123112282], [7.8377378223268716, 11.811037107609447, 15.313615118517857, 18.670704965906724, 21.958290897126571, 25.206207715021249, 28.429037095235496, 31.634879502950644, 34.828638524084437, 38.013473399691765, 41.19151880917741, 44.364272633271975, 47.53281875312084, 50.697961822183806, 53.860312300118388], [8.919605734873789, 13.007711435388313, 16.573915129085334, 19.974342312352426, 23.293972585596648, 26.5667563757203, 29.809531451608321, 33.031769327150685, 36.239265816598239, 39.435790312675323, 42.623910919472727, 45.805442883111651, 48.981708325514764, 52.153694518185572, 55.322154420959698], [9.9946283820824834, 14.190361295800141, 17.817887841179873, 21.26093227125945, 24.612576377421522, 27.910524883974868, 31.173701563441602, 34.412862242025045, 37.634648706110989, 40.843415321050884, 44.04214994542435, 47.232978012841169, 50.417456447370186, 53.596753874948731, 56.771765754432457], [11.064090256031013, 15.361301343575925, 19.047949646361388, 22.532765416313869, 25.91620496332662, 29.2394205079349, 32.523270869465881, 35.779715464475261, 39.016196664616095, 42.237627509803703, 45.4474001519274, 48.647941127433196, 51.841036928216499, 55.028034667184916, 58.209970905250097], [12.128927704415439, 16.522284394784426, 20.265984501212254, 23.791669719454272, 27.206568881574774, 30.555020011020762, 33.859683872746356, 37.133649760307504, 40.385117593813002, 43.619533085646856, 46.840676630553575, 50.051265851897857, 53.253310556711732, 56.448332488918971, 59.637507005589829], [13.189846995683845, 17.674674253171487, 21.473493977824902, 25.03913093040942, 28.485081336558058, 31.858644293774859, 35.184165245422787, 38.475796636190897, 41.742455848758449, 44.990096293791186, 48.222870660068338, 51.443777308699826, 54.655042589416311, 57.858358441436511, 61.055036135780528], [14.247395665073945, 18.819555894710682, 22.671697117872794, 26.276375544903892, 29.752925495549038, 33.151412708998983, 36.497763772987645, 39.807134090704376, 43.089121522203808, 46.350163579538652, 49.594769786270069, 52.82620892320143, 56.046916910756961, 59.258751140598783, 62.463155567737854], [15.30200785858925, 19.957808654258601, 23.861599172945054, 27.504429642227545, 31.011103429019229, 34.434283425782942, 37.801385632318459, 41.128514139788358, 44.425913324440663, 47.700482714581842, 50.957073905278458, 54.199216028087261, 57.429547607017405, 60.65008661807661, 63.862406280068586], [16.354034360047551, 21.090156519983806, 25.044040298785627, 28.724161640881914, 32.260472459522644, 35.708083982611664, 39.095820003878235, 42.440684315990936, 45.75353669045622, 49.041718113283529, 52.310408280968073, 55.56338698149062, 58.803488508906895, 62.032886550960831, 65.253280088312461]] ynp_small_zeros = \ [[2.197141326031017, 5.4296810407941351, 8.5960058683311689, 11.749154830839881, 14.897442128336725, 18.043402276727856, 21.188068934142213, 24.331942571356912, 27.475294980449224, 30.618286491641115, 33.761017796109326, 36.90355531614295, 40.045944640266876, 43.188218097393211, 46.330399250701687], [3.6830228565851777, 6.9414999536541757, 10.123404655436613, 13.285758156782854, 16.440058007293282, 19.590241756629495, 22.738034717396327, 25.884314618788867, 29.029575819372535, 32.174118233366201, 35.318134458192094, 38.461753870997549, 41.605066618873108, 44.74813744908079, 47.891014070791065], [5.0025829314460639, 8.3507247014130795, 11.574195465217647, 14.760909306207676, 17.931285939466855, 21.092894504412739, 24.249231678519058, 27.402145837145258, 30.552708880564553, 33.70158627151572, 36.849213419846257, 39.995887376143356, 43.141817835750686, 46.287157097544201, 49.432018469138281], [6.2536332084598136, 9.6987879841487711, 12.972409052292216, 16.19044719506921, 19.38238844973613, 22.559791857764261, 25.728213194724094, 28.890678419054777, 32.048984005266337, 35.204266606440635, 38.357281675961019, 41.508551443818436, 44.658448731963676, 47.807246956681162, 50.95515126455207], [7.4649217367571329, 11.005169149809189, 14.3317235192331, 17.58443601710272, 20.801062338411128, 23.997004122902644, 27.179886689853435, 30.353960608554323, 33.521797098666792, 36.685048382072301, 39.844826969405863, 43.001910515625288, 46.15685955107263, 49.310088614282257, 52.461911043685864], [8.6495562436971983, 12.280868725807848, 15.660799304540377, 18.949739756016503, 22.192841809428241, 25.409072788867674, 28.608039283077593, 31.795195353138159, 34.973890634255288, 38.14630522169358, 41.313923188794905, 44.477791768537617, 47.638672065035628, 50.797131066967842, 53.953600129601663], [9.8147970120105779, 13.532811875789828, 16.965526446046053, 20.291285512443867, 23.56186260680065, 26.799499736027237, 30.015665481543419, 33.216968050039509, 36.407516858984748, 39.590015243560459, 42.766320595957378, 45.937754257017323, 49.105283450953203, 52.269633324547373, 55.431358715604255], [10.965152105242974, 14.765687379508912, 18.250123150217555, 21.612750053384621, 24.911310600813573, 28.171051927637585, 31.40518108895689, 34.621401012564177, 37.824552065973114, 41.017847386464902, 44.203512240871601, 47.3831408366063, 50.557907466622796, 53.728697478957026, 56.896191727313342], [12.103641941939539, 15.982840905145284, 19.517731005559611, 22.916962141504605, 26.243700855690533, 29.525960140695407, 32.778568197561124, 36.010261572392516, 39.226578757802172, 42.43122493258747, 45.626783824134354, 48.815117837929515, 51.997606404328863, 55.175294723956816, 58.348990221754937], [13.232403808592215, 17.186756572616758, 20.770762917490496, 24.206152448722253, 27.561059462697153, 30.866053571250639, 34.137476603379774, 37.385039772270268, 40.614946085165892, 43.831373184731238, 47.037251786726299, 50.234705848765229, 53.425316228549359, 56.610286079882087, 59.790548623216652], [14.35301374369987, 18.379337301642568, 22.011118775283494, 25.482116178696707, 28.865046588695164, 32.192853922166294, 35.483296655830277, 38.747005493021857, 41.990815194320955, 45.219355876831731, 48.435892856078888, 51.642803925173029, 54.84186659475857, 58.034439083840155, 61.221578745109862], [15.466672066554263, 19.562077985759503, 23.240325531101082, 26.746322986645901, 30.157042415639891, 33.507642948240263, 36.817212798512775, 40.097251300178642, 43.355193847719752, 46.596103410173672, 49.823567279972794, 53.040208868780832, 56.247996968470062, 59.448441365714251, 62.642721301357187], [16.574317035530872, 20.73617763753932, 24.459631728238804, 27.999993668839644, 31.438208790267783, 34.811512070805535, 38.140243708611251, 41.436725143893739, 44.708963264433333, 47.962435051891027, 51.201037321915983, 54.427630745992975, 57.644369734615238, 60.852911791989989, 64.054555435720397], [17.676697936439624, 21.9026148697762, 25.670073356263225, 29.244155124266438, 32.709534477396028, 36.105399554497548, 39.453272918267025, 42.766255701958017, 46.052899215578358, 49.319076602061401, 52.568982147952547, 55.805705507386287, 59.031580956740466, 62.248409689597653, 65.457606670836759], [18.774423978290318, 23.06220035979272, 26.872520985976736, 30.479680663499762, 33.971869047372436, 37.390118854896324, 40.757072537673599, 44.086572292170345, 47.387688809191869, 50.66667461073936, 53.928009929563275, 57.175005343085052, 60.410169281219877, 63.635442539153021, 66.85235358587768]] def test_bessel_zeros(): mp.dps = 15 for v in range(V): for m in range(1,M+1): print(v, m, "of", V, M) # Twice to test cache (if used) assert besseljzero(v,m).ae(jn_small_zeros[v][m-1]) assert besseljzero(v,m).ae(jn_small_zeros[v][m-1]) assert besseljzero(v,m,1).ae(jnp_small_zeros[v][m-1]) assert besseljzero(v,m,1).ae(jnp_small_zeros[v][m-1]) assert besselyzero(v,m).ae(yn_small_zeros[v][m-1]) assert besselyzero(v,m).ae(yn_small_zeros[v][m-1]) assert besselyzero(v,m,1).ae(ynp_small_zeros[v][m-1]) assert besselyzero(v,m,1).ae(ynp_small_zeros[v][m-1]) if __name__ == "__main__": test_bessel_zeros() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_visualization.py0000644000175000017500000000221012014170666030034 0ustar georgeskgeorgesk""" Limited tests of the visualization module. Right now it just makes sure that passing custom Axes works. """ # This test either prints something to the terminal or displays a plot, # depending on whether matplotlib is installed or not. Neither is ideal # for a test, so let's just skip this entirely. disabled = True from sympy.mpmath import mp, fp def test_axes(): try: import matplotlib version = matplotlib.__version__.split("-")[0] version = version.split(".")[:2] if [int(_) for _ in version] < [0,99]: raise ImportError import pylab except ImportError: print("\nSkipping test (pylab not available or too old version)\n") return fig = pylab.figure() axes = fig.add_subplot(111) for ctx in [mp, fp]: ctx.plot(lambda x: x**2, [0, 3], axes=axes) assert axes.get_xlabel() == 'x' assert axes.get_ylabel() == 'f(x)' fig = pylab.figure() axes = fig.add_subplot(111) for ctx in [mp, fp]: ctx.cplot(lambda z: z, [-2, 2], [-10, 10], axes=axes) assert axes.get_xlabel() == 'Re(z)' assert axes.get_ylabel() == 'Im(z)' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/runtests.py0000644000175000017500000001157212014170666025776 0ustar georgeskgeorgesk#!/usr/bin/env python """ python runtests.py -py Use py.test to run tests (more useful for debugging) python runtests.py -psyco Enable psyco to make tests run about 50% faster python runtests.py -coverage Generate test coverage report. Statistics are written to /tmp python runtests.py -profile Generate profile stats (this is much slower) python runtests.py -nogmpy Run tests without using GMPY even if it exists python runtests.py -strict Enforce extra tests in normalize() python runtests.py -local Insert '../..' at the beginning of sys.path to use local mpmath Additional arguments are used to filter the tests to run. Only files that have one of the arguments in their name are executed. """ import sys, os, traceback if "-psyco" in sys.argv: sys.argv.remove('-psyco') import psyco psyco.full() profile = False if "-profile" in sys.argv: sys.argv.remove('-profile') profile = True coverage = False if "-coverage" in sys.argv: sys.argv.remove('-coverage') coverage = True if "-nogmpy" in sys.argv: sys.argv.remove('-nogmpy') os.environ['MPMATH_NOGMPY'] = 'Y' if "-strict" in sys.argv: sys.argv.remove('-strict') os.environ['MPMATH_STRICT'] = 'Y' if "-local" in sys.argv: sys.argv.remove('-local') importdir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..')) else: importdir = '' # TODO: add a flag for this testdir = '' def testit(importdir='', testdir=''): """Run all tests in testdir while importing from importdir.""" if importdir: sys.path.insert(1, importdir) if testdir: sys.path.insert(1, testdir) import os.path import mpmath print("mpmath imported from %s" % os.path.dirname(mpmath.__file__)) print("mpmath backend: %s" % mpmath.libmp.backend.BACKEND) print("mpmath mp class: %s" % repr(mpmath.mp)) print("mpmath version: %s" % mpmath.__version__) print("Python version: %s" % sys.version) print("") if "-py" in sys.argv: sys.argv.remove('-py') import py py.test.cmdline.main() else: import glob from timeit import default_timer as clock modules = [] args = sys.argv[1:] # search for tests in directory of this file if not otherwise specified if not testdir: pattern = os.path.dirname(sys.argv[0]) else: pattern = testdir if pattern: pattern += '/' pattern += 'test*.py' # look for tests (respecting specified filter) for f in glob.glob(pattern): name = os.path.splitext(os.path.basename(f))[0] # If run as a script, only run tests given as args, if any are given if args and __name__ == "__main__": ok = False for arg in args: if arg in name: ok = True break if not ok: continue module = __import__(name) priority = module.__dict__.get('priority', 100) if priority == 666: modules = [[priority, name, module]] break modules.append([priority, name, module]) # execute tests modules.sort() tstart = clock() for priority, name, module in modules: print(name) for f in sorted(module.__dict__.keys()): if f.startswith('test_'): if coverage and ('numpy' in f): continue sys.stdout.write(" " + f[5:].ljust(25) + " ") t1 = clock() try: module.__dict__[f]() except: etype, evalue, trb = sys.exc_info() if etype in (KeyboardInterrupt, SystemExit): raise print("") print("TEST FAILED!") print("") traceback.print_exc() t2 = clock() print("ok " + " " + ("%.7f" % (t2-t1)) + " s") tend = clock() print("") print("finished tests in " + ("%.2f" % (tend-tstart)) + " seconds") # clean sys.path if importdir: sys.path.remove(importdir) if testdir: sys.path.remove(testdir) if __name__ == '__main__': if profile: import cProfile cProfile.run("testit('%s', '%s')" % (importdir, testdir), sort=1) elif coverage: import trace tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], trace=0, count=1) tracer.run('testit(importdir, testdir)') r = tracer.results() r.write_results(show_missing=True, summary=True, coverdir="/tmp") else: testit(importdir, testdir) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_fp.py0000644000175000017500000025763112014170666025563 0ustar georgeskgeorgesk""" Easy-to-use test-generating code: cases = ''' exp 2.25 log 2.25 ''' from sympy.mpmath import * mp.dps = 20 for test in cases.splitlines(): if not test: continue words = test.split() fname = words[0] args = words[1:] argstr = ", ".join(args) testline = "%s(%s)" % (fname, argstr) ans = str(eval(testline)) print " assert ae(fp.%s, %s)" % (testline, ans) """ from sympy.mpmath import fp def ae(x, y, tol=1e-12): if x == y: return True return abs(x-y) <= tol*abs(y) def test_conj(): assert fp.conj(4) == 4 assert fp.conj(3+4j) == 3-4j assert fp.fdot([1,2],[3,2+1j], conjugate=True) == 7-2j def test_fp_number_parts(): assert ae(fp.arg(3), 0.0) assert ae(fp.arg(-3), 3.1415926535897932385) assert ae(fp.arg(3j), 1.5707963267948966192) assert ae(fp.arg(-3j), -1.5707963267948966192) assert ae(fp.arg(2+3j), 0.98279372324732906799) assert ae(fp.arg(-1-1j), -2.3561944901923449288) assert ae(fp.re(2.5), 2.5) assert ae(fp.re(2.5+3j), 2.5) assert ae(fp.im(2.5), 0.0) assert ae(fp.im(2.5+3j), 3.0) assert ae(fp.floor(2.5), 2.0) assert ae(fp.floor(2), 2.0) assert ae(fp.floor(2.0+0j), (2.0 + 0.0j)) assert ae(fp.floor(-1.5-0.5j), (-2.0 - 1.0j)) assert ae(fp.ceil(2.5), 3.0) assert ae(fp.ceil(2), 2.0) assert ae(fp.ceil(2.0+0j), (2.0 + 0.0j)) assert ae(fp.ceil(-1.5-0.5j), (-1.0 + 0.0j)) def test_fp_cospi_sinpi(): assert ae(fp.sinpi(0), 0.0) assert ae(fp.sinpi(0.25), 0.7071067811865475244) assert ae(fp.sinpi(0.5), 1.0) assert ae(fp.sinpi(0.75), 0.7071067811865475244) assert ae(fp.sinpi(1), 0.0) assert ae(fp.sinpi(1.25), -0.7071067811865475244) assert ae(fp.sinpi(1.5), -1.0) assert ae(fp.sinpi(1.75), -0.7071067811865475244) assert ae(fp.sinpi(2), 0.0) assert ae(fp.sinpi(2.25), 0.7071067811865475244) assert ae(fp.sinpi(0+3j), (0.0 + 6195.8238636085899556j)) assert ae(fp.sinpi(0.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.sinpi(0.5+3j), (6195.8239443081075259 + 0.0j)) assert ae(fp.sinpi(0.75+3j), (4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.sinpi(1+3j), (0.0 - 6195.8238636085899556j)) assert ae(fp.sinpi(1.25+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.sinpi(1.5+3j), (-6195.8239443081075259 + 0.0j)) assert ae(fp.sinpi(1.75+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.sinpi(2+3j), (0.0 + 6195.8238636085899556j)) assert ae(fp.sinpi(2.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.sinpi(-0.75), -0.7071067811865475244) assert ae(fp.sinpi(-1e-10), -3.1415926535897933529e-10) assert ae(fp.sinpi(1e-10), 3.1415926535897933529e-10) assert ae(fp.sinpi(1e-10+1e-10j), (3.141592653589793353e-10 + 3.1415926535897933528e-10j)) assert ae(fp.sinpi(1e-10-1e-10j), (3.141592653589793353e-10 - 3.1415926535897933528e-10j)) assert ae(fp.sinpi(-1e-10+1e-10j), (-3.141592653589793353e-10 + 3.1415926535897933528e-10j)) assert ae(fp.sinpi(-1e-10-1e-10j), (-3.141592653589793353e-10 - 3.1415926535897933528e-10j)) assert ae(fp.cospi(0), 1.0) assert ae(fp.cospi(0.25), 0.7071067811865475244) assert ae(fp.cospi(0.5), 0.0) assert ae(fp.cospi(0.75), -0.7071067811865475244) assert ae(fp.cospi(1), -1.0) assert ae(fp.cospi(1.25), -0.7071067811865475244) assert ae(fp.cospi(1.5), 0.0) assert ae(fp.cospi(1.75), 0.7071067811865475244) assert ae(fp.cospi(2), 1.0) assert ae(fp.cospi(2.25), 0.7071067811865475244) assert ae(fp.cospi(0+3j), (6195.8239443081075259 + 0.0j)) assert ae(fp.cospi(0.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.cospi(0.5+3j), (0.0 - 6195.8238636085899556j)) assert ae(fp.cospi(0.75+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.cospi(1+3j), (-6195.8239443081075259 + 0.0j)) assert ae(fp.cospi(1.25+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.cospi(1.5+3j), (0.0 + 6195.8238636085899556j)) assert ae(fp.cospi(1.75+3j), (4381.1091260582448033 + 4381.1090689950686908j)) assert ae(fp.cospi(2+3j), (6195.8239443081075259 + 0.0j)) assert ae(fp.cospi(2.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) assert ae(fp.cospi(-0.75), -0.7071067811865475244) assert ae(fp.sinpi(-0.7), -0.80901699437494750611) assert ae(fp.cospi(-0.7), -0.5877852522924730163) assert ae(fp.cospi(-3+2j), (-267.74676148374822225 + 0.0j)) assert ae(fp.sinpi(-3+2j), (0.0 - 267.74489404101651426j)) assert ae(fp.sinpi(-0.7+2j), (-216.6116802292079471 - 157.37650009392034693j)) assert ae(fp.cospi(-0.7+2j), (-157.37759774921754565 + 216.61016943630197336j)) def test_fp_expj(): assert ae(fp.expj(0), (1.0 + 0.0j)) assert ae(fp.expj(1), (0.5403023058681397174 + 0.84147098480789650665j)) assert ae(fp.expj(2), (-0.416146836547142387 + 0.9092974268256816954j)) assert ae(fp.expj(0.75), (0.73168886887382088631 + 0.68163876002333416673j)) assert ae(fp.expj(2+3j), (-0.020718731002242879378 + 0.045271253156092975488j)) assert ae(fp.expjpi(0), (1.0 + 0.0j)) assert ae(fp.expjpi(1), (-1.0 + 0.0j)) assert ae(fp.expjpi(2), (1.0 + 0.0j)) assert ae(fp.expjpi(0.75), (-0.7071067811865475244 + 0.7071067811865475244j)) assert ae(fp.expjpi(2+3j), (0.000080699517570304599239 + 0.0j)) def test_fp_bernoulli(): assert ae(fp.bernoulli(0), 1.0) assert ae(fp.bernoulli(1), -0.5) assert ae(fp.bernoulli(2), 0.16666666666666666667) assert ae(fp.bernoulli(10), 0.075757575757575757576) assert ae(fp.bernoulli(11), 0.0) def test_fp_gamma(): assert ae(fp.gamma(1), 1.0) assert ae(fp.gamma(1.5), 0.88622692545275801365) assert ae(fp.gamma(10), 362880.0) assert ae(fp.gamma(-0.5), -3.5449077018110320546) assert ae(fp.gamma(-7.1), 0.0016478244570263333622) assert ae(fp.gamma(12.3), 83385367.899970000963) assert ae(fp.gamma(2+0j), (1.0 + 0.0j)) assert ae(fp.gamma(-2.5+0j), (-0.94530872048294188123 + 0.0j)) assert ae(fp.gamma(3+4j), (0.0052255384713692141947 - 0.17254707929430018772j)) assert ae(fp.gamma(-3-4j), (0.00001460997305874775607 - 0.000020760733311509070396j)) assert ae(fp.fac(0), 1.0) assert ae(fp.fac(1), 1.0) assert ae(fp.fac(20), 2432902008176640000.0) assert ae(fp.fac(-3.5), -0.94530872048294188123) assert ae(fp.fac(2+3j), (-0.44011340763700171113 - 0.06363724312631702183j)) assert ae(fp.loggamma(1.0), 0.0) assert ae(fp.loggamma(2.0), 0.0) assert ae(fp.loggamma(3.0), 0.69314718055994530942) assert ae(fp.loggamma(7.25), 7.0521854507385394449) assert ae(fp.loggamma(1000.0), 5905.2204232091812118) assert ae(fp.loggamma(1e50), 1.1412925464970229298e+52) assert ae(fp.loggamma(1e25+1e25j), (5.6125802751733671621e+26 + 5.7696599078528568383e+26j)) assert ae(fp.loggamma(3+4j), (-1.7566267846037841105 + 4.7426644380346579282j)) assert ae(fp.loggamma(-0.5), (1.2655121234846453965 - 3.1415926535897932385j)) assert ae(fp.loggamma(-1.25), (1.3664317612369762346 - 6.2831853071795864769j)) assert ae(fp.loggamma(-2.75), (0.0044878975359557733115 - 9.4247779607693797154j)) assert ae(fp.loggamma(-3.5), (-1.3090066849930420464 - 12.566370614359172954j)) assert ae(fp.loggamma(-4.5), (-2.8130840817693161197 - 15.707963267948966192j)) assert ae(fp.loggamma(-2+3j), (-6.776523813485657093 - 4.568791367260286402j)) assert ae(fp.loggamma(-1000.3), (-5912.8440347785205041 - 3144.7342462433830317j)) assert ae(fp.loggamma(-100-100j), (-632.35117666833135562 - 158.37641469650352462j)) assert ae(fp.loggamma(1e-10), 23.025850929882735237) assert ae(fp.loggamma(-1e-10), (23.02585092999817837 - 3.1415926535897932385j)) assert ae(fp.loggamma(1e-10j), (23.025850929940456804 - 1.5707963268526181857j)) assert ae(fp.loggamma(1e-10j-1e-10), (22.679277339718205716 - 2.3561944902500664954j)) def test_fp_psi(): assert ae(fp.psi(0, 3.7), 1.1671535393615114409) assert ae(fp.psi(0, 0.5), -1.9635100260214234794) assert ae(fp.psi(0, 1), -0.57721566490153286061) assert ae(fp.psi(0, -2.5), 1.1031566406452431872) assert ae(fp.psi(0, 12.9), 2.5179671503279156347) assert ae(fp.psi(0, 100), 4.6001618527380874002) assert ae(fp.psi(0, 2500.3), 7.8239660143238547877) assert ae(fp.psi(0, 1e40), 92.103403719761827391) assert ae(fp.psi(0, 1e200), 460.51701859880913677) assert ae(fp.psi(0, 3.7+0j), (1.1671535393615114409 + 0.0j)) assert ae(fp.psi(1, 3), 0.39493406684822643647) assert ae(fp.psi(3, 2+3j), (-0.05383196209159972116 + 0.0076890935247364805218j)) assert ae(fp.psi(4, -0.5+1j), (1.2719531355492328195 - 18.211833410936276774j)) assert ae(fp.harmonic(0), 0.0) assert ae(fp.harmonic(1), 1.0) assert ae(fp.harmonic(2), 1.5) assert ae(fp.harmonic(100), 5.1873775176396202608) assert ae(fp.harmonic(-2.5), 1.2803723055467760478) assert ae(fp.harmonic(2+3j), (1.9390425294578375875 + 0.87336044981834544043j)) assert ae(fp.harmonic(-5-4j), (2.3725754822349437733 - 2.4160904444801621j)) def test_fp_zeta(): assert ae(fp.zeta(1e100), 1.0) assert ae(fp.zeta(3), 1.2020569031595942854) assert ae(fp.zeta(2+0j), (1.6449340668482264365 + 0.0j)) assert ae(fp.zeta(0.93), -13.713619351638164784) assert ae(fp.zeta(1.74), 1.9796863545771774095) assert ae(fp.zeta(0.0), -0.5) assert ae(fp.zeta(-1.0), -0.083333333333333333333) assert ae(fp.zeta(-2.0), 0.0) assert ae(fp.zeta(-3.0), 0.0083333333333333333333) assert ae(fp.zeta(-500.0), 0.0) assert ae(fp.zeta(-7.4), 0.0036537321227995882447) assert ae(fp.zeta(2.1), 1.5602165335033620158) assert ae(fp.zeta(26.9), 1.0000000079854809935) assert ae(fp.zeta(26), 1.0000000149015548284) assert ae(fp.zeta(27), 1.0000000074507117898) assert ae(fp.zeta(28), 1.0000000037253340248) assert ae(fp.zeta(27.1), 1.000000006951755045) assert ae(fp.zeta(32.7), 1.0000000001433243232) assert ae(fp.zeta(100), 1.0) assert ae(fp.altzeta(3.5), 0.92755357777394803511) assert ae(fp.altzeta(1), 0.69314718055994530942) assert ae(fp.altzeta(2), 0.82246703342411321824) assert ae(fp.altzeta(0), 0.5) assert ae(fp.zeta(-2+3j, 1), (0.13297115587929864827 + 0.12305330040458776494j)) assert ae(fp.zeta(-2+3j, 5), (18.384866151867576927 - 11.377015110597711009j)) assert ae(fp.zeta(1.0000000001), 9999999173.1735741337) assert ae(fp.zeta(0.9999999999), -9999999172.0191428039) assert ae(fp.zeta(1+0.000000001j), (0.57721566490153286061 - 999999999.99999993765j)) assert ae(fp.primezeta(2.5+4j), (-0.16922458243438033385 - 0.010847965298387727811j)) assert ae(fp.primezeta(4), 0.076993139764246844943) assert ae(fp.riemannr(3.7), 2.3034079839110855717) assert ae(fp.riemannr(8), 3.9011860449341499474) assert ae(fp.riemannr(3+4j), (2.2369653314259991796 + 1.6339943856990281694j)) def test_fp_hyp2f1(): assert ae(fp.hyp2f1(1, (3,2), 3.25, 5.0), (-0.46600275923108143059 - 0.74393667908854842325j)) assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 5.0), (-5.9208875603806515987 - 2.3813557707889590686j)) assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 2+3j), (0.17174552030925080445 + 0.19589781970539389999j)) def test_fp_erf(): assert fp.erf(2) == fp.erf(2.0) == fp.erf(2.0+0.0j) assert fp.erf(fp.inf) == 1.0 assert fp.erf(fp.ninf) == -1.0 assert ae(fp.erf(0), 0.0) assert ae(fp.erf(-0), -0.0) assert ae(fp.erf(0.3), 0.32862675945912741619) assert ae(fp.erf(-0.3), -0.32862675945912741619) assert ae(fp.erf(0.9), 0.79690821242283213966) assert ae(fp.erf(-0.9), -0.79690821242283213966) assert ae(fp.erf(1.0), 0.84270079294971486934) assert ae(fp.erf(-1.0), -0.84270079294971486934) assert ae(fp.erf(1.1), 0.88020506957408172966) assert ae(fp.erf(-1.1), -0.88020506957408172966) assert ae(fp.erf(8.5), 1.0) assert ae(fp.erf(-8.5), -1.0) assert ae(fp.erf(9.1), 1.0) assert ae(fp.erf(-9.1), -1.0) assert ae(fp.erf(20.0), 1.0) assert ae(fp.erf(-20.0), -1.0) assert ae(fp.erf(10000.0), 1.0) assert ae(fp.erf(-10000.0), -1.0) assert ae(fp.erf(1e+50), 1.0) assert ae(fp.erf(-1e+50), -1.0) assert ae(fp.erf(1j), 1.650425758797542876j) assert ae(fp.erf(-1j), -1.650425758797542876j) assert ae(fp.erf((2+3j)), (-20.829461427614568389 + 8.6873182714701631444j)) assert ae(fp.erf(-(2+3j)), -(-20.829461427614568389 + 8.6873182714701631444j)) assert ae(fp.erf((8+9j)), (-1072004.2525062051158 + 364149.91954310255423j)) assert ae(fp.erf(-(8+9j)), -(-1072004.2525062051158 + 364149.91954310255423j)) assert fp.erfc(fp.inf) == 0.0 assert fp.erfc(fp.ninf) == 2.0 assert fp.erfc(0) == 1 assert fp.erfc(-0.0) == 1 assert fp.erfc(0+0j) == 1 assert ae(fp.erfc(0.3), 0.67137324054087258381) assert ae(fp.erfc(-0.3), 1.3286267594591274162) assert ae(fp.erfc(0.9), 0.20309178757716786034) assert ae(fp.erfc(-0.9), 1.7969082124228321397) assert ae(fp.erfc(1.0), 0.15729920705028513066) assert ae(fp.erfc(-1.0), 1.8427007929497148693) assert ae(fp.erfc(1.1), 0.11979493042591827034) assert ae(fp.erfc(-1.1), 1.8802050695740817297) assert ae(fp.erfc(8.5), 2.7623240713337714461e-33) assert ae(fp.erfc(-8.5), 2.0) assert ae(fp.erfc(9.1), 6.6969004279886077452e-38) assert ae(fp.erfc(-9.1), 2.0) assert ae(fp.erfc(20.0), 5.3958656116079009289e-176) assert ae(fp.erfc(-20.0), 2.0) assert ae(fp.erfc(10000.0), 0.0) assert ae(fp.erfc(-10000.0), 2.0) assert ae(fp.erfc(1e+50), 0.0) assert ae(fp.erfc(-1e+50), 2.0) assert ae(fp.erfc(1j), (1.0 - 1.650425758797542876j)) assert ae(fp.erfc(-1j), (1.0 + 1.650425758797542876j)) assert ae(fp.erfc((2+3j)), (21.829461427614568389 - 8.6873182714701631444j), 1e-13) assert ae(fp.erfc(-(2+3j)), (-19.829461427614568389 + 8.6873182714701631444j), 1e-13) assert ae(fp.erfc((8+9j)), (1072005.2525062051158 - 364149.91954310255423j)) assert ae(fp.erfc(-(8+9j)), (-1072003.2525062051158 + 364149.91954310255423j)) assert ae(fp.erfc(20+0j), (5.3958656116079009289e-176 + 0.0j)) def test_fp_lambertw(): assert ae(fp.lambertw(0.0), 0.0) assert ae(fp.lambertw(1.0), 0.567143290409783873) assert ae(fp.lambertw(7.5), 1.5662309537823875394) assert ae(fp.lambertw(-0.25), -0.35740295618138890307) assert ae(fp.lambertw(-10.0), (1.3699809685212708156 + 2.140194527074713196j)) assert ae(fp.lambertw(0+0j), (0.0 + 0.0j)) assert ae(fp.lambertw(4+0j), (1.2021678731970429392 + 0.0j)) assert ae(fp.lambertw(1000.5), 5.2500227450408980127) assert ae(fp.lambertw(1e100), 224.84310644511850156) assert ae(fp.lambertw(-1000.0), (5.1501630246362515223 + 2.6641981432905204596j)) assert ae(fp.lambertw(1e-10), 9.9999999990000003645e-11) assert ae(fp.lambertw(1e-10j), (1.0000000000000000728e-20 + 1.0000000000000000364e-10j)) assert ae(fp.lambertw(3+4j), (1.2815618061237758782 + 0.53309522202097107131j)) assert ae(fp.lambertw(-3-4j), (1.0750730665692549276 - 1.3251023817343588823j)) assert ae(fp.lambertw(10000+1000j), (7.2361526563371602186 + 0.087567810943839352034j)) assert ae(fp.lambertw(0.0, -1), -fp.inf) assert ae(fp.lambertw(1.0, -1), (-1.5339133197935745079 - 4.3751851530618983855j)) assert ae(fp.lambertw(7.5, -1), (0.44125668415098614999 - 4.8039842008452390179j)) assert ae(fp.lambertw(-0.25, -1), -2.1532923641103496492) assert ae(fp.lambertw(-10.0, -1), (1.3699809685212708156 - 2.140194527074713196j)) assert ae(fp.lambertw(0+0j, -1), -fp.inf) assert ae(fp.lambertw(4+0j, -1), (-0.15730793189620765317 - 4.6787800704666656212j)) assert ae(fp.lambertw(1000.5, -1), (4.9153765415404024736 - 5.4465682700815159569j)) assert ae(fp.lambertw(1e100, -1), (224.84272130101601052 - 6.2553713838167244141j)) assert ae(fp.lambertw(-1000.0, -1), (5.1501630246362515223 - 2.6641981432905204596j)) assert ae(fp.lambertw(1e-10, -1), (-26.303186778379041521 - 3.2650939117038283975j)) assert ae(fp.lambertw(1e-10j, -1), (-26.297238779529035028 - 1.6328071613455765135j)) assert ae(fp.lambertw(3+4j, -1), (0.25856740686699741676 - 3.8521166861614355895j)) assert ae(fp.lambertw(-3-4j, -1), (-0.32028750204310768396 - 6.8801677192091972343j)) assert ae(fp.lambertw(10000+1000j, -1), (7.0255308742285435567 - 5.5177506835734067601j)) assert ae(fp.lambertw(0.0, 2), -fp.inf) assert ae(fp.lambertw(1.0, 2), (-2.4015851048680028842 + 10.776299516115070898j)) assert ae(fp.lambertw(7.5, 2), (-0.38003357962843791529 + 10.960916473368746184j)) assert ae(fp.lambertw(-0.25, 2), (-4.0558735269061511898 + 13.852334658567271386j)) assert ae(fp.lambertw(-10.0, 2), (-0.34479123764318858696 + 14.112740596763592363j)) assert ae(fp.lambertw(0+0j, 2), -fp.inf) assert ae(fp.lambertw(4+0j, 2), (-1.0070343323804262788 + 10.903476551861683082j)) assert ae(fp.lambertw(1000.5, 2), (4.4076185165459395295 + 11.365524591091402177j)) assert ae(fp.lambertw(1e100, 2), (224.84156762724875878 + 12.510785262632255672j)) assert ae(fp.lambertw(-1000.0, 2), (4.1984245610246530756 + 14.420478573754313845j)) assert ae(fp.lambertw(1e-10, 2), (-26.362258095445866488 + 9.7800247407031482519j)) assert ae(fp.lambertw(1e-10j, 2), (-26.384250801683084252 + 11.403535950607739763j)) assert ae(fp.lambertw(3+4j, 2), (-0.86554679943333993562 + 11.849956798331992027j)) assert ae(fp.lambertw(-3-4j, 2), (-0.55792273874679112639 + 8.7173627024159324811j)) assert ae(fp.lambertw(10000+1000j, 2), (6.6223802254585662734 + 11.61348646825020766j)) def test_fp_stress_ei_e1(): # Can be tightened on recent Pythons with more accurate math/cmath ATOL = 1e-13 PTOL = 1e-12 v = fp.e1(1.1641532182693481445e-10) assert ae(v, 22.296641293693077672, tol=ATOL) assert type(v) is float v = fp.e1(0.25) assert ae(v, 1.0442826344437381945, tol=ATOL) assert type(v) is float v = fp.e1(1.0) assert ae(v, 0.21938393439552027368, tol=ATOL) assert type(v) is float v = fp.e1(2.0) assert ae(v, 0.048900510708061119567, tol=ATOL) assert type(v) is float v = fp.e1(5.0) assert ae(v, 0.0011482955912753257973, tol=ATOL) assert type(v) is float v = fp.e1(20.0) assert ae(v, 9.8355252906498816904e-11, tol=ATOL) assert type(v) is float v = fp.e1(30.0) assert ae(v, 3.0215520106888125448e-15, tol=ATOL) assert type(v) is float v = fp.e1(40.0) assert ae(v, 1.0367732614516569722e-19, tol=ATOL) assert type(v) is float v = fp.e1(50.0) assert ae(v, 3.7832640295504590187e-24, tol=ATOL) assert type(v) is float v = fp.e1(80.0) assert ae(v, 2.2285432586884729112e-37, tol=ATOL) assert type(v) is float v = fp.e1((1.1641532182693481445e-10 + 0.0j)) assert ae(v, (22.296641293693077672 + 0.0j), tol=ATOL) assert ae(v.real, 22.296641293693077672, tol=PTOL) assert v.imag == 0 v = fp.e1((0.25 + 0.0j)) assert ae(v, (1.0442826344437381945 + 0.0j), tol=ATOL) assert ae(v.real, 1.0442826344437381945, tol=PTOL) assert v.imag == 0 v = fp.e1((1.0 + 0.0j)) assert ae(v, (0.21938393439552027368 + 0.0j), tol=ATOL) assert ae(v.real, 0.21938393439552027368, tol=PTOL) assert v.imag == 0 v = fp.e1((2.0 + 0.0j)) assert ae(v, (0.048900510708061119567 + 0.0j), tol=ATOL) assert ae(v.real, 0.048900510708061119567, tol=PTOL) assert v.imag == 0 v = fp.e1((5.0 + 0.0j)) assert ae(v, (0.0011482955912753257973 + 0.0j), tol=ATOL) assert ae(v.real, 0.0011482955912753257973, tol=PTOL) assert v.imag == 0 v = fp.e1((20.0 + 0.0j)) assert ae(v, (9.8355252906498816904e-11 + 0.0j), tol=ATOL) assert ae(v.real, 9.8355252906498816904e-11, tol=PTOL) assert v.imag == 0 v = fp.e1((30.0 + 0.0j)) assert ae(v, (3.0215520106888125448e-15 + 0.0j), tol=ATOL) assert ae(v.real, 3.0215520106888125448e-15, tol=PTOL) assert v.imag == 0 v = fp.e1((40.0 + 0.0j)) assert ae(v, (1.0367732614516569722e-19 + 0.0j), tol=ATOL) assert ae(v.real, 1.0367732614516569722e-19, tol=PTOL) assert v.imag == 0 v = fp.e1((50.0 + 0.0j)) assert ae(v, (3.7832640295504590187e-24 + 0.0j), tol=ATOL) assert ae(v.real, 3.7832640295504590187e-24, tol=PTOL) assert v.imag == 0 v = fp.e1((80.0 + 0.0j)) assert ae(v, (2.2285432586884729112e-37 + 0.0j), tol=ATOL) assert ae(v.real, 2.2285432586884729112e-37, tol=PTOL) assert v.imag == 0 v = fp.e1((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (20.880034622014215597 - 0.24497866301044883237j), tol=ATOL) assert ae(v.real, 20.880034622014215597, tol=PTOL) assert ae(v.imag, -0.24497866301044883237, tol=PTOL) v = fp.e1((1.0 + 0.25j)) assert ae(v, (0.19731063945004229095 - 0.087366045774299963672j), tol=ATOL) assert ae(v.real, 0.19731063945004229095, tol=PTOL) assert ae(v.imag, -0.087366045774299963672, tol=PTOL) v = fp.e1((4.0 + 1.0j)) assert ae(v, (0.0013106173980145506944 - 0.0034542480199350626699j), tol=ATOL) assert ae(v.real, 0.0013106173980145506944, tol=PTOL) assert ae(v.imag, -0.0034542480199350626699, tol=PTOL) v = fp.e1((8.0 + 2.0j)) assert ae(v, (-0.000022278049065270225945 - 0.000029191940456521555288j), tol=ATOL) assert ae(v.real, -0.000022278049065270225945, tol=PTOL) assert ae(v.imag, -0.000029191940456521555288, tol=PTOL) v = fp.e1((20.0 + 5.0j)) assert ae(v, (4.7711374515765346894e-11 + 8.2902652405126947359e-11j), tol=ATOL) assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, 8.2902652405126947359e-11, tol=PTOL) v = fp.e1((80.0 + 20.0j)) assert ae(v, (3.8353473865788235787e-38 - 2.129247592349605139e-37j), tol=ATOL) assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, -2.129247592349605139e-37, tol=PTOL) v = fp.e1((120.0 + 30.0j)) assert ae(v, (2.3836002337480334716e-55 + 5.6704043587126198306e-55j), tol=ATOL) assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, 5.6704043587126198306e-55, tol=PTOL) v = fp.e1((160.0 + 40.0j)) assert ae(v, (-1.6238022898654510661e-72 - 1.104172355572287367e-72j), tol=ATOL) assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, -1.104172355572287367e-72, tol=PTOL) v = fp.e1((200.0 + 50.0j)) assert ae(v, (6.6800061461666228487e-90 + 1.4473816083541016115e-91j), tol=ATOL) assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, 1.4473816083541016115e-91, tol=PTOL) v = fp.e1((320.0 + 80.0j)) assert ae(v, (4.2737871527778786157e-143 + 3.1789935525785660314e-142j), tol=ATOL) assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, 3.1789935525785660314e-142, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) assert ae(v, (21.950067703413105017 - 0.7853981632810329878j), tol=ATOL) assert ae(v.real, 21.950067703413105017, tol=PTOL) assert ae(v.imag, -0.7853981632810329878, tol=PTOL) v = fp.e1((0.25 + 0.25j)) assert ae(v, (0.71092525792923287894 - 0.56491812441304194711j), tol=ATOL) assert ae(v.real, 0.71092525792923287894, tol=PTOL) assert ae(v.imag, -0.56491812441304194711, tol=PTOL) v = fp.e1((1.0 + 1.0j)) assert ae(v, (0.00028162445198141832551 - 0.17932453503935894015j), tol=ATOL) assert ae(v.real, 0.00028162445198141832551, tol=PTOL) assert ae(v.imag, -0.17932453503935894015, tol=PTOL) v = fp.e1((2.0 + 2.0j)) assert ae(v, (-0.033767089606562004246 - 0.018599414169750541925j), tol=ATOL) assert ae(v.real, -0.033767089606562004246, tol=PTOL) assert ae(v.imag, -0.018599414169750541925, tol=PTOL) v = fp.e1((5.0 + 5.0j)) assert ae(v, (0.0007266506660356393891 + 0.00047102780163522245054j), tol=ATOL) assert ae(v.real, 0.0007266506660356393891, tol=PTOL) assert ae(v.imag, 0.00047102780163522245054, tol=PTOL) v = fp.e1((20.0 + 20.0j)) assert ae(v, (-2.3824537449367396579e-11 - 6.6969873156525615158e-11j), tol=ATOL) assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) assert ae(v.imag, -6.6969873156525615158e-11, tol=PTOL) v = fp.e1((30.0 + 30.0j)) assert ae(v, (1.7316045841744061617e-15 + 1.3065678019487308689e-15j), tol=ATOL) assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) assert ae(v.imag, 1.3065678019487308689e-15, tol=PTOL) v = fp.e1((40.0 + 40.0j)) assert ae(v, (-7.4001043002899232182e-20 - 4.991847855336816304e-21j), tol=ATOL) assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) assert ae(v.imag, -4.991847855336816304e-21, tol=PTOL) v = fp.e1((50.0 + 50.0j)) assert ae(v, (2.3566128324644641219e-24 - 1.3188326726201614778e-24j), tol=ATOL) assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) assert ae(v.imag, -1.3188326726201614778e-24, tol=PTOL) v = fp.e1((80.0 + 80.0j)) assert ae(v, (9.8279750572186526673e-38 + 1.243952841288868831e-37j), tol=ATOL) assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) assert ae(v.imag, 1.243952841288868831e-37, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (20.880034621664969632 - 1.3258176632023711778j), tol=ATOL) assert ae(v.real, 20.880034621664969632, tol=PTOL) assert ae(v.imag, -1.3258176632023711778, tol=PTOL) v = fp.e1((0.25 + 1.0j)) assert ae(v, (-0.16868306393667788761 - 0.4858011885947426971j), tol=ATOL) assert ae(v.real, -0.16868306393667788761, tol=PTOL) assert ae(v.imag, -0.4858011885947426971, tol=PTOL) v = fp.e1((1.0 + 4.0j)) assert ae(v, (0.03373591813926547318 + 0.073523452241083821877j), tol=ATOL) assert ae(v.real, 0.03373591813926547318, tol=PTOL) assert ae(v.imag, 0.073523452241083821877, tol=PTOL) v = fp.e1((2.0 + 8.0j)) assert ae(v, (-0.015392833434733785143 - 0.0031747121557605415914j), tol=ATOL) assert ae(v.real, -0.015392833434733785143, tol=PTOL) assert ae(v.imag, -0.0031747121557605415914, tol=PTOL) v = fp.e1((5.0 + 20.0j)) assert ae(v, (-0.00024419662286542966525 - 0.00021008322966152755674j), tol=ATOL) assert ae(v.real, -0.00024419662286542966525, tol=PTOL) assert ae(v.imag, -0.00021008322966152755674, tol=PTOL) v = fp.e1((20.0 + 80.0j)) assert ae(v, (2.3255552781051330088e-11 + 8.9463918891349438007e-12j), tol=ATOL) assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, 8.9463918891349438007e-12, tol=PTOL) v = fp.e1((30.0 + 120.0j)) assert ae(v, (-2.7068919097124652332e-16 - 7.0477762411705130239e-16j), tol=ATOL) assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, -7.0477762411705130239e-16, tol=PTOL) v = fp.e1((40.0 + 160.0j)) assert ae(v, (-1.1695597827678024687e-20 + 2.2907401455645736661e-20j), tol=ATOL) assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, 2.2907401455645736661e-20, tol=PTOL) v = fp.e1((50.0 + 200.0j)) assert ae(v, (9.0323746914410162531e-25 - 2.3950601790033530935e-25j), tol=ATOL) assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, -2.3950601790033530935e-25, tol=PTOL) v = fp.e1((80.0 + 320.0j)) assert ae(v, (3.4819106748728063576e-38 - 4.215653005615772724e-38j), tol=ATOL) assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, -4.215653005615772724e-38, tol=PTOL) v = fp.e1((0.0 + 1.1641532182693481445e-10j)) assert ae(v, (22.29664129357666235 - 1.5707963266784812974j), tol=ATOL) assert ae(v.real, 22.29664129357666235, tol=PTOL) assert ae(v.imag, -1.5707963266784812974, tol=PTOL) v = fp.e1((0.0 + 0.25j)) assert ae(v, (0.82466306258094565309 - 1.3216627564751394551j), tol=ATOL) assert ae(v.real, 0.82466306258094565309, tol=PTOL) assert ae(v.imag, -1.3216627564751394551, tol=PTOL) v = fp.e1((0.0 + 1.0j)) assert ae(v, (-0.33740392290096813466 - 0.62471325642771360429j), tol=ATOL) assert ae(v.real, -0.33740392290096813466, tol=PTOL) assert ae(v.imag, -0.62471325642771360429, tol=PTOL) v = fp.e1((0.0 + 2.0j)) assert ae(v, (-0.4229808287748649957 + 0.034616650007798229345j), tol=ATOL) assert ae(v.real, -0.4229808287748649957, tol=PTOL) assert ae(v.imag, 0.034616650007798229345, tol=PTOL) v = fp.e1((0.0 + 5.0j)) assert ae(v, (0.19002974965664387862 - 0.020865081850222481957j), tol=ATOL) assert ae(v.real, 0.19002974965664387862, tol=PTOL) assert ae(v.imag, -0.020865081850222481957, tol=PTOL) v = fp.e1((0.0 + 20.0j)) assert ae(v, (-0.04441982084535331654 - 0.022554625751456779068j), tol=ATOL) assert ae(v.real, -0.04441982084535331654, tol=PTOL) assert ae(v.imag, -0.022554625751456779068, tol=PTOL) v = fp.e1((0.0 + 30.0j)) assert ae(v, (0.033032417282071143779 - 0.0040397867645455082476j), tol=ATOL) assert ae(v.real, 0.033032417282071143779, tol=PTOL) assert ae(v.imag, -0.0040397867645455082476, tol=PTOL) v = fp.e1((0.0 + 40.0j)) assert ae(v, (-0.019020007896208766962 + 0.016188792559887887544j), tol=ATOL) assert ae(v.real, -0.019020007896208766962, tol=PTOL) assert ae(v.imag, 0.016188792559887887544, tol=PTOL) v = fp.e1((0.0 + 50.0j)) assert ae(v, (0.0056283863241163054402 - 0.019179254308960724503j), tol=ATOL) assert ae(v.real, 0.0056283863241163054402, tol=PTOL) assert ae(v.imag, -0.019179254308960724503, tol=PTOL) v = fp.e1((0.0 + 80.0j)) assert ae(v, (0.012402501155070958192 + 0.0015345601175906961199j), tol=ATOL) assert ae(v.real, 0.012402501155070958192, tol=PTOL) assert ae(v.imag, 0.0015345601175906961199, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (20.880034621432138988 - 1.8157749894560994861j), tol=ATOL) assert ae(v.real, 20.880034621432138988, tol=PTOL) assert ae(v.imag, -1.8157749894560994861, tol=PTOL) v = fp.e1((-0.25 + 1.0j)) assert ae(v, (-0.59066621214766308594 - 0.74474454765205036972j), tol=ATOL) assert ae(v.real, -0.59066621214766308594, tol=PTOL) assert ae(v.imag, -0.74474454765205036972, tol=PTOL) v = fp.e1((-1.0 + 4.0j)) assert ae(v, (0.49739047283060471093 + 0.41543605404038863174j), tol=ATOL) assert ae(v.real, 0.49739047283060471093, tol=PTOL) assert ae(v.imag, 0.41543605404038863174, tol=PTOL) v = fp.e1((-2.0 + 8.0j)) assert ae(v, (-0.8705211147733730969 + 0.24099328498605539667j), tol=ATOL) assert ae(v.real, -0.8705211147733730969, tol=PTOL) assert ae(v.imag, 0.24099328498605539667, tol=PTOL) v = fp.e1((-5.0 + 20.0j)) assert ae(v, (-7.0789514293925893007 - 1.6102177171960790536j), tol=ATOL) assert ae(v.real, -7.0789514293925893007, tol=PTOL) assert ae(v.imag, -1.6102177171960790536, tol=PTOL) v = fp.e1((-20.0 + 80.0j)) assert ae(v, (5855431.4907298084434 - 720920.93315409165707j), tol=ATOL) assert ae(v.real, 5855431.4907298084434, tol=PTOL) assert ae(v.imag, -720920.93315409165707, tol=PTOL) v = fp.e1((-30.0 + 120.0j)) assert ae(v, (-65402491644.703470747 - 56697658399.657460294j), tol=ATOL) assert ae(v.real, -65402491644.703470747, tol=PTOL) assert ae(v.imag, -56697658399.657460294, tol=PTOL) v = fp.e1((-40.0 + 160.0j)) assert ae(v, (25504929379604.776769 + 1429035198630573.2463j), tol=ATOL) assert ae(v.real, 25504929379604.776769, tol=PTOL) assert ae(v.imag, 1429035198630573.2463, tol=PTOL) v = fp.e1((-50.0 + 200.0j)) assert ae(v, (18437746526988116954.0 - 17146362239046152345.0j), tol=ATOL) assert ae(v.real, 18437746526988116954.0, tol=PTOL) assert ae(v.imag, -17146362239046152345.0, tol=PTOL) v = fp.e1((-80.0 + 320.0j)) assert ae(v, (3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) v = fp.e1((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (20.880034621082893023 - 2.8966139903465137624j), tol=ATOL) assert ae(v.real, 20.880034621082893023, tol=PTOL) assert ae(v.imag, -2.8966139903465137624, tol=PTOL) v = fp.e1((-1.0 + 0.25j)) assert ae(v, (-1.8942716983721074932 - 2.4689102827070540799j), tol=ATOL) assert ae(v.real, -1.8942716983721074932, tol=PTOL) assert ae(v.imag, -2.4689102827070540799, tol=PTOL) v = fp.e1((-4.0 + 1.0j)) assert ae(v, (-14.806699492675420438 + 9.1384225230837893776j), tol=ATOL) assert ae(v.real, -14.806699492675420438, tol=PTOL) assert ae(v.imag, 9.1384225230837893776, tol=PTOL) v = fp.e1((-8.0 + 2.0j)) assert ae(v, (54.633252667426386294 + 413.20318163814670688j), tol=ATOL) assert ae(v.real, 54.633252667426386294, tol=PTOL) assert ae(v.imag, 413.20318163814670688, tol=PTOL) v = fp.e1((-20.0 + 5.0j)) assert ae(v, (-711836.97165402624643 - 24745250.939695900956j), tol=ATOL) assert ae(v.real, -711836.97165402624643, tol=PTOL) assert ae(v.imag, -24745250.939695900956, tol=PTOL) v = fp.e1((-80.0 + 20.0j)) assert ae(v, (-4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) v = fp.e1((-120.0 + 30.0j)) assert ae(v, (9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) v = fp.e1((-160.0 + 40.0j)) assert ae(v, (8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) v = fp.e1((-200.0 + 50.0j)) assert ae(v, (-3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) v = fp.e1((-320.0 + 80.0j)) assert ae(v, (9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) v = fp.e1(-1.1641532182693481445e-10) assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 22.296641293460247028, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-0.25) assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 0.54254326466191372953, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-1.0) assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.8951178163559367555, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-2.0) assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.9542343560018901634, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-5.0) assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -40.185275355803177455, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-20.0) assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -25615652.66405658882, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-30.0) assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -368973209407.27419706, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-40.0) assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6039718263611241.5784, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-50.0) assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1(-80.0) assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 + 0.0j)) assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 22.296641293460247028, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-0.25 + 0.0j)) assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 0.54254326466191372953, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-1.0 + 0.0j)) assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.8951178163559367555, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-2.0 + 0.0j)) assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.9542343560018901634, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-5.0 + 0.0j)) assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -40.185275355803177455, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-20.0 + 0.0j)) assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -25615652.66405658882, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-30.0 + 0.0j)) assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -368973209407.27419706, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-40.0 + 0.0j)) assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6039718263611241.5784, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-50.0 + 0.0j)) assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-80.0 + 0.0j)) assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.e1((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (20.880034621082893023 + 2.8966139903465137624j), tol=ATOL) assert ae(v.real, 20.880034621082893023, tol=PTOL) assert ae(v.imag, 2.8966139903465137624, tol=PTOL) v = fp.e1((-1.0 - 0.25j)) assert ae(v, (-1.8942716983721074932 + 2.4689102827070540799j), tol=ATOL) assert ae(v.real, -1.8942716983721074932, tol=PTOL) assert ae(v.imag, 2.4689102827070540799, tol=PTOL) v = fp.e1((-4.0 - 1.0j)) assert ae(v, (-14.806699492675420438 - 9.1384225230837893776j), tol=ATOL) assert ae(v.real, -14.806699492675420438, tol=PTOL) assert ae(v.imag, -9.1384225230837893776, tol=PTOL) v = fp.e1((-8.0 - 2.0j)) assert ae(v, (54.633252667426386294 - 413.20318163814670688j), tol=ATOL) assert ae(v.real, 54.633252667426386294, tol=PTOL) assert ae(v.imag, -413.20318163814670688, tol=PTOL) v = fp.e1((-20.0 - 5.0j)) assert ae(v, (-711836.97165402624643 + 24745250.939695900956j), tol=ATOL) assert ae(v.real, -711836.97165402624643, tol=PTOL) assert ae(v.imag, 24745250.939695900956, tol=PTOL) v = fp.e1((-80.0 - 20.0j)) assert ae(v, (-4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) v = fp.e1((-120.0 - 30.0j)) assert ae(v, (9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) v = fp.e1((-160.0 - 40.0j)) assert ae(v, (8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) v = fp.e1((-200.0 - 50.0j)) assert ae(v, (-3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) v = fp.e1((-320.0 - 80.0j)) assert ae(v, (9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (21.950067703180274374 + 2.356194490075929607j), tol=ATOL) assert ae(v.real, 21.950067703180274374, tol=PTOL) assert ae(v.imag, 2.356194490075929607, tol=PTOL) v = fp.e1((-0.25 - 0.25j)) assert ae(v, (0.21441047326710323254 + 2.0732153554307936389j), tol=ATOL) assert ae(v.real, 0.21441047326710323254, tol=PTOL) assert ae(v.imag, 2.0732153554307936389, tol=PTOL) v = fp.e1((-1.0 - 1.0j)) assert ae(v, (-1.7646259855638540684 + 0.7538228020792708192j), tol=ATOL) assert ae(v.real, -1.7646259855638540684, tol=PTOL) assert ae(v.imag, 0.7538228020792708192, tol=PTOL) v = fp.e1((-2.0 - 2.0j)) assert ae(v, (-1.8920781621855474089 - 2.1753697842428647236j), tol=ATOL) assert ae(v.real, -1.8920781621855474089, tol=PTOL) assert ae(v.imag, -2.1753697842428647236, tol=PTOL) v = fp.e1((-5.0 - 5.0j)) assert ae(v, (13.470936071475245856 + 18.464085049321024206j), tol=ATOL) assert ae(v.real, 13.470936071475245856, tol=PTOL) assert ae(v.imag, 18.464085049321024206, tol=PTOL) v = fp.e1((-20.0 - 20.0j)) assert ae(v, (-16589317.398788971896 - 5831702.3296441771206j), tol=ATOL) assert ae(v.real, -16589317.398788971896, tol=PTOL) assert ae(v.imag, -5831702.3296441771206, tol=PTOL) v = fp.e1((-30.0 - 30.0j)) assert ae(v, (154596484273.69322527 + 204179357837.41389696j), tol=ATOL) assert ae(v.real, 154596484273.69322527, tol=PTOL) assert ae(v.imag, 204179357837.41389696, tol=PTOL) v = fp.e1((-40.0 - 40.0j)) assert ae(v, (-287512180321448.45408 - 4203502407932314.974j), tol=ATOL) assert ae(v.real, -287512180321448.45408, tol=PTOL) assert ae(v.imag, -4203502407932314.974, tol=PTOL) v = fp.e1((-50.0 - 50.0j)) assert ae(v, (-36128528616649268826.0 + 64648801861338741963.0j), tol=ATOL) assert ae(v.real, -36128528616649268826.0, tol=PTOL) assert ae(v.imag, 64648801861338741963.0, tol=PTOL) v = fp.e1((-80.0 - 80.0j)) assert ae(v, (3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) assert ae(v.real, 3.8674816337930010217e+32, tol=PTOL) assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) v = fp.e1((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (20.880034621432138988 + 1.8157749894560994861j), tol=ATOL) assert ae(v.real, 20.880034621432138988, tol=PTOL) assert ae(v.imag, 1.8157749894560994861, tol=PTOL) v = fp.e1((-0.25 - 1.0j)) assert ae(v, (-0.59066621214766308594 + 0.74474454765205036972j), tol=ATOL) assert ae(v.real, -0.59066621214766308594, tol=PTOL) assert ae(v.imag, 0.74474454765205036972, tol=PTOL) v = fp.e1((-1.0 - 4.0j)) assert ae(v, (0.49739047283060471093 - 0.41543605404038863174j), tol=ATOL) assert ae(v.real, 0.49739047283060471093, tol=PTOL) assert ae(v.imag, -0.41543605404038863174, tol=PTOL) v = fp.e1((-2.0 - 8.0j)) assert ae(v, (-0.8705211147733730969 - 0.24099328498605539667j), tol=ATOL) assert ae(v.real, -0.8705211147733730969, tol=PTOL) assert ae(v.imag, -0.24099328498605539667, tol=PTOL) v = fp.e1((-5.0 - 20.0j)) assert ae(v, (-7.0789514293925893007 + 1.6102177171960790536j), tol=ATOL) assert ae(v.real, -7.0789514293925893007, tol=PTOL) assert ae(v.imag, 1.6102177171960790536, tol=PTOL) v = fp.e1((-20.0 - 80.0j)) assert ae(v, (5855431.4907298084434 + 720920.93315409165707j), tol=ATOL) assert ae(v.real, 5855431.4907298084434, tol=PTOL) assert ae(v.imag, 720920.93315409165707, tol=PTOL) v = fp.e1((-30.0 - 120.0j)) assert ae(v, (-65402491644.703470747 + 56697658399.657460294j), tol=ATOL) assert ae(v.real, -65402491644.703470747, tol=PTOL) assert ae(v.imag, 56697658399.657460294, tol=PTOL) v = fp.e1((-40.0 - 160.0j)) assert ae(v, (25504929379604.776769 - 1429035198630573.2463j), tol=ATOL) assert ae(v.real, 25504929379604.776769, tol=PTOL) assert ae(v.imag, -1429035198630573.2463, tol=PTOL) v = fp.e1((-50.0 - 200.0j)) assert ae(v, (18437746526988116954.0 + 17146362239046152345.0j), tol=ATOL) assert ae(v.real, 18437746526988116954.0, tol=PTOL) assert ae(v.imag, 17146362239046152345.0, tol=PTOL) v = fp.e1((-80.0 - 320.0j)) assert ae(v, (3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) v = fp.e1((0.0 - 1.1641532182693481445e-10j)) assert ae(v, (22.29664129357666235 + 1.5707963266784812974j), tol=ATOL) assert ae(v.real, 22.29664129357666235, tol=PTOL) assert ae(v.imag, 1.5707963266784812974, tol=PTOL) v = fp.e1((0.0 - 0.25j)) assert ae(v, (0.82466306258094565309 + 1.3216627564751394551j), tol=ATOL) assert ae(v.real, 0.82466306258094565309, tol=PTOL) assert ae(v.imag, 1.3216627564751394551, tol=PTOL) v = fp.e1((0.0 - 1.0j)) assert ae(v, (-0.33740392290096813466 + 0.62471325642771360429j), tol=ATOL) assert ae(v.real, -0.33740392290096813466, tol=PTOL) assert ae(v.imag, 0.62471325642771360429, tol=PTOL) v = fp.e1((0.0 - 2.0j)) assert ae(v, (-0.4229808287748649957 - 0.034616650007798229345j), tol=ATOL) assert ae(v.real, -0.4229808287748649957, tol=PTOL) assert ae(v.imag, -0.034616650007798229345, tol=PTOL) v = fp.e1((0.0 - 5.0j)) assert ae(v, (0.19002974965664387862 + 0.020865081850222481957j), tol=ATOL) assert ae(v.real, 0.19002974965664387862, tol=PTOL) assert ae(v.imag, 0.020865081850222481957, tol=PTOL) v = fp.e1((0.0 - 20.0j)) assert ae(v, (-0.04441982084535331654 + 0.022554625751456779068j), tol=ATOL) assert ae(v.real, -0.04441982084535331654, tol=PTOL) assert ae(v.imag, 0.022554625751456779068, tol=PTOL) v = fp.e1((0.0 - 30.0j)) assert ae(v, (0.033032417282071143779 + 0.0040397867645455082476j), tol=ATOL) assert ae(v.real, 0.033032417282071143779, tol=PTOL) assert ae(v.imag, 0.0040397867645455082476, tol=PTOL) v = fp.e1((0.0 - 40.0j)) assert ae(v, (-0.019020007896208766962 - 0.016188792559887887544j), tol=ATOL) assert ae(v.real, -0.019020007896208766962, tol=PTOL) assert ae(v.imag, -0.016188792559887887544, tol=PTOL) v = fp.e1((0.0 - 50.0j)) assert ae(v, (0.0056283863241163054402 + 0.019179254308960724503j), tol=ATOL) assert ae(v.real, 0.0056283863241163054402, tol=PTOL) assert ae(v.imag, 0.019179254308960724503, tol=PTOL) v = fp.e1((0.0 - 80.0j)) assert ae(v, (0.012402501155070958192 - 0.0015345601175906961199j), tol=ATOL) assert ae(v.real, 0.012402501155070958192, tol=PTOL) assert ae(v.imag, -0.0015345601175906961199, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (20.880034621664969632 + 1.3258176632023711778j), tol=ATOL) assert ae(v.real, 20.880034621664969632, tol=PTOL) assert ae(v.imag, 1.3258176632023711778, tol=PTOL) v = fp.e1((0.25 - 1.0j)) assert ae(v, (-0.16868306393667788761 + 0.4858011885947426971j), tol=ATOL) assert ae(v.real, -0.16868306393667788761, tol=PTOL) assert ae(v.imag, 0.4858011885947426971, tol=PTOL) v = fp.e1((1.0 - 4.0j)) assert ae(v, (0.03373591813926547318 - 0.073523452241083821877j), tol=ATOL) assert ae(v.real, 0.03373591813926547318, tol=PTOL) assert ae(v.imag, -0.073523452241083821877, tol=PTOL) v = fp.e1((2.0 - 8.0j)) assert ae(v, (-0.015392833434733785143 + 0.0031747121557605415914j), tol=ATOL) assert ae(v.real, -0.015392833434733785143, tol=PTOL) assert ae(v.imag, 0.0031747121557605415914, tol=PTOL) v = fp.e1((5.0 - 20.0j)) assert ae(v, (-0.00024419662286542966525 + 0.00021008322966152755674j), tol=ATOL) assert ae(v.real, -0.00024419662286542966525, tol=PTOL) assert ae(v.imag, 0.00021008322966152755674, tol=PTOL) v = fp.e1((20.0 - 80.0j)) assert ae(v, (2.3255552781051330088e-11 - 8.9463918891349438007e-12j), tol=ATOL) assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, -8.9463918891349438007e-12, tol=PTOL) v = fp.e1((30.0 - 120.0j)) assert ae(v, (-2.7068919097124652332e-16 + 7.0477762411705130239e-16j), tol=ATOL) assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, 7.0477762411705130239e-16, tol=PTOL) v = fp.e1((40.0 - 160.0j)) assert ae(v, (-1.1695597827678024687e-20 - 2.2907401455645736661e-20j), tol=ATOL) assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, -2.2907401455645736661e-20, tol=PTOL) v = fp.e1((50.0 - 200.0j)) assert ae(v, (9.0323746914410162531e-25 + 2.3950601790033530935e-25j), tol=ATOL) assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, 2.3950601790033530935e-25, tol=PTOL) v = fp.e1((80.0 - 320.0j)) assert ae(v, (3.4819106748728063576e-38 + 4.215653005615772724e-38j), tol=ATOL) assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, 4.215653005615772724e-38, tol=PTOL) v = fp.e1((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (21.950067703413105017 + 0.7853981632810329878j), tol=ATOL) assert ae(v.real, 21.950067703413105017, tol=PTOL) assert ae(v.imag, 0.7853981632810329878, tol=PTOL) v = fp.e1((0.25 - 0.25j)) assert ae(v, (0.71092525792923287894 + 0.56491812441304194711j), tol=ATOL) assert ae(v.real, 0.71092525792923287894, tol=PTOL) assert ae(v.imag, 0.56491812441304194711, tol=PTOL) v = fp.e1((1.0 - 1.0j)) assert ae(v, (0.00028162445198141832551 + 0.17932453503935894015j), tol=ATOL) assert ae(v.real, 0.00028162445198141832551, tol=PTOL) assert ae(v.imag, 0.17932453503935894015, tol=PTOL) v = fp.e1((2.0 - 2.0j)) assert ae(v, (-0.033767089606562004246 + 0.018599414169750541925j), tol=ATOL) assert ae(v.real, -0.033767089606562004246, tol=PTOL) assert ae(v.imag, 0.018599414169750541925, tol=PTOL) v = fp.e1((5.0 - 5.0j)) assert ae(v, (0.0007266506660356393891 - 0.00047102780163522245054j), tol=ATOL) assert ae(v.real, 0.0007266506660356393891, tol=PTOL) assert ae(v.imag, -0.00047102780163522245054, tol=PTOL) v = fp.e1((20.0 - 20.0j)) assert ae(v, (-2.3824537449367396579e-11 + 6.6969873156525615158e-11j), tol=ATOL) assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) assert ae(v.imag, 6.6969873156525615158e-11, tol=PTOL) v = fp.e1((30.0 - 30.0j)) assert ae(v, (1.7316045841744061617e-15 - 1.3065678019487308689e-15j), tol=ATOL) assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) assert ae(v.imag, -1.3065678019487308689e-15, tol=PTOL) v = fp.e1((40.0 - 40.0j)) assert ae(v, (-7.4001043002899232182e-20 + 4.991847855336816304e-21j), tol=ATOL) assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) assert ae(v.imag, 4.991847855336816304e-21, tol=PTOL) v = fp.e1((50.0 - 50.0j)) assert ae(v, (2.3566128324644641219e-24 + 1.3188326726201614778e-24j), tol=ATOL) assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) assert ae(v.imag, 1.3188326726201614778e-24, tol=PTOL) v = fp.e1((80.0 - 80.0j)) assert ae(v, (9.8279750572186526673e-38 - 1.243952841288868831e-37j), tol=ATOL) assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) assert ae(v.imag, -1.243952841288868831e-37, tol=PTOL) v = fp.e1((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (20.880034622014215597 + 0.24497866301044883237j), tol=ATOL) assert ae(v.real, 20.880034622014215597, tol=PTOL) assert ae(v.imag, 0.24497866301044883237, tol=PTOL) v = fp.e1((1.0 - 0.25j)) assert ae(v, (0.19731063945004229095 + 0.087366045774299963672j), tol=ATOL) assert ae(v.real, 0.19731063945004229095, tol=PTOL) assert ae(v.imag, 0.087366045774299963672, tol=PTOL) v = fp.e1((4.0 - 1.0j)) assert ae(v, (0.0013106173980145506944 + 0.0034542480199350626699j), tol=ATOL) assert ae(v.real, 0.0013106173980145506944, tol=PTOL) assert ae(v.imag, 0.0034542480199350626699, tol=PTOL) v = fp.e1((8.0 - 2.0j)) assert ae(v, (-0.000022278049065270225945 + 0.000029191940456521555288j), tol=ATOL) assert ae(v.real, -0.000022278049065270225945, tol=PTOL) assert ae(v.imag, 0.000029191940456521555288, tol=PTOL) v = fp.e1((20.0 - 5.0j)) assert ae(v, (4.7711374515765346894e-11 - 8.2902652405126947359e-11j), tol=ATOL) assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, -8.2902652405126947359e-11, tol=PTOL) v = fp.e1((80.0 - 20.0j)) assert ae(v, (3.8353473865788235787e-38 + 2.129247592349605139e-37j), tol=ATOL) assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, 2.129247592349605139e-37, tol=PTOL) v = fp.e1((120.0 - 30.0j)) assert ae(v, (2.3836002337480334716e-55 - 5.6704043587126198306e-55j), tol=ATOL) assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, -5.6704043587126198306e-55, tol=PTOL) v = fp.e1((160.0 - 40.0j)) assert ae(v, (-1.6238022898654510661e-72 + 1.104172355572287367e-72j), tol=ATOL) assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, 1.104172355572287367e-72, tol=PTOL) v = fp.e1((200.0 - 50.0j)) assert ae(v, (6.6800061461666228487e-90 - 1.4473816083541016115e-91j), tol=ATOL) assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, -1.4473816083541016115e-91, tol=PTOL) v = fp.e1((320.0 - 80.0j)) assert ae(v, (4.2737871527778786157e-143 - 3.1789935525785660314e-142j), tol=ATOL) assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, -3.1789935525785660314e-142, tol=PTOL) v = fp.ei(1.1641532182693481445e-10) assert ae(v, -22.296641293460247028, tol=ATOL) assert type(v) is float v = fp.ei(0.25) assert ae(v, -0.54254326466191372953, tol=ATOL) assert type(v) is float v = fp.ei(1.0) assert ae(v, 1.8951178163559367555, tol=ATOL) assert type(v) is float v = fp.ei(2.0) assert ae(v, 4.9542343560018901634, tol=ATOL) assert type(v) is float v = fp.ei(5.0) assert ae(v, 40.185275355803177455, tol=ATOL) assert type(v) is float v = fp.ei(20.0) assert ae(v, 25615652.66405658882, tol=ATOL) assert type(v) is float v = fp.ei(30.0) assert ae(v, 368973209407.27419706, tol=ATOL) assert type(v) is float v = fp.ei(40.0) assert ae(v, 6039718263611241.5784, tol=ATOL) assert type(v) is float v = fp.ei(50.0) assert ae(v, 1.0585636897131690963e+20, tol=ATOL) assert type(v) is float v = fp.ei(80.0) assert ae(v, 7.0146000049047999696e+32, tol=ATOL) assert type(v) is float v = fp.ei((1.1641532182693481445e-10 + 0.0j)) assert ae(v, (-22.296641293460247028 + 0.0j), tol=ATOL) assert ae(v.real, -22.296641293460247028, tol=PTOL) assert v.imag == 0 v = fp.ei((0.25 + 0.0j)) assert ae(v, (-0.54254326466191372953 + 0.0j), tol=ATOL) assert ae(v.real, -0.54254326466191372953, tol=PTOL) assert v.imag == 0 v = fp.ei((1.0 + 0.0j)) assert ae(v, (1.8951178163559367555 + 0.0j), tol=ATOL) assert ae(v.real, 1.8951178163559367555, tol=PTOL) assert v.imag == 0 v = fp.ei((2.0 + 0.0j)) assert ae(v, (4.9542343560018901634 + 0.0j), tol=ATOL) assert ae(v.real, 4.9542343560018901634, tol=PTOL) assert v.imag == 0 v = fp.ei((5.0 + 0.0j)) assert ae(v, (40.185275355803177455 + 0.0j), tol=ATOL) assert ae(v.real, 40.185275355803177455, tol=PTOL) assert v.imag == 0 v = fp.ei((20.0 + 0.0j)) assert ae(v, (25615652.66405658882 + 0.0j), tol=ATOL) assert ae(v.real, 25615652.66405658882, tol=PTOL) assert v.imag == 0 v = fp.ei((30.0 + 0.0j)) assert ae(v, (368973209407.27419706 + 0.0j), tol=ATOL) assert ae(v.real, 368973209407.27419706, tol=PTOL) assert v.imag == 0 v = fp.ei((40.0 + 0.0j)) assert ae(v, (6039718263611241.5784 + 0.0j), tol=ATOL) assert ae(v.real, 6039718263611241.5784, tol=PTOL) assert v.imag == 0 v = fp.ei((50.0 + 0.0j)) assert ae(v, (1.0585636897131690963e+20 + 0.0j), tol=ATOL) assert ae(v.real, 1.0585636897131690963e+20, tol=PTOL) assert v.imag == 0 v = fp.ei((80.0 + 0.0j)) assert ae(v, (7.0146000049047999696e+32 + 0.0j), tol=ATOL) assert ae(v.real, 7.0146000049047999696e+32, tol=PTOL) assert v.imag == 0 v = fp.ei((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (-20.880034621082893023 + 0.24497866324327947603j), tol=ATOL) assert ae(v.real, -20.880034621082893023, tol=PTOL) assert ae(v.imag, 0.24497866324327947603, tol=PTOL) v = fp.ei((1.0 + 0.25j)) assert ae(v, (1.8942716983721074932 + 0.67268237088273915854j), tol=ATOL) assert ae(v.real, 1.8942716983721074932, tol=PTOL) assert ae(v.imag, 0.67268237088273915854, tol=PTOL) v = fp.ei((4.0 + 1.0j)) assert ae(v, (14.806699492675420438 + 12.280015176673582616j), tol=ATOL) assert ae(v.real, 14.806699492675420438, tol=PTOL) assert ae(v.imag, 12.280015176673582616, tol=PTOL) v = fp.ei((8.0 + 2.0j)) assert ae(v, (-54.633252667426386294 + 416.34477429173650012j), tol=ATOL) assert ae(v.real, -54.633252667426386294, tol=PTOL) assert ae(v.imag, 416.34477429173650012, tol=PTOL) v = fp.ei((20.0 + 5.0j)) assert ae(v, (711836.97165402624643 - 24745247.798103247366j), tol=ATOL) assert ae(v.real, 711836.97165402624643, tol=PTOL) assert ae(v.imag, -24745247.798103247366, tol=PTOL) v = fp.ei((80.0 + 20.0j)) assert ae(v, (4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) v = fp.ei((120.0 + 30.0j)) assert ae(v, (-9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) v = fp.ei((160.0 + 40.0j)) assert ae(v, (-8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) v = fp.ei((200.0 + 50.0j)) assert ae(v, (3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) v = fp.ei((320.0 + 80.0j)) assert ae(v, (-9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) assert ae(v, (-21.950067703180274374 + 0.78539816351386363145j), tol=ATOL) assert ae(v.real, -21.950067703180274374, tol=PTOL) assert ae(v.imag, 0.78539816351386363145, tol=PTOL) v = fp.ei((0.25 + 0.25j)) assert ae(v, (-0.21441047326710323254 + 1.0683772981589995996j), tol=ATOL) assert ae(v.real, -0.21441047326710323254, tol=PTOL) assert ae(v.imag, 1.0683772981589995996, tol=PTOL) v = fp.ei((1.0 + 1.0j)) assert ae(v, (1.7646259855638540684 + 2.3877698515105224193j), tol=ATOL) assert ae(v.real, 1.7646259855638540684, tol=PTOL) assert ae(v.imag, 2.3877698515105224193, tol=PTOL) v = fp.ei((2.0 + 2.0j)) assert ae(v, (1.8920781621855474089 + 5.3169624378326579621j), tol=ATOL) assert ae(v.real, 1.8920781621855474089, tol=PTOL) assert ae(v.imag, 5.3169624378326579621, tol=PTOL) v = fp.ei((5.0 + 5.0j)) assert ae(v, (-13.470936071475245856 - 15.322492395731230968j), tol=ATOL) assert ae(v.real, -13.470936071475245856, tol=PTOL) assert ae(v.imag, -15.322492395731230968, tol=PTOL) v = fp.ei((20.0 + 20.0j)) assert ae(v, (16589317.398788971896 + 5831705.4712368307104j), tol=ATOL) assert ae(v.real, 16589317.398788971896, tol=PTOL) assert ae(v.imag, 5831705.4712368307104, tol=PTOL) v = fp.ei((30.0 + 30.0j)) assert ae(v, (-154596484273.69322527 - 204179357834.2723043j), tol=ATOL) assert ae(v.real, -154596484273.69322527, tol=PTOL) assert ae(v.imag, -204179357834.2723043, tol=PTOL) v = fp.ei((40.0 + 40.0j)) assert ae(v, (287512180321448.45408 + 4203502407932318.1156j), tol=ATOL) assert ae(v.real, 287512180321448.45408, tol=PTOL) assert ae(v.imag, 4203502407932318.1156, tol=PTOL) v = fp.ei((50.0 + 50.0j)) assert ae(v, (36128528616649268826.0 - 64648801861338741960.0j), tol=ATOL) assert ae(v.real, 36128528616649268826.0, tol=PTOL) assert ae(v.imag, -64648801861338741960.0, tol=PTOL) v = fp.ei((80.0 + 80.0j)) assert ae(v, (-3.8674816337930010217e+32 - 3.0540709639658071041e+32j), tol=ATOL) assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) assert ae(v.imag, -3.0540709639658071041e+32, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621432138988 + 1.3258176641336937524j), tol=ATOL) assert ae(v.real, -20.880034621432138988, tol=PTOL) assert ae(v.imag, 1.3258176641336937524, tol=PTOL) v = fp.ei((0.25 + 1.0j)) assert ae(v, (0.59066621214766308594 + 2.3968481059377428687j), tol=ATOL) assert ae(v.real, 0.59066621214766308594, tol=PTOL) assert ae(v.imag, 2.3968481059377428687, tol=PTOL) v = fp.ei((1.0 + 4.0j)) assert ae(v, (-0.49739047283060471093 + 3.5570287076301818702j), tol=ATOL) assert ae(v.real, -0.49739047283060471093, tol=PTOL) assert ae(v.imag, 3.5570287076301818702, tol=PTOL) v = fp.ei((2.0 + 8.0j)) assert ae(v, (0.8705211147733730969 + 3.3825859385758486351j), tol=ATOL) assert ae(v.real, 0.8705211147733730969, tol=PTOL) assert ae(v.imag, 3.3825859385758486351, tol=PTOL) v = fp.ei((5.0 + 20.0j)) assert ae(v, (7.0789514293925893007 + 1.5313749363937141849j), tol=ATOL) assert ae(v.real, 7.0789514293925893007, tol=PTOL) assert ae(v.imag, 1.5313749363937141849, tol=PTOL) v = fp.ei((20.0 + 80.0j)) assert ae(v, (-5855431.4907298084434 - 720917.79156143806727j), tol=ATOL) assert ae(v.real, -5855431.4907298084434, tol=PTOL) assert ae(v.imag, -720917.79156143806727, tol=PTOL) v = fp.ei((30.0 + 120.0j)) assert ae(v, (65402491644.703470747 - 56697658396.51586764j), tol=ATOL) assert ae(v.real, 65402491644.703470747, tol=PTOL) assert ae(v.imag, -56697658396.51586764, tol=PTOL) v = fp.ei((40.0 + 160.0j)) assert ae(v, (-25504929379604.776769 + 1429035198630576.3879j), tol=ATOL) assert ae(v.real, -25504929379604.776769, tol=PTOL) assert ae(v.imag, 1429035198630576.3879, tol=PTOL) v = fp.ei((50.0 + 200.0j)) assert ae(v, (-18437746526988116954.0 - 17146362239046152342.0j), tol=ATOL) assert ae(v.real, -18437746526988116954.0, tol=PTOL) assert ae(v.imag, -17146362239046152342.0, tol=PTOL) v = fp.ei((80.0 + 320.0j)) assert ae(v, (-3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) v = fp.ei((0.0 + 1.1641532182693481445e-10j)) assert ae(v, (-22.29664129357666235 + 1.5707963269113119411j), tol=ATOL) assert ae(v.real, -22.29664129357666235, tol=PTOL) assert ae(v.imag, 1.5707963269113119411, tol=PTOL) v = fp.ei((0.0 + 0.25j)) assert ae(v, (-0.82466306258094565309 + 1.8199298971146537833j), tol=ATOL) assert ae(v.real, -0.82466306258094565309, tol=PTOL) assert ae(v.imag, 1.8199298971146537833, tol=PTOL) v = fp.ei((0.0 + 1.0j)) assert ae(v, (0.33740392290096813466 + 2.5168793971620796342j), tol=ATOL) assert ae(v.real, 0.33740392290096813466, tol=PTOL) assert ae(v.imag, 2.5168793971620796342, tol=PTOL) v = fp.ei((0.0 + 2.0j)) assert ae(v, (0.4229808287748649957 + 3.1762093035975914678j), tol=ATOL) assert ae(v.real, 0.4229808287748649957, tol=PTOL) assert ae(v.imag, 3.1762093035975914678, tol=PTOL) v = fp.ei((0.0 + 5.0j)) assert ae(v, (-0.19002974965664387862 + 3.1207275717395707565j), tol=ATOL) assert ae(v.real, -0.19002974965664387862, tol=PTOL) assert ae(v.imag, 3.1207275717395707565, tol=PTOL) v = fp.ei((0.0 + 20.0j)) assert ae(v, (0.04441982084535331654 + 3.1190380278383364594j), tol=ATOL) assert ae(v.real, 0.04441982084535331654, tol=PTOL) assert ae(v.imag, 3.1190380278383364594, tol=PTOL) v = fp.ei((0.0 + 30.0j)) assert ae(v, (-0.033032417282071143779 + 3.1375528668252477302j), tol=ATOL) assert ae(v.real, -0.033032417282071143779, tol=PTOL) assert ae(v.imag, 3.1375528668252477302, tol=PTOL) v = fp.ei((0.0 + 40.0j)) assert ae(v, (0.019020007896208766962 + 3.157781446149681126j), tol=ATOL) assert ae(v.real, 0.019020007896208766962, tol=PTOL) assert ae(v.imag, 3.157781446149681126, tol=PTOL) v = fp.ei((0.0 + 50.0j)) assert ae(v, (-0.0056283863241163054402 + 3.122413399280832514j), tol=ATOL) assert ae(v.real, -0.0056283863241163054402, tol=PTOL) assert ae(v.imag, 3.122413399280832514, tol=PTOL) v = fp.ei((0.0 + 80.0j)) assert ae(v, (-0.012402501155070958192 + 3.1431272137073839346j), tol=ATOL) assert ae(v.real, -0.012402501155070958192, tol=PTOL) assert ae(v.imag, 3.1431272137073839346, tol=PTOL) v = fp.ei((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621664969632 + 1.8157749903874220607j), tol=ATOL) assert ae(v.real, -20.880034621664969632, tol=PTOL) assert ae(v.imag, 1.8157749903874220607, tol=PTOL) v = fp.ei((-0.25 + 1.0j)) assert ae(v, (0.16868306393667788761 + 2.6557914649950505414j), tol=ATOL) assert ae(v.real, 0.16868306393667788761, tol=PTOL) assert ae(v.imag, 2.6557914649950505414, tol=PTOL) v = fp.ei((-1.0 + 4.0j)) assert ae(v, (-0.03373591813926547318 + 3.2151161058308770603j), tol=ATOL) assert ae(v.real, -0.03373591813926547318, tol=PTOL) assert ae(v.imag, 3.2151161058308770603, tol=PTOL) v = fp.ei((-2.0 + 8.0j)) assert ae(v, (0.015392833434733785143 + 3.1384179414340326969j), tol=ATOL) assert ae(v.real, 0.015392833434733785143, tol=PTOL) assert ae(v.imag, 3.1384179414340326969, tol=PTOL) v = fp.ei((-5.0 + 20.0j)) assert ae(v, (0.00024419662286542966525 + 3.1413825703601317109j), tol=ATOL) assert ae(v.real, 0.00024419662286542966525, tol=PTOL) assert ae(v.imag, 3.1413825703601317109, tol=PTOL) v = fp.ei((-20.0 + 80.0j)) assert ae(v, (-2.3255552781051330088e-11 + 3.1415926535987396304j), tol=ATOL) assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, 3.1415926535987396304, tol=PTOL) v = fp.ei((-30.0 + 120.0j)) assert ae(v, (2.7068919097124652332e-16 + 3.1415926535897925337j), tol=ATOL) assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, 3.1415926535897925337, tol=PTOL) v = fp.ei((-40.0 + 160.0j)) assert ae(v, (1.1695597827678024687e-20 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-50.0 + 200.0j)) assert ae(v, (-9.0323746914410162531e-25 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-80.0 + 320.0j)) assert ae(v, (-3.4819106748728063576e-38 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) assert ae(v, (-20.880034622014215597 + 2.8966139905793444061j), tol=ATOL) assert ae(v.real, -20.880034622014215597, tol=PTOL) assert ae(v.imag, 2.8966139905793444061, tol=PTOL) v = fp.ei((-1.0 + 0.25j)) assert ae(v, (-0.19731063945004229095 + 3.0542266078154932748j), tol=ATOL) assert ae(v.real, -0.19731063945004229095, tol=PTOL) assert ae(v.imag, 3.0542266078154932748, tol=PTOL) v = fp.ei((-4.0 + 1.0j)) assert ae(v, (-0.0013106173980145506944 + 3.1381384055698581758j), tol=ATOL) assert ae(v.real, -0.0013106173980145506944, tol=PTOL) assert ae(v.imag, 3.1381384055698581758, tol=PTOL) v = fp.ei((-8.0 + 2.0j)) assert ae(v, (0.000022278049065270225945 + 3.1415634616493367169j), tol=ATOL) assert ae(v.real, 0.000022278049065270225945, tol=PTOL) assert ae(v.imag, 3.1415634616493367169, tol=PTOL) v = fp.ei((-20.0 + 5.0j)) assert ae(v, (-4.7711374515765346894e-11 + 3.1415926536726958909j), tol=ATOL) assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, 3.1415926536726958909, tol=PTOL) v = fp.ei((-80.0 + 20.0j)) assert ae(v, (-3.8353473865788235787e-38 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-120.0 + 30.0j)) assert ae(v, (-2.3836002337480334716e-55 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-160.0 + 40.0j)) assert ae(v, (1.6238022898654510661e-72 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-200.0 + 50.0j)) assert ae(v, (-6.6800061461666228487e-90 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei((-320.0 + 80.0j)) assert ae(v, (-4.2737871527778786157e-143 + 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, 3.1415926535897932385, tol=PTOL) v = fp.ei(-1.1641532182693481445e-10) assert ae(v, -22.296641293693077672, tol=ATOL) assert type(v) is float v = fp.ei(-0.25) assert ae(v, -1.0442826344437381945, tol=ATOL) assert type(v) is float v = fp.ei(-1.0) assert ae(v, -0.21938393439552027368, tol=ATOL) assert type(v) is float v = fp.ei(-2.0) assert ae(v, -0.048900510708061119567, tol=ATOL) assert type(v) is float v = fp.ei(-5.0) assert ae(v, -0.0011482955912753257973, tol=ATOL) assert type(v) is float v = fp.ei(-20.0) assert ae(v, -9.8355252906498816904e-11, tol=ATOL) assert type(v) is float v = fp.ei(-30.0) assert ae(v, -3.0215520106888125448e-15, tol=ATOL) assert type(v) is float v = fp.ei(-40.0) assert ae(v, -1.0367732614516569722e-19, tol=ATOL) assert type(v) is float v = fp.ei(-50.0) assert ae(v, -3.7832640295504590187e-24, tol=ATOL) assert type(v) is float v = fp.ei(-80.0) assert ae(v, -2.2285432586884729112e-37, tol=ATOL) assert type(v) is float v = fp.ei((-1.1641532182693481445e-10 + 0.0j)) assert ae(v, (-22.296641293693077672 + 0.0j), tol=ATOL) assert ae(v.real, -22.296641293693077672, tol=PTOL) assert v.imag == 0 v = fp.ei((-0.25 + 0.0j)) assert ae(v, (-1.0442826344437381945 + 0.0j), tol=ATOL) assert ae(v.real, -1.0442826344437381945, tol=PTOL) assert v.imag == 0 v = fp.ei((-1.0 + 0.0j)) assert ae(v, (-0.21938393439552027368 + 0.0j), tol=ATOL) assert ae(v.real, -0.21938393439552027368, tol=PTOL) assert v.imag == 0 v = fp.ei((-2.0 + 0.0j)) assert ae(v, (-0.048900510708061119567 + 0.0j), tol=ATOL) assert ae(v.real, -0.048900510708061119567, tol=PTOL) assert v.imag == 0 v = fp.ei((-5.0 + 0.0j)) assert ae(v, (-0.0011482955912753257973 + 0.0j), tol=ATOL) assert ae(v.real, -0.0011482955912753257973, tol=PTOL) assert v.imag == 0 v = fp.ei((-20.0 + 0.0j)) assert ae(v, (-9.8355252906498816904e-11 + 0.0j), tol=ATOL) assert ae(v.real, -9.8355252906498816904e-11, tol=PTOL) assert v.imag == 0 v = fp.ei((-30.0 + 0.0j)) assert ae(v, (-3.0215520106888125448e-15 + 0.0j), tol=ATOL) assert ae(v.real, -3.0215520106888125448e-15, tol=PTOL) assert v.imag == 0 v = fp.ei((-40.0 + 0.0j)) assert ae(v, (-1.0367732614516569722e-19 + 0.0j), tol=ATOL) assert ae(v.real, -1.0367732614516569722e-19, tol=PTOL) assert v.imag == 0 v = fp.ei((-50.0 + 0.0j)) assert ae(v, (-3.7832640295504590187e-24 + 0.0j), tol=ATOL) assert ae(v.real, -3.7832640295504590187e-24, tol=PTOL) assert v.imag == 0 v = fp.ei((-80.0 + 0.0j)) assert ae(v, (-2.2285432586884729112e-37 + 0.0j), tol=ATOL) assert ae(v.real, -2.2285432586884729112e-37, tol=PTOL) assert v.imag == 0 v = fp.ei((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-20.880034622014215597 - 2.8966139905793444061j), tol=ATOL) assert ae(v.real, -20.880034622014215597, tol=PTOL) assert ae(v.imag, -2.8966139905793444061, tol=PTOL) v = fp.ei((-1.0 - 0.25j)) assert ae(v, (-0.19731063945004229095 - 3.0542266078154932748j), tol=ATOL) assert ae(v.real, -0.19731063945004229095, tol=PTOL) assert ae(v.imag, -3.0542266078154932748, tol=PTOL) v = fp.ei((-4.0 - 1.0j)) assert ae(v, (-0.0013106173980145506944 - 3.1381384055698581758j), tol=ATOL) assert ae(v.real, -0.0013106173980145506944, tol=PTOL) assert ae(v.imag, -3.1381384055698581758, tol=PTOL) v = fp.ei((-8.0 - 2.0j)) assert ae(v, (0.000022278049065270225945 - 3.1415634616493367169j), tol=ATOL) assert ae(v.real, 0.000022278049065270225945, tol=PTOL) assert ae(v.imag, -3.1415634616493367169, tol=PTOL) v = fp.ei((-20.0 - 5.0j)) assert ae(v, (-4.7711374515765346894e-11 - 3.1415926536726958909j), tol=ATOL) assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) assert ae(v.imag, -3.1415926536726958909, tol=PTOL) v = fp.ei((-80.0 - 20.0j)) assert ae(v, (-3.8353473865788235787e-38 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-120.0 - 30.0j)) assert ae(v, (-2.3836002337480334716e-55 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-160.0 - 40.0j)) assert ae(v, (1.6238022898654510661e-72 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-200.0 - 50.0j)) assert ae(v, (-6.6800061461666228487e-90 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-320.0 - 80.0j)) assert ae(v, (-4.2737871527778786157e-143 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-21.950067703413105017 - 2.3561944903087602507j), tol=ATOL) assert ae(v.real, -21.950067703413105017, tol=PTOL) assert ae(v.imag, -2.3561944903087602507, tol=PTOL) v = fp.ei((-0.25 - 0.25j)) assert ae(v, (-0.71092525792923287894 - 2.5766745291767512913j), tol=ATOL) assert ae(v.real, -0.71092525792923287894, tol=PTOL) assert ae(v.imag, -2.5766745291767512913, tol=PTOL) v = fp.ei((-1.0 - 1.0j)) assert ae(v, (-0.00028162445198141832551 - 2.9622681185504342983j), tol=ATOL) assert ae(v.real, -0.00028162445198141832551, tol=PTOL) assert ae(v.imag, -2.9622681185504342983, tol=PTOL) v = fp.ei((-2.0 - 2.0j)) assert ae(v, (0.033767089606562004246 - 3.1229932394200426965j), tol=ATOL) assert ae(v.real, 0.033767089606562004246, tol=PTOL) assert ae(v.imag, -3.1229932394200426965, tol=PTOL) v = fp.ei((-5.0 - 5.0j)) assert ae(v, (-0.0007266506660356393891 - 3.1420636813914284609j), tol=ATOL) assert ae(v.real, -0.0007266506660356393891, tol=PTOL) assert ae(v.imag, -3.1420636813914284609, tol=PTOL) v = fp.ei((-20.0 - 20.0j)) assert ae(v, (2.3824537449367396579e-11 - 3.1415926535228233653j), tol=ATOL) assert ae(v.real, 2.3824537449367396579e-11, tol=PTOL) assert ae(v.imag, -3.1415926535228233653, tol=PTOL) v = fp.ei((-30.0 - 30.0j)) assert ae(v, (-1.7316045841744061617e-15 - 3.141592653589794545j), tol=ATOL) assert ae(v.real, -1.7316045841744061617e-15, tol=PTOL) assert ae(v.imag, -3.141592653589794545, tol=PTOL) v = fp.ei((-40.0 - 40.0j)) assert ae(v, (7.4001043002899232182e-20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 7.4001043002899232182e-20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-50.0 - 50.0j)) assert ae(v, (-2.3566128324644641219e-24 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -2.3566128324644641219e-24, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-80.0 - 80.0j)) assert ae(v, (-9.8279750572186526673e-38 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -9.8279750572186526673e-38, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621664969632 - 1.8157749903874220607j), tol=ATOL) assert ae(v.real, -20.880034621664969632, tol=PTOL) assert ae(v.imag, -1.8157749903874220607, tol=PTOL) v = fp.ei((-0.25 - 1.0j)) assert ae(v, (0.16868306393667788761 - 2.6557914649950505414j), tol=ATOL) assert ae(v.real, 0.16868306393667788761, tol=PTOL) assert ae(v.imag, -2.6557914649950505414, tol=PTOL) v = fp.ei((-1.0 - 4.0j)) assert ae(v, (-0.03373591813926547318 - 3.2151161058308770603j), tol=ATOL) assert ae(v.real, -0.03373591813926547318, tol=PTOL) assert ae(v.imag, -3.2151161058308770603, tol=PTOL) v = fp.ei((-2.0 - 8.0j)) assert ae(v, (0.015392833434733785143 - 3.1384179414340326969j), tol=ATOL) assert ae(v.real, 0.015392833434733785143, tol=PTOL) assert ae(v.imag, -3.1384179414340326969, tol=PTOL) v = fp.ei((-5.0 - 20.0j)) assert ae(v, (0.00024419662286542966525 - 3.1413825703601317109j), tol=ATOL) assert ae(v.real, 0.00024419662286542966525, tol=PTOL) assert ae(v.imag, -3.1413825703601317109, tol=PTOL) v = fp.ei((-20.0 - 80.0j)) assert ae(v, (-2.3255552781051330088e-11 - 3.1415926535987396304j), tol=ATOL) assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) assert ae(v.imag, -3.1415926535987396304, tol=PTOL) v = fp.ei((-30.0 - 120.0j)) assert ae(v, (2.7068919097124652332e-16 - 3.1415926535897925337j), tol=ATOL) assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) assert ae(v.imag, -3.1415926535897925337, tol=PTOL) v = fp.ei((-40.0 - 160.0j)) assert ae(v, (1.1695597827678024687e-20 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-50.0 - 200.0j)) assert ae(v, (-9.0323746914410162531e-25 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((-80.0 - 320.0j)) assert ae(v, (-3.4819106748728063576e-38 - 3.1415926535897932385j), tol=ATOL) assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) assert ae(v.imag, -3.1415926535897932385, tol=PTOL) v = fp.ei((0.0 - 1.1641532182693481445e-10j)) assert ae(v, (-22.29664129357666235 - 1.5707963269113119411j), tol=ATOL) assert ae(v.real, -22.29664129357666235, tol=PTOL) assert ae(v.imag, -1.5707963269113119411, tol=PTOL) v = fp.ei((0.0 - 0.25j)) assert ae(v, (-0.82466306258094565309 - 1.8199298971146537833j), tol=ATOL) assert ae(v.real, -0.82466306258094565309, tol=PTOL) assert ae(v.imag, -1.8199298971146537833, tol=PTOL) v = fp.ei((0.0 - 1.0j)) assert ae(v, (0.33740392290096813466 - 2.5168793971620796342j), tol=ATOL) assert ae(v.real, 0.33740392290096813466, tol=PTOL) assert ae(v.imag, -2.5168793971620796342, tol=PTOL) v = fp.ei((0.0 - 2.0j)) assert ae(v, (0.4229808287748649957 - 3.1762093035975914678j), tol=ATOL) assert ae(v.real, 0.4229808287748649957, tol=PTOL) assert ae(v.imag, -3.1762093035975914678, tol=PTOL) v = fp.ei((0.0 - 5.0j)) assert ae(v, (-0.19002974965664387862 - 3.1207275717395707565j), tol=ATOL) assert ae(v.real, -0.19002974965664387862, tol=PTOL) assert ae(v.imag, -3.1207275717395707565, tol=PTOL) v = fp.ei((0.0 - 20.0j)) assert ae(v, (0.04441982084535331654 - 3.1190380278383364594j), tol=ATOL) assert ae(v.real, 0.04441982084535331654, tol=PTOL) assert ae(v.imag, -3.1190380278383364594, tol=PTOL) v = fp.ei((0.0 - 30.0j)) assert ae(v, (-0.033032417282071143779 - 3.1375528668252477302j), tol=ATOL) assert ae(v.real, -0.033032417282071143779, tol=PTOL) assert ae(v.imag, -3.1375528668252477302, tol=PTOL) v = fp.ei((0.0 - 40.0j)) assert ae(v, (0.019020007896208766962 - 3.157781446149681126j), tol=ATOL) assert ae(v.real, 0.019020007896208766962, tol=PTOL) assert ae(v.imag, -3.157781446149681126, tol=PTOL) v = fp.ei((0.0 - 50.0j)) assert ae(v, (-0.0056283863241163054402 - 3.122413399280832514j), tol=ATOL) assert ae(v.real, -0.0056283863241163054402, tol=PTOL) assert ae(v.imag, -3.122413399280832514, tol=PTOL) v = fp.ei((0.0 - 80.0j)) assert ae(v, (-0.012402501155070958192 - 3.1431272137073839346j), tol=ATOL) assert ae(v.real, -0.012402501155070958192, tol=PTOL) assert ae(v.imag, -3.1431272137073839346, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) assert ae(v, (-20.880034621432138988 - 1.3258176641336937524j), tol=ATOL) assert ae(v.real, -20.880034621432138988, tol=PTOL) assert ae(v.imag, -1.3258176641336937524, tol=PTOL) v = fp.ei((0.25 - 1.0j)) assert ae(v, (0.59066621214766308594 - 2.3968481059377428687j), tol=ATOL) assert ae(v.real, 0.59066621214766308594, tol=PTOL) assert ae(v.imag, -2.3968481059377428687, tol=PTOL) v = fp.ei((1.0 - 4.0j)) assert ae(v, (-0.49739047283060471093 - 3.5570287076301818702j), tol=ATOL) assert ae(v.real, -0.49739047283060471093, tol=PTOL) assert ae(v.imag, -3.5570287076301818702, tol=PTOL) v = fp.ei((2.0 - 8.0j)) assert ae(v, (0.8705211147733730969 - 3.3825859385758486351j), tol=ATOL) assert ae(v.real, 0.8705211147733730969, tol=PTOL) assert ae(v.imag, -3.3825859385758486351, tol=PTOL) v = fp.ei((5.0 - 20.0j)) assert ae(v, (7.0789514293925893007 - 1.5313749363937141849j), tol=ATOL) assert ae(v.real, 7.0789514293925893007, tol=PTOL) assert ae(v.imag, -1.5313749363937141849, tol=PTOL) v = fp.ei((20.0 - 80.0j)) assert ae(v, (-5855431.4907298084434 + 720917.79156143806727j), tol=ATOL) assert ae(v.real, -5855431.4907298084434, tol=PTOL) assert ae(v.imag, 720917.79156143806727, tol=PTOL) v = fp.ei((30.0 - 120.0j)) assert ae(v, (65402491644.703470747 + 56697658396.51586764j), tol=ATOL) assert ae(v.real, 65402491644.703470747, tol=PTOL) assert ae(v.imag, 56697658396.51586764, tol=PTOL) v = fp.ei((40.0 - 160.0j)) assert ae(v, (-25504929379604.776769 - 1429035198630576.3879j), tol=ATOL) assert ae(v.real, -25504929379604.776769, tol=PTOL) assert ae(v.imag, -1429035198630576.3879, tol=PTOL) v = fp.ei((50.0 - 200.0j)) assert ae(v, (-18437746526988116954.0 + 17146362239046152342.0j), tol=ATOL) assert ae(v.real, -18437746526988116954.0, tol=PTOL) assert ae(v.imag, 17146362239046152342.0, tol=PTOL) v = fp.ei((80.0 - 320.0j)) assert ae(v, (-3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) v = fp.ei((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-21.950067703180274374 - 0.78539816351386363145j), tol=ATOL) assert ae(v.real, -21.950067703180274374, tol=PTOL) assert ae(v.imag, -0.78539816351386363145, tol=PTOL) v = fp.ei((0.25 - 0.25j)) assert ae(v, (-0.21441047326710323254 - 1.0683772981589995996j), tol=ATOL) assert ae(v.real, -0.21441047326710323254, tol=PTOL) assert ae(v.imag, -1.0683772981589995996, tol=PTOL) v = fp.ei((1.0 - 1.0j)) assert ae(v, (1.7646259855638540684 - 2.3877698515105224193j), tol=ATOL) assert ae(v.real, 1.7646259855638540684, tol=PTOL) assert ae(v.imag, -2.3877698515105224193, tol=PTOL) v = fp.ei((2.0 - 2.0j)) assert ae(v, (1.8920781621855474089 - 5.3169624378326579621j), tol=ATOL) assert ae(v.real, 1.8920781621855474089, tol=PTOL) assert ae(v.imag, -5.3169624378326579621, tol=PTOL) v = fp.ei((5.0 - 5.0j)) assert ae(v, (-13.470936071475245856 + 15.322492395731230968j), tol=ATOL) assert ae(v.real, -13.470936071475245856, tol=PTOL) assert ae(v.imag, 15.322492395731230968, tol=PTOL) v = fp.ei((20.0 - 20.0j)) assert ae(v, (16589317.398788971896 - 5831705.4712368307104j), tol=ATOL) assert ae(v.real, 16589317.398788971896, tol=PTOL) assert ae(v.imag, -5831705.4712368307104, tol=PTOL) v = fp.ei((30.0 - 30.0j)) assert ae(v, (-154596484273.69322527 + 204179357834.2723043j), tol=ATOL) assert ae(v.real, -154596484273.69322527, tol=PTOL) assert ae(v.imag, 204179357834.2723043, tol=PTOL) v = fp.ei((40.0 - 40.0j)) assert ae(v, (287512180321448.45408 - 4203502407932318.1156j), tol=ATOL) assert ae(v.real, 287512180321448.45408, tol=PTOL) assert ae(v.imag, -4203502407932318.1156, tol=PTOL) v = fp.ei((50.0 - 50.0j)) assert ae(v, (36128528616649268826.0 + 64648801861338741960.0j), tol=ATOL) assert ae(v.real, 36128528616649268826.0, tol=PTOL) assert ae(v.imag, 64648801861338741960.0, tol=PTOL) v = fp.ei((80.0 - 80.0j)) assert ae(v, (-3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) v = fp.ei((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) assert ae(v, (-20.880034621082893023 - 0.24497866324327947603j), tol=ATOL) assert ae(v.real, -20.880034621082893023, tol=PTOL) assert ae(v.imag, -0.24497866324327947603, tol=PTOL) v = fp.ei((1.0 - 0.25j)) assert ae(v, (1.8942716983721074932 - 0.67268237088273915854j), tol=ATOL) assert ae(v.real, 1.8942716983721074932, tol=PTOL) assert ae(v.imag, -0.67268237088273915854, tol=PTOL) v = fp.ei((4.0 - 1.0j)) assert ae(v, (14.806699492675420438 - 12.280015176673582616j), tol=ATOL) assert ae(v.real, 14.806699492675420438, tol=PTOL) assert ae(v.imag, -12.280015176673582616, tol=PTOL) v = fp.ei((8.0 - 2.0j)) assert ae(v, (-54.633252667426386294 - 416.34477429173650012j), tol=ATOL) assert ae(v.real, -54.633252667426386294, tol=PTOL) assert ae(v.imag, -416.34477429173650012, tol=PTOL) v = fp.ei((20.0 - 5.0j)) assert ae(v, (711836.97165402624643 + 24745247.798103247366j), tol=ATOL) assert ae(v.real, 711836.97165402624643, tol=PTOL) assert ae(v.imag, 24745247.798103247366, tol=PTOL) v = fp.ei((80.0 - 20.0j)) assert ae(v, (4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) v = fp.ei((120.0 - 30.0j)) assert ae(v, (-9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) v = fp.ei((160.0 - 40.0j)) assert ae(v, (-8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) v = fp.ei((200.0 - 50.0j)) assert ae(v, (3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) v = fp.ei((320.0 - 80.0j)) assert ae(v, (-9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/extratest_zeta.py0000644000175000017500000000210312014170666027143 0ustar georgeskgeorgeskfrom mpmath import zetazero from timeit import default_timer as clock def test_zetazero(): cases = [\ (399999999, 156762524.6750591511), (241389216, 97490234.2276711795), (526196239, 202950727.691229534), (542964976, 209039046.578535272), (1048449112, 388858885.231056486), (1048449113, 388858885.384337406), (1048449114, 388858886.002285122), (1048449115, 388858886.00239369), (1048449116, 388858886.690745053) ] for n, v in cases: print(n, v) t1 = clock() ok = zetazero(n).ae(complex(0.5,v)) t2 = clock() print("ok =", ok, ("(time = %s)" % round(t2-t1,3))) print("Now computing two huge zeros (this may take hours)") print("Computing zetazero(8637740722917)") ok = zetazero(8637740722917).ae(complex(0.5,2124447368584.39296466152)) print("ok =", ok) ok = zetazero(8637740722918).ae(complex(0.5,2124447368584.39298170604)) print("ok =", ok) if __name__ == "__main__": try: import psyco psyco.full() except ImportError: pass test_zetazero() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_functions.py0000644000175000017500000007371212014170666027162 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import * from sympy.mpmath import * import random import time import math import cmath def mpc_ae(a, b, eps=eps): res = True res = res and a.real.ae(b.real, eps) res = res and a.imag.ae(b.imag, eps) return res #---------------------------------------------------------------------------- # Constants and functions # tpi = "3.1415926535897932384626433832795028841971693993751058209749445923078\ 1640628620899862803482534211706798" te = "2.71828182845904523536028747135266249775724709369995957496696762772407\ 663035354759457138217852516642743" tdegree = "0.017453292519943295769236907684886127134428718885417254560971914\ 4017100911460344944368224156963450948221" teuler = "0.5772156649015328606065120900824024310421593359399235988057672348\ 84867726777664670936947063291746749516" tln2 = "0.693147180559945309417232121458176568075500134360255254120680009493\ 393621969694715605863326996418687542" tln10 = "2.30258509299404568401799145468436420760110148862877297603332790096\ 757260967735248023599720508959829834" tcatalan = "0.91596559417721901505460351493238411077414937428167213426649811\ 9621763019776254769479356512926115106249" tkhinchin = "2.6854520010653064453097148354817956938203822939944629530511523\ 4555721885953715200280114117493184769800" tglaisher = "1.2824271291006226368753425688697917277676889273250011920637400\ 2174040630885882646112973649195820237439420646" tapery = "1.2020569031595942853997381615114499907649862923404988817922715553\ 4183820578631309018645587360933525815" tphi = "1.618033988749894848204586834365638117720309179805762862135448622705\ 26046281890244970720720418939113748475" tmertens = "0.26149721284764278375542683860869585905156664826119920619206421\ 3924924510897368209714142631434246651052" ttwinprime = "0.660161815846869573927812110014555778432623360284733413319448\ 423335405642304495277143760031413839867912" def test_constants(): for prec in [3, 7, 10, 15, 20, 37, 80, 100, 29]: mp.dps = prec assert pi == mpf(tpi) assert e == mpf(te) assert degree == mpf(tdegree) assert euler == mpf(teuler) assert ln2 == mpf(tln2) assert ln10 == mpf(tln10) assert catalan == mpf(tcatalan) assert khinchin == mpf(tkhinchin) assert glaisher == mpf(tglaisher) assert phi == mpf(tphi) if prec < 50: assert mertens == mpf(tmertens) assert twinprime == mpf(ttwinprime) mp.dps = 15 assert pi >= -1 assert pi > 2 assert pi > 3 assert pi < 4 def test_exact_sqrts(): for i in range(20000): assert sqrt(mpf(i*i)) == i random.seed(1) for prec in [100, 300, 1000, 10000]: mp.dps = prec for i in range(20): A = random.randint(10**(prec//2-2), 10**(prec//2-1)) assert sqrt(mpf(A*A)) == A mp.dps = 15 for i in range(100): for a in [1, 8, 25, 112307]: assert sqrt(mpf((a*a, 2*i))) == mpf((a, i)) assert sqrt(mpf((a*a, -2*i))) == mpf((a, -i)) def test_sqrt_rounding(): for i in [2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]: i = from_int(i) for dps in [7, 15, 83, 106, 2000]: mp.dps = dps a = mpf_pow_int(mpf_sqrt(i, mp.prec, round_down), 2, mp.prec, round_down) b = mpf_pow_int(mpf_sqrt(i, mp.prec, round_up), 2, mp.prec, round_up) assert mpf_lt(a, i) assert mpf_gt(b, i) random.seed(1234) prec = 100 for rnd in [round_down, round_nearest, round_ceiling]: for i in range(100): a = mpf_rand(prec) b = mpf_mul(a, a) assert mpf_sqrt(b, prec, rnd) == a # Test some extreme cases mp.dps = 100 a = mpf(9) + 1e-90 b = mpf(9) - 1e-90 mp.dps = 15 assert sqrt(a, rounding='d') == 3 assert sqrt(a, rounding='n') == 3 assert sqrt(a, rounding='u') > 3 assert sqrt(b, rounding='d') < 3 assert sqrt(b, rounding='n') == 3 assert sqrt(b, rounding='u') == 3 # A worst case, from the MPFR test suite assert sqrt(mpf('7.0503726185518891')) == mpf('2.655253776675949') def test_float_sqrt(): mp.dps = 15 # These should round identically for x in [0, 1e-7, 0.1, 0.5, 1, 2, 3, 4, 5, 0.333, 76.19]: assert sqrt(mpf(x)) == float(x)**0.5 assert sqrt(-1) == 1j assert sqrt(-2).ae(cmath.sqrt(-2)) assert sqrt(-3).ae(cmath.sqrt(-3)) assert sqrt(-100).ae(cmath.sqrt(-100)) assert sqrt(1j).ae(cmath.sqrt(1j)) assert sqrt(-1j).ae(cmath.sqrt(-1j)) assert sqrt(math.pi + math.e*1j).ae(cmath.sqrt(math.pi + math.e*1j)) assert sqrt(math.pi - math.e*1j).ae(cmath.sqrt(math.pi - math.e*1j)) def test_hypot(): assert hypot(0, 0) == 0 assert hypot(0, 0.33) == mpf(0.33) assert hypot(0.33, 0) == mpf(0.33) assert hypot(-0.33, 0) == mpf(0.33) assert hypot(3, 4) == mpf(5) def test_exact_cbrt(): for i in range(0, 20000, 200): assert cbrt(mpf(i*i*i)) == i random.seed(1) for prec in [100, 300, 1000, 10000]: mp.dps = prec A = random.randint(10**(prec//2-2), 10**(prec//2-1)) assert cbrt(mpf(A*A*A)) == A mp.dps = 15 def test_exp(): assert exp(0) == 1 assert exp(10000).ae(mpf('8.8068182256629215873e4342')) assert exp(-10000).ae(mpf('1.1354838653147360985e-4343')) a = exp(mpf((1, 8198646019315405, -53, 53))) assert(a.bc == bitcount(a.man)) mp.prec = 67 a = exp(mpf((1, 1781864658064754565, -60, 61))) assert(a.bc == bitcount(a.man)) mp.prec = 53 assert exp(ln2 * 10).ae(1024) assert exp(2+2j).ae(cmath.exp(2+2j)) def test_issue_33(): mp.dps = 512 a = exp(-1) b = exp(1) mp.dps = 15 assert (+a).ae(0.36787944117144233) assert (+b).ae(2.7182818284590451) def test_log(): mp.dps = 15 assert log(1) == 0 for x in [0.5, 1.5, 2.0, 3.0, 100, 10**50, 1e-50]: assert log(x).ae(math.log(x)) assert log(x, x) == 1 assert log(1024, 2) == 10 assert log(10**1234, 10) == 1234 assert log(2+2j).ae(cmath.log(2+2j)) # Accuracy near 1 assert (log(0.6+0.8j).real*10**17).ae(2.2204460492503131) assert (log(0.6-0.8j).real*10**17).ae(2.2204460492503131) assert (log(0.8-0.6j).real*10**17).ae(2.2204460492503131) assert (log(1+1e-8j).real*10**16).ae(0.5) assert (log(1-1e-8j).real*10**16).ae(0.5) assert (log(-1+1e-8j).real*10**16).ae(0.5) assert (log(-1-1e-8j).real*10**16).ae(0.5) assert (log(1j+1e-8).real*10**16).ae(0.5) assert (log(1j-1e-8).real*10**16).ae(0.5) assert (log(-1j+1e-8).real*10**16).ae(0.5) assert (log(-1j-1e-8).real*10**16).ae(0.5) assert (log(1+1e-40j).real*10**80).ae(0.5) assert (log(1j+1e-40).real*10**80).ae(0.5) # Huge assert log(ldexp(1.234,10**20)).ae(log(2)*1e20) assert log(ldexp(1.234,10**200)).ae(log(2)*1e200) # Some special values assert log(mpc(0,0)) == mpc(-inf,0) assert isnan(log(mpc(nan,0)).real) assert isnan(log(mpc(nan,0)).imag) assert isnan(log(mpc(0,nan)).real) assert isnan(log(mpc(0,nan)).imag) assert isnan(log(mpc(nan,1)).real) assert isnan(log(mpc(nan,1)).imag) assert isnan(log(mpc(1,nan)).real) assert isnan(log(mpc(1,nan)).imag) def test_trig_hyperb_basic(): for x in (list(range(100)) + list(range(-100,0))): t = x / 4.1 assert cos(mpf(t)).ae(math.cos(t)) assert sin(mpf(t)).ae(math.sin(t)) assert tan(mpf(t)).ae(math.tan(t)) assert cosh(mpf(t)).ae(math.cosh(t)) assert sinh(mpf(t)).ae(math.sinh(t)) assert tanh(mpf(t)).ae(math.tanh(t)) assert sin(1+1j).ae(cmath.sin(1+1j)) assert sin(-4-3.6j).ae(cmath.sin(-4-3.6j)) assert cos(1+1j).ae(cmath.cos(1+1j)) assert cos(-4-3.6j).ae(cmath.cos(-4-3.6j)) def test_degrees(): assert cos(0*degree) == 1 assert cos(90*degree).ae(0) assert cos(180*degree).ae(-1) assert cos(270*degree).ae(0) assert cos(360*degree).ae(1) assert sin(0*degree) == 0 assert sin(90*degree).ae(1) assert sin(180*degree).ae(0) assert sin(270*degree).ae(-1) assert sin(360*degree).ae(0) def random_complexes(N): random.seed(1) a = [] for i in range(N): x1 = random.uniform(-10, 10) y1 = random.uniform(-10, 10) x2 = random.uniform(-10, 10) y2 = random.uniform(-10, 10) z1 = complex(x1, y1) z2 = complex(x2, y2) a.append((z1, z2)) return a def test_complex_powers(): for dps in [15, 30, 100]: # Check accuracy for complex square root mp.dps = dps a = mpc(1j)**0.5 assert a.real == a.imag == mpf(2)**0.5 / 2 mp.dps = 15 random.seed(1) for (z1, z2) in random_complexes(100): assert (mpc(z1)**mpc(z2)).ae(z1**z2, 1e-12) assert (e**(-pi*1j)).ae(-1) mp.dps = 50 assert (e**(-pi*1j)).ae(-1) mp.dps = 15 def test_complex_sqrt_accuracy(): def test_mpc_sqrt(lst): for a, b in lst: z = mpc(a + j*b) assert mpc_ae(sqrt(z*z), z) z = mpc(-a + j*b) assert mpc_ae(sqrt(z*z), -z) z = mpc(a - j*b) assert mpc_ae(sqrt(z*z), z) z = mpc(-a - j*b) assert mpc_ae(sqrt(z*z), -z) random.seed(2) N = 10 mp.dps = 30 dps = mp.dps test_mpc_sqrt([(random.uniform(0, 10),random.uniform(0, 10)) for i in range(N)]) test_mpc_sqrt([(i + 0.1, (i + 0.2)*10**i) for i in range(N)]) mp.dps = 15 def test_atan(): mp.dps = 15 assert atan(-2.3).ae(math.atan(-2.3)) assert atan(1e-50) == 1e-50 assert atan(1e50).ae(pi/2) assert atan(-1e-50) == -1e-50 assert atan(-1e50).ae(-pi/2) assert atan(10**1000).ae(pi/2) for dps in [25, 70, 100, 300, 1000]: mp.dps = dps assert (4*atan(1)).ae(pi) mp.dps = 15 pi2 = pi/2 assert atan(mpc(inf,-1)).ae(pi2) assert atan(mpc(inf,0)).ae(pi2) assert atan(mpc(inf,1)).ae(pi2) assert atan(mpc(1,inf)).ae(pi2) assert atan(mpc(0,inf)).ae(pi2) assert atan(mpc(-1,inf)).ae(-pi2) assert atan(mpc(-inf,1)).ae(-pi2) assert atan(mpc(-inf,0)).ae(-pi2) assert atan(mpc(-inf,-1)).ae(-pi2) assert atan(mpc(-1,-inf)).ae(-pi2) assert atan(mpc(0,-inf)).ae(-pi2) assert atan(mpc(1,-inf)).ae(pi2) def test_atan2(): mp.dps = 15 assert atan2(1,1).ae(pi/4) assert atan2(1,-1).ae(3*pi/4) assert atan2(-1,-1).ae(-3*pi/4) assert atan2(-1,1).ae(-pi/4) assert atan2(-1,0).ae(-pi/2) assert atan2(1,0).ae(pi/2) assert atan2(0,0) == 0 assert atan2(inf,0).ae(pi/2) assert atan2(-inf,0).ae(-pi/2) assert isnan(atan2(inf,inf)) assert isnan(atan2(-inf,inf)) assert isnan(atan2(inf,-inf)) assert isnan(atan2(3,nan)) assert isnan(atan2(nan,3)) assert isnan(atan2(0,nan)) assert isnan(atan2(nan,0)) assert atan2(0,inf) == 0 assert atan2(0,-inf).ae(pi) assert atan2(10,inf) == 0 assert atan2(-10,inf) == 0 assert atan2(-10,-inf).ae(-pi) assert atan2(10,-inf).ae(pi) assert atan2(inf,10).ae(pi/2) assert atan2(inf,-10).ae(pi/2) assert atan2(-inf,10).ae(-pi/2) assert atan2(-inf,-10).ae(-pi/2) def test_areal_inverses(): assert asin(mpf(0)) == 0 assert asinh(mpf(0)) == 0 assert acosh(mpf(1)) == 0 assert isinstance(asin(mpf(0.5)), mpf) assert isinstance(asin(mpf(2.0)), mpc) assert isinstance(acos(mpf(0.5)), mpf) assert isinstance(acos(mpf(2.0)), mpc) assert isinstance(atanh(mpf(0.1)), mpf) assert isinstance(atanh(mpf(1.1)), mpc) random.seed(1) for i in range(50): x = random.uniform(0, 1) assert asin(mpf(x)).ae(math.asin(x)) assert acos(mpf(x)).ae(math.acos(x)) x = random.uniform(-10, 10) assert asinh(mpf(x)).ae(cmath.asinh(x).real) assert isinstance(asinh(mpf(x)), mpf) x = random.uniform(1, 10) assert acosh(mpf(x)).ae(cmath.acosh(x).real) assert isinstance(acosh(mpf(x)), mpf) x = random.uniform(-10, 0.999) assert isinstance(acosh(mpf(x)), mpc) x = random.uniform(-1, 1) assert atanh(mpf(x)).ae(cmath.atanh(x).real) assert isinstance(atanh(mpf(x)), mpf) dps = mp.dps mp.dps = 300 assert isinstance(asin(0.5), mpf) mp.dps = 1000 assert asin(1).ae(pi/2) assert asin(-1).ae(-pi/2) mp.dps = dps def test_invhyperb_inaccuracy(): mp.dps = 15 assert (asinh(1e-5)*10**5).ae(0.99999999998333333) assert (asinh(1e-10)*10**10).ae(1) assert (asinh(1e-50)*10**50).ae(1) assert (asinh(-1e-5)*10**5).ae(-0.99999999998333333) assert (asinh(-1e-10)*10**10).ae(-1) assert (asinh(-1e-50)*10**50).ae(-1) assert asinh(10**20).ae(46.744849040440862) assert asinh(-10**20).ae(-46.744849040440862) assert (tanh(1e-10)*10**10).ae(1) assert (tanh(-1e-10)*10**10).ae(-1) assert (atanh(1e-10)*10**10).ae(1) assert (atanh(-1e-10)*10**10).ae(-1) def test_complex_functions(): for x in (list(range(10)) + list(range(-10,0))): for y in (list(range(10)) + list(range(-10,0))): z = complex(x, y)/4.3 + 0.01j assert exp(mpc(z)).ae(cmath.exp(z)) assert log(mpc(z)).ae(cmath.log(z)) assert cos(mpc(z)).ae(cmath.cos(z)) assert sin(mpc(z)).ae(cmath.sin(z)) assert tan(mpc(z)).ae(cmath.tan(z)) assert sinh(mpc(z)).ae(cmath.sinh(z)) assert cosh(mpc(z)).ae(cmath.cosh(z)) assert tanh(mpc(z)).ae(cmath.tanh(z)) def test_complex_inverse_functions(): for (z1, z2) in random_complexes(30): # apparently cmath uses a different branch, so we # can't use it for comparison assert sinh(asinh(z1)).ae(z1) # assert acosh(z1).ae(cmath.acosh(z1)) assert atanh(z1).ae(cmath.atanh(z1)) assert atan(z1).ae(cmath.atan(z1)) # the reason we set a big eps here is that the cmath # functions are inaccurate assert asin(z1).ae(cmath.asin(z1), rel_eps=1e-12) assert acos(z1).ae(cmath.acos(z1), rel_eps=1e-12) one = mpf(1) for i in range(-9, 10, 3): for k in range(-9, 10, 3): a = 0.9*j*10**k + 0.8*one*10**i b = cos(acos(a)) assert b.ae(a) b = sin(asin(a)) assert b.ae(a) one = mpf(1) err = 2*10**-15 for i in range(-9, 9, 3): for k in range(-9, 9, 3): a = -0.9*10**k + j*0.8*one*10**i b = cosh(acosh(a)) assert b.ae(a, err) b = sinh(asinh(a)) assert b.ae(a, err) def test_reciprocal_functions(): assert sec(3).ae(-1.01010866590799375) assert csc(3).ae(7.08616739573718592) assert cot(3).ae(-7.01525255143453347) assert sech(3).ae(0.0993279274194332078) assert csch(3).ae(0.0998215696688227329) assert coth(3).ae(1.00496982331368917) assert asec(3).ae(1.23095941734077468) assert acsc(3).ae(0.339836909454121937) assert acot(3).ae(0.321750554396642193) assert asech(0.5).ae(1.31695789692481671) assert acsch(3).ae(0.327450150237258443) assert acoth(3).ae(0.346573590279972655) def test_ldexp(): mp.dps = 15 assert ldexp(mpf(2.5), 0) == 2.5 assert ldexp(mpf(2.5), -1) == 1.25 assert ldexp(mpf(2.5), 2) == 10 assert ldexp(mpf('inf'), 3) == mpf('inf') def test_frexp(): mp.dps = 15 assert frexp(0) == (0.0, 0) assert frexp(9) == (0.5625, 4) assert frexp(1) == (0.5, 1) assert frexp(0.2) == (0.8, -2) assert frexp(1000) == (0.9765625, 10) def test_aliases(): assert ln(7) == log(7) assert log10(3.75) == log(3.75,10) assert degrees(5.6) == 5.6 / degree assert radians(5.6) == 5.6 * degree assert power(-1,0.5) == j assert fmod(25,7) == 4.0 and isinstance(fmod(25,7), mpf) def test_arg_sign(): assert arg(3) == 0 assert arg(-3).ae(pi) assert arg(j).ae(pi/2) assert arg(-j).ae(-pi/2) assert arg(0) == 0 assert isnan(atan2(3,nan)) assert isnan(atan2(nan,3)) assert isnan(atan2(0,nan)) assert isnan(atan2(nan,0)) assert isnan(atan2(nan,nan)) assert arg(inf) == 0 assert arg(-inf).ae(pi) assert isnan(arg(nan)) #assert arg(inf*j).ae(pi/2) assert sign(0) == 0 assert sign(3) == 1 assert sign(-3) == -1 assert sign(inf) == 1 assert sign(-inf) == -1 assert isnan(sign(nan)) assert sign(j) == j assert sign(-3*j) == -j assert sign(1+j).ae((1+j)/sqrt(2)) def test_misc_bugs(): # test that this doesn't raise an exception mp.dps = 1000 log(1302) mp.dps = 15 def test_arange(): assert arange(10) == [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0'), mpf('5.0'), mpf('6.0'), mpf('7.0'), mpf('8.0'), mpf('9.0')] assert arange(-5, 5) == [mpf('-5.0'), mpf('-4.0'), mpf('-3.0'), mpf('-2.0'), mpf('-1.0'), mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] assert arange(0, 1, 0.1) == [mpf('0.0'), mpf('0.10000000000000001'), mpf('0.20000000000000001'), mpf('0.30000000000000004'), mpf('0.40000000000000002'), mpf('0.5'), mpf('0.60000000000000009'), mpf('0.70000000000000007'), mpf('0.80000000000000004'), mpf('0.90000000000000002')] assert arange(17, -9, -3) == [mpf('17.0'), mpf('14.0'), mpf('11.0'), mpf('8.0'), mpf('5.0'), mpf('2.0'), mpf('-1.0'), mpf('-4.0'), mpf('-7.0')] assert arange(0.2, 0.1, -0.1) == [mpf('0.20000000000000001')] assert arange(0) == [] assert arange(1000, -1) == [] assert arange(-1.23, 3.21, -0.0000001) == [] def test_linspace(): assert linspace(2, 9, 7) == [mpf('2.0'), mpf('3.166666666666667'), mpf('4.3333333333333339'), mpf('5.5'), mpf('6.666666666666667'), mpf('7.8333333333333339'), mpf('9.0')] assert linspace(2, 9, 7, endpoint=0) == [mpf('2.0'), mpf('3.0'), mpf('4.0'), mpf('5.0'), mpf('6.0'), mpf('7.0'), mpf('8.0')] assert linspace(2, 7, 1) == [mpf(2)] def test_float_cbrt(): mp.dps = 30 for a in arange(0,10,0.1): assert cbrt(a*a*a).ae(a, eps) assert cbrt(-1).ae(0.5 + j*sqrt(3)/2) one_third = mpf(1)/3 for a in arange(0,10,2.7) + [0.1 + 10**5]: a = mpc(a + 1.1j) r1 = cbrt(a) mp.dps += 10 r2 = pow(a, one_third) mp.dps -= 10 assert r1.ae(r2, eps) mp.dps = 100 for n in range(100, 301, 100): w = 10**n + j*10**-3 z = w*w*w r = cbrt(z) assert mpc_ae(r, w, eps) mp.dps = 15 def test_root(): mp.dps = 30 random.seed(1) a = random.randint(0, 10000) p = a*a*a r = nthroot(mpf(p), 3) assert r == a for n in range(4, 10): p = p*a assert nthroot(mpf(p), n) == a mp.dps = 40 for n in range(10, 5000, 100): for a in [random.random()*10000, random.random()*10**100]: r = nthroot(a, n) r1 = pow(a, mpf(1)/n) assert r.ae(r1) r = nthroot(a, -n) r1 = pow(a, -mpf(1)/n) assert r.ae(r1) # XXX: this is broken right now # tests for nthroot rounding for rnd in ['nearest', 'up', 'down']: mp.rounding = rnd for n in [-5, -3, 3, 5]: prec = 50 for i in range(10): mp.prec = prec a = rand() mp.prec = 2*prec b = a**n mp.prec = prec r = nthroot(b, n) assert r == a mp.dps = 30 for n in range(3, 21): a = (random.random() + j*random.random()) assert nthroot(a, n).ae(pow(a, mpf(1)/n)) assert mpc_ae(nthroot(a, n), pow(a, mpf(1)/n)) a = (random.random()*10**100 + j*random.random()) r = nthroot(a, n) mp.dps += 4 r1 = pow(a, mpf(1)/n) mp.dps -= 4 assert r.ae(r1) assert mpc_ae(r, r1, eps) r = nthroot(a, -n) mp.dps += 4 r1 = pow(a, -mpf(1)/n) mp.dps -= 4 assert r.ae(r1) assert mpc_ae(r, r1, eps) mp.dps = 15 assert nthroot(4, 1) == 4 assert nthroot(4, 0) == 1 assert nthroot(4, -1) == 0.25 assert nthroot(inf, 1) == inf assert nthroot(inf, 2) == inf assert nthroot(inf, 3) == inf assert nthroot(inf, -1) == 0 assert nthroot(inf, -2) == 0 assert nthroot(inf, -3) == 0 assert nthroot(j, 1) == j assert nthroot(j, 0) == 1 assert nthroot(j, -1) == -j assert isnan(nthroot(nan, 1)) assert isnan(nthroot(nan, 0)) assert isnan(nthroot(nan, -1)) assert isnan(nthroot(inf, 0)) assert root(2,3) == nthroot(2,3) assert root(16,4,0) == 2 assert root(16,4,1) == 2j assert root(16,4,2) == -2 assert root(16,4,3) == -2j assert root(16,4,4) == 2 assert root(-125,3,1) == -5 def test_issue_96(): for dps in [20, 80]: mp.dps = dps r = nthroot(mpf('-1e-20'), 4) assert r.ae(mpf(10)**(-5) * (1 + j) * mpf(2)**(-0.5)) mp.dps = 80 assert nthroot('-1e-3', 4).ae(mpf(10)**(-3./4) * (1 + j)/sqrt(2)) assert nthroot('-1e-6', 4).ae((1 + j)/(10 * sqrt(20))) # Check that this doesn't take eternity to compute mp.dps = 20 assert nthroot('-1e100000000', 4).ae((1+j)*mpf('1e25000000')/sqrt(2)) mp.dps = 15 def test_mpcfun_real_imag(): mp.dps = 15 x = mpf(0.3) y = mpf(0.4) assert exp(mpc(x,0)) == exp(x) assert exp(mpc(0,y)) == mpc(cos(y),sin(y)) assert cos(mpc(x,0)) == cos(x) assert sin(mpc(x,0)) == sin(x) assert cos(mpc(0,y)) == cosh(y) assert sin(mpc(0,y)) == mpc(0,sinh(y)) assert cospi(mpc(x,0)) == cospi(x) assert sinpi(mpc(x,0)) == sinpi(x) assert cospi(mpc(0,y)).ae(cosh(pi*y)) assert sinpi(mpc(0,y)).ae(mpc(0,sinh(pi*y))) c, s = cospi_sinpi(mpc(x,0)) assert c == cospi(x) assert s == sinpi(x) c, s = cospi_sinpi(mpc(0,y)) assert c.ae(cosh(pi*y)) assert s.ae(mpc(0,sinh(pi*y))) c, s = cos_sin(mpc(x,0)) assert c == cos(x) assert s == sin(x) c, s = cos_sin(mpc(0,y)) assert c == cosh(y) assert s == mpc(0,sinh(y)) def test_perturbation_rounding(): mp.dps = 100 a = pi/10**50 b = -pi/10**50 c = 1 + a d = 1 + b mp.dps = 15 assert exp(a) == 1 assert exp(a, rounding='c') > 1 assert exp(b, rounding='c') == 1 assert exp(a, rounding='f') == 1 assert exp(b, rounding='f') < 1 assert cos(a) == 1 assert cos(a, rounding='c') == 1 assert cos(b, rounding='c') == 1 assert cos(a, rounding='f') < 1 assert cos(b, rounding='f') < 1 for f in [sin, atan, asinh, tanh]: assert f(a) == +a assert f(a, rounding='c') > a assert f(a, rounding='f') < a assert f(b) == +b assert f(b, rounding='c') > b assert f(b, rounding='f') < b for f in [asin, tan, sinh, atanh]: assert f(a) == +a assert f(b) == +b assert f(a, rounding='c') > a assert f(b, rounding='c') > b assert f(a, rounding='f') < a assert f(b, rounding='f') < b assert ln(c) == +a assert ln(d) == +b assert ln(c, rounding='c') > a assert ln(c, rounding='f') < a assert ln(d, rounding='c') > b assert ln(d, rounding='f') < b assert cosh(a) == 1 assert cosh(b) == 1 assert cosh(a, rounding='c') > 1 assert cosh(b, rounding='c') > 1 assert cosh(a, rounding='f') == 1 assert cosh(b, rounding='f') == 1 def test_integer_parts(): assert floor(3.2) == 3 assert ceil(3.2) == 4 assert floor(3.2+5j) == 3+5j assert ceil(3.2+5j) == 4+5j def test_complex_parts(): assert fabs('3') == 3 assert fabs(3+4j) == 5 assert re(3) == 3 assert re(1+4j) == 1 assert im(3) == 0 assert im(1+4j) == 4 assert conj(3) == 3 assert conj(3+4j) == 3-4j assert mpf(3).conjugate() == 3 def test_cospi_sinpi(): assert sinpi(0) == 0 assert sinpi(0.5) == 1 assert sinpi(1) == 0 assert sinpi(1.5) == -1 assert sinpi(2) == 0 assert sinpi(2.5) == 1 assert sinpi(-0.5) == -1 assert cospi(0) == 1 assert cospi(0.5) == 0 assert cospi(1) == -1 assert cospi(1.5) == 0 assert cospi(2) == 1 assert cospi(2.5) == 0 assert cospi(-0.5) == 0 assert cospi(100000000000.25).ae(sqrt(2)/2) a = cospi(2+3j) assert a.real.ae(cos((2+3j)*pi).real) assert a.imag == 0 b = sinpi(2+3j) assert b.imag.ae(sin((2+3j)*pi).imag) assert b.real == 0 mp.dps = 35 x1 = mpf(10000) - mpf('1e-15') x2 = mpf(10000) + mpf('1e-15') x3 = mpf(10000.5) - mpf('1e-15') x4 = mpf(10000.5) + mpf('1e-15') x5 = mpf(10001) - mpf('1e-15') x6 = mpf(10001) + mpf('1e-15') x7 = mpf(10001.5) - mpf('1e-15') x8 = mpf(10001.5) + mpf('1e-15') mp.dps = 15 M = 10**15 assert (sinpi(x1)*M).ae(-pi) assert (sinpi(x2)*M).ae(pi) assert (cospi(x3)*M).ae(pi) assert (cospi(x4)*M).ae(-pi) assert (sinpi(x5)*M).ae(pi) assert (sinpi(x6)*M).ae(-pi) assert (cospi(x7)*M).ae(-pi) assert (cospi(x8)*M).ae(pi) assert 0.999 < cospi(x1, rounding='d') < 1 assert 0.999 < cospi(x2, rounding='d') < 1 assert 0.999 < sinpi(x3, rounding='d') < 1 assert 0.999 < sinpi(x4, rounding='d') < 1 assert -1 < cospi(x5, rounding='d') < -0.999 assert -1 < cospi(x6, rounding='d') < -0.999 assert -1 < sinpi(x7, rounding='d') < -0.999 assert -1 < sinpi(x8, rounding='d') < -0.999 assert (sinpi(1e-15)*M).ae(pi) assert (sinpi(-1e-15)*M).ae(-pi) assert cospi(1e-15) == 1 assert cospi(1e-15, rounding='d') < 1 def test_expj(): assert expj(0) == 1 assert expj(1).ae(exp(j)) assert expj(j).ae(exp(-1)) assert expj(1+j).ae(exp(j*(1+j))) assert expjpi(0) == 1 assert expjpi(1).ae(exp(j*pi)) assert expjpi(j).ae(exp(-pi)) assert expjpi(1+j).ae(exp(j*pi*(1+j))) assert expjpi(-10**15 * j).ae('2.22579818340535731e+1364376353841841') def test_sinc(): assert sinc(0) == sincpi(0) == 1 assert sinc(inf) == sincpi(inf) == 0 assert sinc(-inf) == sincpi(-inf) == 0 assert sinc(2).ae(0.45464871341284084770) assert sinc(2+3j).ae(0.4463290318402435457-2.7539470277436474940j) assert sincpi(2) == 0 assert sincpi(1.5).ae(-0.212206590789193781) def test_fibonacci(): mp.dps = 15 assert [fibonacci(n) for n in range(-5, 10)] == \ [5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34] assert fib(2.5).ae(1.4893065462657091) assert fib(3+4j).ae(-5248.51130728372 - 14195.962288353j) assert fib(1000).ae(4.3466557686937455e+208) assert str(fib(10**100)) == '6.24499112864607e+2089876402499787337692720892375554168224592399182109535392875613974104853496745963277658556235103534' mp.dps = 2100 a = fib(10000) assert a % 10**10 == 9947366875 mp.dps = 15 assert fibonacci(inf) == inf assert fib(3+0j) == 2 def test_call_with_dps(): mp.dps = 15 assert abs(exp(1, dps=30)-e(dps=35)) < 1e-29 def test_tanh(): mp.dps = 15 assert tanh(0) == 0 assert tanh(inf) == 1 assert tanh(-inf) == -1 assert isnan(tanh(nan)) assert tanh(mpc('inf', '0')) == 1 def test_atanh(): mp.dps = 15 assert atanh(0) == 0 assert atanh(0.5).ae(0.54930614433405484570) assert atanh(-0.5).ae(-0.54930614433405484570) assert atanh(1) == inf assert atanh(-1) == -inf assert isnan(atanh(nan)) assert isinstance(atanh(1), mpf) assert isinstance(atanh(-1), mpf) # Limits at infinity jpi2 = j*pi/2 assert atanh(inf).ae(-jpi2) assert atanh(-inf).ae(jpi2) assert atanh(mpc(inf,-1)).ae(-jpi2) assert atanh(mpc(inf,0)).ae(-jpi2) assert atanh(mpc(inf,1)).ae(jpi2) assert atanh(mpc(1,inf)).ae(jpi2) assert atanh(mpc(0,inf)).ae(jpi2) assert atanh(mpc(-1,inf)).ae(jpi2) assert atanh(mpc(-inf,1)).ae(jpi2) assert atanh(mpc(-inf,0)).ae(jpi2) assert atanh(mpc(-inf,-1)).ae(-jpi2) assert atanh(mpc(-1,-inf)).ae(-jpi2) assert atanh(mpc(0,-inf)).ae(-jpi2) assert atanh(mpc(1,-inf)).ae(-jpi2) def test_expm1(): mp.dps = 15 assert expm1(0) == 0 assert expm1(3).ae(exp(3)-1) assert expm1(inf) == inf assert expm1(1e-10)*1e10 assert expm1(1e-50).ae(1e-50) assert (expm1(1e-10)*1e10).ae(1.00000000005) def test_powm1(): mp.dps = 15 assert powm1(2,3) == 7 assert powm1(-1,2) == 0 assert powm1(-1,0) == 0 assert powm1(-2,0) == 0 assert powm1(3+4j,0) == 0 assert powm1(0,1) == -1 assert powm1(0,0) == 0 assert powm1(1,0) == 0 assert powm1(1,2) == 0 assert powm1(1,3+4j) == 0 assert powm1(1,5) == 0 assert powm1(j,4) == 0 assert powm1(-j,4) == 0 assert (powm1(2,1e-100)*1e100).ae(ln2) assert powm1(2,'1e-100000000000') != 0 assert (powm1(fadd(1,1e-100,exact=True), 5)*1e100).ae(5) def test_unitroots(): assert unitroots(1) == [1] assert unitroots(2) == [1, -1] a, b, c = unitroots(3) assert a == 1 assert b.ae(-0.5 + 0.86602540378443864676j) assert c.ae(-0.5 - 0.86602540378443864676j) assert unitroots(1, primitive=True) == [1] assert unitroots(2, primitive=True) == [-1] assert unitroots(3, primitive=True) == unitroots(3)[1:] assert unitroots(4, primitive=True) == [j, -j] assert len(unitroots(17, primitive=True)) == 16 assert len(unitroots(16, primitive=True)) == 8 def test_cyclotomic(): mp.dps = 15 assert [cyclotomic(n,1) for n in range(31)] == [1,0,2,3,2,5,1,7,2,3,1,11,1,13,1,1,2,17,1,19,1,1,1,23,1,5,1,3,1,29,1] assert [cyclotomic(n,-1) for n in range(31)] == [1,-2,0,1,2,1,3,1,2,1,5,1,1,1,7,1,2,1,3,1,1,1,11,1,1,1,13,1,1,1,1] assert [cyclotomic(n,j) for n in range(21)] == [1,-1+j,1+j,j,0,1,-j,j,2,-j,1,j,3,1,-j,1,2,1,j,j,5] assert [cyclotomic(n,-j) for n in range(21)] == [1,-1-j,1-j,-j,0,1,j,-j,2,j,1,-j,3,1,j,1,2,1,-j,-j,5] assert cyclotomic(1624,j) == 1 assert cyclotomic(33600,j) == 1 u = sqrt(j, prec=500) assert cyclotomic(8, u).ae(0) assert cyclotomic(30, u).ae(5.8284271247461900976) assert cyclotomic(2040, u).ae(1) assert cyclotomic(0,2.5) == 1 assert cyclotomic(1,2.5) == 2.5-1 assert cyclotomic(2,2.5) == 2.5+1 assert cyclotomic(3,2.5) == 2.5**2 + 2.5 + 1 assert cyclotomic(7,2.5) == 406.234375 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_interval.py0000644000175000017500000004063512014170666026774 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_interval_identity(): iv.dps = 15 assert mpi(2) == mpi(2, 2) assert mpi(2) != mpi(-2, 2) assert not (mpi(2) != mpi(2, 2)) assert mpi(-1, 1) == mpi(-1, 1) assert str(mpi('0.1')) == "[0.099999999999999991673, 0.10000000000000000555]" assert repr(mpi('0.1')) == "mpi('0.099999999999999992', '0.10000000000000001')" u = mpi(-1, 3) assert -1 in u assert 2 in u assert 3 in u assert -1.1 not in u assert 3.1 not in u assert mpi(-1, 3) in u assert mpi(0, 1) in u assert mpi(-1.1, 2) not in u assert mpi(2.5, 3.1) not in u w = mpi(-inf, inf) assert mpi(-5, 5) in w assert mpi(2, inf) in w assert mpi(0, 2) in mpi(0, 10) assert not (3 in mpi(-inf, 0)) def test_interval_arithmetic(): iv.dps = 15 assert mpi(2) + mpi(3,4) == mpi(5,6) assert mpi(1, 2)**2 == mpi(1, 4) assert mpi(1) + mpi(0, 1e-50) == mpi(1, mpf('1.0000000000000002')) x = 1 / (1 / mpi(3)) assert x.a < 3 < x.b x = mpi(2) ** mpi(0.5) iv.dps += 5 sq = iv.sqrt(2) iv.dps -= 5 assert x.a < sq < x.b assert mpi(1) / mpi(1, inf) assert mpi(2, 3) / inf == mpi(0, 0) assert mpi(0) / inf == 0 assert mpi(0) / 0 == mpi(-inf, inf) assert mpi(inf) / 0 == mpi(-inf, inf) assert mpi(0) * inf == mpi(-inf, inf) assert 1 / mpi(2, inf) == mpi(0, 0.5) assert str((mpi(50, 50) * mpi(-10, -10)) / 3) == \ '[-166.66666666666668561, -166.66666666666665719]' assert mpi(0, 4) ** 3 == mpi(0, 64) assert mpi(2,4).mid == 3 iv.dps = 30 a = mpi(iv.pi) iv.dps = 15 b = +a assert b.a < a.a assert b.b > a.b a = mpi(iv.pi) assert a == +a assert abs(mpi(-1,2)) == mpi(0,2) assert abs(mpi(0.5,2)) == mpi(0.5,2) assert abs(mpi(-3,2)) == mpi(0,3) assert abs(mpi(-3,-0.5)) == mpi(0.5,3) assert mpi(0) * mpi(2,3) == mpi(0) assert mpi(2,3) * mpi(0) == mpi(0) assert mpi(1,3).delta == 2 assert mpi(1,2) - mpi(3,4) == mpi(-3,-1) assert mpi(-inf,0) - mpi(0,inf) == mpi(-inf,0) assert mpi(-inf,0) - mpi(-inf,inf) == mpi(-inf,inf) assert mpi(0,inf) - mpi(-inf,1) == mpi(-1,inf) def test_interval_mul(): assert mpi(-1, 0) * inf == mpi(-inf, 0) assert mpi(-1, 0) * -inf == mpi(0, inf) assert mpi(0, 1) * inf == mpi(0, inf) assert mpi(0, 1) * mpi(0, inf) == mpi(0, inf) assert mpi(-1, 1) * inf == mpi(-inf, inf) assert mpi(-1, 1) * mpi(0, inf) == mpi(-inf, inf) assert mpi(-1, 1) * mpi(-inf, inf) == mpi(-inf, inf) assert mpi(-inf, 0) * mpi(0, 1) == mpi(-inf, 0) assert mpi(-inf, 0) * mpi(0, 0) * mpi(-inf, 0) assert mpi(-inf, 0) * mpi(-inf, inf) == mpi(-inf, inf) assert mpi(-5,0)*mpi(-32,28) == mpi(-140,160) assert mpi(2,3) * mpi(-1,2) == mpi(-3,6) # Should be undefined? assert mpi(inf, inf) * 0 == mpi(-inf, inf) assert mpi(-inf, -inf) * 0 == mpi(-inf, inf) assert mpi(0) * mpi(-inf,2) == mpi(-inf,inf) assert mpi(0) * mpi(-2,inf) == mpi(-inf,inf) assert mpi(-2,inf) * mpi(0) == mpi(-inf,inf) assert mpi(-inf,2) * mpi(0) == mpi(-inf,inf) def test_interval_pow(): assert mpi(3)**2 == mpi(9, 9) assert mpi(-3)**2 == mpi(9, 9) assert mpi(-3, 1)**2 == mpi(0, 9) assert mpi(-3, -1)**2 == mpi(1, 9) assert mpi(-3, -1)**3 == mpi(-27, -1) assert mpi(-3, 1)**3 == mpi(-27, 1) assert mpi(-2, 3)**2 == mpi(0, 9) assert mpi(-3, 2)**2 == mpi(0, 9) assert mpi(4) ** -1 == mpi(0.25, 0.25) assert mpi(-4) ** -1 == mpi(-0.25, -0.25) assert mpi(4) ** -2 == mpi(0.0625, 0.0625) assert mpi(-4) ** -2 == mpi(0.0625, 0.0625) assert mpi(0, 1) ** inf == mpi(0, 1) assert mpi(0, 1) ** -inf == mpi(1, inf) assert mpi(0, inf) ** inf == mpi(0, inf) assert mpi(0, inf) ** -inf == mpi(0, inf) assert mpi(1, inf) ** inf == mpi(1, inf) assert mpi(1, inf) ** -inf == mpi(0, 1) assert mpi(2, 3) ** 1 == mpi(2, 3) assert mpi(2, 3) ** 0 == 1 assert mpi(1,3) ** mpi(2) == mpi(1,9) def test_interval_sqrt(): assert mpi(4) ** 0.5 == mpi(2) def test_interval_div(): assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) assert mpi(0, 1) / mpi(0, 1) == mpi(0, inf) assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(inf, inf) / mpi(2, inf) == mpi(0, inf) assert mpi(inf, inf) / mpi(2, 2) == mpi(inf, inf) assert mpi(0, inf) / mpi(2, inf) == mpi(0, inf) assert mpi(0, inf) / mpi(2, 2) == mpi(0, inf) assert mpi(2, inf) / mpi(2, 2) == mpi(1, inf) assert mpi(2, inf) / mpi(2, inf) == mpi(0, inf) assert mpi(-4, 8) / mpi(1, inf) == mpi(-4, 8) assert mpi(-4, 8) / mpi(0.5, inf) == mpi(-8, 16) assert mpi(-inf, 8) / mpi(0.5, inf) == mpi(-inf, 16) assert mpi(-inf, inf) / mpi(0.5, inf) == mpi(-inf, inf) assert mpi(8, inf) / mpi(0.5, inf) == mpi(0, inf) assert mpi(-8, inf) / mpi(0.5, inf) == mpi(-16, inf) assert mpi(-4, 8) / mpi(inf, inf) == mpi(0, 0) assert mpi(0, 8) / mpi(inf, inf) == mpi(0, 0) assert mpi(0, 0) / mpi(inf, inf) == mpi(0, 0) assert mpi(-inf, 0) / mpi(inf, inf) == mpi(-inf, 0) assert mpi(-inf, 8) / mpi(inf, inf) == mpi(-inf, 0) assert mpi(-inf, inf) / mpi(inf, inf) == mpi(-inf, inf) assert mpi(-8, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(0, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(8, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) assert mpi(-1, 2) / mpi(0, 1) == mpi(-inf, +inf) assert mpi(0, 1) / mpi(0, 1) == mpi(0.0, +inf) assert mpi(-1, 0) / mpi(0, 1) == mpi(-inf, 0.0) assert mpi(-0.5, -0.25) / mpi(0, 1) == mpi(-inf, -0.25) assert mpi(0.5, 1) / mpi(0, 1) == mpi(0.5, +inf) assert mpi(0.5, 4) / mpi(0, 1) == mpi(0.5, +inf) assert mpi(-1, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) assert mpi(-4, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) assert mpi(-1, 2) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(0, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-1, 0) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-0.5, -0.25) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(0.5, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(0.5, 4) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-1, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-4, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) assert mpi(-1, 2) / mpi(-1, 0) == mpi(-inf, +inf) assert mpi(0, 1) / mpi(-1, 0) == mpi(-inf, 0.0) assert mpi(-1, 0) / mpi(-1, 0) == mpi(0.0, +inf) assert mpi(-0.5, -0.25) / mpi(-1, 0) == mpi(0.25, +inf) assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) assert mpi(0.5, 4) / mpi(-1, 0) == mpi(-inf, -0.5) assert mpi(-1, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) assert mpi(-4, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) assert mpi(-1, 2) / mpi(0.5, 1) == mpi(-2.0, 4.0) assert mpi(0, 1) / mpi(0.5, 1) == mpi(0.0, 2.0) assert mpi(-1, 0) / mpi(0.5, 1) == mpi(-2.0, 0.0) assert mpi(-0.5, -0.25) / mpi(0.5, 1) == mpi(-1.0, -0.25) assert mpi(0.5, 1) / mpi(0.5, 1) == mpi(0.5, 2.0) assert mpi(0.5, 4) / mpi(0.5, 1) == mpi(0.5, 8.0) assert mpi(-1, -0.5) / mpi(0.5, 1) == mpi(-2.0, -0.5) assert mpi(-4, -0.5) / mpi(0.5, 1) == mpi(-8.0, -0.5) assert mpi(-1, 2) / mpi(-2, -0.5) == mpi(-4.0, 2.0) assert mpi(0, 1) / mpi(-2, -0.5) == mpi(-2.0, 0.0) assert mpi(-1, 0) / mpi(-2, -0.5) == mpi(0.0, 2.0) assert mpi(-0.5, -0.25) / mpi(-2, -0.5) == mpi(0.125, 1.0) assert mpi(0.5, 1) / mpi(-2, -0.5) == mpi(-2.0, -0.25) assert mpi(0.5, 4) / mpi(-2, -0.5) == mpi(-8.0, -0.25) assert mpi(-1, -0.5) / mpi(-2, -0.5) == mpi(0.25, 2.0) assert mpi(-4, -0.5) / mpi(-2, -0.5) == mpi(0.25, 8.0) # Should be undefined? assert mpi(0, 0) / mpi(0, 0) == mpi(-inf, inf) assert mpi(0, 0) / mpi(0, 1) == mpi(-inf, inf) def test_interval_cos_sin(): iv.dps = 15 cos = iv.cos sin = iv.sin tan = iv.tan pi = iv.pi # Around 0 assert cos(mpi(0)) == 1 assert sin(mpi(0)) == 0 assert cos(mpi(0,1)) == mpi(0.54030230586813965399, 1.0) assert sin(mpi(0,1)) == mpi(0, 0.8414709848078966159) assert cos(mpi(1,2)) == mpi(-0.4161468365471424069, 0.54030230586813976501) assert sin(mpi(1,2)) == mpi(0.84147098480789650488, 1.0) assert sin(mpi(1,2.5)) == mpi(0.59847214410395643824, 1.0) assert cos(mpi(-1, 1)) == mpi(0.54030230586813965399, 1.0) assert cos(mpi(-1, 0.5)) == mpi(0.54030230586813965399, 1.0) assert cos(mpi(-1, 1.5)) == mpi(0.070737201667702906405, 1.0) assert sin(mpi(-1,1)) == mpi(-0.8414709848078966159, 0.8414709848078966159) assert sin(mpi(-1,0.5)) == mpi(-0.8414709848078966159, 0.47942553860420300538) assert mpi(-0.8414709848078966159, 1.00000000000000002e-100) in sin(mpi(-1,1e-100)) assert mpi(-2.00000000000000004e-100, 1.00000000000000002e-100) in sin(mpi(-2e-100,1e-100)) # Same interval assert cos(mpi(2, 2.5)) assert cos(mpi(3.5, 4)) == mpi(-0.93645668729079634129, -0.65364362086361182946) assert cos(mpi(5, 5.5)) == mpi(0.28366218546322624627, 0.70866977429126010168) assert mpi(0.59847214410395654927, 0.90929742682568170942) in sin(mpi(2, 2.5)) assert sin(mpi(3.5, 4)) == mpi(-0.75680249530792831347, -0.35078322768961983646) assert sin(mpi(5, 5.5)) == mpi(-0.95892427466313856499, -0.70554032557039181306) # Higher roots iv.dps = 55 w = 4*10**50 + mpi(0.5) for p in [15, 40, 80]: iv.dps = p assert 0 in sin(4*mpi(pi)) assert 0 in sin(4*10**50*mpi(pi)) assert 0 in cos((4+0.5)*mpi(pi)) assert 0 in cos(w*mpi(pi)) assert 1 in cos(4*mpi(pi)) assert 1 in cos(4*10**50*mpi(pi)) iv.dps = 15 assert cos(mpi(2,inf)) == mpi(-1,1) assert sin(mpi(2,inf)) == mpi(-1,1) assert cos(mpi(-inf,2)) == mpi(-1,1) assert sin(mpi(-inf,2)) == mpi(-1,1) u = tan(mpi(0.5,1)) assert mpf(u.a).ae(mp.tan(0.5)) assert mpf(u.b).ae(mp.tan(1)) v = iv.cot(mpi(0.5,1)) assert mpf(v.a).ae(mp.cot(1)) assert mpf(v.b).ae(mp.cot(0.5)) # Sanity check of evaluation at n*pi and (n+1/2)*pi for n in range(-5,7,2): x = iv.cos(n*iv.pi) assert -1 in x assert x >= -1 assert x != -1 x = iv.sin((n+0.5)*iv.pi) assert -1 in x assert x >= -1 assert x != -1 for n in range(-6,8,2): x = iv.cos(n*iv.pi) assert 1 in x assert x <= 1 if n: assert x != 1 x = iv.sin((n+0.5)*iv.pi) assert 1 in x assert x <= 1 assert x != 1 for n in range(-6,7): x = iv.cos((n+0.5)*iv.pi) assert x.a < 0 < x.b x = iv.sin(n*iv.pi) if n: assert x.a < 0 < x.b def test_interval_complex(): # TODO: many more tests iv.dps = 15 mp.dps = 15 assert iv.mpc(2,3) == 2+3j assert iv.mpc(2,3) != 2+4j assert iv.mpc(2,3) != 1+3j assert 1+3j in iv.mpc([1,2],[3,4]) assert 2+5j not in iv.mpc([1,2],[3,4]) assert iv.mpc(1,2) + 1j == 1+3j assert iv.mpc([1,2],[2,3]) + 2+3j == iv.mpc([3,4],[5,6]) assert iv.mpc([2,4],[4,8]) / 2 == iv.mpc([1,2],[2,4]) assert iv.mpc([1,2],[2,4]) * 2j == iv.mpc([-8,-4],[2,4]) assert iv.mpc([2,4],[4,8]) / 2j == iv.mpc([2,4],[-2,-1]) assert iv.exp(2+3j).ae(mp.exp(2+3j)) assert iv.log(2+3j).ae(mp.log(2+3j)) assert (iv.mpc(2,3) ** iv.mpc(0.5,2)).ae(mp.mpc(2,3) ** mp.mpc(0.5,2)) assert 1j in (iv.mpf(-1) ** 0.5) assert 1j in (iv.mpc(-1) ** 0.5) assert abs(iv.mpc(0)) == 0 assert abs(iv.mpc(inf)) == inf assert abs(iv.mpc(3,4)) == 5 assert abs(iv.mpc(4)) == 4 assert abs(iv.mpc(0,4)) == 4 assert abs(iv.mpc(0,[2,3])) == iv.mpf([2,3]) assert abs(iv.mpc(0,[-3,2])) == iv.mpf([0,3]) assert abs(iv.mpc([3,5],[4,12])) == iv.mpf([5,13]) assert abs(iv.mpc([3,5],[-4,12])) == iv.mpf([3,13]) assert iv.mpc(2,3) ** 0 == 1 assert iv.mpc(2,3) ** 1 == (2+3j) assert iv.mpc(2,3) ** 2 == (2+3j)**2 assert iv.mpc(2,3) ** 3 == (2+3j)**3 assert iv.mpc(2,3) ** 4 == (2+3j)**4 assert iv.mpc(2,3) ** 5 == (2+3j)**5 assert iv.mpc(2,2) ** (-1) == (2+2j) ** (-1) assert iv.mpc(2,2) ** (-2) == (2+2j) ** (-2) assert iv.cos(2).ae(mp.cos(2)) assert iv.sin(2).ae(mp.sin(2)) assert iv.cos(2+3j).ae(mp.cos(2+3j)) assert iv.sin(2+3j).ae(mp.sin(2+3j)) def test_interval_complex_arg(): assert iv.arg(3) == 0 assert iv.arg(0) == 0 assert iv.arg([0,3]) == 0 assert iv.arg(-3).ae(pi) assert iv.arg(2+3j).ae(iv.arg(2+3j)) z = iv.mpc([-2,-1],[3,4]) t = iv.arg(z) assert t.a.ae(mp.arg(-1+4j)) assert t.b.ae(mp.arg(-2+3j)) z = iv.mpc([-2,1],[3,4]) t = iv.arg(z) assert t.a.ae(mp.arg(1+3j)) assert t.b.ae(mp.arg(-2+3j)) z = iv.mpc([1,2],[3,4]) t = iv.arg(z) assert t.a.ae(mp.arg(2+3j)) assert t.b.ae(mp.arg(1+4j)) z = iv.mpc([1,2],[-2,3]) t = iv.arg(z) assert t.a.ae(mp.arg(1-2j)) assert t.b.ae(mp.arg(1+3j)) z = iv.mpc([1,2],[-4,-3]) t = iv.arg(z) assert t.a.ae(mp.arg(1-4j)) assert t.b.ae(mp.arg(2-3j)) z = iv.mpc([-1,2],[-4,-3]) t = iv.arg(z) assert t.a.ae(mp.arg(-1-3j)) assert t.b.ae(mp.arg(2-3j)) z = iv.mpc([-2,-1],[-4,-3]) t = iv.arg(z) assert t.a.ae(mp.arg(-2-3j)) assert t.b.ae(mp.arg(-1-4j)) z = iv.mpc([-2,-1],[-3,3]) t = iv.arg(z) assert t.a.ae(-mp.pi) assert t.b.ae(mp.pi) z = iv.mpc([-2,2],[-3,3]) t = iv.arg(z) assert t.a.ae(-mp.pi) assert t.b.ae(mp.pi) def test_interval_ae(): iv.dps = 15 x = iv.mpf([1,2]) assert x.ae(1) is None assert x.ae(1.5) is None assert x.ae(2) is None assert x.ae(2.01) is False assert x.ae(0.99) is False x = iv.mpf(3.5) assert x.ae(3.5) is True assert x.ae(3.5+1e-15) is True assert x.ae(3.5-1e-15) is True assert x.ae(3.501) is False assert x.ae(3.499) is False assert x.ae(iv.mpf([3.5,3.501])) is None assert x.ae(iv.mpf([3.5,4.5+1e-15])) is None def test_interval_nstr(): iv.dps = n = 30 x = mpi(1, 2) # FIXME: error_dps should not be necessary assert iv.nstr(x, n, mode='plusminus', error_dps=6) == '1.5 +- 0.5' assert iv.nstr(x, n, mode='plusminus', use_spaces=False, error_dps=6) == '1.5+-0.5' assert iv.nstr(x, n, mode='percent') == '1.5 (33.33%)' assert iv.nstr(x, n, mode='brackets', use_spaces=False) == '[1.0,2.0]' assert iv.nstr(x, n, mode='brackets' , brackets=('<', '>')) == '<1.0, 2.0>' x = mpi('5.2582327113062393041', '5.2582327113062749951') assert iv.nstr(x, n, mode='diff') == '5.2582327113062[393041, 749951]' assert iv.nstr(iv.cos(mpi(1)), n, mode='diff', use_spaces=False) == '0.54030230586813971740093660744[2955,3053]' assert iv.nstr(mpi('1e123', '1e129'), n, mode='diff') == '[1.0e+123, 1.0e+129]' exp = iv.exp assert iv.nstr(iv.exp(mpi('5000.1')), n, mode='diff') == '3.2797365856787867069110487[0926, 1191]e+2171' def test_mpi_from_str(): iv.dps = 15 assert iv.convert('1.5 +- 0.5') == mpi(mpf('1.0'), mpf('2.0')) assert mpi(1, 2) in iv.convert('1.5 (33.33333333333333333333333333333%)') assert iv.convert('[1, 2]') == mpi(1, 2) assert iv.convert('1[2, 3]') == mpi(12, 13) assert iv.convert('1.[23,46]e-8') == mpi('1.23e-8', '1.46e-8') assert iv.convert('12[3.4,5.9]e4') == mpi('123.4e+4', '125.9e4') def test_interval_gamma(): mp.dps = 15 iv.dps = 15 # TODO: need many more tests assert iv.rgamma(0) == 0 assert iv.fac(0) == 1 assert iv.fac(1) == 1 assert iv.fac(2) == 2 assert iv.fac(3) == 6 assert iv.gamma(0) == [-inf,inf] assert iv.gamma(1) == 1 assert iv.gamma(2) == 1 assert iv.gamma(3) == 2 assert -3.5449077018110320546 in iv.gamma(-0.5) assert iv.loggamma(1) == 0 assert iv.loggamma(2) == 0 assert 0.69314718055994530942 in iv.loggamma(3) # Test tight log-gamma endpoints based on monotonicity xs = [iv.mpc([2,3],[1,4]), iv.mpc([2,3],[-4,-1]), iv.mpc([2,3],[-1,4]), iv.mpc([2,3],[-4,1]), iv.mpc([2,3],[-4,4]), iv.mpc([-3,-2],[2,4]), iv.mpc([-3,-2],[-4,-2])] for x in xs: ys = [mp.loggamma(mp.mpc(x.a,x.c)), mp.loggamma(mp.mpc(x.b,x.c)), mp.loggamma(mp.mpc(x.a,x.d)), mp.loggamma(mp.mpc(x.b,x.d))] if 0 in x.imag: ys += [mp.loggamma(x.a), mp.loggamma(x.b)] min_real = min([y.real for y in ys]) max_real = max([y.real for y in ys]) min_imag = min([y.imag for y in ys]) max_imag = max([y.imag for y in ys]) z = iv.loggamma(x) assert z.a.ae(min_real) assert z.b.ae(max_real) assert z.c.ae(min_imag) assert z.d.ae(max_imag) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_elliptic.py0000644000175000017500000005657612014170666026770 0ustar georgeskgeorgesk""" Limited tests of the elliptic functions module. A full suite of extensive testing can be found in elliptic_torture_tests.py Author of the first version: M.T. Taschuk References: [1] Abramowitz & Stegun. 'Handbook of Mathematical Functions, 9th Ed.', (Dover duplicate of 1972 edition) [2] Whittaker 'A Course of Modern Analysis, 4th Ed.', 1946, Cambridge University Press """ import sympy.mpmath import random from sympy.mpmath import * def mpc_ae(a, b, eps=eps): res = True res = res and a.real.ae(b.real, eps) res = res and a.imag.ae(b.imag, eps) return res zero = mpf(0) one = mpf(1) jsn = ellipfun('sn') jcn = ellipfun('cn') jdn = ellipfun('dn') calculate_nome = lambda k: qfrom(k=k) def test_ellipfun(): assert ellipfun('ss', 0, 0) == 1 assert ellipfun('cc', 0, 0) == 1 assert ellipfun('dd', 0, 0) == 1 assert ellipfun('nn', 0, 0) == 1 assert ellipfun('sn', 0.25, 0).ae(sin(0.25)) assert ellipfun('cn', 0.25, 0).ae(cos(0.25)) assert ellipfun('dn', 0.25, 0).ae(1) assert ellipfun('ns', 0.25, 0).ae(csc(0.25)) assert ellipfun('nc', 0.25, 0).ae(sec(0.25)) assert ellipfun('nd', 0.25, 0).ae(1) assert ellipfun('sc', 0.25, 0).ae(tan(0.25)) assert ellipfun('sd', 0.25, 0).ae(sin(0.25)) assert ellipfun('cd', 0.25, 0).ae(cos(0.25)) assert ellipfun('cs', 0.25, 0).ae(cot(0.25)) assert ellipfun('dc', 0.25, 0).ae(sec(0.25)) assert ellipfun('ds', 0.25, 0).ae(csc(0.25)) assert ellipfun('sn', 0.25, 1).ae(tanh(0.25)) assert ellipfun('cn', 0.25, 1).ae(sech(0.25)) assert ellipfun('dn', 0.25, 1).ae(sech(0.25)) assert ellipfun('ns', 0.25, 1).ae(coth(0.25)) assert ellipfun('nc', 0.25, 1).ae(cosh(0.25)) assert ellipfun('nd', 0.25, 1).ae(cosh(0.25)) assert ellipfun('sc', 0.25, 1).ae(sinh(0.25)) assert ellipfun('sd', 0.25, 1).ae(sinh(0.25)) assert ellipfun('cd', 0.25, 1).ae(1) assert ellipfun('cs', 0.25, 1).ae(csch(0.25)) assert ellipfun('dc', 0.25, 1).ae(1) assert ellipfun('ds', 0.25, 1).ae(csch(0.25)) assert ellipfun('sn', 0.25, 0.5).ae(0.24615967096986145833) assert ellipfun('cn', 0.25, 0.5).ae(0.96922928989378439337) assert ellipfun('dn', 0.25, 0.5).ae(0.98473484156599474563) assert ellipfun('ns', 0.25, 0.5).ae(4.0624038700573130369) assert ellipfun('nc', 0.25, 0.5).ae(1.0317476065024692949) assert ellipfun('nd', 0.25, 0.5).ae(1.0155017958029488665) assert ellipfun('sc', 0.25, 0.5).ae(0.25397465134058993408) assert ellipfun('sd', 0.25, 0.5).ae(0.24997558792415733063) assert ellipfun('cd', 0.25, 0.5).ae(0.98425408443195497052) assert ellipfun('cs', 0.25, 0.5).ae(3.9374008182374110826) assert ellipfun('dc', 0.25, 0.5).ae(1.0159978158253033913) assert ellipfun('ds', 0.25, 0.5).ae(4.0003906313579720593) def test_calculate_nome(): mp.dps = 100 q = calculate_nome(zero) assert(q == zero) mp.dps = 25 # used Mathematica's EllipticNomeQ[m] math1 = [(mpf(1)/10, mpf('0.006584651553858370274473060')), (mpf(2)/10, mpf('0.01394285727531826872146409')), (mpf(3)/10, mpf('0.02227743615715350822901627')), (mpf(4)/10, mpf('0.03188334731336317755064299')), (mpf(5)/10, mpf('0.04321391826377224977441774')), (mpf(6)/10, mpf('0.05702025781460967637754953')), (mpf(7)/10, mpf('0.07468994353717944761143751')), (mpf(8)/10, mpf('0.09927369733882489703607378')), (mpf(9)/10, mpf('0.1401731269542615524091055')), (mpf(9)/10, mpf('0.1401731269542615524091055'))] for i in math1: m = i[0] q = calculate_nome(sqrt(m)) assert q.ae(i[1]) mp.dps = 15 def test_jtheta(): mp.dps = 25 z = q = zero for n in range(1,5): value = jtheta(n, z, q) assert(value == (n-1)//2) for q in [one, mpf(2)]: for n in range(1,5): raised = True try: r = jtheta(n, z, q) except: pass else: raised = False assert(raised) z = one/10 q = one/11 # Mathematical N[EllipticTheta[1, 1/10, 1/11], 25] res = mpf('0.1069552990104042681962096') result = jtheta(1, z, q) assert(result.ae(res)) # Mathematica N[EllipticTheta[2, 1/10, 1/11], 25] res = mpf('1.101385760258855791140606') result = jtheta(2, z, q) assert(result.ae(res)) # Mathematica N[EllipticTheta[3, 1/10, 1/11], 25] res = mpf('1.178319743354331061795905') result = jtheta(3, z, q) assert(result.ae(res)) # Mathematica N[EllipticTheta[4, 1/10, 1/11], 25] res = mpf('0.8219318954665153577314573') result = jtheta(4, z, q) assert(result.ae(res)) # test for sin zeros for jtheta(1, z, q) # test for cos zeros for jtheta(2, z, q) z1 = pi z2 = pi/2 for i in range(10): qstring = str(random.random()) q = mpf(qstring) result = jtheta(1, z1, q) assert(result.ae(0)) result = jtheta(2, z2, q) assert(result.ae(0)) mp.dps = 15 def test_jtheta_issue39(): # near the circle of covergence |q| = 1 the convergence slows # down; for |q| > Q_LIM the theta functions raise ValueError mp.dps = 30 mp.dps += 30 q = mpf(6)/10 - one/10**6 - mpf(8)/10 * j mp.dps -= 30 # Mathematica run first # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 2000] # then it works: # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 30] res = mpf('32.0031009628901652627099524264') + \ mpf('16.6153027998236087899308935624') * j result = jtheta(3, 1, q) # check that for abs(q) > Q_LIM a ValueError exception is raised mp.dps += 30 q = mpf(6)/10 - one/10**7 - mpf(8)/10 * j mp.dps -= 30 try: result = jtheta(3, 1, q) except ValueError: pass else: assert(False) # bug reported in issue39 mp.dps = 100 z = (1+j)/3 q = mpf(368983957219251)/10**15 + mpf(636363636363636)/10**15 * j # Mathematica N[EllipticTheta[1, z, q], 35] res = mpf('2.4439389177990737589761828991467471') + \ mpf('0.5446453005688226915290954851851490') *j mp.dps = 30 result = jtheta(1, z, q) assert(result.ae(res)) mp.dps = 80 z = 3 + 4*j q = 0.5 + 0.5*j r1 = jtheta(1, z, q) mp.dps = 15 r2 = jtheta(1, z, q) assert r1.ae(r2) mp.dps = 80 z = 3 + j q1 = exp(j*3) # longer test # for n in range(1, 6) for n in range(1, 2): mp.dps = 80 q = q1*(1 - mpf(1)/10**n) r1 = jtheta(1, z, q) mp.dps = 15 r2 = jtheta(1, z, q) assert r1.ae(r2) mp.dps = 15 # issue 39 about high derivatives assert jtheta(3, 4.5, 0.25, 9).ae(1359.04892680683) assert jtheta(3, 4.5, 0.25, 50).ae(-6.14832772630905e+33) mp.dps = 50 r = jtheta(3, 4.5, 0.25, 9) assert r.ae('1359.048926806828939547859396600218966947753213803') r = jtheta(3, 4.5, 0.25, 50) assert r.ae('-6148327726309051673317975084654262.4119215720343656') def test_jtheta_identities(): """ Tests the some of the jacobi identidies found in Abramowitz, Sec. 16.28, Pg. 576. The identities are tested to 1 part in 10^98. """ mp.dps = 110 eps1 = ldexp(eps, 30) for i in range(10): qstring = str(random.random()) q = mpf(qstring) zstring = str(10*random.random()) z = mpf(zstring) # Abramowitz 16.28.1 # v_1(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_2(0, q)**2 # - v_2(z, q)**2 * v_3(0, q)**2 term1 = (jtheta(1, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(3, z, q)**2) * (jtheta(2, zero, q)**2) term3 = (jtheta(2, z, q)**2) * (jtheta(3, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) zstring = str(100*random.random()) z = mpf(zstring) # Abramowitz 16.28.2 # v_2(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_2(0, q)**2 # - v_1(z, q)**2 * v_3(0, q)**2 term1 = (jtheta(2, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(4, z, q)**2) * (jtheta(2, zero, q)**2) term3 = (jtheta(1, z, q)**2) * (jtheta(3, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) # Abramowitz 16.28.3 # v_3(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_3(0, q)**2 # - v_1(z, q)**2 * v_2(0, q)**2 term1 = (jtheta(3, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(4, z, q)**2) * (jtheta(3, zero, q)**2) term3 = (jtheta(1, z, q)**2) * (jtheta(2, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) # Abramowitz 16.28.4 # v_4(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_3(0, q)**2 # - v_2(z, q)**2 * v_2(0, q)**2 term1 = (jtheta(4, z, q)**2) * (jtheta(4, zero, q)**2) term2 = (jtheta(3, z, q)**2) * (jtheta(3, zero, q)**2) term3 = (jtheta(2, z, q)**2) * (jtheta(2, zero, q)**2) equality = term1 - term2 + term3 assert(equality.ae(0, eps1)) # Abramowitz 16.28.5 # v_2(0, q)**4 + v_4(0, q)**4 == v_3(0, q)**4 term1 = (jtheta(2, zero, q))**4 term2 = (jtheta(4, zero, q))**4 term3 = (jtheta(3, zero, q))**4 equality = term1 + term2 - term3 assert(equality.ae(0, eps1)) mp.dps = 15 def test_jtheta_complex(): mp.dps = 30 z = mpf(1)/4 + j/8 q = mpf(1)/3 + j/7 # Mathematica N[EllipticTheta[1, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.31618034835986160705729105731678285') + \ mpf('0.07542013825835103435142515194358975') * j r = jtheta(1, z, q) assert(mpc_ae(r, res)) # Mathematica N[EllipticTheta[2, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('1.6530986428239765928634711417951828') + \ mpf('0.2015344864707197230526742145361455') * j r = jtheta(2, z, q) assert(mpc_ae(r, res)) # Mathematica N[EllipticTheta[3, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('1.6520564411784228184326012700348340') + \ mpf('0.1998129119671271328684690067401823') * j r = jtheta(3, z, q) assert(mpc_ae(r, res)) # Mathematica N[EllipticTheta[4, 1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.37619082382228348252047624089973824') - \ mpf('0.15623022130983652972686227200681074') * j r = jtheta(4, z, q) assert(mpc_ae(r, res)) # check some theta function identities mp.dos = 100 z = mpf(1)/4 + j/8 q = mpf(1)/3 + j/7 mp.dps += 10 a = [0,0, jtheta(2, 0, q), jtheta(3, 0, q), jtheta(4, 0, q)] t = [0, jtheta(1, z, q), jtheta(2, z, q), jtheta(3, z, q), jtheta(4, z, q)] r = [(t[2]*a[4])**2 - (t[4]*a[2])**2 + (t[1] *a[3])**2, (t[3]*a[4])**2 - (t[4]*a[3])**2 + (t[1] *a[2])**2, (t[1]*a[4])**2 - (t[3]*a[2])**2 + (t[2] *a[3])**2, (t[4]*a[4])**2 - (t[3]*a[3])**2 + (t[2] *a[2])**2, a[2]**4 + a[4]**4 - a[3]**4] mp.dps -= 10 for x in r: assert(mpc_ae(x, mpc(0))) mp.dps = 15 def test_djtheta(): mp.dps = 30 z = one/7 + j/3 q = one/8 + j/5 # Mathematica N[EllipticThetaPrime[1, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('1.5555195883277196036090928995803201') - \ mpf('0.02439761276895463494054149673076275') * j result = jtheta(1, z, q, 1) assert(mpc_ae(result, res)) # Mathematica N[EllipticThetaPrime[2, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('0.19825296689470982332701283509685662') - \ mpf('0.46038135182282106983251742935250009') * j result = jtheta(2, z, q, 1) assert(mpc_ae(result, res)) # Mathematica N[EllipticThetaPrime[3, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('0.36492498415476212680896699407390026') - \ mpf('0.57743812698666990209897034525640369') * j result = jtheta(3, z, q, 1) assert(mpc_ae(result, res)) # Mathematica N[EllipticThetaPrime[4, 1/7 + I/3, 1/8 + I/5], 35] res = mpf('-0.38936892528126996010818803742007352') + \ mpf('0.66549886179739128256269617407313625') * j result = jtheta(4, z, q, 1) assert(mpc_ae(result, res)) for i in range(10): q = (one*random.random() + j*random.random())/2 # identity in Wittaker, Watson &21.41 a = jtheta(1, 0, q, 1) b = jtheta(2, 0, q)*jtheta(3, 0, q)*jtheta(4, 0, q) assert(a.ae(b)) # test higher derivatives mp.dps = 20 for q,z in [(one/3, one/5), (one/3 + j/8, one/5), (one/3, one/5 + j/8), (one/3 + j/7, one/5 + j/8)]: for n in [1, 2, 3, 4]: r = jtheta(n, z, q, 2) r1 = diff(lambda zz: jtheta(n, zz, q), z, n=2) assert r.ae(r1) r = jtheta(n, z, q, 3) r1 = diff(lambda zz: jtheta(n, zz, q), z, n=3) assert r.ae(r1) # identity in Wittaker, Watson &21.41 q = one/3 z = zero a = [0]*5 a[1] = jtheta(1, z, q, 3)/jtheta(1, z, q, 1) for n in [2,3,4]: a[n] = jtheta(n, z, q, 2)/jtheta(n, z, q) equality = a[2] + a[3] + a[4] - a[1] assert(equality.ae(0)) mp.dps = 15 def test_jsn(): """ Test some special cases of the sn(z, q) function. """ mp.dps = 100 # trival case result = jsn(zero, zero) assert(result == zero) # Abramowitz Table 16.5 # # sn(0, m) = 0 for i in range(10): qstring = str(random.random()) q = mpf(qstring) equality = jsn(zero, q) assert(equality.ae(0)) # Abramowitz Table 16.6.1 # # sn(z, 0) = sin(z), m == 0 # # sn(z, 1) = tanh(z), m == 1 # # It would be nice to test these, but I find that they run # in to numerical trouble. I'm currently treating as a boundary # case for sn function. mp.dps = 25 arg = one/10 #N[JacobiSN[1/10, 2^-100], 25] res = mpf('0.09983341664682815230681420') m = ldexp(one, -100) result = jsn(arg, m) assert(result.ae(res)) # N[JacobiSN[1/10, 1/10], 25] res = mpf('0.09981686718599080096451168') result = jsn(arg, arg) assert(result.ae(res)) mp.dps = 15 def test_jcn(): """ Test some special cases of the cn(z, q) function. """ mp.dps = 100 # Abramowitz Table 16.5 # cn(0, q) = 1 qstring = str(random.random()) q = mpf(qstring) cn = jcn(zero, q) assert(cn.ae(one)) # Abramowitz Table 16.6.2 # # cn(u, 0) = cos(u), m == 0 # # cn(u, 1) = sech(z), m == 1 # # It would be nice to test these, but I find that they run # in to numerical trouble. I'm currently treating as a boundary # case for cn function. mp.dps = 25 arg = one/10 m = ldexp(one, -100) #N[JacobiCN[1/10, 2^-100], 25] res = mpf('0.9950041652780257660955620') result = jcn(arg, m) assert(result.ae(res)) # N[JacobiCN[1/10, 1/10], 25] res = mpf('0.9950058256237368748520459') result = jcn(arg, arg) assert(result.ae(res)) mp.dps = 15 def test_jdn(): """ Test some special cases of the dn(z, q) function. """ mp.dps = 100 # Abramowitz Table 16.5 # dn(0, q) = 1 mstring = str(random.random()) m = mpf(mstring) dn = jdn(zero, m) assert(dn.ae(one)) mp.dps = 25 # N[JacobiDN[1/10, 1/10], 25] res = mpf('0.9995017055025556219713297') arg = one/10 result = jdn(arg, arg) assert(result.ae(res)) mp.dps = 15 def test_sn_cn_dn_identities(): """ Tests the some of the jacobi elliptic function identities found on Mathworld. Haven't found in Abramowitz. """ mp.dps = 100 N = 5 for i in range(N): qstring = str(random.random()) q = mpf(qstring) zstring = str(100*random.random()) z = mpf(zstring) # MathWorld # sn(z, q)**2 + cn(z, q)**2 == 1 term1 = jsn(z, q)**2 term2 = jcn(z, q)**2 equality = one - term1 - term2 assert(equality.ae(0)) # MathWorld # k**2 * sn(z, m)**2 + dn(z, m)**2 == 1 for i in range(N): mstring = str(random.random()) m = mpf(qstring) k = m.sqrt() zstring = str(10*random.random()) z = mpf(zstring) term1 = k**2 * jsn(z, m)**2 term2 = jdn(z, m)**2 equality = one - term1 - term2 assert(equality.ae(0)) for i in range(N): mstring = str(random.random()) m = mpf(mstring) k = m.sqrt() zstring = str(random.random()) z = mpf(zstring) # MathWorld # k**2 * cn(z, m)**2 + (1 - k**2) = dn(z, m)**2 term1 = k**2 * jcn(z, m)**2 term2 = 1 - k**2 term3 = jdn(z, m)**2 equality = term3 - term1 - term2 assert(equality.ae(0)) K = ellipk(k**2) # Abramowitz Table 16.5 # sn(K, m) = 1; K is K(k), first complete elliptic integral r = jsn(K, m) assert(r.ae(one)) # Abramowitz Table 16.5 # cn(K, q) = 0; K is K(k), first complete elliptic integral equality = jcn(K, m) assert(equality.ae(0)) # Abramowitz Table 16.6.3 # dn(z, 0) = 1, m == 0 z = m value = jdn(z, zero) assert(value.ae(one)) mp.dps = 15 def test_sn_cn_dn_complex(): mp.dps = 30 # N[JacobiSN[1/4 + I/8, 1/3 + I/7], 35] in Mathematica res = mpf('0.2495674401066275492326652143537') + \ mpf('0.12017344422863833381301051702823') * j u = mpf(1)/4 + j/8 m = mpf(1)/3 + j/7 r = jsn(u, m) assert(mpc_ae(r, res)) #N[JacobiCN[1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.9762691700944007312693721148331') - \ mpf('0.0307203994181623243583169154824')*j r = jcn(u, m) #assert r.real.ae(res.real) #assert r.imag.ae(res.imag) assert(mpc_ae(r, res)) #N[JacobiDN[1/4 + I/8, 1/3 + I/7], 35] res = mpf('0.99639490163039577560547478589753039') - \ mpf('0.01346296520008176393432491077244994')*j r = jdn(u, m) assert(mpc_ae(r, res)) mp.dps = 15 def test_elliptic_integrals(): # Test cases from Carlson's paper mp.dps = 15 assert elliprd(0,2,1).ae(1.7972103521033883112) assert elliprd(2,3,4).ae(0.16510527294261053349) assert elliprd(j,-j,2).ae(0.65933854154219768919) assert elliprd(0,j,-j).ae(1.2708196271909686299 + 2.7811120159520578777j) assert elliprd(0,j-1,j).ae(-1.8577235439239060056 - 0.96193450888838559989j) assert elliprd(-2-j,-j,-1+j).ae(1.8249027393703805305 - 1.2218475784827035855j) for n in [5, 15, 30, 60, 100]: mp.dps = n assert elliprf(1,2,0).ae('1.3110287771460599052324197949455597068413774757158115814084108519003952935352071251151477664807145467230678763') assert elliprf(0.5,1,0).ae('1.854074677301371918433850347195260046217598823521766905585928045056021776838119978357271861650371897277771871') assert elliprf(j,-j,0).ae('1.854074677301371918433850347195260046217598823521766905585928045056021776838119978357271861650371897277771871') assert elliprf(j-1,j,0).ae(mpc('0.79612586584233913293056938229563057846592264089185680214929401744498956943287031832657642790719940442165621412', '-1.2138566698364959864300942567386038975419875860741507618279563735753073152507112254567291141460317931258599889')) assert elliprf(2,3,4).ae('0.58408284167715170669284916892566789240351359699303216166309375305508295130412919665541330837704050454472379308') assert elliprf(j,-j,2).ae('1.0441445654064360931078658361850779139591660747973017593275012615517220315993723776182276555339288363064476126') assert elliprf(j-1,j,1-j).ae(mpc('0.93912050218619371196624617169781141161485651998254431830645241993282941057500174238125105410055253623847335313', '-0.53296252018635269264859303449447908970360344322834582313172115220559316331271520508208025270300138589669326136')) assert elliprc(0,0.25).ae(+pi) assert elliprc(2.25,2).ae(+ln2) assert elliprc(0,j).ae(mpc('1.1107207345395915617539702475151734246536554223439225557713489017391086982748684776438317336911913093408525532', '-1.1107207345395915617539702475151734246536554223439225557713489017391086982748684776438317336911913093408525532')) assert elliprc(-j,j).ae(mpc('1.2260849569072198222319655083097718755633725139745941606203839524036426936825652935738621522906572884239069297', '-0.34471136988767679699935618332997956653521218571295874986708834375026550946053920574015526038040124556716711353')) assert elliprc(0.25,-2).ae(ln2/3) assert elliprc(j,-1).ae(mpc('0.77778596920447389875196055840799837589537035343923012237628610795937014001905822029050288316217145443865649819', '0.1983248499342877364755170948292130095921681309577950696116251029742793455964385947473103628983664877025779304')) assert elliprj(0,1,2,3).ae('0.77688623778582332014190282640545501102298064276022952731669118325952563819813258230708177398475643634103990878') assert elliprj(2,3,4,5).ae('0.14297579667156753833233879421985774801466647854232626336218889885463800128817976132826443904216546421431528308') assert elliprj(2,3,4,-1+j).ae(mpc('0.13613945827770535203521374457913768360237593025944342652613569368333226052158214183059386307242563164036672709', '-0.38207561624427164249600936454845112611060375760094156571007648297226090050927156176977091273224510621553615189')) assert elliprj(j,-j,0,2).ae('1.6490011662710884518243257224860232300246792717163891216346170272567376981346412066066050103935109581019055806') assert elliprj(-1+j,-1-j,1,2).ae('0.94148358841220238083044612133767270187474673547917988681610772381758628963408843935027667916713866133196845063') assert elliprj(j,-j,0,1-j).ae(mpc('1.8260115229009316249372594065790946657011067182850435297162034335356430755397401849070610280860044610878657501', '1.2290661908643471500163617732957042849283739403009556715926326841959667290840290081010472716420690899886276961')) assert elliprj(-1+j,-1-j,1,-3+j).ae(mpc('-0.61127970812028172123588152373622636829986597243716610650831553882054127570542477508023027578037045504958619422', '-1.0684038390006807880182112972232562745485871763154040245065581157751693730095703406209466903752930797510491155')) assert elliprj(-1+j,-2-j,-j,-1+j).ae(mpc('1.8249027393703805304622013339009022294368078659619988943515764258335975852685224202567854526307030593012768954', '-1.2218475784827035854568450371590419833166777535029296025352291308244564398645467465067845461070602841312456831')) assert elliprg(0,16,16).ae(+pi) assert elliprg(2,3,4).ae('1.7255030280692277601061148835701141842692457170470456590515892070736643637303053506944907685301315299153040991') assert elliprg(0,j,-j).ae('0.42360654239698954330324956174109581824072295516347109253028968632986700241706737986160014699730561497106114281') assert elliprg(j-1,j,0).ae(mpc('0.44660591677018372656731970402124510811555212083508861036067729944477855594654762496407405328607219895053798354', '0.70768352357515390073102719507612395221369717586839400605901402910893345301718731499237159587077682267374159282')) assert elliprg(-j,j-1,j).ae(mpc('0.36023392184473309033675652092928695596803358846377334894215349632203382573844427952830064383286995172598964266', '0.40348623401722113740956336997761033878615232917480045914551915169013722542827052849476969199578321834819903921')) assert elliprg(0, mpf('0.0796'), 4).ae('1.0284758090288040009838871385180217366569777284430590125081211090574701293154645750017813190805144572673802094') mp.dps = 15 def test_issue198(): assert isnan(qfrom(m=nan)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_quad.py0000644000175000017500000000723112014170666026075 0ustar georgeskgeorgeskfrom sympy.mpmath import * def ae(a, b): return abs(a-b) < 10**(-mp.dps+5) def test_basic_integrals(): for prec in [15, 30, 100]: mp.dps = prec assert ae(quadts(lambda x: x**3 - 3*x**2, [-2, 4]), -12) assert ae(quadgl(lambda x: x**3 - 3*x**2, [-2, 4]), -12) assert ae(quadts(sin, [0, pi]), 2) assert ae(quadts(sin, [0, 2*pi]), 0) assert ae(quadts(exp, [-inf, -1]), 1/e) assert ae(quadts(lambda x: exp(-x), [0, inf]), 1) assert ae(quadts(lambda x: exp(-x*x), [-inf, inf]), sqrt(pi)) assert ae(quadts(lambda x: 1/(1+x*x), [-1, 1]), pi/2) assert ae(quadts(lambda x: 1/(1+x*x), [-inf, inf]), pi) assert ae(quadts(lambda x: 2*sqrt(1-x*x), [-1, 1]), pi) mp.dps = 15 def test_quad_symmetry(): assert quadts(sin, [-1, 1]) == 0 assert quadgl(sin, [-1, 1]) == 0 def test_quad_infinite_mirror(): # Check mirrored infinite interval assert ae(quad(lambda x: exp(-x*x), [inf,-inf]), -sqrt(pi)) assert ae(quad(lambda x: exp(x), [0,-inf]), -1) def test_quadgl_linear(): assert quadgl(lambda x: x, [0, 1], maxdegree=1).ae(0.5) def test_complex_integration(): assert quadts(lambda x: x, [0, 1+j]).ae(j) def test_quadosc(): mp.dps = 15 assert quadosc(lambda x: sin(x)/x, [0, inf], period=2*pi).ae(pi/2) # Double integrals def test_double_trivial(): assert ae(quadts(lambda x, y: x, [0, 1], [0, 1]), 0.5) assert ae(quadts(lambda x, y: x, [-1, 1], [-1, 1]), 0.0) def test_double_1(): assert ae(quadts(lambda x, y: cos(x+y/2), [-pi/2, pi/2], [0, pi]), 4) def test_double_2(): assert ae(quadts(lambda x, y: (x-1)/((1-x*y)*log(x*y)), [0, 1], [0, 1]), euler) def test_double_3(): assert ae(quadts(lambda x, y: 1/sqrt(1+x*x+y*y), [-1, 1], [-1, 1]), 4*log(2+sqrt(3))-2*pi/3) def test_double_4(): assert ae(quadts(lambda x, y: 1/(1-x*x * y*y), [0, 1], [0, 1]), pi**2 / 8) def test_double_5(): assert ae(quadts(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]), pi**2 / 6) def test_double_6(): assert ae(quadts(lambda x, y: exp(-(x+y)), [0, inf], [0, inf]), 1) # fails def xtest_double_7(): assert ae(quadts(lambda x, y: exp(-x*x-y*y), [-inf, inf], [-inf, inf]), pi) # Test integrals from "Experimentation in Mathematics" by Borwein, # Bailey & Girgensohn def test_expmath_integrals(): for prec in [15, 30, 50]: mp.dps = prec assert ae(quadts(lambda x: x/sinh(x), [0, inf]), pi**2 / 4) assert ae(quadts(lambda x: log(x)**2 / (1+x**2), [0, inf]), pi**3 / 8) assert ae(quadts(lambda x: (1+x**2)/(1+x**4), [0, inf]), pi/sqrt(2)) assert ae(quadts(lambda x: log(x)/cosh(x)**2, [0, inf]), log(pi)-2*log(2)-euler) assert ae(quadts(lambda x: log(1+x**3)/(1-x+x**2), [0, inf]), 2*pi*log(3)/sqrt(3)) assert ae(quadts(lambda x: log(x)**2 / (x**2+x+1), [0, 1]), 8*pi**3 / (81*sqrt(3))) assert ae(quadts(lambda x: log(cos(x))**2, [0, pi/2]), pi/2 * (log(2)**2+pi**2/12)) assert ae(quadts(lambda x: x**2 / sin(x)**2, [0, pi/2]), pi*log(2)) assert ae(quadts(lambda x: x**2/sqrt(exp(x)-1), [0, inf]), 4*pi*(log(2)**2 + pi**2/12)) assert ae(quadts(lambda x: x*exp(-x)*sqrt(1-exp(-2*x)), [0, inf]), pi*(1+2*log(2))/8) mp.dps = 15 # Do not reach full accuracy def xtest_expmath_fail(): assert ae(quadts(lambda x: sqrt(tan(x)), [0, pi/2]), pi*sqrt(2)/2) assert ae(quadts(lambda x: atan(x)/(x*sqrt(1-x**2)), [0, 1]), pi*log(1+sqrt(2))/2) assert ae(quadts(lambda x: log(1+x**2)/x**2, [0, 1]), pi/2-log(2)) assert ae(quadts(lambda x: x**2/((1+x**4)*sqrt(1-x**4)), [0, 1]), pi/8) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_trig.py0000644000175000017500000001153012014170666026105 0ustar georgeskgeorgeskfrom sympy.mpmath import * from sympy.mpmath.libmp import * def test_trig_misc_hard(): mp.prec = 53 # Worst-case input for an IEEE double, from a paper by Kahan x = ldexp(6381956970095103,797) assert cos(x) == mpf('-4.6871659242546277e-19') assert sin(x) == 1 mp.prec = 150 a = mpf(10**50) mp.prec = 53 assert sin(a).ae(-0.7896724934293100827) assert cos(a).ae(-0.6135286082336635622) # Check relative accuracy close to x = zero assert sin(1e-100) == 1e-100 # when rounding to nearest assert sin(1e-6).ae(9.999999999998333e-007, rel_eps=2e-15, abs_eps=0) assert sin(1e-6j).ae(1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) assert sin(-1e-6j).ae(-1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) assert cos(1e-100) == 1 assert cos(1e-6).ae(0.9999999999995) assert cos(-1e-6j).ae(1.0000000000005) assert tan(1e-100) == 1e-100 assert tan(1e-6).ae(1.0000000000003335e-006, rel_eps=2e-15, abs_eps=0) assert tan(1e-6j).ae(9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) assert tan(-1e-6j).ae(-9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) def test_trig_near_zero(): mp.dps = 15 for r in [round_nearest, round_down, round_up, round_floor, round_ceiling]: assert sin(0, rounding=r) == 0 assert cos(0, rounding=r) == 1 a = mpf('1e-100') b = mpf('-1e-100') assert sin(a, rounding=round_nearest) == a assert sin(a, rounding=round_down) < a assert sin(a, rounding=round_floor) < a assert sin(a, rounding=round_up) >= a assert sin(a, rounding=round_ceiling) >= a assert sin(b, rounding=round_nearest) == b assert sin(b, rounding=round_down) > b assert sin(b, rounding=round_floor) <= b assert sin(b, rounding=round_up) <= b assert sin(b, rounding=round_ceiling) > b assert cos(a, rounding=round_nearest) == 1 assert cos(a, rounding=round_down) < 1 assert cos(a, rounding=round_floor) < 1 assert cos(a, rounding=round_up) == 1 assert cos(a, rounding=round_ceiling) == 1 assert cos(b, rounding=round_nearest) == 1 assert cos(b, rounding=round_down) < 1 assert cos(b, rounding=round_floor) < 1 assert cos(b, rounding=round_up) == 1 assert cos(b, rounding=round_ceiling) == 1 def test_trig_near_n_pi(): mp.dps = 15 a = [n*pi for n in [1, 2, 6, 11, 100, 1001, 10000, 100001]] mp.dps = 135 a.append(10**100 * pi) mp.dps = 15 assert sin(a[0]) == mpf('1.2246467991473531772e-16') assert sin(a[1]) == mpf('-2.4492935982947063545e-16') assert sin(a[2]) == mpf('-7.3478807948841190634e-16') assert sin(a[3]) == mpf('4.8998251578625894243e-15') assert sin(a[4]) == mpf('1.9643867237284719452e-15') assert sin(a[5]) == mpf('-8.8632615209684813458e-15') assert sin(a[6]) == mpf('-4.8568235395684898392e-13') assert sin(a[7]) == mpf('3.9087342299491231029e-11') assert sin(a[8]) == mpf('-1.369235466754566993528e-36') r = round_nearest assert cos(a[0], rounding=r) == -1 assert cos(a[1], rounding=r) == 1 assert cos(a[2], rounding=r) == 1 assert cos(a[3], rounding=r) == -1 assert cos(a[4], rounding=r) == 1 assert cos(a[5], rounding=r) == -1 assert cos(a[6], rounding=r) == 1 assert cos(a[7], rounding=r) == -1 assert cos(a[8], rounding=r) == 1 r = round_up assert cos(a[0], rounding=r) == -1 assert cos(a[1], rounding=r) == 1 assert cos(a[2], rounding=r) == 1 assert cos(a[3], rounding=r) == -1 assert cos(a[4], rounding=r) == 1 assert cos(a[5], rounding=r) == -1 assert cos(a[6], rounding=r) == 1 assert cos(a[7], rounding=r) == -1 assert cos(a[8], rounding=r) == 1 r = round_down assert cos(a[0], rounding=r) > -1 assert cos(a[1], rounding=r) < 1 assert cos(a[2], rounding=r) < 1 assert cos(a[3], rounding=r) > -1 assert cos(a[4], rounding=r) < 1 assert cos(a[5], rounding=r) > -1 assert cos(a[6], rounding=r) < 1 assert cos(a[7], rounding=r) > -1 assert cos(a[8], rounding=r) < 1 r = round_floor assert cos(a[0], rounding=r) == -1 assert cos(a[1], rounding=r) < 1 assert cos(a[2], rounding=r) < 1 assert cos(a[3], rounding=r) == -1 assert cos(a[4], rounding=r) < 1 assert cos(a[5], rounding=r) == -1 assert cos(a[6], rounding=r) < 1 assert cos(a[7], rounding=r) == -1 assert cos(a[8], rounding=r) < 1 r = round_ceiling assert cos(a[0], rounding=r) > -1 assert cos(a[1], rounding=r) == 1 assert cos(a[2], rounding=r) == 1 assert cos(a[3], rounding=r) > -1 assert cos(a[4], rounding=r) == 1 assert cos(a[5], rounding=r) > -1 assert cos(a[6], rounding=r) == 1 assert cos(a[7], rounding=r) > -1 assert cos(a[8], rounding=r) == 1 mp.dps = 15 if __name__ == '__main__': for f in globals().keys(): if f.startswith("test_"): print(f) globals()[f]() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_matrices.py0000644000175000017500000001273612014170666026760 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_matrix_basic(): A1 = matrix(3) for i in range(3): A1[i,i] = 1 assert A1 == eye(3) assert A1 == matrix(A1) A2 = matrix(3, 2) assert not A2._matrix__data A3 = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) assert list(A3) == list(range(1, 10)) A3[1,1] = 0 assert not (1, 1) in A3._matrix__data A4 = matrix([[1, 2, 3], [4, 5, 6]]) A5 = matrix([[6, -1], [3, 2], [0, -3]]) assert A4 * A5 == matrix([[12, -6], [39, -12]]) assert A1 * A3 == A3 * A1 == A3 try: A2 * A2 assert False except ValueError: pass l = [[10, 20, 30], [40, 0, 60], [70, 80, 90]] A6 = matrix(l) assert A6.tolist() == l assert A6 == eval(repr(A6)) A6 = matrix(A6, force_type=float) assert A6 == eval(repr(A6)) assert A6*1j == eval(repr(A6*1j)) assert A3 * 10 == 10 * A3 == A6 assert A2.rows == 3 assert A2.cols == 2 A3.rows = 2 A3.cols = 2 assert len(A3._matrix__data) == 3 assert A4 + A4 == 2*A4 try: A4 + A2 except ValueError: pass assert sum(A1 - A1) == 0 A7 = matrix([[1, 2], [3, 4], [5, 6], [7, 8]]) x = matrix([10, -10]) assert A7*x == matrix([-10, -10, -10, -10]) A8 = ones(5) assert sum((A8 + 1) - (2 - zeros(5))) == 0 assert (1 + ones(4)) / 2 - 1 == zeros(4) assert eye(3)**10 == eye(3) try: A7**2 assert False except ValueError: pass A9 = randmatrix(3) A10 = matrix(A9) A9[0,0] = -100 assert A9 != A10 assert nstr(A9) def test_matrix_slices(): A = matrix([ [1, 2, 3], [4, 5 ,6], [7, 8 ,9]]) V = matrix([1,2,3,4,5]) # Get slice assert A[:,:] == A assert A[:,1] == matrix([[2],[5],[8]]) assert A[2,:] == matrix([[7, 8 ,9]]) assert A[1:3,1:3] == matrix([[5,6],[8,9]]) assert V[2:4] == matrix([3,4]) try: A6 = A[:,1:6] assert False except IndexError: pass # Assign slice with matrix A1 = matrix(3) A1[:,:] = A assert A1[:,:] == matrix([[1, 2, 3], [4, 5 ,6], [7, 8 ,9]]) A1[0,:] = matrix([[10, 11, 12]]) assert A1 == matrix([ [10, 11, 12], [4, 5 ,6], [7, 8 ,9]]) A1[:,2] = matrix([[13], [14], [15]]) assert A1 == matrix([ [10, 11, 13], [4, 5 ,14], [7, 8 ,15]]) A1[:2,:2] = matrix([[16, 17], [18 , 19]]) assert A1 == matrix([ [16, 17, 13], [18, 19 ,14], [7, 8 ,15]]) V[1:3] = 10 assert V == matrix([1,10,10,4,5]) try: A1[2,:] = A[:,1] assert False except ValueError: pass try: A1[2,1:20] = A[:,:] assert False except IndexError: pass # Assign slice with scalar A1[:,2] = 10 assert A1 == matrix([ [16, 17, 10], [18, 19 ,10], [7, 8 ,10]]) A1[:,:] = 40 for x in A1: assert x == 40 def test_matrix_power(): A = matrix([[1, 2], [3, 4]]) assert A**2 == A*A assert A**3 == A*A*A assert A**-1 == inverse(A) assert A**-2 == inverse(A*A) def test_matrix_transform(): A = matrix([[1, 2], [3, 4], [5, 6]]) assert A.T == A.transpose() == matrix([[1, 3, 5], [2, 4, 6]]) swap_row(A, 1, 2) assert A == matrix([[1, 2], [5, 6], [3, 4]]) l = [1, 2] swap_row(l, 0, 1) assert l == [2, 1] assert extend(eye(3), [1,2,3]) == matrix([[1,0,0,1],[0,1,0,2],[0,0,1,3]]) def test_matrix_conjugate(): A = matrix([[1 + j, 0], [2, j]]) assert A.conjugate() == matrix([[mpc(1, -1), 0], [2, mpc(0, -1)]]) assert A.transpose_conj() == A.H == matrix([[mpc(1, -1), 2], [0, mpc(0, -1)]]) def test_matrix_creation(): assert diag([1, 2, 3]) == matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) A1 = ones(2, 3) assert A1.rows == 2 and A1.cols == 3 for a in A1: assert a == 1 A2 = zeros(3, 2) assert A2.rows == 3 and A2.cols == 2 for a in A2: assert a == 0 assert randmatrix(10) != randmatrix(10) one = mpf(1) assert hilbert(3) == matrix([[one, one/2, one/3], [one/2, one/3, one/4], [one/3, one/4, one/5]]) def test_norms(): # matrix norms A = matrix([[1, -2], [-3, -1], [2, 1]]) assert mnorm(A,1) == 6 assert mnorm(A,inf) == 4 assert mnorm(A,'F') == sqrt(20) # vector norms assert norm(-3) == 3 x = [1, -2, 7, -12] assert norm(x, 1) == 22 assert round(norm(x, 2), 10) == 14.0712472795 assert round(norm(x, 10), 10) == 12.0054633727 assert norm(x, inf) == 12 def test_vector(): x = matrix([0, 1, 2, 3, 4]) assert x == matrix([[0], [1], [2], [3], [4]]) assert x[3] == 3 assert len(x._matrix__data) == 4 assert list(x) == list(range(5)) x[0] = -10 x[4] = 0 assert x[0] == -10 assert len(x) == len(x.T) == 5 assert x.T*x == matrix([[114]]) def test_matrix_copy(): A = ones(6) B = A.copy() assert A == B B[0,0] = 0 assert A != B def test_matrix_numpy(): try: import numpy except ImportError: return l = [[1, 2], [3, 4], [5, 6]] a = numpy.matrix(l) assert matrix(l) == matrix(a) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_summation.py0000644000175000017500000000351112014170666027154 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_sumem(): mp.dps = 15 assert sumem(lambda k: 1/k**2.5, [50, 100]).ae(0.0012524505324784962) assert sumem(lambda k: k**4 + 3*k + 1, [10, 100]).ae(2050333103) def test_nsum(): mp.dps = 15 assert nsum(lambda x: x**2, [1, 3]) == 14 assert nsum(lambda k: 1/factorial(k), [0, inf]).ae(e) assert nsum(lambda k: (-1)**(k+1) / k, [1, inf]).ae(log(2)) assert nsum(lambda k: (-1)**(k+1) / k**2, [1, inf]).ae(pi**2 / 12) assert nsum(lambda k: (-1)**k / log(k), [2, inf]).ae(0.9242998972229388) assert nsum(lambda k: 1/k**2, [1, inf]).ae(pi**2 / 6) assert nsum(lambda k: 2**k/fac(k), [0, inf]).ae(exp(2)) assert nsum(lambda k: 1/k**2, [4, inf], method='e').ae(0.2838229557371153) def test_nprod(): mp.dps = 15 assert nprod(lambda k: exp(1/k**2), [1,inf], method='r').ae(exp(pi**2/6)) assert nprod(lambda x: x**2, [1, 3]) == 36 def test_fsum(): mp.dps = 15 assert fsum([]) == 0 assert fsum([-4]) == -4 assert fsum([2,3]) == 5 assert fsum([1e-100,1]) == 1 assert fsum([1,1e-100]) == 1 assert fsum([1e100,1]) == 1e100 assert fsum([1,1e100]) == 1e100 assert fsum([1e-100,0]) == 1e-100 assert fsum([1e-100,1e100,1e-100]) == 1e100 assert fsum([2,1+1j,1]) == 4+1j assert fsum([2,inf,3]) == inf assert fsum([2,-1], absolute=1) == 3 assert fsum([2,-1], squared=1) == 5 assert fsum([1,1+j], squared=1) == 1+2j assert fsum([1,3+4j], absolute=1) == 6 assert fsum([1,2+3j], absolute=1, squared=1) == 14 assert isnan(fsum([inf,-inf])) assert fsum([inf,-inf], absolute=1) == inf assert fsum([inf,-inf], squared=1) == inf assert fsum([inf,-inf], absolute=1, squared=1) == inf assert iv.fsum([1,mpi(2,3)]) == mpi(3,4) def test_fprod(): mp.dps = 15 assert fprod([]) == 1 assert fprod([2,3]) == 6 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/extratest_gamma.py0000644000175000017500000001605512014170666027275 0ustar georgeskgeorgeskfrom mpmath import * from mpmath.libmp import ifac import sys if "-dps" in sys.argv: maxdps = int(sys.argv[sys.argv.index("-dps")+1]) else: maxdps = 1000 raise_ = "-raise" in sys.argv errcount = 0 def check(name, func, z, y): global errcount try: x = func(z) except: errcount += 1 if raise_: raise print() print(name) print("EXCEPTION") import traceback traceback.print_tb(sys.exc_info()[2]) print() return xre = x.real xim = x.imag yre = y.real yim = y.imag tol = eps*8 err = 0 if abs(xre-yre) > abs(yre)*tol: err = 1 print() print("Error! %s (re = %s, wanted %s, err=%s)" % (name, nstr(xre,10), nstr(yre,10), nstr(abs(xre-yre)))) errcount += 1 if raise_: raise SystemExit if abs(xim-yim) > abs(yim)*tol: err = 1 print() print("Error! %s (im = %s, wanted %s, err=%s)" % (name, nstr(xim,10), nstr(yim,10), nstr(abs(xim-yim)))) errcount += 1 if raise_: raise SystemExit if not err: print("%s ok;" % name, end=' ') def testcase(case): z, result = case print("Testing z =", z) mp.dps = 1010 z = eval(z) mp.dps = maxdps + 50 if result is None: gamma_val = gamma(z) loggamma_val = loggamma(z) factorial_val = factorial(z) rgamma_val = rgamma(z) else: loggamma_val = eval(result) gamma_val = exp(loggamma_val) factorial_val = z * gamma_val rgamma_val = 1/gamma_val for dps in [5, 10, 15, 25, 40, 60, 90, 120, 250, 600, 1000, 1800, 3600]: if dps > maxdps: break mp.dps = dps print("dps = %s" % dps) check("gamma", gamma, z, gamma_val) check("rgamma", rgamma, z, rgamma_val) check("loggamma", loggamma, z, loggamma_val) check("factorial", factorial, z, factorial_val) print() mp.dps = 15 testcases = [] # Basic values for n in range(1,200) + range(201,2000,17): testcases.append(["%s" % n, None]) for n in range(-200,200): testcases.append(["%s+0.5" % n, None]) testcases.append(["%s+0.37" % n, None]) testcases += [\ ["(0.1+1j)", None], ["(-0.1+1j)", None], ["(0.1-1j)", None], ["(-0.1-1j)", None], ["10j", None], ["-10j", None], ["100j", None], ["10000j", None], ["-10000000j", None], ["(10**100)*j", None], ["125+(10**100)*j", None], ["-125+(10**100)*j", None], ["(10**10)*(1+j)", None], ["(10**10)*(-1+j)", None], ["(10**100)*(1+j)", None], ["(10**100)*(-1+j)", None], ["(1.5-1j)", None], ["(6+4j)", None], ["(4+1j)", None], ["(3.5+2j)", None], ["(1.5-1j)", None], ["(-6-4j)", None], ["(-2-3j)", None], ["(-2.5-2j)", None], ["(4+1j)", None], ["(3+3j)", None], ["(2-2j)", None], ["1", "0"], ["2", "0"], ["3", "log(2)"], ["4", "log(6)"], ["5", "log(24)"], ["0.5", "log(pi)/2"], ["1.5", "log(sqrt(pi)/2)"], ["2.5", "log(3*sqrt(pi)/4)"], ["mpf('0.37')", None], ["0.25", "log(sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))))"], ["-0.4", None], ["mpf('-1.9')", None], ["mpf('12.8')", None], ["mpf('33.7')", None], ["mpf('95.2')", None], ["mpf('160.3')", None], ["mpf('2057.8')", None], ["25", "log(ifac(24))"], ["80", "log(ifac(79))"], ["500", "log(ifac(500-1))"], ["8000", "log(ifac(8000-1))"], ["8000.5", None], ["mpf('8000.1')", None], ["mpf('1.37e10')", None], ["mpf('1.37e10')*(1+j)", None], ["mpf('1.37e10')*(-1+j)", None], ["mpf('1.37e10')*(-1-j)", None], ["mpf('1.37e10')*(-1+j)", None], ["mpf('1.37e100')", None], ["mpf('1.37e100')*(1+j)", None], ["mpf('1.37e100')*(-1+j)", None], ["mpf('1.37e100')*(-1-j)", None], ["mpf('1.37e100')*(-1+j)", None], ["3+4j", "mpc('" "-1.7566267846037841105306041816232757851567066070613445016197619371316057169" "4723618263960834804618463052988607348289672535780644470689771115236512106002" "5970873471563240537307638968509556191696167970488390423963867031934333890838" "8009531786948197210025029725361069435208930363494971027388382086721660805397" "9163230643216054580167976201709951509519218635460317367338612500626714783631" "7498317478048447525674016344322545858832610325861086336204591943822302971823" "5161814175530618223688296232894588415495615809337292518431903058265147109853" "1710568942184987827643886816200452860853873815413367529829631430146227470517" "6579967222200868632179482214312673161276976117132204633283806161971389519137" "1243359764435612951384238091232760634271570950240717650166551484551654327989" "9360285030081716934130446150245110557038117075172576825490035434069388648124" "6678152254554001586736120762641422590778766100376515737713938521275749049949" "1284143906816424244705094759339932733567910991920631339597278805393743140853" "391550313363278558195609260225928','" "4.74266443803465792819488940755002274088830335171164611359052405215840070271" "5906813009373171139767051863542508136875688550817670379002790304870822775498" "2809996675877564504192565392367259119610438951593128982646945990372179860613" "4294436498090428077839141927485901735557543641049637962003652638924845391650" "9546290137755550107224907606529385248390667634297183361902055842228798984200" "9591180450211798341715874477629099687609819466457990642030707080894518168924" "6805549314043258530272479246115112769957368212585759640878745385160943755234" "9398036774908108204370323896757543121853650025529763655312360354244898913463" "7115955702828838923393113618205074162812089732064414530813087483533203244056" "0546577484241423134079056537777170351934430586103623577814746004431994179990" "5318522939077992613855205801498201930221975721246498720895122345420698451980" "0051215797310305885845964334761831751370672996984756815410977750799748813563" "8784405288158432214886648743541773208808731479748217023665577802702269468013" "673719173759245720489020315779001')"], ] for z in [4, 14, 34, 64]: testcases.append(["(2+j)*%s/3" % z, None]) testcases.append(["(-2+j)*%s/3" % z, None]) testcases.append(["(1+2*j)*%s/3" % z, None]) testcases.append(["(2-j)*%s/3" % z, None]) testcases.append(["(20+j)*%s/3" % z, None]) testcases.append(["(-20+j)*%s/3" % z, None]) testcases.append(["(1+20*j)*%s/3" % z, None]) testcases.append(["(20-j)*%s/3" % z, None]) testcases.append(["(200+j)*%s/3" % z, None]) testcases.append(["(-200+j)*%s/3" % z, None]) testcases.append(["(1+200*j)*%s/3" % z, None]) testcases.append(["(200-j)*%s/3" % z, None]) # Poles for n in [0,1,2,3,4,25,-1,-2,-3,-4,-20,-21,-50,-51,-200,-201,-20000,-20001]: for t in ['1e-5', '1e-20', '1e-100', '1e-10000']: testcases.append(["fadd(%s,'%s',exact=True)" % (n, t), None]) testcases.append(["fsub(%s,'%s',exact=True)" % (n, t), None]) testcases.append(["fadd(%s,'%sj',exact=True)" % (n, t), None]) testcases.append(["fsub(%s,'%sj',exact=True)" % (n, t), None]) if __name__ == "__main__": from timeit import default_timer as clock tot_time = 0.0 for case in testcases: t1 = clock() testcase(case) t2 = clock() print("Test time:", t2-t1) print() tot_time += (t2-t1) print("Total time:", tot_time) print("Errors:", errcount) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_gammazeta.py0000644000175000017500000006452412014170666027121 0ustar georgeskgeorgeskfrom sympy.mpmath import * from sympy.mpmath.libmp import round_up, from_float, mpf_zeta_int def test_zeta_int_bug(): assert mpf_zeta_int(0, 10) == from_float(-0.5) def test_bernoulli(): assert bernfrac(0) == (1,1) assert bernfrac(1) == (-1,2) assert bernfrac(2) == (1,6) assert bernfrac(3) == (0,1) assert bernfrac(4) == (-1,30) assert bernfrac(5) == (0,1) assert bernfrac(6) == (1,42) assert bernfrac(8) == (-1,30) assert bernfrac(10) == (5,66) assert bernfrac(12) == (-691,2730) assert bernfrac(18) == (43867,798) p, q = bernfrac(228) assert p % 10**10 == 164918161 assert q == 625170 p, q = bernfrac(1000) assert p % 10**10 == 7950421099 assert q == 342999030 mp.dps = 15 assert bernoulli(0) == 1 assert bernoulli(1) == -0.5 assert bernoulli(2).ae(1./6) assert bernoulli(3) == 0 assert bernoulli(4).ae(-1./30) assert bernoulli(5) == 0 assert bernoulli(6).ae(1./42) assert str(bernoulli(10)) == '0.0757575757575758' assert str(bernoulli(234)) == '7.62772793964344e+267' assert str(bernoulli(10**5)) == '-5.82229431461335e+376755' assert str(bernoulli(10**8+2)) == '1.19570355039953e+676752584' mp.dps = 50 assert str(bernoulli(10)) == '0.075757575757575757575757575757575757575757575757576' assert str(bernoulli(234)) == '7.6277279396434392486994969020496121553385863373331e+267' assert str(bernoulli(10**5)) == '-5.8222943146133508236497045360612887555320691004308e+376755' assert str(bernoulli(10**8+2)) == '1.1957035503995297272263047884604346914602088317782e+676752584' mp.dps = 1000 assert bernoulli(10).ae(mpf(5)/66) mp.dps = 50000 assert bernoulli(10).ae(mpf(5)/66) mp.dps = 15 def test_bernpoly_eulerpoly(): mp.dps = 15 assert bernpoly(0,-1).ae(1) assert bernpoly(0,0).ae(1) assert bernpoly(0,'1/2').ae(1) assert bernpoly(0,'3/4').ae(1) assert bernpoly(0,1).ae(1) assert bernpoly(0,2).ae(1) assert bernpoly(1,-1).ae('-3/2') assert bernpoly(1,0).ae('-1/2') assert bernpoly(1,'1/2').ae(0) assert bernpoly(1,'3/4').ae('1/4') assert bernpoly(1,1).ae('1/2') assert bernpoly(1,2).ae('3/2') assert bernpoly(2,-1).ae('13/6') assert bernpoly(2,0).ae('1/6') assert bernpoly(2,'1/2').ae('-1/12') assert bernpoly(2,'3/4').ae('-1/48') assert bernpoly(2,1).ae('1/6') assert bernpoly(2,2).ae('13/6') assert bernpoly(3,-1).ae(-3) assert bernpoly(3,0).ae(0) assert bernpoly(3,'1/2').ae(0) assert bernpoly(3,'3/4').ae('-3/64') assert bernpoly(3,1).ae(0) assert bernpoly(3,2).ae(3) assert bernpoly(4,-1).ae('119/30') assert bernpoly(4,0).ae('-1/30') assert bernpoly(4,'1/2').ae('7/240') assert bernpoly(4,'3/4').ae('7/3840') assert bernpoly(4,1).ae('-1/30') assert bernpoly(4,2).ae('119/30') assert bernpoly(5,-1).ae(-5) assert bernpoly(5,0).ae(0) assert bernpoly(5,'1/2').ae(0) assert bernpoly(5,'3/4').ae('25/1024') assert bernpoly(5,1).ae(0) assert bernpoly(5,2).ae(5) assert bernpoly(10,-1).ae('665/66') assert bernpoly(10,0).ae('5/66') assert bernpoly(10,'1/2').ae('-2555/33792') assert bernpoly(10,'3/4').ae('-2555/34603008') assert bernpoly(10,1).ae('5/66') assert bernpoly(10,2).ae('665/66') assert bernpoly(11,-1).ae(-11) assert bernpoly(11,0).ae(0) assert bernpoly(11,'1/2').ae(0) assert bernpoly(11,'3/4').ae('-555731/4194304') assert bernpoly(11,1).ae(0) assert bernpoly(11,2).ae(11) assert eulerpoly(0,-1).ae(1) assert eulerpoly(0,0).ae(1) assert eulerpoly(0,'1/2').ae(1) assert eulerpoly(0,'3/4').ae(1) assert eulerpoly(0,1).ae(1) assert eulerpoly(0,2).ae(1) assert eulerpoly(1,-1).ae('-3/2') assert eulerpoly(1,0).ae('-1/2') assert eulerpoly(1,'1/2').ae(0) assert eulerpoly(1,'3/4').ae('1/4') assert eulerpoly(1,1).ae('1/2') assert eulerpoly(1,2).ae('3/2') assert eulerpoly(2,-1).ae(2) assert eulerpoly(2,0).ae(0) assert eulerpoly(2,'1/2').ae('-1/4') assert eulerpoly(2,'3/4').ae('-3/16') assert eulerpoly(2,1).ae(0) assert eulerpoly(2,2).ae(2) assert eulerpoly(3,-1).ae('-9/4') assert eulerpoly(3,0).ae('1/4') assert eulerpoly(3,'1/2').ae(0) assert eulerpoly(3,'3/4').ae('-11/64') assert eulerpoly(3,1).ae('-1/4') assert eulerpoly(3,2).ae('9/4') assert eulerpoly(4,-1).ae(2) assert eulerpoly(4,0).ae(0) assert eulerpoly(4,'1/2').ae('5/16') assert eulerpoly(4,'3/4').ae('57/256') assert eulerpoly(4,1).ae(0) assert eulerpoly(4,2).ae(2) assert eulerpoly(5,-1).ae('-3/2') assert eulerpoly(5,0).ae('-1/2') assert eulerpoly(5,'1/2').ae(0) assert eulerpoly(5,'3/4').ae('361/1024') assert eulerpoly(5,1).ae('1/2') assert eulerpoly(5,2).ae('3/2') assert eulerpoly(10,-1).ae(2) assert eulerpoly(10,0).ae(0) assert eulerpoly(10,'1/2').ae('-50521/1024') assert eulerpoly(10,'3/4').ae('-36581523/1048576') assert eulerpoly(10,1).ae(0) assert eulerpoly(10,2).ae(2) assert eulerpoly(11,-1).ae('-699/4') assert eulerpoly(11,0).ae('691/4') assert eulerpoly(11,'1/2').ae(0) assert eulerpoly(11,'3/4').ae('-512343611/4194304') assert eulerpoly(11,1).ae('-691/4') assert eulerpoly(11,2).ae('699/4') # Potential accuracy issues assert bernpoly(10000,10000).ae('5.8196915936323387117e+39999') assert bernpoly(200,17.5).ae(3.8048418524583064909e244) assert eulerpoly(200,17.5).ae(-3.7309911582655785929e275) def test_gamma(): mp.dps = 15 assert gamma(0.25).ae(3.6256099082219083119) assert gamma(0.0001).ae(9999.4228832316241908) assert gamma(300).ae('1.0201917073881354535e612') assert gamma(-0.5).ae(-3.5449077018110320546) assert gamma(-7.43).ae(0.00026524416464197007186) #assert gamma(Rational(1,2)) == gamma(0.5) #assert gamma(Rational(-7,3)).ae(gamma(mpf(-7)/3)) assert gamma(1+1j).ae(0.49801566811835604271 - 0.15494982830181068512j) assert gamma(-1+0.01j).ae(-0.422733904013474115 + 99.985883082635367436j) assert gamma(20+30j).ae(-1453876687.5534810 + 1163777777.8031573j) # Should always give exact factorials when they can # be represented as mpfs under the current working precision fact = 1 for i in range(1, 18): assert gamma(i) == fact fact *= i for dps in [170, 600]: fact = 1 mp.dps = dps for i in range(1, 105): assert gamma(i) == fact fact *= i mp.dps = 100 assert gamma(0.5).ae(sqrt(pi)) mp.dps = 15 assert factorial(0) == fac(0) == 1 assert factorial(3) == 6 assert isnan(gamma(nan)) assert gamma(1100).ae('4.8579168073569433667e2866') assert rgamma(0) == 0 assert rgamma(-1) == 0 assert rgamma(2) == 1.0 assert rgamma(3) == 0.5 assert loggamma(2+8j).ae(-8.5205176753667636926 + 10.8569497125597429366j) assert loggamma('1e10000').ae('2.302485092994045684017991e10004') assert loggamma('1e10000j').ae(mpc('-1.570796326794896619231322e10000','2.302485092994045684017991e10004')) def test_fac2(): mp.dps = 15 assert [fac2(n) for n in range(10)] == [1,1,2,3,8,15,48,105,384,945] assert fac2(-5).ae(1./3) assert fac2(-11).ae(-1./945) assert fac2(50).ae(5.20469842636666623e32) assert fac2(0.5+0.75j).ae(0.81546769394688069176-0.34901016085573266889j) assert fac2(inf) == inf assert isnan(fac2(-inf)) def test_gamma_quotients(): mp.dps = 15 h = 1e-8 ep = 1e-4 G = gamma assert gammaprod([-1],[-3,-4]) == 0 assert gammaprod([-1,0],[-5]) == inf assert abs(gammaprod([-1],[-2]) - G(-1+h)/G(-2+h)) < 1e-4 assert abs(gammaprod([-4,-3],[-2,0]) - G(-4+h)*G(-3+h)/G(-2+h)/G(0+h)) < 1e-4 assert rf(3,0) == 1 assert rf(2.5,1) == 2.5 assert rf(-5,2) == 20 assert rf(j,j).ae(gamma(2*j)/gamma(j)) assert ff(-2,0) == 1 assert ff(-2,1) == -2 assert ff(4,3) == 24 assert ff(3,4) == 0 assert binomial(0,0) == 1 assert binomial(1,0) == 1 assert binomial(0,-1) == 0 assert binomial(3,2) == 3 assert binomial(5,2) == 10 assert binomial(5,3) == 10 assert binomial(5,5) == 1 assert binomial(-1,0) == 1 assert binomial(-2,-4) == 3 assert binomial(4.5, 1.5) == 6.5625 assert binomial(1100,1) == 1100 assert binomial(1100,2) == 604450 assert beta(1,1) == 1 assert beta(0,0) == inf assert beta(3,0) == inf assert beta(-1,-1) == inf assert beta(1.5,1).ae(2/3.) assert beta(1.5,2.5).ae(pi/16) assert (10**15*beta(10,100)).ae(2.3455339739604649879) assert beta(inf,inf) == 0 assert isnan(beta(-inf,inf)) assert isnan(beta(-3,inf)) assert isnan(beta(0,inf)) assert beta(inf,0.5) == beta(0.5,inf) == 0 assert beta(inf,-1.5) == inf assert beta(inf,-0.5) == -inf assert beta(1+2j,-1-j/2).ae(1.16396542451069943086+0.08511695947832914640j) assert beta(-0.5,0.5) == 0 assert beta(-3,3).ae(-1/3.) def test_zeta(): mp.dps = 15 assert zeta(2).ae(pi**2 / 6) assert zeta(2.0).ae(pi**2 / 6) assert zeta(mpc(2)).ae(pi**2 / 6) assert zeta(100).ae(1) assert zeta(0).ae(-0.5) assert zeta(0.5).ae(-1.46035450880958681) assert zeta(-1).ae(-mpf(1)/12) assert zeta(-2) == 0 assert zeta(-3).ae(mpf(1)/120) assert zeta(-4) == 0 assert zeta(-100) == 0 assert isnan(zeta(nan)) # Zeros in the critical strip assert zeta(mpc(0.5, 14.1347251417346937904)).ae(0) assert zeta(mpc(0.5, 21.0220396387715549926)).ae(0) assert zeta(mpc(0.5, 25.0108575801456887632)).ae(0) mp.dps = 50 im = '236.5242296658162058024755079556629786895294952121891237' assert zeta(mpc(0.5, im)).ae(0, 1e-46) mp.dps = 15 # Complex reflection formula assert (zeta(-60+3j) / 10**34).ae(8.6270183987866146+15.337398548226238j) def test_altzeta(): mp.dps = 15 assert altzeta(-2) == 0 assert altzeta(-4) == 0 assert altzeta(-100) == 0 assert altzeta(0) == 0.5 assert altzeta(-1) == 0.25 assert altzeta(-3) == -0.125 assert altzeta(-5) == 0.25 assert altzeta(-21) == 1180529130.25 assert altzeta(1).ae(log(2)) assert altzeta(2).ae(pi**2/12) assert altzeta(10).ae(73*pi**10/6842880) assert altzeta(50) < 1 assert altzeta(60, rounding='d') < 1 assert altzeta(60, rounding='u') == 1 assert altzeta(10000, rounding='d') < 1 assert altzeta(10000, rounding='u') == 1 assert altzeta(3+0j) == altzeta(3) s = 3+4j assert altzeta(s).ae((1-2**(1-s))*zeta(s)) s = -3+4j assert altzeta(s).ae((1-2**(1-s))*zeta(s)) assert altzeta(-100.5).ae(4.58595480083585913e+108) assert altzeta(1.3).ae(0.73821404216623045) def test_zeta_huge(): mp.dps = 15 assert zeta(inf) == 1 mp.dps = 50 assert zeta(100).ae('1.0000000000000000000000000000007888609052210118073522') assert zeta(40*pi).ae('1.0000000000000000000000000000000000000148407238666182') mp.dps = 10000 v = zeta(33000) mp.dps = 15 assert str(v-1) == '1.02363019598118e-9934' assert zeta(pi*1000, rounding=round_up) > 1 assert zeta(3000, rounding=round_up) > 1 assert zeta(pi*1000) == 1 assert zeta(3000) == 1 def test_zeta_negative(): mp.dps = 150 a = -pi*10**40 mp.dps = 15 assert str(zeta(a)) == '2.55880492708712e+1233536161668617575553892558646631323374078' mp.dps = 50 assert str(zeta(a)) == '2.5588049270871154960875033337384432038436330847333e+1233536161668617575553892558646631323374078' mp.dps = 15 def test_polygamma(): mp.dps = 15 psi0 = lambda z: psi(0,z) psi1 = lambda z: psi(1,z) assert psi0(3) == psi(0,3) == digamma(3) #assert psi2(3) == psi(2,3) == tetragamma(3) #assert psi3(3) == psi(3,3) == pentagamma(3) assert psi0(pi).ae(0.97721330794200673) assert psi0(-pi).ae(7.8859523853854902) assert psi0(-pi+1).ae(7.5676424992016996) assert psi0(pi+j).ae(1.04224048313859376 + 0.35853686544063749j) assert psi0(-pi-j).ae(1.3404026194821986 - 2.8824392476809402j) assert findroot(psi0, 1).ae(1.4616321449683622) assert psi0(inf) == inf assert psi1(inf) == 0 assert psi(2,inf) == 0 assert psi1(pi).ae(0.37424376965420049) assert psi1(-pi).ae(53.030438740085385) assert psi1(pi+j).ae(0.32935710377142464 - 0.12222163911221135j) assert psi1(-pi-j).ae(-0.30065008356019703 + 0.01149892486928227j) assert (10**6*psi(4,1+10*pi*j)).ae(-6.1491803479004446 - 0.3921316371664063j) assert psi0(1+10*pi*j).ae(3.4473994217222650 + 1.5548808324857071j) assert isnan(psi0(nan)) assert isnan(psi0(-inf)) assert psi0(-100.5).ae(4.615124601338064) assert psi0(3+0j).ae(psi0(3)) assert psi0(-100+3j).ae(4.6106071768714086321+3.1117510556817394626j) assert isnan(psi(2,mpc(0,inf))) assert isnan(psi(2,mpc(0,nan))) assert isnan(psi(2,mpc(0,-inf))) assert isnan(psi(2,mpc(1,inf))) assert isnan(psi(2,mpc(1,nan))) assert isnan(psi(2,mpc(1,-inf))) assert isnan(psi(2,mpc(inf,inf))) assert isnan(psi(2,mpc(nan,nan))) assert isnan(psi(2,mpc(-inf,-inf))) def test_polygamma_high_prec(): mp.dps = 100 assert str(psi(0,pi)) == "0.9772133079420067332920694864061823436408346099943256380095232865318105924777141317302075654362928734" assert str(psi(10,pi)) == "-12.98876181434889529310283769414222588307175962213707170773803550518307617769657562747174101900659238" def test_polygamma_identities(): mp.dps = 15 psi0 = lambda z: psi(0,z) psi1 = lambda z: psi(1,z) psi2 = lambda z: psi(2,z) assert psi0(0.5).ae(-euler-2*log(2)) assert psi0(1).ae(-euler) assert psi1(0.5).ae(0.5*pi**2) assert psi1(1).ae(pi**2/6) assert psi1(0.25).ae(pi**2 + 8*catalan) assert psi2(1).ae(-2*apery) mp.dps = 20 u = -182*apery+4*sqrt(3)*pi**3 mp.dps = 15 assert psi(2,5/6.).ae(u) assert psi(3,0.5).ae(pi**4) def test_foxtrot_identity(): # A test of the complex digamma function. # See http://mathworld.wolfram.com/FoxTrotSeries.html and # http://mathworld.wolfram.com/DigammaFunction.html psi0 = lambda z: psi(0,z) mp.dps = 50 a = (-1)**fraction(1,3) b = (-1)**fraction(2,3) x = -psi0(0.5*a) - psi0(-0.5*b) + psi0(0.5*(1+a)) + psi0(0.5*(1-b)) y = 2*pi*sech(0.5*sqrt(3)*pi) assert x.ae(y) mp.dps = 15 def test_polygamma_high_order(): mp.dps = 100 assert str(psi(50, pi)) == "-1344100348958402765749252447726432491812.641985273160531055707095989227897753035823152397679626136483" assert str(psi(50, pi + 14*e)) == "-0.00000000000000000189793739550804321623512073101895801993019919886375952881053090844591920308111549337295143780341396" assert str(psi(50, pi + 14*e*j)) == ("(-0.0000000000000000522516941152169248975225472155683565752375889510631513244785" "9377385233700094871256507814151956624433 - 0.00000000000000001813157041407010184" "702414110218205348527862196327980417757665282244728963891298080199341480881811613j)") mp.dps = 15 assert str(psi(50, pi)) == "-1.34410034895841e+39" assert str(psi(50, pi + 14*e)) == "-1.89793739550804e-18" assert str(psi(50, pi + 14*e*j)) == "(-5.2251694115217e-17 - 1.81315704140701e-17j)" def test_harmonic(): mp.dps = 15 assert harmonic(0) == 0 assert harmonic(1) == 1 assert harmonic(2) == 1.5 assert harmonic(3).ae(1. + 1./2 + 1./3) assert harmonic(10**10).ae(23.603066594891989701) assert harmonic(10**1000).ae(2303.162308658947) assert harmonic(0.5).ae(2-2*log(2)) assert harmonic(inf) == inf assert harmonic(2+0j) == 1.5+0j assert harmonic(1+2j).ae(1.4918071802755104+0.92080728264223022j) def test_gamma_huge_1(): mp.dps = 500 x = mpf(10**10) / 7 mp.dps = 15 assert str(gamma(x)) == "6.26075321389519e+12458010678" mp.dps = 50 assert str(gamma(x)) == "6.2607532138951929201303779291707455874010420783933e+12458010678" mp.dps = 15 def test_gamma_huge_2(): mp.dps = 500 x = mpf(10**100) / 19 mp.dps = 15 assert str(gamma(x)) == (\ "1.82341134776679e+5172997469323364168990133558175077136829182824042201886051511" "9656908623426021308685461258226190190661") mp.dps = 50 assert str(gamma(x)) == (\ "1.82341134776678875374414910350027596939980412984e+5172997469323364168990133558" "1750771368291828240422018860515119656908623426021308685461258226190190661") def test_gamma_huge_3(): mp.dps = 500 x = 10**80 // 3 + 10**70*j / 7 mp.dps = 15 y = gamma(x) assert str(y.real) == (\ "-6.82925203918106e+2636286142112569524501781477865238132302397236429627932441916" "056964386399485392600") assert str(y.imag) == (\ "8.54647143678418e+26362861421125695245017814778652381323023972364296279324419160" "56964386399485392600") mp.dps = 50 y = gamma(x) assert str(y.real) == (\ "-6.8292520391810548460682736226799637356016538421817e+26362861421125695245017814" "77865238132302397236429627932441916056964386399485392600") assert str(y.imag) == (\ "8.5464714367841748507479306948130687511711420234015e+263628614211256952450178147" "7865238132302397236429627932441916056964386399485392600") def test_gamma_huge_4(): x = 3200+11500j mp.dps = 15 assert str(gamma(x)) == \ "(8.95783268539713e+5164 - 1.94678798329735e+5164j)" mp.dps = 50 assert str(gamma(x)) == (\ "(8.9578326853971339570292952697675570822206567327092e+5164" " - 1.9467879832973509568895402139429643650329524144794e+51" "64j)") mp.dps = 15 def test_gamma_huge_5(): mp.dps = 500 x = 10**60 * j / 3 mp.dps = 15 y = gamma(x) assert str(y.real) == "-3.27753899634941e-227396058973640224580963937571892628368354580620654233316839" assert str(y.imag) == "-7.1519888950416e-227396058973640224580963937571892628368354580620654233316841" mp.dps = 50 y = gamma(x) assert str(y.real) == (\ "-3.2775389963494132168950056995974690946983219123935e-22739605897364022458096393" "7571892628368354580620654233316839") assert str(y.imag) == (\ "-7.1519888950415979749736749222530209713136588885897e-22739605897364022458096393" "7571892628368354580620654233316841") mp.dps = 15 def test_gamma_huge_6(): return mp.dps = 500 x = -10**10 + mpf(10)**(-175)*j mp.dps = 15 assert str(gamma(x)) == \ "(1.86729378905343e-95657055178 - 4.29960285282433e-95657055002j)" mp.dps = 50 assert str(gamma(x)) == (\ "(1.8672937890534298925763143275474177736153484820662e-9565705517" "8 - 4.2996028528243336966001185406200082244961757496106e-9565705" "5002j)") mp.dps = 15 def test_gamma_huge_7(): mp.dps = 100 a = 3 + j/mpf(10)**1000 mp.dps = 15 y = gamma(a) assert str(y.real) == "2.0" # wrong #assert str(y.imag) == "2.16735365342606e-1000" assert str(y.imag) == "1.84556867019693e-1000" mp.dps = 50 y = gamma(a) assert str(y.real) == "2.0" #assert str(y.imag) == "2.1673536534260596065418805612488708028522563689298e-1000" assert str(y.imag) == "1.8455686701969342787869758198351951379156813281202e-1000" def test_stieltjes(): mp.dps = 15 assert stieltjes(0).ae(+euler) mp.dps = 25 assert stieltjes(1).ae('-0.07281584548367672486058637587') assert stieltjes(2).ae('-0.009690363192872318484530386035') assert stieltjes(3).ae('0.002053834420303345866160046543') assert stieltjes(4).ae('0.002325370065467300057468170178') mp.dps = 15 assert stieltjes(1).ae(-0.07281584548367672486058637587) assert stieltjes(2).ae(-0.009690363192872318484530386035) assert stieltjes(3).ae(0.002053834420303345866160046543) assert stieltjes(4).ae(0.0023253700654673000574681701775) def test_barnesg(): mp.dps = 15 assert barnesg(0) == barnesg(-1) == 0 assert [superfac(i) for i in range(8)] == [1, 1, 2, 12, 288, 34560, 24883200, 125411328000] assert str(superfac(1000)) == '3.24570818422368e+1177245' assert isnan(barnesg(nan)) assert isnan(superfac(nan)) assert isnan(hyperfac(nan)) assert barnesg(inf) == inf assert superfac(inf) == inf assert hyperfac(inf) == inf assert isnan(superfac(-inf)) assert barnesg(0.7).ae(0.8068722730141471) assert barnesg(2+3j).ae(-0.17810213864082169+0.04504542715447838j) assert [hyperfac(n) for n in range(7)] == [1, 1, 4, 108, 27648, 86400000, 4031078400000] assert [hyperfac(n) for n in range(0,-7,-1)] == [1,1,-1,-4,108,27648,-86400000] a = barnesg(-3+0j) assert a == 0 and isinstance(a, mpc) a = hyperfac(-3+0j) assert a == -4 and isinstance(a, mpc) def test_polylog(): mp.dps = 15 zs = [mpmathify(z) for z in [0, 0.5, 0.99, 4, -0.5, -4, 1j, 3+4j]] for z in zs: assert polylog(1, z).ae(-log(1-z)) for z in zs: assert polylog(0, z).ae(z/(1-z)) for z in zs: assert polylog(-1, z).ae(z/(1-z)**2) for z in zs: assert polylog(-2, z).ae(z*(1+z)/(1-z)**3) for z in zs: assert polylog(-3, z).ae(z*(1+4*z+z**2)/(1-z)**4) assert polylog(3, 7).ae(5.3192579921456754382-5.9479244480803301023j) assert polylog(3, -7).ae(-4.5693548977219423182) assert polylog(2, 0.9).ae(1.2997147230049587252) assert polylog(2, -0.9).ae(-0.75216317921726162037) assert polylog(2, 0.9j).ae(-0.17177943786580149299+0.83598828572550503226j) assert polylog(2, 1.1).ae(1.9619991013055685931-0.2994257606855892575j) assert polylog(2, -1.1).ae(-0.89083809026228260587) assert polylog(2, 1.1*sqrt(j)).ae(0.58841571107611387722+1.09962542118827026011j) assert polylog(-2, 0.9).ae(1710) assert polylog(-2, -0.9).ae(-90/6859.) assert polylog(3, 0.9).ae(1.0496589501864398696) assert polylog(-3, 0.9).ae(48690) assert polylog(-3, -4).ae(-0.0064) assert polylog(0.5+j/3, 0.5+j/2).ae(0.31739144796565650535 + 0.99255390416556261437j) assert polylog(3+4j,1).ae(zeta(3+4j)) assert polylog(3+4j,-1).ae(-altzeta(3+4j)) def test_bell_polyexp(): mp.dps = 15 # TODO: more tests for polyexp assert (polyexp(0,1e-10)*10**10).ae(1.00000000005) assert (polyexp(1,1e-10)*10**10).ae(1.0000000001) assert polyexp(5,3j).ae(-607.7044517476176454+519.962786482001476087j) assert polyexp(-1,3.5).ae(12.09537536175543444) # bell(0,x) = 1 assert bell(0,0) == 1 assert bell(0,1) == 1 assert bell(0,2) == 1 assert bell(0,inf) == 1 assert bell(0,-inf) == 1 assert isnan(bell(0,nan)) # bell(1,x) = x assert bell(1,4) == 4 assert bell(1,0) == 0 assert bell(1,inf) == inf assert bell(1,-inf) == -inf assert isnan(bell(1,nan)) # bell(2,x) = x*(1+x) assert bell(2,-1) == 0 assert bell(2,0) == 0 # large orders / arguments assert bell(10) == 115975 assert bell(10,1) == 115975 assert bell(10, -8) == 11054008 assert bell(5,-50) == -253087550 assert bell(50,-50).ae('3.4746902914629720259e74') mp.dps = 80 assert bell(50,-50) == 347469029146297202586097646631767227177164818163463279814268368579055777450 assert bell(40,50) == 5575520134721105844739265207408344706846955281965031698187656176321717550 assert bell(74) == 5006908024247925379707076470957722220463116781409659160159536981161298714301202 mp.dps = 15 assert bell(10,20j) == 7504528595600+15649605360020j # continuity of the generalization assert bell(0.5,0).ae(sinc(pi*0.5)) def test_primezeta(): mp.dps = 15 assert primezeta(0.9).ae(1.8388316154446882243 + 3.1415926535897932385j) assert primezeta(4).ae(0.076993139764246844943) assert primezeta(1) == inf assert primezeta(inf) == 0 assert isnan(primezeta(nan)) def test_rs_zeta(): mp.dps = 15 assert zeta(0.5+100000j).ae(1.0730320148577531321 + 5.7808485443635039843j) assert zeta(0.75+100000j).ae(1.837852337251873704 + 1.9988492668661145358j) assert zeta(0.5+1000000j, derivative=3).ae(1647.7744105852674733 - 1423.1270943036622097j) assert zeta(1+1000000j, derivative=3).ae(3.4085866124523582894 - 18.179184721525947301j) assert zeta(1+1000000j, derivative=1).ae(-0.10423479366985452134 - 0.74728992803359056244j) assert zeta(0.5-1000000j, derivative=1).ae(11.636804066002521459 + 17.127254072212996004j) # Additional sanity tests using fp arithmetic. # Some more high-precision tests are found in the docstrings def ae(x, y, tol=1e-6): return abs(x-y) < tol*abs(y) assert ae(fp.zeta(0.5-100000j), 1.0730320148577531321 - 5.7808485443635039843j) assert ae(fp.zeta(0.75-100000j), 1.837852337251873704 - 1.9988492668661145358j) assert ae(fp.zeta(0.5+1e6j), 0.076089069738227100006 + 2.8051021010192989554j) assert ae(fp.zeta(0.5+1e6j, derivative=1), 11.636804066002521459 - 17.127254072212996004j) assert ae(fp.zeta(1+1e6j), 0.94738726251047891048 + 0.59421999312091832833j) assert ae(fp.zeta(1+1e6j, derivative=1), -0.10423479366985452134 - 0.74728992803359056244j) assert ae(fp.zeta(0.5+100000j, derivative=1), 10.766962036817482375 - 30.92705282105996714j) assert ae(fp.zeta(0.5+100000j, derivative=2), -119.40515625740538429 + 217.14780631141830251j) assert ae(fp.zeta(0.5+100000j, derivative=3), 1129.7550282628460881 - 1685.4736895169690346j) assert ae(fp.zeta(0.5+100000j, derivative=4), -10407.160819314958615 + 13777.786698628045085j) assert ae(fp.zeta(0.75+100000j, derivative=1), -0.41742276699594321475 - 6.4453816275049955949j) assert ae(fp.zeta(0.75+100000j, derivative=2), -9.214314279161977266 + 35.07290795337967899j) assert ae(fp.zeta(0.75+100000j, derivative=3), 110.61331857820103469 - 236.87847130518129926j) assert ae(fp.zeta(0.75+100000j, derivative=4), -1054.334275898559401 + 1769.9177890161596383j) def test_siegelz(): mp.dps = 15 assert siegelz(100000).ae(5.87959246868176504171) assert siegelz(100000, derivative=2).ae(-54.1172711010126452832) assert siegelz(100000, derivative=3).ae(-278.930831343966552538) assert siegelz(100000+j,derivative=1).ae(678.214511857070283307-379.742160779916375413j) def test_zeta_near_1(): # Test for a former bug in mpf_zeta and mpc_zeta mp.dps = 15 s1 = fadd(1, '1e-10', exact=True) s2 = fadd(1, '-1e-10', exact=True) s3 = fadd(1, '1e-10j', exact=True) assert zeta(s1).ae(1.000000000057721566490881444e10) assert zeta(s2).ae(-9.99999999942278433510574872e9) z = zeta(s3) assert z.real.ae(0.57721566490153286060) assert z.imag.ae(-9.9999999999999999999927184e9) mp.dps = 30 s1 = fadd(1, '1e-50', exact=True) s2 = fadd(1, '-1e-50', exact=True) s3 = fadd(1, '1e-50j', exact=True) assert zeta(s1).ae('1e50') assert zeta(s2).ae('-1e50') z = zeta(s3) assert z.real.ae('0.57721566490153286060651209008240243104215933593992') assert z.imag.ae('-1e50') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_functions2.py0000644000175000017500000022353612014170666027245 0ustar georgeskgeorgeskimport math from sympy.mpmath import * def test_bessel(): mp.dps = 15 assert j0(1).ae(0.765197686557966551) assert j0(pi).ae(-0.304242177644093864) assert j0(1000).ae(0.0247866861524201746) assert j0(-25).ae(0.0962667832759581162) assert j1(1).ae(0.440050585744933516) assert j1(pi).ae(0.284615343179752757) assert j1(1000).ae(0.00472831190708952392) assert j1(-25).ae(0.125350249580289905) assert besselj(5,1).ae(0.000249757730211234431) assert besselj(5+0j,1).ae(0.000249757730211234431) assert besselj(5,pi).ae(0.0521411843671184747) assert besselj(5,1000).ae(0.00502540694523318607) assert besselj(5,-25).ae(0.0660079953984229934) assert besselj(-3,2).ae(-0.128943249474402051) assert besselj(-4,2).ae(0.0339957198075684341) assert besselj(3,3+2j).ae(0.424718794929639595942 + 0.625665327745785804812j) assert besselj(0.25,4).ae(-0.374760630804249715) assert besselj(1+2j,3+4j).ae(0.319247428741872131 - 0.669557748880365678j) assert (besselj(3, 10**10) * 10**5).ae(0.76765081748139204023) assert bessely(-0.5, 0) == 0 assert bessely(0.5, 0) == -inf assert bessely(1.5, 0) == -inf assert bessely(0,0) == -inf assert bessely(-0.4, 0) == -inf assert bessely(-0.6, 0) == inf assert bessely(-1, 0) == inf assert bessely(-1.4, 0) == inf assert bessely(-1.6, 0) == -inf assert bessely(-1, 0) == inf assert bessely(-2, 0) == -inf assert bessely(-3, 0) == inf assert bessely(0.5, 0) == -inf assert bessely(1, 0) == -inf assert bessely(1.5, 0) == -inf assert bessely(2, 0) == -inf assert bessely(2.5, 0) == -inf assert bessely(3, 0) == -inf assert bessely(0,0.5).ae(-0.44451873350670655715) assert bessely(1,0.5).ae(-1.4714723926702430692) assert bessely(-1,0.5).ae(1.4714723926702430692) assert bessely(3.5,0.5).ae(-138.86400867242488443) assert bessely(0,3+4j).ae(4.6047596915010138655-8.8110771408232264208j) assert bessely(0,j).ae(-0.26803248203398854876+1.26606587775200833560j) assert (bessely(3, 10**10) * 10**5).ae(0.21755917537013204058) assert besseli(0,0) == 1 assert besseli(1,0) == 0 assert besseli(2,0) == 0 assert besseli(-1,0) == 0 assert besseli(-2,0) == 0 assert besseli(0,0.5).ae(1.0634833707413235193) assert besseli(1,0.5).ae(0.25789430539089631636) assert besseli(-1,0.5).ae(0.25789430539089631636) assert besseli(3.5,0.5).ae(0.00068103597085793815863) assert besseli(0,3+4j).ae(-3.3924877882755196097-1.3239458916287264815j) assert besseli(0,j).ae(besselj(0,1)) assert (besseli(3, 10**10) * mpf(10)**(-4342944813)).ae(4.2996028505491271875) assert besselk(0,0) == inf assert besselk(1,0) == inf assert besselk(2,0) == inf assert besselk(-1,0) == inf assert besselk(-2,0) == inf assert besselk(0,0.5).ae(0.92441907122766586178) assert besselk(1,0.5).ae(1.6564411200033008937) assert besselk(-1,0.5).ae(1.6564411200033008937) assert besselk(3.5,0.5).ae(207.48418747548460607) assert besselk(0,3+4j).ae(-0.007239051213570155013+0.026510418350267677215j) assert besselk(0,j).ae(-0.13863371520405399968-1.20196971531720649914j) assert (besselk(3, 10**10) * mpf(10)**4342944824).ae(1.1628981033356187851) def test_bessel_zeros(): mp.dps = 15 assert besseljzero(0,1).ae(2.40482555769577276869) assert besseljzero(2,1).ae(5.1356223018406825563) assert besseljzero(1,50).ae(157.86265540193029781) assert besseljzero(10,1).ae(14.475500686554541220) assert besseljzero(0.5,3).ae(9.4247779607693797153) assert besseljzero(2,1,1).ae(3.0542369282271403228) assert besselyzero(0,1).ae(0.89357696627916752158) assert besselyzero(2,1).ae(3.3842417671495934727) assert besselyzero(1,50).ae(156.29183520147840108) assert besselyzero(10,1).ae(12.128927704415439387) assert besselyzero(0.5,3).ae(7.8539816339744830962) assert besselyzero(2,1,1).ae(5.0025829314460639452) def test_hankel(): mp.dps = 15 assert hankel1(0,0.5).ae(0.93846980724081290423-0.44451873350670655715j) assert hankel1(1,0.5).ae(0.2422684576748738864-1.4714723926702430692j) assert hankel1(-1,0.5).ae(-0.2422684576748738864+1.4714723926702430692j) assert hankel1(1.5,0.5).ae(0.0917016996256513026-2.5214655504213378514j) assert hankel1(1.5,3+4j).ae(0.0066806866476728165382-0.0036684231610839127106j) assert hankel2(0,0.5).ae(0.93846980724081290423+0.44451873350670655715j) assert hankel2(1,0.5).ae(0.2422684576748738864+1.4714723926702430692j) assert hankel2(-1,0.5).ae(-0.2422684576748738864-1.4714723926702430692j) assert hankel2(1.5,0.5).ae(0.0917016996256513026+2.5214655504213378514j) assert hankel2(1.5,3+4j).ae(14.783528526098567526-7.397390270853446512j) def test_struve(): mp.dps = 15 assert struveh(2,3).ae(0.74238666967748318564) assert struveh(-2.5,3).ae(0.41271003220971599344) assert struvel(2,3).ae(1.7476573277362782744) assert struvel(-2.5,3).ae(1.5153394466819651377) def test_whittaker(): mp.dps = 15 assert whitm(2,3,4).ae(49.753745589025246591) assert whitw(2,3,4).ae(14.111656223052932215) def test_kelvin(): mp.dps = 15 assert ber(2,3).ae(0.80836846563726819091) assert ber(3,4).ae(-0.28262680167242600233) assert ber(-3,2).ae(-0.085611448496796363669) assert bei(2,3).ae(-0.89102236377977331571) assert bei(-3,2).ae(-0.14420994155731828415) assert ker(2,3).ae(0.12839126695733458928) assert ker(-3,2).ae(-0.29802153400559142783) assert ker(0.5,3).ae(-0.085662378535217097524) assert kei(2,3).ae(0.036804426134164634000) assert kei(-3,2).ae(0.88682069845786731114) assert kei(0.5,3).ae(0.013633041571314302948) def test_hyper_misc(): mp.dps = 15 assert hyp0f1(1,0) == 1 assert hyp1f1(1,2,0) == 1 assert hyp1f2(1,2,3,0) == 1 assert hyp2f1(1,2,3,0) == 1 assert hyp2f2(1,2,3,4,0) == 1 assert hyp2f3(1,2,3,4,5,0) == 1 # Degenerate case: 0F0 assert hyper([],[],0) == 1 assert hyper([],[],-2).ae(exp(-2)) # Degenerate case: 1F0 assert hyper([2],[],1.5) == 4 # assert hyp2f1((1,3),(2,3),(5,6),mpf(27)/32).ae(1.6) assert hyp2f1((1,4),(1,2),(3,4),mpf(80)/81).ae(1.8) assert hyp2f1((2,3),(1,1),(3,2),(2+j)/3).ae(1.327531603558679093+0.439585080092769253j) mp.dps = 25 v = mpc('1.2282306665029814734863026', '-0.1225033830118305184672133') assert hyper([(3,4),2+j,1],[1,5,j/3],mpf(1)/5+j/8).ae(v) mp.dps = 15 def test_elliptic_integrals(): mp.dps = 15 assert ellipk(0).ae(pi/2) assert ellipk(0.5).ae(gamma(0.25)**2/(4*sqrt(pi))) assert ellipk(1) == inf assert ellipk(1+0j) == inf assert ellipk(-1).ae('1.3110287771460599052') assert ellipk(-2).ae('1.1714200841467698589') assert isinstance(ellipk(-2), mpf) assert isinstance(ellipe(-2), mpf) assert ellipk(-50).ae('0.47103424540873331679') mp.dps = 30 n1 = +fraction(99999,100000) n2 = +fraction(100001,100000) mp.dps = 15 assert ellipk(n1).ae('7.1427724505817781901') assert ellipk(n2).ae(mpc('7.1427417367963090109', '-1.5707923998261688019')) assert ellipe(n1).ae('1.0000332138990829170') v = ellipe(n2) assert v.real.ae('0.999966786328145474069137') assert (v.imag*10**6).ae('7.853952181727432') assert ellipk(2).ae(mpc('1.3110287771460599052', '-1.3110287771460599052')) assert ellipk(50).ae(mpc('0.22326753950210985451', '-0.47434723226254522087')) assert ellipk(3+4j).ae(mpc('0.91119556380496500866', '0.63133428324134524388')) assert ellipk(3-4j).ae(mpc('0.91119556380496500866', '-0.63133428324134524388')) assert ellipk(-3+4j).ae(mpc('0.95357894880405122483', '0.23093044503746114444')) assert ellipk(-3-4j).ae(mpc('0.95357894880405122483', '-0.23093044503746114444')) assert isnan(ellipk(nan)) assert isnan(ellipe(nan)) assert ellipk(inf) == 0 assert isinstance(ellipk(inf), mpc) assert ellipk(-inf) == 0 assert ellipk(1+0j) == inf assert ellipe(0).ae(pi/2) assert ellipe(0.5).ae(pi**(mpf(3)/2)/gamma(0.25)**2 +gamma(0.25)**2/(8*sqrt(pi))) assert ellipe(1) == 1 assert ellipe(1+0j) == 1 assert ellipe(inf) == mpc(0,inf) assert ellipe(-inf) == inf assert ellipe(3+4j).ae(1.4995535209333469543-1.5778790079127582745j) assert ellipe(3-4j).ae(1.4995535209333469543+1.5778790079127582745j) assert ellipe(-3+4j).ae(2.5804237855343377803-0.8306096791000413778j) assert ellipe(-3-4j).ae(2.5804237855343377803+0.8306096791000413778j) assert ellipe(2).ae(0.59907011736779610372+0.59907011736779610372j) assert ellipe('1e-1000000000').ae(pi/2) assert ellipk('1e-1000000000').ae(pi/2) assert ellipe(-pi).ae(2.4535865983838923) mp.dps = 50 assert ellipk(1/pi).ae('1.724756270009501831744438120951614673874904182624739673') assert ellipe(1/pi).ae('1.437129808135123030101542922290970050337425479058225712') assert ellipk(-10*pi).ae('0.5519067523886233967683646782286965823151896970015484512') assert ellipe(-10*pi).ae('5.926192483740483797854383268707108012328213431657645509') v = ellipk(pi) assert v.real.ae('0.973089521698042334840454592642137667227167622330325225') assert v.imag.ae('-1.156151296372835303836814390793087600271609993858798016') v = ellipe(pi) assert v.real.ae('0.4632848917264710404078033487934663562998345622611263332') assert v.imag.ae('1.0637961621753130852473300451583414489944099504180510966') mp.dps = 15 def test_exp_integrals(): mp.dps = 15 x = +e z = e + sqrt(3)*j assert ei(x).ae(8.21168165538361560) assert li(x).ae(1.89511781635593676) assert si(x).ae(1.82104026914756705) assert ci(x).ae(0.213958001340379779) assert shi(x).ae(4.11520706247846193) assert chi(x).ae(4.09647459290515367) assert fresnels(x).ae(0.437189718149787643) assert fresnelc(x).ae(0.401777759590243012) assert airyai(x).ae(0.0108502401568586681) assert airybi(x).ae(8.98245748585468627) assert ei(z).ae(3.72597969491314951 + 7.34213212314224421j) assert li(z).ae(2.28662658112562502 + 1.50427225297269364j) assert si(z).ae(2.48122029237669054 + 0.12684703275254834j) assert ci(z).ae(0.169255590269456633 - 0.892020751420780353j) assert shi(z).ae(1.85810366559344468 + 3.66435842914920263j) assert chi(z).ae(1.86787602931970484 + 3.67777369399304159j) assert fresnels(z/3).ae(0.034534397197008182 + 0.754859844188218737j) assert fresnelc(z/3).ae(1.261581645990027372 + 0.417949198775061893j) assert airyai(z).ae(-0.0162552579839056062 - 0.0018045715700210556j) assert airybi(z).ae(-4.98856113282883371 + 2.08558537872180623j) assert li(0) == 0.0 assert li(1) == -inf assert li(inf) == inf assert isinstance(li(0.7), mpf) assert si(inf).ae(pi/2) assert si(-inf).ae(-pi/2) assert ci(inf) == 0 assert ci(0) == -inf assert isinstance(ei(-0.7), mpf) assert airyai(inf) == 0 assert airybi(inf) == inf assert airyai(-inf) == 0 assert airybi(-inf) == 0 assert fresnels(inf) == 0.5 assert fresnelc(inf) == 0.5 assert fresnels(-inf) == -0.5 assert fresnelc(-inf) == -0.5 assert shi(0) == 0 assert shi(inf) == inf assert shi(-inf) == -inf assert chi(0) == -inf assert chi(inf) == inf def test_ei(): mp.dps = 15 assert ei(0) == -inf assert ei(inf) == inf assert ei(-inf) == -0.0 assert ei(20+70j).ae(6.1041351911152984397e6 - 2.7324109310519928872e6j) # tests for the asymptotic expansion # values checked with Mathematica ExpIntegralEi mp.dps = 50 r = ei(20000) s = '3.8781962825045010930273870085501819470698476975019e+8681' assert str(r) == s r = ei(-200) s = '-6.8852261063076355977108174824557929738368086933303e-90' assert str(r) == s r =ei(20000 + 10*j) sre = '-3.255138234032069402493850638874410725961401274106e+8681' sim = '-2.1081929993474403520785942429469187647767369645423e+8681' assert str(r.real) == sre and str(r.imag) == sim mp.dps = 15 # More asymptotic expansions assert chi(-10**6+100j).ae('1.3077239389562548386e+434288 + 7.6808956999707408158e+434287j') assert shi(-10**6+100j).ae('-1.3077239389562548386e+434288 - 7.6808956999707408158e+434287j') mp.dps = 15 assert ei(10j).ae(-0.0454564330044553726+3.2291439210137706686j) assert ei(100j).ae(-0.0051488251426104921+3.1330217936839529126j) u = ei(fmul(10**20, j, exact=True)) assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) assert u.imag.ae(pi) assert ei(-10j).ae(-0.0454564330044553726-3.2291439210137706686j) assert ei(-100j).ae(-0.0051488251426104921-3.1330217936839529126j) u = ei(fmul(-10**20, j, exact=True)) assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) assert u.imag.ae(-pi) assert ei(10+10j).ae(-1576.1504265768517448+436.9192317011328140j) u = ei(-10+10j) assert u.real.ae(7.6698978415553488362543e-7, abs_eps=0, rel_eps=8*eps) assert u.imag.ae(3.141595611735621062025) def test_e1(): mp.dps = 15 assert e1(0) == inf assert e1(inf) == 0 assert e1(-inf) == mpc(-inf, -pi) assert e1(10j).ae(0.045456433004455372635 + 0.087551267423977430100j) assert e1(100j).ae(0.0051488251426104921444 - 0.0085708599058403258790j) assert e1(fmul(10**20, j, exact=True)).ae(6.4525128526578084421e-21 - 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) assert e1(-10j).ae(0.045456433004455372635 - 0.087551267423977430100j) assert e1(-100j).ae(0.0051488251426104921444 + 0.0085708599058403258790j) assert e1(fmul(-10**20, j, exact=True)).ae(6.4525128526578084421e-21 + 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) def test_expint(): mp.dps = 15 assert expint(0,0) == inf assert expint(0,1).ae(1/e) assert expint(0,1.5).ae(2/exp(1.5)/3) assert expint(1,1).ae(-ei(-1)) assert expint(2,0).ae(1) assert expint(3,0).ae(1/2.) assert expint(4,0).ae(1/3.) assert expint(-2, 0.5).ae(26/sqrt(e)) assert expint(-1,-1) == 0 assert expint(-2,-1).ae(-e) assert expint(5.5, 0).ae(2/9.) assert expint(2.00000001,0).ae(100000000./100000001) assert expint(2+3j,4-j).ae(0.0023461179581675065414+0.0020395540604713669262j) assert expint('1.01', '1e-1000').ae(99.9999999899412802) assert expint('1.000000000001', 3.5).ae(0.00697013985754701819446) assert expint(2,3).ae(3*ei(-3)+exp(-3)) assert (expint(10,20)*10**10).ae(0.694439055541231353) assert expint(3,inf) == 0 assert expint(3.2,inf) == 0 assert expint(3.2+2j,inf) == 0 assert expint(1,3j).ae(-0.11962978600800032763 + 0.27785620120457163717j) assert expint(1,3).ae(0.013048381094197037413) assert expint(1,-3).ae(-ei(3)-pi*j) #assert expint(3) == expint(1,3) assert expint(1,-20).ae(-25615652.66405658882 - 3.1415926535897932385j) assert expint(1000000,0).ae(1./999999) assert expint(0,2+3j).ae(-0.025019798357114678171 + 0.027980439405104419040j) assert expint(-1,2+3j).ae(-0.022411973626262070419 + 0.038058922011377716932j) assert expint(-1.5,0) == inf def test_trig_integrals(): mp.dps = 30 assert si(mpf(1)/1000000).ae('0.000000999999999999944444444444446111') assert ci(mpf(1)/1000000).ae('-13.2382948930629912435014366276') assert si(10**10).ae('1.5707963267075846569685111517747537') assert ci(10**10).ae('-4.87506025174822653785729773959e-11') assert si(10**100).ae(pi/2) assert (ci(10**100)*10**100).ae('-0.372376123661276688262086695553') assert si(-3) == -si(3) assert ci(-3).ae(ci(3) + pi*j) # Test complex structure mp.dps = 15 assert mp.ci(50).ae(-0.0056283863241163054402) assert mp.ci(50+2j).ae(-0.018378282946133067149+0.070352808023688336193j) assert mp.ci(20j).ae(1.28078263320282943611e7+1.5707963267949j) assert mp.ci(-2+20j).ae(-4.050116856873293505e6+1.207476188206989909e7j) assert mp.ci(-50+2j).ae(-0.0183782829461330671+3.0712398455661049023j) assert mp.ci(-50).ae(-0.0056283863241163054+3.1415926535897932385j) assert mp.ci(-50-2j).ae(-0.0183782829461330671-3.0712398455661049023j) assert mp.ci(-2-20j).ae(-4.050116856873293505e6-1.207476188206989909e7j) assert mp.ci(-20j).ae(1.28078263320282943611e7-1.5707963267949j) assert mp.ci(50-2j).ae(-0.018378282946133067149-0.070352808023688336193j) assert mp.si(50).ae(1.5516170724859358947) assert mp.si(50+2j).ae(1.497884414277228461-0.017515007378437448j) assert mp.si(20j).ae(1.2807826332028294459e7j) assert mp.si(-2+20j).ae(-1.20747603112735722103e7-4.050116856873293554e6j) assert mp.si(-50+2j).ae(-1.497884414277228461-0.017515007378437448j) assert mp.si(-50).ae(-1.5516170724859358947) assert mp.si(-50-2j).ae(-1.497884414277228461+0.017515007378437448j) assert mp.si(-2-20j).ae(-1.20747603112735722103e7+4.050116856873293554e6j) assert mp.si(-20j).ae(-1.2807826332028294459e7j) assert mp.si(50-2j).ae(1.497884414277228461+0.017515007378437448j) assert mp.chi(50j).ae(-0.0056283863241163054+1.5707963267948966192j) assert mp.chi(-2+50j).ae(-0.0183782829461330671+1.6411491348185849554j) assert mp.chi(-20).ae(1.28078263320282943611e7+3.1415926535898j) assert mp.chi(-20-2j).ae(-4.050116856873293505e6+1.20747571696809187053e7j) assert mp.chi(-2-50j).ae(-0.0183782829461330671-1.6411491348185849554j) assert mp.chi(-50j).ae(-0.0056283863241163054-1.5707963267948966192j) assert mp.chi(2-50j).ae(-0.0183782829461330671-1.500443518771208283j) assert mp.chi(20-2j).ae(-4.050116856873293505e6-1.20747603112735722951e7j) assert mp.chi(20).ae(1.2807826332028294361e7) assert mp.chi(2+50j).ae(-0.0183782829461330671+1.500443518771208283j) assert mp.shi(50j).ae(1.5516170724859358947j) assert mp.shi(-2+50j).ae(0.017515007378437448+1.497884414277228461j) assert mp.shi(-20).ae(-1.2807826332028294459e7) assert mp.shi(-20-2j).ae(4.050116856873293554e6-1.20747603112735722103e7j) assert mp.shi(-2-50j).ae(0.017515007378437448-1.497884414277228461j) assert mp.shi(-50j).ae(-1.5516170724859358947j) assert mp.shi(2-50j).ae(-0.017515007378437448-1.497884414277228461j) assert mp.shi(20-2j).ae(-4.050116856873293554e6-1.20747603112735722103e7j) assert mp.shi(20).ae(1.2807826332028294459e7) assert mp.shi(2+50j).ae(-0.017515007378437448+1.497884414277228461j) def ae(x,y,tol=1e-12): return abs(x-y) <= abs(y)*tol assert fp.ci(fp.inf) == 0 assert ae(fp.ci(fp.ninf), fp.pi*1j) assert ae(fp.si(fp.inf), fp.pi/2) assert ae(fp.si(fp.ninf), -fp.pi/2) assert fp.si(0) == 0 assert ae(fp.ci(50), -0.0056283863241163054402) assert ae(fp.ci(50+2j), -0.018378282946133067149+0.070352808023688336193j) assert ae(fp.ci(20j), 1.28078263320282943611e7+1.5707963267949j) assert ae(fp.ci(-2+20j), -4.050116856873293505e6+1.207476188206989909e7j) assert ae(fp.ci(-50+2j), -0.0183782829461330671+3.0712398455661049023j) assert ae(fp.ci(-50), -0.0056283863241163054+3.1415926535897932385j) assert ae(fp.ci(-50-2j), -0.0183782829461330671-3.0712398455661049023j) assert ae(fp.ci(-2-20j), -4.050116856873293505e6-1.207476188206989909e7j) assert ae(fp.ci(-20j), 1.28078263320282943611e7-1.5707963267949j) assert ae(fp.ci(50-2j), -0.018378282946133067149-0.070352808023688336193j) assert ae(fp.si(50), 1.5516170724859358947) assert ae(fp.si(50+2j), 1.497884414277228461-0.017515007378437448j) assert ae(fp.si(20j), 1.2807826332028294459e7j) assert ae(fp.si(-2+20j), -1.20747603112735722103e7-4.050116856873293554e6j) assert ae(fp.si(-50+2j), -1.497884414277228461-0.017515007378437448j) assert ae(fp.si(-50), -1.5516170724859358947) assert ae(fp.si(-50-2j), -1.497884414277228461+0.017515007378437448j) assert ae(fp.si(-2-20j), -1.20747603112735722103e7+4.050116856873293554e6j) assert ae(fp.si(-20j), -1.2807826332028294459e7j) assert ae(fp.si(50-2j), 1.497884414277228461+0.017515007378437448j) assert ae(fp.chi(50j), -0.0056283863241163054+1.5707963267948966192j) assert ae(fp.chi(-2+50j), -0.0183782829461330671+1.6411491348185849554j) assert ae(fp.chi(-20), 1.28078263320282943611e7+3.1415926535898j) assert ae(fp.chi(-20-2j), -4.050116856873293505e6+1.20747571696809187053e7j) assert ae(fp.chi(-2-50j), -0.0183782829461330671-1.6411491348185849554j) assert ae(fp.chi(-50j), -0.0056283863241163054-1.5707963267948966192j) assert ae(fp.chi(2-50j), -0.0183782829461330671-1.500443518771208283j) assert ae(fp.chi(20-2j), -4.050116856873293505e6-1.20747603112735722951e7j) assert ae(fp.chi(20), 1.2807826332028294361e7) assert ae(fp.chi(2+50j), -0.0183782829461330671+1.500443518771208283j) assert ae(fp.shi(50j), 1.5516170724859358947j) assert ae(fp.shi(-2+50j), 0.017515007378437448+1.497884414277228461j) assert ae(fp.shi(-20), -1.2807826332028294459e7) assert ae(fp.shi(-20-2j), 4.050116856873293554e6-1.20747603112735722103e7j) assert ae(fp.shi(-2-50j), 0.017515007378437448-1.497884414277228461j) assert ae(fp.shi(-50j), -1.5516170724859358947j) assert ae(fp.shi(2-50j), -0.017515007378437448-1.497884414277228461j) assert ae(fp.shi(20-2j), -4.050116856873293554e6-1.20747603112735722103e7j) assert ae(fp.shi(20), 1.2807826332028294459e7) assert ae(fp.shi(2+50j), -0.017515007378437448+1.497884414277228461j) def test_airy(): mp.dps = 15 assert (airyai(10)*10**10).ae(1.1047532552898687) assert (airybi(10)/10**9).ae(0.45564115354822515) assert (airyai(1000)*10**9158).ae(9.306933063179556004) assert (airybi(1000)/10**9154).ae(5.4077118391949465477) assert airyai(-1000).ae(0.055971895773019918842) assert airybi(-1000).ae(-0.083264574117080633012) assert (airyai(100+100j)*10**188).ae(2.9099582462207032076 + 2.353013591706178756j) assert (airybi(100+100j)/10**185).ae(1.7086751714463652039 - 3.1416590020830804578j) def test_hyper_0f1(): mp.dps = 15 v = 8.63911136507950465 assert hyper([],[(1,3)],1.5).ae(v) assert hyper([],[1/3.],1.5).ae(v) assert hyp0f1(1/3.,1.5).ae(v) assert hyp0f1((1,3),1.5).ae(v) # Asymptotic expansion assert hyp0f1(3,1e9).ae('4.9679055380347771271e+27455') assert hyp0f1(3,1e9j).ae('-2.1222788784457702157e+19410 + 5.0840597555401854116e+19410j') def test_hyper_1f1(): mp.dps = 15 v = 1.2917526488617656673 assert hyper([(1,2)],[(3,2)],0.7).ae(v) assert hyper([(1,2)],[(3,2)],0.7+0j).ae(v) assert hyper([0.5],[(3,2)],0.7).ae(v) assert hyper([0.5],[1.5],0.7).ae(v) assert hyper([0.5],[(3,2)],0.7+0j).ae(v) assert hyper([0.5],[1.5],0.7+0j).ae(v) assert hyper([(1,2)],[1.5+0j],0.7).ae(v) assert hyper([0.5+0j],[1.5],0.7).ae(v) assert hyper([0.5+0j],[1.5+0j],0.7+0j).ae(v) assert hyp1f1(0.5,1.5,0.7).ae(v) assert hyp1f1((1,2),1.5,0.7).ae(v) # Asymptotic expansion assert hyp1f1(2,3,1e10).ae('2.1555012157015796988e+4342944809') assert (hyp1f1(2,3,1e10j)*10**10).ae(-0.97501205020039745852 - 1.7462392454512132074j) # Shouldn't use asymptotic expansion assert hyp1f1(-2, 1, 10000).ae(49980001) # Bug assert hyp1f1(1j,fraction(1,3),0.415-69.739j).ae(25.857588206024346592 + 15.738060264515292063j) def test_hyper_2f1(): mp.dps = 15 v = 1.0652207633823291032 assert hyper([(1,2), (3,4)], [2], 0.3).ae(v) assert hyper([(1,2), 0.75], [2], 0.3).ae(v) assert hyper([0.5, 0.75], [2.0], 0.3).ae(v) assert hyper([0.5, 0.75], [2.0], 0.3+0j).ae(v) assert hyper([0.5+0j, (3,4)], [2.0], 0.3+0j).ae(v) assert hyper([0.5+0j, (3,4)], [2.0], 0.3).ae(v) assert hyper([0.5, (3,4)], [2.0+0j], 0.3).ae(v) assert hyper([0.5+0j, 0.75+0j], [2.0+0j], 0.3+0j).ae(v) v = 1.09234681096223231717 + 0.18104859169479360380j assert hyper([(1,2),0.75+j], [2], 0.5).ae(v) assert hyper([0.5,0.75+j], [2.0], 0.5).ae(v) assert hyper([0.5,0.75+j], [2.0], 0.5+0j).ae(v) assert hyper([0.5,0.75+j], [2.0+0j], 0.5+0j).ae(v) v = 0.9625 - 0.125j assert hyper([(3,2),-1],[4], 0.1+j/3).ae(v) assert hyper([1.5,-1.0],[4], 0.1+j/3).ae(v) assert hyper([1.5,-1.0],[4+0j], 0.1+j/3).ae(v) assert hyper([1.5+0j,-1.0+0j],[4+0j], 0.1+j/3).ae(v) v = 1.02111069501693445001 - 0.50402252613466859521j assert hyper([(2,10),(3,10)],[(4,10)],1.5).ae(v) assert hyper([0.2,(3,10)],[0.4+0j],1.5).ae(v) assert hyper([0.2,(3,10)],[0.4+0j],1.5+0j).ae(v) v = 0.76922501362865848528 + 0.32640579593235886194j assert hyper([(2,10),(3,10)],[(4,10)],4+2j).ae(v) assert hyper([0.2,(3,10)],[0.4+0j],4+2j).ae(v) assert hyper([0.2,(3,10)],[(4,10)],4+2j).ae(v) def test_hyper_2f1_hard(): mp.dps = 15 # Singular cases assert hyp2f1(2,-1,-1,3).ae(0.25) assert hyp2f1(2,-2,-2,3).ae(0.25) assert hyp2f1(2,-1,-1,3,eliminate=False) == 7 assert hyp2f1(2,-2,-2,3,eliminate=False) == 34 assert hyp2f1(2,-2,-3,3) == 14 assert hyp2f1(2,-3,-2,3) == inf assert hyp2f1(2,-1.5,-1.5,3) == 0.25 assert hyp2f1(1,2,3,0) == 1 assert hyp2f1(0,1,0,0) == 1 assert hyp2f1(0,0,0,0) == 1 assert isnan(hyp2f1(1,1,0,0)) assert hyp2f1(2,-1,-5, 0.25+0.25j).ae(1.1+0.1j) assert hyp2f1(2,-5,-5, 0.25+0.25j, eliminate=False).ae(163./128 + 125./128*j) assert hyp2f1(0.7235, -1, -5, 0.3).ae(1.04341) assert hyp2f1(0.7235, -5, -5, 0.3, eliminate=False).ae(1.2939225017815903812) assert hyp2f1(-1,-2,4,1) == 1.5 assert hyp2f1(1,2,-3,1) == inf assert hyp2f1(-2,-2,1,1) == 6 assert hyp2f1(1,-2,-4,1).ae(5./3) assert hyp2f1(0,-6,-4,1) == 1 assert hyp2f1(0,-3,-4,1) == 1 assert hyp2f1(0,0,0,1) == 1 assert hyp2f1(1,0,0,1,eliminate=False) == 1 assert hyp2f1(1,1,0,1) == inf assert hyp2f1(1,-6,-4,1) == inf assert hyp2f1(-7.2,-0.5,-4.5,1) == 0 assert hyp2f1(-7.2,-1,-2,1).ae(-2.6) assert hyp2f1(1,-0.5,-4.5, 1) == inf assert hyp2f1(1,0.5,-4.5, 1) == -inf # Check evaluation on / close to unit circle z = exp(j*pi/3) w = (nthroot(2,3)+1)*exp(j*pi/12)/nthroot(3,4)**3 assert hyp2f1('1/2','1/6','1/3', z).ae(w) assert hyp2f1('1/2','1/6','1/3', z.conjugate()).ae(w.conjugate()) assert hyp2f1(0.25, (1,3), 2, '0.999').ae(1.06826449496030635) assert hyp2f1(0.25, (1,3), 2, '1.001').ae(1.06867299254830309446-0.00001446586793975874j) assert hyp2f1(0.25, (1,3), 2, -1).ae(0.96656584492524351673) assert hyp2f1(0.25, (1,3), 2, j).ae(0.99041766248982072266+0.03777135604180735522j) assert hyp2f1(2,3,5,'0.99').ae(27.699347904322690602) assert hyp2f1((3,2),-0.5,3,'0.99').ae(0.68403036843911661388) assert hyp2f1(2,3,5,1j).ae(0.37290667145974386127+0.59210004902748285917j) assert fsum([hyp2f1((7,10),(2,3),(-1,2), 0.95*exp(j*k)) for k in range(1,15)]).ae(52.851400204289452922+6.244285013912953225j) assert fsum([hyp2f1((7,10),(2,3),(-1,2), 1.05*exp(j*k)) for k in range(1,15)]).ae(54.506013786220655330-3.000118813413217097j) assert fsum([hyp2f1((7,10),(2,3),(-1,2), exp(j*k)) for k in range(1,15)]).ae(55.792077935955314887+1.731986485778500241j) assert hyp2f1(2,2.5,-3.25,0.999).ae(218373932801217082543180041.33) # Branches assert hyp2f1(1,1,2,1.01).ae(4.5595744415723676911-3.1104877758314784539j) assert hyp2f1(1,1,2,1.01+0.1j).ae(2.4149427480552782484+1.4148224796836938829j) assert hyp2f1(1,1,2,3+4j).ae(0.14576709331407297807+0.48379185417980360773j) assert hyp2f1(1,1,2,4).ae(-0.27465307216702742285 - 0.78539816339744830962j) assert hyp2f1(1,1,2,-4).ae(0.40235947810852509365) # Other: # Cancellation with a large parameter involved (bug reported on sage-devel) assert hyp2f1(112, (51,10), (-9,10), -0.99999).ae(-1.6241361047970862961e-24, abs_eps=0, rel_eps=eps*16) def test_hyper_3f2_etc(): assert hyper([1,2,3],[1.5,8],-1).ae(0.67108992351533333030) assert hyper([1,2,3,4],[5,6,7], -1).ae(0.90232988035425506008) assert hyper([1,2,3],[1.25,5], 1).ae(28.924181329701905701) assert hyper([1,2,3,4],[5,6,7],5).ae(1.5192307344006649499-1.1529845225075537461j) assert hyper([1,2,3,4,5],[6,7,8,9],-1).ae(0.96288759462882357253) assert hyper([1,2,3,4,5],[6,7,8,9],1).ae(1.0428697385885855841) assert hyper([1,2,3,4,5],[6,7,8,9],5).ae(1.33980653631074769423-0.07143405251029226699j) assert hyper([1,2.79,3.08,4.37],[5.2,6.1,7.3],5).ae(1.0996321464692607231-1.7748052293979985001j) assert hyper([1,1,1],[1,2],1) == inf assert hyper([1,1,1],[2,(101,100)],1).ae(100.01621213528313220) # slow -- covered by doctests #assert hyper([1,1,1],[2,3],0.9999).ae(1.2897972005319693905) def test_hyper_u(): mp.dps = 15 assert hyperu(2,-3,0).ae(0.05) assert hyperu(2,-3.5,0).ae(4./99) assert hyperu(2,0,0) == 0.5 assert hyperu(-5,1,0) == -120 assert hyperu(-5,2,0) == inf assert hyperu(-5,-2,0) == 0 assert hyperu(7,7,3).ae(0.00014681269365593503986) #exp(3)*gammainc(-6,3) assert hyperu(2,-3,4).ae(0.011836478100271995559) assert hyperu(3,4,5).ae(1./125) assert hyperu(2,3,0.0625) == 256 assert hyperu(-1,2,0.25+0.5j) == -1.75+0.5j assert hyperu(0.5,1.5,7.25).ae(2/sqrt(29)) assert hyperu(2,6,pi).ae(0.55804439825913399130) assert (hyperu((3,2),8,100+201j)*10**4).ae(-0.3797318333856738798 - 2.9974928453561707782j) assert (hyperu((5,2),(-1,2),-5000)*10**10).ae(-5.6681877926881664678j) # XXX: fails because of undetected cancellation in low level series code # Alternatively: could use asymptotic series here, if convergence test # tweaked back to recognize this one #assert (hyperu((5,2),(-1,2),-500)*10**7).ae(-1.82526906001593252847j) def test_hyper_2f0(): mp.dps = 15 assert hyper([1,2],[],3) == hyp2f0(1,2,3) assert hyp2f0(2,3,7).ae(0.0116108068639728714668 - 0.0073727413865865802130j) assert hyp2f0(2,3,0) == 1 assert hyp2f0(0,0,0) == 1 assert hyp2f0(-1,-1,1).ae(2) assert hyp2f0(-4,1,1.5).ae(62.5) assert hyp2f0(-4,1,50).ae(147029801) assert hyp2f0(-4,1,0.0001).ae(0.99960011997600240000) assert hyp2f0(0.5,0.25,0.001).ae(1.0001251174078538115) assert hyp2f0(0.5,0.25,3+4j).ae(0.85548875824755163518 + 0.21636041283392292973j) # Important: cancellation check assert hyp2f0((1,6),(5,6),-0.02371708245126284498).ae(0.996785723120804309) # Should be exact; polynomial case assert hyp2f0(-2,1,0.5+0.5j,zeroprec=200) == 0 assert hyp2f0(1,-2,0.5+0.5j,zeroprec=200) == 0 # There used to be a bug in thresholds that made one of the following hang for d in [15, 50, 80]: mp.dps = d assert hyp2f0(1.5, 0.5, 0.009).ae('1.006867007239309717945323585695344927904000945829843527398772456281301440034218290443367270629519483 + 1.238277162240704919639384945859073461954721356062919829456053965502443570466701567100438048602352623e-46j') def test_hyper_1f2(): mp.dps = 15 assert hyper([1],[2,3],4) == hyp1f2(1,2,3,4) a1,b1,b2 = (1,10),(2,3),1./16 assert hyp1f2(a1,b1,b2,10).ae(298.7482725554557568) assert hyp1f2(a1,b1,b2,100).ae(224128961.48602947604) assert hyp1f2(a1,b1,b2,1000).ae(1.1669528298622675109e+27) assert hyp1f2(a1,b1,b2,10000).ae(2.4780514622487212192e+86) assert hyp1f2(a1,b1,b2,100000).ae(1.3885391458871523997e+274) assert hyp1f2(a1,b1,b2,1000000).ae('9.8851796978960318255e+867') assert hyp1f2(a1,b1,b2,10**7).ae('1.1505659189516303646e+2746') assert hyp1f2(a1,b1,b2,10**8).ae('1.4672005404314334081e+8685') assert hyp1f2(a1,b1,b2,10**20).ae('3.6888217332150976493e+8685889636') assert hyp1f2(a1,b1,b2,10*j).ae(-16.163252524618572878 - 44.321567896480184312j) assert hyp1f2(a1,b1,b2,100*j).ae(61938.155294517848171 + 637349.45215942348739j) assert hyp1f2(a1,b1,b2,1000*j).ae(8455057657257695958.7 + 6261969266997571510.6j) assert hyp1f2(a1,b1,b2,10000*j).ae(-8.9771211184008593089e+60 + 4.6550528111731631456e+59j) assert hyp1f2(a1,b1,b2,100000*j).ae(2.6398091437239324225e+193 + 4.1658080666870618332e+193j) assert hyp1f2(a1,b1,b2,1000000*j).ae('3.5999042951925965458e+613 + 1.5026014707128947992e+613j') assert hyp1f2(a1,b1,b2,10**7*j).ae('-8.3208715051623234801e+1939 - 3.6752883490851869429e+1941j') assert hyp1f2(a1,b1,b2,10**8*j).ae('2.0724195707891484454e+6140 - 1.3276619482724266387e+6141j') assert hyp1f2(a1,b1,b2,10**20*j).ae('-1.1734497974795488504e+6141851462 + 1.1498106965385471542e+6141851462j') def test_hyper_2f3(): mp.dps = 15 assert hyper([1,2],[3,4,5],6) == hyp2f3(1,2,3,4,5,6) a1,a2,b1,b2,b3 = (1,10),(2,3),(3,10), 2, 1./16 # Check asymptotic expansion assert hyp2f3(a1,a2,b1,b2,b3,10).ae(128.98207160698659976) assert hyp2f3(a1,a2,b1,b2,b3,1000).ae(6.6309632883131273141e25) assert hyp2f3(a1,a2,b1,b2,b3,10000).ae(4.6863639362713340539e84) assert hyp2f3(a1,a2,b1,b2,b3,100000).ae(8.6632451236103084119e271) assert hyp2f3(a1,a2,b1,b2,b3,10**6).ae('2.0291718386574980641e865') assert hyp2f3(a1,a2,b1,b2,b3,10**7).ae('7.7639836665710030977e2742') assert hyp2f3(a1,a2,b1,b2,b3,10**8).ae('3.2537462584071268759e8681') assert hyp2f3(a1,a2,b1,b2,b3,10**20).ae('1.2966030542911614163e+8685889627') assert hyp2f3(a1,a2,b1,b2,b3,10*j).ae(-18.551602185587547854 - 13.348031097874113552j) assert hyp2f3(a1,a2,b1,b2,b3,100*j).ae(78634.359124504488695 + 74459.535945281973996j) assert hyp2f3(a1,a2,b1,b2,b3,1000*j).ae(597682550276527901.59 - 65136194809352613.078j) assert hyp2f3(a1,a2,b1,b2,b3,10000*j).ae(-1.1779696326238582496e+59 + 1.2297607505213133872e+59j) assert hyp2f3(a1,a2,b1,b2,b3,100000*j).ae(2.9844228969804380301e+191 + 7.5587163231490273296e+190j) assert hyp2f3(a1,a2,b1,b2,b3,1000000*j).ae('7.4859161049322370311e+610 - 2.8467477015940090189e+610j') assert hyp2f3(a1,a2,b1,b2,b3,10**7*j).ae('-1.7477645579418800826e+1938 - 1.7606522995808116405e+1938j') assert hyp2f3(a1,a2,b1,b2,b3,10**8*j).ae('-1.6932731942958401784e+6137 - 2.4521909113114629368e+6137j') assert hyp2f3(a1,a2,b1,b2,b3,10**20*j).ae('-2.0988815677627225449e+6141851451 + 5.7708223542739208681e+6141851452j') def test_hyper_2f2(): mp.dps = 15 assert hyper([1,2],[3,4],5) == hyp2f2(1,2,3,4,5) a1,a2,b1,b2 = (3,10),4,(1,2),1./16 assert hyp2f2(a1,a2,b1,b2,10).ae(448225936.3377556696) assert hyp2f2(a1,a2,b1,b2,10000).ae('1.2012553712966636711e+4358') assert hyp2f2(a1,a2,b1,b2,-20000).ae(-0.04182343755661214626) assert hyp2f2(a1,a2,b1,b2,10**20).ae('1.1148680024303263661e+43429448190325182840') def test_orthpoly(): mp.dps = 15 assert jacobi(-4,2,3,0.7).ae(22800./4913) assert jacobi(3,2,4,5.5) == 4133.125 assert jacobi(1.5,5/6.,4,0).ae(-1.0851951434075508417) assert jacobi(-2, 1, 2, 4).ae(-0.16) assert jacobi(2, -1, 2.5, 4).ae(34.59375) #assert jacobi(2, -1, 2, 4) == 28.5 assert legendre(5, 7) == 129367 assert legendre(0.5,0).ae(0.53935260118837935667) assert legendre(-1,-1) == 1 assert legendre(0,-1) == 1 assert legendre(0, 1) == 1 assert legendre(1, -1) == -1 assert legendre(7, 1) == 1 assert legendre(7, -1) == -1 assert legendre(8,1.5).ae(15457523./32768) assert legendre(j,-j).ae(2.4448182735671431011 + 0.6928881737669934843j) assert chebyu(5,1) == 6 assert chebyt(3,2) == 26 assert legendre(3.5,-1) == inf assert legendre(4.5,-1) == -inf assert legendre(3.5+1j,-1) == mpc(inf,inf) assert legendre(4.5+1j,-1) == mpc(-inf,-inf) assert laguerre(4, -2, 3).ae(-1.125) assert laguerre(3, 1+j, 0.5).ae(0.2291666666666666667 + 2.5416666666666666667j) def test_hermite(): mp.dps = 15 assert hermite(-2, 0).ae(0.5) assert hermite(-1, 0).ae(0.88622692545275801365) assert hermite(0, 0).ae(1) assert hermite(1, 0) == 0 assert hermite(2, 0).ae(-2) assert hermite(0, 2).ae(1) assert hermite(1, 2).ae(4) assert hermite(1, -2).ae(-4) assert hermite(2, -2).ae(14) assert hermite(0.5, 0).ae(0.69136733903629335053) assert hermite(9, 0) == 0 assert hermite(4,4).ae(3340) assert hermite(3,4).ae(464) assert hermite(-4,4).ae(0.00018623860287512396181) assert hermite(-3,4).ae(0.0016540169879668766270) assert hermite(9, 2.5j).ae(13638725j) assert hermite(9, -2.5j).ae(-13638725j) assert hermite(9, 100).ae(511078883759363024000) assert hermite(9, -100).ae(-511078883759363024000) assert hermite(9, 100j).ae(512922083920643024000j) assert hermite(9, -100j).ae(-512922083920643024000j) assert hermite(-9.5, 2.5j).ae(-2.9004951258126778174e-6 + 1.7601372934039951100e-6j) assert hermite(-9.5, -2.5j).ae(-2.9004951258126778174e-6 - 1.7601372934039951100e-6j) assert hermite(-9.5, 100).ae(1.3776300722767084162e-22, abs_eps=0, rel_eps=eps) assert hermite(-9.5, -100).ae('1.3106082028470671626e4355') assert hermite(-9.5, 100j).ae(-9.7900218581864768430e-23 - 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) assert hermite(-9.5, -100j).ae(-9.7900218581864768430e-23 + 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) assert hermite(2+3j, -1-j).ae(851.3677063883687676 - 1496.4373467871007997j) def test_gegenbauer(): mp.dps = 15 assert gegenbauer(1,2,3).ae(12) assert gegenbauer(2,3,4).ae(381) assert gegenbauer(0,0,0) == 0 assert gegenbauer(2,-1,3) == 0 assert gegenbauer(-7, 0.5, 3).ae(8989) assert gegenbauer(1, -0.5, 3).ae(-3) assert gegenbauer(1, -1.5, 3).ae(-9) assert gegenbauer(1, -0.5, 3).ae(-3) assert gegenbauer(-0.5, -0.5, 3).ae(-2.6383553159023906245) assert gegenbauer(2+3j, 1-j, 3+4j).ae(14.880536623203696780 + 20.022029711598032898j) #assert gegenbauer(-2, -0.5, 3).ae(-12) def test_legenp(): mp.dps = 15 assert legenp(2,0,4) == legendre(2,4) assert legenp(-2, -1, 0.5).ae(0.43301270189221932338) assert legenp(-2, -1, 0.5, type=3).ae(0.43301270189221932338j) assert legenp(-2, 1, 0.5).ae(-0.86602540378443864676) assert legenp(2+j, 3+4j, -j).ae(134742.98773236786148 + 429782.72924463851745j) assert legenp(2+j, 3+4j, -j, type=3).ae(802.59463394152268507 - 251.62481308942906447j) assert legenp(2,4,3).ae(0) assert legenp(2,4,3,type=3).ae(0) assert legenp(2,1,0.5).ae(-1.2990381056766579701) assert legenp(2,1,0.5,type=3).ae(1.2990381056766579701j) assert legenp(3,2,3).ae(-360) assert legenp(3,3,3).ae(240j*2**0.5) assert legenp(3,4,3).ae(0) assert legenp(0,0.5,2).ae(0.52503756790433198939 - 0.52503756790433198939j) assert legenp(-1,-0.5,2).ae(0.60626116232846498110 + 0.60626116232846498110j) assert legenp(-2,0.5,2).ae(1.5751127037129959682 - 1.5751127037129959682j) assert legenp(-2,0.5,-0.5).ae(-0.85738275810499171286) def test_legenq(): mp.dps = 15 f = legenq # Evaluation at poles assert isnan(f(3,2,1)) assert isnan(f(3,2,-1)) assert isnan(f(3,2,1,type=3)) assert isnan(f(3,2,-1,type=3)) # Evaluation at 0 assert f(0,1,0,type=2).ae(-1) assert f(-2,2,0,type=2,zeroprec=200).ae(0) assert f(1.5,3,0,type=2).ae(-2.2239343475841951023) assert f(0,1,0,type=3).ae(j) assert f(-2,2,0,type=3,zeroprec=200).ae(0) assert f(1.5,3,0,type=3).ae(2.2239343475841951022*(1-1j)) # Standard case, degree 0 assert f(0,0,-1.5).ae(-0.8047189562170501873 + 1.5707963267948966192j) assert f(0,0,-0.5).ae(-0.54930614433405484570) assert f(0,0,0,zeroprec=200).ae(0) assert f(0,0,0.5).ae(0.54930614433405484570) assert f(0,0,1.5).ae(0.8047189562170501873 - 1.5707963267948966192j) assert f(0,0,-1.5,type=3).ae(-0.80471895621705018730) assert f(0,0,-0.5,type=3).ae(-0.5493061443340548457 - 1.5707963267948966192j) assert f(0,0,0,type=3).ae(-1.5707963267948966192j) assert f(0,0,0.5,type=3).ae(0.5493061443340548457 - 1.5707963267948966192j) assert f(0,0,1.5,type=3).ae(0.80471895621705018730) # Standard case, degree 1 assert f(1,0,-1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) assert f(1,0,-0.5).ae(-0.72534692783297257715) assert f(1,0,0).ae(-1) assert f(1,0,0.5).ae(-0.72534692783297257715) assert f(1,0,1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) # Standard case, degree 2 assert f(2,0,-1.5).ae(-0.0635669991240192885 + 4.5160394395353277803j) assert f(2,0,-0.5).ae(0.81866326804175685571) assert f(2,0,0,zeroprec=200).ae(0) assert f(2,0,0.5).ae(-0.81866326804175685571) assert f(2,0,1.5).ae(0.0635669991240192885 - 4.5160394395353277803j) # Misc orders and degrees assert f(2,3,1.5,type=2).ae(-5.7243340223994616228j) assert f(2,3,1.5,type=3).ae(-5.7243340223994616228) assert f(2,3,0.5,type=2).ae(-12.316805742712016310) assert f(2,3,0.5,type=3).ae(-12.316805742712016310j) assert f(2,3,-1.5,type=2).ae(-5.7243340223994616228j) assert f(2,3,-1.5,type=3).ae(5.7243340223994616228) assert f(2,3,-0.5,type=2).ae(-12.316805742712016310) assert f(2,3,-0.5,type=3).ae(-12.316805742712016310j) assert f(2+3j, 3+4j, 0.5, type=3).ae(0.0016119404873235186807 - 0.0005885900510718119836j) assert f(2+3j, 3+4j, -1.5, type=3).ae(0.008451400254138808670 + 0.020645193304593235298j) assert f(-2.5,1,-1.5).ae(3.9553395527435335749j) assert f(-2.5,1,-0.5).ae(1.9290561746445456908) assert f(-2.5,1,0).ae(1.2708196271909686299) assert f(-2.5,1,0.5).ae(-0.31584812990742202869) assert f(-2.5,1,1.5).ae(-3.9553395527435335742 + 0.2993235655044701706j) assert f(-2.5,1,-1.5,type=3).ae(0.29932356550447017254j) assert f(-2.5,1,-0.5,type=3).ae(-0.3158481299074220287 - 1.9290561746445456908j) assert f(-2.5,1,0,type=3).ae(1.2708196271909686292 - 1.2708196271909686299j) assert f(-2.5,1,0.5,type=3).ae(1.9290561746445456907 + 0.3158481299074220287j) assert f(-2.5,1,1.5,type=3).ae(-0.29932356550447017254) def test_agm(): mp.dps = 15 assert agm(0,0) == 0 assert agm(0,1) == 0 assert agm(1,1) == 1 assert agm(7,7) == 7 assert agm(j,j) == j assert (1/agm(1,sqrt(2))).ae(0.834626841674073186) assert agm(1,2).ae(1.4567910310469068692) assert agm(1,3).ae(1.8636167832448965424) assert agm(1,j).ae(0.599070117367796104+0.599070117367796104j) assert agm(2) == agm(1,2) assert agm(-3,4).ae(0.63468509766550907+1.3443087080896272j) def test_gammainc(): mp.dps = 15 assert gammainc(2,5).ae(6*exp(-5)) assert gammainc(2,0,5).ae(1-6*exp(-5)) assert gammainc(2,3,5).ae(-6*exp(-5)+4*exp(-3)) assert gammainc(-2.5,-0.5).ae(-0.9453087204829418812-5.3164237738936178621j) assert gammainc(0,2,4).ae(0.045121158298212213088) assert gammainc(0,3).ae(0.013048381094197037413) assert gammainc(0,2+j,1-j).ae(0.00910653685850304839-0.22378752918074432574j) assert gammainc(0,1-j).ae(0.00028162445198141833+0.17932453503935894015j) assert gammainc(3,4,5,True).ae(0.11345128607046320253) assert gammainc(3.5,0,inf).ae(gamma(3.5)) assert gammainc(-150.5,500).ae('6.9825435345798951153e-627') assert gammainc(-150.5,800).ae('4.6885137549474089431e-788') assert gammainc(-3.5, -20.5).ae(0.27008820585226911 - 1310.31447140574997636j) assert gammainc(-3.5, -200.5).ae(0.27008820585226911 - 5.3264597096208368435e76j) # XXX real part assert gammainc(0,0,2) == inf assert gammainc(1,b=1).ae(0.6321205588285576784) assert gammainc(3,2,2) == 0 assert gammainc(2,3+j,3-j).ae(-0.28135485191849314194j) assert gammainc(4+0j,1).ae(5.8860710587430771455) # Regularized upper gamma assert isnan(gammainc(0, 0, regularized=True)) assert gammainc(-1, 0, regularized=True) == inf assert gammainc(1, 0, regularized=True) == 1 assert gammainc(0, 5, regularized=True) == 0 assert gammainc(0, 2+3j, regularized=True) == 0 assert gammainc(0, 5000, regularized=True) == 0 assert gammainc(0, 10**30, regularized=True) == 0 assert gammainc(-1, 5, regularized=True) == 0 assert gammainc(-1, 5000, regularized=True) == 0 assert gammainc(-1, 10**30, regularized=True) == 0 assert gammainc(-1, -5, regularized=True) == 0 assert gammainc(-1, -5000, regularized=True) == 0 assert gammainc(-1, -10**30, regularized=True) == 0 assert gammainc(-1, 3+4j, regularized=True) == 0 assert gammainc(1, 5, regularized=True).ae(exp(-5)) assert gammainc(1, 5000, regularized=True).ae(exp(-5000)) assert gammainc(1, 10**30, regularized=True).ae(exp(-10**30)) assert gammainc(1, 3+4j, regularized=True).ae(exp(-3-4j)) assert gammainc(-1000000,2).ae('1.3669297209397347754e-301037', abs_eps=0, rel_eps=8*eps) assert gammainc(-1000000,2,regularized=True) == 0 assert gammainc(-1000000,3+4j).ae('-1.322575609404222361e-698979 - 4.9274570591854533273e-698978j', abs_eps=0, rel_eps=8*eps) assert gammainc(-1000000,3+4j,regularized=True) == 0 assert gammainc(2+3j, 4+5j, regularized=True).ae(0.085422013530993285774-0.052595379150390078503j) assert gammainc(1000j, 1000j, regularized=True).ae(0.49702647628921131761 + 0.00297355675013575341j) # Generalized assert gammainc(3,4,2) == -gammainc(3,2,4) assert gammainc(4, 2, 3).ae(1.2593494302978947396) assert gammainc(4, 2, 3, regularized=True).ae(0.20989157171631578993) assert gammainc(0, 2, 3).ae(0.035852129613864082155) assert gammainc(0, 2, 3, regularized=True) == 0 assert gammainc(-1, 2, 3).ae(0.015219822548487616132) assert gammainc(-1, 2, 3, regularized=True) == 0 assert gammainc(0, 2, 3).ae(0.035852129613864082155) assert gammainc(0, 2, 3, regularized=True) == 0 # Should use upper gammas assert gammainc(5, 10000, 12000).ae('1.1359381951461801687e-4327', abs_eps=0, rel_eps=8*eps) # Should use lower gammas assert gammainc(10000, 2, 3).ae('8.1244514125995785934e4765') def test_gammainc_expint_n(): # These tests are intended to check all cases of the low-level code # for upper gamma and expint with small integer index. # Need to cover positive/negative arguments; small/large/huge arguments # for both positive and negative indices, as well as indices 0 and 1 # which may be special-cased mp.dps = 15 assert expint(-3,3.5).ae(0.021456366563296693987) assert expint(-2,3.5).ae(0.014966633183073309405) assert expint(-1,3.5).ae(0.011092916359219041088) assert expint(0,3.5).ae(0.0086278238349481430685) assert expint(1,3.5).ae(0.0069701398575483929193) assert expint(2,3.5).ae(0.0058018939208991255223) assert expint(3,3.5).ae(0.0049453773495857807058) assert expint(-3,-3.5).ae(-4.6618170604073311319) assert expint(-2,-3.5).ae(-5.5996974157555515963) assert expint(-1,-3.5).ae(-6.7582555017739415818) assert expint(0,-3.5).ae(-9.4615577024835182145) assert expint(1,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) assert expint(2,-3.5).ae(-15.62328702434085977 - 10.995574287564276335j) assert expint(3,-3.5).ae(-10.783026313250347722 - 19.242255003237483586j) assert expint(-3,350).ae(2.8614825451252838069e-155, abs_eps=0, rel_eps=8*eps) assert expint(-2,350).ae(2.8532837224504675901e-155, abs_eps=0, rel_eps=8*eps) assert expint(-1,350).ae(2.8451316155828634555e-155, abs_eps=0, rel_eps=8*eps) assert expint(0,350).ae(2.8370258275042797989e-155, abs_eps=0, rel_eps=8*eps) assert expint(1,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) assert expint(2,350).ae(2.8209516419468505006e-155, abs_eps=0, rel_eps=8*eps) assert expint(3,350).ae(2.8129824725501272171e-155, abs_eps=0, rel_eps=8*eps) assert expint(-3,-350).ae(-2.8528796154044839443e+149) assert expint(-2,-350).ae(-2.8610072121701264351e+149) assert expint(-1,-350).ae(-2.8691813842677537647e+149) assert expint(0,-350).ae(-2.8774025343659421709e+149) u = expint(1,-350) assert u.ae(-2.8856710698020863568e+149) assert u.imag.ae(-3.1415926535897932385) u = expint(2,-350) assert u.ae(-2.8939874026504650534e+149) assert u.imag.ae(-1099.5574287564276335) u = expint(3,-350) assert u.ae(-2.9023519497915044349e+149) assert u.imag.ae(-192422.55003237483586) assert expint(-3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(-2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(-1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert expint(-3,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') assert expint(-2,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') assert expint(-1,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') assert expint(0,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') u = expint(1,-350000000000000000000000) assert u.ae('-3.7805306852415755699e+152003068666138139677871') assert u.imag.ae(-3.1415926535897932385) u = expint(2,-350000000000000000000000) assert u.imag.ae(-1.0995574287564276335e+24) assert u.ae('-3.7805306852415755699e+152003068666138139677871') u = expint(3,-350000000000000000000000) assert u.imag.ae(-1.9242255003237483586e+47) assert u.ae('-3.7805306852415755699e+152003068666138139677871') # Small case; no branch cut assert gammainc(-3,3.5).ae(0.00010020262545203707109) assert gammainc(-2,3.5).ae(0.00040370427343557393517) assert gammainc(-1,3.5).ae(0.0016576839773997501492) assert gammainc(0,3.5).ae(0.0069701398575483929193) assert gammainc(1,3.5).ae(0.03019738342231850074) assert gammainc(2,3.5).ae(0.13588822540043325333) assert gammainc(3,3.5).ae(0.64169439772426814072) # Small case; with branch cut assert gammainc(-3,-3.5).ae(0.03595832954467563286 - 0.52359877559829887308j) assert gammainc(-2,-3.5).ae(-0.88024704597962022221 - 1.5707963267948966192j) assert gammainc(-1,-3.5).ae(4.4637962926688170771 - 3.1415926535897932385j) assert gammainc(0,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) assert gammainc(1,-3.5).ae(33.115451958692313751) assert gammainc(2,-3.5).ae(-82.788629896730784377) assert gammainc(3,-3.5).ae(240.08702670051927469) # Asymptotic case; no branch cut assert gammainc(-3,350).ae(6.5424095113340358813e-163, abs_eps=0, rel_eps=8*eps) assert gammainc(-2,350).ae(2.296312222489899769e-160, abs_eps=0, rel_eps=8*eps) assert gammainc(-1,350).ae(8.059861834133858573e-158, abs_eps=0, rel_eps=8*eps) assert gammainc(0,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) assert gammainc(1,350).ae(9.9295903962649792963e-153, abs_eps=0, rel_eps=8*eps) assert gammainc(2,350).ae(3.485286229089007733e-150, abs_eps=0, rel_eps=8*eps) assert gammainc(3,350).ae(1.2233453960006379793e-147, abs_eps=0, rel_eps=8*eps) # Asymptotic case; branch cut u = gammainc(-3,-350) assert u.ae(6.7889565783842895085e+141) assert u.imag.ae(-0.52359877559829887308) u = gammainc(-2,-350) assert u.ae(-2.3692668977889832121e+144) assert u.imag.ae(-1.5707963267948966192) u = gammainc(-1,-350) assert u.ae(8.2685354361441858669e+146) assert u.imag.ae(-3.1415926535897932385) u = gammainc(0,-350) assert u.ae(-2.8856710698020863568e+149) assert u.imag.ae(-3.1415926535897932385) u = gammainc(1,-350) assert u.ae(1.0070908870280797598e+152) assert u.imag == 0 u = gammainc(2,-350) assert u.ae(-3.5147471957279983618e+154) assert u.imag == 0 u = gammainc(3,-350) assert u.ae(1.2266568422179417091e+157) assert u.imag == 0 # Extreme asymptotic case assert gammainc(-3,350000000000000000000000).ae('5.0362468738874738859e-152003068666138139677990', abs_eps=0, rel_eps=8*eps) assert gammainc(-2,350000000000000000000000).ae('1.7626864058606158601e-152003068666138139677966', abs_eps=0, rel_eps=8*eps) assert gammainc(-1,350000000000000000000000).ae('6.1694024205121555102e-152003068666138139677943', abs_eps=0, rel_eps=8*eps) assert gammainc(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) assert gammainc(1,350000000000000000000000).ae('7.5575179651273905e-152003068666138139677896', abs_eps=0, rel_eps=8*eps) assert gammainc(2,350000000000000000000000).ae('2.645131287794586675e-152003068666138139677872', abs_eps=0, rel_eps=8*eps) assert gammainc(3,350000000000000000000000).ae('9.2579595072810533625e-152003068666138139677849', abs_eps=0, rel_eps=8*eps) u = gammainc(-3,-350000000000000000000000) assert u.ae('8.8175642804468234866e+152003068666138139677800') assert u.imag.ae(-0.52359877559829887308) u = gammainc(-2,-350000000000000000000000) assert u.ae('-3.0861474981563882203e+152003068666138139677824') assert u.imag.ae(-1.5707963267948966192) u = gammainc(-1,-350000000000000000000000) assert u.ae('1.0801516243547358771e+152003068666138139677848') assert u.imag.ae(-3.1415926535897932385) u = gammainc(0,-350000000000000000000000) assert u.ae('-3.7805306852415755699e+152003068666138139677871') assert u.imag.ae(-3.1415926535897932385) assert gammainc(1,-350000000000000000000000).ae('1.3231857398345514495e+152003068666138139677895') assert gammainc(2,-350000000000000000000000).ae('-4.6311500894209300731e+152003068666138139677918') assert gammainc(3,-350000000000000000000000).ae('1.6209025312973255256e+152003068666138139677942') def test_incomplete_beta(): mp.dps = 15 assert betainc(-2,-3,0.5,0.75).ae(63.4305673311255413583969) assert betainc(4.5,0.5+2j,2.5,6).ae(0.2628801146130621387903065 + 0.5162565234467020592855378j) assert betainc(4,5,0,6).ae(90747.77142857142857142857) def test_erf(): mp.dps = 15 assert erf(0) == 0 assert erf(1).ae(0.84270079294971486934) assert erf(3+4j).ae(-120.186991395079444098 - 27.750337293623902498j) assert erf(-4-3j).ae(-0.99991066178539168236 + 0.00004972026054496604j) assert erf(pi).ae(0.99999112385363235839) assert erf(1j).ae(1.6504257587975428760j) assert erf(-1j).ae(-1.6504257587975428760j) assert isinstance(erf(1), mpf) assert isinstance(erf(-1), mpf) assert isinstance(erf(0), mpf) assert isinstance(erf(0j), mpc) assert erf(inf) == 1 assert erf(-inf) == -1 assert erfi(0) == 0 assert erfi(1/pi).ae(0.371682698493894314) assert erfi(inf) == inf assert erfi(-inf) == -inf assert erf(1+0j) == erf(1) assert erfc(1+0j) == erfc(1) assert erf(0.2+0.5j).ae(1 - erfc(0.2+0.5j)) assert erfc(0) == 1 assert erfc(1).ae(1-erf(1)) assert erfc(-1).ae(1-erf(-1)) assert erfc(1/pi).ae(1-erf(1/pi)) assert erfc(-10) == 2 assert erfc(-1000000) == 2 assert erfc(-inf) == 2 assert erfc(inf) == 0 assert isnan(erfc(nan)) assert (erfc(10**4)*mpf(10)**43429453).ae('3.63998738656420') assert erf(8+9j).ae(-1072004.2525062051158 + 364149.91954310255423j) assert erfc(8+9j).ae(1072005.2525062051158 - 364149.91954310255423j) assert erfc(-8-9j).ae(-1072003.2525062051158 + 364149.91954310255423j) mp.dps = 50 # This one does not use the asymptotic series assert (erfc(10)*10**45).ae('2.0884875837625447570007862949577886115608181193212') # This one does assert (erfc(50)*10**1088).ae('2.0709207788416560484484478751657887929322509209954') mp.dps = 15 assert str(erfc(10**50)) == '3.66744826532555e-4342944819032518276511289189166050822943970058036665661144537831658646492088707747292249493384317534' assert erfinv(0) == 0 assert erfinv(0.5).ae(0.47693627620446987338) assert erfinv(-0.5).ae(-0.47693627620446987338) assert erfinv(1) == inf assert erfinv(-1) == -inf assert erf(erfinv(0.95)).ae(0.95) assert erf(erfinv(0.999999999995)).ae(0.999999999995) assert erf(erfinv(-0.999999999995)).ae(-0.999999999995) mp.dps = 50 assert erf(erfinv('0.99999999999999999999999999999995')).ae('0.99999999999999999999999999999995') assert erf(erfinv('0.999999999999999999999999999999995')).ae('0.999999999999999999999999999999995') assert erf(erfinv('-0.999999999999999999999999999999995')).ae('-0.999999999999999999999999999999995') mp.dps = 15 # Complex asymptotic expansions v = erfc(50j) assert v.real == 1 assert v.imag.ae('-6.1481820666053078736e+1083') assert erfc(-100+5j).ae(2) assert (erfc(100+5j)*10**4335).ae(2.3973567853824133572 - 3.9339259530609420597j) assert erfc(100+100j).ae(0.00065234366376857698698 - 0.0039357263629214118437j) def test_pdf(): mp.dps = 15 assert npdf(-inf) == 0 assert npdf(inf) == 0 assert npdf(5,0,2).ae(npdf(5+4,4,2)) assert quadts(lambda x: npdf(x,-0.5,0.8), [-inf, inf]) == 1 assert ncdf(0) == 0.5 assert ncdf(3,3) == 0.5 assert ncdf(-inf) == 0 assert ncdf(inf) == 1 assert ncdf(10) == 1 # Verify that this is computed accurately assert (ncdf(-10)*10**24).ae(7.619853024160526) def test_lambertw(): mp.dps = 15 assert lambertw(0) == 0 assert lambertw(0+0j) == 0 assert lambertw(inf) == inf assert isnan(lambertw(nan)) assert lambertw(inf,1).real == inf assert lambertw(inf,1).imag.ae(2*pi) assert lambertw(-inf,1).real == inf assert lambertw(-inf,1).imag.ae(3*pi) assert lambertw(0,-1) == -inf assert lambertw(0,1) == -inf assert lambertw(0,3) == -inf assert lambertw(e).ae(1) assert lambertw(1).ae(0.567143290409783873) assert lambertw(-pi/2).ae(j*pi/2) assert lambertw(-log(2)/2).ae(-log(2)) assert lambertw(0.25).ae(0.203888354702240164) assert lambertw(-0.25).ae(-0.357402956181388903) assert lambertw(-1./10000,0).ae(-0.000100010001500266719) assert lambertw(-0.25,-1).ae(-2.15329236411034965) assert lambertw(0.25,-1).ae(-3.00899800997004620-4.07652978899159763j) assert lambertw(-0.25,-1).ae(-2.15329236411034965) assert lambertw(0.25,1).ae(-3.00899800997004620+4.07652978899159763j) assert lambertw(-0.25,1).ae(-3.48973228422959210+7.41405453009603664j) assert lambertw(-4).ae(0.67881197132094523+1.91195078174339937j) assert lambertw(-4,1).ae(-0.66743107129800988+7.76827456802783084j) assert lambertw(-4,-1).ae(0.67881197132094523-1.91195078174339937j) assert lambertw(1000).ae(5.24960285240159623) assert lambertw(1000,1).ae(4.91492239981054535+5.44652615979447070j) assert lambertw(1000,-1).ae(4.91492239981054535-5.44652615979447070j) assert lambertw(1000,5).ae(3.5010625305312892+29.9614548941181328j) assert lambertw(3+4j).ae(1.281561806123775878+0.533095222020971071j) assert lambertw(-0.4+0.4j).ae(-0.10396515323290657+0.61899273315171632j) assert lambertw(3+4j,1).ae(-0.11691092896595324+5.61888039871282334j) assert lambertw(3+4j,-1).ae(0.25856740686699742-3.85211668616143559j) assert lambertw(-0.5,-1).ae(-0.794023632344689368-0.770111750510379110j) assert lambertw(-1./10000,1).ae(-11.82350837248724344+6.80546081842002101j) assert lambertw(-1./10000,-1).ae(-11.6671145325663544) assert lambertw(-1./10000,-2).ae(-11.82350837248724344-6.80546081842002101j) assert lambertw(-1./100000,4).ae(-14.9186890769540539+26.1856750178782046j) assert lambertw(-1./100000,5).ae(-15.0931437726379218666+32.5525721210262290086j) assert lambertw((2+j)/10).ae(0.173704503762911669+0.071781336752835511j) assert lambertw((2+j)/10,1).ae(-3.21746028349820063+4.56175438896292539j) assert lambertw((2+j)/10,-1).ae(-3.03781405002993088-3.53946629633505737j) assert lambertw((2+j)/10,4).ae(-4.6878509692773249+23.8313630697683291j) assert lambertw(-(2+j)/10).ae(-0.226933772515757933-0.164986470020154580j) assert lambertw(-(2+j)/10,1).ae(-2.43569517046110001+0.76974067544756289j) assert lambertw(-(2+j)/10,-1).ae(-3.54858738151989450-6.91627921869943589j) assert lambertw(-(2+j)/10,4).ae(-4.5500846928118151+20.6672982215434637j) mp.dps = 50 assert lambertw(pi).ae('1.073658194796149172092178407024821347547745350410314531') mp.dps = 15 # Former bug in generated branch assert lambertw(-0.5+0.002j).ae(-0.78917138132659918344 + 0.76743539379990327749j) assert lambertw(-0.5-0.002j).ae(-0.78917138132659918344 - 0.76743539379990327749j) assert lambertw(-0.448+0.4j).ae(-0.11855133765652382241 + 0.66570534313583423116j) assert lambertw(-0.448-0.4j).ae(-0.11855133765652382241 - 0.66570534313583423116j) assert lambertw(-0.65475+0.0001j).ae(-0.61053421111385310898+1.0396534993944097723803j) # Huge branch index w = lambertw(1,10**20) assert w.real.ae(-47.889578926290259164) assert w.imag.ae(6.2831853071795864769e+20) def test_lambertw_hard(): def check(x,y): y = convert(y) type_ok = True if isinstance(y, mpf): type_ok = isinstance(x, mpf) real_ok = abs(x.real-y.real) <= abs(y.real)*8*eps imag_ok = abs(x.imag-y.imag) <= abs(y.imag)*8*eps #print x, y, abs(x.real-y.real), abs(x.imag-y.imag) return real_ok and imag_ok # Evaluation near 0 mp.dps = 15 assert check(lambertw(1e-10), 9.999999999000000000e-11) assert check(lambertw(-1e-10), -1.000000000100000000e-10) assert check(lambertw(1e-10j), 9.999999999999999999733e-21 + 9.99999999999999999985e-11j) assert check(lambertw(-1e-10j), 9.999999999999999999733e-21 - 9.99999999999999999985e-11j) assert check(lambertw(1e-10,1), -26.303186778379041559 + 3.265093911703828397j) assert check(lambertw(-1e-10,1), -26.326236166739163892 + 6.526183280686333315j) assert check(lambertw(1e-10j,1), -26.312931726911421551 + 4.896366881798013421j) assert check(lambertw(-1e-10j,1), -26.297238779529035066 + 1.632807161345576513j) assert check(lambertw(1e-10,-1), -26.303186778379041559 - 3.265093911703828397j) assert check(lambertw(-1e-10,-1), -26.295238819246925694) assert check(lambertw(1e-10j,-1), -26.297238779529035028 - 1.6328071613455765135j) assert check(lambertw(-1e-10j,-1), -26.312931726911421551 - 4.896366881798013421j) # Test evaluation very close to the branch point -1/e # on the -1, 0, and 1 branches add = lambda x, y: fadd(x,y,exact=True) sub = lambda x, y: fsub(x,y,exact=True) addj = lambda x, y: fadd(x,fmul(y,1j,exact=True),exact=True) subj = lambda x, y: fadd(x,fmul(y,-1j,exact=True),exact=True) mp.dps = 1500 a = -1/e + 10*eps d3 = mpf('1e-3') d10 = mpf('1e-10') d20 = mpf('1e-20') d40 = mpf('1e-40') d80 = mpf('1e-80') d300 = mpf('1e-300') d1000 = mpf('1e-1000') mp.dps = 15 # ---- Branch 0 ---- # -1/e + eps assert check(lambertw(add(a,d3)), -0.92802015005456704876) assert check(lambertw(add(a,d10)), -0.99997668374140088071) assert check(lambertw(add(a,d20)), -0.99999999976683560186) assert lambertw(add(a,d40)) == -1 assert lambertw(add(a,d80)) == -1 assert lambertw(add(a,d300)) == -1 assert lambertw(add(a,d1000)) == -1 # -1/e - eps assert check(lambertw(sub(a,d3)), -0.99819016149860989001+0.07367191188934638577j) assert check(lambertw(sub(a,d10)), -0.9999999998187812114595992+0.0000233164398140346109194j) assert check(lambertw(sub(a,d20)), -0.99999999999999999998187+2.331643981597124203344e-10j) assert check(lambertw(sub(a,d40)), -1.0+2.33164398159712420336e-20j) assert check(lambertw(sub(a,d80)), -1.0+2.33164398159712420336e-40j) assert check(lambertw(sub(a,d300)), -1.0+2.33164398159712420336e-150j) assert check(lambertw(sub(a,d1000)), mpc(-1,'2.33164398159712420336e-500')) # -1/e + eps*j assert check(lambertw(addj(a,d3)), -0.94790387486938526634+0.05036819639190132490j) assert check(lambertw(addj(a,d10)), -0.9999835127872943680999899+0.0000164870314895821225256j) assert check(lambertw(addj(a,d20)), -0.999999999835127872929987+1.64872127051890935830e-10j) assert check(lambertw(addj(a,d40)), -0.9999999999999999999835+1.6487212707001281468305e-20j) assert check(lambertw(addj(a,d80)), -1.0 + 1.64872127070012814684865e-40j) assert check(lambertw(addj(a,d300)), -1.0 + 1.64872127070012814684865e-150j) assert check(lambertw(addj(a,d1000)), mpc(-1.0,'1.64872127070012814684865e-500')) # -1/e - eps*j assert check(lambertw(subj(a,d3)), -0.94790387486938526634-0.05036819639190132490j) assert check(lambertw(subj(a,d10)), -0.9999835127872943680999899-0.0000164870314895821225256j) assert check(lambertw(subj(a,d20)), -0.999999999835127872929987-1.64872127051890935830e-10j) assert check(lambertw(subj(a,d40)), -0.9999999999999999999835-1.6487212707001281468305e-20j) assert check(lambertw(subj(a,d80)), -1.0 - 1.64872127070012814684865e-40j) assert check(lambertw(subj(a,d300)), -1.0 - 1.64872127070012814684865e-150j) assert check(lambertw(subj(a,d1000)), mpc(-1.0,'-1.64872127070012814684865e-500')) # ---- Branch 1 ---- assert check(lambertw(addj(a,d3),1), -3.088501303219933378005990 + 7.458676867597474813950098j) assert check(lambertw(addj(a,d80),1), -3.088843015613043855957087 + 7.461489285654254556906117j) assert check(lambertw(addj(a,d300),1), -3.088843015613043855957087 + 7.461489285654254556906117j) assert check(lambertw(addj(a,d1000),1), -3.088843015613043855957087 + 7.461489285654254556906117j) assert check(lambertw(subj(a,d3),1), -1.0520914180450129534365906 + 0.0539925638125450525673175j) assert check(lambertw(subj(a,d10),1), -1.0000164872127056318529390 + 0.000016487393927159250398333077j) assert check(lambertw(subj(a,d20),1), -1.0000000001648721270700128 + 1.64872127088134693542628e-10j) assert check(lambertw(subj(a,d40),1), -1.000000000000000000016487 + 1.64872127070012814686677e-20j) assert check(lambertw(subj(a,d80),1), -1.0 + 1.64872127070012814684865e-40j) assert check(lambertw(subj(a,d300),1), -1.0 + 1.64872127070012814684865e-150j) assert check(lambertw(subj(a,d1000),1), mpc(-1.0, '1.64872127070012814684865e-500')) # ---- Branch -1 ---- # -1/e + eps assert check(lambertw(add(a,d3),-1), -1.075608941186624989414945) assert check(lambertw(add(a,d10),-1), -1.000023316621036696460620) assert check(lambertw(add(a,d20),-1), -1.000000000233164398177834) assert lambertw(add(a,d40),-1) == -1 assert lambertw(add(a,d80),-1) == -1 assert lambertw(add(a,d300),-1) == -1 assert lambertw(add(a,d1000),-1) == -1 # -1/e - eps assert check(lambertw(sub(a,d3),-1), -0.99819016149860989001-0.07367191188934638577j) assert check(lambertw(sub(a,d10),-1), -0.9999999998187812114595992-0.0000233164398140346109194j) assert check(lambertw(sub(a,d20),-1), -0.99999999999999999998187-2.331643981597124203344e-10j) assert check(lambertw(sub(a,d40),-1), -1.0-2.33164398159712420336e-20j) assert check(lambertw(sub(a,d80),-1), -1.0-2.33164398159712420336e-40j) assert check(lambertw(sub(a,d300),-1), -1.0-2.33164398159712420336e-150j) assert check(lambertw(sub(a,d1000),-1), mpc(-1,'-2.33164398159712420336e-500')) # -1/e + eps*j assert check(lambertw(addj(a,d3),-1), -1.0520914180450129534365906 - 0.0539925638125450525673175j) assert check(lambertw(addj(a,d10),-1), -1.0000164872127056318529390 - 0.0000164873939271592503983j) assert check(lambertw(addj(a,d20),-1), -1.0000000001648721270700 - 1.64872127088134693542628e-10j) assert check(lambertw(addj(a,d40),-1), -1.00000000000000000001648 - 1.6487212707001281468667726e-20j) assert check(lambertw(addj(a,d80),-1), -1.0 - 1.64872127070012814684865e-40j) assert check(lambertw(addj(a,d300),-1), -1.0 - 1.64872127070012814684865e-150j) assert check(lambertw(addj(a,d1000),-1), mpc(-1.0,'-1.64872127070012814684865e-500')) # -1/e - eps*j assert check(lambertw(subj(a,d3),-1), -3.088501303219933378005990-7.458676867597474813950098j) assert check(lambertw(subj(a,d10),-1), -3.088843015579260686911033-7.461489285372968780020716j) assert check(lambertw(subj(a,d20),-1), -3.088843015613043855953708-7.461489285654254556877988j) assert check(lambertw(subj(a,d40),-1), -3.088843015613043855957087-7.461489285654254556906117j) assert check(lambertw(subj(a,d80),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) assert check(lambertw(subj(a,d300),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) assert check(lambertw(subj(a,d1000),-1), -3.088843015613043855957087 - 7.461489285654254556906117j) # One more case, testing higher precision mp.dps = 500 x = -1/e + mpf('1e-13') ans = "-0.99999926266961377166355784455394913638782494543377383"\ "744978844374498153493943725364881490261187530235150668593869563"\ "168276697689459394902153960200361935311512317183678882" mp.dps = 15 assert lambertw(x).ae(ans) mp.dps = 50 assert lambertw(x).ae(ans) mp.dps = 150 assert lambertw(x).ae(ans) def test_meijerg(): mp.dps = 15 assert meijerg([[2,3],[1]],[[0.5,2],[3,4]], 2.5).ae(4.2181028074787439386) assert meijerg([[],[1+j]],[[1],[1]], 3+4j).ae(271.46290321152464592 - 703.03330399954820169j) assert meijerg([[0.25],[1]],[[0.5],[2]],0) == 0 assert meijerg([[0],[]],[[0,0,'1/3','2/3'], []], '2/27').ae(2.2019391389653314120) # Verify 1/z series being used assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -0.5).ae(-1.338096165935754898687431) assert meijerg([[1-(-1)],[1-(-2.5)]], [[1-(-3)],[1-(-0.5)]], -2.0).ae(-1.338096165935754898687431) assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -1).ae(-(pi+4)/(4*pi)) a = 2.5 b = 1.25 for z in [mpf(0.25), mpf(2)]: x1 = hyp1f1(a,b,z) x2 = gamma(b)/gamma(a)*meijerg([[1-a],[]],[[0],[1-b]],-z) x3 = gamma(b)/gamma(a)*meijerg([[1-0],[1-(1-b)]],[[1-(1-a)],[]],-1/z) assert x1.ae(x2) assert x1.ae(x3) def test_appellf1(): mp.dps = 15 assert appellf1(2,-2,1,1,2,3).ae(-1.75) assert appellf1(2,1,-2,1,2,3).ae(-8) assert appellf1(2,1,-2,1,0.5,0.25).ae(1.5) assert appellf1(-2,1,3,2,3,3).ae(19) assert appellf1(1,2,3,4,0.5,0.125).ae( 1.53843285792549786518) def test_coulomb(): # Note: most tests are doctests # Test for a bug: mp.dps = 15 assert coulombg(mpc(-5,0),2,3).ae(20.087729487721430394) def test_hyper_param_accuracy(): mp.dps = 15 As = [n+1e-10 for n in range(-5,-1)] Bs = [n+1e-10 for n in range(-12,-5)] assert hyper(As,Bs,10).ae(-381757055858.652671927) assert legenp(0.5, 100, 0.25).ae(-2.4124576567211311755e+144) assert (hyp1f1(1000,1,-100)*10**24).ae(5.2589445437370169113) assert (hyp2f1(10, -900, 10.5, 0.99)*10**24).ae(1.9185370579660768203) assert (hyp2f1(1000,1.5,-3.5,-1.5)*10**385).ae(-2.7367529051334000764) assert hyp2f1(-5, 10, 3, 0.5, zeroprec=500) == 0 assert (hyp1f1(-10000, 1000, 100)*10**424).ae(-3.1046080515824859974) assert (hyp2f1(1000,1.5,-3.5,-0.75,maxterms=100000)*10**231).ae(-4.0534790813913998643) assert legenp(2, 3, 0.25) == 0 try: hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3]) assert 0 except ValueError: pass assert hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3], infprec=200) == inf assert meijerg([[],[]],[[0,0,0,0],[]],0.1).ae(1.5680822343832351418) assert (besselk(400,400)*10**94).ae(1.4387057277018550583) mp.dps = 5 (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) mp.dps = 15 (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) assert hyp0f1(fadd(-20,'1e-100',exact=True), 0.25).ae(1.85014429040102783e+49) assert hyp0f1((-20*10**100+1, 10**100), 0.25).ae(1.85014429040102783e+49) def test_hypercomb_zero_pow(): # check that 0^0 = 1 assert hypercomb(lambda a: (([0],[a],[],[],[],[],0),), [0]) == 1 assert meijerg([[-1.5],[]],[[0],[-0.75]],0).ae(1.4464090846320771425) def test_spherharm(): mp.dps = 15 t = 0.5; r = 0.25 assert spherharm(0,0,t,r).ae(0.28209479177387814347) assert spherharm(1,-1,t,r).ae(0.16048941205971996369 - 0.04097967481096344271j) assert spherharm(1,0,t,r).ae(0.42878904414183579379) assert spherharm(1,1,t,r).ae(-0.16048941205971996369 - 0.04097967481096344271j) assert spherharm(2,-2,t,r).ae(0.077915886919031181734 - 0.042565643022253962264j) assert spherharm(2,-1,t,r).ae(0.31493387233497459884 - 0.08041582001959297689j) assert spherharm(2,0,t,r).ae(0.41330596756220761898) assert spherharm(2,1,t,r).ae(-0.31493387233497459884 - 0.08041582001959297689j) assert spherharm(2,2,t,r).ae(0.077915886919031181734 + 0.042565643022253962264j) assert spherharm(3,-3,t,r).ae(0.033640236589690881646 - 0.031339125318637082197j) assert spherharm(3,-2,t,r).ae(0.18091018743101461963 - 0.09883168583167010241j) assert spherharm(3,-1,t,r).ae(0.42796713930907320351 - 0.10927795157064962317j) assert spherharm(3,0,t,r).ae(0.27861659336351639787) assert spherharm(3,1,t,r).ae(-0.42796713930907320351 - 0.10927795157064962317j) assert spherharm(3,2,t,r).ae(0.18091018743101461963 + 0.09883168583167010241j) assert spherharm(3,3,t,r).ae(-0.033640236589690881646 - 0.031339125318637082197j) assert spherharm(0,-1,t,r) == 0 assert spherharm(0,-2,t,r) == 0 assert spherharm(0,1,t,r) == 0 assert spherharm(0,2,t,r) == 0 assert spherharm(1,2,t,r) == 0 assert spherharm(1,3,t,r) == 0 assert spherharm(1,-2,t,r) == 0 assert spherharm(1,-3,t,r) == 0 assert spherharm(2,3,t,r) == 0 assert spherharm(2,4,t,r) == 0 assert spherharm(2,-3,t,r) == 0 assert spherharm(2,-4,t,r) == 0 assert spherharm(3,4.5,0.5,0.25).ae(-22.831053442240790148 + 10.910526059510013757j) assert spherharm(2+3j, 1-j, 1+j, 3+4j).ae(-2.6582752037810116935 - 1.0909214905642160211j) assert spherharm(-6,2.5,t,r).ae(0.39383644983851448178 + 0.28414687085358299021j) assert spherharm(-3.5, 3, 0.5, 0.25).ae(0.014516852987544698924 - 0.015582769591477628495j) assert spherharm(-3, 3, 0.5, 0.25) == 0 assert spherharm(-6, 3, 0.5, 0.25).ae(-0.16544349818782275459 - 0.15412657723253924562j) assert spherharm(-6, 1.5, 0.5, 0.25).ae(0.032208193499767402477 + 0.012678000924063664921j) assert spherharm(3,0,0,1).ae(0.74635266518023078283) assert spherharm(3,-2,0,1) == 0 assert spherharm(3,-2,1,1).ae(-0.16270707338254028971 - 0.35552144137546777097j) def test_qfunctions(): mp.dps = 15 assert qp(2,3,100).ae('2.7291482267247332183e2391') def test_issue199(): mp.prec = 150 x = ldexp(2476979795053773,-52) assert betainc(206, 385, 0, 0.55, 1).ae('0.99999999999999999999996570910644857895771110649954') mp.dps = 15 try: u = hyp2f1(-5,5,0.5,0.5) raise AssertionError("hyp2f1(-5,5,0.5,0.5) (failed zero detection)") except (mp.NoConvergence, ValueError): pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_power.py0000644000175000017500000001173712014170666026305 0ustar georgeskgeorgeskfrom sympy.mpmath import * from sympy.mpmath.libmp import * import random def test_fractional_pow(): assert mpf(16) ** 2.5 == 1024 assert mpf(64) ** 0.5 == 8 assert mpf(64) ** -0.5 == 0.125 assert mpf(16) ** -2.5 == 0.0009765625 assert (mpf(10) ** 0.5).ae(3.1622776601683791) assert (mpf(10) ** 2.5).ae(316.2277660168379) assert (mpf(10) ** -0.5).ae(0.31622776601683794) assert (mpf(10) ** -2.5).ae(0.0031622776601683794) assert (mpf(10) ** 0.3).ae(1.9952623149688795) assert (mpf(10) ** -0.3).ae(0.50118723362727224) def test_pow_integer_direction(): """ Test that inexact integer powers are rounded in the right direction. """ random.seed(1234) for prec in [10, 53, 200]: for i in range(50): a = random.randint(1<<(prec-1), 1< ab def test_pow_epsilon_rounding(): """ Stress test directed rounding for powers with integer exponents. Basically, we look at the following cases: >>> 1.0001 ** -5 0.99950014996500702 >>> 0.9999 ** -5 1.000500150035007 >>> (-1.0001) ** -5 -0.99950014996500702 >>> (-0.9999) ** -5 -1.000500150035007 >>> 1.0001 ** -6 0.99940020994401269 >>> 0.9999 ** -6 1.0006002100560125 >>> (-1.0001) ** -6 0.99940020994401269 >>> (-0.9999) ** -6 1.0006002100560125 etc. We run the tests with values a very small epsilon away from 1: small enough that the result is indistinguishable from 1 when rounded to nearest at the output precision. We check that the result is not erroneously rounded to 1 in cases where the rounding should be done strictly away from 1. """ def powr(x, n, r): return make_mpf(mpf_pow_int(x._mpf_, n, mp.prec, r)) for (inprec, outprec) in [(100, 20), (5000, 3000)]: mp.prec = inprec pos10001 = mpf(1) + mpf(2)**(-inprec+5) pos09999 = mpf(1) - mpf(2)**(-inprec+5) neg10001 = -pos10001 neg09999 = -pos09999 mp.prec = outprec r = round_up assert powr(pos10001, 5, r) > 1 assert powr(pos09999, 5, r) == 1 assert powr(neg10001, 5, r) < -1 assert powr(neg09999, 5, r) == -1 assert powr(pos10001, 6, r) > 1 assert powr(pos09999, 6, r) == 1 assert powr(neg10001, 6, r) > 1 assert powr(neg09999, 6, r) == 1 assert powr(pos10001, -5, r) == 1 assert powr(pos09999, -5, r) > 1 assert powr(neg10001, -5, r) == -1 assert powr(neg09999, -5, r) < -1 assert powr(pos10001, -6, r) == 1 assert powr(pos09999, -6, r) > 1 assert powr(neg10001, -6, r) == 1 assert powr(neg09999, -6, r) > 1 r = round_down assert powr(pos10001, 5, r) == 1 assert powr(pos09999, 5, r) < 1 assert powr(neg10001, 5, r) == -1 assert powr(neg09999, 5, r) > -1 assert powr(pos10001, 6, r) == 1 assert powr(pos09999, 6, r) < 1 assert powr(neg10001, 6, r) == 1 assert powr(neg09999, 6, r) < 1 assert powr(pos10001, -5, r) < 1 assert powr(pos09999, -5, r) == 1 assert powr(neg10001, -5, r) > -1 assert powr(neg09999, -5, r) == -1 assert powr(pos10001, -6, r) < 1 assert powr(pos09999, -6, r) == 1 assert powr(neg10001, -6, r) < 1 assert powr(neg09999, -6, r) == 1 r = round_ceiling assert powr(pos10001, 5, r) > 1 assert powr(pos09999, 5, r) == 1 assert powr(neg10001, 5, r) == -1 assert powr(neg09999, 5, r) > -1 assert powr(pos10001, 6, r) > 1 assert powr(pos09999, 6, r) == 1 assert powr(neg10001, 6, r) > 1 assert powr(neg09999, 6, r) == 1 assert powr(pos10001, -5, r) == 1 assert powr(pos09999, -5, r) > 1 assert powr(neg10001, -5, r) > -1 assert powr(neg09999, -5, r) == -1 assert powr(pos10001, -6, r) == 1 assert powr(pos09999, -6, r) > 1 assert powr(neg10001, -6, r) == 1 assert powr(neg09999, -6, r) > 1 r = round_floor assert powr(pos10001, 5, r) == 1 assert powr(pos09999, 5, r) < 1 assert powr(neg10001, 5, r) < -1 assert powr(neg09999, 5, r) == -1 assert powr(pos10001, 6, r) == 1 assert powr(pos09999, 6, r) < 1 assert powr(neg10001, 6, r) == 1 assert powr(neg09999, 6, r) < 1 assert powr(pos10001, -5, r) < 1 assert powr(pos09999, -5, r) == 1 assert powr(neg10001, -5, r) == -1 assert powr(neg09999, -5, r) < -1 assert powr(pos10001, -6, r) < 1 assert powr(pos09999, -6, r) == 1 assert powr(neg10001, -6, r) < 1 assert powr(neg09999, -6, r) == 1 mp.dps = 15 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_calculus.py0000644000175000017500000000344112014170666026755 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_approximation(): mp.dps = 15 f = lambda x: cos(2-2*x)/x p, err = chebyfit(f, [2, 4], 8, error=True) assert err < 1e-5 for i in range(10): x = 2 + i/5. assert abs(polyval(p, x) - f(x)) < err def test_limits(): mp.dps = 15 assert limit(lambda x: (x-sin(x))/x**3, 0).ae(mpf(1)/6) assert limit(lambda n: (1+1/n)**n, inf).ae(e) def test_polyval(): assert polyval([], 3) == 0 assert polyval([0], 3) == 0 assert polyval([5], 3) == 5 # 4x^3 - 2x + 5 p = [4, 0, -2, 5] assert polyval(p,4) == 253 assert polyval(p,4,derivative=True) == (253, 190) def test_polyroots(): p = polyroots([1,-4]) assert p[0].ae(4) p, q = polyroots([1,2,3]) assert p.ae(-1 - sqrt(2)*j) assert q.ae(-1 + sqrt(2)*j) #this is not a real test, it only tests a specific case assert polyroots([1]) == [] try: polyroots([0]) assert False except ValueError: pass def test_pade(): one = mpf(1) mp.dps = 20 N = 10 a = [one] k = 1 for i in range(1, N+1): k *= i a.append(one/k) p, q = pade(a, N//2, N//2) for x in arange(0, 1, 0.1): r = polyval(p[::-1], x)/polyval(q[::-1], x) assert(r.ae(exp(x), 1.0e-10)) mp.dps = 15 def test_fourier(): mp.dps = 15 c, s = fourier(lambda x: x+1, [-1, 2], 2) #plot([lambda x: x+1, lambda x: fourierval((c, s), [-1, 2], x)], [-1, 2]) assert c[0].ae(1.5) assert c[1].ae(-3*sqrt(3)/(2*pi)) assert c[2].ae(3*sqrt(3)/(4*pi)) assert s[0] == 0 assert s[1].ae(3/(2*pi)) assert s[2].ae(3/(4*pi)) assert fourierval((c, s), [-1, 2], 1).ae(1.9134966715663442) def test_differint(): mp.dps = 15 assert differint(lambda t: t, 2, -0.5).ae(8*sqrt(2/pi)/3) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_str.py0000644000175000017500000000104712014170666025752 0ustar georgeskgeorgeskfrom sympy.mpmath import nstr, matrix, inf def test_nstr(): m = matrix([[0.75, 0.190940654, -0.0299195971], [0.190940654, 0.65625, 0.205663228], [-0.0299195971, 0.205663228, 0.64453125e-20]]) assert nstr(m, 4, min_fixed=-inf) == \ '''[ 0.75 0.1909 -0.02992] [ 0.1909 0.6563 0.2057] [-0.02992 0.2057 0.000000000000000000006445]''' assert nstr(m, 4) == \ '''[ 0.75 0.1909 -0.02992] [ 0.1909 0.6563 0.2057] [-0.02992 0.2057 6.445e-21]''' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_ode.py0000644000175000017500000000345212014170666025713 0ustar georgeskgeorgesk#from sympy.mpmath.calculus import ODE_step_euler, ODE_step_rk4, odeint, arange from sympy.mpmath import odefun, cos, sin, mpf, sinc, mp ''' solvers = [ODE_step_euler, ODE_step_rk4] def test_ode1(): """ Let's solve: x'' + w**2 * x = 0 i.e. x1 = x, x2 = x1': x1' = x2 x2' = -x1 """ def derivs((x1, x2), t): return x2, -x1 for solver in solvers: t = arange(0, 3.1415926, 0.005) sol = odeint(derivs, (0., 1.), t, solver) x1 = [a[0] for a in sol] x2 = [a[1] for a in sol] # the result is x1 = sin(t), x2 = cos(t) # let's just check the end points for t = pi assert abs(x1[-1]) < 1e-2 assert abs(x2[-1] - (-1)) < 1e-2 def test_ode2(): """ Let's solve: x' - x = 0 i.e. x = exp(x) """ def derivs((x), t): return x for solver in solvers: t = arange(0, 1, 1e-3) sol = odeint(derivs, (1.,), t, solver) x = [a[0] for a in sol] # the result is x = exp(t) # let's just check the end point for t = 1, i.e. x = e assert abs(x[-1] - 2.718281828) < 1e-2 ''' def test_odefun_rational(): mp.dps = 15 # A rational function f = lambda t: 1/(1+mpf(t)**2) g = odefun(lambda x, y: [-2*x*y[0]**2], 0, [f(0)]) assert f(2).ae(g(2)[0]) def test_odefun_sinc_large(): mp.dps = 15 # Sinc function; test for large x f = sinc g = odefun(lambda x, y: [(cos(x)-y[0])/x], 1, [f(1)], tol=0.01, degree=5) assert abs(f(100) - g(100)[0])/f(100) < 0.01 def test_odefun_harmonic(): mp.dps = 15 # Harmonic oscillator f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) for x in [0, 1, 2.5, 8, 3.7]: # we go back to 3.7 to check caching c, s = f(x) assert c.ae(cos(x)) assert s.ae(sin(x)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_convert.py0000644000175000017500000001630512014170666026625 0ustar georgeskgeorgeskimport random from sympy.mpmath import * from sympy.mpmath.libmp import * def test_basic_string(): """ Test basic string conversion """ mp.dps = 15 assert mpf('3') == mpf('3.0') == mpf('0003.') == mpf('0.03e2') == mpf(3.0) assert mpf('30') == mpf('30.0') == mpf('00030.') == mpf(30.0) for i in range(10): for j in range(10): assert mpf('%ie%i' % (i,j)) == i * 10**j assert str(mpf('25000.0')) == '25000.0' assert str(mpf('2500.0')) == '2500.0' assert str(mpf('250.0')) == '250.0' assert str(mpf('25.0')) == '25.0' assert str(mpf('2.5')) == '2.5' assert str(mpf('0.25')) == '0.25' assert str(mpf('0.025')) == '0.025' assert str(mpf('0.0025')) == '0.0025' assert str(mpf('0.00025')) == '0.00025' assert str(mpf('0.000025')) == '2.5e-5' assert str(mpf(0)) == '0.0' assert str(mpf('2.5e1000000000000000000000')) == '2.5e+1000000000000000000000' assert str(mpf('2.6e-1000000000000000000000')) == '2.6e-1000000000000000000000' assert str(mpf(1.23402834e-15)) == '1.23402834e-15' assert str(mpf(-1.23402834e-15)) == '-1.23402834e-15' assert str(mpf(-1.2344e-15)) == '-1.2344e-15' assert repr(mpf(-1.2344e-15)) == "mpf('-1.2343999999999999e-15')" def test_pretty(): mp.pretty = True assert repr(mpf(2.5)) == '2.5' assert repr(mpc(2.5,3.5)) == '(2.5 + 3.5j)' mp.pretty = False iv.pretty = True assert repr(mpi(2.5,3.5)) == '[2.5, 3.5]' iv.pretty = False def test_str_whitespace(): assert mpf('1.26 ') == 1.26 def test_unicode(): mp.dps = 15 try: unicode = unicode except NameError: unicode = str assert mpf(unicode('2.76')) == 2.76 assert mpf(unicode('inf')) == inf def test_str_format(): assert to_str(from_float(0.1),15,strip_zeros=False) == '0.100000000000000' assert to_str(from_float(0.0),15,show_zero_exponent=True) == '0.0e+0' assert to_str(from_float(0.0),0,show_zero_exponent=True) == '.0e+0' assert to_str(from_float(0.0),0,show_zero_exponent=False) == '.0' assert to_str(from_float(0.0),1,show_zero_exponent=True) == '0.0e+0' assert to_str(from_float(0.0),1,show_zero_exponent=False) == '0.0' assert to_str(from_float(1.23),3,show_zero_exponent=True) == '1.23e+0' assert to_str(from_float(1.23456789000000e-2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e-2' assert to_str(from_float(1.23456789000000e+2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e+2' assert to_str(from_float(2.1287e14), 15, max_fixed=1000) == '212870000000000.0' assert to_str(from_float(2.1287e15), 15, max_fixed=1000) == '2128700000000000.0' assert to_str(from_float(2.1287e16), 15, max_fixed=1000) == '21287000000000000.0' assert to_str(from_float(2.1287e30), 15, max_fixed=1000) == '2128700000000000000000000000000.0' def test_tight_string_conversion(): mp.dps = 15 # In an old version, '0.5' wasn't recognized as representing # an exact binary number and was erroneously rounded up or down assert from_str('0.5', 10, round_floor) == fhalf assert from_str('0.5', 10, round_ceiling) == fhalf def test_eval_repr_invariant(): """Test that eval(repr(x)) == x""" random.seed(123) for dps in [10, 15, 20, 50, 100]: mp.dps = dps for i in range(1000): a = mpf(random.random())**0.5 * 10**random.randint(-100, 100) assert eval(repr(a)) == a mp.dps = 15 def test_str_bugs(): mp.dps = 15 # Decimal rounding used to give the wrong exponent in some cases assert str(mpf('1e600')) == '1.0e+600' assert str(mpf('1e10000')) == '1.0e+10000' def test_str_prec0(): assert to_str(from_float(1.234), 0) == '.0e+0' assert to_str(from_float(1e-15), 0) == '.0e-15' assert to_str(from_float(1e+15), 0) == '.0e+15' assert to_str(from_float(-1e-15), 0) == '-.0e-15' assert to_str(from_float(-1e+15), 0) == '-.0e+15' def test_convert_rational(): mp.dps = 15 assert from_rational(30, 5, 53, round_nearest) == (0, 3, 1, 2) assert from_rational(-7, 4, 53, round_nearest) == (1, 7, -2, 3) assert to_rational((0, 1, -1, 1)) == (1, 2) def test_custom_class(): class mympf: @property def _mpf_(self): return mpf(3.5)._mpf_ class mympc: @property def _mpc_(self): return mpf(3.5)._mpf_, mpf(2.5)._mpf_ assert mpf(2) + mympf() == 5.5 assert mympf() + mpf(2) == 5.5 assert mpf(mympf()) == 3.5 assert mympc() + mpc(2) == mpc(5.5, 2.5) assert mpc(2) + mympc() == mpc(5.5, 2.5) assert mpc(mympc()) == (3.5+2.5j) def test_conversion_methods(): class SomethingRandom: pass class SomethingReal: def _mpmath_(self, prec, rounding): return mp.make_mpf(from_str('1.3', prec, rounding)) class SomethingComplex: def _mpmath_(self, prec, rounding): return mp.make_mpc((from_str('1.3', prec, rounding), \ from_str('1.7', prec, rounding))) x = mpf(3) z = mpc(3) a = SomethingRandom() y = SomethingReal() w = SomethingComplex() for d in [15, 45]: mp.dps = d assert (x+y).ae(mpf('4.3')) assert (y+x).ae(mpf('4.3')) assert (x+w).ae(mpc('4.3', '1.7')) assert (w+x).ae(mpc('4.3', '1.7')) assert (z+y).ae(mpc('4.3')) assert (y+z).ae(mpc('4.3')) assert (z+w).ae(mpc('4.3', '1.7')) assert (w+z).ae(mpc('4.3', '1.7')) x-y; y-x; x-w; w-x; z-y; y-z; z-w; w-z x*y; y*x; x*w; w*x; z*y; y*z; z*w; w*z x/y; y/x; x/w; w/x; z/y; y/z; z/w; w/z x**y; y**x; x**w; w**x; z**y; y**z; z**w; w**z x==y; y==x; x==w; w==x; z==y; y==z; z==w; w==z mp.dps = 15 assert x.__add__(a) is NotImplemented assert x.__radd__(a) is NotImplemented assert x.__lt__(a) is NotImplemented assert x.__gt__(a) is NotImplemented assert x.__le__(a) is NotImplemented assert x.__ge__(a) is NotImplemented assert x.__eq__(a) is NotImplemented assert x.__ne__(a) is NotImplemented # implementation detail if hasattr(x, "__cmp__"): assert x.__cmp__(a) is NotImplemented assert x.__sub__(a) is NotImplemented assert x.__rsub__(a) is NotImplemented assert x.__mul__(a) is NotImplemented assert x.__rmul__(a) is NotImplemented assert x.__div__(a) is NotImplemented assert x.__rdiv__(a) is NotImplemented assert x.__mod__(a) is NotImplemented assert x.__rmod__(a) is NotImplemented assert x.__pow__(a) is NotImplemented assert x.__rpow__(a) is NotImplemented assert z.__add__(a) is NotImplemented assert z.__radd__(a) is NotImplemented assert z.__eq__(a) is NotImplemented assert z.__ne__(a) is NotImplemented assert z.__sub__(a) is NotImplemented assert z.__rsub__(a) is NotImplemented assert z.__mul__(a) is NotImplemented assert z.__rmul__(a) is NotImplemented assert z.__div__(a) is NotImplemented assert z.__rdiv__(a) is NotImplemented assert z.__pow__(a) is NotImplemented assert z.__rpow__(a) is NotImplemented def test_mpmathify(): assert mpmathify('1/2') == 0.5 assert mpmathify('(1.0+1.0j)') == mpc(1, 1) assert mpmathify('(1.2e-10 - 3.4e5j)') == mpc('1.2e-10', '-3.4e5') assert mpmathify('1j') == mpc(1j) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_compatibility.py0000644000175000017500000000441012014170666030010 0ustar georgeskgeorgeskfrom sympy.mpmath import * from random import seed, randint, random import math # Test compatibility with Python floats, which are # IEEE doubles (53-bit) N = 5000 seed(1) # Choosing exponents between roughly -140, 140 ensures that # the Python floats don't overflow or underflow xs = [(random()-1) * 10**randint(-140, 140) for x in range(N)] ys = [(random()-1) * 10**randint(-140, 140) for x in range(N)] # include some equal values ys[int(N*0.8):] = xs[int(N*0.8):] # Detect whether Python is compiled to use 80-bit floating-point # instructions, in which case the double compatibility test breaks uses_x87 = -4.1974624032366689e+117 / -8.4657370748010221e-47 \ == 4.9581771393902231e+163 def test_double_compatibility(): mp.prec = 53 for x, y in zip(xs, ys): mpx = mpf(x) mpy = mpf(y) assert mpf(x) == x assert (mpx < mpy) == (x < y) assert (mpx > mpy) == (x > y) assert (mpx == mpy) == (x == y) assert (mpx != mpy) == (x != y) assert (mpx <= mpy) == (x <= y) assert (mpx >= mpy) == (x >= y) assert mpx == mpx if uses_x87: mp.prec = 64 a = mpx + mpy b = mpx * mpy c = mpx / mpy d = mpx % mpy mp.prec = 53 assert +a == x + y assert +b == x * y assert +c == x / y assert +d == x % y else: assert mpx + mpy == x + y assert mpx * mpy == x * y assert mpx / mpy == x / y assert mpx % mpy == x % y assert abs(mpx) == abs(x) assert mpf(repr(x)) == x assert ceil(mpx) == math.ceil(x) assert floor(mpx) == math.floor(x) def test_sqrt(): # this fails quite often. it appers to be float # that rounds the wrong way, not mpf fail = 0 mp.prec = 53 for x in xs: x = abs(x) mp.prec = 100 mp_high = mpf(x)**0.5 mp.prec = 53 mp_low = mpf(x)**0.5 fp = x**0.5 assert abs(mp_low-mp_high) <= abs(fp-mp_high) fail += mp_low != fp assert fail < N/10 def test_bugs(): # particular bugs assert mpf(4.4408920985006262E-16) < mpf(1.7763568394002505E-15) assert mpf(-4.4408920985006262E-16) > mpf(-1.7763568394002505E-15) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_linalg.py0000644000175000017500000001631112014170666026410 0ustar georgeskgeorgesk# TODO: don't use round from __future__ import division from sympy.mpmath import * # XXX: these shouldn't be visible(?) LU_decomp = mp.LU_decomp L_solve = mp.L_solve U_solve = mp.U_solve householder = mp.householder improve_solution = mp.improve_solution A1 = matrix([[3, 1, 6], [2, 1, 3], [1, 1, 1]]) b1 = [2, 7, 4] A2 = matrix([[ 2, -1, -1, 2], [ 6, -2, 3, -1], [-4, 2, 3, -2], [ 2, 0, 4, -3]]) b2 = [3, -3, -2, -1] A3 = matrix([[ 1, 0, -1, -1, 0], [ 0, 1, 1, 0, -1], [ 4, -5, 2, 0, 0], [ 0, 0, -2, 9,-12], [ 0, 5, 0, 0, 12]]) b3 = [0, 0, 0, 0, 50] A4 = matrix([[10.235, -4.56, 0., -0.035, 5.67], [-2.463, 1.27, 3.97, -8.63, 1.08], [-6.58, 0.86, -0.257, 9.32, -43.6 ], [ 9.83, 7.39, -17.25, 0.036, 24.86], [-9.31, 34.9, 78.56, 1.07, 65.8 ]]) b4 = [8.95, 20.54, 7.42, 5.60, 58.43] A5 = matrix([[ 1, 2, -4], [-2, -3, 5], [ 3, 5, -8]]) A6 = matrix([[ 1.377360, 2.481400, 5.359190], [ 2.679280, -1.229560, 25.560210], [-1.225280+1.e6, 9.910180, -35.049900-1.e6]]) b6 = [23.500000, -15.760000, 2.340000] A7 = matrix([[1, -0.5], [2, 1], [-2, 6]]) b7 = [3, 2, -4] A8 = matrix([[1, 2, 3], [-1, 0, 1], [-1, -2, -1], [1, 0, -1]]) b8 = [1, 2, 3, 4] A9 = matrix([[ 4, 2, -2], [ 2, 5, -4], [-2, -4, 5.5]]) b9 = [10, 16, -15.5] A10 = matrix([[1.0 + 1.0j, 2.0, 2.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]) b10 = [1.0, 1.0 + 1.0j, 1.0] def test_LU_decomp(): A = A3.copy() b = b3 A, p = LU_decomp(A) y = L_solve(A, b, p) x = U_solve(A, y) assert p == [2, 1, 2, 3] assert [round(i, 14) for i in x] == [3.78953107960742, 2.9989094874591098, -0.081788440567070006, 3.8713195201744801, 2.9171210468920399] A = A4.copy() b = b4 A, p = LU_decomp(A) y = L_solve(A, b, p) x = U_solve(A, y) assert p == [0, 3, 4, 3] assert [round(i, 14) for i in x] == [2.6383625899619201, 2.6643834462368399, 0.79208015947958998, -2.5088376454101899, -1.0567657691375001] A = randmatrix(3) bak = A.copy() LU_decomp(A, overwrite=1) assert A != bak def test_inverse(): for A in [A1, A2, A5]: inv = inverse(A) assert mnorm(A*inv - eye(A.rows), 1) < 1.e-14 def test_householder(): mp.dps = 15 A, b = A8, b8 H, p, x, r = householder(extend(A, b)) assert H == matrix( [[mpf('3.0'), mpf('-2.0'), mpf('-1.0'), 0], [-1.0,mpf('3.333333333333333'),mpf('-2.9999999999999991'),mpf('2.0')], [-1.0, mpf('-0.66666666666666674'),mpf('2.8142135623730948'), mpf('-2.8284271247461898')], [1.0, mpf('-1.3333333333333333'),mpf('-0.20000000000000018'), mpf('4.2426406871192857')]]) assert p == [-2, -2, mpf('-1.4142135623730949')] assert round(norm(r, 2), 10) == 4.2426406870999998 y = [102.102, 58.344, 36.463, 24.310, 17.017, 12.376, 9.282, 7.140, 5.610, 4.488, 3.6465, 3.003] def coeff(n): # similiar to Hilbert matrix A = [] for i in range(1, 13): A.append([1. / (i + j - 1) for j in range(1, n + 1)]) return matrix(A) residuals = [] refres = [] for n in range(2, 7): A = coeff(n) H, p, x, r = householder(extend(A, y)) x = matrix(x) y = matrix(y) residuals.append(norm(r, 2)) refres.append(norm(residual(A, x, y), 2)) assert [round(res, 10) for res in residuals] == [15.1733888877, 0.82378073210000002, 0.302645887, 0.0260109244, 0.00058653999999999998] assert norm(matrix(residuals) - matrix(refres), inf) < 1.e-13 def test_factorization(): A = randmatrix(5) P, L, U = lu(A) assert mnorm(P*A - L*U, 1) < 1.e-15 def test_solve(): assert norm(residual(A6, lu_solve(A6, b6), b6), inf) < 1.e-10 assert norm(residual(A7, lu_solve(A7, b7), b7), inf) < 1.5 assert norm(residual(A8, lu_solve(A8, b8), b8), inf) <= 3 + 1.e-10 assert norm(residual(A6, qr_solve(A6, b6)[0], b6), inf) < 1.e-10 assert norm(residual(A7, qr_solve(A7, b7)[0], b7), inf) < 1.5 assert norm(residual(A8, qr_solve(A8, b8)[0], b8), 2) <= 4.3 assert norm(residual(A10, lu_solve(A10, b10), b10), 2) < 1.e-10 assert norm(residual(A10, qr_solve(A10, b10)[0], b10), 2) < 1.e-10 def test_solve_overdet_complex(): A = matrix([[1, 2j], [3, 4j], [5, 6]]) b = matrix([1 + j, 2, -j]) assert norm(residual(A, lu_solve(A, b), b)) < 1.0208 def test_singular(): mp.dps = 15 A = [[5.6, 1.2], [7./15, .1]] B = repr(zeros(2)) b = [1, 2] def _assert_ZeroDivisionError(statement): try: eval(statement) assert False except (ZeroDivisionError, ValueError): pass for i in ['lu_solve(%s, %s)' % (A, b), 'lu_solve(%s, %s)' % (B, b), 'qr_solve(%s, %s)' % (A, b), 'qr_solve(%s, %s)' % (B, b)]: _assert_ZeroDivisionError(i) def test_cholesky(): assert fp.cholesky(fp.matrix(A9)) == fp.matrix([[2, 0, 0], [1, 2, 0], [-1, -3/2, 3/2]]) x = fp.cholesky_solve(A9, b9) assert fp.norm(fp.residual(A9, x, b9), fp.inf) == 0 def test_det(): assert det(A1) == 1 assert round(det(A2), 14) == 8 assert round(det(A3)) == 1834 assert round(det(A4)) == 4443376 assert det(A5) == 1 assert round(det(A6)) == 78356463 assert det(zeros(3)) == 0 def test_cond(): mp.dps = 15 A = matrix([[1.2969, 0.8648], [0.2161, 0.1441]]) assert cond(A, lambda x: mnorm(x,1)) == mpf('327065209.73817754') assert cond(A, lambda x: mnorm(x,inf)) == mpf('327065209.73817754') assert cond(A, lambda x: mnorm(x,'F')) == mpf('249729266.80008656') @extradps(50) def test_precision(): A = randmatrix(10, 10) assert mnorm(inverse(inverse(A)) - A, 1) < 1.e-45 def test_interval_matrix(): a = iv.matrix([['0.1','0.3','1.0'],['7.1','5.5','4.8'],['3.2','4.4','5.6']]) b = iv.matrix(['4','0.6','0.5']) c = iv.lu_solve(a, b) assert c[0].delta < 1e-13 assert c[1].delta < 1e-13 assert c[2].delta < 1e-13 assert 5.25823271130625686059275 in c[0] assert -13.155049396267837541163 in c[1] assert 7.42069154774972557628979 in c[2] def test_LU_cache(): A = randmatrix(3) LU = LU_decomp(A) assert A._LU == LU_decomp(A) A[0,0] = -1000 assert A._LU is None def test_improve_solution(): A = randmatrix(5, min=1e-20, max=1e20) b = randmatrix(5, 1, min=-1000, max=1000) x1 = lu_solve(A, b) + randmatrix(5, 1, min=-1e-5, max=1.e-5) x2 = improve_solution(A, x1, b) assert norm(residual(A, x2, b), 2) < norm(residual(A, x1, b), 2) def test_exp_pade(): for i in range(3): dps = 15 extra = 5 mp.dps = dps + extra dm = 0 while not dm: m = randmatrix(3) dm = det(m) m = m/dm a = diag([1,2,3]) a1 = m**-1 * a * m mp.dps = dps e1 = expm(a1, method='pade') mp.dps = dps + extra e2 = m * a1 * m**-1 d = e2 - a #print d mp.dps = dps assert norm(d, inf).ae(0) mp.dps = 15 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_identify.py0000644000175000017500000000127212014170666026755 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_pslq(): mp.dps = 15 assert pslq([3*pi+4*e/7, pi, e, log(2)]) == [7, -21, -4, 0] assert pslq([4.9999999999999991, 1]) == [1, -5] assert pslq([2,1]) == [1, -2] def test_identify(): mp.dps = 20 assert identify(zeta(4), ['log(2)', 'pi**4']) == '((1/90)*pi**4)' mp.dps = 15 assert identify(exp(5)) == 'exp(5)' assert identify(exp(4)) == 'exp(4)' assert identify(log(5)) == 'log(5)' assert identify(exp(3*pi), ['pi']) == 'exp((3*pi))' assert identify(3, full=True) == ['3', '3', '1/(1/3)', 'sqrt(9)', '1/sqrt((1/9))', '(sqrt(12)/2)**2', '1/(sqrt(12)/6)**2'] assert identify(pi+1, {'a':+pi}) == '(1 + 1*a)' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_diff.py0000644000175000017500000000417512014170666026057 0ustar georgeskgeorgeskfrom sympy.mpmath import * def test_diff(): mp.dps = 15 assert diff(log, 2.0, n=0).ae(log(2)) assert diff(cos, 1.0).ae(-sin(1)) assert diff(abs, 0.0) == 0 assert diff(abs, 0.0, direction=1) == 1 assert diff(abs, 0.0, direction=-1) == -1 assert diff(exp, 1.0).ae(e) assert diff(exp, 1.0, n=5).ae(e) assert diff(exp, 2.0, n=5, direction=3*j).ae(e**2) assert diff(lambda x: x**2, 3.0, method='quad').ae(6) assert diff(lambda x: 3+x**5, 3.0, n=2, method='quad').ae(540) assert diff(lambda x: 3+x**5, 3.0, n=2, method='step').ae(540) assert diffun(sin)(2).ae(cos(2)) assert diffun(sin, n=2)(2).ae(-sin(2)) def test_taylor(): mp.dps = 15 # Easy to test since the coefficients are exact in floating-point assert taylor(sqrt, 1, 4) == [1, 0.5, -0.125, 0.0625, -0.0390625] def test_diff_partial(): mp.dps = 15 x,y,z = xyz = 2,3,7 f = lambda x,y,z: 3*x**2 * (y+2)**3 * z**5 assert diff(f, xyz, (0,0,0)).ae(25210500) assert diff(f, xyz, (0,0,1)).ae(18007500) assert diff(f, xyz, (0,0,2)).ae(10290000) assert diff(f, xyz, (0,1,0)).ae(15126300) assert diff(f, xyz, (0,1,1)).ae(10804500) assert diff(f, xyz, (0,1,2)).ae(6174000) assert diff(f, xyz, (0,2,0)).ae(6050520) assert diff(f, xyz, (0,2,1)).ae(4321800) assert diff(f, xyz, (0,2,2)).ae(2469600) assert diff(f, xyz, (1,0,0)).ae(25210500) assert diff(f, xyz, (1,0,1)).ae(18007500) assert diff(f, xyz, (1,0,2)).ae(10290000) assert diff(f, xyz, (1,1,0)).ae(15126300) assert diff(f, xyz, (1,1,1)).ae(10804500) assert diff(f, xyz, (1,1,2)).ae(6174000) assert diff(f, xyz, (1,2,0)).ae(6050520) assert diff(f, xyz, (1,2,1)).ae(4321800) assert diff(f, xyz, (1,2,2)).ae(2469600) assert diff(f, xyz, (2,0,0)).ae(12605250) assert diff(f, xyz, (2,0,1)).ae(9003750) assert diff(f, xyz, (2,0,2)).ae(5145000) assert diff(f, xyz, (2,1,0)).ae(7563150) assert diff(f, xyz, (2,1,1)).ae(5402250) assert diff(f, xyz, (2,1,2)).ae(3087000) assert diff(f, xyz, (2,2,0)).ae(3025260) assert diff(f, xyz, (2,2,1)).ae(2160900) assert diff(f, xyz, (2,2,2)).ae(1234800) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/test_mpmath.py0000644000175000017500000000011212014170666026420 0ustar georgeskgeorgeskfrom sympy.mpmath.libmp import * from sympy.mpmath import * import random wxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/tests/__init__.py0000644000175000017500000000000012014170666025626 0ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/mpmath/__init__.py0000644000175000017500000002003612014170666024477 0ustar georgeskgeorgesk__version__ = '0.17' from .usertools import monitor, timing from .ctx_fp import FPContext from .ctx_mp import MPContext from .ctx_iv import MPIntervalContext fp = FPContext() mp = MPContext() iv = MPIntervalContext() fp._mp = mp mp._mp = mp iv._mp = mp mp._fp = fp fp._fp = fp mp._iv = iv fp._iv = iv iv._iv = iv # XXX: extremely bad pickle hack from . import ctx_mp as _ctx_mp _ctx_mp._mpf_module.mpf = mp.mpf _ctx_mp._mpf_module.mpc = mp.mpc make_mpf = mp.make_mpf make_mpc = mp.make_mpc extraprec = mp.extraprec extradps = mp.extradps workprec = mp.workprec workdps = mp.workdps autoprec = mp.autoprec maxcalls = mp.maxcalls memoize = mp.memoize mag = mp.mag bernfrac = mp.bernfrac qfrom = mp.qfrom mfrom = mp.mfrom kfrom = mp.kfrom taufrom = mp.taufrom qbarfrom = mp.qbarfrom ellipfun = mp.ellipfun jtheta = mp.jtheta kleinj = mp.kleinj qp = mp.qp qhyper = mp.qhyper qgamma = mp.qgamma qfac = mp.qfac nint_distance = mp.nint_distance plot = mp.plot cplot = mp.cplot splot = mp.splot odefun = mp.odefun jacobian = mp.jacobian findroot = mp.findroot multiplicity = mp.multiplicity isinf = mp.isinf isnan = mp.isnan isnormal = mp.isnormal isint = mp.isint almosteq = mp.almosteq nan = mp.nan rand = mp.rand absmin = mp.absmin absmax = mp.absmax fraction = mp.fraction linspace = mp.linspace arange = mp.arange mpmathify = convert = mp.convert mpc = mp.mpc mpi = iv._mpi nstr = mp.nstr nprint = mp.nprint chop = mp.chop fneg = mp.fneg fadd = mp.fadd fsub = mp.fsub fmul = mp.fmul fdiv = mp.fdiv fprod = mp.fprod quad = mp.quad quadgl = mp.quadgl quadts = mp.quadts quadosc = mp.quadosc pslq = mp.pslq identify = mp.identify findpoly = mp.findpoly richardson = mp.richardson shanks = mp.shanks nsum = mp.nsum nprod = mp.nprod difference = mp.difference diff = mp.diff diffs = mp.diffs diffs_prod = mp.diffs_prod diffs_exp = mp.diffs_exp diffun = mp.diffun differint = mp.differint taylor = mp.taylor pade = mp.pade polyval = mp.polyval polyroots = mp.polyroots fourier = mp.fourier fourierval = mp.fourierval sumem = mp.sumem sumap = mp.sumap chebyfit = mp.chebyfit limit = mp.limit matrix = mp.matrix eye = mp.eye diag = mp.diag zeros = mp.zeros ones = mp.ones hilbert = mp.hilbert randmatrix = mp.randmatrix swap_row = mp.swap_row extend = mp.extend norm = mp.norm mnorm = mp.mnorm lu_solve = mp.lu_solve lu = mp.lu unitvector = mp.unitvector inverse = mp.inverse residual = mp.residual qr_solve = mp.qr_solve cholesky = mp.cholesky cholesky_solve = mp.cholesky_solve det = mp.det cond = mp.cond expm = mp.expm sqrtm = mp.sqrtm powm = mp.powm logm = mp.logm sinm = mp.sinm cosm = mp.cosm mpf = mp.mpf j = mp.j exp = mp.exp expj = mp.expj expjpi = mp.expjpi ln = mp.ln im = mp.im re = mp.re inf = mp.inf ninf = mp.ninf sign = mp.sign eps = mp.eps pi = mp.pi ln2 = mp.ln2 ln10 = mp.ln10 phi = mp.phi e = mp.e euler = mp.euler catalan = mp.catalan khinchin = mp.khinchin glaisher = mp.glaisher apery = mp.apery degree = mp.degree twinprime = mp.twinprime mertens = mp.mertens ldexp = mp.ldexp frexp = mp.frexp fsum = mp.fsum fdot = mp.fdot sqrt = mp.sqrt cbrt = mp.cbrt exp = mp.exp ln = mp.ln log = mp.log log10 = mp.log10 power = mp.power cos = mp.cos sin = mp.sin tan = mp.tan cosh = mp.cosh sinh = mp.sinh tanh = mp.tanh acos = mp.acos asin = mp.asin atan = mp.atan asinh = mp.asinh acosh = mp.acosh atanh = mp.atanh sec = mp.sec csc = mp.csc cot = mp.cot sech = mp.sech csch = mp.csch coth = mp.coth asec = mp.asec acsc = mp.acsc acot = mp.acot asech = mp.asech acsch = mp.acsch acoth = mp.acoth cospi = mp.cospi sinpi = mp.sinpi sinc = mp.sinc sincpi = mp.sincpi cos_sin = mp.cos_sin cospi_sinpi = mp.cospi_sinpi fabs = mp.fabs re = mp.re im = mp.im conj = mp.conj floor = mp.floor ceil = mp.ceil nint = mp.nint frac = mp.frac root = mp.root nthroot = mp.nthroot hypot = mp.hypot fmod = mp.fmod ldexp = mp.ldexp frexp = mp.frexp sign = mp.sign arg = mp.arg phase = mp.phase polar = mp.polar rect = mp.rect degrees = mp.degrees radians = mp.radians atan2 = mp.atan2 fib = mp.fib fibonacci = mp.fibonacci lambertw = mp.lambertw zeta = mp.zeta altzeta = mp.altzeta gamma = mp.gamma rgamma = mp.rgamma factorial = mp.factorial fac = mp.fac fac2 = mp.fac2 beta = mp.beta betainc = mp.betainc psi = mp.psi #psi0 = mp.psi0 #psi1 = mp.psi1 #psi2 = mp.psi2 #psi3 = mp.psi3 polygamma = mp.polygamma digamma = mp.digamma #trigamma = mp.trigamma #tetragamma = mp.tetragamma #pentagamma = mp.pentagamma harmonic = mp.harmonic bernoulli = mp.bernoulli bernfrac = mp.bernfrac stieltjes = mp.stieltjes hurwitz = mp.hurwitz dirichlet = mp.dirichlet bernpoly = mp.bernpoly eulerpoly = mp.eulerpoly eulernum = mp.eulernum polylog = mp.polylog clsin = mp.clsin clcos = mp.clcos gammainc = mp.gammainc gammaprod = mp.gammaprod binomial = mp.binomial rf = mp.rf ff = mp.ff hyper = mp.hyper hyp0f1 = mp.hyp0f1 hyp1f1 = mp.hyp1f1 hyp1f2 = mp.hyp1f2 hyp2f1 = mp.hyp2f1 hyp2f2 = mp.hyp2f2 hyp2f0 = mp.hyp2f0 hyp2f3 = mp.hyp2f3 hyp3f2 = mp.hyp3f2 hyperu = mp.hyperu hypercomb = mp.hypercomb meijerg = mp.meijerg appellf1 = mp.appellf1 appellf2 = mp.appellf2 appellf3 = mp.appellf3 appellf4 = mp.appellf4 hyper2d = mp.hyper2d bihyper = mp.bihyper erf = mp.erf erfc = mp.erfc erfi = mp.erfi erfinv = mp.erfinv npdf = mp.npdf ncdf = mp.ncdf expint = mp.expint e1 = mp.e1 ei = mp.ei li = mp.li ci = mp.ci si = mp.si chi = mp.chi shi = mp.shi fresnels = mp.fresnels fresnelc = mp.fresnelc airyai = mp.airyai airybi = mp.airybi airyaizero = mp.airyaizero airybizero = mp.airybizero scorergi = mp.scorergi scorerhi = mp.scorerhi ellipk = mp.ellipk ellipe = mp.ellipe ellipf = mp.ellipf ellippi = mp.ellippi elliprc = mp.elliprc elliprj = mp.elliprj elliprf = mp.elliprf elliprd = mp.elliprd elliprg = mp.elliprg agm = mp.agm jacobi = mp.jacobi chebyt = mp.chebyt chebyu = mp.chebyu legendre = mp.legendre legenp = mp.legenp legenq = mp.legenq hermite = mp.hermite pcfd = mp.pcfd pcfu = mp.pcfu pcfv = mp.pcfv pcfw = mp.pcfw gegenbauer = mp.gegenbauer laguerre = mp.laguerre spherharm = mp.spherharm besselj = mp.besselj j0 = mp.j0 j1 = mp.j1 besseli = mp.besseli bessely = mp.bessely besselk = mp.besselk besseljzero = mp.besseljzero besselyzero = mp.besselyzero hankel1 = mp.hankel1 hankel2 = mp.hankel2 struveh = mp.struveh struvel = mp.struvel angerj = mp.angerj webere = mp.webere lommels1 = mp.lommels1 lommels2 = mp.lommels2 whitm = mp.whitm whitw = mp.whitw ber = mp.ber bei = mp.bei ker = mp.ker kei = mp.kei coulombc = mp.coulombc coulombf = mp.coulombf coulombg = mp.coulombg lambertw = mp.lambertw barnesg = mp.barnesg superfac = mp.superfac hyperfac = mp.hyperfac loggamma = mp.loggamma siegeltheta = mp.siegeltheta siegelz = mp.siegelz grampoint = mp.grampoint zetazero = mp.zetazero riemannr = mp.riemannr primepi = mp.primepi primepi2 = mp.primepi2 primezeta = mp.primezeta bell = mp.bell polyexp = mp.polyexp expm1 = mp.expm1 powm1 = mp.powm1 unitroots = mp.unitroots cyclotomic = mp.cyclotomic mangoldt = mp.mangoldt secondzeta = mp.secondzeta nzeros = mp.nzeros backlunds = mp.backlunds lerchphi = mp.lerchphi # be careful when changing this name, don't use test*! def runtests(): """ Run all mpmath tests and print output. """ import os.path from inspect import getsourcefile from .tests import runtests as tests testdir = os.path.dirname(os.path.abspath(getsourcefile(tests))) importdir = os.path.abspath(testdir + '/../..') tests.testit(importdir, testdir) def doctests(): try: import psyco; psyco.full() except ImportError: pass import sys from timeit import default_timer as clock filter = [] for i, arg in enumerate(sys.argv): if '__init__.py' in arg: filter = [sn for sn in sys.argv[i+1:] if not sn.startswith("-")] break import doctest globs = globals().copy() for obj in globs: #sorted(globs.keys()): if filter: if not sum([pat in obj for pat in filter]): continue sys.stdout.write(str(obj) + " ") sys.stdout.flush() t1 = clock() doctest.run_docstring_examples(globs[obj], {}, verbose=("-v" in sys.argv)) t2 = clock() print(round(t2-t1, 3)) if __name__ == '__main__': doctests() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/slow_tests/0000755000175000017500000000000012014170666023305 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/slow_tests/test_core.py0000644000175000017500000000030312014170666025642 0ustar georgeskgeorgeskfrom sympy import integer_nthroot def test_integer_nthroot(): assert integer_nthroot(10**(500*500), 500) == (10**500, True) assert integer_nthroot(10**1000000, 100000) == (10**10, True) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/0000755000175000017500000000000012014170666022371 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/acceleration.py0000644000175000017500000000616512014170666025404 0ustar georgeskgeorgesk""" Convergence acceleration / extrapolation methods for series and sequences. References: Carl M. Bender & Steven A. Orszag, "Advanced Mathematical Methods for Scientists and Engineers: Asymptotic Methods and Perturbation Theory", Springer 1999. (Shanks transformation: pp. 368-375, Richardson extrapolation: pp. 375-377.) """ from sympy import factorial, Integer, S def richardson(A, k, n, N): """ Calculate an approximation for lim k->oo A(k) using Richardson extrapolation with the terms A(n), A(n+1), ..., A(n+N+1). Choosing N ~= 2*n often gives good results. A simple example is to calculate exp(1) using the limit definition. This limit converges slowly; n = 100 only produces two accurate digits: >>> from sympy.abc import n >>> e = (1 + 1/n)**n >>> print round(e.subs(n, 100).evalf(), 10) 2.7048138294 Richardson extrapolation with 11 appropriately chosen terms gives a value that is accurate to the indicated precision: >>> from sympy import E >>> from sympy.series.acceleration import richardson >>> print round(richardson(e, n, 10, 20).evalf(), 10) 2.7182818285 >>> print round(E.evalf(), 10) 2.7182818285 Another useful application is to speed up convergence of series. Computing 100 terms of the zeta(2) series 1/k**2 yields only two accurate digits: >>> from sympy.abc import k, n >>> from sympy import Sum >>> A = Sum(k**-2, (k, 1, n)) >>> print round(A.subs(n, 100).evalf(), 10) 1.6349839002 Richardson extrapolation performs much better: >>> from sympy import pi >>> print round(richardson(A, n, 10, 20).evalf(), 10) 1.6449340668 >>> print round(((pi**2)/6).evalf(), 10) # Exact value 1.6449340668 """ s = S.Zero for j in range(0, N+1): s += A.subs(k, Integer(n+j)).doit() * (n+j)**N * (-1)**(j+N) / \ (factorial(j) * factorial(N-j)) return s def shanks(A, k, n, m=1): """ Calculate an approximation for lim k->oo A(k) using the n-term Shanks transformation S(A)(n). With m > 1, calculate the m-fold recursive Shanks transformation S(S(...S(A)...))(n). The Shanks transformation is useful for summing Taylor series that converge slowly near a pole or singularity, e.g. for log(2): >>> from sympy.abc import k, n >>> from sympy import Sum, Integer >>> from sympy.series.acceleration import shanks >>> A = Sum(Integer(-1)**(k+1) / k, (k, 1, n)) >>> print round(A.subs(n, 100).doit().evalf(), 10) 0.6881721793 >>> print round(shanks(A, n, 25).evalf(), 10) 0.6931396564 >>> print round(shanks(A, n, 25, 5).evalf(), 10) 0.6931471806 The correct value is 0.6931471805599453094172321215. """ table = [A.subs(k, Integer(j)).doit() for j in range(n+m+2)] table2 = table[:] for i in range(1, m+1): for j in range(i, n+m+1): x, y, z = table[j-1], table[j], table[j+1] table2[j] = (z*x - y**2) / (z + x - 2*y) table = table2[:] return table[n] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/residues.py0000644000175000017500000000374412014170666024576 0ustar georgeskgeorgesk""" This module implements the Residue function and related tools for working with residues. """ from sympy import Wild, sympify, Integer, Add def residue(expr, x, x0): """ Finds the residue of ``expr`` at the point x=x0. The residue is defined as the coefficient of 1/(x-x0) in the power series expansion about x=x0. Examples: >>> from sympy import Symbol, residue, sin >>> x = Symbol("x") >>> residue(1/x, x, 0) 1 >>> residue(1/x**2, x, 0) 0 >>> residue(2/sin(x), x, 0) 2 This function is essential for the Residue Theorem [1]. The current implementation uses series expansion to calculate it. A more general implementation is explained in the section 5.6 of the Bronstein's book [2]. For purely rational functions, the algorithm is much easier. See sections 2.4, 2.5, and 2.7 (this section actually gives an algorithm for computing any Laurent series coefficient for a rational function). The theory in section 2.4 will help to understand why the resultant works in the general algorithm. For the definition of a resultant, see section 1.4 (and any previous sections for more review). [1] http://en.wikipedia.org/wiki/Residue_theorem [2] M. Bronstein: Symbolic Integration I, Springer Verlag (2005) """ from sympy import collect expr = sympify(expr) if x0 != 0: expr = expr.subs(x, x+x0) s = expr.series(x, 0, 0).removeO() # TODO: this sometimes helps, but the series expansion should rather be # fixed, see #1627: if s == 0: s = expr.series(x, 0, 6).removeO() s = collect(s, x) if x0 != 0: s = s.subs(x, x-x0) a = Wild("r", exclude=[x]) c = Wild("c", exclude=[x]) r = s.match(a/(x-x0)+c) if r: return r[a] elif isinstance(s, Add): # TODO: this is to overcome a bug in match (#1626) for t in s.args: r = t.match(a/(x-x0)+c) if r: return r[a] return Integer(0) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/limits.py0000644000175000017500000001675312014170666024260 0ustar georgeskgeorgeskfrom sympy.core import S, Add, sympify, Expr, PoleError, Mul, oo, C from gruntz import gruntz from sympy.functions import sign, tan, cot def limit(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. Examples: >>> from sympy import limit, sin, Symbol, oo >>> from sympy.abc import x >>> limit(sin(x)/x, x, 0) 1 >>> limit(1/x, x, 0, dir="+") oo >>> limit(1/x, x, 0, dir="-") -oo >>> limit(1/x, x, oo) 0 Strategy: First we try some heuristics for easy and frequent cases like "x", "1/x", "x**2" and similar, so that it's fast. For all other cases, we use the Gruntz algorithm (see the gruntz() function). """ from sympy import Wild, log e = sympify(e) z = sympify(z) z0 = sympify(z0) if e == z: return z0 if e.is_Rational: return e if not e.has(z): return e if e.func is tan: # discontinuity at odd multiples of pi/2; 0 at even disc = S.Pi/2 sign = 1 if dir == '-': sign *= -1 i = limit(sign*e.args[0], z, z0)/disc if i.is_integer: if i.is_even: return S.Zero elif i.is_odd: if dir == '+': return S.NegativeInfinity else: return S.Infinity if e.func is cot: # discontinuity at multiples of pi; 0 at odd pi/2 multiples disc = S.Pi sign = 1 if dir == '-': sign *= -1 i = limit(sign*e.args[0], z, z0)/disc if i.is_integer: if dir == '-': return S.NegativeInfinity else: return S.Infinity elif (2*i).is_integer: return S.Zero if e.is_Pow: b, ex = e.args c = None # records sign of b if b is +/-z or has a bounded value if b.is_Mul: c, b = b.as_two_terms() if c is S.NegativeOne and b == z: c = '-' elif b == z: c = '+' if ex.is_number: if c is None: base = b.subs(z, z0) if base.is_bounded and (ex.is_bounded or base is not S.One): return base**ex else: if z0 == 0 and ex < 0: if dir != c: # integer if ex.is_even: return S.Infinity elif ex.is_odd: return S.NegativeInfinity # rational elif ex.is_Rational: return (S.NegativeOne**ex)*S.Infinity else: return S.ComplexInfinity return S.Infinity return z0**ex if e.is_Mul or not z0 and e.is_Pow and b.func is log: if e.is_Mul: # weed out the z-independent terms i, d = e.as_independent(z) if i is not S.One and i.is_bounded: return i*limit(d, z, z0, dir) else: i, d = S.One, e if not z0: # look for log(z)**q or z**p*log(z)**q p, q = Wild("p"), Wild("q") r = d.match(z**p * log(z)**q) if r: p, q = [r.get(w, w) for w in [p, q]] if q and q.is_number and p.is_number: if q > 0: if p > 0: return S.Zero else: return -oo*i else: if p >= 0: return S.Zero else: return -oo*i if e.is_Add: if e.is_polynomial() and not z0.is_unbounded: return Add(*[limit(term, z, z0, dir) for term in e.args]) # this is a case like limit(x*y+x*z, z, 2) == x*y+2*x # but we need to make sure, that the general gruntz() algorithm is # executed for a case like "limit(sqrt(x+1)-sqrt(x),x,oo)==0" unbounded = []; unbounded_result=[] finite = []; unknown = [] ok = True for term in e.args: if not term.has(z) and not term.is_unbounded: finite.append(term) continue result = term.subs(z, z0) bounded = result.is_bounded if bounded is False or result is S.NaN: if unknown: ok = False break unbounded.append(term) if result != S.NaN: # take result from direction given result = limit(term, z, z0, dir) unbounded_result.append(result) elif bounded: finite.append(result) else: if unbounded: ok = False break unknown.append(result) if not ok: # we won't be able to resolve this with unbounded # terms, e.g. Sum(1/k, (k, 1, n)) - log(n) as n -> oo: # since the Sum is unevaluated it's boundedness is # unknown and the log(n) is oo so you get Sum - oo # which is unsatisfactory. raise NotImplementedError('unknown boundedness for %s' % (unknown or result)) u = Add(*unknown) if unbounded: inf_limit = Add(*unbounded_result) if inf_limit is not S.NaN: return inf_limit + u if finite: return Add(*finite) + limit(Add(*unbounded), z, z0, dir) + u else: return Add(*finite) + u if e.is_Order: args = e.args return C.Order(limit(args[0], z, z0), *args[1:]) try: r = gruntz(e, z, z0, dir) if r is S.NaN: raise PoleError() except PoleError: r = heuristics(e, z, z0, dir) return r def heuristics(e, z, z0, dir): if z0 == oo: return limit(e.subs(z, 1/z), z, sympify(0), "+") elif e.is_Mul: r = [] for a in e.args: if not a.is_bounded: r.append(a.limit(z, z0, dir)) if r: return Mul(*r) elif e.is_Add: r = [] for a in e.args: r.append(a.limit(z, z0, dir)) return Add(*r) elif e.is_Function: return e.subs(e.args[0], limit(e.args[0], z, z0, dir)) msg = "Don't know how to calculate the limit(%s, %s, %s, dir=%s), sorry." raise PoleError(msg % (e, z, z0, dir)) class Limit(Expr): """Represents an unevaluated limit. Examples: >>> from sympy import Limit, sin, Symbol >>> from sympy.abc import x >>> Limit(sin(x)/x, x, 0) Limit(sin(x)/x, x, 0) >>> Limit(1/x, x, 0, dir="-") Limit(1/x, x, 0, dir='-') """ def __new__(cls, e, z, z0, dir="+"): e = sympify(e) z = sympify(z) z0 = sympify(z0) obj = Expr.__new__(cls) obj._args = (e, z, z0, dir) return obj def doit(self, **hints): e, z, z0, dir = self.args if hints.get('deep', True): e = e.doit(**hints) z = z.doit(**hints) z0 = z0.doit(**hints) return limit(e, z, z0, dir) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/gruntz.py0000644000175000017500000005375012014170666024306 0ustar georgeskgeorgesk""" Limits ====== Implemented according to the PhD thesis http://www.cybertester.com/data/gruntz.pdf, which contains very thorough descriptions of the algorithm including many examples. We summarize here the gist of it. All functions are sorted according to how rapidly varying they are at infinity using the following rules. Any two functions f and g can be compared using the properties of L: L=lim log|f(x)| / log|g(x)| (for x -> oo) We define >, < ~ according to:: 1. f > g .... L=+-oo we say that: - f is greater than any power of g - f is more rapidly varying than g - f goes to infinity/zero faster than g 2. f < g .... L=0 we say that: - f is lower than any power of g 3. f ~ g .... L!=0, +-oo we say that: - both f and g are bounded from above and below by suitable integral powers of the other Examples ======== :: 2 < x < exp(x) < exp(x**2) < exp(exp(x)) 2 ~ 3 ~ -5 x ~ x**2 ~ x**3 ~ 1/x ~ x**m ~ -x exp(x) ~ exp(-x) ~ exp(2x) ~ exp(x)**2 ~ exp(x+exp(-x)) f ~ 1/f So we can divide all the functions into comparability classes (x and x^2 belong to one class, exp(x) and exp(-x) belong to some other class). In principle, we could compare any two functions, but in our algorithm, we don't compare anything below the class 2~3~-5 (for example log(x) is below this), so we set 2~3~-5 as the lowest comparability class. Given the function f, we find the list of most rapidly varying (mrv set) subexpressions of it. This list belongs to the same comparability class. Let's say it is {exp(x), exp(2x)}. Using the rule f ~ 1/f we find an element "w" (either from the list or a new one) from the same comparability class which goes to zero at infinity. In our example we set w=exp(-x) (but we could also set w=exp(-2x) or w=exp(-3x) ...). We rewrite the mrv set using w, in our case {1/w, 1/w^2}, and substitute it into f. Then we expand f into a series in w:: f = c0*w^e0 + c1*w^e1 + ... + O(w^en), where e0oo, lim f = lim c0*w^e0, because all the other terms go to zero, because w goes to zero faster than the ci and ei. So:: for e0>0, lim f = 0 for e0<0, lim f = +-oo (the sign depends on the sign of c0) for e0=0, lim f = lim c0 We need to recursively compute limits at several places of the algorithm, but as is shown in the PhD thesis, it always finishes. Important functions from the implementation: compare(a, b, x) compares "a" and "b" by computing the limit L. mrv(e, x) returns the list of most rapidly varying (mrv) subexpressions of "e" rewrite(e, Omega, x, wsym) rewrites "e" in terms of w leadterm(f, x) returns the lowest power term in the series of f mrv_leadterm(e, x) returns the lead term (c0, e0) for e limitinf(e, x) computes lim e (for x->oo) limit(e, z, z0) computes any limit by converting it to the case x->oo All the functions are really simple and straightforward except rewrite(), which is the most difficult/complex part of the algorithm. When the algorithm fails, the bugs are usually in the series expansion (i.e. in SymPy) or in rewrite. This code is almost exact rewrite of the Maple code inside the Gruntz thesis. Debugging --------- Because the gruntz algorithm is highly recursive, it's difficult to figure out what went wrong inside a debugger. Instead, turn on nice debug prints by defining the environment variable SYMPY_DEBUG. For example: [user@localhost]: SYMPY_DEBUG=True ./bin/isympy In [1]: limit(sin(x)/x, x, 0) limitinf(_x*sin(1/_x), _x) = 1 +-mrv_leadterm(_x*sin(1/_x), _x) = (1, 0) | +-mrv(_x*sin(1/_x), _x) = set([_x]) | | +-mrv(_x, _x) = set([_x]) | | +-mrv(sin(1/_x), _x) = set([_x]) | | +-mrv(1/_x, _x) = set([_x]) | | +-mrv(_x, _x) = set([_x]) | +-mrv_leadterm(exp(_x)*sin(exp(-_x)), _x, set([exp(_x)])) = (1, 0) | +-rewrite(exp(_x)*sin(exp(-_x)), set([exp(_x)]), _x, _w) = (1/_w*sin(_w), -_x) | +-sign(_x, _x) = 1 | +-mrv_leadterm(1, _x) = (1, 0) +-sign(0, _x) = 0 +-limitinf(1, _x) = 1 And check manually which line is wrong. Then go to the source code and debug this function to figure out the exact problem. """ from sympy import SYMPY_DEBUG from sympy.core import Basic, S, oo, Symbol, C, I, Dummy, Wild from sympy.core.function import Function, UndefinedFunction from sympy.functions import log, exp from sympy.series.order import Order from sympy.simplify import powsimp from sympy import cacheit from sympy.core.compatibility import reduce O = Order def debug(func): """Only for debugging purposes: prints a tree It will print a nice execution tree with arguments and results of all decorated functions. """ if not SYMPY_DEBUG: #normal mode - do nothing return func #debug mode def decorated(*args, **kwargs): #r = func(*args, **kwargs) r = maketree(func, *args, **kwargs) #print "%s = %s(%s, %s)" % (r, func.__name__, args, kwargs) return r return decorated from time import time it = 0 do_timings = False def timeit(func): global do_timings if not do_timings: return func def dec(*args, **kwargs): global it it += 1 t0 = time() r = func(*args, **kwargs) t1 = time() print "%s %.3f %s%s" % ('-' * (2+it), t1-t0, func.func_name, args) it -= 1 return r return dec def tree(subtrees): "Only debugging purposes: prints a tree" def indent(s, type=1): x = s.split("\n") r = "+-%s\n"%x[0] for a in x[1:]: if a == "": continue if type == 1: r += "| %s\n"%a else: r += " %s\n"%a return r if len(subtrees) == 0: return "" f = [] for a in subtrees[:-1]: f.append(indent(a)) f.append(indent(subtrees[-1], 2)) return ''.join(f) tmp = [] iter = 0 def maketree(f, *args, **kw): "Only debugging purposes: prints a tree" global tmp global iter oldtmp = tmp tmp = [] iter += 1 # If there is a bug and the algorithm enters an infinite loop, enable the # following line. It will print the names and parameters of all major functions # that are called, *before* they are called #print "%s%s %s%s" % (iter, reduce(lambda x, y: x + y,map(lambda x: '-',range(1,2+iter))), f.func_name, args) r = f(*args, **kw) iter -= 1 s = "%s%s = %s\n" % (f.func_name, args, r) if tmp != []: s += tree(tmp) tmp = oldtmp tmp.append(s) if iter == 0: print tmp[0] tmp = [] return r def compare(a, b, x): """Returns "<" if a" for a>b""" # log(exp(...)) must always be simplified here for termination la, lb = log(a), log(b) if isinstance(a, Basic) and a.func is exp: la = a.args[0] if isinstance(b, Basic) and b.func is exp: lb = b.args[0] c = limitinf(la/lb, x) if c == 0: return "<" elif c.is_unbounded: return ">" else: return "=" class SubsSet(dict): """ Stores (expr, dummy) pairs, and how to rewrite expr-s. The gruntz algorithm needs to rewrite certain expressions in term of a new variable w. We cannot use subs, because it is just too smart for us. For example: > Omega=[exp(exp(_p - exp(-_p))/(1 - 1/_p)), exp(exp(_p))] > O2=[exp(-exp(_p) + exp(-exp(-_p))*exp(_p)/(1 - 1/_p))/_w, 1/_w] > e = exp(exp(_p - exp(-_p))/(1 - 1/_p)) - exp(exp(_p)) > e.subs(Omega[0],O2[0]).subs(Omega[1],O2[1]) -1/w + exp(exp(p)*exp(-exp(-p))/(1 - 1/p)) is really not what we want! So we do it the hard way and keep track of all the things we potentially want to substitute by dummy variables. Consider the expression exp(x - exp(-x)) + exp(x) + x. The mrv set is {exp(x), exp(-x), exp(x - exp(-x))}. We introduce corresponding dummy variables d1, d2, d3 and rewrite: d3 + d1 + x. This class first of all keeps track of the mapping expr->variable, i.e. will at this stage be a dictionary {exp(x): d1, exp(-x): d2, exp(x - exp(-x)): d3}. [It turns out to be more convenient this way round.] But sometimes expressions in the mrv set have other expressions from the mrv set as subexpressions, and we need to keep track of that as well. In this case, d3 is really exp(x - d2), so rewrites at this stage is {d3: exp(x-d2)}. The function rewrite uses all this information to correctly rewrite our expression in terms of w. In this case w can be choosen to be exp(-x), i.e. d2. The correct rewriting then is exp(-w)/w + 1/w + x. """ def __init__(self): self.rewrites = {} def __repr__(self): return super(SubsSet, self).__repr__() + ', ' + self.rewrites.__repr__() def __getitem__(self, key): if not key in self: self[key] = Dummy() return dict.__getitem__(self, key) def do_subs(self, e): for expr, var in self.iteritems(): e = e.subs(var, expr) return e def meets(self, s2): """ Tell whether or not self and s2 have non-empty intersection """ return set(self.keys()).intersection(s2.keys()) != set() def union(self, s2, exps=None): """ Compute the union of self and s2, adjusting exps """ res = self.copy() tr = {} for expr, var in s2.iteritems(): if expr in self: if exps: exps = exps.subs(var, res[expr]) tr[var] = res[expr] else: res[expr] = var for var, rewr in s2.rewrites.iteritems(): res.rewrites[var] = rewr.subs(tr) return res, exps def copy(self): r = SubsSet() r.rewrites = self.rewrites.copy() for expr, var in self.iteritems(): r[expr] = var return r @debug def mrv(e, x): """Returns a SubsSet of most rapidly varying (mrv) subexpressions of 'e', and e rewritten in terms of these""" e = powsimp(e, deep=True, combine='exp') assert isinstance(e, Basic) if not e.has(x): return SubsSet(), e elif e == x: s = SubsSet() return s, s[x] elif e.is_Mul or e.is_Add: i, d = e.as_independent(x) # throw away x-independent terms if d.func != e.func: s, expr = mrv(d, x) return s, e.func(i, expr) a, b = d.as_two_terms() s1, e1 = mrv(a, x) s2, e2 = mrv(b, x) return mrv_max1(s1, s2, e.func(i, e1, e2), x) elif e.is_Pow: b, e = e.as_base_exp() if e.has(x): return mrv(exp(e * log(b)), x) else: s, expr = mrv(b, x) return s, expr**e elif e.func is log: s, expr = mrv(e.args[0], x) return s, log(expr) elif e.func is exp: # We know from the theory of this algorithm that exp(log(...)) may always # be simplified here, and doing so is vital for termination. if e.args[0].func is log: return mrv(e.args[0].args[0], x) if limitinf(e.args[0], x).is_unbounded: s1 = SubsSet() e1 = s1[e] s2, e2 = mrv(e.args[0], x) su = s1.union(s2)[0] su.rewrites[e1] = exp(e2) return mrv_max3(s1, e1, s2, exp(e2), su, e1, x) else: s, expr = mrv(e.args[0], x) return s, exp(expr) elif e.is_Function: l = [mrv(a, x) for a in e.args] l2 = [s for (s, _) in l if s != SubsSet()] if len(l2) != 1: # e.g. something like BesselJ(x, x) raise NotImplementedError("MRV set computation for functions in" " several variables not implemented.") s, ss = l2[0], SubsSet() args = map(lambda x: ss.do_subs(x[1]), l) return s, e.func(*args) elif e.is_Derivative: raise NotImplementedError("MRV set computation for derviatives" " not implemented yet.") return mrv(e.args[0], x) raise NotImplementedError("Don't know how to calculate the mrv of '%s'" % e) def mrv_max3(f, expsf, g, expsg, union, expsboth, x): """Computes the maximum of two sets of expressions f and g, which are in the same comparability class, i.e. max() compares (two elements of) f and g and returns either (f, expsf) [if f is larger], (g, expsg) [if g is larger] or (union, expsboth) [if f, g are of the same class]. """ assert isinstance(f, SubsSet) assert isinstance(g, SubsSet) if f == SubsSet(): return g, expsg elif g == SubsSet(): return f, expsf elif f.meets(g): return union, expsboth c = compare(f.keys()[0], g.keys()[0], x) if c == ">": return f, expsf elif c == "<": return g, expsg else: assert c == "=" return union, expsboth def mrv_max1(f, g, exps, x): """Computes the maximum of two sets of expressions f and g, which are in the same comparability class, i.e. mrv_max1() compares (two elements of) f and g and returns the set, which is in the higher comparability class of the union of both, if they have the same order of variation. Also returns exps, with the appropriate substitutions made. """ u, b = f.union(g, exps) return mrv_max3(f, g.do_subs(exps), g, f.do_subs(exps), u, b, x) @debug @cacheit @timeit def sign(e, x): """Returns a sign of an expression e(x) for x->oo. e > 0 for x sufficiently large ... 1 e == 0 for x sufficiently large ... 0 e < 0 for x sufficiently large ... -1 The result of this function is currently undefined if e changes sign arbitarily often for arbitrarily large x (e.g. sin(x)). Note that this returns zero only if e is *constantly* zero for x sufficiently large. [If e is constant, of course, this is just the same thing as the sign of e.] """ from sympy import sign as _sign assert isinstance(e, Basic) if e.is_positive: return 1 elif e.is_negative: return -1 if e.is_Rational or e.is_Float: assert not e is S.NaN if e == 0: return 0 elif e.evalf() > 0: return 1 else: return -1 elif not e.has(x): return _sign(e) elif e == x: return 1 elif e.is_Mul: a, b = e.as_two_terms() sa = sign(a, x) if not sa: return 0 return sa * sign(b, x) elif e.func is exp: return 1 elif e.is_Pow: s = sign(e.base, x) if s == 1: return 1 if e.exp.is_Integer: return s**e.exp elif e.func is log: return sign(e.args[0] -1, x) # if all else fails, do it the hard way c0, e0 = mrv_leadterm(e, x) return sign(c0, x) @debug @timeit @cacheit def limitinf(e, x): """Limit e(x) for x-> oo""" #rewrite e in terms of tractable functions only e = e.rewrite('tractable', deep=True) if not e.has(x): return e #e is a constant if not x.is_positive: # We make sure that x.is_positive is True so we # get all the correct mathematical bechavior from the expression. # We need a fresh variable. p = Dummy('p', positive=True, bounded=True) e = e.subs(x, p) x = p c0, e0 = mrv_leadterm(e, x) sig = sign(e0, x) if sig == 1: return S.Zero # e0>0: lim f = 0 elif sig == -1: #e0<0: lim f = +-oo (the sign depends on the sign of c0) if c0.match(I*Wild("a", exclude=[I])): return c0*oo s = sign(c0, x) #the leading term shouldn't be 0: assert s != 0 return s*oo elif sig == 0: return limitinf(c0, x) #e0=0: lim f = lim c0 def moveup2(s, x): r = SubsSet() for expr, var in s.iteritems(): r[expr.subs(x, exp(x))] = var for var, expr in s.rewrites.iteritems(): r.rewrites[var] = s.rewrites[var].subs(x, exp(x)) return r def moveup(l, x): return [e.subs(x, exp(x)) for e in l] @debug @timeit def calculate_series(e, x, skip_abs=False, logx=None): """ Calculates at least one term of the series of "e" in "x". This is a place that fails most often, so it is in its own function. """ f = e for n in [1, 2, 4, 6, 8]: series = f.nseries(x, n=n, logx=logx) if not series.has(O): # The series expansion is locally exact. return series series = series.removeO() if series: if (not skip_abs) or series.has(x): break else: raise ValueError('(%s).series(%s, n=8) gave no terms.' % (f, x)) return series @debug @timeit @cacheit def mrv_leadterm(e, x): """Returns (c0, e0) for e.""" Omega = SubsSet() if not e.has(x): return (e, S.Zero) if Omega == SubsSet(): Omega, exps = mrv(e, x) if not Omega: # e really does not depend on x after simplification series = calculate_series(e, x) c0, e0 = series.leadterm(x) assert e0 == 0 return c0, e0 if x in Omega: #move the whole omega up (exponentiate each term): Omega_up = moveup2(Omega, x) e_up = moveup([e], x)[0] exps_up = moveup([exps], x)[0] # NOTE: there is no need to move this down! e = e_up Omega = Omega_up exps = exps_up # # The positive dummy, w, is used here so log(w*2) etc. will expand; # a unique dummy is needed in this algorithm # # For limits of complex functions, the algorithm would have to be # improved, or just find limits of Re and Im components separately. # w = Dummy("w", real=True, positive=True, bounded=True) f, logw = rewrite(exps, Omega, x, w) series = calculate_series(f, w, logx=logw) series = series.subs(log(w), logw) # this should not be necessary return series.leadterm(w) def build_expression_tree(Omega, rewrites): """ Helper function for rewrite. We need to sort Omega (mrv set) so that we replace an expression before we replace any expression in terms of which it has to be rewritten: e1 ---> e2 ---> e3 \ -> e4 Here we can do e1, e2, e3, e4 or e1, e2, e4, e3. To do this we assemble the nodes into a tree, and sort them by height. This function builds the tree, rewrites then sorts the nodes. """ class Node: def ht(self): return reduce(lambda x, y: x + y, map(lambda x: x.ht(), self.before), 1) nodes = {} for expr, v in Omega: n = Node() n.before = [] n.var = v n.expr = expr nodes[v] = n for _, v in Omega: if v in rewrites: n = nodes[v] r = rewrites[v] for _, v2 in Omega: if r.has(v2): n.before.append(nodes[v2]) return nodes @debug @timeit def rewrite(e, Omega, x, wsym): """e(x) ... the function Omega ... the mrv set wsym ... the symbol which is going to be used for w Returns the rewritten e in terms of w and log(w). See test_rewrite1() for examples and correct results. """ assert isinstance(Omega, SubsSet) assert len(Omega) != 0 #all items in Omega must be exponentials for t in Omega.keys(): assert t.func is exp rewrites = Omega.rewrites Omega = Omega.items() nodes = build_expression_tree(Omega, rewrites) Omega.sort(key=lambda x: nodes[x[1]].ht(), reverse=True) g, _ = Omega[-1] #g is going to be the "w" - the simplest one in the mrv set sig = sign(g.args[0], x) if sig == 1: wsym = 1/wsym #if g goes to oo, substitute 1/w elif sig != -1: raise NotImplementedError('Result depends on the sign of %s' % sig) #O2 is a list, which results by rewriting each item in Omega using "w" O2 = [] for f, var in Omega: c = limitinf(f.args[0]/g.args[0], x) arg = f.args[0] if var in rewrites: assert rewrites[var].func is exp arg = rewrites[var].args[0] O2.append((var, exp((arg - c*g.args[0]).expand())*wsym**c)) #Remember that Omega contains subexpressions of "e". So now we find #them in "e" and substitute them for our rewriting, stored in O2 # the following powsimp is necessary to automatically combine exponentials, # so that the .subs() below succeeds: # TODO this should not be necessary f = powsimp(e, deep=True, combine='exp') for a, b in O2: f = f.subs(a, b) for _, var in Omega: assert not f.has(var) #finally compute the logarithm of w (logw). logw = g.args[0] if sig == 1: logw = -logw #log(w)->log(1/w)=-log(w) return f, logw def gruntz(e, z, z0, dir="+"): """ Compute the limit of e(z) at the point z0 using the Gruntz algorithm. z0 can be any expression, including oo and -oo. For dir="+" (default) it calculates the limit from the right (z->z0+) and for dir="-" the limit from the left (z->z0-). For infinite z0 (oo or -oo), the dir argument doesn't matter. This algorithm is fully described in the module docstring in the gruntz.py file. It relies heavily on the series expansion. Most frequently, gruntz() is only used if the faster limit() function (which uses heuristics) fails. """ if not isinstance(z, Symbol): raise NotImplementedError("Second argument must be a Symbol") #convert all limits to the limit z->oo; sign of z is handled in limitinf r = None if z0 == oo: r = limitinf(e, z) elif z0 == -oo: r = limitinf(e.subs(z, -z), z) else: if dir == "-": e0 = e.subs(z, z0 - 1/z) elif dir == "+": e0 = e.subs(z, z0 + 1/z) else: raise NotImplementedError("dir must be '+' or '-'") r = limitinf(e0, z) # This is a bit of a heuristic for nice results... we always rewrite # tractable functions in terms of familiar intractable ones. # It might be nicer to rewrite the exactly to what they were initially, # but that would take some work to implement. return r.rewrite('intractable', deep=True) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/order.py0000644000175000017500000001626712014170666024072 0ustar georgeskgeorgeskfrom sympy.core import Basic, S, C, sympify, Expr, oo, Rational, Symbol, Dummy from sympy.core import Add, Mul from sympy.core.cache import cacheit from sympy.core.compatibility import cmp_to_key class Order(Expr): """ Represents O(f(x)) at the point x = 0. Definition ========== g(x) = O(f(x)) as x->0 if and only if |g(x)|<=M|f(x)| near x=0 (1) for some positive but finite M. An equivalent way of saying (1) is: lim_{x->0} |g(x)/f(x)| < oo Let's illustrate it on the following example: sin x = x - x**3/3! + O(x**5) where in this case O(x**5) = x**5/5! - x**7/7! + .... and the definition of O means: |x**5/5! - x**7/7! + ....| <= M|x**5| near x=0 or equivalently: lim_{x->0} | (x**5/5! - x**7/7! + ....) / x**5| < oo which surely is true, because lim_{x->0} | (x**5/5! - x**7/7! + ....) / x**5| = 1/5! So intuitively O(x**3) means: all terms x**3, x**4 and higher. But not x**2, x or 1. Examples: ========= >>> from sympy import O >>> from sympy.abc import x >>> O(x) O(x) >>> O(x)*x O(x**2) >>> O(x)-O(x) O(x) External links -------------- U{Big O notation} Properties: =========== g(x) = O(f(x)) as x->0 <-> |g(x)| <= M|f(x)| near x=0 <-> lim_{x->0} |g(x)/f(x)| < oo g(x,y) = O(f(x,y)) <-> lim_{x,y->0} |g(x,y)/f(x,y)| < oo; it is assumed that limits commute. Notes: ====== In O(f(x), x) the expression f(x) is assumed to have a leading term. O(f(x), x) is automatically transformed to O(f(x).as_leading_term(x),x). O(expr*f(x), x) is O(f(x), x) O(expr, x) is O(1) O(0, x) is 0. Multivariate O is also supported: O(f(x, y), x, y) is transformed to O(f(x, y).as_leading_term(x,y).as_leading_term(y), x, y) If no symbols are passed then all symbols in the expression are used: """ is_Order = True __slots__ = [] @cacheit def __new__(cls, expr, *symbols, **assumptions): expr = sympify(expr).expand() if expr is S.NaN: return S.NaN if symbols: symbols = map(sympify, symbols) if not all(isinstance(s, Symbol) for s in symbols): raise NotImplementedError('Order at points other than 0 not supported.') else: symbols = list(expr.free_symbols) if expr.is_Order: new_symbols = list(expr.variables) for s in symbols: if s not in new_symbols: new_symbols.append(s) if len(new_symbols) == len(expr.variables): return expr symbols = new_symbols elif symbols: if expr.is_Add: lst = expr.extract_leading_order(*symbols) expr = Add(*[f.expr for (e,f) in lst]) elif expr: if len(symbols) > 1: # TODO # We cannot use compute_leading_term because that only # works in one symbol. expr = expr.as_leading_term(*symbols) else: expr = expr.compute_leading_term(symbols[0]) coeff, terms = expr.as_coeff_mul() expr = Mul(*[t for t in terms if t.has(*symbols)]) if expr is S.Zero: return expr elif not expr.has(*symbols): expr = S.One # create Order instance: symbols.sort(key=cmp_to_key(Basic.compare)) obj = Expr.__new__(cls, expr, *symbols, **assumptions) return obj def _hashable_content(self): return self.args def oseries(self, order): return self def _eval_nseries(self, x, n, logx): return self @property def expr(self): return self._args[0] @property def variables(self): return self._args[1:] @property def free_symbols(self): return self.expr.free_symbols def _eval_power(b, e): if e.is_Number: return Order(b.expr ** e, *b.variables) return def as_expr_variables(self, order_symbols): if order_symbols is None: order_symbols = self.variables else: for s in self.variables: if s not in order_symbols: order_symbols = order_symbols + (s,) return self.expr, order_symbols def removeO(self): return S.Zero def getO(self): return self @cacheit def contains(self, expr): """ Return True if expr belongs to Order(self.expr, *self.variables). Return False if self belongs to expr. Return None if the inclusion relation cannot be determined (e.g. when self and expr have different symbols). """ # NOTE: when multiplying out series a lot of queries like # O(...).contains(a*x**b) with many a and few b are made. # Separating out the independent part allows for better caching. c, m = expr.as_coeff_mul(*self.variables) if m != (): return self._contains(Mul(*m)) else: # Mul(*m) == 1, and O(1) treatment is somewhat peculiar ... # some day this else should not be necessary return self._contains(expr) @cacheit def _contains(self, expr): from sympy import powsimp, limit if expr is S.Zero: return True if expr is S.NaN: return False if expr.is_Order: if self.variables and expr.variables: common_symbols = tuple([s for s in self.variables if s in expr.variables]) elif self.variables: common_symbols = self.variables else: common_symbols = expr.variables if not common_symbols: if not (self.variables or expr.variables): # O(1),O(1) return True return None r = None for s in common_symbols: l = limit(powsimp(self.expr/expr.expr, deep=True,\ combine='exp'), s, 0) != 0 if r is None: r = l else: if r != l: return return r obj = Order(expr, *self.variables) return self.contains(obj) def _eval_subs(self, old, new): if self == old: return new if isinstance(old, Symbol) and old in self.variables: i = list(self.variables).index(old) if isinstance(new, Symbol): return Order(self.expr._eval_subs(old, new), *(self.variables[:i]+(new,)+self.variables[i+1:])) return Order(self.expr._eval_subs(old, new), *(self.variables[:i]+self.variables[i+1:])) return Order(self.expr._eval_subs(old, new), *self.variables) def _eval_derivative(self, x): return self.func(self.expr.diff(x), *self.variables) or self def _sage_(self): #XXX: SAGE doesn't have Order yet. Let's return 0 instead. return Rational(0)._sage_() O = Order wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/series.py0000644000175000017500000000044012014170666024233 0ustar georgeskgeorgeskfrom sympy.core.sympify import sympify def series(expr, x=None, x0=0, n=6, dir="+"): """Series expansion of expr around point `x = x0`. See the doctring of Expr.series() for complete details of this wrapper. """ expr = sympify(expr) return expr.series(x, x0, n, dir) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/0000755000175000017500000000000012014170666023533 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_limits.py0000644000175000017500000002361412014170666026453 0ustar georgeskgeorgeskfrom sympy import (limit, exp, oo, log, sqrt, Limit, sin, floor, cos, ceiling, atan, gamma, Symbol, S, pi, Integral, cot, Rational, I, zoo, tan, cot, integrate, Sum, sign) from sympy.abc import x, y, z from sympy.utilities.pytest import XFAIL, raises from sympy.utilities.iterables import cartes def test_basic1(): assert limit(x, x, oo) == oo assert limit(x, x, -oo) == -oo assert limit(-x, x, oo) == -oo assert limit(x**2, x, -oo) == oo assert limit(-x**2, x, oo) == -oo assert limit(x*log(x), x, 0, dir="+") == 0 assert limit(1/x, x, oo) == 0 assert limit(exp(x), x, oo) == oo assert limit(-exp(x), x, oo) == -oo assert limit(exp(x)/x, x, oo) == oo assert limit(1/x - exp(-x), x, oo) == 0 assert limit(x + 1/x, x, oo) == oo assert limit(x - x**2, x, oo) == -oo assert limit((1 + x)**(1 + sqrt(2)),x,0) == 1 assert limit((1 + x)**oo, x, 0) == oo assert limit((1 + x)**oo, x, 0, dir='-') == 0 assert limit((1 + x + y)**oo, x, 0, dir='-') == (1 + y)**(oo) assert limit(y/x/log(x), x, 0) == -y*oo assert limit(cos(x + y)/x, x, 0) == sign(cos(y))*oo raises(NotImplementedError, 'limit(Sum(1/x, (x, 1, y)) - log(y), y, oo)') assert limit(Sum(1/x, (x, 1, y)) - 1/y, y, oo) == Sum(1/x, (x, 1, oo)) assert limit(gamma(1/x + 3), x, oo) == 2 # approaching 0 # from dir="+" assert limit(1 + 1/x, x, 0) == oo # from dir='-' # Add assert limit(1 + 1/x, x, 0, dir='-') == -oo # Pow assert limit(x**(-2), x, 0, dir='-') == oo assert limit(x**(-3), x, 0, dir='-') == -oo assert limit(x**(-Rational(1, 2)), x, 0, dir='-') == (-oo)*I assert limit(x**2, x, 0, dir='-') == 0 assert limit(x**(Rational(1, 2)), x, 0, dir='-') == 0 assert limit(x**-pi, x, 0, dir='-') == zoo assert limit((1 + cos(x))**oo, x, 0) == oo def test_basic2(): assert limit(x**x, x, 0, dir="+") == 1 assert limit((exp(x)-1)/x, x, 0) == 1 assert limit(1 + 1/x, x, oo) == 1 assert limit(-exp(1/x), x, oo) == -1 assert limit(x + exp(-x), x, oo) == oo assert limit(x + exp(-x**2), x, oo) == oo assert limit(x + exp(-exp(x)), x, oo) == oo assert limit(13 + 1/x - exp(-x), x, oo) == 13 def test_basic3(): assert limit(1/x, x, 0, dir="+") == oo assert limit(1/x, x, 0, dir="-") == -oo def test_basic4(): assert limit(2*x + y*x, x, 0) == 0 assert limit(2*x + y*x, x, 1) == 2+y assert limit(2*x**8 + y*x**(-3), x, -2) == 512 - y/8 assert limit(sqrt(x + 1) - sqrt(x), x, oo)==0 assert integrate(1/(x**3+1),(x,0,oo)) == 2*pi*sqrt(3)/9 def test_issue786(): assert limit(x*y + x*z, z, 2) == x*y+2*x def test_Limit(): assert Limit(sin(x)/x, x, 0) != 1 assert Limit(sin(x)/x, x, 0).doit() == 1 def test_floor(): assert limit(floor(x), x, -2, "+") == -2 assert limit(floor(x), x, -2, "-") == -3 assert limit(floor(x), x, -1, "+") == -1 assert limit(floor(x), x, -1, "-") == -2 assert limit(floor(x), x, 0, "+") == 0 assert limit(floor(x), x, 0, "-") == -1 assert limit(floor(x), x, 1, "+") == 1 assert limit(floor(x), x, 1, "-") == 0 assert limit(floor(x), x, 2, "+") == 2 assert limit(floor(x), x, 2, "-") == 1 assert limit(floor(x), x, 248, "+") == 248 assert limit(floor(x), x, 248, "-") == 247 # note: if any of the tests below fails, just comment it out. General fix # needs better assumptions handling. # this doesn't work, it requires robust assumptions: assert limit(floor(sin(x)), x, 0, "+") == 0 assert limit(floor(sin(x)), x, 0, "-") == -1 assert limit(floor(cos(x)), x, 0, "+") == 0 assert limit(floor(cos(x)), x, 0, "-") == 0 # this doesn't work, it requires robust assumptions: assert limit(floor(5+sin(x)), x, 0, "+") == 5 #assert limit(floor(5+sin(x)), x, 0, "-") == 4 #assert limit(floor(5+cos(x)), x, 0, "+") == 5 #assert limit(floor(5+cos(x)), x, 0, "-") == 5 def test_ceiling(): assert limit(ceiling(x), x, -2, "+") == -1 assert limit(ceiling(x), x, -2, "-") == -2 assert limit(ceiling(x), x, -1, "+") == 0 assert limit(ceiling(x), x, -1, "-") == -1 assert limit(ceiling(x), x, 0, "+") == 1 assert limit(ceiling(x), x, 0, "-") == 0 assert limit(ceiling(x), x, 1, "+") == 2 assert limit(ceiling(x), x, 1, "-") == 1 assert limit(ceiling(x), x, 2, "+") == 3 assert limit(ceiling(x), x, 2, "-") == 2 assert limit(ceiling(x), x, 248, "+") == 249 assert limit(ceiling(x), x, 248, "-") == 248 # note: if any of the tests below fails, just comment it out. General fix # needs better assumptions handling. # this doesn't work, it requires robust assumptions: #assert limit(ceiling(sin(x)), x, 0, "+") == 1 assert limit(ceiling(sin(x)), x, 0, "-") == 0 assert limit(ceiling(cos(x)), x, 0, "+") == 1 assert limit(ceiling(cos(x)), x, 0, "-") == 1 # this doesn't work, it requires robust assumptions: #assert limit(ceiling(5+sin(x)), x, 0, "+") == 6 assert limit(ceiling(5+sin(x)), x, 0, "-") == 5 assert limit(ceiling(5+cos(x)), x, 0, "+") == 6 assert limit(ceiling(5+cos(x)), x, 0, "-") == 6 def test_atan(): x = Symbol("x", real=True) assert limit(atan(x)*sin(1/x), x, 0) == 0 assert limit(atan(x) + sqrt(x+1) - sqrt(x), x, oo) == pi/2 def test_abs(): assert limit(abs(x), x, 0) == 0 assert limit(abs(sin(x)), x, 0) == 0 assert limit(abs(cos(x)), x, 0) == 1 assert limit(abs(sin(x+1)), x, 0) == sin(1) def test_heuristic(): x = Symbol("x", real=True) assert limit(log(2+sqrt(atan(x))*sqrt(sin(1/x))), x, 0) == log(2) def test_issue772(): z = Symbol("z", positive=True) f = -1/z*exp(-z*x) assert limit(f, x, oo) == 0 assert f.limit(x, oo) == 0 def test_exponential(): n = Symbol('n') assert limit((1+x/n)**n,n,oo) == exp(x) assert limit((1+x/(2*n))**n,n,oo) == exp(x/2) assert limit((1+x/(2*n+1))**n,n,oo) == exp(x/2) assert limit(((x-1)/(x+1))**x,x,oo) == exp(-2) @XFAIL def test_exponential2(): n = Symbol('n') assert limit((1+x/(n+sin(n)))**n,n,oo) == exp(x) def test_doit(): f = Integral(2 * x, x) l = Limit(f, x, oo) assert l.doit() == oo @XFAIL def test_doit2(): f = Integral(2 * x, x) l = Limit(f, x, oo) # limit() breaks on the contained Integral. assert l.doit(deep = False) == l def test_bug693a(): assert sin(sin(x+1)+1).limit(x,0) == sin(sin(1)+1) def test_issue693(): assert limit( (1-cos(x))/x**2, x, S(1)/2) == 4 - 4*cos(S(1)/2) assert limit(sin(sin(x+1)+1), x, 0) == sin(1 + sin(1)) assert limit(abs(sin(x+1)+1), x, 0) == 1 + sin(1) def test_issue991(): assert limit(1/(x+3), x, 2) == S(1)/5 assert limit(1/(x+pi), x, 2) == S(1)/(2+pi) assert limit(log(x)/(x**2+3), x, 2) == log(2)/7 assert limit(log(x)/(x**2+pi), x, 2) == log(2)/(4+pi) def test_issue1448(): assert limit(cot(x),x,0,dir='+') == oo assert limit(cot(x),x,pi/2,dir='+') == 0 def test_issue2065(): assert limit(x**0.5, x, oo) == oo**0.5 == oo assert limit(x**0.5, x, 16) == S(16)**0.5 assert limit(x**0.5, x, 0) == 0 assert limit(x**(-0.5), x, oo) == 0 assert limit(x**(-0.5), x, 4) == S(4)**(-0.5) def test_issue2084(): # using list(...) so py.test can recalculate values tests = list(cartes([x, -x], [-1, 1], [2, 3, Rational(1, 2), Rational(2, 3)], ['-', '+'])) results = (oo, oo, -oo, oo, -oo*I, oo, -oo*(-1)**Rational(1, 3), oo, 0, 0, 0, 0, 0, 0, 0, 0, oo, oo, oo, -oo, oo, -oo*I, oo, -oo*(-1)**Rational(1, 3), 0, 0, 0, 0, 0, 0, 0, 0) assert len(tests) == len(results) for i, (args, res) in enumerate(zip(tests, results)): y, s, e, d = args eq=y**(s*e) try: assert limit(eq, x, 0, dir=d) == res except AssertionError: if 0: # change to 1 if you want to see the failing tests print print i, res, eq, d, limit(eq, x, 0, dir=d) else: assert None def test_issue2085(): assert limit(sin(x)/x, x, oo) == 0 assert limit(atan(x), x, oo) == pi/2 assert limit(gamma(x), x, oo) == oo assert limit(cos(x)/x, x, oo) == 0 assert limit(gamma(x), x, Rational(1, 2)) == sqrt(pi) @XFAIL def test_issue2130(): assert limit((1+y)**(1/y) - S.Exp1, y, 0) == 0 def test_issue1447(): # using list(...) so py.test can recalculate values from sympy import sign tests = list(cartes([cot, tan], [-pi/2, 0, pi/2, pi, 3*pi/2], ['-', '+'])) results = (0, 0, -oo, oo, 0, 0, -oo, oo, 0, 0, oo, -oo, 0, 0, oo, -oo, 0, 0, oo, -oo) assert len(tests) == len(results) for i, (args, res) in enumerate(zip(tests, results)): f, l, d= args eq=f(x) try: assert limit(eq, x, l, dir=d) == res except AssertionError: if 0: # change to 1 if you want to see the failing tests print print i, res, eq, l, d, limit(eq, x, l, dir=d) else: assert None def test_issue835(): assert limit((1 + x**log(3))**(1/x), x, 0) == 1 assert limit((5**(1/x) + 3**(1/x))**x, x, 0) == 5 def test_newissue(): assert limit(exp(1/sin(x))/exp(cot(x)), x, 0) == 1 def test_extended_real_line(): assert limit(x - oo, x, oo) == -oo assert limit(oo - x, x, -oo) == oo assert limit(x**2/(x-5) - oo, x, oo) == -oo assert limit(1/(x+sin(x)) - oo, x, 0) == -oo assert limit(x - oo + 1/x, x, oo) == -oo assert limit(x - oo + 1/x, x, 0) == -oo assert limit(oo/x, x, oo) == oo @XFAIL def test_order_oo(): from sympy import C x = Symbol('x', positive=True, bounded=True) assert C.Order(x)*oo != C.Order(1, x) assert limit(oo/(x**2 - 4), x, oo) == oo def test_issue2337(): raises(NotImplementedError, 'limit(exp(x*y), x, oo)') raises(NotImplementedError, 'limit(exp(-x*y), x, oo)') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_residues.py0000644000175000017500000000301112014170666026762 0ustar georgeskgeorgeskfrom sympy import residue, Symbol, Function, sin, S, I, pi, exp def test_basic1(): x = Symbol("x") assert residue(1/x, x, 0) == 1 assert residue(-2/x, x, 0) == -2 assert residue(81/x, x, 0) == 81 assert residue(1/x**2, x, 0) == 0 assert residue(0, x, 0) == 0 assert residue(5, x, 0) == 0 assert residue(x, x, 0) == 0 assert residue(x**2, x, 0) == 0 def test_basic2(): x = Symbol("x") assert residue(1/x, x, 1) == 0 assert residue(-2/x, x, 1) == 0 assert residue(81/x, x, -1) == 0 assert residue(1/x**2, x, 1) == 0 assert residue(0, x, 1) == 0 assert residue(5, x, 1) == 0 assert residue(x, x, 1) == 0 assert residue(x**2, x, 5) == 0 def _test_f(): # FIXME: we get infinite recursion here: x = Symbol("x") f = Function("f") assert residue(f(x)/x**5, x, 0) == f.diff(x, 4)/24 def test_functions(): x = Symbol("x") assert residue(1/sin(x), x, 0) == 1 assert residue(2/sin(x), x, 0) == 2 assert residue(1/sin(x)**2, x, 0) == 0 # FIXME: the series expansion fails to return the right answer: #assert residue(1/sin(x)**5, x, 0) == S(3)/8 def test_expressions(): x = Symbol("x") assert residue(1/(x+1), x, 0) == 0 assert residue(1/(x+1), x, -1) == 1 assert residue(1/(x**2+1), x, -1) == 0 assert residue(1/(x**2+1), x, I) == -I/2 assert residue(1/(x**2+1), x, -I) == I/2 assert residue(1/(x**4+1), x, 0) == 0 # FIXME: this fails: #assert residue(1/(x**4+1), x, exp(I*pi/4)) == -(S(1)/4+I/4)/sqrt(2) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_gruntz.py0000644000175000017500000003134512014170666026503 0ustar georgeskgeorgeskfrom sympy import Symbol, exp, log, oo, Rational, I, sin, gamma, loggamma, S, \ atan, acot, pi, cancel, E, erf, sqrt, zeta, cos, digamma from sympy.series.gruntz import compare, mrv, rewrite, mrv_leadterm, gruntz, \ sign from sympy.utilities.pytest import XFAIL, skip """ This test suite is testing the limit algorithm using the bottom up approach. See the documentation in limits2.py. The algorithm itself is highly recursive by nature, so "compare" is logically the lowest part of the algorithm, yet in some sense it's the most complex part, because it needs to calculate a limit to return the result. Nevertheless the rest of the algorithm depends on compare that it works correctly. """ x = Symbol('x', real=True) m = Symbol('m', real=True) runslow = False def sskip(): if not runslow: skip("slow") def test_gruntz_evaluation(): # Gruntz' thesis pp. 122 to 123 assert gruntz(exp(x)*(exp(1/x-exp(-x))-exp(1/x)), x, oo) == -1 assert gruntz((x*log(x)*(log(x*exp(x)-x**2))**2) / (log(log(x**2+2*exp(exp(3*x**3*log(x)))))), x, oo) == S(1)/3 assert gruntz((3**x + 5**x)**(1/x), x, oo) == 5 assert gruntz(exp(exp(S(5)/2*x**(-S(5)/7)+ S(21)/8*x**(S(6)/11) +2*x**(-8)+S(54)/17*x**(S(49)/45) ))**8 / log(log(-log(S(4)/3*x**(-S(5)/14))))**(S(7)/6), x, oo) == oo assert gruntz(exp(x*exp(-x)/(exp(-x)+exp(-2*x**2/(x+1))))/exp(x), x, oo) \ == 1 assert gruntz(log(x)*(log(log(x)+log(log(x))) - log(log(x))) / (log(log(x)+log(log(log(x))))), x, oo) == 1 assert gruntz(x/log(x**(log(x**(log(2)/log(x))))), x, oo) == oo assert gruntz(x/log(x**(log(x**(log(2)/log(x))))), x, oo) == oo assert gruntz(log(x)**2 * exp(sqrt(log(x))*(log(log(x)))**2 * exp(sqrt(log(log(x))) * (log(log(log(x))))**3)) / sqrt(x), x, oo) == 0 assert gruntz(exp((log(log(x+exp(log(x)*log(log(x)))))) / (log(log(log(exp(x)+x+log(x)))))), x, oo) == E assert gruntz(exp(x)*(exp(1/x+exp(-x)+exp(-x**2)) \ - exp(1/x-exp(-exp(x)))), x, oo) == 1 assert gruntz((exp(4*x*exp(-x)/(1/exp(x)+1/exp(2*x**2/(x+1)))) - exp(x)) / exp(x)**4, x, oo) == 1 assert gruntz(exp(exp(x-exp(-x))/(1-1/x)) - exp(exp(x)), x, oo) == oo assert gruntz(exp(exp(2*log(x**5+x)*log(log(x)))) / exp(exp(10*log(x)*log(log(x)))), x, oo) == oo assert gruntz((exp(x*exp(-x)/(exp(-x)+exp(-2*x**2/(x+1)))) - exp(x))/x, x, oo) == -exp(2) assert gruntz(exp(exp(x)) / exp(exp(x-exp(-exp(exp(x))))), x, oo) == 1 assert gruntz(exp(exp(exp(x+exp(-x)))) / exp(exp(exp(x))), x, oo) == oo assert gruntz(exp(exp(exp(x+exp(-x)))) / exp(exp(exp(x))), x, oo) == oo assert gruntz(exp(exp(exp(x))) / exp(exp(exp(x-exp(-exp(exp(x)))))), x, oo) == 1 def test_gruntz_evaluation_slow(): sskip() assert gruntz((exp(exp(-x/(1+exp(-x))))*exp(-x/(1+exp(-x/(1+exp(-x))))) *exp(exp(-x+exp(-x/(1+exp(-x)))))) / (exp(-x/(1+exp(-x))))**2 - exp(x) + x, x, oo) == 2 assert gruntz(exp(exp(exp(x)/(1-1/x))) - exp(exp(exp(x)/(1-1/x-log(x)**(-log(x))))), x, oo) == -oo def test_gruntz_eval_special(): # Gruntz, p. 126 assert gruntz(exp(x)*(sin(1/x+exp(-x))-sin(1/x+exp(-x**2))), x, oo) == 1 assert gruntz((erf(x-exp(-exp(x))) - erf(x)) * exp(exp(x)) * exp(x**2), x, oo) == -2/sqrt(pi) assert gruntz(exp(exp(x)) * (exp(sin(1/x+exp(-exp(x)))) - exp(sin(1/x))), x, oo) == 1 assert gruntz(exp(x)*(gamma(x+exp(-x)) - gamma(x)), x, oo) == oo assert gruntz(exp(exp(digamma(digamma(x))))/x,x,oo) == exp(-S(1)/2) assert gruntz(exp(exp(digamma(log(x))))/x,x,oo) == exp(-S(1)/2) assert gruntz(digamma(digamma(digamma(x))),x,oo) == oo assert gruntz(loggamma(loggamma(x)), x, oo) == oo assert gruntz(((gamma(x+1/gamma(x)) - gamma(x))/log(x) - cos(1/x)) * x*log(x), x, oo) == -S(1)/2 assert gruntz(x * (gamma(x-1/gamma(x)) - gamma(x) + log(x)), x, oo) \ == S(1)/2 assert gruntz((gamma(x+1/gamma(x)) - gamma(x)) / log(x), x, oo) == 1 def test_gruntz_eval_special_slow(): sskip() assert gruntz(gamma(x+1)/sqrt(2*pi) - exp(-x)*(x**(x+S(1)/2) + x**(x-S(1)/2)/12), x, oo) == oo assert gruntz(exp(exp(exp(digamma(digamma(digamma(x))))))/x, x, oo) == 0 # XXX This sometimes fails!!! assert gruntz(exp(gamma(x-exp(-x))*exp(1/x)) - exp(gamma(x)), x, oo) == oo @XFAIL def test_gruntz_eval_special_fail(): # TODO exponential integral Ei # assert gruntz((Ei(x-exp(-exp(x))) - Ei(x)) *exp(-x)*exp(exp(x))*x, # x, oo) == -1 # TODO zeta function series assert gruntz(exp((log(2)+1)*x) * (zeta(x+exp(-x)) - zeta(x)), x, oo) \ == -log(2) # TODO 8.35 - 8.37 (bessel, max-min) def test_compare1(): assert compare(2, x, x) == "<" assert compare(x, exp(x), x) == "<" assert compare(exp(x), exp(x**2), x) == "<" assert compare(exp(x**2),exp(exp(x)), x) == "<" assert compare(1,exp(exp(x)), x) == "<" assert compare(x, 2, x) == ">" assert compare(exp(x), x, x) == ">" assert compare(exp(x**2), exp(x), x) == ">" assert compare(exp(exp(x)), exp(x**2), x) == ">" assert compare(exp(exp(x)), 1, x) == ">" assert compare(2, 3, x) == "=" assert compare(3, -5, x) == "=" assert compare(2, -5, x) == "=" assert compare(x, x**2, x) == "=" assert compare(x**2, x**3, x) == "=" assert compare(x**3, 1/x, x) == "=" assert compare(1/x, x**m, x) == "=" assert compare(x**m, -x, x) == "=" assert compare(exp(x), exp(-x), x) == "=" assert compare(exp(-x), exp(2*x), x) == "=" assert compare(exp(2*x), exp(x)**2, x) == "=" assert compare(exp(x)**2, exp(x+exp(-x)), x) == "=" assert compare(exp(x), exp(x+exp(-x)), x) == "=" assert compare(exp(x**2), 1/exp(x**2), x) == "=" def test_compare2(): assert compare(exp(x),x**5,x) == ">" assert compare(exp(x**2),exp(x)**2,x) == ">" assert compare(exp(x),exp(x+exp(-x)),x) == "=" assert compare(exp(x+exp(-x)),exp(x),x) == "=" assert compare(exp(x+exp(-x)),exp(-x),x) == "=" assert compare(exp(-x),x,x) == ">" assert compare(x,exp(-x),x) == "<" assert compare(exp(x+1/x),x,x) == ">" assert compare(exp(-exp(x)),exp(x),x) == ">" assert compare(exp(exp(-exp(x))+x),exp(-exp(x)),x) == "<" def test_compare3(): assert compare(exp(exp(x)),exp(x+exp(-exp(x))),x) == ">" def test_sign1(): assert sign(Rational(0), x) == 0 assert sign(Rational(3), x) == 1 assert sign(Rational(-5), x) == -1 assert sign(log(x), x) == 1 assert sign(exp(-x), x) == 1 assert sign(exp(x), x) == 1 assert sign(-exp(x), x) == -1 assert sign(3-1/x, x) == 1 assert sign(-3-1/x, x) == -1 assert sign(sin(1/x), x) == 1 def test_sign2(): assert sign(x, x) == 1 assert sign(-x, x) == -1 y = Symbol("y", positive=True) assert sign(y, x) == 1 assert sign(-y, x) == -1 assert sign(y*x, x) == 1 assert sign(-y*x, x) == -1 def mmrv(a, b): return set(mrv(a, b)[0].keys()) def test_mrv1(): assert mmrv(x, x) == set([x]) assert mmrv(x+1/x, x) == set([x]) assert mmrv(x**2, x) == set([x]) assert mmrv(log(x), x) == set([x]) assert mmrv(exp(x), x) == set([exp(x)]) assert mmrv(exp(-x), x) == set([exp(-x)]) assert mmrv(exp(x**2), x) == set([exp(x**2)]) assert mmrv(-exp(1/x), x) == set([x]) assert mmrv(exp(x+1/x), x) == set([exp(x+1/x)]) def test_mrv2a(): assert mmrv(exp(x+exp(-exp(x))), x) == set([exp(-exp(x))]) assert mmrv(exp(x+exp(-x)), x) == set([exp(x+exp(-x)), exp(-x)]) assert mmrv(exp(1/x+exp(-x)), x) == set([exp(-x)]) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_mrv2b(): assert mmrv(exp(x+exp(-x**2)), x) == set([exp(-x**2)]) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_mrv2c(): assert mmrv(exp(-x+1/x**2)-exp(x+1/x), x) == set([exp(x+1/x), exp(1/x**2-x)]) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_mrv3(): assert mmrv(exp(x**2)+x*exp(x)+log(x)**x/x, x) == set([exp(x**2)]) assert mmrv(exp(x)*(exp(1/x+exp(-x))-exp(1/x)), x) == set([exp(x), exp(-x)]) assert mmrv(log(x**2+2*exp(exp(3*x**3*log(x)))), x) == set([exp(exp(3*x**3*log(x)))]) assert mmrv(log(x-log(x))/log(x), x) == set([x]) assert mmrv((exp(1/x-exp(-x))-exp(1/x))*exp(x), x) == set([exp(x), exp(-x)]) assert mmrv(1/exp(-x+exp(-x))-exp(x), x) == set([exp(x), exp(-x), exp(x-exp(-x))]) assert mmrv(log(log(x*exp(x*exp(x))+1)), x) == set([exp(x*exp(x))]) assert mmrv(exp(exp(log(log(x)+1/x))), x) == set([x]) def test_mrv4(): ln = log assert mmrv((ln(ln(x)+ln(ln(x)))-ln(ln(x)))/ln(ln(x)+ln(ln(ln(x))))*ln(x), x) == set([x]) assert mmrv(log(log(x*exp(x*exp(x))+1)) - exp(exp(log(log(x)+1/x))), x) == \ set([exp(x*exp(x))]) def mrewrite(a, b, c): return rewrite(a[1], a[0], b, c) def test_rewrite1(): e = exp(x) assert mrewrite(mrv(e, x), x, m) == (1/m, -x) e = exp(x**2) assert mrewrite(mrv(e, x), x, m) == (1/m, -x**2) e = exp(x+1/x) assert mrewrite(mrv(e, x), x, m) == (1/m, -x-1/x) e = 1/exp(-x+exp(-x))-exp(x) assert mrewrite(mrv(e, x), x, m) == (1/(m*exp(m))-1/m, -x) def test_rewrite2(): e = exp(x)*log(log(exp(x))) assert mmrv(e, x) == set([exp(x)]) assert mrewrite(mrv(e, x), x, m) == (1/m*log(x), -x) #sometimes infinite recursion due to log(exp(x**2)) not simplifying def test_rewrite3(): e = exp(-x+1/x**2)-exp(x+1/x) #both of these are correct and should be equivalent: assert mrewrite(mrv(e, x), x, m) in [(-1/m + m*exp(1/x+1/x**2), -x-1/x), (m - 1/m*exp(1/x + x**(-2)), x**(-2) - x)] def test_mrv_leadterm1(): assert mrv_leadterm(-exp(1/x), x) == (-1, 0) assert mrv_leadterm(1/exp(-x+exp(-x))-exp(x), x) == (-1, 0) assert mrv_leadterm((exp(1/x-exp(-x))-exp(1/x))*exp(x), x) == (-exp(1/x), 0) def test_mrv_leadterm2(): #Gruntz: p51, 3.25 assert mrv_leadterm((log(exp(x)+x)-x)/log(exp(x)+log(x))*exp(x), x) == \ (1, 0) def test_mrv_leadterm3(): #Gruntz: p56, 3.27 assert mmrv(exp(-x+exp(-x)*exp(-x*log(x))), x) == set([exp(-x-x*log(x))]) assert mrv_leadterm(exp(-x+exp(-x)*exp(-x*log(x))), x) == (exp(-x), 0) def test_limit1(): assert gruntz(x, x, oo) == oo assert gruntz(x, x, -oo) == -oo assert gruntz(-x, x, oo) == -oo assert gruntz(x**2, x, -oo) == oo assert gruntz(-x**2, x, oo) == -oo assert gruntz(x*log(x), x, 0, dir="+") == 0 assert gruntz(1/x,x,oo) == 0 assert gruntz(exp(x),x,oo) == oo assert gruntz(-exp(x),x,oo) == -oo assert gruntz(exp(x)/x,x,oo) == oo assert gruntz(1/x-exp(-x),x,oo) == 0 assert gruntz(x+1/x,x,oo) == oo def test_limit2(): assert gruntz(x**x, x, 0, dir="+") == 1 assert gruntz((exp(x)-1)/x, x, 0) == 1 assert gruntz(1+1/x,x,oo) == 1 assert gruntz(-exp(1/x),x,oo) == -1 assert gruntz(x+exp(-x),x,oo) == oo assert gruntz(x+exp(-x**2),x,oo) == oo assert gruntz(x+exp(-exp(x)),x,oo) == oo assert gruntz(13+1/x-exp(-x),x,oo) == 13 def test_limit3(): a = Symbol('a') assert gruntz(x-log(1+exp(x)), x, oo) == 0 assert gruntz(x-log(a+exp(x)), x, oo) == 0 assert gruntz(exp(x)/(1+exp(x)), x, oo) == 1 assert gruntz(exp(x)/(a+exp(x)), x, oo) == 1 def test_limit4(): #issue 364 assert gruntz((3**x+5**x)**(1/x), x, oo) == 5 #issue 364 assert gruntz((3**(1/x)+5**(1/x))**x, x, 0) == 5 #@XFAIL #def test_MrvTestCase_page47_ex3_21(): # h = exp(-x/(1+exp(-x))) # expr = exp(h)*exp(-x/(1+h))*exp(exp(-x+h))/h**2-exp(x)+x # expected = set([1/h,exp(x),exp(x-h),exp(x/(1+h))]) # # XXX Incorrect result # assert mrv(expr,x).difference(expected) == set() def test_I(): y = Symbol("y") assert gruntz(I*x, x, oo) == I*oo assert gruntz(y*I*x, x, oo) == y*I*oo assert gruntz(y*3*I*x, x, oo) == y*I*oo assert gruntz(y*3*sin(I)*x, x, oo) == y*I*oo def test_issue1715(): assert gruntz((x + 1)**(1/log(x + 1)), x, oo) == E def test_intractable(): assert gruntz(1/gamma(x), x, oo) == 0 assert gruntz(1/loggamma(x), x, oo) == 0 assert gruntz(gamma(x)/loggamma(x), x, oo) == oo assert gruntz(exp(gamma(x))/gamma(x), x, oo) == oo assert gruntz(gamma(x), x, 3) == 2 assert gruntz(gamma(S(1)/7+1/x), x, oo) == gamma(S(1)/7) assert gruntz(log(x**x)/log(gamma(x)), x, oo) == 1 assert gruntz(log(gamma(gamma(x)))/exp(x), x, oo) == oo def test_aseries_trig(): assert cancel(gruntz(1/log(atan(x)), x, oo) \ - 1/(log(pi) + log(S(1)/2))) == 0 assert gruntz(1/acot(x), x, -oo) == -oo def test_exp_log_series(): assert gruntz(x/log(log(x*exp(x))), x, oo) == oo def test_issue545(): assert gruntz(((x**7+x+1)/(2**x+x**2))**(-1/x), x, oo) == 2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_lseries.py0000644000175000017500000000254112014170666026614 0ustar georgeskgeorgeskfrom sympy import sin, cos, exp, E, S, Order from sympy.abc import x, y def test_sin(): e = sin(x).lseries(x) assert e.next() == x assert e.next() == -x**3/6 assert e.next() == x**5/120 def test_cos(): e = cos(x).lseries(x) assert e.next() == 1 assert e.next() == -x**2/2 assert e.next() == x**4/24 def test_exp(): e = exp(x).lseries(x) assert e.next() == 1 assert e.next() == x assert e.next() == x**2/2 assert e.next() == x**3/6 def test_exp2(): e = exp(cos(x)).lseries(x) assert e.next() == E assert e.next() == -E*x**2/2 assert e.next() == E*x**4/6 assert e.next() == -31*E*x**6/720 def test_simple(): assert [t for t in x.lseries()] == [x] assert [t for t in S.One.lseries(x)] == [1] assert not ((x/(x + y)).lseries(y).next()).has(Order) def test_issue_2084(): s = (x + 1/x).lseries() assert [si for si in s] == [1/x, x] assert (x + x**2).lseries().next() == x assert ((1+x)**7).lseries(x).next() == 1 assert (sin(x + y)).series(x, n=3).lseries(y).next() == x # it would be nice if all terms were grouped, but in the # following case that would mean that all the terms would have # to be known since, for example, every term has a constant in it. s = ((1+x)**7).series(x, 1, n=None) assert [s.next() for i in range(2)] == [128, -448 + 448*x] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_nseries.py0000644000175000017500000003674312014170666026631 0ustar georgeskgeorgeskfrom sympy import (Symbol, Rational, ln, exp, log, sqrt, E, O, pi, I, sinh, sin, cosh, cos, tanh, coth, asinh, acosh, atanh, acoth, tan, cot, Integer, PoleError, floor, ceiling, asin, symbols, limit, Piecewise, Eq, sign, Derivative) from sympy.abc import x, y, z from sympy.utilities.pytest import raises def test_simple_1(): assert x.nseries(x, n=5) == x assert y.nseries(x, n=5) == y assert (1/(x*y)).nseries(y, n=5) == 1/(x*y) assert Rational(3,4).nseries(x, n=5) == Rational(3,4) assert x.nseries() == x def test_mul_0(): assert (x*ln(x)).nseries(x, n=5) == x*ln(x) def test_mul_1(): assert (x*ln(2+x)).nseries(x, n=4) == x*log(2) + x**2/2 - x**3/8 + x**4/24 + O(x**5) # x*log(2)+x**2/2-x**3/8+O(x**4) assert (x*ln(1+x)).nseries(x, n=4) == x**2 - x**3/2 + x**4/3 + O(x**5) # x**2- x**3/2 + O(x**4) def test_pow_0(): assert (x**2).nseries(x, n=5) == x**2 assert (1/x).nseries(x, n=5) == 1/x assert (1/x**2).nseries(x, n=5) == 1/x**2 assert (x**(Rational(2,3))).nseries(x, n=5) == (x**(Rational(2,3))) assert (x**(Rational(3,2))).nseries(x, n=5) == (x**(Rational(3,2))) def test_pow_1(): assert ((1+x)**2).nseries(x, n=5) == 1+2*x+x**2 def test_geometric_1(): assert (1/(1-x)).nseries(x, n=5) == 1+x+x**2+x**3+x**4+O(x**5) assert (x/(1-x)).nseries(x, n=5) == x + x**2 + x**3 + x**4 + x**5 + O(x**6) # x+x**2+x**3+x**4+O(x**5) assert (x**3/(1-x)).nseries(x, n=5) == x**3 + x**4 + x**5 + x**6 + x**7 + O(x**8) # x**3+x**4+O(x**5) def test_sqrt_1(): assert sqrt(1+x).nseries(x, n=5) == 1+x/2-x**2/8+x**3/16-5*x**4/128+O(x**5) def test_exp_1(): assert exp(x).nseries(x, n=5) == 1+x+x**2/2+x**3/6+x**4/24 + O(x**5) assert exp(x).nseries(x, n=12) == 1+x+x**2/2+x**3/6+x**4/24+x**5/120+ \ x**6/720+x**7/5040+x**8/40320+x**9/362880+x**10/3628800+ \ x**11/39916800 + O(x**12) assert exp(1/x).nseries(x, n=5) == exp(1/x) assert exp(1/(1+x)).nseries(x, n=4) == \ (E*(1-x-13*x**3/6+3*x**2/2)).expand() + O(x**4) assert exp(2+x).nseries(x, n=5) == \ (exp(2)*(1+x+x**2/2+x**3/6+x**4/24)).expand() + O(x**5) def test_exp_sqrt_1(): assert exp(1+sqrt(x)).nseries(x, n=3) == \ (exp(1)*(1+sqrt(x)+x/2+sqrt(x)*x/6)).expand() + O(sqrt(x)**3) def test_power_x_x1(): assert (exp(x*ln(x))).nseries(x, n=4) == \ 1+x*log(x)+x**2*log(x)**2/2+x**3*log(x)**3/6 + O(x**4*log(x)**4) def test_power_x_x2(): assert (x**x).nseries(x, n=4) == \ 1+x*log(x)+x**2*log(x)**2/2+x**3*log(x)**3/6 + O(x**4*log(x)**4) def test_log_singular1(): assert log(1+1/x).nseries(x, n=5) == x - log(x) - x**2/2 + x**3/3 - \ x**4/4 + O(x**5) def test_log_power1(): e = 1 / (1/x + x ** (log(3)/log(2))) assert e.nseries(x, n=5) == x - x**(2 + log(3)/log(2)) + O(x**5) def test_log_series(): l = Symbol('l') e = 1/(1-log(x)) assert e.nseries(x, n=5, logx=l) == 1/(1-l) def test_log2(): e = log(-1/x) assert e.nseries(x, n=5) == -log(x) + log(-1) def test_log3(): l = Symbol('l') e = 1/log(-1/x) assert e.nseries(x, n=4, logx=l) == 1/(-l + log(-1)) def test_series1(): x = Symbol("x") e = sin(x) assert e.nseries(x,0,0) != 0 assert e.nseries(x,0,0) == O(1, x) assert e.nseries(x,0,1) == O(x, x) assert e.nseries(x,0,2) == x + O(x**2, x) assert e.nseries(x,0,3) == x + O(x**3, x) assert e.nseries(x,0,4) == x-x**3/6 + O(x**4, x) e = (exp(x)-1)/x assert e.nseries(x,0,3) == 1+x/2+O(x**2, x) #assert x.nseries(x,0,0) == O(1, x) #assert x.nseries(x,0,1) == O(x, x) assert x.nseries(x,0,2) == x def test_seriesbug1(): x = Symbol("x") assert (1/x).nseries(x,0,3) == 1/x assert (x+1/x).nseries(x,0,3) == x+1/x def test_series2x(): x = Symbol("x") assert ((x+1)**(-2)).nseries(x,0,4) == 1-2*x+3*x**2-4*x**3+O(x**4, x) assert ((x+1)**(-1)).nseries(x,0,4) == 1-x+x**2-x**3+O(x**4, x) assert ((x+1)**0).nseries(x,0,3) == 1 assert ((x+1)**1).nseries(x,0,3) == 1+x assert ((x+1)**2).nseries(x,0,3) == 1+2*x+x**2 assert ((x+1)**3).nseries(x,0,3) == 1 + 3*x + 3*x**2 + x**3 # 1+3*x+3*x**2+O(x**3) assert (1/(1+x)).nseries(x,0,4) == 1-x+x**2-x**3+O(x**4, x) assert (x+3/(1+2*x)).nseries(x,0,4) == 3-5*x+12*x**2-24*x**3+O(x**4, x) assert ((1/x+1)**3).nseries(x,0,3)== 1+x**(-3)+3*x**(-2)+3/x assert (1/(1+1/x)).nseries(x,0,4) == x-x**2+x**3-O(x**4, x) assert (1/(1+1/x**2)).nseries(x,0,6) == x**2-x**4+O(x**6, x) def test_bug2(): ### 1/log(0) * log(0) problem w = Symbol("w") e = (w**(-1)+w**(-log(3)*log(2)**(-1)))**(-1)*(3*w**(-log(3)*log(2)**(-1))+2*w**(-1)) e = e.expand() assert e.nseries(w, 0, 4).subs(w, 0) == 3 def test_exp(): x = Symbol("x") e = (1+x)**(1/x) assert e.nseries(x, n=3) == exp(1) - x*exp(1)/2 + O(x**2, x) def test_exp2(): x = Symbol("x") w = Symbol("w") e = w**(1-log(x)/(log(2) + log(x))) logw = Symbol("logw") assert e.nseries(w,0,1,logx=logw) == exp(logw - logw*log(x)/(log(2) + log(x))) def test_bug3(): x = Symbol("x") e = (2/x+3/x**2)/(1/x+1/x**2) assert e.nseries(x, n=3) == 3 + O(x) def test_generalexponent(): x = Symbol("x") p = 2 e = (2/x+3/x**p)/(1/x+1/x**p) assert e.nseries(x,0,3) == 3 + O(x) p = Rational(1,2) e = (2/x+3/x**p)/(1/x+1/x**p) assert e.nseries(x,0,2) == 2 + sqrt(x) + O(x) e=1+x**Rational(1,2) assert e.nseries(x,0,4) == 1+x**Rational(1,2) # more complicated example def test_genexp_x(): x = Symbol("x") e=1/(1+x**Rational(1,2)) assert e.nseries(x,0,2) == \ 1+x-x**Rational(1,2)-x**Rational(3,2)+O(x**2, x) # more complicated example def test_genexp_x2(): x = Symbol("x") p = Rational(3,2) e = (2/x+3/x**p)/(1/x+1/x**p) assert e.nseries(x,0,3) == 3 - sqrt(x) + x + O(sqrt(x)**3) def test_seriesbug2(): w = Symbol("w") #simple case (1): e = ((2*w)/w)**(1+w) assert e.nseries(w,0,1) == 2 + O(w, w) assert e.nseries(w,0,1).subs(w,0) == 2 def test_seriesbug2b(): w = Symbol("w") #test sin e = sin(2*w)/w assert e.nseries(w,0,3) == 2 + O(w**2, w) def test_seriesbug2d(): w = Symbol("w", real=True) e = log(sin(2*w)/w) assert e.series(w, n=5) == log(2) - 2*w**2/3 - 4*w**4/45 + O(w**5) def test_seriesbug2c(): w = Symbol("w", real=True) #more complicated case, but sin(x)~x, so the result is the same as in (1) e=(sin(2*w)/w)**(1+w) assert e.series(w,0,1) == 2 + O(w) assert e.series(w,0,3) == 2-Rational(4,3)*w**2+w**2*log(2)**2+2*w*log(2)+O(w**3, w) assert e.series(w,0,2).subs(w,0) == 2 def test_expbug4(): x = Symbol("x", real=True) assert (log(sin(2*x)/x)*(1+x)).series(x,0,2) == log(2) + x*log(2) + O(x**2, x) #assert exp(log(2)+O(x)).nseries(x,0,2) == 2 +O(x**2, x) assert exp(log(sin(2*x)/x)*(1+x)).series(x,0,2) == 2 + 2*x*log(2) + O(x**2) #assert ((2+O(x))**(1+x)).nseries(x,0,2) == 2 + O(x**2, x) def test_logbug4(): x = Symbol("x") assert log(2+O(x)).nseries(x,0,2) == log(2) + O(x, x) def test_expbug5(): x = Symbol("x") #assert exp(O(x)).nseries(x,0,2) == 1 + O(x**2, x) assert exp(log(1+x)/x).nseries(x, n=3) == exp(1) + -exp(1)*x/2 + O(x**2) def test_sinsinbug(): x = Symbol("x") assert sin(sin(x)).nseries(x,0,8) == x-x**3/3+x**5/10-8*x**7/315+O(x**8) def test_issue159(): x = Symbol("x") a=x/(exp(x)-1) assert a.nseries(x,0,6) == 1 - x/2 - x**4/720 + x**2/12 + O(x**5) def test_issue105(): x = Symbol("x", nonnegative=True) f = sin(x**3)**Rational(1,3) assert f.nseries(x,0,17) == x - x**7/18 - x**13/3240 + O(x**17) def test_issue125(): y = Symbol("y") f=(1-y**(Rational(1)/2))**(Rational(1)/2) assert f.nseries(y,0,2) == 1 - sqrt(y)/2-y/8-y**Rational(3,2)/16+O(y**2) def test_issue364(): w = Symbol("w") x = Symbol("x") e = 1/x*(-log(w**(1 + 1/log(3)*log(5))) + log(w + w**(1/log(3)*log(5)))) e_ser = -log(5)*log(w)/(x*log(3)) + w**(log(5)/log(3) - 1)/x - \ w**(2*log(5)/log(3) - 2)/(2*x) + O(w**(-3+3*log(5)/log(3))) assert e.nseries(w, n=3) == e_ser def test_sin(): x = Symbol("x") y = Symbol("y") assert sin(8*x).nseries(x, n=4) == 8*x - 256*x**3/3 + O(x**4) assert sin(x+y).nseries(x, n=1) == sin(y) + O(x) assert sin(x+y).nseries(x, n=2) == sin(y) + cos(y)*x + O(x**2) assert sin(x+y).nseries(x, n=5) == sin(y) + cos(y)*x - sin(y)*x**2/2 - \ cos(y)*x**3/6 + sin(y)*x**4/24 + O(x**5) def test_issue416(): x = Symbol("x") e = sin(8*x)/x assert e.nseries(x, n=6) == 8 - 256*x**2/3 + 4096*x**4/15 + O(x**5) def test_issue406(): x = Symbol("x") e = sin(x)**(-4)*(cos(x)**Rational(1,2)*sin(x)**2 - \ cos(x)**Rational(1,3)*sin(x)**2) assert e.nseries(x, n=8) == -Rational(1)/12 - 7*x**2/288 - \ 43*x**4/10368 + O(x**5) def test_issue402(): x = Symbol("x") a = Symbol("a") e = x**(-2)*(x*sin(a + x) - x*sin(a)) assert e.nseries(x, n=5) == cos(a) - sin(a)*x/2 - cos(a)*x**2/6 + \ sin(a)*x**3/24 + O(x**4) e = x**(-2)*(x*cos(a + x) - x*cos(a)) assert e.nseries(x, n=5) == -sin(a) - cos(a)*x/2 + sin(a)*x**2/6 + \ cos(a)*x**3/24 + O(x**4) def test_issue403(): x = Symbol("x") e = sin(5*x)/sin(2*x) assert e.nseries(x, n=2) == Rational(5,2) + O(x) assert e.nseries(x, n=6) == Rational(5,2) - 35*x**2/4 + 329*x**4/48 + O(x**5) def test_issue404(): x = Symbol("x") e = sin(2 + x)/(2 + x) assert e.nseries(x, n=2) == sin(2)/2 + x*cos(2)/2 - x*sin(2)/4 + O(x**2) def test_issue407(): x = Symbol("x") e = (x + sin(3*x))**(-2)*(x*(x + sin(3*x)) - (x + sin(3*x))*sin(2*x)) assert e.nseries(x, n=6) == -Rational(1,4) + 5*x**2/96 + 91*x**4/768 + O(x**5) def test_issue409(): x = Symbol("x", real=True) assert log(sin(x)).series(x, n=5) == log(x) - x**2/6 - x**4/180 + O(x**5) e = -log(x) + x*(-log(x) + log(sin(2*x))) + log(sin(2*x)) assert e.series(x, n=5) == log(2)+log(2)*x-2*x**2/3-2*x**3/3-4*x**4/45+O(x**5) def test_issue408(): x = Symbol("x") e = x**(-4)*(x**2 - x**2*cos(x)**Rational(1,2)) assert e.nseries(x, n=7) == Rational(1,4) + x**2/96 + 19*x**4/5760 + O(x**5) def test_issue540(): x = Symbol("x") assert sin(cos(x)).nseries(x, n=5) == sin(1) -x**2*cos(1)/2 - x**4*sin(1)/8 + x**4*cos(1)/24 + O(x**5) def test_hyperbolic(): x = Symbol("x") assert sinh(x).nseries(x, n=6) == x + x**3/6 + x**5/120 + O(x**6) assert cosh(x).nseries(x, n=5) == 1 + x**2/2 + x**4/24 + O(x**5) assert tanh(x).nseries(x, n=6) == x - x**3/3 + 2*x**5/15 + O(x**6) assert coth(x).nseries(x, n=6) == 1/x - x**3/45 + x/3 + 2*x**5/945 + O(x**6) assert asinh(x).nseries(x, n=6) == x - x**3/6 + 3*x**5/40 + O(x**6) assert acosh(x).nseries(x, n=6) == pi*I/2 - I*x - 3*I*x**5/40 - I*x**3/6 + O(x**6) assert atanh(x).nseries(x, n=6) == x + x**3/3 + x**5/5 + O(x**6) assert acoth(x).nseries(x, n=6) == x + x**3/3 + x**5/5 + pi*I/2 + O(x**6) def test_series2(): w = Symbol("w", real=True) x = Symbol("x", real=True) e = w**(-2)*(w*exp(1/x - w) - w*exp(1/x)) assert e.nseries(w, n=3) == -exp(1/x) + w * exp(1/x) / 2 + O(w**2) def test_series3(): w = Symbol("w", real=True) x = Symbol("x", real=True) e = w**(-6)*(w**3*tan(w) - w**3*sin(w)) assert e.nseries(w, n=5) == Integer(1)/2 + O(w**2) def test_bug4(): w = Symbol("w") x = Symbol("x") e = x/(w**4 + x**2*w**4 + 2*x*w**4)*w**4 assert e.nseries(w, n=2) in [x/(1 + 2*x + x**2), 1/(1+x/2+1/x/2)/2, 1/x/(1 + 2/x + x**(-2))] def test_bug5(): w = Symbol("w") x = Symbol("x") l = Symbol('l') e = (-log(w) + log(1 + w*log(x)))**(-2)*w**(-2)*((-log(w) + log(1 + \ x*w))*(-log(w) + log(1 + w*log(x)))*w - x*(-log(w) + log(1 + \ w*log(x)))*w) assert e.nseries(w, n=1, logx=l) == x/w/l + 1/w + O(1, w) assert e.nseries(w, n=2, logx=l) == x/w/l + 1/w - x/l + 1/l*log(x)\ + x*log(x)/l**2 + O(w) def test_issue1016(): x = Symbol("x") assert ( sin(x)/(1 - cos(x)) ).nseries(x, n=2) == O(1/x) assert ( sin(x)**2/(1 - cos(x)) ).nseries(x, n=2) == O(1, x) def test_pole(): x = Symbol("x") raises(PoleError, "sin(1/x).series(x, 0, 5)") raises(PoleError, "sin(1+1/x).series(x, 0, 5)") raises(PoleError, "(x*sin(1/x)).series(x, 0, 5)") def test_expsinbug(): x = Symbol("x") assert exp(sin(x)).series(x, 0, 0) == O(1, x) assert exp(sin(x)).series(x, 0, 1) == 1+O(x) assert exp(sin(x)).series(x, 0, 2) == 1+x+O(x**2) assert exp(sin(x)).series(x, 0, 3) == 1+x+x**2/2+O(x**3) assert exp(sin(x)).series(x, 0, 4) == 1+x+x**2/2+O(x**4) assert exp(sin(x)).series(x, 0, 5) == 1+x+x**2/2-x**4/8+O(x**5) def test_floor(): x = Symbol('x') assert floor(x).series(x) == 0 assert floor(-x).series(x) == -1 assert floor(sin(x)).series(x) == 0 assert floor(sin(-x)).series(x) == -1 assert floor(x**3).series(x) == 0 assert floor(-x**3).series(x) == -1 assert floor(cos(x)).series(x) == 0 assert floor(cos(-x)).series(x) == 0 assert floor(5+sin(x)).series(x) == 5 assert floor(5+sin(-x)).series(x) == 4 assert floor(x).series(x, 2) == 2 assert floor(-x).series(x, 2) == -3 x = Symbol('x', negative=True) assert floor(x+1.5).series(x) == 1 def test_ceiling(): x = Symbol('x') assert ceiling(x).series(x) == 1 assert ceiling(-x).series(x) == 0 assert ceiling(sin(x)).series(x) == 1 assert ceiling(sin(-x)).series(x) == 0 assert ceiling(1-cos(x)).series(x) == 1 assert ceiling(1-cos(-x)).series(x) == 1 assert ceiling(x).series(x, 2) == 3 assert ceiling(-x).series(x, 2) == -2 def test_abs(): x = Symbol('x') a = Symbol('a') assert abs(x).nseries(x, n=4) == x assert abs(-x).nseries(x, n=4) == x assert abs(x+1).nseries(x, n=4) == x+1 assert abs(sin(x)).nseries(x, n=4) == x - Rational(1, 6)*x**3 + O(x**4) assert abs(sin(-x)).nseries(x, n=4) == x - Rational(1, 6)*x**3 + O(x**4) assert abs(x - a).nseries(x, 1) == Piecewise((x - 1, Eq(1 - a, 0)), ((x - a)*sign(1 - a), True)) def test_dir(): x = Symbol('x') y = Symbol('y') assert abs(x).series(x, 0, dir="+") == x assert abs(x).series(x, 0, dir="-") == -x assert floor(x+2).series(x,0,dir='+') == 2 assert floor(x+2).series(x,0,dir='-') == 1 assert floor(x+2.2).series(x,0,dir='-') == 2 assert ceiling(x+2.2).series(x,0,dir='-') == 3 assert sin(x+y).series(x,0,dir='-') == sin(x+y).series(x,0,dir='+') def test_issue405(): a = Symbol("a") e = asin(a*x)/x assert e.series(x, 4, n=2).removeO().subs(x, x - 4) == ( asin(4*a)/4 - (x - 4)*asin(4*a)/16 + a*(x - 4)/(4*sqrt(1 - 16*a**2))) def test_issue1342(): x, a, b = symbols('x,a,b') f = 1/(1+a*x) assert f.series(x, 0, 5) == 1 - a*x + a**2*x**2 - a**3*x**3 + \ a**4*x**4 + O(x**5) f = 1/(1+(a+b)*x) assert f.series(x, 0, 3) == 1 - a*x - b*x + a**2*x**2 + b**2*x**2 + \ 2*a*b*x**2 + O(x**3) def test_issue1230(): assert tan(x).series(x, pi/2, n=3).removeO().subs(x, x - pi/2) == \ -pi/6 + x/3 - 1/(x - pi/2) assert cot(x).series(x, pi, n=3).removeO().subs(x, x - pi) == \ -x/3 + pi/3 + 1/(x - pi) assert limit(tan(x)**tan(2*x), x, pi/4) == exp(-1) def test_issue2084(): assert abs(x + x**2).series(n=1) == O(x) assert abs(x + x**2).series(n=2) == x + O(x**2) assert ((1+x)**2).series(x, n=6) == 1 + 2*x + x**2 assert (1 + 1/x).series() == 1 + 1/x assert Derivative(exp(x).series(), x).doit() == \ 1 + x + x**2/2 + x**3/6 + x**4/24 + O(x**5) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_demidovich.py0000644000175000017500000001017012014170666027256 0ustar georgeskgeorgeskfrom sympy import limit, Symbol, oo, sqrt, Rational, log, exp, cos, sin, tan, \ pi, asin, together """ (*) in problem number means that the number is relative to the book "Anti-demidovich, problemas resueltos, Ed. URSS" """ x = Symbol("x") def test_leadterm(): assert (3+2*x**(log(3)/log(2)-1)).leadterm(x)==(3,0) def sqrt3(x): return x**Rational(1,3) def sqrt4(x): return x**Rational(1,4) def test_Limits_simple_0(): assert limit((2**(x+1)+3**(x+1))/(2**x+3**x),x,oo)==3 #175 def test_Limits_simple_1(): assert limit((x+1)*(x+2)*(x+3)/x**3,x, oo)==1 #172 assert limit(sqrt(x+1)-sqrt(x),x,oo)==0 #179 assert limit((2*x-3)*(3*x+5)*(4*x-6)/(3*x**3+x-1),x,oo)==8 #Primjer 1 assert limit(x/sqrt3(x**3+10),x,oo)==1 #Primjer 2 assert limit((x+1)**2/(x**2+1),x,oo)==1 #181 def test_Limits_simple_2(): assert limit(1000*x/(x**2-1),x,oo)==0 #182 assert limit((x**2-5*x+1)/(3*x+7),x,oo)==oo #183 assert limit((2*x**2-x+3)/(x**3-8*x+5),x,oo)==0 #184 assert limit((2*x**2-3*x-4)/sqrt(x**4+1),x,oo)==2 #186 assert limit((2*x+3)/(x+sqrt3(x)),x,oo)==2 #187 assert limit(x**2/(10+x*sqrt(x)),x,oo)==oo #188 assert limit(sqrt3(x**2+1)/(x+1),x,oo)==0 #189 assert limit(sqrt(x)/sqrt(x+sqrt(x+sqrt(x))),x,oo)==1 #190 def test_Limits_simple_3a(): a = Symbol('a') #issue 414 assert together(limit((x**2-(a+1)*x+a)/(x**3-a**3),x,a)) == \ (a-1)/(3*a**2) #196 def test_Limits_simple_3b(): h = Symbol("h") assert limit(((x+h)**3-x**3)/h,h,0)==3*x**2 #197 assert limit((1/(1-x)-3/(1-x**3)),x,1)==-1 #198 assert limit((sqrt(1+x)-1)/(sqrt3(1+x)-1),x,0)==Rational(3)/2 #Primer 4 assert limit((sqrt(x)-1)/(x-1),x,1)==Rational(1)/2 #199 assert limit((sqrt(x)-8)/(sqrt3(x)-4),x,64)==3 #200 assert limit((sqrt3(x)-1)/(sqrt4(x)-1),x,1)==Rational(4)/3 #201 assert limit((sqrt3(x**2)-2*sqrt3(x)+1)/(x-1)**2,x,1)==Rational(1)/9 #202 def test_Limits_simple_4a(): a = Symbol('a') assert limit((sqrt(x)-sqrt(a))/(x-a),x,a)==1/(2*sqrt(a)) #Primer 5 assert limit((sqrt(x)-1)/(sqrt3(x)-1),x,1)==Rational(3)/2 #205 assert limit((sqrt(1+x)-sqrt(1-x))/x,x,0)==1 #207 assert limit(sqrt(x**2-5*x+6)-x,x,oo)==-Rational(5)/2 #213 def test_limits_simple_4aa(): assert limit(x*(sqrt(x**2+1)-x),x,oo)==Rational(1)/2 #214 def test_Limits_simple_4b(): #issue 412 assert limit(x-sqrt3(x**3-1),x,oo)==0 #215 def test_Limits_simple_4c(): assert limit(log(1+exp(x))/x,x,-oo)==0 #267a assert limit(log(1+exp(x))/x,x,oo)==1 #267b def test_bounded(): assert limit(sin(x)/x, x, oo) == 0 #216b assert limit(x*sin(1/x), x, 0) == 0 #227a def test_f1a(): h = Symbol("h") #issue 409: assert limit((sin(2*x)/x)**(1+x),x,0) == 2 #Primer 7 def test_f1a2(): #issue 410: assert limit(((x-1)/(x+1))**x,x,oo) == exp(-2) #Primer 9 def test_f1b(): m = Symbol("m") n = Symbol("n") h = Symbol("h") a = Symbol("a") assert limit(sin(x)/x,x,2) == sin(2)/2 #216a assert limit(sin(3*x)/x,x,0) == 3 #217 assert limit(sin(5*x)/sin(2*x),x,0) == Rational(5)/2 #218 assert limit(sin(pi*x)/sin(3*pi*x),x,0) == Rational(1)/3 #219 assert limit(x*sin(pi/x),x,oo) == pi #220 assert limit((1-cos(x))/x**2,x,0) == Rational(1,2) #221 assert limit(x*sin(1/x),x,oo) == 1 #227b assert limit((cos(m*x)-cos(n*x))/x**2,x,0) == ((n**2-m**2)/2) #232 assert limit((tan(x)-sin(x))/x**3,x,0) == Rational(1,2) #233 assert limit((x-sin(2*x))/(x+sin(3*x)),x,0) == -Rational(1,4) #237 assert limit((1-sqrt(cos(x)))/x**2,x,0) == Rational(1,4) #239 assert limit((sqrt(1+sin(x))-sqrt(1-sin(x)))/x,x,0) == 1 #240 assert limit((1+h/x)**x,x,oo) == exp(h) #Primer 9 assert limit((sin(x)-sin(a))/(x-a),x,a) == cos(a) #222, *176 assert limit((cos(x)-cos(a))/(x-a),x,a) == -sin(a) #223 assert limit((sin(x+h)-sin(x))/h,h,0) == cos(x) #225 def test_f2a(): assert limit(((x+1)/(2*x+1))**(x**2),x,oo) == 0 #Primer 8 def test_f2(): assert limit((sqrt(cos(x))-sqrt3(cos(x)))/(sin(x)**2),x,0) == -Rational(1,12) #*184 def test_f3(): a = Symbol('a') #issue 405 assert limit(asin(a*x)/x, x, 0) == a wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_series.py0000644000175000017500000000600412014170666026436 0ustar georgeskgeorgeskfrom sympy import sin, cos, exp, E, series, oo, S, Derivative, O, Integral, \ Function, log, sqrt, Symbol from sympy.abc import x, y, n, k from sympy.utilities.pytest import raises def test_sin(): e1 = sin(x).series(x, 0) e2 = series(sin(x), x, 0) assert e1 == e2 def test_cos(): e1 = cos(x).series(x, 0) e2 = series(cos(x), x, 0) assert e1 == e2 def test_exp(): e1 = exp(x).series(x, 0) e2 = series(exp(x), x, 0) assert e1 == e2 def test_exp2(): e1 = exp(cos(x)).series(x, 0) e2 = series(exp(cos(x)),x,0) assert e1 == e2 def test_2124(): assert series(1, x) == 1 assert S(0).lseries(x).next() == 0 assert cos(x).series() == cos(x).series(x) raises(ValueError, 'cos(x+y).series()') raises(ValueError, 'x.series(dir="")') assert (cos(x).series(x, 1).removeO().subs(x, x - 1) - cos(x + 1).series(x).removeO().subs(x, x - 1)).expand() == 0 e = cos(x).series(x, 1, n=None) assert [e.next() for i in range(2)] == [cos(1), -((x - 1)*sin(1))] e = cos(x).series(x, 1, n=None, dir='-') assert [e.next() for i in range(2)] == [cos(1), (1 - x)*sin(1)] # the following test is exact so no need for x -> x - 1 replacement assert abs(x).series(x, 1, dir='-') == x assert exp(x).series(x, 1, dir='-', n=3).removeO().subs(x, x - 1) == \ E + E*(x - 1) + E*(x - 1)**2/2 D = Derivative assert D(x**2 + x**3*y**2, x, 2, y, 1).series(x).doit() == 12*x*y assert D(cos(x), x).lseries().next() == D(1, x) assert D(exp(x), x).series(n=3) == D(1, x) + D(x, x) + D(x**2/2, x) + O(x**3) assert Integral(x, (x, 1, 3),(y, 1, x)).series(x) == -4 + 4*x assert (1 + x + O(x**2)).getn() == 2 assert (1 + x).getn() == None assert ((1/sin(x))**oo).series() == oo logx = Symbol('logx') assert ((sin(x))**y).nseries(x, n=1, logx = logx) \ == exp(y*logx) + O(x*exp(y*logx), x) raises(NotImplementedError, 'series(Function("f")(x))') assert sin(1/x).series(x, oo, n=5) == 1/x - 1/(6*x**3) assert abs(x).series(x, oo, n=5, dir='+') == x assert abs(x).series(x, -oo, n=5, dir='-') == -x assert abs(-x).series(x, oo, n=5, dir='+') == x assert abs(-x).series(x, -oo, n=5, dir='-') == -x assert exp(x*log(x)).series(n=3) == \ 1 + x*log(x) + x**2*log(x)**2/2 + O(x**3*log(x)**3) # XXX is this right? If not, fix "ngot > n" handling in expr. p = Symbol('p', positive=True) assert exp(sqrt(p)**3*log(p)).series(n=3) == \ 1 + p**S('3/2')*log(p) + O(p**3*log(p)**3) assert exp(sin(x)*log(x)).series(n=2) == 1 + x*log(x) + O(x**2*log(x)**2) from sympy.series.acceleration import richardson, shanks from sympy import Sum, Integer def test_acceleration(): e = (1 + 1/n)**n assert round(richardson(e, n, 10, 20).evalf(), 10) == round(E.evalf(), 10) A = Sum(Integer(-1)**(k+1) / k, (k, 1, n)) assert round(shanks(A, n, 25).evalf(), 4) == round(log(2).evalf(), 4) assert round(shanks(A, n, 25, 5).evalf(), 10) == round(log(2).evalf(), 10) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/tests/test_order.py0000644000175000017500000001556012014170666026266 0ustar georgeskgeorgeskfrom sympy import Symbol, Rational, Order, C, exp, ln, log, O, var, nan, pi, S from sympy.utilities.pytest import XFAIL, raises from sympy.abc import w, x, y, z def test_caching_bug(): #needs to be a first test, so that all caches are clean #cache it e = O(w) #and test that this won't raise an exception f = O(w**(-1/x/log(3)*log(5)), w) def test_simple_1(): o = Rational(0) assert Order(2*x) == Order(x) assert Order(x)*3 == Order(x) assert -28*Order(x) == Order(x) assert Order(-23) == Order(1) assert Order(exp(x)) == Order(1,x) assert Order(exp(1/x)).expr == exp(1/x) assert Order(x*exp(1/x)).expr == x*exp(1/x) assert Order(x**(o/3)).expr == x**(o/3) assert Order(x**(5*o/3)).expr == x**(5*o/3) assert Order(x**2 + x + y, x) == O(1, x) assert Order(x**2 + x + y, y) == O(1, y) raises(NotImplementedError, 'Order(x, 2 - x)') def test_simple_2(): assert Order(2*x)*x == Order(x**2) assert Order(2*x)/x == Order(1,x) assert Order(2*x)*x*exp(1/x) == Order(x**2*exp(1/x)) assert (Order(2*x)*x*exp(1/x)/ln(x)**3).expr == x**2*exp(1/x)*ln(x)**-3 def test_simple_3(): assert Order(x)+x == Order(x) assert Order(x)+2 == 2+Order(x) assert Order(x)+x**2 == Order(x) assert Order(x)+1/x == 1/x+Order(x) assert Order(1/x)+1/x**2 == 1/x**2+Order(1/x) assert Order(x)+exp(1/x) == Order(x)+exp(1/x) def test_simple_4(): assert Order(x)**2 == Order(x**2) assert Order(x**3)**-2 == Order(x**-6) def test_simple_5(): assert Order(x)+Order(x**2) == Order(x) assert Order(x)+Order(x**-2) == Order(x**-2) assert Order(x)+Order(1/x) == Order(1/x) def test_simple_6(): assert Order(x)-Order(x) == Order(x) assert Order(x)+Order(1) == Order(1) assert Order(x)+Order(x**2) == Order(x) assert Order(1/x)+Order(1) == Order(1/x) assert Order(x)+Order(exp(1/x)) == Order(exp(1/x)) assert Order(x**3)+Order(exp(2/x)) == Order(exp(2/x)) assert Order(x**-3)+Order(exp(2/x)) == Order(exp(2/x)) def test_simple_7(): assert 1+O(1) == O(1) assert 2+O(1) == O(1) assert x+O(1) == O(1) assert 1/x+O(1) == 1/x+O(1) def test_contains_0(): assert Order(1,x).contains(Order(1,x)) assert Order(1,x).contains(Order(1)) assert Order(1).contains(Order(1,x)) def test_contains_1(): assert Order(x).contains(Order(x)) assert Order(x).contains(Order(x**2)) assert not Order(x**2).contains(Order(x)) assert not Order(x).contains(Order(1/x)) assert not Order(1/x).contains(Order(exp(1/x))) assert not Order(x).contains(Order(exp(1/x))) assert Order(1/x).contains(Order(x)) assert Order(exp(1/x)).contains(Order(x)) assert Order(exp(1/x)).contains(Order(1/x)) assert Order(exp(1/x)).contains(Order(exp(1/x))) assert Order(exp(2/x)).contains(Order(exp(1/x))) assert not Order(exp(1/x)).contains(Order(exp(2/x))) def test_contains_2(): assert Order(x).contains(Order(y)) is None assert Order(x).contains(Order(y*x)) assert Order(y*x).contains(Order(x)) assert Order(y).contains(Order(x*y)) assert Order(x).contains(Order(y**2*x)) def test_contains_3(): assert Order(x*y**2).contains(Order(x**2*y)) is None assert Order(x**2*y).contains(Order(x*y**2)) is None def test_add_1(): assert Order(x+x) == Order(x) assert Order(3*x-2*x**2) == Order(x) assert Order(1+x) == Order(1,x) assert Order(1+1/x) == Order(1/x) assert Order(ln(x)+1/ln(x)) == Order(ln(x)) assert Order(exp(1/x)+x) == Order(exp(1/x)) assert Order(exp(1/x)+1/x**20) == Order(exp(1/x)) def test_ln_args(): assert O(log(x)) + O(log(2*x)) == O(log(x)) assert O(log(x)) + O(log(x**3)) == O(log(x)) assert O(log(x*y)) + O(log(x)+log(y)) == O(log(x*y)) def test_multivar_0(): assert Order(x*y).expr == x*y assert Order(x*y**2).expr == x*y**2 assert Order(x*y,x).expr == x assert Order(x*y**2,y).expr == y**2 assert Order(x*y*z).expr == x*y*z assert Order(x/y).expr == x/y assert Order(x*exp(1/y)).expr == x*exp(1/y) assert Order(exp(x)*exp(1/y)).expr == exp(1/y) def test_multivar_0a(): assert Order(exp(1/x)*exp(1/y)).expr == exp(1/x + 1/y) def test_multivar_1(): assert Order(x+y).expr == x+y assert Order(x+2*y).expr == x+y assert (Order(x+y)+x).expr == (x+y) assert (Order(x+y)+x**2) == Order(x+y) assert (Order(x+y)+1/x) == 1/x+Order(x+y) assert Order(x**2+y*x).expr == x**2+y*x def test_multivar_2(): assert Order(x**2*y+y**2*x,x,y).expr == x**2*y+y**2*x def test_multivar_mul_1(): assert Order(x+y)*x == Order(x**2+y*x,x,y) def test_multivar_3(): assert (Order(x)+Order(y)).args in [ (Order(x), Order(y)), (Order(y), Order(x))] assert Order(x)+Order(y)+Order(x+y) == Order(x+y) assert (Order(x**2*y)+Order(y**2*x)).args in [ (Order(x*y**2), Order(y*x**2)), (Order(y*x**2), Order(x*y**2))] assert (Order(x**2*y)+Order(y*x)) == Order(x*y) def test_issue369(): x = Symbol('x') y = Symbol('y', negative=True) z = Symbol('z', complex=True) # check that Order does not modify assumptions about symbols Order(x) Order(y) Order(z) assert x.is_positive == None assert y.is_positive == False assert z.is_positive == None assert x.is_infinitesimal == None assert y.is_infinitesimal == None assert z.is_infinitesimal == None def test_leading_order(): assert (x+1+1/x**5).extract_leading_order(x) == ((1/x**5, O(1/x**5)),) assert (1+1/x).extract_leading_order(x) == ((1/x, O(1/x)),) assert (1+x).extract_leading_order(x) == ((1, O(1, x)),) assert (1+x**2).extract_leading_order(x) == ((1, O(1, x)),) assert (2+x**2).extract_leading_order(x) == ((2, O(1, x)),) assert (x+x**2).extract_leading_order(x) == ((x, O(x)),) def test_leading_order2(): assert set((2+pi+x**2).extract_leading_order(x)) == set(((pi, O(1, x)), (S(2), O(1, x)))) assert set((2*x+pi*x+x**2).extract_leading_order(x)) == set(((2*x, O(x)), (x*pi, O(x)))) def test_order_leadterm(): assert O(x**2)._eval_as_leading_term(x) == O(x**2) def test_nan(): assert not O(x).contains(nan) def test_O1(): assert O(1, x) * x == O(x) assert O(1, y) * x == O(1, y) def test_getn(): # other lines are tested incidentally by the suite assert O(x).getn() == 1 assert O(x/log(x)).getn() == 1 assert O(x**2/log(x)**2).getn() == 2 assert O(x*log(x)).getn() == 1 raises(NotImplementedError, '(O(x) + O(y)).getn()') def test_diff(): assert O(x**2).diff(x) == O(x) def test_getO(): assert (x).getO() is None assert (x).removeO() == x assert (O(x)).getO() == O(x) assert (O(x)).removeO() == 0 assert (z + O(x) + O(y)).getO() == O(x) + O(y) assert (z + O(x) + O(y)).removeO() == z raises(NotImplementedError, '(O(x)+O(y)).getn()') def test_leading_term(): from sympy import digamma assert O(1/digamma(1/x)) == O(1/log(x)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/series/__init__.py0000644000175000017500000000045012014170666024501 0ustar georgeskgeorgesk"""A module that handles series: find a limit, order the series etc. """ from order import Order from limits import limit, Limit from gruntz import gruntz from series import series from residues import residue O = Order __all__ = ['gruntz', 'limit', 'series', 'O', 'Order', 'Limit', "residue"] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/AUTHORS0000644000175000017500000001161012014170666022146 0ustar georgeskgeorgeskAll people who contributed to SymPy by sending at least a patch or more (in the order of the date of their first contribution), except those who explicitely didn't want to be mentioned: Ondrej Certik Fabian Pedregosa Jurjen N.E. Bos Mateusz Paprocki Marc-Etienne M.Leveille Brian Jorgensen Jason Gedge Robert Schwarz Pearu Peterson Fredrik Johansson Chris Wu Ulrich Hecht Goutham Lakshminarayan David Lawrence Jaroslaw Tworek David Marek Bernhard R. Link Andrej Tokarčík Or Dvory Saroj Adhikari Pauli Virtanen Robert Kern James Aspnes Nimish Telang Abderrahim Kitouni Pan Peng Friedrich Hagedorn Elrond der Elbenfuerst Rizgar Mella Felix Kaiser Roberto Nobrega David Roberts Sebastian Krämer Vinzent Steinberg Riccardo Gori Case Van Horsen Stepan Roucka Ali Raza Syed Stefano Maggiolo Robert Cimrman Bastian Weber Sebastian Krause Sebastian Kreft Dan Alan Bromborsky Boris Timokhin Robert Andy R. Terrel Hubert Tsang Konrad Meyer Henrik Johansson Priit Laes Freddie Witherden Brian E. Granger Andrew Straw Kaifeng Zhu Ted Horst Akshay Srinivasan Aaron Meurer Barry Wardell Tomasz Buchert Vinay Kumar Johann Cohen-Tanugi Jochen Voss Luke Peterson Chris Smith Thomas Sidoti Florian Mickler Nicolas Pourcelot Ben Goodrich Toon Verstraelen Ronan Lamy James Abbatiello Ryan Krauss Bill Flynn Jorn Baayen Eh Tan Renato Coutinho Oscar Benjamin Øyvind Jensen Julio Idichekop Filho Łukasz Pankowski Chu-Ching Huang Fernando Perez Raffaele De Feo Christian Muise Matt Curry Kazuo Thow Jezreel Ng Matthew Brett Addison Cugini Nicholas J.S. Kinar Thomas Dixon Cristóvão Sousa Andre de Fortier Smit Alexey U. Goodchenko Gary Kerr Sherjil Ozair Oleksandr Gituliar Sean Vig Prafullkumar P. Tale Vladimir Perić Tom Bachmann Yuri Karadzhov Vladimir Lagunov Matthew Rocklin Saptarshi Mandal Gilbert Gede Anatolii Koval Tomo Lazovich Pavel Fedotov Kibeom Kim Gregory Ksionda Tomáš Bambas Jeremias Yehdegho Jack McCaffery Luca Weihs Shai 'Deshe' Wyborski Thomas Wiecki Óscar Nájera Mario Pernici Benjamin McDonald Sam Magura Stefan Krastanov Bradley Froehle Min Ragan-Kelley Emma Hogan wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/0000755000175000017500000000000012014170666022732 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/exceptions.py0000644000175000017500000000020312014170666025460 0ustar georgeskgeorgesk"""Geometry Errors.""" class GeometryError(ValueError): """An exception raised by classes in the geometry module.""" pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/point.py0000644000175000017500000002470412014170666024444 0ustar georgeskgeorgesk"""Geometrical Points. Contains -------- Point """ from sympy.core import S, sympify from sympy.core.compatibility import iterable from sympy.simplify import simplify from sympy.geometry.exceptions import GeometryError from sympy.functions.elementary.miscellaneous import sqrt from entity import GeometryEntity class Point(GeometryEntity): """A point in a 2-dimensional Euclidean space. Parameters ---------- coords : sequence of 2 coordinate values. Attributes ---------- coordinates : 2-tuple of numbers or sympy objects. Stored in `self`. That is self[0] is the first coordinate value, and self[1] is the second coordinate value. Raises ------ NotImplementedError When trying to create a point with more than two dimensions. When `intersection` is called with object other than a Point. TypeError When trying to add or subtract points with different dimensions. Notes ----- Currently only 2-dimensional points are supported. Examples -------- >>> from sympy.geometry import Point >>> from sympy.abc import x >>> Point(1, 2) Point(1, 2) >>> Point([1, 2]) Point(1, 2) >>> Point(0, x) Point(0, x) """ def __new__(cls, *args, **kwargs): if iterable(args[0]): coords = tuple([sympify(x) for x in args[0]]) else: coords = tuple([sympify(x) for x in args]) if len(coords) != 2: raise NotImplementedError("Only two dimensional points currently supported") return GeometryEntity.__new__(cls, *coords) @property def x(self): return self[0] @property def y(self): return self[1] @property def free_symbols(self): return self.x.free_symbols.union(self.y.free_symbols) def _eval_subs(self, old, new): return type(self)(self.x.subs(old, new), self.y.subs(old, new)) def is_collinear(*points): """Is a sequence of points collinear? Test whether or not a set of points are collinear. Returns True if the set of points are collinear, or False otherwise. Parameters ---------- points : sequence of Point Returns ------- is_collinear : boolean Notes -------------------------- Slope is preserved everywhere on a line, so the slope between any two points on the line should be the same. Take the first two points, p1 and p2, and create a translated point v1 with p1 as the origin. Now for every other point we create a translated point, vi with p1 also as the origin. Note that these translations preserve slope since everything is consistently translated to a new origin of p1. Since slope is preserved then we have the following equality: v1_slope = vi_slope => v1.y/v1.x = vi.y/vi.x (due to translation) => v1.y*vi.x = vi.y*v1.x => v1.y*vi.x - vi.y*v1.x = 0 (*) Hence, if we have a vi such that the equality in (*) is False then the points are not collinear. We do this test for every point in the list, and if all pass then they are collinear. Examples -------- >>> from sympy import Point >>> from sympy.abc import x >>> p1, p2 = Point(0, 0), Point(1, 1) >>> p3, p4, p5 = Point(2, 2), Point(x, x), Point(1, 2) >>> Point.is_collinear(p1, p2, p3, p4) True >>> Point.is_collinear(p1, p2, p3, p5) False """ if len(points) == 0: return False if len(points) <= 2: return True # two points always form a line points = [Point(a) for a in points] # XXX Cross product is used now, but that only extends to three # dimensions. If the concept needs to extend to greater # dimensions then another method would have to be used p1 = points[0] p2 = points[1] v1 = p2 - p1 for p3 in points[2:]: v2 = p3 - p1 test = simplify(v1[0]*v2[1] - v1[1]*v2[0]) if simplify(test) != 0: return False return True def is_concyclic(*points): """Is a sequence of points concyclic? Test whether or not a sequence of points are concyclic (i.e., they lie on a circle). Parameters ---------- points : sequence of Points Returns ------- is_concyclic : boolean True if points are concyclic, False otherwise. Notes ----- No points are not considered to be concyclic. One or two points are definitely concyclic and three points are conyclic iff they are not collinear. For more than three points, create a circle from the first three points. If the circle cannot be created (i.e., they are collinear) then all of the points cannot be concyclic. If the circle is created successfully then simply check the remaining points for containment in the circle. Examples -------- >>> from sympy.geometry import Point >>> p1, p2 = Point(-1, 0), Point(1, 0) >>> p3, p4 = Point(0, 1), Point(-1, 2) >>> Point.is_concyclic(p1, p2, p3) True >>> Point.is_concyclic(p1, p2, p3, p4) False """ if len(points) == 0: return False if len(points) <= 2: return True points = [Point(p) for p in points] if len(points) == 3: return (not Point.is_collinear(*points)) try: from ellipse import Circle c = Circle(points[0], points[1], points[2]) for point in points[3:]: if point not in c: return False return True except GeometryError, e: # Circle could not be created, because of collinearity of the # three points passed in, hence they are not concyclic. return False """ # This code is from Maple def f(u): dd = u[0]**2 + u[1]**2 + 1 u1 = 2*u[0] / dd u2 = 2*u[1] / dd u3 = (dd - 2) / dd return u1,u2,u3 u1,u2,u3 = f(points[0]) v1,v2,v3 = f(points[1]) w1,w2,w3 = f(points[2]) p = [v1 - u1, v2 - u2, v3 - u3] q = [w1 - u1, w2 - u2, w3 - u3] r = [p[1]*q[2] - p[2]*q[1], p[2]*q[0] - p[0]*q[2], p[0]*q[1] - p[1]*q[0]] for ind in xrange(3, len(points)): s1,s2,s3 = f(points[ind]) test = simplify(r[0]*(s1-u1) + r[1]*(s2-u2) + r[2]*(s3-u3)) if test != 0: return False return True """ def distance(self, p): """The Euclidean distance from self to point p. Parameters ---------- p : Point Returns ------- distance : number or symbolic expression. Examples -------- >>> from sympy.geometry import Point >>> p1, p2 = Point(1, 1), Point(4, 5) >>> p1.distance(p2) 5 >>> from sympy.abc import x, y >>> p3 = Point(x, y) >>> p3.distance(Point(0, 0)) (x**2 + y**2)**(1/2) """ return sqrt(sum([(a - b)**2 for a, b in zip(self, p)])) def midpoint(self, p): """The midpoint between self and point p. Parameters ---------- p : Point Returns ------- midpoint : Point Examples -------- >>> from sympy.geometry import Point >>> p1, p2 = Point(1, 1), Point(13, 5) >>> p1.midpoint(p2) Point(7, 3) """ return Point([simplify((a + b)*S.Half) for a, b in zip(self, p)]) def evalf(self): """Evaluate the coordinates of the point. This method will, where possible, create and return a new Point where the coordinates are evaluated as floating point numbers. Returns ------- point : Point Examples -------- >>> from sympy import Point, Rational >>> p1 = Point(Rational(1, 2), Rational(3, 2)) >>> p1 Point(1/2, 3/2) >>> p1.evalf() Point(0.5, 1.5) """ return Point([x.evalf() for x in self]) def intersection(self, o): """The intersection between this point and another point. Parameters ---------- other : Point Returns ------- intersection : list of Points Notes ----- The return value will either be an empty list if there is no intersection, otherwise it will contain this point. Examples -------- >>> from sympy import Point >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, 0) >>> p1.intersection(p2) [] >>> p1.intersection(p3) [Point(0, 0)] """ if isinstance(o, Point): if self == o: return [self] return [] return o.intersection(self) @property def length(self): return S.Zero def __len__(self): return 1 def __add__(self, other): """Add two points, or add a factor to this point's coordinates.""" if isinstance(other, Point): if len(other.args) == len(self.args): return Point( [simplify(a + b) for a, b in zip(self, other)] ) else: raise TypeError("Points must have the same number of dimensions") else: raise ValueError('Cannot add non-Point, %s, to a Point' % other) other = sympify(other) return Point([simplify(a + other) for a in self]) def __sub__(self, other): """Subtract two points, or subtract a factor from this point's coordinates.""" return self + (-other) def __mul__(self, factor): """Multiply point's coordinates by a factor.""" factor = sympify(factor) return Point([x*factor for x in self]) def __div__(self, divisor): """Divide point's coordinates by a factor.""" divisor = sympify(divisor) return Point([x/divisor for x in self]) def __neg__(self): """Negate the point.""" return Point([-x for x in self]) def __abs__(self): """Returns the distance between this point and the origin.""" origin = Point([0]*len(self.args)) return Point.distance(origin, self) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/polygon.py0000644000175000017500000014252412014170666025003 0ustar georgeskgeorgeskfrom sympy.core import Basic, S, C, sympify, oo, pi, Symbol from sympy.functions.elementary.piecewise import Piecewise from sympy.functions.elementary.trigonometric import cos, sin from sympy.simplify import simplify from sympy.geometry.exceptions import GeometryError from sympy.solvers import solve from entity import GeometryEntity from point import Point from ellipse import Circle from line import Line, Segment, Ray from util import _symbol import warnings class Polygon(GeometryEntity): """A two-dimensional polygon. A simple polygon in space. Can be constructed from a sequence of points or from a center, radius, number of sides and rotation angle. Parameters ---------- vertices : sequence of Points Attributes ---------- area angles perimeter vertices centroid sides Raises ------ GeometryError If all parameters are not Points. If the Polygon has intersecting sides. See Also -------- Point Segment Triangle Notes ----- Polygons are treated as closed paths rather than 2D areas so some calculations can be be negative or positive (e.g., area) based on the orientation of the points. Any consecutive identical points are reduced to a single point and any points collinear and between two points will be removed unless they are needed to define an explicit intersection (see examples). A Triangle, Segment or Point will be returned when there are 3 or fewer points provided. Examples -------- >>> from sympy import Point, Polygon, pi >>> p1, p2, p3, p4, p5 = [(0, 0), (1, 0), (5, 1), (0, 1), (3, 0)] >>> Polygon(p1, p2, p3, p4) Polygon(Point(0, 0), Point(1, 0), Point(5, 1), Point(0, 1)) >>> Polygon(p1, p2) Segment(Point(0, 0), Point(1, 0)) >>> Polygon(p1, p2, p5) Segment(Point(0, 0), Point(3, 0)) While the sides of a polygon are not allowed to cross implicitly, they can do so explicitly. For example, a polygon shaped like a Z with the top left connecting to the bottom right of the Z must have the point in the middle of the Z explicitly given: >>> mid = Point(1, 1) >>> Polygon((0, 2), (2, 2), mid, (0, 0), (2, 0), mid).area 0 >>> Polygon((0, 2), (2, 2), mid, (2, 0), (0, 0), mid).area -2 When the the keyword `n` is used to define the number of sides of the Polygon then a RegularPolygon is created and the other arguments are interpreted as center, radius and rotation. The unrotated RegularPolygon will always have a vertex at Point(r, 0) where `r` is the radius of the circle that circumscribes the RegularPolygon. Its method `spin` can be used to increment that angle. >>> p = Polygon((0,0), 1, n=3) >>> p RegularPolygon(Point(0, 0), 1, 3, 0) >>> p[0] Point(1, 0) >>> p.vertices[0] Point(1, 0) >>> p.args[0] Point(0, 0) >>> p.spin(pi/2) >>> p[0] Point(0, 1) """ def __new__(cls, *args, **kwargs): if kwargs.get('n', 0): n = kwargs.pop('n') args = list(args) # return a virtual polygon with n sides if len(args) == 2: # center, radius args.append(n) elif len(args) == 3: # center, radius, rotation args.insert(2, n) return RegularPolygon(*args, **kwargs) vertices = [Point(a) for a in args] # remove consecutive duplicates nodup = [] for p in vertices: if nodup and p == nodup[-1]: continue nodup.append(p) if len(nodup) > 1 and nodup[-1] == nodup[0]: nodup.pop() # last point was same as first # remove collinear points unless they are shared points got = set() shared = set() for p in nodup: if p in got: shared.add(p) else: got.add(p) i = -3 while i < len(nodup) - 3 and len(nodup) > 2: a, b, c = sorted([nodup[i], nodup[i + 1], nodup[i + 2]]) if b not in shared and Point.is_collinear(a, b, c): nodup[i] = a nodup[i + 1] = None nodup.pop(i + 1) i += 1 vertices = filter(lambda x: x is not None, nodup) if len(vertices) > 3: rv = GeometryEntity.__new__(cls, *vertices, **kwargs) elif len(vertices) == 3: return Triangle(*vertices, **kwargs) elif len(vertices) == 2: return Segment(*vertices, **kwargs) else: return Point(*vertices, **kwargs) # reject polygons that have intersecting sides unless the # intersection is a shared point or a generalized intersection. # A self-intersecting polygon is easier to detect than a # random set of segments since only those sides that are not # part of the convex hull can possibly intersect with other # sides of the polygon...but for now we use the n**2 algorithm # and check all sides with intersection with any preceding sides hit = _symbol('hit') if not rv.is_convex: sides = rv.sides for i, si in enumerate(sides): pts = si[0], si[1] ai = si.arbitrary_point(hit) for j in xrange(i): sj = sides[j] if sj[0] not in pts and sj[1] not in pts: aj = si.arbitrary_point(hit) tx = (solve(ai[0] - aj[0]) or [S.Zero])[0] if tx.is_number and 0 <= tx <= 1: ty = (solve(ai[1] - aj[1]) or [S.Zero])[0] if (tx or ty) and ty.is_number and 0 <= ty <= 1: print ai, aj raise GeometryError("Polygon has intersecting sides.") return rv @property def area(self): """ The area of the polygon. Notes ----- The area calculation can be positive or negative based on the orientation of the points. Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.area 3 """ area = 0 for i in xrange(len(self)): pi = self[i - 1] pii = self[i] area += pi[0]*pii[1] - pii[0]*pi[1] return simplify(area) / 2 @property def angles(self): """The internal angle at each vertex. Returns ------- angles : dict A dictionary where each key is a vertex and each value is the internal angle at that vertex. The vertices are represented as Points. See Also -------- Point Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.angles[p1] pi/2 >>> poly.angles[p2] acos(-4*17**(1/2)/17) """ def tarea(a, b, c): return (b[0] - a[0])*(c[1] - a[1]) - (c[0] - a[0])*(b[1] - a[1]) def isright(a, b, c): return bool(tarea(a, b, c) <= 0) # Determine orientation of points cw = isright(self[-1], self[0], self[1]) ret = {} for i in xrange(len(self)): a, b, c = self[i-2], self[i-1], self[i] ang = Line.angle_between(Line(b, a), Line(b, c)) if cw ^ isright(a, b, c): ret[b] = 2*S.Pi - ang else: ret[b] = ang return ret @property def perimeter(self): """The perimeter of the polygon. Returns ------- perimeter : number or Basic instance Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.perimeter 17**(1/2) + 7 """ p = 0 for i in xrange(len(self)): p += Point.distance(self[i - 1], self[i]) return simplify(p) @property def vertices(self): """The vertices of the polygon. Returns ------- vertices : tuple of Points See Also -------- Point Notes ----- When iterating over the vertices, it is more efficient to index self rather than to request the vertices and index them. Only use the vertices when you want to process all of them at once. This is even more important with RegularPolygons that calculate each vertex. Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.vertices (Point(0, 0), Point(1, 0), Point(5, 1), Point(0, 1)) >>> print poly[0] Point(0, 0) """ return self[:] @property def centroid(self): """The centroid of the polygon. Returns ------- centroid : Point See Also -------- Point Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.centroid Point(31/18, 11/18) """ A = 1/(6*self.area) cx, cy = 0, 0 for i in xrange(len(self)): pi = self[i - 1] pii = self[i] v = pi[0]*pii[1] - pii[0]*pi[1] cx += v*(pi[0] + pii[0]) cy += v*(pi[1] + pii[1]) return Point(simplify(A*cx), simplify(A*cy)) @property def sides(self): """The line segments that form the sides of the polygon. Returns ------- sides : list of sides Each side is a Segment. Note ---- The Segments that represent the sides are an undirected line segment so cannot be used to tell the orientation of the polygon. See Also -------- Point Segment Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.sides [Segment(Point(0, 0), Point(1, 0)), Segment(Point(1, 0), Point(5, 1)), Segment(Point(0, 1), Point(5, 1)), Segment(Point(0, 0), Point(0, 1))] """ res = [] for i in xrange(-len(self), 0): res.append(Segment(self[i], self[i + 1])) return res def is_convex(self): """Is the polygon convex? A polygon is convex if all its interior angles are less than 180 degrees. Returns ------- is_convex : boolean True if this polygon is convex, False otherwise. Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly = Polygon(p1, p2, p3, p4) >>> poly.is_convex() True """ def tarea(a, b, c): return (b[0] - a[0])*(c[1] - a[1]) - (c[0] - a[0])*(b[1] - a[1]) def isright(a, b, c): return bool(tarea(a, b, c) <= 0) # Determine orientation of points cw = isright(self[-2], self[-1], self[0]) for i in xrange(1, len(self)): if cw ^ isright(self[i - 2], self[i - 1], self[i]): return False return True def encloses_point(self, p): """ Return True if p is enclosed by (is inside of) self. Notes ----- Being on the border of self is considered False. Parameters ---------- p : Point Returns ------- encloses_point : True, False or None Examples -------- >>> from sympy import Polygon, Point >>> from sympy.abc import t >>> p = Polygon((0, 0),(4, 0), (4, 4)) >>> p.encloses_point(Point(2, 1)) True >>> p.encloses_point(Point(2, 2)) False >>> p.encloses_point(Point(5, 5)) False Adapted from ------------ [1] http://www.ariel.com.au/a/python-point-int-poly.html [2] http://local.wasp.uwa.edu.au/~pbourke/geometry/insidepoly/ """ from sympy import Symbol if p in self: return False def concrete(p): x, y = p return x.is_number and y.is_number # move to p, checking that the result is numeric lit = [] for v in self.vertices: lit.append(v - p) if not concrete(lit[-1]): return None self = Polygon(*lit) # polygon closure is assumed in the following test but Polygon removes duplicate pts so # the last point has to be added so all sides are computed. Using Polygon.sides is # not good since Segments are unordered. indices = range(-len(self), 1) if self.is_convex(): orientation = None for i in indices: x0, y0 = self[i] x1, y1 = self[i + 1] test = ((-y0)*(x1 - x0) - (-x0)*(y1 - y0)).is_negative if orientation is None: orientation = test elif test is not orientation: return False return True hit_odd = False p1x, p1y = self[0] for i in indices[1:]: p2x, p2y = self[i] if 0 > min(p1y, p2y): if 0 <= max(p1y, p2y): if 0 <= max(p1x, p2x): if p1y != p2y: xinters = (-p1y)*(p2x - p1x)/(p2y - p1y) + p1x if p1x == p2x or 0 <= xinters: hit_odd = not hit_odd p1x, p1y = p2x, p2y return hit_odd def arbitrary_point(self, parameter='t'): """A parameterized point on the polygon. The parameter, varying from 0 to 1, assigns points to the position on the perimeter that is that fraction of the total perimeter. So the point evaluated at t=1/2 would return the point from the first vertex that is 1/2 way around the polygon. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- arbitrary_point : Point Raises ------ ValueError When `parameter` already appears in the Polygon's definition. See Also -------- Point Examples -------- >>> from sympy import Polygon, S, Symbol >>> t = Symbol('t', real=True) >>> tri = Polygon((0, 0), (1, 0), (1, 1)) >>> p = tri.arbitrary_point('t') >>> perimeter = tri.perimeter >>> s1, s2 = [s.length for s in tri.sides[:2]] >>> p.subs(t, (s1 + s2/2)/perimeter) Point(1, 1/2) """ t = _symbol(parameter) if t.name in (f.name for f in self.free_symbols): raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) sides = [] perimeter = self.perimeter perim_fraction_start = 0 for s in self.sides: side_perim_fraction = s.length/perimeter perim_fraction_end = perim_fraction_start + side_perim_fraction pt = s.arbitrary_point(parameter).subs( t, (t - perim_fraction_start)/side_perim_fraction) sides.append((pt, (perim_fraction_start <= t < perim_fraction_end))) perim_fraction_start = perim_fraction_end return Piecewise(*sides) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the polygon. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- plot_interval : list (plot interval) [parameter, lower_bound, upper_bound] Examples -------- >>> from sympy import Polygon >>> p = Polygon((0, 0), (1, 0), (1, 1)) >>> p.plot_interval() [t, 0, 1] """ t = Symbol(parameter, real=True) return [t, 0, 1] def intersection(self, o): """The intersection of two polygons. The intersection may be empty and can contain individual Points and complete Line Segments. Parameters ---------- other: Polygon Returns ------- intersection : list The list of Segments and Points Examples -------- >>> from sympy import Point, Polygon >>> p1, p2, p3, p4 = map(Point, [(0, 0), (1, 0), (5, 1), (0, 1)]) >>> poly1 = Polygon(p1, p2, p3, p4) >>> p5, p6, p7 = map(Point, [(3, 2), (1, -1), (0, 2)]) >>> poly2 = Polygon(p5, p6, p7) >>> poly1.intersection(poly2) [Point(2/3, 0), Point(9/5, 1/5), Point(7/3, 1), Point(1/3, 1)] """ res = [] for side in self.sides: inter = side.intersection(o) if inter is not None: res.extend(inter) return res def distance(self, o): if isinstance(o, Point): dist = oo for side in self.sides: current = side.distance(o) if current == 0: return S(0) elif current < dist: dist = current return dist elif isinstance(o, Polygon) and self.is_convex() and o.is_convex(): return self._do_poly_distance(o) raise NotImplementedError() def _do_poly_distance(self, e2): """ Calculates the least distance between the exteriors of two convex polygons e1 and e2. Does not check for the convexity of the polygons as it is assumed only called by Polygon.distance which does such checks. Notes ----- - Prints a warning if the two polygons possibly intersect as the return value will not be valid in such a case. For a more through test of intersection use intersection(). Example ------- >>> from sympy.geometry import Point, Polygon >>> square = Polygon(Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)) >>> triangle = Polygon(Point(1, 2), Point(2, 2), Point(2, 1)) >>> square._do_poly_distance(triangle) 2**(1/2)/2 Description of method used -------------------------- Method: [1] http://cgm.cs.mcgill.ca/~orm/mind2p.html Uses rotating calipers: [2] http://en.wikipedia.org/wiki/Rotating_calipers and antipodal points: [3] http://en.wikipedia.org/wiki/Antipodal_point """ e1 = self '''Tests for a possible intersection between the polygons and outputs a warning''' e1_center = e1.centroid e2_center = e2.centroid e1_max_radius = S(0) e2_max_radius = S(0) for vertex in e1.vertices: r = Point.distance(e1_center, vertex) if e1_max_radius < r: e1_max_radius = r for vertex in e2.vertices: r = Point.distance(e2_center, vertex) if e2_max_radius < r: e2_max_radius = r center_dist = Point.distance(e1_center, e2_center) if center_dist <= e1_max_radius + e2_max_radius: warnings.warn("Polygons may intersect producing erroneous output") ''' Find the upper rightmost vertex of e1 and the lowest leftmost vertex of e2 ''' e1_ymax = (S(0), -oo) e2_ymin = (S(0), oo) for vertex in e1.vertices: if vertex[1] > e1_ymax[1] or (vertex[1] == e1_ymax[1] and vertex[0] > e1_ymax[0]): e1_ymax = vertex for vertex in e2.vertices: if vertex[1] < e2_ymin[1] or (vertex[1] == e2_ymin[1] and vertex[0] < e2_ymin[0]): e2_ymin = vertex min_dist = Point.distance(e1_ymax, e2_ymin) ''' Produce a dictionary with vertices of e1 as the keys and, for each vertex, the points to which the vertex is connected as its value. The same is then done for e2. ''' e1_connections = {} e2_connections = {} for side in e1.sides: if side.p1 in e1_connections: e1_connections[side.p1].append(side.p2) else: e1_connections[side.p1] = [side.p2] if side.p2 in e1_connections: e1_connections[side.p2].append(side.p1) else: e1_connections[side.p2] = [side.p1] for side in e2.sides: if side.p1 in e2_connections: e2_connections[side.p1].append(side.p2) else: e2_connections[side.p1] = [side.p2] if side.p2 in e2_connections: e2_connections[side.p2].append(side.p1) else: e2_connections[side.p2] = [side.p1] e1_current = e1_ymax e2_current = e2_ymin support_line = Line(Point(S(0), S(0)), Point(S(1), S(0))) ''' Determine which point in e1 and e2 will be selected after e2_ymin and e1_ymax, this information combined with the above produced dictionaries determines the path that will be taken around the polygons ''' point1 = e1_connections[e1_ymax][0] point2 = e1_connections[e1_ymax][1] angle1 = support_line.angle_between(Line(e1_ymax, point1)) angle2 = support_line.angle_between(Line(e1_ymax, point2)) if angle1 < angle2: e1_next = point1 elif angle2 < angle1: e1_next = point2 elif Point.distance(e1_ymax, point1) > Point.distance(e1_ymax, point2): e1_next = point2 else: e1_next = point1 point1 = e2_connections[e2_ymin][0] point2 = e2_connections[e2_ymin][1] angle1 = support_line.angle_between(Line(e2_ymin, point1)) angle2 = support_line.angle_between(Line(e2_ymin, point2)) if angle1 > angle2: e2_next = point1 elif angle2 > angle1: e2_next = point2 elif Point.distance(e2_ymin, point1) > Point.distance(e2_ymin, point2): e2_next = point2 else: e2_next = point1 ''' Loop which determins the distance between anti-podal pairs and updates the minimum distance accordingly. It repeats until it reaches the starting position. ''' while True: e1_angle = support_line.angle_between(Line(e1_current, e1_next)) e2_angle = pi - support_line.angle_between(Line(e2_current, e2_next)) if e1_angle < e2_angle: support_line = Line(e1_current, e1_next) e1_segment = Segment(e1_current, e1_next) min_dist_current = e1_segment.distance(e2_current) if min_dist_current.evalf() < min_dist.evalf(): min_dist = min_dist_current if e1_connections[e1_next][0] != e1_current: e1_current = e1_next e1_next = e1_connections[e1_next][0] else: e1_current = e1_next e1_next = e1_connections[e1_next][1] elif e1_angle > e2_angle: support_line = Line(e2_next, e2_current) e2_segment = Segment(e2_current, e2_next) min_dist_current = e2_segment.distance(e1_current) if min_dist_current.evalf() < min_dist.evalf(): min_dist = min_dist_current if e2_connections[e2_next][0] != e2_current: e2_current = e2_next e2_next = e2_connections[e2_next][0] else: e2_current = e2_next e2_next = e2_connections[e2_next][1] else: support_line = Line(e1_current, e1_next) e1_segment = Segment(e1_current, e1_next) e2_segment = Segment(e2_current, e2_next) min1 = e1_segment.distance(e2_next) min2 = e2_segment.distance(e1_next) min_dist_current = min(min1, min2) if min_dist_current.evalf() < min_dist.evalf(): min_dist = min_dist_current if e1_connections[e1_next][0] != e1_current: e1_current = e1_next e1_next = e1_connections[e1_next][0] else: e1_current = e1_next e1_next = e1_connections[e1_next][1] if e2_connections[e2_next][0] != e2_current: e2_current = e2_next e2_next = e2_connections[e2_next][0] else: e2_current = e2_next e2_next = e2_connections[e2_next][1] if e1_current == e1_ymax and e2_current == e2_ymin: break return min_dist def __eq__(self, o): if not isinstance(o, Polygon) or len(self) != len(o): return False # See if self can ever be traversed (cw or ccw) from any of its # vertices to match all points of o n = len(self) o0 = o[0] for i0 in xrange(n): if self[i0] == o0: if all(self[(i0 + i) % n] == o[i] for i in xrange(1, n)): return True if all(self[(i0 - i) % n] == o[i] for i in xrange(1, n)): return True return False def __hash__(self): return super(Polygon, self).__hash__() def __contains__(self, o): """ Return True if o is contained within the boundary lines of self.altitudes Parameters ---------- other : GeometryEntity Returns ------- contained in : bool The points (and sides, if applicable) are contained in self. See Also -------- encloses Examples -------- >>> from sympy import Line, Segment, Point >>> p = Point(0, 0) >>> q = Point(1, 1) >>> s = Segment(p, q*2) >>> l = Line(p, q) >>> p in q False >>> p in s True >>> q*3 in s False >>> s in l True """ if isinstance(o, Polygon): return self == o elif isinstance(o, Segment): return any(o in s for s in self.sides) elif isinstance(o, Point): if o in self.vertices: return True for side in self.sides: if o in side: return True return False class RegularPolygon(Polygon): """ A regular polygon. Such a polygon has all internal angles equal and all sides the same length. Parameters ---------- center : Point radius : number or Basic instance The distance from the center to a vertex n : int The number of sides Attributes ---------- vertices center radius rotation apothem interior_angle exterior_angle circumcircle incircle angles Raises ------ GeometryError If the `center` is not a Point, or the `radius` is not a number or Basic instance, or the number of sides, `n`, is less than three. See Also -------- Point Note ---- A RegularPolygon can be instantiated with Polygon with the kwarg n. Regular polygons are instantiated with a center, radius, number of sides and a rotation angle. They return a vertex when indexed rather than the argument at that index. Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> r = RegularPolygon(Point(0, 0), 5, 3) >>> r RegularPolygon(Point(0, 0), 5, 3, 0) >>> r[0] Point(5, 0) """ def __new__(self, c, r, n, rot=0, **kwargs): r, n, rot = sympify([r, n, rot]) c = Point(c) if not isinstance(r, Basic): raise GeometryError("RegularPolygon.__new__ requires r to be a number or Basic instance") if n < 3: raise GeometryError("RegularPolygon.__new__ requires n >= 3") obj = GeometryEntity.__new__(self, c, r, n, **kwargs) obj._n = n obj._center = c obj._radius = r obj._rot = rot return obj @property def args(self): return self._center, self._radius, self._n, self._rot def __str__(self): return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args) def __repr__(self): return 'RegularPolygon(%s, %s, %s, %s)' % tuple(self.args) @property def center(self): """The center of the RegularPolygon This is also the center of the circumscribing circle. Returns ------- center : Point See Also -------- Point Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> rp = RegularPolygon(Point(0, 0), 5, 4) >>> rp.center Point(0, 0) """ return self._center @property def circumcenter(self): """alias for center""" return self.center @property def radius(self): """Radius of the RegularPolygon This is also the radius of the circumscribing circle. Returns ------- radius : number or instance of Basic Examples -------- >>> from sympy import Symbol >>> from sympy.geometry import RegularPolygon, Point >>> radius = Symbol('r') >>> rp = RegularPolygon(Point(0, 0), radius, 4) >>> rp.radius r """ return self._radius @property def circumradius(self): """alias for radius""" return self.radius @property def rotation(self): """CCW angle by which the RegularPolygon is rotated Returns ------- rotation : number or instance of Basic Examples -------- >>> from sympy import pi >>> from sympy.geometry import RegularPolygon, Point >>> RegularPolygon(Point(0, 0), 3, 4, pi).rotation pi """ return self._rot @property def apothem(self): """The inradius of the RegularPolygon. The apothem/inradius is the radius of the inscribed circle. Returns ------- apothem : number or instance of Basic Examples -------- >>> from sympy import Symbol >>> from sympy.geometry import RegularPolygon, Point >>> radius = Symbol('r') >>> rp = RegularPolygon(Point(0, 0), radius, 4) >>> rp.apothem 2**(1/2)*r/2 """ return self.radius * cos(S.Pi/self._n) @property def inradius(self): """alias for apothem""" return self.apothem @property def interior_angle(self): """Measure of the interior angles. Returns ------- interior_angle : number Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> rp = RegularPolygon(Point(0, 0), 4, 8) >>> rp.interior_angle 3*pi/4 """ return (self._n - 2)*S.Pi/self._n @property def exterior_angle(self): """Measure of the exterior angles. Returns ------- exterior_angle : number Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> rp = RegularPolygon(Point(0, 0), 4, 8) >>> rp.exterior_angle pi/4 """ return 2*S.Pi/self._n @property def circumcircle(self): """The circumcircle of the RegularPolygon. Returns ------- circumcircle : Circle See Also -------- Circle Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> rp = RegularPolygon(Point(0, 0), 4, 8) >>> rp.circumcircle Circle(Point(0, 0), 4) """ return Circle(self.center, self.radius) @property def incircle(self): """The incircle of the RegularPolygon. Returns ------- incircle : Circle See Also -------- Circle Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> rp = RegularPolygon(Point(0, 0), 4, 8) >>> rp.incircle Circle(Point(0, 0), 4*cos(pi/8)) """ return Circle(self.center, self.apothem) @property def angles(self): ret = {} ang = self.interior_angle for v in self.vertices: ret[v] = ang return ret def encloses_point(self, p): """ Return True if p is enclosed by (is inside of) self. Notes ----- Being on the border of self is considered False. The general Polygon.encloses_point method is called only if a point is not within or beyond the incircle or circumcircle, respectively. Parameters ---------- p : Point Returns ------- encloses_point : True, False or None Examples -------- >>> from sympy import RegularPolygon, S, Point, Symbol >>> p = RegularPolygon((0, 0), 3, 4) >>> p.encloses_point(Point(0, 0)) True >>> r, R = p.inradius, p.circumradius >>> p.encloses_point(Point((r + R)/2, 0)) True >>> p.encloses_point(Point(R/2, R/2 + (R - r)/10)) False >>> t = Symbol('t', real=True) >>> p.encloses_point(p.arbitrary_point().subs(t, S.Half)) False >>> p.encloses_point(Point(5, 5)) False """ c = self.center d = Segment(c, p).length if d >= self.radius: return False elif d < self.inradius: return True else: # now enumerate the RegularPolygon like a general polygon. return Polygon.encloses_point(self, p) def spin(self, angle): """Increment *in place* the virtual Polygon's rotation by ccw angle. See also: rotate method which moves the center. >>> from sympy import Polygon, Point, pi >>> r = Polygon(Point(0,0), 1, n=3) >>> r[0] Point(1, 0) >>> r.spin(pi/6) >>> r[0] Point(3**(1/2)/2, 1/2) """ self._rot += angle def rotate(self, angle, pt=None): """Override GeometryEntity.rotate to first rotate the RegularPolygon about its center. >>> from sympy import Point, RegularPolygon, Polygon, pi >>> t = RegularPolygon(Point(1, 0), 1, 3) >>> t[0] # vertex on x-axis Point(2, 0) >>> t.rotate(pi/2).vertices[0] # vertex on y axis now Point(0, 2) """ r = type(self)(*self.args) # need a copy or else changes are in-place r._rot += angle return GeometryEntity.rotate(r, angle, pt) @property def vertices(self): """The vertices of the RegularPolygon. Returns ------- vertices : list Each vertex is a Point. See Also -------- Point Examples -------- >>> from sympy.geometry import RegularPolygon, Point >>> rp = RegularPolygon(Point(0, 0), 5, 4) >>> rp.vertices [Point(5, 0), Point(0, 5), Point(-5, 0), Point(0, -5)] """ return [self[i] for i in xrange(len(self))] def __getitem__(self, k): """ >>> from sympy import Polygon, Point >>> r = Polygon(Point(0, 0), 1, n=3) >>> r[0] Point(1, 0) Note that iteration and indexing do not give the same results. >>> for ri in r: ... print ri Point(0, 0) 1 3 0 """ if k < -self._n or k >= self._n: raise IndexError('virtual tuple index out of range') c = self._center r = self._radius rot = self._rot v = 2*S.Pi/self._n return Point(c[0] + r*cos(k*v + rot), c[1] + r*sin(k*v + rot)) def __iter__(self): for i in [self._center, self._radius, self._n, self._rot]: yield i def __eq__(self, o): if not isinstance(o, Polygon) or len(self) != len(o): return False elif not isinstance(o, RegularPolygon): return Polygon.__eq__(o, self) return self.args == o.args def __len__(self): return self._n class Triangle(Polygon): """ A polygon with three vertices and three sides. Parameters ---------- points : sequence of Points Attributes ---------- vertices altitudes orthocenter circumcenter circumradius circumcircle inradius incircle medians medial Raises ------ GeometryError If the number of vertices is not equal to three, or one of the vertices is not a Point. See Also -------- Point Examples -------- >>> from sympy.geometry import Triangle, Point >>> Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) """ def __new__(cls, *args, **kwargs): if len(args) != 3: raise GeometryError("Triangle.__new__ requires three points") vertices = [Point(a) for a in args] # remove consecutive duplicates nodup = [] for p in vertices: if nodup and p == nodup[-1]: continue nodup.append(p) if len(nodup) > 1 and nodup[-1] == nodup[0]: nodup.pop() # last point was same as first # remove collinear points i = -3 while i < len(nodup) - 3 and len(nodup) > 2: a, b, c = sorted([nodup[i], nodup[i + 1], nodup[i + 2]]) if Point.is_collinear(a, b, c): nodup[i] = a nodup[i + 1] = None nodup.pop(i + 1) i += 1 vertices = filter(lambda x: x is not None, nodup) if len(vertices) == 3: return GeometryEntity.__new__(cls, *vertices, **kwargs) elif len(vertices) == 2: return Segment(*vertices, **kwargs) else: return Point(*vertices, **kwargs) @property def vertices(self): """The triangle's vertices Returns ------- vertices : tuple Each element in the tuple is a Point See Also -------- Point Examples -------- >>> from sympy.geometry import Triangle, Point >>> t = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) >>> t.vertices (Point(0, 0), Point(4, 0), Point(4, 3)) """ return self[:] def is_similar(t1, t2): """Is another triangle similar to this one. Two triangles are similar if one can be uniformly scaled to the other. Parameters ---------- other: Triangle Returns ------- is_similar : boolean Examples -------- >>> from sympy.geometry import Triangle, Point >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) >>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -3)) >>> t1.is_similar(t2) True >>> t2 = Triangle(Point(0, 0), Point(-4, 0), Point(-4, -4)) >>> t1.is_similar(t2) False """ if not isinstance(t2, Polygon) or len(t2) != 3: return False s1_1, s1_2, s1_3 = [side.length for side in t1.sides] s2 = [side.length for side in t2.sides] def _are_similar(u1, u2, u3, v1, v2, v3): e1 = simplify(u1/v1) e2 = simplify(u2/v2) e3 = simplify(u3/v3) return bool(e1 == e2) and bool(e2 == e3) # There's only 6 permutations, so write them out return _are_similar(s1_1, s1_2, s1_3, *s2) or \ _are_similar(s1_1, s1_3, s1_2, *s2) or \ _are_similar(s1_2, s1_1, s1_3, *s2) or \ _are_similar(s1_2, s1_3, s1_1, *s2) or \ _are_similar(s1_3, s1_1, s1_2, *s2) or \ _are_similar(s1_3, s1_2, s1_1, *s2) def is_equilateral(self): """Is the triangle equilateral Returns ------- is_equilateral : boolean Examples -------- >>> from sympy.geometry import Triangle, Point >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) >>> t1.is_equilateral() False >>> from sympy import sqrt >>> t2 = Triangle(Point(0, 0), Point(10, 0), Point(5, 5*sqrt(3))) >>> t2.is_equilateral() True """ s = self.sides return bool(s[0].length == s[1].length) and bool(s[1].length == s[2].length) def is_right(self): """Is the triangle right-angled. Returns ------- is_right : boolean Examples -------- >>> from sympy.geometry import Triangle, Point >>> t1 = Triangle(Point(0, 0), Point(4, 0), Point(4, 3)) >>> t1.is_right() True """ s = self.sides return Segment.is_perpendicular(s[0], s[1]) or \ Segment.is_perpendicular(s[1], s[2]) or \ Segment.is_perpendicular(s[0], s[2]) @property def altitudes(self): """The altitudes of the triangle. An altitude of a triangle is a straight line through a vertex and perpendicular to the opposite side. Returns ------- altitudes : dict The dictionary consists of keys which are vertices and values which are Segments. See Also -------- Point Segment Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.altitudes[p1] Segment(Point(0, 0), Point(1/2, 1/2)) """ s = self.sides v = self.vertices return {v[0]: s[1].perpendicular_segment(v[0]), v[1]: s[2].perpendicular_segment(v[1]), v[2]: s[0].perpendicular_segment(v[2])} @property def orthocenter(self): """The orthocenter of the triangle. The orthocenter is the intersection of the altitudes of a triangle. It may lie inside, outside or on the triangle. Returns ------- orthocenter : Point See Also -------- Point Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.orthocenter Point(0, 0) """ a = self.altitudes v = self.vertices return a[v[0]].intersection(a[v[1]])[0] @property def circumcenter(self): """The circumcenter of the triangle The circumcenter is the center of the circumcircle. Returns ------- circumcenter : Point See Also -------- Point Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.circumcenter Point(1/2, 1/2) """ a,b,c = [x.perpendicular_bisector() for x in self.sides] return a.intersection(b)[0] @property def circumradius(self): """The radius of the circumcircle of the triangle. Returns ------- circumradius : number of Basic instance Examples -------- >>> from sympy import Symbol >>> from sympy.geometry import Point, Triangle >>> a = Symbol('a') >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, a) >>> t = Triangle(p1, p2, p3) >>> t.circumradius (a**2/4 + 1/4)**(1/2) """ return Point.distance(self.circumcenter, self.vertices[0]) @property def circumcircle(self): """The circle which passes through the three vertices of the triangle. Returns ------- circumcircle : Circle See Also -------- Circle Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.circumcircle Circle(Point(1/2, 1/2), 2**(1/2)/2) """ return Circle(self.circumcenter, self.circumradius) def bisectors(self): """The angle bisectors of the triangle. An angle bisector of a triangle is a straight line through a vertex which cuts the corresponding angle in half. Returns ------- bisectors : dict Each key is a vertex (Point) and each value is the corresponding bisector (Segment). See Also -------- Point Segment Examples -------- >>> from sympy.geometry import Point, Triangle, Segment >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> from sympy import sqrt >>> t.bisectors()[p2] == Segment(Point(0, sqrt(2) - 1), Point(1, 0)) True """ s = self.sides v = self.vertices c = self.incenter l1 = Segment(v[0], Line(v[0], c).intersection(s[1])[0]) l2 = Segment(v[1], Line(v[1], c).intersection(s[2])[0]) l3 = Segment(v[2], Line(v[2], c).intersection(s[0])[0]) return {v[0]: l1, v[1]: l2, v[2]: l3} @property def incenter(self): """The center of the incircle. The incircle is the circle which lies inside the triangle and touches all three sides. Returns ------- incenter : Point See Also -------- incircle Point Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.incenter Point(-2**(1/2)/2 + 1, -2**(1/2)/2 + 1) """ s = self.sides v = self.vertices A,B,C = v[0],v[1],v[2] a,b,c = s[1].length,s[2].length,s[0].length x = simplify((a*A[0] + b*B[0] + c*C[0]) / (a+b+c)) y = simplify((a*A[1] + b*B[1] + c*C[1]) / (a+b+c)) return Point(x, y) @property def inradius(self): """The radius of the incircle. Returns ------- inradius : number of Basic instance See Also -------- incircle Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(4, 0), Point(0, 3) >>> t = Triangle(p1, p2, p3) >>> t.inradius 1 """ return simplify(2 * self.area / self.perimeter) @property def incircle(self): """The incircle of the triangle. The incircle is the circle which lies inside the triangle and touches all three sides. Returns ------- incircle : Circle See Also -------- Circle Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(2, 0), Point(0, 2) >>> t = Triangle(p1, p2, p3) >>> t.incircle Circle(Point(-2**(1/2) + 2, -2**(1/2) + 2), -2**(1/2) + 2) """ return Circle(self.incenter, self.inradius) @property def medians(self): """The medians of the triangle. A median of a triangle is a straight line through a vertex and the midpoint of the opposite side, and divides the triangle into two equal areas. Returns ------- medians : dict Each key is a vertex (Point) and each value is the median (Segment) at that point. See Also -------- Point Segment Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.medians[p1] Segment(Point(0, 0), Point(1/2, 1/2)) """ s = self.sides v = self.vertices return {v[0]: Segment(s[1].midpoint, v[0]), v[1]: Segment(s[2].midpoint, v[1]), v[2]: Segment(s[0].midpoint, v[2])} @property def medial(self): """The medial triangle of the triangle. The triangle which is formed from the midpoints of the three sides. Returns ------- medial : Triangle Examples -------- >>> from sympy.geometry import Point, Triangle >>> p1, p2, p3 = Point(0, 0), Point(1, 0), Point(0, 1) >>> t = Triangle(p1, p2, p3) >>> t.medial Triangle(Point(1/2, 0), Point(1/2, 1/2), Point(0, 1/2)) """ s = self.sides return Triangle(s[0].midpoint, s[1].midpoint, s[2].midpoint) #@property #def excircles(self): # """Returns a list of the three excircles for this triangle.""" # pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/util.py0000644000175000017500000001642412014170666024270 0ustar georgeskgeorgesk"""Utility functions for geometrical entities. Contains -------- intersection convex_hull are_similar """ from sympy import Symbol, Function, solve def idiff(eq, y, x, dep=None): """Return dy/dx assuming that y and any other variables given in dep depend on x. >>> from sympy.abc import x, y, a >>> from sympy.geometry.util import idiff >>> idiff(x**2 + y**2 - 4, y, x) -x/y >>> idiff(x + a + y, y, x) -1 >>> idiff(x + a + y, y, x, [a]) -Derivative(a, x) - 1 """ if not dep: dep = [] dep = set(dep) dep.add(y) f = dict([(s, Function(s.name)(x)) for s in eq.atoms(Symbol) if s != x and s in dep]) dydx = Function(y.name)(x).diff(x) return solve(eq.subs(f).diff(x), dydx)[0].subs( [(b, a) for a, b in f.iteritems()]) def _symbol(s, matching_symbol=None): """Return s if s is a Symbol, else return either a new Symbol (real=True) with the same name s or the matching_symbol if s is a string and it matches the name of the matching_symbol. >>> from sympy import Symbol >>> from sympy.geometry.util import _symbol >>> x = Symbol('x') >>> _symbol('y') y >>> _.is_real True >>> _symbol(x) x >>> _.is_real is None True >>> arb = Symbol('foo') >>> _symbol('arb', arb) # arb's name is foo so foo will not be returned arb >>> _symbol('foo', arb) # now it will foo NB: the symbol here may not be the same as a symbol with the same name defined elsewhere as a result of different assumptions. """ if isinstance(s, basestring): if matching_symbol and matching_symbol.name == s: return matching_symbol return Symbol(s, real=True) elif isinstance(s, Symbol): return s else: raise ValueError('symbol must be string for symbol name or Symbol') def intersection(*entities): """The intersection of a collection of GeometryEntity instances. Parameters ---------- entities : sequence of GeometryEntity Returns ------- intersection : list of GeometryEntity Raises ------ NotImplementedError When unable to calculate intersection. Notes ----- The intersection of any geometrical entity with itself should return a list with one item: the entity in question. An intersection requires two or more entities. If only a single entity is given then the function will return an empty list. It is possible for `intersection` to miss intersections that one knows exists because the required quantities were not fully simplified internally. Reals should be converted to Rationals, e.g. Rational(str(real_num)) or else failures due to floating point issues may result. Examples -------- >>> from sympy.geometry import Point, Line, Circle, intersection >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(-1, 5) >>> l1, l2 = Line(p1, p2), Line(p3, p2) >>> c = Circle(p2, 1) >>> intersection(l1, p2) [Point(1, 1)] >>> intersection(l1, l2) [Point(1, 1)] >>> intersection(c, p2) [] >>> intersection(c, Point(1, 0)) [Point(1, 0)] >>> intersection(c, l2) [Point(-5**(1/2)/5 + 1, 2*5**(1/2)/5 + 1), Point(5**(1/2)/5 + 1, -2*5**(1/2)/5 + 1)] """ from entity import GeometryEntity if len(entities) <= 1: return [] for i, e in enumerate(entities): if not isinstance(e, GeometryEntity): try: entities[i] = Point(e) except NotImplementedError: raise ValueError('%s is not a GeometryEntity and cannot be made into Point' % str(e)) res = entities[0].intersection(entities[1]) for entity in entities[2:]: newres = [] for x in res: newres.extend(x.intersection(entity)) res = newres return res def convex_hull(*args): """The convex hull of a collection of 2-dimensional points. Parameters ---------- args : a collection of Points Returns ------- convex_hull : Polygon Notes ----- This can only be performed on a set of non-symbolic points. See Also -------- Point References ---------- [1] http://en.wikipedia.org/wiki/Graham_scan [2] Andrew's Monotone Chain Algorithm ( A.M. Andrew, "Another Efficient Algorithm for Convex Hulls in Two Dimensions", 1979) http://softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm Examples -------- >>> from sympy.geometry import Point, convex_hull >>> points = [(1,1), (1,2), (3,1), (-5,2), (15,4)] >>> convex_hull(*points) Polygon(Point(-5, 2), Point(1, 1), Point(3, 1), Point(15, 4)) """ from entity import GeometryEntity from point import Point from line import Segment from polygon import Polygon p = set() for e in args: if not isinstance(e, GeometryEntity): try: e = Point(e) except NotImplementedError: raise ValueError('%s is not a GeometryEntity and cannot be made into Point' % str(e)) if isinstance(e, Point): p.add(e) elif isinstance(e, Segment): p.update(e.points) elif isinstance(e, Polygon): p.update(e.vertices) else: raise NotImplementedError('Convex hull for %s not implemented.' % type(e)) p = list(p) if len(p) == 1: return p[0] elif len(p) == 2: return Segment(p[0], p[1]) def orientation(p, q, r): '''Return positive if p-q-r are clockwise, neg if ccw, zero if collinear.''' return (q[1] - p[1])*(r[0] - p[0]) - (q[0] - p[0])*(r[1] - p[1]) # scan to find upper and lower convex hulls of a set of 2d points. U = [] L = [] p.sort() for p_i in p: while len(U) > 1 and orientation(U[-2], U[-1], p_i) <= 0: U.pop() while len(L) > 1 and orientation(L[-2], L[-1], p_i) >= 0: L.pop() U.append(p_i) L.append(p_i) U.reverse() convexHull = tuple(L + U[1:-1]) if len(convexHull) == 2: return Segment(convexHull[0], convexHull[1]) return Polygon(*convexHull) def are_similar(e1, e2): """Are two geometrical entities similar. Can one geometrical entity be uniformly scaled to the other? Parameters ---------- e1 : GeometryEntity e2 : GeometryEntity Returns ------- are_similar : boolean Raises ------ GeometryError When `e1` and `e2` cannot be compared. Notes ----- If the two objects are equal then they are similar. Examples -------- >>> from sympy import Point, Circle, Triangle, are_similar >>> c1, c2 = Circle(Point(0, 0), 4), Circle(Point(1, 4), 3) >>> t1 = Triangle(Point(0, 0), Point(1, 0), Point(0, 1)) >>> t2 = Triangle(Point(0, 0), Point(2, 0), Point(0, 2)) >>> t3 = Triangle(Point(0, 0), Point(3, 0), Point(0, 1)) >>> are_similar(t1, t2) True >>> are_similar(t1, t3) False """ if e1 == e2: return True try: return e1.is_similar(e2) except AttributeError: try: return e2.is_similar(e1) except AttributeError: n1 = e1.__class__.__name__ n2 = e2.__class__.__name__ raise GeometryError("Cannot test similarity between %s and %s" % (n1, n2)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/ellipse.py0000644000175000017500000007241212014170666024747 0ustar georgeskgeorgesk"""Elliptical geometrical entities. Contains -------- Ellipse Circle """ from sympy.core import S, C, sympify, symbol, Dummy from sympy.core.logic import fuzzy_bool from sympy.simplify import simplify, trigsimp from sympy.functions.elementary.miscellaneous import sqrt, Max, Min from sympy.functions.elementary.complexes import im from sympy.geometry.exceptions import GeometryError from sympy.solvers import solve_poly_system, solve from entity import GeometryEntity from point import Point from line import LinearEntity, Line from util import _symbol, idiff class Ellipse(GeometryEntity): """An elliptical GeometryEntity. Parameters ---------- center : Point, optional Default value is Point(0, 0) hradius : number or sympy expression, optional vradius : number or sympy expression, optional eccentricity : number or sympy expression, optional Two of `hradius`, `vradius` and `eccentricity` must be supplied to create an Ellipse. The third is derived from the two supplied. Attributes ---------- center hradius vradius area circumference eccentricity periapsis apoapsis focus_distance foci Raises ------ GeometryError When `hradius`, `vradius` and `eccentricity` are incorrectly supplied as parameters. TypeError When `center` is not a Point. See Also -------- Point Notes ----- Constructed from a center and two radii, the first being the horizontal radius (along the x-axis) and the second being the vertical radius (along the y-axis). When symbolic value for hradius and vradius are used, any calculation that refers to the foci or the major or minor axis will assume that the ellipse has its major radius on the x-axis. If this is not true then a manual rotation is necessary. Examples -------- >>> from sympy import Ellipse, Point, Rational >>> e1 = Ellipse(Point(0, 0), 5, 1) >>> e1.hradius, e1.vradius (5, 1) >>> e2 = Ellipse(Point(3, 1), hradius=3, eccentricity=Rational(4, 5)) >>> e2 Ellipse(Point(3, 1), 3, 9/5) Plotting example ---------------- >>> from sympy import Circle, Plot, Segment >>> c1 = Circle(Point(0,0), 1) >>> Plot(c1) # doctest: +SKIP [0]: cos(t), sin(t), 'mode=parametric' >>> p = Plot() # doctest: +SKIP >>> p[0] = c1 # doctest: +SKIP >>> radius = Segment(c1.center, c1.random_point()) # doctest: +SKIP >>> p[1] = radius # doctest: +SKIP >>> p # doctest: +SKIP [0]: cos(t), sin(t), 'mode=parametric' [1]: t*cos(1.546086215036205357975518382), t*sin(1.546086215036205357975518382), 'mode=parametric' """ def __new__(cls, center=None, hradius=None, vradius=None, eccentricity=None, **kwargs): hradius = sympify(hradius) vradius = sympify(vradius) eccentricity = sympify(eccentricity) if center is None: center = Point(0, 0) else: center = Point(center) if len(filter(None, (hradius, vradius, eccentricity))) != 2: raise ValueError('Exactly two arguments of "hradius", '\ '"vradius", and "eccentricity" must not be None."') if eccentricity is not None: if hradius is None: hradius = vradius / sqrt(1 - eccentricity**2) elif vradius is None: vradius = hradius * sqrt(1 - eccentricity**2) if hradius == vradius: return Circle(center, hradius, **kwargs) return GeometryEntity.__new__(cls, center, hradius, vradius, **kwargs) @property def center(self): """The center of the ellipse. Returns ------- center : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.center Point(0, 0) """ return self.__getitem__(0) @property def hradius(self): """The horizontal radius of the ellipse. Returns ------- hradius : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.hradius 3 """ return self.__getitem__(1) @property def vradius(self): """The vertical radius of the ellipse. Returns ------- vradius : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.vradius 1 """ return self.__getitem__(2) @property def minor(self): """Shorter axis of the ellipse (if it can be determined) else vradius. Returns ------- minor : number or expression Examples -------- >>> from sympy import Point, Ellipse, Symbol >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.minor 1 >>> a = Symbol('a') >>> b = Symbol('b') >>> Ellipse(p1, a, b).minor b >>> Ellipse(p1, b, a).minor a >>> m = Symbol('m') >>> M = m + 1 >>> Ellipse(p1, m, M).minor m """ rv = Min(*self[1:3]) if rv.func is Min: return self.vradius return rv @property def major(self): """Longer axis of the ellipse (if it can be determined) else hradius. Returns ------- major : number or expression Examples -------- >>> from sympy import Point, Ellipse, Symbol >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.major 3 >>> a = Symbol('a') >>> b = Symbol('b') >>> Ellipse(p1, a, b).major a >>> Ellipse(p1, b, a).major b >>> m = Symbol('m') >>> M = m + 1 >>> Ellipse(p1, m, M).major m + 1 """ rv = Max(*self[1:3]) if rv.func is Max: return self.hradius return rv @property def area(self): """The area of the ellipse. Returns ------- area : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.area 3*pi """ return simplify(S.Pi * self.hradius * self.vradius) @property def circumference(self): """The circumference of the ellipse. Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.circumference 12*Integral(((-8*_x**2/9 + 1)/(-_x**2 + 1))**(1/2), (_x, 0, 1)) """ if self.eccentricity == 1: return 2*pi*self.hradius else: x = C.Dummy('x', real=True) return 4*self.major*\ C.Integral(sqrt((1 - (self.eccentricity*x)**2)/(1 - x**2)), (x, 0, 1)) @property def eccentricity(self): """The eccentricity of the ellipse. Returns ------- eccentricity : number Examples -------- >>> from sympy import Point, Ellipse, sqrt >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, sqrt(2)) >>> e1.eccentricity 7**(1/2)/3 """ return self.focus_distance / self.major @property def periapsis(self): """The periapsis of the ellipse. The shortest distance between the focus and the contour. Returns ------- periapsis : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.periapsis -2*2**(1/2) + 3 """ return self.major * (1 - self.eccentricity) @property def apoapsis(self): """The periapsis of the ellipse. The greatest distance between the focus and the contour. Returns ------- apoapsis : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.apoapsis 2*2**(1/2) + 3 """ return self.major * (1 + self.eccentricity) @property def focus_distance(self): """The focale distance of the ellipse. The distance between the center and one focus. Returns ------- focus_distance : number Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.focus_distance 2*2**(1/2) """ return Point.distance(self.center, self.foci[0]) @property def foci(self): """The foci of the ellipse. Notes ----- The foci can only be calculated if the major/minor axes are known. Raises ------ ValueError When the major and minor axis cannot be determined. See Also -------- Point Examples -------- >>> from sympy import Point, Ellipse >>> p1 = Point(0, 0) >>> e1 = Ellipse(p1, 3, 1) >>> e1.foci (Point(-2*2**(1/2), 0), Point(2*2**(1/2), 0)) """ c = self.center hr, vr = self.hradius, self.vradius if hr == vr: return (c, c) # calculate focus distance manually, since focus_distance calls this routine fd = sqrt(self.major**2 - self.minor**2) if hr == self.minor: # foci on the y-axis return (c + Point(0, -fd), c + Point(0, fd)) elif hr == self.major: # foci on the x-axis return (c + Point(-fd, 0), c + Point(fd, 0)) def encloses_point(self, p): """ Return True if p is enclosed by (is inside of) self. Notes ----- Being on the border of self is considered False. Parameters ---------- p : Point Returns ------- encloses_point : True, False or None Examples -------- >>> from sympy import Ellipse, S >>> from sympy.abc import t >>> e = Ellipse((0, 0), 3, 2) >>> e.encloses_point((0, 0)) True >>> e.encloses_point(e.arbitrary_point(t).subs(t, S.Half)) False >>> e.encloses_point((4, 0)) False """ if p in self: return False if len(self.foci) == 2: # if the combined distance from the foci to p (h1 + h2) is less # than the combined distance from the foci to the minor axis # (which is the same as the major axis length) then p is inside # the ellipse h1, h2 = [f.distance(p) for f in self.foci] test = 2*self.major - (h1 + h2) else: test = self.radius - self.center.distance(p) return fuzzy_bool(test.is_positive) def tangent_lines(self, p): """Tangent lines between `p` and the ellipse. If `p` is on the ellipse, returns the tangent line through point `p`. Otherwise, returns the tangent line(s) from `p` to the ellipse, or None if no tangent line is possible (e.g., `p` inside ellipse). Parameters ---------- p : Point Returns ------- tangent_lines : list with 1 or 2 Lines Raises ------ NotImplementedError Can only find tangent lines for a point, `p`, on the ellipse. See Also -------- Point Line Examples -------- >>> from sympy import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> e1.tangent_lines(Point(3, 0)) [Line(Point(3, 0), Point(3, -12))] >>> # This will plot an ellipse together with a tangent line. >>> from sympy import Point, Ellipse, Plot >>> e = Ellipse(Point(0,0), 3, 2) >>> t = e.tangent_lines(e.random_point()) # doctest: +SKIP >>> p = Plot() # doctest: +SKIP >>> p[0] = e # doctest: +SKIP >>> p[1] = t # doctest: +SKIP """ from sympy import solve if self.encloses_point(p): return [] if p in self: rise = (self.vradius ** 2)*(self.center[0] - p[0]) run = (self.hradius ** 2)*(p[1] - self.center[1]) p2 = Point(simplify(p[0] + run), simplify(p[1] + rise)) return [Line(p, p2)] else: if len(self.foci) == 2: f1, f2 = self.foci maj = self.hradius test = (2*maj - Point.distance(f1, p) - Point.distance(f2, p)) else: test = self.radius - Point.distance(self.center, p) if test.is_number and test.is_positive: return [] # else p is outside the ellipse or we can't tell. In case of the # latter, the solutions returned will only be valid if # the point is not inside the ellipse; if it is, nan will result. x, y = Dummy('x'), Dummy('y') eq = self.equation(x, y) dydx = idiff(eq, y, x) slope = Line(p, Point(x, y)).slope tangent_points = solve([w.as_numer_denom()[0] for w in [slope - dydx, eq]], [x, y]) # handle horizontal and vertical tangent lines if len(tangent_points) == 1: assert tangent_points[0][0] == p[0] or tangent_points[0][1] == p[1] return [Line(p, Point(p[0]+1, p[1])), Line(p, Point(p[0], p[1]+1))] # others return [Line(p, tangent_points[0]), Line(p, tangent_points[1])] def is_tangent(self, o): """Is `o` tangent to the ellipse? Parameters ---------- o : GeometryEntity An Ellipse, LinearEntity or Polygon Raises ------ NotImplementedError When the wrong type of argument is supplied. Returns ------- is_tangent: boolean True if o is tangent to the ellipse, False otherwise. Examples -------- >>> from sympy import Point, Ellipse, Line >>> p0, p1, p2 = Point(0, 0), Point(3, 0), Point(3, 3) >>> e1 = Ellipse(p0, 3, 2) >>> l1 = Line(p1, p2) >>> e1.is_tangent(l1) True """ inter = None if isinstance(o, Ellipse): inter = self.intersection(o) return (inter is not None and isinstance(inter[0], Point) and len(inter) == 1) elif isinstance(o, LinearEntity): inter = self._do_line_intersection(o) if inter is not None and len(inter) == 1: return inter[0] in o else: return False elif isinstance(o, Polygon): c = 0 for seg in o.sides: inter = self._do_line_intersection(seg) c += len([True for point in inter if point in seg]) return c == 1 else: raise NotImplementedError("Unknown argument type") def arbitrary_point(self, parameter='t'): """A parameterized point on the ellipse. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- arbitrary_point : Point Raises ------ ValueError When `parameter` already appears in the functions. See Also -------- Point Examples -------- >>> from sympy import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> e1.arbitrary_point() Point(3*cos(t), 2*sin(t)) """ t = _symbol(parameter) if t.name in (f.name for f in self.free_symbols): raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) return Point(self.center[0] + self.hradius*C.cos(t), self.center[1] + self.vradius*C.sin(t)) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the Ellipse. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- plot_interval : list [parameter, lower_bound, upper_bound] Examples -------- >>> from sympy import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> e1.plot_interval() [t, -pi, pi] """ t = _symbol(parameter) return [t, -S.Pi, S.Pi] def random_point(self): """A random point on the ellipse. Returns ------- point : Point See Also -------- Point Note ---- A random point may not appear to be on the ellipse, ie, `p in e` may return False. This is because the coordinates of the point will be floating point values, and when these values are substituted into the equation for the ellipse the result may not be zero because of floating point rounding error. Examples -------- >>> from sympy import Point, Ellipse >>> e1 = Ellipse(Point(0, 0), 3, 2) >>> p1 = e1.random_point() >>> # a random point may not appear to be on the ellipse because of >>> # floating point rounding error >>> p1 in e1 # doctest: +SKIP True >>> p1 # doctest +ELLIPSIS Point(...) """ from random import random t = _symbol('t') p = self.arbitrary_point(t) # get a random value in [-pi, pi) subs_val = float(S.Pi)*(2*random() - 1) return Point(p[0].subs(t, subs_val), p[1].subs(t, subs_val)) def equation(self, x='x', y='y'): """The equation of the ellipse. Parameters ---------- x : str, optional Label for the x-axis. Default value is 'x'. y : str, optional Label for the y-axis. Default value is 'y'. Returns ------- equation : sympy expression Examples -------- >>> from sympy import Point, Ellipse >>> e1 = Ellipse(Point(1, 0), 3, 2) >>> e1.equation() y**2/4 + (x/3 - 1/3)**2 - 1 """ x = _symbol(x) y = _symbol(y) t1 = ((x - self.center[0]) / self.hradius)**2 t2 = ((y - self.center[1]) / self.vradius)**2 return t1 + t2 - 1 def _do_line_intersection(self, o): """ Find the intersection of a LinearEntity and the ellipse. All LinearEntities are treated as a line and filtered at the end to see that they lie in o. """ def dot(p1, p2): sum = 0 for ind in xrange(len(p1.args)): sum += p1[ind] * p2[ind] return simplify(sum) hr_sq = self.hradius ** 2 vr_sq = self.vradius ** 2 lp = o.points ldir = lp[1] - lp[0] diff = lp[0] - self.center mdir = (ldir[0] / hr_sq, ldir[1] / vr_sq) mdiff = (diff[0] / hr_sq, diff[1] / vr_sq) a = dot(ldir, mdir) b = dot(ldir, mdiff) c = dot(diff, mdiff) - 1 det = simplify(b*b - a*c); result = [] if det == 0: t = -b / a result.append(lp[0] + (lp[1] - lp[0]) * t) else: is_good = True try: is_good = (det > 0) except NotImplementedError: #symbolic, allow is_good = True if is_good: root = sqrt(det) t_a = (-b - root) / a t_b = (-b + root) / a result.append( lp[0] + (lp[1] - lp[0]) * t_a ) result.append( lp[0] + (lp[1] - lp[0]) * t_b ) return [r for r in result if r in o] def _do_circle_intersection(self, o): """The intersection of an Ellipse and a Circle. Private helper method for `intersection`. """ variables = self.equation().atoms(C.Symbol) if len(variables) > 2: return None if self.center == o.center: a, b, r = o.major, o.minor, self.radius x = a*sqrt(simplify((r**2 - b**2)/(a**2 - b**2))) y = b*sqrt(simplify((a**2 - r**2)/(a**2 - b**2))) return list(set([Point(x, y), Point(x, -y), Point(-x, y), Point(-x, -y)])) else: x, y = variables xx = solve(self.equation(), x) intersect = [] for xi in xx: yy = solve(o.equation().subs(x, xi), y) for yi in yy: intersect.append(Point(xi, yi)) return list(set(intersect)) def _do_ellipse_intersection(self, o): """The intersection of two ellipses. Private helper method for `intersection`. """ x = Dummy('x') y = Dummy('y') seq = self.equation(x, y) oeq = o.equation(x, y) result = solve([seq, oeq], [x, y]) return [Point(*r) for r in result if im(r[0]).is_zero and im(r[1]).is_zero] def intersection(self, o): """The intersection of this ellipse and another geometrical entity `o`. Parameters ---------- o : GeometryEntity Returns ------- intersection : list of GeometryEntities Notes ----- Currently supports intersections with Point, Line, Segment, Ray, Circle and Ellipse types. Example ------- >>> from sympy import Ellipse, Point, Line, sqrt >>> e = Ellipse(Point(0, 0), 5, 7) >>> e.intersection(Point(0, 0)) [] >>> e.intersection(Point(5, 0)) [Point(5, 0)] >>> e.intersection(Line(Point(0,0), Point(0, 1))) [Point(0, -7), Point(0, 7)] >>> e.intersection(Line(Point(5,0), Point(5, 1))) [Point(5, 0)] >>> e.intersection(Line(Point(6,0), Point(6, 1))) [] >>> e = Ellipse(Point(-1, 0), 4, 3) >>> e.intersection(Ellipse(Point(1, 0), 4, 3)) [Point(0, -3*15**(1/2)/4), Point(0, 3*15**(1/2)/4)] >>> e.intersection(Ellipse(Point(5, 0), 4, 3)) [Point(2, -3*7**(1/2)/4), Point(2, 3*7**(1/2)/4)] >>> e.intersection(Ellipse(Point(100500, 0), 4, 3)) [] >>> e.intersection(Ellipse(Point(0, 0), 3, 4)) [Point(-363/175, -48*111**(1/2)/175), Point(-363/175, 48*111**(1/2)/175), Point(3, 0)] >>> e.intersection(Ellipse(Point(-1, 0), 3, 4)) [Point(-17/5, -12/5), Point(-17/5, 12/5), Point(7/5, -12/5), Point(7/5, 12/5)] """ if isinstance(o, Point): if o in self: return [o] else: return [] elif isinstance(o, LinearEntity): # LinearEntity may be a ray/segment, so check the points # of intersection for coincidence first return self._do_line_intersection(o) elif isinstance(o, Circle): return self._do_circle_intersection(o) elif isinstance(o, Ellipse): if o == self: return self else: return self._do_ellipse_intersection(o) return o.intersection(self) def __eq__(self, o): """Is the other GeometryEntity the same as this ellipse?""" return (self.center == o.center and self.hradius == o.hradius and self.vradius == o.vradius) def __hash__(self): return super(Ellipse, self).__hash__() def __contains__(self, o): if isinstance(o, Point): x = C.Dummy('x', real=True) y = C.Dummy('y', real=True) res = self.equation(x, y).subs({x: o[0], y: o[1]}) return trigsimp(simplify(res)) is S.Zero elif isinstance(o, Ellipse): return self == o return False class Circle(Ellipse): """A circle in space. Constructed simply from a center and a radius, or from three non-collinear points. Parameters ---------- center : Point radius : number or sympy expression points : sequence of three Points Attributes ---------- radius (synonymous with hradius, vradius, major and minor) circumference equation Raises ------ GeometryError When trying to construct circle from three collinear points. When trying to construct circle from incorrect parameters. See Also -------- Point Examples -------- >>> from sympy.geometry import Point, Circle >>> # a circle constructed from a center and radius >>> c1 = Circle(Point(0, 0), 5) >>> c1.hradius, c1.vradius, c1.radius (5, 5, 5) >>> # a circle costructed from three points >>> c2 = Circle(Point(0, 0), Point(1, 1), Point(1, 0)) >>> c2.hradius, c2.vradius, c2.radius, c2.center (2**(1/2)/2, 2**(1/2)/2, 2**(1/2)/2, Point(1/2, 1/2)) """ def __new__(cls, *args, **kwargs): c, r = None, None if len(args) == 3: args = [Point(a) for a in args] if Point.is_collinear(*args): raise GeometryError("Cannot construct a circle from three collinear points") from polygon import Triangle t = Triangle(*args) c = t.circumcenter r = t.circumradius elif len(args) == 2: # Assume (center, radius) pair c = Point(args[0]) r = sympify(args[1]) if not (c is None or r is None): return GeometryEntity.__new__(cls, c, r, **kwargs) raise GeometryError("Circle.__new__ received unknown arguments") @property def radius(self): """The radius of the circle. Returns ------- radius : number or sympy expression Examples -------- >>> from sympy import Point, Circle >>> c1 = Circle(Point(3, 4), 6) >>> c1.radius 6 """ return self.__getitem__(1) @property def major(self): return self.radius @property def minor(self): return self.radius @property def hradius(self): return self.radius @property def vradius(self): return self.radius @property def circumference(self): """The circumference of the circle. Returns ------- circumference : number or sympy expression Examples -------- >>> from sympy import Point, Circle >>> c1 = Circle(Point(3, 4), 6) >>> c1.circumference 12*pi """ return 2 * S.Pi * self.radius def equation(self, x='x', y='y'): """The equation of the circle. Parameters ---------- x : str or Symbol, optional Default value is 'x'. y : str or Symbol, optional Default value is 'y'. Returns ------- equation : sympy expression Examples -------- >>> from sympy import Point, Circle >>> c1 = Circle(Point(0, 0), 5) >>> c1.equation() x**2 + y**2 - 25 """ x = _symbol(x) y = _symbol(y) t1 = (x - self.center[0])**2 t2 = (y - self.center[1])**2 return t1 + t2 - self.major**2 def intersection(self, o): """The intersection of this circle with another geometrical entity. Parameters ---------- o : GeometryEntity Returns ------- intersection : list of GeometryEntities Examples -------- >>> from sympy import Point, Circle, Line, Ray >>> p1, p2, p3 = Point(0, 0), Point(5, 5), Point(6, 0) >>> p4 = Point(5, 0) >>> c1 = Circle(p1, 5) >>> c1.intersection(p2) [] >>> c1.intersection(p4) [Point(5, 0)] >>> c1.intersection(Ray(p1, p2)) [Point(5*2**(1/2)/2, 5*2**(1/2)/2)] >>> c1.intersection(Line(p2, p3)) [] """ if isinstance(o, Circle): if o.center == self.center: if o.radius == self.radius: return o return [] dx,dy = o.center - self.center d = sqrt(simplify(dy**2 + dx**2)) R = o.radius + self.radius if d > R or d < abs(self.radius - o.radius): return [] a = simplify((self.radius**2 - o.radius**2 + d**2) / (2*d)) x2 = self.center[0] + (dx * a/d) y2 = self.center[1] + (dy * a/d) h = sqrt(simplify(self.radius**2 - a**2)) rx = -dy * (h/d) ry = dx * (h/d) xi_1 = simplify(x2 + rx) xi_2 = simplify(x2 - rx) yi_1 = simplify(y2 + ry) yi_2 = simplify(y2 - ry) ret = [Point(xi_1, yi_1)] if xi_1 != xi_2 or yi_1 != yi_2: ret.append(Point(xi_2, yi_2)) return ret return Ellipse.intersection(self, o) from polygon import Polygon wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/line.py0000644000175000017500000012046112014170666024237 0ustar georgeskgeorgesk"""Line-like geometrical entities. Contains -------- LinearEntity Line Ray Segment """ from sympy.core import S, C, sympify, Dummy from sympy.functions.elementary.trigonometric import _pi_coeff as pi_coeff from sympy.core.numbers import Float, Rational from sympy.simplify import simplify from sympy.solvers import solve from sympy.geometry.exceptions import GeometryError from entity import GeometryEntity from point import Point from util import _symbol class LinearEntity(GeometryEntity): """An abstract base class for all linear entities (line, ray and segment) in a 2-dimensional Euclidean space. Attributes ---------- p1 p2 coefficients slope points Notes ----- This is an abstract class and is not meant to be instantiated. Subclasses should implement the following methods: __eq__ __contains__ """ def __new__(cls, p1, p2, **kwargs): p1 = Point(p1) p2 = Point(p2) if p1 == p2: # Rolygon returns lower priority classes...should LinearEntity, too? return p1 # raise ValueError("%s.__new__ requires two unique Points." % cls.__name__) return GeometryEntity.__new__(cls, p1, p2, **kwargs) @property def p1(self): """The first defining point of a linear entity. See Also -------- Point Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(5, 3) >>> l = Line(p1, p2) >>> l.p1 Point(0, 0) """ return self.__getitem__(0) @property def p2(self): """The second defining point of a linear entity. See Also -------- Point Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(5, 3) >>> l = Line(p1, p2) >>> l.p2 Point(5, 3) """ return self.__getitem__(1) @property def coefficients(self): """The coefficients (a, b, c) for the linear equation ax + by + c = 0. Examples -------- >>> from sympy import Point, Line >>> from sympy.abc import x, y >>> p1, p2 = Point(0, 0), Point(5, 3) >>> l = Line(p1, p2) >>> l.coefficients (-3, 5, 0) >>> p3 = Point(x, y) >>> l2 = Line(p1, p3) >>> l2.coefficients (-y, x, 0) """ p1, p2 = self.points if p1[0] == p2[0]: return (S.One, S.Zero, -p1[0]) elif p1[1] == p2[1]: return (S.Zero, S.One, -p1[1]) return (self.p1[1]-self.p2[1], self.p2[0]-self.p1[0], self.p1[0]*self.p2[1] - self.p1[1]*self.p2[0]) def is_concurrent(*lines): """Is a sequence of linear entities concurrent? Two or more linear entities are concurrent if they all intersect at a single point. Parameters ---------- lines : a sequence of linear entities. Returns ------- True if the set of linear entities are concurrent, False otherwise. Notes ----- Simply take the first two lines and find their intersection. If there is no intersection, then the first two lines were parallel and had no intersection so concurrency is impossible amongst the whole set. Otherwise, check to see if the intersection point of the first two lines is a member on the rest of the lines. If so, the lines are concurrent. Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(3, 5) >>> p3, p4 = Point(-2, -2), Point(0, 2) >>> l1, l2, l3 = Line(p1, p2), Line(p1, p3), Line(p1, p4) >>> l1.is_concurrent(l2, l3) True >>> l4 = Line(p2, p3) >>> l4.is_concurrent(l2, l3) False """ # Concurrency requires intersection at a single point; One linear # entity cannot be concurrent. if len(lines) <= 1: return False try: # Get the intersection (if parallel) p = lines[0].intersection(lines[1]) if len(p) == 0: return False # Make sure the intersection is on every linear entity for line in lines[2:]: if p[0] not in line: return False return True except AttributeError: return False def is_parallel(l1, l2): """Are two linear entities parallel? Parameters ---------- l1 : LinearEntity l2 : LinearEntity Returns ------- True if l1 and l2 are parallel, False otherwise. Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(1, 1) >>> p3, p4 = Point(3, 4), Point(6, 7) >>> l1, l2 = Line(p1, p2), Line(p3, p4) >>> Line.is_parallel(l1, l2) True >>> p5 = Point(6, 6) >>> l3 = Line(p3, p5) >>> Line.is_parallel(l1, l3) False """ try: a1, b1, c1 = l1.coefficients a2, b2, c2 = l2.coefficients return bool(simplify(a1*b2 - b1*a2) == 0) except AttributeError: return False def is_perpendicular(l1, l2): """Are two linear entities parallel? Parameters ---------- l1 : LinearEntity l2 : LinearEntity Returns ------- True if l1 and l2 are perpendicular, False otherwise. Examples -------- >>> from sympy import Point, Line >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(-1, 1) >>> l1, l2 = Line(p1, p2), Line(p1, p3) >>> l1.is_perpendicular(l2) True >>> p4 = Point(5, 3) >>> l3 = Line(p1, p4) >>> l1.is_perpendicular(l3) False """ try: a1, b1, c1 = l1.coefficients a2, b2, c2 = l2.coefficients return bool(simplify(a1*a2 + b1*b2) == 0) except AttributeError: return False def angle_between(l1, l2): """The angle formed between the two linear entities. Parameters ---------- l1 : LinearEntity l2 : LinearEntity Returns ------- angle : angle in radians Notes ----- From the dot product of vectors v1 and v2 it is known that: dot(v1, v2) = |v1|*|v2|*cos(A) where A is the angle formed between the two vectors. We can get the directional vectors of the two lines and readily find the angle between the two using the above formula. Examples -------- >>> from sympy import Point, Line >>> p1, p2, p3 = Point(0, 0), Point(0, 4), Point(2, 0) >>> l1, l2 = Line(p1, p2), Line(p1, p3) >>> l1.angle_between(l2) pi/2 """ v1 = l1.p2 - l1.p1 v2 = l2.p2 - l2.p1 return C.acos((v1[0]*v2[0] + v1[1]*v2[1]) / (abs(v1)*abs(v2))) def parallel_line(self, p): """Create a new Line parallel to this linear entity which passes through the point `p`. Parameters ---------- p : Point Returns ------- line : Line Examples -------- >>> from sympy import Point, Line >>> p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2) >>> l1 = Line(p1, p2) >>> l2 = l1.parallel_line(p3) >>> p3 in l2 True >>> l1.is_parallel(l2) True """ d = self.p1 - self.p2 return Line(p, p + d) def perpendicular_line(self, p): """Create a new Line perpendicular to this linear entity which passes through the point `p`. Parameters ---------- p : Point Returns ------- line : Line Examples -------- >>> from sympy import Point, Line >>> p1, p2, p3 = Point(0, 0), Point(2, 3), Point(-2, 2) >>> l1 = Line(p1, p2) >>> l2 = l1.perpendicular_line(p3) >>> p3 in l2 True >>> l1.is_perpendicular(l2) True """ d1, d2 = self.p1 - self.p2 if d2 == 0: # If an horizontal line if p[1] == self.p1[1]: # if p is on this linear entity p2 = Point(p[0], p[1] + 1) return Line(p, p2) else: p2 = Point(p[0], self.p1[1]) return Line(p, p2) else: p2 = Point(p[0] - d2, p[1] + d1) return Line(p, p2) def perpendicular_segment(self, p): """Create a perpendicular line segment from `p` to this line. Parameters ---------- p : Point Returns ------- segment : Segment Notes ----- Returns `p` itself if `p` is on this linear entity. Examples -------- >>> from sympy import Point, Line >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, 2) >>> l1 = Line(p1, p2) >>> s1 = l1.perpendicular_segment(p3) >>> l1.is_perpendicular(s1) True >>> p3 in s1 True """ if p in self: return p pl = self.perpendicular_line(p) p2 = self.intersection(pl)[0] return Segment(p, p2) @property def length(self): return S.Infinity @property def slope(self): """The slope of this linear entity, or infinity if vertical. Returns ------- slope : number or sympy expression Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(3, 5) >>> l1 = Line(p1, p2) >>> l1.slope 5/3 >>> p3 = Point(0, 4) >>> l2 = Line(p1, p3) >>> l2.slope oo """ d1, d2 = self.p1 - self.p2 if d1 == 0: return S.Infinity return simplify(d2/d1) @property def points(self): """The two points used to define this linear entity. Returns ------- points : tuple of Points See Also -------- Point Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(5, 11) >>> l1 = Line(p1, p2) >>> l1.points (Point(0, 0), Point(5, 11)) """ return (self.p1, self.p2) def projection(self, o): """Project a point, line, ray, or segment onto this linear entity. Parameters ---------- other : Point or LinearEntity (Line, Ray, Segment) Returns ------- projection : Point or LinearEntity (Line, Ray, Segment) The return type matches the type of the parameter `other`. Raises ------ GeometryError When method is unable to perform projection. See Also -------- Point Notes ----- A projection involves taking the two points that define the linear entity and projecting those points onto a Line and then reforming the linear entity using these projections. A point P is projected onto a line L by finding the point on L that is closest to P. This is done by creating a perpendicular line through P and L and finding its intersection with L. Examples -------- >>> from sympy import Point, Line, Segment, Rational >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(Rational(1, 2), 0) >>> l1 = Line(p1, p2) >>> l1.projection(p3) Point(1/4, 1/4) >>> p4, p5 = Point(10, 0), Point(12, 1) >>> s1 = Segment(p4, p5) >>> l1.projection(s1) Segment(Point(5, 5), Point(13/2, 13/2)) """ tline = Line(self.p1, self.p2) def project(p): """Project a point onto the line representing self.""" if p in tline: return p l1 = tline.perpendicular_line(p) return tline.intersection(l1)[0] projected = None if isinstance(o, Point): return project(o) elif isinstance(o, LinearEntity): n_p1 = project(o.p1) n_p2 = project(o.p2) if n_p1 == n_p2: projected = n_p1 else: projected = o.__class__(n_p1, n_p2) # Didn't know how to project so raise an error if projected is None: n1 = self.__class__.__name__ n2 = o.__class__.__name__ raise GeometryError("Do not know how to project %s onto %s" % (n2, n1)) return self.intersection(projected)[0] def intersection(self, o): """The intersection with another geometrical entity. Parameters ---------- o : Point or LinearEntity Returns ------- intersection : list of geometrical entities Examples -------- >>> from sympy import Point, Line, Segment >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(7, 7) >>> l1 = Line(p1, p2) >>> l1.intersection(p3) [Point(7, 7)] >>> p4, p5 = Point(5, 0), Point(0, 3) >>> l2 = Line(p4, p5) >>> l1.intersection(l2) [Point(15/8, 15/8)] >>> p6, p7 = Point(0, 5), Point(2, 6) >>> s1 = Segment(p6, p7) >>> l1.intersection(s1) [] """ if isinstance(o, Point): if o in self: return [o] else: return [] elif isinstance(o, LinearEntity): a1, b1, c1 = self.coefficients a2, b2, c2 = o.coefficients t = simplify(a1*b2 - a2*b1) if t == 0: # are parallel? if isinstance(self, Line): if o.p1 in self: return [o] return [] elif isinstance(o, Line): if self.p1 in o: return [self] return [] elif isinstance(self, Ray): if isinstance(o, Ray): # case 1, rays in the same direction if self.xdirection == o.xdirection: if self.source[0] < o.source[0]: return [o] return [self] # case 2, rays in the opposite directions else: if o.source in self: if self.source == o.source: return [self.source] return [Segment(o.source, self.source)] return [] elif isinstance(o, Segment): if o.p1 in self: if o.p2 in self: return [o] return [Segment(o.p1, self.source)] elif o.p2 in self: return [Segment(o.p2, self.source)] return [] elif isinstance(self, Segment): if isinstance(o, Ray): return o.intersection(self) elif isinstance(o, Segment): # A reminder that the points of Segments are ordered # in such a way that the following works. See # Segment.__new__ for details on the ordering. if self.p1 not in o: if self.p2 not in o: # Neither of the endpoints are in o so either # o is contained in this segment or it isn't if o in self: return [self] return [] else: # p1 not in o but p2 is. Either there is a # segment as an intersection, or they only # intersect at an endpoint if self.p2 == o.p1: return [o.p1] return [Segment(o.p1, self.p2)] elif self.p2 not in o: # p2 not in o but p1 is. Either there is a # segment as an intersection, or they only # intersect at an endpoint if self.p1 == o.p2: return [o.p2] return [Segment(o.p2, self.p1)] # Both points of self in o so the whole segment # is in o return [self] # Unknown linear entity return [] # Not parallel, so find the point of intersection px = simplify((b1*c2 - c1*b2) / t) py = simplify((a2*c1 - a1*c2) / t) inter = Point(px, py) if inter in self and inter in o: return [inter] return [] return o.intersection(self) def random_point(self): """A random point on a LinearEntity. Returns ------- point : Point See Also -------- Point Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(5, 3) >>> l1 = Line(p1, p2) >>> p3 = l1.random_point() >>> # random point - don't know its coords in advance >>> p3 # doctest: +ELLIPSIS Point(...) >>> # point should belong to the line >>> p3 in l1 True """ from random import randint # The lower and upper lower, upper = -2**32 - 1, 2**32 if self.slope is S.Infinity: if isinstance(self, Ray): if self.ydirection is S.Infinity: lower = self.p1[1] else: upper = self.p1[1] elif isinstance(self, Segment): lower = self.p1[1] upper = self.p2[1] x = self.p1[0] y = randint(lower, upper) else: if isinstance(self, Ray): if self.xdirection is S.Infinity: lower = self.p1[0] else: upper = self.p1[0] elif isinstance(self, Segment): lower = self.p1[0] upper = self.p2[0] a, b, c = self.coefficients x = randint(lower, upper) y = simplify((-c - a*x) / b) return Point(x, y) def is_similar(self, other): """Return True if self and other are contained in the same line.""" def norm(a, b, c): if a != 0: return 1, b/a, c/a elif b != 0: return a/b, 1, c/b else: return c return norm(*self.coefficients) == norm(*other.coefficients) def __eq__(self, other): """Subclasses should implement this method.""" raise NotImplementedError() def __hash__(self): return super(LinearEntity, self).__hash__() class Line(LinearEntity): """An infinite line in space. A line is declared with two distinct points or a point and slope as defined using keyword `slope`. Note ---- At the moment only lines in a 2D space can be declared, because Points can be defined only for 2D spaces. Parameters ---------- p1 : Point pt : Point slope: sympy expression See Also -------- Point Examples -------- >>> import sympy >>> from sympy import Point >>> from sympy.abc import L >>> from sympy.geometry import Line >>> L = Line(Point(2,3), Point(3,5)) >>> L Line(Point(2, 3), Point(3, 5)) >>> L.points (Point(2, 3), Point(3, 5)) >>> L.equation() -2*x + y + 1 >>> L.coefficients (-2, 1, 1) Instantiate with keyword `slope`: >>> Line(Point(0, 0), slope=2) Line(Point(0, 0), Point(1, 2)) """ def __new__(cls, p1, pt=None, slope=None, **kwargs): p1 = Point(p1) if pt and slope is None: try: p2 = Point(pt) except NotImplementedError: raise ValueError('The 2nd argument was not a valid Point; if it was meant to be a slope it should be given with keyword "slope".') if p1 == p2: raise ValueError('A line requires two distinct points.') elif slope and pt is None: slope = sympify(slope) if slope.is_bounded is False: # when unbounded slope, don't change x p2 = p1 + Point(0, 1) else: # go over 1 up slope p2 = p1 + Point(1, slope) else: raise ValueError('A 2nd Point or keyword "slope" must be used.') return LinearEntity.__new__(cls, p1, p2, **kwargs) def arbitrary_point(self, parameter='t'): """A parameterized point on the Line. Parameters ---------- parameter : str, optional The name of the parameter which will be used for the parametric point. The default value is 't'. Returns ------- point : Point Raises ------ ValueError When `parameter` already appears in the Line's definition. See Also -------- Point Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(1, 0), Point(5, 3) >>> l1 = Line(p1, p2) >>> l1.arbitrary_point() Point(4*t + 1, 3*t) """ t = _symbol(parameter) if t.name in (f.name for f in self.free_symbols): raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) x = simplify(self.p1[0] + t*(self.p2[0] - self.p1[0])) y = simplify(self.p1[1] + t*(self.p2[1] - self.p1[1])) return Point(x, y) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of line. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- plot_interval : list (plot interval) [parameter, lower_bound, upper_bound] Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(0, 0), Point(5, 3) >>> l1 = Line(p1, p2) >>> l1.plot_interval() [t, -5, 5] """ t = _symbol(parameter) return [t, -5, 5] def equation(self, x='x', y='y'): """The equation of the line: ax + by + c. Parameters ---------- x : str, optional The name to use for the x-axis, default value is 'x'. y : str, optional The name to use for the y-axis, default value is 'y'. Returns ------- equation : sympy expression Examples -------- >>> from sympy import Point, Line >>> p1, p2 = Point(1, 0), Point(5, 3) >>> l1 = Line(p1, p2) >>> l1.equation() -3*x + 4*y + 3 """ x, y = _symbol(x), _symbol(y) p1, p2 = self.points if p1[0] == p2[0]: return x - p1[0] elif p1[1] == p2[1]: return y - p1[1] a, b, c = self.coefficients return simplify(a*x + b*y + c) def __contains__(self, o): """Return True if o is on this Line, or False otherwise.""" if isinstance(o, Point): return Point.is_collinear(self.p1, self.p2, o) elif not isinstance(o, LinearEntity): return False elif isinstance(o, Line): return self.__eq__(o) elif not self.is_similar(o): return False else: return o[0] in self and o[1] in self def __eq__(self, other): """Return True if other is equal to this Line, or False otherwise.""" if not isinstance(other, Line): return False return Point.is_collinear(self.p1, self.p2, other.p1, other.p2) def __hash__(self): return super(Line, self).__hash__() class Ray(LinearEntity): """A Ray is a semi-line in the space with a source point and a direction. Paramaters ---------- p1 : Point The source of the Ray p2 : Point or radian value This point determines the direction in which the Ray propagates. If given as an angle it is interpreted in radians with the positive direction being ccw. Attributes ---------- source xdirection ydirection See Also -------- Point Notes ----- At the moment only rays in a 2D space can be declared, because Points can be defined only for 2D spaces. Examples -------- >>> import sympy >>> from sympy import Point, pi >>> from sympy.abc import r >>> from sympy.geometry import Ray >>> r = Ray(Point(2, 3), Point(3, 5)) >>> r = Ray(Point(2, 3), Point(3, 5)) >>> r Ray(Point(2, 3), Point(3, 5)) >>> r.points (Point(2, 3), Point(3, 5)) >>> r.source Point(2, 3) >>> r.xdirection oo >>> r.ydirection oo >>> r.slope 2 >>> Ray(Point(0, 0), angle=pi/4).slope 1 """ def __new__(cls, p1, pt=None, angle=None, **kwargs): p1 = Point(p1) if pt and angle is None: try: p2 = Point(pt) except NotImplementedError: raise ValueError('The 2nd argument was not a valid Point;\nif it was meant to be an angle it should be given with keyword "angle".') if p1 == p2: raise ValueError('A Ray requires two distinct points.') elif angle is not None and pt is None: # we need to know if the angle is an odd multiple of pi/2 c = pi_coeff(sympify(angle)) p2 = None if c is not None: if c.is_Rational: if c.q == 2: if c.p == 1: p2 = p1 + Point(0, 1) elif c.p == 3: p2 = p1 + Point(0, -1) elif c.q == 1: if c.p == 0: p2 = p1 + Point(1, 0) elif c.p == 1: p2 = p1 + Point(-1, 0) if p2 is None: c *= S.Pi else: c = angle if not p2: p2 = p1 + Point(1, C.tan(c)) else: raise ValueError('A 2nd point or keyword "angle" must be used.') return LinearEntity.__new__(cls, p1, p2, **kwargs) @property def source(self): """The point from which the ray emanates. Examples -------- >>> from sympy import Point, Ray >>> p1, p2 = Point(0, 0), Point(4, 1) >>> r1 = Ray(p1, p2) >>> r1.source Point(0, 0) """ return self.p1 @property def xdirection(self): """The x direction of the ray. Positive infinity if the ray points in the positive x direction, negative infinity if the ray points in the negative x direction, or 0 if the ray is vertical. Examples -------- >>> from sympy import Point, Ray >>> p1, p2, p3 = Point(0, 0), Point(1, 1), Point(0, -1) >>> r1, r2 = Ray(p1, p2), Ray(p1, p3) >>> r1.xdirection oo >>> r2.xdirection 0 """ if self.p1[0] < self.p2[0]: return S.Infinity elif self.p1[0] == self.p2[0]: return S.Zero else: return S.NegativeInfinity @property def ydirection(self): """The y direction of the ray. Positive infinity if the ray points in the positive y direction, negative infinity if the ray points in the negative y direction, or 0 if the ray is horizontal. Examples -------- >>> from sympy import Point, Ray >>> p1, p2, p3 = Point(0, 0), Point(-1, -1), Point(-1, 0) >>> r1, r2 = Ray(p1, p2), Ray(p1, p3) >>> r1.ydirection -oo >>> r2.ydirection 0 """ if self.p1[1] < self.p2[1]: return S.Infinity elif self.p1[1] == self.p2[1]: return S.Zero else: return S.NegativeInfinity def arbitrary_point(self, parameter='t'): """A parameterized point on the Ray. Parameters ---------- parameter : str, optional The name of the parameter which will be used for the parametric point. The default value is 't'. Returns ------- point : Point Raises ------ ValueError When `parameter` already appears in the Ray's definition. See Also -------- Point Examples -------- >>> from sympy import Ray, Point, Segment, S, simplify, solve >>> from sympy.abc import t >>> r = Ray(Point(0, 0), Point(2, 3)) >>> p = r.arbitrary_point(t) The parameter `t` used in the arbitrary point maps 0 to the origin of the ray and 1 to the end of the ray at infinity (which will show up as NaN). >>> p.subs(t, 0), p.subs(t, 1) (Point(0, 0), Point(oo, oo)) The unit that `t` moves you is based on the spacing of the points used to define the ray. >>> p.subs(t, 1/(S(1) + 1)) # one unit Point(2, 3) >>> p.subs(t, 2/(S(1) + 2)) # two units out Point(4, 6) >>> p.subs(t, S.Half/(S(1) + S.Half)) # half a unit out Point(1, 3/2) If you want to be located a distance of 1 from the origin of the ray, what value of `t` is needed? a) find the unit length and pick t accordingly >>> u = Segment(r[0], p.subs(t, S.Half)).length # S.Half = 1/(1 + 1) >>> want = 1 >>> t_need = want/u >>> p_want = p.subs(t, t_need/(1 + t_need)) >>> simplify(Segment(r[0], p_want).length) 1 b) find the t that makes the length from origin to p equal to 1 >>> l = Segment(r[0], p).length >>> t_need = solve(l**2 - want**2, t) # use the square to remove abs() if it is there >>> t_need = [w for w in t_need if w.n() > 0][0] # take positive t >>> p_want = p.subs(t, t_need) >>> simplify(Segment(r[0], p_want).length) 1 """ t = _symbol(parameter) if t.name in (f.name for f in self.free_symbols): raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) m = self.slope x = simplify(self.p1[0] + t/(1 - t)*(self.p2[0] - self.p1[0])) y = simplify(self.p1[1] + t/(1 - t)*(self.p2[1] - self.p1[1])) return Point(x, y) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the Ray. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- plot_interval : list [parameter, lower_bound, upper_bound] Examples -------- >>> from sympy import Point, Ray, pi >>> r = Ray((0, 0), angle=pi/4) >>> r.plot_interval() [t, 0, 5*2**(1/2)/(1 + 5*2**(1/2))] """ t = _symbol(parameter) p = self.arbitrary_point(t) # get a t corresponding to length of 10 want = 10 u = Segment(self[0], p.subs(t, S.Half)).length # gives unit length t_need = want/u return [t, 0, t_need/(1 + t_need)] def __eq__(self, other): """Is the other GeometryEntity equal to this Ray?""" if not isinstance(other, Ray): return False return (self.source == other.source) and (other.p2 in self) def __hash__(self): return super(Ray, self).__hash__() def __contains__(self, o): """Is other GeometryEntity contained in this Ray?""" if isinstance(o, Ray): d = o.p2 - o.p1 return (Point.is_collinear(self.p1, self.p2, o.p1, o.p2) and (self.xdirection == o.xdirection) and (self.ydirection == o.ydirection)) elif isinstance(o, Segment): return (o.p1 in self) and (o.p2 in self) elif isinstance(o, Point): if Point.is_collinear(self.p1, self.p2, o): if (not self.p1[0].has(C.Symbol) and not self.p1[1].has(C.Symbol) and not self.p2[0].has(C.Symbol) and not self.p2[1].has(C.Symbol)): if self.xdirection is S.Infinity: return o[0] >= self.source[0] elif self.xdirection is S.NegativeInfinity: return o[0] <= self.source[0] elif self.ydirection is S.Infinity: return o[1] >= self.source[1] return o[1] <= self.source[1] else: # There are symbols lying around, so assume that o # is contained in this ray (for now) return True else: # Points are not collinear, so the rays are not parallel # and hence it isimpossible for self to contain o return False # No other known entity can be contained in a Ray return False class Segment(LinearEntity): """An undirected line segment in space. Parameters ---------- p1 : Point p2 : Point Attributes ---------- length : number or sympy expression midpoint : Point See Also -------- Point Notes ----- At the moment only segments in a 2D space can be declared, because Points can be defined only for 2D spaces. Examples -------- >>> import sympy >>> from sympy import Point >>> from sympy.abc import s >>> from sympy.geometry import Segment >>> Segment((1, 0), (1, 1)) # tuples are interpreted as pts Segment(Point(1, 0), Point(1, 1)) >>> s = Segment(Point(4, 3), Point(1, 1)) >>> s Segment(Point(1, 1), Point(4, 3)) >>> s.points (Point(1, 1), Point(4, 3)) >>> s.slope 2/3 >>> s.length 13**(1/2) >>> s.midpoint Point(5/2, 2) """ def __new__(cls, p1, p2, **kwargs): # Reorder the two points under the following ordering: # if p1[0] != p2[0] then p1[0] < p2[0] # if p1[0] == p2[0] then p1[1] < p2[1] p1 = Point(p1) p2 = Point(p2) if p1 == p2: return Point(p1) if p1[0] > p2[0]: p1, p2 = p2, p1 elif p1[0] == p2[0] and p1[1] > p2[0]: p1, p2 = p2, p1 return LinearEntity.__new__(cls, p1, p2, **kwargs) def arbitrary_point(self, parameter='t'): """A parameterized point on the Segment. Parameters ---------- parameter : str, optional The name of the parameter which will be used for the parametric point. The default value is 't'. Returns ------- point : Point Parameters ---------- parameter : str, optional The name of the parameter which will be used for the parametric point. The default value is 't'. Returns ------- point : Point Raises ------ ValueError When `parameter` already appears in the Segment's definition. See Also -------- Point Examples -------- >>> from sympy import Point, Segment >>> p1, p2 = Point(1, 0), Point(5, 3) >>> s1 = Segment(p1, p2) >>> s1.arbitrary_point() Point(4*t + 1, 3*t) """ t = _symbol(parameter) if t.name in (f.name for f in self.free_symbols): raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % t.name) x = simplify(self.p1[0] + t*(self.p2[0] - self.p1[0])) y = simplify(self.p1[1] + t*(self.p2[1] - self.p1[1])) return Point(x, y) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the Segment. Parameters ---------- parameter : str, optional Default value is 't'. Returns ------- plot_interval : list [parameter, lower_bound, upper_bound] Examples -------- >>> from sympy import Point, Segment >>> p1, p2 = Point(0, 0), Point(5, 3) >>> s1 = Segment(p1, p2) >>> s1.plot_interval() [t, 0, 1] """ t = _symbol(parameter) return [t, 0, 1] def perpendicular_bisector(self, p=None): """The perpendicular bisector of this segment. If no point is specified or the point specified is not on the bisector then the bisector is returned as a Line. Otherwise a Segment is returned that joins the point specified and the intersection of the bisector and the segment. Parameters ---------- p : Point Returns ------- bisector : Line or Segment Examples -------- >>> from sympy import Point, Segment >>> p1, p2, p3 = Point(0, 0), Point(6, 6), Point(5, 1) >>> s1 = Segment(p1, p2) >>> s1.perpendicular_bisector() Line(Point(3, 3), Point(9, -3)) >>> s1.perpendicular_bisector(p3) Segment(Point(3, 3), Point(5, 1)) """ l = LinearEntity.perpendicular_line(self, self.midpoint) if p is None or p not in l: return l else: return Segment(self.midpoint, p) @property def length(self): """The length of the line segment. Examples -------- >>> from sympy import Point, Segment >>> p1, p2 = Point(0, 0), Point(4, 3) >>> s1 = Segment(p1, p2) >>> s1.length 5 """ return Point.distance(self.p1, self.p2) @property def midpoint(self): """The midpoint of the line segment. Examples -------- >>> from sympy import Point, Segment >>> p1, p2 = Point(0, 0), Point(4, 3) >>> s1 = Segment(p1, p2) >>> s1.midpoint Point(2, 3/2) """ return Point.midpoint(self.p1, self.p2) def distance(self, o): """Attempts to find the distance of the line segment to an object""" if isinstance(o, Point): return self._do_point_distance(o) raise NotImplementedError() def _do_point_distance(self, pt): """Calculates the distance between a point and a line segment""" seg_vector = Point(self.p2[0] - self.p1[0], self.p2[1] - self.p1[1]) pt_vector = Point(pt[0] - self.p1[0], pt[1] - self.p1[1]) t = (seg_vector[0]*pt_vector[0] + seg_vector[1]*pt_vector[1])/self.length**2 if t >= 1: distance = Point.distance(self.p2, pt) elif t <= 0: distance = Point.distance(self.p1, pt) else: distance = Point.distance(self.p1 + Point(t*seg_vector[0], t*seg_vector[1]), pt) return distance def __eq__(self, other): """Is the other GeometryEntity equal to this Ray?""" if not isinstance(other, Segment): return False return (self.p1 == other.p1) and (self.p2 == other.p2) def __hash__(self): return super(Segment, self).__hash__() def __contains__(self, o): """Is the other GeometryEntity contained within this Ray?""" if isinstance(o, Segment): return o.p1 in self and o.p2 in self elif isinstance(o, Point): if Point.is_collinear(self.p1, self.p2, o): t = Dummy('t') x, y = self.arbitrary_point(t) if self.p1.x != self.p2.x: ti = solve(x - o.x, t)[0] else: ti = solve(y - o.y, t)[0] if ti.is_number: return 0 <= ti <= 1 return None # No other known entity can be contained in a Ray return False wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/entity.py0000644000175000017500000002114712014170666024625 0ustar georgeskgeorgesk"""The definition of the base geometrical entity with attributes common to all derived geometrical entities. Contains -------- GeometryEntity """ from sympy.core.compatibility import cmp # How entities are ordered; used by __cmp__ in GeometryEntity ordering_of_classes = [ "Point", "Segment", "Ray", "Line", "Triangle", "RegularPolygon", "Polygon", "Circle", "Ellipse", "Curve" ] class GeometryEntity(tuple): """The base class for all geometrical entities. This class doesn't represent any particular geometric entity, it only provides the implementation of some methods common to all subclasses. """ def __new__(cls, *args, **kwargs): return tuple.__new__(cls, args) def __getnewargs__(self): return tuple(self) @property def free_symbols(self): free = set() for a in self.args: free |= a.free_symbols return free def intersection(self, o): """ Returns a list of all of the intersections of self with o. Notes ----- An entity is not required to implement this method. If two different types of entities can intersect, the item with higher index in ordering_of_classes should implement intersections with anything having a lower index. See Also -------- intersection function in geometry/util.py which computes the intersection between more than 2 objects. """ raise NotImplementedError() def rotate(self, angle, pt=None): """Rotate the object about pt by the given angle (in radians). The default pt is the origin, Point(0, 0) XXX geometry needs a modify_points method which operates on only the points of the object >>> from sympy import Point, RegularPolygon, Polygon, pi >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) >>> t # vertex on x axis Triangle(Point(1, 0), Point(-1/2, 3**(1/2)/2), Point(-1/2, -3**(1/2)/2)) >>> t.rotate(pi/2) # vertex on y axis now Triangle(Point(0, 1), Point(-3**(1/2)/2, -1/2), Point(3**(1/2)/2, -1/2)) """ from sympy import cos, sin, Point c = cos(angle) s = sin(angle) if isinstance(self, Point): rv = self if pt is not None: rv -= pt x, y = rv rv = Point(c*x-s*y, s*x+c*y) if pt is not None: rv += pt return rv newargs = [] for a in self.args: if isinstance(a, GeometryEntity): newargs.append(a.rotate(angle, pt)) else: newargs.append(a) return type(self)(*newargs) def scale(self, x=1, y=1): """Scale the object by multiplying the x,y-coordinates by x and y. >>> from sympy import RegularPolygon, Point, Polygon >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) >>> t Triangle(Point(1, 0), Point(-1/2, 3**(1/2)/2), Point(-1/2, -3**(1/2)/2)) >>> t.scale(2) Triangle(Point(2, 0), Point(-1, 3**(1/2)/2), Point(-1, -3**(1/2)/2)) >>> t.scale(2,2) Triangle(Point(2, 0), Point(-1, 3**(1/2)), Point(-1, -3**(1/2))) """ from sympy import Point if isinstance(self, Point): return Point(self[0]*x, self[1]*y) newargs = [] for a in self.args: if isinstance(a, GeometryEntity): newargs.append(a.scale(x, y)) else: newargs.append(a) return type(self)(*newargs) def translate(self, x=0, y=0): """Shift the object by adding to the x,y-coordinates the values x and y. >>> from sympy import RegularPolygon, Point, Polygon >>> t = Polygon(*RegularPolygon(Point(0, 0), 1, 3).vertices) >>> t Triangle(Point(1, 0), Point(-1/2, 3**(1/2)/2), Point(-1/2, -3**(1/2)/2)) >>> t.translate(2) Triangle(Point(3, 0), Point(3/2, 3**(1/2)/2), Point(3/2, -3**(1/2)/2)) >>> t.translate(2,2) Triangle(Point(3, 2), Point(3/2, 3**(1/2)/2 + 2), Point(3/2, -3**(1/2)/2 + 2)) """ from sympy import Point if not isinstance(x, Point): pt = Point(x, y) else: pt = x if isinstance(self, Point): return self + pt newargs = [] for a in self.args: if isinstance(a, GeometryEntity): newargs.append(a.translate(pt)) else: newargs.append(a) return type(self)(*newargs) def encloses(self, o): """ Return True if o is inside (not on or outside) the boundaries of self. The object will be decomposed into Points and individual Entities need only define an encloses_point method for their class. """ from sympy.geometry.point import Point from sympy.geometry.line import Segment, Ray, Line from sympy.geometry.ellipse import Circle, Ellipse from sympy.geometry.polygon import Polygon, RegularPolygon if isinstance(o, Point): return self.encloses_point(o) elif isinstance(o, Segment): return all(self.encloses_point(x) for x in o.points) elif isinstance(o, Ray) or isinstance(o, Line): return False elif isinstance(o, Ellipse): return self.encloses_point(o.center) and not self.intersection(o) elif isinstance(o, Polygon): if isinstance(o, RegularPolygon): if not self.encloses_point(o.center): return False return all(self.encloses_point(v) for v in o.vertices) raise NotImplementedError() def is_similar(self, other): """Is this geometrical entity similar to another geometrical entity? Two entities are similar if a uniform scaling (enlarging or shrinking) of one of the entities will allow one to obtain the other. Notes ----- This method is not intended to be used directly but rather through the `are_similar` function found in util.py. An entity is not required to implement this method. If two different types of entities can be similar, it is only required that one of them be able to determine this. """ raise NotImplementedError() def subs(self, old, new): if hasattr(self, '_eval_subs_'): return self.subs(old, new) elif isinstance(self, GeometryEntity): return type(self)(*[a.subs(old, new) for a in self.args]) else: return self @property def args(self): """Return whatever is contained in the object's tuple. The contents will not necessarily be Points. This is also what will be returned when one does "for x in self". """ return tuple(self) def __ne__(self, o): """Test inequality of two geometrical entities.""" return not self.__eq__(o) def __radd__(self, a): return a.__add__(self) def __rsub__(self, a): return a.__sub__(self) def __rmul__(self, a): return a.__mul__(self) def __rdiv__(self, a): return a.__div__(self) def __str__(self): """String representation of a GeometryEntity.""" from sympy.printing import sstr return type(self).__name__ + sstr(tuple(self)) def __repr__(self): """String representation of a GeometryEntity that can be evaluated by sympy.""" return type(self).__name__ + repr(tuple(self)) def __cmp__(self, other): """Comparison of two GeometryEntities.""" n1 = self.__class__.__name__ n2 = other.__class__.__name__ c = cmp(n1, n2) if not c: return 0 i1 = -1 for cls in self.__class__.__mro__: try: i1 = ordering_of_classes.index(cls.__name__) break except ValueError: i1 = -1 if i1 == -1: return c i2 = -1 for cls in other.__class__.__mro__: try: i2 = ordering_of_classes.index(cls.__name__) break except ValueError: i2 = -1 if i2 == -1: return c return cmp(i1, i2) def __contains__(self, other): """Subclasses should implement this method for anything more complex than equality.""" if type(self) == type(other): return self == other raise NotImplementedError() from sympy.core.sympify import converter converter[GeometryEntity] = lambda x: x wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/curve.py0000644000175000017500000001235112014170666024432 0ustar georgeskgeorgesk"""Curves in 2-dimensional Euclidean space. Contains -------- Curve """ from sympy.core import sympify, C, Symbol from sympy.core.compatibility import is_sequence from sympy.geometry.exceptions import GeometryError from sympy.geometry.point import Point from entity import GeometryEntity from util import _symbol class Curve(GeometryEntity): """A curve in space. A curve is defined by parametric functions for the coordinates, a parameter and the lower and upper bounds for the parameter value. Parameters ---------- function : list of functions limits : 3-tuple Function parameter and lower and upper bounds. Attributes ---------- functions parameter limits Raises ------ ValueError When `functions` are specified incorrectly. When `limits` are specified incorrectly. Examples -------- >>> from sympy import sin, cos, Symbol >>> from sympy.abc import t >>> from sympy.geometry import Curve >>> C = Curve((sin(t), cos(t)), (t, 0, 2)) >>> C.functions (sin(t), cos(t)) >>> C.limits (t, 0, 2) >>> C.parameter t """ def __new__(cls, function, limits): fun = sympify(function) if not is_sequence(fun) or len(fun) != 2: raise ValueError("Function argument should be (x(t), y(t)) but got %s" % str(function)) if not is_sequence(limits) or len(limits) != 3: raise ValueError("Limit argument should be (t, tmin, tmax) but got %s" % str(limits)) return GeometryEntity.__new__(cls, tuple(sympify(fun)), tuple(sympify(limits))) @property def free_symbols(self): free = set() for a in self.functions + self.limits[1:]: free |= a.free_symbols free = free.difference(set([self.parameter])) return free @property def functions(self): """The functions specifying the curve. Returns ------- functions : list of parameterized coordinate functions. Examples -------- >>> from sympy.abc import t >>> from sympy.geometry import Curve >>> C = Curve((t, t**2), (t, 0, 2)) >>> C.functions (t, t**2) """ return self.__getitem__(0) @property def parameter(self): """The curve function variable. Returns ------- parameter : sympy symbol Examples -------- >>> from sympy.abc import t >>> from sympy.geometry import Curve >>> C = Curve([t, t**2], (t, 0, 2)) >>> C.parameter t """ return self.__getitem__(1)[0] @property def limits(self): """The limits for the curve. Returns ------- limits : tuple Contains parameter and lower and upper limits. Examples -------- >>> from sympy.abc import t >>> from sympy.geometry import Curve >>> C = Curve([t, t**3], (t, -2, 2)) >>> C.limits (t, -2, 2) """ return self.__getitem__(1) def arbitrary_point(self, parameter='t'): """ A parameterized point on the curve. Parameters ---------- parameter : str or Symbol, optional Default value is 't'; the Curve's parameter is selected with None or self.parameter otherwise the provided symbol is used. Returns ------- arbitrary_point : Point Raises ------ ValueError When `parameter` already appears in the functions. See Also -------- Point Examples -------- >>> from sympy import Symbol >>> from sympy.abc import s >>> from sympy.geometry import Curve >>> C = Curve([2*s, s**2], (s, 0, 2)) >>> C.arbitrary_point() Point(2*t, t**2) >>> C.arbitrary_point(C.parameter) Point(2*s, s**2) >>> C.arbitrary_point(None) Point(2*s, s**2) >>> C.arbitrary_point(Symbol('a')) Point(2*a, a**2) """ if parameter is None: return Point(*self.functions) tnew = _symbol(parameter, self.parameter) t = self.parameter if tnew.name != t.name and tnew.name in (f.name for f in self.free_symbols): raise ValueError('Symbol %s already appears in object and cannot be used as a parameter.' % tnew.name) return Point(*[w.subs(t, tnew) for w in self.functions]) def plot_interval(self, parameter='t'): """The plot interval for the default geometric plot of the curve. Parameters ---------- parameter : str or Symbol, optional Default value is 't'; otherwise the provided symbol is used. Returns ------- plot_interval : list (plot interval) [parameter, lower_bound, upper_bound] Examples -------- >>> from sympy import Curve, sin >>> from sympy.abc import x, t, s >>> Curve((x, sin(x)), (x, 1, 2)).plot_interval() [t, 1, 2] >>> Curve((x, sin(x)), (x, 1, 2)).plot_interval(s) [s, 1, 2] """ t = _symbol(parameter, self.parameter) return [t] + list(self.limits[1:]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/tests/0000755000175000017500000000000012014170666024074 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/tests/test_geometry.py0000644000175000017500000006045312014170666027350 0ustar georgeskgeorgeskfrom sympy import (Abs, C, Dummy, Max, Min, Rational, Float, S, Symbol, cos, oo, pi, simplify, sqrt, symbols) from sympy.geometry import (Circle, Curve, Ellipse, GeometryError, Line, Point, Polygon, Ray, RegularPolygon, Segment, Triangle, are_similar, convex_hull, intersection) from sympy.utilities.pytest import raises, XFAIL x = Symbol('x', real=True) y = Symbol('y', real=True) t = Symbol('t', real=True) x1 = Symbol('x1', real=True) x2 = Symbol('x2', real=True) y1 = Symbol('y1', real=True) y2 = Symbol('y2', real=True) half = Rational(1,2) def feq(a, b): """Test if two floating point values are 'equal'.""" t = Float("1.0E-10") return -t < a-b < t def test_curve(): s = Symbol('s') z = Symbol('z') # this curve is independent of the indicated parameter C = Curve([2*s, s**2], (z, 0, 2)) assert C.parameter == z assert C.functions == (2*s, s**2) assert C.arbitrary_point() == Point(2*s, s**2) assert C.arbitrary_point(z) == Point(2*s, s**2) # this is how it is normally used C = Curve([2*s, s**2], (s, 0, 2)) assert C.parameter == s assert C.functions == (2*s, s**2) t = Symbol('t') assert C.arbitrary_point() != Point(2*t, t**2) # the t returned as assumptions t = Symbol('t', real=True) # now t has the same assumptions so the test passes assert C.arbitrary_point() == Point(2*t, t**2) assert C.arbitrary_point(z) == Point(2*z, z**2) assert C.arbitrary_point(C.parameter) == Point(2*s, s**2) raises(ValueError, 'Curve((s, s + t), (s, 1, 2)).arbitrary_point()') raises(ValueError, 'Curve((s, s + t), (t, 1, 2)).arbitrary_point(s)') def test_point(): p1 = Point(x1, x2) p2 = Point(y1, y2) p3 = Point(0, 0) p4 = Point(1, 1) assert len(p1) == 1 assert p1 in p1 assert p1 not in p2 assert p2[1] == y2 assert (p3+p4) == p4 assert (p2-p1) == Point(y1-x1, y2-x2) assert p4*5 == Point(5, 5) assert -p2 == Point(-y1, -y2) assert Point.midpoint(p3, p4) == Point(half, half) assert Point.midpoint(p1, p4) == Point(half + half*x1, half + half*x2) assert Point.midpoint(p2, p2) == p2 assert p2.midpoint(p2) == p2 assert Point.distance(p3, p4) == sqrt(2) assert Point.distance(p1, p1) == 0 assert Point.distance(p3, p2) == sqrt(p2.x**2 + p2.y**2) p1_1 = Point(x1, x1) p1_2 = Point(y2, y2) p1_3 = Point(x1 + 1, x1) assert Point.is_collinear(p3) assert Point.is_collinear(p3, p4) assert Point.is_collinear(p3, p4, p1_1, p1_2) assert Point.is_collinear(p3, p4, p1_1, p1_3) == False x_pos = Symbol('x', real=True, positive=True) p2_1 = Point(x_pos, 0) p2_2 = Point(0, x_pos) p2_3 = Point(-x_pos, 0) p2_4 = Point(0, -x_pos) p2_5 = Point(x_pos, 5) assert Point.is_concyclic(p2_1) assert Point.is_concyclic(p2_1, p2_2) assert Point.is_concyclic(p2_1, p2_2, p2_3, p2_4) assert Point.is_concyclic(p2_1, p2_2, p2_3, p2_5) == False def test_line(): p1 = Point(0, 0) p2 = Point(1, 1) p3 = Point(x1, x1) p4 = Point(y1, y1) p5 = Point(x1, 1 + x1) p6 = Point(1, 0) p7 = Point(0, 1) p8 = Point(2, 0) p9 = Point(2, 1) l1 = Line(p1, p2) l2 = Line(p3, p4) l3 = Line(p3, p5) l4 = Line(p1, p6) l5 = Line(p1, p7) l6 = Line(p8, p9) l7 = Line(p2, p9) # Basic stuff assert Line((1, 1), slope=1) == Line((1, 1), (2, 2)) assert Line((1, 1), slope=oo) == Line((1, 1), (1, 2)) assert Line((1, 1), slope=-oo) == Line((1, 1), (1, 2)) raises(ValueError, 'Line((1, 1), 1)') assert Line(p1, p2) == Line(p2, p1) assert l1 == l2 assert l1 != l3 assert l1.slope == 1 assert l3.slope == oo assert l4.slope == 0 assert l4.coefficients == (0, 1, 0) assert l4.equation(x=x, y=y) == y assert l5.slope == oo assert l5.coefficients == (1, 0, 0) assert l5.equation() == x assert l6.equation() == x - 2 assert l7.equation() == y - 1 assert p1 in l1 # is p1 on the line l1? assert p1 not in l3 assert simplify(l1.equation()) in (x-y, y-x) assert simplify(l3.equation()) in (x-x1, x1-x) assert l2.arbitrary_point() in l2 for ind in xrange(0, 5): assert l3.random_point() in l3 # Orthogonality p1_1 = Point(-x1, x1) l1_1 = Line(p1, p1_1) assert l1.perpendicular_line(p1) == l1_1 assert Line.is_perpendicular(l1, l1_1) assert Line.is_perpendicular(l1 , l2) == False # Parallelity p2_1 = Point(-2*x1, 0) l2_1 = Line(p3, p5) assert l2.parallel_line(p1_1) == Line(p2_1, p1_1) assert l2_1.parallel_line(p1) == Line(p1, Point(0, 2)) assert Line.is_parallel(l1, l2) assert Line.is_parallel(l2, l3) == False assert Line.is_parallel(l2, l2.parallel_line(p1_1)) assert Line.is_parallel(l2_1, l2_1.parallel_line(p1)) # Intersection assert intersection(l1, p1) == [p1] assert intersection(l1, p5) == [] assert intersection(l1, l2) in [[l1], [l2]] assert intersection(l1, l1.parallel_line(p5)) == [] # Concurrency l3_1 = Line(Point(5, x1), Point(-Rational(3,5), x1)) assert Line.is_concurrent(l1, l3) assert Line.is_concurrent(l1, l3, l3_1) assert Line.is_concurrent(l1, l1_1, l3) == False # Projection assert l2.projection(p4) == p4 assert l1.projection(p1_1) == p1 assert l3.projection(p2) == Point(x1, 1) # Finding angles l1_1 = Line(p1, Point(5, 0)) assert feq(Line.angle_between(l1, l1_1).evalf(), pi.evalf()/4) # Testing Rays and Segments (very similar to Lines) assert Ray((1, 1), angle=pi/4) == Ray((1, 1), (2, 2)) assert Ray((1, 1), angle=pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=-pi/2) == Ray((1, 1), (1, 0)) assert Ray((1, 1), angle=-3*pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=5*pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=5.0*pi/2) == Ray((1, 1), (1, 2)) assert Ray((1, 1), angle=pi) == Ray((1, 1), (0, 1)) assert Ray((1, 1), angle=3.0*pi) == Ray((1, 1), (0, 1)) assert Ray((1, 1), angle=4.0*pi) == Ray((1, 1), (2, 1)) assert Ray((1, 1), angle=0) == Ray((1, 1), (2, 1)) # XXX don't know why this fails without str assert str(Ray((1, 1), angle=4.2*pi)) == str(Ray(Point(1, 1), Point(2, 1 + C.tan(0.2*pi)))) assert Ray((1, 1), angle=5) == Ray((1, 1), (2, 1 + C.tan(5))) raises(ValueError, 'Ray((1, 1), 1)') r1 = Ray(p1, Point(-1, 5)) r2 = Ray(p1, Point(-1, 1)) r3 = Ray(p3, p5) assert l1.projection(r1) == Ray(p1, p2) assert l1.projection(r2) == p1 assert r3 != r1 t = Symbol('t', real=True) assert Ray((1, 1), angle=pi/4).arbitrary_point() == Point(1/(1 - t), 1/(1 - t)) s1 = Segment(p1, p2) s2 = Segment(p1, p1_1) assert s1.midpoint == Point(Rational(1,2), Rational(1,2)) assert s2.length == sqrt( 2*(x1**2) ) assert s1.perpendicular_bisector() == Line(Point(0, 1), Point(1, 0)) assert Segment((1, 1), (2, 3)).arbitrary_point() == Point(1 + t, 1 + 2*t) # Segment contains a, b = symbols('a,b') s = Segment((0, a), (0, b)) assert Point(0, (a + b)/2) in s s = Segment((a, 0), (b, 0)) assert Point((a + b)/2, 0) in s assert (Point(2*a, 0) in s) is False # XXX should be None? # Testing distance from a Segment to an object s1 = Segment(Point(0, 0), Point(1, 1)) s2 = Segment(Point(half, half), Point(1, 0)) pt1 = Point(0, 0) pt2 = Point(Rational(3)/2, Rational(3)/2) assert s1.distance(pt1) == 0 assert s2.distance(pt1) == 2**(half)/2 assert s2.distance(pt2) == 2**(half) # Special cases of projection and intersection r1 = Ray(Point(1, 1), Point(2, 2)) r2 = Ray(Point(2, 2), Point(0, 0)) r3 = Ray(Point(1, 1), Point(-1, -1)) r4 = Ray(Point(0, 4), Point(-1, -5)) assert intersection(r1, r2) == [Segment(Point(1, 1), Point(2, 2))] assert intersection(r1, r3) == [Point(1, 1)] assert r1.projection(r3) == Point(1, 1) assert r1.projection(r4) == Segment(Point(1, 1), Point(2, 2)) r5 = Ray(Point(0, 0), Point(0, 1)) r6 = Ray(Point(0, 0), Point(0, 2)) assert r5 in r6 assert r6 in r5 s1 = Segment(Point(0, 0), Point(2, 2)) s2 = Segment(Point(-1, 5), Point(-5, -10)) s3 = Segment(Point(0, 4), Point(-2, 2)) assert intersection(r1, s1) == [Segment(Point(1, 1), Point(2, 2))] assert r1.projection(s2) == Segment(Point(1, 1), Point(2, 2)) assert s3.projection(r1) == Segment(Point(0, 4), Point(-1, 3)) l1 = Line(Point(0, 0), Point(3, 4)) r1 = Ray(Point(0, 0), Point(3, 4)) s1 = Segment(Point(0, 0), Point(3, 4)) assert intersection(l1, l1) == [l1] assert intersection(l1, r1) == [r1] assert intersection(l1, s1) == [s1] assert intersection(r1, l1) == [r1] assert intersection(s1, l1) == [s1] entity1 = Segment(Point(-10,10), Point(10,10)) entity2 = Segment(Point(-5,-5), Point(-5,5)) assert intersection(entity1, entity2) == [] def test_ellipse(): p1 = Point(0, 0) p2 = Point(1, 1) p3 = Point(x1, x2) p4 = Point(0, 1) p5 = Point(-1, 0) e1 = Ellipse(p1, 1, 1) e2 = Ellipse(p2, half, 1) e3 = Ellipse(p1, y1, y1) c1 = Circle(p1, 1) c2 = Circle(p2,1) c3 = Circle(Point(sqrt(2),sqrt(2)),1) # Test creation with three points cen, rad = Point(3*half, 2), 5*half assert Circle(Point(0,0), Point(3,0), Point(0,4)) == Circle(cen, rad) raises(GeometryError, "Circle(Point(0,0), Point(1,1), Point(2,2))") # Basic Stuff assert e1 == c1 assert e1 != e2 assert p4 in e1 assert p2 not in e2 assert e1.area == pi assert e2.area == pi/2 assert e3.area == pi*(y1**2) assert c1.area == e1.area assert c1.circumference == e1.circumference assert e3.circumference == 2*pi*y1 # with generic symbols, the hradius is assumed to contain the major radius M = Symbol('M') m = Symbol('m') c = Ellipse(p1, M, m).circumference _x = c.atoms(Dummy).pop() assert c == \ 4*M*C.Integral(sqrt((1 - _x**2*(M**2 - m**2)/M**2)/(1 - _x**2)), (_x, 0, 1)) assert e2.arbitrary_point() in e2 # Foci f1, f2 = Point(sqrt(12), 0), Point(-sqrt(12), 0) ef = Ellipse(Point(0, 0), 4, 2) assert ef.foci in [(f1, f2), (f2, f1)] # Tangents v = sqrt(2) / 2 p1_1 = Point(v, v) p1_2 = p2 + Point(half, 0) p1_3 = p2 + Point(0, 1) assert e1.tangent_lines(p4) == c1.tangent_lines(p4) assert e2.tangent_lines(p1_2) == [Line(p1_2, p2 + Point(half, 1))] assert e2.tangent_lines(p1_3) == [Line(p1_3, p2 + Point(half, 1))] assert c1.tangent_lines(p1_1) == [Line(p1_1, Point(0, sqrt(2)))] assert e2.is_tangent(Line(p1_2, p2 + Point(half, 1))) assert e2.is_tangent(Line(p1_3, p2 + Point(half, 1))) assert c1.is_tangent(Line(p1_1, Point(0, sqrt(2)))) assert e1.is_tangent(Line(Point(0, 0), Point(1, 1))) == False assert Ellipse(Point(5, 5), 2, 1).tangent_lines(Point(0, 0)) == \ [Line(Point(0, 0), Point(S(77)/25, S(132)/25)), Line(Point(0, 0), Point(S(33)/5, S(22)/5))] assert Ellipse(Point(5, 5), 2, 1).tangent_lines(Point(3, 4)) == \ [Line(Point(3, 4), Point(3, 5)), Line(Point(3, 4), Point(5, 4))] assert Circle(Point(5, 5), 2).tangent_lines(Point(3, 3)) == \ [Line(Point(3, 3), Point(3, 5)), Line(Point(3, 3), Point(5, 3))] assert Circle(Point(5, 5), 2).tangent_lines(Point(5 - 2*sqrt(2), 5)) == \ [Line(Point(5 - 2*sqrt(2), 5), Point(5 - sqrt(2), 5 - sqrt(2))), Line(Point(5 - 2*sqrt(2), 5), Point(5 - sqrt(2), 5 + sqrt(2))),] # Properties major = 3 minor = 1 e4 = Ellipse(p2, minor, major) assert e4.focus_distance == sqrt(major**2 - minor**2) ecc = e4.focus_distance / major assert e4.eccentricity == ecc assert e4.periapsis == major*(1 - ecc) assert e4.apoapsis == major*(1 + ecc) # independent of orientation e4 = Ellipse(p2, major, minor) assert e4.focus_distance == sqrt(major**2 - minor**2) ecc = e4.focus_distance / major assert e4.eccentricity == ecc assert e4.periapsis == major*(1 - ecc) assert e4.apoapsis == major*(1 + ecc) # Intersection l1 = Line(Point(1, -5), Point(1, 5)) l2 = Line(Point(-5, -1), Point(5, -1)) l3 = Line(Point(-1, -1), Point(1, 1)) l4 = Line(Point(-10, 0), Point(0, 10)) pts_c1_l3 = [Point(sqrt(2)/2, sqrt(2)/2), Point(-sqrt(2)/2, -sqrt(2)/2)] assert intersection(e2, l4) == [] assert intersection(c1, Point(1, 0)) == [Point(1, 0)] assert intersection(c1, l1) == [Point(1, 0)] assert intersection(c1, l2) == [Point(0, -1)] assert intersection(c1, l3) in [pts_c1_l3, [pts_c1_l3[1], pts_c1_l3[0]]] assert intersection(c1, c2) in [[(1,0), (0,1)],[(0,1),(1,0)]] assert intersection(c1, c3) == [(sqrt(2)/2, sqrt(2)/2)] # some special case intersections csmall = Circle(p1, 3) cbig = Circle(p1, 5) cout = Circle(Point(5, 5), 1) # one circle inside of another assert csmall.intersection(cbig) == [] # separate circles assert csmall.intersection(cout) == [] # coincident circles assert csmall.intersection(csmall) == csmall v = sqrt(2) t1 = Triangle(Point(0, v), Point(0, -v), Point(v, 0)) points = intersection(t1, c1) assert len(points) == 4 assert Point(0, 1) in points assert Point(0, -1) in points assert Point(v/2, v/2) in points assert Point(v/2, -v/2) in points circ = Circle(Point(0, 0), 5) elip = Ellipse(Point(0, 0), 5, 20) assert intersection(circ, elip) in \ [[Point(5, 0), Point(-5, 0)], [Point(-5, 0), Point(5, 0)]] assert elip.tangent_lines(Point(0, 0)) == [] elip = Ellipse(Point(0, 0), 3, 2) assert elip.tangent_lines(Point(3, 0)) == [Line(Point(3, 0), Point(3, -12))] e1 = Ellipse(Point(0, 0), 5, 10) e2 = Ellipse(Point(2, 1), 4, 8) a = S(53)/17 c = 2*sqrt(3991)/17 assert e1.intersection(e2) == [Point(a - c/8, a/2 + c), Point(a + c/8, a/2 - c)] # Combinations of above assert e3.is_tangent(e3.tangent_lines(p1 + Point(y1, 0))[0]) e = Ellipse((1, 2), 3, 2) assert e.tangent_lines(Point(10, 0)) == \ [Line(Point(10, 0), Point(1, 0)), Line(Point(10, 0), Point(S(14)/5, S(18)/5))] # encloses_point e = Ellipse((0, 0), 1, 2) assert e.encloses_point(e.center) assert e.encloses_point(e.center + Point(0, e.vradius - Rational(1, 10))) assert e.encloses_point(e.center + Point(e.hradius - Rational(1, 10), 0)) assert e.encloses_point(e.center + Point(e.hradius, 0)) is False assert e.encloses_point(e.center + Point(e.hradius + Rational(1, 10), 0)) is False e = Ellipse((0, 0), 2, 1) assert e.encloses_point(e.center) assert e.encloses_point(e.center + Point(0, e.vradius - Rational(1, 10))) assert e.encloses_point(e.center + Point(e.hradius - Rational(1, 10), 0)) assert e.encloses_point(e.center + Point(e.hradius, 0)) is False assert e.encloses_point(e.center + Point(e.hradius + Rational(1, 10), 0)) is False def test_ellipse_random_point(): e3 = Ellipse(Point(0, 0), y1, y1) rx, ry = Symbol('rx'), Symbol('ry') for ind in xrange(0, 5): r = e3.random_point() # substitution should give zero*y1**2 assert e3.equation(rx, ry).subs(zip((rx, ry), r.args) ).n(3).as_coeff_Mul()[0] < 1e-10 def test_polygon(): t = Triangle(Point(0, 0), Point(2, 0), Point(3, 3)) assert Polygon(Point(0, 0), Point(1, 0), Point(2, 0), Point(3, 3)) == t assert Polygon(Point(1, 0), Point(2, 0), Point(3, 3), Point(0, 0)) == t assert Polygon(Point(2, 0), Point(3, 3), Point(0, 0), Point(1, 0)) == t p1 = Polygon( Point(0, 0), Point(3,-1), Point(6, 0), Point(4, 5), Point(2, 3), Point(0, 3)) p2 = Polygon( Point(6, 0), Point(3,-1), Point(0, 0), Point(0, 3), Point(2, 3), Point(4, 5)) p3 = Polygon( Point(0, 0), Point(3, 0), Point(5, 2), Point(4, 4)) p4 = Polygon( Point(0, 0), Point(4, 4), Point(5, 2), Point(3, 0)) # # General polygon # assert p1 == p2 assert len(p1) == 6 assert len(p1.sides) == 6 assert p1.perimeter == 5+2*sqrt(10)+sqrt(29)+sqrt(8) assert p1.area == 22 assert not p1.is_convex() assert p3.is_convex() assert p4.is_convex() # ensure convex for both CW and CCW point specification # # Regular polygon # p1 = RegularPolygon(Point(0, 0), 10, 5) p2 = RegularPolygon(Point(0, 0), 5, 5) assert p1 != p2 assert p1.interior_angle == 3*pi/5 assert p1.exterior_angle == 2*pi/5 assert p2.apothem == 5*cos(pi/5) assert p2.circumcircle == Circle(Point(0, 0), 5) assert p2.incircle == Circle(Point(0, 0), p2.apothem) assert p1.is_convex() assert p1.rotation == 0 p1.spin(pi/3) assert p1.rotation == pi/3 assert p1[0] == Point(5, 5*sqrt(3)) # while spin works in place (notice that rotation is 2pi/3 below) # rotate returns a new object p1_old = p1 assert p1.rotate(pi/3) == RegularPolygon(Point(0, 0), 10, 5, 2*pi/3) assert p1 == p1_old # # Angles # angles = p4.angles assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483")) assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544")) assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388")) assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449")) angles = p3.angles assert feq(angles[Point(0, 0)].evalf(), Float("0.7853981633974483")) assert feq(angles[Point(4, 4)].evalf(), Float("1.2490457723982544")) assert feq(angles[Point(5, 2)].evalf(), Float("1.8925468811915388")) assert feq(angles[Point(3, 0)].evalf(), Float("2.3561944901923449")) # # Triangle # p1 = Point(0, 0) p2 = Point(5, 0) p3 = Point(0, 5) t1 = Triangle(p1, p2, p3) t2 = Triangle(p1, p2, Point(Rational(5,2), sqrt(Rational(75,4)))) t3 = Triangle(p1, Point(x1, 0), Point(0, x1)) s1 = t1.sides s2 = t2.sides s3 = t3.sides # Basic stuff assert Triangle(p1, p1, p1) == p1 assert Triangle(p2, p2*2, p2*3) == Segment(p2, p2*3) assert t1.area == Rational(25,2) assert t1.is_right() assert t2.is_right() == False assert t3.is_right() assert p1 in t1 assert t1.sides[0] in t1 assert Segment((0, 0), (1, 0)) in t1 assert Point(5, 5) not in t2 assert t1.is_convex() assert feq(t1.angles[p1].evalf(), pi.evalf()/2) assert t1.is_equilateral() == False assert t2.is_equilateral() assert t3.is_equilateral() == False assert are_similar(t1, t2) == False assert are_similar(t1, t3) assert are_similar(t2, t3) == False # Bisectors bisectors = t1.bisectors() assert bisectors[p1] == Segment(p1, Point(Rational(5,2), Rational(5,2))) ic = (250 - 125*sqrt(2)) / 50 assert t1.incenter == Point(ic, ic) # Inradius assert t1.inradius == 5 - 5*sqrt(2)/2 assert t2.inradius == 5*sqrt(3)/6 assert t3.inradius == x1**2/((2 + sqrt(2))*Abs(x1)) # Medians + Centroid m = t1.medians assert t1.centroid == Point(Rational(5,3), Rational(5,3)) assert m[p1] == Segment(p1, Point(Rational(5,2), Rational(5,2))) assert t3.medians[p1] == Segment(p1, Point(x1/2, x1/2)) assert intersection(m[p1], m[p2], m[p3]) == [t1.centroid] # Perpendicular altitudes = t1.altitudes assert altitudes[p1] == Segment(p1, Point(Rational(5,2), Rational(5,2))) assert altitudes[p2] == s1[0] assert altitudes[p3] == s1[2] # Ensure assert len(intersection(*bisectors.values())) == 1 assert len(intersection(*altitudes.values())) == 1 assert len(intersection(*m.values())) == 1 # Distance p1 = Polygon( Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1)) p2 = Polygon( Point(0, Rational(5)/4), Point(1, Rational(5)/4), Point(1, Rational(9)/4), Point(0, Rational(9)/4)) p3 = Polygon( Point(1, 2), Point(2, 2), Point(2, 1)) p4 = Polygon( Point(1, 1), Point(Rational(6)/5, 1), Point(1, Rational(6)/5)) p5 = Polygon( Point(half, 3**(half)/2), Point(-half, 3**(half)/2), Point(-1, 0), Point(-half, -(3)**(half)/2), Point(half, -(3)**(half)/2), Point(1, 0)) p6 = Polygon(Point(2, Rational(3)/10), Point(Rational(17)/10, 0), Point(2, -Rational(3)/10), Point(Rational(23)/10, 0)) pt1 = Point(half, half) pt2 = Point(1, 1) '''Polygon to Point''' assert p1.distance(pt1) == half assert p1.distance(pt2) == 0 assert p2.distance(pt1) == Rational(3)/4 assert p3.distance(pt2) == sqrt(2)/2 @XFAIL def test_polygon_to_polygon(): '''Polygon to Polygon''' # XXX: Because of the way the warnings filters work, this will fail if it's # run more than once in the same session. See issue 2492. import warnings # p1.distance(p2) emits a warning # First, test the warning warnings.filterwarnings("error", "Polygons may intersect producing erroneous output") raises(UserWarning, "p1.distance(p2)") # now test the actual output warnings.filterwarnings("ignore", "Polygons may intersect producing erroneous output") assert p1.distance(p2) == half/2 # Keep testing reasonably thread safe, so reset the warning warnings.filterwarnings("default", "Polygons may intersect producing erroneous output") # Note, in Python 2.6+, this can be done more nicely using the # warnings.catch_warnings context manager. # See http://docs.python.org/library/warnings#testing-warnings. assert p1.distance(p3) == sqrt(2)/2 assert p3.distance(p4) == (sqrt(2)/2 - sqrt(Rational(2)/25)/2) assert p5.distance(p6) == Rational(7)/10 def test_convex_hull(): p = [Point(-5,-1), Point(-2,1), Point(-2,-1), Point(-1,-3), Point(0,0), Point(1,1), Point(2,2), Point(2,-1), Point(3,1), Point(4,-1), Point(6,2)] ch = Polygon(p[0], p[3], p[9], p[10], p[6], p[1]) #test handling of duplicate points p.append(p[3]) #more than 3 collinear points another_p = [Point(-45, -85), Point(-45, 85), Point(-45,26),Point(-45,-24)] ch2 = Segment(another_p[0],another_p[1]) assert convex_hull(*another_p) == ch2 assert convex_hull(*p) == ch assert convex_hull(p[0]) == p[0] assert convex_hull(p[0], p[1]) == Segment(p[0], p[1]) # no unique points assert convex_hull(*[p[-1]]*3) == p[-1] # collection of items assert convex_hull(*[Point(0,0), Segment(Point(1, 0), Point(1, 1)), RegularPolygon(Point(2, 0), 2, 4)]) == \ Polygon(Point(0, 0), Point(2, -2), Point(4, 0), Point(2, 2)) def test_concyclic_doctest_bug(): p1,p2 = Point(-1, 0), Point(1, 0) p3,p4 = Point(0, 1), Point(-1, 2) assert Point.is_concyclic(p1, p2, p3) assert not Point.is_concyclic(p1, p2, p3, p4) def test_subs(): p = Point(x, 2) q = Point(1, 1) r = Point(3, 4) for o in [p, Segment(p, q), Ray(p, q), Line(p, q), Triangle(p, q, r), RegularPolygon(p, 3, 6), Polygon(p, q, r, Point(5,4)), Circle(p, 3), Ellipse(p, 3, 4)]: assert 'y' in str(o.subs(x, y)) def test_encloses(): # square with a dimpled left side s = Polygon(Point(0, 0), Point(1, 0), Point(1, 1), Point(0, 1), Point(S.Half, S.Half)) # the following will be True if the polygon isn't treated as closing on itself assert s.encloses(Point(0, S.Half)) is False assert s.encloses(Point(S.Half, S.Half)) is False # it's a vertex assert s.encloses(Point(Rational(3, 4), S.Half)) is True def test_free_symbols(): a, b, c, d, e, f, s = symbols('a:f,s') assert Point(a,b).free_symbols == set([a, b]) assert Line((a,b),(c,d)).free_symbols == set([a, b, c, d]) assert Ray((a,b),(c,d)).free_symbols == set([a, b, c, d]) assert Ray((a,b),angle=c).free_symbols == set([a, b, c]) assert Segment((a,b),(c,d)).free_symbols == set([a, b, c, d]) assert Line((a,b),slope=c).free_symbols == set([a, b, c]) assert Curve((a*s,b*s),(s,c,d)).free_symbols == set([a, b, c, d]) assert Ellipse((a,b),c,d).free_symbols == set([a, b, c, d]) assert Ellipse((a,b),c, eccentricity=d).free_symbols == set([a, b, c, d]) assert Ellipse((a,b),vradius=c, eccentricity=d).free_symbols == set([a, b, c, d]) assert Circle((a,b),c).free_symbols == set([a, b, c]) assert Circle((a,b),(c,d),(e,f)).free_symbols == set([e, d, c, b, f, a]) assert Polygon((a,b),(c,d),(e,f)).free_symbols == set([e, b, d, f, a, c]) assert RegularPolygon((a,b),c,d,e).free_symbols == set([e, a, b, c, d]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/geometry/__init__.py0000644000175000017500000000122212014170666025040 0ustar georgeskgeorgesk""" A geometry module for the SymPy library. This module contains all of the entities and functions needed to construct basic geometrical data and to perform simple informational queries. Usage: ====== Notes: ====== Currently the geometry module is restricted to the 2-dimensional Euclidean space. Examples: ========= """ from sympy.geometry.point import Point from sympy.geometry.line import Line, Ray, Segment from sympy.geometry.ellipse import Ellipse, Circle from sympy.geometry.polygon import Polygon, RegularPolygon, Triangle from sympy.geometry.util import * from sympy.geometry.exceptions import * from sympy.geometry.curve import Curve wxgeometrie-0.133.2.orig/wxgeometrie/sympy/interactive/0000755000175000017500000000000012014170666023414 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/interactive/session.py0000644000175000017500000001205512014170666025454 0ustar georgeskgeorgesk"""Tools for setting up interactive sessions. """ from sympy.interactive.printing import init_printing preexec_source = """\ from __future__ import division from sympy import * x, y, z, t = symbols('x y z t') k, m, n = symbols('k m n', integer=True) f, g, h = symbols('f g h', cls=Function) """ verbose_message = """\ These commands were executed: %(source)s Documentation can be found at http://www.sympy.org """ no_ipython = """\ Couldn't locate IPython. Having IPython installed is greatly recommended. See http://ipython.scipy.org for more details. If you use Debian/Ubuntu, just install the 'ipython' package and start isympy again. """ def _make_message(ipython=True, quiet=False, source=None): """Create a banner for an interactive session. """ from sympy import __version__ as sympy_version from sympy.polys.domains import GROUND_TYPES from sympy.utilities.misc import ARCH import sys import os python_version = "%d.%d.%d" % sys.version_info[:3] if ipython: shell_name = "IPython" else: shell_name = "Python" info = ['ground types: %s' % GROUND_TYPES] cache = os.getenv('SYMPY_USE_CACHE') if cache is not None and cache.lower() == 'no': info.append('cache: off') args = shell_name, sympy_version, python_version, ARCH, ', '.join(info) message = "%s console for SymPy %s (Python %s-%s) (%s)\n" % args if not quiet: if source is None: source = preexec_source _source = "" for line in source.split('\n')[:-1]: if not line: _source += '\n' else: _source += '>>> ' + line + '\n' message += '\n' + verbose_message % {'source': _source} return message def _init_ipython_session(argv=[]): """Construct new IPython session. """ import IPython if IPython.__version__ >= '0.11': from IPython.frontend.terminal import ipapp # use an app to parse the command line, and init config app = ipapp.TerminalIPythonApp() # don't draw IPython banner during initialization: app.display_banner = False app.initialize(argv) return app.shell else: from IPython.Shell import make_IPython return make_IPython(argv) def _init_python_session(): """Construct new Python session. """ from code import InteractiveConsole class SymPyConsole(InteractiveConsole): """An interactive console with readline support. """ def __init__(self): InteractiveConsole.__init__(self) try: import readline except ImportError: pass else: import os import atexit readline.parse_and_bind('tab: complete') if hasattr(readline, 'read_history_file'): history = os.path.expanduser('~/.sympy-history') try: readline.read_history_file(history) except IOError: pass atexit.register(readline.write_history_file, history) return SymPyConsole() def init_session(ipython=None, pretty_print=True, order=None, use_unicode=None, quiet=False, argv=[]): """Initialize an embedded IPython or Python session. """ import sys in_ipython = False if ipython is False: ip = _init_python_session() mainloop = ip.interact else: try: import IPython except ImportError: if ipython is not True: print no_ipython ip = _init_python_session() mainloop = ip.interact else: raise RuntimeError("IPython is not available on this system") else: ipython = True if IPython.__version__ >= '0.11': try: ip = get_ipython() except NameError: ip = None else: ip = IPython.ipapi.get() if ip: ip = ip.IP if ip is not None: in_ipython = True else: ip = _init_ipython_session(argv) if IPython.__version__ >= '0.11': # runsource is gone, use run_cell instead, which doesn't # take a symbol arg. The second arg is `store_history`, # and False means don't add the line to IPython's history. ip.runsource = lambda src, symbol='exec': ip.run_cell(src, False) mainloop = ip.mainloop else: mainloop = ip.interact _preexec_source = preexec_source ip.runsource(_preexec_source, symbol='exec') init_printing(pretty_print=pretty_print, order=order, use_unicode=use_unicode, ip=ip) message = _make_message(ipython, quiet, _preexec_source) if not in_ipython: mainloop(message) sys.exit('Exiting ...') else: ip.write(message) ip.set_hook('shutdown_hook', lambda ip: ip.write("Exiting ...\n")) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/interactive/printing.py0000644000175000017500000000525512014170666025627 0ustar georgeskgeorgesk"""Tools for setting up printing in interactive sessions. """ def _init_python_printing(stringify_func): """Setup printing in Python interactive session. """ import __builtin__, sys def displayhook(arg): """Python's pretty-printer display hook. This function was adapted from: http://www.python.org/dev/peps/pep-0217/ """ if arg is not None: __builtin__._ = None print stringify_func(arg) __builtin__._ = arg sys.displayhook = displayhook def _init_ipython_printing(ip, stringify_func): """Setup printing in IPython interactive session. """ def pretty_print(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" p.text(stringify_func(arg)) def result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if '\n' in out: print print out else: print repr(arg) import IPython if IPython.__version__ >= '0.11': formatter = ip.display_formatter.formatters['text/plain'] for cls in (object, tuple, list, set, frozenset, dict, str): formatter.for_type(cls, pretty_print) # this loads pretty printing for objects that inherit from Basic or Matrix: formatter.for_type_by_name( 'sympy.core.basic', 'Basic', pretty_print ) formatter.for_type_by_name( 'sympy.matrices.matrices', 'Matrix', pretty_print ) else: ip.set_hook('result_display', result_display) def init_printing(pretty_print=True, order=None, use_unicode=None, wrap_line=None, no_global=False, ip=None): """Initializes pretty-printer depending on the environment. """ from sympy.printing.printer import Printer if pretty_print: from sympy.printing import pretty as stringify_func else: from sympy.printing import sstrrepr as stringify_func if not no_global: Printer.set_global_settings(order=order, use_unicode=use_unicode, wrap_line=wrap_line) else: _stringify_func = stringify_func if pretty_print: stringify_func = lambda expr: _stringify_func(expr, order=order, use_unicode=use_unicode, wrap_line=wrap_line) else: stringify_func = lambda expr: _stringify_func(expr, order=order) if ip is not None and ip.__module__.startswith('IPython'): _init_ipython_printing(ip, stringify_func) else: _init_python_printing(stringify_func) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/interactive/__init__.py0000644000175000017500000000020612014170666025523 0ustar georgeskgeorgesk"""Helper module for setting up interactive SymPy sessions. """ from printing import init_printing from session import init_session wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/0000755000175000017500000000000012014170666022245 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/sparsetools.py0000644000175000017500000000355712014170666025207 0ustar georgeskgeorgesk"""Advanced tools for sparse polynomials in `K[x]` or `K[X]`. """ def smp_clear_denoms(f, u, ord, K, convert=False): pass def smp_lift(f, u, ord, K): pass def smp_eval_in(f, a, j, u, ord, K): pass def smp_half_gcdex(f, g, ord, K): pass def smp_gcdex(f, g, u, ord, K): pass def smp_invert(f, g, u, ord, K): pass def smp_resultant(f, g, u, ord, K): pass def smp_discriminant(f, u, ord, K): pass def smp_cofactors(f, g, u, ord, K): pass def smp_gcd(f, g, u, ord, K): pass def smp_lcm(f, g, u, ord, K): pass def smp_ground_trunc(f, p, u, ord, K): pass def smp_ground_monic(f, u, ord, K): pass def smp_ground_content(f, u, ord, K): pass def smp_ground_primitive(f, u, ord, K): pass def smp_compose(f, g, u, ord, K): pass def smp_decompose(f, u, ord, K): pass def smp_sturm(f, u, ord, K): pass def smp_sqf_norm(f, u, ord, K): pass def smp_sqf_part(f, u, ord, K): pass def smp_sqf_list(f, u, ord, K, all=False): pass def smp_sqf_list_include(f, u, ord, K, all=False): pass def smp_factor_list(f, u, ord, K): pass def smp_factor_list_include(f, u, ord, K): pass def smp_real_intervals(f, u, ord, K, eps, inf, sup, fast): pass def smp_complex_intervals(f, u, ord, K, eps, inf, sup, fast): pass def smp_refine_real_root(f, s, t, u, ord, K, eps, steps, fast): pass def smp_refine_complex_root(f, s, t, u, ord, K, eps, steps, fast): pass def smp_count_real_roots(f, u, ord, K, inf, sup): pass def smp_count_complex_roots(f, u, ord, K, inf, sup): pass def smp_zero_p(f, u): pass def smp_one_p(f, u, K): pass def smp_ground_p(f, u): pass def smp_sqf_p(f, u, ord, K): pass def smp_monic_p(f, u, ord, K): pass def smp_primitive_p(f, u, ord, K): pass def smp_linear_p(f, u, ord, K): pass def smp_homogeneous_p(f, u, ord, K): pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyclasses.py0000644000175000017500000013217112014170666025165 0ustar georgeskgeorgesk"""OO layer for several polynomial representations. """ from sympy.core.compatibility import cmp class GenericPoly(object): """Base class for low-level polynomial representations. """ def ground_to_ring(f): """Make the ground domain a ring. """ return f.set_domain(f.dom.get_ring()) def ground_to_field(f): """Make the ground domain a field. """ return f.set_domain(f.dom.get_field()) def ground_to_exact(f): """Make the ground domain exact. """ return f.set_domain(f.dom.get_exact()) @classmethod def _perify_factors(per, result, include): if include: coeff, factors = result else: coeff = result factors = [ (per(g), k) for g, k in factors ] if include: return coeff, factors else: return factors from sympy.polys.densebasic import ( dmp_validate, dup_normal, dmp_normal, dup_convert, dmp_convert, dup_from_sympy, dmp_from_sympy, dup_strip, dmp_strip, dup_degree, dmp_degree_in, dmp_degree_list, dmp_negative_p, dmp_positive_p, dup_LC, dmp_ground_LC, dup_TC, dmp_ground_TC, dup_nth, dmp_ground_nth, dmp_zero, dmp_one, dmp_ground, dmp_zero_p, dmp_one_p, dmp_ground_p, dup_from_dict, dmp_from_dict, dup_to_raw_dict, dmp_to_dict, dup_deflate, dmp_deflate, dmp_inject, dmp_eject, dup_terms_gcd, dmp_terms_gcd, dmp_list_terms, dmp_exclude, dmp_slice_in, dmp_permute, dmp_to_tuple,) from sympy.polys.densearith import ( dup_add_term, dmp_add_term, dup_sub_term, dmp_sub_term, dup_mul_term, dmp_mul_term, dup_add_ground, dmp_add_ground, dup_sub_ground, dmp_sub_ground, dup_mul_ground, dmp_mul_ground, dup_quo_ground, dmp_quo_ground, dup_exquo_ground, dmp_exquo_ground, dup_abs, dmp_abs, dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_sqr, dmp_sqr, dup_pow, dmp_pow, dup_pdiv, dmp_pdiv, dup_prem, dmp_prem, dup_pquo, dmp_pquo, dup_pexquo, dmp_pexquo, dup_div, dmp_div, dup_rem, dmp_rem, dup_quo, dmp_quo, dup_exquo, dmp_exquo, dmp_add_mul, dmp_sub_mul, dup_max_norm, dmp_max_norm, dup_l1_norm, dmp_l1_norm) from sympy.polys.densetools import ( dup_clear_denoms, dmp_clear_denoms, dup_integrate, dmp_integrate_in, dup_diff, dmp_diff_in, dup_eval, dmp_eval_in, dup_revert, dup_trunc, dmp_ground_trunc, dup_content, dmp_ground_content, dup_primitive, dmp_ground_primitive, dup_monic, dmp_ground_monic, dup_compose, dmp_compose, dup_decompose, dup_shift, dmp_lift) from sympy.polys.euclidtools import ( dup_half_gcdex, dup_gcdex, dup_invert, dup_subresultants, dmp_subresultants, dup_resultant, dmp_resultant, dup_discriminant, dmp_discriminant, dup_inner_gcd, dmp_inner_gcd, dup_gcd, dmp_gcd, dup_lcm, dmp_lcm, dup_cancel, dmp_cancel) from sympy.polys.sqfreetools import ( dup_gff_list, dup_sqf_p, dmp_sqf_p, dup_sqf_norm, dmp_sqf_norm, dup_sqf_part, dmp_sqf_part, dup_sqf_list, dup_sqf_list_include, dmp_sqf_list, dmp_sqf_list_include) from sympy.polys.factortools import ( dup_zz_cyclotomic_p, dup_factor_list, dup_factor_list_include, dmp_factor_list, dmp_factor_list_include) from sympy.polys.rootisolation import ( dup_isolate_real_roots_sqf, dup_isolate_real_roots, dup_isolate_all_roots_sqf, dup_isolate_all_roots, dup_refine_real_root, dup_count_real_roots, dup_count_complex_roots, dup_sturm) from sympy.polys.polyerrors import ( UnificationFailed, PolynomialError, DomainError) def init_normal_DMP(rep, lev, dom): return DMP(dmp_normal(rep, lev, dom), dom, lev) class DMP(object): """Dense Multivariate Polynomials over `K`. """ __slots__ = ['rep', 'lev', 'dom'] def __init__(self, rep, dom, lev=None): if lev is not None: if type(rep) is dict: rep = dmp_from_dict(rep, lev, dom) elif type(rep) is not list: rep = dmp_ground(dom.convert(rep), lev) else: rep, lev = dmp_validate(rep) self.rep = rep self.lev = lev self.dom = dom def __repr__(f): return "%s(%s, %s)" % (f.__class__.__name__, f.rep, f.dom) def __hash__(f): return hash((f.__class__.__name__, f.to_tuple(), f.lev, f.dom)) def __getstate__(self): return (self.rep, self.lev, self.dom) def __getnewargs__(self): return (self.rep, self.lev, self.dom) def unify(f, g): """Unify representations of two multivariate polynomials. """ return f.lev, f.dom, f.per, f.rep, g.rep if not isinstance(g, DMP) or f.lev != g.lev: raise UnificationFailed("can't unify %s with %s" % (f, g)) if f.dom == g.dom: return f.lev, f.dom, f.per, f.rep, g.rep else: lev, dom = f.lev, f.dom.unify(g.dom) F = dmp_convert(f.rep, lev, f.dom, dom) G = dmp_convert(g.rep, lev, g.dom, dom) def per(rep, dom=dom, lev=lev, kill=False): if kill: if not lev: return rep else: lev -= 1 return DMP(rep, dom, lev) return lev, dom, per, F, G def per(f, rep, dom=None, kill=False): """Create a DMP out of the given representation. """ lev = f.lev if kill: if not lev: return rep else: lev -= 1 if dom is None: dom = f.dom return DMP(rep, dom, lev) @classmethod def zero(cls, lev, dom): return DMP(0, dom, lev) @classmethod def one(cls, lev, dom): return DMP(1, dom, lev) @classmethod def from_list(cls, rep, lev, dom): """Create an instance of `cls` given a list of native coefficients. """ return cls(dmp_convert(rep, lev, None, dom), dom, lev) @classmethod def from_sympy_list(cls, rep, lev, dom): """Create an instance of `cls` given a list of SymPy coefficients. """ return cls(dmp_from_sympy(rep, lev, dom), dom, lev) def to_dict(f, zero=False): """Convert `f` to a dict representation with native coefficients. """ return dmp_to_dict(f.rep, f.lev, f.dom, zero=zero) def to_sympy_dict(f, zero=False): """Convert `f` to a dict representation with SymPy coefficients. """ rep = dmp_to_dict(f.rep, f.lev, f.dom, zero=zero) for k, v in rep.iteritems(): rep[k] = f.dom.to_sympy(v) return rep def to_tuple(f): """ Convert `f` to a tuple representation with native coefficients. This is needed for hashing. """ return dmp_to_tuple(f.rep, f.lev) @classmethod def from_dict(cls, rep, lev, dom): """Construct and instance of ``cls`` from a ``dict`` representation. """ return cls(dmp_from_dict(rep, lev, dom), dom, lev) @classmethod def from_monoms_coeffs(cls, monoms, coeffs, lev, dom): return DMP(dict(zip(monoms, coeffs)), dom, lev) def to_ring(f): """Make the ground domain a field. """ return f.convert(f.dom.get_ring()) def to_field(f): """Make the ground domain a field. """ return f.convert(f.dom.get_field()) def to_exact(f): """Make the ground domain exact. """ return f.convert(f.dom.get_exact()) def convert(f, dom): """Convert the ground domain of `f`. """ if f.dom == dom: return f else: return DMP(dmp_convert(f.rep, f.lev, f.dom, dom), dom, f.lev) def slice(f, m, n, j=0): """Take a continuous subsequence of terms of `f`. """ return f.per(dmp_slice_in(f.rep, m, n, j, f.lev, f.dom)) def coeffs(f, order=None): """Returns all non-zero coefficients from `f` in lex order. """ return [ c for _, c in dmp_list_terms(f.rep, f.lev, f.dom, order=order) ] def monoms(f, order=None): """Returns all non-zero monomials from `f` in lex order. """ return [ m for m, _ in dmp_list_terms(f.rep, f.lev, f.dom, order=order) ] def terms(f, order=None): """Returns all non-zero terms from `f` in lex order. """ return dmp_list_terms(f.rep, f.lev, f.dom, order=order) def all_coeffs(f): """Returns all coefficients from `f`. """ if not f.lev: if not f: return [f.dom.zero] else: return [ c for c in f.rep ] else: raise PolynomialError('multivariate polynomials not supported') def all_monoms(f): """Returns all monomials from `f`. """ if not f.lev: n = dup_degree(f.rep) if n < 0: return [(0,)] else: return [ (n-i,) for i, c in enumerate(f.rep) ] else: raise PolynomialError('multivariate polynomials not supported') def all_terms(f): """Returns all terms from a `f`. """ if not f.lev: n = dup_degree(f.rep) if n < 0: return [((0,), f.dom.zero)] else: return [ ((n-i,), c) for i, c in enumerate(f.rep) ] else: raise PolynomialError('multivariate polynomials not supported') def lift(f): """Convert algebraic coefficients to rationals. """ return f.per(dmp_lift(f.rep, f.lev, f.dom), dom=f.dom.dom) def deflate(f): """Reduce degree of `f` by mapping `x_i**m` to `y_i`. """ J, F = dmp_deflate(f.rep, f.lev, f.dom) return J, f.per(F) def inject(f, front=False): """Inject ground domain generators into ``f``. """ F, lev = dmp_inject(f.rep, f.lev, f.dom, front=front) return f.__class__(F, f.dom.dom, lev) def eject(f, dom, front=False): """Eject selected generators into the ground domain. """ F = dmp_eject(f.rep, f.lev, dom, front=front) return f.__class__(F, dom, f.lev - len(dom.gens)) def exclude(f): r""" Remove useless generators from ``f``. Returns the removed generators and the new excluded ``f``. **Example** >>> from sympy.polys.polyclasses import DMP >>> from sympy.polys.domains import ZZ >>> DMP([[[ZZ(1)]], [[ZZ(1)], [ZZ(2)]]], ZZ).exclude() ([2], DMP([[1], [1, 2]], ZZ)) """ J, F, u = dmp_exclude(f.rep, f.lev, f.dom) return J, f.__class__(F, f.dom, u) def permute(f, P): r""" Returns a polynomial in ``K[x_{P(1)}, ..., x_{P(n)}]``. **Example** >>> from sympy.polys.polyclasses import DMP >>> from sympy.polys.domains import ZZ >>> DMP([[[ZZ(2)], [ZZ(1), ZZ(0)]], [[]]], ZZ).permute([1, 0, 2]) DMP([[[2], []], [[1, 0], []]], ZZ) >>> DMP([[[ZZ(2)], [ZZ(1), ZZ(0)]], [[]]], ZZ).permute([1, 2, 0]) DMP([[[1], []], [[2, 0], []]], ZZ) """ return f.per(dmp_permute(f.rep, P, f.lev, f.dom)) def terms_gcd(f): """Remove GCD of terms from the polynomial `f`. """ J, F = dmp_terms_gcd(f.rep, f.lev, f.dom) return J, f.per(F) def add_ground(f, c): """Add an element of the ground domain to ``f``. """ return f.per(dmp_add_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def sub_ground(f, c): """Subtract an element of the ground domain from ``f``. """ return f.per(dmp_sub_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def mul_ground(f, c): """Multiply ``f`` by a an element of the ground domain. """ return f.per(dmp_mul_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def quo_ground(f, c): """Quotient of ``f`` by a an element of the ground domain. """ return f.per(dmp_quo_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def exquo_ground(f, c): """Exact quotient of ``f`` by a an element of the ground domain. """ return f.per(dmp_exquo_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def abs(f): """Make all coefficients in `f` positive. """ return f.per(dmp_abs(f.rep, f.lev, f.dom)) def neg(f): """Negate all cefficients in `f`. """ return f.per(dmp_neg(f.rep, f.lev, f.dom)) def add(f, g): """Add two multivariate polynomials `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_add(F, G, lev, dom)) def sub(f, g): """Subtract two multivariate polynomials `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_sub(F, G, lev, dom)) def mul(f, g): """Multiply two multivariate polynomials `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_mul(F, G, lev, dom)) def sqr(f): """Square a multivariate polynomial `f`. """ return f.per(dmp_sqr(f.rep, f.lev, f.dom)) def pow(f, n): """Raise `f` to a non-negative power `n`. """ if isinstance(n, int): return f.per(dmp_pow(f.rep, n, f.lev, f.dom)) else: raise TypeError("`int` expected, got %s" % type(n)) def pdiv(f, g): """Polynomial pseudo-division of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) q, r = dmp_pdiv(F, G, lev, dom) return per(q), per(r) def prem(f, g): """Polynomial pseudo-remainder of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_prem(F, G, lev, dom)) def pquo(f, g): """Polynomial pseudo-quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_pquo(F, G, lev, dom)) def pexquo(f, g): """Polynomial exact pseudo-quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_pexquo(F, G, lev, dom)) def div(f, g): """Polynomial division with remainder of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) q, r = dmp_div(F, G, lev, dom) return per(q), per(r) def rem(f, g): """Computes polynomial remainder of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_rem(F, G, lev, dom)) def quo(f, g): """Computes polynomial quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_quo(F, G, lev, dom)) def exquo(f, g): """Computes polynomial exact quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_exquo(F, G, lev, dom)) def degree(f, j=0): """Returns the leading degree of `f` in `x_j`. """ if isinstance(j, int): return dmp_degree_in(f.rep, j, f.lev) else: raise TypeError("`int` expected, got %s" % type(j)) def degree_list(f): """Returns a list of degrees of `f`. """ return dmp_degree_list(f.rep, f.lev) def total_degree(f): """Returns the total degree of `f`. """ return sum(dmp_degree_list(f.rep, f.lev)) def LC(f): """Returns the leading coefficent of `f`. """ return dmp_ground_LC(f.rep, f.lev, f.dom) def TC(f): """Returns the trailing coefficent of `f`. """ return dmp_ground_TC(f.rep, f.lev, f.dom) def nth(f, *N): """Returns the `n`-th coefficient of `f`. """ if all(isinstance(n, int) for n in N): return dmp_ground_nth(f.rep, N, f.lev, f.dom) else: raise TypeError("a sequence of integers expected") def max_norm(f): """Returns maximum norm of `f`. """ return dmp_max_norm(f.rep, f.lev, f.dom) def l1_norm(f): """Returns l1 norm of `f`. """ return dmp_l1_norm(f.rep, f.lev, f.dom) def clear_denoms(f): """Clear denominators, but keep the ground domain. """ coeff, F = dmp_clear_denoms(f.rep, f.lev, f.dom) return coeff, f.per(F) def integrate(f, m=1, j=0): """Computes indefinite integral of `f`. """ if not isinstance(m, int): raise TypeError("`int` expected, got %s" % type(m)) if not isinstance(j, int): raise TypeError("`int` expected, got %s" % type(j)) return f.per(dmp_integrate_in(f.rep, m, j, f.lev, f.dom)) def diff(f, m=1, j=0): """Computes `m`-th order derivative of `f` in `x_j`. """ if not isinstance(m, int): raise TypeError("`int` expected, got %s" % type(m)) if not isinstance(j, int): raise TypeError("`int` expected, got %s" % type(j)) return f.per(dmp_diff_in(f.rep, m, j, f.lev, f.dom)) def eval(f, a, j=0): """Evaluates `f` at the given point `a` in `x_j`. """ if not isinstance(j, int): raise TypeError("`int` expected, got %s" % type(j)) return f.per(dmp_eval_in(f.rep, f.dom.convert(a), j, f.lev, f.dom), kill=True) def half_gcdex(f, g): """Half extended Euclidean algorithm, if univariate. """ lev, dom, per, F, G = f.unify(g) if not lev: s, h = dup_half_gcdex(F, G, dom) return per(s), per(h) else: raise ValueError('univariate polynomial expected') def gcdex(f, g): """Extended Euclidean algorithm, if univariate. """ lev, dom, per, F, G = f.unify(g) if not lev: s, t, h = dup_gcdex(F, G, dom) return per(s), per(t), per(h) else: raise ValueError('univariate polynomial expected') def invert(f, g): """Invert `f` modulo `g`, if possible. """ lev, dom, per, F, G = f.unify(g) if not lev: return per(dup_invert(F, G, dom)) else: raise ValueError('univariate polynomial expected') def revert(f, n): """Compute `f**(-1)` mod `x**n`. """ if not f.lev: return f.per(dup_revert(f.rep, n, f.dom)) else: raise ValueError('univariate polynomial expected') def subresultants(f, g): """Computes subresultant PRS sequence of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) R = dmp_subresultants(F, G, lev, dom) return map(per, R) def resultant(f, g): """Computes resultant of `f` and `g` via PRS. """ lev, dom, per, F, G = f.unify(g) return per(dmp_resultant(F, G, lev, dom), kill=True) def discriminant(f): """Computes discriminant of `f`. """ return f.per(dmp_discriminant(f.rep, f.lev, f.dom), kill=True) def cofactors(f, g): """Returns GCD of `f` and `g` and their cofactors. """ lev, dom, per, F, G = f.unify(g) h, cff, cfg = dmp_inner_gcd(F, G, lev, dom) return per(h), per(cff), per(cfg) def gcd(f, g): """Returns polynomial GCD of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_gcd(F, G, lev, dom)) def lcm(f, g): """Returns polynomial LCM of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_lcm(F, G, lev, dom)) def cancel(f, g, include=True): """Cancel common factors in a rational function ``f/g``. """ lev, dom, per, F, G = f.unify(g) if include: F, G = dmp_cancel(F, G, lev, dom, include=True) else: cF, cG, F, G = dmp_cancel(F, G, lev, dom, include=False) F, G = per(F), per(G) if include: return F, G else: return cF, cG, F, G def trunc(f, p): """Reduce `f` modulo a constant `p`. """ return f.per(dmp_ground_trunc(f.rep, f.dom.convert(p), f.lev, f.dom)) def monic(f): """Divides all coefficients by `LC(f)`. """ return f.per(dmp_ground_monic(f.rep, f.lev, f.dom)) def content(f): """Returns GCD of polynomial coefficients. """ return dmp_ground_content(f.rep, f.lev, f.dom) def primitive(f): """Returns content and a primitive form of `f`. """ cont, F = dmp_ground_primitive(f.rep, f.lev, f.dom) return cont, f.per(F) def compose(f, g): """Computes functional composition of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_compose(F, G, lev, dom)) def decompose(f): """Computes functional decomposition of `f`. """ if not f.lev: return map(f.per, dup_decompose(f.rep, f.dom)) else: raise ValueError('univariate polynomial expected') def shift(f, a): """Efficiently compute Taylor shift ``f(x + a)``. """ if not f.lev: return f.per(dup_shift(f.rep, f.dom.convert(a), f.dom)) else: raise ValueError('univariate polynomial expected') def sturm(f): """Computes the Sturm sequence of `f`. """ if not f.lev: return map(f.per, dup_sturm(f.rep, f.dom)) else: raise ValueError('univariate polynomial expected') def gff_list(f): """Computes greatest factorial factorization of `f`. """ if not f.lev: return [ (f.per(g), k) for g, k in dup_gff_list(f.rep, f.dom) ] else: raise ValueError('univariate polynomial expected') def sqf_norm(f): """Computes square-free norm of `f`. """ s, g, r = dmp_sqf_norm(f.rep, f.lev, f.dom) return s, f.per(g), f.per(r, dom=f.dom.dom) def sqf_part(f): """Computes square-free part of `f`. """ return f.per(dmp_sqf_part(f.rep, f.lev, f.dom)) def sqf_list(f, all=False): """Returns a list of square-free factors of `f`. """ coeff, factors = dmp_sqf_list(f.rep, f.lev, f.dom, all) return coeff, [ (f.per(g), k) for g, k in factors ] def sqf_list_include(f, all=False): """Returns a list of square-free factors of `f`. """ factors = dmp_sqf_list_include(f.rep, f.lev, f.dom, all) return [ (f.per(g), k) for g, k in factors ] def factor_list(f): """Returns a list of irreducible factors of `f`. """ coeff, factors = dmp_factor_list(f.rep, f.lev, f.dom) return coeff, [ (f.per(g), k) for g, k in factors ] def factor_list_include(f): """Returns a list of irreducible factors of `f`. """ factors = dmp_factor_list_include(f.rep, f.lev, f.dom) return [ (f.per(g), k) for g, k in factors ] def intervals(f, all=False, eps=None, inf=None, sup=None, fast=False, sqf=False): """Compute isolating intervals for roots of `f`. """ if not f.lev: if not all: if not sqf: return dup_isolate_real_roots(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) else: return dup_isolate_real_roots_sqf(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) else: if not sqf: return dup_isolate_all_roots(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) else: return dup_isolate_all_roots_sqf(f.rep, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) else: raise PolynomialError("can't isolate roots of a multivariate polynomial") def refine_root(f, s, t, eps=None, steps=None, fast=False): """Refine an isolating interval to the given precision. """ if not f.lev: return dup_refine_real_root(f.rep, s, t, f.dom, eps=eps, steps=steps, fast=fast) else: raise PolynomialError("can't refine a root of a multivariate polynomial") def count_real_roots(f, inf=None, sup=None): """Return the number of real roots of ``f`` in ``[inf, sup]``. """ return dup_count_real_roots(f.rep, f.dom, inf=inf, sup=sup) def count_complex_roots(f, inf=None, sup=None): """Return the number of complex roots of ``f`` in ``[inf, sup]``. """ return dup_count_complex_roots(f.rep, f.dom, inf=inf, sup=sup) @property def is_zero(f): """Returns `True` if `f` is a zero polynomial. """ return dmp_zero_p(f.rep, f.lev) @property def is_one(f): """Returns `True` if `f` is a unit polynomial. """ return dmp_one_p(f.rep, f.lev, f.dom) @property def is_ground(f): """Returns `True` if `f` is an element of the ground domain. """ return dmp_ground_p(f.rep, None, f.lev) @property def is_sqf(f): """Returns `True` if `f` is a square-free polynomial. """ return dmp_sqf_p(f.rep, f.lev, f.dom) @property def is_monic(f): """Returns `True` if the leading coefficient of `f` is one. """ return f.dom.is_one(dmp_ground_LC(f.rep, f.lev, f.dom)) @property def is_primitive(f): """Returns `True` if GCD of coefficients of `f` is one. """ return f.dom.is_one(dmp_ground_content(f.rep, f.lev, f.dom)) @property def is_linear(f): """Returns `True` if `f` is linear in all its variables. """ return all([ sum(monom) <= 1 for monom in dmp_to_dict(f.rep, f.lev, f.dom).keys() ]) @property def is_quadratic(f): """Returns `True` if `f` is quadratic in all its variables. """ return all([ sum(monom) <= 2 for monom in dmp_to_dict(f.rep, f.lev, f.dom).keys() ]) @property def is_monomial(f): """Returns `True` if `f` is zero or has only one term. """ return len(f.to_dict()) <= 1 @property def is_homogeneous(f): """Returns `True` if `f` has zero trailing coefficient. """ return f.dom.is_zero(dmp_ground_TC(f.rep, f.lev, f.dom)) @property def is_cyclotomic(f): """Returns ``True`` if ``f`` is a cyclotomic polnomial. """ if not f.lev: return dup_zz_cyclotomic_p(f.rep, f.dom) else: return False def __abs__(f): return f.abs() def __neg__(f): return f.neg() def __add__(f, g): if not isinstance(g, DMP): try: g = f.per(dmp_ground(f.dom.convert(g), f.lev)) except TypeError: return NotImplemented return f.add(g) def __radd__(f, g): return f.__add__(g) def __sub__(f, g): if not isinstance(g, DMP): try: g = f.per(dmp_ground(f.dom.convert(g), f.lev)) except TypeError: return NotImplemented return f.sub(g) def __rsub__(f, g): return (-f).__add__(g) def __mul__(f, g): if isinstance(g, DMP): return f.mul(g) else: try: return f.mul_ground(g) except TypeError: return NotImplemented def __rmul__(f, g): return f.__mul__(g) def __pow__(f, n): return f.pow(n) def __divmod__(f, g): return f.div(g) def __mod__(f, g): return f.rem(g) def __floordiv__(f, g): if isinstance(g, DMP): return f.quo(g) else: try: return f.quo_ground(g) except TypeError: return NotImplemented def __eq__(f, g): try: _, _, _, F, G = f.unify(g) if f.lev == g.lev: return F == G except UnificationFailed: pass return False def __ne__(f, g): try: _, _, _, F, G = f.unify(g) if f.lev == g.lev: return F != G except UnificationFailed: pass return True def __lt__(f, g): _, _, _, F, G = f.unify(g) return F.__lt__(G) def __le__(f, g): _, _, _, F, G = f.unify(g) return F.__le__(G) def __gt__(f, g): _, _, _, F, G = f.unify(g) return F.__gt__(G) def __ge__(f, g): _, _, _, F, G = f.unify(g) return F.__ge__(G) def __nonzero__(f): return not dmp_zero_p(f.rep, f.lev) def init_normal_DMF(num, den, lev, dom): return DMF(dmp_normal(num, lev, dom), dmp_normal(den, lev, dom), dom, lev) class DMF(object): """Dense Multivariate Fractions over `K`. """ __slots__ = ['num', 'den', 'lev', 'dom'] def __init__(self, rep, dom, lev=None): num, den, lev = self._parse(rep, dom, lev) num, den = dmp_cancel(num, den, lev, dom) self.num = num self.den = den self.lev = lev self.dom = dom @classmethod def new(cls, rep, dom, lev=None): num, den, lev = cls._parse(rep, dom, lev) obj = object.__new__(cls) obj.num = num obj.den = den obj.lev = lev obj.dom = dom return obj @classmethod def _parse(cls, rep, dom, lev=None): if type(rep) is tuple: num, den = rep if lev is not None: if type(num) is dict: num = dmp_from_dict(num, lev, dom) if type(den) is dict: den = dmp_from_dict(den, lev, dom) else: num, num_lev = dmp_validate(num) den, den_lev = dmp_validate(den) if num_lev == den_lev: lev = num_lev else: raise ValueError('inconsistent number of levels') if dmp_zero_p(den, lev): raise ZeroDivisionError('fraction denominator') if dmp_zero_p(num, lev): den = dmp_one(lev, dom) else: if dmp_negative_p(den, lev, dom): num = dmp_neg(num, lev, dom) den = dmp_neg(den, lev, dom) else: num = rep if lev is not None: if type(num) is dict: num = dmp_from_dict(num, lev, dom) elif type(num) is not list: num = dmp_ground(dom.convert(num), lev) else: num, lev = dmp_validate(num) den = dmp_one(lev, dom) return num, den, lev def __repr__(f): return "%s((%s, %s), %s)" % (f.__class__.__name__, f.num, f.den, f.dom) def __hash__(f): return hash((f.__class__.__name__, dmp_to_tuple(f.num, f.lev), dmp_to_tuple(f.den, f.lev), f.lev, f.dom)) def __getstate__(self): return (self.num, self.den, self.lev, self.dom) def __getnewargs__(self): return (self.num, self.den, self.lev, self.dom) def poly_unify(f, g): """Unify a multivariate fraction and a polynomial. """ if not isinstance(g, DMP) or f.lev != g.lev: raise UnificationFailed("can't unify %s with %s" % (f, g)) if f.dom == g.dom: return (f.lev, f.dom, f.per, (f.num, f.den), g.rep) else: lev, dom = f.lev, f.dom.unify(g.dom) F = (dmp_convert(f.num, lev, f.dom, dom), dmp_convert(f.den, lev, f.dom, dom)) G = dmp_convert(g.rep, lev, g.dom, dom) def per(num, den, cancel=True, kill=False): if kill: if not lev: return num/den else: lev = lev - 1 if cancel: num, den = dmp_cancel(num, den, lev, dom) return f.__class__.new((num, den), dom, lev) return lev, dom, per, F, G def frac_unify(f, g): """Unify representations of two multivariate fractions. """ if not isinstance(g, DMF) or f.lev != g.lev: raise UnificationFailed("can't unify %s with %s" % (f, g)) if f.dom == g.dom: return (f.lev, f.dom, f.per, (f.num, f.den), (g.num, g.den)) else: lev, dom = f.lev, f.dom.unify(g.dom) F = (dmp_convert(f.num, lev, f.dom, dom), dmp_convert(f.den, lev, f.dom, dom)) G = (dmp_convert(g.num, lev, g.dom, dom), dmp_convert(g.den, lev, g.dom, dom)) def per(num, den, cancel=True, kill=False): if kill: if not lev: return num/den else: lev = lev - 1 if cancel: num, den = dmp_cancel(num, den, lev, dom) return f.__class__.new((num, den), dom, lev) return lev, dom, per, F, G def per(f, num, den, cancel=True, kill=False): """Create a DMF out of the given representation. """ lev, dom = f.lev, f.dom if kill: if not lev: return num/den else: lev -= 1 if cancel: num, den = dmp_cancel(num, den, lev, dom) return f.__class__.new((num, den), dom, lev) def half_per(f, rep, kill=False): """Create a DMP out of the given representation. """ lev = f.lev if kill: if not lev: return rep else: lev -= 1 return DMP(rep, f.dom, lev) @classmethod def zero(cls, lev, dom): return cls.new(0, dom, lev) @classmethod def one(cls, lev, dom): return cls.new(1, dom, lev) def numer(f): """Returns numerator of `f`. """ return f.half_per(f.num) def denom(f): """Returns denominator of `f`. """ return f.half_per(f.den) def cancel(f): """Remove common factors from `f.num` and `f.den`. """ return f.per(f.num, f.den) def neg(f): """Negate all cefficients in `f`. """ return f.per(dmp_neg(f.num, f.lev, f.dom), f.den, cancel=False) def add(f, g): """Add two multivariate fractions `f` and `g`. """ if isinstance(g, DMP): lev, dom, per, (F_num, F_den), G = f.poly_unify(g) num, den = dmp_add_mul(F_num, F_den, G, lev, dom), F_den else: lev, dom, per, F, G = f.frac_unify(g) (F_num, F_den), (G_num, G_den) = F, G num = dmp_add(dmp_mul(F_num, G_den, lev, dom), dmp_mul(F_den, G_num, lev, dom), lev, dom) den = dmp_mul(F_den, G_den, lev, dom) return per(num, den) def sub(f, g): """Subtract two multivariate fractions `f` and `g`. """ if isinstance(g, DMP): lev, dom, per, (F_num, F_den), G = f.poly_unify(g) num, den = dmp_sub_mul(F_num, F_den, G, lev, dom), F_den else: lev, dom, per, F, G = f.frac_unify(g) (F_num, F_den), (G_num, G_den) = F, G num = dmp_sub(dmp_mul(F_num, G_den, lev, dom), dmp_mul(F_den, G_num, lev, dom), lev, dom) den = dmp_mul(F_den, G_den, lev, dom) return per(num, den) def mul(f, g): """Multiply two multivariate fractions `f` and `g`. """ if isinstance(g, DMP): lev, dom, per, (F_num, F_den), G = f.poly_unify(g) num, den = dmp_mul(F_num, G, lev, dom), F_den else: lev, dom, per, F, G = f.frac_unify(g) (F_num, F_den), (G_num, G_den) = F, G num = dmp_mul(F_num, G_num, lev, dom) den = dmp_mul(F_den, G_den, lev, dom) return per(num, den) def pow(f, n): """Raise `f` to a non-negative power `n`. """ if isinstance(n, int): return f.per(dmp_pow(f.num, n, f.lev, f.dom), dmp_pow(f.den, n, f.lev, f.dom), cancel=False) else: raise TypeError("`int` expected, got %s" % type(n)) def quo(f, g): """Computes quotient of fractions `f` and `g`. """ if isinstance(g, DMP): lev, dom, per, (F_num, F_den), G = f.poly_unify(g) num, den = F_num, dmp_mul(F_den, G, lev, dom) else: lev, dom, per, F, G = f.frac_unify(g) (F_num, F_den), (G_num, G_den) = F, G num = dmp_mul(F_num, G_den, lev, dom) den = dmp_mul(F_den, G_num, lev, dom) return per(num, den) exquo = quo def invert(f): """Computes inverse of a fraction `f`. """ return f.per(f.den, f.num, cancel=False) @property def is_zero(f): """Returns `True` if `f` is a zero fraction. """ return dmp_zero_p(f.num, f.lev) @property def is_one(f): """Returns `True` if `f` is a unit fraction. """ return dmp_one_p(f.num, f.lev, f.dom) and \ dmp_one_p(f.den, f.lev, f.dom) def __neg__(f): return f.neg() def __add__(f, g): if isinstance(g, (DMP, DMF)): return f.add(g) try: return f.add(f.half_per(g)) except TypeError: return NotImplemented def __radd__(f, g): return f.__add__(g) def __sub__(f, g): if isinstance(g, (DMP, DMF)): return f.sub(g) try: return f.sub(f.half_per(g)) except TypeError: return NotImplemented def __rsub__(f, g): return (-f).__add__(g) def __mul__(f, g): if isinstance(g, (DMP, DMF)): return f.mul(g) try: return f.mul(f.half_per(g)) except TypeError: return NotImplemented def __rmul__(f, g): return f.__mul__(g) def __pow__(f, n): return f.pow(n) def __div__(f, g): if isinstance(g, (DMP, DMF)): return f.quo(g) try: return f.quo(f.half_per(g)) except TypeError: return NotImplemented __truediv__ = __div__ def __eq__(f, g): try: if isinstance(g, DMP): _, _, _, (F_num, F_den), G = f.poly_unify(g) if f.lev == g.lev: return dmp_one_p(F_den, f.lev, f.dom) and F_num == G else: _, _, _, F, G = f.frac_unify(g) if f.lev == g.lev: return F == G except UnificationFailed: pass return False def __ne__(f, g): try: if isinstance(g, DMP): _, _, _, (F_num, F_den), G = f.poly_unify(g) if f.lev == g.lev: return not (dmp_one_p(F_den, f.lev, f.dom) and F_num == G) else: _, _, _, F, G = f.frac_unify(g) if f.lev == g.lev: return F != G except UnificationFailed: pass return True def __lt__(f, g): _, _, _, F, G = f.frac_unify(g) return F.__lt__(G) def __le__(f, g): _, _, _, F, G = f.frac_unify(g) return F.__le__(G) def __gt__(f, g): _, _, _, F, G = f.frac_unify(g) return F.__gt__(G) def __ge__(f, g): _, _, _, F, G = f.frac_unify(g) return F.__ge__(G) def __nonzero__(f): return not dmp_zero_p(f.num, f.lev) def init_normal_ANP(rep, mod, dom): return ANP(dup_normal(rep, dom), dup_normal(mod, dom), dom) class ANP(object): """Dense Algebraic Number Polynomials over a field. """ __slots__ = ['rep', 'mod', 'dom'] def __init__(self, rep, mod, dom): if type(rep) is dict: self.rep = dup_from_dict(rep, dom) else: if type(rep) is not list: rep = [dom.convert(rep)] self.rep = dup_strip(rep) if isinstance(mod, DMP): self.mod = mod.rep else: if type(mod) is dict: self.mod = dup_from_dict(mod, dom) else: self.mod = dup_strip(mod) self.dom = dom def __repr__(f): return "%s(%s, %s, %s)" % (f.__class__.__name__, f.rep, f.mod, f.dom) def __hash__(f): return hash((f.__class__.__name__, f.to_tuple(), dmp_to_tuple(f.mod, 0), f.dom)) def __getstate__(self): return (self.rep, self.mod, self.dom) def __getnewargs__(self): return (self.rep, self.mod, self.dom) def unify(f, g): """Unify representations of two algebraic numbers. """ if not isinstance(g, ANP) or f.mod != g.mod: raise UnificationFailed("can't unify %s with %s" % (f, g)) if f.dom == g.dom: return f.dom, f.per, f.rep, g.rep, f.mod else: dom = f.dom.unify(g.dom) F = dup_convert(f.rep, f.dom, dom) G = dup_convert(g.rep, g.dom, dom) if dom != f.dom and dom != g.dom: mod = dup_convert(f.mod, f.dom, dom) else: if dom == f.dom: H = f.mod else: H = g.mod per = lambda rep: ANP(rep, mod, dom) return dom, per, F, G, mod def per(f, rep, mod=None, dom=None): return ANP(rep, mod or f.mod, dom or f.dom) @classmethod def zero(cls, mod, dom): return ANP(0, mod, dom) @classmethod def one(cls, mod, dom): return ANP(1, mod, dom) def to_dict(f): """Convert `f` to a dict representation with native coefficients. """ return dmp_to_dict(f.rep, 0, f.dom) def to_sympy_dict(f): """Convert `f` to a dict representation with SymPy coefficients. """ rep = dmp_to_dict(f.rep, 0, f.dom) for k, v in rep.iteritems(): rep[k] = f.dom.to_sympy(v) return rep def to_list(f): """Convert `f` to a list representation with native coefficients. """ return f.rep def to_sympy_list(f): """Convert `f` to a list representation with SymPy coefficients. """ return [ f.dom.to_sympy(c) for c in f.rep ] def to_tuple(f): """ Convert `f` to a tuple representation with native coefficients. This is needed for hashing. """ return dmp_to_tuple(f.rep, 0) @classmethod def from_list(cls, rep, mod, dom): return ANP(dup_strip(map(dom.convert, rep)), mod, dom) def neg(f): return f.per(dup_neg(f.rep, f.dom)) def add(f, g): dom, per, F, G, mod = f.unify(g) return per(dup_add(F, G, dom)) def sub(f, g): dom, per, F, G, mod = f.unify(g) return per(dup_sub(F, G, dom)) def mul(f, g): dom, per, F, G, mod = f.unify(g) return per(dup_rem(dup_mul(F, G, dom), mod, dom)) def pow(f, n): """Raise `f` to a non-negative power `n`. """ if isinstance(n, int): if n < 0: F, n = dup_invert(f.rep, f.mod, f.dom), -n else: F = f.rep return f.per(dup_rem(dup_pow(F, n, f.dom), f.mod, f.dom)) else: raise TypeError("`int` expected, got %s" % type(n)) def div(f, g): dom, per, F, G, mod = f.unify(g) return (per(dup_rem(dup_mul(F, dup_invert(G, mod, dom), dom), mod, dom)), self.zero(mod, dom)) def rem(f, g): dom, _, _, _, mod = f.unify(g) return self.zero(mod, dom) def quo(f, g): dom, per, F, G, mod = f.unify(g) return per(dup_rem(dup_mul(F, dup_invert(G, mod, dom), dom), mod, dom)) exquo = quo def LC(f): """Returns the leading coefficent of `f`. """ return dup_LC(f.rep, f.dom) def TC(f): """Returns the trailing coefficent of `f`. """ return dup_TC(f.rep, f.dom) @property def is_zero(f): """Returns `True` if `f` is a zero algebraic number. """ return not f @property def is_one(f): """Returns `True` if `f` is a unit algebraic number. """ return f.rep == [f.dom.one] @property def is_ground(f): """Returns `True` if `f` is an element of the ground domain. """ return not f.rep or len(f.rep) == 1 def __neg__(f): return f.neg() def __add__(f, g): if isinstance(g, ANP): return f.add(g) else: try: return f.add(f.per(g)) except TypeError: return NotImplemented def __radd__(f, g): return f.__add__(g) def __sub__(f, g): if isinstance(g, ANP): return f.sub(g) else: try: return f.sub(f.per(g)) except TypeError: return NotImplemented def __rsub__(f, g): return (-f).__add__(g) def __mul__(f, g): if isinstance(g, ANP): return f.mul(g) else: try: return f.mul(f.per(g)) except TypeError: return NotImplemented def __rmul__(f, g): return f.__mul__(g) def __pow__(f, n): return f.pow(n) def __divmod__(f, g): return f.div(g) def __mod__(f, g): return f.rem(g) def __div__(f, g): if isinstance(g, ANP): return f.quo(g) else: try: return f.quo(f.per(g)) except TypeError: return NotImplemented __truediv__ = __div__ def __eq__(f, g): try: _, _, F, G, _ = f.unify(g) return F == G except UnificationFailed: return False def __ne__(f, g): try: _, _, F, G, _ = f.unify(g) return F != G except UnificationFailed: return True def __lt__(f, g): _, _, F, G, _ = f.unify(g) return F.__lt__(G) def __le__(f, g): _, _, F, G, _ = f.unify(g) return F.__le__(G) def __gt__(f, g): _, _, F, G, _ = f.unify(g) return F.__gt__(G) def __ge__(f, g): _, _, F, G, _ = f.unify(g) return F.__ge__(G) def __nonzero__(f): return bool(f.rep) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/densearith.py0000644000175000017500000010762312014170666024756 0ustar georgeskgeorgesk"""Arithmetics for dense recursive polynomials in ``K[x]`` or ``K[X]``. """ from sympy.polys.densebasic import ( dup_LC, dup_TC, dmp_LC, dup_degree, dmp_degree, dup_normal, dmp_normal, dup_strip, dmp_strip, dmp_zero_p, dmp_zero, dmp_one_p, dmp_one, dmp_ground, dmp_zeros) from sympy.polys.polyerrors import ( ExactQuotientFailed) from sympy.utilities import cythonized @cythonized("i,n,m") def dup_add_term(f, c, i, K): """ Add ``c*x**i`` to ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_add_term >>> f = ZZ.map([1, 0, -1]) >>> dup_add_term(f, ZZ(2), 4, ZZ) [2, 0, 1, 0, -1] """ if not c: return f n = len(f) m = n-i-1 if i == n-1: return dup_strip([f[0]+c] + f[1:]) else: if i >= n: return [c] + [K.zero]*(i-n) + f else: return f[:m] + [f[m]+c] + f[m+1:] @cythonized("i,u,v,n,m") def dmp_add_term(f, c, i, u, K): """ Add ``c(x_2..x_u)*x_0**i`` to ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_add_term >>> f = ZZ.map([[1, 0], [1]]) >>> c = ZZ.map([2]) >>> dmp_add_term(f, c, 2, 1, ZZ) [[2], [1, 0], [1]] """ if not u: return dup_add_term(f, c, i, K) v = u-1 if dmp_zero_p(c, v): return f n = len(f) m = n-i-1 if i == n-1: return dmp_strip([dmp_add(f[0], c, v, K)] + f[1:], u) else: if i >= n: return [c] + dmp_zeros(i-n, v, K) + f else: return f[:m] + [dmp_add(f[m], c, v, K)] + f[m+1:] @cythonized("i,n,m") def dup_sub_term(f, c, i, K): """ Subtract ``c*x**i`` from ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_sub_term >>> f = ZZ.map([2, 0, 1, 0, -1]) >>> dup_sub_term(f, ZZ(2), 4, ZZ) [1, 0, -1] """ if not c: return f n = len(f) m = n-i-1 if i == n-1: return dup_strip([f[0]-c] + f[1:]) else: if i >= n: return [-c] + [K.zero]*(i-n) + f else: return f[:m] + [f[m]-c] + f[m+1:] @cythonized("i,u,v,n,m") def dmp_sub_term(f, c, i, u, K): """ Subtract ``c(x_2..x_u)*x_0**i`` from ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_sub_term >>> f = ZZ.map([[2], [1, 0], [1]]) >>> c = ZZ.map([2]) >>> dmp_sub_term(f, c, 2, 1, ZZ) [[1, 0], [1]] """ if not u: return dup_add_term(f, -c, i, K) v = u-1 if dmp_zero_p(c, v): return f n = len(f) m = n-i-1 if i == n-1: return dmp_strip([dmp_sub(f[0], c, v, K)] + f[1:], u) else: if i >= n: return [dmp_neg(c, v, K)] + dmp_zeros(i-n, v, K) + f else: return f[:m] + [dmp_sub(f[m], c, v, K)] + f[m+1:] @cythonized("i") def dup_mul_term(f, c, i, K): """ Multiply ``f`` by ``c*x**i`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_mul_term >>> f = ZZ.map([1, 0, -1]) >>> dup_mul_term(f, ZZ(3), 2, ZZ) [3, 0, -3, 0, 0] """ if not c or not f: return [] else: return [ cf * c for cf in f ] + [K.zero]*i @cythonized("i,u,v") def dmp_mul_term(f, c, i, u, K): """ Multiply ``f`` by ``c(x_2..x_u)*x_0**i`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_mul_term >>> f = ZZ.map([[1, 0], [1], []]) >>> c = ZZ.map([3, 0]) >>> dmp_mul_term(f, c, 2, 1, ZZ) [[3, 0, 0], [3, 0], [], [], []] """ if not u: return dup_mul_term(f, c, i, K) v = u-1 if dmp_zero_p(f, u): return f if dmp_zero_p(c, v): return dmp_zero(u) else: return [ dmp_mul(cf, c, v, K) for cf in f ] + dmp_zeros(i, v, K) def dup_add_ground(f, c, K): """ Add an element of the ground domain to ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_add_ground >>> f = ZZ.map([1, 2, 3, 4]) >>> dup_add_ground(f, ZZ(4), ZZ) [1, 2, 3, 8] """ return dup_add_term(f, c, 0, K) def dmp_add_ground(f, c, u, K): """ Add an element of the ground domain to ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_add_ground >>> f = ZZ.map([[1], [2], [3], [4]]) >>> dmp_add_ground(f, ZZ(4), 1, ZZ) [[1], [2], [3], [8]] """ return dmp_add_term(f, dmp_ground(c, u-1), 0, u, K) def dup_sub_ground(f, c, K): """ Subtract an element of the ground domain from ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_sub_ground >>> f = ZZ.map([1, 2, 3, 4]) >>> dup_sub_ground(f, ZZ(4), ZZ) [1, 2, 3, 0] """ return dup_sub_term(f, c, 0, K) def dmp_sub_ground(f, c, u, K): """ Subtract an element of the ground domain from ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_sub_ground >>> f = ZZ.map([[1], [2], [3], [4]]) >>> dmp_sub_ground(f, ZZ(4), 1, ZZ) [[1], [2], [3], []] """ return dmp_sub_term(f, dmp_ground(c, u-1), 0, u, K) def dup_mul_ground(f, c, K): """ Multiply ``f`` by a constant value in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_mul_ground >>> f = ZZ.map([1, 2, -1]) >>> dup_mul_ground(f, ZZ(3), ZZ) [3, 6, -3] """ if not c or not f: return [] else: return [ cf * c for cf in f ] @cythonized("u,v") def dmp_mul_ground(f, c, u, K): """ Multiply ``f`` by a constant value in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_mul_ground >>> f = ZZ.map([[2], [2, 0]]) >>> dmp_mul_ground(f, ZZ(3), 1, ZZ) [[6], [6, 0]] """ if not u: return dup_mul_ground(f, c, K) v = u-1 return [ dmp_mul_ground(cf, c, v, K) for cf in f ] def dup_quo_ground(f, c, K): """ Quotient by a constant in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dup_quo_ground >>> f = ZZ.map([3, 0, 2]) >>> g = QQ.map([3, 0, 2]) >>> dup_quo_ground(f, ZZ(2), ZZ) [1, 0, 1] >>> dup_quo_ground(g, QQ(2), QQ) [3/2, 0/1, 1/1] """ if not c: raise ZeroDivisionError('polynomial division') if not f: return f if K.has_Field or not K.is_Exact: return [ K.quo(cf, c) for cf in f ] else: return [ cf // c for cf in f ] @cythonized("u,v") def dmp_quo_ground(f, c, u, K): """ Quotient by a constant in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dmp_quo_ground >>> f = ZZ.map([[2, 0], [3], []]) >>> g = QQ.map([[2, 0], [3], []]) >>> dmp_quo_ground(f, ZZ(2), 1, ZZ) [[1, 0], [1], []] >>> dmp_quo_ground(g, QQ(2), 1, QQ) [[1/1, 0/1], [3/2], []] """ if not u: return dup_quo_ground(f, c, K) v = u-1 return [ dmp_quo_ground(cf, c, v, K) for cf in f ] def dup_exquo_ground(f, c, K): """ Exact quotient by a constant in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dup_exquo_ground >>> f = QQ.map([1, 0, 2]) >>> dup_exquo_ground(f, QQ(2), QQ) [1/2, 0/1, 1/1] """ if not c: raise ZeroDivisionError('polynomial division') if not f: return f return [ K.exquo(cf, c) for cf in f ] @cythonized("u,v") def dmp_exquo_ground(f, c, u, K): """ Exact quotient by a constant in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dmp_exquo_ground >>> f = QQ.map([[1, 0], [2], []]) >>> dmp_exquo_ground(f, QQ(2), 1, QQ) [[1/2, 0/1], [1/1], []] """ if not u: return dup_exquo_ground(f, c, K) v = u-1 return [ dmp_exquo_ground(cf, c, v, K) for cf in f ] @cythonized("n") def dup_lshift(f, n, K): """ Efficiently multiply ``f`` by ``x**n`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_lshift >>> f = ZZ.map([1, 0, 1]) >>> dup_lshift(f, 2, ZZ) [1, 0, 1, 0, 0] """ if not f: return f else: return f + [K.zero]*n @cythonized("n") def dup_rshift(f, n, K): """ Efficiently divide ``f`` by ``x**n`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_rshift >>> f = ZZ.map([1, 0, 1, 0, 0]) >>> g = ZZ.map([1, 0, 1, 0, 2]) >>> dup_rshift(f, 2, ZZ) [1, 0, 1] >>> dup_rshift(g, 2, ZZ) [1, 0, 1] """ return f[:-n] def dup_abs(f, K): """ Make all coefficients positive in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_abs >>> f = ZZ.map([1, 0, -1]) >>> dup_abs(f, ZZ) [1, 0, 1] """ return [ K.abs(coeff) for coeff in f ] @cythonized("u,v") def dmp_abs(f, u, K): """ Make all coefficients positive in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_abs >>> f = ZZ.map([[1, 0], [-1], []]) >>> dmp_abs(f, 1, ZZ) [[1, 0], [1], []] """ if not u: return dup_abs(f, K) v = u-1 return [ dmp_abs(cf, v, K) for cf in f ] def dup_neg(f, K): """ Negate a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_neg >>> f = ZZ.map([1, 0, -1]) >>> dup_neg(f, ZZ) [-1, 0, 1] """ return [ -coeff for coeff in f ] @cythonized("u,v") def dmp_neg(f, u, K): """ Negate a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_neg >>> f = ZZ.map([[1, 0], [-1], []]) >>> dmp_neg(f, 1, ZZ) [[-1, 0], [1], []] """ if not u: return dup_neg(f, K) v = u-1 return [ dmp_neg(cf, u-1, K) for cf in f ] @cythonized("df,dg,k") def dup_add(f, g, K): """ Add dense polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_add >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -2]) >>> dup_add(f, g, ZZ) [1, 1, -3] """ if not f: return g if not g: return f df = dup_degree(f) dg = dup_degree(g) if df == dg: return dup_strip([ a + b for a, b in zip(f, g) ]) else: k = abs(df - dg) if df > dg: h, f = f[:k], f[k:] else: h, g = g[:k], g[k:] return h + [ a + b for a, b in zip(f, g) ] @cythonized("u,v,df,dg,k") def dmp_add(f, g, u, K): """ Add dense polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_add >>> f = ZZ.map([[1], [], [1, 0]]) >>> g = ZZ.map([[1, 0], [1], []]) >>> dmp_add(f, g, 1, ZZ) [[1, 1], [1], [1, 0]] """ if not u: return dup_add(f, g, K) df = dmp_degree(f, u) if df < 0: return g dg = dmp_degree(g, u) if dg < 0: return f v = u-1 if df == dg: return dmp_strip([ dmp_add(a, b, v, K) for a, b in zip(f, g) ], u) else: k = abs(df - dg) if df > dg: h, f = f[:k], f[k:] else: h, g = g[:k], g[k:] return h + [ dmp_add(a, b, v, K) for a, b in zip(f, g) ] @cythonized("df,dg,k") def dup_sub(f, g, K): """ Subtract dense polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_sub >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -2]) >>> dup_sub(f, g, ZZ) [1, -1, 1] """ if not f: return dup_neg(g, K) if not g: return f df = dup_degree(f) dg = dup_degree(g) if df == dg: return dup_strip([ a - b for a, b in zip(f, g) ]) else: k = abs(df - dg) if df > dg: h, f = f[:k], f[k:] else: h, g = dup_neg(g[:k], K), g[k:] return h + [ a - b for a, b in zip(f, g) ] @cythonized("u,v,df,dg,k") def dmp_sub(f, g, u, K): """ Subtract dense polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_sub >>> f = ZZ.map([[1], [], [1, 0]]) >>> g = ZZ.map([[1, 0], [1], []]) >>> dmp_sub(f, g, 1, ZZ) [[-1, 1], [-1], [1, 0]] """ if not u: return dup_sub(f, g, K) df = dmp_degree(f, u) if df < 0: return dmp_neg(g, u, K) dg = dmp_degree(g, u) if dg < 0: return f v = u-1 if df == dg: return dmp_strip([ dmp_sub(a, b, v, K) for a, b in zip(f, g) ], u) else: k = abs(df - dg) if df > dg: h, f = f[:k], f[k:] else: h, g = dmp_neg(g[:k], u, K), g[k:] return h + [ dmp_sub(a, b, v, K) for a, b in zip(f, g) ] def dup_add_mul(f, g, h, K): """ Returns ``f + g*h`` where ``f, g, h`` are in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_add_mul >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -2]) >>> h = ZZ.map([1, 2]) >>> dup_add_mul(f, g, h, ZZ) [2, 0, -5] """ return dup_add(f, dup_mul(g, h, K), K) @cythonized("u") def dmp_add_mul(f, g, h, u, K): """ Returns ``f + g*h`` where ``f, g, h`` are in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_add_mul >>> f = ZZ.map([[1], [], [1, 0]]) >>> g = ZZ.map([[1], []]) >>> h = ZZ.map([[1], [2]]) >>> dmp_add_mul(f, g, h, 1, ZZ) [[2], [2], [1, 0]] """ return dmp_add(f, dmp_mul(g, h, u, K), u, K) def dup_sub_mul(f, g, h, K): """ Returns ``f - g*h`` where ``f, g, h`` are in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_sub_mul >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -2]) >>> h = ZZ.map([1, 2]) >>> dup_sub_mul(f, g, h, ZZ) [3] """ return dup_sub(f, dup_mul(g, h, K), K) @cythonized("u") def dmp_sub_mul(f, g, h, u, K): """ Returns ``f - g*h`` where ``f, g, h`` are in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_sub_mul >>> f = ZZ.map([[1], [], [1, 0]]) >>> g = ZZ.map([[1], []]) >>> h = ZZ.map([[1], [2]]) >>> dmp_sub_mul(f, g, h, 1, ZZ) [[-2], [1, 0]] """ return dmp_sub(f, dmp_mul(g, h, u, K), u, K) @cythonized("df,dg,i,j") def dup_mul(f, g, K): """ Multiply dense polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_mul >>> f = ZZ.map([1, -2]) >>> g = ZZ.map([1, 2]) >>> dup_mul(f, g, ZZ) [1, 0, -4] """ if f == g: return dup_sqr(f, K) if not (f and g): return [] df = dup_degree(f) dg = dup_degree(g) h = [] for i in xrange(0, df+dg+1): coeff = K.zero for j in xrange(max(0, i-dg), min(df, i)+1): coeff += f[j]*g[i-j] h.append(coeff) return dup_strip(h) @cythonized("u,v,df,dg,i,j") def dmp_mul(f, g, u, K): """ Multiply dense polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_mul >>> f = ZZ.map([[1, 0], [1]]) >>> g = ZZ.map([[1], []]) >>> dmp_mul(f, g, 1, ZZ) [[1, 0], [1], []] """ if not u: return dup_mul(f, g, K) if f == g: return dmp_sqr(f, u, K) df = dmp_degree(f, u) if df < 0: return f dg = dmp_degree(g, u) if dg < 0: return g h, v = [], u-1 for i in xrange(0, df+dg+1): coeff = dmp_zero(v) for j in xrange(max(0, i-dg), min(df, i)+1): coeff = dmp_add(coeff, dmp_mul(f[j], g[i-j], v, K), v, K) h.append(coeff) return dmp_strip(h, u) @cythonized("df,jmin,jmax,n,i,j") def dup_sqr(f, K): """ Square dense polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_sqr >>> f = ZZ.map([1, 0, 1]) >>> dup_sqr(f, ZZ) [1, 0, 2, 0, 1] """ df, h = dup_degree(f), [] for i in xrange(0, 2*df+1): c = K.zero jmin = max(0, i-df) jmax = min(i, df) n = jmax - jmin + 1 jmax = jmin + n // 2 - 1 for j in xrange(jmin, jmax+1): c += f[j]*f[i-j] c += c if n & 1: elem = f[jmax+1] c += elem**2 h.append(c) return dup_strip(h) @cythonized("u,v,df,jmin,jmax,n,i,j") def dmp_sqr(f, u, K): """ Square dense polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_sqr >>> f = ZZ.map([[1], [1, 0], [1, 0, 0]]) >>> dmp_sqr(f, 1, ZZ) [[1], [2, 0], [3, 0, 0], [2, 0, 0, 0], [1, 0, 0, 0, 0]] """ if not u: return dup_sqr(f, K) df = dmp_degree(f, u) if df < 0: return f h, v = [], u-1 for i in xrange(0, 2*df+1): c = dmp_zero(v) jmin = max(0, i-df) jmax = min(i, df) n = jmax - jmin + 1 jmax = jmin + n // 2 - 1 for j in xrange(jmin, jmax+1): c = dmp_add(c, dmp_mul(f[j], f[i-j], v, K), v, K) c = dmp_mul_ground(c, K(2), v, K) if n & 1: elem = dmp_sqr(f[jmax+1], v, K) c = dmp_add(c, elem, v, K) h.append(c) return dmp_strip(h, u) @cythonized("n,m") def dup_pow(f, n, K): """ Raise ``f`` to the ``n``-th power in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_pow >>> dup_pow([ZZ(1), -ZZ(2)], 3, ZZ) [1, -6, 12, -8] """ if not n: return [K.one] if n < 0: raise ValueError("can't raise polynomial to a negative power") if n == 1 or not f or f == [K.one]: return f g = [K.one] while True: n, m = n//2, n if m % 2: g = dup_mul(g, f, K) if not n: break f = dup_sqr(f, K) return g @cythonized("u,n,m") def dmp_pow(f, n, u, K): """ Raise ``f`` to the ``n``-th power in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_pow >>> f = ZZ.map([[1, 0], [1]]) >>> dmp_pow(f, 3, 1, ZZ) [[1, 0, 0, 0], [3, 0, 0], [3, 0], [1]] """ if not u: return dup_pow(f, n, K) if not n: return dmp_one(u, K) if n < 0: raise ValueError("can't raise polynomial to a negative power") if n == 1 or dmp_zero_p(f, u) or dmp_one_p(f, u, K): return f g = dmp_one(u, K) while True: n, m = n//2, n if m & 1: g = dmp_mul(g, f, u, K) if not n: break f = dmp_sqr(f, u, K) return g @cythonized("df,dg,dr,N,j") def dup_pdiv(f, g, K): """ Polynomial pseudo-division in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_pdiv >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_pdiv(f, g, ZZ) ([2, 4], [20]) """ df = dup_degree(f) dg = dup_degree(g) q, r = [], f if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r N = df - dg + 1 lc_g = dup_LC(g, K) while True: dr = dup_degree(r) if dr < dg: break lc_r = dup_LC(r, K) j, N = dr-dg, N-1 Q = dup_mul_ground(q, lc_g, K) q = dup_add_term(Q, lc_r, j, K) R = dup_mul_ground(r, lc_g, K) G = dup_mul_term(g, lc_r, j, K) r = dup_sub(R, G, K) c = lc_g**N q = dup_mul_ground(q, c, K) r = dup_mul_ground(r, c, K) return q, r @cythonized("df,dg,dr,N,j") def dup_prem(f, g, K): """ Polynomial pseudo-remainder in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_prem >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_prem(f, g, ZZ) [20] """ df = dup_degree(f) dg = dup_degree(g) r = f if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return r N = df - dg + 1 lc_g = dup_LC(g, K) while True: dr = dup_degree(r) if dr < dg: break lc_r = dup_LC(r, K) j, N = dr-dg, N-1 R = dup_mul_ground(r, lc_g, K) G = dup_mul_term(g, lc_r, j, K) r = dup_sub(R, G, K) return dup_mul_ground(r, lc_g**N, K) def dup_pquo(f, g, K): """ Polynomial exact pseudo-quotient in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_pquo >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([2, -2]) >>> dup_pquo(f, g, ZZ) [2, 2] >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_pquo(f, g, ZZ) [2, 4] """ return dup_pdiv(f, g, K)[0] def dup_pexquo(f, g, K): """ Polynomial pseudo-quotient in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_pexquo >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([2, -2]) >>> dup_pexquo(f, g, ZZ) [2, 2] >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_pexquo(f, g, ZZ) Traceback (most recent call last): ... ExactQuotientFailed: [2, -4] does not divide [1, 0, 1] """ q, r = dup_pdiv(f, g, K) if not r: return q else: raise ExactQuotientFailed(f, g) @cythonized("u,df,dg,dr,N,j") def dmp_pdiv(f, g, u, K): """ Polynomial pseudo-division in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_pdiv >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2]]) >>> dmp_pdiv(f, g, 1, ZZ) ([[2], [2, -2]], [[-4, 4]]) """ if not u: return dup_pdiv(f, g, K) df = dmp_degree(f, u) dg = dmp_degree(g, u) if dg < 0: raise ZeroDivisionError("polynomial division") q, r = dmp_zero(u), f if df < dg: return q, r N = df - dg + 1 lc_g = dmp_LC(g, K) while True: dr = dmp_degree(r, u) if dr < dg: break lc_r = dmp_LC(r, K) j, N = dr-dg, N-1 Q = dmp_mul_term(q, lc_g, 0, u, K) q = dmp_add_term(Q, lc_r, j, u, K) R = dmp_mul_term(r, lc_g, 0, u, K) G = dmp_mul_term(g, lc_r, j, u, K) r = dmp_sub(R, G, u, K) c = dmp_pow(lc_g, N, u-1, K) q = dmp_mul_term(q, c, 0, u, K) r = dmp_mul_term(r, c, 0, u, K) return q, r @cythonized("u,df,dg,dr,N,j") def dmp_prem(f, g, u, K): """ Polynomial pseudo-remainder in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_prem >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2]]) >>> dmp_prem(f, g, 1, ZZ) [[-4, 4]] """ if not u: return dup_prem(f, g, K) df = dmp_degree(f, u) dg = dmp_degree(g, u) if dg < 0: raise ZeroDivisionError("polynomial division") r = f if df < dg: return r N = df - dg + 1 lc_g = dmp_LC(g, K) while True: dr = dmp_degree(r, u) if dr < dg: break lc_r = dmp_LC(r, K) j, N = dr-dg, N-1 R = dmp_mul_term(r, lc_g, 0, u, K) G = dmp_mul_term(g, lc_r, j, u, K) r = dmp_sub(R, G, u, K) c = dmp_pow(lc_g, N, u-1, K) return dmp_mul_term(r, c, 0, u, K) def dmp_pquo(f, g, u, K): """ Polynomial exact pseudo-quotient in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_pquo >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2, 0]]) >>> h = ZZ.map([[2], [2]]) >>> dmp_pquo(f, g, 1, ZZ) [[2], []] >>> dmp_pquo(f, h, 1, ZZ) [[2], [2, -2]] """ return dmp_pdiv(f, g, u, K)[0] def dmp_pexquo(f, g, u, K): """ Polynomial pseudo-quotient in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_pexquo >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2, 0]]) >>> h = ZZ.map([[2], [2]]) >>> dmp_pexquo(f, g, 1, ZZ) [[2], []] >>> dmp_pexquo(f, h, 1, ZZ) Traceback (most recent call last): ... ExactQuotientFailed: [[2], [2]] does not divide [[1], [1, 0], []] """ q, r = dmp_pdiv(f, g, u, K) if dmp_zero_p(r, u): return q else: raise ExactQuotientFailed(f, g) @cythonized("df,dg,dr,j") def dup_rr_div(f, g, K): """ Univariate division with remainder over a ring. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_rr_div >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_rr_div(f, g, ZZ) ([], [1, 0, 1]) """ df = dup_degree(f) dg = dup_degree(g) q, r = [], f if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r lc_g = dup_LC(g, K) while True: dr = dup_degree(r) if dr < dg: break lc_r = dup_LC(r, K) if lc_r % lc_g: break c = K.exquo(lc_r, lc_g) j = dr - dg q = dup_add_term(q, c, j, K) h = dup_mul_term(g, c, j, K) r = dup_sub(r, h, K) return q, r @cythonized("u,df,dg,dr,j") def dmp_rr_div(f, g, u, K): """ Multivariate division with remainder over a ring. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_rr_div >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2]]) >>> dmp_rr_div(f, g, 1, ZZ) ([[]], [[1], [1, 0], []]) """ if not u: return dup_rr_div(f, g, K) df = dmp_degree(f, u) dg = dmp_degree(g, u) if dg < 0: raise ZeroDivisionError("polynomial division") q, r = dmp_zero(u), f if df < dg: return q, r lc_g, v = dmp_LC(g, K), u-1 while True: dr = dmp_degree(r, u) if dr < dg: break lc_r = dmp_LC(r, K) c, R = dmp_rr_div(lc_r, lc_g, v, K) if not dmp_zero_p(R, v): break j = dr - dg q = dmp_add_term(q, c, j, u, K) h = dmp_mul_term(g, c, j, u, K) r = dmp_sub(r, h, u, K) return q, r @cythonized("df,dg,dr,j") def dup_ff_div(f, g, K): """ Polynomial division with remainder over a field. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densearith import dup_ff_div >>> f = QQ.map([1, 0, 1]) >>> g = QQ.map([2, -4]) >>> dup_ff_div(f, g, QQ) ([1/2, 1/1], [5/1]) """ df = dup_degree(f) dg = dup_degree(g) q, r = [], f if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return q, r lc_g = dup_LC(g, K) while True: dr = dup_degree(r) if dr < dg: break lc_r = dup_LC(r, K) c = K.exquo(lc_r, lc_g) j = dr - dg q = dup_add_term(q, c, j, K) h = dup_mul_term(g, c, j, K) r = dup_sub(r, h, K) if not K.is_Exact: r = dup_normal(r, K) return q, r @cythonized("u,df,dg,dr,j") def dmp_ff_div(f, g, u, K): """ Polynomial division with remainder over a field. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densearith import dmp_ff_div >>> f = QQ.map([[1], [1, 0], []]) >>> g = QQ.map([[2], [2]]) >>> dmp_ff_div(f, g, 1, QQ) ([[1/2], [1/2, -1/2]], [[-1/1, 1/1]]) """ if not u: return dup_ff_div(f, g, K) df = dmp_degree(f, u) dg = dmp_degree(g, u) if dg < 0: raise ZeroDivisionError("polynomial division") q, r = dmp_zero(u), f if df < dg: return q, r lc_g, v = dmp_LC(g, K), u-1 while True: dr = dmp_degree(r, u) if dr < dg: break lc_r = dmp_LC(r, K) c, R = dmp_ff_div(lc_r, lc_g, v, K) if not dmp_zero_p(R, v): break j = dr - dg q = dmp_add_term(q, c, j, u, K) h = dmp_mul_term(g, c, j, u, K) r = dmp_sub(r, h, u, K) return q, r def dup_div(f, g, K): """ Polynomial division with remainder in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dup_div >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_div(f, g, ZZ) ([], [1, 0, 1]) >>> f = QQ.map([1, 0, 1]) >>> g = QQ.map([2, -4]) >>> dup_div(f, g, QQ) ([1/2, 1/1], [5/1]) """ if K.has_Field or not K.is_Exact: return dup_ff_div(f, g, K) else: return dup_rr_div(f, g, K) def dup_rem(f, g, K): """ Returns polynomial remainder in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dup_rem >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_rem(f, g, ZZ) [1, 0, 1] >>> f = QQ.map([1, 0, 1]) >>> g = QQ.map([2, -4]) >>> dup_rem(f, g, QQ) [5/1] """ return dup_div(f, g, K)[1] def dup_quo(f, g, K): """ Returns exact polynomial quotient in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dup_quo >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_quo(f, g, ZZ) [] >>> f = QQ.map([1, 0, 1]) >>> g = QQ.map([2, -4]) >>> dup_quo(f, g, QQ) [1/2, 1/1] """ return dup_div(f, g, K)[0] def dup_exquo(f, g, K): """ Returns polynomial quotient in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_exquo >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -1]) >>> dup_exquo(f, g, ZZ) [1, 1] >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([2, -4]) >>> dup_exquo(f, g, ZZ) Traceback (most recent call last): ... ExactQuotientFailed: [2, -4] does not divide [1, 0, 1] """ q, r = dup_div(f, g, K) if not r: return q else: raise ExactQuotientFailed(f, g) @cythonized("u") def dmp_div(f, g, u, K): """ Polynomial division with remainder in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dmp_div >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2]]) >>> dmp_div(f, g, 1, ZZ) ([[]], [[1], [1, 0], []]) >>> f = QQ.map([[1], [1, 0], []]) >>> g = QQ.map([[2], [2]]) >>> dmp_div(f, g, 1, QQ) ([[1/2], [1/2, -1/2]], [[-1/1, 1/1]]) """ if K.has_Field or not K.is_Exact: return dmp_ff_div(f, g, u, K) else: return dmp_rr_div(f, g, u, K) @cythonized("u") def dmp_rem(f, g, u, K): """ Returns polynomial remainder in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dmp_rem >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2]]) >>> dmp_rem(f, g, 1, ZZ) [[1], [1, 0], []] >>> f = QQ.map([[1], [1, 0], []]) >>> g = QQ.map([[2], [2]]) >>> dmp_rem(f, g, 1, QQ) [[-1/1, 1/1]] """ return dmp_div(f, g, u, K)[1] @cythonized("u") def dmp_quo(f, g, u, K): """ Returns exact polynomial quotient in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densearith import dmp_quo >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[2], [2]]) >>> dmp_quo(f, g, 1, ZZ) [[]] >>> f = QQ.map([[1], [1, 0], []]) >>> g = QQ.map([[2], [2]]) >>> dmp_quo(f, g, 1, QQ) [[1/2], [1/2, -1/2]] """ return dmp_div(f, g, u, K)[0] @cythonized("u") def dmp_exquo(f, g, u, K): """ Returns polynomial quotient in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_exquo >>> f = ZZ.map([[1], [1, 0], []]) >>> g = ZZ.map([[1], [1, 0]]) >>> h = ZZ.map([[2], [2]]) >>> dmp_exquo(f, g, 1, ZZ) [[1], []] >>> dmp_exquo(f, h, 1, ZZ) Traceback (most recent call last): ... ExactQuotientFailed: [[2], [2]] does not divide [[1], [1, 0], []] """ q, r = dmp_div(f, g, u, K) if dmp_zero_p(r, u): return q else: raise ExactQuotientFailed(f, g) def dup_max_norm(f, K): """ Returns maximum norm of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_max_norm >>> f = ZZ.map([-1, 2, -3]) >>> dup_max_norm(f, ZZ) 3 """ if not f: return K.zero else: return max(dup_abs(f, K)) @cythonized("u,v") def dmp_max_norm(f, u, K): """ Returns maximum norm of a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_max_norm >>> f = ZZ.map([[2, -1], [-3]]) >>> dmp_max_norm(f, 1, ZZ) 3 """ if not u: return dup_max_norm(f, K) v = u-1 return max([ dmp_max_norm(c, v, K) for c in f ]) def dup_l1_norm(f, K): """ Returns l1 norm of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_l1_norm >>> f = ZZ.map([2, -3, 0, 1]) >>> dup_l1_norm(f, ZZ) 6 """ if not f: return K.zero else: return sum(dup_abs(f, K)) @cythonized("u,v") def dmp_l1_norm(f, u, K): """ Returns l1 norm of a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_l1_norm >>> f = ZZ.map([[2, -1], [-3]]) >>> dmp_l1_norm(f, 1, ZZ) 6 """ if not u: return dup_l1_norm(f, K) v = u-1 return sum([ dmp_l1_norm(c, v, K) for c in f ]) def dup_expand(polys, K): """ Multiply together several polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dup_expand >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, 0]) >>> h = ZZ.map([2]) >>> dup_expand([f, g, h], ZZ) [2, 0, -2, 0] """ if not polys: return [K.one] f = polys[0] for g in polys[1:]: f = dup_mul(f, g, K) return f @cythonized("u") def dmp_expand(polys, u, K): """ Multiply together several polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densearith import dmp_expand >>> f = ZZ.map([[1], [], [1, 0, 0]]) >>> g = ZZ.map([[1], [1]]) >>> dmp_expand([f, g], 1, ZZ) [[1], [1], [1, 0, 0], [1, 0, 0]] """ if not polys: return dmp_one(u, K) f = polys[0] for g in polys[1:]: f = dmp_mul(f, g, u, K) return f wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/sqfreetools.py0000644000175000017500000002715212014170666025174 0ustar georgeskgeorgesk"""Square-free decomposition algorithms and related tools. """ from sympy.polys.densebasic import ( dup_strip, dup_LC, dmp_ground_LC, dmp_zero_p, dmp_ground, dup_degree, dmp_degree, dmp_raise, dmp_inject, dup_convert) from sympy.polys.densearith import ( dup_neg, dmp_neg, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_quo, dmp_quo, dup_mul_ground, dmp_mul_ground) from sympy.polys.densetools import ( dup_diff, dmp_diff, dup_shift, dmp_compose, dup_monic, dmp_ground_monic, dup_primitive, dmp_ground_primitive) from sympy.polys.euclidtools import ( dup_inner_gcd, dmp_inner_gcd, dup_gcd, dmp_gcd, dmp_resultant) from sympy.polys.galoistools import ( gf_sqf_list, gf_sqf_part) from sympy.polys.polyerrors import ( MultivariatePolynomialError, DomainError) from sympy.utilities import cythonized def dup_sqf_p(f, K): """ Return ``True`` if ``f`` is a square-free polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dup_sqf_p >>> dup_sqf_p([ZZ(1),-ZZ(2), ZZ(1)], ZZ) False >>> dup_sqf_p([ZZ(1), ZZ(0),-ZZ(1)], ZZ) True """ if not f: return True else: return not dup_degree(dup_gcd(f, dup_diff(f, 1, K), K)) @cythonized("u") def dmp_sqf_p(f, u, K): """ Return ``True`` if ``f`` is a square-free polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dmp_sqf_p >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> dmp_sqf_p(f, 1, ZZ) False >>> f = ZZ.map([[1], [], [1, 0, 0]]) >>> dmp_sqf_p(f, 1, ZZ) True """ if dmp_zero_p(f, u): return True else: return not dmp_degree(dmp_gcd(f, dmp_diff(f, 1, u, K), u, K), u) @cythonized("s") def dup_sqf_norm(f, K): """ Square-free norm of ``f`` in ``K[x]``, useful over algebraic domains. Returns ``s``, ``f``, ``r``, such that ``g(x) = f(x-sa)`` and ``r(x) = Norm(g(x))`` is a square-free polynomial over K, where ``a`` is the algebraic extension of ``K``. **Examples** >>> from sympy import sqrt >>> from sympy.polys.domains import QQ >>> from sympy.polys.sqfreetools import dup_sqf_norm >>> K = QQ.algebraic_field(sqrt(3)) >>> s, f, r = dup_sqf_norm([K(1), K(0), K(-2)], K) >>> s == 1 True >>> f == [K(1), K([QQ(-2), QQ(0)]), K(1)] True >>> r == [1, 0, -10, 0, 1] True """ if not K.is_Algebraic: raise DomainError("ground domain must be algebraic") s, g = 0, dmp_raise(K.mod.rep, 1, 0, K.dom) while True: h, _ = dmp_inject(f, 0, K, front=True) r = dmp_resultant(g, h, 1, K.dom) if dup_sqf_p(r, K.dom): break else: f, s = dup_shift(f, -K.unit, K), s+1 return s, f, r @cythonized("s,u") def dmp_sqf_norm(f, u, K): """ Square-free norm of ``f`` in ``K[X]``, useful over algebraic domains. Returns ``s``, ``f``, ``r``, such that ``g(x) = f(x-sa)`` and ``r(x) = Norm(g(x))`` is a square-free polynomial over K, where ``a`` is the algebraic extension of ``K``. **Examples** >>> from sympy import I >>> from sympy.polys.domains import QQ >>> from sympy.polys.sqfreetools import dmp_sqf_norm >>> K = QQ.algebraic_field(I) >>> s, f, r = dmp_sqf_norm([[K(1), K(0)], [K(1), K(0), K(0)]], 1, K) >>> s == 1 True >>> f == [[K(1), K(0)], [K(1), K([QQ(-1), QQ(0)]), K(0)]] True >>> r == [[1, 0, 0], [2, 0, 0, 0], [1, 0, 1, 0, 0]] True """ if not u: return dup_sqf_norm(f, K) if not K.is_Algebraic: raise DomainError("ground domain must be algebraic") g = dmp_raise(K.mod.rep, u+1, 0, K.dom) F = dmp_raise([K.one,-K.unit], u, 0, K) s = 0 while True: h, _ = dmp_inject(f, u, K, front=True) r = dmp_resultant(g, h, u+1, K.dom) if dmp_sqf_p(r, u, K.dom): break else: f, s = dmp_compose(f, F, u, K), s+1 return s, f, r @cythonized("i") def dup_gf_sqf_part(f, K): """Compute square-free part of ``f`` in ``GF(p)[x]``. """ f = dup_convert(f, K, K.dom) g = gf_sqf_part(f, K.mod, K.dom) return dup_convert(g, K.dom, K) def dmp_gf_sqf_part(f, K): """Compute square-free part of ``f`` in ``GF(p)[X]``. """ raise DomainError('multivariate polynomials over %s' % K) def dup_sqf_part(f, K): """ Returns square-free part of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dup_sqf_part >>> dup_sqf_part([ZZ(1), ZZ(0), -ZZ(3), -ZZ(2)], ZZ) [1, -1, -2] """ if not K.has_CharacteristicZero: return dup_gf_sqf_part(f, K) if not f: return f if K.is_negative(dup_LC(f, K)): f = dup_neg(f, K) gcd = dup_gcd(f, dup_diff(f, 1, K), K) sqf = dup_quo(f, gcd, K) if K.has_Field or not K.is_Exact: return dup_monic(sqf, K) else: return dup_primitive(sqf, K)[1] @cythonized("u") def dmp_sqf_part(f, u, K): """ Returns square-free part of a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dmp_sqf_part >>> f = ZZ.map([[1], [2, 0], [1, 0, 0], []]) >>> dmp_sqf_part(f, 1, ZZ) [[1], [1, 0], []] """ if not u: return dup_sqf_part(f, K) if not K.has_CharacteristicZero: return dmp_gf_sqf_part(f, u, K) if dmp_zero_p(f, u): return f if K.is_negative(dmp_ground_LC(f, u, K)): f = dmp_neg(f, u, K) gcd = dmp_gcd(f, dmp_diff(f, 1, u, K), u, K) sqf = dmp_quo(f, gcd, u, K) if K.has_Field or not K.is_Exact: return dmp_ground_monic(sqf, u, K) else: return dmp_ground_primitive(sqf, u, K)[1] @cythonized("i") def dup_gf_sqf_list(f, K, all=False): """Compute square-free decomposition of ``f`` in ``GF(p)[x]``. """ f = dup_convert(f, K, K.dom) coeff, factors = gf_sqf_list(f, K.mod, K.dom, all=all) for i, (f, k) in enumerate(factors): factors[i] = (dup_convert(f, K.dom, K), k) return K.convert(coeff, K.dom), factors def dmp_gf_sqf_list(f, u, K, all=False): """Compute square-free decomposition of ``f`` in ``GF(p)[X]``. """ raise DomainError('multivariate polynomials over %s' % K) @cythonized("i") def dup_sqf_list(f, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dup_sqf_list >>> f = ZZ.map([2, 16, 50, 76, 56, 16]) >>> dup_sqf_list(f, ZZ) (2, [([1, 1], 2), ([1, 2], 3)]) >>> dup_sqf_list(f, ZZ, all=True) (2, [([1], 1), ([1, 1], 2), ([1, 2], 3)]) """ if not K.has_CharacteristicZero: return dup_gf_sqf_list(f, K, all=all) if K.has_Field or not K.is_Exact: coeff = dup_LC(f, K) f = dup_monic(f, K) else: coeff, f = dup_primitive(f, K) if K.is_negative(dup_LC(f, K)): f = dup_neg(f, K) coeff = -coeff if dup_degree(f) <= 0: return coeff, [] result, i = [], 1 h = dup_diff(f, 1, K) g, p, q = dup_inner_gcd(f, h, K) while True: d = dup_diff(p, 1, K) h = dup_sub(q, d, K) if not h: result.append((p, i)) break g, p, q = dup_inner_gcd(p, h, K) if all or dup_degree(g) > 0: result.append((g, i)) i += 1 return coeff, result def dup_sqf_list_include(f, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dup_sqf_list_include >>> f = ZZ.map([2, 16, 50, 76, 56, 16]) >>> dup_sqf_list_include(f, ZZ) [([2], 1), ([1, 1], 2), ([1, 2], 3)] >>> dup_sqf_list_include(f, ZZ, all=True) [([2], 1), ([1, 1], 2), ([1, 2], 3)] """ coeff, factors = dup_sqf_list(f, K, all=all) if factors and factors[0][1] == 1: g = dup_mul_ground(factors[0][0], coeff, K) return [(g, 1)] + factors[1:] else: g = dup_strip([coeff]) return [(g, 1)] + factors @cythonized("u,i") def dmp_sqf_list(f, u, K, all=False): """ Return square-free decomposition of a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dmp_sqf_list >>> f = ZZ.map([[1], [2, 0], [1, 0, 0], [], [], []]) >>> dmp_sqf_list(f, 1, ZZ) (1, [([[1], [1, 0]], 2), ([[1], []], 3)]) >>> dmp_sqf_list(f, 1, ZZ, all=True) (1, [([[1]], 1), ([[1], [1, 0]], 2), ([[1], []], 3)]) """ if not u: return dup_sqf_list(f, K, all=all) if not K.has_CharacteristicZero: return dmp_gf_sqf_list(f, u, K, all=all) if K.has_Field or not K.is_Exact: coeff = dmp_ground_LC(f, u, K) f = dmp_ground_monic(f, u, K) else: coeff, f = dmp_ground_primitive(f, u, K) if K.is_negative(dmp_ground_LC(f, u, K)): f = dmp_neg(f, u, K) coeff = -coeff if dmp_degree(f, u) <= 0: return coeff, [] result, i = [], 1 h = dmp_diff(f, 1, u, K) g, p, q = dmp_inner_gcd(f, h, u, K) while True: d = dmp_diff(p, 1, u, K) h = dmp_sub(q, d, u, K) if dmp_zero_p(h, u): result.append((p, i)) break g, p, q = dmp_inner_gcd(p, h, u, K) if all or dmp_degree(g, u) > 0: result.append((g, i)) i += 1 return coeff, result @cythonized("u") def dmp_sqf_list_include(f, u, K, all=False): """ Return square-free decomposition of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dmp_sqf_list_include >>> f = ZZ.map([[1], [2, 0], [1, 0, 0], [], [], []]) >>> dmp_sqf_list_include(f, 1, ZZ) [([[1]], 1), ([[1], [1, 0]], 2), ([[1], []], 3)] >>> dmp_sqf_list_include(f, 1, ZZ, all=True) [([[1]], 1), ([[1], [1, 0]], 2), ([[1], []], 3)] """ if not u: return dup_sqf_list_include(f, K, all=all) coeff, factors = dmp_sqf_list(f, u, K, all=all) if factors and factors[0][1] == 1: g = dmp_mul_ground(factors[0][0], coeff, u, K) return [(g, 1)] + factors[1:] else: g = dmp_ground(coeff, u) return [(g, 1)] + factors def dup_gff_list(f, K): """ Compute greatest factorial factorization of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dup_gff_list >>> f = ZZ.map([1, 2, -1, -2, 0, 0]) >>> dup_gff_list(f, ZZ) [([1, 0], 1), ([1, 2], 4)] """ if not f: raise ValueError("greatest factorial factorization doesn't exist for a zero polynomial") f = dup_monic(f, K) if not dup_degree(f): return [] else: g = dup_gcd(f, dup_shift(f, K.one, K), K) H = dup_gff_list(g, K) for i, (h, k) in enumerate(H): g = dup_mul(g, dup_shift(h, -K(k), K), K) H[i] = (h, k + 1) f = dup_quo(f, g, K) if not dup_degree(f): return H else: return [(f, 1)] + H def dmp_gff_list(f, u, K): """ Compute greatest factorial factorization of ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.sqfreetools import dmp_gff_list """ if not u: return dup_gff_list(f, K) else: raise MultivariatePolynomialError(f) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/rationaltools.py0000644000175000017500000000543012014170666025513 0ustar georgeskgeorgesk"""Tools for manipulation of rational expressions. """ from sympy.core import Basic, Add, sympify from sympy.core.exprtools import gcd_terms def together(expr, deep=False): """ Denest and combine rational expressions using symbolic methods. This function takes an expression or a container of expressions and puts it (them) together by denesting and combining rational subexpressions. No heroic measures are taken to minimize degree of the resulting numerator and denominator. To obtain completely reduced expression use :func:`cancel`. However, :func:`together` can preserve as much as possible of the structure of the input expression in the output (no expansion is performed). A wide variety of objects can be put together including lists, tuples, sets, relational objects, integrals and others. It is also possible to transform interior of function applications, by setting ``deep`` flag to ``True``. By definition, :func:`together` is a complement to :func:`apart`, so ``apart(together(expr))`` should return expr unchanged. Note however, that :func:`together` uses only symbolic methods, so it might be necessary to use :func:`cancel` to perform algebraic simplification and minimise degree of the numerator and denominator. **Example** >>> from sympy import together, exp >>> from sympy.abc import x, y, z >>> together(1/x + 1/y) (x + y)/(x*y) >>> together(1/x + 1/y + 1/z) (x*y + x*z + y*z)/(x*y*z) >>> together(1/(x*y) + 1/y**2) (x + y)/(x*y**2) >>> together(1/(1 + 1/x) + 1/(1 + 1/y)) (x*(y + 1) + y*(x + 1))/((x + 1)*(y + 1)) >>> together(exp(1/x + 1/y)) exp(1/y + 1/x) >>> together(exp(1/x + 1/y), deep=True) exp((x + y)/(x*y)) >>> together(1/exp(x) + 1/(x*exp(x))) (x + 1)*exp(-x)/x >>> together(1/exp(2*x) + 1/(x*exp(3*x))) (x*exp(x) + 1)*exp(-3*x)/x """ def _together(expr): if isinstance(expr, Basic): if expr.is_commutative is False: numer, denom = expr.as_numer_denom() return numer/denom elif expr.is_Atom or (expr.is_Function and not deep): return expr elif expr.is_Add: return gcd_terms(map(_together, Add.make_args(expr))) elif expr.is_Pow: base = _together(expr.base) if deep: exp = _together(expr.exp) else: exp = expr.exp return expr.__class__(base, exp) else: return expr.__class__(*[ _together(arg) for arg in expr.args ]) elif hasattr(expr, '__iter__'): return expr.__class__([ _together(ex) for ex in expr ]) return expr return _together(sympify(expr)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyerrors.py0000644000175000017500000000565512014170666025052 0ustar georgeskgeorgesk"""Definitions of common exceptions for `polys` module. """ class BasePolynomialError(Exception): """Base class for polynomial related exceptions. """ def new(self, *args): raise NotImplementedError("abstract base class") class ExactQuotientFailed(BasePolynomialError): def __init__(self, f, g, dom=None): self.f, self.g, self.dom = f, g, dom def __str__(self): # pragma: no cover from sympy.printing.str import sstr if self.dom is None: return "%s does not divide %s" % (sstr(self.g), sstr(self.f)) else: return "%s does not divide %s in %s" % (sstr(self.g), sstr(self.f), sstr(self.dom)) def new(self, f, g): return self.__class__(f, g, self.dom) class OperationNotSupported(BasePolynomialError): def __init__(self, poly, func): self.poly = poly self.func = func def __str__(self): # pragma: no cover return "`%s` operation not supported by %s representation" % (self.func, self.poly.rep.__class__.__name__) class HeuristicGCDFailed(BasePolynomialError): pass class HomomorphismFailed(BasePolynomialError): pass class IsomorphismFailed(BasePolynomialError): pass class ExtraneousFactors(BasePolynomialError): pass class EvaluationFailed(BasePolynomialError): pass class RefinementFailed(BasePolynomialError): pass class CoercionFailed(BasePolynomialError): pass class NotInvertible(BasePolynomialError): pass class NotReversible(BasePolynomialError): pass class NotAlgebraic(BasePolynomialError): pass class DomainError(BasePolynomialError): pass class PolynomialError(BasePolynomialError): pass class UnificationFailed(BasePolynomialError): pass class GeneratorsNeeded(BasePolynomialError): pass class ComputationFailed(BasePolynomialError): def __init__(self, func, nargs, exc): self.func = func self.nargs = nargs self.exc = exc def __str__(self): return "%s(%s) failed without generators" % (self.func, ', '.join(map(str, self.exc.exprs[:self.nargs]))) class GeneratorsError(BasePolynomialError): pass class UnivariatePolynomialError(PolynomialError): pass class MultivariatePolynomialError(PolynomialError): pass class PolificationFailed(PolynomialError): def __init__(self, opt, origs, exprs, seq=False): if not seq: self.orig = origs self.expr = exprs self.origs = [origs] self.exprs = [exprs] else: self.origs = origs self.exprs = exprs self.opt = opt self.seq = seq def __str__(self): # pragma: no cover if not self.seq: return "can't construct a polynomial from %s" % str(self.orig) else: return "can't construct polynomials from %s" % ', '.join(map(str, self.origs)) class OptionError(BasePolynomialError): pass class FlagError(OptionError): pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyutils.py0000644000175000017500000002135312014170666024667 0ustar georgeskgeorgesk"""Useful utilities for higher level polynomial classes. """ from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded from sympy.polys.polyoptions import build_options from sympy.core.exprtools import decompose_power from sympy.core import S, Add, Mul, Pow from sympy.assumptions import ask, Q import re _gens_order = { 'a': 301, 'b': 302, 'c': 303, 'd': 304, 'e': 305, 'f': 306, 'g': 307, 'h': 308, 'i': 309, 'j': 310, 'k': 311, 'l': 312, 'm': 313, 'n': 314, 'o': 315, 'p': 216, 'q': 217, 'r': 218, 's': 219, 't': 220, 'u': 221, 'v': 222, 'w': 223, 'x': 124, 'y': 125, 'z': 126, } _max_order = 1000 _re_gen = re.compile(r"^(.+?)(\d*)$") def _sort_gens(gens, **args): """Sort generators in a reasonably intelligent way. """ opt = build_options(args) gens_order, wrt = {}, None if opt is not None: gens_order, wrt = {}, opt.wrt for i, gen in enumerate(opt.sort): gens_order[gen] = i+1 def order_key(gen): gen = str(gen) if wrt is not None: try: return (-len(wrt) + wrt.index(gen), gen, 0) except ValueError: pass name, index = _re_gen.match(gen).groups() if index: index = int(index) else: index = 0 try: return ( gens_order[name], name, index) except KeyError: pass try: return (_gens_order[name], name, index) except KeyError: pass return (_max_order, name, index) try: gens = sorted(gens, key=order_key) except TypeError: # pragma: no cover pass return tuple(gens) def _unify_gens(f_gens, g_gens): """Unify generators in a reasonably intelligent way. """ f_gens = list(f_gens) g_gens = list(g_gens) if f_gens == g_gens: return tuple(f_gens) gens, common, k = [], [], 0 for gen in f_gens: if gen in g_gens: common.append(gen) for i, gen in enumerate(g_gens): if gen in common: g_gens[i], k = common[k], k+1 for gen in common: i = f_gens.index(gen) gens.extend(f_gens[:i]) f_gens = f_gens[i+1:] i = g_gens.index(gen) gens.extend(g_gens[:i]) g_gens = g_gens[i+1:] gens.append(gen) gens.extend(f_gens) gens.extend(g_gens) return tuple(gens) def _analyze_gens(gens): """Support for passing generators as `*gens` and `[gens]`. """ if len(gens) == 1 and hasattr(gens[0], '__iter__'): return tuple(gens[0]) else: return tuple(gens) def _sort_factors(factors, **args): """Sort low-level factors in increasing 'complexity' order. """ def order_if_multiple_key(factor): (f, n) = factor return (len(f), n, f) def order_no_multiple_key(f): return (len(f), f) if args.get('multiple', True): return sorted(factors, key=order_if_multiple_key) else: return sorted(factors, key=order_no_multiple_key) def _parallel_dict_from_expr_if_gens(exprs, opt): """Transform expressions into a multinomial form given generators. """ k, indices = len(opt.gens), {} for i, g in enumerate(opt.gens): indices[g] = i polys = [] for expr in exprs: poly = {} for term in Add.make_args(expr): coeff, monom = [], [0]*k for factor in Mul.make_args(term): if factor.is_Number: coeff.append(factor) else: try: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) monom[indices[base]] = exp except KeyError: if not factor.has(*opt.gens): coeff.append(factor) else: raise PolynomialError("%s contains an element of the generators set" % factor) monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, opt.gens def _parallel_dict_from_expr_no_gens(exprs, opt): """Transform expressions into a multinomial form and figure out generators. """ if opt.domain is not None: def _is_coeff(factor): return factor in opt.domain elif opt.extension is True: def _is_coeff(factor): return ask(Q.algebraic(factor)) elif opt.greedy is not False: def _is_coeff(factor): return False else: def _is_coeff(factor): return factor.is_number gens, reprs = set([]), [] for expr in exprs: terms = [] for term in Add.make_args(expr): coeff, elements = [], {} for factor in Mul.make_args(term): if factor.is_Number or _is_coeff(factor): coeff.append(factor) else: base, exp = decompose_power(factor) if exp < 0: exp, base = -exp, Pow(base, -S.One) elements[base] = exp gens.add(base) terms.append((coeff, elements)) reprs.append(terms) if not gens: if len(exprs) == 1: arg = exprs[0] else: arg = (exprs,) raise GeneratorsNeeded("specify generators to give %s a meaning" % arg) gens = _sort_gens(gens, opt=opt) k, indices = len(gens), {} for i, g in enumerate(gens): indices[g] = i polys = [] for terms in reprs: poly = {} for coeff, term in terms: monom = [0]*k for base, exp in term.iteritems(): monom[indices[base]] = exp monom = tuple(monom) if monom in poly: poly[monom] += Mul(*coeff) else: poly[monom] = Mul(*coeff) polys.append(poly) return polys, tuple(gens) def _dict_from_expr_if_gens(expr, opt): """Transform an expression into a multinomial form given generators. """ (poly,), gens = _parallel_dict_from_expr_if_gens((expr,), opt) return poly, gens def _dict_from_expr_no_gens(expr, opt): """Transform an expression into a multinomial form and figure out generators. """ (poly,), gens = _parallel_dict_from_expr_no_gens((expr,), opt) return poly, gens def parallel_dict_from_expr(exprs, **args): """Transform expressions into a multinomial form. """ reps, opt = _parallel_dict_from_expr(exprs, build_options(args)) return reps, opt.gens def _parallel_dict_from_expr(exprs, opt): """Transform expressions into a multinomial form. """ if opt.expand is not False: exprs = [ expr.expand() for expr in exprs ] if any(expr.is_commutative is False for expr in exprs): raise PolynomialError('non-commutative expressions are not supported') if opt.gens: reps, gens = _parallel_dict_from_expr_if_gens(exprs, opt) else: reps, gens = _parallel_dict_from_expr_no_gens(exprs, opt) return reps, opt.clone({'gens': gens}) def dict_from_expr(expr, **args): """Transform an expression into a multinomial form. """ rep, opt = _dict_from_expr(expr, build_options(args)) return rep, opt.gens def _dict_from_expr(expr, opt): """Transform an expression into a multinomial form. """ if opt.expand is not False: expr = expr.expand() if expr.is_commutative is False: raise PolynomialError('non-commutative expressions are not supported') if opt.gens: rep, gens = _dict_from_expr_if_gens(expr, opt) else: rep, gens = _dict_from_expr_no_gens(expr, opt) return rep, opt.clone({'gens': gens}) def expr_from_dict(rep, *gens): """Convert a multinomial form into an expression. """ result = [] for monom, coeff in rep.iteritems(): term = [coeff] for g, m in zip(gens, monom): term.append(Pow(g, m)) result.append(Mul(*term)) return Add(*result) parallel_dict_from_basic = parallel_dict_from_expr dict_from_basic = dict_from_expr basic_from_dict = expr_from_dict def _dict_reorder(rep, gens, new_gens): """Reorder levels using dict representation. """ gens = list(gens) monoms = rep.keys() coeffs = rep.values() new_monoms = [ [] for _ in xrange(len(rep)) ] for gen in new_gens: try: j = gens.index(gen) for M, new_M in zip(monoms, new_monoms): new_M.append(M[j]) except ValueError: for new_M in new_monoms: new_M.append(0) return map(tuple, new_monoms), coeffs wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/monomialtools.py0000644000175000017500000002416112014170666025517 0ustar georgeskgeorgesk"""Tools and arithmetics for monomials of distributed polynomials. """ from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.basic import C from sympy.polys.polyerrors import ExactQuotientFailed from sympy.utilities import cythonized from sympy.core.compatibility import cmp def monomials(variables, degree): r""" Generate a set of monomials of the given total degree or less. Given a set of variables `V` and a total degree `N` generate a set of monomials of degree at most `N`. The total number of monomials is huge and is given by the following formula: .. math:: \frac{(\#V + N)!}{\#V! N!} For example if we would like to generate a dense polynomial of a total degree `N = 50` in 5 variables, assuming that exponents and all of coefficients are 32-bit long and stored in an array we would need almost 80 GiB of memory! Fortunately most polynomials, that we will encounter, are sparse. **Examples** Consider monomials in variables `x` and `y`:: >>> from sympy import monomials >>> from sympy.abc import x, y >>> sorted(monomials([x, y], 2)) [1, x, y, x**2, y**2, x*y] >>> sorted(monomials([x, y], 3)) [1, x, y, x**2, x**3, y**2, y**3, x*y, x*y**2, x**2*y] """ if not variables: return set([S.One]) else: x, tail = variables[0], variables[1:] monoms = monomials(tail, degree) for i in range(1, degree+1): monoms |= set([ x**i * m for m in monomials(tail, degree-i) ]) return monoms def monomial_count(V, N): r""" Computes the number of monomials. The number of monomials is given by the following formula: .. math:: \frac{(\#V + N)!}{\#V! N!} where `N` is a total degree and `V` is a set of variables. **Examples** >>> from sympy import monomials, monomial_count >>> from sympy.abc import x, y >>> monomial_count(2, 2) 6 >>> M = monomials([x, y], 2) >>> sorted(M) [1, x, y, x**2, y**2, x*y] >>> len(M) 6 """ return C.factorial(V + N) / C.factorial(V) / C.factorial(N) def monomial_lex_key(monom): """Key function for sorting monomials in lexicographic order. """ return monom def monomial_grlex_key(monom): """Key function for sorting monomials in graded lexicographic order. """ return (sum(monom), monom) def monomial_grevlex_key(monom): """Key function for sorting monomials in reversed graded lexicographic order. """ return (sum(monom), tuple(reversed(monom))) _monomial_key = { 'lex' : monomial_lex_key, 'grlex' : monomial_grlex_key, 'grevlex' : monomial_grevlex_key, } def monomial_key(order=None): """ Return a function defining admissible order on monomials. The result of a call to :func:`monomial_key` is a function which should be used as a key to :func:`sorted` built-in function, to provide order in a set of monomials of the same length. Currently supported monomial orderings are: 1. lex - lexicographic order (default) 2. grlex - graded lexicographic order 3. grevlex - reversed graded lexicographic order If the input argument is not a string but has ``__call__`` attribute, then it will pass through with an assumption that the callable object defines an admissible order on monomials. """ if order is None: return _monomial_key['lex'] if isinstance(order, str): try: return _monomial_key[order] except KeyError: raise ValueError("supported monomial orderings are 'lex', 'grlex' and 'grevlex', got %r" % order) elif hasattr(order, '__call__'): return order else: raise ValueError("monomial ordering specification must be a string or a callable, got %s" % order) def monomial_lex_cmp(a, b): return cmp(a, b) def monomial_grlex_cmp(a, b): return cmp(sum(a), sum(b)) or cmp(a, b) def monomial_grevlex_cmp(a, b): return cmp(sum(a), sum(b)) or cmp(tuple(reversed(b)), tuple(reversed(a))) _monomial_order = { 'lex' : monomial_lex_cmp, 'grlex' : monomial_grlex_cmp, 'grevlex' : monomial_grevlex_cmp, } def monomial_cmp(order): """ Returns a function defining admissible order on monomials. Currently supported orderings are: 1. lex - lexicographic order 2. grlex - graded lexicographic order 3. grevlex - reversed graded lexicographic order """ try: return _monomial_order[order] except KeyError: raise ValueError("expected valid monomial order, got %s" % order) @cythonized("a,b") def monomial_mul(A, B): """ Multiplication of tuples representing monomials. Lets multiply `x**3*y**4*z` with `x*y**2`:: >>> from sympy.polys.monomialtools import monomial_mul >>> monomial_mul((3, 4, 1), (1, 2, 0)) (4, 6, 1) which gives `x**4*y**5*z`. """ return tuple([ a + b for a, b in zip(A, B) ]) @cythonized("a,b,c") def monomial_div(A, B): """ Division of tuples representing monomials. Lets divide `x**3*y**4*z` by `x*y**2`:: >>> from sympy.polys.monomialtools import monomial_div >>> monomial_div((3, 4, 1), (1, 2, 0)) (2, 2, 1) which gives `x**2*y**2*z`. However:: >>> monomial_div((3, 4, 1), (1, 2, 2)) is None True `x*y**2*z**2` does not divide `x**3*y**4*z`. """ C = [ a - b for a, b in zip(A, B) ] if all([ c >= 0 for c in C ]): return tuple(C) else: return None @cythonized("a,b") def monomial_gcd(A, B): """ Greatest common divisor of tuples representing monomials. Lets compute GCD of `x**3*y**4*z` and `x*y**2`:: >>> from sympy.polys.monomialtools import monomial_gcd >>> monomial_gcd((3, 4, 1), (1, 2, 0)) (1, 2, 0) which gives `x*y**2`. """ return tuple([ min(a, b) for a, b in zip(A, B) ]) @cythonized("a,b") def monomial_lcm(A, B): """ Least common multiple of tuples representing monomials. Lets compute LCM of `x**3*y**4*z` and `x*y**2`:: >>> from sympy.polys.monomialtools import monomial_lcm >>> monomial_lcm((3, 4, 1), (1, 2, 0)) (3, 4, 1) which gives `x**3*y**4*z`. """ return tuple([ max(a, b) for a, b in zip(A, B) ]) @cythonized("i,n") def monomial_max(*monoms): """ Returns maximal degree for each variable in a set of monomials. Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`. We wish to find out what is the maximal degree for each of `x`, `y` and `z` variables:: >>> from sympy.polys.monomialtools import monomial_max >>> monomial_max((3,4,5), (0,5,1), (6,3,9)) (6, 5, 9) """ M = list(monoms[0]) for N in monoms[1:]: for i, n in enumerate(N): M[i] = max(M[i], n) return tuple(M) @cythonized("i,n") def monomial_min(*monoms): """ Returns minimal degree for each variable in a set of monomials. Consider monomials `x**3*y**4*z**5`, `y**5*z` and `x**6*y**3*z**9`. We wish to find out what is the minimal degree for each of `x`, `y` and `z` variables:: >>> from sympy.polys.monomialtools import monomial_min >>> monomial_min((3,4,5), (0,5,1), (6,3,9)) (0, 3, 1) """ M = list(monoms[0]) for N in monoms[1:]: for i, n in enumerate(N): M[i] = min(M[i], n) return tuple(M) class Monomial(object): """Class representing a monomial, i.e. a product of powers. """ __slots__ = ['data'] def __init__(self, *data): self.data = tuple(map(int, data)) def __hash__(self): return hash((self.__class__.__name__, self.data)) def __repr__(self): return "Monomial(%s)" % ", ".join(map(str, self.data)) def as_expr(self, *gens): """Convert a monomial instance to a SymPy expression. """ return Mul(*[ gen**exp for gen, exp in zip(gens, self.data) ]) def __eq__(self, other): if isinstance(other, Monomial): return self.data == other.data else: return False def __ne__(self, other): return not self.__eq__(other) def __mul__(self, other): if isinstance(other, Monomial): return Monomial(*monomial_mul(self.data, other.data)) else: raise TypeError("an instance of Monomial class expected, got %s" % other) def __pow__(self, other): n = int(other) if not n: return Monomial(*((0,)*len(self.data))) elif n > 0: data = self.data for i in xrange(1, n): data = monomial_mul(data, self.data) return Monomial(*data) else: raise ValueError("a non-negative integer expected, got %s" % other) def __div__(self, other): if isinstance(other, Monomial): result = monomial_div(self.data, other.data) if result is not None: return Monomial(*result) else: raise ExactQuotientFailed(self, other) else: raise TypeError("an instance of Monomial class expected, got %s" % other) __floordiv__ = __truediv__ = __div__ def gcd(self, other): """Greatest common divisor of monomials. """ if isinstance(other, Monomial): return Monomial(*monomial_gcd(self.data, other.data)) else: raise TypeError("an instance of Monomial class expected, got %s" % other) def lcm(self, other): """Least common multiple of monomials. """ if isinstance(other, Monomial): return Monomial(*monomial_lcm(self.data, other.data)) else: raise TypeError("an instance of Monomial class expected, got %s" % other) @classmethod def max(cls, *monomials): """Returns maximal degree for each variable in a set of monomials. """ return Monomial(*monomial_max(*[ monomial.data for monomial in monomials ])) @classmethod def min(cls, *monomials): """Returns minimal degree for each variable in a set of monomials. """ return Monomial(*monomial_min(*[ monomial.data for monomial in monomials ])) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polytools.py0000644000175000017500000040537112014170666024675 0ustar georgeskgeorgesk"""User-friendly public interface to polynomial functions. """ from sympy.core import ( S, Basic, Expr, I, Integer, Add, Mul, Dummy, Tuple ) from sympy.core.sympify import ( sympify, SympifyError, ) from sympy.core.decorators import ( _sympifyit, ) from sympy.polys.polyclasses import ( DMP, ANP, DMF, ) from sympy.polys.polyutils import ( basic_from_dict, _sort_gens, _unify_gens, _dict_reorder, _dict_from_expr, _parallel_dict_from_expr, ) from sympy.polys.rationaltools import ( together, ) from sympy.polys.rootisolation import ( dup_isolate_real_roots_list, ) from sympy.polys.groebnertools import ( sdp_from_dict, sdp_div, sdp_groebner, ) from sympy.polys.monomialtools import ( Monomial, monomial_key, ) from sympy.polys.polyerrors import ( OperationNotSupported, DomainError, CoercionFailed, UnificationFailed, GeneratorsNeeded, PolynomialError, PolificationFailed, FlagError, MultivariatePolynomialError, ExactQuotientFailed, ComputationFailed, GeneratorsError, ) from sympy.polys.polycontext import ( register_context, ) from sympy.utilities import ( group, ) from sympy.ntheory import isprime import sympy.polys import sympy.mpmath from sympy.polys.domains import FF, QQ from sympy.polys.constructor import construct_domain from sympy.polys import polyoptions as options class Poly(Expr): """Generic class for representing polynomial expressions. """ __slots__ = ['rep', 'gens'] is_Poly = True def __new__(cls, rep, *gens, **args): """Create a new polynomial instance out of something useful. """ opt = options.build_options(gens, args) if 'order' in opt: raise NotImplementedError("'order' keyword is not implemented yet") if hasattr(rep, '__iter__'): if isinstance(rep, dict): return cls._from_dict(rep, opt) else: return cls._from_list(list(rep), opt) else: rep = sympify(rep) if rep.is_Poly: return cls._from_poly(rep, opt) else: return cls._from_expr(rep, opt) @classmethod def new(cls, rep, *gens): """Construct :class:`Poly` instance from raw representation. """ if not isinstance(rep, DMP): raise PolynomialError("invalid polynomial representation: %s" % rep) elif rep.lev != len(gens)-1: raise PolynomialError("invalid arguments: %s, %s" % (rep, gens)) obj = Basic.__new__(cls) obj.rep = rep obj.gens = gens return obj @classmethod def from_dict(cls, rep, *gens, **args): """Construct a polynomial from a ``dict``. """ opt = options.build_options(gens, args) return cls._from_dict(rep, opt) @classmethod def from_list(cls, rep, *gens, **args): """Construct a polynomial from a ``list``. """ opt = options.build_options(gens, args) return cls._from_list(rep, opt) @classmethod def from_poly(cls, rep, *gens, **args): """Construct a polynomial from a polynomial. """ opt = options.build_options(gens, args) return cls._from_poly(rep, opt) @classmethod def from_expr(cls, rep, *gens, **args): """Construct a polynomial from an expression. """ opt = options.build_options(gens, args) return cls._from_expr(rep, opt) @classmethod def _from_dict(cls, rep, opt): """Construct a polynomial from a ``dict``. """ gens = opt.gens if not gens: raise GeneratorsNeeded("can't initialize from 'dict' without generators") level = len(gens)-1 domain = opt.domain if domain is None: domain, rep = construct_domain(rep, opt=opt) else: for monom, coeff in rep.iteritems(): rep[monom] = domain.convert(coeff) return cls.new(DMP.from_dict(rep, level, domain), *gens) @classmethod def _from_list(cls, rep, opt): """Construct a polynomial from a ``list``. """ gens = opt.gens if not gens: raise GeneratorsNeeded("can't initialize from 'list' without generators") elif len(gens) != 1: raise MultivariatePolynomialError("'list' representation not supported") level = len(gens)-1 domain = opt.domain if domain is None: domain, rep = construct_domain(rep, opt=opt) else: rep = map(domain.convert, rep) return cls.new(DMP.from_list(rep, level, domain), *gens) @classmethod def _from_poly(cls, rep, opt): """Construct a polynomial from a polynomial. """ if cls != rep.__class__: rep = cls.new(rep.rep, *rep.gens) gens = opt.gens order = opt.order field = opt.field domain = opt.domain if gens and rep.gens != gens: if set(rep.gens) != set(gens): return cls._from_expr(rep.as_expr(), opt) else: rep = rep.reorder(*gens) if 'order' in opt: rep = rep.set_order(order) if 'domain' in opt and domain: rep = rep.set_domain(domain) elif field is True: rep = rep.to_field() return rep @classmethod def _from_expr(cls, rep, opt): """Construct a polynomial from an expression. """ rep, opt = _dict_from_expr(rep, opt) return cls._from_dict(rep, opt) def __getnewargs__(self): """Data used by pickling protocol version 2. """ return (self.rep, self.gens) def _hashable_content(self): """Allow SymPy to hash Poly instances. """ return (self.rep, self.gens) def __hash__(self): return super(Poly, self).__hash__() @property def free_symbols(self): """ Free symbols of a polynomial expression. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 1).free_symbols set([x]) >>> Poly(x**2 + y).free_symbols set([x, y]) >>> Poly(x**2 + y, x).free_symbols set([x, y]) """ symbols = set([]) for gen in self.gens: symbols |= gen.free_symbols return symbols | self.free_symbols_in_domain @property def free_symbols_in_domain(self): """ Free symbols of the domain of ``self``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 1).free_symbols_in_domain set() >>> Poly(x**2 + y).free_symbols_in_domain set() >>> Poly(x**2 + y, x).free_symbols_in_domain set([y]) """ domain, symbols = self.rep.dom, set() if domain.is_Composite: for gen in domain.gens: symbols |= gen.free_symbols elif domain.is_EX: for coeff in self.coeffs(): symbols |= coeff.free_symbols return symbols @property def args(self): """ Don't mess up with the core. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).args [x**2 + 1] """ return [self.as_expr()] @property def gen(self): """ Return the principal generator. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).gen x """ return self.gens[0] @property def domain(self): """Get the ground domain of ``self``. """ return self.get_domain() @property def zero(self): """Return zero polynomial with ``self``'s properties. """ return self.new(self.rep.zero(self.rep.lev, self.rep.dom), *self.gens) @property def one(self): """Return one polynomial with ``self``'s properties. """ return self.new(self.rep.one(self.rep.lev, self.rep.dom), *self.gens) @property def unit(f): """Return unit polynomial with ``self``'s properties. """ return self.new(self.rep.unit(self.rep.lev, self.rep.dom), *self.gens) def unify(f, g): """ Make ``f`` and ``g`` belong to the same domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f, g = Poly(x/2 + 1), Poly(2*x + 1) >>> f Poly(1/2*x + 1, x, domain='QQ') >>> g Poly(2*x + 1, x, domain='ZZ') >>> F, G = f.unify(g) >>> F Poly(1/2*x + 1, x, domain='QQ') >>> G Poly(2*x + 1, x, domain='QQ') """ _, per, F, G = f._unify(g) return per(F), per(G) def _unify(f, g): g = sympify(g) if not g.is_Poly: try: return f.rep.dom, f.per, f.rep, f.rep.per(f.rep.dom.from_sympy(g)) except CoercionFailed: raise UnificationFailed("can't unify %s with %s" % (f, g)) if isinstance(f.rep, DMP) and isinstance(g.rep, DMP): gens = _unify_gens(f.gens, g.gens) dom, lev = f.rep.dom.unify(g.rep.dom, gens), len(gens)-1 if f.gens != gens: f_monoms, f_coeffs = _dict_reorder(f.rep.to_dict(), f.gens, gens) if f.rep.dom != dom: f_coeffs = [ dom.convert(c, f.rep.dom) for c in f_coeffs ] F = DMP(dict(zip(f_monoms, f_coeffs)), dom, lev) else: F = f.rep.convert(dom) if g.gens != gens: g_monoms, g_coeffs = _dict_reorder(g.rep.to_dict(), g.gens, gens) if g.rep.dom != dom: g_coeffs = [ dom.convert(c, g.rep.dom) for c in g_coeffs ] G = DMP(dict(zip(g_monoms, g_coeffs)), dom, lev) else: G = g.rep.convert(dom) else: raise UnificationFailed("can't unify %s with %s" % (f, g)) cls = f.__class__ def per(rep, dom=dom, gens=gens, remove=None): if remove is not None: gens = gens[:remove]+gens[remove+1:] if not gens: return dom.to_sympy(rep) return cls.new(rep, *gens) return dom, per, F, G def per(f, rep, gens=None, remove=None): """ Create a Poly out of the given representation. **Examples** >>> from sympy import Poly, ZZ >>> from sympy.abc import x, y >>> from sympy.polys.polyclasses import DMP >>> a = Poly(x**2 + 1) >>> a.per(DMP([ZZ(1), ZZ(1)], ZZ), gens=[y]) Poly(y + 1, y, domain='ZZ') """ if gens is None: gens = f.gens if remove is not None: gens = gens[:remove]+gens[remove+1:] if not gens: return f.rep.dom.to_sympy(rep) return f.__class__.new(rep, *gens) def set_domain(f, domain): """Set the ground domain of ``f``. """ opt = options.build_options(f.gens, {'domain': domain}) return f.per(f.rep.convert(opt.domain)) def get_domain(f): """Get the ground domain of ``f``. """ return f.rep.dom def set_modulus(f, modulus): """ Set the modulus of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(5*x**2 + 2*x - 1, x).set_modulus(2) Poly(x**2 + 1, x, modulus=2) """ modulus = options.Modulus.preprocess(modulus) return f.set_domain(FF(modulus)) def get_modulus(f): """ Get the modulus of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, modulus=2).get_modulus() 2 """ domain = f.get_domain() if not domain.has_CharacteristicZero: return Integer(domain.characteristic()) else: raise PolynomialError("not a polynomial over a Galois field") def _eval_subs(f, old, new): """Internal implementation of :func:`subs`. """ if old in f.gens: if new.is_number: return f.eval(old, new) else: try: return f.replace(old, new) except PolynomialError: pass return f.as_expr().subs(old, new) def exclude(f): """ Remove unnecessary generators from ``f``. **Example** >>> from sympy import Poly >>> from sympy.abc import a, b, c, d, x >>> Poly(a + x, a, b, c, d, x).exclude() Poly(a + x, a, x, domain='ZZ') """ J, new = f.rep.exclude() gens = [] for j in range(len(f.gens)): if j not in J: gens.append(f.gens[j]) return f.per(new, gens=gens) def replace(f, x, y=None): """ Replace ``x`` with ``y`` in generators list. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 1, x).replace(x, y) Poly(y**2 + 1, y, domain='ZZ') """ if y is None: if f.is_univariate: x, y = f.gen, x else: raise PolynomialError("syntax supported only in univariate case") if x == y: return f if x in f.gens and y not in f.gens: dom = f.get_domain() if not dom.is_Composite or y not in dom.gens: gens = list(f.gens) gens[gens.index(x)] = y return f.per(f.rep, gens=gens) raise PolynomialError("can't replace %s with %s in %s" % (x, y, f)) def reorder(f, *gens, **args): """ Efficiently apply new order of generators. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + x*y**2, x, y).reorder(y, x) Poly(y**2*x + x**2, y, x, domain='ZZ') """ opt = options.Options((), args) if not gens: gens = _sort_gens(f.gens, opt=opt) elif set(f.gens) != set(gens): raise PolynomialError("generators list can differ only up to order of elements") rep = dict(zip(*_dict_reorder(f.rep.to_dict(), f.gens, gens))) return f.per(DMP(rep, f.rep.dom, len(gens)-1), gens=gens) def ltrim(f, gen): """ Remove dummy generators from the "left" of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y, z >>> Poly(y**2 + y*z**2, x, y, z).ltrim(y) Poly(y**2 + y*z**2, y, z, domain='ZZ') """ rep = f.as_dict(native=True) j = f._gen_to_level(gen) terms = {} for monom, coeff in rep.iteritems(): monom = monom[j:] if monom not in terms: terms[monom] = coeff else: raise PolynomialError("can't left trim %s" % f) gens = f.gens[j:] return f.new(DMP.from_dict(terms, len(gens)-1, f.rep.dom), *gens) def has_only_gens(f, *gens): """ Return ``True`` if ``Poly(f, *gens)`` retains ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y, z >>> Poly(x*y + 1, x, y, z).has_only_gens(x, y) True >>> Poly(x*y + z, x, y, z).has_only_gens(x, y) False """ f_gens = list(f.gens) indices = set([]) for gen in gens: try: index = f_gens.index(gen) except ValueError: raise GeneratorsError("%s doesn't have %s as generator" % (f, gen)) else: indices.add(index) for monom in f.monoms(): for i, elt in enumerate(monom): if i not in indices and elt: return False return True def to_ring(f): """ Make the ground domain a ring. **Examples** >>> from sympy import Poly, QQ >>> from sympy.abc import x >>> Poly(x**2 + 1, domain=QQ).to_ring() Poly(x**2 + 1, x, domain='ZZ') """ if hasattr(f.rep, 'to_ring'): result = f.rep.to_ring() else: # pragma: no cover raise OperationNotSupported(f, 'to_ring') return f.per(result) def to_field(f): """ Make the ground domain a field. **Examples** >>> from sympy import Poly, ZZ >>> from sympy.abc import x >>> Poly(x**2 + 1, x, domain=ZZ).to_field() Poly(x**2 + 1, x, domain='QQ') """ if hasattr(f.rep, 'to_field'): result = f.rep.to_field() else: # pragma: no cover raise OperationNotSupported(f, 'to_field') return f.per(result) def to_exact(f): """ Make the ground domain exact. **Examples** >>> from sympy import Poly, RR >>> from sympy.abc import x >>> Poly(x**2 + 1.0, x, domain=RR).to_exact() Poly(x**2 + 1, x, domain='QQ') """ if hasattr(f.rep, 'to_exact'): result = f.rep.to_exact() else: # pragma: no cover raise OperationNotSupported(f, 'to_exact') return f.per(result) def retract(f, field=None): """ Recalculate the ground domain of a polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = Poly(x**2 + 1, x, domain='QQ[y]') >>> f Poly(x**2 + 1, x, domain='QQ[y]') >>> f.retract() Poly(x**2 + 1, x, domain='ZZ') >>> f.retract(field=True) Poly(x**2 + 1, x, domain='QQ') """ dom, rep = construct_domain(f.as_dict(zero=True), field=field) return f.from_dict(rep, f.gens, domain=dom) def slice(f, x, m, n=None): """Take a continuous subsequence of terms of ``f``. """ if n is None: j, m, n = 0, x, m else: j = f._gen_to_level(x) m, n = int(m), int(n) if hasattr(f.rep, 'slice'): result = f.rep.slice(m, n, j) else: # pragma: no cover raise OperationNotSupported(f, 'slice') return f.per(result) def coeffs(f, order=None): """ Returns all non-zero coefficients from ``f`` in lex order. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 + 2*x + 3, x).coeffs() [1, 2, 3] """ return [ f.rep.dom.to_sympy(c) for c in f.rep.coeffs(order=order) ] def monoms(f, order=None): """ Returns all non-zero monomials from ``f`` in lex order. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 2*x*y**2 + x*y + 3*y, x, y).monoms() [(2, 0), (1, 2), (1, 1), (0, 1)] """ return f.rep.monoms(order=order) def terms(f, order=None): """ Returns all non-zero terms from ``f`` in lex order. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 2*x*y**2 + x*y + 3*y, x, y).terms() [((2, 0), 1), ((1, 2), 2), ((1, 1), 1), ((0, 1), 3)] """ return [ (m, f.rep.dom.to_sympy(c)) for m, c in f.rep.terms(order=order) ] def all_coeffs(f): """ Returns all coefficients from a univariate polynomial ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 + 2*x - 1, x).all_coeffs() [1, 0, 2, -1] """ return [ f.rep.dom.to_sympy(c) for c in f.rep.all_coeffs() ] def all_monoms(f): """ Returns all monomials from a univariate polynomial ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 + 2*x - 1, x).all_monoms() [(3,), (2,), (1,), (0,)] """ return f.rep.all_monoms() def all_terms(f): """ Returns all terms from a univariate polynomial ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 + 2*x - 1, x).all_terms() [((3,), 1), ((2,), 0), ((1,), 2), ((0,), -1)] """ return [ (m, f.rep.dom.to_sympy(c)) for m, c in f.rep.all_terms() ] def termwise(f, func, *gens, **args): """ Apply a function to all terms of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> def func((k,), coeff): ... return coeff//10**(2-k) >>> Poly(x**2 + 20*x + 400).termwise(func) Poly(x**2 + 2*x + 4, x, domain='ZZ') """ terms = {} for monom, coeff in f.terms(): result = func(monom, coeff) if isinstance(result, tuple): monom, coeff = result else: coeff = result if coeff: if monom not in terms: terms[monom] = coeff else: raise PolynomialError("%s monomial was generated twice" % monom) return f.from_dict(terms, *(gens or f.gens), **args) def length(f): """ Returns the number of non-zero terms in ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 2*x - 1).length() 3 """ return len(f.as_dict()) def as_dict(f, native=False, zero=False): """ Switch to a ``dict`` representation. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 2*x*y**2 - y, x, y).as_dict() {(0, 1): -1, (1, 2): 2, (2, 0): 1} """ if native: return f.rep.to_dict(zero=zero) else: return f.rep.to_sympy_dict(zero=zero) def as_list(f, native=False): """Switch to a ``list`` representation. """ if native: return f.rep.to_list() else: return f.rep.to_sympy_list() def as_expr(f, *gens): """ Convert a polynomial an expression. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = Poly(x**2 + 2*x*y**2 - y, x, y) >>> f.as_expr() x**2 + 2*x*y**2 - y >>> f.as_expr({x: 5}) 10*y**2 - y + 25 >>> f.as_expr(5, 6) 379 """ if not gens: gens = f.gens elif len(gens) == 1 and isinstance(gens[0], dict): mapping = gens[0] gens = list(f.gens) for gen, value in mapping.iteritems(): try: index = gens.index(gen) except ValueError: raise GeneratorsError("%s doesn't have %s as generator" % (f, gen)) else: gens[index] = value return basic_from_dict(f.rep.to_sympy_dict(), *gens) def lift(f): """ Convert algebraic coefficients to rationals. **Examples** >>> from sympy import Poly, I >>> from sympy.abc import x >>> Poly(x**2 + I*x + 1, x, extension=I).lift() Poly(x**4 + 3*x**2 + 1, x, domain='QQ') """ if hasattr(f.rep, 'lift'): result = f.rep.lift() else: # pragma: no cover raise OperationNotSupported(f, 'lift') return f.per(result) def deflate(f): """ Reduce degree of ``f`` by mapping ``x_i**m`` to ``y_i``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**6*y**2 + x**3 + 1, x, y).deflate() ((3, 2), Poly(x**2*y + x + 1, x, y, domain='ZZ')) """ if hasattr(f.rep, 'deflate'): J, result = f.rep.deflate() else: # pragma: no cover raise OperationNotSupported(f, 'deflate') return J, f.per(result) def inject(f, front=False): """ Inject ground domain generators into ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = Poly(x**2*y + x*y**3 + x*y + 1, x) >>> f.inject() Poly(x**2*y + x*y**3 + x*y + 1, x, y, domain='ZZ') >>> f.inject(front=True) Poly(y**3*x + y*x**2 + y*x + 1, y, x, domain='ZZ') """ dom = f.rep.dom if dom.is_Numerical: return f elif not dom.is_Poly: raise DomainError("can't inject generators over %s" % dom) if hasattr(f.rep, 'inject'): result = f.rep.inject(front=front) else: # pragma: no cover raise OperationNotSupported(f, 'inject') if front: gens = dom.gens + f.gens else: gens = f.gens + dom.gens return f.new(result, *gens) def eject(f, *gens): """ Eject selected generators into the ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = Poly(x**2*y + x*y**3 + x*y + 1, x, y) >>> f.eject(x) Poly(x*y**3 + (x**2 + x)*y + 1, y, domain='ZZ[x]') >>> f.eject(y) Poly(y*x**2 + (y**3 + y)*x + 1, x, domain='ZZ[y]') """ dom = f.rep.dom if not dom.is_Numerical: raise DomainError("can't eject generators over %s" % dom) n, k = len(f.gens), len(gens) if f.gens[:k] == gens: _gens, front = f.gens[n-k:], True elif f.gens[-k:] == gens: _gens, front = f.gens[:n-k], False else: raise NotImplementedError("can only eject front or back generators") dom = dom.inject(*gens) if hasattr(f.rep, 'eject'): result = f.rep.eject(dom, front=front) else: # pragma: no cover raise OperationNotSupported(f, 'eject') return f.new(result, *_gens) def terms_gcd(f): """ Remove GCD of terms from the polynomial ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**6*y**2 + x**3*y, x, y).terms_gcd() ((3, 1), Poly(x**3*y + 1, x, y, domain='ZZ')) """ if hasattr(f.rep, 'terms_gcd'): J, result = f.rep.terms_gcd() else: # pragma: no cover raise OperationNotSupported(f, 'terms_gcd') return J, f.per(result) def add_ground(f, coeff): """ Add an element of the ground domain to ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x + 1).add_ground(2) Poly(x + 3, x, domain='ZZ') """ if hasattr(f.rep, 'add_ground'): result = f.rep.add_ground(coeff) else: # pragma: no cover raise OperationNotSupported(f, 'add_ground') return f.per(result) def sub_ground(f, coeff): """ Subtract an element of the ground domain from ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x + 1).sub_ground(2) Poly(x - 1, x, domain='ZZ') """ if hasattr(f.rep, 'sub_ground'): result = f.rep.sub_ground(coeff) else: # pragma: no cover raise OperationNotSupported(f, 'sub_ground') return f.per(result) def mul_ground(f, coeff): """ Multiply ``f`` by a an element of the ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x + 1).mul_ground(2) Poly(2*x + 2, x, domain='ZZ') """ if hasattr(f.rep, 'mul_ground'): result = f.rep.mul_ground(coeff) else: # pragma: no cover raise OperationNotSupported(f, 'mul_ground') return f.per(result) def quo_ground(f, coeff): """ Quotient of ``f`` by a an element of the ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x + 4).quo_ground(2) Poly(x + 2, x, domain='ZZ') >>> Poly(2*x + 3).quo_ground(2) Poly(x + 1, x, domain='ZZ') """ if hasattr(f.rep, 'quo_ground'): result = f.rep.quo_ground(coeff) else: # pragma: no cover raise OperationNotSupported(f, 'quo_ground') return f.per(result) def exquo_ground(f, coeff): """ Exact quotient of ``f`` by a an element of the ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x + 4).exquo_ground(2) Poly(x + 2, x, domain='ZZ') >>> Poly(2*x + 3).exquo_ground(2) Traceback (most recent call last): ... ExactQuotientFailed: 2 does not divide 3 in ZZ """ if hasattr(f.rep, 'exquo_ground'): result = f.rep.exquo_ground(coeff) else: # pragma: no cover raise OperationNotSupported(f, 'exquo_ground') return f.per(result) def abs(f): """ Make all coefficients in ``f`` positive. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).abs() Poly(x**2 + 1, x, domain='ZZ') """ if hasattr(f.rep, 'abs'): result = f.rep.abs() else: # pragma: no cover raise OperationNotSupported(f, 'abs') return f.per(result) def neg(f): """ Negate all coefficients in ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).neg() Poly(-x**2 + 1, x, domain='ZZ') >>> -Poly(x**2 - 1, x) Poly(-x**2 + 1, x, domain='ZZ') """ if hasattr(f.rep, 'neg'): result = f.rep.neg() else: # pragma: no cover raise OperationNotSupported(f, 'neg') return f.per(result) def add(f, g): """ Add two polynomials ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).add(Poly(x - 2, x)) Poly(x**2 + x - 1, x, domain='ZZ') >>> Poly(x**2 + 1, x) + Poly(x - 2, x) Poly(x**2 + x - 1, x, domain='ZZ') """ g = sympify(g) if not g.is_Poly: return f.add_ground(g) _, per, F, G = f._unify(g) if hasattr(f.rep, 'add'): result = F.add(G) else: # pragma: no cover raise OperationNotSupported(f, 'add') return per(result) def sub(f, g): """ Subtract two polynomials ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).sub(Poly(x - 2, x)) Poly(x**2 - x + 3, x, domain='ZZ') >>> Poly(x**2 + 1, x) - Poly(x - 2, x) Poly(x**2 - x + 3, x, domain='ZZ') """ g = sympify(g) if not g.is_Poly: return f.sub_ground(g) _, per, F, G = f._unify(g) if hasattr(f.rep, 'sub'): result = F.sub(G) else: # pragma: no cover raise OperationNotSupported(f, 'sub') return per(result) def mul(f, g): """ Multiply two polynomials ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).mul(Poly(x - 2, x)) Poly(x**3 - 2*x**2 + x - 2, x, domain='ZZ') >>> Poly(x**2 + 1, x)*Poly(x - 2, x) Poly(x**3 - 2*x**2 + x - 2, x, domain='ZZ') """ g = sympify(g) if not g.is_Poly: return f.mul_ground(g) _, per, F, G = f._unify(g) if hasattr(f.rep, 'mul'): result = F.mul(G) else: # pragma: no cover raise OperationNotSupported(f, 'mul') return per(result) def sqr(f): """ Square a polynomial ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x - 2, x).sqr() Poly(x**2 - 4*x + 4, x, domain='ZZ') >>> Poly(x - 2, x)**2 Poly(x**2 - 4*x + 4, x, domain='ZZ') """ if hasattr(f.rep, 'sqr'): result = f.rep.sqr() else: # pragma: no cover raise OperationNotSupported(f, 'sqr') return f.per(result) def pow(f, n): """ Raise ``f`` to a non-negative power ``n``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x - 2, x).pow(3) Poly(x**3 - 6*x**2 + 12*x - 8, x, domain='ZZ') >>> Poly(x - 2, x)**3 Poly(x**3 - 6*x**2 + 12*x - 8, x, domain='ZZ') """ n = int(n) if hasattr(f.rep, 'pow'): result = f.rep.pow(n) else: # pragma: no cover raise OperationNotSupported(f, 'pow') return f.per(result) def pdiv(f, g): """ Polynomial pseudo-division of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).pdiv(Poly(2*x - 4, x)) (Poly(2*x + 4, x, domain='ZZ'), Poly(20, x, domain='ZZ')) """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'pdiv'): q, r = F.pdiv(G) else: # pragma: no cover raise OperationNotSupported(f, 'pdiv') return per(q), per(r) def prem(f, g): """ Polynomial pseudo-remainder of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).prem(Poly(2*x - 4, x)) Poly(20, x, domain='ZZ') """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'prem'): result = F.prem(G) else: # pragma: no cover raise OperationNotSupported(f, 'prem') return per(result) def pquo(f, g): """ Polynomial pseudo-quotient of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).pquo(Poly(2*x - 4, x)) Poly(2*x + 4, x, domain='ZZ') >>> Poly(x**2 - 1, x).pquo(Poly(2*x - 2, x)) Poly(2*x + 2, x, domain='ZZ') """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'pquo'): result = F.pquo(G) else: # pragma: no cover raise OperationNotSupported(f, 'pquo') return per(result) def pexquo(f, g): """ Polynomial exact pseudo-quotient of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).pexquo(Poly(2*x - 2, x)) Poly(2*x + 2, x, domain='ZZ') >>> Poly(x**2 + 1, x).pexquo(Poly(2*x - 4, x)) Traceback (most recent call last): ... ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'pexquo'): try: result = F.pexquo(G) except ExactQuotientFailed, exc: raise exc.new(f.as_expr(), g.as_expr()) else: # pragma: no cover raise OperationNotSupported(f, 'pexquo') return per(result) def div(f, g, auto=True): """ Polynomial division with remainder of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).div(Poly(2*x - 4, x)) (Poly(1/2*x + 1, x, domain='QQ'), Poly(5, x, domain='QQ')) >>> Poly(x**2 + 1, x).div(Poly(2*x - 4, x), auto=False) (Poly(0, x, domain='ZZ'), Poly(x**2 + 1, x, domain='ZZ')) """ dom, per, F, G = f._unify(g) retract = False if auto and dom.has_Ring and not dom.has_Field: F, G = F.to_field(), G.to_field() retract = True if hasattr(f.rep, 'div'): q, r = F.div(G) else: # pragma: no cover raise OperationNotSupported(f, 'div') if retract: try: Q, R = q.to_ring(), r.to_ring() except CoercionFailed: pass else: q, r = Q, R return per(q), per(r) def rem(f, g, auto=True): """ Computes the polynomial remainder of ``f`` by ``g``. **Examples** >>> from sympy import Poly, ZZ, QQ >>> from sympy.abc import x >>> Poly(x**2 + 1, x).rem(Poly(2*x - 4, x)) Poly(5, x, domain='ZZ') >>> Poly(x**2 + 1, x).rem(Poly(2*x - 4, x), auto=False) Poly(x**2 + 1, x, domain='ZZ') """ dom, per, F, G = f._unify(g) retract = False if auto and dom.has_Ring and not dom.has_Field: F, G = F.to_field(), G.to_field() retract = True if hasattr(f.rep, 'rem'): r = F.rem(G) else: # pragma: no cover raise OperationNotSupported(f, 'rem') if retract: try: r = r.to_ring() except CoercionFailed: pass return per(r) def quo(f, g, auto=True): """ Computes polynomial quotient of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).quo(Poly(2*x - 4, x)) Poly(1/2*x + 1, x, domain='QQ') >>> Poly(x**2 - 1, x).quo(Poly(x - 1, x)) Poly(x + 1, x, domain='ZZ') """ dom, per, F, G = f._unify(g) retract = False if auto and dom.has_Ring and not dom.has_Field: F, G = F.to_field(), G.to_field() retract = True if hasattr(f.rep, 'quo'): q = F.quo(G) else: # pragma: no cover raise OperationNotSupported(f, 'quo') if retract: try: q = q.to_ring() except CoercionFailed: pass return per(q) def exquo(f, g, auto=True): """ Computes polynomial exact quotient of ``f`` by ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).exquo(Poly(x - 1, x)) Poly(x + 1, x, domain='ZZ') >>> Poly(x**2 + 1, x).exquo(Poly(2*x - 4, x)) Traceback (most recent call last): ... ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 """ dom, per, F, G = f._unify(g) retract = False if auto and dom.has_Ring and not dom.has_Field: F, G = F.to_field(), G.to_field() retract = True if hasattr(f.rep, 'exquo'): try: q = F.exquo(G) except ExactQuotientFailed, exc: raise exc.new(f.as_expr(), g.as_expr()) else: # pragma: no cover raise OperationNotSupported(f, 'exquo') if retract: try: q = q.to_ring() except CoercionFailed: pass return per(q) def _gen_to_level(f, gen): """Returns level associated with the given generator. """ if isinstance(gen, int): length = len(f.gens) if -length <= gen < length: if gen < 0: return length + gen else: return gen else: raise PolynomialError("-%s <= gen < %s expected, got %s" % (length, length, gen)) else: try: return list(f.gens).index(sympify(gen)) except ValueError: raise PolynomialError("a valid generator expected, got %s" % gen) def degree(f, gen=0): """ Returns degree of ``f`` in ``x_j``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + y*x + 1, x, y).degree() 2 >>> Poly(x**2 + y*x + y, x, y).degree(y) 1 """ j = f._gen_to_level(gen) if hasattr(f.rep, 'degree'): return f.rep.degree(j) else: # pragma: no cover raise OperationNotSupported(f, 'degree') def degree_list(f): """ Returns a list of degrees of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + y*x + 1, x, y).degree_list() (2, 1) """ if hasattr(f.rep, 'degree_list'): return f.rep.degree_list() else: # pragma: no cover raise OperationNotSupported(f, 'degree_list') def total_degree(f): """ Returns the total degree of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + y*x + 1, x, y).total_degree() 3 """ if hasattr(f.rep, 'total_degree'): return f.rep.total_degree() else: # pragma: no cover raise OperationNotSupported(f, 'total_degree') def LC(f, order=None): """ Returns the leading coefficient of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(4*x**3 + 2*x**2 + 3*x, x).LC() 4 """ if order is not None: return f.coeffs(order)[0] if hasattr(f.rep, 'LC'): result = f.rep.LC() else: # pragma: no cover raise OperationNotSupported(f, 'LC') return f.rep.dom.to_sympy(result) def TC(f): """ Returns the trailing coefficent of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 + 2*x**2 + 3*x, x).TC() 0 """ if hasattr(f.rep, 'TC'): result = f.rep.TC() else: # pragma: no cover raise OperationNotSupported(f, 'TC') return f.rep.dom.to_sympy(result) def EC(f, order=None): """ Returns the last non-zero coefficient of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 + 2*x**2 + 3*x, x).EC() 3 """ if hasattr(f.rep, 'coeffs'): return f.coeffs(order)[-1] else: # pragma: no cover raise OperationNotSupported(f, 'EC') def nth(f, *N): """ Returns the ``n``-th coefficient of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**3 + 2*x**2 + 3*x, x).nth(2) 2 >>> Poly(x**3 + 2*x*y**2 + y**2, x, y).nth(1, 2) 2 """ if hasattr(f.rep, 'nth'): result = f.rep.nth(*map(int, N)) else: # pragma: no cover raise OperationNotSupported(f, 'nth') return f.rep.dom.to_sympy(result) def LM(f, order=None): """ Returns the leading monomial of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).LM() (2, 0) """ return f.monoms(order)[0] def EM(f, order=None): """ Returns the last non-zero monomial of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).EM() (0, 1) """ return f.monoms(order)[-1] def LT(f, order=None): """ Returns the leading term of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).LT() ((2, 0), 4) """ return f.terms(order)[0] def ET(f, order=None): """ Returns the last non-zero term of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(4*x**2 + 2*x*y**2 + x*y + 3*y, x, y).ET() ((0, 1), 3) """ return f.terms(order)[-1] def max_norm(f): """ Returns maximum norm of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(-x**2 + 2*x - 3, x).max_norm() 3 """ if hasattr(f.rep, 'max_norm'): result = f.rep.max_norm() else: # pragma: no cover raise OperationNotSupported(f, 'max_norm') return f.rep.dom.to_sympy(result) def l1_norm(f): """ Returns l1 norm of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(-x**2 + 2*x - 3, x).l1_norm() 6 """ if hasattr(f.rep, 'l1_norm'): result = f.rep.l1_norm() else: # pragma: no cover raise OperationNotSupported(f, 'l1_norm') return f.rep.dom.to_sympy(result) def clear_denoms(f, convert=False): """ Clear denominators, but keep the ground domain. **Examples** >>> from sympy import Poly, S, QQ >>> from sympy.abc import x >>> f = Poly(x/2 + S(1)/3, x, domain=QQ) >>> f.clear_denoms() (6, Poly(3*x + 2, x, domain='QQ')) >>> f.clear_denoms(convert=True) (6, Poly(3*x + 2, x, domain='ZZ')) """ if not f.rep.dom.has_Field: return S.One, f dom = f.rep.dom.get_ring() if hasattr(f.rep, 'clear_denoms'): coeff, result = f.rep.clear_denoms() else: # pragma: no cover raise OperationNotSupported(f, 'clear_denoms') coeff, f = dom.to_sympy(coeff), f.per(result) if not convert: return coeff, f else: return coeff, f.to_ring() def rat_clear_denoms(f, g): """ Clear denominators in a rational function ``f/g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = Poly(x**2/y + 1, x) >>> g = Poly(x**3 + y, x) >>> p, q = f.rat_clear_denoms(g) >>> p Poly(x**2 + y, x, domain='ZZ[y]') >>> q Poly(y*x**3 + y**2, x, domain='ZZ[y]') """ dom, per, f, g = f._unify(g) f = per(f) g = per(g) if not (dom.has_Field and dom.has_assoc_Ring): return f, g a, f = f.clear_denoms(convert=True) b, g = g.clear_denoms(convert=True) f = f.mul_ground(b) g = g.mul_ground(a) return f, g def integrate(f, *specs, **args): """ Computes indefinite integral of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 2*x + 1, x).integrate() Poly(1/3*x**3 + x**2 + x, x, domain='QQ') >>> Poly(x*y**2 + x, x, y).integrate((0, 1), (1, 0)) Poly(1/2*x**2*y**2 + 1/2*x**2, x, y, domain='QQ') """ if args.get('auto', True) and f.rep.dom.has_Ring: f = f.to_field() if hasattr(f.rep, 'integrate'): if not specs: return f.per(f.rep.integrate(m=1)) rep = f.rep for spec in specs: if type(spec) is tuple: gen, m = spec else: gen, m = spec, 1 rep = rep.integrate(int(m), f._gen_to_level(gen)) return f.per(rep) else: # pragma: no cover raise OperationNotSupported(f, 'integrate') def diff(f, *specs): """ Computes partial derivative of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + 2*x + 1, x).diff() Poly(2*x + 2, x, domain='ZZ') >>> Poly(x*y**2 + x, x, y).diff((0, 0), (1, 1)) Poly(2*x*y, x, y, domain='ZZ') """ if hasattr(f.rep, 'diff'): if not specs: return f.per(f.rep.diff(m=1)) rep = f.rep for spec in specs: if type(spec) is tuple: gen, m = spec else: gen, m = spec, 1 rep = rep.diff(int(m), f._gen_to_level(gen)) return f.per(rep) else: # pragma: no cover raise OperationNotSupported(f, 'diff') def eval(f, x, a=None, auto=True): """ Evaluate ``f`` at ``a`` in the given variable. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y, z >>> Poly(x**2 + 2*x + 3, x).eval(2) 11 >>> Poly(2*x*y + 3*x + y + 2, x, y).eval(x, 2) Poly(5*y + 8, y, domain='ZZ') >>> f = Poly(2*x*y + 3*x + y + 2*z, x, y, z) >>> f.eval({x: 2}) Poly(5*y + 2*z + 6, y, z, domain='ZZ') >>> f.eval({x: 2, y: 5}) Poly(2*z + 31, z, domain='ZZ') >>> f.eval({x: 2, y: 5, z: 7}) 45 """ if a is None: if isinstance(x, (list, dict)): try: mapping = x.items() except AttributeError: mapping = x for gen, value in mapping: f = f.eval(gen, value) return f else: j, a = 0, x else: j = f._gen_to_level(x) if not hasattr(f.rep, 'eval'): # pragma: no cover raise OperationNotSupported(f, 'eval') try: result = f.rep.eval(a, j) except CoercionFailed: if not auto: raise DomainError("can't evaluate at %s in %s" % (a, f.rep.dom)) else: domain, [a] = construct_domain([a]) f = f.set_domain(domain) result = f.rep.eval(a, j) return f.per(result, remove=j) def half_gcdex(f, g, auto=True): """ Half extended Euclidean algorithm of ``f`` and ``g``. Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 >>> g = x**3 + x**2 - 4*x - 4 >>> Poly(f).half_gcdex(Poly(g)) (Poly(-1/5*x + 3/5, x, domain='QQ'), Poly(x + 1, x, domain='QQ')) """ dom, per, F, G = f._unify(g) if auto and dom.has_Ring: F, G = F.to_field(), G.to_field() if hasattr(f.rep, 'half_gcdex'): s, h = F.half_gcdex(G) else: # pragma: no cover raise OperationNotSupported(f, 'half_gcdex') return per(s), per(h) def gcdex(f, g, auto=True): """ Extended Euclidean algorithm of ``f`` and ``g``. Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 >>> g = x**3 + x**2 - 4*x - 4 >>> Poly(f).gcdex(Poly(g)) (Poly(-1/5*x + 3/5, x, domain='QQ'), Poly(1/5*x**2 - 6/5*x + 2, x, domain='QQ'), Poly(x + 1, x, domain='QQ')) """ dom, per, F, G = f._unify(g) if auto and dom.has_Ring: F, G = F.to_field(), G.to_field() if hasattr(f.rep, 'gcdex'): s, t, h = F.gcdex(G) else: # pragma: no cover raise OperationNotSupported(f, 'gcdex') return per(s), per(t), per(h) def invert(f, g, auto=True): """ Invert ``f`` modulo ``g`` when possible. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).invert(Poly(2*x - 1, x)) Poly(-4/3, x, domain='QQ') >>> Poly(x**2 - 1, x).invert(Poly(x - 1, x)) Traceback (most recent call last): ... NotInvertible: zero divisor """ dom, per, F, G = f._unify(g) if auto and dom.has_Ring: F, G = F.to_field(), G.to_field() if hasattr(f.rep, 'invert'): result = F.invert(G) else: # pragma: no cover raise OperationNotSupported(f, 'invert') return per(result) def revert(f, n): """Compute ``f**(-1)`` mod ``x**n``. """ if hasattr(f.rep, 'revert'): result = f.rep.revert(int(n)) else: # pragma: no cover raise OperationNotSupported(f, 'revert') return f.per(result) def subresultants(f, g): """ Computes the subresultant PRS sequence of ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).subresultants(Poly(x**2 - 1, x)) [Poly(x**2 + 1, x, domain='ZZ'), Poly(x**2 - 1, x, domain='ZZ'), Poly(-2, x, domain='ZZ')] """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'subresultants'): result = F.subresultants(G) else: # pragma: no cover raise OperationNotSupported(f, 'subresultants') return map(per, result) def resultant(f, g): """ Computes the resultant of ``f`` and ``g`` via PRS. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 1, x).resultant(Poly(x**2 - 1, x)) 4 """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'resultant'): result = F.resultant(G) else: # pragma: no cover raise OperationNotSupported(f, 'resultant') return per(result, remove=0) def discriminant(f): """ Computes the discriminant of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + 2*x + 3, x).discriminant() -8 """ if hasattr(f.rep, 'discriminant'): result = f.rep.discriminant() else: # pragma: no cover raise OperationNotSupported(f, 'discriminant') return f.per(result, remove=0) def cofactors(f, g): """ Returns the GCD of ``f`` and ``g`` and their cofactors. Returns polynomials ``(h, cff, cfg)`` such that ``h = gcd(f, g)``, and ``cff = quo(f, h)`` and ``cfg = quo(g, h)`` are, so called, cofactors of ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).cofactors(Poly(x**2 - 3*x + 2, x)) (Poly(x - 1, x, domain='ZZ'), Poly(x + 1, x, domain='ZZ'), Poly(x - 2, x, domain='ZZ')) """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'cofactors'): h, cff, cfg = F.cofactors(G) else: # pragma: no cover raise OperationNotSupported(f, 'cofactors') return per(h), per(cff), per(cfg) def gcd(f, g): """ Returns the polynomial GCD of ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).gcd(Poly(x**2 - 3*x + 2, x)) Poly(x - 1, x, domain='ZZ') """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'gcd'): result = F.gcd(G) else: # pragma: no cover raise OperationNotSupported(f, 'gcd') return per(result) def lcm(f, g): """ Returns polynomial LCM of ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 1, x).lcm(Poly(x**2 - 3*x + 2, x)) Poly(x**3 - 2*x**2 - x + 2, x, domain='ZZ') """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'lcm'): result = F.lcm(G) else: # pragma: no cover raise OperationNotSupported(f, 'lcm') return per(result) def trunc(f, p): """ Reduce ``f`` modulo a constant ``p``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x**3 + 3*x**2 + 5*x + 7, x).trunc(3) Poly(-x**3 - x + 1, x, domain='ZZ') """ p = f.rep.dom.convert(p) if hasattr(f.rep, 'trunc'): result = f.rep.trunc(p) else: # pragma: no cover raise OperationNotSupported(f, 'trunc') return f.per(result) def monic(f, auto=True): """ Divides all coefficients by ``LC(f)``. **Examples** >>> from sympy import Poly, ZZ >>> from sympy.abc import x >>> Poly(3*x**2 + 6*x + 9, x, domain=ZZ).monic() Poly(x**2 + 2*x + 3, x, domain='QQ') >>> Poly(3*x**2 + 4*x + 2, x, domain=ZZ).monic() Poly(x**2 + 4/3*x + 2/3, x, domain='QQ') """ if auto and f.rep.dom.has_Ring: f = f.to_field() if hasattr(f.rep, 'monic'): result = f.rep.monic() else: # pragma: no cover raise OperationNotSupported(f, 'monic') return f.per(result) def content(f): """ Returns the GCD of polynomial coefficients. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(6*x**2 + 8*x + 12, x).content() 2 """ if hasattr(f.rep, 'content'): result = f.rep.content() else: # pragma: no cover raise OperationNotSupported(f, 'content') return f.rep.dom.to_sympy(result) def primitive(f): """ Returns the content and a primitive form of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x**2 + 8*x + 12, x).primitive() (2, Poly(x**2 + 4*x + 6, x, domain='ZZ')) """ if hasattr(f.rep, 'primitive'): cont, result = f.rep.primitive() else: # pragma: no cover raise OperationNotSupported(f, 'primitive') return f.rep.dom.to_sympy(cont), f.per(result) def compose(f, g): """ Computes the functional composition of ``f`` and ``g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + x, x).compose(Poly(x - 1, x)) Poly(x**2 - x, x, domain='ZZ') """ _, per, F, G = f._unify(g) if hasattr(f.rep, 'compose'): result = F.compose(G) else: # pragma: no cover raise OperationNotSupported(f, 'compose') return per(result) def decompose(f): """ Computes a functional decomposition of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**4 + 2*x**3 - x - 1, x, domain='ZZ').decompose() [Poly(x**2 - x - 1, x, domain='ZZ'), Poly(x**2 + x, x, domain='ZZ')] """ if hasattr(f.rep, 'decompose'): result = f.rep.decompose() else: # pragma: no cover raise OperationNotSupported(f, 'decompose') return map(f.per, result) def shift(f, a): """ Efficiently compute Taylor shift ``f(x + a)``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 2*x + 1, x).shift(2) Poly(x**2 + 2*x + 1, x, domain='ZZ') """ if hasattr(f.rep, 'shift'): result = f.rep.shift(a) else: # pragma: no cover raise OperationNotSupported(f, 'shift') return f.per(result) def sturm(f, auto=True): """ Computes the Sturm sequence of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 - 2*x**2 + x - 3, x).sturm() [Poly(x**3 - 2*x**2 + x - 3, x, domain='QQ'), Poly(3*x**2 - 4*x + 1, x, domain='QQ'), Poly(2/9*x + 25/9, x, domain='QQ'), Poly(-2079/4, x, domain='QQ')] """ if auto and f.rep.dom.has_Ring: f = f.to_field() if hasattr(f.rep, 'sturm'): result = f.rep.sturm() else: # pragma: no cover raise OperationNotSupported(f, 'sturm') return map(f.per, result) def gff_list(f): """ Computes greatest factorial factorization of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = x**5 + 2*x**4 - x**3 - 2*x**2 >>> Poly(f).gff_list() [(Poly(x, x, domain='ZZ'), 1), (Poly(x + 2, x, domain='ZZ'), 4)] """ if hasattr(f.rep, 'gff_list'): result = f.rep.gff_list() else: # pragma: no cover raise OperationNotSupported(f, 'gff_list') return [ (f.per(g), k) for g, k in result ] def sqf_norm(f): """ Computes square-free norm of ``f``. Returns ``s``, ``f``, ``r``, such that ``g(x) = f(x-sa)`` and ``r(x) = Norm(g(x))`` is a square-free polynomial over ``K``, where ``a`` is the algebraic extension of the ground domain. **Examples** >>> from sympy import Poly, sqrt >>> from sympy.abc import x >>> s, f, r = Poly(x**2 + 1, x, extension=[sqrt(3)]).sqf_norm() >>> s 1 >>> f Poly(x**2 - 2*3**(1/2)*x + 4, x, domain='QQ<3**(1/2)>') >>> r Poly(x**4 - 4*x**2 + 16, x, domain='QQ') """ if hasattr(f.rep, 'sqf_norm'): s, g, r = f.rep.sqf_norm() else: # pragma: no cover raise OperationNotSupported(f, 'sqf_norm') return s, f.per(g), f.per(r) def sqf_part(f): """ Computes square-free part of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**3 - 3*x - 2, x).sqf_part() Poly(x**2 - x - 2, x, domain='ZZ') """ if hasattr(f.rep, 'sqf_part'): result = f.rep.sqf_part() else: # pragma: no cover raise OperationNotSupported(f, 'sqf_part') return f.per(result) def sqf_list(f, all=False): """ Returns a list of square-free factors of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = 2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16 >>> Poly(f).sqf_list() (2, [(Poly(x + 1, x, domain='ZZ'), 2), (Poly(x + 2, x, domain='ZZ'), 3)]) >>> Poly(f).sqf_list(all=True) (2, [(Poly(1, x, domain='ZZ'), 1), (Poly(x + 1, x, domain='ZZ'), 2), (Poly(x + 2, x, domain='ZZ'), 3)]) """ if hasattr(f.rep, 'sqf_list'): coeff, factors = f.rep.sqf_list(all) else: # pragma: no cover raise OperationNotSupported(f, 'sqf_list') return f.rep.dom.to_sympy(coeff), [ (f.per(g), k) for g, k in factors ] def sqf_list_include(f, all=False): """ Returns a list of square-free factors of ``f``. **Examples** >>> from sympy import Poly, expand >>> from sympy.abc import x >>> f = expand(2*(x + 1)**3*x**4) >>> f 2*x**7 + 6*x**6 + 6*x**5 + 2*x**4 >>> Poly(f).sqf_list_include() [(Poly(2, x, domain='ZZ'), 1), (Poly(x + 1, x, domain='ZZ'), 3), (Poly(x, x, domain='ZZ'), 4)] >>> Poly(f).sqf_list_include(all=True) [(Poly(2, x, domain='ZZ'), 1), (Poly(1, x, domain='ZZ'), 2), (Poly(x + 1, x, domain='ZZ'), 3), (Poly(x, x, domain='ZZ'), 4)] """ if hasattr(f.rep, 'sqf_list_include'): factors = f.rep.sqf_list_include(all) else: # pragma: no cover raise OperationNotSupported(f, 'sqf_list_include') return [ (f.per(g), k) for g, k in factors ] def factor_list(f): """ Returns a list of irreducible factors of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = 2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y >>> Poly(f).factor_list() (2, [(Poly(x + y, x, y, domain='ZZ'), 1), (Poly(x**2 + 1, x, y, domain='ZZ'), 2)]) """ if hasattr(f.rep, 'factor_list'): try: coeff, factors = f.rep.factor_list() except DomainError: return S.One, [(f, 1)] else: # pragma: no cover raise OperationNotSupported(f, 'factor_list') return f.rep.dom.to_sympy(coeff), [ (f.per(g), k) for g, k in factors ] def factor_list_include(f): """ Returns a list of irreducible factors of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> f = 2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y >>> Poly(f).factor_list_include() [(Poly(2*x + 2*y, x, y, domain='ZZ'), 1), (Poly(x**2 + 1, x, y, domain='ZZ'), 2)] """ if hasattr(f.rep, 'factor_list_include'): try: factors = f.rep.factor_list_include() except DomainError: return [(f, 1)] else: # pragma: no cover raise OperationNotSupported(f, 'factor_list_include') return [ (f.per(g), k) for g, k in factors ] def intervals(f, all=False, eps=None, inf=None, sup=None, fast=False, sqf=False): """ Compute isolating intervals for roots of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 3, x).intervals() [((-2, -1), 1), ((1, 2), 1)] >>> Poly(x**2 - 3, x).intervals(eps=1e-2) [((-26/15, -19/11), 1), ((19/11, 26/15), 1)] """ if eps is not None: eps = QQ.convert(eps) if eps <= 0: raise ValueError("'eps' must be a positive rational") if inf is not None: inf = QQ.convert(inf) if sup is not None: sup = QQ.convert(sup) if hasattr(f.rep, 'intervals'): result = f.rep.intervals(all=all, eps=eps, inf=inf, sup=sup, fast=fast, sqf=sqf) else: # pragma: no cover raise OperationNotSupported(f, 'intervals') if sqf: def _real(interval): s, t = interval return (QQ.to_sympy(s), QQ.to_sympy(t)) if not all: return map(_real, result) def _complex(rectangle): (u, v), (s, t) = rectangle return (QQ.to_sympy(u) + I*QQ.to_sympy(v), QQ.to_sympy(s) + I*QQ.to_sympy(t)) real_part, complex_part = result return map(_real, real_part), map(_complex, complex_part) else: def _real(interval): (s, t), k = interval return ((QQ.to_sympy(s), QQ.to_sympy(t)), k) if not all: return map(_real, result) def _complex(rectangle): ((u, v), (s, t)), k = rectangle return ((QQ.to_sympy(u) + I*QQ.to_sympy(v), QQ.to_sympy(s) + I*QQ.to_sympy(t)), k) real_part, complex_part = result return map(_real, real_part), map(_complex, complex_part) def refine_root(f, s, t, eps=None, steps=None, fast=False, check_sqf=False): """ Refine an isolating interval of a root to the given precision. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 3, x).refine_root(1, 2, eps=1e-2) (19/11, 26/15) """ if check_sqf and not f.is_sqf: raise PolynomialError("only square-free polynomials supported") s, t = QQ.convert(s), QQ.convert(t) if eps is not None: eps = QQ.convert(eps) if eps <= 0: raise ValueError("'eps' must be a positive rational") if steps is not None: steps = int(steps) elif eps is None: steps = 1 if hasattr(f.rep, 'refine_root'): S, T = f.rep.refine_root(s, t, eps=eps, steps=steps, fast=fast) else: # pragma: no cover raise OperationNotSupported(f, 'refine_root') return QQ.to_sympy(S), QQ.to_sympy(T) def count_roots(f, inf=None, sup=None): """ Return the number of roots of ``f`` in ``[inf, sup]`` interval. **Examples** >>> from sympy import Poly, I >>> from sympy.abc import x >>> Poly(x**4 - 4, x).count_roots(-3, 3) 2 >>> Poly(x**4 - 4, x).count_roots(0, 1 + 3*I) 1 """ inf_real, sup_real = True, True if inf is not None: inf = sympify(inf) if inf is S.NegativeInfinity: inf = None else: re, im = inf.as_real_imag() if not im: inf = QQ.convert(inf) else: inf, inf_real = map(QQ.convert, (re, im)), False if sup is not None: sup = sympify(sup) if sup is S.Infinity: sup = None else: re, im = sup.as_real_imag() if not im: sup = QQ.convert(sup) else: sup, sup_real = map(QQ.convert, (re, im)), False if inf_real and sup_real: if hasattr(f.rep, 'count_real_roots'): count = f.rep.count_real_roots(inf=inf, sup=sup) else: # pragma: no cover raise OperationNotSupported(f, 'count_real_roots') else: if inf_real and inf is not None: inf = (inf, QQ.zero) if sup_real and sup is not None: sup = (sup, QQ.zero) if hasattr(f.rep, 'count_complex_roots'): count = f.rep.count_complex_roots(inf=inf, sup=sup) else: # pragma: no cover raise OperationNotSupported(f, 'count_complex_roots') return Integer(count) def root(f, index, radicals=True): """ Get an indexed root of a polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = Poly(2*x**3 - 7*x**2 + 4*x + 4) >>> f.root(0) -1/2 >>> f.root(1) 2 >>> f.root(2) 2 >>> f.root(3) Traceback (most recent call last): ... IndexError: root index out of [-3, 2] range, got 3 >>> Poly(x**5 + x + 1).root(0) RootOf(x**3 - x**2 + 1, 0) """ return sympy.polys.rootoftools.RootOf(f, index, radicals=radicals) def real_roots(f, multiple=True, radicals=True): """ Return a list of real roots with multiplicities. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x**3 - 7*x**2 + 4*x + 4).real_roots() [-1/2, 2, 2] >>> Poly(x**3 + x + 1).real_roots() [RootOf(x**3 + x + 1, 0)] """ reals = sympy.polys.rootoftools.RootOf.real_roots(f, radicals=radicals) if multiple: return reals else: return group(reals, multiple=False) def all_roots(f, multiple=True, radicals=True): """ Return a list of real and complex roots with multiplicities. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x**3 - 7*x**2 + 4*x + 4).all_roots() [-1/2, 2, 2] >>> Poly(x**3 + x + 1).all_roots() [RootOf(x**3 + x + 1, 0), RootOf(x**3 + x + 1, 1), RootOf(x**3 + x + 1, 2)] """ roots = sympy.polys.rootoftools.RootOf.all_roots(f, radicals=radicals) if multiple: return roots else: return group(roots, multiple=False) def nroots(f, n=15, maxsteps=50, cleanup=True, error=False): """ Compute numerical approximations of roots of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 3).nroots(n=15) [-1.73205080756888, 1.73205080756888] >>> Poly(x**2 - 3).nroots(n=30) [-1.73205080756887729352744634151, 1.73205080756887729352744634151] """ if f.is_multivariate: raise MultivariatePolynomialError("can't compute numerical roots " "of %s" % f) if f.degree() <= 0: return [] coeffs = [ coeff.evalf(n=n).as_real_imag() for coeff in f.all_coeffs() ] dps = sympy.mpmath.mp.dps sympy.mpmath.mp.dps = n try: try: coeffs = [ sympy.mpmath.mpc(*coeff) for coeff in coeffs ] except TypeError: raise DomainError("numerical domain expected, got %s" % f.rep.dom) result = sympy.mpmath.polyroots(coeffs, maxsteps=maxsteps, cleanup=cleanup, error=error) if error: roots, error = result else: roots, error = result, None roots = map(sympify, sorted(roots, key=lambda r: (r.real, r.imag))) finally: sympy.mpmath.mp.dps = dps if error is not None: return roots, sympify(error) else: return roots def ground_roots(f): """ Compute roots of ``f`` by factorization in the ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**6 - 4*x**4 + 4*x**3 - x**2).ground_roots() {0: 2, 1: 2} """ if f.is_multivariate: raise MultivariatePolynomialError("can't compute ground roots of %s" % f) roots = {} for factor, k in f.factor_list()[1]: if factor.is_linear: a, b = factor.all_coeffs() roots[-b/a] = k return roots def nth_power_roots_poly(f, n): """ Construct a polynomial with n-th powers of roots of ``f``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = Poly(x**4 - x**2 + 1) >>> f.nth_power_roots_poly(2) Poly(x**4 - 2*x**3 + 3*x**2 - 2*x + 1, x, domain='ZZ') >>> f.nth_power_roots_poly(3) Poly(x**4 + 2*x**2 + 1, x, domain='ZZ') >>> f.nth_power_roots_poly(4) Poly(x**4 + 2*x**3 + 3*x**2 + 2*x + 1, x, domain='ZZ') >>> f.nth_power_roots_poly(12) Poly(x**4 - 4*x**3 + 6*x**2 - 4*x + 1, x, domain='ZZ') """ if f.is_multivariate: raise MultivariatePolynomialError("must be a univariate polynomial") N = sympify(n) if N.is_Integer and N >= 1: n = int(N) else: raise ValueError("'n' must an integer and n >= 1, got %s" % n) x = f.gen t = Dummy('t') r = f.resultant(f.__class__.from_expr(x**n - t, x, t)) return r.replace(t, x) def cancel(f, g, include=False): """ Cancel common factors in a rational function ``f/g``. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x**2 - 2, x).cancel(Poly(x**2 - 2*x + 1, x)) (1, Poly(2*x + 2, x, domain='ZZ'), Poly(x - 1, x, domain='ZZ')) >>> Poly(2*x**2 - 2, x).cancel(Poly(x**2 - 2*x + 1, x), include=True) (Poly(2*x + 2, x, domain='ZZ'), Poly(x - 1, x, domain='ZZ')) """ dom, per, F, G = f._unify(g) if hasattr(F, 'cancel'): result = F.cancel(G, include=include) else: # pragma: no cover raise OperationNotSupported(f, 'cancel') if not include: if dom.has_assoc_Ring: dom = dom.get_ring() cp, cq, p, q = result cp = dom.to_sympy(cp) cq = dom.to_sympy(cq) return cp/cq, per(p), per(q) else: return tuple(map(per, result)) @property def is_zero(f): """ Returns ``True`` if ``f`` is a zero polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(0, x).is_zero True >>> Poly(1, x).is_zero False """ return f.rep.is_zero @property def is_one(f): """ Returns ``True`` if ``f`` is a unit polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(0, x).is_one False >>> Poly(1, x).is_one True """ return f.rep.is_one @property def is_sqf(f): """ Returns ``True`` if ``f`` is a square-free polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 - 2*x + 1, x).is_sqf False >>> Poly(x**2 - 1, x).is_sqf True """ return f.rep.is_sqf @property def is_monic(f): """ Returns ``True`` if the leading coefficient of ``f`` is one. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x + 2, x).is_monic True >>> Poly(2*x + 2, x).is_monic False """ return f.rep.is_monic @property def is_primitive(f): """ Returns ``True`` if GCD of the coefficients of ``f`` is one. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(2*x**2 + 6*x + 12, x).is_primitive False >>> Poly(x**2 + 3*x + 6, x).is_primitive True """ return f.rep.is_primitive @property def is_ground(f): """ Returns ``True`` if ``f`` is an element of the ground domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x, x).is_ground False >>> Poly(2, x).is_ground True >>> Poly(y, x).is_ground True """ return f.rep.is_ground @property def is_linear(f): """ Returns ``True`` if ``f`` is linear in all its variables. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x + y + 2, x, y).is_linear True >>> Poly(x*y + 2, x, y).is_linear False """ return f.rep.is_linear @property def is_quadratic(f): """ Returns ``True`` if ``f`` is quadratic in all its variables. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x*y + 2, x, y).is_quadratic True >>> Poly(x*y**2 + 2, x, y).is_quadratic False """ return f.rep.is_quadratic @property def is_monomial(f): """ Returns ``True`` if ``f`` is zero or has only one term. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(3*x**2, x).is_monomial True >>> Poly(3*x**2 + 1, x).is_monomial False """ return f.rep.is_monomial @property def is_homogeneous(f): """ Returns ``True`` if ``f`` has zero trailing coefficient. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x*y + x + y, x, y).is_homogeneous True >>> Poly(x*y + x + y + 1, x, y).is_homogeneous False """ return f.rep.is_homogeneous @property def is_irreducible(f): """ Returns ``True`` if ``f`` has no factors over its domain. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> Poly(x**2 + x + 1, x, modulus=2).is_irreducible #doctest: +SKIP True >>> Poly(x**2 + 1, x, modulus=2).is_irreducible #doctest: +SKIP False """ return f.rep.is_irreducible @property def is_univariate(f): """ Returns ``True`` if ``f`` is a univariate polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + x + 1, x).is_univariate True >>> Poly(x*y**2 + x*y + 1, x, y).is_univariate False >>> Poly(x*y**2 + x*y + 1, x).is_univariate True >>> Poly(x**2 + x + 1, x, y).is_univariate False """ return len(f.gens) == 1 @property def is_multivariate(f): """ Returns ``True`` if ``f`` is a multivariate polynomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x, y >>> Poly(x**2 + x + 1, x).is_multivariate False >>> Poly(x*y**2 + x*y + 1, x, y).is_multivariate True >>> Poly(x*y**2 + x*y + 1, x).is_multivariate False >>> Poly(x**2 + x + 1, x, y).is_multivariate True """ return len(f.gens) != 1 @property def is_cyclotomic(f): """ Returns ``True`` if ``f`` is a cyclotomic polnomial. **Examples** >>> from sympy import Poly >>> from sympy.abc import x >>> f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 >>> Poly(f).is_cyclotomic False >>> g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 >>> Poly(g).is_cyclotomic True """ return f.rep.is_cyclotomic def __abs__(f): return f.abs() def __neg__(f): return f.neg() @_sympifyit('g', NotImplemented) def __add__(f, g): if not g.is_Poly: try: g = f.__class__(g, *f.gens) except PolynomialError: return f.as_expr() + g return f.add(g) @_sympifyit('g', NotImplemented) def __radd__(f, g): if not g.is_Poly: try: g = f.__class__(g, *f.gens) except PolynomialError: return g + f.as_expr() return g.add(f) @_sympifyit('g', NotImplemented) def __sub__(f, g): if not g.is_Poly: try: g = f.__class__(g, *f.gens) except PolynomialError: return f.as_expr() - g return f.sub(g) @_sympifyit('g', NotImplemented) def __rsub__(f, g): if not g.is_Poly: try: g = f.__class__(g, *f.gens) except PolynomialError: return g - f.as_expr() return g.sub(f) @_sympifyit('g', NotImplemented) def __mul__(f, g): if not g.is_Poly: try: g = f.__class__(g, *f.gens) except PolynomialError: return f.as_expr()*g return f.mul(g) @_sympifyit('g', NotImplemented) def __rmul__(f, g): if not g.is_Poly: try: g = f.__class__(g, *f.gens) except PolynomialError: return g*f.as_expr() return g.mul(f) @_sympifyit('n', NotImplemented) def __pow__(f, n): if n.is_Integer and n >= 0: return f.pow(n) else: return f.as_expr()**n @_sympifyit('g', NotImplemented) def __divmod__(f, g): if not g.is_Poly: g = f.__class__(g, *f.gens) return f.div(g) @_sympifyit('g', NotImplemented) def __rdivmod__(f, g): if not g.is_Poly: g = f.__class__(g, *f.gens) return g.div(f) @_sympifyit('g', NotImplemented) def __mod__(f, g): if not g.is_Poly: g = f.__class__(g, *f.gens) return f.rem(g) @_sympifyit('g', NotImplemented) def __rmod__(f, g): if not g.is_Poly: g = f.__class__(g, *f.gens) return g.rem(f) @_sympifyit('g', NotImplemented) def __floordiv__(f, g): if not g.is_Poly: g = f.__class__(g, *f.gens) return f.quo(g) @_sympifyit('g', NotImplemented) def __rfloordiv__(f, g): if not g.is_Poly: g = f.__class__(g, *f.gens) return g.quo(f) @_sympifyit('g', NotImplemented) def __div__(f, g): return f.as_expr()/g.as_expr() @_sympifyit('g', NotImplemented) def __rdiv__(f, g): return g.as_expr()/f.as_expr() __truediv__ = __div__ __rtruediv__ = __rdiv__ @_sympifyit('g', NotImplemented) def __eq__(f, g): if not g.is_Poly: try: g = f.__class__(g, f.gens, domain=f.get_domain()) except (PolynomialError, DomainError, CoercionFailed): return False if f.gens != g.gens: return False if f.rep.dom != g.rep.dom: try: dom = f.rep.dom.unify(g.rep.dom, f.gens) except UnificationFailed: return False f = f.set_domain(dom) g = g.set_domain(dom) return f.rep == g.rep @_sympifyit('g', NotImplemented) def __ne__(f, g): return not f.__eq__(g) def __nonzero__(f): return not f.is_zero class PurePoly(Poly): """Class for representing pure polynomials. """ def _hashable_content(self): """Allow SymPy to hash Poly instances. """ return (self.rep,) def __hash__(self): return super(PurePoly, self).__hash__() @property def free_symbols(self): """ Free symbols of a polynomial. **Examples** >>> from sympy import PurePoly >>> from sympy.abc import x, y >>> PurePoly(x**2 + 1).free_symbols set() >>> PurePoly(x**2 + y).free_symbols set() >>> PurePoly(x**2 + y, x).free_symbols set([y]) """ return self.free_symbols_in_domain @_sympifyit('g', NotImplemented) def __eq__(f, g): if not g.is_Poly: try: g = f.__class__(g, f.gens, domain=f.get_domain()) except (PolynomialError, DomainError, CoercionFailed): return False if len(f.gens) != len(g.gens): return False if f.rep.dom != g.rep.dom: try: dom = f.rep.dom.unify(g.rep.dom, f.gens) except UnificationFailed: return False f = f.set_domain(dom) g = g.set_domain(dom) return f.rep == g.rep def _unify(f, g): g = sympify(g) if not g.is_Poly: try: return f.rep.dom, f.per, f.rep, f.rep.per(f.rep.dom.from_sympy(g)) except CoercionFailed: raise UnificationFailed("can't unify %s with %s" % (f, g)) if len(f.gens) != len(g.gens): raise UnificationFailed("can't unify %s with %s" % (f, g)) if not (isinstance(f.rep, DMP) and isinstance(g.rep, DMP)): raise UnificationFailed("can't unify %s with %s" % (f, g)) cls = f.__class__ gens = f.gens lev = len(gens)-1 dom = f.rep.dom.unify(g.rep.dom, gens) F = f.rep.convert(dom) G = g.rep.convert(dom) def per(rep, dom=dom, gens=gens, remove=None): if remove is not None: gens = gens[:remove]+gens[remove+1:] if not gens: return dom.to_sympy(rep) return cls.new(rep, *gens) return dom, per, F, G def poly_from_expr(expr, *gens, **args): """Construct a polynomial from an expression. """ opt = options.build_options(gens, args) return _poly_from_expr(expr, opt) def _poly_from_expr(expr, opt): """Construct a polynomial from an expression. """ orig, expr = expr, sympify(expr) if not isinstance(expr, Basic): raise PolificationFailed(opt, orig, expr) elif expr.is_Poly: poly = expr.__class__._from_poly(expr, opt) opt['gens'] = poly.gens opt['domain'] = poly.domain if opt.polys is None: opt['polys'] = True return poly, opt elif opt.expand: expr = expr.expand() try: rep, opt = _dict_from_expr(expr, opt) except GeneratorsNeeded: raise PolificationFailed(opt, orig, expr) monoms, coeffs = zip(*rep.items()) domain = opt.domain if domain is None: domain, coeffs = construct_domain(coeffs, opt=opt) else: coeffs = map(domain.from_sympy, coeffs) level = len(opt.gens)-1 poly = Poly.new(DMP.from_monoms_coeffs(monoms, coeffs, level, domain), *opt.gens) opt['domain'] = domain if opt.polys is None: opt['polys'] = False return poly, opt def parallel_poly_from_expr(exprs, *gens, **args): """Construct polynomials from expressions. """ opt = options.build_options(gens, args) return _parallel_poly_from_expr(exprs, opt) def _parallel_poly_from_expr(exprs, opt): """Construct polynomials from expressions. """ if len(exprs) == 2: f, g = exprs if isinstance(f, Poly) and isinstance(g, Poly): f = f.__class__._from_poly(f, opt) g = g.__class__._from_poly(g, opt) f, g = f.unify(g) opt['gens'] = f.gens opt['domain'] = f.domain if opt.polys is None: opt['polys'] = True return [f, g], opt origs, exprs = list(exprs), [] _exprs, _polys = [], [] failed = False for i, expr in enumerate(origs): expr = sympify(expr) if isinstance(expr, Basic): if expr.is_Poly: _polys.append(i) else: _exprs.append(i) if opt.expand: expr = expr.expand() else: failed = True exprs.append(expr) if failed: raise PolificationFailed(opt, origs, exprs, True) if _polys: # XXX: this is a temporary solution for i in _polys: exprs[i] = exprs[i].as_expr() try: reps, opt = _parallel_dict_from_expr(exprs, opt) except GeneratorsNeeded: raise PolificationFailed(opt, origs, exprs, True) coeffs_list, lengths = [], [] all_monoms = [] all_coeffs = [] for rep in reps: monoms, coeffs = zip(*rep.items()) coeffs_list.extend(coeffs) all_monoms.append(monoms) lengths.append(len(coeffs)) domain = opt.domain if domain is None: domain, coeffs_list = construct_domain(coeffs_list, opt=opt) else: coeffs_list = map(domain.from_sympy, coeffs_list) for k in lengths: all_coeffs.append(coeffs_list[:k]) coeffs_list = coeffs_list[k:] polys, level = [], len(opt.gens)-1 for monoms, coeffs in zip(all_monoms, all_coeffs): rep = DMP.from_monoms_coeffs(monoms, coeffs, level, domain) polys.append(Poly.new(rep, *opt.gens)) opt['domain'] = domain if opt.polys is None: opt['polys'] = bool(_polys) return polys, opt def _update_args(args, key, value): """Add a new ``(key, value)`` pair to arguments ``dict``. """ args = dict(args) if key not in args: args[key] = value return args def _keep_coeff(coeff, factors): """Return ``coeff*factors`` unevaluated if necessary. """ if coeff == 1: return factors elif coeff == -1: return -factors elif not factors.is_Add: return coeff*factors else: return Mul(coeff, factors, evaluate=False) def degree(f, *gens, **args): """ Return the degree of ``f`` in the given variable. **Examples** >>> from sympy import degree >>> from sympy.abc import x, y >>> degree(x**2 + y*x + 1, gen=x) 2 >>> degree(x**2 + y*x + 1, gen=y) 1 """ options.allowed_flags(args, ['gen', 'polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('degree', 1, exc) return Integer(F.degree(opt.gen)) def degree_list(f, *gens, **args): """ Return a list of degrees of ``f`` in all variables. **Examples** >>> from sympy import degree_list >>> from sympy.abc import x, y >>> degree_list(x**2 + y*x + 1) (2, 1) """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('degree_list', 1, exc) degrees = F.degree_list() return tuple(map(Integer, degrees)) def LC(f, *gens, **args): """ Return the leading coefficient of ``f``. **Examples** >>> from sympy import LC >>> from sympy.abc import x, y >>> LC(4*x**2 + 2*x*y**2 + x*y + 3*y) 4 """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('LC', 1, exc) return F.LC(order=opt.order) def LM(f, *gens, **args): """ Return the leading monomial of ``f``. **Examples** >>> from sympy import LM >>> from sympy.abc import x, y >>> LM(4*x**2 + 2*x*y**2 + x*y + 3*y) x**2 """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('LM', 1, exc) monom = Monomial(*F.LM(order=opt.order)) return monom.as_expr(*opt.gens) def LT(f, *gens, **args): """ Return the leading term of ``f``. **Examples** >>> from sympy import LT >>> from sympy.abc import x, y >>> LT(4*x**2 + 2*x*y**2 + x*y + 3*y) 4*x**2 """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('LT', 1, exc) monom, coeff = F.LT(order=opt.order) return coeff*Monomial(*monom).as_expr(*opt.gens) def pdiv(f, g, *gens, **args): """ Compute polynomial pseudo-division of ``f`` and ``g``. **Examples** >>> from sympy import pdiv >>> from sympy.abc import x >>> pdiv(x**2 + 1, 2*x - 4) (2*x + 4, 20) """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('pdiv', 2, exc) q, r = F.pdiv(G) if not opt.polys: return q.as_expr(), r.as_expr() else: return q, r def prem(f, g, *gens, **args): """ Compute polynomial pseudo-remainder of ``f`` and ``g``. **Examples** >>> from sympy import prem >>> from sympy.abc import x >>> prem(x**2 + 1, 2*x - 4) 20 """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('prem', 2, exc) r = F.prem(G) if not opt.polys: return r.as_expr() else: return r def pquo(f, g, *gens, **args): """ Compute polynomial pseudo-quotient of ``f`` and ``g``. **Examples** >>> from sympy import pquo >>> from sympy.abc import x >>> pquo(x**2 + 1, 2*x - 4) 2*x + 4 >>> pquo(x**2 - 1, 2*x - 1) 2*x + 1 """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('pquo', 2, exc) q = F.pquo(G) if not opt.polys: return q.as_expr() else: return q def pexquo(f, g, *gens, **args): """ Compute polynomial exact pseudo-quotient of ``f`` and ``g``. **Examples** >>> from sympy import pexquo >>> from sympy.abc import x >>> pexquo(x**2 - 1, 2*x - 2) 2*x + 2 >>> pexquo(x**2 + 1, 2*x - 4) Traceback (most recent call last): ... ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('pexquo', 2, exc) q = F.pexquo(G) if not opt.polys: return q.as_expr() else: return q def div(f, g, *gens, **args): """ Compute polynomial division of ``f`` and ``g``. **Examples** >>> from sympy import div, ZZ, QQ >>> from sympy.abc import x >>> div(x**2 + 1, 2*x - 4, domain=ZZ) (0, x**2 + 1) >>> div(x**2 + 1, 2*x - 4, domain=QQ) (x/2 + 1, 5) """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('div', 2, exc) q, r = F.div(G, auto=opt.auto) if not opt.polys: return q.as_expr(), r.as_expr() else: return q, r def rem(f, g, *gens, **args): """ Compute polynomial remainder of ``f`` and ``g``. **Examples** >>> from sympy import rem, ZZ, QQ >>> from sympy.abc import x >>> rem(x**2 + 1, 2*x - 4, domain=ZZ) x**2 + 1 >>> rem(x**2 + 1, 2*x - 4, domain=QQ) 5 """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('rem', 2, exc) r = F.rem(G, auto=opt.auto) if not opt.polys: return r.as_expr() else: return r def quo(f, g, *gens, **args): """ Compute polynomial quotient of ``f`` and ``g``. **Examples** >>> from sympy import quo >>> from sympy.abc import x >>> quo(x**2 + 1, 2*x - 4) x/2 + 1 >>> quo(x**2 - 1, x - 1) x + 1 """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('quo', 2, exc) q = F.quo(G, auto=opt.auto) if not opt.polys: return q.as_expr() else: return q def exquo(f, g, *gens, **args): """ Compute polynomial exact quotient of ``f`` and ``g``. **Examples** >>> from sympy import exquo >>> from sympy.abc import x >>> exquo(x**2 - 1, x - 1) x + 1 >>> exquo(x**2 + 1, 2*x - 4) Traceback (most recent call last): ... ExactQuotientFailed: 2*x - 4 does not divide x**2 + 1 """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('exquo', 2, exc) q = F.exquo(G, auto=opt.auto) if not opt.polys: return q.as_expr() else: return q def half_gcdex(f, g, *gens, **args): """ Half extended Euclidean algorithm of ``f`` and ``g``. Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. **Examples** >>> from sympy import half_gcdex >>> from sympy.abc import x >>> half_gcdex(x**4 - 2*x**3 - 6*x**2 + 12*x + 15, x**3 + x**2 - 4*x - 4) (-x/5 + 3/5, x + 1) """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: f, g = exc.exprs if hasattr(f, 'half_gcdex'): try: return f.half_gcdex(g) except (SympifyError, ValueError): pass raise ComputationFailed('half_gcdex', 2, exc) s, h = F.half_gcdex(G, auto=opt.auto) if not opt.polys: return s.as_expr(), h.as_expr() else: return s, h def gcdex(f, g, *gens, **args): """ Extended Euclidean algorithm of ``f`` and ``g``. Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. **Examples** >>> from sympy import gcdex >>> from sympy.abc import x >>> gcdex(x**4 - 2*x**3 - 6*x**2 + 12*x + 15, x**3 + x**2 - 4*x - 4) (-x/5 + 3/5, x**2/5 - 6*x/5 + 2, x + 1) """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: f, g = exc.exprs if hasattr(f, 'gcdex'): try: return f.gcdex(g) except (SympifyError, ValueError): pass raise ComputationFailed('gcdex', 2, exc) s, t, h = F.gcdex(G, auto=opt.auto) if not opt.polys: return s.as_expr(), t.as_expr(), h.as_expr() else: return s, t, h def invert(f, g, *gens, **args): """ Invert ``f`` modulo ``g`` when possible. **Examples** >>> from sympy import invert >>> from sympy.abc import x >>> invert(x**2 - 1, 2*x - 1) -4/3 >>> invert(x**2 - 1, x - 1) Traceback (most recent call last): ... NotInvertible: zero divisor """ options.allowed_flags(args, ['auto', 'polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: f, g = exc.exprs if hasattr(f, 'invert'): try: return f.invert(g) except (SympifyError, ValueError): pass raise ComputationFailed('invert', 2, exc) h = F.invert(G, auto=opt.auto) if not opt.polys: return h.as_expr() else: return h def subresultants(f, g, *gens, **args): """ Compute subresultant PRS of ``f`` and ``g``. **Examples** >>> from sympy import subresultants >>> from sympy.abc import x >>> subresultants(x**2 + 1, x**2 - 1) [x**2 + 1, x**2 - 1, -2] """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('subresultants', 2, exc) result = F.subresultants(G) if not opt.polys: return [ r.as_expr() for r in result ] else: return result def resultant(f, g, *gens, **args): """ Compute resultant of ``f`` and ``g``. **Examples** >>> from sympy import resultant >>> from sympy.abc import x >>> resultant(x**2 + 1, x**2 - 1) 4 """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('resultant', 2, exc) result = F.resultant(G) if not opt.polys: return result.as_expr() else: return result def discriminant(f, *gens, **args): """ Compute discriminant of ``f``. **Examples** >>> from sympy import discriminant >>> from sympy.abc import x >>> discriminant(x**2 + 2*x + 3) -8 """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('discriminant', 1, exc) result = F.discriminant() if not opt.polys: return result.as_expr() else: return result def cofactors(f, g, *gens, **args): """ Compute GCD and cofactors of ``f`` and ``g``. Returns polynomials ``(h, cff, cfg)`` such that ``h = gcd(f, g)``, and ``cff = quo(f, h)`` and ``cfg = quo(g, h)`` are, so called, cofactors of ``f`` and ``g``. **Examples** >>> from sympy import cofactors >>> from sympy.abc import x >>> cofactors(x**2 - 1, x**2 - 3*x + 2) (x - 1, x + 1, x - 2) """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: f, g = exc.exprs if hasattr(f, 'cofactors'): try: return f.cofactors(g) except (SympifyError, ValueError): pass raise ComputationFailed('cofactors', 2, exc) h, cff, cfg = F.cofactors(G) if not opt.polys: return h.as_expr(), cff.as_expr(), cfg.as_expr() else: return h, cff, cfg def gcd_list(seq, *gens, **args): """ Compute GCD of a list of polynomials. **Examples** >>> from sympy import gcd_list >>> from sympy.abc import x >>> gcd_list([x**3 - 1, x**2 - 1, x**2 - 3*x + 2]) x - 1 """ if not gens and not args: if not seq: return S.Zero seq = sympify(seq) if all(s.is_Number for s in seq): result, numbers = seq[0], seq[1:] for number in numbers: result = result.gcd(number) if result is S.One: break return result options.allowed_flags(args, ['polys']) try: polys, opt = parallel_poly_from_expr(seq, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('gcd_list', len(seq), exc) if not polys: if not opt.polys: return S.Zero else: return Poly(0, opt=opt) result, polys = polys[0], polys[1:] for poly in polys: result = result.gcd(poly) if result.is_one: break if not opt.polys: return result.as_expr() else: return result def gcd(f, g=None, *gens, **args): """ Compute GCD of ``f`` and ``g``. **Examples** >>> from sympy import gcd >>> from sympy.abc import x >>> gcd(x**2 - 1, x**2 - 3*x + 2) x - 1 """ if hasattr(f, '__iter__'): if g is not None: gens = (g,) + gens return gcd_list(f, *gens, **args) options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: f, g = exc.exprs if hasattr(f, 'gcd'): try: return f.gcd(g) except (SympifyError, ValueError): pass raise ComputationFailed('gcd', 2, exc) result = F.gcd(G) if not opt.polys: return result.as_expr() else: return result def lcm_list(seq, *gens, **args): """ Compute LCM of a list of polynomials. **Examples** >>> from sympy import lcm_list >>> from sympy.abc import x >>> lcm_list([x**3 - 1, x**2 - 1, x**2 - 3*x + 2]) x**5 - x**4 - 2*x**3 - x**2 + x + 2 """ if not gens and not args: if not seq: return S.One seq = sympify(seq) if all(s.is_Number for s in seq): result, numbers = seq[0], seq[1:] for number in numbers: result = result.lcm(number) return result options.allowed_flags(args, ['polys']) try: polys, opt = parallel_poly_from_expr(seq, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('lcm_list', len(seq), exc) if not polys: if not opt.polys: return S.One else: return Poly(1, opt=opt) result, polys = polys[0], polys[1:] for poly in polys: result = result.lcm(poly) if not opt.polys: return result.as_expr() else: return result def lcm(f, g=None, *gens, **args): """ Compute LCM of ``f`` and ``g``. **Examples** >>> from sympy import lcm >>> from sympy.abc import x >>> lcm(x**2 - 1, x**2 - 3*x + 2) x**3 - 2*x**2 - x + 2 """ if hasattr(f, '__iter__'): if g is not None: gens = (g,) + gens return lcm_list(f, *gens, **args) options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: f, g = exc.exprs if hasattr(f, 'lcm'): try: return f.lcm(g) except (SympifyError, ValueError): pass raise ComputationFailed('lcm', 2, exc) result = F.lcm(G) if not opt.polys: return result.as_expr() else: return result def terms_gcd(f, *gens, **args): """ Remove GCD of terms from ``f``. **Examples** >>> from sympy import terms_gcd >>> from sympy.abc import x, y >>> terms_gcd(x**6*y**2 + x**3*y, x, y) x**3*y*(x**3*y + 1) """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: return exc.expr J, f = F.terms_gcd() if opt.domain.has_Ring: if opt.domain.has_Field: denom, f = f.clear_denoms(convert=True) coeff, f = f.primitive() if opt.domain.has_Field: coeff /= denom else: coeff = S.One term = Mul(*[ x**j for x, j in zip(f.gens, J) ]) return _keep_coeff(coeff, term*f.as_expr()) def trunc(f, p, *gens, **args): """ Reduce ``f`` modulo a constant ``p``. **Examples** >>> from sympy import trunc >>> from sympy.abc import x >>> trunc(2*x**3 + 3*x**2 + 5*x + 7, 3) -x**3 - x + 1 """ options.allowed_flags(args, ['auto', 'polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('trunc', 1, exc) result = F.trunc(sympify(p)) if not opt.polys: return result.as_expr() else: return result def monic(f, *gens, **args): """ Divide all coefficients of ``f`` by ``LC(f)``. **Examples** >>> from sympy import monic >>> from sympy.abc import x >>> monic(3*x**2 + 4*x + 2) x**2 + 4*x/3 + 2/3 """ options.allowed_flags(args, ['auto', 'polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('monic', 1, exc) result = F.monic(auto=opt.auto) if not opt.polys: return result.as_expr() else: return result def content(f, *gens, **args): """ Compute GCD of coefficients of ``f``. **Examples** >>> from sympy import content >>> from sympy.abc import x >>> content(6*x**2 + 8*x + 12) 2 """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('content', 1, exc) return F.content() def primitive(f, *gens, **args): """ Compute content and the primitive form of ``f``. **Examples** >>> from sympy import primitive >>> from sympy.abc import x >>> primitive(6*x**2 + 8*x + 12) (2, 3*x**2 + 4*x + 6) """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('primitive', 1, exc) cont, result = F.primitive() if not opt.polys: return cont, result.as_expr() else: return cont, result def compose(f, g, *gens, **args): """ Compute functional composition ``f(g)``. **Examples** >>> from sympy import compose >>> from sympy.abc import x >>> compose(x**2 + x, x - 1) x**2 - x """ options.allowed_flags(args, ['polys']) try: (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('compose', 2, exc) result = F.compose(G) if not opt.polys: return result.as_expr() else: return result def decompose(f, *gens, **args): """ Compute functional decomposition of ``f``. **Examples** >>> from sympy import decompose >>> from sympy.abc import x >>> decompose(x**4 + 2*x**3 - x - 1) [x**2 - x - 1, x**2 + x] """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('decompose', 1, exc) result = F.decompose() if not opt.polys: return [ r.as_expr() for r in result ] else: return result def sturm(f, *gens, **args): """ Compute Sturm sequence of ``f``. **Examples** >>> from sympy import sturm >>> from sympy.abc import x >>> sturm(x**3 - 2*x**2 + x - 3) [x**3 - 2*x**2 + x - 3, 3*x**2 - 4*x + 1, 2*x/9 + 25/9, -2079/4] """ options.allowed_flags(args, ['auto', 'polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('sturm', 1, exc) result = F.sturm(auto=opt.auto) if not opt.polys: return [ r.as_expr() for r in result ] else: return result def gff_list(f, *gens, **args): """ Compute a list of greatest factorial factors of ``f``. **Examples** >>> from sympy import gff_list, ff >>> from sympy.abc import x >>> f = x**5 + 2*x**4 - x**3 - 2*x**2 >>> gff_list(f) [(x, 1), (x + 2, 4)] >>> (ff(x, 1)*ff(x + 2, 4)).expand() == f True """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('gff_list', 1, exc) factors = F.gff_list() if not opt.polys: return [ (g.as_expr(), k) for g, k in factors ] else: return factors def gff(f, *gens, **args): """Compute greatest factorial factorization of ``f``. """ raise NotImplementedError('symbolic falling factorial') def sqf_norm(f, *gens, **args): """ Compute square-free norm of ``f``. Returns ``s``, ``f``, ``r``, such that ``g(x) = f(x-sa)`` and ``r(x) = Norm(g(x))`` is a square-free polynomial over ``K``, where ``a`` is the algebraic extension of the ground domain. **Examples** >>> from sympy import sqf_norm, sqrt >>> from sympy.abc import x >>> sqf_norm(x**2 + 1, extension=[sqrt(3)]) (1, x**2 - 2*3**(1/2)*x + 4, x**4 - 4*x**2 + 16) """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('sqf_norm', 1, exc) s, g, r = F.sqf_norm() if not opt.polys: return Integer(s), g.as_expr(), r.as_expr() else: return Integer(s), g, r def sqf_part(f, *gens, **args): """ Compute square-free part of ``f``. **Examples** >>> from sympy import sqf_part >>> from sympy.abc import x >>> sqf_part(x**3 - 3*x - 2) x**2 - x - 2 """ options.allowed_flags(args, ['polys']) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('sqf_part', 1, exc) result = F.sqf_part() if not opt.polys: return result.as_expr() else: return result def _sorted_factors(factors, method): """Sort a list of ``(expr, exp)`` pairs. """ if method == 'sqf': def key(obj): poly, exp = obj rep = poly.rep.rep return (exp, len(rep), rep) else: def key(obj): poly, exp = obj rep = poly.rep.rep return (len(rep), exp, rep) return sorted(factors, key=key) def _factors_product(factors): """Multiply a list of ``(expr, exp)`` pairs. """ return Mul(*[ f.as_expr()**k for f, k in factors ]) def _symbolic_factor_list(expr, opt, method): """Helper function for :func:`_symbolic_factor`. """ coeff, factors = S.One, [] for arg in Mul.make_args(expr): if arg.is_Pow: base, exp = arg.args else: base, exp = arg, S.One if base.is_Number: coeff *= arg else: try: poly, _ = _poly_from_expr(base, opt) except PolificationFailed, exc: coeff *= exc.expr**exp else: func = getattr(poly, method + '_list') _coeff, _factors = func() coeff *= _coeff**exp if exp is S.One: factors.extend(_factors) else: for factor, k in _factors: factors.append((factor, k*exp)) return coeff, factors def _symbolic_factor(expr, opt, method): """Helper function for :func:`_factor`. """ if isinstance(expr, Expr) and not expr.is_Relational: coeff, factors = _symbolic_factor_list(together(expr), opt, method) return _keep_coeff(coeff, _factors_product(factors)) elif hasattr(expr, 'args'): return expr.func(*[ _symbolic_factor(arg, opt, method) for arg in expr.args ]) elif hasattr(expr, '__iter__'): return expr.__class__([ _symbolic_factor(arg, opt, method) for arg in expr ]) else: return expr def _generic_factor_list(expr, gens, args, method): """Helper function for :func:`sqf_list` and :func:`factor_list`. """ options.allowed_flags(args, ['frac', 'polys']) opt = options.build_options(gens, args) expr = sympify(expr) if isinstance(expr, Expr) and not expr.is_Relational: numer, denom = together(expr).as_numer_denom() cp, fp = _symbolic_factor_list(numer, opt, method) cq, fq = _symbolic_factor_list(denom, opt, method) if fq and not opt.frac: raise PolynomialError("a polynomial expected, got %s" % expr) fp = _sorted_factors(fp, method) fq = _sorted_factors(fq, method) if not opt.polys: fp = [ (f.as_expr(), k) for f, k in fp ] fq = [ (f.as_expr(), k) for f, k in fq ] coeff = cp/cq if not opt.frac: return coeff, fp else: return coeff, fp, fq else: raise PolynomialError("a polynomial expected, got %s" % expr) def _generic_factor(expr, gens, args, method): """Helper function for :func:`sqf` and :func:`factor`. """ options.allowed_flags(args, []) opt = options.build_options(gens, args) return _symbolic_factor(sympify(expr), opt, method) def sqf_list(f, *gens, **args): """ Compute a list of square-free factors of ``f``. **Examples** >>> from sympy import sqf_list >>> from sympy.abc import x >>> sqf_list(2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16) (2, [(x + 1, 2), (x + 2, 3)]) """ return _generic_factor_list(f, gens, args, method='sqf') def sqf(f, *gens, **args): """ Compute square-free factorization of ``f``. **Examples** >>> from sympy import sqf >>> from sympy.abc import x >>> sqf(2*x**5 + 16*x**4 + 50*x**3 + 76*x**2 + 56*x + 16) 2*(x + 1)**2*(x + 2)**3 """ return _generic_factor(f, gens, args, method='sqf') def factor_list(f, *gens, **args): """ Compute a list of irreducible factors of ``f``. **Examples** >>> from sympy import factor_list >>> from sympy.abc import x, y >>> factor_list(2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y) (2, [(x + y, 1), (x**2 + 1, 2)]) """ return _generic_factor_list(f, gens, args, method='factor') def factor(f, *gens, **args): """ Compute the factorization of ``f`` into irreducibles. (Use factorint to factor an integer.) There two modes implemented: symbolic and formal. If ``f`` is not an instance of :class:`Poly` and generators are not specified, then the former mode is used. Otherwise, the formal mode is used. In symbolic mode, :func:`factor` will traverse the expression tree and factor its components without any prior expansion, unless an instance of :class:`Add` is encountered (in this case formal factorization is used). This way :func:`factor` can handle large or symbolic exponents. By default, the factorization is computed over the rationals. To factor over other domain, e.g. an algebraic or finite field, use appropriate options: ``extension``, ``modulus`` or ``domain``. **Examples** >>> from sympy import factor, sqrt >>> from sympy.abc import x, y >>> factor(2*x**5 + 2*x**4*y + 4*x**3 + 4*x**2*y + 2*x + 2*y) 2*(x + y)*(x**2 + 1)**2 >>> factor(x**2 + 1) x**2 + 1 >>> factor(x**2 + 1, modulus=2) (x + 1)**2 >>> factor(x**2 + 1, gaussian=True) (x - I)*(x + I) >>> factor(x**2 - 2, extension=sqrt(2)) (x - 2**(1/2))*(x + 2**(1/2)) >>> factor((x**2 - 1)/(x**2 + 4*x + 4)) (x - 1)*(x + 1)/(x + 2)**2 >>> factor((x**2 + 4*x + 4)**10000000*(x**2 + 1)) (x + 2)**20000000*(x**2 + 1) """ return _generic_factor(f, gens, args, method='factor') def intervals(F, all=False, eps=None, inf=None, sup=None, strict=False, fast=False, sqf=False): """ Compute isolating intervals for roots of ``f``. **Examples** >>> from sympy import intervals >>> from sympy.abc import x >>> intervals(x**2 - 3) [((-2, -1), 1), ((1, 2), 1)] >>> intervals(x**2 - 3, eps=1e-2) [((-26/15, -19/11), 1), ((19/11, 26/15), 1)] """ if not hasattr(F, '__iter__'): try: F = Poly(F) except GeneratorsNeeded: return [] return F.intervals(all=all, eps=eps, inf=inf, sup=sup, fast=fast, sqf=sqf) else: polys, opt = parallel_poly_from_expr(F, domain='QQ') if len(opt.gens) > 1: raise MultivariatePolynomialError for i, poly in enumerate(polys): polys[i] = poly.rep.rep if eps is not None: eps = opt.domain.convert(eps) if eps <= 0: raise ValueError("'eps' must be a positive rational") if inf is not None: inf = opt.domain.convert(inf) if sup is not None: sup = opt.domain.convert(sup) intervals = dup_isolate_real_roots_list(polys, opt.domain, eps=eps, inf=inf, sup=sup, strict=strict, fast=fast) result = [] for (s, t), indices in intervals: s, t = opt.domain.to_sympy(s), opt.domain.to_sympy(t) result.append(((s, t), indices)) return result def refine_root(f, s, t, eps=None, steps=None, fast=False, check_sqf=False): """ Refine an isolating interval of a root to the given precision. **Examples** >>> from sympy import refine_root >>> from sympy.abc import x >>> refine_root(x**2 - 3, 1, 2, eps=1e-2) (19/11, 26/15) """ try: F = Poly(f) except GeneratorsNeeded: raise PolynomialError("can't refine a root of %s, not a polynomial" % f) return F.refine_root(s, t, eps=eps, steps=steps, fast=fast, check_sqf=check_sqf) def count_roots(f, inf=None, sup=None): """ Return the number of roots of ``f`` in ``[inf, sup]`` interval. If one of ``inf`` or ``sup`` is complex, it will return the number of roots in the complex rectangle with corners at ``inf`` and ``sup``. **Examples** >>> from sympy import count_roots, I >>> from sympy.abc import x >>> count_roots(x**4 - 4, -3, 3) 2 >>> count_roots(x**4 - 4, 0, 1 + 3*I) 1 """ try: F = Poly(f, greedy=False) except GeneratorsNeeded: raise PolynomialError("can't count roots of %s, not a polynomial" % f) return F.count_roots(inf=inf, sup=sup) def real_roots(f, multiple=True): """ Return a list of real roots with multiplicities of ``f``. **Examples** >>> from sympy import real_roots >>> from sympy.abc import x >>> real_roots(2*x**3 - 7*x**2 + 4*x + 4) [-1/2, 2, 2] """ try: F = Poly(f, greedy=False) except GeneratorsNeeded: raise PolynomialError("can't compute real roots of %s, not a polynomial" % f) return F.real_roots(multiple=multiple) def nroots(f, n=15, maxsteps=50, cleanup=True, error=False): """ Compute numerical approximations of roots of ``f``. **Examples** >>> from sympy import nroots >>> from sympy.abc import x >>> nroots(x**2 - 3, n=15) [-1.73205080756888, 1.73205080756888] >>> nroots(x**2 - 3, n=30) [-1.73205080756887729352744634151, 1.73205080756887729352744634151] """ try: F = Poly(f, greedy=False) except GeneratorsNeeded: raise PolynomialError("can't compute numerical roots of %s, not a polynomial" % f) return F.nroots(n=n, maxsteps=maxsteps, cleanup=cleanup, error=error) def ground_roots(f, *gens, **args): """ Compute roots of ``f`` by factorization in the ground domain. **Examples** >>> from sympy import ground_roots >>> from sympy.abc import x >>> ground_roots(x**6 - 4*x**4 + 4*x**3 - x**2) {0: 2, 1: 2} """ options.allowed_flags(args, []) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('ground_roots', 1, exc) return F.ground_roots() def nth_power_roots_poly(f, n, *gens, **args): """ Construct a polynomial with n-th powers of roots of ``f``. **Examples** >>> from sympy import nth_power_roots_poly, factor, roots >>> from sympy.abc import x >>> f = x**4 - x**2 + 1 >>> g = factor(nth_power_roots_poly(f, 2)) >>> g (x**2 - x + 1)**2 >>> R_f = [ (r**2).expand() for r in roots(f) ] >>> R_g = roots(g).keys() >>> set(R_f) == set(R_g) True """ options.allowed_flags(args, []) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('nth_power_roots_poly', 1, exc) result = F.nth_power_roots_poly(n) if not opt.polys: return result.as_expr() else: return result def cancel(f, *gens, **args): """ Cancel common factors in a rational function ``f``. **Examples** >>> from sympy import cancel >>> from sympy.abc import x >>> cancel((2*x**2 - 2)/(x**2 - 2*x + 1)) (2*x + 2)/(x - 1) """ options.allowed_flags(args, ['polys']) f = sympify(f) if not isinstance(f, (tuple, Tuple)): if f.is_Number: return f else: p, q = f.as_numer_denom() else: p, q = f try: (F, G), opt = parallel_poly_from_expr((p, q), *gens, **args) except PolificationFailed, exc: if not isinstance(f, (tuple, Tuple)): return f else: return S.One, p, q c, P, Q = F.cancel(G) if not isinstance(f, (tuple, Tuple)): return c*(P.as_expr()/Q.as_expr()) else: if not opt.polys: return c, P.as_expr(), Q.as_expr() else: return c, P, Q def reduced(f, G, *gens, **args): """ Reduces a polynomial ``f`` modulo a set of polynomials ``G``. Given a polynomial ``f`` and a set of polynomials ``G = (g_1, ..., g_n)``, computes a set of quotients ``q = (q_1, ..., q_n)`` and the remainder ``r`` such that ``f = q_1*f_1 + ... + q_n*f_n + r``, where ``r`` vanishes or ``r`` is a completely reduced polynomial with respect to ``G``. **Examples** >>> from sympy import reduced >>> from sympy.abc import x, y >>> reduced(2*x**4 + y**2 - x**2 + y**3, [x**3 - x, y**3 - y]) ([2*x, 1], x**2 + y**2 + y) """ options.allowed_flags(args, ['polys']) try: polys, opt = parallel_poly_from_expr([f] + list(G), *gens, **args) except PolificationFailed, exc: raise ComputationFailed('reduced', 0, exc) for i, poly in enumerate(polys): polys[i] = sdp_from_dict(poly.rep.to_dict(), opt.order) level = len(opt.gens)-1 Q, r = sdp_div(polys[0], polys[1:], level, opt.order, opt.domain) Q = [ Poly.new(DMP(dict(q), opt.domain, level), *opt.gens) for q in Q ] r = Poly.new(DMP(dict(r), opt.domain, level), *opt.gens) if not opt.polys: return [ q.as_expr() for q in Q ], r.as_expr() else: return Q, r def groebner(F, *gens, **args): """ Computes the reduced Groebner basis for a set of polynomials. Use the ``order`` argument to set the monomial ordering that will be used to compute the basis. Allowed orders are ``lex``, ``grlex`` and ``grevlex``. If no order is specified, it defaults to ``lex``. **Examples** >>> from sympy import groebner >>> from sympy.abc import x, y >>> groebner([x*y - 2*y, 2*y**2 - x**2], order='lex') [x**2 - 2*y**2, x*y - 2*y, y**3 - 2*y] >>> groebner([x*y - 2*y, 2*y**2 - x**2], order='grlex') [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] >>> groebner([x*y - 2*y, 2*y**2 - x**2], order='grevlex') [x**3 - 2*x**2, -x**2 + 2*y**2, x*y - 2*y] **References** 1. [Buchberger01]_ 2. [Cox97]_ """ options.allowed_flags(args, ['polys']) try: polys, opt = parallel_poly_from_expr(F, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('groebner', len(F), exc) domain = opt.domain if domain.has_assoc_Field: opt.domain = domain.get_field() else: raise DomainError("can't compute a Groebner basis over %s" % domain) for i, poly in enumerate(polys): poly = poly.set_domain(opt.domain).rep.to_dict() polys[i] = sdp_from_dict(poly, opt.order) level = len(gens)-1 G = sdp_groebner(polys, level, opt.order, opt.domain) G = [ Poly._from_dict(dict(g), opt) for g in G ] if not domain.has_Field: G = [ g.clear_denoms(convert=True)[1] for g in G ] if not opt.polys: return [ g.as_expr() for g in G ] else: return G def poly(expr, *gens, **args): """ Efficiently transform an expression into a polynomial. **Examples** >>> from sympy import poly >>> from sympy.abc import x >>> poly(x*(x**2 + x - 1)**2) Poly(x**5 + 2*x**4 - x**3 - 2*x**2 + x, x, domain='ZZ') """ options.allowed_flags(args, []) def _poly(expr, opt): terms, poly_terms = [], [] for term in Add.make_args(expr): factors, poly_factors = [], [] for factor in Mul.make_args(term): if factor.is_Add: poly_factors.append(_poly(factor, opt)) elif factor.is_Pow and factor.base.is_Add and factor.exp.is_Integer: poly_factors.append(_poly(factor.base, opt).pow(factor.exp)) else: factors.append(factor) if not poly_factors: terms.append(term) else: product = poly_factors[0] for factor in poly_factors[1:]: product = product.mul(factor) if factors: factor = Mul(*factors) if factor.is_Number: product = product.mul(factor) else: product = product.mul(Poly._from_expr(factor, opt)) poly_terms.append(product) if not poly_terms: result = Poly._from_expr(expr, opt) else: result = poly_terms[0] for term in poly_terms[1:]: result = result.add(term) if terms: term = Add(*terms) if term.is_Number: result = result.add(term) else: result = result.add(Poly._from_expr(term, opt)) return result.reorder(**args) expr = sympify(expr) if expr.is_Poly: return Poly(expr, *gens, **args) if 'expand' not in args: args['expand'] = False opt = options.build_options(gens, args) return _poly(expr, opt) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/sparsepolys.py0000644000175000017500000005037712014170666025217 0ustar georgeskgeorgesk"""Object-oriented interface to sparse polynomial representation. """ from sympy.polys.polyclasses import GenericPoly class SparsePoly(GenericPoly): """Sparse polynomial over an arbitrary domain. """ __slots__ = ['rep', 'lev', 'ord', 'dom', '_hash'] def __init__(self, rep, ord, dom, lev=None): if lev is None: rep, lev = smp_validate(rep) self.rep = rep self.lev = lev self.ord = ord self.dom = dom self._hash = None def __repr__(self): return "%s(%s, %s, %s)" % (self.__class__.__name__, self.rep, self.ord, self.dom) def __hash__(self): _hash = self._hash if _hash is None: self._hash = _hash = hash((self.__class__.__name__, repr(self.rep), self.ord, self.dom)) return _hash def __getstate__(self): return (self.rep, self.lev, self.ord, self.dom, self._hash) def __getnewargs__(self): return (self.rep, self.lev, self.ord, self.dom, self._hash) def unify(f, g): """Unify representations of two sparse polynomials. """ if not hasattr(g, '__iter__'): if f.lev == g.lev and f.ord == g.ord and f.dom == g.dom: return f.lev, f.ord, f.dom, f.per, f.rep, g.rep else: raise UnificationFailed("can't unify %s with %s" % (f, g)) else: lev, ord, dom, reps = f.lev, f.ord, f.dom, [] for gg in g: if gg.lev == lev and gg.ord == ord and gg.dom == dom: reps.append(gg.rep) else: raise UnificationFailed("can't unify %s with %s" % (f, g)) return lev, ord, dom, f.per, f.rep, reps def per(f, rep, ord=None, dom=None, lower=False): """Create a sparse polynomial out of the given representation. """ lev = f.lev if lower: if not lev: return rep else: lev -= 1 if dom is None: dom = f.dom if ord is None: ord = f.ord return SparsePoly(rep, dom, ord, lev) @classmethod def zero(cls, lev, ord, dom): """Construct a zero-polynomial with appropriate properties. """ return cls(smp_zero(lev), ord, dom, lev) @classmethod def one(cls, lev, ord, dom): """Construct a one-polynomial with appropriate properties. """ return cls(smp_one(lev, dom), ord, dom, lev) @classmethod def from_ground(cls, rep, lev, ord, dom): """Create sparse representation from an element of the ground domain. """ return cls(smp_from_ground(rep, lev, ord, dom), ord, dom, lev) @classmethod def from_dict(cls, rep, lev, ord, dom): """Create sparse representation from a ``dict`` with native coefficients. """ return cls(smp_from_dict(rep, lev, ord, dom), ord, dom, lev) @classmethod def from_sympy_dict(cls, rep, lev, ord, dom): """Create sparse representation from a ``dict`` with SymPy's coefficients. """ return cls(smp_from_sympy_dict(rep, lev, ord, dom), ord, dom, lev) @classmethod def from_list(cls, rep, lev, ord, dom): """Create sparse representation from a ``list`` with native coefficients. """ return cls(smp_from_dict(rep, lev, ord, dom), ord, dom, lev) @classmethod def from_sympy_list(cls, rep, lev, ord, dom): """Create sparse representation from a ``list`` with SymPy's coefficients. """ return cls(smp_from_sympy_dict(rep, lev, ord, dom), ord, dom, lev) def to_ground(f): """Convert sparse representation to an element of the ground domain. """ return smp_to_ground(f.rep, f.lev, f.ord, f.dom) def to_dict(f): """Convert sparse representation to a ``dict`` with native coefficients. """ return smp_to_dict(f.rep, f.lev, f.ord, f.dom) def to_sympy_dict(f): """Convert sparse representation to a ``dict`` with SymPy's coefficients. """ return smp_to_sympy_dict(f.rep, f.lev, f.ord, f.dom) def to_list(f): """Convert sparse representation to a ``list`` with native coefficients. """ return smp_to_dict(f.rep, f.lev, f.ord, f.dom) def to_sympy_list(f): """Convert sparse representation to a ``list`` with SymPy's coefficients. """ return smp_to_sympy_dict(f.rep, f.lev, f.ord, f.dom) def set_order(f, ord): """Set the ordering of monomials in `f` to ``ord``. """ if f.ord == ord: return f else: return f.per(smp_set_order(f.rep, f.lev, ord, f.dom), ord=ord) def set_domain(f, dom): """Set the ground domain in `f` to ``dom``. """ if f.dom == dom: return f else: return f.per(smp_set_domain(f.rep, f.lev, f.ord, f.dom, dom), dom=dom) def LC(f): """Return the leading coefficient of `f`. """ return smp_ground_LC(f.rep, f.lev, f.ord, f.dom) def LM(f): """Return the leading monomial of `f`. """ return smp_ground_LM(f.rep, f.lev, f.ord, f.dom) def LT(f): """Return the leading term of `f`. """ return smp_ground_LT(f.rep, f.lev, f.ord, f.dom) def TC(f): """Return the trailing coefficient of `f`. """ return smp_ground_TC(f.rep, f.lev, f.ord, f.dom) def TM(f): """Return the trailing monomial of `f`. """ return smp_ground_TM(f.rep, f.lev, f.ord, f.dom) def TT(f): """Return the trailing coefficient of `f`. """ return smp_ground_TT(f.rep, f.lev, f.ord, f.dom) def EC(f): """Return the last non-zero coefficient of `f`. """ return smp_ground_EC(f.rep, f.lev, f.ord, f.dom) def EM(f): """Return the last non-zero monomial of `f`. """ return smp_ground_EM(f.rep, f.lev, f.ord, f.dom) def ET(f): """Return the last non-zero coefficient of `f`. """ return smp_ground_ET(f.rep, f.lev, f.ord, f.dom) def nth(f, *N): """Return `n`-th coefficient of `f`. """ return smp_ground_nth(f.rep, N, f.lev, f.dom) def coeffs(f): """Return all non-zero coefficients of `f`. """ return smp_coeffs(f.rep, f.lev, f.ord, f.dom) def monoms(f): """Return all non-zero monomials of `f`. """ return smp_monoms(f.rep, f.lev, f.ord, f.dom) def terms(f): """Return all non-zero terms from `f`. """ return smp_terms(f.rep, f.lev, f.ord, f.dom) def all_coeffs(f): """Return all coefficients of `f`. """ return smp_all_coeffs(f.rep, f.lev, f.ord, f.dom) def all_monoms(f): """Return all monomials of `f`. """ return smp_all_monoms(f.rep, f.lev, f.ord, f.dom) def all_terms(f): """Return all terms of `f`. """ return smp_all_terms(f.rep, f.lev, f.ord, f.dom) def degree(f, j=0): """Return the degree of `f` in `x_j`. """ return smp_degree(f.rep, j, f.lev) def degrees(f): """Return the list of degrees of `f`. """ return smp_degrees(f.rep, f.lev) def total_degree(f): """Return the total degree of `f`. """ return smp_total_degree(f.rep, f.lev) def deflate(f): """Reduce degree of `f` by mapping `x_i^m` to `y_i`. """ M, F = smp_deflate(f.rep, f.lev, f.ord, f.dom) return M, f.per(F) def inflate(f, M): """Revert :func:`deflate` by mapping `y_i` to `x_i^m`. """ return f.per(smp_inflate(f.rep, M, f.lev, f.ord, f.dom)) def terms_gcd(f): """Remove GCD of terms from the polynomial `f`. """ J, F = smp_terms_gcd(f.rep, f.lev, f.ord, f.dom) return J, f.per(F) def add_ground(f, c): """Add an element of the ground domain to `f`. """ return f.per(smp_add_ground(f.rep, f.dom.convert(c), f.lev, f.ord, f.dom)) def sub_ground(f, c): """Subtract an element of the ground domain from `f`. """ return f.per(smp_sub_ground(f.rep, f.dom.convert(c), f.lev, f.ord, f.dom)) def mul_ground(f, c): """Multiply `f` by an element of the ground domain. """ return f.per(smp_mul_ground(f.rep, f.dom.convert(c), f.lev, f.ord, f.dom)) def quo_ground(f, c): """Quotient of `f` by an element of the ground domain. """ return f.per(smp_quo_ground(f.rep, f.dom.convert(c), f.lev, f.ord, f.dom)) def exquo_ground(f, c): """Exact quotient of `f` by an element of the ground domain. """ return f.per(smp_exquo_ground(f.rep, f.dom.convert(c), f.lev, f.ord, f.dom)) def abs(f): """Make all coefficients in `f` positive. """ return f.per(smp_abs(f.rep, f.lev, f.ord, f.dom)) def neg(f): """Negate all coefficients in `f`. """ return f.per(smp_neg(f.rep, f.lev, f.ord, f.dom)) def add(f, g): """Add two multivariate polynomials `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_add(F, G, lev, ord, dom)) def sub(f, g): """Subtract two multivariate polynomials `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_sub(F, G, lev, ord, dom)) def mul(f, g): """Multiply two multivariate polynomials `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_mul(F, G, lev, ord, dom)) def sqr(f): """Square a multivariate polynomial `f`. """ return f.per(smp_sqr(f.rep, f.lev, f.ord, f.dom)) def pow(f, n): """Raise `f` to a non-negative power `n`. """ return f.per(smp_pow(f.rep, n, f.lev, f.ord, f.dom)) def pdiv(f, g): """Polynomial pseudo-division of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) q, r = smp_pdiv(F, G, lev, ord, dom) return per(q), per(r) def prem(f, g): """Polynomial pseudo-remainder of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_prem(F, G, lev, dom)) def pquo(f, g): """Polynomial pseudo-quotient of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_pquo(F, G, lev, ord, dom)) def pexquo(f, g): """Polynomial exact pseudo-quotient of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_pexquo(F, G, lev, ord, dom)) def div(f, g): """Polynomial division with remainder of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) q, r = smp_div(F, G, lev, ord, dom) return per(q), per(r) def rem(f, g): """Compute polynomial remainder of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_rem(F, G, lev, ord, dom)) def quo(f, g): """Compute polynomial quotient of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_quo(F, G, lev, ord, dom)) def exquo(f, g): """Compute polynomial exact quotient of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_exquo(F, G, lev, ord, dom)) def reduced(f, G): """Reduce `f` modulo a set of polynomials `G`. """ lev, ord, dom, per, f, G = f.unify(G) return per(smp_reduced(f, G, lev, ord, dom)) def max_norm(f): """Returns maximum norm of `f`. """ return smp_max_norm(f.rep, f.lev, f.ord, f.dom) def l1_norm(f): """Returns l1 norm of `f`. """ return smp_l1_norm(f.rep, f.lev, f.ord, f.dom) def clear_denoms(f, convert=False): """Clear denominators in `f`, but keep the ground domain. """ coeff, F = smp_clear_denoms(f.rep, f.lev, f.ord, f.dom, convert=convert) return coeff, f.per(F) def lift(f): """Convert algebraic coefficients to rationals. """ return f.per(smp_lift(f.rep, f.lev, f.ord, f.dom), dom=f.dom.dom) def half_gcdex(f, g): """Half extended Euclidean algorithm. """ lev, ord, dom, per, F, G = f.unify(g) s, h = smp_half_gcdex(F, G, ord, dom) return per(s), per(h) def gcdex(f, g): """Extended Euclidean algorithm. """ lev, ord, dom, per, F, G = f.unify(g) s, t, h = smp_gcdex(F, G, lev, ord, dom) return per(s), per(t), per(h) def invert(f, g): """Invert `f` modulo `g`, if possible. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_invert(F, G, lev, ord, dom)) def subresultants(f, g): """Compute subresultant PRS sequence of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) R = smp_subresultants(F, G, lev, ord, dom) return map(per, R) def resultant(f, g): """Compute resultant of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_resultant(F, G, lev, ord, dom), lower=True) def discriminant(f): """Compute discriminant of `f`. """ return f.per(smp_discriminant(f.rep, f.lev, f.ord, f.dom), lower=True) def cofactors(f, g): """Compute GCD of `f` and `g` and their cofactors. """ lev, ord, dom, per, F, G = f.unify(g) h, cff, cfg = smp_cofactors(F, G, lev, ord, dom) return per(h), per(cff), per(cfg) def gcd(f, g): """Compute polynomial GCD of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_gcd(F, G, lev, ord, dom)) def lcm(f, g): """Compute polynomial LCM of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_lcm(F, G, lev, ord, dom)) def trunc(f, p): """Reduce `f` modulo an element of the ground domain. """ return f.per(smp_ground_trunc(f.rep, f.dom.convert(p), f.lev, f.ord, f.dom)) def monic(f): """Divide all coefficients by the leading coefficient of `f`. """ return f.per(smp_ground_monic(f.rep, f.lev, f.ord, f.dom)) def content(f): """Compute GCD of all coefficients of `f`. """ return smp_ground_content(f.rep, f.lev, f.ord, f.dom) def primitive(f): """Compute content and the primitive form of `f`. """ cont, F = smp_ground_primitive(f.rep, f.lev, f.ord, f.dom) return cont, f.per(F) def integrate(f, m=1, j=0): """Compute `m`-th order indefinite integral of `f` in `x_j`. """ return f.per(smp_integrate_in(f.rep, m, j, f.lev, f.ord, f.dom)) def diff(f, m=1, j=0): """Compute `m`-th order derivative of `f` in `x_j`. """ return f.per(smp_diff_in(f.rep, m, j, f.lev, f.ord, f.dom)) def eval(f, a, j=0): """Evaluate `f` at the given point `a` in `x_j`. """ return f.per(smp_eval_in(f.rep, f.dom.convert(a), j, f.lev, f.ord, f.dom), lower=True) def mirror(f, j=0): """Evaluate efficiently composition `f(-x_j)`. """ return f.per(smp_mirror_in(f.rep, j, f.lev, f.ord, f.dom)) def scale(f, a, j=0): """Evaluate efficiently composition `f(a x_j)`. """ return f.per(smp_scale_in(f.rep, f.dom.convert(a), j, f.lev, f.ord, f.dom)) def taylor(f, a, j=0): """Evaluate efficiently Taylor shift `f(x_j + a)`. """ return f.per(smp_taylor_in(f.rep, f.dom.convert(a), j, f.lev, f.ord, f.dom)) def transform(f, p, q, j=0): """Evaluate functional transformation `q^n \cdot f(p/q)`. """ lev, ord, dom, per, F, (P, Q) = f.unify((p, q)) return per(smp_transform_in(F, P, Q, j, lev, ord, dom)) def compose(f, g): """Compute functional composition of `f` and `g`. """ lev, ord, dom, per, F, G = f.unify(g) return per(smp_compose(F, G, lev, ord, dom)) def decompose(f): """Computes functional decomposition of `f`. """ return map(f.per, smp_decompose(f.rep, f.lev, f.ord, f.dom)) def sturm(f): """Computes the Sturm sequence of `f`. """ return map(f.per, smp_sturm(f.rep, f.lev, f.ord, f.dom)) def sqf_norm(f): """Compute square-free norm of `f`. """ s, g, r = smp_sqf_norm(f.rep, f.lev, f.ord, f.dom) return s, f.per(g), f.per(r, dom=f.dom.dom) def sqf_part(f): """Compute square-free part of `f`. """ return f.per(smp_sqf_part(f.rep, f.lev, f.ord, f.dom)) def sqf_list(f, all=False, include=False): """Return a list of square-free factors of `f`. """ result = smp_sqf_list(f.rep, f.lev, f.ord, f.dom, all=all, include=include) return f._perify_factors(result, include) def factor_list(f, include=False): """Return a list of irreducible factors of `f`. """ result = smp_factor_list(f.rep, f.lev, f.ord, f.dom, include=include) return f._perify_factors(f.per, result, include) def real_intervals(f, eps=None, inf=None, sup=None, fast=False, sqf=False): """Compute isolating intervals for real roots of `f`. """ return smp_real_intervals(f.rep, f.lev, f.ord, f.dom, eps=eps, inf=inf, sup=sup, fast=fast, sqf=sqf) def complex_intervals(f, eps=None, inf=None, sup=None, fast=False, sqf=False): """Compute isolating rectangles for complex roots of `f`. """ return smp_complex_intervals(f.rep, f.lev, f.ord, f.dom, eps=eps, inf=inf, sup=sup, fast=fast, sqf=sqf) def refine_real_root(f, s, t, eps=None, steps=None, fast=False, sqf=False): """Refine a real root isolating interval to the given precision. """ return smp_refine_real_root(f.rep, s, t, f.lev, f.ord, f.dom, eps=eps, steps=steps, fast=fast, sqf=sqf) def refine_complex_root(f, s, t, eps=None, steps=None, fast=False, sqf=False): """Refine a complex root isolating rectangle to the given precision. """ return smp_refine_complex_root(f.rep, s, t, f.lev, f.ord, f.dom, eps=eps, steps=steps, fast=fast, sqf=sqf) def count_real_roots(f, inf=None, sup=None): """Return the number of real roots of `f` in the ``[inf, sup]`` interval. """ return smp_count_real_roots(f.rep, f.lev, f.ord, f.dom, inf=inf, sup=sup) def count_complex_roots(f, inf=None, sup=None): """Return the number of complex roots of `f` in the ``[inf, sup]`` rectangle. """ return smp_count_complex_roots(f.rep, f.lev, f.ord, f.dom, inf=inf, sup=sup) @property def is_zero(f): """Returns ``True`` if `f` is equivalent to zero. """ return smp_zero_p(f.rep, f.lev) @property def is_one(f): """Return ``True`` if `f` is equivalent to one. """ return smp_one_p(f.rep, f.lev, f.dom) @property def is_ground(f): """Return ``True`` if `f` is an element of the ground domain. """ return smp_ground_p(f.rep, f.lev) @property def is_sqf(f): """Return ``True`` if `f` is a square-free polynomial. """ return smp_sqf_p(f.rep, f.lev, f.ord, f.dom) @property def is_monic(f): """Return ``True`` if the leading coefficient of `f` is one. """ return smp_monic_p(f.rep, f.lev, f.ord, f.dom) @property def is_primitive(f): """Return ``True`` if GCD of coefficients of `f` is one. """ return smp_primitive_p(f.rep, f.lev, f.ord, f.dom) @property def is_linear(f): """Return ``True`` if `f` is linear in all its variables. """ return smp_linear_p(f.rep, f.lev, f.ord, f.dom) @property def is_homogeneous(f): """Return ``True`` if `f` has zero trailing coefficient. """ return smp_homogeneous_p(f.rep, f.lev, f.ord, f.dom) def __abs__(f): return f.abs() def __neg__(f): return f.neg() def __add__(f, g): if not isinstance(g, SparsePoly): return f.add_ground(g) else: return f.add(g) def __radd__(f, g): return f.__add__(g) def __sub__(f, g): if not isinstance(g, SparsePoly): return f.sub_ground(g) else: return f.sub(g) def __rsub__(f, g): return (-f).__add__(g) def __mul__(f, g): if not isinstance(g, SparsePoly): return f.mul_ground(g) else: return f.mul(g) def __rmul__(f, g): return f.__mul__(g) def __pow__(f, n): return f.pow(n) def __divmod__(f, g): return f.div(g) def __mod__(f, g): return f.rem(g) def __floordiv__(f, g): if not isinstance(g, SparsePoly): return f.exquo_ground(g) else: return f.exquo(g) def __eq__(f, g): return isinstance(g, SparsePoly) and f.rep == g.rep def __ne__(f, g): return not f.__eq__(g) def __nonzero__(f): return not f.is_zero wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/specialpolys.py0000644000175000017500000002166012014170666025333 0ustar georgeskgeorgesk"""Functions for generating interesting polynomials, e.g. for benchmarking. """ from sympy.core import Add, Mul, Symbol, Rational, sympify, Dummy, symbols from sympy.core.singleton import S from sympy.polys.polytools import Poly, PurePoly from sympy.polys.polyutils import _analyze_gens from sympy.polys.polyclasses import DMP from sympy.polys.densebasic import ( dmp_zero, dmp_one, dmp_ground, dmp_normal, dup_from_raw_dict, dmp_raise, dup_random ) from sympy.polys.densearith import ( dmp_add_term, dmp_neg, dmp_mul, dmp_sqr ) from sympy.polys.factortools import ( dup_zz_cyclotomic_poly ) from sympy.polys.domains import ZZ from sympy.ntheory import nextprime from sympy.utilities import cythonized, subsets @cythonized("n,i") def swinnerton_dyer_poly(n, x=None, **args): """Generates n-th Swinnerton-Dyer polynomial in `x`. """ if n <= 0: raise ValueError("can't generate Swinnerton-Dyer polynomial of order %s" % n) if x is not None: x, cls = sympify(x), Poly else: x, cls = Dummy('x'), PurePoly p, elts = 2, [[x, -2**Rational(1,2)], [x, 2**Rational(1,2)]] for i in xrange(2, n+1): p, _elts = nextprime(p), [] neg_sqrt = -p**Rational(1,2) pos_sqrt = +p**Rational(1,2) for elt in elts: _elts.append(elt + [neg_sqrt]) _elts.append(elt + [pos_sqrt]) elts = _elts poly = [] for elt in elts: poly.append(Add(*elt)) if not args.get('polys', False): return Mul(*poly).expand() else: return PurePoly(Mul(*poly), x) def cyclotomic_poly(n, x=None, **args): """Generates cyclotomic polynomial of order `n` in `x`. """ if n <= 0: raise ValueError("can't generate cyclotomic polynomial of order %s" % n) poly = DMP(dup_zz_cyclotomic_poly(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly def symmetric_poly(n, *gens, **args): """Generates symmetric polynomial of order `n`. """ gens = _analyze_gens(gens) if n < 0 or n > len(gens) or not gens: raise ValueError("can't generate symmetric polynomial of order %s for %s" % (n, gens)) elif not n: poly = S.One else: poly = Add(*[ Mul(*s) for s in subsets(gens, int(n)) ]) if not args.get('polys', False): return poly else: return Poly(poly, *gens) def random_poly(x, n, inf, sup, domain=ZZ, polys=False): """Return a polynomial of degree ``n`` with coefficients in ``[inf, sup]``. """ poly = Poly(dup_random(n, inf, sup, domain), x, domain=domain) if not polys: return poly.as_expr() else: return poly @cythonized("n,i,j") def interpolating_poly(n, x, X='x', Y='y'): """Construct Lagrange interpolating polynomial for ``n`` data points. """ if isinstance(X, str): X = symbols("%s:%s" % (X, n)) if isinstance(Y, str): Y = symbols("%s:%s" % (Y, n)) coeffs = [] for i in xrange(0, n): numer = [] denom = [] for j in xrange(0, n): if i == j: continue numer.append(x - X[j]) denom.append(X[i] - X[j]) numer = Mul(*numer) denom = Mul(*denom) coeffs.append(numer/denom) return Add(*[ coeff*y for coeff, y in zip(coeffs, Y) ]) @cythonized("n,i") def fateman_poly_F_1(n): """Fateman's GCD benchmark: trivial GCD """ Y = [ Symbol('y_' + str(i)) for i in xrange(0, n+1) ] y_0, y_1 = Y[0], Y[1] u = y_0 + Add(*[ y for y in Y[1:] ]) v = y_0**2 + Add(*[ y**2 for y in Y[1:] ]) F = ((u + 1)*(u + 2)).as_poly(*Y) G = ((v + 1)*(-3*y_1*y_0**2 + y_1**2 - 1)).as_poly(*Y) H = Poly(1, *Y) return F, G, H @cythonized("n,m,i") def dmp_fateman_poly_F_1(n, K): """Fateman's GCD benchmark: trivial GCD """ u = [K(1), K(0)] for i in xrange(0, n): u = [dmp_one(i, K), u] v = [K(1), K(0), K(0)] for i in xrange(0, n): v = [dmp_one(i, K), dmp_zero(i), v] m = n-1 U = dmp_add_term(u, dmp_ground(K(1), m), 0, n, K) V = dmp_add_term(u, dmp_ground(K(2), m), 0, n, K) f = [[-K(3), K(0)], [], [K(1), K(0), -K(1)]] W = dmp_add_term(v, dmp_ground(K(1), m), 0, n, K) Y = dmp_raise(f, m, 1, K) F = dmp_mul(U, V, n, K) G = dmp_mul(W, Y, n, K) H = dmp_one(n, K) return F, G, H @cythonized("n,i") def fateman_poly_F_2(n): """Fateman's GCD benchmark: linearly dense quartic inputs """ Y = [ Symbol('y_' + str(i)) for i in xrange(0, n+1) ] y_0 = Y[0] u = Add(*[ y for y in Y[1:] ]) H = Poly((y_0 + u + 1)**2, *Y) F = Poly((y_0 - u - 2)**2, *Y) G = Poly((y_0 + u + 2)**2, *Y) return H*F, H*G, H @cythonized("n,m,i") def dmp_fateman_poly_F_2(n, K): """Fateman's GCD benchmark: linearly dense quartic inputs """ u = [K(1), K(0)] for i in xrange(0, n-1): u = [dmp_one(i, K), u] m = n-1 v = dmp_add_term(u, dmp_ground(K(2), m-1), 0, n, K) f = dmp_sqr([dmp_one(m, K), dmp_neg(v, m, K)], n, K) g = dmp_sqr([dmp_one(m, K), v], n, K) v = dmp_add_term(u, dmp_one(m-1, K), 0, n, K) h = dmp_sqr([dmp_one(m, K), v], n, K) return dmp_mul(f, h, n, K), dmp_mul(g, h, n, K), h @cythonized("n,i") def fateman_poly_F_3(n): """Fateman's GCD benchmark: sparse inputs (deg f ~ vars f) """ Y = [ Symbol('y_' + str(i)) for i in xrange(0, n+1) ] y_0 = Y[0] u = Add(*[ y**(n+1) for y in Y[1:] ]) H = Poly((y_0**(n+1) + u + 1)**2, *Y) F = Poly((y_0**(n+1) - u - 2)**2, *Y) G = Poly((y_0**(n+1) + u + 2)**2, *Y) return H*F, H*G, H @cythonized("n,i") def dmp_fateman_poly_F_3(n, K): """Fateman's GCD benchmark: sparse inputs (deg f ~ vars f) """ u = dup_from_raw_dict({n+1: K.one}, K) for i in xrange(0, n-1): u = dmp_add_term([u], dmp_one(i, K), n+1, i+1, K) v = dmp_add_term(u, dmp_ground(K(2), n-2), 0, n, K) f = dmp_sqr(dmp_add_term([dmp_neg(v, n-1, K)], dmp_one(n-1, K), n+1, n, K), n, K) g = dmp_sqr(dmp_add_term([v], dmp_one(n-1, K), n+1, n, K), n, K) v = dmp_add_term(u, dmp_one(n-2, K), 0, n-1, K) h = dmp_sqr(dmp_add_term([v], dmp_one(n-1, K), n+1, n, K), n, K) return dmp_mul(f, h, n, K), dmp_mul(g, h, n, K), h # A few useful polynomials from Wang's paper ('78). f_0 = dmp_normal([ [[1,2,3], [2]], [[3]], [[4,5,6], [1,2,1], [1]] ], 2, ZZ) f_1 = dmp_normal([ [[1, 0], []], [[1, 0, 1], [20, 30], [1, 10, 0]], [[1, 0], [30, 20], [1, 10, 1, 610], [20, 230, 300]], [[1, 10, 0], [30, 320, 200], [600, 6000]] ], 2, ZZ) f_2 = dmp_normal([ [[1], [1, 0], [1, 0, 0], [1, 0, 0, 0]], [[]], [[1], [1, 90], [90, 0]], [[1, -11], [], [1, -11, 0, 0]], [[]], [[1, -11], [90, -990]] ], 2, ZZ) f_3 = dmp_normal([ [[1], [], []], [[1, 0, 0, 0, 1]], [[1, 0], [], [], [1, 0]], [[1], [1, 0, 0, 0], [], [1, 0, 0, 0, 1, 0], []], [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1, 1, 0, 0], []], [[1, 0], [1, 0, 0, 0, 0], []] ], 2, ZZ) f_4 = dmp_normal([ [[-1, 0], [], [], [], [], [], [], [], []], [[-1, 0, 0, 0], [], [], [], [], []], [[-1, 0, 0], [], [], [], [-5], [], [], [], [], [], [], [], []], [[-1, 0, 0, 0, 0], [], [1, 0, 3, 0], [], [-5, 0, 0], [-1, 0, 0, 0], [], [], [], []], [[1, 0, 3, 0, 0, 0], [], [], [-1, 0, 0, 0, 0, 0], []], [[1, 0, 3, 0, 0], [], [], [-1, 0, 0, 0, 0], [5, 0, 15], [], [], [-5, 0, 0], [], [], [], []], [[1, 0, 3, 0, 0, 0, 0], [], [], [-1, 0, 0, 0, 0, 0, 0], [5, 0, 15, 0, 0], [1, 0, 3, 0, 0, 0], [], [-5, 0, 0, 0, 0], []], [[1, 0, 3, 0, 0, 0, 0, 0]], [[1, 0, 3, 0, 0, 0, 0], [], [], [], [5, 0, 15, 0, 0], [], [], []], [[1, 0, 3, 0, 0, 0, 0, 0, 0], [], [], [], [5, 0, 15, 0, 0, 0, 0]] ], 2, ZZ) f_5 = dmp_normal([ [[-1]], [[-3], [3, 0]], [[-3], [6, 0], [-3, 0, 0]], [[-1], [3, 0], [-3, 0, 0], [1, 0, 0, 0]] ], 2, ZZ) f_6 = dmp_normal([ [[[2115]], [[]]], [[[45, 0, 0], [], [], [-45, 0, 0]]], [[[]]], [[[-423]], [[-47]], [[]], [[141], [], [94, 0], []], [[]]], [[[-9, 0, 0], [], [], [9, 0, 0]], [[-1, 0, 0], [], [], [1, 0, 0]], [[]], [[3, 0, 0], [], [2, 0, 0, 0], [-3, 0, 0], [], [-2, 0, 0, 0], []] ] ], 3, ZZ) w_1 = dmp_normal([ [[4, 0, 0], [4, 0, 0, 0], [-4, 0, 0, 0, 0], [-4, 0, 0, 0, 0, 0], []], [[1, 0, 0, 0], [12, 0], [-1, 0, 0, 12, 0, 0], [-12, 0, 0, 0], [-12, 0, 0, 0, 0]], [[8], [6, 8, 0], [-4, 4, -8, 0, 0], [-4, -2, -8, 0, 0, 0], []], [[2, 0], [1, 0, 0, 0], [-1, 0, -2 , 0, 9, 0], [-12, 12, 0, 0], [-12, 3, 0, 0, 0]], [[6], [-6, 8, 0], [-2, -8, 2, 0, 0], []], [[2, 0], [-2, 0, 0, 0], [-3, 0], [3, 0, 0, 0]], [[-2], [2, 0, 0], []] ], 2, ZZ) w_2 = dmp_normal([ [24, 48, 0, 0], [24, 0, 0, -72, 0, 0], [25, 2, 0, 4, 8], [1, 0, 0, 1, 0, 0, -12], [1, -1, -2, 292, 0, 0], [-1, 0, 0, 3, 0, 0, 0], [-1, 0, 12, 0, 0, 48], [], [-12, 0, 0, 0] ], 1, ZZ) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/sparsebasic.py0000644000175000017500000000315412014170666025121 0ustar georgeskgeorgesk"""Basic tools for sparse polynomials in `K[x]` or `K[X]`. """ def smp_validate(f): pass def sup_zero(): return {} def smp_zero(u): return {} def smp_one(u, K): return {(0,)*(u+1): K.one} def smp_from_ground(f, u, ord, K): pass def smp_from_dict(f, u, ord, K): pass def smp_from_sympy_dict(f, u, ord, K): pass def smp_from_dict(f, u, ord, K): pass def smp_from_sympy_dict(f, u, ord, K): pass def smp_to_ground(f, u, ord, K): pass def smp_to_dict(f, u, ord, K): pass def smp_to_sympy_dict(f, u, ord, K): pass def smp_to_dict(f, u, ord, K): pass def smp_to_sympy_dict(f, u, ord, K): pass def smp_set_order(f, u, ord, K): pass def smp_set_domain(f, u, ord, K0, K1): pass def smp_ground_LC(f, u, ord, K): pass def smp_ground_LM(f, u, ord, K): pass def smp_ground_LT(f, u, ord, K): pass def smp_ground_TC(f, u, ord, K): pass def smp_ground_TM(f, u, ord, K): pass def smp_ground_TT(f, u, ord, K): pass def smp_ground_EC(f, u, ord, K): pass def smp_ground_EM(f, u, ord, K): pass def smp_ground_ET(f, u, ord, K): pass def smp_coeffs(f, u, ord, K): pass def smp_monoms(f, u, ord, K): pass def smp_terms(f, u, ord, K): pass def smp_all_coeffs(f, u, ord, K): pass def smp_all_monoms(f, u, ord, K): pass def smp_all_terms(f, u, ord, K): pass def smp_degree(f, j, u): pass def smp_degrees(f, u): pass def smp_total_degree(f, u): return sum(smp_degrees(f, u)) def smp_deflate(f, u, ord, K): pass def smp_inflate(f, M, u, ord, K): pass def smp_terms_gcd(f, u, ord, K): pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polycontext.py0000644000175000017500000000344412014170666025214 0ustar georgeskgeorgesk"""Tools for managing evaluation contexts. """ from sympy.utilities.iterables import dict_merge __known_options__ = set(['frac', 'gens', 'wrt', 'sort', 'order', 'domain', 'modulus', 'gaussian', 'extension', 'field', 'greedy', 'symmetric']) __global_options__ = [] __template__ = """\ def %(option)s(_%(option)s): return Context(%(option)s=_%(option)s) """ for option in __known_options__: exec __template__ % { 'option': option } class Context(object): __slots__ = ['__options__'] def __init__(self, dict=None, **options): if dict is not None: self.__options__ = dict_merget(dict, options) else: self.__options__ = options def __getattribute__(self, name): if name in __known_options__: try: return object.__getattribute__(self, '__options__')[name] except KeyError: return None else: return object.__getattribute__(self, name) def __str__(self): return 'Context(%s)' % ', '.join( [ '%s=%r' % (key, value) for key, value in self.__options__.iteritems() ]) def __and__(self, other): if isinstance(other, Context): return Context(**dict_merge(self.__options__, other.__options__)) else: raise TypeError("a context manager expected, got %s" % other) def __enter__(self): raise NotImplementedError('global context') def __exit__(self, exc_type, exc_val, exc_tb): raise NotImplementedError('global context') def register_context(func): def wrapper(self, *args, **kwargs): return func(*args, **dict_merge(self.__options__, kwargs)) wrapper.__doc__ = func.__doc__ wrapper.__name__ = func.__name__ setattr(Context, func.__name__, wrapper) return func wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/galoistools.py0000644000175000017500000012612612014170666025166 0ustar georgeskgeorgesk"""Dense univariate polynomials with coefficients in Galois fields. """ from random import uniform from math import ceil, sqrt, log from sympy.polys.polyutils import _sort_factors from sympy.polys.polyconfig import query from sympy.polys.polyerrors import ExactQuotientFailed from sympy.utilities import cythonized from sympy.ntheory import factorint def gf_crt(U, M, K): """ Chinese Remainder Theorem. Given a set of integer residues ``u_0,...,u_n`` and a set of co-prime integer moduli ``m_0,...,m_n``, returns an integer ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``. As an example consider a set of residues ``U = [49, 76, 65]`` and a set of moduli ``M = [99, 97, 95]``. Then we have:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ) 639985 This is the correct result because:: >>> 639985 % 99 49 >>> 639985 % 97 76 >>> 639985 % 95 65 """ p, v = K.one, K.zero for m in M: p *= m for u, m in zip(U, M): e = p // m s, _, _ = K.gcdex(e, m) v += e*(u*s % m) return v % p def gf_crt1(M, K): """ First part of the Chinese Remainder Theorem. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt1 >>> gf_crt1([99, 97, 95], ZZ) (912285, [9215, 9405, 9603], [62, 24, 12]) """ p, E, S = K.one, [], [] for m in M: p *= m for m in M: E.append(p // m) S.append(K.gcdex(E[-1], m)[0] % m) return p, E, S def gf_crt2(U, M, p, E, S, K): """ Second part of the Chinese Remainder Theorem. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt2 >>> U = [49, 76, 65] >>> M = [99, 97, 95] >>> p = 912285 >>> E = [9215, 9405, 9603] >>> S = [62, 24, 12] >>> gf_crt2(U, M, p, E, S, ZZ) 639985 """ v = K.zero for u, m, e, s in zip(U, M, E, S): v += e*(u*s % m) return v % p def gf_int(a, p): """ Coerce ``a mod p`` to an integer in ``[-p/2, p/2]`` range. **Examples** >>> from sympy.polys.galoistools import gf_int >>> gf_int(2, 7) 2 >>> gf_int(5, 7) -2 """ if a <= p // 2: return a else: return a - p def gf_degree(f): """ Return leading degree of ``f``. **Examples** >>> from sympy.polys.galoistools import gf_degree >>> gf_degree([1, 1, 2, 0]) 3 >>> gf_degree([]) -1 """ return len(f)-1 def gf_LC(f, K): """ Return leading coefficient of ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_LC >>> gf_LC([3, 0, 1], ZZ) 3 """ if not f: return K.zero else: return f[0] def gf_TC(f, K): """ Return trailing coefficient of ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_TC >>> gf_TC([3, 0, 1], ZZ) 1 """ if not f: return K.zero else: return f[-1] @cythonized("k") def gf_strip(f): """ Remove leading zeros from ``f``. **Examples** >>> from sympy.polys.galoistools import gf_strip >>> gf_strip([0, 0, 0, 3, 0, 1]) [3, 0, 1] """ if not f or f[0]: return f k = 0 for coeff in f: if coeff: break else: k += 1 return f[k:] def gf_trunc(f, p): """ Reduce all coefficients modulo ``p``. **Examples** >>> from sympy.polys.galoistools import gf_trunc >>> gf_trunc([7, -2, 3], 5) [2, 3, 3] """ return gf_strip([ a % p for a in f ]) def gf_normal(f, p, K): """ Normalize all coefficients in ``K``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_normal >>> gf_normal([5, 10, 21, -3], 5, ZZ) [1, 2] """ return gf_trunc(map(K, f), p) def gf_convert(f, p, K0, K1): """ Normalize all coefficients in ``K``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.galoistools import gf_convert >>> gf_convert([QQ(1), QQ(4), QQ(0)], 3, QQ, ZZ) [1, 1, 0] """ return gf_trunc([ K1.convert(c, K0) for c in f ], p) @cythonized("k,n") def gf_from_dict(f, p, K): """ Create ``GF(p)[x]`` polynomial from a dict. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_from_dict >>> gf_from_dict({10: 4, 4: 33, 0: -1}, 5, ZZ) [4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4] """ n, h = max(f.iterkeys()), [] if type(n) is int: for k in xrange(n, -1, -1): h.append(f.get(k, K.zero) % p) else: (n,) = n for k in xrange(n, -1, -1): h.append(f.get((k,), K.zero) % p) return gf_trunc(h, p) @cythonized("k,n") def gf_to_dict(f, p, symmetric=True): """ Convert ``GF(p)[x]`` polynomial to a dict. **Examples** >>> from sympy.polys.galoistools import gf_to_dict >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5) {0: -1, 4: -2, 10: -1} >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5, symmetric=False) {0: 4, 4: 3, 10: 4} """ n, result = gf_degree(f), {} for k in xrange(0, n+1): if symmetric: a = gf_int(f[n-k], p) else: a = f[n-k] if a: result[k] = a return result def gf_from_int_poly(f, p): """ Create ``GF(p)[x]`` polynomial from ``Z[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_from_int_poly >>> gf_from_int_poly([7, -2, 3], 5) [2, 3, 3] """ return gf_trunc(f, p) def gf_to_int_poly(f, p, symmetric=True): """ Convert ``GF(p)[x]`` polynomial to ``Z[x]``. **Examples** >>> from sympy.polys.galoistools import gf_to_int_poly >>> gf_to_int_poly([2, 3, 3], 5) [2, -2, -2] >>> gf_to_int_poly([2, 3, 3], 5, symmetric=False) [2, 3, 3] """ if symmetric: return [ gf_int(c, p) for c in f ] else: return f def gf_neg(f, p, K): """ Negate a polynomial in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_neg >>> gf_neg([3, 2, 1, 0], 5, ZZ) [2, 3, 4, 0] """ return [ -coeff % p for coeff in f ] def gf_add_ground(f, a, p, K): """ Compute ``f + a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_add_ground >>> gf_add_ground([3, 2, 4], 2, 5, ZZ) [3, 2, 1] """ if not f: a = a % p else: a = (f[-1] + a) % p if len(f) > 1: return f[:-1] + [a] if not a: return [] else: return [a] def gf_sub_ground(f, a, p, K): """ Compute ``f - a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sub_ground >>> gf_sub_ground([3, 2, 4], 2, 5, ZZ) [3, 2, 2] """ if not f: a = -a % p else: a = (f[-1] - a) % p if len(f) > 1: return f[:-1] + [a] if not a: return [] else: return [a] def gf_mul_ground(f, a, p, K): """ Compute ``f * a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_mul_ground >>> gf_mul_ground([3, 2, 4], 2, 5, ZZ) [1, 4, 3] """ if not a: return [] else: return [ (a*b) % p for b in f ] def gf_quo_ground(f, a, p, K): """ Compute ``f/a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_quo_ground >>> gf_quo_ground([3, 2, 4], 2, 5, ZZ) [4, 1, 2] """ return gf_mul_ground(f, K.invert(a, p), p, K) @cythonized("df,dg,k") def gf_add(f, g, p, K): """ Add polynomials in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_add >>> gf_add([3, 2, 4], [2, 2, 2], 5, ZZ) [4, 1] """ if not f: return g if not g: return f df = gf_degree(f) dg = gf_degree(g) if df == dg: return gf_strip([ (a + b) % p for a, b in zip(f, g) ]) else: k = abs(df - dg) if df > dg: h, f = f[:k], f[k:] else: h, g = g[:k], g[k:] return h + [ (a + b) % p for a, b in zip(f, g) ] @cythonized("df,dg,k") def gf_sub(f, g, p, K): """ Subtract polynomials in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sub >>> gf_sub([3, 2, 4], [2, 2, 2], 5, ZZ) [1, 0, 2] """ if not g: return f if not f: return gf_neg(g, p, K) df = gf_degree(f) dg = gf_degree(g) if df == dg: return gf_strip([ (a - b) % p for a, b in zip(f, g) ]) else: k = abs(df - dg) if df > dg: h, f = f[:k], f[k:] else: h, g = gf_neg(g[:k], p, K), g[k:] return h + [ (a - b) % p for a, b in zip(f, g) ] @cythonized("df,dg,dh,i,j") def gf_mul(f, g, p, K): """ Multiply polynomials in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_mul >>> gf_mul([3, 2, 4], [2, 2, 2], 5, ZZ) [1, 0, 3, 2, 3] """ df = gf_degree(f) dg = gf_degree(g) dh = df + dg h = [0]*(dh+1) for i in xrange(0, dh+1): coeff = K.zero for j in xrange(max(0, i-dg), min(i, df)+1): coeff += f[j]*g[i-j] h[i] = coeff % p return gf_strip(h) @cythonized("df,dh,i,j,jmin,jmax,n") def gf_sqr(f, p, K): """ Square polynomials in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sqr >>> gf_sqr([3, 2, 4], 5, ZZ) [4, 2, 3, 1, 1] """ df = gf_degree(f) dh = 2*df h = [0]*(dh+1) for i in xrange(0, dh+1): coeff = K.zero jmin = max(0, i-df) jmax = min(i, df) n = jmax - jmin + 1 jmax = jmin + n // 2 - 1 for j in xrange(jmin, jmax+1): coeff += f[j]*f[i-j] coeff += coeff if n & 1: elem = f[jmax+1] coeff += elem**2 h[i] = coeff % p return gf_strip(h) def gf_add_mul(f, g, h, p, K): """ Returns ``f + g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_add_mul >>> gf_add_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) [2, 3, 2, 2] """ return gf_add(f, gf_mul(g, h, p, K), p, K) def gf_sub_mul(f, g, h, p, K): """ Compute ``f - g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sub_mul >>> gf_sub_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) [3, 3, 2, 1] """ return gf_sub(f, gf_mul(g, h, p, K), p, K) @cythonized("k") def gf_expand(F, p, K): """ Expand results of :func:`factor` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_expand >>> gf_expand([([3, 2, 4], 1), ([2, 2], 2), ([3, 1], 3)], 5, ZZ) [4, 3, 0, 3, 0, 1, 4, 1] """ if type(F) is tuple: lc, F = F else: lc = K.one g = [lc] for f, k in F: f = gf_pow(f, k, p, K) g = gf_mul(g, f, p, K) return g @cythonized("df,dg,dq,dr,i,j") def gf_div(f, g, p, K): """ Division with remainder in ``GF(p)[x]``. Given univariate polynomials ``f`` and ``g`` with coefficients in a finite field with ``p`` elements, returns polynomials ``q`` and ``r`` (quotient and remainder) such that ``f = q*g + r``. Consider polynomials ``x**3 + x + 1`` and ``x**2 + x`` in GF(2):: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_div, gf_add_mul >>> gf_div([1, 0, 1, 1], [1, 1, 0], 2, ZZ) ([1, 1], [1]) As result we obtained quotient ``x + 1`` and remainder ``1``, thus:: >>> gf_add_mul([1], [1, 1], [1, 1, 0], 2, ZZ) [1, 0, 1, 1] **References** 1. [Monagan93]_ 2. [Gathen99]_ """ df = gf_degree(f) dg = gf_degree(g) if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return [], f inv = K.invert(g[0], p) h, dq, dr = list(f), df-dg, dg-1 for i in xrange(0, df+1): coeff = h[i] for j in xrange(max(0, dg-i), min(df-i, dr)+1): coeff -= h[i+j-dg] * g[dg-j] if i <= dq: coeff *= inv h[i] = coeff % p return h[:dq+1], gf_strip(h[dq+1:]) def gf_rem(f, g, p, K): """ Compute polynomial remainder in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_rem >>> gf_rem([1, 0, 1, 1], [1, 1, 0], 2, ZZ) [1] """ return gf_div(f, g, p, K)[1] @cythonized("df,dg,dq,dr,i,j") def gf_quo(f, g, p, K): """ Compute exact quotient in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_quo >>> gf_quo([1, 0, 1, 1], [1, 1, 0], 2, ZZ) [1, 1] >>> gf_quo([1, 0, 3, 2, 3], [2, 2, 2], 5, ZZ) [3, 2, 4] """ df = gf_degree(f) dg = gf_degree(g) if not g: raise ZeroDivisionError("polynomial division") elif df < dg: return [] inv = K.invert(g[0], p) h, dq, dr = f[:], df-dg, dg-1 for i in xrange(0, dq+1): coeff = h[i] for j in xrange(max(0, dg-i), min(df-i, dr)+1): coeff -= h[i+j-dg] * g[dg-j] h[i] = (coeff * inv) % p return h[:dq+1] def gf_exquo(f, g, p, K): """ Compute polynomial quotient in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_exquo >>> gf_exquo([1, 0, 3, 2, 3], [2, 2, 2], 5, ZZ) [3, 2, 4] >>> gf_exquo([1, 0, 1, 1], [1, 1, 0], 2, ZZ) Traceback (most recent call last): ... ExactQuotientFailed: [1, 1, 0] does not divide [1, 0, 1, 1] """ q, r = gf_div(f, g, p, K) if not r: return q else: raise ExactQuotientFailed(f, g) @cythonized("n") def gf_lshift(f, n, K): """ Efficiently multiply ``f`` by ``x**n``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_lshift >>> gf_lshift([3, 2, 4], 4, ZZ) [3, 2, 4, 0, 0, 0, 0] """ if not f: return f else: return f + [K.zero]*n @cythonized("n") def gf_rshift(f, n, K): """ Efficiently divide ``f`` by ``x**n``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_rshift >>> gf_rshift([1, 2, 3, 4, 0], 3, ZZ) ([1, 2], [3, 4, 0]) """ if not n: return f, [] else: return f[:-n], f[-n:] def gf_pow(f, n, p, K): """ Compute ``f**n`` in ``GF(p)[x]`` using repeated squaring. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_pow >>> gf_pow([3, 2, 4], 3, 5, ZZ) [2, 4, 4, 2, 2, 1, 4] """ if not n: return [K.one] elif n == 1: return f elif n == 2: return gf_sqr(f, p, K) h = [K.one] while True: if n & 1: h = gf_mul(h, f, p, K) n -= 1 n >>= 1 if not n: break f = gf_sqr(f, p, K) return h def gf_pow_mod(f, n, g, p, K): """ Compute ``f**n`` in ``GF(p)[x]/(g)`` using repeated squaring. Given polynomials ``f`` and ``g`` in ``GF(p)[x]`` and a non-negative integer ``n``, efficiently computes ``f**n (mod g)`` i.e. remainder from division ``f**n`` by ``g`` using repeated squaring algorithm. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_pow_mod >>> gf_pow_mod([3, 2, 4], 3, [1, 1], 5, ZZ) [] **References** 1. [Gathen99]_ """ if not n: return [K.one] elif n == 1: return gf_rem(f, g, p, K) elif n == 2: return gf_rem(gf_sqr(f, p, K), g, p, K) h = [K.one] while True: if n & 1: h = gf_mul(h, f, p, K) h = gf_rem(h, g, p, K) n -= 1 n >>= 1 if not n: break f = gf_sqr(f, p, K) f = gf_rem(f, g, p, K) return h def gf_gcd(f, g, p, K): """ Euclidean Algorithm in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_gcd >>> gf_gcd([3, 2, 4], [2, 2, 3], 5, ZZ) [1, 3] """ while g: f, g = g, gf_rem(f, g, p, K) return gf_monic(f, p, K)[1] def gf_lcm(f, g, p, K): """ Compute polynomial LCM in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_lcm >>> gf_lcm([3, 2, 4], [2, 2, 3], 5, ZZ) [1, 2, 0, 4] """ if not f or not g: return [] h = gf_quo(gf_mul(f, g, p, K), gf_gcd(f, g, p, K), p, K) return gf_monic(h, p, K)[1] def gf_cofactors(f, g, p, K): """ Compute polynomial GCD and cofactors in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_cofactors >>> gf_cofactors([3, 2, 4], [2, 2, 3], 5, ZZ) ([1, 3], [3, 3], [2, 1]) """ if not f and not g: return ([], [], []) h = gf_gcd(f, g, p, K) return (h, gf_quo(f, h, p, K), gf_quo(g, h, p, K)) def gf_gcdex(f, g, p, K): """ Extended Euclidean Algorithm in ``GF(p)[x]``. Given polynomials ``f`` and ``g`` in ``GF(p)[x]``, computes polynomials ``s``, ``t`` and ``h``, such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. The typical application of EEA is solving polynomial diophantine equations. Consider polynomials ``f = (x + 7) (x + 1)``, ``g = (x + 7) (x**2 + 1)`` in ``GF(11)[x]``. Application of Extended Euclidean Algorithm gives:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_gcdex, gf_mul, gf_add >>> s, t, g = gf_gcdex([1,8,7], [1,7,1,7], 11, ZZ) >>> s, t, g ([5, 6], [6], [1, 7]) As result we obtained polynomials ``s = 5*x + 6`` and ``t = 6``, and additionally ``gcd(f, g) = x + 7``. This is correct because:: >>> S = gf_mul(s, [1,8,7], 11, ZZ) >>> T = gf_mul(t, [1,7,1,7], 11, ZZ) >>> gf_add(S, T, 11, ZZ) == [1, 7] True **References** 1. [Gathen99]_ """ if not (f or g): return [K.one], [], [] p0, r0 = gf_monic(f, p, K) p1, r1 = gf_monic(g, p, K) if not f: return [], [K.invert(p1, p)], r1 if not g: return [K.invert(p0, p)], [], r0 s0, s1 = [K.invert(p0, p)], [] t0, t1 = [], [K.invert(p1, p)] while True: Q, R = gf_div(r0, r1, p, K) if not R: break (lc, r1), r0 = gf_monic(R, p, K), r1 inv = K.invert(lc, p) s = gf_sub_mul(s0, s1, Q, p, K) t = gf_sub_mul(t0, t1, Q, p, K) s1, s0 = gf_mul_ground(s, inv, p, K), s1 t1, t0 = gf_mul_ground(t, inv, p, K), t1 return s1, t1, r1 def gf_monic(f, p, K): """ Compute LC and a monic polynomial in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_monic >>> gf_monic([3, 2, 4], 5, ZZ) (3, [1, 4, 3]) """ if not f: return K.zero, [] else: lc = f[0] if K.is_one(lc): return lc, list(f) else: return lc, gf_quo_ground(f, lc, p, K) @cythonized("df,n") def gf_diff(f, p, K): """ Differentiate polynomial in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_diff >>> gf_diff([3, 2, 4], 5, ZZ) [1, 2] """ df = gf_degree(f) h, n = [K.zero]*df, df for coeff in f[:-1]: coeff *= K(n) coeff %= p if coeff: h[df-n] = coeff n -= 1 return gf_strip(h) def gf_eval(f, a, p, K): """ Evaluate ``f(a)`` in ``GF(p)`` using Horner scheme. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_eval >>> gf_eval([3, 2, 4], 2, 5, ZZ) 0 """ result = K.zero for c in f: result *= a result += c result %= p return result def gf_multi_eval(f, A, p, K): """ Evaluate ``f(a)`` for ``a`` in ``[a_1, ..., a_n]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_multi_eval >>> gf_multi_eval([3, 2, 4], [0, 1, 2, 3, 4], 5, ZZ) [4, 4, 0, 2, 0] """ return [ gf_eval(f, a, p, K) for a in A ] def gf_compose(f, g, p, K): """ Compute polynomial composition ``f(g)`` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_compose >>> gf_compose([3, 2, 4], [2, 2, 2], 5, ZZ) [2, 4, 0, 3, 0] """ if len(g) <= 1: return gf_strip([gf_eval(f, gf_LC(g, K), p, K)]) if not f: return [] h = [f[0]] for c in f[1:]: h = gf_mul(h, g, p, K) h = gf_add_ground(h, c, p, K) return h def gf_compose_mod(g, h, f, p, K): """ Compute polynomial composition ``g(h)`` in ``GF(p)[x]/(f)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_compose_mod >>> gf_compose_mod([3, 2, 4], [2, 2, 2], [4, 3], 5, ZZ) [4] """ if not g: return [] comp = [g[0]] for a in g[1:]: comp = gf_mul(comp, h, p, K) comp = gf_add_ground(comp, a, p, K) comp = gf_rem(comp, f, p, K) return comp @cythonized("n") def gf_trace_map(a, b, c, n, f, p, K): """ Compute polynomial trace map in ``GF(p)[x]/(f)``. Given a polynomial ``f`` in ``GF(p)[x]``, polynomials ``a``, ``b``, ``c`` in the quotient ring ``GF(p)[x]/(f)`` such that ``b = c**t (mod f)`` for some positive power ``t`` of ``p``, and a positive integer ``n``, returns a mapping:: a -> a**t**n, a + a**t + a**t**2 + ... + a**t**n (mod f) In factorization context, ``b = x**p mod f`` and ``c = x mod f``. This way we can efficiently compute trace polynomials in equal degree factorization routine, much faster than with other methods, like iterated Frobenius algorithm, for large degrees. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_trace_map >>> gf_trace_map([1, 2], [4, 4], [1, 1], 4, [3, 2, 4], 5, ZZ) ([1, 3], [1, 3]) **References** 1. [Gathen92]_ """ u = gf_compose_mod(a, b, f, p, K) v = b if n & 1: U = gf_add(a, u, p, K) V = b else: U = a V = c n >>= 1 while n: u = gf_add(u, gf_compose_mod(u, v, f, p, K), p, K) v = gf_compose_mod(v, v, f, p, K) if n & 1: U = gf_add(U, gf_compose_mod(u, V, f, p, K), p, K) V = gf_compose_mod(v, V, f, p, K) n >>= 1 return gf_compose_mod(a, V, f, p, K), U @cythonized("i,n") def gf_random(n, p, K): """ Generate a random polynomial in ``GF(p)[x]`` of degree ``n``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_random >>> gf_random(10, 5, ZZ) #doctest: +SKIP [1, 2, 3, 2, 1, 1, 1, 2, 0, 4, 2] """ return [K.one] + [ K(int(uniform(0, p))) for i in xrange(0, n) ] @cythonized("i,n") def gf_irreducible(n, p, K): """ Generate random irreducible polynomial of degree ``n`` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irreducible >>> gf_irreducible(10, 5, ZZ) #doctest: +SKIP [1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4] """ while True: f = gf_random(n, p, K) if gf_irreducible_p(f, p, K): return f @cythonized("i,n") def gf_irred_p_ben_or(f, p, K): """ Ben-Or's polynomial irreducibility test over finite fields. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irred_p_ben_or >>> gf_irred_p_ben_or([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4], 5, ZZ) True >>> gf_irred_p_ben_or([3, 2, 4], 5, ZZ) False """ n = gf_degree(f) if n <= 1: return True _, f = gf_monic(f, p, K) H = h = gf_pow_mod([K.one, K.zero], p, f, p, K) for i in xrange(0, n//2): g = gf_sub(h, [K.one, K.zero], p, K) if gf_gcd(f, g, p, K) == [K.one]: h = gf_compose_mod(h, H, f, p, K) else: return False return True @cythonized("i,n,d") def gf_irred_p_rabin(f, p, K): """ Rabin's polynomial irreducibility test over finite fields. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irred_p_rabin >>> gf_irred_p_rabin([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4], 5, ZZ) True >>> gf_irred_p_rabin([3, 2, 4], 5, ZZ) False """ n = gf_degree(f) if n <= 1: return True _, f = gf_monic(f, p, K) x = [K.one, K.zero] H = h = gf_pow_mod(x, p, f, p, K) indices = set([ n//d for d in factorint(n) ]) for i in xrange(1, n): if i in indices: g = gf_sub(h, x, p, K) if gf_gcd(f, g, p, K) != [K.one]: return False h = gf_compose_mod(h, H, f, p, K) return h == x _irred_methods = { 'ben-or' : gf_irred_p_ben_or, 'rabin' : gf_irred_p_rabin, } def gf_irreducible_p(f, p, K): """ Test irreducibility of a polynomial ``f`` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irreducible_p >>> gf_irreducible_p([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4], 5, ZZ) True >>> gf_irreducible_p([3, 2, 4], 5, ZZ) False """ method = query('GF_IRRED_METHOD') if method is not None: irred = _irred_methods[method](f, p, K) else: irred = gf_irred_p_rabin(f, p, K) return irred def gf_sqf_p(f, p, K): """ Return ``True`` if ``f`` is square-free in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sqf_p >>> gf_sqf_p([3, 2, 4], 5, ZZ) True >>> gf_sqf_p([2, 4, 4, 2, 2, 1, 4], 5, ZZ) False """ _, f = gf_monic(f, p, K) if not f: return True else: return gf_gcd(f, gf_diff(f, p, K), p, K) == [K.one] def gf_sqf_part(f, p, K): """ Return square-free part of a ``GF(p)[x]`` polynomial. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sqf_part >>> gf_sqf_part([1, 1, 3, 0, 1, 0, 2, 2, 1], 5, ZZ) [1, 4, 3] """ _, sqf = gf_sqf_list(f, p, K) g = [K.one] for f, _ in sqf: g = gf_mul(g, f, p, K) return g @cythonized("i,n,d,r") def gf_sqf_list(f, p, K, all=False): """ Return square-free decomposition of a ``GF(p)[x]`` polynomial. Given a polynomial ``f`` in ``GF(p)[x]``, returns the leading coefficient of ``f`` and a square-free decomposition ``f_1**e_1 f_2**e_2 ... f_k**e_k`` such that all ``f_i`` are monic polynomials and ``(f_i, f_j)`` for ``i != j`` are co-prime and ``e_1 ... e_k`` are given in increasing order. All trivial terms (i.e. ``f_i = 1``) aren't included in the output. Consider polynomial ``f = x**11 + 1`` over ``GF(11)[x]``:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import ( ... gf_from_dict, gf_diff, gf_sqf_list, gf_pow, ... ) ... # doctest: +NORMALIZE_WHITESPACE >>> f = gf_from_dict({11: 1, 0: 1}, 11, ZZ) Note that ``f'(x) = 0``:: >>> gf_diff(f, 11, ZZ) [] This phenomenon doesn't happen in characteristic zero. However we can still compute square-free decomposition of ``f`` using ``gf_sqf()``:: >>> gf_sqf_list(f, 11, ZZ) (1, [([1, 1], 11)]) We obtained factorization ``f = (x + 1)**11``. This is correct because:: >>> gf_pow([1, 1], 11, 11, ZZ) == f True **References** 1. [Geddes92]_ """ n, sqf, factors, r = 1, False, [], int(p) lc, f = gf_monic(f, p, K) if gf_degree(f) < 1: return lc, [] while True: F = gf_diff(f, p, K) if F != []: g = gf_gcd(f, F, p, K) h = gf_quo(f, g, p, K) i = 1 while h != [K.one]: G = gf_gcd(g, h, p, K) H = gf_quo(h, G, p, K) if gf_degree(H) > 0: factors.append((H, i*n)) g, h, i = gf_quo(g, G, p, K), G, i+1 if g == [K.one]: sqf = True else: f = g if not sqf: d = gf_degree(f) // r for i in xrange(0, d+1): f[i] = f[i*r] f, n = f[:d+1], n*r else: break if all: raise ValueError("'all=True' is not supported yet") return lc, factors @cythonized("n,i,j,r") def gf_Qmatrix(f, p, K): """ Calculate Berlekamp's ``Q`` matrix. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_Qmatrix >>> gf_Qmatrix([3, 2, 4], 5, ZZ) [[1, 0], [3, 4]] >>> gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ) [[1, 0, 0, 0], [0, 4, 0, 0], [0, 0, 1, 0], [0, 0, 0, 4]] """ n, r = gf_degree(f), int(p) q = [K.one] + [K.zero]*(n-1) Q = [list(q)] + [[]]*(n-1) for i in xrange(1, (n-1)*r + 1): qq, c = [(-q[-1]*f[-1]) % p], q[-1] for j in xrange(1, n): qq.append((q[j-1] - c*f[-j-1]) % p) if not (i % r): Q[i//r] = list(qq) q = qq return Q @cythonized("n,i,j,k") def gf_Qbasis(Q, p, K): """ Compute a basis of the kernel of ``Q``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_Qmatrix, gf_Qbasis >>> gf_Qbasis(gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ), 5, ZZ) [[1, 0, 0, 0], [0, 0, 1, 0]] >>> gf_Qbasis(gf_Qmatrix([3, 2, 4], 5, ZZ), 5, ZZ) [[1, 0]] """ Q, n = [ list(q) for q in Q ], len(Q) for k in xrange(0, n): Q[k][k] = (Q[k][k] - K.one) % p for k in xrange(0, n): for i in xrange(k, n): if Q[k][i]: break else: continue inv = K.invert(Q[k][i], p) for j in xrange(0, n): Q[j][i] = (Q[j][i]*inv) % p for j in xrange(0, n): t = Q[j][k] Q[j][k] = Q[j][i] Q[j][i] = t for i in xrange(0, n): if i != k: q = Q[k][i] for j in xrange(0, n): Q[j][i] = (Q[j][i] - Q[j][k]*q) % p for i in xrange(0, n): for j in xrange(0, n): if i == j: Q[i][j] = (K.one - Q[i][j]) % p else: Q[i][j] = (-Q[i][j]) % p basis = [] for q in Q: if any(q): basis.append(q) return basis @cythonized("i,k") def gf_berlekamp(f, p, K): """ Factor a square-free ``f`` in ``GF(p)[x]`` for small ``p``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_berlekamp >>> gf_berlekamp([1, 0, 0, 0, 1], 5, ZZ) [[1, 0, 2], [1, 0, 3]] """ Q = gf_Qmatrix(f, p, K) V = gf_Qbasis(Q, p, K) for i, v in enumerate(V): V[i] = gf_strip(list(reversed(v))) factors = [f] for k in xrange(1, len(V)): for f in list(factors): s = K.zero while s < p: g = gf_sub_ground(V[k], s, p, K) h = gf_gcd(f, g, p, K) if h != [K.one] and h != f: factors.remove(f) f = gf_quo(f, h, p, K) factors.extend([f, h]) if len(factors) == len(V): return _sort_factors(factors, multiple=False) s += K.one return _sort_factors(factors, multiple=False) @cythonized("i") def gf_ddf_zassenhaus(f, p, K): """ Cantor-Zassenhaus: Deterministic Distinct Degree Factorization Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes partial distinct degree factorization ``f_1 ... f_d`` of ``f`` where ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` is an argument to the equal degree factorization routine. Consider the polynomial ``x**15 - 1`` in ``GF(11)[x]``:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_from_dict >>> f = gf_from_dict({15: 1, 0: -1}, 11, ZZ) Distinct degree factorization gives:: >>> from sympy.polys.galoistools import gf_ddf_zassenhaus >>> gf_ddf_zassenhaus(f, 11, ZZ) [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] which means ``x**15 - 1 = (x**5 - 1) (x**10 + x**5 + 1)``. To obtain factorization into irreducibles, use equal degree factorization procedure (EDF) with each of the factors. **References** 1. [Gathen99]_ 2. [Geddes92]_ """ i, g, factors = 1, [K.one, K.zero], [] while 2*i <= gf_degree(f): g = gf_pow_mod(g, int(p), f, p, K) h = gf_gcd(f, gf_sub(g, [K.one, K.zero], p, K), p, K) if h != [K.one]: factors.append((h, i)) f = gf_quo(f, h, p, K) g = gf_rem(g, f, p, K) i += 1 if f != [K.one]: return factors + [(f, gf_degree(f))] else: return factors @cythonized("n,N,i") def gf_edf_zassenhaus(f, n, p, K): """ Cantor-Zassenhaus: Probabilistic Equal Degree Factorization Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and an integer ``n``, such that ``n`` divides ``deg(f)``, returns all irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``. EDF procedure gives complete factorization over Galois fields. Consider the square-free polynomial ``f = x**3 + x**2 + x + 1`` in ``GF(5)[x]``. Let's compute its irreducible factors of degree one:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_edf_zassenhaus >>> gf_edf_zassenhaus([1,1,1,1], 1, 5, ZZ) [[1, 1], [1, 2], [1, 3]] **References** 1. [Gathen99]_ 2. [Geddes92]_ """ factors, q = [f], int(p) if gf_degree(f) <= n: return factors N = gf_degree(f) // n while len(factors) < N: r = gf_random(2*n-1, p, K) if p == 2: h = r for i in xrange(0, 2**(n*N-1)): r = gf_pow_mod(r, 2, f, p, K) h = gf_add(h, r, p, K) g = gf_gcd(f, h, p, K) else: h = gf_pow_mod(r, (q**n-1) // 2, f, p, K) g = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) if g != [K.one] and g != f: factors = gf_edf_zassenhaus(g, n, p, K) \ + gf_edf_zassenhaus(gf_quo(f, g, p, K), n, p, K) return _sort_factors(factors, multiple=False) @cythonized("n,k,i,j") def gf_ddf_shoup(f, p, K): """ Kaltofen-Shoup: Deterministic Distinct Degree Factorization Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes partial distinct degree factorization ``f_1,...,f_d`` of ``f`` where ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` is an argument to the equal degree factorization routine. This algorithm is an improved version of Zassenhaus algorithm for large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_ddf_shoup, gf_from_dict >>> f = gf_from_dict({6: 1, 5: -1, 4: 1, 3: 1, 1: -1}, 3, ZZ) >>> gf_ddf_shoup(f, 3, ZZ) [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)] **References** 1. [Kaltofen98]_ 2. [Shoup95]_ 3. [Gathen92]_ """ n = gf_degree(f) k = int(ceil(sqrt(n//2))) h = gf_pow_mod([K.one, K.zero], int(p), f, p, K) U = [[K.one,K.zero], h] + [K.zero]*(k-1) for i in xrange(2, k+1): U[i] = gf_compose_mod(U[i-1], h, f, p, K) h, U = U[k], U[:k] V = [h] + [K.zero]*(k-1) for i in xrange(1, k): V[i] = gf_compose_mod(V[i-1], h, f, p, K) factors = [] for i, v in enumerate(V): h, j = [K.one], k-1 for u in U: g = gf_sub(v, u, p, K) h = gf_mul(h, g, p, K) h = gf_rem(h, f, p, K) g = gf_gcd(f, h, p, K) f = gf_quo(f, g, p, K) for u in reversed(U): h = gf_sub(v, u, p, K) F = gf_gcd(g, h, p, K) if F != [K.one]: factors.append((F, k*(i+1)-j)) g, j = gf_quo(g, F, p, K), j-1 if f != [K.one]: factors.append((f, gf_degree(f))) return factors @cythonized("n,N,q") def gf_edf_shoup(f, n, p, K): """ Gathen-Shoup: Probabilistic Equal Degree Factorization Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and integer ``n`` such that ``n`` divides ``deg(f)``, returns all irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``. This is a complete factorization over Galois fields. This algorithm is an improved version of Zassenhaus algorithm for large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_edf_shoup >>> gf_edf_shoup([1, 2837, 2277], 1, 2917, ZZ) [[1, 852], [1, 1985]] **References** 1. [Shoup91]_ 2. [Gathen92]_ """ N, q = gf_degree(f), int(p) if not N: return [] if N <= n: return [f] factors, x = [f], [K.one, K.zero] r = gf_random(N-1, p, K) h = gf_pow_mod(x, q, f, p, K) H = gf_trace_map(r, h, x, n-1, f, p, K)[1] if p == 2: h1 = gf_gcd(f, H, p, K) h2 = gf_quo(f, h1, p, K) factors = gf_edf_shoup(h1, n, p, K) \ + gf_edf_shoup(h2, n, p, K) else: h = gf_pow_mod(H, (q-1)//2, f, p, K) h1 = gf_gcd(f, h, p, K) h2 = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) h3 = gf_quo(f, gf_mul(h1, h2, p, K), p, K) factors = gf_edf_shoup(h1, n, p, K) \ + gf_edf_shoup(h2, n, p, K) \ + gf_edf_shoup(h3, n, p, K) return _sort_factors(factors, multiple=False) @cythonized("n") def gf_zassenhaus(f, p, K): """ Factor a square-free ``f`` in ``GF(p)[x]`` for medium ``p``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_zassenhaus >>> gf_zassenhaus([1, 4, 3], 5, ZZ) [[1, 1], [1, 3]] """ factors = [] for factor, n in gf_ddf_zassenhaus(f, p, K): factors += gf_edf_zassenhaus(factor, n, p, K) return _sort_factors(factors, multiple=False) @cythonized("n") def gf_shoup(f, p, K): """ Factor a square-free ``f`` in ``GF(p)[x]`` for large ``p``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_shoup >>> gf_shoup([1, 4, 3], 5, ZZ) [[1, 1], [1, 3]] """ factors = [] for factor, n in gf_ddf_shoup(f, p, K): factors += gf_edf_shoup(factor, n, p, K) return _sort_factors(factors, multiple=False) _factor_methods = { 'berlekamp' : gf_berlekamp, # ``p`` : small 'zassenhaus' : gf_zassenhaus, # ``p`` : medium 'shoup' : gf_shoup, # ``p`` : large } def gf_factor_sqf(f, p, K, method=None): """ Factor a square-free polynomial ``f`` in ``GF(p)[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_factor_sqf >>> gf_factor_sqf([3, 2, 4], 5, ZZ) (3, [[1, 1], [1, 3]]) """ lc, f = gf_monic(f, p, K) if gf_degree(f) < 1: return lc, [] method = method or query('GF_FACTOR_METHOD') if method is not None: factors = _factor_methods[method](f, p, K) else: factors = gf_zassenhaus(f, p, K) return lc, factors @cythonized("n") def gf_factor(f, p, K): """ Factor (non square-free) polynomials in ``GF(p)[x]``. Given a possibly non square-free polynomial ``f`` in ``GF(p)[x]``, returns its complete factorization into irreducibles:: f_1(x)**e_1 f_2(x)**e_2 ... f_d(x)**e_d where each ``f_i`` is a monic polynomial and ``gcd(f_i, f_j) == 1``, for ``i != j``. The result is given as a tuple consisting of the leading coefficient of ``f`` and a list of factors of ``f`` with their multiplicities. The algorithm proceeds by first computing square-free decomposition of ``f`` and then iteratively factoring each of square-free factors. Consider a non square-free polynomial ``f = (7*x + 1) (x + 2)**2`` in ``GF(11)[x]``. We obtain its factorization into irreducibles as follows:: >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_factor >>> gf_factor([5, 2, 7, 2], 11, ZZ) (5, [([1, 2], 1), ([1, 8], 2)]) We arrived with factorization ``f = 5 (x + 2) (x + 8)**2``. We didn't recover the exact form of the input polynomial because we requested to get monic factors of ``f`` and its leading coefficient separately. Square-free factors of ``f`` can be factored into irreducibles over ``GF(p)`` using three very different methods: Berlekamp efficient for very small values of ``p`` (usually ``p < 25``) Cantor-Zassenhaus efficient on average input and with "typical" ``p`` Shoup-Kaltofen-Gathen efficient with very large inputs and modulus If you want to use a specific factorization method, instead of the default one, set ``GF_FACTOR_METHOD`` with one of ``berlekamp``, ``zassenhaus`` or ``shoup`` values. **References** 1. [Gathen99]_ """ lc, f = gf_monic(f, p, K) if gf_degree(f) < 1: return lc, [] factors = [] for g, n in gf_sqf_list(f, p, K)[1]: for h in gf_factor_sqf(g, p, K)[1]: factors.append((h, n)) return lc, _sort_factors(factors) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/densebasic.py0000644000175000017500000010625012014170666024723 0ustar georgeskgeorgesk"""Basic tools for dense recursive polynomials in ``K[x]`` or ``K[X]``. """ from sympy.core import igcd, ilcm from sympy.polys.monomialtools import ( monomial_key, monomial_min, monomial_div ) from sympy.polys.groebnertools import sdp_sort from sympy.utilities import cythonized import random def poly_LC(f, K): """ Return leading coefficient of ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import poly_LC >>> poly_LC([], ZZ) 0 >>> poly_LC([ZZ(1), ZZ(2), ZZ(3)], ZZ) 1 """ if not f: return K.zero else: return f[0] def poly_TC(f, K): """ Return trailing coefficient of ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import poly_TC >>> poly_TC([], ZZ) 0 >>> poly_TC([ZZ(1), ZZ(2), ZZ(3)], ZZ) 3 """ if not f: return K.zero else: return f[-1] dup_LC = dmp_LC = poly_LC dup_TC = dmp_TC = poly_TC @cythonized("u") def dmp_ground_LC(f, u, K): """ Return ground leading coefficient. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_ground_LC >>> f = ZZ.map([[[1], [2, 3]]]) >>> dmp_ground_LC(f, 2, ZZ) 1 """ while u: f = dmp_LC(f, K) u -= 1 return dup_LC(f, K) @cythonized("u") def dmp_ground_TC(f, u, K): """ Return ground trailing coefficient. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_ground_TC >>> f = ZZ.map([[[1], [2, 3]]]) >>> dmp_ground_TC(f, 2, ZZ) 3 """ while u: f = dmp_TC(f, K) u -= 1 return dup_TC(f, K) @cythonized("u") def dmp_true_LT(f, u, K): """ Return leading term ``c * x_1**n_1 ... x_k**n_k``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_true_LT >>> f = ZZ.map([[4], [2, 0], [3, 0, 0]]) >>> dmp_true_LT(f, 1, ZZ) ((2, 0), 4) """ monom = [] while u: monom.append(len(f) - 1) f, u = f[0], u - 1 if not f: monom.append(0) else: monom.append(len(f) - 1) return tuple(monom), dup_LC(f, K) def dup_degree(f): """ Return leading degree of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_degree >>> f = ZZ.map([1, 2, 0, 3]) >>> dup_degree(f) 3 """ return len(f) - 1 @cythonized("u") def dmp_degree(f, u): """ Return leading degree of ``f`` in ``x_0`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_degree >>> dmp_degree([[[]]], 2) -1 >>> f = ZZ.map([[2], [1, 2, 3]]) >>> dmp_degree(f, 1) 1 """ if dmp_zero_p(f, u): return -1 else: return len(f) - 1 @cythonized("v,i,j") def _rec_degree_in(g, v, i, j): """Recursive helper function for :func:`dmp_degree_in`.""" if i == j: return dmp_degree(g, v) v, i = v-1, i+1 return max([ _rec_degree_in(c, v, i, j) for c in g ]) @cythonized("j,u") def dmp_degree_in(f, j, u): """ Return leading degree of ``f`` in ``x_j`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_degree_in >>> f = ZZ.map([[2], [1, 2, 3]]) >>> dmp_degree_in(f, 0, 1) 1 >>> dmp_degree_in(f, 1, 1) 2 """ if not j: return dmp_degree(f, u) if j < 0 or j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) return _rec_degree_in(f, u, 0, j) @cythonized("v,i") def _rec_degree_list(g, v, i, degs): """Recursive helper for :func:`dmp_degree_list`.""" degs[i] = max(degs[i], dmp_degree(g, v)) if v > 0: v, i = v-1, i+1 for c in g: _rec_degree_list(c, v, i, degs) @cythonized("u") def dmp_degree_list(f, u): """ Return a list of degrees of ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_degree_list >>> f = ZZ.map([[1], [1, 2, 3]]) >>> dmp_degree_list(f, 1) (1, 2) """ degs = [-1]*(u+1) _rec_degree_list(f, u, 0, degs) return tuple(degs) @cythonized("i") def dup_strip(f): """ Remove leading zeros from ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.densebasic import dup_strip >>> dup_strip([0, 0, 1, 2, 3, 0]) [1, 2, 3, 0] """ if not f or f[0]: return f i = 0 for cf in f: if cf: break else: i += 1 return f[i:] @cythonized("u,v,i") def dmp_strip(f, u): """ Remove leading zeros from ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.densebasic import dmp_strip >>> dmp_strip([[], [0, 1, 2], [1]], 1) [[0, 1, 2], [1]] """ if not u: return dup_strip(f) if dmp_zero_p(f, u): return f i, v = 0, u-1 for c in f: if not dmp_zero_p(c, v): break else: i += 1 if i == len(f): return dmp_zero(u) else: return f[i:] @cythonized("i,j") def _rec_validate(f, g, i, K): """Recursive helper for :func:`dmp_validate`.""" if type(g) is not list: if K is not None and not K.of_type(g): raise TypeError("%s in %s in not of type %s" % (g, f, K.dtype)) return set([i-1]) elif not g: return set([i]) else: j, levels = i+1, set([]) for c in g: levels |= _rec_validate(f, c, i+1, K) return levels @cythonized("v,w") def _rec_strip(g, v): """Recursive helper for :func:`_rec_strip`.""" if not v: return dup_strip(g) w = v-1 return dmp_strip([ _rec_strip(c, w) for c in g ], v) @cythonized("u") def dmp_validate(f, K=None): """ Return number of levels in ``f`` and recursively strips it. **Examples** >>> from sympy.polys.densebasic import dmp_validate >>> dmp_validate([[], [0, 1, 2], [1]]) ([[1, 2], [1]], 1) >>> dmp_validate([[1], 1]) Traceback (most recent call last): ... ValueError: invalid data structure for a multivariate polynomial """ levels = _rec_validate(f, f, 0, K) u = levels.pop() if not levels: return _rec_strip(f, u), u else: raise ValueError("invalid data structure for a multivariate polynomial") def dup_reverse(f): """ Compute ``x**n * f(1/x)``, i.e.: reverse ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_reverse >>> f = ZZ.map([1, 2, 3, 0]) >>> dup_reverse(f) [3, 2, 1] """ return dup_strip(list(reversed(f))) def dup_copy(f): """ Create a new copy of a polynomial ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_copy >>> f = ZZ.map([1, 2, 3, 0]) >>> dup_copy([1, 2, 3, 0]) [1, 2, 3, 0] """ return list(f) @cythonized("u,v") def dmp_copy(f, u): """ Create a new copy of a polynomial ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_copy >>> f = ZZ.map([[1], [1, 2]]) >>> dmp_copy(f, 1) [[1], [1, 2]] """ if not u: return list(f) v = u-1 return [ dmp_copy(c, v) for c in f ] def dup_to_tuple(f): """ Convert `f` into a tuple. This is needed for hashing. This is similar to dup_copy(). **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_copy >>> f = ZZ.map([1, 2, 3, 0]) >>> dup_copy([1, 2, 3, 0]) [1, 2, 3, 0] """ return tuple(f) @cythonized("u,v") def dmp_to_tuple(f, u): """ Convert `f` into a nested tuple of tuples. This is needed for hashing. This is similar to dmp_copy(). **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_to_tuple >>> f = ZZ.map([[1], [1, 2]]) >>> dmp_to_tuple(f, 1) ((1,), (1, 2)) """ if not u: return tuple(f) v = u - 1 return tuple(dmp_to_tuple(c, v) for c in f) def dup_normal(f, K): """ Normalize univariate polynomial in the given domain. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_normal >>> dup_normal([0, 1.5, 2, 3], ZZ) [1, 2, 3] """ return dup_strip([ K.normal(c) for c in f ]) @cythonized("u,v") def dmp_normal(f, u, K): """ Normalize multivariate polynomial in the given domain. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_normal >>> dmp_normal([[], [0, 1.5, 2]], 1, ZZ) [[1, 2]] """ if not u: return dup_normal(f, K) v = u-1 return dmp_strip([ dmp_normal(c, v, K) for c in f ], u) def dup_convert(f, K0, K1): """ Convert ground domain of ``f`` from ``K0`` to ``K1``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.polyclasses import DMP >>> from sympy.polys.densebasic import dup_convert >>> dup_convert([DMP([1], ZZ), DMP([2], ZZ)], ZZ['x'], ZZ) [1, 2] >>> dup_convert([ZZ(1), ZZ(2)], ZZ, ZZ['x']) [DMP([1], ZZ), DMP([2], ZZ)] """ if K0 is not None and K0 == K1: return f else: return dup_strip([ K1.convert(c, K0) for c in f ]) @cythonized("u,v") def dmp_convert(f, u, K0, K1): """ Convert ground domain of ``f`` from ``K0`` to ``K1``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.polyclasses import DMP >>> from sympy.polys.densebasic import dmp_convert >>> f = [[DMP([1], ZZ)], [DMP([2], ZZ)]] >>> g = [[ZZ(1)], [ZZ(2)]] >>> dmp_convert(f, 1, ZZ['x'], ZZ) [[1], [2]] >>> dmp_convert(g, 1, ZZ, ZZ['x']) [[DMP([1], ZZ)], [DMP([2], ZZ)]] """ if not u: return dup_convert(f, K0, K1) if K0 is not None and K0 == K1: return f v = u-1 return dmp_strip([ dmp_convert(c, v, K0, K1) for c in f ], u) def dup_from_sympy(f, K): """ Convert ground domain of ``f`` from SymPy to ``K``. **Examples** >>> from sympy import S >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_from_sympy >>> dup_from_sympy([S(1), S(2)], ZZ) == [ZZ(1), ZZ(2)] True """ return dup_strip([ K.from_sympy(c) for c in f ]) @cythonized("u,v") def dmp_from_sympy(f, u, K): """ Convert ground domain of ``f`` from SymPy to ``K``. **Examples** >>> from sympy import S >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_from_sympy >>> dmp_from_sympy([[S(1)], [S(2)]], 1, ZZ) == [[ZZ(1)], [ZZ(2)]] True """ if not u: return dup_from_sympy(f, K) v = u-1 return dmp_strip([ dmp_from_sympy(c, v, K) for c in f ], u) @cythonized("n") def dup_nth(f, n, K): """ Return ``n``-th coefficient of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_nth >>> f = ZZ.map([1, 2, 3]) >>> dup_nth(f, 0, ZZ) 3 >>> dup_nth(f, 4, ZZ) 0 """ if n < 0: raise IndexError("'n' must be non-negative, got %i" % n) elif n >= len(f): return K.zero else: return f[dup_degree(f)-n] @cythonized("n,u") def dmp_nth(f, n, u, K): """ Return ``n``-th coefficient of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_nth >>> f = ZZ.map([[1], [2], [3]]) >>> dmp_nth(f, 0, 1, ZZ) [3] >>> dmp_nth(f, 4, 1, ZZ) [] """ if n < 0: raise IndexError("'n' must be non-negative, got %i" % n) elif n >= len(f): return dmp_zero(u-1) else: return f[dmp_degree(f, u)-n] @cythonized("n,u,v") def dmp_ground_nth(f, N, u, K): """ Return ground ``n``-th coefficient of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_ground_nth >>> f = ZZ.map([[1], [2, 3]]) >>> dmp_ground_nth(f, (0, 1), 1, ZZ) 2 """ v = u for n in N: if n < 0: raise IndexError("`n` must be non-negative, got %i" % n) elif n >= len(f): return K.zero else: f, v = f[dmp_degree(f, v)-n], v-1 return f @cythonized("u") def dmp_zero_p(f, u): """ Return ``True`` if ``f`` is zero in ``K[X]``. **Examples** >>> from sympy.polys.densebasic import dmp_zero_p >>> dmp_zero_p([[[[[]]]]], 4) True >>> dmp_zero_p([[[[[1]]]]], 4) False """ while u: if len(f) != 1: return False f = f[0] u -= 1 return not f @cythonized("u") def dmp_zero(u): """ Return a multivariate zero. **Examples** >>> from sympy.polys.densebasic import dmp_zero >>> dmp_zero(4) [[[[[]]]]] """ r = [] for i in xrange(u): r = [r] return r @cythonized("u") def dmp_one_p(f, u, K): """ Return ``True`` if ``f`` is one in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_one_p >>> dmp_one_p([[[ZZ(1)]]], 2, ZZ) True """ return dmp_ground_p(f, K.one, u) @cythonized("u") def dmp_one(u, K): """ Return a multivariate one over ``K``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_one >>> dmp_one(2, ZZ) [[[1]]] """ return dmp_ground(K.one, u) @cythonized("u") def dmp_ground_p(f, c, u): """ Return True if ``f`` is constant in ``K[X]``. **Examples** >>> from sympy.polys.densebasic import dmp_ground_p >>> dmp_ground_p([[[3]]], 3, 2) True >>> dmp_ground_p([[[4]]], None, 2) True """ if c is not None and not c: return dmp_zero_p(f, u) while u: if len(f) != 1: return False f = f[0] u -= 1 if c is None: return len(f) <= 1 else: return f == [c] @cythonized("i,u") def dmp_ground(c, u): """ Return a multivariate constant. **Examples** >>> from sympy.polys.densebasic import dmp_ground >>> dmp_ground(3, 5) [[[[[[3]]]]]] >>> dmp_ground(1, -1) 1 """ if not c: return dmp_zero(u) for i in xrange(u+1): c = [c] return c @cythonized("n,u") def dmp_zeros(n, u, K): """ Return a list of multivariate zeros. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_zeros >>> dmp_zeros(3, 2, ZZ) [[[[]]], [[[]]], [[[]]]] >>> dmp_zeros(3, -1, ZZ) [0, 0, 0] """ if not n: return [] if u < 0: return [K.zero]*n else: return [ dmp_zero(u) for i in xrange(n) ] @cythonized("n,u") def dmp_grounds(c, n, u): """ Return a list of multivariate constants. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_grounds >>> dmp_grounds(ZZ(4), 3, 2) [[[[4]]], [[[4]]], [[[4]]]] >>> dmp_grounds(ZZ(4), 3, -1) [4, 4, 4] """ if not n: return [] if u < 0: return [c]*n else: return [ dmp_ground(c, u) for i in xrange(n) ] @cythonized("u") def dmp_negative_p(f, u, K): """ Return ``True`` if ``LC(f)`` is negative. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_negative_p >>> dmp_negative_p([[ZZ(1)], [-ZZ(1)]], 1, ZZ) False >>> dmp_negative_p([[-ZZ(1)], [ZZ(1)]], 1, ZZ) True """ return K.is_negative(dmp_ground_LC(f, u, K)) @cythonized("u") def dmp_positive_p(f, u, K): """ Return ``True`` if ``LC(f)`` is positive. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_positive_p >>> dmp_positive_p([[ZZ(1)], [-ZZ(1)]], 1, ZZ) True >>> dmp_positive_p([[-ZZ(1)], [ZZ(1)]], 1, ZZ) False """ return K.is_positive(dmp_ground_LC(f, u, K)) @cythonized("n,k") def dup_from_dict(f, K): """ Create ``K[x]`` polynomial from a ``dict``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_from_dict >>> dup_from_dict({(0,): ZZ(7), (2,): ZZ(5), (4,): ZZ(1)}, ZZ) [1, 0, 5, 0, 7] >>> dup_from_dict({}, ZZ) [] """ if not f: return [] n, h = max(f.iterkeys()), [] if type(n) is int: for k in xrange(n, -1, -1): h.append(f.get(k, K.zero)) else: (n,) = n for k in xrange(n, -1, -1): h.append(f.get((k,), K.zero)) return dup_strip(h) @cythonized("n,k") def dup_from_raw_dict(f, K): """ Create ``K[x]`` polynomial from a raw ``dict``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_from_raw_dict >>> dup_from_raw_dict({0: ZZ(7), 2: ZZ(5), 4: ZZ(1)}, ZZ) [1, 0, 5, 0, 7] """ if not f: return [] n, h = max(f.iterkeys()), [] for k in xrange(n, -1, -1): h.append(f.get(k, K.zero)) return dup_strip(h) @cythonized("u,v,n,k") def dmp_from_dict(f, u, K): """ Create ``K[X]`` polynomial from a ``dict``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_from_dict >>> dmp_from_dict({(0, 0): ZZ(3), (0, 1): ZZ(2), (2, 1): ZZ(1)}, 1, ZZ) [[1, 0], [], [2, 3]] >>> dmp_from_dict({}, 0, ZZ) [] """ if not u: return dup_from_dict(f, K) if not f: return dmp_zero(u) coeffs = {} for monom, coeff in f.iteritems(): head, tail = monom[0], monom[1:] if head in coeffs: coeffs[head][tail] = coeff else: coeffs[head] = { tail : coeff } n, v, h = max(coeffs.iterkeys()), u-1, [] for k in xrange(n, -1, -1): coeff = coeffs.get(k) if coeff is not None: h.append(dmp_from_dict(coeff, v, K)) else: h.append(dmp_zero(v)) return dmp_strip(h, u) @cythonized("n,k") def dup_to_dict(f, K=None, zero=False): """ Convert ``K[x]`` polynomial to a ``dict``. **Examples** >>> from sympy.polys.densebasic import dup_to_dict >>> dup_to_dict([1, 0, 5, 0, 7]) {(0,): 7, (2,): 5, (4,): 1} >>> dup_to_dict([]) {} """ if not f and zero: return {(0,): K.zero} n, result = dup_degree(f), {} for k in xrange(0, n+1): if f[n-k]: result[(k,)] = f[n-k] return result @cythonized("n,k") def dup_to_raw_dict(f, K=None, zero=False): """ Convert ``K[x]`` polynomial to a raw ``dict``. **Examples** >>> from sympy.polys.densebasic import dup_to_raw_dict >>> dup_to_raw_dict([1, 0, 5, 0, 7]) {0: 7, 2: 5, 4: 1} """ if not f and zero: return {0: K.zero} n, result = dup_degree(f), {} for k in xrange(0, n+1): if f[n-k]: result[k] = f[n-k] return result @cythonized("u,v,n,k") def dmp_to_dict(f, u, K=None, zero=False): """ Convert ``K[X]`` polynomial to a ``dict````. **Examples** >>> from sympy.polys.densebasic import dmp_to_dict >>> dmp_to_dict([[1, 0], [], [2, 3]], 1) {(0, 0): 3, (0, 1): 2, (2, 1): 1} >>> dmp_to_dict([], 0) {} """ if not u: return dup_to_dict(f, K, zero=zero) if dmp_zero_p(f, u) and zero: return {(0,)*(u + 1): K.zero} n, v, result = dmp_degree(f, u), u-1, {} for k in xrange(0, n+1): h = dmp_to_dict(f[n-k], v) for exp, coeff in h.iteritems(): result[(k,)+exp] = coeff return result @cythonized("u,i,j") def dmp_swap(f, i, j, u, K): """ Transform ``K[..x_i..x_j..]`` to ``K[..x_j..x_i..]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_swap >>> f = ZZ.map([[[2], [1, 0]], []]) >>> dmp_swap(f, 0, 1, 2, ZZ) [[[2], []], [[1, 0], []]] >>> dmp_swap(f, 1, 2, 2, ZZ) [[[1], [2, 0]], [[]]] >>> dmp_swap(f, 0, 2, 2, ZZ) [[[1, 0]], [[2, 0], []]] """ if i < 0 or j < 0 or i > u or j > u: raise IndexError("0 <= i < j <= %s expected" % u) elif i == j: return f F, H = dmp_to_dict(f, u), {} for exp, coeff in F.iteritems(): H[exp[:i] + (exp[j],) + exp[i+1:j] + (exp[i],) + exp[j+1:]] = coeff return dmp_from_dict(H, u, K) @cythonized("u") def dmp_permute(f, P, u, K): """ Return a polynomial in ``K[x_{P(1)},..,x_{P(n)}]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_permute >>> f = ZZ.map([[[2], [1, 0]], []]) >>> dmp_permute(f, [1, 0, 2], 2, ZZ) [[[2], []], [[1, 0], []]] >>> dmp_permute(f, [1, 2, 0], 2, ZZ) [[[1], []], [[2, 0], []]] """ F, H = dmp_to_dict(f, u), {} for exp, coeff in F.iteritems(): new_exp = [0]*len(exp) for e, p in zip(exp, P): new_exp[p] = e H[tuple(new_exp)] = coeff return dmp_from_dict(H, u, K) @cythonized("i,l") def dmp_nest(f, l, K): """ Return multivariate value nested ``l``-levels. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_nest >>> dmp_nest([[ZZ(1)]], 2, ZZ) [[[[1]]]] """ if not isinstance(f, list): return dmp_ground(f, l) for i in xrange(l): f = [f] return f @cythonized("l,k,u,v") def dmp_raise(f, l, u, K): """ Return multivariate polynomial raised ``l``-levels. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_raise >>> f = ZZ.map([[], [1, 2]]) >>> dmp_raise(f, 2, 1, ZZ) [[[[]]], [[[1]], [[2]]]] """ if not l: return f if not u: if not f: return dmp_zero(l) k = l-1 return [ dmp_ground(c, k) for c in f ] v = u-1 return [ dmp_raise(c, l, v, K) for c in f ] @cythonized("g,i") def dup_deflate(f, K): """ Map ``x**m`` to ``y`` in a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_deflate >>> f = ZZ.map([1, 0, 0, 1, 0, 0, 1]) >>> dup_deflate(f, ZZ) (3, [1, 1, 1]) """ if dup_degree(f) <= 0: return 1, f g = 0 for i in xrange(len(f)): if not f[-i-1]: continue g = igcd(g, i) if g == 1: return 1, f return g, f[::g] @cythonized("u,i,m,a,b") def dmp_deflate(f, u, K): """ Map ``x_i**m_i`` to ``y_i`` in a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_deflate >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) >>> dmp_deflate(f, 1, ZZ) ((2, 3), [[1, 2], [3, 4]]) """ if dmp_zero_p(f, u): return (1,)*(u+1), f F = dmp_to_dict(f, u) B = [0]*(u+1) for M in F.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([ b == 1 for b in B ]): return B, f H = {} for A, coeff in F.iteritems(): N = [ a // b for a, b in zip(A, B) ] H[tuple(N)] = coeff return B, dmp_from_dict(H, u, K) @cythonized("G,g,i") def dup_multi_deflate(polys, K): """ Map ``x**m`` to ``y`` in a set of polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_multi_deflate >>> f = ZZ.map([1, 0, 2, 0, 3]) >>> g = ZZ.map([4, 0, 0]) >>> dup_multi_deflate((f, g), ZZ) (2, ([1, 2, 3], [4, 0])) """ G = 0 for p in polys: if dup_degree(p) <= 0: return 1, polys g = 0 for i in xrange(len(p)): if not p[-i-1]: continue g = igcd(g, i) if g == 1: return 1, polys G = igcd(G, g) return G, tuple([ p[::G] for p in polys ]) @cythonized("u,G,g,m,a,b") def dmp_multi_deflate(polys, u, K): """ Map ``x_i**m_i`` to ``y_i`` in a set of polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_multi_deflate >>> f = ZZ.map([[1, 0, 0, 2], [], [3, 0, 0, 4]]) >>> g = ZZ.map([[1, 0, 2], [], [3, 0, 4]]) >>> dmp_multi_deflate((f, g), 1, ZZ) ((2, 1), ([[1, 0, 0, 2], [3, 0, 0, 4]], [[1, 0, 2], [3, 0, 4]])) """ if not u: M, H = dup_multi_deflate(polys, K) return (M,), H F, B = [], [0]*(u+1) for p in polys: f = dmp_to_dict(p, u) if not dmp_zero_p(p, u): for M in f.iterkeys(): for i, m in enumerate(M): B[i] = igcd(B[i], m) F.append(f) for i, b in enumerate(B): if not b: B[i] = 1 B = tuple(B) if all([ b == 1 for b in B ]): return B, polys H = [] for f in F: h = {} for A, coeff in f.iteritems(): N = [ a // b for a, b in zip(A, B) ] h[tuple(N)] = coeff H.append(dmp_from_dict(h, u, K)) return B, tuple(H) @cythonized("m") def dup_inflate(f, m, K): """ Map ``y`` to ``x**m`` in a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_inflate >>> f = ZZ.map([1, 1, 1]) >>> dup_inflate(f, 3, ZZ) [1, 0, 0, 1, 0, 0, 1] """ if m <= 0: raise IndexError("'m' must be positive, got %s" % m) if m == 1 or not f: return f result = [f[0]] for coeff in f[1:]: result.extend([K.zero]*(m-1)) result.append(coeff) return result @cythonized("u,v,i,j") def _rec_inflate(g, M, v, i, K): """Recursive helper for :func:`dmp_inflate`.""" if not v: return dup_inflate(g, M[i], K) if M[i] <= 0: raise IndexError("all M[i] must be positive, got %s" % M[i]) w, j = v-1, i+1 g = [ _rec_inflate(c, M, w, j, K) for c in g ] result = [g[0]] for coeff in g[1:]: for _ in xrange(1, M[i]): result.append(dmp_zero(w)) result.append(coeff) return result @cythonized("u,m") def dmp_inflate(f, M, u, K): """ Map ``y_i`` to ``x_i**k_i`` in a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_inflate >>> f = ZZ.map([[1, 2], [3, 4]]) >>> dmp_inflate(f, (2, 3), 1, ZZ) [[1, 0, 0, 2], [], [3, 0, 0, 4]] """ if not u: return dup_inflate(f, M[0], K) if all([ m == 1 for m in M ]): return f else: return _rec_inflate(f, M, u, 0, K) @cythonized("u,j") def dmp_exclude(f, u, K): """ Exclude useless levels from ``f``. Return the levels excluded, the new excluded ``f``, and the new ``u``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_exclude >>> f = ZZ.map([[[1]], [[1], [2]]]) >>> dmp_exclude(f, 2, ZZ) ([2], [[1], [1, 2]], 1) """ if not u or dmp_ground_p(f, None, u): return [], f, u J, F = [], dmp_to_dict(f, u) for j in xrange(0, u+1): for monom in F.iterkeys(): if monom[j]: break else: J.append(j) if not J: return [], f, u f = {} for monom, coeff in F.iteritems(): monom = list(monom) for j in reversed(J): del monom[j] f[tuple(monom)] = coeff u -= len(J) return J, dmp_from_dict(f, u, K), u @cythonized("u,j") def dmp_include(f, J, u, K): """ Include useless levels in ``f``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_include >>> f = ZZ.map([[1], [1, 2]]) >>> dmp_include(f, [2], 1, ZZ) [[[1]], [[1], [2]]] """ if not J: return f F, f = dmp_to_dict(f, u), {} for monom, coeff in F.iteritems(): monom = list(monom) for j in J: monom.insert(j, 0) f[tuple(monom)] = coeff u += len(J) return dmp_from_dict(f, u, K) @cythonized("u,v,w") def dmp_inject(f, u, K, front=False): """ Convert ``f`` from ``K[X][Y]`` to ``K[X,Y]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_inject >>> K = ZZ['x', 'y'] >>> dmp_inject([K([[1]]), K([[1], [2]])], 0, K) ([[[1]], [[1], [2]]], 2) >>> dmp_inject([K([[1]]), K([[1], [2]])], 0, K, front=True) ([[[1]], [[1, 2]]], 2) """ f, h = dmp_to_dict(f, u), {} v = len(K.gens) - 1 for f_monom, g in f.iteritems(): g = g.to_dict() for g_monom, c in g.iteritems(): if front: h[g_monom + f_monom] = c else: h[f_monom + g_monom] = c w = u + v + 1 return dmp_from_dict(h, w, K.dom), w @cythonized("u,v") def dmp_eject(f, u, K, front=False): """ Convert ``f`` from ``K[X,Y]`` to ``K[X][Y]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_eject >>> K = ZZ['x', 'y'] >>> dmp_eject([[[1]], [[1], [2]]], 2, K) [DMP([[1]], ZZ), DMP([[1], [2]], ZZ)] """ f, h = dmp_to_dict(f, u), {} v = u - len(K.gens) + 1 for monom, c in f.iteritems(): if front: g_monom, f_monom = monom[:v], monom[v:] else: f_monom, g_monom = monom[:v], monom[v:] if f_monom in h: h[f_monom][g_monom] = c else: h[f_monom] = {g_monom: c} for monom, c in h.iteritems(): h[monom] = K(c) return dmp_from_dict(h, v-1, K) @cythonized("i") def dup_terms_gcd(f, K): """ Remove GCD of terms from ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_terms_gcd >>> f = ZZ.map([1, 0, 1, 0, 0]) >>> dup_terms_gcd(f, ZZ) (2, [1, 0, 1]) """ if dup_TC(f, K) or not f: return 0, f i = 0 for c in reversed(f): if not c: i += 1 else: break return i, f[:-i] @cythonized("u,g") def dmp_terms_gcd(f, u, K): """ Remove GCD of terms from ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_terms_gcd >>> f = ZZ.map([[1, 0], [1, 0, 0], [], []]) >>> dmp_terms_gcd(f, 1, ZZ) ((2, 1), [[1], [1, 0]]) """ if dmp_ground_TC(f, u, K) or dmp_zero_p(f, u): return (0,)*(u+1), f F = dmp_to_dict(f, u) G = monomial_min(*F.keys()) if all([ g == 0 for g in G ]): return G, f f = {} for monom, coeff in F.iteritems(): f[monomial_div(monom, G)] = coeff return G, dmp_from_dict(f, u, K) @cythonized("v,w,d,i") def _rec_list_terms(g, v, monom): """Recursive helper for :func:`dmp_list_terms`.""" d, terms = dmp_degree(g, v), [] if not v: for i, c in enumerate(g): if not c: continue terms.append((monom + (d-i,), c)) else: w = v-1 for i, c in enumerate(g): terms.extend(_rec_list_terms(c, v-1, monom + (d-i,))) return terms @cythonized("u") def dmp_list_terms(f, u, K, order=None): """ List all non-zero terms from ``f`` in the given order ``order``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_list_terms >>> f = ZZ.map([[1, 1], [2, 3]]) >>> dmp_list_terms(f, 1, ZZ) [((1, 1), 1), ((1, 0), 1), ((0, 1), 2), ((0, 0), 3)] >>> dmp_list_terms(f, 1, ZZ, order='grevlex') [((1, 1), 1), ((0, 1), 2), ((1, 0), 1), ((0, 0), 3)] """ terms = _rec_list_terms(f, u, ()) if not terms: return [((0,)*(u+1), K.zero)] if order is None: return terms else: return sdp_sort(terms, monomial_key(order)) @cythonized("n,m") def dup_apply_pairs(f, g, h, args, K): """ Apply ``h`` to pairs of coefficients of ``f`` and ``g``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_apply_pairs >>> h = lambda x, y, z: 2*x + y - z >>> dup_apply_pairs([1, 2, 3], [3, 2, 1], h, (1,), ZZ) [4, 5, 6] """ n, m = len(f), len(g) if n != m: if n > m: g = [K.zero]*(n-m) + g else: f = [K.zero]*(m-n) + f result = [] for a, b in zip(f, g): result.append(h(a, b, *args)) return dup_strip(result) @cythonized("u,v,n,m") def dmp_apply_pairs(f, g, h, args, u, K): """ Apply ``h`` to pairs of coefficients of ``f`` and ``g``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dmp_apply_pairs >>> h = lambda x, y, z: 2*x + y - z >>> dmp_apply_pairs([[1], [2, 3]], [[3], [2, 1]], h, (1,), 1, ZZ) [[4], [5, 6]] """ if not u: return dup_apply_pairs(f, g, h, args, K) n, m, v = len(f), len(g), u-1 if n != m: if n > m: g = dmp_zeros(n-m, v, K) + g else: f = dmp_zeros(m-n, v, K) + f result = [] for a, b in zip(f, g): result.append(dmp_apply_pairs(a, b, h, args, v, K)) return dmp_strip(result, u) @cythonized("m,n,k,M,N") def dup_slice(f, m, n, K): """Take a continuous subsequence of terms of ``f`` in ``K[x]``. """ k = len(f) M = k - m N = k - n f = f[N:M] if not f: return [] else: return f + [K.zero]*m @cythonized("m,n,u") def dmp_slice(f, m, n, u, K): """Take a continuous subsequence of terms of ``f`` in ``K[X]``. """ return dmp_slice_in(f, m, n, 0, u, K) @cythonized("m,n,j,u,k") def dmp_slice_in(f, m, n, j, u, K): """Take a continuous subsequence of terms of ``f`` in ``x_j`` in ``K[X]``. """ if j < 0 or j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) if not u: return dup_slice(f, m, n, K) f, g = dmp_to_dict(f, u), {} for monom, coeff in f.iteritems(): k = monom[j] if k < m or k >= n: monom = monom[:j] + (0,) + monom[j+1:] if monom in g: g[monom] += coeff else: g[monom] = coeff return dmp_from_dict(g, u, K) def dup_random(n, a, b, K): """ Return a polynomial of degree ``n`` with coefficients in ``[a, b]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densebasic import dup_random >>> dup_random(3, -10, 10, ZZ) #doctest: +SKIP [-2, -8, 9, -4] """ f = [ K.convert(random.randint(a, b)) for _ in xrange(0, n+1) ] while not f[0]: f[0] = K.convert(random.randint(a, b)) return f wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyconfig.py0000644000175000017500000000246112014170666024773 0ustar georgeskgeorgesk"""Configuration utilities for polynomial manipulation algorithms. """ _default_config = { 'USE_COLLINS_RESULTANT' : False, 'USE_SIMPLIFY_GCD' : True, 'USE_HEU_GCD' : True, 'USE_IRREDUCIBLE_IN_FACTOR' : False, 'USE_CYCLOTOMIC_FACTOR' : True, 'EEZ_RESTART_IF_NEEDED' : True, 'EEZ_NUMBER_OF_CONFIGS' : 3, 'EEZ_NUMBER_OF_TRIES' : 5, 'EEZ_MODULUS_STEP' : 2, 'GF_IRRED_METHOD' : 'rabin', 'GF_FACTOR_METHOD' : 'zassenhaus', } _current_config = {} def setup(key, value=None): """Assign a value to (or reset) a configuration item. """ if value is not None: _current_config[key] = value else: _current_config[key] = _default_config[key] def query(key): """Ask for a value of the given configuration item. """ return _current_config.get(key, None) def configure(): """Initialized configuration of polys module. """ from os import getenv for key, default in _default_config.items(): value = getenv('SYMPY_' + key) if value is not None: try: _current_config[key] = eval(value) except NameError: _current_config[key] = value else: _current_config[key] = default configure() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/rootoftools.py0000644000175000017500000004536512014170666025225 0ustar georgeskgeorgesk"""Implementation of RootOf class and related tools. """ from sympy.core import S, Basic, Expr, Integer, Float, I, Add, Lambda, symbols, sympify from sympy.polys.polytools import Poly, PurePoly, factor from sympy.polys.rationaltools import together from sympy.polys.polyfuncs import symmetrize, viete from sympy.polys.rootisolation import ( dup_isolate_complex_roots_sqf, dup_isolate_real_roots_sqf) from sympy.polys.polyroots import ( roots_linear, roots_quadratic, roots_binomial, preprocess_roots) from sympy.polys.polyerrors import ( MultivariatePolynomialError, GeneratorsNeeded, PolynomialError) from sympy.polys.domains import QQ from sympy.mpmath import ( mp, mpf, mpc, findroot) from sympy.utilities import lambdify import operator def dup_minpoly_add(f, g, K): F = dmp_raise(f, 1, 0, K) G = dmp_raise(g, 1, 0, K) H = [[-K.one], [K.one, K.zero]] F = dmp_compose(F, H, 1, K) return dmp_resultant(F, G, 1, K) def dup_minpoly_sub(f, g, K): F = dmp_raise(f, 1, 0, K) G = dmp_raise(g, 1, 0, K) H = [[K.one], [K.one, K.zero]] F = dmp_compose(F, H, 1, K) return dmp_resultant(F, G, 1, K) def dup_minpoly_mul(f, g, K): f, F = reversed(f), [] for i, c in enumerate(f): if not c: F.append([]) else: F.append(dup_lshift([c], i, K)) F = dmp_strip(F) G = dmp_raise(g, 1, 0, K) return dmp_resultant(F, G, 1, K) def dup_minpoly_div(f, g, K): F = dmp_raise(f, 1, 0, K) G = dmp_raise(g, 1, 0, K) H = [[K.one, K.zero], []] F = dmp_compose(F, H, 1, K) return dmp_resultant(F, G, 1, K) def dup_minpoly_pow(f, p, q, K): d = {(p, 0): -K.one, (0, q): K.one} F = dmp_raise(f, 1, 0, K) G = dmp_from_dict(d, 1, K) return dmp_resultant(F, G, 1, K) _reals_cache = {} _complexes_cache = {} class RootOf(Expr): """Represents ``k``-th root of a univariate polynomial. """ __slots__ = ['poly', 'index'] def __new__(cls, f, x, index=None, radicals=True, expand=True): """Construct a new ``RootOf`` object for ``k``-th root of ``f``. """ x = sympify(x) if index is None and x.is_Integer: x, index = None, x else: index = sympify(index) if index.is_Integer: index = int(index) else: raise ValueError("expected an integer root index, got %d" % index) poly = PurePoly(f, x, greedy=False, expand=expand) if not poly.is_univariate: raise PolynomialError("only univariate polynomials are allowed") degree = poly.degree() if degree <= 0: raise PolynomialError("can't construct RootOf object for %s" % f) if index < -degree or index >= degree: raise IndexError("root index out of [%d, %d] range, got %d" % (-degree, degree-1, index)) elif index < 0: index += degree dom = poly.get_domain() if not dom.is_Exact: poly = poly.to_exact() roots = cls._roots_trivial(poly, radicals) if roots is not None: return roots[index] coeff, poly = preprocess_roots(poly) dom = poly.get_domain() if not dom.is_ZZ: raise NotImplementedError("RootOf is not supported over %s" % dom) root = cls._indexed_root(poly, index) return coeff*cls._postprocess_root(root, radicals) @classmethod def _new(cls, poly, index): """Construct new ``RootOf`` object from raw data. """ obj = Expr.__new__(cls) obj.poly = poly obj.index = index return obj def _hashable_content(self): return (self.poly, self.index) @property def expr(self): return self.poly.as_expr() @property def args(self): return (self.expr, Integer(self.index)) @property def free_symbols(self): return self.poly.free_symbols @property def is_commutative(self): return True @property def is_real(self): """Return ``True`` if the root is real. """ return self.index < len(_reals_cache[self.poly]) @property def is_complex(self): """Return ``True`` if the root is complex. """ return not self.is_real @classmethod def real_roots(cls, poly, radicals=True): """Get real roots of a polynomial. """ return cls._get_roots("_real_roots", poly, radicals) @classmethod def all_roots(cls, poly, radicals=True): """Get real and complex roots of a polynomial. """ return cls._get_roots("_all_roots", poly, radicals) @classmethod def _get_reals_sqf(cls, factor): """Compute real root isolating intervals for a square-free polynomial. """ if factor in _reals_cache: real_part = _reals_cache[factor] else: _reals_cache[factor] = real_part = \ dup_isolate_real_roots_sqf(factor.rep.rep, factor.rep.dom, blackbox=True) return real_part @classmethod def _get_complexes_sqf(cls, factor): """Compute complex root isolating intervals for a square-free polynomial. """ if factor in _complexes_cache: complex_part = _complexes_cache[factor] else: _complexes_cache[factor] = complex_part = \ dup_isolate_complex_roots_sqf(factor.rep.rep, factor.rep.dom, blackbox=True) return complex_part @classmethod def _get_reals(cls, factors): """Compute real root isolating intervals for a list of factors. """ reals = [] for factor, k in factors: real_part = cls._get_reals_sqf(factor) reals.extend([ (root, factor, k) for root in real_part ]) return reals @classmethod def _get_complexes(cls, factors): """Compute complex root isolating intervals for a list of factors. """ complexes = [] for factor, k in factors: complex_part = cls._get_complexes_sqf(factor) complexes.extend([ (root, factor, k) for root in complex_part ]) return complexes @classmethod def _reals_sorted(cls, reals): """Make real isolating intervals disjoint and sort roots. """ cache = {} for i, (u, f, k) in enumerate(reals): for j, (v, g, m) in enumerate(reals[i+1:]): u, v = u.refine_disjoint(v) reals[i+j+1] = (v, g, m) reals[i] = (u, f, k) reals = sorted(reals, key=lambda r: r[0].a) for root, factor, _ in reals: if factor in cache: cache[factor].append(root) else: cache[factor] = [root] for factor, roots in cache.iteritems(): _reals_cache[factor] = roots return reals @classmethod def _complexes_sorted(cls, complexes): """Make complex isolating intervals disjoint and sort roots. """ cache = {} for i, (u, f, k) in enumerate(complexes): for j, (v, g, m) in enumerate(complexes[i+1:]): u, v = u.refine_disjoint(v) complexes[i+j+1] = (v, g, m) complexes[i] = (u, f, k) complexes = sorted(complexes, key=lambda r: (r[0].ax, r[0].ay)) for root, factor, _ in complexes: if factor in cache: cache[factor].append(root) else: cache[factor] = [root] for factor, roots in cache.iteritems(): _complexes_cache[factor] = roots return complexes @classmethod def _reals_index(cls, reals, index): """Map initial real root index to an index in a factor where the root belongs. """ i = 0 for j, (_, factor, k) in enumerate(reals): if index < i + k: poly, index = factor, 0 for _, factor, _ in reals[:j]: if factor == poly: index += 1 return poly, index else: i += k @classmethod def _complexes_index(cls, complexes, index): """Map initial complex root index to an index in a factor where the root belongs. """ index, i = index, 0 for j, (_, factor, k) in enumerate(complexes): if index < i + k: poly, index = factor, 0 for _, factor, _ in complexes[:j]: if factor == poly: index += 1 index += len(_reals_cache[poly]) return poly, index else: i += k @classmethod def _count_roots(cls, roots): """Count the number of real or complex roots including multiplicites. """ return sum([ k for _, _, k in roots ]) @classmethod def _indexed_root(cls, poly, index): """Get a root of a composite polynomial by index. """ (_, factors) = poly.factor_list() reals = cls._get_reals(factors) reals_count = cls._count_roots(reals) if index < reals_count: reals = cls._reals_sorted(reals) return cls._reals_index(reals, index) else: complexes = cls._get_complexes(factors) complexes = cls._complexes_sorted(complexes) return cls._complexes_index(complexes, index-reals_count) @classmethod def _real_roots(cls, poly): """Get real roots of a composite polynomial. """ (_, factors) = poly.factor_list() reals = cls._get_reals(factors) reals = cls._reals_sorted(reals) reals_count = cls._count_roots(reals) roots = [] for index in xrange(0, reals_count): roots.append(cls._reals_index(reals, index)) return roots @classmethod def _all_roots(cls, poly): """Get real and complex roots of a composite polynomial. """ (_, factors) = poly.factor_list() reals = cls._get_reals(factors) reals = cls._reals_sorted(reals) reals_count = cls._count_roots(reals) roots = [] for index in xrange(0, reals_count): roots.append(cls._reals_index(reals, index)) complexes = cls._get_complexes(factors) complexes = cls._complexes_sorted(complexes) complexes_count = cls._count_roots(complexes) for index in xrange(0, complexes_count): roots.append(cls._complexes_index(complexes, index)) return roots @classmethod def _roots_trivial(cls, poly, radicals): """Compute roots in linear, quadratic and binomial cases. """ if poly.degree() == 1: return roots_linear(poly) if not radicals: return None if radicals and poly.degree() == 2: return roots_quadratic(poly) elif radicals and poly.length() == 2 and poly.TC(): return roots_binomial(poly) else: return None @classmethod def _preprocess_roots(cls, poly): """Take heroic measures to make ``poly`` compatible with ``RootOf``. """ dom = poly.get_domain() if not dom.is_Exact: poly = poly.to_exact() coeff, poly = preprocess_roots(poly) dom = poly.get_domain() if not dom.is_ZZ: raise NotImplementedError("RootOf is not supported over %s" % dom) return coeff, poly @classmethod def _postprocess_root(cls, root, radicals): """Return the root if it is trivial or a ``RootOf`` object. """ poly, index = root roots = cls._roots_trivial(poly, radicals) if roots is not None: return roots[index] else: return cls._new(poly, index) @classmethod def _get_roots(cls, method, poly, radicals): """Return postprocessed roots of specified kind. """ if not poly.is_univariate: raise PolynomialError("only univariate polynomials are allowed") coeff, poly = cls._preprocess_roots(poly) roots = [] for root in getattr(cls, method)(poly): roots.append(coeff*cls._postprocess_root(root, radicals)) return roots def _get_interval(self): """Internal function for retrieving isolation interval from cache. """ if self.is_real: return _reals_cache[self.poly][self.index] else: reals_count = len(_reals_cache[self.poly]) return _complexes_cache[self.poly][self.index - reals_count] def _set_interval(self, interval): """Internal function for updating isolation interval in cache. """ if self.is_real: _reals_cache[self.poly][self.index] = interval else: reals_count = len(_reals_cache[self.poly]) _complexes_cache[self.poly][self.index - reals_count] = interval def _eval_evalf(self, prec): """Evaluate this complex root to the given precision. """ _prec, mp.prec = mp.prec, prec try: func = lambdify(self.poly.gen, self.expr) interval = self._get_interval() refined = False while True: if self.is_real: x0 = mpf(str(interval.center)) else: x0 = mpc(*map(str, interval.center)) try: root = findroot(func, x0) except ValueError: interval = interval.refine() refined = True continue else: if refined: self._set_interval(interval) break finally: mp.prec = _prec return Float._new(root.real._mpf_, prec) + I*Float._new(root.imag._mpf_, prec) class RootSum(Expr): """Represents a sum of all roots of a univariate polynomial. """ __slots__ = ['poly', 'fun', 'auto'] def __new__(cls, expr, func=None, x=None, auto=True, quadratic=False): """Construct a new ``RootSum`` instance carrying all roots of a polynomial. """ coeff, poly = cls._transform(expr, x) if not poly.is_univariate: raise MultivariatePolynomialError("only univariate polynomials are allowed") if func is None: func = Lambda(poly.gen, poly.gen) else: try: is_func = func.is_Function except AttributeError: is_func = False if is_func and (func.nargs == 1 or 1 in func.nargs): if not isinstance(func, Lambda): func = Lambda(poly.gen, func(poly.gen)) else: raise ValueError("expected a univariate function, got %s" % func) var, expr = func.variables[0], func.expr if coeff is not S.One: expr = expr.subs(var, coeff*var) deg = poly.degree() if not expr.has(var): return deg*expr if expr.is_Add: add_const, expr = expr.as_independent(var) else: add_const = S.Zero if expr.is_Mul: mul_const, expr = expr.as_independent(var) else: mul_const = S.One func = Lambda(var, expr) rational = cls._is_func_rational(poly, func) (_, factors), terms = poly.factor_list(), [] for poly, k in factors: if poly.is_linear: term = func(roots_linear(poly)[0]) elif quadratic and poly.is_quadratic: term = sum(map(func, roots_quadratic(poly))) else: if not rational or not auto: term = cls._new(poly, func, auto) else: term = cls._rational_case(poly, func) terms.append(k*term) return mul_const*Add(*terms) + deg*add_const @classmethod def _new(cls, poly, func, auto=True): """Construct new raw ``RootSum`` instance. """ obj = Expr.__new__(cls) obj.poly = poly obj.fun = func obj.auto = auto return obj @classmethod def new(cls, poly, func, auto=True): """Construct new ``RootSum`` instance. """ if not func.expr.has(*func.variables): return func.expr rational = cls._is_func_rational(poly, func) if not rational or not auto: return cls._new(poly, func, auto) else: return cls._rational_case(poly, func) @classmethod def _transform(cls, expr, x): """Transform an expression to a polynomial. """ poly = PurePoly(expr, x, greedy=False) return preprocess_roots(poly) @classmethod def _is_func_rational(cls, poly, func): """Check if a lambda is areational function. """ var, expr = func.variables[0], func.expr return expr.is_rational_function(var) @classmethod def _rational_case(cls, poly, func): """Handle the rational function case. """ roots = symbols('r:%d' % poly.degree()) var, expr = func.variables[0], func.expr f = sum(expr.subs(var, r) for r in roots) p, q = together(f).as_numer_denom() domain = QQ[roots] p = p.expand() q = q.expand() try: p = Poly(p, domain=domain, expand=False) except GeneratorsNeeded: p, p_coeff = None, (p,) else: p_monom, p_coeff = zip(*p.terms()) try: q = Poly(q, domain=domain, expand=False) except GeneratorsNeeded: q, q_coeff = None, (q,) else: q_monom, q_coeff = zip(*q.terms()) coeffs, mapping = symmetrize(p_coeff + q_coeff, formal=True) formulas, values = viete(poly, roots), [] for (sym, _), (_, val) in zip(mapping, formulas): values.append((sym, val)) for i, (coeff, _) in enumerate(coeffs): coeffs[i] = coeff.subs(values) n = len(p_coeff) p_coeff = coeffs[:n] q_coeff = coeffs[n:] if p is not None: p = Poly(dict(zip(p_monom, p_coeff)), *p.gens).as_expr() else: (p,) = p_coeff if q is not None: q = Poly(dict(zip(q_monom, q_coeff)), *q.gens).as_expr() else: (q,) = q_coeff return factor(p/q) def _hashable_content(self): return (self.poly, self.fun) @property def expr(self): return self.poly.as_expr() @property def args(self): return (self.expr, self.fun, self.poly.gen) @property def free_symbols(self): return self.poly.free_symbols | self.fun.free_symbols @property def is_commutative(self): return True def doit(self, **hints): if hints.get('roots', True): return Add(*map(self.fun, self.poly.all_roots())) else: return self def _eval_derivative(self, x): var, expr = self.fun.args func = Lambda(var, expr.diff(x)) return self.new(self.poly, func, self.auto) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyroots.py0000644000175000017500000004351212014170666024676 0ustar georgeskgeorgesk"""Algorithms for computing symbolic roots of polynomials. """ from sympy.core.symbol import Dummy from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core import S, I, Basic from sympy.core.sympify import sympify from sympy.core.numbers import Rational, igcd from sympy.ntheory import divisors, isprime, nextprime from sympy.functions import exp, sqrt, re, im from sympy.polys.polytools import Poly, cancel, factor, gcd_list from sympy.polys.specialpolys import cyclotomic_poly from sympy.polys.polyerrors import PolynomialError, GeneratorsNeeded, DomainError from sympy.simplify import simplify from sympy.utilities import default_sort_key from sympy.core.compatibility import reduce import math def roots_linear(f): """Returns a list of roots of a linear polynomial.""" r = -f.nth(0)/f.nth(1) dom = f.get_domain() if not dom.is_Numerical: if dom.is_Composite: r = factor(r) else: r = simplify(r) return [r] def roots_quadratic(f): """Returns a list of roots of a quadratic polynomial.""" a, b, c = f.all_coeffs() dom = f.get_domain() def _simplify(expr): if dom.is_Composite: return factor(expr) else: return simplify(expr) if c is S.Zero: r0, r1 = S.Zero, -b/a if not dom.is_Numerical: r1 = _simplify(r1) elif b is S.Zero: r = -c/a if not dom.is_Numerical: R = sqrt(_simplify(r)) else: R = sqrt(r) r0 = R r1 = -R else: d = b**2 - 4*a*c if dom.is_Numerical: D = sqrt(d) r0 = (-b + D) / (2*a) r1 = (-b - D) / (2*a) else: D = sqrt(_simplify(d)) A = 2*a E = _simplify(-b/A) F = D/A r0 = E + F r1 = E - F return sorted([r0, r1], key=default_sort_key) def roots_cubic(f): """Returns a list of roots of a cubic polynomial.""" _, a, b, c = f.monic().all_coeffs() if c is S.Zero: x1, x2 = roots([1,a,b], multiple = True) return [x1, S.Zero, x2] p = b - a**2/3 q = c - a*b/3 + 2*a**3/27 pon3 = p/3 aon3 = a/3 if p is S.Zero: if q is S.Zero: return [-aon3]*3 else: u1 = q**Rational(1, 3) elif q is S.Zero: y1, y2 = roots([1, 0, p], multiple=True) return [tmp - aon3 for tmp in [y1, S.Zero, y2]] else: u1 = (q/2 + sqrt(q**2/4 + pon3**3))**Rational(1, 3) coeff = S.ImaginaryUnit*sqrt(3)/2 u2 = u1*(-S.Half + coeff) u3 = u1*(-S.Half - coeff) soln = [ -u1 + pon3/u1 - aon3, -u2 + pon3/u2 - aon3, -u3 + pon3/u3 - aon3 ] return soln def roots_quartic(f): r""" Returns a list of roots of a quartic polynomial. There are many references for solving quartic expressions available [1-5]. This reviewer has found that many of them require one to select from among 2 or more possible sets of solutions and that some solutions work when one is searching for real roots but don't work when searching for complex roots (though this is not always stated clearly). The following routine has been tested and found to be correct for 0, 2 or 4 complex roots. The quasisymmetric case solution [6] looks for quartics that have the form `x**4 + A*x**3 + B*x**2 + C*x + D = 0` where `(C/A)**2 = D`. Although there is a general solution, simpler results can be obtained for certain values of the coefficients. In all cases, 4 roots are returned: 1) `f = c + a*(a**2/8 - b/2) == 0` 2) `g = d - a*(a*(3*a**2/256 - b/16) + c/4) = 0` 3) if `f != 0` and `g != 0` and `p = -d + a*c/4 - b**2/12` then a) `p == 0` b) `p != 0` **Examples** >>> from sympy import Poly, symbols, I >>> from sympy.polys.polyroots import roots_quartic >>> r = roots_quartic(Poly('x**4-6*x**3+17*x**2-26*x+20')) >>> # 4 complex roots: 1+-I*sqrt(3), 2+-I >>> sorted(str(tmp.evalf(n=2)) for tmp in r) ['1.0 + 1.7*I', '1.0 - 1.7*I', '2.0 + 1.0*I', '2.0 - 1.0*I'] **References** 1. http://mathforum.org/dr.math/faq/faq.cubic.equations.html 2. http://en.wikipedia.org/wiki/Quartic_function#Summary_of_Ferrari.27s_method 3. http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html 4. http://staff.bath.ac.uk/masjhd/JHD-CA.pdf 5. http://www.albmath.org/files/Math_5713.pdf 6. http://www.statemaster.com/encyclopedia/Quartic-equation """ _, a, b, c, d = f.monic().all_coeffs() if not d: return [S.Zero] + roots([1, a, b, c], multiple=True) elif (c/a)**2 == d: x, m = f.gen, c/a g = Poly(x**2 + a*x + b - 2*m, x) z1, z2 = roots_quadratic(g) h1 = Poly(x**2 - z1*x + m, x) h2 = Poly(x**2 - z2*x + m, x) r1 = roots_quadratic(h1) r2 = roots_quadratic(h2) return r1 + r2 else: a2 = a**2 e = b - 3*a2/8 f = c + a*(a2/8 - b/2) g = d - a*(a*(3*a2/256 - b/16) + c/4) aon4 = a/4 ans = [] if f is S.Zero: y1, y2 = [tmp**S.Half for tmp in roots([1, e, g], multiple = True)] return [tmp - aon4 for tmp in [-y1, -y2, y1, y2]] if g is S.Zero: y = [S.Zero] + roots([1, 0, e, f], multiple = True) return [tmp - aon4 for tmp in y] else: p = -e**2/12 - g q = -e**3/108 + e*g/3 - f**2/8 TH = Rational(1, 3) if p is S.Zero: y = -5*e/6 - q**TH else: # with p !=0 then u below is not 0 root = sqrt(q**2/4 + p**3/27) r = -q/2 + root # or -q/2 - root u = r**TH # primary root of solve(x**3-r, x) y = -5*e/6 + u - p/u/3 w = sqrt(e + 2*y) arg1 = 3*e + 2*y arg2 = 2*f/w for s in [-1, 1]: root = sqrt(-(arg1 + s*arg2)) for t in [-1, 1]: ans.append((s*w - t*root)/2 - aon4) return ans def roots_binomial(f): """Returns a list of roots of a binomial polynomial.""" n = f.degree() a, b = f.nth(n), f.nth(0) alpha = (-cancel(b/a))**Rational(1, n) if alpha.is_number: alpha = alpha.expand(complex=True) roots, I = [], S.ImaginaryUnit for k in xrange(n): zeta = exp(2*k*S.Pi*I/n).expand(complex=True) roots.append((alpha*zeta).expand(power_base=False)) return sorted(roots, key=default_sort_key) def _inv_totient_estimate(m): """ Find ``(L, U)`` such that ``L <= phi^-1(m) <= U``. **Examples** >>> from sympy.polys.polyroots import _inv_totient_estimate >>> _inv_totient_estimate(192) (192, 840) >>> _inv_totient_estimate(400) (400, 1750) """ primes = [ d + 1 for d in divisors(m) if isprime(d + 1) ] a, b = 1, 1 for p in primes: a *= p b *= p - 1 L = m U = int(math.ceil(m*(float(a)/b))) P = p = 2 primes = [] while P <= U: p = nextprime(p) primes.append(p) P *= p P //= p b = 1 for p in primes[:-1]: b *= p - 1 U = int(math.ceil(m*(float(P)/b))) return L, U def roots_cyclotomic(f, factor=False): """Compute roots of cyclotomic polynomials. """ L, U = _inv_totient_estimate(f.degree()) for n in xrange(L, U+1): g = cyclotomic_poly(n, f.gen, polys=True) if f == g: break else: # pragma: no cover raise RuntimeError("failed to find index of a cyclotomic polynomial") roots = [] if not factor: for k in xrange(1, n+1): if igcd(k, n) == 1: roots.append(exp(2*k*S.Pi*I/n).expand(complex=True)) else: g = Poly(f, extension=(-1)**Rational(1, n)) for h, _ in g.factor_list()[1]: roots.append(-h.TC()) return sorted(roots, key=default_sort_key) def roots_rational(f): """Returns a list of rational roots of a polynomial.""" domain = f.get_domain() if domain.is_QQ: _, f = f.clear_denoms() elif domain.is_ZZ: f = f.set_domain('QQ') else: return [] LC_divs = divisors(int(f.LC())) EC_divs = divisors(int(f.EC())) if not f.eval(S.Zero): zeros = [S.Zero] else: zeros = [] for p in LC_divs: for q in EC_divs: zero = Rational(p, q) if not f.eval(zero): zeros.append(zero) if not f.eval(-zero): zeros.append(-zero) return sorted(zeros, key=default_sort_key) def _integer_basis(poly): """Compute coefficient basis for a polynomial over integers. """ monoms, coeffs = zip(*poly.terms()) monoms, = zip(*monoms) coeffs = map(abs, coeffs) if coeffs[0] < coeffs[-1]: coeffs = list(reversed(coeffs)) else: return None monoms = monoms[:-1] coeffs = coeffs[:-1] divs = reversed(divisors(gcd_list(coeffs))[1:]) try: div = divs.next() except StopIteration: return None while True: for monom, coeff in zip(monoms, coeffs): if coeff % div**monom != 0: try: div = divs.next() except StopIteration: return None else: break else: return div def preprocess_roots(poly): """Try to get rid of symbolic coefficients from ``poly``. """ coeff = S.One try: _, poly = poly.clear_denoms(convert=True) except DomainError: return coeff, poly poly = poly.primitive()[1] poly = poly.retract() if poly.get_domain().is_Poly and all(c.is_monomial for c in poly.rep.coeffs()): poly = poly.inject() strips = zip(*poly.monoms()) gens = list(poly.gens[1:]) base, strips = strips[0], strips[1:] for gen, strip in zip(list(gens), strips): reverse = False if strip[0] < strip[-1]: strip = reversed(strip) reverse = True ratio = None for a, b in zip(base, strip): if not a and not b: continue elif not a or not b: break elif b % a != 0: break else: _ratio = b // a if ratio is None: ratio = _ratio elif ratio != _ratio: break else: if reverse: ratio = -ratio poly = poly.eval(gen, 1) coeff *= gen**(-ratio) gens.remove(gen) if gens: poly = poly.eject(*gens) if poly.is_univariate and poly.get_domain().is_ZZ: basis = _integer_basis(poly) if basis is not None: n = poly.degree() def func(k, coeff): return coeff//basis**(n-k[0]) poly = poly.termwise(func) coeff *= basis return coeff, poly def roots(f, *gens, **flags): """ Computes symbolic roots of a univariate polynomial. Given a univariate polynomial f with symbolic coefficients (or a list of the polynomial's coefficients), returns a dictionary with its roots and their multiplicities. Only roots expressible via radicals will be returned. To get a complete set of roots use RootOf class or numerical methods instead. By default cubic and quartic formulas are used in the algorithm. To disable them because of unreadable output set ``cubics=False`` or ``quartics=False`` respectively. To get roots from a specific domain set the ``filter`` flag with one of the following specifiers: Z, Q, R, I, C. By default all roots are returned (this is equivalent to setting ``filter='C'``). By default a dictionary is returned giving a compact result in case of multiple roots. However to get a tuple containing all those roots set the ``multiple`` flag to True. **Examples** >>> from sympy import Poly, roots >>> from sympy.abc import x, y >>> roots(x**2 - 1, x) {-1: 1, 1: 1} >>> p = Poly(x**2-1, x) >>> roots(p) {-1: 1, 1: 1} >>> p = Poly(x**2-y, x, y) >>> roots(Poly(p, x)) {-y**(1/2): 1, y**(1/2): 1} >>> roots(x**2 - y, x) {-y**(1/2): 1, y**(1/2): 1} >>> roots([1, 0, -1]) {-1: 1, 1: 1} """ flags = dict(flags) auto = flags.pop('auto', True) cubics = flags.pop('cubics', True) quartics = flags.pop('quartics', True) multiple = flags.pop('multiple', False) filter = flags.pop('filter', None) predicate = flags.pop('predicate', None) if isinstance(f, list): if gens: raise ValueError('redundant generators given') x = Dummy('x') poly, i = {}, len(f)-1 for coeff in f: poly[i], i = sympify(coeff), i-1 f = Poly(poly, x, field=True) else: try: f = Poly(f, *gens, **flags) except GeneratorsNeeded: if multiple: return [] else: return {} if f.is_multivariate: raise PolynomialError('multivariate polynomials are not supported') def _update_dict(result, root, k): if root in result: result[root] += k else: result[root] = k def _try_decompose(f): """Find roots using functional decomposition. """ factors, roots = f.decompose(), [] for root in _try_heuristics(factors[0]): roots.append(root) for factor in factors[1:]: previous, roots = list(roots), [] for root in previous: g = factor - Poly(root, f.gen) for root in _try_heuristics(g): roots.append(root) return roots def _try_heuristics(f): """Find roots using formulas and some tricks. """ if f.is_ground: return [] if f.is_monomial: return [S(0)]*f.degree() if f.length() == 2: if f.degree() == 1: return map(cancel, roots_linear(f)) else: return roots_binomial(f) result = [] for i in [-1, 1]: if not f.eval(i): f = f.quo(Poly(f.gen - i, f.gen)) result.append(i) break n = f.degree() if n == 1: result += map(cancel, roots_linear(f)) elif n == 2: result += map(cancel, roots_quadratic(f)) elif f.is_cyclotomic: result += roots_cyclotomic(f) elif n == 3 and cubics: result += roots_cubic(f) elif n == 4 and quartics: result += roots_quartic(f) return result (k,), f = f.terms_gcd() if not k: zeros = {} else: zeros = {S(0) : k} coeff, f = preprocess_roots(f) if auto and f.get_domain().has_Ring: f = f.to_field() result = {} if not f.is_ground: if not f.get_domain().is_Exact: for r in f.nroots(): _update_dict(result, r, 1) elif f.degree() == 1: result[roots_linear(f)[0]] = 1 elif f.degree() == 2: for r in roots_quadratic(f): _update_dict(result, r, 1) elif f.length() == 2: for r in roots_binomial(f): _update_dict(result, r, 1) else: _, factors = Poly(f.as_expr()).factor_list() if len(factors) == 1 and factors[0][1] == 1: for root in _try_decompose(f): _update_dict(result, root, 1) else: for factor, k in factors: for r in _try_heuristics(Poly(factor, f.gen, field=True)): _update_dict(result, r, k) if coeff is not S.One: _result, result, = result, {} for root, k in _result.iteritems(): result[coeff*root] = k result.update(zeros) if filter not in [None, 'C']: handlers = { 'Z' : lambda r: r.is_Integer, 'Q' : lambda r: r.is_Rational, 'R' : lambda r: r.is_real, 'I' : lambda r: r.is_imaginary, } try: query = handlers[filter] except KeyError: raise ValueError("Invalid filter: %s" % filter) for zero in dict(result).iterkeys(): if not query(zero): del result[zero] if predicate is not None: for zero in dict(result).iterkeys(): if not predicate(zero): del result[zero] if not multiple: return result else: zeros = [] for zero, k in result.iteritems(): zeros.extend([zero]*k) return sorted(zeros, key=default_sort_key) def root_factors(f, *gens, **args): """ Returns all factors of a univariate polynomial. **Examples** >>> from sympy.abc import x, y >>> from sympy.polys.polyroots import root_factors >>> root_factors(x**2-y, x) [x - y**(1/2), x + y**(1/2)] """ args = dict(args) filter = args.pop('filter', None) F = Poly(f, *gens, **args) if not F.is_Poly: return [f] if F.is_multivariate: raise ValueError('multivariate polynomials not supported') x = F.gens[0] zeros = roots(F, filter=filter) if not zeros: factors = [F] else: factors, N = [], 0 for r, n in zeros.iteritems(): factors, N = factors + [Poly(x-r, x)]*n, N + n if N < F.degree(): G = reduce(lambda p,q: p*q, factors) factors.append(F.quo(G)) if not isinstance(f, Poly): return [ f.as_expr() for f in factors ] else: return factors wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/densepolys.py0000644000175000017500000004747412014170666025024 0ustar georgeskgeorgesk"""Object-oriented interface to dense polynomial representation. """ from sympy.polys.polyclasses import GenericPoly class DensePoly(GenericPoly): """Dense polynomial over an arbitrary domain. """ __slots__ = ['rep', 'lev', 'dom', '_hash'] def __init__(self, rep, dom, lev=None): if lev is None: rep, lev = dmp_validate(rep) self.rep = rep self.lev = lev self.dom = dom self._hash = None def __repr__(self): return "%s(%s, %s, %s)" % (self.__class__.__name__, self.rep, self.dom) def __hash__(self): _hash = self._hash if _hash is None: self._hash = _hash = hash((self.__class__.__name__, repr(self.rep), self.dom)) return _hash def __getstate__(self): return (self.rep, self.lev, self.dom, self._hash) def __getnewargs__(self): return (self.rep, self.lev, self.dom, self._hash) def unify(f, g): """Unify representations of two multivariate polynomials. """ if not hasattr(g, '__iter__'): if f.lev == g.lev and f.dom == g.dom: return f.lev, f.dom, f.per, f.rep, g.rep else: raise UnificationFailed("can't unify %s with %s" % (f, g)) else: lev, dom, reps = f.lev, f.dom, [] for gg in g: if gg.lev == lev and gg.dom == dom: reps.append(gg.rep) else: raise UnificationFailed("can't unify %s with %s" % (f, g)) return lev, dom, f.per, f.rep, reps def per(f, rep, dom=None, lower=False): """Create a dense polynomial out of the given representation. """ lev = f.lev if lower: if not lev: return rep else: lev -= 1 if dom is None: dom = f.dom return DensePoly(rep, dom, lev) @classmethod def zero(cls, lev, ord, dom): """Construct a zero-polynomial with appropriate properties. """ return cls(dmp_zero(lev), dom, lev) @classmethod def one(cls, lev, ord, dom): """Construct a one-polynomial with appropriate properties. """ return cls(dmp_one(lev, dom), dom, lev) @classmethod def from_ground(cls, rep, lev, ord, dom): """Create dense representation from an element of the ground domain. """ return cls(dmp_from_ground(rep, lev, dom), dom, lev) @classmethod def from_dict(cls, rep, lev, ord, dom): """Create dense representation from a ``dict`` with native coefficients. """ return cls(dmp_from_dict(rep, lev, dom), dom, lev) @classmethod def from_sympy_dict(cls, rep, lev, ord, dom): """Create dense representation from a ``dict`` with SymPy's coefficients. """ return cls(dmp_from_sympy_dict(rep, lev, dom), dom, lev) @classmethod def from_list(cls, rep, lev, ord, dom): """Create dense representation from a ``list`` with native coefficients. """ return cls(dmp_from_dict(rep, lev, dom), dom, lev) @classmethod def from_sympy_list(cls, rep, lev, ord, dom): """Create dense representation from a ``list`` with SymPy's coefficients. """ return cls(dmp_from_sympy_dict(rep, lev, dom), dom, lev) def to_ground(f): """Convert dense representation to an element of the ground domain. """ return dmp_to_ground(f.rep, f.lev, f.dom) def to_dict(f): """Convert dense representation to a ``dict`` with native coefficients. """ return dmp_to_dict(f.rep, f.lev, f.dom) def to_sympy_dict(f): """Convert dense representation to a ``dict`` with SymPy's coefficients. """ return dmp_to_sympy_dict(f.rep, f.lev, f.dom) def to_list(f): """Convert dense representation to a ``list`` with native coefficients. """ return dmp_to_dict(f.rep, f.lev, f.dom) def to_sympy_list(f): """Convert dense representation to a ``list`` with SymPy's coefficients. """ return dmp_to_sympy_dict(f.rep, f.lev, f.dom) def set_domain(f, dom): """Set the ground domain in `f` to ``dom``. """ if f.dom == dom: return f else: return f.per(dmp_set_domain(f.rep, f.lev, f.dom, dom), dom=dom) def ground_to_ring(f): """Make the ground domain a ring. """ return f.set_domain(f.dom.get_ring()) def ground_to_field(f): """Make the ground domain a field. """ return f.set_domain(f.dom.get_field()) def ground_to_exact(f): """Make the ground domain exact. """ return f.set_domain(f.dom.get_exact()) def LC(f): """Return the leading coefficient of `f`. """ return dmp_ground_LC(f.rep, f.lev, f.dom) def LM(f): """Return the leading monomial of `f`. """ return dmp_ground_LM(f.rep, f.lev, f.dom) def LT(f): """Return the leading term of `f`. """ return dmp_ground_LT(f.rep, f.lev, f.dom) def TC(f): """Return the trailing coefficient of `f`. """ return dmp_ground_TC(f.rep, f.lev, f.dom) def TM(f): """Return the trailing monomial of `f`. """ return dmp_ground_TM(f.rep, f.lev, f.dom) def TT(f): """Return the trailing coefficient of `f`. """ return dmp_ground_TT(f.rep, f.lev, f.dom) def EC(f): """Return the last non-zero coefficient of `f`. """ return dmp_ground_EC(f.rep, f.lev, f.dom) def EM(f): """Return the last non-zero monomial of `f`. """ return dmp_ground_EM(f.rep, f.lev, f.dom) def ET(f): """Return the last non-zero coefficient of `f`. """ return dmp_ground_ET(f.rep, f.lev, f.dom) def nth(f, *N): """Return `n`-th coefficient of `f`. """ return dmp_ground_nth(f.rep, N, f.lev, f.dom) def coeffs(f): """Return all non-zero coefficients of `f`. """ return dmp_coeffs(f.rep, f.lev, f.dom) def monoms(f): """Return all non-zero monomials of `f`. """ return dmp_monoms(f.rep, f.lev, f.dom) def terms(f): """Return all non-zero terms from `f`. """ return dmp_terms(f.rep, f.lev, f.dom) def all_coeffs(f): """Return all coefficients of `f`. """ return dmp_all_coeffs(f.rep, f.lev, f.dom) def all_monoms(f): """Return all monomials of `f`. """ return dmp_all_monoms(f.rep, f.lev, f.dom) def all_terms(f): """Return all terms of `f`. """ return dmp_all_terms(f.rep, f.lev, f.dom) def degree(f, j=0): """Return the degree of `f` in `x_j`. """ return dmp_degree_in(f.rep, j, f.lev) def degree_list(f): """Return the list of degrees of `f`. """ return dmp_degree_list(f.rep, f.lev) def total_degree(f): """Return the total degree of `f`. """ return dmp_total_degree(f.rep, f.lev) def deflate(f): """Reduce degree of `f` by mapping `x_i^m` to `y_i`. """ J, F = dmp_deflate(f.rep, f.lev, f.dom) return J, f.per(F) def inflate(f, M): """Revert :func:`deflate` by mapping `y_i` to `x_i^m`. """ return f.per(dmp_inflate(f.rep, M, f.lev, f.dom)) def terms_gcd(f): """Remove GCD of terms from the polynomial `f`. """ J, F = dmp_terms_gcd(f.rep, f.lev, f.dom) return J, f.per(F) def add_ground(f, c): """Add an element of the ground domain to `f`. """ return f.per(dmp_add_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def sub_ground(f, c): """Subtract an element of the ground domain from `f`. """ return f.per(dmp_sub_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def mul_ground(f, c): """Multiply `f` by an element of the ground domain. """ return f.per(dmp_mul_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def quo_ground(f, c): """Quotient of `f` by an element of the ground domain. """ return f.per(dmp_quo_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def exquo_ground(f, c): """Exact quotient of `f` by an element of the ground domain. """ return f.per(dmp_exquo_ground(f.rep, f.dom.convert(c), f.lev, f.dom)) def abs(f): """Make all coefficients in `f` positive. """ return f.per(dmp_abs(f.rep, f.lev, f.dom)) def neg(f): """Negate all coefficients in `f`. """ return f.per(dmp_neg(f.rep, f.lev, f.dom)) def add(f, g): """Add two multivariate polynomials `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_add(F, G, lev, dom)) def sub(f, g): """Subtract two multivariate polynomials `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_sub(F, G, lev, dom)) def mul(f, g): """Multiply two multivariate polynomials `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_mul(F, G, lev, dom)) def sqr(f): """Square a multivariate polynomial `f`. """ return f.per(dmp_sqr(f.rep, f.lev, f.dom)) def pow(f, n): """Raise `f` to a non-negative power `n`. """ return f.per(dmp_pow(f.rep, n, f.lev, f.dom)) def pdiv(f, g): """Polynomial pseudo-division of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) q, r = dmp_pdiv(F, G, lev, dom) return per(q), per(r) def prem(f, g): """Polynomial pseudo-remainder of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_prem(F, G, lev, dom)) def pquo(f, g): """Polynomial pseudo-quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_pquo(F, G, lev, dom)) def pexquo(f, g): """Polynomial exact pseudo-quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_pexquo(F, G, lev, dom)) def div(f, g): """Polynomial division with remainder of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) q, r = dmp_div(F, G, lev, dom) return per(q), per(r) def rem(f, g): """Compute polynomial remainder of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_rem(F, G, lev, dom)) def quo(f, g): """Compute polynomial quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_quo(F, G, lev, dom)) def exquo(f, g): """Compute polynomial exact quotient of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_exquo(F, G, lev, dom)) def reduced(f, G): """Reduce `f` modulo a set of polynomials `G`. """ lev, dom, per, f, G = f.unify(G) return per(dmp_reduced(f, G, lev, dom)) def max_norm(f): """Returns maximum norm of `f`. """ return dmp_max_norm(f.rep, f.lev, f.dom) def l1_norm(f): """Returns l1 norm of `f`. """ return dmp_l1_norm(f.rep, f.lev, f.dom) def clear_denoms(f, convert=False): """Clear denominators in `f`, but keep the ground domain. """ coeff, F = dmp_clear_denoms(f.rep, f.lev, f.dom, convert=convert) return coeff, f.per(F) def lift(f): """Convert algebraic coefficients to rationals. """ return f.per(dmp_lift(f.rep, f.lev, f.dom), dom=f.dom.dom) def half_gcdex(f, g): """Half extended Euclidean algorithm. """ lev, dom, per, F, G = f.unify(g) s, h = dmp_half_gcdex(F, G, dom) return per(s), per(h) def gcdex(f, g): """Extended Euclidean algorithm. """ lev, dom, per, F, G = f.unify(g) s, t, h = dmp_gcdex(F, G, lev, dom) return per(s), per(t), per(h) def invert(f, g): """Invert `f` modulo `g`, if possible. """ lev, dom, per, F, G = f.unify(g) return per(dmp_invert(F, G, lev, dom)) def subresultants(f, g): """Compute subresultant PRS sequence of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) R = dmp_subresultants(F, G, lev, dom) return map(per, R) def resultant(f, g): """Compute resultant of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_resultant(F, G, lev, dom), lower=True) def discriminant(f): """Compute discriminant of `f`. """ return f.per(dmp_discriminant(f.rep, f.lev, f.dom), lower=True) def cofactors(f, g): """Compute GCD of `f` and `g` and their cofactors. """ lev, dom, per, F, G = f.unify(g) h, cff, cfg = dmp_cofactors(F, G, lev, dom) return per(h), per(cff), per(cfg) def gcd(f, g): """Compute polynomial GCD of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_gcd(F, G, lev, dom)) def lcm(f, g): """Compute polynomial LCM of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_lcm(F, G, lev, dom)) def trunc(f, p): """Reduce `f` modulo an element of the ground domain. """ return f.per(dmp_ground_trunc(f.rep, f.dom.convert(p), f.lev, f.dom)) def monic(f): """Divide all coefficients by the leading coefficient of `f`. """ return f.per(dmp_ground_monic(f.rep, f.lev, f.dom)) def content(f): """Compute GCD of all coefficients of `f`. """ return dmp_ground_content(f.rep, f.lev, f.dom) def primitive(f): """Compute content and the primitive form of `f`. """ cont, F = dmp_ground_primitive(f.rep, f.lev, f.dom) return cont, f.per(F) def integrate(f, m=1, j=0): """Compute `m`-th order indefinite integral of `f` in `x_j`. """ return f.per(dmp_integrate_in(f.rep, m, j, f.lev, f.dom)) def diff(f, m=1, j=0): """Compute `m`-th order derivative of `f` in `x_j`. """ return f.per(dmp_diff_in(f.rep, m, j, f.lev, f.dom)) def eval(f, a, j=0): """Evaluate `f` at the given point `a` in `x_j`. """ return f.per(dmp_eval_in(f.rep, f.dom.convert(a), j, f.lev, f.dom), lower=True) def mirror(f, j=0): """Evaluate efficiently composition `f(-x_j)`. """ return f.per(dmp_mirror_in(f.rep, j, f.lev, f.dom)) def scale(f, a, j=0): """Evaluate efficiently composition `f(a x_j)`. """ return f.per(dmp_scale_in(f.rep, f.dom.convert(a), j, f.lev, f.dom)) def taylor(f, a, j=0): """Evaluate efficiently Taylor shift `f(x_j + a)`. """ return f.per(dmp_taylor_in(f.rep, f.dom.convert(a), j, f.lev, f.dom)) def transform(f, p, q, j=0): """Evaluate functional transformation `q^n \cdot f(p/q)`. """ lev, dom, per, F, (P, Q) = f.unify((p, q)) return per(dmp_transform_in(F, P, Q, j, lev, dom)) def compose(f, g): """Compute functional composition of `f` and `g`. """ lev, dom, per, F, G = f.unify(g) return per(dmp_compose(F, G, lev, dom)) def decompose(f): """Computes functional decomposition of `f`. """ return map(f.per, dmp_decompose(f.rep, f.lev, f.dom)) def sturm(f): """Computes the Sturm sequence of `f`. """ return map(f.per, dmp_sturm(f.rep, f.lev, f.dom)) def sqf_norm(f): """Computes square-free norm of `f`. """ s, g, r = dmp_sqf_norm(f.rep, f.lev, f.dom) return s, f.per(g), f.per(r, dom=f.dom.dom) def sqf_part(f): """Computes square-free part of `f`. """ return f.per(dmp_sqf_part(f.rep, f.lev, f.dom)) def sqf_list(f, all=False): """Returns a list of square-free factors of `f`. """ coeff, factors = dmp_sqf_list(f.rep, f.lev, f.dom, all=all) return coeff, [ (f.per(g), k) for g, k in factors ] def sqf_list_include(f, all=False): """Returns a list of square-free factors of `f`. """ factors = dmp_sqf_list_include(f.rep, f.lev, f.dom, all=all) return [ (f.per(g), k) for g, k in factors ] def factor_list(f): """Returns a list of irreducible factors of `f`. """ coeff, factors = dmp_factor_list(f.rep, f.lev, f.dom) return coeff, [ (f.per(g), k) for g, k in factors ] def factor_list_include(f): """Returns a list of irreducible factors of `f`. """ factors = dmp_factor_list_include(f.rep, f.lev, f.dom) return [ (f.per(g), k) for g, k in factors ] def real_intervals(f, eps=None, inf=None, sup=None, fast=False, sqf=False): """Compute isolating intervals for real roots of `f`. """ return dmp_real_intervals(f.rep, f.lev, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) def complex_intervals(f, eps=None, inf=None, sup=None, fast=False, sqf=False): """Compute isolating rectangles for complex roots of `f`. """ return dmp_complex_intervals(f.rep, f.lev, f.dom, eps=eps, inf=inf, sup=sup, fast=fast) def refine_real_root(f, s, t, eps=None, steps=None, fast=False): """Refine a real root isolating interval to the given precision. """ return dmp_refine_real_root(f.rep, s, t, f.lev, f.dom, eps=eps, steps=steps, fast=fast) def refine_complex_root(f, s, t, eps=None, steps=None, fast=False): """Refine a complex root isolating rectangle to the given precision. """ return dmp_refine_complex_root(f.rep, s, t, f.lev, f.dom, eps=eps, steps=steps, fast=fast) def count_real_roots(f, inf=None, sup=None): """Return the number of real roots of `f` in the ``[inf, sup]`` interval. """ return dmp_count_real_roots(f.rep, f.lev, f.dom, inf=inf, sup=sup) def count_complex_roots(f, inf=None, sup=None): """Return the number of complex roots of `f` in the ``[inf, sup]`` rectangle. """ return dmp_count_complex_roots(f.rep, f.lev, f.dom, inf=inf, sup=sup) @property def is_zero(f): """Returns ``True`` if `f` is equivalent to zero. """ return dmp_zero_p(f.rep, f.lev) @property def is_one(f): """Return ``True`` if `f` is equivalent to one. """ return dmp_one_p(f.rep, f.lev, f.dom) @property def is_ground(f): """Return ``True`` if `f` is an element of the ground domain. """ return dmp_ground_p(f.rep, f.lev) @property def is_sqf(f): """Return ``True`` if `f` is a square-free polynomial. """ return dmp_sqf_p(f.rep, f.lev, f.dom) @property def is_monic(f): """Return ``True`` if the leading coefficient of `f` is one. """ return dmp_monic_p(f.rep, f.lev, f.dom) @property def is_primitive(f): """Return ``True`` if GCD of coefficients of `f` is one. """ return dmp_primitive_p(f.rep, f.lev, f.dom) @property def is_linear(f): """Return ``True`` if `f` is linear in all its variables. """ return dmp_linear_p(f.rep, f.lev, f.dom) @property def is_homogeneous(f): """Return ``True`` if `f` has zero trailing coefficient. """ return dmp_homogeneous_p(f.rep, f.lev, f.dom) def __abs__(f): return f.abs() def __neg__(f): return f.neg() def __add__(f, g): if not isinstance(g, DensePoly): return f.add_ground(g) else: return f.add(g) def __radd__(f, g): return f.__add__(g) def __sub__(f, g): if not isinstance(g, DensePoly): return f.sub_ground(g) else: return f.sub(g) def __rsub__(f, g): return (-f).__add__(g) def __mul__(f, g): if not isinstance(g, DensePoly): return f.mul_ground(g) else: return f.mul(g) def __rmul__(f, g): return f.__mul__(g) def __pow__(f, n): return f.pow(n) def __divmod__(f, g): return f.div(g) def __mod__(f, g): return f.rem(g) def __floordiv__(f, g): if not isinstance(g, DensePoly): return f.exquo_ground(g) else: return f.exquo(g) def __eq__(f, g): return isinstance(g, DensePoly) and f.rep == g.rep def __ne__(f, g): return not f.__eq__(g) def __nonzero__(f): return not f.is_zero wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/numberfields.py0000644000175000017500000003524112014170666025303 0ustar georgeskgeorgesk"""Computational algebraic number field theory. """ from sympy import ( S, Expr, I, Integer, Rational, Float, Symbol, Add, Mul, sympify, Q, ask, Dummy, Tuple ) from sympy.polys.polytools import ( Poly, PurePoly, sqf_norm, invert, factor_list, groebner, ) from sympy.polys.polyutils import ( basic_from_dict, ) from sympy.polys.polyclasses import ( ANP, DMP, ) from sympy.polys.polyerrors import ( IsomorphismFailed, CoercionFailed, NotAlgebraic, ) from sympy.utilities import ( numbered_symbols, variations, lambdify, ) from sympy.ntheory import sieve from sympy.mpmath import pslq, mp def minimal_polynomial(ex, x=None, **args): """ Computes the minimal polynomial of an algebraic number. **Example** >>> from sympy import minimal_polynomial, sqrt >>> from sympy.abc import x >>> minimal_polynomial(sqrt(2), x) x**2 - 2 >>> minimal_polynomial(sqrt(2) + sqrt(3), x) x**4 - 10*x**2 + 1 """ generator = numbered_symbols('a', cls=Dummy) mapping, symbols, replace = {}, {}, [] ex = sympify(ex) if x is not None: x, cls = sympify(x), Poly else: x, cls = Dummy('x'), PurePoly def update_mapping(ex, exp, base=None): a = generator.next() symbols[ex] = a if base is not None: mapping[ex] = a**exp + base else: mapping[ex] = exp.as_expr(a) return a def bottom_up_scan(ex): if ex.is_Atom: if ex is S.ImaginaryUnit: if ex not in mapping: return update_mapping(ex, 2, 1) else: return symbols[ex] elif ex.is_Rational and ex.q != 0: return ex elif ex.is_Add: return Add(*[ bottom_up_scan(g) for g in ex.args ]) elif ex.is_Mul: return Mul(*[ bottom_up_scan(g) for g in ex.args ]) elif ex.is_Pow: if ex.exp.is_Rational: if ex.exp < 0 and ex.base.is_Add: coeff, terms = ex.base.as_coeff_add() elt, _ = primitive_element(terms, polys=True) alg = ex.base - coeff # XXX: turn this into eval() inverse = invert(elt.gen + coeff, elt).as_expr() base = inverse.subs(elt.gen, alg).expand() if ex.exp == -1: return bottom_up_scan(base) else: ex = base**(-ex.exp) if not ex.exp.is_Integer: base, exp = (ex.base**ex.exp.p).expand(), Rational(1, ex.exp.q) else: base, exp = ex.base, ex.exp base = bottom_up_scan(base) expr = base**exp if expr not in mapping: return update_mapping(expr, 1/exp, -base) else: return symbols[expr] elif ex.is_AlgebraicNumber: if ex.root not in mapping: return update_mapping(ex.root, ex.minpoly) else: return symbols[ex.root] raise NotAlgebraic("%s doesn't seem to be an algebraic number" % ex) polys = args.get('polys', False) if ex.is_AlgebraicNumber: if not polys: return ex.minpoly.as_expr(x) else: return ex.minpoly.replace(x) elif ex.is_Rational and ex.q != 0: result = ex.q*x - ex.p else: F = [x - bottom_up_scan(ex)] + mapping.values() G = groebner(F, symbols.values() + [x], order='lex') _, factors = factor_list(G[-1]) if len(factors) == 1: ((result, _),) = factors else: for result, _ in factors: if result.subs(x, ex).evalf(chop=True) == 0: break else: # pragma: no cover raise NotImplementedError("multiple candidates for the minimal polynomial of %s" % ex) if polys: return cls(result, x, field=True) else: return result minpoly = minimal_polynomial def _coeffs_generator(n): """Generate coefficients for `primitive_element()`. """ for coeffs in variations([1,-1], n, repetition=True): yield coeffs def primitive_element(extension, x=None, **args): """Construct a common number field for all extensions. """ if not extension: raise ValueError("can't compute primitive element for empty extension") if x is not None: x, cls = sympify(x), Poly else: x, cls = Dummy('x'), PurePoly if not args.get('ex', False): extension = [ AlgebraicNumber(ext, gen=x) for ext in extension ] g, coeffs = extension[0].minpoly.replace(x), [1] for ext in extension[1:]: s, _, g = sqf_norm(g, x, extension=ext) coeffs = [ s*c for c in coeffs ] + [1] if not args.get('polys', False): return g.as_expr(), coeffs else: return cls(g), coeffs generator = numbered_symbols('y', cls=Dummy) F, Y = [], [] for ext in extension: y = generator.next() if ext.is_Poly: if ext.is_univariate: f = ext.as_expr(y) else: raise ValueError("expected minimal polynomial, got %s" % ext) else: f = minpoly(ext, y) F.append(f) Y.append(y) coeffs_generator = args.get('coeffs', _coeffs_generator) for coeffs in coeffs_generator(len(Y)): f = x - sum([ c*y for c, y in zip(coeffs, Y)]) G = groebner(F + [f], Y + [x], order='lex', field=True) H, g = G[:-1], cls(G[-1], x, domain='QQ') for i, (h, y) in enumerate(zip(H, Y)): try: H[i] = Poly(y - h, x, domain='QQ').all_coeffs() # XXX: composite=False except CoercionFailed: # pragma: no cover break # G is not a triangular set else: break else: # pragma: no cover raise RuntimeError("run out of coefficient configurations") _, g = g.clear_denoms() if not args.get('polys', False): return g.as_expr(), coeffs, H else: return g, coeffs, H primelt = primitive_element def is_isomorphism_possible(a, b): """Returns `True` if there is a chance for isomorphism. """ n = a.minpoly.degree() m = b.minpoly.degree() if m % n != 0: return False if n == m: return True da = a.minpoly.discriminant() db = b.minpoly.discriminant() i, k, half = 1, m//n, db//2 while True: p = sieve[i] P = p**k if P > half: break if ((da % p) % 2) and not (db % P): return False i += 1 return True def field_isomorphism_pslq(a, b): """Construct field isomorphism using PSLQ algorithm. """ if not a.root.is_real or not b.root.is_real: raise NotImplementedError("PSLQ doesn't support complex coefficients") f = a.minpoly g = b.minpoly.replace(f.gen) n, m, prev = 100, b.minpoly.degree(), None for i in xrange(1, 5): A = a.root.evalf(n) B = b.root.evalf(n) basis = [1, B] + [ B**i for i in xrange(2, m) ] + [A] dps, mp.dps = mp.dps, n coeffs = pslq(basis, maxcoeff=int(1e10), maxsteps=1000) mp.dps = dps if coeffs is None: break if coeffs != prev: prev = coeffs else: break coeffs = [S(c)/coeffs[-1] for c in coeffs[:-1]] while not coeffs[-1]: coeffs.pop() coeffs = list(reversed(coeffs)) h = Poly(coeffs, f.gen, domain='QQ') if f.compose(h).rem(g).is_zero: d, approx = len(coeffs)-1, 0 for i, coeff in enumerate(coeffs): approx += coeff*B**(d-i) if A*approx < 0: return [ -c for c in coeffs ] else: return coeffs elif f.compose(-h).rem(g).is_zero: return [ -c for c in coeffs ] else: n *= 2 return None def field_isomorphism_factor(a, b): """Construct field isomorphism via factorization. """ _, factors = factor_list(a.minpoly, extension=b) for f, _ in factors: if f.degree() == 1: coeffs = f.rep.TC().to_sympy_list() d, terms = len(coeffs)-1, [] for i, coeff in enumerate(coeffs): terms.append(coeff*b.root**(d-i)) root = Add(*terms) if (a.root - root).evalf(chop=True) == 0: return coeffs if (a.root + root).evalf(chop=True) == 0: return [ -c for c in coeffs ] else: return None def field_isomorphism(a, b, **args): """Construct an isomorphism between two number fields. """ a, b = sympify(a), sympify(b) if not a.is_AlgebraicNumber: a = AlgebraicNumber(a) if not b.is_AlgebraicNumber: b = AlgebraicNumber(b) if a == b: return a.coeffs() n = a.minpoly.degree() m = b.minpoly.degree() if n == 1: return [a.root] if m % n != 0: return None if args.get('fast', True): try: result = field_isomorphism_pslq(a, b) if result is not None: return result except NotImplementedError: pass return field_isomorphism_factor(a, b) def to_number_field(extension, theta=None, **args): """Express `extension` in the field generated by `theta`. """ gen = args.get('gen') if hasattr(extension, '__iter__'): extension = list(extension) else: extension = [extension] if len(extension) == 1 and type(extension[0]) is tuple: return AlgebraicNumber(extension[0]) minpoly, coeffs = primitive_element(extension, gen, polys=True) root = sum([ coeff*ext for coeff, ext in zip(coeffs, extension) ]) if theta is None: return AlgebraicNumber((minpoly, root)) else: theta = sympify(theta) if not theta.is_AlgebraicNumber: theta = AlgebraicNumber(theta, gen=gen) coeffs = field_isomorphism(root, theta) if coeffs is not None: return AlgebraicNumber(theta, coeffs) else: raise IsomorphismFailed("%s is not in a subfield of %s" % (root, theta.root)) class AlgebraicNumber(Expr): """Class for representing algebraic numbers in SymPy. """ __slots__ = ['rep', 'root', 'alias', 'minpoly'] is_AlgebraicNumber = True def __new__(cls, expr, coeffs=None, **args): """Construct a new algebraic number. """ expr = sympify(expr) if isinstance(expr, (tuple, Tuple)): minpoly, root = expr if not minpoly.is_Poly: minpoly = Poly(minpoly) elif expr.is_AlgebraicNumber: minpoly, root = expr.minpoly, expr.root else: minpoly, root = minimal_polynomial(expr, args.get('gen'), polys=True), expr dom = minpoly.get_domain() if coeffs is not None: if not isinstance(coeffs, ANP): rep = DMP.from_sympy_list(sympify(coeffs), 0, dom) else: rep = DMP.from_list(coeffs.to_list(), 0, dom) if rep.degree() >= minpoly.degree(): rep = rep.rem(minpoly.rep) else: rep = DMP.from_list([1, 0], 0, dom) if ask(Q.negative(root)): rep = -rep alias = args.get('alias') if alias is not None: if not isinstance(alias, Symbol): alias = Symbol(alias) obj = Expr.__new__(cls) obj.rep = rep obj.root = root obj.alias = alias obj.minpoly = minpoly return obj def __eq__(a, b): if not b.is_AlgebraicNumber: try: b = to_number_field(b, a) except (NotAlgebraic, IsomorphismFailed): return False return a.rep == b.rep and \ a.minpoly.all_coeffs() == b.minpoly.all_coeffs() def __ne__(a, b): return not a.__eq__(b) def __hash__(self): return super(AlgebraicNumber, self).__hash__() def _eval_evalf(self, prec): return self.as_expr()._evalf(prec) @property def is_aliased(self): """Returns `True` if `alias` was set. """ return self.alias is not None def as_poly(self, x=None): """Create a Poly instance from `self`. """ if x is not None: return Poly.new(self.rep, x) else: if self.alias is not None: return Poly.new(self.rep, self.alias) else: return PurePoly.new(self.rep, Dummy('x')) def as_expr(self, x=None): """Create a Basic expression from `self`. """ return self.as_poly(x or self.root).as_expr().expand() def coeffs(self): """Returns all SymPy coefficients of an algebraic number. """ return [ self.rep.dom.to_sympy(c) for c in self.rep.all_coeffs() ] def native_coeffs(self): """Returns all native coefficients of an algebraic number. """ return self.rep.all_coeffs() def to_algebraic_integer(self): """Convert `self` to an algebraic integer. """ f = self.minpoly if f.LC() == 1: return self coeff = f.LC()**(f.degree()-1) poly = f.compose(Poly(f.gen/f.LC())) minpoly = poly*coeff root = f.LC()*self.root return AlgebraicNumber((minpoly, root), self.coeffs()) def isolate(alg, eps=None, fast=False): """Give a rational isolating interval for an algebraic number. """ alg = sympify(alg) if alg.is_Rational: return (alg, alg) elif not ask(Q.real(alg)): raise NotImplementedError("complex algebraic numbers are not supported") from sympy.printing.lambdarepr import LambdaPrinter class IntervalPrinter(LambdaPrinter): """Use ``lambda`` printer but print numbers as ``mpi`` intervals. """ def _print_Integer(self, expr): return "mpi('%s')" % super(IntervalPrinter, self)._print_Integer(expr) def _print_Rational(self, expr): return "mpi('%s')" % super(IntervalPrinter, self)._print_Rational(expr) func = lambdify((), alg, modules="mpmath", printer=IntervalPrinter()) poly = minpoly(alg, polys=True) intervals = poly.intervals(sqf=True) dps, done = mp.dps, False try: while not done: alg = func() for a, b in intervals: if a <= alg.a and alg.b <= b: done = True break else: mp.dps *= 2 finally: mp.dps = dps if eps is not None: a, b = poly.refine_root(a, b, eps=eps, fast=fast) return (a, b) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/factortools.py0000644000175000017500000007772112014170666025174 0ustar georgeskgeorgesk"""Polynomial factorization routines in characteristic zero. """ from sympy.polys.galoistools import ( gf_from_int_poly, gf_to_int_poly, gf_degree, gf_from_dict, gf_lshift, gf_add_mul, gf_mul, gf_div, gf_rem, gf_gcd, gf_gcdex, gf_sqf_p, gf_factor_sqf, gf_factor) from sympy.polys.densebasic import ( dup_LC, dmp_LC, dmp_ground_LC, dup_TC, dmp_TC, dmp_ground_TC, dup_convert, dmp_convert, dup_degree, dmp_degree, dmp_degree_in, dmp_degree_list, dup_from_dict, dmp_from_dict, dmp_zero, dmp_zero_p, dmp_one, dmp_one_p, dmp_nest, dmp_raise, dup_strip, dmp_strip, dmp_ground, dup_inflate, dmp_exclude, dmp_include, dmp_inject, dmp_eject, dup_terms_gcd, dmp_terms_gcd) from sympy.polys.densearith import ( dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_sqr, dmp_sqr, dup_pow, dmp_pow, dup_div, dmp_div, dup_rem, dmp_rem, dup_quo, dmp_quo, dup_expand, dmp_expand, dup_add_mul, dmp_add_mul, dup_sub_mul, dmp_sub_mul, dup_lshift, dup_rshift, dup_max_norm, dmp_max_norm, dup_l1_norm, dmp_l1_norm, dup_mul_ground, dmp_mul_ground, dup_quo_ground, dmp_quo_ground) from sympy.polys.densetools import ( dup_clear_denoms, dmp_clear_denoms, dup_trunc, dmp_ground_trunc, dup_content, dmp_ground_content, dup_monic, dmp_ground_monic, dup_primitive, dmp_ground_primitive, dup_eval, dmp_eval_tail, dmp_eval_in, dmp_diff_eval_in, dup_compose, dmp_compose, dup_shift, dup_mirror) from sympy.polys.euclidtools import ( dmp_primitive, dup_gcd, dmp_gcd, dup_inner_gcd, dmp_inner_gcd) from sympy.polys.sqfreetools import ( dup_sqf_p, dmp_sqf_p, dup_sqf_norm, dmp_sqf_norm, dup_sqf_part, dmp_sqf_part) from sympy.polys.polyutils import _sort_factors from sympy.polys.polyconfig import query from sympy.polys.polyerrors import ( ExtraneousFactors, DomainError, CoercionFailed, EvaluationFailed) from sympy.ntheory import nextprime, isprime, factorint from sympy.utilities import subsets, cythonized from math import ceil, log from random import randint @cythonized("k") def dup_trial_division(f, factors, K): """Determine multiplicities of factors using trial division. """ result = [] for factor in factors: k = 0 while True: q, r = dup_div(f, factor, K) if not r: f, k = q, k+1 else: break result.append((factor, k)) return _sort_factors(result) @cythonized("u,k") def dmp_trial_division(f, factors, u, K): """Determine multiplicities of factors using trial division. """ result = [] for factor in factors: k = 0 while True: q, r = dmp_div(f, factor, u, K) if dmp_zero_p(r, u): f, k = q, k+1 else: break result.append((factor, k)) return _sort_factors(result) def dup_zz_mignotte_bound(f, K): """Mignotte bound for univariate polynomials in `K[x]`. """ a = dup_max_norm(f, K) b = abs(dup_LC(f, K)) n = dup_degree(f) return K.sqrt(K(n+1))*2**n*a*b def dmp_zz_mignotte_bound(f, u, K): """Mignotte bound for multivariate polynomials in `K[X]`. """ a = dmp_max_norm(f, u, K) b = abs(dmp_ground_LC(f, u, K)) n = sum(dmp_degree_list(f, u)) return K.sqrt(K(n+1))*2**n*a*b def dup_zz_hensel_step(m, f, g, h, s, t, K): """ One step in Hensel lifting in `Z[x]`. Given positive integer `m` and `Z[x]` polynomials `f`, `g`, `h`, `s` and `t` such that:: f == g*h (mod m) s*g + t*h == 1 (mod m) lc(f) is not a zero divisor (mod m) lc(h) == 1 deg(f) == deg(g) + deg(h) deg(s) < deg(h) deg(t) < deg(g) returns polynomials `G`, `H`, `S` and `T`, such that:: f == G*H (mod m**2) S*G + T**H == 1 (mod m**2) **References** 1. [Gathen99]_ """ M = m**2 e = dup_sub_mul(f, g, h, K) e = dup_trunc(e, M, K) q, r = dup_div(dup_mul(s, e, K), h, K) q = dup_trunc(q, M, K) r = dup_trunc(r, M, K) u = dup_add(dup_mul(t, e, K), dup_mul(q, g, K), K) G = dup_trunc(dup_add(g, u, K), M, K) H = dup_trunc(dup_add(h, r, K), M, K) u = dup_add(dup_mul(s, G, K), dup_mul(t, H, K), K) b = dup_trunc(dup_sub(u, [K.one], K), M, K) c, d = dup_div(dup_mul(s, b, K), H, K) c = dup_trunc(c, M, K) d = dup_trunc(d, M, K) u = dup_add(dup_mul(t, b, K), dup_mul(c, G, K), K) S = dup_trunc(dup_sub(s, d, K), M, K) T = dup_trunc(dup_sub(t, u, K), M, K) return G, H, S, T @cythonized("l,r,k,d") def dup_zz_hensel_lift(p, f, f_list, l, K): """ Multifactor Hensel lifting in `Z[x]`. Given a prime `p`, polynomial `f` over `Z[x]` such that `lc(f)` is a unit modulo `p`, monic pair-wise coprime polynomials `f_i` over `Z[x]` satisfying:: f = lc(f) f_1 ... f_r (mod p) and a positive integer `l`, returns a list of monic polynomials `F_1`, `F_2`, ..., `F_r` satisfying:: f = lc(f) F_1 ... F_r (mod p**l) F_i = f_i (mod p), i = 1..r **References** 1. [Gathen99]_ """ r = len(f_list) lc = dup_LC(f, K) if r == 1: F = dup_mul_ground(f, K.gcdex(lc, p**l)[0], K) return [ dup_trunc(F, p**l, K) ] m = p k = r // 2 d = int(ceil(log(l, 2))) g = gf_from_int_poly([lc], p) for f_i in f_list[:k]: g = gf_mul(g, gf_from_int_poly(f_i, p), p, K) h = gf_from_int_poly(f_list[k], p) for f_i in f_list[k+1:]: h = gf_mul(h, gf_from_int_poly(f_i, p), p, K) s, t, _ = gf_gcdex(g, h, p, K) g = gf_to_int_poly(g, p) h = gf_to_int_poly(h, p) s = gf_to_int_poly(s, p) t = gf_to_int_poly(t, p) for _ in range(1, d+1): (g, h, s, t), m = dup_zz_hensel_step(m, f, g, h, s, t, K), m**2 return dup_zz_hensel_lift(p, g, f_list[:k], l, K) \ + dup_zz_hensel_lift(p, h, f_list[k:], l, K) @cythonized("l,s") def dup_zz_zassenhaus(f, K): """Factor primitive square-free polynomials in `Z[x]`. """ n = dup_degree(f) if n == 1: return [f] A = dup_max_norm(f, K) b = dup_LC(f, K) B = int(abs(K.sqrt(K(n+1))*2**n*A*b)) C = int((n+1)**(2*n)*A**(2*n-1)) gamma = int(ceil(2*log(C, 2))) bound = int(2*gamma*log(gamma)) for p in xrange(3, bound+1): if not isprime(p) or b % p == 0: continue p = K.convert(p) F = gf_from_int_poly(f, p) if gf_sqf_p(F, p, K): break l = int(ceil(log(2*B + 1, p))) modular = [] for ff in gf_factor_sqf(F, p, K)[1]: modular.append(gf_to_int_poly(ff, p)) g = dup_zz_hensel_lift(p, f, modular, l, K) T = set(range(len(g))) factors, s = [], 1 while 2*s <= len(T): for S in subsets(T, s): G, H = [b], [b] S = set(S) for i in S: G = dup_mul(G, g[i], K) for i in T-S: H = dup_mul(H, g[i], K) G = dup_trunc(G, p**l, K) H = dup_trunc(H, p**l, K) G_norm = dup_l1_norm(G, K) H_norm = dup_l1_norm(H, K) if G_norm*H_norm <= B: T = T - S G = dup_primitive(G, K)[1] f = dup_primitive(H, K)[1] factors.append(G) b = dup_LC(f, K) break else: s += 1 return factors + [f] def dup_zz_irreducible_p(f, K): """Test irreducibility using Eisenstein's criterion. """ lc = dup_LC(f, K) tc = dup_TC(f, K) e_fc = dup_content(f[1:], K) if e_fc: e_ff = factorint(int(e_fc)) for p in e_ff.iterkeys(): if (lc % p) and (tc % p**2): return True @cythonized("n,i") def dup_zz_cyclotomic_p(f, K, irreducible=False): """ Efficiently test if ``f`` is a cyclotomic polnomial. **Examples** >>> from sympy.polys.factortools import dup_zz_cyclotomic_p >>> from sympy.polys.domains import ZZ >>> f = [1, 0, 1, 0, 0, 0,-1, 0, 1, 0,-1, 0, 0, 0, 1, 0, 1] >>> dup_zz_cyclotomic_p(f, ZZ) False >>> g = [1, 0, 1, 0, 0, 0,-1, 0,-1, 0,-1, 0, 0, 0, 1, 0, 1] >>> dup_zz_cyclotomic_p(g, ZZ) True """ if K.is_QQ: try: K0, K = K, K.get_ring() f = dup_convert(f, K0, K) except CoercionFailed: return False elif not K.is_ZZ: return False lc = dup_LC(f, K) tc = dup_TC(f, K) if lc != 1 or (tc != -1 and tc != 1): return False if not irreducible: coeff, factors = dup_factor_list(f, K) if coeff != K.one or factors != [(f, 1)]: return False n = dup_degree(f) g, h = [], [] for i in xrange(n, -1, -2): g.insert(0, f[i]) for i in xrange(n-1, -1, -2): h.insert(0, f[i]) g = dup_sqr(dup_strip(g), K) h = dup_sqr(dup_strip(h), K) F = dup_sub(g, dup_lshift(h, 1, K), K) if K.is_negative(dup_LC(F, K)): F = dup_neg(F, K) if F == f: return True g = dup_mirror(f, K) if K.is_negative(dup_LC(g, K)): g = dup_neg(g, K) if F == g and dup_zz_cyclotomic_p(g, K): return True G = dup_sqf_part(F, K) if dup_sqr(G, K) == F and dup_zz_cyclotomic_p(G, K): return True return False @cythonized("n,p,k") def dup_zz_cyclotomic_poly(n, K): """Efficiently generate n-th cyclotomic polnomial. """ h = [K.one,-K.one] for p, k in factorint(n).iteritems(): h = dup_quo(dup_inflate(h, p, K), h, K) h = dup_inflate(h, p**(k-1), K) return h @cythonized("n,p,k,i") def _dup_cyclotomic_decompose(n, K): H = [[K.one,-K.one]] for p, k in factorint(n).iteritems(): Q = [ dup_quo(dup_inflate(h, p, K), h, K) for h in H ] H.extend(Q) for i in xrange(1, k): Q = [ dup_inflate(q, p, K) for q in Q ] H.extend(Q) return H @cythonized("n") def dup_zz_cyclotomic_factor(f, K): """ Efficiently factor polynomials `x**n - 1` and `x**n + 1` in `Z[x]`. Given a univariate polynomial `f` in `Z[x]` returns a list of factors of `f`, provided that `f` is in the form `x**n - 1` or `x**n + 1` for `n >= 1`. Otherwise returns None. Factorization is performed using using cyclotomic decomposition of `f`, which makes this method much faster that any other direct factorization approach (e.g. Zassenhaus's). **References** 1. [Weisstein09]_ """ lc_f, tc_f = dup_LC(f, K), dup_TC(f, K) if dup_degree(f) <= 0: return None if lc_f != 1 or tc_f not in [-1, 1]: return None if any([ bool(cf) for cf in f[1:-1] ]): return None n = dup_degree(f) F = _dup_cyclotomic_decompose(n, K) if not K.is_one(tc_f): return F else: H = [] for h in _dup_cyclotomic_decompose(2*n, K): if h not in F: H.append(h) return H @cythonized("n") def dup_zz_factor_sqf(f, K): """Factor square-free (non-primitive) polyomials in `Z[x]`. """ cont, g = dup_primitive(f, K) n = dup_degree(g) if dup_LC(g, K) < 0: cont, g = -cont, dup_neg(g, K) if n <= 0: return cont, [] elif n == 1: return cont, [(g, 1)] if query('USE_IRREDUCIBLE_IN_FACTOR'): if dup_zz_irreducible_p(g, K): return cont, [(g, 1)] factors = None if query('USE_CYCLOTOMIC_FACTOR'): factors = dup_zz_cyclotomic_factor(g, K) if factors is None: factors = dup_zz_zassenhaus(g, K) return cont, _sort_factors(factors, multiple=False) @cythonized("n,k") def dup_zz_factor(f, K): """ Factor (non square-free) polynomials in `Z[x]`. Given a univariate polynomial `f` in `Z[x]` computes its complete factorization `f_1, ..., f_n` into irreducibles over integers:: f = content(f) f_1**k_1 ... f_n**k_n The factorization is computed by reducing the input polynomial into a primitive square-free polynomial and factoring it using Zassenhaus algorithm. Trial division is used to recover the multiplicities of factors. The result is returned as a tuple consisting of:: (content(f), [(f_1, k_1), ..., (f_n, k_n)) Consider polynomial `f = 2*x**4 - 2`:: >>> from sympy.polys.factortools import dup_zz_factor >>> from sympy.polys.domains import ZZ >>> dup_zz_factor([2, 0, 0, 0, -2], ZZ) (2, [([1, -1], 1), ([1, 1], 1), ([1, 0, 1], 1)]) In result we got the following factorization:: f = 2 (x - 1) (x + 1) (x**2 + 1) Note that this is a complete factorization over integers, however over Gaussian integers we can factor the last term. By default, polynomials `x**n - 1` and `x**n + 1` are factored using cyclotomic decomposition to speedup computations. To disable this behaviour set cyclotomic=False. **References** 1. [Gathen99]_ """ cont, g = dup_primitive(f, K) n = dup_degree(g) if dup_LC(g, K) < 0: cont, g = -cont, dup_neg(g, K) if n <= 0: return cont, [] elif n == 1: return cont, [(g, 1)] if query('USE_IRREDUCIBLE_IN_FACTOR'): if dup_zz_irreducible_p(g, K): return cont, [(g, 1)] g = dup_sqf_part(g, K) H, factors = None, [] if query('USE_CYCLOTOMIC_FACTOR'): H = dup_zz_cyclotomic_factor(g, K) if H is None: H = dup_zz_zassenhaus(g, K) for h in H: k = 0 while True: q, r = dup_div(f, h, K) if not r: f, k = q, k+1 else: break factors.append((h, k)) return cont, _sort_factors(factors) def dmp_zz_wang_non_divisors(E, cs, ct, K): """Wang/EEZ: Compute a set of valid divisors. """ result = [ cs*ct ] for q in E: q = abs(q) for r in reversed(result): while r != 1: r = K.gcd(r, q) q = q // r if K.is_one(q): return None result.append(q) return result[1:] @cythonized("u,v") def dmp_zz_wang_test_points(f, T, ct, A, u, K): """Wang/EEZ: Test evaluation points for suitability. """ if not dmp_eval_tail(dmp_LC(f, K), A, u-1, K): raise EvaluationFailed('no luck') g = dmp_eval_tail(f, A, u, K) if not dup_sqf_p(g, K): raise EvaluationFailed('no luck') c, h = dup_primitive(g, K) if K.is_negative(dup_LC(h, K)): c, h = -c, dup_neg(h, K) v = u-1 E = [ dmp_eval_tail(t, A, v, K) for t, _ in T ] D = dmp_zz_wang_non_divisors(E, c, ct, K) if D is not None: return c, h, E else: raise EvaluationFailed('no luck') @cythonized("u,v,i,j,k") def dmp_zz_wang_lead_coeffs(f, T, cs, E, H, A, u, K): """Wang/EEZ: Compute correct leading coefficients. """ C, J, v = [], [0]*len(E), u-1 for h in H: c = dmp_one(v, K) d = dup_LC(h, K)*cs for i in reversed(xrange(len(E))): k, e, (t, _) = 0, E[i], T[i] while not (d % e): d, k = d//e, k+1 if k != 0: c, J[i] = dmp_mul(c, dmp_pow(t, k, v, K), v, K), 1 C.append(c) if any([ not j for j in J ]): raise ExtraneousFactors # pragma: no cover CC, HH = [], [] for c, h in zip(C, H): d = dmp_eval_tail(c, A, v, K) lc = dup_LC(h, K) if K.is_one(cs): cc = lc//d else: g = K.gcd(lc, d) d, cc = d//g, lc//g h, cs = dup_mul_ground(h, d, K), cs//d c = dmp_mul_ground(c, cc, v, K) CC.append(c) HH.append(h) if K.is_one(cs): return f, HH, CC CCC, HHH = [], [] for c, h in zip(CC, HH): CCC.append(dmp_mul_ground(c, cs, v, K)) HHH.append(dmp_mul_ground(h, cs, 0, K)) f = dmp_mul_ground(f, cs**(len(H)-1), u, K) return f, HHH, CCC @cythonized("m") def dup_zz_diophantine(F, m, p, K): """Wang/EEZ: Solve univariate Diophantine equations. """ if len(F) == 2: a, b = F f = gf_from_int_poly(a, p) g = gf_from_int_poly(b, p) s, t, G = gf_gcdex(g, f, p, K) s = gf_lshift(s, m, K) t = gf_lshift(t, m, K) q, s = gf_div(s, f, p, K) t = gf_add_mul(t, q, g, p, K) s = gf_to_int_poly(s, p) t = gf_to_int_poly(t, p) result = [s, t] else: G = [F[-1]] for f in reversed(F[1:-1]): G.insert(0, dup_mul(f, G[0], K)) S, T = [], [[1]] for f, g in zip(F, G): t, s = dmp_zz_diophantine([g, f], T[-1], [], 0, p, 1, K) T.append(t) S.append(s) result, S = [], S + [T[-1]] for s, f in zip(S, F): s = gf_from_int_poly(s, p) f = gf_from_int_poly(f, p) r = gf_rem(gf_lshift(s, m, K), f, p, K) s = gf_to_int_poly(r, p) result.append(s) return result @cythonized("u,v,d,n,i,j,k") def dmp_zz_diophantine(F, c, A, d, p, u, K): """Wang/EEZ: Solve multivariate Diophantine equations. """ if not A: S = [ [] for _ in F ] n = dup_degree(c) for i, coeff in enumerate(c): if not coeff: continue T = dup_zz_diophantine(F, n-i, p, K) for j, (s, t) in enumerate(zip(S, T)): t = dup_mul_ground(t, coeff, K) S[j] = dup_trunc(dup_add(s, t, K), p, K) else: n = len(A) e = dmp_expand(F, u, K) a, A = A[-1], A[:-1] B, G = [], [] for f in F: B.append(dmp_quo(e, f, u, K)) G.append(dmp_eval_in(f, a, n, u, K)) C = dmp_eval_in(c, a, n, u, K) v = u - 1 S = dmp_zz_diophantine(G, C, A, d, p, v, K) S = [ dmp_raise(s, 1, v, K) for s in S ] for s, b in zip(S, B): c = dmp_sub_mul(c, s, b, u, K) c = dmp_ground_trunc(c, p, u, K) m = dmp_nest([K.one, -a], n, K) M = dmp_one(n, K) for k in xrange(0, d): if dmp_zero_p(c, u): break M = dmp_mul(M, m, u, K) C = dmp_diff_eval_in(c, k+1, a, n, u, K) if not dmp_zero_p(C, v): C = dmp_quo_ground(C, K.factorial(k+1), v, K) T = dmp_zz_diophantine(G, C, A, d, p, v, K) for i, t in enumerate(T): T[i] = dmp_mul(dmp_raise(t, 1, v, K), M, u, K) for i, (s, t) in enumerate(zip(S, T)): S[i] = dmp_add(s, t, u, K) for t, b in zip(T, B): c = dmp_sub_mul(c, t, b, u, K) c = dmp_ground_trunc(c, p, u, K) S = [ dmp_ground_trunc(s, p, u, K) for s in S ] return S @cythonized("u,v,d,dj,n,i,j,k,w") def dmp_zz_wang_hensel_lifting(f, H, LC, A, p, u, K): """Wang/EEZ: Parallel Hensel lifting algorithm. """ S, n, v = [f], len(A), u-1 H = list(H) for i, a in enumerate(reversed(A[1:])): s = dmp_eval_in(S[0], a, n-i, u-i, K) S.insert(0, dmp_ground_trunc(s, p, v-i, K)) d = max(dmp_degree_list(f, u)[1:]) for j, s, a in zip(xrange(2, n+2), S, A): G, w = list(H), j-1 I, J = A[:j-2], A[j-1:] for i, (h, lc) in enumerate(zip(H, LC)): lc = dmp_ground_trunc(dmp_eval_tail(lc, J, v, K), p, w-1, K) H[i] = [lc] + dmp_raise(h[1:], 1, w-1, K) m = dmp_nest([K.one, -a], w, K) M = dmp_one(w, K) c = dmp_sub(s, dmp_expand(H, w, K), w, K) dj = dmp_degree_in(s, w, w) for k in xrange(0, dj): if dmp_zero_p(c, w): break M = dmp_mul(M, m, w, K) C = dmp_diff_eval_in(c, k+1, a, w, w, K) if not dmp_zero_p(C, w-1): C = dmp_quo_ground(C, K.factorial(k+1), w-1, K) T = dmp_zz_diophantine(G, C, I, d, p, w-1, K) for i, (h, t) in enumerate(zip(H, T)): h = dmp_add_mul(h, dmp_raise(t, 1, w-1, K), M, w, K) H[i] = dmp_ground_trunc(h, p, w, K) h = dmp_sub(s, dmp_expand(H, w, K), w, K) c = dmp_ground_trunc(h, p, w, K) if dmp_expand(H, u, K) != f: raise ExtraneousFactors # pragma: no cover else: return H @cythonized("u,mod,i,j,s_arg,negative") def dmp_zz_wang(f, u, K, mod=None): """ Factor primitive square-free polynomials in `Z[X]`. Given a multivariate polynomial `f` in `Z[x_1,...,x_n]`, which is primitive and square-free in `x_1`, computes factorization of `f` into irreducibles over integers. The procedure is based on Wang's Enhanced Extended Zassenhaus algorithm. The algorithm works by viewing `f` as a univariate polynomial in `Z[x_2,...,x_n][x_1]`, for which an evaluation mapping is computed:: x_2 -> a_2, ..., x_n -> a_n where `a_i`, for `i = 2, ..., n`, are carefully chosen integers. The mapping is used to transform `f` into a univariate polynomial in `Z[x_1]`, which can be factored efficiently using Zassenhaus algorithm. The last step is to lift univariate factors to obtain true multivariate factors. For this purpose a parallel Hensel lifting procedure is used. **References** 1. [Wang78]_ 2. [Geddes92]_ """ ct, T = dmp_zz_factor(dmp_LC(f, K), u-1, K) b = dmp_zz_mignotte_bound(f, u, K) p = K(nextprime(b)) if mod is None: if u == 1: mod = 2 else: mod = 1 history, configs, A, r = set([]), [], [K.zero]*u, None try: cs, s, E = dmp_zz_wang_test_points(f, T, ct, A, u, K) _, H = dup_zz_factor_sqf(s, K) r = len(H) if r == 1: return [f] bad_points = set([tuple(A)]) configs = [(s, cs, E, H, A)] except EvaluationFailed: pass eez_num_configs = query('EEZ_NUMBER_OF_CONFIGS') eez_num_tries = query('EEZ_NUMBER_OF_TRIES') eez_mod_step = query('EEZ_MODULUS_STEP') while len(configs) < eez_num_configs: for _ in xrange(eez_num_tries): A = [ K(randint(-mod, mod)) for _ in xrange(u) ] if tuple(A) not in history: history.add(tuple(A)) else: continue try: cs, s, E = dmp_zz_wang_test_points(f, T, ct, A, u, K) except EvaluationFailed: continue _, H = dup_zz_factor_sqf(s, K) rr = len(H) if r is not None: if rr != r: # pragma: no cover if rr < r: configs, r = [], rr else: continue else: r = rr if r == 1: return [f] configs.append((s, cs, E, H, A)) if len(configs) == eez_num_configs: break else: mod += eez_mod_step s_norm, s_arg, i = None, 0, 0 for s, _, _, _, _ in configs: _s_norm = dup_max_norm(s, K) if s_norm is not None: if _s_norm < s_norm: s_norm = _s_norm s_arg = i else: s_norm = _s_norm i += 1 _, cs, E, H, A = configs[s_arg] try: f, H, LC = dmp_zz_wang_lead_coeffs(f, T, cs, E, H, A, u, K) factors = dmp_zz_wang_hensel_lifting(f, H, LC, A, p, u, K) except ExtraneousFactors: # pragma: no cover if query('EEZ_RESTART_IF_NEEDED'): return dmp_zz_wang(f, u, K, mod+1) else: raise ExtraneousFactors("we need to restart algorithm with better parameters") negative, result = 0, [] for f in factors: _, f = dmp_ground_primitive(f, u, K) if K.is_negative(dmp_ground_LC(f, u, K)): f = dmp_neg(f, u, K) result.append(f) return result @cythonized("u,d,k") def dmp_zz_factor(f, u, K): """ Factor (non square-free) polynomials in `Z[X]`. Given a multivariate polynomial `f` in `Z[x]` computes its complete factorization `f_1, ..., f_n` into irreducibles over integers:: f = content(f) f_1**k_1 ... f_n**k_n The factorization is computed by reducing the input polynomial into a primitive square-free polynomial and factoring it using Enhanced Extended Zassenhaus (EEZ) algorithm. Trial division is used to recover the multiplicities of factors. The result is returned as a tuple consisting of:: (content(f), [(f_1, k_1), ..., (f_n, k_n)) Consider polynomial `f = 2*(x**2 - y**2)`:: >>> from sympy.polys.factortools import dmp_zz_factor >>> from sympy.polys.domains import ZZ >>> dmp_zz_factor([[2], [], [-2, 0, 0]], 1, ZZ) (2, [([[1], [-1, 0]], 1), ([[1], [1, 0]], 1)]) In result we got the following factorization:: f = 2 (x - y) (x + y) **References** 1. [Gathen99]_ """ if not u: return dup_zz_factor(f, K) if dmp_zero_p(f, u): return K.zero, [] cont, g = dmp_ground_primitive(f, u, K) if dmp_ground_LC(g, u, K) < 0: cont, g = -cont, dmp_neg(g, u, K) if all([ d <= 0 for d in dmp_degree_list(g, u) ]): return cont, [] G, g = dmp_primitive(g, u, K) factors = [] if dmp_degree(g, u) > 0: g = dmp_sqf_part(g, u, K) H = dmp_zz_wang(g, u, K) for h in H: k = 0 while True: q, r = dmp_div(f, h, u, K) if dmp_zero_p(r, u): f, k = q, k+1 else: break factors.append((h, k)) for g, k in dmp_zz_factor(G, u-1, K)[1]: factors.insert(0, ([g], k)) return cont, _sort_factors(factors) def dup_ext_factor(f, K): """Factor univariate polynomials over algebraic number fields. """ n, lc = dup_degree(f), dup_LC(f, K) f = dup_monic(f, K) if n <= 0: return lc, [] if n == 1: return lc, [(f, 1)] f, F = dup_sqf_part(f, K), f s, g, r = dup_sqf_norm(f, K) factors = dup_factor_list_include(r, K.dom) if len(factors) == 1: return lc, [(f, n//dup_degree(f))] H = s*K.unit for i, (factor, _) in enumerate(factors): h = dup_convert(factor, K.dom, K) h, _, g = dup_inner_gcd(h, g, K) h = dup_shift(h, H, K) factors[i] = h factors = dup_trial_division(F, factors, K) return lc, factors @cythonized("u") def dmp_ext_factor(f, u, K): """Factor multivariate polynomials over algebraic number fields. """ if not u: return dup_ext_factor(f, K) lc = dmp_ground_LC(f, u, K) f = dmp_ground_monic(f, u, K) if all([ d <= 0 for d in dmp_degree_list(f, u) ]): return lc, [] f, F = dmp_sqf_part(f, u, K), f s, g, r = dmp_sqf_norm(f, u, K) factors = dmp_factor_list_include(r, u, K.dom) if len(factors) == 1: coeff, factors = lc, [f] else: H = dmp_raise([K.one, s*K.unit], u, 0, K) for i, (factor, _) in enumerate(factors): h = dmp_convert(factor, u, K.dom, K) h, _, g = dmp_inner_gcd(h, g, u, K) h = dmp_compose(h, H, u, K) factors[i] = h return lc, dmp_trial_division(F, factors, u, K) @cythonized("i") def dup_gf_factor(f, K): """Factor univariate polynomials over finite fields. """ f = dup_convert(f, K, K.dom) coeff, factors = gf_factor(f, K.mod, K.dom) for i, (f, k) in enumerate(factors): factors[i] = (dup_convert(f, K.dom, K), k) return K.convert(coeff, K.dom), factors def dmp_gf_factor(f, u, K): """Factor multivariate polynomials over finite fields. """ raise DomainError('multivariate polynomials over %s' % K) @cythonized("i,k,u") def dup_factor_list(f, K0): """Factor polynomials into irreducibles in `K[x]`. """ j, f = dup_terms_gcd(f, K0) if not K0.has_CharacteristicZero: coeff, factors = dup_gf_factor(f, K0) elif K0.is_Algebraic: coeff, factors = dup_ext_factor(f, K0) else: if not K0.is_Exact: K0_inexact, K0 = K0, K0.get_exact() f = dup_convert(f, K0_inexact, K0) else: K0_inexact = None if K0.has_Field: K = K0.get_ring() denom, f = dup_clear_denoms(f, K0, K) f = dup_convert(f, K0, K) else: K = K0 if K.is_ZZ: coeff, factors = dup_zz_factor(f, K) elif K.is_Poly: f, u = dmp_inject(f, 0, K) coeff, factors = dmp_factor_list(f, u, K.dom) for i, (f, k) in enumerate(factors): factors[i] = (dmp_eject(f, u, K), k) coeff = K.convert(coeff, K.dom) else: # pragma: no cover raise DomainError('factorization not supported over %s' % K0) if K0.has_Field: for i, (f, k) in enumerate(factors): factors[i] = (dup_convert(f, K, K0), k) coeff = K0.convert(coeff, K) denom = K0.convert(denom, K) coeff = K0.quo(coeff, denom) if K0_inexact is not None: for i, (f, k) in enumerate(factors): factors[i] = (dup_convert(f, K0, K0_inexact), k) coeff = K0_inexact.convert(coeff, K0) if j: factors.insert(0, ([K0.one, K0.zero], j)) return coeff, _sort_factors(factors) def dup_factor_list_include(f, K): """Factor polynomials into irreducibles in `K[x]`. """ coeff, factors = dup_factor_list(f, K) if not factors: return [(dup_strip([coeff]), 1)] else: g = dup_mul_ground(factors[0][0], coeff, K) return [(g, factors[0][1])] + factors[1:] @cythonized("u,v,i,k") def dmp_factor_list(f, u, K0): """Factor polynomials into irreducibles in `K[X]`. """ if not u: return dup_factor_list(f, K0) J, f = dmp_terms_gcd(f, u, K0) if not K0.has_CharacteristicZero: # pragma: no cover coeff, factors = dmp_gf_factor(f, u, K0) elif K0.is_Algebraic: coeff, factors = dmp_ext_factor(f, u, K0) else: if not K0.is_Exact: K0_inexact, K0 = K0, K0.get_exact() f = dmp_convert(f, u, K0_inexact, K0) else: K0_inexact = None if K0.has_Field: K = K0.get_ring() denom, f = dmp_clear_denoms(f, u, K0, K) f = dmp_convert(f, u, K0, K) else: K = K0 if K.is_ZZ: levels, f, v = dmp_exclude(f, u, K) coeff, factors = dmp_zz_factor(f, v, K) for i, (f, k) in enumerate(factors): factors[i] = (dmp_include(f, levels, v, K), k) elif K.is_Poly: f, v = dmp_inject(f, u, K) coeff, factors = dmp_factor_list(f, v, K.dom) for i, (f, k) in enumerate(factors): factors[i] = (dmp_eject(f, v, K), k) coeff = K.convert(coeff, K.dom) else: # pragma: no cover raise DomainError('factorization not supported over %s' % K0) if K0.has_Field: for i, (f, k) in enumerate(factors): factors[i] = (dmp_convert(f, u, K, K0), k) coeff = K0.convert(coeff, K) denom = K0.convert(denom, K) coeff = K0.quo(coeff, denom) if K0_inexact is not None: for i, (f, k) in enumerate(factors): factors[i] = (dmp_convert(f, u, K0, K0_inexact), k) coeff = K0_inexact.convert(coeff, K0) for i, j in enumerate(reversed(J)): if not j: continue term = {(0,)*(u-i) + (1,) + (0,)*i: K0.one} factors.insert(0, (dmp_from_dict(term, u, K0), j)) return coeff, _sort_factors(factors) @cythonized("u") def dmp_factor_list_include(f, u, K): """Factor polynomials into irreducibles in `K[X]`. """ if not u: return dup_factor_list_include(f, K) coeff, factors = dmp_factor_list(f, u, K) if not factors: return [(dmp_ground(coeff, u), 1)] else: g = dmp_mul_ground(factors[0][0], coeff, u, K) return [(g, factors[0][1])] + factors[1:] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/rootisolation.py0000644000175000017500000014535212014170666025536 0ustar georgeskgeorgesk"""Real and complex root isolation and refinement algorithms. """ from sympy.polys.densebasic import ( dup_LC, dup_TC, dup_degree, dup_strip, dup_reverse, dup_convert, dmp_convert, dup_terms_gcd) from sympy.polys.densearith import ( dup_neg, dup_rshift, dup_rem) from sympy.polys.densetools import ( dup_clear_denoms, dup_mirror, dup_scale, dup_shift, dup_transform, dup_diff, dup_eval, dmp_eval_in, dup_sign_variations, dup_real_imag) from sympy.polys.sqfreetools import ( dup_sqf_part, dup_sqf_list) from sympy.polys.factortools import ( dup_factor_list) from sympy.polys.polyerrors import ( RefinementFailed, DomainError) import operator def dup_sturm(f, K): """ Computes the Sturm sequence of ``f`` in ``F[x]``. Given a univariate, square-free polynomial ``f(x)`` returns the associated Sturm sequence ``f_0(x), ..., f_n(x)`` defined by:: f_0(x), f_1(x) = f(x), f'(x) f_n = -rem(f_{n-2}(x), f_{n-1}(x)) **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.rootisolation import dup_sturm >>> f = QQ.map([1, -2, 1, -3]) >>> dup_sturm(f, QQ) [[1/1, -2/1, 1/1, -3/1], [3/1, -4/1, 1/1], [2/9, 25/9], [-2079/4]] **References** 1. [Davenport88]_ """ if not (K.has_Field or not K.is_Exact): raise DomainError("can't compute Sturm sequence over %s" % K) f = dup_sqf_part(f, K) sturm = [f, dup_diff(f, 1, K)] while sturm[-1]: s = dup_rem(sturm[-2], sturm[-1], K) sturm.append(dup_neg(s, K)) return sturm[:-1] def dup_root_upper_bound(f, K): """Compute LMQ upper bound for `f`'s positive roots. """ n, t, P = len(f), K.one, [] if dup_LC(f, K) < 0: f = dup_neg(f, K) f = list(reversed(f)) for i in xrange(0, n): if f[i] >= 0: continue a, Q = K.log(-f[i], 2), [] for j in xrange(i+1, n): if f[j] <= 0: continue q = t + a - K.log(f[j], 2) Q.append(q // (j - i)) t += 1 if not Q: continue P.append(min(Q)) if not P: return None else: return 2.0**(max(P)+1) def dup_root_lower_bound(f, K): """Compute LMQ lower bound for `f`'s positive roots. """ bound = dup_root_upper_bound(dup_reverse(f), K) if bound is not None: return 1.0 / bound else: return None def _mobius_from_interval(I, field): """Convert an open interval to a Mobius transform. """ s, t = I a, c = field.numer(s), field.denom(s) b, d = field.numer(t), field.denom(t) return a, b, c, d def _mobius_to_interval(M, field): """Convert a Mobius transform to an open interval. """ a, b, c, d = M s, t = field(a, c), field(b, d) if s <= t: return (s, t) else: return (t, s) def dup_step_refine_real_root(f, M, K, fast=False): """One step of positive real root refinement algorithm. """ a, b, c, d = M if a == b and c == d: return f, (a, b, c, d) A = dup_root_lower_bound(f, K) if A is not None: A = K(int(A)) else: A = K.zero if fast and A > 16: f = dup_scale(f, A, K) a, c, A = A*a, A*c, K.one if A >= K.one: f = dup_shift(f, A, K) b, d = A*a + b, A*c + d if not dup_eval(f, K.zero, K): return f, (b, b, d, d) f, g = dup_shift(f, K.one, K), f a1, b1, c1, d1 = a, a+b, c, c+d if not dup_eval(f, K.zero, K): return f, (b1, b1, d1, d1) k = dup_sign_variations(f, K) if k == 1: a, b, c, d = a1, b1, c1, d1 else: f = dup_shift(dup_reverse(g), K.one, K) if not dup_eval(f, K.zero, K): f = dup_rshift(f, 1, K) a, b, c, d = b, a+b, d, c+d return f, (a, b, c, d) def dup_inner_refine_real_root(f, M, K, eps=None, steps=None, disjoint=None, fast=False, mobius=False): """Refine a positive root of `f` given a Mobius transform or an interval. """ F = K.get_field() if len(M) == 2: a, b, c, d = _mobius_from_interval(M, F) else: a, b, c, d = M while not c: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if eps is not None and steps is not None: for i in xrange(0, steps): if abs(F(a, c) - F(b, d)) >= eps: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) else: break else: if eps is not None: while abs(F(a, c) - F(b, d)) >= eps: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if steps is not None: for i in xrange(0, steps): f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if disjoint is not None: while True: u, v = _mobius_to_interval((a, b, c, d), F) if v <= disjoint or disjoint <= u: break else: f, (a, b, c, d) = dup_step_refine_real_root(f, (a, b, c, d), K, fast=fast) if not mobius: return _mobius_to_interval((a, b, c, d), F) else: return f, (a, b, c, d) def dup_outer_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False): """Refine a positive root of `f` given an interval `(s, t)`. """ a, b, c, d = _mobius_from_interval((s, t), K.get_field()) f = dup_transform(f, dup_strip([a, b]), dup_strip([c, d]), K) if dup_sign_variations(f, K) != 1: raise RefinementFailed("there should be exactly one root in (%s, %s) interval" % (s, t)) return dup_inner_refine_real_root(f, (a, b, c, d), K, eps=eps, steps=steps, disjoint=disjoint, fast=fast) def dup_refine_real_root(f, s, t, K, eps=None, steps=None, disjoint=None, fast=False): """Refine real root's approximating interval to the given precision. """ if K.is_QQ: (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring() elif not K.is_ZZ: raise DomainError("real root refinement not supported over %s" % K) if s == t: return (s, t) if s > t: s, t = t, s negative = False if s < 0: if t <= 0: f, s, t, negative = dup_mirror(f, K), -t, -s, True else: raise ValueError("can't refine a real root in (%s, %s)" % (s, t)) if negative and disjoint is not None: if disjoint < 0: disjoint = -disjoint else: disjoint = None s, t = dup_outer_refine_real_root(f, s, t, K, eps=eps, steps=steps, disjoint=disjoint, fast=fast) if negative: return (-t, -s) else: return ( s, t) def dup_inner_isolate_real_roots(f, K, eps=None, fast=False): """Internal function for isolation positive roots up to given precision. """ a, b, c, d = K.one, K.zero, K.zero, K.one k = dup_sign_variations(f, K) if k == 0: return [] if k == 1: roots = [dup_inner_refine_real_root(f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True)] else: roots, stack = [], [(a, b, c, d, f, k)] F = K.get_field() while stack: a, b, c, d, f, k = stack.pop() A = dup_root_lower_bound(f, K) if A is not None: A = K(int(A)) else: A = K.zero if fast and A > 16: f = dup_scale(f, A, K) a, c, A = A*a, A*c, K.one if A >= K.one: f = dup_shift(f, A, K) b, d = A*a + b, A*c + d if not dup_TC(f, K): roots.append((f, (b, b, d, d))) f = dup_rshift(f, 1, K) k = dup_sign_variations(f, K) if k == 0: continue if k == 1: roots.append(dup_inner_refine_real_root(f, (a, b, c, d), K, eps=eps, fast=fast, mobius=True)) continue f1 = dup_shift(f, K.one, K) a1, b1, c1, d1, r = a, a+b, c, c+d, 0 if not dup_TC(f1, K): roots.append((f1, (b1, b1, d1, d1))) f1, r = dup_rshift(f1, 1, K), 1 k1 = dup_sign_variations(f1, K) k2 = k - k1 - r a2, b2, c2, d2 = b, a+b, d, c+d if k2 > 1: f2 = dup_shift(dup_reverse(f), K.one, K) if not dup_TC(f2, K): f2 = dup_rshift(f2, 1, K) k2 = dup_sign_variations(f2, K) else: f2 = None if k1 < k2: a1, a2, b1, b2 = a2, a1, b2, b1 c1, c2, d1, d2 = c2, c1, d2, d1 f1, f2, k1, k2 = f2, f1, k2, k1 if not k1: continue if f1 is None: f1 = dup_shift(dup_reverse(f), K.one, K) if not dup_TC(f1, K): f1 = dup_rshift(f1, 1, K) if k1 == 1: roots.append(dup_inner_refine_real_root(f1, (a1, b1, c1, d1), K, eps=eps, fast=fast, mobius=True)) else: stack.append((a1, b1, c1, d1, f1, k1)) if not k2: continue if f2 is None: f2 = dup_shift(dup_reverse(f), K.one, K) if not dup_TC(f2, K): f2 = dup_rshift(f2, 1, K) if k2 == 1: roots.append(dup_inner_refine_real_root(f2, (a2, b2, c2, d2), K, eps=eps, fast=fast, mobius=True)) else: stack.append((a2, b2, c2, d2, f2, k2)) return roots def _discard_if_outside_interval(f, M, inf, sup, K, negative, fast, mobius): """Discard an isolating interval if outside ``(inf, sup)``. """ F = K.get_field() while True: u, v = _mobius_to_interval(M, F) if negative: u, v = -v, -u if (inf is None or u >= inf) and (sup is None or v <= sup): if not mobius: return u, v else: return f, M elif (sup is not None and u > sup) or (inf is not None and v < inf): return None else: f, M = dup_step_refine_real_root(f, M, K, fast=fast) def dup_inner_isolate_positive_roots(f, K, eps=None, inf=None, sup=None, fast=False, mobius=False): """Iteratively compute disjoint positive root isolation intervals. """ if sup is not None and sup < 0: return [] roots = dup_inner_isolate_real_roots(f, K, eps=eps, fast=fast) F, results = K.get_field(), [] if inf is not None or sup is not None: for f, M in roots: result = _discard_if_outside_interval(f, M, inf, sup, K, False, fast, mobius) if result is not None: results.append(result) elif not mobius: for f, M in roots: u, v = _mobius_to_interval(M, F) results.append((u, v)) else: results = roots return results def dup_inner_isolate_negative_roots(f, K, inf=None, sup=None, eps=None, fast=False, mobius=False): """Iteratively compute disjoint negative root isolation intervals. """ if inf is not None and inf >= 0: return [] roots = dup_inner_isolate_real_roots(dup_mirror(f, K), K, eps=eps, fast=fast) F, results = K.get_field(), [] if inf is not None or sup is not None: for f, M in roots: result = _discard_if_outside_interval(f, M, inf, sup, K, True, fast, mobius) if result is not None: results.append(result) elif not mobius: for f, M in roots: u, v = _mobius_to_interval(M, F) results.append((-v, -u)) else: results = roots return results def _isolate_zero(f, K, inf, sup, basis=False, sqf=False): """Handle special case of CF algorithm when ``f`` is homogeneous. """ j, f = dup_terms_gcd(f, K) if j > 0: F = K.get_field() if (inf is None or inf <= 0) and (sup is None or 0 <= sup): if not sqf: if not basis: return [((F.zero, F.zero), j)], f else: return [((F.zero, F.zero), j, [K.one, K.zero])], f else: return [(F.zero, F.zero)], f return [], f def dup_isolate_real_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False): """Isolate real roots of a square-free polynomial using CF approach. """ if K.is_QQ: (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring() elif not K.is_ZZ: raise DomainError("isolation of real roots not supported over %s" % K) if dup_degree(f) <= 0: return [] I_zero, f = _isolate_zero(f, K, inf, sup, basis=False, sqf=True) I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) roots = sorted(I_neg + I_zero + I_pos) if not blackbox: return roots else: return [ RealInterval((a, b), f, K) for (a, b) in roots ] def dup_isolate_real_roots(f, K, eps=None, inf=None, sup=None, basis=False, fast=False): """Isolate real roots using continued fractions approach. """ if K.is_QQ: (_, f), K = dup_clear_denoms(f, K, convert=True), K.get_ring() elif not K.is_ZZ: raise DomainError("isolation of real roots not supported over %s" % K) if dup_degree(f) <= 0: return [] I_zero, f = _isolate_zero(f, K, inf, sup, basis=basis, sqf=False) _, factors = dup_sqf_list(f, K) if len(factors) == 1: ((f, k),) = factors I_neg = dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) I_pos = dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast) I_neg = [ ((u, v), k) for u, v in I_neg ] I_pos = [ ((u, v), k) for u, v in I_pos ] else: I_neg, I_pos = _real_isolate_and_disjoin(factors, K, eps=eps, inf=inf, sup=sup, basis=basis, fast=fast) return sorted(I_neg + I_zero + I_pos) def dup_isolate_real_roots_list(polys, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False): """Isolate real roots of a list of square-free polynomial using CF approach. """ if K.is_QQ: K, F, polys = K.get_ring(), K, polys[:] for i, p in enumerate(polys): polys[i] = dup_clear_denoms(p, F, K, convert=True)[1] elif not K.is_ZZ: raise DomainError("isolation of real roots not supported over %s" % K) zeros, factors_dict = False, {} if (inf is None or inf <= 0) and (sup is None or 0 <= sup): zeros, zero_indices = True, {} for i, p in enumerate(polys): j, p = dup_terms_gcd(p, K) if zeros and j > 0: zero_indices[i] = j for f, k in dup_factor_list(p, K)[1]: f = tuple(f) if f not in factors_dict: factors_dict[f] = {i: k} else: factors_dict[f][i] = k factors_list = [] for f, indices in factors_dict.items(): factors_list.append((list(f), indices)) I_neg, I_pos = _real_isolate_and_disjoin(factors_list, K, eps=eps, inf=inf, sup=sup, strict=strict, basis=basis, fast=fast) F = K.get_field() if not zeros or not zero_indices: I_zero = [] else: if not basis: I_zero = [((F.zero, F.zero), zero_indices)] else: I_zero = [((F.zero, F.zero), zero_indices, [K.one, K.zero])] return sorted(I_neg + I_zero + I_pos) def _disjoint_p(M, N, strict=False): """Check if Mobius transforms define disjoint intervals. """ a1, b1, c1, d1 = M a2, b2, c2, d2 = N a1d1, b1c1 = a1*d1, b1*c1 a2d2, b2c2 = a2*d2, b2*c2 if a1d1 == b1c1 and a2d2 == b2c2: return True if a1d1 > b1c1: a1, c1, b1, d1 = b1, d1, a1, c1 if a2d2 > b2c2: a2, c2, b2, d2 = b2, d2, a2, c2 if not strict: return a2*d1 >= c2*b1 or b2*c1 <= d2*a1 else: return a2*d1 > c2*b1 or b2*c1 < d2*a1 def _real_isolate_and_disjoin(factors, K, eps=None, inf=None, sup=None, strict=False, basis=False, fast=False): """Isolate real roots of a list of polynomials and disjoin intervals. """ I_pos, I_neg = [], [] for i, (f, k) in enumerate(factors): for F, M in dup_inner_isolate_positive_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True): I_pos.append((F, M, k, f)) for G, N in dup_inner_isolate_negative_roots(f, K, eps=eps, inf=inf, sup=sup, fast=fast, mobius=True): I_neg.append((G, N, k, f)) for i, (f, M, k, F) in enumerate(I_pos): for j, (g, N, m, G) in enumerate(I_pos[i+1:]): while not _disjoint_p(M, N, strict=strict): f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True) g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True) I_pos[i+j+1] = (g, N, m, G) I_pos[i] = (f, M, k, F) for i, (f, M, k, F) in enumerate(I_neg): for j, (g, N, m, G) in enumerate(I_neg[i+1:]): while not _disjoint_p(M, N, strict=strict): f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True) g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True) I_neg[i+j+1] = (g, N, m, G) I_neg[i] = (f, M, k, F) if strict: for i, (f, M, k, F) in enumerate(I_neg): if not M[0]: while not M[0]: f, M = dup_inner_refine_real_root(f, M, K, steps=1, fast=fast, mobius=True) I_neg[i] = (f, M, k, F) break for j, (g, N, m, G) in enumerate(I_pos): if not N[0]: while not N[0]: g, N = dup_inner_refine_real_root(g, N, K, steps=1, fast=fast, mobius=True) I_pos[j] = (g, N, m, G) break field = K.get_field() I_neg = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_neg ] I_pos = [ (_mobius_to_interval(M, field), k, f) for (_, M, k, f) in I_pos ] if not basis: I_neg = [ ((-v, -u), k) for ((u, v), k, _) in I_neg ] I_pos = [ (( u, v), k) for ((u, v), k, _) in I_pos ] else: I_neg = [ ((-v, -u), k, f) for ((u, v), k, f) in I_neg ] I_pos = [ (( u, v), k, f) for ((u, v), k, f) in I_pos ] return I_neg, I_pos def dup_count_real_roots(f, K, inf=None, sup=None): """Returns the number of distinct real roots of ``f`` in ``[inf, sup]``. """ if dup_degree(f) <= 0: return 0 if not K.has_Field and K.is_Exact: R, K = K, K.get_field() f = dup_convert(f, R, K) sturm = dup_sturm(f, K) if inf is None: signs_inf = dup_sign_variations([ dup_LC(s, K)*(-1)**dup_degree(s) for s in sturm ], K) else: signs_inf = dup_sign_variations([ dup_eval(s, inf, K) for s in sturm ], K) if sup is None: signs_sup = dup_sign_variations([ dup_LC(s, K) for s in sturm ], K) else: signs_sup = dup_sign_variations([ dup_eval(s, sup, K) for s in sturm ], K) count = abs(signs_inf - signs_sup) if inf is not None and not dup_eval(f, inf, K): count += 1 return count OO = 'OO' # Origin of (re, im) coordinate system Q1 = 'Q1' # Quadrant #1 (++): re > 0 and im > 0 Q2 = 'Q2' # Quadrant #2 (-+): re < 0 and im > 0 Q3 = 'Q3' # Quadrant #3 (--): re < 0 and im < 0 Q4 = 'Q4' # Quadrant #4 (+-): re > 0 and im < 0 A1 = 'A1' # Axis #1 (+0): re > 0 and im = 0 A2 = 'A2' # Axis #2 (0+): re = 0 and im > 0 A3 = 'A3' # Axis #3 (-0): re < 0 and im = 0 A4 = 'A4' # Axis #4 (0-): re = 0 and im < 0 _rules_simple = { # A -- CCW --> Q => +1/4 (CCW) (A1, Q1): 1, (A2, Q2): 1, (A3, Q3): 1, (A4, Q4): 1, # A -- CW --> Q => -1/4 (CCW) (A1, Q4): 2, (A2, Q1): 2, (A3, Q2): 2, (A4, Q3): 2, # Q -- CCW --> A => +1/4 (CCW) (Q1, A2): 3, (Q2, A3): 3, (Q3, A4): 3, (Q4, A1): 3, # Q -- CW --> A => -1/4 (CCW) (Q1, A1): 4, (Q2, A2): 4, (Q3, A3): 4, (Q4, A4): 4, # Q -- CCW --> Q => +1/2 (CCW) (Q1, Q2): +5, (Q2, Q3): +5, (Q3, Q4): +5, (Q4, Q1): +5, # Q -- CW --> Q => -1/2 (CW) (Q1, Q4): -5, (Q2, Q1): -5, (Q3, Q2): -5, (Q4, Q3): -5, } _rules_ambiguous = { # A -- CCW --> Q => { +1/4 (CCW), -9/4 (CW) } (A1, OO, Q1): -1, (A2, OO, Q2): -1, (A3, OO, Q3): -1, (A4, OO, Q4): -1, # A -- CW --> Q => { -1/4 (CCW), +7/4 (CW) } (A1, OO, Q4): -2, (A2, OO, Q1): -2, (A3, OO, Q2): -2, (A4, OO, Q3): -2, # Q -- CCW --> A => { +1/4 (CCW), -9/4 (CW) } (Q1, OO, A2): -3, (Q2, OO, A3): -3, (Q3, OO, A4): -3, (Q4, OO, A1): -3, # Q -- CW --> A => { -1/4 (CCW), +7/4 (CW) } (Q1, OO, A1): -4, (Q2, OO, A2): -4, (Q3, OO, A3): -4, (Q4, OO, A4): -4, # A -- OO --> A => { +1/1 (CCW), -1/1 (CW) } (A1, A3): 7, (A2, A4): 7, (A3, A1): 7, (A4, A2): 7, (A1, OO, A3): 7, (A2, OO, A4): 7, (A3, OO, A1): 7, (A4, OO, A2): 7, # Q -- DIA --> Q => { +1/1 (CCW), -1/1 (CW) } (Q1, Q3): 8, (Q2, Q4): 8, (Q3, Q1): 8, (Q4, Q2): 8, (Q1, OO, Q3): 8, (Q2, OO, Q4): 8, (Q3, OO, Q1): 8, (Q4, OO, Q2): 8, # A --- R ---> A => { +1/2 (CCW), -3/2 (CW) } (A1, A2): 9, (A2, A3): 9, (A3, A4): 9, (A4, A1): 9, (A1, OO, A2): 9, (A2, OO, A3): 9, (A3, OO, A4): 9, (A4, OO, A1): 9, # A --- L ---> A => { +3/2 (CCW), -1/2 (CW) } (A1, A4): 10, (A2, A1): 10, (A3, A2): 10, (A4, A3): 10, (A1, OO, A4): 10, (A2, OO, A1): 10, (A3, OO, A2): 10, (A4, OO, A3): 10, # Q --- 1 ---> A => { +3/4 (CCW), -5/4 (CW) } (Q1, A3): 11, (Q2, A4): 11, (Q3, A1): 11, (Q4, A2): 11, (Q1, OO, A3): 11, (Q2, OO, A4): 11, (Q3, OO, A1): 11, (Q4, OO, A2): 11, # Q --- 2 ---> A => { +5/4 (CCW), -3/4 (CW) } (Q1, A4): 12, (Q2, A1): 12, (Q3, A2): 12, (Q4, A3): 12, (Q1, OO, A4): 12, (Q2, OO, A1): 12, (Q3, OO, A2): 12, (Q4, OO, A3): 12, # A --- 1 ---> Q => { +5/4 (CCW), -3/4 (CW) } (A1, Q3): 13, (A2, Q4): 13, (A3, Q1): 13, (A4, Q2): 13, (A1, OO, Q3): 13, (A2, OO, Q4): 13, (A3, OO, Q1): 13, (A4, OO, Q2): 13, # A --- 2 ---> Q => { +3/4 (CCW), -5/4 (CW) } (A1, Q2): 14, (A2, Q3): 14, (A3, Q4): 14, (A4, Q1): 14, (A1, OO, Q2): 14, (A2, OO, Q3): 14, (A3, OO, Q4): 14, (A4, OO, Q1): 14, # Q --> OO --> Q => { +1/2 (CCW), -3/2 (CW) } (Q1, OO, Q2): 15, (Q2, OO, Q3): 15, (Q3, OO, Q4): 15, (Q4, OO, Q1): 15, # Q --> OO --> Q => { +3/2 (CCW), -1/2 (CW) } (Q1, OO, Q4): 16, (Q2, OO, Q1): 16, (Q3, OO, Q2): 16, (Q4, OO, Q3): 16, # A --> OO --> A => { +2/1 (CCW), 0 (CW) } (A1, OO, A1): 17, (A2, OO, A2): 17, (A3, OO, A3): 17, (A4, OO, A4): 17, # Q --> OO --> Q => { +2/1 (CCW), 0 (CW) } (Q1, OO, Q1): 18, (Q2, OO, Q2): 18, (Q3, OO, Q3): 18, (Q4, OO, Q4): 18, } _values = { 1: [(+1, 4)], 2: [(-1, 4)], 3: [(+1, 4)], 4: [(-1, 4)], -1: [(+9, 4), (+1, 4)], -2: [(+7, 4), (-1, 4)], -3: [(+9, 4), (+1, 4)], -4: [(+7, 4), (-1, 4)], +5: [(+1, 2)], -5: [(-1, 2)], 7: [(+1, 1), (-1, 1)], 8: [(+1, 1), (-1, 1)], 9: [(+1, 2), (-3, 2)], 10: [(+3, 2), (-1, 2)], 11: [(+3, 4), (-5, 4)], 12: [(+5, 4), (-3, 4)], 13: [(+5, 4), (-3, 4)], 14: [(+3, 4), (-5, 4)], 15: [(+1, 2), (-3, 2)], 16: [(+3, 2), (-1, 2)], 17: [(+2, 1), ( 0, 1)], 18: [(+2, 1), ( 0, 1)], } def _classify_point(re, im): """Return the half-axis (or origin) on which (re, im) point is located. """ if not re and not im: return OO if not re: if im > 0: return A2 else: return A4 elif not im: if re > 0: return A1 else: return A3 def _intervals_to_quadrants(intervals, f1, f2, s, t, F): """Generate a sequence of extended quadrants from a list of critical points. """ if not intervals: return [] Q = [] if not f1: (a, b), _, _ = intervals[0] if a == b == s: if len(intervals) == 1: if dup_eval(f2, t, F) > 0: return [OO, A2] else: return [OO, A4] else: (a, _), _, _ = intervals[1] if dup_eval(f2, (s+a)/2, F) > 0: Q.extend([OO, A2]) f2_sgn = +1 else: Q.extend([OO, A4]) f2_sgn = -1 intervals = intervals[1:] else: if dup_eval(f2, s, F) > 0: Q.append(A2) f2_sgn = +1 else: Q.append(A4) f2_sgn = -1 for (a, _), indices, _ in intervals: Q.append(OO) if indices[1] % 2 == 1: f2_sgn = -f2_sgn if a != t: if f2_sgn > 0: Q.append(A2) else: Q.append(A4) return Q if not f2: (a, b), _, _ = intervals[0] if a == b == s: if len(intervals) == 1: if dup_eval(f1, t, F) > 0: return [OO, A1] else: return [OO, A3] else: (a, _), _, _ = intervals[1] if dup_eval(f1, (s+a)/2, F) > 0: Q.extend([OO, A1]) f1_sgn = +1 else: Q.extend([OO, A3]) f1_sgn = -1 intervals = intervals[1:] else: if dup_eval(f1, s, F) > 0: Q.append(A1) f1_sgn = +1 else: Q.append(A3) f1_sgn = -1 for (a, _), indices, _ in intervals: Q.append(OO) if indices[0] % 2 == 1: f1_sgn = -f1_sgn if a != t: if f1_sgn > 0: Q.append(A1) else: Q.append(A3) return Q re = dup_eval(f1, s, F) im = dup_eval(f2, s, F) if not re or not im: Q.append(_classify_point(re, im)) if len(intervals) == 1: re = dup_eval(f1, t, F) im = dup_eval(f2, t, F) else: (a, _), _, _ = intervals[1] re = dup_eval(f1, (s+a)/2, F) im = dup_eval(f2, (s+a)/2, F) intervals = intervals[1:] if re > 0: f1_sgn = +1 else: f1_sgn = -1 if im > 0: f2_sgn = +1 else: f2_sgn = -1 sgn = { (+1, +1): Q1, (-1, +1): Q2, (-1, -1): Q3, (+1, -1): Q4, } Q.append(sgn[(f1_sgn, f2_sgn)]) for (a, b), indices, _ in intervals: if a == b: re = dup_eval(f1, a, F) im = dup_eval(f2, a, F) cls = _classify_point(re, im) if cls is not None: Q.append(cls) if 0 in indices: if indices[0] % 2 == 1: f1_sgn = -f1_sgn if 1 in indices: if indices[1] % 2 == 1: f2_sgn = -f2_sgn if not (a == b and b == t): Q.append(sgn[(f1_sgn, f2_sgn)]) return Q def _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=None): """Transform sequences of quadrants to a sequence of rules. """ if exclude is True: edges = [1, 1, 0, 0] corners = { (0, 1): 1, (1, 2): 1, (2, 3): 0, (3, 0): 1, } else: edges = [0, 0, 0, 0] corners = { (0, 1): 0, (1, 2): 0, (2, 3): 0, (3, 0): 0, } if exclude is not None and exclude is not True: exclude = set(exclude) for i, edge in enumerate(['S', 'E', 'N', 'W']): if edge in exclude: edges[i] = 1 for i, corner in enumerate(['SW', 'SE', 'NE', 'NW']): if corner in exclude: corners[((i - 1) % 4, i)] = 1 QQ, rules = [Q_L1, Q_L2, Q_L3, Q_L4], [] for i, Q in enumerate(QQ): if not Q: continue if Q[-1] == OO: Q = Q[:-1] if Q[0] == OO: j, Q = (i - 1) % 4, Q[1:] qq = (QQ[j][-2], OO, Q[0]) if qq in _rules_ambiguous: rules.append((_rules_ambiguous[qq], corners[(j, i)])) else: raise NotImplementedError("3 element rule (corner): " + str(qq)) q1, k = Q[0], 1 while k < len(Q): q2, k = Q[k], k+1 if q2 != OO: qq = (q1, q2) if qq in _rules_simple: rules.append((_rules_simple[qq], 0)) elif qq in _rules_ambiguous: rules.append((_rules_ambiguous[qq], edges[i])) else: raise NotImplementedError("2 element rule (inside): " + str(qq)) else: qq, k = (q1, q2, Q[k]), k+1 if qq in _rules_ambiguous: rules.append((_rules_ambiguous[qq], edges[i])) else: raise NotImplementedError("3 element rule (edge): " + str(qq)) q1 = qq[-1] return rules def _reverse_intervals(intervals): """Reverse intervals for traversal from right to left and from top to bottom. """ return [ ((b, a), indices, f) for (a, b), indices, f in reversed(intervals) ] def _winding_number(T, field): """Compute the winding number of the input polynomial, i.e. the number of roots. """ return int(sum([ field(*_values[t][i]) for t, i in T ]) / field(2)) def dup_count_complex_roots(f, K, inf=None, sup=None, exclude=None): """Count all roots in [u + v*I, s + t*I] rectangle using Collins-Krandick algorithm. """ if not K.is_ZZ and not K.is_QQ: raise DomainError("complex root counting is not supported over %s" % K) if K.is_ZZ: R, F = K, K.get_field() else: R, F = K.get_ring(), K f = dup_convert(f, K, F) if inf is None or sup is None: n, lc = dup_degree(f), abs(dup_LC(f, F)) B = 2*max([ F.quo(abs(c), lc) for c in f ]) if inf is None: (u, v) = (-B, -B) else: (u, v) = inf if sup is None: (s, t) = (+B, +B) else: (s, t) = sup f1, f2 = dup_real_imag(f, F) f1L1F = dmp_eval_in(f1, v, 1, 1, F) f2L1F = dmp_eval_in(f2, v, 1, 1, F) _, f1L1R = dup_clear_denoms(f1L1F, F, R, convert=True) _, f2L1R = dup_clear_denoms(f2L1F, F, R, convert=True) f1L2F = dmp_eval_in(f1, s, 0, 1, F) f2L2F = dmp_eval_in(f2, s, 0, 1, F) _, f1L2R = dup_clear_denoms(f1L2F, F, R, convert=True) _, f2L2R = dup_clear_denoms(f2L2F, F, R, convert=True) f1L3F = dmp_eval_in(f1, t, 1, 1, F) f2L3F = dmp_eval_in(f2, t, 1, 1, F) _, f1L3R = dup_clear_denoms(f1L3F, F, R, convert=True) _, f2L3R = dup_clear_denoms(f2L3F, F, R, convert=True) f1L4F = dmp_eval_in(f1, u, 0, 1, F) f2L4F = dmp_eval_in(f2, u, 0, 1, F) _, f1L4R = dup_clear_denoms(f1L4F, F, R, convert=True) _, f2L4R = dup_clear_denoms(f2L4F, F, R, convert=True) S_L1 = [f1L1R, f2L1R] S_L2 = [f1L2R, f2L2R] S_L3 = [f1L3R, f2L3R] S_L4 = [f1L4R, f2L4R] I_L1 = dup_isolate_real_roots_list(S_L1, R, inf=u, sup=s, fast=True, basis=True, strict=True) I_L2 = dup_isolate_real_roots_list(S_L2, R, inf=v, sup=t, fast=True, basis=True, strict=True) I_L3 = dup_isolate_real_roots_list(S_L3, R, inf=u, sup=s, fast=True, basis=True, strict=True) I_L4 = dup_isolate_real_roots_list(S_L4, R, inf=v, sup=t, fast=True, basis=True, strict=True) I_L3 = _reverse_intervals(I_L3) I_L4 = _reverse_intervals(I_L4) Q_L1 = _intervals_to_quadrants(I_L1, f1L1F, f2L1F, u, s, F) Q_L2 = _intervals_to_quadrants(I_L2, f1L2F, f2L2F, v, t, F) Q_L3 = _intervals_to_quadrants(I_L3, f1L3F, f2L3F, s, u, F) Q_L4 = _intervals_to_quadrants(I_L4, f1L4F, f2L4F, t, v, F) T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4, exclude=exclude) return _winding_number(T, F) def _vertical_bisection(N, a, b, I, Q, F1, F2, f1, f2, F): """Vertical bisection step in Collins-Krandick root isolation algorithm. """ (u, v), (s, t) = a, b I_L1, I_L2, I_L3, I_L4 = I Q_L1, Q_L2, Q_L3, Q_L4 = Q f1L1F, f1L2F, f1L3F, f1L4F = F1 f2L1F, f2L2F, f2L3F, f2L4F = F2 x = (u + s) / 2 f1V = dmp_eval_in(f1, x, 0, 1, F) f2V = dmp_eval_in(f2, x, 0, 1, F) I_V = dup_isolate_real_roots_list([f1V, f2V], F, inf=v, sup=t, fast=True, strict=True, basis=True) I_L1_L, I_L1_R = [], [] I_L2_L, I_L2_R = I_V, I_L2 I_L3_L, I_L3_R = [], [] I_L4_L, I_L4_R = I_L4, _reverse_intervals(I_V) for I in I_L1: (a, b), indices, h = I if a == b: if a == x: I_L1_L.append(I) I_L1_R.append(I) elif a < x: I_L1_L.append(I) else: I_L1_R.append(I) else: if b <= x: I_L1_L.append(I) elif a >= x: I_L1_R.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True) if b <= x: I_L1_L.append(((a, b), indices, h)) else: I_L1_R.append(((a, b), indices, h)) for I in I_L3: (b, a), indices, h = I if a == b: if a == x: I_L3_L.append(I) I_L3_R.append(I) elif a < x: I_L3_L.append(I) else: I_L3_R.append(I) else: if b <= x: I_L3_L.append(I) elif a >= x: I_L3_R.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=x, fast=True) if b <= x: I_L3_L.append(((b, a), indices, h)) else: I_L3_R.append(((b, a), indices, h)) Q_L1_L = _intervals_to_quadrants(I_L1_L, f1L1F, f2L1F, u, x, F) Q_L2_L = _intervals_to_quadrants(I_L2_L, f1V, f2V, v, t, F) Q_L3_L = _intervals_to_quadrants(I_L3_L, f1L3F, f2L3F, x, u, F) Q_L4_L = Q_L4 Q_L1_R = _intervals_to_quadrants(I_L1_R, f1L1F, f2L1F, x, s, F) Q_L2_R = Q_L2 Q_L3_R = _intervals_to_quadrants(I_L3_R, f1L3F, f2L3F, s, x, F) Q_L4_R = _intervals_to_quadrants(I_L4_R, f1V, f2V, t, v, F) T_L = _traverse_quadrants(Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L, exclude=True) T_R = _traverse_quadrants(Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R, exclude=True) N_L = _winding_number(T_L, F) N_R = _winding_number(T_R, F) I_L = (I_L1_L, I_L2_L, I_L3_L, I_L4_L) Q_L = (Q_L1_L, Q_L2_L, Q_L3_L, Q_L4_L) I_R = (I_L1_R, I_L2_R, I_L3_R, I_L4_R) Q_R = (Q_L1_R, Q_L2_R, Q_L3_R, Q_L4_R) F1_L = (f1L1F, f1V, f1L3F, f1L4F) F2_L = (f2L1F, f2V, f2L3F, f2L4F) F1_R = (f1L1F, f1L2F, f1L3F, f1V) F2_R = (f2L1F, f2L2F, f2L3F, f2V) a, b = (u, v), (x, t) c, d = (x, v), (s, t) D_L = (N_L, a, b, I_L, Q_L, F1_L, F2_L) D_R = (N_R, c, d, I_R, Q_R, F1_R, F2_R) return D_L, D_R def _horizontal_bisection(N, a, b, I, Q, F1, F2, f1, f2, F): """Horizontal bisection step in Collins-Krandick root isolation algorithm. """ (u, v), (s, t) = a, b I_L1, I_L2, I_L3, I_L4 = I Q_L1, Q_L2, Q_L3, Q_L4 = Q f1L1F, f1L2F, f1L3F, f1L4F = F1 f2L1F, f2L2F, f2L3F, f2L4F = F2 y = (v + t) / 2 f1H = dmp_eval_in(f1, y, 1, 1, F) f2H = dmp_eval_in(f2, y, 1, 1, F) I_H = dup_isolate_real_roots_list([f1H, f2H], F, inf=u, sup=s, fast=True, strict=True, basis=True) I_L1_B, I_L1_U = I_L1, I_H I_L2_B, I_L2_U = [], [] I_L3_B, I_L3_U = _reverse_intervals(I_H), I_L3 I_L4_B, I_L4_U = [], [] for I in I_L2: (a, b), indices, h = I if a == b: if a == y: I_L2_B.append(I) I_L2_U.append(I) elif a < y: I_L2_B.append(I) else: I_L2_U.append(I) else: if b <= y: I_L2_B.append(I) elif a >= y: I_L2_U.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True) if b <= y: I_L2_B.append(((a, b), indices, h)) else: I_L2_U.append(((a, b), indices, h)) for I in I_L4: (b, a), indices, h = I if a == b: if a == y: I_L4_B.append(I) I_L4_U.append(I) elif a < y: I_L4_B.append(I) else: I_L4_U.append(I) else: if b <= y: I_L4_B.append(I) elif a >= y: I_L4_U.append(I) else: a, b = dup_refine_real_root(h, a, b, F.get_ring(), disjoint=y, fast=True) if b <= y: I_L4_B.append(((b, a), indices, h)) else: I_L4_U.append(((b, a), indices, h)) Q_L1_B = Q_L1 Q_L2_B = _intervals_to_quadrants(I_L2_B, f1L2F, f2L2F, v, y, F) Q_L3_B = _intervals_to_quadrants(I_L3_B, f1H, f2H, s, u, F) Q_L4_B = _intervals_to_quadrants(I_L4_B, f1L4F, f2L4F, y, v, F) Q_L1_U = _intervals_to_quadrants(I_L1_U, f1H, f2H, u, s, F) Q_L2_U = _intervals_to_quadrants(I_L2_U, f1L2F, f2L2F, y, t, F) Q_L3_U = Q_L3 Q_L4_U = _intervals_to_quadrants(I_L4_U, f1L4F, f2L4F, t, y, F) T_B = _traverse_quadrants(Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B, exclude=True) T_U = _traverse_quadrants(Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U, exclude=True) N_B = _winding_number(T_B, F) N_U = _winding_number(T_U, F) I_B = (I_L1_B, I_L2_B, I_L3_B, I_L4_B) Q_B = (Q_L1_B, Q_L2_B, Q_L3_B, Q_L4_B) I_U = (I_L1_U, I_L2_U, I_L3_U, I_L4_U) Q_U = (Q_L1_U, Q_L2_U, Q_L3_U, Q_L4_U) F1_B = (f1L1F, f1L2F, f1H, f1L4F) F2_B = (f2L1F, f2L2F, f2H, f2L4F) F1_U = (f1H, f1L2F, f1L3F, f1L4F) F2_U = (f2H, f2L2F, f2L3F, f2L4F) a, b = (u, v), (s, y) c, d = (u, y), (s, t) D_B = (N_B, a, b, I_B, Q_B, F1_B, F2_B) D_U = (N_U, c, d, I_U, Q_U, F1_U, F2_U) return D_B, D_U def _depth_first_select(rectangles): """Find a rectangle of minimum area for bisection. """ min_area, j = None, None for i, (_, (u, v), (s, t), _, _, _, _) in enumerate(rectangles): area = (s - u)*(t - v) if min_area is None or area < min_area: min_area, j = area, i return rectangles.pop(j) def _rectangle_small_p(a, b, eps): """Return ``True`` if the given rectangle is small enough. """ (u, v), (s, t) = a, b if eps is not None: return s - u < eps and t - v < eps else: return True def dup_isolate_complex_roots_sqf(f, K, eps=None, inf=None, sup=None, blackbox=False): """Isolate complex roots of a square-free polynomial using Collins-Krandick algorithm. """ if not K.is_ZZ and not K.is_QQ: raise DomainError("isolation of complex roots is not supported over %s" % K) if dup_degree(f) <= 0: return [] if K.is_ZZ: R, F = K, K.get_field() else: R, F = K.get_ring(), K f = dup_convert(f, K, F) n, lc = dup_degree(f), abs(dup_LC(f, F)) B = 2*max([ F.quo(abs(c), lc) for c in f ]) (u, v), (s, t) = (-B, F.zero), (B, B) if inf is not None: u = inf if sup is not None: s = sup if v < 0 or t <= v or s <= u: raise ValueError("not a valid complex isolation rectangle") f1, f2 = dup_real_imag(f, F) f1L1 = dmp_eval_in(f1, v, 1, 1, F) f2L1 = dmp_eval_in(f2, v, 1, 1, F) f1L2 = dmp_eval_in(f1, s, 0, 1, F) f2L2 = dmp_eval_in(f2, s, 0, 1, F) f1L3 = dmp_eval_in(f1, t, 1, 1, F) f2L3 = dmp_eval_in(f2, t, 1, 1, F) f1L4 = dmp_eval_in(f1, u, 0, 1, F) f2L4 = dmp_eval_in(f2, u, 0, 1, F) S_L1 = [f1L1, f2L1] S_L2 = [f1L2, f2L2] S_L3 = [f1L3, f2L3] S_L4 = [f1L4, f2L4] I_L1 = dup_isolate_real_roots_list(S_L1, F, inf=u, sup=s, fast=True, strict=True, basis=True) I_L2 = dup_isolate_real_roots_list(S_L2, F, inf=v, sup=t, fast=True, strict=True, basis=True) I_L3 = dup_isolate_real_roots_list(S_L3, F, inf=u, sup=s, fast=True, strict=True, basis=True) I_L4 = dup_isolate_real_roots_list(S_L4, F, inf=v, sup=t, fast=True, strict=True, basis=True) I_L3 = _reverse_intervals(I_L3) I_L4 = _reverse_intervals(I_L4) Q_L1 = _intervals_to_quadrants(I_L1, f1L1, f2L1, u, s, F) Q_L2 = _intervals_to_quadrants(I_L2, f1L2, f2L2, v, t, F) Q_L3 = _intervals_to_quadrants(I_L3, f1L3, f2L3, s, u, F) Q_L4 = _intervals_to_quadrants(I_L4, f1L4, f2L4, t, v, F) T = _traverse_quadrants(Q_L1, Q_L2, Q_L3, Q_L4) N = _winding_number(T, F) if not N: return [] I = (I_L1, I_L2, I_L3, I_L4) Q = (Q_L1, Q_L2, Q_L3, Q_L4) F1 = (f1L1, f1L2, f1L3, f1L4) F2 = (f2L1, f2L2, f2L3, f2L4) rectangles, roots = [(N, (u, v), (s, t), I, Q, F1, F2)], [] while rectangles: N, (u, v), (s, t), I, Q, F1, F2 = _depth_first_select(rectangles) if s - u > t - v: D_L, D_R = _vertical_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F) N_L, a, b, I_L, Q_L, F1_L, F2_L = D_L N_R, c, d, I_R, Q_R, F1_R, F2_R = D_R if N_L >= 1: if N_L == 1 and _rectangle_small_p(a, b, eps): roots.append(ComplexInterval(a, b, I_L, Q_L, F1_L, F2_L, f1, f2, F)) else: rectangles.append(D_L) if N_R >= 1: if N_R == 1 and _rectangle_small_p(c, d, eps): roots.append(ComplexInterval(c, d, I_R, Q_R, F1_R, F2_R, f1, f2, F)) else: rectangles.append(D_R) else: D_B, D_U = _horizontal_bisection(N, (u, v), (s, t), I, Q, F1, F2, f1, f2, F) N_B, a, b, I_B, Q_B, F1_B, F2_B = D_B N_U, c, d, I_U, Q_U, F1_U, F2_U = D_U if N_B >= 1: if N_B == 1 and _rectangle_small_p(a, b, eps): roots.append(ComplexInterval(a, b, I_B, Q_B, F1_B, F2_B, f1, f2, F)) else: rectangles.append(D_B) if N_U >= 1: if N_U == 1 and _rectangle_small_p(c, d, eps): roots.append(ComplexInterval(c, d, I_U, Q_U, F1_U, F2_U, f1, f2, F)) else: rectangles.append(D_U) _roots, roots = sorted(roots, key=lambda r: (r.ax, r.ay)), [] for root in _roots: roots.extend([root.conjugate(), root]) if blackbox: return roots else: return [ r.as_tuple() for r in roots ] def dup_isolate_all_roots_sqf(f, K, eps=None, inf=None, sup=None, fast=False, blackbox=False): """Isolate real and complex roots of a square-free polynomial ``f``. """ return (dup_isolate_real_roots_sqf(f, K, eps=eps, inf=inf, sup=sup, fast=fast, blackbox=blackbox), dup_isolate_complex_roots_sqf(f, K, eps=eps, inf=inf, sup=sup, blackbox=blackbox)) def dup_isolate_all_roots(f, K, eps=None, inf=None, sup=None, fast=False): """Isolate real and complex roots of a non-square-free polynomial ``f``. """ if not K.is_ZZ and not K.is_QQ: raise DomainError("isolation of real and complex roots is not supported over %s" % K) _, factors = dup_sqf_list(f, K) if len(factors) == 1: ((f, k),) = factors real_part, complex_part = dup_isolate_all_roots_sqf(f, K, eps=eps, inf=inf, sup=sup, fast=fast) real_part = [ ((a, b), k) for (a, b) in real_part ] complex_part = [ ((a, b), k) for (a, b) in complex_part ] return real_part, complex_part else: raise NotImplementedError("only trivial square-free polynomials are supported") class RealInterval(object): """A fully qualified representation of a real isolation interval. """ def __init__(self, data, f, dom): """Initialize new real interval with complete information. """ if len(data) == 2: s, t = data self.neg = False if s < 0: if t <= 0: f, s, t, self.neg = dup_mirror(f, dom), -t, -s, True else: raise ValueError("can't refine a real root in (%s, %s)" % (s, t)) a, b, c, d = _mobius_from_interval((s, t), dom.get_field()) f = dup_transform(f, dup_strip([a, b]), dup_strip([c, d]), dom) self.mobius = a, b, c, d else: self.mobius = data[:-1] self.neg = data[-1] self.f, self.dom = f, dom @property def a(self): """Return the position of the left end. """ field = self.dom.get_field() a, b, c, d = self.mobius if not self.neg: return field(a, c) else: return -field(b, d) @property def b(self): """Return the position of the right end. """ field = self.dom.get_field() a, b, c, d = self.mobius if not self.neg: return field(b, d) else: return -field(a, c) @property def dx(self): """Return width of the real isolating interval. """ return self.b - self.a @property def center(self): """Return the center of the real isolating interval. """ return (self.a + self.b)/2 def as_tuple(self): """Return tuple representation of real isolating interval. """ return (self.a, self.b) def __repr__(self): return "(%s, %s)" % (self.a, self.b) def is_disjoint(self, other): """Return ``True`` if two isolation intervals are disjoint. """ return (self.b <= other.a or other.b <= self.a) def _inner_refine(self): """Internal one step real root refinement procedure. """ if self.mobius is None: return self f, mobius = dup_inner_refine_real_root(self.f, self.mobius, self.dom, steps=1, mobius=True) return RealInterval(mobius + (self.neg,), f, self.dom) def refine_disjoint(self, other): """Refine an isolating interval until it is disjoint with another one. """ while not self.is_disjoint(other): self, other = self._inner_refine(), other._inner_refine() return self, other def refine_size(self, dx): """Refine an isolating interval until it is of sufficiently small size. """ while not (self.dx < dx): self = self._inner_refine() return self def refine_step(self, steps=1): """Perform several steps of real root refinement algorithm. """ for _ in xrange(steps): self = self._inner_refine() return self def refine(self): """Perform one step of real root refinement algorithm. """ return self._inner_refine() class ComplexInterval(object): """A fully qualified representation of a complex isolation interval. """ def __init__(self, a, b, I, Q, F1, F2, f1, f2, dom, conj=False): """Initialize new complex interval with complete information. """ self.a, self.b = a, b self.I, self.Q = I, Q self.f1, self.F1 = f1, F1 self.f2, self.F2 = f2, F2 self.dom = dom self.conj = conj @property def ax(self): """Return ``x`` coordinate of south-western corner. """ return self.a[0] @property def ay(self): """Return ``y`` coordinate of south-western corner. """ if not self.conj: return self.a[1] else: return -self.b[1] @property def bx(self): """Return ``x`` coordinate of north-eastern corner. """ return self.b[0] @property def by(self): """Return ``y`` coordinate of north-eastern corner. """ if not self.conj: return self.b[1] else: return -self.a[1] @property def dx(self): """Return width of the complex isolating interval. """ return self.b[0] - self.a[0] @property def dy(self): """Return height of the complex isolating interval. """ return self.b[1] - self.a[1] @property def center(self): """Return the center of the complex isolating interval. """ return ((self.ax + self.bx)/2, (self.ay + self.by)/2) def as_tuple(self): """Return tuple representation of complex isolating interval. """ return ((self.ax, self.ay), (self.bx, self.by)) def __repr__(self): return "(%s, %s) x (%s, %s)" % (self.ax, self.bx, self.ay, self.by) def conjugate(self): """This complex interval really is located in lower half-plane. """ return ComplexInterval(self.a, self.b, self.I, self.Q, self.F1, self.F2, self.f1, self.f2, self.dom, conj=True) def is_disjoint(self, other): """Return ``True`` if two isolation intervals are disjoint. """ return ((self.conj != other.conj) or ((self.bx <= other.ax or other.bx <= self.ax) or (self.by <= other.ay or other.by <= self.ay))) def _inner_refine(self): """Internal one step complex root refinement procedure. """ (u, v), (s, t) = self.a, self.b I, Q = self.I, self.Q f1, F1 = self.f1, self.F1 f2, F2 = self.f2, self.F2 dom = self.dom if s - u > t - v: D_L, D_R = _vertical_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom) if D_L[0] == 1: _, a, b, I, Q, F1, F2 = D_L else: _, a, b, I, Q, F1, F2 = D_R else: D_B, D_U = _horizontal_bisection(1, (u, v), (s, t), I, Q, F1, F2, f1, f2, dom) if D_B[0] == 1: _, a, b, I, Q, F1, F2 = D_B else: _, a, b, I, Q, F1, F2 = D_U return ComplexInterval(a, b, I, Q, F1, F2, f1, f2, dom, self.conj) def refine_disjoint(self, other): """Refine an isolating interval until it is disjoint with another one. """ while not self.is_disjoint(other): self, other = self._inner_refine(), other._inner_refine() return self, other def refine_size(self, dx, dy=None): """Refine an isolating interval until it is of sufficiently small size. """ if dy is None: dy = dx while not (self.dx < dx and self.dy < dy): self = self._inner_refine() return self def refine_step(self, steps=1): """Perform several steps of complex root refinement algorithm. """ for _ in xrange(steps): self = self._inner_refine() return self def refine(self): """Perform one step of complex root refinement algorithm. """ return self._inner_refine() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/orthopolys.py0000644000175000017500000001406412014170666025046 0ustar georgeskgeorgesk"""Efficient functions for generating orthogonal polynomials. """ from sympy import Dummy from sympy.utilities import cythonized from sympy.polys.constructor import construct_domain from sympy.polys.polytools import Poly, PurePoly from sympy.polys.polyclasses import DMP from sympy.polys.densearith import ( dup_mul, dup_mul_ground, dup_lshift, dup_sub ) from sympy.polys.domains import ZZ, QQ @cythonized("n,i") def dup_chebyshevt(n, K): """Low-level implementation of Chebyshev polynomials of the 1st kind. """ seq = [[K.one], [K.one, K.zero]] for i in xrange(2, n+1): a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2), K) seq.append(dup_sub(a, seq[-2], K)) return seq[n] def chebyshevt_poly(n, x=None, **args): """Generates Chebyshev polynomial of the first kind of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate 1st kind Chebyshev polynomial of degree %s" % n) poly = DMP(dup_chebyshevt(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly @cythonized("n,i") def dup_chebyshevu(n, K): """Low-level implementation of Chebyshev polynomials of the 2nd kind. """ seq = [[K.one], [K(2), K.zero]] for i in xrange(2, n+1): a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2), K) seq.append(dup_sub(a, seq[-2], K)) return seq[n] def chebyshevu_poly(n, x=None, **args): """Generates Chebyshev polynomial of the second kind of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate 2nd kind Chebyshev polynomial of degree %s" % n) poly = DMP(dup_chebyshevu(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly @cythonized("n,i") def dup_hermite(n, K): """Low-level implementation of Hermite polynomials. """ seq = [[K.one], [K(2), K.zero]] for i in xrange(2, n+1): a = dup_lshift(seq[-1], 1, K) b = dup_mul_ground(seq[-2], K(i-1), K) c = dup_mul_ground(dup_sub(a, b, K), K(2), K) seq.append(c) return seq[n] def hermite_poly(n, x=None, **args): """Generates Hermite polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Hermite polynomial of degree %s" % n) poly = DMP(dup_hermite(int(n), ZZ), ZZ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly @cythonized("n,i") def dup_legendre(n, K): """Low-level implementation of Legendre polynomials. """ seq = [[K.one], [K.one, K.zero]] for i in xrange(2, n+1): a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2*i-1, i), K) b = dup_mul_ground(seq[-2], K(i-1, i), K) seq.append(dup_sub(a, b, K)) return seq[n] def legendre_poly(n, x=None, **args): """Generates Legendre polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Legendre polynomial of degree %s" % n) poly = DMP(dup_legendre(int(n), QQ), QQ) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly @cythonized("n,i") def dup_laguerre(n, alpha, K): """Low-level implementation of Laguerre polynomials. """ seq = [[K.zero], [K.one]] for i in xrange(1, n+1): a = dup_mul(seq[-1], [-K.one/i, alpha/i + K(2*i-1)/i], K) b = dup_mul_ground(seq[-2], alpha/i + K(i-1)/i, K) seq.append(dup_sub(a, b, K)) return seq[-1] def laguerre_poly(n, x=None, alpha=None, **args): """Generates Laguerre polynomial of degree `n` in `x`. """ if n < 0: raise ValueError("can't generate Laguerre polynomial of degree %s" % n) if alpha is not None: K, alpha = construct_domain(alpha, field=True) # XXX: ground_field=True else: K, alpha = QQ, QQ(0) poly = DMP(dup_laguerre(int(n), alpha, K), K) if x is not None: poly = Poly.new(poly, x) else: poly = PurePoly.new(poly, Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly @cythonized("n,i") def dup_spherical_bessel_fn(n, K): """ Low-level implementation of fn(n, x) """ seq = [[K.one], [K.one, K.zero]] for i in xrange(2, n+1): a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(2*i-1), K) seq.append(dup_sub(a, seq[-2], K)) return dup_lshift(seq[n], 1, K) @cythonized("n,i") def dup_spherical_bessel_fn_minus(n, K): """ Low-level implementation of fn(-n, x) """ seq = [[K.one, K.zero], [K.zero]] for i in xrange(2, n+1): a = dup_mul_ground(dup_lshift(seq[-1], 1, K), K(3 - 2*i), K) seq.append(dup_sub(a, seq[-2], K)) return seq[n] def spherical_bessel_fn(n, x=None, **args): """ Coefficients for the spherical Bessel functions. Those are only needed in the jn() function. The coefficients are calculated from: fn(0, z) = 1/z fn(1, z) = 1/z**2 fn(n-1, z) + fn(n+1, z) == (2*n+1)/z * fn(n, z) Examples: >>> from sympy.polys.orthopolys import spherical_bessel_fn as fn >>> from sympy import Symbol >>> z = Symbol("z") >>> fn(1, z) z**(-2) >>> fn(2, z) -1/z + 3/z**3 >>> fn(3, z) -6/z**2 + 15/z**4 >>> fn(4, z) 1/z - 45/z**3 + 105/z**5 """ from sympy import sympify if n < 0: dup = dup_spherical_bessel_fn_minus(-int(n), ZZ) else: dup = dup_spherical_bessel_fn(int(n), ZZ) poly = DMP(dup, ZZ) if x is not None: poly = Poly.new(poly, 1/x) else: poly = PurePoly.new(poly, 1/Dummy('x')) if not args.get('polys', False): return poly.as_expr() else: return poly wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyfuncs.py0000644000175000017500000001477712014170666024661 0ustar georgeskgeorgesk"""High-level polynomials manipulation functions. """ from sympy.polys.polytools import ( poly_from_expr, parallel_poly_from_expr, Poly) from sympy.polys.polyoptions import allowed_flags from sympy.polys.specialpolys import ( symmetric_poly, interpolating_poly) from sympy.polys.polyerrors import ( PolificationFailed, ComputationFailed, MultivariatePolynomialError) from sympy.utilities import numbered_symbols, take from sympy.core import S, Basic, Add, Mul def symmetrize(F, *gens, **args): """ Rewrite a polynomial in terms of elementary symmetric polynomials. **Examples** >>> from sympy.polys.polyfuncs import symmetrize >>> from sympy.abc import x, y >>> symmetrize(x**2 + y**2) (-2*x*y + (x + y)**2, 0) >>> symmetrize(x**2 + y**2, formal=True) (s1**2 - 2*s2, 0, [(s1, x + y), (s2, x*y)]) >>> symmetrize(x**2 - y**2) (-2*x*y + (x + y)**2, -2*y**2) >>> symmetrize(x**2 - y**2, formal=True) (s1**2 - 2*s2, -2*y**2, [(s1, x + y), (s2, x*y)]) """ allowed_flags(args, ['formal', 'symbols']) iterable = True if not hasattr(F, '__iter__'): iterable = False F = [F] try: F, opt = parallel_poly_from_expr(F, *gens, **args) except PolificationFailed, exc: result = [] for expr in exc.exprs: if expr.is_Number: result.append((expr, S.Zero)) else: raise ComputationFailed('symmetrize', len(F), exc) else: if not iterable: result, = result if not exc.opt.formal: return result else: if iterable: return result, [] else: return result + ([],) polys, symbols = [], opt.symbols gens, dom = opt.gens, opt.domain for i in xrange(0, len(gens)): poly = symmetric_poly(i+1, gens, polys=True) polys.append((symbols.next(), poly.set_domain(dom))) indices = range(0, len(gens) - 1) weights = range(len(gens), 0, -1) result = [] for f in F: symmetric = [] if not f.is_homogeneous: symmetric.append(f.TC()) f -= f.TC() while f: _height, _monom, _coeff = -1, None, None for i, (monom, coeff) in enumerate(f.terms()): if all(monom[i] >= monom[i+1] for i in indices): height = max([ n*m for n, m in zip(weights, monom) ]) if height > _height: _height, _monom, _coeff = height, monom, coeff if _height != -1: monom, coeff = _monom, _coeff else: break exponents = [] for m1, m2 in zip(monom, monom[1:] + (0,)): exponents.append(m1 - m2) term = [ s**n for (s, _), n in zip(polys, exponents) ] poly = [ p**n for (_, p), n in zip(polys, exponents) ] symmetric.append(Mul(coeff, *term)) product = poly[0].mul(coeff) for p in poly[1:]: product = product.mul(p) f -= product result.append((Add(*symmetric), f.as_expr())) polys = [ (s, p.as_expr()) for s, p in polys ] if not opt.formal: for i, (sym, non_sym) in enumerate(result): result[i] = (sym.subs(polys), non_sym) if not iterable: result, = result if not opt.formal: return result else: if iterable: return result, polys else: return result + (polys,) def horner(f, *gens, **args): """ Rewrite a polynomial in Horner form. **Examples** >>> from sympy.polys.polyfuncs import horner >>> from sympy.abc import x, y, a, b, c, d, e >>> horner(9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5) x*(x*(x*(9*x + 8) + 7) + 6) + 5 >>> horner(a*x**4 + b*x**3 + c*x**2 + d*x + e) e + x*(d + x*(c + x*(a*x + b))) >>> f = 4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y >>> horner(f, wrt=x) x*(x*y*(4*y + 2) + y*(2*y + 1)) >>> horner(f, wrt=y) y*(x*y*(4*x + 2) + x*(2*x + 1)) """ allowed_flags(args, []) try: F, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: return exc.expr form, gen = S.Zero, F.gen if F.is_univariate: for coeff in F.all_coeffs(): form = form*gen + coeff else: F, gens = Poly(F, gen), gens[1:] for coeff in F.all_coeffs(): form = form*gen + horner(coeff, *gens, **args) return form def interpolate(data, x): """ Construct an interpolating polynomial for the data points. **Examples** >>> from sympy.polys.polyfuncs import interpolate >>> from sympy.abc import x >>> interpolate([1, 4, 9, 16], x) x**2 >>> interpolate([(1, 1), (2, 4), (3, 9)], x) x**2 >>> interpolate([(1, 2), (2, 5), (3, 10)], x) x**2 + 1 >>> interpolate({1: 2, 2: 5, 3: 10}, x) x**2 + 1 """ n = len(data) if isinstance(data, dict): X, Y = zip(*data.items()) else: if isinstance(data[0], tuple): X, Y = zip(*data) else: X = range(1, n+1) Y = list(data) poly = interpolating_poly(n, x, X, Y) return poly.expand() def viete(f, roots=None, *gens, **args): """ Generate Viete's formulas for ``f``. **Examples** >>> from sympy.polys.polyfuncs import viete >>> from sympy import symbols >>> x, a, b, c, r1, r2 = symbols('x,a:c,r1:3') >>> viete(a*x**2 + b*x + c, [r1, r2], x) [(r1 + r2, -b/a), (r1*r2, c/a)] """ allowed_flags(args, []) if isinstance(roots, Basic): gens, roots = (roots,) + gens, None try: f, opt = poly_from_expr(f, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('viete', 1, exc) if f.is_multivariate: raise MultivariatePolynomialError("multivariate polynomials are not allowed") n = f.degree() if n < 1: raise ValueError("can't derive Viete's formulas for a constant polynomial") if roots is None: roots = numbered_symbols('r', start=1) roots = take(roots, n) if n != len(roots): raise ValueError("required %s roots, got %s" % (n, len(roots))) lc, coeffs = f.LC(), f.all_coeffs() result, sign = [], -1 for i, coeff in enumerate(coeffs[1:]): poly = symmetric_poly(i+1, roots) coeff = sign*(coeff/lc) result.append((poly, coeff)) sign = -sign return result wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/0000755000175000017500000000000012014170666023677 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/pythonrationaltype.py0000644000175000017500000001172412014170666030233 0ustar georgeskgeorgesk"""Rational number type based on Python integers. """ from sympy.core.numbers import igcd import operator class PythonRationalType(object): """ Rational number type based on Python integers. **Examples** >>> from sympy.polys.domains import PythonRationalType >>> PythonRationalType(1) 1/1 >>> PythonRationalType(2, 3) 2/3 >>> PythonRationalType(14, 10) 7/5 """ __slots__ = ['p', 'q'] def __init__(self, p, q=None): if q is None: self.p = p self.q = 1 else: if not q: raise ZeroDivisionError('rational number') elif q < 0: p, q = -p, -q g = igcd(p, q) self.p = p//g self.q = q//g @classmethod def new(cls, p, q): obj = object.__new__(cls) obj.p = p obj.q = q return obj def __hash__(self): if self.q == 1: return hash(self.p) else: return hash((self.p, self.q)) def __repr__(self): return "%s(%d, %d)" % (self.__class__.__name__, self.p, self.q) def __str__(self): return "%d/%d" % (self.p, self.q) def __int__(self): return int(float(self.p)/self.q) def __float__(self): return float(self.p)/self.q def __abs__(self): return self.new(abs(self.p), self.q) def __pos__(self): return self.new(+self.p, self.q) def __neg__(self): return self.new(-self.p, self.q) def __add__(self, other): if isinstance(other, PythonRationalType): p = self.p*other.q + self.q*other.p q = self.q*other.q elif isinstance(other, (int, long)): p = self.p + self.q*other q = self.q else: return NotImplemented return self.__class__(p, q) def __radd__(self, other): if not isinstance(other, (int, long)): return NotImplemented p = self.p + self.q*other q = self.q return self.__class__(p, q) def __sub__(self, other): if isinstance(other, PythonRationalType): p = self.p*other.q - self.q*other.p q = self.q*other.q elif isinstance(other, (int, long)): p = self.p - self.q*other q = self.q else: return NotImplemented return self.__class__(p, q) def __rsub__(self, other): if not isinstance(other, (int, long)): return NotImplemented p = self.q*other - self.p q = self.q return self.__class__(p, q) def __mul__(self, other): if isinstance(other, PythonRationalType): p = self.p*other.p q = self.q*other.q elif isinstance(other, (int, long)): p = self.p*other q = self.q else: return NotImplemented return self.__class__(p, q) def __rmul__(self, other): if not isinstance(other, (int, long)): return NotImplemented p = self.p*other q = self.q return self.__class__(p, q) def __div__(self, other): if isinstance(other, PythonRationalType): p = self.p*other.q q = self.q*other.p elif isinstance(other, (int, long)): p = self.p q = self.q*other else: return NotImplemented return self.__class__(p, q) __truediv__ = __div__ def __rdiv__(self, other): if not isinstance(other, (int, long)): return NotImplemented p = self.q*other q = self.p return self.__class__(p, q) __rtruediv__ = __rdiv__ def __mod__(self, other): return self.__class__(0) def __divmod__(self, other): return (self//other, self%other) def __pow__(self, exp): p, q = self.p, self.q if exp < 0: p, q, exp = q, p, -exp return self.new(p**exp, q**exp) def __nonzero__(self): return self.p != 0 def __eq__(self, other): if isinstance(other, PythonRationalType): return self.q == other.q and self.p == other.p elif isinstance(other, (int, long)): return self.q == 1 and self.p == other else: return False def __ne__(self, other): return not self.__eq__(other) def _cmp(self, other, op): try: diff = self - other except TypeError: return NotImplemented else: return op(diff.p, 0) def __lt__(self, other): return self._cmp(other, operator.lt) def __le__(self, other): return self._cmp(other, operator.le) def __gt__(self, other): return self._cmp(other, operator.gt) def __ge__(self, other): return self._cmp(other, operator.ge) @property def numer(self): return self.p @property def denom(self): return self.q numerator = numer denominator = denom wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/groundtypes.py0000644000175000017500000000354012014170666026636 0ustar georgeskgeorgesk"""Ground types for various mathematical domains in SymPy. """ from sympy.external import import_module HAS_GMPY = True # Versions of gmpy prior to 1.03 do not work correctly with int(largempz) # For example, int(gmpy.mpz(2**256)) would raise OverflowError. # See issue 1881. gmpy = import_module('gmpy', min_module_version='1.03', module_version_attr='version', module_version_attr_call_args=()) HAS_GMPY = bool(gmpy) from __builtin__ import ( int as PythonIntegerType, float as PythonRealType, complex as PythonComplexType, ) from pythonrationaltype import PythonRationalType def python_factorial(n): from sympy.functions.combinatorial.factorials import factorial return int(factorial(n)) from sympy.core.numbers import ( igcdex as python_gcdex, igcd as python_gcd, ilcm as python_lcm, ) from sympy import ( Float as SymPyRealType, Integer as SymPyIntegerType, Rational as SymPyRationalType, ) if HAS_GMPY: from gmpy import ( mpz as GMPYIntegerType, mpq as GMPYRationalType, fac as gmpy_factorial, numer as gmpy_numer, denom as gmpy_denom, gcdext as gmpy_gcdex, gcd as gmpy_gcd, lcm as gmpy_lcm, sqrt as gmpy_sqrt, ) else: class GMPYIntegerType(object): def __init__(self, obj): pass class GMPYRationalType(object): def __init__(self, obj): pass gmpy_factorial = None gmpy_numer = None gmpy_denom = None gmpy_gcdex = None gmpy_gcd = None gmpy_lcm = None gmpy_sqrt = None from sympy.mpmath import ( mpf as MPmathRealType, mpc as MPmathComplexType, mpi as MPmathIntervalType, ) from sympy.mpmath.libmp.libmpf import isqrt def python_sqrt(a): return int(isqrt(a)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/rationalfield.py0000644000175000017500000000172412014170666027072 0ustar georgeskgeorgesk"""Implementation of :class:`RationalField` class. """ from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero class RationalField(Field, CharacteristicZero, SimpleDomain): """General class for rational fields. """ is_QQ = True rep = 'QQ' is_Numerical = True has_assoc_Ring = True has_assoc_Field = True def get_ring(self): """Returns a ring associated with `self`. """ from sympy.polys.domains import ZZ return ZZ def algebraic_field(self, *extension): """Returns an algebraic field, i.e. `QQ(alpha, ...)`. """ from sympy.polys.domains import AlgebraicField return AlgebraicField(self, *extension) def from_AlgebraicField(K1, a, K0): """Convert a `ANP` object to `dtype`. """ if a.is_ground: return K1.convert(a.LC(), K0.dom) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/pythonintegerring.py0000644000175000017500000000607512014170666030040 0ustar georgeskgeorgesk"""Implementaton of :class:`PythonIntegerRing` class. """ from sympy.polys.domains.integerring import IntegerRing from sympy.polys.domains.groundtypes import ( PythonIntegerType, SymPyIntegerType, python_sqrt, python_factorial, python_gcdex, python_gcd, python_lcm, ) from sympy.polys.polyerrors import CoercionFailed class PythonIntegerRing(IntegerRing): """Integer ring based on Python's `int` type. """ dtype = PythonIntegerType zero = dtype(0) one = dtype(1) alias = 'ZZ_python' def __init__(self): """Allow instantiation of this domain. """ def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyIntegerType(a) def from_sympy(self, a): """Convert SymPy's Integer to `dtype`. """ if a.is_Integer: return PythonIntegerType(a.p) elif a.is_Float and int(a) == a: return PythonIntegerType(int(a)) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0): """Convert `ModularInteger(int)` to Python's `int`. """ return a.to_int() def from_ZZ_python(K1, a, K0): """Convert Python's `int` to Python's `int`. """ return a def from_QQ_python(K1, a, K0): """Convert Python's `Fraction` to Python's `int`. """ if a.denominator == 1: return a.numerator def from_FF_sympy(K1, a, K0): """Convert `ModularInteger(Integer)` to Python's `int`. """ return a.to_int().p def from_ZZ_sympy(K1, a, K0): """Convert SymPy's `Integer` to Python's `int`. """ return a.p def from_QQ_sympy(K1, a, K0): """Convert SymPy's `Rational` to Python's `int`. """ if a.q == 1: return a.p def from_FF_gmpy(K1, a, K0): """Convert `ModularInteger(mpz)` to Python's `int`. """ return PythonIntegerType(a.to_int()) def from_ZZ_gmpy(K1, a, K0): """Convert GMPY's `mpz` to Python's `int`. """ return PythonIntegerType(a) def from_QQ_gmpy(K1, a, K0): """Convert GMPY's `mpq` to Python's `int`. """ if a.denom() == 1: return PythonIntegerType(a.numer()) def from_RR_sympy(K1, a, K0): """Convert SymPy's `Float` to Python's `int`. """ p, q = K0.as_integer_ratio(a) if q == 1: return PythonIntegerType(p) def from_RR_mpmath(K1, a, K0): """Convert mpmath's `mpf` to Python's `int`. """ p, q = K0.as_integer_ratio(a) if q == 1: return PythonIntegerType(p) def gcdex(self, a, b): """Compute extended GCD of `a` and `b`. """ return python_gcdex(a, b) def gcd(self, a, b): """Compute GCD of `a` and `b`. """ return python_gcd(a, b) def lcm(self, a, b): """Compute LCM of `a` and `b`. """ return python_lcm(a, b) def sqrt(self, a): """Compute square root of `a`. """ return python_sqrt(a) def factorial(self, a): """Compute factorial of `a`. """ return python_factorial(a) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/mpmathcomplexdomain.py0000644000175000017500000000041212014170666030314 0ustar georgeskgeorgesk"""Implementation of :class:`MPmathComplexDomain` class. """ from sympy.polys.domains.realdomain import RealDomain class MPmathComplexDomain(RealDomain): # XXX: tmp solution """Complex domain. """ alias = 'CC_mpmath' def __init__(self): pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/gmpyfinitefield.py0000644000175000017500000000047612014170666027437 0ustar georgeskgeorgesk"""Implementation of :class:`GMPYFiniteField` class. """ from sympy.polys.domains.finitefield import FiniteField from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing class GMPYFiniteField(FiniteField): """Finite field based on Python's integers. """ dom = GMPYIntegerRing() alias = 'FF_gmpy' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/domain.py0000644000175000017500000003351512014170666025527 0ustar georgeskgeorgesk"""Implementation of :class:`Domain` class. """ from sympy.core import Basic, sympify from sympy.polys.polyerrors import ( UnificationFailed, CoercionFailed, DomainError, ) from sympy.polys.domains.groundtypes import python_factorial class Domain(object): """Represents an abstract domain. """ dtype = None zero = None one = None has_Ring = False has_Field = False has_assoc_Ring = False has_assoc_Field = False is_ZZ = False is_QQ = False is_FF = False is_CC = False is_Poly = False is_Frac = False is_Exact = True is_Numerical = False is_Algebraic = False is_Simple = False is_Composite = False has_CharacteristicZero = False is_EX = False rep = None alias = None def __init__(self): raise NotImplementedError def __str__(self): return self.rep def __repr__(self): return str(self) def __hash__(self): return hash((self.__class__.__name__, self.dtype)) def __call__(self, *args): """Construct an element of `self` domain from `args`. """ return self.dtype(*args) def normal(self, *args): return self.dtype(*args) def convert(K1, a, K0=None): """Convert an object `a` from `K0` to `K1`. """ if K0 is not None: if K0.alias is not None: method = "from_" + K0.alias else: method = "from_" + K0.__class__.__name__ _convert = getattr(K1, method) if _convert is not None: result = _convert(a, K0) if result is not None: return result raise CoercionFailed("can't convert %s of type %s to %s" % (a, K0, K1)) else: try: if K1.of_type(a): return a if type(a) is int: return K1(a) if type(a) is long: return K1(a) if K1.is_Numerical and getattr(a, 'is_ground', False): return K1.convert(a.LC()) a = sympify(a) if isinstance(a, Basic): return K1.from_sympy(a) except (TypeError, ValueError): pass raise CoercionFailed("can't convert %s to type %s" % (a, K1)) def of_type(self, a): """Check if `a` is of type `dtype`. """ return type(a) is type(self.one) def __contains__(self, a): """Check if `a` belongs to this domain. """ try: self.convert(a) except CoercionFailed: return False return True def to_sympy(self, a): """Convert `a` to a SymPy object. """ raise NotImplementedError def from_sympy(self, a): """Convert a SymPy object to `dtype`. """ raise NotImplementedError def from_FF_python(K1, a, K0): """Convert `ModularInteger(int)` to `dtype`. """ return None def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return None def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return None def from_FF_sympy(K1, a, K0): """Convert `ModularInteger(Integer)` to `dtype`. """ return None def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return None def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return None def from_FF_gmpy(K1, a, K0): """Convert `ModularInteger(mpz)` to `dtype`. """ return None def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return None def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return None def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return None def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return None def from_AlgebraicField(K1, a, K0): """Convert a `ANP` object to `dtype`. """ return None def from_PolynomialRing(K1, a, K0): """Convert a `DMP` object to `dtype`. """ if a.degree() <= 0: return K1.convert(a.LC(), K0.dom) def from_FractionField(K1, a, K0): """Convert a `DMF` object to `dtype`. """ return None def from_ExpressionDomain(K1, a, K0): """Convert a `EX` object to `dtype`. """ return K1.from_sympy(a.ex) def unify(K0, K1, gens=None): """Returns a maximal domain containg `K0` and `K1`. """ if gens is not None: if (K0.is_Composite and (set(K0.gens) & set(gens))) or (K1.is_Composite and (set(K1.gens) & set(gens))): raise UnificationFailed("can't unify %s with %s, given %s generators" % (K0, K1, tuple(gens))) if K0 == K1: return K0 if not K0.has_CharacteristicZero: if not K1.has_CharacteristicZero: if K0.mod == K1.mod and K0.dom == K1.dom: return K0 elif K1.is_ZZ: return K0 raise UnificationFailed("can't unify %s with %s" % (K0, K1)) if not K1.has_CharacteristicZero: if K0.is_ZZ: return K1 else: raise UnificationFailed("can't unify %s with %s" % (K0, K1)) if K0.is_EX: return K0 if K1.is_EX: return K1 if not K0.is_Exact: return K0 if not K1.is_Exact: return K1 if K0.is_Composite: if K1.is_Composite: if K0.gens == K1.gens: if K0.has_Field and K1.has_Field: if K0.dom.has_Field: return K0 else: return K1 elif K0.has_Field: if K0.dom == K1.dom: return K0 elif K1.has_Field: if K0.dom == K1.dom: return K1 else: if K0.dom.has_Field: return K0 else: return K1 else: gens = set(K0.gens + K1.gens) try: gens = sorted(gens) except TypeError: gens = list(gens) if K0.has_Field and K1.has_Field: if K0.dom.has_Field: return K0.__class__(K0.dom, *gens) else: return K1.__class__(K1.dom, *gens) elif K0.has_Field: if K0.dom == K1.dom: return K0.__class__(K0.dom, *gens) elif K1.has_Field: if K0.dom == K1.dom: return K1.__class__(K1.dom, *gens) else: if K0.dom.has_Field: return K0.__class__(K0.dom, *gens) else: return K1.__class__(K1.dom, *gens) elif K1.is_Algebraic: return K0.__class__(K1.unify(K0.dom), *K0.gens) else: if K0.has_Field: if K0.dom == K1: return K0 else: if K0.dom.has_Field: return K0 else: return K0.__class__(K1, *K0.gens) elif K0.is_Algebraic: if K1.is_Composite: return K1.__class__(K0.unify(K1.dom), *K1.gens) elif K1.is_Algebraic: raise NotImplementedError("unification of different algebraic extensions") elif K1.is_ZZ or K1.is_QQ: return K0 else: raise UnificationFailed("can't unify %s with %s" % (K0, K1)) else: if K1.is_Composite: if K1.has_Field: if K0 == K1.dom: return K1 else: if K1.dom.has_Field: return K1 else: return K1.__class__(K0, *K1.gens) elif K1.is_Algebraic: if K0.is_ZZ or K0.is_QQ: return K1 else: raise UnificationFailed("can't unify %s with %s" % (K0, K1)) else: if K0.has_Field: return K0 else: return K1 from sympy.polys.domains import EX return EX def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return self.dtype == other.dtype def __ne__(self, other): """Returns `False` if two domains are equivalent. """ return self.dtype != other.dtype def map(self, seq): """Rersively apply ``self`` to all elements of ``seq``. """ result = [] for elt in seq: if isinstance(elt, list): result.append(self.map(elt)) else: result.append(self(elt)) return result def get_ring(self): """Returns a ring associated with `self`. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with `self`. """ raise DomainError('there is no field associated with %s' % self) def get_exact(self): """Returns an exact domain associated with `self`. """ return self def float_domain(self): return FF def complex_domain(self): return CC def __getitem__(self, gens): """The mathematical way do make a polynomial ring. """ if hasattr(gens, '__iter__'): return self.poly_ring(*gens) else: return self.poly_ring(gens) def poly_ring(self, *gens): """Returns a polynomial ring, i.e. `K[X]`. """ from sympy.polys.domains import PolynomialRing return PolynomialRing(self, *gens) def frac_field(self, *gens): """Returns a fraction field, i.e. `K(X)`. """ from sympy.polys.domains import FractionField return FractionField(self, *gens) def algebraic_field(self, *extension): """Returns an algebraic field, i.e. `K(alpha, ...)`. """ raise DomainError("can't create algebraic field over %s" % self) def inject(self, *gens): """Inject generators into this domain. """ raise NotImplementedError def is_zero(self, a): """Returns True if `a` is zero. """ return not a def is_one(self, a): """Returns True if `a` is one. """ return a == self.one def is_positive(self, a): """Returns True if `a` is positive. """ return a > 0 def is_negative(self, a): """Returns True if `a` is negative. """ return a < 0 def is_nonpositive(self, a): """Returns True if `a` is non-positive. """ return a <= 0 def is_nonnegative(self, a): """Returns True if `a` is non-negative. """ return a >= 0 def abs(self, a): """Absolute value of `a`, implies `__abs__`. """ return abs(a) def neg(self, a): """Returns `a` negated, implies `__neg__`. """ return -a def pos(self, a): """Returns `a` positive, implies `__pos__`. """ return +a def add(self, a, b): """Sum of `a` and `b`, implies `__add__`. """ return a + b def sub(self, a, b): """Difference of `a` and `b`, implies `__sub__`. """ return a - b def mul(self, a, b): """Product of `a` and `b`, implies `__mul__`. """ return a * b def pow(self, a, b): """Raise `a` to power `b`, implies `__pow__`. """ return a ** b def exquo(self, a, b): """Exact quotient of `a` and `b`, implies something. """ raise NotImplementedError def quo(self, a, b): """Quotient of `a` and `b`, implies something. """ raise NotImplementedError def rem(self, a, b): """Remainder of `a` and `b`, implies `__mod__`. """ raise NotImplementedError def div(self, a, b): """Division of `a` and `b`, implies something. """ raise NotImplementedError def invert(self, a, b): """Returns inversion of `a mod b`, implies something. """ raise NotImplementedError def revert(self, a): """Returns `a**(-1)` if possible. """ raise NotImplementedError def numer(self, a): """Returns numerator of `a`. """ raise NotImplementedError def denom(self, a): """Returns denominator of `a`. """ raise NotImplementedError def gcdex(self, a, b): """Extended GCD of `a` and `b`. """ raise NotImplementedError def gcd(self, a, b): """Returns GCD of `a` and `b`. """ raise NotImplementedError def lcm(self, a, b): """Returns LCM of `a` and `b`. """ raise NotImplementedError def log(self, a, b): """Returns b-base logarithm of `a`. """ raise NotImplementedError def sqrt(self, a): """Returns square root of `a`. """ raise NotImplementedError def evalf(self, a, **args): """Returns numerical approximation of `a`. """ return self.to_sympy(a).evalf(**args) def real(self, a): return a def imag(self, a): return self.zero def characteristic(self): """Return the characteristic of this domain. """ raise NotImplementedError('characteristic()') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/algebraicfield.py0000644000175000017500000001104012014170666027162 0ustar georgeskgeorgesk"""Implementation of :class:`AlgebraicField` class. """ from sympy.core import sympify from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.polyclasses import ANP from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed class AlgebraicField(Field, CharacteristicZero, SimpleDomain): """A class for representing algebraic number fields. """ dtype = ANP is_Numerical = True is_Algebraic = True has_assoc_Ring = False has_assoc_Field = True def __init__(self, dom, *ext): if not dom.is_QQ: raise DomainError("ground domain must be a rational field") from sympy.polys.numberfields import to_number_field self.ext = to_number_field(ext) self.mod = self.ext.minpoly.rep self.dom = dom self.gens = (self.ext,) self.unit = self([dom(1), dom(0)]) self.zero = self.dtype.zero(self.mod.rep, dom) self.one = self.dtype.one(self.mod.rep, dom) def __str__(self): return str(self.dom) + '<' + str(self.ext) + '>' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.dom, self.ext)) def __call__(self, a): """Construct an element of `self` domain from `a`. """ return ANP(a, self.mod.rep, self.dom) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ if self.dtype == other.dtype: return self.ext == other.ext else: return False def __ne__(self, other): """Returns `False` if two domains are equivalent. """ if self.dtype == other.dtype: return self.ext != other.ext else: return True def algebraic_field(self, *extension): """Returns an algebraic field, i.e. `QQ(alpha, ...)`. """ return AlgebraicField(self.dom, *((self.ext,) + extension)) def to_sympy(self, a): """Convert `a` to a SymPy object. """ from sympy.polys.numberfields import AlgebraicNumber return AlgebraicNumber(self.ext, a).as_expr() def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ try: return self([self.dom.from_sympy(a)]) except CoercionFailed: pass from sympy.polys.numberfields import to_number_field try: return self(to_number_field(a, self.ext).native_coeffs()) except (NotAlgebraic, IsomorphismFailed): raise CoercionFailed("%s is not a valid algebraic number in %s" % (a, self)) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def get_ring(self): """Returns a ring associated with `self`. """ raise DomainError('there is no ring associated with %s' % self) def is_positive(self, a): """Returns True if `a` is positive. """ return self.dom.is_positive(a.LC()) def is_negative(self, a): """Returns True if `a` is negative. """ return self.dom.is_negative(a.LC()) def is_nonpositive(self, a): """Returns True if `a` is non-positive. """ return self.dom.is_nonpositive(a.LC()) def is_nonnegative(self, a): """Returns True if `a` is non-negative. """ return self.dom.is_nonnegative(a.LC()) def numer(self, a): """Returns numerator of `a`. """ return a def denom(self, a): """Returns denominator of `a`. """ return self.one wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/pythonfinitefield.py0000644000175000017500000000051212014170666027773 0ustar georgeskgeorgesk"""Implementation of :class:`PythonFiniteField` class. """ from sympy.polys.domains.finitefield import FiniteField from sympy.polys.domains.pythonintegerring import PythonIntegerRing class PythonFiniteField(FiniteField): """Finite field based on Python's integers. """ dom = PythonIntegerRing() alias = 'FF_python' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/mpmathrealdomain.py0000644000175000017500000000267312014170666027603 0ustar georgeskgeorgesk"""Implementation of :class:`MPmathRealDomain` class. """ from sympy.polys.domains.realdomain import RealDomain from sympy.polys.domains.groundtypes import MPmathRealType class MPmathRealDomain(RealDomain): """Domain for real numbers based on mpmath mpf type. """ dtype = MPmathRealType zero = dtype(0) one = dtype(1) alias = 'RR_mpmath' def __init__(self): pass def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return MPmathRealType(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return MPmathRealType(a.numerator) / a.denominator def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return MPmathRealType(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return MPmathRealType(a.p) / a.q def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return MPmathRealType(int(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return MPmathRealType(int(a.numer())) / int(a.denom()) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return MPmathRealType(a) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return a wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/gmpyintegerring.py0000644000175000017500000000614512014170666027471 0ustar georgeskgeorgesk"""Implementaton of :class:`GMPYIntegerRing` class. """ from sympy.polys.domains.integerring import IntegerRing from sympy.polys.domains.groundtypes import ( GMPYIntegerType, SymPyIntegerType, gmpy_numer, gmpy_denom, gmpy_factorial, gmpy_gcdex, gmpy_gcd, gmpy_lcm, gmpy_sqrt, ) from sympy.polys.polyerrors import CoercionFailed class GMPYIntegerRing(IntegerRing): """Integer ring based on GMPY's `mpz` type. """ dtype = GMPYIntegerType zero = dtype(0) one = dtype(1) alias = 'ZZ_gmpy' def __init__(self): """Allow instantiation of this domain. """ def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyIntegerType(int(a)) def from_sympy(self, a): """Convert SymPy's Integer to `dtype`. """ if a.is_Integer: return GMPYIntegerType(a.p) elif a.is_Float and int(a) == a: return GMPYIntegerType(int(a)) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0): """Convert `ModularInteger(int)` to GMPY's `mpz`. """ return GMPYIntegerType(a.to_int()) def from_ZZ_python(K1, a, K0): """Convert Python's `int` to GMPY's `mpz`. """ return GMPYIntegerType(a) def from_QQ_python(K1, a, K0): """Convert Python's `Fraction` to GMPY's `mpz`. """ if a.denominator == 1: return GMPYIntegerType(a.numerator) def from_FF_sympy(K1, a, K0): """Convert `ModularInteger(Integer)` to GMPY's `mpz`. """ return GMPYIntegerType(a.to_int().p) def from_ZZ_sympy(K1, a, K0): """Convert SymPy's `Integer` to GMPY's `mpz`. """ return GMPYIntegerType(a.p) def from_QQ_sympy(K1, a, K0): """Convert SymPy's `Rational` to GMPY's `mpz`. """ if a.q == 1: return GMPYIntegerType(a.p) def from_FF_gmpy(K1, a, K0): """Convert `ModularInteger(mpz)` to GMPY's `mpz`. """ return a.to_int() def from_ZZ_gmpy(K1, a, K0): """Convert GMPY's `mpz` to GMPY's `mpz`. """ return a def from_QQ_gmpy(K1, a, K0): """Convert GMPY `mpq` to GMPY's `mpz`. """ if a.denom() == 1: return a.numer() def from_RR_sympy(K1, a, K0): """Convert SymPy's `Float` to GMPY's `mpz`. """ p, q = K0.as_integer_ratio(a) if q == 1: return GMPYIntegerType(p) def from_RR_mpmath(K1, a, K0): """Convert mpmath's `mpf` to GMPY's `mpz`. """ p, q = K0.as_integer_ratio(a) if q == 1: return GMPYIntegerType(p) def gcdex(self, a, b): """Compute extended GCD of `a` and `b`. """ h, s, t = gmpy_gcdex(a, b) return s, t, h def gcd(self, a, b): """Compute GCD of `a` and `b`. """ return gmpy_gcd(a, b) def lcm(self, a, b): """Compute LCM of `a` and `b`. """ return gmpy_lcm(a, b) def sqrt(self, a): """Compute square root of `a`. """ return gmpy_sqrt(a) def factorial(self, a): """Compute factorial of `a`. """ return gmpy_factorial(a) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/sympyrationalfield.py0000644000175000017500000000562012014170666030173 0ustar georgeskgeorgesk"""Implementaton of :class:`SymPyRationalField` class. """ from sympy.polys.domains.rationalfield import RationalField from sympy.polys.domains.groundtypes import SymPyIntegerType from sympy.polys.domains.groundtypes import SymPyRationalType from sympy.polys.polyerrors import CoercionFailed from sympy import ( Integer as sympy_int, Rational as sympy_rat, ) class SymPyRationalField(RationalField): """Rational field based on SymPy Rational class. """ dtype = SymPyRationalType zero = dtype(0) one = dtype(1) alias = 'QQ_sympy' def __init__(self): pass def of_type(self, a): """ Check if ``a`` is of type ``Rational``. Example ======= >>> from sympy import Rational, Real >>> from sympy.polys.domains import QQ_sympy >>> QQ_sympy().of_type(Rational(3, 2)) True >>> QQ_sympy().of_type(2) False """ return type(a) in [type(self.one), type(self.zero), type(sympy_rat(-1)), type(sympy_rat(2)), type(sympy_rat(1, 2)), type(sympy_rat(3, 2))] def to_sympy(self, a): """Convert `a` to a SymPy object. """ return a def from_sympy(self, a): """Convert SymPy's Rational to `dtype`. """ if a.is_Rational and a.q != 0: return a elif a.is_Float: from sympy.polys.domains import RR return SymPyRationalType(*RR.as_integer_ratio(a)) else: raise CoercionFailed("expected `Rational` object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return SymPyRationalType(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return SymPyRationalType(a.numerator, a.denominator) def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return SymPyRationalType(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return a def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return SymPyRationalType(int(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return SymPyRationalType(int(a.numer()), int(a.denom())) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return SymPyRationalType(*K0.as_integer_ratio(a)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return SymPyRationalType(*K0.as_integer_ratio(a)) def numer(self, a): """Returns numerator of `a`. """ return SymPyIntegerType(a.p) def denom(self, a): """Returns denominator of `a`. """ return SymPyIntegerType(a.q) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/pythonrationalfield.py0000644000175000017500000000471312014170666030335 0ustar georgeskgeorgesk"""Implementation of :class:`PythonRationalField` class. """ from sympy.polys.domains.rationalfield import RationalField from sympy.polys.domains.groundtypes import PythonIntegerType from sympy.polys.domains.groundtypes import PythonRationalType from sympy.polys.domains.groundtypes import SymPyRationalType from sympy.polys.polyerrors import CoercionFailed class PythonRationalField(RationalField): """Rational field based on Python rational number type. """ dtype = PythonRationalType zero = dtype(0) one = dtype(1) alias = 'QQ_python' def __init__(self): pass def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyRationalType(a.numerator, a.denominator) def from_sympy(self, a): """Convert SymPy's Rational to `dtype`. """ if a.is_Rational and a.q != 0: return PythonRationalType(a.p, a.q) elif a.is_Float: from sympy.polys.domains import RR return PythonRationalType(*RR.as_integer_ratio(a)) else: raise CoercionFailed("expected `Rational` object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return PythonRationalType(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return a def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return PythonRationalType(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return PythonRationalType(a.p, a.q) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return PythonRationalType(PythonIntegerType(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return PythonRationalType(PythonIntegerType(a.numer()), PythonIntegerType(a.denom())) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return PythonRationalType(*K0.as_integer_ratio(a)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return PythonRationalType(*K0.as_integer_ratio(a)) def numer(self, a): """Returns numerator of `a`. """ return a.numerator def denom(self, a): """Returns denominator of `a`. """ return a.denominator wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/ring.py0000644000175000017500000000274012014170666025213 0ustar georgeskgeorgesk"""Implementation of :class:`Ring` class. """ from sympy.polys.domains.domain import Domain from sympy.polys.polyerrors import ExactQuotientFailed, NotInvertible, NotReversible class Ring(Domain): """Represents a ring domain. """ has_Ring = True def get_ring(self): """Returns a ring associated with `self`. """ return self def exquo(self, a, b): """Exact quotient of `a` and `b`, implies `__floordiv__`. """ if a % b: raise ExactQuotientFailed(a, b, self) else: return a // b def quo(self, a, b): """Quotient of `a` and `b`, implies `__floordiv__`. """ return a // b def rem(self, a, b): """Remainder of `a` and `b`, implies `__mod__`. """ return a % b def div(self, a, b): """Division of `a` and `b`, implies `__divmod__`. """ return divmod(a, b) def invert(self, a, b): """Returns inversion of `a mod b`. """ s, t, h = self.gcdex(a, b) if self.is_one(h): return s % b else: raise NotInvertible("zero divisor") def revert(self, a): """Returns `a**(-1)` if possible. """ if self.is_one(a): return a else: raise NotReversible('only unity is reversible in a ring') def numer(self, a): """Returns numerator of `a`. """ return a def denom(self, a): """Returns denominator of `a`. """ return self.one wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/finitefield.py0000644000175000017500000000751112014170666026537 0ustar georgeskgeorgesk"""Implementation of :class:`FiniteField` class. """ from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.groundtypes import SymPyIntegerType from sympy.polys.domains.modularinteger import ModularIntegerFactory from sympy.polys.polyerrors import CoercionFailed class FiniteField(Field, SimpleDomain): """General class for finite fields. """ rep = 'FF' is_Numerical = True has_assoc_Ring = False has_assoc_Field = True dom = None mod = None def __init__(self, mod, symmetric=True): if mod <= 0: raise ValueError('modulus must be a positive integer, got %s' % mod) self.dtype = ModularIntegerFactory(mod, self.dom, symmetric) self.zero = self.dtype(0) self.one = self.dtype(1) self.mod = mod def __str__(self): return 'GF(%s)' % self.mod def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.mod, self.dom)) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return isinstance(other, FiniteField) and \ self.mod == other.mod and self.dom == other.dom def __ne__(self, other): """Returns `False` if two domains are equivalent. """ return not isinstance(other, FiniteField) or \ self.mod != other.mod or self.dom != other.dom def characteristic(self): """Return the characteristic of this domain. """ return self.mod def get_field(self): """Returns a field associated with `self`. """ return self def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyIntegerType(int(a)) def from_sympy(self, a): """Convert SymPy's Integer to SymPy's `Integer`. """ if a.is_Integer: return self.dtype(self.dom.dtype(int(a))) elif a.is_Float and int(a) == a: return self.dtype(self.dom.dtype(int(a))) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0=None): """Convert `ModularInteger(int)` to `dtype`. """ return K1.dtype(K1.dom.from_ZZ_python(a.val, K0.dom)) def from_ZZ_python(K1, a, K0=None): """Convert Python's `int` to `dtype`. """ return K1.dtype(K1.dom.from_ZZ_python(a, K0)) def from_QQ_python(K1, a, K0=None): """Convert Python's `Fraction` to `dtype`. """ if a.denominator == 1: return K1.from_ZZ_python(a.numerator) def from_FF_sympy(K1, a, K0=None): """Convert `ModularInteger(Integer)` to `dtype`. """ return K1.dtype(K1.dom.from_ZZ_sympy(a.val, K0.dom)) def from_ZZ_sympy(K1, a, K0=None): """Convert SymPy's `Integer` to `dtype`. """ return K1.dtype(K1.dom.from_ZZ_sympy(a, K0)) def from_QQ_sympy(K1, a, K0=None): """Convert SymPy's `Rational` to `dtype`. """ if a.q == 1: return K1.from_ZZ_python(a.p) def from_FF_gmpy(K1, a, K0=None): """Convert `ModularInteger(mpz)` to `dtype`. """ return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom)) def from_ZZ_gmpy(K1, a, K0=None): """Convert GMPY's `mpz` to `dtype`. """ return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0)) def from_QQ_gmpy(K1, a, K0=None): """Convert GMPY's `mpq` to `dtype`. """ if a.denom() == 1: return K1.from_ZZ_gmpy(a.numer()) def from_RR_sympy(K1, a, K0=None): """Convert SymPy's `Float` to `dtype`. """ p, q = K0.as_integer_ratio(a) if q == 1: return K1.dtype(self.dom.dtype(p)) def from_RR_mpmath(K1, a, K0): """Convert mpmath's `mpf` to `dtype`. """ p, q = K0.as_integer_ratio(a) if q == 1: return K1.dtype(self.dom.dtype(p)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/modularinteger.py0000644000175000017500000000426412014170666027300 0ustar georgeskgeorgesk"""Implementation of :class:`ModularInteger` class. """ class ModularInteger(object): """A class representing an modular integers. """ mod, dom, sym = None, None, None __slots__ = ['val'] def __init__(self, val): self.val = val % self.mod def __hash__(self): return hash((self.val, self.mod)) def __repr__(self): return "%s(%s)" % (self.__class__.__name__, self.val) def __str__(self): return "%s mod %s" % (self.val, self.mod) def __int__(self): return int(self.to_int()) def to_int(self): if self.sym: if self.val <= self.mod // 2: return self.val else: return self.val - self.mod else: return self.val def __pos__(self): return self def __neg__(self): return self.__class__(-self.val) def __add__(self, other): return self.__class__(self.val + other.val) def __sub__(self, other): return self.__class__(self.val - other.val) def __mul__(self, other): return self.__class__(self.val * other.val) def __div__(self, other): return self.__class__(self.val * self.dom.invert(other.val, self.mod)) __truediv__ = __div__ def __mod__(self, other): return self.__class__(self.val % other.val) def __pow__(self, exp): if not exp: return self.__class__(self.dom.one) if exp < 0: val, exp = self.dom.invert(self.val, self.mod), -exp else: val = self.val return self.__class__(val**exp) def __eq__(self, other): return isinstance(other, ModularInteger) and self.val == other.val def __ne__(self, other): return not isinstance(other, ModularInteger) or self.val != other.val def __nonzero__(self): return bool(self.val) def ModularIntegerFactory(_mod, _dom, _sym): """Create custom class for specific integer modulus.""" class cls(ModularInteger): mod, dom, sym = _dom.convert(_mod), _dom, _sym if _sym: cls.__name__ = "SymmetricModularInteger%s" % _mod else: cls.__name__ = "ModularInteger%s" % _mod return cls wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/sympyintegerring.py0000644000175000017500000000700512014170666027672 0ustar georgeskgeorgesk"""Implementaton of :class:`SymPyIntegerRing` class. """ from sympy.polys.domains.integerring import IntegerRing from sympy.polys.domains.groundtypes import SymPyIntegerType from sympy.polys.polyerrors import CoercionFailed class SymPyIntegerRing(IntegerRing): """Integer ring based on SymPy's `Integer` type. """ dtype = SymPyIntegerType zero = dtype(0) one = dtype(1) alias = 'ZZ_sympy' def __init__(self): """Allow instantiation of this domain. """ def of_type(self, a): """ Check if `a` is of type `dtype` (`sympy`). **Example** >>> from sympy import S, Integer, Rational >>> from sympy.polys.domains import ZZ_sympy >>> ZZ_sympy().of_type(S.One) True >>> ZZ_sympy().of_type(Integer(3)) True >>> ZZ_sympy().of_type(Rational(3, 2)) False """ return type(a) in [type(self.one), type(self.zero), type(self.dtype(-1)), type(self.dtype(2))] def to_sympy(self, a): """Convert `a` to a SymPy object. """ return a def from_sympy(self, a): """Convert SymPy's Integer to SymPy's `Integer`. """ if a.is_Integer: return a elif a.is_Float and int(a) == a: return SymPyIntegerType(int(a)) else: raise CoercionFailed("expected an integer, got %s" % a) def from_FF_python(K1, a, K0): """Convert `ModularInteger(int)` to SymPy's `Integer`. """ return SymPyIntegerType(a.to_int()) def from_ZZ_python(K1, a, K0): """Convert Python's `int` to SymPy's `Integer`. """ return SymPyIntegerType(a) def from_QQ_python(K1, a, K0): """Convert Python's `Fraction` to SymPy's `Integer`. """ if a.denominator == 1: return SymPyIntegerType(a.numerator) def from_FF_sympy(K1, a, K0): """Convert `ModularInteger(Integer)` to SymPy's `Integer`. """ return a.to_int() def from_ZZ_sympy(K1, a, K0): """Convert SymPy's `Integer` to SymPy's `Integer`. """ return a def from_QQ_sympy(K1, a, K0): """Convert SymPy's `Rational` to SymPy's `Integer`. """ if a.q == 1: return SymPyIntegerType(a.p) def from_FF_gmpy(K1, a, K0): """Convert `ModularInteger(mpz)` to SymPy's `Integer`. """ return SymPyIntegerType(int(a.to_int())) def from_ZZ_gmpy(K1, a, K0): """Convert GMPY's `mpz` to SymPy's `Integer`. """ return SymPyIntegerType(int(a)) def from_QQ_gmpy(K1, a, K0): """Convert GMPY's `mpq` to SymPy's `Integer`. """ if a.denom() == 1: return SymPyIntegerType(int(a.numer())) def from_RR_sympy(K1, a, K0): """Convert SymPy's `Float` to SymPy's `Integer`. """ p, q = K0.as_integer_ratio(a) if q == 1: return SymPyIntegerType(p) def from_RR_mpmath(K1, a, K0): """Convert mpmath's `mpf` to SymPy's `Integer`. """ p, q = K0.as_integer_ratio(a) if q == 1: return SymPyIntegerType(p) def gcdex(self, a, b): """Compute extended GCD of `a` and `b`. """ return a.gcdex(b) def gcd(self, a, b): """Compute GCD of `a` and `b`. """ return a.gcd(b) def lcm(self, a, b): """Compute LCM of `a` and `b`. """ return a.lcm(b) def sqrt(self, a): """Compute square root of `a`. """ return a.isqrt() def factorial(self, a): """Compute factorial of `a`. """ return a.factorial() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/fractionfield.py0000644000175000017500000001436612014170666027074 0ustar georgeskgeorgesk"""Implementation of :class:`FractionField` class. """ from sympy.polys.domains.field import Field from sympy.polys.domains.compositedomain import CompositeDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.polyclasses import DMF from sympy.polys.polyerrors import GeneratorsNeeded, GeneratorsError from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder class FractionField(Field, CharacteristicZero, CompositeDomain): """A class for representing rational function fields. """ dtype = DMF is_Frac = True has_assoc_Ring = True has_assoc_Field = True def __init__(self, dom, *gens): if not gens: raise GeneratorsNeeded("generators not specified") lev = len(gens) - 1 self.zero = self.dtype.zero(lev, dom) self.one = self.dtype.one(lev, dom) self.dom = dom self.gens = gens def __str__(self): return str(self.dom) + '(' + ','.join(map(str, self.gens)) + ')' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.dom, self.gens)) def __call__(self, a): """Construct an element of `self` domain from `a`. """ return DMF(a, self.dom, len(self.gens)-1) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens def __ne__(self, other): """Returns `False` if two domains are equivalent. """ return not self.__eq__(other) def to_sympy(self, a): """Convert `a` to a SymPy object. """ return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) / basic_from_dict(a.denom().to_sympy_dict(), *self.gens)) def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ p, q = a.as_numer_denom() num, _ = dict_from_basic(p, gens=self.gens) den, _ = dict_from_basic(q, gens=self.gens) for k, v in num.iteritems(): num[k] = self.dom.from_sympy(v) for k, v in den.iteritems(): den[k] = self.dom.from_sympy(v) return self((num, den)).cancel() def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_PolynomialRing(K1, a, K0): """Convert a `DMF` object to `dtype`. """ if K1.gens == K0.gens: if K1.dom == K0.dom: return K1(a.rep) else: return K1(a.convert(K1.dom).rep) else: monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens) if K1.dom != K0.dom: coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] return K1(dict(zip(monoms, coeffs))) def from_FractionField(K1, a, K0): """ Convert a fraction field element to another fraction field. **Examples** >>> from sympy.polys.polyclasses import DMF >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.abc import x >>> f = DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(1)]), ZZ) >>> QQx = QQ.frac_field(x) >>> ZZx = ZZ.frac_field(x) >>> QQx.from_FractionField(f, ZZx) DMF(([1/1, 2/1], [1/1, 1/1]), QQ) """ if K1.gens == K0.gens: if K1.dom == K0.dom: return a else: return K1((a.numer().convert(K1.dom).rep, a.denom().convert(K1.dom).rep)) elif set(K0.gens).issubset(K1.gens): nmonoms, ncoeffs = _dict_reorder(a.numer().to_dict(), K0.gens, K1.gens) dmonoms, dcoeffs = _dict_reorder(a.denom().to_dict(), K0.gens, K1.gens) if K1.dom != K0.dom: ncoeffs = [ K1.dom.convert(c, K0.dom) for c in ncoeffs ] dcoeffs = [ K1.dom.convert(c, K0.dom) for c in dcoeffs ] return K1((dict(zip(nmonoms, ncoeffs)), dict(zip(dmonoms, dcoeffs)))) def get_ring(self): """Returns a ring associated with `self`. """ from sympy.polys.domains import PolynomialRing return PolynomialRing(self.dom, *self.gens) def poly_ring(self, *gens): """Returns a polynomial ring, i.e. `K[X]`. """ raise NotImplementedError('nested domains not allowed') def frac_field(self, *gens): """Returns a fraction field, i.e. `K(X)`. """ raise NotImplementedError('nested domains not allowed') def is_positive(self, a): """Returns True if `a` is positive. """ return self.dom.is_positive(a.numer().LC()) def is_negative(self, a): """Returns True if `a` is negative. """ return self.dom.is_negative(a.numer().LC()) def is_nonpositive(self, a): """Returns True if `a` is non-positive. """ return self.dom.is_nonpositive(a.numer().LC()) def is_nonnegative(self, a): """Returns True if `a` is non-negative. """ return self.dom.is_nonnegative(a.numer().LC()) def numer(self, a): """Returns numerator of `a`. """ return a.numer() def denom(self, a): """Returns denominator of `a`. """ return a.denom() def factorial(self, a): """Returns factorial of `a`. """ return self.dtype(self.dom.factorial(a)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/realdomain.py0000644000175000017500000000653012014170666026370 0ustar georgeskgeorgesk"""Implementation of :class:`RealDomain` class. """ from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.groundtypes import SymPyRealType from sympy.polys.polyerrors import DomainError, CoercionFailed import math class RealDomain(CharacteristicZero, SimpleDomain): # XXX: should be a field """Abstract domain for real numbers. """ rep = 'RR' is_Exact = False is_Numerical = True _convert_excludes = [ SymPyRealType('+inf'), SymPyRealType('-inf'), ] def as_integer_ratio(self, a, **args): """Convert real number to a (numer, denom) pair. """ v, n = math.frexp(a) # XXX: hack, will work only for floats for i in xrange(300): if v != math.floor(v): v, n = 2*v, n-1 else: break numer, denom = int(v), 1 m = 1 << abs(n) if n > 0: numer *= m else: denom = m n, d = self.limit_denom(numer, denom, **args) if a and not n: return numer, denom else: return n, d def limit_denom(self, n, d, **args): """Find closest rational to `n/d` (up to `max_denom`). """ max_denom = args.get('max_denom', 1000000) if d <= max_denom: return n, d from sympy.polys.domains import QQ self = QQ(n, d) p0, q0, p1, q1 = 0, 1, 1, 0 while True: a = n//d q2 = q0 + a*q1 if q2 > max_denom: break p0, q0, p1, q1, n, d = \ p1, q1, p0 + a*p1, q2, d, n - a*d k = (max_denom - q0)//q1 P1, Q1 = p0 + k*p1, q0 + k*q1 bound1 = QQ(P1, Q1) bound2 = QQ(p1, q1) if abs(bound2 - self) <= abs(bound1 - self): return p1, q1 else: return P1, Q1 def get_ring(self): """Returns a ring associated with `self`. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with `self`. """ raise DomainError('there is no field associated with %s' % self) def get_exact(self): """Returns an exact domain associated with `self`. """ from sympy.polys.domains import QQ return QQ def exquo(self, a, b): """Exact quotient of `a` and `b`, implies `__div__`. """ return a / b def quo(self, a, b): """Quotient of `a` and `b`, implies `__div__`. """ return a / b def rem(self, a, b): """Remainder of `a` and `b`, implies nothing. """ return self.zero def div(self, a, b): """Division of `a` and `b`, implies `__div__`. """ def gcd(self, a, b): """Returns GCD of `a` and `b`. """ return self.one def lcm(self, a, b): """Returns LCM of `a` and `b`. """ return a*b def to_sympy(self, a): """Convert `a` to SymPy number. """ return SymPyRealType(a) def from_sympy(self, a): """Convert SymPy's number to `dtype`. """ b = a.evalf() if b.is_Number and b not in self._convert_excludes: return self.dtype(b) else: raise CoercionFailed("expected Number object, got %s" % a) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/polynomialring.py0000644000175000017500000001336512014170666027324 0ustar georgeskgeorgesk"""Implementation of :class:`PolynomialRing` class. """ from sympy.polys.domains.ring import Ring from sympy.polys.domains.compositedomain import CompositeDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.polys.polyclasses import DMP from sympy.polys.polyerrors import GeneratorsNeeded, PolynomialError, CoercionFailed from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder class PolynomialRing(Ring, CharacteristicZero, CompositeDomain): """A class for representing multivariate polynomial rings. """ dtype = DMP is_Poly = True has_assoc_Ring = True has_assoc_Field = True def __init__(self, dom, *gens): if not gens: raise GeneratorsNeeded("generators not specified") lev = len(gens) - 1 self.zero = self.dtype.zero(lev, dom) self.one = self.dtype.one(lev, dom) self.dom = dom self.gens = gens def __str__(self): return str(self.dom) + '[' + ','.join(map(str, self.gens)) + ']' def __hash__(self): return hash((self.__class__.__name__, self.dtype, self.dom, self.gens)) def __call__(self, a): """Construct an element of `self` domain from `a`. """ return DMP(a, self.dom, len(self.gens)-1) def __eq__(self, other): """Returns `True` if two domains are equivalent. """ return self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens def __ne__(self, other): """Returns `False` if two domains are equivalent. """ return not self.__eq__(other) def to_sympy(self, a): """Convert `a` to a SymPy object. """ return basic_from_dict(a.to_sympy_dict(), *self.gens) def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ try: rep, _ = dict_from_basic(a, gens=self.gens) except PolynomialError: raise CoercionFailed("can't convert %s to type %s" % (a, self)) for k, v in rep.iteritems(): rep[k] = self.dom.from_sympy(v) return self(rep) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Real` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K1.dom.convert(a, K0)) def from_AlgebraicField(K1, a, K0): """Convert a `ANP` object to `dtype`. """ if K1.dom == K0: return K1(a) def from_PolynomialRing(K1, a, K0): """Convert a `DMP` object to `dtype`. """ if K1.gens == K0.gens: if K1.dom == K0.dom: return a else: return a.convert(K1.dom) else: monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens) if K1.dom != K0.dom: coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] return K1(dict(zip(monoms, coeffs))) def from_FractionField(K1, a, K0): """ Convert a ``DMF`` object to ``DMP``. **Examples** >>> from sympy.polys.polyclasses import DMP, DMF >>> from sympy.polys.domains import ZZ >>> from sympy.abc import x >>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ) >>> K = ZZ.frac_field(x) >>> F = ZZ[x].from_FractionField(f, K) >>> F == DMP([ZZ(1), ZZ(1)], ZZ) True >>> type(F) """ if a.denom().is_one: return K1.from_PolynomialRing(a.numer(), K0) def get_field(self): """Returns a field associated with `self`. """ from sympy.polys.domains import FractionField return FractionField(self.dom, *self.gens) def poly_ring(self, *gens): """Returns a polynomial ring, i.e. `K[X]`. """ raise NotImplementedError('nested domains not allowed') def frac_field(self, *gens): """Returns a fraction field, i.e. `K(X)`. """ raise NotImplementedError('nested domains not allowed') def is_positive(self, a): """Returns True if `LC(a)` is positive. """ return self.dom.is_positive(a.LC()) def is_negative(self, a): """Returns True if `LC(a)` is negative. """ return self.dom.is_negative(a.LC()) def is_nonpositive(self, a): """Returns True if `LC(a)` is non-positive. """ return self.dom.is_nonpositive(a.LC()) def is_nonnegative(self, a): """Returns True if `LC(a)` is non-negative. """ return self.dom.is_nonnegative(a.LC()) def gcdex(self, a, b): """Extended GCD of `a` and `b`. """ return a.gcdex(b) def gcd(self, a, b): """Returns GCD of `a` and `b`. """ return a.gcd(b) def lcm(self, a, b): """Returns LCM of `a` and `b`. """ return a.lcm(b) def factorial(self, a): """Returns factorial of `a`. """ return self.dtype(self.dom.factorial(a)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/characteristiczero.py0000644000175000017500000000052112014170666030137 0ustar georgeskgeorgesk"""Implementaton of :class:`CharacteristicZero` class. """ from sympy.polys.domains.domain import Domain class CharacteristicZero(Domain): """Domain that has infinite number of elements. """ has_CharacteristicZero = True def characteristic(self): """Return the characteristic of this domain. """ return 0 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/pythonrealdomain.py0000644000175000017500000000375212014170666027635 0ustar georgeskgeorgesk"""Implementation of :class:`PythonRealDomain` class. """ from sympy.polys.domains.realdomain import RealDomain from sympy.polys.polyerrors import CoercionFailed from sympy.core import S class PythonRealDomain(RealDomain): # XXX: tmp solution """Float domain. """ rep = 'RR' is_FF = True dtype = float zero = dtype(0) one = dtype(1) alias = 'RR_python' def __init__(self): pass def normal(self, a): if abs(a) < 1e-15: return self.zero else: return self.dtype(a) def to_sympy(self, a): """Convert `a` to a SymPy object. """ return sympy_mpf(a) def from_sympy(self, a): """Convert SymPy's Integer to `dtype`. """ b = a.evalf() if b.is_Float and b not in [S.Infinity, S.NegativeInfinity]: return float(b) else: raise CoercionFailed("expected Float object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1.dtype(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1.dtype(a.numerator) / a.denominator def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return K1.dtype(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return K1.dtype(a.p) / a.q def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1.dtype(int(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1.dtype(int(a.numer())) / int(a.denom) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return K1.dtype(a) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1.dtype(a) def complex_domain(self): return CC wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/compositedomain.py0000644000175000017500000000107112014170666027442 0ustar georgeskgeorgesk"""Implementation of :class:`CompositeDomain` class. """ from sympy.polys.domains.domain import Domain from sympy.polys.polyerrors import GeneratorsError class CompositeDomain(Domain): """Base class for composite domains, e.g. ZZ[x], ZZ(X). """ is_Composite = True def inject(self, *gens): """Inject generators into this domain. """ if not (set(self.gens) & set(gens)): return self.__class__(self.dom, *(self.gens + gens)) else: raise GeneratorsError("common generators in %s and %s" % (self.gens, gens)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/sympyfinitefield.py0000644000175000017500000000050312014170666027633 0ustar georgeskgeorgesk"""Implementation of :class:`SymPyFiniteField` class. """ from sympy.polys.domains.finitefield import FiniteField from sympy.polys.domains.sympyintegerring import SymPyIntegerRing class SymPyFiniteField(FiniteField): """Finite field based on SymPy's integers. """ dom = SymPyIntegerRing() alias = 'FF_sympy' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/expressiondomain.py0000644000175000017500000001233412014170666027643 0ustar georgeskgeorgesk"""Implementation of :class:`ExpressionDomain` class. """ from sympy.polys.domains.field import Field from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero from sympy.core import sympify from sympy.polys.polyerrors import DomainError class ExpressionDomain(Field, CharacteristicZero, SimpleDomain): """A class for arbitrary expressions. """ is_EX = True class Expression(object): """An arbitrary expression. """ __slots__ = ['ex'] def __init__(self, ex): if not isinstance(ex, self.__class__): self.ex = sympify(ex) else: self.ex = ex.ex def __repr__(f): return 'EX(%s)' % repr(f.ex) def __str__(f): return 'EX(%s)' % str(f.ex) def __hash__(self): return hash((self.__class__.__name__, self.ex)) def as_expr(f): return f.ex def numer(f): return EX(f.ex.as_numer_denom()[0]) def denom(f): return EX(f.ex.as_numer_denom()[1]) def simplify(f, ex): return f.__class__(ex.cancel()) def __abs__(f): return f.__class__(abs(f.ex)) def __neg__(f): return f.__class__(-f.ex) def __add__(f, g): return f.simplify(f.ex+f.__class__(g).ex) def __radd__(f, g): return f.simplify(f.__class__(g).ex+f.ex) def __sub__(f, g): return f.simplify(f.ex-f.__class__(g).ex) def __rsub__(f, g): return f.simplify(f.__class__(g).ex-f.ex) def __mul__(f, g): return f.simplify(f.ex*f.__class__(g).ex) def __rmul__(f, g): return f.simplify(f.__class__(g).ex*f.ex) def __pow__(f, n): return f.simplify(f.ex**n) def __div__(f, g): return f.simplify(f.ex/f.__class__(g).ex) def __rdiv__(f, g): return f.simplify(f.__class__(g).ex/f.ex) def __truediv__(f, g): return f.simplify(f.ex/f.__class__(g).ex) def __rtruediv__(f, g): return f.simplify(f.__class__(g).ex/f.ex) def __eq__(f, g): return f.ex == f.__class__(g).ex def __req__(f, g): return f.__class__(g).ex == f.ex def __ne__(f, g): return f.ex != f.__class__(g).ex def __rne__(f, g): return f.__class__(g).ex != f.ex def __nonzero__(f): return f.ex != 0 dtype = Expression zero = Expression(0) one = Expression(1) rep = 'EX' has_assoc_Ring = False has_assoc_Field = True def __init__(self): pass def to_sympy(self, a): """Convert `a` to a SymPy object. """ return a.as_expr() def from_sympy(self, a): """Convert SymPy's expression to `dtype`. """ return self.dtype(a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_PolynomialRing(K1, a, K0): """Convert a `DMP` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_FractionField(K1, a, K0): """Convert a `DMF` object to `dtype`. """ return K1(K0.to_sympy(a)) def from_ExpressionDomain(K1, a, K0): """Convert a `EX` object to `dtype`. """ return a def get_ring(self): """Returns a ring associated with `self`. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with `self`. """ return self def is_positive(self, a): """Returns True if `a` is positive. """ return a.ex.as_coeff_mul()[0].is_positive def is_negative(self, a): """Returns True if `a` is negative. """ return a.ex.as_coeff_mul()[0].is_negative def is_nonpositive(self, a): """Returns True if `a` is non-positive. """ return a.ex.as_coeff_mul()[0].is_nonpositive def is_nonnegative(self, a): """Returns True if `a` is non-negative. """ return a.ex.as_coeff_mul()[0].is_nonnegative def numer(self, a): """Returns numerator of `a`. """ return a.numer() def denom(self, a): """Returns denominator of `a`. """ return a.denom() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/gmpyrationalfield.py0000644000175000017500000000576512014170666030000 0ustar georgeskgeorgesk"""Implementaton of :class:`GMPYRationalField` class. """ from sympy.polys.domains.rationalfield import RationalField from sympy.polys.domains.groundtypes import ( GMPYRationalType, SymPyRationalType, gmpy_numer, gmpy_denom, gmpy_factorial, gmpy_gcdex, gmpy_gcd, gmpy_lcm, gmpy_sqrt, ) from sympy.polys.polyerrors import CoercionFailed class GMPYRationalField(RationalField): """Rational field based on GMPY mpq class. """ dtype = GMPYRationalType zero = dtype(0) one = dtype(1) alias = 'QQ_gmpy' def __init__(self): pass def to_sympy(self, a): """Convert `a` to a SymPy object. """ return SymPyRationalType(int(gmpy_numer(a)), int(gmpy_denom(a))) def from_sympy(self, a): """Convert SymPy's Integer to `dtype`. """ if a.is_Rational and a.q != 0: return GMPYRationalType(a.p, a.q) elif a.is_Float: from sympy.polys.domains import RR return GMPYRationalType(*RR.as_integer_ratio(a)) else: raise CoercionFailed("expected `Rational` object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return GMPYRationalType(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return GMPYRationalType(a.numerator, a.denominator) def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return GMPYRationalType(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return GMPYRationalType(a.p, a.q) def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return GMPYRationalType(a) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return a def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return GMPYRationalType(*K0.as_integer_ratio(a)) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return GMPYRationalType(*K0.as_integer_ratio(a)) def exquo(self, a, b): """Exact quotient of `a` and `b`, implies `__div__`. """ return GMPYRationalType(a.qdiv(b)) def quo(self, a, b): """Quotient of `a` and `b`, implies `__div__`. """ return GMPYRationalType(a.qdiv(b)) def rem(self, a, b): """Remainder of `a` and `b`, implies nothing. """ return self.zero def div(self, a, b): """Division of `a` and `b`, implies `__div__`. """ return GMPYRationalType(a.qdiv(b)), self.zero def numer(self, a): """Returns numerator of `a`. """ return gmpy_numer(a) def denom(self, a): """Returns denominator of `a`. """ return gmpy_denom(a) def factorial(self, a): """Returns factorial of `a`. """ return GMPYRationalType(gmpy_factorial(int(a))) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/field.py0000644000175000017500000000241612014170666025337 0ustar georgeskgeorgesk"""Implementation of :class:`Field` class. """ from sympy.polys.domains.ring import Ring from sympy.polys.polyerrors import NotReversible, DomainError class Field(Ring): """Represents a field domain. """ has_Field = True def get_ring(self): """Returns a ring associated with `self`. """ raise DomainError('there is no ring associated with %s' % self) def get_field(self): """Returns a field associated with `self`. """ return self def exquo(self, a, b): """Exact quotient of `a` and `b`, implies `__div__`. """ return a / b def quo(self, a, b): """Quotient of `a` and `b`, implies `__div__`. """ return a / b def rem(self, a, b): """Remainder of `a` and `b`, implies nothing. """ return self.zero def div(self, a, b): """Division of `a` and `b`, implies `__div__`. """ return a / b, self.zero def gcd(self, a, b): """Returns GCD of `a` and `b`. """ return self.one def lcm(self, a, b): """Returns LCM of `a` and `b`. """ return a*b def revert(self, a): """Returns `a**(-1)` if possible. """ if a: return 1/a else: raise NotReversible('zero is not reversible') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/sympyrealdomain.py0000644000175000017500000000266012014170666027472 0ustar georgeskgeorgesk"""Implementation of :class:`SymPyRealDomain` class. """ from sympy.polys.domains.realdomain import RealDomain from sympy.polys.domains.groundtypes import SymPyRealType class SymPyRealDomain(RealDomain): """Domain for real numbers based on SymPy Float type. """ dtype = SymPyRealType zero = dtype(0) one = dtype(1) alias = 'RR_sympy' def __init__(self): pass def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return SymPyRealType(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return SymPyRealType(a.numerator) / a.denominator def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return SymPyRealType(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return SymPyRealType(a.p) / a.q def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return SymPyRealType(int(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return SymPyRealType(int(a.numer())) / int(a.denom()) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return a def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return SymPyRealType(a) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/pythoncomplexdomain.py0000644000175000017500000000366112014170666030360 0ustar georgeskgeorgesk"""Implementation of :class:`PythonComplexDomain` class. """ from sympy.polys.domains.realdomain import RealDomain from sympy.polys.polyerrors import CoercionFailed class PythonComplexDomain(RealDomain): # XXX: tmp solution """Complex domain. """ rep = 'CC' dtype = complex zero = dtype(0) one = dtype(1) alias = 'CC_python' def __init__(self): pass def to_sympy(self, a): """Convert `a` to a SymPy object. """ return sympy_mpf(a) def from_sympy(self, a): """Convert SymPy's Integer to `dtype`. """ b = a.evalf() if b.is_Float and b not in [S.Infinity, S.NegativeInfinity]: return float(b) else: raise CoercionFailed("expected Float object, got %s" % a) def from_ZZ_python(K1, a, K0): """Convert a Python `int` object to `dtype`. """ return K1.dtype(a) def from_QQ_python(K1, a, K0): """Convert a Python `Fraction` object to `dtype`. """ return K1.dtype(a.numerator) / a.denominator def from_ZZ_sympy(K1, a, K0): """Convert a SymPy `Integer` object to `dtype`. """ return K1.dtype(a.p) def from_QQ_sympy(K1, a, K0): """Convert a SymPy `Rational` object to `dtype`. """ return K1.dtype(a.p) / a.q def from_ZZ_gmpy(K1, a, K0): """Convert a GMPY `mpz` object to `dtype`. """ return K1.dtype(int(a)) def from_QQ_gmpy(K1, a, K0): """Convert a GMPY `mpq` object to `dtype`. """ return K1.dtype(int(a.numer())) / int(a.denom) def from_RR_sympy(K1, a, K0): """Convert a SymPy `Float` object to `dtype`. """ return K1.dtype(a) def from_RR_mpmath(K1, a, K0): """Convert a mpmath `mpf` object to `dtype`. """ return K1.dtype(a) def from_FF_float(K1, a, K0): return K1.dtype(a) def real(self, a): return a.real def imag(self, a): return a.imag wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/simpledomain.py0000644000175000017500000000050512014170666026732 0ustar georgeskgeorgesk"""Implementation of :class:`SimpleDomain` class. """ from sympy.polys.domains.domain import Domain class SimpleDomain(Domain): """Base class for simple domains, e.g. ZZ, QQ. """ is_Simple = True def inject(self, *gens): """Inject generators into this domain. """ return self.poly_ring(*gens) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/integerring.py0000644000175000017500000000205012014170666026563 0ustar georgeskgeorgesk"""Implementation of :class:`IntegerRing` class. """ from sympy.polys.domains.ring import Ring from sympy.polys.domains.simpledomain import SimpleDomain from sympy.polys.domains.characteristiczero import CharacteristicZero import math class IntegerRing(Ring, CharacteristicZero, SimpleDomain): """General class for integer rings. """ is_ZZ = True rep = 'ZZ' is_Numerical = True has_assoc_Ring = True has_assoc_Field = True def get_field(self): """Returns a field associated with `self`. """ from sympy.polys.domains import QQ return QQ def algebraic_field(self, *extension): """Returns an algebraic field, i.e. `QQ(alpha, ...)`. """ return self.get_field().algebraic_field(*extension) def from_AlgebraicField(K1, a, K0): """Convert a `ANP` object to `dtype`. """ if a.is_ground: return K1.convert(a.LC(), K0.dom) def log(self, a, b): """Returns b-base logarithm of `a`. """ return self.dtype(math.log(int(a), b)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/domains/__init__.py0000644000175000017500000000501212014170666026006 0ustar georgeskgeorgesk"""Implementation of mathematical domains. """ from domain import Domain from ring import Ring from field import Field from simpledomain import SimpleDomain from compositedomain import CompositeDomain from characteristiczero import CharacteristicZero from finitefield import FiniteField from integerring import IntegerRing from rationalfield import RationalField from realdomain import RealDomain from pythonfinitefield import PythonFiniteField from sympyfinitefield import SymPyFiniteField from gmpyfinitefield import GMPYFiniteField from pythonintegerring import PythonIntegerRing from sympyintegerring import SymPyIntegerRing from gmpyintegerring import GMPYIntegerRing from pythonrationalfield import PythonRationalField from sympyrationalfield import SymPyRationalField from gmpyrationalfield import GMPYRationalField from sympyrealdomain import SymPyRealDomain from pythonrealdomain import PythonRealDomain from mpmathrealdomain import MPmathRealDomain from pythoncomplexdomain import PythonComplexDomain from mpmathcomplexdomain import MPmathComplexDomain from algebraicfield import AlgebraicField from polynomialring import PolynomialRing from fractionfield import FractionField from expressiondomain import ExpressionDomain FF_python = PythonFiniteField FF_sympy = SymPyFiniteField FF_gmpy = GMPYFiniteField ZZ_python = PythonIntegerRing ZZ_sympy = SymPyIntegerRing ZZ_gmpy = GMPYIntegerRing QQ_python = PythonRationalField QQ_sympy = SymPyRationalField QQ_gmpy = GMPYRationalField RR_sympy = SymPyRealDomain RR_python = PythonRealDomain RR_mpmath = MPmathRealDomain CC_python = PythonComplexDomain CC_mpmath = MPmathComplexDomain from pythonrationaltype import PythonRationalType from groundtypes import HAS_GMPY def _getenv(key, default=None): from os import getenv return getenv(key, default) GROUND_TYPES = _getenv('SYMPY_GROUND_TYPES', 'auto').lower() if GROUND_TYPES == 'auto': if HAS_GMPY: GROUND_TYPES = 'gmpy' else: GROUND_TYPES = 'python' if GROUND_TYPES == 'gmpy' and not HAS_GMPY: from warnings import warn warn("gmpy library is not installed, switching to 'python' ground types") GROUND_TYPES = 'python' _GROUND_TYPES_MAP = { 'gmpy': (FF_gmpy, ZZ_gmpy(), QQ_gmpy()), 'sympy': (FF_sympy, ZZ_sympy(), QQ_sympy()), 'python': (FF_python, ZZ_python(), QQ_python()), } try: FF, ZZ, QQ = _GROUND_TYPES_MAP[GROUND_TYPES] except KeyError: raise ValueError("invalid ground types: %s" % GROUND_TYPES) GF = FF RR = RR_mpmath() CC = CC_mpmath() EX = ExpressionDomain() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/polyoptions.py0000644000175000017500000004561312014170666025227 0ustar georgeskgeorgesk"""Options manager for :class:`Poly` and public API functions. """ from sympy.core import S, Basic, sympify from sympy.utilities import numbered_symbols, topological_sort from sympy.polys.polyerrors import ( GeneratorsError, OptionError, FlagError, ) import sympy.polys import re class Option(object): """Base class for all kinds of options. """ option = None is_Flag = False requires = [] excludes = [] after = [] before = [] @classmethod def default(cls): return None @classmethod def preprocess(cls, option): return None @classmethod def postprocess(cls, options): pass class Flag(Option): """Base class for all kinds of flags. """ is_Flag = True class BooleanOption(Option): """An option that must have a boolean value or equivalent assigned. """ @classmethod def preprocess(cls, value): if value is True or value is False or value is 1 or value is 0: return bool(value) else: raise OptionError("'%s' must have a boolean value assigned, got %s" % (cls.option, value)) class OptionType(type): """Base type for all options that does registers options. """ def __init__(cls, *args, **kwargs): @property def getter(self): try: return self[cls.option] except KeyError: return cls.default() setattr(Options, cls.option, getter) Options.__options__[cls.option] = cls class Options(dict): """ Options manager for polynomial manipulation module. **Examples** >>> from sympy.polys.polyoptions import Options >>> from sympy.polys.polyoptions import build_options >>> from sympy.abc import x, y, z >>> Options((x, y, z), {'domain': 'ZZ'}) {'auto': False, 'domain': ZZ, 'gens': (x, y, z)} >>> build_options((x, y, z), {'domain': 'ZZ'}) {'auto': False, 'domain': ZZ, 'gens': (x, y, z)} **Options** * Expand --- boolean option * Gens --- option * Wrt --- option * Sort --- option * Order --- option * Field --- boolean option * Greedy --- boolean option * Domain --- option * Split --- boolean option * Gaussian --- boolean option * Extension --- option * Modulus --- option * Symmetric --- boolean option * Strict --- boolean option * Repr --- option **Flags** * Auto --- boolean flag * Frac --- boolean flag * Formal --- boolean flag * Polys --- boolean flag * Include --- boolean flag * All --- boolean flag * Gen --- flag """ __order__ = None __options__ = {} def __init__(self, gens, args, flags=None, strict=False): dict.__init__(self) if gens and args.get('gens', ()): raise OptionError("both '*gens' and keyword argument 'gens' supplied") elif gens: args = dict(args) args['gens'] = gens for option, value in args.iteritems(): try: cls = self.__options__[option] except KeyError: raise OptionError("'%s' is not a valid option" % option) if issubclass(cls, Flag): if flags is None or option not in flags: if strict: raise OptionError("'%s' flag is not allowed in this context" % option) if value is not None: self[option] = cls.preprocess(value) for option in self.keys(): cls = self.__options__[option] for require_option in cls.requires: if self.get(require_option) is None: raise OptionError("'%s' option is only allowed together with '%s'" % (option, require_option)) for exclude_option in cls.excludes: if self.get(exclude_option) is not None: raise OptionError("'%s' option is not allowed together with '%s'" % (option, exclude_option)) for option in self.__order__: self.__options__[option].postprocess(self) @classmethod def _init_dependencies_order(cls): """Resolve the order of options' processing. """ if cls.__order__ is None: vertices, edges = [], set([]) for name, option in cls.__options__.iteritems(): vertices.append(name) for _name in option.after: edges.add((_name, name)) for _name in option.before: edges.add((name, _name)) try: cls.__order__ = topological_sort((vertices, list(edges))) except ValueError: raise RuntimeError("cycle detected in sympy.polys options framework") def clone(self, updates={}): """Clone ``self`` and update specified options. """ obj = dict.__new__(self.__class__) for option, value in self.iteritems(): obj[option] = value for option, value in updates.iteritems(): obj[option] = value return obj def __setattr__(self, attr, value): if attr in self.__options__: self[attr] = value else: super(Options, self).__setattr__(attr, value) @property def args(self): args = {} for option, value in self.iteritems(): if value is not None and option != 'gens': cls = self.__options__[option] if not issubclass(cls, Flag): args[option] = value return args @property def options(self): options = {} for option, cls in self.__options__.iteritems(): if not issubclass(cls, Flag): options[option] = getattr(self, option) return options @property def flags(self): flags = {} for option, cls in self.__options__.iteritems(): if issubclass(cls, Flag): flags[option] = getattr(self, option) return flags class Expand(BooleanOption): """``expand`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'expand' requires = [] excludes = [] @classmethod def default(cls): return True class Gens(Option): """``gens`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'gens' requires = [] excludes = [] @classmethod def default(cls): return () @classmethod def preprocess(cls, gens): if isinstance(gens, Basic): gens = (gens,) elif len(gens) == 1 and hasattr(gens[0], '__iter__'): gens = gens[0] if gens == (None,): gens = () elif len(set(gens)) != len(gens): raise GeneratorsError("duplicated generators: %s" % str(gens)) elif any(gen.is_commutative is False for gen in gens): raise GeneratorsError("non-commutative generators: %s" % str(gens)) return tuple(gens) class Wrt(Option): """``wrt`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'wrt' requires = [] excludes = [] _re_split = re.compile(r"\s*,\s*|\s+") @classmethod def preprocess(cls, wrt): if isinstance(wrt, Basic): return [str(wrt)] elif isinstance(wrt, str): wrt = wrt.strip() if wrt.endswith(','): raise OptionError('Bad input: missing parameter.') if not wrt: return [] return [ gen for gen in cls._re_split.split(wrt) ] elif hasattr(wrt, '__getitem__'): return list(map(str, wrt)) else: raise OptionError("invalid argument for 'wrt' option") class Sort(Option): """``sort`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'sort' requires = [] excludes = [] @classmethod def default(cls): return [] @classmethod def preprocess(cls, sort): if isinstance(sort, str): return [ gen.strip() for gen in sort.split('>') ] elif hasattr(sort, '__getitem__'): return list(map(str, sort)) else: raise OptionError("invalid argument for 'sort' option") class Order(Option): """``order`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'order' requires = [] excludes = [] @classmethod def default(cls): return sympy.polys.monomialtools.monomial_key('lex') @classmethod def preprocess(cls, order): return sympy.polys.monomialtools.monomial_key(order) class Field(BooleanOption): """``field`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'field' requires = [] excludes = ['domain', 'split', 'gaussian'] class Greedy(BooleanOption): """``greedy`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'greedy' requires = [] excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric'] class Composite(BooleanOption): """ """ __metaclass__ = OptionType option = 'composite' @classmethod def default(cls): return True requires = [] excludes = ['domain', 'split', 'gaussian', 'extension', 'modulus', 'symmetric'] class Domain(Option): """``domain`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'domain' requires = [] excludes = ['field', 'greedy', 'split', 'gaussian', 'extension'] _re_finitefield = re.compile("^(FF|GF)\((\d+)\)$") _re_polynomial = re.compile("^(Z|ZZ|Q|QQ)\[(.+)\]$") _re_fraction = re.compile("^(Z|ZZ|Q|QQ)\((.+)\)$") _re_algebraic = re.compile("^(Q|QQ)\<(.+)\>$") @classmethod def preprocess(cls, domain): if not isinstance(domain, str): return domain else: if domain in ['Z', 'ZZ']: return sympy.polys.domains.ZZ if domain in ['Q', 'QQ']: return sympy.polys.domains.QQ if domain in ['R', 'RR']: return sympy.polys.domains.RR if domain == 'EX': return sympy.polys.domains.EX r = cls._re_finitefield.match(domain) if r is not None: return sympy.polys.domains.FF(int(r.groups()[1])) r = cls._re_polynomial.match(domain) if r is not None: ground, gens = r.groups() gens = map(sympify, gens.split(',')) if ground in ['Z', 'ZZ']: return sympy.polys.domains.ZZ.poly_ring(*gens) else: return sympy.polys.domains.QQ.poly_ring(*gens) r = cls._re_fraction.match(domain) if r is not None: ground, gens = r.groups() gens = map(sympify, gens.split(',')) if ground in ['Z', 'ZZ']: return sympy.polys.domains.ZZ.frac_field(*gens) else: return sympy.polys.domains.QQ.frac_field(*gens) r = cls._re_algebraic.match(domain) if r is not None: gens = map(sympify, r.groups()[1].split(',')) return sympy.polys.domains.QQ.algebraic_field(*gens) raise OptionError('expected a valid domain specification, got %s' % domain) @classmethod def postprocess(cls, options): if 'gens' in options and 'domain' in options and options['domain'].is_Composite and \ (set(options['domain'].gens) & set(options['gens'])): raise GeneratorsError("ground domain and generators interferes together") class Split(BooleanOption): """``split`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'split' requires = [] excludes = ['field', 'greedy', 'domain', 'gaussian', 'extension', 'modulus', 'symmetric'] @classmethod def postprocess(cls, options): if 'split' in options: raise NotImplementedError("'split' option is not implemented yet") class Gaussian(BooleanOption): """``gaussian`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'gaussian' requires = [] excludes = ['field', 'greedy', 'domain', 'split', 'extension', 'modulus', 'symmetric'] @classmethod def postprocess(cls, options): if 'gaussian' in options and options['gaussian'] is True: options['extension'] = set([S.ImaginaryUnit]) Extension.postprocess(options) class Extension(Option): """``extension`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'extension' requires = [] excludes = ['greedy', 'domain', 'split', 'gaussian', 'modulus', 'symmetric'] @classmethod def preprocess(cls, extension): if extension is True or extension is 1: return bool(extension) elif extension is False or extension is 0: raise OptionError("'False' is an invalid argument for 'extension'") else: if not hasattr(extension, '__iter__'): extension = set([extension]) else: if not extension: extension = None else: extension = set(extension) return extension @classmethod def postprocess(cls, options): if 'extension' in options and options['extension'] is not True: options['domain'] = sympy.polys.domains.QQ.algebraic_field(*options['extension']) class Modulus(Option): """``modulus`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'modulus' requires = [] excludes = ['greedy', 'split', 'domain', 'gaussian', 'extension'] @classmethod def preprocess(cls, modulus): modulus = sympify(modulus) if modulus.is_Integer and modulus > 0: return int(modulus) else: raise OptionError("'modulus' must a positive integer, got %s" % modulus) @classmethod def postprocess(cls, options): if 'modulus' in options: modulus = options['modulus'] symmetric = options.get('symmetric', True) options['domain'] = sympy.polys.domains.FF(modulus, symmetric) class Symmetric(BooleanOption): """``symmetric`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'symmetric' requires = ['modulus'] excludes = ['greedy', 'domain', 'split', 'gaussian', 'extension'] class Strict(BooleanOption): """``strict`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'strict' @classmethod def default(cls): return True class Repr(Option): """``repr`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'repr' @classmethod def default(cls): return sympy.polys.densepolys.DensePoly @classmethod def preprocess(cls, repr): if isinstance(repr, str): if repr == 'sparse': return sympy.polys.sparsepolys.SparsePoly elif repr == 'dense': return sympy.polys.densepolys.DensePoly else: raise OptionError("'%s' is not a valid value 'repr' option" % repr) elif isinstance(repr, sympy.polys.polyclasses.GenericPoly): return repr else: raise OptionError("'repr' must a string or a class, got %s" % repr) class Auto(BooleanOption, Flag): """``auto`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'auto' after = ['field', 'domain', 'extension', 'gaussian'] @classmethod def default(cls): return True @classmethod def postprocess(cls, options): if ('domain' in options or 'field' in options) and 'auto' not in options: options['auto'] = False class Frac(BooleanOption, Flag): """``auto`` option to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'frac' @classmethod def default(cls): return False class Formal(BooleanOption, Flag): """``formal`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'formal' @classmethod def default(cls): return False class Polys(BooleanOption, Flag): """``polys`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'polys' class Include(BooleanOption, Flag): """``include`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'include' @classmethod def default(cls): return False class All(BooleanOption, Flag): """``all`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'all' @classmethod def default(cls): return False class Gen(Flag): """``gen`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'gen' @classmethod def default(cls): return 0 @classmethod def preprocess(cls, gen): if isinstance(gen, (Basic, int)): return gen else: raise OptionError("invalid argument for 'gen' option") class Symbols(Flag): """``symbols`` flag to polynomial manipulation functions. """ __metaclass__ = OptionType option = 'symbols' @classmethod def default(cls): return numbered_symbols('s', start=1) @classmethod def preprocess(cls, symbols): if hasattr(symbols, '__iter__'): return iter(symbols) else: raise OptionError("expected an iterator or iterable container, got %s" % symbols) def build_options(gens, args=None): """Construct options from keyword arguments or ... options. """ if args is None: gens, args = (), gens if len(args) != 1 or 'opt' not in args or gens: return Options(gens, args) else: return args['opt'] def allowed_flags(args, flags): """ Allow specified flags to be used in the given context. **Examples** >>> from sympy.polys.polyoptions import allowed_flags >>> from sympy.polys.domains import ZZ >>> allowed_flags({'domain': ZZ}, []) >>> allowed_flags({'domain': ZZ, 'frac': True}, []) Traceback (most recent call last): ... FlagError: 'frac' flag is not allowed in this context >>> allowed_flags({'domain': ZZ, 'frac': True}, ['frac']) """ flags = set(flags) for arg in args.iterkeys(): try: if Options.__options__[arg].is_Flag and not arg in flags: raise FlagError("'%s' flag is not allowed in this context" % arg) except KeyError: raise OptionError("'%s' is not a valid option" % arg) Options._init_dependencies_order() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/densetools.py0000644000175000017500000006564612014170666025017 0ustar georgeskgeorgesk"""Advanced tools for dense recursive polynomials in ``K[x]`` or ``K[X]``. """ from sympy.polys.densebasic import ( dup_strip, dmp_strip, dup_convert, dmp_convert, dup_degree, dmp_degree, dmp_degree_in, dup_to_dict, dmp_to_dict, dup_from_dict, dmp_from_dict, dup_LC, dmp_LC, dmp_ground_LC, dup_TC, dmp_TC, dmp_ground_TC, dmp_zero, dmp_one, dmp_ground, dmp_zero_p, dmp_one_p, dmp_multi_deflate, dmp_inflate, dup_to_raw_dict, dup_from_raw_dict, dmp_raise, dmp_apply_pairs, dmp_inject, dmp_zeros, dup_terms_gcd ) from sympy.polys.densearith import ( dup_add_term, dmp_add_term, dup_mul_term, dmp_mul_term, dup_lshift, dup_rshift, dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_sqr, dmp_sqr, dup_pow, dmp_pow, dup_div, dmp_div, dup_rem, dmp_rem, dup_quo, dmp_quo, dup_prem, dmp_prem, dup_expand, dmp_expand, dup_add_mul, dup_sub_mul, dup_mul_ground, dmp_mul_ground, dup_quo_ground, dmp_quo_ground, dup_exquo_ground, dmp_exquo_ground, dup_max_norm, dmp_max_norm ) from sympy.polys.polyerrors import ( MultivariatePolynomialError, HeuristicGCDFailed, HomomorphismFailed, RefinementFailed, NotInvertible, DomainError ) from sympy.utilities import ( cythonized, variations ) from math import ceil, log @cythonized("m,n,i,j") def dup_integrate(f, m, K): """ Computes indefinite integral of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densetools import dup_integrate >>> dup_integrate([QQ(1), QQ(2), QQ(0)], 1, QQ) [1/3, 1/1, 0/1, 0/1] >>> dup_integrate([QQ(1), QQ(2), QQ(0)], 2, QQ) [1/12, 1/3, 0/1, 0/1, 0/1] """ if m <= 0 or not f: return f g = [K.zero]*m for i, c in enumerate(reversed(f)): n = i+1 for j in xrange(1, m): n *= i+j+1 g.insert(0, K.quo(c, K(n))) return g @cythonized("m,u,v,n,i,j") def dmp_integrate(f, m, u, K): """ Computes indefinite integral of ``f`` in ``x_0`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densetools import dmp_integrate >>> dmp_integrate([[QQ(1)], [QQ(2), QQ(0)]], 1, 1, QQ) [[1/2], [2/1, 0/1], []] >>> dmp_integrate([[QQ(1)], [QQ(2), QQ(0)]], 2, 1, QQ) [[1/6], [1/1, 0/1], [], []] """ if not u: return dup_integrate(f, m, K) if m <= 0 or dmp_zero_p(f, u): return f g, v = dmp_zeros(m, u-1, K), u-1 for i, c in enumerate(reversed(f)): n = i+1 for j in xrange(1, m): n *= i+j+1 g.insert(0, dmp_quo_ground(c, K(n), v, K)) return g @cythonized("m,v,w,i,j") def _rec_integrate_in(g, m, v, i, j, K): """Recursive helper for :func:`dmp_integrate_in`.""" if i == j: return dmp_integrate(g, m, v, K) w, i = v-1, i+1 return dmp_strip([ _rec_integrate_in(c, m, w, i, j, K) for c in g ], v) @cythonized("m,j,u") def dmp_integrate_in(f, m, j, u, K): """ Computes indefinite integral of ``f`` in ``x_j`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densetools import dmp_integrate_in >>> dmp_integrate_in([[QQ(1)], [QQ(2), QQ(0)]], 1, 0, 1, QQ) [[1/2], [2/1, 0/1], []] >>> dmp_integrate_in([[QQ(1)], [QQ(2), QQ(0)]], 1, 1, 1, QQ) [[1/1, 0/1], [1/1, 0/1, 0/1]] """ if j < 0 or j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) return _rec_integrate_in(f, m, u, 0, j, K) @cythonized("m,n,k,i") def dup_diff(f, m, K): """ ``m``-th order derivative of a polynomial in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_diff >>> dup_diff([ZZ(1), ZZ(2), ZZ(3), ZZ(4)], 1, ZZ) [3, 4, 3] >>> dup_diff([ZZ(1), ZZ(2), ZZ(3), ZZ(4)], 2, ZZ) [6, 4] """ if m <= 0: return f n = dup_degree(f) if n < m: return [] deriv = [] if m == 1: for coeff in f[:-m]: deriv.append(K(n)*coeff) n -= 1 else: for coeff in f[:-m]: k = n for i in xrange(n-1, n-m, -1): k *= i deriv.append(K(k)*coeff) n -= 1 return dup_strip(deriv) @cythonized("u,v,m,n,k,i") def dmp_diff(f, m, u, K): """ ``m``-th order derivative in ``x_0`` of a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_diff >>> f = ZZ.map([[1, 2, 3], [2, 3, 1]]) >>> dmp_diff(f, 1, 1, ZZ) [[1, 2, 3]] >>> dmp_diff(f, 2, 1, ZZ) [[]] """ if not u: return dup_diff(f, m, K) if m <= 0: return f n = dmp_degree(f, u) if n < m: return dmp_zero(u) deriv, v = [], u-1 if m == 1: for coeff in f[:-m]: deriv.append(dmp_mul_ground(coeff, K(n), v, K)) n -= 1 else: for coeff in f[:-m]: k = n for i in xrange(n-1, n-m, -1): k *= i deriv.append(dmp_mul_ground(coeff, K(k), v, K)) n -= 1 return dmp_strip(deriv, u) @cythonized("m,v,w,i,j") def _rec_diff_in(g, m, v, i, j, K): """Recursive helper for :func:`dmp_diff_in`.""" if i == j: return dmp_diff(g, m, v, K) w, i = v-1, i+1 return dmp_strip([ _rec_diff_in(c, m, w, i, j, K) for c in g ], v) @cythonized("m,j,u") def dmp_diff_in(f, m, j, u, K): """ ``m``-th order derivative in ``x_j`` of a polynomial in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_diff_in >>> f = ZZ.map([[1, 2, 3], [2, 3, 1]]) >>> dmp_diff_in(f, 1, 0, 1, ZZ) [[1, 2, 3]] >>> dmp_diff_in(f, 1, 1, 1, ZZ) [[2, 2], [4, 3]] """ if j < 0 or j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) return _rec_diff_in(f, m, u, 0, j, K) def dup_eval(f, a, K): """ Evaluate a polynomial at ``x = a`` in ``K[x]`` using Horner scheme. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_eval >>> dup_eval([ZZ(1), ZZ(2), ZZ(3)], 2, ZZ) 11 """ if not a: return dup_TC(f, K) result = K.zero for c in f: result *= a result += c return result @cythonized("u,v") def dmp_eval(f, a, u, K): """ Evaluate a polynomial at ``x_0 = a`` in ``K[X]`` using the Horner scheme. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_eval >>> f = ZZ.map([[2, 3], [1, 2]]) >>> dmp_eval(f, 2, 1, ZZ) [5, 8] """ if not u: return dup_eval(f, a, K) if not a: return dmp_TC(f, K) result, v = dmp_LC(f, K), u-1 for coeff in f[1:]: result = dmp_mul_ground(result, a, v, K) result = dmp_add(result, coeff, v, K) return result @cythonized("v,i,j") def _rec_eval_in(g, a, v, i, j, K): """Recursive helper for :func:`dmp_eval_in`.""" if i == j: return dmp_eval(g, a, v, K) v, i = v-1, i+1 return dmp_strip([ _rec_eval_in(c, a, v, i, j, K) for c in g ], v) @cythonized("u") def dmp_eval_in(f, a, j, u, K): """ Evaluate a polynomial at ``x_j = a`` in ``K[X]`` using the Horner scheme. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_eval_in >>> f = ZZ.map([[2, 3], [1, 2]]) >>> dmp_eval_in(f, 2, 0, 1, ZZ) [5, 8] >>> dmp_eval_in(f, 2, 1, 1, ZZ) [7, 4] """ if j < 0 or j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) return _rec_eval_in(f, a, u, 0, j, K) @cythonized("i,u") def _rec_eval_tail(g, i, A, u, K): """Recursive helper for :func:`dmp_eval_tail`.""" if i == u: return dup_eval(g, A[-1], K) else: h = [ _rec_eval_tail(c, i+1, A, u, K) for c in g ] if i < u - len(A) + 1: return h else: return dup_eval(h, A[-u+i-1], K) @cythonized("u") def dmp_eval_tail(f, A, u, K): """ Evaluate a polynomial at ``x_j = a_j, ...`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_eval_tail >>> f = ZZ.map([[2, 3], [1, 2]]) >>> dmp_eval_tail(f, (2, 2), 1, ZZ) 18 >>> dmp_eval_tail(f, (2,), 1, ZZ) [7, 4] """ if not A: return f if dmp_zero_p(f, u): return dmp_zero(u - len(A)) e = _rec_eval_tail(f, 0, A, u, K) if u == len(A)-1: return e else: return dmp_strip(e, u - len(A)) @cythonized("m,v,i,j") def _rec_diff_eval(g, m, a, v, i, j, K): """Recursive helper for :func:`dmp_diff_eval`.""" if i == j: return dmp_eval(dmp_diff(g, m, v, K), a, v, K) v, i = v-1, i+1 return dmp_strip([ _rec_diff_eval(c, m, a, v, i, j, K) for c in g ], v) @cythonized("m,j,u") def dmp_diff_eval_in(f, m, a, j, u, K): """ Differentiate and evaluate a polynomial in ``x_j`` at ``a`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_diff_eval_in >>> f = ZZ.map([[1, 2, 3], [2, 3, 1]]) >>> dmp_diff_eval_in(f, 1, 2, 0, 1, ZZ) [1, 2, 3] >>> dmp_diff_eval_in(f, 1, 2, 1, 1, ZZ) [6, 11] """ if j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) if not j: return dmp_eval(dmp_diff(f, m, u, K), a, u, K) return _rec_diff_eval(f, m, a, u, 0, j, K) def dup_trunc(f, p, K): """ Reduce ``K[x]`` polynomial modulo a constant ``p`` in ``K``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_trunc >>> f = ZZ.map([2, 3, 5, 7]) >>> dup_trunc(f, ZZ(3), ZZ) [-1, 0, -1, 1] """ if K.is_ZZ: g = [] for c in f: c = c % p if c > p // 2: g.append(c - p) else: g.append(c) else: g = [ c % p for c in f ] return dup_strip(g) @cythonized("u") def dmp_trunc(f, p, u, K): """ Reduce ``K[X]`` polynomial modulo a polynomial ``p`` in ``K[Y]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_trunc >>> f = ZZ.map([[3, 8], [5, 6], [2, 3]]) >>> g = ZZ.map([1, -1]) >>> dmp_trunc(f, g, 1, ZZ) [[11], [11], [5]] """ return dmp_strip([ dmp_rem(c, p, u-1, K) for c in f ], u) @cythonized("u,v") def dmp_ground_trunc(f, p, u, K): """ Reduce ``K[X]`` polynomial modulo a constant ``p`` in ``K``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_ground_trunc >>> f = ZZ.map([[3, 8], [5, 6], [2, 3]]) >>> dmp_ground_trunc(f, ZZ(3), 1, ZZ) [[-1], [-1, 0], [-1, 0]] """ if not u: return dup_trunc(f, p, K) v = u-1 return dmp_strip([ dmp_ground_trunc(c, p, v, K) for c in f ], u) def dup_monic(f, K): """ Divides all coefficients by ``LC(f)`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densetools import dup_monic >>> dup_monic([ZZ(3), ZZ(6), ZZ(9)], ZZ) [1, 2, 3] >>> dup_monic([QQ(3), QQ(4), QQ(2)], QQ) [1/1, 4/3, 2/3] """ if not f: return f lc = dup_LC(f, K) if K.is_one(lc): return f else: return dup_exquo_ground(f, lc, K) @cythonized("u") def dmp_ground_monic(f, u, K): """ Divides all coefficients by ``LC(f)`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densetools import dmp_ground_monic >>> f = ZZ.map([[3, 6], [3, 0], [9, 3]]) >>> g = QQ.map([[3, 8], [5, 6], [2, 3]]) >>> dmp_ground_monic(f, 1, ZZ) [[1, 2], [1, 0], [3, 1]] >>> dmp_ground_monic(g, 1, QQ) [[1/1, 8/3], [5/3, 2/1], [2/3, 1/1]] """ if not u: return dup_monic(f, K) if dmp_zero_p(f, u): return f lc = dmp_ground_LC(f, u, K) if K.is_one(lc): return f else: return dmp_exquo_ground(f, lc, u, K) def dup_content(f, K): """ Compute the GCD of coefficients of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densetools import dup_content >>> f = ZZ.map([6, 8, 12]) >>> g = QQ.map([6, 8, 12]) >>> dup_content(f, ZZ) 2 >>> dup_content(g, QQ) 1/1 """ if not f: return K.zero cont = K.zero for c in f: cont = K.gcd(cont, c) if K.is_one(cont): break return cont @cythonized("u,v") def dmp_ground_content(f, u, K): """ Compute the GCD of coefficients of ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densetools import dmp_ground_content >>> f = ZZ.map([[2, 6], [4, 12]]) >>> g = QQ.map([[2, 6], [4, 12]]) >>> dmp_ground_content(f, 1, ZZ) 2 >>> dmp_ground_content(g, 1, QQ) 1/1 """ if not u: return dup_content(f, K) if dmp_zero_p(f, u): return K.zero cont, v = K.zero, u-1 for c in f: cont = K.gcd(cont, dmp_ground_content(c, v, K)) if K.is_one(cont): break return cont def dup_primitive(f, K): """ Compute content and the primitive form of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densetools import dup_primitive >>> f = ZZ.map([6, 8, 12]) >>> g = QQ.map([6, 8, 12]) >>> dup_primitive(f, ZZ) (2, [3, 4, 6]) >>> dup_primitive(g, QQ) (1/1, [6/1, 8/1, 12/1]) """ if not f: return K.zero, f cont = dup_content(f, K) if K.is_one(cont): return cont, f else: return cont, dup_quo_ground(f, cont, K) @cythonized("u") def dmp_ground_primitive(f, u, K): """ Compute content and the primitive form of ``f`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ, QQ >>> from sympy.polys.densetools import dmp_ground_primitive >>> f = ZZ.map([[2, 6], [4, 12]]) >>> g = QQ.map([[2, 6], [4, 12]]) >>> dmp_ground_primitive(f, 1, ZZ) (2, [[1, 3], [2, 6]]) >>> dmp_ground_primitive(g, 1, QQ) (1/1, [[2/1, 6/1], [4/1, 12/1]]) """ if not u: return dup_primitive(f, K) if dmp_zero_p(f, u): return K.zero, f cont = dmp_ground_content(f, u, K) if K.is_one(cont): return cont, f else: return cont, dmp_quo_ground(f, cont, u, K) def dup_extract(f, g, K): """ Extract common content from a pair of polynomials in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_extract >>> f = ZZ.map([6, 12, 18]) >>> g = ZZ.map([4, 8, 12]) >>> dup_extract(f, g, ZZ) (2, [3, 6, 9], [2, 4, 6]) """ fc = dup_content(f, K) gc = dup_content(g, K) gcd = K.gcd(fc, gc) if not K.is_one(gcd): f = dup_quo_ground(f, gcd, K) g = dup_quo_ground(g, gcd, K) return gcd, f, g @cythonized("u") def dmp_ground_extract(f, g, u, K): """ Extract common content from a pair of polynomials in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_ground_extract >>> f = ZZ.map([[6, 12], [18]]) >>> g = ZZ.map([[4, 8], [12]]) >>> dmp_ground_extract(f, g, 1, ZZ) (2, [[3, 6], [9]], [[2, 4], [6]]) """ fc = dmp_ground_content(f, u, K) gc = dmp_ground_content(g, u, K) gcd = K.gcd(fc, gc) if not K.is_one(gcd): f = dmp_quo_ground(f, gcd, u, K) g = dmp_quo_ground(g, gcd, u, K) return gcd, f, g def dup_real_imag(f, K): """ Return bivariate polynomials ``f1`` and ``f2``, such that ``f = f1 + f2*I``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_real_imag >>> dup_real_imag([ZZ(1), ZZ(1), ZZ(1), ZZ(1)], ZZ) ([[1], [1], [-3, 0, 1], [-1, 0, 1]], [[3, 0], [2, 0], [-1, 0, 1, 0]]) """ if not K.is_ZZ and not K.is_QQ: raise DomainError("computing real and imaginary parts is not supported over %s" % K) f1 = dmp_zero(1) f2 = dmp_zero(1) if not f: return f1, f2 g = [[[K.one, K.zero]], [[K.one], []]] h = dmp_ground(f[0], 2) for c in f[1:]: h = dmp_mul(h, g, 2, K) h = dmp_add_term(h, dmp_ground(c, 1), 0, 2, K) H = dup_to_raw_dict(h) for k, h in H.iteritems(): m = k % 4 if not m: f1 = dmp_add(f1, h, 1, K) elif m == 1: f2 = dmp_add(f2, h, 1, K) elif m == 2: f1 = dmp_sub(f1, h, 1, K) else: f2 = dmp_sub(f2, h, 1, K) return f1, f2 @cythonized('i,n') def dup_mirror(f, K): """ Evaluate efficiently the composition ``f(-x)`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_mirror >>> dup_mirror([ZZ(1), ZZ(2), -ZZ(4), ZZ(2)], ZZ) [-1, 2, 4, 2] """ f, n, a = list(f), dup_degree(f), -K.one for i in xrange(n-1, -1, -1): f[i], a = a*f[i], -a return f @cythonized('i,n') def dup_scale(f, a, K): """ Evaluate efficiently composition ``f(a*x)`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_scale >>> dup_scale([ZZ(1), -ZZ(2), ZZ(1)], ZZ(2), ZZ) [4, -4, 1] """ f, n, b = list(f), dup_degree(f), a for i in xrange(n-1, -1, -1): f[i], b = b*f[i], b*a return f @cythonized('i,j,n') def dup_shift(f, a, K): """ Evaluate efficiently Taylor shift ``f(x + a)`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_shift >>> dup_shift([ZZ(1), -ZZ(2), ZZ(1)], ZZ(2), ZZ) [1, 2, 1] """ f, n = list(f), dup_degree(f) for i in xrange(n, 0, -1): for j in xrange(0, i): f[j+1] += a*f[j] return f @cythonized('i,n') def dup_transform(f, p, q, K): """ Evaluate functional transformation ``q**n * f(p/q)`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_transform >>> f = ZZ.map([1, -2, 1]) >>> p = ZZ.map([1, 0, 1]) >>> q = ZZ.map([1, -1]) >>> dup_transform(f, p, q, ZZ) [1, -2, 5, -4, 4] """ if not f: return [] n = dup_degree(f) h, Q = [f[0]], [[K.one]] for i in xrange(0, n): Q.append(dup_mul(Q[-1], q, K)) for c, q in zip(f[1:], Q[1:]): h = dup_mul(h, p, K) q = dup_mul_ground(q, c, K) h = dup_add(h, q, K) return h def dup_compose(f, g, K): """ Evaluate functional composition ``f(g)`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_compose >>> f = ZZ.map([1, 1, 0]) >>> g = ZZ.map([1, -1]) >>> dup_compose(f, g, ZZ) [1, -1, 0] """ if len(g) <= 1: return dup_strip([dup_eval(f, dup_LC(g, K), K)]) if not f: return [] h = [f[0]] for c in f[1:]: h = dup_mul(h, g, K) h = dup_add_term(h, c, 0, K) return h @cythonized("u") def dmp_compose(f, g, u, K): """ Evaluate functional composition ``f(g)`` in ``K[X]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dmp_compose >>> f = ZZ.map([[1, 2], [1, 0]]) >>> g = ZZ.map([[1, 0]]) >>> dmp_compose(f, g, 1, ZZ) [[1, 3, 0]] """ if not u: return dup_compose(f, g, K) if dmp_zero_p(f, u): return f h = [f[0]] for c in f[1:]: h = dmp_mul(h, g, u, K) h = dmp_add_term(h, c, 0, u, K) return h @cythonized("s,n,r,i,j") def _dup_right_decompose(f, s, K): """Helper function for :func:`_dup_decompose`.""" n = dup_degree(f) lc = dup_LC(f, K) f = dup_to_raw_dict(f) g = { s : K.one } r = n // s for i in xrange(1, s): coeff = K.zero for j in xrange(0, i): if not n+j-i in f: continue if not s-j in g: continue fc, gc = f[n+j-i], g[s-j] coeff += (i - r*j)*fc*gc g[s-i] = K.quo(coeff, i*r*lc) return dup_from_raw_dict(g, K) @cythonized("i") def _dup_left_decompose(f, h, K): """Helper function for :func:`_dup_decompose`.""" g, i = {}, 0 while f: q, r = dup_div(f, h, K) if dup_degree(r) > 0: return None else: g[i] = dup_LC(r, K) f, i = q, i + 1 return dup_from_raw_dict(g, K) @cythonized("df,s") def _dup_decompose(f, K): """Helper function for :func:`dup_decompose`.""" df = dup_degree(f) for s in xrange(2, df): if df % s != 0: continue h = _dup_right_decompose(f, s, K) if h is not None: g = _dup_left_decompose(f, h, K) if g is not None: return g, h return None def dup_decompose(f, K): """ Computes functional decomposition of ``f`` in ``K[x]``. Given a univariate polynomial ``f`` with coefficients in a field of characteristic zero, returns list ``[f_1, f_2, ..., f_n]``, where:: f = f_1 o f_2 o ... f_n = f_1(f_2(... f_n)) and ``f_2, ..., f_n`` are monic and homogeneous polynomials of at least second degree. Unlike factorization, complete functional decompositions of polynomials are not unique, consider examples: 1. ``f o g = f(x + b) o (g - b)`` 2. ``x**n o x**m = x**m o x**n`` 3. ``T_n o T_m = T_m o T_n`` where ``T_n`` and ``T_m`` are Chebyshev polynomials. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_decompose >>> f = ZZ.map([1, -2, 1, 0, 0]) >>> dup_decompose(f, ZZ) [[1, 0, 0], [1, -1, 0]] **References** 1. [Kozen89]_ """ F = [] while True: result = _dup_decompose(f, K) if result is not None: f, h = result F = [h] + F else: break return [f] + F @cythonized("u") def dmp_lift(f, u, K): """ Convert algebraic coefficients to integers in ``K[X]``. **Examples** >>> from sympy import I >>> from sympy.polys.domains import QQ >>> from sympy.polys.densetools import dmp_lift >>> K = QQ.algebraic_field(I) >>> f = [K(1), K([QQ(1), QQ(0)]), K([QQ(2), QQ(0)])] >>> dmp_lift(f, 0, K) [1/1, 0/1, 2/1, 0/1, 9/1, 0/1, -8/1, 0/1, 16/1] """ if not K.is_Algebraic: raise DomainError('computation can be done only in an algebraic domain') F, monoms, polys = dmp_to_dict(f, u), [], [] for monom, coeff in F.iteritems(): if not coeff.is_ground: monoms.append(monom) perms = variations([-1, 1], len(monoms), repetition=True) for perm in perms: G = dict(F) for sign, monom in zip(perm, monoms): if sign == -1: G[monom] = -G[monom] polys.append(dmp_from_dict(G, u, K)) return dmp_convert(dmp_expand(polys, u, K), u, K, K.dom) def dup_sign_variations(f, K): """ Compute the number of sign variations of ``f`` in ``K[x]``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.densetools import dup_sign_variations >>> f = ZZ.map([1, 0, -1, -1, 1]) >>> dup_sign_variations(f, ZZ) 2 """ prev, k = K.zero, 0 for coeff in f: if coeff*prev < 0: k += 1 if coeff: prev = coeff return k def dup_clear_denoms(f, K0, K1=None, convert=False): """ Clear denominators, i.e. transform ``K_0`` to ``K_1``. **Examples** >>> from sympy.polys.domains import QQ, ZZ >>> from sympy.polys.densetools import dup_clear_denoms >>> f = [QQ(1,2), QQ(1,3)] >>> dup_clear_denoms(f, QQ, convert=False) (6, [3/1, 2/1]) >>> f = [QQ(1,2), QQ(1,3)] >>> dup_clear_denoms(f, QQ, convert=True) (6, [3, 2]) """ if K1 is None: K1 = K0.get_ring() common = K1.one for c in f: common = K1.lcm(common, K0.denom(c)) if not K1.is_one(common): f = dup_mul_ground(f, common, K0) if not convert: return common, f else: return common, dup_convert(f, K0, K1) @cythonized("v,w") def _rec_clear_denoms(g, v, K0, K1): """Recursive helper for :func:`dmp_clear_denoms`.""" common = K1.one if not v: for c in g: common = K1.lcm(common, K0.denom(c)) else: w = v-1 for c in g: common = K1.lcm(common, _rec_clear_denoms(c, w, K0, K1)) return common @cythonized("u") def dmp_clear_denoms(f, u, K0, K1=None, convert=False): """ Clear denominators, i.e. transform ``K_0`` to ``K_1``. **Examples** >>> from sympy.polys.domains import QQ, ZZ >>> from sympy.polys.densetools import dmp_clear_denoms >>> f = [[QQ(1,2)], [QQ(1,3), QQ(1)]] >>> dmp_clear_denoms(f, 1, QQ, convert=False) (6, [[3/1], [2/1, 6/1]]) >>> f = [[QQ(1,2)], [QQ(1,3), QQ(1)]] >>> dmp_clear_denoms(f, 1, QQ, convert=True) (6, [[3], [2, 6]]) """ if not u: return dup_clear_denoms(f, K0, K1, convert=convert) if K1 is None: K1 = K0.get_ring() common = _rec_clear_denoms(f, u, K0, K1) if not K1.is_one(common): f = dmp_mul_ground(f, common, u, K0) if not convert: return common, f else: return common, dmp_convert(f, u, K0, K1) @cythonized('i,n') def dup_revert(f, n, K): """ Compute ``f**(-1)`` mod ``x**n`` using Newton iteration. This function computes first ``2**n`` terms of a polynomial that is a result of inversion of a polynomial modulo ``x**n``. This is useful to efficiently compute series expansion of ``1/f``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densetools import dup_revert >>> f = [-QQ(1,720), QQ(0), QQ(1,24), QQ(0), -QQ(1,2), QQ(0), QQ(1)] >>> dup_revert(f, 8, QQ) [61/720, 0/1, 5/24, 0/1, 1/2, 0/1, 1/1] """ g = [K.revert(dup_TC(f, K))] h = [K.one, K.zero, K.zero] N = int(ceil(log(n, 2))) for i in xrange(1, N + 1): a = dup_mul_ground(g, K(2), K) b = dup_mul(f, dup_sqr(g, K), K) g = dup_rem(dup_sub(a, b, K), h, K) h = dup_lshift(h, dup_degree(h), K) return g def dmp_revert(f, g, u, K): """ Compute ``f**(-1)`` mod ``x**n`` using Newton iteration. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.densetools import dmp_revert """ if not u: return dup_revert(f, g, K) else: raise MultivariatePolynomialError(f, g) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/euclidtools.py0000644000175000017500000012543612014170666025160 0ustar georgeskgeorgesk"""Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ from sympy.polys.densebasic import ( dup_strip, dmp_raise, dmp_zero, dmp_one, dmp_ground, dmp_one_p, dmp_zero_p, dmp_zeros, dup_degree, dmp_degree, dmp_degree_in, dup_LC, dmp_LC, dmp_ground_LC, dmp_multi_deflate, dmp_inflate, dup_convert, dmp_convert, dmp_apply_pairs) from sympy.polys.densearith import ( dup_sub_mul, dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_pow, dmp_pow, dup_div, dmp_div, dup_rem, dmp_rem, dup_quo, dmp_quo, dup_prem, dmp_prem, dup_mul_ground, dmp_mul_ground, dup_mul_term, dmp_mul_term, dup_quo_ground, dmp_quo_ground, dup_max_norm, dmp_max_norm) from sympy.polys.densetools import ( dup_clear_denoms, dmp_clear_denoms, dup_diff, dmp_diff, dup_eval, dmp_eval, dmp_eval_in, dup_trunc, dmp_ground_trunc, dup_monic, dmp_ground_monic, dup_primitive, dmp_ground_primitive, dup_extract, dmp_ground_extract) from sympy.polys.galoistools import ( gf_int, gf_crt) from sympy.polys.polyerrors import ( MultivariatePolynomialError, HeuristicGCDFailed, HomomorphismFailed, NotInvertible, DomainError) from sympy.polys.polyconfig import query from sympy.utilities import cythonized from sympy.ntheory import nextprime def dup_half_gcdex(f, g, K): """ Half extended Euclidean algorithm in `F[x]`. Returns ``(s, h)`` such that ``h = gcd(f, g)`` and ``s*f = h (mod g)``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_half_gcdex >>> f = QQ.map([1, -2, -6, 12, 15]) >>> g = QQ.map([1, 1, -4, -4]) >>> dup_half_gcdex(f, g, QQ) ([-1/5, 3/5], [1/1, 1/1]) """ if not (K.has_Field or not K.is_Exact): raise DomainError("can't compute half extended GCD over %s" % K) a, b = [K.one], [] while g: q, r = dup_div(f, g, K) f, g = g, r a, b = b, dup_sub_mul(a, q, b, K) a = dup_quo_ground(a, dup_LC(f, K), K) f = dup_monic(f, K) return a, f def dmp_half_gcdex(f, g, u, K): """ Half extended Euclidean algorithm in `F[X]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_half_gcdex """ if not u: return dup_half_gcdex(f, g, K) else: raise MultivariatePolynomialError(f, g) def dup_gcdex(f, g, K): """ Extended Euclidean algorithm in `F[x]`. Returns ``(s, t, h)`` such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_gcdex >>> f = QQ.map([1, -2, -6, 12, 15]) >>> g = QQ.map([1, 1, -4, -4]) >>> dup_gcdex(f, g, QQ) ([-1/5, 3/5], [1/5, -6/5, 2/1], [1/1, 1/1]) """ s, h = dup_half_gcdex(f, g, K) F = dup_sub_mul(h, s, f, K) t = dup_quo(F, g, K) return s, t, h def dmp_gcdex(f, g, u, K): """ Extended Euclidean algorithm in `F[X]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_gcdex """ if not u: return dup_gcdex(f, g, K) else: raise MultivariatePolynomialError(f, g) def dup_invert(f, g, K): """ Compute multiplicative inverse of `f` modulo `g` in `F[x]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_invert >>> f = QQ.map([1, 0, -1]) >>> g = QQ.map([2, -1]) >>> h = QQ.map([1, -1]) >>> dup_invert(f, g, QQ) [-4/3] >>> dup_invert(f, h, QQ) Traceback (most recent call last): ... NotInvertible: zero divisor """ s, h = dup_half_gcdex(f, g, K) if h == [K.one]: return dup_rem(s, g, K) else: raise NotInvertible("zero divisor") def dmp_invert(f, g, u, K): """ Compute multiplicative inverse of `f` modulo `g` in `F[X]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_invert """ if not u: return dup_invert(f, g, K) else: raise MultivariatePolynomialError(f, g) def dup_euclidean_prs(f, g, K): """ Euclidean polynomial remainder sequence (PRS) in `K[x]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_euclidean_prs >>> f = QQ.map([1, 0, 1, 0, -3, -3, 8, 2, -5]) >>> g = QQ.map([3, 0, 5, 0, -4, -9, 21]) >>> prs = dup_euclidean_prs(f, g, QQ) >>> prs[0] [1/1, 0/1, 1/1, 0/1, -3/1, -3/1, 8/1, 2/1, -5/1] >>> prs[1] [3/1, 0/1, 5/1, 0/1, -4/1, -9/1, 21/1] >>> prs[2] [-5/9, 0/1, 1/9, 0/1, -1/3] >>> prs[3] [-117/25, -9/1, 441/25] >>> prs[4] [233150/19773, -102500/6591] >>> prs[5] [-1288744821/543589225] """ prs = [f, g] h = dup_rem(f, g, K) while h: prs.append(h) f, g = g, h h = dup_rem(f, g, K) return prs def dmp_euclidean_prs(f, g, u, K): """ Euclidean polynomial remainder sequence (PRS) in `K[X]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_euclidean_prs """ if not u: return dup_euclidean_prs(f, g, K) else: raise MultivariatePolynomialError(f, g) def dup_primitive_prs(f, g, K): """ Primitive polynomial remainder sequence (PRS) in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_primitive_prs >>> f = ZZ.map([1, 0, 1, 0, -3, -3, 8, 2, -5]) >>> g = ZZ.map([3, 0, 5, 0, -4, -9, 21]) >>> prs = dup_primitive_prs(f, g, ZZ) >>> prs[0] [1, 0, 1, 0, -3, -3, 8, 2, -5] >>> prs[1] [3, 0, 5, 0, -4, -9, 21] >>> prs[2] [-5, 0, 1, 0, -3] >>> prs[3] [13, 25, -49] >>> prs[4] [4663, -6150] >>> prs[5] [1] """ prs = [f, g] _, h = dup_primitive(dup_prem(f, g, K), K) while h: prs.append(h) f, g = g, h _, h = dup_primitive(dup_prem(f, g, K), K) return prs def dmp_primitive_prs(f, g, u, K): """ Primitive polynomial remainder sequence (PRS) in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_primitive_prs """ if not u: return dup_primitive_prs(f, g, K) else: raise MultivariatePolynomialError(f, g) @cythonized("n,m,d,k") def dup_inner_subresultants(f, g, K): """ Subresultant PRS algorithm in `K[x]`. Computes the subresultant polynomial remainder sequence (PRS) of `f` and `g`, and the values for `\beta_i` and `\delta_i`. The last two sequences of values are necessary for computing the resultant in :func:`dup_prs_resultant`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_inner_subresultants >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([1, 0, -1]) >>> dup_inner_subresultants(f, g, ZZ) ([[1, 0, 1], [1, 0, -1], [-2]], [-1, -1], [0, 2]) """ n = dup_degree(f) m = dup_degree(g) if n < m: f, g = g, f n, m = m, n R = [f, g] d = n - m b = (-K.one)**(d+1) c = -K.one B, D = [b], [d] if not f or not g: return R, B, D h = dup_prem(f, g, K) h = dup_mul_ground(h, b, K) while h: k = dup_degree(h) R.append(h) lc = dup_LC(g, K) if not d: q = c else: q = c**(d-1) c = K.quo((-lc)**d, q) b = -lc * c**(m-k) f, g, m, d = g, h, k, m-k B.append(b) D.append(d) h = dup_prem(f, g, K) h = dup_quo_ground(h, b, K) return R, B, D def dup_subresultants(f, g, K): """ Computes subresultant PRS of two polynomials in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_subresultants >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([1, 0, -1]) >>> dup_subresultants(f, g, ZZ) [[1, 0, 1], [1, 0, -1], [-2]] """ return dup_inner_subresultants(f, g, K)[0] @cythonized("s,i,du,dv,dw") def dup_prs_resultant(f, g, K): """ Resultant algorithm in `K[x]` using subresultant PRS. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_prs_resultant >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([1, 0, -1]) >>> dup_prs_resultant(f, g, ZZ) (4, [[1, 0, 1], [1, 0, -1], [-2]]) """ if not f or not g: return (K.zero, []) R, B, D = dup_inner_subresultants(f, g, K) if dup_degree(R[-1]) > 0: return (K.zero, R) if R[-2] == [K.one]: return (dup_LC(R[-1], K), R) s, i = 1, 1 p, q = K.one, K.one for b, d in list(zip(B, D))[:-1]: du = dup_degree(R[i-1]) dv = dup_degree(R[i ]) dw = dup_degree(R[i+1]) if du % 2 and dv % 2: s = -s lc, i = dup_LC(R[i], K), i+1 p *= b**dv * lc**(du-dw) q *= lc**(dv*(1+d)) if s < 0: p = -p i = dup_degree(R[-2]) res = dup_LC(R[-1], K)**i res = K.quo(res*p, q) return res, R def dup_resultant(f, g, K): """ Computes resultant of two polynomials in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_resultant >>> f = ZZ.map([1, 0, 1]) >>> g = ZZ.map([1, 0, -1]) >>> dup_resultant(f, g, ZZ) 4 """ return dup_prs_resultant(f, g, K)[0] @cythonized("u,v,n,m,d,k") def dmp_inner_subresultants(f, g, u, K): """ Subresultant PRS algorithm in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_inner_subresultants >>> f = ZZ.map([[3, 0], [], [-1, 0, 0, -4]]) >>> g = ZZ.map([[1], [1, 0, 0, 0], [-9]]) >>> a = [[3, 0, 0, 0, 0], [1, 0, -27, 4]] >>> b = [[-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]] >>> R = ZZ.map([f, g, a, b]) >>> B = ZZ.map([[-1], [1], [9, 0, 0, 0, 0, 0, 0, 0, 0]]) >>> D = ZZ.map([0, 1, 1]) >>> dmp_inner_subresultants(f, g, 1, ZZ) == (R, B, D) True """ if not u: return dup_inner_subresultants(f, g, K) n = dmp_degree(f, u) m = dmp_degree(g, u) if n < m: f, g = g, f n, m = m, n R = [f, g] d = n - m v = u - 1 b = dmp_pow(dmp_ground(-K.one, v), d+1, v, K) c = dmp_ground(-K.one, v) B, D = [b], [d] if dmp_zero_p(f, u) or dmp_zero_p(g, u): return R, B, D h = dmp_prem(f, g, u, K) h = dmp_mul_term(h, b, 0, u, K) while not dmp_zero_p(h, u): k = dmp_degree(h, u) R.append(h) lc = dmp_LC(g, K) p = dmp_pow(dmp_neg(lc, v, K), d, v, K) if not d: q = c else: q = dmp_pow(c, d-1, v, K) c = dmp_quo(p, q, v, K) b = dmp_mul(dmp_neg(lc, v, K), dmp_pow(c, m-k, v, K), v, K) f, g, m, d = g, h, k, m-k B.append(b) D.append(d) h = dmp_prem(f, g, u, K) h = [ dmp_quo(ch, b, v, K) for ch in h ] return R, B, D @cythonized("u") def dmp_subresultants(f, g, u, K): """ Computes subresultant PRS of two polynomials in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_subresultants >>> f = [[3, 0], [], [-1, 0, 0, -4]] >>> g = [[1], [1, 0, 0, 0], [-9]] >>> a = [[3, 0, 0, 0, 0], [1, 0, -27, 4]] >>> b = [[-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]] >>> dmp_subresultants(f, g, 1, ZZ) == [f, g, a, b] True """ return dmp_inner_subresultants(f, g, u, K)[0] @cythonized("u,v,s,i,d,du,dv,dw") def dmp_prs_resultant(f, g, u, K): """ Resultant algorithm in `K[X]` using subresultant PRS. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_prs_resultant >>> f = ZZ.map([[3, 0], [], [-1, 0, 0, -4]]) >>> g = ZZ.map([[1], [1, 0, 0, 0], [-9]]) >>> a = ZZ.map([[3, 0, 0, 0, 0], [1, 0, -27, 4]]) >>> b = ZZ.map([[-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16]]) >>> dmp_prs_resultant(f, g, 1, ZZ) == (b[0], [f, g, a, b]) True """ if not u: return dup_prs_resultant(f, g, K) if dmp_zero_p(f, u) or dmp_zero_p(g, u): return (dmp_zero(u-1), []) R, B, D = dmp_inner_subresultants(f, g, u, K) if dmp_degree(R[-1], u) > 0: return (dmp_zero(u-1), R) if dmp_one_p(R[-2], u, K): return (dmp_LC(R[-1], K), R) s, i, v = 1, 1, u-1 p = dmp_one(v, K) q = dmp_one(v, K) for b, d in list(zip(B, D))[:-1]: du = dmp_degree(R[i-1], u) dv = dmp_degree(R[i ], u) dw = dmp_degree(R[i+1], u) if du % 2 and dv % 2: s = -s lc, i = dmp_LC(R[i], K), i+1 p = dmp_mul(dmp_mul(p, dmp_pow(b, dv, v, K), v, K), dmp_pow(lc, du-dw, v, K), v, K) q = dmp_mul(q, dmp_pow(lc, dv*(1+d), v, K), v, K) _, p, q = dmp_inner_gcd(p, q, v, K) if s < 0: p = dmp_neg(p, v, K) i = dmp_degree(R[-2], u) res = dmp_pow(dmp_LC(R[-1], K), i, v, K) res = dmp_quo(dmp_mul(res, p, v, K), q, v, K) return res, R @cythonized("u,v,n,m,N,M,B") def dmp_zz_modular_resultant(f, g, p, u, K): """ Compute resultant of `f` and `g` modulo a prime `p`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_zz_modular_resultant >>> f = ZZ.map([[1], [1, 2]]) >>> g = ZZ.map([[2, 1], [3]]) >>> dmp_zz_modular_resultant(f, g, ZZ(5), 1, ZZ) [-2, 0, 1] """ if not u: return gf_int(dup_prs_resultant(f, g, K)[0] % p, p) v = u - 1 n = dmp_degree(f, u) m = dmp_degree(g, u) N = dmp_degree_in(f, 1, u) M = dmp_degree_in(g, 1, u) B = n*M + m*N D, a = [K.one], -K.one r = dmp_zero(v) while dup_degree(D) <= B: while True: a += K.one if a == p: raise HomomorphismFailed('no luck') F = dmp_eval_in(f, gf_int(a, p), 1, u, K) if dmp_degree(F, v) == n: G = dmp_eval_in(g, gf_int(a, p), 1, u, K) if dmp_degree(G, v) == m: break R = dmp_zz_modular_resultant(F, G, p, v, K) e = dmp_eval(r, a, v, K) if not v: R = dup_strip([R]) e = dup_strip([e]) else: R = [R] e = [e] d = K.invert(dup_eval(D, a, K), p) d = dup_mul_ground(D, d, K) d = dmp_raise(d, v, 0, K) c = dmp_mul(d, dmp_sub(R, e, v, K), v, K) r = dmp_add(r, c, v, K) r = dmp_ground_trunc(r, p, v, K) D = dup_mul(D, [K.one, -a], K) D = dup_trunc(D, p, K) return r def _collins_crt(r, R, P, p, K): """Wrapper of CRT for Collins's resultant algorithm. """ return gf_int(gf_crt([r, R], [P, p], K), P*p) @cythonized("u,v,n,m") def dmp_zz_collins_resultant(f, g, u, K): """ Collins's modular resultant algorithm in `Z[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_zz_collins_resultant >>> f = ZZ.map([[1], [1, 2]]) >>> g = ZZ.map([[2, 1], [3]]) >>> dmp_zz_collins_resultant(f, g, 1, ZZ) [-2, -5, 1] """ n = dmp_degree(f, u) m = dmp_degree(g, u) if n < 0 or m < 0: return dmp_zero(u-1) A = dmp_max_norm(f, u, K) B = dmp_max_norm(g, u, K) a = dmp_ground_LC(f, u, K) b = dmp_ground_LC(g, u, K) v = u - 1 B = K(2)*K.factorial(n+m)*A**m*B**n r, p, P = dmp_zero(v), K.one, K.one while P <= B: p = K(nextprime(p)) while not (a % p) or not (b % p): p = K(nextprime(p)) F = dmp_ground_trunc(f, p, u, K) G = dmp_ground_trunc(g, p, u, K) try: R = dmp_zz_modular_resultant(F, G, p, u, K) except HomomorphismFailed: continue if K.is_one(P): r = R else: r = dmp_apply_pairs(r, R, _collins_crt, (P, p, K), v, K) P *= p return r @cythonized("u,n,m") def dmp_qq_collins_resultant(f, g, u, K0): """ Collins's modular resultant algorithm in `Q[X]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_qq_collins_resultant >>> f = [[QQ(1,2)], [QQ(1), QQ(2,3)]] >>> g = [[QQ(2), QQ(1)], [QQ(3)]] >>> dmp_qq_collins_resultant(f, g, 1, QQ) [-2/1, -7/3, 5/6] """ n = dmp_degree(f, u) m = dmp_degree(g, u) if n < 0 or m < 0: return dmp_zero(u-1) K1 = K0.get_ring() cf, f = dmp_clear_denoms(f, u, K0, K1) cg, g = dmp_clear_denoms(g, u, K0, K1) f = dmp_convert(f, u, K0, K1) g = dmp_convert(g, u, K0, K1) r = dmp_zz_collins_resultant(f, g, u, K1) r = dmp_convert(r, u-1, K1, K0) c = K0.convert(cf**m * cg**n, K1) return dmp_quo_ground(r, c, u-1, K0) @cythonized("u") def dmp_resultant(f, g, u, K): """ Computes resultant of two polynomials in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_resultant >>> f = ZZ.map([[3, 0], [], [-1, 0, 0, -4]]) >>> g = ZZ.map([[1], [1, 0, 0, 0], [-9]]) >>> dmp_resultant(f, g, 1, ZZ) [-3, 0, 0, -12, 1, 0, -54, 8, 729, -216, 16] """ if not u: return dup_resultant(f, g, K) if K.has_Field: if K.is_QQ and query('USE_COLLINS_RESULTANT'): return dmp_qq_collins_resultant(f, g, u, K) else: if K.is_ZZ and query('USE_COLLINS_RESULTANT'): return dmp_zz_collins_resultant(f, g, u, K) return dmp_prs_resultant(f, g, u, K)[0] @cythonized("d,s") def dup_discriminant(f, K): """ Computes discriminant of a polynomial in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_discriminant >>> dup_discriminant([ZZ(1), ZZ(2), ZZ(3)], ZZ) -8 """ d = dup_degree(f) if d <= 0: return K.zero else: s = (-1)**((d*(d-1)) // 2) c = dup_LC(f, K) r = dup_resultant(f, dup_diff(f, 1, K), K) return K.quo(r, c*K(s)) @cythonized("u,v,d,s") def dmp_discriminant(f, u, K): """ Computes discriminant of a polynomial in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_discriminant >>> f = ZZ.map([[[[1]], [[]]], [[[1], []]], [[[1, 0]]]]) >>> dmp_discriminant(f, 3, ZZ) [[[-4, 0]], [[1], [], []]] """ if not u: return dup_discriminant(f, K) d, v = dmp_degree(f, u), u-1 if d <= 0: return dmp_zero(v) else: s = (-1)**((d*(d-1)) // 2) c = dmp_LC(f, K) r = dmp_resultant(f, dmp_diff(f, 1, u, K), u, K) c = dmp_mul_ground(c, K(s), v, K) return dmp_quo(r, c, v, K) def _dup_rr_trivial_gcd(f, g, K): """Handle trivial cases in GCD algorithm over a ring. """ if not (f or g): return [], [], [] elif not f: if K.is_nonnegative(dup_LC(g, K)): return g, [], [K.one] else: return dup_neg(g, K), [], [-K.one] elif not g: if K.is_nonnegative(dup_LC(f, K)): return f, [K.one], [] else: return dup_neg(f, K), [-K.one], [] return None def _dup_ff_trivial_gcd(f, g, K): """Handle trivial cases in GCD algorithm over a field. """ if not (f or g): return [], [], [] elif not f: return dup_monic(g, K), [], [dup_LC(g, K)] elif not g: return dup_monic(f, K), [dup_LC(f, K)], [] else: return None @cythonized("u") def _dmp_rr_trivial_gcd(f, g, u, K): """Handle trivial cases in GCD algorithm over a ring. """ zero_f = dmp_zero_p(f, u) zero_g = dmp_zero_p(g, u) if zero_f and zero_g: return tuple(dmp_zeros(3, u, K)) elif zero_f: if K.is_nonnegative(dmp_ground_LC(g, u, K)): return g, dmp_zero(u), dmp_one(u, K) else: return dmp_neg(g, u, K), dmp_zero(u), dmp_ground(-K.one, u) elif zero_g: if K.is_nonnegative(dmp_ground_LC(f, u, K)): return f, dmp_one(u, K), dmp_zero(u) else: return dmp_neg(f, u, K), dmp_ground(-K.one, u), dmp_zero(u) elif query('USE_SIMPLIFY_GCD'): return _dmp_simplify_gcd(f, g, u, K) else: return None @cythonized("u") def _dmp_ff_trivial_gcd(f, g, u, K): """Handle trivial cases in GCD algorithm over a field. """ zero_f = dmp_zero_p(f, u) zero_g = dmp_zero_p(g, u) if zero_f and zero_g: return tuple(dmp_zeros(3, u, K)) elif zero_f: return (dmp_ground_monic(g, u, K), dmp_zero(u), dmp_ground(dmp_ground_LC(g, u, K), u)) elif zero_g: return (dmp_ground_monic(f, u, K), dmp_ground(dmp_ground_LC(f, u, K), u), dmp_zero(u)) elif query('USE_SIMPLIFY_GCD'): return _dmp_simplify_gcd(f, g, u, K) else: return None @cythonized("u,v,df,dg") def _dmp_simplify_gcd(f, g, u, K): """Try to eliminate `x_0` from GCD computation in `K[X]`. """ df = dmp_degree(f, u) dg = dmp_degree(g, u) if df > 0 and dg > 0: return None if not (df or dg): F = dmp_LC(f, K) G = dmp_LC(g, K) else: if not df: F = dmp_LC(f, K) G = dmp_content(g, u, K) else: F = dmp_content(f, u, K) G = dmp_LC(g, K) v = u - 1 h = dmp_gcd(F, G, v, K) cff = [ dmp_quo(cf, h, v, K) for cf in f ] cfg = [ dmp_quo(cg, h, v, K) for cg in g ] return [h], cff, cfg def dup_rr_prs_gcd(f, g, K): """ Computes polynomial GCD using subresultants over a ring. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_rr_prs_gcd >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -3, 2]) >>> dup_rr_prs_gcd(f, g, ZZ) ([1, -1], [1, 1], [1, -2]) """ result = _dup_rr_trivial_gcd(f, g, K) if result is not None: return result fc, F = dup_primitive(f, K) gc, G = dup_primitive(g, K) c = K.gcd(fc, gc) h = dup_subresultants(F, G, K)[-1] _, h = dup_primitive(h, K) if K.is_negative(dup_LC(h, K)): c = -c h = dup_mul_ground(h, c, K) cff = dup_quo(f, h, K) cfg = dup_quo(g, h, K) return h, cff, cfg def dup_ff_prs_gcd(f, g, K): """ Computes polynomial GCD using subresultants over a field. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_ff_prs_gcd >>> f = QQ.map([1, 0, -1]) >>> g = QQ.map([1, -3, 2]) >>> dup_ff_prs_gcd(f, g, QQ) ([1/1, -1/1], [1/1, 1/1], [1/1, -2/1]) """ result = _dup_ff_trivial_gcd(f, g, K) if result is not None: return result h = dup_subresultants(f, g, K)[-1] h = dup_monic(h, K) cff = dup_quo(f, h, K) cfg = dup_quo(g, h, K) return h, cff, cfg @cythonized("u") def dmp_rr_prs_gcd(f, g, u, K): """ Computes polynomial GCD using subresultants over a ring. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_rr_prs_gcd >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> g = ZZ.map([[1], [1, 0], []]) >>> dmp_rr_prs_gcd(f, g, 1, ZZ) ([[1], [1, 0]], [[1], [1, 0]], [[1], []]) """ if not u: return dup_rr_prs_gcd(f, g, K) result = _dmp_rr_trivial_gcd(f, g, u, K) if result is not None: return result fc, F = dmp_primitive(f, u, K) gc, G = dmp_primitive(g, u, K) h = dmp_subresultants(F, G, u, K)[-1] c, _, _ = dmp_rr_prs_gcd(fc, gc, u-1, K) if K.is_negative(dmp_ground_LC(h, u, K)): h = dmp_neg(h, u, K) _, h = dmp_primitive(h, u, K) h = dmp_mul_term(h, c, 0, u, K) cff = dmp_quo(f, h, u, K) cfg = dmp_quo(g, h, u, K) return h, cff, cfg @cythonized("u") def dmp_ff_prs_gcd(f, g, u, K): """ Computes polynomial GCD using subresultants over a field. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_ff_prs_gcd >>> f = [[QQ(1,2)], [QQ(1), QQ(0)], [QQ(1,2), QQ(0), QQ(0)]] >>> g = [[QQ(1)], [QQ(1), QQ(0)], []] >>> dmp_ff_prs_gcd(f, g, 1, QQ) ([[1/1], [1/1, 0/1]], [[1/2], [1/2, 0/1]], [[1/1], []]) """ if not u: return dup_ff_prs_gcd(f, g, K) result = _dmp_ff_trivial_gcd(f, g, u, K) if result is not None: return result fc, F = dmp_primitive(f, u, K) gc, G = dmp_primitive(g, u, K) h = dmp_subresultants(F, G, u, K)[-1] c, _, _ = dmp_ff_prs_gcd(fc, gc, u-1, K) _, h = dmp_primitive(h, u, K) h = dmp_mul_term(h, c, 0, u, K) h = dmp_ground_monic(h, u, K) cff = dmp_quo(f, h, u, K) cfg = dmp_quo(g, h, u, K) return h, cff, cfg HEU_GCD_MAX = 6 def _dup_zz_gcd_interpolate(h, x, K): """Interpolate polynomial GCD from integer GCD. """ f = [] while h: g = h % x if g > x // 2: g -= x f.insert(0, g) h = (h-g) // x return f @cythonized("i,df,dg") def dup_zz_heu_gcd(f, g, K): """ Heuristic polynomial GCD in `Z[x]`. Given univariate polynomials `f` and `g` in `Z[x]`, returns their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` such that:: h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) The algorithm is purely heuristic which means it may fail to compute the GCD. This will be signaled by raising an exception. In this case you will need to switch to another GCD method. The algorithm computes the polynomial GCD by evaluating polynomials f and g at certain points and computing (fast) integer GCD of those evaluations. The polynomial GCD is recovered from the integer image by interpolation. The final step is to verify if the result is the correct GCD. This gives cofactors as a side effect. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_zz_heu_gcd >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -3, 2]) >>> dup_zz_heu_gcd(f, g, ZZ) ([1, -1], [1, 1], [1, -2]) **References** 1. [Liao95]_ """ result = _dup_rr_trivial_gcd(f, g, K) if result is not None: return result df = dup_degree(f) dg = dup_degree(g) gcd, f, g = dup_extract(f, g, K) if df == 0 or dg == 0: return [gcd], f, g f_norm = dup_max_norm(f, K) g_norm = dup_max_norm(g, K) B = 2*min(f_norm, g_norm) + 29 x = max(min(B, 99*K.sqrt(B)), 2*min(f_norm // abs(dup_LC(f, K)), g_norm // abs(dup_LC(g, K))) + 2) for i in xrange(0, HEU_GCD_MAX): ff = dup_eval(f, x, K) gg = dup_eval(g, x, K) if ff and gg: h = K.gcd(ff, gg) cff = ff // h cfg = gg // h h = _dup_zz_gcd_interpolate(h, x, K) h = dup_primitive(h, K)[1] cff_, r = dup_div(f, h, K) if not r: cfg_, r = dup_div(g, h, K) if not r: h = dup_mul_ground(h, gcd, K) return h, cff_, cfg_ cff = _dup_zz_gcd_interpolate(cff, x, K) h, r = dup_div(f, cff, K) if not r: cfg_, r = dup_div(g, h, K) if not r: h = dup_mul_ground(h, gcd, K) return h, cff, cfg_ cfg = _dup_zz_gcd_interpolate(cfg, x, K) h, r = dup_div(g, cfg, K) if not r: cff_, r = dup_div(f, h, K) if not r: h = dup_mul_ground(h, gcd, K) return h, cff, cfg x = 73794*x * K.sqrt(K.sqrt(x)) // 27011 raise HeuristicGCDFailed('no luck') @cythonized("v") def _dmp_zz_gcd_interpolate(h, x, v, K): """Interpolate polynomial GCD from integer GCD. """ f = [] while not dmp_zero_p(h, v): g = dmp_ground_trunc(h, x, v, K) f.insert(0, g) h = dmp_sub(h, g, v, K) h = dmp_quo_ground(h, x, v, K) if K.is_negative(dmp_ground_LC(f, v+1, K)): return dmp_neg(f, v+1, K) else: return f @cythonized("u,v,i,dg,df") def dmp_zz_heu_gcd(f, g, u, K): """ Heuristic polynomial GCD in `Z[X]`. Given univariate polynomials `f` and `g` in `Z[X]`, returns their GCD and cofactors, i.e. polynomials ``h``, ``cff`` and ``cfg`` such that:: h = gcd(f, g), cff = quo(f, h) and cfg = quo(g, h) The algorithm is purely heuristic which means it may fail to compute the GCD. This will be signaled by raising an exception. In this case you will need to switch to another GCD method. The algorithm computes the polynomial GCD by evaluating polynomials f and g at certain points and computing (fast) integer GCD of those evaluations. The polynomial GCD is recovered from the integer image by interpolation. The evaluation proces reduces f and g variable by variable into a large integer. The final step is to verify if the interpolated polynomial is the correct GCD. This gives cofactors of the input polynomials as a side effect. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_zz_heu_gcd >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> g = ZZ.map([[1], [1, 0], []]) >>> dmp_zz_heu_gcd(f, g, 1, ZZ) ([[1], [1, 0]], [[1], [1, 0]], [[1], []]) **References** 1. [Liao95]_ """ if not u: return dup_zz_heu_gcd(f, g, K) result = _dmp_rr_trivial_gcd(f, g, u, K) if result is not None: return result df = dmp_degree(f, u) dg = dmp_degree(g, u) gcd, f, g = dmp_ground_extract(f, g, u, K) f_norm = dmp_max_norm(f, u, K) g_norm = dmp_max_norm(g, u, K) B = 2*min(f_norm, g_norm) + 29 x = max(min(B, 99*K.sqrt(B)), 2*min(f_norm // abs(dmp_ground_LC(f, u, K)), g_norm // abs(dmp_ground_LC(g, u, K))) + 2) for i in xrange(0, HEU_GCD_MAX): ff = dmp_eval(f, x, u, K) gg = dmp_eval(g, x, u, K) v = u - 1 if not (dmp_zero_p(ff, v) or dmp_zero_p(gg, v)): h, cff, cfg = dmp_zz_heu_gcd(ff, gg, v, K) h = _dmp_zz_gcd_interpolate(h, x, v, K) h = dmp_ground_primitive(h, u, K)[1] cff_, r = dmp_div(f, h, u, K) if dmp_zero_p(r, u): cfg_, r = dmp_div(g, h, u, K) if dmp_zero_p(r, u): h = dmp_mul_ground(h, gcd, u, K) return h, cff_, cfg_ cff = _dmp_zz_gcd_interpolate(cff, x, v, K) h, r = dmp_div(f, cff, u, K) if dmp_zero_p(r, u): cfg_, r = dmp_div(g, h, u, K) if dmp_zero_p(r, u): h = dmp_mul_ground(h, gcd, u, K) return h, cff, cfg_ cfg = _dmp_zz_gcd_interpolate(cfg, x, v, K) h, r = dmp_div(g, cfg, u, K) if dmp_zero_p(r, u): cff_, r = dmp_div(f, h, u, K) if dmp_zero_p(r, u): h = dmp_mul_ground(h, gcd, u, K) return h, cff_, cfg x = 73794*x * K.sqrt(K.sqrt(x)) // 27011 raise HeuristicGCDFailed('no luck') def dup_qq_heu_gcd(f, g, K0): """ Heuristic polynomial GCD in `Q[x]`. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_qq_heu_gcd >>> f = [QQ(1,2), QQ(7,4), QQ(3,2)] >>> g = [QQ(1,2), QQ(1), QQ(0)] >>> dup_qq_heu_gcd(f, g, QQ) ([1/1, 2/1], [1/2, 3/4], [1/2, 0/1]) """ result = _dup_ff_trivial_gcd(f, g, K0) if result is not None: return result K1 = K0.get_ring() cf, f = dup_clear_denoms(f, K0, K1) cg, g = dup_clear_denoms(g, K0, K1) f = dup_convert(f, K0, K1) g = dup_convert(g, K0, K1) h, cff, cfg = dup_zz_heu_gcd(f, g, K1) h = dup_convert(h, K1, K0) c = dup_LC(h, K0) h = dup_monic(h, K0) cff = dup_convert(cff, K1, K0) cfg = dup_convert(cfg, K1, K0) cff = dup_mul_ground(cff, K0.quo(c, cf), K0) cfg = dup_mul_ground(cfg, K0.quo(c, cg), K0) return h, cff, cfg @cythonized("u") def dmp_qq_heu_gcd(f, g, u, K0): """ Heuristic polynomial GCD in `Q[X]`. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_qq_heu_gcd >>> f = [[QQ(1,4)], [QQ(1), QQ(0)], [QQ(1), QQ(0), QQ(0)]] >>> g = [[QQ(1,2)], [QQ(1), QQ(0)], []] >>> dmp_qq_heu_gcd(f, g, 1, QQ) ([[1/1], [2/1, 0/1]], [[1/4], [1/2, 0/1]], [[1/2], []]) """ result = _dmp_ff_trivial_gcd(f, g, u, K0) if result is not None: return result K1 = K0.get_ring() cf, f = dmp_clear_denoms(f, u, K0, K1) cg, g = dmp_clear_denoms(g, u, K0, K1) f = dmp_convert(f, u, K0, K1) g = dmp_convert(g, u, K0, K1) h, cff, cfg = dmp_zz_heu_gcd(f, g, u, K1) h = dmp_convert(h, u, K1, K0) c = dmp_ground_LC(h, u, K0) h = dmp_ground_monic(h, u, K0) cff = dmp_convert(cff, u, K1, K0) cfg = dmp_convert(cfg, u, K1, K0) cff = dmp_mul_ground(cff, K0.quo(c, cf), u, K0) cfg = dmp_mul_ground(cfg, K0.quo(c, cg), u, K0) return h, cff, cfg def dup_inner_gcd(f, g, K): """ Computes polynomial GCD and cofactors of `f` and `g` in `K[x]`. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_inner_gcd >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -3, 2]) >>> dup_inner_gcd(f, g, ZZ) ([1, -1], [1, 1], [1, -2]) """ if K.has_Field or not K.is_Exact: if K.is_QQ and query('USE_HEU_GCD'): try: return dup_qq_heu_gcd(f, g, K) except HeuristicGCDFailed: pass return dup_ff_prs_gcd(f, g, K) else: if K.is_ZZ and query('USE_HEU_GCD'): try: return dup_zz_heu_gcd(f, g, K) except HeuristicGCDFailed: pass return dup_rr_prs_gcd(f, g, K) @cythonized("u") def _dmp_inner_gcd(f, g, u, K): """Helper function for `dmp_inner_gcd()`. """ if K.has_Field or not K.is_Exact: if K.is_QQ and query('USE_HEU_GCD'): try: return dmp_qq_heu_gcd(f, g, u, K) except HeuristicGCDFailed: pass return dmp_ff_prs_gcd(f, g, u, K) else: if K.is_ZZ and query('USE_HEU_GCD'): try: return dmp_zz_heu_gcd(f, g, u, K) except HeuristicGCDFailed: pass return dmp_rr_prs_gcd(f, g, u, K) @cythonized("u") def dmp_inner_gcd(f, g, u, K): """ Computes polynomial GCD and cofactors of `f` and `g` in `K[X]`. Returns ``(h, cff, cfg)`` such that ``a = gcd(f, g)``, ``cff = quo(f, h)``, and ``cfg = quo(g, h)``. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_inner_gcd >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> g = ZZ.map([[1], [1, 0], []]) >>> dmp_inner_gcd(f, g, 1, ZZ) ([[1], [1, 0]], [[1], [1, 0]], [[1], []]) """ if not u: return dup_inner_gcd(f, g, K) J, (f, g) = dmp_multi_deflate((f, g), u, K) h, cff, cfg = _dmp_inner_gcd(f, g, u, K) return (dmp_inflate(h, J, u, K), dmp_inflate(cff, J, u, K), dmp_inflate(cfg, J, u, K)) def dup_gcd(f, g, K): """ Computes polynomial GCD of `f` and `g` in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_gcd >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -3, 2]) >>> dup_gcd(f, g, ZZ) [1, -1] """ return dup_inner_gcd(f, g, K)[0] @cythonized("u") def dmp_gcd(f, g, u, K): """ Computes polynomial GCD of `f` and `g` in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_gcd >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> g = ZZ.map([[1], [1, 0], []]) >>> dmp_gcd(f, g, 1, ZZ) [[1], [1, 0]] """ return dmp_inner_gcd(f, g, u, K)[0] def dup_rr_lcm(f, g, K): """ Computes polynomial LCM over a ring in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_rr_lcm >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -3, 2]) >>> dup_rr_lcm(f, g, ZZ) [1, -2, -1, 2] """ fc, f = dup_primitive(f, K) gc, g = dup_primitive(g, K) c = K.lcm(fc, gc) h = dup_quo(dup_mul(f, g, K), dup_gcd(f, g, K), K) return dup_mul_ground(h, c, K) def dup_ff_lcm(f, g, K): """ Computes polynomial LCM over a field in `K[x]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dup_ff_lcm >>> f = [QQ(1,2), QQ(7,4), QQ(3,2)] >>> g = [QQ(1,2), QQ(1), QQ(0)] >>> dup_ff_lcm(f, g, QQ) [1/1, 7/2, 3/1, 0/1] """ h = dup_quo(dup_mul(f, g, K), dup_gcd(f, g, K), K) return dup_monic(h, K) def dup_lcm(f, g, K): """ Computes polynomial LCM of `f` and `g` in `K[x]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_lcm >>> f = ZZ.map([1, 0, -1]) >>> g = ZZ.map([1, -3, 2]) >>> dup_lcm(f, g, ZZ) [1, -2, -1, 2] """ if K.has_Field or not K.is_Exact: return dup_ff_lcm(f, g, K) else: return dup_rr_lcm(f, g, K) @cythonized("u") def dmp_rr_lcm(f, g, u, K): """ Computes polynomial LCM over a ring in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_rr_lcm >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> g = ZZ.map([[1], [1, 0], []]) >>> dmp_rr_lcm(f, g, 1, ZZ) [[1], [2, 0], [1, 0, 0], []] """ fc, f = dmp_ground_primitive(f, u, K) gc, g = dmp_ground_primitive(g, u, K) c = K.lcm(fc, gc) h = dmp_quo(dmp_mul(f, g, u, K), dmp_gcd(f, g, u, K), u, K) return dmp_mul_ground(h, c, u, K) @cythonized("u") def dmp_ff_lcm(f, g, u, K): """ Computes polynomial LCM over a field in `K[X]`. **Examples** >>> from sympy.polys.domains import QQ >>> from sympy.polys.euclidtools import dmp_ff_lcm >>> f = [[QQ(1,4)], [QQ(1), QQ(0)], [QQ(1), QQ(0), QQ(0)]] >>> g = [[QQ(1,2)], [QQ(1), QQ(0)], []] >>> dmp_ff_lcm(f, g, 1, QQ) [[1/1], [4/1, 0/1], [4/1, 0/1, 0/1], []] """ h = dmp_quo(dmp_mul(f, g, u, K), dmp_gcd(f, g, u, K), u, K) return dmp_ground_monic(h, u, K) @cythonized("u") def dmp_lcm(f, g, u, K): """ Computes polynomial LCM of `f` and `g` in `K[X]`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_lcm >>> f = ZZ.map([[1], [2, 0], [1, 0, 0]]) >>> g = ZZ.map([[1], [1, 0], []]) >>> dmp_lcm(f, g, 1, ZZ) [[1], [2, 0], [1, 0, 0], []] """ if not u: return dup_lcm(f, g, K) if K.has_Field or not K.is_Exact: return dmp_ff_lcm(f, g, u, K) else: return dmp_rr_lcm(f, g, u, K) @cythonized("u,v") def dmp_content(f, u, K): """ Returns GCD of multivariate coefficients. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_content >>> f = ZZ.map([[2, 6], [4, 12]]) >>> dmp_content(f, 1, ZZ) [2, 6] """ cont, v = dmp_LC(f, K), u-1 if dmp_zero_p(f, u): return cont for c in f[1:]: cont = dmp_gcd(cont, c, v, K) if dmp_one_p(cont, v, K): break if K.is_negative(dmp_ground_LC(cont, v, K)): return dmp_neg(cont, v, K) else: return cont @cythonized("u,v") def dmp_primitive(f, u, K): """ Returns multivariate content and a primitive polynomial. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_primitive >>> f = ZZ.map([[2, 6], [4, 12]]) >>> dmp_primitive(f, 1, ZZ) ([2, 6], [[1], [2]]) """ cont, v = dmp_content(f, u, K), u-1 if dmp_zero_p(f, u) or dmp_one_p(cont, v, K): return cont, f else: return cont, [ dmp_quo(c, cont, v, K) for c in f ] def dup_cancel(f, g, K, include=True): """ Cancel common factors in a rational function `f/g`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dup_cancel >>> f = ZZ.map([2, 0, -2]) >>> g = ZZ.map([1, -2, 1]) >>> dup_cancel(f, g, ZZ) ([2, 2], [1, -1]) """ return dmp_cancel(f, g, 0, K, include=include) def dmp_cancel(f, g, u, K, include=True): """ Cancel common factors in a rational function `f/g`. **Examples** >>> from sympy.polys.domains import ZZ >>> from sympy.polys.euclidtools import dmp_cancel >>> f = ZZ.map([[2], [0], [-2]]) >>> g = ZZ.map([[1], [-2], [1]]) >>> dmp_cancel(f, g, 1, ZZ) ([[2], [2]], [[1], [-1]]) """ if dmp_zero_p(f, u) or dmp_zero_p(g, u): if include: return f, g else: return K.one, K.one, f, g K0 = None if K.has_Field and K.has_assoc_Ring: K0, K = K, K.get_ring() cq, f = dmp_clear_denoms(f, u, K0, K, convert=True) cp, g = dmp_clear_denoms(g, u, K0, K, convert=True) else: cp, cq = K.one, K.one _, p, q = dmp_inner_gcd(f, g, u, K) if K0 is not None: p = dmp_convert(p, u, K, K0) q = dmp_convert(q, u, K, K0) K = K0 p_neg = K.is_negative(dmp_ground_LC(p, u, K)) q_neg = K.is_negative(dmp_ground_LC(q, u, K)) if p_neg and q_neg: p, q = dmp_neg(p, u, K), dmp_neg(q, u, K) elif p_neg: cp, p = -cp, dmp_neg(p, u, K) elif q_neg: cp, q = -cp, dmp_neg(q, u, K) if not include: return cp, cq, p, q p = dmp_mul_ground(p, cp, u, K) q = dmp_mul_ground(q, cq, u, K) return p, q wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/groebnertools.py0000644000175000017500000004454512014170666025517 0ustar georgeskgeorgesk"""Sparse distributed multivariate polynomials and Groebner bases. """ from sympy.core.compatibility import cmp from sympy.polys.monomialtools import ( monomial_mul, monomial_div, monomial_lcm, monomial_lex_key as O_lex, monomial_grlex_key as O_grlex, monomial_grevlex_key as O_grevlex, ) from sympy.polys.polyerrors import ( ExactQuotientFailed, DomainError, ) from operator import itemgetter def sdp_LC(f, K): """Returns the leading coeffcient of `f`. """ if not f: return K.zero else: return f[0][1] def sdp_LM(f, u): """Returns the leading monomial of `f`. """ if not f: return (0,)*(u+1) else: return f[0][0] def sdp_LT(f, u, K): """Returns the leading term of `f`. """ if f: return f[0] else: return (0,)*(u+1), K.zero def sdp_del_LT(f): """Removes the leading from `f`. """ return f[1:] def sdp_coeffs(f): """Returns a list of monomials in `f`. """ return [ coeff for _, coeff in f ] def sdp_monoms(f): """Returns a list of monomials in `f`. """ return [ monom for monom, _ in f ] def sdp_sort(f, O): """Sort terms in `f` using the given monomial order `O`. """ return sorted(f, key=lambda term: O(term[0]), reverse=True) def sdp_strip(f): """Remove terms with zero coefficients from `f` in `K[X]`. """ return [ (monom, coeff) for monom, coeff in f if coeff ] def sdp_normal(f, K): """Normalize distributed polynomial in the given domain. """ return [ (monom, K.convert(coeff)) for monom, coeff in f if coeff ] def sdp_from_dict(f, O): """Make a distributed polynomial from a dictionary. """ return sdp_sort(f.items(), O) def sdp_to_dict(f): """Make a dictionary from a distributed polynomial. """ return dict(f) def sdp_indep_p(f, j, u): """Returns `True` if a polynomial is independent of `x_j`. """ if j < 0 or j > u: raise IndexError("-%s <= j < %s expected, got %s" % (u, u, j)) else: return all(not monom[j] for monom in sdp_monoms(h)) def sdp_one_p(f, u, K): """Returns True if `f` is a multivariate one in `K[X]`. """ return f == sdp_one(u, K) def sdp_one(u, K): """Returns a multivariate one in `K[X]`. """ return (((0,)*(u+1), K.one),) def sdp_term_p(f): """Returns True if `f` has a single term or is zero. """ return len(f) <= 1 def sdp_abs(f, u, O, K): """Make all coefficients positive in `K[X]`. """ return [ (monom, K.abs(coeff)) for monom, coeff in f ] def sdp_neg(f, u, O, K): """Negate a polynomial in `K[X]`. """ return [ (monom, -coeff) for monom, coeff in f ] def sdp_add_term(f, term, u, O, K): """Add a single term using bisection method. """ M, c = term if not c: return f if not f: return [(M, c)] monoms = sdp_monoms(f) if cmp(O(M), O(monoms[ 0])) > 0: return [(M, c)] + f if cmp(O(M), O(monoms[-1])) < 0: return f + [(M, c)] lo, hi = 0, len(monoms)-1 while lo <= hi: i = (lo + hi) // 2 j = cmp(O(M), O(monoms[i])) if not j: coeff = f[i][1] + c if not coeff: return f[:i] + f[i+1:] else: return f[:i] + [(M, coeff)] + f[i+1:] else: if j > 0: hi = i - 1 else: lo = i + 1 else: return f[:i] + [(M, c)] + f[i+1:] def sdp_sub_term(f, term, u, O, K): """Sub a single term using bisection method. """ M, c = term if not c: return f if not f: return [(M, -c)] monoms = sdp_monoms(f) if cmp(O(M), O(monoms[ 0])) > 0: return [(M, -c)] + f if cmp(O(M), O(monoms[-1])) < 0: return f + [(M, -c)] lo, hi = 0, len(monoms)-1 while lo <= hi: i = (lo + hi) // 2 j = cmp(O(M), O(monoms[i])) if not j: coeff = f[i][1] - c if not coeff: return f[:i] + f[i+1:] else: return f[:i] + [(M, coeff)] + f[i+1:] else: if j > 0: hi = i - 1 else: lo = i + 1 else: return f[:i] + [(M, -c)] + f[i+1:] def sdp_mul_term(f, term, u, O, K): """Multiply a distributed polynomial by a term. """ M, c = term if not f or not c: return [] else: if K.is_one(c): return [ (monomial_mul(f_M, M), f_c) for f_M, f_c in f ] else: return [ (monomial_mul(f_M, M), f_c*c) for f_M, f_c in f ] def sdp_add(f, g, u, O, K): """Add distributed polynomials in `K[X]`. """ h = dict(f) for monom, c in g: if monom in h: coeff = h[monom] + c if not coeff: del h[monom] else: h[monom] = coeff else: h[monom] = c return sdp_from_dict(h, O) def sdp_sub(f, g, u, O, K): """Subtract distributed polynomials in `K[X]`. """ h = dict(f) for monom, c in g: if monom in h: coeff = h[monom] - c if not coeff: del h[monom] else: h[monom] = coeff else: h[monom] = -c return sdp_from_dict(h, O) def sdp_mul(f, g, u, O, K): """Multiply distributed polynomials in `K[X]`. """ if sdp_term_p(f): if not f: return f else: return sdp_mul_term(g, f[0], u, O, K) if sdp_term_p(g): if not g: return g else: return sdp_mul_term(f, g[0], u, O, K) h = {} for fm, fc in f: for gm, gc in g: monom = monomial_mul(fm, gm) coeff = fc*gc if monom in h: coeff += h[monom] if not coeff: del h[monom] continue h[monom] = coeff return sdp_from_dict(h, O) def sdp_sqr(f, u, O, K): """Square a distributed polynomial in `K[X]`. """ h = {} for fm, fc in f: for Fm, Fc in f: monom = monomial_mul(fm, Fm) coeff = fc*Fc if monom in h: coeff += h[monom] if not coeff: del h[monom] continue h[monom] = coeff return sdp_from_dict(h, O) def sdp_pow(f, n, u, O, K): """Raise `f` to the n-th power in `K[X]`. """ if not n: return sdp_one(u, K) if n < 0: raise ValueError("can't raise a polynomial to negative power") if n == 1 or not f or sdp_one_p(f, u, K): return f g = sdp_one(u, K) while True: n, m = n//2, n if m & 1: g = sdp_mul(g, f, u, O, K) if not n: break f = sdp_sqr(f, u, O, K) return g def sdp_monic(f, K): """Divides all coefficients by `LC(f)` in `K[X]`. """ if not f: return f lc_f = sdp_LC(f, K) if K.is_one(lc_f): return f else: return [ (m, K.quo(c, lc_f)) for m, c in f ] def sdp_content(f, K): """Returns GCD of coefficients in `K[X]`. """ if K.has_Field: return K.one else: cont = K.zero for _, c in f: cont = K.gcd(cont, c) if K.is_one(cont): break return cont def sdp_primitive(f, K): """Returns content and a primitive polynomial in `K[X]`. """ if K.has_Field: return K.one, f else: cont = sdp_content(f, K) if K.is_one(cont): return cont, f else: return cont, [ (m, K.quo(c, cont)) for m, c in f ] def _term_rr_div(a, b, K): """Division of two terms in over a ring. """ a_lm, a_lc = a b_lm, b_lc = b monom = monomial_div(a_lm, b_lm) if not (monom is None or a_lc % b_lc): return monom, K.quo(a_lc, b_lc) else: return None def _term_ff_div(a, b, K): """Division of two terms in over a field. """ a_lm, a_lc = a b_lm, b_lc = b monom = monomial_div(a_lm, b_lm) if monom is not None: return monom, K.quo(a_lc, b_lc) else: return None def sdp_div(f, G, u, O, K): """ Generalized polynomial division with remainder in `K[X]`. Given polynomial `f` and a set of polynomials `g = (g_1, ..., g_n)` compute a set of quotients `q = (q_1, ..., q_n)` and remainder `r` such that `f = q_1*f_1 + ... + q_n*f_n + r`, where `r = 0` or `r` is a completely reduced polynomial with respect to `g`. **References** 1. [Cox97]_ 2. [Ajwa95]_ """ Q, r = [ [] for _ in xrange(len(G)) ], [] if K.has_Field: term_div = _term_ff_div else: term_div = _term_rr_div while f: for i, g in enumerate(G): tq = term_div(sdp_LT(f, u, K), sdp_LT(g, u, K), K) if tq is not None: Q[i] = sdp_add_term(Q[i], tq, u, O, K) f = sdp_sub(f, sdp_mul_term(g, tq, u, O, K), u, O, K) break else: r = sdp_add_term(r, sdp_LT(f, u, K), u, O, K) f = sdp_del_LT(f) return Q, r def sdp_rem(f, g, u, O, K): """Returns polynomial remainder in `K[X]`. """ return sdp_div(f, g, u, O, K)[1] def sdp_quo(f, g, u, O, K): """Returns polynomial quotient in `K[x]`. """ return sdp_div(f, g, u, O, K)[0] def sdp_exquo(f, g, u, O, K): """Returns exact polynomial quotient in `K[X]`. """ q, r = sdp_div(f, g, u, O, K) if not r: return q else: raise ExactQuotientFailed(f, g) def sdp_lcm(f, g, u, O, K): """ Computes LCM of two polynomials in `K[X]`. The LCM is computed as the unique generater of the intersection of the two ideals generated by `f` and `g`. The approach is to compute a Groebner basis with respect to lexicographic ordering of `t*f` and `(1 - t)*g`, where `t` is an unrelated variable and then filtering out the solution that doesn't contain `t`. **References** 1. [Cox97]_ """ if not f or not g: return [] if sdp_term_p(f) and sdp_term_p(g): monom = monomial_lcm(sdp_LM(f, u), sdp_LM(g, u)) fc, gc = sdp_LC(f, K), sdp_LC(g, K) if K.has_Field: coeff = K.one else: coeff = K.lcm(fc, gc) return [(monom, coeff)] if not K.has_Field: lcm = K.one else: fc, f = sdp_primitive(f, K) gc, g = sdp_primitive(g, K) lcm = K.lcm(fc, gc) f_terms = tuple( ((1,) + m, c) for m, c in f ) g_terms = tuple( ((0,) + m, c) for m, c in g ) \ + tuple( ((1,) + m, -c) for m, c in g ) F = sdp_sort(f_terms, O_lex) G = sdp_sort(g_terms, O_lex) basis = sdp_groebner([F, G], u, O_lex, K) H = [ h for h in basis if sdp_indep_p(h, 0, u) ] if K.is_one(lcm): h = [ (m[1:], c) for m, c in H[0] ] else: h = [ (m[1:], c*lcm) for m, c in H[0] ] return sdp_sort(h, O) def sdp_gcd(f, g, u, O, K): """Compute GCD of two polynomials in `K[X]` via LCM. """ if not K.has_Field: fc, f = sdp_primitive(f, K) gc, g = sdp_primitive(g, K) gcd = K.gcd(fc, gc) h = sdp_quo(sdp_mul(f, g, u, O, K), sdp_lcm(f, g, u, O, K), u, O, K) if not K.has_Field: if K.is_one(gcd): return h else: return [ (m, c*gcd) for m, c in h ] else: return sdp_monic(h, K) def sdp_groebner(f, u, O, K, gens='', verbose=False): """ Computes Groebner basis for a set of polynomials in `K[X]`. Given a set of multivariate polynomials `F`, finds another set `G`, such that Ideal `F = Ideal G` and `G` is a reduced Groebner basis. The resulting basis is unique and has monic generators if the ground domains is a field. Otherwise the result is non-unique but Groebner bases over e.g. integers can be computed (if the input polynomials are monic). Groebner bases can be used to choose specific generators for a polynomial ideal. Because these bases are unique you can check for ideal equality by comparing the Groebner bases. To see if one polynomial lies in an ideal, divide by the elements in the base and see if the remainder vanishes. They can also be used to solve systems of polynomial equations as, by choosing lexicographic ordering, you can eliminate one variable at a time, provided that the ideal is zero-dimensional (finite number of solutions). **References** 1. [Bose03]_ 2. [Giovini91]_ 3. [Ajwa95]_ 4. [Cox97]_ Algorithm used: an improved version of Buchberger's algorithm as presented in T. Becker, V. Weispfenning, Groebner Bases: A Computational Approach to Commutative Algebra, Springer, 1993, page 232. Added optional ``gens`` argument to apply :func:`sdp_str` for the purpose of debugging the algorithm. """ if not K.has_Field: raise DomainError("can't compute a Groebner basis over %s" % K) def select(P): # normal selection strategy # select the pair with minimum LCM(LM(f), LM(g)) pr = min(P, key=lambda pair: O(monomial_lcm(sdp_LM(f[pair[0]], u), sdp_LM(f[pair[1]], u)))) return pr def normal(g, J): h = sdp_rem(g, [ f[j] for j in J ], u, O, K) if not h: return None else: h = sdp_monic(h, K) h = tuple(h) if not h in I: I[h] = len(f) f.append(h) return sdp_LM(h, u), I[h] def update(G, B, ih): # update G using the set of critical pairs B and h # [BW] page 230 h = f[ih] mh = sdp_LM(h, u) # filter new pairs (h, g), g in G C = G.copy() D = set() while C: # select a pair (h, g) by popping an element from C ig = C.pop() g = f[ig] mg = sdp_LM(g, u) LCMhg = monomial_lcm(mh, mg) def lcm_divides(ip): # LCM(LM(h), LM(p)) divides LCM(LM(h), LM(g)) m = monomial_lcm(mh, sdp_LM(f[ip], u)) return monomial_div(LCMhg, m) # HT(h) and HT(g) disjoint: mh*mg == LCMhg if monomial_mul(mh, mg) == LCMhg or ( not any(lcm_divides(ipx) for ipx in C) and not any(lcm_divides(pr[1]) for pr in D)): D.add((ih, ig)) E = set() while D: # select h, g from D (h the same as above) ih, ig = D.pop() mg = sdp_LM(f[ig], u) LCMhg = monomial_lcm(mh, mg) if not monomial_mul(mh, mg) == LCMhg: E.add((ih, ig)) # filter old pairs B_new = set() while B: # select g1, g2 from B (-> CP) ig1, ig2 = B.pop() mg1 = sdp_LM(f[ig1], u) mg2 = sdp_LM(f[ig2], u) LCM12 = monomial_lcm(mg1, mg2) # if HT(h) does not divide lcm(HT(g1), HT(g2)) if not monomial_div(LCM12, mh) or \ monomial_lcm(mg1, mh) == LCM12 or \ monomial_lcm(mg2, mh) == LCM12: B_new.add((ig1, ig2)) B_new |= E # filter polynomials G_new = set() while G: ig = G.pop() mg = sdp_LM(f[ig], u) if not monomial_div(mg, mh): G_new.add(ig) G_new.add(ih) return G_new, B_new # end of update ################################ if not f: return [] # replace f with a reduced list of initial polynomials; see [BW] page 203 f1 = f[:] while True: f = f1[:] f1 = [] for i in range(len(f)): p = f[i] r = sdp_rem(p, f[:i], u, O, K) if r: f1.append(sdp_monic(r, K)) if f == f1: break f = [tuple(p) for p in f] I = {} # ip = I[p]; p = f[ip] F = set() # set of indices of polynomials G = set() # set of indices of intermediate would-be Groebner basis CP = set() # set of pairs of indices of critical pairs for i, h in enumerate(f): I[h] = i F.add(i) ##################################### # algorithm GROEBNERNEWS2 in [BW] page 232 while F: # select p with minimum monomial according to the monomial ordering O h = min([f[x] for x in F], key=lambda f: O(sdp_LM(f, u))) ih = I[h] F.remove(ih) G, CP = update(G, CP, ih) # count the number of critical pairs which reduce to zero reductions_to_zero = 0 while CP: ig1, ig2 = select(CP) CP.remove((ig1, ig2)) h = sdp_spoly(f[ig1], f[ig2], u, O, K) # ordering divisors is on average more efficient [Cox] page 111 G1 = sorted(G, key=lambda g: O(sdp_LM(f[g], u))) ht = normal(h, G1) if ht: G, CP = update(G, CP, ht[1]) else: reductions_to_zero += 1 ###################################### # now G is a Groebner basis; reduce it Gr = set() for ig in G: ht = normal(f[ig], G - set([ig])) if ht: Gr.add(ht[1]) Gr = [list(f[ig]) for ig in Gr] # order according to the monomial ordering Gr = sorted(Gr, key=lambda f: O(sdp_LM(f, u)), reverse=True) if verbose: print 'reductions_to_zero = %d' % reductions_to_zero return Gr def sdp_str(f, gens): if isinstance(gens, basestring): gens = gens.split(',') ngens = len(gens) z = (0,)*ngens s = '' for expv, c in f: if c > 0: s += ' +' else: s += ' -' if c < 0: c = -c if c != 1: # and expv != z: cnt1 = str(c) else: cnt1 = '' sa = [] for i in range(ngens): exp = expv[i] if exp > 1: sa.append('%s^%d' % (gens[i], exp)) if exp == 1: sa.append('%s' % gens[i]) if cnt1: sa = [cnt1] + sa s += '*'.join(sa) return s def sdp_spoly(p1, p2, u, O, K): """ Compute LCM(LM(p1), LM(p2))/LM(p1)*p1 - LCM(LM(p1), LM(p2))/LM(p2)*p2 This is the S-poly provided p1 and p2 are monic """ LM1 = sdp_LM(p1, u) LM2 = sdp_LM(p2, u) LCM12 = monomial_lcm(LM1, LM2) m1 = monomial_div(LCM12, LM1) m2 = monomial_div(LCM12, LM2) s1 = sdp_mul_term(p1, (m1, K.one), u, O, K) s2 = sdp_mul_term(p2, (m2, K.one), u, O, K) s = sdp_sub(s1, s2, u, O, K) return s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/constructor.py0000644000175000017500000001416212014170666025210 0ustar georgeskgeorgesk"""Tools for constructing domains for expressions. """ from sympy.polys.polyutils import parallel_dict_from_basic from sympy.polys.polyoptions import build_options from sympy.polys.domains import ZZ, QQ, RR, EX from sympy.assumptions import ask, Q from sympy.core import S, sympify def _construct_simple(coeffs, opt): """Handle simple domains, e.g.: ZZ, QQ, RR and algebraic domains. """ result, rationals, reals, algebraics = {}, False, False, False if opt.extension is True: is_algebraic = lambda coeff: ask(Q.algebraic(coeff)) else: is_algebraic = lambda coeff: False # XXX: add support for a + b*I coefficients for coeff in coeffs: if coeff.is_Rational: if not coeff.is_Integer: rationals = True elif coeff.is_Float: if not algebraics: reals = True else: # there are both reals and algebraics -> EX return False elif is_algebraic(coeff): if not reals: algebraics = True else: # there are both algebraics and reals -> EX return False else: # this is a composite domain, e.g. ZZ[X], EX return None if algebraics: domain, result = _construct_algebraic(coeffs, opt) else: if reals: domain = RR else: if opt.field or rationals: domain = QQ else: domain = ZZ result = [] for coeff in coeffs: result.append(domain.from_sympy(coeff)) return domain, result def _construct_algebraic(coeffs, opt): """We know that coefficients are algebraic so construct the extension. """ from sympy.polys.numberfields import primitive_element result, exts = [], set([]) for coeff in coeffs: if coeff.is_Rational: coeff = (None, 0, QQ.from_sympy(coeff)) else: a = coeff.as_coeff_add()[0] coeff -= a b = coeff.as_coeff_mul()[0] coeff /= b exts.add(coeff) a = QQ.from_sympy(a) b = QQ.from_sympy(b) coeff = (coeff, b, a) result.append(coeff) exts = list(exts) g, span, H = primitive_element(exts, ex=True, polys=True) root = sum([ s*ext for s, ext in zip(span, exts) ]) domain, g = QQ.algebraic_field((g, root)), g.rep.rep for i, (coeff, a, b) in enumerate(result): if coeff is not None: coeff = a*domain.dtype.from_list(H[exts.index(coeff)], g, QQ) + b else: coeff = domain.dtype.from_list([b], g, QQ) result[i] = coeff return domain, result def _construct_composite(coeffs, opt): """Handle composite domains, e.g.: ZZ[X], QQ[X], ZZ(X), QQ(X). """ numers, denoms = [], [] for coeff in coeffs: numer, denom = coeff.as_numer_denom() numers.append(numer) denoms.append(denom) polys, gens = parallel_dict_from_basic(numers + denoms) # XXX: sorting if any(gen.is_number for gen in gens): return None # generators are number-like so lets better use EX n = len(gens) k = len(polys)//2 numers = polys[:k] denoms = polys[k:] if opt.field: fractions = True else: fractions, zeros = False, (0,)*n for denom in denoms: if len(denom) > 1 or zeros not in denom: fractions = True break coeffs = set([]) if not fractions: for numer, denom in zip(numers, denoms): denom = denom[zeros] for monom, coeff in numer.iteritems(): coeff /= denom coeffs.add(coeff) numer[monom] = coeff else: for numer, denom in zip(numers, denoms): coeffs.update(numer.values()) coeffs.update(denom.values()) rationals, reals = False, False for coeff in coeffs: if coeff.is_Rational: if not coeff.is_Integer: rationals = True elif coeff.is_Float: reals = True break if reals: ground = RR elif rationals: ground = QQ else: ground = ZZ result = [] if not fractions: domain = ground.poly_ring(*gens) for numer in numers: for monom, coeff in numer.iteritems(): numer[monom] = ground.from_sympy(coeff) result.append(domain(numer)) else: domain = ground.frac_field(*gens) for numer, denom in zip(numers, denoms): for monom, coeff in numer.iteritems(): numer[monom] = ground.from_sympy(coeff) for monom, coeff in denom.iteritems(): denom[monom] = ground.from_sympy(coeff) result.append(domain((numer, denom))) return domain, result def _construct_expression(coeffs, opt): """The last resort case, i.e. use the expression domain. """ domain, result = EX, [] for coeff in coeffs: result.append(domain.from_sympy(coeff)) return domain, result def construct_domain(obj, **args): """Construct a minimal domain for the list of coefficients. """ opt = build_options(args) if hasattr(obj, '__iter__'): if isinstance(obj, dict): monoms, coeffs = zip(*obj.items()) else: coeffs = obj else: coeffs = [obj] coeffs = map(sympify, coeffs) result = _construct_simple(coeffs, opt) if result is not None: if result is not False: domain, coeffs = result else: domain, coeffs = _construct_expression(coeffs, opt) else: if opt.composite: result = _construct_composite(coeffs, opt) else: result = None if result is not None: domain, coeffs = result else: domain, coeffs = _construct_expression(coeffs, opt) if hasattr(obj, '__iter__'): if isinstance(obj, dict): return domain, dict(zip(monoms, coeffs)) else: return domain, coeffs else: return domain, coeffs[0] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/partfrac.py0000644000175000017500000000777512014170666024441 0ustar georgeskgeorgesk"""Algorithms for partial fraction decomposition of rational functions. """ from sympy.polys import Poly, RootSum, cancel, factor from sympy.polys.polytools import parallel_poly_from_expr from sympy.core import S, Add, sympify, Symbol, Function, Lambda, Dummy from sympy.utilities import numbered_symbols, take, threaded @threaded def apart(f, x=None, full=False): """ Compute partial fraction decomposition of a rational function. Given a rational function ``f`` compute partial fraction decomposition of ``f``. Two algorithms are available: one is based on undetermined coefficients method and the other is Bronstein's full partial fraction decomposition algorithm. **Examples** >>> from sympy.polys.partfrac import apart >>> from sympy.abc import x, y >>> apart(y/(x + 2)/(x + 1), x) -y/(x + 2) + y/(x + 1) """ f = sympify(f) if f.is_Atom: return f else: P, Q = f.as_numer_denom() (P, Q), opt = parallel_poly_from_expr((P, Q), x) if P.is_multivariate: raise NotImplementedError("multivariate partial fraction decomposition") common, P, Q = P.cancel(Q) poly, P = P.div(Q, auto=True) P, Q = P.rat_clear_denoms(Q) if Q.degree() <= 1: partial = P/Q else: if not full: partial = apart_undetermined_coeffs(P, Q) else: partial = apart_full_decomposition(P, Q) terms = S.Zero for term in Add.make_args(partial): terms += factor(term) return common*(poly.as_expr() + terms) def apart_undetermined_coeffs(P, Q): """Partial fractions via method of undetermined coefficients. """ X = numbered_symbols(cls=Dummy) partial, symbols = [], [] _, factors = Q.factor_list() for f, k in factors: n, q = f.degree(), Q for i in xrange(1, k+1): coeffs, q = take(X, n), q.quo(f) partial.append((coeffs, q, f, i)) symbols.extend(coeffs) dom = Q.get_domain().inject(*symbols) F = Poly(0, Q.gen, domain=dom) for i, (coeffs, q, f, k) in enumerate(partial): h = Poly(coeffs, Q.gen, domain=dom) partial[i] = (h, f, k) q = q.set_domain(dom) F += h*q system, result = [], S(0) for (k,), coeff in F.terms(): system.append(coeff - P.nth(k)) from sympy.solvers import solve solution = solve(system, symbols) for h, f, k in partial: h = h.as_expr().subs(solution) result += h/f.as_expr()**k return result def apart_full_decomposition(P, Q): """ Bronstein's full partial fraction decomposition algorithm. Given a univariate rational function ``f``, performing only GCD operations over the algebraic closure of the initial ground domain of definition, compute full partial fraction decomposition with fractions having linear denominators. Note that no factorization of the initial denominator of ``f`` is performed. The final decomposition is formed in terms of a sum of :class:`RootSum` instances. **References** 1. [Bronstein93]_ """ f, x, U = P/Q, P.gen, [] u = Function('u')(x) a = Dummy('a') partial = S(0) for d, n in Q.sqf_list_include(all=True): b = d.as_expr() U += [ u.diff(x, n-1) ] h = cancel(f*b**n) / u**n H, subs = [h], [] for j in range(1, n): H += [ H[-1].diff(x) / j ] for j in range(1, n+1): subs += [ (U[j-1], b.diff(x, j) / j) ] for j in range(0, n): P, Q = cancel(H[j]).as_numer_denom() for i in range(0, j+1): P = P.subs(*subs[j-i]) Q = Q.subs(*subs[0]) P = Poly(P, x) Q = Poly(Q, x) G = P.gcd(d) D = d.quo(G) B, g = Q.half_gcdex(D) b = (P * B.quo(g)).rem(D) numer = b.as_expr() denom = (x-a)**(n-j) expr = numer.subs(x, a) / denom partial += RootSum(D, Lambda(a, expr)) return partial wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/0000755000175000017500000000000012014170666023407 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_monomialtools.py0000644000175000017500000001141312014170666027714 0ustar georgeskgeorgesk"""Tests for tools and arithmetics for monomials of distributed polynomials. """ from sympy.polys.monomialtools import ( monomials, monomial_count, monomial_lex_key, monomial_grlex_key, monomial_grevlex_key, monomial_key, monomial_lex_cmp, monomial_grlex_cmp, monomial_grevlex_cmp, monomial_cmp, monomial_mul, monomial_div, monomial_gcd, monomial_lcm, monomial_max, monomial_min, Monomial, ) from sympy.polys.polyerrors import ExactQuotientFailed from sympy.abc import x, y, z from sympy.utilities.pytest import raises def test_monomials(): assert sorted(monomials([], 0)) == [1] assert sorted(monomials([], 1)) == [1] assert sorted(monomials([], 2)) == [1] assert sorted(monomials([], 3)) == [1] assert sorted(monomials([x], 0)) == [1] assert sorted(monomials([x], 1)) == [1, x] assert sorted(monomials([x], 2)) == [1, x, x**2] assert sorted(monomials([x], 3)) == [1, x, x**2, x**3] assert sorted(monomials([x, y], 0)) == [1] assert sorted(monomials([x, y], 1)) == [1, x, y] assert sorted(monomials([x, y], 2)) == [1, x, y, x**2, y**2, x*y] assert sorted(monomials([x, y], 3)) == [1, x, y, x**2, x**3, y**2, y**3, x*y, x*y**2, y*x**2] def test_monomial_count(): assert monomial_count(2, 2) == 6 assert monomial_count(2, 3) == 10 def test_monomial_lex_key(): assert monomial_lex_key((1,2,3)) == (1,2,3) def test_monomial_grlex_key(): assert monomial_grlex_key((1,2,3)) == (6, (1,2,3)) def test_monomial_grevlex_key(): assert monomial_grevlex_key((1,2,3)) == (6, (3,2,1)) def test_monomial_key(): assert monomial_key() == monomial_lex_key assert monomial_key('lex') == monomial_lex_key assert monomial_key('grlex') == monomial_grlex_key assert monomial_key('grevlex') == monomial_grevlex_key raises(ValueError, "monomial_key('foo')") raises(ValueError, "monomial_key(1)") def test_monomial_lex_cmp(): assert monomial_lex_cmp((1,2,3), (1,2,3)) == 0 assert monomial_lex_cmp((2,2,3), (1,2,3)) == 1 assert monomial_lex_cmp((1,3,3), (1,2,3)) == 1 assert monomial_lex_cmp((1,2,4), (1,2,3)) == 1 assert monomial_lex_cmp((0,2,3), (1,2,3)) == -1 assert monomial_lex_cmp((1,1,3), (1,2,3)) == -1 assert monomial_lex_cmp((1,2,2), (1,2,3)) == -1 def test_monomial_grlex_cmp(): assert monomial_grlex_cmp((1,2,3), (1,2,3)) == 0 assert monomial_grlex_cmp((2,2,3), (1,2,3)) == 1 assert monomial_grlex_cmp((1,3,3), (1,2,3)) == 1 assert monomial_grlex_cmp((1,2,4), (1,2,3)) == 1 assert monomial_grlex_cmp((0,2,3), (1,2,3)) == -1 assert monomial_grlex_cmp((1,1,3), (1,2,3)) == -1 assert monomial_grlex_cmp((1,2,2), (1,2,3)) == -1 assert monomial_grlex_cmp((2,2,3), (1,2,4)) == 1 assert monomial_grlex_cmp((1,3,3), (1,2,4)) == 1 assert monomial_grlex_cmp((0,2,3), (1,2,2)) == -1 assert monomial_grlex_cmp((1,1,3), (1,2,2)) == -1 def test_monomial_grevlex_cmp(): assert monomial_grevlex_cmp((1,2,3), (1,2,3)) == 0 assert monomial_grevlex_cmp((2,2,3), (1,2,3)) == 1 assert monomial_grevlex_cmp((1,3,3), (1,2,3)) == 1 assert monomial_grevlex_cmp((1,2,4), (1,2,3)) == 1 assert monomial_grevlex_cmp((0,2,3), (1,2,3)) == -1 assert monomial_grevlex_cmp((1,1,3), (1,2,3)) == -1 assert monomial_grevlex_cmp((1,2,2), (1,2,3)) == -1 assert monomial_grevlex_cmp((2,2,3), (1,2,4)) == 1 assert monomial_grevlex_cmp((1,3,3), (1,2,4)) == 1 assert monomial_grevlex_cmp((0,2,3), (1,2,2)) == -1 assert monomial_grevlex_cmp((1,1,3), (1,2,2)) == -1 def test_monomial_cmp(): assert monomial_cmp('lex') == monomial_lex_cmp assert monomial_cmp('grlex') == monomial_grlex_cmp assert monomial_cmp('grevlex') == monomial_grevlex_cmp raises(ValueError, "monomial_cmp('unknown')") def test_monomial_mul(): assert monomial_mul((3,4,1), (1,2,0)) == (4,6,1) def test_monomial_div(): assert monomial_div((3,4,1), (1,2,0)) == (2,2,1) def test_monomial_gcd(): assert monomial_gcd((3,4,1), (1,2,0)) == (1,2,0) def test_monomial_lcm(): assert monomial_lcm((3,4,1), (1,2,0)) == (3,4,1) def test_monomial_max(): assert monomial_max((3,4,5), (0,5,1), (6,3,9)) == (6,5,9) def test_monomial_min(): assert monomial_min((3,4,5), (0,5,1), (6,3,9)) == (0,3,1) def test_Monomial(): assert Monomial(1, 2, 3).data == (1, 2, 3) m, n = Monomial(3, 4, 1), Monomial(1, 2, 0) assert m*n == Monomial(4, 6, 1) assert m/n == Monomial(2, 2, 1) assert m.gcd(n) == Monomial(1, 2, 0) assert m.lcm(n) == Monomial(3, 4, 1) a, b, c = [ Monomial(*monom) for monom in [(3,4,5), (0,5,1), (6,3,9)] ] assert Monomial.max(a, b, c) == Monomial(6, 5, 9) assert Monomial.min(a, b, c) == Monomial(0, 3, 1) n = Monomial(5, 2, 0) raises(ExactQuotientFailed, "m / n") assert n.as_expr(x, y, z) == x**5*y**2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_polyclasses.py0000644000175000017500000002711412014170666027366 0ustar georgeskgeorgesk"""Tests for OO layer of several polynomial representations. """ from sympy.polys.polyclasses import ( DMP, init_normal_DMP, DMF, init_normal_DMF, ANP, init_normal_ANP, ) from sympy.polys.domains import ZZ, QQ from sympy.polys.specialpolys import f_4 from sympy.polys.polyerrors import ( ExactQuotientFailed, ) from sympy.utilities.pytest import raises def test_DMP___init__(): f = DMP([[0],[],[0,1,2],[3]], ZZ) assert f.rep == [[1,2],[3]] assert f.dom == ZZ assert f.lev == 1 f = DMP([[1,2],[3]], ZZ, 1) assert f.rep == [[1,2],[3]] assert f.dom == ZZ assert f.lev == 1 f = DMP({(1,1): 1, (0,0): 2}, ZZ, 1) assert f.rep == [[1,0],[2]] assert f.dom == ZZ assert f.lev == 1 def test_DMP___eq__(): assert DMP([[ZZ(1),ZZ(2)],[ZZ(3)]], ZZ) == \ DMP([[ZZ(1),ZZ(2)],[ZZ(3)]], ZZ) assert DMP([[ZZ(1),ZZ(2)],[ZZ(3)]], ZZ) == \ DMP([[QQ(1),QQ(2)],[QQ(3)]], QQ) assert DMP([[QQ(1),QQ(2)],[QQ(3)]], QQ) == \ DMP([[ZZ(1),ZZ(2)],[ZZ(3)]], ZZ) assert DMP([[[ZZ(1)]]], ZZ) != DMP([[ZZ(1)]], ZZ) assert DMP([[ZZ(1)]], ZZ) != DMP([[[ZZ(1)]]], ZZ) def test_DMP___bool__(): assert bool(DMP([[]], ZZ)) == False assert bool(DMP([[1]], ZZ)) == True def test_DMP_to_dict(): f = DMP([[3],[],[2],[],[8]], ZZ) assert f.to_dict() == \ {(4, 0): 3, (2, 0): 2, (0, 0): 8} assert f.to_sympy_dict() == \ {(4, 0): ZZ.to_sympy(3), (2, 0): ZZ.to_sympy(2), (0, 0): ZZ.to_sympy(8)} def test_DMP_properties(): assert DMP([[]], ZZ).is_zero == True assert DMP([[1]], ZZ).is_zero == False assert DMP([[1]], ZZ).is_one == True assert DMP([[2]], ZZ).is_one == False assert DMP([[1]], ZZ).is_ground == True assert DMP([[1],[2],[1]], ZZ).is_ground == False assert DMP([[1],[2,0],[1,0]], ZZ).is_sqf == True assert DMP([[1],[2,0],[1,0,0]], ZZ).is_sqf == False assert DMP([[1,2],[3]], ZZ).is_monic == True assert DMP([[2,2],[3]], ZZ).is_monic == False assert DMP([[1,2],[3]], ZZ).is_primitive == True assert DMP([[2,4],[6]], ZZ).is_primitive == False def test_DMP_arithmetics(): f = DMP([[2],[2,0]], ZZ) assert f.mul_ground(2) == DMP([[4],[4,0]], ZZ) assert f.quo_ground(2) == DMP([[1],[1,0]], ZZ) raises(ExactQuotientFailed, 'f.exquo_ground(3)') f = DMP([[-5]], ZZ) g = DMP([[5]], ZZ) assert f.abs() == g assert abs(f) == g assert g.neg() == f assert -g == f h = DMP([[]], ZZ) assert f.add(g) == h assert f + g == h assert g + f == h assert f + 5 == h assert 5 + f == h h = DMP([[-10]], ZZ) assert f.sub(g) == h assert f - g == h assert g - f == -h assert f - 5 == h assert 5 - f == -h h = DMP([[-25]], ZZ) assert f.mul(g) == h assert f * g == h assert g * f == h assert f * 5 == h assert 5 * f == h h = DMP([[25]], ZZ) assert f.sqr() == h assert f.pow(2) == h assert f**2 == h raises(TypeError, "f.pow('x')") f = DMP([[1],[],[1,0,0]], ZZ) g = DMP([[2],[-2,0]], ZZ) q = DMP([[2],[2,0]], ZZ) r = DMP([[8,0,0]], ZZ) assert f.pdiv(g) == (q, r) assert f.pquo(g) == q assert f.prem(g) == r raises(ExactQuotientFailed, 'f.pexquo(g)') f = DMP([[1],[],[1,0,0]], ZZ) g = DMP([[1],[-1,0]], ZZ) q = DMP([[1],[1,0]], ZZ) r = DMP([[2,0,0]], ZZ) assert f.div(g) == (q, r) assert f.quo(g) == q assert f.rem(g) == r assert divmod(f, g) == (q, r) assert f // g == q assert f % g == r raises(ExactQuotientFailed, 'f.exquo(g)') def test_DMP_functionality(): f = DMP([[1],[2,0],[1,0,0]], ZZ) g = DMP([[1],[1,0]], ZZ) h = DMP([[1]], ZZ) assert f.degree() == 2 assert f.degree_list() == (2, 2) assert f.total_degree() == 4 assert f.LC() == ZZ(1) assert f.TC() == ZZ(0) assert f.nth(1, 1) == ZZ(2) raises(TypeError, "f.nth(0, 'x')") assert f.max_norm() == 2 assert f.l1_norm() == 4 u = DMP([[2],[2,0]], ZZ) assert f.diff(m=1, j=0) == u assert f.diff(m=1, j=1) == u raises(TypeError, "f.diff(m='x', j=0)") u = DMP([1,2,1], ZZ) v = DMP([1,2,1], ZZ) assert f.eval(a=1, j=0) == u assert f.eval(a=1, j=1) == v assert f.eval(1).eval(1) == ZZ(4) assert f.cofactors(g) == (g, g, h) assert f.gcd(g) == g assert f.lcm(g) == f u = DMP([[QQ(45),QQ(30),QQ(5)]], QQ) v = DMP([[QQ(1),QQ(2,3),QQ(1,9)]], QQ) assert u.monic() == v assert (4*f).content() == ZZ(4) assert (4*f).primitive() == (ZZ(4), f) f = DMP([[1],[2],[3],[4],[5],[6]], ZZ) assert f.trunc(3) == DMP([[1],[-1],[],[1],[-1],[]], ZZ) f = DMP(f_4, ZZ) assert f.sqf_part() == -f assert f.sqf_list() == (ZZ(-1), [(-f, 1)]) f = DMP([[-1],[],[],[5]], ZZ) g = DMP([[3,1],[],[]], ZZ) h = DMP([[45,30,5]], ZZ) r = DMP([675,675,225,25], ZZ) assert f.subresultants(g) == [f, g, h] assert f.resultant(g) == r f = DMP([1,3,9,-13], ZZ) assert f.discriminant() == -11664 f = DMP([QQ(2),QQ(0)], QQ) g = DMP([QQ(1),QQ(0),QQ(-16)], QQ) s = DMP([QQ(1,32),QQ(0)], QQ) t = DMP([QQ(-1,16)], QQ) h = DMP([QQ(1)], QQ) assert f.half_gcdex(g) == (s, h) assert f.gcdex(g) == (s, t, h) assert f.invert(g) == s f = DMP([[1],[2],[3]], QQ) raises(ValueError, "f.half_gcdex(f)") raises(ValueError, "f.gcdex(f)") raises(ValueError, "f.invert(f)") f = DMP([1,0,20,0,150,0,500,0,625,-2,0,-10,9], ZZ) g = DMP([1,0,0,-2,9], ZZ) h = DMP([1,0,5,0], ZZ) assert g.compose(h) == f assert f.decompose() == [g, h] f = DMP([[1],[2],[3]], QQ) raises(ValueError, "f.decompose()") raises(ValueError, "f.sturm()") def test_DMP_exclude(): f = [[[[[[[[[[[[[[[[[[[[[[[[[[1]], [[]]]]]]]]]]]]]]]]]]]]]]]]]] J = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25] assert DMP(f, ZZ).exclude() == (J, DMP([1, 0], ZZ)) assert DMP([[1], [1, 0]], ZZ).exclude() == ([], DMP([[1], [1, 0]], ZZ)) def test_DMF__init__(): f = DMF(([[0],[],[0,1,2],[3]], [[1,2,3]]), ZZ) assert f.num == [[1,2],[3]] assert f.den == [[1,2,3]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1,2],[3]], [[1,2,3]]), ZZ, 1) assert f.num == [[1,2],[3]] assert f.den == [[1,2,3]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[-1],[-2]],[[3],[-4]]), ZZ) assert f.num == [[-1],[-2]] assert f.den == [[3],[-4]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1],[2]],[[-3],[4]]), ZZ) assert f.num == [[-1],[-2]] assert f.den == [[3],[-4]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1],[2]],[[-3],[4]]), ZZ) assert f.num == [[-1],[-2]] assert f.den == [[3],[-4]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[]],[[-3],[4]]), ZZ) assert f.num == [[]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF(17, ZZ, 1) assert f.num == [[17]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[1],[2]]), ZZ) assert f.num == [[1],[2]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF([[0],[],[0,1,2],[3]], ZZ) assert f.num == [[1,2],[3]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF({(1,1): 1, (0,0): 2}, ZZ, 1) assert f.num == [[1,0],[2]] assert f.den == [[1]] assert f.lev == 1 assert f.dom == ZZ f = DMF(([[QQ(1)],[QQ(2)]], [[-QQ(3)],[QQ(4)]]), QQ) assert f.num == [[-QQ(1)],[-QQ(2)]] assert f.den == [[QQ(3)],[-QQ(4)]] assert f.lev == 1 assert f.dom == QQ f = DMF(([[QQ(1,5)],[QQ(2,5)]], [[-QQ(3,7)],[QQ(4,7)]]), QQ) assert f.num == [[-QQ(7)],[-QQ(14)]] assert f.den == [[QQ(15)],[-QQ(20)]] assert f.lev == 1 assert f.dom == QQ raises(ValueError, "DMF(([1], [[1]]), ZZ)") raises(ZeroDivisionError, "DMF(([1], []), ZZ)") def test_DMF__eq__(): pass def test_DMF__bool__(): assert bool(DMF([[]], ZZ)) == False assert bool(DMF([[1]], ZZ)) == True def test_DMF_properties(): assert DMF([[]], ZZ).is_zero == True assert DMF([[]], ZZ).is_one == False assert DMF([[1]], ZZ).is_zero == False assert DMF([[1]], ZZ).is_one == True assert DMF(([[1]], [[2]]), ZZ).is_one == False def test_DMF_arithmetics(): f = DMF([[7],[-9]], ZZ) g = DMF([[-7],[9]], ZZ) assert f.neg() == -f == g f = DMF(([[1]], [[1],[]]), ZZ) g = DMF(([[1]], [[1,0]]), ZZ) h = DMF(([[1],[1,0]], [[1,0],[]]), ZZ) assert f.add(g) == f + g == h assert g.add(f) == g + f == h h = DMF(([[-1],[1,0]], [[1,0],[]]), ZZ) assert f.sub(g) == f - g == h h = DMF(([[1]], [[1,0],[]]), ZZ) assert f.mul(g) == f*g == h assert g.mul(f) == g*f == h h = DMF(([[1,0]], [[1],[]]), ZZ) assert f.quo(g) == f/g == h h = DMF(([[1]], [[1],[],[],[]]), ZZ) assert f.pow(3) == f**3 == h h = DMF(([[1]], [[1,0,0,0]]), ZZ) assert g.pow(3) == g**3 == h def test_ANP___init__(): rep = [QQ(1),QQ(1)] mod = [QQ(1),QQ(0),QQ(1)] f = ANP(rep, mod, QQ) assert f.rep == [QQ(1),QQ(1)] assert f.mod == [QQ(1),QQ(0),QQ(1)] assert f.dom == QQ rep = {1: QQ(1), 0: QQ(1)} mod = {2: QQ(1), 0: QQ(1)} f = ANP(rep, mod, QQ) assert f.rep == [QQ(1),QQ(1)] assert f.mod == [QQ(1),QQ(0),QQ(1)] assert f.dom == QQ f = ANP(1, mod, QQ) assert f.rep == [QQ(1)] assert f.mod == [QQ(1),QQ(0),QQ(1)] assert f.dom == QQ def test_ANP___eq__(): a = ANP([QQ(1), QQ(1)], [QQ(1),QQ(0),QQ(1)], QQ) b = ANP([QQ(1), QQ(1)], [QQ(1),QQ(0),QQ(2)], QQ) assert (a == a) == True assert (a != a) == False assert (a == b) == False assert (a != b) == True b = ANP([QQ(1), QQ(2)], [QQ(1),QQ(0),QQ(1)], QQ) assert (a == b) == False assert (a != b) == True def test_ANP___bool__(): assert bool(ANP([], [QQ(1),QQ(0),QQ(1)], QQ)) == False assert bool(ANP([QQ(1)], [QQ(1),QQ(0),QQ(1)], QQ)) == True def test_ANP_properties(): mod = [QQ(1),QQ(0),QQ(1)] assert ANP([QQ(0)], mod, QQ).is_zero == True assert ANP([QQ(1)], mod, QQ).is_zero == False assert ANP([QQ(1)], mod, QQ).is_one == True assert ANP([QQ(2)], mod, QQ).is_one == False def test_ANP_arithmetics(): mod = [QQ(1),QQ(0),QQ(0),QQ(-2)] a = ANP([QQ(2),QQ(-1),QQ(1)], mod, QQ) b = ANP([QQ(1),QQ(2)], mod, QQ) c = ANP([QQ(-2), QQ(1), QQ(-1)], mod, QQ) assert a.neg() == -a == c c = ANP([QQ(2), QQ(0), QQ(3)], mod, QQ) assert a.add(b) == a+b == c assert b.add(a) == b+a == c c = ANP([QQ(2), QQ(-2), QQ(-1)], mod, QQ) assert a.sub(b) == a-b == c c = ANP([QQ(-2), QQ(2), QQ(1)], mod, QQ) assert b.sub(a) == b-a == c c = ANP([QQ(3), QQ(-1), QQ(6)], mod, QQ) assert a.mul(b) == a*b == c assert b.mul(a) == b*a == c c = ANP([QQ(-1,43), QQ(9,43), QQ(5,43)], mod, QQ) assert a.pow(0) == a**(0) == ANP(1, mod, QQ) assert a.pow(1) == a**(1) == a assert a.pow(-1) == a**(-1) == c assert a.quo(a) == a.mul(a.pow(-1)) == a*a**(-1) == ANP(1, mod, QQ) def test___hash__(): # Issue 2472 # Make sure int vs. long doesn't affect hashing with Python ground types assert DMP([[1, 2], [3]], ZZ) == DMP([[1l, 2l], [3l]], ZZ) assert hash(DMP([[1, 2], [3]], ZZ)) == hash(DMP([[1l, 2l], [3l]], ZZ)) assert DMF(([[1, 2], [3]], [[1]]), ZZ) == DMF(([[1L, 2L], [3L]], [[1L]]), ZZ) assert hash(DMF(([[1, 2], [3]], [[1]]), ZZ)) == hash(DMF(([[1L, 2L], [3L]], [[1L]]), ZZ)) assert ANP([1, 1], [1, 0, 1], ZZ) == ANP([1l, 1l], [1l, 0l, 1l], ZZ) assert hash(ANP([1, 1], [1, 0, 1], ZZ)) == hash(ANP([1l, 1l], [1l, 0l, 1l], ZZ)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_rootisolation.py0000644000175000017500000007452012014170666027735 0ustar georgeskgeorgesk"""Tests for real and complex root isolation and refinement algorithms. """ from sympy.polys.rootisolation import ( dup_sturm, dup_refine_real_root, dup_isolate_real_roots, dup_isolate_real_roots_sqf, dup_isolate_real_roots_list, dup_count_real_roots, dup_count_complex_roots, dup_isolate_complex_roots_sqf, dup_isolate_all_roots, dup_isolate_all_roots_sqf) from sympy.polys.densebasic import ( dup_from_raw_dict) from sympy.polys.sqfreetools import ( dup_sqf_part) from sympy.polys.polyerrors import ( RefinementFailed, DomainError) from sympy.polys.domains import ZZ, QQ, EX from sympy.utilities.pytest import raises def test_dup_sturm(): assert dup_sturm([QQ(5)], QQ) == [[QQ(1)]] assert dup_sturm([QQ(1),QQ(0)], QQ) == [[QQ(1),QQ(0)], [QQ(1)]] f = QQ.map([1,-2,3,-5]) assert dup_sturm(f, QQ) == \ [f, [QQ(3),QQ(-4),QQ(3)], [QQ(-10,9),QQ(13,3)], [QQ(-3303,100)]] def test_dup_refine_real_root(): f = [1,0,-2] assert dup_refine_real_root(f, QQ(1), QQ(1), ZZ, steps=1) == (QQ(1), QQ(1)) assert dup_refine_real_root(f, QQ(1), QQ(1), ZZ, steps=9) == (QQ(1), QQ(1)) raises(ValueError, "dup_refine_real_root(f, QQ(-2), QQ(2), ZZ)") s, t = QQ(1,1), QQ(2,1) assert dup_refine_real_root(f, s, t, ZZ, steps=0) == (QQ(1, 1), QQ(2, 1)) assert dup_refine_real_root(f, s, t, ZZ, steps=1) == (QQ(1, 1), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=2) == (QQ(4, 3), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=3) == (QQ(7, 5), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=4) == (QQ(7, 5), QQ(10, 7)) s, t = QQ(1,1), QQ(3,2) assert dup_refine_real_root(f, s, t, ZZ, steps=0) == (QQ(1, 1), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=1) == (QQ(4, 3), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=2) == (QQ(7, 5), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=3) == (QQ(7, 5), QQ(10, 7)) assert dup_refine_real_root(f, s, t, ZZ, steps=4) == (QQ(7, 5), QQ(17, 12)) s, t = QQ(1,1), QQ(5,3) assert dup_refine_real_root(f, s, t, ZZ, steps=0) == (QQ(1, 1), QQ(5, 3)) assert dup_refine_real_root(f, s, t, ZZ, steps=1) == (QQ(1, 1), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=2) == (QQ(7, 5), QQ(3, 2)) assert dup_refine_real_root(f, s, t, ZZ, steps=3) == (QQ(7, 5), QQ(13, 9)) assert dup_refine_real_root(f, s, t, ZZ, steps=4) == (QQ(7, 5), QQ(10, 7)) s, t = QQ(-1,1), QQ(-2,1) assert dup_refine_real_root(f, s, t, ZZ, steps=0) == (-QQ(2, 1), -QQ(1, 1)) assert dup_refine_real_root(f, s, t, ZZ, steps=1) == (-QQ(3, 2), -QQ(1, 1)) assert dup_refine_real_root(f, s, t, ZZ, steps=2) == (-QQ(3, 2), -QQ(4, 3)) assert dup_refine_real_root(f, s, t, ZZ, steps=3) == (-QQ(3, 2), -QQ(7, 5)) assert dup_refine_real_root(f, s, t, ZZ, steps=4) == (-QQ(10, 7), -QQ(7, 5)) raises(RefinementFailed, "dup_refine_real_root(f, QQ(0), QQ(1), ZZ)") s, t, u, v, w = QQ(1), QQ(2), QQ(24,17), QQ(17,12), QQ(7,5) assert dup_refine_real_root(f, s, t, ZZ, eps=QQ(1,100)) == (u, v) assert dup_refine_real_root(f, s, t, ZZ, steps=6) == (u, v) assert dup_refine_real_root(f, s, t, ZZ, eps=QQ(1,100), steps=5) == (w, v) assert dup_refine_real_root(f, s, t, ZZ, eps=QQ(1,100), steps=6) == (u, v) assert dup_refine_real_root(f, s, t, ZZ, eps=QQ(1,100), steps=7) == (u, v) s, t, u, v = QQ(-2), QQ(-1), QQ(-3,2), QQ(-4,3) assert dup_refine_real_root([1,0,-2], s, t, ZZ, disjoint=QQ(-5)) == (s, t) assert dup_refine_real_root([1,0,-2], s, t, ZZ, disjoint=-v) == (s, t) assert dup_refine_real_root([1,0,-2], s, t, ZZ, disjoint=v) == (u, v) s, t, u, v = QQ(1), QQ(2), QQ(4,3), QQ(3,2) assert dup_refine_real_root([1,0,-2], s, t, ZZ, disjoint=QQ(5)) == (s, t) assert dup_refine_real_root([1,0,-2], s, t, ZZ, disjoint=-u) == (s, t) assert dup_refine_real_root([1,0,-2], s, t, ZZ, disjoint=u) == (u, v) def test_dup_isolate_real_roots_sqf(): assert dup_isolate_real_roots_sqf([], ZZ) == [] assert dup_isolate_real_roots_sqf([5], ZZ) == [] assert dup_isolate_real_roots_sqf([1, 1,0], ZZ) == [(-QQ(1), -QQ(1)), (QQ(0), QQ(0))] assert dup_isolate_real_roots_sqf([1,-1,0], ZZ) == [( QQ(0), QQ(0)), (QQ(1), QQ(1))] assert dup_isolate_real_roots_sqf([1,0,0,1,1], ZZ) == [] I = [ (-QQ(2), -QQ(1)), (QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,-2], ZZ) == I assert dup_isolate_real_roots_sqf([-1,0,2], ZZ) == I assert dup_isolate_real_roots_sqf([1,-1], ZZ) == \ [(QQ(1), QQ(1))] assert dup_isolate_real_roots_sqf([1,-3,2], ZZ) == \ [(QQ(1), QQ(1)), (QQ(2), QQ(2))] assert dup_isolate_real_roots_sqf([1,-6,11,-6], ZZ) == \ [(QQ(1), QQ(1)), (QQ(2), QQ(2)), (QQ(3), QQ(3))] assert dup_isolate_real_roots_sqf([1,-10,35,-50,24], ZZ) == \ [(QQ(1), QQ(1)), (QQ(2), QQ(2)), (QQ(3), QQ(3)), (QQ(4), QQ(4))] assert dup_isolate_real_roots_sqf([1,-15,85,-225,274,-120], ZZ) == \ [(QQ(1), QQ(1)), (QQ(2), QQ(2)), (QQ(3), QQ(3)), (QQ(4), QQ(4)), (QQ(5), QQ(5))] assert dup_isolate_real_roots_sqf([1,-10], ZZ) == \ [(QQ(10), QQ(10))] assert dup_isolate_real_roots_sqf([1,-30,200], ZZ) == \ [(QQ(10), QQ(10)), (QQ(20), QQ(20))] assert dup_isolate_real_roots_sqf([1,-60,1100,-6000], ZZ) == \ [(QQ(10), QQ(10)), (QQ(20), QQ(20)), (QQ(30), QQ(30))] assert dup_isolate_real_roots_sqf([1,-100,3500,-50000,240000], ZZ) == \ [(QQ(10), QQ(10)), (QQ(20), QQ(20)), (QQ(30), QQ(30)), (QQ(40), QQ(40))] assert dup_isolate_real_roots_sqf([1,-150,8500,-225000,2740000,-12000000], ZZ) == \ [(QQ(10), QQ(10)), (QQ(20), QQ(20)), (QQ(30), QQ(30)), (QQ(40), QQ(40)), (QQ(50), QQ(50))] assert dup_isolate_real_roots_sqf([1,1], ZZ) == \ [(-QQ(1), -QQ(1))] assert dup_isolate_real_roots_sqf([1,3,2], ZZ) == \ [(-QQ(2), -QQ(2)), (-QQ(1), -QQ(1))] assert dup_isolate_real_roots_sqf([1,6,11,6], ZZ) == \ [(-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1))] assert dup_isolate_real_roots_sqf([1,10,35,50,24], ZZ) == \ [(-QQ(4), -QQ(4)), (-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1))] assert dup_isolate_real_roots_sqf([1,15,85,225,274,120], ZZ) == \ [(-QQ(5), -QQ(5)), (-QQ(4), -QQ(4)), (-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1))] assert dup_isolate_real_roots_sqf([1,10], ZZ) == \ [(-QQ(10), -QQ(10))] assert dup_isolate_real_roots_sqf([1,30,200], ZZ) == \ [(-QQ(20), -QQ(20)), (-QQ(10), -QQ(10))] assert dup_isolate_real_roots_sqf([1,60,1100,6000], ZZ) == \ [(-QQ(30), -QQ(30)), (-QQ(20), -QQ(20)), (-QQ(10), -QQ(10))] assert dup_isolate_real_roots_sqf([1,100,3500,50000,240000], ZZ) == \ [(-QQ(40), -QQ(40)), (-QQ(30), -QQ(30)), (-QQ(20), -QQ(20)), (-QQ(10), -QQ(10))] assert dup_isolate_real_roots_sqf([1,150,8500,225000,2740000,12000000], ZZ) == \ [(-QQ(50), -QQ(50)), (-QQ(40), -QQ(40)), (-QQ(30), -QQ(30)), (-QQ(20), -QQ(20)), (-QQ(10), -QQ(10))] assert dup_isolate_real_roots_sqf([1,0,-5], ZZ) == \ [(QQ(-3), QQ(-2)), (QQ(2), QQ(3))] assert dup_isolate_real_roots_sqf([1,0,0,-5], ZZ) == \ [(QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,0,0,-5], ZZ) == \ [(QQ(-2), QQ(-1)), (QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,0,0,0,-5], ZZ) == \ [(QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,0,0,0,0,-5], ZZ) == \ [(QQ(-2), QQ(-1)), (QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,0,0,0,0,0,-5], ZZ) == \ [(QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,0,0,0,0,0,0,-5], ZZ) == \ [(QQ(-2), QQ(-1)), (QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,0,0,0,0,0,0,0,-5], ZZ) == \ [(QQ(1), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,-1], ZZ) == \ [(-QQ(1), -QQ(1)), (QQ(1), QQ(1))] assert dup_isolate_real_roots_sqf([1,2,-1,-2], ZZ) == \ [(-QQ(2), -QQ(2)), (-QQ(1), -QQ(1)), (QQ(1), QQ(1))] assert dup_isolate_real_roots_sqf([1,0,-5,0,4], ZZ) == \ [(-QQ(2), -QQ(2)), (-QQ(1), -QQ(1)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] assert dup_isolate_real_roots_sqf([1,3,-5,-15,4,12], ZZ) == \ [(-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1)), (QQ(1), QQ(1)), ( QQ(2), QQ(2))] assert dup_isolate_real_roots_sqf([1,0,-14,0,49,0,-36], ZZ) == \ [(-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1)), (QQ(1), QQ(1)), ( QQ(2), QQ(2)), ( QQ(3), QQ(3))] assert dup_isolate_real_roots_sqf([2,1,-28,-14,98,49,-72,-36], ZZ) == \ [(-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1)), (-QQ(1), QQ(0)), ( QQ(1), QQ(1)), ( QQ(2), QQ(2)), ( QQ(3), QQ(3))] assert dup_isolate_real_roots_sqf([4,0,-57,0,210,0,-193,0,36], ZZ) == \ [(-QQ(3), -QQ(3)), (-QQ(2), -QQ(2)), (-QQ(1), -QQ(1)), (-QQ(1), QQ(0)), ( QQ(0), QQ(1)), ( QQ(1), QQ(1)), ( QQ(2), QQ(2)), ( QQ(3), QQ(3))] f = [9,0,-2] assert dup_isolate_real_roots_sqf(f, ZZ) == \ [(QQ(-1), QQ(0)), (QQ(0), QQ(1))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,10)) == \ [(QQ(-1,2), QQ(-3,7)), (QQ(3,7), QQ(1,2))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,100)) == \ [(QQ(-9,19), QQ(-8,17)), (QQ(8,17), QQ(9,19))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,1000)) == \ [(QQ(-33,70), QQ(-8,17)), (QQ(8,17), QQ(33,70))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,10000)) == \ [(QQ(-33,70), QQ(-107,227)), (QQ(107,227), QQ(33,70))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,100000)) == \ [(QQ(-305,647), QQ(-272,577)), (QQ(272,577), QQ(305,647))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,1000000)) == \ [(QQ(-1121,2378), QQ(-272,577)), (QQ(272,577), QQ(1121,2378))] f = [200100012, -700390052, 700490079, -200240054, 40017, -2] assert dup_isolate_real_roots_sqf(f, ZZ) == \ [(QQ(0), QQ(1,10002)), (QQ(1,10002), QQ(1,10002)), (QQ(1,2), QQ(1,2)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,100000)) == \ [(QQ(1,10003), QQ(1,10003)), (QQ(1,10002), QQ(1,10002)), (QQ(1,2), QQ(1,2)), (QQ(1), QQ(1)), (QQ(2), QQ(2))] a, b, c, d = 10000090000001, 2000100003, 10000300007, 10000005000008 f = [ 20001600074001600021, 1700135866278935491773999857, -2000179008931031182161141026995283662899200197, -800027600594323913802305066986600025, 100000950000540000725000008] assert dup_isolate_real_roots_sqf(f, ZZ) == \ [(-QQ(a), -QQ(a)), (-QQ(1,1), QQ(0,1)), (QQ(0,1), QQ(1,1)), (QQ(d), QQ(d))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,100000000000)) == \ [(-QQ(a), -QQ(a)), (-QQ(1,b), -QQ(1,b)), (QQ(1,c), QQ(1,c)), (QQ(d), QQ(d))] (u, v), B, C, (s, t) = dup_isolate_real_roots_sqf(f, ZZ, fast=True) assert u < -a < v and B == (-QQ(1), QQ(0)) and C == (QQ(0), QQ(1)) and s < d < t assert dup_isolate_real_roots_sqf(f, ZZ, fast=True, eps=QQ(1,100000000000000000000000000000)) == \ [(-QQ(a), -QQ(a)), (-QQ(1,b), -QQ(1,b)), (QQ(1,c), QQ(1,c)), (QQ(d), QQ(d))] assert dup_isolate_real_roots_sqf([QQ(8,5), QQ(-87374,3855), QQ(-17,771)], QQ) == \ [(QQ(-1), QQ(0)), (QQ(14), QQ(15))] f = [-10, 8, 80, -32, -160] assert dup_isolate_real_roots_sqf(f, ZZ) == \ [(-QQ(2), -QQ(2)), (-QQ(2), -QQ(1)), (QQ(2), QQ(2)), (QQ(2), QQ(3))] assert dup_isolate_real_roots_sqf(f, ZZ, eps=QQ(1,100)) == \ [(-QQ(2), -QQ(2)), (-QQ(23,14), -QQ(18,11)), (QQ(2), QQ(2)), (QQ(39,16), QQ(22,9))] assert dup_isolate_real_roots_sqf([1, -1], ZZ, inf=2) == [] assert dup_isolate_real_roots_sqf([1, -1], ZZ, sup=0) == [] assert dup_isolate_real_roots_sqf([1, -1], ZZ) == [(1, 1)] assert dup_isolate_real_roots_sqf([1, -1], ZZ, inf=1) == [(1, 1)] assert dup_isolate_real_roots_sqf([1, -1], ZZ, sup=1) == [(1, 1)] assert dup_isolate_real_roots_sqf([1, -1], ZZ, inf=1, sup=1) == [(1, 1)] f = [1, 0, -2] assert dup_isolate_real_roots_sqf(f, ZZ, inf=QQ(7,4)) == [] assert dup_isolate_real_roots_sqf(f, ZZ, inf=QQ(7,5)) == [(QQ(7,5), QQ(3,2))] assert dup_isolate_real_roots_sqf(f, ZZ, sup=QQ(7,5)) == [(-2, -1)] assert dup_isolate_real_roots_sqf(f, ZZ, sup=QQ(7,4)) == [(-2, -1), (1, QQ(3,2))] assert dup_isolate_real_roots_sqf(f, ZZ, sup=-QQ(7,4)) == [] assert dup_isolate_real_roots_sqf(f, ZZ, sup=-QQ(7,5)) == [(-QQ(3,2), -QQ(7,5))] assert dup_isolate_real_roots_sqf(f, ZZ, inf=-QQ(7,5)) == [(1, 2)] assert dup_isolate_real_roots_sqf(f, ZZ, inf=-QQ(7,4)) == [(-QQ(3,2), -1), (1, 2)] I = [(-2, -1), (1, 2)] assert dup_isolate_real_roots_sqf(f, ZZ, inf=-2) == I assert dup_isolate_real_roots_sqf(f, ZZ, sup=+2) == I assert dup_isolate_real_roots_sqf(f, ZZ, inf=-2, sup=2) == I raises(DomainError, "dup_isolate_real_roots_sqf([EX(1), EX(2)], EX)") def test_dup_isolate_real_roots(): assert dup_isolate_real_roots([], ZZ) == [] assert dup_isolate_real_roots([3], ZZ) == [] assert dup_isolate_real_roots([5,0], ZZ) == [((QQ(0), QQ(0)), 1)] assert dup_isolate_real_roots([7,0,0,0,0], ZZ) == [((QQ(0), QQ(0)), 4)] assert dup_isolate_real_roots([1, 1,0], ZZ) == [((-QQ(1), -QQ(1)), 1), ((QQ(0), QQ(0)), 1)] assert dup_isolate_real_roots([1,-1,0], ZZ) == [(( QQ(0), QQ(0)), 1), ((QQ(1), QQ(1)), 1)] assert dup_isolate_real_roots([1,0,0,1,1], ZZ) == [] I = [((-QQ(2), -QQ(1)), 1), ((QQ(1), QQ(2)), 1)] assert dup_isolate_real_roots([1,0,-2], ZZ) == I assert dup_isolate_real_roots([-1,0,2], ZZ) == I f = [16,-96,24,936,-1599,-2880,9196,552,-21831,13968,21690,-26784,-2916,15552,-5832] g = dup_sqf_part(f, ZZ) assert dup_isolate_real_roots(f, ZZ) == \ [((-QQ(2), -QQ(3,2)), 2), ((-QQ(3,2), -QQ(1,1)), 3), (( QQ(1), QQ(3,2)), 3), (( QQ(3,2), QQ(3,2)), 4), ((QQ(5,3), QQ(2)), 2)] assert dup_isolate_real_roots_sqf(g, ZZ) == \ [(-QQ(2), -QQ(3,2)), (-QQ(3,2), -QQ(1,1)), ( QQ(1), QQ(3,2)), ( QQ(3,2), QQ(3,2)), (QQ(3,2), QQ(2))] assert dup_isolate_real_roots(g, ZZ) == \ [((-QQ(2), -QQ(3,2)), 1), ((-QQ(3,2), -QQ(1,1)), 1), (( QQ(1), QQ(3,2)), 1), (( QQ(3,2), QQ(3,2)), 1), ((QQ(3,2), QQ(2)), 1)] assert dup_isolate_real_roots([1, -1], ZZ, inf=2) == [] assert dup_isolate_real_roots([1, -1], ZZ, sup=0) == [] assert dup_isolate_real_roots([1, -1], ZZ) == [((1, 1), 1)] assert dup_isolate_real_roots([1, -1], ZZ, inf=1) == [((1, 1), 1)] assert dup_isolate_real_roots([1, -1], ZZ, sup=1) == [((1, 1), 1)] assert dup_isolate_real_roots([1, -1], ZZ, inf=1, sup=1) == [((1, 1), 1)] f = [1, 0, -4, 0, 4] assert dup_isolate_real_roots(f, ZZ, inf=QQ(7,4)) == [] assert dup_isolate_real_roots(f, ZZ, inf=QQ(7,5)) == [((QQ(7,5), QQ(3,2)), 2)] assert dup_isolate_real_roots(f, ZZ, sup=QQ(7,5)) == [((-2, -1), 2)] assert dup_isolate_real_roots(f, ZZ, sup=QQ(7,4)) == [((-2, -1), 2), ((1, QQ(3,2)), 2)] assert dup_isolate_real_roots(f, ZZ, sup=-QQ(7,4)) == [] assert dup_isolate_real_roots(f, ZZ, sup=-QQ(7,5)) == [((-QQ(3,2), -QQ(7,5)), 2)] assert dup_isolate_real_roots(f, ZZ, inf=-QQ(7,5)) == [((1, 2), 2)] assert dup_isolate_real_roots(f, ZZ, inf=-QQ(7,4)) == [((-QQ(3,2), -1), 2), ((1, 2), 2)] I = [((-2, -1), 2), ((1, 2), 2)] assert dup_isolate_real_roots(f, ZZ, inf=-2) == I assert dup_isolate_real_roots(f, ZZ, sup=+2) == I assert dup_isolate_real_roots(f, ZZ, inf=-2, sup=2) == I f = [1, -3, -1, 11, -8, -8, 12, -4, 0, 0, 0, 0] assert dup_isolate_real_roots(f, ZZ, basis=False) == \ [((-2, -1), 2), ((0, 0), 4), ((1, 1), 3), ((1, 2), 2)] assert dup_isolate_real_roots(f, ZZ, basis=True) == \ [((-2, -1), 2, [1, 0, -2]), ((0, 0), 4, [1, 0]), ((1, 1), 3, [1, -1]), ((1, 2), 2, [1, 0, -2])] raises(DomainError, "dup_isolate_real_roots([EX(1), EX(2)], EX)") def test_dup_isolate_real_roots_list(): assert dup_isolate_real_roots_list([[1, 1,0], [1,0]], ZZ) == \ [((-QQ(1), -QQ(1)), {0: 1}), ((QQ(0), QQ(0)), {0: 1, 1: 1})] assert dup_isolate_real_roots_list([[1,-1,0], [1,0]], ZZ) == \ [((QQ(0), QQ(0)), {0: 1, 1: 1}), ((QQ(1), QQ(1)), {0: 1})] f = dup_from_raw_dict({5: ZZ(1), 0: -ZZ(200)}, ZZ) g = dup_from_raw_dict({5: ZZ(1), 0: -ZZ(201)}, ZZ) assert dup_isolate_real_roots_list([f, g], ZZ) == \ [((QQ(75,26), QQ(101,35)), {0: 1}), ((QQ(283,98), QQ(26,9)), {1: 1})] f = dup_from_raw_dict({5: -QQ(1,200), 0: QQ(1)}, QQ) g = dup_from_raw_dict({5: -QQ(1,201), 0: QQ(1)}, QQ) assert dup_isolate_real_roots_list([f, g], QQ) == \ [((QQ(75,26), QQ(101,35)), {0: 1}), ((QQ(283,98), QQ(26,9)), {1: 1})] assert dup_isolate_real_roots_list([[1,1], [1,2], [1,-1], [1,1], [1,-1], [1,-1]], ZZ) == \ [((-QQ(2), -QQ(2)), {1: 1}), ((-QQ(1), -QQ(1)), {0: 1, 3: 1}), ((QQ(1), QQ(1)), {2: 1, 4: 1, 5: 1})] assert dup_isolate_real_roots_list([[1,1], [1,2], [1,-1], [1,1], [1,-1], [1,2]], ZZ) == \ [((-QQ(2), -QQ(2)), {1: 1, 5: 1}), ((-QQ(1), -QQ(1)), {0: 1, 3: 1}), ((QQ(1), QQ(1)), {2: 1, 4: 1})] f, g = [1, 0, -4, 0, 4], [1, -1] assert dup_isolate_real_roots_list([f, g], ZZ, inf=QQ(7,4)) == [] assert dup_isolate_real_roots_list([f, g], ZZ, inf=QQ(7,5)) == [((QQ(7,5), QQ(3,2)), {0: 2})] assert dup_isolate_real_roots_list([f, g], ZZ, sup=QQ(7,5)) == [((-2, -1), {0: 2}), ((1, 1), {1: 1})] assert dup_isolate_real_roots_list([f, g], ZZ, sup=QQ(7,4)) == [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, QQ(3,2)), {0: 2})] assert dup_isolate_real_roots_list([f, g], ZZ, sup=-QQ(7,4)) == [] assert dup_isolate_real_roots_list([f, g], ZZ, sup=-QQ(7,5)) == [((-QQ(3,2), -QQ(7,5)), {0: 2})] assert dup_isolate_real_roots_list([f, g], ZZ, inf=-QQ(7,5)) == [((1, 1), {1: 1}), ((1, 2), {0: 2})] assert dup_isolate_real_roots_list([f, g], ZZ, inf=-QQ(7,4)) == [((-QQ(3,2), -1), {0: 2}), ((1, 1), {1: 1}), ((1, 2), {0: 2})] f, g = [2, 0, -1], [1, 0, -2] assert dup_isolate_real_roots_list([f, g], ZZ) == \ [((-QQ(2), -QQ(1)), {1: 1}), ((-QQ(1), QQ(0)), {0: 1}), ((QQ(0), QQ(1)), {0: 1}), ((QQ(1), QQ(2)), {1: 1})] assert dup_isolate_real_roots_list([f, g], ZZ, strict=True) == \ [((-QQ(3,2), -QQ(4,3)), {1: 1}), ((-QQ(1), -QQ(2,3)), {0: 1}), ((QQ(2,3), QQ(1)), {0: 1}), ((QQ(4,3), QQ(3,2)), {1: 1})] f, g = [1, 0, -2], [1, -1, -2, 2] assert dup_isolate_real_roots_list([f, g], ZZ) == \ [((-QQ(2), -QQ(1)), {1: 1, 0: 1}), ((QQ(1), QQ(1)), {1: 1}), ((QQ(1), QQ(2)), {1: 1, 0: 1})] f, g = [1, 0, -2, 0], [1, -1, -2, 2, 0, 0] assert dup_isolate_real_roots_list([f, g], ZZ) == \ [((-QQ(2), -QQ(1)), {1: 1, 0: 1}), ((QQ(0), QQ(0)), {0: 1, 1: 2}), ((QQ(1), QQ(1)), {1: 1}), ((QQ(1), QQ(2)), {1: 1, 0: 1})] f, g = [1, -3, -1, 11, -8, -8, 12, -4, 0, 0], [1, -2, 3, -4, 2, 0] assert dup_isolate_real_roots_list([f, g], ZZ, basis=False) == \ [((-2, -1), {0: 2}), ((0, 0), {0: 2, 1: 1}), ((1, 1), {0: 3, 1: 2}), ((1, 2), {0: 2})] assert dup_isolate_real_roots_list([f, g], ZZ, basis=True) == \ [((-2, -1), {0: 2}, [1, 0, -2]), ((0, 0), {0: 2, 1: 1}, [1, 0]), ((1, 1), {0: 3, 1: 2}, [1, -1]), ((1, 2), {0: 2}, [1, 0, -2])] raises(DomainError, "dup_isolate_real_roots_list([[EX(1), EX(2)]], EX)") def test_dup_count_real_roots(): assert dup_count_real_roots([], ZZ) == 0 assert dup_count_real_roots([7], ZZ) == 0 assert dup_count_real_roots([1,-1], ZZ) == 1 assert dup_count_real_roots([1,-1], ZZ, inf=1) == 1 assert dup_count_real_roots([1,-1], ZZ, sup=0) == 0 assert dup_count_real_roots([1,-1], ZZ, sup=1) == 1 assert dup_count_real_roots([1,-1], ZZ, inf=0, sup=1) == 1 assert dup_count_real_roots([1,-1], ZZ, inf=0, sup=2) == 1 assert dup_count_real_roots([1,-1], ZZ, inf=1, sup=2) == 1 assert dup_count_real_roots([1,0,-2], ZZ) == 2 assert dup_count_real_roots([1,0,-2], ZZ, sup=0) == 1 assert dup_count_real_roots([1,0,-2], ZZ, inf=-1, sup=1) == 0 a, b = (-QQ(1), -QQ(1)), (QQ(1), QQ(1)) c, d = ( QQ(0), QQ(0)), (QQ(1), QQ(1)) def test_dup_count_complex_roots_1(): # z-1 assert dup_count_complex_roots([1,-1], ZZ, a, b) == 1 assert dup_count_complex_roots([1,-1], ZZ, c, d) == 1 # z+1 assert dup_count_complex_roots([1,1], ZZ, a, b) == 1 assert dup_count_complex_roots([1,1], ZZ, c, d) == 0 def test_dup_count_complex_roots_2(): # (z-1)*(z) assert dup_count_complex_roots([1,-1,0], ZZ, a, b) == 2 assert dup_count_complex_roots([1,-1,0], ZZ, c, d) == 2 # (z-1)*(-z) assert dup_count_complex_roots([-1,1,0], ZZ, a, b) == 2 assert dup_count_complex_roots([-1,1,0], ZZ, c, d) == 2 # (z+1)*(z) assert dup_count_complex_roots([1,1,0], ZZ, a, b) == 2 assert dup_count_complex_roots([1,1,0], ZZ, c, d) == 1 # (z+1)*(-z) assert dup_count_complex_roots([-1,-1,0], ZZ, a, b) == 2 assert dup_count_complex_roots([-1,-1,0], ZZ, c, d) == 1 def test_dup_count_complex_roots_3(): # (z-1)*(z+1) assert dup_count_complex_roots([1,0,-1], ZZ, a, b) == 2 assert dup_count_complex_roots([1,0,-1], ZZ, c, d) == 1 # (z-1)*(z+1)*(z) assert dup_count_complex_roots([1,0,-1,0], ZZ, a, b) == 3 assert dup_count_complex_roots([1,0,-1,0], ZZ, c, d) == 2 # (z-1)*(z+1)*(-z) assert dup_count_complex_roots([-1,0,1,0], ZZ, a, b) == 3 assert dup_count_complex_roots([-1,0,1,0], ZZ, c, d) == 2 def test_dup_count_complex_roots_4(): # (z-I)*(z+I) assert dup_count_complex_roots([1,0,1], ZZ, a, b) == 2 assert dup_count_complex_roots([1,0,1], ZZ, c, d) == 1 # (z-I)*(z+I)*(z) assert dup_count_complex_roots([1,0,1,0], ZZ, a, b) == 3 assert dup_count_complex_roots([1,0,1,0], ZZ, c, d) == 2 # (z-I)*(z+I)*(-z) assert dup_count_complex_roots([-1,0,-1,0], ZZ, a, b) == 3 assert dup_count_complex_roots([-1,0,-1,0], ZZ, c, d) == 2 # (z-I)*(z+I)*(z-1) assert dup_count_complex_roots([1,-1,1,-1], ZZ, a, b) == 3 assert dup_count_complex_roots([1,-1,1,-1], ZZ, c, d) == 2 # (z-I)*(z+I)*(z-1)*(z) assert dup_count_complex_roots([1,-1,1,-1,0], ZZ, a, b) == 4 assert dup_count_complex_roots([1,-1,1,-1,0], ZZ, c, d) == 3 # (z-I)*(z+I)*(z-1)*(-z) assert dup_count_complex_roots([-1,1,-1,1,0], ZZ, a, b) == 4 assert dup_count_complex_roots([-1,1,-1,1,0], ZZ, c, d) == 3 # (z-I)*(z+I)*(z-1)*(z+1) assert dup_count_complex_roots([1,0,0,0,-1], ZZ, a, b) == 4 assert dup_count_complex_roots([1,0,0,0,-1], ZZ, c, d) == 2 # (z-I)*(z+I)*(z-1)*(z+1)*(z) assert dup_count_complex_roots([1,0,0,0,-1,0], ZZ, a, b) == 5 assert dup_count_complex_roots([1,0,0,0,-1,0], ZZ, c, d) == 3 # (z-I)*(z+I)*(z-1)*(z+1)*(-z) assert dup_count_complex_roots([-1,0,0,0,1,0], ZZ, a, b) == 5 assert dup_count_complex_roots([-1,0,0,0,1,0], ZZ, c, d) == 3 def test_dup_count_complex_roots_5(): # (z-I+1)*(z+I+1) assert dup_count_complex_roots([1,2,2], ZZ, a, b) == 2 assert dup_count_complex_roots([1,2,2], ZZ, c, d) == 0 # (z-I+1)*(z+I+1)*(z-1) assert dup_count_complex_roots([1,1,0,-2], ZZ, a, b) == 3 assert dup_count_complex_roots([1,1,0,-2], ZZ, c, d) == 1 # (z-I+1)*(z+I+1)*(z-1)*z assert dup_count_complex_roots([1,1,0,-2,0], ZZ, a, b) == 4 assert dup_count_complex_roots([1,1,0,-2,0], ZZ, c, d) == 2 # (z-I+1)*(z+I+1)*(z+1) assert dup_count_complex_roots([1,3,4,2], ZZ, a, b) == 3 assert dup_count_complex_roots([1,3,4,2], ZZ, c, d) == 0 # (z-I+1)*(z+I+1)*(z+1)*z assert dup_count_complex_roots([1,3,4,2,0], ZZ, a, b) == 4 assert dup_count_complex_roots([1,3,4,2,0], ZZ, c, d) == 1 # (z-I+1)*(z+I+1)*(z-1)*(z+1) assert dup_count_complex_roots([1,2,1,-2,-2], ZZ, a, b) == 4 assert dup_count_complex_roots([1,2,1,-2,-2], ZZ, c, d) == 1 # (z-I+1)*(z+I+1)*(z-1)*(z+1)*z assert dup_count_complex_roots([1,2,1,-2,-2,0], ZZ, a, b) == 5 assert dup_count_complex_roots([1,2,1,-2,-2,0], ZZ, c, d) == 2 def test_dup_count_complex_roots_6(): # (z-I-1)*(z+I-1) assert dup_count_complex_roots([1,-2,2], ZZ, a, b) == 2 assert dup_count_complex_roots([1,-2,2], ZZ, c, d) == 1 # (z-I-1)*(z+I-1)*(z-1) assert dup_count_complex_roots([1,-3,4,-2], ZZ, a, b) == 3 assert dup_count_complex_roots([1,-3,4,-2], ZZ, c, d) == 2 # (z-I-1)*(z+I-1)*(z-1)*z assert dup_count_complex_roots([1,-3,4,-2,0], ZZ, a, b) == 4 assert dup_count_complex_roots([1,-3,4,-2,0], ZZ, c, d) == 3 # (z-I-1)*(z+I-1)*(z+1) assert dup_count_complex_roots([1,-1,0,2], ZZ, a, b) == 3 assert dup_count_complex_roots([1,-1,0,2], ZZ, c, d) == 1 # (z-I-1)*(z+I-1)*(z+1)*z assert dup_count_complex_roots([1,-1,0,2,0], ZZ, a, b) == 4 assert dup_count_complex_roots([1,-1,0,2,0], ZZ, c, d) == 2 # (z-I-1)*(z+I-1)*(z-1)*(z+1) assert dup_count_complex_roots([1,-2,1,2,-2], ZZ, a, b) == 4 assert dup_count_complex_roots([1,-2,1,2,-2], ZZ, c, d) == 2 # (z-I-1)*(z+I-1)*(z-1)*(z+1)*z assert dup_count_complex_roots([1,-2,1,2,-2,0], ZZ, a, b) == 5 assert dup_count_complex_roots([1,-2,1,2,-2,0], ZZ, c, d) == 3 def test_dup_count_complex_roots_7(): # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1) assert dup_count_complex_roots([1,0,0,0,4], ZZ, a, b) == 4 assert dup_count_complex_roots([1,0,0,0,4], ZZ, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-2) assert dup_count_complex_roots([1,-2,0,0,4,-8], ZZ, a, b) == 4 assert dup_count_complex_roots([1,-2,0,0,4,-8], ZZ, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z**2-2) assert dup_count_complex_roots([1,0,-2,0,4,0,-8], ZZ, a, b) == 4 assert dup_count_complex_roots([1,0,-2,0,4,0,-8], ZZ, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1) assert dup_count_complex_roots([1,-1,0,0,4,-4], ZZ, a, b) == 5 assert dup_count_complex_roots([1,-1,0,0,4,-4], ZZ, c, d) == 2 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*z assert dup_count_complex_roots([1,-1,0,0,4,-4,0], ZZ, a, b) == 6 assert dup_count_complex_roots([1,-1,0,0,4,-4,0], ZZ, c, d) == 3 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z+1) assert dup_count_complex_roots([1,1,0,0,4,4], ZZ, a, b) == 5 assert dup_count_complex_roots([1,1,0,0,4,4], ZZ, c, d) == 1 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z+1)*z assert dup_count_complex_roots([1,1,0,0,4,4,0], ZZ, a, b) == 6 assert dup_count_complex_roots([1,1,0,0,4,4,0], ZZ, c, d) == 2 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1) assert dup_count_complex_roots([1,0,-1,0,4,0,-4], ZZ, a, b) == 6 assert dup_count_complex_roots([1,0,-1,0,4,0,-4], ZZ, c, d) == 2 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*z assert dup_count_complex_roots([1,0,-1,0,4,0,-4,0], ZZ, a, b) == 7 assert dup_count_complex_roots([1,0,-1,0,4,0,-4,0], ZZ, c, d) == 3 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I) assert dup_count_complex_roots([1,0,0,0,3,0,0,0,-4], ZZ, a, b) == 8 assert dup_count_complex_roots([1,0,0,0,3,0,0,0,-4], ZZ, c, d) == 3 def test_dup_count_complex_roots_8(): # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I)*z assert dup_count_complex_roots([1,0,0,0,3,0,0,0,-4,0], ZZ, a, b) == 9 assert dup_count_complex_roots([1,0,0,0,3,0,0,0,-4,0], ZZ, c, d) == 4 # (z-I-1)*(z+I-1)*(z-I+1)*(z+I+1)*(z-1)*(z+1)*(z-I)*(z+I)*(z**2-2)*z assert dup_count_complex_roots([1,0,-2,0,3,0,-6,0,-4,0,8,0], ZZ, a, b) == 9 assert dup_count_complex_roots([1,0,-2,0,3,0,-6,0,-4,0,8,0], ZZ, c, d) == 4 def test_dup_count_complex_roots_implicit(): f = [1,0,0,0,-1,0] # z*(z-1)*(z+1)*(z-I)*(z+I) assert dup_count_complex_roots(f, ZZ) == 5 assert dup_count_complex_roots(f, ZZ, sup=(0, 0)) == 3 assert dup_count_complex_roots(f, ZZ, inf=(0, 0)) == 3 def test_dup_count_complex_roots_exclude(): f = [1,0,0,0,-1,0] # z*(z-1)*(z+1)*(z-I)*(z+I) a, b = (-QQ(1), QQ(0)), (QQ(1), QQ(1)) assert dup_count_complex_roots(f, ZZ, a, b) == 4 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['S']) == 3 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['N']) == 3 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['S', 'N']) == 2 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['E']) == 4 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['W']) == 4 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['E', 'W']) == 4 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['N', 'S', 'E', 'W']) == 2 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['SW']) == 3 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['SE']) == 3 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['SW', 'SE']) == 2 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['SW', 'SE', 'S']) == 1 assert dup_count_complex_roots(f, ZZ, a, b, exclude=['SW', 'SE', 'S', 'N']) == 0 a, b = (QQ(0), QQ(0)), (QQ(1), QQ(1)) assert dup_count_complex_roots(f, ZZ, a, b, exclude=True) == 1 def test_dup_isolate_complex_roots_sqf(): f = [1, -2, 3] assert dup_isolate_complex_roots_sqf(f, ZZ) == \ [((0, -6), (6, 0)), ((0, 0), (6, 6))] assert [ r.as_tuple() for r in dup_isolate_complex_roots_sqf(f, ZZ, blackbox=True) ] == \ [((0, -6), (6, 0)), ((0, 0), (6, 6))] assert dup_isolate_complex_roots_sqf(f, ZZ, eps=QQ(1,10)) == \ [((QQ(15,16), -QQ(3,2)), (QQ(33,32), -QQ(45,32))), ((QQ(15,16), QQ(45,32)), (QQ(33,32), QQ(3,2)))] assert dup_isolate_complex_roots_sqf(f, ZZ, eps=QQ(1,100)) == \ [((QQ(255,256), -QQ(363,256)), (QQ(513,512), -QQ(723,512))), ((QQ(255,256), QQ(723,512)), (QQ(513,512), QQ(363,256)))] f = [7, -19, 20, 17, 20] assert dup_isolate_complex_roots_sqf(f, ZZ) == \ [((-QQ(40,7), -QQ(40,7)), (0, 0)), ((-QQ(40,7), 0), (0, QQ(40,7))), ((0, -QQ(40,7)), (QQ(40,7), 0)), ((0, 0), (QQ(40,7), QQ(40,7)))] def test_dup_isolate_all_roots_sqf(): f = [4, -1, 2, 5, 0] assert dup_isolate_all_roots_sqf(f, ZZ) == \ ([(-1, 0), (0, 0)], [((0, -QQ(5,2)), (QQ(5,2), 0)), ((0, 0), (QQ(5,2), QQ(5,2)))]) assert dup_isolate_all_roots_sqf(f, ZZ, eps=QQ(1,10)) == \ ([(QQ(-7,8), QQ(-6,7)), (0, 0)], [((QQ(35,64), -QQ(35,32)), (QQ(5,8), -QQ(65,64))), ((QQ(35,64), QQ(65,64)), (QQ(5,8), QQ(35,32)))]) def test_dup_isolate_all_roots(): f = [4, -1, 2, 5, 0] assert dup_isolate_all_roots(f, ZZ) == \ ([((-1, 0), 1), ((0, 0), 1)], [(((0, -QQ(5,2)), (QQ(5,2), 0)), 1), (((0, 0), (QQ(5,2), QQ(5,2))), 1)]) assert dup_isolate_all_roots(f, ZZ, eps=QQ(1,10)) == \ ([((QQ(-7,8), QQ(-6,7)), 1), ((0, 0), 1)], [(((QQ(35,64), -QQ(35,32)), (QQ(5,8), -QQ(65,64))), 1), (((QQ(35,64), QQ(65,64)), (QQ(5,8), QQ(35,32))), 1)]) raises(NotImplementedError, "dup_isolate_all_roots([1, 1, -2, -2, 1, 1], ZZ)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_orthopolys.py0000644000175000017500000000735612014170666027255 0ustar georgeskgeorgesk"""Tests for efficient functions for generating orthogonal polynomials. """ from sympy import Poly, Rational as Q from sympy.utilities.pytest import raises from sympy.polys.orthopolys import ( chebyshevt_poly, chebyshevu_poly, hermite_poly, legendre_poly, laguerre_poly, ) from sympy.abc import x, a def test_chebyshevt_poly(): raises(ValueError, "chebyshevt_poly(-1, x)") assert chebyshevt_poly(1, x, polys=True) == Poly(x) assert chebyshevt_poly(0, x) == 1 assert chebyshevt_poly(1, x) == x assert chebyshevt_poly(2, x) == 2*x**2 - 1 assert chebyshevt_poly(3, x) == 4*x**3 - 3*x assert chebyshevt_poly(4, x) == 8*x**4 - 8*x**2 + 1 assert chebyshevt_poly(5, x) == 16*x**5 - 20*x**3 + 5*x assert chebyshevt_poly(6, x) == 32*x**6 - 48*x**4 + 18*x**2 - 1 assert chebyshevt_poly(1).dummy_eq(x) assert chebyshevt_poly(1, polys=True) == Poly(x) def test_chebyshevu_poly(): raises(ValueError, "chebyshevu_poly(-1, x)") assert chebyshevu_poly(1, x, polys=True) == Poly(2*x) assert chebyshevu_poly(0, x) == 1 assert chebyshevu_poly(1, x) == 2*x assert chebyshevu_poly(2, x) == 4*x**2 - 1 assert chebyshevu_poly(3, x) == 8*x**3 - 4*x assert chebyshevu_poly(4, x) == 16*x**4 - 12*x**2 + 1 assert chebyshevu_poly(5, x) == 32*x**5 - 32*x**3 + 6*x assert chebyshevu_poly(6, x) == 64*x**6 - 80*x**4 + 24*x**2 - 1 assert chebyshevu_poly(1).dummy_eq(2*x) assert chebyshevu_poly(1, polys=True) == Poly(2*x) def test_hermite_poly(): raises(ValueError, "hermite_poly(-1, x)") assert hermite_poly(1, x, polys=True) == Poly(2*x) assert hermite_poly(0, x) == 1 assert hermite_poly(1, x) == 2*x assert hermite_poly(2, x) == 4*x**2 - 2 assert hermite_poly(3, x) == 8*x**3 - 12*x assert hermite_poly(4, x) == 16*x**4 - 48*x**2 + 12 assert hermite_poly(5, x) == 32*x**5 - 160*x**3 + 120*x assert hermite_poly(6, x) == 64*x**6 - 480*x**4 + 720*x**2 - 120 assert hermite_poly(1).dummy_eq(2*x) assert hermite_poly(1, polys=True) == Poly(2*x) def test_legendre_poly(): raises(ValueError, "legendre_poly(-1, x)") assert legendre_poly(1, x, polys=True) == Poly(x) assert legendre_poly(0, x) == 1 assert legendre_poly(1, x) == x assert legendre_poly(2, x) == Q(3,2)*x**2 - Q(1,2) assert legendre_poly(3, x) == Q(5,2)*x**3 - Q(3,2)*x assert legendre_poly(4, x) == Q(35,8)*x**4 - Q(30,8)*x**2 + Q(3,8) assert legendre_poly(5, x) == Q(63,8)*x**5 - Q(70,8)*x**3 + Q(15,8)*x assert legendre_poly(6, x) == Q(231,16)*x**6 - Q(315,16)*x**4 + Q(105,16)*x**2 - Q(5,16) assert legendre_poly(1).dummy_eq(x) assert legendre_poly(1, polys=True) == Poly(x) def test_laguerre_poly(): raises(ValueError, "laguerre_poly(-1, x)") assert laguerre_poly(1, x, polys=True) == Poly(-x + 1) assert laguerre_poly(0, x) == 1 assert laguerre_poly(1, x) == -x + 1 assert laguerre_poly(2, x) == Q(1,2)*x**2 - Q(4,2)*x + 1 assert laguerre_poly(3, x) == -Q(1,6)*x**3 + Q(9,6)*x**2 - Q(18,6)*x + 1 assert laguerre_poly(4, x) == Q(1,24)*x**4 - Q(16,24)*x**3 + Q(72,24)*x**2 - Q(96,24)*x + 1 assert laguerre_poly(5, x) == -Q(1,120)*x**5 + Q(25,120)*x**4 - Q(200,120)*x**3 + Q(600,120)*x**2 - Q(600,120)*x + 1 assert laguerre_poly(6, x) == Q(1,720)*x**6 - Q(36,720)*x**5 + Q(450,720)*x**4 - Q(2400,720)*x**3 + Q(5400,720)*x**2 - Q(4320,720)*x + 1 assert laguerre_poly(0, x, a) == 1 assert laguerre_poly(1, x, a) == -x + a + 1 assert laguerre_poly(2, x, a) == x**2/2 + (-a - 2)*x + a**2/2 + 3*a/2 + 1 assert laguerre_poly(3, x, a) == -x**3/6 + (a/2 + Q(3)/2)*x**2 + (-a**2/2 - 5*a/2 - 3)*x + a**3/6 + a**2 + 11*a/6 + 1 assert laguerre_poly(1).dummy_eq(-x + 1) assert laguerre_poly(1, polys=True) == Poly(-x + 1) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_euclidtools.py0000644000175000017500000005115012014170666027350 0ustar georgeskgeorgesk"""Tests for Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ from sympy.polys.euclidtools import ( dup_gcdex, dup_half_gcdex, dup_invert, dup_euclidean_prs, dmp_euclidean_prs, dup_primitive_prs, dmp_primitive_prs, dup_subresultants, dmp_subresultants, dup_prs_resultant, dmp_prs_resultant, dmp_zz_collins_resultant, dmp_qq_collins_resultant, dup_resultant, dmp_resultant, dup_discriminant, dmp_discriminant, dup_zz_heu_gcd, dmp_zz_heu_gcd, dup_qq_heu_gcd, dmp_qq_heu_gcd, dup_rr_prs_gcd, dmp_rr_prs_gcd, dup_ff_prs_gcd, dmp_ff_prs_gcd, dup_inner_gcd, dmp_inner_gcd, dup_lcm, dmp_lcm, dmp_content, dmp_primitive, dup_cancel, dmp_cancel) from sympy.polys.densebasic import ( dmp_one_p, dup_LC, dmp_LC, dup_normal, dmp_normal) from sympy.polys.densearith import ( dup_add, dup_mul, dmp_mul, dup_exquo) from sympy.polys.densetools import ( dup_diff) from sympy.polys.specialpolys import ( f_4, f_5, f_6, dmp_fateman_poly_F_1, dmp_fateman_poly_F_2, dmp_fateman_poly_F_3) from sympy.polys.domains import ZZ, QQ, RR def test_dup_gcdex(): f = dup_normal([1,-2,-6,12,15], QQ) g = dup_normal([1,1,-4,-4], QQ) s = [QQ(-1,5),QQ(3,5)] t = [QQ(1,5),QQ(-6,5),QQ(2)] h = [QQ(1),QQ(1)] assert dup_half_gcdex(f, g, QQ) == (s, h) assert dup_gcdex(f, g, QQ) == (s, t, h) f = dup_normal([1,4,0,-1,1], QQ) g = dup_normal([1,0,-1,1], QQ) s, t, h = dup_gcdex(f, g, QQ) S, T, H = dup_gcdex(g, f, QQ) assert dup_add(dup_mul(s, f, QQ), dup_mul(t, g, QQ), QQ) == h assert dup_add(dup_mul(S, g, QQ), dup_mul(T, f, QQ), QQ) == H f = dup_normal([2,0], QQ) g = dup_normal([1,0,-16], QQ) s = [QQ(1,32),QQ(0)] t = [QQ(-1,16)] h = [QQ(1)] assert dup_half_gcdex(f, g, QQ) == (s, h) assert dup_gcdex(f, g, QQ) == (s, t, h) def test_dup_invert(): assert dup_invert([QQ(2),QQ(0)], [QQ(1),QQ(0),QQ(-16)], QQ) == [QQ(1,32),QQ(0)] def test_dup_euclidean_prs(): f = QQ.map([1, 0, 1, 0, -3, -3, 8, 2, -5]) g = QQ.map([3, 0, 5, 0, -4, -9, 21]) assert dup_euclidean_prs(f, g, QQ) == [f, g, [-QQ(5,9), QQ(0,1), QQ(1,9), QQ(0,1), -QQ(1,3)], [-QQ(117,25), -QQ(9,1), QQ(441,25)], [QQ(233150,19773), -QQ(102500,6591)], [-QQ(1288744821,543589225)]] def test_dup_primitive_prs(): f = ZZ.map([1, 0, 1, 0, -3, -3, 8, 2, -5]) g = ZZ.map([3, 0, 5, 0, -4, -9, 21]) assert dup_primitive_prs(f, g, ZZ) == [f, g, [-ZZ(5), ZZ(0), ZZ(1), ZZ(0), -ZZ(3)], [ZZ(13), ZZ(25), -ZZ(49)], [ZZ(4663), -ZZ(6150)], [ZZ(1)]] def test_dup_subresultants(): assert dup_resultant([], [], ZZ) == ZZ(0) assert dup_resultant([ZZ(1)], [], ZZ) == ZZ(0) assert dup_resultant([], [ZZ(1)], ZZ) == ZZ(0) f = dup_normal([1,0,1,0,-3,-3,8,2,-5], ZZ) g = dup_normal([3,0,5,0,-4,-9,21], ZZ) a = dup_normal([15,0,-3,0,9], ZZ) b = dup_normal([65,125,-245], ZZ) c = dup_normal([9326,-12300], ZZ) d = dup_normal([260708], ZZ) assert dup_subresultants(f, g, ZZ) == [f, g, a, b, c, d] assert dup_resultant(f, g, ZZ) == dup_LC(d, ZZ) f = dup_normal([1,-2,1], ZZ) g = dup_normal([1,0,-1], ZZ) a = dup_normal([2,-2], ZZ) assert dup_subresultants(f, g, ZZ) == [f, g, a] assert dup_resultant(f, g, ZZ) == 0 f = dup_normal([1,0, 1], ZZ) g = dup_normal([1,0,-1], ZZ) a = dup_normal([-2], ZZ) assert dup_subresultants(f, g, ZZ) == [f, g, a] assert dup_resultant(f, g, ZZ) == 4 f = dup_normal([1,0,-1], ZZ) g = dup_normal([1,-1,0,2], ZZ) assert dup_resultant(f, g, ZZ) == 0 f = dup_normal([3,0,-1,0], ZZ) g = dup_normal([5,0,1], ZZ) assert dup_resultant(f, g, ZZ) == 64 f = dup_normal([1,-2,7], ZZ) g = dup_normal([1,0,-1,5], ZZ) assert dup_resultant(f, g, ZZ) == 265 f = dup_normal([1,-6,11,-6], ZZ) g = dup_normal([1,-15,74,-120], ZZ) assert dup_resultant(f, g, ZZ) == -8640 f = dup_normal([1,-6,11,-6], ZZ) g = dup_normal([1,-10,29,-20], ZZ) assert dup_resultant(f, g, ZZ) == 0 f = dup_normal([1,0,0,-1], ZZ) g = dup_normal([1,2,2,-1], ZZ) assert dup_resultant(f, g, ZZ) == 16 f = dup_normal([1,0,0,0,0,0,0,0,-2], ZZ) g = dup_normal([1,-1], ZZ) assert dup_resultant(f, g, ZZ) == -1 def test_dmp_subresultants(): assert dmp_resultant([[]], [[]], 1, ZZ) == [] assert dmp_prs_resultant([[]], [[]], 1, ZZ)[0] == [] assert dmp_zz_collins_resultant([[]], [[]], 1, ZZ) == [] assert dmp_qq_collins_resultant([[]], [[]], 1, ZZ) == [] assert dmp_resultant([[ZZ(1)]], [[]], 1, ZZ) == [] assert dmp_resultant([[ZZ(1)]], [[]], 1, ZZ) == [] assert dmp_resultant([[ZZ(1)]], [[]], 1, ZZ) == [] assert dmp_resultant([[]], [[ZZ(1)]], 1, ZZ) == [] assert dmp_prs_resultant([[]], [[ZZ(1)]], 1, ZZ)[0] == [] assert dmp_zz_collins_resultant([[]], [[ZZ(1)]], 1, ZZ) == [] assert dmp_qq_collins_resultant([[]], [[ZZ(1)]], 1, ZZ) == [] f = dmp_normal([[3,0],[],[-1,0,0,-4]], 1, ZZ) g = dmp_normal([[1],[1,0,0,0],[-9]], 1, ZZ) a = dmp_normal([[3,0,0,0,0],[1,0,-27,4]], 1, ZZ) b = dmp_normal([[-3,0,0,-12,1,0,-54,8,729,-216,16]], 1, ZZ) r = dmp_LC(b, ZZ) assert dmp_subresultants(f, g, 1, ZZ) == [f, g, a, b] assert dmp_resultant(f, g, 1, ZZ) == r assert dmp_prs_resultant(f, g, 1, ZZ)[0] == r assert dmp_zz_collins_resultant(f, g, 1, ZZ) == r assert dmp_qq_collins_resultant(f, g, 1, ZZ) == r f = dmp_normal([[-1],[],[],[5]], 1, ZZ) g = dmp_normal([[3,1],[],[]], 1, ZZ) a = dmp_normal([[45,30,5]], 1, ZZ) b = dmp_normal([[675,675,225,25]], 1, ZZ) r = dmp_LC(b, ZZ) assert dmp_subresultants(f, g, 1, ZZ) == [f, g, a] assert dmp_resultant(f, g, 1, ZZ) == r assert dmp_prs_resultant(f, g, 1, ZZ)[0] == r assert dmp_zz_collins_resultant(f, g, 1, ZZ) == r assert dmp_qq_collins_resultant(f, g, 1, ZZ) == r f = [[[[[6]]]], [[[[-3]]], [[[-2]], [[]]]], [[[[1]], [[]]], [[[]]]]] g = [[[[[1]]]], [[[[-1], [-1, 0]]]], [[[[1, 0], []]]]] r = [[[[1]], [[-3], [-3, 0]], [[9, 0], []]], [[[-2], [-2, 0]], [[6], [12, 0], [6, 0, 0]], [[-18, 0], [-18, 0, 0], []]], [[[4, 0], []], [[-12, 0], [-12, 0, 0], []], [[36, 0, 0], [], []]]] assert dmp_zz_collins_resultant(f, g, 4, ZZ) == r f = [[[[[QQ(1,1)]]]], [[[[QQ(-1,2)]]], [[[QQ(-1,3)]], [[]]]], [[[[QQ(1,6)]], [[]]], [[[]]]]] g = [[[[[QQ(1,1)]]]], [[[[QQ(-1,1)], [QQ(-1,1), QQ(0, 1)]]]], [[[[QQ(1,1), QQ(0,1)], []]]]] r = [[[[QQ(1,36)]], [[QQ(-1,12)], [QQ(-1,12), QQ(0,1)]], [[QQ(1,4), QQ(0,1)], []]], [[[QQ(-1,18)], [QQ(-1,18), QQ(0,1)]], [[QQ(1,6)], [QQ(1,3), QQ(0,1)], [QQ(1,6), QQ(0,1), QQ(0,1)]], [[QQ(-1,2), QQ(0,1)], [QQ(-1,2), QQ(0,1), QQ(0,1)], []]], [[[QQ(1,9), QQ(0,1)], []], [[QQ(-1,3), QQ(0,1)], [QQ(-1,3), QQ(0,1), QQ(0,1)], []], [[QQ(1,1), QQ(0,1), QQ(0,1)], [], []]]] assert dmp_qq_collins_resultant(f, g, 4, QQ) == r def test_dup_discriminant(): assert dup_discriminant([], ZZ) == 0 assert dup_discriminant([1,0], ZZ) == 1 assert dup_discriminant([1,3,9,-13], ZZ) == -11664 assert dup_discriminant([5,0,1,0,0,2], ZZ) == 31252160 assert dup_discriminant([1,2,6,-22,13], ZZ) == 0 assert dup_discriminant([12,0,0,15,30,1,0,1], ZZ) == -220289699947514112 def test_dmp_discriminant(): assert dmp_discriminant([], 0, ZZ) == 0 assert dmp_discriminant([[]], 1, ZZ) == [] assert dmp_discriminant([[1,0]], 1, ZZ) == [] assert dmp_discriminant([1,3,9,-13], 0, ZZ) == -11664 assert dmp_discriminant([5,0,1,0,0,2], 0, ZZ) == 31252160 assert dmp_discriminant([1,2,6,-22,13], 0, ZZ) == 0 assert dmp_discriminant([12,0,0,15,30,1,0,1], 0, ZZ) == -220289699947514112 assert dmp_discriminant([[1,0],[],[2,0]], 1, ZZ) == [-8,0,0] assert dmp_discriminant([[1,0,2],[]], 1, ZZ) == [1] assert dmp_discriminant([[[1],[]],[[1,0]]], 2, ZZ) == [[1]] assert dmp_discriminant([[[[1]],[[]]],[[[1],[]]],[[[1,0]]]], 3, ZZ) == \ [[[-4, 0]], [[1], [], []]] assert dmp_discriminant([[[[[1]]],[[[]]]],[[[[1]],[[]]]],[[[[1],[]]]],[[[[1,0]]]]], 4, ZZ) == \ [[[[-27,0,0]]],[[[18,0],[]],[[-4],[],[],[]]],[[[-4,0]],[[1],[],[]],[[]],[[]]]] def test_dup_gcd(): assert dup_zz_heu_gcd([], [], ZZ) == ([], [], []) assert dup_rr_prs_gcd([], [], ZZ) == ([], [], []) assert dup_zz_heu_gcd([2], [], ZZ) == ([2], [1], []) assert dup_rr_prs_gcd([2], [], ZZ) == ([2], [1], []) assert dup_zz_heu_gcd([-2], [], ZZ) == ([2], [-1], []) assert dup_rr_prs_gcd([-2], [], ZZ) == ([2], [-1], []) assert dup_zz_heu_gcd([], [-2], ZZ) == ([2], [], [-1]) assert dup_rr_prs_gcd([], [-2], ZZ) == ([2], [], [-1]) assert dup_zz_heu_gcd([], [2,4], ZZ) == ([2,4], [], [1]) assert dup_rr_prs_gcd([], [2,4], ZZ) == ([2,4], [], [1]) assert dup_zz_heu_gcd([2,4], [], ZZ) == ([2,4], [1], []) assert dup_rr_prs_gcd([2,4], [], ZZ) == ([2,4], [1], []) assert dup_zz_heu_gcd([2], [2], ZZ) == ([2], [1], [1]) assert dup_rr_prs_gcd([2], [2], ZZ) == ([2], [1], [1]) assert dup_zz_heu_gcd([-2], [2], ZZ) == ([2], [-1], [1]) assert dup_rr_prs_gcd([-2], [2], ZZ) == ([2], [-1], [1]) assert dup_zz_heu_gcd([2], [-2], ZZ) == ([2], [1], [-1]) assert dup_rr_prs_gcd([2], [-2], ZZ) == ([2], [1], [-1]) assert dup_zz_heu_gcd([-2], [-2], ZZ) == ([2], [-1], [-1]) assert dup_rr_prs_gcd([-2], [-2], ZZ) == ([2], [-1], [-1]) assert dup_zz_heu_gcd([1,2,1], [1], ZZ) == ([1], [1, 2, 1], [1]) assert dup_rr_prs_gcd([1,2,1], [1], ZZ) == ([1], [1, 2, 1], [1]) assert dup_zz_heu_gcd([1,2,1], [2], ZZ) == ([1], [1, 2, 1], [2]) assert dup_rr_prs_gcd([1,2,1], [2], ZZ) == ([1], [1, 2, 1], [2]) assert dup_zz_heu_gcd([2,4,2], [2], ZZ) == ([2], [1, 2, 1], [1]) assert dup_rr_prs_gcd([2,4,2], [2], ZZ) == ([2], [1, 2, 1], [1]) assert dup_zz_heu_gcd([2], [2,4,2], ZZ) == ([2], [1], [1, 2, 1]) assert dup_rr_prs_gcd([2], [2,4,2], ZZ) == ([2], [1], [1, 2, 1]) assert dup_zz_heu_gcd([2,4,2], [1,1], ZZ) == ([1, 1], [2, 2], [1]) assert dup_rr_prs_gcd([2,4,2], [1,1], ZZ) == ([1, 1], [2, 2], [1]) assert dup_zz_heu_gcd([1,1], [2,4,2], ZZ) == ([1, 1], [1], [2, 2]) assert dup_rr_prs_gcd([1,1], [2,4,2], ZZ) == ([1, 1], [1], [2, 2]) f, g = [1, -31], [1, 0] assert dup_zz_heu_gcd(f, g, ZZ) == ([1], f, g) assert dup_rr_prs_gcd(f, g, ZZ) == ([1], f, g) f = [1,8,21,22,8] g = [1,6,11,6] h = [1,3,2] cff = [1,5,4] cfg = [1,3] assert dup_zz_heu_gcd(f, g, ZZ) == (h, cff, cfg) assert dup_rr_prs_gcd(f, g, ZZ) == (h, cff, cfg) f = [1,0,0,0,-4] g = [1,0,4,0, 4] h = [1,0,2] cff = [1,0,-2] cfg = [1,0, 2] assert dup_zz_heu_gcd(f, g, ZZ) == (h, cff, cfg) assert dup_rr_prs_gcd(f, g, ZZ) == (h, cff, cfg) f = [1,0,1,0,-3,-3,8,2,-5] g = [3,0,5,-0,-4,-9,21] h = [1] cff = f cfg = g assert dup_zz_heu_gcd(f, g, ZZ) == (h, cff, cfg) assert dup_rr_prs_gcd(f, g, ZZ) == (h, cff, cfg) f = dup_normal([1,0,1,0,-3,-3,8,2,-5], QQ) g = dup_normal([3,0,5,-0,-4,-9,21], QQ) h = dup_normal([1], QQ) assert dup_qq_heu_gcd(f, g, QQ) == (h, cff, cfg) assert dup_ff_prs_gcd(f, g, QQ) == (h, cff, cfg) f = [-352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272, 0, 0, 0, 0, 0, 0, 46818041807522713962450042363465092040687472354933295397472942006618953623327997952, 0, 0, 0, 0, 0, 0, 378182690892293941192071663536490788434899030680411695933646320291525827756032, 0, 0, 0, 0, 0, 0, 112806468807371824947796775491032386836656074179286744191026149539708928, 0, 0, 0, 0, 0, 0, -12278371209708240950316872681744825481125965781519138077173235712, 0, 0, 0, 0, 0, 0, 289127344604779611146960547954288113529690984687482920704, 0, 0, 0, 0, 0, 0, 19007977035740498977629742919480623972236450681, 0, 0, 0, 0, 0, 0, 311973482284542371301330321821976049] g = [365431878023781158602430064717380211405897160759702125019136, 0, 0, 0, 0, 0, 0, 197599133478719444145775798221171663643171734081650688, 0, 0, 0, 0, 0, 0, -9504116979659010018253915765478924103928886144, 0, 0, 0, 0, 0, 0, -311973482284542371301330321821976049] f = dup_normal(f, ZZ) g = dup_normal(g, ZZ) assert dup_zz_heu_gcd(f, dup_diff(f, 1, ZZ), ZZ)[0] == g assert dup_rr_prs_gcd(f, dup_diff(f, 1, ZZ), ZZ)[0] == g f = [QQ(1,2),QQ(1),QQ(1,2)] g = [QQ(1,2),QQ(1,2)] h = [QQ(1), QQ(1)] assert dup_qq_heu_gcd(f, g, QQ) == (h, g, [QQ(1,2)]) assert dup_ff_prs_gcd(f, g, QQ) == (h, g, [QQ(1,2)]) def test_dmp_gcd(): assert dmp_zz_heu_gcd([[]], [[]], 1, ZZ) == ([[]], [[]], [[]]) assert dmp_rr_prs_gcd([[]], [[]], 1, ZZ) == ([[]], [[]], [[]]) assert dmp_zz_heu_gcd([[2]], [[]], 1, ZZ) == ([[2]], [[1]], [[]]) assert dmp_rr_prs_gcd([[2]], [[]], 1, ZZ) == ([[2]], [[1]], [[]]) assert dmp_zz_heu_gcd([[-2]], [[]], 1, ZZ) == ([[2]], [[-1]], [[]]) assert dmp_rr_prs_gcd([[-2]], [[]], 1, ZZ) == ([[2]], [[-1]], [[]]) assert dmp_zz_heu_gcd([[]], [[-2]], 1, ZZ) == ([[2]], [[]], [[-1]]) assert dmp_rr_prs_gcd([[]], [[-2]], 1, ZZ) == ([[2]], [[]], [[-1]]) assert dmp_zz_heu_gcd([[]], [[2],[4]], 1, ZZ) == ([[2],[4]], [[]], [[1]]) assert dmp_rr_prs_gcd([[]], [[2],[4]], 1, ZZ) == ([[2],[4]], [[]], [[1]]) assert dmp_zz_heu_gcd([[2],[4]], [[]], 1, ZZ) == ([[2],[4]], [[1]], [[]]) assert dmp_rr_prs_gcd([[2],[4]], [[]], 1, ZZ) == ([[2],[4]], [[1]], [[]]) assert dmp_zz_heu_gcd([[2]], [[2]], 1, ZZ) == ([[2]], [[1]], [[1]]) assert dmp_rr_prs_gcd([[2]], [[2]], 1, ZZ) == ([[2]], [[1]], [[1]]) assert dmp_zz_heu_gcd([[-2]], [[2]], 1, ZZ) == ([[2]], [[-1]], [[1]]) assert dmp_rr_prs_gcd([[-2]], [[2]], 1, ZZ) == ([[2]], [[-1]], [[1]]) assert dmp_zz_heu_gcd([[2]], [[-2]], 1, ZZ) == ([[2]], [[1]], [[-1]]) assert dmp_rr_prs_gcd([[2]], [[-2]], 1, ZZ) == ([[2]], [[1]], [[-1]]) assert dmp_zz_heu_gcd([[-2]], [[-2]], 1, ZZ) == ([[2]], [[-1]], [[-1]]) assert dmp_rr_prs_gcd([[-2]], [[-2]], 1, ZZ) == ([[2]], [[-1]], [[-1]]) assert dmp_zz_heu_gcd([[1],[2],[1]], [[1]], 1, ZZ) == ([[1]], [[1], [2], [1]], [[1]]) assert dmp_rr_prs_gcd([[1],[2],[1]], [[1]], 1, ZZ) == ([[1]], [[1], [2], [1]], [[1]]) assert dmp_zz_heu_gcd([[1],[2],[1]], [[2]], 1, ZZ) == ([[1]], [[1], [2], [1]], [[2]]) assert dmp_rr_prs_gcd([[1],[2],[1]], [[2]], 1, ZZ) == ([[1]], [[1], [2], [1]], [[2]]) assert dmp_zz_heu_gcd([[2],[4],[2]], [[2]], 1, ZZ) == ([[2]], [[1], [2], [1]], [[1]]) assert dmp_rr_prs_gcd([[2],[4],[2]], [[2]], 1, ZZ) == ([[2]], [[1], [2], [1]], [[1]]) assert dmp_zz_heu_gcd([[2]], [[2],[4],[2]], 1, ZZ) == ([[2]], [[1]], [[1], [2], [1]]) assert dmp_rr_prs_gcd([[2]], [[2],[4],[2]], 1, ZZ) == ([[2]], [[1]], [[1], [2], [1]]) assert dmp_zz_heu_gcd([[2],[4],[2]], [[1],[1]], 1, ZZ) == ([[1], [1]], [[2], [2]], [[1]]) assert dmp_rr_prs_gcd([[2],[4],[2]], [[1],[1]], 1, ZZ) == ([[1], [1]], [[2], [2]], [[1]]) assert dmp_zz_heu_gcd([[1],[1]], [[2],[4],[2]], 1, ZZ) == ([[1], [1]], [[1]], [[2], [2]]) assert dmp_rr_prs_gcd([[1],[1]], [[2],[4],[2]], 1, ZZ) == ([[1], [1]], [[1]], [[2], [2]]) assert dmp_zz_heu_gcd([[[[1,2,1]]]], [[[[2,2]]]], 3, ZZ) == ([[[[1,1]]]], [[[[1,1]]]], [[[[2]]]]) assert dmp_rr_prs_gcd([[[[1,2,1]]]], [[[[2,2]]]], 3, ZZ) == ([[[[1,1]]]], [[[[1,1]]]], [[[[2]]]]) f, g = [[[[1,2,1],[1,1],[]]]], [[[[1,2,1]]]] h, cff, cfg = [[[[1,1]]]], [[[[1,1],[1],[]]]], [[[[1,1]]]] assert dmp_zz_heu_gcd(f, g, 3, ZZ) == (h, cff, cfg) assert dmp_rr_prs_gcd(f, g, 3, ZZ) == (h, cff, cfg) assert dmp_zz_heu_gcd(g, f, 3, ZZ) == (h, cfg, cff) assert dmp_rr_prs_gcd(g, f, 3, ZZ) == (h, cfg, cff) f, g, h = dmp_fateman_poly_F_1(2, ZZ) H, cff, cfg = dmp_zz_heu_gcd(f, g, 2, ZZ) assert H == h and dmp_mul(H, cff, 2, ZZ) == f \ and dmp_mul(H, cfg, 2, ZZ) == g H, cff, cfg = dmp_rr_prs_gcd(f, g, 2, ZZ) assert H == h and dmp_mul(H, cff, 2, ZZ) == f \ and dmp_mul(H, cfg, 2, ZZ) == g f, g, h = dmp_fateman_poly_F_1(4, ZZ) H, cff, cfg = dmp_zz_heu_gcd(f, g, 4, ZZ) assert H == h and dmp_mul(H, cff, 4, ZZ) == f \ and dmp_mul(H, cfg, 4, ZZ) == g f, g, h = dmp_fateman_poly_F_1(6, ZZ) H, cff, cfg = dmp_zz_heu_gcd(f, g, 6, ZZ) assert H == h and dmp_mul(H, cff, 6, ZZ) == f \ and dmp_mul(H, cfg, 6, ZZ) == g f, g, h = dmp_fateman_poly_F_1(8, ZZ) H, cff, cfg = dmp_zz_heu_gcd(f, g, 8, ZZ) assert H == h and dmp_mul(H, cff, 8, ZZ) == f \ and dmp_mul(H, cfg, 8, ZZ) == g f, g, h = dmp_fateman_poly_F_2(2, ZZ) H, cff, cfg = dmp_zz_heu_gcd(f, g, 2, ZZ) assert H == h and dmp_mul(H, cff, 2, ZZ) == f \ and dmp_mul(H, cfg, 2, ZZ) == g H, cff, cfg = dmp_rr_prs_gcd(f, g, 2, ZZ) assert H == h and dmp_mul(H, cff, 2, ZZ) == f \ and dmp_mul(H, cfg, 2, ZZ) == g f, g, h = dmp_fateman_poly_F_3(2, ZZ) H, cff, cfg = dmp_zz_heu_gcd(f, g, 2, ZZ) assert H == h and dmp_mul(H, cff, 2, ZZ) == f \ and dmp_mul(H, cfg, 2, ZZ) == g H, cff, cfg = dmp_rr_prs_gcd(f, g, 2, ZZ) assert H == h and dmp_mul(H, cff, 2, ZZ) == f \ and dmp_mul(H, cfg, 2, ZZ) == g f, g, h = dmp_fateman_poly_F_3(4, ZZ) H, cff, cfg = dmp_inner_gcd(f, g, 4, ZZ) assert H == h and dmp_mul(H, cff, 4, ZZ) == f \ and dmp_mul(H, cfg, 4, ZZ) == g f = [[QQ(1,2)],[QQ(1)],[QQ(1,2)]] g = [[QQ(1,2)],[QQ(1,2)]] h = [[QQ(1)],[QQ(1)]] assert dmp_qq_heu_gcd(f, g, 1, QQ) == (h, g, [[QQ(1,2)]]) assert dmp_ff_prs_gcd(f, g, 1, QQ) == (h, g, [[QQ(1,2)]]) f = [[RR(2.1), RR(-2.2), RR(2.1)], []] g = [[RR(1.0)], [], [], []] assert dmp_ff_prs_gcd(f, g, 1, RR) == \ ([[RR(1.0)], []], [[RR(2.1), RR(-2.2), RR(2.1)]], [[RR(1.0)], [], []]) def test_dup_lcm(): assert dup_lcm([2], [6], ZZ) == [6] assert dup_lcm([2,0,0,0], [6,0], ZZ) == [6,0,0,0] assert dup_lcm([2,0,0,0], [3,0], ZZ) == [6,0,0,0] assert dup_lcm([1,1,0], [1,0], ZZ) == [1,1,0] assert dup_lcm([1,1,0], [2,0], ZZ) == [2,2,0] assert dup_lcm([1,2,0], [1,0], ZZ) == [1,2,0] assert dup_lcm([2,1,0], [1,0], ZZ) == [2,1,0] assert dup_lcm([2,1,0], [2,0], ZZ) == [4,2,0] def test_dmp_lcm(): assert dmp_lcm([[2]], [[6]], 1, ZZ) == [[6]] assert dmp_lcm([[1],[]], [[1,0]], 1, ZZ) == [[1,0],[]] assert dmp_lcm([[2],[],[],[]], [[6,0,0],[]], 1, ZZ) == [[6,0,0],[],[],[]] assert dmp_lcm([[2],[],[],[]], [[3,0,0],[]], 1, ZZ) == [[6,0,0],[],[],[]] assert dmp_lcm([[1,0],[],[]], [[1,0,0],[]], 1, ZZ) == [[1,0,0],[],[]] f = [[2,-3,-2,3,0,0],[]] g = [[1,0,-2,0,1,0]] h = [[2,-3,-4,6,2,-3,0,0],[]] assert dmp_lcm(f, g, 1, ZZ) == h f = [[1],[-3,0],[-9,0,0],[-5,0,0,0]] g = [[1],[6,0],[12,0,0],[10,0,0,0],[3,0,0,0,0]] h = [[1],[1,0],[-18,0,0],[-50,0,0,0],[-47,0,0,0,0],[-15,0,0,0,0,0]] assert dmp_lcm(f, g, 1, ZZ) == h def test_dmp_content(): assert dmp_content([[-2]], 1, ZZ) == [2] f, g, F = [ZZ(3),ZZ(2),ZZ(1)], [ZZ(1)], [] for i in xrange(0, 5): g = dup_mul(g, f, ZZ) F.insert(0, g) assert dmp_content(F, 1, ZZ) == f assert dmp_one_p(dmp_content(f_4, 2, ZZ), 1, ZZ) assert dmp_one_p(dmp_content(f_5, 2, ZZ), 1, ZZ) assert dmp_one_p(dmp_content(f_6, 3, ZZ), 2, ZZ) def test_dmp_primitive(): assert dmp_primitive([[]], 1, ZZ) == ([], [[]]) assert dmp_primitive([[1]], 1, ZZ) == ([1], [[1]]) f, g, F = [ZZ(3),ZZ(2),ZZ(1)], [ZZ(1)], [] for i in xrange(0, 5): g = dup_mul(g, f, ZZ) F.insert(0, g) assert dmp_primitive(F, 1, ZZ) == (f, [ dup_exquo(c, f, ZZ) for c in F ]) cont, f = dmp_primitive(f_4, 2, ZZ) assert dmp_one_p(cont, 1, ZZ) and f == f_4 cont, f = dmp_primitive(f_5, 2, ZZ) assert dmp_one_p(cont, 1, ZZ) and f == f_5 cont, f = dmp_primitive(f_6, 3, ZZ) assert dmp_one_p(cont, 2, ZZ) and f == f_6 def test_dup_cancel(): f = ZZ.map([2, 0, -2]) g = ZZ.map([1, -2, 1]) p = [ZZ(2), ZZ(2)] q = [ZZ(1), -ZZ(1)] assert dup_cancel(f, g, ZZ) == (p, q) assert dup_cancel(f, g, ZZ, include=False) == (ZZ(1), ZZ(1), p, q) f = [-ZZ(1),-ZZ(2)] g = [ ZZ(3),-ZZ(4)] F = [ ZZ(1), ZZ(2)] G = [-ZZ(3), ZZ(4)] dup_cancel(f, g, ZZ) == (f, g) dup_cancel(F, G, ZZ) == (f, g) def test_dmp_cancel(): f = ZZ.map([[2], [0], [-2]]) g = ZZ.map([[1], [-2], [1]]) p = [[ZZ(2)], [ZZ(2)]] q = [[ZZ(1)], [-ZZ(1)]] assert dmp_cancel(f, g, 1, ZZ) == (p, q) assert dmp_cancel(f, g, 1, ZZ, include=False) == (ZZ(1), ZZ(1), p, q) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_numberfields.py0000644000175000017500000004260012014170666027501 0ustar georgeskgeorgesk"""Tests for computational algebraic number field theory. """ from sympy import S, Rational, Symbol, Poly, sin, sqrt, I, oo from sympy.utilities.pytest import raises from sympy.polys.numberfields import ( minimal_polynomial, primitive_element, is_isomorphism_possible, field_isomorphism_pslq, field_isomorphism, to_number_field, AlgebraicNumber, isolate, ) from sympy.polys.polyerrors import ( IsomorphismFailed, NotAlgebraic, ) from sympy.polys.polyclasses import DMP from sympy.polys.domains import QQ from sympy.abc import x, y Q = Rational def test_minimal_polynomial(): assert minimal_polynomial(-7, x) == x + 7 assert minimal_polynomial(-1, x) == x + 1 assert minimal_polynomial( 0, x) == x assert minimal_polynomial( 1, x) == x - 1 assert minimal_polynomial( 7, x) == x - 7 assert minimal_polynomial(sqrt(2), x) == x**2 - 2 assert minimal_polynomial(sqrt(5), x) == x**2 - 5 assert minimal_polynomial(sqrt(6), x) == x**2 - 6 assert minimal_polynomial(2*sqrt(2), x) == x**2 - 8 assert minimal_polynomial(3*sqrt(5), x) == x**2 - 45 assert minimal_polynomial(4*sqrt(6), x) == x**2 - 96 assert minimal_polynomial(2*sqrt(2) + 3, x) == x**2 - 6*x + 1 assert minimal_polynomial(3*sqrt(5) + 6, x) == x**2 - 12*x - 9 assert minimal_polynomial(4*sqrt(6) + 7, x) == x**2 - 14*x - 47 assert minimal_polynomial(2*sqrt(2) - 3, x) == x**2 + 6*x + 1 assert minimal_polynomial(3*sqrt(5) - 6, x) == x**2 + 12*x - 9 assert minimal_polynomial(4*sqrt(6) - 7, x) == x**2 + 14*x - 47 assert minimal_polynomial(sqrt(1 + sqrt(6)), x) == x**4 - 2*x**2 - 5 assert minimal_polynomial(sqrt(I + sqrt(6)), x) == x**8 - 10*x**4 + 49 assert minimal_polynomial(2*I + sqrt(2 + I), x) == x**4 + 4*x**2 + 8*x + 37 assert minimal_polynomial(sqrt(2) + sqrt(3), x) == x**4 - 10*x**2 + 1 assert minimal_polynomial(sqrt(2) + sqrt(3) + sqrt(6), x) == x**4 - 22*x**2 - 48*x - 23 a = 1 - 9*sqrt(2) + 7*sqrt(3) assert minimal_polynomial(1/a, x) == 392*x**4 - 1232*x**3 + 612*x**2 + 4*x - 1 assert minimal_polynomial(1/sqrt(a), x) == 392*x**8 - 1232*x**6 + 612*x**4 + 4*x**2 - 1 raises(NotAlgebraic, "minimal_polynomial(y, x)") raises(NotAlgebraic, "minimal_polynomial(oo, x)") raises(NotAlgebraic, "minimal_polynomial(2**y, x)") raises(NotAlgebraic, "minimal_polynomial(sin(1), x)") assert minimal_polynomial(sqrt(2)).dummy_eq(x**2 - 2) assert minimal_polynomial(sqrt(2), x) == x**2 - 2 assert minimal_polynomial(sqrt(2), polys=True) == Poly(x**2 - 2) assert minimal_polynomial(sqrt(2), x, polys=True) == Poly(x**2 - 2) a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(3)) assert minimal_polynomial(a, x) == x**2 - 2 assert minimal_polynomial(b, x) == x**2 - 3 assert minimal_polynomial(a, x, polys=True) == Poly(x**2 - 2) assert minimal_polynomial(b, x, polys=True) == Poly(x**2 - 3) assert minimal_polynomial(sqrt(a/2 + 17), x) == 2*x**4 - 68*x**2 + 577 assert minimal_polynomial(sqrt(b/2 + 17), x) == 4*x**4 - 136*x**2 + 1153 a, b = sqrt(2)/3 + 7, AlgebraicNumber(sqrt(2)/3 + 7) f = 81*x**8 - 2268*x**6 - 4536*x**5 + 22644*x**4 + 63216*x**3 - 31608*x**2 - 189648*x + 141358 assert minimal_polynomial(sqrt(a) + sqrt(sqrt(a)), x) == f assert minimal_polynomial(sqrt(b) + sqrt(sqrt(b)), x) == f assert minimal_polynomial(a**Rational(3, 2), x) == 729*x**4 - 506898*x**2 + 84604519 def test_primitive_element(): assert primitive_element([sqrt(2)], x) == (x**2 - 2, [1]) assert primitive_element([sqrt(2), sqrt(3)], x) == (x**4 - 10*x**2 + 1, [1, 1]) assert primitive_element([sqrt(2)], x, polys=True) == (Poly(x**2 - 2), [1]) assert primitive_element([sqrt(2), sqrt(3)], x, polys=True) == (Poly(x**4 - 10*x**2 + 1), [1, 1]) assert primitive_element([sqrt(2)], x, ex=True) == (x**2 - 2, [1], [[1, 0]]) assert primitive_element([sqrt(2), sqrt(3)], x, ex=True) == \ (x**4 - 10*x**2 + 1, [1, 1], [[Q(1,2), 0, -Q(9,2), 0], [-Q(1,2), 0, Q(11,2), 0]]) assert primitive_element([sqrt(2)], x, ex=True, polys=True) == (Poly(x**2 - 2), [1], [[1, 0]]) assert primitive_element([sqrt(2), sqrt(3)], x, ex=True, polys=True) == \ (Poly(x**4 - 10*x**2 + 1), [1, 1], [[Q(1,2), 0, -Q(9,2), 0], [-Q(1,2), 0, Q(11,2), 0]]) assert primitive_element([sqrt(2)], polys=True) == (Poly(x**2 - 2), [1]) raises(ValueError, "primitive_element([], x, ex=False)") raises(ValueError, "primitive_element([], x, ex=True)") def test_field_isomorphism_pslq(): a = AlgebraicNumber(I) b = AlgebraicNumber(I*sqrt(3)) raises(NotImplementedError, "field_isomorphism_pslq(a, b)") a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(3)) c = AlgebraicNumber(sqrt(7)) d = AlgebraicNumber(sqrt(2)+sqrt(3)) e = AlgebraicNumber(sqrt(2)+sqrt(3)+sqrt(7)) assert field_isomorphism_pslq(a, a) == [1, 0] assert field_isomorphism_pslq(a, b) == None assert field_isomorphism_pslq(a, c) == None assert field_isomorphism_pslq(a, d) == [Q(1,2), 0, -Q(9,2), 0] assert field_isomorphism_pslq(a, e) == [Q(1,80), 0, -Q(1,2), 0, Q(59,20), 0] assert field_isomorphism_pslq(b, a) == None assert field_isomorphism_pslq(b, b) == [1, 0] assert field_isomorphism_pslq(b, c) == None assert field_isomorphism_pslq(b, d) == [-Q(1,2), 0, Q(11,2), 0] assert field_isomorphism_pslq(b, e) == [-Q(3,640), 0, Q(67,320), 0, -Q(297,160), 0, Q(313,80), 0] assert field_isomorphism_pslq(c, a) == None assert field_isomorphism_pslq(c, b) == None assert field_isomorphism_pslq(c, c) == [1, 0] assert field_isomorphism_pslq(c, d) == None assert field_isomorphism_pslq(c, e) == [Q(3,640), 0, -Q(71,320), 0, Q(377,160), 0, -Q(469,80), 0] assert field_isomorphism_pslq(d, a) == None assert field_isomorphism_pslq(d, b) == None assert field_isomorphism_pslq(d, c) == None assert field_isomorphism_pslq(d, d) == [1, 0] assert field_isomorphism_pslq(d, e) == [-Q(3,640), 0, Q(71,320), 0, -Q(377,160), 0, Q(549,80), 0] assert field_isomorphism_pslq(e, a) == None assert field_isomorphism_pslq(e, b) == None assert field_isomorphism_pslq(e, c) == None assert field_isomorphism_pslq(e, d) == None assert field_isomorphism_pslq(e, e) == [1, 0] f = AlgebraicNumber(3*sqrt(2)+8*sqrt(7)-5) assert field_isomorphism_pslq(f, e) == [Q(3,80), 0, -Q(139,80), 0, Q(347,20), 0, -Q(761,20), -5] def test_field_isomorphism(): assert field_isomorphism(3, sqrt(2)) == [3] assert field_isomorphism( I*sqrt(3), I*sqrt(3)/2) == [ 2, 0] assert field_isomorphism(-I*sqrt(3), I*sqrt(3)/2) == [-2, 0] assert field_isomorphism( I*sqrt(3),-I*sqrt(3)/2) == [-2, 0] assert field_isomorphism(-I*sqrt(3),-I*sqrt(3)/2) == [ 2, 0] assert field_isomorphism( 2*I*sqrt(3)/7, 5*I*sqrt(3)/3) == [ S(6)/35, 0] assert field_isomorphism(-2*I*sqrt(3)/7, 5*I*sqrt(3)/3) == [-S(6)/35, 0] assert field_isomorphism( 2*I*sqrt(3)/7,-5*I*sqrt(3)/3) == [-S(6)/35, 0] assert field_isomorphism(-2*I*sqrt(3)/7,-5*I*sqrt(3)/3) == [ S(6)/35, 0] assert field_isomorphism( 2*I*sqrt(3)/7+27, 5*I*sqrt(3)/3) == [ S(6)/35, 27] assert field_isomorphism(-2*I*sqrt(3)/7+27, 5*I*sqrt(3)/3) == [-S(6)/35, 27] assert field_isomorphism( 2*I*sqrt(3)/7+27,-5*I*sqrt(3)/3) == [-S(6)/35, 27] assert field_isomorphism(-2*I*sqrt(3)/7+27,-5*I*sqrt(3)/3) == [ S(6)/35, 27] p = AlgebraicNumber( sqrt(2) + sqrt(3)) q = AlgebraicNumber(-sqrt(2) + sqrt(3)) r = AlgebraicNumber( sqrt(2) - sqrt(3)) s = AlgebraicNumber(-sqrt(2) - sqrt(3)) pos_coeffs = [ S(1)/2, S(0), -S(9)/2, S(0)] neg_coeffs = [-S(1)/2, S(0), S(9)/2, S(0)] a = AlgebraicNumber(sqrt(2)) assert is_isomorphism_possible(a, p) == True assert is_isomorphism_possible(a, q) == True assert is_isomorphism_possible(a, r) == True assert is_isomorphism_possible(a, s) == True assert field_isomorphism(a, p, fast=True) == pos_coeffs assert field_isomorphism(a, q, fast=True) == neg_coeffs assert field_isomorphism(a, r, fast=True) == pos_coeffs assert field_isomorphism(a, s, fast=True) == neg_coeffs assert field_isomorphism(a, p, fast=False) == pos_coeffs assert field_isomorphism(a, q, fast=False) == neg_coeffs assert field_isomorphism(a, r, fast=False) == pos_coeffs assert field_isomorphism(a, s, fast=False) == neg_coeffs a = AlgebraicNumber(-sqrt(2)) assert is_isomorphism_possible(a, p) == True assert is_isomorphism_possible(a, q) == True assert is_isomorphism_possible(a, r) == True assert is_isomorphism_possible(a, s) == True assert field_isomorphism(a, p, fast=True) == neg_coeffs assert field_isomorphism(a, q, fast=True) == pos_coeffs assert field_isomorphism(a, r, fast=True) == neg_coeffs assert field_isomorphism(a, s, fast=True) == pos_coeffs assert field_isomorphism(a, p, fast=False) == neg_coeffs assert field_isomorphism(a, q, fast=False) == pos_coeffs assert field_isomorphism(a, r, fast=False) == neg_coeffs assert field_isomorphism(a, s, fast=False) == pos_coeffs pos_coeffs = [ S(1)/2, S(0), -S(11)/2, S(0)] neg_coeffs = [-S(1)/2, S(0), S(11)/2, S(0)] a = AlgebraicNumber(sqrt(3)) assert is_isomorphism_possible(a, p) == True assert is_isomorphism_possible(a, q) == True assert is_isomorphism_possible(a, r) == True assert is_isomorphism_possible(a, s) == True assert field_isomorphism(a, p, fast=True) == neg_coeffs assert field_isomorphism(a, q, fast=True) == neg_coeffs assert field_isomorphism(a, r, fast=True) == pos_coeffs assert field_isomorphism(a, s, fast=True) == pos_coeffs assert field_isomorphism(a, p, fast=False) == neg_coeffs assert field_isomorphism(a, q, fast=False) == neg_coeffs assert field_isomorphism(a, r, fast=False) == pos_coeffs assert field_isomorphism(a, s, fast=False) == pos_coeffs a = AlgebraicNumber(-sqrt(3)) assert is_isomorphism_possible(a, p) == True assert is_isomorphism_possible(a, q) == True assert is_isomorphism_possible(a, r) == True assert is_isomorphism_possible(a, s) == True assert field_isomorphism(a, p, fast=True) == pos_coeffs assert field_isomorphism(a, q, fast=True) == pos_coeffs assert field_isomorphism(a, r, fast=True) == neg_coeffs assert field_isomorphism(a, s, fast=True) == neg_coeffs assert field_isomorphism(a, p, fast=False) == pos_coeffs assert field_isomorphism(a, q, fast=False) == pos_coeffs assert field_isomorphism(a, r, fast=False) == neg_coeffs assert field_isomorphism(a, s, fast=False) == neg_coeffs pos_coeffs = [ S(3)/2, S(0), -S(33)/2, -S(8)] neg_coeffs = [-S(3)/2, S(0), S(33)/2, -S(8)] a = AlgebraicNumber(3*sqrt(3)-8) assert is_isomorphism_possible(a, p) == True assert is_isomorphism_possible(a, q) == True assert is_isomorphism_possible(a, r) == True assert is_isomorphism_possible(a, s) == True assert field_isomorphism(a, p, fast=True) == neg_coeffs assert field_isomorphism(a, q, fast=True) == neg_coeffs assert field_isomorphism(a, r, fast=True) == pos_coeffs assert field_isomorphism(a, s, fast=True) == pos_coeffs assert field_isomorphism(a, p, fast=False) == neg_coeffs assert field_isomorphism(a, q, fast=False) == neg_coeffs assert field_isomorphism(a, r, fast=False) == pos_coeffs assert field_isomorphism(a, s, fast=False) == pos_coeffs a = AlgebraicNumber(3*sqrt(2)+2*sqrt(3)+1) pos_1_coeffs = [ S(1)/2, S(0), -S(5)/2, S(1)] neg_5_coeffs = [-S(5)/2, S(0), S(49)/2, S(1)] pos_5_coeffs = [ S(5)/2, S(0), -S(49)/2, S(1)] neg_1_coeffs = [-S(1)/2, S(0), S(5)/2, S(1)] assert is_isomorphism_possible(a, p) == True assert is_isomorphism_possible(a, q) == True assert is_isomorphism_possible(a, r) == True assert is_isomorphism_possible(a, s) == True assert field_isomorphism(a, p, fast=True) == pos_1_coeffs assert field_isomorphism(a, q, fast=True) == neg_5_coeffs assert field_isomorphism(a, r, fast=True) == pos_5_coeffs assert field_isomorphism(a, s, fast=True) == neg_1_coeffs assert field_isomorphism(a, p, fast=False) == pos_1_coeffs assert field_isomorphism(a, q, fast=False) == neg_5_coeffs assert field_isomorphism(a, r, fast=False) == pos_5_coeffs assert field_isomorphism(a, s, fast=False) == neg_1_coeffs a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(3)) c = AlgebraicNumber(sqrt(7)) assert is_isomorphism_possible(a, b) == True assert is_isomorphism_possible(b, a) == True assert is_isomorphism_possible(c, p) == False assert field_isomorphism(sqrt(2), sqrt(3), fast=True) is None assert field_isomorphism(sqrt(3), sqrt(2), fast=True) is None assert field_isomorphism(sqrt(2), sqrt(3), fast=False) is None assert field_isomorphism(sqrt(3), sqrt(2), fast=False) is None def test_to_number_field(): assert to_number_field(sqrt(2)) == AlgebraicNumber(sqrt(2)) assert to_number_field([sqrt(2), sqrt(3)]) == AlgebraicNumber(sqrt(2)+sqrt(3)) a = AlgebraicNumber(sqrt(2)+sqrt(3), [S(1)/2, S(0), -S(9)/2, S(0)]) assert to_number_field(sqrt(2), sqrt(2)+sqrt(3)) == a assert to_number_field(sqrt(2), AlgebraicNumber(sqrt(2)+sqrt(3))) == a raises(IsomorphismFailed, "to_number_field(sqrt(2), sqrt(3))") def test_AlgebraicNumber(): minpoly, root = x**2 - 2, sqrt(2) a = AlgebraicNumber(root, gen=x) assert a.rep == DMP([QQ(1),QQ(0)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased == False assert a.coeffs() == [S(1), S(0)] assert a.native_coeffs() == [QQ(1), QQ(0)] a = AlgebraicNumber(root, gen=x, alias='y') assert a.rep == DMP([QQ(1),QQ(0)], QQ) assert a.root == root assert a.alias == Symbol('y') assert a.minpoly == minpoly assert a.is_aliased == True a = AlgebraicNumber(root, gen=x, alias=Symbol('y')) assert a.rep == DMP([QQ(1),QQ(0)], QQ) assert a.root == root assert a.alias == Symbol('y') assert a.minpoly == minpoly assert a.is_aliased == True assert AlgebraicNumber(sqrt(2), []).rep == DMP([], QQ) assert AlgebraicNumber(sqrt(2), [8]).rep == DMP([QQ(8)], QQ) assert AlgebraicNumber(sqrt(2), [S(8)/3]).rep == DMP([QQ(8,3)], QQ) assert AlgebraicNumber(sqrt(2), [7, 3]).rep == DMP([QQ(7),QQ(3)], QQ) assert AlgebraicNumber(sqrt(2), [S(7)/9, S(3)/2]).rep == DMP([QQ(7,9),QQ(3,2)], QQ) assert AlgebraicNumber(sqrt(2), [1, 2, 3]).rep == DMP([QQ(2),QQ(5)], QQ) a = AlgebraicNumber(AlgebraicNumber(root, gen=x), [1,2]) assert a.rep == DMP([QQ(1),QQ(2)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased == False assert a.coeffs() == [S(1), S(2)] assert a.native_coeffs() == [QQ(1), QQ(2)] a = AlgebraicNumber((minpoly, root), [1,2]) assert a.rep == DMP([QQ(1),QQ(2)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased == False a = AlgebraicNumber((Poly(minpoly), root), [1,2]) assert a.rep == DMP([QQ(1),QQ(2)], QQ) assert a.root == root assert a.alias is None assert a.minpoly == minpoly assert a.is_aliased == False assert AlgebraicNumber( sqrt(3)).rep == DMP([ QQ(1),QQ(0)], QQ) assert AlgebraicNumber(-sqrt(3)).rep == DMP([-QQ(1),QQ(0)], QQ) a = AlgebraicNumber(sqrt(2)) b = AlgebraicNumber(sqrt(2)) assert a == b and a == sqrt(2) a = AlgebraicNumber(sqrt(2), gen=x) b = AlgebraicNumber(sqrt(2), gen=x) assert a == b and a == sqrt(2) a = AlgebraicNumber(sqrt(2), [1,2]) b = AlgebraicNumber(sqrt(2), [1,3]) assert a != b and a != sqrt(2)+3 assert (a == x) == False and (a != x) == True a = AlgebraicNumber(sqrt(2), [1,0]) b = AlgebraicNumber(sqrt(2), [1,0], alias=y) assert a.as_poly(x) == Poly(x) assert b.as_poly() == Poly(y) assert a.as_expr() == sqrt(2) assert a.as_expr(x) == x assert b.as_expr() == sqrt(2) assert b.as_expr(x) == x a = AlgebraicNumber(sqrt(2), [2,3]) b = AlgebraicNumber(sqrt(2), [2,3], alias=y) p = a.as_poly() assert p == Poly(2*p.gen+3) assert a.as_poly(x) == Poly(2*x+3) assert b.as_poly() == Poly(2*y+3) assert a.as_expr() == 2*sqrt(2)+3 assert a.as_expr(x) == 2*x+3 assert b.as_expr() == 2*sqrt(2)+3 assert b.as_expr(x) == 2*x+3 def test_to_algebraic_integer(): a = AlgebraicNumber(sqrt(3), gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 3 assert a.root == sqrt(3) assert a.rep == DMP([QQ(1),QQ(0)], QQ) a = AlgebraicNumber(2*sqrt(3), gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 12 assert a.root == 2*sqrt(3) assert a.rep == DMP([QQ(1),QQ(0)], QQ) a = AlgebraicNumber(sqrt(3)/2, gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 12 assert a.root == 2*sqrt(3) assert a.rep == DMP([QQ(1),QQ(0)], QQ) a = AlgebraicNumber(sqrt(3)/2, [S(7)/19, 3], gen=x).to_algebraic_integer() assert a.minpoly == x**2 - 12 assert a.root == 2*sqrt(3) assert a.rep == DMP([QQ(7,19),QQ(3)], QQ) def test_isolate(): assert isolate(1) == (1, 1) assert isolate(S(1)/2) == (S(1)/2, S(1)/2) assert isolate(sqrt(2)) == (1, 2) assert isolate(-sqrt(2)) == (-2, -1) assert isolate(sqrt(2), eps=S(1)/100) == (S(24)/17, S(17)/12) assert isolate(-sqrt(2), eps=S(1)/100) == (-S(17)/12, -S(24)/17) raises(NotImplementedError, "isolate(I)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_sqfreetools.py0000644000175000017500000001177512014170666027401 0ustar georgeskgeorgesk"""Tests for square-free decomposition algorithms and related tools. """ from sympy.polys.sqfreetools import ( dup_sqf_p, dmp_sqf_p, dup_sqf_norm, dmp_sqf_norm, dup_sqf_part, dmp_sqf_part, dup_sqf_list, dup_sqf_list_include, dmp_sqf_list, dmp_sqf_list_include, dup_gff_list, dmp_gff_list) from sympy.polys.euclidtools import ( dmp_resultant) from sympy.polys.densearith import ( dmp_neg, dmp_sub, dmp_mul, dmp_sqr) from sympy.polys.densetools import ( dmp_diff) from sympy.polys.polyclasses import ( DMP ) from sympy.polys.polyerrors import ( DomainError) from sympy.polys.specialpolys import ( f_0, f_1, f_2, f_3, f_4, f_5, f_6) from sympy.polys.domains import FF, ZZ, QQ from sympy.abc import x from sympy.utilities.pytest import raises def test_dup_sqf(): assert dup_sqf_part([], ZZ) == [] assert dup_sqf_p([], ZZ) == True assert dup_sqf_part([7], ZZ) == [1] assert dup_sqf_p([7], ZZ) == True assert dup_sqf_part([2,2], ZZ) == [1,1] assert dup_sqf_p([2,2], ZZ) == True assert dup_sqf_part([1,0,1,1], ZZ) == [1,0,1,1] assert dup_sqf_p([1,0,1,1], ZZ) == True assert dup_sqf_part([-1,0,1,1], ZZ) == [1,0,-1,-1] assert dup_sqf_p([-1,0,1,1], ZZ) == True assert dup_sqf_part([2,3,0,0], ZZ) == [2,3,0] assert dup_sqf_p([2,3,0,0], ZZ) == False assert dup_sqf_part([-2,3,0,0], ZZ) == [2,-3,0] assert dup_sqf_p([-2,3,0,0], ZZ) == False assert dup_sqf_list([], ZZ) == (0, []) assert dup_sqf_list([1], ZZ) == (1, []) assert dup_sqf_list([1,0], ZZ) == (1, [([1,0], 1)]) assert dup_sqf_list([2,0,0], ZZ) == (2, [([1,0], 2)]) assert dup_sqf_list([3,0,0,0], ZZ) == (3, [([1,0], 3)]) assert dup_sqf_list([ZZ(2),ZZ(4),ZZ(2)], ZZ) == \ (ZZ(2), [([ZZ(1),ZZ(1)], 2)]) assert dup_sqf_list([QQ(2),QQ(4),QQ(2)], QQ) == \ (QQ(2), [([QQ(1),QQ(1)], 2)]) assert dup_sqf_list([-1,1,0,0,1,-1], ZZ) == \ (-1, [([1,1,1,1], 1), ([1,-1], 2)]) assert dup_sqf_list([1,0,6,0,12,0,8,0,0], ZZ) == \ (1, [([1,0], 2), ([1,0,2], 3)]) K = FF(2) f = map(K, [1,0,1]) assert dup_sqf_list(f, K) == \ (K(1), [([K(1),K(1)], 2)]) K = FF(3) f = map(K, [1,0,0,2,0,0,2,0,0,1,0]) assert dup_sqf_list(f, K) == \ (K(1), [([K(1), K(0)], 1), ([K(1), K(1)], 3), ([K(1), K(2)], 6)]) f = [1,0,0,1] g = map(K, f) assert dup_sqf_part(f, ZZ) == f assert dup_sqf_part(g, K) == [K(1), K(1)] assert dup_sqf_p(f, ZZ) == True assert dup_sqf_p(g, K) == False A = [[1],[],[-3],[],[6]] D = [[1],[],[-5],[],[5],[],[4]] f, g = D, dmp_sub(A, dmp_mul(dmp_diff(D, 1, 1, ZZ), [[1,0]], 1, ZZ), 1, ZZ) res = dmp_resultant(f, g, 1, ZZ) assert dup_sqf_list(res, ZZ) == (45796, [([4,0,1], 3)]) assert dup_sqf_list_include([DMP([1, 0, 0, 0], ZZ), DMP([], ZZ), DMP([], ZZ)], ZZ[x]) == \ [([DMP([1, 0, 0, 0], ZZ)], 1), ([DMP([1], ZZ), DMP([], ZZ)], 2)] def test_dmp_sqf(): assert dmp_sqf_part([[]], 1, ZZ) == [[]] assert dmp_sqf_p([[]], 1, ZZ) == True assert dmp_sqf_part([[7]], 1, ZZ) == [[1]] assert dmp_sqf_p([[7]], 1, ZZ) == True assert dmp_sqf_p(f_0, 2, ZZ) == True assert dmp_sqf_p(dmp_sqr(f_0, 2, ZZ), 2, ZZ) == False assert dmp_sqf_p(f_1, 2, ZZ) == True assert dmp_sqf_p(dmp_sqr(f_1, 2, ZZ), 2, ZZ) == False assert dmp_sqf_p(f_2, 2, ZZ) == True assert dmp_sqf_p(dmp_sqr(f_2, 2, ZZ), 2, ZZ) == False assert dmp_sqf_p(f_3, 2, ZZ) == True assert dmp_sqf_p(dmp_sqr(f_3, 2, ZZ), 2, ZZ) == False assert dmp_sqf_p(f_5, 2, ZZ) == False assert dmp_sqf_p(dmp_sqr(f_5, 2, ZZ), 2, ZZ) == False assert dmp_sqf_p(f_4, 2, ZZ) == True assert dmp_sqf_part(f_4, 2, ZZ) == dmp_neg(f_4, 2, ZZ) assert dmp_sqf_p(f_6, 3, ZZ) == True assert dmp_sqf_part(f_6, 3, ZZ) == f_6 assert dmp_sqf_part(f_5, 2, ZZ) == [[[1]], [[1], [-1, 0]]] assert dup_sqf_list([], ZZ) == (ZZ(0), []) assert dup_sqf_list_include([], ZZ) == [([], 1)] assert dmp_sqf_list([[ZZ(3)]], 1, ZZ) == (ZZ(3), []) assert dmp_sqf_list_include([[ZZ(3)]], 1, ZZ) == [([[ZZ(3)]], 1)] f = [-1,1,0,0,1,-1] assert dmp_sqf_list(f, 0, ZZ) == \ (-1, [([1,1,1,1], 1), ([1,-1], 2)]) assert dmp_sqf_list_include(f, 0, ZZ) == \ [([-1,-1,-1,-1], 1), ([1,-1], 2)] f = [[-1],[1],[],[],[1],[-1]] assert dmp_sqf_list(f, 1, ZZ) == \ (-1, [([[1],[1],[1],[1]], 1), ([[1],[-1]], 2)]) assert dmp_sqf_list_include(f, 1, ZZ) == \ [([[-1],[-1],[-1],[-1]], 1), ([[1],[-1]], 2)] K = FF(2) f = [[-1], [2], [-1]] assert dmp_sqf_list_include(f, 1, ZZ) == \ [([[-1]], 1), ([[1], [-1]], 2)] raises(DomainError, "dmp_sqf_list([[K(1), K(0), K(1)]], 1, K)") def test_dup_gff_list(): f = [1, 2, -1, -2, 0, 0] assert dup_gff_list(f, ZZ) == [([1, 0], 1), ([1, 2], 4)] g = [1, -20, 166, -744, 1965, -3132, 2948, -1504, 320, 0] assert dup_gff_list(g, ZZ) == [([1, -5, 4], 1), ([1, -5, 4], 2), ([1, 0], 3)] raises(ValueError, "dup_gff_list([], ZZ)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_densebasic.py0000644000175000017500000005031412014170666027123 0ustar georgeskgeorgesk"""Tests for dense recursive polynomials' basic tools. """ from sympy.polys.densebasic import ( dup_LC, dmp_LC, dup_TC, dmp_TC, dmp_ground_LC, dmp_ground_TC, dmp_true_LT, dup_degree, dmp_degree, dmp_degree_in, dmp_degree_list, dup_strip, dmp_strip, dmp_validate, dup_reverse, dup_copy, dmp_copy, dup_normal, dmp_normal, dup_convert, dmp_convert, dup_from_sympy, dmp_from_sympy, dup_nth, dmp_nth, dmp_ground_nth, dmp_zero_p, dmp_zero, dmp_one_p, dmp_one, dmp_ground_p, dmp_ground, dmp_negative_p, dmp_positive_p, dmp_zeros, dmp_grounds, dup_from_dict, dup_from_raw_dict, dup_to_dict, dup_to_raw_dict, dmp_from_dict, dmp_to_dict, dmp_swap, dmp_permute, dmp_nest, dmp_raise, dup_deflate, dmp_deflate, dup_multi_deflate, dmp_multi_deflate, dup_inflate, dmp_inflate, dmp_exclude, dmp_include, dmp_inject, dmp_eject, dup_terms_gcd, dmp_terms_gcd, dmp_list_terms, dmp_apply_pairs, dup_slice, dmp_slice, dmp_slice_in, dup_random, ) from sympy.polys.specialpolys import ( f_0, f_1, f_2, f_3, f_4, f_5, f_6 ) from sympy.polys.polyclasses import DMP from sympy.polys.domains import ZZ, QQ from sympy.core.singleton import S from sympy.utilities.pytest import raises def test_dup_LC(): assert dup_LC([], ZZ) == 0 assert dup_LC([2,3,4,5], ZZ) == 2 def test_dup_TC(): assert dup_TC([], ZZ) == 0 assert dup_TC([2,3,4,5], ZZ) == 5 def test_dmp_LC(): assert dmp_LC([[]], ZZ) == [] assert dmp_LC([[2,3,4],[5]], ZZ) == [2,3,4] assert dmp_LC([[[]]], ZZ) == [[]] assert dmp_LC([[[2],[3,4]],[[5]]], ZZ) == [[2],[3,4]] def test_dmp_TC(): assert dmp_TC([[]], ZZ) == [] assert dmp_TC([[2,3,4],[5]], ZZ) == [5] assert dmp_TC([[[]]], ZZ) == [[]] assert dmp_TC([[[2],[3,4]],[[5]]], ZZ) == [[5]] def test_dmp_ground_LC(): assert dmp_ground_LC([[]], 1, ZZ) == 0 assert dmp_ground_LC([[2,3,4],[5]], 1, ZZ) == 2 assert dmp_ground_LC([[[]]], 2, ZZ) == 0 assert dmp_ground_LC([[[2],[3,4]],[[5]]], 2, ZZ) == 2 def test_dmp_ground_TC(): assert dmp_ground_TC([[]], 1, ZZ) == 0 assert dmp_ground_TC([[2,3,4],[5]], 1, ZZ) == 5 assert dmp_ground_TC([[[]]], 2, ZZ) == 0 assert dmp_ground_TC([[[2],[3,4]],[[5]]], 2, ZZ) == 5 def test_dmp_true_LT(): assert dmp_true_LT([[]], 1, ZZ) == ((0, 0), 0) assert dmp_true_LT([[7]], 1, ZZ) == ((0, 0), 7) assert dmp_true_LT([[1,0]], 1, ZZ) == ((0, 1), 1) assert dmp_true_LT([[1],[]], 1, ZZ) == ((1, 0), 1) assert dmp_true_LT([[1,0],[]], 1, ZZ) == ((1, 1), 1) def test_dup_degree(): assert dup_degree([]) == -1 assert dup_degree([1]) == 0 assert dup_degree([1,0]) == 1 assert dup_degree([1,0,0,0,1]) == 4 def test_dmp_degree(): assert dmp_degree([[]], 1) == -1 assert dmp_degree([[[]]], 2) == -1 assert dmp_degree([[1]], 1) == 0 assert dmp_degree([[2],[1]], 1) == 1 def test_dmp_degree_in(): assert dmp_degree_in([[[]]], 0, 2) == -1 assert dmp_degree_in([[[]]], 1, 2) == -1 assert dmp_degree_in([[[]]], 2, 2) == -1 assert dmp_degree_in([[[1]]], 0, 2) == 0 assert dmp_degree_in([[[1]]], 1, 2) == 0 assert dmp_degree_in([[[1]]], 2, 2) == 0 assert dmp_degree_in(f_4, 0, 2) == 9 assert dmp_degree_in(f_4, 1, 2) == 12 assert dmp_degree_in(f_4, 2, 2) == 8 assert dmp_degree_in(f_6, 0, 2) == 4 assert dmp_degree_in(f_6, 1, 2) == 4 assert dmp_degree_in(f_6, 2, 2) == 6 assert dmp_degree_in(f_6, 3, 3) == 3 raises(IndexError, "dmp_degree_in([[1]], -5, 1)") def test_dmp_degree_list(): assert dmp_degree_list([[[[ ]]]], 3) == (-1,-1,-1,-1) assert dmp_degree_list([[[[1]]]], 3) == ( 0, 0, 0, 0) assert dmp_degree_list(f_0, 2) == (2, 2, 2) assert dmp_degree_list(f_1, 2) == (3, 3, 3) assert dmp_degree_list(f_2, 2) == (5, 3, 3) assert dmp_degree_list(f_3, 2) == (5, 4, 7) assert dmp_degree_list(f_4, 2) == (9, 12, 8) assert dmp_degree_list(f_5, 2) == (3, 3, 3) assert dmp_degree_list(f_6, 3) == (4, 4, 6, 3) def test_dup_strip(): assert dup_strip([]) == [] assert dup_strip([0]) == [] assert dup_strip([0,0,0]) == [] assert dup_strip([1]) == [1] assert dup_strip([0,1]) == [1] assert dup_strip([0,0,0,1]) == [1] assert dup_strip([1,2,0]) == [1,2,0] assert dup_strip([0,1,2,0]) == [1,2,0] assert dup_strip([0,0,0,1,2,0]) == [1,2,0] def test_dmp_strip(): assert dmp_strip([0,1,0], 0) == [1,0] assert dmp_strip([[]], 1) == [[]] assert dmp_strip([[], []], 1) == [[]] assert dmp_strip([[], [], []], 1) == [[]] assert dmp_strip([[[]]], 2) == [[[]]] assert dmp_strip([[[]], [[]]], 2) == [[[]]] assert dmp_strip([[[]], [[]], [[]]], 2) == [[[]]] assert dmp_strip([[[1]]], 2) == [[[1]]] assert dmp_strip([[[]], [[1]]], 2) == [[[1]]] assert dmp_strip([[[]], [[1]], [[]]], 2) == [[[1]], [[]]] def test_dmp_validate(): assert dmp_validate([]) == ([], 0) assert dmp_validate([0,0,0,1,0]) == ([1,0], 0) assert dmp_validate([[[]]]) == ([[[]]], 2) assert dmp_validate([[0],[],[0],[1],[0]]) == ([[1],[]], 1) raises(ValueError, 'dmp_validate([[0],0,[0],[1],[0]])') def test_dup_reverse(): assert dup_reverse([1,2,0,3]) == [3,0,2,1] assert dup_reverse([1,2,3,0]) == [3,2,1] def test_dup_copy(): f = [ZZ(1),ZZ(0),ZZ(2)] g = dup_copy(f) g[0], g[2] = ZZ(7), ZZ(0) assert f != g def test_dmp_copy(): f = [[ZZ(1)],[ZZ(2),ZZ(0)]] g = dmp_copy(f, 1) g[0][0], g[1][1] = ZZ(7), ZZ(1) assert f != g def test_dup_normal(): assert dup_normal([0,0,2,1,0,11,0], ZZ) == \ [ZZ(2),ZZ(1),ZZ(0),ZZ(11),ZZ(0)] def test_dmp_normal(): assert dmp_normal([[0],[],[0,2,1],[0],[11],[]], 1, ZZ) == \ [[ZZ(2),ZZ(1)],[],[ZZ(11)],[]] def test_dup_convert(): K0, K1 = ZZ['x'], ZZ f = [DMP([1], ZZ),DMP([2], ZZ),DMP([], ZZ),DMP([3], ZZ)] assert dup_convert(f, K0, K1) == \ [ZZ(1),ZZ(2),ZZ(0),ZZ(3)] def test_dmp_convert(): K0, K1 = ZZ['x'], ZZ f = [[DMP([1], ZZ)],[DMP([2], ZZ)],[],[DMP([3], ZZ)]] assert dmp_convert(f, 1, K0, K1) == \ [[ZZ(1)],[ZZ(2)],[],[ZZ(3)]] def test_dup_from_sympy(): assert dup_from_sympy([S(1), S(2)], ZZ) == \ [ZZ(1), ZZ(2)] assert dup_from_sympy([S(1)/2, S(3)], QQ) == \ [QQ(1, 2), QQ(3, 1)] def test_dmp_from_sympy(): assert dmp_from_sympy([[S(1), S(2)], [S(0)]], 1, ZZ) == \ [[ZZ(1), ZZ(2)], []] assert dmp_from_sympy([[S(1)/2, S(2)]], 1, QQ) == \ [[QQ(1, 2), QQ(2, 1)]] def test_dup_nth(): assert dup_nth([1,2,3], 0, ZZ) == 3 assert dup_nth([1,2,3], 1, ZZ) == 2 assert dup_nth([1,2,3], 2, ZZ) == 1 assert dup_nth([1,2,3], 9, ZZ) == 0 raises(IndexError, 'dup_nth([3,4,5], -1, ZZ)') def test_dmp_nth(): assert dmp_nth([[1],[2],[3]], 0, 1, ZZ) == [3] assert dmp_nth([[1],[2],[3]], 1, 1, ZZ) == [2] assert dmp_nth([[1],[2],[3]], 2, 1, ZZ) == [1] assert dmp_nth([[1],[2],[3]], 9, 1, ZZ) == [] raises(IndexError, 'dmp_nth([[3],[4],[5]], -1, 1, ZZ)') def test_dmp_ground_nth(): assert dmp_ground_nth([[1],[2],[3]], (0,0), 1, ZZ) == 3 assert dmp_ground_nth([[1],[2],[3]], (1,0), 1, ZZ) == 2 assert dmp_ground_nth([[1],[2],[3]], (2,0), 1, ZZ) == 1 assert dmp_ground_nth([[1],[2],[3]], (2,1), 1, ZZ) == 0 assert dmp_ground_nth([[1],[2],[3]], (3,0), 1, ZZ) == 0 raises(IndexError, 'dmp_ground_nth([[3],[4],[5]], (2,-1), 1, ZZ)') def test_dmp_zero_p(): assert dmp_zero_p([], 0) == True assert dmp_zero_p([[]], 1) == True assert dmp_zero_p([[[]]], 2) == True assert dmp_zero_p([[[1]]], 2) == False def test_dmp_zero(): assert dmp_zero(0) == [] assert dmp_zero(2) == [[[]]] def test_dmp_one_p(): assert dmp_one_p([1], 0, ZZ) == True assert dmp_one_p([[1]], 1, ZZ) == True assert dmp_one_p([[[1]]], 2, ZZ) == True assert dmp_one_p([[[12]]], 2, ZZ) == False def test_dmp_one(): assert dmp_one(0, ZZ) == [ZZ(1)] assert dmp_one(2, ZZ) == [[[ZZ(1)]]] def test_dmp_ground_p(): assert dmp_ground_p([], 0, 0) == True assert dmp_ground_p([[]], 0, 1) == True assert dmp_ground_p([[]], 1, 1) == False assert dmp_ground_p([[ZZ(1)]], 1, 1) == True assert dmp_ground_p([[[ZZ(2)]]], 2, 2) == True assert dmp_ground_p([[[ZZ(2)]]], 3, 2) == False assert dmp_ground_p([[[ZZ(3)], []]], 3, 2) == False assert dmp_ground_p([], None, 0) == True assert dmp_ground_p([[]], None, 1) == True assert dmp_ground_p([ZZ(1)], None, 0) == True assert dmp_ground_p([[[ZZ(1)]]], None, 2) == True assert dmp_ground_p([[[ZZ(3)], []]], None, 2) == False def test_dmp_ground(): assert dmp_ground(ZZ(0), 2) == [[[]]] assert dmp_ground(ZZ(7),-1) == ZZ(7) assert dmp_ground(ZZ(7), 0) == [ZZ(7)] assert dmp_ground(ZZ(7), 2) == [[[ZZ(7)]]] def test_dmp_zeros(): assert dmp_zeros(4, 0, ZZ) == [[], [], [], []] assert dmp_zeros(0, 2, ZZ) == [] assert dmp_zeros(1, 2, ZZ) == [[[[]]]] assert dmp_zeros(2, 2, ZZ) == [[[[]]], [[[]]]] assert dmp_zeros(3, 2, ZZ) == [[[[]]], [[[]]], [[[]]]] assert dmp_zeros(3, -1, ZZ) == [0, 0, 0] def test_dmp_grounds(): assert dmp_grounds(ZZ(7), 0, 2) == [] assert dmp_grounds(ZZ(7), 1, 2) == [[[[7]]]] assert dmp_grounds(ZZ(7), 2, 2) == [[[[7]]], [[[7]]]] assert dmp_grounds(ZZ(7), 3, 2) == [[[[7]]], [[[7]]], [[[7]]]] assert dmp_grounds(ZZ(7), 3, -1) == [7, 7, 7] def test_dmp_negative_p(): assert dmp_negative_p([[[]]], 2, ZZ) == False assert dmp_negative_p([[[1], [2]]], 2, ZZ) == False assert dmp_negative_p([[[-1], [2]]], 2, ZZ) == True def test_dmp_positive_p(): assert dmp_positive_p([[[]]], 2, ZZ) == False assert dmp_positive_p([[[1], [2]]], 2, ZZ) == True assert dmp_positive_p([[[-1], [2]]], 2, ZZ) == False def test_dup_from_to_dict(): assert dup_from_raw_dict({}, ZZ) == [] assert dup_from_dict({}, ZZ) == [] assert dup_to_raw_dict([]) == {} assert dup_to_dict([]) == {} assert dup_to_raw_dict([], ZZ, zero=True) == {0: ZZ(0)} assert dup_to_dict([], ZZ, zero=True) == {(0,): ZZ(0)} f = [3,0,0,2,0,0,0,0,8] g = {8: 3, 5: 2, 0: 8} h = {(8,): 3, (5,): 2, (0,): 8} assert dup_from_raw_dict(g, ZZ) == f assert dup_from_dict(h, ZZ) == f assert dup_to_raw_dict(f) == g assert dup_to_dict(f) == h K = ZZ['x','y'] f = [K([[3]]),K([[]]),K([[2]]),K([[]]),K([[]]),K([[8]])] g = {5: K([[3]]), 3: K([[2]]), 0: K([[8]])} h = {(5,): K([[3]]), (3,): K([[2]]), (0,): K([[8]])} assert dup_from_raw_dict(g, K) == f assert dup_from_dict(h, K) == f assert dup_to_raw_dict(f) == g assert dup_to_dict(f) == h def test_dmp_from_to_dict(): assert dmp_from_dict({}, 1, ZZ) == [[]] assert dmp_to_dict([[]], 1) == {} assert dmp_to_dict([], 0, ZZ, zero=True) == {(0,): ZZ(0)} assert dmp_to_dict([[]], 1, ZZ, zero=True) == {(0,0): ZZ(0)} f = [[3],[],[],[2],[],[],[],[],[8]] g = {(8,0): 3, (5,0): 2, (0,0): 8} assert dmp_from_dict(g, 1, ZZ) == f assert dmp_to_dict(f, 1) == g def test_dmp_swap(): f = dmp_normal([[1,0,0],[],[1,0],[],[1]], 1, ZZ) g = dmp_normal([[1,0,0,0,0],[1,0,0],[1]], 1, ZZ) assert dmp_swap(f, 1, 1, 1, ZZ) == f assert dmp_swap(f, 0, 1, 1, ZZ) == g assert dmp_swap(g, 0, 1, 1, ZZ) == f raises(IndexError, "dmp_swap(f, -1, -7, 1, ZZ)") def test_dmp_permute(): f = dmp_normal([[1,0,0],[],[1,0],[],[1]], 1, ZZ) g = dmp_normal([[1,0,0,0,0],[1,0,0],[1]], 1, ZZ) assert dmp_permute(f, [0, 1], 1, ZZ) == f assert dmp_permute(g, [0, 1], 1, ZZ) == g assert dmp_permute(f, [1, 0], 1, ZZ) == g assert dmp_permute(g, [1, 0], 1, ZZ) == f def test_dmp_nest(): assert dmp_nest(ZZ(1), 2, ZZ) == [[[1]]] assert dmp_nest([[1]], 0, ZZ) == [[1]] assert dmp_nest([[1]], 1, ZZ) == [[[1]]] assert dmp_nest([[1]], 2, ZZ) == [[[[1]]]] def test_dmp_raise(): assert dmp_raise([], 2, 0, ZZ) == [[[]]] assert dmp_raise([[1]], 0, 1, ZZ) == [[1]] assert dmp_raise([[1,2,3], [], [2,3]], 2, 1, ZZ) == \ [[[[1]],[[2]],[[3]]], [[[]]], [[[2]],[[3]]]] def test_dup_deflate(): assert dup_deflate([], ZZ) == (1, []) assert dup_deflate([2], ZZ) == (1, [2]) assert dup_deflate([1,2,3], ZZ) == (1, [1,2,3]) assert dup_deflate([1,0,2,0,3], ZZ) == (2, [1,2,3]) assert dup_deflate(dup_from_raw_dict({7:1,1:1}, ZZ), ZZ) == \ (1, [1, 0, 0, 0, 0, 0, 1, 0]) assert dup_deflate(dup_from_raw_dict({7:1,0:1}, ZZ), ZZ) == \ (7, [1, 1]) assert dup_deflate(dup_from_raw_dict({7:1,3:1}, ZZ), ZZ) == \ (1, [1, 0, 0, 0, 1, 0, 0, 0]) assert dup_deflate(dup_from_raw_dict({7:1,4:1}, ZZ), ZZ) == \ (1, [1, 0, 0, 1, 0, 0, 0, 0]) assert dup_deflate(dup_from_raw_dict({8:1,4:1}, ZZ), ZZ) == \ (4, [1, 1, 0]) assert dup_deflate(dup_from_raw_dict({8:1}, ZZ), ZZ) == \ (8, [1, 0]) assert dup_deflate(dup_from_raw_dict({7:1}, ZZ), ZZ) == \ (7, [1, 0]) assert dup_deflate(dup_from_raw_dict({1:1}, ZZ), ZZ) == \ (1, [1, 0]) def test_dmp_deflate(): assert dmp_deflate([[]], 1, ZZ) == ((1, 1), [[]]) assert dmp_deflate([[2]], 1, ZZ) == ((1, 1), [[2]]) f = [[1, 0, 0], [], [1, 0], [], [1]] assert dmp_deflate(f, 1, ZZ) == ((2, 1), [[1, 0, 0], [1, 0], [1]]) def test_dup_multi_deflate(): assert dup_multi_deflate(([2],), ZZ) == (1, ([2],)) assert dup_multi_deflate(([], []), ZZ) == (1, ([], [])) assert dup_multi_deflate(([1,2,3],), ZZ) == (1, ([1,2,3],)) assert dup_multi_deflate(([1,0,2,0,3],), ZZ) == (2, ([1,2,3],)) assert dup_multi_deflate(([1,0,2,0,3], [2,0,0]), ZZ) == \ (2, ([1,2,3], [2,0])) assert dup_multi_deflate(([1,0,2,0,3], [2,1,0]), ZZ) == \ (1, ([1,0,2,0,3], [2,1,0])) def test_dmp_multi_deflate(): assert dmp_multi_deflate(([[]],), 1, ZZ) == \ ((1, 1), ([[]],)) assert dmp_multi_deflate(([[]], [[]]), 1, ZZ) == \ ((1, 1), ([[]], [[]])) assert dmp_multi_deflate(([[1]], [[]]), 1, ZZ) == \ ((1, 1), ([[1]], [[]])) assert dmp_multi_deflate(([[1]], [[2]]), 1, ZZ) == \ ((1, 1), ([[1]], [[2]])) assert dmp_multi_deflate(([[1]], [[2,0]]), 1, ZZ) == \ ((1, 1), ([[1]], [[2, 0]])) assert dmp_multi_deflate(([[2,0]], [[2,0]]), 1, ZZ) == \ ((1, 1), ([[2, 0]], [[2, 0]])) assert dmp_multi_deflate(([[2]], [[2,0,0]]), 1, ZZ) == ((1, 2), ([[2]], [[2, 0]])) assert dmp_multi_deflate(([[2,0,0]], [[2,0,0]]), 1, ZZ) == ((1, 2), ([[2, 0]], [[2, 0]])) assert dmp_multi_deflate(([2,0,0], [1,0,4,0,1]), 0, ZZ) == \ ((2,), ([2, 0], [1, 4, 1])) f = [[1, 0, 0], [], [1, 0], [], [1]] g = [[1, 0, 1, 0], [], [1]] assert dmp_multi_deflate((f,), 1, ZZ) == \ ((2, 1), ([[1, 0, 0], [1, 0], [1]],)) assert dmp_multi_deflate((f, g), 1, ZZ) == \ ((2, 1), ([[1, 0, 0], [1, 0], [1]], [[1, 0, 1, 0], [1]])) def test_dup_inflate(): assert dup_inflate([], 17, ZZ) == [] assert dup_inflate([1,2,3], 1, ZZ) == [1,2,3] assert dup_inflate([1,2,3], 2, ZZ) == [1,0,2,0,3] assert dup_inflate([1,2,3], 3, ZZ) == [1,0,0,2,0,0,3] assert dup_inflate([1,2,3], 4, ZZ) == [1,0,0,0,2,0,0,0,3] raises(IndexError, 'dup_inflate([1,2,3], 0, ZZ)') def test_dmp_inflate(): assert dmp_inflate([1], (3,), 0, ZZ) == [1] assert dmp_inflate([[]], (3, 7), 1, ZZ) == [[]] assert dmp_inflate([[2]], (1, 2), 1, ZZ) == [[2]] assert dmp_inflate([[2,0]], (1, 1), 1, ZZ) == [[2,0]] assert dmp_inflate([[2,0]], (1, 2), 1, ZZ) == [[2,0,0]] assert dmp_inflate([[2,0]], (1, 3), 1, ZZ) == [[2,0,0,0]] assert dmp_inflate([[1, 0, 0], [1], [1, 0]], (2, 1), 1, ZZ) == \ [[1, 0, 0], [], [1], [], [1, 0]] raises(IndexError, "dmp_inflate([[]], (-3, 7), 1, ZZ)") def test_dmp_exclude(): assert dmp_exclude([[[]]], 2, ZZ) == ([], [[[]]], 2) assert dmp_exclude([[[7]]], 2, ZZ) == ([], [[[7]]], 2) assert dmp_exclude([1,2,3], 0, ZZ) == ([], [1,2,3], 0) assert dmp_exclude([[1],[2,3]], 1, ZZ) == ([], [[1],[2,3]], 1) assert dmp_exclude([[1,2,3]], 1, ZZ) == ([0], [1,2,3], 0) assert dmp_exclude([[1],[2],[3]], 1, ZZ) == ([1], [1,2,3], 0) assert dmp_exclude([[[1,2,3]]], 2, ZZ) == ([0,1], [1,2,3], 0) assert dmp_exclude([[[1]],[[2]],[[3]]], 2, ZZ) == ([1,2], [1,2,3], 0) def test_dmp_include(): assert dmp_include([1,2,3], [], 0, ZZ) == [1,2,3] assert dmp_include([1,2,3], [0], 0, ZZ) == [[1,2,3]] assert dmp_include([1,2,3], [1], 0, ZZ) == [[1],[2],[3]] assert dmp_include([1,2,3], [0,1], 0, ZZ) == [[[1,2,3]]] assert dmp_include([1,2,3], [1,2], 0, ZZ) == [[[1]],[[2]],[[3]]] def test_dmp_inject(): K = ZZ['x','y'] assert dmp_inject([], 0, K) == ([[[]]], 2) assert dmp_inject([[]], 1, K) == ([[[[]]]], 3) assert dmp_inject([K([[1]])], 0, K) == ([[[1]]], 2) assert dmp_inject([[K([[1]])]], 1, K) == ([[[[1]]]], 3) assert dmp_inject([K([[1]]),K([[2],[3,4]])], 0, K) == ([[[1]],[[2],[3,4]]], 2) f = [K([[3],[7,0],[5,0,0]]),K([[2],[]]),K([[]]),K([[1,0,0],[11]])] g = [[[3],[7,0],[5,0,0]],[[2],[]],[[]],[[1,0,0],[11]]] assert dmp_inject(f, 0, K) == (g, 2) def test_dmp_eject(): K = ZZ['x','y'] assert dmp_eject([[[]]], 2, K) == [] assert dmp_eject([[[[]]]], 3, K) == [[]] assert dmp_eject([[[1]]], 2, K) == [K([[1]])] assert dmp_eject([[[[1]]]], 3, K) == [[K([[1]])]] assert dmp_eject([[[1]],[[2],[3,4]]], 2, K) == [K([[1]]),K([[2],[3,4]])] f = [K([[3],[7,0],[5,0,0]]),K([[2],[]]),K([[]]),K([[1,0,0],[11]])] g = [[[3],[7,0],[5,0,0]],[[2],[]],[[]],[[1,0,0],[11]]] assert dmp_eject(g, 2, K) == f def test_dup_terms_gcd(): assert dup_terms_gcd([], ZZ) == (0, []) assert dup_terms_gcd([1,0,1], ZZ) == (0, [1,0,1]) assert dup_terms_gcd([1,0,1,0], ZZ) == (1, [1,0,1]) def test_dmp_terms_gcd(): assert dmp_terms_gcd([[]], 1, ZZ) == ((0,0), [[]]) assert dmp_terms_gcd([1,0,1,0], 0, ZZ) == ((1,), [1,0,1]) assert dmp_terms_gcd([[1],[],[1],[]], 1, ZZ) == ((1,0), [[1],[],[1]]) assert dmp_terms_gcd([[1,0],[],[1]], 1, ZZ) == ((0,0), [[1,0],[],[1]]) assert dmp_terms_gcd([[1,0],[1,0,0],[],[]], 1, ZZ) == ((2,1), [[1],[1,0]]) def test_dmp_list_terms(): assert dmp_list_terms([[[]]], 2, ZZ) == [((0,0,0), 0)] assert dmp_list_terms([[[1]]], 2, ZZ) == [((0,0,0), 1)] assert dmp_list_terms([1,2,4,3,5], 0, ZZ) == \ [((4,), 1), ((3,), 2), ((2,), 4), ((1,), 3), ((0,), 5)] assert dmp_list_terms([[1],[2,4],[3,5,0]], 1, ZZ) == \ [((2, 0), 1), ((1, 1), 2), ((1, 0), 4), ((0, 2), 3), ((0, 1), 5)] f = [[2, 0, 0, 0], [1, 0, 0], []] assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 2), 1)] assert dmp_list_terms(f, 1, ZZ, order='grlex') == [((2, 3), 2), ((1, 2), 1)] f = [[2, 0, 0, 0], [1, 0, 0, 0, 0, 0], []] assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 5), 1)] assert dmp_list_terms(f, 1, ZZ, order='grlex') == [((1, 5), 1), ((2, 3), 2)] def test_dmp_apply_pairs(): h = lambda a, b: a*b assert dmp_apply_pairs([1,2,3], [4,5,6], h, [], 0, ZZ) == [4,10,18] assert dmp_apply_pairs([2,3], [4,5,6], h, [], 0, ZZ) == [10,18] assert dmp_apply_pairs([1,2,3], [5,6], h, [], 0, ZZ) == [10,18] assert dmp_apply_pairs([[1,2],[3]], [[4,5],[6]], h, [], 1, ZZ) == [[4,10],[18]] assert dmp_apply_pairs([[1,2],[3]], [[4],[5,6]], h, [], 1, ZZ) == [[8],[18]] assert dmp_apply_pairs([[1],[2,3]], [[4,5],[6]], h, [], 1, ZZ) == [[5],[18]] def test_dup_slice(): f = [1, 2, 3, 4] assert dup_slice(f, 0, 0, ZZ) == [] assert dup_slice(f, 0, 1, ZZ) == [4] assert dup_slice(f, 0, 2, ZZ) == [3,4] assert dup_slice(f, 0, 3, ZZ) == [2,3,4] assert dup_slice(f, 0, 4, ZZ) == [1,2,3,4] assert dup_slice(f, 0, 4, ZZ) == f assert dup_slice(f, 0, 9, ZZ) == f assert dup_slice(f, 1, 0, ZZ) == [] assert dup_slice(f, 1, 1, ZZ) == [] assert dup_slice(f, 1, 2, ZZ) == [3,0] assert dup_slice(f, 1, 3, ZZ) == [2,3,0] assert dup_slice(f, 1, 4, ZZ) == [1,2,3,0] def test_dup_random(): f = dup_random(0, -10, 10, ZZ) assert dup_degree(f) == 0 assert all([ -10 <= c <= 10 for c in f ]) f = dup_random(1, -20, 20, ZZ) assert dup_degree(f) == 1 assert all([ -20 <= c <= 20 for c in f ]) f = dup_random(2, -30, 30, ZZ) assert dup_degree(f) == 2 assert all([ -30 <= c <= 30 for c in f ]) f = dup_random(3, -40, 40, ZZ) assert dup_degree(f) == 3 assert all([ -40 <= c <= 40 for c in f ]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_polytools.py0000644000175000017500000025237312014170666027100 0ustar georgeskgeorgesk"""Tests for user-friendly public interface to polynomial functions. """ from sympy.polys.polytools import ( Poly, PurePoly, poly, parallel_poly_from_expr, degree, degree_list, LC, LM, LT, pdiv, prem, pquo, pexquo, div, rem, quo, exquo, half_gcdex, gcdex, invert, subresultants, resultant, discriminant, terms_gcd, cofactors, gcd, gcd_list, lcm, lcm_list, trunc, monic, content, primitive, compose, decompose, sturm, gff_list, gff, sqf_norm, sqf_part, sqf_list, sqf, factor_list, factor, intervals, refine_root, count_roots, real_roots, nroots, ground_roots, nth_power_roots_poly, cancel, reduced, groebner) from sympy.polys.polyerrors import ( MultivariatePolynomialError, OperationNotSupported, ExactQuotientFailed, PolificationFailed, ComputationFailed, UnificationFailed, RefinementFailed, GeneratorsNeeded, GeneratorsError, PolynomialError, CoercionFailed, NotAlgebraic, DomainError, OptionError, FlagError) from sympy.polys.monomialtools import ( monomial_lex_key) from sympy.polys.polyclasses import DMP, DMF from sympy.polys.domains import FF, ZZ, QQ, RR, EX from sympy import ( S, Integer, Rational, Float, Mul, symbols, sqrt, exp, sin, expand, oo, I, pi, re, im, RootOf, Eq, Tuple) from sympy.utilities.pytest import raises, XFAIL x,y,z,p,q,r,s,t,u,v,w,a,b,c,d,e = symbols('x,y,z,p,q,r,s,t,u,v,w,a,b,c,d,e') def _eq(a, b): for x, y in zip(a, b): if abs(x-y) > 1e-10: return False return True def test_Poly_from_dict(): K = FF(3) assert Poly.from_dict({0: 1, 1: 2}, gens=x, domain=K).rep == DMP([K(2),K(1)], K) assert Poly.from_dict({0: 1, 1: 5}, gens=x, domain=K).rep == DMP([K(2),K(1)], K) assert Poly.from_dict({(0,): 1, (1,): 2}, gens=x, domain=K).rep == DMP([K(2),K(1)], K) assert Poly.from_dict({(0,): 1, (1,): 5}, gens=x, domain=K).rep == DMP([K(2),K(1)], K) assert Poly.from_dict({(0, 0): 1, (1, 1): 2}, gens=(x,y), domain=K).rep == DMP([[K(2),K(0)],[K(1)]], K) assert Poly.from_dict({0: 1, 1: 2}, gens=x).rep == DMP([ZZ(2),ZZ(1)], ZZ) assert Poly.from_dict({0: 1, 1: 2}, gens=x, field=True).rep == DMP([QQ(2),QQ(1)], QQ) assert Poly.from_dict({0: 1, 1: 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2),ZZ(1)], ZZ) assert Poly.from_dict({0: 1, 1: 2}, gens=x, domain=QQ).rep == DMP([QQ(2),QQ(1)], QQ) assert Poly.from_dict({(0,): 1, (1,): 2}, gens=x).rep == DMP([ZZ(2),ZZ(1)], ZZ) assert Poly.from_dict({(0,): 1, (1,): 2}, gens=x, field=True).rep == DMP([QQ(2),QQ(1)], QQ) assert Poly.from_dict({(0,): 1, (1,): 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2),ZZ(1)], ZZ) assert Poly.from_dict({(0,): 1, (1,): 2}, gens=x, domain=QQ).rep == DMP([QQ(2),QQ(1)], QQ) def test_Poly_from_list(): K = FF(3) assert Poly.from_list([2,1], gens=x, domain=K).rep == DMP([K(2),K(1)], K) assert Poly.from_list([5,1], gens=x, domain=K).rep == DMP([K(2),K(1)], K) assert Poly.from_list([2,1], gens=x).rep == DMP([ZZ(2),ZZ(1)], ZZ) assert Poly.from_list([2,1], gens=x, field=True).rep == DMP([QQ(2),QQ(1)], QQ) assert Poly.from_list([2,1], gens=x, domain=ZZ).rep == DMP([ZZ(2),ZZ(1)], ZZ) assert Poly.from_list([2,1], gens=x, domain=QQ).rep == DMP([QQ(2),QQ(1)], QQ) assert Poly.from_list([0, 1.0], gens=x).rep == DMP([RR(1.0)], RR) assert Poly.from_list([1.0, 0], gens=x).rep == DMP([RR(1.0), RR(0.0)], RR) raises(MultivariatePolynomialError, "Poly.from_list([[]], gens=(x,y))") def test_Poly_from_poly(): f = Poly(x+7, x, domain=ZZ) g = Poly(x+2, x, modulus=3) h = Poly(x+y, x, y, domain=ZZ) K = FF(3) assert Poly.from_poly(f) == f assert Poly.from_poly(f, domain=K).rep == DMP([K(1),K(1)], K) assert Poly.from_poly(f, domain=ZZ).rep == DMP([1,7], ZZ) assert Poly.from_poly(f, domain=QQ).rep == DMP([1,7], QQ) assert Poly.from_poly(f, gens=x) == f assert Poly.from_poly(f, gens=x, domain=K).rep == DMP([K(1),K(1)], K) assert Poly.from_poly(f, gens=x, domain=ZZ).rep == DMP([1,7], ZZ) assert Poly.from_poly(f, gens=x, domain=QQ).rep == DMP([1,7], QQ) assert Poly.from_poly(f, gens=y) == Poly(x + 7, y, domain='ZZ[x]') raises(CoercionFailed, "Poly.from_poly(f, gens=y, domain=K)") raises(CoercionFailed, "Poly.from_poly(f, gens=y, domain=ZZ)") raises(CoercionFailed, "Poly.from_poly(f, gens=y, domain=QQ)") assert Poly.from_poly(f, gens=(x,y)) == Poly(x + 7, x, y, domain='ZZ') assert Poly.from_poly(f, gens=(x,y), domain=ZZ) == Poly(x + 7, x, y, domain='ZZ') assert Poly.from_poly(f, gens=(x,y), domain=QQ) == Poly(x + 7, x, y, domain='QQ') assert Poly.from_poly(f, gens=(x,y), modulus=3) == Poly(x + 7, x, y, domain='FF(3)') K = FF(2) assert Poly.from_poly(g) == g assert Poly.from_poly(g, domain=ZZ).rep == DMP([1,-1], ZZ) raises(CoercionFailed, "Poly.from_poly(g, domain=QQ)") assert Poly.from_poly(g, domain=K).rep == DMP([K(1),K(0)], K) assert Poly.from_poly(g, gens=x) == g assert Poly.from_poly(g, gens=x, domain=ZZ).rep == DMP([1,-1], ZZ) raises(CoercionFailed, "Poly.from_poly(g, gens=x, domain=QQ)") assert Poly.from_poly(g, gens=x, domain=K).rep == DMP([K(1),K(0)], K) K = FF(3) assert Poly.from_poly(h) == h assert Poly.from_poly(h, domain=ZZ).rep == DMP([[ZZ(1)],[ZZ(1),ZZ(0)]], ZZ) assert Poly.from_poly(h, domain=QQ).rep == DMP([[QQ(1)],[QQ(1),QQ(0)]], QQ) assert Poly.from_poly(h, domain=K).rep == DMP([[K(1)],[K(1),K(0)]], K) assert Poly.from_poly(h, gens=x) == Poly(x+y, x, domain=ZZ[y]) raises(CoercionFailed, "Poly.from_poly(h, gens=x, domain=ZZ)") assert Poly.from_poly(h, gens=x, domain=ZZ[y]) == Poly(x+y, x, domain=ZZ[y]) raises(CoercionFailed, "Poly.from_poly(h, gens=x, domain=QQ)") assert Poly.from_poly(h, gens=x, domain=QQ[y]) == Poly(x+y, x, domain=QQ[y]) raises(CoercionFailed, "Poly.from_poly(h, gens=x, modulus=3)") assert Poly.from_poly(h, gens=y) == Poly(x+y, y, domain=ZZ[x]) raises(CoercionFailed, "Poly.from_poly(h, gens=y, domain=ZZ)") assert Poly.from_poly(h, gens=y, domain=ZZ[x]) == Poly(x+y, y, domain=ZZ[x]) raises(CoercionFailed, "Poly.from_poly(h, gens=y, domain=QQ)") assert Poly.from_poly(h, gens=y, domain=QQ[x]) == Poly(x+y, y, domain=QQ[x]) raises(CoercionFailed, "Poly.from_poly(h, gens=y, modulus=3)") assert Poly.from_poly(h, gens=(x,y)) == h assert Poly.from_poly(h, gens=(x,y), domain=ZZ).rep == DMP([[ZZ(1)],[ZZ(1),ZZ(0)]], ZZ) assert Poly.from_poly(h, gens=(x,y), domain=QQ).rep == DMP([[QQ(1)],[QQ(1),QQ(0)]], QQ) assert Poly.from_poly(h, gens=(x,y), domain=K).rep == DMP([[K(1)],[K(1),K(0)]], K) assert Poly.from_poly(h, gens=(y,x)).rep == DMP([[ZZ(1)],[ZZ(1),ZZ(0)]], ZZ) assert Poly.from_poly(h, gens=(y,x), domain=ZZ).rep == DMP([[ZZ(1)],[ZZ(1),ZZ(0)]], ZZ) assert Poly.from_poly(h, gens=(y,x), domain=QQ).rep == DMP([[QQ(1)],[QQ(1),QQ(0)]], QQ) assert Poly.from_poly(h, gens=(y,x), domain=K).rep == DMP([[K(1)],[K(1),K(0)]], K) assert Poly.from_poly(h, gens=(x,y), field=True).rep == DMP([[QQ(1)],[QQ(1),QQ(0)]], QQ) assert Poly.from_poly(h, gens=(x,y), field=True).rep == DMP([[QQ(1)],[QQ(1),QQ(0)]], QQ) def test_Poly_from_expr(): raises(GeneratorsNeeded, "Poly.from_expr(S(0))") raises(GeneratorsNeeded, "Poly.from_expr(S(7))") K = FF(3) assert Poly.from_expr(x + 5, domain=K).rep == DMP([K(1),K(2)], K) assert Poly.from_expr(y + 5, domain=K).rep == DMP([K(1),K(2)], K) assert Poly.from_expr(x + 5, x, domain=K).rep == DMP([K(1),K(2)], K) assert Poly.from_expr(y + 5, y, domain=K).rep == DMP([K(1),K(2)], K) assert Poly.from_expr(x + y, domain=K).rep == DMP([[K(1)],[K(1),K(0)]], K) assert Poly.from_expr(x + y, x, y, domain=K).rep == DMP([[K(1)],[K(1),K(0)]], K) assert Poly.from_expr(x + 5).rep == DMP([1,5], ZZ) assert Poly.from_expr(y + 5).rep == DMP([1,5], ZZ) assert Poly.from_expr(x + 5, x).rep == DMP([1,5], ZZ) assert Poly.from_expr(y + 5, y).rep == DMP([1,5], ZZ) assert Poly.from_expr(x + 5, domain=ZZ).rep == DMP([1,5], ZZ) assert Poly.from_expr(y + 5, domain=ZZ).rep == DMP([1,5], ZZ) assert Poly.from_expr(x + 5, x, domain=ZZ).rep == DMP([1,5], ZZ) assert Poly.from_expr(y + 5, y, domain=ZZ).rep == DMP([1,5], ZZ) assert Poly.from_expr(x + 5, x, y, domain=ZZ).rep == DMP([[1],[5]], ZZ) assert Poly.from_expr(y + 5, x, y, domain=ZZ).rep == DMP([[1,5]], ZZ) def test_Poly__new__(): raises(GeneratorsError, "Poly(x+1, x, x)") raises(GeneratorsError, "Poly(x+y, x, y, domain=ZZ[x])") raises(GeneratorsError, "Poly(x+y, x, y, domain=ZZ[y])") raises(OptionError, "Poly(x, x, symmetric=True)") raises(OptionError, "Poly(x+2, x, modulus=3, domain=QQ)") raises(OptionError, "Poly(x+2, x, domain=ZZ, gaussian=True)") raises(OptionError, "Poly(x+2, x, modulus=3, gaussian=True)") raises(OptionError, "Poly(x+2, x, domain=ZZ, extension=[sqrt(3)])") raises(OptionError, "Poly(x+2, x, modulus=3, extension=[sqrt(3)])") raises(OptionError, "Poly(x+2, x, domain=ZZ, extension=True)") raises(OptionError, "Poly(x+2, x, modulus=3, extension=True)") raises(OptionError, "Poly(x+2, x, domain=ZZ, greedy=True)") raises(OptionError, "Poly(x+2, x, domain=QQ, field=True)") raises(OptionError, "Poly(x+2, x, domain=ZZ, greedy=False)") raises(OptionError, "Poly(x+2, x, domain=QQ, field=False)") raises(NotImplementedError, "Poly(x+1, x, modulus=3, order='grlex')") raises(NotImplementedError, "Poly(x+1, x, order='grlex')") raises(GeneratorsNeeded, "Poly({1: 2, 0: 1})") raises(GeneratorsNeeded, "Poly([2, 1])") raises(GeneratorsNeeded, "Poly((2, 1))") raises(GeneratorsNeeded, "Poly(1)") f = a*x**2 + b*x + c assert Poly({2: a, 1: b, 0: c}, x) == f assert Poly(iter([a, b, c]), x) == f assert Poly([a, b, c], x) == f assert Poly((a, b, c), x) == f assert Poly(Poly(a*x + b*y, x, y), x) == Poly(a*x + b*y, x) assert Poly(3*x**2 + 2*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] assert Poly(3*x**2 + 2*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] assert Poly(3*x**2 + 2*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] raises(CoercionFailed, "Poly(3*x**2/5 + 2*x/5 + 1, domain='ZZ')") assert Poly(3*x**2/5 + 2*x/5 + 1, domain='QQ').all_coeffs() == [S(3)/5, S(2)/5, 1] assert _eq(Poly(3*x**2/5 + 2*x/5 + 1, domain='RR').all_coeffs(), [0.6, 0.4, 1.0]) assert Poly(3.0*x**2 + 2.0*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] assert Poly(3.0*x**2 + 2.0*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] assert Poly(3.0*x**2 + 2.0*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] raises(CoercionFailed, "Poly(3.1*x**2 + 2.1*x + 1, domain='ZZ')") assert Poly(3.1*x**2 + 2.1*x + 1, domain='QQ').all_coeffs() == [S(31)/10, S(21)/10, 1] assert Poly(3.1*x**2 + 2.1*x + 1, domain='RR').all_coeffs() == [3.1, 2.1, 1.0] assert Poly({(2,1): 1, (1,2): 2, (1,1): 3}, x, y) == \ Poly(x**2*y + 2*x*y**2 + 3*x*y, x, y) assert Poly(x**2 + 1, extension=I).get_domain() == QQ.algebraic_field(I) f = 3*x**5 - x**4 + x**3 - x** 2 + 65538 assert Poly(f, x, modulus=65537, symmetric=True) == \ Poly(3*x**5 - x**4 + x**3 - x** 2 + 1, x, modulus=65537, symmetric=True) assert Poly(f, x, modulus=65537, symmetric=False) == \ Poly(3*x**5 + 65536*x**4 + x**3 + 65536*x** 2 + 1, x, modulus=65537, symmetric=False) assert Poly(x**2 + x + 1.0).get_domain() == RR def test_Poly__args(): assert Poly(x**2 + 1).args == [x**2 + 1] def test_Poly__gens(): assert Poly((x-p)*(x-q), x).gens == (x,) assert Poly((x-p)*(x-q), p).gens == (p,) assert Poly((x-p)*(x-q), q).gens == (q,) assert Poly((x-p)*(x-q), x, p).gens == (x, p) assert Poly((x-p)*(x-q), x, q).gens == (x, q) assert Poly((x-p)*(x-q), x, p, q).gens == (x, p, q) assert Poly((x-p)*(x-q), p, x, q).gens == (p, x, q) assert Poly((x-p)*(x-q), p, q, x).gens == (p, q, x) assert Poly((x-p)*(x-q)).gens == (x, p, q) assert Poly((x-p)*(x-q), sort='x > p > q').gens == (x, p, q) assert Poly((x-p)*(x-q), sort='p > x > q').gens == (p, x, q) assert Poly((x-p)*(x-q), sort='p > q > x').gens == (p, q, x) assert Poly((x-p)*(x-q), x, p, q, sort='p > q > x').gens == (x, p, q) assert Poly((x-p)*(x-q), wrt='x').gens == (x, p, q) assert Poly((x-p)*(x-q), wrt='p').gens == (p, x, q) assert Poly((x-p)*(x-q), wrt='q').gens == (q, x, p) assert Poly((x-p)*(x-q), wrt=x).gens == (x, p, q) assert Poly((x-p)*(x-q), wrt=p).gens == (p, x, q) assert Poly((x-p)*(x-q), wrt=q).gens == (q, x, p) assert Poly((x-p)*(x-q), x, p, q, wrt='p').gens == (x, p, q) assert Poly((x-p)*(x-q), wrt='p', sort='q > x').gens == (p, q, x) assert Poly((x-p)*(x-q), wrt='q', sort='p > x').gens == (q, p, x) def test_Poly_zero(): assert Poly(x).zero == Poly(0, x, domain=ZZ) assert Poly(x/2).zero == Poly(0, x, domain=QQ) def test_Poly_one(): assert Poly(x).one == Poly(1, x, domain=ZZ) assert Poly(x/2).one == Poly(1, x, domain=QQ) def test_Poly__unify(): raises(UnificationFailed, "Poly(x)._unify(y)") K = FF(3) raises(UnificationFailed, "Poly(x, x, modulus=3)._unify(Poly(x, x, modulus=5))") assert Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=3))[2:] == (DMP([[K(1)],[]], K), DMP([[K(1),K(0)]], K)) assert Poly(y, x, y)._unify(Poly(x, x, modulus=3))[2:] == (DMP([[K(1),K(0)]], K), DMP([[K(1)],[]], K)) assert Poly(x, x, modulus=3)._unify(Poly(y, x, y))[2:] == (DMP([[K(1)],[]], K), DMP([[K(1),K(0)]], K)) assert Poly(x+1, x)._unify(Poly(x+2, x))[2:] == (DMP([1, 1], ZZ), DMP([1, 2], ZZ)) assert Poly(x+1, x, domain='QQ')._unify(Poly(x+2, x))[2:] == (DMP([1, 1], QQ), DMP([1, 2], QQ)) assert Poly(x+1, x)._unify(Poly(x+2, x, domain='QQ'))[2:] == (DMP([1, 1], QQ), DMP([1, 2], QQ)) assert Poly(x+1, x)._unify(Poly(x+2, x, y))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x+1, x, domain='QQ')._unify(Poly(x+2, x, y))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x)._unify(Poly(x+2, x, y, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x, y)._unify(Poly(x+2, x))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x+1, x, y, domain='QQ')._unify(Poly(x+2, x))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x, y)._unify(Poly(x+2, x, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x, y)._unify(Poly(x+2, x, y))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x+1, x, y, domain='QQ')._unify(Poly(x+2, x, y))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x, y)._unify(Poly(x+2, x, y, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x)._unify(Poly(x+2, y, x))[2:] == (DMP([[1, 1]], ZZ), DMP([[1, 2]], ZZ)) assert Poly(x+1, x, domain='QQ')._unify(Poly(x+2, y, x))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x+1, x)._unify(Poly(x+2, y, x, domain='QQ'))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x+1, y, x)._unify(Poly(x+2, x))[2:] == (DMP([[1, 1]], ZZ), DMP([[1, 2]], ZZ)) assert Poly(x+1, y, x, domain='QQ')._unify(Poly(x+2, x))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x+1, y, x)._unify(Poly(x+2, x, domain='QQ'))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x+1, x, y)._unify(Poly(x+2, y, x))[2:] == (DMP([[1], [1]], ZZ), DMP([[1], [2]], ZZ)) assert Poly(x+1, x, y, domain='QQ')._unify(Poly(x+2, y, x))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, x, y)._unify(Poly(x+2, y, x, domain='QQ'))[2:] == (DMP([[1], [1]], QQ), DMP([[1], [2]], QQ)) assert Poly(x+1, y, x)._unify(Poly(x+2, x, y))[2:] == (DMP([[1, 1]], ZZ), DMP([[1, 2]], ZZ)) assert Poly(x+1, y, x, domain='QQ')._unify(Poly(x+2, x, y))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(x+1, y, x)._unify(Poly(x+2, x, y, domain='QQ'))[2:] == (DMP([[1, 1]], QQ), DMP([[1, 2]], QQ)) assert Poly(a*x, x, domain='ZZ[a]')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ (DMP([DMF(([[1], []], [[1]]), ZZ), DMF(([[]], [[1]]), ZZ)], ZZ.frac_field(a,b)), DMP([DMF(([[1, 0], []], [[1]]), ZZ), DMF(([[]], [[1]]), ZZ)], ZZ.frac_field(a,b))) assert Poly(a*x, x, domain='ZZ(a)')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ (DMP([DMF(([[1], []], [[1]]), ZZ), DMF(([[]], [[1]]), ZZ)], ZZ.frac_field(a,b)), DMP([DMF(([[1, 0], []], [[1]]), ZZ), DMF(([[]], [[1]]), ZZ)], ZZ.frac_field(a,b))) raises(CoercionFailed, "Poly(Poly(x**2 + x**2*z, y, field=True), domain='ZZ(x)')") def test_Poly_free_symbols(): assert Poly(x**2 + 1).free_symbols == set([x]) assert Poly(x**2 + y*z).free_symbols == set([x, y, z]) assert Poly(x**2 + y*z, x).free_symbols == set([x, y, z]) assert Poly(x**2 + sin(y*z)).free_symbols == set([x, y, z]) assert Poly(x**2 + sin(y*z), x).free_symbols == set([x, y, z]) assert Poly(x**2 + sin(y*z), x, domain=EX).free_symbols == set([x, y, z]) def test_PurePoly_free_symbols(): assert PurePoly(x**2 + 1).free_symbols == set([]) assert PurePoly(x**2 + y*z).free_symbols == set([]) assert PurePoly(x**2 + y*z, x).free_symbols == set([y, z]) assert PurePoly(x**2 + sin(y*z)).free_symbols == set([]) assert PurePoly(x**2 + sin(y*z), x).free_symbols == set([y, z]) assert PurePoly(x**2 + sin(y*z), x, domain=EX).free_symbols == set([y, z]) def test_Poly__eq__(): assert (Poly(x, x) == Poly(x, x)) == True assert (Poly(x, x, domain=QQ) == Poly(x, x)) == True assert (Poly(x, x) == Poly(x, x, domain=QQ)) == True assert (Poly(x, x, domain=ZZ[a]) == Poly(x, x)) == True assert (Poly(x, x) == Poly(x, x, domain=ZZ[a])) == True assert (Poly(x*y, x, y) == Poly(x, x)) == False assert (Poly(x, x, y) == Poly(x, x)) == False assert (Poly(x, x) == Poly(x, x, y)) == False assert (Poly(x**2 + 1, x) == Poly(y**2 + 1, y)) == False assert (Poly(y**2 + 1, y) == Poly(x**2 + 1, x)) == False def test_PurePoly__eq__(): assert (PurePoly(x, x) == PurePoly(x, x)) == True assert (PurePoly(x, x, domain=QQ) == PurePoly(x, x)) == True assert (PurePoly(x, x) == PurePoly(x, x, domain=QQ)) == True assert (PurePoly(x, x, domain=ZZ[a]) == PurePoly(x, x)) == True assert (PurePoly(x, x) == PurePoly(x, x, domain=ZZ[a])) == True assert (PurePoly(x*y, x, y) == PurePoly(x, x)) == False assert (PurePoly(x, x, y) == PurePoly(x, x)) == False assert (PurePoly(x, x) == PurePoly(x, x, y)) == False assert (PurePoly(x**2 + 1, x) == PurePoly(y**2 + 1, y)) == True assert (PurePoly(y**2 + 1, y) == PurePoly(x**2 + 1, x)) == True def test_PurePoly_Poly(): assert isinstance(PurePoly(Poly(x**2 + 1)), PurePoly) == True assert isinstance(Poly(PurePoly(x**2 + 1)), Poly) == True def test_Poly_get_domain(): assert Poly(2*x).get_domain() == ZZ assert Poly(2*x, domain='ZZ').get_domain() == ZZ assert Poly(2*x, domain='QQ').get_domain() == QQ assert Poly(x/2).get_domain() == QQ raises(CoercionFailed, "Poly(x/2, domain='ZZ')") assert Poly(x/2, domain='QQ').get_domain() == QQ assert Poly(0.2*x).get_domain() == RR def test_Poly_set_domain(): assert Poly(2*x + 1).set_domain(ZZ) == Poly(2*x + 1) assert Poly(2*x + 1).set_domain('ZZ') == Poly(2*x + 1) assert Poly(2*x + 1).set_domain(QQ) == Poly(2*x + 1, domain='QQ') assert Poly(2*x + 1).set_domain('QQ') == Poly(2*x + 1, domain='QQ') assert Poly(S(2)/10*x + S(1)/10).set_domain('RR') == Poly(0.2*x + 0.1) assert Poly(0.2*x + 0.1).set_domain('QQ') == Poly(S(2)/10*x + S(1)/10) raises(CoercionFailed, "Poly(x/2 + 1).set_domain(ZZ)") raises(CoercionFailed, "Poly(x + 1, modulus=2).set_domain(QQ)") raises(GeneratorsError, "Poly(x*y, x, y).set_domain(ZZ[y])") def test_Poly_get_modulus(): Poly(x**2 + 1, modulus=2).get_modulus() == 2 raises(PolynomialError, "Poly(x**2 + 1).get_modulus()") def test_Poly_set_modulus(): Poly(x**2 + 1, modulus=2).set_modulus(7) == Poly(x**2 + 1, modulus=7) Poly(x**2 + 5, modulus=7).set_modulus(2) == Poly(x**2 + 1, modulus=2) Poly(x**2 + 1).set_modulus(2) == Poly(x**2 + 1, modulus=2) raises(CoercionFailed, "Poly(x/2 + 1).set_modulus(2)") def test_Poly_add_ground(): assert Poly(x + 1).add_ground(2) == Poly(x + 3) def test_Poly_sub_ground(): assert Poly(x + 1).sub_ground(2) == Poly(x - 1) def test_Poly_mul_ground(): assert Poly(x + 1).mul_ground(2) == Poly(2*x + 2) def test_Poly_quo_ground(): assert Poly(2*x + 4).quo_ground(2) == Poly(x + 2) assert Poly(2*x + 3).quo_ground(2) == Poly(x + 1) def test_Poly_exquo_ground(): assert Poly(2*x + 4).exquo_ground(2) == Poly(x + 2) raises(ExactQuotientFailed, "Poly(2*x + 3).exquo_ground(2)") def test_Poly_abs(): assert Poly(-x+1, x).abs() == abs(Poly(-x+1, x)) == Poly(x+1, x) def test_Poly_neg(): assert Poly(-x+1, x).neg() == -Poly(-x+1, x) == Poly(x-1, x) def test_Poly_add(): assert Poly(0, x).add(Poly(0, x)) == Poly(0, x) assert Poly(0, x) + Poly(0, x) == Poly(0, x) assert Poly(1, x).add(Poly(0, x)) == Poly(1, x) assert Poly(1, x, y) + Poly(0, x) == Poly(1, x, y) assert Poly(0, x).add(Poly(1, x, y)) == Poly(1, x, y) assert Poly(0, x, y) + Poly(1, x, y) == Poly(1, x, y) assert Poly(1, x) + x == Poly(x+1, x) assert Poly(1, x) + sin(x) == 1+sin(x) assert Poly(x, x) + 1 == Poly(x+1, x) assert 1 + Poly(x, x) == Poly(x+1, x) def test_Poly_sub(): assert Poly(0, x).sub(Poly(0, x)) == Poly(0, x) assert Poly(0, x) - Poly(0, x) == Poly(0, x) assert Poly(1, x).sub(Poly(0, x)) == Poly(1, x) assert Poly(1, x, y) - Poly(0, x) == Poly(1, x, y) assert Poly(0, x).sub(Poly(1, x, y)) == Poly(-1, x, y) assert Poly(0, x, y) - Poly(1, x, y) == Poly(-1, x, y) assert Poly(1, x) - x == Poly(1-x, x) assert Poly(1, x) - sin(x) == 1-sin(x) assert Poly(x, x) - 1 == Poly(x-1, x) assert 1 - Poly(x, x) == Poly(1-x, x) def test_Poly_mul(): assert Poly(0, x).mul(Poly(0, x)) == Poly(0, x) assert Poly(0, x) * Poly(0, x) == Poly(0, x) assert Poly(2, x).mul(Poly(4, x)) == Poly(8, x) assert Poly(2, x, y) * Poly(4, x) == Poly(8, x, y) assert Poly(4, x).mul(Poly(2, x, y)) == Poly(8, x, y) assert Poly(4, x, y) * Poly(2, x, y) == Poly(8, x, y) assert Poly(1, x) * x == Poly(x, x) assert Poly(1, x) * sin(x) == sin(x) assert Poly(x, x) * 2 == Poly(2*x, x) assert 2 * Poly(x, x) == Poly(2*x, x) def test_Poly_sqr(): assert Poly(x*y, x, y).sqr() == Poly(x**2*y**2, x, y) def test_Poly_pow(): assert Poly(x, x).pow(10) == Poly(x**10, x) assert Poly(x, x).pow(Integer(10)) == Poly(x**10, x) assert Poly(2*y, x, y).pow(4) == Poly(16*y**4, x, y) assert Poly(2*y, x, y).pow(Integer(4)) == Poly(16*y**4, x, y) assert Poly(7*x*y, x, y)**3 == Poly(343*x**3*y**3, x, y) assert Poly(x*y+1, x, y)**(-1) == (x*y+1)**(-1) assert Poly(x*y+1, x, y)**x == (x*y+1)**x def test_Poly_divmod(): f, g = Poly(x**2), Poly(x) q, r = g, Poly(0, x) assert divmod(f, g) == (q, r) assert f // g == q assert f % g == r assert divmod(f, x) == (q, r) assert f // x == q assert f % x == r q, r = Poly(0, x), Poly(2, x) assert divmod(2, g) == (q, r) assert 2 // g == q assert 2 % g == r assert Poly(x)/Poly(x) == 1 assert Poly(x**2)/Poly(x) == x assert Poly(x)/Poly(x**2) == 1/x def test_Poly_eq_ne(): assert (Poly(x+y, x, y) == Poly(x+y, x, y)) == True assert (Poly(x+y, x) == Poly(x+y, x, y)) == False assert (Poly(x+y, x, y) == Poly(x+y, x)) == False assert (Poly(x+y, x) == Poly(x+y, x)) == True assert (Poly(x+y, y) == Poly(x+y, y)) == True assert (Poly(x+y, x, y) == x+y) == True assert (Poly(x+y, x) == x+y) == True assert (Poly(x+y, x, y) == x+y) == True assert (Poly(x+y, x) == x+y) == True assert (Poly(x+y, y) == x+y) == True assert (Poly(x+y, x, y) != Poly(x+y, x, y)) == False assert (Poly(x+y, x) != Poly(x+y, x, y)) == True assert (Poly(x+y, x, y) != Poly(x+y, x)) == True assert (Poly(x+y, x) != Poly(x+y, x)) == False assert (Poly(x+y, y) != Poly(x+y, y)) == False assert (Poly(x+y, x, y) != x+y) == False assert (Poly(x+y, x) != x+y) == False assert (Poly(x+y, x, y) != x+y) == False assert (Poly(x+y, x) != x+y) == False assert (Poly(x+y, y) != x+y) == False assert (Poly(x, x) == sin(x)) == False assert (Poly(x, x) != sin(x)) == True def test_Poly_nonzero(): assert not bool(Poly(0, x)) == True assert not bool(Poly(1, x)) == False def test_Poly_properties(): assert Poly(0, x).is_zero == True assert Poly(1, x).is_zero == False assert Poly(1, x).is_one == True assert Poly(2, x).is_one == False assert Poly(x-1, x).is_sqf == True assert Poly((x-1)**2, x).is_sqf == False assert Poly(x-1, x).is_monic == True assert Poly(2*x-1, x).is_monic == False assert Poly(3*x+2, x).is_primitive == True assert Poly(4*x+2, x).is_primitive == False assert Poly(1, x).is_ground == True assert Poly(x, x).is_ground == False assert Poly(x+y+z+1).is_linear == True assert Poly(x*y*z+1).is_linear == False assert Poly(x*y+z+1).is_quadratic == True assert Poly(x*y*z+1).is_quadratic == False assert Poly(x*y).is_monomial == True assert Poly(x*y+1).is_monomial == False assert Poly(x*y+x).is_homogeneous == True assert Poly(x*y+x+1).is_homogeneous == False assert Poly(x).is_univariate == True assert Poly(x*y).is_univariate == False assert Poly(x*y).is_multivariate == True assert Poly(x).is_multivariate == False assert Poly(x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1).is_cyclotomic == False assert Poly(x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1).is_cyclotomic == True @XFAIL def test_Poly_is_irreducible(): # when this passes, check the polytools is_irreducible docstring, too. assert Poly(7*x + 3, modulus=11).is_irreducible == True assert Poly(7*x**2 + 3*x + 1, modulus=11).is_irreducible == False def test_Poly_subs(): assert Poly(x + 1).subs(x, 0) == 1 assert Poly(x + 1).subs(x, x) == Poly(x + 1) assert Poly(x + 1).subs(x, y) == Poly(y + 1) assert Poly(x*y, x).subs(y, x) == x**2 assert Poly(x*y, x).subs(x, y) == y**2 def test_Poly_replace(): assert Poly(x + 1).replace(x) == Poly(x + 1) assert Poly(x + 1).replace(y) == Poly(y + 1) raises(PolynomialError, "Poly(x + y).replace(z)") assert Poly(x + 1).replace(x, x) == Poly(x + 1) assert Poly(x + 1).replace(x, y) == Poly(y + 1) assert Poly(x + y).replace(x, x) == Poly(x + y) assert Poly(x + y).replace(x, z) == Poly(z + y, z, y) assert Poly(x + y).replace(y, y) == Poly(x + y) assert Poly(x + y).replace(y, z) == Poly(x + z, x, z) raises(PolynomialError, "Poly(x + y).replace(x, y)") raises(PolynomialError, "Poly(x + y).replace(z, t)") assert Poly(x + y, x).replace(x, z) == Poly(z + y, z) assert Poly(x + y, y).replace(y, z) == Poly(x + z, z) raises(PolynomialError, "Poly(x + y, x).replace(x, y)") raises(PolynomialError, "Poly(x + y, y).replace(y, x)") def test_Poly_reorder(): raises(PolynomialError, "Poly(x+y).reorder(x, z)") assert Poly(x + y, x, y).reorder(x, y) == Poly(x + y, x, y) assert Poly(x + y, x, y).reorder(y, x) == Poly(x + y, y, x) assert Poly(x + y, y, x).reorder(x, y) == Poly(x + y, x, y) assert Poly(x + y, y, x).reorder(y, x) == Poly(x + y, y, x) assert Poly(x + y, x, y).reorder(wrt=x) == Poly(x + y, x, y) assert Poly(x + y, x, y).reorder(wrt=y) == Poly(x + y, y, x) def test_Poly_ltrim(): f = Poly(y**2 + y*z**2, x, y, z).ltrim(y) assert f.as_expr() == y**2 + y*z**2 and f.gens == (y, z) raises(PolynomialError, "Poly(x*y**2 + y**2, x, y).ltrim(y)") def test_Poly_has_only_gens(): assert Poly(x*y + 1, x, y, z).has_only_gens(x, y) == True assert Poly(x*y + z, x, y, z).has_only_gens(x, y) == False raises(GeneratorsError, "Poly(x*y**2 + y**2, x, y).has_only_gens(t)") def test_Poly_to_ring(): assert Poly(2*x+1, domain='ZZ').to_ring() == Poly(2*x+1, domain='ZZ') assert Poly(2*x+1, domain='QQ').to_ring() == Poly(2*x+1, domain='ZZ') raises(CoercionFailed, "Poly(x/2+1).to_ring()") raises(DomainError, "Poly(2*x+1, modulus=3).to_ring()") def test_Poly_to_field(): assert Poly(2*x+1, domain='ZZ').to_field() == Poly(2*x+1, domain='QQ') assert Poly(2*x+1, domain='QQ').to_field() == Poly(2*x+1, domain='QQ') assert Poly(x/2+1, domain='QQ').to_field() == Poly(x/2+1, domain='QQ') assert Poly(2*x+1, modulus=3).to_field() == Poly(2*x+1, modulus=3) raises(DomainError, "Poly(2.0*x + 1.0).to_field()") def test_Poly_to_exact(): assert Poly(2*x).to_exact() == Poly(2*x) assert Poly(x/2).to_exact() == Poly(x/2) assert Poly(0.1*x).to_exact() == Poly(x/10) def test_Poly_retract(): f = Poly(x**2 + 1, x, domain=QQ[y]) assert f.retract() == Poly(x**2 + 1, x, domain='ZZ') assert f.retract(field=True) == Poly(x**2 + 1, x, domain='QQ') assert Poly(0, x, y).retract() == Poly(0, x, y) def test_Poly_slice(): f = Poly(x**3 + 2*x**2 + 3*x + 4) assert f.slice(0, 0) == Poly(0, x) assert f.slice(0, 1) == Poly(4, x) assert f.slice(0, 2) == Poly(3*x + 4, x) assert f.slice(0, 3) == Poly(2*x**2 + 3*x + 4, x) assert f.slice(0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) assert f.slice(x, 0, 0) == Poly(0, x) assert f.slice(x, 0, 1) == Poly(4, x) assert f.slice(x, 0, 2) == Poly(3*x + 4, x) assert f.slice(x, 0, 3) == Poly(2*x**2 + 3*x + 4, x) assert f.slice(x, 0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) def test_Poly_coeffs(): assert Poly(0, x).coeffs() == [0] assert Poly(1, x).coeffs() == [1] assert Poly(2*x+1, x).coeffs() == [2,1] assert Poly(7*x**2+2*x+1, x).coeffs() == [7,2,1] assert Poly(7*x**4+2*x+1, x).coeffs() == [7,2,1] assert Poly(x*y**7 + 2*x**2*y**3).coeffs('lex') == [2, 1] assert Poly(x*y**7 + 2*x**2*y**3).coeffs('grlex') == [1, 2] def test_Poly_monoms(): assert Poly(0, x).monoms() == [(0,)] assert Poly(1, x).monoms() == [(0,)] assert Poly(2*x+1, x).monoms() == [(1,),(0,)] assert Poly(7*x**2+2*x+1, x).monoms() == [(2,),(1,),(0,)] assert Poly(7*x**4+2*x+1, x).monoms() == [(4,),(1,),(0,)] assert Poly(x*y**7 + 2*x**2*y**3).monoms('lex') == [(2, 3), (1, 7)] assert Poly(x*y**7 + 2*x**2*y**3).monoms('grlex') == [(1, 7), (2, 3)] def test_Poly_terms(): assert Poly(0, x).terms() == [((0,), 0)] assert Poly(1, x).terms() == [((0,), 1)] assert Poly(2*x+1, x).terms() == [((1,), 2),((0,), 1)] assert Poly(7*x**2+2*x+1, x).terms() == [((2,), 7),((1,), 2),((0,), 1)] assert Poly(7*x**4+2*x+1, x).terms() == [((4,), 7),((1,), 2),((0,), 1)] assert Poly(x*y**7 + 2*x**2*y**3).terms('lex') == [((2, 3), 2), ((1, 7), 1)] assert Poly(x*y**7 + 2*x**2*y**3).terms('grlex') == [((1, 7), 1), ((2, 3), 2)] def test_Poly_all_coeffs(): assert Poly(0, x).all_coeffs() == [0] assert Poly(1, x).all_coeffs() == [1] assert Poly(2*x+1, x).all_coeffs() == [2,1] assert Poly(7*x**2+2*x+1, x).all_coeffs() == [7,2,1] assert Poly(7*x**4+2*x+1, x).all_coeffs() == [7,0,0,2,1] def test_Poly_all_monoms(): assert Poly(0, x).all_monoms() == [(0,)] assert Poly(1, x).all_monoms() == [(0,)] assert Poly(2*x+1, x).all_monoms() == [(1,),(0,)] assert Poly(7*x**2+2*x+1, x).all_monoms() == [(2,),(1,),(0,)] assert Poly(7*x**4+2*x+1, x).all_monoms() == [(4,),(3,),(2,),(1,),(0,)] def test_Poly_all_terms(): assert Poly(0, x).all_terms() == [((0,), 0)] assert Poly(1, x).all_terms() == [((0,), 1)] assert Poly(2*x+1, x).all_terms() == [((1,), 2),((0,), 1)] assert Poly(7*x**2+2*x+1, x).all_terms() == [((2,), 7),((1,), 2),((0,), 1)] assert Poly(7*x**4+2*x+1, x).all_terms() == [((4,), 7),((3,),0),((2,),0),((1,), 2),((0,), 1)] def test_Poly_termwise(): f = Poly(x**2 + 20*x + 400) g = Poly(x**2 + 2*x + 4) def func(monom, coeff): (k,) = monom return coeff//10**(2-k) assert f.termwise(func) == g def func(monom, coeff): (k,) = monom return (k,), coeff//10**(2-k) assert f.termwise(func) == g def test_Poly_length(): assert Poly(0, x).length() == 0 assert Poly(1, x).length() == 1 assert Poly(x, x).length() == 1 assert Poly(x+1, x).length() == 2 assert Poly(x**2+1, x).length() == 2 assert Poly(x**2+x+1, x).length() == 3 def test_Poly_as_dict(): assert Poly(0, x).as_dict() == {} assert Poly(0, x, y, z).as_dict() == {} assert Poly(1, x).as_dict() == {(0,): 1} assert Poly(1, x, y, z).as_dict() == {(0,0,0): 1} assert Poly(x**2+3, x).as_dict() == {(2,): 1, (0,): 3} assert Poly(x**2+3, x, y, z).as_dict() == {(2,0,0): 1, (0,0,0): 3} assert Poly(3*x**2*y*z**3+4*x*y+5*x*z).as_dict() == {(2,1,3): 3, (1,1,0): 4, (1,0,1): 5} def test_Poly_as_expr(): assert Poly(0, x).as_expr() == 0 assert Poly(0, x, y, z).as_expr() == 0 assert Poly(1, x).as_expr() == 1 assert Poly(1, x, y, z).as_expr() == 1 assert Poly(x**2+3, x).as_expr() == x**2 + 3 assert Poly(x**2+3, x, y, z).as_expr() == x**2 + 3 assert Poly(3*x**2*y*z**3+4*x*y+5*x*z).as_expr() == 3*x**2*y*z**3 + 4*x*y + 5*x*z f = Poly(x**2 + 2*x*y**2 - y, x, y) assert f.as_expr() == -y + x**2 + 2*x*y**2 assert f.as_expr({x: 5}) == 25 - y + 10*y**2 assert f.as_expr({y: 6}) == -6 + 72*x + x**2 assert f.as_expr({x: 5, y: 6}) == 379 assert f.as_expr(5, 6) == 379 raises(GeneratorsError, "f.as_expr({z: 7})") def test_Poly_lift(): assert Poly(x**4 - I*x + 17*I, x, gaussian=True).lift() == \ Poly(x**16 + 2*x**10 + 578*x**8 + x**4 - 578*x**2 + 83521, x, domain='QQ') def test_Poly_deflate(): assert Poly(0, x).deflate() == ((1,), Poly(0, x)) assert Poly(1, x).deflate() == ((1,), Poly(1, x)) assert Poly(x, x).deflate() == ((1,), Poly(x, x)) assert Poly(x**2, x).deflate() == ((2,), Poly(x, x)) assert Poly(x**17, x).deflate() == ((17,), Poly(x, x)) assert Poly(x**2*y*z**11+x**4*z**11).deflate() == ((2,1,11), Poly(x*y*z+x**2*z)) def test_Poly_inject(): f = Poly(x**2*y + x*y**3 + x*y + 1, x) assert f.inject() == Poly(x**2*y + x*y**3 + x*y + 1, x, y) assert f.inject(front=True) == Poly(y**3*x + y*x**2 + y*x + 1, y, x) def test_Poly_eject(): f = Poly(x**2*y + x*y**3 + x*y + 1, x, y) assert f.eject(x) == Poly(x*y**3 + (x**2 + x)*y + 1, y, domain='ZZ[x]') assert f.eject(y) == Poly(y*x**2 + (y**3 + y)*x + 1, x, domain='ZZ[y]') raises(DomainError, "Poly(x*y, x, y, domain=ZZ[z]).eject(y)") raises(NotImplementedError, "Poly(x*y, x, y, z).eject(y)") def test_Poly_exclude(): assert Poly(x, x, y).exclude() == Poly(x, x) assert Poly(x*y, x, y).exclude() == Poly(x*y, x, y) assert Poly(1, x, y).exclude() == Poly(1, x, y) def test_Poly__gen_to_level(): assert Poly(1, x, y)._gen_to_level(-2) == 0 assert Poly(1, x, y)._gen_to_level(-1) == 1 assert Poly(1, x, y)._gen_to_level( 0) == 0 assert Poly(1, x, y)._gen_to_level( 1) == 1 raises(PolynomialError, "Poly(1, x, y)._gen_to_level(-3)") raises(PolynomialError, "Poly(1, x, y)._gen_to_level( 2)") assert Poly(1, x, y)._gen_to_level(x) == 0 assert Poly(1, x, y)._gen_to_level(y) == 1 assert Poly(1, x, y)._gen_to_level('x') == 0 assert Poly(1, x, y)._gen_to_level('y') == 1 raises(PolynomialError, "Poly(1, x, y)._gen_to_level(z)") raises(PolynomialError, "Poly(1, x, y)._gen_to_level('z')") def test_Poly_degree(): assert Poly(0, x).degree() ==-1 assert Poly(1, x).degree() == 0 assert Poly(x, x).degree() == 1 assert Poly(0, x).degree(gen=0) ==-1 assert Poly(1, x).degree(gen=0) == 0 assert Poly(x, x).degree(gen=0) == 1 assert Poly(0, x).degree(gen=x) ==-1 assert Poly(1, x).degree(gen=x) == 0 assert Poly(x, x).degree(gen=x) == 1 assert Poly(0, x).degree(gen='x') ==-1 assert Poly(1, x).degree(gen='x') == 0 assert Poly(x, x).degree(gen='x') == 1 raises(PolynomialError, "Poly(1, x).degree(gen=1)") raises(PolynomialError, "Poly(1, x).degree(gen=y)") raises(PolynomialError, "Poly(1, x).degree(gen='y')") assert Poly(1, x, y).degree() == 0 assert Poly(2*y, x, y).degree() == 0 assert Poly(x*y, x, y).degree() == 1 assert Poly(1, x, y).degree(gen=x) == 0 assert Poly(2*y, x, y).degree(gen=x) == 0 assert Poly(x*y, x, y).degree(gen=x) == 1 assert Poly(1, x, y).degree(gen=y) == 0 assert Poly(2*y, x, y).degree(gen=y) == 1 assert Poly(x*y, x, y).degree(gen=y) == 1 assert degree(1, x) == 0 assert degree(x, x) == 1 assert degree(x*y**2, gen=x) == 1 assert degree(x*y**2, gen=y) == 2 assert degree(x*y**2, x, y) == 1 assert degree(x*y**2, y, x) == 2 raises(ComputationFailed, "degree(1)") def test_Poly_degree_list(): assert Poly(0, x).degree_list() == (-1,) assert Poly(0, x, y).degree_list() == (-1,-1) assert Poly(0, x, y, z).degree_list() == (-1,-1,-1) assert Poly(1, x).degree_list() == (0,) assert Poly(1, x, y).degree_list() == (0,0) assert Poly(1, x, y, z).degree_list() == (0,0,0) assert Poly(x**2*y+x**3*z**2+1).degree_list() == (3,1,2) assert degree_list(1, x) == (0,) assert degree_list(x, x) == (1,) assert degree_list(x*y**2) == (1,2) raises(ComputationFailed, "degree_list(1)") def test_Poly_total_degree(): assert Poly(x**2*y+x**3*z**2+1).total_degree() == 6 def test_Poly_LC(): assert Poly(0, x).LC() == 0 assert Poly(1, x).LC() == 1 assert Poly(2*x**2+x, x).LC() == 2 assert Poly(x*y**7 + 2*x**2*y**3).LC('lex') == 2 assert Poly(x*y**7 + 2*x**2*y**3).LC('grlex') == 1 assert LC(x*y**7 + 2*x**2*y**3, order='lex') == 2 assert LC(x*y**7 + 2*x**2*y**3, order='grlex') == 1 def test_Poly_TC(): assert Poly(0, x).TC() == 0 assert Poly(1, x).TC() == 1 assert Poly(2*x**2+x, x).TC() == 0 def test_Poly_EC(): assert Poly(0, x).EC() == 0 assert Poly(1, x).EC() == 1 assert Poly(2*x**2+x, x).EC() == 1 assert Poly(x*y**7 + 2*x**2*y**3).EC('lex') == 1 assert Poly(x*y**7 + 2*x**2*y**3).EC('grlex') == 2 def test_Poly_nth(): assert Poly(0, x).nth(0) == 0 assert Poly(0, x).nth(1) == 0 assert Poly(1, x).nth(0) == 1 assert Poly(1, x).nth(1) == 0 assert Poly(x**8, x).nth(0) == 0 assert Poly(x**8, x).nth(7) == 0 assert Poly(x**8, x).nth(8) == 1 assert Poly(x**8, x).nth(9) == 0 assert Poly(3*x*y**2 + 1).nth(0, 0) == 1 assert Poly(3*x*y**2 + 1).nth(1, 2) == 3 def test_Poly_LM(): assert Poly(0, x).LM() == (0,) assert Poly(1, x).LM() == (0,) assert Poly(2*x**2+x, x).LM() == (2,) assert Poly(x*y**7 + 2*x**2*y**3).LM('lex') == (2, 3) assert Poly(x*y**7 + 2*x**2*y**3).LM('grlex') == (1, 7) assert LM(x*y**7 + 2*x**2*y**3, order='lex') == x**2*y**3 assert LM(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 def test_Poly_LM_custom_order(): f = Poly(x**2*y**3*z + x**2*y*z**3 + x*y*z + 1) rev_lex = lambda monom: tuple(reversed(monom)) assert f.LM(order='lex') == (2, 3, 1) assert f.LM(order=rev_lex) == (2, 1, 3) def test_Poly_EM(): assert Poly(0, x).EM() == (0,) assert Poly(1, x).EM() == (0,) assert Poly(2*x**2+x, x).EM() == (1,) assert Poly(x*y**7 + 2*x**2*y**3).EM('lex') == (1, 7) assert Poly(x*y**7 + 2*x**2*y**3).EM('grlex') == (2, 3) def test_Poly_LT(): assert Poly(0, x).LT() == ((0,), 0) assert Poly(1, x).LT() == ((0,), 1) assert Poly(2*x**2+x, x).LT() == ((2,), 2) assert Poly(x*y**7 + 2*x**2*y**3).LT('lex') == ((2, 3), 2) assert Poly(x*y**7 + 2*x**2*y**3).LT('grlex') == ((1, 7), 1) assert LT(x*y**7 + 2*x**2*y**3, order='lex') == 2*x**2*y**3 assert LT(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 def test_Poly_ET(): assert Poly(0, x).ET() == ((0,), 0) assert Poly(1, x).ET() == ((0,), 1) assert Poly(2*x**2+x, x).ET() == ((1,), 1) assert Poly(x*y**7 + 2*x**2*y**3).ET('lex') == ((1, 7), 1) assert Poly(x*y**7 + 2*x**2*y**3).ET('grlex') == ((2, 3), 2) def test_Poly_max_norm(): assert Poly(-1, x).max_norm() == 1 assert Poly( 0, x).max_norm() == 0 assert Poly( 1, x).max_norm() == 1 def test_Poly_l1_norm(): assert Poly(-1, x).l1_norm() == 1 assert Poly( 0, x).l1_norm() == 0 assert Poly( 1, x).l1_norm() == 1 def test_Poly_clear_denoms(): coeff, poly = Poly(x + 2, x).clear_denoms() assert coeff == 1 and poly == Poly(x + 2, x, domain='ZZ') and poly.get_domain() == ZZ coeff, poly = Poly(x/2 + 1, x).clear_denoms() assert coeff == 2 and poly == Poly(x + 2, x, domain='QQ') and poly.get_domain() == QQ coeff, poly = Poly(x/2 + 1, x).clear_denoms(convert=True) assert coeff == 2 and poly == Poly(x + 2, x, domain='ZZ') and poly.get_domain() == ZZ coeff, poly = Poly(x/y + 1, x).clear_denoms(convert=True) assert coeff == y and poly == Poly(x + y, x, domain='ZZ[y]') and poly.get_domain() == ZZ[y] def test_Poly_rat_clear_denoms(): f = Poly(x**2/y + 1, x) g = Poly(x**3 + y, x) assert f.rat_clear_denoms(g) == \ (Poly(x**2 + y, x), Poly(y*x**3 + y**2, x)) f = f.set_domain(EX) g = g.set_domain(EX) assert f.rat_clear_denoms(g) == (f, g) def test_Poly_integrate(): assert Poly(x + 1).integrate() == Poly(x**2/2 + x) assert Poly(x + 1).integrate(x) == Poly(x**2/2 + x) assert Poly(x + 1).integrate((x, 1)) == Poly(x**2/2 + x) assert Poly(x*y + 1).integrate(x) == Poly(x**2*y/2 + x) assert Poly(x*y + 1).integrate(y) == Poly(x*y**2/2 + y) assert Poly(x*y + 1).integrate(x, x) == Poly(x**3*y/6 + x**2/2) assert Poly(x*y + 1).integrate(y, y) == Poly(x*y**3/6 + y**2/2) assert Poly(x*y + 1).integrate((x, 2)) == Poly(x**3*y/6 + x**2/2) assert Poly(x*y + 1).integrate((y, 2)) == Poly(x*y**3/6 + y**2/2) assert Poly(x*y + 1).integrate(x, y) == Poly(x**2*y**2/4 + x*y) assert Poly(x*y + 1).integrate(y, x) == Poly(x**2*y**2/4 + x*y) def test_Poly_diff(): assert Poly(x**2 + x).diff() == Poly(2*x + 1) assert Poly(x**2 + x).diff(x) == Poly(2*x + 1) assert Poly(x**2 + x).diff((x, 1)) == Poly(2*x + 1) assert Poly(x**2*y**2 + x*y).diff(x) == Poly(2*x*y**2 + y) assert Poly(x**2*y**2 + x*y).diff(y) == Poly(2*x**2*y + x) assert Poly(x**2*y**2 + x*y).diff(x, x) == Poly(2*y**2, x, y) assert Poly(x**2*y**2 + x*y).diff(y, y) == Poly(2*x**2, x, y) assert Poly(x**2*y**2 + x*y).diff((x, 2)) == Poly(2*y**2, x, y) assert Poly(x**2*y**2 + x*y).diff((y, 2)) == Poly(2*x**2, x, y) assert Poly(x**2*y**2 + x*y).diff(x, y) == Poly(4*x*y + 1) assert Poly(x**2*y**2 + x*y).diff(y, x) == Poly(4*x*y + 1) def test_Poly_eval(): assert Poly(0, x).eval(7) == 0 assert Poly(1, x).eval(7) == 1 assert Poly(x, x).eval(7) == 7 assert Poly(0, x).eval(0, 7) == 0 assert Poly(1, x).eval(0, 7) == 1 assert Poly(x, x).eval(0, 7) == 7 assert Poly(0, x).eval(x, 7) == 0 assert Poly(1, x).eval(x, 7) == 1 assert Poly(x, x).eval(x, 7) == 7 assert Poly(0, x).eval('x', 7) == 0 assert Poly(1, x).eval('x', 7) == 1 assert Poly(x, x).eval('x', 7) == 7 raises(PolynomialError, "Poly(1, x).eval(1, 7)") raises(PolynomialError, "Poly(1, x).eval(y, 7)") raises(PolynomialError, "Poly(1, x).eval('y', 7)") assert Poly(123, x, y).eval(7) == Poly(123, y) assert Poly(2*y, x, y).eval(7) == Poly(2*y, y) assert Poly(x*y, x, y).eval(7) == Poly(7*y, y) assert Poly(123, x, y).eval(x, 7) == Poly(123, y) assert Poly(2*y, x, y).eval(x, 7) == Poly(2*y, y) assert Poly(x*y, x, y).eval(x, 7) == Poly(7*y, y) assert Poly(123, x, y).eval(y, 7) == Poly(123, x) assert Poly(2*y, x, y).eval(y, 7) == Poly(14, x) assert Poly(x*y, x, y).eval(y, 7) == Poly(7*x, x) assert Poly(x*y + y, x, y).eval({x: 7}) == Poly(8*y, y) assert Poly(x*y + y, x, y).eval({y: 7}) == Poly(7*x + 7, x) assert Poly(x*y + y, x, y).eval({x: 6, y: 7}) == 49 assert Poly(x*y + y, x, y).eval({x: 7, y: 6}) == 48 Poly(x+1, domain='ZZ').eval(S(1)/2) == S(3)/2 Poly(x+1, domain='ZZ').eval(sqrt(2)) == sqrt(2) + 1 raises(DomainError, "Poly(x+1, domain='ZZ').eval(S(1)/2, auto=False)") def test_parallel_poly_from_expr(): assert parallel_poly_from_expr([x-1, x**2-1], x)[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([Poly(x-1, x), x**2-1], x)[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([x-1, Poly(x**2-1, x)], x)[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([Poly(x-1, x), Poly(x**2-1, x)], x)[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([x-1, x**2-1], x, y)[0] == [Poly(x-1, x, y), Poly(x**2-1, x, y)] assert parallel_poly_from_expr([Poly(x-1, x), x**2-1], x, y)[0] == [Poly(x-1, x, y), Poly(x**2-1, x, y)] assert parallel_poly_from_expr([x-1, Poly(x**2-1, x)], x, y)[0] == [Poly(x-1, x, y), Poly(x**2-1, x, y)] assert parallel_poly_from_expr([Poly(x-1, x), Poly(x**2-1, x)], x, y)[0] == [Poly(x-1, x, y), Poly(x**2-1, x, y)] assert parallel_poly_from_expr([x-1, x**2-1])[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([Poly(x-1, x), x**2-1])[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([x-1, Poly(x**2-1, x)])[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([Poly(x-1, x), Poly(x**2-1, x)])[0] == [Poly(x-1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([1, x**2-1])[0] == [Poly(1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([1, x**2-1])[0] == [Poly(1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([1, Poly(x**2-1, x)])[0] == [Poly(1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([1, Poly(x**2-1, x)])[0] == [Poly(1, x), Poly(x**2-1, x)] assert parallel_poly_from_expr([x**2-1, 1])[0] == [Poly(x**2-1, x), Poly(1, x)] assert parallel_poly_from_expr([x**2-1, 1])[0] == [Poly(x**2-1, x), Poly(1, x)] assert parallel_poly_from_expr([Poly(x**2-1, x), 1])[0] == [Poly(x**2-1, x), Poly(1, x)] assert parallel_poly_from_expr([Poly(x**2-1, x), 1])[0] == [Poly(x**2-1, x), Poly(1, x)] raises(PolificationFailed, "parallel_poly_from_expr([0, 1])") def test_pdiv(): f, g = x**2 - y**2, x - y q, r = x + y, 0 F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] assert F.pdiv(G) == (Q, R) assert F.prem(G) == R assert F.pquo(G) == Q assert F.pexquo(G) == Q assert pdiv(f, g) == (q, r) assert prem(f, g) == r assert pquo(f, g) == q assert pexquo(f, g) == q assert pdiv(f, g, x, y) == (q, r) assert prem(f, g, x, y) == r assert pquo(f, g, x, y) == q assert pexquo(f, g, x, y) == q assert pdiv(f, g, (x,y)) == (q, r) assert prem(f, g, (x,y)) == r assert pquo(f, g, (x,y)) == q assert pexquo(f, g, (x,y)) == q assert pdiv(F, G) == (Q, R) assert prem(F, G) == R assert pquo(F, G) == Q assert pexquo(F, G) == Q assert pdiv(f, g, polys=True) == (Q, R) assert prem(f, g, polys=True) == R assert pquo(f, g, polys=True) == Q assert pexquo(f, g, polys=True) == Q assert pdiv(F, G, polys=False) == (q, r) assert prem(F, G, polys=False) == r assert pquo(F, G, polys=False) == q assert pexquo(F, G, polys=False) == q raises(ComputationFailed, "pdiv(4, 2)") raises(ComputationFailed, "prem(4, 2)") raises(ComputationFailed, "pquo(4, 2)") raises(ComputationFailed, "pexquo(4, 2)") def test_div(): f, g = x**2 - y**2, x - y q, r = x + y, 0 F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] assert F.div(G) == (Q, R) assert F.rem(G) == R assert F.quo(G) == Q assert F.exquo(G) == Q assert div(f, g) == (q, r) assert rem(f, g) == r assert quo(f, g) == q assert exquo(f, g) == q assert div(f, g, x, y) == (q, r) assert rem(f, g, x, y) == r assert quo(f, g, x, y) == q assert exquo(f, g, x, y) == q assert div(f, g, (x,y)) == (q, r) assert rem(f, g, (x,y)) == r assert quo(f, g, (x,y)) == q assert exquo(f, g, (x,y)) == q assert div(F, G) == (Q, R) assert rem(F, G) == R assert quo(F, G) == Q assert exquo(F, G) == Q assert div(f, g, polys=True) == (Q, R) assert rem(f, g, polys=True) == R assert quo(f, g, polys=True) == Q assert exquo(f, g, polys=True) == Q assert div(F, G, polys=False) == (q, r) assert rem(F, G, polys=False) == r assert quo(F, G, polys=False) == q assert exquo(F, G, polys=False) == q raises(ComputationFailed, "div(4, 2)") raises(ComputationFailed, "rem(4, 2)") raises(ComputationFailed, "quo(4, 2)") raises(ComputationFailed, "exquo(4, 2)") f, g = x**2 + 1, 2*x - 4 qz, rz = 0, x**2 + 1 qq, rq = x/2 + 1, 5 assert div(f, g) == (qq, rq) assert div(f, g, auto=True) == (qq, rq) assert div(f, g, auto=False) == (qz, rz) assert div(f, g, domain=ZZ) == (qz, rz) assert div(f, g, domain=QQ) == (qq, rq) assert div(f, g, domain=ZZ, auto=True) == (qq, rq) assert div(f, g, domain=ZZ, auto=False) == (qz, rz) assert div(f, g, domain=QQ, auto=True) == (qq, rq) assert div(f, g, domain=QQ, auto=False) == (qq, rq) assert rem(f, g) == rq assert rem(f, g, auto=True) == rq assert rem(f, g, auto=False) == rz assert rem(f, g, domain=ZZ) == rz assert rem(f, g, domain=QQ) == rq assert rem(f, g, domain=ZZ, auto=True) == rq assert rem(f, g, domain=ZZ, auto=False) == rz assert rem(f, g, domain=QQ, auto=True) == rq assert rem(f, g, domain=QQ, auto=False) == rq assert quo(f, g) == qq assert quo(f, g, auto=True) == qq assert quo(f, g, auto=False) == qz assert quo(f, g, domain=ZZ) == qz assert quo(f, g, domain=QQ) == qq assert quo(f, g, domain=ZZ, auto=True) == qq assert quo(f, g, domain=ZZ, auto=False) == qz assert quo(f, g, domain=QQ, auto=True) == qq assert quo(f, g, domain=QQ, auto=False) == qq f, g, q = x**2, 2*x, x/2 assert exquo(f, g) == q assert exquo(f, g, auto=True) == q raises(ExactQuotientFailed, "exquo(f, g, auto=False)") raises(ExactQuotientFailed, "exquo(f, g, domain=ZZ)") assert exquo(f, g, domain=QQ) == q assert exquo(f, g, domain=ZZ, auto=True) == q raises(ExactQuotientFailed, "exquo(f, g, domain=ZZ, auto=False)") assert exquo(f, g, domain=QQ, auto=True) == q assert exquo(f, g, domain=QQ, auto=False) == q f, g = Poly(x**2), Poly(x) q, r = f.div(g) assert q.get_domain().is_ZZ and r.get_domain().is_ZZ r = f.rem(g) assert r.get_domain().is_ZZ q = f.quo(g) assert q.get_domain().is_ZZ q = f.exquo(g) assert q.get_domain().is_ZZ def test_gcdex(): f, g = 2*x, x**2 - 16 s, t, h = x/32, -Rational(1,16), 1 F, G, S, T, H = [ Poly(u, x, domain='QQ') for u in (f, g, s, t, h) ] assert F.half_gcdex(G) == (S, H) assert F.gcdex(G) == (S, T, H) assert F.invert(G) == S assert half_gcdex(f, g) == (s, h) assert gcdex(f, g) == (s, t, h) assert invert(f, g) == s assert half_gcdex(f, g, x) == (s, h) assert gcdex(f, g, x) == (s, t, h) assert invert(f, g, x) == s assert half_gcdex(f, g, (x,)) == (s, h) assert gcdex(f, g, (x,)) == (s, t, h) assert invert(f, g, (x,)) == s assert half_gcdex(F, G) == (S, H) assert gcdex(F, G) == (S, T, H) assert invert(F, G) == S assert half_gcdex(f, g, polys=True) == (S, H) assert gcdex(f, g, polys=True) == (S, T, H) assert invert(f, g, polys=True) == S assert half_gcdex(F, G, polys=False) == (s, h) assert gcdex(F, G, polys=False) == (s, t, h) assert invert(F, G, polys=False) == s assert half_gcdex(100, 2004) == (-20, 4) assert gcdex(100, 2004) == (-20, 1, 4) assert invert(3, 7) == 5 raises(DomainError, "half_gcdex(x + 1, 2*x + 1, auto=False)") raises(DomainError, "gcdex(x + 1, 2*x + 1, auto=False)") raises(DomainError, "invert(x + 1, 2*x + 1, auto=False)") def test_revert(): f = Poly(1 - x**2/2 + x**4/24 - x**6/720) g = Poly(61*x**6/720 + 5*x**4/24 + x**2/2 + 1) assert f.revert(8) == g def test_subresultants(): f, g, h = x**2 - 2*x + 1, x**2 - 1, 2*x - 2 F, G, H = Poly(f), Poly(g), Poly(h) assert F.subresultants(G) == [F, G, H] assert subresultants(f, g) == [f, g, h] assert subresultants(f, g, x) == [f, g, h] assert subresultants(f, g, (x,)) == [f, g, h] assert subresultants(F, G) == [F, G, H] assert subresultants(f, g, polys=True) == [F, G, H] assert subresultants(F, G, polys=False) == [f, g, h] raises(ComputationFailed, "subresultants(4, 2)") def test_resultant(): f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 F, G = Poly(f), Poly(g) assert F.resultant(G) == h assert resultant(f, g) == h assert resultant(f, g, x) == h assert resultant(f, g, (x,)) == h assert resultant(F, G) == h assert resultant(f, g, polys=True) == h assert resultant(F, G, polys=False) == h f, g, h = x - a, x - b, a - b F, G, H = Poly(f), Poly(g), Poly(h) assert F.resultant(G) == H assert resultant(f, g) == h assert resultant(f, g, x) == h assert resultant(f, g, (x,)) == h assert resultant(F, G) == H assert resultant(f, g, polys=True) == H assert resultant(F, G, polys=False) == h raises(ComputationFailed, "resultant(4, 2)") def test_discriminant(): f, g = x**3 + 3*x**2 + 9*x - 13, -11664 F = Poly(f) assert F.discriminant() == g assert discriminant(f) == g assert discriminant(f, x) == g assert discriminant(f, (x,)) == g assert discriminant(F) == g assert discriminant(f, polys=True) == g assert discriminant(F, polys=False) == g f, g = a*x**2 + b*x + c, b**2 - 4*a*c F, G = Poly(f), Poly(g) assert F.discriminant() == G assert discriminant(f) == g assert discriminant(f, x, a, b, c) == g assert discriminant(f, (x, a, b, c)) == g assert discriminant(F) == G assert discriminant(f, polys=True) == G assert discriminant(F, polys=False) == g raises(ComputationFailed, "discriminant(4)") def test_gcd_list(): F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] assert gcd_list(F) == x - 1 assert gcd_list(F, polys=True) == Poly(x - 1) assert gcd_list([]) == 0 assert gcd_list([1, 2]) == 1 assert gcd_list([4, 6, 8]) == 2 gcd = gcd_list([], x) assert gcd.is_Number and gcd is S.Zero gcd = gcd_list([], x, polys=True) assert gcd.is_Poly and gcd.is_zero raises(ComputationFailed, "gcd_list([], polys=True)") def test_lcm_list(): F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] assert lcm_list(F) == x**5 - x**4 - 2*x**3 - x**2 + x + 2 assert lcm_list(F, polys=True) == Poly(x**5 - x**4 - 2*x**3 - x**2 + x + 2) assert lcm_list([]) == 1 assert lcm_list([1, 2]) == 2 assert lcm_list([4, 6, 8]) == 24 lcm = lcm_list([], x) assert lcm.is_Number and lcm is S.One lcm = lcm_list([], x, polys=True) assert lcm.is_Poly and lcm.is_one raises(ComputationFailed, "lcm_list([], polys=True)") def test_gcd(): f, g = x**3 - 1, x**2 - 1 s, t = x**2 + x + 1, x + 1 h, r = x - 1, x**4 + x**3 - x - 1 F, G, S, T, H, R = [ Poly(u) for u in (f, g, s, t, h, r) ] assert F.cofactors(G) == (H, S, T) assert F.gcd(G) == H assert F.lcm(G) == R assert cofactors(f, g) == (h, s, t) assert gcd(f, g) == h assert lcm(f, g) == r assert cofactors(f, g, x) == (h, s, t) assert gcd(f, g, x) == h assert lcm(f, g, x) == r assert cofactors(f, g, (x,)) == (h, s, t) assert gcd(f, g, (x,)) == h assert lcm(f, g, (x,)) == r assert cofactors(F, G) == (H, S, T) assert gcd(F, G) == H assert lcm(F, G) == R assert cofactors(f, g, polys=True) == (H, S, T) assert gcd(f, g, polys=True) == H assert lcm(f, g, polys=True) == R assert cofactors(F, G, polys=False) == (h, s, t) assert gcd(F, G, polys=False) == h assert lcm(F, G, polys=False) == r f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 h, s, t = g, 1.0*x + 1.0, 1.0 assert cofactors(f, g) == (h, s, t) assert gcd(f, g) == h assert lcm(f, g) == f f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 h, s, t = g, 1.0*x + 1.0, 1.0 assert cofactors(f, g) == (h, s, t) assert gcd(f, g) == h assert lcm(f, g) == f assert cofactors(8, 6) == (2, 4, 3) assert gcd(8, 6) == 2 assert lcm(8, 6) == 24 f, g = x**2 - 3*x - 4, x**3 - 4*x**2 + x - 4 l = x**4 - 3*x**3 - 3*x**2 - 3*x - 4 h, s, t = x - 4, x + 1, x**2 + 1 assert cofactors(f, g, modulus=11) == (h, s, t) assert gcd(f, g, modulus=11) == h assert lcm(f, g, modulus=11) == l f, g = x**2 + 8*x + 7, x**3 + 7*x**2 + x + 7 l = x**4 + 8*x**3 + 8*x**2 + 8*x + 7 h, s, t = x + 7, x + 1, x**2 + 1 assert cofactors(f, g, modulus=11, symmetric=False) == (h, s, t) assert gcd(f, g, modulus=11, symmetric=False) == h assert lcm(f, g, modulus=11, symmetric=False) == l def test_terms_gcd(): assert terms_gcd(1) == 1 assert terms_gcd(1, x) == 1 assert terms_gcd(x - 1) == x - 1 assert terms_gcd(-x - 1) == -x - 1 assert terms_gcd(2*x + 3) != Mul(1, 2*x + 3, evaluate=False) assert terms_gcd(6*x + 4) == Mul(2, 3*x + 2, evaluate=False) assert terms_gcd(x**3*y + x*y**3) == x*y*(x**2 + y**2) assert terms_gcd(2*x**3*y + 2*x*y**3) == 2*x*y*(x**2 + y**2) assert terms_gcd(x**3*y/2 + x*y**3/2) == x*y/2*(x**2 + y**2) assert terms_gcd(x**3*y + 2*x*y**3) == x*y*(x**2 + 2*y**2) assert terms_gcd(2*x**3*y + 4*x*y**3) == 2*x*y*(x**2 + 2*y**2) assert terms_gcd(2*x**3*y/3 + 4*x*y**3/5) == 2*x*y/15*(5*x**2 + 6*y**2) assert terms_gcd(2.0*x**3*y + 4.1*x*y**3) == x*y*(2.0*x**2 + 4.1*y**2) def test_trunc(): f, g = x**5 + 2*x**4 + 3*x**3 + 4*x**2 + 5*x + 6, x**5 - x**4 + x**2 - x F, G = Poly(f), Poly(g) assert F.trunc(3) == G assert trunc(f, 3) == g assert trunc(f, 3, x) == g assert trunc(f, 3, (x,)) == g assert trunc(F, 3) == G assert trunc(f, 3, polys=True) == G assert trunc(F, 3, polys=False) == g f, g = 6*x**5 + 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, -x**4 + x**3 - x + 1 F, G = Poly(f), Poly(g) assert F.trunc(3) == G assert trunc(f, 3) == g assert trunc(f, 3, x) == g assert trunc(f, 3, (x,)) == g assert trunc(F, 3) == G assert trunc(f, 3, polys=True) == G assert trunc(F, 3, polys=False) == g f = Poly(x**2 + 2*x + 3, modulus=5) assert f.trunc(2) == Poly(x**2 + 1, modulus=5) def test_monic(): f, g = 2*x - 1, x - S(1)/2 F, G = Poly(f, domain='QQ'), Poly(g) assert F.monic() == G assert monic(f) == g assert monic(f, x) == g assert monic(f, (x,)) == g assert monic(F) == G assert monic(f, polys=True) == G assert monic(F, polys=False) == g raises(ComputationFailed, "monic(4)") assert monic(2*x**2 + 6*x + 4, auto=False) == x**2 + 3*x + 2 raises(ExactQuotientFailed, "monic(2*x + 6*x + 1, auto=False)") assert monic(2.0*x**2 + 6.0*x + 4.0) == 1.0*x**2 + 3.0*x + 2.0 assert monic(2*x**2 + 3*x + 4, modulus=5) == x**2 - x + 2 def test_content(): f, F = 4*x + 2, Poly(4*x + 2) F.content() == 2 content(f) == 2 raises(ComputationFailed, "content(4)") f = Poly(2*x, modulus=3) assert f.content() == 1 def test_primitive(): f, g = 4*x + 2, 2*x + 1 F, G = Poly(f), Poly(g) assert F.primitive() == (2, G) assert primitive(f) == (2, g) assert primitive(f, x) == (2, g) assert primitive(f, (x,)) == (2, g) assert primitive(F) == (2, G) assert primitive(f, polys=True) == (2, G) assert primitive(F, polys=False) == (2, g) raises(ComputationFailed, "primitive(4)") f = Poly(2*x, modulus=3) g = Poly(2.0*x, domain=RR) assert f.primitive() == (1, f) assert g.primitive() == (1.0, g) def test_compose(): f = x**12+20*x**10+150*x**8+500*x**6+625*x**4-2*x**3-10*x+9 g = x**4 - 2*x + 9 h = x**3 + 5*x F, G, H = map(Poly, (f, g, h)) assert G.compose(H) == F assert compose(g, h) == f assert compose(g, h, x) == f assert compose(g, h, (x,)) == f assert compose(G, H) == F assert compose(g, h, polys=True) == F assert compose(G, H, polys=False) == f assert F.decompose() == [G, H] assert decompose(f) == [g, h] assert decompose(f, x) == [g, h] assert decompose(f, (x,)) == [g, h] assert decompose(F) == [G, H] assert decompose(f, polys=True) == [G, H] assert decompose(F, polys=False) == [g, h] raises(ComputationFailed, "compose(4, 2)") raises(ComputationFailed, "decompose(4)") assert compose(x**2 - y**2, x - y, x, y) == x**2 - 2*x*y assert compose(x**2 - y**2, x - y, y, x) == -y**2 + 2*x*y def test_shift(): assert Poly(x**2 - 2*x + 1, x).shift(2) == Poly(x**2 + 2*x + 1, x) def test_sturm(): f, F = x, Poly(x, domain='QQ') g, G = 1, Poly(1, x, domain='QQ') assert F.sturm() == [F, G] assert sturm(f) == [f, g] assert sturm(f, x) == [f, g] assert sturm(f, (x,)) == [f, g] assert sturm(F) == [F, G] assert sturm(f, polys=True) == [F, G] assert sturm(F, polys=False) == [f, g] raises(ComputationFailed, "sturm(4)") raises(DomainError, "sturm(f, auto=False)") f = Poly(S(1024)/(15625*pi**8)*x**5 \ - S(4096)/(625*pi**8)*x**4 \ + S(32)/(15625*pi**4)*x**3 \ - S(128)/(625*pi**4)*x**2 \ + S(1)/62500*x \ - S(1)/625, x, domain='ZZ(pi)') assert sturm(f) == \ [Poly(x**3 - 100*x**2 + pi**4/64*x - 25*pi**4/16, x, domain='ZZ(pi)'), Poly(3*x**2 - 200*x + pi**4/64, x, domain='ZZ(pi)'), Poly((S(20000)/9 - pi**4/96)*x + 25*pi**4/18, x, domain='ZZ(pi)'), Poly((-3686400000000*pi**4 - 11520000*pi**8 - 9*pi**12)/(26214400000000 - 245760000*pi**4 + 576*pi**8), x, domain='ZZ(pi)')] def test_gff(): f = x**5 + 2*x**4 - x**3 - 2*x**2 assert Poly(f).gff_list() == [(Poly(x), 1), (Poly(x + 2), 4)] assert gff_list(f) == [(x, 1), (x + 2, 4)] raises(NotImplementedError, "gff(f)") f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) assert Poly(f).gff_list() == [(Poly(x**2 - 5*x + 4), 1), (Poly(x**2 - 5*x + 4), 2), (Poly(x), 3)] assert gff_list(f) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] raises(NotImplementedError, "gff(f)") def test_sqf_norm(): assert sqf_norm(x**2-2, extension=sqrt(3)) == \ (1, x**2 - 2*sqrt(3)*x + 1, x**4 - 10*x**2 + 1) assert sqf_norm(x**2-3, extension=sqrt(2)) == \ (1, x**2 - 2*sqrt(2)*x - 1, x**4 - 10*x**2 + 1) assert Poly(x**2-2, extension=sqrt(3)).sqf_norm() == \ (1, Poly(x**2 - 2*sqrt(3)*x + 1, x, extension=sqrt(3)), Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) assert Poly(x**2-3, extension=sqrt(2)).sqf_norm() == \ (1, Poly(x**2 - 2*sqrt(2)*x - 1, x, extension=sqrt(2)), Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) def test_sqf(): f = x**5 - x**3 - x**2 + 1 g = x**3 + 2*x**2 + 2*x + 1 h = x - 1 p = x**4 + x**3 - x - 1 F, G, H, P = map(Poly, (f, g, h, p)) assert F.sqf_part() == P assert sqf_part(f) == p assert sqf_part(f, x) == p assert sqf_part(f, (x,)) == p assert sqf_part(F) == P assert sqf_part(f, polys=True) == P assert sqf_part(F, polys=False) == p assert F.sqf_list() == (1, [(G, 1), (H, 2)]) assert sqf_list(f) == (1, [(g, 1), (h, 2)]) assert sqf_list(f, x) == (1, [(g, 1), (h, 2)]) assert sqf_list(f, (x,)) == (1, [(g, 1), (h, 2)]) assert sqf_list(F) == (1, [(G, 1), (H, 2)]) assert sqf_list(f, polys=True) == (1, [(G, 1), (H, 2)]) assert sqf_list(F, polys=False) == (1, [(g, 1), (h, 2)]) assert F.sqf_list_include() == [(G, 1), (H, 2)] raises(ComputationFailed, "sqf_part(4)") assert sqf(1) == 1 assert sqf_list(1) == (1, []) assert sqf((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 assert sqf(f) == g*h**2 assert sqf(f, x) == g*h**2 assert sqf(f, (x,)) == g*h**2 d = x**2 + y**2 assert sqf(f/d) == (g*h**2)/d assert sqf(f/d, x) == (g*h**2)/d assert sqf(f/d, (x,)) == (g*h**2)/d assert sqf(x - 1) == x - 1 assert sqf(-x - 1) == -x - 1 assert sqf(x - 1) != Mul(1, x - 1, evaluate=False) assert sqf(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) assert sqf((6*x - 10)/(3*x - 6)) == S(2)/3*((3*x - 5)/(x - 2)) assert sqf(Poly(x**2 - 2*x + 1)) == (x - 1)**2 f = 3 + x - x*(1 + x) + x**2 assert sqf(f) == 3 f = (x**2 + 2*x + 1)**20000000000 assert sqf(f) == (x + 1)**40000000000 assert sqf_list(f) == (1, [(x + 1, 40000000000)]) def test_factor(): f = x**5 - x**3 - x**2 + 1 u = x + 1 v = x - 1 w = x**2 + x + 1 F, U, V, W = map(Poly, (f, u, v, w)) assert F.factor_list() == (1, [(U, 1), (V, 2), (W, 1)]) assert factor_list(f) == (1, [(u, 1), (v, 2), (w, 1)]) assert factor_list(f, x) == (1, [(u, 1), (v, 2), (w, 1)]) assert factor_list(f, (x,)) == (1, [(u, 1), (v, 2), (w, 1)]) assert factor_list(F) == (1, [(U, 1), (V, 2), (W, 1)]) assert factor_list(f, polys=True) == (1, [(U, 1), (V, 2), (W, 1)]) assert factor_list(F, polys=False) == (1, [(u, 1), (v, 2), (w, 1)]) assert F.factor_list_include() == [(U, 1), (V, 2), (W, 1)] assert factor_list(1) == (1, []) assert factor_list(6) == (6, []) assert factor(1) == 1 assert factor(6) == 6 assert factor_list(3*x) == (3, [(x, 1)]) assert factor_list(3*x**2) == (3, [(x, 2)]) assert factor(3*x) == 3*x assert factor(3*x**2) == 3*x**2 assert factor((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 assert factor(f) == u*v**2*w assert factor(f, x) == u*v**2*w assert factor(f, (x,)) == u*v**2*w g, p, q = x**2 - y**2, x - y, x + y assert factor(f/g) == (u*v**2*w)/(p*q) assert factor(f/g, x) == (u*v**2*w)/(p*q) assert factor(f/g, (x,)) == (u*v**2*w)/(p*q) f = Poly(sin(1)*x + 1, x, domain=EX) assert f.factor_list() == (1, [(f, 1)]) f = x**4 + 1 assert factor(f) == f assert factor(f, extension=I) == (x**2 - I)*(x**2 + I) assert factor(f, gaussian=True) == (x**2 - I)*(x**2 + I) assert factor(f, extension=sqrt(2)) == (x**2 + sqrt(2)*x + 1)*(x**2 - sqrt(2)*x + 1) f = x**2 + 2*sqrt(2)*x + 2 assert factor(f, extension=sqrt(2)) == (x + sqrt(2))**2 assert factor(f**3, extension=sqrt(2)) == (x + sqrt(2))**6 assert factor(x**2 - 2*y**2, extension=sqrt(2)) == \ (x + sqrt(2)*y)*(x - sqrt(2)*y) assert factor(2*x**2 - 4*y**2, extension=sqrt(2)) == \ 2*((x + sqrt(2)*y)*(x - sqrt(2)*y)) assert factor(x - 1) == x - 1 assert factor(-x - 1) == -x - 1 assert factor(x**11 + x + 1, modulus=65537, symmetric=True) == \ (x**2 + x + 1)*(x**9 - x**8 + x**6 - x**5 + x**3 - x** 2 + 1) assert factor(x**11 + x + 1, modulus=65537, symmetric=False) == \ (x**2 + x + 1)*(x**9 + 65536*x**8 + x**6 + 65536*x**5 + x**3 + 65536*x** 2 + 1) f = x/pi + x*sin(x)/pi g = y/(pi**2 + 2*pi + 1) + y*sin(x)/(pi**2 + 2*pi + 1) assert factor(f) == x*(sin(x) + 1)/pi assert factor(g) == y*(sin(x) + 1)/(pi + 1)**2 assert factor(Eq(x**2 + 2*x + 1, x**3 + 1)) == Eq((x + 1)**2, (x + 1)*(x**2 - x + 1)) f = (x**2 - 1)/(x**2 + 4*x + 4) assert factor(f) == (x + 1)*(x - 1)/(x + 2)**2 assert factor(f, x) == (x + 1)*(x - 1)/(x + 2)**2 f = 3 + x - x*(1 + x) + x**2 assert factor(f) == 3 assert factor(f, x) == 3 assert factor(1/(x**2 + 2*x + 1/x) - 1) == -((1 - x + 2*x**2 + x**3)/(1 + 2*x**2 + x**3)) assert factor(f, expand=False) == f raises(PolynomialError, "factor(f, x, expand=False)") raises(FlagError, "factor(x**2 - 1, polys=True)") assert factor([x, Eq(x**2 - y**2, Tuple(x**2 - z**2, 1/x + 1/y))]) == \ [x, Eq((x - y)*(x + y), Tuple((x - z)*(x + z), (x + y)/x/y))] assert not isinstance(Poly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) == True assert isinstance(PurePoly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) == True def test_factor_large(): f = (x**2 + 4*x + 4)**10000000*(x**2 + 1)*(x**2 + 2*x + 1)**1234567 g = ((x**2 + 2*x + 1)**3000*y**2 + (x**2 + 2*x + 1)**3000*2*y + (x**2 + 2*x + 1)**3000) assert factor(f) == (x + 2)**20000000*(x**2 + 1)*(x + 1)**2469134 assert factor(g) == (x + 1)**6000*(y + 1)**2 assert factor_list(f) == (1, [(x + 1, 2469134), (x + 2, 20000000), (x**2 + 1, 1)]) assert factor_list(g) == (1, [(y + 1, 2), (x + 1, 6000)]) f = (x**2 - y**2)**200000*(x**7 + 1) g = (x**2 + y**2)**200000*(x**7 + 1) assert factor(f) == \ (x + 1)*(x - y)**200000*(x + y)**200000*(x**6 - x**5 + x**4 - x**3 + x**2 - x + 1) assert factor(g, gaussian=True) == \ (x + 1)*(x - I*y)**200000*(x + I*y)**200000*(x**6 - x**5 + x**4 - x**3 + x**2 - x + 1) assert factor_list(f) == \ (1, [(x + 1, 1), (x - y, 200000), (x + y, 200000), (x**6 - x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) assert factor_list(g, gaussian=True) == \ (1, [(x + 1, 1), (x - I*y, 200000), (x + I*y, 200000), (x**6 - x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) @XFAIL def test_factor_noeval(): assert factor(6*x - 10) == 2*(3*x - 5) assert factor((6*x - 10)/(3*x - 6)) == S(2)/3*((3*x - 5)/(x - 2)) def test_intervals(): assert intervals(0) == [] assert intervals(1) == [] assert intervals(x, sqf=True) == [(0, 0)] assert intervals(x) == [((0, 0), 1)] assert intervals(x**128) == [((0, 0), 128)] assert intervals([x**2, x**4]) == [((0, 0), {0: 2, 1: 4})] f = Poly((2*x/5 - S(17)/3)*(4*x + S(1)/257)) assert f.intervals(sqf=True) == [(-1, 0), (14, 15)] assert f.intervals() == [((-1, 0), 1), ((14, 15), 1)] assert f.intervals(fast=True, sqf=True) == [(-1, 0), (14, 15)] assert f.intervals(fast=True) == [((-1, 0), 1), ((14, 15), 1)] assert f.intervals(eps=S(1)/10) == f.intervals(eps=0.1) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert f.intervals(eps=S(1)/100) == f.intervals(eps=0.01) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert f.intervals(eps=S(1)/1000) == f.intervals(eps=0.001) == \ [((-S(1)/1005, 0), 1), ((S(85)/6, S(85)/6), 1)] assert f.intervals(eps=S(1)/10000) == f.intervals(eps=0.0001) == \ [((-S(1)/1028, -S(1)/1028), 1), ((S(85)/6, S(85)/6), 1)] f = (2*x/5 - S(17)/3)*(4*x + S(1)/257) assert intervals(f, sqf=True) == [(-1, 0), (14, 15)] assert intervals(f) == [((-1, 0), 1), ((14, 15), 1)] assert intervals(f, eps=S(1)/10) == intervals(f, eps=0.1) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert intervals(f, eps=S(1)/100) == intervals(f, eps=0.01) == \ [((-S(1)/258, 0), 1), ((S(85)/6, S(85)/6), 1)] assert intervals(f, eps=S(1)/1000) == intervals(f, eps=0.001) == \ [((-S(1)/1005, 0), 1), ((S(85)/6, S(85)/6), 1)] assert intervals(f, eps=S(1)/10000) == intervals(f, eps=0.0001) == \ [((-S(1)/1028, -S(1)/1028), 1), ((S(85)/6, S(85)/6), 1)] f = Poly((x**2 - 2)*(x**2-3)**7*(x+1)*(7*x+3)**3) assert f.intervals() == \ [((-2, -S(3)/2), 7), ((-S(3)/2, -1), 1), ((-1, -1), 1), ((-1, 0), 3), ((1, S(3)/2), 1), ((S(3)/2, 2), 7)] assert intervals([x**5 - 200, x**5 - 201]) == \ [((S(75)/26, S(101)/35), {0: 1}), ((S(283)/98, S(26)/9), {1: 1})] assert intervals([x**5 - 200, x**5 - 201], fast=True) == \ [((S(75)/26, S(101)/35), {0: 1}), ((S(283)/98, S(26)/9), {1: 1})] assert intervals([x**2 - 200, x**2 - 201]) == \ [((-S(71)/5, -S(85)/6), {1: 1}), ((-S(85)/6, -14), {0: 1}), ((14, S(85)/6), {0: 1}), ((S(85)/6, S(71)/5), {1: 1})] assert intervals([x+1, x+2, x-1, x+1, 1, x-1, x-1, (x-2)**2]) == \ [((-2, -2), {1: 1}), ((-1, -1), {0: 1, 3: 1}), ((1, 1), {2: 1, 5: 1, 6: 1}), ((2, 2), {7: 2})] f, g, h = x**2 - 2, x**4 - 4*x**2 + 4, x - 1 assert intervals(f, inf=S(7)/4, sqf=True) == [] assert intervals(f, inf=S(7)/5, sqf=True) == [(S(7)/5, S(3)/2)] assert intervals(f, sup=S(7)/4, sqf=True) == [(-2, -1), (1, S(3)/2)] assert intervals(f, sup=S(7)/5, sqf=True) == [(-2, -1)] assert intervals(g, inf=S(7)/4) == [] assert intervals(g, inf=S(7)/5) == [((S(7)/5, S(3)/2), 2)] assert intervals(g, sup=S(7)/4) == [((-2, -1), 2), ((1, S(3)/2), 2)] assert intervals(g, sup=S(7)/5) == [((-2, -1), 2)] assert intervals([g, h], inf=S(7)/4) == [] assert intervals([g, h], inf=S(7)/5) == [((S(7)/5, S(3)/2), {0: 2})] assert intervals([g, h], sup=S(7)/4) == [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, S(3)/2), {0: 2})] assert intervals([g, h], sup=S(7)/5) == [((-2, -1), {0: 2}), ((1, 1), {1: 1})] assert intervals([x+2, x**2 - 2]) == \ [((-2, -2), {0: 1}), ((-2, -1), {1: 1}), ((1, 2), {1: 1})] assert intervals([x+2, x**2 - 2], strict=True) == \ [((-2, -2), {0: 1}), ((-S(3)/2, -1), {1: 1}), ((1, 2), {1: 1})] f = 7*z**4 - 19*z**3 + 20*z**2 + 17*z + 20 assert intervals(f) == [] real_part, complex_part = intervals(f, all=True, sqf=True) assert real_part == [] assert all([ re(a) < re(r) < re(b) and im(a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f)) ]) assert complex_part == [(-S(40)/7 - 40*I/7, 0), (-S(40)/7, 40*I/7), (-40*I/7, S(40)/7), (0, S(40)/7 + 40*I/7)] real_part, complex_part = intervals(f, all=True, sqf=True, eps=S(1)/10) assert real_part == [] assert all([ re(a) < re(r) < re(b) and im(a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f)) ]) raises(ValueError, "intervals(x**2 - 2, eps=10**-100000)") raises(ValueError, "Poly(x**2 - 2).intervals(eps=10**-100000)") raises(ValueError, "intervals([x**2 - 2, x**2 - 3], eps=10**-100000)") def test_refine_root(): f = Poly(x**2 - 2) assert f.refine_root(1, 2, steps=0) == (1, 2) assert f.refine_root(-2, -1, steps=0) == (-2, -1) assert f.refine_root(1, 2, steps=None) == (1, S(3)/2) assert f.refine_root(-2, -1, steps=None) == (-S(3)/2, -1) assert f.refine_root(1, 2, steps=1) == (1, S(3)/2) assert f.refine_root(-2, -1, steps=1) == (-S(3)/2, -1) assert f.refine_root(1, 2, steps=1, fast=True) == (1, S(3)/2) assert f.refine_root(-2, -1, steps=1, fast=True) == (-S(3)/2, -1) assert f.refine_root(1, 2, eps=S(1)/100) == (S(24)/17, S(17)/12) assert f.refine_root(1, 2, eps=1e-2) == (S(24)/17, S(17)/12) raises(PolynomialError, "(f**2).refine_root(1, 2, check_sqf=True)") raises(RefinementFailed, "(f**2).refine_root(1, 2)") raises(RefinementFailed, "(f**2).refine_root(2, 3)") f = x**2 - 2 assert refine_root(f, 1, 2, steps=1) == (1, S(3)/2) assert refine_root(f, -2, -1, steps=1) == (-S(3)/2, -1) assert refine_root(f, 1, 2, steps=1, fast=True) == (1, S(3)/2) assert refine_root(f, -2, -1, steps=1, fast=True) == (-S(3)/2, -1) assert refine_root(f, 1, 2, eps=S(1)/100) == (S(24)/17, S(17)/12) assert refine_root(f, 1, 2, eps=1e-2) == (S(24)/17, S(17)/12) raises(PolynomialError, "refine_root(1, 7, 8, eps=S(1)/100)") raises(ValueError, "Poly(f).refine_root(1, 2, eps=10**-100000)") raises(ValueError, "refine_root(f, 1, 2, eps=10**-100000)") def test_count_roots(): assert count_roots(x**2 - 2) == 2 assert count_roots(x**2 - 2, inf=-oo) == 2 assert count_roots(x**2 - 2, sup=+oo) == 2 assert count_roots(x**2 - 2, inf=-oo, sup=+oo) == 2 assert count_roots(x**2 - 2, inf=-2) == 2 assert count_roots(x**2 - 2, inf=-1) == 1 assert count_roots(x**2 - 2, sup=1) == 1 assert count_roots(x**2 - 2, sup=2) == 2 assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 assert count_roots(x**2 + 2) == 0 assert count_roots(x**2 + 2, inf=-2*I) == 2 assert count_roots(x**2 + 2, sup=+2*I) == 2 assert count_roots(x**2 + 2, inf=-2*I, sup=+2*I) == 2 assert count_roots(x**2 + 2, inf=0) == 0 assert count_roots(x**2 + 2, sup=0) == 0 assert count_roots(x**2 + 2, inf=-I) == 1 assert count_roots(x**2 + 2, sup=+I) == 1 assert count_roots(x**2 + 2, inf=+I/2, sup=+I) == 0 assert count_roots(x**2 + 2, inf=-I, sup=-I/2) == 0 raises(PolynomialError, "count_roots(1)") def test_Poly_root(): f = Poly(2*x**3 - 7*x**2 + 4*x + 4) assert f.root(0) == -S(1)/2 assert f.root(1) == 2 assert f.root(2) == 2 raises(IndexError, "f.root(3)") assert Poly(x**5 + x + 1).root(0) == RootOf(x**3 - x**2 + 1, 0) def test_real_roots(): assert real_roots(x) == [0] assert real_roots(x, multiple=False) == [(0, 1)] assert real_roots(x**3) == [0, 0, 0] assert real_roots(x**3, multiple=False) == [(0, 3)] assert real_roots(x*(x**3 + x + 3)) == [RootOf(x**3 + x + 3, 0), 0] assert real_roots(x*(x**3 + x + 3), multiple=False) == [(RootOf(x**3 + x + 3, 0), 1), (0, 1)] assert real_roots(x**3*(x**3 + x + 3)) == [RootOf(x**3 + x + 3, 0), 0, 0, 0] assert real_roots(x**3*(x**3 + x + 3), multiple=False) == [(RootOf(x**3 + x + 3, 0), 1), (0, 3)] f = 2*x**3 - 7*x**2 + 4*x + 4 g = x**3 + x + 1 assert Poly(f).real_roots() == [-S(1)/2, 2, 2] assert Poly(g).real_roots() == [RootOf(g, 0)] def test_all_roots(): f = 2*x**3 - 7*x**2 + 4*x + 4 g = x**3 + x + 1 assert Poly(f).all_roots() == [-S(1)/2, 2, 2] assert Poly(g).all_roots() == [RootOf(g, 0), RootOf(g, 1), RootOf(g, 2)] def test_nroots(): assert Poly(0, x).nroots() == [] assert Poly(1, x).nroots() == [] assert Poly(x**2 - 1, x).nroots() == [-1.0, 1.0] assert Poly(x**2 + 1, x).nroots() == [-1.0*I, 1.0*I] roots, error = Poly(x**2 - 1, x).nroots(error=True) assert roots == [-1.0, 1.0] and error < 1e25; roots, error = Poly(x**2 + 1, x).nroots(error=True) assert roots == [-1.0*I, 1.0*I] and error < 1e25; roots, error = Poly(x**2/3 - S(1)/3, x).nroots(error=True) assert roots == [-1.0, 1.0] and error < 1e25; roots, error = Poly(x**2/3 + S(1)/3, x).nroots(error=True) assert roots == [-1.0*I, 1.0*I] and error < 1e25; assert Poly(x**2 + 2*I, x).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] assert Poly(x**2 + 2*I, x, extension=I).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] assert Poly(0.2*x + 0.1).nroots() == [-0.5] roots = nroots(x**5 + x + 1, n=5) eps = Float("1e-5") assert re(roots[0]).epsilon_eq(-0.75487, eps) is True assert im(roots[0]) == 0.0 assert re(roots[1]) == -0.5 assert im(roots[1]).epsilon_eq(-0.86602, eps) is True assert re(roots[2]) == -0.5 assert im(roots[2]).epsilon_eq(+0.86602, eps) is True assert re(roots[3]).epsilon_eq(+0.87743, eps) is True assert im(roots[3]).epsilon_eq(-0.74486, eps) is True assert re(roots[4]).epsilon_eq(+0.87743, eps) is True assert im(roots[4]).epsilon_eq(+0.74486, eps) is True eps = Float("1e-6") assert re(roots[0]).epsilon_eq(-0.75487, eps) is False assert im(roots[0]) == 0.0 assert re(roots[1]) == -0.5 assert im(roots[1]).epsilon_eq(-0.86602, eps) is False assert re(roots[2]) == -0.5 assert im(roots[2]).epsilon_eq(+0.86602, eps) is False assert re(roots[3]).epsilon_eq(+0.87743, eps) is False assert im(roots[3]).epsilon_eq(-0.74486, eps) is False assert re(roots[4]).epsilon_eq(+0.87743, eps) is False assert im(roots[4]).epsilon_eq(+0.74486, eps) is False raises(DomainError, "Poly(x + y, x).nroots()") raises(MultivariatePolynomialError, "Poly(x + y).nroots()") assert nroots(x**2 - 1) == [-1.0, 1.0] roots, error = nroots(x**2 - 1, error=True) assert roots == [-1.0, 1.0] and error < 1e25; assert nroots(x + I) == [-1.0*I] assert nroots(x + 2*I) == [-2.0*I] raises(PolynomialError, "nroots(0)") def test_ground_roots(): f = x**6 - 4*x**4 + 4*x**3 - x**2 assert Poly(f).ground_roots() == {S(1): 2, S(0): 2} assert ground_roots(f) == {S(1): 2, S(0): 2} def test_nth_power_roots_poly(): f = x**4 - x**2 + 1 f_2 = (x**2 - x + 1)**2 f_3 = (x**2 + 1)**2 f_4 = (x**2 + x + 1)**2 f_12 = (x - 1)**4 nth_power_roots_poly(f, 1) == f raises(ValueError, "nth_power_roots_poly(f, 0)") raises(ValueError, "nth_power_roots_poly(f, x)") factor(nth_power_roots_poly(f, 2)) == f_2 factor(nth_power_roots_poly(f, 3)) == f_3 factor(nth_power_roots_poly(f, 4)) == f_4 factor(nth_power_roots_poly(f, 12)) == f_12 raises(MultivariatePolynomialError, "nth_power_roots_poly(x + y, 2, x, y)") def test_cancel(): assert cancel(0) == 0 assert cancel(7) == 7 assert cancel(x) == x assert cancel(oo) == oo assert cancel((2, 3)) == (1, 2, 3) assert cancel((1, 0), x) == (1, 1, 0) assert cancel((0, 1), x) == (1, 0, 1) f, g, p, q = 4*x**2-4, 2*x-2, 2*x+2, 1 F, G, P, Q = [ Poly(u, x) for u in (f, g, p, q) ] assert F.cancel(G) == (1, P, Q) assert cancel((f, g)) == (1, p, q) assert cancel((f, g), x) == (1, p, q) assert cancel((f, g), (x,)) == (1, p, q) assert cancel((F, G)) == (1, P, Q) assert cancel((f, g), polys=True) == (1, P, Q) assert cancel((F, G), polys=False) == (1, p, q) f = (x**2 - 2)/(x + sqrt(2)) assert cancel(f) == f assert cancel(f, greedy=False) == x - sqrt(2) f = (x**2 - 2)/(x - sqrt(2)) assert cancel(f) == f assert cancel(f, greedy=False) == x + sqrt(2) assert cancel((x**2/4 - 1, x/2 - 1)) == (S(1)/2, x + 2, 1) assert cancel((x**2-y)/(x-y)) == 1/(x - y)*(x**2 - y) assert cancel((x**2-y**2)/(x-y), x) == x + y assert cancel((x**2-y**2)/(x-y), y) == x + y assert cancel((x**2-y**2)/(x-y)) == x + y assert cancel((x**3-1)/(x**2-1)) == (x**2+x+1)/(x+1) assert cancel((x**3/2-S(1)/2)/(x**2-1)) == (x**2+x+1)/(2*x+2) assert cancel((exp(2*x) + 2*exp(x) + 1)/(exp(x) + 1)) == exp(x) + 1 f = Poly(x**2 - a**2, x) g = Poly(x - a, x) F = Poly(x + a, x) G = Poly(1, x) assert cancel((f, g)) == (1, F, G) f = x**3 + (sqrt(2) - 2)*x**2 - (2*sqrt(2) + 3)*x - 3*sqrt(2) g = x**2 - 2 assert cancel((f, g), extension=True) == (1, x**2 - 2*x - 3, x - sqrt(2)) f = Poly(-2*x + 3, x) g = Poly(-x**9 + x**8 + x**6 - x**5 + 2*x**2 - 3*x + 1, x) assert cancel((f, g)) == (1, -f, -g) f = Poly(y, y, domain='ZZ(x)') g = Poly(1, y, domain='ZZ[x]') assert f.cancel(g) == (1, Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) assert f.cancel(g, include=True) == (Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) f = Poly(5*x*y + x, y, domain='ZZ(x)') g = Poly(2*x**2*y, y, domain='ZZ(x)') assert f.cancel(g, include=True) == (Poly(5*y + 1, y, domain='ZZ(x)'), Poly(2*x*y, y, domain='ZZ(x)')) def test_reduced(): f = 2*x**4 + y**2 - x**2 + y**3 G = [x**3 - x, y**3 - y] Q = [2*x, 1] r = x**2 + y**2 + y assert reduced(f, G) == (Q, r) assert reduced(f, G, x, y) == (Q, r) Q = [Poly(2*x, x, y), Poly(1, x, y)] r = Poly(x**2 + y**2 + y, x, y) assert reduced(f, G, polys=True) == (Q, r) assert reduced(f, G, x, y, polys=True) == (Q, r) assert reduced(1, [1], x) == ([1], 0) raises(ComputationFailed, "reduced(1, [1])") def test_groebner(): assert groebner([], x, y, z) == [] assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex') == [1 + x**2, -1 + y**4] assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex') == [-1 + y**4, z**3, 1 + x**2] assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex', polys=True) == \ [Poly(1 + x**2, x, y), Poly(-1 + y**4, x, y)] assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex', polys=True) == \ [Poly(-1 + y**4, x, y, z), Poly(z**3, x, y, z), Poly(1 + x**2, x, y, z)] F = [3*x**2 + y*z - 5*x - 1, 2*x + 3*x*y + y**2, x - 3*y + x*z - 2*z**2] f = z**9 - x**2*y**3 - 3*x*y**2*z + 11*y*z**2 + x**2*z**2 - 5 G = groebner(F, x, y, z, modulus=7, symmetric=False) assert G == [1 + x + y + 3*z + 2*z**2 + 2*z**3 + 6*z**4 + z**5, 1 + 3*y + y**2 + 6*z**2 + 3*z**3 + 3*z**4 + 3*z**5 + 4*z**6, 1 + 4*y + 4*z + y*z + 4*z**3 + z**4 + z**6, 6 + 6*z + z**2 + 4*z**3 + 3*z**4 + 6*z**5 + 3*z**6 + z**7] Q, r = reduced(f, G, x, y, z, modulus=7, symmetric=False, polys=True) assert sum([ q*g for q, g in zip(Q, G)]) + r == Poly(f, modulus=7) F = [x*y - 2*y, 2*y**2 - x**2] assert groebner(F, order='grevlex') == \ [-2*x**2 + x**3, -x**2 + 2*y**2, -2*y + x*y] assert groebner(F, order='grevlex', field=True) == \ [-2*x**2 + x**3, -x**2/2 + y**2, -2*y + x*y] assert groebner([1], x) == [1] raises(DomainError, "groebner([x**2 + 2.0*y], x, y)") raises(ComputationFailed, "groebner([1])") def test_poly(): assert poly(x) == Poly(x, x) assert poly(y) == Poly(y, y) assert poly(x + y) == Poly(x + y, x, y) assert poly(x + sin(x)) == Poly(x + sin(x), x, sin(x)) assert poly(x + y, wrt=y) == Poly(x + y, y, x) assert poly(x + sin(x), wrt=sin(x)) == Poly(x + sin(x), sin(x), x) assert poly(x*y + 2*x*z**2 + 17) == Poly(x*y + 2*x*z**2 + 17, x, y, z) assert poly(2*(y + z)**2 - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - 1, y, z) assert poly(x*(y + z)**2 - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - 1, x, y, z) assert poly(2*x*(y + z)**2 - 1) == Poly(2*x*y**2 + 4*x*y*z + 2*x*z**2 - 1, x, y, z) assert poly(2*(y + z)**2 - x - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - x - 1, x, y, z) assert poly(x*(y + z)**2 - x - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - x - 1, x, y, z) assert poly(2*x*(y + z)**2 - x - 1) == Poly(2*x*y**2 + 4*x*y*z + 2*x*z**2 - x - 1, x, y, z) assert poly(x*y + (x + y)**2 + (x + z)**2) == \ Poly(2*x*z + 3*x*y + y**2 + z**2 + 2*x**2, x, y, z) assert poly(x*y*(x + y)*(x + z)**2) == \ Poly(x**3*y**2 + x*y**2*z**2 + y*x**2*z**2 + 2*z*x**2*y**2 + 2*y*z*x**3 + y*x**4, x, y, z) assert poly(Poly(x + y + z, y, x, z)) == Poly(x + y + z, y, x, z) assert poly((x + y)**2, x) == Poly(x**2 + 2*x*y + y**2, x, domain=ZZ[y]) assert poly((x + y)**2, y) == Poly(x**2 + 2*x*y + y**2, y, domain=ZZ[x]) assert poly(1, x) == Poly(1, x) raises(GeneratorsNeeded, "poly(1)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_specialpolys.py0000644000175000017500000000647512014170666027543 0ustar georgeskgeorgesk"""Tests for functions for generating interesting polynomials. """ from sympy import Poly, ZZ, symbols from sympy.utilities.pytest import raises from sympy.polys.specialpolys import ( swinnerton_dyer_poly, cyclotomic_poly, symmetric_poly, random_poly, interpolating_poly, fateman_poly_F_1, dmp_fateman_poly_F_1, fateman_poly_F_2, dmp_fateman_poly_F_2, fateman_poly_F_3, dmp_fateman_poly_F_3, ) from sympy.abc import x, y, z def test_swinnerton_dyer_poly(): raises(ValueError, "swinnerton_dyer_poly(0, x)") assert swinnerton_dyer_poly(1, x, polys=True) == Poly(x**2 - 2) assert swinnerton_dyer_poly(1, x) == x**2 - 2 assert swinnerton_dyer_poly(2, x) == x**4 - 10*x**2 + 1 assert swinnerton_dyer_poly(3, x) == x**8 - 40*x**6 + 352*x**4 - 960*x**2 + 576 def test_cyclotomic_poly(): raises(ValueError, "cyclotomic_poly(0, x)") assert cyclotomic_poly(1, x, polys=True) == Poly(x - 1) assert cyclotomic_poly(1, x) == x - 1 assert cyclotomic_poly(2, x) == x + 1 assert cyclotomic_poly(3, x) == x**2 + x + 1 assert cyclotomic_poly(4, x) == x**2 + 1 assert cyclotomic_poly(5, x) == x**4 + x**3 + x**2 + x + 1 assert cyclotomic_poly(6, x) == x**2 - x + 1 def test_symmetric_poly(): raises(ValueError, "symmetric_poly(-1, x, y, z)") raises(ValueError, "symmetric_poly(5, x, y, z)") assert symmetric_poly(1, x, y, z, polys=True) == Poly(x + y + z) assert symmetric_poly(1, (x, y, z), polys=True) == Poly(x + y + z) assert symmetric_poly(0, x, y, z) == 1 assert symmetric_poly(1, x, y, z) == x + y + z assert symmetric_poly(2, x, y, z) == x*y + x*z + y*z assert symmetric_poly(3, x, y, z) == x*y*z def test_random_poly(): poly = random_poly(x, 10, -100, 100, polys=False) assert Poly(poly).degree() == 10 assert all(-100 <= coeff <= 100 for coeff in Poly(poly).coeffs()) is True poly = random_poly(x, 10, -100, 100, polys=True) assert poly.degree() == 10 assert all(-100 <= coeff <= 100 for coeff in poly.coeffs()) is True def test_interpolating_poly(): x0,x1,x2, y0,y1,y2 = symbols('x:3, y:3') assert interpolating_poly(0, x) == 0 assert interpolating_poly(1, x) == y0 assert interpolating_poly(2, x) == \ y0*(x - x1)/(x0 - x1) + y1*(x - x0)/(x1 - x0) assert interpolating_poly(3, x) == \ y0*(x - x1)*(x - x2)/((x0 - x1)*(x0 - x2)) + \ y1*(x - x0)*(x - x2)/((x1 - x0)*(x1 - x2)) + \ y2*(x - x0)*(x - x1)/((x2 - x0)*(x2 - x1)) def test_fateman_poly_F_1(): f,g,h = fateman_poly_F_1(1) F,G,H = dmp_fateman_poly_F_1(1, ZZ) assert [ t.rep.rep for t in [f,g,h] ] == [F,G,H] f,g,h = fateman_poly_F_1(3) F,G,H = dmp_fateman_poly_F_1(3, ZZ) assert [ t.rep.rep for t in [f,g,h] ] == [F,G,H] def test_fateman_poly_F_2(): f,g,h = fateman_poly_F_2(1) F,G,H = dmp_fateman_poly_F_2(1, ZZ) assert [ t.rep.rep for t in [f,g,h] ] == [F,G,H] f,g,h = fateman_poly_F_2(3) F,G,H = dmp_fateman_poly_F_2(3, ZZ) assert [ t.rep.rep for t in [f,g,h] ] == [F,G,H] def test_fateman_poly_F_3(): f,g,h = fateman_poly_F_3(1) F,G,H = dmp_fateman_poly_F_3(1, ZZ) assert [ t.rep.rep for t in [f,g,h] ] == [F,G,H] f,g,h = fateman_poly_F_3(3) F,G,H = dmp_fateman_poly_F_3(3, ZZ) assert [ t.rep.rep for t in [f,g,h] ] == [F,G,H] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_polyroots.py0000644000175000017500000003470312014170666027101 0ustar georgeskgeorgesk"""Tests for algorithms for computing symbolic roots of polynomials. """ from sympy import (S, symbols, Symbol, Wild, Integer, Rational, sqrt, powsimp, Lambda, sin, cos, pi, I) from sympy.utilities.pytest import raises from sympy.polys import Poly, cyclotomic_poly from sympy.polys.polyroots import (root_factors, roots_linear, roots_quadratic, roots_cubic, roots_quartic, roots_cyclotomic, roots_binomial, roots_rational, preprocess_roots, roots) a, b, c, d, e, t, x, y, z = symbols('a,b,c,d,e,t,x,y,z') def test_roots_linear(): assert roots_linear(Poly(2*x+1, x)) == [-Rational(1, 2)] def test_roots_quadratic(): assert roots_quadratic(Poly(2*x**2, x)) == [0, 0] assert roots_quadratic(Poly(2*x**2 + 3*x, x)) == [-Rational(3, 2), 0] assert roots_quadratic(Poly(2*x**2 + 3, x)) == [-I*sqrt(6)/2, I*sqrt(6)/2] assert roots_quadratic(Poly(2*x**2 + 4*x+3, x)) == [-1 - I*sqrt(2)/2, -1 + I*sqrt(2)/2] f = x**2 + (2*a*e + 2*c*e)/(a - c)*x + (d - b + a*e**2 - c*e**2)/(a - c) assert roots_quadratic(Poly(f, x)) == \ [-e*(a + c)/(a - c) - ((a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c)**2)**S.Half, -e*(a + c)/(a - c) + ((a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c)**2)**S.Half] def test_roots_cubic(): assert roots_cubic(Poly(2*x**3, x)) == [0, 0, 0] assert roots_cubic(Poly(x**3-3*x**2+3*x-1, x)) == [1, 1, 1] assert roots_cubic(Poly(x**3+1, x)) == \ [-1, S.Half - I*sqrt(3)/2, S.Half + I*sqrt(3)/2] def test_roots_quartic(): assert roots_quartic(Poly(x**4, x)) == [0, 0, 0, 0] assert roots_quartic(Poly(x**4 + x**3, x)) in [ [-1,0,0,0], [0,-1,0,0], [0,0,-1,0], [0,0,0,-1] ] assert roots_quartic(Poly(x**4 - x**3, x)) in [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ] lhs = roots_quartic(Poly(x**4 + x, x)) rhs = [S.Half + I*sqrt(3)/2, S.Half - I*sqrt(3)/2, S.Zero, -S.One] assert sorted(lhs, key=hash) == sorted(rhs, key=hash) # test of all branches of roots quartic for i, (a, b, c, d) in enumerate([(1, 2, 3, 0), (3, -7, -9, 9), (1, 2, 3, 4), (1, 2, 3, 4), (-7, -3, 3, -6), (-3, 5, -6, -4)]): if i == 2: c = -a*(a**2/S(8) - b/S(2)) elif i == 3: d = a*(a*(3*a**2/S(256) - b/S(16)) + c/S(4)) eq = x**4 + a*x**3 + b*x**2 + c*x + d ans = roots_quartic(Poly(eq, x)) assert all([eq.subs(x, ai).n(chop=True) == 0 for ai in ans]) def test_roots_cyclotomic(): assert roots_cyclotomic(cyclotomic_poly(1, x, polys=True)) == [1] assert roots_cyclotomic(cyclotomic_poly(2, x, polys=True)) == [-1] assert roots_cyclotomic(cyclotomic_poly(3, x, polys=True)) == [-S(1)/2 - I*3**(S(1)/2)/2, -S(1)/2 + I*3**(S(1)/2)/2] assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True)) == [-I, I] assert roots_cyclotomic(cyclotomic_poly(6, x, polys=True)) == [S(1)/2 - I*3**(S(1)/2)/2, S(1)/2 + I*3**(S(1)/2)/2] assert roots_cyclotomic(cyclotomic_poly(7, x, polys=True)) == [ -cos(pi/7) - I*sin(pi/7), -cos(pi/7) + I*sin(pi/7), cos(2*pi/7) - I*sin(2*pi/7), cos(2*pi/7) + I*sin(2*pi/7), -cos(3*pi/7) - I*sin(3*pi/7), -cos(3*pi/7) + I*sin(3*pi/7), ] assert roots_cyclotomic(cyclotomic_poly(8, x, polys=True)) == [ -2**(S(1)/2)/2 - I*2**(S(1)/2)/2, -2**(S(1)/2)/2 + I*2**(S(1)/2)/2, 2**(S(1)/2)/2 - I*2**(S(1)/2)/2, 2**(S(1)/2)/2 + I*2**(S(1)/2)/2, ] assert roots_cyclotomic(cyclotomic_poly(12, x, polys=True)) == [ -3**(S(1)/2)/2 - I/2, -3**(S(1)/2)/2 + I/2, 3**(S(1)/2)/2 - I/2, 3**(S(1)/2)/2 + I/2, ] assert roots_cyclotomic(cyclotomic_poly(1, x, polys=True), factor=True) == [1] assert roots_cyclotomic(cyclotomic_poly(2, x, polys=True), factor=True) == [-1] assert roots_cyclotomic(cyclotomic_poly(3, x, polys=True), factor=True) == \ [-(-1)**(S(1)/3), -1 + (-1)**(S(1)/3)] assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True), factor=True) == \ [-I, I] assert roots_cyclotomic(cyclotomic_poly(5, x, polys=True), factor=True) == \ [-(-1)**(S(1)/5), (-1)**(S(2)/5), -(-1)**(S(3)/5), -1 + (-1)**(S(1)/5) - (-1)**(S(2)/5) + (-1)**(S(3)/5)] assert roots_cyclotomic(cyclotomic_poly(6, x, polys=True), factor=True) == \ [(-1)**(S(1)/3), 1 - (-1)**(S(1)/3)] def test_roots_binomial(): assert roots_binomial(Poly(5*x, x)) == [0] assert roots_binomial(Poly(5*x**4, x)) == [0, 0, 0, 0] assert roots_binomial(Poly(5*x+2, x)) == [-Rational(2, 5)] A = 10**Rational(3, 4)/10 assert roots_binomial(Poly(5*x**4+2, x)) == \ [-A - A*I, -A + A*I, A - A*I, A + A*I] a1 = Symbol('a1', nonnegative=True) b1 = Symbol('b1', nonnegative=True) r0 = roots_quadratic(Poly(a1*x**2 + b1, x)) r1 = roots_binomial(Poly(a1*x**2 + b1, x)) assert powsimp(r0[0]) == powsimp(r1[0]) assert powsimp(r0[1]) == powsimp(r1[1]) def test_roots_rational(): assert roots_rational(Poly(x**2-1, x)) == [-S.One, S.One] assert roots_rational(Poly(x**2-x, x)) == [S.Zero, S.One] assert roots_rational(Poly(x**2-x/2, x)) == [S.Zero] assert roots_rational(Poly(2*x**2-x, x)) == [S.Zero] assert roots_rational(Poly(t*x**2-x, x)) == [] def test_roots_preprocessing(): f = a*y*x**2 + y - b coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1 assert poly == Poly(a*y*x**2 + y - b, x) f = c**3*x**3 + c**2*x**2 + c*x + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + x**2 + x + a, x) f = c**3*x**3 + c**2*x**2 + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + x**2 + a, x) f = c**3*x**3 + c*x + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + x + a, x) f = c**3*x**3 + a coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 1/c assert poly == Poly(x**3 + a, x) E, F, J, L = symbols("E,F,J,L") f = -21601054687500000000*E**8*J**8/L**16 + \ 508232812500000000*F*x*E**7*J**7/L**14 - \ 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ 27633173750*E**4*F**4*J**4*x**4/L**8 + \ 14840215*E**3*F**5*J**3*x**5/L**6 + \ 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ 1153*E*J*F**7*x**7/(80*L**2) + \ 633*F**8*x**8/160000 coeff, poly = preprocess_roots(Poly(f, x)) assert coeff == 20*E*J/(F*L**2) assert poly == 633*x**8 - 115300*x**7 + 4383520*x**6 + 296804300*x**5 - 27633173750*x**4 + \ 809735812500*x**3 - 10673859375000*x**2 + 63529101562500*x - 135006591796875 def test_roots(): assert roots(1, x) == {} assert roots(x, x) == {S.Zero: 1} assert roots(x**9, x) == {S.Zero: 9} assert roots(((x-2)*(x+3)*(x-4)).expand(), x) == {-S(3): 1, S(2): 1, S(4): 1} assert roots(2*x+1, x) == {-S.Half: 1} assert roots((2*x+1)**2, x) == {-S.Half: 2} assert roots((2*x+1)**5, x) == {-S.Half: 5} assert roots((2*x+1)**10, x) == {-S.Half: 10} assert roots(x**4 - 1, x) == {I: 1, S.One: 1, -S.One: 1, -I: 1} assert roots((x**4 - 1)**2, x) == {I: 2, S.One: 2, -S.One: 2, -I: 2} assert roots(((2*x-3)**2).expand(), x) == { Rational(3,2): 2} assert roots(((2*x+3)**2).expand(), x) == {-Rational(3,2): 2} assert roots(((2*x-3)**3).expand(), x) == { Rational(3,2): 3} assert roots(((2*x+3)**3).expand(), x) == {-Rational(3,2): 3} assert roots(((2*x-3)**5).expand(), x) == { Rational(3,2): 5} assert roots(((2*x+3)**5).expand(), x) == {-Rational(3,2): 5} assert roots(((a*x-b)**5).expand(), x) == { b/a: 5} assert roots(((a*x+b)**5).expand(), x) == {-b/a: 5} assert roots(x**4-2*x**2+1, x) == {S.One: 2, -S.One: 2} assert roots(x**6-4*x**4+4*x**3-x**2, x) == \ {S.One: 2, -1 - sqrt(2): 1, S.Zero: 2, -1 + sqrt(2): 1} assert roots(x**8-1, x) == { 2**S.Half/2 + I*2**S.Half/2: 1, 2**S.Half/2 - I*2**S.Half/2: 1, -2**S.Half/2 + I*2**S.Half/2: 1, -2**S.Half/2 - I*2**S.Half/2: 1, S.One: 1, -S.One: 1, I: 1, -I: 1 } f = -2016*x**2 - 5616*x**3 - 2056*x**4 + 3324*x**5 + 2176*x**6 - 224*x**7 - 384*x**8 - 64*x**9 assert roots(f) == {S(0): 2, -S(2): 2, S(2): 1, -S(7)/2: 1, -S(3)/2: 1, -S(1)/2: 1, S(3)/2: 1} assert roots((a+b+c)*x - (a+b+c+d), x) == {(a+b+c+d)/(a+b+c): 1} assert roots(x**3+x**2-x+1, x, cubics=False) == {} assert roots(((x-2)*(x+3)*(x-4)).expand(), x, cubics=False) == {-S(3): 1, S(2): 1, S(4): 1} assert roots(((x-2)*(x+3)*(x-4)*(x-5)).expand(), x, cubics=False) == \ {-S(3): 1, S(2): 1, S(4): 1, S(5): 1} assert roots(x**3 + 2*x**2 + 4*x + 8, x) == {-S(2): 1, -2*I: 1, 2*I: 1} assert roots(x**3 + 2*x**2 + 4*x + 8, x, cubics=True) == \ {-2*I: 1, 2*I: 1, -S(2): 1} assert roots((x**2 - x)*(x**3 + 2*x**2 + 4*x + 8), x ) == \ {S(1): 1, S(0): 1, -S(2): 1, -2*I: 1, 2*I: 1} r1_2, r1_3, r1_9, r4_9, r19_27 = [ Rational(*r) \ for r in ((1,2), (1,3), (1,9), (4,9), (19,27)) ] U = -r1_2 - r1_2*I*3**r1_2 V = -r1_2 + r1_2*I*3**r1_2 W = (r19_27 + r1_9*33**r1_2)**r1_3 assert roots(x**3+x**2-x+1, x, cubics=True) == { -r1_3 - U*W - r4_9*(U*W)**(-1): 1, -r1_3 - V*W - r4_9*(V*W)**(-1): 1, -r1_3 - W - r4_9*( W)**(-1): 1, } f = (x**2+2*x+3).subs(x, 2*x**2 + 3*x).subs(x, 5*x-4) r1_2, r13_20, r1_100 = [ Rational(*r) \ for r in ((1,2), (13,20), (1,100)) ] assert roots(f, x) == { r13_20 + r1_100*(25 - 200*I*2**r1_2)**r1_2: 1, r13_20 - r1_100*(25 - 200*I*2**r1_2)**r1_2: 1, r13_20 + r1_100*(25 + 200*I*2**r1_2)**r1_2: 1, r13_20 - r1_100*(25 + 200*I*2**r1_2)**r1_2: 1, } f = x**4 + x**3 + x**2 + x + 1 r1_4, r1_8, r5_8 = [ Rational(*r) for r in ((1,4), (1,8), (5,8)) ] assert roots(f, x) == { -r1_4 + r1_4*5**r1_2 + I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, -r1_4 + r1_4*5**r1_2 - I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, -r1_4 - r1_4*5**r1_2 + I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, -r1_4 - r1_4*5**r1_2 - I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, } f = z**3 + (-2 - y)*z**2 + (1 + 2*y - 2*x**2)*z - y + 2*x**2 assert roots(f, z) == { S.One: 1, S.Half + S.Half*y + S.Half*(1 - 2*y + y**2 + 8*x**2)**S.Half: 1, S.Half + S.Half*y - S.Half*(1 - 2*y + y**2 + 8*x**2)**S.Half: 1, } assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=False) == {} assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=True) != {} assert roots(x**4-1, x, filter='Z') == {S.One: 1, -S.One: 1} assert roots(x**4-1, x, filter='I') == {I: 1, -I: 1} assert roots((x-1)*(x+1), x) == {S.One: 1, -S.One: 1} assert roots((x-1)*(x+1), x, predicate=lambda r: r.is_positive) == {S.One: 1} assert roots(x**4-1, x, filter='Z', multiple=True) == [-S.One, S.One] assert roots(x**4-1, x, filter='I', multiple=True) == [-I, I] assert roots(x**3, x, multiple=True) == [S.Zero, S.Zero, S.Zero] assert roots(1234, x, multiple=True) == [] f = x**6 - x**5 + x**4 - x**3 + x**2 - x + 1 assert roots(f) == { -I*sin(pi/7) + cos(pi/7): 1, -I*sin(2*pi/7) - cos(2*pi/7): 1, -I*sin(3*pi/7) + cos(3*pi/7): 1, I*sin(pi/7) + cos(pi/7): 1, I*sin(2*pi/7) - cos(2*pi/7): 1, I*sin(3*pi/7) + cos(3*pi/7): 1, } g = ((x**2 + 1)*f**2).expand() assert roots(g) == { -I*sin(pi/7) + cos(pi/7): 2, -I*sin(2*pi/7) - cos(2*pi/7): 2, -I*sin(3*pi/7) + cos(3*pi/7): 2, I*sin(pi/7) + cos(pi/7): 2, I*sin(2*pi/7) - cos(2*pi/7): 2, I*sin(3*pi/7) + cos(3*pi/7): 2, -I: 1, I: 1, } def test_roots2(): """Just test that calculating these roots does not hang (final result is not checked) """ a, b, c, d, x = symbols("a,b,c,d,x") f1 = x**2*c + (a/b) + x*c*d - a f2 = x**2*(a + b*(c-d)*a) + x*a*b*c/(b*d-d) + (a*d-c/d) assert roots(f1, x).values() == [1, 1] assert roots(f2, x).values() == [1, 1] (zz, yy, xx, zy, zx, yx, k) = symbols("zz,yy,xx,zy,zx,yx,k") e1 = (zz-k)*(yy-k)*(xx-k) + zy*yx*zx + zx-zy-yx e2 = (zz-k)*yx*yx + zx*(yy-k)*zx + zy*zy*(xx-k) assert roots(e1 - e2, k).values() == [1, 1, 1] def test_roots_inexact(): R1 = sorted([ r.evalf() for r in roots(x**2 + x + 1, x) ]) R2 = sorted([ r for r in roots(x**2 + x + 1.0, x) ]) for r1, r2 in zip(R1, R2): assert abs(r1 - r2) < 1e-12 f = x**4 + 3.0*sqrt(2.0)*x**3 - (78.0 + 24.0*sqrt(3.0))*x**2 + 144.0*(2*sqrt(3.0) + 9.0) R1 = sorted(roots(f, multiple=True)) R2 = sorted([-12.7530479110482, -3.85012393732929, 4.89897948556636, 7.46155167569183]) for r1, r2 in zip(R1, R2): assert abs(r1 - r2) < 1e-10 def test_roots_preprocessed(): E, F, J, L = symbols("E,F,J,L") f = -21601054687500000000*E**8*J**8/L**16 + \ 508232812500000000*F*x*E**7*J**7/L**14 - \ 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ 27633173750*E**4*F**4*J**4*x**4/L**8 + \ 14840215*E**3*F**5*J**3*x**5/L**6 + \ 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ 1153*E*J*F**7*x**7/(80*L**2) + \ 633*F**8*x**8/160000 assert roots(f, x) == {} R1 = roots(f.evalf(), x, multiple=True) R2 = [-1304.88375606366, 97.1168816800648, 186.946430171876, 245.526792947065, 503.441004174773, 791.549343830097, 1273.16678129348, 1850.10650616851] w = Wild('w') p = w*E*J/(F*L**2) assert len(R1) == len(R2) for r1, r2 in zip(R1, R2): match = r1.match(p) assert match is not None and abs(match[w] - r2) < 1e-10 def test_root_factors(): assert root_factors(Poly(1, x)) == [Poly(1, x)] assert root_factors(Poly(x, x)) == [Poly(x, x)] assert root_factors(Poly(x**2-1, x)) == [Poly(x-1, x), Poly(x+1, x)] factors = root_factors(Poly((x**4 - 1)**2, x)) assert len(factors) == 8 assert set(factors) == set([Poly(x-I, x), Poly(x-1, x), Poly(x+1, x), Poly(x+I, x)]) assert root_factors(Poly(x**4-1, x), filter='Z') == \ [Poly(x-1, x), Poly(x+1, x), Poly(x**2+1, x)] assert root_factors(8*x**2 + 12*x**4 + 6*x**6 + x**8, x, filter='Q') == \ [x, x, x**6 + 6*x**4 + 12*x**2 + 8] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_polyfuncs.py0000644000175000017500000000542012014170666027043 0ustar georgeskgeorgesk"""Tests for high-level polynomials manipulation functions. """ from sympy.polys.polyfuncs import ( symmetrize, horner, interpolate, viete, ) from sympy.polys.polyerrors import ( MultivariatePolynomialError, ) from sympy import symbols from sympy.utilities.pytest import raises from sympy.abc import a, b, c, d, e, x, y, z def test_symmetrize(): assert symmetrize(0, x, y, z) == (0, 0) assert symmetrize(1, x, y, z) == (1, 0) s1 = x + y + z s2 = x*y + x*z + y*z s3 = x*y*z assert symmetrize(1) == (1, 0) assert symmetrize(1, formal=True) == (1, 0, []) assert symmetrize(x) == (x, 0) assert symmetrize(x + 1) == (x + 1, 0) assert symmetrize(x, x, y) == (x + y, -y) assert symmetrize(x + 1, x, y) == (x + y + 1, -y) assert symmetrize(x, x, y, z) == (s1, -y - z) assert symmetrize(x + 1, x, y, z) == (s1 + 1, -y - z) assert symmetrize(x**2, x, y, z) == (s1**2 - 2*s2, -y**2 - z**2) assert symmetrize(x**2 + y**2) == (-2*x*y + (x + y)**2, 0) assert symmetrize(x**2 - y**2) == (-2*x*y + (x + y)**2, -2*y**2) assert symmetrize(x**3 + y**2 + a*x**2 + b*y**3, x, y) == \ (-3*x*y*(x + y) - 2*a*x*y + a*(x + y)**2 + (x + y)**3, y**2*(1 - a) + y**3*(b - 1)) U = [u0, u1, u2] = symbols('u:3') assert symmetrize(x + 1, x, y, z, formal=True, symbols=U) == \ (u0 + 1, -y - z, [(u0, x + y + z), (u1, x*y + x*z + y*z), (u2, x*y*z)]) assert symmetrize([1, 2, 3]) == [(1, 0), (2, 0), (3, 0)] assert symmetrize([1, 2, 3], formal=True) == ([(1, 0), (2, 0), (3, 0)], []) assert symmetrize([x + y, x - y]) == [(x + y, 0), (x + y, -2*y)] def test_horner(): assert horner(0) == 0 assert horner(1) == 1 assert horner(x) == x assert horner(x + 1) == x + 1 assert horner(x**2 + 1) == x**2 + 1 assert horner(x**2 + x) == (x + 1)*x assert horner(x**2 + x + 1) == (x + 1)*x + 1 assert horner(9*x**4 + 8*x**3 + 7*x**2 + 6*x + 5) == (((9*x + 8)*x + 7)*x + 6)*x + 5 assert horner(a*x**4 + b*x**3 + c*x**2 + d*x + e) == (((a*x + b)*x + c)*x + d)*x + e assert horner(4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y, wrt=x) == ((4*y + 2)*x*y + (2*y + 1)*y)*x assert horner(4*x**2*y**2 + 2*x**2*y + 2*x*y**2 + x*y, wrt=y) == ((4*x + 2)*y*x + (2*x + 1)*x)*y def test_interpolate(): assert interpolate([1,4,9,16], x) == x**2 assert interpolate([(1, 1), (2, 4), (3, 9)], x) == x**2 assert interpolate([(1, 2), (2, 5), (3, 10)], x) == 1 + x**2 assert interpolate({1: 2, 2: 5, 3: 10}, x) == 1 + x**2 def test_viete(): r1, r2 = symbols('r1, r2') assert viete(a*x**2 + b*x + c, [r1, r2], x) == [(r1 + r2, -b/a), (r1*r2, c/a)] raises(ValueError, "viete(1, [], x)") raises(ValueError, "viete(x**2 + 1, [r1])") raises(MultivariatePolynomialError, "viete(x + y, [r1])") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_rootoftools.py0000644000175000017500000002235412014170666027417 0ustar georgeskgeorgesk"""Tests for the implementation of RootOf class and related tools. """ from sympy.polys.polytools import Poly from sympy.polys.rootoftools import RootOf, RootSum from sympy.polys.polyerrors import ( MultivariatePolynomialError, GeneratorsNeeded, PolynomialError, ) from sympy import ( S, symbols, sqrt, I, Rational, Float, Lambda, log, exp, tan, ) from sympy.utilities.pytest import raises from sympy.abc import a, b, c, d, x, y, z, r def test_RootOf___new__(): assert RootOf(x, 0) == 0 assert RootOf(x,-1) == 0 assert RootOf(x, S.Zero) == 0 assert RootOf(x - 1, 0) == 1 assert RootOf(x - 1,-1) == 1 assert RootOf(x + 1, 0) ==-1 assert RootOf(x + 1,-1) ==-1 assert RootOf(x**2 + 2*x + 3, 0) == -1 - I*sqrt(2) assert RootOf(x**2 + 2*x + 3, 1) == -1 + I*sqrt(2) assert RootOf(x**2 + 2*x + 3,-1) == -1 + I*sqrt(2) assert RootOf(x**2 + 2*x + 3,-2) == -1 - I*sqrt(2) r = RootOf(x**2 + 2*x + 3, 0, radicals=False) assert isinstance(r, RootOf) == True r = RootOf(x**2 + 2*x + 3, 1, radicals=False) assert isinstance(r, RootOf) == True r = RootOf(x**2 + 2*x + 3,-1, radicals=False) assert isinstance(r, RootOf) == True r = RootOf(x**2 + 2*x + 3,-2, radicals=False) assert isinstance(r, RootOf) == True assert RootOf((x - 1)*(x + 1), 0, radicals=False) ==-1 assert RootOf((x - 1)*(x + 1), 1, radicals=False) == 1 assert RootOf((x - 1)*(x + 1),-1, radicals=False) == 1 assert RootOf((x - 1)*(x + 1),-2, radicals=False) ==-1 assert RootOf((x - 1)*(x + 1), 0, radicals=True) ==-1 assert RootOf((x - 1)*(x + 1), 1, radicals=True) == 1 assert RootOf((x - 1)*(x + 1),-1, radicals=True) == 1 assert RootOf((x - 1)*(x + 1),-2, radicals=True) ==-1 assert RootOf((x - 1)*(x**3 + x + 3), 0) == RootOf(x**3 + x + 3, 0) assert RootOf((x - 1)*(x**3 + x + 3), 1) == 1 assert RootOf((x - 1)*(x**3 + x + 3), 2) == RootOf(x**3 + x + 3, 1) assert RootOf((x - 1)*(x**3 + x + 3), 3) == RootOf(x**3 + x + 3, 2) assert RootOf((x - 1)*(x**3 + x + 3),-1) == RootOf(x**3 + x + 3, 2) assert RootOf((x - 1)*(x**3 + x + 3),-2) == RootOf(x**3 + x + 3, 1) assert RootOf((x - 1)*(x**3 + x + 3),-3) == 1 assert RootOf((x - 1)*(x**3 + x + 3),-4) == RootOf(x**3 + x + 3, 0) assert RootOf(x**4 + 3*x**3, 0) ==-3 assert RootOf(x**4 + 3*x**3, 1) == 0 assert RootOf(x**4 + 3*x**3, 2) == 0 assert RootOf(x**4 + 3*x**3, 3) == 0 raises(GeneratorsNeeded, "RootOf(0, 0)") raises(GeneratorsNeeded, "RootOf(1, 0)") raises(PolynomialError, "RootOf(Poly(0, x), 0)") raises(PolynomialError, "RootOf(Poly(1, x), 0)") raises(PolynomialError, "RootOf(x - y, 0)") raises(NotImplementedError, "RootOf(x**3 - x + sqrt(2), 0)") raises(NotImplementedError, "RootOf(x**3 - x + I, 0)") raises(IndexError, "RootOf(x**2 - 1,-4)") raises(IndexError, "RootOf(x**2 - 1,-3)") raises(IndexError, "RootOf(x**2 - 1, 2)") raises(IndexError, "RootOf(x**2 - 1, 3)") assert RootOf(Poly(x - y, x), 0) == y assert RootOf(Poly(x**2 - y, x), 0) == -sqrt(y) assert RootOf(Poly(x**2 - y, x), 1) == sqrt(y) assert RootOf(Poly(x**3 - y, x), 0) == y**Rational(1,3) assert RootOf(y*x**3 + y*x + 2*y, x, 0) == -1 raises(NotImplementedError, "RootOf(x**3 + x + 2*y, x, 0)") assert RootOf(x**3 + x + 1, 0).is_commutative == True def test_RootOf_free_symbols(): assert RootOf(x**3 + x + 3, 0).free_symbols == set() def test_RootOf___eq__(): assert (RootOf(x**3 + x + 3, 0) == RootOf(x**3 + x + 3, 0)) == True assert (RootOf(x**3 + x + 3, 0) == RootOf(x**3 + x + 3, 1)) == False assert (RootOf(x**3 + x + 3, 1) == RootOf(x**3 + x + 3, 1)) == True assert (RootOf(x**3 + x + 3, 1) == RootOf(x**3 + x + 3, 2)) == False assert (RootOf(x**3 + x + 3, 2) == RootOf(x**3 + x + 3, 2)) == True assert (RootOf(x**3 + x + 3, 0) == RootOf(y**3 + y + 3, 0)) == True assert (RootOf(x**3 + x + 3, 0) == RootOf(y**3 + y + 3, 1)) == False assert (RootOf(x**3 + x + 3, 1) == RootOf(y**3 + y + 3, 1)) == True assert (RootOf(x**3 + x + 3, 1) == RootOf(y**3 + y + 3, 2)) == False assert (RootOf(x**3 + x + 3, 2) == RootOf(y**3 + y + 3, 2)) == True def test_RootOf_is_real(): assert RootOf(x**3 + x + 3, 0).is_real == True assert RootOf(x**3 + x + 3, 1).is_real == False assert RootOf(x**3 + x + 3, 2).is_real == False def test_RootOf_is_complex(): assert RootOf(x**3 + x + 3, 0).is_complex == False assert RootOf(x**3 + x + 3, 1).is_complex == True assert RootOf(x**3 + x + 3, 2).is_complex == True def test_RootOf_subs(): assert RootOf(x**3 + x + 1, 0).subs(x, y) == RootOf(y**3 + y + 1, 0) def test_RootOf_diff(): assert RootOf(x**3 + x + 1, 0).diff(x) == 0 assert RootOf(x**3 + x + 1, 0).diff(y) == 0 def test_RootOf_evalf(): real = RootOf(x**3 + x + 3, 0).evalf(n=20) assert real.epsilon_eq(Float("-1.2134116627622296341")) re, im = RootOf(x**3 + x + 3, 1).evalf(n=20).as_real_imag() assert re.epsilon_eq( Float("0.60670583138111481707")) assert im.epsilon_eq(-Float("1.45061224918844152650")) re, im = RootOf(x**3 + x + 3, 2).evalf(n=20).as_real_imag() assert re.epsilon_eq(Float("0.60670583138111481707")) assert im.epsilon_eq(Float("1.45061224918844152650")) def test_RootOf_real_roots(): assert Poly(x**5 + x + 1).real_roots() == [RootOf(x**3 - x**2 + 1, 0)] assert Poly(x**5 + x + 1).real_roots(radicals=False) == [RootOf(x**3 - x**2 + 1, 0)] def test_RootOf_all_roots(): assert Poly(x**5 + x + 1).all_roots() == [ RootOf(x**3 - x**2 + 1, 0), -S(1)/2 - 3**(S(1)/2)*I/2, -S(1)/2 + 3**(S(1)/2)*I/2, RootOf(x**3 - x**2 + 1, 1), RootOf(x**3 - x**2 + 1, 2), ] assert Poly(x**5 + x + 1).all_roots(radicals=False) == [ RootOf(x**3 - x**2 + 1, 0), RootOf(x**2 + x + 1, 0, radicals=False), RootOf(x**2 + x + 1, 1, radicals=False), RootOf(x**3 - x**2 + 1, 1), RootOf(x**3 - x**2 + 1, 2), ] def test_RootSum___new__(): f = x**3 + x + 3 g = Lambda(r, log(r*x)) s = RootSum(f, g) rootofs = sum(log(RootOf(f, i)*x) for i in (0, 1, 2)) assert isinstance(s, RootSum) == True assert s.doit() == rootofs assert RootSum(f**2, g) == 2*RootSum(f, g) assert RootSum(f**2, g).doit() == 2*rootofs assert RootSum((x - 7)*f**3, g) == log(7*x) + 3*RootSum(f, g) assert RootSum((x - 7)*f**3, g).doit() == log(7*x) + 3*rootofs # Issue 2472 assert hash(RootSum((x - 7)*f**3, g)) == hash(log(7*x) + 3*RootSum(f, g)) raises(MultivariatePolynomialError, "RootSum(x**3 + x + y)") raises(ValueError, "RootSum(x**2 + 3, lambda x: x)") assert RootSum(f, exp) == RootSum(f, Lambda(x, exp(x))) assert RootSum(f, log) == RootSum(f, Lambda(x, log(x))) assert isinstance(RootSum(f, auto=False), RootSum) == True assert RootSum(f) == 0 assert RootSum(f, Lambda(x, x)) == 0 assert RootSum(f, Lambda(x, x**2)) == -2 assert RootSum(f, Lambda(x, 1)) == 3 assert RootSum(f, Lambda(x, 2)) == 6 assert RootSum(f, auto=False).is_commutative == True assert RootSum(f, Lambda(x, 1/(x + x**2))) == S(11)/3 assert RootSum(f, Lambda(x, y/(x + x**2))) == S(11)/3*y assert RootSum(x**2 - 1, Lambda(x, 3*x**2), x) == 6 assert RootSum(x**2 - y, Lambda(x, 3*x**2), x) == 6*y assert RootSum(x**2 - 1, Lambda(x, z*x**2), x) == 2*z assert RootSum(x**2 - y, Lambda(x, z*x**2), x) == 2*z*y assert RootSum(x**2 - 1, Lambda(x, exp(x)), quadratic=True) == exp(-1) + exp(1) assert RootSum(x**3 + a*x + a**3, tan, x) == RootSum(x**3 + x + 1, Lambda(x, tan(a*x))) assert RootSum(a**3*x**3 + a*x + 1, tan, x) == RootSum(x**3 + x + 1, Lambda(x, tan(x/a))) def test_RootSum_free_symbols(): assert RootSum(x**3 + x + 3, Lambda(r, exp(r))).free_symbols == set() assert RootSum(x**3 + x + 3, Lambda(r, exp(a*r))).free_symbols == set([a]) assert RootSum(x**3 + x + y, Lambda(r, exp(a*r)), x).free_symbols == set([a, y]) def test_RootSum___eq__(): f = Lambda(x, exp(x)) assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 1, f)) == True assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 1, f)) == True assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 2, f)) == False assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 2, f)) == False def test_RootSum_diff(): f = x**3 + x + 3 g = Lambda(r, exp(r*x)) h = Lambda(r, r*exp(r*x)) assert RootSum(f, g).diff(x) == RootSum(f, h) def test_RootSum_subs(): f = x**3 + x + 3 g = Lambda(r, exp(r*x)) F = y**3 + y + 3 G = Lambda(r, exp(r*y)) assert RootSum(f, g).subs(y, 1) == RootSum(f, g) assert RootSum(f, g).subs(x, y) == RootSum(F, G) def test_RootSum_rational(): assert RootSum(z**5 - z + 1, Lambda(z, z/(x - z))) == (4*x - 5)/(x**5 - x + 1) f = 161*z**3 + 115*z**2 + 19*z + 1 g = Lambda(z, z*log(-3381*z**4/4 - 3381*z**3/4 - 625*z**2/2 - 125*z/2 - 5 + exp(x))) assert RootSum(f, g).diff(x) == -((5*exp(2*x) - 6*exp(x) + 4)*exp(x)/(exp(3*x) - exp(2*x) + 1))/7 def test_RootSum_independent(): f = (x**3 - a)**2*(x**4 - b)**3 g = Lambda(x, 5*tan(x) + 7) h = Lambda(x, tan(x)) r0 = RootSum(x**3 - a, h, x) r1 = RootSum(x**4 - b, h, x) assert RootSum(f, g, x).as_ordered_terms() == [10*r0, 15*r1, 126] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_constructor.py0000644000175000017500000000603212014170666027406 0ustar georgeskgeorgesk"""Tests for tools for constructing domains for expressions. """ from sympy.polys.constructor import construct_domain from sympy.polys.domains import ZZ, QQ, RR, EX from sympy import S, sqrt, sin from sympy.abc import x, y def test_construct_domain(): assert construct_domain([1, 2, 3]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) assert construct_domain([1, 2, 3], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) assert construct_domain([S(1), S(2), S(3)]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) assert construct_domain([S(1), S(2), S(3)], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) assert construct_domain([S(1)/2, S(2)]) == (QQ, [QQ(1,2), QQ(2)]) assert construct_domain([3.14, 1, S(1)/2]) == (RR, [RR(3.14), RR(1.0), RR(0.5)]) assert construct_domain([3.14, sqrt(2)], extension=None) == (EX, [EX(3.14), EX(sqrt(2))]) assert construct_domain([3.14, sqrt(2)], extension=True) == (EX, [EX(3.14), EX(sqrt(2))]) assert construct_domain([1, sqrt(2)], extension=None) == (EX, [EX(1), EX(sqrt(2))]) alg = QQ.algebraic_field(sqrt(2)) assert construct_domain([7, S(1)/2, sqrt(2)], extension=True) == \ (alg, [alg.convert(7), alg.convert(S(1)/2), alg.convert(sqrt(2))]) alg = QQ.algebraic_field(sqrt(2)+sqrt(3)) assert construct_domain([7, sqrt(2), sqrt(3)], extension=True) == \ (alg, [alg.convert(7), alg.convert(sqrt(2)), alg.convert(sqrt(3))]) dom = ZZ[x] assert construct_domain([2*x, 3]) == \ (dom, [dom.convert(2*x), dom.convert(3)]) dom = ZZ[x,y] assert construct_domain([2*x, 3*y]) == \ (dom, [dom.convert(2*x), dom.convert(3*y)]) dom = QQ[x] assert construct_domain([x/2, 3]) == \ (dom, [dom.convert(x/2), dom.convert(3)]) dom = QQ[x,y] assert construct_domain([x/2, 3*y]) == \ (dom, [dom.convert(x/2), dom.convert(3*y)]) dom = RR[x] assert construct_domain([x/2, 3.5]) == \ (dom, [dom.convert(x/2), dom.convert(3.5)]) dom = RR[x,y] assert construct_domain([x/2, 3.5*y]) == \ (dom, [dom.convert(x/2), dom.convert(3.5*y)]) dom = ZZ.frac_field(x) assert construct_domain([2/x, 3]) == \ (dom, [dom.convert(2/x), dom.convert(3)]) dom = ZZ.frac_field(x,y) assert construct_domain([2/x, 3*y]) == \ (dom, [dom.convert(2/x), dom.convert(3*y)]) dom = RR.frac_field(x) assert construct_domain([2/x, 3.5]) == \ (dom, [dom.convert(2/x), dom.convert(3.5)]) dom = RR.frac_field(x,y) assert construct_domain([2/x, 3.5*y]) == \ (dom, [dom.convert(2/x), dom.convert(3.5*y)]) assert construct_domain(2) == (ZZ, ZZ(2)) assert construct_domain(S(2)/3) == (QQ, QQ(2, 3)) def test_composite_option(): assert construct_domain({(1,): sin(y)}, composite=False) == \ (EX, {(1,): EX(sin(y))}) assert construct_domain({(1,): y}, composite=False) == \ (EX, {(1,): EX(y)}) assert construct_domain({(1, 1): 1}, composite=False) == \ (ZZ, {(1, 1): 1}) assert construct_domain({(1, 0): y}, composite=False) == \ (EX, {(1, 0): EX(y)}) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_partfrac.py0000644000175000017500000000451112014170666026623 0ustar georgeskgeorgesk"""Tests for algorithms for partial fraction decomposition of rational functions. """ from sympy.polys.partfrac import ( apart_undetermined_coeffs, apart_full_decomposition, apart, ) from sympy import S, Poly, E, pi, Matrix, Eq from sympy.utilities.pytest import raises from sympy.abc import x, y, a, b, c def test_apart(): assert apart(1) == 1 assert apart(1, x) == 1 f, g = (x**2 + 1)/(x + 1), 2/(x + 1) + x - 1 assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x+2)/(x+1), 1/(1 + x) - 1/(2 + x) assert apart(f, full=False) == g assert apart(f, full=True) == g f, g = 1/(x+1)/(x+5), -1/(5 + x)/4 + 1/(1 + x)/4 assert apart(f, full=False) == g assert apart(f, full=True) == g assert apart((E*x+2)/(x-pi)*(x-1), x) == \ 2 - E + E*pi + E*x + (E*pi + 2)*(pi - 1)/(x - pi) assert apart(Eq((x**2 + 1)/(x + 1), x), x) == Eq(x - 1 + 2/(x + 1), x) raises(NotImplementedError, "apart(1/(x + 1)/(y + 2))") def test_apart_matrix(): M = Matrix(2, 2, lambda i, j: 1/(x + i + 1)/(x + j)) assert apart(M) == Matrix([ [1/x - 1/(x + 1), (x + 1)**(-2) ], [1/(2*x) - (S(1)/2)/(x + 2), 1/(x + 1) - 1/(x + 2)], ]) def test_apart_symbolic(): f = a*x**4 + (2*b + 2*a*c)*x**3 + (4*b*c - a**2 + a*c**2)*x**2 + (-2*a*b + 2*b*c**2)*x - b**2 g = a**2*x**4 + (2*a*b + 2*c*a**2)*x**3 + (4*a*b*c + b**2 + a**2*c**2)*x**2 + (2*c*b**2 + 2*a*b*c**2)*x + b**2*c**2 assert apart(f/g, x) == 1/a - 1/(x + c)**2 - b**2/(a*(a*x + b)**2) assert apart(1/((x + a)*(x + b)*(x + c)), x) == \ 1/((a - c)*(b - c)*(c + x)) - 1/((a - b)*(b - c)*(b + x)) + 1/((a - b)*(a - c)*(a + x)) def test_apart_undetermined_coeffs(): p = Poly(2*x - 3) q = Poly(x**9 - x**8 - x**6 + x**5 - 2*x**2 + 3*x - 1) r = (-x**7 - x**6 - x**5 + 4)/(x**8 - x**5 - 2*x + 1) + 1/(x - 1) assert apart_undetermined_coeffs(p, q) == r p = Poly(1, x, domain='ZZ[a,b]') q = Poly((x + a)*(x + b), x, domain='ZZ[a,b]') r = 1/((x + b)*(a - b)) + 1/((x + a)*(b - a)) assert apart_undetermined_coeffs(p, q) == r def test_apart_full_decomposition(): p = Poly(1, x) q = Poly(x**5 + 1, x) assert apart_full_decomposition(p, q) == \ (-S(1)/5)*((x**3 - 2*x**2 + 3*x - 4)/(x**4 - x**3 + x**2 - x + 1)) + (S(1)/5)/(x + 1) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_polyoptions.py0000644000175000017500000002624512014170666027430 0ustar georgeskgeorgesk"""Tests for options manager for :class:`Poly` and public API functions. """ from sympy.polys.polyoptions import ( Options, Expand, Gens, Wrt, Sort, Order, Field, Greedy, Domain, Split, Gaussian, Extension, Modulus, Symmetric, Strict, Auto, Frac, Formal, Polys, Include, All, Gen, Symbols) from sympy.polys.monomialtools import monomial_lex_key from sympy.polys.domains import FF, GF, ZZ, QQ, RR, EX from sympy.polys.polyerrors import OptionError, GeneratorsError from sympy import Integer, Symbol, I, sqrt from sympy.utilities.pytest import raises from sympy.abc import x, y, z def test_Options_clone(): opt = Options((x, y, z), {'domain': 'ZZ'}) assert opt.gens == (x, y, z) assert opt.domain == ZZ assert ('order' in opt) == False new_opt = opt.clone({'gens': (x,y), 'order': 'lex'}) assert opt.gens == (x, y, z) assert opt.domain == ZZ assert ('order' in opt) == False assert new_opt.gens == (x, y) assert new_opt.domain == ZZ assert ('order' in new_opt) == True def test_Expand_preprocess(): assert Expand.preprocess(False) is False assert Expand.preprocess(True) is True assert Expand.preprocess(0) is False assert Expand.preprocess(1) is True raises(OptionError, "Expand.preprocess(x)") def test_Expand_postprocess(): opt = {'expand': True} Expand.postprocess(opt) assert opt == {'expand': True} def test_Gens_preprocess(): assert Gens.preprocess((None,)) == () assert Gens.preprocess((x, y, z)) == (x, y, z) assert Gens.preprocess(((x, y, z),)) == (x, y, z) a = Symbol('a', commutative=False) raises(GeneratorsError, "Gens.preprocess((x, x, y))") raises(GeneratorsError, "Gens.preprocess((x, y, a))") def test_Gens_postprocess(): opt = {'gens': (x, y)} Gens.postprocess(opt) assert opt == {'gens': (x, y)} def test_Wrt_preprocess(): assert Wrt.preprocess(x) == ['x'] assert Wrt.preprocess('') == [] assert Wrt.preprocess(' ') == [] assert Wrt.preprocess('x,y') == ['x', 'y'] assert Wrt.preprocess('x y') == ['x', 'y'] assert Wrt.preprocess('x, y') == ['x', 'y'] assert Wrt.preprocess('x , y') == ['x', 'y'] assert Wrt.preprocess(' x, y') == ['x', 'y'] assert Wrt.preprocess(' x, y') == ['x', 'y'] assert Wrt.preprocess([x, y]) == ['x', 'y'] raises(OptionError, "Wrt.preprocess(',')") raises(OptionError, "Wrt.preprocess(0)") def test_Wrt_postprocess(): opt = {'wrt': ['x']} Wrt.postprocess(opt) assert opt == {'wrt': ['x']} def test_Sort_preprocess(): assert Sort.preprocess([x, y, z]) == ['x', 'y', 'z'] assert Sort.preprocess((x, y, z)) == ['x', 'y', 'z'] assert Sort.preprocess('x > y > z') == ['x', 'y', 'z'] assert Sort.preprocess('x>y>z') == ['x', 'y', 'z'] raises(OptionError, "Sort.preprocess(0)") raises(OptionError, "Sort.preprocess(set([x, y, z]))") def test_Sort_postprocess(): opt = {'sort': 'x > y'} Sort.postprocess(opt) assert opt == {'sort': 'x > y'} def test_Order_preprocess(): assert Order.preprocess('lex') == monomial_lex_key def test_Order_postprocess(): opt = {'order': True} Order.postprocess(opt) assert opt == {'order': True} def test_Field_preprocess(): assert Field.preprocess(False) is False assert Field.preprocess(True) is True assert Field.preprocess(0) is False assert Field.preprocess(1) is True raises(OptionError, "Field.preprocess(x)") def test_Field_postprocess(): opt = {'field': True} Field.postprocess(opt) assert opt == {'field': True} def test_Greedy_preprocess(): assert Greedy.preprocess(False) is False assert Greedy.preprocess(True) is True assert Greedy.preprocess(0) is False assert Greedy.preprocess(1) is True raises(OptionError, "Greedy.preprocess(x)") def test_Greedy_postprocess(): opt = {'greedy': True} Greedy.postprocess(opt) assert opt == {'greedy': True} def test_Domain_preprocess(): assert Domain.preprocess(ZZ) == ZZ assert Domain.preprocess(QQ) == QQ assert Domain.preprocess(EX) == EX assert Domain.preprocess(FF(2)) == FF(2) assert Domain.preprocess(ZZ[x,y]) == ZZ[x,y] assert Domain.preprocess('Z') == ZZ assert Domain.preprocess('Q') == QQ assert Domain.preprocess('ZZ') == ZZ assert Domain.preprocess('QQ') == QQ assert Domain.preprocess('EX') == EX assert Domain.preprocess('FF(23)') == FF(23) assert Domain.preprocess('GF(23)') == GF(23) raises(OptionError, "Domain.preprocess('Z[]')") assert Domain.preprocess('Z[x]') == ZZ[x] assert Domain.preprocess('Q[x]') == QQ[x] assert Domain.preprocess('ZZ[x]') == ZZ[x] assert Domain.preprocess('QQ[x]') == QQ[x] assert Domain.preprocess('Z[x,y]') == ZZ[x,y] assert Domain.preprocess('Q[x,y]') == QQ[x,y] assert Domain.preprocess('ZZ[x,y]') == ZZ[x,y] assert Domain.preprocess('QQ[x,y]') == QQ[x,y] raises(OptionError, "Domain.preprocess('Z()')") assert Domain.preprocess('Z(x)') == ZZ.frac_field(x) assert Domain.preprocess('Q(x)') == QQ.frac_field(x) assert Domain.preprocess('ZZ(x)') == ZZ.frac_field(x) assert Domain.preprocess('QQ(x)') == QQ.frac_field(x) assert Domain.preprocess('Z(x,y)') == ZZ.frac_field(x,y) assert Domain.preprocess('Q(x,y)') == QQ.frac_field(x,y) assert Domain.preprocess('ZZ(x,y)') == ZZ.frac_field(x,y) assert Domain.preprocess('QQ(x,y)') == QQ.frac_field(x,y) assert Domain.preprocess('Q') == QQ.algebraic_field(I) assert Domain.preprocess('QQ') == QQ.algebraic_field(I) assert Domain.preprocess('Q') == QQ.algebraic_field(sqrt(2), I) assert Domain.preprocess('QQ') == QQ.algebraic_field(sqrt(2), I) raises(OptionError, "Domain.preprocess('abc')") def test_Domain_postprocess(): raises(GeneratorsError, "Domain.postprocess({'gens': (x, y), 'domain': ZZ[y, z]})") def test_Split_preprocess(): assert Split.preprocess(False) is False assert Split.preprocess(True) is True assert Split.preprocess(0) is False assert Split.preprocess(1) is True raises(OptionError, "Split.preprocess(x)") def test_Split_postprocess(): raises(NotImplementedError, "Split.postprocess({'split': True})") def test_Gaussian_preprocess(): assert Gaussian.preprocess(False) is False assert Gaussian.preprocess(True) is True assert Gaussian.preprocess(0) is False assert Gaussian.preprocess(1) is True raises(OptionError, "Gaussian.preprocess(x)") def test_Gaussian_postprocess(): opt = {'gaussian': True} Gaussian.postprocess(opt) assert opt == { 'gaussian': True, 'extension': set([I]), 'domain': QQ.algebraic_field(I), } def test_Extension_preprocess(): assert Extension.preprocess(True) is True assert Extension.preprocess(1) is True assert Extension.preprocess([]) is None assert Extension.preprocess(sqrt(2)) == set([sqrt(2)]) assert Extension.preprocess([sqrt(2)]) == set([sqrt(2)]) assert Extension.preprocess([sqrt(2), I]) == set([sqrt(2), I]) raises(OptionError, "Extension.preprocess(False)") raises(OptionError, "Extension.preprocess(0)") def test_Extension_postprocess(): opt = {'extension': set([sqrt(2)])} Extension.postprocess(opt) assert opt == { 'extension': set([sqrt(2)]), 'domain': QQ.algebraic_field(sqrt(2)), } opt = {'extension': True} Extension.postprocess(opt) assert opt == {'extension': True} def test_Modulus_preprocess(): assert Modulus.preprocess(23) == 23 assert Modulus.preprocess(Integer(23)) == 23 raises(OptionError, "Modulus.preprocess(0)") raises(OptionError, "Modulus.preprocess(x)") def test_Modulus_postprocess(): opt = {'modulus': 5} Modulus.postprocess(opt) assert opt == { 'modulus': 5, 'domain': FF(5), } opt = {'modulus': 5, 'symmetric': False} Modulus.postprocess(opt) assert opt == { 'modulus': 5, 'domain': FF(5, False), 'symmetric': False, } def test_Symmetric_preprocess(): assert Symmetric.preprocess(False) is False assert Symmetric.preprocess(True) is True assert Symmetric.preprocess(0) is False assert Symmetric.preprocess(1) is True raises(OptionError, "Symmetric.preprocess(x)") def test_Symmetric_postprocess(): opt = {'symmetric': True} Symmetric.postprocess(opt) assert opt == {'symmetric': True} def test_Strict_preprocess(): assert Strict.preprocess(False) is False assert Strict.preprocess(True) is True assert Strict.preprocess(0) is False assert Strict.preprocess(1) is True raises(OptionError, "Strict.preprocess(x)") def test_Strict_postprocess(): opt = {'strict': True} Strict.postprocess(opt) assert opt == {'strict': True} def test_Auto_preprocess(): assert Auto.preprocess(False) is False assert Auto.preprocess(True) is True assert Auto.preprocess(0) is False assert Auto.preprocess(1) is True raises(OptionError, "Auto.preprocess(x)") def test_Auto_postprocess(): opt = {'auto': True} Auto.postprocess(opt) assert opt == {'auto': True} def test_Frac_preprocess(): assert Frac.preprocess(False) is False assert Frac.preprocess(True) is True assert Frac.preprocess(0) is False assert Frac.preprocess(1) is True raises(OptionError, "Frac.preprocess(x)") def test_Frac_postprocess(): opt = {'frac': True} Frac.postprocess(opt) assert opt == {'frac': True} def test_Formal_preprocess(): assert Formal.preprocess(False) is False assert Formal.preprocess(True) is True assert Formal.preprocess(0) is False assert Formal.preprocess(1) is True raises(OptionError, "Formal.preprocess(x)") def test_Formal_postprocess(): opt = {'formal': True} Formal.postprocess(opt) assert opt == {'formal': True} def test_Polys_preprocess(): assert Polys.preprocess(False) is False assert Polys.preprocess(True) is True assert Polys.preprocess(0) is False assert Polys.preprocess(1) is True raises(OptionError, "Polys.preprocess(x)") def test_Polys_postprocess(): opt = {'polys': True} Polys.postprocess(opt) assert opt == {'polys': True} def test_Include_preprocess(): assert Include.preprocess(False) is False assert Include.preprocess(True) is True assert Include.preprocess(0) is False assert Include.preprocess(1) is True raises(OptionError, "Include.preprocess(x)") def test_Include_postprocess(): opt = {'include': True} Include.postprocess(opt) assert opt == {'include': True} def test_All_preprocess(): assert All.preprocess(False) is False assert All.preprocess(True) is True assert All.preprocess(0) is False assert All.preprocess(1) is True raises(OptionError, "All.preprocess(x)") def test_All_postprocess(): opt = {'all': True} All.postprocess(opt) assert opt == {'all': True} def test_Gen_preprocess(): pass def test_Gen_postprocess(): opt = {'gen': x} Gen.postprocess(opt) assert opt == {'gen': x} def test_Symbols_preprocess(): raises(OptionError, "Symbols.preprocess(x)") def test_Symbols_postprocess(): opt = {'symbols': [x, y, z]} Symbols.postprocess(opt) assert opt == {'symbols': [x, y, z]} wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_factortools.py0000644000175000017500000006071312014170666027366 0ustar georgeskgeorgesk"""Tools for polynomial factorization routines in characteristic zero. """ from sympy.polys.densearith import ( dup_mul_ground, dmp_mul_ground, dup_pow, dmp_pow, dmp_expand) from sympy.polys.densebasic import ( dup_degree, dmp_degree, dup_normal, dmp_normal, dup_from_raw_dict, dup_to_raw_dict, dmp_from_dict, dmp_to_dict, dmp_nest, dmp_raise) from sympy.polys.densetools import ( dup_primitive, dmp_eval_tail) from sympy.polys.sqfreetools import ( dup_sqf_p) from sympy.polys.factortools import ( dup_trial_division, dmp_trial_division, dup_zz_mignotte_bound, dmp_zz_mignotte_bound, dup_zz_hensel_step, dup_zz_hensel_lift, dup_zz_irreducible_p, dup_zz_zassenhaus, dmp_zz_wang, dmp_zz_wang_non_divisors, dmp_zz_wang_test_points, dmp_zz_wang_lead_coeffs, dmp_zz_wang_hensel_lifting, dup_zz_diophantine, dmp_zz_diophantine, dup_zz_cyclotomic_p, dup_zz_cyclotomic_poly, dup_zz_cyclotomic_factor, dup_zz_factor, dup_zz_factor_sqf, dmp_zz_factor, dup_ext_factor, dmp_ext_factor, dup_factor_list, dmp_factor_list, dup_factor_list_include, dmp_factor_list_include) from sympy.polys.specialpolys import ( f_1, f_2, f_3, f_4, f_5, f_6, w_1, w_2) from sympy.polys.polyconfig import setup from sympy.polys.polyerrors import DomainError from sympy.polys.polyclasses import DMP, DMF, ANP from sympy.polys.domains import FF, ZZ, QQ, RR, EX from sympy import nextprime, sin, sqrt, I from sympy.utilities.pytest import raises def test_dup_trial_division(): assert dup_trial_division([1,8,25,38,28,8], ([1,1], [1,2]), ZZ) == [([1,1], 2), ([1,2], 3)] def test_dmp_trial_division(): assert dmp_trial_division([[1],[8],[25],[38],[28],[8]], ([[1],[1]], [[1],[2]]), 1, ZZ) == [([[1],[1]], 2), ([[1],[2]], 3)] def test_dup_zz_mignotte_bound(): assert dup_zz_mignotte_bound([2,3,4], ZZ) == 32 def test_dmp_zz_mignotte_bound(): assert dmp_zz_mignotte_bound([[2],[3],[4]], 1, ZZ) == 32 def test_dup_zz_hensel_step(): f = dup_from_raw_dict({4:1, 0:-1}, ZZ) g = dup_from_raw_dict({3:1, 2:2, 1:-1, 0:-2}, ZZ) h = dup_from_raw_dict({1:1, 0:-2}, ZZ) s = dup_from_raw_dict({0:-2}, ZZ) t = dup_from_raw_dict({2:2, 1:-2, 0:-1}, ZZ) G, H, S, T = dup_zz_hensel_step(5, f, g, h, s, t, ZZ) assert G == dup_from_raw_dict({3:1, 2:7, 1:-1, 0:-7}, ZZ) assert H == dup_from_raw_dict({1:1, 0:-7}, ZZ) assert S == dup_from_raw_dict({0:8}, ZZ) assert T == dup_from_raw_dict({2:-8, 1:-12, 0:-1}, ZZ) def test_dup_zz_hensel_lift(): f = dup_from_raw_dict({4:1, 0:-1}, ZZ) f1 = dup_from_raw_dict({1:1, 0:-1}, ZZ) f2 = dup_from_raw_dict({1:1, 0:-2}, ZZ) f3 = dup_from_raw_dict({1:1, 0: 2}, ZZ) f4 = dup_from_raw_dict({1:1, 0: 1}, ZZ) ff_list = dup_zz_hensel_lift(5, f, [f1, f2, f3, f4], 4, ZZ) assert dup_to_raw_dict(ff_list[0]) == {0: -1, 1: 1} assert dup_to_raw_dict(ff_list[1]) == {0: -182, 1: 1} assert dup_to_raw_dict(ff_list[2]) == {0: 182, 1: 1} assert dup_to_raw_dict(ff_list[3]) == {0: 1, 1: 1} def test_dup_zz_irreducible_p(): assert dup_zz_irreducible_p([3, 2, 6, 8, 7], ZZ) is None assert dup_zz_irreducible_p([3, 2, 6, 8, 4], ZZ) is None assert dup_zz_irreducible_p([3, 2, 6, 8, 10], ZZ) == True assert dup_zz_irreducible_p([3, 2, 6, 8, 14], ZZ) == True def test_dup_zz_cyclotomic_p(): assert dup_zz_cyclotomic_p([1,-1], ZZ) == True assert dup_zz_cyclotomic_p([1,1], ZZ) == True assert dup_zz_cyclotomic_p([1,1,1], ZZ) == True assert dup_zz_cyclotomic_p([1,0,1], ZZ) == True assert dup_zz_cyclotomic_p([1,1,1,1,1], ZZ) == True assert dup_zz_cyclotomic_p([1,-1,1], ZZ) == True assert dup_zz_cyclotomic_p([1,1,1,1,1,1,1], ZZ) == True assert dup_zz_cyclotomic_p([1,0,0,0,1], ZZ) == True assert dup_zz_cyclotomic_p([1,0,0,1,0,0,1], ZZ) == True assert dup_zz_cyclotomic_p([], ZZ) == False assert dup_zz_cyclotomic_p([1], ZZ) == False assert dup_zz_cyclotomic_p([1, 0], ZZ) == False assert dup_zz_cyclotomic_p([1, 2], ZZ) == False assert dup_zz_cyclotomic_p([3, 1], ZZ) == False assert dup_zz_cyclotomic_p([1, 0, -1], ZZ) == False f = [1, 0, 1, 0, 0, 0,-1, 0, 1, 0,-1, 0, 0, 0, 1, 0, 1] assert dup_zz_cyclotomic_p(f, ZZ) == False g = [1, 0, 1, 0, 0, 0,-1, 0,-1, 0,-1, 0, 0, 0, 1, 0, 1] assert dup_zz_cyclotomic_p(g, ZZ) == True assert dup_zz_cyclotomic_p([QQ(1),QQ(1),QQ(1)], QQ) == True assert dup_zz_cyclotomic_p([QQ(1,2),QQ(1),QQ(1)], QQ) == False def test_dup_zz_cyclotomic_poly(): assert dup_zz_cyclotomic_poly(1, ZZ) == [1,-1] assert dup_zz_cyclotomic_poly(2, ZZ) == [1,1] assert dup_zz_cyclotomic_poly(3, ZZ) == [1,1,1] assert dup_zz_cyclotomic_poly(4, ZZ) == [1,0,1] assert dup_zz_cyclotomic_poly(5, ZZ) == [1,1,1,1,1] assert dup_zz_cyclotomic_poly(6, ZZ) == [1,-1,1] assert dup_zz_cyclotomic_poly(7, ZZ) == [1,1,1,1,1,1,1] assert dup_zz_cyclotomic_poly(8, ZZ) == [1,0,0,0,1] assert dup_zz_cyclotomic_poly(9, ZZ) == [1,0,0,1,0,0,1] def test_dup_zz_cyclotomic_factor(): assert dup_zz_cyclotomic_factor([], ZZ) is None assert dup_zz_cyclotomic_factor([1], ZZ) is None f = dup_from_raw_dict({10:2, 0:-1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) is None f = dup_from_raw_dict({10:1, 0:-3}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) is None f = dup_from_raw_dict({10:1, 5:1, 0:-1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) is None f = dup_from_raw_dict({1:1,0:1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) == \ [[1, 1]] f = dup_from_raw_dict({1:1,0:-1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) == \ [[1, -1]] f = dup_from_raw_dict({2:1,0:1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) == \ [[1, 0, 1]] f = dup_from_raw_dict({2:1,0:-1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) == \ [[1,-1], [1, 1]] f = dup_from_raw_dict({27:1,0:1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) == \ [[1, 1], [1, -1, 1], [1, 0, 0, -1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1]] f = dup_from_raw_dict({27:1,0:-1}, ZZ) assert dup_zz_cyclotomic_factor(f, ZZ) == \ [[1, -1], [1, 1, 1], [1, 0, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1]] def test_dup_zz_factor(): assert dup_zz_factor([], ZZ) == (0, []) assert dup_zz_factor([7], ZZ) == (7, []) assert dup_zz_factor([-7], ZZ) == (-7, []) assert dup_zz_factor_sqf([], ZZ) == (0, []) assert dup_zz_factor_sqf([7], ZZ) == (7, []) assert dup_zz_factor_sqf([-7], ZZ) == (-7, []) assert dup_zz_factor([2,4], ZZ) == \ (2, [([1, 2], 1)]) assert dup_zz_factor_sqf([2,4], ZZ) == \ (2, [([1, 2], 1)]) f = [1,0,0,1,1] for i in xrange(0, 20): assert dup_zz_factor(f, ZZ) == (1, [(f, 1)]) assert dup_zz_factor([1,2,2], ZZ) == \ (1, [([1,2,2], 1)]) assert dup_zz_factor([18,12,2], ZZ) == \ (2, [([3, 1], 2)]) assert dup_zz_factor([-9,0,1], ZZ) == \ (-1, [([3,-1], 1), ([3, 1], 1)]) assert dup_zz_factor_sqf([-9,0,1], ZZ) == \ (-1, [[3,-1], [3, 1]]) assert dup_zz_factor([1,-6,11,-6], ZZ) == \ (1, [([1,-3], 1), ([1,-2], 1), ([1,-1], 1)]) assert dup_zz_factor_sqf([1,-6,11,-6], ZZ) == \ (1, [[1,-3], [1,-2], [1,-1]]) assert dup_zz_factor([3,10,13,10], ZZ) == \ (1, [([1,2], 1), ([3,4,5], 1)]) assert dup_zz_factor_sqf([3,10,13,10], ZZ) == \ (1, [[1,2], [3,4,5]]) assert dup_zz_factor([-1,0,0,0,1,0,0], ZZ) == \ (-1, [([1,-1], 1), ([1, 1], 1), ([1, 0], 2), ([1, 0, 1], 1)]) f = [1080, 5184, 2099, 744, 2736, -648, 129, 0, -324] assert dup_zz_factor(f, ZZ) == \ (1, [([5, 24, 9, 0, 12], 1), ([216, 0, 31, 0, -27], 1)]) f = [-29802322387695312500000000000000000000, 0, 0, 0, 0, 2980232238769531250000000000000000, 0, 0, 0, 0, 1743435859680175781250000000000, 0, 0, 0, 0, 114142894744873046875000000, 0, 0, 0, 0, -210106372833251953125, 0, 0, 0, 0, 95367431640625] assert dup_zz_factor(f, ZZ) == \ (-95367431640625, [([5, -1], 1), ([100, 10, -1], 2), ([625, 125, 25, 5, 1], 1), ([10000, -3000, 400, -20, 1], 2), ([10000, 2000, 400, 30, 1], 2)]) f = dup_from_raw_dict({10:1, 0:-1}, ZZ) setup('USE_CYCLOTOMIC_FACTOR', True) F_0 = dup_zz_factor(f, ZZ) setup('USE_CYCLOTOMIC_FACTOR', False) F_1 = dup_zz_factor(f, ZZ) assert F_0 == F_1 == \ (1, [([1,-1], 1), ([1, 1], 1), ([1,-1, 1,-1, 1], 1), ([1, 1, 1, 1, 1], 1)]) setup('USE_CYCLOTOMIC_FACTOR') f = dup_from_raw_dict({10:1, 0:1}, ZZ) setup('USE_CYCLOTOMIC_FACTOR', True) F_0 = dup_zz_factor(f, ZZ) setup('USE_CYCLOTOMIC_FACTOR', False) F_1 = dup_zz_factor(f, ZZ) assert F_0 == F_1 == \ (1, [([1, 0, 1], 1), ([1, 0, -1, 0, 1, 0, -1, 0, 1], 1)]) setup('USE_CYCLOTOMIC_FACTOR') def test_dmp_zz_wang(): p = ZZ(nextprime(dmp_zz_mignotte_bound(w_1, 2, ZZ))) assert p == ZZ(6291469) t_1, k_1, e_1 = dmp_normal([[1],[]], 1, ZZ), 1, ZZ(-14) t_2, k_2, e_2 = dmp_normal([[1, 0]], 1, ZZ), 2, ZZ(3) t_3, k_3, e_3 = dmp_normal([[1],[ 1, 0]], 1, ZZ), 2, ZZ(-11) t_4, k_4, e_4 = dmp_normal([[1],[-1, 0]], 1, ZZ), 1, ZZ(-17) T = [t_1, t_2, t_3, t_4] K = [k_1, k_2, k_3, k_4] E = [e_1, e_2, e_3, e_4] T = zip(T, K) A = [ZZ(-14), ZZ(3)] S = dmp_eval_tail(w_1, A, 2, ZZ) cs, s = dup_primitive(S, ZZ) assert cs == 1 and s == S == \ dup_normal([1036728, 915552, 55748, 105621, -17304, -26841, -644], ZZ) assert dmp_zz_wang_non_divisors(E, cs, 4, ZZ) == [7, 3, 11, 17] assert dup_sqf_p(s, ZZ) and dup_degree(s) == dmp_degree(w_1, 2) _, H = dup_zz_factor_sqf(s, ZZ) h_1 = dup_normal([44, 42, 1], ZZ) h_2 = dup_normal([126, -9, 28], ZZ) h_3 = dup_normal([187, 0, -23], ZZ) assert H == [h_1, h_2, h_3] lc_1 = dmp_normal([[-4], [-4,0]], 1, ZZ) lc_2 = dmp_normal([[-1,0,0], []], 1, ZZ) lc_3 = dmp_normal([[1], [], [-1,0,0]], 1, ZZ) LC = [lc_1, lc_2, lc_3] assert dmp_zz_wang_lead_coeffs(w_1, T, cs, E, H, A, 2, ZZ) == (w_1, H, LC) H_1 = [ dmp_normal(t, 0, ZZ) for t in [[44L,42L,1L],[126L,-9L,28L],[187L,0L,-23L]] ] H_2 = [ dmp_normal(t, 1, ZZ) for t in [[[-4,-12],[-3,0],[1]],[[-9,0],[-9],[-2,0]],[[1,0,-9],[],[1,-9]]] ] H_3 = [ dmp_normal(t, 1, ZZ) for t in [[[-4,-12],[-3,0],[1]],[[-9,0],[-9],[-2,0]],[[1,0,-9],[],[1,-9]]] ] c_1 = dmp_normal([-70686,-5863,-17826,2009,5031,74], 0, ZZ) c_2 = dmp_normal([[9,12,-45,-108,-324],[18,-216,-810,0],[2,9,-252,-288,-945],[-30,-414,0],[2,-54,-3,81],[12,0]], 1, ZZ) c_3 = dmp_normal([[-36,-108,0],[-27,-36,-108],[-8,-42,0],[-6,0,9],[2,0]], 1, ZZ) T_1 = [ dmp_normal(t, 0, ZZ) for t in [[-3,0],[-2],[1]] ] T_2 = [ dmp_normal(t, 1, ZZ) for t in [[[-1,0],[]],[[-3],[]],[[-6]]] ] T_3 = [ dmp_normal(t, 1, ZZ) for t in [[[]],[[]],[[-1]]] ] assert dmp_zz_diophantine(H_1, c_1, [], 5, p, 0, ZZ) == T_1 assert dmp_zz_diophantine(H_2, c_2, [ZZ(-14)], 5, p, 1, ZZ) == T_2 assert dmp_zz_diophantine(H_3, c_3, [ZZ(-14)], 5, p, 1, ZZ) == T_3 factors = dmp_zz_wang_hensel_lifting(w_1, H, LC, A, p, 2, ZZ) assert dmp_expand(factors, 2, ZZ) == w_1 def test_dmp_zz_factor(): assert dmp_zz_factor([], 0, ZZ) == (0, []) assert dmp_zz_factor([7], 0, ZZ) == (7, []) assert dmp_zz_factor([-7], 0, ZZ) == (-7, []) assert dmp_zz_factor([[]], 1, ZZ) == (0, []) assert dmp_zz_factor([[7]], 1, ZZ) == (7, []) assert dmp_zz_factor([[-7]], 1, ZZ) == (-7, []) assert dmp_zz_factor([[1], []], 1, ZZ) == \ (1, [([[1], []], 1)]) assert dmp_zz_factor([[4], []], 1, ZZ) == \ (4, [([[1], []], 1)]) assert dmp_zz_factor([[4], [2]], 1, ZZ) == \ (2, [([[2], [1]], 1)]) assert dmp_zz_factor([[1, 0], [1]], 1, ZZ) == \ (1, [([[1, 0], [1]], 1)]) assert dmp_zz_factor([[1,0,1]], 1, ZZ) == \ (1, [([[1, 0, 1]], 1)]) assert dmp_zz_factor([[1,0,-1]], 1, ZZ) == \ (1, [([[1,-1]], 1), ([[1, 1]], 1)]) assert dmp_zz_factor([[1, 6, 9], [], [-1]], 1, ZZ) == \ (1, [([[1, 3], [-1]], 1), ([[1, 3], [1]], 1)]) assert dmp_zz_factor([1, 0, -9], 0, ZZ) == \ (1, [([1, -3], 1), ([1, 3], 1)]) assert dmp_zz_factor([[1, 0, 0], [], [-9]], 1, ZZ) == \ (1, [([[1, 0], [-3]], 1), ([[1, 0], [3]], 1)]) assert dmp_zz_factor([[[1, 0, 0], [], []], [[]], [[-9]]], 2, ZZ) == \ (1, [([[[1, 0], []], [[-3]]], 1), ([[[1, 0], []], [[3]]], 1)]) assert dmp_zz_factor([[[[1, 0, 0], [], []], [[]], [[]]], [[[]]], [[[-9]]]], 3, ZZ) == \ (1, [([[[[1, 0], []], [[]]], [[[-3]]]], 1), ([[[[1, 0], []], [[]]], [[[3]]]], 1)]) assert dmp_zz_factor(f_1, 2, ZZ) == \ (1, [([[[1]], [[1, 0], [20]]], 1), ([[[1], []], [[1, 10]]], 1), ([[[1, 0]], [[1], [30]]], 1)]) assert dmp_zz_factor(f_2, 2, ZZ) == \ (1, [([[[1], [], [1, 0, 0]], [[]], [[1], [90]]], 1), ([[[1], [1, 0]], [[]], [[]], [[1, -11]]], 1)]) assert dmp_zz_factor(f_3, 2, ZZ) == \ (1, [([[[1], [], []], [[1, 0, 0, 0, 1]], [[1, 0]]], 1), ([[[1]], [[]], [[1, 0], []], [[1], [1, 0, 0, 0], []]], 1)]) assert dmp_zz_factor(f_4, 2, ZZ) == \ (-1, [([[[1], [], [], []], [[1, 0, 0]]], 1), ([[[1, 0]], [[]], [[1, 0, 0], [], [], [], [5]]], 1), ([[[1], []], [[]], [[]], [[-1, 0, -3]]], 1), ([[[1], [], [], [], []], [[]], [[]], [[1, 0, 0]]], 1)]) assert dmp_zz_factor(f_5, 2, ZZ) == \ (-1, [([[[1]], [[1], [-1, 0]]], 3)]) assert dmp_zz_factor(f_6, 3, ZZ) == \ (1, [([[[[47]], [[]]], [[[1, 0, 0], [], [], [-1, 0, 0]]]], 1), ([[[[45]]], [[[]]], [[[]]], [[[-9]], [[-1]], [[]], [[3], [], [2, 0], []]]], 1)]) assert dmp_zz_factor(w_1, 2, ZZ) == \ (1, [([[[1], [], [-1, 0, 0]], [[]], [[1], [-1, 0, 0]]], 1), ([[[1, 0, 0], []], [[3, 0]], [[2], []]], 1), ([[[4], [4, 0]], [[1, 0], []], [[-1]]], 1)]) f = [[-12, 0], [], [], [], [240, 0, 0, 0], [], [-768, 0, 0, 0, 0], [], [1080, 0, 0, 0, 0, 0], [], [-768, 0, 0, 0, 0, 0, 0], [], [240, 0, 0, 0, 0, 0, 0, 0], [], [], [], [-12, 0, 0, 0, 0, 0, 0, 0, 0, 0]] assert dmp_zz_factor(f, 1, ZZ) == \ (-12, [([[1, 0]], 1), ([[1], [], [-1, 0]], 6), ([[1], [], [6, 0], [], [1, 0, 0]], 1)]) def test_dup_ext_factor(): h = [QQ(1),QQ(0),QQ(1)] K = QQ.algebraic_field(I) assert dup_ext_factor([], K) == (ANP([], h, QQ), []) f = [ANP([QQ(1)], h, QQ), ANP([QQ(1)], h, QQ)] assert dup_ext_factor(f, K) == (ANP([QQ(1)], h, QQ), [(f, 1)]) g = [ANP([QQ(2)], h, QQ), ANP([QQ(2)], h, QQ)] assert dup_ext_factor(g, K) == (ANP([QQ(2)], h, QQ), [(f, 1)]) f = [ANP([QQ(7)], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([QQ(1,1)], h, QQ)] g = [ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([QQ(1,7)], h, QQ)] assert dup_ext_factor(f, K) == (ANP([QQ(7)], h, QQ), [(g, 1)]) f = [ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([QQ(1)], h, QQ)] assert dup_ext_factor(f, K) == \ (ANP([QQ(1,1)], h, QQ), [ ([ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([QQ(-1),QQ(0)], h, QQ)], 1), ([ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([QQ( 1),QQ(0)], h, QQ)], 1), ]) f = [ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([QQ(1)], h, QQ)] assert dup_ext_factor(f, K) == \ (ANP([QQ(1,1)], h, QQ), [ ([ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([QQ(-1),QQ(0)], h, QQ)], 1), ([ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([QQ( 1),QQ(0)], h, QQ)], 1), ]) h = [QQ(1),QQ(0),QQ(-2)] K = QQ.algebraic_field(sqrt(2)) f = [ANP([QQ(1)], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([], h, QQ), ANP([QQ(1,1)], h, QQ)] assert dup_ext_factor(f, K) == \ (ANP([QQ(1)], h, QQ), [ ([ANP([QQ(1)], h, QQ), ANP([QQ(-1),QQ(0)], h, QQ), ANP([QQ(1)], h, QQ)], 1), ([ANP([QQ(1)], h, QQ), ANP([QQ( 1),QQ(0)], h, QQ), ANP([QQ(1)], h, QQ)], 1), ]) f = [ANP([QQ(1,1)], h, QQ), ANP([2,0], h, QQ), ANP([QQ(2,1)], h, QQ)] assert dup_ext_factor(f, K) == \ (ANP([QQ(1,1)], h, QQ), [ ([ANP([1], h, QQ), ANP([1,0], h, QQ)], 2), ]) assert dup_ext_factor(dup_pow(f, 3, K), K) == \ (ANP([QQ(1,1)], h, QQ), [ ([ANP([1], h, QQ), ANP([1,0], h, QQ)], 6), ]) f = dup_mul_ground(f, ANP([QQ(2,1)], h, QQ), K) assert dup_ext_factor(f, K) == \ (ANP([QQ(2,1)], h, QQ), [ ([ANP([1], h, QQ), ANP([1,0], h, QQ)], 2), ]) assert dup_ext_factor(dup_pow(f, 3, K), K) == \ (ANP([QQ(8,1)], h, QQ), [ ([ANP([1], h, QQ), ANP([1,0], h, QQ)], 6), ]) h = [QQ(1,1), QQ(0,1), QQ(1,1)] K = QQ.algebraic_field(I) f = [ANP([QQ(4,1)], h, QQ), ANP([], h, QQ), ANP([QQ(9,1)], h, QQ)] assert dup_ext_factor(f, K) == \ (ANP([QQ(4,1)], h, QQ), [ ([ANP([QQ(1,1)], h, QQ), ANP([-QQ(3,2), QQ(0,1)], h, QQ)], 1), ([ANP([QQ(1,1)], h, QQ), ANP([ QQ(3,2), QQ(0,1)], h, QQ)], 1), ]) f = [ANP([QQ(4,1)], h, QQ), ANP([QQ(8,1)], h, QQ), ANP([QQ(77,1)], h, QQ), ANP([QQ(18,1)], h, QQ), ANP([QQ(153,1)], h, QQ)] assert dup_ext_factor(f, K) == \ (ANP([QQ(4,1)], h, QQ), [ ([ANP([QQ(1,1)], h, QQ), ANP([-QQ(4,1), QQ(1,1)], h, QQ)], 1), ([ANP([QQ(1,1)], h, QQ), ANP([-QQ(3,2), QQ(0,1)], h, QQ)], 1), ([ANP([QQ(1,1)], h, QQ), ANP([ QQ(3,2), QQ(0,1)], h, QQ)], 1), ([ANP([QQ(1,1)], h, QQ), ANP([ QQ(4,1), QQ(1,1)], h, QQ)], 1), ]) def test_dmp_ext_factor(): h = [QQ(1),QQ(0),QQ(-2)] K = QQ.algebraic_field(sqrt(2)) assert dmp_ext_factor([], 0, K) == (ANP([], h, QQ), []) assert dmp_ext_factor([[]], 1, K) == (ANP([], h, QQ), []) f = [[ANP([QQ(1)], h, QQ)], [ANP([QQ(1)], h, QQ)]] assert dmp_ext_factor(f, 1, K) == (ANP([QQ(1)], h, QQ), [(f, 1)]) g = [[ANP([QQ(2)], h, QQ)], [ANP([QQ(2)], h, QQ)]] assert dmp_ext_factor(g, 1, K) == (ANP([QQ(2)], h, QQ), [(f, 1)]) f = [[ANP([QQ(1)], h, QQ)], [], [ANP([QQ(-2)], h, QQ), ANP([], h, QQ), ANP([], h, QQ)]] assert dmp_ext_factor(f, 1, K) == \ (ANP([QQ(1)], h, QQ), [ ([[ANP([QQ(1)], h, QQ)], [ANP([QQ(-1),QQ(0)], h, QQ), ANP([], h, QQ)]], 1), ([[ANP([QQ(1)], h, QQ)], [ANP([QQ( 1),QQ(0)], h, QQ), ANP([], h, QQ)]], 1), ]) f = [[ANP([QQ(2)], h, QQ)], [], [ANP([QQ(-4)], h, QQ), ANP([], h, QQ), ANP([], h, QQ)]] assert dmp_ext_factor(f, 1, K) == \ (ANP([QQ(2)], h, QQ), [ ([[ANP([QQ(1)], h, QQ)], [ANP([QQ(-1),QQ(0)], h, QQ), ANP([], h, QQ)]], 1), ([[ANP([QQ(1)], h, QQ)], [ANP([QQ( 1),QQ(0)], h, QQ), ANP([], h, QQ)]], 1), ]) def test_dup_factor_list(): assert dup_factor_list([], ZZ) == (ZZ(0), []) assert dup_factor_list([], QQ) == (QQ(0), []) assert dup_factor_list([], ZZ['y']) == (DMP([],ZZ), []) assert dup_factor_list([], QQ['y']) == (DMP([],QQ), []) assert dup_factor_list_include([], ZZ) == [([], 1)] assert dup_factor_list([ZZ(7)], ZZ) == (ZZ(7), []) assert dup_factor_list([QQ(1,7)], QQ) == (QQ(1,7), []) assert dup_factor_list([DMP([ZZ(7)],ZZ)], ZZ['y']) == (DMP([ZZ(7)],ZZ), []) assert dup_factor_list([DMP([QQ(1,7)],QQ)], QQ['y']) == (DMP([QQ(1,7)],QQ), []) assert dup_factor_list_include([ZZ(7)], ZZ) == [([ZZ(7)], 1)] assert dup_factor_list([ZZ(1),ZZ(2),ZZ(1)], ZZ) == \ (ZZ(1), [([ZZ(1), ZZ(1)], 2)]) assert dup_factor_list([QQ(1,2),QQ(1),QQ(1,2)], QQ) == \ (QQ(1,2), [([QQ(1),QQ(1)], 2)]) assert dup_factor_list_include([ZZ(1),ZZ(2),ZZ(1)], ZZ) == \ [([ZZ(1), ZZ(1)], 2)] K = FF(2) assert dup_factor_list([K(1),K(0),K(1)], K) == \ (K(1), [([K(1), K(1)], 2)]) assert dup_factor_list([RR(1.0),RR(2.0),RR(1.0)], RR) == \ (RR(1.0), [([RR(1.0),RR(1.0)], 2)]) assert dup_factor_list([RR(2.0),RR(4.0),RR(2.0)], RR) == \ (RR(2.0), [([RR(1.0),RR(1.0)], 2)]) f = [DMP([ZZ(4),ZZ(0)],ZZ),DMP([ZZ(4),ZZ(0),ZZ(0)],ZZ),DMP([],ZZ)] assert dup_factor_list(f, ZZ['y']) == \ (DMP([ZZ(4)],ZZ), [([DMP([ZZ(1),ZZ(0)],ZZ)], 1), ([DMP([ZZ(1)],ZZ),DMP([],ZZ)], 1), ([DMP([ZZ(1)],ZZ),DMP([ZZ(1),ZZ(0)],ZZ)], 1)]) f = [DMP([QQ(1,2),QQ(0)],ZZ),DMP([QQ(1,2),QQ(0),QQ(0)],ZZ),DMP([],ZZ)] assert dup_factor_list(f, QQ['y']) == \ (DMP([QQ(1,2)],QQ), [([DMP([QQ(1),QQ(0)],QQ)], 1), ([DMP([QQ(1)],QQ),DMP([],QQ)], 1), ([DMP([QQ(1)],QQ),DMP([QQ(1),QQ(0)],QQ)], 1)]) K = QQ.algebraic_field(I) h = [QQ(1,1), QQ(0,1), QQ(1,1)] f = [ANP([QQ(1,1)], h, QQ), ANP([], h, QQ), ANP([QQ(2,1)], h, QQ), ANP([], h, QQ), ANP([], h, QQ)] assert dup_factor_list(f, K) == \ (ANP([QQ(1,1)], h, QQ), [([ANP([QQ(1,1)], h, QQ), ANP([], h, QQ)], 2), ([ANP([QQ(1,1)], h, QQ), ANP([], h, QQ), ANP([QQ(2,1)], h, QQ)], 1)]) raises(DomainError, "dup_factor_list([EX(sin(1))], EX)") def test_dmp_factor_list(): assert dmp_factor_list([[]], 1, ZZ) == (ZZ(0), []) assert dmp_factor_list([[]], 1, QQ) == (QQ(0), []) assert dmp_factor_list([[]], 1, ZZ['y']) == (DMP([],ZZ), []) assert dmp_factor_list([[]], 1, QQ['y']) == (DMP([],QQ), []) assert dmp_factor_list_include([[]], 1, ZZ) == [([[]], 1)] assert dmp_factor_list([[ZZ(7)]], 1, ZZ) == (ZZ(7), []) assert dmp_factor_list([[QQ(1,7)]], 1, QQ) == (QQ(1,7), []) assert dmp_factor_list([[DMP([ZZ(7)],ZZ)]], 1, ZZ['y']) == (DMP([ZZ(7)],ZZ), []) assert dmp_factor_list([[DMP([QQ(1,7)],QQ)]], 1, QQ['y']) == (DMP([QQ(1,7)],QQ), []) assert dmp_factor_list_include([[ZZ(7)]], 1, ZZ) == [([[ZZ(7)]], 1)] f, g = [ZZ(1),ZZ(2),ZZ(1)], [ZZ(1),ZZ(1)] assert dmp_factor_list(dmp_nest(f, 200, ZZ), 200, ZZ) == \ (ZZ(1), [(dmp_nest(g, 200, ZZ), 2)]) assert dmp_factor_list(dmp_raise(f, 200, 0, ZZ), 200, ZZ) == \ (ZZ(1), [(dmp_raise(g, 200, 0, ZZ), 2)]) assert dmp_factor_list([ZZ(1),ZZ(2),ZZ(1)], 0, ZZ) == \ (ZZ(1), [([ZZ(1), ZZ(1)], 2)]) assert dmp_factor_list([QQ(1,2),QQ(1),QQ(1,2)], 0, QQ) == \ (QQ(1,2), [([QQ(1),QQ(1)], 2)]) assert dmp_factor_list([[ZZ(1)],[ZZ(2)],[ZZ(1)]], 1, ZZ) == \ (ZZ(1), [([[ZZ(1)], [ZZ(1)]], 2)]) assert dmp_factor_list([[QQ(1,2)],[QQ(1)],[QQ(1,2)]], 1, QQ) == \ (QQ(1,2), [([[QQ(1)],[QQ(1)]], 2)]) f = [[ZZ(4),ZZ(0)],[ZZ(4),ZZ(0),ZZ(0)],[]] assert dmp_factor_list(f, 1, ZZ) == \ (ZZ(4), [([[ZZ(1),ZZ(0)]], 1), ([[ZZ(1)],[]], 1), ([[ZZ(1)],[ZZ(1),ZZ(0)]], 1)]) assert dmp_factor_list_include(f, 1, ZZ) == \ [([[ZZ(4),ZZ(0)]], 1), ([[ZZ(1)],[]], 1), ([[ZZ(1)],[ZZ(1),ZZ(0)]], 1)] f = [[QQ(1,2),QQ(0)],[QQ(1,2),QQ(0),QQ(0)],[]] assert dmp_factor_list(f, 1, QQ) == \ (QQ(1,2), [([[QQ(1),QQ(0)]], 1), ([[QQ(1)],[]], 1), ([[QQ(1)],[QQ(1),QQ(0)]], 1)]) f = [[RR(2.0)],[],[-RR(8.0),RR(0.0),RR(0.0)]] assert dmp_factor_list(f, 1, RR) == \ (RR(2.0), [([[RR(1.0)],[-RR(2.0),RR(0.0)]], 1), ([[RR(1.0)],[ RR(2.0),RR(0.0)]], 1)]) f = [[DMP([ZZ(4),ZZ(0)],ZZ)],[DMP([ZZ(4),ZZ(0),ZZ(0)],ZZ)],[DMP([],ZZ)]] assert dmp_factor_list(f, 1, ZZ['y']) == \ (DMP([ZZ(4)],ZZ), [([[DMP([ZZ(1),ZZ(0)],ZZ)]], 1), ([[DMP([ZZ(1)],ZZ)],[]], 1), ([[DMP([ZZ(1)],ZZ)],[DMP([ZZ(1),ZZ(0)],ZZ)]], 1)]) f = [[DMP([QQ(1,2),QQ(0)],ZZ)],[DMP([QQ(1,2),QQ(0),QQ(0)],ZZ)],[DMP([],ZZ)]] assert dmp_factor_list(f, 1, QQ['y']) == \ (DMP([QQ(1,2)],QQ), [([[DMP([QQ(1),QQ(0)],QQ)]], 1), ([[DMP([QQ(1)],QQ)],[]], 1), ([[DMP([QQ(1)],QQ)],[DMP([QQ(1),QQ(0)],QQ)]], 1)]) K = FF(2) raises(DomainError, "dmp_factor_list([[K(1)],[],[K(1),K(0),K(0)]], 1, K)") raises(DomainError, "dmp_factor_list([[EX(sin(1))]], 1, EX)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_groebnertools.py0000644000175000017500000004007212014170666027707 0ustar georgeskgeorgesk"""Tests for sparse distributed polynomials and Groebner bases. """ from sympy.polys.groebnertools import ( sdp_LC, sdp_LM, sdp_LT, sdp_del_LT, sdp_coeffs, sdp_monoms, sdp_sort, sdp_strip, sdp_normal, sdp_from_dict, sdp_to_dict, sdp_indep_p, sdp_one_p, sdp_one, sdp_term_p, sdp_abs, sdp_neg, sdp_add_term, sdp_sub_term, sdp_mul_term, sdp_add, sdp_sub, sdp_mul, sdp_sqr, sdp_pow, sdp_monic, sdp_content, sdp_primitive, _term_rr_div, _term_ff_div, sdp_div, sdp_quo, sdp_rem, sdp_lcm, sdp_gcd, sdp_groebner, ) from sympy.polys.monomialtools import ( monomial_lex_key as O_lex, monomial_grlex_key as O_grlex, monomial_grevlex_key as O_grevlex, ) from sympy.polys.polyerrors import ( ExactQuotientFailed, DomainError, ) from sympy.polys.domains import ZZ, QQ from sympy import S, Symbol, symbols, groebner from sympy.utilities.pytest import raises, skip, XFAIL def test_sdp_LC(): assert sdp_LC([], QQ) == QQ(0) assert sdp_LC([((1,0), QQ(1,2))], QQ) == QQ(1,2) assert sdp_LC([((1,1), QQ(1,4)), ((1,0), QQ(1,2))], QQ) == QQ(1,4) def test_sdp_LM(): assert sdp_LM([], 1) == (0, 0) assert sdp_LM([((1,0), QQ(1,2))], 1) == (1, 0) assert sdp_LM([((1,1), QQ(1,4)), ((1,0), QQ(1,2))], 1) == (1, 1) def test_sdp_LT(): assert sdp_LT([], 1, QQ) == ((0, 0), QQ(0)) assert sdp_LT([((1,0), QQ(1,2))], 1, QQ) == ((1, 0), QQ(1,2)) assert sdp_LT([((1,1), QQ(1,4)), ((1,0), QQ(1,2))], 1, QQ) == ((1, 1), QQ(1,4)) def test_sdp_del_LT(): assert sdp_del_LT([]) == [] assert sdp_del_LT([((1,0), QQ(1,2))]) == [] assert sdp_del_LT([((1,1), QQ(1,4)), ((1,0), QQ(1,2))]) == [((1,0), QQ(1,2))] def test_sdp_coeffs(): assert sdp_coeffs([]) == [] assert sdp_coeffs([((1,0), QQ(1,2))]) == [QQ(1,2)] assert sdp_coeffs([((1,1), QQ(1,4)), ((1,0), QQ(1,2))]) == [QQ(1,4), QQ(1,2)] def test_sdp_monoms(): assert sdp_monoms([]) == [] assert sdp_monoms([((1,0), QQ(1,2))]) == [(1,0)] assert sdp_monoms([((1,1), QQ(1,4)), ((1,0), QQ(1,2))]) == [(1,1), (1,0)] def test_sdp_sort(): pass def test_sdp_strip(): assert sdp_strip([((2,2), 0), ((1,1), 1), ((0,0), 0)]) == [((1,1), 1)] def test_sdp_normal(): pass def test_sdp_from_dict(): pass def test_sdp_indep_p(): pass def test_sdp_one_p(): pass def test_sdp_one(): pass def test_sdp_term_p(): pass def test_sdp_abs(): pass def test_sdp_neg(): pass def test_sdp_add_term(): pass def test_sdp_sub_term(): pass def test_sdp_mul_term(): pass def test_sdp_add(): pass def test_sdp_sub(): pass def test_sdp_mul(): pass def test_sdp_sqr(): pass def test_sdp_pow(): f = sdp_from_dict({(1,): 2, (0,): 3}, O_grlex) assert sdp_pow(f, 0, 0, O_grlex, ZZ) == sdp_one(0, ZZ) assert sdp_pow(f, 1, 0, O_grlex, ZZ) == f assert sdp_pow(f, 2, 0, O_grlex, ZZ) == \ sdp_from_dict({(2,): 4, (1,): 12, (0,): 9}, O_grlex) assert sdp_pow(f, 3, 0, O_grlex, ZZ) == \ sdp_from_dict({(3,): 8, (2,): 36, (1,): 54, (0,): 27}, O_grlex) assert sdp_pow(f, 4, 0, O_grlex, ZZ) == \ sdp_from_dict({(4,): 16, (3,): 96, (2,): 216, (1,): 216, (0,): 81}, O_grlex) assert sdp_pow(f, 5, 0, O_grlex, ZZ) == \ sdp_from_dict({(5,): 32, (4,): 240, (3,): 720, (2,): 1080, (1,): 810, (0,): 243}, O_grlex) f = sdp_from_dict({(3,1,0): 1, (1,2,0): -2, (0,0,1): -3, (0,0,0): 1}, O_grlex) g = sdp_from_dict({(6,2,0): 1, (4,3,0): -4, (2,4,0): 4, (3,1,1): -6, (3,1,0): 2, (1,2,1): 12, (1,2,0): -4, (0,0,2): 9, (0,0,1): -6, (0,0,0): 1}, O_grlex) assert sdp_pow(f, 2, 2, O_grlex, ZZ) == g raises(ValueError, "sdp_pow(f, -2, 2, O_grlex, ZZ)") def test_sdp_monic(): pass def test_sdp_content(): pass def test_sdp_primitive(): pass def test_sdp_div(): f = sdp_from_dict({(2,1): 4, (1,1): -2, (1,0): 4, (0,1): -2, (0,0): 8}, O_grlex) assert sdp_div(f, [sdp_from_dict({(0,0): 2}, O_grlex)], 1, O_grlex, ZZ) == \ ([sdp_from_dict({(2,1): 2, (1,1): -1, (1,0): 2, (0,1): -1, (0,0): 4}, O_grlex)], []) assert sdp_div(f, [sdp_from_dict({(0,1): 2}, O_grlex)], 1, O_grlex, ZZ) == \ ([sdp_from_dict({(2,0): 2, (1,0): -1, (0,0): -1}, O_grlex)], sdp_from_dict({(1,0): 4, (0,0): 8}, O_grlex)) f = sdp_from_dict({(1,0): 1, (0,0): -1}, O_grlex) g = sdp_from_dict({(0,1): 1, (0,0): -1}, O_grlex) assert sdp_div(f, [g], 1, O_grlex, ZZ) == ([[]], f) f = sdp_from_dict({(3,): 1, (2,): -12, (0,): -42}, O_grlex) g = sdp_from_dict({(1,): 1, (0,): -3}, O_grlex) q = sdp_from_dict({(2,): 1, (1,): -9, (0,): -27}, O_grlex) r = sdp_from_dict({(0,): -123}, O_grlex) assert sdp_div(f, [g], 0, O_grlex, ZZ) == ([q], r) f = sdp_from_dict({(2,): QQ(1), (1,): QQ(2), (0,): QQ(2)}, O_grlex) g = sdp_from_dict({(0,): QQ(1)}, O_grlex) h = sdp_from_dict({(0,): QQ(2)}, O_grlex) q = sdp_from_dict({(2,): QQ(1,2), (1,): QQ(1), (0,): QQ(1)}, O_grlex) assert sdp_div(f, [g], 0, O_grlex, QQ) == ([f], []) assert sdp_div(f, [h], 0, O_grlex, QQ) == ([q], []) f = sdp_from_dict({(1,2): 1, (0,0): 1}, O_grlex) G = [sdp_from_dict({(1,1): 1, (0,0): 1}, O_grlex), sdp_from_dict({(0,1): 1, (0,0): 1}, O_grlex)] Q = [sdp_from_dict({(0,1): 1}, O_grlex), sdp_from_dict({(0,0): -1}, O_grlex)] r = sdp_from_dict({(0,0): 2}, O_grlex) assert sdp_div(f, G, 1, O_grlex, ZZ) == (Q, r) f = sdp_from_dict({(2,1): 1, (1,2): 1, (0,2): 1}, O_grlex) G = [sdp_from_dict({(1,1): 1, (0,0): -1}, O_grlex), sdp_from_dict({(0,2): 1, (0,0): -1}, O_grlex)] Q = [sdp_from_dict({(1,0): 1, (0,1): 1}, O_grlex), sdp_from_dict({(0,0): 1}, O_grlex)] r = sdp_from_dict({(1,0): 1, (0,1): 1, (0,0): 1}, O_grlex) assert sdp_div(f, G, 1, O_grlex, ZZ) == (Q, r) G = [sdp_from_dict({(0,2): 1, (0,0): -1}, O_grlex), sdp_from_dict({(1,1): 1, (0,0): -1}, O_grlex)] Q = [sdp_from_dict({(1,0): 1, (0,0): 1}, O_grlex), sdp_from_dict({(1,0): 1}, O_grlex)] r = sdp_from_dict({(1,0): 2, (0,0): 1}, O_grlex) assert sdp_div(f, G, 1, O_grlex, ZZ) == (Q, r) def test_sdp_lcm(): pass def test_sdp_gcd(): pass def test_sdp_groebner(): f = sdp_from_dict({(1,2): QQ(2,), (2,0): QQ(1)}, O_lex) g = sdp_from_dict({(0,3): QQ(2), (1,1): QQ(1), (0,0): QQ(-1)}, O_lex) a = sdp_from_dict({(1,0): QQ(1,1)}, O_lex) b = sdp_from_dict({(0,3): QQ(1,1), (0,0): QQ(-1,2)}, O_lex) assert sdp_groebner((f, g), 1, O_lex, QQ) == [a, b] f = sdp_from_dict({(2,1): QQ(2,), (0,2): QQ(1)}, O_lex) g = sdp_from_dict({(3,0): QQ(2), (1,1): QQ(1), (0,0): QQ(-1)}, O_lex) a = sdp_from_dict({(0,1): QQ(1,1)}, O_lex) b = sdp_from_dict({(3,0): QQ(1,1), (0,0): QQ(-1,2)}, O_lex) assert sdp_groebner((f, g), 1, O_lex, QQ) == [b, a] f = sdp_from_dict({(0,0,2): QQ(-1), (1,0,0): QQ(1)}, O_lex) g = sdp_from_dict({(0,0,3): QQ(-1), (0,1,0): QQ(1)}, O_lex) assert sdp_groebner((f, g), 1, O_lex, QQ) == [f, g] f = sdp_from_dict({(3,0): QQ(1), (1,1): QQ(-2)}, O_grlex) g = sdp_from_dict({(2,1): QQ(1), (0,2): QQ(-2), (1,0): QQ(1)}, O_grlex) a = sdp_from_dict({(2,0): QQ(1)}, O_grlex) b = sdp_from_dict({(1,1): QQ(1)}, O_grlex) c = sdp_from_dict({(0,2): QQ(1), (1, 0): QQ(-1,2)}, O_grlex) assert sdp_groebner((f, g), 1, O_grlex, QQ) == [a, b, c] f = sdp_from_dict({(2,0,0): -QQ(1), (0,1,0): QQ(1)}, O_lex) g = sdp_from_dict({(3,0,0): -QQ(1), (0,0,1): QQ(1)}, O_lex) assert sdp_groebner((f, g), 2, O_lex, QQ) == [ sdp_from_dict({(2,0,0): QQ(1), (0,1,0): -QQ(1)}, O_lex), sdp_from_dict({(1,1,0): QQ(1), (0,0,1): -QQ(1)}, O_lex), sdp_from_dict({(1,0,1): QQ(1), (0,2,0): -QQ(1)}, O_lex), sdp_from_dict({(0,3,0): QQ(1), (0,0,2): -QQ(1)}, O_lex), ] f = sdp_from_dict({(2,0,0): -QQ(1), (0,1,0): QQ(1)}, O_grlex) g = sdp_from_dict({(3,0,0): -QQ(1), (0,0,1): QQ(1)}, O_grlex) assert sdp_groebner((f, g), 2, O_grlex, QQ) == [ sdp_from_dict({(0,3,0): QQ(1), (0,0,2): -QQ(1)}, O_grlex), sdp_from_dict({(2,0,0): QQ(1), (0,1,0): -QQ(1)}, O_grlex), sdp_from_dict({(1,1,0): QQ(1), (0,0,1): -QQ(1)}, O_grlex), sdp_from_dict({(1,0,1): QQ(1), (0,2,0): -QQ(1)}, O_grlex), ] f = sdp_from_dict({(2,0,0): -QQ(1), (0,0,1): QQ(1)}, O_lex) g = sdp_from_dict({(3,0,0): -QQ(1), (0,1,0): QQ(1)}, O_lex) assert sdp_groebner((f, g), 2, O_lex, QQ) == [ sdp_from_dict({(2,0,0): QQ(1), (0,0,1): -QQ(1)}, O_lex), sdp_from_dict({(1,1,0): QQ(1), (0,0,2): -QQ(1)}, O_lex), sdp_from_dict({(1,0,1): QQ(1), (0,1,0): -QQ(1)}, O_lex), sdp_from_dict({(0,2,0): QQ(1), (0,0,3): -QQ(1)}, O_lex), ] f = sdp_from_dict({(2,0,0): -QQ(1), (0,0,1): QQ(1)}, O_grlex) g = sdp_from_dict({(3,0,0): -QQ(1), (0,1,0): QQ(1)}, O_grlex) assert sdp_groebner((f, g), 2, O_grlex, QQ) == [ sdp_from_dict({(0,0,3): QQ(1), (0,2,0): -QQ(1)}, O_grlex), sdp_from_dict({(2,0,0): QQ(1), (0,0,1): -QQ(1)}, O_grlex), sdp_from_dict({(1,1,0): QQ(1), (0,0,2): -QQ(1)}, O_grlex), sdp_from_dict({(1,0,1): QQ(1), (0,1,0): -QQ(1)}, O_grlex), ] f = sdp_from_dict({(0,2,0): -QQ(1), (1,0,0): QQ(1)}, O_lex) g = sdp_from_dict({(0,3,0): -QQ(1), (0,0,1): QQ(1)}, O_lex) assert sdp_groebner((f, g), 2, O_lex, QQ) == [ sdp_from_dict({(1,0,0): QQ(1), (0,2,0): -QQ(1)}, O_lex), sdp_from_dict({(0,3,0): QQ(1), (0,0,1): -QQ(1)}, O_lex), ] f = sdp_from_dict({(0,2,0): -QQ(1), (1,0,0): QQ(1)}, O_grlex) g = sdp_from_dict({(0,3,0): -QQ(1), (0,0,1): QQ(1)}, O_grlex) assert sdp_groebner((f, g), 2, O_grlex, QQ) == [ sdp_from_dict({(2,0,0): QQ(1), (0,1,1): -QQ(1)}, O_grlex), sdp_from_dict({(1,1,0): QQ(1), (0,0,1): -QQ(1)}, O_grlex), sdp_from_dict({(0,2,0): QQ(1), (1,0,0): -QQ(1)}, O_grlex), ] f = sdp_from_dict({(0,0,2): -QQ(1), (1,0,0): QQ(1)}, O_lex) g = sdp_from_dict({(0,0,3): -QQ(1), (0,1,0): QQ(1)}, O_lex) assert sdp_groebner((f, g), 2, O_lex, QQ) == [ sdp_from_dict({(1,0,0): QQ(1), (0,0,2): -QQ(1)}, O_lex), sdp_from_dict({(0,1,0): QQ(1), (0,0,3): -QQ(1)}, O_lex), ] f = sdp_from_dict({(0,0,2): -QQ(1), (1,0,0): QQ(1)}, O_grlex) g = sdp_from_dict({(0,0,3): -QQ(1), (0,1,0): QQ(1)}, O_grlex) assert sdp_groebner((f, g), 2, O_grlex, QQ) == [ sdp_from_dict({(2,0,0): QQ(1), (0,1,1): -QQ(1)}, O_grlex), sdp_from_dict({(1,0,1): QQ(1), (0,1,0): -QQ(1)}, O_grlex), sdp_from_dict({(0,0,2): QQ(1), (1,0,0): -QQ(1)}, O_grlex), ] f = sdp_from_dict({(0,2,0): -QQ(1), (0,0,1): QQ(1)}, O_lex) g = sdp_from_dict({(0,3,0): -QQ(1), (1,0,0): QQ(1)}, O_lex) assert sdp_groebner((f, g), 2, O_lex, QQ) == [ sdp_from_dict({(1,0,0): QQ(1), (0,1,1): -QQ(1)}, O_lex), sdp_from_dict({(0,2,0): QQ(1), (0,0,1): -QQ(1)}, O_lex), ] f = sdp_from_dict({(0,2,0): -QQ(1), (0,0,1): QQ(1)}, O_grlex) g = sdp_from_dict({(0,3,0): -QQ(1), (1,0,0): QQ(1)}, O_grlex) assert sdp_groebner((f, g), 2, O_grlex, QQ) == [ sdp_from_dict({(0,0,3): QQ(1), (2,0,0): -QQ(1)}, O_grlex), sdp_from_dict({(1,1,0): QQ(1), (0,0,2): -QQ(1)}, O_grlex), sdp_from_dict({(0,2,0): QQ(1), (0,0,1): -QQ(1)}, O_grlex), sdp_from_dict({(0,1,1): QQ(1), (1,0,0): -QQ(1)}, O_grlex), ] f = sdp_from_dict({(0,0,2): -QQ(1), (0,1,0): QQ(1)}, O_lex) g = sdp_from_dict({(0,0,3): -QQ(1), (1,0,0): QQ(1)}, O_lex) assert sdp_groebner((f, g), 2, O_lex, QQ) == [ sdp_from_dict({(1,0,0): QQ(1), (0,0,3): -QQ(1)}, O_lex), sdp_from_dict({(0,1,0): QQ(1), (0,0,2): -QQ(1)}, O_lex), ] f = sdp_from_dict({(0,0,2): -QQ(1), (0,1,0): QQ(1)}, O_grlex) g = sdp_from_dict({(0,0,3): -QQ(1), (1,0,0): QQ(1)}, O_grlex) assert sdp_groebner((f, g), 2, O_grlex, QQ) == [ sdp_from_dict({(0,3,0): QQ(1), (2,0,0): -QQ(1)}, O_grlex), sdp_from_dict({(1,0,1): QQ(1), (0,2,0): -QQ(1)}, O_grlex), sdp_from_dict({(0,1,1): QQ(1), (1,0,0): -QQ(1)}, O_grlex), sdp_from_dict({(0,0,2): QQ(1), (0,1,0): -QQ(1)}, O_grlex), ] f = sdp_from_dict({(2,2): QQ(4), (1,1): QQ(4), (0,0): QQ(1)}, O_lex) g = sdp_from_dict({(2,0): QQ(1), (0,2): QQ(1), (0,0):-QQ(1)}, O_lex) assert sdp_groebner((f, g), 1, O_lex, QQ) == [ sdp_from_dict({(1,0): QQ(1,1), (0,7): QQ(-4,1), (0,5): QQ(8,1), (0,3): QQ(-7,1), (0,1): QQ(3,1)}, O_lex), sdp_from_dict({(0,8): QQ(1,1), (0,6): QQ(-2,1), (0,4): QQ(3,2), (0,2): QQ(-1,2), (0,0): QQ(1,16)}, O_lex), ] raises(DomainError, "sdp_groebner([], 1, O_lex, ZZ)") def test_benchmark_minpoly(): x, y, z = symbols('x,y,z') I = [x**3+x+1, y**2+y+1, (x+y)*z-(x**2+y)] assert groebner(I, x, y, z, order='lex') == [ -975 + 2067*x + 6878*z - 11061*z**2 + 6062*z**3 - 1065*z**4 + 155*z**5, -308 + 159*y + 1043*z - 1161*z**2 + 523*z**3 - 91*z**4 + 12*z**5, 13 - 46*z + 89*z**2 - 82*z**3 + 41*z**4 - 7*z**5 + z**6, ] assert groebner(I, x, y, z, order='lex', field=True) == [ -S(25)/53 + x + 6878*z/2067 - 3687*z**2/689 + 6062*z**3/2067 - 355*z**4/689 + 155*z**5/2067, -S(308)/159 + y + 1043*z/159 - 387*z**2/53 + 523*z**3/159 - 91*z**4/159 + 4*z**5/53, 13 - 46*z + 89*z**2 - 82*z**3 + 41*z**4 - 7*z**5 + z**6, ] @XFAIL def test_benchmark_coloring(): skip('takes too much time') V = range(1, 12+1) E = [(1,2),(2,3),(1,4),(1,6),(1,12),(2,5),(2,7),(3,8),(3,10), (4,11),(4,9),(5,6),(6,7),(7,8),(8,9),(9,10),(10,11), (11,12),(5,12),(5,9),(6,10),(7,11),(8,12),(3,4)] V = [Symbol('x' + str(i)) for i in V] E = [(V[i-1], V[j-1]) for i, j in E] x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12 = V I3 = [x**3 - 1 for x in V] Ig = [x**2 + x*y + y**2 for x, y in E] I = I3 + Ig assert groebner(I[:-1], V, order='lex') == [ x1 + x11 + x12, x2 - x11, x3 - x12, x4 - x12, x5 + x11 + x12, x6 - x11, x7 - x12, x8 + x11 + x12, x9 - x11, x10 + x11 + x12, x11**2 + x11*x12 + x12**2, x12**3 - 1, ] assert groebner(I, V, order='lex') == [1] def test_benchmark_katsura_3(): x0, x1, x2 = symbols('x:3') I = [x0 + 2*x1 + 2*x2 - 1, x0**2 + 2*x1**2 + 2*x2**2 - x0, 2*x0*x1 + 2*x1*x2 - x1] assert groebner(I, x0, x1, x2, order='lex') == [ -7 + 7*x0 + 8*x2 + 158*x2**2 - 420*x2**3, 7*x1 + 3*x2 - 79*x2**2 + 210*x2**3, x2 + x2**2 - 40*x2**3 + 84*x2**4, ] assert groebner(I, x0, x1, x2, order='grlex') == [ 7*x1 + 3*x2 - 79*x2**2 + 210*x2**3, -x1 + x2 - 3*x2**2 + 5*x1**2, -x1 - 4*x2 + 10*x1*x2 + 12*x2**2, -1 + x0 + 2*x1 + 2*x2, ] def test_benchmark_katsura_4(): x0, x1, x2, x3 = symbols('x:4') I = [x0 + 2*x1 + 2*x2 + 2*x3 - 1, x0**2 + 2*x1**2 + 2*x2**2 + 2*x3**2 - x0, 2*x0*x1 + 2*x1*x2 + 2*x2*x3 - x1, x1**2 + 2*x0*x2 + 2*x1*x3 - x2] assert groebner(I, x0, x1, x2, x3, order='lex') == [ 5913075*x0 - 159690237696*x3**7 + 31246269696*x3**6 + 27439610544*x3**5 - 6475723368*x3**4 - 838935856*x3**3 + 275119624*x3**2 + 4884038*x3 - 5913075, 1971025*x1 - 97197721632*x3**7 + 73975630752*x3**6 - 12121915032*x3**5 - 2760941496*x3**4 + 814792828*x3**3 - 1678512*x3**2 - 9158924*x3, 5913075*x2 + 371438283744*x3**7 - 237550027104*x3**6 + 22645939824*x3**5 + 11520686172*x3**4 - 2024910556*x3**3 - 132524276*x3**2 + 30947828*x3, 128304*x3**8 - 93312*x3**7 + 15552*x3**6 + 3144*x3**5 - 1120*x3**4 + 36*x3**3 + 15*x3**2 - x3, ] assert groebner(I, x0, x1, x2, x3, order='grlex') == [ 393*x1 - 4662*x2**2 + 4462*x2*x3 - 59*x2 + 224532*x3**4 - 91224*x3**3 - 678*x3**2 + 2046*x3, -x1 + 196*x2**3 - 21*x2**2 + 60*x2*x3 - 18*x2 - 168*x3**3 + 83*x3**2 - 9*x3, -6*x1 + 1134*x2**2*x3 - 189*x2**2 - 466*x2*x3 + 32*x2 - 630*x3**3 + 57*x3**2 + 51*x3, 33*x1 + 63*x2**2 + 2268*x2*x3**2 - 188*x2*x3 + 34*x2 + 2520*x3**3 - 849*x3**2 + 3*x3, 7*x1**2 - x1 - 7*x2**2 - 24*x2*x3 + 3*x2 - 15*x3**2 + 5*x3, 14*x1*x2 - x1 + 14*x2**2 + 18*x2*x3 - 4*x2 + 6*x3**2 - 2*x3, 14*x1*x3 - x1 + 7*x2**2 + 32*x2*x3 - 4*x2 + 27*x3**2 - 9*x3, x0 + 2*x1 + 2*x2 + 2*x3 - 1, ] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_domains.py0000644000175000017500000005645012014170666026464 0ustar georgeskgeorgesk"""Tests for classes defining properties of ground domains, e.g. ZZ, QQ, ZZ[x] ... """ from sympy import S, sqrt, sin, oo, nan, Poly, Integer, Rational from sympy.abc import x, y, z from sympy.polys.domains import ( ZZ, QQ, RR, PythonRationalType as Q, ZZ_sympy, QQ_sympy, RR_mpmath, RR_sympy, PolynomialRing, FractionField, EX) from sympy.polys.polyerrors import ( UnificationFailed, GeneratorsNeeded, GeneratorsError, CoercionFailed, DomainError) from sympy.polys.polyclasses import DMP, DMF from sympy.utilities.pytest import raises ALG = QQ.algebraic_field(sqrt(2) + sqrt(3)) def test_Domain__unify(): assert ZZ.unify(ZZ) == ZZ assert QQ.unify(QQ) == QQ assert ZZ.unify(QQ) == QQ assert QQ.unify(ZZ) == QQ assert EX.unify(EX) == EX assert ZZ.unify(EX) == EX assert QQ.unify(EX) == EX assert EX.unify(ZZ) == EX assert EX.unify(QQ) == EX assert ZZ.poly_ring('x').unify(EX) == EX assert ZZ.frac_field('x').unify(EX) == EX assert EX.unify(ZZ.poly_ring('x')) == EX assert EX.unify(ZZ.frac_field('x')) == EX assert ZZ.poly_ring('x','y').unify(EX) == EX assert ZZ.frac_field('x','y').unify(EX) == EX assert EX.unify(ZZ.poly_ring('x','y')) == EX assert EX.unify(ZZ.frac_field('x','y')) == EX assert QQ.poly_ring('x').unify(EX) == EX assert QQ.frac_field('x').unify(EX) == EX assert EX.unify(QQ.poly_ring('x')) == EX assert EX.unify(QQ.frac_field('x')) == EX assert QQ.poly_ring('x','y').unify(EX) == EX assert QQ.frac_field('x','y').unify(EX) == EX assert EX.unify(QQ.poly_ring('x','y')) == EX assert EX.unify(QQ.frac_field('x','y')) == EX assert ZZ.poly_ring('x').unify(ZZ) == ZZ.poly_ring('x') assert ZZ.poly_ring('x').unify(QQ) == QQ.poly_ring('x') assert QQ.poly_ring('x').unify(ZZ) == QQ.poly_ring('x') assert QQ.poly_ring('x').unify(QQ) == QQ.poly_ring('x') assert ZZ.unify(ZZ.poly_ring('x')) == ZZ.poly_ring('x') assert QQ.unify(ZZ.poly_ring('x')) == QQ.poly_ring('x') assert ZZ.unify(QQ.poly_ring('x')) == QQ.poly_ring('x') assert QQ.unify(QQ.poly_ring('x')) == QQ.poly_ring('x') assert ZZ.poly_ring('x','y').unify(ZZ) == ZZ.poly_ring('x','y') assert ZZ.poly_ring('x','y').unify(QQ) == QQ.poly_ring('x','y') assert QQ.poly_ring('x','y').unify(ZZ) == QQ.poly_ring('x','y') assert QQ.poly_ring('x','y').unify(QQ) == QQ.poly_ring('x','y') assert ZZ.unify(ZZ.poly_ring('x','y')) == ZZ.poly_ring('x','y') assert QQ.unify(ZZ.poly_ring('x','y')) == QQ.poly_ring('x','y') assert ZZ.unify(QQ.poly_ring('x','y')) == QQ.poly_ring('x','y') assert QQ.unify(QQ.poly_ring('x','y')) == QQ.poly_ring('x','y') assert ZZ.frac_field('x').unify(ZZ) == ZZ.frac_field('x') assert ZZ.frac_field('x').unify(QQ) == EX # QQ.frac_field('x') assert QQ.frac_field('x').unify(ZZ) == EX # QQ.frac_field('x') assert QQ.frac_field('x').unify(QQ) == QQ.frac_field('x') assert ZZ.unify(ZZ.frac_field('x')) == ZZ.frac_field('x') assert QQ.unify(ZZ.frac_field('x')) == EX # QQ.frac_field('x') assert ZZ.unify(QQ.frac_field('x')) == EX # QQ.frac_field('x') assert QQ.unify(QQ.frac_field('x')) == QQ.frac_field('x') assert ZZ.frac_field('x','y').unify(ZZ) == ZZ.frac_field('x','y') assert ZZ.frac_field('x','y').unify(QQ) == EX # QQ.frac_field('x','y') assert QQ.frac_field('x','y').unify(ZZ) == EX # QQ.frac_field('x','y') assert QQ.frac_field('x','y').unify(QQ) == QQ.frac_field('x','y') assert ZZ.unify(ZZ.frac_field('x','y')) == ZZ.frac_field('x','y') assert QQ.unify(ZZ.frac_field('x','y')) == EX # QQ.frac_field('x','y') assert ZZ.unify(QQ.frac_field('x','y')) == EX # QQ.frac_field('x','y') assert QQ.unify(QQ.frac_field('x','y')) == QQ.frac_field('x','y') assert ZZ.poly_ring('x').unify(ZZ.poly_ring('x')) == ZZ.poly_ring('x') assert ZZ.poly_ring('x').unify(QQ.poly_ring('x')) == QQ.poly_ring('x') assert QQ.poly_ring('x').unify(ZZ.poly_ring('x')) == QQ.poly_ring('x') assert QQ.poly_ring('x').unify(QQ.poly_ring('x')) == QQ.poly_ring('x') assert ZZ.poly_ring('x','y').unify(ZZ.poly_ring('x')) == ZZ.poly_ring('x','y') assert ZZ.poly_ring('x','y').unify(QQ.poly_ring('x')) == QQ.poly_ring('x','y') assert QQ.poly_ring('x','y').unify(ZZ.poly_ring('x')) == QQ.poly_ring('x','y') assert QQ.poly_ring('x','y').unify(QQ.poly_ring('x')) == QQ.poly_ring('x','y') assert ZZ.poly_ring('x').unify(ZZ.poly_ring('x','y')) == ZZ.poly_ring('x','y') assert ZZ.poly_ring('x').unify(QQ.poly_ring('x','y')) == QQ.poly_ring('x','y') assert QQ.poly_ring('x').unify(ZZ.poly_ring('x','y')) == QQ.poly_ring('x','y') assert QQ.poly_ring('x').unify(QQ.poly_ring('x','y')) == QQ.poly_ring('x','y') assert ZZ.poly_ring('x','y').unify(ZZ.poly_ring('x','z')) == ZZ.poly_ring('x','y','z') assert ZZ.poly_ring('x','y').unify(QQ.poly_ring('x','z')) == QQ.poly_ring('x','y','z') assert QQ.poly_ring('x','y').unify(ZZ.poly_ring('x','z')) == QQ.poly_ring('x','y','z') assert QQ.poly_ring('x','y').unify(QQ.poly_ring('x','z')) == QQ.poly_ring('x','y','z') assert ZZ.frac_field('x').unify(ZZ.frac_field('x')) == ZZ.frac_field('x') assert ZZ.frac_field('x').unify(QQ.frac_field('x')) == QQ.frac_field('x') assert QQ.frac_field('x').unify(ZZ.frac_field('x')) == QQ.frac_field('x') assert QQ.frac_field('x').unify(QQ.frac_field('x')) == QQ.frac_field('x') assert ZZ.frac_field('x','y').unify(ZZ.frac_field('x')) == ZZ.frac_field('x','y') assert ZZ.frac_field('x','y').unify(QQ.frac_field('x')) == QQ.frac_field('x','y') assert QQ.frac_field('x','y').unify(ZZ.frac_field('x')) == QQ.frac_field('x','y') assert QQ.frac_field('x','y').unify(QQ.frac_field('x')) == QQ.frac_field('x','y') assert ZZ.frac_field('x').unify(ZZ.frac_field('x','y')) == ZZ.frac_field('x','y') assert ZZ.frac_field('x').unify(QQ.frac_field('x','y')) == QQ.frac_field('x','y') assert QQ.frac_field('x').unify(ZZ.frac_field('x','y')) == QQ.frac_field('x','y') assert QQ.frac_field('x').unify(QQ.frac_field('x','y')) == QQ.frac_field('x','y') assert ZZ.frac_field('x','y').unify(ZZ.frac_field('x','z')) == ZZ.frac_field('x','y','z') assert ZZ.frac_field('x','y').unify(QQ.frac_field('x','z')) == QQ.frac_field('x','y','z') assert QQ.frac_field('x','y').unify(ZZ.frac_field('x','z')) == QQ.frac_field('x','y','z') assert QQ.frac_field('x','y').unify(QQ.frac_field('x','z')) == QQ.frac_field('x','y','z') assert ZZ.poly_ring('x').unify(ZZ.frac_field('x')) == ZZ.frac_field('x') assert ZZ.poly_ring('x').unify(QQ.frac_field('x')) == EX # QQ.frac_field('x') assert QQ.poly_ring('x').unify(ZZ.frac_field('x')) == EX # QQ.frac_field('x') assert QQ.poly_ring('x').unify(QQ.frac_field('x')) == QQ.frac_field('x') assert ZZ.poly_ring('x','y').unify(ZZ.frac_field('x')) == ZZ.frac_field('x','y') assert ZZ.poly_ring('x','y').unify(QQ.frac_field('x')) == EX # QQ.frac_field('x','y') assert QQ.poly_ring('x','y').unify(ZZ.frac_field('x')) == EX # QQ.frac_field('x','y') assert QQ.poly_ring('x','y').unify(QQ.frac_field('x')) == QQ.frac_field('x','y') assert ZZ.poly_ring('x').unify(ZZ.frac_field('x','y')) == ZZ.frac_field('x','y') assert ZZ.poly_ring('x').unify(QQ.frac_field('x','y')) == EX # QQ.frac_field('x','y') assert QQ.poly_ring('x').unify(ZZ.frac_field('x','y')) == EX # QQ.frac_field('x','y') assert QQ.poly_ring('x').unify(QQ.frac_field('x','y')) == QQ.frac_field('x','y') assert ZZ.poly_ring('x','y').unify(ZZ.frac_field('x','z')) == ZZ.frac_field('x','y','z') assert ZZ.poly_ring('x','y').unify(QQ.frac_field('x','z')) == EX # QQ.frac_field('x','y','z') assert QQ.poly_ring('x','y').unify(ZZ.frac_field('x','z')) == EX # QQ.frac_field('x','y','z') assert QQ.poly_ring('x','y').unify(QQ.frac_field('x','z')) == QQ.frac_field('x','y','z') assert ZZ.frac_field('x').unify(ZZ.poly_ring('x')) == ZZ.frac_field('x') assert ZZ.frac_field('x').unify(QQ.poly_ring('x')) == EX # QQ.frac_field('x') assert QQ.frac_field('x').unify(ZZ.poly_ring('x')) == EX # QQ.frac_field('x') assert QQ.frac_field('x').unify(QQ.poly_ring('x')) == QQ.frac_field('x') assert ZZ.frac_field('x','y').unify(ZZ.poly_ring('x')) == ZZ.frac_field('x','y') assert ZZ.frac_field('x','y').unify(QQ.poly_ring('x')) == EX # QQ.frac_field('x','y') assert QQ.frac_field('x','y').unify(ZZ.poly_ring('x')) == EX # QQ.frac_field('x','y') assert QQ.frac_field('x','y').unify(QQ.poly_ring('x')) == QQ.frac_field('x','y') assert ZZ.frac_field('x').unify(ZZ.poly_ring('x','y')) == ZZ.frac_field('x','y') assert ZZ.frac_field('x').unify(QQ.poly_ring('x','y')) == EX # QQ.frac_field('x','y') assert QQ.frac_field('x').unify(ZZ.poly_ring('x','y')) == EX # QQ.frac_field('x','y') assert QQ.frac_field('x').unify(QQ.poly_ring('x','y')) == QQ.frac_field('x','y') assert ZZ.frac_field('x','y').unify(ZZ.poly_ring('x','z')) == ZZ.frac_field('x','y','z') assert ZZ.frac_field('x','y').unify(QQ.poly_ring('x','z')) == EX # QQ.frac_field('x','y','z') assert QQ.frac_field('x','y').unify(ZZ.poly_ring('x','z')) == EX # QQ.frac_field('x','y','z') assert QQ.frac_field('x','y').unify(QQ.poly_ring('x','z')) == QQ.frac_field('x','y','z') alg = QQ.algebraic_field(sqrt(5)) assert alg.unify(alg['x','y']) == alg['x','y'] assert alg['x','y'].unify(alg) == alg['x','y'] assert alg.unify(alg.frac_field('x','y')) == alg.frac_field('x','y') assert alg.frac_field('x','y').unify(alg) == alg.frac_field('x','y') ext = QQ.algebraic_field(sqrt(7)) raises(NotImplementedError, "alg.unify(ext)") raises(UnificationFailed, "ZZ.poly_ring('x','y').unify(ZZ, gens=('y', 'z'))") raises(UnificationFailed, "ZZ.unify(ZZ.poly_ring('x','y'), gens=('y', 'z'))") def test_Domain__contains__(): assert (0 in EX) == True assert (0 in ZZ) == True assert (0 in QQ) == True assert (0 in RR) == True assert (0 in ALG) == True assert (0 in ZZ[x,y]) == True assert (0 in QQ[x,y]) == True assert (0 in RR[x,y]) == True assert (-7 in EX) == True assert (-7 in ZZ) == True assert (-7 in QQ) == True assert (-7 in RR) == True assert (-7 in ALG) == True assert (-7 in ZZ[x,y]) == True assert (-7 in QQ[x,y]) == True assert (-7 in RR[x,y]) == True assert (17 in EX) == True assert (17 in ZZ) == True assert (17 in QQ) == True assert (17 in RR) == True assert (17 in ALG) == True assert (17 in ZZ[x,y]) == True assert (17 in QQ[x,y]) == True assert (17 in RR[x,y]) == True assert (-S(1)/7 in EX) == True assert (-S(1)/7 in ZZ) == False assert (-S(1)/7 in QQ) == True assert (-S(1)/7 in RR) == True assert (-S(1)/7 in ALG) == True assert (-S(1)/7 in ZZ[x,y]) == False assert (-S(1)/7 in QQ[x,y]) == True assert (-S(1)/7 in RR[x,y]) == True assert (S(3)/5 in EX) == True assert (S(3)/5 in ZZ) == False assert (S(3)/5 in QQ) == True assert (S(3)/5 in RR) == True assert (S(3)/5 in ALG) == True assert (S(3)/5 in ZZ[x,y]) == False assert (S(3)/5 in QQ[x,y]) == True assert (S(3)/5 in RR[x,y]) == True assert (3.0 in EX) == True assert (3.0 in ZZ) == True assert (3.0 in QQ) == True assert (3.0 in RR) == True assert (3.0 in ALG) == True assert (3.0 in ZZ[x,y]) == True assert (3.0 in QQ[x,y]) == True assert (3.0 in RR[x,y]) == True assert (3.14 in EX) == True assert (3.14 in ZZ) == False assert (3.14 in QQ) == True assert (3.14 in RR) == True assert (3.14 in ALG) == True assert (3.14 in ZZ[x,y]) == False assert (3.14 in QQ[x,y]) == True assert (3.14 in RR[x,y]) == True assert (oo in EX) == True assert (oo in ZZ) == False assert (oo in QQ) == False assert (oo in RR) == False assert (oo in ALG) == False assert (oo in ZZ[x,y]) == False assert (oo in QQ[x,y]) == False assert (oo in RR[x,y]) == False assert (-oo in EX) == True assert (-oo in ZZ) == False assert (-oo in QQ) == False assert (-oo in RR) == False assert (-oo in ALG) == False assert (-oo in ZZ[x,y]) == False assert (-oo in QQ[x,y]) == False assert (-oo in RR[x,y]) == False assert (sqrt(7) in EX) == True assert (sqrt(7) in ZZ) == False assert (sqrt(7) in QQ) == False assert (sqrt(7) in RR) == True assert (sqrt(7) in ALG) == False assert (sqrt(7) in ZZ[x,y]) == False assert (sqrt(7) in QQ[x,y]) == False assert (sqrt(7) in RR[x,y]) == True assert (2*sqrt(3)+1 in EX) == True assert (2*sqrt(3)+1 in ZZ) == False assert (2*sqrt(3)+1 in QQ) == False assert (2*sqrt(3)+1 in RR) == True assert (2*sqrt(3)+1 in ALG) == True assert (2*sqrt(3)+1 in ZZ[x,y]) == False assert (2*sqrt(3)+1 in QQ[x,y]) == False assert (2*sqrt(3)+1 in RR[x,y]) == True assert (sin(1) in EX) == True assert (sin(1) in ZZ) == False assert (sin(1) in QQ) == False assert (sin(1) in RR) == True assert (sin(1) in ALG) == False assert (sin(1) in ZZ[x,y]) == False assert (sin(1) in QQ[x,y]) == False assert (sin(1) in RR[x,y]) == True assert (x**2 + 1 in EX) == True assert (x**2 + 1 in ZZ) == False assert (x**2 + 1 in QQ) == False assert (x**2 + 1 in RR) == False assert (x**2 + 1 in ALG) == False assert (x**2 + 1 in ZZ[x]) == True assert (x**2 + 1 in QQ[x]) == True assert (x**2 + 1 in RR[x]) == True assert (x**2 + 1 in ZZ[x,y]) == True assert (x**2 + 1 in QQ[x,y]) == True assert (x**2 + 1 in RR[x,y]) == True assert (x**2 + y**2 in EX) == True assert (x**2 + y**2 in ZZ) == False assert (x**2 + y**2 in QQ) == False assert (x**2 + y**2 in RR) == False assert (x**2 + y**2 in ALG) == False assert (x**2 + y**2 in ZZ[x]) == False assert (x**2 + y**2 in QQ[x]) == False assert (x**2 + y**2 in RR[x]) == False assert (x**2 + y**2 in ZZ[x,y]) == True assert (x**2 + y**2 in QQ[x,y]) == True assert (x**2 + y**2 in RR[x,y]) == True assert (S(3)/2*x/(y + 1) - z in QQ[x, y, z]) == False def test_Domain_get_ring(): assert ZZ.has_assoc_Ring == True assert QQ.has_assoc_Ring == True assert ZZ[x].has_assoc_Ring == True assert QQ[x].has_assoc_Ring == True assert ZZ[x,y].has_assoc_Ring == True assert QQ[x,y].has_assoc_Ring == True assert ZZ.frac_field(x).has_assoc_Ring == True assert QQ.frac_field(x).has_assoc_Ring == True assert ZZ.frac_field(x,y).has_assoc_Ring == True assert QQ.frac_field(x,y).has_assoc_Ring == True assert EX.has_assoc_Ring == False assert RR.has_assoc_Ring == False assert ALG.has_assoc_Ring == False assert ZZ.get_ring() == ZZ assert QQ.get_ring() == ZZ assert ZZ[x].get_ring() == ZZ[x] assert QQ[x].get_ring() == QQ[x] assert ZZ[x,y].get_ring() == ZZ[x,y] assert QQ[x,y].get_ring() == QQ[x,y] assert ZZ.frac_field(x).get_ring() == ZZ[x] assert QQ.frac_field(x).get_ring() == QQ[x] assert ZZ.frac_field(x,y).get_ring() == ZZ[x,y] assert QQ.frac_field(x,y).get_ring() == QQ[x,y] raises(DomainError, "EX.get_ring()") raises(DomainError, "RR.get_ring()") raises(DomainError, "ALG.get_ring()") def test_Domain_get_field(): assert EX.has_assoc_Field == True assert ZZ.has_assoc_Field == True assert QQ.has_assoc_Field == True assert RR.has_assoc_Field == False assert ALG.has_assoc_Field == True assert ZZ[x].has_assoc_Field == True assert QQ[x].has_assoc_Field == True assert ZZ[x,y].has_assoc_Field == True assert QQ[x,y].has_assoc_Field == True assert EX.get_field() == EX assert ZZ.get_field() == QQ assert QQ.get_field() == QQ raises(DomainError, "RR.get_field()") assert ALG.get_field() == ALG assert ZZ[x].get_field() == ZZ.frac_field(x) assert QQ[x].get_field() == QQ.frac_field(x) assert ZZ[x,y].get_field() == ZZ.frac_field(x,y) assert QQ[x,y].get_field() == QQ.frac_field(x,y) def test_Domain_get_exact(): assert EX.get_exact() == EX assert ZZ.get_exact() == ZZ assert QQ.get_exact() == QQ assert RR.get_exact() == QQ assert ALG.get_exact() == ALG assert ZZ[x].get_exact() == ZZ[x] assert QQ[x].get_exact() == QQ[x] assert ZZ[x,y].get_exact() == ZZ[x,y] assert QQ[x,y].get_exact() == QQ[x,y] assert ZZ.frac_field(x).get_exact() == ZZ.frac_field(x) assert QQ.frac_field(x).get_exact() == QQ.frac_field(x) assert ZZ.frac_field(x,y).get_exact() == ZZ.frac_field(x,y) assert QQ.frac_field(x,y).get_exact() == QQ.frac_field(x,y) def test_Domain_convert(): assert QQ.convert(10e-52) != QQ(0) assert ZZ.convert(DMP([[ZZ(1)]], ZZ)) == ZZ(1) def test_PolynomialRing__init(): raises(GeneratorsNeeded, "ZZ.poly_ring()") def test_PolynomialRing_from_FractionField(): x = DMF(([1, 0, 1], [1, 1]), ZZ) y = DMF(([1, 0, 1], [1]), ZZ) assert ZZ['x'].from_FractionField(x, ZZ['x']) is None assert ZZ['x'].from_FractionField(y, ZZ['x']) == DMP([ZZ(1), ZZ(0), ZZ(1)], ZZ) def test_FractionField__init(): raises(GeneratorsNeeded, "ZZ.frac_field()") def test_inject(): assert ZZ.inject(x, y, z) == ZZ[x, y, z] assert ZZ[x].inject(y, z) == ZZ[x, y, z] assert ZZ.frac_field(x).inject(y, z) == ZZ.frac_field(x, y, z) raises(GeneratorsError, "ZZ[x].inject(x)") def test_Domain_map(): seq = ZZ.map([1, 2, 3, 4]) assert all([ ZZ.of_type(elt) for elt in seq ]) seq = ZZ.map([[1, 2, 3, 4]]) assert all([ ZZ.of_type(elt) for elt in seq[0] ]) and len(seq) == 1 def test_Domain___eq__(): assert (ZZ[x,y] == ZZ[x,y]) == True assert (QQ[x,y] == QQ[x,y]) == True assert (ZZ[x,y] == QQ[x,y]) == False assert (QQ[x,y] == ZZ[x,y]) == False assert (ZZ.frac_field(x,y) == ZZ.frac_field(x,y)) == True assert (QQ.frac_field(x,y) == QQ.frac_field(x,y)) == True assert (ZZ.frac_field(x,y) == QQ.frac_field(x,y)) == False assert (QQ.frac_field(x,y) == ZZ.frac_field(x,y)) == False def test_Domain__algebraic_field(): alg = ZZ.algebraic_field(sqrt(2)) assert alg.ext.minpoly == Poly(x**2 - 2) assert alg.dom == QQ alg = QQ.algebraic_field(sqrt(2)) assert alg.ext.minpoly == Poly(x**2 - 2) assert alg.dom == QQ alg = alg.algebraic_field(sqrt(3)) assert alg.ext.minpoly == Poly(x**4 - 10*x**2 + 1) assert alg.dom == QQ def test_PolynomialRing__from_FractionField(): f = DMF(([1, 0, 1], [1, 1]), ZZ) g = DMF(([1, 0, 1], [1]), ZZ) assert ZZ[x].from_FractionField(f, ZZ[x]) is None assert ZZ[x].from_FractionField(g, ZZ[x]) == DMP([ZZ(1), ZZ(0), ZZ(1)], ZZ) def test_PythonRationalType__init__(): assert Q(0).p == 0 assert Q(0).q == 1 assert Q(0, 1).p == 0 assert Q(0, 1).q == 1 assert Q(0,-1).p == 0 assert Q(0,-1).q == 1 assert Q(1).p == 1 assert Q(1).q == 1 assert Q(1, 1).p == 1 assert Q(1, 1).q == 1 assert Q(-1,-1).p == 1 assert Q(-1,-1).q == 1 assert Q(-1).p == -1 assert Q(-1).q == 1 assert Q(-1, 1).p == -1 assert Q(-1, 1).q == 1 assert Q( 1,-1).p == -1 assert Q( 1,-1).q == 1 assert Q(1, 2).p == 1 assert Q(1, 2).q == 2 assert Q(3, 4).p == 3 assert Q(3, 4).q == 4 assert Q(2, 2).p == 1 assert Q(2, 2).q == 1 assert Q(2, 4).p == 1 assert Q(2, 4).q == 2 def test_PythonRationalType__hash__(): assert hash(Q(0)) == hash(0) assert hash(Q(1)) == hash(1) assert hash(Q(117)) == hash(117) def test_PythonRationalType__int__(): assert int(Q(-1, 4)) == 0 assert int(Q( 1, 4)) == 0 assert int(Q(-5, 4)) == -1 assert int(Q( 5, 4)) == 1 def test_PythonRationalType__float__(): assert float(Q(-1, 2)) == -0.5 assert float(Q( 1, 2)) == 0.5 def test_PythonRationalType__abs__(): assert abs(Q(-1, 2)) == Q(1, 2) assert abs(Q( 1, 2)) == Q(1, 2) def test_PythonRationalType__pos__(): assert +Q(-1, 2) == Q(-1, 2) assert +Q( 1, 2) == Q( 1, 2) def test_PythonRationalType__neg__(): assert -Q(-1, 2) == Q( 1, 2) assert -Q( 1, 2) == Q(-1, 2) def test_PythonRationalType__add__(): assert Q(-1, 2) + Q( 1, 2) == Q(0) assert Q( 1, 2) + Q(-1, 2) == Q(0) assert Q(1, 2) + Q(1, 2) == Q(1) assert Q(1, 2) + Q(3, 2) == Q(2) assert Q(3, 2) + Q(1, 2) == Q(2) assert Q(3, 2) + Q(3, 2) == Q(3) assert 1 + Q(1, 2) == Q(3, 2) assert Q(1, 2) + 1 == Q(3, 2) def test_PythonRationalType__sub__(): assert Q(-1, 2) - Q( 1, 2) == Q(-1) assert Q( 1, 2) - Q(-1, 2) == Q( 1) assert Q(1, 2) - Q(1, 2) == Q( 0) assert Q(1, 2) - Q(3, 2) == Q(-1) assert Q(3, 2) - Q(1, 2) == Q( 1) assert Q(3, 2) - Q(3, 2) == Q( 0) assert 1 - Q(1, 2) == Q( 1, 2) assert Q(1, 2) - 1 == Q(-1, 2) def test_PythonRationalType__mul__(): assert Q(-1, 2) * Q( 1, 2) == Q(-1, 4) assert Q( 1, 2) * Q(-1, 2) == Q(-1, 4) assert Q(1, 2) * Q(1, 2) == Q(1, 4) assert Q(1, 2) * Q(3, 2) == Q(3, 4) assert Q(3, 2) * Q(1, 2) == Q(3, 4) assert Q(3, 2) * Q(3, 2) == Q(9, 4) assert 2 * Q(1, 2) == Q(1) assert Q(1, 2) * 2 == Q(1) def test_PythonRationalType__div__(): assert Q(-1, 2) / Q( 1, 2) == Q(-1) assert Q( 1, 2) / Q(-1, 2) == Q(-1) assert Q(1, 2) / Q(1, 2) == Q(1) assert Q(1, 2) / Q(3, 2) == Q(1, 3) assert Q(3, 2) / Q(1, 2) == Q(3) assert Q(3, 2) / Q(3, 2) == Q(1) assert 2 / Q(1, 2) == Q(4) assert Q(1, 2) / 2 == Q(1, 4) raises(ZeroDivisionError, "Q(1, 2) / Q(0)") raises(ZeroDivisionError, "Q(1, 2) / 0") def test_PythonRationalType__pow__(): assert Q(1)**10 == Q(1) assert Q(2)**10 == Q(1024) assert Q(1)**(-10) == Q(1) assert Q(2)**(-10) == Q(1, 1024) def test_PythonRationalType__eq__(): assert (Q(1, 2) == Q(1, 2)) is True assert (Q(1, 2) != Q(1, 2)) is False assert (Q(1, 2) == Q(1, 3)) is False assert (Q(1, 2) != Q(1, 3)) is True def test_PythonRationalType__lt_le_gt_ge__(): assert (Q(1, 2) < Q(1, 4)) is False assert (Q(1, 2) <= Q(1, 4)) is False assert (Q(1, 2) > Q(1, 4)) is True assert (Q(1, 2) >= Q(1, 4)) is True assert (Q(1, 4) < Q(1, 2)) is True assert (Q(1, 4) <= Q(1, 2)) is True assert (Q(1, 4) > Q(1, 2)) is False assert (Q(1, 4) >= Q(1, 2)) is False def test_sympy_of_type(): assert ZZ_sympy().of_type(Integer(1)) assert ZZ_sympy().of_type(Integer(0)) assert ZZ_sympy().of_type(Integer(-1)) assert ZZ_sympy().of_type(Integer(2)) assert not ZZ_sympy().of_type(Rational(1, 2)) assert QQ_sympy().of_type(Rational(1)) assert QQ_sympy().of_type(Rational(-1)) assert QQ_sympy().of_type(Rational(0)) assert QQ_sympy().of_type(Rational(2)) assert QQ_sympy().of_type(Rational(1, 2)) assert QQ_sympy().of_type(Rational(3, 2)) def test___eq__(): assert not QQ['x'] == ZZ['x'] assert not QQ.frac_field(x) == ZZ.frac_field(x) def test_RealDomain_from_sympy(): RR = RR_mpmath() assert RR.convert(S(0)) == RR.dtype(0) assert RR.convert(S(0.0)) == RR.dtype(0.0) assert RR.convert(S(1)) == RR.dtype(1) assert RR.convert(S(1.0)) == RR.dtype(1.0) assert RR.convert(sin(1)) == RR.dtype(sin(1).evalf()) raises(CoercionFailed, "RR.convert(x)") raises(CoercionFailed, "RR.convert(oo)") raises(CoercionFailed, "RR.convert(-oo)") RR = RR_sympy() assert RR.convert(S(0)) == RR.dtype(0) assert RR.convert(S(0.0)) == RR.dtype(0.0) assert RR.convert(S(1)) == RR.dtype(1) assert RR.convert(S(1.0)) == RR.dtype(1.0) assert RR.convert(sin(1)) == RR.dtype(sin(1).evalf()) raises(CoercionFailed, "RR.convert(x)") raises(CoercionFailed, "RR.convert(oo)") raises(CoercionFailed, "RR.convert(-oo)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_rationaltools.py0000644000175000017500000000414112014170666027712 0ustar georgeskgeorgesk"""Tests for tools for manipulation of rational expressions. """ from sympy.polys.rationaltools import together from sympy import S, symbols, Rational, sin, exp, Eq, Integral, Mul from sympy.abc import x, y, z A, B = symbols('A,B', commutative=False) def test_together(): assert together(0) == 0 assert together(1) == 1 assert together(x*y*z) == x*y*z assert together(x + y) == x + y assert together(1/x) == 1/x assert together(1/x + 1) == (x + 1)/x assert together(1/x + 3) == (3*x + 1)/x assert together(1/x + x) == (x**2 + 1)/x assert together(1/x + Rational(1, 2)) == (x + 2)/(2*x) assert together(Rational(1, 2) + x/2) == Mul(S.Half, x + 1, evaluate=False) assert together(1/x + 2/y) == (2*x + y)/(y*x) assert together(1/(1 + 1/x)) == x/(1 + x) assert together(x/(1 + 1/x)) == x**2/(1 + x) assert together(1/x + 1/y + 1/z) == (x*y + x*z + y*z)/(x*y*z) assert together(1/(1 + x + 1/y + 1/z)) == y*z/(y + z + y*z + x*y*z) assert together(1/(x*y) + 1/(x*y)**2) == y**(-2)*x**(-2)*(1 + x*y) assert together(1/(x*y) + 1/(x*y)**4) == y**(-4)*x**(-4)*(1 + x**3*y**3) assert together(1/(x**7*y) + 1/(x*y)**4) == y**(-4)*x**(-7)*(x**3 + y**3) assert together(5/(2 + 6/(3 + 7/(4 + 8/(5 + 9/x))))) == \ (S(5)/2)*((171 + 119*x)/(279 + 203*x)) assert together(1 + 1/(x + 1)**2) == (1 + (x + 1)**2)/(x + 1)**2 assert together(1 + 1/(x*(1 + x))) == (1 + x*(1 + x))/(x*(1 + x)) assert together(1/(x*(x + 1)) + 1/(x*(x + 2))) == (3 + 2*x)/(x*(1 + x)*(2 + x)) assert together(1 + 1/(2*x + 2)**2) == (4*(x + 1)**2 + 1)/(4*(x + 1)**2) assert together(sin(1/x + 1/y)) == sin(1/x + 1/y) assert together(sin(1/x + 1/y), deep=True) == sin((x + y)/(x*y)) assert together(1/exp(x) + 1/(x*exp(x))) == (1+x)/(x*exp(x)) assert together(1/exp(2*x) + 1/(x*exp(3*x))) == (1+exp(x)*x)/(x*exp(3*x)) assert together(Integral(1/x + 1/y, x)) == Integral((x + y)/(x*y), x) assert together(Eq(1/x + 1/y, 1 + 1/z)) == Eq((x + y)/(x*y), (z + 1)/z) assert together(1/(A*B) + 1/(B*A)) in [(A*B + B*A)/(B*A**2*B), (A*B + B*A)/(A*B**2*A)] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_densearith.py0000644000175000017500000005643212014170666027160 0ustar georgeskgeorgesk"""Tests for dense recursive polynomials' arithmetics. """ from sympy.polys.densebasic import ( dup_normal, dmp_normal, ) from sympy.polys.densearith import ( dup_add_term, dmp_add_term, dup_sub_term, dmp_sub_term, dup_mul_term, dmp_mul_term, dup_add_ground, dmp_add_ground, dup_sub_ground, dmp_sub_ground, dup_mul_ground, dmp_mul_ground, dup_quo_ground, dmp_quo_ground, dup_exquo_ground, dmp_exquo_ground, dup_lshift, dup_rshift, dup_abs, dmp_abs, dup_neg, dmp_neg, dup_add, dmp_add, dup_sub, dmp_sub, dup_mul, dmp_mul, dup_sqr, dmp_sqr, dup_pow, dmp_pow, dup_add_mul, dmp_add_mul, dup_sub_mul, dmp_sub_mul, dup_pdiv, dup_prem, dup_pquo, dup_pexquo, dmp_pdiv, dmp_prem, dmp_pquo, dmp_pexquo, dup_rr_div, dmp_rr_div, dup_ff_div, dmp_ff_div, dup_div, dup_rem, dup_quo, dup_exquo, dmp_div, dmp_rem, dmp_quo, dmp_exquo, dup_max_norm, dmp_max_norm, dup_l1_norm, dmp_l1_norm, dup_expand, dmp_expand, ) from sympy.polys.polyerrors import ( ExactQuotientFailed, ) from sympy.polys.specialpolys import f_0 from sympy.polys.domains import FF, ZZ, QQ from sympy.utilities.pytest import raises F_0 = dmp_mul_ground(dmp_normal(f_0, 2, QQ), QQ(1,7), 2, QQ) def test_dup_add_term(): f = dup_normal([], ZZ) assert dup_add_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1], ZZ) assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 0], ZZ) assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 0, 0], ZZ) f = dup_normal([1,1,1], ZZ) assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1, 1, 2], ZZ) assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 2, 1], ZZ) assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([2, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 3, ZZ) == dup_normal([1, 1, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 4, ZZ) == dup_normal([1, 0, 1, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 5, ZZ) == dup_normal([1, 0, 0, 1, 1, 1], ZZ) assert dup_add_term(f, ZZ(1), 6, ZZ) == dup_normal([1, 0, 0, 0, 1, 1, 1], ZZ) assert dup_add_term(f,ZZ(-1), 2, ZZ) == dup_normal([1, 1], ZZ) def test_dmp_add_term(): assert dmp_add_term([ZZ(1),ZZ(1),ZZ(1)], ZZ(1), 2, 0, ZZ) == \ dup_add_term([ZZ(1),ZZ(1),ZZ(1)], ZZ(1), 2, ZZ) assert dmp_add_term(f_0, [[]], 3, 2, ZZ) == f_0 assert dmp_add_term(F_0, [[]], 3, 2, QQ) == F_0 def test_dup_sub_term(): f = dup_normal([], ZZ) assert dup_sub_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) assert dup_sub_term(f, ZZ(1), 0, ZZ) == dup_normal([-1], ZZ) assert dup_sub_term(f, ZZ(1), 1, ZZ) == dup_normal([-1, 0], ZZ) assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([-1, 0, 0], ZZ) f = dup_normal([1,1,1], ZZ) assert dup_sub_term(f, ZZ(2), 0, ZZ) == dup_normal([ 1, 1,-1], ZZ) assert dup_sub_term(f, ZZ(2), 1, ZZ) == dup_normal([ 1,-1, 1], ZZ) assert dup_sub_term(f, ZZ(2), 2, ZZ) == dup_normal([-1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 3, ZZ) == dup_normal([-1, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 4, ZZ) == dup_normal([-1, 0, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 5, ZZ) == dup_normal([-1, 0, 0, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 6, ZZ) == dup_normal([-1, 0, 0, 0, 1, 1, 1], ZZ) assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 1], ZZ) def test_dmp_sub_term(): assert dmp_sub_term([ZZ(1),ZZ(1),ZZ(1)], ZZ(1), 2, 0, ZZ) == \ dup_sub_term([ZZ(1),ZZ(1),ZZ(1)], ZZ(1), 2, ZZ) assert dmp_sub_term(f_0, [[]], 3, 2, ZZ) == f_0 assert dmp_sub_term(F_0, [[]], 3, 2, QQ) == F_0 def test_dup_mul_term(): f = dup_normal([], ZZ) assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([], ZZ) f = dup_normal([1,1], ZZ) assert dup_mul_term(f, ZZ(0), 3, ZZ) == dup_normal([], ZZ) f = dup_normal([1,2,3], ZZ) assert dup_mul_term(f, ZZ(2), 0, ZZ) == dup_normal([2,4,6], ZZ) assert dup_mul_term(f, ZZ(2), 1, ZZ) == dup_normal([2,4,6,0], ZZ) assert dup_mul_term(f, ZZ(2), 2, ZZ) == dup_normal([2,4,6,0,0], ZZ) assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([2,4,6,0,0,0], ZZ) def test_dmp_mul_term(): assert dmp_mul_term([ZZ(1),ZZ(2),ZZ(3)], ZZ(2), 1, 0, ZZ) == \ dup_mul_term([ZZ(1),ZZ(2),ZZ(3)], ZZ(2), 1, ZZ) assert dmp_mul_term([[]], [ZZ(2)], 3, 1, ZZ) == [[]] assert dmp_mul_term([[ZZ(1)]], [], 3, 1, ZZ) == [[]] assert dmp_mul_term([[ZZ(1),ZZ(2)], [ZZ(3)]], [ZZ(2)], 2, 1, ZZ) == \ [[ZZ(2),ZZ(4)], [ZZ(6)], [], []] assert dmp_mul_term([[]], [QQ(2,3)], 3, 1, QQ) == [[]] assert dmp_mul_term([[QQ(1,2)]], [], 3, 1, QQ) == [[]] assert dmp_mul_term([[QQ(1,5),QQ(2,5)], [QQ(3,5)]], [QQ(2,3)], 2, 1, QQ) == \ [[QQ(2,15),QQ(4,15)], [QQ(6,15)], [], []] def dup_add_ground(): f = ZZ.map([1, 2, 3, 4]) g = ZZ.map([1, 2, 3, 8]) assert dup_add_ground(f, ZZ(4), ZZ) == g def dmp_add_ground(): f = ZZ.map([[1], [2], [3], [4]]) g = ZZ.map([[1], [2], [3], [8]]) assert dmp_add_ground(f, ZZ(4), 1, ZZ) == g def dup_sub_ground(): f = ZZ.map([1, 2, 3, 4]) g = ZZ.map([1, 2, 3, 0]) assert dup_sub_ground(f, ZZ(4), ZZ) == g def dmp_sub_ground(): f = ZZ.map([[1], [2], [3], [4]]) g = ZZ.map([[1], [2], [3], []]) assert dmp_sub_ground(f, ZZ(4), 1, ZZ) == g def test_dup_mul_ground(): f = dup_normal([], ZZ) assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([], ZZ) f = dup_normal([1,2,3], ZZ) assert dup_mul_ground(f, ZZ(0), ZZ) == dup_normal([], ZZ) assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([2,4,6], ZZ) def test_dmp_mul_ground(): assert dmp_mul_ground(f_0, ZZ(2), 2, ZZ) == [ [[ZZ(2),ZZ(4),ZZ(6)], [ZZ(4)]], [[ZZ(6)]], [[ZZ(8),ZZ(10),ZZ(12)], [ZZ(2),ZZ(4),ZZ(2)], [ZZ(2)]] ] assert dmp_mul_ground(F_0, QQ(1,2), 2, QQ) == [ [[QQ(1,14),QQ(2,14),QQ(3,14)], [QQ(2,14)]], [[QQ(3,14)]], [[QQ(4,14),QQ(5,14),QQ(6,14)], [QQ(1,14),QQ(2,14),QQ(1,14)], [QQ(1,14)]] ] def test_dup_quo_ground(): raises(ZeroDivisionError, 'dup_quo_ground(dup_normal([1,2,3], ZZ), ZZ(0), ZZ)') f = dup_normal([], ZZ) assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) f = dup_normal([6,2,8], ZZ) assert dup_quo_ground(f, ZZ(1), ZZ) == f assert dup_quo_ground(f, ZZ(2), ZZ) == dup_normal([3,1,4], ZZ) assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([2,0,2], ZZ) f = dup_normal([6,2,8], QQ) assert dup_quo_ground(f, QQ(1), QQ) == f assert dup_quo_ground(f, QQ(2), QQ) == [QQ(3),QQ(1),QQ(4)] assert dup_quo_ground(f, QQ(7), QQ) == [QQ(6,7),QQ(2,7),QQ(8,7)] def test_dup_exquo_ground(): raises(ZeroDivisionError, 'dup_exquo_ground(dup_normal([1,2,3], ZZ), ZZ(0), ZZ)') raises(ExactQuotientFailed, 'dup_exquo_ground(dup_normal([1,2,3], ZZ), ZZ(3), ZZ)') f = dup_normal([], ZZ) assert dup_exquo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) f = dup_normal([6,2,8], ZZ) assert dup_exquo_ground(f, ZZ(1), ZZ) == f assert dup_exquo_ground(f, ZZ(2), ZZ) == dup_normal([3,1,4], ZZ) f = dup_normal([6,2,8], QQ) assert dup_exquo_ground(f, QQ(1), QQ) == f assert dup_exquo_ground(f, QQ(2), QQ) == [QQ(3),QQ(1),QQ(4)] assert dup_exquo_ground(f, QQ(7), QQ) == [QQ(6,7),QQ(2,7),QQ(8,7)] def test_dmp_quo_ground(): f = dmp_normal([[6],[2],[8]], 1, ZZ) assert dmp_quo_ground(f, ZZ(1), 1, ZZ) == f assert dmp_quo_ground(f, ZZ(2), 1, ZZ) == dmp_normal([[3],[1],[4]], 1, ZZ) assert dmp_normal(dmp_quo_ground(f, ZZ(3), 1, ZZ), 1, ZZ) == dmp_normal([[2],[],[2]], 1, ZZ) def test_dmp_exquo_ground(): f = dmp_normal([[6],[2],[8]], 1, ZZ) assert dmp_exquo_ground(f, ZZ(1), 1, ZZ) == f assert dmp_exquo_ground(f, ZZ(2), 1, ZZ) == dmp_normal([[3],[1],[4]], 1, ZZ) def test_dup_lshift(): assert dup_lshift([], 3, ZZ) == [] assert dup_lshift([1], 3, ZZ) == [1,0,0,0] def test_dup_rshift(): assert dup_rshift([], 3, ZZ) == [] assert dup_rshift([1,0,0,0], 3, ZZ) == [1] def test_dup_abs(): assert dup_abs([], ZZ) == [] assert dup_abs([ZZ( 1)], ZZ) == [ZZ(1)] assert dup_abs([ZZ(-7)], ZZ) == [ZZ(7)] assert dup_abs([ZZ(-1),ZZ(2),ZZ(3)], ZZ) == [ZZ(1),ZZ(2),ZZ(3)] assert dup_abs([], QQ) == [] assert dup_abs([QQ( 1,2)], QQ) == [QQ(1,2)] assert dup_abs([QQ(-7,3)], QQ) == [QQ(7,3)] assert dup_abs([QQ(-1,7),QQ(2,7),QQ(3,7)], QQ) == [QQ(1,7),QQ(2,7),QQ(3,7)] def test_dmp_abs(): assert dmp_abs([ZZ(-1)], 0, ZZ) == [ZZ(1)] assert dmp_abs([QQ(-1,2)], 0, QQ) == [QQ(1,2)] assert dmp_abs([[[]]], 2, ZZ) == [[[]]] assert dmp_abs([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_abs([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] assert dmp_abs([[[]]], 2, QQ) == [[[]]] assert dmp_abs([[[QQ(1,2)]]], 2, QQ) == [[[QQ(1,2)]]] assert dmp_abs([[[QQ(-7,9)]]], 2, QQ) == [[[QQ(7,9)]]] def test_dup_neg(): assert dup_neg([], ZZ) == [] assert dup_neg([ZZ(1)], ZZ) == [ZZ(-1)] assert dup_neg([ZZ(-7)], ZZ) == [ZZ(7)] assert dup_neg([ZZ(-1),ZZ(2),ZZ(3)], ZZ) == [ZZ(1),ZZ(-2),ZZ(-3)] assert dup_neg([], QQ) == [] assert dup_neg([QQ(1,2)], QQ) == [QQ(-1,2)] assert dup_neg([QQ(-7,9)], QQ) == [QQ(7,9)] assert dup_neg([QQ(-1,7),QQ(2,7),QQ(3,7)], QQ) == [QQ(1,7),QQ(-2,7),QQ(-3,7)] def test_dmp_neg(): assert dmp_neg([ZZ(-1)], 0, ZZ) == [ZZ(1)] assert dmp_neg([QQ(-1,2)], 0, QQ) == [QQ(1,2)] assert dmp_neg([[[]]], 2, ZZ) == [[[]]] assert dmp_neg([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] assert dmp_neg([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] assert dmp_neg([[[]]], 2, QQ) == [[[]]] assert dmp_neg([[[QQ(1,9)]]], 2, QQ) == [[[QQ(-1,9)]]] assert dmp_neg([[[QQ(-7,9)]]], 2, QQ) == [[[QQ(7,9)]]] def test_dup_add(): assert dup_add([], [], ZZ) == [] assert dup_add([ZZ(1)], [], ZZ) == [ZZ(1)] assert dup_add([], [ZZ(1)], ZZ) == [ZZ(1)] assert dup_add([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(2)] assert dup_add([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(3)] assert dup_add([ZZ(1),ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1),ZZ(3)] assert dup_add([ZZ(1)], [ZZ(1),ZZ(2)], ZZ) == [ZZ(1),ZZ(3)] assert dup_add([ZZ(1),ZZ(2),ZZ(3)], [ZZ(8),ZZ(9),ZZ(10)], ZZ) == [ZZ(9),ZZ(11),ZZ(13)] assert dup_add([], [], QQ) == [] assert dup_add([QQ(1,2)], [], QQ) == [QQ(1,2)] assert dup_add([], [QQ(1,2)], QQ) == [QQ(1,2)] assert dup_add([QQ(1,4)], [QQ(1,4)], QQ) == [QQ(1,2)] assert dup_add([QQ(1,4)], [QQ(1,2)], QQ) == [QQ(3,4)] assert dup_add([QQ(1,2),QQ(2,3)], [QQ(1)], QQ) == [QQ(1,2),QQ(5,3)] assert dup_add([QQ(1)], [QQ(1,2),QQ(2,3)], QQ) == [QQ(1,2),QQ(5,3)] assert dup_add([QQ(1,7),QQ(2,7),QQ(3,7)], [QQ(8,7),QQ(9,7),QQ(10,7)], QQ) == [QQ(9,7),QQ(11,7),QQ(13,7)] def test_dmp_add(): assert dmp_add([ZZ(1),ZZ(2)], [ZZ(1)], 0, ZZ) == \ dup_add([ZZ(1),ZZ(2)], [ZZ(1)], ZZ) assert dmp_add([QQ(1,2),QQ(2,3)], [QQ(1)], 0, QQ) == \ dup_add([QQ(1,2),QQ(2,3)], [QQ(1)], QQ) assert dmp_add([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_add([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_add([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_add([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(3)]]] assert dmp_add([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(3)]]] assert dmp_add([[[]]], [[[]]], 2, QQ) == [[[]]] assert dmp_add([[[QQ(1,2)]]], [[[]]], 2, QQ) == [[[QQ(1,2)]]] assert dmp_add([[[]]], [[[QQ(1,2)]]], 2, QQ) == [[[QQ(1,2)]]] assert dmp_add([[[QQ(2,7)]]], [[[QQ(1,7)]]], 2, QQ) == [[[QQ(3,7)]]] assert dmp_add([[[QQ(1,7)]]], [[[QQ(2,7)]]], 2, QQ) == [[[QQ(3,7)]]] def test_dup_sub(): assert dup_sub([], [], ZZ) == [] assert dup_sub([ZZ(1)], [], ZZ) == [ZZ(1)] assert dup_sub([], [ZZ(1)], ZZ) == [ZZ(-1)] assert dup_sub([ZZ(1)], [ZZ(1)], ZZ) == [] assert dup_sub([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(-1)] assert dup_sub([ZZ(1),ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1),ZZ(1)] assert dup_sub([ZZ(1)], [ZZ(1),ZZ(2)], ZZ) == [ZZ(-1),ZZ(-1)] assert dup_sub([ZZ(3),ZZ(2),ZZ(1)], [ZZ(8),ZZ(9),ZZ(10)], ZZ) == [ZZ(-5),ZZ(-7),ZZ(-9)] assert dup_sub([], [], QQ) == [] assert dup_sub([QQ(1,2)], [], QQ) == [QQ(1,2)] assert dup_sub([], [QQ(1,2)], QQ) == [QQ(-1,2)] assert dup_sub([QQ(1,3)], [QQ(1,3)], QQ) == [] assert dup_sub([QQ(1,3)], [QQ(2,3)], QQ) == [QQ(-1,3)] assert dup_sub([QQ(1,7),QQ(2,7)], [QQ(1)], QQ) == [QQ(1,7),QQ(-5,7)] assert dup_sub([QQ(1)], [QQ(1,7),QQ(2,7)], QQ) == [QQ(-1,7),QQ(5,7)] assert dup_sub([QQ(3,7),QQ(2,7),QQ(1,7)], [QQ(8,7),QQ(9,7),QQ(10,7)], QQ) == [QQ(-5,7),QQ(-7,7),QQ(-9,7)] def test_dmp_sub(): assert dmp_sub([ZZ(1),ZZ(2)], [ZZ(1)], 0, ZZ) == \ dup_sub([ZZ(1),ZZ(2)], [ZZ(1)], ZZ) assert dmp_sub([QQ(1,2),QQ(2,3)], [QQ(1)], 0, QQ) == \ dup_sub([QQ(1,2),QQ(2,3)], [QQ(1)], QQ) assert dmp_sub([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_sub([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_sub([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] assert dmp_sub([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] assert dmp_sub([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(-1)]]] assert dmp_sub([[[]]], [[[]]], 2, QQ) == [[[]]] assert dmp_sub([[[QQ(1,2)]]], [[[]]], 2, QQ) == [[[QQ(1,2)]]] assert dmp_sub([[[]]], [[[QQ(1,2)]]], 2, QQ) == [[[QQ(-1,2)]]] assert dmp_sub([[[QQ(2,7)]]], [[[QQ(1,7)]]], 2, QQ) == [[[QQ(1,7)]]] assert dmp_sub([[[QQ(1,7)]]], [[[QQ(2,7)]]], 2, QQ) == [[[QQ(-1,7)]]] def test_dup_add_mul(): assert dup_add_mul([ZZ(1),ZZ(2),ZZ(3)], [ZZ(3),ZZ(2),ZZ(1)], [ZZ(1),ZZ(2)], ZZ) == [ZZ(3), ZZ(9), ZZ(7), ZZ(5)] def test_dup_add_mul(): assert dmp_add_mul([[ZZ(1),ZZ(2)],[ZZ(3)]], [[ZZ(3)],[ZZ(2),ZZ(1)]], [[ZZ(1)],[ZZ(2)]], 1, ZZ) == [[ZZ(3)], [ZZ(3), ZZ(9)], [ZZ(4), ZZ(5)]] def test_dup_sub_mul(): assert dup_sub_mul([ZZ(1),ZZ(2),ZZ(3)], [ZZ(3),ZZ(2),ZZ(1)], [ZZ(1),ZZ(2)], ZZ) == [ZZ(-3),ZZ(-7),ZZ(-3), ZZ(1)] def test_dup_sub_mul(): assert dmp_sub_mul([[ZZ(1),ZZ(2)],[ZZ(3)]], [[ZZ(3)],[ZZ(2),ZZ(1)]], [[ZZ(1)],[ZZ(2)]], 1, ZZ) == [[ZZ(-3)], [ZZ(-1), ZZ(-5)], [ZZ(-4), ZZ(1)]] def test_dup_mul(): assert dup_mul([], [], ZZ) == [] assert dup_mul([], [ZZ(1)], ZZ) == [] assert dup_mul([ZZ(1)], [], ZZ) == [] assert dup_mul([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(1)] assert dup_mul([ZZ(5)], [ZZ(7)], ZZ) == [ZZ(35)] assert dup_mul([], [], QQ) == [] assert dup_mul([], [QQ(1,2)], QQ) == [] assert dup_mul([QQ(1,2)], [], QQ) == [] assert dup_mul([QQ(1,2)], [QQ(4,7)], QQ) == [QQ(2,7)] assert dup_mul([QQ(5,7)], [QQ(3,7)], QQ) == [QQ(15,49)] f = dup_normal([3,0,0,6,1,2], ZZ) g = dup_normal([4,0,1,0], ZZ) h = dup_normal([12,0,3,24,4,14,1,2,0], ZZ) assert dup_mul(f, g, ZZ) == h assert dup_mul(g, f, ZZ) == h f = dup_normal([2,0,0,1,7], ZZ) h = dup_normal([4,0,0,4,28,0,1,14,49], ZZ) assert dup_mul(f, f, ZZ) == h K = FF(6) assert dup_mul([K(2),K(1)], [K(3),K(4)], K) == [K(5),K(4)] def test_dmp_mul(): assert dmp_mul([ZZ(5)], [ZZ(7)], 0, ZZ) == \ dup_mul([ZZ(5)], [ZZ(7)], ZZ) assert dmp_mul([QQ(5,7)], [QQ(3,7)], 0, QQ) == \ dup_mul([QQ(5,7)], [QQ(3,7)], QQ) assert dmp_mul([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_mul([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_mul([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[]]] assert dmp_mul([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(2)]]] assert dmp_mul([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(2)]]] assert dmp_mul([[[]]], [[[]]], 2, QQ) == [[[]]] assert dmp_mul([[[QQ(1,2)]]], [[[]]], 2, QQ) == [[[]]] assert dmp_mul([[[]]], [[[QQ(1,2)]]], 2, QQ) == [[[]]] assert dmp_mul([[[QQ(2,7)]]], [[[QQ(1,3)]]], 2, QQ) == [[[QQ(2,21)]]] assert dmp_mul([[[QQ(1,7)]]], [[[QQ(2,3)]]], 2, QQ) == [[[QQ(2,21)]]] K = FF(6) assert dmp_mul([[K(2)],[K(1)]], [[K(3)],[K(4)]], 1, K) == [[K(5)],[K(4)]] def test_dup_sqr(): assert dup_sqr([], ZZ) == [] assert dup_sqr([ZZ(2)], ZZ) == [ZZ(4)] assert dup_sqr([ZZ(1),ZZ(2)], ZZ) == [ZZ(1),ZZ(4),ZZ(4)] assert dup_sqr([], QQ) == [] assert dup_sqr([QQ(2,3)], QQ) == [QQ(4,9)] assert dup_sqr([QQ(1,3),QQ(2,3)], QQ) == [QQ(1,9),QQ(4,9),QQ(4,9)] f = dup_normal([2,0,0,1,7], ZZ) assert dup_sqr(f, ZZ) == dup_normal([4,0,0,4,28,0,1,14,49], ZZ) K = FF(9) assert dup_sqr([K(3),K(4)], K) == [K(6),K(7)] def test_dmp_sqr(): assert dmp_sqr([ZZ(1),ZZ(2)], 0, ZZ) == \ dup_sqr([ZZ(1),ZZ(2)], ZZ) assert dmp_sqr([[[]]], 2, ZZ) == [[[]]] assert dmp_sqr([[[ZZ(2)]]], 2, ZZ) == [[[ZZ(4)]]] assert dmp_sqr([[[]]], 2, QQ) == [[[]]] assert dmp_sqr([[[QQ(2,3)]]], 2, QQ) == [[[QQ(4,9)]]] K = FF(9) assert dmp_sqr([[K(3)],[K(4)]], 1, K) == [[K(6)],[K(7)]] def test_dup_pow(): assert dup_pow([], 0, ZZ) == [ZZ(1)] assert dup_pow([], 0, QQ) == [QQ(1)] assert dup_pow([], 1, ZZ) == [] assert dup_pow([], 7, ZZ) == [] assert dup_pow([ZZ(1)], 0, ZZ) == [ZZ(1)] assert dup_pow([ZZ(1)], 1, ZZ) == [ZZ(1)] assert dup_pow([ZZ(1)], 7, ZZ) == [ZZ(1)] assert dup_pow([ZZ(3)], 0, ZZ) == [ZZ(1)] assert dup_pow([ZZ(3)], 1, ZZ) == [ZZ(3)] assert dup_pow([ZZ(3)], 7, ZZ) == [ZZ(2187)] assert dup_pow([QQ(1,1)], 0, QQ) == [QQ(1,1)] assert dup_pow([QQ(1,1)], 1, QQ) == [QQ(1,1)] assert dup_pow([QQ(1,1)], 7, QQ) == [QQ(1,1)] assert dup_pow([QQ(3,7)], 0, QQ) == [QQ(1,1)] assert dup_pow([QQ(3,7)], 1, QQ) == [QQ(3,7)] assert dup_pow([QQ(3,7)], 7, QQ) == [QQ(2187,823543)] f = dup_normal([2,0,0,1,7], ZZ) assert dup_pow(f, 0, ZZ) == dup_normal([1], ZZ) assert dup_pow(f, 1, ZZ) == dup_normal([2,0,0,1,7], ZZ) assert dup_pow(f, 2, ZZ) == dup_normal([4,0,0,4,28,0,1,14,49], ZZ) assert dup_pow(f, 3, ZZ) == dup_normal([8,0,0,12,84,0,6,84,294,1,21,147,343], ZZ) def test_dmp_pow(): assert dmp_pow([[]], 0, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[]], 0, 1, QQ) == [[QQ(1)]] assert dmp_pow([[]], 1, 1, ZZ) == [[]] assert dmp_pow([[]], 7, 1, ZZ) == [[]] assert dmp_pow([[ZZ(1)]], 0, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[ZZ(1)]], 1, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[ZZ(1)]], 7, 1, ZZ) == [[ZZ(1)]] assert dmp_pow([[QQ(3,7)]], 0, 1, QQ) == [[QQ(1,1)]] assert dmp_pow([[QQ(3,7)]], 1, 1, QQ) == [[QQ(3,7)]] assert dmp_pow([[QQ(3,7)]], 7, 1, QQ) == [[QQ(2187,823543)]] f = dup_normal([2,0,0,1,7], ZZ) assert dmp_pow(f, 2, 0, ZZ) == dup_pow(f, 2, ZZ) def test_dup_pdiv(): f = dup_normal([3,1,1,5], ZZ) g = dup_normal([5,-3,1], ZZ) q = dup_normal([15, 14], ZZ) r = dup_normal([52, 111], ZZ) assert dup_pdiv(f, g, ZZ) == (q, r) assert dup_pquo(f, g, ZZ) == q assert dup_prem(f, g, ZZ) == r raises(ExactQuotientFailed, 'dup_pexquo(f, g, ZZ)') f = dup_normal([3,1,1,5], QQ) g = dup_normal([5,-3,1], QQ) q = dup_normal([15, 14], QQ) r = dup_normal([52, 111], QQ) assert dup_pdiv(f, g, QQ) == (q, r) assert dup_pquo(f, g, QQ) == q assert dup_prem(f, g, QQ) == r raises(ExactQuotientFailed, 'dup_pexquo(f, g, QQ)') def test_dmp_pdiv(): f = dmp_normal([[1], [], [1,0,0]], 1, ZZ) g = dmp_normal([[1], [-1,0]], 1, ZZ) q = dmp_normal([[1], [1, 0]], 1, ZZ) r = dmp_normal([[2, 0, 0]], 1, ZZ) assert dmp_pdiv(f, g, 1, ZZ) == (q, r) assert dmp_pquo(f, g, 1, ZZ) == q assert dmp_prem(f, g, 1, ZZ) == r raises(ExactQuotientFailed, 'dmp_pexquo(f, g, 1, ZZ)') f = dmp_normal([[1], [], [1,0,0]], 1, ZZ) g = dmp_normal([[2], [-2,0]], 1, ZZ) q = dmp_normal([[2], [2, 0]], 1, ZZ) r = dmp_normal([[8, 0, 0]], 1, ZZ) assert dmp_pdiv(f, g, 1, ZZ) == (q, r) assert dmp_pquo(f, g, 1, ZZ) == q assert dmp_prem(f, g, 1, ZZ) == r raises(ExactQuotientFailed, 'dmp_pexquo(f, g, 1, ZZ)') def test_dup_rr_div(): raises(ZeroDivisionError, "dup_rr_div([1,2,3], [], ZZ)") f = dup_normal([3,1,1,5], ZZ) g = dup_normal([5,-3,1], ZZ) q, r = [], f assert dup_rr_div(f, g, ZZ) == (q, r) def test_dmp_rr_div(): raises(ZeroDivisionError, "dmp_rr_div([[1,2],[3]], [[]], 1, ZZ)") f = dmp_normal([[1], [], [1,0,0]], 1, ZZ) g = dmp_normal([[1], [-1,0]], 1, ZZ) q = dmp_normal([[1], [1, 0]], 1, ZZ) r = dmp_normal([[2, 0, 0]], 1, ZZ) assert dmp_rr_div(f, g, 1, ZZ) == (q, r) f = dmp_normal([[1], [], [1,0,0]], 1, ZZ) g = dmp_normal([[-1], [1,0]], 1, ZZ) q = dmp_normal([[-1], [-1, 0]], 1, ZZ) r = dmp_normal([[2, 0, 0]], 1, ZZ) assert dmp_rr_div(f, g, 1, ZZ) == (q, r) f = dmp_normal([[1], [], [1,0,0]], 1, ZZ) g = dmp_normal([[2], [-2,0]], 1, ZZ) q, r = [[]], f assert dmp_rr_div(f, g, 1, ZZ) == (q, r) def test_dup_ff_div(): raises(ZeroDivisionError, "dup_ff_div([1,2,3], [], QQ)") f = dup_normal([3,1,1,5], QQ) g = dup_normal([5,-3,1], QQ) q = [QQ(3,5), QQ(14,25)] r = [QQ(52,25), QQ(111,25)] assert dup_ff_div(f, g, QQ) == (q, r) def test_dmp_ff_div(): raises(ZeroDivisionError, "dmp_ff_div([[1,2],[3]], [[]], 1, QQ)") f = dmp_normal([[1], [], [1,0,0]], 1, QQ) g = dmp_normal([[1], [-1,0]], 1, QQ) q = [[QQ(1, 1)], [QQ(1, 1), QQ(0, 1)]] r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] assert dmp_ff_div(f, g, 1, QQ) == (q, r) f = dmp_normal([[1], [], [1,0,0]], 1, QQ) g = dmp_normal([[-1], [1,0]], 1, QQ) q = [[QQ(-1, 1)], [QQ(-1, 1), QQ(0, 1)]] r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] assert dmp_ff_div(f, g, 1, QQ) == (q, r) f = dmp_normal([[1], [], [1,0,0]], 1, QQ) g = dmp_normal([[2], [-2,0]], 1, QQ) q = [[QQ(1, 2)], [QQ(1, 2), QQ(0, 1)]] r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] assert dmp_ff_div(f, g, 1, QQ) == (q, r) def test_dup_div(): f, g, q, r = [5,4,3,2,1], [1,2,3], [5,-6,0], [20,1] assert dup_div(f, g, ZZ) == (q, r) assert dup_quo(f, g, ZZ) == q assert dup_rem(f, g, ZZ) == r raises(ExactQuotientFailed, 'dup_exquo(f, g, ZZ)') f, g, q, r = [5,4,3,2,1,0], [1,2,0,0,9], [5,-6], [15,2,-44,54] assert dup_div(f, g, ZZ) == (q, r) assert dup_quo(f, g, ZZ) == q assert dup_rem(f, g, ZZ) == r raises(ExactQuotientFailed, 'dup_exquo(f, g, ZZ)') def test_dmp_div(): f, g, q, r = [5,4,3,2,1], [1,2,3], [5,-6,0], [20,1] assert dmp_div(f, g, 0, ZZ) == (q, r) assert dmp_quo(f, g, 0, ZZ) == q assert dmp_rem(f, g, 0, ZZ) == r raises(ExactQuotientFailed, 'dmp_exquo(f, g, 0, ZZ)') f, g, q, r = [[[1]]], [[[2]],[1]], [[[]]], [[[1]]] assert dmp_div(f, g, 2, ZZ) == (q, r) assert dmp_quo(f, g, 2, ZZ) == q assert dmp_rem(f, g, 2, ZZ) == r raises(ExactQuotientFailed, 'dmp_exquo(f, g, 2, ZZ)') def test_dup_max_norm(): assert dup_max_norm([], ZZ) == 0 assert dup_max_norm([1], ZZ) == 1 assert dup_max_norm([1,4,2,3], ZZ) == 4 def test_dmp_max_norm(): assert dmp_max_norm([[[]]], 2, ZZ) == 0 assert dmp_max_norm([[[1]]], 2, ZZ) == 1 assert dmp_max_norm(f_0, 2, ZZ) == 6 def test_dup_l1_norm(): assert dup_l1_norm([], ZZ) == 0 assert dup_l1_norm([1], ZZ) == 1 assert dup_l1_norm([1,4,2,3], ZZ) == 10 def test_dmp_l1_norm(): assert dmp_l1_norm([[[]]], 2, ZZ) == 0 assert dmp_l1_norm([[[1]]], 2, ZZ) == 1 assert dmp_l1_norm(f_0, 2, ZZ) == 31 def test_dup_expand(): assert dup_expand((), ZZ) == [1] assert dup_expand(([1,2,3], [1,2], [7,5,4,3]), ZZ) == \ dup_mul([1,2,3], dup_mul([1,2], [7,5,4,3], ZZ), ZZ) def test_dmp_expand(): assert dmp_expand((), 1, ZZ) == [[1]] assert dmp_expand(([[1],[2],[3]], [[1],[2]], [[7],[5],[4],[3]]), 1, ZZ) == \ dmp_mul([[1],[2],[3]], dmp_mul([[1],[2]], [[7],[5],[4],[3]], 1, ZZ), 1, ZZ) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_polyutils.py0000644000175000017500000002065512014170666027074 0ustar georgeskgeorgesk"""Tests for useful utilities for higher level polynomial classes. """ from sympy import S, I, Integer, sin, cos, sqrt, symbols, pi from sympy.utilities.pytest import raises from sympy.polys.polyutils import ( _sort_gens, _unify_gens, _analyze_gens, _sort_factors, _parallel_dict_from_expr_if_gens, _parallel_dict_from_expr_no_gens, _dict_from_expr_if_gens, _dict_from_expr_no_gens, parallel_dict_from_expr, dict_from_expr, expr_from_dict, ) from sympy.polys.polyerrors import ( GeneratorsNeeded, PolynomialError, ) from sympy.polys.domains import ZZ, QQ, EX x,y,z,p,q,r,s,t,u,v,w = symbols('x,y,z,p,q,r,s,t,u,v,w') A, B = symbols('A,B', commutative=False) def test__sort_gens(): assert _sort_gens([]) == () assert _sort_gens([x]) == (x,) assert _sort_gens([p]) == (p,) assert _sort_gens([q]) == (q,) assert _sort_gens([x,p]) == (x,p) assert _sort_gens([p,x]) == (x,p) assert _sort_gens([q,p]) == (p,q) assert _sort_gens([q,p,x]) == (x,p,q) assert _sort_gens([x,p,q], wrt=x) == (x,p,q) assert _sort_gens([x,p,q], wrt=p) == (p,x,q) assert _sort_gens([x,p,q], wrt=q) == (q,x,p) assert _sort_gens([x,p,q], wrt='x') == (x,p,q) assert _sort_gens([x,p,q], wrt='p') == (p,x,q) assert _sort_gens([x,p,q], wrt='q') == (q,x,p) assert _sort_gens([x,p,q], wrt='x,q') == (x,q,p) assert _sort_gens([x,p,q], wrt='q,x') == (q,x,p) assert _sort_gens([x,p,q], wrt='p,q') == (p,q,x) assert _sort_gens([x,p,q], wrt='q,p') == (q,p,x) assert _sort_gens([x,p,q], wrt='x, q') == (x,q,p) assert _sort_gens([x,p,q], wrt='q, x') == (q,x,p) assert _sort_gens([x,p,q], wrt='p, q') == (p,q,x) assert _sort_gens([x,p,q], wrt='q, p') == (q,p,x) assert _sort_gens([x,p,q], wrt=[x, 'q']) == (x,q,p) assert _sort_gens([x,p,q], wrt=[q, 'x']) == (q,x,p) assert _sort_gens([x,p,q], wrt=[p, 'q']) == (p,q,x) assert _sort_gens([x,p,q], wrt=[q, 'p']) == (q,p,x) assert _sort_gens([x,p,q], wrt=['x', 'q']) == (x,q,p) assert _sort_gens([x,p,q], wrt=['q', 'x']) == (q,x,p) assert _sort_gens([x,p,q], wrt=['p', 'q']) == (p,q,x) assert _sort_gens([x,p,q], wrt=['q', 'p']) == (q,p,x) assert _sort_gens([x,p,q], sort='x > p > q') == (x, p, q) assert _sort_gens([x,p,q], sort='p > x > q') == (p, x, q) assert _sort_gens([x,p,q], sort='p > q > x') == (p, q, x) assert _sort_gens([x,p,q], wrt='x', sort='q > p') == (x, q, p) assert _sort_gens([x,p,q], wrt='p', sort='q > x') == (p, q, x) assert _sort_gens([x,p,q], wrt='q', sort='p > x') == (q, p, x) X = symbols('x0,x1,x2,x10,x11,x12,x20,x21,x22') assert _sort_gens(X) == X def test__unify_gens(): assert _unify_gens([], []) == () assert _unify_gens([x], [x]) == (x,) assert _unify_gens([y], [y]) == (y,) assert _unify_gens([x,y], [x]) == (x,y) assert _unify_gens([x], [x,y]) == (x,y) assert _unify_gens([x,y], [x,y]) == (x,y) assert _unify_gens([y,x], [y,x]) == (y,x) assert _unify_gens([x], [y]) == (x,y) assert _unify_gens([y], [x]) == (y,x) assert _unify_gens([x], [y,x]) == (y,x) assert _unify_gens([y,x], [x]) == (y,x) assert _unify_gens([x,y,z], [x,y,z]) == (x,y,z) assert _unify_gens([z,y,x], [x,y,z]) == (z,y,x) assert _unify_gens([x,y,z], [z,y,x]) == (x,y,z) assert _unify_gens([z,y,x], [z,y,x]) == (z,y,x) assert _unify_gens([x,y,z], [t,x,p,q,z]) == (t,x,y,p,q,z) def test__analyze_gens(): assert _analyze_gens((x,y,z)) == (x,y,z) assert _analyze_gens([x,y,z]) == (x,y,z) assert _analyze_gens(([x,y,z],)) == (x,y,z) assert _analyze_gens(((x,y,z),)) == (x,y,z) def test__sort_factors(): assert _sort_factors([], multiple=True) == [] assert _sort_factors([], multiple=False) == [] F = [[1,2,3], [1,2], [1]] G = [[1], [1,2], [1,2,3]] assert _sort_factors(F, multiple=False) == G F = [[1,2], [1,2,3], [1,2], [1]] G = [[1], [1,2], [1,2], [1,2,3]] assert _sort_factors(F, multiple=False) == G F = [[2,2], [1,2,3], [1,2], [1]] G = [[1], [1,2], [2,2], [1,2,3]] assert _sort_factors(F, multiple=False) == G F = [([1,2,3], 1), ([1,2], 1), ([1], 1)] G = [([1], 1), ([1,2], 1), ([1,2,3], 1)] assert _sort_factors(F, multiple=True) == G F = [([1,2], 1), ([1,2,3], 1), ([1,2], 1), ([1], 1)] G = [([1], 1), ([1,2], 1), ([1,2], 1), ([1,2,3], 1)] assert _sort_factors(F, multiple=True) == G F = [([2,2], 1), ([1,2,3], 1), ([1,2], 1), ([1], 1)] G = [([1], 1), ([1,2], 1), ([2,2], 1), ([1,2,3], 1)] assert _sort_factors(F, multiple=True) == G F = [([2,2], 1), ([1,2,3], 1), ([1,2], 2), ([1], 1)] G = [([1], 1), ([2,2], 1), ([1,2], 2), ([1,2,3], 1)] assert _sort_factors(F, multiple=True) == G def test__dict_from_expr_if_gens(): assert dict_from_expr(Integer(17), gens=(x,)) == ({(0,): Integer(17)}, (x,)) assert dict_from_expr(Integer(17), gens=(x,y)) == ({(0,0): Integer(17)}, (x,y)) assert dict_from_expr(Integer(17), gens=(x,y,z)) == ({(0,0,0): Integer(17)}, (x,y,z)) assert dict_from_expr(Integer(-17), gens=(x,)) == ({(0,): Integer(-17)}, (x,)) assert dict_from_expr(Integer(-17), gens=(x,y)) == ({(0,0): Integer(-17)}, (x,y)) assert dict_from_expr(Integer(-17), gens=(x,y,z)) == ({(0,0,0): Integer(-17)}, (x,y,z)) assert dict_from_expr(Integer(17)*x, gens=(x,)) == ({(1,): Integer(17)}, (x,)) assert dict_from_expr(Integer(17)*x, gens=(x,y)) == ({(1,0): Integer(17)}, (x,y)) assert dict_from_expr(Integer(17)*x, gens=(x,y,z)) == ({(1,0,0): Integer(17)}, (x,y,z)) assert dict_from_expr(Integer(17)*x**7, gens=(x,)) == ({(7,): Integer(17)}, (x,)) assert dict_from_expr(Integer(17)*x**7*y, gens=(x,y)) == ({(7,1): Integer(17)}, (x,y)) assert dict_from_expr(Integer(17)*x**7*y*z**12, gens=(x,y,z)) == ({(7,1,12): Integer(17)}, (x,y,z)) assert dict_from_expr(x+2*y+3*z, gens=(x,)) == \ ({(1,): Integer(1), (0,): 2*y+3*z}, (x,)) assert dict_from_expr(x+2*y+3*z, gens=(x,y)) == \ ({(1,0): Integer(1), (0,1): Integer(2), (0,0): 3*z}, (x,y)) assert dict_from_expr(x+2*y+3*z, gens=(x,y,z)) == \ ({(1,0,0): Integer(1), (0,1,0): Integer(2), (0,0,1): Integer(3)}, (x,y,z)) assert dict_from_expr(x*y+2*x*z+3*y*z, gens=(x,)) == \ ({(1,): y+2*z, (0,): 3*y*z}, (x,)) assert dict_from_expr(x*y+2*x*z+3*y*z, gens=(x,y)) == \ ({(1,1): Integer(1), (1,0): 2*z, (0,1): 3*z}, (x,y)) assert dict_from_expr(x*y+2*x*z+3*y*z, gens=(x,y,z)) == \ ({(1,1,0): Integer(1), (1,0,1): Integer(2), (0,1,1): Integer(3)}, (x,y,z)) assert dict_from_expr(2**y*x, gens=(x,)) == ({(1,): 2**y}, (x,)) raises(PolynomialError, "dict_from_expr(2**y*x, gens=(x,y))") def test__dict_from_expr_no_gens(): raises(GeneratorsNeeded, "dict_from_expr(Integer(17))") assert dict_from_expr(x) == ({(1,): Integer(1)}, (x,)) assert dict_from_expr(y) == ({(1,): Integer(1)}, (y,)) assert dict_from_expr(x*y) == ({(1,1): Integer(1)}, (x,y)) assert dict_from_expr(x+y) == ({(1,0): Integer(1), (0,1): Integer(1)}, (x,y)) assert dict_from_expr(sqrt(2)) == ({(1,): Integer(1)}, (sqrt(2),)) raises(GeneratorsNeeded, "dict_from_expr(sqrt(2), greedy=False)") assert dict_from_expr(x*y, domain=ZZ[x]) == ({(1,): x}, (y,)) assert dict_from_expr(x*y, domain=ZZ[y]) == ({(1,): y}, (x,)) assert dict_from_expr(3*sqrt(2)*pi*x*y, extension=None) == ({(1,1,1,1): 3}, (x,y,sqrt(2),pi)) assert dict_from_expr(3*sqrt(2)*pi*x*y, extension=True) == ({(1,1,1): 3*sqrt(2)}, (x,y,pi)) assert dict_from_expr(3*sqrt(2)*pi*x*y, extension=True) == ({(1,1,1): 3*sqrt(2)}, (x,y,pi)) f = cos(x)*sin(x) + cos(x)*sin(y) + cos(y)*sin(x) + cos(y)*sin(y) assert dict_from_expr(f) == ({(0, 1, 0, 1): 1, (0, 1, 1, 0): 1, (1, 0, 0, 1): 1, (1, 0, 1, 0): 1}, (cos(x), cos(y), sin(x), sin(y))) def test__parallel_dict_from_expr_if_gens(): assert parallel_dict_from_expr([x+2*y+3*z, Integer(7)], gens=(x,)) == \ ([{(1,): Integer(1), (0,): 2*y+3*z}, {(0,): Integer(7)}], (x,)) def test__parallel_dict_from_expr_no_gens(): assert parallel_dict_from_expr([x*y, Integer(3)]) == \ ([{(1,1): Integer(1)}, {(0,0): Integer(3)}], (x,y)) assert parallel_dict_from_expr([x*y, 2*z, Integer(3)]) == \ ([{(1,1,0): Integer(1)}, {(0,0,1): Integer(2)}, {(0,0,0): Integer(3)}], (x,y,z)) def test_parallel_dict_from_expr(): raises(PolynomialError, "parallel_dict_from_expr([A*B - B*A])") def test_dict_from_expr(): raises(PolynomialError, "dict_from_expr(A*B - B*A)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_galoistools.py0000644000175000017500000005770312014170666027373 0ustar georgeskgeorgesk from sympy.polys.galoistools import ( gf_crt, gf_crt1, gf_crt2, gf_int, gf_degree, gf_strip, gf_trunc, gf_normal, gf_from_dict, gf_to_dict, gf_from_int_poly, gf_to_int_poly, gf_neg, gf_add_ground, gf_sub_ground, gf_mul_ground, gf_quo_ground, gf_add, gf_sub, gf_add_mul, gf_sub_mul, gf_mul, gf_sqr, gf_div, gf_rem, gf_quo, gf_exquo, gf_lshift, gf_rshift, gf_expand, gf_pow, gf_pow_mod, gf_gcdex, gf_gcd, gf_lcm, gf_cofactors, gf_LC, gf_TC, gf_monic, gf_eval, gf_multi_eval, gf_compose, gf_compose_mod, gf_trace_map, gf_diff, gf_random, gf_irreducible, gf_irreducible_p, gf_irred_p_ben_or, gf_irred_p_rabin, gf_sqf_list, gf_sqf_part, gf_sqf_p, gf_Qmatrix, gf_Qbasis, gf_ddf_zassenhaus, gf_ddf_shoup, gf_edf_zassenhaus, gf_edf_shoup, gf_berlekamp, gf_zassenhaus, gf_shoup, gf_factor_sqf, gf_factor, ) from sympy.polys.polyerrors import ( ExactQuotientFailed, ) from sympy.polys.polyconfig import setup from sympy.polys.domains import ZZ from sympy import pi, nextprime from sympy.utilities.pytest import raises def test_gf_crt(): U = [49, 76, 65] M = [99, 97, 95] p = 912285 u = 639985 assert gf_crt(U, M, ZZ) == u E = [9215, 9405, 9603] S = [62, 24, 12] assert gf_crt1(M, ZZ) == (p, E, S) assert gf_crt2(U, M, p, E, S, ZZ) == u def test_gf_int(): assert gf_int(0, 5) == 0 assert gf_int(1, 5) == 1 assert gf_int(2, 5) == 2 assert gf_int(3, 5) ==-2 assert gf_int(4, 5) ==-1 assert gf_int(5, 5) == 0 def test_gf_degree(): assert gf_degree([]) == -1 assert gf_degree([1]) == 0 assert gf_degree([1,0]) == 1 assert gf_degree([1,0,0,0,1]) == 4 def test_gf_strip(): assert gf_strip([]) == [] assert gf_strip([0]) == [] assert gf_strip([0,0,0]) == [] assert gf_strip([1]) == [1] assert gf_strip([0,1]) == [1] assert gf_strip([0,0,0,1]) == [1] assert gf_strip([1,2,0]) == [1,2,0] assert gf_strip([0,1,2,0]) == [1,2,0] assert gf_strip([0,0,0,1,2,0]) == [1,2,0] def test_gf_trunc(): assert gf_trunc([], 11) == [] assert gf_trunc([1], 11) == [1] assert gf_trunc([22], 11) == [] assert gf_trunc([12], 11) == [1] assert gf_trunc([11,22,17,1,0], 11) == [6,1,0] assert gf_trunc([12,23,17,1,0], 11) == [1,1,6,1,0] def test_gf_normal(): assert gf_normal([11,22,17,1,0], 11, ZZ) == [6,1,0] def test_gf_from_to_dict(): f = {11: 12, 6: 2, 0: 25} F = {11: 1, 6: 2, 0: 3} g = [1,0,0,0,0,2,0,0,0,0,0,3] assert gf_from_dict(f, 11, ZZ) == g assert gf_to_dict(g, 11) == F f = {11: -5, 4: 0, 3: 1, 0: 12} F = {11: -5, 3: 1, 0: 1} g = [6,0,0,0,0,0,0,0,1,0,0,1] assert gf_from_dict(f, 11, ZZ) == g assert gf_to_dict(g, 11) == F assert gf_to_dict([10], 11, symmetric=True) == {0: -1} assert gf_to_dict([10], 11, symmetric=False) == {0: 10} def test_gf_from_to_int_poly(): assert gf_from_int_poly([1,0,7,2,20], 5) == [1,0,2,2,0] assert gf_to_int_poly([1,0,4,2,3], 5) == [1,0,-1,2,-2] assert gf_to_int_poly([10], 11, symmetric=True) == [-1] assert gf_to_int_poly([10], 11, symmetric=False) == [10] def test_gf_LC(): assert gf_LC([], ZZ) == 0 assert gf_LC([1], ZZ) == 1 assert gf_LC([1,2], ZZ) == 1 def test_gf_TC(): assert gf_TC([], ZZ) == 0 assert gf_TC([1], ZZ) == 1 assert gf_TC([1,2], ZZ) == 2 def test_gf_monic(): assert gf_monic([], 11, ZZ) == (0, []) assert gf_monic([1], 11, ZZ) == (1, [1]) assert gf_monic([2], 11, ZZ) == (2, [1]) assert gf_monic([1,2,3,4], 11, ZZ) == (1, [1,2,3,4]) assert gf_monic([2,3,4,5], 11, ZZ) == (2, [1,7,2,8]) def test_gf_arith(): assert gf_neg([], 11, ZZ) == [] assert gf_neg([1], 11, ZZ) == [10] assert gf_neg([1,2,3], 11, ZZ) == [10,9,8] assert gf_add_ground([], 0, 11, ZZ) == [] assert gf_sub_ground([], 0, 11, ZZ) == [] assert gf_add_ground([], 3, 11, ZZ) == [3] assert gf_sub_ground([], 3, 11, ZZ) == [8] assert gf_add_ground([1], 3, 11, ZZ) == [4] assert gf_sub_ground([1], 3, 11, ZZ) == [9] assert gf_add_ground([8], 3, 11, ZZ) == [] assert gf_sub_ground([3], 3, 11, ZZ) == [] assert gf_add_ground([1,2,3], 3, 11, ZZ) == [1,2,6] assert gf_sub_ground([1,2,3], 3, 11, ZZ) == [1,2,0] assert gf_mul_ground([], 0, 11, ZZ) == [] assert gf_mul_ground([], 1, 11, ZZ) == [] assert gf_mul_ground([1], 0, 11, ZZ) == [] assert gf_mul_ground([1], 1, 11, ZZ) == [1] assert gf_mul_ground([1,2,3], 0, 11, ZZ) == [] assert gf_mul_ground([1,2,3], 1, 11, ZZ) == [1,2,3] assert gf_mul_ground([1,2,3], 7, 11, ZZ) == [7,3,10] assert gf_add([], [], 11, ZZ) == [] assert gf_add([1], [], 11, ZZ) == [1] assert gf_add([], [1], 11, ZZ) == [1] assert gf_add([1], [1], 11, ZZ) == [2] assert gf_add([1], [2], 11, ZZ) == [3] assert gf_add([1,2], [1], 11, ZZ) == [1,3] assert gf_add([1], [1,2], 11, ZZ) == [1,3] assert gf_add([1,2,3], [8,9,10], 11, ZZ) == [9,0,2] assert gf_sub([], [], 11, ZZ) == [] assert gf_sub([1], [], 11, ZZ) == [1] assert gf_sub([], [1], 11, ZZ) == [10] assert gf_sub([1], [1], 11, ZZ) == [] assert gf_sub([1], [2], 11, ZZ) == [10] assert gf_sub([1,2], [1], 11, ZZ) == [1,1] assert gf_sub([1], [1,2], 11, ZZ) == [10,10] assert gf_sub([3,2,1], [8,9,10], 11, ZZ) == [6,4,2] assert gf_add_mul([1,5,6], [7,3], [8,0,6,1], 11, ZZ) == [1,2,10,8,9] assert gf_sub_mul([1,5,6], [7,3], [8,0,6,1], 11, ZZ) == [10,9,3,2,3] assert gf_mul([], [], 11, ZZ) == [] assert gf_mul([], [1], 11, ZZ) == [] assert gf_mul([1], [], 11, ZZ) == [] assert gf_mul([1], [1], 11, ZZ) == [1] assert gf_mul([5], [7], 11, ZZ) == [2] assert gf_mul([3,0,0,6,1,2], [4,0,1,0], 11, ZZ) == [1,0,3,2,4,3,1,2,0] assert gf_mul([4,0,1,0], [3,0,0,6,1,2], 11, ZZ) == [1,0,3,2,4,3,1,2,0] assert gf_mul([2,0,0,1,7], [2,0,0,1,7], 11, ZZ) == [4,0,0,4,6,0,1,3,5] assert gf_sqr([], 11, ZZ) == [] assert gf_sqr([2], 11, ZZ) == [4] assert gf_sqr([1,2], 11, ZZ) == [1,4,4] assert gf_sqr([2,0,0,1,7], 11, ZZ) == [4,0,0,4,6,0,1,3,5] def test_gf_division(): raises(ZeroDivisionError, "gf_div([1,2,3], [], 11, ZZ)") raises(ZeroDivisionError, "gf_rem([1,2,3], [], 11, ZZ)") raises(ZeroDivisionError, "gf_quo([1,2,3], [], 11, ZZ)") raises(ZeroDivisionError, "gf_quo([1,2,3], [], 11, ZZ)") assert gf_div([1], [1,2,3], 7, ZZ) == ([], [1]) assert gf_rem([1], [1,2,3], 7, ZZ) == [1] assert gf_quo([1], [1,2,3], 7, ZZ) == [] f, g, q, r = [5,4,3,2,1,0], [1,2,3], [5,1,0,6], [3,3] assert gf_div(f, g, 7, ZZ) == (q, r) assert gf_rem(f, g, 7, ZZ) == r assert gf_quo(f, g, 7, ZZ) == q raises(ExactQuotientFailed, "gf_exquo(f, g, 7, ZZ)") f, g, q, r = [5,4,3,2,1,0], [1,2,3,0], [5,1,0], [6,1,0] assert gf_div(f, g, 7, ZZ) == (q, r) assert gf_rem(f, g, 7, ZZ) == r assert gf_quo(f, g, 7, ZZ) == q raises(ExactQuotientFailed, "gf_exquo(f, g, 7, ZZ)") assert gf_quo([1,2,1], [1,1], 11, ZZ) == [1,1] def test_gf_shift(): f = [1,2,3,4,5] assert gf_lshift([], 5, ZZ) == [] assert gf_rshift([], 5, ZZ) == ([], []) assert gf_lshift(f, 1, ZZ) == [1,2,3,4,5,0] assert gf_lshift(f, 2, ZZ) == [1,2,3,4,5,0,0] assert gf_rshift(f, 0, ZZ) == (f, []) assert gf_rshift(f, 1, ZZ) == ([1,2,3,4], [5]) assert gf_rshift(f, 3, ZZ) == ([1,2], [3,4,5]) assert gf_rshift(f, 5, ZZ) == ([], f) def test_gf_expand(): F = [([1,1], 2), ([1,2], 3)] assert gf_expand(F, 11, ZZ) == [1,8,3,5,6,8] assert gf_expand((4, F), 11, ZZ) == [4,10,1,9,2,10] def test_gf_powering(): assert gf_pow([1,0,0,1,8], 0, 11, ZZ) == [1] assert gf_pow([1,0,0,1,8], 1, 11, ZZ) == [1, 0, 0, 1, 8] assert gf_pow([1,0,0,1,8], 2, 11, ZZ) == [1, 0, 0, 2, 5, 0, 1, 5, 9] assert gf_pow([1,0,0,1,8], 5, 11, ZZ) == \ [1, 0, 0, 5, 7, 0, 10, 6, 2, 10, 9, 6, 10, 6, 6, 0, 5, 2, 5, 9, 10] assert gf_pow([1,0,0,1,8], 8, 11, ZZ) == \ [1, 0, 0, 8, 9, 0, 6, 8, 10, 1, 2, 5, 10, 7, 7, 9, 1, 2, 0, 0, 6, 2, 5, 2, 5, 7, 7, 9, 10, 10, 7, 5, 5] assert gf_pow([1,0,0,1,8], 45, 11, ZZ) == \ [ 1, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 6, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10] assert gf_pow_mod([1,0,0,1,8], 0, [2,0,7], 11, ZZ) == [1] assert gf_pow_mod([1,0,0,1,8], 1, [2,0,7], 11, ZZ) == [1,1] assert gf_pow_mod([1,0,0,1,8], 2, [2,0,7], 11, ZZ) == [2,3] assert gf_pow_mod([1,0,0,1,8], 5, [2,0,7], 11, ZZ) == [7,8] assert gf_pow_mod([1,0,0,1,8], 8, [2,0,7], 11, ZZ) == [1,5] assert gf_pow_mod([1,0,0,1,8], 45, [2,0,7], 11, ZZ) == [5,4] def test_gf_gcdex(): assert gf_gcdex([], [], 11, ZZ) == ([1], [], []) assert gf_gcdex([2], [], 11, ZZ) == ([6], [], [1]) assert gf_gcdex([], [2], 11, ZZ) == ([], [6], [1]) assert gf_gcdex([2], [2], 11, ZZ) == ([], [6], [1]) assert gf_gcdex([], [3,0], 11, ZZ) == ([], [4], [1,0]) assert gf_gcdex([3,0], [], 11, ZZ) == ([4], [], [1,0]) assert gf_gcdex([3,0], [3,0], 11, ZZ) == ([], [4], [1,0]) assert gf_gcdex([1,8,7], [1,7,1,7], 11, ZZ) == ([5,6], [6], [1,7]) def test_gf_gcd(): assert gf_gcd([], [], 11, ZZ) == [] assert gf_gcd([2], [], 11, ZZ) == [1] assert gf_gcd([], [2], 11, ZZ) == [1] assert gf_gcd([2], [2], 11, ZZ) == [1] assert gf_gcd([], [1,0], 11, ZZ) == [1,0] assert gf_gcd([1,0], [], 11, ZZ) == [1,0] assert gf_gcd([3,0], [3,0], 11, ZZ) == [1,0] assert gf_gcd([1,8,7], [1,7,1,7], 11, ZZ) == [1,7] def test_gf_lcm(): assert gf_lcm([], [], 11, ZZ) == [] assert gf_lcm([2], [], 11, ZZ) == [] assert gf_lcm([], [2], 11, ZZ) == [] assert gf_lcm([2], [2], 11, ZZ) == [1] assert gf_lcm([], [1,0], 11, ZZ) == [] assert gf_lcm([1,0], [], 11, ZZ) == [] assert gf_lcm([3,0], [3,0], 11, ZZ) == [1,0] assert gf_lcm([1,8,7], [1,7,1,7], 11, ZZ) == [1,8,8,8,7] def test_gf_cofactors(): assert gf_cofactors([], [], 11, ZZ) == ([], [], []) assert gf_cofactors([2], [], 11, ZZ) == ([1], [2], []) assert gf_cofactors([], [2], 11, ZZ) == ([1], [], [2]) assert gf_cofactors([2], [2], 11, ZZ) == ([1], [2], [2]) assert gf_cofactors([], [1,0], 11, ZZ) == ([1,0], [], [1]) assert gf_cofactors([1,0], [], 11, ZZ) == ([1,0], [1], []) assert gf_cofactors([3,0], [3,0], 11, ZZ) == ([1,0], [3], [3]) assert gf_cofactors([1,8,7], [1,7,1,7], 11, ZZ) == (([1,7], [1,1], [1,0,1])) def test_gf_diff(): assert gf_diff([], 11, ZZ) == [] assert gf_diff([7], 11, ZZ) == [] assert gf_diff([7,3], 11, ZZ) == [7] assert gf_diff([7,3,1], 11, ZZ) == [3,3] assert gf_diff([1,0,0,0,0,0,0,0,0,0,0,1], 11, ZZ) == [] def test_gf_eval(): assert gf_eval([], 4, 11, ZZ) == 0 assert gf_eval([], 27, 11, ZZ) == 0 assert gf_eval([7], 4, 11, ZZ) == 7 assert gf_eval([7], 27, 11, ZZ) == 7 assert gf_eval([1,0,3,2,4,3,1,2,0], 0, 11, ZZ) == 0 assert gf_eval([1,0,3,2,4,3,1,2,0], 4, 11, ZZ) == 9 assert gf_eval([1,0,3,2,4,3,1,2,0], 27, 11, ZZ) == 5 assert gf_eval([4,0,0,4,6,0,1,3,5], 0, 11, ZZ) == 5 assert gf_eval([4,0,0,4,6,0,1,3,5], 4, 11, ZZ) == 3 assert gf_eval([4,0,0,4,6,0,1,3,5], 27, 11, ZZ) == 9 assert gf_multi_eval([3,2,1], [0,1,2,3], 11, ZZ) == [1,6,6,1] def test_gf_compose(): assert gf_compose([], [1,0], 11, ZZ) == [] assert gf_compose_mod([], [1,0], [1,0], 11, ZZ) == [] assert gf_compose([1], [], 11, ZZ) == [1] assert gf_compose([1,0], [], 11, ZZ) == [] assert gf_compose([1,0], [1,0], 11, ZZ) == [1,0] f, g, h = [1, 1, 4, 9, 1], [1,1,1], [1,0,0,2] assert gf_compose(g, h, 11, ZZ) == [1,0,0,5,0,0,7] assert gf_compose_mod(g, h, f, 11, ZZ) == [3,9,6,10] def test_gf_trace_map(): f, a, c = [1, 1, 4, 9, 1], [1,1,1], [1,0] b = gf_pow_mod(c, 11, f, 11, ZZ) assert gf_trace_map(a, b, c, 0, f, 11, ZZ) == \ ([1, 1, 1], [1, 1, 1]) assert gf_trace_map(a, b, c, 1, f, 11, ZZ) == \ ([5, 2, 10, 3], [5, 3, 0, 4]) assert gf_trace_map(a, b, c, 2, f, 11, ZZ) == \ ([5, 9, 5, 3], [10, 1, 5, 7]) assert gf_trace_map(a, b, c, 3, f, 11, ZZ) == \ ([1, 10, 6, 0], [7]) assert gf_trace_map(a, b, c, 4, f, 11, ZZ) == \ ([1, 1, 1], [1, 1, 8]) assert gf_trace_map(a, b, c, 5, f, 11, ZZ) == \ ([5, 2, 10, 3], [5, 3, 0, 0]) assert gf_trace_map(a, b, c, 11, f, 11, ZZ) == \ ([1, 10, 6, 0], [10]) def test_gf_irreducible(): assert gf_irreducible_p(gf_irreducible(1, 11, ZZ), 11, ZZ) == True assert gf_irreducible_p(gf_irreducible(2, 11, ZZ), 11, ZZ) == True assert gf_irreducible_p(gf_irreducible(3, 11, ZZ), 11, ZZ) == True assert gf_irreducible_p(gf_irreducible(4, 11, ZZ), 11, ZZ) == True assert gf_irreducible_p(gf_irreducible(5, 11, ZZ), 11, ZZ) == True assert gf_irreducible_p(gf_irreducible(6, 11, ZZ), 11, ZZ) == True assert gf_irreducible_p(gf_irreducible(7, 11, ZZ), 11, ZZ) == True def test_gf_irreducible_p(): assert gf_irred_p_ben_or([7], 11, ZZ) == True assert gf_irred_p_ben_or([7,3], 11, ZZ) == True assert gf_irred_p_ben_or([7,3,1], 11, ZZ) == False assert gf_irred_p_rabin([7], 11, ZZ) == True assert gf_irred_p_rabin([7,3], 11, ZZ) == True assert gf_irred_p_rabin([7,3,1], 11, ZZ) == False setup('GF_IRRED_METHOD', 'ben-or') assert gf_irreducible_p([7], 11, ZZ) == True assert gf_irreducible_p([7,3], 11, ZZ) == True assert gf_irreducible_p([7,3,1], 11, ZZ) == False setup('GF_IRRED_METHOD', 'rabin') assert gf_irreducible_p([7], 11, ZZ) == True assert gf_irreducible_p([7,3], 11, ZZ) == True assert gf_irreducible_p([7,3,1], 11, ZZ) == False setup('GF_IRRED_METHOD', 'other') raises(KeyError, "gf_irreducible_p([7], 11, ZZ)") setup('GF_IRRED_METHOD') f = [1, 9, 9, 13, 16, 15, 6, 7, 7, 7, 10] g = [1, 7, 16, 7, 15, 13, 13, 11, 16, 10, 9] h = gf_mul(f, g, 17, ZZ) assert gf_irred_p_ben_or(f, 17, ZZ) == True assert gf_irred_p_ben_or(g, 17, ZZ) == True assert gf_irred_p_ben_or(h, 17, ZZ) == False assert gf_irred_p_rabin(f, 17, ZZ) == True assert gf_irred_p_rabin(g, 17, ZZ) == True assert gf_irred_p_rabin(h, 17, ZZ) == False def test_gf_squarefree(): assert gf_sqf_list([], 11, ZZ) == (0, []) assert gf_sqf_list([1], 11, ZZ) == (1, []) assert gf_sqf_list([1,1], 11, ZZ) == (1, [([1, 1], 1)]) assert gf_sqf_p([], 11, ZZ) == True assert gf_sqf_p([1], 11, ZZ) == True assert gf_sqf_p([1,1], 11, ZZ) == True f = gf_from_dict({11: 1, 0: 1}, 11, ZZ) assert gf_sqf_p(f, 11, ZZ) == False assert gf_sqf_list(f, 11, ZZ) == \ (1, [([1, 1], 11)]) f = [1, 5, 8, 4] assert gf_sqf_p(f, 11, ZZ) == False assert gf_sqf_list(f, 11, ZZ) == \ (1, [([1, 1], 1), ([1, 2], 2)]) assert gf_sqf_part(f, 11, ZZ) == [1, 3, 2] f = [1,0,0,2,0,0,2,0,0,1,0] assert gf_sqf_list(f, 3, ZZ) == \ (1, [([1, 0], 1), ([1, 1], 3), ([1, 2], 6)]) def test_gf_berlekamp(): f = gf_from_int_poly([1,-3,1,-3,-1,-3,1], 11) Q = [[1, 0, 0, 0, 0, 0], [3, 5, 8, 8, 6, 5], [3, 6, 6, 1,10, 0], [9, 4,10, 3, 7, 9], [7, 8,10, 0, 0, 8], [8,10, 7, 8,10, 8]] V = [[1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0], [0, 0, 7, 9, 0, 1]] assert gf_Qmatrix(f, 11, ZZ) == Q assert gf_Qbasis(Q, 11, ZZ) == V assert gf_berlekamp(f, 11, ZZ) == \ [[1, 1], [1, 5, 3], [1, 2, 3, 4]] f = [1,0,1,0,10,10,8,2,8] Q = [[1, 0, 0, 0, 0, 0, 0, 0], [2, 1, 7,11,10,12, 5,11], [3, 6, 4, 3, 0, 4, 7, 2], [4, 3, 6, 5, 1, 6, 2, 3], [2,11, 8, 8, 3, 1, 3,11], [6,11, 8, 6, 2, 7,10, 9], [5,11, 7,10, 0,11, 7,12], [3, 3,12, 5, 0,11, 9,12]] V = [[1, 0, 0, 0, 0, 0, 0, 0], [0, 5, 5, 0, 9, 5, 1, 0], [0, 9,11, 9,10,12, 0, 1]] assert gf_Qmatrix(f, 13, ZZ) == Q assert gf_Qbasis(Q, 13, ZZ) == V assert gf_berlekamp(f, 13, ZZ) == \ [[1, 3], [1, 8, 4, 12], [1, 2, 3, 4, 6]] def test_gf_ddf(): f = gf_from_dict({15: 1, 0: -1}, 11, ZZ) g = [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] assert gf_ddf_zassenhaus(f, 11, ZZ) == g assert gf_ddf_shoup(f, 11, ZZ) == g f = gf_from_dict({63: 1, 0: 1}, 2, ZZ) g = [([1, 1], 1), ([1, 1, 1], 2), ([1, 1, 1, 1, 1, 1, 1], 3), ([1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1], 6)] assert gf_ddf_zassenhaus(f, 2, ZZ) == g assert gf_ddf_shoup(f, 2, ZZ) == g f = gf_from_dict({6: 1, 5: -1, 4: 1, 3: 1, 1: -1}, 3, ZZ) g = [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)] assert gf_ddf_zassenhaus(f, 3, ZZ) == g assert gf_ddf_shoup(f, 3, ZZ) == g f = [1, 2, 5, 26, 677, 436, 791, 325, 456, 24, 577] g = [([1, 701], 1), ([1, 110, 559, 532, 694, 151, 110, 70, 735, 122], 9)] assert gf_ddf_zassenhaus(f, 809, ZZ) == g assert gf_ddf_shoup(f, 809, ZZ) == g p = ZZ(nextprime(int((2**15 * pi).evalf()))) f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) g = [([1, 22730, 68144], 2), ([1, 64876, 83977, 10787, 12561, 68608, 52650, 88001, 84356], 4), ([1, 15347, 95022, 84569, 94508, 92335], 5)] assert gf_ddf_zassenhaus(f, p, ZZ) == g assert gf_ddf_shoup(f, p, ZZ) == g def test_gf_edf(): f = [1, 1, 0, 1, 2] g = [[1, 0, 1], [1, 1, 2]] assert gf_edf_zassenhaus(f, 2, 3, ZZ) == g assert gf_edf_shoup(f, 2, 3, ZZ) == g def test_gf_factor(): assert gf_factor([], 11, ZZ) == (0, []) assert gf_factor([1], 11, ZZ) == (1, []) assert gf_factor([1,1], 11, ZZ) == (1, [([1, 1], 1)]) assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1,1], 11, ZZ) == (1, [[1, 1]]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1,1], 11, ZZ) == (1, [[1, 1]]) setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1,1], 11, ZZ) == (1, [[1, 1]]) setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf([], 11, ZZ) == (0, []) assert gf_factor_sqf([1], 11, ZZ) == (1, []) assert gf_factor_sqf([1,1], 11, ZZ) == (1, [[1, 1]]) f, p = [1,0,0,1,0], 2 g = (1, [([1, 0], 1), ([1, 1], 1), ([1, 1, 1], 1)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g g = (1, [[1, 0], [1, 1], [1, 1, 1]]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor_sqf(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(f, p, ZZ) == g f, p = gf_from_int_poly([1,-3,1,-3,-1,-3,1], 11), 11 g = (1, [([1, 1], 1), ([1, 5, 3], 1), ([1, 2, 3, 4], 1)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = [1, 5, 8, 4], 11 g = (1, [([1, 1], 1), ([1, 2], 2)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = [1, 1, 10, 1, 0, 10, 10, 10, 0, 0], 11 g = (1, [([1, 0], 2), ([1, 9, 5], 1), ([1, 3, 0, 8, 5, 2], 1)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = gf_from_dict({32: 1, 0: 1}, 11, ZZ), 11 g = (1, [([1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 10], 1)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = gf_from_dict({32: 8, 0: 5}, 11, ZZ), 11 g = (8, [([1, 3], 1), ([1, 8], 1), ([1, 0, 9], 1), ([1, 2, 2], 1), ([1, 9, 2], 1), ([1, 0, 5, 0, 7], 1), ([1, 0, 6, 0, 7], 1), ([1, 0, 0, 0, 1, 0, 0, 0, 6], 1), ([1, 0, 0, 0, 10, 0, 0, 0, 6], 1)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g f, p = gf_from_dict({63: 8, 0: 5}, 11, ZZ), 11 g = (8, [([1, 7], 1), ([1, 4, 5], 1), ([1, 6, 8, 2], 1), ([1, 9, 9, 2], 1), ([1, 0, 0, 9, 0, 0, 4], 1), ([1, 2, 0, 8, 4, 6, 4], 1), ([1, 2, 3, 8, 0, 6, 4], 1), ([1, 2, 6, 0, 8, 4, 4], 1), ([1, 3, 3, 1, 6, 8, 4], 1), ([1, 5, 6, 0, 8, 6, 4], 1), ([1, 6, 2, 7, 9, 8, 4], 1), ([1, 10, 4, 7, 10, 7, 4], 1), ([1, 10, 10, 1, 4, 9, 4], 1)]) setup('GF_FACTOR_METHOD', 'berlekamp') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g # Gathen polynomials: x**n + x + 1 (mod p > 2**n * pi) p = ZZ(nextprime(int((2**15 * pi).evalf()))) f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) assert gf_sqf_p(f, p, ZZ) == True g = (1, [([1, 22730, 68144], 1), ([1, 81553, 77449, 86810, 4724], 1), ([1, 86276, 56779, 14859, 31575], 1), ([1, 15347, 95022, 84569, 94508, 92335], 1)]) setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g g = (1, [[1, 22730, 68144], [1, 81553, 77449, 86810, 4724], [1, 86276, 56779, 14859, 31575], [1, 15347, 95022, 84569, 94508, 92335]]) setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(f, p, ZZ) == g # Shoup polynomials: f = a_0 x**n + a_1 x**(n-1) + ... + a_n # (mod p > 2**(n-2) * pi), where a_n = a_{n-1}**2 + 1, a_0 = 1 p = ZZ(nextprime(int((2**4 * pi).evalf()))) f = [1, 2, 5, 26, 41, 39, 38] assert gf_sqf_p(f, p, ZZ) == True g = (1, [([1, 44, 26], 1), ([1, 11, 25, 18, 30], 1)]) setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor(f, p, ZZ) == g g = (1, [[1, 44, 26], [1, 11, 25, 18, 30]]) setup('GF_FACTOR_METHOD', 'zassenhaus') assert gf_factor_sqf(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'shoup') assert gf_factor_sqf(f, p, ZZ) == g setup('GF_FACTOR_METHOD', 'other') raises(KeyError, "gf_factor([1,1], 11, ZZ)") setup('GF_FACTOR_METHOD') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/tests/test_densetools.py0000644000175000017500000005400612014170666027204 0ustar georgeskgeorgesk"""Tests for dense recursive polynomials' tools. """ from sympy.polys.densebasic import ( dup_LC, dmp_LC, dup_normal, dmp_normal, dup_from_raw_dict, dmp_from_dict, dmp_convert, dmp_swap, dmp_one_p, ) from sympy.polys.densearith import ( dup_add, dup_mul, dup_exquo, dmp_neg, dmp_sub, dmp_mul_ground, dmp_mul, dmp_sqr, ) from sympy.polys.densetools import ( dup_clear_denoms, dmp_clear_denoms, dup_integrate, dmp_integrate, dmp_integrate_in, dup_diff, dmp_diff, dmp_diff_in, dup_eval, dmp_eval, dmp_eval_in, dmp_eval_tail, dmp_diff_eval_in, dup_trunc, dmp_trunc, dmp_ground_trunc, dup_monic, dmp_ground_monic, dup_content, dmp_ground_content, dup_primitive, dmp_ground_primitive, dup_extract, dmp_ground_extract, dup_real_imag, dup_mirror, dup_scale, dup_shift, dup_transform, dup_compose, dmp_compose, dup_decompose, dmp_lift, dup_sign_variations, dup_revert, dmp_revert, ) from sympy.polys.polyclasses import DMP, ANP from sympy.polys.polyerrors import ( MultivariatePolynomialError, ExactQuotientFailed, NotReversible, DomainError, ) from sympy.polys.specialpolys import ( f_0, f_1, f_2, f_3, f_4, f_5, f_6, dmp_fateman_poly_F_1, dmp_fateman_poly_F_2, dmp_fateman_poly_F_3, ) from sympy.polys.domains import FF, ZZ, QQ, EX from sympy import I from sympy.utilities.pytest import raises def test_dup_integrate(): assert dup_integrate([], 1, QQ) == [] assert dup_integrate([], 2, QQ) == [] assert dup_integrate([QQ(1)], 1, QQ) == [QQ(1),QQ(0)] assert dup_integrate([QQ(1)], 2, QQ) == [QQ(1,2),QQ(0),QQ(0)] assert dup_integrate([QQ(1),QQ(2),QQ(3)], 0, QQ) == \ [QQ(1),QQ(2),QQ(3)] assert dup_integrate([QQ(1),QQ(2),QQ(3)], 1, QQ) == \ [QQ(1,3),QQ(1),QQ(3),QQ(0)] assert dup_integrate([QQ(1),QQ(2),QQ(3)], 2, QQ) == \ [QQ(1,12),QQ(1,3),QQ(3,2),QQ(0),QQ(0)] assert dup_integrate([QQ(1),QQ(2),QQ(3)], 3, QQ) == \ [QQ(1,60),QQ(1,12),QQ(1,2),QQ(0),QQ(0),QQ(0)] assert dup_integrate(dup_from_raw_dict({29: QQ(17)}, QQ), 3, QQ) == \ dup_from_raw_dict({32: QQ(17,29760)}, QQ) assert dup_integrate(dup_from_raw_dict({29: QQ(17), 5: QQ(1,2)}, QQ), 3, QQ) == \ dup_from_raw_dict({32: QQ(17,29760), 8: QQ(1, 672)}, QQ) def test_dmp_integrate(): assert dmp_integrate([[[]]], 1, 2, QQ) == [[[]]] assert dmp_integrate([[[]]], 2, 2, QQ) == [[[]]] assert dmp_integrate([[[QQ(1)]]], 1, 2, QQ) == [[[QQ(1)]],[[]]] assert dmp_integrate([[[QQ(1)]]], 2, 2, QQ) == [[[QQ(1,2)]],[[]],[[]]] assert dmp_integrate([[QQ(1)],[QQ(2)],[QQ(3)]], 0, 1, QQ) == \ [[QQ(1)],[QQ(2)],[QQ(3)]] assert dmp_integrate([[QQ(1)],[QQ(2)],[QQ(3)]], 1, 1, QQ) == \ [[QQ(1,3)],[QQ(1)],[QQ(3)],[]] assert dmp_integrate([[QQ(1)],[QQ(2)],[QQ(3)]], 2, 1, QQ) == \ [[QQ(1,12)],[QQ(1,3)],[QQ(3,2)],[],[]] assert dmp_integrate([[QQ(1)],[QQ(2)],[QQ(3)]], 3, 1, QQ) == \ [[QQ(1,60)],[QQ(1,12)],[QQ(1,2)],[],[],[]] def test_dmp_integrate_in(): f = dmp_convert(f_6, 3, ZZ, QQ) assert dmp_integrate_in(f, 2, 1, 3, QQ) == \ dmp_swap(dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 2, 3, QQ), 0, 1, 3, QQ) assert dmp_integrate_in(f, 3, 1, 3, QQ) == \ dmp_swap(dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 3, 3, QQ), 0, 1, 3, QQ) assert dmp_integrate_in(f, 2, 2, 3, QQ) == \ dmp_swap(dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 2, 3, QQ), 0, 2, 3, QQ) assert dmp_integrate_in(f, 3, 2, 3, QQ) == \ dmp_swap(dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 3, 3, QQ), 0, 2, 3, QQ) def test_dup_diff(): assert dup_diff([], 1, ZZ) == [] assert dup_diff([7], 1, ZZ) == [] assert dup_diff([2,7], 1, ZZ) == [2] assert dup_diff([1,2,1], 1, ZZ) == [2,2] assert dup_diff([1,2,3,4], 1, ZZ) == [3,4,3] assert dup_diff([1,-1,0,0,2], 1, ZZ) == [4,-3,0,0] f = dup_normal([17,34,56,-345,23,76,0,0,12,3,7], ZZ) assert dup_diff(f, 0, ZZ) == f assert dup_diff(f, 1, ZZ) == dup_diff(f, 1, ZZ) assert dup_diff(f, 2, ZZ) == dup_diff(dup_diff(f, 1, ZZ), 1, ZZ) assert dup_diff(f, 3, ZZ) == dup_diff(dup_diff(dup_diff(f, 1, ZZ), 1, ZZ), 1, ZZ) K = FF(3) f = dup_normal([17,34,56,-345,23,76,0,0,12,3,7], K) assert dup_diff(f, 1, K) == dup_normal([2,0,1,0,0,2,0,0,0,0], K) assert dup_diff(f, 2, K) == dup_normal([1,0,0,2,0,0,0], K) assert dup_diff(f, 3, K) == dup_normal([], K) assert dup_diff(f, 0, K) == f assert dup_diff(f, 1, K) == dup_diff(f, 1, K) assert dup_diff(f, 2, K) == dup_diff(dup_diff(f, 1, K), 1, K) assert dup_diff(f, 3, K) == dup_diff(dup_diff(dup_diff(f, 1, K), 1, K), 1, K) def test_dmp_diff(): assert dmp_diff([], 1, 0, ZZ) == [] assert dmp_diff([[]], 1, 1, ZZ) == [[]] assert dmp_diff([[[]]], 1, 2, ZZ) == [[[]]] assert dmp_diff([[[1], [2]]], 1, 2, ZZ) == [[[]]] assert dmp_diff([[[1]], [[]]], 1, 2, ZZ) == [[[1]]] assert dmp_diff([[[3]], [[1]], [[]]], 1, 2, ZZ) == [[[6]], [[1]]] assert dmp_diff([1,-1,0,0,2], 1, 0, ZZ) == \ dup_diff([1,-1,0,0,2], 1, ZZ) assert dmp_diff(f_6, 0, 3, ZZ) == f_6 assert dmp_diff(f_6, 1, 3, ZZ) == dmp_diff(f_6, 1, 3, ZZ) assert dmp_diff(f_6, 2, 3, ZZ) == dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ) assert dmp_diff(f_6, 3, 3, ZZ) == dmp_diff(dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ), 1, 3, ZZ) K = FF(23) F_6 = dmp_normal(f_6, 3, K) assert dmp_diff(F_6, 0, 3, K) == F_6 assert dmp_diff(F_6, 1, 3, K) == dmp_diff(F_6, 1, 3, K) assert dmp_diff(F_6, 2, 3, K) == dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K) assert dmp_diff(F_6, 3, 3, K) == dmp_diff(dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K), 1, 3, K) def test_dmp_diff_in(): assert dmp_diff_in(f_6, 2, 1, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 0, 1, 3, ZZ) assert dmp_diff_in(f_6, 3, 1, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 3, 3, ZZ), 0, 1, 3, ZZ) assert dmp_diff_in(f_6, 2, 2, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 2, 3, ZZ), 0, 2, 3, ZZ) assert dmp_diff_in(f_6, 3, 2, 3, ZZ) == \ dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 3, 3, ZZ), 0, 2, 3, ZZ) def test_dup_eval(): assert dup_eval([], 7, ZZ) == 0 assert dup_eval([1,2], 0, ZZ) == 2 assert dup_eval([1,2,3], 7, ZZ) == 66 def test_dmp_eval(): assert dmp_eval([], 3, 0, ZZ) == 0 assert dmp_eval([[]], 3, 1, ZZ) == [] assert dmp_eval([[[]]], 3, 2, ZZ) == [[]] assert dmp_eval([[1,2]], 0, 1, ZZ) == [1,2] assert dmp_eval([[[1]]], 3, 2, ZZ) == [[1]] assert dmp_eval([[[1, 2]]], 3, 2, ZZ) == [[1, 2]] assert dmp_eval([[3, 2], [1, 2]], 3, 1, ZZ) == [10, 8] assert dmp_eval([[[3, 2]], [[1, 2]]], 3, 2, ZZ) == [[10, 8]] def test_dmp_eval_in(): assert dmp_eval_in(f_6,-2, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ),-2, 3, ZZ) assert dmp_eval_in(f_6, 7, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), 7, 3, ZZ) assert dmp_eval_in(f_6,-2, 2, 3, ZZ) == dmp_swap(dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ),-2, 3, ZZ), 0, 1, 2, ZZ) assert dmp_eval_in(f_6, 7, 2, 3, ZZ) == dmp_swap(dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), 7, 3, ZZ), 0, 1, 2, ZZ) f = [[[45L]], [[]], [[]], [[-9L], [-1L], [], [3L, 0L, 10L, 0L]]] assert dmp_eval_in(f, -2, 2, 2, ZZ) == \ [[45], [], [], [-9, -1, 0, -44]] def test_dmp_eval_tail(): assert dmp_eval_tail([[]], [1], 1, ZZ) == [] assert dmp_eval_tail([[[]]], [1], 2, ZZ) == [[]] assert dmp_eval_tail([[[]]], [1, 2], 2, ZZ) == [] assert dmp_eval_tail(f_0, [], 2, ZZ) == f_0 assert dmp_eval_tail(f_0, [1,-17,8], 2, ZZ) == 84496 assert dmp_eval_tail(f_0, [-17, 8], 2, ZZ) == [-1409, 3, 85902] assert dmp_eval_tail(f_0, [8], 2, ZZ) == [[83, 2], [3], [302, 81, 1]] assert dmp_eval_tail(f_1, [-17, 8], 2, ZZ) == [-136, 15699, 9166, -27144] assert dmp_eval_tail(f_2, [-12, 3], 2, ZZ) == [-1377, 0, -702, -1224, 0, -624] assert dmp_eval_tail(f_3, [-12, 3], 2, ZZ) == [144, 82, -5181, -28872, -14868, -540] assert dmp_eval_tail(f_4, [25, -1], 2, ZZ) == [152587890625, 9765625, -59605407714843750, -3839159765625, -1562475, 9536712644531250, 610349546750, -4, 24414375000, 1562520] assert dmp_eval_tail(f_5, [25, -1], 2, ZZ) == [-1, -78, -2028, -17576] assert dmp_eval_tail(f_6, [0, 2, 4], 3, ZZ) == [5040, 0, 0, 4480] def test_dmp_diff_eval_in(): assert dmp_diff_eval_in(f_6, 2, 7, 1, 3, ZZ) == \ dmp_eval(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 7, 3, ZZ) def test_dup_revert(): f = [-QQ(1,720),QQ(0),QQ(1,24),QQ(0),-QQ(1,2),QQ(0),QQ(1)] g = [QQ(61,720),QQ(0),QQ(5,24),QQ(0), QQ(1,2),QQ(0),QQ(1)] assert dup_revert(f, 8, QQ) == g raises(NotReversible, "dup_revert([QQ(1), QQ(0)], 3, QQ)") def test_dmp_revert(): f = [-QQ(1,720),QQ(0),QQ(1,24),QQ(0),-QQ(1,2),QQ(0),QQ(1)] g = [QQ(61,720),QQ(0),QQ(5,24),QQ(0), QQ(1,2),QQ(0),QQ(1)] assert dmp_revert(f, 8, 0, QQ) == g raises(MultivariatePolynomialError, "dmp_revert([[1]], 2, 1, QQ)") def test_dup_trunc(): assert dup_trunc([1,2,3,4,5,6], ZZ(3), ZZ) == [1, -1, 0, 1, -1, 0] assert dup_trunc([6,5,4,3,2,1], ZZ(3), ZZ) == [-1, 1, 0, -1, 1] def test_dmp_trunc(): assert dmp_trunc([[]], [1,2], 2, ZZ) == [[]] assert dmp_trunc([[1,2], [1,4,1], [1]], [1,2], 1, ZZ) == [[-3], [1]] def test_dmp_ground_trunc(): assert dmp_ground_trunc(f_0, ZZ(3), 2, ZZ) == \ dmp_normal([[[1, -1, 0], [-1]], [[]], [[1, -1, 0], [1, -1, 1], [1]]], 2, ZZ) def test_dup_monic(): assert dup_monic([3,6,9], ZZ) == [1,2,3] raises(ExactQuotientFailed, 'dup_monic([3,4,5], ZZ)') assert dup_monic([], QQ) == [] assert dup_monic([QQ(1)], QQ) == [QQ(1)] assert dup_monic([QQ(7),QQ(1),QQ(21)], QQ) == [QQ(1),QQ(1,7),QQ(3)] def test_dmp_ground_monic(): assert dmp_ground_monic([[3],[6],[9]], 1, ZZ) == [[1],[2],[3]] raises(ExactQuotientFailed, 'dmp_ground_monic([[3],[4],[5]], 1, ZZ)') assert dmp_ground_monic([[]], 1, QQ) == [[]] assert dmp_ground_monic([[QQ(1)]], 1, QQ) == [[QQ(1)]] assert dmp_ground_monic([[QQ(7)],[QQ(1)],[QQ(21)]], 1, QQ) == [[QQ(1)],[QQ(1,7)],[QQ(3)]] def test_dup_content(): assert dup_content([], ZZ) == ZZ(0) assert dup_content([1], ZZ) == ZZ(1) assert dup_content([-1], ZZ) == ZZ(1) assert dup_content([1,1], ZZ) == ZZ(1) assert dup_content([2,2], ZZ) == ZZ(2) assert dup_content([1,2,1], ZZ) == ZZ(1) assert dup_content([2,4,2], ZZ) == ZZ(2) assert dup_content([QQ(2,3),QQ(4,5)], QQ) == QQ(1) def test_dmp_ground_content(): assert dmp_ground_content([[]], 1, ZZ) == ZZ(0) assert dmp_ground_content([[]], 1, QQ) == QQ(0) assert dmp_ground_content([[1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[-1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[1],[1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[2],[2]], 1, ZZ) == ZZ(2) assert dmp_ground_content([[1],[2],[1]], 1, ZZ) == ZZ(1) assert dmp_ground_content([[2],[4],[2]], 1, ZZ) == ZZ(2) assert dmp_ground_content([[QQ(2,3)],[QQ(4,5)]], 1, QQ) == QQ(1) assert dmp_ground_content(f_0, 2, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == ZZ(2) assert dmp_ground_content(f_1, 2, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == ZZ(3) assert dmp_ground_content(f_2, 2, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == ZZ(4) assert dmp_ground_content(f_3, 2, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == ZZ(5) assert dmp_ground_content(f_4, 2, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == ZZ(6) assert dmp_ground_content(f_5, 2, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == ZZ(7) assert dmp_ground_content(f_6, 3, ZZ) == ZZ(1) assert dmp_ground_content(dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == ZZ(8) def test_dup_primitive(): assert dup_primitive([], ZZ) == (ZZ(0), []) assert dup_primitive([ZZ(1)], ZZ) == (ZZ(1), [ZZ(1)]) assert dup_primitive([ZZ(1),ZZ(1)], ZZ) == (ZZ(1), [ZZ(1),ZZ(1)]) assert dup_primitive([ZZ(2),ZZ(2)], ZZ) == (ZZ(2), [ZZ(1),ZZ(1)]) assert dup_primitive([ZZ(1),ZZ(2),ZZ(1)], ZZ) == (ZZ(1), [ZZ(1),ZZ(2),ZZ(1)]) assert dup_primitive([ZZ(2),ZZ(4),ZZ(2)], ZZ) == (ZZ(2), [ZZ(1),ZZ(2),ZZ(1)]) assert dup_primitive([], QQ) == (QQ(0), []) assert dup_primitive([QQ(1)], QQ) == (QQ(1), [QQ(1)]) assert dup_primitive([QQ(1),QQ(1)], QQ) == (QQ(1), [QQ(1),QQ(1)]) assert dup_primitive([QQ(2),QQ(2)], QQ) == (QQ(1), [QQ(2),QQ(2)]) assert dup_primitive([QQ(1),QQ(2),QQ(1)], QQ) == (QQ(1), [QQ(1),QQ(2),QQ(1)]) assert dup_primitive([QQ(2),QQ(4),QQ(2)], QQ) == (QQ(1), [QQ(2),QQ(4),QQ(2)]) assert dup_primitive([QQ(2,3),QQ(4,5)], QQ) == (QQ(1), [QQ(2,3),QQ(4,5)]) def test_dmp_ground_primitive(): assert dmp_ground_primitive([[]], 1, ZZ) == (ZZ(0), [[]]) assert dmp_ground_primitive(f_0, 2, ZZ) == (ZZ(1), f_0) assert dmp_ground_primitive(dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == (ZZ(2), f_0) assert dmp_ground_primitive(f_1, 2, ZZ) == (ZZ(1), f_1) assert dmp_ground_primitive(dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == (ZZ(3), f_1) assert dmp_ground_primitive(f_2, 2, ZZ) == (ZZ(1), f_2) assert dmp_ground_primitive(dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == (ZZ(4), f_2) assert dmp_ground_primitive(f_3, 2, ZZ) == (ZZ(1), f_3) assert dmp_ground_primitive(dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == (ZZ(5), f_3) assert dmp_ground_primitive(f_4, 2, ZZ) == (ZZ(1), f_4) assert dmp_ground_primitive(dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == (ZZ(6), f_4) assert dmp_ground_primitive(f_5, 2, ZZ) == (ZZ(1), f_5) assert dmp_ground_primitive(dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == (ZZ(7), f_5) assert dmp_ground_primitive(f_6, 3, ZZ) == (ZZ(1), f_6) assert dmp_ground_primitive(dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == (ZZ(8), f_6) assert dmp_ground_primitive([[ZZ(2)]], 1, ZZ) == (ZZ(2), [[ZZ(1)]]) assert dmp_ground_primitive([[QQ(2)]], 1, QQ) == (QQ(1), [[QQ(2)]]) def test_dup_extract(): f = dup_normal([2930944, 0, 2198208, 0, 549552, 0, 45796], ZZ) g = dup_normal([17585664, 0, 8792832, 0, 1099104, 0], ZZ) F = dup_normal([64, 0, 48, 0, 12, 0, 1], ZZ) G = dup_normal([384, 0, 192, 0, 24, 0], ZZ) assert dup_extract(f, g, ZZ) == (45796, F, G) def test_dmp_ground_extract(): f = dmp_normal([[2930944], [], [2198208], [], [549552], [], [45796]], 1, ZZ) g = dmp_normal([[17585664], [], [8792832], [], [1099104], []], 1, ZZ) F = dmp_normal([[64], [], [48], [], [12], [], [1]], 1, ZZ) G = dmp_normal([[384], [], [192], [], [24], []], 1, ZZ) assert dmp_ground_extract(f, g, 1, ZZ) == (45796, F, G) def test_dup_real_imag(): assert dup_real_imag([], ZZ) == ([[]], [[]]) assert dup_real_imag([1], ZZ) == ([[1]], [[]]) assert dup_real_imag([1,1], ZZ) == ([[1], [1]], [[1,0]]) assert dup_real_imag([1,2], ZZ) == ([[1], [2]], [[1,0]]) assert dup_real_imag([1,2,3], ZZ) == ([[1], [2], [-1,0,3]], [[2,0], [2,0]]) raises(DomainError, "dup_real_imag([EX(1), EX(2)], EX)") def test_dup_mirror(): assert dup_mirror([], ZZ) == [] assert dup_mirror([1], ZZ) == [1] assert dup_mirror([1,2,3,4,5], ZZ) == [1,-2,3,-4,5] assert dup_mirror([1,2,3,4,5,6], ZZ) == [-1,2,-3,4,-5,6] def test_dup_scale(): assert dup_scale([], -1, ZZ) == [] assert dup_scale([1], -1, ZZ) == [1] assert dup_scale([1,2,3,4,5], -1, ZZ) == [1,-2,3,-4,5] assert dup_scale([1,2,3,4,5], -7, ZZ) == [2401,-686,147,-28,5] def test_dup_shift(): assert dup_shift([], 1, ZZ) == [] assert dup_shift([1], 1, ZZ) == [1] assert dup_shift([1,2,3,4,5], 1, ZZ) == [1,6,15,20,15] assert dup_shift([1,2,3,4,5], 7, ZZ) == [1,30,339,1712,3267] def test_dup_transform(): assert dup_transform([], [], [1,1], ZZ) == [] assert dup_transform([], [1], [1,1], ZZ) == [] assert dup_transform([], [1,2], [1,1], ZZ) == [] assert dup_transform([6,-5,4,-3,17], [1,-3,4], [2,-3], ZZ) == \ [6,-82,541,-2205,6277,-12723,17191,-13603,4773] def test_dup_compose(): assert dup_compose([], [], ZZ) == [] assert dup_compose([], [1], ZZ) == [] assert dup_compose([], [1,2], ZZ) == [] assert dup_compose([1], [], ZZ) == [1] assert dup_compose([1,2,0], [], ZZ) == [] assert dup_compose([1,2,1], [], ZZ) == [1] assert dup_compose([1,2,1], [1], ZZ) == [4] assert dup_compose([1,2,1], [7], ZZ) == [64] assert dup_compose([1,2,1], [1,-1], ZZ) == [1,0,0] assert dup_compose([1,2,1], [1, 1], ZZ) == [1,4,4] assert dup_compose([1,2,1], [1, 2, 1], ZZ) == [1,4,8,8,4] def test_dmp_compose(): assert dmp_compose([1,2,1], [1,2,1], 0, ZZ) == [1,4,8,8,4] assert dmp_compose([[[]]], [[[]]], 2, ZZ) == [[[]]] assert dmp_compose([[[]]], [[[1]]], 2, ZZ) == [[[]]] assert dmp_compose([[[]]], [[[1]],[[2]]], 2, ZZ) == [[[]]] assert dmp_compose([[[1]]], [], 2, ZZ) == [[[1]]] assert dmp_compose([[1],[2],[ ]], [[]], 1, ZZ) == [[]] assert dmp_compose([[1],[2],[1]], [[]], 1, ZZ) == [[1]] assert dmp_compose([[1],[2],[1]], [[1]], 1, ZZ) == [[4]] assert dmp_compose([[1],[2],[1]], [[7]], 1, ZZ) == [[64]] assert dmp_compose([[1],[2],[1]], [[1],[-1]], 1, ZZ) == [[1],[ ],[ ]] assert dmp_compose([[1],[2],[1]], [[1],[ 1]], 1, ZZ) == [[1],[4],[4]] assert dmp_compose([[1],[2],[1]], [[1], [2], [1]], 1, ZZ) == [[1],[4],[8],[8],[4]] def test_dup_decompose(): assert dup_decompose([1], ZZ) == [[1]] assert dup_decompose([1,0], ZZ) == [[1,0]] assert dup_decompose([1,0,0,0], ZZ) == [[1,0,0,0]] assert dup_decompose([1,0,0,0,0], ZZ) == [[1,0,0], [1,0,0]] assert dup_decompose([1,0,0,0,0,0,0], ZZ) == [[1,0,0,0], [1,0,0]] assert dup_decompose([7,0,0,0,1], ZZ) == [[7,0,1], [1,0,0]] assert dup_decompose([4,0,3,0,2], ZZ) == [[4,3,2], [1,0,0]] f = [1,0,20,0,150,0,500,0,625,-2,0,-10,9] assert dup_decompose(f, ZZ) == [[1,0,0,-2,9], [1,0,5,0]] f = [2,0,40,0,300,0,1000,0,1250,-4,0,-20,18] assert dup_decompose(f, ZZ) == [[2,0,0,-4,18], [1,0,5,0]] f = [1,0,20,-8,150,-120,524,-600,865,-1034,600,-170,29] assert dup_decompose(f, ZZ) == [[1,-8,24,-34,29], [1,0,5,0]] f = [DMP([6,0,-42], ZZ), DMP([48,0,96], ZZ), DMP([144,648,288], ZZ), DMP([624,864,384], ZZ), DMP([108,312,432,192], ZZ)] assert dup_decompose(f, ZZ['a']) == [f] def test_dmp_lift(): q = [QQ(1,1),QQ(0,1),QQ(1,1)] f = [ANP([QQ(1,1)], q, QQ), ANP([], q, QQ), ANP([], q, QQ), ANP([QQ(1,1),QQ(0,1)], q, QQ), ANP([QQ(17,1),QQ(0,1)], q, QQ)] assert dmp_lift(f, 0, QQ.algebraic_field(I)) == \ [QQ(1),QQ(0),QQ(0),QQ(0),QQ(0),QQ(0),QQ(2),QQ(0),QQ(578), QQ(0),QQ(0),QQ(0),QQ(1),QQ(0),QQ(-578),QQ(0),QQ(83521)] raises(DomainError, "dmp_lift([EX(1), EX(2)], 0, EX)") def test_dup_sign_variations(): assert dup_sign_variations([], ZZ) == 0 assert dup_sign_variations([1,0], ZZ) == 0 assert dup_sign_variations([1,0,2], ZZ) == 0 assert dup_sign_variations([1,0,3,0], ZZ) == 0 assert dup_sign_variations([1,0,4,0,5], ZZ) == 0 assert dup_sign_variations([-1,0,2], ZZ) == 1 assert dup_sign_variations([-1,0,3,0], ZZ) == 1 assert dup_sign_variations([-1,0,4,0,5], ZZ) == 1 assert dup_sign_variations([-1,-4,-5], ZZ) == 0 assert dup_sign_variations([ 1,-4,-5], ZZ) == 1 assert dup_sign_variations([ 1, 4,-5], ZZ) == 1 assert dup_sign_variations([ 1,-4, 5], ZZ) == 2 assert dup_sign_variations([-1, 4,-5], ZZ) == 2 assert dup_sign_variations([-1, 4, 5], ZZ) == 1 assert dup_sign_variations([-1,-4, 5], ZZ) == 1 assert dup_sign_variations([ 1, 4, 5], ZZ) == 0 assert dup_sign_variations([-1,0,-4,0,-5], ZZ) == 0 assert dup_sign_variations([ 1,0,-4,0,-5], ZZ) == 1 assert dup_sign_variations([ 1,0, 4,0,-5], ZZ) == 1 assert dup_sign_variations([ 1,0,-4,0, 5], ZZ) == 2 assert dup_sign_variations([-1,0, 4,0,-5], ZZ) == 2 assert dup_sign_variations([-1,0, 4,0, 5], ZZ) == 1 assert dup_sign_variations([-1,0,-4,0, 5], ZZ) == 1 assert dup_sign_variations([ 1,0, 4,0, 5], ZZ) == 0 def test_dup_clear_denoms(): assert dup_clear_denoms([], QQ, ZZ) == (ZZ(1), []) assert dup_clear_denoms([QQ(1)], QQ, ZZ) == (ZZ(1), [QQ(1)]) assert dup_clear_denoms([QQ(7)], QQ, ZZ) == (ZZ(1), [QQ(7)]) assert dup_clear_denoms([QQ(7,3)], QQ) == (ZZ(3), [QQ(7)]) assert dup_clear_denoms([QQ(7,3)], QQ, ZZ) == (ZZ(3), [QQ(7)]) assert dup_clear_denoms([QQ(3),QQ(1),QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3),QQ(1),QQ(0)]) assert dup_clear_denoms([QQ(1),QQ(1,2),QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2),QQ(1),QQ(0)]) assert dup_clear_denoms([QQ(3),QQ(1),QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3),ZZ(1),ZZ(0)]) assert dup_clear_denoms([QQ(1),QQ(1,2),QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2),ZZ(1),ZZ(0)]) raises(DomainError, "dup_clear_denoms([EX(7)], EX)") def test_dmp_clear_denoms(): assert dmp_clear_denoms([[]], 1, QQ, ZZ) == (ZZ(1), [[]]) assert dmp_clear_denoms([[QQ(1)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(1)]]) assert dmp_clear_denoms([[QQ(7)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(7)]]) assert dmp_clear_denoms([[QQ(7,3)]], 1, QQ) == (ZZ(3), [[QQ(7)]]) assert dmp_clear_denoms([[QQ(7,3)]], 1, QQ, ZZ) == (ZZ(3), [[QQ(7)]]) assert dmp_clear_denoms([[QQ(3)],[QQ(1)],[]], 1, QQ, ZZ) == (ZZ(1), [[QQ(3)],[QQ(1)],[]]) assert dmp_clear_denoms([[QQ(1)],[QQ(1,2)],[]], 1, QQ, ZZ) == (ZZ(2), [[QQ(2)],[QQ(1)],[]]) assert dmp_clear_denoms([QQ(3),QQ(1),QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3),ZZ(1),ZZ(0)]) assert dmp_clear_denoms([QQ(1),QQ(1,2),QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2),ZZ(1),ZZ(0)]) assert dmp_clear_denoms([[QQ(3)],[QQ(1)],[]], 1, QQ, ZZ, convert=True) == (ZZ(1), [[QQ(3)],[QQ(1)],[]]) assert dmp_clear_denoms([[QQ(1)],[QQ(1,2)],[]], 1, QQ, ZZ, convert=True) == (ZZ(2), [[QQ(2)],[QQ(1)],[]]) raises(DomainError, "dmp_clear_denoms([[EX(7)]], 1, EX)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/sparsearith.py0000644000175000017500000000364612014170666025155 0ustar georgeskgeorgesk"""Arithmetics of sparse polynomials in `K[x]` or `K[X]`. """ def sup_add_ground(f, coeff, K): pass def smp_add_ground(f, coeff, u, ord, K): pass def sup_sub_ground(f, coeff, K): pass def smp_sub_ground(f, coeff, u, ord, K): pass def sup_mul_ground(f, coeff, K): pass def smp_mul_ground(f, coeff, u, ord, K): pass def sup_quo_ground(f, coeff, K): pass def smp_quo_ground(f, coeff, u, ord, K): pass def sup_exquo_ground(f, coeff, K): pass def smp_exquo_ground(f, coeff, u, ord, K): pass def sup_add_term(f, term, K): pass def smp_add_term(f, term, u, ord, K): pass def sup_sub_term(f, term, K): pass def smp_sub_term(f, term, u, ord, K): pass def sup_mul_term(f, term, K): pass def smp_mul_term(f, term, u, ord, K): pass def sup_abs(f, K): pass def smp_abs(f, u, ord, K): pass def sup_neg(f, K): pass def smp_neg(f, u, ord, K): pass def sup_add(f, g, K): pass def smp_add(f, g, u, ord, K): pass def sup_sub(f, g, K): pass def smp_sub(f, g, u, ord, K): pass def sup_mul(f, g, K): pass def smp_mul(f, g, u, ord, K): pass def sup_sqr(f, K): pass def smp_sqr(f, u, ord, K): pass def sup_pow(f, n, K): pass def smp_pow(f, n, u, ord, K): pass def sup_pdiv(f, g, K): pass def smp_pdiv(f, g, u, ord, K): pass def sup_prem(f, g, K): pass def smp_prem(f, g, u, ord, K): pass def sup_pquo(f, g, K): pass def smp_pquo(f, g, u, ord, K): pass def sup_pexquo(f, g, K): pass def smp_pexquo(f, g, u, ord, K): pass def sup_div(f, g, K): pass def smp_div(f, g, u, ord, K): pass def sup_rem(f, g, K): pass def smp_rem(f, g, u, ord, K): pass def sup_quo(f, g, K): pass def smp_quo(f, g, u, ord, K): pass def sup_exquo(f, g, K): pass def smp_exquo(f, g, u, ord, K): pass def sup_reduced(f, G, K): pass def smp_reduced(f, G, u, ord, K): pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/polys/__init__.py0000644000175000017500000000356112014170666024363 0ustar georgeskgeorgesk"""Polynomial manipulation algorithms and algebraic objects. """ from polytools import ( Poly, PurePoly, poly, poly_from_expr, parallel_poly_from_expr, degree, degree_list, LC, LM, LT, pdiv, prem, pquo, pexquo, div, rem, quo, exquo, half_gcdex, gcdex, invert, subresultants, resultant, discriminant, terms_gcd, cofactors, gcd, gcd_list, lcm, lcm_list, trunc, monic, content, primitive, compose, decompose, sturm, gff_list, gff, sqf_norm, sqf_part, sqf_list, sqf, factor_list, factor, intervals, refine_root, count_roots, real_roots, nroots, ground_roots, nth_power_roots_poly, cancel, reduced, groebner, ) from polyfuncs import ( symmetrize, horner, interpolate, viete, ) from rationaltools import ( together, ) from polyerrors import ( OperationNotSupported, ExactQuotientFailed, ComputationFailed, UnificationFailed, GeneratorsNeeded, RefinementFailed, PolynomialError, CoercionFailed, NotInvertible, NotReversible, NotAlgebraic, DomainError, ) from numberfields import ( minimal_polynomial, minpoly, primitive_element, primelt, field_isomorphism, to_number_field, AlgebraicNumber, isolate, ) from monomialtools import ( Monomial, monomials, monomial_count, ) from rootoftools import ( RootOf, RootSum, ) from polyroots import ( roots, ) from domains import ( FF, GF, ZZ, QQ, RR, EX, ) from constructor import ( construct_domain, ) from specialpolys import ( swinnerton_dyer_poly, interpolating_poly, cyclotomic_poly, symmetric_poly, random_poly, ) from orthopolys import ( chebyshevt_poly, chebyshevu_poly, hermite_poly, legendre_poly, laguerre_poly, ) from partfrac import ( apart, ) from polyoptions import Options import polycontext as ctx wxgeometrie-0.133.2.orig/wxgeometrie/sympy/LICENSE0000644000175000017500000000277512014170666022117 0ustar georgeskgeorgeskCopyright (c) 2006, 2007, 2008, 2009, 2010, 2011 SymPy Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: a. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. b. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. c. Neither the name of the SymPy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. wxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/0000755000175000017500000000000012014170666022411 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/index_methods.py0000644000175000017500000003513112014170666025620 0ustar georgeskgeorgesk"""Module with functions operating on IndexedBase, Indexed and Idx objects - Check shape conformance - Determine indices in resulting expression etc. Methods in this module could be implemented by calling methods on Expr objects instead. When things stabilize this could be a useful refactoring. """ from sympy.tensor.indexed import Idx, IndexedBase, Indexed from sympy.functions import exp from sympy.core import C from sympy.core.compatibility import reduce class IndexConformanceException(Exception): pass def _remove_repeated(inds): """Removes repeated objects from sequences Returns a set of the unique objects and a tuple of all that have been removed. >>> from sympy.tensor.index_methods import _remove_repeated >>> l1 = [1, 2, 3, 2] >>> _remove_repeated(l1) (set([1, 3]), (2,)) """ sum_index = {} for i in inds: if i in sum_index: sum_index[i] += 1 else: sum_index[i] = 0 inds = filter(lambda x: not sum_index[x], inds) return set(inds), tuple([ i for i in sum_index if sum_index[i] ]) def _get_indices_Mul(expr, return_dummies=False): """Determine the outer indices of a Mul object. >>> from sympy.tensor.index_methods import _get_indices_Mul >>> from sympy.tensor.indexed import IndexedBase, Idx >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> x = IndexedBase('x') >>> y = IndexedBase('y') >>> _get_indices_Mul(x[i, k]*y[j, k]) (set([i, j]), {}) >>> _get_indices_Mul(x[i, k]*y[j, k], return_dummies=True) (set([i, j]), {}, (k,)) """ junk, factors = expr.as_coeff_mul() inds = map(get_indices, factors) inds, syms = zip(*inds) inds = map(list, inds) inds = reduce(lambda x, y: x + y, inds) inds, dummies = _remove_repeated(inds) symmetry = {} for s in syms: for pair in s: if pair in symmetry: symmetry[pair] *= s[pair] else: symmetry[pair] = s[pair] if return_dummies: return inds, symmetry, dummies else: return inds, symmetry def _get_indices_Pow(expr): """Determine outer indices of a power or an exponential. A power is considered a universal function, so that the indices of a Pow is just the collection of indices present in the expression. This may be viewed as a bit inconsistent in the special case: x[i]**2 = x[i]*x[i] (1) The above expression could have been interpreted as the contraction of x[i] with itself, but we choose instead to interpret it as a function lambda y: y**2 applied to each element of x (a universal function in numpy terms). In order to allow an interpretation of (1) as a contraction, we need contravariant and covariant Idx subclasses. (FIXME: this is not yet implemented) Expressions in the base or exponent are subject to contraction as usual, but an index that is present in the exponent, will not be considered contractable with its own base. Note however, that indices in the same exponent can be contracted with each other. >>> from sympy.tensor.index_methods import _get_indices_Pow >>> from sympy import Pow, exp, IndexedBase, Idx >>> A = IndexedBase('A') >>> x = IndexedBase('x') >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> _get_indices_Pow(exp(A[i, j]*x[j])) (set([i]), {}) >>> _get_indices_Pow(Pow(x[i], x[i])) (set([i]), {}) >>> _get_indices_Pow(Pow(A[i, j]*x[j], x[i])) (set([i]), {}) """ base, exp = expr.as_base_exp() binds, bsyms = get_indices(base) einds, esyms = get_indices(exp) inds = binds | einds # FIXME: symmetries from power needs to check special cases, else nothing symmetries = {} return inds, symmetries def _get_indices_Add(expr): """Determine outer indices of an Add object. In a sum, each term must have the same set of outer indices. A valid expression could be x(i)*y(j) - x(j)*y(i) But we do not allow expressions like: x(i)*y(j) - z(j)*z(j) FIXME: Add support for Numpy broadcasting >>> from sympy.tensor.index_methods import _get_indices_Add >>> from sympy.tensor.indexed import IndexedBase, Idx >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> x = IndexedBase('x') >>> y = IndexedBase('y') >>> _get_indices_Add(x[i] + x[k]*y[i, k]) (set([i]), {}) """ inds = map(get_indices, expr.args) inds, syms = zip(*inds) # allow broadcast of scalars non_scalars = filter(lambda x: x != set(), inds) if not non_scalars: return set(), {} if not all(map(lambda x: x == non_scalars[0], non_scalars[1:])): raise IndexConformanceException("Indices are not consistent: %s"%expr) if not reduce(lambda x, y: x!=y or y, syms): symmetries = syms[0] else: # FIXME: search for symmetries symmetries = {} return non_scalars[0], symmetries def get_indices(expr): """Determine the outer indices of expression ``expr`` By *outer* we mean indices that are not summation indices. Returns a set and a dict. The set contains outer indices and the dict contains information about index symmetries. :Examples: >>> from sympy.tensor.index_methods import get_indices >>> from sympy import symbols >>> from sympy.tensor import IndexedBase, Idx >>> x, y, A = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, a, z = symbols('i j a z', integer=True) The indices of the total expression is determined, Repeated indices imply a summation, for instance the trace of a matrix A: >>> get_indices(A[i, i]) (set(), {}) In the case of many terms, the terms are required to have identical outer indices. Else an IndexConformanceException is raised. >>> get_indices(x[i] + A[i, j]*y[j]) (set([i]), {}) :Exceptions: An IndexConformanceException means that the terms ar not compatible, e.g. >>> get_indices(x[i] + y[j]) #doctest: +SKIP (...) IndexConformanceException: Indices are not consistent: x(i) + y(j) .. warning:: The concept of *outer* indices applies recursively, starting on the deepest level. This implies that dummies inside parenthesis are assumed to be summed first, so that the following expression is handled gracefully: >>> get_indices((x[i] + A[i, j]*y[j])*x[j]) (set([i, j]), {}) This is correct and may appear convenient, but you need to be careful with this as Sympy wil happily .expand() the product, if requested. The resulting expression would mix the outer ``j`` with the dummies inside the parenthesis, which makes it a different expression. To be on the safe side, it is best to avoid such ambiguities by using unique indices for all contractions that should be held separate. """ # We call ourself recursively to determine indices of sub expressions. # break recursion if isinstance(expr, Indexed): c = expr.indices inds, dummies = _remove_repeated(c) return inds, {} elif expr is None: return set(), {} elif expr.is_Atom: return set(), {} elif isinstance(expr, Idx): return set([expr]), {} # recurse via specialized functions else: if expr.is_Mul: return _get_indices_Mul(expr) elif expr.is_Add: return _get_indices_Add(expr) elif expr.is_Pow or isinstance(expr, exp): return _get_indices_Pow(expr) elif isinstance(expr, C.Piecewise): # FIXME: No support for Piecewise yet return set(), {} elif isinstance(expr, C.Function): # Support ufunc like behaviour by returning indices from arguments. # Functions do not interpret repeated indices across argumnts # as summation ind0 = set() for arg in expr.args: ind, sym = get_indices(arg) ind0 |= ind return ind0, sym # this test is expensive, so it should be at the end elif not expr.has(Indexed): return set(), {} raise NotImplementedError( "FIXME: No specialized handling of type %s"%type(expr)) def get_contraction_structure(expr): """Determine dummy indices of ``expr`` and describe it's structure By *dummy* we mean indices that are summation indices. The stucture of the expression is determined and described as follows: 1) A conforming summation of Indexed objects is described with a dict where the keys are summation indices and the corresponding values are sets containing all terms for which the summation applies. All Add objects in the Sympy expression tree are described like this. 2) For all nodes in the Sympy expression tree that are *not* of type Add, the following applies: If a node discovers contractions in one of it's arguments, the node itself will be stored as a key in the dict. For that key, the corresponding value is a list of dicts, each of which is the result of a recursive call to get_contraction_structure(). The list contains only dicts for the non-trivial deeper contractions, ommitting dicts with None as the one and only key. .. Note:: The presence of expressions among the dictinary keys indicates multiple levels of index contractions. A nested dict displays nested contractions and may itself contain dicts from a deeper level. In practical calculations the summation in the deepest nested level must be calculated first so that the outer expression can access the resulting indexed object. :Examples: >>> from sympy.tensor.index_methods import get_contraction_structure >>> from sympy import symbols >>> from sympy.tensor import IndexedBase, Idx >>> x, y, A = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, k, l = map(Idx, ['i', 'j', 'k', 'l']) >>> get_contraction_structure(x[i]*y[i] + A[j, j]) {(i,): set([x[i]*y[i]]), (j,): set([A[j, j]])} >>> get_contraction_structure(x[i]*y[j]) {None: set([x[i]*y[j]])} A multiplication of contracted factors results in nested dicts representing the internal contractions. >>> d = get_contraction_structure(x[i, i]*y[j, j]) >>> sorted(d.keys()) [None, x[i, i]*y[j, j]] >>> d[None] # Note that the product has no contractions set([x[i, i]*y[j, j]]) >>> sorted(d[x[i, i]*y[j, j]]) # factors are contracted ``first'' [{(i,): set([x[i, i]])}, {(j,): set([y[j, j]])}] A parenthesized Add object is also returned as a nested dictionary. The term containing the parenthesis is a Mul with a contraction among the arguments, so it will be found as a key in the result. It stores the dictionary resulting from a recursive call on the Add expression. >>> d = get_contraction_structure(x[i]*(y[i] + A[i, j]*x[j])) >>> sorted(d.keys()) [(i,), x[i]*(y[i] + A[i, j]*x[j])] >>> d[(i,)] set([x[i]*(y[i] + A[i, j]*x[j])]) >>> d[x[i]*(A[i, j]*x[j] + y[i])] [{None: set([y[i]]), (j,): set([A[i, j]*x[j]])}] Powers with contractions in either base or exponent will also be found as keys in the dictionary, mapping to a list of results from recursive calls: >>> d = get_contraction_structure(A[j, j]**A[i, i]) >>> d[None] set([A[j, j]**A[i, i]]) >>> nested_contractions = d[A[j, j]**A[i, i]] >>> nested_contractions[0] {(j,): set([A[j, j]])} >>> nested_contractions[1] {(i,): set([A[i, i]])} The description of the contraction structure may appear complicated when represented with a string in the above examples, but it is easy to iterate over: >>> from sympy import Expr >>> for key in d: ... if isinstance(key, Expr): ... continue ... for term in d[key]: ... if term in d: ... # treat deepest contraction first ... pass ... # treat outermost contactions here """ # We call ourself recursively to inspect sub expressions. if isinstance(expr, Indexed): junk, key = _remove_repeated(expr.indices) return {key or None: set([expr])} elif expr.is_Atom: return {None: set([expr])} elif expr.is_Mul: junk, junk, key = _get_indices_Mul(expr, return_dummies=True) result = {key or None: set([expr])} # recurse on every factor nested = [] for fac in expr.args: facd = get_contraction_structure(fac) if not (None in facd and len(facd) == 1): nested.append(facd) if nested: result[expr] = nested return result elif expr.is_Pow or isinstance(expr, exp): # recurse in base and exp separately. If either has internal # contractions we must include ourselves as a key in the returned dict b, e = expr.as_base_exp() dbase = get_contraction_structure(b) dexp = get_contraction_structure(e) dicts = [] for d in dbase, dexp: if not (None in d and len(d) == 1): dicts.append(d) result = {None: set([expr])} if dicts: result[expr] = dicts return result elif expr.is_Add: # Note: we just collect all terms with identical summation indices, We # do nothing to identify equivalent terms here, as this would require # substitutions or pattern matching in expressions of unknown # complexity. result = {} for term in expr.args: # recurse on every term d = get_contraction_structure(term) for key in d: if key in result: result[key] |= d[key] else: result[key] = d[key] return result elif isinstance(expr, C.Piecewise): # FIXME: No support for Piecewise yet return {None: expr} elif isinstance(expr, C.Function): # Collect non-trivial contraction structures in each argument # We do not report repeated indices in separate arguments as a # contraction deeplist = [] for arg in expr.args: deep = get_contraction_structure(arg) if not (None in deep and len(deep) == 1): deeplist.append(deep) d = {None: set([expr])} if deeplist: d[expr] = deeplist return d # this test is expensive, so it should be at the end elif not expr.has(Indexed): return {None: set([expr])} raise NotImplementedError( "FIXME: No specialized handling of type %s"%type(expr)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/indexed.py0000644000175000017500000003234412014170666024411 0ustar georgeskgeorgesk"""Module that defines indexed objects The classes IndexedBase, Indexed and Idx would represent a matrix element M[i, j] as in the following graph:: 1) The Indexed class represents the entire indexed object. | ___|___ ' ' M[i, j] / \__\______ | | | | | 2) The Idx class represent indices and each Idx can | optionally contain information about its range. | 3) IndexedBase represents the `stem' of an indexed object, here `M'. The stem used by itself is usually taken to represent the entire array. There can be any number of indices on an Indexed object. No transformation properties are implemented in these Base objects, but implicit contraction of repeated indices is supported. Note that the support for complicated (i.e. non-atomic) integer expressions as indices is limited. (This should be improved in future releases.) Examples ======== To express the above matrix element example you would write: >>> from sympy.tensor import IndexedBase, Idx >>> from sympy import symbols >>> M = IndexedBase('M') >>> i, j = map(Idx, ['i', 'j']) >>> M[i, j] M[i, j] Repreated indices in a product implies a summation, so to express a matrix-vector product in terms of Indexed objects: >>> x = IndexedBase('x') >>> M[i, j]*x[j] M[i, j]*x[j] If the indexed objects will be converted to component based arrays, e.g. with the code printers or the autowrap framework, you also need to provide (symbolic or numerical) dimensions. This can be done by passing an optional shape parameter to IndexedBase upon construction: >>> dim1, dim2 = symbols('dim1 dim2', integer=True) >>> A = IndexedBase('A', shape=(dim1, 2*dim1, dim2)) >>> A.shape (dim1, 2*dim1, dim2) >>> A[i, j, 3].shape (dim1, 2*dim1, dim2) If an IndexedBase object has no shape information, it is assumed that the array is as large as the ranges of it's indices: >>> n, m = symbols('n m', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', n) >>> M[i, j].shape (m, n) >>> M[i, j].ranges [(0, m - 1), (0, n - 1)] The above can be compared with the following: >>> A[i, 2, j].shape (dim1, 2*dim1, dim2) >>> A[i, 2, j].ranges [(0, m - 1), None, (0, n - 1)] To analyze the structure of indexed expressions, you can use the methods get_indices() and get_contraction_structure(): >>> from sympy.tensor import get_indices, get_contraction_structure >>> get_indices(A[i, j, j]) (set([i]), {}) >>> get_contraction_structure(A[i, j, j]) {(j,): set([A[i, j, j]])} See the appropriate docstrings for a detailed explanation of the output. """ # TODO: (some ideas for improvement) # # o test and guarantee numpy compatibility # - implement full support for broadcasting # - strided arrays # # o more functions to analyze indexed expressions # - identify standard constructs, e.g matrix-vector product in a subexpression # # o functions to generate component based arrays (numpy and sympy.Matrix) # - generate a single array directly from Indexed # - convert simple sub-expressions # # o sophisticated indexing (possibly in subclasses to preserve simplicity) # - Idx with range smaller than dimension of Indexed # - Idx with stepsize != 1 # - Idx with step determined by function call from sympy.core import Expr, Basic, Tuple, Symbol, Integer, sympify, S from sympy.core.compatibility import is_sequence class IndexException(Exception): pass class IndexedBase(Expr): """Represent the base or stem of an indexed object The IndexedBase class represent an array that contains elements. The main purpose of this class is to allow the convenient creation of objects of the Indexed class. The __getitem__ method of IndexedBase returns an instance of Indexed. Alone, without indices, the IndexedBase class can be used as a notation for e.g. matrix equations, resembling what you could do with the Symbol class. But, the IndexedBase class adds functionality that is not available for Symbol instances: - An IndexedBase object can optionally store shape information. This can be used in to check array conformance and conditions for numpy broadcasting. (TODO) - An IndexedBase object implements syntactic sugar that allows easy symbolic representation of array operations, using implicit summation of repeated indices. - The IndexedBase object symbolizes a mathematical structure equivalent to arrays, and is recognized as such for code generation and automatic compilation and wrapping. >>> from sympy.tensor import IndexedBase, Idx >>> from sympy import symbols >>> A = IndexedBase('A'); A A >>> type(A) When an IndexedBase object recieves indices, it returns an array with named axes, represented by an Indexed object: >>> i, j = symbols('i j', integer=True) >>> A[i, j, 2] A[i, j, 2] >>> type(A[i, j, 2]) The IndexedBase constructor takes an optional shape argument. If given, it overrides any shape information in the indices. (But not the index ranges!) >>> m, n, o, p = symbols('m n o p', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', n) >>> A[i, j].shape (m, n) >>> B = IndexedBase('B', shape=(o, p)) >>> B[i, j].shape (o, p) """ is_commutative = False def __new__(cls, label, shape=None, **kw_args): if isinstance(label, basestring): label = Symbol(label) obj = Expr.__new__(cls, label, **kw_args) if is_sequence(shape): obj._shape = Tuple(*shape) else: obj._shape = shape return obj @property def args(self): if self._shape: return self._args + (self._shape,) else: return self._args def _hashable_content(self): return Expr._hashable_content(self) + (self._shape,) def __getitem__(self, indices, **kw_args): if is_sequence(indices): # Special case needed because M[*my_tuple] is a syntax error. if self.shape and len(self.shape) != len(indices): raise IndexException("Rank mismatch") return Indexed(self, *indices, **kw_args) else: if self.shape and len(self.shape) != 1: raise IndexException("Rank mismatch") return Indexed(self, indices, **kw_args) @property def shape(self): return self._shape @property def label(self): return self.args[0] def _sympystr(self, p): return p.doprint(self.label) class Indexed(Expr): """Represents a mathematical object with indices. >>> from sympy.tensor import Indexed, IndexedBase, Idx >>> from sympy import symbols >>> i, j = map(Idx, ['i', 'j']) >>> Indexed('A', i, j) A[i, j] It is recommended that Indexed objects are created via IndexedBase: >>> A = IndexedBase('A') >>> Indexed('A', i, j) == A[i, j] True """ is_commutative = False def __new__(cls, base, *args, **kw_args): if not args: raise IndexException("Indexed needs at least one index") if isinstance(base, (basestring, Symbol)): base = IndexedBase(base) elif not isinstance(base, IndexedBase): raise TypeError("Indexed expects string, Symbol or IndexedBase as base") return Expr.__new__(cls, base, *args, **kw_args) @property def base(self): return self.args[0] @property def indices(self): return self.args[1:] @property def rank(self): """returns the number of indices""" return len(self.args)-1 @property def shape(self): """returns a list with dimensions of each index. Dimensions is a property of the array, not of the indices. Still, if the IndexedBase does not define a shape attribute, it is assumed that the ranges of the indices correspond to the shape of the array. >>> from sympy.tensor.indexed import IndexedBase, Idx >>> from sympy import symbols >>> n, m = symbols('n m', integer=True) >>> i = Idx('i', m) >>> j = Idx('j', m) >>> A = IndexedBase('A', shape=(n, n)) >>> B = IndexedBase('B') >>> A[i, j].shape (n, n) >>> B[i, j].shape (m, m) """ if self.base.shape: return self.base.shape try: return Tuple(*[i.upper - i.lower + 1 for i in self.indices]) except AttributeError: raise IndexException("Range is not defined for all indices in: %s" % self) except TypeError: raise IndexException("Shape cannot be inferred from Idx with undefined range: %s"%self) @property def ranges(self): """returns a list of tuples with lower and upper range of each index If an index does not define the data members upper and lower, the corresponding slot in the list contains ``None'' instead of a tuple. """ ranges = [] for i in self.indices: try: ranges.append(Tuple(i.lower, i.upper)) except AttributeError: ranges.append(None) return ranges def _sympystr(self, p): indices = map(p.doprint, self.indices) return "%s[%s]" % (p.doprint(self.base), ", ".join(indices)) class Idx(Expr): """Represents an index, either symbolic or integer. There are a number of ways to create an Idx object. The constructor takes two arguments: ``label`` An integer or a symbol that labels the index. ``range`` Optionally you can specify a range as either - Symbol or integer: This is interpreted as dimension. lower and upper ranges are set to 0 and range-1 - tuple: This is interpreted as the lower and upper bounds in the range. Note that the Idx constructor is rather pedantic, and will not accept non-integer symbols. The only exception is that you can use oo and -oo to specify an unbounded range. For all other cases, both label and bounds must be declared as integers, in the sense that for a index label n, n.is_integer must return True. For convenience, if the label is given as a string, it is automatically converted to an integer symbol. (Note that this conversion is not done for range or dimension arguments.) :Examples: >>> from sympy.tensor import IndexedBase, Idx >>> from sympy import symbols, oo >>> n, i, L, U = symbols('n i L U', integer=True) 0) Construction from a string. An integer symbol is created from the string. >>> Idx('qwerty') qwerty 1) Both upper and lower bound specified >>> idx = Idx(i, (L, U)); idx i >>> idx.lower, idx.upper (L, U) 2) Only dimension specified, lower bound defaults to 0 >>> idx = Idx(i, n); idx.lower, idx.upper (0, n - 1) >>> idx = Idx(i, 4); idx.lower, idx.upper (0, 3) >>> idx = Idx(i, oo); idx.lower, idx.upper (0, oo) 3) No bounds given, interpretation of this depends on context. >>> idx = Idx(i); idx.lower, idx.upper (None, None) 4) for a literal integer instead of a symbolic label the bounds are still there: >>> idx = Idx(2, n); idx.lower, idx.upper (0, n - 1) """ is_integer = True def __new__(cls, label, range=None, **kw_args): if isinstance(label, basestring): label = Symbol(label, integer=True) label, range = map(sympify, (label, range)) if not label.is_integer: raise TypeError("Idx object requires an integer label") elif is_sequence(range): assert len(range) == 2, "Idx got range tuple with wrong length" for bound in range: if not (bound.is_integer or abs(bound) is S.Infinity): raise TypeError("Idx object requires integer bounds") args = label, Tuple(*range) elif isinstance(range, Expr): if not (range.is_integer or range is S.Infinity): raise TypeError("Idx object requires an integer dimension") args = label, Tuple(S.Zero, range-S.One) elif range: raise TypeError("range must be ordered iterable or integer sympy expression") else: args = label, obj = Expr.__new__(cls, *args, **kw_args) return obj @property def label(self): """Returns the name/label of the index, or it's integer value""" return self.args[0] @property def lower(self): """Returns the lower bound of the index""" try: return self.args[1][0] except IndexError: return @property def upper(self): """Returns the upper bound of the index""" try: return self.args[1][1] except IndexError: return def _sympystr(self, p): return p.doprint(self.label) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/tests/0000755000175000017500000000000012014170666023553 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/tests/test_indexed.py0000644000175000017500000001207712014170666026613 0ustar georgeskgeorgeskfrom sympy.core import symbols, Integer, Symbol, Tuple, oo from sympy.tensor.indexed import IndexException from sympy.utilities.pytest import raises # import test: from sympy import IndexedBase, Idx, Indexed def test_Idx_construction(): i, a, b = symbols('i a b', integer=True) assert Idx(i) != Idx(i, 1) assert Idx(i, a) == Idx(i, (0, a - 1)) assert Idx(i, oo) == Idx(i, (0, oo)) x = symbols('x') raises(TypeError, "Idx(x)") raises(TypeError, "Idx(0.5)") raises(TypeError, "Idx(i, x)") raises(TypeError, "Idx(i, 0.5)") raises(TypeError, "Idx(i, (x, 5))") raises(TypeError, "Idx(i, (2, x))") raises(TypeError, "Idx(i, (2, 3.5))") def test_Idx_properties(): i, a, b = symbols('i a b', integer=True) assert Idx(i).is_integer def test_Idx_bounds(): i, a, b = symbols('i a b', integer=True) assert Idx(i).lower == None assert Idx(i).upper == None assert Idx(i, a).lower == 0 assert Idx(i, a).upper == a - 1 assert Idx(i, 5).lower == 0 assert Idx(i, 5).upper == 4 assert Idx(i, oo).lower == 0 assert Idx(i, oo).upper == oo assert Idx(i, (a, b)).lower == a assert Idx(i, (a, b)).upper == b assert Idx(i, (1, 5)).lower == 1 assert Idx(i, (1, 5)).upper == 5 assert Idx(i, (-oo, oo)).lower == -oo assert Idx(i, (-oo, oo)).upper == oo def test_Idx_fixed_bounds(): i, a, b = symbols('i a b', integer=True) assert Idx(2).lower == None assert Idx(2).upper == None assert Idx(2, a).lower == 0 assert Idx(2, a).upper == a - 1 assert Idx(2, 5).lower == 0 assert Idx(2, 5).upper == 4 assert Idx(2, oo).lower == 0 assert Idx(2, oo).upper == oo assert Idx(2, (a, b)).lower == a assert Idx(2, (a, b)).upper == b assert Idx(2, (1, 5)).lower == 1 assert Idx(2, (1, 5)).upper == 5 assert Idx(2, (-oo, oo)).lower == -oo assert Idx(2, (-oo, oo)).upper == oo def test_Idx_func_args(): i, a, b = symbols('i a b', integer=True) ii = Idx(i) assert ii.func(*ii.args) == ii ii = Idx(i, a) assert ii.func(*ii.args) == ii ii = Idx(i, (a, b)) assert ii.func(*ii.args) == ii def test_Idx_subs(): i, a, b = symbols('i a b', integer=True) assert Idx(i, a).subs(a, b) == Idx(i, b) assert Idx(i, a).subs(i, b) == Idx(b, a) assert Idx(i).subs(i,2) == Idx(2) assert Idx(i, a).subs(a, 2) == Idx(i, 2) assert Idx(i, (a, b)).subs(i, 2) == Idx(2, (a, b)) def test_IndexedBase_sugar(): i, j = symbols('i j', integer=True) a = symbols('a') A1 = Indexed(a, i, j) A2 = IndexedBase(a) assert A1 == A2[i, j] assert A1 == A2[(i, j)] assert A1 == A2[[i, j]] assert A1 == A2[Tuple(i, j)] def test_IndexedBase_subs(): i, j, k = symbols('i j k', integer=True) a, b = symbols('a b') A = IndexedBase(a) B = IndexedBase(b) assert A[i] == B[i].subs(b, a) def test_IndexedBase_shape(): i, j, m, n = symbols('i j m n', integer=True) a = IndexedBase('a', shape=(m, m)) b = IndexedBase('a', shape=(m, n)) assert b.shape == Tuple(m, n) assert a[i, j] != b[i, j] assert a[i, j] == b[i, j].subs(n, m) assert b.func(*b.args) == b assert b[i, j].func(*b[i, j].args) == b[i, j] raises(IndexException, 'b[i]') raises(IndexException, 'b[i, i, j]') def test_Indexed_constructor(): i, j = symbols('i j', integer=True) A = Indexed('A', i, j) assert A == Indexed(Symbol('A'), i, j) assert A == Indexed(IndexedBase('A'), i, j) raises(TypeError, 'Indexed(A, i, j)') raises(IndexException, 'Indexed("A")') def test_Indexed_func_args(): i, j = symbols('i j', integer=True) a = symbols('a') A = Indexed(a, i, j) assert A == A.func(*A.args) def test_Indexed_subs(): i, j, k = symbols('i j k', integer=True) a, b = symbols('a b') A = IndexedBase(a) B = IndexedBase(b) assert A[i, j] == B[i, j].subs(b, a) assert A[i, j] == A[i, k].subs(k, j) def test_Indexed_properties(): i, j = symbols('i j', integer=True) A = Indexed('A', i, j) assert A.rank == 2 assert A.indices == (i, j) assert A.base == IndexedBase('A') assert A.ranges == [None, None] raises(IndexException, 'A.shape') n, m = symbols('n m', integer=True) assert Indexed('A', Idx(i, m), Idx(j, n)).ranges == [Tuple(0, m - 1), Tuple(0, n - 1)] assert Indexed('A', Idx(i, m), Idx(j, n)).shape == Tuple(m, n) raises(IndexException, 'Indexed("A", Idx(i, m), Idx(j)).shape') def test_Indexed_shape_precedence(): i, j = symbols('i j', integer=True) o, p = symbols('o p', integer=True) n, m = symbols('n m', integer=True) a = IndexedBase('a', shape=(o, p)) assert a.shape == Tuple(o, p) assert Indexed(a, Idx(i, m), Idx(j, n)).ranges == [Tuple(0, m - 1), Tuple(0, n - 1)] assert Indexed(a, Idx(i, m), Idx(j, n)).shape == Tuple(o, p) assert Indexed(a, Idx(i, m), Idx(j)).ranges == [Tuple(0, m - 1), Tuple(None, None)] assert Indexed(a, Idx(i, m), Idx(j)).shape == Tuple(o, p) def test_complex_indices(): i, j = symbols('i j', integer=True) A = Indexed('A', i, i + j) assert A.rank == 2 assert A.indices == (i, i + j) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/tests/test_index_methods.py0000644000175000017500000001554312014170666030026 0ustar georgeskgeorgeskfrom sympy.core import symbols, S, Pow, Function from sympy.functions import exp from sympy.utilities.pytest import raises from sympy.tensor.indexed import Idx, IndexedBase, Indexed from sympy.tensor.index_methods import IndexConformanceException # import test from sympy import get_contraction_structure, get_indices def test_trivial_indices(): x, y = symbols('x y') assert get_indices(x) == (set([]), {}) assert get_indices(x*y) == (set([]), {}) assert get_indices(x + y) == (set([]), {}) assert get_indices(x**y) == (set([]), {}) def test_get_indices_Indexed(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_indices(x[i, j]) == (set([i, j]), {}) assert get_indices(x[j, i]) == (set([j, i]), {}) def test_get_indices_Idx(): f = Function('f') i, j = Idx('i'), Idx('j') assert get_indices(f(i)*j) == (set([i, j]), {}) assert get_indices(f(j, i)) == (set([j, i]), {}) assert get_indices(f(i)*i) == (set(), {}) def test_get_indices_mul(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_indices(x[j]*y[i]) == (set([i, j]), {}) assert get_indices(x[i]*y[j]) == (set([i, j]), {}) def test_get_indices_exceptions(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') raises(IndexConformanceException, 'get_indices(x[i] + y[j])') def test_scalar_broadcast(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_indices(x[i] + y[i, i]) == (set([i]), {}) def test_get_indices_add(): x = IndexedBase('x') y = IndexedBase('y') A = IndexedBase('A') i, j, k = Idx('i'), Idx('j'), Idx('k') assert get_indices(x[i] + 2*y[i]) == (set([i,]), {}) assert get_indices(y[i] + 2*A[i, j]*x[j]) == (set([i,]), {}) assert get_indices(y[i] + 2*(x[i] + A[i, j]*x[j])) == (set([i,]), {}) assert get_indices(y[i] + x[i]*(A[j, j] + 1)) == (set([i,]), {}) assert get_indices(y[i] + x[i]*x[j]*(y[j] + A[j, k]*x[k])) == (set([i,]), {}) def test_get_indices_Pow(): x = IndexedBase('x') y = IndexedBase('y') A = IndexedBase('A') i, j, k = Idx('i'), Idx('j'), Idx('k') assert get_indices(Pow(x[i], y[j])) == (set([i,j]), {}) assert get_indices(Pow(x[i, k], y[j, k])) == (set([i, j, k]), {}) assert get_indices(Pow(A[i, k], y[k] + A[k, j]*x[j])) == (set([i, k]), {}) assert get_indices(Pow(2, x[i])) == get_indices(exp(x[i])) # test of a design decision, this may change: assert get_indices(Pow(x[i], 2)) == (set([i,]), {}) def test_get_contraction_structure_basic(): x = IndexedBase('x') y = IndexedBase('y') i, j = Idx('i'), Idx('j') assert get_contraction_structure(x[i]*y[j]) == {None: set([x[i]*y[j]])} assert get_contraction_structure(x[i] + y[j]) == {None: set([x[i], y[j]])} assert get_contraction_structure(x[i]*y[i]) == {(i,): set([x[i]*y[i]])} assert get_contraction_structure(1 + x[i]*y[i]) == {None: set([S.One]), (i,): set([x[i]*y[i]])} assert get_contraction_structure(x[i]**y[i]) == {None: set([x[i]**y[i]])} def test_get_contraction_structure_complex(): x = IndexedBase('x') y = IndexedBase('y') A = IndexedBase('A') i, j, k = Idx('i'), Idx('j'), Idx('k') expr1 = y[i] + A[i, j]*x[j] d1 = {None: set([y[i]]), (j,): set([A[i, j]*x[j]])} assert get_contraction_structure(expr1) == d1 expr2 = expr1*A[k, i] + x[k] d2 = {None: set([x[k]]), (i,): set([expr1*A[k, i]]), expr1*A[k, i]: [d1]} assert get_contraction_structure(expr2) == d2 def test_contraction_structure_simple_Pow(): x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') ii_jj = x[i, i]**y[j, j] assert get_contraction_structure(ii_jj) == { None: set([ii_jj]), ii_jj: [ {(i,): set([x[i, i]])}, {(j,): set([y[j, j]])} ] } def test_contraction_structure_Mul_and_Pow(): x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') i_ji = x[i]**(y[j]*x[i]) assert get_contraction_structure(i_ji) == {None: set([i_ji])} ij_i = (x[i]*y[j])**(y[i]) assert get_contraction_structure(ij_i) == {None: set([ij_i])} j_ij_i = x[j]*(x[i]*y[j])**(y[i]) assert get_contraction_structure(j_ij_i) == {(j,): set([j_ij_i])} j_i_ji = x[j]*x[i]**(y[j]*x[i]) assert get_contraction_structure(j_i_ji) == {(j,): set([j_i_ji])} ij_exp_kki = x[i]*y[j]*exp(y[i]*y[k, k]) result = get_contraction_structure(ij_exp_kki) expected = { (i,): set([ij_exp_kki]), ij_exp_kki: [{ None: set([exp(y[i]*y[k, k])]), exp(y[i]*y[k, k]): [{ None: set([y[i]*y[k, k]]), y[i]*y[k, k]: [{(k,): set([y[k, k]])}] }]} ] } assert result == expected def test_contraction_structure_Add_in_Pow(): x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') s_ii_jj_s = (1 + x[i, i])**(1 + y[j, j]) expected = { None: set([s_ii_jj_s]), s_ii_jj_s: [ {None: set([S.One]), (i,): set([x[i, i]])}, {None: set([S.One]), (j,): set([y[j, j]])} ] } result = get_contraction_structure(s_ii_jj_s) assert result == expected def test_contraction_structure_Pow_in_Pow(): x = IndexedBase('x') y = IndexedBase('y') z = IndexedBase('z') i, j, k = Idx('i'), Idx('j'), Idx('k') ii_jj_kk = x[i, i]**y[j, j]**z[k, k] expected = { None: set([ii_jj_kk]), ii_jj_kk: [ {(i,): set([x[i, i]])}, { None: set([y[j, j]**z[k, k]]), y[j, j]**z[k, k]: [ {(j,): set([y[j, j]])}, {(k,): set([z[k, k]])} ] } ] } assert get_contraction_structure(ii_jj_kk) == expected def test_ufunc_support(): f = Function('f') g = Function('g') x = IndexedBase('x') y = IndexedBase('y') i, j, k = Idx('i'), Idx('j'), Idx('k') a = symbols('a') assert get_indices(f(x[i])) == (set([i]), {}) assert get_indices(f(x[i], y[j])) == (set([i, j]), {}) assert get_indices(f(y[i])*g(x[i])) == (set(), {}) assert get_indices(f(a, x[i])) == (set([i]), {}) assert get_indices(f(a, y[i], x[j])*g(x[i])) == (set([j]), {}) assert get_indices(g(f(x[i]))) == (set([i]), {}) assert get_contraction_structure(f(x[i])) == {None: set([f(x[i])])} assert get_contraction_structure(f(y[i])*g(x[i])) == {(i,): set([f(y[i])*g(x[i])])} assert get_contraction_structure(f(y[i])*g(f(x[i]))) == {(i,): set([f(y[i])*g(f(x[i]))])} assert get_contraction_structure(f(x[j], y[i])*g(x[i])) == {(i,): set([f(x[j], y[i])*g(x[i])])} wxgeometrie-0.133.2.orig/wxgeometrie/sympy/tensor/__init__.py0000644000175000017500000000027612014170666024527 0ustar georgeskgeorgesk"""A module to manipulate symbolic objects with indices including tensors """ from indexed import IndexedBase, Idx, Indexed from index_methods import get_contraction_structure, get_indices wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/0000755000175000017500000000000012014170666023464 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/refine.py0000644000175000017500000001163712014170666025316 0ustar georgeskgeorgeskfrom sympy.core import S, Add from sympy.assumptions import Q, ask from sympy.logic.boolalg import fuzzy_not def refine(expr, assumptions=True): """ Simplify an expression using assumptions. Gives the form of expr that would be obtained if symbols in it were replaced by explicit numerical expressions satisfying the assumptions. Examples:: >>> from sympy import refine, sqrt, Q >>> from sympy.abc import x >>> refine(sqrt(x**2), Q.real(x)) Abs(x) >>> refine(sqrt(x**2), Q.positive(x)) x """ if not expr.is_Atom: args = [refine(arg, assumptions) for arg in expr.args] # TODO: this will probably not work with Integral or Polynomial expr = expr.func(*args) name = expr.__class__.__name__ handler = handlers_dict.get(name, None) if handler is None: return expr new_expr = handler(expr, assumptions) if (new_expr is None) or (expr == new_expr): return expr return refine(new_expr, assumptions) def refine_abs(expr, assumptions): """ Handler for the absolute value. Examples:: >>> from sympy import Symbol, Q, refine, Abs >>> from sympy.assumptions.refine import refine_abs >>> from sympy.abc import x >>> refine_abs(Abs(x), Q.real(x)) >>> refine_abs(Abs(x), Q.positive(x)) x >>> refine_abs(Abs(x), Q.negative(x)) -x """ arg = expr.args[0] if ask(Q.real(arg), assumptions) and \ fuzzy_not(ask(Q.negative(arg), assumptions)): # if it's nonnegative return arg if ask(Q.negative(arg), assumptions): return -arg def refine_Pow(expr, assumptions): """ Handler for instances of Pow. >>> from sympy import Symbol, Q >>> from sympy.assumptions.refine import refine_Pow >>> from sympy.abc import x,y,z >>> refine_Pow((-1)**x, Q.real(x)) >>> refine_Pow((-1)**x, Q.even(x)) 1 >>> refine_Pow((-1)**x, Q.odd(x)) -1 For powers of -1, even parts of the exponent can be simplified: >>> refine_Pow((-1)**(x+y), Q.even(x)) (-1)**y >>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z)) (-1)**y >>> refine_Pow((-1)**(x+y+2), Q.odd(x)) (-1)**(y + 1) >>> refine_Pow((-1)**(x+3), True) (-1)**(x + 1) """ from sympy.core import Pow, Rational from sympy.functions import sign if ask(Q.real(expr.base), assumptions): if expr.base.is_number: if ask(Q.even(expr.exp), assumptions): return abs(expr.base) ** expr.exp if ask(Q.odd(expr.exp), assumptions): return sign(expr.base) * abs(expr.base) ** expr.exp if isinstance(expr.exp, Rational): if type(expr.base) is Pow: return abs(expr.base.base) ** (expr.base.exp * expr.exp) if expr.base is S.NegativeOne: if expr.exp.is_Add: # For powers of (-1) we can remove # - even terms # - pairs of odd terms # - a single odd term + 1 # - A numerical constant N can be replaced with mod(N,2) coeff, terms = expr.exp.as_coeff_add() terms = set(terms) even_terms = set([]) odd_terms = set([]) initial_number_of_terms = len(terms) for t in terms: if ask(Q.even(t), assumptions): even_terms.add(t) elif ask(Q.odd(t), assumptions): odd_terms.add(t) terms -= even_terms if len(odd_terms)%2: terms -= odd_terms new_coeff = (coeff + S.One) % 2 else: terms -= odd_terms new_coeff = coeff % 2 if new_coeff != coeff or len(terms) < initial_number_of_terms: terms.add(new_coeff) return expr.base**(Add(*terms)) def refine_exp(expr, assumptions): """ Handler for exponential function. >>> from sympy import Symbol, Q, exp, I, pi >>> from sympy.assumptions.refine import refine_exp >>> from sympy.abc import x >>> refine_exp(exp(pi*I*2*x), Q.real(x)) >>> refine_exp(exp(pi*I*2*x), Q.integer(x)) 1 """ arg = expr.args[0] if arg.is_Mul: coeff = arg.as_coefficient(S.Pi*S.ImaginaryUnit) if coeff: if ask(Q.integer(2*coeff), assumptions): if ask(Q.even(coeff), assumptions): return S.One elif ask(Q.odd(coeff), assumptions): return S.NegativeOne elif ask(Q.even(coeff + S.Half), assumptions): return -S.ImaginaryUnit elif ask(Q.odd(coeff + S.Half), assumptions): return S.ImaginaryUnit handlers_dict = { 'Abs' : refine_abs, 'Pow' : refine_Pow, 'exp' : refine_exp, } wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/handlers/0000755000175000017500000000000012014170666025264 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/handlers/sets.py0000644000175000017500000002700612014170666026621 0ustar georgeskgeorgesk""" Handlers for predicates related to set membership: integer, rational, etc. """ from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler class AskIntegerHandler(CommonHandler): """ Handler for Q.integer Test that an expression belongs to the field of integer numbers """ @staticmethod def _number(expr, assumptions): # helper method if expr.as_real_imag()[1] == 0: return expr.evalf(1) == expr return False @staticmethod def Add(expr, assumptions): """ Integer + Integer -> Integer Integer + !Integer -> !Integer !Integer + !Integer -> ? """ if expr.is_number: return AskIntegerHandler._number(expr, assumptions) return test_closed_group(expr, assumptions, Q.integer) @staticmethod def Mul(expr, assumptions): """ Integer*Integer -> Integer Integer*Irrational -> !Integer Odd/Even -> !Integer Integer*Rational -> ? """ if expr.is_number: return AskIntegerHandler._number(expr, assumptions) _output = True for arg in expr.args: if not ask(Q.integer(arg), assumptions): if arg.is_Rational: if arg.q == 2: return ask(Q.even(2*expr), assumptions) if ~(arg.q & 1): return None elif ask(Q.irrational(arg), assumptions): if _output: _output = False else: return else: return else: return _output Pow = Add @staticmethod def int(expr, assumptions): return True @staticmethod def Integer(expr, assumptions): return True @staticmethod def Rational(expr, assumptions): # rationals with denominator one get # evaluated to Integers return False @staticmethod def Float(expr, assumptions): return int(expr) == expr @staticmethod def Pi(expr, assumptions): return False @staticmethod def Exp1(expr, assumptions): return False @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def Abs(expr, assumptions): return ask(Q.integer(expr.args[0]), assumptions) class AskRationalHandler(CommonHandler): """ Handler for Q.rational Test that an expression belongs to the field of rational numbers """ @staticmethod def Add(expr, assumptions): """ Rational + Rational -> Rational Rational + !Rational -> !Rational !Rational + !Rational -> ? """ if expr.is_number: if expr.as_real_imag()[1]: return False return test_closed_group(expr, assumptions, Q.rational) Mul = Add @staticmethod def Pow(expr, assumptions): """ Rational ** Integer -> Rational Irrational ** Rational -> Irrational Rational ** Irrational -> ? """ if ask(Q.integer(expr.exp), assumptions): return ask(Q.rational(expr.base), assumptions) elif ask(Q.rational(expr.exp), assumptions): if ask(Q.prime(expr.base), assumptions): return False @staticmethod def Rational(expr, assumptions): return True @staticmethod def Float(expr, assumptions): # it's finite-precission return True @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False @staticmethod def Pi(expr, assumptions): return False @staticmethod def Exp1(expr, assumptions): return False class AskIrrationalHandler(CommonHandler): @staticmethod def Basic(expr, assumptions): _real = ask(Q.real(expr), assumptions) if _real: _rational = ask(Q.rational(expr), assumptions) if _rational is None: return None return not _rational else: return _real class AskRealHandler(CommonHandler): """ Handler for Q.real Test that an expression belongs to the field of real numbers """ @staticmethod def _number(expr, assumptions): return not expr.as_real_imag()[1] @staticmethod def Add(expr, assumptions): """ Real + Real -> Real Real + (Complex & !Real) -> !Real """ if expr.is_number: return AskRealHandler._number(expr, assumptions) return test_closed_group(expr, assumptions, Q.real) @staticmethod def Mul(expr, assumptions): """ Real*Real -> Real Real*Imaginary -> !Real Imaginary*Imaginary -> Real """ if expr.is_number: return AskRealHandler._number(expr, assumptions) result = True for arg in expr.args: if ask(Q.real(arg), assumptions): pass elif ask(Q.imaginary(arg), assumptions): result = result ^ True else: break else: return result @staticmethod def Pow(expr, assumptions): """ Real**Integer -> Real Positive**Real -> Real Real**(Integer/Even) -> Real if base is nonnegative Real**(Integer/Odd) -> Real """ if expr.is_number: return AskRealHandler._number(expr, assumptions) if ask(Q.real(expr.base), assumptions): if ask(Q.integer(expr.exp), assumptions): return True elif expr.exp.is_Rational: if (expr.exp.q % 2 == 0): return ask(Q.real(expr.base), assumptions) and \ not ask(Q.negative(expr.base), assumptions) else: return True elif ask(Q.real(expr.exp), assumptions): if ask(Q.positive(expr.base), assumptions): return True @staticmethod def Rational(expr, assumptions): return True @staticmethod def Float(expr, assumptions): return True @staticmethod def Pi(expr, assumptions): return True @staticmethod def Exp1(expr, assumptions): return True @staticmethod def Abs(expr, assumptions): return True @staticmethod def re(expr, assumptions): return True im = re @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False @staticmethod def sin(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return True cos, exp = sin, sin class AskExtendedRealHandler(AskRealHandler): """ Handler for Q.extended_real Test that an expression belongs to the field of extended real numbers, that is real numbers union {Infinity, -Infinity} """ @staticmethod def Add(expr, assumptions): return test_closed_group(expr, assumptions, Q.extended_real) Mul, Pow = Add, Add @staticmethod def Infinity(expr, assumptions): return True @staticmethod def NegativeInfinity(expr, assumptions): return True class AskComplexHandler(CommonHandler): """ Handler for Q.complex Test that an expression belongs to the field of complex numbers """ @staticmethod def Add(expr, assumptions): return test_closed_group(expr, assumptions, Q.complex) Mul, Pow = Add, Add @staticmethod def Number(expr, assumptions): return True @staticmethod def NumberSymbol(expr, assumptions): return True @staticmethod def Abs(expr, assumptions): return True @staticmethod def ImaginaryUnit(expr, assumptions): return True @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False sin, cos, exp, re, im = [Abs]*5 # they are all complex functions class AskImaginaryHandler(CommonHandler): """ Handler for Q.imaginary Test that an expression belongs to the field of imaginary numbers, that is, numbers in the form x*I, where x is real """ @staticmethod def _number(expr, assumptions): # helper method return not expr.as_real_imag()[0] @staticmethod def Add(expr, assumptions): """ Imaginary + Imaginary -> Imaginary Imaginary + Complex -> ? Imaginary + Real -> !Imaginary """ if expr.is_number: return AskImaginaryHandler._number(expr, assumptions) reals = 0 for arg in expr.args: if ask(Q.imaginary(arg), assumptions): pass elif ask(Q.real(arg), assumptions): reals += 1 else: break else: if reals == 0: return True if reals == 1 or (len(expr.args) == reals): # two reals could sum 0 thus giving an imaginary return False @staticmethod def Mul(expr, assumptions): """ Real*Imaginary -> Imaginary Imaginary*Imaginary -> Real """ if expr.is_number: return AskImaginaryHandler._number(expr, assumptions) result = False reals = 0 for arg in expr.args: if ask(Q.imaginary(arg), assumptions): result = result ^ True elif not ask(Q.real(arg), assumptions): break else: if reals == len(expr.args): return False return result Pow = Add @staticmethod def Number(expr, assumptions): return not (expr.as_real_imag()[1] == 0) NumberSymbol = Number @staticmethod def ImaginaryUnit(expr, assumptions): return True class AskAlgebraicHandler(CommonHandler): """Handler for Q.algebraic key. """ @staticmethod def Add(expr, assumptions): return test_closed_group(expr, assumptions, Q.algebraic) @staticmethod def Mul(expr, assumptions): return test_closed_group(expr, assumptions, Q.algebraic) @staticmethod def Pow(expr, assumptions): return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions) @staticmethod def Number(expr, assumptions): return False @staticmethod def Rational(expr, assumptions): return expr.q != 0 @staticmethod def ImaginaryUnit(expr, assumptions): return True @staticmethod def AlgebraicNumber(expr, assumptions): return True #### Helper methods def test_closed_group(expr, assumptions, key): """ Test for membership in a group with respect to the current operation """ result = True for arg in expr.args: _out = ask(key(arg), assumptions) if _out is None: break elif _out is False: if result: result = False else: break else: return result wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/handlers/ntheory.py0000644000175000017500000001375312014170666027337 0ustar georgeskgeorgesk""" Handlers for keys related to number theory: prime, even, odd, etc. """ from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler from sympy.ntheory import isprime class AskPrimeHandler(CommonHandler): """ Handler for key 'prime' Test that an expression represents a prime number """ @staticmethod def _number(expr, assumptions): # helper method if (expr.as_real_imag()[1] == 0) and int(expr.evalf()) == expr: return isprime(expr.evalf(1)) return False @staticmethod def Basic(expr, assumptions): # Just use int(expr) once # http://code.google.com/p/sympy/issues/detail?id=1462 # is solved if expr.is_number: return AskPrimeHandler._number(expr, assumptions) @staticmethod def Mul(expr, assumptions): if expr.is_number: return AskPrimeHandler._number(expr, assumptions) for arg in expr.args: if ask(Q.integer(arg), assumptions): pass else: break else: # a product of integers can't be a prime return False @staticmethod def Pow(expr, assumptions): """ Integer**Integer -> !Prime """ if expr.is_number: return AskPrimeHandler._number(expr, assumptions) if ask(Q.integer(expr.exp), assumptions) and \ ask(Q.integer(expr.base), assumptions): return False @staticmethod def Integer(expr, assumptions): return isprime(expr) @staticmethod def Rational(expr, assumptions): return False @staticmethod def Float(expr, assumptions): return AskPrimeHandler._number(expr, assumptions) @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def NumberSymbol(expr, assumptions): return AskPrimeHandler._number(expr, assumptions) class AskCompositeHandler(CommonHandler): @staticmethod def Basic(expr, assumptions): _positive = ask(Q.positive(expr), assumptions) if _positive: _integer = ask(Q.integer(expr), assumptions) if _integer: _prime = ask(Q.prime(expr), assumptions) if _prime is None: return return not _prime else: return _integer else: return _positive class AskEvenHandler(CommonHandler): @staticmethod def _number(expr, assumptions): # helper method if (expr.as_real_imag()[1] == 0) and expr.evalf(1) == expr: return float(expr.evalf()) % 2 == 0 else: return False @staticmethod def Basic(expr, assumptions): if expr.is_number: return AskEvenHandler._number(expr, assumptions) @staticmethod def Mul(expr, assumptions): """ Even * Integer -> Even Even * Odd -> Even Integer * Odd -> ? Odd * Odd -> Odd """ if expr.is_number: return AskEvenHandler._number(expr, assumptions) even, odd, irrational = False, 0, False for arg in expr.args: # check for all integers and at least one even if ask(Q.integer(arg), assumptions): if ask(Q.even(arg), assumptions): even = True elif ask(Q.odd(arg), assumptions): odd += 1 elif ask(Q.irrational(arg), assumptions): # one irrational makes the result False # two makes it undefined if irrational: break irrational = True else: break else: if irrational: return False if even: return True if odd == len(expr.args): return False @staticmethod def Add(expr, assumptions): """ Even + Odd -> Odd Even + Even -> Even Odd + Odd -> Even TODO: remove float() when issue http://code.google.com/p/sympy/issues/detail?id=1473 is solved """ if expr.is_number: return AskEvenHandler._number(expr, assumptions) _result = True for arg in expr.args: if ask(Q.even(arg), assumptions): pass elif ask(Q.odd(arg), assumptions): _result = not _result else: break else: return _result @staticmethod def Integer(expr, assumptions): return not bool(expr.p & 1) @staticmethod def Rational(expr, assumptions): return False @staticmethod def Float(expr, assumptions): return expr % 2 == 0 @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False @staticmethod def NumberSymbol(expr, assumptions): return AskEvenHandler._number(expr, assumptions) @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def Abs(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return ask(Q.even(expr.args[0]), assumptions) @staticmethod def re(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return ask(Q.even(expr.args[0]), assumptions) @staticmethod def im(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return True class AskOddHandler(CommonHandler): """ Handler for key 'odd' Test that an expression represents an odd number """ @staticmethod def Basic(expr, assumptions): _integer = ask(Q.integer(expr), assumptions) if _integer: _even = ask(Q.even(expr), assumptions) if _even is None: return None return not _even return _integer wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/handlers/order.py0000644000175000017500000001241012014170666026747 0ustar georgeskgeorgesk""" AskHandlers related to order relations: positive, negative, etc. """ from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler class AskNegativeHandler(CommonHandler): """ This is called by ask() when key='negative' Test that an expression is less (strict) than zero. Examples: >>> from sympy import ask, Q, pi >>> ask(Q.negative(pi+1)) # this calls AskNegativeHandler.Add False >>> ask(Q.negative(pi**2)) # this calls AskNegativeHandler.Pow False """ @staticmethod def _number(expr, assumptions): if not expr.as_real_imag()[1]: return expr.evalf() < 0 else: return False @staticmethod def Basic(expr, assumptions): if expr.is_number: return AskNegativeHandler._number(expr, assumptions) @staticmethod def Add(expr, assumptions): """ Positive + Positive -> Positive, Negative + Negative -> Negative """ if expr.is_number: return AskNegativeHandler._number(expr, assumptions) for arg in expr.args: if not ask(Q.negative(arg), assumptions): break else: # if all argument's are negative return True @staticmethod def Mul(expr, assumptions): if expr.is_number: return AskNegativeHandler._number(expr, assumptions) result = None for arg in expr.args: if result is None: result = False if ask(Q.negative(arg), assumptions): result = not result elif ask(Q.positive(arg), assumptions): pass else: return return result @staticmethod def Pow(expr, assumptions): """ Real ** Even -> NonNegative Real ** Odd -> same_as_base NonNegative ** Positive -> NonNegative """ if expr.is_number: return AskNegativeHandler._number(expr, assumptions) if ask(Q.real(expr.base), assumptions): if ask(Q.positive(expr.base), assumptions): return False if ask(Q.even(expr.exp), assumptions): return False if ask(Q.odd(expr.exp), assumptions): return ask(Q.negative(expr.base), assumptions) @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def Abs(expr, assumptions): return False class AskNonZeroHandler(CommonHandler): """ Handler for key 'zero' Test that an expression is not identically zero """ @staticmethod def Basic(expr, assumptions): if expr.is_number: # if there are no symbols just evalf return expr.evalf() != 0 @staticmethod def Add(expr, assumptions): if all([ask(Q.positive(x), assumptions) for x in expr.args]) \ or all([ask(Q.negative(x), assumptions) for x in expr.args]): return True @staticmethod def Mul(expr, assumptions): for arg in expr.args: result = ask(Q.nonzero(arg), assumptions) if result: continue return result return True @staticmethod def Pow(expr, assumptions): return ask(Q.nonzero(expr.base), assumptions) @staticmethod def NaN(expr, assumptions): return True @staticmethod def Abs(expr, assumptions): return ask(Q.nonzero(expr.args[0]), assumptions) class AskPositiveHandler(CommonHandler): """ Handler for key 'positive' Test that an expression is greater (strict) than zero """ @staticmethod def _number(expr, assumptions): if not expr.as_real_imag()[1]: return expr.evalf() > 0 else: return False @staticmethod def Basic(expr, assumptions): if expr.is_number: return AskPositiveHandler._number(expr, assumptions) @staticmethod def Mul(expr, assumptions): if expr.is_number: return AskPositiveHandler._number(expr, assumptions) result = True for arg in expr.args: if ask(Q.positive(arg), assumptions): continue elif ask(Q.negative(arg), assumptions): result = result ^ True else: return return result @staticmethod def Add(expr, assumptions): if expr.is_number: return AskPositiveHandler._number(expr, assumptions) for arg in expr.args: if ask(Q.positive(arg), assumptions) is not True: break else: # if all argument's are positive return True @staticmethod def Pow(expr, assumptions): if expr.is_number: return expr.evalf() > 0 if ask(Q.positive(expr.base), assumptions): return True if ask(Q.negative(expr.base), assumptions): if ask(Q.even(expr.exp), assumptions): return True if ask(Q.even(expr.exp), assumptions): return False @staticmethod def exp(expr, assumptions): if ask(Q.real(expr.args[0]), assumptions): return True @staticmethod def ImaginaryUnit(expr, assumptions): return False @staticmethod def Abs(expr, assumptions): return ask(Q.nonzero(expr), assumptions) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/handlers/calculus.py0000644000175000017500000001022212014170666027446 0ustar georgeskgeorgesk""" This module contains query handlers responsible for calculus queries: infinitesimal, bounded, etc. """ from sympy.logic.boolalg import conjuncts from sympy.assumptions import Q, ask from sympy.assumptions.handlers import CommonHandler class AskInfinitesimalHandler(CommonHandler): """ Handler for key 'infinitesimal' Test that a given expression is equivalent to an infinitesimal number """ @staticmethod def _number(expr, assumptions): # helper method return expr.evalf() == 0 @staticmethod def Basic(expr, assumptions): if expr.is_number: return AskInfinitesimalHandler._number(expr, assumptions) @staticmethod def Mul(expr, assumptions): """ Infinitesimal*Bounded -> Infinitesimal """ if expr.is_number: return AskInfinitesimalHandler._number(expr, assumptions) result = False for arg in expr.args: if ask(Q.infinitesimal(arg), assumptions): result = True elif ask(Q.bounded(arg), assumptions): continue else: break else: return result Add, Pow = Mul, Mul @staticmethod def Number(expr, assumptions): return expr == 0 NumberSymbol = Number @staticmethod def ImaginaryUnit(expr, assumptions): return False class AskBoundedHandler(CommonHandler): """ Handler for key 'bounded'. Test that an expression is bounded respect to all its variables. Example of usage: >>> from sympy import Symbol, Q >>> from sympy.assumptions.handlers.calculus import AskBoundedHandler >>> from sympy.abc import x >>> a = AskBoundedHandler() >>> a.Symbol(x, Q.positive(x)) False >>> a.Symbol(x, Q.bounded(x)) True """ @staticmethod def Symbol(expr, assumptions): """ Handles Symbol. Example: >>> from sympy import Symbol, Q >>> from sympy.assumptions.handlers.calculus import AskBoundedHandler >>> from sympy.abc import x >>> a = AskBoundedHandler() >>> a.Symbol(x, Q.positive(x)) False >>> a.Symbol(x, Q.bounded(x)) True """ if Q.bounded(expr) in conjuncts(assumptions): return True return False @staticmethod def Add(expr, assumptions): """ Bounded + Bounded -> Bounded Unbounded + Bounded -> Unbounded Unbounded + Unbounded -> ? """ result = True for arg in expr.args: _bounded = ask(Q.bounded(arg), assumptions) if _bounded: continue elif _bounded is None: return elif _bounded is False: if result: result = False else: return return result Mul = Add @staticmethod def Pow(expr, assumptions): """ Unbounded ** Whatever -> Unbounded Bounded ** Unbounded -> Unbounded if base > 1 Bounded ** Unbounded -> Unbounded if base < 1 """ base_bounded = ask(Q.bounded(expr.base), assumptions) if not base_bounded: return base_bounded if ask(Q.bounded(expr.exp), assumptions) and base_bounded: return True if base_bounded and expr.base.is_number: # We need to implement relations for this if abs(expr.base) > 1: return False return True @staticmethod def log(expr, assumptions): return ask(Q.bounded(expr.args[0]), assumptions) exp = log @staticmethod def sin(expr, assumptions): return True cos = sin @staticmethod def Number(expr, assumptions): return True @staticmethod def Infinity(expr, assumptions): return False @staticmethod def NegativeInfinity(expr, assumptions): return False @staticmethod def Pi(expr, assumptions): return True @staticmethod def Exp1(expr, assumptions): return True @staticmethod def ImaginaryUnit(expr, assumptions): return True @staticmethod def sign(expr, assumptions): return True wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/handlers/__init__.py0000644000175000017500000000504612014170666027402 0ustar georgeskgeorgeskfrom sympy.logic.boolalg import conjuncts from sympy.assumptions import Q, ask class AskHandler(object): """Base class that all Ask Handlers must inherit""" pass class CommonHandler(AskHandler): """Defines some useful methods common to most Handlers """ @staticmethod def NaN(expr, assumptions): return False class AskCommutativeHandler(CommonHandler): """ Handler for key 'commutative' """ @staticmethod def Symbol(expr, assumptions): """Objects are expected to be commutative unless otherwise stated""" assumps = conjuncts(assumptions) if Q.commutative(expr) in assumps: return True elif ~Q.commutative(expr) in assumps: return False return True @staticmethod def Basic(expr, assumptions): for arg in expr.args: if not ask(Q.commutative(arg), assumptions): return False return True @staticmethod def Number(expr, assumptions): return True @staticmethod def NaN(expr, assumptions): return True class TautologicalHandler(AskHandler): """Wrapper allowing to query the truth value of a boolean expression.""" @staticmethod def bool(expr, assumptions): return expr @staticmethod def AppliedPredicate(expr, assumptions): return ask(expr, assumptions) @staticmethod def Not(expr, assumptions): value = ask(expr.args[0], assumptions=assumptions) if value in (True, False): return not value else: return None @staticmethod def Or(expr, assumptions): result = False for arg in expr.args: p = ask(arg, assumptions=assumptions) if p == True: return True if p == None: result = None return result @staticmethod def And(expr, assumptions): result = True for arg in expr.args: p = ask(arg, assumptions=assumptions) if p == False: return False if p == None: result = None return result @staticmethod def Implies(expr, assumptions): p, q = expr.args return ask(~p | q, assumptions=assumptions) @staticmethod def Equivalent(expr, assumptions): p, q = expr.args pt = ask(p, assumptions=assumptions) if pt == None: return None qt = ask(q, assumptions=assumptions) if qt == None: return None return pt == qt wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/ask.py0000644000175000017500000002610112014170666024614 0ustar georgeskgeorgesk"""Module for querying SymPy objects about assumptions.""" from sympy.core import sympify from sympy.logic.boolalg import to_cnf, And, Not, Or, Implies, Equivalent from sympy.logic.inference import satisfiable from sympy.assumptions.assume import (global_assumptions, Predicate, AppliedPredicate) class Q: """Supported ask keys.""" bounded = Predicate('bounded') commutative = Predicate('commutative') complex = Predicate('complex') composite = Predicate('composite') even = Predicate('even') extended_real = Predicate('extended_real') imaginary = Predicate('imaginary') infinitesimal = Predicate('infinitesimal') infinity = Predicate('infinity') integer = Predicate('integer') irrational = Predicate('irrational') rational = Predicate('rational') negative = Predicate('negative') nonzero = Predicate('nonzero') positive = Predicate('positive') prime = Predicate('prime') real = Predicate('real') odd = Predicate('odd') is_true = Predicate('is_true') def _extract_facts(expr, symbol): """ Helper for ask(). Extracts the facts relevant to the symbol from an assumption. Returns None if there is nothing to extract. """ if not expr.has(symbol): return None if isinstance(expr, AppliedPredicate): return expr.func return expr.func(*filter(lambda x: x is not None, [_extract_facts(arg, symbol) for arg in expr.args])) def ask(proposition, assumptions=True, context=global_assumptions): """ Method for inferring properties about objects. **Syntax** * ask(proposition) * ask(proposition, assumptions) where ``proposition`` is any boolean expression **Examples** >>> from sympy import ask, Q, pi >>> from sympy.abc import x, y >>> ask(Q.rational(pi)) False >>> ask(Q.even(x*y), Q.even(x) & Q.integer(y)) True >>> ask(Q.prime(x*y), Q.integer(x) & Q.integer(y)) False **Remarks** Relations in assumptions are not implemented (yet), so the following will not give a meaningful result. >>> ask(Q.positive(x), Q.is_true(x > 0)) # doctest: +SKIP It is however a work in progress and should be available before the official release """ assumptions = And(assumptions, And(*context)) if isinstance(proposition, AppliedPredicate): key, expr = proposition.func, sympify(proposition.arg) else: key, expr = Q.is_true, sympify(proposition) # direct resolution method, no logic res = key(expr)._eval_ask(assumptions) if res is not None: return res if assumptions is True: return if not expr.is_Atom: return local_facts = _extract_facts(assumptions, expr) if local_facts is None or local_facts is True: return # See if there's a straight-forward conclusion we can make for the inference if local_facts.is_Atom: if key in known_facts_dict[local_facts]: return True if Not(key) in known_facts_dict[local_facts]: return False elif local_facts.func is And: for assum in local_facts.args: if assum.is_Atom: if key in known_facts_dict[assum]: return True if Not(key) in known_facts_dict[assum]: return False elif assum.func is Not and assum.args[0].is_Atom: if key in known_facts_dict[assum]: return False if Not(key) in known_facts_dict[assum]: return True elif (isinstance(key, Predicate) and local_facts.func is Not and local_facts.args[0].is_Atom): if local_facts.args[0] in known_facts_dict[key]: return False # Failing all else, we do a full logical inference return ask_full_inference(key, local_facts) def ask_full_inference(proposition, assumptions): """ Method for inferring properties about objects. """ if not satisfiable(And(known_facts_cnf, assumptions, proposition)): return False if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))): return True return None def register_handler(key, handler): """Register a handler in the ask system. key must be a string and handler a class inheriting from AskHandler. >>> from sympy.assumptions import register_handler, ask, Q >>> from sympy.assumptions.handlers import AskHandler >>> class MersenneHandler(AskHandler): ... # Mersenne numbers are in the form 2**n + 1, n integer ... @staticmethod ... def Integer(expr, assumptions): ... import math ... return ask(Q.integer(math.log(expr + 1, 2))) >>> register_handler('mersenne', MersenneHandler) >>> ask(Q.mersenne(7)) True """ if type(key) is Predicate: key = key.name try: getattr(Q, key).add_handler(handler) except AttributeError: setattr(Q, key, Predicate(key, handlers=[handler])) def remove_handler(key, handler): """Removes a handler from the ask system. Same syntax as register_handler""" if type(key) is Predicate: key = key.name getattr(Q, key).remove_handler(handler) def compute_known_facts(): """Compute the various forms of knowledge compilation used by the assumptions system. """ # Compute the known facts in CNF form for logical inference fact_string = "# -{ Known facts in CNF }-\n" cnf = to_cnf(known_facts) fact_string += "known_facts_cnf = And(\n " fact_string += ",\n ".join(map(str, cnf.args)) fact_string += "\n)\n" # Compute the quick lookup for single facts mapping = {} for key in known_facts_keys: mapping[key] = set([key]) for other_key in known_facts_keys: if other_key != key: if ask_full_inference(other_key, key): mapping[key].add(other_key) fact_string += "\n# -{ Known facts in compressed sets }-\n" fact_string += "known_facts_dict = {\n " fact_string += ",\n ".join(["%s: %s" % item for item in mapping.items()]) fact_string += "\n}\n" return fact_string # handlers_dict tells us what ask handler we should use # for a particular key _handlers_dict = { 'bounded' : ['sympy.assumptions.handlers.calculus.AskBoundedHandler'], 'commutative' : ['sympy.assumptions.handlers.AskCommutativeHandler'], 'complex' : ['sympy.assumptions.handlers.sets.AskComplexHandler'], 'composite' : ['sympy.assumptions.handlers.ntheory.AskCompositeHandler'], 'even' : ['sympy.assumptions.handlers.ntheory.AskEvenHandler'], 'extended_real' : ['sympy.assumptions.handlers.sets.AskExtendedRealHandler'], 'imaginary' : ['sympy.assumptions.handlers.sets.AskImaginaryHandler'], 'infinitesimal' : ['sympy.assumptions.handlers.calculus.AskInfinitesimalHandler'], 'integer' : ['sympy.assumptions.handlers.sets.AskIntegerHandler'], 'irrational' : ['sympy.assumptions.handlers.sets.AskIrrationalHandler'], 'rational' : ['sympy.assumptions.handlers.sets.AskRationalHandler'], 'negative' : ['sympy.assumptions.handlers.order.AskNegativeHandler'], 'nonzero' : ['sympy.assumptions.handlers.order.AskNonZeroHandler'], 'positive' : ['sympy.assumptions.handlers.order.AskPositiveHandler'], 'prime' : ['sympy.assumptions.handlers.ntheory.AskPrimeHandler'], 'real' : ['sympy.assumptions.handlers.sets.AskRealHandler'], 'odd' : ['sympy.assumptions.handlers.ntheory.AskOddHandler'], 'algebraic' : ['sympy.assumptions.handlers.sets.AskAlgebraicHandler'], 'is_true' : ['sympy.assumptions.handlers.TautologicalHandler'] } for name, value in _handlers_dict.iteritems(): register_handler(name, value[0]) known_facts_keys = [getattr(Q, attr) for attr in Q.__dict__ \ if not attr.startswith('__')] known_facts = And( Implies (Q.real, Q.complex), Equivalent(Q.even, Q.integer & ~Q.odd), Equivalent(Q.extended_real, Q.real | Q.infinity), Equivalent(Q.odd, Q.integer & ~Q.even), Equivalent(Q.prime, Q.integer & Q.positive & ~Q.composite), Implies (Q.integer, Q.rational), Implies (Q.imaginary, Q.complex & ~Q.real), Equivalent(Q.negative, Q.nonzero & ~Q.positive), Equivalent(Q.positive, Q.nonzero & ~Q.negative), Equivalent(Q.rational, Q.real & ~Q.irrational), Equivalent(Q.real, Q.rational | Q.irrational), Implies (Q.nonzero, Q.real), Equivalent(Q.nonzero, Q.positive | Q.negative) ) ################################################################################ # Note: The following facts are generated by the compute_known_facts function. # ################################################################################ # -{ Known facts in CNF }- known_facts_cnf = And( Or(Not(Q.integer), Q.even, Q.odd), Or(Not(Q.extended_real), Q.infinity, Q.real), Or(Not(Q.real), Q.irrational, Q.rational), Or(Not(Q.real), Q.complex), Or(Not(Q.integer), Not(Q.positive), Q.composite, Q.prime), Or(Not(Q.integer), Q.rational), Or(Not(Q.imaginary), Q.complex), Or(Not(Q.even), Q.integer), Or(Not(Q.positive), Q.nonzero), Or(Not(Q.nonzero), Q.negative, Q.positive), Or(Not(Q.prime), Q.positive), Or(Not(Q.rational), Q.real), Or(Not(Q.imaginary), Not(Q.real)), Or(Not(Q.odd), Q.integer), Or(Not(Q.real), Q.extended_real), Or(Not(Q.composite), Not(Q.prime)), Or(Not(Q.negative), Q.nonzero), Or(Not(Q.negative), Not(Q.positive)), Or(Not(Q.prime), Q.integer), Or(Not(Q.even), Not(Q.odd)), Or(Not(Q.nonzero), Q.real), Or(Not(Q.irrational), Q.real), Or(Not(Q.irrational), Not(Q.rational)), Or(Not(Q.infinity), Q.extended_real) ) # -{ Known facts in compressed sets }- known_facts_dict = { Q.is_true: set([Q.is_true]), Q.complex: set([Q.complex]), Q.odd: set([Q.complex, Q.odd, Q.real, Q.rational, Q.extended_real, Q.integer]), Q.positive: set([Q.real, Q.complex, Q.extended_real, Q.positive, Q.nonzero]), Q.real: set([Q.real, Q.complex, Q.extended_real]), Q.composite: set([Q.composite]), Q.bounded: set([Q.bounded]), Q.prime: set([Q.real, Q.complex, Q.positive, Q.nonzero, Q.prime, Q.rational, Q.extended_real, Q.integer]), Q.infinitesimal: set([Q.infinitesimal]), Q.even: set([Q.complex, Q.real, Q.even, Q.rational, Q.extended_real, Q.integer]), Q.negative: set([Q.real, Q.negative, Q.complex, Q.extended_real, Q.nonzero]), Q.rational: set([Q.real, Q.rational, Q.complex, Q.extended_real]), Q.extended_real: set([Q.extended_real]), Q.nonzero: set([Q.nonzero, Q.complex, Q.extended_real, Q.real]), Q.integer: set([Q.real, Q.rational, Q.complex, Q.extended_real, Q.integer]), Q.irrational: set([Q.real, Q.irrational, Q.complex, Q.extended_real]), Q.commutative: set([Q.commutative]), Q.infinity: set([Q.extended_real, Q.infinity]), Q.algebraic: set([Q.algebraic]), Q.imaginary: set([Q.complex, Q.imaginary]) } wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/assume.py0000644000175000017500000001043512014170666025336 0ustar georgeskgeorgeskimport inspect from sympy.utilities.source import get_class from sympy.logic.boolalg import Boolean, Not class AssumptionsContext(set): """Set representing assumptions. This is used to represent global assumptions, but you can also use this class to create your own local assumptions contexts. It is basically a thin wrapper to Python's set, so see its documentation for advanced usage. Examples: >>> from sympy import global_assumptions, AppliedPredicate, Q >>> global_assumptions AssumptionsContext() >>> from sympy.abc import x >>> global_assumptions.add(Q.real(x)) >>> global_assumptions AssumptionsContext([Q.real(x)]) >>> global_assumptions.remove(Q.real(x)) >>> global_assumptions AssumptionsContext() >>> global_assumptions.clear() """ def add(self, *assumptions): """Add an assumption.""" for a in assumptions: super(AssumptionsContext, self).add(a) global_assumptions = AssumptionsContext() class AppliedPredicate(Boolean): """The class of expressions resulting from applying a Predicate. >>> from sympy import Q, Symbol >>> x = Symbol('x') >>> Q.integer(x) Q.integer(x) >>> type(Q.integer(x)) """ __slots__ = [] def __new__(cls, predicate, arg): return Boolean.__new__(cls, predicate, arg) is_Atom = True # do not attempt to decompose this @property def arg(self): """ Return the expression used by this assumption. Examples: >>> from sympy import Q, Symbol >>> x = Symbol('x') >>> a = Q.integer(x + 1) >>> a.arg x + 1 """ return self._args[1] @property def args(self): return self._args[1:] @property def func(self): return self._args[0] def __eq__(self, other): if type(other) is AppliedPredicate: return self._args == other._args return False def __hash__(self): return super(AppliedPredicate, self).__hash__() def _eval_ask(self, assumptions): return self.func.eval(self.arg, assumptions) class Predicate(Boolean): """A predicate is a function that returns a boolean value. Predicates merely wrap their argument and remain unevaluated: >>> from sympy import Q, ask, Symbol >>> x = Symbol('x') >>> Q.prime(7) Q.prime(7) To obtain the truth value of an expression containing predicates, use the function `ask`: >>> ask(Q.prime(7)) True The tautological predicate `Q.is_true` can be used to wrap other objects: >>> Q.is_true(x > 1) Q.is_true(1 < x) """ is_Atom = True def __new__(cls, name, handlers=None): obj = Boolean.__new__(cls) obj.name = name obj.handlers = handlers or [] return obj def _hashable_content(self): return (self.name,) def __getnewargs__(self): return (self.name,) def __call__(self, expr): return AppliedPredicate(self, expr) def add_handler(self, handler): self.handlers.append(handler) def remove_handler(self, handler): self.handlers.remove(handler) def eval(self, expr, assumptions=True): """ Evaluate self(expr) under the given assumptions. This uses only direct resolution methods, not logical inference. """ res, _res = None, None mro = inspect.getmro(type(expr)) for handler in self.handlers: cls = get_class(handler) for subclass in mro: try: eval = getattr(cls, subclass.__name__) except AttributeError: continue res = eval(expr, assumptions) if _res is None: _res = res elif res is None: # since first resolutor was conclusive, we keep that value res = _res else: # only check consistency if both resolutors have concluded if _res != res: raise ValueError('incompatible resolutors') break return res wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/tests/0000755000175000017500000000000012014170666024626 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/tests/test_assumptions_2.py0000644000175000017500000000346512014170666031055 0ustar georgeskgeorgesk"""rename this to test_assumptions.py when the old assumptions system is deleted""" from sympy.core import symbols from sympy.assumptions import AppliedPredicate, global_assumptions, Predicate from sympy.assumptions.ask import _extract_facts from sympy.printing import pretty from sympy.assumptions.ask import Q from sympy.logic.boolalg import Or from sympy.utilities.pytest import XFAIL def test_equal(): """Test for equality""" x = symbols('x') assert Q.positive(x) == Q.positive(x) assert Q.positive(x) != ~Q.positive(x) assert ~Q.positive(x) == ~Q.positive(x) def test_pretty(): x = symbols('x') assert pretty(Q.positive(x)) == "Q.positive(x)" def test_extract_facts(): a, b = symbols('a b', cls=Predicate) x, y = symbols('x y') assert _extract_facts(a(x), x) == a assert _extract_facts(a(x), y) == None assert _extract_facts(~a(x), x) == ~a assert _extract_facts(~a(x), y) == None assert _extract_facts(a(x) | b(x), x) == a | b assert _extract_facts(a(x) | ~b(x), x) == a | ~b def test_global(): """Test for global assumptions""" x, y = symbols('x,y') global_assumptions.add(Q.is_true(x > 0)) assert Q.is_true(x > 0) in global_assumptions global_assumptions.remove(Q.is_true(x > 0)) assert not Q.is_true(x > 0) in global_assumptions # same with multiple of assumptions global_assumptions.add(Q.is_true(x > 0), Q.is_true(y > 0)) assert Q.is_true(x > 0) in global_assumptions assert Q.is_true(y > 0) in global_assumptions global_assumptions.clear() assert not Q.is_true(x > 0) in global_assumptions assert not Q.is_true(y > 0) in global_assumptions def test_composite_predicates(): x = symbols('x') pred = Q.integer | ~Q.positive assert type(pred(x)) is Or assert pred(x) == Q.integer(x) | ~Q.positive(x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/tests/test_query.py0000644000175000017500000011750012014170666027410 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import raises, XFAIL from sympy.core import Symbol, symbols, S, Rational, Integer, I, pi, oo from sympy.functions import exp, log, sin, cos, sign, re, im, sqrt, Abs from sympy.assumptions import (global_assumptions, Q, ask, register_handler, remove_handler, AssumptionsContext) from sympy.assumptions.handlers import AskHandler from sympy.assumptions.ask import (compute_known_facts, known_facts_cnf, known_facts_dict) def test_int_1(): z = 1 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == True assert ask(Q.rational(z)) == True assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == True assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == True def test_float_1(): z = 1.0 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == True assert ask(Q.rational(z)) == True assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == True assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == True z = 7.2123 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == True assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_zero_0(): z = Integer(0) assert ask(Q.nonzero(z)) == False assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == True assert ask(Q.rational(z)) == True assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == False assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == True assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == True assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_negativeone(): z = Integer(-1) assert ask(Q.nonzero(z)) == True assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == True assert ask(Q.rational(z)) == True assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == False assert ask(Q.negative(z)) == True assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == True assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_infinity(): oo = S.Infinity assert ask(Q.commutative(oo)) == True assert ask(Q.integer(oo)) == False assert ask(Q.rational(oo)) == False assert ask(Q.real(oo)) == False assert ask(Q.extended_real(oo)) == True assert ask(Q.complex(oo)) == False assert ask(Q.irrational(oo)) == False assert ask(Q.imaginary(oo)) == False assert ask(Q.positive(oo)) == True assert ask(Q.negative(oo)) == False assert ask(Q.even(oo)) == False assert ask(Q.odd(oo)) == False assert ask(Q.bounded(oo)) == False assert ask(Q.infinitesimal(oo)) == False assert ask(Q.prime(oo)) == False assert ask(Q.composite(oo)) == False def test_neg_infinity(): mm = S.NegativeInfinity assert ask(Q.commutative(mm)) == True assert ask(Q.integer(mm)) == False assert ask(Q.rational(mm)) == False assert ask(Q.real(mm)) == False assert ask(Q.extended_real(mm)) == True assert ask(Q.complex(mm)) == False assert ask(Q.irrational(mm)) == False assert ask(Q.imaginary(mm)) == False assert ask(Q.positive(mm)) == False assert ask(Q.negative(mm)) == True assert ask(Q.even(mm)) == False assert ask(Q.odd(mm)) == False assert ask(Q.bounded(mm)) == False assert ask(Q.infinitesimal(mm)) == False assert ask(Q.prime(mm)) == False assert ask(Q.composite(mm)) == False def test_nan(): nan = S.NaN assert ask(Q.commutative(nan)) == True assert ask(Q.integer(nan)) == False assert ask(Q.rational(nan)) == False assert ask(Q.real(nan)) == False assert ask(Q.extended_real(nan)) == False assert ask(Q.complex(nan)) == False assert ask(Q.irrational(nan)) == False assert ask(Q.imaginary(nan)) == False assert ask(Q.positive(nan)) == False assert ask(Q.nonzero(nan)) == True assert ask(Q.even(nan)) == False assert ask(Q.odd(nan)) == False assert ask(Q.bounded(nan)) == False assert ask(Q.infinitesimal(nan)) == False assert ask(Q.prime(nan)) == False assert ask(Q.composite(nan)) == False def test_Rational_number(): r = Rational(3,4) assert ask(Q.commutative(r)) == True assert ask(Q.integer(r)) == False assert ask(Q.rational(r)) == True assert ask(Q.real(r)) == True assert ask(Q.complex(r)) == True assert ask(Q.irrational(r)) == False assert ask(Q.imaginary(r)) == False assert ask(Q.positive(r)) == True assert ask(Q.negative(r)) == False assert ask(Q.even(r)) == False assert ask(Q.odd(r)) == False assert ask(Q.bounded(r)) == True assert ask(Q.infinitesimal(r)) == False assert ask(Q.prime(r)) == False assert ask(Q.composite(r)) == False r = Rational(1,4) assert ask(Q.positive(r)) == True assert ask(Q.negative(r)) == False r = Rational(5,4) assert ask(Q.negative(r)) == False assert ask(Q.positive(r)) == True r = Rational(5,3) assert ask(Q.positive(r)) == True assert ask(Q.negative(r)) == False r = Rational(-3,4) assert ask(Q.positive(r)) == False assert ask(Q.negative(r)) == True r = Rational(-1,4) assert ask(Q.positive(r)) == False assert ask(Q.negative(r)) == True r = Rational(-5,4) assert ask(Q.negative(r)) == True assert ask(Q.positive(r)) == False r = Rational(-5,3) assert ask(Q.positive(r)) == False assert ask(Q.negative(r)) == True def test_sqrt_2(): z = sqrt(2) assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_pi(): z = S.Pi assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False z = S.Pi + 1 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False z = 2*S.Pi assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False z = S.Pi ** 2 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False z = (1+S.Pi) ** 2 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_E(): z = S.Exp1 assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == True assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == True assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == True assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_I(): I = S.ImaginaryUnit z = I assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == False assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == True assert ask(Q.positive(z)) == False assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False z = 1 + I assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == False assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == False assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False z = I*(1+I) assert ask(Q.commutative(z)) == True assert ask(Q.integer(z)) == False assert ask(Q.rational(z)) == False assert ask(Q.real(z)) == False assert ask(Q.complex(z)) == True assert ask(Q.irrational(z)) == False assert ask(Q.imaginary(z)) == False assert ask(Q.positive(z)) == False assert ask(Q.negative(z)) == False assert ask(Q.even(z)) == False assert ask(Q.odd(z)) == False assert ask(Q.bounded(z)) == True assert ask(Q.infinitesimal(z)) == False assert ask(Q.prime(z)) == False assert ask(Q.composite(z)) == False def test_bounded(): x, y = symbols('x,y') assert ask(Q.bounded(x)) == False assert ask(Q.bounded(x), Q.bounded(x)) == True assert ask(Q.bounded(x), Q.bounded(y)) == False assert ask(Q.bounded(x), Q.complex(x)) == False assert ask(Q.bounded(x+1)) == False assert ask(Q.bounded(x+1), Q.bounded(x)) == True assert ask(Q.bounded(x+y)) == None assert ask(Q.bounded(x+y), Q.bounded(x)) == False assert ask(Q.bounded(x+1), Q.bounded(x) & Q.bounded(y)) == True assert ask(Q.bounded(2*x)) == False assert ask(Q.bounded(2*x), Q.bounded(x)) == True assert ask(Q.bounded(x*y)) == None assert ask(Q.bounded(x*y), Q.bounded(x)) == False assert ask(Q.bounded(x*y), Q.bounded(x) & Q.bounded(y)) == True assert ask(Q.bounded(x**2)) == False assert ask(Q.bounded(2**x)) == False assert ask(Q.bounded(2**x), Q.bounded(x)) == True assert ask(Q.bounded(x**x)) == False assert ask(Q.bounded(Rational(1,2) ** x)) == True assert ask(Q.bounded(x ** Rational(1,2))) == False # sign function assert ask(Q.bounded(sign(x))) == True assert ask(Q.bounded(sign(x)), ~Q.bounded(x)) == True # exponential functions assert ask(Q.bounded(log(x))) == False assert ask(Q.bounded(log(x)), Q.bounded(x)) == True assert ask(Q.bounded(exp(x))) == False assert ask(Q.bounded(exp(x)), Q.bounded(x)) == True assert ask(Q.bounded(exp(2))) == True # trigonometric functions assert ask(Q.bounded(sin(x))) == True assert ask(Q.bounded(sin(x)), ~Q.bounded(x)) == True assert ask(Q.bounded(cos(x))) == True assert ask(Q.bounded(cos(x)), ~Q.bounded(x)) == True assert ask(Q.bounded(2*sin(x))) == True assert ask(Q.bounded(sin(x)**2)) == True assert ask(Q.bounded(cos(x)**2)) == True assert ask(Q.bounded(cos(x) + sin(x))) == True @XFAIL def test_bounded_xfail(): """We need to support relations in ask for this to work""" x = Symbol('x') assert ask(Q.bounded(sin(x)**x)) == True assert ask(Q.bounded(cos(x)**x)) == True assert ask(Q.bounded(sin(x) ** x)) == True def test_commutative(): """By default objects are Q.commutative that is why it returns True for both key=True and key=False""" x, y = symbols('x,y') assert ask(Q.commutative(x)) == True assert ask(Q.commutative(x), ~Q.commutative(x)) == False assert ask(Q.commutative(x), Q.complex(x)) == True assert ask(Q.commutative(x), Q.imaginary(x)) == True assert ask(Q.commutative(x), Q.real(x)) == True assert ask(Q.commutative(x), Q.positive(x)) == True assert ask(Q.commutative(x), ~Q.commutative(y)) == True assert ask(Q.commutative(2*x)) == True assert ask(Q.commutative(2*x), ~Q.commutative(x)) == False assert ask(Q.commutative(x + 1)) == True assert ask(Q.commutative(x + 1), ~Q.commutative(x)) == False assert ask(Q.commutative(x**2)) == True assert ask(Q.commutative(x**2), ~Q.commutative(x)) == False assert ask(Q.commutative(log(x))) == True def test_complex(): x, y = symbols('x,y') assert ask(Q.complex(x)) == None assert ask(Q.complex(x), Q.complex(x)) == True assert ask(Q.complex(x), Q.complex(y)) == None assert ask(Q.complex(x), ~Q.complex(x)) == False assert ask(Q.complex(x), Q.real(x)) == True assert ask(Q.complex(x), ~Q.real(x)) == None assert ask(Q.complex(x), Q.rational(x)) == True assert ask(Q.complex(x), Q.irrational(x)) == True assert ask(Q.complex(x), Q.positive(x)) == True assert ask(Q.complex(x), Q.imaginary(x)) == True # a+b assert ask(Q.complex(x+1), Q.complex(x)) == True assert ask(Q.complex(x+1), Q.real(x)) == True assert ask(Q.complex(x+1), Q.rational(x)) == True assert ask(Q.complex(x+1), Q.irrational(x)) == True assert ask(Q.complex(x+1), Q.imaginary(x)) == True assert ask(Q.complex(x+1), Q.integer(x)) == True assert ask(Q.complex(x+1), Q.even(x)) == True assert ask(Q.complex(x+1), Q.odd(x)) == True assert ask(Q.complex(x+y), Q.complex(x) & Q.complex(y)) == True assert ask(Q.complex(x+y), Q.real(x) & Q.imaginary(y)) == True # a*x +b assert ask(Q.complex(2*x+1), Q.complex(x)) == True assert ask(Q.complex(2*x+1), Q.real(x)) == True assert ask(Q.complex(2*x+1), Q.positive(x)) == True assert ask(Q.complex(2*x+1), Q.rational(x)) == True assert ask(Q.complex(2*x+1), Q.irrational(x)) == True assert ask(Q.complex(2*x+1), Q.imaginary(x)) == True assert ask(Q.complex(2*x+1), Q.integer(x)) == True assert ask(Q.complex(2*x+1), Q.even(x)) == True assert ask(Q.complex(2*x+1), Q.odd(x)) == True # x**2 assert ask(Q.complex(x**2), Q.complex(x)) == True assert ask(Q.complex(x**2), Q.real(x)) == True assert ask(Q.complex(x**2), Q.positive(x)) == True assert ask(Q.complex(x**2), Q.rational(x)) == True assert ask(Q.complex(x**2), Q.irrational(x)) == True assert ask(Q.complex(x**2), Q.imaginary(x)) == True assert ask(Q.complex(x**2), Q.integer(x)) == True assert ask(Q.complex(x**2), Q.even(x)) == True assert ask(Q.complex(x**2), Q.odd(x)) == True # 2**x assert ask(Q.complex(2**x), Q.complex(x)) == True assert ask(Q.complex(2**x), Q.real(x)) == True assert ask(Q.complex(2**x), Q.positive(x)) == True assert ask(Q.complex(2**x), Q.rational(x)) == True assert ask(Q.complex(2**x), Q.irrational(x)) == True assert ask(Q.complex(2**x), Q.imaginary(x)) == True assert ask(Q.complex(2**x), Q.integer(x)) == True assert ask(Q.complex(2**x), Q.even(x)) == True assert ask(Q.complex(2**x), Q.odd(x)) == True assert ask(Q.complex(x**y), Q.complex(x) & Q.complex(y)) == True # trigonometric expressions assert ask(Q.complex(sin(x))) == True assert ask(Q.complex(sin(2*x + 1))) == True assert ask(Q.complex(cos(x))) == True assert ask(Q.complex(cos(2*x+1))) == True # exponential assert ask(Q.complex(exp(x))) == True assert ask(Q.complex(exp(x))) == True # Q.complexes assert ask(Q.complex(Abs(x))) == True assert ask(Q.complex(re(x))) == True assert ask(Q.complex(im(x))) == True def test_even(): x, y, z, t = symbols('x,y,z,t') assert ask(Q.even(x)) == None assert ask(Q.even(x), Q.integer(x)) == None assert ask(Q.even(x), ~Q.integer(x)) == False assert ask(Q.even(x), Q.rational(x)) == None assert ask(Q.even(x), Q.positive(x)) == None assert ask(Q.even(2*x)) == None assert ask(Q.even(2*x), Q.integer(x)) == True assert ask(Q.even(2*x), Q.even(x)) == True assert ask(Q.even(2*x), Q.irrational(x)) == False assert ask(Q.even(2*x), Q.odd(x)) == True assert ask(Q.even(2*x), ~Q.integer(x)) == None assert ask(Q.even(3*x), Q.integer(x)) == None assert ask(Q.even(3*x), Q.even(x)) == True assert ask(Q.even(3*x), Q.odd(x)) == False assert ask(Q.even(x+1), Q.odd(x)) == True assert ask(Q.even(x+1), Q.even(x)) == False assert ask(Q.even(x+2), Q.odd(x)) == False assert ask(Q.even(x+2), Q.even(x)) == True assert ask(Q.even(7-x), Q.odd(x)) == True assert ask(Q.even(7+x), Q.odd(x)) == True assert ask(Q.even(x+y), Q.odd(x) & Q.odd(y)) == True assert ask(Q.even(x+y), Q.odd(x) & Q.even(y)) == False assert ask(Q.even(x+y), Q.even(x) & Q.even(y)) == True assert ask(Q.even(2*x + 1), Q.integer(x)) == False assert ask(Q.even(2*x*y), Q.rational(x) & Q.rational(x)) == None assert ask(Q.even(2*x*y), Q.irrational(x) & Q.irrational(x)) == None assert ask(Q.even(x+y+z), Q.odd(x) & Q.odd(y) & Q.even(z)) == True assert ask(Q.even(x+y+z+t), Q.odd(x) & Q.odd(y) & Q.even(z) & Q.integer(t)) == None assert ask(Q.even(Abs(x)), Q.even(x)) == True assert ask(Q.even(Abs(x)), ~Q.even(x)) == None assert ask(Q.even(re(x)), Q.even(x)) == True assert ask(Q.even(re(x)), ~Q.even(x)) == None assert ask(Q.even(im(x)), Q.even(x)) == True assert ask(Q.even(im(x)), Q.real(x)) == True def test_extended_real(): x = symbols('x') assert ask(Q.extended_real(x), Q.positive(x)) == True assert ask(Q.extended_real(-x), Q.positive(x)) == True assert ask(Q.extended_real(-x), Q.negative(x)) == True assert ask(Q.extended_real(x+S.Infinity), Q.real(x)) == True def test_rational(): x, y = symbols('x,y') assert ask(Q.rational(x), Q.integer(x)) == True assert ask(Q.rational(x), Q.irrational(x)) == False assert ask(Q.rational(x), Q.real(x)) == None assert ask(Q.rational(x), Q.positive(x)) == None assert ask(Q.rational(x), Q.negative(x)) == None assert ask(Q.rational(x), Q.nonzero(x)) == None assert ask(Q.rational(2*x), Q.rational(x)) == True assert ask(Q.rational(2*x), Q.integer(x)) == True assert ask(Q.rational(2*x), Q.even(x)) == True assert ask(Q.rational(2*x), Q.odd(x)) == True assert ask(Q.rational(2*x), Q.irrational(x)) == False assert ask(Q.rational(x/2), Q.rational(x)) == True assert ask(Q.rational(x/2), Q.integer(x)) == True assert ask(Q.rational(x/2), Q.even(x)) == True assert ask(Q.rational(x/2), Q.odd(x)) == True assert ask(Q.rational(x/2), Q.irrational(x)) == False assert ask(Q.rational(1/x), Q.rational(x)) == True assert ask(Q.rational(1/x), Q.integer(x)) == True assert ask(Q.rational(1/x), Q.even(x)) == True assert ask(Q.rational(1/x), Q.odd(x)) == True assert ask(Q.rational(1/x), Q.irrational(x)) == False assert ask(Q.rational(2/x), Q.rational(x)) == True assert ask(Q.rational(2/x), Q.integer(x)) == True assert ask(Q.rational(2/x), Q.even(x)) == True assert ask(Q.rational(2/x), Q.odd(x)) == True assert ask(Q.rational(2/x), Q.irrational(x)) == False # with multiple symbols assert ask(Q.rational(x*y), Q.irrational(x) & Q.irrational(y)) == None assert ask(Q.rational(y/x), Q.rational(x) & Q.rational(y)) == True assert ask(Q.rational(y/x), Q.integer(x) & Q.rational(y)) == True assert ask(Q.rational(y/x), Q.even(x) & Q.rational(y)) == True assert ask(Q.rational(y/x), Q.odd(x) & Q.rational(y)) == True assert ask(Q.rational(y/x), Q.irrational(x) & Q.rational(y)) == False def test_imaginary(): x, y, z = symbols('x,y,z') I = S.ImaginaryUnit assert ask(Q.imaginary(x)) == None assert ask(Q.imaginary(x), Q.real(x)) == False assert ask(Q.imaginary(x), Q.prime(x)) == False assert ask(Q.imaginary(x+1), Q.real(x)) == False assert ask(Q.imaginary(x+1), Q.imaginary(x)) == False assert ask(Q.imaginary(x+I), Q.real(x)) == False assert ask(Q.imaginary(x+I), Q.imaginary(x)) == True assert ask(Q.imaginary(x+y), Q.imaginary(x) & Q.imaginary(y)) == True assert ask(Q.imaginary(x+y), Q.real(x) & Q.real(y)) == False assert ask(Q.imaginary(x+y), Q.imaginary(x) & Q.real(y)) == False assert ask(Q.imaginary(x+y), Q.complex(x) & Q.real(y)) == None assert ask(Q.imaginary(I*x), Q.real(x)) == True assert ask(Q.imaginary(I*x), Q.imaginary(x)) == False assert ask(Q.imaginary(I*x), Q.complex(x)) == None assert ask(Q.imaginary(x*y), Q.imaginary(x) & Q.real(y)) == True assert ask(Q.imaginary(x+y+z), Q.real(x) & Q.real(y) & Q.real(z)) == False assert ask(Q.imaginary(x+y+z), Q.real(x) & Q.real(y) & Q.imaginary(z)) == None assert ask(Q.imaginary(x+y+z), Q.real(x) & Q.imaginary(y) & Q.imaginary(z)) == False def test_infinitesimal(): x, y = symbols('x,y') assert ask(Q.infinitesimal(x)) == None assert ask(Q.infinitesimal(x), Q.infinitesimal(x)) == True assert ask(Q.infinitesimal(2*x), Q.infinitesimal(x)) == True assert ask(Q.infinitesimal(x*y), Q.infinitesimal(x)) == None assert ask(Q.infinitesimal(x*y), Q.infinitesimal(x) & Q.infinitesimal(y)) == True assert ask(Q.infinitesimal(x*y), Q.infinitesimal(x) & Q.bounded(y)) == True assert ask(Q.infinitesimal(x**2), Q.infinitesimal(x)) == True def test_integer(): x = symbols('x') assert ask(Q.integer(x)) == None assert ask(Q.integer(x), Q.integer(x)) == True assert ask(Q.integer(x), ~Q.integer(x)) == False assert ask(Q.integer(x), ~Q.real(x)) == False assert ask(Q.integer(x), ~Q.positive(x)) == None assert ask(Q.integer(x), Q.even(x) | Q.odd(x)) == True assert ask(Q.integer(2*x), Q.integer(x)) == True assert ask(Q.integer(2*x), Q.even(x)) == True assert ask(Q.integer(2*x), Q.prime(x)) == True assert ask(Q.integer(2*x), Q.rational(x)) == None assert ask(Q.integer(2*x), Q.real(x)) == None assert ask(Q.integer(sqrt(2)*x), Q.integer(x)) == False assert ask(Q.integer(x/2), Q.odd(x)) == False assert ask(Q.integer(x/2), Q.even(x)) == True assert ask(Q.integer(x/3), Q.odd(x)) == None assert ask(Q.integer(x/3), Q.even(x)) == None def test_negative(): x, y = symbols('x,y') assert ask(Q.negative(x), Q.negative(x)) == True assert ask(Q.negative(x), Q.positive(x)) == False assert ask(Q.negative(x), ~Q.real(x)) == False assert ask(Q.negative(x), Q.prime(x)) == False assert ask(Q.negative(x), ~Q.prime(x)) == None assert ask(Q.negative(-x), Q.positive(x)) == True assert ask(Q.negative(-x), ~Q.positive(x)) == None assert ask(Q.negative(-x), Q.negative(x)) == False assert ask(Q.negative(-x), Q.positive(x)) == True assert ask(Q.negative(x-1), Q.negative(x)) == True assert ask(Q.negative(x+y)) == None assert ask(Q.negative(x+y), Q.negative(x)) == None assert ask(Q.negative(x+y), Q.negative(x) & Q.negative(y)) == True assert ask(Q.negative(x**2)) == None assert ask(Q.negative(x**2), Q.real(x)) == False assert ask(Q.negative(x**1.4), Q.real(x)) == None assert ask(Q.negative(x*y)) == None assert ask(Q.negative(x*y), Q.positive(x) & Q.positive(y)) == False assert ask(Q.negative(x*y), Q.positive(x) & Q.negative(y)) == True assert ask(Q.negative(x*y), Q.complex(x) & Q.complex(y)) == None assert ask(Q.negative(x**y)) == None assert ask(Q.negative(x**y), Q.negative(x) & Q.even(y)) == False assert ask(Q.negative(x**y), Q.negative(x) & Q.odd(y)) == True assert ask(Q.negative(x**y), Q.positive(x) & Q.integer(y)) == False assert ask(Q.negative(Abs(x))) == False def test_nonzero(): x, y = symbols('x,y') assert ask(Q.nonzero(x)) == None assert ask(Q.nonzero(x), Q.real(x)) == None assert ask(Q.nonzero(x), Q.positive(x)) == True assert ask(Q.nonzero(x), Q.negative(x)) == True assert ask(Q.nonzero(x), Q.negative(x) | Q.positive(x)) == True assert ask(Q.nonzero(x+y)) == None assert ask(Q.nonzero(x+y), Q.positive(x) & Q.positive(y)) == True assert ask(Q.nonzero(x+y), Q.positive(x) & Q.negative(y)) == None assert ask(Q.nonzero(x+y), Q.negative(x) & Q.negative(y)) == True assert ask(Q.nonzero(2*x)) == None assert ask(Q.nonzero(2*x), Q.positive(x)) == True assert ask(Q.nonzero(2*x), Q.negative(x)) == True assert ask(Q.nonzero(x*y), Q.nonzero(x)) == None assert ask(Q.nonzero(x*y), Q.nonzero(x) & Q.nonzero(y)) == True assert ask(Q.nonzero(Abs(x))) == None assert ask(Q.nonzero(Abs(x)), Q.nonzero(x)) == True def test_odd(): x, y, z, t = symbols('x,y,z,t') assert ask(Q.odd(x)) == None assert ask(Q.odd(x), Q.odd(x)) == True assert ask(Q.odd(x), Q.integer(x)) == None assert ask(Q.odd(x), ~Q.integer(x)) == False assert ask(Q.odd(x), Q.rational(x)) == None assert ask(Q.odd(x), Q.positive(x)) == None assert ask(Q.odd(-x), Q.odd(x)) == True assert ask(Q.odd(2*x)) == None assert ask(Q.odd(2*x), Q.integer(x)) == False assert ask(Q.odd(2*x), Q.odd(x)) == False assert ask(Q.odd(2*x), Q.irrational(x)) == False assert ask(Q.odd(2*x), ~Q.integer(x)) == None assert ask(Q.odd(3*x), Q.integer(x)) == None assert ask(Q.odd(x/3), Q.odd(x)) == None assert ask(Q.odd(x/3), Q.even(x)) == None assert ask(Q.odd(x+1), Q.even(x)) == True assert ask(Q.odd(x+2), Q.even(x)) == False assert ask(Q.odd(x+2), Q.odd(x)) == True assert ask(Q.odd(3-x), Q.odd(x)) == False assert ask(Q.odd(3-x), Q.even(x)) == True assert ask(Q.odd(3+x), Q.odd(x)) == False assert ask(Q.odd(3+x), Q.even(x)) == True assert ask(Q.odd(x+y), Q.odd(x) & Q.odd(y)) == False assert ask(Q.odd(x+y), Q.odd(x) & Q.even(y)) == True assert ask(Q.odd(x-y), Q.even(x) & Q.odd(y)) == True assert ask(Q.odd(x-y), Q.odd(x) & Q.odd(y)) == False assert ask(Q.odd(x+y+z), Q.odd(x) & Q.odd(y) & Q.even(z)) == False assert ask(Q.odd(x+y+z+t), Q.odd(x) & Q.odd(y) & Q.even(z) & Q.integer(t)) == None assert ask(Q.odd(2*x + 1), Q.integer(x)) == True assert ask(Q.odd(2*x + y), Q.integer(x) & Q.odd(y)) == True assert ask(Q.odd(2*x + y), Q.integer(x) & Q.even(y)) == False assert ask(Q.odd(2*x + y), Q.integer(x) & Q.integer(y)) == None assert ask(Q.odd(x*y), Q.odd(x) & Q.even(y)) == False assert ask(Q.odd(x*y), Q.odd(x) & Q.odd(y)) == True assert ask(Q.odd(2*x*y), Q.rational(x) & Q.rational(x)) == None assert ask(Q.odd(2*x*y), Q.irrational(x) & Q.irrational(x)) == None assert ask(Q.odd(Abs(x)), Q.odd(x)) == True def test_prime(): x, y = symbols('x,y') assert ask(Q.prime(x), Q.prime(x)) == True assert ask(Q.prime(x), ~Q.prime(x)) == False assert ask(Q.prime(x), Q.integer(x)) == None assert ask(Q.prime(x), ~Q.integer(x)) == False assert ask(Q.prime(2*x), Q.integer(x)) == False assert ask(Q.prime(x*y)) == None assert ask(Q.prime(x*y), Q.prime(x)) == None assert ask(Q.prime(x*y), Q.integer(x) & Q.integer(y)) == False assert ask(Q.prime(x**2), Q.integer(x)) == False assert ask(Q.prime(x**2), Q.prime(x)) == False assert ask(Q.prime(x**y), Q.integer(x) & Q.integer(y)) == False def test_positive(): x, y, z, w = symbols('x,y,z,w') assert ask(Q.positive(x), Q.positive(x)) == True assert ask(Q.positive(x), Q.negative(x)) == False assert ask(Q.positive(x), Q.nonzero(x)) == None assert ask(Q.positive(-x), Q.positive(x)) == False assert ask(Q.positive(-x), Q.negative(x)) == True assert ask(Q.positive(x+y), Q.positive(x) & Q.positive(y)) == True assert ask(Q.positive(x+y), Q.positive(x) & Q.negative(y)) == None assert ask(Q.positive(2*x), Q.positive(x)) == True assumptions = Q.positive(x) & Q.negative(y) & Q.negative(z) & Q.positive(w) assert ask(Q.positive(x*y*z)) == None assert ask(Q.positive(x*y*z), assumptions) == True assert ask(Q.positive(-x*y*z), assumptions) == False assert ask(Q.positive(x**2), Q.positive(x)) == True assert ask(Q.positive(x**2), Q.negative(x)) == True #exponential assert ask(Q.positive(exp(x)), Q.real(x)) == True assert ask(Q.positive(x + exp(x)), Q.real(x)) == None #absolute value assert ask(Q.positive(Abs(x))) == None # Abs(0) = 0 assert ask(Q.positive(Abs(x)), Q.positive(x)) == True @XFAIL def test_positive_xfail(): assert ask(Q.positive(1/(1 + x**2)), Q.real(x)) == True def test_real(): x, y = symbols('x,y') assert ask(Q.real(x)) == None assert ask(Q.real(x), Q.real(x)) == True assert ask(Q.real(x), Q.nonzero(x)) == True assert ask(Q.real(x), Q.positive(x)) == True assert ask(Q.real(x), Q.negative(x)) == True assert ask(Q.real(x), Q.integer(x)) == True assert ask(Q.real(x), Q.even(x)) == True assert ask(Q.real(x), Q.prime(x)) == True assert ask(Q.real(x/sqrt(2)), Q.real(x)) == True assert ask(Q.real(x/sqrt(-2)), Q.real(x)) == False I = S.ImaginaryUnit assert ask(Q.real(x+1), Q.real(x)) == True assert ask(Q.real(x+I), Q.real(x)) == False assert ask(Q.real(x+I), Q.complex(x)) == None assert ask(Q.real(2*x), Q.real(x)) == True assert ask(Q.real(I*x), Q.real(x)) == False assert ask(Q.real(I*x), Q.imaginary(x)) == True assert ask(Q.real(I*x), Q.complex(x)) == None assert ask(Q.real(x**2), Q.real(x)) == True assert ask(Q.real(sqrt(x)), Q.negative(x)) == False assert ask(Q.real(x**y), Q.real(x) & Q.integer(y)) == True assert ask(Q.real(x**y), Q.real(x) & Q.real(y)) == None assert ask(Q.real(x**y), Q.positive(x) & Q.real(y)) == True # trigonometric functions assert ask(Q.real(sin(x))) == None assert ask(Q.real(cos(x))) == None assert ask(Q.real(sin(x)), Q.real(x)) == True assert ask(Q.real(cos(x)), Q.real(x)) == True # exponential function assert ask(Q.real(exp(x))) == None assert ask(Q.real(exp(x)), Q.real(x)) == True assert ask(Q.real(x + exp(x)), Q.real(x)) == True # Q.complexes assert ask(Q.real(re(x))) == True assert ask(Q.real(im(x))) == True def test_algebraic(): x, y = symbols('x,y') assert ask(Q.algebraic(x)) == None assert ask(Q.algebraic(I)) == True assert ask(Q.algebraic(2*I)) == True assert ask(Q.algebraic(I/3)) == True assert ask(Q.algebraic(sqrt(7))) == True assert ask(Q.algebraic(2*sqrt(7))) == True assert ask(Q.algebraic(sqrt(7)/3)) == True assert ask(Q.algebraic(I*sqrt(3))) == True assert ask(Q.algebraic(sqrt(1+I*sqrt(3)))) == True assert ask(Q.algebraic((1+I*sqrt(3)**(S(17)/31)))) == True assert ask(Q.algebraic((1+I*sqrt(3)**(S(17)/pi)))) == False assert ask(Q.algebraic(sin(7))) == None assert ask(Q.algebraic(sqrt(sin(7)))) == None assert ask(Q.algebraic(sqrt(y+I*sqrt(7)))) == None assert ask(Q.algebraic(oo)) == False assert ask(Q.algebraic(-oo)) == False assert ask(Q.algebraic(2.47)) == False def test_global(): """Test ask with global assumptions""" x = symbols('x') assert ask(Q.integer(x)) == None global_assumptions.add(Q.integer(x)) assert ask(Q.integer(x)) == True global_assumptions.clear() assert ask(Q.integer(x)) == None def test_custom_context(): """Test ask with custom assumptions context""" x = symbols('x') assert ask(Q.integer(x)) == None local_context = AssumptionsContext() local_context.add(Q.integer(x)) assert ask(Q.integer(x), context = local_context) == True assert ask(Q.integer(x)) == None def test_functions_in_assumptions(): from sympy.logic.boolalg import Equivalent, Xor x = symbols('x') assert ask(Q.negative(x), Q.real(x) >> Q.positive(x)) is False assert ask(Q.negative(x), Equivalent(Q.real(x), Q.positive(x))) is False assert ask(Q.negative(x), Xor(Q.real(x), Q.negative(x))) is False def test_composite_ask(): x = symbols('x') assert ask(Q.negative(x) & Q.integer(x), assumptions=Q.real(x) >> Q.positive(x)) is False def test_composite_proposition(): from sympy.logic.boolalg import Equivalent, Implies x = symbols('x') assert ask(True) is True assert ask(~Q.negative(x), Q.positive(x)) is True assert ask(~Q.real(x), Q.commutative(x)) is None assert ask(Q.negative(x) & Q.integer(x), Q.positive(x)) is False assert ask(Q.negative(x) & Q.integer(x)) is None assert ask(Q.real(x) | Q.integer(x), Q.positive(x)) is True assert ask(Q.real(x) | Q.integer(x)) is None assert ask(Q.real(x) >> Q.positive(x), Q.negative(x)) is False assert ask(Implies(Q.real(x), Q.positive(x), evaluate=False), Q.negative(x)) is False assert ask(Implies(Q.real(x), Q.positive(x), evaluate=False)) is None assert ask(Equivalent(Q.integer(x), Q.even(x)), Q.even(x)) is True assert ask(Equivalent(Q.integer(x), Q.even(x))) is None assert ask(Equivalent(Q.positive(x), Q.integer(x)), Q.integer(x)) is None def test_incompatible_resolutors(): x = symbols('x') class Prime2AskHandler(AskHandler): @staticmethod def Number(expr, assumptions): return True register_handler('prime', Prime2AskHandler) raises(ValueError, 'ask(Q.prime(4))') remove_handler('prime', Prime2AskHandler) class InconclusiveHandler(AskHandler): @staticmethod def Number(expr, assumptions): return None register_handler('prime', InconclusiveHandler) assert ask(Q.prime(3)) == True def test_key_extensibility(): """test that you can add keys to the ask system at runtime""" x = Symbol('x') # make sure the key is not defined raises(AttributeError, "ask(Q.my_key(x))") class MyAskHandler(AskHandler): @staticmethod def Symbol(expr, assumptions): return True register_handler('my_key', MyAskHandler) assert ask(Q.my_key(x)) == True assert ask(Q.my_key(x+1)) == None remove_handler('my_key', MyAskHandler) del Q.my_key raises(AttributeError, "ask(Q.my_key(x))") def test_type_extensibility(): """test that new types can be added to the ask system at runtime We create a custom type MyType, and override ask Q.prime=True with handler MyAskHandler for this type TODO: test incompatible resolutors """ from sympy.core import Basic class MyType(Basic): pass class MyAskHandler(AskHandler): @staticmethod def MyType(expr, assumptions): return True a = MyType() register_handler(Q.prime, MyAskHandler) assert ask(Q.prime(a)) == True def test_compute_known_facts(): ns = {} exec 'from sympy.logic.boolalg import And, Or, Not' in globals(), ns exec compute_known_facts() in globals(), ns assert ns['known_facts_cnf'] == known_facts_cnf assert ns['known_facts_dict'] == known_facts_dict wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/tests/test_refine.py0000644000175000017500000000421312014170666027507 0ustar georgeskgeorgeskfrom sympy import S, symbols, exp, pi, sqrt, Rational, I, Q, refine, Abs from sympy.utilities.pytest import XFAIL def test_Abs(): x = symbols('x') assert refine(Abs(x), Q.positive(x)) == x assert refine(1+Abs(x), Q.positive(x)) == 1+x assert refine(Abs(x), Q.negative(x)) == -x assert refine(1+Abs(x), Q.negative(x)) == 1-x assert refine(Abs(x**2)) != x**2 assert refine(Abs(x**2), Q.real(x)) == x**2 def test_pow(): x, y, z = symbols('x,y,z') assert refine((-1)**x, Q.even(x)) == 1 assert refine((-1)**x, Q.odd(x)) == -1 assert refine((-2)**x, Q.even(x)) == 2**x # nested powers assert refine(sqrt(x**2)) != Abs(x) assert refine(sqrt(x**2), Q.complex(x)) != Abs(x) assert refine(sqrt(x**2), Q.real(x)) == Abs(x) assert refine(sqrt(x**2), Q.positive(x)) == x assert refine((x**3)**(S(1)/3)) != x assert refine((x**3)**(S(1)/3), Q.real(x)) != x assert refine((x**3)**(S(1)/3), Q.positive(x)) == x assert refine(sqrt(1/x), Q.real(x)) != 1/sqrt(x) assert refine(sqrt(1/x), Q.positive(x)) == 1/sqrt(x) # powers of (-1) assert refine((-1)**(x+y), Q.even(x)) == (-1)**y assert refine((-1)**(x+y+z), Q.odd(x) & Q.odd(z))==(-1)**y assert refine((-1)**(x+y+1), Q.odd(x))==(-1)**y assert refine((-1)**(x+y+2), Q.odd(x))==(-1)**(y+1) assert refine((-1)**(x+3)) == (-1)**(x+1) def test_exp(): x = symbols('x') assert refine(exp(pi*I*2*x), Q.integer(x)) == 1 assert refine(exp(pi*I*2*(x+Rational(1,2))), Q.integer(x)) == -1 assert refine(exp(pi*I*2*(x+Rational(1,4))), Q.integer(x)) == I assert refine(exp(pi*I*2*(x+Rational(3,4))), Q.integer(x)) == -I def test_func_args(): from sympy.core import Expr class MyClass(Expr): # A class with nontrivial .func def __init__(self, *args): self.my_member = "" @property def func(self): def my_func(*args): obj = MyClass(*args) obj.my_member = self.my_member return obj return my_func x = MyClass() x.my_member = "A very important value" assert x.my_member == refine(x).my_member wxgeometrie-0.133.2.orig/wxgeometrie/sympy/assumptions/__init__.py0000644000175000017500000000025212014170666025574 0ustar georgeskgeorgeskfrom assume import AppliedPredicate, global_assumptions, Predicate, AssumptionsContext from ask import Q, ask, register_handler, remove_handler from refine import refine wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/0000755000175000017500000000000012014170666022574 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/solvers.py0000644000175000017500000012740412014170666024653 0ustar georgeskgeorgesk""" This module contain solvers for all kinds of equations: - algebraic, use solve() - recurrence, use rsolve() - differential, use dsolve() - transcendental, use tsolve() - nonlinear (numerically), use nsolve() (you will need a good starting point) """ from sympy.core.compatibility import iterable, is_sequence from sympy.core.sympify import sympify from sympy.core import S, Mul, Add, Pow, Symbol, Wild, Equality, Dummy, Basic from sympy.core.numbers import ilcm from sympy.functions import log, exp, LambertW from sympy.simplify import simplify, collect, powsimp from sympy.matrices import Matrix, zeros from sympy.polys import roots, cancel, Poly, together from sympy.functions.elementary.piecewise import piecewise_fold from sympy.utilities.lambdify import lambdify from sympy.mpmath import findroot from sympy.solvers.polysys import solve_poly_system from sympy.solvers.inequalities import reduce_inequalities from sympy.core.compatibility import reduce from warnings import warn from types import GeneratorType def denoms(eq, x=None): """Return (recursively) set of all denominators that appear in eq that contain any symbol in x; if x is None (default) then all denominators with symbols will be returned.""" from sympy.utilities.iterables import preorder_traversal if x is None: x = eq.free_symbols dens = set() pt = preorder_traversal(eq) for e in pt: if e.is_Pow or e.func is exp: n, d = e.as_numer_denom() if d in dens: pt.skip() elif d.has(*x): dens.add(d.as_base_exp()[0]) return dens def checksol(f, symbol, sol=None, **flags): """Checks whether sol is a solution of equation f == 0. Input can be either a single symbol and corresponding value or a dictionary of symbols and values. Examples: --------- >>> from sympy import symbols >>> from sympy.solvers import checksol >>> x, y = symbols('x,y') >>> checksol(x**4-1, x, 1) True >>> checksol(x**4-1, x, 0) False >>> checksol(x**2 + y**2 - 5**2, {x:3, y: 4}) True None is returned if checksol() could not conclude. flags: 'numerical=True (default)' do a fast numerical check if f has only one symbol. 'minimal=True (default is False)' a very fast, minimal testing. 'warning=True (default is False)' print a warning if checksol() could not conclude. 'simplified=True (default)' solution should be simplified before substituting into function and function should be simplified after making substitution. 'force=True (default is False)' make positive all symbols without assumptions regarding sign. """ if sol is not None: sol = {symbol: sol} elif isinstance(symbol, dict): sol = symbol else: msg = 'Expecting sym, val or {sym: val}, None but got %s, %s' raise ValueError(msg % (symbol, sol)) if hasattr(f, '__iter__') and hasattr(f, '__len__'): if not f: raise ValueError('no functions to check') rv = set() for fi in f: check = checksol(fi, sol, **flags) if check is False: return False rv.add(check) if None in rv: # rv might contain True and/or None return None assert len(rv) == 1 # True return True if isinstance(f, Poly): f = f.as_expr() elif isinstance(f, Equality): f = f.lhs - f.rhs if not f: return True if not f.has(*sol.keys()): return False attempt = -1 numerical = flags.get('numerical', True) while 1: attempt += 1 if attempt == 0: val = f.subs(sol) elif attempt == 1: if not val.atoms(Symbol) and numerical: # val is a constant, so a fast numerical test may suffice if val not in [S.Infinity, S.NegativeInfinity]: # issue 2088 shows that +/-oo chops to 0 val = val.evalf(36).n(30, chop=True) elif attempt == 2: if flags.get('minimal', False): return # the flag 'simplified=False' is used in solve to avoid # simplifying the solution. So if it is set to False there # the simplification will not be attempted here, either. But # if the simplification is done here then the flag should be # set to False so it isn't done again there. if flags.get('simplified', True): for k in sol: sol[k] = simplify(sympify(sol[k])) flags['simplified'] = False val = simplify(f.subs(sol)) if flags.get('force', False): val = posify(val)[0] elif attempt == 3: val = powsimp(val) elif attempt == 4: val = cancel(val) elif attempt == 5: val = val.expand() elif attempt == 6: val = together(val) elif attempt == 7: val = powsimp(val) else: break if val.is_zero: return True elif attempt > 0 and numerical and val.is_nonzero: return False if flags.get('warning', False): print("Warning: could not verify solution %s." % sol) # returns None if it can't conclude # TODO: improve solution testing # Codes for guess solve strategy GS_POLY = 0 GS_RATIONAL = 1 GS_POLY_CV_1 = 2 # can be converted to a polynomial equation via the change of variable y -> x**a, a real GS_POLY_CV_2 = 3 # can be converted to a polynomial equation multiplying on both sides by x**m # for example, x + 1/x == 0. Multiplying by x yields x**2 + x == 0 GS_RATIONAL_CV_1 = 4 # can be converted to a rational equation via the change of variable y -> x**n GS_PIECEWISE = 5 GS_TRANSCENDENTAL = 6 def guess_solve_strategy(expr, symbol): """ Tries to guess what approach should be used to solve a specific equation Returns ======= - -1: could not guess - integer > 0: code representing certain type of equation. See GS_* fields on this module for a complete list Examples ======== >>> from sympy import Symbol, Rational >>> from sympy.solvers.solvers import guess_solve_strategy >>> from sympy.abc import x >>> guess_solve_strategy(x**2 + 1, x) 0 >>> guess_solve_strategy(x**Rational(1,2) + 1, x) 2 """ eq_type = -1 if expr.is_Add: return max([guess_solve_strategy(i, symbol) for i in expr.args]) elif expr.is_Mul: # check for rational functions num, denom = expr.as_numer_denom() if denom.has(symbol): #we have a quotient m = max(guess_solve_strategy(num, symbol), guess_solve_strategy(denom, symbol)) if m == GS_POLY: return GS_RATIONAL elif m == GS_POLY_CV_1: return GS_RATIONAL_CV_1 else: raise NotImplementedError else: return max([guess_solve_strategy(i, symbol) for i in expr.args]) elif expr.is_Symbol: return GS_POLY elif expr.is_Pow: if expr.exp.has(symbol): return GS_TRANSCENDENTAL elif not expr.exp.has(symbol) and expr.base.has(symbol): if expr.exp.is_Integer and expr.exp > 0: eq_type = max(eq_type, GS_POLY) elif expr.exp.is_Integer and expr.exp < 0: eq_type = max(eq_type, GS_POLY_CV_2) elif expr.exp.is_Rational: eq_type = max(eq_type, GS_POLY_CV_1) else: return GS_TRANSCENDENTAL elif expr.is_Piecewise: return GS_PIECEWISE elif expr.is_Function and expr.has(symbol): return GS_TRANSCENDENTAL elif not expr.has(symbol): return GS_POLY return eq_type def solve(f, *symbols, **flags): """ Algebraically solves equations and systems of equations. Currently supported are: - univariate polynomial, - transcendental - piecewise combinations of the above - systems of linear and polynomial equations - sytems containing relational expressions. Input is formed as: f - a single Expr or Poly that must be zero, - an Equality - a Relational expression or boolean - iterable of one or more of the above symbols (Symbol, Function or Derivative) specified as - none given (all free symbols will be used) - single symbol - denested list of symbols e.g. solve(f, x, y) - ordered iterable of symbols e.g. solve(f, [x, y]) flags - ``simplified``, when False, will not simplify solutions (default=True except for polynomials of order 3 or greater) The output varies according to the input and can be seen by example: >>> from sympy import solve, Poly, Eq, Function, exp >>> from sympy.abc import x, y, z, a, b o boolean or univariate Relational >>> solve(x < 3) And(im(x) == 0, re(x) < 3) o single expression and single symbol that is in the expression >>> solve(x - y, x) [y] >>> solve(x - 3, x) [3] >>> solve(Eq(x, 3), x) [3] >>> solve(Poly(x - 3), x) [3] >>> solve(x**2 - y**2, x) [y, -y] >>> solve(x**4 - 1, x) [1, -1, -I, I] o single expression with no symbol that is in the expression >>> solve(3, x) [] >>> solve(x - 3, y) [] o when no symbol is given then all free symbols will be used and sorted with default_sort_key and the result will be the same as above as if those symbols had been supplied >>> solve(x - 3) [3] >>> solve(x**2 - y**2) [y, -y] o when a Function or Derivative is given as a symbol, it is isolated algebraically and an implicit solution may be obtained >>> f = Function('f') >>> solve(f(x) - x, f(x)) [x] >>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x)) [x + f(x)] o single expression and more than 1 symbol when there is a linear solution >>> solve(x - y**2, x, y) {x: y**2} >>> solve(x**2 - y, x, y) {y: x**2} when undetermined coefficients are identified that are linear >>> solve((a + b)*x - b + 2, a, b) {a: -2, b: 2} that are nonlinear >>> solve((a + b)*x - b**2 + 2, a, b) [(-2**(1/2), 2**(1/2)), (2**(1/2), -2**(1/2))] if there is no linear solution then the first successful attempt for a nonlinear solution will be returned >>> solve(x**2 - y**2, x, y) [y, -y] >>> solve(x**2 - y**2/exp(x), x, y) [x*exp(x/2), -x*exp(x/2)] o iterable of one or more of the above involving relationals or bools >>> solve([x < 3, x - 2]) And(im(x) == 0, re(x) == 2) >>> solve([x > 3, x - 2]) False when the system is linear with a solution >>> solve([x - 3], x) {x: 3} >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y) {x: -3, y: 1} >>> solve((x + 5*y - 2, -3*x + 6*y - 15), x, y, z) {x: -3, y: 1} >>> solve((x + 5*y - 2, -3*x + 6*y - z), z, x, y) {x: -5*y + 2, z: 21*y - 6} without a solution >>> solve([x + 3, x - 3]) when the system is not linear >>> solve([x**2 + y -2, y**2 - 4], x, y) [(-2, -2), (0, 2), (0, 2), (2, -2)] Warning: there is a possibility of obtaining ambiguous results if no symbols are given for a nonlinear system of equations or are given as a set since the symbols are not presently reported with the solution. A warning will be issued in this situation. >>> solve([x - 2, x**2 + y]) For nonlinear systems of equations, symbols should be given as a list so as to avoid ambiguity in the results. solve sorted the symbols as [x, y] [(2, -4)] >>> solve([x - 2, x**2 + f(x)], set([f(x), x])) For nonlinear systems of equations, symbols should be given as a list so as to avoid ambiguity in the results. solve sorted the symbols as [x, f(x)] [(2, -4)] See also: rsolve() for solving recurrence relationships dsolve() for solving differential equations """ # make f and symbols into lists of sympified quantities # keeping track of how f was passed since if it is a list # a dictionary of results will be returned. ########################################################################### def sympified_list(w): return map(sympify, w if iterable(w) else [w]) bare_f = not iterable(f) ordered_symbols = (symbols and symbols[0] and (isinstance(symbols[0], Symbol) or is_sequence(symbols[0], include=GeneratorType) ) ) f, symbols = (sympified_list(w) for w in [f, symbols]) # preprocess equation(s) ########################################################################### for i, fi in enumerate(f): if isinstance(fi, Equality): f[i] = fi.lhs - fi.rhs elif isinstance(fi, Poly): f[i] = fi.as_expr() elif isinstance(fi, bool) or fi.is_Relational: return reduce_inequalities(f, assume=flags.get('assume')) # Any embedded piecewise functions need to be brought out to the # top level so that the appropriate strategy gets selected. f[i] = piecewise_fold(f[i]) # preprocess symbol(s) ########################################################################### if not symbols: # get symbols from equations or supply dummy symbols so solve(3) behaves # like solve(3, x). symbols = set([]) for fi in f: symbols |= fi.free_symbols or set([Dummy()]) ordered_symbols = False elif len(symbols) == 1 and iterable(symbols[0]): symbols = symbols[0] if not ordered_symbols: # we do this to make the results returned canonical in case f # contains a system of nonlinear equations; all other cases should # be unambiguous symbols = sorted(symbols, key=lambda i: i.sort_key()) # we can solve for Function and Derivative instances by replacing them # with Dummy symbols symbols_new = [] symbol_swapped = False symbols_passed = list(symbols) for i, s in enumerate(symbols): if s.is_Symbol: s_new = s elif s.is_Function: symbol_swapped = True s_new = Dummy('F%d' % i) elif s.is_Derivative: symbol_swapped = True s_new = Dummy('D%d' % i) else: msg = 'expected Symbol, Function or Derivative but got %s' raise TypeError(msg % type(s)) symbols_new.append(s_new) if symbol_swapped: swap_back_dict = dict(zip(symbols_new, symbols)) swap_dict = zip(symbols, symbols_new) f = [fi.subs(swap_dict) for fi in f] symbols = symbols_new # # try to get a solution ########################################################################### if bare_f: # pass f the way it was passed to solve; if it wasn't a list then # a list of solutions will be returned, otherwise a dictionary is # going to be returned f = f[0] solution = _solve(f, *symbols, **flags) # # postprocessing ########################################################################### # Restore original Functions and Derivatives if a dictionary is returned. # This is not necessary for # - the single equation, single unknown case # since the symbol will have been removed from the solution; # - the nonlinear poly_system since that only support zero-dimensional # systems and those results come back as a list if symbol_swapped and type(solution) is dict: solution = dict([(swap_back_dict[k], v.subs(swap_back_dict)) for k, v in solution.iteritems()]) # warn if ambiguous results are being obtained # XXX agree on how to make this unambiguous # see issue 2405 for logic in how Polys chooses ordering and # for discussion of what to return see http://groups.google.com/group/sympy # Apr 18, 2011 posting 'using results from solve' elif (not ordered_symbols and len(symbols) > 1 and solution and is_sequence(solution) and is_sequence(solution[0]) and any(len(set(s)) > 1 for s in solution) ): msg = ('\n\tFor nonlinear systems of equations, symbols should be' + '\n\tgiven as a list so as to avoid ambiguity in the results.' + '\n\tsolve sorted the symbols as %s') print msg % str(bool(symbol_swapped) and list(zip(*swap_dict)[0]) or symbols) # # done ########################################################################### return solution def _solve(f, *symbols, **flags): """ Return a checked solution for f in terms of one or more of the symbols.""" if not iterable(f): if len(symbols) != 1: soln = None free = f.free_symbols ex = free - set(symbols) if len(ex) == 1: ex = ex.pop() try: # may come back as dict or list (if non-linear) soln = solve_undetermined_coeffs(f, symbols, ex) except NotImplementedError: pass if not soln is None: return soln # find first successful solution failed = [] for s in symbols: n, d = solve_linear(f, x=[s]) if n.is_Symbol: soln = {n: cancel(d)} return soln failed.append(s) for s in failed: try: soln = _solve(f, s, **flags) return soln except NotImplementedError: pass else: msg = "No algorithms are implemented to solve equation %s" raise NotImplementedError(msg % f) symbol = symbols[0] # first see if it really depends on symbol and whether there # is a linear solution f_num, sol = solve_linear(f, x=symbols) if not symbol in f_num.free_symbols: return [] elif f_num.is_Symbol: return [cancel(sol)] strategy = guess_solve_strategy(f, symbol) result = False # no solution was obtained if strategy == GS_POLY: poly = f.as_poly(symbol) if poly is None: msg = "Cannot solve equation %s for %s" % (f, symbol) else: # for cubics and quartics, if the flag wasn't set, DON'T do it # by default since the results are quite long. Perhaps one could # base this decision on a certain critical length of the roots. if poly.degree() > 2: flags['simplified'] = flags.get('simplified', False) result = roots(poly, cubics=True, quartics=True).keys() elif strategy == GS_RATIONAL: P, _ = f.as_numer_denom() dens = denoms(f, x=symbols) try: soln = _solve(P, symbol, **flags) except NotImplementedError: msg = "Cannot solve equation %s for %s" % (P, symbol) result = [] else: if dens: # reject any result that makes any denom. affirmatively 0; # if in doubt, keep it result = [s for s in soln if all(not checksol(den, {symbol: s}) for den in dens)] else: result = soln elif strategy == GS_POLY_CV_1: args = list(f.args) if isinstance(f, Pow): result = _solve(args[0], symbol, **flags) elif isinstance(f, Add): # we must search for a suitable change of variables # collect exponents exponents_denom = list() for arg in args: if isinstance(arg, Pow): exponents_denom.append(arg.exp.q) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): exponents_denom.append(mul_arg.exp.q) assert len(exponents_denom) > 0 if len(exponents_denom) == 1: m = exponents_denom[0] else: # get the LCM of the denominators m = reduce(ilcm, exponents_denom) # x -> y**m. # we assume positive for simplification purposes t = Dummy('t', positive=True) f_ = f.subs(symbol, t**m) if guess_solve_strategy(f_, t) != GS_POLY: msg = "Could not convert to a polynomial equation: %s" % f_ result = [] else: soln = [s**m for s in _solve(f_, t)] # we might have introduced solutions from another branch # when changing variables; check and keep solutions # unless they definitely aren't a solution result = [s for s in soln if checksol(f, {symbol: s}) is not False] elif isinstance(f, Mul): result = [] for m in f.args: result.extend(_solve(m, symbol, **flags) or []) elif strategy == GS_POLY_CV_2: m = 0 args = list(f.args) if isinstance(f, Add): for arg in args: if isinstance(arg, Pow): m = min(m, arg.exp) elif isinstance(arg, Mul): for mul_arg in arg.args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) elif isinstance(f, Mul): for mul_arg in args: if isinstance(mul_arg, Pow): m = min(m, mul_arg.exp) if m and m != 1: f_ = simplify(f*symbol**(-m)) try: sols = _solve(f_, symbol) except NotImplementedError: msg = 'Could not solve %s for %s' % (f_, symbol) else: # we might have introduced unwanted solutions # when multiplying by x**-m; check and keep solutions # unless they definitely aren't a solution if sols: result = [s for s in sols if checksol(f, {symbol: s}) is not False] else: msg = 'CV_2 calculated %d but it should have been other than 0 or 1' % m elif strategy == GS_PIECEWISE: result = set() for expr, cond in f.args: candidates = _solve(expr, *symbols) if isinstance(cond, bool) or cond.is_Number: if not cond: continue # Only include solutions that do not match the condition # of any of the other pieces. for candidate in candidates: matches_other_piece = False for other_expr, other_cond in f.args: if isinstance(other_cond, bool) \ or other_cond.is_Number: continue if bool(other_cond.subs(symbol, candidate)): matches_other_piece = True break if not matches_other_piece: result.add(candidate) else: for candidate in candidates: if bool(cond.subs(symbol, candidate)): result.add(candidate) result = list(result) elif strategy == -1: raise ValueError('Could not parse expression %s' % f) # this is the fallback for not getting any other solution if result is False or strategy == GS_TRANSCENDENTAL: soln = tsolve(f_num, symbol) dens = denoms(f, x=symbols) if not dens: result = soln else: # reject any result that makes any denom. affirmatively 0; # if in doubt, keep it result = [s for s in soln if all(not checksol(den, {symbol: s}) for den in dens)] if result is False: raise NotImplementedError(msg + "\nNo algorithms are implemented to solve equation %s" % f) if flags.get('simplified', True) and strategy != GS_RATIONAL: result = map(simplify, result) return result else: if not f: return [] else: polys = [] for g in f: poly = g.as_poly(*symbols, **{'extension': True}) if poly is not None: polys.append(poly) else: raise NotImplementedError() if all(p.is_linear for p in polys): n, m = len(f), len(symbols) matrix = zeros((n, m + 1)) for i, poly in enumerate(polys): for monom, coeff in poly.terms(): try: j = list(monom).index(1) matrix[i, j] = coeff except ValueError: matrix[i, m] = -coeff # a dictionary of symbols: values or None result = solve_linear_system(matrix, *symbols, **flags) return result else: # a list of tuples, T, where T[i] [j] corresponds to the ith solution for symbols[j] result = solve_poly_system(polys) return result def solve_linear(lhs, rhs=0, x=[], exclude=[]): """ Return a tuple containing derived from f = lhs - rhs that is either: (numerator, denominator) of f; if this comes back as (0, 1) it means that f is independent of the symbols of x, e.g. y*cos(x)**2 + y*sin(x)**2 - y = y*(0) = 0 cos(x)**2 + sin(x)**2 = 1 If the numerator is not zero then the function is guaranteed to be dependent on a symbol in x. or (symbol, solution) where symbol appears linearly in the numerator of f, is in x (if given) and is not in exclude (if given). No simplification is done to f other than and mul=True expansion, so the solution will correspond strictly to a unique solution. Examples: >>> from sympy.solvers.solvers import solve_linear >>> from sympy.abc import x, y, z These are linear in x and 1/x: >>> solve_linear(x + y**2) (x, -y**2) >>> solve_linear(1/x - y**2) (x, y**(-2)) When not linear in x or y then the numerator and denominator are returned. >>> solve_linear(x**2/y**2 - 3) (x**2 - 3*y**2, y**2) If x is allowed to cancel, then this appears linear, but this sort of cancellation is not done so the solution will always satisfy the original expression without causing a division by zero error. >>> solve_linear(x**2*(1/x - z**2/x)) (x**2*(-x*z**2 + x), x**2) You can give a list of what you prefer for x candidates: >>> solve_linear(x + y + z, x=[y]) (y, -x - z) You can also indicate what variables you don't want to consider: >>> solve_linear(x + y + z, exclude=[x, z]) (y, -x - z) If only x was excluded then a solution for y or z might be obtained. """ from sympy import expand_mul, Equality if isinstance(lhs, Equality): rhs += lhs.rhs lhs = lhs.lhs n, d = (lhs - rhs).as_numer_denom() ex = expand_mul(n) if not ex: return ex, S.One exclude = set(exclude) syms = ex.free_symbols if not x: x = syms else: x = syms.intersection(x) x = x.difference(exclude) d_free = d.free_symbols if x: all_zero = True for xi in x: dn = n.diff(xi) if dn: all_zero = False if not xi in dn.free_symbols: vi = -(n.subs(xi, 0))/dn if not checksol(d, {xi: vi}, minimal=True) is True: return xi, vi if all_zero: return S.Zero, S.One return n, d # should we cancel now? def solve_linear_system(system, *symbols, **flags): """Solve system of N linear equations with M variables, which means both Cramer and over defined systems are supported. The possible number of solutions is zero, one or infinite. Respectively this procedure will return None or dictionary with solutions. In the case of over-defined systems all arbitrary parameters are skipped. This may cause situation in which an empty dictionary is returned. In this case it means all symbols can be assigned arbitrary values. Input to this functions is a Nx(M+1) matrix, which means it has to be in augmented form. If you prefer to enter N equations and M unknowns then use 'solve(Neqs, *Msymbols)' instead. Note: a local copy of the matrix is made by this routine so the matrix that is passed will not be modified. The algorithm used here is fraction-free Gaussian elimination, which results, after elimination, in an upper-triangular matrix. Then solutions are found using back-substitution. This approach is more efficient and compact than the Gauss-Jordan method. >>> from sympy import Matrix, solve_linear_system >>> from sympy.abc import x, y Solve the following system: x + 4 y == 2 -2 x + y == 14 >>> system = Matrix(( (1, 4, 2), (-2, 1, 14))) >>> solve_linear_system(system, x, y) {x: -6, y: 2} """ matrix = system[:,:] syms = list(symbols) i, m = 0, matrix.cols-1 # don't count augmentation while i < matrix.rows: if i == m: # an overdetermined system if any(matrix[i:,m]): return None # no solutions else: # remove trailing rows matrix = matrix[:i,:] break if not matrix[i, i]: # there is no pivot in current column # so try to find one in other columns for k in xrange(i+1, m): if matrix[i, k]: break else: if matrix[i, m]: return None # no solutions else: # zero row or was a linear combination of # other rows so now we can safely skip it matrix.row_del(i) continue # we want to change the order of colums so # the order of variables must also change syms[i], syms[k] = syms[k], syms[i] matrix.col_swap(i, k) pivot_inv = S.One / matrix [i, i] # divide all elements in the current row by the pivot matrix.row(i, lambda x, _: x * pivot_inv) for k in xrange(i+1, matrix.rows): if matrix[k, i]: coeff = matrix[k, i] # subtract from the current row the row containing # pivot and multiplied by extracted coefficient matrix.row(k, lambda x, j: simplify(x - matrix[i, j]*coeff)) i += 1 # if there weren't any problems, augmented matrix is now # in row-echelon form so we can check how many solutions # there are and extract them using back substitution simplified = flags.get('simplified', True) if len(syms) == matrix.rows: # this system is Cramer equivalent so there is # exactly one solution to this system of equations k, solutions = i-1, {} while k >= 0: content = matrix[k, m] # run back-substitution for variables for j in xrange(k+1, m): content -= matrix[k, j]*solutions[syms[j]] if simplified: solutions[syms[k]] = simplify(content) else: solutions[syms[k]] = content k -= 1 return solutions elif len(syms) > matrix.rows: # this system will have infinite number of solutions # dependent on exactly len(syms) - i parameters k, solutions = i-1, {} while k >= 0: content = matrix[k, m] # run back-substitution for variables for j in xrange(k+1, i): content -= matrix[k, j]*solutions[syms[j]] # run back-substitution for parameters for j in xrange(i, m): content -= matrix[k, j]*syms[j] if simplified: solutions[syms[k]] = simplify(content) else: solutions[syms[k]] = content k -= 1 return solutions else: return None # no solutions def solve_undetermined_coeffs(equ, coeffs, sym, **flags): """Solve equation of a type p(x; a_1, ..., a_k) == q(x) where both p, q are univariate polynomials and f depends on k parameters. The result of this functions is a dictionary with symbolic values of those parameters with respect to coefficients in q. This functions accepts both Equations class instances and ordinary SymPy expressions. Specification of parameters and variable is obligatory for efficiency and simplicity reason. >>> from sympy import Eq >>> from sympy.abc import a, b, c, x >>> from sympy.solvers import solve_undetermined_coeffs >>> solve_undetermined_coeffs(Eq(2*a*x + a+b, x), [a, b], x) {a: 1/2, b: -1/2} >>> solve_undetermined_coeffs(Eq(a*c*x + a+b, x), [a, b], x) {a: 1/c, b: -1/c} """ if isinstance(equ, Equality): # got equation, so move all the # terms to the left hand side equ = equ.lhs - equ.rhs equ = cancel(equ).as_numer_denom()[0] system = collect(equ.expand(), sym, evaluate=False).values() if not any([ equ.has(sym) for equ in system ]): # consecutive powers in the input expressions have # been successfully collected, so solve remaining # system using Gaussian elimination algorithm return solve(system, *coeffs, **flags) else: return None # no solutions def solve_linear_system_LU(matrix, syms): """ LU function works for invertible only """ assert matrix.rows == matrix.cols-1 A = matrix[:matrix.rows,:matrix.rows] b = matrix[:,matrix.cols-1:] soln = A.LUsolve(b) solutions = {} for i in range(soln.rows): solutions[syms[i]] = soln[i,0] return solutions x = Dummy('x') a,b,c,d,e,f,g,h = [Wild(t, exclude=[x]) for t in 'abcdefgh'] patterns = None def _generate_patterns(): """ Generates patterns for transcendental equations. This is lazily calculated (called) in the tsolve() function and stored in the patterns global variable. """ tmp1 = f ** (h-(c*g/b)) tmp2 = (-e*tmp1/a)**(1/d) global patterns patterns = [ (a*(b*x+c)**d + e , ((-(e/a))**(1/d)-c)/b), ( b+c*exp(d*x+e) , (log(-b/c)-e)/d), (a*x+b+c*exp(d*x+e) , -b/a-LambertW(c*d*exp(e-b*d/a)/a)/d), ( b+c*f**(d*x+e) , (log(-b/c)-e*log(f))/d/log(f)), (a*x+b+c*f**(d*x+e) , -b/a-LambertW(c*d*f**(e-b*d/a)*log(f)/a)/d/log(f)), ( b+c*log(d*x+e) , (exp(-b/c)-e)/d), (a*x+b+c*log(d*x+e) , -e/d+c/a*LambertW(a/c/d*exp(-b/c+a*e/c/d))), (a*(b*x+c)**d + e*f**(g*x+h) , -c/b-d*LambertW(-tmp2*g*log(f)/b/d)/g/log(f)) ] def tsolve(eq, sym): """ Solves a transcendental equation with respect to the given symbol. Various equations containing mixed linear terms, powers, and logarithms, can be solved. Only a single solution is returned. This solution is generally not unique. In some cases, a complex solution may be returned even though a real solution exists. >>> from sympy import tsolve, log >>> from sympy.abc import x >>> tsolve(3**(2*x+5)-4, x) [(-5*log(3) + 2*log(2))/(2*log(3))] >>> tsolve(log(x) + 2*x, x) [LambertW(2)/2] """ if patterns is None: _generate_patterns() eq = sympify(eq) if isinstance(eq, Equality): eq = eq.lhs - eq.rhs sym = sympify(sym) eq2 = eq.subs(sym, x) # First see if the equation has a linear factor # In that case, the other factor can contain x in any way (as long as it # is finite), and we have a direct solution to which we add others that # may be found for the remaining portion. r = Wild('r') m = eq2.match((a*x+b)*r) if m and m[a]: return [(-b/a).subs(m).subs(x, sym)] + solve(m[r], x) for p, sol in patterns: m = eq2.match(p) if m: soln = sol.subs(m).subs(x, sym) if not(soln is S.NaN or soln.has(S.Infinity) or soln.has(S.NegativeInfinity) or sym in soln.free_symbols): return [soln] # let's also try to inverse the equation lhs = eq rhs = S.Zero while True: indep, dep = lhs.as_independent(sym) # dep + indep == rhs if lhs.is_Add: # this indicates we have done it all if indep is S.Zero: break lhs = dep rhs-= indep # dep * indep == rhs else: # this indicates we have done it all if indep is S.One: break lhs = dep rhs/= indep # -1 # f(x) = g -> x = f (g) if lhs.is_Function and lhs.nargs==1 and hasattr(lhs, 'inverse'): rhs = lhs.inverse() (rhs) lhs = lhs.args[0] sol = solve(lhs-rhs, sym) return sol elif lhs.is_Add: # just a simple case - we do variable substitution for first function, # and if it removes all functions - let's call solve. # x -x -1 # UC: e + e = y -> t + t = y t = Dummy('t') terms = lhs.args # find first term which is Function for f1 in lhs.args: if f1.is_Function: break else: raise NotImplementedError("Unable to solve the equation" + \ "(tsolve: at least one Function expected at this point") # perform the substitution lhs_ = lhs.subs(f1, t) # if no Functions left, we can proceed with usual solve if not (lhs_.is_Function or any(term.is_Function for term in lhs_.args)): cv_sols = solve(lhs_ - rhs, t) for sol in cv_sols: if sol.has(sym): raise NotImplementedError("Unable to solve the equation") cv_inv = solve( t - f1, sym )[0] sols = list() for sol in cv_sols: sols.append(cv_inv.subs(t, sol)) return sols raise NotImplementedError("Unable to solve the equation.") # TODO: option for calculating J numerically def nsolve(*args, **kwargs): """ Solve a nonlinear equation system numerically. nsolve(f, [args,] x0, modules=['mpmath'], **kwargs) f is a vector function of symbolic expressions representing the system. args are the variables. If there is only one variable, this argument can be omitted. x0 is a starting vector close to a solution. Use the modules keyword to specify which modules should be used to evaluate the function and the Jacobian matrix. Make sure to use a module that supports matrices. For more information on the syntax, please see the docstring of lambdify. Overdetermined systems are supported. >>> from sympy import Symbol, nsolve >>> import sympy >>> sympy.mpmath.mp.dps = 15 >>> x1 = Symbol('x1') >>> x2 = Symbol('x2') >>> f1 = 3 * x1**2 - 2 * x2**2 - 1 >>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8 >>> print nsolve((f1, f2), (x1, x2), (-1, 1)) [-1.19287309935246] [ 1.27844411169911] For one-dimensional functions the syntax is simplified: >>> from sympy import sin, nsolve >>> from sympy.abc import x >>> nsolve(sin(x), x, 2) 3.14159265358979 >>> nsolve(sin(x), 2) 3.14159265358979 mpmath.findroot is used, you can find there more extensive documentation, especially concerning keyword parameters and available solvers. """ # interpret arguments if len(args) == 3: f = args[0] fargs = args[1] x0 = args[2] elif len(args) == 2: f = args[0] fargs = None x0 = args[1] elif len(args) < 2: raise TypeError('nsolve expected at least 2 arguments, got %i' % len(args)) else: raise TypeError('nsolve expected at most 3 arguments, got %i' % len(args)) modules = kwargs.get('modules', ['mpmath']) if isinstance(f, (list, tuple)): f = Matrix(f).T if not isinstance(f, Matrix): # assume it's a sympy expression if isinstance(f, Equality): f = f.lhs - f.rhs f = f.evalf() atoms = f.atoms(Symbol) if fargs is None: fargs = atoms.copy().pop() if not (len(atoms) == 1 and (fargs in atoms or fargs[0] in atoms)): raise ValueError('expected a one-dimensional and numerical function') # the function is much better behaved if there is no denominator f = f.as_numer_denom()[0] f = lambdify(fargs, f, modules) return findroot(f, x0, **kwargs) if len(fargs) > f.cols: raise NotImplementedError('need at least as many equations as variables') verbose = kwargs.get('verbose', False) if verbose: print 'f(x):' print f # derive Jacobian J = f.jacobian(fargs) if verbose: print 'J(x):' print J # create functions f = lambdify(fargs, f.T, modules) J = lambdify(fargs, J, modules) # solve the system numerically x = findroot(f, x0, J=J, **kwargs) return x wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/ode.py0000644000175000017500000034775312014170666023740 0ustar georgeskgeorgesk""" This module contains dsolve() and different helper functions that it uses. dsolve() solves ordinary differential equations. See the docstring on the various functions for their uses. Note that partial differential equations support is in pde.py. Note that ode_hint() functions have docstrings describing their various methods, but they are intended for internal use. Use dsolve(ode, func, hint=hint) to solve an ode using a specific hint. See also the docstring on dsolve(). **Functions in this module** These are the user functions in this module: - dsolve() - Solves ODEs. - classify_ode() - Classifies ODEs into possible hints for dsolve(). - checkodesol() - Checks if an equation is the solution to an ODE. - ode_order() - Returns the order (degree) of an ODE. - homogeneous_order() - Returns the homogeneous order of an expression. These are the non-solver helper functions that are for internal use. The user should use the various options to dsolve() to obtain the functionality provided by these functions: - odesimp() - Does all forms of ODE simplification. - ode_sol_simplicity() - A key function for comparing solutions by simplicity. - constantsimp() - Simplifies arbitrary constants. - constant_renumber() - Renumber arbitrary constants - _handle_Integral() - Evaluate unevaluated Integrals. See also the docstrings of these functions. **Solving methods currently implemented** The following methods are implemented for solving ordinary differential equations. See the docstrings of the various ode_hint() functions for more information on each (run help(ode)): - 1st order separable differential equations - 1st order differential equations whose coefficients or dx and dy are functions homogeneous of the same order. - 1st order exact differential equations. - 1st order linear differential equations - 1st order Bernoulli differential equations. - 2nd order Liouville differential equations. - nth order linear homogeneous differential equation with constant coefficients. - nth order linear inhomogeneous differential equation with constant coefficients using the method of undetermined coefficients. - nth order linear inhomogeneous differential equation with constant coefficients using the method of variation of parameters. **Philosophy behind this module** This module is designed to make it easy to add new ODE solving methods without having to mess with the solving code for other methods. The idea is that there is a classify_ode() function, which takes in an ODE and tells you what hints, if any, will solve the ODE. It does this without attempting to solve the ODE, so it is fast. Each solving method is a hint, and it has its own function, named ode_hint. That function takes in the ODE and any match expression gathered by classify_ode and returns a solved result. If this result has any integrals in it, the ode_hint function will return an unevaluated Integral class. dsolve(), which is the user wrapper function around all of this, will then call odesimp() on the result, which, among other things, will attempt to solve the equation for the dependent variable (the function we are solving for), simplify the arbitrary constants in the expression, and evaluate any integrals, if the hint allows it. **How to add new solution methods** If you have an ODE that you want dsolve() to be able to solve, try to avoid adding special case code here. Instead, try finding a general method that will solve your ODE, as well as others. This way, the ode module will become more robust, and unhindered by special case hacks. WolphramAlpha and Maple's DETools[odeadvisor] function are two resources you can use to classify a specific ODE. It is also better for a method to work with an nth order ODE instead of only with specific orders, if possible. To add a new method, there are a few things that you need to do. First, you need a hint name for your method. Try to name your hint so that it is unambiguous with all other methods, including ones that may not be implemented yet. If your method uses integrals, also include a "hint_Integral" hint. If there is more than one way to solve ODEs with your method, include a hint for each one, as well as a "hint_best" hint. Your ode_hint_best() function should choose the best using min with ode_sol_simplicity as the key argument. See ode_1st_homogeneous_coeff_best(), for example. The function that uses your method will be called ode_hint(), so the hint must only use characters that are allowed in a Python function name (alphanumeric characters and the underscore '_' character). Include a function for every hint, except for "_Integral" hints (dsolve() takes care of those automatically). Hint names should be all lowercase, unless a word is commonly capitalized (such as Integral or Bernoulli). If you have a hint that you do not want to run with "all_Integral" that doesn't have an "_Integral" counterpart (such as a best hint that would defeat the purpose of "all_Integral"), you will need to remove it manually in the dsolve() code. See also the classify_ode() docstring for guidelines on writing a hint name. Determine *in general* how the solutions returned by your method compare with other methods that can potentially solve the same ODEs. Then, put your hints in the allhints tuple in the order that they should be called. The ordering of this tuple determines which hints are default. Note that exceptions are ok, because it is easy for the user to choose individual hints with dsolve(). In general, "_Integral" variants should go at the end of the list, and "_best" variants should go before the various hints they apply to. For example, the "undetermined_coefficients" hint comes before the "variation_of_parameters" hint because, even though variation of parameters is more general than undetermined coefficients, undetermined coefficients generally returns cleaner results for the ODEs that it can solve than variation of parameters does, and it does not require integration, so it is much faster. Next, you need to have a match expression or a function that matches the type of the ODE, which you should put in classify_ode() (if the match function is more than just a few lines, like _undetermined_coefficients_match(), it should go outside of classify_ode()). It should match the ODE without solving for it as much as possible, so that classify_ode() remains fast and is not hindered by bugs in solving code. Be sure to consider corner cases. For example, if your solution method involves dividing by something, make sure you exclude the case where that division will be 0. In most cases, the matching of the ODE will also give you the various parts that you need to solve it. You should put that in a dictionary (.match() will do this for you), and add that as matching_hints['hint'] = matchdict in the relevant part of classify_ode. classify_ode will then send this to dsolve(), which will send it to your function as the match argument. Your function should be named ode_hint(eq, func, order, match). If you need to send more information, put it in the match dictionary. For example, if you had to substitute in a dummy variable in classify_ode to match the ODE, you will need to pass it to your function using the match dict to access it. You can access the independent variable using func.args[0], and the dependent variable (the function you are trying to solve for) as func.func. If, while trying to solve the ODE, you find that you cannot, raise NotImplementedError. dsolve() will catch this error with the "all" meta-hint, rather than causing the whole routine to fail. Add a docstring to your function that describes the method employed. Like with anything else in SymPy, you will need to add a doctest to the docstring, in addition to real tests in test_ode.py. Try to maintain consistency with the other hint functions' docstrings. Add your method to the list at the top of this docstring. Also, add your method to ode.txt in the docs/src directory, so that the Sphinx docs will pull its docstring into the main SymPy documentation. Be sure to make the Sphinx documentation by running "make html" from within the doc directory to verify that the docstring formats correctly. If your solution method involves integrating, use C.Integral() instead of integrate(). This allows the user to bypass hard/slow integration by using the "_Integral" variant of your hint. In most cases, calling .doit() will integrate your solution. If this is not the case, you will need to write special code in _handle_Integral(). Arbitrary constants should be symbols named C1, C2, and so on. All solution methods should return an equality instance. If you need an arbitrary number of arbitrary constants, you can use constants = numbered_symbols(prefix='C', cls=Symbol, start=1). If it is possible to solve for the dependent function in a general way, do so. Otherwise, do as best as you can, but do not call solve in your ode_hint() function. odesimp() will attempt to solve the solution for you, so you do not need to do that. Lastly, if your ODE has a common simplification that can be applied to your solutions, you can add a special case in odesimp() for it. For example, solutions returned from the "1st_homogeneous_coeff" hints often have many log() terms, so odesimp() calls logcombine() on them (it also helps to write the arbitrary constant as log(C1) instead of C1 in this case). Also consider common ways that you can rearrange your solution to have constantsimp() take better advantage of it. It is better to put simplification in odesimp() than in your method, because it can then be turned off with the simplify flag in dsolve(). If you have any extraneous simplification in your function, be sure to only run it using "if match.get('simplify', True):", especially if it can be slow or if it can reduce the domain of the solution. Finally, as with every contribution to SymPy, your method will need to be tested. Add a test for each method in test_ode.py. Follow the conventions there, i.e., test the solver using dsolve(eq, f(x), hint=your_hint), and also test the solution using checkodesol (you can put these in a separate tests and skip/XFAIL if it runs too slow/doesn't work). Be sure to call your hint specifically in dsolve, that way the test won't be broken simply by the introduction of another matching hint. If your method works for higher order (>1) ODEs, you will need to run sol = constant_renumber(sol, 'C', 1, order), for each solution, where order is the order of the ODE. This is because constant_renumber renumbers the arbitrary constants by printing order, which is platform dependent. Try to test every corner case of your solver, including a range of orders if it is a nth order solver, but if your solver is slow, auch as if it involves hard integration, try to keep the test run time down. Feel free to refactor existing hints to avoid duplicating code or creating inconsistencies. If you can show that your method exactly duplicates an existing method, including in the simplicity and speed of obtaining the solutions, then you can remove the old, less general method. The existing code is tested extensively in test_ode.py, so if anything is broken, one of those tests will surely fail. """ from sympy.core import Add, Basic, C, S, Mul, Pow, oo from sympy.core.compatibility import iterable, cmp_to_key from sympy.core.function import Derivative, diff, expand_mul from sympy.core.multidimensional import vectorize from sympy.core.relational import Equality, Eq from sympy.core.symbol import Symbol, Wild, Dummy from sympy.core.sympify import sympify from sympy.functions import cos, exp, im, log, re, sin, tan, sign, sqrt from sympy.matrices import wronskian from sympy.polys import Poly, RootOf from sympy.series import Order from sympy.simplify import collect, logcombine, powsimp, separatevars, \ simplify, trigsimp from sympy.solvers import solve from sympy.utilities import numbered_symbols # This is a list of hints in the order that they should be applied. That means # that, in general, hints earlier in the list should produce simpler results # than those later for ODEs that fit both. This is just based on my own # empirical observations, so if you find that *in general*, a hint later in # the list is better than one before it, fell free to modify the list. Note # however that you can easily override the hint used in dsolve() for a specific ODE # (see the docstring). In general, "_Integral" hints should be grouped # at the end of the list, unless there is a method that returns an unevaluable # integral most of the time (which should surely go near the end of the list # anyway). # "default", "all", "best", and "all_Integral" meta-hints should not be # included in this list, but "_best" and "_Integral" hints should be included. allhints = ("separable", "1st_exact", "1st_linear", "Bernoulli", "Riccati_special_minus2", "1st_homogeneous_coeff_best", "1st_homogeneous_coeff_subs_indep_div_dep", "1st_homogeneous_coeff_subs_dep_div_indep", "nth_linear_constant_coeff_homogeneous", "nth_linear_constant_coeff_undetermined_coefficients", "nth_linear_constant_coeff_variation_of_parameters", "Liouville", "separable_Integral", "1st_exact_Integral", "1st_linear_Integral", "Bernoulli_Integral", "1st_homogeneous_coeff_subs_indep_div_dep_Integral", "1st_homogeneous_coeff_subs_dep_div_indep_Integral", "nth_linear_constant_coeff_variation_of_parameters_Integral", "Liouville_Integral") def sub_func_doit(eq, func, new): """When replacing the func with something else, we usually want the derivative evaluated, so this function helps in making that happen. To keep subs from having to look through all derivatives, we mask them off with dummy variables, do the func sub, and then replace masked off derivatives with their doit values. """ reps = {} repu = {} for d in eq.atoms(Derivative): u = C.Dummy('u') repu[u] = d.subs(func, new).doit() reps[d] = u return eq.subs(reps).subs(func, new).subs(repu) def dsolve(eq, func, hint="default", simplify=True, **kwargs): """ Solves any (supported) kind of ordinary differential equation. **Usage** dsolve(eq, f(x), hint) -> Solve ordinary differential equation eq for function f(x), using method hint. **Details** ``eq`` can be any supported ordinary differential equation (see the ode docstring for supported methods). This can either be an Equality, or an expression, which is assumed to be equal to 0. ``f(x)`` is a function of one variable whose derivatives in that variable make up the ordinary differential equation eq. ``hint`` is the solving method that you want dsolve to use. Use classify_ode(eq, f(x)) to get all of the possible hints for an ODE. The default hint, 'default', will use whatever hint is returned first by classify_ode(). See Hints below for more options that you can use for hint. ``simplify`` enables simplification by odesimp(). See its docstring for more information. Turn this off, for example, to disable solving of solutions for func or simplification of arbitrary constants. It will still integrate with this hint. Note that the solution may contain more arbitrary constants than the order of the ODE with this option enabled. **Hints** Aside from the various solving methods, there are also some meta-hints that you can pass to dsolve(): "default": This uses whatever hint is returned first by classify_ode(). This is the default argument to dsolve(). "all": To make dsolve apply all relevant classification hints, use dsolve(ODE, func, hint="all"). This will return a dictionary of hint:solution terms. If a hint causes dsolve to raise the NotImplementedError, value of that hint's key will be the exception object raised. The dictionary will also include some special keys: - order: The order of the ODE. See also ode_order(). - best: The simplest hint; what would be returned by "best" below. - best_hint: The hint that would produce the solution given by 'best'. If more than one hint produces the best solution, the first one in the tuple returned by classify_ode() is chosen. - default: The solution that would be returned by default. This is the one produced by the hint that appears first in the tuple returned by classify_ode(). "all_Integral": This is the same as "all", except if a hint also has a corresponding "_Integral" hint, it only returns the "_Integral" hint. This is useful if "all" causes dsolve() to hang because of a difficult or impossible integral. This meta-hint will also be much faster than "all", because integrate() is an expensive routine. "best": To have dsolve() try all methods and return the simplest one. This takes into account whether the solution is solvable in the function, whether it contains any Integral classes (i.e. unevaluatable integrals), and which one is the shortest in size. See also the classify_ode() docstring for more info on hints, and the ode docstring for a list of all supported hints. **Tips** - You can declare the derivative of an unknown function this way: >>> from sympy import Function, Derivative >>> from sympy.abc import x # x is the independent variable >>> f = Function("f")(x) # f is a function of x >>> # f_ will be the derivative of f with respect to x >>> f_ = Derivative(f, x) - See test_ode.py for many tests, which serves also as a set of examples for how to use dsolve(). - dsolve always returns an Equality class (except for the case when the hint is "all" or "all_Integral"). If possible, it solves the solution explicitly for the function being solved for. Otherwise, it returns an implicit solution. - Arbitrary constants are symbols named C1, C2, and so on. - Because all solutions should be mathematically equivalent, some hints may return the exact same result for an ODE. Often, though, two different hints will return the same solution formatted differently. The two should be equivalent. Also note that sometimes the values of the arbitrary constants in two different solutions may not be the same, because one constant may have "absorbed" other constants into it. - Do help(ode.ode_hintname) to get help more information on a specific hint, where hintname is the name of a hint without "_Integral". **Examples** >>> from sympy import Function, dsolve, Eq, Derivative, sin, cos >>> from sympy.abc import x >>> f = Function('f') >>> dsolve(Derivative(f(x),x,x)+9*f(x), f(x)) f(x) == C1*cos(3*x) + C2*sin(3*x) >>> dsolve(sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x), f(x), ... hint='separable') -log(sin(f(x))**2 - 1)/2 == C1 + log(sin(x)**2 - 1)/2 >>> dsolve(sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x), f(x), ... hint='1st_exact') f(x) == acos(C1/cos(x)) >>> dsolve(sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x), f(x), ... hint='best') f(x) == acos(C1/cos(x)) >>> # Note that even though separable is the default, 1st_exact produces >>> # a simpler result in this case. """ # TODO: Implement initial conditions # See issue 1621. We first need a way to represent things like f'(0). if isinstance(eq, Equality): if eq.rhs != 0: return dsolve(eq.lhs-eq.rhs, func, hint=hint, simplify=simplify, **kwargs) eq = eq.lhs # Magic that should only be used internally. Prevents classify_ode from # being called more than it needs to be by passing its results through # recursive calls. if kwargs.get('classify', True): hints = classify_ode(eq, func, dict=True) else: # Here is what all this means: # # hint: The hint method given to dsolve() by the user. # hints: The dictionary of hints that match the ODE, along with # other information (including the internal pass-through magic). # default: The default hint to return, the first hint from allhints # that matches the hint. This is obtained from classify_ode(). # match: The hints dictionary contains a match dictionary for each hint # (the parts of the ODE for solving). When going through the # hints in "all", this holds the match string for the current # hint. # order: The order of the ODE, as determined by ode_order(). hints = kwargs.get('hint', {'default': hint, hint: kwargs['match'], 'order': kwargs['order']}) if hints['order'] == 0: raise ValueError(str(eq) + " is not a differential equation in " + str(func)) if not hints['default']: # classify_ode will set hints['default'] to None if no hints match. raise NotImplementedError("dsolve: Cannot solve " + str(eq)) if hint == 'default': return dsolve(eq, func, hint=hints['default'], simplify=simplify, classify=False, order=hints['order'], match=hints[hints['default']]) elif hint in ('all', 'all_Integral', 'best'): retdict = {} failedhints = {} gethints = set(hints) - set(['order', 'default', 'ordered_hints']) if hint == 'all_Integral': for i in hints: if i.endswith('_Integral'): gethints.remove(i[:-len('_Integral')]) # special case if "1st_homogeneous_coeff_best" in gethints: gethints.remove("1st_homogeneous_coeff_best") for i in gethints: try: sol = dsolve(eq, func, hint=i, simplify=simplify, classify=False, order=hints['order'], match=hints[i]) except NotImplementedError, detail: # except NotImplementedError as detail: failedhints[i] = detail else: retdict[i] = sol retdict['best'] = min(retdict.values(), key=lambda x: ode_sol_simplicity(x, func, trysolving=not simplify)) if hint == 'best': return retdict['best'] for i in hints['ordered_hints']: if retdict['best'] == retdict.get(i, None): retdict['best_hint'] = i break retdict['default'] = hints['default'] retdict['order'] = sympify(hints['order']) retdict.update(failedhints) return retdict elif hint not in allhints: # and hint not in ('default', 'ordered_hints'): raise ValueError("Hint not recognized: " + hint) elif hint not in hints: raise ValueError("ODE " + str(eq) + " does not match hint " + hint) elif hint.endswith('_Integral'): solvefunc = globals()['ode_' + hint[:-len('_Integral')]] else: solvefunc = globals()['ode_' + hint] # convert the string into a function # odesimp() will attempt to integrate, if necessary, apply constantsimp(), # attempt to solve for func, and apply any other hint specific simplifications if simplify: rv = odesimp(solvefunc(eq, func, order=hints['order'], match=hints[hint]), func, hints['order'], hint) else: # We still want to integrate (you can disable it separately with the hint) r = hints[hint] r['simplify'] = False # Some hints can take advantage of this option rv = _handle_Integral(solvefunc(eq, func, order=hints['order'], match=hints[hint]), func, hints['order'], hint) return rv def classify_ode(eq, func, dict=False): """ Returns a tuple of possible dsolve() classifications for an ODE. The tuple is ordered so that first item is the classification that dsolve() uses to solve the ODE by default. In general, classifications at the near the beginning of the list will produce better solutions faster than those near the end, thought there are always exceptions. To make dsolve use a different classification, use dsolve(ODE, func, hint=). See also the dsolve() docstring for different meta-hints you can use. If dict is true, classify_ode() will return a dictionary of hint:match expression terms. This is intended for internal use by dsolve(). Note that because dictionaries are ordered arbitrarily, this will most likely not be in the same order as the tuple. You can get help on different hints by doing help(ode.ode_hintname), where hintname is the name of the hint without "_Integral". See sympy.ode.allhints or the sympy.ode docstring for a list of all supported hints that can be returned from classify_ode. **Notes on Hint Names** *"_Integral"* If a classification has "_Integral" at the end, it will return the expression with an unevaluated Integral class in it. Note that a hint may do this anyway if integrate() cannot do the integral, though just using an "_Integral" will do so much faster. Indeed, an "_Integral" hint will always be faster than its corresponding hint without "_Integral" because integrate() is an expensive routine. If dsolve() hangs, it is probably because integrate() is hanging on a tough or impossible integral. Try using an "_Integral" hint or "all_Integral" to get it return something. Note that some hints do not have "_Integral" counterparts. This is because integrate() is not used in solving the ODE for those method. For example, nth order linear homogeneous ODEs with constant coefficients do not require integration to solve, so there is no "nth_linear_homogeneous_constant_coeff_Integrate" hint. You can easily evaluate any unevaluated Integrals in an expression by doing expr.doit(). *Ordinals* Some hints contain an ordinal such as "1st_linear". This is to help differentiate them from other hints, as well as from other methods that may not be implemented yet. If a hint has "nth" in it, such as the "nth_linear" hints, this means that the method used to applies to ODEs of any order. *"indep" and "dep"* Some hints contain the words "indep" or "dep". These reference the independent variable and the dependent function, respectively. For example, if an ODE is in terms of f(x), then "indep" will refer to x and "dep" will refer to f. *"subs"* If a hints has the word "subs" in it, it means the the ODE is solved by substituting the expression given after the word "subs" for a single dummy variable. This is usually in terms of "indep" and "dep" as above. The substituted expression will be written only in characters allowed for names of Python objects, meaning operators will be spelled out. For example, indep/dep will be written as indep_div_dep. *"coeff"* The word "coeff" in a hint refers to the coefficients of something in the ODE, usually of the derivative terms. See the docstring for the individual methods for more info (help(ode)). This is contrast to "coefficients", as in "undetermined_coefficients", which refers to the common name of a method. *"_best"* Methods that have more than one fundamental way to solve will have a hint for each sub-method and a "_best" meta-classification. This will evaluate all hints and return the best, using the same considerations as the normal "best" meta-hint. **Examples** >>> from sympy import Function, classify_ode, Eq >>> from sympy.abc import x >>> f = Function('f') >>> classify_ode(Eq(f(x).diff(x), 0), f(x)) ('separable', '1st_linear', '1st_homogeneous_coeff_best', '1st_homogeneous_coeff_subs_indep_div_dep', '1st_homogeneous_coeff_subs_dep_div_indep', 'nth_linear_constant_coeff_homogeneous', 'separable_Integral', '1st_linear_Integral', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_homogeneous_coeff_subs_dep_div_indep_Integral') >>> classify_ode(f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - 4, f(x)) ('nth_linear_constant_coeff_undetermined_coefficients', 'nth_linear_constant_coeff_variation_of_parameters', 'nth_linear_constant_coeff_variation_of_parameters_Integral') """ from sympy import expand if len(func.args) != 1: raise ValueError("dsolve() and classify_ode() only work with functions " + \ "of one variable") x = func.args[0] f = func.func y = Dummy('y') if isinstance(eq, Equality): if eq.rhs != 0: return classify_ode(eq.lhs-eq.rhs, func) eq = eq.lhs order = ode_order(eq, f(x)) # hint:matchdict or hint:(tuple of matchdicts) # Also will contain "default": and "order":order items. matching_hints = {"order": order} if not order: if dict: matching_hints["default"] = None return matching_hints else: return () df = f(x).diff(x) a = Wild('a', exclude=[f(x)]) b = Wild('b', exclude=[f(x)]) c = Wild('c', exclude=[f(x)]) d = Wild('d', exclude=[df, f(x).diff(x, 2)]) e = Wild('e', exclude=[df]) k = Wild('k', exclude=[df]) n = Wild('n', exclude=[f(x)]) c1 = Wild('c1', exclude=[x]) a2 = Wild('a2', exclude=[x, f(x), df]) b2 = Wild('b2', exclude=[x, f(x), df]) c2 = Wild('c2', exclude=[x, f(x), df]) d2 = Wild('d2', exclude=[x, f(x), df]) eq = expand(eq) # Precondition to try remove f(x) from highest order derivative reduced_eq = None if eq.is_Add: deriv_coef = eq.coeff(f(x).diff(x, order)) if deriv_coef != 1: r = deriv_coef.match(a*f(x)**c1) if r and r[c1]: den = f(x)**r[c1] reduced_eq = Add(*[arg/den for arg in eq.args]) if not reduced_eq: reduced_eq = eq if order == 1: # Linear case: a(x)*y'+b(x)*y+c(x) == 0 if eq.is_Add: ind, dep = reduced_eq.as_independent(f) else: u = Dummy('u') ind, dep = (reduced_eq + u).as_independent(f) ind, dep = [tmp.subs(u, 0) for tmp in [ind, dep]] r = {a: dep.coeff(df) or S.Zero, # if we get None for coeff, take 0 b: dep.coeff(f(x)) or S.Zero, # ditto c: ind} # double check f[a] since the preconditioning may have failed if not r[a].has(f) and (r[a]*df + r[b]*f(x) + r[c]).expand() - reduced_eq == 0: r['a'] = a r['b'] = b r['c'] = c matching_hints["1st_linear"] = r matching_hints["1st_linear_Integral"] = r # Bernoulli case: a(x)*y'+b(x)*y+c(x)*y**n == 0 r = collect(reduced_eq, f(x), exact = True).match(a*df + b*f(x) + c*f(x)**n) if r and r[c] != 0 and r[n] != 1: # See issue 1577 r['a'] = a r['b'] = b r['c'] = c r['n'] = n matching_hints["Bernoulli"] = r matching_hints["Bernoulli_Integral"] = r # Riccati special n == -2 case: a2*y'+b2*y**2+c2*y/x+d2/x**2 == 0 r = collect(reduced_eq, f(x), exact = True).match(a2*df + b2*f(x)**2 + c2*f(x)/x + d2/x**2) if r and r[b2] != 0 and (r[c2] != 0 or r[d2] != 0): r['a2'] = a2 r['b2'] = b2 r['c2'] = c2 r['d2'] = d2 matching_hints["Riccati_special_minus2"] = r # Exact Differential Equation: P(x,y)+Q(x,y)*y'=0 where dP/dy == dQ/dx # WITH NON-REDUCED FORM OF EQUATION r = collect(eq, df, exact = True).match(d + e * df) if r: r['d'] = d r['e'] = e r['y'] = y r[d] = r[d].subs(f(x),y) r[e] = r[e].subs(f(x),y) try: if r[d] != 0 and simplify(r[d].diff(y)) == simplify(r[e].diff(x)): matching_hints["1st_exact"] = r matching_hints["1st_exact_Integral"] = r except NotImplementedError: # Differentiating the coefficients might fail because of things # like f(2*x).diff(x). See issue 1525 and issue 1620. pass # This match is used for several cases below; we now collect on # f(x) so the matching works. r = collect(reduced_eq, df, exact = True).match(d+e*df) if r: r['d'] = d r['e'] = e r['y'] = y r[d] = r[d].subs(f(x),y) r[e] = r[e].subs(f(x),y) # Separable Case: y' == P(y)*Q(x) r[d] = separatevars(r[d]) r[e] = separatevars(r[e]) # m1[coeff]*m1[x]*m1[y] + m2[coeff]*m2[x]*m2[y]*y' m1 = separatevars(r[d], dict=True, symbols=(x, y)) m2 = separatevars(r[e], dict=True, symbols=(x, y)) if m1 and m2: r1 = {'m1':m1, 'm2':m2, 'y':y} matching_hints["separable"] = r1 matching_hints["separable_Integral"] = r1 # First order equation with homogeneous coefficients: # dy/dx == F(y/x) or dy/dx == F(x/y) ordera = homogeneous_order(r[d], x, y) orderb = homogeneous_order(r[e], x, y) if ordera == orderb and ordera is not None: # u1=y/x and u2=x/y u1 = Dummy('u1') u2 = Dummy('u2') if simplify((r[d]+u1*r[e]).subs({x:1, y:u1})) != 0: matching_hints["1st_homogeneous_coeff_subs_dep_div_indep"] = r matching_hints["1st_homogeneous_coeff_subs_dep_div_indep_Integral"] = r if simplify((r[e]+u2*r[d]).subs({x:u2, y:1})) != 0: matching_hints["1st_homogeneous_coeff_subs_indep_div_dep"] = r matching_hints["1st_homogeneous_coeff_subs_indep_div_dep_Integral"] = r if "1st_homogeneous_coeff_subs_dep_div_indep" in matching_hints \ and "1st_homogeneous_coeff_subs_indep_div_dep" in matching_hints: matching_hints["1st_homogeneous_coeff_best"] = r if order == 2: # Liouville ODE f(x).diff(x, 2) + g(f(x))*(f(x).diff(x))**2 + h(x)*f(x).diff(x) # See Goldstein and Braun, "Advanced Methods for the Solution of # Differential Equations", pg. 98 s = d*f(x).diff(x, 2) + e*df**2 + k*df r = reduced_eq.match(s) if r and r[d] != 0: y = Dummy('y') g = simplify(r[e]/r[d]).subs(f(x), y) h = simplify(r[k]/r[d]) if h.has(f(x)) or g.has(x): pass else: r = {'g':g, 'h':h, 'y':y} matching_hints["Liouville"] = r matching_hints["Liouville_Integral"] = r if order > 0: # nth order linear ODE # a_n(x)y^(n) + ... + a_1(x)y' + a_0(x)y = F(x) = b r = _nth_linear_match(reduced_eq, func, order) # Constant coefficient case (a_i is constant for all i) if r and not any(r[i].has(x) for i in r if i >= 0): # Inhomogeneous case: F(x) is not identically 0 if r[-1]: undetcoeff = _undetermined_coefficients_match(r[-1], x) matching_hints["nth_linear_constant_coeff_variation_of_parameters"] = r matching_hints["nth_linear_constant_coeff_variation_of_parameters" + \ "_Integral"] = r if undetcoeff['test']: r['trialset'] = undetcoeff['trialset'] matching_hints["nth_linear_constant_coeff_undetermined_" + \ "coefficients"] = r # Homogeneous case: F(x) is identically 0 else: matching_hints["nth_linear_constant_coeff_homogeneous"] = r # Order keys based on allhints. retlist = [] for i in allhints: if i in matching_hints: retlist.append(i) if dict: # Dictionaries are ordered arbitrarily, so we need to make note of which # hint would come first for dsolve(). In Python 3, this should be replaced # with an ordered dictionary. matching_hints["default"] = None matching_hints["ordered_hints"] = tuple(retlist) for i in allhints: if i in matching_hints: matching_hints["default"] = i break return matching_hints else: return tuple(retlist) @vectorize(0) def odesimp(eq, func, order, hint): r""" Simplifies ODEs, including trying to solve for func and running constantsimp(). It may use knowledge of the type of solution that that hint returns to apply additional simplifications. It also attempts to integrate any Integrals in the expression, if the hint is not an "_Integral" hint. This function should have no effect on expressions returned by dsolve(), as dsolve already calls odesimp(), but the individual hint functions do not call odesimp (because the dsolve() wrapper does). Therefore, this function is designed for mainly internal use. **Example** >>> from sympy import sin, symbols, dsolve, pprint, Function >>> from sympy.solvers.ode import odesimp >>> x , u2, C1= symbols('x,u2,C1') >>> f = Function('f') >>> eq = dsolve(x*f(x).diff(x) - f(x) - x*sin(f(x)/x), f(x), ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral', ... simplify=False) >>> pprint(eq) x ---- f(x) / | | / /1 \ \ | -|u2*sin|--| + 1| /f(x)\ | \ \u2/ / log|----| - | ----------------- d(u2) = 0 \ C1 / | 2 /1 \ | u2 *sin|--| | \u2/ | / >> pprint(odesimp(eq, f(x), 1, ... hint='1st_homogeneous_coeff_subs_indep_div_dep' ... )) # (this is slow, so we skip) x --------- = C1 /f(x)\ tan|----| \2*x / """ x = func.args[0] f = func.func C1 = Symbol('C1') # First, integrate if the hint allows it. eq = _handle_Integral(eq, func, order, hint) assert isinstance(eq, Equality) # Second, clean up the arbitrary constants. # Right now, nth linear hints can put as many as 2*order constants in an # expression. If that number grows with another hint, the third argument # here should be raised accordingly, or constantsimp() rewritten to handle # an arbitrary number of constants. eq = constantsimp(eq, x, 2*order) # Lastly, now that we have cleaned up the expression, try solving for func. # When RootOf is implemented in solve(), we will want to return a RootOf # everytime instead of an Equality. # Get the f(x) on the left if possible. if eq.rhs == func and not eq.lhs.has(func): eq = [Eq(eq.rhs, eq.lhs)] # make sure we are working with lists of solutions in simplified form. if eq.lhs == func and not eq.rhs.has(func): # The solution is already solved eq = [eq] # special simplification of the rhs if hint.startswith("nth_linear_constant_coeff"): # Collect terms to make the solution look nice. # This is also necessary for constantsimp to remove unnecessary terms # from the particular solution from variation of parameters global collectterms assert len(eq) == 1 and eq[0].lhs == f(x) sol = eq[0].rhs sol = expand_mul(sol) for i, reroot, imroot in collectterms: sol = collect(sol, x**i*exp(reroot*x)*sin(abs(imroot)*x)) sol = collect(sol, x**i*exp(reroot*x)*cos(imroot*x)) for i, reroot, imroot in collectterms: sol = collect(sol, x**i*exp(reroot*x)) del collectterms eq[0] = Eq(f(x), sol) else: # The solution is not solved, so try to solve it try: eqsol = solve(eq, func) if eqsol == []: raise NotImplementedError except NotImplementedError: eq = [eq] else: def _expand(expr): numer, denom = expr.as_numer_denom() if denom.is_Add: return expr else: return powsimp(expr.expand(), combine='exp', deep=True) # XXX: the rest of odesimp() expects each ``t`` to be in a # specific normal form: rational expression with numerator # expanded, but with combined exponential functions (at # least in this setup all tests pass). eq = [Eq(f(x), _expand(t)) for t in eqsol] # special simplification of the lhs. if hint.startswith("1st_homogeneous_coeff"): for j, eqi in enumerate(eq): newi = logcombine(eqi, force=True) if newi.lhs.is_Function and newi.lhs.func is log and newi.rhs == 0: newi = Eq(newi.lhs.args[0]/C1, C1) eq[j] = newi # We cleaned up the costants before solving to help the solve engine with # a simpler expression, but the solved expression could have introduced # things like -C1, so rerun constantsimp() one last time before returning. for i, eqi in enumerate(eq): eq[i] = constant_renumber(constantsimp(eqi, x, 2*order), 'C', 1, 2*order) # If there is only 1 solution, return it; # otherwise return the list of solutions. if len(eq) == 1: eq = eq[0] return eq def checkodesol(ode, func, sol, order='auto', solve_for_func=True): """ Substitutes sol for func in ode and checks that the result is 0. This only works when func is one function, like f(x). sol can be a single solution or a list of solutions. Either way, each solution must be an Equality instance (e.g., Eq(f(x), C1*cos(x) + C2*sin(x))). If it is a list of solutions, it will return a list of the checkodesol() result for each solution. It tries the following methods, in order, until it finds zero equivalence: 1. Substitute the solution for f in the original equation. This only works if the ode is solved for f. It will attempt to solve it first unless solve_for_func == False 2. Take n derivatives of the solution, where n is the order of ode, and check to see if that is equal to the solution. This only works on exact odes. 3. Take the 1st, 2nd, ..., nth derivatives of the solution, each time solving for the derivative of f of that order (this will always be possible because f is a linear operator). Then back substitute each derivative into ode in reverse order. This function returns a tuple. The first item in the tuple is True if the substitution results in 0, and False otherwise. The second item in the tuple is what the substitution results in. It should always be 0 if the first item is True. Note that sometimes this function will False, but with an expression that is identically equal to 0, instead of returning True. This is because simplify() cannot reduce the expression to 0. If an expression returned by this function vanishes identically, then sol really is a solution to ode. If this function seems to hang, it is probably because of a hard simplification. To use this function to test, test the first item of the tuple. **Examples** >>> from sympy import Eq, Function, checkodesol, symbols >>> x, C1 = symbols('x,C1') >>> f = Function('f') >>> checkodesol(f(x).diff(x), f(x), Eq(f(x), C1)) (True, 0) >>> assert checkodesol(f(x).diff(x), f(x), Eq(f(x), C1))[0] >>> assert not checkodesol(f(x).diff(x), f(x), Eq(f(x), x))[0] >>> checkodesol(f(x).diff(x, 2), f(x), Eq(f(x), x**2)) (False, 2) """ if type(sol) in (tuple, list, set): return type(sol)(map(lambda i: checkodesol(ode, func, i, order=order, solve_for_func=solve_for_func), sol)) if not func.is_Function or len(func.args) != 1: raise ValueError("func must be a function of one variable, not " + str(func)) x = func.args[0] s = True testnum = 0 if not isinstance(ode, Equality): ode = Eq(ode, 0) if not isinstance(sol, Equality): raise ValueError("sol must be an Equality, got " + str(sol)) if order == 'auto': order = ode_order(ode, func) if solve_for_func and not (sol.lhs == func and not sol.rhs.has(func)) and not \ (sol.rhs == func and not sol.lhs.has(func)): try: solved = solve(sol, func) if solved == []: raise NotImplementedError except NotImplementedError: pass else: if len(solved) == 1: result = checkodesol(ode, func, Eq(func, solved[0]), \ order=order, solve_for_func=False) else: result = checkodesol(ode, func, [Eq(func, t) for t in solved], order=order, solve_for_func=False) return result while s: if testnum == 0: # First pass, try substituting a solved solution directly into the ode # This has the highest chance of succeeding. ode_diff = ode.lhs - ode.rhs if sol.lhs == func: s = sub_func_doit(ode_diff, func, sol.rhs) elif sol.rhs == func: s = sub_func_doit(ode_diff, func, sol.lhs) else: testnum += 1 continue ss = simplify(s) if ss: # with the new numer_denom in power.py, if we do a simple # expansion then testnum == 0 verifies all solutions. s = ss.expand() else: s = 0 testnum += 1 elif testnum == 1: # Second pass. If we cannot substitute f, try seeing if the nth # derivative is equal, this will only work for odes that are exact, # by definition. s = simplify(trigsimp(diff(sol.lhs, x, order) - diff(sol.rhs, x, order)) - \ trigsimp(ode.lhs) + trigsimp(ode.rhs)) # s2 = simplify(diff(sol.lhs, x, order) - diff(sol.rhs, x, order) - \ # ode.lhs + ode.rhs) testnum += 1 elif testnum == 2: # Third pass. Try solving for df/dx and substituting that into the ode. # Thanks to Chris Smith for suggesting this method. Many of the # comments below are his too. # The method: # - Take each of 1..n derivatives of the solution. # - Solve each nth derivative for d^(n)f/dx^(n) # (the differential of that order) # - Back substitute into the ode in decreasing order # (i.e., n, n-1, ...) # - Check the result for zero equivalence if sol.lhs == func and not sol.rhs.has(func): diffsols = {0:sol.rhs} elif sol.rhs == func and not sol.lhs.has(func): diffsols = {0:sol.lhs} else: diffsols = {} sol = sol.lhs - sol.rhs for i in range(1, order + 1): # Differentiation is a linear operator, so there should always # be 1 solution. Nonetheless, we test just to make sure. # We only need to solve once. After that, we will automatically # have the solution to the differential in the order we want. if i == 1: ds = sol.diff(x) try: sdf = solve(ds,func.diff(x, i)) if len(sdf) != 1: raise NotImplementedError except NotImplementedError: testnum += 1 break else: diffsols[i] = sdf[0] else: # This is what the solution says df/dx should be. diffsols[i] = diffsols[i - 1].diff(x) # Make sure the above didn't fail. if testnum > 2: continue else: # Substitute it into ode to check for self consistency. lhs, rhs = ode.lhs, ode.rhs for i in range(order, -1, -1): if i == 0 and 0 not in diffsols: # We can only substitute f(x) if the solution was # solved for f(x). break lhs = sub_func_doit(lhs, func.diff(x, i), diffsols[i]) rhs = sub_func_doit(rhs, func.diff(x, i), diffsols[i]) ode_or_bool = Eq(lhs,rhs) ode_or_bool = simplify(ode_or_bool) if isinstance(ode_or_bool, bool): if ode_or_bool: lhs = rhs = S.Zero else: lhs = ode_or_bool.lhs rhs = ode_or_bool.rhs # No sense in overworking simplify--just prove the numerator goes to zero s = simplify(trigsimp((lhs-rhs).as_numer_denom()[0])) testnum += 1 else: break if not s: return (True, s) elif s is True: # The code above never was able to change s raise NotImplementedError("Unable to test if " + str(sol) + \ " is a solution to " + str(ode) + ".") else: return (False, s) def ode_sol_simplicity(sol, func, trysolving=True): """ Returns an extended integer representing how simple a solution to an ODE is. The following things are considered, in order from most simple to least: - sol is solved for func. - sol is not solved for func, but can be if passed to solve (e.g., a solution returned by dsolve(ode, func, simplify=False) - If sol is not solved for func, then base the result on the length of sol, as computed by len(str(sol)). - If sol has any unevaluated Integrals, this will automatically be considered less simple than any of the above. This function returns an integer such that if solution A is simpler than solution B by above metric, then ode_sol_simplicity(sola, func) < ode_sol_simplicity(solb, func). Currently, the following are the numbers returned, but if the heuristic is ever improved, this may change. Only the ordering is guaranteed. sol solved for func -2 sol not solved for func but can be -1 sol is not solved or solvable for func len(str(sol)) sol contains an Integral oo oo here means the SymPy infinity, which should compare greater than any integer. If you already know solve() cannot solve sol, you can use trysolving=False to skip that step, which is the only potentially slow step. For example, dsolve with the simplify=False flag should do this. If sol is a list of solutions, if the worst solution in the list returns oo it returns that, otherwise it returns len(str(sol)), that is, the length of the string representation of the whole list. **Examples** This function is designed to be passed to min as the key argument, such as min(listofsolutions, key=lambda i: ode_sol_simplicity(i, f(x))). >>> from sympy import symbols, Function, Eq, tan, cos, sqrt, Integral >>> from sympy.solvers.ode import ode_sol_simplicity >>> x, C1 = symbols('x,C1') >>> f = Function('f') >>> ode_sol_simplicity(Eq(f(x), C1*x**2), f(x)) -2 >>> ode_sol_simplicity(Eq(x**2 + f(x), C1), f(x)) -1 >>> ode_sol_simplicity(Eq(f(x), C1*Integral(2*x, x)), f(x)) oo >>> # This is from dsolve(x*f(x).diff(x) - f(x) - x*sin(f(x)/x), \ >>> # f(x), hint='1st_homogeneous_coeff_subs_indep_div_dep') >>> eq1 = Eq(x/tan(f(x)/(2*x)), C1) >>> # This is from the same ode with the >>> # '1st_homogeneous_coeff_subs_dep_div_indep' hint. >>> eq2 = Eq(x*sqrt(1 + cos(f(x)/x))/sqrt(-1 + cos(f(x)/x)), C1) >>> ode_sol_simplicity(eq1, f(x)) 23 >>> min([eq1, eq2], key=lambda i: ode_sol_simplicity(i, f(x))) x/tan(f(x)/(2*x)) == C1 """ #TODO: write examples # See the docstring for the coercion rules. We check easier (faster) # things here first, to save time. if iterable(sol): # See if there are Integrals for i in sol: if ode_sol_simplicity(i, func, trysolving=trysolving) == oo: return oo return len(str(sol)) if sol.has(C.Integral): return oo # Next, try to solve for func. This code will change slightly when RootOf # is implemented in solve(). Probably a RootOf solution should fall somewhere # between a normal solution and an unsolvable expression. # First, see if they are already solved if sol.lhs == func and not sol.rhs.has(func) or\ sol.rhs == func and not sol.lhs.has(func): return -2 # We are not so lucky, try solving manually if trysolving: try: sols = solve(sol, func) if sols == []: raise NotImplementedError except NotImplementedError: pass else: return -1 # Finally, a naive computation based on the length of the string version # of the expression. This may favor combined fractions because they # will not have duplicate denominators, and may slightly favor expressions # with fewer additions and subtractions, as those are separated by spaces # by the printer. # Additional ideas for simplicity heuristics are welcome, like maybe # checking if a equation has a larger domain, or if constantsimp has # introduced arbitrary constants numbered higher than the order of a # given ode that sol is a solution of. return len(str(sol)) @vectorize(0) def constantsimp(expr, independentsymbol, endnumber, startnumber=1, symbolname='C'): """ Simplifies an expression with arbitrary constants in it. This function is written specifically to work with dsolve(), and is not intended for general use. Simplification is done by "absorbing" the arbitrary constants in to other arbitrary constants, numbers, and symbols that they are not independent of. The symbols must all have the same name with numbers after it, for example, C1, C2, C3. The symbolname here would be 'C', the startnumber would be 1, and the end number would be 3. If the arbitrary constants are independent of the variable x, then the independent symbol would be x. There is no need to specify the dependent function, such as f(x), because it already has the independent symbol, x, in it. Because terms are "absorbed" into arbitrary constants and because constants are renumbered after simplifying, the arbitrary constants in expr are not necessarily equal to the ones of the same name in the returned result. If two or more arbitrary constants are added, multiplied, or raised to the power of each other, they are first absorbed together into a single arbitrary constant. Then the new constant is combined into other terms if necessary. Absorption is done naively. constantsimp() does not attempt to expand or simplify the expression first to obtain better absorption. So for example, exp(C1)*exp(x) will be simplified to C1*exp(x), but exp(C1 + x) will be left alone. Use constant_renumber() to renumber constants after simplification. Without using that function, simplified constants may end up having any numbering to them. In rare cases, a single constant can be "simplified" into two constants. Every differential equation solution should have as many arbitrary constants as the order of the differential equation. The result here will be technically correct, but it may, for example, have C1 and C2 in an expression, when C1 is actually equal to C2. Use your discretion in such situations, and also take advantage of the ability to use hints in dsolve(). **Examples** >>> from sympy import symbols >>> from sympy.solvers.ode import constantsimp >>> C1, C2, C3, x, y = symbols('C1,C2,C3,x,y') >>> constantsimp(2*C1*x, x, 3) C1*x >>> constantsimp(C1 + 2 + x + y, x, 3) C1 + x >>> constantsimp(C1*C2 + 2 + x + y + C3*x, x, 3) C2 + C3*x + x """ # This function works recursively. The idea is that, for Mul, # Add, Pow, and Function, if the class has a constant in it, then # we can simplify it, which we do by recursing down and # simplifying up. Otherwise, we can skip that part of the # expression. constantsymbols = [Symbol(symbolname+"%d" % t) for t in range(startnumber, endnumber + 1)] constantsymbols_set = set(constantsymbols) x = independentsymbol if isinstance(expr, Equality): # For now, only treat the special case where one side of the equation # is a constant if expr.lhs in constantsymbols_set: return Eq(expr.lhs, constantsimp(expr.rhs + expr.lhs, x, endnumber, startnumber, symbolname) - expr.lhs) # this could break if expr.lhs is absorbed into another constant, # but for now, the only solutions that return Eq's with a constant # on one side are first order. At any rate, it will still be # technically correct. The expression will just have too many # constants in it elif expr.rhs in constantsymbols_set: return Eq(constantsimp(expr.lhs + expr.rhs, x, endnumber, startnumber, symbolname) - expr.rhs, expr.rhs) else: return Eq(constantsimp(expr.lhs, x, endnumber, startnumber, symbolname), constantsimp(expr.rhs, x, endnumber, startnumber, symbolname)) if type(expr) not in (Mul, Add, Pow) and not expr.is_Function: # We don't know how to handle other classes # This also serves as the base case for the recursion return expr elif not expr.has(*constantsymbols): return expr else: newargs = [] hasconst = False isPowExp = False reeval = False for i in expr.args: if i not in constantsymbols: newargs.append(i) else: newconst = i hasconst = True if expr.is_Pow and i == expr.exp: isPowExp = True for i in range(len(newargs)): isimp = constantsimp(newargs[i], x, endnumber, startnumber, symbolname) if isimp in constantsymbols: reeval = True hasconst = True newconst = isimp if expr.is_Pow and i == 1: isPowExp = True newargs[i] = isimp if hasconst: newargs = [i for i in newargs if i.has(x)] if isPowExp: newargs = newargs + [newconst] # Order matters in this case else: newargs = [newconst] + newargs if expr.is_Pow and len(newargs) == 1: newargs.append(S.One) if expr.is_Function: if (len(newargs) == 0 or hasconst and len(newargs) == 1): return newconst else: newfuncargs = [constantsimp(t, x, endnumber, startnumber, symbolname) for t in expr.args] return expr.func(*newfuncargs) else: newexpr = expr.func(*newargs) if reeval: return constantsimp(newexpr, x, endnumber, startnumber, symbolname) else: return newexpr def constant_renumber(expr, symbolname, startnumber, endnumber): """ Renumber arbitrary constants in expr. This is a simple function that goes through and renumbers any Symbol with a name in the form symbolname + num where num is in the range from startnumber to endnumber. Symbols are renumbered based on Basic._compare_pretty, so they should be numbered roughly in the order that they appear in the final, printed expression. Note that this ordering is based in part on hashes, so it can produce different results on different machines. The structure of this function is very similar to that of constantsimp(). **Example** >>> from sympy import symbols, Eq, pprint >>> from sympy.solvers.ode import constant_renumber >>> x, C1, C2, C3 = symbols('x,C1,C2,C3') >>> pprint(C2 + C1*x + C3*x**2) 2 C1*x + C2 + C3*x >>> pprint(constant_renumber(C2 + C1*x + C3*x**2, 'C', 1, 3)) 2 C1 + C2*x + C3*x """ if type(expr) in (set, list, tuple): return type(expr)(map(lambda i: constant_renumber(i, symbolname=symbolname, startnumber=startnumber, endnumber=endnumber), expr)) global newstartnumber newstartnumber = 1 def _constant_renumber(expr, symbolname, startnumber, endnumber): """ We need to have an internal recursive function so that newstartnumber maintains its values throughout recursive calls. """ constantsymbols = [Symbol(symbolname+"%d" % t) for t in range(startnumber, endnumber + 1)] global newstartnumber if isinstance(expr, Equality): return Eq(_constant_renumber(expr.lhs, symbolname, startnumber, endnumber), _constant_renumber(expr.rhs, symbolname, startnumber, endnumber)) if type(expr) not in (Mul, Add, Pow) and not expr.is_Function and\ not expr.has(*constantsymbols): # Base case, as above. We better hope there aren't constants inside # of some other class, because they won't be renumbered. return expr elif expr in constantsymbols: # Renumbering happens here newconst = Symbol(symbolname + str(newstartnumber)) newstartnumber += 1 return newconst else: if expr.is_Function or expr.is_Pow: return expr.func(*[_constant_renumber(x, symbolname, startnumber, endnumber) for x in expr.args]) else: sortedargs = list(expr.args) # make a mapping to send all constantsymbols to S.One and use # that to make sure that term ordering is not dependent on # the indexed value of C C_1 = [(ci, S.One) for ci in constantsymbols] sortedargs.sort(key=cmp_to_key(lambda x, y:\ Basic._compare_pretty(x.subs(C_1), y.subs(C_1)))) return expr.func(*[_constant_renumber(x, symbolname, startnumber, endnumber) for x in sortedargs]) return _constant_renumber(expr, symbolname, startnumber, endnumber) def _handle_Integral(expr, func, order, hint): """ Converts a solution with Integrals in it into an actual solution. For most hints, this simply runs expr.doit() """ x = func.args[0] f = func.func if hint == "1st_exact": global exactvars x0 = exactvars['x0'] y0 = exactvars['y0'] y = exactvars['y'] tmpsol = expr.lhs.doit() sol = 0 assert tmpsol.is_Add for i in tmpsol.args: if x0 not in i and y0 not in i: sol += i assert sol != 0 sol = Eq(sol.subs(y, f(x)),expr.rhs) # expr.rhs == C1 del exactvars elif hint == "1st_exact_Integral": # FIXME: We still need to back substitute y # y = exactvars['y'] # sol = expr.subs(y, f(x)) # For now, we are going to have to return an expression with f(x) replaced # with y. Substituting results in the y's in the second integral # becoming f(x), which prevents the integral from being evaluatable. # For example, Integral(cos(f(x)), (x, x0, x)). If there were a way to # do inert substitution, that could maybe be used here instead. del exactvars sol = expr elif hint == "nth_linear_constant_coeff_homogeneous": sol = expr elif not hint.endswith("_Integral"): sol = expr.doit() else: sol = expr return sol def ode_order(expr, func): """ Returns the order of a given ODE with respect to func. This function is implemented recursively. **Examples** >>> from sympy import Function, ode_order >>> from sympy.abc import x >>> f, g = map(Function, ['f', 'g']) >>> ode_order(f(x).diff(x, 2) + f(x).diff(x)**2 + ... f(x).diff(x), f(x)) 2 >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), f(x)) 2 >>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), g(x)) 3 """ a = Wild('a', exclude=[func]) order = 0 if isinstance(expr, Derivative) and expr.args[0] == func: order = len(expr.variables) else: for arg in expr.args: if isinstance(arg, Derivative) and arg.args[0] == func: order = max(order, len(arg.variables)) elif expr.match(a): order = 0 else : for arg1 in arg.args: order = max(order, ode_order(arg1, func)) return order # FIXME: replace the general solution in the docstring with # dsolve(equation, hint='1st_exact_Integral'). You will need to be able # to have assumptions on P and Q that dP/dy = dQ/dx. def ode_1st_exact(eq, func, order, match): r""" Solves 1st order exact ordinary differential equations. A 1st order differential equation is called exact if it is the total differential of a function. That is, the differential equation P(x, y)dx + Q(x, y)dy = 0 is exact if there is some function F(x, y) such that P(x, y) = dF/dx and Q(x, y) = dF/dy (d here refers to the partial derivative). It can be shown that a necessary and sufficient condition for a first order ODE to be exact is that dP/dy = dQ/dx. Then, the solution will be as given below:: >>> from sympy import Function, Eq, Integral, symbols, pprint >>> x, y, t, x0, y0, C1= symbols('x,y,t,x0,y0,C1') >>> P, Q, F= map(Function, ['P', 'Q', 'F']) >>> pprint(Eq(Eq(F(x, y), Integral(P(t, y), (t, x0, x)) + ... Integral(Q(x0, t), (t, y0, y))), C1)) x y / / | | F(x, y) = | P(t, y) dt + | Q(x0, t) dt = C1 | | / / x0 y0 Where the first partials of P and Q exist and are continuous in a simply connected region. A note: SymPy currently has no way to represent inert substitution on an expression, so the hint '1st_exact_Integral' will return an integral with dy. This is supposed to represent the function that you are solving for. **Example** >>> from sympy import Function, dsolve, cos, sin >>> from sympy.abc import x >>> f = Function('f') >>> dsolve(cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x), ... f(x), hint='1st_exact') x*cos(f(x)) + f(x)**3/3 == C1 **References** - http://en.wikipedia.org/wiki/Exact_differential_equation - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 73 # indirect doctest """ x = func.args[0] f = func.func r = match # d+e*diff(f(x),x) C1 = Symbol('C1') x0 = Dummy('x0') y0 = Dummy('y0') global exactvars # This is the only way to pass these dummy variables to # _handle_Integral exactvars = {'y0':y0, 'x0':x0, 'y':r['y']} # If we ever get a Constant class, x0 and y0 should be constants, I think sol = C.Integral(r[r['e']].subs(x,x0),(r['y'],y0,f(x)))+C.Integral(r[r['d']],(x,x0,x)) return Eq(sol, C1) def ode_1st_homogeneous_coeff_best(eq, func, order, match): r""" Returns the best solution to an ODE from the two hints '1st_homogeneous_coeff_subs_dep_div_indep' and '1st_homogeneous_coeff_subs_indep_div_dep'. This is as determined by ode_sol_simplicity(). See the ode_1st_homogeneous_coeff_subs_indep_div_dep() and ode_1st_homogeneous_coeff_subs_dep_div_indep() docstrings for more information on these hints. Note that there is no '1st_homogeneous_coeff_best_Integral' hint. **Example** :: >>> from sympy import Function, dsolve, pprint >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x), ... hint='1st_homogeneous_coeff_best')) ___________ / 2 / 3*x / ----- + 1 *f(x) = C1 3 / 2 \/ f (x) **References** - http://en.wikipedia.org/wiki/Homogeneous_differential_equation - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 59 # indirect doctest """ # There are two substitutions that solve the equation, u1=y/x and u2=x/y # They produce different integrals, so try them both and see which # one is easier. sol1 = ode_1st_homogeneous_coeff_subs_indep_div_dep(eq, func, order, match) sol2 = ode_1st_homogeneous_coeff_subs_dep_div_indep(eq, func, order, match) simplify = match.get('simplify', True) if simplify: sol1 = odesimp(sol1, func, order, "1st_homogeneous_coeff_subs_indep_div_dep") sol2 = odesimp(sol2, func, order, "1st_homogeneous_coeff_subs_dep_div_indep") return min([sol1, sol2], key=lambda x: ode_sol_simplicity(x, func, trysolving=not simplify)) def ode_1st_homogeneous_coeff_subs_dep_div_indep(eq, func, order, match): r""" Solves a 1st order differential equation with homogeneous coefficients using the substitution u1 = /. This is a differential equation P(x, y) + Q(x, y)dy/dx = 0, that P and Q are homogeneous of the same order. A function F(x, y) is homogeneous of order n if F(xt, yt) = t**n*F(x, y). Equivalently, F(x, y) can be rewritten as G(y/x) or H(x/y). See also the docstring of homogeneous_order(). If the coefficients P and Q in the differential equation above are homogeneous functions of the same order, then it can be shown that the substitution y = u1*x (u1 = y/x) will turn the differential equation into an equation separable in the variables x and u. If h(u1) is the function that results from making the substitution u1 = f(x)/x on P(x, f(x)) and g(u2) is the function that results from the substitution on Q(x, f(x)) in the differential equation P(x, f(x)) + Q(x, f(x))*diff(f(x), x) = 0, then the general solution is:: >>> from sympy import Function, dsolve, pprint >>> from sympy.abc import x >>> f, g, h = map(Function, ['f', 'g', 'h']) >>> genform = g(f(x)/x) + h(f(x)/x)*f(x).diff(x) >>> pprint(genform) /f(x)\ /f(x)\ d g|----| + h|----|*--(f(x)) \ x / \ x / dx >>> pprint(dsolve(genform, f(x), ... hint='1st_homogeneous_coeff_subs_dep_div_indep_Integral')) f(x) ---- x / | | -h(u1) log(C1*x) - | ---------------- d(u1) = 0 | u1*h(u1) + g(u1) | / Where u1*h(u1) + g(u1) != 0 and x != 0. See also the docstrings of ode_1st_homogeneous_coeff_best() and ode_1st_homogeneous_coeff_subs_indep_div_dep(). **Example** :: >>> from sympy import Function, dsolve >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x), ... hint='1st_homogeneous_coeff_subs_dep_div_indep')) ________________ / 3 / 3*f(x) f (x) x* / ------ + ----- = C1 3 / x 3 \/ x **References** - http://en.wikipedia.org/wiki/Homogeneous_differential_equation - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 59 # indirect doctest """ x = func.args[0] f = func.func u1 = Dummy('u1') # u1 == f(x)/x r = match # d+e*diff(f(x),x) C1 = Symbol('C1') int = C.Integral((-r[r['e']]/(r[r['d']]+u1*r[r['e']])).subs({x:1, r['y']:u1}), (u1, None, f(x)/x)) sol = logcombine(Eq(log(x), int + log(C1)), force=True) return sol def ode_1st_homogeneous_coeff_subs_indep_div_dep(eq, func, order, match): r""" Solves a 1st order differential equation with homogeneous coefficients using the substitution u2 = /. This is a differential equation P(x, y) + Q(x, y)dy/dx = 0, that P and Q are homogeneous of the same order. A function F(x, y) is homogeneous of order n if F(xt, yt) = t**n*F(x, y). Equivalently, F(x, y) can be rewritten as G(y/x) or H(x/y). See also the docstring of homogeneous_order(). If the coefficients P and Q in the differential equation above are homogeneous functions of the same order, then it can be shown that the substitution x = u2*y (u2 = x/y) will turn the differential equation into an equation separable in the variables y and u2. If h(u2) is the function that results from making the substitution u2 = x/f(x) on P(x, f(x)) and g(u2) is the function that results from the substitution on Q(x, f(x)) in the differential equation P(x, f(x)) + Q(x, f(x))*diff(f(x), x) = 0, then the general solution is: >>> from sympy import Function, dsolve, pprint >>> from sympy.abc import x >>> f, g, h = map(Function, ['f', 'g', 'h']) >>> genform = g(x/f(x)) + h(x/f(x))*f(x).diff(x) >>> pprint(genform) / x \ / x \ d g|----| + h|----|*--(f(x)) \f(x)/ \f(x)/ dx >>> pprint(dsolve(genform, f(x), ... hint='1st_homogeneous_coeff_subs_indep_div_dep_Integral')) x ---- f(x) / | | g(u2) log(C1*f(x)) - | ----------------- d(u2) = 0 | -u2*g(u2) - h(u2) | / Where u2*g(u2) + h(u2) != 0 and f(x) != 0. See also the docstrings of ode_1st_homogeneous_coeff_best() and ode_1st_homogeneous_coeff_subs_dep_div_indep(). **Example** >>> from sympy import Function, pprint >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x), f(x), ... hint='1st_homogeneous_coeff_subs_indep_div_dep')) ___________ / 2 / 3*x / ----- + 1 *f(x) = C1 3 / 2 \/ f (x) **References** - http://en.wikipedia.org/wiki/Homogeneous_differential_equation - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 59 # indirect doctest """ x = func.args[0] f = func.func u2 = Dummy('u2') # u2 == x/f(x) r = match # d+e*diff(f(x),x) C1 = Symbol('C1') int = C.Integral(simplify((-r[r['d']]/(r[r['e']]+u2*r[r['d']])).subs({x:u2, r['y']:1})), (u2, None, x/f(x))) sol = logcombine(Eq(log(f(x)), int + log(C1)), force=True) return sol # XXX: Should this function maybe go somewhere else? def homogeneous_order(eq, *symbols): """ Returns the order n if g is homogeneous and None if it is not homogeneous. Determines if a function is homogeneous and if so of what order. A function f(x,y,...) is homogeneous of order n if f(t*x,t*y,t*...) == t**n*f(x,y,...). If the function is of two variables, F(x, y), then f being homogeneous of any order is equivalent to being able to rewrite F(x, y) as G(x/y) or H(y/x). This fact is used to solve 1st order ordinary differential equations whose coefficients are homogeneous of the same order (see the docstrings of ode.ode_1st_homogeneous_coeff_subs_indep_div_dep() and ode.ode_1st_homogeneous_coeff_subs_indep_div_dep() Symbols can be functions, but every argument of the function must be a symbol, and the arguments of the function that appear in the expression must match those given in the list of symbols. If a declared function appears with different arguments than given in the list of symbols, None is returned. **Examples** >>> from sympy import Function, homogeneous_order, sqrt >>> from sympy.abc import x, y >>> f = Function('f') >>> homogeneous_order(f(x), f(x)) == None True >>> homogeneous_order(f(x,y), f(y, x), x, y) == None True >>> homogeneous_order(f(x), f(x), x) 1 >>> homogeneous_order(x**2*f(x)/sqrt(x**2+f(x)**2), x, f(x)) 2 >>> homogeneous_order(x**2+f(x), x, f(x)) == None True """ if eq.has(log): eq = logcombine(eq, force=True) return _homogeneous_order(eq, *symbols) def _homogeneous_order(eq, *symbols): """ The real work for homogeneous_order. This runs as a separate function call so that logcombine doesn't endlessly put back together what homogeneous_order is trying to take apart. """ if not symbols: raise ValueError("homogeneous_order: no symbols were given.") n = set() # Replace all functions with dummy variables for i in symbols: if i.is_Function: if not all([j in symbols for j in i.args]): return None else: dummyvar = numbered_symbols(prefix='d', cls=Dummy).next() eq = eq.subs(i, dummyvar) symbols = list(symbols) symbols.remove(i) symbols.append(dummyvar) symbols = tuple(symbols) # The following are not supported if eq.has(Order, Derivative): return None # These are all constants if type(eq) in (int, float) or eq.is_Number or eq.is_Integer or \ eq.is_Rational or eq.is_NumberSymbol or eq.is_Float: return sympify(0) # Break the equation into additive parts if eq.is_Add: s = set() for i in eq.args: s.add(_homogeneous_order(i, *symbols)) if len(s) != 1: return None else: n = s if eq.is_Pow: if not eq.exp.is_number: return None o = _homogeneous_order(eq.base, *symbols) if o == None: return None else: n.add(sympify(o*eq.exp)) t = Dummy('t', positive=True) # It is sufficient that t > 0 r = Wild('r', exclude=[t]) a = Wild('a', exclude=[t]) eqs = eq.subs(dict(zip(symbols,(t*i for i in symbols)))) if eqs.is_Mul: if t not in eqs: n.add(sympify(0)) else: m = eqs.match(r*t**a) if m: n.add(sympify(m[a])) else: s = 0 for i in eq.args: o = _homogeneous_order(i, *symbols) if o == None: return None else: s += o n.add(sympify(s)) if eq.is_Function: if eq.func is log: # The only possibility to pull a t out of a function is a power in # a logarithm. This is very likely due to calling of logcombine(). args = Mul.make_args(eq.args[0]) if all(i.is_Pow for i in args): base = 1 expos = set() for pow in args: if sign(pow.exp).is_negative: s = -1 else: s = 1 expos.add(s*pow.exp) base *= pow.base**s if len(expos) != 1: return None else: return _homogeneous_order(expos.pop()*log(base), *symbols) else: if _homogeneous_order(eq.args[0], *symbols) == 0: return sympify(0) else: return None else: if _homogeneous_order(eq.args[0], *symbols) == 0: return sympify(0) else: return None if len(n) != 1 or n == None: return None else: return n.pop() return None def ode_1st_linear(eq, func, order, match): r""" Solves 1st order linear differential equations. These are differential equations of the form dy/dx _ P(x)*y = Q(x). These kinds of differential equations can be solved in a general way. The integrating factor exp(Integral(P(x), x)) will turn the equation into a separable equation. The general solution is:: >>> from sympy import Function, dsolve, Eq, pprint, diff, sin >>> from sympy.abc import x >>> f, P, Q = map(Function, ['f', 'P', 'Q']) >>> genform = Eq(f(x).diff(x) + P(x)*f(x), Q(x)) >>> pprint(genform) d P(x)*f(x) + --(f(x)) = Q(x) dx >>> pprint(dsolve(genform, f(x), hint='1st_linear_Integral')) / / \ | | | | | / | / | | | | | | | | P(x) dx | - | P(x) dx | | | | | | | / | / f(x) = |C1 + | Q(x)*e dx|*e | | | \ / / **Example** >>> f = Function('f') >>> pprint(dsolve(Eq(x*diff(f(x), x) - f(x), x**2*sin(x)), ... f(x), '1st_linear')) f(x) = x*(C1 - cos(x)) **References** - http://en.wikipedia.org/wiki/Linear_differential_equation#First_order_equation - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 92 # indirect doctest """ x = func.args[0] f = func.func r = match # a*diff(f(x),x) + b*f(x) + c C1 = Symbol('C1') t = exp(C.Integral(r[r['b']]/r[r['a']], x)) tt = C.Integral(t*(-r[r['c']]/r[r['a']]), x) return Eq(f(x),(tt + C1)/t) def ode_Bernoulli(eq, func, order, match): r""" Solves Bernoulli differential equations. These are equations of the form dy/dx + P(x)*y = Q(x)*y**n, n != 1. The substitution w = 1/y**(1-n) will transform an equation of this form into one that is linear (see the docstring of ode_1st_linear()). The general solution is:: >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x, n >>> f, P, Q = map(Function, ['f', 'P', 'Q']) >>> genform = Eq(f(x).diff(x) + P(x)*f(x), Q(x)*f(x)**n) >>> pprint(genform) d n P(x)*f(x) + --(f(x)) = Q(x)*f (x) dx >>> pprint(dsolve(genform, f(x), hint='Bernoulli_Integral')) #doctest: +SKIP 1 ---- 1 - n // / \ \ || | | | || | / | / | || | | | | | || | (1 - n)* | P(x) dx | (-1 + n)* | P(x) dx| || | | | | | || | / | / | f(x) = ||C1 + (-1 + n)* | -Q(x)*e dx|*e | || | | | \\ / / / Note that when n = 1, then the equation is separable (see the docstring of ode_separable()). >>> pprint(dsolve(Eq(f(x).diff(x) + P(x)*f(x), Q(x)*f(x)), f(x), ... hint='separable_Integral')) f(x) / | / | 1 | | - dy = C1 + | (-P(x) + Q(x)) dx | y | | / / **Example** >>> from sympy import Function, dsolve, Eq, pprint, log >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(Eq(x*f(x).diff(x) + f(x), log(x)*f(x)**2), ... f(x), hint='Bernoulli')) 1 f(x) = ------------------- / log(x) 1\ x*|C1 + ------ + -| \ x x/ **References** - http://en.wikipedia.org/wiki/Bernoulli_differential_equation - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 95 # indirect doctest """ x = func.args[0] f = func.func r = match # a*diff(f(x),x) + b*f(x) + c*f(x)**n, n != 1 C1 = Symbol('C1') t = exp((1-r[r['n']])*C.Integral(r[r['b']]/r[r['a']],x)) tt = (r[r['n']]-1)*C.Integral(t*r[r['c']]/r[r['a']],x) return Eq(f(x),((tt + C1)/t)**(1/(1-r[r['n']]))) def ode_Riccati_special_minus2(eq, func, order, match): r""" The general Riccati equation has the form dy/dx = f(x)*y**2 + g(x)*y + h(x). While it does not have a general solution [1], the "special" form, dy/dx = a*y**2 - b*x**c, does have solutions in many cases [2]. This routine returns a solution for a*dy/dx = b*y**2 + c*y/x + d/x**2 that is obtained by using a suitable change of variables to reduce it to the special form and is valid when neither a nor b are zero and either c or d is zero. >>> from sympy.abc import x, y, a, b, c, d >>> from sympy.solvers.ode import dsolve, checkodesol >>> from sympy import pprint, Function >>> f = Function('f') >>> y = f(x) >>> genform = a*y.diff(x) - (b*y**2 + c*y/x + d/x**2) >>> sol = dsolve(genform, y) >>> pprint(sol) / / __________________ \\ | __________________ | / 2 || | / 2 | \/ 4*b*d - (a + c) *log(x)|| -|a + c - \/ 4*b*d - (a + c) *tan|C1 + ----------------------------|| \ \ 2*a // f(x) = ----------------------------------------------------------------------- 2*b*x >>> checkodesol(genform, y, sol, order=1)[0] True References: [1] http://www.maplesoft.com/support/help/Maple/view.aspx?path=odeadvisor/Riccati [2] http://eqworld.ipmnet.ru/en/solutions/ode/ode0106.pdf - http://eqworld.ipmnet.ru/en/solutions/ode/ode0123.pdf """ x = func.args[0] f = func.func r = match # a2*diff(f(x),x) + b2*f(x) + c2*f(x)/x + d2/x**2 a2, b2, c2, d2 = [r[r[s]] for s in 'a2 b2 c2 d2'.split()] C1 = Symbol('C1') mu = sqrt(4*d2*b2 - (a2 - c2)**2) return Eq(f(x), (a2 - c2 - mu*tan(mu/(2*a2)*log(x)+C1))/(2*b2*x)) def ode_Liouville(eq, func, order, match): r""" Solves 2nd order Liouville differential equations. The general form of a Liouville ODE is d^2y/dx^2 + g(y)*(dy/dx)**2 + h(x)*dy/dx. The general solution is:: >>> from sympy import Function, dsolve, Eq, pprint, diff >>> from sympy.abc import x >>> f, g, h = map(Function, ['f', 'g', 'h']) >>> genform = Eq(diff(f(x),x,x) + g(f(x))*diff(f(x),x)**2 + ... h(x)*diff(f(x),x), 0) >>> pprint(genform) 2 2 d d d g(f(x))*--(f(x)) + h(x)*--(f(x)) + ---(f(x)) = 0 dx dx 2 dx >>> pprint(dsolve(genform, f(x), hint='Liouville_Integral')) f(x) / / | | | / | / | | | | | - | h(x) dx | | g(y) dy | | | | | / | / C1 + C2* | e dx + | e dy = 0 | | / / **Example** :: >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(diff(f(x), x, x) + diff(f(x), x)**2/f(x) + ... diff(f(x), x)/x, f(x), hint='Liouville')) ___ ________________ ___ ________________ [f(x) = -\/ 2 *\/ C1 + C2*log(x) , f(x) = \/ 2 *\/ C1 + C2*log(x) ] **References** - Goldstein and Braun, "Advanced Methods for the Solution of Differential Equations", pp. 98 - http://www.maplesoft.com/support/help/Maple/view.aspx?path=odeadvisor/Liouville # indirect doctest """ # Liouville ODE f(x).diff(x, 2) + g(f(x))*(f(x).diff(x, 2))**2 + h(x)*f(x).diff(x) # See Goldstein and Braun, "Advanced Methods for the Solution of # Differential Equations", pg. 98, as well as # http://www.maplesoft.com/support/help/view.aspx?path=odeadvisor/Liouville x = func.args[0] f = func.func r = match # f(x).diff(x, 2) + g*f(x).diff(x)**2 + h*f(x).diff(x) y = r['y'] C1 = Symbol('C1') C2 = Symbol('C2') int = C.Integral(exp(C.Integral(r['g'], y)), (y, None, f(x))) sol = Eq(int + C1*C.Integral(exp(-C.Integral(r['h'], x)), x) + C2, 0) return sol def _nth_linear_match(eq, func, order): """ Matches a differential equation to the linear form: a_n(x)y^(n) + ... + a_1(x)y' + a_0(x)y + B(x) = 0 Returns a dict of order:coeff terms, where order is the order of the derivative on each term, and coeff is the coefficient of that derivative. The key -1 holds the function B(x). Returns None if the ode is not linear. This function assumes that func has already been checked to be good. **Examples** >>> from sympy import Function, cos, sin >>> from sympy.abc import x >>> from sympy.solvers.ode import _nth_linear_match >>> f = Function('f') >>> _nth_linear_match(f(x).diff(x, 3) + 2*f(x).diff(x) + ... x*f(x).diff(x, 2) + cos(x)*f(x).diff(x) + x - f(x) - ... sin(x), f(x), 3) {-1: x - sin(x), 0: -1, 1: cos(x) + 2, 2: x, 3: 1} >>> _nth_linear_match(f(x).diff(x, 3) + 2*f(x).diff(x) + ... x*f(x).diff(x, 2) + cos(x)*f(x).diff(x) + x - f(x) - ... sin(f(x)), f(x), 3) == None True """ x = func.args[0] one_x = set([x]) terms = dict([(i, S.Zero) for i in range(-1, order+1)]) for i in Add.make_args(eq): if not i.has(func): terms[-1] += i else: c, f = i.as_independent(func) if not ((isinstance(f, Derivative) and set(f.variables) == one_x) or\ f == func): return None else: terms[len(f.args[1:])] += c return terms def ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, returns='sol'): """ Solves an nth order linear homogeneous differential equation with constant coefficients. This is an equation of the form a_n*f(x)^(n) + a_(n-1)*f(x)^(n-1) + ... + a1*f'(x) + a0*f(x) = 0 These equations can be solved in a general manner, by taking the roots of the characteristic equation a_n*m**n + a_(n-1)*m**(n-1) + ... + a1*m + a0 = 0. The solution will then be the sum of Cn*x**i*exp(r*x) terms, for each where Cn is an arbitrary constant, r is a root of the characteristic equation and i is is one of each from 0 to the multiplicity of the root - 1 (for example, a root 3 of multiplicity 2 would create the terms C1*exp(3*x) + C2*x*exp(3*x)). The exponential is usually expanded for complex roots using Euler's equation exp(I*x) = cos(x) + I*sin(x). Complex roots always come in conjugate pars in polynomials with real coefficients, so the two roots will be represented (after simplifying the constants) as exp(a*x)*(C1*cos(b*x) + C2*sin(b*x)). If SymPy cannot find exact roots to the characteristic equation, a RootOf instance will be return in its stead. >>> from sympy import Function, dsolve, Eq >>> from sympy.abc import x >>> f = Function('f') >>> dsolve(f(x).diff(x, 5) + 10*f(x).diff(x) - 2*f(x), f(x), ... hint='nth_linear_constant_coeff_homogeneous') ... # doctest: +NORMALIZE_WHITESPACE f(x) == C1*exp(x*RootOf(_x**5 + 10*_x - 2, 0)) + \ C2*exp(x*RootOf(_x**5 + 10*_x - 2, 1)) + \ C3*exp(x*RootOf(_x**5 + 10*_x - 2, 2)) + \ C4*exp(x*RootOf(_x**5 + 10*_x - 2, 3)) + \ C5*exp(x*RootOf(_x**5 + 10*_x - 2, 4)) Note that because this method does not involve integration, there is no 'nth_linear_constant_coeff_homogeneous_Integral' hint. The following is for internal use: - returns = 'sol' returns the solution to the ODE. - returns = 'list' returns a list of linearly independent solutions, for use with non homogeneous solution methods like variation of parameters and undetermined coefficients. Note that, though the solutions should be linearly independent, this function does not explicitly check that. You can do "assert simplify(wronskian(sollist)) != 0" to check for linear independence. Also, "assert len(sollist) == order" will need to pass. - returns = 'both', return a dictionary {'sol':solution to ODE, 'list': list of linearly independent solutions}. **Example** >>> from sympy import Function, dsolve, pprint >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(f(x).diff(x, 4) + 2*f(x).diff(x, 3) - ... 2*f(x).diff(x, 2) - 6*f(x).diff(x) + 5*f(x), f(x), ... hint='nth_linear_constant_coeff_homogeneous')) x -2*x f(x) = (C1 + C2*x)*e + (C3*cos(x) + C4*sin(x))*e **References** - http://en.wikipedia.org/wiki/Linear_differential_equation section: Nonhomogeneous_equation_with_constant_coefficients - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 211 # indirect doctest """ x = func.args[0] f = func.func r = match # A generator of constants constants = numbered_symbols(prefix='C', cls=Symbol, start=1) # First, set up characteristic equation. chareq, symbol = S.Zero, Dummy('x') for i in r.keys(): if type(i) == str or i < 0: pass else: chareq += r[i]*symbol**i chareq = Poly(chareq, symbol) chareqroots = [ RootOf(chareq, k) for k in xrange(chareq.degree()) ] # Create a dict root: multiplicity or charroots charroots = {} for root in chareqroots: if root in charroots: charroots[root] += 1 else: charroots[root] = 1 gsol = S(0) # We need keep track of terms so we can run collect() at the end. # This is necessary for constantsimp to work properly. global collectterms collectterms = [] for root, multiplicity in charroots.items(): for i in range(multiplicity): if isinstance(root, RootOf): gsol += exp(root*x)*constants.next() assert multiplicity == 1 collectterms = [(0, root, 0)] + collectterms else: reroot = re(root) imroot = im(root) gsol += x**i*exp(reroot*x)*(constants.next()*sin(abs(imroot)*x) \ + constants.next()*cos(imroot*x)) # This ordering is important collectterms = [(i, reroot, imroot)] + collectterms if returns == 'sol': return Eq(f(x), gsol) elif returns in ('list' 'both'): # Create a list of (hopefully) linearly independent solutions gensols = [] # Keep track of when to use sin or cos for nonzero imroot for i, reroot, imroot in collectterms: if imroot == 0: gensols.append(x**i*exp(reroot*x)) else: if x**i*exp(reroot*x)*sin(abs(imroot)*x) in gensols: gensols.append(x**i*exp(reroot*x)*cos(imroot*x)) else: gensols.append(x**i*exp(reroot*x)*sin(abs(imroot)*x)) if returns == 'list': return gensols else: return {'sol':Eq(f(x), gsol), 'list':gensols} else: raise ValueError('Unknown value for key "returns".') def ode_nth_linear_constant_coeff_undetermined_coefficients(eq, func, order, match): r""" Solves an nth order linear differential equation with constant coefficients using the method of undetermined coefficients. This method works on differential equations of the form a_n*f(x)^(n) + a_(n-1)*f(x)^(n-1) + ... + a1*f'(x) + a0*f(x) = P(x), where P(x) is a function that has a finite number of linearly independent derivatives. Functions that fit this requirement are finite sums functions of the form a*x**i*exp(b*x)*sin(c*x + d) or a*x**i*exp(b*x)*cos(c*x + d), where i is a non-negative integer and a, b, c, and d are constants. For example any polynomial in x, functions like x**2*exp(2*x), x*sin(x), and exp(x)*cos(x) can all be used. Products of sin's and cos's have a finite number of derivatives, because they can be expanded into sin(a*x) and cos(b*x) terms. However, SymPy currently cannot do that expansion, so you will need to manually rewrite the expression in terms of the above to use this method. So, for example, you will need to manually convert sin(x)**2 into (1 + cos(2*x))/2 to properly apply the method of undetermined coefficients on it. This method works by creating a trial function from the expression and all of its linear independent derivatives and substituting them into the original ODE. The coefficients for each term will be a system of linear equations, which are be solved for and substituted, giving the solution. If any of the trial functions are linearly dependent on the solution to the homogeneous equation, they are multiplied by sufficient x to make them linearly independent. **Example** >>> from sympy import Function, dsolve, pprint, exp, cos >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) - ... 4*exp(-x)*x**2 + cos(2*x), f(x), ... hint='nth_linear_constant_coeff_undetermined_coefficients')) / 4\ | x | -x 4*sin(2*x) 3*cos(2*x) f(x) = |C1 + C2*x + --|*e - ---------- + ---------- \ 3 / 25 25 **References** - http://en.wikipedia.org/wiki/Method_of_undetermined_coefficients - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 221 # indirect doctest """ gensol = ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, returns='both') match.update(gensol) return _solve_undetermined_coefficients(eq, func, order, match) def _solve_undetermined_coefficients(eq, func, order, match): """ Helper function for the method of undetermined coefficients. See the ode_nth_linear_constant_coeff_undetermined_coefficients() docstring for more information on this method. match should be a dictionary that has the following keys: 'list' - A list of solutions to the homogeneous equation, such as the list returned by ode_nth_linear_constant_coeff_homogeneous(returns='list') 'sol' - The general solution, such as the solution returned by ode_nth_linear_constant_coeff_homogeneous(returns='sol') 'trialset' - The set of trial functions as returned by _undetermined_coefficients_match()['trialset'] """ x = func.args[0] f = func.func r = match coeffs = numbered_symbols('a', cls=Dummy) coefflist = [] gensols = r['list'] gsol = r['sol'] trialset = r['trialset'] notneedset = set([]) newtrialset = set([]) global collectterms if len(gensols) != order: raise NotImplementedError("Cannot find " + str(order) + \ " solutions to the homogeneous equation nessesary to apply " + \ "undetermined coefficients to " + str(eq) + " (number of terms != order)") usedsin = set([]) mult = 0 # The multiplicity of the root getmult = True for i, reroot, imroot in collectterms: if getmult: mult = i + 1 getmult = False if i == 0: getmult = True if imroot: # Alternate between sin and cos if (i, reroot) in usedsin: check = x**i*exp(reroot*x)*cos(imroot*x) else: check = x**i*exp(reroot*x)*sin(abs(imroot)*x) usedsin.add((i, reroot)) else: check = x**i*exp(reroot*x) if check in trialset: # If an element of the trial function is already part of the homogeneous # solution, we need to multiply by sufficient x to make it linearly # independent. We also don't need to bother checking for the coefficients # on those elements, since we already know it will be 0. while True: if check*x**mult in trialset: mult += 1 else: break trialset.add(check*x**mult) notneedset.add(check) newtrialset = trialset - notneedset trialfunc = 0 for i in newtrialset: c = coeffs.next() coefflist.append(c) trialfunc += c*i eqs = sub_func_doit(eq, f(x), trialfunc) coeffsdict = dict(zip(trialset, [0]*(len(trialset) + 1))) eqs = expand_mul(eqs) for i in Add.make_args(eqs): s = separatevars(i, dict=True, symbols=[x]) coeffsdict[s[x]] += s['coeff'] coeffvals = solve(coeffsdict.values(), coefflist) if not coeffvals: raise NotImplementedError("Could not solve " + str(eq) + " using the " + \ " method of undetermined coefficients (unable to solve for coefficients).") psol = trialfunc.subs(coeffvals) return Eq(f(x), gsol.rhs + psol) def _undetermined_coefficients_match(expr, x): """ Returns a trial function match if undetermined coefficients can be applied to expr, and None otherwise. A trial expression can be found for an expression for use with the method of undetermined coefficients if the expression is an additive/multiplicative combination of constants, polynomials in x (the independent variable of expr), sin(a*x + b), cos(a*x + b), and exp(a*x) terms (in other words, it has a finite number of linearly independent derivatives). Note that you may still need to multiply each term returned here by sufficient x to make it linearly independent with the solutions to the homogeneous equation. This is intended for internal use by undetermined_coefficients hints. SymPy currently has no way to convert sin(x)**n*cos(y)**m into a sum of only sin(a*x) and cos(b*x) terms, so these are not implemented. So, for example, you will need to manually convert sin(x)**2 into (1 + cos(2*x))/2 to properly apply the method of undetermined coefficients on it. **Example** >>> from sympy import log, exp >>> from sympy.solvers.ode import _undetermined_coefficients_match >>> from sympy.abc import x >>> _undetermined_coefficients_match(9*x*exp(x) + exp(-x), x) {'test': True, 'trialset': set([x*exp(x), exp(-x), exp(x)])} >>> _undetermined_coefficients_match(log(x), x) {'test': False} """ from sympy import S a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) expr = powsimp(expr, combine='exp') # exp(x)*exp(2*x + 1) => exp(3*x + 1) retdict = {} def _test_term(expr, x): """ Test if expr fits the proper form for undetermined coefficients. """ if expr.is_Add: return all([_test_term(i, x) for i in expr.args]) elif expr.is_Mul: if expr.has(sin, cos): foundtrig = False # Make sure that there is only one trig function in the args. # See the docstring. for i in expr.args: if i.has(sin, cos): if foundtrig: return False else: foundtrig = True return all([_test_term(i, x) for i in expr.args]) elif expr.is_Function: if expr.func in (sin, cos, exp): if expr.args[0].match(a*x + b): return True else: return False else: return False elif expr.is_Pow and expr.base.is_Symbol and expr.exp.is_Integer and \ expr.exp >= 0: return True elif expr.is_Pow and expr.base.is_number: if expr.exp.match(a*x + b): return True else: return False elif expr.is_Symbol or expr.is_Number: return True else: return False def _get_trial_set(expr, x, exprs=set([])): """ Returns a set of trial terms for undetermined coefficients. The idea behind undetermined coefficients is that the terms expression repeat themselves after a finite number of derivatives, except for the coefficients (they are linearly dependent). So if we collect these, we should have the terms of our trial function. """ def _remove_coefficient(expr, x): """ Returns the expression without a coefficient. Similar to expr.as_independent(x)[1], except it only works multiplicatively. """ # I was using the below match, but it doesn't always put all of the # coefficient in c. c.f. 2**x*6*exp(x)*log(2) # The below code is probably cleaner anyway. # c = Wild('c', exclude=[x]) # t = Wild('t') # r = expr.match(c*t) term = S.One if expr.is_Mul: for i in expr.args: if i.has(x): term *= i elif expr.has(x): term = expr return term expr = expand_mul(expr) if expr.is_Add: for term in expr.args: if _remove_coefficient(term, x) in exprs: pass else: exprs.add(_remove_coefficient(term, x)) exprs = exprs.union(_get_trial_set(term, x, exprs)) else: term = _remove_coefficient(expr, x) tmpset = exprs.union(set([term])) oldset = set([]) while tmpset != oldset: # If you get stuck in this loop, then _test_term is probably broken oldset = tmpset.copy() expr = expr.diff(x) term = _remove_coefficient(expr, x) if term.is_Add: tmpset = tmpset.union(_get_trial_set(term, x, tmpset)) else: tmpset.add(term) exprs = tmpset return exprs retdict['test'] = _test_term(expr, x) if retdict['test']: # Try to generate a list of trial solutions that will have the undetermined # coefficients. Note that if any of these are not linearly independent # with any of the solutions to the homogeneous equation, then they will # need to be multiplied by sufficient x to make them so. This function # DOES NOT do that (it doesn't even look at the homogeneous equation). retdict['trialset'] = _get_trial_set(expr, x) return retdict def ode_nth_linear_constant_coeff_variation_of_parameters(eq, func, order, match): r""" Solves an nth order linear differential equation with constant coefficients using the method of undetermined coefficients. This method works on any differential equations of the form f(x)^(n) + a_(n-1)*f(x)^(n-1) + ... + a1*f'(x) + a0*f(x) = P(x). This method works by assuming that the particular solution takes the form Sum(c_i(x)*y_i(x), (x, 1, n)), where y_i is the ith solution to the homogeneous equation. The solution is then solved using Wronskian's and Cramer's Rule. The particular solution is given by Sum(Integral(W_i(x)/W(x), x)*y_i(x), (x, 1, n)), where W(x) is the Wronskian of the fundamental system (the system of n linearly independent solutions to the homogeneous equation), and W_i(x) is the Wronskian of the fundamental system with the ith column replaced with [0, 0, ..., 0, P(x)]. This method is general enough to solve any nth order inhomogeneous linear differential equation with constant coefficients, but sometimes SymPy cannot simplify the Wronskian well enough to integrate it. If this method hangs, try using the 'nth_linear_constant_coeff_variation_of_parameters_Integral' hint and simplifying the integrals manually. Also, prefer using 'nth_linear_constant_coeff_undetermined_coefficients' when it applies, because it doesn't use integration, making it faster and more reliable. Warning, using simplify=False with 'nth_linear_constant_coeff_variation_of_parameters' in dsolve() may cause it to hang, because it will not attempt to simplify the Wronskian before integrating. It is recommended that you only use simplify=False with 'nth_linear_constant_coeff_variation_of_parameters_Integral' for this method, especially if the solution to the homogeneous equation has trigonometric functions in it. **Example** >>> from sympy import Function, dsolve, pprint, exp, log >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(f(x).diff(x, 3) - 3*f(x).diff(x, 2) + ... 3*f(x).diff(x) - f(x) - exp(x)*log(x), f(x), ... hint='nth_linear_constant_coeff_variation_of_parameters')) / 2 3 /log(x) 11\\ x f(x) = |C1 + C2*x + C3*x + x *|------ - --||*e \ \ 6 36// **References** - http://en.wikipedia.org/wiki/Variation_of_parameters - http://planetmath.org/encyclopedia/VariationOfParameters.html - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 233 # indirect doctest """ gensol = ode_nth_linear_constant_coeff_homogeneous(eq, func, order, match, returns='both') match.update(gensol) return _solve_variation_of_parameters(eq, func, order, match) def _solve_variation_of_parameters(eq, func, order, match): """ Helper function for the method of variation of parameters. See the ode_nth_linear_constant_coeff_variation_of_parameters() docstring for more information on this method. match should be a dictionary that has the following keys: 'list' - A list of solutions to the homogeneous equation, such as the list returned by ode_nth_linear_constant_coeff_homogeneous(returns='list') 'sol' - The general solution, such as the solution returned by ode_nth_linear_constant_coeff_homogeneous(returns='sol') """ x = func.args[0] f = func.func r = match psol = 0 gensols = r['list'] gsol = r['sol'] wr = wronskian(gensols, x) if r.get('simplify', True): wr = simplify(wr) # We need much better simplification for some ODEs. # See issue 1563, for example. # To reduce commonly occuring sin(x)**2 + cos(x)**2 to 1 wr = trigsimp(wr, deep=True, recursive=True) if not wr: # The wronskian will be 0 iff the solutions are not linearly independent. raise NotImplementedError("Cannot find " + str(order) + \ " solutions to the homogeneous equation nessesary to apply " + \ "variation of parameters to " + str(eq) + " (Wronskian == 0)") if len(gensols) != order: raise NotImplementedError("Cannot find " + str(order) + \ " solutions to the homogeneous equation nessesary to apply " + \ "variation of parameters to " + str(eq) + " (number of terms != order)") negoneterm = (-1)**(order) for i in gensols: psol += negoneterm*C.Integral(wronskian(filter(lambda x: x != i, \ gensols), x)*r[-1]/wr, x)*i/r[order] negoneterm *= -1 if r.get('simplify', True): psol = simplify(psol) psol = trigsimp(psol, deep=True) return Eq(f(x), gsol.rhs + psol) def ode_separable(eq, func, order, match): r""" Solves separable 1st order differential equations. This is any differential equation that can be written as P(y)*dy/dx = Q(x). The solution can then just be found by rearranging terms and integrating: Integral(P(y), y) = Integral(Q(x), x). This hint uses separatevars() as its back end, so if a separable equation is not caught by this solver, it is most likely the fault of that function. separatevars() is smart enough to do most expansion and factoring necessary to convert a separable equation F(x, y) into the proper form P(x)*Q(y). The general solution is:: >>> from sympy import Function, dsolve, Eq, pprint >>> from sympy.abc import x >>> a, b, c, d, f = map(Function, ['a', 'b', 'c', 'd', 'f']) >>> genform = Eq(a(x)*b(f(x))*f(x).diff(x), c(x)*d(f(x))) >>> pprint(genform) d a(x)*b(f(x))*--(f(x)) = c(x)*d(f(x)) dx >>> pprint(dsolve(genform, f(x), hint='separable_Integral')) f(x) / / | | | b(y) | c(x) | ---- dy = C1 + | ---- dx | d(y) | a(x) | | / / **Example** :: >>> from sympy import Function, dsolve, Eq >>> from sympy.abc import x >>> f = Function('f') >>> pprint(dsolve(Eq(f(x)*f(x).diff(x) + x, 3*x*f(x)**2), f(x), ... hint='separable')) / 2 \ 2 log\3*f (x) - 1/ x ---------------- = C1 + -- 6 2 **Reference** - M. Tenenbaum & H. Pollard, "Ordinary Differential Equations", Dover 1963, pp. 52 # indirect doctest """ x = func.args[0] f = func.func C1 = Symbol('C1') r = match # {'m1':m1, 'm2':m2, 'y':y} return Eq(C.Integral(r['m2']['coeff']*r['m2'][r['y']]/r['m1'][r['y']], (r['y'], None, f(x))), C.Integral(-r['m1']['coeff']*r['m1'][x]/ r['m2'][x], x)+C1) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/polysys.py0000644000175000017500000001714112014170666024674 0ustar georgeskgeorgesk"""Solvers of systems of polynomial equations. """ from sympy.polys import Poly, groebner, roots from sympy.polys.polytools import parallel_poly_from_expr from sympy.polys.polyerrors import ComputationFailed, PolificationFailed from sympy.utilities import postfixes from sympy.simplify import rcollect from sympy.core import S class SolveFailed(Exception): """Raised when solver's conditions weren't met. """ def solve_poly_system(seq, *gens, **args): """ Solve a system of polynomial equations. Example ======= >>> from sympy import solve_poly_system >>> from sympy.abc import x, y >>> solve_poly_system([x*y - 2*y, 2*y**2 - x**2], x, y) [(0, 0), (2, -2**(1/2)), (2, 2**(1/2))] """ try: polys, opt = parallel_poly_from_expr(seq, *gens, **args) except PolificationFailed, exc: raise ComputationFailed('solve_poly_system', len(seq), exc) if len(polys) == len(opt.gens) == 2: f, g = polys a, b = f.degree_list() c, d = g.degree_list() if a <= 2 and b <= 2 and c <= 2 and d <= 2: try: return solve_biquadratic(f, g, opt) except SolveFailed: pass return solve_generic(polys, opt) def solve_biquadratic(f, g, opt): """Solve a system of two bivariate quadratic polynomial equations. """ G = groebner([f, g]) if len(G) == 1 and G[0].is_ground: return None if len(G) != 2: raise SolveFailed p, q = G x, y = opt.gens p = Poly(p, x, expand=False) q = q.ltrim(-1) p_roots = [ rcollect(expr, y) for expr in roots(p).keys() ] q_roots = roots(q).keys() solutions = [] for q_root in q_roots: for p_root in p_roots: solution = (p_root.subs(y, q_root), q_root) solutions.append(solution) return sorted(solutions) def solve_generic(polys, opt): """ Solve a generic system of polynomial equations. Returns all possible solutions over C[x_1, x_2, ..., x_m] of a set F = { f_1, f_2, ..., f_n } of polynomial equations, using Groebner basis approach. For now only zero-dimensional systems are supported, which means F can have at most a finite number of solutions. The algorithm works by the fact that, supposing G is the basis of F with respect to an elimination order (here lexicographic order is used), G and F generate the same ideal, they have the same set of solutions. By the elimination property, if G is a reduced, zero-dimensional Groebner basis, then there exists an univariate polynomial in G (in its last variable). This can be solved by computing its roots. Substituting all computed roots for the last (eliminated) variable in other elements of G, new polynomial system is generated. Applying the above procedure recursively, a finite number of solutions can be found. The ability of finding all solutions by this procedure depends on the root finding algorithms. If no solutions were found, it means only that roots() failed, but the system is solvable. To overcome this difficulty use numerical algorithms instead. References ========== .. [Buchberger01] B. Buchberger, Groebner Bases: A Short Introduction for Systems Theorists, In: R. Moreno-Diaz, B. Buchberger, J.L. Freire, Proceedings of EUROCAST'01, February, 2001 .. [Cox97] D. Cox, J. Little, D. O'Shea, Ideals, Varieties and Algorithms, Springer, Second Edition, 1997, pp. 112 """ def is_univariate(f): """Returns True if 'f' is univariate in its last variable. """ for monom in f.monoms(): if any(m > 0 for m in monom[:-1]): return False return True def subs_root(f, gen, zero): """Replace generator with a root so that the result is nice. """ p = f.as_expr({gen: zero}) if f.degree(gen) >= 2: p = p.expand(deep=False) return p def solve_reduced_system(system, gens, entry=False): """Recursively solves reduced polynomial systems. """ if len(system) == len(gens) == 1: zeros = roots(system[0], gens[-1]).keys() return [ (zero,) for zero in zeros ] basis = groebner(system, gens, polys=True) if len(basis) == 1 and basis[0].is_ground: if not entry: return [] else: return None univariate = filter(is_univariate, basis) if len(univariate) == 1: f = univariate.pop() else: raise NotImplementedError("only zero-dimensional systems supported (finite number of solutions)") gens = f.gens gen = gens[-1] zeros = roots(f.ltrim(gen)).keys() if not zeros: return [] if len(basis) == 1: return [ (zero,) for zero in zeros ] solutions = [] for zero in zeros: new_system = [] new_gens = gens[:-1] for b in basis[:-1]: eq = subs_root(b, gen, zero) if eq is not S.Zero: new_system.append(eq) for solution in solve_reduced_system(new_system, new_gens): solutions.append(solution + (zero,)) return solutions result = solve_reduced_system(polys, opt.gens, entry=True) if result is not None: return sorted(result) else: return None def solve_triangulated(polys, *gens, **args): """ Solve a polynomial system using Gianni-Kalkbrenner algorithm. The algorithm proceeds by computing one Groebner basis in the ground domain and then by iteratively computing polynomial factorizations in appropriately constructed algebraic extensions of the ground domain. Example ======= >>> from sympy.solvers.polysys import solve_triangulated >>> from sympy.abc import x, y, z >>> F = [x**2 + y + z - 1, x + y**2 + z - 1, x + y + z**2 - 1] >>> solve_triangulated(F, x, y, z) [(0, 0, 1), (0, 1, 0), (1, 0, 0)] References ========== .. [Gianni89] Patrizia Gianni, Teo Mora, Algebraic Solution of System of Polynomial Equations using Groebner Bases, AAECC-5 on Applied Algebra, Algebraic Algorithms and Error-Correcting Codes, LNCS 356 247--257, 1989 """ G = groebner(polys, gens, polys=True) G = list(reversed(G)) domain = args.get('domain') if domain is not None: for i, g in enumerate(G): G[i] = g.set_domain(domain) f, G = G[0].ltrim(-1), G[1:] dom = f.get_domain() zeros = f.ground_roots() solutions = set([]) for zero in zeros: solutions.add(((zero,), dom)) var_seq = reversed(gens[:-1]) vars_seq = postfixes(gens[1:]) for var, vars in zip(var_seq, vars_seq): _solutions = set([]) for values, dom in solutions: H, mapping = [], zip(vars, values) for g in G: _vars = (var,) + vars if g.has_only_gens(*_vars) and g.degree(var) != 0: h = g.ltrim(var).eval(mapping) if g.degree(var) == h.degree(): H.append(h) p = min(H, key=lambda h: h.degree()) zeros = p.ground_roots() for zero in zeros: if not zero.is_Rational: dom_zero = dom.algebraic_field(zero) else: dom_zero = dom _solutions.add(((zero,) + values, dom_zero)) solutions = _solutions solutions = list(solutions) for i, (solution, _) in enumerate(solutions): solutions[i] = solution return sorted(solutions) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/recurr.py0000644000175000017500000005412412014170666024456 0ustar georgeskgeorgesk"""This module is intended for solving recurrences or, in other words, difference equations. Currently supported are linear, inhomogeneous equations with polynomial or rational coefficients. The solutions are obtained among polynomials, rational functions, hypergeometric terms, or combinations of hypergeometric term which are pairwise dissimilar. rsolve_X functions were meant as a low level interface for rsolve() which would use Mathematica's syntax. Given a recurrence relation: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + ... + a_{0}(n) y(n) = f(n) where k > 0 and a_{i}(n) are polynomials in n. To use rsolve_X we need to put all coefficients in to a list L of k+1 elements the following way: L = [ a_{0}(n), ..., a_{k-1}(n), a_{k}(n) ] where L[i], for i=0..k, maps to a_{i}(n) y(n+i) (y(n+i) is implicit). For example if we would like to compute m-th Bernoulli polynomial up to a constant (example was taken from rsolve_poly docstring), then we would use b(n+1) - b(n) == m*n**(m-1) recurrence, which has solution b(n) = B_m + C. Then L = [-1, 1] and f(n) = m*n**(m-1) and finally for m=4: >>> from sympy import Symbol, bernoulli, rsolve_poly >>> n = Symbol('n', integer=True) >>> rsolve_poly([-1, 1], 4*n**3, n) C0 + n**4 - 2*n**3 + n**2 >>> bernoulli(4, n) n**4 - 2*n**3 + n**2 - 1/30 For the sake of completeness, f(n) can be: [1] a polynomial -> rsolve_poly [2] a rational function -> rsolve_ratio [3] a hypergeometric function -> rsolve_hyper """ from sympy.core.singleton import S from sympy.core.numbers import Rational from sympy.core.symbol import Symbol, Wild, Dummy from sympy.core.relational import Equality from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core import sympify from sympy.simplify import simplify, hypersimp, hypersimilar from sympy.solvers import solve, solve_undetermined_coeffs from sympy.polys import Poly, quo, gcd, lcm, roots, resultant from sympy.functions import binomial, FallingFactorial from sympy.matrices import Matrix, casoratian from sympy.concrete import product def rsolve_poly(coeffs, f, n, **hints): """Given linear recurrence operator L of order 'k' with polynomial coefficients and inhomogeneous equation Ly = f, where 'f' is a polynomial, we seek for all polynomial solutions over field K of characteristic zero. The algorithm performs two basic steps: (1) Compute degree N of the general polynomial solution. (2) Find all polynomials of degree N or less of Ly = f. There are two methods for computing the polynomial solutions. If the degree bound is relatively small, i.e. it's smaller than or equal to the order of the recurrence, then naive method of undetermined coefficients is being used. This gives system of algebraic equations with N+1 unknowns. In the other case, the algorithm performs transformation of the initial equation to an equivalent one, for which the system of algebraic equations has only 'r' indeterminates. This method is quite sophisticated (in comparison with the naive one) and was invented together by Abramov, Bronstein and Petkovsek. It is possible to generalize the algorithm implemented here to the case of linear q-difference and differential equations. Lets say that we would like to compute m-th Bernoulli polynomial up to a constant. For this we can use b(n+1) - b(n) == m*n**(m-1) recurrence, which has solution b(n) = B_m + C. For example: >>> from sympy import Symbol, rsolve_poly >>> n = Symbol('n', integer=True) >>> rsolve_poly([-1, 1], 4*n**3, n) C0 + n**4 - 2*n**3 + n**2 For more information on implemented algorithms refer to: [1] S. A. Abramov, M. Bronstein and M. Petkovsek, On polynomial solutions of linear operator equations, in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, 1995, 290-296. [2] M. Petkovsek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. [3] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. """ f = sympify(f) if not f.is_polynomial(n): return None homogeneous = f.is_zero r = len(coeffs)-1 coeffs = [ Poly(coeff, n) for coeff in coeffs ] polys = [ Poly(0, n) ] * (r+1) terms = [ (S.Zero, S.NegativeInfinity) ] *(r+1) for i in xrange(0, r+1): for j in xrange(i, r+1): polys[i] += coeffs[j]*binomial(j, i) if not polys[i].is_zero: (exp,), coeff = polys[i].LT() terms[i] = (coeff, exp) d = b = terms[0][1] for i in xrange(1, r+1): if terms[i][1] > d: d = terms[i][1] if terms[i][1] - i > b: b = terms[i][1] - i d, b = int(d), int(b) x = Dummy('x') degree_poly = S.Zero for i in xrange(0, r+1): if terms[i][1] - i == b: degree_poly += terms[i][0]*FallingFactorial(x, i) nni_roots = roots(degree_poly, x, filter='Z', predicate=lambda r: r >= 0).keys() if nni_roots: N = [max(nni_roots)] else: N = [] if homogeneous: N += [-b-1] else: N += [f.as_poly(n).degree() - b, -b-1] N = int(max(N)) if N < 0: if homogeneous: if hints.get('symbols', False): return (S.Zero, []) else: return S.Zero else: return None if N <= r: C = [] y = E = S.Zero for i in xrange(0, N+1): C.append(Symbol('C'+str(i))) y += C[i] * n**i for i in xrange(0, r+1): E += coeffs[i].as_expr()*y.subs(n, n+i) solutions = solve_undetermined_coeffs(E-f, C, n) if solutions is not None: C = [ c for c in C if (c not in solutions) ] result = y.subs(solutions) else: return None # TBD else: A = r U = N+A+b+1 nni_roots = roots(polys[r], filter='Z', predicate=lambda r: r >= 0).keys() if nni_roots != []: a = max(nni_roots) + 1 else: a = S.Zero def zero_vector(k): return [S.Zero] * k def one_vector(k): return [S.One] * k def delta(p, k): B = S.One D = p.subs(n, a+k) for i in xrange(1, k+1): B *= -Rational(k-i+1, i) D += B * p.subs(n, a+k-i) return D alpha = {} for i in xrange(-A, d+1): I = one_vector(d+1) for k in xrange(1, d+1): I[k] = I[k-1] * (x+i-k+1)/k alpha[i] = S.Zero for j in xrange(0, A+1): for k in xrange(0, d+1): B = binomial(k, i+j) D = delta(polys[j].as_expr(), k) alpha[i] += I[k]*B*D V = Matrix(U, A, lambda i, j: int(i == j)) if homogeneous: for i in xrange(A, U): v = zero_vector(A) for k in xrange(1, A+b+1): if i - k < 0: break B = alpha[k-A].subs(x, i-k) for j in xrange(0, A): v[j] += B * V[i-k, j] denom = alpha[-A].subs(x, i) for j in xrange(0, A): V[i, j] = -v[j] / denom else: G = zero_vector(U) for i in xrange(A, U): v = zero_vector(A) g = S.Zero for k in xrange(1, A+b+1): if i - k < 0: break B = alpha[k-A].subs(x, i-k) for j in xrange(0, A): v[j] += B * V[i-k, j] g += B * G[i-k] denom = alpha[-A].subs(x, i) for j in xrange(0, A): V[i, j] = -v[j] / denom G[i] = (delta(f, i-A) - g) / denom P, Q = one_vector(U), zero_vector(A) for i in xrange(1, U): P[i] = (P[i-1] * (n-a-i+1)/i).expand() for i in xrange(0, A): Q[i] = Add(*[ (v*p).expand() for v, p in zip(V[:,i], P) ]) if not homogeneous: h = Add(*[ (g*p).expand() for g, p in zip(G, P) ]) C = [ Symbol('C'+str(i)) for i in xrange(0, A) ] g = lambda i: Add(*[ c*delta(q, i) for c, q in zip(C, Q) ]) if homogeneous: E = [ g(i) for i in xrange(N+1, U) ] else: E = [ g(i) + delta(h, i) for i in xrange(N+1, U) ] if E != []: solutions = solve(E, *C) if solutions is None: if homogeneous: if hints.get('symbols', False): return (S.Zero, []) else: return S.Zero else: return None else: solutions = {} if homogeneous: result = S.Zero else: result = h for c, q in zip(C, Q): if c in solutions: s = solutions[c]*q C.remove(c) else: s = c*q result += s.expand() if hints.get('symbols', False): return (result, C) else: return result def rsolve_ratio(coeffs, f, n, **hints): """Given linear recurrence operator L of order 'k' with polynomial coefficients and inhomogeneous equation Ly = f, where 'f' is a polynomial, we seek for all rational solutions over field K of characteristic zero. This procedure accepts only polynomials, however if you are interested in solving recurrence with rational coefficients then use rsolve() which will pre-process the given equation and run this procedure with polynomial arguments. The algorithm performs two basic steps: (1) Compute polynomial v(n) which can be used as universal denominator of any rational solution of equation Ly = f. (2) Construct new linear difference equation by substitution y(n) = u(n)/v(n) and solve it for u(n) finding all its polynomial solutions. Return None if none were found. Algorithm implemented here is a revised version of the original Abramov's algorithm, developed in 1989. The new approach is much simpler to implement and has better overall efficiency. This method can be easily adapted to q-difference equations case. Besides finding rational solutions alone, this functions is an important part of Hyper algorithm were it is used to find particular solution of inhomogeneous part of a recurrence. For more information on the implemented algorithm refer to: [1] S. A. Abramov, Rational solutions of linear difference and q-difference equations with polynomial coefficients, in: T. Levelt, ed., Proc. ISSAC '95, ACM Press, New York, 1995, 285-289 """ f = sympify(f) if not f.is_polynomial(n): return None coeffs = map(sympify, coeffs) r = len(coeffs)-1 A, B = coeffs[r], coeffs[0] A = A.subs(n, n-r).expand() h = Dummy('h') res = resultant(A, B.subs(n, n+h), n) if not res.is_polynomial(h): p, q = res.as_numer_denom() res = quo(p, q, h) nni_roots = roots(res, h, filter='Z', predicate=lambda r: r >= 0).keys() if not nni_roots: return rsolve_poly(coeffs, f, n, **hints) else: C, numers = S.One, [S.Zero]*(r+1) for i in xrange(int(max(nni_roots)), -1, -1): d = gcd(A, B.subs(n, n+i), n) A = quo(A, d, n) B = quo(B, d.subs(n, n-i), n) C *= Mul(*[ d.subs(n, n-j) for j in xrange(0, i+1) ]) denoms = [ C.subs(n, n+i) for i in range(0, r+1) ] for i in range(0, r+1): g = gcd(coeffs[i], denoms[i], n) numers[i] = quo(coeffs[i], g, n) denoms[i] = quo(denoms[i], g, n) for i in xrange(0, r+1): numers[i] *= Mul(*(denoms[:i] + denoms[i+1:])) result = rsolve_poly(numers, f * Mul(*denoms), n, **hints) if result is not None: if hints.get('symbols', False): return (simplify(result[0] / C), result[1]) else: return simplify(result / C) else: return None def rsolve_hyper(coeffs, f, n, **hints): """Given linear recurrence operator L of order 'k' with polynomial coefficients and inhomogeneous equation Ly = f we seek for all hypergeometric solutions over field K of characteristic zero. The inhomogeneous part can be either hypergeometric or a sum of a fixed number of pairwise dissimilar hypergeometric terms. The algorithm performs three basic steps: (1) Group together similar hypergeometric terms in the inhomogeneous part of Ly = f, and find particular solution using Abramov's algorithm. (2) Compute generating set of L and find basis in it, so that all solutions are linearly independent. (3) Form final solution with the number of arbitrary constants equal to dimension of basis of L. Term a(n) is hypergeometric if it is annihilated by first order linear difference equations with polynomial coefficients or, in simpler words, if consecutive term ratio is a rational function. The output of this procedure is a linear combination of fixed number of hypergeometric terms. However the underlying method can generate larger class of solutions - D'Alembertian terms. Note also that this method not only computes the kernel of the inhomogeneous equation, but also reduces in to a basis so that solutions generated by this procedure are linearly independent For more information on the implemented algorithm refer to: [1] M. Petkovsek, Hypergeometric solutions of linear recurrences with polynomial coefficients, J. Symbolic Computation, 14 (1992), 243-264. [2] M. Petkovsek, H. S. Wilf, D. Zeilberger, A = B, 1996. """ coeffs = map(sympify, coeffs) f = sympify(f) r, kernel = len(coeffs)-1, [] if not f.is_zero: if f.is_Add: similar = {} for g in f.expand().args: if not g.is_hypergeometric(n): return None for h in similar.iterkeys(): if hypersimilar(g, h, n): similar[h] += g break else: similar[g] = S.Zero inhomogeneous = [] for g, h in similar.iteritems(): inhomogeneous.append(g+h) elif f.is_hypergeometric(n): inhomogeneous = [f] else: return None for i, g in enumerate(inhomogeneous): coeff, polys = S.One, coeffs[:] denoms = [ S.One ] * (r+1) s = hypersimp(g, n) for j in xrange(1, r+1): coeff *= s.subs(n, n+j-1) p, q = coeff.as_numer_denom() polys[j] *= p denoms[j] = q for j in xrange(0, r+1): polys[j] *= Mul(*(denoms[:j] + denoms[j+1:])) R = rsolve_poly(polys, Mul(*denoms), n) if not (R is None or R is S.Zero): inhomogeneous[i] *= R else: return None result = Add(*inhomogeneous) else: result = S.Zero Z = Dummy('Z') p, q = coeffs[0], coeffs[r].subs(n, n-r+1) p_factors = [ z for z in roots(p, n).iterkeys() ] q_factors = [ z for z in roots(q, n).iterkeys() ] factors = [ (S.One, S.One) ] for p in p_factors: for q in q_factors: if p.is_integer and q.is_integer and p <= q: continue else: factors += [(n-p, n-q)] p = [ (n-p, S.One) for p in p_factors ] q = [ (S.One, n-q) for q in q_factors ] factors = p + factors + q for A, B in factors: polys, degrees = [], [] D = A*B.subs(n, n+r-1) for i in xrange(0, r+1): a = Mul(*[ A.subs(n, n+j) for j in xrange(0, i) ]) b = Mul(*[ B.subs(n, n+j) for j in xrange(i, r) ]) poly = quo(coeffs[i]*a*b, D, n) polys.append(poly.as_poly(n)) if not poly.is_zero: degrees.append(polys[i].degree()) d, poly = max(degrees), S.Zero for i in xrange(0, r+1): coeff = polys[i].nth(d) if coeff is not S.Zero: poly += coeff * Z**i for z in roots(poly, Z).iterkeys(): if not z.is_real or z.is_zero: continue C = rsolve_poly([ polys[i]*z**i for i in xrange(r+1) ], 0, n) if C is not None and C is not S.Zero: ratio = z * A * C.subs(n, n + 1) / B / C K = product(simplify(ratio), (n, 0, n-1)) if casoratian(kernel+[K], n) != 0: kernel.append(K) symbols = [ Symbol('C'+str(i)) for i in xrange(len(kernel)) ] for C, ker in zip(symbols, kernel): result += C * ker if hints.get('symbols', False): return (result, symbols) else: return result def rsolve(f, y, init=None): """Solve univariate recurrence with rational coefficients. Given k-th order linear recurrence Ly = f, or equivalently: a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + ... + a_{0}(n) y(n) = f where a_{i}(n), for i=0..k, are polynomials or rational functions in n, and f is a hypergeometric function or a sum of a fixed number of pairwise dissimilar hypergeometric terms in n, finds all solutions or returns None, if none were found. Initial conditions can be given as a dictionary in two forms: [1] { n_0 : v_0, n_1 : v_1, ..., n_m : v_m } [2] { y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m } or as a list L of values: L = [ v_0, v_1, ..., v_m ] where L[i] = v_i, for i=0..m, maps to y(n_i). As an example lets consider the following recurrence: (n - 1) y(n + 2) - (n**2 + 3 n - 2) y(n + 1) + 2 n (n + 1) y(n) == 0 >>> from sympy import Function, rsolve >>> from sympy.abc import n >>> y = Function('y') >>> f = (n-1)*y(n+2) - (n**2+3*n-2)*y(n+1) + 2*n*(n+1)*y(n) >>> rsolve(f, y(n)) 2**n*C1 + C0*n! >>> rsolve(f, y(n), { y(0):0, y(1):3 }) 3*2**n - 3*n! """ if isinstance(f, Equality): f = f.lhs - f.rhs if f.is_Add: F = f.args else: F = [f] k = Wild('k') n = y.args[0] h_part = {} i_part = S.Zero for g in F: if g.is_Mul: G = g.args else: G = [g] coeff = S.One kspec = None for h in G: if h.is_Function: if h.func == y.func: result = h.args[0].match(n + k) if result is not None: kspec = int(result[k]) else: raise ValueError("'%s(%s+k)' expected, got '%s'" % (y.func, n, h)) else: raise ValueError("'%s' expected, got '%s'" % (y.func, h.func)) else: coeff *= h if kspec is not None: if kspec in h_part: h_part[kspec] += coeff else: h_part[kspec] = coeff else: i_part += coeff for k, coeff in h_part.iteritems(): h_part[k] = simplify(coeff) common = S.One for coeff in h_part.itervalues(): if coeff.is_rational_function(n): if not coeff.is_polynomial(n): common = lcm(common, coeff.as_numer_denom()[1], n) else: raise ValueError("Polynomial or rational function expected, got '%s'" % coeff) i_numer, i_denom = i_part.as_numer_denom() if i_denom.is_polynomial(n): common = lcm(common, i_denom, n) if common is not S.One: for k, coeff in h_part.iteritems(): numer, denom = coeff.as_numer_denom() h_part[k] = numer*quo(common, denom, n) i_part = i_numer*quo(common, i_denom, n) K_min = min(h_part.keys()) if K_min < 0: K = abs(K_min) H_part = {} i_part = i_part.subs(n, n+K).expand() common = common.subs(n, n+K).expand() for k, coeff in h_part.iteritems(): H_part[k+K] = coeff.subs(n, n+K).expand() else: H_part = h_part K_max = max(H_part.keys()) coeffs = [] for i in xrange(0, K_max+1): if i in H_part: coeffs.append(H_part[i]) else: coeffs.append(S.Zero) result = rsolve_hyper(coeffs, i_part, n, symbols=True) if result is None: return None else: solution, symbols = result if symbols and init is not None: equations = [] if type(init) is list: for i in xrange(0, len(init)): eq = solution.subs(n, i) - init[i] equations.append(eq) else: for k, v in init.iteritems(): try: i = int(k) except TypeError: if k.is_Function and k.func == y.func: i = int(k.args[0]) else: raise ValueError("Integer or term expected, got '%s'" % k) eq = solution.subs(n, i) - v equations.append(eq) result = solve(equations, *symbols) if result is None: return None else: for k, v in result.iteritems(): solution = solution.subs(k, v) return (solution.expand()) / common wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/pde.py0000644000175000017500000001304412014170666023720 0ustar georgeskgeorgesk""" Analytical methods for solving Partial Differential Equations Currently implemented methods: - separation of variables - pde_separate """ from sympy import Eq, Equality from sympy.simplify import simplify from sympy.core.compatibility import reduce import operator def pde_separate(eq, fun, sep, strategy='mul'): """Separate variables in partial differential equation either by additive or multiplicative separation approach. It tries to rewrite an equation so that one of the specified variables occurs on a different side of the equation than the others. :param eq: Partial differential equation :param fun: Original function F(x, y, z) :param sep: List of separated functions [X(x), u(y, z)] :param strategy: Separation strategy. You can choose between additive separation ('add') and multiplicative separation ('mul') which is default. """ do_add = False if strategy == 'add': do_add = True elif strategy == 'mul': do_add = False else: assert ValueError('Unknown strategy: %s' % strategy) if isinstance(eq, Equality): if eq.rhs != 0: return pde_separate(Eq(eq.lhs - eq.rhs), fun, sep, strategy) assert eq.rhs == 0 # Handle arguments orig_args = list(fun.args) subs_args = [] for s in sep: for j in range(0, len(s.args)): subs_args.append(s.args[j]) if do_add: functions = reduce(operator.add, sep) else: functions = reduce(operator.mul, sep) # Check whether variables match if len(subs_args) != len(orig_args): raise ValueError("Variable counts do not match") # Check for duplicate arguments like [X(x), u(x, y)] if len(subs_args) != len(set(subs_args)): raise ValueError("Duplicate substitution arguments detected") # Check whether the variables match if set(orig_args) != set(subs_args): raise ValueError("Arguments do not match") # Substitute original function with separated... result = eq.lhs.subs(fun, functions).doit() # Divide by terms when doing multiplicative separation if not do_add: eq = 0 for i in result.args: eq += i/functions result = eq svar = subs_args[0] dvar = subs_args[1:] return _separate(result, svar, dvar) def pde_separate_add(eq, fun, sep): """ Helper function for searching additive separable solutions. Consider an equation of two independent variables x, y and a dependent variable w, we look for the product of two functions depending on different arguments: `w(x, y, z) = X(x) + y(y, z)` Examples: >>> from sympy import E, Eq, Function, pde_separate_add, Derivative as D >>> from sympy.abc import x, t >>> u, X, T = map(Function, 'uXT') >>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t)) >>> pde_separate_add(eq, u(x, t), [X(x), T(t)]) [exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] """ return pde_separate(eq, fun, sep, strategy='add') def pde_separate_mul(eq, fun, sep): """ Helper function for searching multiplicative separable solutions. Consider an equation of two independent variables x, y and a dependent variable w, we look for the product of two functions depending on different arguments: `w(x, y, z) = X(x)*u(y, z)` Examples: >>> from sympy import Function, Eq, pde_separate_mul, Derivative as D >>> from sympy.abc import x, y >>> u, X, Y = map(Function, 'uXY') >>> eq = Eq(D(u(x, y), x, 2), D(u(x, y), y, 2)) >>> pde_separate_mul(eq, u(x, y), [X(x), Y(y)]) [Derivative(X(x), x, x)/X(x), Derivative(Y(y), y, y)/Y(y)] """ return pde_separate(eq, fun, sep, strategy='mul') def _separate(eq, dep, others): """Separate expression into two parts based on dependencies of variables.""" # FIRST PASS # Extract derivatives depending our separable variable... terms = set() for term in eq.args: if term.is_Mul: for i in term.args: if i.is_Derivative and not i.has(*others): terms.add(term) continue elif term.is_Derivative and not term.has(*others): terms.add(term) # Find the factor that we need to divide by div = set() for term in terms: ext, sep = term.expand().as_independent(dep) # Failed? if sep.has(*others): return None div.add(ext) # FIXME: Find lcm() of all the divisors and divide with it, instead of # current hack :( # http://code.google.com/p/sympy/issues/detail?id=1498 if len(div) > 0: final = 0 for term in eq.args: eqn = 0 for i in div: eqn += term / i final += simplify(eqn) eq = final # SECOND PASS - separate the derivatives div = set() lhs = rhs = 0 for term in eq.args: # Check, whether we have already term with independent variable... if not term.has(*others): lhs += term continue # ...otherwise, try to separate temp, sep = term.expand().as_independent(dep) # Failed? if sep.has(*others): return None # Extract the divisors div.add(sep) rhs -= term.expand() # Do the division fulldiv = reduce(operator.add, div) lhs = simplify(lhs/fulldiv).expand() rhs = simplify(rhs/fulldiv).expand() # ...and check whether we were successful :) if lhs.has(*others) or rhs.has(dep): return None return [lhs, rhs] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/inequalities.py0000644000175000017500000002064612014170666025652 0ustar georgeskgeorgesk"""Tools for solving inequalities and systems of inequalities. """ from sympy.core import Symbol, Interval, Union from sympy.core.relational import Relational, Eq, Ge, Lt from sympy.core.singleton import S from sympy.assumptions import ask, AppliedPredicate, Q from sympy.functions import re, im, Abs from sympy.logic import And, Or from sympy.polys import Poly def interval_evalf(interval): """Proper implementation of evalf() on Interval. """ return Interval(interval.left.evalf(), interval.right.evalf(), left_open=interval.left_open, right_open=interval.right_open) def solve_poly_inequality(poly, rel): """Solve a polynomial inequality with rational coefficients. """ reals, intervals = poly.real_roots(multiple=False), [] if rel == '==': for root, _ in reals: interval = Interval(root, root) intervals.append(interval) elif rel == '!=': left = S.NegativeInfinity for right, _ in reals + [(S.Infinity, 1)]: interval = Interval(left, right, True, True) intervals.append(interval) left = right else: if poly.LC() > 0: sign = +1 else: sign = -1 eq_sign, equal = None, False if rel == '>': eq_sign = +1 elif rel == '<': eq_sign = -1 elif rel == '>=': eq_sign, equal = +1, True elif rel == '<=': eq_sign, equal = -1, True else: raise ValueError("'%s' is not a valid relation" % rel) right, right_open = S.Infinity, True for left, multiplicity in reversed(reals): if multiplicity % 2: if sign == eq_sign: intervals.insert(0, Interval(left, right, not equal, right_open)) sign, right, right_open = -sign, left, not equal else: if sign == eq_sign and not equal: intervals.insert(0, Interval(left, right, True, right_open)) right, right_open = left, True elif sign != eq_sign and equal: intervals.insert(0, Interval(left, left)) if sign == eq_sign: intervals.insert(0, Interval(S.NegativeInfinity, right, True, right_open)) return intervals def solve_poly_inequalities(polys): """Solve a system of polynomial inequalities with rational coefficients. """ result = S.EmptySet for _polys in polys: global_intervals = None for poly, rel in _polys: local_intervals = solve_poly_inequality(poly, rel) if global_intervals is None: global_intervals = local_intervals else: intervals = [] for local_interval in local_intervals: for global_interval in global_intervals: interval = local_interval.intersect(global_interval) if interval is not S.EmptySet: intervals.append(interval) global_intervals = intervals if not global_intervals: break for interval in global_intervals: result = result.union(interval) return result def reduce_poly_inequalities(exprs, gen, assume=True, relational=True): """Reduce a system of polynomial inequalities with rational coefficients. """ exact = True polys = [] for _exprs in exprs: _polys = [] for expr in _exprs: if isinstance(expr, tuple): expr, rel = expr else: if expr.is_Relational: expr, rel = expr.lhs - expr.rhs, expr.rel_op else: expr, rel = expr, '==' poly = Poly(expr, gen) if not poly.get_domain().is_Exact: poly, exact = poly.to_exact(), False domain = poly.get_domain() if not (domain.is_ZZ or domain.is_QQ): raise NotImplementedError("inequality solving is not supported over %s" % domain) _polys.append((poly, rel)) polys.append(_polys) solution = solve_poly_inequalities(polys) if not exact: solution = solution.evalf() if not relational: return solution real = ask(Q.real(gen), assumptions=assume) if not real: result = And(solution.as_relational(re(gen)), Eq(im(gen), 0)) else: result = solution.as_relational(gen) return result def reduce_abs_inequality(expr, rel, gen, assume=True): """Reduce an inequality with nested absolute values. """ if not ask(Q.real(gen), assumptions=assume): raise NotImplementedError("can't solve inequalities with absolute values of a complex variable") def bottom_up_scan(expr): exprs = [] if expr.is_Add or expr.is_Mul: op = expr.__class__ for arg in expr.args: _exprs = bottom_up_scan(arg) if not exprs: exprs = _exprs else: args = [] for expr, conds in exprs: for _expr, _conds in _exprs: args.append((op(expr, _expr), conds + _conds)) exprs = args elif expr.is_Pow: n = expr.exp if not n.is_Integer or n < 0: raise ValueError("only non-negative integer powers are allowed") _exprs = bottom_up_scan(expr.base) for expr, conds in _exprs: exprs.append((expr**n, conds)) elif isinstance(expr, Abs): _exprs = bottom_up_scan(expr.args[0]) for expr, conds in _exprs: exprs.append(( expr, conds + [Ge(expr, 0)])) exprs.append((-expr, conds + [Lt(expr, 0)])) else: exprs = [(expr, [])] return exprs exprs = bottom_up_scan(expr) mapping = {'<': '>', '<=': '>='} inequalities = [] for expr, conds in exprs: if rel not in mapping.keys(): expr = Relational( expr, 0, rel) else: expr = Relational(-expr, 0, mapping[rel]) inequalities.append([expr] + conds) return reduce_poly_inequalities(inequalities, gen, assume) def reduce_abs_inequalities(exprs, gen, assume=True): """Reduce a system of inequalities with nested absolute values. """ return And(*[ reduce_abs_inequality(expr, rel, gen, assume) for expr, rel in exprs ]) def reduce_inequalities(inequalities, assume=True): """Reduce a system of inequalities with rational coefficients. """ if not hasattr(inequalities, '__iter__'): inequalities = [inequalities] poly_part, abs_part, extra_assume = {}, {}, [] for inequality in inequalities: if isinstance(inequality, bool): if inequality is False: return False else: continue if isinstance(inequality, AppliedPredicate): extra_assume.append(inequality) continue if inequality.is_Relational: expr, rel = inequality.lhs - inequality.rhs, inequality.rel_op else: expr, rel = inequality, '==' gens = expr.atoms(Symbol) if not gens: return False elif len(gens) == 1: gen = gens.pop() else: raise NotImplementedError("only univariate inequalities are supported") components = expr.find(lambda u: u.is_Function) if not components: if gen in poly_part: poly_part[gen].append((expr, rel)) else: poly_part[gen] = [(expr, rel)] else: if all(isinstance(comp, Abs) for comp in components): if gen in abs_part: abs_part[gen].append((expr, rel)) else: abs_part[gen] = [(expr, rel)] else: raise NotImplementedError("can't reduce %s" % inequalities) extra_assume = And(*extra_assume) if assume is not None: assume = And(assume, extra_assume) else: assume = extra_assume poly_reduced = [] abs_reduced = [] for gen, exprs in poly_part.iteritems(): poly_reduced.append(reduce_poly_inequalities([exprs], gen, assume)) for gen, exprs in abs_part.iteritems(): abs_reduced.append(reduce_abs_inequalities(exprs, gen, assume)) return And(*(poly_reduced + abs_reduced)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/0000755000175000017500000000000012014170666023736 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_solvers.py0000644000175000017500000003634112014170666027053 0ustar georgeskgeorgeskfrom sympy import (Matrix, Symbol, solve, exp, log, cos, acos, Rational, Eq, sqrt, oo, LambertW, pi, I, sin, asin, Function, diff, Derivative, symbols, S, sympify, var, simplify, Integral, sstr, Wild, solve_linear, Interval, And, Or, Lt, Gt, Q, re, im, expand, zoo, tan) from sympy.solvers import solve_linear_system, solve_linear_system_LU,dsolve,\ tsolve, solve_undetermined_coeffs from sympy.solvers.solvers import guess_solve_strategy, GS_POLY, GS_POLY_CV_1, GS_POLY_CV_2,\ GS_TRANSCENDENTAL, GS_RATIONAL, GS_RATIONAL_CV_1 from sympy.utilities.pytest import XFAIL, raises from sympy.abc import x def NS(e, n=15, **options): return sstr(sympify(e).evalf(n, **options), full_prec=True) def test_free_symbols(): x = Symbol('x') f = Function('f') raises(NotImplementedError, "solve(Eq(log(f(x)), Integral(x, (x, 1, f(x)))), f(x))") def test_swap_back(): f, g = map(Function, 'fg') x, y = symbols('x,y') a, b = f(x), g(x) assert solve([a + y - 2, a - b - 5], a, y, b) == \ {a: b + 5, y: -b - 3} assert solve(a + b*x - 2, [a, b]) == {a: 2, b: 0} assert solve(a + b**2*x - y, [a, b]) == {a: y - b**2*x} def test_guess_poly(): """ See solvers.guess_solve_strategy """ x, y, a = symbols('x,y,a') # polynomial equations assert guess_solve_strategy( S(4), x ) == GS_POLY assert guess_solve_strategy( x, x ) == GS_POLY assert guess_solve_strategy( x + a, x ) == GS_POLY assert guess_solve_strategy( 2*x, x ) == GS_POLY assert guess_solve_strategy( x + sqrt(2), x) == GS_POLY assert guess_solve_strategy( x + 2**Rational(1,4), x) == GS_POLY assert guess_solve_strategy( x**2 + 1, x ) == GS_POLY assert guess_solve_strategy( x**2 - 1, x ) == GS_POLY assert guess_solve_strategy( x*y + y, x ) == GS_POLY assert guess_solve_strategy( x*exp(y) + y, x) == GS_POLY assert guess_solve_strategy( (x - y**3)/(y**2*(1 - y**2)**(S(1)/2)), x) == GS_POLY def test_guess_poly_cv(): x, y = symbols('x,y') # polynomial equations via a change of variable assert guess_solve_strategy( x**Rational(1,2) + 1, x ) == GS_POLY_CV_1 assert guess_solve_strategy( x**Rational(1,3) + x**Rational(1,2) + 1, x ) == GS_POLY_CV_1 assert guess_solve_strategy( 4*x*(1 - sqrt(x)), x ) == GS_POLY_CV_1 # polynomial equation multiplying both sides by x**n assert guess_solve_strategy( x + 1/x + y, x ) == GS_POLY_CV_2 def test_guess_rational_cv(): # rational functions x, y = symbols('x,y') assert guess_solve_strategy( (x+1)/(x**2 + 2), x) == GS_RATIONAL assert guess_solve_strategy( (x - y**3)/(y**2*(1 - y**2)**(S(1)/2)), y) == GS_RATIONAL_CV_1 # rational functions via the change of variable y -> x**n assert guess_solve_strategy( (x**Rational(1,2) + 1)/(x**Rational(1,3) + x**Rational(1,2) + 1), x ) \ == GS_RATIONAL_CV_1 def test_guess_transcendental(): x, y, a, b = symbols('x,y,a,b') #transcendental functions assert guess_solve_strategy( exp(x) + 1, x ) == GS_TRANSCENDENTAL assert guess_solve_strategy( 2*cos(x)-y, x ) == GS_TRANSCENDENTAL assert guess_solve_strategy( exp(x) + exp(-x) - y, x ) == GS_TRANSCENDENTAL assert guess_solve_strategy(3**x-10, x) == GS_TRANSCENDENTAL assert guess_solve_strategy(-3**x+10, x) == GS_TRANSCENDENTAL assert guess_solve_strategy(a*x**b-y, x) == GS_TRANSCENDENTAL def test_solve_args(): a, b, x, y = symbols('a,b,x,y') #implicit symbol to solve for assert set(int(tmp) for tmp in solve(x**2-4)) == set([2,-2]) assert solve([x+y-3,x-y-5]) == {x: 4, y: -1} #no symbol to solve for assert solve(42) == [] assert solve([1,2]) is None #multiple symbols: take the first linear solution assert solve(x + y - 3, [x, y]) == {x: 3 - y} # unless it is an undetermined coefficients system assert solve(a + b*x - 2, [a, b]) == {a: 2, b: 0} #symbol is not a symbol or function raises(TypeError, "solve(x**2-pi, pi)") def test_solve_polynomial1(): x, y, a = symbols('x,y,a') assert solve(3*x-2, x) == [Rational(2,3)] assert solve(Eq(3*x, 2), x) == [Rational(2,3)] assert solve(x**2-1, x) in [[-1, 1], [1, -1]] assert solve(Eq(x**2, 1), x) in [[-1, 1], [1, -1]] assert solve( x - y**3, x) == [y**3] assert sorted(solve( x - y**3, y)) == sorted([ (-x**Rational(1,3))/2 + I*sqrt(3)*x**Rational(1,3)/2, x**Rational(1,3), (-x**Rational(1,3))/2 - I*sqrt(3)*x**Rational(1,3)/2, ]) a11,a12,a21,a22,b1,b2 = symbols('a11,a12,a21,a22,b1,b2') assert solve([a11*x + a12*y - b1, a21*x + a22*y - b2], x, y) == \ { x : (a22*b1 - a12*b2)/(a11*a22 - a12*a21), y : (a11*b2 - a21*b1)/(a11*a22 - a12*a21), } solution = {y: S.Zero, x: S.Zero} assert solve((x-y, x+y), x, y ) == solution assert solve((x-y, x+y), (x, y)) == solution assert solve((x-y, x+y), [x, y]) == solution assert solve( x**3 - 15*x - 4, x) == [-2 + 3**Rational(1,2), 4, -2 - 3**Rational(1,2) ] assert sorted(solve((x**2 - 1)**2 - a, x)) == \ sorted([(1 + a**S.Half)**S.Half, -(1 + a**S.Half)**S.Half, (1 - a**S.Half)**S.Half, -(1 - a**S.Half)**S.Half]) def test_solve_polynomial2(): x = Symbol('x') assert solve(4, x) == [] def test_solve_polynomial_cv_1a(): """ Test for solving on equations that can be converted to a polynomial equation using the change of variable y -> x**Rational(p, q) """ x = Symbol('x') assert solve( x**Rational(1,2) - 1, x) == [1] assert solve( x**Rational(1,2) - 2, x) == [4] assert solve( x**Rational(1,4) - 2, x) == [16] assert solve( x**Rational(1,3) - 3, x) == [27] # XXX there are imaginary roots that are being missed assert solve(x**Rational(1,2)+x**Rational(1,3)+x**Rational(1,4),x) == [0] def test_solve_polynomial_cv_1b(): x, a = symbols('x a') assert set(solve(4*x*(1 - a*x**(S(1)/2)), x)) == set([S(0), 1/a**2]) assert set(solve(x * (x**(S(1)/3) - 3), x)) == set([S(0), S(27)]) def test_solve_polynomial_cv_2(): """ Test for solving on equations that can be converted to a polynomial equation multiplying both sides of the equation by x**m """ x = Symbol('x') assert solve(x + 1/x - 1, x) in \ [[ Rational(1,2) + I*sqrt(3)/2, Rational(1,2) - I*sqrt(3)/2], [ Rational(1,2) - I*sqrt(3)/2, Rational(1,2) + I*sqrt(3)/2]] def test_solve_rational(): """Test solve for rational functions""" x, y, a, b = symbols('x,y,a,b') assert solve( ( x - y**3 )/( (y**2)*sqrt(1 - y**2) ), x) == [y**3] def test_linear_system(): x, y, z, t, n, a = symbols('x,y,z,t,n,a') assert solve([x-1, x-y, x-2*y, y-1], [x,y]) is None assert solve([x-1, x-y, x-2*y, x-1], [x,y]) is None assert solve([x-1, x-1, x-y, x-2*y], [x,y]) is None assert solve([x+5*y-2, -3*x+6*y-15], x, y) == {x: -3, y: 1} M = Matrix([[0,0,n*(n+1),(n+1)**2,0], [n+1,n+1,-2*n-1,-(n+1),0], [-1, 0, 1, 0, 0]]) assert solve_linear_system(M, x, y, z, t) == \ {y: 0, z: t*(-n - 1)/n, x: t*(-n - 1)/n} assert solve([x + y + z + t, -z-t], x, y, z, t) == {x: -y, z: -t} assert solve([a(0, 0) + a(0, 1) + a(1, 0) + a(1, 1), -a(1, 0) - a(1, 1)], a(0, 0), a(0, 1), a(1, 0), a(1, 1)) == {a(1, 0): -a(1, 1), a(0, 0): -a(0, 1)} def test_linear_systemLU(): x, y, z, n = symbols('x,y,z,n') M = Matrix([[1,2,0,1],[1,3,2*n,1],[4,-1,n**2,1]]) assert solve_linear_system_LU(M, [x,y,z]) == {z: -3/(n**2+18*n), x: 1-12*n/(n**2+18*n), y: 6*n/(n**2+18*n)} # Note: multiple solutions exist for some of these equations, so the tests # should be expected to break if the implementation of the solver changes # in such a way that a different branch is chosen def test_tsolve(): a, b = symbols('a,b') x, y, z = symbols('x,y,z') assert solve(exp(x)-3, x) == [log(3)] assert solve((a*x+b)*(exp(x)-3), x) == [-b/a, log(3)] assert solve(cos(x)-y, x) == [acos(y)] assert solve(2*cos(x)-y,x)== [acos(y/2)] raises(NotImplementedError, "solve(Eq(cos(x), sin(x)), x)") assert solve(exp(x) + exp(-x) - y, x, simplified=False) == [ log(y**2/2 + y*sqrt(y**2 - 4)/2 - 1)/2, log(y**2/2 - y*sqrt(y**2 - 4)/2 - 1)/2] assert solve(exp(x)-3, x) == [log(3)] assert solve(Eq(exp(x), 3), x) == [log(3)] assert solve(log(x)-3, x) == [exp(3)] assert solve(sqrt(3*x)-4, x) == [Rational(16,3)] assert solve(3**(x+2), x) == [zoo] assert solve(3**(2-x), x) == [zoo] assert solve(4*3**(5*x+2)-7, x) == [(-2*log(3) - 2*log(2) + log(7))/(5*log(3))] assert solve(x+2**x, x) == [-LambertW(log(2))/log(2)] assert solve(3*x+5+2**(-5*x+3), x) in [ [Rational(-5, 3) + LambertW(log(2**(-10240*2**(Rational(1, 3))/3)))/(5*log(2))], [-Rational(5,3) + LambertW(-10240*2**Rational(1,3)*log(2)/3)/(5*log(2))], [(-25*log(2) + 3*LambertW(-10240*2**(Rational(1, 3))*log(2)/3))/(15*log(2))], [-((25*log(2) - 3*LambertW(-10240*2**(Rational(1, 3))*log(2)/3)))/(15*log(2))], [-(25*log(2) - 3*LambertW(log(2**(-10240*2**Rational(1, 3)/3))))/(15*log(2))], [(25*log(2) - 3*LambertW(log(2**(-10240*2**Rational(1, 3)/3))))/(-15*log(2))] ] assert solve(5*x-1+3*exp(2-7*x), x) == \ [Rational(1,5) + LambertW(-21*exp(Rational(3,5))/5)/7] assert solve(2*x+5+log(3*x-2), x) == \ [Rational(2,3) + LambertW(2*exp(-Rational(19,3))/3)/2] assert solve(3*x+log(4*x), x) == [LambertW(Rational(3,4))/3] assert solve((2*x+8)*(8+exp(x)), x) == [-4, log(8) + pi*I] assert solve(2*exp(3*x+4)-3, x) in [ [Rational(-4, 3) + log(2**(Rational(2, 3))*3**(Rational(1, 3))/2)], [-Rational(4,3)+log(Rational(3,2))/3], [Rational(-4, 3) - log(2)/3 + log(3)/3], ] assert solve(2*log(3*x+4)-3, x) == [(exp(Rational(3,2))-4)/3] assert solve(exp(x)+1, x) == [pi*I] assert solve(x**2 - 2**x, x) == [2] assert solve(x**3 - 3**x, x) == [-3*LambertW(-log(3)/3)/log(3)] A = -7*2**Rational(4, 5)*6**Rational(1, 5)*log(7)/10 B = -7*3**Rational(1, 5)*log(7)/5 result = solve(2*(3*x+4)**5 - 6*7**(3*x+9), x) assert len(result) == 1 and expand(result[0]) in [ Rational(-4, 3) - 5/log(7)/3*LambertW(A), Rational(-4, 3) - 5/log(7)/3*LambertW(B), ] assert solve(z*cos(x)-y, x) == [acos(y/z)] assert solve(z*cos(2*x)-y, x) == [acos(y/z)/2] assert solve(z*cos(sin(x))-y, x) == [asin(acos(y/z))] assert solve(z*cos(x), x) == [acos(0)] # issue #1409 assert solve(y - b*x/(a+x), x) in [[-a*y/(y - b)], [a*y/(b - y)]] assert solve(y - b*exp(a/x), x) == [a/log(y/b)] # issue #1408 assert solve(y-b/(1+a*x), x) in [[(b - y)/(a*y)], [-((y - b)/(a*y))]] # issue #1407 assert solve(y-a*x**b , x) == [(y/a)**(1/b)] # issue #1406 assert solve(z**x - y, x) == [log(y)/log(z)] # issue #1405 assert solve(2**x - 10, x) == [log(10)/log(2)] def test_solve_for_functions_derivatives(): t = Symbol('t') x = Function('x')(t) y = Function('y')(t) a11,a12,a21,a22,b1,b2 = symbols('a11,a12,a21,a22,b1,b2') soln = solve([a11*x + a12*y - b1, a21*x + a22*y - b2], x, y) assert soln == { x : (a22*b1 - a12*b2)/(a11*a22 - a12*a21), y : (a11*b2 - a21*b1)/(a11*a22 - a12*a21), } assert solve(x - 1, x) == [1] assert solve(3*x - 2, x) == [Rational(2, 3)] soln = solve([a11*x.diff(t) + a12*y.diff(t) - b1, a21*x.diff(t) + a22*y.diff(t) - b2], x.diff(t), y.diff(t)) assert soln == { y.diff(t) : (a11*b2 - a21*b1)/(a11*a22 - a12*a21), x.diff(t) : (a22*b1 - a12*b2)/(a11*a22 - a12*a21) } assert solve(x.diff(t)-1, x.diff(t)) == [1] assert solve(3*x.diff(t)-2, x.diff(t)) == [Rational(2,3)] eqns = set((3*x - 1, 2*y-4)) assert solve(eqns, set((x,y))) == { x : Rational(1, 3), y: 2 } x = Symbol('x') f = Function('f') F = x**2 + f(x)**2 - 4*x - 1 assert solve(F.diff(x), diff(f(x), x)) == [-((x - 2)/f(x))] # Mixed cased with a Symbol and a Function x = Symbol('x') y = Function('y')(t) soln = solve([a11*x + a12*y.diff(t) - b1, a21*x + a22*y.diff(t) - b2], x, y.diff(t)) assert soln == { y.diff(t) : (a11*b2 - a21*b1)/(a11*a22 - a12*a21), x : (a22*b1 - a12*b2)/(a11*a22 - a12*a21) } def test_issue626(): x = Symbol("x") f = Function("f") F = x**2 + f(x)**2 - 4*x - 1 e = F.diff(x) assert solve(e, f(x).diff(x)) in [[(2 - x)/f(x)], [-((x - 2)/f(x))]] def test_solve_linear(): x, y = symbols('x y') w = Wild('w') assert solve_linear(x, x) == (0, 1) assert solve_linear(x, y - 2*x) in [(x, y/3), (y, 3*x)] assert solve_linear(x, y - 2*x, exclude=[x]) ==(y, 3*x) assert solve_linear(3*x - y, 0) in [(x, y/3), (y, 3*x)] assert solve_linear(3*x - y, 0, [x]) == (x, y/3) assert solve_linear(3*x - y, 0, [y]) == (y, 3*x) assert solve_linear(x**2/y, 1) == (y, x**2) assert solve_linear(w, x) in [(w, x), (x, w)] assert solve_linear(cos(x)**2 + sin(x)**2 + 2 + y) == \ (y, -2 - cos(x)**2 - sin(x)**2) assert solve_linear(cos(x)**2 + sin(x)**2 + 2 + y, x=[x]) == (0, 1) def test_solve_undetermined_coeffs(): a, b, c, x = symbols('a, b, c, x') assert solve_undetermined_coeffs(a*x**2 + b*x**2 + b*x + 2*c*x + c + 1, [a, b, c], x) == \ {a: -2, b: 2, c: -1} # Test that rational functions work assert solve_undetermined_coeffs(a/x + b/(x + 1) - (2*x + 1)/(x**2 + x), [a, b], x) == \ {a: 1, b: 1} # Test cancellation in rational functions assert solve_undetermined_coeffs(((c + 1)*a*x**2 + (c + 1)*b*x**2 + (c + 1)*b*x + (c + 1)*2*c*x + (c + 1)**2)/(c + 1), [a, b, c], x) == \ {a: -2, b: 2, c: -1} def test_solve_inequalities(): system = [Lt(x**2 - 2, 0), Gt(x**2 - 1, 0)] assert solve(system) == \ And(Or(And(Lt(-sqrt(2), re(x)), Lt(re(x), -1)), And(Lt(1, re(x)), Lt(re(x), sqrt(2)))), Eq(im(x), 0)) assert solve(system, assume=Q.real(x)) == \ Or(And(Lt(-sqrt(2), x), Lt(x, -1)), And(Lt(1, x), Lt(x, sqrt(2)))) def test_issue_1694(): x, y = symbols('x,y') assert solve(1/x) == [] assert solve(x*(1 - 5/x)) == [5] assert solve(x + sqrt(x) - 2) == [1] assert solve(-(1 + x)/(2 + x)**2 + 1/(2 + x)) == [] assert solve(-x**2 - 2*x + (x + 1)**2 - 1) == [] assert solve((x/(x + 1) + 3)**(-2)) == [] assert solve(x/sqrt(x**2 + 1),x) == [0] assert solve(exp(x) - y,x) == [log(y)] assert solve(exp(x)) == [zoo] assert solve(x**2 + x + sin(y)**2 + cos(y)**2 - 1, x) in [[0, -1], [-1, 0]] assert solve(4*3**(5*x + 2) - 7, x) == [(-2*log(3) - log(4) + log(7))/(5*log(3))] assert solve(x**2 - y**2/exp(x), x, y) == [x*exp(x/2), -x*exp(x/2)] # 2072 assert solve(sqrt(x)) == solve(sqrt(x**3)) == [0] assert solve(sqrt(x - 1)) == [1] # 1363 a = symbols('a') assert solve(-3*a/sqrt(x),x) == [] # 1387 assert solve(2*x/(x + 2) - 1,x) == [2] # 1397 assert solve((x**2/(7 - x)).diff(x)) == [14, 0] # 1596 f = Function('f') assert solve((3 - 5*x/f(x))*f(x), f(x)) == [5*x/3] # 1398 #assert solve(1/(5 + x)**(S(1)/5) - 9, x) == [-295244/S(59049)] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_recurr.py0000644000175000017500000000721112014170666026652 0ustar georgeskgeorgeskfrom sympy import Function, symbols, S, sqrt, rf, factorial from sympy.solvers.recurr import rsolve, rsolve_poly, rsolve_ratio, rsolve_hyper y = Function('y') n, k = symbols('n,k', integer=True) C0, C1, C2 = symbols('C0,C1,C2') def test_rsolve_poly(): assert rsolve_poly([-1, -1, 1], 0, n) == 0 assert rsolve_poly([-1, -1, 1], 1, n) == -1 assert rsolve_poly([-1, n+1], n, n) == 1 assert rsolve_poly([-1, 1], n, n) == C0 + (n**2 - n)/2 assert rsolve_poly([-n-1, n], 1, n) == C1*n - 1 assert rsolve_poly([-4*n-2, 1], 4*n+1, n) == -1 assert rsolve_poly([-1, 1], n**5 + n**3, n) == C0 - n**3 / 2 - n**5 / 2 + n**2 / 6 + n**6 / 6 + 2*n**4 / 3 def test_rsolve_ratio(): solution = rsolve_ratio([-2*n**3+n**2+2*n-1, 2*n**3+n**2-6*n, -2*n**3-11*n**2-18*n-9, 2*n**3+13*n**2+22*n+8], 0, n) assert solution in [ C1*((-2*n + 3)/(n**2 - 1))/3, (S(1)/2)*(C1*(-3 + 2*n)/(-1 + n**2)), (S(1)/2)*(C1*( 3 - 2*n)/( 1 - n**2)), (S(1)/2)*(C2*(-3 + 2*n)/(-1 + n**2)), (S(1)/2)*(C2*( 3 - 2*n)/( 1 - n**2)), ] def test_rsolve_hyper(): assert rsolve_hyper([-1, -1, 1], 0, n) in [ C0*(S.Half - S.Half*sqrt(5))**n + C1*(S.Half + S.Half*sqrt(5))**n, C1*(S.Half - S.Half*sqrt(5))**n + C0*(S.Half + S.Half*sqrt(5))**n, ] assert rsolve_hyper([n**2-2, -2*n-1, 1], 0, n) in [ C0*rf(sqrt(2), n) + C1*rf(-sqrt(2), n), C1*rf(sqrt(2), n) + C0*rf(-sqrt(2), n), ] assert rsolve_hyper([n**2-k, -2*n-1, 1], 0, n) in [ C0*rf(sqrt(k), n) + C1*rf(-sqrt(k), n), C1*rf(sqrt(k), n) + C0*rf(-sqrt(k), n), ] assert rsolve_hyper([2*n*(n+1), -n**2-3*n+2, n-1], 0, n) == C0*factorial(n) + C1*2**n assert rsolve_hyper([n + 2, -(2*n + 3)*(17*n**2 + 51*n + 39), n + 1], 0, n) == 0 assert rsolve_hyper([-n-1, -1, 1], 0, n) == 0 assert rsolve_hyper([-1, 1], n, n).expand() == C0 + n**2/2 - n/2 assert rsolve_hyper([-1, 1], 1+n, n).expand() == C0 + n**2/2 + n/2 assert rsolve_hyper([-1, 1], 3*(n+n**2), n).expand() == C0 + n**3 - n def recurrence_term(c, f): """Compute RHS of recurrence in f(n) with coefficients in c.""" return sum(c[i]*f.subs(n, n+i) for i in range(len(c))) def rsolve_bulk_checker(solver, c, q, p): """Used by test_rsolve_bulk.""" pp = solver(c, q, n) assert pp == p def test_rsolve_bulk(): """Some bulk-generated tests.""" funcs = [ n, n+1, n**2, n**3, n**4, n+n**2, 27*n + 52*n**2 - 3*n**3 + 12*n**4 - 52*n**5 ] coeffs = [ [-2, 1], [-2, -1, 1], [-1, 1, 1, -1, 1], [-n, 1], [n**2-n+12, 1] ] for p in funcs: # compute difference for c in coeffs: q = recurrence_term(c, p) if p.is_polynomial(n): yield rsolve_bulk_checker, rsolve_poly, c, q, p #if p.is_hypergeometric(n): # yield rsolve_bulk_checker, rsolve_hyper, c, q, p def test_rsolve(): f = y(n+2) - y(n+1) - y(n) h = sqrt(5)*(S.Half + S.Half*sqrt(5))**n \ - sqrt(5)*(S.Half - S.Half*sqrt(5))**n assert rsolve(f, y(n)) in [ C0*(S.Half - S.Half*sqrt(5))**n + C1*(S.Half + S.Half*sqrt(5))**n, C1*(S.Half - S.Half*sqrt(5))**n + C0*(S.Half + S.Half*sqrt(5))**n, ] assert rsolve(f, y(n), [ 0, 5 ]) == h assert rsolve(f, y(n), { 0 :0, 1 :5 }) == h assert rsolve(f, y(n), { y(0):0, y(1):5 }) == h f = (n-1)*y(n+2) - (n**2+3*n-2)*y(n+1) + 2*n*(n+1)*y(n) g = C0*factorial(n) + C1*2**n h = -3*factorial(n) + 3*2**n assert rsolve(f, y(n)) == g assert rsolve(f, y(n), [ 0, 3 ]) == h assert rsolve(f, y(n), { 0 :0, 1 :3 }) == h assert rsolve(f, y(n), { y(0):0, y(1):3 }) == h wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_numeric.py0000644000175000017500000000337312014170666027017 0ustar georgeskgeorgeskfrom sympy.mpmath import mnorm, mpf from sympy.solvers import nsolve from sympy.utilities.lambdify import lambdify from sympy import Symbol, Matrix, sqrt, Eq from sympy.utilities.pytest import raises def test_nsolve(): # onedimensional from sympy import Symbol, sin, pi x = Symbol('x') assert nsolve(sin(x), 2) - pi.evalf() < 1e-16 assert nsolve(Eq(2*x, 2), x, -10) == nsolve(2*x - 2, -10) # Testing checks on number of inputs raises(TypeError, "nsolve(Eq(2*x,2))") raises(TypeError, "nsolve(Eq(2*x,2),x,1,2)") # Issue 1730 assert nsolve(x**2/(1-x)/(1-2*x)**2-100, x, 0) # doesn't fail # multidimensional x1 = Symbol('x1') x2 = Symbol('x2') f1 = 3 * x1**2 - 2 * x2**2 - 1 f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8 f = Matrix((f1, f2)).T F = lambdify((x1, x2), f.T, modules='mpmath') for x0 in [(-1, 1), (1, -2), (4, 4), (-4, -4)]: x = nsolve(f, (x1, x2), x0, tol=1.e-8) assert mnorm(F(*x),1) <= 1.e-10 # The Chinese mathematician Zhu Shijie was the very first to solve this # nonlinear system 700 years ago (z was added to make it 3-dimensional) x = Symbol('x') y = Symbol('y') z = Symbol('z') f1 = -x + 2*y f2 = (x**2 + x*(y**2 - 2) - 4*y) / (x + 4) f3 = sqrt(x**2 + y**2)*z f = Matrix((f1, f2, f3)).T F = lambdify((x, y, z), f.T, modules='mpmath') def getroot(x0): root = nsolve((f1, f2, f3), (x, y, z), x0) assert mnorm(F(*root),1) <= 1.e-8 return root assert map(round, getroot((1, 1, 1))) == [2.0, 1.0, 0.0] assert nsolve([Eq(f1), Eq(f2), Eq(f3)], [x, y, z], (1, 1, 1)) # just see that it works a = Symbol('a') assert nsolve(1/(0.001 + a)**3 - 6/(0.9 - a)**3, a, 0.3).ae( mpf('0.31883011387318591')) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_polysys.py0000644000175000017500000000635012014170666027075 0ustar georgeskgeorgesk"""Tests for solvers of systems of polynomial equations. """ from sympy import S, symbols, Integer, Rational, sqrt, I, Poly, QQ, flatten from sympy.abc import x, y, z from sympy.polys import PolynomialError from sympy.solvers.polysys import solve_poly_system, solve_triangulated from sympy.utilities.pytest import raises def test_solve_poly_system(): assert solve_poly_system([x-1], x) == [(S.One,)] assert solve_poly_system([y - x, y - x - 1], x, y) == None assert solve_poly_system([y - x**2, y + x**2], x, y) == [(S.Zero, S.Zero)] assert solve_poly_system([2*x - 3, 3*y/2 - 2*x, z - 5*y], x, y, z) == \ [(Rational(3, 2), Integer(2), Integer(10))] assert solve_poly_system([x*y - 2*y, 2*y**2 - x**2], x, y) == \ [(0, 0), (2, -2**S.Half), (2, 2**S.Half)] assert solve_poly_system([y - x**2, y + x**2 + 1], x, y) == \ [(I*S.Half**S.Half, -S.Half), (-I*S.Half**S.Half, -S.Half)] f_1 = x**2 + y + z - 1 f_2 = x + y**2 + z - 1 f_3 = x + y + z**2 - 1 a, b = -sqrt(2) - 1, sqrt(2) - 1 assert solve_poly_system([f_1, f_2, f_3], x, y, z) == \ [(a, a, a), (0, 0, 1), (0, 1, 0), (b, b, b), (1, 0, 0)] solution = [(1, -1), (1, 1)] assert solve_poly_system([Poly(x**2 - y**2), Poly(x - 1)]) == solution assert solve_poly_system([x**2 - y**2, x - 1], x, y) == solution assert solve_poly_system([x**2 - y**2, x - 1]) == solution assert solve_poly_system([x + x*y - 3, y + x*y - 4], x, y) == [(-3, -2), (1, 2)] raises(NotImplementedError, "solve_poly_system([x**3-y**3], x, y)") raises(PolynomialError, "solve_poly_system([1/x], x)") def test_solve_biquadratic(): x0, y0, x1, y1, r = symbols('x0 y0 x1 y1 r') f_1 = (x - 1)**2 + (y - 1)**2 - r**2 f_2 = (x - 2)**2 + (y - 2)**2 - r**2 assert solve_poly_system([f_1, f_2], x, y) == \ [(S(3)/2 + (-1 + 2*r**2)**(S(1)/2)/2, S(3)/2 - (-1 + 2*r**2)**(S(1)/2)/2), (S(3)/2 - (-1 + 2*r**2)**(S(1)/2)/2, S(3)/2 + (-1 + 2*r**2)**(S(1)/2)/2)] f_1 = (x - 1)**2 + (y - 2)**2 - r**2 f_2 = (x - 1)**2 + (y - 1)**2 - r**2 assert solve_poly_system([f_1, f_2], x, y) == \ [(1 + (((2*r - 1)*(2*r + 1)))**(S(1)/2)/2, S(3)/2), (1 - (((2*r - 1)*(2*r + 1)))**(S(1)/2)/2, S(3)/2)] query = lambda expr: expr.is_Pow and expr.exp is S.Half f_1 = (x - 1 )**2 + (y - 2)**2 - r**2 f_2 = (x - x1)**2 + (y - 1)**2 - r**2 result = solve_poly_system([f_1, f_2], x, y) assert len(result) == 2 and all(len(r) == 2 for r in result) assert all(r.count(query) == 1 for r in flatten(result)) f_1 = (x - x0)**2 + (y - y0)**2 - r**2 f_2 = (x - x1)**2 + (y - y1)**2 - r**2 result = solve_poly_system([f_1, f_2], x, y) assert len(result) == 2 and all(len(r) == 2 for r in result) assert all(len(r.find(query)) == 1 for r in flatten(result)) def test_solve_triangualted(): f_1 = x**2 + y + z - 1 f_2 = x + y**2 + z - 1 f_3 = x + y + z**2 - 1 a, b = -sqrt(2) - 1, sqrt(2) - 1 assert solve_triangulated([f_1, f_2, f_3], x, y, z) == \ [(0, 0, 1), (0, 1, 0), (1, 0, 0)] dom = QQ.algebraic_field(sqrt(2)) assert solve_triangulated([f_1, f_2, f_3], x, y, z, domain=dom) == \ [(a, a, a), (0, 0, 1), (0, 1, 0), (b, b, b), (1, 0, 0)] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_inequalities.py0000644000175000017500000002221512014170666030045 0ustar georgeskgeorgesk"""Tests for tools for solving inequalities and systems of inequalities. """ from sympy.solvers.inequalities import ( reduce_poly_inequalities, reduce_inequalities) from sympy import (S, Symbol, Union, Interval, FiniteSet, Eq, Ne, Lt, Le, Gt, Ge, Or, And, pi, oo, sqrt, Q, global_assumptions, re, im, sin) from sympy.utilities.pytest import raises from sympy.abc import x, y inf = oo.evalf() def test_reduce_poly_inequalities_real_interval(): global_assumptions.add(Q.real(x)) global_assumptions.add(Q.real(y)) assert reduce_poly_inequalities([[Eq(x**2, 0)]], x, relational=False) == FiniteSet(0) assert reduce_poly_inequalities([[Le(x**2, 0)]], x, relational=False) == FiniteSet(0) assert reduce_poly_inequalities([[Lt(x**2, 0)]], x, relational=False) == S.EmptySet assert reduce_poly_inequalities([[Ge(x**2, 0)]], x, relational=False) == Interval(-oo, oo) assert reduce_poly_inequalities([[Gt(x**2, 0)]], x, relational=False) == FiniteSet(0).complement assert reduce_poly_inequalities([[Ne(x**2, 0)]], x, relational=False) == FiniteSet(0).complement assert reduce_poly_inequalities([[Eq(x**2, 1)]], x, relational=False) == FiniteSet(-1, 1) assert reduce_poly_inequalities([[Le(x**2, 1)]], x, relational=False) == Interval(-1, 1) assert reduce_poly_inequalities([[Lt(x**2, 1)]], x, relational=False) == Interval(-1, 1, True, True) assert reduce_poly_inequalities([[Ge(x**2, 1)]], x, relational=False) == Union(Interval(-oo, -1), Interval(1, oo)) assert reduce_poly_inequalities([[Gt(x**2, 1)]], x, relational=False) == Interval(-1,1).complement assert reduce_poly_inequalities([[Ne(x**2, 1)]], x, relational=False) == FiniteSet(-1,1).complement assert reduce_poly_inequalities([[Eq(x**2, 1.0)]], x, relational=False) == FiniteSet(-1.0,1.0).evalf() assert reduce_poly_inequalities([[Le(x**2, 1.0)]], x, relational=False) == Interval(-1.0, 1.0) assert reduce_poly_inequalities([[Lt(x**2, 1.0)]], x, relational=False) == Interval(-1.0, 1.0, True, True) assert reduce_poly_inequalities([[Ge(x**2, 1.0)]], x, relational=False) == Union(Interval(-inf, -1.0), Interval(1.0, inf)) assert reduce_poly_inequalities([[Gt(x**2, 1.0)]], x, relational=False) == Union(Interval(-inf, -1.0, right_open=True), Interval(1.0, inf, left_open=True)) assert reduce_poly_inequalities([[Ne(x**2, 1.0)]], x, relational=False) == FiniteSet(-1.0, 1.0).complement s = sqrt(2) assert reduce_poly_inequalities([[Lt(x**2 - 1, 0), Gt(x**2 - 1, 0)]], x, relational=False) == S.EmptySet assert reduce_poly_inequalities([[Le(x**2 - 1, 0), Ge(x**2 - 1, 0)]], x, relational=False) == FiniteSet(-1,1) assert reduce_poly_inequalities([[Le(x**2 - 2, 0), Ge(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, False, False), Interval(1, s, False, False)) assert reduce_poly_inequalities([[Le(x**2 - 2, 0), Gt(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, False, True), Interval(1, s, True, False)) assert reduce_poly_inequalities([[Lt(x**2 - 2, 0), Ge(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, True, False), Interval(1, s, False, True)) assert reduce_poly_inequalities([[Lt(x**2 - 2, 0), Gt(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, True, True), Interval(1, s, True, True)) assert reduce_poly_inequalities([[Lt(x**2 - 2, 0), Ne(x**2 - 1, 0)]], x, relational=False) == Union(Interval(-s, -1, True, True), Interval(-1, 1, True, True), Interval(1, s, True, True)) global_assumptions.remove(Q.real(x)) global_assumptions.remove(Q.real(y)) def test_reduce_poly_inequalities_real_relational(): global_assumptions.add(Q.real(x)) global_assumptions.add(Q.real(y)) assert reduce_poly_inequalities([[Eq(x**2, 0)]], x, relational=True) == Eq(x, 0) assert reduce_poly_inequalities([[Le(x**2, 0)]], x, relational=True) == Eq(x, 0) assert reduce_poly_inequalities([[Lt(x**2, 0)]], x, relational=True) == False assert reduce_poly_inequalities([[Ge(x**2, 0)]], x, relational=True) == True assert reduce_poly_inequalities([[Gt(x**2, 0)]], x, relational=True) == Or(Lt(x, 0), Lt(0, x)) assert reduce_poly_inequalities([[Ne(x**2, 0)]], x, relational=True) == Or(Lt(x, 0), Lt(0, x)) assert reduce_poly_inequalities([[Eq(x**2, 1)]], x, relational=True) == Or(Eq(x, -1), Eq(x, 1)) assert reduce_poly_inequalities([[Le(x**2, 1)]], x, relational=True) == And(Le(-1, x), Le(x, 1)) assert reduce_poly_inequalities([[Lt(x**2, 1)]], x, relational=True) == And(Lt(-1, x), Lt(x, 1)) assert reduce_poly_inequalities([[Ge(x**2, 1)]], x, relational=True) == Or(Le(x, -1), Le(1, x)) assert reduce_poly_inequalities([[Gt(x**2, 1)]], x, relational=True) == Or(Lt(x, -1), Lt(1, x)) assert reduce_poly_inequalities([[Ne(x**2, 1)]], x, relational=True) == Or(Lt(x, -1), And(Lt(-1, x), Lt(x, 1)), Lt(1, x)) assert reduce_poly_inequalities([[Eq(x**2, 1.0)]], x, relational=True).evalf() == Or(Eq(x, -1.0), Eq(x, 1.0)).evalf() assert reduce_poly_inequalities([[Le(x**2, 1.0)]], x, relational=True) == And(Le(-1.0, x), Le(x, 1.0)) assert reduce_poly_inequalities([[Lt(x**2, 1.0)]], x, relational=True) == And(Lt(-1.0, x), Lt(x, 1.0)) assert reduce_poly_inequalities([[Ge(x**2, 1.0)]], x, relational=True) == Or(Le(x, -1.0), Le(1.0, x)) assert reduce_poly_inequalities([[Gt(x**2, 1.0)]], x, relational=True) == Or(Lt(x, -1.0), Lt(1.0, x)) assert reduce_poly_inequalities([[Ne(x**2, 1.0)]], x, relational=True) == Or(Lt(x, -1.0), And(Lt(-1.0, x), Lt(x, 1.0)), Lt(1.0, x)) global_assumptions.remove(Q.real(x)) global_assumptions.remove(Q.real(y)) def test_reduce_poly_inequalities_complex_relational(): cond = Eq(im(x), 0) assert reduce_poly_inequalities([[Eq(x**2, 0)]], x, relational=True) == And(Eq(re(x), 0), cond) assert reduce_poly_inequalities([[Le(x**2, 0)]], x, relational=True) == And(Eq(re(x), 0), cond) assert reduce_poly_inequalities([[Lt(x**2, 0)]], x, relational=True) == False assert reduce_poly_inequalities([[Ge(x**2, 0)]], x, relational=True) == cond assert reduce_poly_inequalities([[Gt(x**2, 0)]], x, relational=True) == And(Or(Lt(re(x), 0), Lt(0, re(x))), cond) assert reduce_poly_inequalities([[Ne(x**2, 0)]], x, relational=True) == And(Or(Lt(re(x), 0), Lt(0, re(x))), cond) assert reduce_poly_inequalities([[Eq(x**2, 1)]], x, relational=True) == And(Or(Eq(re(x), -1), Eq(re(x), 1)), cond) assert reduce_poly_inequalities([[Le(x**2, 1)]], x, relational=True) == And(And(Le(-1, re(x)), Le(re(x), 1)), cond) assert reduce_poly_inequalities([[Lt(x**2, 1)]], x, relational=True) == And(And(Lt(-1, re(x)), Lt(re(x), 1)), cond) assert reduce_poly_inequalities([[Ge(x**2, 1)]], x, relational=True) == And(Or(Le(re(x), -1), Le(1, re(x))), cond) assert reduce_poly_inequalities([[Gt(x**2, 1)]], x, relational=True) == And(Or(Lt(re(x), -1), Lt(1, re(x))), cond) assert reduce_poly_inequalities([[Ne(x**2, 1)]], x, relational=True) == And(Or(Lt(re(x), -1), And(Lt(-1, re(x)), Lt(re(x), 1)), Lt(1, re(x))), cond) assert reduce_poly_inequalities([[Eq(x**2, 1.0)]], x, relational=True).evalf() == And(Or(Eq(re(x), -1.0), Eq(re(x), 1.0)), cond) assert reduce_poly_inequalities([[Le(x**2, 1.0)]], x, relational=True) == And(And(Le(-1.0, re(x)), Le(re(x), 1.0)), cond) assert reduce_poly_inequalities([[Lt(x**2, 1.0)]], x, relational=True) == And(And(Lt(-1.0, re(x)), Lt(re(x), 1.0)), cond) assert reduce_poly_inequalities([[Ge(x**2, 1.0)]], x, relational=True) == And(Or(Le(re(x), -1.0), Le(1.0, re(x))), cond) assert reduce_poly_inequalities([[Gt(x**2, 1.0)]], x, relational=True) == And(Or(Lt(re(x), -1.0), Lt(1.0, re(x))), cond) assert reduce_poly_inequalities([[Ne(x**2, 1.0)]], x, relational=True) == And(Or(Lt(re(x), -1.0), And(Lt(-1.0, re(x)), Lt(re(x), 1.0)), Lt(1.0, re(x))), cond) def test_reduce_abs_inequalities(): real = Q.real(x) assert reduce_inequalities(abs(x - 5) < 3, assume=real) == And(Gt(x, 2), Lt(x, 8)) assert reduce_inequalities(abs(2*x + 3) >= 8, assume=real) == Or(Le(x, -S(11)/2), Ge(x, S(5)/2)) assert reduce_inequalities(abs(x - 4) + abs(3*x - 5) < 7, assume=real) == And(Gt(x, S(1)/2), Lt(x, 4)) assert reduce_inequalities(abs(x - 4) + abs(3*abs(x) - 5) < 7, assume=real) == Or(And(-2 < x, x < -1), And(S(1)/2 < x, x < 4)) raises(NotImplementedError, "reduce_inequalities(abs(x - 5) < 3)") def test_reduce_inequalities_boolean(): assert reduce_inequalities([Eq(x**2, 0), True]) == And(Eq(re(x), 0), Eq(im(x), 0)) assert reduce_inequalities([Eq(x**2, 0), False]) == False def test_reduce_inequalities_assume(): assert reduce_inequalities([Le(x**2, 1), Q.real(x)]) == And(Le(-1, x), Le(x, 1)) assert reduce_inequalities([Le(x**2, 1)], Q.real(x)) == And(Le(-1, x), Le(x, 1)) def test_reduce_inequalities_multivariate(): assert reduce_inequalities([Ge(x**2, 1), Ge(y**2, 1)]) == \ And(And(Or(Le(re(x), -1), Le(1, re(x))), Eq(im(x), 0)), And(Or(Le(re(y), -1), Le(1, re(y))), Eq(im(y), 0))) def test_reduce_inequalities_errors(): raises(NotImplementedError, "reduce_inequalities(Ge(sin(x) + x, 1))") raises(NotImplementedError, "reduce_inequalities(Ge(x**2*y + y, 1))") raises(NotImplementedError, "reduce_inequalities(Ge(sqrt(2)*x, 1))") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_pde.py0000644000175000017500000000511212014170666026116 0ustar georgeskgeorgeskfrom sympy.solvers.pde import pde_separate_add, pde_separate_mul, _separate from sympy import Eq, exp, Function, Symbol, symbols from sympy import Derivative as D from sympy.utilities.pytest import raises def test_pde_separate_add(): x, y, z, t = symbols("x,y,z,t") c = Symbol("C", real=True) F, T, X, Y, Z, u = map(Function, 'FTXYZu') eq = Eq(D(u(x, t), x), D(u(x, t), t)*exp(u(x, t))) res = pde_separate_add(eq, u(x, t), [X(x), T(t)]) assert res == [D(X(x), x)*exp(-X(x)), D(T(t), t)*exp(T(t))] def test_pde_separate_mul(): x, y, z, t = symbols("x,y,z,t") c = Symbol("C", real=True) Phi = Function('Phi') F, R, T, X, Y, Z, u = map(Function, 'FRTXYZu') r, theta, z = symbols('r,theta,z') # Something simple :) eq = Eq(D(F(x, y, z), x) + D(F(x, y, z), y) + D(F(x, y, z), z)) # Duplicate arguments in functions raises (ValueError, "pde_separate_mul(eq, F(x, y, z), [X(x), u(z, z)])") # Wrong number of arguments raises (ValueError, "pde_separate_mul(eq, F(x, y, z), [X(x), Y(y)])") # Wrong variables: [x, y] -> [x, z] raises (ValueError, "pde_separate_mul(eq, F(x, y, z), [X(t), Y(x, y)])") assert pde_separate_mul(eq, F(x, y, z), [Y(y), u(x, z)]) == \ [D(Y(y), y)/Y(y), -D(u(x, z), x)/u(x, z) - D(u(x, z), z)/u(x, z)] assert pde_separate_mul(eq, F(x, y, z), [X(x), Y(y), Z(z)]) == \ [D(X(x), x)/X(x), -D(Z(z), z)/Z(z) - D(Y(y), y)/Y(y)] # wave equation wave = Eq(D(u(x, t), t, t), c**2*D(u(x, t), x, x)) res = pde_separate_mul(wave, u(x, t), [X(x), T(t)]) assert res == [D(X(x), x, x)/X(x), D(T(t), t, t)/(c**2*T(t))] # Laplace equation in cylindrical coords eq = Eq(1/r * D(Phi(r, theta, z), r) + D(Phi(r, theta, z), r, 2) + \ 1/r**2 * D(Phi(r, theta, z), theta, 2) + D(Phi(r, theta, z), z, 2)) # Separate z res = pde_separate_mul(eq, Phi(r, theta, z), [Z(z), u(theta, r)]) assert res == [D(Z(z), z, z)/Z(z), \ -D(u(theta, r), r, r)/u(theta, r) - \ D(u(theta, r), r)/(r*u(theta, r)) - \ D(u(theta, r), theta, theta)/(r**2*u(theta, r))] # Lets use the result to create a new equation... eq = Eq(res[1], c) # ...and separate theta... res = pde_separate_mul(eq, u(theta, r), [T(theta), R(r)]) assert res == [D(T(theta), theta, theta)/T(theta), \ -r*D(R(r), r)/R(r) - r**2*D(R(r), r, r)/R(r) - c*r**2] # ...or r... res = pde_separate_mul(eq, u(theta, r), [R(r), T(theta)]) assert res == [r*D(R(r), r)/R(r) + r**2*D(R(r), r, r)/R(r) + c*r**2, \ -D(T(theta), theta, theta)/T(theta)] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_ode.py0000644000175000017500000017576112014170666026137 0ustar georgeskgeorgeskfrom __future__ import division from sympy import (Function, dsolve, Symbol, sin, cos, sinh, acos, tan, cosh, I, exp, log, simplify, together, powsimp, fraction, radsimp, Eq, sqrt, pi, erf, diff, Rational, asinh, trigsimp, S, RootOf, Poly, Integral, atan, Equality, solve, O, LambertW, Dummy) from sympy.abc import x, y, z from sympy.solvers.ode import ode_order, homogeneous_order, \ _undetermined_coefficients_match, classify_ode, checkodesol, constant_renumber from sympy.utilities.pytest import XFAIL, skip, raises C1 = Symbol('C1') C2 = Symbol('C2') C3 = Symbol('C3') C4 = Symbol('C4') C5 = Symbol('C5') C6 = Symbol('C6') C7 = Symbol('C7') C8 = Symbol('C8') C9 = Symbol('C9') C10 = Symbol('C10') f = Function('f') g = Function('g') # Note that if the ODE solver, the integral engine, solve(), or even simplify(), # changes, these tests could fail but still be correct, only written differently. # Also not that in differently formatted solutions, the arbitrary constants # might not be equal. Using specific hints in tests can help avoid this. # Tests of order higher than 1 should run the solutions through constant_renumber # because it will normalize it (constant_renumber causes dsolve() to return different # results on different machines) def test_checkodesol(): # For the most part, checkodesol is well tested in the tests below. # These tests only handle cases not checked below. raises(ValueError, "checkodesol(f(x).diff(x), f(x), x)") raises(ValueError, "checkodesol(f(x).diff(x), f(x, y), Eq(f(x), x))") assert checkodesol(f(x).diff(x), f(x), Eq(f(x), x)) is not True assert checkodesol(f(x).diff(x), f(x), Eq(f(x), x)) == (False, 1) sol1 = Eq(f(x)**5 + 11*f(x) - 2*f(x) + x, 0) assert checkodesol(diff(sol1.lhs, x), f(x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x)*exp(f(x)), f(x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 2), f(x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 2)*exp(f(x)), f(x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 3), f(x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 3)*exp(f(x)), f(x), sol1) == (True, 0) assert checkodesol(diff(sol1.lhs, x, 3), f(x), Eq(f(x), x*log(x))) == \ (False, -9 + 60*x**4*log(x)**2 + 240*x**4*log(x)**3 + 235*x**4*log(x)**4 + 60*x**4*log(x)**5) assert checkodesol(diff(exp(f(x)) + x, x)*x, f(x), Eq(exp(f(x)) + x)) == (True, 0) assert checkodesol(diff(exp(f(x)) + x, x)*x, f(x), Eq(exp(f(x)) + x), \ solve_for_func=False) == (True, 0) assert checkodesol(f(x).diff(x, 2), f(x), [Eq(f(x), C1 + C2*x), \ Eq(f(x), C2 + C1*x), Eq(f(x), C1*x + C2*x**2)]) == \ [(True, 0), (True, 0), (False, 2*C2)] assert checkodesol(f(x).diff(x, 2), f(x), set([Eq(f(x), C1 + C2*x), \ Eq(f(x), C2 + C1*x), Eq(f(x), C1*x + C2*x**2)])) == \ set([(True, 0), (True, 0), (False, 2*C2)]) assert checkodesol(f(x).diff(x) - 1/f(x)/2, f(x), Eq(f(x)**2, x)) == \ [(True, 0), (True, 0)] assert checkodesol(f(x).diff(x) - f(x), f(x), Eq(C1*exp(x), f(x))) == (True, 0) # Based on test_1st_homogeneous_coeff_ode2_eq3sol. Make sure that # checkodesol tries back substituting f(x) when it can. eq3 = x*exp(f(x)/x) + f(x) - x*f(x).diff(x) sol3 = Eq(f(x), log(log(C1/x)**(-x))) assert not checkodesol(eq3, f(x), sol3)[1].has(f(x)) def test_dsolve_options(): eq = x*f(x).diff(x) + f(x) a = dsolve(eq, f(x), hint='all') b = dsolve(eq, f(x), hint='all', simplify=False) c = dsolve(eq, f(x), hint='all_Integral') keys = ['1st_exact', '1st_exact_Integral', '1st_homogeneous_coeff_best', \ '1st_homogeneous_coeff_subs_dep_div_indep', \ '1st_homogeneous_coeff_subs_dep_div_indep_Integral', \ '1st_homogeneous_coeff_subs_indep_div_dep', \ '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_linear', \ '1st_linear_Integral', 'best', 'best_hint', 'default', 'order', \ 'separable', 'separable_Integral'] Integral_keys = ['1st_exact_Integral', '1st_homogeneous_coeff_subs_dep_div_indep_Integral', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_linear_Integral', 'best', 'best_hint', 'default', 'order', 'separable_Integral'] assert sorted(a.keys()) == keys assert a['order'] == ode_order(eq, f(x)) assert a['best'] == Eq(f(x), C1/x) assert dsolve(eq, f(x), hint='best') == Eq(f(x), C1/x) assert a['default'] == 'separable' assert a['best_hint'] == 'separable' assert not a['1st_exact'].has(Integral) assert not a['separable'].has(Integral) assert not a['1st_homogeneous_coeff_best'].has(Integral) assert not a['1st_homogeneous_coeff_subs_dep_div_indep'].has(Integral) assert not a['1st_homogeneous_coeff_subs_indep_div_dep'].has(Integral) assert not a['1st_linear'].has(Integral) assert a['1st_linear_Integral'].has(Integral) assert a['1st_exact_Integral'].has(Integral) assert a['1st_homogeneous_coeff_subs_dep_div_indep_Integral'].has(Integral) assert a['1st_homogeneous_coeff_subs_indep_div_dep_Integral'].has(Integral) assert a['separable_Integral'].has(Integral) assert sorted(b.keys()) == keys assert b['order'] == ode_order(eq, f(x)) assert b['best'] == Eq(f(x), C1/x) assert dsolve(eq, f(x), hint='best', simplify=False) == Eq(f(x), C1/x) assert b['default'] == 'separable' assert b['best_hint'] == '1st_linear' assert a['separable'] != b['separable'] assert a['1st_homogeneous_coeff_subs_dep_div_indep'] != \ b['1st_homogeneous_coeff_subs_dep_div_indep'] assert a['1st_homogeneous_coeff_subs_indep_div_dep'] != \ b['1st_homogeneous_coeff_subs_indep_div_dep'] assert not b['1st_exact'].has(Integral) assert not b['separable'].has(Integral) assert not b['1st_homogeneous_coeff_best'].has(Integral) assert not b['1st_homogeneous_coeff_subs_dep_div_indep'].has(Integral) assert not b['1st_homogeneous_coeff_subs_indep_div_dep'].has(Integral) assert not b['1st_linear'].has(Integral) assert b['1st_linear_Integral'].has(Integral) assert b['1st_exact_Integral'].has(Integral) assert b['1st_homogeneous_coeff_subs_dep_div_indep_Integral'].has(Integral) assert b['1st_homogeneous_coeff_subs_indep_div_dep_Integral'].has(Integral) assert b['separable_Integral'].has(Integral) assert sorted(c.keys()) == Integral_keys raises(ValueError, "dsolve(eq, f(x), 'notarealhint')") raises(ValueError, "dsolve(eq, f(x), 'Liouville')") assert dsolve(f(x).diff(x) - 1/f(x)**2, f(x), 'all')['best'] == \ dsolve(f(x).diff(x) - 1/f(x)**2, f(x), 'best') def test_classify_ode(): assert classify_ode(f(x).diff(x, 2), f(x)) == \ ('nth_linear_constant_coeff_homogeneous', 'Liouville', 'Liouville_Integral') assert classify_ode(f(x), f(x)) == () assert classify_ode(Eq(f(x).diff(x), 0), f(x)) == ('separable', '1st_linear', \ '1st_homogeneous_coeff_best', '1st_homogeneous_coeff_subs_indep_div_dep', \ '1st_homogeneous_coeff_subs_dep_div_indep', \ 'nth_linear_constant_coeff_homogeneous', 'separable_Integral', \ '1st_linear_Integral', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', \ '1st_homogeneous_coeff_subs_dep_div_indep_Integral') assert classify_ode(f(x).diff(x)**2, f(x)) == () # 1650: f(x) should be cleared from highest derivative before classifying a = classify_ode(Eq(f(x).diff(x) + f(x), x), f(x)) b = classify_ode(f(x).diff(x)*f(x) + f(x)*f(x) - x*f(x), f(x)) c = classify_ode(f(x).diff(x)/f(x) + f(x)/f(x) - x/f(x), f(x)) assert a == b == c != () assert classify_ode(2*x*f(x)*f(x).diff(x) + (1 + x)*f(x)**2 - exp(x), f(x)) ==\ ('Bernoulli', 'Bernoulli_Integral') assert 'Riccati_special_minus2' in\ classify_ode(2*f(x).diff(x) + f(x)**2 - f(x)/x + 3*x**(-2), f(x)) raises(ValueError, "classify_ode(x + f(x, y).diff(x).diff(y), f(x, y))") # 2077 k = Symbol('k') assert classify_ode(f(x).diff(x)/(k*f(x) + k*x*f(x)) + 2*f(x)/(k*f(x) + k*x*f(x)) + x*f(x).diff(x)/(k*f(x) + k*x*f(x)) + z, f(x)) == ('separable', '1st_exact', 'separable_Integral', '1st_exact_Integral') def test_ode_order(): f = Function('f') g = Function('g') x = Symbol('x') assert ode_order(3*x*exp(f(x)), f(x)) == 0 assert ode_order(x*diff(f(x),x)+3*x*f(x)-sin(x)/x, f(x)) == 1 assert ode_order(x**2*f(x).diff(x,x)+x*diff(f(x),x)-f(x),f(x)) == 2 assert ode_order(diff(x*exp(f(x)),x,x), f(x)) == 2 assert ode_order(diff(x*diff(x*exp(f(x)), x,x), x), f(x)) == 3 assert ode_order(diff(f(x), x, x), g(x)) == 0 assert ode_order(diff(f(x), x, x)*diff(g(x), x), f(x)) == 2 assert ode_order(diff(f(x), x, x)*diff(g(x), x), g(x)) == 1 assert ode_order(diff(x*diff(x*exp(f(x)), x,x), x), g(x)) == 0 # In all tests below, checkodesol has the order option set to prevent superfluous # calls to ode_order(), and the solve_for_func flag set to False because # dsolve() already tries to solve for the function, unless the simplify=False # option is set. def test_old_ode_tests(): # These are simple tests from the old ode module eq1 = Eq(f(x).diff(x), 0) eq2 = Eq(3*f(x).diff(x) - 5, 0) eq3 = Eq(3*f(x).diff(x), 5) eq4 = Eq(9*f(x).diff(x, x) + f(x), 0) eq5 = Eq(9*f(x).diff(x, x), f(x)) # Type: a(x)f'(x)+b(x)*f(x)+c(x)=0 eq6 = Eq(x**2*f(x).diff(x) + 3*x*f(x) - sin(x)/x, 0) eq7 = Eq(f(x).diff(x,x) - 3*diff(f(x),x) + 2*f(x), 0) # Type: 2nd order, constant coefficients (two real different roots) eq8 = Eq(f(x).diff(x,x) - 4*diff(f(x),x) + 4*f(x), 0) # Type: 2nd order, constant coefficients (two real equal roots) eq9 = Eq(f(x).diff(x,x)+2*diff(f(x),x)+3*f(x), 0) # Type: 2nd order, constant coefficients (two complex roots) eq10 = Eq(3*f(x).diff(x) -1,0) eq11 = Eq(x*f(x).diff(x) -1,0) sol1 = Eq(f(x),C1) sol2 = Eq(f(x),C1+5*x/3) sol3 = Eq(f(x),C1+5*x/3) sol4 = Eq(f(x),C1*cos(x/3) + C2*sin(x/3)) sol5 = Eq(f(x),C1*exp(-x/3) + C2*exp(x/3)) sol6 = Eq(f(x),(C1-cos(x))/x**3) sol7 = Eq(f(x), C1*exp(x) + C2*exp(2*x)) sol8 = Eq(f(x), (C1 + C2*x)*exp(2*x)) sol9 = Eq(f(x), (C1*cos(x*sqrt(2)) + C2*sin(x*sqrt(2)))*exp(-x)) sol10 = Eq(f(x), C1 + x/3) sol11 = Eq(f(x), C1 + log(x)) assert dsolve(eq1, f(x)) == sol1 assert dsolve(eq1.lhs, f(x)) == sol1 assert dsolve(eq2, f(x)) == sol2 assert dsolve(eq3, f(x)) == sol3 assert dsolve(eq4, f(x)) == sol4 assert dsolve(eq5, f(x)) == sol5 assert dsolve(eq6, f(x)) == sol6 assert dsolve(eq7, f(x)) == sol7 assert dsolve(eq8, f(x)) == sol8 assert dsolve(eq9, f(x)) == sol9 assert dsolve(eq10, f(x)) == sol10 assert dsolve(eq11, f(x)) == sol11 assert checkodesol(eq1, f(x), sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=2, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, f(x), sol6, order=1, solve_for_func=False)[0] assert checkodesol(eq7, f(x), sol7, order=2, solve_for_func=False)[0] assert checkodesol(eq8, f(x), sol8, order=2, solve_for_func=False)[0] assert checkodesol(eq9, f(x), sol9, order=2, solve_for_func=False)[0] assert checkodesol(eq10, f(x), sol10, order=1, solve_for_func=False)[0] assert checkodesol(eq11, f(x), sol11, order=1, solve_for_func=False)[0] def test_1st_linear(): # Type: first order linear form f'(x)+p(x)f(x)=q(x) eq = Eq(f(x).diff(x) + x*f(x), x**2) sol = Eq(f(x),exp(-x**2/2)*(sqrt(2)*sqrt(pi)*I*erf(I*x/sqrt(2))/2 \ + x*exp(x**2/2) + C1)) assert dsolve(eq, f(x), hint='1st_linear') == sol assert checkodesol(eq, f(x), sol, order=1, solve_for_func=False)[0] def test_Bernoulli(): # Type: Bernoulli, f'(x) + p(x)*f(x) == q(x)*f(x)**n eq = Eq(x*f(x).diff(x) + f(x) - f(x)**2,0) sol = dsolve(eq,f(x), hint='Bernoulli') assert sol == Eq(f(x),1/(x*(C1 + 1/x))) assert checkodesol(eq, f(x), sol, order=1, solve_for_func=False)[0] def test_Riccati_special_minus2(): # Type: Riccati special alpha = -2, a*dy/dx + b*y**2 + c*y/x +d/x**2 eq = 2*f(x).diff(x) + f(x)**2 - f(x)/x + 3*x**(-2) sol = dsolve(eq,f(x), hint='Riccati_special_minus2') assert checkodesol(eq, f(x), sol, order=1, solve_for_func=False)[0] def test_1st_exact1(): # Type: Exact differential equation, p(x,f) + q(x,f)*f' == 0, # where dp/df == dq/dx eq1 = sin(x)*cos(f(x)) + cos(x)*sin(f(x))*f(x).diff(x) eq2 = (2*x*f(x) + 1)/f(x) + (f(x) - x)/f(x)**2*f(x).diff(x) eq3 = 2*x + f(x)*cos(x) + (2*f(x) + sin(x) - sin(f(x)))*f(x).diff(x) eq4 = cos(f(x)) - (x*sin(f(x)) - f(x)**2)*f(x).diff(x) eq5 = 2*x*f(x) + (x**2 + f(x)**2)*f(x).diff(x) sol1 = Eq(f(x),acos((C1)/cos(x))) sol2 = Eq(log(f(x))+x/f(x)+x**2,C1) sol3 = Eq(f(x)*sin(x)+cos(f(x))+x**2+f(x)**2,C1) sol4 = Eq(x*cos(f(x))+f(x)**3/3,C1) sol5 = Eq(x**2*f(x) + f(x)**3/3, C1) assert dsolve(eq1,f(x), hint='1st_exact') == sol1 assert dsolve(eq2,f(x), hint='1st_exact') == sol2 assert dsolve(eq3,f(x), hint='1st_exact') == sol3 assert dsolve(eq4, f(x), hint='1st_exact') == sol4 assert dsolve(eq5, f(x), hint='1st_exact', simplify=False) == sol5 assert checkodesol(eq1, f(x), sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=1, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=1, solve_for_func=False)[0] @XFAIL def test_1st_exact2(): """ This is an exact equation that fails under the exact engine. It is caught by first order homogeneous albeit with a much contorted solution. The exact engine fails because of a poorly simplified integral of q(0,y)dy, where q is the function multiplying f'. The solutions should be Eq((x**2+f(x)**2)**Rational(3,2)+y**3, C1). The equation below is equivalent, but it is so complex that checkodesol fails, and takes a long time to do so. """ skip("takes too much time") eq = x*sqrt(x**2 + f(x)**2) - (x**2*f(x)/(f(x) - sqrt(x**2 + f(x)**2)))*f(x).diff(x) sol = dsolve(eq, f(x)) assert sol == Eq(log(x),C1 - 9*sqrt(1 + f(x)**2/x**2)*asinh(f(x)/x)/(-27*f(x)/x + \ 27*sqrt(1 + f(x)**2/x**2)) - 9*sqrt(1 + f(x)**2/x**2)*log(1 - sqrt(1 + f(x)**2/x**2)*\ f(x)/x + 2*f(x)**2/x**2)/(-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2)) \ + 9*asinh(f(x)/x)*f(x)/(x*(-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2))) \ + 9*f(x)*log(1 - sqrt(1 + f(x)**2/x**2)*f(x)/x + 2*f(x)**2/x**2)/\ (x*(-27*f(x)/x + 27*sqrt(1 + f(x)**2/x**2)))) assert checkodesol(eq, f(x), sol, order=1, solve_for_func=False)[0] def test_separable1(): # test_separable1-5 are from Ordinary Differential Equations, Tenenbaum and # Pollard, pg. 55 eq1 = f(x).diff(x) - f(x) eq2 = x*f(x).diff(x) - f(x) eq3 = f(x).diff(x) + sin(x) eq4 = f(x)**2 + 1 - (x**2 + 1)*f(x).diff(x) eq5 = f(x).diff(x)/tan(x) - f(x) - 2 sol1 = Eq(f(x), exp(C1 + x)) sol2 = Eq(f(x), C1*x) sol3 = Eq(f(x), C1 + cos(x)) sol4 = Eq(atan(f(x)), C1 + atan(x)) sol5 = Eq(f(x), -2 + C1*sqrt(1 + tan(x)**2)) #sol5 = Eq(f(x), C1*(C2 + sqrt(1 + tan(x)**2))) #sol5 = Eq(-log(2 + f(x)), C1 - log(1 + tan(x)**2)/2) assert dsolve(eq1, f(x), hint='separable') == sol1 assert dsolve(eq2, f(x), hint='separable') == sol2 assert dsolve(eq3, f(x), hint='separable') == sol3 assert dsolve(eq4, f(x), hint='separable') == sol4 assert dsolve(eq5, f(x), hint='separable') == simplify(sol5) assert checkodesol(eq1, f(x), sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=1, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=1, solve_for_func=False)[0] def test_separable2(): a = Symbol('a') eq6 = f(x)*x**2*f(x).diff(x) - f(x)**3 - 2*x**2*f(x).diff(x) eq7 = f(x)**2 - 1 - (2*f(x) + x*f(x))*f(x).diff(x) eq8 = x*log(x)*f(x).diff(x) + sqrt(1 + f(x)**2) eq9 = exp(x + 1)*tan(f(x)) + cos(f(x))*f(x).diff(x) eq10 = x*cos(f(x)) + x**2*sin(f(x))*f(x).diff(x) - a**2*sin(f(x))*f(x).diff(x) # solve() messes this one up a little bit, so lets test _Integral here # We have to test strings with _Integral because y is a dummy variable. sol6str = "Integral((_y - 2)/_y**3, (_y, f(x))) == C1 + Integral(x**(-2), x)" sol7 = Eq(-log(-1 + f(x)**2)/2, C1 - log(2 + x)) sol8 = Eq(asinh(f(x)), C1 - log(log(x))) # integrate cannot handle the integral on the lhs (cos/tan) sol9str = "Integral(cos(_y)/tan(_y), (_y, f(x))) == C1 + Integral(-E*exp(x), x)" sol10 = Eq(-log(-1 + sin(f(x))**2)/2, C1 - log(x**2 - a**2)/2) assert str(dsolve(eq6, f(x), hint='separable_Integral')) == sol6str assert dsolve(eq7, f(x), hint='separable') == sol7 assert dsolve(eq8, f(x), hint='separable') == sol8 assert str(dsolve(eq9, f(x), hint='separable_Integral')) == sol9str assert dsolve(eq10, f(x), hint='separable') == sol10 assert checkodesol(eq7, f(x), sol7, order=1, solve_for_func=False)[0] assert checkodesol(eq8, f(x), sol8, order=1, solve_for_func=False)[0] assert checkodesol(eq10, f(x), sol10, order=1, solve_for_func=False)[0] def test_separable3(): eq11 = f(x).diff(x) - f(x)*tan(x) eq12 = (x - 1)*cos(f(x))*f(x).diff(x) - 2*x*sin(f(x)) eq13 = f(x).diff(x) - f(x)*log(f(x))/tan(x) sol11 = Eq(f(x), C1*sqrt(1 + tan(x)**2)) sol12 = Eq(log(-1 + cos(f(x))**2)/2, C1 + 2*x + 2*log(x - 1)) sol13 = Eq(log(log(f(x))), C1 - log(1 + tan(x)**2)/2 + log(tan(x))) assert dsolve(eq11, f(x), hint='separable') == simplify(sol11) assert dsolve(eq12, f(x), hint='separable') == sol12 assert dsolve(eq13, f(x), hint='separable') == sol13 assert checkodesol(eq11, f(x), sol11, order=1, solve_for_func=False)[0] assert checkodesol(eq13, f(x), sol13, order=1, solve_for_func=False)[0] def test_separable4(): # This has a slow integral (1/((1 + y**2)*atan(y))), so we isolate it. eq14 = x*f(x).diff(x) + (1 + f(x)**2)*atan(f(x)) sol14 = Eq(log(atan(f(x))), C1 - log(x)) assert dsolve(eq14, f(x), hint='separable') == sol14 assert checkodesol(eq14, f(x), sol14, order=1, solve_for_func=False)[0] def test_separable5(): eq15 = f(x).diff(x) + x*(f(x) + 1) eq16 = exp(f(x)**2)*(x**2 + 2*x + 1) + (x*f(x) + f(x))*f(x).diff(x) eq17 = f(x).diff(x) + f(x) eq18 = sin(x)*cos(2*f(x)) + cos(x)*sin(2*f(x))*f(x).diff(x) eq19 = (1 - x)*f(x).diff(x) - x*(f(x) + 1) eq20 = f(x)*diff(f(x), x) + x - 3*x*f(x)**2 eq21 = f(x).diff(x) - exp(x + f(x)) sol15 = Eq(f(x), -1 + exp(C1 - x**2/2)) sol16 = Eq(-exp(-f(x)**2)/2, C1 - x - x**2/2) sol17 = Eq(f(x), exp(C1 - x)) sol18 = Eq(-log(-1 + sin(2*f(x))**2)/4, C1 + log(-1 + sin(x)**2)/2) sol19a = Eq(f(x), -(1 - x - exp(C1 - x))/(1 - x)) sol19b = Eq(f(x), -1 + exp(C1 - x)/(-1 + x)) sol19c = Eq(f(x), -1/(1 - x) + x/(1 - x) + exp(C1 - x)/(1 - x)) sol19d = Eq(f(x), (C1*(1 - x) + x*(-x*exp(x) + exp(x))- exp(x) + x*exp(x))/ ((1 - x)*(-x*exp(x) + exp(x)))) sol19e = Eq(f(x), (C1*(1 - x) - x*(-x*exp(x) + exp(x)) - x*exp(x) + exp(x))/((1 - x)*(-exp(x) + x*exp(x)))) sol20 = Eq(log(-1 + 3*f(x)**2)/6, C1 + x**2/2) sol21 = Eq(-exp(-f(x)), C1 + exp(x)) assert dsolve(eq15, f(x), hint='separable') == sol15 assert dsolve(eq16, f(x), hint='separable') == sol16 assert dsolve(eq17, f(x), hint='separable') == sol17 assert dsolve(eq18, f(x), hint='separable') == sol18 assert dsolve(eq19, f(x), hint='separable') in [sol19a, sol19b, sol19c, sol19d, sol19e] assert dsolve(eq20, f(x), hint='separable') == sol20 assert dsolve(eq21, f(x), hint='separable') == sol21 assert checkodesol(eq15, f(x), sol15, order=1, solve_for_func=False)[0] assert checkodesol(eq16, f(x), sol16, order=1, solve_for_func=False)[0] assert checkodesol(eq17, f(x), sol17, order=1, solve_for_func=False)[0] assert checkodesol(eq18, f(x), sol18, order=1, solve_for_func=False)[0] assert checkodesol(eq19, f(x), sol19a, order=1, solve_for_func=False)[0] assert checkodesol(eq20, f(x), sol20, order=1, solve_for_func=False)[0] assert checkodesol(eq21, f(x), sol21, order=1, solve_for_func=False)[0] def test_separable_1_5_checkodesol(): eq12 = (x - 1)*cos(f(x))*f(x).diff(x) - 2*x*sin(f(x)) sol12 = Eq(-log(1 - cos(f(x))**2)/2, C1 - 2*x - 2*log(1 - x)) assert checkodesol(eq12, f(x), sol12, order=1, solve_for_func=False)[0] def test_homogeneous_order(): assert homogeneous_order(exp(y/x) + tan(y/x), x, y) == 0 assert homogeneous_order(x**2 + sin(x)*cos(y), x, y) == None assert homogeneous_order(x - y - x*sin(y/x), x, y) == 1 assert homogeneous_order((x*y + sqrt(x**4+y**4) + x**2*(log(x) - log(y)))/\ (pi*x**Rational(2,3)*y**Rational(3,2)), x, y) == Rational(-1,6) assert homogeneous_order(y/x*cos(y/x) - x/y*sin(y/x) + cos(y/x), x, y) == 0 assert homogeneous_order(f(x), x, f(x)) == 1 assert homogeneous_order(f(x)**2, x, f(x)) == 2 assert homogeneous_order(x*y*z, x, y) == 2 assert homogeneous_order(x*y*z, x, y, z) == 3 assert homogeneous_order(x**2*f(x)/sqrt(x**2 + f(x)**2), f(x)) == None assert homogeneous_order(f(x,y)**2, x, f(x,y), y) == 2 assert homogeneous_order(f(x,y)**2, x, f(x), y) == None assert homogeneous_order(f(x,y)**2, x, f(x,y)) == None assert homogeneous_order(f(y,x)**2, x, y, f(x, y)) == None assert homogeneous_order(f(y), f(x), x) == None assert homogeneous_order(-f(x)/x + 1/sin(f(x)/ x), f(x), x) == 0 assert homogeneous_order(log(1/y) + log(x**2), x, y) == None assert homogeneous_order(log(1/y) + log(x), x, y) == 0 assert homogeneous_order(log(x/y), x, y) == 0 assert homogeneous_order(2*log(1/y) + 2*log(x), x, y) == 0 a = Symbol('a') assert homogeneous_order(a*log(1/y) + a*log(x), x, y) == 0 assert homogeneous_order(f(x).diff(x), x, y) == None assert homogeneous_order(-f(x).diff(x) + x, x, y) == None assert homogeneous_order(O(x), x, y) == None assert homogeneous_order(x + O(x**2), x, y) == None assert homogeneous_order(x**pi, x) == pi assert homogeneous_order(x**x, x) == None raises(ValueError, "homogeneous_order(x*y)") def test_1st_homogeneous_coeff_ode1(): # Type: First order homogeneous, y'=f(y/x) eq1 = f(x)/x*cos(f(x)/x) - (x/f(x)*sin(f(x)/x) + cos(f(x)/x))*f(x).diff(x) eq2 = x*f(x).diff(x) - f(x) - x*sin(f(x)/x) eq3 = f(x) + (x*log(f(x)/x) - 2*x)*diff(f(x),x) eq4 = 2*f(x)*exp(x/f(x)) + f(x)*f(x).diff(x) - 2*x*exp(x/f(x))*f(x).diff(x) eq5 = 2*x**2*f(x) + f(x)**3 + (x*f(x)**2 - 2*x**3)*f(x).diff(x) eq6 = x*exp(f(x)/x) - f(x)*sin(f(x)/x) + x*sin(f(x)/x)*f(x).diff(x) eq7 = (x + sqrt(f(x)**2 - x*f(x)))*f(x).diff(x) - f(x) eq8 = x+f(x)-(x-f(x))*f(x).diff(x) sol1 = Eq(f(x)*sin(f(x)/x), C1) sol2 = Eq(x*sqrt(1 + cos(f(x)/x))/sqrt(-1 + cos(f(x)/x)), C1) sol3 = Eq(f(x), x*exp(1 - LambertW(C1*x))) sol4 = Eq(log(C1*f(x)) + 2*exp(x/f(x)), 0) #sol5 = Eq(log(C1*x*sqrt(1/x)*sqrt(f(x))) + x**2/(2*f(x)**2), 0) sol5 = Eq(log(C1*x*sqrt(f(x)/x)) + x**2/(2*f(x)**2), 0) sol6 = Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(C1*x) - cos(f(x)/x)*exp(-f(x)/x)/2, 0) sol7 = Eq(log(C1*f(x)) + 2*sqrt(1 - x/f(x)), 0) sol8 = Eq(-atan(f(x)/x) + log(C1*x*sqrt(1 + f(x)**2/x**2)), 0) assert dsolve(eq1, f(x), hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol1 # indep_div_dep actually has a simpler solution for eq2, but it runs too slow assert dsolve(eq2, f(x), hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol2 assert dsolve(eq3, f(x), hint='1st_homogeneous_coeff_best') == sol3 assert dsolve(eq4, f(x), hint='1st_homogeneous_coeff_best') == sol4 assert dsolve(eq5, f(x), hint='1st_homogeneous_coeff_best') == sol5 assert dsolve(eq6, f(x), hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol6 assert dsolve(eq7, f(x), hint='1st_homogeneous_coeff_best') == sol7 assert dsolve(eq8, f(x), hint='1st_homogeneous_coeff_best') == sol8 def test_1st_homogeneous_coeff_ode1_sol(): skip("This test passes, but it takes too long") # These are the checkodesols from test_homogeneous_coeff_ode1. eq1 = f(x)/x*cos(f(x)/x) - (x/f(x)*sin(f(x)/x) + cos(f(x)/x))*f(x).diff(x) eq3 = f(x) + (x*log(f(x)/x) - 2*x)*diff(f(x),x) eq4 = 2*f(x)*exp(x/f(x)) + f(x)*f(x).diff(x) - 2*x*exp(x/f(x))*f(x).diff(x) eq5 = 2*x**2*f(x) + f(x)**3 + (x*f(x)**2 - 2*x**3)*f(x).diff(x) eq6 = x*exp(f(x)/x) - f(x)*sin(f(x)/x) + x*sin(f(x)/x)*f(x).diff(x) eq8 = x+f(x)-(x-f(x))*f(x).diff(x) sol1 = Eq(f(x)*sin(f(x)/x), C1) sol3 = Eq(-f(x)/(1+log(x/f(x))),C1) sol4 = Eq(log(C1*f(x)) + 2*exp(x/f(x)), 0) sol5 = Eq(log(C1*x*sqrt(1/x)*sqrt(f(x))) + x**2/(2*f(x)**2), 0) sol6 = Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(C1*x) - cos(f(x)/x)*exp(-f(x)/x)/2, 0) sol8 = Eq(-atan(f(x)/x) + log(C1*x*sqrt(1 + f(x)**2/x**2)), 0) assert checkodesol(eq1, f(x), sol1, order=1, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=1, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=1, solve_for_func=False)[0] assert checkodesol(eq6, f(x), sol6, order=1, solve_for_func=False)[0] assert checkodesol(eq8, f(x), sol8, order=1, solve_for_func=False)[0] @XFAIL def test_1st_homogeneous_coeff_ode1_sol_fail(): #skip("Takes too long.") _u2 = Dummy('u2') __a = Dummy('a') eq2 = x*f(x).diff(x) - f(x) - x*sin(f(x)/x) eq7 = (x + sqrt(f(x)**2 - x*f(x)))*f(x).diff(x) - f(x) # test_1st_homogeneous_coeff_ode3 eq9 = f(x)**2 + (x*sqrt(f(x)**2 - x**2) - x*f(x))*f(x).diff(x) sol2 = Eq(x/tan(f(x)/(2*x)), C1) sol7 = Eq(log(C1*f(x)) + 2*sqrt(1 - x/f(x)), 0) sol9 = Eq(-Integral(-1/(-(1 - (1 - _u2**2)**(1/2))*_u2 + _u2), (_u2, __a, \ x/f(x))) + log(C1*f(x)), 0) assert checkodesol(eq2, f(x), sol2, order=1, solve_for_func=False)[0] assert checkodesol(eq7, f(x), sol7, order=1, solve_for_func=False)[0] assert checkodesol(eq9, f(x), sol9, order=1, solve_for_func=False)[0] def test_1st_homogeneous_coeff_ode2(): eq1 = f(x).diff(x) - f(x)/x+1/sin(f(x)/x) eq2 = x**2 + f(x)**2 - 2*x*f(x)*f(x).diff(x) eq3 = x*exp(f(x)/x) + f(x) - x*f(x).diff(x) sol1 = Eq(f(x), x*acos(log(C1*x))) sol2 = set([Eq(f(x), -sqrt(C1*x + x**2)), Eq(f(x), sqrt(C1*x + x**2))]) sol3 = Eq(f(x), log((-1/log(C1*x))**x)) # specific hints are applied for speed reasons assert dsolve(eq1, f(x), hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol1 assert set(dsolve(eq2, f(x), hint='1st_homogeneous_coeff_best')) == sol2 assert dsolve(eq3, f(x), hint='1st_homogeneous_coeff_subs_dep_div_indep') == sol3 assert checkodesol(eq1, f(x), sol1, order=1, solve_for_func=False)[0] assert all(i[0] for i in checkodesol(eq2, f(x), sol2, order=1, solve_for_func=False)) # the solution doesn't check...perhaps there is something wrong with the routine or the solver? # assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] @XFAIL def test_1st_homogeneous_coeff_ode2_eq3sol(): # simplify() will need to get way better before it can do this one eq3 = x*exp(f(x)/x) + f(x) - x*f(x).diff(x) sol3 = Eq(f(x), log(log(C1/x)**(-x))) assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] def test_1st_homogeneous_coeff_ode3(): # This can be solved explicitly, but the the integration engine cannot handle # it (see issue 1452). The explicit solution is included in an XFAIL test # below. checkodesol fails for this equation, so its test is in # test_homogeneous_order_ode1_sol above. It has to compare string # expressions because u2 is a dummy variable. eq = f(x)**2+(x*sqrt(f(x)**2-x**2)-x*f(x))*f(x).diff(x) solstr = "log(C1*f(x)) - Integral(-1/(_u2*(-_u2**2 + 1)**(1/2)), (_u2, x/f(x))) == 0" assert str(dsolve(eq, f(x), hint='1st_homogeneous_coeff_subs_indep_div_dep')) == solstr def test_1st_homogeneous_coeff_ode4_explicit(): x = Symbol('x', positive=True) eq = f(x)**2+(x*sqrt(f(x)**2-x**2)-x*f(x))*f(x).diff(x) sol = dsolve(eq, f(x)) assert checkodesol(eq, f(x), sol)[0] def test_1st_homogeneous_coeff_corner_case(): eq1 = f(x).diff(x) - f(x)/x eq2 = x*f(x).diff(x) - f(x) assert "1st_homogeneous_coeff_subs_dep_div_indep" not in classify_ode(eq1, f(x)) assert "1st_homogeneous_coeff_subs_indep_div_dep" not in classify_ode(eq1, f(x)) assert "1st_homogeneous_coeff_subs_dep_div_indep" not in classify_ode(eq2, f(x)) assert "1st_homogeneous_coeff_subs_indep_div_dep" not in classify_ode(eq2, f(x)) def test_nth_linear_constant_coeff_homogeneous(): # From Exercise 20, in Ordinary Differential Equations, Tenenbaum and Pollard # pg. 220 a = Symbol('a', positive=True) k = Symbol('k', real=True) eq1 = f(x).diff(x, 2) + 2*f(x).diff(x) eq2 = f(x).diff(x, 2) - 3*f(x).diff(x) + 2*f(x) eq3 = f(x).diff(x, 2) - f(x) eq4 = f(x).diff(x, 3) + f(x).diff(x, 2) - 6*f(x).diff(x) eq5 = 6*f(x).diff(x, 2) - 11*f(x).diff(x) + 4*f(x) eq6 = Eq(f(x).diff(x, 2) + 2*f(x).diff(x) - f(x), 0) eq7 = diff(f(x), x, 3) + diff(f(x), x, 2) - 10*diff(f(x), x) - 6*f(x) eq8 = f(x).diff(x, 4) - f(x).diff(x, 3) - 4*f(x).diff(x, 2) + 4*f(x).diff(x) eq9 = f(x).diff(x, 4) + 4*f(x).diff(x, 3) + f(x).diff(x, 2) - \ 4*f(x).diff(x) - 2*f(x) eq10 = f(x).diff(x, 4) - a**2*f(x) eq11 = f(x).diff(x, 2) - 2*k*f(x).diff(x) - 2*f(x) eq12 = f(x).diff(x, 2) + 4*k*f(x).diff(x) - 12*k**2*f(x) eq13 = f(x).diff(x, 4) eq14 = f(x).diff(x, 2) + 4*f(x).diff(x) + 4*f(x) eq15 = 3*f(x).diff(x, 3) + 5*f(x).diff(x, 2) + f(x).diff(x) - f(x) eq16 = f(x).diff(x, 3) - 6*f(x).diff(x, 2) +12*f(x).diff(x) - 8*f(x) eq17 = f(x).diff(x, 2) - 2*a*f(x).diff(x) + a**2*f(x) eq18 = f(x).diff(x, 4) + 3*f(x).diff(x, 3) eq19 = f(x).diff(x, 4) - 2*f(x).diff(x, 2) eq20 = f(x).diff(x, 4) + 2*f(x).diff(x, 3) - 11*f(x).diff(x, 2) - \ 12*f(x).diff(x) + 36*f(x) eq21 = 36*f(x).diff(x, 4) - 37*f(x).diff(x, 2) + 4*f(x).diff(x) + 5*f(x) eq22 = f(x).diff(x, 4) - 8*f(x).diff(x, 2) + 16*f(x) eq23 = f(x).diff(x, 2) - 2*f(x).diff(x) + 5*f(x) eq24 = f(x).diff(x, 2) - f(x).diff(x) + f(x) eq25 = f(x).diff(x, 4) + 5*f(x).diff(x, 2) + 6*f(x) eq26 = f(x).diff(x, 2) - 4*f(x).diff(x) + 20*f(x) eq27 = f(x).diff(x, 4) + 4*f(x).diff(x, 2) + 4*f(x) eq28 = f(x).diff(x, 3) + 8*f(x) eq29 = f(x).diff(x, 4) + 4*f(x).diff(x, 2) eq30 = f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) sol1 = Eq(f(x), C1 + C2*exp(-2*x)) sol2 = Eq(f(x), C1*exp(x) + C2*exp(2*x)) sol3 = Eq(f(x), C1*exp(x) + C2*exp(-x)) sol4 = Eq(f(x), C1 + C2*exp(-3*x) + C3*exp(2*x)) sol5 = Eq(f(x), C1*exp(x/2) + C2*exp(4*x/3)) sol6 = Eq(f(x), C1*exp(-x + x*sqrt(2)) + C2*exp(-x - x*sqrt(2))) sol7 = Eq(f(x), C1*exp(3*x) + C2*exp(-2*x + x*sqrt(2)) + C3*exp(-2*x - x*sqrt(2))) sol8 = Eq(f(x), C1 + C2*exp(x) + C3*exp(-2*x) + C4*exp(2*x)) sol9 = Eq(f(x), C1*exp(x) + C2*exp(-x) + C3*exp(-2*x + x*sqrt(2)) + \ C4*exp(-2*x - x*sqrt(2))) sol10 = Eq(f(x), C1*sin(x*sqrt(a)) + C2*cos(x*sqrt(a)) + C3*exp(x*sqrt(a)) + \ C4*exp(-x*sqrt(a))) sol11 = Eq(f(x), C1*exp(k*x + x*sqrt(2 + k**2)) + C2*exp(k*x - x*sqrt(2 + k**2))) sol12 = Eq(f(x), C1*exp(-4*x*abs(k) - 2*k*x) + C2*exp(-2*k*x + 4*x*abs(k))) sol13 = Eq(f(x), C1 + C2*x + C3*x**2 + C4*x**3) sol14 = Eq(f(x), (C1 + C2*x)*exp(-2*x)) sol15 = Eq(f(x), (C1 + C2*x)*exp(-x) + C3*exp(x/3)) sol16 = Eq(f(x), (C1 + C2*x + C3*x**2)*exp(2*x)) sol17 = Eq(f(x), (C1 + C2*x)*exp(a*x)) sol18 = Eq(f(x), C1 + C2*x + C3*x**2 + C4*exp(-3*x)) sol19 = Eq(f(x), C1 + C2*x + C3*exp(x*sqrt(2)) + C4*exp(-x*sqrt(2))) sol20 = Eq(f(x), (C1 + C2*x)*exp(-3*x) + (C3 + C4*x)*exp(2*x)) sol21 = Eq(f(x), C1*exp(x/2) + C2*exp(-x) + C3*exp(-x/3) + C4*exp(5*x/6)) sol22 = Eq(f(x), (C1 + C2*x)*exp(-2*x) + (C3 + C4*x)*exp(2*x)) sol23 = Eq(f(x), (C1*sin(2*x) + C2*cos(2*x))*exp(x)) sol24 = Eq(f(x), (C1*sin(x*sqrt(3)/2) + C2*cos(x*sqrt(3)/2))*exp(x/2)) sol25 = Eq(f(x), C1*cos(x*sqrt(3)) + C2*sin(x*sqrt(3)) + C3*sin(x*sqrt(2)) + \ C4*cos(x*sqrt(2))) sol26 = Eq(f(x), (C1*sin(4*x) + C2*cos(4*x))*exp(2*x)) sol27 = Eq(f(x), (C1 + C2*x)*sin(x*sqrt(2)) + (C3 + C4*x)*cos(x*sqrt(2))) sol28 = Eq(f(x), (C1*sin(x*sqrt(3)) + C2*cos(x*sqrt(3)))*exp(x) + C3*exp(-2*x)) sol29 = Eq(f(x), C1 + C2*sin(2*x) + C3*cos(2*x) + C4*x) sol30 = Eq(f(x), C1 + (C2 + C3*x)*sin(x) + (C4 + C5*x)*cos(x)) sol1s = constant_renumber(sol1, 'C', 1, 2) sol2s = constant_renumber(sol2, 'C', 1, 2) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 3) sol5s = constant_renumber(sol5, 'C', 1, 2) sol6s = constant_renumber(sol6, 'C', 1, 2) sol7s = constant_renumber(sol7, 'C', 1, 3) sol8s = constant_renumber(sol8, 'C', 1, 4) sol9s = constant_renumber(sol9, 'C', 1, 4) sol10s = constant_renumber(sol10, 'C', 1, 4) sol11s = constant_renumber(sol11, 'C', 1, 2) sol12s = constant_renumber(sol12, 'C', 1, 2) sol13s = constant_renumber(sol13, 'C', 1, 4) sol14s = constant_renumber(sol14, 'C', 1, 2) sol15s = constant_renumber(sol15, 'C', 1, 3) sol16s = constant_renumber(sol16, 'C', 1, 3) sol17s = constant_renumber(sol17, 'C', 1, 2) sol18s = constant_renumber(sol18, 'C', 1, 4) sol19s = constant_renumber(sol19, 'C', 1, 4) sol20s = constant_renumber(sol20, 'C', 1, 4) sol21s = constant_renumber(sol21, 'C', 1, 4) sol22s = constant_renumber(sol22, 'C', 1, 4) sol23s = constant_renumber(sol23, 'C', 1, 2) sol24s = constant_renumber(sol24, 'C', 1, 2) sol25s = constant_renumber(sol25, 'C', 1, 4) sol26s = constant_renumber(sol26, 'C', 1, 2) sol27s = constant_renumber(sol27, 'C', 1, 4) sol28s = constant_renumber(sol28, 'C', 1, 3) sol29s = constant_renumber(sol29, 'C', 1, 4) sol30s = constant_renumber(sol30, 'C', 1, 5) assert dsolve(eq1, f(x)) in (sol1, sol1s) assert dsolve(eq2, f(x)) in (sol2, sol2s) assert dsolve(eq3, f(x)) in (sol3, sol3s) assert dsolve(eq4, f(x)) in (sol4, sol4s) assert dsolve(eq5, f(x)) in (sol5, sol5s) assert dsolve(eq6, f(x)) in (sol6, sol6s) assert dsolve(eq7, f(x)) in (sol7, sol7s) assert dsolve(eq8, f(x)) in (sol8, sol8s) assert dsolve(eq9, f(x)) in (sol9, sol9s) assert dsolve(eq10, f(x)) in (sol10, sol10s) assert dsolve(eq11, f(x)) in (sol11, sol11s) assert dsolve(eq12, f(x)) in (sol12, sol12s) assert dsolve(eq13, f(x)) in (sol13, sol13s) assert dsolve(eq14, f(x)) in (sol14, sol14s) assert dsolve(eq15, f(x)) in (sol15, sol15s) assert dsolve(eq16, f(x)) in (sol16, sol16s) assert dsolve(eq17, f(x)) in (sol17, sol17s) assert dsolve(eq18, f(x)) in (sol18, sol18s) assert dsolve(eq19, f(x)) in (sol19, sol19s) assert dsolve(eq20, f(x)) in (sol20, sol20s) assert dsolve(eq21, f(x)) in (sol21, sol21s) assert dsolve(eq22, f(x)) in (sol22, sol22s) assert dsolve(eq23, f(x)) in (sol23, sol23s) assert dsolve(eq24, f(x)) in (sol24, sol24s) assert dsolve(eq25, f(x)) in (sol25, sol25s) assert dsolve(eq26, f(x)) in (sol26, sol26s) assert dsolve(eq27, f(x)) in (sol27, sol27s) assert dsolve(eq28, f(x)) in (sol28, sol28s) assert dsolve(eq29, f(x)) in (sol29, sol29s) assert dsolve(eq30, f(x)) in (sol30, sol30s) assert checkodesol(eq1, f(x), sol1, order=2, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol2, order=2, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=2, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=3, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, f(x), sol6, order=2, solve_for_func=False)[0] assert checkodesol(eq7, f(x), sol7, order=3, solve_for_func=False)[0] assert checkodesol(eq8, f(x), sol8, order=4, solve_for_func=False)[0] assert checkodesol(eq9, f(x), sol9, order=4, solve_for_func=False)[0] assert checkodesol(eq10, f(x), sol10, order=4, solve_for_func=False)[0] assert checkodesol(eq11, f(x), sol11, order=2, solve_for_func=False)[0] assert checkodesol(eq12, f(x), sol12, order=2, solve_for_func=False)[0] assert checkodesol(eq13, f(x), sol13, order=4, solve_for_func=False)[0] assert checkodesol(eq14, f(x), sol14, order=2, solve_for_func=False)[0] assert checkodesol(eq15, f(x), sol15, order=3, solve_for_func=False)[0] assert checkodesol(eq16, f(x), sol16, order=3, solve_for_func=False)[0] assert checkodesol(eq17, f(x), sol17, order=2, solve_for_func=False)[0] assert checkodesol(eq18, f(x), sol18, order=4, solve_for_func=False)[0] assert checkodesol(eq19, f(x), sol19, order=4, solve_for_func=False)[0] assert checkodesol(eq20, f(x), sol20, order=4, solve_for_func=False)[0] assert checkodesol(eq21, f(x), sol21, order=4, solve_for_func=False)[0] assert checkodesol(eq22, f(x), sol22, order=4, solve_for_func=False)[0] assert checkodesol(eq23, f(x), sol23, order=2, solve_for_func=False)[0] assert checkodesol(eq24, f(x), sol24, order=2, solve_for_func=False)[0] assert checkodesol(eq25, f(x), sol25, order=4, solve_for_func=False)[0] assert checkodesol(eq26, f(x), sol26, order=2, solve_for_func=False)[0] assert checkodesol(eq27, f(x), sol27, order=4, solve_for_func=False)[0] assert checkodesol(eq28, f(x), sol28, order=3, solve_for_func=False)[0] assert checkodesol(eq29, f(x), sol29, order=4, solve_for_func=False)[0] assert checkodesol(eq30, f(x), sol30, order=5, solve_for_func=False)[0] def test_nth_linear_constant_coeff_homogeneous_RootOf(): eq = f(x).diff(x, 5) + 11*f(x).diff(x) - 2*f(x) sol = Eq(f(x), C1*exp(x*RootOf(x**5 + 11*x - 2, 0)) + \ C2*exp(x*RootOf(x**5 + 11*x - 2, 1)) + \ C3*exp(x*RootOf(x**5 + 11*x - 2, 2)) + \ C4*exp(x*RootOf(x**5 + 11*x - 2, 3)) + \ C5*exp(x*RootOf(x**5 + 11*x - 2, 4))) assert dsolve(eq, f(x)) == sol @XFAIL def test_nth_linear_constant_coeff_homogeneous_RootOf_sol(): eq = f(x).diff(x, 5) + 11*f(x).diff(x) - 2*f(x) sol = Eq(f(x), C1*exp(x*RootOf(x**5 + 11*x - 2, 0)) + \ C2*exp(x*RootOf(x**5 + 11*x - 2, 1)) + \ C3*exp(x*RootOf(x**5 + 11*x - 2, 2)) + \ C4*exp(x*RootOf(x**5 + 11*x - 2, 3)) + \ C5*exp(x*RootOf(x**5 + 11*x - 2, 4))) assert checkodesol(eq, f(x), sol, order=5, solve_for_func=False)[0] def test_undetermined_coefficients_match(): assert _undetermined_coefficients_match(g(x), x) == {'test': False} assert _undetermined_coefficients_match(sin(2*x + sqrt(5)), x) == \ {'test': True, 'trialset': set([cos(2*x + sqrt(5)), sin(2*x + sqrt(5))])} assert _undetermined_coefficients_match(sin(x)*cos(x), x) == {'test': False} assert _undetermined_coefficients_match(sin(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': set([cos(x), x*cos(x), x**2*sin(x), x**2*cos(x), x*sin(x), sin(x)])} assert _undetermined_coefficients_match(sin(x)*x**2 + sin(x)*x + sin(x), x) == \ {'test': True, 'trialset': set([x*cos(x), x*sin(x), x**2*cos(x), x**2*sin(x), cos(x), sin(x)])} assert _undetermined_coefficients_match(exp(2*x)*sin(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': set([exp(2*x)*sin(x), x**2*exp(2*x)*sin(x), cos(x)*exp(2*x), x**2*cos(x)*exp(2*x), x*cos(x)*exp(2*x), x*exp(2*x)*sin(x)])} assert _undetermined_coefficients_match(1/sin(x), x) == {'test': False} assert _undetermined_coefficients_match(log(x), x) == {'test': False} assert _undetermined_coefficients_match(2**(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': set([2**x, x*2**x, x**2*2**x])} assert _undetermined_coefficients_match(x**y, x) == {'test': False} assert _undetermined_coefficients_match(exp(x)*exp(2*x + 1), x) == \ {'test': True, 'trialset': set([exp(1 + 3*x)])} assert _undetermined_coefficients_match(sin(x)*(x**2 + x + 1), x) == \ {'test': True, 'trialset': set([x*cos(x), x*sin(x), x**2*cos(x), x**2*sin(x), cos(x), sin(x)])} assert _undetermined_coefficients_match(sin(x)*(x + sin(x)), x) == {'test': False} assert _undetermined_coefficients_match(sin(x)*(x + sin(2*x)), x) == {'test': False} assert _undetermined_coefficients_match(sin(x)*tan(x), x) == {'test': False} assert _undetermined_coefficients_match(x**2*sin(x)*exp(x) + x*sin(x) + x, x) == \ {'test': True, 'trialset': set([x**2*cos(x)*exp(x), x, cos(x), S(1), exp(x)*sin(x), sin(x), x*exp(x)*sin(x), x*cos(x), x*cos(x)*exp(x), x*sin(x), cos(x)*exp(x), x**2*exp(x)*sin(x)])} assert _undetermined_coefficients_match(4*x*sin(x - 2), x) == \ {'test': True, 'trialset': set([x*cos(x - 2), x*sin(x - 2), cos(x - 2), sin(x - 2)])} assert _undetermined_coefficients_match(2**x*x, x) == \ {'test': True, 'trialset': set([2**x, x*2**x])} assert _undetermined_coefficients_match(2**x*exp(2*x), x) == \ {'test': True, 'trialset': set([2**x*exp(2*x)])} assert _undetermined_coefficients_match(exp(-x)/x, x) == \ {'test': False} # Below are from Ordinary Differential Equations, Tenenbaum and Pollard, pg. 231 assert _undetermined_coefficients_match(S(4), x) == \ {'test': True, 'trialset': set([S(1)])} assert _undetermined_coefficients_match(12*exp(x), x) == \ {'test': True, 'trialset': set([exp(x)])} assert _undetermined_coefficients_match(exp(I*x), x) == \ {'test': True, 'trialset': set([exp(I*x)])} assert _undetermined_coefficients_match(sin(x), x) == \ {'test': True, 'trialset': set([cos(x), sin(x)])} assert _undetermined_coefficients_match(cos(x), x) == \ {'test': True, 'trialset': set([cos(x), sin(x)])} assert _undetermined_coefficients_match(8 + 6*exp(x) + 2*sin(x), x) == \ {'test': True, 'trialset': set([S(1), cos(x), sin(x), exp(x)])} assert _undetermined_coefficients_match(x**2, x) == \ {'test': True, 'trialset': set([S(1), x, x**2])} assert _undetermined_coefficients_match(9*x*exp(x) + exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(x), exp(x), exp(-x)])} assert _undetermined_coefficients_match(2*exp(2*x)*sin(x), x) == \ {'test': True, 'trialset': set([exp(2*x)*sin(x), cos(x)*exp(2*x)])} assert _undetermined_coefficients_match(x - sin(x), x) == \ {'test': True, 'trialset': set([S(1), x, cos(x), sin(x)])} assert _undetermined_coefficients_match(x**2 + 2*x, x) == \ {'test': True, 'trialset': set([S(1), x, x**2])} assert _undetermined_coefficients_match(4*x*sin(x), x) == \ {'test': True, 'trialset': set([x*cos(x), x*sin(x), cos(x), sin(x)])} assert _undetermined_coefficients_match(x*sin(2*x), x) == \ {'test': True, 'trialset': set([x*cos(2*x), x*sin(2*x), cos(2*x), sin(2*x)])} assert _undetermined_coefficients_match(x**2*exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(-x), x**2*exp(-x), exp(-x)])} assert _undetermined_coefficients_match(2*exp(-x) - x**2*exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(-x), x**2*exp(-x), exp(-x)])} assert _undetermined_coefficients_match(exp(-2*x) + x**2, x) == \ {'test': True, 'trialset': set([S(1), x, x**2, exp(-2*x)])} assert _undetermined_coefficients_match(x*exp(-x), x) == \ {'test': True, 'trialset': set([x*exp(-x), exp(-x)])} assert _undetermined_coefficients_match(x + exp(2*x), x) == \ {'test': True, 'trialset': set([S(1), x, exp(2*x)])} assert _undetermined_coefficients_match(sin(x) + exp(-x), x) == \ {'test': True, 'trialset': set([cos(x), sin(x), exp(-x)])} assert _undetermined_coefficients_match(exp(x), x) == \ {'test': True, 'trialset': set([exp(x)])} # converted from sin(x)**2 assert _undetermined_coefficients_match(S(1)/2 - cos(2*x)/2, x) == \ {'test': True, 'trialset': set([S(1), cos(2*x), sin(2*x)])} # converted from exp(2*x)*sin(x)**2 assert _undetermined_coefficients_match(exp(2*x)*(S(1)/2 + cos(2*x)/2), x) == \ {'test': True, 'trialset': set([exp(2*x)*sin(2*x), cos(2*x)*exp(2*x), exp(2*x)])} assert _undetermined_coefficients_match(2*x + sin(x) + cos(x), x) == \ {'test': True, 'trialset': set([S(1), x, cos(x), sin(x)])} # converted from sin(2*x)*sin(x) assert _undetermined_coefficients_match(cos(x)/2 - cos(3*x)/2, x) == \ {'test': True, 'trialset': set([cos(x), cos(3*x), sin(x), sin(3*x)])} assert _undetermined_coefficients_match(cos(x**2), x) == {'test': False} assert _undetermined_coefficients_match(2**(x**2), x) == {'test': False} def test_nth_linear_constant_coeff_undetermined_coefficients(): hint = 'nth_linear_constant_coeff_undetermined_coefficients' eq1 = 3*f(x).diff(x, 3) + 5*f(x).diff(x, 2) + f(x).diff(x) - f(x) - x*exp(-x) - x eq2 = 3*f(x).diff(x, 3) + 5*f(x).diff(x, 2) + f(x).diff(x) - f(x) - exp(-x) - x # 3-27 below are from Ordinary Differential Equations, Tenenbaum and Pollard, pg. 231 eq3 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - 4 eq4 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - 12*exp(x) eq5 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - exp(I*x) eq6 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - sin(x) eq7 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - cos(x) eq8 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - (8 + 6*exp(x) + 2*sin(x)) eq9 = f(x).diff(x, 2) + f(x).diff(x) + f(x) - x**2 eq10 = f(x).diff(x, 2) - 2*f(x).diff(x) - 8*f(x) - 9*x*exp(x) - 10*exp(-x) eq11 = f(x).diff(x, 2) - 3*f(x).diff(x) - 2*exp(2*x)*sin(x) eq12 = f(x).diff(x, 4) - 2*f(x).diff(x, 2) + f(x) - x + sin(x) eq13 = f(x).diff(x, 2) + f(x).diff(x) - x**2 - 2*x eq14 = f(x).diff(x, 2) + f(x).diff(x) - x - sin(2*x) eq15 = f(x).diff(x, 2) + f(x) - 4*x*sin(x) eq16 = f(x).diff(x, 2) + 4*f(x) - x*sin(2*x) eq17 = f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) - x**2*exp(-x) eq18 = f(x).diff(x, 3) + 3*f(x).diff(x, 2) + 3*f(x).diff(x) + f(x) - 2*exp(-x) + \ x**2*exp(-x) eq19 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - exp(-2*x) - x**2 eq20 = f(x).diff(x, 2) - 3*f(x).diff(x) + 2*f(x) - x*exp(-x) eq21 = f(x).diff(x, 2) + f(x).diff(x) - 6*f(x) - x - exp(2*x) eq22 = f(x).diff(x, 2) + f(x) - sin(x) - exp(-x) eq23 = f(x).diff(x, 3) - 3*f(x).diff(x, 2) + 3*f(x).diff(x) - f(x) - exp(x) # sin(x)**2 eq24 = f(x).diff(x, 2) + f(x) - S(1)/2 - cos(2*x)/2 # exp(2*x)*sin(x)**2 eq25 = f(x).diff(x, 3) - f(x).diff(x) - exp(2*x)*(S(1)/2 - cos(2*x)/2) eq26 = f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - sin(x) - cos(x) # sin(2*x)*sin(x), skip 3127 for now, match bug eq27 = f(x).diff(x, 2) + f(x) - cos(x)/2 + cos(3*x)/2 eq28 = f(x).diff(x) - 1 sol1 = Eq(f(x), -1 - x + (C1 + C2*x - 3*x**2/32 - x**3/24)*exp(-x) + C3*exp(x/3)) sol2 = Eq(f(x), -1 - x + (C1 + C2*x - x**2/8)*exp(-x) + C3*exp(x/3)) sol3 = Eq(f(x), 2 + C1*exp(-x) + C2*exp(-2*x)) sol4 = Eq(f(x), 2*exp(x) + C1*exp(-x) + C2*exp(-2*x)) sol5 = Eq(f(x), C1*exp(-x) + C2*exp(-2*x) + exp(I*x)/10 - 3*I*exp(I*x)/10) sol6 = Eq(f(x), -3*cos(x)/10 + sin(x)/10 + C1*exp(-x) + C2*exp(-2*x)) sol7 = Eq(f(x), cos(x)/10 + 3*sin(x)/10 + C1*exp(-x) + C2*exp(-2*x)) sol8 = Eq(f(x), 4 - 3*cos(x)/5 + sin(x)/5 + exp(x) + C1*exp(-x) + C2*exp(-2*x)) sol9 = Eq(f(x), -2*x + x**2 + (C1*sin(x*sqrt(3)/2) + C2*cos(x*sqrt(3)/2))*exp(-x/2)) sol10 = Eq(f(x), -x*exp(x) - 2*exp(-x) + C1*exp(-2*x) + C2*exp(4*x)) sol11 = Eq(f(x), C1 + (-3*sin(x)/5 - cos(x)/5)*exp(2*x) + C2*exp(3*x)) sol12 = Eq(f(x), x - sin(x)/4 + (C1 + C2*x)*exp(x) + (C3 + C4*x)*exp(-x)) sol13 = Eq(f(x), C1 + x**3/3 + C2*exp(-x)) sol14 = Eq(f(x), C1 - x - sin(2*x)/5 - cos(2*x)/10 + x**2/2 + C2*exp(-x)) sol15 = Eq(f(x), (C1 + x)*sin(x) + (C2 - x**2)*cos(x)) sol16 = Eq(f(x), (C1 + x/16)*sin(2*x) + (C2 - x**2/8)*cos(2*x)) sol17 = Eq(f(x), (C1 + C2*x + x**4/12)*exp(-x)) sol18 = Eq(f(x), (C1 + C2*x + C3*x**2 + x**3/3 - x**5/60)*exp(-x)) sol19 = Eq(f(x), S(7)/4 - 3*x/2 + x**2/2 + C1*exp(-x) + (C2 - x)*exp(-2*x)) sol20 = Eq(f(x), C1*exp(x) + (S(5)/36 + x/6)*exp(-x) + C2*exp(2*x)) sol21 = Eq(f(x), -S(1)/36 - x/6 + C1*exp(-3*x) + (C2 + x/5)*exp(2*x)) sol22 = Eq(f(x), C1*sin(x) + (C2 - x/2)*cos(x) + exp(-x)/2) sol23 = Eq(f(x), (C1 + C2*x + C3*x**2 + x**3/6)*exp(x)) sol24 = Eq(f(x), S(1)/2 - cos(2*x)/6 + C1*sin(x) + C2*cos(x)) sol25 = Eq(f(x), C1 + C2*exp(x) + C3*exp(-x) + (S(1)/12 - 7*sin(2*x)/520 + \ 9*cos(2*x)/520)*exp(2*x)) sol26 = Eq(f(x), C1 + (C2 + C3*x - x**2/8)*sin(x) + (C4 + C5*x + x**2/8)*cos(x) + x**2) sol27 = Eq(f(x), cos(3*x)/16 + C1*cos(x) + (C2 + x/4)*sin(x)) sol28 = Eq(f(x), C1 + x) sol1s = constant_renumber(sol1, 'C', 1, 3) sol2s = constant_renumber(sol2, 'C', 1, 3) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 2) sol5s = constant_renumber(sol5, 'C', 1, 2) sol6s = constant_renumber(sol6, 'C', 1, 2) sol7s = constant_renumber(sol7, 'C', 1, 2) sol8s = constant_renumber(sol8, 'C', 1, 2) sol9s = constant_renumber(sol9, 'C', 1, 2) sol10s = constant_renumber(sol10, 'C', 1, 2) sol11s = constant_renumber(sol11, 'C', 1, 2) sol12s = constant_renumber(sol12, 'C', 1, 2) sol13s = constant_renumber(sol13, 'C', 1, 4) sol14s = constant_renumber(sol14, 'C', 1, 2) sol15s = constant_renumber(sol15, 'C', 1, 2) sol16s = constant_renumber(sol16, 'C', 1, 2) sol17s = constant_renumber(sol17, 'C', 1, 2) sol18s = constant_renumber(sol18, 'C', 1, 3) sol19s = constant_renumber(sol19, 'C', 1, 2) sol20s = constant_renumber(sol20, 'C', 1, 2) sol21s = constant_renumber(sol21, 'C', 1, 2) sol22s = constant_renumber(sol22, 'C', 1, 2) sol23s = constant_renumber(sol23, 'C', 1, 3) sol24s = constant_renumber(sol24, 'C', 1, 2) sol25s = constant_renumber(sol25, 'C', 1, 3) sol26s = constant_renumber(sol26, 'C', 1, 5) sol27s = constant_renumber(sol27, 'C', 1, 2) assert dsolve(eq1, f(x), hint=hint) in (sol1, sol1s) assert dsolve(eq2, f(x), hint=hint) in (sol2, sol2s) assert dsolve(eq3, f(x), hint=hint) in (sol3, sol3s) assert dsolve(eq4, f(x), hint=hint) in (sol4, sol4s) assert dsolve(eq5, f(x), hint=hint) in (sol5, sol5s) assert dsolve(eq6, f(x), hint=hint) in (sol6, sol6s) assert dsolve(eq7, f(x), hint=hint) in (sol7, sol7s) assert dsolve(eq8, f(x), hint=hint) in (sol8, sol8s) assert dsolve(eq9, f(x), hint=hint) in (sol9, sol9s) assert dsolve(eq10, f(x), hint=hint) in (sol10, sol10s) assert dsolve(eq11, f(x), hint=hint) in (sol11, sol11s) assert dsolve(eq12, f(x), hint=hint) in (sol12, sol12s) assert dsolve(eq13, f(x), hint=hint) in (sol13, sol13s) assert dsolve(eq14, f(x), hint=hint) in (sol14, sol14s) assert dsolve(eq15, f(x), hint=hint) in (sol15, sol15s) assert dsolve(eq16, f(x), hint=hint) in (sol16, sol16s) assert dsolve(eq17, f(x), hint=hint) in (sol17, sol17s) assert dsolve(eq18, f(x), hint=hint) in (sol18, sol18s) assert dsolve(eq19, f(x), hint=hint) in (sol19, sol19s) assert dsolve(eq20, f(x), hint=hint) in (sol20, sol20s) assert dsolve(eq21, f(x), hint=hint) in (sol21, sol21s) assert dsolve(eq22, f(x), hint=hint) in (sol22, sol22s) assert dsolve(eq23, f(x), hint=hint) in (sol23, sol23s) assert dsolve(eq24, f(x), hint=hint) in (sol24, sol24s) assert dsolve(eq25, f(x), hint=hint) in (sol25, sol25s) assert dsolve(eq26, f(x), hint=hint) in (sol26, sol26s) assert dsolve(eq27, f(x), hint=hint) in (sol27, sol27s) assert dsolve(eq28, f(x), hint=hint) == sol28 assert checkodesol(eq1, f(x), sol1, order=3, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol2, order=3, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=2, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=2, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, f(x), sol6, order=2, solve_for_func=False)[0] assert checkodesol(eq7, f(x), sol7, order=2, solve_for_func=False)[0] assert checkodesol(eq8, f(x), sol8, order=2, solve_for_func=False)[0] assert checkodesol(eq9, f(x), sol9, order=2, solve_for_func=False)[0] assert checkodesol(eq10, f(x), sol10, order=2, solve_for_func=False)[0] assert checkodesol(eq11, f(x), sol11, order=2, solve_for_func=False)[0] assert checkodesol(eq12, f(x), sol12, order=4, solve_for_func=False)[0] assert checkodesol(eq13, f(x), sol13, order=2, solve_for_func=False)[0] assert checkodesol(eq14, f(x), sol14, order=2, solve_for_func=False)[0] assert checkodesol(eq15, f(x), sol15, order=2, solve_for_func=False)[0] assert checkodesol(eq16, f(x), sol16, order=2, solve_for_func=False)[0] assert checkodesol(eq17, f(x), sol17, order=2, solve_for_func=False)[0] assert checkodesol(eq18, f(x), sol18, order=3, solve_for_func=False)[0] assert checkodesol(eq19, f(x), sol19, order=2, solve_for_func=False)[0] assert checkodesol(eq20, f(x), sol20, order=2, solve_for_func=False)[0] assert checkodesol(eq21, f(x), sol21, order=2, solve_for_func=False)[0] assert checkodesol(eq22, f(x), sol22, order=2, solve_for_func=False)[0] assert checkodesol(eq23, f(x), sol23, order=3, solve_for_func=False)[0] assert checkodesol(eq24, f(x), sol24, order=2, solve_for_func=False)[0] assert checkodesol(eq25, f(x), sol25, order=3, solve_for_func=False)[0] assert checkodesol(eq26, f(x), sol26, order=5, solve_for_func=False)[0] assert checkodesol(eq27, f(x), sol27, order=2, solve_for_func=False)[0] assert checkodesol(eq28, f(x), sol28, order=1, solve_for_func=False)[0] @XFAIL def test_nth_linear_constant_coeff_undetermined_coefficients_imaginary_exp(): # Equivalent to eq26, in test_nth_linear_constant_coeff_undetermined_coefficients # above. This fails because the algorithm for undetermined coefficients # doesn't know to multiply exp(I*x) by sufficient x because it is linearly # dependent on sin(x) and cos(x). eq26a = f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - exp(I*x) sol26 = Eq(f(x), C1 + (C2 + C3*x - x**2/8)*sin(x) + (C4 + C5*x + x**2/8)*cos(x) + x**2) assert dsolve(eq26a, f(x), hint=hint) == sol26 assert checkodesol(eq26a, f(x), sol26, order=5, solve_for_func=False)[0] def test_nth_linear_constant_coeff_variation_of_parameters(): hint = 'nth_linear_constant_coeff_variation_of_parameters' eq1 = 3*f(x).diff(x, 3) + 5*f(x).diff(x, 2) + f(x).diff(x) - f(x) - x*exp(-x) - x eq2 = 3*f(x).diff(x, 3) + 5*f(x).diff(x, 2) + f(x).diff(x) - f(x) - exp(-x) - x eq3 = f(x).diff(x) - 1 eq4 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - 4 eq5 = f(x).diff(x, 2) + 3*f(x).diff(x) + 2*f(x) - 12*exp(x) eq6 = f(x).diff(x, 2) - 2*f(x).diff(x) - 8*f(x) - 9*x*exp(x) - 10*exp(-x) eq7 = f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) - x**2*exp(-x) eq8 = f(x).diff(x, 2) - 3*f(x).diff(x) + 2*f(x) - x*exp(-x) eq9 = f(x).diff(x, 3) - 3*f(x).diff(x, 2) + 3*f(x).diff(x) - f(x) - exp(x) eq10 = f(x).diff(x, 2) + 2*f(x).diff(x) + f(x) - exp(-x)/x eq11 = f(x).diff(x, 2) + f(x) - 1/sin(x)*1/cos(x) eq12 = f(x).diff(x, 4) - 1/x sol1 = Eq(f(x), -1 - x + (C1 + C2*x - 3*x**2/32 - x**3/24)*exp(-x) + C3*exp(x/3)) sol2 = Eq(f(x), -1 - x + (C1 + C2*x - x**2/8)*exp(-x) + C3*exp(x/3)) sol3 = Eq(f(x), C1 + x) sol4 = Eq(f(x), 2 + C1*exp(-x) + C2*exp(-2*x)) sol5 = Eq(f(x), 2*exp(x) + C1*exp(-x) + C2*exp(-2*x)) sol6 = Eq(f(x), -x*exp(x) - 2*exp(-x) + C1*exp(-2*x) + C2*exp(4*x)) sol7 = Eq(f(x), (C1 + C2*x + x**4/12)*exp(-x)) sol8 = Eq(f(x), C1*exp(x) + (S(5)/36 + x/6)*exp(-x) + C2*exp(2*x)) sol9 = Eq(f(x), (C1 + C2*x + C3*x**2 + x**3/6)*exp(x)) sol10 = Eq(f(x), (C1 + x*(C2 + log(x)))*exp(-x)) sol11 = Eq(f(x), cos(x)*(C2 - Integral(1/cos(x), x)) + sin(x)*(C1 + \ Integral(1/sin(x), x))) sol12 = Eq(f(x), C1 + C2*x + x**3*(C3 + log(x)/6) + C4*x**2) sol1s = constant_renumber(sol1, 'C', 1, 3) sol2s = constant_renumber(sol2, 'C', 1, 3) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 2) sol5s = constant_renumber(sol5, 'C', 1, 2) sol6s = constant_renumber(sol6, 'C', 1, 2) sol7s = constant_renumber(sol7, 'C', 1, 2) sol8s = constant_renumber(sol8, 'C', 1, 2) sol9s = constant_renumber(sol9, 'C', 1, 3) sol10s = constant_renumber(sol10, 'C', 1, 2) sol11s = constant_renumber(sol11, 'C', 1, 2) sol12s = constant_renumber(sol12, 'C', 1, 4) assert dsolve(eq1, f(x), hint=hint) in (sol1, sol1s) assert dsolve(eq2, f(x), hint=hint) in (sol2, sol2s) assert dsolve(eq3, f(x), hint=hint) in (sol3, sol3s) assert dsolve(eq4, f(x), hint=hint) in (sol4, sol4s) assert dsolve(eq5, f(x), hint=hint) in (sol5, sol5s) assert dsolve(eq6, f(x), hint=hint) in (sol6, sol6s) assert dsolve(eq7, f(x), hint=hint) in (sol7, sol7s) assert dsolve(eq8, f(x), hint=hint) in (sol8, sol8s) assert dsolve(eq9, f(x), hint=hint) in (sol9, sol9s) assert dsolve(eq10, f(x), hint=hint) in (sol10, sol10s) assert dsolve(eq11, f(x), hint=hint+'_Integral') in (sol11, sol11s) assert dsolve(eq12, f(x), hint=hint) in (sol12, sol12s) assert checkodesol(eq1, f(x), sol1, order=3, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol2, order=3, solve_for_func=False)[0] assert checkodesol(eq3, f(x), sol3, order=1, solve_for_func=False)[0] assert checkodesol(eq4, f(x), sol4, order=2, solve_for_func=False)[0] assert checkodesol(eq5, f(x), sol5, order=2, solve_for_func=False)[0] assert checkodesol(eq6, f(x), sol6, order=2, solve_for_func=False)[0] assert checkodesol(eq7, f(x), sol7, order=2, solve_for_func=False)[0] assert checkodesol(eq8, f(x), sol8, order=2, solve_for_func=False)[0] assert checkodesol(eq9, f(x), sol9, order=3, solve_for_func=False)[0] assert checkodesol(eq10, f(x), sol10, order=2, solve_for_func=False)[0] assert checkodesol(eq12, f(x), sol12, order=4, solve_for_func=False)[0] def test_nth_linear_constant_coeff_variation_of_parameters_simplify_False(): # solve_variation_of_parameters shouldn't attempt to simplify the # Wronskian if simplify=False. If wronskian() ever gets good enough # to simplify the result itself, this test might fail. hint = 'nth_linear_constant_coeff_variation_of_parameters' assert dsolve(f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - exp(I*x), f(x), hint + "_Integral", simplify=False) != \ dsolve(f(x).diff(x, 5) + 2*f(x).diff(x, 3) + f(x).diff(x) - 2*x - exp(I*x), f(x), hint + "_Integral", simplify=True) def test_Liouville_ODE(): hint = 'Liouville' # The first part here used to be test_ODE_1() from test_solvers.py eq1 = diff(f(x),x)/x + diff(f(x),x,x)/2 - diff(f(x),x)**2/2 eq1a = diff(x*exp(-f(x)), x, x) eq2 = (eq1*exp(-f(x))/exp(f(x))).expand() # see test_unexpanded_Liouville_ODE() below eq3 = diff(f(x), x, x) + 1/f(x)*(diff(f(x), x))**2 + 1/x*diff(f(x), x) eq4 = x*diff(f(x), x, x) + x/f(x)*diff(f(x), x)**2 + x*diff(f(x), x) eq5 = Eq((x*exp(f(x))).diff(x, x), 0) sol1 = Eq(C1 + C2/x - exp(-f(x)), 0) # If solve() is ever improved, this is a better solution sol1a = Eq(f(x), -log((C1*x+C2)/x)) sol2 = Eq(C1 + C2/x - exp(-f(x)), 0) # This is equivalent to sol1 sol3 = set([Eq(f(x), -sqrt(2)*sqrt(C1 + C2*log(x))), Eq(f(x), sqrt(2)*sqrt(C1 + C2*log(x)))]) sol4 = set([Eq(f(x), -sqrt(2)*sqrt(C1 + C2*exp(-x))), Eq(f(x), sqrt(2)*sqrt(C1 + C2*exp(-x)))]) sol5 = Eq(f(x), log(C1 + C2/x)) sol1s = constant_renumber(sol1, 'C', 1, 2) sol2s = constant_renumber(sol2, 'C', 1, 2) sol3s = constant_renumber(sol3, 'C', 1, 2) sol4s = constant_renumber(sol4, 'C', 1, 2) sol5s = constant_renumber(sol5, 'C', 1, 2) assert dsolve(eq1, f(x), hint) in (sol1, sol1s) assert dsolve(eq1a, f(x), hint) in (sol1, sol1s) assert dsolve(eq2, f(x), hint) in (sol2, sol2s) assert set(dsolve(eq3, f(x), hint)) in (sol3, sol3s) # XXX: remove sqrt(2) factor assert set(dsolve(eq4, f(x), hint)) in (sol4, sol4s) # XXX: remove sqrt(2) factor assert dsolve(eq5, f(x), hint) in (sol5, sol5s) assert checkodesol(sol1, f(x), sol1a, order=2, solve_for_func=False)[0] assert checkodesol(eq1, f(x), sol1a, order=2, solve_for_func=False)[0] assert checkodesol(eq1a, f(x), sol1a, order=2, solve_for_func=False)[0] assert checkodesol(sol2, f(x), sol1a, order=2, solve_for_func=False)[0] assert checkodesol(eq2, f(x), sol1a, order=2, solve_for_func=False)[0] assert all(i[0] for i in checkodesol(eq3, f(x), sol3, order=2, solve_for_func=False)) assert all(i[0] for i in checkodesol(eq4, f(x), sol4, order=2, solve_for_func=False)) assert checkodesol(eq5, f(x), sol5, order=2, solve_for_func=False)[0] not_Liouville1 = classify_ode(diff(f(x),x)/x + f(x)*diff(f(x),x,x)/2 - diff(f(x),x)**2/2, f(x)) not_Liouville2 = classify_ode(diff(f(x),x)/x + diff(f(x),x,x)/2 - x*diff(f(x),x)**2/2, f(x)) assert hint not in not_Liouville1 assert hint not in not_Liouville2 assert hint+'_Integral' not in not_Liouville1 assert hint+'_Integral' not in not_Liouville2 def test_unexpanded_Liouville_ODE(): # This is the same as eq1 from test_Liouville_ODE() above. eq1 = diff(f(x),x)/x+diff(f(x),x,x)/2- diff(f(x),x)**2/2 eq2 = eq1*exp(-f(x))/exp(f(x)) sol2 = Eq(C1 + C2/x - exp(-f(x)), 0) sol2s = constant_renumber(sol2, 'C', 1, 2) assert dsolve(eq2, f(x)) in (sol2, sol2s) assert checkodesol(eq2, f(x), sol2, order=2, solve_for_func=False)[0] def test_1686(): from sympy.abc import A eq = x + A*(x + diff(f(x), x) + f(x)) + diff(f(x), x) + f(x) + 2 assert classify_ode(eq, f(x)) == ('1st_linear', \ 'nth_linear_constant_coeff_undetermined_coefficients', \ 'nth_linear_constant_coeff_variation_of_parameters', '1st_linear_Integral', \ 'nth_linear_constant_coeff_variation_of_parameters_Integral') # 1765 eq=(x**2 + f(x)**2)*f(x).diff(x) - 2*x*f(x) assert classify_ode(eq, f(x)) == ( '1st_homogeneous_coeff_best', '1st_homogeneous_coeff_subs_indep_div_dep', '1st_homogeneous_coeff_subs_dep_div_indep', '1st_homogeneous_coeff_subs_indep_div_dep_Integral', '1st_homogeneous_coeff_subs_dep_div_indep_Integral') def test_1726(): raises(ValueError, "dsolve(f(x, y).diff(x) - y*f(x, y), f(x))") assert classify_ode(f(x, y).diff(x) - y*f(x, y), f(x), dict=True) == \ {'default': None, 'order': 0} # See also issue 694, test Z13. raises(ValueError, "dsolve(f(x).diff(x), f(y))") assert classify_ode(f(x).diff(x), f(y), dict=True) == {'default': None, 'order': 0} def test_constant_renumber_order_issue2209(): from sympy.utilities.iterables import variations assert constant_renumber(C1*x + C2*y, "C", 1, 2) == \ constant_renumber(C1*y + C2*x, "C", 1, 2) == \ C1*x + C2*y e = C1*(C2 + x)*(C3 + y) for a, b, c in variations([C1, C2, C3], 3): assert constant_renumber(a*(b + x)*(c + y), "C", 1, 3) == e wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/tests/test_constantsimp.py0000644000175000017500000002162012014170666030072 0ustar georgeskgeorgesk""" If the arbitrary constant class from issue 1336 is ever implemented, this should serve as a set of test cases. """ from sympy import sin, exp, Function, Symbol, S, Pow, Eq, I, sinh, cosh, acos,\ cos, log, Rational, sqrt, Integral from sympy.solvers.ode import constantsimp, constant_renumber from sympy.utilities.pytest import XFAIL x = Symbol('x') y = Symbol('y') z = Symbol('z') C1 = Symbol('C1') C2 = Symbol('C2') C3 = Symbol('C3') f = Function('f') def test_constant_mul(): # We want C1 (Constant) below to absorb the y's, but not the x's assert constant_renumber(constantsimp(y*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*y, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*x, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(2*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(y*C1*x, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(x*y*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(y*x*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*y*(y + 1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(y*C1*(y + 1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x*(y*C1), x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(x*(C1*y), x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*(x*y), x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp((x*y)*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp((y*x)*C1, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(y*(y + 1)*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*x*y, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(x*C1*y, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp((C1*x)*y, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(y*(x*C1), x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp((x*C1)*y, x, 1), 'C', 1, 1) == x*C1 assert constant_renumber(constantsimp(C1*x*y*x*y*2, x, 1), 'C', 1, 1) == C1*x**2 assert constant_renumber(constantsimp(C1*x*y*z, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(C1*x*y**2*sin(z), x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(C1*C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1*C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2*C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1*C1*C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1*x*2**x, x, 1), 'C', 1, 1) == C1*x*2**x def test_constant_add(): assert constant_renumber(constantsimp(C1 + C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + 2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(2 + C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + y, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + x, x, 1), 'C', 1, 1) == C1 + x assert constant_renumber(constantsimp(C1 + x + y + x*y + 2, x, 1), 'C', 1, 1) == \ C1 + x + x*y assert constant_renumber(constantsimp(C1 + x + 2**x + y + 2, x, 1), 'C', 1, 1) == \ C1 + x + 2**x assert constant_renumber(constantsimp(C1 + C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1 + C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2 + C1, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1 + C2 + C1, x, 2), 'C', 1, 2) == C1 def test_constant_power_as_base(): assert constant_renumber(constantsimp(C1**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(Pow(C1,C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2**C1, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C2**C2, x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(C1**y, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**x, x, 1), 'C', 1, 1) == C1**x assert constant_renumber(constantsimp(C1**2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(C1**(x*y), x, 1), 'C', 1, 1) == C1**(x*y) def test_constant_power_as_exp(): assert constant_renumber(constantsimp(x**C1, x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp(y**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x**y**C1, x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp((x**y)**C1, x, 1), 'C', 1, 1) == (x**y)**C1 assert constant_renumber(constantsimp(x**(y**C1), x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp(x**C1**y, x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp(x**(C1**y), x, 1), 'C', 1, 1) == x**C1 assert constant_renumber(constantsimp((x**C1)**y, x, 1), 'C', 1, 1) == (x**C1)**y assert constant_renumber(constantsimp(2**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(S(2)**C1, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(exp(C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(exp(C1+x), x, 1), 'C', 1, 1) == exp(C1+x) assert constant_renumber(constantsimp(Pow(2, C1), x, 1), 'C', 1, 1) == C1 def test_constant_function(): assert constant_renumber(constantsimp(sin(C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(f(C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(f(C1, C1), x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(f(C1, C2), x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C2, C1), x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C2, C2), x, 2), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C1, x), x, 1), 'C', 1, 2) == f(C1, x) assert constant_renumber(constantsimp(f(C1, y), x, 1), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(y, C1), x, 1), 'C', 1, 2) == C1 assert constant_renumber(constantsimp(f(C1, y, C2), x, 2), 'C', 1, 2) == C1 @XFAIL def test_constant_function_multiple(): # The rules to not renumber in this case would be too complicated, and # dsolve is not likely to ever encounter anything remotely like this. assert constant_renumber(constantsimp(f(C1, C1, x), x, 1), 'C', 1, 1) == f(C1, C1, x) def test_constant_multiple(): assert constant_renumber(constantsimp(C1*2 + 2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(x*2/C1, x, 1), 'C', 1, 1) == C1*x assert constant_renumber(constantsimp(C1**2*2 + 2, x, 1), 'C', 1, 1) == C1 assert constant_renumber(constantsimp(sin(2*C1) + x + sqrt(2), x, 1), 'C', 1, 1) == C1 + x assert constant_renumber(constantsimp(2*C1 + C2, x, 2), 'C', 1, 2) == C1 def test_ode_solutions(): # only a few examples here, the rest will be tested in the actual dsolve tests assert constant_renumber(constantsimp(C1*exp(2*x)+exp(x)*(C2+C3), x, 3), 'C', 1, 3) == \ constant_renumber(C1*exp(x)+C2*exp(2*x), 'C', 1, 2) assert constant_renumber(constantsimp(Eq(f(x),I*C1*sinh(x/3) + C2*cosh(x/3)), x, 2), 'C', 1, 2) == constant_renumber(Eq(f(x), C1*sinh(x/3) + C2*cosh(x/3)), 'C', 1, 2) assert constant_renumber(constantsimp(Eq(f(x),acos((-C1)/cos(x))), x, 1), 'C', 1, 1) == \ Eq(f(x),acos(C1/cos(x))) assert constant_renumber(constantsimp(Eq(log(f(x)/C1) + 2*exp(x/f(x)), 0), x, 1), 'C', 1, 1) == Eq(log(C1*f(x)) + 2*exp(x/f(x)), 0) assert constant_renumber(constantsimp(Eq(log(x*2**Rational(1,2)*(1/x)**Rational(1,2)*f(x)\ **Rational(1,2)/C1) + x**2/(2*f(x)**2), 0), x, 1), 'C', 1, 1) == \ Eq(log(C1*x*(1/x)**Rational(1,2)*f(x)**Rational(1,2)) + x**2/(2*f(x)**2), 0) assert constant_renumber(constantsimp(Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(x/C1) - \ cos(f(x)/x)*exp(-f(x)/x)/2, 0), x, 1), 'C', 1, 1) == \ Eq(-exp(-f(x)/x)*sin(f(x)/x)/2 + log(C1*x) - cos(f(x)/x)*exp(-f(x)/x)/2, 0) u2 = Symbol('u2') _a = Symbol('_a') assert constant_renumber(constantsimp(Eq(-Integral(-1/((1 - u2**2)**Rational(1,2)*u2), \ (u2, _a, x/f(x))) + log(f(x)/C1), 0), x, 1), 'C', 1, 1) == \ Eq(-Integral(-1/(u2*(1 - u2**2)**Rational(1,2)), (u2, _a, x/f(x))) + \ log(C1*f(x)), 0) assert [constant_renumber(constantsimp(i, x, 1), 'C', 1, 1) for i in [Eq(f(x), (-C1*x + x**2)**Rational(1,2)), Eq(f(x), -(-C1*x + x**2)**Rational(1,2))]] == [Eq(f(x), (C1*x + x**2)**Rational(1,2)), Eq(f(x), -(C1*x + x**2)**Rational(1,2))] def test_constant_Eq(): # C1 on the rhs is well-tested, but the lhs is only tested here assert constantsimp(Eq(C1, 3 + f(x)*x), x, 1) == Eq(C1, f(x)*x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/solvers/__init__.py0000644000175000017500000000117312014170666024707 0ustar georgeskgeorgesk"""A module for solving all kinds of equations. Examples -------- >>> from sympy.solvers import solve >>> from sympy.abc import x >>> solve(x**5+5*x**4+10*x**3+10*x**2+5*x+1,x) [-1] """ from solvers import solve, solve_linear_system, solve_linear_system_LU, \ solve_undetermined_coeffs, tsolve, nsolve, solve_linear, checksol from recurr import rsolve, rsolve_poly, rsolve_ratio, rsolve_hyper from ode import checkodesol, classify_ode, ode_order, dsolve, \ homogeneous_order from polysys import solve_poly_system, solve_triangulated from pde import pde_separate, pde_separate_add, pde_separate_mul wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/0000755000175000017500000000000012014170666022174 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/algorithms/0000755000175000017500000000000012014170666024345 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/algorithms/dpll.py0000644000175000017500000002164212014170666025657 0ustar georgeskgeorgesk"""Implementation of DPLL algorithm Further improvements: eliminate calls to pl_true, implement branching rules, efficient unit propagation. References: - http://en.wikipedia.org/wiki/DPLL_algorithm - http://bioinformatics.louisville.edu/ouyang/MingOuyangThesis.pdf """ from sympy.core import Symbol from sympy import Predicate from sympy.logic.boolalg import Or, Not, conjuncts, disjuncts, to_cnf, \ to_int_repr from sympy.logic.inference import pl_true, literal_symbol def dpll_satisfiable(expr): """ Check satisfiability of a propositional sentence. It returns a model rather than True when it succeeds >>> from sympy import symbols >>> from sympy.abc import A, B >>> from sympy.logic.algorithms.dpll import dpll_satisfiable >>> dpll_satisfiable(A & ~B) {A: True, B: False} >>> dpll_satisfiable(A & ~A) False """ symbols = list(expr.atoms(Symbol, Predicate)) symbols_int_repr = set(range(1, len(symbols) + 1)) clauses = conjuncts(to_cnf(expr)) clauses_int_repr = to_int_repr(clauses, symbols) result = dpll_int_repr(clauses_int_repr, symbols_int_repr, {}) if not result: return result output = {} for key in result: output.update({symbols[key-1]: result[key]}) return output def dpll(clauses, symbols, model): """ Compute satisfiability in a partial model. Clauses is an array of conjuncts. >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import dpll >>> dpll([A, B, D], [A, B], {D: False}) False """ # compute DP kernel P, value = find_unit_clause(clauses, model) while P: model.update({P: value}) symbols.remove(P) if not value: P = ~P clauses = unit_propagate(clauses, P) P, value = find_unit_clause(clauses, model) P, value = find_pure_symbol(symbols, clauses) while P: model.update({P: value}) symbols.remove(P) if not value: P = ~P clauses = unit_propagate(clauses, P) P, value = find_pure_symbol(symbols, clauses) # end DP kernel unknown_clauses = [] for c in clauses: val = pl_true(c, model) if val == False: return False if val != True: unknown_clauses.append(c) if not unknown_clauses: return model if not clauses: return model P = symbols.pop() model_copy = model.copy() model.update({P: True}) model_copy.update({P: False}) symbols_copy = symbols[:] return (dpll(unit_propagate(unknown_clauses, P), symbols, model) or dpll(unit_propagate(unknown_clauses, Not(P)), symbols_copy, model_copy)) def dpll_int_repr(clauses, symbols, model): """ Compute satisfiability in a partial model. Arguments are expected to be in integer representation >>> from sympy.logic.algorithms.dpll import dpll_int_repr >>> dpll_int_repr([set([1]), set([2]), set([3])], set([1, 2]), {3: False}) False """ # compute DP kernel P, value = find_unit_clause_int_repr(clauses, model) while P: model.update({P: value}) symbols.remove(P) if not value: P = -P clauses = unit_propagate_int_repr(clauses, P) P, value = find_unit_clause_int_repr(clauses, model) P, value = find_pure_symbol_int_repr(symbols, clauses) while P: model.update({P: value}) symbols.remove(P) if not value: P = -P clauses = unit_propagate_int_repr(clauses, P) P, value = find_pure_symbol_int_repr(symbols, clauses) # end DP kernel unknown_clauses = [] for c in clauses: val = pl_true_int_repr(c, model) if val is False: return False if val is not True: unknown_clauses.append(c) if not unknown_clauses: return model P = symbols.pop() model_copy = model.copy() model.update({P: True}) model_copy.update({P: False}) symbols_copy = symbols.copy() return (dpll_int_repr(unit_propagate_int_repr(unknown_clauses, P), symbols, model) or dpll_int_repr(unit_propagate_int_repr(unknown_clauses, -P), symbols_copy, model_copy)) ### helper methods for DPLL def pl_true_int_repr(clause, model={}): """ Lightweight version of pl_true. Argument clause represents the set of args of an Or clause. This is used inside dpll_int_repr, it is not meant to be used directly. >>> from sympy.logic.algorithms.dpll import pl_true_int_repr >>> pl_true_int_repr(set([1, 2]), {1: False}) >>> pl_true_int_repr(set([1, 2]), {1: False, 2: False}) False """ result = False for lit in clause: if lit < 0: p = model.get(-lit) if p is not None: p = not p else: p = model.get(lit) if p is True: return True elif p is None: result = None return result def unit_propagate(clauses, symbol): """ Returns an equivalent set of clauses If a set of clauses contains the unit clause l, the other clauses are simplified by the application of the two following rules: 1. every clause containing l is removed 2. in every clause that contains ~l this literal is deleted Arguments are expected to be in CNF. >>> from sympy import symbols >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import unit_propagate >>> unit_propagate([A | B, D | ~B, B], B) [D, B] """ output = [] for c in clauses: if c.func != Or: output.append(c) continue for arg in c.args: if arg == ~symbol: output.append(Or(*[x for x in c.args if x != ~symbol])) break if arg == symbol: break else: output.append(c) return output def unit_propagate_int_repr(clauses, s): """ Same as unit_propagate, but arguments are expected to be in integer representation >>> from sympy.logic.algorithms.dpll import unit_propagate_int_repr >>> unit_propagate_int_repr([set([1, 2]), set([3, -2]), set([2])], 2) [set([3])] """ negated = set([-s]) return [clause - negated for clause in clauses if s not in clause] def find_pure_symbol(symbols, unknown_clauses): """ Find a symbol and its value if it appears only as a positive literal (or only as a negative) in clauses. >>> from sympy import symbols >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import find_pure_symbol >>> find_pure_symbol([A, B, D], [A|~B,~B|~D,D|A]) (A, True) """ for sym in symbols: found_pos, found_neg = False, False for c in unknown_clauses: if not found_pos and sym in disjuncts(c): found_pos = True if not found_neg and Not(sym) in disjuncts(c): found_neg = True if found_pos != found_neg: return sym, found_pos return None, None def find_pure_symbol_int_repr(symbols, unknown_clauses): """ Same as find_pure_symbol, but arguments are expected to be in integer representation >>> from sympy.logic.algorithms.dpll import find_pure_symbol_int_repr >>> find_pure_symbol_int_repr(set([1,2,3]), [set([1, -2]), set([-2, -3]), set([3, 1])]) (1, True) """ all_symbols = set() for c in unknown_clauses: all_symbols.update(c) found_pos = all_symbols.intersection(symbols) found_neg = all_symbols.intersection([-s for s in symbols]) for p in found_pos: if -p not in found_neg: return p, True for p in found_neg: if -p not in found_pos: return -p, False return None, None def find_unit_clause(clauses, model): """ A unit clause has only 1 variable that is not bound in the model. >>> from sympy import symbols >>> from sympy.abc import A, B, D >>> from sympy.logic.algorithms.dpll import find_unit_clause >>> find_unit_clause([A | B | D, B | ~D, A | ~B], {A:True}) (B, False) """ for clause in clauses: num_not_in_model = 0 for literal in disjuncts(clause): sym = literal_symbol(literal) if sym not in model: num_not_in_model += 1 P, value = sym, not (literal.func is Not) if num_not_in_model == 1: return P, value return None, None def find_unit_clause_int_repr(clauses, model): """ Same as find_unit_clause, but arguments are expected to be in integer representation. >>> from sympy.logic.algorithms.dpll import find_unit_clause_int_repr >>> find_unit_clause_int_repr([set([1, 2, 3]), set([2, -3]), set([1, -2])], {1: True}) (2, False) """ bound = set(model) | set(-sym for sym in model) for clause in clauses: unbound = clause - bound if len(unbound) == 1: p = unbound.pop() if p < 0: return -p, False else: return p, True return None, None wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/algorithms/dpll2.py0000644000175000017500000003202012014170666025731 0ustar georgeskgeorgesk"""Implementation of DPLL algorithm Features: - Clause learning - Watch literal scheme - VSIDS heuristic References: - http://en.wikipedia.org/wiki/DPLL_algorithm """ from sympy.core import Symbol from sympy import Predicate from sympy.logic.boolalg import conjuncts, to_cnf, to_int_repr from heapq import heappush, heappop def dpll_satisfiable(expr): """ Check satisfiability of a propositional sentence. It returns a model rather than True when it succeeds >>> from sympy import symbols >>> from sympy.abc import A, B >>> from sympy.logic.algorithms.dpll import dpll_satisfiable >>> dpll_satisfiable(A & ~B) {A: True, B: False} >>> dpll_satisfiable(A & ~A) False """ symbols = list(expr.atoms(Symbol, Predicate)) symbols_int_repr = set(range(1, len(symbols) + 1)) clauses = conjuncts(to_cnf(expr)) clauses_int_repr = to_int_repr(clauses, symbols) solver = SATSolver(clauses_int_repr, symbols_int_repr, set()) result = solver.find_model() if not result: return result # Uncomment to confirm the solution is valid (hitting set for the clauses) #else: #for cls in clauses_int_repr: #assert solver.var_settings.intersection(cls) return dict((symbols[abs(lit) - 1], lit > 0) for lit in solver.var_settings) class SATSolver(object): """ Class for representing a SAT solver capable of finding a model to a boolean theory in conjunctive normal form. """ def __init__(self, clauses, variables, var_settings, heuristic = 'vsids', \ clause_learning = 'none', INTERVAL = 500): self.var_settings = var_settings self.heuristic = heuristic self.is_unsatisfied = False self.unit_prop_queue = [] self.update_functions = [] self.INTERVAL = INTERVAL self.initialize_variables(variables) self.initialize_clauses(clauses) if 'vsids' == heuristic: self.vsids_init() self.heur_calculate = self.vsids_calculate self.heur_lit_assigned = self.vsids_lit_assigned self.heur_lit_unset = self.vsids_lit_unset self.heur_clause_added = self.vsids_clause_added # Note: Uncomment this if/when clause learning is enabled #self.update_functions.append(self.vsids_decay) else: raise NotImplementedError if 'simple' == clause_learning: self.add_learned_clause = self.simple_add_learned_clause self.compute_conflict = self.simple_compute_conflict self.update_functions.append(self.simple_clean_clauses) elif 'none' == clause_learning: self.add_learned_clause = lambda x: None self.compute_conflict = lambda: None else: raise NotImplementedError # Create the base level self.levels = [Level(0)] self.current_level.varsettings = var_settings # Keep stats self.num_decisions = 0 self.num_learned_clauses = 0 self.original_num_clauses = len(self.clauses) def initialize_variables(self, variables): """Set up the variable data structures needed.""" self.sentinels = {} self.occurrence_count = {} for i in xrange(1, len(variables)+1): self.sentinels[i] = set() self.sentinels[-i] = set() self.occurrence_count[i] = 0 self.occurrence_count[-i] = 0 self.variable_set = [False] * (len(variables) + 1) def initialize_clauses(self, clauses): """Set up the clause data structures needed. For each clause, the following changes are made: - Unit clauses are queued for propagation right away. - Non-unit clauses have their first and last literals set as sentinels. - The number of clauses a literal appears in is computed. """ self.clauses = [] for cls in clauses: self.clauses.append(list(cls)) for i in range(len(self.clauses)): # Handle the unit clauses if 1 == len(self.clauses[i]): self.unit_prop_queue.append(self.clauses[i][0]) continue self.sentinels[self.clauses[i][0]].add(i) self.sentinels[self.clauses[i][-1]].add(i) for lit in self.clauses[i]: self.occurrence_count[lit] += 1 def find_model(self): """Main DPLL loop. Variables are chosen successively, and assigned to be either True or False. If a solution is not found with this setting, the opposite is chosen and the search continues. The solver halts when every variable has a setting. """ # We use this variable to keep track of if we should flip a # variable setting in successive rounds flip_var = False # Check if unit prop says the theory is unsat right off the bat self.simplify() if self.is_unsatisfied: return False # While the theory still has clauses remaining while True: # Perform cleanup / fixup at regular intervals if self.num_decisions % self.INTERVAL == 0: for func in self.update_functions: func() if flip_var: # We have just backtracked and we are trying to opposite literal flip_var = False lit = self.current_level.decision else: # Pick a literal to set lit = self.heur_calculate() self.num_decisions += 1 # Stopping condition for a satisfying theory if 0 == lit: return True # Start the new decision level self.levels.append(Level(lit)) # Assign the literal, updating the clauses it satisfies self.assign_literal(lit) # Simplify the theory self.simplify() # Check if we've made the theory unsat if self.is_unsatisfied: self.is_unsatisfied = False # We unroll all of the decisions until we can flip a literal while self.current_level.flipped: self.undo() # If we've unrolled all the way, the theory is unsat if 1 == len(self.levels): return False # Detect and add a learned clause self.add_learned_clause(self.compute_conflict()) # Try the opposite setting of the most recent decision flip_lit = -self.current_level.decision self.undo() self.levels.append(Level(flip_lit, flipped = True)) flip_var = True ######################## # Helper Methods # ######################## @property def current_level(self): """The current decision level data structure""" return self.levels[-1] def clause_sat(self, cls): """Check if a clause is satisfied by the current variable setting.""" for lit in self.clauses[cls]: if lit in self.var_settings: return True return False def is_sentinel(self, lit, cls): """Check if a literal is a sentinel of a given clause.""" return cls in self.sentinels[lit] def assign_literal(self, lit): """Make a literal assignment. The literal assignment must be recorded as part of the current decision level. Additionally, if the literal is marked as a sentinel of any clause, then a new sentinel must be chosen. If this is not possible, then unit propagation is triggered and another literal is added to the queue to be set in the future. """ self.var_settings.add(lit) self.current_level.var_settings.add(lit) self.variable_set[abs(lit)] = True self.heur_lit_assigned(lit) sentinel_list = list(self.sentinels[-lit]) for cls in sentinel_list: if not self.clause_sat(cls): other_sentinel = None for newlit in self.clauses[cls]: if newlit != -lit: if self.is_sentinel(newlit, cls): other_sentinel = newlit elif not self.variable_set[abs(newlit)]: self.sentinels[-lit].remove(cls) self.sentinels[newlit].add(cls) other_sentinel = None break # Check if no sentinel update exists if other_sentinel: self.unit_prop_queue.append(other_sentinel) def undo(self): """ Undo the changes of the most recent decision level. """ # Undo the variable settings for lit in self.current_level.var_settings: self.var_settings.remove(lit) self.heur_lit_unset(lit) self.variable_set[abs(lit)] = False # Pop the level off the stack self.levels.pop() ######################### # Propagation # ######################### """ Propagation methods should attempt to soundly simplify the boolean theory, and return True if any simplification occurred and False otherwise. """ def simplify(self): """Iterate over the various forms of propagation to simplify the theory.""" changed = True while changed: changed = False changed |= self.unit_prop() changed |= self.pure_literal() def unit_prop(self): """Perform unit propagation on the current theory.""" result = len(self.unit_prop_queue) > 0 while self.unit_prop_queue: next_lit = self.unit_prop_queue.pop() if -next_lit in self.var_settings: self.is_unsatisfied = True self.unit_prop_queue = [] return False else: self.assign_literal(next_lit) return result def pure_literal(self): """Look for pure literals and assign them when found.""" return False ######################### # Heuristics # ######################### def vsids_init(self): """Initialize the data structures needed for the VSIDS heuristic.""" self.lit_heap = [] self.lit_scores = {} for var in range(1, len(self.variable_set)): self.lit_scores[var] = -float(self.occurrence_count[var]) self.lit_scores[-var] = -float(self.occurrence_count[-var]) heappush(self.lit_heap, (self.lit_scores[var], var)) heappush(self.lit_heap, (self.lit_scores[-var], -var)) def vsids_decay(self): """Decay the VSIDS scores for every literal.""" # We divide every literal score by 2 for a decay factor # Note: This doesn't change the heap property for lit in self.lit_scores.keys(): self.lit_scores[lit] /= 2.0 def vsids_calculate(self): """ VSIDS Heuristic Calculation """ if len(self.lit_heap) == 0: return 0 # Clean out the front of the heap as long the variables are set while self.variable_set[abs(self.lit_heap[0][1])]: heappop(self.lit_heap) if len(self.lit_heap) == 0: return 0 return heappop(self.lit_heap)[1] def vsids_lit_assigned(self, lit): """Handle the assignment of a literal for the VSIDS heuristic.""" pass def vsids_lit_unset(self, lit): """Handle the unsetting of a literal for the VSIDS heuristic.""" var = abs(lit) heappush(self.lit_heap, (self.lit_scores[var], var)) heappush(self.lit_heap, (self.lit_scores[-var], -var)) def vsids_clause_added(self, cls): """Handle the addition of a new clause for the VSIDS heuristic.""" self.num_learned_clauses += 1 for lit in cls: self.lit_scores[lit] += 1 ######################## # Clause Learning # ######################## def simple_add_learned_clause(self, cls): """Add a new clause to the theory.""" cls_num = len(self.clauses) self.clauses.append(cls) for lit in cls: self.occurrence_count[lit] += 1 self.sentinels[cls[0]].add(cls_num) self.sentinels[cls[-1]].add(cls_num) self.heur_clause_added(cls) def simple_compute_conflict(self): """ Build a clause representing the fact that at least one decision made so far is wrong. """ return [-(level.decision) for level in self.levels[1:]] def simple_clean_clauses(self): """Clean up learned clauses.""" pass class Level(object): """ Represents a single level in the DPLL algorithm, and contains enough information for a sound backtracking procedure. """ def __init__(self, decision, flipped = False): self.decision = decision self.var_settings = set() self.flipped = flipped wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/algorithms/__init__.py0000644000175000017500000000000012014170666026444 0ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/boolalg.py0000755000175000017500000002374112014170666024177 0ustar georgeskgeorgesk"""Boolean algebra module for SymPy""" from sympy.core.basic import Basic from sympy.core.operations import LatticeOp from sympy.core.function import Application, sympify class Boolean(Basic): """A boolean object is an object for which logic operations make sense.""" __slots__ = [] def __and__(self, other): """Overloading for & operator""" return And(self, other) def __or__(self, other): """Overloading for |""" return Or(self, other) def __invert__(self): """Overloading for ~""" return Not(self) def __rshift__(self, other): """Overloading for >>""" return Implies(self, other) def __lshift__(self, other): """Overloading for <<""" return Implies(other, self) def __xor__(self, other): return Xor(self, other) class BooleanFunction(Application, Boolean): """Boolean function is a function that lives in a boolean space It is used as base class for And, Or, Not, etc. """ is_Boolean = True def __call__(self, *args): return self.func(*[arg(*args) for arg in self.args]) class And(LatticeOp, BooleanFunction): """ Logical AND function. It evaluates its arguments in order, giving False immediately if any of them are False, and True if they are all True. Examples: >>> from sympy.core import symbols >>> from sympy.abc import x, y >>> x & y And(x, y) """ zero = False identity = True class Or(LatticeOp, BooleanFunction): """ Logical OR function It evaluates its arguments in order, giving True immediately if any of them are True, and False if they are all False. """ zero = True identity = False class Xor(BooleanFunction): """Logical XOR (exclusive OR) function. returns True if an odd number of the arguments are True, and the rest are False. returns False if an even number of the arguments are True, and the rest are False. """ @classmethod def eval(cls, *args): if not args: return False args = list(args) A = args.pop() while args: B = args.pop() A = Or(And(A, Not(B)), And(Not(A), B)) return A class Not(BooleanFunction): """Logical Not function (negation) Note: De Morgan rules applied automatically""" is_Not = True @classmethod def eval(cls, *args): if len(args) > 1: return map(cls, args) arg = args[0] if type(arg) is bool: return not arg # apply De Morgan Rules if arg.func is And: return Or(*[Not(a) for a in arg.args]) if arg.func is Or: return And(*[Not(a) for a in arg.args]) if arg.func is Not: return arg.args[0] class Nand(BooleanFunction): """Logical NAND function. It evaluates its arguments in order, giving True immediately if any of them are False, and False if they are all True. """ @classmethod def eval(cls, *args): return Not(And(*args)) class Nor(BooleanFunction): """Logical NOR function. It evaluates its arguments in order, giving False immediately if any of them are True, and True if they are all False. """ @classmethod def eval(cls, *args): return Not(Or(*args)) class Implies(BooleanFunction): """Logical implication. A implies B is equivalent to !A v B """ @classmethod def eval(cls, *args): try: A, B = args except ValueError: raise ValueError("%d operand(s) used for an Implies (pairs are required): %s" % (len(args), str(args))) if A is True or A is False or B is True or B is False: return Or(Not(A), B) else: return Basic.__new__(cls, *args) class Equivalent(BooleanFunction): """Equivalence relation. Equivalent(A, B) is True if and only if A and B are both True or both False """ @classmethod def eval(cls, *args): argset = set(args) if len(argset) <= 1: return True if True in argset: argset.discard(True) return And(*argset) if False in argset: argset.discard(False) return Nor(*argset) return Basic.__new__(cls, *set(args)) class ITE(BooleanFunction): """ If then else clause. ITE(A, B, C) evaluates and returns the result of B if A is true else it returns the result of C Example: >>> from sympy.logic.boolalg import ITE, And, Xor, Or >>> from sympy.abc import x,y,z >>> x = True >>> y = False >>> z = True >>> ITE(x,y,z) False >>> ITE(Or(x, y), And(x, z), Xor(z, x)) True """ @classmethod def eval(cls, *args): args = list(args) if len(args) == 3: return Or(And(args[0], args[1]), And(Not(args[0]), args[2])) raise ValueError("ITE expects 3 arguments, but got %d: %s" % (len(args), str(args))) ### end class definitions. Some useful methods def fuzzy_not(arg): """ Not in fuzzy logic will return Not if arg is a boolean value, and None if argument is None >>> from sympy.logic.boolalg import fuzzy_not >>> fuzzy_not(True) False >>> fuzzy_not(None) >>> fuzzy_not(False) True """ if arg is None: return return not arg def conjuncts(expr): """Return a list of the conjuncts in the expr s. >>> from sympy.logic.boolalg import conjuncts >>> from sympy.abc import A, B >>> conjuncts(A & B) frozenset([A, B]) >>> conjuncts(A | B) frozenset([Or(A, B)]) """ return And.make_args(expr) def disjuncts(expr): """Return a list of the disjuncts in the sentence s. >>> from sympy.logic.boolalg import disjuncts >>> from sympy.abc import A, B >>> disjuncts(A | B) frozenset([A, B]) >>> disjuncts(A & B) frozenset([And(A, B)]) """ return Or.make_args(expr) def distribute_and_over_or(expr): """ Given a sentence s consisting of conjunctions and disjunctions of literals, return an equivalent sentence in CNF. """ if expr.func is Or: for arg in expr.args: if arg.func is And: conj = arg break else: return expr rest = Or(*[a for a in expr.args if a is not conj]) return And(*map(distribute_and_over_or, [Or(c, rest) for c in conj.args])) elif expr.func is And: return And(*map(distribute_and_over_or, expr.args)) else: return expr def to_cnf(expr): """Convert a propositional logical sentence s to conjunctive normal form. That is, of the form ((A | ~B | ...) & (B | C | ...) & ...) Examples: >>> from sympy.logic.boolalg import to_cnf >>> from sympy.abc import A, B, D >>> to_cnf(~(A | B) | D) And(Or(D, Not(A)), Or(D, Not(B))) """ # Don't convert unless we have to if is_cnf(expr): return expr expr = sympify(expr) expr = eliminate_implications(expr) return distribute_and_over_or(expr) def is_cnf(expr): """Test whether or not an expression is in conjunctive normal form. Examples: >>> from sympy.logic.boolalg import is_cnf >>> from sympy.abc import A, B, C >>> is_cnf(A | B | C) True >>> is_cnf(A & B & C) True >>> is_cnf((A & B) | C) False """ expr = sympify(expr) # Special case of a single disjunction if expr.func is Or: for lit in expr.args: if lit.func is Not: if not lit.args[0].is_Atom: return False else: if not lit.is_Atom: return False return True # Special case of a single negation if expr.func is Not: if not expr.args[0].is_Atom: return False if not expr.func is And: return False for cls in expr.args: if cls.is_Atom: continue if cls.func is Not: if not cls.args[0].is_Atom: return False elif not cls.func is Or: return False for lit in cls.args: if lit.func is Not: if not lit.args[0].is_Atom: return False else: if not lit.is_Atom: return False return True def eliminate_implications(expr): """Change >>, <<, and Equivalent into &, |, and ~. That is, return an expression that is equivalent to s, but has only &, |, and ~ as logical operators. """ expr = sympify(expr) if expr.is_Atom: return expr ## (Atoms are unchanged.) args = map(eliminate_implications, expr.args) if expr.func is Implies: a, b = args[0], args[-1] return (~a) | b elif expr.func is Equivalent: a, b = args[0], args[-1] return (a | Not(b)) & (b | Not(a)) else: return expr.func(*args) def compile_rule(s): """Transforms a rule into a sympy expression A rule is a string of the form "symbol1 & symbol2 | ..." See sympy.assumptions.known_facts for examples of rules TODO: can this be replaced by sympify ? """ import re from sympy.core import Symbol return eval(re.sub(r'([a-zA-Z0-9_.]+)', r'Symbol("\1")', s), {'Symbol' : Symbol}) def to_int_repr(clauses, symbols): """ takes clauses in CNF puts them into integer representation Examples: >>> from sympy.logic.boolalg import to_int_repr >>> from sympy.abc import x, y >>> to_int_repr([x | y, y], [x, y]) == [set([1, 2]), set([2])] True """ # Convert the symbol list into a dict symbols = dict(zip(symbols, xrange(1, len(symbols) + 1))) def append_symbol(arg, symbols): if arg.func is Not: return -symbols[arg.args[0]] else: return symbols[arg] return [set(append_symbol(arg, symbols) for arg in Or.make_args(c)) \ for c in clauses] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/inference.py0000755000175000017500000001012012014170666024501 0ustar georgeskgeorgesk"""Inference in propositional logic""" from sympy.logic.boolalg import And, Or, Not, Implies, Equivalent, \ conjuncts, to_cnf from sympy.core.basic import C from sympy.core.sympify import sympify def literal_symbol(literal): """The symbol in this literal (without the negation). >>> from sympy import Symbol >>> from sympy.abc import A >>> from sympy.logic.inference import literal_symbol >>> literal_symbol(A) A >>> literal_symbol(~A) A """ if literal.func is Not: return literal.args[0] else: return literal def satisfiable(expr, algorithm="dpll2"): """Check satisfiability of a propositional sentence. Returns a model when it succeeds Examples >>> from sympy.abc import A, B >>> from sympy.logic.inference import satisfiable >>> satisfiable(A & ~B) {A: True, B: False} >>> satisfiable(A & ~A) False """ expr = to_cnf(expr) if algorithm == "dpll": from sympy.logic.algorithms.dpll import dpll_satisfiable return dpll_satisfiable(expr) elif algorithm == "dpll2": from sympy.logic.algorithms.dpll2 import dpll_satisfiable return dpll_satisfiable(expr) raise NotImplementedError def pl_true(expr, model={}): """Return True if the propositional logic expression is true in the model, and False if it is false. If the model does not specify the value for every proposition, this may return None to indicate 'not obvious'; this may happen even when the expression is tautological. The model is implemented as a dict containing the pair symbol, boolean value. Examples: >>> from sympy.abc import A, B >>> from sympy.logic.inference import pl_true >>> pl_true( A & B, {A: True, B : True}) True """ if isinstance(expr, bool): return expr expr = sympify(expr) if expr.is_Atom: return model.get(expr) args = expr.args if expr.func is Not: p = pl_true(args[0], model) if p is None: return None else: return not p elif expr.func is Or: result = False for arg in args: p = pl_true(arg, model) if p == True: return True if p == None: result = None return result elif expr.func is And: result = True for arg in args: p = pl_true(arg, model) if p == False: return False if p == None: result = None return result elif expr.func is Implies: p, q = args return pl_true(Or(Not(p), q), model) elif expr.func is Equivalent: p, q = args pt = pl_true(p, model) if pt == None: return None qt = pl_true(q, model) if qt == None: return None return pt == qt else: raise ValueError("Illegal operator in logic expression" + str(expr)) class KB(object): """Base class for all knowledge bases""" def __init__(self, sentence=None): self.clauses = [] if sentence: self.tell(sentence) def tell(self, sentence): raise NotImplementedError def ask(self, query): raise NotImplementedError def retract(self, sentence): raise NotImplementedError class PropKB(KB): "A KB for Propositional Logic. Inefficient, with no indexing." def tell(self, sentence): "Add the sentence's clauses to the KB" for c in conjuncts(to_cnf(sentence)): if not c in self.clauses: self.clauses.append(c) def ask(self, query): """TODO: examples""" if len(self.clauses) == 0: return False from sympy.logic.algorithms.dpll import dpll query_conjuncts = self.clauses[:] query_conjuncts.extend(conjuncts(to_cnf(query))) s = set() for q in query_conjuncts: s = s.union(q.atoms(C.Symbol)) return bool(dpll(query_conjuncts, list(s), {})) def retract(self, sentence): "Remove the sentence's clauses from the KB" for c in conjuncts(to_cnf(sentence)): if c in self.clauses: self.clauses.remove(c) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/utilities/0000755000175000017500000000000012014170666024207 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/utilities/dimacs.py0000644000175000017500000000247312014170666026027 0ustar georgeskgeorgesk"""For reading in DIMACS file format www.cs.ubc.ca/~hoos/SATLIB/Benchmarks/SAT/satformat.ps """ from sympy.core import Symbol from sympy.logic.boolalg import And, Or import re def load(s): clauses = [] lines = s.split('\n') pComment = re.compile('c.*') pStats = re.compile('p\s*cnf\s*(\d*)\s*(\d*)') numVars = 0 numClauses = 0 while len(lines) > 0: line = lines.pop(0) # Only deal with lines that aren't comments if not pComment.match(line): m = pStats.match(line) if m: numVars = int(m.group(1)) numClauses = int(m.group(2)) else: nums = line.rstrip('\n').split(' ') list = [] for lit in nums: if lit != '': if int(lit) == 0: continue num = abs(int(lit)) sign = True if int(lit) < 0: sign = False if sign: list.append(Symbol("cnf_%s" % num)) else: list.append(~Symbol("cnf_%s" % num)) if len(list) > 0: clauses.append(Or(*list)) return And(*clauses) def load_file(location): s = open(location).read() return load(s) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/utilities/__init__.py0000644000175000017500000000003612014170666026317 0ustar georgeskgeorgeskfrom dimacs import load_file wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/tests/0000755000175000017500000000000012014170666023336 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/tests/test_boolalg.py0000755000175000017500000002043412014170666026374 0ustar georgeskgeorgeskfrom sympy.logic.boolalg import to_cnf, eliminate_implications, distribute_and_over_or, \ compile_rule, conjuncts, disjuncts, to_int_repr, fuzzy_not, Boolean, is_cnf from sympy import symbols, And, Or, Xor, Not, Nand, Nor, Implies, Equivalent, ITE from sympy.utilities.pytest import raises, XFAIL def test_overloading(): """Test that |, & are overloaded as expected""" A, B, C = map(Boolean, symbols('A,B,C')) assert A & B == And(A, B) assert A | B == Or(A, B) assert (A & B) | C == Or(And(A, B), C) assert A >> B == Implies(A, B) assert A << B == Implies(B, A) assert ~A == Not(A) assert A ^ B == Xor(A, B) def test_And(): A, B, C = map(Boolean, symbols('A,B,C')) assert And() == True assert And(A) == A assert And(True) == True assert And(False) == False assert And(True, True ) == True assert And(True, False) == False assert And(False, False) == False assert And(True, A) == A assert And(False, A) == False assert And(True, True, True) == True assert And(True, True , A) == A assert And(True, False, A) == False def test_Or(): A, B, C = map(Boolean, symbols('A,B,C')) assert Or() == False assert Or(A) == A assert Or(True) == True assert Or(False) == False assert Or(True, True ) == True assert Or(True, False) == True assert Or(False, False) == False assert Or(True, A) == True assert Or(False, A) == A assert Or(True, False, False) == True assert Or(True, False, A) == True assert Or(False, False, A) == A def test_Xor(): A, B, C = map(Boolean, symbols('A,B,C')) assert Xor() == False assert Xor(A) == A assert Xor(True) == True assert Xor(False) == False assert Xor(True, True ) == False assert Xor(True, False) == True assert Xor(False, False) == False assert Xor(True, A) == ~A assert Xor(False, A) == A assert Xor(True, False, False) == True assert Xor(True, False, A) == ~A assert Xor(False, False, A) == A def test_Not(): assert Not(True) == False assert Not(False) == True assert Not(True, True ) == [False, False] assert Not(True, False) == [False, True ] assert Not(False,False) == [True, True ] def test_Nand(): A, B, C = map(Boolean, symbols('A,B,C')) assert Nand() == False assert Nand(A) == ~A assert Nand(True) == False assert Nand(False) == True assert Nand(True, True ) == False assert Nand(True, False) == True assert Nand(False, False) == True assert Nand(True, A) == ~A assert Nand(False, A) == True assert Nand(True, True, True) == False assert Nand(True, True , A) == ~A assert Nand(True, False, A) == True def test_Nor(): A, B, C = map(Boolean, symbols('A,B,C')) assert Nor() == True assert Nor(A) == ~A assert Nor(True) == False assert Nor(False) == True assert Nor(True, True ) == False assert Nor(True, False) == False assert Nor(False, False) == True assert Nor(True, A) == False assert Nor(False, A) == ~A assert Nor(True, True, True) == False assert Nor(True, True , A) == False assert Nor(True, False, A) == False def test_Implies(): A, B, C = map(Boolean, symbols('A,B,C')) raises(ValueError, "Implies(A,B,C)") assert Implies(True, True) == True assert Implies(True, False) == False assert Implies(False, True) == True assert Implies(False, False) == True assert A >> B == B << A def test_Equivalent(): A, B, C = map(Boolean, symbols('A,B,C')) assert Equivalent(A, B) == Equivalent(B, A) == Equivalent(A, B, A) assert Equivalent() == True assert Equivalent(A, A) == Equivalent(A) == True assert Equivalent(True, True) == Equivalent(False, False) == True assert Equivalent(True, False) == Equivalent(False, True) == False assert Equivalent(A, True) == A assert Equivalent(A, False) == Not(A) assert Equivalent(A, B, True) == A & B assert Equivalent(A, B, False) == ~A & ~B def test_bool_symbol(): """Test that mixing symbols with boolean values works as expected""" A, B, C = map(Boolean, symbols('A,B,C')) assert And(A, True) == A assert And(A, True, True) == A assert And(A, False) == False assert And(A, True, False) == False assert Or(A, True) == True assert Or(A, False) == A def test_subs(): A, B, C = map(Boolean, symbols('A,B,C')) assert (A & B).subs(A, True) == B assert (A & B).subs(A, False) == False assert (A & B).subs(B, True) == A assert (A & B).subs(B, False) == False assert (A & B).subs({A: True, B:True}) == True assert (A | B).subs(A, True) == True assert (A | B).subs(A, False) == B assert (A | B).subs(B, True) == True assert (A | B).subs(B, False) == A assert (A | B).subs({A: True, B:True}) == True """ we test for axioms of boolean algebra see http://en.wikipedia.org/wiki/Boolean_algebra_(structure) """ def test_commutative(): """Test for commutivity of And and Or""" A, B = map(Boolean, symbols('A,B')) assert A & B == B & A assert A | B == B | A def test_and_associativity(): """Test for associativity of And""" A, B, C = map(Boolean, symbols('A,B,C')) assert (A & B) & C == A & (B & C) def test_or_assicativity(): A, B, C = map(Boolean, symbols('A,B,C')) assert ((A | B) | C) == (A | (B | C)) def test_double_negation(): a = Boolean() assert ~(~a) == a def test_De_Morgan(): A, B, C = map(Boolean, symbols('A,B,C')) assert ~(A & B) == (~A) | (~B) assert ~(A | B) == (~A) & (~B) assert ~(A | B | C) == ~A & ~B & ~C # test methods def test_eliminate_implications(): A, B, C = map(Boolean, symbols('A,B,C')) assert eliminate_implications(Implies(A, B, evaluate=False)) == (~A) | B assert eliminate_implications(A >> (C >>Not(B))) == Or(Or(Not(B), Not(C)), Not(A)) def test_conjuncts(): A, B, C = map(Boolean, symbols('A,B,C')) assert conjuncts(A & B & C) == set([A, B, C]) assert conjuncts((A | B) & C) == set([A | B, C]) assert conjuncts(A) == set([A]) assert conjuncts(True) == set([True]) assert conjuncts(False) == set([False]) def test_disjuncts(): A, B, C = map(Boolean, symbols('A,B,C')) assert disjuncts(A | B | C) == set([A, B, C]) assert disjuncts((A | B) & C) == set([(A | B) & C]) assert disjuncts(A) == set([A]) assert disjuncts(True) == set([True]) assert disjuncts(False) == set([False]) def test_distribute(): A, B, C = map(Boolean, symbols('A,B,C')) assert distribute_and_over_or(Or(And(A, B), C)) == And(Or(A, C), Or(B, C)) def test_to_cnf(): A, B, C = map(Boolean, symbols('A,B,C')) assert to_cnf(~(B | C)) == And(Not(B), Not(C)) assert to_cnf((A & B) | C) == And(Or(A, C), Or(B, C)) assert to_cnf(A >> B) == (~A) | B assert to_cnf(A >> (B & C)) == (~A | B) & (~A | C) assert to_cnf(Equivalent(A, B)) == And(Or(A, Not(B)), Or(B, Not(A))) assert to_cnf(Equivalent(A, B & C)) == (~A | B) & (~A | C) & (~B | ~C | A) assert to_cnf(Equivalent(A, B | C)) == \ And(Or(Not(B), A), Or(Not(C), A), Or(B, C, Not(A))) def test_compile_rule(): from sympy import sympify assert compile_rule("A & B") == sympify("A & B") def test_to_int_repr(): x, y, z = map(Boolean, symbols('x,y,z')) def sorted_recursive(arg): try: return sorted(sorted_recursive(x) for x in arg) except TypeError: #arg is not a sequence return arg assert sorted_recursive(to_int_repr([x | y, z | x], [x, y, z])) == \ sorted_recursive([[1, 2], [1, 3]]) assert sorted_recursive(to_int_repr([x | y, z | ~x], [x, y, z])) == \ sorted_recursive([[1, 2], [3, -1]]) def test_is_cnf(): x, y, z = symbols('x,y,z') assert is_cnf(x | y | z) == True assert is_cnf(x & y & z) == True assert is_cnf((x | y) & z) == True assert is_cnf((x & y) | z) == False def test_ITE(): A, B, C = map(Boolean, symbols('A,B,C')) assert ITE(True, False, True) == False assert ITE(True, True, False) == True assert ITE(False, True, False) == False assert ITE(False, False, True) == True A = True assert ITE(A, B, C) == B A = False assert ITE(A, B, C) == C B = True assert ITE(And(A, B), B, C) == C assert ITE(Or(A, False), And(B, True), False) == False wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/tests/test_inference.py0000755000175000017500000001415512014170666026716 0ustar georgeskgeorgesk"""For more tests on satisfiability, see test_dimacs""" from sympy import symbols from sympy.logic.boolalg import Equivalent, Implies from sympy.logic.inference import pl_true, satisfiable, PropKB from sympy.logic.algorithms.dpll import dpll, dpll_satisfiable, \ find_pure_symbol, find_unit_clause, unit_propagate, \ find_pure_symbol_int_repr, find_unit_clause_int_repr, \ unit_propagate_int_repr from sympy.utilities.pytest import raises, XFAIL def test_find_pure_symbol(): A, B, C = symbols('A,B,C') assert find_pure_symbol([A], [A]) == (A, True) assert find_pure_symbol([A, B], [~A | B, ~B | A]) == (None, None) assert find_pure_symbol([A, B, C], [ A | ~B, ~B | ~C, C | A]) == (A, True) assert find_pure_symbol([A, B, C], [~A | B, B | ~C, C | A]) == (B, True) assert find_pure_symbol([A, B, C], [~A | ~B, ~B | ~C, C | A]) == (B, False) assert find_pure_symbol([A, B, C], [~A | B, ~B | ~C, C | A]) == (None, None) def test_find_pure_symbol_int_repr(): assert find_pure_symbol_int_repr([1], [set([1])]) == (1, True) assert find_pure_symbol_int_repr([1, 2], [set([-1, 2]), set([-2, 1])]) == (None, None) assert find_pure_symbol_int_repr([1, 2, 3], [set([1, -2]), set([-2, -3]), set([3, 1])]) == (1, True) assert find_pure_symbol_int_repr([1, 2, 3], [set([-1, 2]), set([2, -3]), set([3, 1])]) == (2, True) assert find_pure_symbol_int_repr([1, 2, 3], [set([-1, -2]), set([-2, -3]), set([3, 1])]) == (2, False) assert find_pure_symbol_int_repr([1, 2, 3], [set([-1, 2]), set([-2, -3]), set([3, 1])]) == (None, None) def test_unit_clause(): A, B, C = symbols('A,B,C') assert find_unit_clause([A], {}) == (A, True) assert find_unit_clause([A, ~A], {}) == (A, True) ### Wrong ?? assert find_unit_clause([A | B], {A: True}) == (B, True) assert find_unit_clause([A | B], {B: True}) == (A, True) assert find_unit_clause([A | B | C, B | ~C, A | ~B], {A:True}) == (B, False) assert find_unit_clause([A | B | C, B | ~C, A | B], {A:True}) == (B, True) assert find_unit_clause([A | B | C, B | ~C, A ], {}) == (A, True) def test_unit_clause_int_repr(): assert find_unit_clause_int_repr(map(set, [[1]]), {}) == (1, True) assert find_unit_clause_int_repr(map(set, [[1], [-1]]), {}) == (1, True) assert find_unit_clause_int_repr([set([1,2])], {1: True}) == (2, True) assert find_unit_clause_int_repr([set([1,2])], {2: True}) == (1, True) assert find_unit_clause_int_repr(map(set, [[1,2,3], [2, -3], [1, -2]]), {1: True}) == \ (2, False) assert find_unit_clause_int_repr(map(set, [[1, 2, 3], [3, -3], [1, 2]]), {1: True}) == \ (2, True) # assert find_unit_clause([A | B | C, B | ~C, A ], {}) == (A, True) def test_unit_propagate(): A, B, C = symbols('A,B,C') assert unit_propagate([A | B], A) == [] assert unit_propagate([A | B, ~A | C, ~C | B, A], A) == [C, ~C | B, A] def test_unit_propagate_int_repr(): assert unit_propagate_int_repr([set([1, 2])], 1) == [] assert unit_propagate_int_repr(map(set, [[1, 2], [-1, 3], [-3, 2], [1]]), 1) == \ [set([3]), set([-3, 2])] def test_dpll(): """This is also tested in test_dimacs""" A, B, C = symbols('A,B,C') assert dpll([A | B], [A, B], {A: True, B: True}) == {A: True, B: True} def test_dpll_satisfiable(): A, B, C = symbols('A,B,C') assert dpll_satisfiable( A & ~A ) == False assert dpll_satisfiable( A & ~B ) == {A: True, B: False} assert dpll_satisfiable( A | B ) in ({A: True}, {B: True}, {A: True, B: True}) assert dpll_satisfiable( (~A | B) & (~B | A) ) in ({A: True, B: True}, {A: False, B:False}) assert dpll_satisfiable( (A | B) & (~B | C) ) in ({A: True, B: False}, {A: True, C:True}) assert dpll_satisfiable( A & B & C ) == {A: True, B: True, C: True} assert dpll_satisfiable( (A | B) & (A >> B) ) == {B: True} assert dpll_satisfiable( Equivalent(A, B) & A ) == {A: True, B: True} assert dpll_satisfiable( Equivalent(A, B) & ~A ) == {A: False, B: False} def test_satisfiable(): A, B, C = symbols('A,B,C') assert satisfiable(A & (A >> B) & ~B) == False def test_pl_true(): A, B, C = symbols('A,B,C') assert pl_true(True) == True assert pl_true( A & B, {A : True, B : True}) == True assert pl_true( A | B, {A : True}) == True assert pl_true( A | B, {B : True}) == True assert pl_true( A | B, {A: None, B: True}) == True assert pl_true( A >> B, {A: False}) == True assert pl_true( A | B | ~C, {A: False, B: True, C: True}) == True assert pl_true(Equivalent(A, B), {A:False, B:False}) == True # test for false assert pl_true(False) == False assert pl_true( A & B, {A: False, B: False}) == False assert pl_true( A & B, {A: False}) == False assert pl_true( A & B, {B: False}) == False assert pl_true( A | B, {A: False, B: False}) == False #test for None assert pl_true(B, {B: None}) is None assert pl_true( A & B, {A: True, B: None}) is None assert pl_true( A >> B, {A: True, B: None}) is None assert pl_true(Equivalent(A, B), {A:None}) is None assert pl_true(Equivalent(A, B), {A:True, B:None}) is None def test_pl_true_wrong_input(): from sympy import pi raises(ValueError, "pl_true('John Cleese')") raises(ValueError, "pl_true(42+pi+pi**2)") #raises(ValueError, "pl_true(42)") #returns None, but should it? def test_PropKB(): A, B, C = symbols('A,B,C') kb = PropKB() kb.tell(A >> B) kb.tell(B >> C) assert kb.ask(A) == True assert kb.ask(B) == True assert kb.ask(C) == True assert kb.ask(~A) == True assert kb.ask(~B) == True assert kb.ask(~C) == True kb.tell(A) assert kb.ask(A) == True assert kb.ask(B) == True assert kb.ask(C) == True assert kb.ask(~C) == False kb.retract(A) assert kb.ask(~C) == True kb2 = PropKB(Equivalent(A, B)) assert kb2.ask(A) == True assert kb2.ask(B) == True kb2.tell(A) assert kb2.ask(A) == True kb3 = PropKB() kb3.tell(A) def test_propKB_tolerant(): """"tolerant to bad input""" kb = PropKB() A, B, C = symbols('A,B,C') assert kb.ask(B) == False wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/tests/test_dimacs.py0000644000175000017500000000737612014170666026224 0ustar georgeskgeorgesk"""Various tests on satisfiability using dimacs cnf file syntax You can find lots of cnf files in ftp://dimacs.rutgers.edu/pub/challenge/satisfiability/benchmarks/cnf/ """ from sympy.logic.utilities.dimacs import load from sympy.logic.algorithms.dpll import dpll_satisfiable from sympy.utilities.pytest import skip def test_f1(): assert bool(dpll_satisfiable(load(f1))) def test_f2(): assert bool(dpll_satisfiable(load(f2))) def test_f3(): assert bool(dpll_satisfiable(load(f3))) def test_f4(): assert not bool(dpll_satisfiable(load(f4))) def test_f5(): assert bool(dpll_satisfiable(load(f5))) f1 = """c simple example c Resolution: SATISFIABLE c p cnf 3 2 1 -3 0 2 3 -1 0 """ f2 = """c an example from Quinn's text, 16 variables and 18 clauses. c Resolution: SATISFIABLE c p cnf 16 18 1 2 0 -2 -4 0 3 4 0 -4 -5 0 5 -6 0 6 -7 0 6 7 0 7 -16 0 8 -9 0 -8 -14 0 9 10 0 9 -10 0 -10 -11 0 10 12 0 11 12 0 13 14 0 14 -15 0 15 16 0 """ f3 = """c p cnf 6 9 -1 0 -3 0 2 -1 0 2 -4 0 5 -4 0 -1 -3 0 -4 -6 0 1 3 -2 0 4 6 -2 -5 0 """ f4 = """c c c SOURCE: John Hooker (jh38+@andrew.cmu.edu) c c DESCRIPTION: Pigeon hole problem of placing n (for file holen) pigeons c in n+1 holes without placing 2 pigeons in the same hole c c NOTE: Part of the collection at the Forschungsinstitut fuer c anwendungsorientierte Wissensverarbeitung in Ulm Germany. c c NOTE: Not satisfiable c p cnf 42 133 -1 -7 0 -1 -13 0 -1 -19 0 -1 -25 0 -1 -31 0 -1 -37 0 -7 -13 0 -7 -19 0 -7 -25 0 -7 -31 0 -7 -37 0 -13 -19 0 -13 -25 0 -13 -31 0 -13 -37 0 -19 -25 0 -19 -31 0 -19 -37 0 -25 -31 0 -25 -37 0 -31 -37 0 -2 -8 0 -2 -14 0 -2 -20 0 -2 -26 0 -2 -32 0 -2 -38 0 -8 -14 0 -8 -20 0 -8 -26 0 -8 -32 0 -8 -38 0 -14 -20 0 -14 -26 0 -14 -32 0 -14 -38 0 -20 -26 0 -20 -32 0 -20 -38 0 -26 -32 0 -26 -38 0 -32 -38 0 -3 -9 0 -3 -15 0 -3 -21 0 -3 -27 0 -3 -33 0 -3 -39 0 -9 -15 0 -9 -21 0 -9 -27 0 -9 -33 0 -9 -39 0 -15 -21 0 -15 -27 0 -15 -33 0 -15 -39 0 -21 -27 0 -21 -33 0 -21 -39 0 -27 -33 0 -27 -39 0 -33 -39 0 -4 -10 0 -4 -16 0 -4 -22 0 -4 -28 0 -4 -34 0 -4 -40 0 -10 -16 0 -10 -22 0 -10 -28 0 -10 -34 0 -10 -40 0 -16 -22 0 -16 -28 0 -16 -34 0 -16 -40 0 -22 -28 0 -22 -34 0 -22 -40 0 -28 -34 0 -28 -40 0 -34 -40 0 -5 -11 0 -5 -17 0 -5 -23 0 -5 -29 0 -5 -35 0 -5 -41 0 -11 -17 0 -11 -23 0 -11 -29 0 -11 -35 0 -11 -41 0 -17 -23 0 -17 -29 0 -17 -35 0 -17 -41 0 -23 -29 0 -23 -35 0 -23 -41 0 -29 -35 0 -29 -41 0 -35 -41 0 -6 -12 0 -6 -18 0 -6 -24 0 -6 -30 0 -6 -36 0 -6 -42 0 -12 -18 0 -12 -24 0 -12 -30 0 -12 -36 0 -12 -42 0 -18 -24 0 -18 -30 0 -18 -36 0 -18 -42 0 -24 -30 0 -24 -36 0 -24 -42 0 -30 -36 0 -30 -42 0 -36 -42 0 6 5 4 3 2 1 0 12 11 10 9 8 7 0 18 17 16 15 14 13 0 24 23 22 21 20 19 0 30 29 28 27 26 25 0 36 35 34 33 32 31 0 42 41 40 39 38 37 0 """ f5 = """c simple example requiring variable selection c c NOTE: Satisfiable c p cnf 5 5 1 2 3 0 1 -2 3 0 4 5 -3 0 1 -4 -3 0 -1 -5 0 """ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/logic/__init__.py0000644000175000017500000000016512014170666024307 0ustar georgeskgeorgeskfrom boolalg import to_cnf, And, Or, Not, Xor, Nand, Nor, Implies, Equivalent, ITE from inference import satisfiable wxgeometrie-0.133.2.orig/wxgeometrie/sympy/abc.py0000644000175000017500000000063212014170666022177 0ustar georgeskgeorgeskfrom core import Symbol _latin = list('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') # COSINEQ should not be imported as they clash; gamma, pi and zeta clash, too _greek = 'alpha beta gamma delta epsilon zeta eta theta iota kappa '\ 'mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega'.split(' ') for _s in _latin + _greek: exec "%s = Symbol('%s')" % (_s, _s) del _latin, _greek, _s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/0000755000175000017500000000000012014170666022721 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/importtools.py0000644000175000017500000001311712014170666025671 0ustar georgeskgeorgesk"""Tools to assist importing optional external modules.""" import sys # Override these in the module to change the default warning behavior. # For example, you might set both to False before running the tests so that # warnings are not printed to the console, or set both to True for debugging. WARN_NOT_INSTALLED = False WARN_OLD_VERSION = True def __sympy_debug(): # helper function from sympy/__init__.py # We don't just import SYMPY_DEBUG from that file because we don't want to # import all of sympy just to use this module. import os return eval(os.getenv('SYMPY_DEBUG', 'False')) if __sympy_debug(): WARN_OLD_VERSION = True WARN_NOT_INSTALLED = True def import_module(module, min_module_version=None, min_python_version=None, warn_not_installed=None, warn_old_version=None, module_version_attr='__version__', module_version_attr_call_args=None, __import__kwargs={}): """ Import and return a module if it is installed. If the module is not installed, it returns None. A minimum version for the module can be given as the keyword argument min_module_version. This should be comparable against the module version. By default, module.__version__ is used to get the module version. To override this, set the module_version_attr keyword argument. If the attribute of the module to get the version should be called (e.g., module.version()), then set module_version_attr_call_args to the args such that module.module_version_attr(*module_version_attr_call_args) returns the module's version. If the module version is less than min_module_version using the Python < comparison, None will be returned, even if the module is installed. You can use this to keep from importing an incompatible older version of a module. You can also specify a minimum Python version by using the min_python_version keyword argument. This should be comparable against sys.version_info. If the keyword argument warn_not_installed is set to True, the function will emit a UserWarning when the module is not installed. If the keyword argument warn_old_version is set to True, the function will emit a UserWarning when the library is installed, but cannot be imported because of the min_module_version or min_python_version options. Note that because of the way warnings are handled, a warning will be emitted for each module only once. You can change the default warning behavior by overriding the values of WARN_NOT_INSTALLED and WARN_OLD_VERSION in sympy.external.importtools. By default, WARN_NOT_INSTALLED is False and WARN_OLD_VERSION is True. This function uses __import__() to import the module. To pass additional options to __import__(), use the __import__kwargs keyword argument. For example, to import a submodule A.B, you must pass a nonempty fromlist option to __import__. See the docstring of __import__(). **Example** >>> from sympy.external import import_module >>> numpy = import_module('numpy') >>> numpy = import_module('numpy', min_python_version=(2, 6), ... warn_old_version=False) >>> numpy = import_module('numpy', min_module_version='1.5', ... warn_old_version=False) # numpy.__version__ is a string >>> # gmpy does not have __version__, but it does have gmpy.version() >>> gmpy = import_module('gmpy', min_module_version='1.14', ... module_version_attr='version', module_version_attr_call_args=(), ... warn_old_version=False) >>> # To import a submodule, you must pass a nonempty fromlist to >>> # __import__(). The values do not matter. >>> p3 = import_module('mpl_toolkits.mplot3d', ... __import__kwargs={'fromlist':['something']}) """ if warn_old_version is None: warn_old_version = WARN_OLD_VERSION if warn_not_installed is None: warn_not_installed = WARN_NOT_INSTALLED import warnings # Check Python first so we don't waste time importing a module we can't use if min_python_version: if sys.version_info < min_python_version: if warn_old_version: warnings.warn("Python version is too old to use %s " "(%s or newer required)" % (module, '.'.join(map(str, min_python_version))), UserWarning) return try: mod = __import__(module, **__import__kwargs) except ImportError: installed = False else: installed = True if not installed: if warn_not_installed: warnings.warn("%s module is not installed" % module, UserWarning) return if min_module_version: modversion = getattr(mod, module_version_attr) if module_version_attr_call_args is not None: modversion = modversion(*module_version_attr_call_args) if modversion < min_module_version: if warn_old_version: # Attempt to create a pretty string version of the version if isinstance(min_module_version, basestring): verstr = min_module_version elif isinstance(min_module_version, (tuple, list)): verstr = '.'.join(map(str, min_module_version)) else: # Either don't know what this is. Hopefully # it's something that has a nice str version, like an int. verstr = str(min_module_version) warnings.warn("%s version is too old to use " "(%s or newer required)" % (module, verstr), UserWarning) return return mod wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/tests/0000755000175000017500000000000012014170666024063 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/tests/test_scipy.py0000644000175000017500000000216612014170666026630 0ustar georgeskgeorgesk# This testfile tests SymPy <-> SciPy compatibility # Don't test any SymPy features here. Just pure interaction with SciPy. # Always write regular SymPy tests for anything, that can be tested in pure # Python (without scipy). Here we test everything, that a user may need when # using SymPy with SciPy from sympy.external import import_module scipy = import_module('scipy') if not scipy: #py.test will not execute any tests now disabled = True from sympy import jn_zeros def eq(a, b, tol=1e-6): for x, y in zip(a, b): if not (abs(x-y) < tol): return False return True def test_jn_zeros(): assert eq(jn_zeros(0, 4, method="scipy"), [3.141592, 6.283185, 9.424777, 12.566370]) assert eq(jn_zeros(1, 4, method="scipy"), [4.493409, 7.725251, 10.904121, 14.066193]) assert eq(jn_zeros(2, 4, method="scipy"), [5.763459, 9.095011, 12.322940, 15.514603]) assert eq(jn_zeros(3, 4, method="scipy"), [6.987932, 10.417118, 13.698023, 16.923621]) assert eq(jn_zeros(4, 4, method="scipy"), [8.182561, 11.704907, 15.039664, 18.301255]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/tests/test_autowrap.py0000644000175000017500000001034312014170666027337 0ustar georgeskgeorgeskfrom sympy import symbols, Eq from sympy.external import import_module from sympy.tensor import IndexedBase, Idx from sympy.utilities.autowrap import autowrap, ufuncify, CodeWrapError from sympy.utilities.pytest import XFAIL, skip numpy = import_module('numpy') Cython = import_module('Cython') f2py = import_module('numpy.f2py', __import__kwargs={'fromlist':['f2py']}) f2pyworks = False if f2py: try: autowrap(symbols('x'), 'f95', 'f2py') except (CodeWrapError, ImportError): f2pyworks = False else: f2pyworks = True def has_module(module): """ Return True if module exists, otherwise run skip(). module should be a string. """ # To give a string of the module name to skip(), this function takes a # string. So we don't waste time running import_module() more than once, # just map the three modules tested here in this dict. modnames = {'numpy':numpy, 'Cython':Cython, 'f2py':f2py} if modnames[module]: if module == 'f2py' and not f2pyworks: skip("Couldn't run f2py.") return True skip("Couldn't import %s." % module) # # test runners used by several language-backend combinations # def runtest_autowrap_twice(language, backend): a, b, c = symbols('a b c') f = autowrap((((a + b)/c)**5).expand(), language, backend) g = autowrap((((a + b)/c)**4).expand(), language, backend) # check that autowrap updates the module name. Else, g gives the same as f assert f(1, -2, 1) == -1.0 assert g(1, -2, 1) == 1.0 def runtest_autowrap_trace(language, backend): has_module('numpy') A = IndexedBase('A') n = symbols('n', integer=True) i = Idx('i', n) trace = autowrap(A[i, i], language, backend) assert trace(numpy.eye(100)) == 100 def runtest_autowrap_matrix_vector(language, backend): has_module('numpy') A, x, y = map(IndexedBase, ['A', 'x', 'y']) n, m = symbols('n m', integer=True) i = Idx('i', m) j = Idx('j', n) expr = Eq(y[i], A[i, j]*x[j]) mv = autowrap(expr, language, backend) # compare with numpy's dot product M = numpy.random.rand(10, 20) x = numpy.random.rand(20) y = numpy.dot(M, x) assert numpy.sum(numpy.abs(y - mv(M, x))) < 1e-13 def runtest_autowrap_matrix_matrix(language, backend): has_module('numpy') A, B, C = map(IndexedBase, ['A', 'B', 'C']) n, m, d = symbols('n m d', integer=True) i = Idx('i', m) j = Idx('j', n) k = Idx('k', d) expr = Eq(C[i, j], A[i, k]*B[k, j]) matmat = autowrap(expr, language, backend) # compare with numpy's dot product M1 = numpy.random.rand(10, 20) M2 = numpy.random.rand(20, 15) M3 = numpy.dot(M1, M2) assert numpy.sum(numpy.abs(M3 - matmat(M1, M2))) < 1e-13 def runtest_ufuncify(language, backend): has_module('numpy') a, b, c = symbols('a b c') f = ufuncify([a, b, c], a*b + c, language=language, backend=backend) grid = numpy.linspace(-2, 2, 50) for b in numpy.linspace(-5, 4, 3): for c in numpy.linspace(-1, 1, 3): expected = grid*b + c assert numpy.sum(numpy.abs(expected - f(grid, b, c))) < 1e-13 # # tests of language-backend combinations # # f2py def test_wrap_twice_f95_f2py(): has_module('f2py') runtest_autowrap_twice('f95', 'f2py') def test_autowrap_trace_f95_f2py(): has_module('f2py') runtest_autowrap_trace('f95', 'f2py') def test_autowrap_matrix_vector_f95_f2py(): has_module('f2py') runtest_autowrap_matrix_vector('f95', 'f2py') def test_autowrap_matrix_matrix_f95_f2py(): has_module('f2py') runtest_autowrap_matrix_matrix('f95', 'f2py') def test_ufuncify_f95_f2py(): has_module('f2py') runtest_ufuncify('f95', 'f2py') # Cython def test_wrap_twice_c_cython(): has_module('Cython') runtest_autowrap_twice('C', 'cython') @XFAIL def test_autowrap_trace_C_Cython(): has_module('Cython') runtest_autowrap_trace('C', 'cython') @XFAIL def test_autowrap_matrix_vector_C_cython(): has_module('Cython') runtest_autowrap_matrix_vector('C', 'cython') @XFAIL def test_autowrap_matrix_matrix_C_cython(): has_module('Cython') runtest_autowrap_matrix_matrix('C', 'cython') @XFAIL def test_ufuncify_C_Cython(): has_module('Cython') runtest_ufuncify('C', 'cython') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/tests/test_sage.py0000644000175000017500000001134612014170666026420 0ustar georgeskgeorgesk# This testfile tests SymPy <-> Sage compatibility # # Execute this test inside Sage, e.g. with: # sage -python bin/test sympy/external/tests/test_sage.py # # This file can be tested by Sage itself by: # sage -t sympy/external/tests/test_sage.py # and if all tests pass, it should be copied (verbatim) to Sage, so that it is # automatically doctested by Sage. Note that this second method imports the # version of SymPy in Sage, whereas the -python method imports the local version # of SymPy (both use the local version of the tests, however). # # Don't test any SymPy features here. Just pure interaction with Sage. # Always write regular SymPy tests for anything, that can be tested in pure # Python (without Sage). Here we test everything, that a user may need when # using SymPy with Sage. import os import re import sys from sympy.external import import_module sage = import_module('sage.all', __import__kwargs={'fromlist':['all']}) if not sage: #py.test will not execute any tests now disabled = True import sympy from sympy.utilities.pytest import XFAIL def check_expression(expr, var_symbols): """Does eval(expr) both in Sage and SymPy and does other checks.""" # evaluate the expression in the context of Sage: sage.var(var_symbols) a = globals().copy() # safety checks... assert not "sin" in a a.update(sage.__dict__) assert "sin" in a e_sage = eval(expr, a) assert not isinstance(e_sage, sympy.Basic) # evaluate the expression in the context of SymPy: if var_symbols: sympy.var(var_symbols) b = globals().copy() assert not "sin" in b b.update(sympy.__dict__) assert "sin" in b b.update(sympy.__dict__) e_sympy = eval(expr, b) assert isinstance(e_sympy, sympy.Basic) # Do the actual checks: assert sympy.S(e_sage) == e_sympy assert e_sage == sage.SR(e_sympy) def test_basics(): check_expression("x", "x") check_expression("x**2", "x") check_expression("x**2+y**3", "x y") check_expression("1/(x+y)**2-x**3/4", "x y") def test_complex(): check_expression("I", "") check_expression("23+I*4", "x") @XFAIL def test_complex_fail(): # Sage doesn't properly implement _sympy_ on I check_expression("I*y", "y") check_expression("x+I*y", "x y") def test_integer(): check_expression("4*x", "x") check_expression("-4*x", "x") def test_real(): check_expression("1.123*x", "x") check_expression("-18.22*x", "x") def test_E(): assert sympy.sympify(sage.e) == sympy.E assert sage.e == sage.SR(sympy.E) def test_pi(): assert sympy.sympify(sage.pi) == sympy.pi assert sage.pi == sage.SR(sympy.pi) def test_euler_gamma(): assert sympy.sympify(sage.euler_gamma) == sympy.EulerGamma assert sage.euler_gamma == sage.SR(sympy.EulerGamma) def test_oo(): assert sympy.sympify(sage.oo) == sympy.oo assert sage.oo == sage.SR(sympy.oo) assert sympy.sympify(-sage.oo) == -sympy.oo assert -sage.oo == sage.SR(-sympy.oo) def test_NaN(): assert sympy.sympify(sage.NaN) == sympy.nan assert sage.NaN == sage.SR(sympy.nan) def test_Catalan(): assert sympy.sympify(sage.catalan) == sympy.Catalan assert sage.catalan == sage.SR(sympy.Catalan) def test_GoldenRation(): assert sympy.sympify(sage.golden_ratio) == sympy.GoldenRatio assert sage.golden_ratio == sage.SR(sympy.GoldenRatio) def test_functions(): check_expression("sin(x)", "x") check_expression("cos(x)", "x") check_expression("tan(x)", "x") check_expression("cot(x)", "x") check_expression("asin(x)", "x") check_expression("acos(x)", "x") check_expression("atan(x)", "x") check_expression("atan2(y, x)", "x, y") check_expression("acot(x)", "x") check_expression("sinh(x)", "x") check_expression("cosh(x)", "x") check_expression("tanh(x)", "x") check_expression("coth(x)", "x") check_expression("asinh(x)", "x") check_expression("acosh(x)", "x") check_expression("atanh(x)", "x") check_expression("acoth(x)", "x") check_expression("exp(x)", "x") check_expression("log(x)", "x") def test_issue924(): sage.var("a x") log = sage.log i = sympy.integrate(log(x)/a, (x, a, a+1)) i2 = sympy.simplify(i) s = sage.SR(i2) assert s == (a*log(1+a) - a*log(a) + log(1+a) - 1)/a # This string contains Sage doctests, that execute all the functions above. # When you add a new function, please add it here as well. """ TESTS:: sage: test_basics() sage: test_basics() sage: test_complex() sage: test_integer() sage: test_real() sage: test_E() sage: test_pi() sage: test_euler_gamma() sage: test_oo() sage: test_NaN() sage: test_Catalan() sage: test_GoldenRation() sage: test_functions() sage: test_issue924() """ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/tests/test_codegen.py0000644000175000017500000002723612014170666027112 0ustar georgeskgeorgesk# This tests the compilation and execution of the source code generated with # utilities.codegen. The compilation takes place in a temporary directory that # is removed after the test. By default the test directory is always removed, # but this behavior can be changed by setting the environment variable # SYMPY_TEST_CLEAN_TEMP to: # export SYMPY_TEST_CLEAN_TEMP=always : the default behavior. # export SYMPY_TEST_CLEAN_TEMP=success : only remove the directories of working tests. # export SYMPY_TEST_CLEAN_TEMP=never : never remove the directories with the test code. # When a directory is not removed, the necessary information is printed on # screen to find the files that belong to the (failed) tests. If a test does # not fail, py.test captures all the output and you will not see the directories # corresponding to the successful tests. Use the --nocapture option to see all # the output. # All tests below have a counterpart in utilities/test/test_codegen.py. In the # latter file, the resulting code is compared with predefined strings, without # compilation or execution. # All the generated Fortran code should conform with the Fortran 95 standard, # and all the generated C code should be ANSI C, which facilitates the # incorporation in various projects. The tests below assume that the binary cc # is somewhere in the path and that it can compile ANSI C code. from sympy import symbols from sympy.utilities.pytest import skip from sympy.utilities.codegen import( codegen, Routine, InputArgument, Result, get_code_generator ) import sys import os import tempfile import subprocess # templates for the main program that will test the generated code. main_template = {} main_template['F95'] = """ program main include "codegen.h" integer :: result; result = 0 %(statements)s call exit(result) end program """ main_template['C'] = """ #include "codegen.h" #include #include int main() { int result = 0; %(statements)s return result; } """ # templates for the numerical tests numerical_test_template = {} numerical_test_template['C'] = """ if (fabs(%(call)s)>%(threshold)s) { printf("Numerical validation failed: %(call)s=%%e threshold=%(threshold)s\\n", %(call)s); result = -1; } """ numerical_test_template['F95'] = """ if (abs(%(call)s)>%(threshold)s) then write(6,"('Numerical validation failed:')") write(6,"('%(call)s=',e15.5,'threshold=',e15.5)") %(call)s, %(threshold)s result = -1; end if """ # command sequences for supported compilers compile_commands = {} compile_commands['cc'] = [ "cc -c codegen.c -o codegen.o", "cc -c main.c -o main.o", "cc main.o codegen.o -lm -o test.exe" ] compile_commands['gfortran'] = [ "gfortran -c codegen.f90 -o codegen.o", "gfortran -ffree-line-length-none -c main.f90 -o main.o", "gfortran main.o codegen.o -o test.exe" ] compile_commands['g95'] = [ "g95 -c codegen.f90 -o codegen.o", "g95 -ffree-line-length-huge -c main.f90 -o main.o", "g95 main.o codegen.o -o test.exe" ] compile_commands['ifort'] = [ "ifort -c codegen.f90 -o codegen.o", "ifort -c main.f90 -o main.o", "ifort main.o codegen.o -o test.exe" ] combinations_lang_compiler = [ ('C', 'cc'), ('F95', 'ifort'), ('F95', 'gfortran'), ('F95', 'g95') ] def try_run(commands): """Run a series of commands and only return True if all ran fine.""" null = open(os.devnull, 'w') for command in commands: retcode = subprocess.call(command, stdout=null, shell=True, stderr=subprocess.STDOUT) if retcode != 0: return False return True def run_test(label, routines, numerical_tests, language, commands, friendly=True): """A driver for the codegen tests. This driver assumes that a compiler ifort is present in the PATH and that ifort is (at least) a Fortran 90 compiler. The generated code is written in a temporary directory, together with a main program that validates the generated code. The test passes when the compilation and the validation run correctly. """ # Check input arguments before touching the file system language = language.upper() assert language in main_template assert language in numerical_test_template # Check that evironment variable makes sense clean = os.getenv('SYMPY_TEST_CLEAN_TEMP', 'always').lower() if clean not in ('always', 'success', 'never'): raise ValueError("SYMPY_TEST_CLEAN_TEMP must be one of the following: 'always', 'success' or 'never'.") # Do all the magic to compile, run and validate the test code # 1) prepare the temporary working directory, switch to that dir work = tempfile.mkdtemp("_sympy_%s_test" % language, "%s_" % label) oldwork = os.getcwd() os.chdir(work) # 2) write the generated code if friendly: # interpret the routines as a name_expr list and call the friendly # function codegen codegen(routines, language, "codegen", to_files=True) else: code_gen = get_code_generator(language, "codegen") code_gen.write(routines, "codegen", to_files=True) # 3) write a simple main program that links to the generated code, and that # includes the numerical tests test_strings = [] for fn_name, args, expected, threshold in numerical_tests: call_string = "%s(%s)-(%s)" % (fn_name, ",".join(str(arg) for arg in args), expected) if language == "F95": call_string = fortranize_double_constants(call_string) threshold = fortranize_double_constants(str(threshold)) test_strings.append(numerical_test_template[language] % { "call": call_string, "threshold": threshold, }) if language == "F95": f = open("main.f90", "w") elif language == "C": f = open("main.c", "w") else: raise NotImplemented( "FIXME: filename extension unknown for language: %s"%language) f.write(main_template[language] % {'statements': "".join(test_strings)}) f.close() # 4) Compile and link compiled = try_run(commands) # 5) Run if compiled if compiled: executed = try_run(["./test.exe"]) else: executed = False # 6) Clean up stuff if clean == 'always' or (clean == 'success' and compiled and executed): def safe_remove(filename): if os.path.isfile(filename): os.remove(filename) safe_remove("codegen.f90") safe_remove("codegen.c") safe_remove("codegen.h") safe_remove("codegen.o") safe_remove("main.f90") safe_remove("main.c") safe_remove("main.o") safe_remove("test.exe") os.chdir(oldwork) os.rmdir(work) else: print >> sys.stderr, "TEST NOT REMOVED: %s" % work os.chdir(oldwork) # 7) Do the assertions in the end assert compiled, "failed to compile %s code with:\n%s" %(language, "\n".join(commands)) assert executed, "failed to execute %s code from:\n%s" %(language, "\n".join(commands)) def fortranize_double_constants(code_string): """ Replaces every literal float with literal doubles """ import re pattern_exp = re.compile('\d+(\.)?\d*[eE]-?\d+') pattern_float = re.compile('\d+\.\d*(?!\d*d)') def subs_exp(matchobj): return re.sub('[eE]','d',matchobj.group(0)) def subs_float(matchobj): return "%sd0" % matchobj.group(0) code_string = pattern_exp.sub(subs_exp, code_string) code_string = pattern_float.sub(subs_float, code_string) return code_string def is_feasible(language, commands): # This test should always work, otherwise the compiler is not present. x = symbols('x') routine = Routine("test", x) numerical_tests = [ ("test", ( 1.0,), 1.0, 1e-15), ("test", (-1.0,), -1.0, 1e-15), ] try: run_test("is_feasible", [routine], numerical_tests, language, commands, friendly=False) return True except AssertionError: return False valid_lang_commands = [] invalid_lang_compilers = [] for lang, compiler in combinations_lang_compiler: commands = compile_commands[compiler] if is_feasible(lang, commands): valid_lang_commands.append((lang, commands)) else: invalid_lang_compilers.append((lang, compiler)) # We test all language-compiler combinations, just to report what is skipped def test_C_cc(): if ("C", 'cc') in invalid_lang_compilers: skip("`cc' command didn't work as expected") def test_F95_ifort(): if ("F95", 'ifort') in invalid_lang_compilers: skip("`ifort' command didn't work as expected") def test_F95_gfortran(): if ("F95", 'gfortran') in invalid_lang_compilers: skip("`gfortran' command didn't work as expected") def test_F95_g95(): if ("F95", 'g95') in invalid_lang_compilers: skip("`g95' command didn't work as expected") # Here comes the actual tests def test_basic_codegen(): x,y,z = symbols('x,y,z') numerical_tests = [ ("test", (1.0, 6.0, 3.0), 21.0, 1e-15), ("test", (-1.0, 2.0, -2.5), -2.5, 1e-15), ] name_expr = [("test", (x+y)*z)] for lang, commands in valid_lang_commands: run_test("basic_codegen", name_expr, numerical_tests, lang, commands) def test_intrinsic_math1_codegen(): # not included: log10 from sympy import acos, asin, atan, ceiling, cos, cosh, floor, log, ln, \ sin, sinh, sqrt, tan, tanh, N x = symbols('x') name_expr = [ ("test_fabs", abs(x)), ("test_acos", acos(x)), ("test_asin", asin(x)), ("test_atan", atan(x)), ("test_cos", cos(x)), ("test_cosh", cosh(x)), ("test_log", log(x)), ("test_ln", ln(x)), ("test_sin", sin(x)), ("test_sinh", sinh(x)), ("test_sqrt", sqrt(x)), ("test_tan", tan(x)), ("test_tanh", tanh(x)), ] numerical_tests = [] for name, expr in name_expr: for xval in 0.2, 0.5, 0.8: expected = N(expr.subs(x, xval)) numerical_tests.append((name, (xval,), expected, 1e-14)) for lang, commands in valid_lang_commands: if lang == "C": name_expr_C = [("test_floor", floor(x)), ("test_ceil", ceiling(x))] else: name_expr_C = [] run_test("intrinsic_math1", name_expr + name_expr_C, numerical_tests, lang, commands) def test_instrinsic_math2_codegen(): # not included: frexp, ldexp, modf, fmod from sympy import atan2, N x, y = symbols('x,y') name_expr = [ ("test_atan2", atan2(x,y)), ("test_pow", x**y), ] numerical_tests = [] for name, expr in name_expr: for xval,yval in (0.2, 1.3), (0.5, -0.2), (0.8, 0.8): expected = N(expr.subs(x, xval).subs(y, yval)) numerical_tests.append((name, (xval,yval), expected, 1e-14)) for lang, commands in valid_lang_commands: run_test("intrinsic_math2", name_expr, numerical_tests, lang, commands) def test_complicated_codegen(): from sympy import sin, cos, tan, N x,y,z = symbols('x,y,z') name_expr = [ ("test1", ((sin(x)+cos(y)+tan(z))**7).expand()), ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x+y+z))))))))), ] numerical_tests = [] for name, expr in name_expr: for xval,yval,zval in (0.2, 1.3, -0.3), (0.5, -0.2, 0.0), (0.8, 2.1, 0.8): expected = N(expr.subs(x, xval).subs(y, yval).subs(z, zval)) numerical_tests.append((name, (xval,yval,zval), expected, 1e-12)) for lang, commands in valid_lang_commands: run_test("complicated_codegen", name_expr, numerical_tests, lang, commands) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/tests/test_numpy.py0000644000175000017500000002121312014170666026643 0ustar georgeskgeorgesk# This testfile tests SymPy <-> NumPy compatibility # Don't test any SymPy features here. Just pure interaction with NumPy. # Always write regular SymPy tests for anything, that can be tested in pure # Python (without numpy). Here we test everything, that a user may need when # using SymPy with NumPy from __future__ import division from sympy.external import import_module numpy = import_module('numpy') if numpy: array, matrix, ndarray = numpy.array, numpy.matrix, numpy.ndarray else: #py.test will not execute any tests now disabled = True from sympy import (Rational, Symbol, list2numpy, sin, Float, Matrix, lambdify, symarray, symbols, Integer) import sympy from sympy import mpmath # first, systematically check, that all operations are implemented and don't # raise and exception def test_systematic_basic(): def s(sympy_object, numpy_array): x = sympy_object + numpy_array x = numpy_array + sympy_object x = sympy_object - numpy_array x = numpy_array - sympy_object x = sympy_object * numpy_array x = numpy_array * sympy_object x = sympy_object / numpy_array x = numpy_array / sympy_object x = sympy_object ** numpy_array x = numpy_array ** sympy_object x = Symbol("x") y = Symbol("y") sympy_objs = [ Rational(2, 3), Float("1.3"), x, y, pow(x,y)*y, Integer(5), Float(5.5), ] numpy_objs = [ array([1]), array([3, 8, -1]), array([x, x**2, Rational(5)]), array([x/y*sin(y), 5, Rational(5)]), ] for x in sympy_objs: for y in numpy_objs: s(x,y) # now some random tests, that test particular problems and that also # check that the results of the operations are correct def test_basics(): one = Rational(1) zero = Rational(0) x = Symbol("x") assert array(1) == array(one) assert array([one]) == array([one]) assert array([x]) == array([x]) assert array(x) == array(Symbol("x")) assert array(one+x) == array(1+x) X = array([one, zero, zero]) assert (X == array([one, zero, zero])).all() assert (X == array([one, 0, 0])).all() def test_arrays(): one = Rational(1) zero = Rational(0) X = array([one, zero, zero]) Y = one*X X = array([Symbol("a")+Rational(1,2)]) Y = X+X assert Y == array([1+2*Symbol("a")]) Y = Y + 1 assert Y == array([2+2*Symbol("a")]) Y = X-X assert Y == array([0]) def test_conversion1(): x = Symbol("x") a = list2numpy([x**2, x]) #looks like an array? assert isinstance(a, ndarray) assert a[0] == x**2 assert a[1] == x assert len(a) == 2 #yes, it's the array def test_conversion2(): x = Symbol("x") a = 2*list2numpy([x**2, x]) b = list2numpy([2*x**2, 2*x]) assert (a == b).all() one = Rational(1) zero = Rational(0) X = list2numpy([one, zero, zero]) Y = one*X X = list2numpy([Symbol("a")+Rational(1,2)]) Y = X+X assert Y == array([1+2*Symbol("a")]) Y = Y + 1 assert Y == array([2+2*Symbol("a")]) Y = X-X assert Y == array([0]) def test_list2numpy(): x = Symbol("x") assert (array([x**2, x]) == list2numpy([x**2, x])).all() def test_Matrix1(): x = Symbol("x") m = Matrix([[x, x**2], [5, 2/x]]) assert (array(m.subs(x, 2)) == array([[2, 4],[5, 1]])).all() m = Matrix([[sin(x), x**2], [5, 2/x]]) assert (array(m.subs(x, 2)) == array([[sin(2), 4],[5, 1]])).all() def test_Matrix2(): x = Symbol("x") m = Matrix([[x, x**2], [5, 2/x]]) assert (matrix(m.subs(x, 2)) == matrix([[2, 4],[5, 1]])).all() m = Matrix([[sin(x), x**2], [5, 2/x]]) assert (matrix(m.subs(x, 2)) == matrix([[sin(2), 4],[5, 1]])).all() def test_Matrix3(): x = Symbol("x") a = array([[2, 4],[5, 1]]) assert Matrix(a) == Matrix([[2, 4], [5, 1]]) assert Matrix(a) != Matrix([[2, 4], [5, 2]]) a = array([[sin(2), 4], [5, 1]]) assert Matrix(a) == Matrix([[sin(2), 4],[5, 1]]) assert Matrix(a) != Matrix([[sin(0), 4],[5, 1]]) def test_Matrix4(): x = Symbol("x") a = matrix([[2, 4],[5, 1]]) assert Matrix(a) == Matrix([[2, 4], [5, 1]]) assert Matrix(a) != Matrix([[2, 4], [5, 2]]) a = matrix([[sin(2), 4], [5, 1]]) assert Matrix(a) == Matrix([[sin(2), 4],[5, 1]]) assert Matrix(a) != Matrix([[sin(0), 4],[5, 1]]) def test_Matrix_sum(): x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix([[1,2,3],[x,y,x],[2*y,-50,z*x]]) m = matrix([[2,3,4],[x,5,6],[x,y,z**2]]) assert M+m == Matrix([[3,5,7],[2*x,y+5,x+6],[2*y+x,y-50,z*x+z**2]]) assert m+M == Matrix([[3,5,7],[2*x,y+5,x+6],[2*y+x,y-50,z*x+z**2]]) assert M+m == M.add(m) def test_Matrix_mul(): x, y, z = Symbol('x'), Symbol('y'), Symbol('z') M = Matrix([[1,2,3],[x,y,x]]) m = matrix([[2,4],[x,6],[x,z**2]]) assert M*m == Matrix([ [ 2 + 5*x, 16 + 3*z**2], [2*x + x*y + x**2, 4*x + 6*y + x*z**2], ]) assert m*M == Matrix([ [ 2 + 4*x, 4 + 4*y, 6 + 4*x], [ 7*x, 2*x + 6*y, 9*x], [x + x*z**2, 2*x + y*z**2, 3*x + x*z**2], ]) a = array([2]) assert a[0] * M == 2 * M assert M * a[0] == 2 * M def test_Matrix_array(): class matarray(object): def __array__(self): from numpy import array return array([[1,2,3],[4,5,6],[7,8,9]]) matarr = matarray() assert Matrix(matarr) == Matrix([[1,2,3],[4,5,6],[7,8,9]]) def test_issue629(): x = Symbol("x") assert (Rational(1,2)*array([2*x, 0]) == array([x, 0])).all() assert (Rational(1,2) + array([2*x, 0]) == array([2*x + Rational(1,2), Rational(1,2)])).all() assert (Float("0.5")*array([2*x, 0]) == array([Float("1.0")*x, 0])).all() assert (Float("0.5") + array([2*x, 0]) == array([2*x + Float("0.5"), Float("0.5")])).all() def test_lambdify(): dps = mpmath.mp.dps try: mpmath.mp.dps = 16 sin02 = mpmath.mpf("0.198669330795061215459412627") x = Symbol("x") f = lambdify(x, sin(x), "numpy") prec = 1e-15 assert -prec < f(0.2) - sin02 < prec try: f(x) # if this succeeds, it can't be a numpy function assert False except AttributeError: pass finally: mpmath.mp.dps = dps def test_lambdify_matrix(): x = Symbol("x") f = lambdify(x, Matrix([[x, 2*x],[1, 2]]), "numpy") assert (f(1) == matrix([[1,2],[1,2]])).all() def test_lambdify_matrix_multi_input(): x,y,z = symbols('x,y,z') M=sympy.Matrix([[x**2, x*y, x*z], [y*x, y**2, y*z], [z*x, z*y, z**2]]) f = lambdify((x,y,z), M, "numpy") xh,yh,zh = 1.0, 2.0, 3.0 expected = matrix([[xh**2, xh*yh, xh*zh], [yh*xh, yh**2, yh*zh], [zh*xh, zh*yh, zh**2]]) actual = f(xh,yh,zh) assert numpy.allclose(actual,expected) def test_lambdify_matrix_vec_input(): X=sympy.DeferredVector('X') M=Matrix([[X[0]**2, X[0]*X[1], X[0]*X[2]], [X[1]*X[0], X[1]**2, X[1]*X[2]], [X[2]*X[0], X[2]*X[1], X[2]**2]]) f = lambdify(X, M, "numpy") Xh = array([1.0, 2.0, 3.0]) expected = matrix([[Xh[0]**2, Xh[0]*Xh[1], Xh[0]*Xh[2]], [Xh[1]*Xh[0], Xh[1]**2, Xh[1]*Xh[2]], [Xh[2]*Xh[0], Xh[2]*Xh[1], Xh[2]**2]]) actual = f(Xh) assert numpy.allclose(actual,expected) def test_lambdify_transl(): from sympy.utilities.lambdify import NUMPY_TRANSLATIONS for sym, mat in NUMPY_TRANSLATIONS.iteritems(): assert sym in sympy.__dict__ assert mat in numpy.__dict__ def test_symarray(): """Test creation of numpy arrays of sympy symbols.""" import numpy as np import numpy.testing as npt syms = symbols('_0,_1,_2') s1 = symarray("", 3) s2 = symarray("", 3) npt.assert_array_equal (s1, np.array(syms, dtype=object)) assert s1[0] is s2[0] a = symarray('a', 3) b = symarray('b', 3) assert not(a[0] is b[0]) asyms = symbols('a_0,a_1,a_2') npt.assert_array_equal (a, np.array(asyms, dtype=object)) # Multidimensional checks a2d = symarray('a', (2,3)) assert a2d.shape == (2,3) a00, a12 = symbols('a_0_0,a_1_2') assert a2d[0,0] is a00 assert a2d[1,2] is a12 a3d = symarray('a', (2,3,2)) assert a3d.shape == (2,3,2) a000, a120, a121 = symbols('a_0_0_0,a_1_2_0,a_1_2_1') assert a3d[0,0,0] is a000 assert a3d[1,2,0] is a120 assert a3d[1,2,1] is a121 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/external/__init__.py0000644000175000017500000000102712014170666025032 0ustar georgeskgeorgesk""" Unified place for determining if external dependencies are installed or not. You should import all external modules using the import_module() function. For example >>> from sympy.external import import_module >>> numpy = import_module('numpy') If the resulting library is not installed, or if the installed version is less than a given minimum version, the function will return None. Otherwise, it will return the library. See the docstring of import_module() for more information. """ from importtools import import_module wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/0000755000175000017500000000000012014170666023107 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/0000755000175000017500000000000012014170666025254 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/complexes.py0000644000175000017500000002320312014170666027625 0ustar georgeskgeorgeskfrom sympy.core import S, C from sympy.core.function import Function, Derivative, ArgumentIndexError from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.elementary.piecewise import Piecewise from sympy.core import Add, Mul from sympy.core.relational import Eq ############################################################################### ######################### REAL and IMAGINARY PARTS ############################ ############################################################################### class re(Function): """Returns real part of expression. This function performs only elementary analysis and so it will fail to decompose properly more complicated expressions. If completely simplified result is needed then use Basic.as_real_imag() or perform complex expansion on instance of this function. >>> from sympy import re, im, I, E >>> from sympy.abc import x, y >>> re(2*E) 2*E >>> re(2*I + 17) 17 >>> re(2*I) 0 >>> re(im(x) + x*I + 2) 2 """ nargs = 1 is_real = True @classmethod def eval(cls, arg): if arg is S.NaN: return S.NaN elif arg.is_real: return arg elif arg.is_Function and arg.func == conjugate: return re(arg.args[0]) else: included, reverted, excluded = [], [], [] arg = Add.make_args(arg) for term in arg: coeff = term.as_coefficient(S.ImaginaryUnit) if coeff is not None: if not coeff.is_real: reverted.append(coeff) elif not term.has(S.ImaginaryUnit) and term.is_real: excluded.append(term) else: included.append(term) if len(arg) != len(included): a, b, c = map(lambda xs: Add(*xs), [included, reverted, excluded]) return cls(a) - im(b) + c def _eval_conjugate(self): return self def as_real_imag(self, deep=True): return (self, S.Zero) def _eval_expand_complex(self, deep=True, **hints): # if deep: # return self.args[0].expand(deep, **hints).as_real_imag()[0] # else: return self.args[0].as_real_imag()[0] def _eval_derivative(self, x): return re(Derivative(self.args[0], x, **{'evaluate': True})) class im(Function): """Returns imaginary part of expression. This function performs only elementary analysis and so it will fail to decompose properly more complicated expressions. If completely simplified result is needed then use Basic.as_real_imag() or perform complex expansion on instance of this function. >>> from sympy import re, im, E, I >>> from sympy.abc import x, y >>> im(2*E) 0 >>> re(2*I + 17) 17 >>> im(x*I) re(x) >>> im(re(x) + y) im(y) """ nargs = 1 is_real = True @classmethod def eval(cls, arg): if arg is S.NaN: return S.NaN elif arg.is_real: return S.Zero elif arg.is_Function and arg.func == conjugate: return -im(arg.args[0]) else: included, reverted, excluded = [], [], [] arg = Add.make_args(arg) for term in arg: coeff = term.as_coefficient(S.ImaginaryUnit) if coeff is not None: if not coeff.is_real: reverted.append(coeff) else: excluded.append(coeff) elif term.has(S.ImaginaryUnit) or not term.is_real: included.append(term) if len(arg) != len(included): a, b, c = map(lambda xs: Add(*xs), [included, reverted, excluded]) return cls(a) + re(b) + c def _eval_conjugate(self): return self def as_real_imag(self, deep=True): return (self, S.Zero) def _eval_expand_complex(self, deep=True, **hints): # if deep: # return self.args[0].expand(deep, **hints).as_real_imag()[1] return self.args[0].as_real_imag()[1] def _eval_derivative(self, x): return im(Derivative(self.args[0], x, **{'evaluate': True})) ############################################################################### ############### SIGN, ABSOLUTE VALUE, ARGUMENT and CONJUGATION ################ ############################################################################### class sign(Function): """Return the sign of an expression, that is: -1 if expr < 0 0 if expr == 0 1 if expr > 0 """ nargs = 1 @classmethod def eval(cls, arg): if arg is S.NaN: return S.NaN if arg is S.Zero: return S.Zero if arg.is_positive: return S.One if arg.is_negative: return S.NegativeOne if arg.is_Function: if arg.func is sign: return arg if arg.is_Mul: c, args = arg.as_coeff_mul() unk = [] is_neg = c.is_negative for ai in args: if ai.is_negative is None: unk.append(ai) elif ai.is_negative: is_neg = not is_neg if c is S.One and len(unk) == len(args): return None return (S.NegativeOne if is_neg else S.One) * cls(arg._new_rawargs(*unk)) is_bounded = True def _eval_derivative(self, x): return S.Zero def _eval_conjugate(self): return self def _eval_is_zero(self): return (self.args[0] is S.Zero) def _sage_(self): import sage.all as sage return sage.sgn(self.args[0]._sage_()) class Abs(Function): """Return the absolute value of the argument. This is an extension of the built-in function abs() to accept symbolic values. If you pass a SymPy expression to the built-in abs(), it will pass it automatically to Abs(). Examples >>> from sympy import Abs, Symbol, S >>> Abs(-1) 1 >>> x = Symbol('x', real=True) >>> Abs(-x) Abs(x) >>> Abs(x**2) x**2 >>> abs(-x) # The Python built-in Abs(x) Note that the Python built-in will return either an Expr or int depending on the argument:: >>> type(abs(-1)) >>> type(abs(S.NegativeOne)) Abs will always return a sympy object. """ nargs = 1 is_real = True is_negative = False def fdiff(self, argindex=1): if argindex == 1: return sign(self.args[0]) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg is S.NaN: return S.NaN if arg.is_zero: return arg if arg.is_positive: return arg if arg.is_negative: return -arg coeff, terms = arg.as_coeff_mul() if coeff is not S.One: return cls(coeff) * cls(Mul(*terms)) if arg.is_real is False: return sqrt( (arg * arg.conjugate()).expand() ) if arg.is_Pow: base, exponent = arg.as_base_exp() if exponent.is_even and base.is_real: return arg return def _eval_is_nonzero(self): return self._args[0].is_nonzero def _eval_is_positive(self): return self.is_nonzero def _eval_conjugate(self): return self def _eval_power(self,other): if self.args[0].is_real and other.is_integer: if other.is_even: return self.args[0]**other return def _eval_nseries(self, x, n, logx): direction = self.args[0].leadterm(x)[0] s = self.args[0]._eval_nseries(x, n=n, logx=logx) when = Eq(direction, 0) return Piecewise( ((s.subs(direction, 0)), when), (sign(direction)*s, True), ) def _sage_(self): import sage.all as sage return sage.abs_symbolic(self.args[0]._sage_()) def _eval_derivative(self, x): if self.args[0].is_real: return Derivative(self.args[0], x, **{'evaluate': True}) * sign(self.args[0]) return (re(self.args[0]) * re(Derivative(self.args[0], x, **{'evaluate': True})) + im(self.args[0]) * im(Derivative(self.args[0], x, **{'evaluate': True}))) / Abs(self.args[0]) class arg(Function): """Returns the argument (in radians) of a complex number""" nargs = 1 is_real = True is_bounded = True @classmethod def eval(cls, arg): x, y = re(arg), im(arg) arg = C.atan2(y, x) if arg.is_number: return arg def _eval_conjugate(self): return self def _eval_derivative(self, t): x, y = re(self.args[0]), im(self.args[0]) return (x * Derivative(y, t, **{'evaluate': True}) - y * Derivative(x, t, **{'evaluate': True})) / (x**2 + y**2) class conjugate(Function): """Changes the sign of the imaginary part of a complex number. >>> from sympy import conjugate, I >>> conjugate(1 + I) 1 - I """ nargs = 1 @classmethod def eval(cls, arg): obj = arg._eval_conjugate() if obj is not None: return obj def _eval_conjugate(self): return self.args[0] def _eval_derivative(self, x): return conjugate(Derivative(self.args[0], x, **{'evaluate': True})) # /cyclic/ from sympy.core import basic as _ _.abs_ = Abs del _ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/hyperbolic.py0000644000175000017500000005643412014170666030002 0ustar georgeskgeorgeskfrom sympy.core import S, C, sympify, cacheit from sympy.core.function import Function, ArgumentIndexError from sympy.functions.elementary.miscellaneous import sqrt ############################################################################### ########################### HYPERBOLIC FUNCTIONS ############################## ############################################################################### class HyperbolicFunction(Function): """Base class for hyperbolic functions. """ class sinh(HyperbolicFunction): """ Usage ===== sinh(x) -> Returns the hyperbolic sine of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return cosh(self.args[0]) else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return asinh @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.NegativeInfinity elif arg is S.Zero: return S.Zero elif arg.is_negative: return -cls(-arg) else: if arg is S.ComplexInfinity: return S.NaN i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.sin(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) if arg.func == asinh: return arg.args[0] if arg.func == acosh: x = arg.args[0] return sqrt(x-1) * sqrt(x+1) if arg.func == atanh: x = arg.args[0] return x/sqrt(1-x**2) if arg.func == acoth: x = arg.args[0] return 1/(sqrt(x-1) * sqrt(x+1)) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) > 2: p = previous_terms[-2] return p * x**2 / (n*(n-1)) else: return x**(n) / C.factorial(n) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() return (sinh(re)*C.cos(im), cosh(re)*C.sin(im)) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_rewrite_as_exp(self, arg): return (C.exp(arg) - C.exp(-arg)) / 2 def _eval_rewrite_as_cosh(self, arg): return -S.ImaginaryUnit*cosh(arg + S.Pi*S.ImaginaryUnit/2) def _eval_rewrite_as_tanh(self, arg): tanh_half = tanh(S.Half*arg) return 2*tanh_half/(1 - tanh_half**2) def _eval_rewrite_as_coth(self, arg): coth_half = coth(S.Half*arg) return 2*coth_half/(coth_half**2 - 1) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return S.One else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_imaginary: return True def _sage_(self): import sage.all as sage return sage.sinh(self.args[0]._sage_()) class cosh(HyperbolicFunction): """ Usage ===== cosh(x) -> Returns the hyperbolic cosine of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return sinh(self.args[0]) else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return acosh @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Infinity elif arg is S.Zero: return S.One elif arg.is_negative: return cls(-arg) else: if arg is S.ComplexInfinity: return S.NaN i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return C.cos(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return cls(-arg) if arg.func == asinh: return sqrt(1+arg.args[0]**2) if arg.func == acosh: return arg.args[0] if arg.func == atanh: return 1/sqrt(1-arg.args[0]**2) if arg.func == acoth: x = arg.args[0] return x/(sqrt(x-1) * sqrt(x+1)) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 1: return S.Zero else: x = sympify(x) if len(previous_terms) > 2: p = previous_terms[-2] return p * x**2 / (n*(n-1)) else: return x**(n)/C.factorial(n) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() return (cosh(re)*C.cos(im), sinh(re)*C.sin(im)) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_rewrite_as_exp(self, arg): return (C.exp(arg) + C.exp(-arg)) / 2 def _eval_rewrite_as_sinh(self, arg): return -S.ImaginaryUnit*sinh(arg + S.Pi*S.ImaginaryUnit/2) def _eval_rewrite_as_tanh(self, arg): tanh_half = tanh(S.Half*arg)**2 return (1+tanh_half)/(1-tanh_half) def _eval_rewrite_as_coth(self, arg): coth_half = coth(S.Half*arg)**2 return (coth_half+1)/(coth_half-1) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return S.One else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_imaginary: return True def _sage_(self): import sage.all as sage return sage.cosh(self.args[0]._sage_()) class tanh(HyperbolicFunction): """ Usage ===== tanh(x) -> Returns the hyperbolic tangent of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return S.One - tanh(self.args[0])**2 else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return atanh @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.One elif arg is S.NegativeInfinity: return S.NegativeOne elif arg is S.Zero: return S.Zero elif arg.is_negative: return -cls(-arg) else: if arg is S.ComplexInfinity: return S.NaN i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: if i_coeff.as_coeff_mul()[0].is_negative: return -S.ImaginaryUnit * C.tan(-i_coeff) return S.ImaginaryUnit * C.tan(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) if arg.func == asinh: x = arg.args[0] return x/sqrt(1+x**2) if arg.func == acosh: x = arg.args[0] return sqrt(x-1) * sqrt(x+1) / x if arg.func == atanh: return arg.args[0] if arg.func == acoth: return 1/arg.args[0] @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) a = 2**(n+1) B = C.bernoulli(n+1) F = C.factorial(n+1) return a*(a-1) * B/F * x**n def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() denom = sinh(re)**2 + C.cos(im)**2 return (sinh(re)*cosh(re)/denom, C.sin(im)*C.cos(im)/denom) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_rewrite_as_exp(self, arg): neg_exp, pos_exp = C.exp(-arg), C.exp(arg) return (pos_exp-neg_exp)/(pos_exp+neg_exp) def _eval_rewrite_as_sinh(self, arg): return S.ImaginaryUnit*sinh(arg)/sinh(S.Pi*S.ImaginaryUnit/2 - arg) def _eval_rewrite_as_cosh(self, arg): return S.ImaginaryUnit*cosh(S.Pi*S.ImaginaryUnit/2 - arg)/cosh(arg) def _eval_rewrite_as_coth(self, arg): return 1/coth(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return S.One else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_real: return True def _sage_(self): import sage.all as sage return sage.tanh(self.args[0]._sage_()) class coth(HyperbolicFunction): """ Usage ===== coth(x) -> Returns the hyperbolic cotangent of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 1/sinh(self.args[0])**2 else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return acoth @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.One elif arg is S.NegativeInfinity: return S.NegativeOne elif arg is S.Zero: return S.Zero elif arg.is_negative: return -cls(-arg) else: if arg is S.ComplexInfinity: return S.NaN i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: if i_coeff.as_coeff_mul()[0].is_negative: return S.ImaginaryUnit * C.cot(-i_coeff) return -S.ImaginaryUnit * C.cot(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) if arg.func == asinh: x = arg.args[0] return sqrt(1+x**2)/x if arg.func == acosh: x = arg.args[0] return x/(sqrt(x-1) * sqrt(x+1)) if arg.func == atanh: return 1/arg.args[0] if arg.func == acoth: return arg.args[0] @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return 1 / sympify(x) elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) B = C.bernoulli(n+1) F = C.factorial(n+1) return 2**(n+1) * B/F * x**n def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() denom = sinh(re)**2 + C.sin(im)**2 return (sinh(re)*cosh(re)/denom, -C.sin(im)*C.cos(im)/denom) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_rewrite_as_exp(self, arg): neg_exp, pos_exp = C.exp(-arg), C.exp(arg) return (pos_exp+neg_exp)/(pos_exp-neg_exp) def _eval_rewrite_as_sinh(self, arg): return -S.ImaginaryUnit*sinh(S.Pi*S.ImaginaryUnit/2 - arg)/sinh(arg) def _eval_rewrite_as_cosh(self, arg): return -S.ImaginaryUnit*cosh(arg)/cosh(S.Pi*S.ImaginaryUnit/2 - arg) def _eval_rewrite_as_tanh(self, arg): return 1/tanh(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return S.One else: return self.func(arg) def _sage_(self): import sage.all as sage return sage.coth(self.args[0]._sage_()) ############################################################################### ############################# HYPERBOLIC INVERSES ############################# ############################################################################### class asinh(Function): """ Usage ===== asinh(x) -> Returns the inverse hyperbolic sine of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return (self.args[0]**2 + 1)**(-S.Half) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.NegativeInfinity elif arg is S.Zero: return S.Zero elif arg is S.One: return C.log(2**S.Half + 1) elif arg is S.NegativeOne: return C.log(2**S.Half - 1) elif arg.is_negative: return -cls(-arg) else: if arg is S.ComplexInfinity: return S.ComplexInfinity i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.asin(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) >= 2 and n > 2: p = previous_terms[-2] return -p * (n-2)**2/(n*(n-1)) * x**2 else: k = (n - 1) // 2 R = C.RisingFactorial(S.Half, k) F = C.factorial(k) return (-1)**k * R / F * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _sage_(self): import sage.all as sage return sage.asinh(self.args[0]._sage_()) class acosh(Function): """ Usage ===== acosh(x) -> Returns the inverse hyperbolic cosine of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return (self.args[0]**2 - 1)**(-S.Half) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Infinity elif arg is S.Zero: return S.Pi*S.ImaginaryUnit / 2 elif arg is S.One: return S.Zero elif arg is S.NegativeOne: return S.Pi*S.ImaginaryUnit if arg.is_number: cst_table = { S.ImaginaryUnit : C.log(S.ImaginaryUnit*(1+sqrt(2))), -S.ImaginaryUnit : C.log(-S.ImaginaryUnit*(1+sqrt(2))), S.Half : S.Pi/3, -S.Half : 2*S.Pi/3, sqrt(2)/2 : S.Pi/4, -sqrt(2)/2 : 3*S.Pi/4, 1/sqrt(2) : S.Pi/4, -1/sqrt(2) : 3*S.Pi/4, sqrt(3)/2 : S.Pi/6, -sqrt(3)/2 : 5*S.Pi/6, (sqrt(3)-1)/sqrt(2**3) : 5*S.Pi/12, -(sqrt(3)-1)/sqrt(2**3) : 7*S.Pi/12, sqrt(2+sqrt(2))/2 : S.Pi/8, -sqrt(2+sqrt(2))/2 : 7*S.Pi/8, sqrt(2-sqrt(2))/2 : 3*S.Pi/8, -sqrt(2-sqrt(2))/2 : 5*S.Pi/8, (1+sqrt(3))/(2*sqrt(2)) : S.Pi/12, -(1+sqrt(3))/(2*sqrt(2)) : 11*S.Pi/12, (sqrt(5)+1)/4 : S.Pi/5, -(sqrt(5)+1)/4 : 4*S.Pi/5 } if arg in cst_table: if arg.is_real: return cst_table[arg]*S.ImaginaryUnit return cst_table[arg] if arg is S.ComplexInfinity: return S.Infinity i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: if i_coeff.as_coeff_mul()[0].is_negative: return S.ImaginaryUnit * C.acos(i_coeff) return S.ImaginaryUnit * C.acos(-i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.Pi*S.ImaginaryUnit / 2 elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) >= 2 and n > 2: p = previous_terms[-2] return p * (n-2)**2/(n*(n-1)) * x**2 else: k = (n - 1) // 2 R = C.RisingFactorial(S.Half, k) F = C.factorial(k) return -R / F * S.ImaginaryUnit * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _sage_(self): import sage.all as sage return sage.acosh(self.args[0]._sage_()) class atanh(Function): """ Usage ===== atanh(x) -> Returns the inverse hyperbolic tangent of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 1/(1-self.args[0]**2) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.Zero elif arg is S.One: return S.Infinity elif arg is S.NegativeOne: return S.NegativeInfinity elif arg is S.Infinity: return -S.ImaginaryUnit * C.atan(arg) elif arg is S.NegativeInfinity: return S.ImaginaryUnit * C.atan(-arg) elif arg.is_negative: return -cls(-arg) else: if arg is S.ComplexInfinity: return S.NaN i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.atan(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) return x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _sage_(self): import sage.all as sage return sage.atanh(self.args[0]._sage_()) class acoth(Function): """ Usage ===== acoth(x) -> Returns the inverse hyperbolic cotangent of x """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 1/(1-self.args[0]**2) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): arg = sympify(arg) if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Zero elif arg is S.NegativeInfinity: return S.Zero elif arg is S.Zero: return S.Pi*S.ImaginaryUnit / 2 elif arg is S.One: return S.Infinity elif arg is S.NegativeOne: return S.NegativeInfinity elif arg.is_negative: return -cls(-arg) else: if arg is S.ComplexInfinity: return 0 i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return -S.ImaginaryUnit * C.acot(i_coeff) else: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.Pi*S.ImaginaryUnit / 2 elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) return x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _sage_(self): import sage.all as sage return sage.acoth(self.args[0]._sage_()) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/integers.py0000644000175000017500000001153212014170666027450 0ustar georgeskgeorgeskfrom sympy.core.basic import C from sympy.core.singleton import S from sympy.core.function import Function from sympy.core import Add from sympy.core.evalf import get_integer_part, PrecisionExhausted ############################################################################### ######################### FLOOR and CEILING FUNCTIONS ######################### ############################################################################### class RoundFunction(Function): nargs = 1 @classmethod def eval(cls, arg): if arg.is_integer: return arg if arg.is_imaginary: return cls(C.im(arg))*S.ImaginaryUnit v = cls._eval_number(arg) if v is not None: return v # Integral, numerical, symbolic part ipart = npart = spart = S.Zero # Extract integral (or complex integral) terms terms = Add.make_args(arg) for t in terms: if t.is_integer or (t.is_imaginary and C.im(t).is_integer): ipart += t elif t.has(C.Symbol): spart += t else: npart += t if not (npart or spart): return ipart # Evaluate npart numerically if independent of spart orthogonal = (npart.is_real and spart.is_imaginary) or \ (npart.is_imaginary and spart.is_real) if npart and ((not spart) or orthogonal): try: re, im = get_integer_part(npart, cls._dir, {}, return_ints=True) ipart += C.Integer(re) + C.Integer(im)*S.ImaginaryUnit npart = S.Zero except (PrecisionExhausted, NotImplementedError): pass spart = npart + spart if not spart: return ipart elif spart.is_imaginary: return ipart + cls(C.im(spart), evaluate=False)*S.ImaginaryUnit else: return ipart + cls(spart, evaluate=False) def _eval_is_bounded(self): return self.args[0].is_bounded def _eval_is_real(self): return self.args[0].is_real def _eval_is_integer(self): return self.args[0].is_real class floor(RoundFunction): """ Floor is a univariate function which returns the largest integer value not greater than its argument. However this implementation generalizes floor to complex numbers. More information can be found in "Concrete mathematics" by Graham, pp. 87 or visit http://mathworld.wolfram.com/FloorFunction.html. >>> from sympy import floor, E, I, Float, Rational >>> floor(17) 17 >>> floor(Rational(23, 10)) 2 >>> floor(2*E) 5 >>> floor(-Float(0.567)) -1 >>> floor(-I/2) -I """ _dir = -1 @classmethod def _eval_number(cls, arg): if arg.is_Number: if arg.is_Rational: if not arg.q: return arg return C.Integer(arg.p // arg.q) elif arg.is_Float: return C.Integer(int(arg.floor())) if arg.is_NumberSymbol: return arg.approximation_interval(C.Integer)[0] def _eval_nseries(self, x, n, logx): r = self.subs(x, 0) args = self.args[0] args0 = args.subs(x, 0) if args0 == r: direction = (args - args0).leadterm(x)[0] if direction.is_positive: return r else: return r-1 else: return r class ceiling(RoundFunction): """ Ceiling is a univariate function which returns the smallest integer value not less than its argument. Ceiling function is generalized in this implementation to complex numbers. More information can be found in "Concrete mathematics" by Graham, pp. 87 or visit http://mathworld.wolfram.com/CeilingFunction.html. >>> from sympy import ceiling, E, I, Float, Rational >>> ceiling(17) 17 >>> ceiling(Rational(23, 10)) 3 >>> ceiling(2*E) 6 >>> ceiling(-Float(0.567)) 0 >>> ceiling(I/2) I """ _dir = 1 @classmethod def _eval_number(cls, arg): if arg.is_Number: if arg.is_Rational: if not arg.q: return arg return -C.Integer(-arg.p // arg.q) elif arg.is_Float: return C.Integer(int(arg.ceiling())) if arg.is_NumberSymbol: return arg.approximation_interval(C.Integer)[1] def _eval_nseries(self, x, n, logx): r = self.subs(x, 0) args = self.args[0] args0 = args.subs(x, 0) if args0 == r: direction = (args - args0).leadterm(x)[0] if direction.is_positive: return r + 1 else: return r else: return r wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/trigonometric.py0000644000175000017500000011330012014170666030511 0ustar georgeskgeorgeskfrom sympy.core.add import Add from sympy.core.numbers import Rational, Float from sympy.core.basic import C, sympify, cacheit from sympy.core.singleton import S from sympy.core.function import Function, ArgumentIndexError from miscellaneous import sqrt ############################################################################### ########################## TRIGONOMETRIC FUNCTIONS ############################ ############################################################################### class TrigonometricFunction(Function): """Base class for trigonometric functions. """ def _peeloff_pi(arg): """ Split ARG into two parts, a "rest" and a multiple of pi/2. This assumes ARG to be an Add. The multiple of pi returned in the second position is always a Rational. Examples: >>> from sympy.functions.elementary.trigonometric import _peeloff_pi as peel >>> from sympy import pi >>> from sympy.abc import x, y >>> peel(x + pi/2) (x, pi/2) >>> peel(x + 2*pi/3 + pi*y) (x + pi*y + pi/6, pi/2) """ for a in Add.make_args(arg): if a is S.Pi: K = S.One break elif a.is_Mul: K, p = a.as_two_terms() if p is S.Pi and K.is_Rational: break else: return arg, 0 m1 = (K % S.Half) * S.Pi m2 = K*S.Pi - m1 return arg - m2, m2 def _pi_coeff(arg, cycles=1): """ When arg is a Number times pi (e.g. 3*pi/2) then return the Number normalized to be in the range [0, 2], else None. When an even multiple of pi is encountered, if it is multiplying something with known parity then the multiple is returned as 0 otherwise as 2. Examples: >>> from sympy.functions.elementary.trigonometric import _pi_coeff as coeff >>> from sympy import pi >>> from sympy.abc import x, y >>> coeff(3*x*pi) 3*x >>> coeff(11*pi/7) 11/7 >>> coeff(-11*pi/7) 3/7 >>> coeff(4*pi) 0 >>> coeff(5*pi) 1 >>> coeff(5.0*pi) 1 >>> coeff(5.5*pi) 3/2 >>> coeff(2 + pi) """ arg = sympify(arg) if arg is S.Pi: return S.One elif not arg: return S.Zero elif arg.is_Mul: cx = arg.coeff(S.Pi) if cx: c, x = cx.as_coeff_Mul() # pi is not included as coeff if c.is_Float: # recast exact binary fractions to Rationals m = int(c*2) if Float(float(m)/2) == c: c = Rational(m, 2) if x is not S.One or not (c.is_Rational and c.q != 1): if x.is_integer: c2 = c % 2 if c2 == 1: return x elif not c2: if x.is_even is not None: # known parity return S.Zero return 2*x else: return c2*x return cx else: return Rational(c.p % (2*c.q), c.q) class sin(TrigonometricFunction): """ Usage ===== sin(x) -> Returns the sine of x (measured in radians) Notes ===== sin(x) will evaluate automatically in the case x is a multiple of pi, pi/2, pi/3, pi/4 and pi/6. Examples ======== >>> from sympy import sin, pi >>> from sympy.abc import x >>> sin(x**2).diff(x) 2*x*cos(x**2) >>> sin(1).diff(x) 0 >>> sin(pi) 0 >>> sin(pi/2) 1 >>> sin(pi/6) 1/2 See also ======== L{cos}, L{tan} External links -------------- U{Definitions in trigonometry} """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return cos(self.args[0]) else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return asin @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.Zero elif arg is S.Infinity: return if arg.could_extract_minus_sign(): return -cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.sinh(i_coeff) pi_coeff = _pi_coeff(arg) if pi_coeff is not None: if pi_coeff.is_integer: return S.Zero if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None cst_table_some = { 2 : S.One, 3 : S.Half*sqrt(3), 4 : S.Half*sqrt(2), 6 : S.Half, } cst_table_more = { (1, 5) : sqrt((5 - sqrt(5)) / 8), (2, 5) : sqrt((5 + sqrt(5)) / 8) } p = pi_coeff.p q = pi_coeff.q Q, P = p // q, p % q try: result = cst_table_some[q] except KeyError: if abs(P) > q // 2: P = q - P try: result = cst_table_more[(P, q)] except KeyError: if P != p: result = cls(C.Rational(P, q)*S.Pi) else: return None if Q % 2 == 1: return -result else: return result if arg.is_Add: x, m = _peeloff_pi(arg) if m: return sin(m)*cos(x)+cos(m)*sin(x) if arg.func is asin: return arg.args[0] if arg.func is atan: x = arg.args[0] return x / sqrt(1 + x**2) if arg.func is acos: x = arg.args[0] return sqrt(1 - x**2) if arg.func is acot: x = arg.args[0]; return 1 / (sqrt(1 + 1 / x**2) * x) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) > 2: p = previous_terms[-2] return -p * x**2 / (n*(n-1)) else: return (-1)**(n//2) * x**(n)/C.factorial(n) def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit return (exp(arg*I) - exp(-arg*I)) / (2*I) def _eval_rewrite_as_cos(self, arg): return -cos(arg + S.Pi/2) def _eval_rewrite_as_tan(self, arg): tan_half = tan(S.Half*arg) return 2*tan_half/(1 + tan_half**2) def _eval_rewrite_as_cot(self, arg): cot_half = cot(S.Half*arg) return 2*cot_half/(1 + cot_half**2) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() return (sin(re)*C.cosh(im), cos(re)*C.sinh(im)) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_expand_trig(self, deep=True, **hints): if deep: arg = self.args[0].expand(deep, **hints) else: arg = self.args[0] x = None if arg.is_Add: # TODO, implement more if deep stuff here x, y = arg.as_two_terms() else: coeff, terms = arg.as_coeff_mul() if not (coeff is S.One) and coeff.is_Integer and terms: x = arg._new_rawargs(*terms) y = (coeff-1)*x if x is not None: return (sin(x)*cos(y) + sin(y)*cos(x)).expand(trig=True) return sin(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_real: return True def _sage_(self): import sage.all as sage return sage.sin(self.args[0]._sage_()) class cos(TrigonometricFunction): """ Usage ===== cos(x) -> Returns the cosine of x (measured in radians) Notes ===== cos(x) will evaluate automatically in the case x is a multiple of pi, pi/2, pi/3, pi/4 and pi/6. Examples ======== >>> from sympy import cos, pi >>> from sympy.abc import x >>> cos(x**2).diff(x) -2*x*sin(x**2) >>> cos(1).diff(x) 0 >>> cos(pi) -1 >>> cos(pi/2) 0 >>> cos(2*pi/3) -1/2 See also ======== L{sin}, L{tan} External links -------------- U{Definitions in trigonometry} """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return -sin(self.args[0]) else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return acos @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.Infinity: return if arg.could_extract_minus_sign(): return cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return C.cosh(i_coeff) pi_coeff = _pi_coeff(arg) if pi_coeff is not None: if not pi_coeff.is_Rational: if pi_coeff.is_integer: even = pi_coeff.is_even if even: return S.One elif even is False: return S.NegativeOne narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None cst_table_some = { 1 : S.One, 2 : S.Zero, 3 : S.Half, 4 : S.Half*sqrt(2), 6 : S.Half*sqrt(3), } cst_table_more = { (1, 5) : (sqrt(5) + 1)/4, (2, 5) : (sqrt(5) - 1)/4 } p = pi_coeff.p q = pi_coeff.q Q, P = 2*p // q, p % q try: result = cst_table_some[q] except KeyError: if abs(P) > q // 2: P = q - P try: result = cst_table_more[(P, q)] except KeyError: if P != p: result = cls(C.Rational(P, q)*S.Pi) else: return None if Q % 4 in (1, 2): return -result else: return result if arg.is_Add: x, m = _peeloff_pi(arg) if m: return cos(m)*cos(x)-sin(m)*sin(x) if arg.func is acos: return arg.args[0] if arg.func is atan: x = arg.args[0] return 1 / sqrt(1 + x**2) if arg.func is asin: x = arg.args[0] return sqrt(1 - x ** 2) if arg.func is acot: x = arg.args[0] return 1 / sqrt(1 + 1 / x**2) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 1: return S.Zero else: x = sympify(x) if len(previous_terms) > 2: p = previous_terms[-2] return -p * x**2 / (n*(n-1)) else: return (-1)**(n//2)*x**(n)/C.factorial(n) def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit return (exp(arg*I) + exp(-arg*I)) / 2 def _eval_rewrite_as_sin(self, arg): return sin(arg + S.Pi/2) def _eval_rewrite_as_tan(self, arg): tan_half = tan(S.Half*arg)**2 return (1-tan_half)/(1+tan_half) def _eval_rewrite_as_cot(self, arg): cot_half = cot(S.Half*arg)**2 return (cot_half-1)/(cot_half+1) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() return (cos(re)*C.cosh(im), -sin(re)*C.sinh(im)) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_expand_trig(self, deep=True, **hints): if deep: arg = self.args[0].expand() else: arg = self.args[0] x = None if arg.is_Add: # TODO, implement more if deep stuff here x, y = arg.as_two_terms() return (cos(x)*cos(y) - sin(y)*sin(x)).expand(trig=True) else: coeff, terms = arg.as_coeff_mul() if not (coeff is S.One) and coeff.is_Integer and terms: x = arg._new_rawargs(*terms) return C.chebyshevt(coeff, cos(x)) return cos(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return S.One else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_real: return True def _sage_(self): import sage.all as sage return sage.cos(self.args[0]._sage_()) class tan(TrigonometricFunction): """ Usage ===== tan(x) -> Returns the tangent of x (measured in radians) Notes ===== tan(x) will evaluate automatically in the case x is a multiple of pi. Examples ======== >>> from sympy import tan >>> from sympy.abc import x >>> tan(x**2).diff(x) 2*x*(tan(x**2)**2 + 1) >>> tan(1).diff(x) 0 See also ======== L{sin}, L{tan} External links -------------- U{Definitions in trigonometry} """ nargs = 1 def fdiff(self, argindex=1): if argindex==1: return S.One + self**2 else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return atan @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.Zero if arg.could_extract_minus_sign(): return -cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.tanh(i_coeff) pi_coeff = _pi_coeff(arg, 2) if pi_coeff is not None: if pi_coeff.is_integer: return S.Zero if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None cst_table = { 2 : S.ComplexInfinity, 3 : sqrt(3), 4 : S.One, 6 : 1 / sqrt(3), } try: result = cst_table[pi_coeff.q] if (2*pi_coeff.p // pi_coeff.q) % 4 in (1, 3): return -result else: return result except KeyError: if pi_coeff.p > pi_coeff.q: p, q = pi_coeff.p % pi_coeff.q, pi_coeff.q if 2 * p > q: return -cls(Rational(q - p, q)*S.Pi) return cls(Rational(p, q)*S.Pi) if arg.is_Add: x, m = _peeloff_pi(arg) if m: if (m*2/S.Pi) % 2 == 0: return tan(x) else: return -cot(x) if arg.func is atan: return arg.args[0] if arg.func is asin: x = arg.args[0] return x / sqrt(1 - x**2) if arg.func is acos: x = arg.args[0] return sqrt(1 - x**2) / x if arg.func is acot: x = arg.args[0] return 1 / x @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) a, b = ((n-1)//2), 2**(n+1) B = C.bernoulli(n+1) F = C.factorial(n+1) return (-1)**a * b*(b-1) * B/F * x**n def _eval_nseries(self, x, n, logx): i = self.args[0].limit(x, 0)*2/S.Pi if i and i.is_Integer: return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx) return Function._eval_nseries(self, x, n=n, logx=logx) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() denom = cos(re)**2 + C.sinh(im)**2 return (sin(re)*cos(re)/denom, C.sinh(im)*C.cosh(im)/denom) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_expand_trig(self, deep=True, **hints): return self def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit neg_exp, pos_exp = exp(-arg*I), exp(arg*I) return I*(neg_exp-pos_exp)/(neg_exp+pos_exp) def _eval_rewrite_as_sin(self, x): return 2*sin(x)**2/sin(2*x) def _eval_rewrite_as_cos(self, x): return -cos(x + S.Pi/2)/cos(x) def _eval_rewrite_as_cot(self, arg): return 1/cot(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_is_bounded(self): arg = self.args[0] if arg.is_imaginary: return True def _eval_subs(self, old, new): if self == old: return new arg = self.args[0] argnew = arg.subs(old, new) if arg != argnew and (argnew/(S.Pi/2)).is_odd: return S.NaN return tan(argnew) def _sage_(self): import sage.all as sage return sage.tan(self.args[0]._sage_()) class cot(TrigonometricFunction): """ Usage ===== cot(x) -> Returns the cotangent of x (measured in radians) """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return S.NegativeOne - self**2 else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return acot @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN if arg is S.Zero: return S.ComplexInfinity if arg.could_extract_minus_sign(): return -cls(-arg) i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return -S.ImaginaryUnit * C.coth(i_coeff) pi_coeff = _pi_coeff(arg, 2) if pi_coeff is not None: if pi_coeff.is_integer: return S.ComplexInfinity if not pi_coeff.is_Rational: narg = pi_coeff*S.Pi if narg != arg: return cls(narg) return None cst_table = { 2 : S.Zero, 3 : 1 / sqrt(3), 4 : S.One, 6 : sqrt(3) } try: result = cst_table[pi_coeff.q] if (2*pi_coeff.p // pi_coeff.q) % 4 in (1, 3): return -result else: return result except KeyError: if pi_coeff.p > pi_coeff.q: p, q = pi_coeff.p % pi_coeff.q, pi_coeff.q if 2 * p > q: return -cls(Rational(q - p, q)*S.Pi) return cls(Rational(p, q)*S.Pi) if arg.is_Add: x, m = _peeloff_pi(arg) if m: if (m*2/S.Pi) % 2 == 0: return cot(x) else: return -tan(x) if arg.func is acot: return arg.args[0] if arg.func is atan: x = arg.args[0] return 1 / x if arg.func is asin: x = arg.args[0] return sqrt(1 - x**2) / x if arg.func is acos: x = arg.args[0] return x / sqrt(1 - x**2) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return 1 / sympify(x) elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) B = C.bernoulli(n+1) F = C.factorial(n+1) return (-1)**((n+1)//2) * 2**(n+1) * B/F * x**n def _eval_nseries(self, x, n, logx): i = self.args[0].limit(x, 0)/S.Pi if i and i.is_Integer: return self.rewrite(cos)._eval_nseries(x, n=n, logx=logx) return Function._eval_nseries(self, x, n=n, logx=logx) def _eval_conjugate(self): assert len(self.args) == 1 return self.func(self.args[0].conjugate()) def as_real_imag(self, deep=True, **hints): if self.args[0].is_real: if deep: hints['complex'] = False return (self.expand(deep, **hints), S.Zero) else: return (self, S.Zero) if deep: re, im = self.args[0].expand(deep, **hints).as_real_imag() else: re, im = self.args[0].as_real_imag() denom = sin(re)**2 + C.sinh(im)**2 return (sin(re)*cos(re)/denom, -C.sinh(im)*C.cosh(im)/denom) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_rewrite_as_exp(self, arg): exp, I = C.exp, S.ImaginaryUnit neg_exp, pos_exp = exp(-arg*I), exp(arg*I) return I*(pos_exp+neg_exp)/(pos_exp-neg_exp) def _eval_rewrite_as_sin(self, x): return 2*sin(2*x)/sin(x)**2 def _eval_rewrite_as_cos(self, x): return -cos(x)/cos(x + S.Pi/2) def _eval_rewrite_as_tan(self, arg): return 1/tan(arg) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return 1/arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_subs(self, old, new): if self == old: return new arg = self.args[0] argnew = arg.subs(old, new) if arg != argnew and (argnew/S.Pi).is_integer: return S.NaN return cot(argnew) def _sage_(self): import sage.all as sage return sage.cot(self.args[0]._sage_()) ############################################################################### ########################### TRIGONOMETRIC INVERSES ############################ ############################################################################### class asin(Function): """ Usage ===== asin(x) -> Returns the arc sine of x (measured in radians) Notes ==== asin(x) will evaluate automatically in the cases oo, -oo, 0, 1, -1 Examples ======== >>> from sympy import asin, oo, pi >>> asin(1) pi/2 >>> asin(-1) -pi/2 """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return (1 - self.args[0]**2)**(-S.Half) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.NegativeInfinity * S.ImaginaryUnit elif arg is S.NegativeInfinity: return S.Infinity * S.ImaginaryUnit elif arg is S.Zero: return S.Zero elif arg is S.One: return S.Pi / 2 elif arg is S.NegativeOne: return -S.Pi / 2 if arg.could_extract_minus_sign(): return -cls(-arg) if arg.is_number: cst_table = { sqrt(3)/2 : 3, -sqrt(3)/2 : -3, sqrt(2)/2 : 4, -sqrt(2)/2 : -4, 1/sqrt(2) : 4, -1/sqrt(2) : -4, sqrt((5-sqrt(5))/8) : 5, -sqrt((5-sqrt(5))/8) : -5, S.Half : 6, -S.Half : -6, sqrt(2-sqrt(2))/2 : 8, -sqrt(2-sqrt(2))/2 : -8, (sqrt(5)-1)/4 : 10, (1-sqrt(5))/4 : -10, (sqrt(3)-1)/sqrt(2**3) : 12, (1-sqrt(3))/sqrt(2**3) : -12, (sqrt(5)+1)/4 : S(10)/3, -(sqrt(5)+1)/4 : -S(10)/3 } if arg in cst_table: return S.Pi / cst_table[arg] i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.asinh(i_coeff) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) >= 2 and n > 2: p = previous_terms[-2] return p * (n-2)**2/(n*(n-1)) * x**2 else: k = (n - 1) // 2 R = C.RisingFactorial(S.Half, k) F = C.factorial(k) return R / F * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_rewrite_as_acos(self, x): return S.Pi/2 - acos(x) def _eval_rewrite_as_atan(self, x): return 2*atan(x/(1 + sqrt(1 - x**2))) def _eval_rewrite_as_log(self, x): return -S.ImaginaryUnit*C.log(S.ImaginaryUnit*x + sqrt(1-x**2)) def _eval_is_real(self): return self.args[0].is_real and (self.args[0]>=-1 and self.args[0]<=1) def _sage_(self): import sage.all as sage return sage.asin(self.args[0]._sage_()) class acos(Function): """ Usage ===== acos(x) -> Returns the arc cosine of x (measured in radians) Notes ===== acos(x) will evaluate automatically in the cases oo, -oo, 0, 1, -1 Examples ======== >>> from sympy import acos, oo, pi >>> acos(1) 0 >>> acos(0) pi/2 >>> acos(oo) (oo)*I """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return -(1 - self.args[0]**2)**(-S.Half) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity * S.ImaginaryUnit elif arg is S.NegativeInfinity: return S.NegativeInfinity * S.ImaginaryUnit elif arg is S.Zero: return S.Pi / 2 elif arg is S.One: return S.Zero elif arg is S.NegativeOne: return S.Pi if arg.is_number: cst_table = { S.Half : S.Pi/3, -S.Half : 2*S.Pi/3, sqrt(2)/2 : S.Pi/4, -sqrt(2)/2 : 3*S.Pi/4, 1/sqrt(2) : S.Pi/4, -1/sqrt(2) : 3*S.Pi/4, sqrt(3)/2 : S.Pi/6, -sqrt(3)/2 : 5*S.Pi/6, } if arg in cst_table: return cst_table[arg] @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.Pi / 2 elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) if len(previous_terms) >= 2 and n > 2: p = previous_terms[-2] return p * (n-2)**2/(n*(n-1)) * x**2 else: k = (n - 1) // 2 R = C.RisingFactorial(S.Half, k) F = C.factorial(k) return -R / F * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real and (self.args[0]>=-1 and self.args[0]<=1) def _eval_rewrite_as_log(self, x): return S.Pi/2 + S.ImaginaryUnit * C.log(S.ImaginaryUnit * x + sqrt(1 - x**2)) def _eval_rewrite_as_asin(self, x): return S.Pi/2 - asin(x) def _eval_rewrite_as_atan(self, x): if x > -1 and x <= 1: return 2 * atan(sqrt(1 - x**2)/(1 + x)) else: raise ValueError("The argument must be bounded in the interval (-1,1]") def _sage_(self): import sage.all as sage return sage.acos(self.args[0]._sage_()) class atan(Function): """ Usage ===== atan(x) -> Returns the arc tangent of x (measured in radians) Notes ===== atan(x) will evaluate automatically in the cases oo, -oo, 0, 1, -1 Examples ======== >>> from sympy import atan, oo, pi >>> atan(0) 0 >>> atan(1) pi/4 >>> atan(oo) pi/2 """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 1/(1+self.args[0]**2) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Pi / 2 elif arg is S.NegativeInfinity: return -S.Pi / 2 elif arg is S.Zero: return S.Zero elif arg is S.One: return S.Pi / 4 elif arg is S.NegativeOne: return -S.Pi / 4 if arg.could_extract_minus_sign(): return -cls(-arg) if arg.is_number: cst_table = { sqrt(3)/3 : 6, -sqrt(3)/3 : -6, 1/sqrt(3) : 6, -1/sqrt(3) : -6, sqrt(3) : 3, -sqrt(3) : -3, (1+sqrt(2)) : S(8)/3, -(1+sqrt(2)) : S(8)/3, (sqrt(2)-1) : 8, (1-sqrt(2)) : -8, sqrt((5+2*sqrt(5))) : S(5)/2, -sqrt((5+2*sqrt(5))) : -S(5)/2, (2-sqrt(3)) : 12, -(2-sqrt(3)) : -12 } if arg in cst_table: return S.Pi / cst_table[arg] i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return S.ImaginaryUnit * C.atanh(i_coeff) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) return (-1)**((n-1)//2) * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_rewrite_as_log(self, x): return S.ImaginaryUnit/2 * \ (C.log((S(1) - S.ImaginaryUnit * x)/(S(1) + S.ImaginaryUnit * x))) def _eval_aseries(self, n, args0, x, logx): if args0[0] == S.Infinity: return S.Pi/2 - atan(1/self.args[0]) elif args0[0] == S.NegativeInfinity: return -S.Pi/2 - atan(1/self.args[0]) else: return super(atan, self)._eval_aseries(n, args0, x, logx) def _sage_(self): import sage.all as sage return sage.atan(self.args[0]._sage_()) class acot(Function): """ Usage ===== acot(x) -> Returns the arc cotangent of x (measured in radians) """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return -1 / (1+self.args[0]**2) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Zero elif arg is S.NegativeInfinity: return S.Zero elif arg is S.Zero: return S.Pi/ 2 elif arg is S.One: return S.Pi / 4 elif arg is S.NegativeOne: return -S.Pi / 4 if arg.could_extract_minus_sign(): return -cls(-arg) if arg.is_number: cst_table = { sqrt(3)/3 : 3, -sqrt(3)/3 : -3, 1/sqrt(3) : 3, -1/sqrt(3) : -3, sqrt(3) : 6, -sqrt(3) : -6, (1+sqrt(2)) : 8, -(1+sqrt(2)) : -8, (1-sqrt(2)) : -S(8)/3, (sqrt(2)-1) : S(8)/3, sqrt(5+2*sqrt(5)) : 10, -sqrt(5+2*sqrt(5)) : -10, (2+sqrt(3)) : 12, -(2+sqrt(3)) : -12, (2-sqrt(3)) : S(12)/5, -(2-sqrt(3)) : -S(12)/5, } if arg in cst_table: return S.Pi / cst_table[arg] i_coeff = arg.as_coefficient(S.ImaginaryUnit) if i_coeff is not None: return -S.ImaginaryUnit * C.acoth(i_coeff) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n == 0: return S.Pi / 2 # FIX THIS elif n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) return (-1)**((n+1)//2) * x**n / n def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real def _eval_aseries(self, n, args0, x, logx): if args0[0] == S.Infinity: return S.Pi/2 - acot(1/self.args[0]) elif args0[0] == S.NegativeInfinity: return 3*S.Pi/2 - acot(1/self.args[0]) else: return super(atan, self)._eval_aseries(n, args0, x, logx) def _sage_(self): import sage.all as sage return sage.acot(self.args[0]._sage_()) def _eval_rewrite_as_log(self, x): return S.ImaginaryUnit/2 * \ (C.log((x - S.ImaginaryUnit)/(x + S.ImaginaryUnit))) class atan2(Function): """ atan2(y,x) -> Returns the atan(y/x) taking two arguments y and x. Signs of both y and x are considered to determine the appropriate quadrant of atan(y/x). The range is (-pi, pi]. """ nargs = 2 @classmethod def eval(cls, y, x): sign_y = C.sign(y) if y.is_zero: if x.is_positive: return S.Zero elif x.is_zero: return S.NaN elif x.is_negative: return S.Pi elif x.is_zero: if sign_y.is_Number: return sign_y * S.Pi/2 else: abs_yx = C.Abs(y/x) if sign_y.is_Number and abs_yx.is_number: phi = C.atan(abs_yx) if x.is_positive: return sign_y * phi else: return sign_y * (S.Pi - phi) def _eval_is_real(self): return self.args[0].is_real and self.args[1].is_real def fdiff(self, argindex): x, y = self.args if argindex == 1: return y/(x**2 + y**2) elif argindex == 2: return -x/(x**2 + y**2) else: raise ArgumentIndexError(self, argindex) def _sage_(self): import sage.all as sage return sage.atan2(self.args[0]._sage_(), self.args[1]._sage_()) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/piecewise.py0000644000175000017500000002570012014170666027607 0ustar georgeskgeorgeskfrom sympy.core import Basic, S, Function, diff, Number, sympify from sympy.core.relational import Equality, Relational from sympy.logic.boolalg import Boolean from sympy.core.sets import Set class ExprCondPair(Function): """Represents an expression, condition pair.""" def __new__(cls, *args, **assumptions): if isinstance(args[0], cls): expr = args[0].expr cond = args[0].cond elif len(args) == 2: expr = sympify(args[0]) cond = sympify(args[1]) else: raise TypeError("args must be a (expr, cond) pair") return Basic.__new__(cls, expr, cond, **assumptions) @property def expr(self): return self.args[0] @property def cond(self): return self.args[1] @property def is_commutative(self): return self.expr.is_commutative @property def free_symbols(self): # Overload Basic.free_symbols because self.args[1] may contain non-Basic result = self.expr.free_symbols if hasattr(self.cond, 'free_symbols'): result |= self.cond.free_symbols return result def __iter__(self): yield self.expr yield self.cond class Piecewise(Function): """ Represents a piecewise function. Usage ===== Piecewise( (expr,cond), (expr,cond), ... ) - Each argument is a 2-tuple defining a expression and condition - The conds are evaluated in turn returning the first that is True. If any of the evaluated conds are not determined explicitly False, e.g. x < 1, the function is returned in symbolic form. - If the function is evaluated at a place where all conditions are False, a ValueError exception will be raised. - Pairs where the cond is explicitly False, will be removed. Examples ======== >>> from sympy import Piecewise, log >>> from sympy.abc import x >>> f = x**2 >>> g = log(x) >>> p = Piecewise( (0, x<-1), (f, x<=1), (g, True)) >>> p.subs(x,1) 1 >>> p.subs(x,5) log(5) """ nargs = None is_Piecewise = True def __new__(cls, *args, **options): # (Try to) sympify args first newargs = [] for ec in args: pair = ExprCondPair(*ec) cond_type = type(pair.cond) if not (cond_type is bool or issubclass(cond_type, Relational) or \ issubclass(cond_type, Number) or \ issubclass(cond_type, Set) or issubclass(cond_type, Boolean)): raise TypeError( "Cond %s is of type %s, but must be a bool," \ " Relational, Number or Set" % (pair.cond, cond_type)) newargs.append(pair) if options.pop('evaluate', True): r = cls.eval(*newargs) else: r = None if r is None: return Basic.__new__(cls, *newargs, **options) else: return r def __getnewargs__(self): # Convert ExprCondPair objects to tuples. args = [] for expr, condition in self.args: args.append((expr, condition)) return tuple(args) @classmethod def eval(cls, *args): # Check for situations where we can evaluate the Piecewise object. # 1) Hit an unevaluable cond (e.g. x<1) -> keep object # 2) Hit a true condition -> return that expr # 3) Remove false conditions, if no conditions left -> raise ValueError all_conds_evaled = True # Do all conds eval to a bool? piecewise_again = False # Should we pass args to Piecewise again? non_false_ecpairs = [] for expr, cond in args: # Check here if expr is a Piecewise and collapse if one of # the conds in expr matches cond. This allows the collapsing # of Piecewise((Piecewise(x,x<0),x<0)) to Piecewise((x,x<0)). # This is important when using piecewise_fold to simplify # multiple Piecewise instances having the same conds. # Eventually, this code should be able to collapse Piecewise's # having different intervals, but this will probably require # using the new assumptions. if isinstance(expr, Piecewise): for e, c in expr.args: # Don't collapse if cond is "True" as this leads to # incorrect simplifications with nested Piecewises. if c == cond and cond is not True: expr = e piecewise_again = True cond_eval = cls.__eval_cond(cond) if cond_eval is None: all_conds_evaled = False non_false_ecpairs.append( (expr, cond) ) elif cond_eval: if all_conds_evaled: return expr non_false_ecpairs.append( (expr, cond) ) if len(non_false_ecpairs) != len(args) or piecewise_again: return Piecewise(*non_false_ecpairs) return None def doit(self, **hints): newargs = [] for e, c in self.args: if hints.get('deep', True): if isinstance(e, Basic): e = e.doit(**hints) if isinstance(c, Basic): c = c.doit(**hints) newargs.append((e, c)) return Piecewise(*newargs) def _eval_integral(self,x): from sympy.integrals import integrate return Piecewise(*[(integrate(e, x), c) for e, c in self.args]) def _eval_interval(self, sym, a, b): """Evaluates the function along the sym in a given interval ab""" # FIXME: Currently complex intervals are not supported. A possible # replacement algorithm, discussed in issue 2128, can be found in the # following papers; # http://portal.acm.org/citation.cfm?id=281649 # http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.70.4127&rep=rep1&type=pdf int_expr = [] mul = 1 if a > b: a, b, mul = b, a, -1 default = None # Determine what intervals the expr,cond pairs affect. # 1) If cond is True, then log it as default # 1.1) Currently if cond can't be evaluated, throw NotImplentedError. # 2) For each inequality, if previous cond defines part of the interval # update the new conds interval. # - eg x < 1, x < 3 -> [oo,1],[1,3] instead of [oo,1],[oo,3] # 3) Sort the intervals to make it easier to find correct exprs for expr, cond in self.args: if isinstance(cond, bool) or cond.is_Number: if cond: default = expr break else: continue elif isinstance(cond, Equality): continue curr = list(cond.args) if cond.args[0].has(sym): curr[0] = S.NegativeInfinity elif cond.args[1].has(sym): curr[1] = S.Infinity else: raise NotImplementedError(\ "Unable handle interval evaluation of expression.") curr = [max(a, curr[0]), min(b, curr[1])] for n in xrange(len(int_expr)): if self.__eval_cond(curr[0] < int_expr[n][1]) and \ self.__eval_cond(curr[0] >= int_expr[n][0]): curr[0] = int_expr[n][1] if self.__eval_cond(curr[1] > int_expr[n][0]) and \ self.__eval_cond(curr[1] <= int_expr[n][1]): curr[1] = int_expr[n][0] if self.__eval_cond(curr[0] < curr[1]): int_expr.append(curr + [expr]) int_expr.sort(key=lambda x:x[0]) # Add holes to list of intervals if there is a default value, # otherwise raise a ValueError. holes = [] curr_low = a for int_a, int_b, expr in int_expr: if curr_low < int_a: holes.append([curr_low, min(b, int_a), default]) curr_low = int_b if curr_low > b: break if curr_low < b: holes.append([curr_low, b, default]) if holes and default is not None: int_expr.extend(holes) elif holes and default == None: raise ValueError("Called interval evaluation over piecewise " \ "function on undefined intervals %s" % \ ", ".join([str((h[0], h[1])) for h in holes])) # Finally run through the intervals and sum the evaluation. ret_fun = 0 for int_a, int_b, expr in int_expr: ret_fun += expr._eval_interval(sym, max(a, int_a), min(b, int_b)) return mul * ret_fun def _eval_derivative(self, s): return Piecewise(*[(diff(e, s), c) for e, c in self.args]) def _eval_subs(self, old, new): if self == old: return new new_args = [] for e, c in self.args: if isinstance(c, bool): new_args.append((e._eval_subs(old, new), c)) elif isinstance(c, Set): # What do we do if there are more than one symbolic # variable. Which do we put pass to Set.contains? new_args.append((e._eval_subs(old, new), c.contains(new))) else: new_args.append((e._eval_subs(old, new), c._eval_subs(old, new))) return Piecewise( *new_args ) def _eval_nseries(self, x, n, logx): args = map(lambda ec: (ec.expr._eval_nseries(x, n, logx), ec.cond), \ self.args) return self.func(*args) @classmethod def __eval_cond(cls, cond): """Returns S.One if True, S.Zero if False, or None if undecidable.""" if type(cond) == bool or cond.is_number: if cond: return S.One else: return S.Zero elif type(cond) == Set: return None return None def piecewise_fold(expr): """ Takes an expression containing a piecewise function and returns the expression in piecewise form. >>> from sympy import Piecewise, piecewise_fold >>> from sympy.abc import x >>> p = Piecewise((x, x < 1), (1, 1 <= x)) >>> piecewise_fold(x*p) Piecewise((x**2, x < 1), (x, 1 <= x)) """ if not isinstance(expr, Basic) or not expr.has(Piecewise): return expr new_args = map(piecewise_fold, expr.args) if expr.func is ExprCondPair: return ExprCondPair(*new_args) piecewise_args = [] for n, arg in enumerate(new_args): if arg.func is Piecewise: piecewise_args.append(n) if len(piecewise_args) > 0: n = piecewise_args[0] new_args = [(expr.func(*(new_args[:n] + [e] + new_args[n+1:])), c) \ for e, c in new_args[n].args] if len(piecewise_args) > 1: return piecewise_fold(Piecewise(*new_args)) return Piecewise(*new_args) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/miscellaneous.py0000644000175000017500000002106412014170666030474 0ustar georgeskgeorgeskfrom sympy.core import S, C, sympify from sympy.core.basic import Basic from sympy.core.containers import Tuple from sympy.core.operations import LatticeOp, ShortCircuit from sympy.core.function import Application, Lambda from sympy.core.expr import Expr from sympy.core.singleton import Singleton class IdentityFunction(Lambda): """The identity function >>> from sympy import Id, Symbol >>> x = Symbol('x') >>> Id(x) x """ __metaclass__ = Singleton __slots__ = [] nargs = 1 def __new__(cls): x = C.Dummy('x') #construct "by hand" to avoid infinite loop return Expr.__new__(cls, Tuple(x), x) Id = S.IdentityFunction ############################################################################### ############################# SQUARE ROOT FUNCTION ############################ ############################################################################### def sqrt(arg): # arg = sympify(arg) is handled by Pow return C.Pow(arg, S.Half) ############################################################################### ############################# MINIMUM and MAXIMUM ############################# ############################################################################### class MinMaxBase(LatticeOp): def __new__(cls, *args, **assumptions): if not args: raise ValueError("The Max/Min functions must have arguments.") args = (sympify(arg) for arg in args) # first standard filter, for cls.zero and cls.identity # also reshape Max(a, Max(b, c)) to Max(a, b, c) try: _args = frozenset(cls._new_args_filter(args)) except ShortCircuit: return cls.zero # second filter # variant I: remove ones which can be removed # args = cls._collapse_arguments(set(_args), **assumptions) # variant II: find local zeros args = cls._find_localzeros(set(_args), **assumptions) _args = frozenset(args) if not _args: return cls.identity elif len(_args) == 1: return set(_args).pop() else: # base creation obj = Expr.__new__(cls, _args, **assumptions) obj._argset = _args return obj @classmethod def _new_args_filter(cls, arg_sequence): """ Generator filtering args. first standard filter, for cls.zero and cls.identity. Also reshape Max(a, Max(b, c)) to Max(a, b, c), and check arguments for comparability """ for arg in arg_sequence: # pre-filter, checking comparability of arguments if (arg.is_real == False) or (arg is S.ComplexInfinity): raise ValueError("The argument '%s' is not comparable." % arg) if arg == cls.zero: raise ShortCircuit(arg) elif arg == cls.identity: continue elif arg.func == cls: for x in arg.iter_basic_args(): yield x else: yield arg @classmethod def _find_localzeros(cls, values, **options): """ Sequentially allocate values to localzeros. If value is greter than all of the localzeros, then it is new localzero and it is apending to them. if value is greter than one of the localzeros, then update localzero's set. """ localzeros = set() for v in values: is_newzero = True for z in localzeros: if id(v) == id(z): is_newzero = False elif cls._is_connected(v, z): is_newzero = False if cls._is_asneeded(v, z): localzeros.remove(z) localzeros.update([v]) break if is_newzero: localzeros.update([v]) return localzeros @classmethod def _is_connected(cls, x, y): """ Check if x and y are connected somehow. """ if (x == y) or isinstance(x > y, bool) or isinstance(x < y, bool): return True if x.is_Number and y.is_Number: return True return False @classmethod def _is_asneeded(cls, x, y): """ Check if x and y satisfy relation condition. The relation condition for Max function is x > y, for Min function is x < y. They are defined in children Max and Min classes through the method _rel(cls, x, y) """ if (x == y): return False if x.is_Number and y.is_Number: if cls._rel(x, y): return True xy = cls._rel(x, y) if isinstance(xy, bool): if xy: return True return False yx = cls._rel_inversed(x, y) if isinstance(yx, bool): if yx: return False # never occurs? return True return False class Max(MinMaxBase, Application, Basic): """ Return, if possible, the maximum value of the list. When number of arguments is equal one, then return this argument. When number of arguments is equal two, then return, if possible, the value from (a, b) that is >= the other. In common case, when the length of list greater than 2, the task is more complicated. Return only the arguments, which are greater than others, if it is possible to determine directional relation. If is not possible to determine such a relation, return a partially evaluated result. Assumptions are used to make the decision too. Also, only comparable arguments are permitted. Example ------- >>> from sympy import Max, Symbol, oo >>> from sympy.abc import x, y >>> p = Symbol('p', positive=True) >>> n = Symbol('n', negative=True) >>> Max(x, -2) #doctest: +SKIP Max(x, -2) >>> Max(x, -2).subs(x, 3) 3 >>> Max(p, -2) p >>> Max(x, y) #doctest: +SKIP Max(x, y) >>> Max(x, y) == Max(y, x) True >>> Max(x, Max(y, z)) #doctest: +SKIP Max(x, y, z) >>> Max(n, 8, p, 7, -oo) #doctest: +SKIP Max(8, p) >>> Max (1, x, oo) oo Algorithm --------- The task can be considered as searching of supremums in the directed complete partial orders [1]_. The source values are sequentially allocated by the isolated subsets in which supremums are searched and result as Max arguments. If the resulted supremum is single, then it is returned. The isolated subsets are the sets of values which are only the comparable with each other in the current set. E.g. natural numbers are comparable with each other, but not comparable with the `x` symbol. Another example: the symbol `x` with negative assumption is comparable with a natural number. Also there are "least" elements, which are comparable with all others, and have a zero property (maximum or minimum for all elements). E.g. `oo`. In case of it the allocation operation is terminated and only this value is returned. Assumption: - if A > B > C then A > C - if A==B then B can be removed [1] http://en.wikipedia.org/wiki/Directed_complete_partial_order [2] http://en.wikipedia.org/wiki/Lattice_(order) See Also -------- Min() : find minimum values """ zero = S.Infinity identity = S.NegativeInfinity @classmethod def _rel(cls, x, y): """ Check if x > y. """ return (x > y) @classmethod def _rel_inversed(cls, x, y): """ Check if x < y. """ return (x < y) class Min(MinMaxBase, Application, Basic): """ Return, if possible, the minimum value of the list. Example ------- >>> from sympy import Min, Symbol, oo >>> from sympy.abc import x, y >>> p = Symbol('p', positive=True) >>> n = Symbol('n', negative=True) >>> Min(x, -2) #doctest: +SKIP Min(x, -2) >>> Min(x, -2).subs(x, 3) -2 >>> Min(p, -3) -3 >>> Min(x, y) #doctest: +SKIP Min(x, y) >>> Min(n, 8, p, -7, p, oo) #doctest: +SKIP Min(n, -7) See Also -------- Max() : find maximum values """ zero = S.NegativeInfinity identity = S.Infinity @classmethod def _rel(cls, x, y): """ Check if x < y. """ return (x < y) @classmethod def _rel_inversed(cls, x, y): """ Check if x > y. """ return (x > y) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/exponential.py0000644000175000017500000004056512014170666030166 0ustar georgeskgeorgeskfrom sympy.core import C, sympify from sympy.core.add import Add from sympy.core.function import Lambda, Function, ArgumentIndexError from sympy.core.cache import cacheit from sympy.core.singleton import S from sympy.core.symbol import Wild, Dummy from sympy.core.mul import Mul from sympy.ntheory import multiplicity, perfect_power # NOTE IMPORTANT # The series expansion code in this file is an important part of the gruntz # algorithm for determining limits. _eval_nseries has to return a generalised # power series with coefficients in C(log(x), log). # In more detail, the result of _eval_nseries(self, x, n) must be # c_0*x**e_0 + ... (finitely many terms) # where e_i are numbers (not necessarily integers) and c_i involve only # numbers, the function log, and log(x). [This also means it must not contain # log(x(1+p)), this *has* to be expanded to log(x)+log(1+p) if x.is_positive and # p.is_positive.] class exp(Function): nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return self else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return log def as_numer_denom(self): c, t = self.args[0].as_coeff_mul() if c.is_negative: return S.One, exp(-self.args[0]) return self, S.One @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Zero: return S.One elif arg is S.One: return S.Exp1 elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Zero elif arg.func is log: return arg.args[0] elif arg.is_Mul: coeff = arg.as_coefficient(S.Pi*S.ImaginaryUnit) if coeff is not None: if (2*coeff).is_integer: if coeff.is_even: return S.One elif coeff.is_odd: return S.NegativeOne elif (coeff + S.Half).is_even: return -S.ImaginaryUnit elif (coeff + S.Half).is_odd: return S.ImaginaryUnit I = S.ImaginaryUnit oo = S.Infinity a = Wild("a", exclude=[I, oo]) r = arg.match(I*a*oo) if r and r[a] != 0: return S.NaN args = Add.make_args(arg) included, excluded = [], [] for arg in args: coeff, terms = arg.as_coeff_mul() if coeff is S.Infinity: excluded.append(coeff**Mul(*terms)) else: coeffs, log_term = [coeff], None for term in terms: if term.func is log: if log_term is None: log_term = term.args[0] else: log_term = None break elif term.is_comparable: coeffs.append(term) else: log_term = None break if log_term is not None: excluded.append(log_term**Mul(*coeffs)) else: included.append(arg) if excluded: return Mul(*(excluded + [cls(Add(*included))])) @property def base(self): return S.Exp1 @property def exp(self): return self.args[0] @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n<0: return S.Zero if n==0: return S.One x = sympify(x) if previous_terms: p = previous_terms[-1] if p is not None: return p * x / n return x**n/C.factorial()(n) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def as_real_imag(self, deep=True, **hints): re, im = self.args[0].as_real_imag() if deep: re = re.expand(deep, **hints) im = im.expand(deep, **hints) cos, sin = C.cos(im), C.sin(im) return (exp(re)*cos, exp(re)*sin) def _eval_conjugate(self): return self.func(self.args[0].conjugate()) def as_base_exp(self): return S.Exp1, Mul(*self.args) def _eval_subs(self, old, new): if self == old: return new arg = self.args[0] o = old if old.is_Pow: # handle (exp(3*log(x))).subs(x**2, z) -> z**(3/2) old = exp(old.exp*log(old.base)) if old.func is exp: # exp(a*expr) .subs( exp(b*expr), y ) -> y ** (a/b) a, expr_terms = self.args[0].as_coeff_mul() b, expr_terms_= old.args[0].as_coeff_mul() if expr_terms == expr_terms_: return new**(a/b) if arg.is_Add: # exp(2*x+a).subs(exp(3*x),y) -> y**(2/3) * exp(a) # exp(exp(x) + exp(x**2)).subs(exp(exp(x)), w) -> w * exp(exp(x**2)) oarg = old.args[0] new_l = [] old_al = [] coeff2, terms2 = oarg.as_coeff_mul() for a in arg.args: a = a._eval_subs(old, new) coeff1, terms1 = a.as_coeff_mul() if terms1 == terms2: new_l.append(new**(coeff1/coeff2)) else: old_al.append(a._eval_subs(old, new)) if new_l: new_l.append(self.func(Add(*old_al))) r = Mul(*new_l) return r if old is S.Exp1: # treat this however Pow is being treated u = C.Dummy('u') return (u**self.args[0]).subs(u, new) old = o return Function._eval_subs(self, old, new) def _eval_is_real(self): return self.args[0].is_real def _eval_is_positive(self): if self.args[0].is_real: return True def _eval_is_bounded(self): arg = self.args[0] if arg.is_unbounded: if arg.is_negative: return True if arg.is_positive: return False if arg.is_bounded: return True def _eval_is_zero(self): return (self.args[0] is S.NegativeInfinity) def _eval_power(b, e): """exp(b[0])**e -> exp(b[0]*e)""" return exp(b.args[0] * e) def _eval_lseries(self, x): s = self.args[0] yield exp(s.subs(x, 0)) from sympy import integrate t = Dummy("t") f = s.subs(x, t) for term in (exp(f)*f.diff(t)).lseries(t): yield integrate(term, (t, 0, x)) def _eval_nseries(self, x, n, logx): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. from sympy import limit, oo, powsimp arg = self.args[0] arg_series = arg._eval_nseries(x, n=n, logx=logx) if arg_series.is_Order: return 1 + arg_series arg0 = limit(arg_series.removeO(), x, 0) if arg0 in [-oo, oo]: return self t = Dummy("t") exp_series = exp(t)._taylor(t, n) o = exp_series.getO() exp_series = exp_series.removeO() r = exp(arg0)*exp_series.subs(t, arg_series - arg0) r += C.Order(o.expr.subs(t, (arg_series - arg0)), x) r = r.expand() return powsimp(r, deep=True, combine='exp') def _taylor(self, x, n): l = [] g = None for i in xrange(n): g = self.taylor_term(i, self.args[0], g) g = g.nseries(x, n=n) l.append(g) return Add(*l) + C.Order(x**n, x) def _eval_as_leading_term(self, x): arg = self.args[0] if arg.is_Add: return Mul(*[exp(f).as_leading_term(x) for f in arg.args]) arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return S.One return exp(arg) def _eval_expand_power_exp(self, deep=True, **hints): if deep: arg = self.args[0].expand(deep=deep, **hints) else: arg = self.args[0] if arg.is_Add and arg.is_commutative: expr = 1 for x in arg.args: if deep: x = x.expand(deep=deep, **hints) expr *= self.func(x) return expr return self.func(arg) def _eval_rewrite_as_sin(self, arg): I = S.ImaginaryUnit return C.sin(I*arg+S.Pi/2) - I*C.sin(I*arg) def _eval_rewrite_as_cos(self, arg): I = S.ImaginaryUnit return C.cos(I*arg) + I*C.cos(I*arg+S.Pi/2) def _sage_(self): import sage.all as sage return sage.exp(self.args[0]._sage_()) class log(Function): nargs = (1,2) def fdiff(self, argindex=1): if argindex == 1: return 1/self.args[0] s = C.Dummy('x') return Lambda(s**(-1), s) else: raise ArgumentIndexError(self, argindex) def inverse(self, argindex=1): return exp @classmethod def eval(cls, arg, base=None): if base is not None: base = sympify(base) if arg.is_positive and arg.is_Integer and \ base.is_positive and base.is_Integer: base = int(base) arg = int(arg) n = multiplicity(base, arg) return S(n) + log(arg // base ** n) / log(base) if base is not S.Exp1: return cls(arg)/cls(base) else: return cls(arg) arg = sympify(arg) if arg.is_Number: if arg is S.Zero: return S.ComplexInfinity elif arg is S.One: return S.Zero elif arg is S.Infinity: return S.Infinity elif arg is S.NegativeInfinity: return S.Infinity elif arg is S.NaN: return S.NaN elif arg.is_negative: return S.Pi * S.ImaginaryUnit + cls(-arg) elif arg.is_Rational: if arg.q != 1: return cls(arg.p) - cls(arg.q) # remove perfect powers automatically p = perfect_power(int(arg)) if p is not False: return p[1]*cls(p[0]) elif arg is S.ComplexInfinity: return S.ComplexInfinity elif arg is S.Exp1: return S.One elif arg.func is exp and arg.args[0].is_real: return arg.args[0] #don't autoexpand Pow or Mul (see the issue 252): elif not arg.is_Add: coeff = arg.as_coefficient(S.ImaginaryUnit) if coeff is not None: if coeff is S.Infinity: return S.Infinity elif coeff is S.NegativeInfinity: return S.Infinity elif coeff.is_Rational: if coeff.is_nonnegative: return S.Pi * S.ImaginaryUnit * S.Half + cls(coeff) else: return -S.Pi * S.ImaginaryUnit * S.Half + cls(-coeff) def as_base_exp(self): return self, S.One @staticmethod @cacheit def taylor_term(n, x, *previous_terms): # of log(1+x) from sympy import powsimp if n<0: return S.Zero x = sympify(x) if n==0: return x if previous_terms: p = previous_terms[-1] if p is not None: return powsimp((-n) * p * x / (n+1), deep=True, combine='exp') return (1-2*(n%2)) * x**(n+1)/(n+1) def _eval_expand_log(self, deep=True, **hints): force = hints.get('force', False) if deep: arg = self.args[0].expand(deep=deep, **hints) else: arg = self.args[0] if arg.is_Mul: expr = [] nonpos = [] for x in arg.args: if deep: x = x.expand(deep=deep, **hints) if force or x.is_positive: expr.append(self.func(x)._eval_expand_log(deep=deep, **hints)) else: nonpos.append(x) return Add(*expr) + log(Mul(*nonpos)) elif arg.is_Pow: if force or arg.exp.is_real and arg.base.is_positive: if deep: b = arg.base.expand(deep=deep, **hints) e = arg.exp.expand(deep=deep, **hints) else: b = arg.base e = arg.exp return e * self.func(b)._eval_expand_log(deep=deep,\ **hints) return self.func(arg) def as_real_imag(self, deep=True, **hints): if deep: abs = C.Abs(self.args[0].expand(deep, **hints)) arg = C.arg(self.args[0].expand(deep, **hints)) else: abs = C.Abs(self.args[0]) arg = C.arg(self.args[0]) if hints.get('log', False): # Expand the log hints['complex'] = False return (log(abs).expand(deep, **hints), arg) else: return (log(abs), arg) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def _eval_is_real(self): return self.args[0].is_positive def _eval_is_bounded(self): arg = self.args[0] if arg.is_infinitesimal: return False return arg.is_bounded def _eval_is_positive(self): arg = self.args[0] if arg.is_positive: if arg.is_unbounded: return True if arg.is_infinitesimal: return False if arg.is_Number: return arg>1 def _eval_is_zero(self): # XXX This is not quite useless. Try evaluating log(0.5).is_negative # without it. There's probably a nicer way though. if self.args[0] is S.One: return True elif self.args[0].is_number: return self.args[0].expand() is S.One elif self.args[0].is_negative: return False def _eval_nseries(self, x, n, logx): # NOTE Please see the comment at the beginning of this file, labelled # IMPORTANT. from sympy import cancel if not logx: logx = log(x) if self.args[0] == x: return logx arg = self.args[0] k, l = Wild("k"), Wild("l") r = arg.match(k*x**l) if r is not None: #k = r.get(r, S.One) #l = r.get(l, S.Zero) k, l = r[k], r[l] if l != 0 and not l.has(x) and not k.has(x): r = log(k) + l*logx # XXX true regardless of assumptions? return r # TODO new and probably slow s = self.args[0].nseries(x, n=n, logx=logx) while s.is_Order: n += 1 s = self.args[0].nseries(x, n=n, logx=logx) a, b = s.leadterm(x) p = cancel(s/(a*x**b) - 1) g = None l = [] for i in xrange(n + 2): g = log.taylor_term(i, p, g) g = g.nseries(x, n=n, logx=logx) l.append(g) return log(a) + b*logx + Add(*l) + C.Order(p**n, x) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if arg is S.One: return (self.args[0] - 1).as_leading_term(x) return self.func(arg) def _sage_(self): import sage.all as sage return sage.log(self.args[0]._sage_()) class LambertW(Function): """Lambert W function, defined as the inverse function of x*exp(x). This function represents the principal branch of this inverse function, which like the natural logarithm is multivalued. For more information, see: http://en.wikipedia.org/wiki/Lambert_W_function """ nargs = 1 @classmethod def eval(cls, x): if x == S.Zero: return S.Zero if x == S.Exp1: return S.One if x == -1/S.Exp1: return S.NegativeOne if x == -log(2)/2: return -log(2) if x == S.Infinity: return S.Infinity def fdiff(self, argindex=1): if argindex == 1: x = self.args[0] return LambertW(x)/(x*(1+LambertW(x))) else: raise ArgumentIndexError(self, argindex) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/0000755000175000017500000000000012014170666026416 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_interface.py0000644000175000017500000000363112014170666031772 0ustar georgeskgeorgesk# This test file tests the SymPy function interface, that people use to create # their own new functions. It should be as easy as possible. from sympy import Function, sympify, sin, cos, limit, tanh from sympy.abc import x def test_function_series1(): """Create our new "sin" function.""" class my_function(Function): nargs = 1 def fdiff(self, argindex = 1): return cos(self.args[0]) @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(0) #Test that the taylor series is correct assert my_function(x).series(x, 0, 10) == sin(x).series(x, 0, 10) assert limit(my_function(x)/x, x, 0) == 1 def test_function_series2(): """Create our new "cos" function.""" class my_function2(Function): nargs = 1 def fdiff(self, argindex = 1): return -sin(self.args[0]) @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(1) #Test that the taylor series is correct assert my_function2(x).series(x, 0, 10) == cos(x).series(x, 0, 10) def test_function_series3(): """ Test our easy "tanh" function. This test tests two things: * that the Function interface works as expected and it's easy to use * that the general algorithm for the series expansion works even when the derivative is defined recursively in terms of the original function, since tanh(x).diff(x) == 1-tanh(x)**2 """ class mytanh(Function): nargs = 1 def fdiff(self, argindex = 1): return 1-mytanh(self.args[0])**2 @classmethod def eval(cls, arg): arg = sympify(arg) if arg == 0: return sympify(0) e = tanh(x) f = mytanh(x) assert tanh(x).series(x, 0, 6) == mytanh(x).series(x, 0, 6) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_miscellaneous.py0000644000175000017500000001036312014170666032675 0ustar georgeskgeorgeskfrom sympy import S from sympy.core.symbol import Symbol from sympy.utilities.pytest import raises from sympy.functions.elementary.miscellaneous import Min, Max from sympy import I, cos, sin, oo def test_Min(): from sympy.abc import x, y, z n = Symbol('n', negative=True) n_ = Symbol('n_', negative=True) nn = Symbol('nn', nonnegative=True) nn_ = Symbol('nn_', nonnegative=True) p = Symbol('p', positive=True) p_ = Symbol('p_', positive=True) np = Symbol('np', nonpositive=True) np_ = Symbol('np_', nonpositive=True) assert Min(5, 4) == 4 assert Min(-oo, -oo) == -oo assert Min(-oo, n) == -oo assert Min(n, -oo) == -oo assert Min(-oo, np) == -oo assert Min(np, -oo) == -oo assert Min(-oo, 0) == -oo assert Min(0, -oo) == -oo assert Min(-oo, nn) == -oo assert Min(nn, -oo) == -oo assert Min(-oo, p) == -oo assert Min(p, -oo) == -oo assert Min(-oo, oo) == -oo assert Min(oo, -oo) == -oo assert Min(n, n) == n assert Min(n, np) == Min(n, np) assert Min(np, n) == Min(np, n) assert Min(n, 0) == n assert Min(0, n) == n assert Min(n, nn) == n assert Min(nn, n) == n assert Min(n, p) == n assert Min(p, n) == n assert Min(n, oo) == n assert Min(oo, n) == n assert Min(np, np) == np assert Min(np, 0) == np assert Min(0, np) == np assert Min(np, nn) == np assert Min(nn, np) == np assert Min(np, p) == np assert Min(p, np) == np assert Min(np, oo) == np assert Min(oo, np) == np assert Min(0, 0) == 0 assert Min(0, nn) == 0 assert Min(nn, 0) == 0 assert Min(0, p) == 0 assert Min(p, 0) == 0 assert Min(0, oo) == 0 assert Min(oo, 0) == 0 assert Min(nn, nn) == nn assert Min(nn, p) == Min(nn, p) assert Min(p, nn) == Min(p, nn) assert Min(nn, oo) == nn assert Min(oo, nn) == nn assert Min(p, p) == p assert Min(p, oo) == p assert Min(oo, p) == p assert Min(oo, oo) == oo assert Min(n, n_).func is Min assert Min(nn, nn_).func is Min assert Min(np, np_).func is Min assert Min(p, p_).func is Min # lists raises(ValueError, 'Min()') assert Min(x, y) == Min(y, x) assert Min(x, y, z) == Min(z, y, x) assert Min(x, Min(y, z)) == Min(z, y, x) assert Min(x, Max(y, -oo)) == Min(x, y) assert Min(p, oo, n, p, p, p_) == n assert Min(n, oo, -7, p, p, 2) == Min(n, -7) assert Min(2, x, p, n, oo, n_, p, 2, -2, -2) == Min(-2, x, n, n_) assert Min(0, x, 1, y) == Min(0, x, y) assert Min(1000, 100, -100, x, p, n) == Min(n, x, -100) assert Min(cos(x), sin(x)) == Min(cos(x), sin(x)) assert Min(cos(x), sin(x)).subs(x, 1) == cos(1) assert Min(cos(x), sin(x)).subs(x, S(1)/2) == sin(S(1)/2) raises(ValueError, 'Min(cos(x), sin(x)).subs(x, I)') raises(ValueError, 'Min(I)') raises(ValueError, 'Min(I, x)') raises(ValueError, 'Min(S.ComplexInfinity, x)') def test_Max(): from sympy.abc import x, y, z n = Symbol('n', negative=True) n_ = Symbol('n_', negative=True) nn = Symbol('nn', nonnegative=True) nn_ = Symbol('nn_', nonnegative=True) p = Symbol('p', positive=True) p_ = Symbol('p_', positive=True) np = Symbol('np', nonpositive=True) np_ = Symbol('np_', nonpositive=True) assert Max(5, 4) == 5 # lists raises(ValueError, 'Max()') assert Max(x, y) == Max(y, x) assert Max(x, y, z) == Max(z, y, x) assert Max(x, Max(y, z)) == Max(z, y, x) assert Max(x, Min(y, oo)) == Max(x, y) assert Max(n, -oo, n_, p, 2) == Max(p, 2) assert Max(n, -oo, n_, p) == p assert Max(2, x, p, n, -oo, S.NegativeInfinity, n_, p, 2) == Max(2, x, p) assert Max(0, x, 1, y) == Max(1, x, y) assert Max(x, x + 1, x - 1) == 1 + x assert Max(1000, 100, -100, x, p, n) == Max(p, x, 1000) assert Max(cos(x), sin(x)) == Max(sin(x), cos(x)) assert Max(cos(x), sin(x)).subs(x, 1) == sin(1) assert Max(cos(x), sin(x)).subs(x, S(1)/2) == cos(S(1)/2) raises(ValueError, 'Max(cos(x), sin(x)).subs(x, I)') raises(ValueError, 'Max(I)') raises(ValueError, 'Max(I, x)') raises(ValueError, 'Max(S.ComplexInfinity, 1)') # interesting: # Max(n, -oo, n_, p, 2) == Max(p, 2) # True # Max(n, -oo, n_, p, 1000) == Max(p, 1000) # False wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_hyperbolic.py0000644000175000017500000003234112014170666032172 0ustar georgeskgeorgeskfrom sympy import symbols, Symbol, sinh, nan, oo, zoo, pi, asinh, acosh, log, sqrt, \ coth, I, cot, E, tanh, tan, cosh, cos, S, sin, Rational, atanh, acoth, \ Integer, O, exp from sympy.utilities.pytest import XFAIL def test_sinh(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert sinh(nan) == nan assert sinh(zoo) == nan assert sinh(oo) == oo assert sinh(-oo) == -oo assert sinh(0) == 0 assert sinh(1) == sinh(1) assert sinh(-1) == -sinh(1) assert sinh(x) == sinh(x) assert sinh(-x) == -sinh(x) assert sinh(pi) == sinh(pi) assert sinh(-pi) == -sinh(pi) assert sinh(2**1024 * E) == sinh(2**1024 * E) assert sinh(-2**1024 * E) == -sinh(2**1024 * E) assert sinh(pi*I) == 0 assert sinh(-pi*I) == 0 assert sinh(2*pi*I) == 0 assert sinh(-2*pi*I) == 0 assert sinh(-3*10**73*pi*I) == 0 assert sinh(7*10**103*pi*I) == 0 assert sinh(pi*I/2) == I assert sinh(-pi*I/2) == -I assert sinh(5*pi*I/2) == I assert sinh(7*pi*I/2) == -I assert sinh(pi*I/3) == S.Half*sqrt(3)*I assert sinh(-2*pi*I/3) == -S.Half*sqrt(3)*I assert sinh(pi*I/4) == S.Half*sqrt(2)*I assert sinh(-pi*I/4) == -S.Half*sqrt(2)*I assert sinh(17*pi*I/4) == S.Half*sqrt(2)*I assert sinh(-3*pi*I/4) == -S.Half*sqrt(2)*I assert sinh(pi*I/6) == S.Half*I assert sinh(-pi*I/6) == -S.Half*I assert sinh(7*pi*I/6) == -S.Half*I assert sinh(-5*pi*I/6) == -S.Half*I assert sinh(pi*I/105) == sin(pi/105)*I assert sinh(-pi*I/105) == -sin(pi/105)*I assert sinh(2 + 3*I) == sinh(2 + 3*I) assert sinh(x*I) == sin(x)*I assert sinh(k*pi*I) == 0 assert sinh(17*k*pi*I) == 0 assert sinh(k*pi*I/2) == sin(k*pi/2)*I def test_cosh(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert cosh(nan) == nan assert cosh(zoo) == nan assert cosh(oo) == oo assert cosh(-oo) == oo assert cosh(0) == 1 assert cosh(1) == cosh(1) assert cosh(-1) == cosh(1) assert cosh(x) == cosh(x) assert cosh(-x) == cosh(x) assert cosh(pi*I) == cos(pi) assert cosh(-pi*I) == cos(pi) assert cosh(2**1024 * E) == cosh(2**1024 * E) assert cosh(-2**1024 * E) == cosh(2**1024 * E) assert cosh(pi*I/2) == 0 assert cosh(-pi*I/2) == 0 assert cosh(pi*I/2) == 0 assert cosh(-pi*I/2) == 0 assert cosh((-3*10**73+1)*pi*I/2) == 0 assert cosh((7*10**103+1)*pi*I/2) == 0 assert cosh(pi*I) == -1 assert cosh(-pi*I) == -1 assert cosh(5*pi*I) == -1 assert cosh(8*pi*I) == 1 assert cosh(pi*I/3) == S.Half assert cosh(-2*pi*I/3) == -S.Half assert cosh(pi*I/4) == S.Half*sqrt(2) assert cosh(-pi*I/4) == S.Half*sqrt(2) assert cosh(11*pi*I/4) == -S.Half*sqrt(2) assert cosh(-3*pi*I/4) == -S.Half*sqrt(2) assert cosh(pi*I/6) == S.Half*sqrt(3) assert cosh(-pi*I/6) == S.Half*sqrt(3) assert cosh(7*pi*I/6) == -S.Half*sqrt(3) assert cosh(-5*pi*I/6) == -S.Half*sqrt(3) assert cosh(pi*I/105) == cos(pi/105) assert cosh(-pi*I/105) == cos(pi/105) assert cosh(2 + 3*I) == cosh(2 + 3*I) assert cosh(x*I) == cos(x) assert cosh(k*pi*I) == cos(k*pi) assert cosh(17*k*pi*I) == cos(17*k*pi) assert cosh(k*pi) == cosh(k*pi) def test_tanh(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert tanh(nan) == nan assert tanh(zoo) == nan assert tanh(oo) == 1 assert tanh(-oo) == -1 assert tanh(0) == 0 assert tanh(1) == tanh(1) assert tanh(-1) == -tanh(1) assert tanh(x) == tanh(x) assert tanh(-x) == -tanh(x) assert tanh(pi) == tanh(pi) assert tanh(-pi) == -tanh(pi) assert tanh(2**1024 * E) == tanh(2**1024 * E) assert tanh(-2**1024 * E) == -tanh(2**1024 * E) assert tanh(pi*I) == 0 assert tanh(-pi*I) == 0 assert tanh(2*pi*I) == 0 assert tanh(-2*pi*I) == 0 assert tanh(-3*10**73*pi*I) == 0 assert tanh(7*10**103*pi*I) == 0 assert tanh(pi*I/2) == tanh(pi*I/2) assert tanh(-pi*I/2) == -tanh(pi*I/2) assert tanh(5*pi*I/2) == tanh(5*pi*I/2) assert tanh(7*pi*I/2) == tanh(7*pi*I/2) assert tanh(pi*I/3) == sqrt(3)*I assert tanh(-2*pi*I/3) == sqrt(3)*I assert tanh(pi*I/4) == I assert tanh(-pi*I/4) == -I assert tanh(17*pi*I/4) == I assert tanh(-3*pi*I/4) == I assert tanh(pi*I/6) == I/sqrt(3) assert tanh(-pi*I/6) == -I/sqrt(3) assert tanh(7*pi*I/6) == I/sqrt(3) assert tanh(-5*pi*I/6) == I/sqrt(3) assert tanh(pi*I/105) == tan(pi/105)*I assert tanh(-pi*I/105) == -tan(pi/105)*I assert tanh(2 + 3*I) == tanh(2 + 3*I) assert tanh(x*I) == tan(x)*I assert tanh(k*pi*I) == 0 assert tanh(17*k*pi*I) == 0 assert tanh(k*pi*I/2) == tan(k*pi/2)*I def test_coth(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert coth(nan) == nan assert coth(zoo) == nan assert coth(oo) == 1 assert coth(-oo) == -1 assert coth(0) == coth(0) assert coth(1) == coth(1) assert coth(-1) == -coth(1) assert coth(x) == coth(x) assert coth(-x) == -coth(x) assert coth(pi*I) == -I*cot(pi) assert coth(-pi*I) == cot(pi)*I assert coth(2**1024 * E) == coth(2**1024 * E) assert coth(-2**1024 * E) == -coth(2**1024 * E) assert coth(pi*I) == -I*cot(pi) assert coth(-pi*I) == I*cot(pi) assert coth(2*pi*I) == -I*cot(2*pi) assert coth(-2*pi*I) == I*cot(2*pi) assert coth(-3*10**73*pi*I) == I*cot(3*10**73*pi) assert coth(7*10**103*pi*I) == -I*cot(7*10**103*pi) assert coth(pi*I/2) == 0 assert coth(-pi*I/2) == 0 assert coth(5*pi*I/2) == 0 assert coth(7*pi*I/2) == 0 assert coth(pi*I/3) == -I/sqrt(3) assert coth(-2*pi*I/3) == -I/sqrt(3) assert coth(pi*I/4) == -I assert coth(-pi*I/4) == I assert coth(17*pi*I/4) == -I assert coth(-3*pi*I/4) == -I assert coth(pi*I/6) == -sqrt(3)*I assert coth(-pi*I/6) == sqrt(3)*I assert coth(7*pi*I/6) == -sqrt(3)*I assert coth(-5*pi*I/6) == -sqrt(3)*I assert coth(pi*I/105) == -cot(pi/105)*I assert coth(-pi*I/105) == cot(pi/105)*I assert coth(2 + 3*I) == coth(2 + 3*I) assert coth(x*I) == -cot(x)*I assert coth(k*pi*I) == -cot(k*pi)*I assert coth(17*k*pi*I) == -cot(17*k*pi)*I assert coth(k*pi*I) == -cot(k*pi)*I def test_asinh(): x, y = symbols('x,y') assert asinh(x) == asinh(x) assert asinh(-x) == -asinh(x) assert asinh(nan) == nan assert asinh( 0) == 0 assert asinh(+1) == log(sqrt(2)+1) assert asinh(-1) == log(sqrt(2)-1) assert asinh(I) == pi*I/2 assert asinh(-I) == -pi*I/2 assert asinh(I/2) == pi*I/6 assert asinh(-I/2) == -pi*I/6 assert asinh(oo) == oo assert asinh(-oo) == -oo assert asinh(I*oo) == oo assert asinh(-I *oo) == -oo assert asinh(zoo) == zoo assert asinh(I *(sqrt(3) - 1)/(2**(S(3)/2))) == pi*I/12 assert asinh(-I *(sqrt(3) - 1)/(2**(S(3)/2))) == -pi*I/12 assert asinh(I*(sqrt(5)-1)/4) == pi*I/10 assert asinh(-I*(sqrt(5)-1)/4) == -pi*I/10 assert asinh(I*(sqrt(5)+1)/4) == 3*pi*I/10 assert asinh(-I*(sqrt(5)+1)/4) == -3*pi*I/10 def test_asinh_series(): x = Symbol('x') assert asinh(x).series(x, 0, 8) == \ x - x**3/6 + 3*x**5/40 - 5*x**7/112 + O(x**8) t5 = asinh(x).taylor_term(5, x) assert t5 == 3*x**5/40 assert asinh(x).taylor_term(7, x, t5, 0) == -5*x**7/112 def test_acosh(): # TODO please write more tests -- see #652 # From http://functions.wolfram.com/ElementaryFunctions/ArcCosh/03/01/ # at specific points assert acosh(1) == 0 assert acosh(-1) == pi*I assert acosh(0) == I*pi/2 assert acosh(Rational(1,2)) == I*pi/3 assert acosh(Rational(-1,2)) == 2*pi*I/3 assert acosh(zoo) == oo assert acosh(I) == log(I*(1+sqrt(2))) assert acosh(-I) == log(-I*(1+sqrt(2))) assert acosh((sqrt(3)-1)/(2*sqrt(2))) == 5*pi*I/12 assert acosh(-(sqrt(3)-1)/(2*sqrt(2))) == 7*pi*I/12 assert acosh(sqrt(2)/2) == I*pi/4 assert acosh(-sqrt(2)/2) == 3*I*pi/4 assert acosh(sqrt(3)/2) == I*pi/6 assert acosh(-sqrt(3)/2) == 5*I*pi/6 assert acosh(sqrt(2+sqrt(2))/2) == I*pi/8 assert acosh(-sqrt(2+sqrt(2))/2) == 7*I*pi/8 assert acosh(sqrt(2-sqrt(2))/2) == 3*I*pi/8 assert acosh(-sqrt(2-sqrt(2))/2) == 5*I*pi/8 assert acosh((1+sqrt(3))/(2*sqrt(2))) == I*pi/12 assert acosh(-(1+sqrt(3))/(2*sqrt(2))) == 11*I*pi/12 assert acosh((sqrt(5)+1)/4) == I*pi/5 assert acosh(-(sqrt(5)+1)/4) == 4*I*pi/5 def test_acosh_infinities(): assert acosh(oo) == oo assert acosh(-oo) == oo assert acosh(I*oo) == oo assert acosh(-I*oo) == oo def test_acosh_series(): x = Symbol('x') assert acosh(x).series(x, 0, 8) == \ -I*x + pi*I/2 - I*x**3/6 - 3*I*x**5/40 - 5*I*x**7/112 + O(x**8) t5 = acosh(x).taylor_term(5, x) assert t5 == - 3*I*x**5/40 assert acosh(x).taylor_term(7, x, t5, 0) == - 5*I*x**7/112 # TODO please write more tests -- see #652 def test_atanh(): # TODO please write more tests -- see #652 # From http://functions.wolfram.com/ElementaryFunctions/ArcTanh/03/01/ # at specific points x = Symbol('x') #at specific points assert atanh(0) == 0 assert atanh(I) == I*pi/4 assert atanh(-I) == -I*pi/4 assert atanh(1) == oo assert atanh(-1) == -oo # at infinites assert atanh(I*oo) == I*pi/2 assert atanh(-I*oo) == -I*pi/2 assert atanh(zoo) == nan #properties assert atanh(-x) == -atanh(x) assert atanh(I/sqrt(3)) == I*pi/6 assert atanh(-I/sqrt(3)) == -I*pi/6 assert atanh(I*sqrt(3)) == I*pi/3 assert atanh(-I*sqrt(3)) == -I*pi/3 assert atanh(I*(1+sqrt(2))) == 3*pi*I/8 assert atanh(I*(sqrt(2)-1)) == pi*I/8 assert atanh(I*(1-sqrt(2))) == -pi*I/8 assert atanh(-I*(1+sqrt(2))) == -3*pi*I/8 assert atanh(I*sqrt(5+2*sqrt(5))) == 2*I*pi/5 assert atanh(-I*sqrt(5+2*sqrt(5))) == -2*I*pi/5 assert atanh(I*(2-sqrt(3))) == pi*I/12 assert atanh(I*(sqrt(3)-2)) == -pi*I/12 assert atanh(oo) == -I*pi/2 def test_atanh_infinities(): assert atanh(oo) == -I*pi/2 assert atanh(-oo) == I*pi/2 # TODO please write more tests -- see #652 def test_acoth(): # TODO please write more tests -- see #652 # From http://functions.wolfram.com/ElementaryFunctions/ArcCoth/03/01/ # at specific points x = Symbol('x') #at specific points assert acoth(0) == I*pi/2 assert acoth(I) == -I*pi/4 assert acoth(-I) == I*pi/4 assert acoth(1) == oo assert acoth(-1) == -oo # at infinites assert acoth(oo) == 0 assert acoth(-oo) == 0 assert acoth(I*oo) == 0 assert acoth(-I*oo) == 0 assert acoth(zoo) == 0 #properties assert acoth(-x) == -acoth(x) assert acoth(I/sqrt(3)) == -I*pi/3 assert acoth(-I/sqrt(3)) == I*pi/3 assert acoth(I*sqrt(3)) == -I*pi/6 assert acoth(-I*sqrt(3)) == I*pi/6 assert acoth(I*(1+sqrt(2))) == -pi*I/8 assert acoth(-I*(sqrt(2)+1)) == pi*I/8 assert acoth(I*(1-sqrt(2))) == 3*pi*I/8 assert acoth(I*(sqrt(2)-1)) == -3*pi*I/8 assert acoth(I*sqrt(5+2*sqrt(5))) == -I*pi/10 assert acoth(-I*sqrt(5+2*sqrt(5))) == I*pi/10 assert acoth(I*(2+sqrt(3))) == -pi*I/12 assert acoth(-I*(2+sqrt(3))) == pi*I/12 assert acoth(I*(2-sqrt(3))) == -5*pi*I/12 assert acoth(I*(sqrt(3)-2)) == 5*pi*I/12 def test_simplifications(): x = Symbol('x') assert sinh(asinh(x)) == x assert sinh(acosh(x)) == sqrt(x-1) * sqrt(x+1) assert sinh(atanh(x)) == x/sqrt(1-x**2) assert sinh(acoth(x)) == 1/(sqrt(x-1) * sqrt(x+1)) assert cosh(asinh(x)) == sqrt(1+x**2) assert cosh(acosh(x)) == x assert cosh(atanh(x)) == 1/sqrt(1-x**2) assert cosh(acoth(x)) == x/(sqrt(x-1) * sqrt(x+1)) assert tanh(asinh(x)) == x/sqrt(1+x**2) assert tanh(acosh(x)) == sqrt(x-1) * sqrt(x+1) / x assert tanh(atanh(x)) == x assert tanh(acoth(x)) == 1/x assert coth(asinh(x)) == sqrt(1+x**2)/x assert coth(acosh(x)) == x/(sqrt(x-1) * sqrt(x+1)) assert coth(atanh(x)) == 1/x assert coth(acoth(x)) == x def test_issue1037(): assert cosh(asinh(Integer(3)/2)) == sqrt(Integer(13)/4) def test_sinh_rewrite(): x = Symbol('x') assert sinh(x).rewrite(exp) == (exp(x)-exp(-x))/2 assert sinh(x).rewrite(cosh) == -I*cosh(x+I*pi/2) tanh_half = tanh(S.Half*x) assert sinh(x).rewrite(tanh) == 2*tanh_half/(1-tanh_half**2) coth_half = coth(S.Half*x) assert sinh(x).rewrite(coth) == 2*coth_half/(coth_half**2-1) def test_cosh_rewrite(): x = Symbol('x') assert cosh(x).rewrite(exp) == (exp(x)+exp(-x))/2 assert cosh(x).rewrite(sinh) == -I*sinh(x+I*pi/2) tanh_half = tanh(S.Half*x)**2 assert cosh(x).rewrite(tanh) == (1+tanh_half)/(1-tanh_half) coth_half = coth(S.Half*x)**2 assert cosh(x).rewrite(coth) == (coth_half+1)/(coth_half-1) def test_tanh_rewrite(): x = Symbol('x') assert tanh(x).rewrite(exp) == (exp(x)-exp(-x))/(exp(x)+exp(-x)) assert tanh(x).rewrite(sinh) == I*sinh(x)/sinh(I*pi/2-x) assert tanh(x).rewrite(cosh) == I*cosh(I*pi/2-x)/cosh(x) assert tanh(x).rewrite(coth) == 1/coth(x) def test_coth_rewrite(): x = Symbol('x') assert coth(x).rewrite(exp) == (exp(x)+exp(-x))/(exp(x)-exp(-x)) assert coth(x).rewrite(sinh) == -I*sinh(I*pi/2-x)/sinh(x) assert coth(x).rewrite(cosh) == -I*cosh(x)/cosh(I*pi/2-x) assert coth(x).rewrite(tanh) == 1/tanh(x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_integers.py0000644000175000017500000001144712014170666031656 0ustar georgeskgeorgeskfrom sympy import Symbol, floor, nan, oo, E, symbols, ceiling, pi, Rational, \ Float, I, sin, exp, log, factorial from sympy.utilities.pytest import XFAIL def test_floor(): x = Symbol('x') y = Symbol('y', real=True) k, n = symbols('k,n', integer=True) assert floor(nan) == nan assert floor(oo) == oo assert floor(-oo) == -oo assert floor(0) == 0 assert floor(1) == 1 assert floor(-1) == -1 assert floor(E) == 2 assert floor(-E) == -3 assert floor(2*E) == 5 assert floor(-2*E) == -6 assert floor(pi) == 3 assert floor(-pi) == -4 assert floor(Rational(1, 2)) == 0 assert floor(-Rational(1, 2)) == -1 assert floor(Rational(7, 3)) == 2 assert floor(-Rational(7, 3)) == -3 assert floor(Float(17.0)) == 17 assert floor(-Float(17.0)) == -17 assert floor(Float(7.69)) == 7 assert floor(-Float(7.69)) == -8 assert floor(I) == I assert floor(-I) == -I assert floor(oo*I) == oo*I assert floor(-oo*I) == -oo*I assert floor(2*I) == 2*I assert floor(-2*I) == -2*I assert floor(I/2) == 0 assert floor(-I/2) == -I assert floor(E + 17) == 19 assert floor(pi + 2) == 5 assert floor(E + pi) == floor(E + pi) assert floor(I + pi) == floor(I + pi) assert floor(floor(pi)) == 3 assert floor(floor(y)) == floor(y) assert floor(floor(x)) == floor(floor(x)) assert floor(x) == floor(x) assert floor(2*x) == floor(2*x) assert floor(k*x) == floor(k*x) assert floor(k) == k assert floor(2*k) == 2*k assert floor(k*n) == k*n assert floor(k/2) == floor(k/2) assert floor(x + y) == floor(x + y) assert floor(x + 3) == floor(x + 3) assert floor(x + k) == floor(x + k) assert floor(y + 3) == floor(y) + 3 assert floor(y + k) == floor(y) + k assert floor(3 + I*y + pi) == 6 + floor(y)*I assert floor(k + n) == k + n assert floor(x*I) == floor(x*I) assert floor(k*I) == k*I assert floor(Rational(23, 10) - E*I) == 2 - 3*I assert floor(sin(1)) == 0 assert floor(sin(-1)) == -1 assert floor(exp(2)) == 7 assert floor(log(8)/log(2)) != 2 assert int(floor(log(8)/log(2)).evalf(chop=True)) == 3 assert floor(factorial(50)/exp(1)) == \ 11188719610782480504630258070757734324011354208865721592720336800 def test_ceiling(): x = Symbol('x') y = Symbol('y', real=True) k, n = symbols('k,n', integer=True) assert ceiling(nan) == nan assert ceiling(oo) == oo assert ceiling(-oo) == -oo assert ceiling(0) == 0 assert ceiling(1) == 1 assert ceiling(-1) == -1 assert ceiling(E) == 3 assert ceiling(-E) == -2 assert ceiling(2*E) == 6 assert ceiling(-2*E) == -5 assert ceiling(pi) == 4 assert ceiling(-pi) == -3 assert ceiling(Rational(1, 2)) == 1 assert ceiling(-Rational(1, 2)) == 0 assert ceiling(Rational(7, 3)) == 3 assert ceiling(-Rational(7, 3)) == -2 assert ceiling(Float(17.0)) == 17 assert ceiling(-Float(17.0)) == -17 assert ceiling(Float(7.69)) == 8 assert ceiling(-Float(7.69)) == -7 assert ceiling(I) == I assert ceiling(-I) == -I assert ceiling(oo*I) == oo*I assert ceiling(-oo*I) == -oo*I assert ceiling(2*I) == 2*I assert ceiling(-2*I) == -2*I assert ceiling(I/2) == I assert ceiling(-I/2) == 0 assert ceiling(E + 17) == 20 assert ceiling(pi + 2) == 6 assert ceiling(E + pi) == ceiling(E + pi) assert ceiling(I + pi) == ceiling(I + pi) assert ceiling(ceiling(pi)) == 4 assert ceiling(ceiling(y)) == ceiling(y) assert ceiling(ceiling(x)) == ceiling(ceiling(x)) assert ceiling(x) == ceiling(x) assert ceiling(2*x) == ceiling(2*x) assert ceiling(k*x) == ceiling(k*x) assert ceiling(k) == k assert ceiling(2*k) == 2*k assert ceiling(k*n) == k*n assert ceiling(k/2) == ceiling(k/2) assert ceiling(x + y) == ceiling(x + y) assert ceiling(x + 3) == ceiling(x + 3) assert ceiling(x + k) == ceiling(x + k) assert ceiling(y + 3) == ceiling(y) + 3 assert ceiling(y + k) == ceiling(y) + k assert ceiling(3 + pi + y*I) == 7 + ceiling(y)*I assert ceiling(k + n) == k + n assert ceiling(x*I) == ceiling(x*I) assert ceiling(k*I) == k*I assert ceiling(Rational(23, 10) - E*I) == 3 - 2*I assert ceiling(sin(1)) == 1 assert ceiling(sin(-1)) == 0 assert ceiling(exp(2)) == 8 assert ceiling(-log(8)/log(2)) != -2 assert int(ceiling(-log(8)/log(2)).evalf(chop=True)) == -3 assert ceiling(factorial(50)/exp(1)) == \ 11188719610782480504630258070757734324011354208865721592720336801 @XFAIL def test_issue_1050(): assert floor(3 + pi*I + y*I) == 3 + floor(pi+y)*I assert floor(3*I + pi*I + y*I) == floor(3+pi+y)*I assert floor(3 + E + pi*I + y*I) == 5 + floor(pi+y)*I wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_complexes.py0000644000175000017500000001076112014170666032033 0ustar georgeskgeorgeskfrom sympy import symbols, Symbol, sqrt, oo, re, nan, im, sign, I, E, log, \ pi, arg, conjugate, expand, exp, sin, cos, Function, Abs from sympy.utilities.pytest import XFAIL def test_re(): x, y = symbols('x,y') r = Symbol('r', real=True) assert re(nan) == nan assert re(oo) == oo assert re(-oo) == -oo assert re(0) == 0 assert re(1) == 1 assert re(-1) == -1 assert re(E) == E assert re(-E) == -E assert re(x) == re(x) assert re(x*I) == -im(x) assert re(r*I) == 0 assert re(r) == r assert re(x + y) == re(x + y) assert re(x + r) == re(x) + r assert re(re(x)) == re(x) assert re(2 + I) == 2 assert re(x + I) == re(x) assert re(x + y*I) == re(x) - im(y) assert re(x + r*I) == re(x) assert re(log(2*I)) == log(2) assert re((2+I)**2).expand(complex=True) == 3 def test_im(): x, y = symbols('x,y') r = Symbol('r', real=True) assert im(nan) == nan assert im(oo*I) == oo assert im(-oo*I) == -oo assert im(0) == 0 assert im(1) == 0 assert im(-1) == 0 assert im(E*I) == E assert im(-E*I) == -E assert im(x) == im(x) assert im(x*I) == re(x) assert im(r*I) == r assert im(r) == 0 assert im(x + y) == im(x + y) assert im(x + r) == im(x) assert im(x + r*I) == im(x) + r assert im(im(x)*I) == im(x) assert im(2 + I) == 1 assert im(x + I) == im(x) + 1 assert im(x + y*I) == im(x) + re(y) assert im(x + r*I) == im(x) + r assert im(log(2*I)) == pi/2 assert im((2+I)**2).expand(complex=True) == 4 def test_sign(): assert sign(1.2) == 1 assert sign(-1.2) == -1 assert sign(0) == 0 x = Symbol('x') assert sign(x).is_zero == False assert sign(2*x) == sign(x) p = Symbol('p', positive = True) n = Symbol('n', negative = True) m = Symbol('m', negative = True) assert sign(2*p*x) == sign(x) assert sign(n*x) == -sign(x) assert sign(n*m*x) == sign(x) x = 0 assert sign(x).is_zero == True def test_Abs(): x, y = symbols('x,y') assert Abs(0) == 0 assert Abs(1) == 1 assert Abs(-1)== 1 x = Symbol('x',real=True) n = Symbol('n',integer=True) assert x**(2*n) == Abs(x)**(2*n) assert Abs(x).diff(x) == sign(x) assert abs(x) == Abs(x) # Python built-in def test_abs_real(): # test some properties of abs that only apply # to real numbers x = Symbol('x', complex=True) assert sqrt(x**2) != Abs(x) assert Abs(x**2) != x**2 x = Symbol('x', real=True) assert sqrt(x**2) == Abs(x) assert Abs(x**2) == x**2 def test_abs_properties(): x = Symbol('x') assert Abs(x).is_real == True assert Abs(x).is_positive == None assert Abs(x).is_nonnegative == True w = Symbol('w', complex=True, zero=False) assert Abs(w).is_real == True assert Abs(w).is_positive == True assert Abs(w).is_zero == False q = Symbol('q', positive=True) assert Abs(q).is_real == True assert Abs(q).is_positive == True assert Abs(q).is_zero == False def test_arg(): assert arg(0) == nan assert arg(1) == 0 assert arg(-1) == pi assert arg(I) == pi/2 assert arg(-I) == -pi/2 assert arg(1+I) == pi/4 assert arg(-1+I) == 3*pi/4 assert arg(1-I) == -pi/4 p = Symbol('p', positive=True) assert arg(p) == 0 n = Symbol('n', negative=True) assert arg(n) == pi def test_conjugate(): a = Symbol('a', real=True) assert conjugate(a) == a assert conjugate(I*a) == -I*a x, y = symbols('x,y') assert conjugate(conjugate(x)) == x assert conjugate(x + y) == conjugate(x) + conjugate(y) assert conjugate(x - y) == conjugate(x) - conjugate(y) assert conjugate(x * y) == conjugate(x) * conjugate(y) assert conjugate(x / y) == conjugate(x) / conjugate(y) assert conjugate(-x) == -conjugate(x) def test_issue936(): x = Symbol('x') assert Abs(x).expand(trig=True) == Abs(x) assert sign(x).expand(trig=True) == sign(x) assert arg(x).expand(trig=True) == arg(x) def test_issue1655_derivative_conjugate(): x = Symbol('x') f = Function('f') assert (f(x).conjugate()).diff(x) == (f(x).diff(x)).conjugate() def test_derivatives_issue1658(): x = Symbol('x') f = Function('f') assert re(f(x)).diff(x) == re(f(x).diff(x)) assert im(f(x)).diff(x) == im(f(x).diff(x)) x = Symbol('x', real=True) assert Abs(f(x)).diff(x).subs(f(x), 1+I*x).doit() == x/sqrt(1 + x**2) assert arg(f(x)).diff(x).subs(f(x), 1+I*x**2).doit() == 2*x/(1+x**4) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_exponential.py0000644000175000017500000001632512014170666032364 0ustar georgeskgeorgeskfrom sympy import (symbols, log, Float, nan, oo, zoo, I, pi, E, exp, Symbol, LambertW, sqrt, Rational, sin, expand_log, S, sign, nextprime) from sympy.utilities.pytest import XFAIL def test_exp_values(): x, y = symbols('x,y') k = Symbol('k', integer=True) assert exp(nan) == nan assert exp(oo) == oo assert exp(-oo) == 0 assert exp(0) == 1 assert exp(1) == E assert exp(pi*I/2) == I assert exp(pi*I) == -1 assert exp(3*pi*I/2) == -I assert exp(2*pi*I) == 1 assert exp(pi*I*2*k) == 1 assert exp(pi*I*2*(k+Rational(1,2))) == -1 assert exp(pi*I*2*(k+Rational(1,4))) == I assert exp(pi*I*2*(k+Rational(3,4))) == -I assert exp(log(x)) == x assert exp(2*log(x)) == x**2 assert exp(pi*log(x)) == x**pi assert exp(17*log(x) + E*log(y)) == x**17 * y**E assert exp(x*log(x)) != x**x assert exp(sin(x)*log(x)) != x def test_exp_log(): x = Symbol("x", real=True) assert log(exp(x)) == x assert exp(log(x)) == x def test_exp_expand(): x = Symbol("x") y = Symbol("y") e = exp(log(Rational(2))*(1+x)-log(Rational(2))*x) assert e.expand() == 2 assert exp(x+y) != exp(x)*exp(y) assert exp(x+y).expand() == exp(x)*exp(y) def test_exp__as_base_exp(): x,y = symbols('x,y') assert exp(x).as_base_exp() == (E, x) assert exp(2*x).as_base_exp() == (E, 2*x) assert exp(x*y).as_base_exp() == (E, x*y) assert exp(-x).as_base_exp() == (E, -x) # Pow( *expr.as_base_exp() ) == expr invariant should hold assert E**x == exp(x) assert E**(2*x) == exp(2*x) assert E**(x*y) == exp(x*y) assert exp(x).base is S.Exp1 assert exp(x).exp == x def test_exp_infinity(): y = Symbol('y') assert exp(I*y) != nan assert exp(I*oo) == nan assert exp(y*I*oo) == nan def test_log_values(): assert log(nan) == nan assert log(oo) == oo assert log(-oo) == oo assert log(zoo) == zoo assert log(-zoo) == zoo assert log(0) == zoo assert log(1) == 0 assert log(-1) == I*pi assert log(E) == 1 assert log(-E).expand() == 1 + I*pi assert log(pi) == log(pi) assert log(-pi).expand() == log(pi) + I*pi assert log(17) == log(17) assert log(-17) == log(17) + I*pi assert log(I) == I*pi/2 assert log(-I) == -I*pi/2 assert log(17*I) == I*pi/2 + log(17) assert log(-17*I).expand() == -I*pi/2 + log(17) assert log(oo*I) == oo assert log(-oo*I) == oo assert exp(-log(3))**(-1) == 3 assert log(S.Half) == -log(2) assert log(2*3).func is log assert log(2*3**2).func is log def test_log_base(): assert log(1, 2) == 0 assert log(2, 2) == 1 assert log(3, 2) == log(3)/log(2) assert log(6, 2) == 1 + log(3)/log(2) assert log(6, 3) == 1 + log(2)/log(3) assert log(2**3, 2) == 3 assert log(3**3, 3) == 3 def test_log_symbolic(): x, y = symbols('x,y') assert log(x, exp(1)) == log(x) assert log(exp(x)) != x assert log(x, exp(1)) == log(x) assert log(x*y) != log(x) + log(y) assert log(x/y).expand() != log(x) - log(y) assert log(x/y).expand(force=True) == log(x) - log(y) assert log(x**y).expand() != y*log(x) assert log(x**y).expand(force=True) == y*log(x) assert log(x, 2) == log(x)/log(2) assert log(E, 2) == 1/log(2) p, q = symbols('p,q', positive=True) r = Symbol('r', real=True) assert log(p**2) != 2*log(p) assert log(p**2).expand() == 2*log(p) assert log(x**2).expand() != 2*log(x) assert log(p**q) != q*log(p) assert log(exp(p)) == p assert log(p*q) != log(p) + log(q) assert log(p*q).expand() == log(p) + log(q) assert log(-exp(p)) != p + I*pi assert log(-exp(x)).expand() != x + I*pi assert log(-exp(r)).expand() == r + I*pi assert log(x**y) != y*log(x) assert (log(x**-5)**-1).expand() != -1/log(x)/5 assert (log(p**-5)**-1).expand() == -1/log(p)/5 assert log(-x).func is log and log(-x).args[0] == -x assert log(-p).func is log and log(-p).args[0] == -p def test_log_assumptions(): p = symbols('p', positive=True) n = symbols('n', negative=True) assert log(2) > 0 assert log(1).is_zero assert log(2-pi-pi*(1/pi-1)).is_zero assert log(p).is_zero is None assert log(n).is_zero is False assert log(0.5).is_negative == True def test_log_hashing(): x = Symbol("y") assert x != log(log(x)) assert hash(x) != hash(log(log(x))) assert log(x) != log(log(log(x))) e = 1/log(log(x)+log(log(x))) assert e.base.func is log e = 1/log(log(x)+log(log(log(x)))) assert e.base.func is log x = Symbol("x") e = log(log(x)) assert e.func is log assert not x.func is log assert hash(log(log(x))) != hash(x) assert e != x def test_log_sign(): assert sign(log(2)) == 1 def test_log_expand_complex(): assert log(1+I).expand(complex=True) == log(2)/2 + I*pi/4 assert log(1 - sqrt(2)).expand(complex=True) == log(sqrt(2)-1) + I*pi def test_log_apply_evalf(): value = (log(3)/log(2)-1).evalf() assert value.epsilon_eq(Float("0.58496250072115618145373")) def test_log_expand(): w = Symbol("w", positive=True) e = log(w**(log(5)/log(3))) assert e.expand() == log(5)/log(3) * log(w) x, y, z = symbols('x,y,z', positive=True) assert log(x*(y+z)).expand(mul=False) == log(x)+log(y+z) assert log(log(x**2)*log(y*z)).expand() == log(2*log(x)*log(y) + 2*log(x)*log(z)) assert log(x**log(x**2)).expand(deep=False) == log(x)*log(x**2) assert log(x**log(x**2)).expand() == 2*log(x)**2 assert (log(x*(y+z))*(x+y)),expand(mul=True, log=True) == y*log(x) + y*log(y + z) + z*log(x) + z*log(y + z) x, y = symbols('x,y') assert log(x*y).expand(force=True) == log(x) + log(y) assert log(x**y).expand(force=True) == y*log(x) # there's generally no need to expand out logs since this requires # factoring and if simplification is sought, it's cheaper to put # logs together than it is to take them apart. assert log(2*3**2).expand() != 2*log(3) + log(2) def test_log_simplify(): x = Symbol("x", positive=True) assert log(x**2).expand() == 2*log(x) assert expand_log(log(x**(2+log(2)))) == (2+log(2))*log(x) def test_lambertw(): x = Symbol('x') assert LambertW(x) == LambertW(x) assert LambertW(0) == 0 assert LambertW(E) == 1 assert LambertW(-1/E) == -1 assert LambertW(-log(2)/2) == -log(2) assert LambertW(oo) == oo assert LambertW(x**2).diff(x) == 2*LambertW(x**2)/x/(1+LambertW(x**2)) assert LambertW(sqrt(2)).evalf(30).epsilon_eq( Float("0.701338383413663009202120278965",30),1e-29) def test_exp_expand(): A,B,C = symbols('A,B,C', commutative=False) x,y,z = symbols('x,y,z') assert exp(A+B).expand() == exp(A+B) assert exp(A+B+C).expand() == exp(A+B+C) assert exp(x+y).expand() == exp(x)*exp(y) assert exp(x+y+z).expand() == exp(x)*exp(y)*exp(z) def test_as_numer_denom(): from sympy.abc import x n = symbols('n', negative=True) assert exp(x).as_numer_denom() == (exp(x), 1) assert exp(-x).as_numer_denom() == (1, exp(x)) assert exp(-2*x).as_numer_denom() == (1, exp(2*x)) assert exp(-2).as_numer_denom() == (1, exp(2)) assert exp(n).as_numer_denom() == (exp(n), 1) assert exp(-n).as_numer_denom() == (1, exp(n)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_piecewise.py0000644000175000017500000001611412014170666032007 0ustar georgeskgeorgeskfrom sympy import (diff, expand, Eq, Integral, integrate, Interval, lambdify, log, oo, Piecewise, piecewise_fold, symbols, pi, solve, Rational, Basic) from sympy.utilities.pytest import XFAIL, raises x,y = symbols('x,y') def test_piecewise(): # Test canonization assert Piecewise((x, x < 1), (0, True)) == Piecewise((x, x < 1), (0, True)) assert Piecewise((x, x < 1), (0, False), (-1, 1>2)) == Piecewise((x, x < 1)) assert Piecewise((x, True)) == x raises(TypeError,"Piecewise(x)") raises(TypeError,"Piecewise((x,x**2))") # Test subs p = Piecewise((-1, x < -1), (x**2, x < 0), (log(x), x >=0)) p_x2 = Piecewise((-1, x**2 < -1), (x**4, x**2 < 0), (log(x**2), x**2 >=0)) assert p.subs(x,x**2) == p_x2 assert p.subs(x,-5) == -1 assert p.subs(x,-1) == 1 assert p.subs(x,1) == log(1) # More subs tests p2 = Piecewise((1, x < pi), (-1, x < 2*pi), (0, x > 2*pi)) assert p2.subs(x,2) == 1 assert p2.subs(x,4) == -1 assert p2.subs(x,10) == 0 # Test evalf assert p.evalf() == p assert p.evalf(subs={x:-2}) == -1 assert p.evalf(subs={x:-1}) == 1 assert p.evalf(subs={x:1}) == log(1) # Test doit f_int = Piecewise((Integral(x,(x,0,1)), x < 1)) assert f_int.doit() == Piecewise( (1.0/2.0, x < 1) ) # Test differentiation f = x fp = x*p dp = Piecewise((0, x < -1), (2*x, x < 0), (1/x, x >= 0)) fp_dx = x*dp + p assert diff(p,x) == dp assert diff(f*p,x) == fp_dx # Test simple arithmetic assert x*p == fp assert x*p + p == p + x*p assert p + f == f + p assert p + dp == dp + p assert p - dp == -(dp - p) # Test _eval_interval f1 = x*y + 2 f2 = x*y**2 + 3 peval = Piecewise( (f1, x<0), (f2, x>0)) peval_interval = f1.subs(x,0) - f1.subs(x,-1) + f2.subs(x,1) - f2.subs(x,0) assert peval._eval_interval(x, -1, 1) == peval_interval # Test integration p_int = Piecewise((-x,x < -1), (x**3/3.0, x < 0), (-x + x*log(x), x >= 0)) assert integrate(p,x) == p_int p = Piecewise((x, x < 1),(x**2, -1 <= x),(x,3=' is not yet implemented .. f = Piecewise(((x - 2)**2, 0 <= x), (1, True)) assert integrate(f, (x, -2, 2)) == Rational(14, 3) g = Piecewise(((x - 5)**5, 4 <= x), (f, True)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == Rational(43, 6) g = Piecewise(((x - 5)**5, 4 <= x), (f, x < 4)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == Rational(43, 6) g = Piecewise(((x - 5)**5, 2 <= x), (f, x < 2)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == -Rational(701, 6) g = Piecewise(((x - 5)**5, 2 <= x), (f, True)) assert integrate(g, (x, -2, 2)) == Rational(14, 3) assert integrate(g, (x, -2, 5)) == -Rational(701, 6) g = Piecewise(((x - 5)**5, 2 <= x), (2 * f, True)) assert integrate(g, (x, -2, 2)) == 2 * Rational(14, 3) assert integrate(g, (x, -2, 5)) == -Rational(673, 6) g = Piecewise((1, x > 0), (0, Eq(x, 0)), (-1, x < 0)) assert integrate(g, (x, -1, 1)) == 0 g = Piecewise((1, x - y < 0), (0, True)) assert integrate(g, (y, -oo, oo)) == oo def test_piecewise_solve(): abs2 = Piecewise((-x, x <= 0), (x, x > 0)) f = abs2.subs(x, x - 2) assert solve(f, x) == [2] assert solve(f - 1,x) == [1, 3] f = Piecewise(((x - 2)**2, x >= 0), (1, True)) assert solve(f, x) == [2] g = Piecewise(((x - 5)**5, x >= 4), (f, True)) assert solve(g, x) == [2, 5] g = Piecewise(((x - 5)**5, x >= 4), (f, x < 4)) assert solve(g, x) == [2, 5] g = Piecewise(((x - 5)**5, x >= 2), (f, x < 2)) assert solve(g, x) == [5] g = Piecewise(((x - 5)**5, x >= 2), (f, True)) assert solve(g, x) == [5] g = Piecewise(((x - 5)**5, x >= 2), (f, True), (10, False)) assert solve(g, x) == [5] # See issue 1253 (enhance the solver to handle inequalities). @XFAIL def test_piecewise_solve2(): f = Piecewise(((x - 2)**2, x >= 0), (0, True)) assert solve(f, x) == [2, Interval(0, oo, True, True)] def test_piecewise_fold(): p = Piecewise((x, x < 1), (1, 1 <= x)) assert piecewise_fold(x*p) == Piecewise((x**2, x < 1), (x, 1 <= x)) assert piecewise_fold(p+p) == Piecewise((2*x, x < 1), (2, 1 <= x)) p1 = Piecewise((0, x < 0), (x, x <= 1), (0, True)) p2 = Piecewise((0, x < 0), (1 - x, x <=1), (0, True)) p = 4*p1 + 2*p2 assert integrate(piecewise_fold(p),(x,-oo,oo)) == integrate(2*x + 2, (x, 0, 1)) def test_piecewise_fold_expand(): p1 = Piecewise((1,Interval(0,1,False,True)),(0,True)) p2 = piecewise_fold(expand((1-x)*p1)) assert p2 == Piecewise((1 - x, Interval(0,1,False,True)), \ (Piecewise((-x, Interval(0,1,False,True)), (0, True)), True)) p2 = expand(piecewise_fold((1-x)*p1)) assert p2 == Piecewise((1 - x, Interval(0,1,False,True)), (0, True)) def test_piecewise_duplicate(): p = Piecewise((x, x < -10),(x**2, x <= -1),(x, 1 < x)) assert p == Piecewise(*p.args) def test_doit(): p1 = Piecewise((x, x < 1), (x**2, -1 <= x), (x, 3 < x)) p2 = Piecewise((x, x < 1), (Integral(2 * x), -1 <= x), (x, 3 < x)) assert p2.doit() == p1 assert p2.doit(deep = False) == p2 def test_piecewise_interval(): p1 = Piecewise((x, Interval(0,1)), (0, True)) assert p1.subs(x, -0.5) == 0 assert p1.subs(x, 0.5) == 0.5 assert p1.diff(x) == Piecewise((1, Interval(0, 1)), (0, True)) assert integrate(p1, x) == Piecewise((x**2/2, Interval(0, 1)), (0, True)) def test_piecewise_collapse(): p1 = Piecewise((x, x<0),(x**2,x>1)) p2 = Piecewise((p1,x<0),(p1,x>1)) assert p2 == Piecewise((x, x < 0), (x**2, 1 < x)) p1 = Piecewise((Piecewise((x,x<0),(1,True)),True)) assert p1 == Piecewise((Piecewise((x,x<0),(1,True)),True)) def test_piecewise_lambdify(): p = Piecewise( (x**2, x < 0), (x, Interval(0, 1, False, True)), (2 - x, x >= 1), (0, True) ) f = lambdify(x, p) assert f(-2.0) == 4.0 assert f(0.0) == 0.0 assert f(0.5) == 0.5 assert f(2.0) == 0.0 def test_piecewise_series(): from sympy import sin, cos, O p1 = Piecewise((sin(x), x<0),(cos(x),x>0)) p2 = Piecewise((x+O(x**2), x<0),(1+O(x**2),x>0)) assert p1.nseries(x,n=2) == p2 def test_piecewise_evaluate(): assert Piecewise((x, True)) == x assert Piecewise((x, True), evaluate=True) == x p = Piecewise((x, True), evaluate=False) assert p != x assert p.is_Piecewise assert all(isinstance(i, Basic) for i in p.args) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/tests/test_trigonometric.py0000644000175000017500000004445612014170666032731 0ustar georgeskgeorgeskfrom sympy import symbols, Symbol, nan, oo, zoo, I, sinh, sin, acot, pi, atan, \ acos, Rational, sqrt, asin, acot, cot, coth, E, S, tan, tanh, cos, \ cosh, atan2, exp, log, asinh, acoth, atanh, O, cancel, Matrix def test_sin(): x, y = symbols('x,y') r = Symbol('r', real=True) k = Symbol('k', integer=True) assert sin(nan) == nan assert sin(oo*I) == oo*I assert sin(-oo*I) == -oo*I assert sin(oo).args[0] == oo assert sin(0) == 0 assert sin(asin(x)) == x assert sin(atan(x)) == x / sqrt(1 + x**2) assert sin(acos(x)) == sqrt(1 - x**2) assert sin(acot(x)) == 1 / (sqrt(1 + 1 / x**2) * x) assert sin(pi*I) == sinh(pi)*I assert sin(-pi*I) == -sinh(pi)*I assert sin(-2*I) == -sinh(2)*I assert sin(pi) == 0 assert sin(-pi) == 0 assert sin(2*pi) == 0 assert sin(-2*pi) == 0 assert sin(-3*10**73*pi) == 0 assert sin(7*10**103*pi) == 0 assert sin(pi/2) == 1 assert sin(-pi/2) == -1 assert sin(5*pi/2) == 1 assert sin(7*pi/2) == -1 assert sin(pi/3) == S.Half*sqrt(3) assert sin(-2*pi/3) == -S.Half*sqrt(3) assert sin(pi/4) == S.Half*sqrt(2) assert sin(-pi/4) == -S.Half*sqrt(2) assert sin(17*pi/4) == S.Half*sqrt(2) assert sin(-3*pi/4) == -S.Half*sqrt(2) assert sin(pi/6) == S.Half assert sin(-pi/6) == -S.Half assert sin(7*pi/6) == -S.Half assert sin(-5*pi/6) == -S.Half assert sin(1*pi/5) == sqrt((5 - sqrt(5)) / 8) assert sin(2*pi/5) == sqrt((5 + sqrt(5)) / 8) assert sin(3*pi/5) == sin(2*pi/5) assert sin(4*pi/5) == sin(1*pi/5) assert sin(6*pi/5) == -sin(1*pi/5) assert sin(8*pi/5) == -sin(2*pi/5) assert sin(-1273*pi/5) == -sin(2*pi/5) assert sin(104*pi/105) == sin(pi/105) assert sin(106*pi/105) == -sin(pi/105) assert sin(-104*pi/105) == -sin(pi/105) assert sin(-106*pi/105) == sin(pi/105) assert sin(x*I) == sinh(x)*I assert sin(k*pi) == 0 assert sin(17*k*pi) == 0 assert sin(k*pi*I) == sinh(k*pi)*I assert sin(r).is_real == True def test_sin_rewrite(): x = Symbol('x') assert sin(x).rewrite(exp) == -I*(exp(I*x) - exp(-I*x))/2 assert sin(x).rewrite(tan) == 2*tan(x/2)/(1 + tan(x/2)**2) assert sin(x).rewrite(cot) == 2*cot(x/2)/(1 + cot(x/2)**2) def test_trig_symmetry(): x = Symbol('x') y = Symbol('y') k = Symbol('k', integer=True) assert sin(-x) == -sin(x) assert cos(-x) == cos(x) assert tan(-x) == -tan(x) assert cot(-x) == -cot(x) assert sin(x+pi) == -sin(x) assert sin(x+2*pi) == sin(x) assert sin(x+3*pi) == -sin(x) assert sin(x+4*pi) == sin(x) assert sin(x-5*pi) == -sin(x) assert cos(x+pi) == -cos(x) assert cos(x+2*pi) == cos(x) assert cos(x+3*pi) == -cos(x) assert cos(x+4*pi) == cos(x) assert cos(x-5*pi) == -cos(x) assert tan(x+pi) == tan(x) assert tan(x-3*pi) == tan(x) assert cot(x+pi) == cot(x) assert cot(x-3*pi) == cot(x) assert sin(pi/2-x) == cos(x) assert sin(3*pi/2-x) == -cos(x) assert sin(5*pi/2-x) == cos(x) assert cos(pi/2-x) == sin(x) assert cos(3*pi/2-x) == -sin(x) assert cos(5*pi/2-x) == sin(x) assert tan(pi/2-x) == cot(x) assert tan(3*pi/2-x) == cot(x) assert tan(5*pi/2-x) == cot(x) assert cot(pi/2-x) == tan(x) assert cot(3*pi/2-x) == tan(x) assert cot(5*pi/2-x) == tan(x) assert sin(pi/2+x) == cos(x) assert cos(pi/2+x) == -sin(x) assert tan(pi/2+x) == -cot(x) assert cot(pi/2+x) == -tan(x) def test_cos(): x, y = symbols('x,y') r = Symbol('r', real=True) k = Symbol('k', integer=True) assert cos(nan) == nan assert cos(oo*I) == oo assert cos(-oo*I) == oo assert cos(0) == 1 assert cos(acos(x)) == x assert cos(atan(x)) == 1 / sqrt(1 + x**2) assert cos(asin(x)) == sqrt(1 - x**2) assert cos(acot(x)) == 1 / sqrt(1 + 1 / x**2) assert cos(pi*I) == cosh(pi) assert cos(-pi*I) == cosh(pi) assert cos(-2*I) == cosh(2) assert cos(pi/2) == 0 assert cos(-pi/2) == 0 assert cos(pi/2) == 0 assert cos(-pi/2) == 0 assert cos((-3*10**73+1)*pi/2) == 0 assert cos((7*10**103+1)*pi/2) == 0 assert cos(pi) == -1 assert cos(-pi) == -1 assert cos(2*pi)==1 assert cos(5*pi) == -1 assert cos(8*pi) == 1 assert cos(pi/3) == S.Half assert cos(-2*pi/3) == -S.Half assert cos(pi/4) == S.Half*sqrt(2) assert cos(-pi/4) == S.Half*sqrt(2) assert cos(11*pi/4) == -S.Half*sqrt(2) assert cos(-3*pi/4) == -S.Half*sqrt(2) assert cos(pi/6) == S.Half*sqrt(3) assert cos(-pi/6) == S.Half*sqrt(3) assert cos(7*pi/6) == -S.Half*sqrt(3) assert cos(-5*pi/6) == -S.Half*sqrt(3) assert cos(1*pi/5) == (sqrt(5) + 1)/4 assert cos(2*pi/5) == (sqrt(5) - 1)/4 assert cos(3*pi/5) == -cos(2*pi/5) assert cos(4*pi/5) == -cos(1*pi/5) assert cos(6*pi/5) == -cos(1*pi/5) assert cos(8*pi/5) == cos(2*pi/5) assert cos(-1273*pi/5) == -cos(2*pi/5) assert cos(104*pi/105) == -cos(pi/105) assert cos(106*pi/105) == -cos(pi/105) assert cos(-104*pi/105) == -cos(pi/105) assert cos(-106*pi/105) == -cos(pi/105) assert cos(x*I) == cosh(x) assert cos(k*pi*I) == cosh(k*pi) assert cos(r).is_real == True def test_cos_rewrite(): x = Symbol('x') assert cos(x).rewrite(exp) == exp(I*x)/2 + exp(-I*x)/2 assert cos(x).rewrite(tan) == (1 - tan(x/2)**2)/(1 + tan(x/2)**2) assert cos(x).rewrite(cot) == -(1 - cot(x/2)**2)/(1 + cot(x/2)**2) def test_tan(): x, y = symbols('x,y') r = Symbol('r', real=True) k = Symbol('k', integer=True) assert tan(nan) == nan assert tan(oo*I) == I assert tan(-oo*I) == -I assert tan(0) == 0 assert tan(atan(x)) == x assert tan(asin(x)) == x / sqrt(1 - x**2) assert tan(acos(x)) == sqrt(1 - x**2) / x assert tan(acot(x)) == 1 / x assert tan(pi*I) == tanh(pi)*I assert tan(-pi*I) == -tanh(pi)*I assert tan(-2*I) == -tanh(2)*I assert tan(pi) == 0 assert tan(-pi) == 0 assert tan(2*pi) == 0 assert tan(-2*pi) == 0 assert tan(-3*10**73*pi) == 0 assert tan(pi/2) == zoo assert tan(3*pi/2) == zoo assert tan(pi/3) == sqrt(3) assert tan(-2*pi/3) == sqrt(3) assert tan(pi/4) == S.One assert tan(-pi/4) == -S.One assert tan(17*pi/4) == S.One assert tan(-3*pi/4) == S.One assert tan(pi/6) == 1/sqrt(3) assert tan(-pi/6) == -1/sqrt(3) assert tan(7*pi/6) == 1/sqrt(3) assert tan(-5*pi/6) == 1/sqrt(3) assert tan(x*I) == tanh(x)*I assert tan(k*pi) == 0 assert tan(17*k*pi) == 0 assert tan(k*pi*I) == tanh(k*pi)*I assert tan(r).is_real == True assert tan(10*pi/7) == tan(3*pi/7) assert tan(11*pi/7) == -tan(3*pi/7) assert tan(-11*pi/7) == tan(3*pi/7) def test_tan_rewrite(): x = Symbol('x') neg_exp, pos_exp = exp(-x*I), exp(x*I) assert tan(x).rewrite(exp) == I*(neg_exp-pos_exp)/(neg_exp+pos_exp) assert tan(x).rewrite(sin) == 2*sin(x)**2/sin(2*x) assert tan(x).rewrite(cos) == -cos(x + S.Pi/2)/cos(x) assert tan(x).rewrite(cot) == 1/cot(x) def test_cot(): x, y = symbols('x,y') r = Symbol('r', real=True) k = Symbol('k', integer=True) assert cot(nan) == nan assert cot(oo*I) == -I assert cot(-oo*I) == I assert cot(0) == zoo assert cot(2*pi) == zoo assert cot(acot(x)) == x assert cot(atan(x)) == 1 / x assert cot(asin(x)) == sqrt(1 - x**2) / x assert cot(acos(x)) == x / sqrt(1 - x**2) assert cot(pi*I) == -coth(pi)*I assert cot(-pi*I) == coth(pi)*I assert cot(-2*I) == coth(2)*I assert cot(pi) == cot(2*pi) == cot(3*pi) assert cot(-pi) == cot(-2*pi) == cot(-3*pi) assert cot(pi/2) == 0 assert cot(-pi/2) == 0 assert cot(5*pi/2) == 0 assert cot(7*pi/2) == 0 assert cot(pi/3) == 1/sqrt(3) assert cot(-2*pi/3) == 1/sqrt(3) assert cot(pi/4) == S.One assert cot(-pi/4) == -S.One assert cot(17*pi/4) == S.One assert cot(-3*pi/4) == S.One assert cot(pi/6) == sqrt(3) assert cot(-pi/6) == -sqrt(3) assert cot(7*pi/6) == sqrt(3) assert cot(-5*pi/6) == sqrt(3) assert cot(x*I) == -coth(x)*I assert cot(k*pi*I) == -coth(k*pi)*I assert cot(r).is_real == True assert cot(10*pi/7) == cot(3*pi/7) assert cot(11*pi/7) == -cot(3*pi/7) assert cot(-11*pi/7) == cot(3*pi/7) def test_cot_rewrite(): x = Symbol('x') neg_exp, pos_exp = exp(-x*I), exp(x*I) assert cot(x).rewrite(exp) == I*(pos_exp+neg_exp)/(pos_exp-neg_exp) assert cot(x).rewrite(sin) == 2*sin(2*x)/sin(x)**2 assert cot(x).rewrite(cos) == -cos(x)/cos(x + S.Pi/2) assert cot(x).rewrite(tan) == 1/tan(x) def test_asin(): x = Symbol('x') assert asin(nan) == nan assert asin(oo) == -I*oo assert asin(-oo) == I*oo # Note: asin(-x) = - asin(x) assert asin(0) == 0 assert asin(1) == pi/2 assert asin(-1) == -pi/2 assert asin(sqrt(3)/2) == pi/3 assert asin(-sqrt(3)/2) == -pi/3 assert asin(sqrt(2)/2) == pi/4 assert asin(-sqrt(2)/2) == -pi/4 assert asin(sqrt((5-sqrt(5))/8)) == pi/5 assert asin(-sqrt((5-sqrt(5))/8)) == -pi/5 assert asin(Rational(1,2)) == pi/6 assert asin(-Rational(1,2)) == -pi/6 assert asin((sqrt(2-sqrt(2)))/2) == pi/8 assert asin(-(sqrt(2-sqrt(2)))/2) == -pi/8 assert asin((sqrt(5)-1)/4) == pi/10 assert asin(-(sqrt(5)-1)/4) == -pi/10 assert asin((sqrt(3)-1)/sqrt(2**3)) == pi/12 assert asin(-(sqrt(3)-1)/sqrt(2**3)) == -pi/12 assert asin(x).diff(x) == 1/sqrt(1-x**2) assert asin(0.2).is_real == True assert asin(-2).is_real == False assert asin(-2*I) == -I*asinh(2) def test_asin_series(): x = Symbol('x') assert asin(x).series(x, 0, 9) == \ x + x**3/6 + 3*x**5/40 + 5*x**7/112 + O(x**9) t5 = asin(x).taylor_term(5, x) assert t5 == 3*x**5/40 assert asin(x).taylor_term(7, x, t5, 0) == 5*x**7/112 def test_asin_rewrite(): x = Symbol('x') assert asin(x).rewrite(log) == -I*log(I*x + (1 - x**2)**(S(1)/2)) assert asin(x).rewrite(atan) == 2*atan(x/(1 + (1 - x**2)**(S(1)/2))) assert asin(x).rewrite(acos) == S.Pi/2 - acos(x) def test_acos(): x = Symbol('x') r = Symbol('r', real=True) assert acos(nan) == nan assert acos(oo) == I*oo assert acos(-oo) == -I*oo # Note: acos(-x) = pi - acos(x) assert acos(0) == pi/2 assert acos(Rational(1,2)) == pi/3 assert acos(-Rational(1,2)) == (2*pi)/3 assert acos(1) == 0 assert acos(-1) == pi assert acos(sqrt(2)/2) == pi/4 assert acos(-sqrt(2)/2) == (3*pi)/4 assert acos(x).diff(x) == -1/sqrt(1-x**2) assert acos(0.2).is_real == True assert acos(-2).is_real == False def test_acos_series(): x = Symbol('x') assert acos(x).series(x, 0, 8) == \ pi/2 - x - x**3/6 - 3*x**5/40 - 5*x**7/112 + O(x**8) assert acos(x).series(x, 0, 8) == pi/2 - asin(x).series(x, 0, 8) t5 = acos(x).taylor_term(5, x) assert t5 == -3*x**5/40 assert acos(x).taylor_term(7, x, t5, 0) == -5*x**7/112 def test_acos_rewrite(): x = Symbol('x') assert acos(x).rewrite(log) == pi/2 + I*log(I*x + (1 - x**2)**(S(1)/2)) assert acos(0).rewrite(atan) == S.Pi/2 assert acos(0.5).rewrite(atan) == acos(0.5).rewrite(log) assert acos(x).rewrite(asin) == S.Pi/2 - asin(x) def test_atan(): x = Symbol('x') r = Symbol('r', real=True) assert atan(nan) == nan assert atan(oo) == pi/2 assert atan(-oo) == -pi/2 assert atan(0) == 0 assert atan(1) == pi/4 assert atan(sqrt(3)) == pi/3 assert atan(oo) == pi/2 assert atan(x).diff(x) == 1/(1+x**2) assert atan(r).is_real == True assert atan(-2*I) == -I*atanh(2) def test_atan_rewrite(): x = Symbol('x') assert atan(x).rewrite(log) == I*log((1 - I*x)/(1 + I*x))/2 def test_atan2(): assert atan2(0, 0) == S.NaN assert atan2(0, 1) == 0 assert atan2(1, 0) == pi/2 assert atan2(1, -1) == 3*pi/4 assert atan2(-1, 1) == -pi/4 assert atan2(0, -1) == pi def test_acot(): x = Symbol('x') r = Symbol('r', real=True) assert acot(nan) == nan assert acot(-oo) == 0 assert acot(oo) == 0 assert acot(1) == pi/4 assert acot(0) == pi/2 assert acot(sqrt(3)/3) == pi/3 assert acot(1/sqrt(3)) == pi/3 assert acot(-1/sqrt(3)) == -pi/3 assert acot(x).diff(x) == -1/(1+x**2) assert acot(r).is_real == True assert acot(I*pi) == -I*acoth(pi) assert acot(-2*I) == I*acoth(2) def test_acot_rewrite(): x = Symbol('x') assert acot(x).rewrite(log) == I*log((x - I)/(x + I))/2 def test_attributes(): x = Symbol('x') assert sin(x).args == (x,) def test_sincos_rewrite(): x = Symbol("x") y = Symbol("y") assert sin(pi/2-x) == cos(x) assert sin(pi-x) == sin(x) assert cos(pi/2-x) == sin(x) assert cos(pi-x) == -cos(x) def _check_even_rewrite(func, arg): """Checks that the expr has been rewritten using f(-x) -> f(x) arg : -x """ return func(arg).args[0] == -arg def _check_odd_rewrite(func, arg): """Checks that the expr has been rewritten using f(-x) -> -f(x) arg : -x """ return func(arg).func.is_Mul def _check_no_rewrite(func, arg): """Checks that the expr is not rewritten""" return func(arg).args[0] == arg def test_evenodd_rewrite(): x, y = symbols('x,y') a = cos(2) #negative b = sin(1) #positive even = [cos] odd = [sin, tan, cot, asin, atan, acot] with_minus = [-1, -2**1024 * E, -pi/105, -x*y, -x-y] for func in even: for expr in with_minus: assert _check_even_rewrite(func, expr) assert _check_no_rewrite(func, a*b) assert func(x-y) == func(y-x) #it doesn't matter which form is canonical for func in odd: for expr in with_minus: assert _check_odd_rewrite(func, expr) assert _check_no_rewrite(func, a*b) assert func(x-y) == -func(y-x) #it doesn't matter which form is canonical def test_issue1448(): x = Symbol('x') assert cot(x).inverse() == acot assert sin(x).rewrite(cot) == 2*cot(x/2)/(1 + cot(x/2)**2) assert cos(x).rewrite(cot) == -(1 - cot(x/2)**2)/(1 + cot(x/2)**2) assert tan(x).rewrite(cot) == 1/cot(x) assert cot(x).fdiff() == -1 - cot(x)**2 def test_as_leading_term_issue2173(): x = Symbol('x') assert sin(x).as_leading_term(x) == x assert cos(x).as_leading_term(x) == 1 assert tan(x).as_leading_term(x) == x assert cot(x).as_leading_term(x) == 1/x assert asin(x).as_leading_term(x) == x assert acos(x).as_leading_term(x) == x assert atan(x).as_leading_term(x) == x assert acot(x).as_leading_term(x) == x def test_atan2_expansion(): x, y = symbols("x,y") assert cancel(atan2(x+1,x**2).diff(x) - atan((x+1)/x**2).diff(x)) == 0 assert cancel(atan(x/y).series(x, 0, 5) - atan2(x, y).series(x, 0, 5) \ + atan2(0, y) - atan(0)) == O(x**5) assert cancel(atan(x/y).series(y, 1, 4) - atan2(x, y).series(y, 1, 4) \ + atan2(x, 1) - atan(x)) == O(y**4) assert cancel(atan((x+y)/y).series(y, 1, 3) - atan2(x+y, y).series(y, 1, 3) \ + atan2(1+x, 1) - atan(1+x)) == O(y**3) assert Matrix([atan2(x, y)]).jacobian([x, y]) \ == Matrix([[y/(x**2+y**2), -x/(x**2+y**2)]]) def test_aseries(): x = Symbol('x') def t(n, v, d, e): assert abs(n(1/v).evalf() - n(1/x).series(x, dir=d).removeO().subs(x, v)) < e t(atan, 0.1, '+', 1e-5) t(atan, -0.1, '-', 1e-5) t(acot, 0.1, '+', 1e-5) t(acot, -0.1, '-', 1e-5) def test_issueXXX(): i = Symbol('i', integer=True) e = Symbol('e', even=True) o = Symbol('o', odd=True) x = Symbol('x') # unknown parity for variable assert cos(4*i*pi) == 1 assert sin(4*i*pi) == 0 assert tan(4*i*pi) == 0 assert cot(4*i*pi) == zoo assert cos(3*i*pi) == cos(pi*i) # +/-1 assert sin(3*i*pi) == 0 assert tan(3*i*pi) == 0 assert cot(3*i*pi) == zoo assert cos(4.0*i*pi) == 1 assert sin(4.0*i*pi) == 0 assert tan(4.0*i*pi) == 0 assert cot(4.0*i*pi) == zoo assert cos(3.0*i*pi) == cos(pi*i) # +/-1 assert sin(3.0*i*pi) == 0 assert tan(3.0*i*pi) == 0 assert cot(3.0*i*pi) == zoo assert cos(4.5*i*pi) == cos(0.5*pi*i) assert sin(4.5*i*pi) == sin(0.5*pi*i) assert tan(4.5*i*pi) == tan(0.5*pi*i) assert cot(4.5*i*pi) == cot(0.5*pi*i) # parity of variable is known assert cos(4*e*pi) == 1 assert sin(4*e*pi) == 0 assert tan(4*e*pi) == 0 assert cot(4*e*pi) == zoo assert cos(3*e*pi) == 1 assert sin(3*e*pi) == 0 assert tan(3*e*pi) == 0 assert cot(3*e*pi) == zoo assert cos(4.0*e*pi) == 1 assert sin(4.0*e*pi) == 0 assert tan(4.0*e*pi) == 0 assert cot(4.0*e*pi) == zoo assert cos(3.0*e*pi) == 1 assert sin(3.0*e*pi) == 0 assert tan(3.0*e*pi) == 0 assert cot(3.0*e*pi) == zoo assert cos(4.5*e*pi) == cos(0.5*pi*e) assert sin(4.5*e*pi) == sin(0.5*pi*e) assert tan(4.5*e*pi) == tan(0.5*pi*e) assert cot(4.5*e*pi) == cot(0.5*pi*e) assert cos(4*o*pi) == 1 assert sin(4*o*pi) == 0 assert tan(4*o*pi) == 0 assert cot(4*o*pi) == zoo assert cos(3*o*pi) == -1 assert sin(3*o*pi) == 0 assert tan(3*o*pi) == 0 assert cot(3*o*pi) == zoo assert cos(4.0*o*pi) == 1 assert sin(4.0*o*pi) == 0 assert tan(4.0*o*pi) == 0 assert cot(4.0*o*pi) == zoo assert cos(3.0*o*pi) == -1 assert sin(3.0*o*pi) == 0 assert tan(3.0*o*pi) == 0 assert cot(3.0*o*pi) == zoo assert cos(4.5*o*pi) == cos(0.5*pi*o) assert sin(4.5*o*pi) == sin(0.5*pi*o) assert tan(4.5*o*pi) == tan(0.5*pi*o) assert cot(4.5*o*pi) == cot(0.5*pi*o) # x could be imaginary assert cos(4*x*pi) == cos(4*pi*x) assert sin(4*x*pi) == sin(4*pi*x) assert tan(4*x*pi) == tan(4*pi*x) assert cot(4*x*pi) == cot(4*pi*x) assert cos(3*x*pi) == cos(3*pi*x) assert sin(3*x*pi) == sin(3*pi*x) assert tan(3*x*pi) == tan(3*pi*x) assert cot(3*x*pi) == cot(3*pi*x) assert cos(4.0*x*pi) == cos(4.0*pi*x) assert sin(4.0*x*pi) == sin(4.0*pi*x) assert tan(4.0*x*pi) == tan(4.0*pi*x) assert cot(4.0*x*pi) == cot(4.0*pi*x) assert cos(3.0*x*pi) == cos(3.0*pi*x) assert sin(3.0*x*pi) == sin(3.0*pi*x) assert tan(3.0*x*pi) == tan(3.0*pi*x) assert cot(3.0*x*pi) == cot(3.0*pi*x) assert cos(4.5*x*pi) == cos(4.5*pi*x) assert sin(4.5*x*pi) == sin(4.5*pi*x) assert tan(4.5*x*pi) == tan(4.5*pi*x) assert cot(4.5*x*pi) == cot(4.5*pi*x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/elementary/__init__.py0000644000175000017500000000016012014170666027362 0ustar georgeskgeorgeskimport complexes import exponential import hyperbolic import integers import trigonometric import miscellaneous wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/0000755000175000017500000000000012014170666025732 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/numbers.py0000644000175000017500000003242112014170666027761 0ustar georgeskgeorgesk""" This module implements some special functions that commonly appear in combinatorial contexts (e.g. in power series); in particular, sequences of rational numbers such as Bernoulli and Fibonacci numbers. Factorials, binomial coefficients and related functions are located in the separate 'factorials' module. """ from sympy import Function, S, Symbol, Rational, oo, Integer, C, Add from sympy.mpmath import bernfrac def _product(a, b): p = 1 for k in xrange(a, b+1): p *= k return p from sympy.utilities.memoization import recurrence_memo # Dummy symbol used for computing polynomial sequences _sym = Symbol('x') #----------------------------------------------------------------------------# # # # Fibonacci numbers # # # #----------------------------------------------------------------------------# class fibonacci(Function): """ Fibonacci numbers / Fibonacci polynomials Usage ===== fibonacci(n) gives the nth Fibonacci number, F_n fibonacci(n, x) gives the nth Fibonacci polynomial in x, F_n(x) Examples ======== >>> from sympy import fibonacci, Symbol >>> [fibonacci(x) for x in range(11)] [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] >>> fibonacci(5, Symbol('t')) t**4 + 3*t**2 + 1 Mathematical description ======================== The Fibonacci numbers are the integer sequence defined by the initial terms F_0 = 0, F_1 = 1 and the two-term recurrence relation F_n = F_{n-1} + F_{n-2}. The Fibonacci polynomials are defined by F_1(x) = 1, F_2(x) = x, and F_n(x) = x*F_{n-1}(x) + F_{n-2}(x) for n > 2. For all positive integers n, F_n(1) = F_n. References and further reading ============================== * http://en.wikipedia.org/wiki/Fibonacci_number * http://mathworld.wolfram.com/FibonacciNumber.html """ @staticmethod @recurrence_memo([0, 1]) def _fib(n, prev): return prev[-1] + prev[-2] @staticmethod @recurrence_memo([None, S.One, _sym]) def _fibpoly(n, prev): return (prev[-2] + _sym*prev[-1]).expand() @classmethod def eval(cls, n, sym=None): if n.is_Integer: n = int(n) if n < 0: return S.NegativeOne**(n+1) * fibonacci(-n) if sym is None: return Integer(cls._fib(n)) else: if n < 1: raise ValueError("Fibonacci polynomials are defined " "only for positive integer indices.") return cls._fibpoly(n).subs(_sym, sym) class lucas(Function): """ Lucas numbers Usage ===== lucas(n) gives the nth Lucas number Examples ======== >>> from sympy import lucas >>> [lucas(x) for x in range(11)] [2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123] Mathematical description ======================== Lucas numbers satisfy a recurrence relation similar to that of the Fibonacci sequence, in which each term is the sum of the preceding two. They are generated by choosing the initial values L_0 = 2 and L_1 = 1. References and further reading ============================== * http://en.wikipedia.org/wiki/Lucas_number """ @classmethod def eval(cls, n): if n.is_Integer: return fibonacci(n+1) + fibonacci(n-1) #----------------------------------------------------------------------------# # # # Bernoulli numbers # # # #----------------------------------------------------------------------------# class bernoulli(Function): r""" Bernoulli numbers / Bernoulli polynomials Usage ===== bernoulli(n) gives the nth Bernoulli number, B_n bernoulli(n, x) gives the nth Bernoulli polynomial in x, B_n(x) Examples ======== >>> from sympy import bernoulli >>> [bernoulli(n) for n in range(11)] [1, -1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66] >>> bernoulli(1000001) 0 Mathematical description ======================== The Bernoulli numbers are a sequence of rational numbers defined by B_0 = 1 and the recursive relation (n > 0) n ___ \ / n + 1 \ 0 = ) | | * B . /___ \ k / k k = 0 They are also commonly defined by their exponential generating function, which is x/(exp(x) - 1). For odd indices > 1, the Bernoulli numbers are zero. The Bernoulli polynomials satisfy the analogous formula n ___ \ / n \ n-k B (x) = ) | | * B * x . n /___ \ k / k k = 0 Bernoulli numbers and Bernoulli polynomials are related as B_n(0) = B_n. Implementation ============== We compute Bernoulli numbers using Ramanujan's formula / n + 3 \ B = (A(n) - S(n)) / | | n \ n / where A(n) = (n+3)/3 when n = 0 or 2 (mod 6), A(n) = -(n+3)/6 when n = 4 (mod 6), and [n/6] ___ \ / n + 3 \ S(n) = ) | | * B /___ \ n - 6*k / n-6*k k = 1 This formula is similar to the sum given in the definition, but cuts 2/3 of the terms. For Bernoulli polynomials, we use the formula in the definition. References and further reading ============================== * http://en.wikipedia.org/wiki/Bernoulli_number * http://en.wikipedia.org/wiki/Bernoulli_polynomial """ # Calculates B_n for positive even n @staticmethod def _calc_bernoulli(n): s = 0 a = int(C.binomial(n+3, n-6)) for j in xrange(1, n//6+1): s += a * bernoulli(n - 6*j) # Avoid computing each binomial coefficient from scratch a *= _product(n-6 - 6*j + 1, n-6*j) a //= _product(6*j+4, 6*j+9) if n % 6 == 4: s = -Rational(n+3, 6) - s else: s = Rational(n+3, 3) - s return s / C.binomial(n+3, n) # We implement a specialized memoization scheme to handle each # case modulo 6 separately _cache = {0: S.One, 2:Rational(1,6), 4:Rational(-1,30)} _highest = {0:0, 2:2, 4:4} @classmethod def eval(cls, n, sym=None): if n.is_Number: if n.is_Integer and n.is_nonnegative: if n is S.Zero: return S.One elif n is S.One: if sym is None: return -S.Half else: return sym - S.Half # Bernoulli numbers elif sym is None: if n.is_odd: return S.Zero n = int(n) # Use mpmath for enormous Bernoulli numbers if n > 500: p, q = bernfrac(n) return Rational(int(p), int(q)) case = n % 6 highest_cached = cls._highest[case] if n <= highest_cached: return cls._cache[n] # To avoid excessive recursion when, say, bernoulli(1000) is # requested, calculate and cache the entire sequence ... B_988, # B_994, B_1000 in increasing order for i in xrange(highest_cached+6, n+6, 6): b = cls._calc_bernoulli(i) cls._cache[i] = b cls._highest[case] = i return b # Bernoulli polynomials else: n, result = int(n), [] for k in xrange(n + 1): result.append(C.binomial(n, k)*cls(k)*sym**(n-k)) return Add(*result) else: raise ValueError("Bernoulli numbers are defined only" " for nonnegative integer indices.") #----------------------------------------------------------------------------# # # # Bell numbers # # # #----------------------------------------------------------------------------# class bell(Function): r""" Bell numbers / Bell polynomials Usage ===== bell(n) gives the nth Bell number, B_n bell(n, x) gives the nth Bell polynomial, B_n(x) Not to be confused with Bernoulli numbers and Bernoulli polynomials, which use the same notation. Examples ======== >>> from sympy import bell, Symbol >>> [bell(n) for n in range(11)] [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975] >>> bell(30) 846749014511809332450147 >>> bell(4, Symbol('t')) t**4 + 6*t**3 + 7*t**2 + t Mathematical description ======================== The Bell numbers satisfy B_0 = 1 and n-1 ___ \ / n - 1 \ B = ) | | * B . n /___ \ k / k k = 0 They are also given by oo ___ n 1 \ k B = - * ) --. n e /___ k! k = 0 The Bell polynomials are given by B_0(x) = 1 and n-1 ___ \ / n - 1 \ B (x) = x * ) | | * B (x). n /___ \ k - 1 / k-1 k = 1 References and further reading ============================== * http://en.wikipedia.org/wiki/Bell_number * http://mathworld.wolfram.com/BellNumber.html * http://mathworld.wolfram.com/BellPolynomial.html """ @staticmethod @recurrence_memo([1, 1]) def _bell(n, prev): s = 1 a = 1 for k in xrange(1, n): a = a * (n-k) // k s += a * prev[k] return s @staticmethod @recurrence_memo([S.One, _sym]) def _bell_poly(n, prev): s = 1 a = 1 for k in xrange(2, n+1): a = a * (n-k+1) // (k-1) s += a * prev[k-1] return (_sym * s).expand() @classmethod def eval(cls, n, sym=None): if n.is_Integer and n.is_nonnegative: if sym is None: return Integer(cls._bell(int(n))) else: return cls._bell_poly(int(n)).subs(_sym, sym) #----------------------------------------------------------------------------# # # # Harmonic numbers # # # #----------------------------------------------------------------------------# class harmonic(Function): r""" Harmonic numbers Usage ===== harmonic(n) gives the nth harmonic number, H_n harmonic(n, m) gives the nth generalized harmonic number of order m, H_{n,m}, where harmonic(n) == harmonic(n, 1) Examples ======== >>> from sympy import harmonic, oo >>> [harmonic(n) for n in range(6)] [0, 1, 3/2, 11/6, 25/12, 137/60] >>> [harmonic(n, 2) for n in range(6)] [0, 1, 5/4, 49/36, 205/144, 5269/3600] >>> harmonic(oo, 2) pi**2/6 Mathematical description ======================== The nth harmonic number is given by 1 + 1/2 + 1/3 + ... + 1/n. More generally, n ___ \ -m H = ) k . n,m /___ k = 1 As n -> oo, H_{n,m} -> zeta(m) (the Riemann zeta function) References and further reading ============================== * http://en.wikipedia.org/wiki/Harmonic_number """ # Generate one memoized Harmonic number-generating function for each # order and store it in a dictionary _functions = {} nargs = (1, 2) @classmethod def eval(cls, n, m=None): if m is None: m = S.One if n == oo: return C.zeta(m) if n.is_Integer and n.is_nonnegative and m.is_Integer: if n == 0: return S.Zero if not m in cls._functions: @recurrence_memo([0]) def f(n, prev): return prev[-1] + S.One / n**m cls._functions[m] = f return cls._functions[m](int(n)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/factorials.py0000644000175000017500000003260712014170666030443 0ustar georgeskgeorgeskfrom sympy.core import S, C, sympify, Function from sympy.ntheory import sieve from math import sqrt from sympy.core.compatibility import reduce class CombinatorialFunction(Function): """Base class for combinatorial functions. """ ############################################################################### ######################## FACTORIAL and MULTI-FACTORIAL ######################## ############################################################################### class factorial(CombinatorialFunction): """Implementation of factorial function over nonnegative integers. For the sake of convenience and simplicity of procedures using this function it is defined for negative integers and returns zero in this case. The factorial is very important in combinatorics where it gives the number of ways in which 'n' objects can be permuted. It also arises in calculus, probability, number theory etc. There is strict relation of factorial with gamma function. In fact n! = gamma(n+1) for nonnegative integers. Rewrite of this kind is very useful in case of combinatorial simplification. Computation of the factorial is done using two algorithms. For small arguments naive product is evaluated. However for bigger input algorithm Prime-Swing is used. It is the fastest algorithm known and computes n! via prime factorization of special class of numbers, called here the 'Swing Numbers'. >>> from sympy import Symbol, factorial >>> n = Symbol('n', integer=True) >>> factorial(-2) 0 >>> factorial(0) 1 >>> factorial(7) 5040 >>> factorial(n) n! >>> factorial(2*n) (2*n)! """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return C.gamma(self.args[0] + 1)*C.polygamma(0, self.args[0] + 1) else: raise ArgumentIndexError(self, argindex) _small_swing = [ 1,1,1,3,3,15,5,35,35,315,63,693,231,3003,429,6435,6435,109395, 12155,230945,46189,969969,88179,2028117,676039,16900975,1300075, 35102025,5014575,145422675,9694845,300540195,300540195 ] @classmethod def _swing(cls, n): if n < 33: return cls._small_swing[n] else: N, primes = int(sqrt(n)), [] for prime in sieve.primerange(3, N+1): p, q = 1, n while True: q //= prime if q > 0: if q & 1 == 1: p *= prime else: break if p > 1: primes.append(p) for prime in sieve.primerange(N+1, n//3 + 1): if (n // prime) & 1 == 1: primes.append(prime) L_product = R_product = 1 for prime in sieve.primerange(n//2 + 1, n+1): L_product *= prime for prime in primes: R_product *= prime return L_product*R_product @classmethod def _recursive(cls, n): if n < 2: return 1 else: return (cls._recursive(n//2)**2)*cls._swing(n) @classmethod def eval(cls, n): n = sympify(n) if n.is_Number: if n is S.Zero: return S.One elif n.is_Integer: if n.is_negative: return S.Zero else: n, result = n.p, 1 if n < 20: for i in range(2, n+1): result *= i else: N, bits = n, 0 while N != 0: if N & 1 == 1: bits += 1 N = N >> 1 result = cls._recursive(n)*2**(n-bits) return C.Integer(result) if n.is_negative: return S.Zero def _eval_rewrite_as_gamma(self, n): return C.gamma(n + 1) def _eval_is_integer(self): return self.args[0].is_integer class MultiFactorial(CombinatorialFunction): pass class factorial2(CombinatorialFunction): """The double factorial n!!, not to be confused with (n!)! The double facotrial is defined for integers >= -1 as , | n*(n - 2)*(n - 4)* ... * 1 for n odd n!! = -| n*(n - 2)*(n - 4)* ... * 2 for n even | 1 for n = 0, -1 ' >>> from sympy import factorial2, var >>> var('n') n >>> factorial2(n + 1) (n + 1)!! >>> factorial2(5) 15 >>> factorial2(-1) 1 """ nargs = 1 @classmethod def eval(cls, arg): if arg.is_Number: if arg == S.Zero or arg == S.NegativeOne: return S.One return factorial2(arg - 2)*arg def _sympystr(self, p): if self.args[0].is_Atom: return "%s!!" % p.doprint(self.args[0]) else: return "(%s)!!" % p.doprint(self.args[0]) ############################################################################### ######################## RISING and FALLING FACTORIALS ######################## ############################################################################### class RisingFactorial(CombinatorialFunction): """Rising factorial (also called Pochhammer symbol) is a double valued function arising in concrete mathematics, hypergeometric functions and series expansions. It is defined by rf(x, k) = x * (x+1) * ... * (x + k-1) where 'x' can be arbitrary expression and 'k' is an integer. For more information check "Concrete mathematics" by Graham, pp. 66 or visit http://mathworld.wolfram.com/RisingFactorial.html page. >>> from sympy import rf >>> from sympy.abc import x >>> rf(x, 0) 1 >>> rf(1, 5) 120 >>> rf(x, 5) == x*(1 + x)*(2 + x)*(3 + x)*(4 + x) True """ nargs = 2 @classmethod def eval(cls, x, k): x = sympify(x) k = sympify(k) if x is S.NaN: return S.NaN elif x is S.One: return factorial(k) elif k.is_Integer: if k is S.NaN: return S.NaN elif k is S.Zero: return S.One else: if k.is_positive: if x is S.Infinity: return S.Infinity elif x is S.NegativeInfinity: if k.is_odd: return S.NegativeInfinity else: return S.Infinity else: return reduce(lambda r, i: r*(x+i), xrange(0, int(k)), 1) else: if x is S.Infinity: return S.Infinity elif x is S.NegativeInfinity: return S.Infinity else: return 1/reduce(lambda r, i: r*(x-i), xrange(1, abs(int(k))+1), 1) def _eval_rewrite_as_gamma(self, x, k): return C.gamma(x + k) / C.gamma(x) class FallingFactorial(CombinatorialFunction): """Falling factorial (related to rising factorial) is a double valued function arising in concrete mathematics, hypergeometric functions and series expansions. It is defined by ff(x, k) = x * (x-1) * ... * (x - k+1) where 'x' can be arbitrary expression and 'k' is an integer. For more information check "Concrete mathematics" by Graham, pp. 66 or visit http://mathworld.wolfram.com/FallingFactorial.html page. >>> from sympy import ff >>> from sympy.abc import x >>> ff(x, 0) 1 >>> ff(5, 5) 120 >>> ff(x, 5) == x*(x-1)*(x-2)*(x-3)*(x-4) True """ nargs = 2 @classmethod def eval(cls, x, k): x = sympify(x) k = sympify(k) if x is S.NaN: return S.NaN elif k.is_Integer: if k is S.NaN: return S.NaN elif k is S.Zero: return S.One else: if k.is_positive: if x is S.Infinity: return S.Infinity elif x is S.NegativeInfinity: if k.is_odd: return S.NegativeInfinity else: return S.Infinity else: return reduce(lambda r, i: r*(x-i), xrange(0, int(k)), 1) else: if x is S.Infinity: return S.Infinity elif x is S.NegativeInfinity: return S.Infinity else: return 1/reduce(lambda r, i: r*(x+i), xrange(1, abs(int(k))+1), 1) def _eval_rewrite_as_gamma(self, x, k): return (-1)**k * C.gamma(-x + k) / C.gamma(-x) rf = RisingFactorial ff = FallingFactorial ############################################################################### ########################### BINOMIAL COEFFICIENTS ############################# ############################################################################### class binomial(CombinatorialFunction): """Implementation of the binomial coefficient. It can be defined in two ways depending on its desired interpretation: C(n,k) = n!/(k!(n-k)!) or C(n, k) = ff(n, k)/k! First, in a strict combinatorial sense it defines the number of ways we can choose 'k' elements from a set of 'n' elements. In this case both arguments are nonnegative integers and binomial is computed using an efficient algorithm based on prime factorization. The other definition is generalization for arbitrary 'n', however 'k' must also be nonnegative. This case is very useful when evaluating summations. For the sake of convenience for negative 'k' this function will return zero no matter what valued is the other argument. >>> from sympy import Symbol, Rational, binomial >>> n = Symbol('n', integer=True) >>> binomial(15, 8) 6435 >>> binomial(n, -1) 0 >>> [ binomial(0, i) for i in range(1)] [1] >>> [ binomial(1, i) for i in range(2)] [1, 1] >>> [ binomial(2, i) for i in range(3)] [1, 2, 1] >>> [ binomial(3, i) for i in range(4)] [1, 3, 3, 1] >>> [ binomial(4, i) for i in range(5)] [1, 4, 6, 4, 1] >>> binomial(Rational(5,4), 3) -5/128 >>> binomial(n, 3) n*(n - 2)*(n - 1)/6 """ nargs = 2 def fdiff(self, argindex=1): if argindex == 1: # http://functions.wolfram.com/GammaBetaErf/Binomial/20/01/01/ n, k = self.args return binomial(n, k)*(C.polygamma(0, n + 1) - C.polygamma(0, n - k + 1)) elif argindex == 2: # http://functions.wolfram.com/GammaBetaErf/Binomial/20/01/02/ n, k = self.args return binomial(n, k)*(C.polygamma(0, n - k + 1) - C.polygamma(0, k + 1)) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, n, k): n, k = map(sympify, (n, k)) if k.is_Number: if k.is_Integer: if k < 0: return S.Zero elif k == 0 or n == k: return S.One elif n.is_Integer and n >= 0: n, k = int(n), int(k) if k > n: return S.Zero elif k > n // 2: k = n - k M, result = int(sqrt(n)), 1 for prime in sieve.primerange(2, n+1): if prime > n - k: result *= prime elif prime > n // 2: continue elif prime > M: if n % prime < k % prime: result *= prime else: N, K = n, k exp = a = 0 while N > 0: a = int((N % prime) < (K % prime + a)) N, K = N // prime, K // prime exp = a + exp if exp > 0: result *= prime**exp return C.Integer(result) else: result = n - k + 1 for i in xrange(2, k+1): result *= n-k+i result /= i return result elif k.is_negative: return S.Zero else: d = n - k if d.is_Integer: return cls.eval(n, d) def _eval_rewrite_as_factorial(self, n, k): return C.factorial(n)/(C.factorial(k)*C.factorial(n - k)) def _eval_rewrite_as_gamma(self, n, k): return C.gamma(n + 1)/(C.gamma(k + 1)*C.gamma(n - k + 1)) def _eval_is_integer(self): return self.args[0].is_integer and self.args[1].is_integer wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/tests/0000755000175000017500000000000012014170666027074 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/tests/test_comb_numbers.py0000644000175000017500000000375012014170666033165 0ustar georgeskgeorgeskfrom sympy import bernoulli, Symbol, harmonic, Rational, oo, zoo, pi, bell, \ fibonacci, lucas x = Symbol('x') def test_bernoulli(): assert bernoulli(0) == 1 assert bernoulli(1) == Rational(-1,2) assert bernoulli(2) == Rational(1,6) assert bernoulli(3) == 0 assert bernoulli(4) == Rational(-1,30) assert bernoulli(5) == 0 assert bernoulli(6) == Rational(1,42) assert bernoulli(7) == 0 assert bernoulli(8) == Rational(-1,30) assert bernoulli(10) == Rational(5,66) assert bernoulli(1000001) == 0 assert bernoulli(0, x) == 1 assert bernoulli(1, x) == x-Rational(1,2) assert bernoulli(2, x) == x**2-x+Rational(1,6) assert bernoulli(3, x) == x**3 - (3*x**2)/2 + x/2 # Should be fast; computed with mpmath b = bernoulli(1000) assert b.p % 10**10 == 7950421099 assert b.q == 342999030 b = bernoulli(10**6, evaluate=False).evalf() assert str(b) == '-2.23799235765713e+4767529' def test_fibonacci(): assert [fibonacci(n) for n in range(-3, 5)] == [2, -1, 1, 0, 1, 1, 2, 3] assert fibonacci(100) == 354224848179261915075 assert [lucas(n) for n in range(-3, 5)] == [-4, 3, -1, 2, 1, 3, 4, 7] assert lucas(100) == 792070839848372253127 assert fibonacci(1, x) == 1 assert fibonacci(2, x) == x assert fibonacci(3, x) == x**2 + 1 assert fibonacci(4, x) == x**3 + 2*x def test_bell(): assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877] assert bell(0, x) == 1 assert bell(1, x) == x assert bell(2, x) == x**2 + x assert bell(5, x) == x**5 + 10*x**4 + 25*x**3 + 15*x**2 + x def test_harmonic(): assert harmonic(1,1) == 1 assert harmonic(2,1) == Rational(3,2) assert harmonic(3,1) == Rational(11,6) assert harmonic(4,1) == Rational(25,12) # assert harmonic(3,1) == harmonic(3) assert harmonic(3,5) == 1 + Rational(1,2**5) + Rational(1,3**5) assert harmonic(10,0) == 10 assert harmonic(oo,1) == zoo assert harmonic(oo,2) == (pi**2)/6 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/tests/test_comb_factorials.py0000644000175000017500000000771712014170666033650 0ustar georgeskgeorgeskfrom sympy import (Symbol, symbols, factorial, factorial2, binomial, rf, ff, gamma, polygamma, EulerGamma, O, pi, nan, oo) def test_rf_eval_apply(): x, y = symbols('x,y') assert rf(nan, y) == nan assert rf(x, y) == rf(x, y) assert rf(oo, 0) == 1 assert rf(-oo, 0) == 1 assert rf(oo, 6) == oo assert rf(-oo, 7) == -oo assert rf(oo, -6) == oo assert rf(-oo, -7) == oo assert rf(x, 0) == 1 assert rf(x, 1) == x assert rf(x, 2) == x*(x+1) assert rf(x, 3) == x*(x+1)*(x+2) assert rf(x, 5) == x*(x+1)*(x+2)*(x+3)*(x+4) assert rf(x, -1) == 1/(x-1) assert rf(x, -2) == 1/((x-1)*(x-2)) assert rf(x, -3) == 1/((x-1)*(x-2)*(x-3)) assert rf(1, 100) == factorial(100) def test_ff_eval_apply(): x, y = symbols('x,y') assert ff(nan, y) == nan assert ff(x, y) == ff(x, y) assert ff(oo, 0) == 1 assert ff(-oo, 0) == 1 assert ff(oo, 6) == oo assert ff(-oo, 7) == -oo assert ff(oo, -6) == oo assert ff(-oo, -7) == oo assert ff(x, 0) == 1 assert ff(x, 1) == x assert ff(x, 2) == x*(x-1) assert ff(x, 3) == x*(x-1)*(x-2) assert ff(x, 5) == x*(x-1)*(x-2)*(x-3)*(x-4) assert ff(x, -1) == 1/(x+1) assert ff(x, -2) == 1/((x+1)*(x+2)) assert ff(x, -3) == 1/((x+1)*(x+2)*(x+3)) assert ff(100, 100) == factorial(100) def test_factorial(): n = Symbol('n', integer=True) assert factorial(-2) == 0 assert factorial(0) == 1 assert factorial(7) == 5040 assert factorial(n).func == factorial assert factorial(2*n).func == factorial def test_factorial_diff(): n = Symbol('n', integer=True) assert factorial(n).diff(n) == \ gamma(1 + n)*polygamma(0, 1 + n) assert factorial(n**2).diff(n) == \ 2*n*gamma(1 + n**2)*polygamma(0, 1 + n**2) def test_factorial_series(): n = Symbol('n', integer=True) assert factorial(n).series(n, 0, 3) == \ 1 - n*EulerGamma + n**2*EulerGamma**2/2 + pi**2*n**2/12 + O(n**3) def test_factorial_rewrite(): n = Symbol('n', integer=True) assert factorial(n).rewrite(gamma) == gamma(n + 1) def test_factorial2(): n = Symbol('n', integer=True) assert factorial2(-1) == 1 assert factorial2(0) == 1 assert factorial2(7) == 105 assert factorial2(8) == 384 assert factorial2(n).func == factorial2 def test_binomial(): n = Symbol('n', integer=True) k = Symbol('k', integer=True) u = Symbol('v', negative=True) v = Symbol('m', positive=True) assert binomial(0, 0) == 1 assert binomial(1, 1) == 1 assert binomial(10, 10) == 1 assert binomial(1, 2) == 0 assert binomial(1, -1) == 0 assert binomial(-1, 1) == -1 assert binomial(-10, 1) == -10 assert binomial(-10, 7) == -11440 assert binomial(n, -1) == 0 assert binomial(n, 0) == 1 assert binomial(n, 1) == n assert binomial(n, 2) == n*(n - 1)/2 assert binomial(n, n-2) == n*(n - 1)/2 assert binomial(n, n-1) == n assert binomial(n, n) == 1 assert binomial(n, n+1) == 0 assert binomial(n, u) == 0 assert binomial(n, v).func == binomial assert binomial(n, k).func == binomial def test_binomial_diff(): n = Symbol('n', integer=True) k = Symbol('k', integer=True) assert binomial(n, k).diff(n) == \ (-polygamma(0, 1 + n - k) + polygamma(0, 1 + n))*binomial(n, k) assert binomial(n**2, k**3).diff(n) == \ 2*n*(-polygamma(0, 1 + n**2 - k**3) + polygamma(0, 1 + n**2))*binomial(n**2, k**3) assert binomial(n, k).diff(k) == \ (-polygamma(0, 1 + k) + polygamma(0, 1 + n - k))*binomial(n, k) assert binomial(n**2, k**3).diff(k) == \ 3*k**2*(-polygamma(0, 1 + k**3) + polygamma(0, 1 + n**2 - k**3))*binomial(n**2, k**3) def test_binomial_rewrite(): n = Symbol('n', integer=True) k = Symbol('k', integer=True) assert binomial(n, k).rewrite(factorial) == factorial(n)/(factorial(k)*factorial(n - k)) assert binomial(n, k).rewrite(gamma) == gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/combinatorial/__init__.py0000644000175000017500000000004112014170666030036 0ustar georgeskgeorgeskimport factorials import numbers wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/0000755000175000017500000000000012014170666024527 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/delta_functions.py0000644000175000017500000001342512014170666030267 0ustar georgeskgeorgeskfrom sympy.core import S, sympify, diff from sympy.core.function import Function, ArgumentIndexError from sympy.polys.polyerrors import PolynomialError ############################################################################### ################################ DELTA FUNCTION ############################### ############################################################################### class DiracDelta(Function): """DiracDelta function, and the derivatives. DiracDelta function has the following properties: 1) diff(Heaviside(x),x) = DiracDelta(x) 2) integrate(DiracDelta(x-a)*f(x),(x,-oo,oo)) = f(a) integrate(DiracDelta(x-a)*f(x),(x,a-e,a+e)) = f(a) 3) DiracDelta(x) = 0, for all x != 0 4) DiracDelta(g(x)) = Sum_i(DiracDelta(x-xi)/abs(g'(xi))) Where xis are the roots of g Derivatives of k order of DiracDelta have the following property: 5) DiracDelta(x,k) = 0, for all x!=0 For more information, see: http://mathworld.wolfram.com/DeltaFunction.html """ nargs = (1,2) def fdiff(self, argindex = 1): if argindex == 1: #I didn't know if there is a better way to handle default arguments k = 0 if len(self.args) > 1: k = self.args[1] return DiracDelta(self.args[0],k+1) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg, k=0): k = sympify(k) if not k.is_Integer or k.is_negative: raise ValueError("Error: the second argument of DiracDelta must be \ a non-negative integer, %s given instead." %(k,)) arg = sympify(arg) if arg is S.NaN: return S.NaN if arg.is_positive or arg.is_negative: return S.Zero elif arg.is_zero: return S.Infinity def simplify(self, x): """simplify(self, x) Compute a simplified representation of the function using property number 4. x can be: - a symbol Examples -------- >>> from sympy import DiracDelta >>> from sympy.abc import x, y >>> DiracDelta(x*y).simplify(x) DiracDelta(x)/Abs(y) >>> DiracDelta(x*y).simplify(y) DiracDelta(y)/Abs(x) >>> DiracDelta(x**2 + x - 2).simplify(x) DiracDelta(x - 1)/3 + DiracDelta(x + 2)/3 """ from sympy.polys.polyroots import roots if not self.args[0].has(x) or (len(self.args)>1 and self.args[1] != 0 ): return self try: argroots = roots(self.args[0],x, \ multiple=True) result = 0 valid = True darg = diff(self.args[0], x) for r in argroots: #should I care about multiplicities of roots? if r.is_real and not darg.subs(x,r).is_zero: result = result + DiracDelta(x - r)/abs(darg.subs(x,r)) else: valid = False break if valid: return result except PolynomialError: pass return self def is_simple(self,x): """is_simple(self, x) Tells whether the argument(args[0]) of DiracDelta is a linear expression in x. x can be: - a symbol Examples -------- >>> from sympy import DiracDelta, cos >>> from sympy.abc import x, y >>> DiracDelta(x*y).is_simple(x) True >>> DiracDelta(x*y).is_simple(y) True >>> DiracDelta(x**2+x-2).is_simple(x) False >>> DiracDelta(cos(x)).is_simple(x) False """ p = self.args[0].as_poly(x) if p: return p.degree() == 1 return False ############################################################################### ############################## HEAVISIDE FUNCTION ############################# ############################################################################### class Heaviside(Function): """Heaviside Piecewise function. Heaviside function has the following properties: 1) diff(Heaviside(x),x) = DiracDelta(x) ( 0, if x<0 2) Heaviside(x) = < [*] 1/2 if x==0 ( 1, if x>0 [*]Regarding to the value at 0, Mathematica adopt the value H(0)=1, and Maple H(0)=undefined I think is better to have H(0)=1/2, due to the following: integrate(DiracDelta(x),x) = Heaviside(x) integrate(DiracDelta(x),(x,-oo,oo)) = 1 and since DiracDelta is a symmetric function, integrate(DiracDelta(x),(x,0,oo)) should be 1/2 in fact, that is what maple returns. If we take Heaviside(0)=1/2, we would have integrate(DiracDelta(x),(x,0,oo)) = Heaviside(oo)-Heaviside(0)=1-1/2= 1/2 and integrate(DiracDelta(x),(x,-oo,0)) = Heaviside(0)-Heaviside(-oo)=1/2-0= 1/2 If we consider, instead Heaviside(0)=1, we would have integrate(DiracDelta(x),(x,0,oo)) = Heaviside(oo)-Heaviside(0) = 0 and integrate(DiracDelta(x),(x,-oo,0)) = Heaviside(0)-Heaviside(-oo) = 1 For more information, see: http://mathworld.wolfram.com/HeavisideStepFunction.html """ nargs = 1 def fdiff(self, argindex = 1): if argindex == 1: # property number 1 return DiracDelta(self.args[0]) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): arg = sympify(arg) if arg is S.NaN: return S.NaN elif arg.is_negative: return S.Zero elif arg.is_zero: return S.Half elif arg.is_positive: return S.One wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/bsplines.py0000644000175000017500000001073212014170666026723 0ustar georgeskgeorgeskfrom sympy.core import S, sympify, expand from sympy.functions import Piecewise, piecewise_fold from sympy.functions.elementary.piecewise import ExprCondPair from sympy.core.sets import Interval def _add_splines(c, b1, d, b2): """Construct c*b1 + d*b2.""" if b1 == S.Zero or c == S.Zero: return expand(piecewise_fold(d*b2)) if b2 == S.Zero or d == S.Zero: return expand(piecewise_fold(c*b1)) new_args = [] n_intervals = len(b1.args) assert(n_intervals==len(b2.args)) new_args.append((expand(c*b1.args[0].expr), b1.args[0].cond)) for i in range(1, n_intervals-1): new_args.append(( expand(c*b1.args[i].expr+d*b2.args[i-1].expr), b1.args[i].cond )) new_args.append((expand(d*b2.args[-2].expr), b2.args[-2].cond)) new_args.append(b2.args[-1]) return Piecewise(*new_args) def bspline_basis(d, knots, n, x, close=True): """The n-th B-spline at x of degree d with knots. B-Splines are piecewise polynomials of degree d [1]. They are defined on a set of knots, which is a sequence of integers or floats. The 0th degree splines have a value of one on a single interval: >>> from sympy import bspline_basis >>> from sympy.abc import x >>> d = 0 >>> knots = range(5) >>> bspline_basis(d, knots, 0, x) Piecewise((1, [0, 1]), (0, True)) For a given (d, knots) there are len(knots)-d-1 B-splines defined, that are indexed by n (starting at 0). Here is an example of a cubic B-spline: >>> bspline_basis(3, range(5), 0, x) Piecewise((x**3/6, [0, 1)), (-x**3/2 + 2*x**2 - 2*x + 2/3, [1, 2)), (x**3/2 - 4*x**2 + 10*x - 22/3, [2, 3)), (-x**3/6 + 2*x**2 - 8*x + 32/3, [3, 4]), (0, True)) By repeating knot points, you can introduce discontinuities in the B-splines and their derivatives: >>> d = 1 >>> knots = [0,0,2,3,4] >>> bspline_basis(d, knots, 0, x) Piecewise((-x/2 + 1, [0, 2]), (0, True)) It is quite time consuming to construct and evaluate B-splines. If you need to evaluate a B-splines many times, it is best to lambdify them first: >>> from sympy import lambdify >>> d = 3 >>> knots = range(10) >>> b0 = bspline_basis(d, knots, 0, x) >>> f = lambdify(x, b0) >>> y = f(0.5) [1] http://en.wikipedia.org/wiki/B-spline """ knots = [sympify(k) for k in knots] d = int(d) n = int(n) n_knots = len(knots) n_intervals = n_knots-1 if n+d+1 > n_intervals: raise ValueError('n+d+1 must not exceed len(knots)-1') if d==0: result = Piecewise( (S.One, Interval(knots[n], knots[n+1], False, True)), (0, True) ) elif d > 0: denom = knots[n+d] - knots[n] if denom != S.Zero: A = (x - knots[n])/denom b1 = bspline_basis(d-1, knots, n, x, close=False) else: b1 = A = S.Zero denom = knots[n+d+1] - knots[n+1] if denom != S.Zero: B = (knots[n+d+1] - x)/denom b2 = bspline_basis(d-1, knots, n+1, x, close=False) else: b2 = B = S.Zero result = _add_splines(A, b1, B, b2) else: raise ValueError('degree must be non-negative: %r' % n) if close: final_ec_pair = result.args[-2] final_cond = final_ec_pair.cond final_expr = final_ec_pair.expr new_args = final_cond.args[:3] + (False,) new_ec_pair = ExprCondPair(final_expr, Interval(*new_args)) new_args = result.args[:-2] + (new_ec_pair, result.args[-1]) result = Piecewise(*new_args) return result def bspline_basis_set(d, knots, x): """Return the len(knots)-d-1 B-splines at x of degree d with knots. This function returns a list of Piecewise polynomials that are the len(knots)-d-1 B-splines of degree d for the given knots. This function calls bspline_basis(d, knots, n, x) for different values of n. >>> from sympy import bspline_basis_set >>> from sympy.abc import x >>> d = 2 >>> knots = range(5) >>> splines = bspline_basis_set(d, knots, x) >>> splines [Piecewise((x**2/2, [0, 1)), (-x**2 + 3*x - 3/2, [1, 2)), (x**2/2 - 3*x + 9/2, [2, 3]), (0, True)), Piecewise((x**2/2 - x + 1/2, [1, 2)), (-x**2 + 5*x - 11/2, [2, 3)), (x**2/2 - 4*x + 8, [3, 4]), (0, True))] """ n_splines = len(knots)-d-1 return [bspline_basis(d, knots, i, x) for i in range(n_splines)] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tensor_functions.py0000644000175000017500000000406312014170666030506 0ustar georgeskgeorgeskimport operator from sympy.core.compatibility import reduce from sympy.core.function import Function from sympy.core import sympify, S, Integer ############################################################################### ###################### Kronecker Delta, Levi-Civita etc. ###################### ############################################################################### class Dij(Function): """ Represents the Kronecker Delta Function if i == j, Dij(i, j) = 1 otherwise Dij(i, j) = 0 where i, j are usually integers """ nargs = (1, 2) @classmethod def eval(cls, i, j=0): i, j = map(sympify, (i, j)) if i == j: return S.One elif i.is_number and j.is_number: return S.Zero def Eijk(*args, **kwargs): """ Represent the Levi-Civita symbol. This is just compatibility wrapper to LeviCivita(). """ return LeviCivita(*args, **kwargs) def prod(a): return reduce(operator.mul, a, 1) def eval_levicivita(*args): """Evaluate Levi-Civita symbol.""" from sympy import factorial n = len(args) return prod( prod(args[j] - args[i] for j in xrange(i + 1, n)) / factorial(i) for i in xrange(n)) # converting factorial(i) to int is slightly faster class LeviCivita(Function): """Represent the Levi-Civita symbol. For even permutations of indices it returns 1, for odd permutations -1, and for everything else (a repeated index) it returns 0. Thus it represents an alternating pseudotensor. >>> from sympy import LeviCivita, symbols >>> LeviCivita(1,2,3) 1 >>> LeviCivita(1,3,2) -1 >>> LeviCivita(1,2,2) 0 >>> i,j,k = symbols('i j k') >>> LeviCivita(i,j,k) LeviCivita(i, j, k) >>> LeviCivita(i,j,i) 0 """ @classmethod def eval(cls, *args): if all(isinstance(a, (int, Integer)) for a in args): return eval_levicivita(*args) if len(set(args)) < len(args): return S.Zero def doit(self): return eval_levicivita(*self.args) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/gamma_functions.py0000644000175000017500000003372112014170666030261 0ustar georgeskgeorgeskfrom sympy.core import Add, S, C, sympify, oo, pi from sympy.core.function import Function, ArgumentIndexError from zeta_functions import zeta from error_functions import erf from sympy.functions.elementary.exponential import log from sympy.functions.elementary.integers import floor from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.combinatorial.numbers import bernoulli from sympy.functions.combinatorial.factorials import rf ############################################################################### ############################ COMPLETE GAMMA FUNCTION ########################## ############################################################################### class gamma(Function): """The gamma function returns a function which passes through the integral values of the factorial function, i.e. though defined in the complex plane, when n is an integer, gamma(n) = (n - 1)! Reference: http://en.wikipedia.org/wiki/Gamma_function """ nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return gamma(self.args[0])*polygamma(0, self.args[0]) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.Infinity elif arg.is_Integer: if arg.is_positive: return C.factorial(arg-1) else: return S.ComplexInfinity elif arg.is_Rational: if arg.q == 2: n = abs(arg.p) // arg.q if arg.is_positive: k, coeff = n, S.One else: n = k = n + 1 if n & 1 == 0: coeff = S.One else: coeff = S.NegativeOne for i in range(3, 2*k, 2): coeff *= i if arg.is_positive: return coeff*sqrt(S.Pi) / 2**n else: return 2**n*sqrt(S.Pi) / coeff def _eval_expand_func(self, deep=True, **hints): if deep: arg = self.args[0].expand(deep, **hints) else: arg = self.args[0] if arg.is_Add: coeff, tail = arg.as_coeff_add() if coeff and coeff.q != 1: tail = (C.Rational(1, coeff.q),) + tail coeff = floor(coeff) tail = arg._new_rawargs(*tail, **dict(reeval=False)) return gamma(tail)*C.RisingFactorial(tail, coeff) return self.func(*self.args) def _eval_is_real(self): return self.args[0].is_real def _eval_rewrite_as_tractable(self, z): return C.exp(loggamma(z)) def _eval_nseries(self, x, n, logx): x0 = self.args[0].limit(x, 0) if not (x0.is_Integer and x0 <= 0): return super(gamma, self)._eval_nseries(x, n, logx) t = self.args[0] - x0 return (gamma(t + 1)/rf(self.args[0], -x0 + 1))._eval_nseries(x, n, logx) ############################################################################### ################## LOWER and UPPER INCOMPLETE GAMMA FUNCTIONS ################# ############################################################################### class lowergamma(Function): r""" Lower incomplete gamma function It can be defined as the meromorphic continuation of .. math :: \gamma(s, x) = \int_0^x t^{s-1} e^{-t} \mathrm{d}t. This can be shown to be the same as .. math :: \gamma(s, x) = \frac{x^s}{s} {}_1F_1\left.\left({s \atop s+1} \right| -x\right), where :math:`{}_1F_1` is the (confluent) hypergeometric function. **See also:** :class:`gamma`, :class:`uppergamma`, :class:`hyper`. **Examples** >>> from sympy import lowergamma, S >>> from sympy.abc import s, x >>> lowergamma(s, x) lowergamma(s, x) >>> lowergamma(3, x) -x**2*exp(-x) - 2*x*exp(-x) + 2 - 2*exp(-x) >>> lowergamma(-S(1)/2, x) -2*pi**(1/2)*erf(x**(1/2)) - 2*exp(-x)/x**(1/2) **References** - Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, Section 5, Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables - http://en.wikipedia.org/wiki/Incomplete_gamma_function """ nargs = 2 def fdiff(self, argindex=2): from sympy import meijerg if argindex == 2: a, z = self.args return C.exp(-z)*z**(a-1) elif argindex == 1: a, z = self.args return gamma(a)*digamma(a) - log(z)*uppergamma(a, z) \ + meijerg([], [1, 1], [0, 0, a], [], z) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, a, x): if a.is_Number: # TODO this should be non-recursive if a is S.One: return S.One - C.exp(-x) elif a is S.Half: return sqrt(pi)*erf(sqrt(x)) elif a.is_Integer or (2*a).is_Integer: b = a - 1 if b.is_positive: return b*cls(b, x) - x**b * C.exp(-x) if not a.is_Integer: return (cls(a + 1, x) + x**a * C.exp(-x))/a def _eval_evalf(self, prec): from sympy.mpmath import mp from sympy import Expr a = self.args[0]._to_mpmath(prec) z = self.args[1]._to_mpmath(prec) oprec = mp.prec mp.prec = prec res = mp.gammainc(a, 0, z) mp.prec = oprec return Expr._from_mpmath(res, prec) class uppergamma(Function): r""" Upper incomplete gamma function It can be defined as the meromorphic continuation of .. math :: \Gamma(s, x) = \int_x^\infty t^{s-1} e^{-t} \mathrm{d}t = \Gamma(s) - \gamma(s, x). This can be shown to be the same as .. math :: \Gamma(s, x) = \Gamma(s) - \frac{x^s}{s} {}_1F_1\left.\left({s \atop s+1} \right| -x\right), where :math:`{}_1F_1` is the (confluent) hypergeometric function. **Examples** >>> from sympy import uppergamma, S >>> from sympy.abc import s, x >>> uppergamma(s, x) uppergamma(s, x) >>> uppergamma(3, x) x**2*exp(-x) + 2*x*exp(-x) + 2*exp(-x) >>> uppergamma(-S(1)/2, x) -2*pi**(1/2)*(-erf(x**(1/2)) + 1) + 2*exp(-x)/x**(1/2) **See also:** :class:`gamma`, :class:`lowergamma`, :class:`hyper`. **References** - Abramowitz, Milton; Stegun, Irene A., eds. (1965), Chapter 6, Section 5, Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables - http://en.wikipedia.org/wiki/Incomplete_gamma_function """ nargs = 2 def fdiff(self, argindex=2): from sympy import meijerg if argindex == 2: a, z = self.args return -C.exp(-z)*z**(a-1) elif argindex == 1: a, z = self.args return uppergamma(a, z)*log(z) + meijerg([], [1, 1], [0, 0, a], [], z) else: raise ArgumentIndexError(self, argindex) def _eval_evalf(self, prec): from sympy.mpmath import mp from sympy import Expr a = self.args[0]._to_mpmath(prec) z = self.args[1]._to_mpmath(prec) oprec = mp.prec mp.prec = prec res = mp.gammainc(a, z, mp.inf) mp.prec = oprec return Expr._from_mpmath(res, prec) @classmethod def eval(cls, a, z): if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: return S.Zero elif z is S.Zero: return gamma(a) if a.is_Number: # TODO this should be non-recursive if a is S.One: return C.exp(-z) elif a is S.Half: return sqrt(pi)*(1 - erf(sqrt(z))) # TODO could use erfc... elif a.is_Integer or (2*a).is_Integer: b = a - 1 if b.is_positive: return b*cls(b, z) + z**b * C.exp(-z) if not a.is_Integer: return (cls(a + 1, z) - z**a * C.exp(-z))/a ############################################################################### ########################### GAMMA RELATED FUNCTIONS ########################### ############################################################################### class polygamma(Function): """The function polygamma(n, z) returns log(gamma(z)).diff(n + 1) Reference: http://en.wikipedia.org/wiki/Polygamma_function """ nargs = 2 def fdiff(self, argindex=2): if argindex == 2: n, z = self.args[0:2] return polygamma(n+1, z) else: raise ArgumentIndexError(self, argindex) def _eval_is_positive(self): if self.args[1].is_positive and self.args[0] > 0: return self.args[0].is_odd def _eval_is_negative(self): if self.args[1].is_positive and self.args[0] > 0: return self.args[0].is_even def _eval_is_real(self): return self.args[0].is_real def _eval_aseries(self, n, args0, x, logx): if args0[1] != oo or not \ (self.args[0].is_Integer and self.args[0].is_nonnegative): return super(polygamma, self)._eval_aseries(n, args0, x, logx) z = self.args[1] N = self.args[0] if N == 0: # digamma function series # Abramowitz & Stegun, p. 259, 6.3.18 r = log(z) - 1/(2*z) o = None if n < 2: o = C.Order(1/z, x) else: m = C.ceiling((n+1)//2) l = [bernoulli(2*k) / (2*k*z**(2*k)) for k in range(1, m)] r -= Add(*l) o = C.Order(1/z**(2*m), x) return r._eval_nseries(x, n, logx) + o else: # proper polygamma function # Abramowitz & Stegun, p. 260, 6.4.10 # We return terms to order higher than O(x**n) on purpose # -- otherwise we would not be able to return any terms for # quite a long time! fac = gamma(N) e0 = fac + N*fac/(2*z) m = C.ceiling((n+1)//2) for k in range(1, m): fac = fac*(2*k+N-1)*(2*k+N-2) / ((2*k)*(2*k-1)) e0 += bernoulli(2*k)*fac/z**(2*k) o = C.Order(1/z**(2*m), x) if n == 0: o = C.Order(1/z, x) elif n == 1: o = C.Order(1/z**2, x) r = e0._eval_nseries(z, n, logx) + o return -1 * (-1/z)**N * r @classmethod def eval(cls, n, z): n, z = map(sympify, (n, z)) if n.is_integer: if n.is_negative: return loggamma(z) else: if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: if n.is_Number: if n is S.Zero: return S.Infinity else: return S.Zero elif z.is_Integer: if z.is_nonpositive: return S.ComplexInfinity else: if n is S.Zero: return -S.EulerGamma + C.harmonic(z-1, 1) elif n.is_odd: return (-1)**(n+1)*C.factorial(n)*zeta(n+1, z) def _eval_expand_func(self, deep=True, **hints): if deep: hints['func'] = False n = self.args[0].expand(deep, **hints) z = self.args[1].expand(deep, **hints) else: n, z = self.args[0], self.args[1].expand(deep, func=True) if n.is_Integer and n.is_nonnegative: if z.is_Add: coeff = z.args[0] if coeff.is_Integer: e = -(n + 1) if coeff > 0: tail = Add(*[C.Pow(z - i, e) for i in xrange(1, int(coeff) + 1)]) else: tail = -Add(*[C.Pow(z + i, e) for i in xrange(0, int(-coeff))]) return polygamma(n, z - coeff) + (-1)**n*C.factorial(n)*tail elif z.is_Mul: coeff, z = z.as_two_terms() if coeff.is_Integer and coeff.is_positive: tail = [ polygamma(n, z + C.Rational(i, coeff)) for i in xrange(0, int(coeff)) ] if n == 0: return Add(*tail)/coeff + log(coeff) else: return Add(*tail)/coeff**(n+1) return polygamma(n, z) def _eval_rewrite_as_zeta(self, n, z): return (-1)**(n+1)*C.factorial(n)*zeta(n+1, z-1) class loggamma(Function): nargs = 1 def _eval_aseries(self, n, args0, x, logx): if args0[0] != oo: return super(loggamma, self)._eval_aseries(n, args0, x, logx) z = self.args[0] m = min(n, C.ceiling((n+S(1))/2)) r = log(z)*(z-S(1)/2) - z + log(2*pi)/2 l = [bernoulli(2*k) / (2*k*(2*k-1)*z**(2*k-1)) for k in range(1, m)] o = None if m == 0: o = C.Order(1, x) else: o = C.Order(1/z**(2*m-1), x) # It is very inefficient to first add the order and then do the nseries return (r + Add(*l))._eval_nseries(x, n, logx) + o def _eval_rewrite_as_intractable(self, z): return log(gamma(z)) def _eval_is_real(self): return self.args[0].is_real def fdiff(self, argindex=1): if argindex == 1: return polygamma(0, self.args[0]) else: raise ArgumentIndexError(self, argindex) def digamma(x): return polygamma(0, x) def trigamma(x): return polygamma(1, x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/hyper.py0000644000175000017500000003621712014170666026241 0ustar georgeskgeorgesk"""Hypergeometric and Meijer G-functions""" from sympy import S from sympy.core.function import Function, ArgumentIndexError from sympy.core.containers import Tuple from sympy.core.sympify import sympify from sympy.core.mul import Mul # TODO should __new__ accept **options? # TODO should constructors should check if parameters are sensible? # TODO when pull request #399 is in, this should be no longer necessary def _make_tuple(v): """ Turn an iterable argument V into a Tuple. Examples: >>> from sympy.functions.special.hyper import _make_tuple as mt >>> from sympy.core.containers import Tuple >>> mt([1, 2, 3]) (1, 2, 3) >>> mt((4, 5)) (4, 5) >>> mt((7, 8, 9)) (7, 8, 9) """ return Tuple(*[sympify(x) for x in v]) class TupleParametersBase(Function): """ Base class that takes care of differentiation, when some of the arguments are actually tuples. """ def _eval_derivative(self, s): if self.args[0].has(s) or self.args[1].has(s): raise NotImplementedError('differentiation with respect to ' \ 'a parameter') return self.fdiff(3)*self.args[2].diff(s) class hyper(TupleParametersBase): r""" The (generalized) hypergeometric function is defined by a series where the ratios of successive terms are a rational function of the summation index. When convergent, it is continued analytically to the largest possible domain. The hypergeometric function depends on two vectors of parameters, called the numerator parameters :math:`a_p`, and the denominator parameters :math:`b_q`. It also has an argument :math:`z`. The series definition is .. math :: {}_pF_q\left.\left(\begin{matrix} a_1, \dots, a_p \\ b_1, \dots, b_q \end{matrix} \right| z \right) = \sum_{n=0}^\infty \frac{(a_1)_n \dots (a_p)_n}{(b_1)_n \dots (b_q)_n} \frac{z^n}{n!}, where :math:`(a)_n = (a)(a+1)\dots(a+n-1)` denotes the rising factorial. If one of the :math:`b_q` is a non-positive integer then the series is undefined unless one of the `a_p` is a larger (i.e. smaller in magnitude) non-positive integer. If none of the :math:`b_q` is a non-positive integer and one of the :math:`a_p` is a non-positive integer, then the series reduces to a polynomial. To simplify the following discussion, we assume that none of the :math:`a_p` or :math:`b_q` is a non-positive integer. For more details, see the references. The series converges for all :math:`z` if :math:`p \le q`, and thus defines an entire single-valued function in this case. If :math:`p = q+1` the series converges for :math:`|z| < 1`, and can be continued analytically into a half-plane. If :math:`p > q+1` the series is divergent for all :math:`z`. Note: The hypergeometric function constructor currently does *not* check if the parameters actually yield a well-defined function. **Examples** The parameters :math:`a_p` and :math:`b_q` can be passed as arbitrary iterables, for example: >>> from sympy.functions import hyper >>> from sympy.abc import x, n, a >>> hyper((1, 2, 3), [3, 4], x) hyper((1, 2, 3), (3, 4), x) There is also pretty printing (it looks better using unicode): >>> from sympy import pprint >>> pprint(hyper((1, 2, 3), [3, 4], x), use_unicode=False) _ |_ /1, 2, 3 | \ | | | x| 3 2 \ 3, 4 | / The parameters must always be iterables, even if they are vectors of length one or zero: >>> hyper((1, ), [], x) hyper((1,), (), x) But of course they may be variables (but if they depend on x then you should not expect much implemented functionality): >>> hyper((n, a), (n**2,), x) hyper((n, a), (n**2,), x) The hypergeometric function generalises many named special functions. The function hyperexpand() tries to express a hypergeometric function using named special functions. For example: >>> from sympy import hyperexpand >>> hyperexpand(hyper([], [], x)) exp(x) You can also use expand_func: >>> from sympy import expand_func >>> expand_func(x*hyper([1, 1], [2], -x)) log(x + 1) More examples: >>> from sympy import S >>> hyperexpand(hyper([], [S(1)/2], -x**2/4)) cos(x) >>> hyperexpand(x*hyper([S(1)/2, S(1)/2], [S(3)/2], x**2)) asin(x) We can also sometimes hyperexpand parametric functions: >>> from sympy.abc import a >>> hyperexpand(hyper([-a], [], x)) (-x + 1)**a See Also: - :func:`sympy.simplify.hyperexpand` **References** - Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 - http://en.wikipedia.org/wiki/Generalized_hypergeometric_function """ nargs = 3 def __new__(cls, ap, bq, z): # TODO should we check convergence conditions? return Function.__new__(cls, _make_tuple(ap), _make_tuple(bq), z) def fdiff(self, argindex=3): if argindex != 3: raise ArgumentIndexError(self, argindex) nap = Tuple(*[a + 1 for a in self.ap]) nbq = Tuple(*[b + 1 for b in self.bq]) fac = Mul(*self.ap)/Mul(*self.bq) return fac*hyper(nap, nbq, self.argument) def _eval_expand_func(self, deep=True, **hints): from sympy import gamma, hyperexpand if len(self.ap) == 2 and len(self.bq) == 1 and self.argument == 1: a, b = self.ap c = self.bq[0] return gamma(c)*gamma(c - a - b)/gamma(c - a)/gamma(c - b) return hyperexpand(self) @property def argument(self): """ Argument of the hypergeometric function. """ return self.args[2] @property def ap(self): """ Numerator parameters of the hypergeometric function. """ return self.args[0] @property def bq(self): """ Denominator parameters of the hypergeometric function. """ return self.args[1] @property def eta(self): """ A quantity related to the convergence of the series. """ return sum(self.ap) - sum(self.bq) @property def radius_of_convergence(self): """ Compute the radius of convergence of the defining series. Note that even if this is not oo, the function may still be evaluated outside of the radius of convergence by analytic continuation. But if this is zero, then the function is not actually defined anywhere else. >>> from sympy.functions import hyper >>> from sympy.abc import z >>> hyper((1, 2), [3], z).radius_of_convergence 1 >>> hyper((1, 2, 3), [4], z).radius_of_convergence 0 >>> hyper((1, 2), (3, 4), z).radius_of_convergence oo """ from sympy import oo if any(a.is_integer and a <= 0 for a in self.ap + self.bq): aints = [a for a in self.ap if a.is_Integer and a <= 0] bints = [a for a in self.bq if a.is_Integer and a <= 0] if len(aints) < len(bints): return S(0) popped = False for b in bints: cancelled = False while aints: a = aints.pop() if a >= b: cancelled = True break popped = True if not cancelled: return S(0) if aints or popped: # There are still non-positive numerator parameters. # This is a polynomial. return oo if len(self.ap) == len(self.bq) + 1: return S(1) elif len(self.ap) <= len(self.bq): return oo else: return S(0) @property def convergence_statement(self): """ Return a condition on z under which the series converges. """ from sympy import And, Or, re, Ne, oo R = self.radius_of_convergence if R == 0: return False if R == oo: return True # The special functions and their approximations, page 44 e = self.eta z = self.argument c1 = And(re(e) < 0, abs(z) <= 1) c2 = And(0 <= re(e), re(e) < 1, abs(z) <= 1, Ne(z, 1)) c3 = And(re(e) >= 1, abs(z) < 1) return Or(c1, c2, c3) class meijerg(TupleParametersBase): r""" The Meijer G-function is defined by a Mellin-Barnes type integral that resembles an inverse Mellin transform. It generalises the hypergeometric functions. The Meijer G-function depends on four sets of parameters. There are "*numerator parameters*" :math:`a_1, \dots, a_n` and :math:`a_{n+1}, \dots, a_p`, and there are "*denominator parameters*" :math:`b_1, \dots, b_m` and :math:`b_{m+1}, \dots, b_q`. Confusingly, it is traditionally denoted as follows (note the position of `m`, `n`, `p`, `q`, and how they relate to the lengths of the four parameter vectors): .. math :: G_{p,q}^{m,n} \left.\left(\begin{matrix}a_1, \dots, a_n & a_{n+1}, \dots, a_p \\ b_1, \dots, b_m & b_{m+1}, \dots, b_q \end{matrix} \right| z \right). However, in sympy the four parameter vectors are always available separately (see examples), so that there is no need to keep track of the decorating sub- and super-scripts on the G symbol. The G function is defined as the following integral: .. math :: \frac{1}{2 \pi i} \int_L \frac{\prod_{j=1}^m \Gamma(b_j - s) \prod_{j=1}^n \Gamma(1 - a_j + s)}{\prod_{j=m+1}^q \Gamma(1- b_j +s) \prod_{j=n+1}^p \Gamma(a_j - s)} z^s \mathrm{d}s, where :math:`\Gamma(z)` is the gamma function. There are three possible contours which we will not describe in detail here (see the references). If the integral converges along more than one of them the definitions agree. The contours all separate the poles of :math:`\Gamma(1-a_j+s)` from the poles of :math:`\Gamma(b_k-s)`, so in particular the G function is undefined if :math:`a_j - b_k \in \mathbb{Z}_{>0}` for some :math:`j \le n` and :math:`k \le m`. The conditions under which one of the contours yields a convergent integral are complicated and we do not state them here, see the references. Note: Currently the Meijer G-function constructor does *not* check any convergence conditions. **Examples** You can pass the parameters either as four separate vectors: >>> from sympy.functions import meijerg >>> from sympy.abc import x, a >>> from sympy.core.containers import Tuple >>> from sympy import pprint >>> pprint(meijerg((1, 2), (a, 4), (5,), [], x), use_unicode=False) __1, 2 /1, 2 a, 4 | \ /__ | | x| \_|4, 1 \ 5 | / or as two nested vectors: >>> pprint(meijerg([(1, 2), (3, 4)], ([5], Tuple()), x), use_unicode=False) __1, 2 /1, 2 3, 4 | \ /__ | | x| \_|4, 1 \ 5 | / As with the hypergeometric function, the parameters may be passed as arbitrary iterables. Vectors of length zero and one also have to be passed as iterables. The parameters need not be constants, but if they depend on the argument then not much implemented functionality should be expected. All the subvectors of parameters are available: >>> from sympy import pprint >>> g = meijerg([1], [2], [3], [4], x) >>> pprint(g, use_unicode=False) __1, 1 /1 2 | \ /__ | | x| \_|2, 2 \3 4 | / >>> g.an (1,) >>> g.ap (1, 2) >>> g.aother (2,) >>> g.bm (3,) >>> g.bq (3, 4) >>> g.bother (4,) The Meijer G-function generalises the hypergeometric functions. In some cases it can be expressed in terms of hypergeometric functions, using Slater's theorem. For example: >>> from sympy import hyperexpand >>> from sympy.abc import a, b, c >>> hyperexpand(meijerg([a], [], [c], [b], x), allow_hyper=True) x**c*gamma(-a + c + 1)*hyper((-a + c + 1,), (-b + c + 1,), -x)/gamma(-b + c + 1) Thus the Meijer G-function also subsumes many named functions as special cases. You can use expand_func or hyperexpand to (try to) rewrite a Meijer G-function in terms of named special functions. For example: >>> from sympy import expand_func, S >>> expand_func(meijerg([[],[]], [[0],[]], -x)) exp(x) >>> hyperexpand(meijerg([[],[]], [[S(1)/2],[0]], (x/2)**2)) sin(x)/pi**(1/2) See Also: - :func:`sympy.simplify.hyperexpand` **References** - Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 - http://en.wikipedia.org/wiki/Meijer_G-function """ nargs = 3 def __new__(cls, *args): if len(args) == 5: args = [(args[0], args[1]), (args[2], args[3]), args[4]] if len(args) != 3: raise TypeError("args must eiter be as, as', bs, bs', z or " \ "as, bs, z") def tr(p): if len(p) != 2: raise TypeError("wrong argument") return Tuple(_make_tuple(p[0]), _make_tuple(p[1])) # TODO should we check convergence conditions? return Function.__new__(cls, tr(args[0]), tr(args[1]), args[2]) def fdiff(self, argindex=3): if argindex != 3: raise ArgumentIndexError(self, argindex) if len(self.an) >= 1: a = list(self.an) a[0] -= 1 G = meijerg(a, self.aother, self.bm, self.bother, self.argument) return 1/self.argument * ((self.an[0]-1)*self + G) elif len(self.bm) >= 1: b = list(self.bm) b[0] += 1 G = meijerg(self.an, self.aother, b, self.bother, self.argument) return 1/self.argument * (self.bm[0]*self - G) else: return S.Zero def _eval_expand_func(self, deep=True, **hints): from sympy import hyperexpand return hyperexpand(self) @property def argument(self): """ Argument of the Meijer G-function. """ return self.args[2] @property def an(self): """ First set of numerator parameters. """ return self.args[0][0] @property def ap(self): """ Combined numerator parameters. """ return self.args[0][0] + self.args[0][1] @property def aother(self): """ Second set of numerator parameters. """ return self.args[0][1] @property def bm(self): """ First set of denominator parameters. """ return self.args[1][0] @property def bq(self): """ Combined denominator parameters. """ return self.args[1][0] + self.args[1][1] @property def bother(self): """ Second set of denominator parameters. """ return self.args[1][1] @property def nu(self): """ A quantity related to the convergence region of the integral, c.f. references. """ return sum(self.bq) - sum(self.ap) @property def delta(self): """ A quantity related to the convergence region of the integral, c.f. references. """ return len(self.bm) + len(self.an) - S(len(self.ap) + len(self.bq))/2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/zeta_functions.py0000644000175000017500000000331412014170666030135 0ustar georgeskgeorgeskfrom sympy.core import Function, S, C, sympify, pi ############################################################################### ###################### HURWITZ GENERALIZED ZETA FUNCTION ###################### ############################################################################### class zeta(Function): nargs = (1, 2) @classmethod def eval(cls, z, a=S.One): z, a = map(sympify, (z, a)) if a.is_Number: if a is S.NaN: return S.NaN elif a is S.Zero: return cls(z) if z.is_Number: if z is S.NaN: return S.NaN elif z is S.Infinity: return S.One elif z is S.Zero: if a.is_negative: return S.Half - a - 1 else: return S.Half - a elif z is S.One: return S.ComplexInfinity elif z.is_Integer: if a.is_Integer: if z.is_negative: zeta = (-1)**z * C.bernoulli(-z+1)/(-z+1) elif z.is_even: B, F = C.bernoulli(z), C.factorial(z) zeta = 2**(z-1) * abs(B) * pi**z / F else: return if a.is_negative: return zeta + C.harmonic(abs(a), z) else: return zeta - C.harmonic(a-1, z) class dirichlet_eta(Function): """ Dirichlet eta function """ nargs = 1 @classmethod def eval(cls, s): if s == 1: return C.log(2) else: return (1-2**(1-s)) * zeta(s) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/spherical_harmonics.py0000644000175000017500000000321512014170666031117 0ustar georgeskgeorgeskfrom sympy import C, pi, I from sympy.core import Dummy, sympify from sympy.functions import legendre, assoc_legendre from sympy.functions.elementary.miscellaneous import sqrt Pl = legendre Plm= assoc_legendre _x = Dummy("x") def Plmcos(l, m, th): l = sympify(l) m = sympify(m) sin = C.sin cos = C.cos P = Plm(l, m, _x).subs(_x, cos(th)) # assume th in (0,pi) => sin(th) is nonegative _sinth = Dummy("_sinth", nonnegative=True) P = P.subs(1-cos(th)**2, _sinth**2).subs(_sinth, sin(th)) return P def Ylm(l, m, theta, phi): """ Spherical harmonics Ylm. Examples: >>> from sympy import symbols, Ylm >>> theta, phi = symbols("theta phi") >>> Ylm(0, 0, theta, phi) 1/(2*pi**(1/2)) >>> Ylm(1, -1, theta, phi) 6**(1/2)*exp(-I*phi)*sin(theta)/(4*pi**(1/2)) >>> Ylm(1, 0, theta, phi) 3**(1/2)*cos(theta)/(2*pi**(1/2)) """ l, m, theta, phi = [sympify(x) for x in (l, m, theta, phi)] factorial = C.factorial return sqrt((2*l+1)/(4*pi) * factorial(l-m)/factorial(l+m)) * \ Plmcos(l, m, theta) * C.exp(I*m*phi) def Ylm_c(l, m, theta, phi): """Conjugate spherical harmonics.""" return (-1)**m * Ylm(l, -m, theta, phi) def Zlm(l, m, th, ph): """ Real spherical harmonics. """ from sympy import simplify if m > 0: zz = C.NegativeOne()**m*(Ylm(l, m, th, ph) + Ylm_c(l, m, th, ph))/sqrt(2) elif m == 0: return Ylm(l, m, th, ph) else: zz = C.NegativeOne()**m*(Ylm(l, -m, th, ph) - Ylm_c(l, -m, th, ph))/(I*sqrt(2)) zz = zz.expand(complex=True) zz = simplify(zz) return zz wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/bessel.py0000644000175000017500000003141712014170666026364 0ustar georgeskgeorgesk"""Bessel type functions""" from sympy import S, pi from sympy.core import sympify from sympy.core.function import Function, ArgumentIndexError from sympy.functions.elementary.trigonometric import sin, cos from sympy.functions.elementary.miscellaneous import sqrt # TODO # o Airy Ai and Bi functions # o Scorer functions G1 and G2 # o Asymptotic expansions # These are possible, e.g. for fixed order, but since the bessel type # functions are oscillatory they are not actually tractable at # infinity, so this is not particularly useful right now. # o Series Expansions for functions of the second kind about zero # o Nicer series expansions. # o More rewriting. # o Add solvers to ode.py (or rather add solvers for the hypergeometric equation). class BesselBase(Function): """ Abstract base class for bessel-type functions. This class is meant to reduce code duplication. All bessel type functions can 1) be differentiated, and the derivatives expressed in terms of similar functions and 2) be rewritten in terms of other bessel-type functions. Here "bessel-type functions" are assumed to have one complex parameter. To use this base class, define class attributes _a and _b such that 2F_n' = -_a*F_{n+1} b*F_{n-1}. """ nargs = 2 @property def order(self): """ The order of the bessel-type function. """ return self.args[0] @property def argument(self): """ The argument of the bessel-type function. """ return self.args[1] def fdiff(self, argindex=2): if argindex != 2: raise ArgumentIndexError(self, argindex) return self._b/2 * self.__class__(self.order - 1, self.argument) \ - self._a/2 * self.__class__(self.order + 1, self.argument) \ class besselj(BesselBase): r""" Bessel function of the first kind. The Bessel J function of order :math:`\nu` is defined to be the function satisfying Bessel's differential equation .. math :: z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2} + z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 - \nu^2) w = 0, with Laurent expansion .. math :: J_\nu(z) = z^\nu \left(\frac{1}{\Gamma(\nu + 1) 2^\nu} + O(z^2) \right), if :math:`\nu` is not a negative integer. If :math:`\nu=-n \in \mathbb{Z}_{<0}` *is* a negative integer, then the definition is .. math :: J_{-n}(z) = (-1)^n J_n(z). **Examples** Create a bessel function object: >>> from sympy import besselj, jn >>> from sympy.abc import z, n >>> b = besselj(n, z) Differentiate it: >>> b.diff(z) besselj(n - 1, z)/2 - besselj(n + 1, z)/2 Rewrite in terms of spherical bessel functions: >>> b.rewrite(jn) 2**(1/2)*z**(1/2)*jn(n - 1/2, z)/pi**(1/2) Access the parameter and argument: >>> b.order n >>> b.argument z **References** - Abramowitz, Milton; Stegun, Irene A., eds. (1965), "Chapter 9", Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables - Luke, Y. L. (1969), The Special Functions and Their Approximations, Volume 1 - http://en.wikipedia.org/wiki/Bessel_function """ _a = S.One _b = S.One def _eval_rewrite_as_jn(self, nu, z): return sqrt(2*z/pi) * jn(nu - S('1/2'), self.argument) class bessely(BesselBase): r""" Bessel function of the second kind. The Bessel Y function of order :math:`\nu` is defined as .. math :: Y_\nu(z) = \lim_{\mu \to \nu} \frac{J_\mu(z) \cos(\pi \mu) - J_{-\mu}(z)}{\sin(\pi \mu)}, where :math:`J_\mu(z)` is the Bessel function of the first kind. It is a solution to Bessel's equation, and linearly independent from :math:`J_\nu`. **Examples** >>> from sympy import bessely, yn >>> from sympy.abc import z, n >>> b = bessely(n, z) >>> b.diff(z) bessely(n - 1, z)/2 - bessely(n + 1, z)/2 >>> b.rewrite(yn) 2**(1/2)*z**(1/2)*yn(n - 1/2, z)/pi**(1/2) **See also:** :class:`besselj` """ _a = S.One _b = S.One def _eval_rewrite_as_yn(self, nu, z): return sqrt(2*z/pi) * yn(nu - S('1/2'), self.argument) class besseli(BesselBase): r""" Modified Bessel function of the first kind. The Bessel I function is a solution to the modified Bessel equation .. math :: z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2} + z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 + \nu^2)^2 w = 0. It can be defined as .. math :: I_\nu(z) = i^{-\nu} J_\nu(iz), where :math:`J_\mu(z)` is the Bessel function of the first kind. **Examples** >>> from sympy import besseli >>> from sympy.abc import z, n >>> besseli(n, z).diff(z) besseli(n - 1, z)/2 + besseli(n + 1, z)/2 **See also:** :class:`besselj` """ _a = -S.One _b = S.One class besselk(BesselBase): r""" Modified Bessel function of the second kind. The Bessel K function of order :math:`\nu` is defined as .. math :: K_\nu(z) = \lim_{\mu \to \nu} \frac{\pi}{2} \frac{I_{-\mu}(z) -I_\mu(z)}{\sin(\pi \mu)}, where :math:`I_\mu(z)` is the modified Bessel function of the first kind. It is a solution of the modified Bessel equation, and linearly independent from :math:`Y_\nu`. **Examples** >>> from sympy import besselk >>> from sympy.abc import z, n >>> besselk(n, z).diff(z) -besselk(n - 1, z)/2 - besselk(n + 1, z)/2 **See also:** :class:`besselj` """ _a = S.One _b = -S.One class hankel1(BesselBase): r""" Hankel function of the first kind. This function is defined as .. math :: H_\nu^{(1)} = J_\nu(z) + iY_\nu(z), where :math:`J_\nu(z)` is the Bessel function of the first kind, and :math:`Y_\nu(z)` is the Bessel function of the second kind. It is a solution to Bessel's equation. **Examples** >>> from sympy import hankel1 >>> from sympy.abc import z, n >>> hankel1(n, z).diff(z) hankel1(n - 1, z)/2 - hankel1(n + 1, z)/2 **See also:** :class:`besselj` """ _a = S.One _b = S.One class hankel2(BesselBase): r""" Hankel function of the second kind. This function is defined as .. math :: H_\nu^{(2)} = J_\nu(z) - iY_\nu(z), where :math:`J_\nu(z)` is the Bessel function of the first kind, and :math:`Y_\nu(z)` is the Bessel function of the second kind. It is a solution to Bessel's equation, and linearly independent from :math:`H_\nu^{(1)}`. **Examples** >>> from sympy import hankel2 >>> from sympy.abc import z, n >>> hankel2(n, z).diff(z) hankel2(n - 1, z)/2 - hankel2(n + 1, z)/2 **See also:** :class:`besselj` """ _a = S.One _b = S.One from sympy.polys.orthopolys import spherical_bessel_fn as fn class SphericalBesselBase(BesselBase): """ Base class for spherical bessel functions. These are thin wrappers around ordinary bessel functions, since spherical bessel functions differ from the ordinary ones just by a slight change in order. To use this class, define the _rewrite and _expand methods. """ def _expand(self): """ Expand self into a polynomial. Nu is guaranteed to be Integer. """ raise NotImplementedError('expansion') def _rewrite(self): """ Rewrite self in terms of ordinary bessel functions. """ raise NotImplementedError('rewriting') def _eval_expand_func(self, deep=False, **hints): if self.order.is_Integer: return self._expand() def _eval_evalf(self, prec): return self._rewrite()._eval_evalf(prec) def fdiff(self, argindex=2): if argindex != 2: raise ArgumentIndexError(self, argindex) return self.__class__(self.order - 1, self.argument) - \ self * (self.order + 1)/self.argument class jn(SphericalBesselBase): r""" Spherical Bessel function of the first kind. This function is a solution to the spherical bessel equation .. math :: z^2 \frac{\mathrm{d}^2 w}{\mathrm{d}z^2} + 2z \frac{\mathrm{d}w}{\mathrm{d}z} + (z^2 - \nu(\nu + 1)) w = 0. It can be defined as .. math :: j_\nu(z) = \sqrt{\frac{\pi}{2z}} J_{\nu + \frac{1}{2}}(z), where :math:`J_\nu(z)` is the Bessel function of the first kind. **Examples** >>> from sympy import Symbol, jn, sin, cos, expand_func >>> z = Symbol("z") >>> print jn(0, z).expand(func=True) sin(z)/z >>> jn(1, z).expand(func=True) == sin(z)/z**2 - cos(z)/z True >>> expand_func(jn(3, z)) (-6/z**2 + 15/z**4)*sin(z) + (1/z - 15/z**3)*cos(z) The spherical Bessel functions of integral order are calculated using the formula: .. math:: j_n(z) = f_n(z) \sin{z} + (-1)^{n+1} f_{-n-1}(z) \cos{z}, where the coefficients :math:`f_n(z)` are available as :func:`polys.orthopolys.spherical_bessel_fn`. **See also:** :class:`besselj` """ def _rewrite(self): return self._eval_rewrite_as_besselj(self.order, self.argument) def _eval_rewrite_as_besselj(self, nu, z): return sqrt(pi/(2*z)) * besselj(nu + S('1/2'), z) def _expand(self): n = self.order z = self.argument return fn(n, z) * sin(z) + (-1)**(n+1) * fn(-n-1, z) * cos(z) class yn(SphericalBesselBase): r""" Spherical Bessel function of the second kind. This function is another solution to the spherical bessel equation, and linearly independent from :math:`j_n`. It can be defined as .. math :: j_\nu(z) = \sqrt{\frac{\pi}{2z}} Y_{\nu + \frac{1}{2}}(z), where :math:`Y_\nu(z)` is the Bessel function of the second kind. **Examples** >>> from sympy import Symbol, yn, sin, cos, expand_func >>> z = Symbol("z") >>> print expand_func(yn(0, z)) -cos(z)/z >>> expand_func(yn(1, z)) == -cos(z)/z**2-sin(z)/z True For integral orders :math:`n`, :math:`y_n` is calculated using the formula: .. math:: y_n(z) = (-1)^{n+1} j_{-n-1}(z) **See also:** :class:`besselj`, :class:`bessely`, :class:`jn` """ def _rewrite(self): return self._eval_rewrite_as_bessely(self.order, self.argument) def _eval_rewrite_as_bessely(self, nu, z): return sqrt(pi/(2*z)) * bessely(nu + S('1/2'), z) def _expand(self): n = self.order z = self.argument return (-1)**(n+1) * \ (fn(-n-1, z) * sin(z) + (-1)**(-n) * fn(n, z) * cos(z)) def jn_zeros(n, k, method="sympy"): """ Zeros of the spherical Bessel function of the first kind. This returns an array of zeros of jn up to the k-th zero. method = "sympy": uses the SymPy's jn and findroot to find all roots method = "scipy": uses the SciPy's sph_jn and newton to find all roots, which if faster than method="sympy", but it requires SciPy and only works with low precision floating point numbers **Examples** >>> from sympy.mpmath import nprint >>> from sympy import jn_zeros >>> nprint(jn_zeros(2, 4)) [5.76345919689, 9.09501133048, 12.3229409706, 15.5146030109] """ from math import pi if method == "sympy": from sympy.mpmath import findroot f = lambda x: jn(n, x).n() elif method == "scipy": from scipy.special import sph_jn from scipy.optimize import newton f = lambda x: sph_jn(n, x)[0][-1] elif method == 'mpmath': # this needs a recent version of mpmath, newer than in sympy from mpmath import besseljzero return [besseljzero(n + 0.5, k) for k in xrange(1, k + 1)] else: raise NotImplementedError("Unknown method.") def solver(f, x): if method == "sympy": # findroot(solver="newton") or findroot(solver="secant") can't find # the root within the given tolerance. So we use solver="muller", # which converges towards complex roots (even for real starting # points), and so we need to chop all complex parts (that are small # anyway). Also we need to set the tolerance, as it sometimes fail # without it. def f_real(x): return f(complex(x).real) root = findroot(f_real, x, solver="muller", tol=1e-9) root = complex(root).real elif method == "scipy": root = newton(f, x) else: raise NotImplementedError("Unknown method.") return root # we need to approximate the position of the first root: root = n+pi # determine the first root exactly: root = solver(f, root) roots = [root] for i in range(k-1): # estimate the position of the next root using the last root + pi: root = solver(f, root+pi) roots.append(root) return roots wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/polynomials.py0000644000175000017500000001657012014170666027460 0ustar georgeskgeorgesk""" This module mainly implements special orthogonal polynomials. See also functions.combinatorial.numbers which contains some combinatorial polynomials. """ from sympy.core.basic import C from sympy.core.singleton import S from sympy.core import Rational from sympy.core.function import Function from sympy.polys.orthopolys import ( chebyshevt_poly, chebyshevu_poly, laguerre_poly, hermite_poly, legendre_poly, ) _x = C.Dummy('x') class PolynomialSequence(Function): """Polynomial sequence with one index and n >= 0. """ nargs = 2 @classmethod def eval(cls, n, x): if n.is_integer and n >= 0: return cls._ortho_poly(int(n), _x).subs(_x, x) if n.is_negative: raise ValueError("%s index must be nonnegative integer (got %r)" % (cls, n)) #---------------------------------------------------------------------------- # Chebyshev polynomials of first and second kind # class chebyshevt(PolynomialSequence): """ chebyshevt(n, x) gives the nth Chebyshev polynomial (of the first kind) of x, T_n(x) The Chebyshev polynomials of the first kind are orthogonal on [-1, 1] with respect to the weight 1/sqrt(1-x**2). Examples ======== >>> from sympy import chebyshevt >>> from sympy.abc import x >>> chebyshevt(0, x) 1 >>> chebyshevt(1, x) x >>> chebyshevt(2, x) 2*x**2 - 1 References ========== * http://en.wikipedia.org/wiki/Chebyshev_polynomial """ """ Chebyshev polynomial of the first kind, T_n(x) """ _ortho_poly = staticmethod(chebyshevt_poly) class chebyshevu(PolynomialSequence): """ chebyshevu(n, x) gives the nth Chebyshev polynomial of the second kind of x, U_n(x) The Chebyshev polynomials of the second kind are orthogonal on [-1, 1] with respect to the weight sqrt(1-x**2). Examples ======== >>> from sympy import chebyshevu >>> from sympy.abc import x >>> chebyshevu(0, x) 1 >>> chebyshevu(1, x) 2*x >>> chebyshevu(2, x) 4*x**2 - 1 """ _ortho_poly = staticmethod(chebyshevu_poly) class chebyshevt_root(Function): """ chebyshev_root(n, k) returns the kth root (indexed from zero) of the nth Chebyshev polynomial of the first kind; that is, if 0 <= k < n, chebyshevt(n, chebyshevt_root(n, k)) == 0. Examples ======== >>> from sympy import chebyshevt, chebyshevt_root >>> chebyshevt_root(3, 2) -3**(1/2)/2 >>> chebyshevt(3, chebyshevt_root(3, 2)) 0 """ nargs = 2 @classmethod def eval(cls, n, k): if not 0 <= k < n: raise ValueError("must have 0 <= k < n") return C.cos(S.Pi*(2*k+1)/(2*n)) class chebyshevu_root(Function): """ chebyshevu_root(n, k) returns the kth root (indexed from zero) of the nth Chebyshev polynomial of the second kind; that is, if 0 <= k < n, chebyshevu(n, chebyshevu_root(n, k)) == 0. Examples ======== >>> from sympy import chebyshevu, chebyshevu_root >>> chebyshevu_root(3, 2) -2**(1/2)/2 >>> chebyshevu(3, chebyshevu_root(3, 2)) 0 """ nargs = 2 @classmethod def eval(cls, n, k): if not 0 <= k < n: raise ValueError("must have 0 <= k < n") return C.cos(S.Pi*(k+1)/(n+1)) #---------------------------------------------------------------------------- # Legendre polynomials and Associated Legendre polynomials # class legendre(PolynomialSequence): """ legendre(n, x) gives the nth Legendre polynomial of x, P_n(x) The Legendre polynomials are orthogonal on [-1, 1] with respect to the constant weight 1. They satisfy P_n(1) = 1 for all n; further, P_n is odd for odd n and even for even n Examples ======== >>> from sympy import legendre >>> from sympy.abc import x >>> legendre(0, x) 1 >>> legendre(1, x) x >>> legendre(2, x) 3*x**2/2 - 1/2 References ========== * http://en.wikipedia.org/wiki/Legendre_polynomial """ _ortho_poly = staticmethod(legendre_poly) class assoc_legendre(Function): """ assoc_legendre(n,m, x) gives P_nm(x), where n and m are the degree and order or an expression which is related to the nth order Legendre polynomial, P_n(x) in the following manner: P_nm(x) = (-1)**m * (1 - x**2)**(m/2) * diff(P_n(x), x, m) Associated Legendre polynomial are orthogonal on [-1, 1] with: - weight = 1 for the same m, and different n. - weight = 1/(1-x**2) for the same n, and different m. Examples ======== >>> from sympy import assoc_legendre >>> from sympy.abc import x >>> assoc_legendre(0,0, x) 1 >>> assoc_legendre(1,0, x) x >>> assoc_legendre(1,1, x) -(-x**2 + 1)**(1/2) References ========== * http://en.wikipedia.org/wiki/Associated_Legendre_polynomials """ nargs = 3 @classmethod def calc(cls, n, m): P = legendre_poly(n, _x, polys=True).diff((_x, m)) return (-1)**m * (1 - _x**2)**Rational(m, 2) * P.as_expr() @classmethod def eval(cls, n, m, x): if n.is_integer and n >= 0 and m.is_integer and abs(m) <= n: assoc = cls.calc(int(n), abs(int(m))) if m < 0: assoc *= (-1)**(-m) * (C.factorial(n + m)/C.factorial(n - m)) return assoc.subs(_x, x) if n.is_negative: raise ValueError("%s : 1st index must be nonnegative integer (got %r)" % (cls, n)) if abs(m) > n: raise ValueError("%s : abs('2nd index') must be <= '1st index' (got %r, %r)" % (cls, n, m)) #---------------------------------------------------------------------------- # Hermite polynomials # class hermite(PolynomialSequence): """ hermite(n, x) gives the nth Hermite polynomial in x, H_n(x) The Hermite polynomials are orthogonal on (-oo, oo) with respect to the weight exp(-x**2/2). Examples ======== >>> from sympy import hermite >>> from sympy.abc import x >>> hermite(0, x) 1 >>> hermite(1, x) 2*x >>> hermite(2, x) 4*x**2 - 2 References ========== * http://mathworld.wolfram.com/HermitePolynomial.html """ _ortho_poly = staticmethod(hermite_poly) def laguerre_l(n, alpha, x): """ Returns the generalized Laguerre polynomial. ``n`` : ``int`` Degree of Laguerre polynomial. Must be ``n >= 0``. ``alpha`` : ``Expr`` Arbitrary expression. For ``alpha=0`` regular Laguerre polynomials will be generated. **Examples** To construct generalized Laguerre polynomials issue:: >>> from sympy import laguerre_l, var >>> var("alpha, x") (alpha, x) >>> laguerre_l(0, alpha, x) 1 >>> laguerre_l(1, alpha, x) alpha - x + 1 >>> laguerre_l(2, alpha, x) alpha**2/2 + 3*alpha/2 + x**2/2 + x*(-alpha - 2) + 1 If you set ``alpha=0``, you get regular Laguerre polynomials:: >>> laguerre_l(1, 0, x) -x + 1 >>> laguerre_l(2, 0, x) x**2/2 - 2*x + 1 >>> laguerre_l(3, 0, x) -x**3/6 + 3*x**2/2 - 3*x + 1 >>> laguerre_l(4, 0, x) x**4/24 - 2*x**3/3 + 3*x**2 - 4*x + 1 """ return laguerre_poly(n, x, alpha) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/error_functions.py0000644000175000017500000000340512014170666030324 0ustar georgeskgeorgeskfrom sympy.core import S, C, sympify, cacheit from sympy.core.function import Function, ArgumentIndexError from sympy.functions.elementary.miscellaneous import sqrt ############################################################################### ################################ ERROR FUNCTION ############################### ############################################################################### class erf(Function): nargs = 1 def fdiff(self, argindex=1): if argindex == 1: return 2*C.exp(-self.args[0]**2)/sqrt(S.Pi) else: raise ArgumentIndexError(self, argindex) @classmethod def eval(cls, arg): if arg.is_Number: if arg is S.NaN: return S.NaN elif arg is S.Infinity: return S.One elif arg is S.NegativeInfinity: return S.NegativeOne elif arg is S.Zero: return S.Zero elif arg.is_negative: return -cls(-arg) elif arg.is_Mul: if arg.as_coeff_mul()[0].is_negative: return -cls(-arg) @staticmethod @cacheit def taylor_term(n, x, *previous_terms): if n < 0 or n % 2 == 0: return S.Zero else: x = sympify(x) k = (n - 1)//2 if len(previous_terms) > 2: return -previous_terms[-2] * x**2 * (n-2)/(n*k) else: return 2*(-1)**k * x**n/(n*C.factorial(k)*sqrt(S.Pi)) def _eval_as_leading_term(self, x): arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) def _eval_is_real(self): return self.args[0].is_real wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/0000755000175000017500000000000012014170666025671 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_hyper.py0000644000175000017500000001156312014170666030437 0ustar georgeskgeorgeskfrom sympy import (hyper, meijerg, S, Tuple, pi, I, exp, log, cos, sqrt, symbols, oo) from sympy.abc import x, z, k from sympy.utilities.pytest import raises, XFAIL from sympy.utilities.randtest import ( random_complex_number as randcplx, test_numerically as tn, test_derivative_numerically as td) def test_TupleParametersBase(): # test that our implementation of the chain rule works p = hyper((), (), z**2) assert p.diff(z) == p*2*z def test_hyper(): raises(TypeError, 'hyper(1, 2, z)') assert hyper((1, 2),(1,), z) == hyper(Tuple(1, 2), Tuple(1), z) h = hyper((1, 2), (3, 4, 5), z) assert h.ap == Tuple(1, 2) assert h.bq == Tuple(3, 4, 5) assert h.argument == z # just a few checks to make sure that all arguments go where they should assert tn(hyper(Tuple(), Tuple(), z), exp(z), z) assert tn(z*hyper((1, 1), Tuple(2), -z), log(1 + z), z) # differentiation h = hyper((randcplx(), randcplx(), randcplx()), (randcplx(), randcplx()), z) assert td(h, z) a1, a2, b1, b2, b3 = symbols('a1:3, b1:4') assert hyper((a1, a2), (b1, b2, b3), z).diff(z) == \ a1*a2/(b1*b2*b3) * hyper((a1+1, a2+1), (b1+1, b2+1, b3+1), z) # differentiation wrt parameters is not supported raises(NotImplementedError, 'hyper((z,), (), z).diff(z)') def test_expand_func(): # evaluation at 1 of Gauss' hypergeometric function: from sympy.abc import a, b, c from sympy import gamma, expand_func a1, b1, c1 = randcplx(), randcplx(), randcplx() + 5 assert expand_func(hyper([a, b], [c], 1)) == \ gamma(c)*gamma(-a - b + c)/(gamma(-a + c)*gamma(-b + c)) assert abs(expand_func(hyper([a1, b1], [c1], 1)).n() - hyper([a1, b1], [c1], 1).n()) < 1e-10 # hyperexpand wrapper for hyper: assert expand_func(hyper([], [], z)) == exp(z) assert expand_func(hyper([1, 2, 3], [], z)) == hyper([1, 2, 3], [], z) assert expand_func(meijerg([[1,1],[]], [[1],[0]], z)) == log(z + 1) assert expand_func(meijerg([[1,1],[]], [[],[]], z)) \ == meijerg([[1,1],[]], [[],[]], z) def test_radius_of_convergence(): assert hyper((1, 2), [3], z).radius_of_convergence == 1 assert hyper((1, 2), [3, 4], z).radius_of_convergence == oo assert hyper((1, 2, 3), [4], z).radius_of_convergence == 0 assert hyper((0, 1, 2), [4], z).radius_of_convergence == oo assert hyper((-1, 1, 2), [-4], z).radius_of_convergence == 0 assert hyper((-1, -2, 2), [-1], z).radius_of_convergence == oo assert hyper((-1, 2), [-1, -2], z).radius_of_convergence == 0 assert hyper([-1, 1, 3], [-2, 2], z).radius_of_convergence == 1 assert hyper([-1, 1], [-2, 2], z).radius_of_convergence == oo assert hyper([-1, 1, 3], [-2], z).radius_of_convergence == 0 assert hyper((-1, 2, 3, 4), [], z).radius_of_convergence == oo assert hyper([1, 1], [3], 1).convergence_statement is True assert hyper([1, 1], [2], 1).convergence_statement is False assert hyper([1, 1], [2], -1).convergence_statement is True assert hyper([1, 1], [1], -1).convergence_statement is False def test_meijer(): raises(TypeError, 'meijerg(1, z)') raises(TypeError, 'meijerg(((1,), (2,)), (3,), (4,), z)') assert meijerg(((1, 2), (3,)), ((4,), (5,)), z) == \ meijerg(Tuple(1, 2), Tuple(3), Tuple(4), Tuple(5), z) g = meijerg((1, 2), (3, 4, 5), (6, 7, 8, 9), (10, 11, 12, 13, 14), z) assert g.an == Tuple(1, 2) assert g.ap == Tuple(1, 2, 3, 4, 5) assert g.aother == Tuple(3, 4, 5) assert g.bm == Tuple(6, 7, 8, 9) assert g.bq == Tuple(6, 7, 8, 9, 10, 11, 12, 13, 14) assert g.bother == Tuple(10, 11, 12, 13, 14) assert g.argument == z assert g.nu == 75 assert g.delta == -1 assert meijerg([1, 2], [3], [4], [5], z).delta == S(1)/2 # just a few checks to make sure that all arguments go where they should assert tn(meijerg(Tuple(), Tuple(), Tuple(0), Tuple(), -z), exp(z), z) assert tn(sqrt(pi)*meijerg(Tuple(), Tuple(), Tuple(0), Tuple(S(1)/2), z**2/4), cos(z), z) assert tn(meijerg(Tuple(1, 1),Tuple(), Tuple(1), Tuple(0), z), log(1 + z), z) # differentiation g = meijerg((randcplx(),), (randcplx() + 2*I,), Tuple(), (randcplx(), randcplx()), z) assert td(g, z) g = meijerg(Tuple(), (randcplx(),), Tuple(), (randcplx(), randcplx()), z) assert td(g, z) g = meijerg(Tuple(), Tuple(), Tuple(randcplx()), Tuple(randcplx(), randcplx()), z) assert td(g, z) a1, a2, b1, b2, c1, c2, d1, d2 = symbols('a1:3, b1:3, c1:3, d1:3') assert meijerg((a1, a2), (b1, b2), (c1, c2), (d1, d2), z).diff(z) == \ (meijerg((a1-1, a2), (b1, b2), (c1, c2), (d1, d2), z) \ + (a1 - 1)*meijerg((a1, a2), (b1, b2), (c1, c2), (d1, d2), z))/z raises(NotImplementedError, 'meijerg((z,), (), (), (), z).diff(z)') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_bessel.py0000644000175000017500000000717512014170666030571 0ustar georgeskgeorgeskfrom sympy import jn, yn, symbols, sin, cos, pi, S, jn_zeros, besselj, \ bessely, besseli, besselk, hankel1, hankel2, expand_func, \ latex, sqrt from sympy.functions.special.bessel import fn from sympy.utilities.pytest import raises, skip from sympy.utilities.randtest import \ random_complex_number as randcplx, \ test_numerically as tn, \ test_derivative_numerically as td from sympy.abc import z, n, k def test_bessel_rand(): assert td(besselj(randcplx(), z), z) assert td(bessely(randcplx(), z), z) assert td(besseli(randcplx(), z), z) assert td(besselk(randcplx(), z), z) assert td(hankel1(randcplx(), z), z) assert td(hankel2(randcplx(), z), z) assert td(jn(randcplx(), z), z) assert td(yn(randcplx(), z), z) def test_diff(): assert besselj(n, z).diff(z) == besselj(n-1, z)/2 - besselj(n+1, z)/2 assert bessely(n, z).diff(z) == bessely(n-1, z)/2 - bessely(n+1, z)/2 assert besseli(n, z).diff(z) == besseli(n-1, z)/2 + besseli(n+1, z)/2 assert besselk(n, z).diff(z) == -besselk(n-1, z)/2 - besselk(n+1, z)/2 assert hankel1(n, z).diff(z) == hankel1(n-1, z)/2 - hankel1(n+1, z)/2 assert hankel2(n, z).diff(z) == hankel2(n-1, z)/2 - hankel2(n+1, z)/2 def test_rewrite(): assert besselj(n, z).rewrite(jn) == sqrt(2*z/pi)*jn(n - S(1)/2, z) assert bessely(n, z).rewrite(yn) == sqrt(2*z/pi)*yn(n - S(1)/2, z) def test_fn(): x, z = symbols("x z") assert fn(1, z) == 1/z**2 assert fn(2, z) == -1/z + 3/z**3 assert fn(3, z) == -6/z**2 + 15/z**4 assert fn(4, z) == 1/z - 45/z**3 + 105/z**5 def mjn(n, z): return expand_func(jn(n,z)) def myn(n, z): return expand_func(yn(n,z)) def test_jn(): z = symbols("z") assert mjn(0, z) == sin(z)/z assert mjn(1, z) == sin(z)/z**2 - cos(z)/z assert mjn(2, z) == (3/z**3-1/z)*sin(z) - (3/z**2) * cos(z) assert mjn(3, z) == (15/z**4 - 6/z**2)*sin(z) + (1/z - 15/z**3)*cos(z) assert mjn(4, z) == (1/z + 105/z**5 - 45/z**3)*sin(z) + \ (-105/z**4 + 10/z**2)*cos(z) assert mjn(5, z) == (945/z**6 - 420/z**4 + 15/z**2)*sin(z) + \ (-1/z - 945/z**5 + 105/z**3)*cos(z) assert mjn(6, z) == (-1/z + 10395/z**7 - 4725/z**5 + 210/z**3)*sin(z) + \ (-10395/z**6 + 1260/z**4 - 21/z**2)*cos(z) def test_yn(): z = symbols("z") assert myn(0, z) == -cos(z)/z assert myn(1, z) == -cos(z)/z**2-sin(z)/z assert myn(2, z) == -((3/z**3-1/z)*cos(z)+(3/z**2)*sin(z)) def test_sympify_yn(): assert S(15) in myn(3, pi).atoms() assert myn(3, pi) == 15/pi**4 - 6/pi**2 def eq(a, b, tol=1e-6): for x, y in zip(a, b): if not (abs(x-y) < tol): return False return True def test_jn_zeros(): assert eq(jn_zeros(0, 4), [3.141592, 6.283185, 9.424777, 12.566370]) assert eq(jn_zeros(1, 4), [4.493409, 7.725251, 10.904121, 14.066193]) assert eq(jn_zeros(2, 4), [5.763459, 9.095011, 12.322940, 15.514603]) assert eq(jn_zeros(3, 4), [6.987932, 10.417118, 13.698023, 16.923621]) assert eq(jn_zeros(4, 4), [8.182561, 11.704907, 15.039664, 18.301255]) def test_jn_zeros_mpmath(): try: from mpmath import besseljzero except ImportError: skip("Cannot import mpmath.besseljzero.") zeros = lambda n, k: jn_zeros(n, k, method='mpmath') assert eq(zeros(0, 4), [3.141592, 6.283185, 9.424777, 12.566370]) assert eq(zeros(1, 4), [4.493409, 7.725251, 10.904121, 14.066193]) assert eq(zeros(2, 4), [5.763459, 9.095011, 12.322940, 15.514603]) assert eq(zeros(3, 4), [6.987932, 10.417118, 13.698023, 16.923621]) assert eq(zeros(4, 4), [8.182561, 11.704907, 15.039664, 18.301255]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_zeta_functions.py0000644000175000017500000000343012014170666032335 0ustar georgeskgeorgeskfrom sympy import Symbol, zeta, nan, Rational, Float, pi, dirichlet_eta, log, zoo x = Symbol('x') def test_zeta(): assert zeta(nan) == nan assert zeta(x, nan) == nan assert zeta(0) == Rational(-1,2) assert zeta(0, x) == Rational(1,2) - x assert zeta(1) == zoo assert zeta(1, 2) == zoo assert zeta(1, -7) == zoo assert zeta(1, x) == zoo assert zeta(2, 0) == pi**2/6 assert zeta(2, 1) == pi**2/6 assert zeta(2) == pi**2/6 assert zeta(4) == pi**4/90 assert zeta(6) == pi**6/945 assert zeta(2, 2) == pi**2/6 - 1 assert zeta(4, 3) == pi**4/90 - Rational(17, 16) assert zeta(6, 4) == pi**6/945 - Rational(47449, 46656) assert zeta(2, -2) == pi**2/6 + Rational(5, 4) assert zeta(4, -3) == pi**4/90 + Rational(1393, 1296) assert zeta(6, -4) == pi**6/945 + Rational(3037465, 2985984) assert zeta(-1) == -Rational(1, 12) assert zeta(-2) == 0 assert zeta(-3) == Rational(1, 120) assert zeta(-4) == 0 assert zeta(-5) == -Rational(1, 252) assert zeta(-1, 3) == -Rational(37, 12) assert zeta(-1, 7) == -Rational(253, 12) assert zeta(-1, -4) == Rational(119, 12) assert zeta(-1, -9) == Rational(539, 12) assert zeta(-4, 3) == -17 assert zeta(-4, -8) == 8772 assert zeta(0, 0) == -Rational(1, 2) assert zeta(0, 1) == -Rational(1, 2) assert zeta(0, -1) == Rational(1, 2) assert zeta(0, 2) == -Rational(3, 2) assert zeta(0, -2) == Rational(3, 2) assert zeta(3).evalf(20).epsilon_eq(Float("1.2020569031595942854",20), 1e-19) def test_dirichlet_eta(): assert dirichlet_eta(0) == Rational(1,2) assert dirichlet_eta(-1) == Rational(1,4) assert dirichlet_eta(1) == log(2) assert dirichlet_eta(2) == pi**2/12 assert dirichlet_eta(4) == pi**4*Rational(7,720) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_spherical_harmonics.py0000644000175000017500000000725112014170666033324 0ustar georgeskgeorgeskfrom sympy import Ylm, Zlm, Symbol, sympify, sqrt, pi, sin, cos, exp, I, S from sympy.functions.special.spherical_harmonics import Pl, Plm, Plmcos def test_Pl(): x = Symbol("x") assert Pl(0, x) == 1 assert Pl(1, x) == x assert Pl(2, x) == ((3*x**2 - 1)/2).expand() assert Pl(3, x) == ((5*x**3 - 3*x)/2).expand() assert Pl(4, x) == ((35*x**4-30*x**2+3)/8).expand() assert Pl(5, x) == ((63*x**5-70*x**3+15*x)/8).expand() assert Pl(6, x) == ((231*x**6-315*x**4+105*x**2-5)/16).expand() def test_Plm(): #http://en.wikipedia.org/wiki/Legendre_function x = Symbol("x") assert Plm(0, 0, x) == 1 assert Plm(1, -1, x) == -Plm(1, 1, x)/2 assert Plm(1, 0, x) == x assert Plm(1, 1, x) == -(1-x**2)**(sympify(1)/2) assert Plm(2, -2, x) == Plm(2, 2, x)/24 assert Plm(2, -1, x) == -Plm(2, 1, x)/6 assert Plm(2, 0, x) == (3*x**2-1)/2 assert Plm(2, 1, x) == -3*x*(1-x**2)**(sympify(1)/2) assert Plm(2, 2, x) == 3*(1-x**2) assert Plm(3, -3, x) == -Plm(3, 3, x)/720 assert Plm(3, -2, x) == Plm(3, 2, x)/120 assert Plm(3, -1, x) == -Plm(3, 1, x)/12 assert Plm(3, 0, x) == (5*x**3-3*x)/2 assert Plm(3, 1, x).expand() == (( 3*(1-5*x**2)/2 ).expand() \ *(1-x**2)**(sympify(1)/2)).expand() assert Plm(3, 2, x) == 15*x*(1-x**2) assert Plm(3, 3, x) == -15*(1-x**2)**(sympify(3)/2) def test_Plmcos(): #http://en.wikipedia.org/wiki/Legendre_function th = Symbol("th", real = True) assert Plmcos(0, 0, th) == 1 assert Plmcos(1, -1, th) == sin(th)/2 assert Plmcos(1, 0, th) == cos(th) assert Plmcos(1, 1, th) == -sin(th) assert Plmcos(2, 0, th) == (3*cos(th)**2-1)/2 assert Plmcos(2, 1, th) == -3*cos(th)*sin(th) assert Plmcos(2, 2, th) in [3*sin(th)**2, 3*(1-cos(th)**2)] assert Plmcos(3, 0, th) == (5*cos(th)**3-3*cos(th))/2 assert Plmcos(3, 1, th) == -sin(th)*(15*cos(th)**2/2-S(3)/2) assert Plmcos(3, 2, th) == 15*cos(th)*sin(th)**2 assert Plmcos(3, 3, th) == -15*sin(th)**3 def test_Ylm(): #http://en.wikipedia.org/wiki/Spherical_harmonics th, ph = Symbol("theta", real = True), Symbol("phi", real = True) assert Ylm(0, 0, th, ph) == sympify(1)/(2*sqrt(pi)) assert Ylm(1, -1, th, ph) == sympify(1)/2 * sqrt(3/(2*pi)) * sin(th) * \ exp(-I*ph) assert Ylm(1, 0, th, ph) == sympify(1)/2 * sqrt(3/pi) * cos(th) assert Ylm(1, 1, th, ph) == -sympify(1)/2 * sqrt(3/(2*pi)) * sin(th) * \ exp(I*ph) #Ylm returns here a correct, but different expression: #assert Ylm(2, -2, th, ph).expand() == (sympify(1)/4 * sqrt(15/(2*pi)) * \ # sin(th)**2 * exp(-2*I*ph)).expand() assert Ylm(2, 0, th, ph).expand() == (sympify(1)/4 * sqrt(5/pi) * \ (3*cos(th)**2-1)).expand() assert Ylm(2, 1, th, ph).expand() == (-sympify(1)/2 * \ sqrt(3)*sqrt(5/(2*pi)) * (sin(th)*cos(th)) * exp(I*ph)).expand() #Ylm returns here a correct, but different expression: #assert Ylm(2, 2, th, ph).expand() == (sympify(1)/4 * sqrt(15/(2*pi)) * \ # sin(th)**2 * exp(2*I*ph)).expand() def test_Zlm(): #http://en.wikipedia.org/wiki/Solid_harmonics#List_of_lowest_functions th, ph = Symbol("theta", real = True), Symbol("phi", real = True) assert Zlm(0, 0, th, ph) == sqrt(1/(4*pi)) assert Zlm(1, -1, th, ph) == sqrt(3/(4*pi))*sin(th)*sin(ph) assert Zlm(1, 0, th, ph) == sqrt(3/(4*pi))*cos(th) assert Zlm(1, 1, th, ph) == sqrt(3/(4*pi))*sin(th)*cos(ph) assert Zlm(2, -1, th, ph) == sqrt(15/(4*pi))*sin(th)*cos(th)*sin(ph) assert Zlm(2, 0, th, ph).expand() == (sympify(1)/4 * sqrt(5/pi) * \ (3*cos(th)**2-1)).expand() assert Zlm(2, 1, th, ph) == sqrt(15/(4*pi))*sin(th)*cos(th)*cos(ph) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_tensor_functions.py0000644000175000017500000000127212014170666032706 0ustar georgeskgeorgeskfrom sympy import symbols, Dij, LeviCivita x, y = symbols('x,y') def test_Dij(): assert Dij(1, 1) == 1 assert Dij(1, 2) == 0 assert Dij(x, x) == 1 assert Dij(x**2-y**2, x**2-y**2) == 1 def test_levicivita(): assert LeviCivita(1, 2, 3) == 1 assert LeviCivita(1, 3, 2) == -1 assert LeviCivita(1, 2, 2) == 0 i,j,k = symbols('i j k') assert LeviCivita(i, j, k) == LeviCivita(i,j,k, evaluate=False) assert LeviCivita(i, j, i) == 0 assert LeviCivita(1, i, i) == 0 assert LeviCivita(i, j, k).doit() == (j - i)*(k - i)*(k - j)/2 assert LeviCivita(1, 2, 3, 1) == 0 assert LeviCivita(4, 5, 1, 2, 3) == 1 assert LeviCivita(4, 5, 2, 1, 3) == -1 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_delta_functions.py0000644000175000017500000000071012014170666032461 0ustar georgeskgeorgeskfrom sympy import symbols, DiracDelta, Heaviside, nan, oo, sqrt, pi x = symbols('x') def test_DiracDelta(): assert DiracDelta(1) == 0 assert DiracDelta(5.1) == 0 assert DiracDelta(-pi) == 0 assert DiracDelta(5,7) == 0 assert DiracDelta(0) == oo assert DiracDelta(0,5) == oo assert DiracDelta(x).func == DiracDelta def test_heaviside(): assert Heaviside(0) == 0.5 assert Heaviside(-5) == 0 assert Heaviside(1) == 1 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_gamma_functions.py0000644000175000017500000001663412014170666032466 0ustar georgeskgeorgeskfrom sympy import Symbol, gamma, oo, nan, zoo, factorial, sqrt, Rational, log,\ polygamma, EulerGamma, pi, uppergamma, S, expand_func, loggamma, sin, cos, \ O, cancel, lowergamma, exp, erf from sympy.utilities.randtest import (test_derivative_numerically as td, random_complex_number as randcplx, test_numerically as tn) x = Symbol('x') y = Symbol('y') n = Symbol('n', integer=True) def test_gamma(): assert gamma(nan) == nan assert gamma(oo) == oo assert gamma(-100) == zoo assert gamma(0) == zoo assert gamma(1) == 1 assert gamma(2) == 1 assert gamma(3) == 2 assert gamma(102) == factorial(101) assert gamma(Rational(1,2)) == sqrt(pi) assert gamma(Rational(3, 2)) == Rational(1, 2)*sqrt(pi) assert gamma(Rational(5, 2)) == Rational(3, 4)*sqrt(pi) assert gamma(Rational(7, 2)) == Rational(15, 8)*sqrt(pi) assert gamma(Rational(-1, 2)) == -2*sqrt(pi) assert gamma(Rational(-3, 2)) == Rational(4, 3)*sqrt(pi) assert gamma(Rational(-5, 2)) == -Rational(8, 15)*sqrt(pi) assert gamma(Rational(-15, 2)) == Rational(256, 2027025)*sqrt(pi) assert gamma(x).diff(x) == gamma(x)*polygamma(0, x) assert gamma(x - 1).expand(func=True) == gamma(x)/(x-1) assert gamma(x + 2).expand(func=True, mul=False) == x*(x+1)*gamma(x) assert expand_func(gamma(x + Rational(3, 2))) == \ (x + Rational(1, 2))*gamma(x + Rational(1, 2)) assert expand_func(gamma(x - Rational(1, 2))) == \ gamma(Rational(1, 2) + x)/(x - Rational(1, 2)) def test_gamma_series(): assert gamma(x + 1).series(x, 0, 3) == \ 1 - x*EulerGamma + x**2*EulerGamma**2/2 + pi**2*x**2/12 + O(x**3) assert gamma(x).series(x, -1, 3) == \ -1/x + EulerGamma - 1 + EulerGamma*x - EulerGamma**2*x/2 - pi**2*x/12 \ - x + EulerGamma*x**2 + EulerGamma*pi**2*x**2/12 - \ x**2*polygamma(2, 1)/6 + EulerGamma**3*x**2/6 - EulerGamma**2*x**2/2 \ - pi**2*x**2/12 - x**2 + O(x**3) def test_lowergamma(): from sympy import meijerg assert lowergamma(x, y).diff(y) == y**(x-1)*exp(-y) assert td(lowergamma(randcplx(), y), y) assert lowergamma(x, y).diff(x) == \ gamma(x)*polygamma(0, x) - uppergamma(x, y)*log(y) \ + meijerg([], [1, 1], [0, 0, x], [], y) assert lowergamma(S.Half, x) == sqrt(pi)*erf(sqrt(x)) assert not lowergamma(S.Half - 3, x).has(lowergamma) assert not lowergamma(S.Half + 3, x).has(lowergamma) assert lowergamma(S.Half, x, evaluate=False).has(lowergamma) assert tn(lowergamma(S.Half + 3, x, evaluate=False), lowergamma(S.Half + 3, x), x) assert tn(lowergamma(S.Half - 3, x, evaluate=False), lowergamma(S.Half - 3, x), x) def test_uppergamma(): from sympy import meijerg assert uppergamma(4, 0) == 6 assert uppergamma(x, y).diff(y) == -y**(x-1)*exp(-y) assert td(uppergamma(randcplx(), y), y) assert uppergamma(x, y).diff(x) == \ uppergamma(x, y)*log(y) + meijerg([], [1, 1], [0, 0, x], [], y) assert td(uppergamma(x, randcplx()), x) assert uppergamma(S.Half, x) == sqrt(pi)*(1 - erf(sqrt(x))) assert not uppergamma(S.Half - 3, x).has(uppergamma) assert not uppergamma(S.Half + 3, x).has(uppergamma) assert uppergamma(S.Half, x, evaluate=False).has(uppergamma) assert tn(uppergamma(S.Half + 3, x, evaluate=False), uppergamma(S.Half + 3, x), x) assert tn(uppergamma(S.Half - 3, x, evaluate=False), uppergamma(S.Half - 3, x), x) def test_polygamma(): assert polygamma(n, nan) == nan assert polygamma(0, oo) == oo assert polygamma(1, oo) == 0 assert polygamma(5, oo) == 0 assert polygamma(0, -9) == zoo assert polygamma(0, -9) == zoo assert polygamma(0, -1) == zoo assert polygamma(0, 0) == zoo assert polygamma(0, 1) == -EulerGamma assert polygamma(0, 7) == Rational(49, 20) - EulerGamma assert polygamma(1, 1) == pi**2/6 assert polygamma(1, 2) == pi**2/6 - 1 assert polygamma(1, 3) == pi**2/6 - Rational(5, 4) assert polygamma(3, 1) == pi**4 / 15 assert polygamma(3, 5) == 6*(Rational(-22369,20736) + pi**4/90) assert polygamma(5, 1) == 8 * pi**6 / 63 assert polygamma(3, 7*x).diff(x) == 7*polygamma(4, 7*x) def test_polygamma_expand_func(): assert polygamma(0, x).expand(func=True) == polygamma(0, x) assert polygamma(0, 2*x).expand(func=True) == \ polygamma(0, x)/2 + polygamma(0, Rational(1, 2) + x)/2 + log(2) assert polygamma(1, 2*x).expand(func=True) == \ polygamma(1, x)/4 + polygamma(1, Rational(1, 2) + x)/4 assert polygamma(2, x).expand(func=True) == \ polygamma(2, x) assert polygamma(0, -1 + x).expand(func=True) == \ polygamma(0, x) - 1/(x - 1) assert polygamma(0, 1 + x).expand(func=True) == \ 1/x + polygamma(0, x ) assert polygamma(0, 2 + x).expand(func=True) == \ 1/x + 1/(1 + x) + polygamma(0, x) assert polygamma(0, 3 + x).expand(func=True) == \ polygamma(0, x) + 1/x + 1/(1 + x) + 1/(2 + x) assert polygamma(0, 4 + x).expand(func=True) == \ polygamma(0, x) + 1/x + 1/(1 + x) + 1/(2 + x) + 1/(3 + x) assert polygamma(1, 1 + x).expand(func=True) == \ polygamma(1, x) - 1/x**2 assert polygamma(1, 2 + x).expand(func=True, multinomial=False) == \ polygamma(1, x) - 1/x**2 - 1/(1 + x)**2 assert polygamma(1, 3 + x).expand(func=True, multinomial=False) == \ polygamma(1, x) - 1/x**2 - 1/(1 + x)**2 - 1/(2 + x)**2 assert polygamma(1, 4 + x).expand(func=True, multinomial=False) == \ polygamma(1, x) - 1/x**2 - 1/(1 + x)**2 - \ 1/(2 + x)**2 - 1/(3 + x)**2 assert polygamma(0, x + y).expand(func=True) == \ polygamma(0, x + y) assert polygamma(1, x + y).expand(func=True) == \ polygamma(1, x + y) assert polygamma(1, 3 + 4*x + y).expand(func=True, multinomial=False) == \ polygamma(1, y + 4*x) - 1/(y + 4*x)**2 - \ 1/(1 + y + 4*x)**2 - 1/(2 + y + 4*x)**2 assert polygamma(3, 3 + 4*x + y).expand(func=True, multinomial=False) == \ polygamma(3, y + 4*x) - 6/(y + 4*x)**4 - \ 6/(1 + y + 4*x)**4 - 6/(2 + y + 4*x)**4 assert polygamma(3, 4*x + y + 1).expand(func=True, multinomial=False) == \ polygamma(3, y + 4*x) - 6/(y + 4*x)**4 e = polygamma(3, 4*x + y + S(3)/2) assert e.expand(func=True) == e e = polygamma(3, x + y + S(3)/4) assert e.expand(func = True, basic = False) == e def test_loggamma(): s1 = loggamma(1/(x+sin(x))+cos(x)).nseries(x,n=4) s2 = (-log(2*x)-1)/(2*x) - log(x/pi)/2 + (4-log(2*x))*x/24 + O(x**2) assert (s1 - s2).expand(force=True).removeO() == 0 s1 = loggamma(1/x).series(x) s2 = (1/x-S(1)/2)*log(1/x) - 1/x + log(2*pi)/2 + \ x/12 - x**3/360 + x**5/1260 + O(x**7) assert ((s1 - s2).expand(force=True)).removeO() == 0 def tN(N, M): assert loggamma(1/x)._eval_nseries(x,n=N,logx=None).getn() == M tN(0, 0) tN(1, 1) tN(2, 3) tN(3, 3) tN(4, 5) tN(5, 5) def test_polygamma_expansion(): # A. & S., pa. 259 and 260 assert polygamma(0, 1/x).nseries(x, n=3) \ == -log(x) - x/2 - x**2/12 + O(x**4) assert polygamma(1, 1/x).series(x, n=5) \ == x + x**2/2 + x**3/6 + O(x**5) assert polygamma(3, 1/x).nseries(x, n=8) \ == 2*x**3 + 3*x**4 + 2*x**5 - x**7 + 4*x**9/3 + O(x**11) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_bsplines.py0000644000175000017500000000435212014170666031125 0ustar georgeskgeorgeskfrom sympy.functions import bspline_basis, bspline_basis_set from sympy import Piecewise, Interval from sympy import symbols, Rational x, y = symbols('x,y') def test_basic_degree_0(): d = 0 knots = range(5) splines = bspline_basis_set(d, knots, x) for i in range(len(splines)): assert splines[i] == Piecewise((1, Interval(i, i+1)), (0, True)) def test_basic_degree_1(): d = 1 knots = range(5) splines = bspline_basis_set(d, knots, x) assert splines[0] == Piecewise((x,Interval(0,1,False,True)),(2-x,Interval(1,2)),(0,True)) assert splines[1] == Piecewise((-1+x,Interval(1,2,False,True)),(3-x,Interval(2,3)),(0,True)) assert splines[2] == Piecewise((-2+x,Interval(2,3,False,True)),(4-x,Interval(3,4)),(0,True)) def test_basic_degree_2(): d = 2 knots = range(5) splines = bspline_basis_set(d, knots, x) b0 = Piecewise((x**2/2,Interval(0,1,False,True)),(Rational('-3/2')+3*x-x**2,Interval(1,2,False,True)),(Rational(9,2)-3*x+x**2/2,Interval(2,3)),(0,True)) b1 = Piecewise((Rational(1,2)-x+x**2/2,Interval(1,2,False,True)),(Rational(-11,2)+5*x-x**2,Interval(2,3,False,True)),(8-4*x+x**2/2,Interval(3,4)),(0,True)) assert splines[0] == b0 assert splines[1] == b1 def test_basic_degree_3(): d = 3 knots = range(5) splines = bspline_basis_set(d, knots, x) b0 = Piecewise( (x**3/6, Interval(0,1,False,True)), (Rational(2,3) - 2*x + 2*x**2 - x**3/2, Interval(1,2,False,True)), (Rational(-22,3) + 10*x - 4*x**2 + x**3/2, Interval(2,3,False,True)), (Rational(32,3) - 8*x + 2*x**2 - x**3/6, Interval(3,4)), (0, True) ) assert splines[0] == b0 def test_repeated_degree_1(): d = 1 knots = [0,0,1,2,2,3,4,4] splines = bspline_basis_set(d, knots, x) assert splines[0] == Piecewise((1 - x, Interval(0,1)), (0, True)) assert splines[1] == Piecewise((x, Interval(0,1,False,True)), (2 - x, Interval(1,2)), (0, True)) assert splines[2] == Piecewise((-1 + x, Interval(1,2)), (0, True)) assert splines[3] == Piecewise((3 - x, Interval(2,3)), (0, True)) assert splines[4] == Piecewise((-2 + x, Interval(2,3,False,True)), (4 - x, Interval(3,4)), (0, True)) assert splines[5] == Piecewise((-3 + x, Interval(3,4)), (0, True)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_spec_polynomials.py0000644000175000017500000000626312014170666032671 0ustar georgeskgeorgeskfrom sympy import (legendre, Symbol, hermite, chebyshevu, chebyshevt, chebyshevt_root, chebyshevu_root, assoc_legendre, Rational, roots, sympify, S, laguerre_l, laguerre_poly) x = Symbol('x') def test_legendre(): assert legendre(0, x) == 1 assert legendre(1, x) == x assert legendre(2, x) == ((3*x**2-1)/2).expand() assert legendre(3, x) == ((5*x**3-3*x)/2).expand() assert legendre(4, x) == ((35*x**4-30*x**2+3)/8).expand() assert legendre(5, x) == ((63*x**5-70*x**3+15*x)/8).expand() assert legendre(6, x) == ((231*x**6-315*x**4+105*x**2-5)/16).expand() assert legendre(10, -1) == 1 assert legendre(11, -1) == -1 assert legendre(10, 1) == 1 assert legendre(11, 1) == 1 assert legendre(10, 0) != 0 assert legendre(11, 0) == 0 assert roots(legendre(4,x), x) == { (Rational(3, 7) - Rational(2, 35)*30**S.Half)**S.Half: 1, -(Rational(3, 7) - Rational(2, 35)*30**S.Half)**S.Half: 1, (Rational(3, 7) + Rational(2, 35)*30**S.Half)**S.Half: 1, -(Rational(3, 7) + Rational(2, 35)*30**S.Half)**S.Half: 1, } def test_assoc_legendre(): Plm=assoc_legendre Q=(1-x**2)**Rational(1,2) assert Plm(0, 0, x) == 1 assert Plm(1, 0, x) == x assert Plm(1, 1, x) == -Q assert Plm(2, 0, x) == (3*x**2-1)/2 assert Plm(2, 1, x) == -3*x*Q assert Plm(2, 2, x) == 3*Q**2 assert Plm(3, 0, x) == (5*x**3-3*x)/2 assert Plm(3, 1, x).expand() == (( 3*(1-5*x**2)/2 ).expand() * Q).expand() assert Plm(3, 2, x) == 15*x * Q**2 assert Plm(3, 3, x) == -15 * Q**3 # negative m assert Plm(1,-1, x) == -Plm(1, 1, x)/2 assert Plm(2,-2, x) == Plm(2, 2, x)/24 assert Plm(2,-1, x) == -Plm(2, 1, x)/6 assert Plm(3,-3, x) == -Plm(3, 3, x)/720 assert Plm(3,-2, x) == Plm(3, 2, x)/120 assert Plm(3,-1, x) == -Plm(3, 1, x)/12 def test_chebyshev(): assert chebyshevt(0, x) == 1 assert chebyshevt(1, x) == x assert chebyshevt(2, x) == 2*x**2-1 assert chebyshevt(3, x) == 4*x**3-3*x for n in range(1, 4): for k in range(n): z = chebyshevt_root(n, k) assert chebyshevt(n, z) == 0 for n in range(1, 4): for k in range(n): z = chebyshevu_root(n, k) assert chebyshevu(n, z) == 0 def test_hermite(): assert hermite(6, x) == 64*x**6 - 480*x**4 + 720*x**2 - 120 def test_laguerre(): alpha = Symbol("alpha") # generalized Laguerre polynomials: assert laguerre_l(0, alpha, x) == 1 assert laguerre_l(1, alpha, x) == -x + alpha + 1 assert laguerre_l(2, alpha, x).expand() == (x**2/2 - (alpha+2)*x + (alpha+2)*(alpha+1)/2).expand() assert laguerre_l(3, alpha, x).expand() == (-x**3/6 + (alpha+3)*x**2/2 - (alpha+2)*(alpha+3)*x/2 + (alpha+1)*(alpha+2)*(alpha+3)/6).expand() # Laguerre polynomials: assert laguerre_l(0, 0, x) == 1 assert laguerre_l(1, 0, x) == 1 - x assert laguerre_l(2, 0, x).expand() == 1 - 2*x + x**2/2 assert laguerre_l(3, 0, x).expand() == 1 - 3*x + 3*x**2/2 - x**3/6 # Test the lowest 10 polynomials with laguerre_poly, to make sure that it # works: for i in range(10): assert laguerre_l(i, 0, x).expand() == laguerre_poly(i, x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/tests/test_error_functions.py0000644000175000017500000000074612014170666032532 0ustar georgeskgeorgeskfrom sympy import symbols, erf, nan, oo, Float, sqrt, pi, O x, y = symbols('x,y') def test_erf(): assert erf(nan) == nan assert erf(oo) == 1 assert erf(-oo) == -1 assert erf(0) == 0 assert erf(-2) == -erf(2) assert erf(-x*y) == -erf(x*y) def test_erf_series(): assert erf(x).series(x, 0, 7) == 2*x/sqrt(pi) - \ 2*x**3/3/sqrt(pi) + x**5/5/sqrt(pi) + O(x**7) def test_erf_evalf(): assert abs( erf(Float(2.0)) - 0.995322265 ) < 1E-8 # XXX wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/special/__init__.py0000644000175000017500000000020712014170666026637 0ustar georgeskgeorgeskimport gamma_functions import error_functions import zeta_functions import tensor_functions import delta_functions import polynomials wxgeometrie-0.133.2.orig/wxgeometrie/sympy/functions/__init__.py0000644000175000017500000000351612014170666025225 0ustar georgeskgeorgesk"""A functions module, includes all the standard functions. Combinatorial - factorial, fibonacci, harmonic, bernoulli... Elementary - hyperbolic, trigonometric, exponential, floor and ceiling, sqrt... Special - gamma, zeta,spherical harmonics... """ from sympy.core.basic import Basic import combinatorial import elementary import special from special.polynomials import (legendre, assoc_legendre, hermite, chebyshevt, chebyshevu, chebyshevu_root, chebyshevt_root, laguerre_l) # see #391 from combinatorial.factorials import factorial, factorial2, rf, ff, binomial from combinatorial.factorials import factorial, RisingFactorial, FallingFactorial from combinatorial.factorials import binomial, factorial2 from combinatorial.numbers import fibonacci, lucas, harmonic, bernoulli, bell from elementary.miscellaneous import sqrt, Min, Max, Id from elementary.complexes import re, im, sign, Abs, conjugate, arg from elementary.trigonometric import acot, cot, tan, cos, sin, asin, acos, atan, atan2 from elementary.exponential import exp, log, LambertW from elementary.hyperbolic import sinh, cosh, tanh, coth, asinh, acosh, atanh, acoth from elementary.integers import floor, ceiling from elementary.piecewise import Piecewise, piecewise_fold from special.error_functions import erf from special.gamma_functions import gamma, lowergamma, uppergamma, polygamma, \ loggamma, digamma, trigamma from special.zeta_functions import dirichlet_eta, zeta from special.spherical_harmonics import Ylm, Zlm from special.tensor_functions import Dij, Eijk, LeviCivita from special.delta_functions import DiracDelta, Heaviside from special.bsplines import bspline_basis, bspline_basis_set from special.bessel import besselj, bessely, besseli, besselk, hankel1, \ hankel2, jn, yn, jn_zeros from special.hyper import hyper, meijerg ln = log wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/0000755000175000017500000000000012014170666022027 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/expr.py0000644000175000017500000020271512014170666023366 0ustar georgeskgeorgeskfrom core import C from basic import Basic, Atom from singleton import S from evalf import EvalfMixin from decorators import _sympifyit, call_highest_priority from cache import cacheit from compatibility import reduce class Expr(Basic, EvalfMixin): __slots__ = [] def sort_key(self, order=None): # XXX: The order argument does not actually work from sympy.core import S def key_inner(arg): if isinstance(arg, Basic): return arg.sort_key(order=order) elif hasattr(arg, '__iter__'): return tuple(key_inner(arg) for arg in args) else: return arg coeff, expr = self.as_coeff_Mul() if expr.is_Pow: expr, exp = expr.args else: expr, exp = expr, S.One if expr.is_Atom: if expr.is_Symbol: args = (str(expr),) else: args = (expr,) else: if expr.is_Add: args = expr.as_ordered_terms(order=order) else: args = expr.args args = tuple(key_inner(arg) for arg in args) if expr.is_Mul: args = sorted(args) args = (len(args), args) exp = exp.sort_key(order=order) return expr.class_key(), args, exp, coeff # *************** # * Arithmetics * # *************** # Expr and its sublcasses use _op_priority to determine which object # passed to a binary special method (__mul__, etc.) will handle the # operation. In general, the 'call_highest_priority' decorator will choose # the object with the highest _op_priority to handle the call. # Custom subclasses that want to define their own binary special methods # should set an _op_priority value that is higher than the default. # # **NOTE**: # This is a temporary fix, and will eventually be replaced with # something better and more powerful. See issue 2411. _op_priority = 10.0 def __pos__(self): return self def __neg__(self): return Mul(S.NegativeOne, self) def __abs__(self): return C.Abs(self) @_sympifyit('other', NotImplemented) @call_highest_priority('__radd__') def __add__(self, other): return Add(self, other) @_sympifyit('other', NotImplemented) @call_highest_priority('__add__') def __radd__(self, other): return Add(other, self) @_sympifyit('other', NotImplemented) @call_highest_priority('__rsub__') def __sub__(self, other): return Add(self, -other) @_sympifyit('other', NotImplemented) @call_highest_priority('__sub__') def __rsub__(self, other): return Add(other, -self) @_sympifyit('other', NotImplemented) @call_highest_priority('__rmul__') def __mul__(self, other): return Mul(self, other) @_sympifyit('other', NotImplemented) @call_highest_priority('__mul__') def __rmul__(self, other): return Mul(other, self) @_sympifyit('other', NotImplemented) @call_highest_priority('__rpow__') def __pow__(self, other): return Pow(self, other) @_sympifyit('other', NotImplemented) @call_highest_priority('__pow__') def __rpow__(self, other): return Pow(other, self) @_sympifyit('other', NotImplemented) @call_highest_priority('__rdiv__') def __div__(self, other): return Mul(self, Pow(other, S.NegativeOne)) @_sympifyit('other', NotImplemented) @call_highest_priority('__div__') def __rdiv__(self, other): return Mul(other, Pow(self, S.NegativeOne)) __truediv__ = __div__ __rtruediv__ = __rdiv__ def __float__(self): result = self.evalf() if result.is_Number: return float(result) else: raise ValueError("Symbolic value, can't compute") def __complex__(self): result = self.evalf() re, im = result.as_real_imag() return complex(float(re), float(im)) @_sympifyit('other', False) # sympy > other def __lt__(self, other): dif = self - other if dif.is_negative != dif.is_nonnegative: return dif.is_negative return C.StrictInequality(self, other) @_sympifyit('other', True) # sympy > other def __gt__(self, other): dif = self - other if dif.is_positive != dif.is_nonpositive: return dif.is_positive return C.StrictInequality(other, self) @_sympifyit('other', False) # sympy > other def __le__(self, other): dif = self - other if dif.is_nonpositive != dif.is_positive: return dif.is_nonpositive return C.Inequality(self, other) @_sympifyit('other', True) # sympy > other def __ge__(self, other): dif = self - other if dif.is_nonnegative != dif.is_negative: return dif.is_nonnegative return C.Inequality(other, self) @staticmethod def _from_mpmath(x, prec): if hasattr(x, "_mpf_"): return C.Float._new(x._mpf_, prec) elif hasattr(x, "_mpc_"): re, im = x._mpc_ re = C.Float._new(re, prec) im = C.Float._new(im, prec)*S.ImaginaryUnit return re+im else: raise TypeError("expected mpmath number (mpf or mpc)") @property def is_number(self): """Returns True if 'self' is a number. >>> from sympy import log, Integral >>> from sympy.abc import x, y >>> x.is_number False >>> (2*x).is_number False >>> (2 + log(2)).is_number True >>> (2 + Integral(2, x)).is_number False >>> (2 + Integral(2, (x, 1, 2))).is_number True """ if not self.args: return False return all(obj.is_number for obj in self.iter_basic_args()) def _eval_interval(self, x, a, b): """ Returns evaluation over an interval. For most functions this is: self.subs(x, b) - self.subs(x, a), possibly using limit() if NaN is returned from subs. If b or a is None, it only evaluates -self.subs(x, a) or self.subs(b, x), respectively. """ from sympy.series import limit if (a is None and b is None): raise ValueError('Both interval ends cannot be None.') if a is None: A = 0 else: A = self.subs(x, a) if A is S.NaN: A = limit(self, x, a) if A is S.NaN: return A if b is None: B = 0 else: B = self.subs(x, b) if B is S.NaN: B = limit(self, x, b) return B - A def _eval_power(self, other): return None def _eval_conjugate(self): if self.is_real: return self def conjugate(self): from sympy.functions.elementary.complexes import conjugate as c return c(self) @classmethod def _parse_order(cls, order): """Parse and configure the ordering of terms. """ from sympy.polys.monomialtools import monomial_key try: reverse = order.startswith('rev-') except AttributeError: reverse = False else: if reverse: order = order[4:] monom_key = monomial_key(order) def neg(monom): result = [] for m in monom: if isinstance(m, tuple): result.append(neg(m)) else: result.append(-m) return tuple(result) def key(term): _, ((re, im), monom, ncpart) = term monom = neg(monom_key(monom)) ncpart = tuple([ e.sort_key(order=order) for e in ncpart ]) coeff = ((bool(im), im), (re, im)) return monom, ncpart, coeff return key, reverse def as_ordered_factors(self, order=None): """ Transform an expression to an ordered list of factors. **Examples** >>> from sympy import sin, cos >>> from sympy.abc import x, y >>> (2*x*y*sin(x)*cos(x)).as_ordered_factors() [2, x, y, sin(x), cos(x)] """ if not self.is_Mul: return [self] cpart = [] ncpart = [] for arg in self.args: if arg.is_commutative: cpart.append(arg) else: ncpart.append(arg) return sorted(cpart, key=lambda expr: expr.sort_key(order=order)) + ncpart def as_ordered_terms(self, order=None, data=False): """ Transform an expression to an ordered list of terms. **Examples** >>> from sympy import sin, cos >>> from sympy.abc import x, y >>> (sin(x)**2*cos(x) + sin(x)**2 + 1).as_ordered_terms() [sin(x)**2*cos(x), sin(x)**2, 1] """ key, reverse = self._parse_order(order) terms, gens = self.as_terms() if not any(term.is_Order for term, _ in terms): ordered = sorted(terms, key=key, reverse=reverse) else: _terms, _order = [], [] for term, repr in terms: if not term.is_Order: _terms.append((term, repr)) else: _order.append((term, repr)) ordered = sorted(_terms, key=key, reverse=True) \ + sorted(_order, key=key, reverse=True) if data: return ordered, gens else: return [ term for term, _ in ordered ] def as_terms(self): """Transform an expression to a list of terms. """ from sympy.core import Add, Mul, S from sympy.core.exprtools import decompose_power from sympy.utilities import default_sort_key gens, terms = set([]), [] for term in Add.make_args(self): coeff, _term = term.as_coeff_Mul() coeff = complex(coeff) cpart, ncpart = {}, [] if _term is not S.One: for factor in Mul.make_args(_term): if factor.is_number: try: coeff *= complex(factor) except ValueError: pass else: continue if factor.is_commutative: base, exp = decompose_power(factor) cpart[base] = exp gens.add(base) else: ncpart.append(factor) coeff = coeff.real, coeff.imag ncpart = tuple(ncpart) terms.append((term, (coeff, cpart, ncpart))) gens = sorted(gens, key=default_sort_key) k, indices = len(gens), {} for i, g in enumerate(gens): indices[g] = i result = [] for term, (coeff, cpart, ncpart) in terms: monom = [0]*k for base, exp in cpart.iteritems(): monom[indices[base]] = exp result.append((term, (coeff, tuple(monom), ncpart))) return result, gens def removeO(self): """Removes the additive O(..) symbol if there is one""" return self def getO(self): """Returns the additive O(..) symbol if there is one, else None.""" return None def getn(self): """ Returns the order of the expression. The order is determined either from the O(...) term. If there is no O(...) term, it returns None. Example: >>> from sympy import O >>> from sympy.abc import x >>> (1 + x + O(x**2)).getn() 2 >>> (1 + x).getn() """ o = self.getO() if o is None: return None elif o.is_Order: o = o.expr if o is S.One: return S.Zero if o.is_Symbol: return S.One if o.is_Pow: return o.args[1] if o.is_Mul: # x**n*log(x)**n or x**n/log(x)**n for oi in o.args: if oi.is_Symbol: return S.One if oi.is_Pow: syms = oi.atoms(C.Symbol) if len(syms) == 1: x = syms.pop() oi = oi.subs(x, C.Dummy('x', positive=True)) if oi.base.is_Symbol and oi.exp.is_Rational: return abs(oi.exp) raise NotImplementedError('not sure of order of %s' % o) def count_ops(self, visual=None): """wrapper for count_ops that returns the operation count.""" from sympy import count_ops return count_ops(self, visual) def args_cnc(self): """treat self as Mul and split it into tuple (set, list) where ``set`` contains the commutative parts and ``list`` contains the ordered non-commutative args. A special treatment is that -1 is separated from a Rational: >>> from sympy import symbols >>> A, B = symbols('A B', commutative=0) >>> x, y = symbols('x y') >>> (-2*x*y).args_cnc() [set([-1, 2, x, y]), []] >>> (-2*x*A*B*y).args_cnc() [set([-1, 2, x, y]), [A, B]] The arg is treated as a Mul: >>> (-2 + x + A).args_cnc() [set(), [x - 2 + A]] """ if self.is_Mul: args = list(self.args) else: args = [self] for i, mi in enumerate(args): if not mi.is_commutative: c = args[:i] nc = args[i:] break else: c = args nc = [] if c and c[0].is_Rational and c[0].is_negative and c[0] != S.NegativeOne: c[:1] = [S.NegativeOne, -c[0]] return [set(c), nc] def coeff(self, x, right=False): """ Returns the coefficient of the exact term "x" or None if there is no "x". When x is noncommutative, the coeff to the left (default) or right of x can be returned. The keyword 'right' is ignored when x is commutative. Examples:: >>> from sympy import symbols >>> from sympy.abc import x, y, z You can select terms that have an explicit negative in front of them: >>> (-x+2*y).coeff(-1) x >>> (x-2*y).coeff(-1) 2*y You can select terms with no rational coefficient: >>> (x+2*y).coeff(1) x >>> (3+2*x+4*x**2).coeff(1) You can select terms that have a numerical term in front of them: >>> (-x-2*y).coeff(2) -y >>> from sympy import sqrt >>> (x+sqrt(2)*x).coeff(sqrt(2)) x The matching is exact: >>> (3+2*x+4*x**2).coeff(x) 2 >>> (3+2*x+4*x**2).coeff(x**2) 4 >>> (3+2*x+4*x**2).coeff(x**3) >>> (z*(x+y)**2).coeff((x+y)**2) z >>> (z*(x+y)**2).coeff(x+y) In addition, no factoring is done, so 2 + y is not obtained from the following: >>> (2*x+2+(x+1)*y).coeff(x+1) y >>> n, m, o = symbols('n m o', commutative=False) >>> n.coeff(n) 1 >>> (3*n).coeff(n) 3 >>> (n*m + m*n*m).coeff(n) # = (1 + m)*n*m 1 + m >>> (n*m + m*n*m).coeff(n, right=True) # = (1 + m)*n*m m If there is more than one possible coefficient None is returned: >>> (n*m + m*n).coeff(n) If there is only one possible coefficient, it is returned: >>> (n*m + o*m*n).coeff(m*n) o >>> (n*m + o*m*n).coeff(m*n, right=1) 1 """ x = sympify(x) if not x: # 0 or None return None if x == self: return S.One if x is S.One: try: assert Add.make_args(S.Zero) and Mul.make_args(S.One) # replace try/except with this co = [a for a in Add.make_args(self) if not any(ai.is_number for ai in Mul.make_args(a))] except AssertionError: co = [a for a in (Add.make_args(self) or [S.Zero]) if not any(ai.is_number for ai in (Mul.make_args(a) or [S.One]))] if not co: return None return Add(*co) def incommon(l1, l2): if not l1 or not l2: return [] n = min(len(l1), len(l2)) for i in xrange(n): if l1[i] != l2[i]: return l1[:i] return l1[:] def arglist(x): """ Return list of x's args when treated as a Mul after checking to see if a negative Rational is present (in which case it is made positive and a -1 is added to the list). """ margs = list(Mul.make_args(x)) try: assert Mul.make_args(S.One) # replace try/except with the following if margs[0].is_Rational and margs[0].is_negative and margs[0] != S.NegativeOne: margs.append(S.NegativeOne) margs[0] *= -1 except AssertionError: if margs and margs[0].is_Rational and margs[0].is_negative and margs[0] != S.NegativeOne: margs.append(S.NegativeOne) margs[0] *= -1 return margs def find(l, sub, first=True): """ Find where list sub appears in list l. When ``first`` is True the first occurance from the left is returned, else the last occurance is returned. Return None if sub is not in l. >> l = range(5)*2 >> find(l, [2, 3]) 2 >> find(l, [2, 3], first=0) 7 >> find(l, [2, 4]) None """ if not sub or not l or len(sub) > len(l): return None n = len(sub) if not first: l.reverse() sub.reverse() for i in xrange(0, len(l) - n + 1): if all(l[i + j] == sub[j] for j in range(n)): break else: i = None if not first: l.reverse() sub.reverse() if i is not None and not first: i = len(l) - (i + n) return i co = [] try: assert Add.make_args(S.Zero) # replace try/except with this args = Add.make_args(self) except AssertionError: args = Add.make_args(self) or [S.Zero] self_c = self.is_commutative x_c = x.is_commutative if self_c and not x_c: return None if self_c: xargs = set(arglist(x)) for a in Add.make_args(self): margs = set(arglist(a)) if len(xargs) > len(margs): continue resid = margs.difference(xargs) if len(resid) + len(xargs) == len(margs): co.append(Mul(*resid)) if co == []: return None elif co: return Add(*co) elif x_c: xargs = set(arglist(x)) for a in Add.make_args(self): margs, nc = a.args_cnc() if len(xargs) > len(margs): continue resid = margs.difference(xargs) if len(resid) + len(xargs) == len(margs): co.append(Mul(*(list(resid) + nc))) if co == []: return None elif co: return Add(*co) else: # both nc xargs, nx = x.args_cnc() # find the parts that pass the commutative terms for a in Add.make_args(self): margs, nc = a.args_cnc() if len(xargs) > len(margs): continue resid = margs.difference(xargs) if len(resid) + len(xargs) == len(margs): co.append((resid, nc)) # now check the non-comm parts if not co: return None if all(n == co[0][1] for r, n in co): ii = find(co[0][1], nx, right) if not ii is None: if not right: return Mul(Add(*[Mul(*r) for r, c in co]), Mul(*co[0][1][:ii])) else: return Mul(*co[0][1][ii+len(nx):]) beg = reduce(incommon, (n[1] for n in co)) if beg: ii = find(beg, nx, right) if not ii is None: if not right: gcdc = co[0][0] for i in xrange(1, len(co)): gcdc = gcdc.intersection(co[i][0]) if not gcdc: break return Mul(*(list(gcdc) + beg[:ii])) else: m = ii + len(nx) return Add(*[Mul(*(list(r) + n[m:])) for r, n in co]) end = list(reversed(reduce(incommon, (list(reversed(n[1])) for n in co)))) if end: ii = find(end, nx, right) if not ii is None: if not right: return Add(*[Mul(*(list(r) + n[:-len(end)+ii])) for r, n in co]) else: return Mul(*end[ii+len(nx):]) # look for single match hit = None for i, (r, n) in enumerate(co): ii = find(n, nx, right) if not ii is None: if not hit: hit = ii, r, n else: break else: if hit: ii, r, n = hit if not right: return Mul(*(list(r) + n[:ii])) else: return Mul(*n[ii+len(nx):]) return None def as_expr(self, *gens): """ Convert a polynomial to a SymPy expression. **Examples** >>> from sympy import sin >>> from sympy.abc import x, y >>> f = (x**2 + x*y).as_poly(x, y) >>> f.as_expr() x**2 + x*y >>> sin(x).as_expr() sin(x) """ return self def as_coefficient(self, expr): """Extracts symbolic coefficient at the given expression. In other words, this functions separates 'self' into product of 'expr' and 'expr'-free coefficient. If such separation is not possible it will return None. >>> from sympy import E, pi, sin, I, symbols >>> from sympy.abc import x, y >>> E.as_coefficient(E) 1 >>> (2*E).as_coefficient(E) 2 >>> (2*sin(E)*E).as_coefficient(E) >>> (2*E + x*E).as_coefficient(E) x + 2 >>> (2*E*x + x).as_coefficient(E) >>> (E*(x + 1) + x).as_coefficient(E) >>> (2*pi*I).as_coefficient(pi*I) 2 >>> (2*I).as_coefficient(pi*I) """ r = self.extract_multiplicatively(expr) if r and not r.has(expr): return r def as_independent(self, *deps, **hint): """ A mostly naive separation of a Mul or Add into arguments that are not are dependent on deps. To obtain as complete a separation of variables as possible, use a separation method first, e.g.: * separatevars() to change Mul, Add and Pow (including exp) into Mul * .expand(mul=True) to change Add or Mul into Add * .expand(log=True) to change log expr into an Add The only non-naive thing that is done here is to respect noncommutative ordering of variables. The returned tuple (i, d) has the following interpretation: * i will has no variable that appears in deps * d will be 1 or else have terms that contain variables that are in deps * if self is an Add then self = i + d * if self is a Mul then self = i*d * if self is anything else, either tuple (self, S.One) or (S.One, self) is returned. To force the expression to be treated as an Add, use the hint as_Add=True Examples: -- self is an Add >>> from sympy import sin, cos, exp >>> from sympy.abc import x, y, z >>> (x + x*y).as_independent(x) (0, x*y + x) >>> (x + x*y).as_independent(y) (x, x*y) >>> (2*x*sin(x) + y + x + z).as_independent(x) (y + z, 2*x*sin(x) + x) >>> (2*x*sin(x) + y + x + z).as_independent(x, y) (z, 2*x*sin(x) + x + y) -- self is a Mul >>> (x*sin(x)*cos(y)).as_independent(x) (cos(y), x*sin(x)) non-commutative terms cannot always be separated out when self is a Mul >>> from sympy import symbols >>> n1, n2, n3 = symbols('n1 n2 n3', commutative=False) >>> (n1 + n1*n2).as_independent(n2) (n1, n1*n2) >>> (n2*n1 + n1*n2).as_independent(n2) (0, n1*n2 + n2*n1) >>> (n1*n2*n3).as_independent(n1) (1, n1*n2*n3) >>> (n1*n2*n3).as_independent(n2) (n1, n2*n3) >>> ((x-n1)*(x-y)).as_independent(x) (1, (x - y)*(x - n1)) -- self is anything else: >>> (sin(x)).as_independent(x) (1, sin(x)) >>> (sin(x)).as_independent(y) (sin(x), 1) >>> exp(x+y).as_independent(x) (1, exp(x + y)) -- force self to be treated as an Add: >>> (3*x).as_independent(x, as_Add=1) (0, 3*x) -- force self to be treated as a Mul: >>> (3+x).as_independent(x, as_Add=0) (1, x + 3) >>> (-3+x).as_independent(x, as_Add=0) (1, x - 3) Note how the below differs from the above in making the constant on the dep term positive. >>> (y*(-3+x)).as_independent(x) (y, x - 3) Note: when trying to get independent terms, a separation method might need to be used first. In this case, it is important to keep track of what you send to this routine so you know how to interpret the returned values >>> from sympy import separatevars, log >>> separatevars(exp(x+y)).as_independent(x) (exp(y), exp(x)) >>> (x + x*y).as_independent(y) (x, x*y) >>> separatevars(x + x*y).as_independent(y) (x, y + 1) >>> (x*(1 + y)).as_independent(y) (x, y + 1) >>> (x*(1 + y)).expand(mul=True).as_independent(y) (x, x*y) >>> a, b=symbols('a b',positive=True) >>> (log(a*b).expand(log=True)).as_independent(b) (log(a), log(b)) See also: .separatevars(), .expand(log=True), .as_two_terms(), .as_coeff_add(), .as_coeff_mul() """ from sympy.utilities.iterables import sift func = self.func if hint.get('as_Add', func is Add): want = Add else: want = Mul if (want is not func or func is not Add and func is not Mul): if self.has(*deps): return (want.identity, self) else: return (self, want.identity) else: if func is Add: args = list(self.args) else: args, nc = self.args_cnc() d = sift(args, lambda x: x.has(*deps)) depend = d.pop(True, []) indep = d.pop(False, []) if func is Add: # all terms were treated as commutative return (Add(*indep), Add(*depend)) else: # handle noncommutative by stopping at first dependent term for i, n in enumerate(nc): if n.has(*deps): depend.extend(nc[i:]) break indep.append(n) return Mul(*indep), Mul(*depend) def as_real_imag(self, deep=True): """Performs complex expansion on 'self' and returns a tuple containing collected both real and imaginary parts. This method can't be confused with re() and im() functions, which does not perform complex expansion at evaluation. However it is possible to expand both re() and im() functions and get exactly the same results as with a single call to this function. >>> from sympy import symbols, I >>> x, y = symbols('x,y', real=True) >>> (x + y*I).as_real_imag() (x, y) >>> from sympy.abc import z, w >>> (z + w*I).as_real_imag() (-im(w) + re(z), im(z) + re(w)) """ return (C.re(self), C.im(self)) def as_powers_dict(self): return dict([self.as_base_exp()]) def as_base_exp(self): # a -> b ** e return self, S.One def as_coeff_terms(self, *deps): import warnings warnings.warn("\nuse as_coeff_mul() instead of as_coeff_terms().", DeprecationWarning) def as_coeff_factors(self, *deps): import warnings warnings.warn("\nuse as_coeff_add() instead of as_coeff_factors().", DeprecationWarning) def as_coeff_mul(self, *deps): """Return the tuple (c, args) where self is written as a Mul, ``m``. c should be a Rational multiplied by any terms of the Mul that are independent of deps. args should be a tuple of all other terms of m; args is empty if self is a Number or if self is independent of deps (when given). This should be used when you don't know if self is a Mul or not but you want to treat self as a Mul or if you want to process the individual arguments of the tail of self as a Mul. - if you know self is a Mul and want only the head, use self.args[0]; - if you don't want to process the arguments of the tail but need the tail then use self.as_two_terms() which gives the head and tail; - if you want to split self into an independent and dependent parts use self.as_independent(\*deps) >>> from sympy import S >>> from sympy.abc import x, y >>> (S(3)).as_coeff_mul() (3, ()) >>> (3*x*y).as_coeff_mul() (3, (x, y)) >>> (3*x*y).as_coeff_mul(x) (3*y, (x,)) >>> (3*y).as_coeff_mul(x) (3*y, ()) """ if deps: if not self.has(*deps): return self, tuple() return S.One, (self,) def as_coeff_add(self, *deps): """Return the tuple (c, args) where self is written as an Add, ``a``. c should be a Rational added to any terms of the Add that are independent of deps. args should be a tuple of all other terms of ``a``; args is empty if self is a Number or if self is independent of deps (when given). This should be used when you don't know if self is an Add or not but you want to treat self as an Add or if you want to process the individual arguments of the tail of self as an Add. - if you know self is an Add and want only the head, use self.args[0]; - if you don't want to process the arguments of the tail but need the tail then use self.as_two_terms() which gives the head and tail. - if you want to split self into an independent and dependent parts use self.as_independent(\*deps) >>> from sympy import S >>> from sympy.abc import x, y >>> (S(3)).as_coeff_add() (3, ()) >>> (3 + x + y).as_coeff_add() (3, (y, x)) >>> (3 + x +y).as_coeff_add(x) (y + 3, (x,)) >>> (3 + y).as_coeff_add(x) (y + 3, ()) """ if deps: if not self.has(*deps): return self, tuple() return S.Zero, (self,) def as_numer_denom(self): """ a/b -> a,b This is just a stub that should be defined by an object's class methods to get anything else.""" return self, S.One def normal(self): n, d = self.as_numer_denom() if d is S.One: return n return n/d def extract_multiplicatively(self, c): """Return None if it's not possible to make self in the form c * something in a nice way, i.e. preserving the properties of arguments of self. >>> from sympy import symbols, Rational >>> x, y = symbols('x,y', real=True) >>> ((x*y)**3).extract_multiplicatively(x**2 * y) x*y**2 >>> ((x*y)**3).extract_multiplicatively(x**4 * y) >>> (2*x).extract_multiplicatively(2) x >>> (2*x).extract_multiplicatively(3) >>> (Rational(1,2)*x).extract_multiplicatively(3) x/6 """ c = sympify(c) if c is S.One: return self elif c == self: return S.One elif c.is_Mul: x = self.extract_multiplicatively(c.as_two_terms()[0]) if x != None: return x.extract_multiplicatively(c.as_two_terms()[1]) quotient = self / c if self.is_Number: if self is S.Infinity: if c.is_positive: return S.Infinity elif self is S.NegativeInfinity: if c.is_negative: return S.Infinity elif c.is_positive: return S.NegativeInfinity elif self is S.ComplexInfinity: if not c.is_zero: return S.ComplexInfinity elif self is S.NaN: return S.NaN elif self.is_Integer: if not quotient.is_Integer: return None elif self.is_positive and quotient.is_negative: return None else: return quotient elif self.is_Rational: if not quotient.is_Rational: return None elif self.is_positive and quotient.is_negative: return None else: return quotient elif self.is_Float: if not quotient.is_Float: return None elif self.is_positive and quotient.is_negative: return None else: return quotient elif self.is_NumberSymbol or self.is_Symbol or self is S.ImaginaryUnit: if quotient.is_Mul and len(quotient.args) == 2: if quotient.args[0].is_Integer and quotient.args[0].is_positive and quotient.args[1] == self: return quotient elif quotient.is_Integer: return quotient elif self.is_Add: newargs = [] for arg in self.args: newarg = arg.extract_multiplicatively(c) if newarg != None: newargs.append(newarg) else: return None return Add(*newargs) elif self.is_Mul: for i in xrange(len(self.args)): newargs = list(self.args) del(newargs[i]) tmp = self._new_rawargs(*newargs).extract_multiplicatively(c) if tmp != None: return tmp * self.args[i] elif self.is_Pow: if c.is_Pow and c.base == self.base: new_exp = self.exp.extract_additively(c.exp) if new_exp != None: return self.base ** (new_exp) elif c == self.base: new_exp = self.exp.extract_additively(1) if new_exp != None: return self.base ** (new_exp) def extract_additively(self, c): """Return None if it's not possible to make self in the form something + c in a nice way, i.e. preserving the properties of arguments of self. >>> from sympy import symbols >>> x, y = symbols('x,y', real=True) >>> ((x*y)**3).extract_additively(1) >>> (x+1).extract_additively(x) 1 >>> (x+1).extract_additively(2*x) >>> (x+1).extract_additively(-x) 2*x + 1 >>> (-x+1).extract_additively(2*x) -3*x + 1 """ c = sympify(c) if c is S.Zero: return self elif c == self: return S.Zero elif self is S.Zero: return None elif c.is_Add: x = self.extract_additively(c.as_two_terms()[0]) if x != None: return x.extract_additively(c.as_two_terms()[1]) sub = self - c if self.is_Number: if self.is_Integer: if not sub.is_Integer: return None elif self.is_positive and sub.is_negative: return None else: return sub elif self.is_Rational: if not sub.is_Rational: return None elif self.is_positive and sub.is_negative: return None else: return sub elif self.is_Float: if not sub.is_Float: return None elif self.is_positive and sub.is_negative: return None else: return sub elif self.is_NumberSymbol or self.is_Symbol or self is S.ImaginaryUnit: if sub.is_Mul and len(sub.args) == 2: if sub.args[0].is_Integer and sub.args[0].is_positive and sub.args[1] == self: return sub elif sub.is_Integer: return sub elif self.is_Add: terms = self.as_two_terms() subs0 = terms[0].extract_additively(c) if subs0 != None: return subs0 + terms[1] else: subs1 = terms[1].extract_additively(c) if subs1 != None: return subs1 + terms[0] elif self.is_Mul: self_coeff, self_terms = self.as_coeff_mul() if c.is_Mul: c_coeff, c_terms = c.as_coeff_mul() if c_terms == self_terms: new_coeff = self_coeff.extract_additively(c_coeff) if new_coeff != None: return new_coeff * c._new_rawargs(*c_terms) elif c == self_terms: new_coeff = self_coeff.extract_additively(1) if new_coeff != None: return new_coeff * c def could_extract_minus_sign(self): """Canonical way to choose an element in the set {e, -e} where e is any expression. If the canonical element is e, we have e.could_extract_minus_sign() == True, else e.could_extract_minus_sign() == False. For any expression, the set ``{e.could_extract_minus_sign(), (-e).could_extract_minus_sign()}`` must be ``{True, False}``. >>> from sympy.abc import x, y >>> (x-y).could_extract_minus_sign() != (y-x).could_extract_minus_sign() True """ negative_self = -self self_has_minus = (self.extract_multiplicatively(-1) != None) negative_self_has_minus = ((negative_self).extract_multiplicatively(-1) != None) if self_has_minus != negative_self_has_minus: return self_has_minus else: if self.is_Add: # We choose the one with less arguments with minus signs all_args = len(self.args) negative_args = len([False for arg in self.args if arg.could_extract_minus_sign()]) positive_args = all_args - negative_args if positive_args > negative_args: return False elif positive_args < negative_args: return True elif self.is_Mul: # We choose the one with an odd number of minus signs num, den = self.as_numer_denom() args = Mul.make_args(num) + Mul.make_args(den) arg_signs = [arg.could_extract_minus_sign() for arg in args] negative_args = filter(None, arg_signs) return len(negative_args) % 2 == 1 # As a last resort, we choose the one with greater hash return hash(self) < hash(negative_self) def _eval_is_polynomial(self, syms): if self.free_symbols.intersection(syms) == set([]): return True return False def is_polynomial(self, *syms): """ Return True if self is a polynomial in syms and False otherwise. This checks if self is an exact polynomial in syms. This function returns False for expressions that are "polynomials" with symbolic exponents. Thus, you should be able to apply polynomial algorithms to expressions for which this returns True, and Poly(expr, \*syms) should work only if and only if expr.is_polynomial(\*syms) returns True. The polynomial does not have to be in expanded form. If no symbols are given, all free symbols in the expression will be used. This is not part of the assumptions system. You cannot do Symbol('z', polynomial=True). **Examples** >>> from sympy import Symbol >>> x = Symbol('x') >>> ((x**2 + 1)**4).is_polynomial(x) True >>> ((x**2 + 1)**4).is_polynomial() True >>> (2**x + 1).is_polynomial(x) False >>> n = Symbol('n', nonnegative=True, integer=True) >>> (x**n + 1).is_polynomial(x) False This function does not attempt any nontrivial simplifications that may result in an expression that does not appear to be a polynomial to become one. >>> from sympy import sqrt, factor, cancel >>> y = Symbol('y', positive=True) >>> a = sqrt(y**2 + 2*y + 1) >>> a.is_polynomial(y) False >>> factor(a) y + 1 >>> factor(a).is_polynomial(y) True >>> b = (y**2 + 2*y + 1)/(y + 1) >>> b.is_polynomial(y) False >>> cancel(b) y + 1 >>> cancel(b).is_polynomial(y) True See also .is_rational_function() """ if syms: syms = set(map(sympify, syms)) else: syms = self.free_symbols if syms.intersection(self.free_symbols) == set([]): # constant polynomial return True else: return self._eval_is_polynomial(syms) def _eval_is_rational_function(self, syms): if self.free_symbols.intersection(syms) == set([]): return True return False def is_rational_function(self, *syms): """ Test whether function is a ratio of two polynomials in the given symbols, syms. When syms is not given, all free symbols will be used. The rational function does not have to be in expanded or in any kind of canonical form. This function returns False for expressions that are "rational functions" with symbolic exponents. Thus, you should be able to call .as_numer_denom() and apply polynomial algorithms to the result for expressions for which this returns True. This is not part of the assumptions system. You cannot do Symbol('z', rational_function=True). Example: >>> from sympy import Symbol, sin >>> from sympy.abc import x, y >>> (x/y).is_rational_function() True >>> (x**2).is_rational_function() True >>> (x/sin(y)).is_rational_function(y) False >>> n = Symbol('n', integer=True) >>> (x**n + 1).is_rational_function(x) False This function does not attempt any nontrivial simplifications that may result in an expression that does not appear to be a rational function to become one. >>> from sympy import sqrt, factor, cancel >>> y = Symbol('y', positive=True) >>> a = sqrt(y**2 + 2*y + 1)/y >>> a.is_rational_function(y) False >>> factor(a) (y + 1)/y >>> factor(a).is_rational_function(y) True See also is_rational_function(). """ if syms: syms = set(map(sympify, syms)) else: syms = self.free_symbols if syms.intersection(self.free_symbols) == set([]): # constant rational function return True else: return self._eval_is_rational_function(syms) ################################################################################### ##################### SERIES, LEADING TERM, LIMIT, ORDER METHODS ################## ################################################################################### def series(self, x=None, x0=0, n=6, dir="+"): """ Series expansion of "self" around ``x = x0`` yielding either terms of the series one by one (the lazy series given when n=None), else all the terms at once when n != None. Note: when n != None, if an O() term is returned then the x in the in it and the entire expression represents x - x0, the displacement from x0. (If there is no O() term then the series was exact and x has it's normal meaning.) This is currently necessary since sympy's O() can only represent terms at x0=0. So instead of:: cos(x).series(x0=1, n=2) --> (1 - x)*sin(1) + cos(1) + O((x - 1)**2) which graphically looks like this:: | .|. . . . | \ . . ---+---------------------- | . . . . | \ x=0 the following is returned instead:: -x*sin(1) + cos(1) + O(x**2) whose graph is this:: \ | . .| . . . \ . . -----+\------------------. | . . . . | \ x=0 which is identical to ``cos(x + 1).series(n=2)``. Usage: Returns the series expansion of "self" around the point ``x = x0`` with respect to ``x`` up to O(x**n) (default n is 6). If ``x=None`` and ``self`` is univariate, the univariate symbol will be supplied, otherwise an error will be raised. >>> from sympy import cos, exp >>> from sympy.abc import x, y >>> cos(x).series() 1 - x**2/2 + x**4/24 + O(x**6) >>> cos(x).series(n=4) 1 - x**2/2 + O(x**4) >>> e = cos(x + exp(y)) >>> e.series(y, n=2) cos(x + 1) - y*sin(x + 1) + O(y**2) >>> e.series(x, n=2) cos(exp(y)) - x*sin(exp(y)) + O(x**2) If ``n=None`` then an iterator of the series terms will be returned. >>> term=cos(x).series(n=None) >>> [term.next() for i in range(2)] [1, -x**2/2] For ``dir=+`` (default) the series is calculated from the right and for ``dir=-`` the series from the left. For smooth functions this flag will not alter the results. >>> abs(x).series(dir="+") x >>> abs(x).series(dir="-") -x """ if x is None: syms = self.atoms(C.Symbol) if len(syms) > 1: raise ValueError('x must be given for multivariate functions.') x = syms.pop() if not self.has(x): if n is None: return (s for s in [self]) else: return self ## it seems like the following should be doable, but several failures ## then occur. Is this related to issue 1747 et al See also XPOS below. #if x.is_positive is x.is_negative is None: # # replace x with an x that has a positive assumption # xpos = C.Dummy('x', positive=True) # rv = self.subs(x, xpos).series(xpos, x0, n, dir) # if n is None: # return (s.subs(xpos, x) for s in rv) # else: # return rv.subs(xpos, x) if len(dir) != 1 or dir not in '+-': raise ValueError("Dir must be '+' or '-'") if x0 in [S.Infinity, S.NegativeInfinity]: dir = {S.Infinity: '+', S.NegativeInfinity: '-'}[x0] s = self.subs(x, 1/x).series(x, n=n, dir=dir) if n is None: return (si.subs(x, 1/x) for si in s) # don't include the order term since it will eat the larger terms return s.removeO().subs(x, 1/x) # use rep to shift origin to x0 and change sign (if dir is negative) # and undo the process with rep2 if x0 or dir == '-': if dir == '-': rep = -x + x0 rep2 = -x rep2b = x0 else: rep = x + x0 rep2 = x rep2b = -x0 s = self.subs(x, rep).series(x, x0=0, n=n, dir='+') if n is None: # lseries... return (si.subs(x, rep2 + rep2b) for si in s) # nseries... o = s.getO() or S.Zero s = s.removeO() if o and x0: rep2b = 0 # when O() can handle x0 != 0 this can be removed return s.subs(x, rep2 + rep2b) + o # from here on it's x0=0 and dir='+' handling if n != None: # nseries handling s1 = self._eval_nseries(x, n=n, logx=None) o = s1.getO() or S.Zero if o: # make sure the requested order is returned ngot = o.getn() if ngot > n: # leave o in its current form (e.g. with x*log(x)) so # it eats terms properly, then replace it below s1 += o.subs(x, x**C.Rational(n, ngot)) elif ngot < n: # increase the requested number of terms to get the desired # number keep increasing (up to 9) until the received order # is different than the original order and then predict how # many additional terms are needed for more in range(1, 9): s1 = self._eval_nseries(x, n=n + more, logx=None) newn = s1.getn() if newn != ngot: ndo = n + (n - ngot)*more/(newn - ngot) s1 = self._eval_nseries(x, n=ndo, logx=None) # if this assertion fails then our ndo calculation # needs modification assert s1.getn() == n break else: raise ValueError('Could not calculate %s terms for %s' % (str(n), self)) o = s1.getO() s1 = s1.removeO() else: o = C.Order(x**n) if (s1 + o).removeO() == s1: o = S.Zero return s1 + o else: # lseries handling def yield_lseries(s): """Return terms of lseries one at a time.""" for si in s: if not si.is_Add: yield si continue # yield terms 1 at a time if possible # by increasing order until all the # terms have been returned yielded = 0 o = C.Order(si)*x ndid = 0 ndo = len(si.args) while 1: do = (si - yielded + o).removeO() o *= x if not do or do.is_Order: continue if do.is_Add: ndid += len(do.args) else: ndid += 1 yield do if ndid == ndo: raise StopIteration yielded += do return yield_lseries(self.removeO()._eval_lseries(x)) def lseries(self, x=None, x0=0, dir='+'): """ Wrapper for series yielding an iterator of the terms of the series. Note: an infinite series will yield an infinite iterator. The following, for exaxmple, will never terminate. It will just keep printing terms of the sin(x) series:: for term in sin(x).lseries(x): print term The advantage of lseries() over nseries() is that many times you are just interested in the next term in the series (i.e. the first term for example), but you don't know how many you should ask for in nseries() using the "n" parameter. See also nseries(). """ return self.series(x, x0, n=None, dir=dir) def _eval_lseries(self, x): # default implementation of lseries is using nseries(), and adaptively # increasing the "n". As you can see, it is not very efficient, because # we are calculating the series over and over again. Subclasses should # override this method and implement much more efficient yielding of # terms. n = 0 series = self._eval_nseries(x, n=n, logx=None) if not series.is_Order: if series.is_Add: yield series.removeO() else: yield series raise StopIteration while series.is_Order: n += 1 series = self._eval_nseries(x, n=n, logx=None) e = series.removeO() yield e while 1: while 1: n += 1 series = self._eval_nseries(x, n=n, logx=None).removeO() if e != series: break yield series - e e = series def nseries(self, x=None, x0=0, n=6, dir='+',logx=None): """ Wrapper to _eval_nseries if assumptions allow, else to series. If x is given, x0 is 0, dir='+', and self has x, then _eval_nseries is called. This calculates "n" terms in the innermost expressions and then builds up the final series just by "cross-multiplying" everything out. Advantage -- it's fast, because we don't have to determine how many terms we need to calculate in advance. Disadvantage -- you may end up with less terms than you may have expected, but the O(x**n) term appended will always be correct and so the result, though perhaps shorter, will also be correct. If any of those assumptions is not met, this is treated like a wrapper to series which will try harder to return the correct number of terms. See also lseries(). """ if x and not self.has(x): return self if x is None or x0 or dir != '+':#{see XPOS above} or (x.is_positive == x.is_negative == None): assert logx == None return self.series(x, x0, n, dir) else: return self._eval_nseries(x, n=n, logx=logx) def _eval_nseries(self, x, n, logx): """ Return terms of series for self up to O(x**n) at x=0 from the positive direction. This is a method that should be overridden in subclasses. Users should never call this method directly (use .nseries() instead), so you don't have to write docstrings for _eval_nseries(). """ raise NotImplementedError("(%s).nseries(%s, %s, %s)" % (self, x, x0, n)) def limit(self, x, xlim, dir='+'): """ Compute limit x->xlim. """ from sympy.series.limits import limit return limit(self, x, xlim, dir) def compute_leading_term(self, x, skip_abs=False, logx=None): """ as_leading_term is only allowed for results of .series() This is a wrapper to compute a series first. If skip_abs is true, the absolute term is assumed to be zero. (This is necessary because sometimes it cannot be simplified to zero without a lot of work, but is still known to be zero. See log._eval_nseries for an example.) If skip_log is true, log(x) is treated as an independent symbol. (This is needed for the gruntz algorithm.) """ from sympy.series.gruntz import calculate_series from sympy import cancel, expand_mul if self.removeO() == 0: return self if logx is None: d = C.Dummy('logx') s = calculate_series(self, x, skip_abs, d).subs(d, C.log(x)) else: s = calculate_series(self, x, skip_abs, logx) s = cancel(s) if skip_abs: s = expand_mul(s).as_independent(x)[1] return s.as_leading_term(x) @cacheit def as_leading_term(self, *symbols): """ Returns the leading term. Example: >>> from sympy.abc import x >>> (1+x+x**2).as_leading_term(x) 1 >>> (1/x**2+x+x**2).as_leading_term(x) x**(-2) Note: self is assumed to be the result returned by Basic.series(). """ from sympy import powsimp if len(symbols)>1: c = self for x in symbols: c = c.as_leading_term(x) return c elif not symbols: return self x = sympify(symbols[0]) assert x.is_Symbol, repr(x) if not self.has(x): return self obj = self._eval_as_leading_term(x) if obj is not None: return powsimp(obj, deep=True, combine='exp') raise NotImplementedError('as_leading_term(%s, %s)' % (self, x)) def _eval_as_leading_term(self, x): return self def as_coeff_exponent(self, x): """ c*x**e -> c,e where x can be any symbolic expression. """ x = sympify(x) wc = Wild('wc') we = Wild('we') p = wc*x**we from sympy import collect s = collect(self, x) d = s.match(p) if d is not None and we in d: return d[wc], d[we] return s, S.Zero def leadterm(self, x): """ Returns the leading term a*x**b as a tuple (a, b). Example: >>> from sympy.abc import x >>> (1+x+x**2).leadterm(x) (1, 0) >>> (1/x**2+x+x**2).leadterm(x) (1, -2) Note: self is assumed to be the result returned by Basic.series(). """ from sympy import powsimp x = sympify(x) c, e = self.as_leading_term(x).as_coeff_exponent(x) c = powsimp(c, deep=True, combine='exp') if not c.has(x): return c, e raise ValueError("cannot compute leadterm(%s, %s), got c=%s" % (self, x, c)) def as_coeff_Mul(self): """Efficiently extract the coefficient of a product. """ return S.One, self ################################################################################### ##################### DERIVATIVE, INTEGRAL, FUNCTIONAL METHODS #################### ################################################################################### def diff(self, *symbols, **assumptions): new_symbols = map(sympify, symbols) # e.g. x, 2, y, z assumptions.setdefault("evaluate", True) return Derivative(self, *new_symbols, **assumptions) ########################################################################### ###################### EXPRESSION EXPANSION METHODS ####################### ########################################################################### # These should be overridden in subclasses def _eval_expand_basic(self, deep=True, **hints): return self def _eval_expand_power_exp(self, deep=True, **hints): return self def _eval_expand_power_base(self, deep=True, **hints): return self def _eval_expand_mul(self, deep=True, **hints): return self def _eval_expand_multinomial(self, deep=True, **hints): return self def _eval_expand_log(self, deep=True, **hints): return self def _eval_expand_complex(self, deep=True, **hints): return self def _eval_expand_trig(self, deep=True, **hints): return self def _eval_expand_func(self, deep=True, **hints): return self def expand(self, deep=True, modulus=None, power_base=True, power_exp=True, \ mul=True, log=True, multinomial=True, basic=True, **hints): """ Expand an expression using hints. See the docstring in function.expand for more information. """ hints.update(power_base=power_base, power_exp=power_exp, mul=mul, \ log=log, multinomial=multinomial, basic=basic) expr = self for hint, use_hint in hints.iteritems(): if use_hint: func = getattr(expr, '_eval_expand_'+hint, None) if func is not None: expr = func(deep=deep, **hints) if modulus is not None: modulus = sympify(modulus) if not modulus.is_Integer or modulus <= 0: raise ValueError("modulus must be a positive integer, got %s" % modulus) terms = [] for term in Add.make_args(expr): coeff, tail = term.as_coeff_mul() coeff %= modulus if coeff: terms.append(Mul(*((coeff,) + tail))) expr = Add(*terms) return expr ########################################################################### ################### GLOBAL ACTION VERB WRAPPER METHODS #################### ########################################################################### def integrate(self, *args, **kwargs): """See the integrate function in sympy.integrals""" from sympy.integrals import integrate return integrate(self, *args, **kwargs) def simplify(self): """See the simplify function in sympy.simplify""" from sympy.simplify import simplify return simplify(self) def nsimplify(self, constants=[], tolerance=None, full=False): """See the nsimplify function in sympy.simplify""" from sympy.simplify import nsimplify return nsimplify(self, constants, tolerance, full) def separate(self, deep=False, force=False): """See the separate function in sympy.simplify""" from sympy.simplify import separate return separate(self, deep) def collect(self, syms, evaluate=True, exact=False): """See the collect function in sympy.simplify""" from sympy.simplify import collect return collect(self, syms, evaluate, exact) def together(self, *args, **kwargs): """See the together function in sympy.polys""" from sympy.polys import together return together(self, *args, **kwargs) def apart(self, x=None, **args): """See the apart function in sympy.polys""" from sympy.polys import apart return apart(self, x, **args) def ratsimp(self): """See the ratsimp function in sympy.simplify""" from sympy.simplify import ratsimp return ratsimp(self) def trigsimp(self, deep=False, recursive=False): """See the trigsimp function in sympy.simplify""" from sympy.simplify import trigsimp return trigsimp(self, deep, recursive) def radsimp(self): """See the radsimp function in sympy.simplify""" from sympy.simplify import radsimp return radsimp(self) def powsimp(self, deep=False, combine='all'): """See the powsimp function in sympy.simplify""" from sympy.simplify import powsimp return powsimp(self, deep, combine) def combsimp(self): """See the combsimp function in sympy.simplify""" from sympy.simplify import combsimp return combsimp(self) def factor(self, *gens, **args): """See the factor() function in sympy.polys.polytools""" from sympy.polys import factor return factor(self, *gens, **args) def refine(self, assumption=True): """See the refine function in sympy.assumptions""" from sympy.assumptions import refine return refine(self, assumption) def cancel(self, *gens, **args): """See the cancel function in sympy.polys""" from sympy.polys import cancel return cancel(self, *gens, **args) def invert(self, g): """See the invert function in sympy.polys""" from sympy.polys import invert return invert(self, g) class AtomicExpr(Atom, Expr): """ A parent class for object which are both atoms and Exprs. Examples: Symbol, Number, Rational, Integer, ... But not: Add, Mul, Pow, ... """ is_Atom = True __slots__ = [] def _eval_derivative(self, s): if self == s: return S.One return S.Zero def as_numer_denom(self): return self, S.One def _eval_is_polynomial(self, syms): return True def _eval_is_rational_function(self, syms): return True def _eval_nseries(self, x, n, logx): return self from mul import Mul from add import Add from power import Pow from function import Derivative from sympify import sympify from symbol import Wild wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/sets.py0000644000175000017500000010213212014170666023356 0ustar georgeskgeorgeskfrom basic import Basic from singleton import Singleton, S from evalf import EvalfMixin from numbers import Float, Integer from sympify import _sympify, sympify, SympifyError from sympy.mpmath import mpi, mpf from containers import Tuple class Set(Basic): """ Represents any kind of set. Real intervals are represented by the Interval class and unions of sets by the Union class. The empty set is represented by the EmptySet class and available as a singleton as S.EmptySet. """ def union(self, other): """ Returns the union of 'self' and 'other'. As a shortcut it is possible to use the '+' operator: >>> from sympy import Interval, FiniteSet >>> Interval(0, 1).union(Interval(2, 3)) [0, 1] U [2, 3] >>> Interval(0, 1) + Interval(2, 3) [0, 1] U [2, 3] >>> Interval(1, 2, True, True) + FiniteSet(2, 3) (1, 2] U {3} Similarly it is possible to use the '-' operator for set differences: >>> Interval(0, 2) - Interval(0, 1) (1, 2] >>> Interval(1, 3) - FiniteSet(2) [1, 2) U (2, 3] """ return Union(self, other) def intersect(self, other): """ Returns the intersection of 'self' and 'other'. >>> from sympy import Interval >>> Interval(1, 3).intersect(Interval(1, 2)) [1, 2] """ return self._intersect(other) def _intersect(self, other): raise NotImplementedError("(%s)._intersect(%s)" % (self, other)) @property def complement(self): """ The complement of 'self'. As a shortcut it is possible to use the '~' or '-' operators: >>> from sympy import Interval >>> Interval(0, 1).complement (-oo, 0) U (1, oo) >>> ~Interval(0, 1) (-oo, 0) U (1, oo) >>> -Interval(0, 1) (-oo, 0) U (1, oo) """ return self._complement @property def _complement(self): raise NotImplementedError("(%s)._complement" % self) @property def inf(self): """ The infimum of 'self'. >>> from sympy import Interval, Union >>> Interval(0, 1).inf 0 >>> Union(Interval(0, 1), Interval(2, 3)).inf 0 """ return self._inf @property def _inf(self): raise NotImplementedError("(%s)._inf" % self) @property def sup(self): """ The supremum of 'self'. >>> from sympy import Interval, Union >>> Interval(0, 1).sup 1 >>> Union(Interval(0, 1), Interval(2, 3)).sup 3 """ return self._sup @property def _sup(self): raise NotImplementedError("(%s)._sup" % self) def contains(self, other): """ Returns True if 'other' is contained in 'self' as an element. As a shortcut it is possible to use the 'in' operator: >>> from sympy import Interval >>> Interval(0, 1).contains(0.5) True >>> 0.5 in Interval(0, 1) True """ return self._contains(other) def _contains(self, other): raise NotImplementedError("(%s)._contains(%s)" % (self, other)) def subset(self, other): """ Returns True if 'other' is a subset of 'self'. >>> from sympy import Interval >>> Interval(0, 1).contains(0) True >>> Interval(0, 1, left_open=True).contains(0) False """ if isinstance(other, Set): return self.intersect(other) == other else: raise ValueError("Unknown argument '%s'" % other) @property def measure(self): """ The (Lebesgue) measure of 'self'. >>> from sympy import Interval, Union >>> Interval(0, 1).measure 1 >>> Union(Interval(0, 1), Interval(2, 3)).measure 2 """ return self._measure @property def _measure(self): raise NotImplementedError("(%s)._measure" % self) def __add__(self, other): return self.union(other) def __or__(self, other): return self.union(other) def __and__(self, other): return self.intersect(other) def __mul__(self, other): return ProductSet(self, other) def __pow__(self, exp): if not sympify(exp).is_Integer and exp>=0: raise ValueError("%s: Exponent must be a positive Integer"%exp) return ProductSet([self]*exp) def __sub__(self, other): return self.intersect(other.complement) def __neg__(self): return self.complement def __invert__(self): return self.complement def __contains__(self, other): result = self.contains(other) if not isinstance(result, bool): raise TypeError('contains did not evaluate to a bool: %r' % result) return result def _eval_subs(self, old, new): if self == old: return new new_args = [] for arg in self.args: if arg == old: new_args.append(new) elif isinstance(arg, Basic): new_args.append(arg._eval_subs(old, new)) else: new_args.append(arg) return self.__class__(*new_args) @property def is_number(self): return False @property def is_real(self): return False @property def is_iterable(self): return False @property def is_interval(self): return False @property def is_FiniteSet(self): return False @property def is_Interval(self): return False @property def is_ProductSet(self): return False @property def is_Union(self): return False class RealSet(Set, EvalfMixin): """ A set of real values """ @property def is_real(self): return True class ProductSet(Set): """ Represents a Cartesian Product of Sets. Usage: Returns a cartesian product given several sets as either an iterable or individual arguments. Can use '*' operator on any sets for convenient shorthand. Examples: >>> from sympy import Interval, FiniteSet, ProductSet >>> I = Interval(0, 5); S = FiniteSet(1, 2, 3) >>> ProductSet(I, S) [0, 5] x {1, 2, 3} >>> (2, 2) in ProductSet(I, S) True >>> Interval(0, 1) * Interval(0, 1) # The unit square [0, 1] x [0, 1] >>> coin = FiniteSet('H','T') >>> for pair in coin**2: print pair (H, H) (H, T) (T, H) (T, T) Notes: - Passes most operations down to the argument sets - Flattens Products of ProductSets """ def __new__(cls, *sets, **assumptions): def flatten(arg): if isinstance(arg, Set): if arg.is_ProductSet: return sum(map(flatten, arg.args), []) else: return [arg] elif is_flattenable(arg): return sum(map(flatten, arg), []) raise TypeError("Input must be Sets or iterables of Sets") sets = flatten(list(sets)) if EmptySet() in sets or len(sets)==0: return EmptySet() return Basic.__new__(cls, *sets, **assumptions) def _contains(self, element): """ in operator for ProductSets >>> from sympy import Interval >>> (2, 3) in Interval(0, 5) * Interval(0, 5) True >>> (10, 10) in Interval(0, 5) * Interval(0, 5) False Passes operation on to constitent sets """ if len(element) is not len(self.args): return False from sympy.logic.boolalg import And return And(*[set.contains(item) for set,item in zip(self.sets,element)]) def _intersect(self, other): if other.is_Union: return Union(self.intersect(set) for set in other.args) if not other.is_ProductSet: raise TypeError("%s is not a Product Set."%str(other)) if len(other.args) != len(self.args): raise ValueError("Sets not the same size Left: %d, Right: %d" %(len(self.args), len(other.args))) return ProductSet(a.intersect(b) for a, b in zip(self.sets, other.sets)) @property def sets(self): return self.args @property def _complement(self): # For each set consider it or it's complement # We need at least one of the sets to be complemented # Consider all 2^n combinations. # We can conveniently represent these options easily using a ProductSet switch_sets = ProductSet(FiniteSet(set, set.complement) for set in self.sets) product_sets = (ProductSet(*set) for set in switch_sets) # Union of all combinations but this one return Union(p for p in product_sets if p != self) @property def is_real(self): return all(set.is_real for set in self.sets) @property def is_iterable(self): return all(set.is_iterable for set in self.sets) def __iter__(self): if self.is_iterable: from sympy.core.compatibility import product return product(*self.sets) else: raise TypeError("Not all constituent sets are iterable") @property def _measure(self): measure = 1 for set in self.sets: measure *= set.measure return measure @property def is_ProductSet(self): return True class RealSet(Set, EvalfMixin): """ A set of real values """ @property def is_real(self): return True class CountableSet(Set): """ Represents a set of countable numbers such as {1, 2, 3, 4} or {1, 2, 3, ...} """ @property def _measure(self): return 0 @property def is_iterable(self): return True def __iter__(self): raise NotImplementedError("Iteration not yet implemented") class Interval(RealSet): """ Represents a real interval as a Set. Usage: Returns an interval with end points "start" and "end". For left_open=True (default left_open is False) the interval will be open on the left. Similarly, for right_open=True the interval will be open on the right. Examples: >>> from sympy import Symbol, Interval, sets >>> Interval(0, 1) [0, 1] >>> Interval(0, 1, False, True) [0, 1) >>> a = Symbol('a', real=True) >>> Interval(0, a) [0, a] Notes: - Only real end points are supported - Interval(a, b) with a > b will return the empty set - Use the evalf() method to turn an Interval into an mpmath 'mpi' interval instance """ def __new__(cls, start, end, left_open=False, right_open=False): start = _sympify(start) end = _sympify(end) # Only allow real intervals (use symbols with 'is_real=True'). if not start.is_real or not end.is_real: raise ValueError("Only real intervals are supported") # Make sure that the created interval will be valid. if end.is_comparable and start.is_comparable: if end < start: return S.EmptySet if end == start and (left_open or right_open): return S.EmptySet if end == start and not (left_open or right_open): return FiniteSet(end) # Make sure infinite interval end points are open. if start == S.NegativeInfinity: left_open = True if end == S.Infinity: right_open = True return Basic.__new__(cls, start, end, left_open, right_open) @property def start(self): """ The left end point of 'self'. This property takes the same value as the 'inf' property. >>> from sympy import Interval >>> Interval(0, 1).start 0 """ return self._args[0] _inf = left = start @property def end(self): """ The right end point of 'self'. This property takes the same value as the 'sup' property. >>> from sympy import Interval >>> Interval(0, 1).end 1 """ return self._args[1] _sup = right = end @property def left_open(self): """ True if 'self' is left-open. >>> from sympy import Interval >>> Interval(0, 1, left_open=True).left_open True >>> Interval(0, 1, left_open=False).left_open False """ return self._args[2] @property def right_open(self): """ True if 'self' is right-open. >>> from sympy import Interval >>> Interval(0, 1, right_open=True).right_open True >>> Interval(0, 1, right_open=False).right_open False """ return self._args[3] def _intersect(self, other): if not isinstance(other, Interval): return other.intersect(self) if not self._is_comparable(other): raise NotImplementedError("Intersection of intervals with symbolic " "end points is not yet implemented") empty = False if self.start <= other.end and other.start <= self.end: # Get topology right. if self.start < other.start: start = other.start left_open = other.left_open elif self.start > other.start: start = self.start left_open = self.left_open else: start = self.start left_open = self.left_open or other.left_open if self.end < other.end: end = self.end right_open = self.right_open elif self.end > other.end: end = other.end right_open = other.right_open else: end = self.end right_open = self.right_open or other.right_open if end - start == 0 and (left_open or right_open): empty = True else: empty = True if empty: return S.EmptySet return self.__class__(start, end, left_open, right_open) @property def _complement(self): a = Interval(S.NegativeInfinity, self.start, True, not self.left_open) b = Interval(self.end, S.Infinity, not self.right_open, True) return Union(a, b) def _contains(self, other): # We use the logic module here so that this method is meaningful # when used with symbolic end points. from sympy.logic.boolalg import And try: other = _sympify(other) except SympifyError: return False if self.left_open: expr = other > self.start else: expr = other >= self.start if self.right_open: expr = And(expr, other < self.end) else: expr = And(expr, other <= self.end) return expr @property def _measure(self): return self.end - self.start def to_mpi(self, prec=53): return mpi(mpf(self.start.evalf(prec)), mpf(self.end.evalf(prec))) def _eval_evalf(self, prec): return Interval(self.left.evalf(), self.right.evalf(), left_open=self.left_open, right_open=self.right_open) def _is_comparable(self, other): is_comparable = self.start.is_comparable is_comparable &= self.end.is_comparable is_comparable &= other.start.is_comparable is_comparable &= other.end.is_comparable return is_comparable @property def is_Interval(self): return True @property def is_left_unbounded(self): """Return ``True`` if the left endpoint is negative infinity. """ return self.left is S.NegativeInfinity or self.left == Float("-inf") @property def is_right_unbounded(self): """Return ``True`` if the right endpoint is positive infinity. """ return self.right is S.Infinity or self.right == Float("+inf") def as_relational(self, symbol): """Rewrite an interval in terms of inequalities and logic operators. """ from sympy.core.relational import Eq, Lt, Le from sympy.logic.boolalg import And if not self.is_left_unbounded: if self.left_open: left = Lt(self.start, symbol) else: left = Le(self.start, symbol) if not self.is_right_unbounded: if self.right_open: right = Lt(symbol, self.right) else: right = Le(symbol, self.right) if self.is_left_unbounded and self.is_right_unbounded: return True # XXX: Contained(symbol, Floats) elif self.is_left_unbounded: return right elif self.is_right_unbounded: return left else: return And(left, right) class Union(Set): """ Represents a union of sets as a Set. Examples: >>> from sympy import Union, Interval >>> Union(Interval(1, 2), Interval(3, 4)) [1, 2] U [3, 4] The Union constructor will always try to merge overlapping intervals, if possible. For example: >>> Union(Interval(1, 2), Interval(2, 3)) [1, 3] """ def __new__(cls, *args): # Flatten out Iterators and Unions to form one list of sets args = list(args) def flatten(arg): if arg == S.EmptySet: return [] if isinstance(arg, Set): if arg.is_Union: return sum(map(flatten, arg.args), []) else: return [arg] if is_flattenable(arg): # and not isinstance(arg, Set) (implicit) return sum(map(flatten, arg), []) raise TypeError("Input must be Sets or iterables of Sets") args = flatten(args) if len(args) == 0: return S.EmptySet # Only real parts? Return a RealUnion if all(arg.is_real for arg in args): return RealUnion(args) # Lets find and merge real elements if we have them # Separate into finite, real and other sets finite_set = sum([s for s in args if s.is_FiniteSet], S.EmptySet) real_sets = [s for s in args if s.is_real] other_sets = [s for s in args if not s.is_FiniteSet and not s.is_real] # Separate finite_set into real and other part real_finite = RealFiniteSet(i for i in finite_set if i.is_real) other_finite = FiniteSet(i for i in finite_set if not i.is_real) # Merge real part of set real_union = RealUnion(real_sets+[real_finite]) if not real_union: # Real part was empty sets = other_sets + [other_finite] elif real_union.is_FiniteSet: # Real part was just a FiniteSet sets = other_sets + [real_union+other_finite] elif real_union.is_Interval: # Real part was just an Interval sets = [real_union] + other_sets + [other_finite] # If is_RealUnion then separate elif real_union.is_Union and real_union.is_real: intervals = [s for s in real_union.args if s.is_Interval] finite_set = sum([s for s in real_union.args if s.is_FiniteSet] + [other_finite], S.EmptySet) # Join FiniteSet back together sets = intervals + [finite_set] + other_sets # Clear out Empty Sets sets = [set for set in sets if set != S.EmptySet] # If a single set is left over, don't create a new Union object but # rather return the single set. if len(sets) == 1: return sets[0] return Basic.__new__(cls, *sets) @property def _inf(self): # We use Min so that sup is meaningful in combination with symbolic # interval end points. from sympy.functions.elementary.miscellaneous import Min return Min(*[set.inf for set in self.args]) @property def _sup(self): # We use Max so that sup is meaningful in combination with symbolic # end points. from sympy.functions.elementary.miscellaneous import Max return Max(*[set.sup for set in self.args]) def _intersect(self, other): # Distributivity. if other.is_Interval: intersections = [] for interval in self.args: intersections.append(interval.intersect(other)) return self.__class__(*intersections) if other.is_FiniteSet: return other._intersect(self) elif other.is_Union: intersections = [] for s in other.args: intersections.append(self.intersect(s)) return self.__class__(*intersections) else: return other.intersect(self) @property def _complement(self): # De Morgan's formula. complement = self.args[0].complement for set in self.args[1:]: complement = complement.intersect(set.complement) return complement def _contains(self, other): from sympy.logic.boolalg import Or or_args = [the_set.contains(other) for the_set in self.args] return Or(*or_args) @property def _measure(self): # Measure of a union is the sum of the measures of the sets minus # the sum of their pairwise intersections plus the sum of their # triple-wise intersections minus ... etc... # Sets is a collection of intersections and a set of elementary # sets which made up those interections (called "sos" for set of sets) # An example element might of this list might be: # ( {A,B,C}, A.intersect(B).intersect(C) ) # Start with just elementary sets ( ({A}, A), ({B}, B), ... ) # Then get and subtract ( ({A,B}, (A int B), ... ) while non-zero sets = [(FiniteSet(s), s) for s in self.args] measure = 0 parity = 1 while sets: # Add up the measure of these sets and add or subtract it to total measure += parity * sum(inter.measure for sos, inter in sets) # For each intersection in sets, compute the intersection with every # other set not already part of the intersection. sets = ((sos + FiniteSet(newset), newset.intersect(intersection)) for sos, intersection in sets for newset in self.args if newset not in sos) # Clear out sets with no measure sets = [(sos, inter) for sos, inter in sets if inter.measure != 0] # Clear out duplicates sos_list = [] sets_list = [] for set in sets: if set[0] in sos_list: continue else: sos_list.append(set[0]) sets_list.append(set) sets = sets_list # Flip Parity - next time subtract/add if we added/subtracted here parity *= -1 return measure def as_relational(self, symbol): """Rewrite a Union in terms of equalities and logic operators. """ from sympy.logic.boolalg import Or return Or(*[set.as_relational(symbol) for set in self.args]) @property def is_iterable(self): return all(arg.is_iterable for arg in self.args) @property def is_Union(self): return True class RealUnion(Union, RealSet): """ Represents a union of Real Sets (Intervals, RealFiniteSets) This class should only be used internally. Please make unions with Union class. See Union for details """ def __new__(cls, *args): intervals, finite_sets, other_sets = [], [], [] args = list(args) for arg in args: if isinstance(arg, Set): if arg == S.EmptySet: continue elif arg.is_Union: args += arg.args elif arg.is_FiniteSet: finite_sets.append(arg) elif arg.is_Interval: intervals.append(arg) else: other_sets.append(arg) elif is_flattenable(arg): args += arg else: raise TypeError("%s: Not a set or iterable of sets"%arg) # Sort intervals according to their infimum intervals.sort(key=lambda i: i.start) # Merge comparable overlapping intervals i = 0 while i < len(intervals) - 1: cur = intervals[i] next = intervals[i + 1] merge = False if cur._is_comparable(next): if next.start < cur.end: merge = True elif next.start == cur.end: # Must be careful with boundaries. merge = not(next.left_open and cur.right_open) if merge: if cur.start == next.start: left_open = cur.left_open and next.left_open else: left_open = cur.left_open if cur.end < next.end: right_open = next.right_open end = next.end elif cur.end > next.end: right_open = cur.right_open end = cur.end else: right_open = cur.right_open and next.right_open end = cur.end intervals[i] = Interval(cur.start, end, left_open, right_open) del intervals[i + 1] else: i += 1 # Collect all elements in the finite sets not in any interval if finite_sets: # Merge Finite Sets finite_set = sum(finite_sets, S.EmptySet) # Close open intervals if boundary is in finite_set for num, i in enumerate(intervals): closeLeft = i.start in finite_set if i.left_open else False closeRight = i.end in finite_set if i.right_open else False if ((closeLeft and i.left_open) or (closeRight and i.right_open)): intervals[num] = Interval(i.start, i.end, not closeLeft, not closeRight) # All elements in finite_set not in any interval finite_complement = FiniteSet( el for el in finite_set if not el.is_number or not any(el in i for i in intervals)) if len(finite_complement)>0: # Anything left? other_sets.append(finite_complement) # Clear out empty sets sets = [set for set in (intervals + other_sets) if set] # If nothing is there then return the empty set if not sets: return S.EmptySet # If a single set is left over, don't create a new Union object but # rather return the single set. if len(sets) == 1: return sets[0] return Basic.__new__(cls, *sets) def _eval_evalf(self, prec): return RealUnion(set.evalf() for set in self.args) def __iter__(self): import itertools if all(set.is_iterable for set in self.args): return itertools.chain(*(iter(arg) for arg in self.args)) else: raise TypeError("Not all constituent sets are iterable") class EmptySet(Set): """ Represents the empty set. The empty set is available as a singleton as S.EmptySet. Examples: >>> from sympy import S, Interval >>> S.EmptySet EmptySet() >>> Interval(1, 2).intersect(S.EmptySet) EmptySet() """ __metaclass__ = Singleton def _intersect(self, other): return S.EmptySet @property def _complement(self): return Interval(S.NegativeInfinity, S.Infinity) @property def _measure(self): return 0 def _contains(self, other): return False def as_relational(self, symbol): return False def __len__(self): return 0 def union(self, other): return other def __iter__(self): return iter([]) class FiniteSet(CountableSet): """ Represents a finite set of discrete numbers Examples: >>> from sympy import Symbol, FiniteSet, sets >>> FiniteSet(1, 2, 3, 4) {1, 2, 3, 4} >>> 3 in FiniteSet(1, 2, 3, 4) True """ def __new__(cls, *args): def flatten(arg): if is_flattenable(arg): return sum(map(flatten, arg), []) return [arg] args = flatten(list(args)) # Sympify Arguments args = map(sympify, args) # Turn tuples into Tuples args = [Tuple(*arg) if arg.__class__ is tuple else arg for arg in args] if len(args) == 0: return EmptySet() if all([arg.is_real and arg.is_number for arg in args]): cls = RealFiniteSet elements = frozenset(map(sympify, args)) obj = Basic.__new__(cls, *elements) obj.elements = elements return obj def __iter__(self): return self.elements.__iter__() def _intersect(self, other): if isinstance(other, self.__class__): return self.__class__(*(self.elements & other.elements)) return self.__class__(el for el in self if el in other) def union(self, other): """ Returns the union of 'self' and 'other'. As a shortcut it is possible to use the '+' operator: >>> from sympy import FiniteSet, Interval, Symbol >>> FiniteSet(0, 1).union(FiniteSet(2, 3)) {0, 1, 2, 3} >>> FiniteSet(Symbol('x'), 1, 2) + FiniteSet(2, 3) {1, 2, 3, x} >>> Interval(1, 2, True, True) + FiniteSet(2, 3) (1, 2] U {3} Similarly it is possible to use the '-' operator for set differences: >>> FiniteSet(Symbol('x'), 1, 2) - FiniteSet(2, 3) {1, x} >>> Interval(1, 2) - FiniteSet(2, 3) [1, 2) """ if other == S.EmptySet: return self if other.is_FiniteSet: return FiniteSet(*(self.elements | other.elements)) return Union(self, other) # Resort to default def _contains(self, other): """ Tests whether an element, other, is in the set. Relies on Python's set class. This tests for object equality All inputs are sympified >>> from sympy import FiniteSet >>> 1 in FiniteSet(1, 2) True >>> 5 in FiniteSet(1, 2) False """ return sympify(other) in self.elements @property def _inf(self): from sympy.functions.elementary.miscellaneous import Min return Min(*self) @property def _sup(self): from sympy.functions.elementary.miscellaneous import Max return Max(*self) def __len__(self): return len(self.elements) def __sub__(self, other): return FiniteSet(el for el in self if el not in other) def as_relational(self, symbol): """Rewrite a FiniteSet in terms of equalities and logic operators. """ from sympy.core.relational import Eq from sympy.logic.boolalg import Or return Or(*[Eq(symbol, elem) for elem in self]) @property def is_FiniteSet(self): return True @property def is_real(self): return all(el.is_real for el in self) class RealFiniteSet(FiniteSet, RealSet): """ A FiniteSet with all elements Real Numbers. Allows for good integration with Intervals This class for internal use only. Use FiniteSet to create a RealFiniteSet See FiniteSet for more details """ def _eval_evalf(self, prec): return RealFiniteSet(elem.evalf(prec) for elem in self) @property def _complement(self): """ The complement of a real finite set is the Union of open Intervals between the elements of the set. >>> from sympy import FiniteSet >>> FiniteSet(1, 2, 3).complement (-oo, 1) U (1, 2) U (2, 3) U (3, oo) """ if not all(elem.is_number for elem in self.elements): raise ValueError("%s: Complement not defined for symbolic inputs" %self) sorted_elements = sorted(list(self.elements)) intervals = [] # Build up a list of intervals between the elements intervals += [Interval(S.NegativeInfinity,sorted_elements[0],True,True)] for a, b in zip(sorted_elements[0:-1], sorted_elements[1:]): intervals.append(Interval(a, b, True, True)) # open intervals intervals.append(Interval(sorted_elements[-1], S.Infinity, True, True)) return Union(*intervals) def as_relational(self, symbol): """Rewrite a FiniteSet in terms of equalities and logic operators. """ from sympy.core.relational import Eq from sympy.logic.boolalg import Or return Or(*[Eq(symbol, elem) for elem in self]) def _eval_evalf(self, prec): return FiniteSet(elem.evalf(prec) for elem in self) genclass = (1 for i in xrange(2)).__class__ def is_flattenable(obj): """ Checks that an argument to a Set constructor should be flattened """ return obj.__class__ in [list, set, genclass] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/mul.py0000644000175000017500000011632512014170666023206 0ustar georgeskgeorgeskfrom basic import Basic, C from singleton import S from operations import AssocOp from cache import cacheit from logic import fuzzy_not from compatibility import cmp_to_key # internal marker to indicate: # "there are still non-commutative objects -- don't forget to process them" class NC_Marker: is_Order = False is_Mul = False is_Number = False is_Poly = False is_commutative = False class Mul(AssocOp): __slots__ = [] is_Mul = True #identity = S.One # cyclic import, so defined in numbers.py @classmethod def flatten(cls, seq): # apply associativity, separate commutative part of seq c_part = [] # out: commutative factors nc_part = [] # out: non-commutative factors nc_seq = [] coeff = S.One # standalone term # e.g. 3 * ... c_powers = [] # (base,exp) n # e.g. (x,n) for x num_exp = [] # (num-base, exp) y # e.g. (3, y) for ... * 3 * ... neg1e = 0 # exponent on -1 extracted from Number-based Pow pnum_rat = {} # (num-base, Rat-exp) 1/2 # e.g. (3, 1/2) for ... * 3 * ... order_symbols = None # --- PART 1 --- # # "collect powers and coeff": # # o coeff # o c_powers # o num_exp # o neg1e # o pnum_rat # # NOTE: this is optimized for all-objects-are-commutative case for o in seq: # O(x) if o.is_Order: o, order_symbols = o.as_expr_variables(order_symbols) # Mul([...]) if o.is_Mul: if o.is_commutative: seq.extend(o.args) # XXX zerocopy? else: # NCMul can have commutative parts as well for q in o.args: if q.is_commutative: seq.append(q) else: nc_seq.append(q) # append non-commutative marker, so we don't forget to # process scheduled non-commutative objects seq.append(NC_Marker) continue # 3 elif o.is_Number: if o is S.NaN or coeff is S.ComplexInfinity and o is S.Zero: # we know for sure the result will be nan return [S.NaN], [], None elif coeff.is_Number: # it could be zoo coeff *= o if coeff is S.NaN: # we know for sure the result will be nan return [S.NaN], [], None continue elif o is S.ComplexInfinity: if not coeff or coeff is S.ComplexInfinity: # we know for sure the result will be nan return [S.NaN], [], None coeff = S.ComplexInfinity continue elif o.is_commutative: # e # o = b b, e = o.as_base_exp() # y # 3 if o.is_Pow and b.is_Number: # get all the factors with numeric base so they can be # combined below, but don't combine negatives unless # the exponent is an integer if e.is_Rational: if e.is_Integer: coeff *= Pow(b, e) # it is an unevaluated power continue elif e.is_negative: # also a sign of an unevaluated power seq.append(Pow(b, e)) continue elif b.is_negative: neg1e += e b = -b if b is not S.One: pnum_rat.setdefault(b, []).append(e) continue elif b.is_positive or e.is_integer: num_exp.append((b, e)) continue c_powers.append((b,e)) # NON-COMMUTATIVE # TODO: Make non-commutative exponents not combine automatically else: if o is not NC_Marker: nc_seq.append(o) # process nc_seq (if any) while nc_seq: o = nc_seq.pop(0) if not nc_part: nc_part.append(o) continue # b c b+c # try to combine last terms: a * a -> a o1 = nc_part.pop() b1,e1 = o1.as_base_exp() b2,e2 = o.as_base_exp() new_exp = e1 + e2 # Only allow powers to combine if the new exponent is # not an Add. This allow things like a**2*b**3 == a**5 # if a.is_commutative == False, but prohibits # a**x*a**y and x**a*x**b from combining (x,y commute). if b1==b2 and (not new_exp.is_Add): o12 = b1 ** new_exp # now o12 could be a commutative object if o12.is_commutative: seq.append(o12) continue else: nc_seq.insert(0, o12) else: nc_part.append(o1) nc_part.append(o) # We do want a combined exponent if it would not be an Add, such as # y 2y 3y # x * x -> x # We determine this if two exponents have the same term in as_coeff_mul # # Unfortunately, this isn't smart enough to consider combining into # exponents that might already be adds, so things like: # z - y y # x * x will be left alone. This is because checking every possible # combination can slow things down. # gather exponents of common bases... # in c_powers new_c_powers = [] common_b = {} # b:e for b, e in c_powers: co = e.as_coeff_mul() common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0]) for b, d in common_b.items(): for di, li in d.items(): d[di] = Add(*li) for b, e in common_b.items(): for t, c in e.items(): new_c_powers.append((b,c*Mul(*t))) c_powers = new_c_powers # and in num_exp new_num_exp = [] common_b = {} # b:e for b, e in num_exp: co = e.as_coeff_mul() common_b.setdefault(b, {}).setdefault(co[1], []).append(co[0]) for b, d in common_b.items(): for di, li in d.items(): d[di] = Add(*li) for b, e in common_b.items(): for t, c in e.items(): new_num_exp.append((b,c*Mul(*t))) num_exp = new_num_exp # --- PART 2 --- # # o process collected powers (x**0 -> 1; x**1 -> x; otherwise Pow) # o combine collected powers (2**x * 3**x -> 6**x) # with numeric base # ................................ # now we have: # - coeff: # - c_powers: (b, e) # - num_exp: (2, e) # - pnum_rat: {(1/3, [1/3, 2/3, 1/4])} # 0 1 # x -> 1 x -> x for b, e in c_powers: if e is S.One: if b.is_Number: coeff *= b else: c_part.append(b) elif not e is S.Zero: c_part.append(Pow(b, e)) # x x x # 2 * 3 -> 6 inv_exp_dict = {} # exp:Mul(num-bases) x x # e.g. x:6 for ... * 2 * 3 * ... for b, e in num_exp: inv_exp_dict.setdefault(e, []).append(b) for e, b in inv_exp_dict.items(): inv_exp_dict[e] = Mul(*b) c_part.extend([Pow(b, e) for e, b in inv_exp_dict.iteritems() if e]) # b, e -> e, b # {(1/5, [1/3]), (1/2, [1/12, 1/4]} -> {(1/3, [1/5, 1/2])} comb_e = {} for b, e in pnum_rat.iteritems(): comb_e.setdefault(Add(*e), []).append(b) del pnum_rat # process them, reducing exponents to values less than 1 # and updating coeff if necessary else adding them to # num_rat for further processing num_rat = [] for e, b in comb_e.iteritems(): b = Mul(*b) if e.q == 1: coeff *= Pow(b, e) continue if e.p > e.q: e_i, ep = divmod(e.p, e.q) coeff *= Pow(b, e_i) e = Rational(ep, e.q) num_rat.append((b, e)) del comb_e # extract gcd of bases in num_rat # 2**(1/3)*6**(1/4) -> 2**(1/3+1/4)*3**(1/4) pnew = {} i = 0 # steps through num_rat which may grow while i < len(num_rat): bi, ei = num_rat[i] grow = [] for j in range(i + 1, len(num_rat)): bj, ej = num_rat[j] g = igcd(bi, bj) if g != 1: # 4**r1*6**r2 -> 2**(r1+r2) * 2**r1 * 3**r2 # this might have a gcd with something else e = ei + ej if e.q == 1: coeff *= Pow(g, e) else: if e.p > e.q: e_i, ep = divmod(e.p, e.q) # change e in place coeff *= Pow(g, e_i) e = Rational(ep, e.q) grow.append((g, e)) # update the jth item num_rat[j] = (bj//g, ej) # update bi that we are checking with bi = bi//g if bi is S.One: break if bi is not S.One: obj = Pow(bi, ei) if obj.is_Number: coeff *= obj else: if obj.is_Mul: # 12**(1/2) -> 2*sqrt(3) c, obj = obj.args # expecting only 2 args coeff *= c assert obj.is_Pow bi, ei = obj.args pnew.setdefault(ei, []).append(bi) num_rat.extend(grow) i += 1 # combine bases of the new powers for e, b in pnew.iteritems(): pnew[e] = Mul(*b) # see if there is a base with matching coefficient # that the -1 can be joined with if neg1e: p = Pow(S.NegativeOne, neg1e) if p.is_Number: coeff *= p else: if p.is_Mul: c, p = p.args coeff *= c assert p.is_Pow and p.base is S.NegativeOne neg1e = p.args[1] for e, b in pnew.iteritems(): if e == neg1e and b.is_positive: pnew[e] = -b break else: c_part.append(p) # add all the pnew powers c_part.extend([Pow(b, e) for e, b in pnew.iteritems()]) # oo, -oo if (coeff is S.Infinity) or (coeff is S.NegativeInfinity): new_c_part = [] coeff_sign = 1 for t in c_part: if t.is_positive: continue if t.is_negative: coeff_sign *= -1 continue new_c_part.append(t) c_part = new_c_part new_nc_part = [] for t in nc_part: if t.is_positive: continue if t.is_negative: coeff_sign *= -1 continue new_nc_part.append(t) nc_part = new_nc_part coeff *= coeff_sign # zoo if coeff is S.ComplexInfinity: # zoo might be # unbounded_real + bounded_im # bounded_real + unbounded_im # unbounded_real + unbounded_im # and non-zero real or imaginary will not change that status. c_part = [c for c in c_part if not (c.is_nonzero and c.is_real is not None)] nc_part = [c for c in nc_part if not (c.is_nonzero and c.is_real is not None)] # 0 elif coeff is S.Zero: # we know for sure the result will be 0 return [coeff], [], order_symbols # order commutative part canonically c_part.sort(key=cmp_to_key(Basic.compare)) # current code expects coeff to be always in slot-0 if coeff is not S.One: c_part.insert(0, coeff) # we are done if len(c_part)==2 and c_part[0].is_Number and c_part[1].is_Add: # 2*(1+a) -> 2 + 2 * a coeff = c_part[0] c_part = [Add(*[coeff*f for f in c_part[1].args])] return c_part, nc_part, order_symbols def _eval_power(b, e): if e.is_Number: if b.is_commutative: if e.is_Integer: # (a*b)**2 -> a**2 * b**2 return Mul(*[s**e for s in b.args]) if e.is_rational: coeff, rest = b.as_coeff_mul() rest = list(rest) unk=[] nonneg=[] neg=[] for bi in rest: if not bi.is_negative is None: #then we know the sign if bi.is_negative: neg.append(bi) else: nonneg.append(bi) else: unk.append(bi) if len(unk) == len(rest) or len(neg) == len(rest) == 1: # if all terms were unknown there is nothing to pull # out except maybe the coeff; if there is a single # negative term, this is the base case which cannot # be processed further if coeff.is_negative: coeff *= -1 rest[0] = -rest[0] if coeff is S.One: return None return Mul(Pow(coeff, e), Pow(Mul(*rest), e)) # otherwise return the new expression expanding out the # known terms; those that are not known can be expanded # out with separate() but this will introduce a lot of # "garbage" that is needed to keep one on the same branch # as the unexpanded expression. The negatives are brought # out with a negative sign added and a negative left behind # in the unexpanded terms if there were an odd number of # negatives. if neg: neg = [-w for w in neg] if coeff.is_negative: coeff = -coeff unk.append(S.NegativeOne) if len(neg) % 2: unk.append(S.NegativeOne) return Mul(*[Pow(s, e) for s in nonneg + neg + [coeff]])* \ Pow(Mul(*unk), e) coeff, rest = b.as_coeff_mul() if coeff is not S.One: # (2*a)**3 -> 2**3 * a**3 return Mul(Pow(coeff, e), Mul(*[s**e for s in rest])) elif e.is_Integer: coeff, rest = b.as_coeff_mul() if coeff == S.One: return # the test below for even exponent needs coeff != 1 else: return Mul(Pow(coeff, e), Pow(Mul(*rest), e)) c, t = b.as_coeff_mul() if e.is_even and c.is_Number and c < 0: return Pow((Mul(-c, Mul(*t))), e) #if e.has(Wild): # return Mul(*[t**e for t in b]) @classmethod def class_key(cls): return 3, 0, cls.__name__ def _eval_evalf(self, prec): return AssocOp._eval_evalf(self, prec).expand() @cacheit def as_two_terms(self): """Return head and tail of self. This is the most efficient way to get the head and tail of an expression. - if you want only the head, use self.args[0]; - if you want to process the arguments of the tail then use self.as_coef_mul() which gives the head and a tuple containing the arguments of the tail when treated as a Mul. - if you want the coefficient when self is treated as an Add then use self.as_coeff_add()[0] >>> from sympy.abc import x, y >>> (3*x*y).as_two_terms() (3, x*y) """ args = self.args if len(args) == 1: return S.One, self elif len(args) == 2: return args else: return args[0], self._new_rawargs(*args[1:]) @cacheit def as_coeff_mul(self, *deps): if deps: l1 = [] l2 = [] for f in self.args: if f.has(*deps): l2.append(f) else: l1.append(f) return self._new_rawargs(*l1), tuple(l2) coeff, notrat = self.args[0].as_coeff_mul() if not coeff is S.One: return coeff, notrat + self.args[1:] return S.One, self.args @staticmethod def _expandsums(sums): """ Helper function for _eval_expand_mul. sums must be a list of instances of Basic. """ L = len(sums) if L == 1: return sums[0].args terms = [] left = Mul._expandsums(sums[:L//2]) right = Mul._expandsums(sums[L//2:]) terms = [Mul(a, b) for a in left for b in right] added = Add(*terms) return Add.make_args(added) #it may have collapsed down to one term def _eval_expand_basic(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_basic'): newterm = term._eval_expand_basic(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_exp(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_power_exp'): newterm = term._eval_expand_power_exp(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_base(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_power_base'): newterm = term._eval_expand_power_base(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_mul(self, deep=True, **hints): plain, sums, rewrite = [], [], False for factor in self.args: if deep: term = factor.expand(deep=deep, **hints) if term != factor: factor = term rewrite = True if factor.is_Add: sums.append(factor) rewrite = True else: if factor.is_commutative: plain.append(factor) else: Wrapper = Basic sums.append(Wrapper(factor)) if not rewrite: return self else: if sums: terms = Mul._expandsums(sums) plain = Mul(*plain) return Add(*[Mul(plain, term) for term in terms]) else: return Mul(*plain) def _eval_expand_multinomial(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_multinomial'): newterm = term._eval_expand_multinomial(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_log(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_log'): newterm = term._eval_expand_log(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_complex(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_complex'): newterm = term._eval_expand_complex(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_trig(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_trig'): newterm = term._eval_expand_trig(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_func(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_func'): newterm = term._eval_expand_func(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_derivative(self, s): terms = list(self.args) factors = [] for i in xrange(len(terms)): t = terms[i].diff(s) if t is S.Zero: continue factors.append(Mul(*(terms[:i]+[t]+terms[i+1:]))) return Add(*factors) def _matches_simple(self, expr, repl_dict): # handle (w*3).matches('x*5') -> {w: x*5/3} coeff, terms = self.as_coeff_mul() if len(terms) == 1: newexpr = self.__class__._combine_inverse(expr, coeff) return terms[0].matches(newexpr, repl_dict) return def matches(self, expr, repl_dict={}, evaluate=False): expr = sympify(expr) if self.is_commutative and expr.is_commutative: return AssocOp._matches_commutative(self, expr, repl_dict, evaluate) # todo for commutative parts, until then use the default matches method for non-commutative products return self._matches(expr, repl_dict, evaluate) def _matches(self, expr, repl_dict={}, evaluate=False): if evaluate: return self.subs(repl_dict).matches(expr, repl_dict) # weed out negative one prefixes sign = 1 a, b = self.as_two_terms() if a is S.NegativeOne: if b.is_Mul: sign = -sign else: # the remainder, b, is not a Mul anymore return b.matches(-expr, repl_dict, evaluate) expr = sympify(expr) if expr.is_Mul and expr.args[0] is S.NegativeOne: expr = -expr; sign = -sign if not expr.is_Mul: # expr can only match if it matches b and a matches +/- 1 if len(self.args) == 2: # quickly test for equality if b == expr: return a.matches(Rational(sign), repl_dict, evaluate) # do more expensive match dd = b.matches(expr, repl_dict, evaluate) if dd == None: return None dd = a.matches(Rational(sign), dd, evaluate) return dd return None d = repl_dict.copy() # weed out identical terms pp = list(self.args) ee = list(expr.args) for p in self.args: if p in expr.args: ee.remove(p) pp.remove(p) # only one symbol left in pattern -> match the remaining expression if len(pp) == 1 and isinstance(pp[0], C.Wild): if len(ee) == 1: d[pp[0]] = sign * ee[0] else: d[pp[0]] = sign * (type(expr)(*ee)) return d if len(ee) != len(pp): return None i = 0 for p, e in zip(pp, ee): if i == 0 and sign != 1: try: e = sign * e except TypeError: return None d = p.matches(e, d, evaluate=not i) i += 1 if d is None: return None return d @staticmethod def _combine_inverse(lhs, rhs): """ Returns lhs/rhs, but treats arguments like symbols, so things like oo/oo return 1, instead of a nan. """ if lhs == rhs: return S.One def check(l, r): if l.is_Float and r.is_comparable: return Add(l, 0) == Add(r.evalf(), 0) return False if check(lhs, rhs) or check(rhs, lhs): return S.One if lhs.is_Mul and rhs.is_Mul: a = list(lhs.args) b = [1] for x in rhs.args: if x in a: a.remove(x) else: b.append(x) return Mul(*a)/Mul(*b) return lhs/rhs def as_powers_dict(self): d = {} for term in self.args: b, e = term.as_base_exp() if b not in d: d[b] = e else: d[b] += e return d def as_numer_denom(self): numers, denoms = [],[] for t in self.args: n,d = t.as_numer_denom() numers.append(n) denoms.append(d) return Mul(*numers), Mul(*denoms) def as_coeff_Mul(self): """Efficiently extract the coefficient of a product. """ coeff, args = self.args[0], self.args[1:] if coeff.is_Number: if len(args) == 1: return coeff, args[0] else: return coeff, self._new_rawargs(*args) else: return S.One, self def as_base_exp(self): e1 = None bases = [] nc = 0 for m in self.args: b, e = m.as_base_exp() if not b.is_commutative: nc += 1 if e1 is None: e1 = e elif e != e1 or nc > 1: return self, S.One bases.append(b) return Mul(*bases), e1 def _eval_is_polynomial(self, syms): return all(term._eval_is_polynomial(syms) for term in self.args) def _eval_is_rational_function(self, syms): return all(term._eval_is_rational_function(syms) for term in self.args) _eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded') _eval_is_commutative = lambda self: self._eval_template_is_attr('is_commutative') _eval_is_integer = lambda self: self._eval_template_is_attr('is_integer') _eval_is_comparable = lambda self: self._eval_template_is_attr('is_comparable') # I*I -> R, I*I*I -> -I def _eval_is_real(self): im_count = 0 is_neither = False for t in self.args: if t.is_imaginary: im_count += 1 continue t_real = t.is_real if t_real: continue elif t_real is False: if is_neither: return None else: is_neither = True else: return None if is_neither: return False return (im_count % 2 == 0) def _eval_is_imaginary(self): im_count = 0 is_neither = False for t in self.args: if t.is_imaginary: im_count += 1 continue t_real = t.is_real if t_real: continue elif t_real is False: if is_neither: return None else: is_neither = True else: return None if is_neither: return False return (im_count % 2 == 1) def _eval_is_irrational(self): for t in self.args: a = t.is_irrational if a: return True if a is None: return return False def _eval_is_positive(self): terms = [t for t in self.args if not t.is_positive] if not terms: return True c = terms[0] if len(terms)==1: if c.is_nonpositive: return False return r = self._new_rawargs(*terms[1:]) if c.is_negative and r.is_negative: return True if r.is_negative and c.is_negative: return True # check for nonpositivity, <=0 if c.is_negative and r.is_nonnegative: return False if r.is_negative and c.is_nonnegative: return False if c.is_nonnegative and r.is_nonpositive: return False if r.is_nonnegative and c.is_nonpositive: return False def _eval_is_negative(self): terms = [t for t in self.args if not t.is_positive] if not terms: # all terms are either positive -- 2*Symbol('n', positive=T) # or unknown -- 2*Symbol('x') if self.is_positive: return False else: return None c = terms[0] if len(terms)==1: return c.is_negative r = self._new_rawargs(*terms[1:]) # check for nonnegativity, >=0 if c.is_negative and r.is_nonpositive: return False if r.is_negative and c.is_nonpositive: return False if c.is_nonpositive and r.is_nonpositive: return False if c.is_nonnegative and r.is_nonnegative: return False def _eval_is_odd(self): is_integer = self.is_integer if is_integer: r = True for t in self.args: if t.is_even: return False if t.is_odd is None: r = None return r # !integer -> !odd elif is_integer == False: return False def _eval_is_even(self): is_integer = self.is_integer if is_integer: return fuzzy_not(self._eval_is_odd()) elif is_integer == False: return False def _eval_subs(self, old, new): from sympy import sign from sympy.simplify.simplify import powdenest if self == old: return new def fallback(): """Return this value when partial subs has failed.""" return self.__class__(*[s._eval_subs(old, new) for s in self.args]) def breakup(eq): """break up powers assuming (not checking) that eq is a Mul: b**(Rational*e) -> b**e, Rational commutatives come back as a dictionary {b**e: Rational} noncommutatives come back as a list [(b**e, Rational)] """ (c, nc) = (dict(), list()) for (i, a) in enumerate(Mul.make_args(eq)): a = powdenest(a) (b, e) = a.as_base_exp() if not e is S.One: (co, _) = e.as_coeff_mul() b = Pow(b, e/co) e = co if a.is_commutative: if b in c: # handle I and -1 like things where b, e for I is -1, 1/2 c[b] += e else: c[b] = e else: nc.append([b, e]) return (c, nc) def rejoin(b, co): """ Put rational back with exponent; in general this is not ok, but since we took it from the exponent for analysis, it's ok to put it back. """ (b, e) = b.as_base_exp() return Pow(b, e*co) def ndiv(a, b): """if b divides a in an extractive way (like 1/4 divides 1/2 but not vice versa, and 2/5 does not divide 1/3) then return the integer number of times it divides, else return 0. """ if not b.q % a.q or not a.q % b.q: return int(a/b) return 0 if not old.is_Mul: return fallback() # handle the leading coefficient and use it to decide if anything # should even be started; we always know where to find the Rational # so it's a quick test co_self = self.args[0] co_old = old.args[0] if co_old.is_Rational and co_self.is_Rational: co_xmul = co_self.extract_multiplicatively(co_old) elif co_old.is_Rational: co_xmul = None else: co_xmul = True if not co_xmul: return fallback() (c, nc) = breakup(self) (old_c, old_nc) = breakup(old) # update the coefficients if we had an extraction if getattr(co_xmul, 'is_Rational', False): c.pop(co_self) c[co_xmul] = S.One old_c.pop(co_old) # Do some quick tests to see whether we can succeed: # 1) check for more non-commutative or 2) commutative terms # 3) ... unmatched non-commutative bases # 4) ... unmatched commutative terms # 5) and finally differences in sign if len(old_nc) > len(nc) or len(old_c) > len(c) or \ set(_[0] for _ in old_nc).difference(set(_[0] for _ in nc)) or \ set(old_c).difference(set(c)) or \ any(sign(c[b]) != sign(old_c[b]) for b in old_c): return fallback() if not old_c: cdid = None else: rat = [] for (b, old_e) in old_c.items(): c_e = c[b] rat.append(ndiv(c_e, old_e)) if not rat[-1]: return fallback() cdid = min(rat) if not old_nc: ncdid = None for i in range(len(nc)): nc[i] = rejoin(*nc[i]) else: ncdid = 0 # number of nc replacements we did take = len(old_nc) # how much to look at each time limit = cdid or S.Infinity # max number that we can take failed = [] # failed terms will need subs if other terms pass i = 0 while limit and i + take <= len(nc): hit = False # the bases must be equivalent in succession, and # the powers must be extractively compatible on the # first and last factor but equal inbetween. rat = [] for j in range(take): if nc[i + j][0] != old_nc[j][0]: break elif j == 0: rat.append(ndiv(nc[i + j][1], old_nc[j][1])) elif j == take - 1: rat.append(ndiv(nc[i + j][1], old_nc[j][1])) elif nc[i + j][1] != old_nc[j][1]: break else: rat.append(1) j += 1 else: ndo = min(rat) if ndo: if take == 1: if cdid: ndo = min(cdid, ndo) nc[i] = Pow(new, ndo)*rejoin(nc[i][0], nc[i][1] - ndo*old_nc[0][1]) else: ndo = 1 # the left residual l = rejoin(nc[i][0], nc[i][1] - ndo* old_nc[0][1]) # eliminate all middle terms mid = new # the right residual (which may be the same as the middle if take == 2) ir = i + take - 1 r = (nc[ir][0], nc[ir][1] - ndo* old_nc[-1][1]) if r[1]: if i + take < len(nc): nc[i:i + take] = [l*mid, r] else: r = rejoin(*r) nc[i:i + take] = [l*mid*r] else: # there was nothing left on the right nc[i:i + take] = [l*mid] limit -= ndo ncdid += ndo hit = True if not hit: # do the subs on this failing factor failed.append(i) i += 1 else: if not ncdid: return fallback() # although we didn't fail, certain nc terms may have # failed so we rebuild them after attempting a partial # subs on them failed.extend(range(i, len(nc))) for i in failed: nc[i] = rejoin(*nc[i]).subs(old, new) # rebuild the expression if cdid is None: do = ncdid elif ncdid is None: do = cdid else: do = min(ncdid, cdid) margs = [] for b in c: if b in old_c: # calculate the new exponent e = c[b] - old_c[b]*do margs.append(rejoin(b, e)) else: margs.append(rejoin(b.subs(old, new), c[b])) if cdid and not ncdid: # in case we are replacing commutative with non-commutative, # we want the new term to come at the front just like the # rest of this routine margs = [Pow(new, cdid)] + margs return Mul(*margs)*Mul(*nc) def _eval_nseries(self, x, n, logx): from sympy import powsimp terms = [t.nseries(x, n=n, logx=logx) for t in self.args] return powsimp(Mul(*terms).expand(), combine='exp', deep=True) def _eval_as_leading_term(self, x): return Mul(*[t.as_leading_term(x) for t in self.args]) def _eval_conjugate(self): return Mul(*[t.conjugate() for t in self.args]) def _sage_(self): s = 1 for x in self.args: s *= x._sage_() return s from numbers import Rational, igcd from power import Pow from sympify import sympify from add import Add wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/relational.py0000644000175000017500000001063112014170666024534 0ustar georgeskgeorgeskfrom expr import Expr from evalf import EvalfMixin from sympify import _sympify def Rel(a, b, op): """ A handy wrapper around the Relational class. Rel(a,b, op) Example: >>> from sympy import Rel >>> from sympy.abc import x, y >>> Rel(y, x+x**2, '==') y == x**2 + x """ return Relational(a,b,op) def Eq(a, b=0): """ A handy wrapper around the Relational class. Eq(a,b) Example: >>> from sympy import Eq >>> from sympy.abc import x, y >>> Eq(y, x+x**2) y == x**2 + x """ return Relational(a,b,'==') def Ne(a, b): """ A handy wrapper around the Relational class. Ne(a,b) Example: >>> from sympy import Ne >>> from sympy.abc import x, y >>> Ne(y, x+x**2) y != x**2 + x """ return Relational(a,b,'!=') def Lt(a, b): """ A handy wrapper around the Relational class. Lt(a,b) Example: >>> from sympy import Lt >>> from sympy.abc import x, y >>> Lt(y, x+x**2) y < x**2 + x """ return Relational(a,b,'<') def Le(a, b): """ A handy wrapper around the Relational class. Le(a,b) Example: >>> from sympy import Le >>> from sympy.abc import x, y >>> Le(y, x+x**2) y <= x**2 + x """ return Relational(a,b,'<=') def Gt(a, b): """ A handy wrapper around the Relational class. Gt(a,b) Example: >>> from sympy import Gt >>> from sympy.abc import x, y >>> Gt(y, x+x**2) x**2 + x < y """ return Relational(a,b,'>') def Ge(a, b): """ A handy wrapper around the Relational class. Ge(a,b) Example: >>> from sympy import Ge >>> from sympy.abc import x, y >>> Ge(y, x+x**2) x**2 + x <= y """ return Relational(a,b,'>=') class Relational(Expr, EvalfMixin): __slots__ = [] is_Relational = True @staticmethod def get_relational_class(rop): if rop is None or rop in ['==','eq']: return Equality, False if rop in ['!=','<>','ne']: return Unequality, False if rop in ['<','lt']: return StrictInequality, False if rop in ['>','gt']: return StrictInequality, True if rop in ['<=','le']: return Inequality, False if rop in ['>=','ge']: return Inequality, True raise ValueError("Invalid relational operator symbol: %r" % (rop)) def __new__(cls, lhs, rhs, rop=None, **assumptions): lhs = _sympify(lhs) rhs = _sympify(rhs) if cls is not Relational: rop_cls = cls else: rop_cls, swap = Relational.get_relational_class(rop) if swap: lhs, rhs = rhs, lhs if lhs.is_real and lhs.is_number and rhs.is_real and rhs.is_number: # Just becase something is a number, doesn't mean you can evalf it. Nlhs = lhs.evalf() if Nlhs.is_Number: # S.Zero.evalf() returns S.Zero, so test Number instead of Float Nrhs = rhs.evalf() if Nrhs.is_Number: return rop_cls._eval_relation(Nlhs, Nrhs) obj = Expr.__new__(rop_cls, lhs, rhs, **assumptions) return obj @property def lhs(self): return self._args[0] @property def rhs(self): return self._args[1] def _eval_subs(self, old, new): if self == old: return new return self.__class__(self.lhs._eval_subs(old, new), self.rhs._eval_subs(old, new)) def _eval_evalf(self, prec): return self.func(*[s._evalf(prec) for s in self.args]) class Equality(Relational): rel_op = '==' __slots__ = [] is_Equality = True @classmethod def _eval_relation(cls, lhs, rhs): return lhs == rhs def __nonzero__(self): return self.lhs.compare(self.rhs)==0 class Unequality(Relational): rel_op = '!=' __slots__ = [] @classmethod def _eval_relation(cls, lhs, rhs): return lhs != rhs def __nonzero__(self): return self.lhs.compare(self.rhs)!=0 class StrictInequality(Relational): rel_op = '<' __slots__ = [] @classmethod def _eval_relation(cls, lhs, rhs): return lhs < rhs def __nonzero__(self): return self.lhs.compare(self.rhs)==-1 class Inequality(Relational): rel_op = '<=' __slots__ = [] @classmethod def _eval_relation(cls, lhs, rhs): return lhs <= rhs def __nonzero__(self): return self.lhs.compare(self.rhs)<=0 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/operations.py0000644000175000017500000003004212014170666024563 0ustar georgeskgeorgeskfrom core import C from expr import Expr from sympify import _sympify, sympify from cache import cacheit from compatibility import cmp # from add import Add /cyclic/ # from mul import Mul /cyclic/ # from function import Lambda, WildFunction /cyclic/ class AssocOp(Expr): """ Associative operations, can separate noncommutative and commutative parts. (a op b) op c == a op (b op c) == a op b op c. Base class for Add and Mul. This is an abstract base class, concrete derived classes must define the attribute `identity`. """ # for performance reason, we don't let is_commutative go to assumptions, # and keep it right here __slots__ = ['is_commutative'] @cacheit def __new__(cls, *args, **options): if len(args) == 0: return cls.identity args = map(_sympify, args) if len(args) == 1: return args[0] if not options.pop('evaluate', True): obj = Expr.__new__(cls, *args) obj.is_commutative = all(a.is_commutative for a in args) return obj c_part, nc_part, order_symbols = cls.flatten(args) if len(c_part) + len(nc_part) <= 1: if c_part: obj = c_part[0] elif nc_part: obj = nc_part[0] else: obj = cls.identity else: obj = Expr.__new__(cls, *(c_part + nc_part)) obj.is_commutative = not nc_part if order_symbols is not None: obj = C.Order(obj, *order_symbols) return obj def _new_rawargs(self, *args, **kwargs): """create new instance of own class with args exactly as provided by caller but returning the self class identity if args is empty. This is handy when we want to optimize things, e.g. >>> from sympy import Mul, symbols, S >>> from sympy.abc import x, y >>> e = Mul(3, x, y) >>> e.args (3, x, y) >>> Mul(*e.args[1:]) x*y >>> e._new_rawargs(*e.args[1:]) # the same as above, but faster x*y Note: use this with caution. There is no checking of arguments at all. This is best used when you are rebuilding an Add or Mul after simply removing one or more terms. If modification which result, for example, in extra 1s being inserted (as when collecting an expression's numerators and denominators) they will not show up in the result but a Mul will be returned nonetheless: >>> m = (x*y)._new_rawargs(S.One, x); m x >>> m == x False >>> m.is_Mul True Another issue to be aware of is that the commutativity of the result is based on the commutativity of self. If you are rebuilding the terms that came from a commutative object then there will be no problem, but if self was non-commutative then what you are rebuilding may now be commutative. Although this routine tries to do as little as possible with the input, getting the commutativity right is important, so this level of safety is enforced: commutativity will always be recomputed if either a) self has no is_commutate attribute or b) self is non-commutative and kwarg `reeval=False` has not been passed. If you don't have an existing Add or Mul and need one quickly, try the following. >>> m = object.__new__(Mul) >>> m._new_rawargs(x, y) x*y Note that the commutativity is always computed in this case since m doesn't have an is_commutative attribute; reeval is ignored: >>> _.is_commutative True >>> hasattr(m, 'is_commutative') False >>> m._new_rawargs(x, y, reeval=False).is_commutative True It is possible to define the commutativity of m. If it's False then the new Mul's commutivity will be re-evaluated: >>> m.is_commutative = False >>> m._new_rawargs(x, y).is_commutative True But if reeval=False then a non-commutative self can pass along its non-commutativity to the result (but at least you have to *work* to get this wrong): >>> m._new_rawargs(x, y, reeval=False).is_commutative False """ if len(args) > 1: obj = Expr.__new__(type(self), *args) # NB no assumptions for Add/Mul if (hasattr(self, 'is_commutative') and (self.is_commutative or not kwargs.pop('reeval', True))): obj.is_commutative = self.is_commutative else: obj.is_commutative = all(a.is_commutative for a in args) elif len(args) == 1: obj = args[0] else: obj = self.identity return obj @classmethod def flatten(cls, seq): # apply associativity, no commutivity property is used new_seq = [] while seq: o = seq.pop(0) if o.__class__ is cls: # classes must match exactly seq = list(o[:]) + seq continue new_seq.append(o) # c_part, nc_part, order_symbols return [], new_seq, None def _matches_commutative(self, expr, repl_dict={}, evaluate=False): """ Matches Add/Mul "pattern" to an expression "expr". repl_dict ... a dictionary of (wild: expression) pairs, that get returned with the results evaluate .... if True, then repl_dict is first substituted into the pattern, and then _matches_commutative is run This function is the main workhorse for Add/Mul. For instance: >>> from sympy import symbols, Wild, sin >>> a = Wild("a") >>> b = Wild("b") >>> c = Wild("c") >>> x, y, z = symbols("x y z") >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z) {a_: x, b_: y, c_: z} In the example above, "a+sin(b)*c" is the pattern, and "x+sin(y)*z" is the expression. The repl_dict contains parts that were already matched, and the "evaluate=True" kwarg tells _matches_commutative to substitute this repl_dict into pattern. For example here: >>> (a+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x}, evaluate=True) {a_: x, b_: y, c_: z} _matches_commutative substitutes "x" for "a" in the pattern and calls itself again with the new pattern "x+b*c" and evaluate=False (default): >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z, repl_dict={a: x}) {a_: x, b_: y, c_: z} the only function of the repl_dict now is just to return it in the result, e.g. if you omit it: >>> (x+sin(b)*c)._matches_commutative(x+sin(y)*z) {b_: y, c_: z} the "a: x" is not returned in the result, but otherwise it is equivalent. """ # apply repl_dict to pattern to eliminate fixed wild parts if evaluate: return self.subs(repl_dict.items()).matches(expr, repl_dict) # handle simple patterns if self == expr: return repl_dict d = self._matches_simple(expr, repl_dict) if d is not None: return d # eliminate exact part from pattern: (2+a+w1+w2).matches(expr) -> (w1+w2).matches(expr-a-2) wild_part = [] exact_part = [] from function import WildFunction from symbol import Wild for p in self.args: if p.has(Wild, WildFunction) and (not p in expr): # not all Wild should stay Wilds, for example: # (w2+w3).matches(w1) -> (w1+w3).matches(w1) -> w3.matches(0) wild_part.append(p) else: exact_part.append(p) if exact_part: newpattern = self.func(*wild_part) newexpr = self._combine_inverse(expr, self.func(*exact_part)) return newpattern.matches(newexpr, repl_dict) # now to real work ;) expr_list = self.make_args(expr) for last_op in reversed(expr_list): for w in reversed(wild_part): d1 = w.matches(last_op, repl_dict) if d1 is not None: d2 = self.subs(d1.items()).matches(expr, d1) if d2 is not None: return d2 return def _eval_template_is_attr(self, is_attr): # return True if all elements have the property r = True for t in self.args: a = getattr(t, is_attr) if a is None: return if r and not a: r = False return r def _eval_evalf(self, prec): return self.func(*[s._evalf(prec) for s in self.args]) @classmethod def make_args(cls, expr): """ Return a sequence of elements `args` such that cls(*args) == expr >>> from sympy import Symbol, Mul, Add >>> x, y = map(Symbol, 'xy') >>> Mul.make_args(x*y) (x, y) >>> Add.make_args(x*y) (x*y,) >>> set(Add.make_args(x*y + y)) == set([y, x*y]) True """ if isinstance(expr, cls): return expr.args else: return (expr,) class ShortCircuit(Exception): pass class LatticeOp(AssocOp): """ Join/meet operations of an algebraic lattice[1]. These binary operations are associative (op(op(a, b), c) = op(a, op(b, c))), commutative (op(a, b) = op(b, a)) and idempotent (op(a, a) = op(a) = a). Common examples are AND, OR, Union, Intersection, max or min. They have an identity element (op(identity, a) = a) and an absorbing element conventionally called zero (op(zero, a) = zero). This is an abstract base class, concrete derived classes must declare attributes zero and identity. All defining properties are then respected. >>> from sympy import Integer >>> from sympy.core.operations import LatticeOp >>> class my_join(LatticeOp): ... zero = Integer(0) ... identity = Integer(1) >>> my_join(2, 3) == my_join(3, 2) True >>> my_join(2, my_join(3, 4)) == my_join(2, 3, 4) True >>> my_join(0, 1, 4, 2, 3, 4) 0 >>> my_join(1, 2) 2 References: [1] - http://en.wikipedia.org/wiki/Lattice_(order) """ is_commutative = True def __new__(cls, *args, **options): args = (sympify(arg) for arg in args) try: _args = frozenset(cls._new_args_filter(args)) except ShortCircuit: return cls.zero if not _args: return cls.identity elif len(_args) == 1: return set(_args).pop() else: obj = Expr.__new__(cls, _args) obj._argset = _args return obj @classmethod def _new_args_filter(cls, arg_sequence): """Generator filtering args""" for arg in arg_sequence: if arg == cls.zero: raise ShortCircuit(arg) elif arg == cls.identity: continue elif arg.func == cls: for x in arg.iter_basic_args(): yield x else: yield arg @classmethod def make_args(cls, expr): """ Return a sequence of elements `args` such that cls(*args) == expr >>> from sympy import Symbol, Mul, Add >>> x, y = map(Symbol, 'xy') >>> Mul.make_args(x*y) (x, y) >>> Add.make_args(x*y) (x*y,) >>> set(Add.make_args(x*y + y)) == set([y, x*y]) True """ if isinstance(expr, cls): return expr._argset else: return frozenset([expr]) @property def args(self): return tuple(self._argset) @staticmethod def _compare_pretty(a, b): return cmp(str(a), str(b)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/cache.py0000644000175000017500000002422112014170666023445 0ustar georgeskgeorgesk""" Caching facility for SymPy """ # TODO: refactor CACHE & friends into class? # global cache registry: CACHE = [] # [] of # (item, {} or tuple of {}) from sympy.core.logic import fuzzy_bool from sympy.core.decorators import wraps def print_cache(): """print cache content""" for item, cache in CACHE: item = str(item) head = '='*len(item) print head print item print head if not isinstance(cache, tuple): cache = (cache,) shown = False else: shown = True for i, kv in enumerate(cache): if shown: print '\n*** %i ***\n' % i for k, v in kv.iteritems(): print ' %s :\t%s' % (k, v) def clear_cache(): """clear cache content""" for item, cache in CACHE: if not isinstance(cache, tuple): cache = (cache,) for kv in cache: kv.clear() ######################################## def __cacheit_nocache(func): return func def __cacheit(func): """caching decorator. important: the result of cached function must be *immutable* **Example** >>> from sympy.core.cache import cacheit >>> @cacheit ... def f(a,b): ... return a+b >>> @cacheit ... def f(a,b): ... return [a,b] # <-- WRONG, returns mutable object to force cacheit to check returned results mutability and consistency, set environment variable SYMPY_USE_CACHE to 'debug' """ func._cache_it_cache = func_cache_it_cache = {} CACHE.append((func, func_cache_it_cache)) @wraps(func) def wrapper(*args, **kw_args): """ Assemble the args and kw_args to compute the hash. It is important that kw_args be standardized since if they have the same meaning but in different forms (e.g. one kw_arg having a value of 1 for an object and another object with identical args but a kw_arg of True) then two different hashes will be computed and the two objects will not be identical. """ if kw_args: keys = kw_args.keys() # make keywords all the same for k in keys: kw_args[k] = fuzzy_bool(kw_args[k]) keys.sort() items = [(k+'=', kw_args[k]) for k in keys] k = args + tuple(items) else: k = args k = k + tuple(map(lambda x: type(x), k)) try: return func_cache_it_cache[k] except KeyError: pass func_cache_it_cache[k] = r = func(*args, **kw_args) return r return wrapper def __cacheit_debug(func): """cacheit + code to check cache consistency""" cfunc = __cacheit(func) @wraps(func) def wrapper(*args, **kw_args): # always call function itself and compare it with cached version r1 = func (*args, **kw_args) r2 = cfunc(*args, **kw_args) # try to see if the result is immutable # # this works because: # # hash([1,2,3]) -> raise TypeError # hash({'a':1, 'b':2}) -> raise TypeError # hash((1,[2,3])) -> raise TypeError # # hash((1,2,3)) -> just computes the hash hash(r1), hash(r2) # also see if returned values are the same assert r1 == r2 return r1 return wrapper class MemoizerArg: """ See Memoizer. """ def __init__(self, allowed_types, converter = None, name = None): self._allowed_types = allowed_types self.converter = converter self.name = name def fix_allowed_types(self, have_been_here={}): from basic import C i = id(self) if have_been_here.get(i): return allowed_types = self._allowed_types if isinstance(allowed_types, str): self.allowed_types = getattr(C, allowed_types) elif isinstance(allowed_types, (tuple, list)): new_allowed_types = [] for t in allowed_types: if isinstance(t, str): t = getattr(C, t) new_allowed_types.append(t) self.allowed_types = tuple(new_allowed_types) else: self.allowed_types = allowed_types have_been_here[i] = True return def process(self, obj, func, index = None): if isinstance(obj, self.allowed_types): if self.converter is not None: obj = self.converter(obj) return obj func_src = '%s:%s:function %s' % (func.func_code.co_filename, func.func_code.co_firstlineno, func.func_name) if index is None: raise ValueError('%s return value must be of type %r but got %r' % (func_src, self.allowed_types, obj)) if isinstance(index, (int,long)): raise ValueError('%s %s-th argument must be of type %r but got %r' % (func_src, index, self.allowed_types, obj)) if isinstance(index, str): raise ValueError('%s %r keyword argument must be of type %r but got %r' % (func_src, index, self.allowed_types, obj)) raise NotImplementedError(repr((index,type(index)))) class Memoizer: """ Memoizer function decorator generator. Features: - checks that function arguments have allowed types - optionally apply converters to arguments - cache the results of function calls - optionally apply converter to function values Usage: @Memoizer(, MemoizerArg(), MemoizerArg(, ), MemoizerArg(, , name=), ... return_value_converter = ) def function(, ): ... Details: - if allowed type is string object then there `C` must have attribute with the string name that is used as the allowed type --- this is needed for applying Memoizer decorator to Basic methods when Basic definition is not defined. Restrictions: - arguments must be immutable - when function values are mutable then one must use return_value_converter to deep copy the returned values Ref: http://en.wikipedia.org/wiki/Memoization """ def __init__(self, *arg_templates, **kw_arg_templates): new_arg_templates = [] for t in arg_templates: if not isinstance(t, MemoizerArg): t = MemoizerArg(t) new_arg_templates.append(t) self.arg_templates = tuple(new_arg_templates) return_value_converter = kw_arg_templates.pop('return_value_converter', None) self.kw_arg_templates = kw_arg_templates.copy() for template in self.arg_templates: if template.name is not None: self.kw_arg_templates[template.name] = template if return_value_converter is None: self.return_value_converter = lambda obj: obj else: self.return_value_converter = return_value_converter def fix_allowed_types(self, have_been_here={}): i = id(self) if have_been_here.get(i): return for t in self.arg_templates: t.fix_allowed_types() for k,t in self.kw_arg_templates.items(): t.fix_allowed_types() have_been_here[i] = True def __call__(self, func): cache = {} value_cache = {} CACHE.append((func, (cache, value_cache))) @wraps(func) def wrapper(*args, **kw_args): kw_items = tuple(kw_args.items()) try: return self.return_value_converter(cache[args,kw_items]) except KeyError: pass self.fix_allowed_types() new_args = tuple([template.process(a,func,i) for (a, template, i) in zip(args, self.arg_templates, range(len(args)))]) assert len(args)==len(new_args) new_kw_args = {} for k, v in kw_items: template = self.kw_arg_templates[k] v = template.process(v, func, k) new_kw_args[k] = v new_kw_items = tuple(new_kw_args.items()) try: return self.return_value_converter(cache[new_args, new_kw_items]) except KeyError: r = func(*new_args, **new_kw_args) try: try: r = value_cache[r] except KeyError: value_cache[r] = r except TypeError: pass cache[new_args, new_kw_items] = cache[args, kw_items] = r return self.return_value_converter(r) return wrapper class Memoizer_nocache(Memoizer): def __call__(self, func): # XXX I would be happy just to return func, but we need to provide # argument convertion, and it is really needed for e.g. Float("0.5") @wraps(func) def wrapper(*args, **kw_args): kw_items = tuple(kw_args.items()) self.fix_allowed_types() new_args = tuple([template.process(a,func,i) for (a, template, i) in zip(args, self.arg_templates, range(len(args)))]) assert len(args)==len(new_args) new_kw_args = {} for k, v in kw_items: template = self.kw_arg_templates[k] v = template.process(v, func, k) new_kw_args[k] = v r = func(*new_args, **new_kw_args) return self.return_value_converter(r) return wrapper # SYMPY_USE_CACHE=yes/no/debug def __usecache(): import os return os.getenv('SYMPY_USE_CACHE', 'yes').lower() usecache = __usecache() if usecache=='no': Memoizer = Memoizer_nocache cacheit = __cacheit_nocache elif usecache=='yes': cacheit = __cacheit elif usecache=='debug': cacheit = __cacheit_debug # a lot slower else: raise RuntimeError('unknown argument in SYMPY_USE_CACHE: %s' % usecache) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/containers.py0000644000175000017500000000612112014170666024546 0ustar georgeskgeorgesk"""Module for Sympy containers (Sympy objects that store other Sympy objects) The containers implemented in this module are subclassed to Basic. They are supposed to work seamlessly within the Sympy framework. """ from basic import Basic from sympify import sympify class Tuple(Basic): """ Wrapper around the builtin tuple object The Tuple is a subclass of Basic, so that it works well in the Sympy framework. The wrapped tuple is available as self.args, but you can also access elements or slices with [:] syntax. >>> from sympy import symbols >>> from sympy.core.containers import Tuple >>> a, b, c, d = symbols('a b c d') >>> Tuple(a, b, c)[1:] (b, c) >>> Tuple(a, b, c).subs(a, d) (d, b, c) """ def __new__(cls, *args, **assumptions): args = [ sympify(arg) for arg in args ] obj = Basic.__new__(cls, *args, **assumptions) return obj def __getitem__(self,i): if isinstance(i,slice): indices = i.indices(len(self)) return Tuple(*[self.args[i] for i in range(*indices)]) return self.args[i] def __len__(self): return len(self.args) def __contains__(self, item): return item in self.args def __iter__(self): return iter(self.args) def __add__(self, other): if isinstance(other, Tuple): return Tuple(*(self.args + other.args)) elif isinstance(other, tuple): return Tuple(*(self.args + other)) else: return NotImplemented def __radd__(self, other): if isinstance(other, Tuple): return Tuple(*(other.args + self.args)) elif isinstance(other, tuple): return Tuple(*(other + self.args)) else: return NotImplemented def __eq__(self, other): if isinstance(other, Basic): return super(Tuple, self).__eq__(other) return self.args == other def __ne__(self, other): if isinstance(other, Basic): return super(Tuple, self).__ne__(other) return self.args != other def __hash__(self): return hash(self.args) def _to_mpmath(self, prec): return tuple([a._to_mpmath(prec) for a in self.args]) def tuple_wrapper(method): """ Decorator that converts any tuple in the function arguments into a Tuple. The motivation for this is to provide simple user interfaces. The user can call a function with regular tuples in the argument, and the wrapper will convert them to Tuples before handing them to the function. >>> from sympy.core.containers import tuple_wrapper, Tuple >>> def f(*args): ... return args >>> g = tuple_wrapper(f) The decorated function g sees only the Tuple argument: >>> g(0, (1, 2), 3) (0, (1, 2), 3) """ def wrap_tuples(*args, **kw_args): newargs=[] for arg in args: if type(arg) is tuple: newargs.append(Tuple(*arg)) else: newargs.append(arg) return method(*newargs, **kw_args) return wrap_tuples wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/multidimensional.py0000644000175000017500000000752112014170666025763 0ustar georgeskgeorgesk""" Provides functionality for multidimensional usage of scalar-functions. Read the vectorize docstring for more details. """ from sympy.core.decorators import wraps def apply_on_element(f, args, kwargs, n): """ Returns a structure with the same dimension as the specified argument, where each basic element is replaced by the function f applied on it. All other arguments stay the same. """ # Get the specified argument. if isinstance(n, int): structure = args[n] is_arg = True elif isinstance(n, str): structure = kwargs[n] is_arg = False # Define reduced function that is only dependend of the specified argument. def f_reduced(x): if hasattr(x, "__iter__"): return map(f_reduced, x) else: if is_arg: args[n] = x else: kwargs[n] = x return f(*args, **kwargs) # f_reduced will call itself recursively so that in the end f is applied to # all basic elements. return map(f_reduced, structure) def iter_copy(structure): """ Returns a copy of an iterable object (also copying all embedded iterables). """ l = [] for i in structure: if hasattr(i, "__iter__"): l.append(iter_copy(i)) else: l.append(i) return l def structure_copy(structure): """ Returns a copy of the given structure (numpy-array, list, iterable, ..). """ if hasattr(structure, "copy"): return structure.copy() return iter_copy(structure) class vectorize: """ Generalizes a function taking scalars to accept multidimensional arguments. For example:: (1) @vectorize(0) def sin(x): .... sin([1, x, y]) --> [sin(1), sin(x), sin(y)] (2) @vectorize(0,1) def diff(f(y), y) .... diff([f(x,y,z),g(x,y,z),h(x,y,z)], [x,y,z]) --> [[d/dx f, d/dy f, d/dz f], [d/dx g, d/dy g, d/dz g], [d/dx h, d/dy h, d/dz h]] """ def __init__(self, *mdargs): """ The given numbers and strings characterize the arguments that will be treated as data structures, where the decorated function will be applied to every single element. If no argument is given, everything is treated multidimensional. """ for a in mdargs: assert isinstance(a, (int,str)) self.mdargs = mdargs def __call__(self, f): """ Returns a wrapper for the one-dimensional function that can handle multidimensional arguments. """ @wraps(f) def wrapper(*args, **kwargs): # Get arguments that should be treated multidimensional if self.mdargs: mdargs = self.mdargs else: mdargs = range(len(args)) + kwargs.keys() arglength = len(args) for n in mdargs: if isinstance(n, int): if n>=arglength: continue entry = args[n] is_arg = True elif isinstance(n, str): try: entry = kwargs[n] except KeyError: continue is_arg = False if hasattr(entry, "__iter__"): # Create now a copy of the given array and manipulate then # the entries directly. if is_arg: args = list(args) args[n] = structure_copy(entry) else: kwargs[n] = structure_copy(entry) result = apply_on_element(wrapper, args, kwargs, n) return result return f(*args, **kwargs) return wrapper wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/facts.py0000644000175000017500000007210012014170666023501 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- """This is rule-based deduction system for SymPy The whole thing is split into two parts - rules compilation and preparation of tables - runtime inference For rule-based inference engines, the classical work is RETE algorithm [1], [2] Although we are not implementing it in full (or even significantly) it's still still worth a read to understand the underlying ideas. In short, every rule in a system of rules is one of two forms: - atom -> ... (alpha rule) - And(atom1, atom2, ...) -> ... (beta rule) The major complexity is in efficient beta-rules processing and usually for an expert system a lot of effort goes into code that operates on beta-rules. Here we take minimalistic approach to get something usable first. - (preparation) of alpha- and beta- networks, everything except - (runtime) FactRules.deduce_all_facts _____________________________________ ( Kirr: I've never thought that doing ) ( logic stuff is that difficult... ) ------------------------------------- o ^__^ o (oo)\_______ (__)\ )\/\ ||----w | || || Some references on the topic ---------------------------- [1] http://en.wikipedia.org/wiki/Rete_algorithm [2] http://reports-archive.adm.cs.cmu.edu/anon/1995/CMU-CS-95-113.pdf http://en.wikipedia.org/wiki/Propositional_formula http://en.wikipedia.org/wiki/Inference_rule http://en.wikipedia.org/wiki/List_of_rules_of_inference """ from logic import fuzzy_not, name_not, Logic, And, Not def list_populate(l, item, skipif=None): """update list with an item, but only if it is not already there""" if item != skipif and (item not in l): l.append(item) # XXX this prepares forward-chaining rules for alpha-network # XXX better words? # NB: this procedure does not 'know' about not-relations # i.e. it treats 'a' and '!a' as independent variables while deriving implications. # # only in the end, we check that the whole result is consitent def deduce_alpha_implications(implications): """deduce all implications Description by example ---------------------- given set of logic rules: a -> b b -> c we deduce all possible rules: a -> b, c b -> c implications: [] of (a,b) return: {} of a -> [b, c, ...] """ res = {} # a -> [] of implications(a) # NOTE: this could be optimized with e.g. toposort (?), but we need this # only at FactRules creation time (i.e. only once), so there is no demand # in further optimising this. for a,b in implications: if a==b: continue # skip a->a cyclic input I = res.setdefault(a,[]) list_populate(I,b) # UC: ------------------------- # | | # v | # a='rat' -> b='real' ==> (a_='int') -> 'real' for a_ in res: ra_ = res[a_] if a in ra_: list_populate(ra_, b, skipif=a_) # UC: # a='pos' -> b='real' && (already have b='real' -> 'complex') # || # vv # a='pos' -> 'complex' if b in res: ra = res[a] for b_ in res[b]: list_populate(ra, b_, skipif=a) #print 'D:', res # let's see if the result is consistent for a, impl in res.iteritems(): na = name_not(a) if na in impl: raise ValueError('implications are inconsistent: %s -> %s %s' % (a, na, impl)) return res def apply_beta_to_alpha_route(alpha_implications, beta_rules): """apply additional beta-rules (And conditions) to already-built alpha implication tables TODO: write about - static extension of alpha-chains - attaching refs to beta-nodes to alpha chains e.g. alpha_implications: a -> [b, !c, d] b -> [d] ... beta_rules: &(b,d) -> e then we'll extend a's rule to the following a -> [b, !c, d, e] """ x_impl = {} for x in alpha_implications.keys(): x_impl[x] = (alpha_implications[x][:], []) # let's ensure that every beta-rule has appropriate entry (maybe even # empty) in the table. for bidx, (bcond,bimpl) in enumerate(beta_rules): for bk in bcond.args: if bk in x_impl: continue x_impl[bk] = ([], []) # XXX maybe simplify passes logic? # we do it in 2 phases: # # 1st phase -- only do static extensions to alpha rules # 2nd phase -- attach beta-nodes which can be possibly triggered by an # alpha-chain phase=1 while True: seen_static_extension=False for bidx, (bcond,bimpl) in enumerate(beta_rules): assert isinstance(bcond, And) for x, (ximpls, bb) in x_impl.iteritems(): # A: x -> a B: &(...) -> x (non-informative) if x == bimpl: # XXX bimpl may become a list continue # A: ... -> a B: &(...) -> a (non-informative) if bimpl in ximpls: continue # A: x -> a B: &(a,!x) -> ... (will never trigger) if Not(x) in bcond.args: continue # A: x -> a... B: &(!a,...) -> ... (will never trigger) # A: x -> a... B: &(...) -> !a (will never trigger) maytrigger=True for xi in ximpls: if (Not(xi) in bcond.args) or (Not(xi) == bimpl): maytrigger=False break if not maytrigger: continue # A: x -> a,b B: &(a,b) -> c (static extension) # | # A: x -> a,b,c <-----------------------+ for barg in bcond.args: if not ( (barg == x) or (barg in ximpls) ): break else: assert phase==1 list_populate(ximpls, bimpl) # XXX bimpl may become a list # we introduced new implication - now we have to restore # completness of the whole set. bimpl_impl = x_impl.get(bimpl) if bimpl_impl is not None: for _ in bimpl_impl[0]: list_populate(ximpls, _) seen_static_extension=True continue # does this beta-rule even has a chance to be triggered ? if phase == 2: for barg in bcond.args: if (barg == x) or (barg in ximpls): bb.append( bidx ) break # no static extensions was seen at this pass -- lets move to phase2 if phase==1 and (not seen_static_extension): phase = 2 continue # let's finish at the end of phase2 if phase==2: break return x_impl # XXX this is something related to backward-chaining for alpha-network (?) # TODO adapt to handle beta nodes ? # TODO we need to sort prerequisites somehow, so that more closer prerequisites # would be tried first. e.g. # # nonzero <- [zero, integer, ..., prime], but not # nonzero <- [prime, ...] # # this could be done in other place... def rules_2prereq(rules): """build prerequisites table from rules Description by example ---------------------- given set of logic rules: a -> b, c b -> c we build prerequisites (from what points something can be deduced): b <- a c <- a, b rules: {} of a -> [b, c, ...] return: {} of c <- [a, b, ...] Note however, that this prerequisites may be *not* enough to prove a fact. An example is 'a -> b' rule, where prereq(a) is b, and prereq(b) is a. That's because a=T -> b=T, and b=F -> a=F, but a=F -> b=? """ prereq = {} for a, impl in rules.iteritems(): for i in impl: pa = prereq.setdefault(i,[]) pa.append(a) return prereq def split_rules_tt_tf_ft_ff(rules): """split alpha-rules into T->T & T->F & F->T & F->F chains and also rewrite them to be free of not-names Example ------- 'a' -> ['b', '!c'] will be split into 'a' -> ['b'] # tt: a -> b 'a' -> ['c'] # tf: a -> !c and '!a' -> ['b'] will become 'b' -> ['a'] # ft: !b -> a """ # print 'split_rules_tt_tf_ft_ff' # print rules tt = {} tf = {} ft = {} for k,impl in rules.iteritems(): # k is not not-name if k[:1] != '!': for i in impl: if i[:1] != '!': dd = tt else: dd = tf i = i[1:] I = dd.setdefault(k,[]) list_populate(I, i) # k is not-name else: k = k[1:] for i in impl: if i[:1] != '!': dd = ft else: dd = tt i = i[1:] I = dd.setdefault(i,[]) list_populate(I, k) # FF is related to TT ff = {} for k,impl in tt.iteritems(): for i in impl: I = ff.setdefault(i,[]) I.append(k) return tt, tf, ft, ff ################ # RULES PROVER # ################ # XXX only for debugging -- kill me ? dbg_level = 0 class TautologyDetected(Exception): """(internal) Prover uses it for reporting detected tautology""" pass class Prover(object): """ai - prover of logic rules given a set of initial rules, Prover tries to prove all possible rules which follow from given premises. As a result proved_rules are always either in one of two forms: alpha or beta: Alpha rules ----------- This are rules of the form:: a -> b & c & d & ... Beta rules ---------- This are rules of the form:: &(a,b,...) -> c & d & ... i.e. beta rules are join conditions that say that something follows when *several* facts are true at the same time. """ __slots__ = ['proved_rules', # [] of a,b (a -> b rule) '_rules_seen', # setof all seen rules ] def __init__(self): self.proved_rules = [] self._rules_seen = set() def print_proved(self, title='proved rules'): print '\n--- %s ---' % title for a,b in self.proved_rules: print '%s\t-> %s' % (a,b) print ' - - - - - ' print def print_beta(self, title='proved rules (beta)'): print '\n --- %s ---' % title for n, (a,b) in enumerate(self.rules_beta): print '[#%i] %s\t-> %s' % (n,a,b) print ' - - - - - ' print def split_alpha_beta(self): """split proved rules into alpha and beta chains""" rules_alpha = [] # a -> b rules_beta = [] # &(...) -> b for a,b in self.proved_rules: if isinstance(a, And): rules_beta.append( (a,b) ) else: rules_alpha.append((a,b) ) return rules_alpha, rules_beta @property def rules_alpha(self): return self.split_alpha_beta()[0] @property def rules_beta(self): return self.split_alpha_beta()[1] # XXX rename? def process_rule(self, a, b): """process a -> b rule""" # TODO write more? # XXX this seems to be needed, but I can't get it to work without cycles # XXX sigh ... #if isinstance(a, And): # a = a.expand() #if isinstance(b, And): # b = b.expand() # print ' -- %s\t-> %s' % (a,b) # we don't want rules whose premise is F, or who are trivial # skip: F -> ... # skip: ... -> F # skip: ... -> T if (not a) or isinstance(b, bool): return # XXX can't we perform without this? # skip: T -> ... if isinstance(a, bool): return # cycle detection: we don't want to process already seen rules if (a,b) in self._rules_seen: # already seen rule # XXX is it possible to design process_rule so we don't need # XXX this recursion detector? For me current answer is 'no' # -- kirr return else: self._rules_seen.add((a,b)) # this is the core of processing try: self._process_rule(a, b) except TautologyDetected, t: #print 'Tautology: %s -> %s (%s)' % (t.args[0], t.args[1], t.args[2]) pass else: # XXX negating here seems like the way to go. However we do # XXX *not* do it -- read below about why ... # # if not tautology, process negative rule as well # a -> b --> !b -> !a # self.process_rule(Not(b), Not(a)) # Unfortunately we can't handle the above negation -- in the # end our cycles detector is unable to catch all cycles, # especially ones where noise is mixed to already-known-facts, # e.g. # # say # # a -> b # # is known fact, and at some point we get # # a -> |(....., b) # # At present we are unable to detect this situation. # # I'm sorry -- I have no deep understanding of this logic # processing stuff and I'm very short on time ... # -- kirr pass # --- processing --- def _process_rule(self, a, b): # right part first if isinstance(b, Logic): # a -> b & c --> a -> b ; a -> c # (?) FIXME this is only correct when b & c != null ! if b.op == '&': for barg in b.args: self.process_rule(a, barg) # a -> b | c --> !b & !c -> !a # --> a & !b -> c & !b # --> a & !c -> b & !c # # NB: the last two rewrites add 1 term, so the rule *grows* in size. # NB: without catching terminating conditions this could continue infinitely elif b.op == '|': # detect tautology first if not isinstance(a, Logic): # Atom # tautology: a -> a|c|... if a in b.args: raise TautologyDetected(a,b, 'a -> a|c|...') self.process_rule( And( *[Not(barg) for barg in b.args] ), Not(a) ) for bidx in range(len(b.args)): barg = b.args[bidx] brest= b.args[:bidx] + b.args[bidx+1:] self.process_rule( And( a, Not(barg) ), And( b.__class__(*brest), Not(barg) ) ) else: raise ValueError('unknown b.op %r' % b.op) # left part elif isinstance(a, Logic): # a & b -> c --> IRREDUCIBLE CASE -- WE STORE IT AS IS # (this will be the basis of beta-network) if a.op == '&': # at this stage we should have right part alredy in simple form assert not isinstance(b, Logic) # tautology: a & b -> a if b in a.args: raise TautologyDetected(a,b, 'a & b -> a') self.proved_rules.append((a,b)) # XXX NOTE at present we ignore !c -> !a | !b # a | b -> c --> a -> c ; b -> c elif a.op == '|': # tautology: a | b -> a if b in a.args: raise TautologyDetected(a,b, 'a | b -> a') for aarg in a.args: self.process_rule(aarg, b) else: raise ValueError('unknown a.op %r' % a.op) else: # now this is the case where both `a` and `b` are atoms na, nb = name_not(a), name_not(b) self.proved_rules.append((a,b)) # a -> b # XXX one day we may not need this -- read comments about # XXX negation close to the end of `process_rule` self.proved_rules.append((nb,na)) # !b -> !a def dbg_process_rule_2(a, b): global dbg_level print '%s%s\t-> %s' % (' '*(2*dbg_level), a, b) dbg_level += 1 try: old_process_rule_2(a, b) finally: dbg_level -= 1 #old_process_rule_2 = process_rule_2 #process_rule_2 = dbg_process_rule_2 ######################################## # TODO link to RETE ? # TODO link to proposition logic ? class FactRules: """Rules that describe how to deduce facts in logic space When defined, these rules allow implications to quickly be determined for a set of facts. For this precomputed deduction tables are used. see `deduce_all_facts` (forward-chaining) Also it is possible to gather prerequisites for a fact, which is tried to be proven. (backward-chaining) Definition Syntax ----------------- a -> b -- a=T -> b=T (and automatically b=F -> a=F) a -> !b -- a=T -> b=F a == b -- a -> b & b -> a a -> b & c -- a=T -> b=T & c=T # TODO b | c Internals --------- {} k -> [] of implications: .rel_tt k=T -> k2=T .rel_tf k=T -> k2=F .rel_ff k=F -> k2=F .rel_ft k=F -> k2=T .rel_tbeta k=T -> [] of possibly triggering # of beta-rules .rel_fbeta k=F -> ------------------//--------------------- .rels -- {} k -> tt, tf, ff, ft (list of implications for k) .prereq -- {} k <- [] of k's prerequisites .defined_facts -- set of defined fact names """ def __init__(self, rules): """Compile rules into internal lookup tables""" if isinstance(rules, basestring): rules = rules.splitlines() # --- parse and process rules --- P = Prover() for rule in rules: # XXX `a` is hardcoded to be always atom a, op, b = rule.split(None, 2) a = Logic.fromstring(a) b = Logic.fromstring(b) if op == '->': P.process_rule(a, b) elif op == '==': P.process_rule(a, b) P.process_rule(b, a) else: raise ValueError('unknown op %r' % op) #P.print_proved('RULES') #P.print_beta('BETA-RULES') # --- build deduction networks --- # deduce alpha implications impl_a = deduce_alpha_implications(P.rules_alpha) # now: # - apply beta rules to alpha chains (static extension), and # - further associate beta rules to alpha chain (for inference at runtime) impl_ab = apply_beta_to_alpha_route(impl_a, P.rules_beta) if 0: print '\n --- ALPHA-CHAINS (I) ---' for a,b in impl_a.iteritems(): print '%s\t-> α(%2i):%s' % (a,len(b),b) print ' - - - - - ' print if 0: print '\n --- ALPHA-CHAINS (II) ---' for a,(b,bb) in impl_ab.iteritems(): print '%s\t-> α(%2i):%s β(%s)' % (a,len(b),b, ' '.join(str(x) for x in bb)) print ' - - - - - ' print # extract defined fact names self.defined_facts = set() for k in impl_ab.keys(): if k[:1] == '!': k = k[1:] self.defined_facts.add(k) #print 'defined facts: (%2i) %s' % (len(self.defined_facts), self.defined_facts) # now split each rule into four logic chains # (removing betaidxs from impl_ab view) (XXX is this needed?) impl_ab_ = dict( (k,impl) for k, (impl,betaidxs) in impl_ab.iteritems()) rel_tt, rel_tf, rel_ft, rel_ff = split_rules_tt_tf_ft_ff(impl_ab_) # XXX merge me with split_rules_tt_tf_ft_ff ? rel_tbeta = {} rel_fbeta = {} for k, (impl,betaidxs) in impl_ab.iteritems(): if k[:1] == '!': rel_xbeta = rel_fbeta k = name_not(k) else: rel_xbeta = rel_tbeta rel_xbeta[k] = betaidxs self.rel_tt = rel_tt self.rel_tf = rel_tf self.rel_tbeta = rel_tbeta self.rel_ff = rel_ff self.rel_ft = rel_ft self.rel_fbeta = rel_fbeta self.beta_rules = P.rules_beta # build rels (forward chains) K = set (rel_tt.keys()) K.update(rel_tf.keys()) K.update(rel_ff.keys()) K.update(rel_ft.keys()) rels = {} empty= () for k in K: tt = rel_tt.get(k,empty) tf = rel_tf.get(k,empty) ft = rel_ft.get(k,empty) ff = rel_ff.get(k,empty) tbeta = rel_tbeta.get(k,empty) fbeta = rel_fbeta.get(k,empty) rels[k] = tt, tf, tbeta, ft, ff, fbeta self.rels = rels # build prereq (backward chains) prereq = {} for rel in [rel_tt, rel_tf, rel_ff, rel_ft]: rel_prereq = rules_2prereq(rel) for k,pitems in rel_prereq.iteritems(): kp = prereq.setdefault(k,[]) for p in pitems: list_populate(kp, p) self.prereq = prereq # --- DEDUCTION ENGINE: RUNTIME CORE --- # TODO: add proper support for U (None), i.e. # integer=U -> rational=U ??? (XXX i'm not sure) def deduce_all_facts(self, facts, base=None): """Deduce all facts from known facts ({} or [] of (k,v)) ********************************************* * This is the workhorse, so keep it *fast*. * ********************************************* base -- previously known facts (must be: fully deduced set) attention: base is modified *in place* /optional/ providing `base` could be needed for performance reasons -- we don't want to spend most of the time just re-deducing base from base (e.g. #base=50, #facts=2) """ # keep frequently used attributes locally, so we'll avoid extra # attribute access overhead rels = self.rels beta_rules = self.beta_rules if base is not None: new_facts = base else: new_facts = {} # XXX better name ? def x_new_facts(keys, v): for k in keys: if k in new_facts and new_facts[k] is not None: assert new_facts[k] == v, \ ('inconsistency between facts', new_facts, k, v) continue else: new_facts[k] = v if type(facts) is dict: fseq = facts.iteritems() else: fseq = facts while True: beta_maytrigger = set() # --- alpha chains --- #print '**' for k,v in fseq: #print '--' # first, convert name to be not a not-name if k[:1] == '!': k = name_not(k) v = fuzzy_not(v) #new_fact(k, v) if k in new_facts: assert new_facts[k] is None or new_facts[k] == v, \ ('inconsistency between facts', new_facts, k, v) # performance-wise it is important not to fire implied rules # for already-seen fact -- we already did them all. continue else: new_facts[k] = v # some known fact -- let's follow its implications if v is not None: # lookup routing tables try: tt, tf, tbeta, ft, ff, fbeta = rels[k] except KeyError: pass else: # Now we have routing tables with *all* the needed # implications for this k. This means we do not have to # process each implications recursively! # XXX this ^^^ is true only for alpha chains # k=T if v: x_new_facts(tt, True) # k -> i x_new_facts(tf, False) # k -> !i beta_maytrigger.update(tbeta) # k=F else: x_new_facts(ft, True) # !k -> i x_new_facts(ff, False) # !k -> !i beta_maytrigger.update(fbeta) # --- beta chains --- # if no beta-rules may trigger -- it's an end-of-story if not beta_maytrigger: break #print '(β) MayTrigger: %s' % beta_maytrigger fseq = [] # XXX this is dumb proof-of-concept trigger -- we'll need to optimize it # let's see which beta-rules to trigger for bidx in beta_maytrigger: bcond,bimpl = beta_rules[bidx] # let's see whether bcond is satisfied for bk in bcond.args: try: if bk[:1] == '!': bv = fuzzy_not(new_facts[bk[1:]]) else: bv = new_facts[bk] except KeyError: break # fact not found -- bcond not satisfied # one of bcond's condition does not hold if not bv: break else: # all of bcond's condition hold -- let's fire this beta rule #print '(β) Trigger #%i (%s)' % (bidx, bimpl) if bimpl[:1] == '!': bimpl = bimpl[1:] v = False else: v = True fseq.append( (bimpl,v) ) return new_facts ######################################## # this was neeeded for testing -- feel free to remove this when everything # settles. # -- kirr if 0: FF = FactRules([ 'integer -> rational', 'rational -> real', 'real -> complex', 'imaginary -> complex', 'complex -> commutative', 'odd == integer & !even', 'even == integer & !odd', 'real == negative | zero | positive', 'positive -> real & !negative & !zero', 'negative -> real & !positive & !zero', 'nonpositive == real & !positive', 'nonnegative == real & !negative', 'zero -> infinitesimal & even', 'prime -> integer & positive', 'composite == integer & positive & !prime', 'irrational == real & !rational', 'imaginary -> !real', # XXX new ? '!bounded == unbounded', '!commutative == noncommutative', '!complex == noncomplex', # '!integer == noninteger', 'noninteger == real & !integer', # XXX ok? '!zero == nonzero', '!homogeneous == inhomogeneous', # XXX do we need this ? 'finite -> bounded', # XXX do we need this? 'finite -> !zero', # XXX wrong? 'infinitesimal -> !finite', # XXX is this ok? # TODO we should remove this (very seldomly used, but affect performance): 'nni == integer & nonnegative', 'npi == integer & nonpositive', 'pi == integer & positive', 'ni == integer & negative', ]) """ FF = FactRules([ 'real == pos | npos', 'pos -> real & !npos', 'npos -> real & !pos' ]) """ """ ff = FactRules([ 'real -> complex', 'real == neg | zero | pos', 'neg -> real & !pos & !zero', 'zero -> real & !neg & !pos', 'pos -> real & !neg & !zero', 'npos == real & !pos', 'real == pos | npos' # XXX this is a workaround for bug in process_rule ]) FF = ff """ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/function.py0000644000175000017500000014253612014170666024241 0ustar georgeskgeorgesk""" There are two types of functions: 1) defined function like exp or sin that has a name and body (in the sense that function can be evaluated). e = exp 2) undefined function with a name but no body. Undefined functions can be defined using a Function class as follows: f = Function('f') (the result will be a Function instance) 3) this isn't implemented yet: anonymous function or lambda function that has no name but has body with dummy variables. Examples of anonymous function creation: f = Lambda(x, exp(x)*x) f = Lambda(exp(x)*x) # free symbols in the expression define the number of arguments f = exp * Lambda(x,x) 4) isn't implemented yet: composition of functions, like (sin+cos)(x), this works in sympy core, but needs to be ported back to SymPy. Example: >>> import sympy >>> f = sympy.Function("f") >>> from sympy.abc import x >>> f(x) f(x) >>> print sympy.srepr(f(x).func) Function('f') >>> f(x).args (x,) """ from core import BasicMeta, C from basic import Basic from singleton import S from expr import Expr, AtomicExpr from decorators import _sympifyit from compatibility import iterable,is_sequence from cache import cacheit from numbers import Rational from sympy.core.containers import Tuple from sympy.core.decorators import deprecated from sympy.utilities import default_sort_key from sympy.utilities.iterables import uniq from sympy import mpmath import sympy.mpmath.libmp as mlib class PoleError(Exception): pass class ArgumentIndexError(ValueError): def __str__(self): return ("Invalid operation with argument number %s for Function %s" % (self.args[1], self.args[0])) class FunctionClass(BasicMeta): """ Base class for function classes. FunctionClass is a subclass of type. Use Function('' [ , signature ]) to create undefined function classes. """ __metaclass__ = BasicMeta _new = type.__new__ def __repr__(cls): return cls.__name__ def __contains__(self, obj): return (self == obj) class Application(Basic): """ Base class for applied functions. Instances of Application represent the result of applying an application of any type to any object. """ __metaclass__ = FunctionClass __slots__ = [] is_Function = True nargs = None @cacheit def __new__(cls, *args, **options): args = map(sympify, args) # these lines should be refactored for opt in ["nargs", "dummy", "comparable", "noncommutative", "commutative"]: if opt in options: del options[opt] if options.pop('evaluate', True): evaluated = cls.eval(*args) if evaluated is not None: return evaluated return super(Application, cls).__new__(cls, *args, **options) @classmethod def eval(cls, *args): """ Returns a canonical form of cls applied to arguments args. The eval() method is called when the class cls is about to be instantiated and it should return either some simplified instance (possible of some other class), or if the class cls should be unmodified, return None. Example of eval() for the function "sign" --------------------------------------------- @classmethod def eval(cls, arg): if arg is S.NaN: return S.NaN if arg is S.Zero: return S.Zero if arg.is_positive: return S.One if arg.is_negative: return S.NegativeOne if isinstance(arg, C.Mul): coeff, terms = arg.as_coeff_mul() if coeff is not S.One: return cls(coeff) * cls(arg._new_rawargs(*terms)) """ return @property def func(self): return self.__class__ def _eval_subs(self, old, new): if self == old: return new elif old.is_Function and new.is_Function: if old == self.func: nargs = len(self.args) if (nargs == new.nargs or new.nargs is None or (isinstance(new.nargs, tuple) and nargs in new.nargs)): return new(*self.args) return self.func(*[s.subs(old, new) for s in self.args]) def __contains__(self, obj): if self.func == obj: return True return super(Application, self).__contains__(obj) class Function(Application, Expr): """ Base class for applied numeric functions. Constructor of undefined function classes. """ @cacheit def __new__(cls, *args, **options): # Handle calls like Function('f') if cls is Function: return UndefinedFunction(*args) if cls.nargs is not None: if (isinstance(cls.nargs, tuple) and len(args) not in cls.nargs) \ or (not isinstance(cls.nargs, tuple) and cls.nargs != len(args)): raise ValueError('Function %s expects %s argument(s), got %s.' % ( cls, cls.nargs, len(args))) args = map(sympify, args) evaluate = options.pop('evaluate', True) if evaluate: evaluated = cls.eval(*args) if evaluated is not None: return evaluated result = super(Application, cls).__new__(cls, *args, **options) pr = max(cls._should_evalf(a) for a in args) pr2 = min(cls._should_evalf(a) for a in args) if evaluate and pr2 > 0: return result.evalf(mlib.libmpf.prec_to_dps(pr)) return result @classmethod def _should_evalf(cls, arg): """ Decide if the function should automatically evalf(). By default (in this implementation), this happens if (and only if) the ARG is a floating point number. This function is used by __new__. """ if arg.is_Float: return arg._prec if not arg.is_Add: return -1 re, im = arg.as_real_imag() l = [a._prec for a in [re, im] if a.is_Float] l.append(-1) return max(l) @classmethod def class_key(cls): funcs = { 'exp': 10, 'log': 11, 'sin': 20, 'cos': 21, 'tan': 22, 'cot': 23, 'sinh': 30, 'cosh': 31, 'tanh': 32, 'coth': 33, } name = cls.__name__ try: i = funcs[name] except KeyError: nargs = cls.nargs if nargs is None: i = 0 else: i = 10000 return 4, i, name @property def is_commutative(self): if all(getattr(t, 'is_commutative') for t in self.args): return True else: return False @classmethod @deprecated def canonize(cls, *args): return cls.eval(*args) def _eval_evalf(self, prec): # Lookup mpmath function based on name fname = self.func.__name__ try: if not hasattr(mpmath, fname): from sympy.utilities.lambdify import MPMATH_TRANSLATIONS fname = MPMATH_TRANSLATIONS[fname] func = getattr(mpmath, fname) except (AttributeError, KeyError): try: return C.Float(self._imp_(*self.args), prec) except (AttributeError, TypeError): return # Convert all args to mpf or mpc try: args = [arg._to_mpmath(prec) for arg in self.args] except ValueError: return # Set mpmath precision and apply. Make sure precision is restored # afterwards orig = mpmath.mp.prec try: mpmath.mp.prec = prec v = func(*args) finally: mpmath.mp.prec = orig return Expr._from_mpmath(v, prec) def _eval_is_comparable(self): if self.is_Function: r = True for s in self.args: c = s.is_comparable if c is None: return if not c: r = False return r return def _eval_derivative(self, s): # f(x).diff(s) -> x.diff(s) * f.fdiff(1)(s) i = 0 l = [] for a in self.args: i += 1 da = a.diff(s) if da is S.Zero: continue try: df = self.fdiff(i) except ArgumentIndexError: df = Function.fdiff(self, i) l.append(df * da) return Add(*l) def _eval_is_commutative(self): r = True for a in self._args: c = a.is_commutative if c is None: return None if not c: r = False return r def as_base_exp(self): return self, S.One def _eval_aseries(self, n, args0, x, logx): """ Compute an asymptotic expansion around args0, in terms of self.args. This function is only used internally by _eval_nseries and should not be called directly; derived classes can overwrite this to implement asymptotic expansions. """ raise PoleError('Asymptotic expansion of %s around %s ' 'not implemented.' % (type(self), args0)) def _eval_nseries(self, x, n, logx): """ This function does compute series for multivariate functions, but the expansion is always in terms of *one* variable. Examples: >>> from sympy import atan2, O >>> from sympy.abc import x, y >>> atan2(x, y).series(x, n=2) atan2(0, y) + x/y + O(x**2) >>> atan2(x, y).series(y, n=2) -y/x + atan2(x, 0) + O(y**2) This function also computes asymptotic expansions, if necessary and possible: >>> from sympy import loggamma >>> loggamma(1/x)._eval_nseries(x,0,None) -1/x - log(x)/x + log(x)/2 + O(1) """ if self.func.nargs is None: raise NotImplementedError('series for user-defined \ functions are not supported.') args = self.args args0 = [t.limit(x, 0) for t in args] if any([t.is_bounded == False for t in args0]): from sympy import Dummy, oo, zoo, nan a = [t.compute_leading_term(x, logx=logx) for t in args] a0 = [t.limit(x, 0) for t in a] if any ([t.has(oo, -oo, zoo, nan) for t in a0]): return self._eval_aseries(n, args0, x, logx)._eval_nseries(x, n, logx) # Careful: the argument goes to oo, but only logarithmically so. We # are supposed to do a power series expansion "around the # logarithmic term". e.g. # f(1+x+log(x)) # -> f(1+logx) + x*f'(1+logx) + O(x**2) # where 'logx' is given in the argument a = [t._eval_nseries(x, n, logx) for t in args] z = [r - r0 for (r, r0) in zip(a, a0)] p = [Dummy() for t in z] q = [] v = None for ai, zi, pi in zip(a0, z, p): if zi.has(x): if v is not None: raise NotImplementedError q.append(ai + pi) v = pi else: q.append(ai) e1 = self.func(*q) if v is None: return e1 s = e1._eval_nseries(v, n, logx) o = s.getO() s = s.removeO() s = s.subs(v, zi).expand() + C.Order(o.expr.subs(v, zi), x) return s if (self.func.nargs == 1 and args0[0]) or self.func.nargs > 1: e = self e1 = e.expand() if e == e1: #for example when e = sin(x+1) or e = sin(cos(x)) #let's try the general algorithm term = e.subs(x, S.Zero) if term.is_bounded is False or term is S.NaN: raise PoleError("Cannot expand %s around 0" % (self)) series = term fact = S.One for i in range(n-1): i += 1 fact *= Rational(i) e = e.diff(x) subs = e.subs(x, S.Zero) if subs is S.NaN: # try to evaluate a limit if we have to subs = e.limit(x, S.Zero) if subs.is_bounded is False: raise PoleError("Cannot expand %s around 0" % (self)) term = subs*(x**i)/fact term = term.expand() series += term return series + C.Order(x**n, x) return e1.nseries(x, n=n, logx=logx) arg = self.args[0] l = [] g = None for i in xrange(n+2): g = self.taylor_term(i, arg, g) g = g.nseries(x, n=n, logx=logx) l.append(g) return Add(*l) + C.Order(x**n, x) def _eval_expand_basic(self, deep=True, **hints): if not deep: return self sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_basic'): newterm = term._eval_expand_basic(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_exp(self, deep=True, **hints): if not deep: return self sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_power_exp'): newterm = term._eval_expand_power_exp(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_base(self, deep=True, **hints): if not deep: return self sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_power_base'): newterm = term._eval_expand_power_base(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_mul(self, deep=True, **hints): if not deep: return self sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_mul'): newterm = term._eval_expand_mul(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_multinomial(self, deep=True, **hints): if not deep: return self sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_multinomail'): newterm = term._eval_expand_multinomial(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_log(self, deep=True, **hints): if not deep: return self sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_log'): newterm = term._eval_expand_log(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_complex(self, deep=True, **hints): if deep: func = self.func(*[ a.expand(deep, **hints) for a in self.args ]) else: func = self.func(*self.args) return C.re(func) + S.ImaginaryUnit * C.im(func) def _eval_expand_trig(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_trig'): newterm = term._eval_expand_trig(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_func(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_func'): newterm = term._eval_expand_func(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_rewrite(self, pattern, rule, **hints): if hints.get('deep', False): args = [ a._eval_rewrite(pattern, rule, **hints) for a in self.args ] else: args = self.args if pattern is None or isinstance(self.func, pattern): if hasattr(self, rule): rewritten = getattr(self, rule)(*args) if rewritten is not None: return rewritten return self.func(*args) def fdiff(self, argindex=1): if self.nargs is not None: if isinstance(self.nargs, tuple): nargs = self.nargs[-1] else: nargs = self.nargs if not (1<=argindex<=nargs): raise ArgumentIndexError(self, argindex) if not self.args[argindex-1].is_Symbol: # See issue 1525 and issue 1620 and issue 2501 arg_dummy = C.Dummy('xi_%i' % argindex) return Subs(Derivative( self.subs(self.args[argindex-1], arg_dummy), arg_dummy), arg_dummy, self.args[argindex-1]) return Derivative(self,self.args[argindex-1],evaluate=False) def _eval_as_leading_term(self, x): """General method for the leading term""" # XXX This seems broken to me! arg = self.args[0].as_leading_term(x) if C.Order(1,x).contains(arg): return arg else: return self.func(arg) @classmethod def taylor_term(cls, n, x, *previous_terms): """General method for the taylor term. This method is slow, because it differentiates n-times. Subclasses can redefine it to make it faster by using the "previous_terms". """ x = sympify(x) return cls(x).diff(x, n).subs(x, 0) * x**n / C.factorial(n) class AppliedUndef(Function): """ Base class for expressions resulting from the application of an undefined function. """ def __new__(cls, *args, **options): args = map(sympify, args) result = Expr.__new__(cls, *args, **options) result.nargs = len(args) return result class UndefinedFunction(FunctionClass): """ The (meta)class of undefined functions. """ def __new__(mcl, name): return BasicMeta.__new__(mcl, name, (AppliedUndef,), {}) class WildFunction(Function, AtomicExpr): """ WildFunction() matches any expression but another WildFunction() XXX is this as intended, does it work ? """ nargs = 1 def __new__(cls, name, **assumptions): obj = Function.__new__(cls, name, **assumptions) obj.name = name return obj def matches(self, expr, repl_dict={}, evaluate=False): if self in repl_dict: if repl_dict[self] == expr: return repl_dict else: return None if self.nargs is not None: if not hasattr(expr,'nargs') or self.nargs != expr.nargs: return None repl_dict = repl_dict.copy() repl_dict[self] = expr return repl_dict @property def is_number(self): return False class Derivative(Expr): """ Carries out differentiation of the given expression with respect to symbols. expr must define ._eval_derivative(symbol) method that returns the differentiation result. This function only needs to consider the non-trivial case where expr contains symbol and it should call the diff() method internally (not _eval_derivative); Derivative should be the only one to call _eval_derivative. If evaluate is set to True and the expression can not be evaluated, the list of differentiation symbols will be sorted, that is, the expression is assumed to have continuous derivatives up to the order asked. Examples: * Derivative(Derivative(expr, x), y) -> Derivative(expr, x, y) * Derivative(expr, x, 3) -> Derivative(expr, x, x, x) * Derivative(f(x, y), y, x, evaluate=True) -> Derivative(f(x, y), x, y) """ is_Derivative = True def __new__(cls, expr, *symbols, **assumptions): expr = sympify(expr) if not symbols: symbols = expr.free_symbols if len(symbols) != 1: raise ValueError("specify differentiation variables to differentiate %s" % expr) # standardize symbols symbols = list(sympify(symbols)) if not symbols[-1].is_Integer or len(symbols) == 1: symbols.append(S.One) symbol_count = [] all_zero = True i = 0 while i < len(symbols) - 1: # process up to final Integer s, count = symbols[i: i+2] iwas = i if s.is_Symbol: if count.is_Symbol: count = 1 i += 1 elif count.is_Integer: count = int(count) i += 2 if i == iwas: # didn't get an update because of bad input raise ValueError('Derivative expects Symbol [, Integer] args but got %s, %s' % (s, count)) symbol_count.append((s, count)) if all_zero and not count == 0: all_zero = False # We make a special case for 0th derivative, because there # is no good way to unambiguously print this. if all_zero: return expr evaluate = assumptions.pop('evaluate', False) # look for a quick exit if there are symbols that are not in the free symbols if evaluate: if set(sc[0] for sc in symbol_count ).difference(expr.free_symbols): return S.Zero # We make a generator so as to only generate a symbol when necessary. # If a high order of derivative is requested and the expr becomes 0 # after a few differentiations, then we won't need the other symbols symbolgen = (s for s, count in symbol_count for i in xrange(count)) if expr.is_commutative: assumptions['commutative'] = True if (not (hasattr(expr, '_eval_derivative') and evaluate) and not isinstance(expr, Derivative)): symbols = list(symbolgen) if evaluate: #TODO: check if assumption of discontinuous derivatives exist symbols.sort(key=default_sort_key) obj = Expr.__new__(cls, expr, *symbols, **assumptions) return obj # compute the derivative now unevaluated_symbols = [] for s in symbolgen: obj = expr._eval_derivative(s) if obj is None: unevaluated_symbols.append(s) elif obj is S.Zero: return S.Zero else: expr = obj if not unevaluated_symbols: if isinstance(expr, Derivative): return Derivative(expr.args[0], *sorted(expr.args[1:], key=default_sort_key)) return expr unevaluated_symbols.sort(key=default_sort_key) return Expr.__new__(cls, expr, *unevaluated_symbols, **assumptions) def _eval_derivative(self, s): if s not in self.variables: obj = self.expr.diff(s) if not obj: return obj if isinstance(obj, Derivative): return Derivative(obj.expr, *(self.variables + obj.variables)) return Derivative(obj, *self.variables) return Derivative(self.expr, *(self.variables + (s, )), **{'evaluate': False}) def doit(self, **hints): expr = self.expr if hints.get('deep', True): expr = expr.doit(**hints) hints['evaluate'] = True return Derivative(expr, *self.variables, **hints) @_sympifyit('z0', NotImplementedError) def doit_numerically(self, z0): """ Evaluate the derivative at z numerically. When we can represent derivatives at a point, this should be folded into the normal evalf. For now, we need a special method. """ from sympy import mpmath from sympy.core.expr import Expr if len(self.free_symbols) != 1 or len(self.variables) != 1: raise NotImplementedError('partials and higher order derivatives') z = list(self.free_symbols)[0] def eval(x): f0 = self.expr.subs(z, Expr._from_mpmath(x, prec=mpmath.mp.prec)) f0 = f0.evalf(mlib.libmpf.prec_to_dps(mpmath.mp.prec)) return f0._to_mpmath(mpmath.mp.prec) return Expr._from_mpmath(mpmath.diff(eval, z0._to_mpmath(mpmath.mp.prec)), mpmath.mp.prec) @property def expr(self): return self._args[0] @property def variables(self): return self._args[1:] @property def free_symbols(self): return self.expr.free_symbols def _eval_subs(self, old, new): if self==old: return new if old in self.variables and not new.is_Symbol: # Issue 1620 return Subs(self, old, new) return Derivative(*map(lambda x: x._eval_subs(old, new), self.args)) def matches(self, expr, repl_dict={}, evaluate=False): if self in repl_dict: if repl_dict[self] == expr: return repl_dict elif isinstance(expr, Derivative): if len(expr.variables) == len(self.variables): return Expr.matches(self, expr, repl_dict, evaluate) def _eval_lseries(self, x): dx = self.args[1:] for term in self.args[0].lseries(x): yield Derivative(term, *dx) def _eval_nseries(self, x, n, logx): arg = self.args[0].nseries(x, n=n, logx=logx) o = arg.getO() dx = self.args[1:] rv = [Derivative(a, *dx) for a in Add.make_args(arg.removeO())] if o: rv.append(o/x) return Add(*rv) def _eval_as_leading_term(self, x): return self.args[0].as_leading_term(x) class Lambda(Expr): """ Lambda(x, expr) represents a lambda function similar to Python's 'lambda x: expr'. A function of several variables is written as Lambda((x, y, ...), expr). A simple example: >>> from sympy import Lambda >>> from sympy.abc import x >>> f = Lambda(x, x**2) >>> f(4) 16 For multivariate functions, use: >>> from sympy.abc import y, z, t >>> f2 = Lambda((x, y, z, t), x + y**z + t**z) >>> f2(1, 2, 3, 4) 73 A handy shortcut for lots of arguments: >>> p = x, y, z >>> f = Lambda(p, x + y*z) >>> f(*p) x + y*z """ is_Function = True __slots__ = [] def __new__(cls, variables, expr): try: variables = Tuple(*variables) except TypeError: variables = Tuple(variables) if len(variables) == 1 and variables[0] == expr: return S.IdentityFunction #use dummy variables internally, just to be sure new_variables = [C.Dummy(arg.name) for arg in variables] expr = sympify(expr).subs(tuple(zip(variables, new_variables))) obj = Expr.__new__(cls, Tuple(*new_variables), expr) return obj @property def variables(self): """The variables used in the internal representation of the function""" return self._args[0] @property def expr(self): """The return value of the function""" return self._args[1] @property def free_symbols(self): return self.expr.free_symbols - set(self.variables) @property def nargs(self): """The number of arguments that this function takes""" return len(self._args[0]) @deprecated def apply(self, *args): # pragma: no cover return self(*args) def __call__(self, *args): if len(args) != self.nargs: raise TypeError("%s takes %d arguments (%d given)" % (self, self.nargs, len(args))) return self.expr.subs(tuple(zip(self.variables, args))) def __eq__(self, other): if not isinstance(other, Lambda): return False if self.nargs != other.nargs: return False selfexpr = self.args[1] otherexpr = other.args[1] otherexpr = otherexpr.subs(tuple(zip(other.args[0], self.args[0]))) return selfexpr == otherexpr def __ne__(self, other): return not(self == other) def __hash__(self): return super(Lambda, self).__hash__() def _hashable_content(self): return (self.nargs, ) + tuple(sorted(self.free_symbols)) @property def is_identity(self): """Return ``True`` if this ``Lambda`` is an identity function. """ if len(self.args) == 2: return self.args[0] == self.args[1] else: return None class Subs(Expr): """ Represents unevaluated substitutions of an expression. ``Subs(expr, x, x0)`` receives 3 arguments: an expression, a variable or list of distinct variables and a point or list of evaluation points corresponding to those variables. ``Subs`` objects are generally useful to represent unevaluated derivatives calculated at a point. The variables may be expressions, but they are subjected to the limitations of subs(), so it is usually a good practice to use only symbols for variables, since in that case there can be no ambiguity. There's no automatic expansion - use the method .doit() to effect all possible substitutions of the object and also of objects inside the expression. When evaluating derivatives at a point that is not a symbol, a Subs object is returned. One is also able to calculate derivatives of Subs objects - in this case the expression is always expanded (for the unevaluated form, use Derivative()). A simple example: >>> from sympy import Subs, Function, sin >>> from sympy.abc import x, y, z >>> f = Function('f') >>> e = Subs(f(x).diff(x), x, y) >>> e.subs(y, 0) Subs(Derivative(f(_x), _x), (_x,), (0,)) >>> e.subs(f, sin).doit() cos(y) An example with several variables: >>> Subs(f(x)*sin(y)+z, (x, y), (0, 1)) Subs(z + f(_x)*sin(_y), (_x, _y), (0, 1)) >>> _.doit() z + f(0)*sin(1) """ def __new__(cls, expr, variables, point, **assumptions): if not is_sequence(variables, Tuple): variables = [variables] variables = Tuple(*sympify(variables)) if uniq(variables) != variables: repeated = repeated = [ v for v in set(variables) if list(variables).count(v) > 1 ] raise ValueError('cannot substitute expressions %s more than ' 'once.' % repeated) if not is_sequence(point, Tuple): point = [point] point = Tuple(*sympify(point)) if len(point) != len(variables): raise ValueError('Number of point values must be the same as ' 'the number of variables.') # it's necessary to use dummy variables internally new_variables = Tuple(*[ arg.as_dummy() if arg.is_Symbol else C.Dummy(str(arg)) for arg in variables ]) expr = sympify(expr).subs(tuple(zip(variables, new_variables))) if expr.is_commutative: assumptions['commutative'] = True obj = Expr.__new__(cls, expr, new_variables, point, **assumptions) return obj def doit(self): return self.expr.doit().subs(zip(self.variables, self.point)) def evalf(self): return self.doit().evalf() @property def variables(self): """The variables to be evaluated""" return self._args[1] @property def expr(self): """The expression on which the substitution operates""" return self._args[0] @property def point(self): """The values for which the variables are to be substituted""" return self._args[2] @property def free_symbols(self): return (self.expr.free_symbols - set(self.variables) | set(self.point.free_symbols)) def __eq__(self, other): if not isinstance(other, Subs): return False if (len(self.point) != len(other.point) or self.free_symbols != other.free_symbols or sorted(self.point) != sorted(other.point)): return False # non-repeated point args selfargs = [ v[0] for v in sorted(zip(self.variables, self.point), key = lambda v: v[1]) if list(self.point.args).count(v[1]) == 1 ] otherargs = [ v[0] for v in sorted(zip(other.variables, other.point), key = lambda v: v[1]) if list(other.point.args).count(v[1]) == 1 ] # find repeated point values and subs each associated variable # for a single symbol selfrepargs = [] otherrepargs = [] if uniq(self.point) != self.point: repeated = uniq([ v for v in self.point if list(self.point.args).count(v) > 1 ]) repswap = dict(zip(repeated, [ C.Dummy() for _ in xrange(len(repeated)) ])) selfrepargs = [ (self.variables[i], repswap[v]) for i, v in enumerate(self.point) if v in repeated ] otherrepargs = [ (other.variables[i], repswap[v]) for i, v in enumerate(other.point) if v in repeated ] return self.expr.subs(selfrepargs) == other.expr.subs( tuple(zip(otherargs, selfargs))).subs(otherrepargs) def __ne__(self, other): return not(self == other) def __hash__(self): return super(Subs, self).__hash__() def _hashable_content(self): return tuple(sorted(self.point)) + tuple(sorted(self.free_symbols)) def _eval_subs(self, old, new): if self == old: return new return Subs(self.expr.subs(old, new), self.variables, self.point.subs(old, new)) def _eval_derivative(self, s): if s not in self.free_symbols: return S.Zero return Subs(self.expr.diff(s), self.variables, self.point).doit() \ + Add(*[ Subs(point.diff(s) * self.expr.diff(arg), self.variables, self.point).doit() for arg, point in zip(self.variables, self.point) ]) def diff(f, *symbols, **kwargs): """ Differentiate f with respect to symbols. This is just a wrapper to unify .diff() and the Derivative class; its interface is similar to that of integrate(). You can use the same shortcuts for multiple variables as with Derivative. For example, diff(f(x), x, x, x) and diff(f(x), x, 3) both return the third derivative of f(x). You can pass evaluate=False to get an unevaluated Derivative class. Note that if there are 0 symbols (such as diff(f(x), x, 0), then the result will be the function (the zeroth derivative), even if evaluate=False. **Examples** >>> from sympy import sin, cos, Function, diff >>> from sympy.abc import x, y >>> f = Function('f') >>> diff(sin(x), x) cos(x) >>> diff(f(x), x, x, x) Derivative(f(x), x, x, x) >>> diff(f(x), x, 3) Derivative(f(x), x, x, x) >>> diff(sin(x)*cos(y), x, 2, y, 2) sin(x)*cos(y) >>> type(diff(sin(x), x)) cos >>> type(diff(sin(x), x, evaluate=False)) >>> type(diff(sin(x), x, 0)) sin >>> type(diff(sin(x), x, 0, evaluate=False)) sin >>> diff(sin(x)) cos(x) >>> diff(sin(x*y)) Traceback (most recent call last): ... ValueError: specify differentiation variables to differentiate sin(x*y) Note that ``diff(sin(x))`` syntax is meant only for convenience in interactive sessions and should be avoided in library code. See Also http://documents.wolfram.com/v5/Built-inFunctions/AlgebraicComputation/Calculus/D.html """ kwargs.setdefault('evaluate', True) return Derivative(f, *symbols, **kwargs) def expand(e, deep=True, modulus=None, power_base=True, power_exp=True, \ mul=True, log=True, multinomial=True, basic=True, **hints): """ Expand an expression using methods given as hints. Hints are applied with arbitrary order so your code shouldn't depend on the way hints are passed to this method. Hints evaluated unless explicitly set to False are: basic, log, multinomial, mul, power_base, and power_exp The following hints are supported but not applied unless set to True: complex, func, and trig. basic is a generic keyword for methods that want to be expanded automatically. For example, Integral uses expand_basic to expand the integrand. If you want your class expand methods to run automatically and they don't fit one of the already automatic methods, wrap it around _eval_expand_basic. If deep is set to True, things like arguments of functions are recursively expanded. Use deep=False to only expand on the top level. If the 'force' hint is used, assumptions about variables will be ignored in making the expansion. Also see expand_log, expand_mul, separate, expand_complex, expand_trig, and expand_func, which are wrappers around those expansion methods. >>> from sympy import cos, exp >>> from sympy.abc import x, y, z mul - Distributes multiplication over addition: >>> (y*(x + z)).expand(mul=True) x*y + y*z complex - Split an expression into real and imaginary parts: >>> (x + y).expand(complex=True) I*im(x) + I*im(y) + re(x) + re(y) >>> cos(x).expand(complex=True) -I*sin(re(x))*sinh(im(x)) + cos(re(x))*cosh(im(x)) power_exp - Expand addition in exponents into multiplied bases: >>> exp(x + y).expand(power_exp=True) exp(x)*exp(y) >>> (2**(x + y)).expand(power_exp=True) 2**x*2**y power_base - Split powers of multiplied bases if assumptions allow or if the 'force' hint is used: >>> ((x*y)**z).expand(power_base=True) (x*y)**z >>> ((x*y)**z).expand(power_base=True, force=True) x**z*y**z >>> ((2*y)**z).expand(power_base=True) 2**z*y**z log - Pull out power of an argument as a coefficient and split logs products into sums of logs. Note that these only work if the arguments of the log function have the proper assumptions: the arguments must be positive and the exponents must be real or else the force hint must be True: >>> from sympy import log, symbols, oo >>> log(x**2*y).expand(log=True) log(x**2*y) >>> log(x**2*y).expand(log=True, force=True) 2*log(x) + log(y) >>> x, y = symbols('x,y', positive=True) >>> log(x**2*y).expand(log=True) 2*log(x) + log(y) trig - Do trigonometric expansions: >>> cos(x + y).expand(trig=True) -sin(x)*sin(y) + cos(x)*cos(y) func - Expand other functions: >>> from sympy import gamma >>> gamma(x + 1).expand(func=True) x*gamma(x) multinomial - Expand (x + y + ...)**n where n is a positive integer: >>> ((x + y + z)**2).expand(multinomial=True) x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2 You can shut off methods that you don't want: >>> (exp(x + y)*(x + y)).expand() x*exp(x)*exp(y) + y*exp(x)*exp(y) >>> (exp(x + y)*(x + y)).expand(power_exp=False) x*exp(x + y) + y*exp(x + y) >>> (exp(x + y)*(x + y)).expand(mul=False) (x + y)*exp(x)*exp(y) Use deep=False to only expand on the top level: >>> exp(x + exp(x + y)).expand() exp(x)*exp(exp(x)*exp(y)) >>> exp(x + exp(x + y)).expand(deep=False) exp(x)*exp(exp(x + y)) Note: because hints are applied in arbitrary order, some hints may prevent expansion by other hints if they are applied first. In particular, mul may distribute multiplications and prevent log and power_base from expanding them. Also, if mul is applied before multinomial, the expression might not be fully distributed. The solution is to expand with mul=False first, then run expand_mul if you need further expansion. Examples: >>> from sympy import expand_log, expand, expand_mul >>> x, y, z = symbols('x,y,z', positive=True) :: expand(log(x*(y + z))) # could be either one below log(x*y + x*z) log(x) + log(y + z) >>> expand_log(log(x*y + x*z)) log(x*y + x*z) >>> expand(log(x*(y + z)), mul=False) log(x) + log(y + z) :: expand((x*(y + z))**x) # could be either one below (x*y + x*z)**x x**x*(y + z)**x >>> expand((x*(y + z))**x, mul=False) x**x*(y + z)**x :: expand(x*(y + z)**2) # could be either one below 2*x*y*z + x*y**2 + x*z**2 x*(y + z)**2 >>> expand(x*(y + z)**2, mul=False) x*(y**2 + 2*y*z + z**2) >>> expand_mul(_) x*y**2 + 2*x*y*z + x*z**2 """ hints['power_base'] = power_base hints['power_exp'] = power_exp hints['mul'] = mul hints['log'] = log hints['multinomial'] = multinomial hints['basic'] = basic return sympify(e).expand(deep=deep, modulus=modulus, **hints) # These are simple wrappers around single hints. Feel free to add ones for # power_exp, power_base, multinomial, or basic if you need them. def expand_mul(expr, deep=True): """ Wrapper around expand that only uses the mul hint. See the expand docstring for more information. Example: >>> from sympy import symbols, expand_mul, exp, log >>> x, y = symbols('x,y', positive=True) >>> expand_mul(exp(x+y)*(x+y)*log(x*y**2)) x*exp(x + y)*log(x*y**2) + y*exp(x + y)*log(x*y**2) """ return sympify(expr).expand(deep=deep, mul=True, power_exp=False,\ power_base=False, basic=False, multinomial=False, log=False) def expand_multinomial(expr, deep=True): """ Wrapper around expand that only uses the multinomial hint. See the expand docstring for more information. Example: >>> from sympy import symbols, expand_multinomial, exp >>> x, y = symbols('x y', positive=True) >>> expand_multinomial((x + exp(x + 1))**2) x**2 + 2*x*exp(x + 1) + exp(2*x + 2) """ return sympify(expr).expand(deep=deep, mul=False, power_exp=False,\ power_base=False, basic=False, multinomial=True, log=False) def expand_log(expr, deep=True): """ Wrapper around expand that only uses the log hint. See the expand docstring for more information. Example: >>> from sympy import symbols, expand_log, exp, log >>> x, y = symbols('x,y', positive=True) >>> expand_log(exp(x+y)*(x+y)*log(x*y**2)) (x + y)*(log(x) + 2*log(y))*exp(x + y) """ return sympify(expr).expand(deep=deep, log=True, mul=False,\ power_exp=False, power_base=False, multinomial=False, basic=False) def expand_func(expr, deep=True): """ Wrapper around expand that only uses the func hint. See the expand docstring for more information. Example: >>> from sympy import expand_func, gamma >>> from sympy.abc import x >>> expand_func(gamma(x + 2)) x*(x + 1)*gamma(x) """ return sympify(expr).expand(deep=deep, func=True, basic=False,\ log=False, mul=False, power_exp=False, power_base=False, multinomial=False) def expand_trig(expr, deep=True): """ Wrapper around expand that only uses the trig hint. See the expand docstring for more information. Example: >>> from sympy import expand_trig, sin, cos >>> from sympy.abc import x, y >>> expand_trig(sin(x+y)*(x+y)) (x + y)*(sin(x)*cos(y) + sin(y)*cos(x)) """ return sympify(expr).expand(deep=deep, trig=True, basic=False,\ log=False, mul=False, power_exp=False, power_base=False, multinomial=False) def expand_complex(expr, deep=True): """ Wrapper around expand that only uses the complex hint. See the expand docstring for more information. Example: >>> from sympy import expand_complex, I, im, re >>> from sympy.abc import z >>> expand_complex(z**(2*I)) I*im(z**(2*I)) + re(z**(2*I)) """ return sympify(expr).expand(deep=deep, complex=True, basic=False,\ log=False, mul=False, power_exp=False, power_base=False, multinomial=False) def count_ops(expr, visual=False): """ Return a representation (integer or expression) of the operations in expr. If ``visual`` is ``False`` (default) then the sum of the coefficients of the visual expression will be returned. If ``visual`` is ``True`` then the number of each type of operation is shown with the core class types (or their virtual equivalent) multiplied by the number of times they occur. If expr is an iterable, the sum of the op counts of the items will be returned. Examples: >>> from sympy.abc import a, b, x, y >>> from sympy import sin, count_ops Although there isn't a SUB object, minus signs are interpreted as either negations or subtractions: >>> (x - y).count_ops(visual=True) SUB >>> (-x).count_ops(visual=True) NEG Here, there are two Adds and a Pow: >>> (1 + a + b**2).count_ops(visual=True) 2*ADD + POW In the following, an Add, Mul, Pow and two functions: >>> (sin(x)*x + sin(x)**2).count_ops(visual=True) ADD + MUL + POW + 2*SIN for a total of 5: >>> (sin(x)*x + sin(x)**2).count_ops(visual=False) 5 Note that "what you type" is not always what you get. The expression 1/x/y is translated by sympy into 1/(x*y) so it gives a DIV and MUL rather than two DIVs: >>> (1/x/y).count_ops(visual=True) DIV + MUL The visual option can be used to demonstrate the difference in operations for expressions in different forms. Here, the Horner representation is compared with the expanded form of a polynomial: >>> eq=x*(1 + x*(2 + x*(3 + x))) >>> count_ops(eq.expand(), visual=True) - count_ops(eq, visual=True) -MUL + 3*POW The count_ops function also handles iterables: >>> count_ops([x, sin(x), None, True, x + 2], visual=False) 2 >>> count_ops([x, sin(x), None, True, x + 2], visual=True) ADD + SIN >>> count_ops({x: sin(x), x + 2: y + 1}, visual=True) 2*ADD + SIN """ from sympy.simplify.simplify import fraction expr = sympify(expr) if isinstance(expr, Expr): ops = [] args = [expr] NEG = C.Symbol('NEG') DIV = C.Symbol('DIV') SUB = C.Symbol('SUB') ADD = C.Symbol('ADD') def isneg(a): c = a.as_coeff_mul()[0] return c.is_Number and c.is_negative while args: a = args.pop() if a.is_Rational: #-1/3 = NEG + DIV if a is not S.One: if a.p < 0: ops.append(NEG) if a.q != 1: ops.append(DIV) continue elif a.is_Mul: if isneg(a): ops.append(NEG) if a.args[0] is S.NegativeOne: a = a.as_two_terms()[1] else: a = -a n, d = fraction(a) if n.is_Integer: ops.append(DIV) if n < 0: ops.append(NEG) args.append(d) continue # won't be -Mul but could be Add elif d is not S.One: if not d.is_Integer: args.append(d) ops.append(DIV) args.append(n) continue # could be -Mul elif a.is_Add: aargs = list(a.args) negs = 0 for i, ai in enumerate(aargs): if isneg(ai): negs += 1 args.append(-ai) if i > 0: ops.append(SUB) else: args.append(ai) if i > 0: ops.append(ADD) if negs == len(aargs): # -x - y = NEG + SUB ops.append(NEG) elif isneg(aargs[0]): # -x + y = SUB, but we already recorded an ADD ops.append(SUB - ADD) continue if a.is_Pow and a.exp is S.NegativeOne: ops.append(DIV) args.append(a.base) # won't be -Mul but could be Add continue if (a.is_Mul or a.is_Pow or a.is_Function or isinstance(a, Derivative) or isinstance(a, C.Integral)): o = C.Symbol(a.func.__name__.upper()) # count the args if (a.is_Mul or isinstance(a, C.LatticeOp)): ops.append(o*(len(a.args) - 1)) else: ops.append(o) args.extend(a.args) elif type(expr) is dict: ops = [count_ops(k, visual=visual) + count_ops(v, visual=visual) for k, v in expr.iteritems()] elif iterable(expr): ops = [count_ops(i, visual=visual) for i in expr] elif not isinstance(expr, Basic): ops = [] else: # it's Basic not isinstance(expr, Expr): assert isinstance(expr, Basic) ops = [count_ops(a, visual=visual) for a in expr.args] if not ops: if visual: return S.Zero return 0 ops = Add(*ops) if visual: return ops if ops.is_Number: return int(ops) return sum(int((a.args or [1])[0]) for a in Add.make_args(ops)) from sympify import sympify from add import Add wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/decorators.py0000644000175000017500000000746412014170666024561 0ustar georgeskgeorgesk""" SymPy core decorators. The purpose of this module is to expose decorators without any other dependencies, so that they can be easily imported anywhere in sympy/core. """ from sympify import SympifyError, sympify import warnings try: from functools import wraps except ImportError: def wraps(old_func): """Copy private data from ``old_func`` to ``new_func``. """ def decorate(new_func): new_func.__dict__.update(old_func.__dict__) new_func.__module__ = old_func.__module__ new_func.__name__ = old_func.__name__ new_func.__doc__ = old_func.__doc__ return new_func return decorate def deprecated(func): """This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted when the function is used.""" @wraps(func) def new_func(*args, **kwargs): warnings.warn("Call to deprecated function %s." % func.__name__, category=DeprecationWarning) return func(*args, **kwargs) return new_func def _sympifyit(arg, retval=None): """decorator to smartly _sympify function arguments @_sympifyit('other', NotImplemented) def add(self, other): ... In add, other can be thought of as already being a SymPy object. If it is not, the code is likely to catch an exception, then other will be explicitly _sympified, and the whole code restarted. if _sympify(arg) fails, NotImplemented will be returned see: __sympifyit """ def deco(func): return __sympifyit(func, arg, retval) return deco def __sympifyit(func, arg, retval=None): """decorator to _sympify `arg` argument for function `func` don't use directly -- use _sympifyit instead """ # we support f(a,b) only assert func.func_code.co_argcount # only b is _sympified assert func.func_code.co_varnames[1] == arg if retval is None: @wraps(func) def __sympifyit_wrapper(a, b): return func(a, sympify(b, strict=True)) else: @wraps(func) def __sympifyit_wrapper(a, b): try: return func(a, sympify(b, strict=True)) except SympifyError: return retval return __sympifyit_wrapper def call_highest_priority(method_name): """A decorator for binary special methods to handle _op_priority. Binary special methods in Expr and its subclasses use a special attribute '_op_priority' to determine whose special method will be called to handle the operation. In general, the object having the highest value of '_op_priority' will handle the operation. Expr and subclasses that define custom binary special methods (__mul__, etc.) should decorate those methods with this decorator to add the priority logic. The ``method_name`` argument is the name of the method of the other class that will be called. Use this decorator in the following manner:: # Call other.__rmul__ if other._op_priority > self._op_priority @call_highest_priority('__rmul__') def __mul__(self, other): ... # Call other.__mul__ if other._op_priority > self._op_priority @call_highest_priority('__mul__') def __rmul__(self, other): ... """ def priority_decorator(func): def binary_op_wrapper(self, other): if hasattr(other, '_op_priority'): if other._op_priority > self._op_priority: try: f = getattr(other, method_name) except AttributeError: pass else: return f(self) return func(self, other) return binary_op_wrapper return priority_decorator wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/sympify.py0000644000175000017500000001320012014170666024075 0ustar georgeskgeorgesk"""sympify -- convert objects SymPy internal format""" from inspect import getmro from core import all_classes as sympy_classes from sympy.core.compatibility import iterable class SympifyError(ValueError): def __init__(self, expr, base_exc=None): self.expr = expr self.base_exc = base_exc def __str__(self): if self.base_exc is None: return "SympifyError: %r" % (self.expr,) return ("Sympify of expression '%s' failed, because of exception being " "raised:\n%s: %s" % (self.expr, self.base_exc.__class__.__name__, str(self.base_exc))) converter = {} def sympify(a, locals=None, convert_xor=True, strict=False, rational=False): """ Converts an arbitrary expression to a type that can be used inside sympy. For example, it will convert python ints into instance of sympy.Rational, floats into instances of sympy.Float, etc. It is also able to coerce symbolic expressions which inherit from Basic. This can be useful in cooperation with SAGE. It currently accepts as arguments: - any object defined in sympy (except matrices [TODO]) - standard numeric python types: int, long, float, Decimal - strings (like "0.09" or "2e-19") - booleans, including ``None`` (will leave them unchanged) - lists, sets or tuples containing any of the above If the argument is already a type that sympy understands, it will do nothing but return that value. This can be used at the beginning of a function to ensure you are working with the correct type. >>> from sympy import sympify >>> sympify(2).is_integer True >>> sympify(2).is_real True >>> sympify(2.0).is_real True >>> sympify("2.0").is_real True >>> sympify("2e-45").is_real True If the expression could not be converted, a SympifyError is raised. >>> sympify("x***2") Traceback (most recent call last): ... SympifyError: SympifyError: "could not parse u'x***2'" If the option ``strict`` is set to ``True``, only the types for which an explicit conversion has been defined are converted. In the other cases, a SympifyError is raised. >>> sympify(True) True >>> sympify(True, strict=True) Traceback (most recent call last): ... SympifyError: SympifyError: True To extend `sympify` to convert custom objects (not derived from `Basic`), the static dictionary `convert` is provided. The custom converters are usually added at import time, and will apply to all objects of the given class or its derived classes. For example, all geometry objects derive from `GeometryEntity` class, and should not be altered by the converter, so we add the following after defining that class: >>> from sympy.core.sympify import converter >>> from sympy.geometry.entity import GeometryEntity >>> converter[GeometryEntity] = lambda x: x """ from containers import Tuple try: cls = a.__class__ except AttributeError: #a is probably an old-style class object cls = type(a) if cls in sympy_classes: return a if cls in (bool, type(None)): if strict: raise SympifyError(a) else: return a try: return converter[cls](a) except KeyError: for superclass in getmro(cls): try: return converter[superclass](a) except KeyError: continue try: return a._sympy_() except AttributeError: pass if not isinstance(a, basestring): for coerce in (float, int): try: return sympify(coerce(a)) except (TypeError, ValueError, AttributeError, SympifyError): continue if strict: raise SympifyError(a) if isinstance(a, tuple): return Tuple(*[sympify(x, locals=locals, convert_xor=convert_xor, rational=rational) for x in a]) if iterable(a): try: return type(a)([sympify(x, locals=locals, convert_xor=convert_xor, rational=rational) for x in a]) except TypeError: # Not all iterables are rebuildable with their type. pass # At this point we were given an arbitrary expression # which does not inherit from Basic and doesn't implement # _sympy_ (which is a canonical and robust way to convert # anything to SymPy expression). # # As a last chance, we try to take "a"'s normal form via unicode() # and try to parse it. If it fails, then we have no luck and # return an exception try: a = unicode(a) except Exception, exc: raise SympifyError(a, exc) from sympy.parsing.sympy_parser import parse_expr, TokenError try: expr = parse_expr(a, locals or {}, rational, convert_xor) except (TokenError, SyntaxError): raise SympifyError('could not parse %r' % a) return expr def _sympify(a): """Short version of sympify for internal usage for __add__ and __eq__ methods where it is ok to allow some things (like Python integers and floats) in the expression. This excludes things (like strings) that are unwise to allow into such an expression. >>> from sympy import Integer >>> Integer(1) == 1 True >>> Integer(1) == '1' False >>> from sympy import Symbol >>> from sympy.abc import x >>> x + 1 x + 1 >>> x + '1' Traceback (most recent call last): ... TypeError: unsupported operand type(s) for +: 'Symbol' and 'str' see: sympify """ return sympify(a, strict=True) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/basic.py0000644000175000017500000010572412014170666023473 0ustar georgeskgeorgesk"""Base class for all the objects in SymPy""" from assumptions import AssumeMeths, make__get_assumption from cache import cacheit from core import BasicMeta, BasicType, C from sympify import _sympify, sympify, SympifyError from compatibility import callable, reduce, cmp, iterable from sympy.core.decorators import deprecated class Basic(AssumeMeths): """ Base class for all objects in sympy. Conventions: 1) When you want to access parameters of some instance, always use .args: Example: >>> from sympy import symbols, cot >>> from sympy.abc import x, y >>> cot(x).args (x,) >>> cot(x).args[0] x >>> (x*y).args (x, y) >>> (x*y).args[1] y 2) Never use internal methods or variables (the ones prefixed with "_"). Example: >>> cot(x)._args #don't use this, use cot(x).args instead (x,) """ __metaclass__ = BasicMeta __slots__ = ['_mhash', # hash value '_args', # arguments '_assume_type_keys', # assumptions typeinfo keys ] # To be overridden with True in the appropriate subclasses is_Atom = False is_Symbol = False is_Dummy = False is_Wild = False is_Function = False is_Add = False is_Mul = False is_Pow = False is_Number = False is_Float = False is_Rational = False is_Integer = False is_NumberSymbol = False is_Order = False is_Derivative = False is_Piecewise = False is_Poly = False is_AlgebraicNumber = False is_Relational = False is_Equality = False is_Boolean = False is_Not = False @property @deprecated def is_Real(self): # pragma: no cover """Deprecated alias for ``is_Float``""" return self.is_Float def __new__(cls, *args, **assumptions): obj = object.__new__(cls) # FIXME we are slowed a *lot* by Add/Mul passing is_commutative as the # only assumption. # # .is_commutative is not an assumption -- it's like typeinfo!!! # we should remove it. # initially assumptions are shared between instances and class obj._assumptions = cls.default_assumptions obj._a_inprogress = [] # NOTE this could be made lazy -- probably not all instances will need # fully derived assumptions? if assumptions: obj._learn_new_facts(assumptions) # ^ # FIXME this is slow | another NOTE: speeding this up is *not* # | | important. say for %timeit x+y most of # .------' | the time is spent elsewhere # | | # | XXX _learn_new_facts could be asked about what *new* facts have # v XXX been learned -- we'll need this to append to _hashable_content basek = set(cls.default_assumptions.keys()) k2 = set(obj._assumptions.keys()) newk = k2.difference(basek) obj._assume_type_keys = frozenset(newk) else: obj._assume_type_keys = None obj._mhash = None # will be set by __hash__ method. obj._args = args # all items in args must be Basic objects return obj # XXX better name? @property def assumptions0(self): """ Return object ``type`` assumptions. For example: Symbol('x', real=True) Symbol('x', integer=True) are different objects. In other words, besides Python type (Symbol in this case), the initial assumptions are also forming their typeinfo. Example: >>> from sympy import Symbol >>> from sympy.abc import x >>> x.assumptions0 {} >>> x = Symbol("x", positive=True) >>> x.assumptions0 {'commutative': True, 'complex': True, 'imaginary': False, 'negative': False, 'nonnegative': True, 'nonpositive': False, 'nonzero': True, 'positive': True, 'real': True, 'zero': False} """ cls = type(self) A = self._assumptions # assumptions shared: if A is cls.default_assumptions or (self._assume_type_keys is None): assumptions0 = {} else: assumptions0 = dict( (k, A[k]) for k in self._assume_type_keys ) return assumptions0 # NOTE NOTE NOTE # -------------- # # new-style classes + __getattr__ is *very* slow! # def __getattr__(self, name): # raise Warning('no way, *all* attribute access will be 2.5x slower') # here is what we do instead: for k in AssumeMeths._assume_defined: exec "is_%s = property(make__get_assumption('Basic', '%s'))" % (k,k) del k # NB: there is no need in protective __setattr__ def __getnewargs__(self): """ Pickling support. """ return tuple(self.args) def __hash__(self): # hash cannot be cached using cache_it because infinite recurrence # occurs as hash is needed for setting cache dictionary keys h = self._mhash if h is None: h = (type(self).__name__,) + self._hashable_content() if self._assume_type_keys is not None: a = [] kv= self._assumptions for k in sorted(self._assume_type_keys): a.append( (k, kv[k]) ) h = hash( h + tuple(a) ) else: h = hash( h ) self._mhash = h return h else: return h def _hashable_content(self): # If class defines additional attributes, like name in Symbol, # then this method should be updated accordingly to return # relevant attributes as tuple. return self._args def compare(self, other): """ Return -1,0,1 if the object is smaller, equal, or greater than other. Not in the mathematical sense. If the object is of a different type from the "other" then their classes are ordered according to the sorted_classes list. Example: >>> from sympy.abc import x, y >>> x.compare(y) -1 >>> x.compare(x) 0 >>> y.compare(x) 1 """ # all redefinitions of __cmp__ method should start with the # following three lines: if self is other: return 0 c = cmp(self.__class__, other.__class__) if c: return c # st = self._hashable_content() ot = other._hashable_content() c = cmp(len(st),len(ot)) if c: return c for l,r in zip(st,ot): if isinstance(l, Basic): c = l.compare(r) else: c = cmp(l, r) if c: return c return 0 @staticmethod def _compare_pretty(a, b): from sympy.series.order import Order if isinstance(a, Order) and not isinstance(b, Order): return 1 if not isinstance(a, Order) and isinstance(b, Order): return -1 if a.is_Rational and b.is_Rational: return cmp(a.p*b.q, b.p*a.q) else: from sympy.core.symbol import Wild p1, p2, p3 = Wild("p1"), Wild("p2"), Wild("p3") r_a = a.match(p1 * p2**p3) if r_a and p3 in r_a: a3 = r_a[p3] r_b = b.match(p1 * p2**p3) if r_b and p3 in r_b: b3 = r_b[p3] c = Basic.compare(a3, b3) if c != 0: return c return Basic.compare(a,b) @staticmethod def compare_pretty(a, b): """ Is a > b in the sense of ordering in printing? :: yes ..... return 1 no ...... return -1 equal ... return 0 Strategy: It uses Basic.compare as a fallback, but improves it in many cases, like x**3, x**4, O(x**3) etc. In those simple cases, it just parses the expression and returns the "sane" ordering such as:: 1 < x < x**2 < x**3 < O(x**4) etc. Example: >>> from sympy.abc import x >>> from sympy import Basic, Number >>> Basic._compare_pretty(x, x**2) -1 >>> Basic._compare_pretty(x**2, x**2) 0 >>> Basic._compare_pretty(x**3, x**2) 1 >>> Basic._compare_pretty(Number(1, 2), Number(1, 3)) 1 >>> Basic._compare_pretty(Number(0), Number(-1)) 1 """ try: a = _sympify(a) except SympifyError: pass try: b = _sympify(b) except SympifyError: pass # both objects are non-SymPy if (not isinstance(a, Basic)) and (not isinstance(b, Basic)): return cmp(a,b) if not isinstance(a, Basic): return -1 # other < sympy if not isinstance(b, Basic): return +1 # sympy > other # now both objects are from SymPy, so we can proceed to usual comparison return cmp(a.sort_key(), b.sort_key()) @classmethod def fromiter(cls, args, **assumptions): """ Create a new object from an iterable. This is a convenience function that allows one to create objects from any iterable, without having to convert to a list or tuple first. Example: >>> from sympy import Tuple >>> Tuple.fromiter(i for i in xrange(5)) (0, 1, 2, 3, 4) """ return cls(*tuple(args), **assumptions) @classmethod def class_key(cls): """Nice order of classes. """ return 5, 0, cls.__name__ def sort_key(self, order=None): """ Return a sort key. **Examples** >>> from sympy.core import Basic, S, I >>> from sympy.abc import x >>> sorted([S(1)/2, I, -I], key=lambda x: x.sort_key()) [1/2, -I, I] >>> S("[x, 1/x, 1/x**2, x**2, x**(1/2), x**(1/4), x**(3/2)]") [x, 1/x, x**(-2), x**2, x**(1/2), x**(1/4), x**(3/2)] >>> sorted(_, key=lambda x: x.sort_key()) [x**(-2), 1/x, x**(1/4), x**(1/2), x, x**(3/2), x**2] """ from sympy.core.singleton import S return self.class_key(), (len(self.args), self.args), S.One.sort_key(), S.One def __eq__(self, other): """a == b -> Compare two symbolic trees and see whether they are equal this is the same as: a.compare(b) == 0 but faster """ if type(self) is not type(other): try: other = _sympify(other) except SympifyError: return False # sympy != other if type(self) is not type(other): return False # type(self) == type(other) st = self._hashable_content() ot = other._hashable_content() return st == ot and self._assume_type_keys == other._assume_type_keys def __ne__(self, other): """a != b -> Compare two symbolic trees and see whether they are different this is the same as: a.compare(b) != 0 but faster """ if type(self) is not type(other): try: other = _sympify(other) except SympifyError: return True # sympy != other if type(self) is not type(other): return True # type(self) == type(other) st = self._hashable_content() ot = other._hashable_content() return (st != ot) or self._assume_type_keys != other._assume_type_keys def dummy_eq(self, other, symbol=None): """ Compare two expressions and handle dummy symbols. **Examples** >>> from sympy import Dummy >>> from sympy.abc import x, y >>> u = Dummy('u') >>> (u**2 + 1).dummy_eq(x**2 + 1) True >>> (u**2 + 1) == (x**2 + 1) False >>> (u**2 + y).dummy_eq(x**2 + y, x) True >>> (u**2 + y).dummy_eq(x**2 + y, y) False """ dummy_symbols = [ s for s in self.free_symbols if s.is_Dummy ] if not dummy_symbols: return self == other elif len(dummy_symbols) == 1: dummy = dummy_symbols.pop() else: raise ValueError("only one dummy symbol allowed on the left-hand side") if symbol is None: symbols = other.free_symbols if not symbols: return self == other elif len(symbols) == 1: symbol = symbols.pop() else: raise ValueError("specify a symbol in which expressions should be compared") tmp = dummy.__class__() return self.subs(dummy, tmp) == other.subs(symbol, tmp) # Note, we always use the default ordering (lex) in __str__ and __repr__, # regardless of the global setting. See issue 2388. def __repr__(self): from sympy.printing import sstr return sstr(self, order=None) def __str__(self): from sympy.printing import sstr return sstr(self, order=None) def atoms(self, *types): """Returns the atoms that form the current object. By default, only objects that are truly atomic and can't be divided into smaller pieces are returned: symbols, numbers, and number symbols like I and pi. It is possible to request atoms of any type, however, as demonstrated below. Examples: >>> from sympy import I, pi, sin >>> from sympy.abc import x, y >>> (1 + x + 2*sin(y + I*pi)).atoms() set([1, 2, I, pi, x, y]) If one or more types are given, the results will contain only those types of atoms. Examples: >>> from sympy import Number, NumberSymbol, Symbol >>> (1 + x + 2*sin(y + I*pi)).atoms(Symbol) set([x, y]) >>> (1 + x + 2*sin(y + I*pi)).atoms(Number) set([1, 2]) >>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol) set([1, 2, pi]) >>> (1 + x + 2*sin(y + I*pi)).atoms(Number, NumberSymbol, I) set([1, 2, I, pi]) Note that I (imaginary unit) and zoo (complex infinity) are special types of number symbols and are not part of the NumberSymbol class. The type can be given implicitly, too: >>> (1 + x + 2*sin(y + I*pi)).atoms(x) # x is a Symbol set([x, y]) Be careful to check your assumptions when using the implicit option since ``S(1).is_Integer = True`` but ``type(S(1))`` is ``One``, a special type of sympy atom, while ``type(S(2))`` is type ``Integer`` and will find all integers in an expression: >>> from sympy import S >>> (1 + x + 2*sin(y + I*pi)).atoms(S(1)) set([1]) >>> (1 + x + 2*sin(y + I*pi)).atoms(S(2)) set([1, 2]) Finally, arguments to atoms() can select more than atomic atoms: any sympy type (loaded in core/__init__.py) can be listed as an argument and those types of "atoms" as found in scanning the arguments of the expression recursively: >>> from sympy import Function, Mul >>> (1 + x + 2*sin(y + I*pi)).atoms(Function) set([sin(y + I*pi)]) >>> (1 + x + 2*sin(y + I*pi)).atoms(Mul) set([I*pi, 2*sin(y + I*pi)]) """ def _atoms(expr, typ): """Helper function for recursively denesting atoms""" result = set() if isinstance(expr, Basic): if expr.is_Atom and len(typ) == 0: # if we haven't specified types return set([expr]) else: try: if isinstance(expr, typ): result.add(expr) except TypeError: #one or more types is in implicit form for t in typ: if isinstance(t, type): if isinstance(expr, t): result.add(expr) else: if isinstance(expr, type(t)): result.add(expr) iter = expr.iter_basic_args() elif iterable(expr): iter = expr.__iter__() else: iter = [] for obj in iter: result.update(_atoms(obj, typ)) return result return _atoms(self, typ=types) @property def free_symbols(self): """Return from the atoms of self those which are free symbols. For most expressions, all symbols are free symbols. For some classes this is not true. e.g. Integrals use Symbols for the dummy variables which are bound variables, so Integral has a method to return all symbols except those. Derivative keeps track of symbols with respect to which it will perform a derivative; those are bound variables, too, so it has its own symbols method. Any other method that uses bound variables should implement a symbols method.""" union = set.union return reduce(union, [arg.free_symbols for arg in self.args], set()) def is_hypergeometric(self, k): from sympy.simplify import hypersimp return hypersimp(self, k) is not None @property def is_number(self): """Returns ``True`` if 'self' is a number. >>> from sympy import log, Integral >>> from sympy.abc import x, y >>> x.is_number False >>> (2*x).is_number False >>> (2 + log(2)).is_number True >>> (2 + Integral(2, x)).is_number False >>> (2 + Integral(2, (x, 1, 2))).is_number True """ # should be overriden by subclasses return False @property def func(self): """ The top-level function in an expression. The following should hold for all objects:: >> x == x.func(*x.args) Example: >>> from sympy.abc import x >>> a = 2*x >>> a.func >>> a.args (2, x) >>> a.func(*a.args) 2*x >>> a == a.func(*a.args) True """ return self.__class__ @property def args(self): """Returns a tuple of arguments of 'self'. Example: >>> from sympy import symbols, cot >>> from sympy.abc import x, y >>> cot(x).args (x,) >>> cot(x).args[0] x >>> (x*y).args (x, y) >>> (x*y).args[1] y Note for developers: Never use self._args, always use self.args. Only when you are creating your own new function, use _args in the __new__. Don't override .args() from Basic (so that it's easy to change the interface in the future if needed). """ return self._args def iter_basic_args(self): """ Iterates arguments of 'self'. Example: >>> from sympy.abc import x >>> a = 2*x >>> a.iter_basic_args() >>> list(a.iter_basic_args()) [2, x] """ return iter(self.args) def as_poly(self, *gens, **args): """Converts ``self`` to a polynomial or returns ``None``. >>> from sympy import Poly, sin >>> from sympy.abc import x, y >>> print (x**2 + x*y).as_poly() Poly(x**2 + x*y, x, y, domain='ZZ') >>> print (x**2 + x*y).as_poly(x, y) Poly(x**2 + x*y, x, y, domain='ZZ') >>> print (x**2 + sin(y)).as_poly(x, y) None """ from sympy.polys import Poly, PolynomialError try: poly = Poly(self, *gens, **args) if not poly.is_Poly: return None else: return poly except PolynomialError: return None def subs(self, *args): """ Substitutes an expression. Calls either _subs_old_new, _subs_dict or _subs_list depending if you give it two arguments (old, new), a dictionary or a list. Examples: >>> from sympy import pi >>> from sympy.abc import x, y >>> (1 + x*y).subs(x, pi) pi*y + 1 >>> (1 + x*y).subs({x:pi, y:2}) 1 + 2*pi >>> (1 + x*y).subs([(x,pi), (y,2)]) 1 + 2*pi >>> (x + y).subs([(y,x**2), (x,2)]) 6 >>> (x + y).subs([(x,2), (y,x**2)]) x**2 + 2 """ if len(args) == 1: sequence = args[0] if isinstance(sequence, dict): return self._subs_dict(sequence) elif iterable(sequence): return self._subs_list(sequence) else: raise TypeError("Not an iterable container") elif len(args) == 2: old, new = args return self._subs_old_new(old, new) else: raise TypeError("subs accepts either 1 or 2 arguments") @cacheit def _subs_old_new(self, old, new): """Substitutes an expression old -> new.""" old = sympify(old) new = sympify(new) return self._eval_subs(old, new) def _eval_subs(self, old, new): if self == old: return new else: return self.func(*[arg._eval_subs(old, new) for arg in self.args]) def _subs_list(self, sequence): """ Performs an order sensitive substitution from the input sequence list. Examples: >>> from sympy.abc import x, y >>> (x+y)._subs_list( [(x, 3), (y, x**2)] ) x**2 + 3 >>> (x+y)._subs_list( [(y, x**2), (x, 3) ] ) 12 """ result = self for old, new in sequence: if hasattr(result, 'subs'): result = result.subs(old, new) return result def _subs_dict(self, sequence): """Performs sequential substitution. Given a collection of key, value pairs, which correspond to old and new expressions respectively, substitute all given pairs handling properly all overlapping keys (according to 'in' relation). We have to use naive O(n**2) sorting algorithm, as 'in' gives only partial order and all asymptotically faster fail (depending on the initial order). >>> from sympy import sqrt, sin, cos, exp >>> from sympy.abc import x, y >>> from sympy.abc import a, b, c, d, e >>> A = (sqrt(sin(2*x)), a) >>> B = (sin(2*x), b) >>> C = (cos(2*x), c) >>> D = (x, d) >>> E = (exp(x), e) >>> expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) >>> expr._subs_dict([A,B,C,D,E]) a*c*sin(d*e) + b """ if isinstance(sequence, dict): sequence = sequence.items() subst = [] for pattern in sequence: for i, (expr, _) in enumerate(subst): if pattern[0] in expr: subst.insert(i, pattern) break else: subst.append(pattern) subst.reverse() return self._subs_list(subst) def __contains__(self, obj): if self == obj: return True for arg in self.args: try: if obj in arg: return True except TypeError: if obj == arg: return True return False @cacheit def has(self, *patterns): """ Test whether any subexpression matches any of the patterns. Examples: >>> from sympy import sin, S >>> from sympy.abc import x, y, z >>> (x**2 + sin(x*y)).has(z) False >>> (x**2 + sin(x*y)).has(x, y, z) True >>> x.has(x) True Note that ``expr.has(*patterns)`` is exactly equivalent to ``any(expr.has(p) for p in patterns)``. In particular, ``False`` is returned when the list of patterns is empty. >>> x.has() False """ def search(expr, test): if not isinstance(expr, Basic): try: return any(search(i, test) for i in expr) except TypeError: return False elif test(expr): return True else: return any(search(i, test) for i in expr.iter_basic_args()) def _match(p): if isinstance(p, BasicType): return lambda w: isinstance(w, p) else: return lambda w: p.matches(w) is not None patterns = map(sympify, patterns) return any(search(self, _match(p)) for p in patterns) def replace(self, query, value, map=False): """ Replace matching subexpressions of ``self`` with ``value``. If ``map = True`` then also return the mapping {old: new} where ``old`` was a sub-expression found with query and ``new`` is the replacement value for it. Traverses an expression tree and performs replacement of matching subexpressions from the bottom to the top of the tree. The list of possible combinations of queries and replacement values is listed below: 1.1. type -> type obj.replace(sin, tan) 1.2. type -> func obj.replace(sin, lambda expr, arg: ...) 2.1. expr -> expr obj.replace(sin(a), tan(a)) 2.2. expr -> func obj.replace(sin(a), lambda a: ...) 3.1. func -> func obj.replace(lambda expr: ..., lambda expr: ...) Examples: >>> from sympy import log, sin, cos, tan, Wild >>> from sympy.abc import x >>> f = log(sin(x)) + tan(sin(x**2)) >>> f.replace(sin, cos) log(cos(x)) + tan(cos(x**2)) >>> f.replace(sin, lambda arg: sin(2*arg)) log(sin(2*x)) + tan(sin(2*x**2)) >>> sin(x).replace(sin, cos, map=True) (cos(x), {sin(x): cos(x)}) >>> a = Wild('a') >>> f.replace(sin(a), cos(a)) log(cos(x)) + tan(cos(x**2)) >>> f.replace(sin(a), lambda a: sin(2*a)) log(sin(2*x)) + tan(sin(2*x**2)) >>> g = 2*sin(x**3) >>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) 4*sin(x**9) """ if isinstance(query, type): _query = lambda expr: isinstance(expr, query) if isinstance(value, type): _value = lambda expr, result: value(*expr.args) elif callable(value): _value = lambda expr, result: value(*expr.args) else: raise TypeError("given a type, replace() expects another type or a callable") elif isinstance(query, Basic): _query = lambda expr: expr.match(query) if isinstance(value, Basic): _value = lambda expr, result: value.subs(result) elif callable(value): _value = lambda expr, result: value(**dict([ (str(key)[:-1], val) for key, val in result.iteritems() ])) else: raise TypeError("given an expression, replace() expects another expression or a callable") elif callable(query): _query = query if callable(value): _value = lambda expr, result: value(expr) else: raise TypeError("given a callable, replace() expects another callable") else: raise TypeError("first argument to replace() must be a type, an expression or a callable") mapping = {} def rec_replace(expr): args, construct = [], False for arg in expr.args: result = rec_replace(arg) if result is not None: construct = True else: result = arg args.append(result) if construct: return expr.__class__(*args) else: result = _query(expr) if result: value = _value(expr, result) if map: mapping[expr] = value return value else: return None result = rec_replace(self) if result is None: result = self if not map: return result else: return result, mapping def find(self, query, group=False): """Find all subexpressions matching a query. """ if isinstance(query, type): _query = lambda expr: isinstance(expr, query) elif isinstance(query, Basic): _query = lambda expr: expr.match(query) else: _query = query results = [] def rec_find(expr): if _query(expr): results.append(expr) for arg in expr.args: rec_find(arg) rec_find(self) if not group: return set(results) else: groups = {} for result in results: if result in groups: groups[result] += 1 else: groups[result] = 1 return groups def count(self, query): """Count the number of matching subexpressions. """ return sum(self.find(query, group=True).values()) def matches(self, expr, repl_dict={}, evaluate=False): """ Helper method for match() - switches the pattern and expr. Can be used to solve linear equations: >>> from sympy import Symbol, Wild, Integer >>> a,b = map(Symbol, 'ab') >>> x = Wild('x') >>> (a+b*x).matches(Integer(0)) {x_: -a/b} """ if evaluate: return self.subs(repl_dict).matches(expr, repl_dict) expr = sympify(expr) if not isinstance(expr, self.__class__): return None if self == expr: return repl_dict if len(self.args) != len(expr.args): return None d = repl_dict.copy() for arg, other_arg in zip(self.args, expr.args): if arg == other_arg: continue d = arg.subs(d).matches(other_arg, d) if d is None: return None return d def match(self, pattern): """ Pattern matching. Wild symbols match all. Return ``None`` when expression (self) does not match with pattern. Otherwise return a dictionary such that:: pattern.subs(self.match(pattern)) == self Example: >>> from sympy import symbols, Wild >>> from sympy.abc import x, y >>> p = Wild("p") >>> q = Wild("q") >>> r = Wild("r") >>> e = (x+y)**(x+y) >>> e.match(p**p) {p_: x + y} >>> e.match(p**q) {p_: x + y, q_: x + y} >>> e = (2*x)**2 >>> e.match(p*q**r) {p_: 4, q_: x, r_: 2} >>> (p*q**r).subs(e.match(p*q**r)) 4*x**2 """ pattern = sympify(pattern) return pattern.matches(self) def count_ops(self, visual=None): """wrapper for count_ops that returns the operation count.""" from sympy import count_ops return count_ops(self, visual) return sum(a.count_ops(visual) for a in self.args) def doit(self, **hints): """Evaluate objects that are not evaluated by default like limits, integrals, sums and products. All objects of this kind will be evaluated recursively, unless some species were excluded via 'hints' or unless the 'deep' hint was set to 'False'. >>> from sympy import Integral >>> from sympy.abc import x, y >>> 2*Integral(x, x) 2*Integral(x, x) >>> (2*Integral(x, x)).doit() x**2 >>> (2*Integral(x, x)).doit(deep = False) 2*Integral(x, x) """ if hints.get('deep', True): terms = [ term.doit(**hints) for term in self.args ] return self.func(*terms) else: return self def _eval_rewrite(self, pattern, rule, **hints): if self.is_Atom: return self sargs = self.args terms = [ t._eval_rewrite(pattern, rule, **hints) for t in sargs ] return self.func(*terms) def rewrite(self, *args, **hints): """Rewrites expression containing applications of functions of one kind in terms of functions of different kind. For example you can rewrite trigonometric functions as complex exponentials or combinatorial functions as gamma function. As a pattern this function accepts a list of functions to to rewrite (instances of DefinedFunction class). As rule you can use string or a destination function instance (in this case rewrite() will use the str() function). There is also possibility to pass hints on how to rewrite the given expressions. For now there is only one such hint defined called 'deep'. When 'deep' is set to False it will forbid functions to rewrite their contents. >>> from sympy import sin, exp, I >>> from sympy.abc import x, y >>> sin(x).rewrite(sin, exp) -I*(exp(I*x) - exp(-I*x))/2 """ if self.is_Atom or not args: return self else: pattern = args[:-1] rule = '_eval_rewrite_as_' + str(args[-1]) if not pattern: return self._eval_rewrite(None, rule, **hints) else: if iterable(pattern[0]): pattern = pattern[0] pattern = [ p.__class__ for p in pattern if self.has(p) ] if pattern: return self._eval_rewrite(tuple(pattern), rule, **hints) else: return self class Atom(Basic): """ A parent class for atomic things. An atom is an expression with no subexpressions. Examples: Symbol, Number, Rational, Integer, ... But not: Add, Mul, Pow, ... """ is_Atom = True __slots__ = [] def matches(self, expr, repl_dict={}, evaluate=False): if self == expr: return repl_dict def _eval_subs(self, old, new): if self == old: return new else: return self def doit(self, **hints): return self def __contains__(self, obj): return (self == obj) @classmethod def class_key(cls): return 2, 0, cls.__name__ def sort_key(self, order=None): from sympy.core import S return self.class_key(), (1, (self,)), S.One.sort_key(), S.One wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/core.py0000644000175000017500000001767212014170666023346 0ustar georgeskgeorgesk""" The core's core. """ from assumptions import AssumeMeths, make__get_assumption from sympy.core.compatibility import cmp # used for canonical ordering of symbolic sequences # via __cmp__ method: # FIXME this is *so* irrelevant and outdated! ordering_of_classes = [ # singleton numbers 'Zero', 'One','Half','Infinity','NaN','NegativeOne','NegativeInfinity', # numbers 'Integer','Rational','Float', # singleton symbols 'Exp1','Pi','ImaginaryUnit', # symbols 'Symbol','Wild','Temporary', # Functions that should come before Pow/Add/Mul 'ApplyConjugate', 'ApplyAbs', # arithmetic operations 'Pow', 'Mul', 'Add', # function values 'Apply', 'ApplyExp','ApplyLog', 'ApplySin','ApplyCos','ApplyTan','ApplyCot', 'ApplyASin','ApplyACos','ApplyATan','ApplyACot', 'ApplySinh','ApplyCosh','ApplyTanh','ApplyCoth', 'ApplyASinh','ApplyACosh','ApplyATanh','ApplyACoth', 'ApplyRisingFactorial','ApplyFallingFactorial', 'ApplyFactorial','ApplyBinomial', 'ApplyFloor', 'ApplyCeiling', 'ApplyRe','ApplyIm', 'ApplyArg', 'ApplySqrt','ApplySign', 'ApplyGamma','ApplyLowerGamma','ApplyUpperGamma','ApplyPolyGamma', 'ApplyErf', 'ApplyChebyshev','ApplyChebyshev2', 'Derivative','Integral', # defined singleton functions 'Abs','Sign','Sqrt', 'Floor', 'Ceiling', 'Re', 'Im', 'Arg', 'Conjugate', 'Exp','Log', 'Sin','Cos','Tan','Cot','ASin','ACos','ATan','ACot', 'Sinh','Cosh','Tanh','Coth','ASinh','ACosh','ATanh','ACoth', 'RisingFactorial','FallingFactorial', 'factorial','binomial', 'Gamma','LowerGamma','UpperGamma','PolyGamma', 'Erf', # special polynomials 'Chebyshev','Chebyshev2', # undefined functions 'Function','WildFunction', # anonymous functions 'Lambda', # Landau O symbol 'Order', # relational operations 'Equality', 'Unequality', 'StrictInequality', 'Inequality', ] class BasicType(type): pass class Registry(object): """ Base class for registry objects. Registries map a name to an object using attribute notation. Registry classes behave singletonically: all their instances share the same state, which is stored in the class object. All subclasses should set `__slots__ = []`. """ __slots__ = [] def __setattr__(self, name, obj): setattr(self.__class__, name, obj) def __delattr__(self, name): delattr(self.__class__, name) #A set containing all sympy class objects, kept in sync with C all_classes = set() class ClassRegistry(Registry): """ Namespace for SymPy classes This is needed to avoid problems with cyclic imports. To get a SymPy class, use `C.` e.g. `C.Rational`, `C.Add`. For performance reasons, this is coupled with a set `all_classes` holding the classes, which should not be modified directly. """ __slots__ = [] def __setattr__(self, name, cls): Registry.__setattr__(self, name, cls) all_classes.add(cls) def __delattr__(self, name): cls = getattr(self, name) Registry.__delattr__(self, name) # The same class could have different names, so make sure # it's really gone from C before removing it from all_classes. if cls not in self.__class__.__dict__.itervalues(): all_classes.remove(cls) C = ClassRegistry() class BasicMeta(BasicType): def __init__(cls, *args, **kws): setattr(C, cls.__name__, cls) # --- assumptions --- # initialize default_assumptions dictionary default_assumptions = {} for k,v in cls.__dict__.iteritems(): if not k.startswith('is_'): continue # this is not an assumption (e.g. is_Integer) if k[3:] not in AssumeMeths._assume_defined: continue k = k[3:] if isinstance(v,(bool,int,long,type(None))): if v is not None: v = bool(v) default_assumptions[k] = v # XXX maybe we should try to keep ._default_premises out of class ? # XXX __slots__ in class ? cls._default_premises = default_assumptions for base in cls.__bases__: try: base_premises = base._default_premises except AttributeError: continue # no ._default_premises is ok for k,v in base_premises.iteritems(): # if an assumption is already present in child, we should ignore base # e.g. Integer.is_integer=T, but Rational.is_integer=F (for speed) if k in default_assumptions: continue default_assumptions[k] = v # deduce all consequences from default assumptions -- make it complete xass = AssumeMeths._assume_rules.deduce_all_facts(default_assumptions) # and store completed set into cls -- this way we'll avoid rededucing # extensions of class default assumptions each time on instance # creation -- we keep it prededuced already. cls.default_assumptions = xass # let's store new derived assumptions back into class. # this will result in faster access to this attributes. # # Timings # ------- # # a = Integer(5) # %timeit a.is_zero -> 20 us (without this optimization) # %timeit a.is_zero -> 2 us (with this optimization) # # # BTW: it is very important to study the lessons learned here -- # we could drop Basic.__getattr__ completely (!) # # %timeit x.is_Add -> 2090 ns (Basic.__getattr__ present) # %timeit x.is_Add -> 825 ns (Basic.__getattr__ absent) # # so we may want to override all assumptions is_ methods and # remove Basic.__getattr__ # first we need to collect derived premises derived_premises = {} for k,v in xass.iteritems(): if k not in default_assumptions: derived_premises[k] = v cls._derived_premises = derived_premises for k,v in xass.iteritems(): assert v == cls.__dict__.get('is_'+k, v), (cls,k,v) # NOTE: this way Integer.is_even = False (inherited from Rational) # NOTE: the next code blocks add 'protection-properties' to overcome this setattr(cls, 'is_'+k, v) # protection e.g. for Initeger.is_even=F <- (Rational.is_integer=F) for base in cls.__bases__: try: base_derived_premises = base._derived_premises except AttributeError: continue # no ._derived_premises is ok for k,v in base_derived_premises.iteritems(): if ('is_'+k) not in cls.__dict__: is_k = make__get_assumption(cls.__name__, k) setattr(cls, 'is_'+k, property(is_k)) def __cmp__(cls, other): # If the other object is not a Basic subclass, then we are not equal to # it. if not isinstance(other, BasicType): return -1 n1 = cls.__name__ n2 = other.__name__ c = cmp(n1,n2) if not c: return 0 UNKNOWN = len(ordering_of_classes)+1 try: i1 = ordering_of_classes.index(n1) except ValueError: #print 'Add',n1,'to basic.ordering_of_classes list' #return c i1 = UNKNOWN try: i2 = ordering_of_classes.index(n2) except ValueError: #print 'Add',n2,'to basic.ordering_of_classes list' #return c i2 = UNKNOWN if i1 == UNKNOWN and i2 == UNKNOWN: return c return cmp(i1,i2) def __lt__(cls, other): if cls.__cmp__(other)==-1: return True return False def __gt__(cls, other): if cls.__cmp__(other)==1: return True return False C.BasicMeta = BasicMeta wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/exprtools.py0000644000175000017500000002323412014170666024444 0ustar georgeskgeorgesk"""Tools for manipulating of large commutative expressions. """ from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.power import Pow from sympy.core.basic import Basic from sympy.core.sympify import sympify from sympy.core.numbers import Rational from sympy.core.singleton import S from sympy.core.coreerrors import NonCommutativeExpression from sympy.core.containers import Tuple def decompose_power(expr): """ Decompose power into symbolic base and integer exponent. **Example** >>> from sympy.core.exprtools import decompose_power >>> from sympy.abc import x, y >>> decompose_power(x) (x, 1) >>> decompose_power(x**2) (x, 2) >>> decompose_power(x**(2*y)) (x**y, 2) >>> decompose_power(x**(2*y/3)) (x**(y/3), 2) """ base, exp = expr.as_base_exp() if exp.is_Number: if exp.is_Rational: if not exp.is_Integer: base = Pow(base, Rational(1, exp.q)) exp = exp.p else: base, exp = expr, 1 else: exp, tail = exp.as_coeff_mul() if exp.is_Rational: if not exp.is_Integer: tail += (Rational(1, exp.q),) base, exp = Pow(base, Mul(*tail)), exp.p else: base, exp = expr, 1 return base, exp class Factors(object): """Efficient representation of ``f_1*f_2*...*f_n``. """ __slots__ = ['factors', 'gens'] def __init__(self, factors=None): if factors is None: factors = {} self.factors = factors self.gens = frozenset(factors.keys()) def __hash__(self): return hash((tuple(self.factors), self.gens)) def __repr__(self): return "Factors(%s)" % self.factors def as_expr(self): return Mul(*[ factor**exp for factor, exp in self.factors.iteritems() ]) def normal(self, other): self_factors = dict(self.factors) other_factors = dict(other.factors) for factor, self_exp in self.factors.iteritems(): try: other_exp = other.factors[factor] except KeyError: continue exp = self_exp - other_exp if not exp: del self_factors[factor] del other_factors[factor] else: if exp > 0: self_factors[factor] = exp del other_factors[factor] else: del self_factors[factor] other_factors[factor] = -exp return Factors(self_factors), Factors(other_factors) def mul(self, other): factors = dict(self.factors) for factor, exp in other.factors.iteritems(): if factor in factors: exp = factors[factor] + exp if not exp: del factors[factor] continue factors[factor] = exp return Factors(factors) def div(self, other): quo, rem = dict(self.factors), {} for factor, exp in other.factors.iteritems(): if factor in quo: exp = quo[factor] - exp if exp <= 0: del quo[factor] if exp >= 0: if exp: quo[factor] = exp continue exp = -exp rem[factor] = exp return Factors(quo), Factors(rem) def quo(self, other): return self.div(other)[0] def rem(self, other): return self.div(other)[1] def pow(self, other): if type(other) is int and other >= 0: factors = {} if other: for factor, exp in self.factors.iteritems(): factors[factor] = exp*other return Factors(factors) else: raise ValueError("expected non-negative integer, got %s" % other) def gcd(self, other): factors = {} for factor, exp in self.factors.iteritems(): if factor in other.factors: exp = min(exp, other.factors[factor]) factors[factor] = exp return Factors(factors) def lcm(self, other): factors = dict(self.factors) for factor, exp in other.factors.iteritems(): if factor in factors: exp = max(exp, factors[factor]) factors[factor] = exp return Factors(factors) def __mul__(self, other): if isinstance(other, Factors): return self.mul(other) else: return NotImplemented def __divmod__(self, other): if isinstance(other, Factors): return self.div(other) else: return NotImplemented def __div__(self, other): if isinstance(other, Factors): return self.quo(other) else: return NotImplemented __truediv__ = __div__ def __mod__(self, other): if isinstance(other, Factors): return self.rem(other) else: return NotImplemented def __pow__(self, other): if type(other) is int: return self.pow(other) else: return NotImplemented def __eq__(self, other): return self.factors == other.factors def __ne__(self, other): return not self.__eq__(other) class Term(object): """Efficient representation of ``coeff*(numer/denom)``. """ __slots__ = ['coeff', 'numer', 'denom'] def __init__(self, term, numer=None, denom=None): if numer is None and denom is None: if not term.is_commutative: raise NonCommutativeExpression('commutative expression expected') coeff, factors = term.as_coeff_mul() numer, denom = {}, {} for factor in factors: base, exp = decompose_power(factor) if base.is_Add: cont, base = base.primitive() coeff *= cont**exp if exp > 0: numer[base] = exp else: denom[base] = -exp numer = Factors(numer) denom = Factors(denom) else: coeff = term if numer is None: numer = Factors() if denom is None: denom = Factors() self.coeff = coeff self.numer = numer self.denom = denom def __hash__(self): return hash((self.coeff, self.numer, self.denom)) def __repr__(self): return "Term(%s, %s, %s)" % (self.coeff, self.numer, self.denom) def as_expr(self): return self.coeff*(self.numer.as_expr()/self.denom.as_expr()) def mul(self, other): coeff = self.coeff*other.coeff numer = self.numer.mul(other.numer) denom = self.denom.mul(other.denom) numer, denom = numer.normal(denom) return Term(coeff, numer, denom) def inv(self): return Term(1/self.coeff, self.denom, self.numer) def quo(self, other): return self.mul(other.inv()) def pow(self, other): if other < 0: return self.inv().pow(-other) else: return Term(self.coeff ** other, self.numer.pow(other), self.denom.pow(other)) def gcd(self, other): return Term(self.coeff.gcd(other.coeff), self.numer.gcd(other.numer), self.denom.gcd(other.denom)) def lcm(self, other): return Term(self.coeff.lcm(other.coeff), self.numer.lcm(other.numer), self.denom.lcm(other.denom)) def __mul__(self, other): if isinstance(other, Term): return self.mul(other) else: return NotImplemented def __div__(self, other): if isinstance(other, Term): return self.quo(other) else: return NotImplemented __truediv__ = __div__ def __pow__(self, other): if type(other) is int: return self.pow(other) else: return NotImplemented def __eq__(self, other): return (self.coeff == other.coeff and self.numer == other.numer and self.denom == other.denom) def __ne__(self, other): return not self.__eq__(other) def _gcd_terms(terms): """Helper function for :func:`gcd_terms`. """ if isinstance(terms, Basic) and not isinstance(terms, Tuple): terms = Add.make_args(terms) if len(terms) <= 1: if not terms: return S.Zero, S.Zero, S.One else: return terms[0], S.One, S.One terms = map(Term, terms) cont = terms[0] for term in terms[1:]: cont = cont.gcd(term) for i, term in enumerate(terms): terms[i] = term.quo(cont) denom = terms[0].denom for term in terms[1:]: denom = denom.lcm(term.denom) numers = [] for term in terms: numer = term.numer.mul(denom.quo(term.denom)) numers.append(term.coeff*numer.as_expr()) cont = cont.as_expr() numer = Add(*numers) denom = denom.as_expr() if numer.is_Add: _cont, numer = numer.primitive() cont *= _cont return cont, numer, denom def gcd_terms(terms): """ Compute the GCD of ``terms`` and put them together. **Example** >>> from sympy.core import gcd_terms >>> from sympy.abc import x, y >>> gcd_terms((x + 1)**2*y + (x + 1)*y**2) y*(x + 1)*(x + y + 1) """ from sympy.polys.polytools import _keep_coeff cont, numer, denom = _gcd_terms(sympify(terms)) coeff, factors = cont.as_coeff_Mul() return _keep_coeff(coeff, factors*numer/denom) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/symbol.py0000644000175000017500000002750312014170666023715 0ustar georgeskgeorgeskfrom basic import Basic from core import C from sympify import sympify from singleton import S from expr import Expr, AtomicExpr from cache import cacheit from function import FunctionClass from sympy.logic.boolalg import Boolean import re import warnings class Symbol(AtomicExpr, Boolean): """ Assumptions: commutative = True You can override the default assumptions in the constructor: >>> from sympy import symbols >>> A,B = symbols('A,B', commutative = False) >>> bool(A*B != B*A) True >>> bool(A*B*2 == 2*A*B) == True # multiplication by scalars is commutative True """ is_comparable = False __slots__ = ['is_commutative', 'name'] is_Symbol = True def __new__(cls, name, commutative=True, **assumptions): """Symbols are identified by name and assumptions:: >>> from sympy import Symbol >>> Symbol("x") == Symbol("x") True >>> Symbol("x", real=True) == Symbol("x", real=False) False """ if 'dummy' in assumptions: warnings.warn( "\nThe syntax Symbol('x', dummy=True) is deprecated and will" "\nbe dropped in a future version of Sympy. Please use Dummy()" "\nor symbols(..., cls=Dummy) to create dummy symbols.", DeprecationWarning) if assumptions.pop('dummy'): return Dummy(name, commutative, **assumptions) return Symbol.__xnew_cached_(cls, name, commutative, **assumptions) def __new_stage2__(cls, name, commutative=True, **assumptions): assert isinstance(name, str),repr(type(name)) obj = Expr.__new__(cls, **assumptions) obj.is_commutative = commutative obj.name = name return obj __xnew__ = staticmethod(__new_stage2__) # never cached (e.g. dummy) __xnew_cached_ = staticmethod(cacheit(__new_stage2__)) # symbols are always cached def __getnewargs__(self): return (self.name, self.is_commutative) def _hashable_content(self): return (self.is_commutative, self.name) def sort_key(self, order=None): from sympy.core import S return self.class_key(), (1, (str(self),)), S.One.sort_key(), S.One def as_dummy(self): assumptions = self.assumptions0.copy() assumptions.pop('commutative', None) return Dummy(self.name, self.is_commutative, **assumptions) def __call__(self, *args): from function import Function return Function(self.name, nargs=len(args))(*args, **self.assumptions0) def as_real_imag(self, deep=True): return (C.re(self), C.im(self)) def _eval_expand_complex(self, deep=True, **hints): re, im = self.as_real_imag() return re + im*S.ImaginaryUnit def _sage_(self): import sage.all as sage return sage.var(self.name) @property def is_number(self): return False @property def free_symbols(self): return set([self]) class Dummy(Symbol): """Dummy symbols are each unique, identified by an internal count index: >>> from sympy import Dummy >>> bool(Dummy("x") == Dummy("x")) == True False If a name is not supplied then a string value of the count index will be used. This is useful when a temporary variable is needed and the name of the variable used in the expression is not important. >>> Dummy._count = 0 # /!\ this should generally not be changed; it is being >>> Dummy() # used here to make sure that the doctest passes. _0 """ _count = 0 __slots__ = ['dummy_index'] is_Dummy = True def __new__(cls, name=None, commutative=True, **assumptions): if name is None: name = str(Dummy._count) obj = Symbol.__xnew__(cls, name, commutative=commutative, **assumptions) Dummy._count += 1 obj.dummy_index = Dummy._count return obj def _hashable_content(self): return Symbol._hashable_content(self) + (self.dummy_index,) class Wild(Symbol): """ Wild() matches any expression but another Wild(). """ __slots__ = ['exclude', 'properties'] is_Wild = True def __new__(cls, name, exclude=None, properties=None, **assumptions): if type(exclude) is list: exclude = tuple(exclude) if type(properties) is list: properties = tuple(properties) return Wild.__xnew__(cls, name, exclude, properties, **assumptions) def __getnewargs__(self): return (self.name, self.exclude, self.properties) @staticmethod @cacheit def __xnew__(cls, name, exclude, properties, **assumptions): obj = Symbol.__xnew__(cls, name, **assumptions) if exclude is None: obj.exclude = None else: obj.exclude = tuple([sympify(x) for x in exclude]) if properties is None: obj.properties = None else: obj.properties = tuple(properties) return obj def _hashable_content(self): return (self.name, self.exclude, self.properties ) # TODO add check against another Wild def matches(self, expr, repl_dict={}, evaluate=False): if self in repl_dict: if repl_dict[self] == expr: return repl_dict else: return None if self.exclude: for x in self.exclude: if x in expr: return None if self.properties: for f in self.properties: if not f(expr): return None repl_dict = repl_dict.copy() repl_dict[self] = expr return repl_dict def __call__(self, *args, **assumptions): from sympy.core.function import WildFunction return WildFunction(self.name, nargs=len(args))(*args, **assumptions) _re_var_range = re.compile(r"^(.*?)(\d*):(\d+)$") _re_var_scope = re.compile(r"^(.):(.)$") _re_var_split = re.compile(r"\s*,\s*|\s+") def symbols(names, **args): """ Transform strings into instances of :class:`Symbol` class. :func:`symbols` function returns a sequence of symbols with names taken from ``names`` argument, which can be a comma or whitespace delimited string, or a sequence of strings:: >>> from sympy import symbols, Function >>> x, y, z = symbols('x,y,z') >>> a, b, c = symbols('a b c') The type of output is dependent on the properties of input arguments:: >>> symbols('x') x >>> symbols('x,') (x,) >>> symbols('x,y') (x, y) >>> symbols(('a', 'b', 'c')) (a, b, c) >>> symbols(['a', 'b', 'c']) [a, b, c] >>> symbols(set(['a', 'b', 'c'])) set([a, b, c]) If an iterable container is needed for a single symbol, set the ``seq`` argument to ``True`` or terminate the symbol name with a comma:: >>> symbols('x', seq=True) (x,) To reduce typing, range syntax is supported to create indexed symbols:: >>> symbols('x:10') (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) >>> symbols('x5:10') (x5, x6, x7, x8, x9) >>> symbols('x5:10,y:5') (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4) >>> symbols(('x5:10', 'y:5')) ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4)) To reduce typing even more, lexicographic range syntax is supported:: >>> symbols('x:z') (x, y, z) >>> symbols('a:d,x:z') (a, b, c, d, x, y, z) >>> symbols(('a:d', 'x:z')) ((a, b, c, d), (x, y, z)) All newly created symbols have assumptions set accordingly to ``args``:: >>> a = symbols('a', integer=True) >>> a.is_integer True >>> x, y, z = symbols('x,y,z', real=True) >>> x.is_real and y.is_real and z.is_real True Despite its name, :func:`symbols` can create symbol--like objects of other type, for example instances of Function or Wild classes. To achieve this, set ``cls`` keyword argument to the desired type:: >>> symbols('f,g,h', cls=Function) (f, g, h) >>> type(_[0]) """ result = [] if 'each_char' in args: warnings.warn("The each_char option to symbols() and var() is " "deprecated. Separate symbol names by spaces or commas instead.", DeprecationWarning) if isinstance(names, basestring): names = names.strip() as_seq= names.endswith(',') if as_seq: names = names[:-1].rstrip() if not names: raise ValueError('no symbols given') names = _re_var_split.split(names) if args.pop('each_char', False) and not as_seq and len(names) == 1: return symbols(tuple(names[0]), **args) cls = args.pop('cls', Symbol) seq = args.pop('seq', as_seq) for name in names: if not name: raise ValueError('missing symbol') if ':' not in name: symbol = cls(name, **args) result.append(symbol) continue match = _re_var_range.match(name) if match is not None: name, start, end = match.groups() if not start: start = 0 else: start = int(start) for i in xrange(start, int(end)): symbol = cls("%s%i" % (name, i), **args) result.append(symbol) seq = True continue match = _re_var_scope.match(name) if match is not None: start, end = match.groups() for name in xrange(ord(start), ord(end)+1): symbol = cls(chr(name), **args) result.append(symbol) seq = True continue raise ValueError("'%s' is not a valid symbol range specification" % name) if not seq and len(result) <= 1: if not result: raise ValueError('missing symbol') # should never happen return result[0] return tuple(result) else: for name in names: result.append(symbols(name, **args)) return type(names)(result) def var(names, **args): """ Create symbols and inject them into the global namespace. This calls :func:`symbols` with the same arguments and puts the results into the *global* namespace. It's recommended not to use :func:`var` in library code, where :func:`symbols` has to be used:: >>> from sympy import var >>> var('x') x >>> x x >>> var('a,ab,abc') (a, ab, abc) >>> abc abc >>> var('x,y', real=True) (x, y) >>> x.is_real and y.is_real True See :func:`symbol` documentation for more details on what kinds of arguments can be passed to :func:`var`. """ def traverse(symbols, frame): """Recursively inject symbols to the global namespace. """ for symbol in symbols: if isinstance(symbol, Basic): frame.f_globals[symbol.name] = symbol elif isinstance(symbol, FunctionClass): frame.f_globals[symbol.__name__] = symbol else: traverse(symbol, frame) from inspect import currentframe frame = currentframe().f_back try: syms = symbols(names, **args) if syms is not None: if isinstance(syms, Basic): frame.f_globals[syms.name] = syms elif isinstance(syms, FunctionClass): frame.f_globals[syms.__name__] = syms else: traverse(syms, frame) finally: del frame # break cyclic dependencies as stated in inspect docs return syms wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/add.py0000644000175000017500000004437712014170666023150 0ustar georgeskgeorgeskfrom core import C from basic import Basic from singleton import S from operations import AssocOp from cache import cacheit from expr import Expr class Add(AssocOp): __slots__ = [] is_Add = True #identity = S.Zero # cyclic import, so defined in numbers.py @classmethod def flatten(cls, seq): """ Takes the sequence "seq" of nested Adds and returns a flatten list. Returns: (commutative_part, noncommutative_part, order_symbols) Applies associativity, all terms are commutable with respect to addition. """ terms = {} # term -> coeff # e.g. x**2 -> 5 for ... + 5*x**2 + ... coeff = S.Zero # standalone term # e.g. 3 + ... order_factors = [] for o in seq: # O(x) if o.is_Order: for o1 in order_factors: if o1.contains(o): o = None break if o is None: continue order_factors = [o]+[o1 for o1 in order_factors if not o.contains(o1)] continue # 3 or NaN elif o.is_Number: if o is S.NaN or coeff is S.ComplexInfinity and o.is_bounded is False: # we know for sure the result will be nan return [S.NaN], [], None if coeff.is_Number: coeff += o if coeff is S.NaN: # we know for sure the result will be nan return [S.NaN], [], None continue elif o is S.ComplexInfinity: if coeff.is_bounded is False: # we know for sure the result will be nan return [S.NaN], [], None coeff = S.ComplexInfinity continue # Add([...]) elif o.is_Add: # NB: here we assume Add is always commutative seq.extend(o.args) # TODO zerocopy? continue # Mul([...]) elif o.is_Mul: c, s = o.as_coeff_Mul() # 3*... if c.is_Number: # unevaluated 2-arg Mul if s.is_Add and s.is_commutative: seq.extend([c*a for a in s.args]) continue # everything else else: c = S.One s = o # now we have: # o = c*s, where # # c is a Number # s is an expression with number factor extracted # let's collect terms with the same s, so e.g. # 2*x**2 + 3*x**2 -> 5*x**2 if s in terms: terms[s] += c else: terms[s] = c # now let's construct new args: # [2*x**2, x**3, 7*x**4, pi, ...] newseq = [] noncommutative = False for s,c in terms.items(): # 0*s if c is S.Zero: continue # 1*s elif c is S.One: newseq.append(s) # c*s else: if s.is_Mul: # Mul, already keeps its arguments in perfect order. # so we can simply put c in slot0 and go the fast way. cs = s._new_rawargs(*((c,) + s.args)) newseq.append(cs) else: # alternatively we have to call all Mul's machinery (slow) newseq.append(Mul(c,s)) noncommutative = noncommutative or not s.is_commutative # oo, -oo if coeff is S.Infinity: newseq = [f for f in newseq if not (f.is_nonnegative or f.is_real and (f.is_bounded or f.is_finite or f.is_infinitesimal))] elif coeff is S.NegativeInfinity: newseq = [f for f in newseq if not (f.is_nonpositive or f.is_real and (f.is_bounded or f.is_finite or f.is_infinitesimal))] if coeff is S.ComplexInfinity: # zoo might be # unbounded_real + bounded_im # bounded_real + unbounded_im # unbounded_real + unbounded_im # addition of a bounded real or imaginary number won't be able to # change the zoo nature; if unbounded a NaN condition could result if # the unbounded symbol had sign opposite of the unbounded portion of zoo, # e.g. unbounded_real - unbounded_real newseq = [c for c in newseq if not (c.is_bounded and c.is_real is not None)] # process O(x) if order_factors: newseq2 = [] for t in newseq: for o in order_factors: # x + O(x) -> O(x) if o.contains(t): t = None break # x + O(x**2) -> x + O(x**2) if t is not None: newseq2.append(t) newseq = newseq2 + order_factors # 1 + O(1) -> O(1) for o in order_factors: if o.contains(coeff): coeff = S.Zero break # order args canonically # Currently we sort things using hashes, as it is quite fast. A better # solution is not to sort things at all - but this needs some more # fixing. newseq.sort(key=hash) # current code expects coeff to be always in slot-0 if coeff is not S.Zero: newseq.insert(0, coeff) # we are done if noncommutative: return [], newseq, None else: return newseq, [], None @classmethod def class_key(cls): return 3, 1, cls.__name__ @cacheit def as_coeff_add(self, *deps): if deps: l1 = [] l2 = [] for f in self.args: if f.has(*deps): l2.append(f) else: l1.append(f) return self._new_rawargs(*l1), tuple(l2) coeff, notrat = self.args[0].as_coeff_add() if not coeff is S.Zero: return coeff, notrat + self.args[1:] return S.Zero, self.args # Note, we intentionally do not implement Add.as_coeff_mul(). Rather, we # let Expr.as_coeff_mul() just always return (S.One, self) for an Add. See # issue 2425. def _eval_derivative(self, s): return Add(*[f.diff(s) for f in self.args]) def _eval_nseries(self, x, n, logx): terms = [t.nseries(x, n=n, logx=logx) for t in self.args] return Add(*terms) def _matches_simple(self, expr, repl_dict): # handle (w+3).matches('x+5') -> {w: x+2} coeff, terms = self.as_coeff_add() if len(terms)==1: return terms[0].matches(expr - coeff, repl_dict) return matches = AssocOp._matches_commutative @staticmethod def _combine_inverse(lhs, rhs): """ Returns lhs - rhs, but treats arguments like symbols, so things like oo - oo return 0, instead of a nan. """ from sympy import oo, I, expand_mul if lhs == oo and rhs == oo or lhs == oo*I and rhs == oo*I: return S.Zero return expand_mul(lhs - rhs) @cacheit def as_two_terms(self): """Return head and tail of self. This is the most efficient way to get the head and tail of an expression. - if you want only the head, use self.args[0]; - if you want to process the arguments of the tail then use self.as_coef_add() which gives the head and a tuple containing the arguments of the tail when treated as an Add. - if you want the coefficient when self is treated as a Mul then use self.as_coeff_mul()[0] >>> from sympy.abc import x, y >>> (3*x*y).as_two_terms() (3, x*y) """ if len(self.args) == 1: return S.Zero, self return self.args[0], self._new_rawargs(*self.args[1:]) def as_numer_denom(self): numers, denoms = [],[] for n,d in [f.as_numer_denom() for f in self.args]: numers.append(n) denoms.append(d) r = xrange(len(numers)) return Add(*[Mul(*(denoms[:i]+[numers[i]]+denoms[i+1:])) for i in r]), Mul(*denoms) def _eval_is_polynomial(self, syms): return all(term._eval_is_polynomial(syms) for term in self.args) def _eval_is_rational_function(self, syms): return all(term._eval_is_rational_function(syms) for term in self.args) # assumption methods _eval_is_real = lambda self: self._eval_template_is_attr('is_real') _eval_is_bounded = lambda self: self._eval_template_is_attr('is_bounded') _eval_is_commutative = lambda self: self._eval_template_is_attr('is_commutative') _eval_is_integer = lambda self: self._eval_template_is_attr('is_integer') _eval_is_comparable = lambda self: self._eval_template_is_attr('is_comparable') def _eval_is_odd(self): l = [f for f in self.args if not (f.is_even==True)] if not l: return False if l[0].is_odd: return self._new_rawargs(*l[1:]).is_even def _eval_is_irrational(self): for t in self.args: a = t.is_irrational if a: return True if a is None: return return False def _eval_is_positive(self): c, r = self.as_two_terms() if c.is_positive and r.is_positive: return True if c.is_unbounded: if r.is_unbounded: # either c or r is negative return else: return c.is_positive elif r.is_unbounded: return r.is_positive if c.is_nonnegative and r.is_positive: return True if r.is_nonnegative and c.is_positive: return True if c.is_nonpositive and r.is_nonpositive: return False def _eval_is_negative(self): c, r = self.as_two_terms() if c.is_negative and r.is_negative: return True if c.is_unbounded: if r.is_unbounded: # either c or r is positive return else: return c.is_negative elif r.is_unbounded: return r.is_negative if c.is_nonpositive and r.is_negative: return True if r.is_nonpositive and c.is_negative: return True if c.is_nonnegative and r.is_nonnegative: return False def _eval_subs(self, old, new): if self == old: return new if isinstance(old, FunctionClass): return self.__class__(*[s._eval_subs(old, new) for s in self.args ]) coeff_self, terms_self = self.as_coeff_add() coeff_old, terms_old = old.as_coeff_add() if terms_self == terms_old: # (2+a).subs(3+a,y) -> 2-3+y return Add(new, coeff_self, -coeff_old) if old.is_Add: if len(terms_old) < len(terms_self): # (a+b+c+d).subs(b+c,x) -> a+x+d self_set = set(terms_self) old_set = set(terms_old) if old_set < self_set: ret_set = self_set - old_set return Add(new, coeff_self, -coeff_old, *[s._eval_subs(old, new) for s in ret_set]) return self.__class__(*[s._eval_subs(old, new) for s in self.args]) def removeO(self): args = [a for a in self.args if not a.is_Order] return self._new_rawargs(*args) def getO(self): args = [a for a in self.args if a.is_Order] if args: return self._new_rawargs(*args) @cacheit def extract_leading_order(self, *symbols): """ Returns the leading term and it's order. Examples: >>> from sympy.abc import x >>> (x+1+1/x**5).extract_leading_order(x) ((x**(-5), O(x**(-5))),) >>> (1+x).extract_leading_order(x) ((1, O(1)),) >>> (x+x**2).extract_leading_order(x) ((x, O(x)),) """ lst = [] seq = [(f, C.Order(f, *symbols)) for f in self.args] for ef,of in seq: for e,o in lst: if o.contains(of) and o != of: of = None break if of is None: continue new_lst = [(ef,of)] for e,o in lst: if of.contains(o) and o != of: continue new_lst.append((e,o)) lst = new_lst return tuple(lst) def as_real_imag(self, deep=True): sargs, terms = self.args, [] re_part, im_part = [], [] for term in sargs: re, im = term.as_real_imag(deep=deep) re_part.append(re) im_part.append(im) return (self.func(*re_part), self.func(*im_part)) def _eval_as_leading_term(self, x): # TODO this does not need to call nseries! coeff, terms = self.collect(x).as_coeff_add(x) has_unbounded = bool([f for f in self.args if f.is_unbounded]) if has_unbounded: if isinstance(terms, Basic): terms = terms.args terms = [f for f in terms if not f.is_bounded] n = 1 s = self.nseries(x, n=n).collect(x) # could be 1/x + 1/(y*x) while s.is_Order: n +=1 s = self.nseries(x, n=n) if s.is_Add: s = s.removeO() if s.is_Add: lst = s.extract_leading_order(x) return Add(*[e for (e,f) in lst]) return s.as_leading_term(x) def _eval_conjugate(self): return Add(*[t.conjugate() for t in self.args]) def _eval_expand_basic(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_basic'): newterm = term._eval_expand_basic(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_exp(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_power_exp'): newterm = term._eval_expand_power_exp(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_base(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_power_base'): newterm = term._eval_expand_power_base(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_mul(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_mul'): newterm = term._eval_expand_mul(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_multinomial(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_multinomial'): newterm = term._eval_expand_multinomial(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_log(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_log'): newterm = term._eval_expand_log(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_complex(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_complex'): newterm = term._eval_expand_complex(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_trig(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_trig'): newterm = term._eval_expand_trig(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_func(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_func'): newterm = term._eval_expand_func(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def __neg__(self): return Add(*[-t for t in self.args]) def _sage_(self): s = 0 for x in self.args: s += x._sage_() return s def primitive(self): """ Divide ``self`` by the GCD of coefficients of ``self``. **Example** >>> from sympy.abc import x, y >>> (2*x + 4*y).primitive() (2, x + 2*y) >>> (2*x/3 + 4*y/9).primitive() (2/9, 3*x + 2*y) >>> (2*x/3 + 4.1*y).primitive() (1, 2*x/3 + 4.1*y) """ terms = [] cont = S.Zero for term in self.args: coeff = term.as_coeff_mul()[0] if coeff.is_Rational: cont = cont.gcd(coeff) if cont is not S.One: terms.append(term) continue return S.One, self for i, term in enumerate(terms): # XXX: this is extremely slow terms[i] = term/cont return cont, self._new_rawargs(*terms) from function import FunctionClass from mul import Mul wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/numbers.py0000644000175000017500000015514412014170666024066 0ustar georgeskgeorgeskfrom core import C from sympify import converter, sympify, _sympify, SympifyError from basic import Basic from singleton import S, Singleton from expr import Expr, AtomicExpr from decorators import _sympifyit, deprecated from cache import cacheit, clear_cache import sympy.mpmath as mpmath import sympy.mpmath.libmp as mlib from sympy.mpmath.libmp import mpf_pow, mpf_pi, mpf_e, phi_fixed from sympy.mpmath.ctx_mp import mpnumeric import decimal rnd = mlib.round_nearest # TODO: we should use the warnings module _errdict = {"divide": False} def seterr(divide=False): """ Should sympy raise an exception on 0/0 or return a nan? divide == True .... raise an exception divide == False ... return nan """ if _errdict["divide"] != divide: clear_cache() _errdict["divide"] = divide # (a,b) -> gcd(a,b) _gcdcache = {} # TODO caching with decorator, but not to degrade performance def igcd(a, b): """Computes positive, integer greatest common divisor of two numbers. The algorithm is based on the well known Euclid's algorithm. To improve speed, igcd() has its own caching mechanism implemented. """ try: return _gcdcache[(a,b)] except KeyError: if a and b: if b < 0: b = -b while b: a, b = b, a % b else: a = abs(a or b) _gcdcache[(a,b)] = a return a def ilcm(a, b): """Computes integer least common multiple of two numbers. """ if a == 0 and b == 0: return 0 else: return a * b // igcd(a, b) def igcdex(a, b): """Returns x, y, g such that g = x*a + y*b = gcd(a, b). >>> from sympy.core.numbers import igcdex >>> igcdex(2, 3) (-1, 1, 1) >>> igcdex(10, 12) (-1, 1, 2) >>> x, y, g = igcdex(100, 2004) >>> x, y, g (-20, 1, 4) >>> x*100 + y*2004 4 """ if (not a) and (not b): return (0, 1, 0) if not a: return (0, b//abs(b), abs(b)) if not b: return (a//abs(a), 0, abs(a)) if a < 0: a, x_sign = -a, -1 else: x_sign = 1 if b < 0: b, y_sign = -b, -1 else: y_sign = 1 x, y, r, s = 1, 0, 0, 1 while b: (c, q) = (a % b, a // b) (a, b, r, s, x, y) = (b, c, x-q*r, y-q*s, r, s) return (x*x_sign, y*y_sign, a) class Number(AtomicExpr): """ Represents any kind of number in sympy. Floating point numbers are represented by the Float class. Integer numbers (of any size), together with rational numbers (again, there is no limit on their size) are represented by the Rational class. If you want to represent, for example, ``1+sqrt(2)``, then you need to do:: Rational(1) + sqrt(Rational(2)) """ is_commutative = True is_comparable = True is_bounded = True is_finite = True is_number = True __slots__ = [] # Used to make max(x._prec, y._prec) return x._prec when only x is a float _prec = -1 is_Number = True def __new__(cls, *obj): if len(obj)==1: obj=obj[0] if isinstance(obj, (int, long)): return Integer(obj) if isinstance(obj, tuple) and len(obj) == 2: return Rational(*obj) if isinstance(obj, (float, mpmath.mpf, decimal.Decimal)): return Float(obj) if isinstance(obj, str): val = sympify(obj) if isinstance(val, Number): return val else: raise ValueError('String "%s" does not denote a Number'%obj) if isinstance(obj, Number): return obj raise TypeError("expected str|int|long|float|Decimal|Number object but got %r" % (obj)) def _as_mpf_val(self, prec): """Evaluation of mpf tuple accurate to at least prec bits.""" raise NotImplementedError('%s needs ._as_mpf_val() method' % \ (self.__class__.__name__)) def _eval_evalf(self, prec): return Float._new(self._as_mpf_val(prec), prec) def _as_mpf_op(self, prec): prec = max(prec, self._prec) return self._as_mpf_val(prec), prec def __float__(self): return mlib.to_float(self._as_mpf_val(53)) def _eval_conjugate(self): return self def _eval_order(self, *symbols): # Order(5, x, y) -> Order(1,x,y) return C.Order(S.One, *symbols) @classmethod def class_key(cls): return 1, 0, 'Number' def sort_key(self, order=None): return self.class_key(), (0, ()), (), self def __eq__(self, other): raise NotImplementedError('%s needs .__eq__() method' % (self.__class__.__name__)) def __ne__(self, other): raise NotImplementedError('%s needs .__ne__() method' % (self.__class__.__name__)) def __lt__(self, other): raise NotImplementedError('%s needs .__lt__() method' % (self.__class__.__name__)) def __le__(self, other): raise NotImplementedError('%s needs .__le__() method' % (self.__class__.__name__)) def __gt__(self, other): return _sympify(other).__lt__(self) def __ge__(self, other): return _sympify(other).__le__(self) def __hash__(self): return super(Number, self).__hash__() @property def is_number(self): return True def as_coeff_mul(self, *deps): # a -> c * t if self.is_Rational: return self, tuple() elif self.is_negative: return S.NegativeOne, (-self,) return S.One, (self,) def as_coeff_add(self, *deps): # a -> c + t if self.is_Rational: return self, tuple() return S.Zero, (self,) def gcd(self, other): """Compute greatest common divisor of input arguments. """ _ = _sympify(other) return S.One def lcm(self, other): """Compute least common multiple of input arguments. """ other = _sympify(other) return self*other def cofactors(self, other): """Compute GCD and cofactors of input arguments. """ other = _sympify(other) return S.One, self, other def as_coeff_Mul(self): """Efficiently extract the coefficient of a product. """ return self, S.One class Float(Number): """ Represents a floating point number. It is capable of representing arbitrary-precision floating-point numbers **Usage** :: Float(3.5) 3.5 # (the 3.5 was converted from a python float) Float("3.0000000000000005") >>> from sympy import Float >>> Float((1,3,0,2)) # mpmath tuple: (-1)**1 * 3 * 2**0; 3 has 2 bits -3.00000000000000 **Notes** - Float(x) with x being a Python int/long will return Integer(x) """ is_real = True is_irrational = False is_integer = False __slots__ = ['_mpf_', '_prec'] # mpz can't be pickled def __getnewargs__(self): return (mlib.to_pickable(self._mpf_),) def __getstate__(self): d = Expr.__getstate__(self).copy() del d["_mpf_"] return mlib.to_pickable(self._mpf_), d def __setstate__(self, state): _mpf_, d = state _mpf_ = mlib.from_pickable(_mpf_) self._mpf_ = _mpf_ Expr.__setstate__(self, d) is_Float = True def floor(self): return C.Integer(int(mlib.to_int(mlib.mpf_floor(self._mpf_, self._prec)))) def ceiling(self): return C.Integer(int(mlib.to_int(mlib.mpf_ceil(self._mpf_, self._prec)))) @property def num(self): return mpmath.mpf(self._mpf_) def _as_mpf_val(self, prec): return self._mpf_ def _as_mpf_op(self, prec): return self._mpf_, max(prec, self._prec) def __new__(cls, num, prec=15): prec = mlib.libmpf.dps_to_prec(prec) if isinstance(num, (int, long)): return Integer(num) if isinstance(num, (str, decimal.Decimal)): _mpf_ = mlib.from_str(str(num), prec, rnd) elif isinstance(num, tuple) and len(num) == 4: if type(num[1]) is str: # it's a hexadecimal (coming from a pickled object) # assume that it is in standard form num = list(num) num[1] = long(num[1], 16) _mpf_ = tuple(num) else: _mpf_ = mpmath.mpf( S.NegativeOne ** num[0] * num[1] * 2 ** num[2])._mpf_ else: _mpf_ = mpmath.mpf(num)._mpf_ if not num: return C.Zero() obj = Expr.__new__(cls) obj._mpf_ = _mpf_ obj._prec = prec return obj @classmethod def _new(cls, _mpf_, _prec): if _mpf_ == mlib.fzero: return S.Zero obj = Expr.__new__(cls) obj._mpf_ = _mpf_ obj._prec = _prec return obj def _hashable_content(self): return (self._mpf_, self._prec) def _eval_is_positive(self): return self.num > 0 def _eval_is_negative(self): return self.num < 0 def __neg__(self): return Float._new(mlib.mpf_neg(self._mpf_), self._prec) @_sympifyit('other', NotImplemented) def __mul__(self, other): if isinstance(other, Number): rhs, prec = other._as_mpf_op(self._prec) return Float._new(mlib.mpf_mul(self._mpf_, rhs, prec, rnd), prec) return Number.__mul__(self, other) @_sympifyit('other', NotImplemented) def __mod__(self, other): if isinstance(other, Number): rhs, prec = other._as_mpf_op(self._prec) return Float._new(mlib.mpf_mod(self._mpf_, rhs, prec, rnd), prec) return Number.__mod__(self, other) @_sympifyit('other', NotImplemented) def __rmod__(self, other): if isinstance(other, Number): rhs, prec = other._as_mpf_op(self._prec) return Float._new(mlib.mpf_mod(rhs, self._mpf_, prec, rnd), prec) return Number.__rmod__(self, other) @_sympifyit('other', NotImplemented) def __add__(self, other): if (other is S.NaN) or (self is NaN): return S.NaN if isinstance(other, Number): rhs, prec = other._as_mpf_op(self._prec) return Float._new(mlib.mpf_add(self._mpf_, rhs, prec, rnd), prec) return Number.__add__(self, other) def _eval_power(self, e): """ e is symbolic object but not equal to 0, 1 (-p) ** r -> exp(r * log(-p)) -> exp(r * (log(p) + I*Pi)) -> -> p ** r * (sin(Pi*r) + cos(Pi*r) * I) """ if isinstance(e, Number): if isinstance(e, Integer): prec = self._prec return Float._new(mlib.mpf_pow_int(self._mpf_, e.p, prec, rnd), prec) e, prec = e._as_mpf_op(self._prec) b = self._mpf_ try: y = mpf_pow(b, e, prec, rnd) return Float._new(y, prec) except mlib.ComplexResult: re, im = mlib.mpc_pow((b, mlib.fzero), (e, mlib.fzero), prec, rnd) return Float._new(re, prec) + Float._new(im, prec) * S.ImaginaryUnit def __abs__(self): return Float._new(mlib.mpf_abs(self._mpf_), self._prec) def __int__(self): return int(mlib.to_int(self._mpf_)) def __eq__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy != other --> not == if isinstance(other, NumberSymbol): if other.is_irrational: return False return other.__eq__(self) if isinstance(other, FunctionClass): #cos as opposed to cos(x) return False if isinstance(other, Number): return bool(mlib.mpf_eq(self._mpf_, other._as_mpf_val(self._prec))) return False # Float != non-Number def __ne__(self, other): try: other = _sympify(other) except SympifyError: return True # sympy != other if isinstance(other, NumberSymbol): if other.is_irrational: return True return other.__ne__(self) if isinstance(other, FunctionClass): #cos as opposed to cos(x) return True if isinstance(other, Number): return bool(not mlib.mpf_eq(self._mpf_, other._as_mpf_val(self._prec))) return True # Float != non-Number def __lt__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy > other if isinstance(other, NumberSymbol): return other.__ge__(self) if other.is_comparable: other = other.evalf() if isinstance(other, Number): return bool(mlib.mpf_lt(self._mpf_, other._as_mpf_val(self._prec))) return Expr.__lt__(self, other) def __le__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy > other --> ! <= if isinstance(other, NumberSymbol): return other.__gt__(self) if other.is_comparable: other = other.evalf() if isinstance(other, Number): return bool(mlib.mpf_le(self._mpf_, other._as_mpf_val(self._prec))) return Expr.__le__(self, other) def __hash__(self): return super(Float, self).__hash__() def epsilon_eq(self, other, epsilon="10e-16"): return abs(self - other) < Float(epsilon) def _sage_(self): import sage.all as sage return sage.RealNumber(str(self)) # Add sympify converters converter[float] = converter[decimal.Decimal] = Float # this is here to work nicely in Sage RealNumber = Float @deprecated def Real(*args, **kwargs): # pragma: no cover """Deprecated alias for the Float constructor.""" return Float(*args, **kwargs) class Rational(Number): """Represents integers and rational numbers (p/q) of any size. **Examples** >>> from sympy import Rational >>> from sympy.abc import x, y >>> Rational(3) 3 >>> Rational(1,2) 1/2 >>> Rational(1.5) 1 Rational can also accept strings that are valid literals for reals: >>> Rational("1.23") 123/100 >>> Rational('1e-2') 1/100 >>> Rational(".1") 1/10 Parsing needs for any other type of string for which a Rational is desired can be handled with the rational=True option in sympify() which produces rationals from strings like '.[3]' (=1/3) and '3/10' (=3/10). **Low-level** Access numerator and denominator as .p and .q: >>> r = Rational(3,4) >>> r 3/4 >>> r.p 3 >>> r.q 4 Note that p and q return integers (not sympy Integers) so some care is needed when using them in expressions: >>> r.p/r.q 0 """ is_real = True is_integer = False is_rational = True __slots__ = ['p', 'q'] is_Rational = True @cacheit def __new__(cls, p, q=None): if q is None: if isinstance(p, Rational): return p if isinstance(p, basestring): try: # we might have a Float neg_pow, digits, expt = decimal.Decimal(p).as_tuple() p = [1, -1][neg_pow] * int("".join(str(x) for x in digits)) if expt > 0: # TODO: this branch needs a test return Rational(p*Pow(10, expt), 1) return Rational(p, Pow(10, -expt)) except decimal.InvalidOperation: import re f = re.match('^([-+]?[0-9]+)/([0-9]+)$', p.replace(' ','')) if f: n, d = f.groups() return Rational(int(n), int(d)) raise ValueError('invalid literal: %s' % p) elif not isinstance(p, Basic): return Rational(S(p)) q = S.One if isinstance(q, Rational): p *= q.q q = q.p if isinstance(p, Rational): q *= p.q p = p.p p = int(p) q = int(q) if q == 0: if p == 0: if _errdict["divide"]: raise ValueError("Indeterminate 0/0") else: return S.NaN if p < 0: return S.NegativeInfinity return S.Infinity if q < 0: q = -q p = -p n = igcd(abs(p), q) if n > 1: p //= n q //= n if q == 1: return Integer(p) if p == 1 and q == 2: return S.Half obj = Expr.__new__(cls) obj.p = p obj.q = q #obj._args = (p, q) return obj def limit_denominator(self, max_denominator=1000000): """Closest Rational to self with denominator at most max_denominator. >>> from sympy import Rational >>> Rational('3.141592653589793').limit_denominator(10) 22/7 >>> Rational('3.141592653589793').limit_denominator(100) 311/99 """ # Algorithm notes: For any real number x, define a *best upper # approximation* to x to be a rational number p/q such that: # # (1) p/q >= x, and # (2) if p/q > r/s >= x then s > q, for any rational r/s. # # Define *best lower approximation* similarly. Then it can be # proved that a rational number is a best upper or lower # approximation to x if, and only if, it is a convergent or # semiconvergent of the (unique shortest) continued fraction # associated to x. # # To find a best rational approximation with denominator <= M, # we find the best upper and lower approximations with # denominator <= M and take whichever of these is closer to x. # In the event of a tie, the bound with smaller denominator is # chosen. If both denominators are equal (which can happen # only when max_denominator == 1 and self is midway between # two integers) the lower bound---i.e., the floor of self, is # taken. if max_denominator < 1: raise ValueError("max_denominator should be at least 1") if self.q <= max_denominator: return self p0, q0, p1, q1 = 0, 1, 1, 0 n, d = self.p, self.q while True: a = n//d q2 = q0+a*q1 if q2 > max_denominator: break p0, q0, p1, q1 = p1, q1, p0+a*p1, q2 n, d = d, n-a*d k = (max_denominator-q0)//q1 bound1 = Rational(p0+k*p1, q0+k*q1) bound2 = Rational(p1, q1) if abs(bound2 - self) <= abs(bound1-self): return bound2 else: return bound1 def __getnewargs__(self): return (self.p, self.q) def _hashable_content(self): return (self.p, self.q) def _eval_is_positive(self): return self.p > 0 def _eval_is_zero(self): return self.p == 0 def __neg__(self): return Rational(-self.p, self.q) @_sympifyit('other', NotImplemented) def __mul__(self, other): if (other is S.NaN) or (self is S.NaN): return S.NaN if isinstance(other, Float): return other * self if isinstance(other, Rational): return Rational(self.p * other.p, self.q * other.q) return Number.__mul__(self, other) @_sympifyit('other', NotImplemented) def __mod__(self, other): if isinstance(other, Rational): n = (self.p*other.q) // (other.p*self.q) return Rational(self.p*other.q - n*other.p*self.q, self.q*other.q) if isinstance(other, Float): return self.evalf() % other return Number.__mod__(self, other) @_sympifyit('other', NotImplemented) def __rmod__(self, other): if isinstance(other, Rational): return Rational.__mod__(other, self) if isinstance(other, Float): return other % self.evalf() return Number.__rmod__(self, other) # TODO reorder @_sympifyit('other', NotImplemented) def __add__(self, other): if (other is S.NaN) or (self is S.NaN): return S.NaN if isinstance(other, Float): return other + self if isinstance(other, Rational): if self.is_unbounded: if other.is_bounded: return self elif self==other: return self else: if other.is_unbounded: return other return Rational(self.p * other.q + self.q * other.p, self.q * other.q) return Number.__add__(self, other) def _eval_power(b, e): if (e is S.NaN): return S.NaN if isinstance(e, Number): if isinstance(e, Float): return b._eval_evalf(e._prec) ** e if e.is_negative: # (3/4)**-2 -> (4/3)**2 ne = -e if (ne is S.One): return Rational(b.q, b.p) if b < 0: if e.q != 1: return -(S.NegativeOne) ** ((e.p % e.q) / S(e.q)) * Rational(b.q, -b.p) ** ne else: return S.NegativeOne ** ne * Rational(b.q, -b.p) ** ne else: return Rational(b.q, b.p) ** ne if (e is S.Infinity): if b.p > b.q: # (3/2)**oo -> oo return S.Infinity if b.p < -b.q: # (-3/2)**oo -> oo + I*oo return S.Infinity + S.Infinity * S.ImaginaryUnit return S.Zero if isinstance(e, Integer): # (4/3)**2 -> 4**2 / 3**2 return Rational(b.p ** e.p, b.q ** e.p) if isinstance(e, Rational): if b.p != 1: # (4/3)**(5/6) -> 4**(5/6) * 3**(-5/6) return Integer(b.p) ** e * Integer(b.q) ** (-e) if b >= 0: return Integer(b.q)**Rational(e.p * (e.q-1), e.q) / ( Integer(b.q) ** Integer(e.p)) else: return (-1)**e * (-b)**e c, t = b.as_coeff_mul() if e.is_even and isinstance(c, Number) and c < 0: return (-c * Mul(*t)) ** e return def _as_mpf_val(self, prec): return mlib.from_rational(self.p, self.q, prec, rnd) def _mpmath_(self, prec, rnd): return mpmath.make_mpf(mlib.from_rational(self.p, self.q, prec, rnd)) def __abs__(self): return Rational(abs(self.p), self.q) def __int__(self): return int(float(self.p)/self.q) def __eq__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy != other --> not == if isinstance(other, NumberSymbol): if other.is_irrational: return False return other.__eq__(self) if isinstance(other, FunctionClass): #cos as opposed to cos(x) return False if other.is_comparable and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): return bool(mlib.mpf_eq(self._as_mpf_val(other._prec), other._mpf_)) return bool(self.p==other.p and self.q==other.q) return False # Rational != non-Number def __ne__(self, other): try: other = _sympify(other) except SympifyError: return True # sympy != other if isinstance(other, NumberSymbol): if other.is_irrational: return True return other.__ne__(self) if isinstance(other, FunctionClass): #cos as opposed to cos(x) return True if other.is_comparable and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): return bool(not mlib.mpf_eq(self._as_mpf_val(other._prec), other._mpf_)) return bool(self.p!=other.p or self.q!=other.q) return True # Rational != non-Number def __lt__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy > other --> not < if isinstance(other, NumberSymbol): return other.__ge__(self) if other.is_comparable and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): return bool(mlib.mpf_lt(self._as_mpf_val(other._prec), other._mpf_)) return bool(self.p * other.q < self.q * other.p) return Expr.__lt__(self, other) def __le__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy > other --> not <= if isinstance(other, NumberSymbol): return other.__gt__(self) if other.is_comparable and not isinstance(other, Rational): other = other.evalf() if isinstance(other, Number): if isinstance(other, Float): return bool(mlib.mpf_le(self._as_mpf_val(other._prec), other._mpf_)) return bool(self.p * other.q <= self.q * other.p) return Expr.__le__(self, other) def __hash__(self): return super(Rational, self).__hash__() def factors(self, limit=None, use_trial=True, use_rho=False, use_pm1=False, verbose=False): """A wrapper to factorint which return factors of self that are smaller than limit (or cheap to compute). Special methods of factoring are disabled by default so that only trial division is used. """ from sympy.ntheory import factorint f = factorint(self.p, limit=limit, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose).copy() for p, e in factorint(self.q, limit=limit, use_trial=use_trial, use_rho=use_rho, use_pm1=use_pm1, verbose=verbose).items(): try: f[p] += -e except KeyError: f[p] = -e if len(f)>1 and 1 in f: del f[1] return f def gcd(self, other): """Compute greatest common divisor of input arguments. """ if type(other) in (int, long): p = igcd(self.p, other) if self.is_Integer: return Integer(p) else: return Rational(p, self.q) else: other = _sympify(other) if other.is_Rational: p = igcd(self.p, other.p) if other.is_Integer: if self.is_Integer: return Integer(p) else: return Rational(p, self.q) else: if self.is_Integer: return Rational(p, other.q) else: return Rational(p, ilcm(self.q, other.q)) elif other.is_Number: return S.One else: raise TypeError("expected an integer or rational, got %s" % other) def lcm(self, other): """Compute least common multiple of input arguments. """ if type(other) in (int, long): return Integer(ilcm(self.p, other)) else: other = _sympify(other) if other.is_Rational: p = ilcm(self.p, other.p) if self.is_Integer or other.is_Integer: return Integer(p) else: return Rational(p, igcd(self.q, other.q)) elif other.is_Number: return self*other else: raise TypeError("expected an integer or rational, got %s" % other) def cofactors(self, other): """Compute GCD and cofactors of input arguments. """ other = _sympify(other) gcd = self.gcd(other) if gcd is S.One: return gcd, self, other else: return gcd, self/gcd, other/gcd def as_numer_denom(self): return Integer(self.p), Integer(self.q) def _sage_(self): import sage.all as sage return sage.Integer(self.p)/sage.Integer(self.q) # int -> Integer _intcache = {} # TODO move this tracing facility to sympy/core/trace.py ? def _intcache_printinfo(): ints = sorted(_intcache.keys()) nhit = _intcache_hits nmiss= _intcache_misses if nhit == 0 and nmiss == 0: print print 'Integer cache statistic was not collected' return miss_ratio = float(nmiss) / (nhit+nmiss) print print 'Integer cache statistic' print '-----------------------' print print '#items: %i' % len(ints) print print ' #hit #miss #total' print print '%5i %5i (%7.5f %%) %5i' % (nhit, nmiss, miss_ratio*100, nhit+nmiss) print print ints _intcache_hits = 0 _intcache_misses = 0 def int_trace(f): import os if os.getenv('SYMPY_TRACE_INT', 'no').lower() != 'yes': return f def Integer_tracer(cls, i): global _intcache_hits, _intcache_misses try: _intcache_hits += 1 return _intcache[i] except KeyError: _intcache_hits -= 1 _intcache_misses += 1 return f(cls, i) # also we want to hook our _intcache_printinfo into sys.atexit import atexit atexit.register(_intcache_printinfo) return Integer_tracer class Integer(Rational): q = 1 is_integer = True is_Integer = True __slots__ = ['p'] def _as_mpf_val(self, prec): return mlib.from_int(self.p) def _mpmath_(self, prec, rnd): return mpmath.make_mpf(self._as_mpf_val(prec)) # TODO caching with decorator, but not to degrade performance @int_trace def __new__(cls, i): ival = int(i) try: return _intcache[ival] except KeyError: # We only work with well-behaved integer types. This converts, for # example, numpy.int32 instances. if ival == 0: obj = S.Zero elif ival == 1: obj = S.One elif ival == -1: obj = S.NegativeOne else: obj = Expr.__new__(cls) obj.p = ival _intcache[ival] = obj return obj def __getnewargs__(self): return (self.p,) # Arithmetic operations are here for efficiency def __int__(self): return self.p def __neg__(self): return Integer(-self.p) def __abs__(self): if self.p >= 0: return self else: return Integer(-self.p) def __divmod__(self, other): return divmod(self.p, other.p) # TODO make it decorator + bytecodehacks? def __add__(a, b): if isinstance(b, (int, long)): return Integer(a.p + b) elif isinstance(b, Integer): return Integer(a.p + b.p) return Rational.__add__(a, b) # a,b -not- b,a def __radd__(a, b): if isinstance(b, (int, long)): return Integer(b + a.p) elif isinstance(b, Integer): return Integer(b.p + a.p) return Rational.__add__(a, b) def __sub__(a, b): if isinstance(b, (int, long)): return Integer(a.p - b) elif isinstance(b, Integer): return Integer(a.p - b.p) return Rational.__sub__(a, b) def __rsub__(a, b): if isinstance(b, (int, long)): return Integer(b - a.p) elif isinstance(b, Integer): return Integer(b.p - a.p) return Rational.__rsub__(a, b) def __mul__(a, b): if isinstance(b, (int, long)): return Integer(a.p * b) elif isinstance(b, Integer): return Integer(a.p * b.p) return Rational.__mul__(a, b) def __rmul__(a, b): if isinstance(b, (int, long)): return Integer(b * a.p) elif isinstance(b, Integer): return Integer(b.p * a.p) return Rational.__mul__(a, b) def __mod__(a, b): if isinstance(b, (int, long)): return Integer(a.p % b) elif isinstance(b, Integer): return Integer(a.p % b.p) return Rational.__mod__(a, b) def __rmod__(a, b): if isinstance(b, (int, long)): return Integer(b % a.p) elif isinstance(b, Integer): return Integer(b.p % a.p) return Rational.__rmod__(a, b) def __eq__(a, b): if isinstance(b, (int, long)): return (a.p == b) elif isinstance(b, Integer): return (a.p == b.p) return Rational.__eq__(a, b) def __ne__(a, b): if isinstance(b, (int, long)): return (a.p != b) elif isinstance(b, Integer): return (a.p != b.p) return Rational.__ne__(a, b) def __gt__(a, b): if isinstance(b, (int, long)): return (a.p > b) elif isinstance(b, Integer): return (a.p > b.p) return Rational.__gt__(a, b) def __lt__(a, b): if isinstance(b, (int, long)): return (a.p < b) elif isinstance(b, Integer): return (a.p < b.p) return Rational.__lt__(a, b) def __ge__(a, b): if isinstance(b, (int, long)): return (a.p >= b) elif isinstance(b, Integer): return (a.p >= b.p) return Rational.__ge__(a, b) def __le__(a, b): if isinstance(b, (int, long)): return (a.p <= b) elif isinstance(b, Integer): return (a.p <= b.p) return Rational.__le__(a, b) def __hash__(self): return super(Integer, self).__hash__() def __index__(self): return self.p ######################################## def _eval_is_odd(self): return bool(self.p % 2) def _eval_power(b, e): """ Tries to do some simplifications on b ** e, where b is an instance of Integer Returns None if no further simplifications can be done When exponent is a fraction (so we have for example a square root), we try to find a simpler representation by factoring the argument up to factors of 2**15, e.g. - 4**Rational(1,2) becomes 2 - (-4)**Rational(1,2) becomes 2*I - (2**(3+7)*3**(6+7))**Rational(1,7) becomes 6*18**(3/7) Further simplification would require a special call to factorint on the argument which is not done here for sake of speed. """ from sympy import perfect_power if e is S.NaN: return S.NaN if b is S.One: return S.One if b is S.NegativeOne: return if e is S.Infinity: if b > S.One: return S.Infinity if b is S.NegativeOne: return S.NaN # cases for 0 and 1 are done in their respective classes return S.Infinity + S.ImaginaryUnit * S.Infinity if not isinstance(e, Number): # simplify when exp is even # (-2) ** k --> 2 ** k c, t = b.as_coeff_mul() if e.is_even and isinstance(c, Number) and c < 0: return (-c*Mul(*t))**e if not isinstance(e, Rational): return if e is S.Half and b < 0: # we extract I for this special case since everyone is doing so return S.ImaginaryUnit*Pow(-b, e) if e < 0: # invert base and change sign on exponent ne = -e if b < 0: if e.q != 1: return -(S.NegativeOne)**((e.p % e.q) / S(e.q)) * Rational(1, -b)**ne else: return (S.NegativeOne)**ne*Rational(1, -b)**ne else: return Rational(1, b)**ne # see if base is a perfect root, sqrt(4) --> 2 b_pos = int(abs(b)) x, xexact = integer_nthroot(b_pos, e.q) if xexact: # if it's a perfect root we've finished result = Integer(x ** abs(e.p)) if b < 0: result *= (-1)**e return result # The following is an algorithm where we collect perfect roots # from the factors of base. # if it's not an nth root, it still might be a perfect power p = perfect_power(b_pos) if p is not False: dict = {p[0]: p[1]} else: dict = Integer(b_pos).factors(limit=2**15) # now process the dict of factors if b.is_negative: dict[-1] = 1 out_int = 1 # integer part out_rad = 1 # extracted radicals sqr_int = 1 sqr_gcd = 0 sqr_dict = {} for prime, exponent in dict.items(): exponent *= e.p # remove multiples of e.q, e.g. (2**12)**(1/10) -> 2*(2**2)**(1/10) div_e, div_m = divmod(exponent, e.q) if div_e > 0: out_int *= prime**div_e if div_m > 0: # see if the reduced exponent shares a gcd with e.q # (2**2)**(1/10) -> 2**(1/5) g = igcd(div_m, e.q) if g != 1: out_rad *= Pow(prime, Rational(div_m//g, e.q//g)) else: sqr_dict[prime] = div_m # identify gcd of remaining powers for p, ex in sqr_dict.iteritems(): if sqr_gcd == 0: sqr_gcd = ex else: sqr_gcd = igcd(sqr_gcd, ex) if sqr_gcd == 1: break for k, v in sqr_dict.iteritems(): sqr_int *= k**(v//sqr_gcd) if sqr_int == b and out_int == 1 and out_rad == 1: result = None else: result = out_int*out_rad*Pow(sqr_int, Rational(sqr_gcd, e.q)) return result def _eval_is_prime(self): if self.p < 0: return False def as_numer_denom(self): return self, S.One def __floordiv__(self, other): return Integer(self.p // Integer(other).p) def __rfloordiv__(self, other): return Integer(Integer(other).p // self.p) def factorial(a): """Compute factorial of `a`. """ from sympy.functions.combinatorial.factorials import factorial return Integer(factorial(int(a))) def isqrt(a): """Compute integer square root of `a`. """ return Integer(mlib.isqrt(int(a))) def half_gcdex(a, b): """Half Extended Euclidean Algorithm. """ s, _, h = a.gcdex(b) return s, h def gcdex(a, b): """Extended Euclidean Algorithm. """ if isinstance(b, (int, long)): return tuple(map(Integer, igcdex(int(a), b))) else: b = _sympify(b) if b.is_Integer: return tuple(map(Integer, igcdex(int(a), int(b)))) else: raise ValueError("expected an integer, got %s" % b) def invert(a, b): """Invert `a` modulo `b`, if possible. """ if isinstance(b, (int, long)): a = int(a) else: b = _sympify(b) if b.is_Integer: a, b = int(a), int(b) else: raise ValueError("expected an integer, got %s" % b) s, _, h = igcdex(a, b) if h == 1: return Integer(s % b) else: raise ZeroDivisionError("zero divisor") # Add sympify converters converter[int] = converter[long] = Integer class RationalConstant(Rational): """ Abstract base class for rationals with specific behaviors Derived classes must define class attributes p and q and should probably all be singletons. """ __slots__ = [] def __new__(cls): return AtomicExpr.__new__(cls) class IntegerConstant(Integer): __slots__ = [] def __new__(cls): return AtomicExpr.__new__(cls) class Zero(IntegerConstant): __metaclass__ = Singleton p = 0 q = 1 is_positive = False is_negative = False is_finite = False is_zero = True is_prime = False is_composite = False __slots__ = [] @staticmethod def __abs__(): return S.Zero @staticmethod def __neg__(): return S.Zero def _eval_power(b, e): if e.is_negative: return S.Infinity if e.is_positive: return b d = e.evalf() if isinstance(d, Number): if d.is_negative: return S.Infinity return b coeff, terms = e.as_coeff_mul() if coeff.is_negative: return S.Infinity ** Mul(*terms) if coeff is not S.One: return b ** Mul(*terms) def _eval_order(self, *symbols): # Order(0,x) -> 0 return self def __nonzero__(self): return False class One(IntegerConstant): __metaclass__ = Singleton p = 1 q = 1 is_prime = True __slots__ = [] def _eval_evalf(self, prec): return self @staticmethod def __abs__(): return S.One @staticmethod def __neg__(): return S.NegativeOne def _eval_order(self, *symbols): return @staticmethod def factors(): return {1: 1} class NegativeOne(IntegerConstant): __metaclass__ = Singleton p = -1 q = 1 __slots__ = [] def _eval_evalf(self, prec): return self @staticmethod def __abs__(): return S.One @staticmethod def __neg__(): return S.One def _eval_power(b, e): if e.is_odd: return S.NegativeOne if e.is_even: return S.One if isinstance(e, Number): if isinstance(e, Float): return Float(-1.0) ** e if e is S.NaN: return S.NaN if e is S.Infinity or e is S.NegativeInfinity: return S.NaN if e is S.Half: return S.ImaginaryUnit if isinstance(e, Rational): if e.q == 2: return S.ImaginaryUnit ** Integer(e.p) q = Float(e).floor() if q: q = Integer(q) return b ** q * b ** (e - q) return class Half(RationalConstant): __metaclass__ = Singleton p = 1 q = 2 __slots__ = [] @staticmethod def __abs__(): return S.Half class Infinity(RationalConstant): __metaclass__ = Singleton p = 1 q = 0 __slots__ = [] is_commutative = True is_positive = True is_bounded = False is_finite = False is_infinitesimal = False is_integer = None is_rational = None is_odd = None @staticmethod def __abs__(): return S.Infinity @staticmethod def __neg__(): return S.NegativeInfinity def _eval_power(b, e): """ e is symbolic object but not equal to 0, 1 oo ** nan -> nan oo ** (-p) -> 0, p is number, oo """ if e.is_positive: return S.Infinity if e.is_negative: return S.Zero if isinstance(e, Number): if e is S.NaN: return S.NaN d = e.evalf() if isinstance(d, Number): return b ** d return def _as_mpf_val(self, prec): return mlib.finf def _sage_(self): import sage.all as sage return sage.oo def __gt__(a, b): if b is S.Infinity: return False return True def __lt__(a, b): return False def __ge__(a, b): return True def __le__(a, b): if b is S.Infinity: return True return False def __mod__(self, other): return S.NaN __rmod__ = __mod__ oo = S.Infinity class NegativeInfinity(RationalConstant): __metaclass__ = Singleton p = -1 q = 0 __slots__ = [] is_commutative = True is_real = True is_positive = False is_bounded = False is_finite = False is_infinitesimal = False is_integer = None is_rational = None @staticmethod def __abs__(): return S.Infinity @staticmethod def __neg__(): return S.Infinity def _eval_power(b, e): """ e is symbolic object but not equal to 0, 1 (-oo) ** nan -> nan (-oo) ** oo -> nan (-oo) ** (-oo) -> nan (-oo) ** e -> oo, e is positive even integer (-oo) ** o -> -oo, o is positive odd integer """ if isinstance(e, Number): if (e is S.NaN) or (e is S.Infinity) or (e is S.NegativeInfinity): return S.NaN if isinstance(e, Integer): if e.is_positive: if e.is_odd: return S.NegativeInfinity return S.Infinity return S.NegativeOne**e * S.Infinity ** e return def _as_mpf_val(self, prec): return mlib.fninf def _sage_(self): import sage.all as sage return -(sage.oo) def __gt__(a, b): return False def __lt__(a, b): if b is S.NegativeInfinity: return False return True def __ge__(a, b): if b is S.NegativeInfinity: return True return False def __le__(a, b): return True class NaN(RationalConstant): __metaclass__ = Singleton p = 0 q = 0 is_commutative = True is_real = None is_rational = None is_integer = None is_comparable = False is_finite = None is_bounded = None #is_unbounded = False is_zero = None is_prime = None is_positive = None __slots__ = [] def _as_mpf_val(self, prec): return mlib.fnan def _eval_power(b, e): if e is S.Zero: return S.One return b def _sage_(self): import sage.all as sage return sage.NaN nan = S.NaN class ComplexInfinity(AtomicExpr): __metaclass__ = Singleton is_commutative = True is_comparable = None is_bounded = False is_real = None is_number = True __slots__ = [] def __new__(cls): return AtomicExpr.__new__(cls) @staticmethod def __abs__(): return S.Infinity @staticmethod def __neg__(): return S.ComplexInfinity def _eval_power(b, e): if e is S.ComplexInfinity: return S.NaN if isinstance(e, Number): if e is S.Zero: return S.NaN else: if e.is_positive: return S.ComplexInfinity else: return S.Zero zoo = S.ComplexInfinity class NumberSymbol(AtomicExpr): __metaclass__ = Singleton is_commutative = True is_comparable = True is_bounded = True is_finite = True is_number = True __slots__ = [] is_NumberSymbol = True def __new__(cls): return AtomicExpr.__new__(cls) def approximation(self, number_cls): """ Return an interval with number_cls endpoints that contains the value of NumberSymbol. If not implemented, then return None. """ def _eval_evalf(self, prec): return Float._new(self._as_mpf_val(prec), prec) def __eq__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy != other --> not == if self is other: return True if isinstance(other, Number) and self.is_irrational: return False return False # NumberSymbol != non-(Number|self) def __ne__(self, other): try: other = _sympify(other) except SympifyError: return True # sympy != other if self is other: return False if isinstance(other, Number) and self.is_irrational: return True return True # NumberSymbol != non(Number|self) def __lt__(self, other): try: other = _sympify(other) except SympifyError: return False # sympy > other --> not < if self is other: return False if isinstance(other, Number): approx = self.approximation_interval(other.__class__) if approx is not None: l,u = approx if other < l: return False if other > u: return True return self.evalf() other --> not <= if self is other: return True if other.is_comparable: other = other.evalf() if isinstance(other, Number): return self.evalf()<=other return Expr.__le__(self, other) def __gt__(self, other): return (-self) < (-other) def __ge__(self, other): return (-self) <= (-other) def __int__(self): return int(self.evalf(0)) def __hash__(self): return super(NumberSymbol, self).__hash__() class Exp1(NumberSymbol): __metaclass__ = Singleton is_real = True is_positive = True is_negative = False # XXX Forces is_negative/is_nonnegative is_irrational = True __slots__ = [] @staticmethod def __abs__(): return S.Exp1 def _as_mpf_val(self, prec): return mpf_e(prec) def approximation_interval(self, number_cls): if issubclass(number_cls,Integer): return (Integer(2),Integer(3)) elif issubclass(number_cls,Rational): pass def _eval_power(self, exp): return C.exp(exp) def _sage_(self): import sage.all as sage return sage.e E = S.Exp1 class Pi(NumberSymbol): __metaclass__ = Singleton is_real = True is_positive = True is_negative = False is_irrational = True __slots__ = [] @staticmethod def __abs__(): return S.Pi def _as_mpf_val(self, prec): return mpf_pi(prec) def approximation_interval(self, number_cls): if issubclass(number_cls, Integer): return (Integer(3), Integer(4)) elif issubclass(number_cls, Rational): return (Rational(223,71), Rational(22,7)) def _sage_(self): import sage.all as sage return sage.pi pi = S.Pi class GoldenRatio(NumberSymbol): __metaclass__ = Singleton is_real = True is_positive = True is_negative = False is_irrational = True __slots__ = [] def _as_mpf_val(self, prec): return mlib.from_man_exp(phi_fixed(prec+10), -prec-10) def _eval_expand_func(self, deep=True, **hints): return S.Half + S.Half*S.Sqrt(5) def approximation_interval(self, number_cls): if issubclass(number_cls, Integer): return (S.One, Rational(2)) elif issubclass(number_cls, Rational): pass def _sage_(self): import sage.all as sage return sage.golden_ratio class EulerGamma(NumberSymbol): __metaclass__ = Singleton is_real = True is_positive = True is_negative = False is_irrational = None __slots__ = [] def _as_mpf_val(self, prec): return mlib.from_man_exp(mlib.libhyper.euler_fixed( prec+10), -prec-10) def approximation_interval(self, number_cls): if issubclass(number_cls, Integer): return (S.Zero, S.One) elif issubclass(number_cls, Rational): return (S.Half, Rational(3, 5)) def _sage_(self): import sage.all as sage return sage.euler_gamma class Catalan(NumberSymbol): __metaclass__ = Singleton is_real = True is_positive = True is_negative = False is_irrational = None __slots__ = [] def _as_mpf_val(self, prec): return mlib.from_man_exp(mlib.catalan_fixed(prec+10), -prec-10) def approximation_interval(self, number_cls): if issubclass(number_cls, Integer): return (S.Zero, S.One) elif issubclass(number_cls, Rational): return (Rational(9, 10), S.One) def _sage_(self): import sage.all as sage return sage.catalan class ImaginaryUnit(AtomicExpr): __metaclass__ = Singleton is_commutative = True is_imaginary = True is_bounded = True is_finite = True is_number = True __slots__ = [] @staticmethod def __abs__(): return S.One def _eval_evalf(self, prec): return self def _eval_conjugate(self): return -S.ImaginaryUnit def _eval_power(b, e): """ b is I = sqrt(-1) e is symbolic object but not equal to 0, 1 I ** r -> (-1)**(r/2) -> exp(r/2 * Pi * I) -> sin(Pi*r/2) + cos(Pi*r/2) * I, r is decimal I ** 0 mod 4 -> 1 I ** 1 mod 4 -> I I ** 2 mod 4 -> -1 I ** 3 mod 4 -> -I """ if isinstance(e, Number): if isinstance(e, Integer): ei = e.p % 4 if ei == 0: return S.One if ei == 1: return S.ImaginaryUnit if ei == 2: return -S.One return -S.ImaginaryUnit return (S.NegativeOne) ** (e * S.Half) return def as_base_exp(self): return S.NegativeOne, S.Half def _sage_(self): import sage.all as sage return sage.I I = S.ImaginaryUnit try: # fractions is only available for python 2.6+ import fractions def sympify_fractions(f): return Rational(f.numerator, f.denominator) converter[fractions.Fraction] = sympify_fractions except ImportError: pass try: import gmpy def sympify_mpz(x): return Integer(long(x)) def sympify_mpq(x): return Rational(long(x.numer()), long(x.denom())) converter[type(gmpy.mpz(1))] = sympify_mpz converter[type(gmpy.mpq(1, 2))] = sympify_mpq except ImportError: pass def sympify_mpmath(x): return Expr._from_mpmath(x, x.context.prec) converter[mpnumeric] = sympify_mpmath def sympify_complex(a): real, imag = map(sympify, (a.real, a.imag)) return real + S.ImaginaryUnit * imag converter[complex] = sympify_complex _intcache[0] = S.Zero _intcache[1] = S.One _intcache[-1]= S.NegativeOne from function import FunctionClass from power import Pow, integer_nthroot from mul import Mul Mul.identity = One() from add import Add Add.identity = Zero() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/logic.py0000644000175000017500000002061612014170666023503 0ustar georgeskgeorgesk"""Logic expressions handling NOTE ---- at present this is mainly needed for facts.py , feel free however to improve this stuff for general purpose. """ from sympy.core.compatibility import iterable, cmp def fuzzy_bool(x): """Return True, False or None according to x. Whereas bool(x) returns True or False, fuzzy_bool allows for the None value. """ if x is None: return None return bool(x) def fuzzy_and(*args): """Return True (all True), False (any False) or None. If `a` is an iterable it must have more than one element.""" if (len(args) == 1 and iterable(args[0]) or len(args) > 2): if len(args) == 1: args = args[0] rv = True i = 0 for ai in args: ai = fuzzy_bool(ai) if ai is False: return False if rv: # this will stop updating if a None is ever trapped rv = ai i += 1 if i < 2: raise ValueError('iterables must have 2 or more elements') return rv a, b = [fuzzy_bool(i) for i in args] if a is True and b is True: return True elif a is False or b is False: return False def fuzzy_not(v): """'not' in fuzzy logic""" if v is None: return v else: return not v def name_not(k): """negate a name >>> from sympy.core.logic import name_not >>> name_not('zero') '!zero' >>> name_not('!zero') 'zero' """ if k[:1] != '!': return '!'+k else: return k[1:] class Logic(object): """Logical expression""" __slots__ = ['args'] # {} 'op' -> LogicClass op_2class = {} def __new__(cls, args): obj = object.__new__(cls) obj.args = tuple(args) # XXX do we need this: #print 'L: %s' % (obj.args,) assert not isinstance(obj.args[0], tuple) return obj def __hash__(self): return hash( (type(self).__name__, self.args) ) def __eq__(a, b): if not isinstance(b, type(a)): return False else: return a.args == b.args def __ne__(a, b): if not isinstance(b, type(a)): return True else: return a.args != b.args def __lt__(cls, other): if cls.__cmp__(other) == -1: return True return False def __cmp__(a, b): if type(a) is not type(b): return cmp( str(type(a)), str(type(b)) ) else: return cmp(a.args, b.args) # XXX later, we may want to change how expressions are printed def __str__(self): return '%s(%s)' % (self.op, ', '.join(str(a) for a in self.args)) # XXX this is not good ... __repr__ = __str__ @staticmethod def fromstring(text): """Logic from string e.g. !a & !b | c """ # XXX this is not general, but good enough terms = text.split() lexpr = None # current logical expression schedop = None # scheduled operation while True: # pop next term and exit loop if there is no terms left try: term = terms.pop(0) except IndexError: break # operation symbol if term in '&|': if schedop is not None: raise ValueError('double op forbidden: "%s %s"' % (term, schedop)) if lexpr is None: raise ValueError('%s cannot be in the beginning of expression' % term) schedop = term continue # already scheduled operation, e.g. '&' if schedop: lexpr = Logic.op_2class[schedop] ( *(lexpr, term) ) schedop = None continue # this should be atom if lexpr is not None: raise ValueError('missing op between "%s" and "%s"' % (lexpr, term)) lexpr = term # let's check that we ended up in correct state if schedop is not None: raise ValueError('premature end-of-expression in "%s"' % text) if lexpr is None: raise ValueError('"%s" is empty' % text) # everything looks good now return lexpr # XXX better name? class AndOr_Base(Logic): __slots__ = [] def __new__(cls, *args): if len(args) == 0: raise TypeError('%s requires at least one argument' % cls.__name__) # process bool args early bargs = [] for a in args: # &(F, ...) -> F # |(T, ...) -> T if a == cls.op_x_notx: return a # &(T, ...) -> &(...) # |(F, ...) -> |(...) elif a == (not cls.op_x_notx): continue # skip this argument bargs.append(a) args = bargs # &(a, !a) -> F # |(a, !a) -> T # XXX suboptinal for a in args: if Not(a) in args: return cls.op_x_notx args = cls.flatten(args) # canonicalize arguments # XXX do we always need this? # NB: this is needed to reduce number of &-nodes in beta-network args = sorted(args, key=hash) # now let's kill duplicate arguments, e.g. &(a,a,b) -> &(a,b) prev = None uargs= [] for a in args: if a != prev: uargs.append(a) prev = a args = uargs # &(a) -> a # |(a) -> a if len(args) == 1: return args[0] # when we are at this stage, it means that _all_ arguments were T/F and # all arguments were accepted as "let's see what follows next", so at # _this_ point the rule is: # |() -> F (*not* T) # &() -> T (*not* F) elif len(args) == 0: return not cls.op_x_notx return Logic.__new__(cls, args) @classmethod def flatten(cls, args): # quick-n-dirty flattening for And and Or args_queue = list(args) res = [] while True: try: arg = args_queue.pop(0) except IndexError: break if isinstance(arg, Logic): if arg.op == cls.op: #print 'flattening...', fargs, i, arg.args args_queue.extend( arg.args ) continue # another op -- leave it as is res.append( arg ) args = tuple(res) return args expand_lvl=0 class And(AndOr_Base): op = '&' op_x_notx = False __slots__ = [] def _eval_propagate_not(self): # !(a&b&c ...) == !a | !b | !c ... return Or( *[Not(a) for a in self.args] ) # (a|b|...) & c == (a&c) | (b&c) | ... def expand(self): # first locate Or for i in range(len(self.args)): arg = self.args[i] if isinstance(arg, Or): arest = self.args[:i] + self.args[i+1:] orterms = [And( *(arest + (a,)) ) for a in arg.args] for j in range(len(orterms)): if isinstance(orterms[j], Logic): orterms[j] = orterms[j].expand() res = Or(*orterms) return res else: return self def dbg_expand(self): global expand_lvl print '%sexpand %s' % (' '*expand_lvl, self) expand_lvl += 1 try: return self.old_expand() finally: expand_lvl -= 1 #old_expand = expand #expand = dbg_expand class Or(AndOr_Base): op = '|' op_x_notx = True __slots__ = [] def _eval_propagate_not(self): # !(a|b|c ...) == !a & !b & !c ... return And( *[Not(a) for a in self.args] ) class Not(Logic): op = '!' __slots__ = [] def __new__(cls, arg): if isinstance(arg, str): return name_not(arg) elif isinstance(arg, bool): return not arg elif isinstance(arg, Logic): # XXX this is a hack to expand right from the beginning arg = arg._eval_propagate_not() return arg obj = Logic.__new__(cls, (arg,)) return obj else: raise ValueError('Not: unknown argument %r' % (arg,)) Logic.op_2class['&'] = And Logic.op_2class['|'] = Or Logic.op_2class['!'] = Not wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/evalf.py0000644000175000017500000011340212014170666023477 0ustar georgeskgeorgesk""" Adaptive numerical evaluation of SymPy expressions, using mpmath for mathematical functions. """ import sympy.mpmath.libmp as libmp from sympy.mpmath import make_mpc, make_mpf, mp, mpc, mpf, nsum, quadts, quadosc from sympy.mpmath import inf as mpmath_inf from sympy.mpmath.libmp import (bitcount, from_int, from_man_exp, \ from_rational, fhalf, fnone, fone, fzero, mpf_abs, mpf_add, mpf_atan, \ mpf_atan2, mpf_cmp, mpf_cos, mpf_e, mpf_exp, mpf_log, mpf_lt, mpf_mul, \ mpf_neg, mpf_pi, mpf_pow, mpf_pow_int, mpf_shift, mpf_sin, mpf_sqrt, \ normalize, round_nearest, to_int, to_str) from sympy.mpmath.libmp.backend import MPZ from sympy.mpmath.libmp.libmpf import dps_to_prec from sympy.mpmath.libmp.gammazeta import mpf_bernoulli import math from sympify import sympify from core import C from singleton import S from containers import Tuple LG10 = math.log(10,2) # Used in a few places as placeholder values to denote exponents and # precision levels, e.g. of exact numbers. Must be careful to avoid # passing these to mpmath functions or returning them in final results. INF = float(mpmath_inf) MINUS_INF = float(-mpmath_inf) # ~= 100 digits. Real men set this to INF. DEFAULT_MAXPREC = 333 class PrecisionExhausted(ArithmeticError): pass #----------------------------------------------------------------------------# # # # Helper functions for arithmetic and complex parts # # # #----------------------------------------------------------------------------# """ An mpf value tuple is a tuple of integers (sign, man, exp, bc) representing a floating-point number: (-1)**sign*man*2**exp where bc should correspond to the number of bits used to represent the mantissa (man) in binary notation, e.g. (0,5,1,3) represents 10:: >>> from sympy.core.evalf import bitcount >>> n=(-1)**0 * 5 * 2**1; n, bitcount(5) (10, 3) A temporary result is a tuple (re, im, re_acc, im_acc) where re and im are nonzero mpf value tuples representing approximate numbers, or None to denote exact zeros. re_acc, im_acc are integers denoting log2(e) where e is the estimated relative accuracy of the respective complex part, but may be anything if the corresponding complex part is None. """ def fastlog(x): """Fast approximation of log2(x) for an mpf value tuple x. Notes: Calculated as exponent + width of mantissa. This is an approximation for two reasons: 1) it gives the ceil(log2(abs(x))) value and 2) it is too high by 1 in the case that x is an exact power of 2. Although this is easy to remedy by testing to see if the odd mpf mantissa is 1 (indicating that one was dealing with an exact power of 2) that would decrease the speed and is not necessary as this is only being used as an approximation for the number of bits in x. The correct return value could be written as "x[2] + (x[3] if x[1] != 1 else 0)". Since mpf tuples always have an odd mantissa, no check is done to see if the mantissa is a multiple of 2 (in which case the result would be too large by 1). Example:: >>> from sympy import log >>> from sympy.core.evalf import fastlog, bitcount >>> n=(-1)**0*5*2**1; n, (log(n)/log(2)).evalf(), fastlog((0,5,1,bitcount(5))) (10, 3.32192809488736, 4) """ if not x or x == fzero: return MINUS_INF return x[2] + x[3] def complex_accuracy(result): """ Returns relative accuracy of a complex number with given accuracies for the real and imaginary parts. The relative accuracy is defined in the complex norm sense as ||z|+|error|| / |z| where error is equal to (real absolute error) + (imag absolute error)*i. The full expression for the (logarithmic) error can be approximated easily by using the max norm to approximate the complex norm. In the worst case (re and im equal), this is wrong by a factor sqrt(2), or by log2(sqrt(2)) = 0.5 bit. """ re, im, re_acc, im_acc = result if not im: if not re: return INF return re_acc if not re: return im_acc re_size = fastlog(re) im_size = fastlog(im) absolute_error = max(re_size-re_acc, im_size-im_acc) relative_error = absolute_error - max(re_size, im_size) return -relative_error def get_abs(expr, prec, options): re, im, re_acc, im_acc = evalf(expr, prec+2, options) if not re: re, re_acc, im, im_acc = im, im_acc, re, re_acc if im: return libmp.mpc_abs((re, im), prec), None, re_acc, None else: return mpf_abs(re), None, re_acc, None def get_complex_part(expr, no, prec, options): """no = 0 for real part, no = 1 for imaginary part""" workprec = prec i = 0 while 1: res = evalf(expr, workprec, options) value, accuracy = res[no::2] if (not value) or accuracy >= prec: return value, None, accuracy, None workprec += max(30, 2**i) i += 1 def evalf_abs(expr, prec, options): return get_abs(expr.args[0], prec, options) def evalf_re(expr, prec, options): return get_complex_part(expr.args[0], 0, prec, options) def evalf_im(expr, prec, options): return get_complex_part(expr.args[0], 1, prec, options) def finalize_complex(re, im, prec): assert re and im if re == fzero and im == fzero: raise ValueError("got complex zero with unknown accuracy") size_re = fastlog(re) size_im = fastlog(im) # Convert fzeros to scaled zeros if re == fzero: re = mpf_shift(fone, size_im-prec) size_re = fastlog(re) elif im == fzero: im = mpf_shift(fone, size_re-prec) size_im = fastlog(im) if size_re > size_im: re_acc = prec im_acc = prec + min(-(size_re - size_im), 0) else: im_acc = prec re_acc = prec + min(-(size_im - size_re), 0) return re, im, re_acc, im_acc def chop_parts(value, prec): """ Chop off tiny real or complex parts. """ re, im, re_acc, im_acc = value # Method 1: chop based on absolute value if re and (fastlog(re) < -prec+4): re, re_acc = None, None if im and (fastlog(im) < -prec+4): im, im_acc = None, None # Method 2: chop if inaccurate and relatively small if re and im: delta = fastlog(re) - fastlog(im) if re_acc < 2 and (delta - re_acc <= -prec+4): re, re_acc = None, None if im_acc < 2 and (delta - im_acc >= prec-4): im, im_acc = None, None return re, im, re_acc, im_acc def check_target(expr, result, prec): a = complex_accuracy(result) if a < prec: raise PrecisionExhausted("Failed to distinguish the expression: \n\n%s\n\n" "from zero. Try simplifying the input, using chop=True, or providing " "a higher maxn for evalf" % (expr)) def get_integer_part(expr, no, options, return_ints=False): """ With no = 1, computes ceiling(expr) With no = -1, computes floor(expr) Note: this function either gives the exact result or signals failure. """ # The expression is likely less than 2^30 or so assumed_size = 30 ire, iim, ire_acc, iim_acc = evalf(expr, assumed_size, options) # We now know the size, so we can calculate how much extra precision # (if any) is needed to get within the nearest integer if ire and iim: gap = max(fastlog(ire)-ire_acc, fastlog(iim)-iim_acc) elif ire: gap = fastlog(ire)-ire_acc elif iim: gap = fastlog(iim)-iim_acc else: # ... or maybe the expression was exactly zero return None, None, None, None margin = 10 if gap >= -margin: ire, iim, ire_acc, iim_acc = evalf(expr, margin+assumed_size+gap, options) # We can now easily find the nearest integer, but to find floor/ceil, we # must also calculate whether the difference to the nearest integer is # positive or negative (which may fail if very close) def calc_part(expr, nexpr): nint = int(to_int(nexpr, round_nearest)) expr = C.Add(expr, -nint, evaluate=False) x, _, x_acc, _ = evalf(expr, 10, options) check_target(expr, (x, None, x_acc, None), 3) nint += int(no*(mpf_cmp(x or fzero, fzero) == no)) nint = from_int(nint) return nint, fastlog(nint) + 10 re, im, re_acc, im_acc = None, None, None, None if ire: re, re_acc = calc_part(C.re(expr, evaluate=False), ire) if iim: im, im_acc = calc_part(C.im(expr, evaluate=False), iim) if return_ints: return int(to_int(re or fzero)), int(to_int(im or fzero)) return re, im, re_acc, im_acc def evalf_ceiling(expr, prec, options): return get_integer_part(expr.args[0], 1, options) def evalf_floor(expr, prec, options): return get_integer_part(expr.args[0], -1, options) #----------------------------------------------------------------------------# # # # Arithmetic operations # # # #----------------------------------------------------------------------------# def add_terms(terms, prec, target_prec): """ Helper for evalf_add. Adds a list of (mpfval, accuracy) terms. """ if len(terms) == 1: if not terms[0]: # XXX: this is supposed to represent a scaled zero return mpf_shift(fone, target_prec), -1 return terms[0] max_extra_prec = 2*prec sum_man, sum_exp, absolute_error = 0, 0, MINUS_INF for x, accuracy in terms: if not x: continue sign, man, exp, bc = x if sign: man = -man absolute_error = max(absolute_error, bc+exp-accuracy) delta = exp - sum_exp if exp >= sum_exp: # x much larger than existing sum? # first: quick test if (delta > max_extra_prec) and \ ((not sum_man) or delta-bitcount(abs(sum_man)) > max_extra_prec): sum_man = man sum_exp = exp else: sum_man += (man << delta) else: delta = -delta # x much smaller than existing sum? if delta-bc > max_extra_prec: if not sum_man: sum_man, sum_exp = man, exp else: sum_man = (sum_man << delta) + man sum_exp = exp if absolute_error == MINUS_INF: return None, None if not sum_man: # XXX: this is supposed to represent a scaled zero return mpf_shift(fone, absolute_error), -1 if sum_man < 0: sum_sign = 1 sum_man = -sum_man else: sum_sign = 0 sum_bc = bitcount(sum_man) sum_accuracy = sum_exp + sum_bc - absolute_error r = normalize(sum_sign, sum_man, sum_exp, sum_bc, target_prec, round_nearest), sum_accuracy #print "returning", to_str(r[0],50), r[1] return r def evalf_add(v, prec, options): args = v.args target_prec = prec i = 0 oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) options['maxprec'] = min(oldmaxprec, 2*prec) try: while 1: terms = [evalf(arg, prec+10, options) for arg in args] re, re_acc = add_terms([(a[0],a[2]) for a in terms if a[0]], prec, target_prec) im, im_acc = add_terms([(a[1],a[3]) for a in terms if a[1]], prec, target_prec) accuracy = complex_accuracy((re, im, re_acc, im_acc)) if accuracy >= target_prec: if options.get('verbose'): print "ADD: wanted", target_prec, "accurate bits, got", re_acc, im_acc return re, im, re_acc, im_acc else: diff = target_prec - accuracy if (prec-target_prec) > options.get('maxprec', DEFAULT_MAXPREC): return re, im, re_acc, im_acc prec = prec + max(10+2**i, diff) options['maxprec'] = min(oldmaxprec, 2*prec) if options.get('verbose'): print "ADD: restarting with prec", prec i += 1 finally: options['maxprec'] = oldmaxprec def evalf_mul(v, prec, options): args = v.args # With guard digits, multiplication in the real case does not destroy # accuracy. This is also true in the complex case when considering the # total accuracy; however accuracy for the real or imaginary parts # separately may be lower. acc = prec target_prec = prec # XXX: big overestimate prec = prec + len(args) + 5 direction = 0 # Empty product is 1 man, exp, bc = MPZ(1), 0, 1 direction = 0 complex_factors = [] # First, we multiply all pure real or pure imaginary numbers. # direction tells us that the result should be multiplied by # i**direction for arg in args: re, im, re_acc, im_acc = evalf(arg, prec, options) if re and im: complex_factors.append((re, im, re_acc, im_acc)) continue elif re: (s, m, e, b), w_acc = re, re_acc elif im: (s, m, e, b), w_acc = im, im_acc direction += 1 else: return None, None, None, None direction += 2*s man *= m exp += e bc += b if bc > 3*prec: man >>= prec exp += prec acc = min(acc, w_acc) sign = (direction & 2) >> 1 v = normalize(sign, man, exp, bitcount(man), prec, round_nearest) if complex_factors: # make existing real scalar look like an imaginary and # multiply by the remaining complex numbers re, im = v, (0, MPZ(0), 0, 0) for wre, wim, wre_acc, wim_acc in complex_factors: # acc is the overall accuracy of the product; we aren't # computing exact accuracies of the product. acc = min(acc, complex_accuracy((wre, wim, wre_acc, wim_acc))) A = mpf_mul(re, wre, prec) B = mpf_mul(mpf_neg(im), wim, prec) C = mpf_mul(re, wim, prec) D = mpf_mul(im, wre, prec) re, xre_acc = add_terms([(A, acc), (B, acc)], prec, target_prec) im, xim_acc = add_terms([(C, acc), (D, acc)], prec, target_prec) if options.get('verbose'): print "MUL: wanted", target_prec, "accurate bits, got", acc # multiply by i if direction & 1: return mpf_neg(im), re, acc, acc else: return re, im, acc, acc else: # multiply by i if direction & 1: return None, v, None, acc else: return v, None, acc, None def evalf_pow(v, prec, options): target_prec = prec base, exp = v.args # We handle x**n separately. This has two purposes: 1) it is much # faster, because we avoid calling evalf on the exponent, and 2) it # allows better handling of real/imaginary parts that are exactly zero if exp.is_Integer: p = exp.p # Exact if not p: return fone, None, prec, None # Exponentiation by p magnifies relative error by |p|, so the # base must be evaluated with increased precision if p is large prec += int(math.log(abs(p),2)) re, im, re_acc, im_acc = evalf(base, prec+5, options) # Real to integer power if re and not im: return mpf_pow_int(re, p, target_prec), None, target_prec, None # (x*I)**n = I**n * x**n if im and not re: z = mpf_pow_int(im, p, target_prec) case = p % 4 if case == 0: return z, None, target_prec, None if case == 1: return None, z, None, target_prec if case == 2: return mpf_neg(z), None, target_prec, None if case == 3: return None, mpf_neg(z), None, target_prec # Zero raised to an integer power if not re: return None, None, None, None # General complex number to arbitrary integer power re, im = libmp.mpc_pow_int((re, im), p, prec) # Assumes full accuracy in input return finalize_complex(re, im, target_prec) # Pure square root if exp is S.Half: xre, xim, xre_acc, yim_acc = evalf(base, prec+5, options) # General complex square root if xim: re, im = libmp.mpc_sqrt((xre or fzero, xim), prec) return finalize_complex(re, im, prec) if not xre: return None, None, None, None # Square root of a negative real number if mpf_lt(xre, fzero): return None, mpf_sqrt(mpf_neg(xre), prec), None, prec # Positive square root return mpf_sqrt(xre, prec), None, prec, None # We first evaluate the exponent to find its magnitude # This determines the working precision that must be used prec += 10 yre, yim, yre_acc, yim_acc = evalf(exp, prec, options) # Special cases: x**0 if not (yre or yim): return fone, None, prec, None ysize = fastlog(yre) # Restart if too big # XXX: prec + ysize might exceed maxprec if ysize > 5: prec += ysize yre, yim, yre_acc, yim_acc = evalf(exp, prec, options) # Pure exponential function; no need to evalf the base if base is S.Exp1: if yim: re, im = libmp.mpc_exp((yre or fzero, yim), prec) return finalize_complex(re, im, target_prec) return mpf_exp(yre, target_prec), None, target_prec, None xre, xim, xre_acc, yim_acc = evalf(base, prec+5, options) # 0**y if not (xre or xim): return None, None, None, None # (real ** complex) or (complex ** complex) if yim: re, im = libmp.mpc_pow((xre or fzero, xim or fzero), (yre or fzero, yim), target_prec) return finalize_complex(re, im, target_prec) # complex ** real if xim: re, im = libmp.mpc_pow_mpf((xre or fzero, xim), yre, target_prec) return finalize_complex(re, im, target_prec) # negative ** real elif mpf_lt(xre, fzero): re, im = libmp.mpc_pow_mpf((xre, fzero), yre, target_prec) return finalize_complex(re, im, target_prec) # positive ** real else: return mpf_pow(xre, yre, target_prec), None, target_prec, None #----------------------------------------------------------------------------# # # # Special functions # # # #----------------------------------------------------------------------------# def evalf_trig(v, prec, options): """ This function handles sin and cos of real arguments. TODO: should also handle tan and complex arguments. """ if v.func is C.cos: func = mpf_cos elif v.func is C.sin: func = mpf_sin else: raise NotImplementedError arg = v.args[0] # 20 extra bits is possibly overkill. It does make the need # to restart very unlikely xprec = prec + 20 re, im, re_acc, im_acc = evalf(arg, xprec, options) if im: raise NotImplementedError if not re: if v.func is C.cos: return fone, None, prec, None elif v.func is C.sin: return None, None, None, None else: raise NotImplementedError # For trigonometric functions, we are interested in the # fixed-point (absolute) accuracy of the argument. xsize = fastlog(re) # Magnitude <= 1.0. OK to compute directly, because there is no # danger of hitting the first root of cos (with sin, magnitude # <= 2.0 would actually be ok) if xsize < 1: return func(re, prec, round_nearest), None, prec, None # Very large if xsize >= 10: xprec = prec + xsize re, im, re_acc, im_acc = evalf(arg, xprec, options) # Need to repeat in case the argument is very close to a # multiple of pi (or pi/2), hitting close to a root while 1: y = func(re, prec, round_nearest) ysize = fastlog(y) gap = -ysize accuracy = (xprec - xsize) - gap if accuracy < prec: if options.get('verbose'): print "SIN/COS", accuracy, "wanted", prec, "gap", gap print to_str(y,10) if xprec > options.get('maxprec', DEFAULT_MAXPREC): return y, None, accuracy, None xprec += gap re, im, re_acc, im_acc = evalf(arg, xprec, options) continue else: return y, None, prec, None def evalf_log(expr, prec, options): arg = expr.args[0] workprec = prec+10 xre, xim, xacc, _ = evalf(arg, workprec, options) if xim: # XXX: use get_abs etc instead re = evalf_log(C.log(C.Abs(arg, evaluate=False), evaluate=False), prec, options) im = mpf_atan2(xim, xre or fzero, prec) return re[0], im, re[2], prec imaginary_term = (mpf_cmp(xre, fzero) < 0) re = mpf_log(mpf_abs(xre), prec, round_nearest) size = fastlog(re) if prec - size > workprec: # We actually need to compute 1+x accurately, not x arg = C.Add(S.NegativeOne,arg,evaluate=False) xre, xim, xre_acc, xim_acc = evalf_add(arg, prec, options) prec2 = workprec - fastlog(xre) re = mpf_log(mpf_add(xre, fone, prec2), prec, round_nearest) re_acc = prec if imaginary_term: return re, mpf_pi(prec), re_acc, prec else: return re, None, re_acc, None def evalf_atan(v, prec, options): arg = v.args[0] xre, xim, reacc, imacc = evalf(arg, prec+5, options) if xim: raise NotImplementedError return mpf_atan(xre, prec, round_nearest), None, prec, None def evalf_piecewise(expr, prec, options): if 'subs' in options: expr = expr.subs(options['subs']) del options['subs'] if hasattr(expr,'func'): return evalf(expr, prec, options) if type(expr) == float: return evalf(C.Float(expr), prec, options) if type(expr) == int: return evalf(C.Integer(expr), prec, options) # We still have undefined symbols raise NotImplementedError def evalf_bernoulli(expr, prec, options): arg = expr.args[0] if not arg.is_Integer: raise ValueError("Bernoulli number index must be an integer") n = int(arg) b = mpf_bernoulli(n, prec, round_nearest) if b == fzero: return None, None, None, None return b, None, prec, None #----------------------------------------------------------------------------# # # # High-level operations # # # #----------------------------------------------------------------------------# def as_mpmath(x, prec, options): x = sympify(x) if isinstance(x, C.Zero): return mpf(0) if isinstance(x, C.Infinity): return mpf('inf') if isinstance(x, C.NegativeInfinity): return mpf('-inf') # XXX re, im, _, _ = evalf(x, prec, options) if im: return mpc(re or fzero, im) return mpf(re) def do_integral(expr, prec, options): func = expr.args[0] x, xlow, xhigh = expr.args[1] orig = mp.prec oldmaxprec = options.get('maxprec', DEFAULT_MAXPREC) options['maxprec'] = min(oldmaxprec, 2*prec) try: mp.prec = prec+5 xlow = as_mpmath(xlow, prec+15, options) xhigh = as_mpmath(xhigh, prec+15, options) # Integration is like summation, and we can phone home from # the integrand function to update accuracy summation style # Note that this accuracy is inaccurate, since it fails # to account for the variable quadrature weights, # but it is better than nothing have_part = [False, False] max_real_term = [MINUS_INF] max_imag_term = [MINUS_INF] def f(t): re, im, re_acc, im_acc = evalf(func, mp.prec, {'subs':{x:t}}) have_part[0] = re or have_part[0] have_part[1] = im or have_part[1] max_real_term[0] = max(max_real_term[0], fastlog(re)) max_imag_term[0] = max(max_imag_term[0], fastlog(im)) if im: return mpc(re or fzero, im) return mpf(re or fzero) if options.get('quad') == 'osc': A = C.Wild('A', exclude=[x]) B = C.Wild('B', exclude=[x]) D = C.Wild('D') m = func.match(C.cos(A*x+B)*D) if not m: m = func.match(C.sin(A*x+B)*D) if not m: raise ValueError("An integrand of the form sin(A*x+B)*f(x) " "or cos(A*x+B)*f(x) is required for oscillatory quadrature") period = as_mpmath(2*S.Pi/m[A], prec+15, options) result = quadosc(f, [xlow, xhigh], period=period) # XXX: quadosc does not do error detection yet quadrature_error = MINUS_INF else: result, quadrature_error = quadts(f, [xlow, xhigh], error=1) quadrature_error = fastlog(quadrature_error._mpf_) finally: options['maxprec'] = oldmaxprec mp.prec = orig if have_part[0]: re = result.real._mpf_ if re == fzero: re = mpf_shift(fone, min(-prec,-max_real_term[0],-quadrature_error)) re_acc = -1 else: re_acc = -max(max_real_term[0]-fastlog(re)-prec, quadrature_error) else: re, re_acc = None, None if have_part[1]: im = result.imag._mpf_ if im == fzero: im = mpf_shift(fone, min(-prec,-max_imag_term[0],-quadrature_error)) im_acc = -1 else: im_acc = -max(max_imag_term[0]-fastlog(im)-prec, quadrature_error) else: im, im_acc = None, None result = re, im, re_acc, im_acc return result def evalf_integral(expr, prec, options): workprec = prec i = 0 maxprec = options.get('maxprec', INF) while 1: result = do_integral(expr, workprec, options) accuracy = complex_accuracy(result) if accuracy >= prec or workprec >= maxprec: return result workprec += prec - max(-2**i, accuracy) i += 1 def check_convergence(numer, denom, n): """ Returns (h, g, p) where -- h is: > 0 for convergence of rate 1/factorial(n)**h < 0 for divergence of rate factorial(n)**(-h) = 0 for geometric or polynomial convergence or divergence -- abs(g) is: > 1 for geometric convergence of rate 1/h**n < 1 for geometric divergence of rate h**n = 1 for polynomial convergence or divergence (g < 0 indicates an alternating series) -- p is: > 1 for polynomial convergence of rate 1/n**h <= 1 for polynomial divergence of rate n**(-h) """ npol = C.Poly(numer, n) dpol = C.Poly(denom, n) p = npol.degree() q = dpol.degree() rate = q - p if rate: return rate, None, None constant = dpol.LC() / npol.LC() if abs(constant) != 1: return rate, constant, None if npol.degree() == dpol.degree() == 0: return rate, constant, 0 pc = npol.all_coeffs()[1] qc = dpol.all_coeffs()[1] return rate, constant, (qc-pc)/dpol.LC() def hypsum(expr, n, start, prec): """ Sum a rapidly convergent infinite hypergeometric series with given general term, e.g. e = hypsum(1/factorial(n), n). The quotient between successive terms must be a quotient of integer polynomials. """ from sympy import hypersimp, lambdify if start: expr = expr.subs(n, n+start) hs = hypersimp(expr, n) if hs is None: raise NotImplementedError("a hypergeometric series is required") num, den = hs.as_numer_denom() func1 = lambdify(n, num) func2 = lambdify(n, den) h, g, p = check_convergence(num, den, n) if h < 0: raise ValueError("Sum diverges like (n!)^%i" % (-h)) # Direct summation if geometric or faster if h > 0 or (h == 0 and abs(g) > 1): term = expr.subs(n, 0) term = (MPZ(term.p) << prec) // term.q s = term k = 1 while abs(term) > 5: term *= MPZ(func1(k-1)) term //= MPZ(func2(k-1)) s += term k += 1 return from_man_exp(s, -prec) else: alt = g < 0 if abs(g) < 1: raise ValueError("Sum diverges like (%i)^n" % abs(1/g)) if p < 1 or (p == 1 and not alt): raise ValueError("Sum diverges like n^%i" % (-p)) # We have polynomial convergence: use Richardson extrapolation # Need to use at least quad precision because a lot of cancellation # might occur in the extrapolation process prec2 = 4*prec term = expr.subs(n, 0) term = (MPZ(term.p) << prec2) // term.q def summand(k, _term=[term]): if k: k = int(k) _term[0] *= MPZ(func1(k-1)) _term[0] //= MPZ(func2(k-1)) return make_mpf(from_man_exp(_term[0], -prec2)) orig = mp.prec try: mp.prec = prec v = nsum(summand, [0, mpmath_inf], method='richardson') finally: mp.prec = orig return v._mpf_ def evalf_sum(expr, prec, options): func = expr.function limits = expr.limits if len(limits) != 1 or not isinstance(limits[0], Tuple) or \ len(limits[0]) != 3: raise NotImplementedError prec2 = prec+10 try: n, a, b = limits[0] if b != S.Infinity or a != int(a): raise NotImplementedError # Use fast hypergeometric summation if possible v = hypsum(func, n, int(a), prec2) delta = prec - fastlog(v) if fastlog(v) < -10: v = hypsum(func, n, int(a), delta) return v, None, min(prec, delta), None except NotImplementedError: # Euler-Maclaurin summation for general series eps = C.Float(2.0)**(-prec) for i in range(1, 5): m = n = 2**i * prec s, err = expr.euler_maclaurin(m=m, n=n, eps=eps, \ eval_integral=False) err = err.evalf() if err <= eps: break err = fastlog(evalf(abs(err), 20, options)[0]) re, im, re_acc, im_acc = evalf(s, prec2, options) if re_acc is None: re_acc = -err if im_acc is None: im_acc = -err return re, im, re_acc, im_acc #----------------------------------------------------------------------------# # # # Symbolic interface # # # #----------------------------------------------------------------------------# def evalf_symbol(x, prec, options): val = options['subs'][x] if isinstance(val, mpf): if not val: return None, None, None, None return val._mpf_, None, prec, None else: if not '_cache' in options: options['_cache'] = {} cache = options['_cache'] cached, cached_prec = cache.get(x.name, (None, MINUS_INF)) if cached_prec >= prec: return cached v = evalf(sympify(val), prec, options) cache[x.name] = (v, prec) return v evalf_table = None def _create_evalf_table(): global evalf_table evalf_table = { C.Symbol : evalf_symbol, C.Dummy : evalf_symbol, C.Float : lambda x, prec, options: (x._mpf_, None, prec, None), C.Rational : lambda x, prec, options: (from_rational(x.p, x.q, prec), None, prec, None), C.Integer : lambda x, prec, options: (from_int(x.p, prec), None, prec, None), C.Zero : lambda x, prec, options: (None, None, prec, None), C.One : lambda x, prec, options: (fone, None, prec, None), C.Half : lambda x, prec, options: (fhalf, None, prec, None), C.Pi : lambda x, prec, options: (mpf_pi(prec), None, prec, None), C.Exp1 : lambda x, prec, options: (mpf_e(prec), None, prec, None), C.ImaginaryUnit : lambda x, prec, options: (None, fone, None, prec), C.NegativeOne : lambda x, prec, options: (fnone, None, prec, None), C.exp : lambda x, prec, options: evalf_pow(C.Pow(S.Exp1, x.args[0], evaluate=False), prec, options), C.cos : evalf_trig, C.sin : evalf_trig, C.Add : evalf_add, C.Mul : evalf_mul, C.Pow : evalf_pow, C.log : evalf_log, C.atan : evalf_atan, C.Abs : evalf_abs, C.re : evalf_re, C.im : evalf_im, C.floor : evalf_floor, C.ceiling : evalf_ceiling, C.Integral : evalf_integral, C.Sum : evalf_sum, C.Piecewise : evalf_piecewise, C.bernoulli : evalf_bernoulli, } def evalf(x, prec, options): try: rf = evalf_table[x.func] r = rf(x, prec, options) except KeyError: #r = finalize_complex(x._eval_evalf(prec)._mpf_, fzero, prec) try: # Fall back to ordinary evalf if possible if 'subs' in options: x = x.subs(options['subs']) r = x._eval_evalf(prec)._mpf_, None, prec, None except AttributeError: raise NotImplementedError if options.get("verbose"): print "### input", x print "### output", to_str(r[0] or fzero, 50) print "### raw", r#r[0], r[2] print if options.get("chop"): r = chop_parts(r, prec) if options.get("strict"): check_target(x, r, prec) return r class EvalfMixin(object): """Mixin class adding evalf capabililty.""" __slots__ = [] def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False): """ Evaluate the given formula to an accuracy of n digits. Optional keyword arguments: subs= Substitute numerical values for symbols, e.g. subs={x:3, y:1+pi}. maxn= Allow a maximum temporary working precision of maxn digits (default=100) chop= Replace tiny real or imaginary parts in subresults by exact zeros (default=False) strict= Raise PrecisionExhausted if any subresult fails to evaluate to full accuracy, given the available maxprec (default=False) quad= Choose algorithm for numerical quadrature. By default, tanh-sinh quadrature is used. For oscillatory integrals on an infinite interval, try quad='osc'. verbose= Print debug information (default=False) """ if not evalf_table: _create_evalf_table() prec = dps_to_prec(n) options = {'maxprec': max(prec,int(maxn*LG10)), 'chop': chop, 'strict': strict, 'verbose': verbose} if subs is not None: options['subs'] = subs if quad is not None: options['quad'] = quad try: result = evalf(self, prec+4, options) except NotImplementedError: # Fall back to the ordinary evalf v = self._eval_evalf(prec) if v is None: return self try: # If the result is numerical, normalize it result = evalf(v, prec, options) except: # Probably contains symbols or unknown functions return v re, im, re_acc, im_acc = result if re: p = max(min(prec, re_acc), 1) #re = mpf_pos(re, p, round_nearest) re = C.Float._new(re, p) else: re = S.Zero if im: p = max(min(prec, im_acc), 1) #im = mpf_pos(im, p, round_nearest) im = C.Float._new(im, p) return re + im*S.ImaginaryUnit else: return re n = evalf def _evalf(self, prec): """Helper for evalf. Does the same thing but takes binary precision""" r = self._eval_evalf(prec) if r is None: r = self return r def _eval_evalf(self, prec): return def _to_mpmath(self, prec, allow_ints=True): # mpmath functions accept ints as input errmsg = "cannot convert to mpmath number" if allow_ints and self.is_Integer: return self.p try: re, im, _, _ = evalf(self, prec, {}) if im: if not re: re = fzero return make_mpc((re, im)) else: return make_mpf(re) except NotImplementedError: v = self._eval_evalf(prec) if v is None: raise ValueError(errmsg) if v.is_Float: return make_mpf(v._mpf_) # Number + Number*I is also fine re, im = v.as_real_imag() if allow_ints and re.is_Integer: re = from_int(re.p) elif re.is_Float: re = re._mpf_ else: raise ValueError(errmsg) if allow_ints and im.is_Integer: im = from_int(im.p) elif im.is_Float: im = im._mpf_ else: raise ValueError(errmsg) return make_mpc((re, im)) def N(x, n=15, **options): """ Calls x.evalf(n, \*\*options). Both .evalf() and N() are equivalent, use the one that you like better. See also the docstring of .evalf() for information on the options. Example: >>> from sympy import Sum, Symbol, oo, N >>> from sympy.abc import k >>> Sum(1/k**k, (k, 1, oo)) Sum(k**(-k), (k, 1, oo)) >>> N(Sum(1/k**k, (k, 1, oo)), 4) 1.291 """ return sympify(x).evalf(n, **options) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/compatibility.py0000644000175000017500000001243112014170666025253 0ustar georgeskgeorgesk""" Reimplementations of constructs introduced in later versions of Python than we support. """ # These are in here because telling if something is an iterable just by calling # hasattr(obj, "__iter__") behaves differently in Python 2 and Python 3. In # particular, hasattr(str, "__iter__") is False in Python 2 and True in Python 3. # I think putting them here also makes it easier to use them in the core. def iterable(i, exclude=(basestring, dict)): """ Return a boolean indicating whether i is an iterable in the sympy sense. When sympy is working with iterables, it is almost always assuming that the iterable is not a string or a mapping, so those are excluded by default. If you want a pure python definition, make exclude=None. To exclude multiple items, pass them as a tuple. See also: is_sequence Examples: >>> from sympy.utilities.iterables import iterable >>> from sympy import Tuple >>> things = [[1], (1,), set([1]), Tuple(1), (j for j in [1, 2]), {1:2}, '1', 1] >>> for i in things: ... print iterable(i), type(i) True True True True True False False False >>> iterable({}, exclude=None) True >>> iterable({}, exclude=str) True >>> iterable("no", exclude=str) False """ try: iter(i) except TypeError: return False if exclude: return not isinstance(i, exclude) return True def is_sequence(i, include=None): """ Return a boolean indicating whether i is a sequence in the sympy sense. If anything that fails the test below should be included as being a sequence for your application, set 'include' to that object's type; multiple types should be passed as a tuple of types. Note: although generators can generate a sequence, they often need special handling to make sure their elements are captured before the generator is exhausted, so these are not included by default in the definition of a sequence. See also: iterable Examples: >>> from sympy.utilities.iterables import is_sequence >>> from types import GeneratorType >>> is_sequence([]) True >>> is_sequence(set()) False >>> is_sequence('abc') False >>> is_sequence('abc', include=str) True >>> generator = (c for c in 'abc') >>> is_sequence(generator) False >>> is_sequence(generator, include=(str, GeneratorType)) True """ return (hasattr(i, '__getitem__') and iterable(i) or bool(include) and isinstance(i, include)) """ Wrapping some imports in try/except statements to allow the same code to be used in Python 3+ as well. """ try: callable = callable except NameError: import collections def callable(obj): return isinstance(obj, collections.Callable) try: from functools import reduce except ImportError: reduce = reduce def cmp_to_key(mycmp): """ Convert a cmp= function into a key= function This code is included in Python 2.7 and 3.2 in functools. """ class K(object): def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): return mycmp(self.obj, other.obj) < 0 def __gt__(self, other): return mycmp(self.obj, other.obj) > 0 def __eq__(self, other): return mycmp(self.obj, other.obj) == 0 def __le__(self, other): return mycmp(self.obj, other.obj) <= 0 def __ge__(self, other): return mycmp(self.obj, other.obj) >= 0 def __ne__(self, other): return mycmp(self.obj, other.obj) != 0 return K try: import __builtin__ cmp = __builtin__.cmp except AttributeError: def cmp(a,b): return (a > b) - (a < b) try: from itertools import product except ImportError: # Python 2.5 def product(*args, **kwds): # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 pools = map(tuple, args) * kwds.get('repeat', 1) result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod) try: from itertools import permutations except ImportError: # Python 2.5 def permutations(iterable, r=None): # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC # permutations(range(3)) --> 012 021 102 120 201 210 pool = tuple(iterable) n = len(pool) r = n if r is None else r if r > n: return indices = range(n) cycles = range(n, n-r, -1) yield tuple(pool[i] for i in indices[:r]) while n: for i in reversed(range(r)): cycles[i] -= 1 if cycles[i] == 0: indices[i:] = indices[i+1:] + indices[i:i+1] cycles[i] = n - i else: j = cycles[i] indices[i], indices[-j] = indices[-j], indices[i] yield tuple(pool[i] for i in indices[:r]) break else: return wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/assumptions.py0000644000175000017500000002762712014170666025004 0ustar georgeskgeorgeskfrom facts import FactRules from sympy.core.compatibility import cmp class CycleDetected(Exception): """(internal) used to detect cycles when evaluating assumptions through prerequisites """ pass class AssumeMeths(object): """ Define default assumption methods. AssumeMeths should be used to derive Basic class only. All symbolic objects have assumption attributes that can be accessed via .is_ attribute. Assumptions determine certain properties of symbolic objects. Assumptions can have 3 possible values: True, False, None. None is returned when it is impossible to say something about the property. For example, a Symbol is not know beforehand to be positive. By default, all symbolic values are in the largest set in the given context without specifying the property. For example, a symbol that has a property being integer, is also real, complex, etc. Here follows a list of possible assumption names: - commutative - object commutes with any other object with respect to multiplication operation. - real - object can have only values from the set of real numbers - integer - object can have only values from the set of integers - bounded - object absolute value is bounded - positive - object can have only positive values - negative - object can have only negative values - nonpositive - object can have only nonpositive values - nonnegative - object can have only nonnegative values - comparable - object.evalf() returns Number object. - irrational - object value cannot be represented exactly by Rational - unbounded - object value is arbitrarily large - infinitesimal - object value is infinitesimal Example rules: positive=T -> nonpositive=F, real=T real=T & positive=F -> nonpositive=T unbounded=F|T -> bounded=not unbounded XXX ok? irrational=T -> real=T Implementation note: assumption values are stored in ._assumption dictionary or are returned by getter methods (with property decorators) or are attributes of objects/classes. Examples: - True, when we are sure about a property. For example, when we are working only with real numbers: >>> from sympy import Symbol >>> Symbol('x', real = True) x - False - None (if you don't know if the property is True or false) """ __slots__ = ['_assumptions', # assumptions '_a_inprogress', # already-seen requests (when deducing # through prerequisites -- see CycleDetected) ] # This are the rules under which our assumptions function # # References # ---------- # # negative, -- http://en.wikipedia.org/wiki/Negative_number # nonnegative # # even, odd -- http://en.wikipedia.org/wiki/Parity_(mathematics) # imaginary -- http://en.wikipedia.org/wiki/Imaginary_number # composite -- http://en.wikipedia.org/wiki/Composite_number # finite -- http://en.wikipedia.org/wiki/Finite # infinitesimal -- http://en.wikipedia.org/wiki/Infinitesimal # irrational -- http://en.wikipedia.org/wiki/Irrational_number # ... _assume_rules = FactRules([ 'integer -> rational', 'rational -> real', 'real -> complex', 'imaginary -> complex', 'complex -> commutative', 'odd == integer & !even', 'even == integer & !odd', 'real == negative | zero | positive', 'positive -> real & !negative & !zero', 'negative -> real & !positive & !zero', 'nonpositive == real & !positive', 'nonnegative == real & !negative', 'zero -> infinitesimal & even', 'prime -> integer & positive', 'composite == integer & positive & !prime', 'irrational == real & !rational', 'imaginary -> !real', '!bounded == unbounded', 'noninteger == real & !integer', '!zero == nonzero', # XXX do we need this ? 'finite -> bounded', # XXX do we need this? 'finite -> !zero', # XXX wrong? 'infinitesimal -> !finite', # XXX is this ok? ]) _assume_defined = _assume_rules.defined_facts.copy() _assume_defined.add('comparable') _assume_defined = frozenset(_assume_defined) ################################### # positive/negative from .evalf() # ################################### # properties that indicate ordering on real axis _real_ordering = set(['negative', 'nonnegative', 'positive', 'nonpositive']) # what can be said from cmp(x.evalf(),0) # NOTE: if x.evalf() is zero we can say nothing _real_cmp0_table= { 'positive': {1: True, -1: False, 0: None}, 'negative': {1: False, -1: True, 0: None}, } # because we can say nothing if x.evalf() is zero, nonpositive is the same # as negative _real_cmp0_table['nonpositive'] = _real_cmp0_table['negative'] _real_cmp0_table['nonnegative'] = _real_cmp0_table['positive'] def __getstate__(self, cls=None): if cls is None: # This is the case for the instance that gets pickled cls = self.__class__ d = {} # Get all data that should be stored from super classes for c in cls.__bases__: if hasattr(c, "__getstate__"): d.update(c.__getstate__(self, c)) # Get all information that should be stored from cls and return the dic for name in cls.__slots__: if hasattr(self, name): d[name] = getattr(self, name) return d def __setstate__(self, d): # All values that were pickled are now assigned to a fresh instance for name, value in d.iteritems(): try: setattr(self, name, value) except: pass def _what_known_about(self, k): """tries hard to give an answer to: what is known about fact `k` NOTE: You should not use this directly -- see make__get_assumption instead This function is called when a request is made to see what a fact value is. If we are here, it means that the asked-for fact is not known, and we should try to find a way to find its value. For this we use several techniques: 1. _eval_is_ ------------------ first fact-evaluation function is tried, for example _eval_is_integer 2. relations ------------ if the first step did not succeeded (no such function, or its return is None) then we try related facts. For example means rational --> integer another example is joined rule: integer & !odd --> even so in the latter case if we are looking at what 'even' value is, 'integer' and 'odd' facts will be asked. 3. evalf() for comparable ------------------------- as a last resort for comparable objects we get their numerical value -- this helps to determine facts like 'positive' and 'negative' In all cases when we settle on some fact value, it is given to _learn_new_facts to deduce all its implications, and also the result is cached in ._assumptions for later quick access. """ # 'defined' assumption if k not in self._assume_defined: raise AttributeError('undefined assumption %r' % (k)) assumptions = self._assumptions seen = self._a_inprogress #print '%s=?\t%s %s' % (name, self,seen) if k in seen: raise CycleDetected seen.append(k) try: # First try the assumption evaluation function if it exists if hasattr(self, '_eval_is_'+k): #print 'FWDREQ: %s\t%s' % (self, k) try: a = getattr(self,'_eval_is_'+k)() # no luck - e.g. is_integer -> ... -> is_integer except CycleDetected: #print 'CYC' pass else: if a is not None: self._learn_new_facts( ((k,a),) ) return a # Try assumption's prerequisites for pk in self._assume_rules.prereq.get(k,()): #print 'pk: %s' % pk if hasattr(self, '_eval_is_'+pk): # cycle if pk in seen: continue #print 'PREREQ: %s\t%s <- %s' % (self, k, pk) a = getattr(self,'is_'+pk) if a is not None: self._learn_new_facts( ((pk,a),) ) # it is possible that we either know or don't know k at # this point try: return self._assumptions[k] except KeyError: pass finally: seen.pop() # For positive/negative try to ask evalf if k in self._real_ordering: if self.is_comparable: v = self.evalf() #FIXME-py3k: this fails for complex numbers, when we define cmp #FIXME-py3k: as (a>b) - (a>> from sympy import integer_nthroot >>> integer_nthroot(16,2) (4, True) >>> integer_nthroot(26,2) (5, False) """ y, n = int(y), int(n) if y < 0: raise ValueError("y must be nonnegative") if n < 1: raise ValueError("n must be positive") if y in (0, 1): return y, True if n == 1: return y, True if n == 2: x, rem = mpmath.libmp.sqrtrem(y) return int(x), not rem if n > y: return 1, False # Get initial estimate for Newton's method. Care must be taken to # avoid overflow try: guess = int(y**(1./n) + 0.5) except OverflowError: expt = _log(y, 2)/n if expt > 53: shift = int(expt - 53) guess = int(2.0**(expt-shift) + 1) << shift else: guess = int(2.0**expt) #print n if guess > 2**50: # Newton iteration xprev, x = -1, guess while 1: t = x**(n-1) #xprev, x = x, x - (t*x-y)//(n*t) xprev, x = x, ((n-1)*x + y//t)//n #print n, x-xprev, abs(x-xprev) < 2 if abs(x - xprev) < 2: break else: x = guess # Compensate t = x**n while t < y: x += 1 t = x**n while t > y: x -= 1 t = x**n return x, t == y class Pow(Expr): is_Pow = True __slots__ = ['is_commutative'] @cacheit def __new__(cls, b, e, evaluate=True): b = _sympify(b) e = _sympify(e) if evaluate: if e is S.Zero: return S.One elif e is S.One: return b else: obj = b._eval_power(e) if obj is not None: return obj obj = Expr.__new__(cls, b, e) obj.is_commutative = (b.is_commutative and e.is_commutative) return obj @property def base(self): return self._args[0] @property def exp(self): return self._args[1] @classmethod def class_key(cls): return 3, 2, cls.__name__ def _eval_power(self, other): b, e = self.as_base_exp() if other.is_integer: return Pow(b, e * other) if b.is_nonnegative and (e.is_real or other.is_real): return Pow(b, e * other) if e.is_even and b.is_real: # hence b is pos and e is real return Pow(abs(b), e * other) if abs(e) < S.One and other.is_real: return Pow(b, e * other) def _eval_is_comparable(self): c1 = self.base.is_comparable if c1 is None: return c2 = self.exp.is_comparable if c2 is None: return return c1 and c2 def _eval_is_even(self): if self.exp.is_integer and self.exp.is_positive: if self.base.is_even: return True if self.base.is_integer: return False def _eval_is_positive(self): if self.base.is_positive: if self.exp.is_real: return True elif self.base.is_negative: if self.exp.is_even: return True if self.exp.is_odd: return False elif self.base.is_nonpositive: if self.exp.is_odd: return False def _eval_is_negative(self): if self.base.is_negative: if self.exp.is_odd: return True if self.exp.is_even: return False elif self.base.is_positive: if self.exp.is_real: return False elif self.base.is_nonnegative: if self.exp.is_real: return False elif self.base.is_nonpositive: if self.exp.is_even: return False elif self.base.is_real: if self.exp.is_even: return False def _eval_is_integer(self): c1 = self.base.is_integer c2 = self.exp.is_integer if c1 is None or c2 is None: return None if not c1: if self.exp.is_nonnegative: return False if c1 and c2: if self.exp.is_nonnegative or self.exp.is_positive: return True if self.exp.is_negative: return False def _eval_is_real(self): real_b = self.base.is_real if real_b is None: return real_e = self.exp.is_real if real_e is None: return if real_b and real_e: if self.base.is_positive: return True else: # negative or zero (or positive) if self.exp.is_integer: return True elif self.base.is_negative: if self.exp.is_Rational: return False im_b = self.base.is_imaginary im_e = self.exp.is_imaginary if im_b: if self.exp.is_integer: if self.exp.is_even: return True elif self.exp.is_odd: return False elif (self.exp in [S.ImaginaryUnit, -S.ImaginaryUnit] and self.base in [S.ImaginaryUnit, -S.ImaginaryUnit]): return True if real_b and im_e: c = self.exp.coeff(S.ImaginaryUnit) if c: ok = (c*C.log(self.base)/S.Pi).is_Integer if ok is not None: return ok def _eval_is_odd(self): if not (self.base.is_integer and self.exp.is_nonnegative): return return self.base.is_odd def _eval_is_bounded(self): if self.exp.is_negative: if self.base.is_infinitesimal: return False if self.base.is_unbounded: return True c1 = self.base.is_bounded if c1 is None: return c2 = self.exp.is_bounded if c2 is None: return if c1 and c2: if self.exp.is_nonnegative or self.base.is_nonzero: return True def _eval_subs(self, old, new): if self == old: return new if old.func is self.func and self.base == old.base: coeff1, terms1 = self.exp.as_coeff_mul() coeff2, terms2 = old.exp.as_coeff_mul() if terms1 == terms2: pow = coeff1/coeff2 if pow.is_Integer or self.base.is_commutative: return Pow(new, pow) # (x**(2*y)).subs(x**(3*y),z) -> z**(2/3) if old.func is C.exp: coeff1, terms1 = old.args[0].as_coeff_mul() coeff2, terms2 = (self.exp*C.log(self.base)).as_coeff_mul() if terms1 == terms2: pow = coeff1/coeff2 if pow.is_Integer or self.base.is_commutative: return Pow(new, pow) # (x**(2*y)).subs(x**(3*y),z) -> z**(2/3) return Pow(self.base._eval_subs(old, new), self.exp._eval_subs(old, new)) def as_base_exp(self): if self.base.is_Rational and self.base.p==1 and self.base is not S.Infinity: return 1/self.base, -self.exp return self.base, self.exp def _eval_conjugate(self): from sympy.functions.elementary.complexes import conjugate as c return c(self.base)**self.exp def _eval_expand_basic(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_basic'): newterm = term._eval_expand_basic(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_power_exp(self, deep=True, *args, **hints): """a**(n+m) -> a**n*a**m""" if deep: b = self.base.expand(deep=deep, **hints) e = self.exp.expand(deep=deep, **hints) else: b = self.base e = self.exp if e.is_Add and e.is_commutative: expr = [] for x in e.args: if deep: x = x.expand(deep=deep, **hints) expr.append(Pow(self.base, x)) return Mul(*expr) return Pow(b, e) def _eval_expand_power_base(self, deep=True, **hints): """(a*b)**n -> a**n * b**n""" force = hints.get('force', False) b, ewas = self.args if deep: e = self.exp.expand(deep=deep, **hints) else: e = self.exp if b.is_Mul: bargs = b.args if force or e.is_integer: nonneg = bargs other = [] elif ewas.is_Rational or len(bargs) == 2 and bargs[0] is S.NegativeOne: # the Rational exponent was already expanded automatically # if there is a negative Number * foo, foo must be unknown # or else it, too, would have automatically expanded; # sqrt(-Number*foo) -> sqrt(Number)*sqrt(-foo); then # sqrt(-foo) -> unchanged if foo is not positive else # -> I*sqrt(foo) # So...if we have a 2 arg Mul and the first is a Number # that number is -1 and there is nothing more than can # be done without the force=True hint nonneg= [] else: # this is just like what is happening automatically, except # that now we are doing it for an arbitrary exponent for which # no automatic expansion is done sifted = sift(b.args, lambda x: x.is_nonnegative) nonneg = sifted.get(True, []) other = sifted.get(None, []) neg = sifted.get(False, []) # make sure the Number gets pulled out if neg and neg[0].is_Number and neg[0] is not S.NegativeOne: nonneg.append(-neg[0]) neg[0] = S.NegativeOne # leave behind a negative sign oddneg = len(neg) % 2 if oddneg: other.append(S.NegativeOne) # negate all negatives and append to nonneg nonneg += [-n for n in neg] if nonneg: # then there's a new expression to return other = [Pow(Mul(*other), e)] if deep: return Mul(*([Pow(b.expand(deep=deep, **hints), e)\ for b in nonneg] + other)) else: return Mul(*([Pow(b, e) for b in nonneg] + other)) return Pow(b, e) def _eval_expand_mul(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_mul'): newterm = term._eval_expand_mul(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_multinomial(self, deep=True, **hints): """(a+b+..) ** n -> a**n + n*a**(n-1)*b + .., n is nonzero integer""" if deep: b = self.base.expand(deep=deep, **hints) e = self.exp.expand(deep=deep, **hints) else: b = self.base e = self.exp if b is None: base = self.base else: base = b if e is None: exp = self.exp else: exp = e if e is not None or b is not None: result = Pow(base, exp) if result.is_Pow: base, exp = result.base, result.exp else: return result else: result = None if exp.is_Rational and exp.p > 0 and base.is_Add: if not exp.is_Integer: n = Integer(exp.p // exp.q) if not n: return Pow(base, exp) else: radical, result = Pow(base, exp - n), [] for term in Add.make_args(Pow(base, n)._eval_expand_multinomial(deep=False)): result.append(term*radical) return Add(*result) n = int(exp) if base.is_commutative: order_terms, other_terms = [], [] for order in base.args: if order.is_Order: order_terms.append(order) else: other_terms.append(order) if order_terms: # (f(x) + O(x^n))^m -> f(x)^m + m*f(x)^{m-1} *O(x^n) f = Add(*other_terms) g = (f**(n-1)).expand() return (f*g).expand() + n*g*Add(*order_terms) if base.is_number: # Efficiently expand expressions of the form (a + b*I)**n # where 'a' and 'b' are real numbers and 'n' is integer. a, b = base.as_real_imag() if a.is_Rational and b.is_Rational: if not a.is_Integer: if not b.is_Integer: k = Pow(a.q * b.q, n) a, b = a.p*b.q, a.q*b.p else: k = Pow(a.q, n) a, b = a.p, a.q*b elif not b.is_Integer: k = Pow(b.q, n) a, b = a*b.q, b.p else: k = 1 a, b, c, d = int(a), int(b), 1, 0 while n: if n & 1: c, d = a*c-b*d, b*c+a*d n -= 1 a, b = a*a-b*b, 2*a*b n //= 2 I = S.ImaginaryUnit if k == 1: return c + I*d else: return Integer(c)/k + I*d/k p = other_terms # (x+y)**3 -> x**3 + 3*x**2*y + 3*x*y**2 + y**3 # in this particular example: # p = [x,y]; n = 3 # so now it's easy to get the correct result -- we get the # coefficients first: from sympy import multinomial_coefficients expansion_dict = multinomial_coefficients(len(p), n) # in our example: {(3, 0): 1, (1, 2): 3, (0, 3): 1, (2, 1): 3} # and now construct the expression. # An elegant way would be to use Poly, but unfortunately it is # slower than the direct method below, so it is commented out: #b = {} #for k in expansion_dict: # b[k] = Integer(expansion_dict[k]) #return Poly(b, *p).as_expr() from sympy.polys.polyutils import basic_from_dict result = basic_from_dict(expansion_dict, *p) return result else: if n == 2: return Add(*[f*g for f in base.args for g in base.args]) else: multi = (base**(n-1))._eval_expand_multinomial(deep=False) if multi.is_Add: return Add(*[f*g for f in base.args for g in multi.args]) else: return Add(*[f*multi for f in base.args]) elif exp.is_Rational and exp.p < 0 and base.is_Add and abs(exp.p) > exp.q: return 1 / Pow(base, -exp)._eval_expand_multinomial(deep=False) elif exp.is_Add and base.is_Number: # a + b a b # n --> n n , where n, a, b are Numbers coeff, tail = S.One, S.Zero for term in exp.args: if term.is_Number: coeff *= Pow(base, term) else: tail += term return coeff * Pow(base, tail) else: return result def _eval_expand_log(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_log'): newterm = term._eval_expand_log(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_complex(self, deep=True, **hints): re_part, im_part = self.as_real_imag(deep=deep, **hints) return re_part + im_part*S.ImaginaryUnit def as_real_imag(self, deep=True, **hints): from sympy.core.symbol import symbols from sympy.polys.polytools import poly from sympy.core.function import expand_multinomial if self.exp.is_Integer: exp = self.exp re, im = self.base.as_real_imag(deep=deep) a, b = symbols('a b', cls=Dummy) if exp >= 0: if re.is_Number and im.is_Number: # We can be more efficient in this case expr = expand_multinomial(self.base**exp) return expr.as_real_imag() expr = poly((a + b)**exp) # a = re, b = im; expr = (a + b*I)**exp else: mag = re**2 + im**2 re, im = re/mag, -im/mag if re.is_Number and im.is_Number: # We can be more efficient in this case expr = expand_multinomial((re + im*S.ImaginaryUnit)**-exp) return expr.as_real_imag() expr = poly((a + b)**-exp) # Terms with even b powers will be real r = [i for i in expr.terms() if not i[0][1] % 2] re_part = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) # Terms with odd b powers will be imaginary r = [i for i in expr.terms() if i[0][1] % 4 == 1] im_part1 = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) r = [i for i in expr.terms() if i[0][1] % 4 == 3] im_part3 = Add(*[cc*a**aa*b**bb for (aa, bb), cc in r]) return (re_part.subs({a: re, b: S.ImaginaryUnit*im}), im_part1.subs({a: re, b: im}) + im_part3.subs({a: re, b: -im})) elif self.exp.is_Rational: # NOTE: This is not totally correct since for x**(p/q) with # x being imaginary there are actually q roots, but # only a single one is returned from here. re, im = self.base.as_real_imag(deep=deep) r = Pow(Pow(re, 2) + Pow(im, 2), S.Half) t = C.atan2(im, re) rp, tp = Pow(r, self.exp), t*self.exp return (rp*C.cos(tp), rp*C.sin(tp)) else: if deep: hints['complex'] = False return (C.re(self.expand(deep, complex=False)), C.im(self. expand(deep, **hints))) else: return (C.re(self), C.im(self)) def _eval_expand_trig(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_trig'): newterm = term._eval_expand_trig(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_expand_func(self, deep=True, **hints): sargs, terms = self.args, [] for term in sargs: if hasattr(term, '_eval_expand_func'): newterm = term._eval_expand_func(deep=deep, **hints) else: newterm = term terms.append(newterm) return self.func(*terms) def _eval_derivative(self, s): dbase = self.base.diff(s) dexp = self.exp.diff(s) return self * (dexp * C.log(self.base) + dbase * self.exp/self.base) def _eval_evalf(self, prec): base, exp = self.as_base_exp() base = base._evalf(prec) if not exp.is_Integer: exp = exp._evalf(prec) if exp < 0 and not base.is_real: base = base.conjugate() / (base * base.conjugate())._evalf(prec) exp = -exp return Pow(base, exp).expand() def _eval_is_polynomial(self, syms): if self.exp.has(*syms): return False if self.base.has(*syms): return self.base._eval_is_polynomial(syms) and \ self.exp.is_Integer and \ self.exp >= 0 else: return True def _eval_is_rational_function(self, syms): if self.exp.has(*syms): return False if self.base.has(*syms): return self.base._eval_is_rational_function(syms) and \ self.exp.is_Integer else: return True def as_numer_denom(self): base, exp = self.as_base_exp() n, d = base.as_numer_denom() if d.is_negative and n.is_negative: n, d = -n, -d if exp.is_Integer: if exp.is_negative: n, d = d, n exp = -exp return Pow(n, exp), Pow(d, exp) elif exp.is_Rational or d.is_positive: if d.is_negative is None: # we won't split up the base if exp.is_negative: return S.One, Pow(base, -exp) else: return self, S.One if d.is_negative: n = -n d = -d c, t = exp.as_coeff_mul() if c.is_negative: n, d = d, n exp = -exp return Pow(n, exp), Pow(d, exp) else: c, t = exp.as_coeff_mul() if c.is_negative: return 1, base**-exp # unprocessed Float and NumberSymbol return self, S.One def matches(self, expr, repl_dict={}, evaluate=False): if evaluate: return self.subs(repl_dict).matches(expr, repl_dict) expr = _sympify(expr) b, e = expr.as_base_exp() # special case, pattern = 1 and expr.exp can match to 0 if expr is S.One: d = repl_dict.copy() d = self.exp.matches(S.Zero, d) if d is not None: return d d = repl_dict.copy() d = self.base.matches(b, d) if d is None: return None d = self.exp.subs(d).matches(e, d) if d is None: return Expr.matches(self, expr, repl_dict, evaluate) return d def _eval_nseries(self, x, n, logx): # NOTE! This function is an important part of the gruntz algorithm # for computing limits. It has to return a generalised power series # with coefficients in C(log, log(x)). In more detail: # It has to return an expression # c_0*x**e_0 + c_1*x**e_1 + ... (finitely many terms) # where e_i are numbers (not necessarily integers) and c_i are expression # involving only numbers, the log function, and log(x). from sympy import powsimp, collect, exp, log, O, ceiling b, e = self.args if e.is_Integer: if e > 0: # positive integer powers are easy to expand, e.g.: # sin(x)**4 = (x-x**3/3+...)**4 = ... return Pow(b._eval_nseries(x, n=n, logx=logx), e )._eval_expand_multinomial(deep = False) elif e is S.NegativeOne: # this is also easy to expand using the formula: # 1/(1 + x) = 1 - x + x**2 - x**3 ... # so we need to rewrite base to the form "1+x" b = b._eval_nseries(x, n=n, logx=logx) prefactor = b.as_leading_term(x) # express "rest" as: rest = 1 + k*x**l + ... + O(x**n) rest = ((b - prefactor)/prefactor)._eval_expand_mul() if rest == 0: # if prefactor == w**4 + x**2*w**4 + 2*x*w**4, we need to # factor the w**4 out using collect: return 1/collect(prefactor, x) if rest.is_Order: return 1/prefactor + rest/prefactor n2 = rest.getn() if n2 is not None: n = n2 # remove the O - powering this is slow if logx is not None: rest = rest.removeO() k, l = rest.leadterm(x) if l.is_Rational and l > 0: pass elif l.is_number and l > 0: l = l.evalf() else: raise NotImplementedError() terms = [1/prefactor] for m in xrange(1, ceiling(n/l)): new_term = terms[-1]*(-rest) if new_term.is_Pow: new_term = new_term._eval_expand_multinomial(deep = False) else: new_term = new_term._eval_expand_mul(deep = False) terms.append(new_term) # Append O(...), we know the order. if n2 is None or logx is not None: terms.append(O(x**n)) return powsimp(Add(*terms), deep=True, combine='exp') else: # negative powers are rewritten to the cases above, for example: # sin(x)**(-4) = 1/( sin(x)**4) = ... # and expand the denominator: denominator = (b**(-e))._eval_nseries(x, n=n, logx=logx) if 1/denominator == self: return self # now we have a type 1/f(x), that we know how to expand return (1/denominator)._eval_nseries(x, n=n, logx=logx) if e.has(Symbol): return exp(e*log(b))._eval_nseries(x, n=n, logx=logx) if b == x: return powsimp(self, deep=True, combine='exp') # work for b(x)**e where e is not an Integer and does not contain x # and hopefully has no other symbols def e2int(e): """return the integer value (if possible) of e and a flag indicating whether it is bounded or not.""" n = e.limit(x, 0) unbounded = n.is_unbounded if not unbounded: # XXX was int or floor intended? int used to behave like floor # so int(-Rational(1, 2)) returned -1 rather than int's 0 try: n = int(n) except TypeError: #well, the n is something more complicated (like 1+log(2)) try: n = int(n.evalf()) + 1 # XXX why is 1 being added? except TypeError: pass # hope that base allows this to be resolved n = _sympify(n) return n, unbounded order = O(x**n, x) ei, unbounded = e2int(e) b0 = b.limit(x, 0) if unbounded and (b0 is S.One or b0.has(Symbol)): # XXX what order if b0 is S.One: resid = (b - 1) if resid.is_positive: return S.Infinity elif resid.is_negative: return S.Zero raise ValueError('cannot determine sign of %s' % resid) return b0**ei if (b0 is S.Zero or b0.is_unbounded): if unbounded is not False: return b0**e # XXX what order if not ei.is_number: # if not, how will we proceed? raise ValueError('expecting numerical exponent but got %s' % ei) nuse = n - ei lt = b.compute_leading_term(x, logx=logx) # arg = sin(x); lt = x # XXX o is not used -- was this to be used as o and o2 below to compute a new e? o = order*lt**(1 - e) bs = b._eval_nseries(x, n=nuse, logx=logx) if bs.is_Add: bs = bs.removeO() if bs.is_Add: # bs -> lt + rest -> lt*(1 + (bs/lt - 1)) return ((Pow(lt, e)* Pow((bs/lt).expand(), e). nseries(x, n=nuse, logx=logx)).expand() + order) return bs**e + order # either b0 is bounded but neither 1 nor 0 or e is unbounded # b -> b0 + (b-b0) -> b0 * (1 + (b/b0-1)) o2 = order*(b0**-e) z = (b/b0 - 1) o = O(z, x) #r = self._compute_oseries3(z, o2, self.taylor_term) if o is S.Zero or o2 is S.Zero: unbounded = True else: if o.expr.is_number: e2 = log(o2.expr*x)/log(x) else: e2 = log(o2.expr)/log(o.expr) n, unbounded = e2int(e2) if unbounded: # requested accuracy gives infinite series, # order is probably nonpolynomial e.g. O(exp(-1/x), x). r = 1 + z else: l = [] g = None for i in xrange(n + 2): g = self.taylor_term(i, z, g) g = g.nseries(x, n=n, logx=logx) l.append(g) r = Add(*l) return r*b0**e + order def _eval_as_leading_term(self, x): if not self.exp.has(x): return Pow(self.base.as_leading_term(x), self.exp) return C.exp(self.exp * C.log(self.base)).as_leading_term(x) @cacheit def taylor_term(self, n, x, *previous_terms): # of (1+x)**e if n<0: return S.Zero x = _sympify(x) return C.binomial(self.exp, n) * Pow(x, n) def _sage_(self): return self.args[0]._sage_()**self.args[1]._sage_() from add import Add from numbers import Integer from mul import Mul from symbol import Symbol, Dummy wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/singleton.py0000644000175000017500000000403412014170666024404 0ustar georgeskgeorgesk"""Singleton mechanism""" from core import BasicMeta, Registry from sympify import sympify class SingletonRegistry(Registry): """ A map between singleton classes and the corresponding instances. E.g. S.Exp == C.Exp() """ __slots__ = [] __call__ = staticmethod(sympify) def __repr__(self): return "S" S = SingletonRegistry() class Singleton(BasicMeta): """ Metaclass for singleton classes. A singleton class has only one instance which is returned every time the class is instantiated. Additionally, this instance can be accessed through the global registry object S as S.. Example:: >>> from sympy import S, Basic >>> from sympy.core.singleton import Singleton >>> class MySingleton(Basic): ... __metaclass__ = Singleton >>> Basic() is Basic() False >>> MySingleton() is MySingleton() True >>> S.MySingleton is MySingleton() True ** Developer notes ** The class is instanciated immediately at the point where it is defined by calling cls.__new__(cls). This instance is cached and cls.__new__ is rebound to return it directly. The original constructor is also cached to allow subclasses to access it and have their own instance. """ def __init__(cls, name, bases, dict_): super(Singleton, cls).__init__(cls, name, bases, dict_) for ancestor in cls.mro(): if '__new__' in ancestor.__dict__: break if isinstance(ancestor, Singleton) and ancestor is not cls: ctor = ancestor._new_instance else: ctor = cls.__new__ cls._new_instance = staticmethod(ctor) the_instance = ctor(cls) def __new__(cls): return the_instance cls.__new__ = staticmethod(__new__) setattr(S, name, the_instance) # Inject pickling support. def __getnewargs__(self): return () cls.__getnewargs__ = __getnewargs__ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/0000755000175000017500000000000012014170666023171 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_assumptions.py0000644000175000017500000003377112014170666027202 0ustar georgeskgeorgeskfrom sympy.core import Symbol, S, Rational, Integer from sympy.utilities.pytest import XFAIL, raises from sympy import I, sqrt def test_symbol_unset(): x = Symbol('x',real=True, integer=True) assert x.is_real == True assert x.is_integer == True assert x.is_imaginary == False assert x.is_noninteger == False def test_zero(): z = Integer(0) assert z.is_commutative == True assert z.is_integer == True assert z.is_rational == True assert z.is_real == True assert z.is_complex == True assert z.is_noninteger == False assert z.is_irrational == False assert z.is_imaginary == False assert z.is_positive == False assert z.is_negative == False assert z.is_nonpositive == True assert z.is_nonnegative == True assert z.is_even == True assert z.is_odd == False assert z.is_bounded == True assert z.is_unbounded == False assert z.is_finite == False assert z.is_infinitesimal == True assert z.is_comparable == True assert z.is_prime == False assert z.is_composite == False def test_one(): z = Integer(1) assert z.is_commutative == True assert z.is_integer == True assert z.is_rational == True assert z.is_real == True assert z.is_complex == True assert z.is_noninteger == False assert z.is_irrational == False assert z.is_imaginary == False assert z.is_positive == True assert z.is_negative == False assert z.is_nonpositive == False assert z.is_nonnegative == True assert z.is_even == False assert z.is_odd == True assert z.is_bounded == True assert z.is_unbounded == False assert z.is_finite == True assert z.is_infinitesimal == False assert z.is_comparable == True assert z.is_prime == True assert z.is_composite == False def test_negativeone(): z = Integer(-1) assert z.is_commutative == True assert z.is_integer == True assert z.is_rational == True assert z.is_real == True assert z.is_complex == True assert z.is_noninteger == False assert z.is_irrational == False assert z.is_imaginary == False assert z.is_positive == False assert z.is_negative == True assert z.is_nonpositive == True assert z.is_nonnegative == False assert z.is_even == False assert z.is_odd == True assert z.is_bounded == True assert z.is_unbounded == False assert z.is_finite == True assert z.is_infinitesimal == False assert z.is_comparable == True assert z.is_prime == False assert z.is_composite == False def test_infinity(): oo = S.Infinity assert oo.is_commutative == True assert oo.is_integer == None assert oo.is_rational == None assert oo.is_real == True assert oo.is_complex == True assert oo.is_noninteger == None assert oo.is_irrational == None assert oo.is_imaginary == False assert oo.is_positive == True assert oo.is_negative == False assert oo.is_nonpositive == False assert oo.is_nonnegative == True assert oo.is_even == None assert oo.is_odd == None assert oo.is_bounded == False assert oo.is_unbounded == True assert oo.is_finite == False assert oo.is_infinitesimal == False assert oo.is_comparable == True assert oo.is_prime == None assert oo.is_composite == None def test_neg_infinity(): mm = S.NegativeInfinity assert mm.is_commutative == True assert mm.is_integer == None assert mm.is_rational == None assert mm.is_real == True assert mm.is_complex == True assert mm.is_noninteger == None assert mm.is_irrational == None assert mm.is_imaginary == False assert mm.is_positive == False assert mm.is_negative == True assert mm.is_nonpositive == True assert mm.is_nonnegative == False assert mm.is_even == None assert mm.is_odd == None assert mm.is_bounded == False assert mm.is_unbounded == True assert mm.is_finite == False assert mm.is_infinitesimal == False assert mm.is_comparable == True assert mm.is_prime == False assert mm.is_composite == False def test_nan(): nan = S.NaN assert nan.is_commutative == True assert nan.is_integer == None assert nan.is_rational == None assert nan.is_real == None assert nan.is_complex == None assert nan.is_noninteger == None assert nan.is_irrational == None assert nan.is_imaginary == None assert nan.is_positive == None assert nan.is_negative == None assert nan.is_nonpositive == None assert nan.is_nonnegative == None assert nan.is_even == None assert nan.is_odd == None assert nan.is_bounded == None assert nan.is_unbounded == None assert nan.is_finite == None assert nan.is_infinitesimal == None assert nan.is_comparable == False assert nan.is_prime == None assert nan.is_composite == None def test_pos_rational(): r = Rational(3,4) assert r.is_commutative == True assert r.is_integer == False assert r.is_rational == True assert r.is_real == True assert r.is_complex == True assert r.is_noninteger == True assert r.is_irrational == False assert r.is_imaginary == False assert r.is_positive == True assert r.is_negative == False assert r.is_nonpositive == False assert r.is_nonnegative == True assert r.is_even == False assert r.is_odd == False assert r.is_bounded == True assert r.is_unbounded == False assert r.is_finite == True assert r.is_infinitesimal == False assert r.is_comparable == True assert r.is_prime == False assert r.is_composite == False r = Rational(1,4) assert r.is_nonpositive == False assert r.is_positive == True assert r.is_negative == False assert r.is_nonnegative == True r = Rational(5,4) assert r.is_negative == False assert r.is_positive == True assert r.is_nonpositive == False assert r.is_nonnegative == True r = Rational(5,3) assert r.is_nonnegative == True assert r.is_positive == True assert r.is_negative == False assert r.is_nonpositive == False def test_neg_rational(): r = Rational(-3,4) assert r.is_positive == False assert r.is_nonpositive == True assert r.is_negative == True assert r.is_nonnegative == False r = Rational(-1,4) assert r.is_nonpositive == True assert r.is_positive == False assert r.is_negative == True assert r.is_nonnegative == False r = Rational(-5,4) assert r.is_negative == True assert r.is_positive == False assert r.is_nonpositive == True assert r.is_nonnegative == False r = Rational(-5,3) assert r.is_nonnegative == False assert r.is_positive == False assert r.is_negative == True assert r.is_nonpositive == True def test_pi(): z = S.Pi assert z.is_commutative == True assert z.is_integer == False assert z.is_rational == False assert z.is_real == True assert z.is_complex == True assert z.is_noninteger == True assert z.is_irrational == True assert z.is_imaginary == False assert z.is_positive == True assert z.is_negative == False assert z.is_nonpositive == False assert z.is_nonnegative == True assert z.is_even == False assert z.is_odd == False assert z.is_bounded == True assert z.is_unbounded == False assert z.is_finite == True assert z.is_infinitesimal == False assert z.is_comparable == True assert z.is_prime == False assert z.is_composite == False def test_E(): z = S.Exp1 assert z.is_commutative == True assert z.is_integer == False assert z.is_rational == False assert z.is_real == True assert z.is_complex == True assert z.is_noninteger == True assert z.is_irrational == True assert z.is_imaginary == False assert z.is_positive == True assert z.is_negative == False assert z.is_nonpositive == False assert z.is_nonnegative == True assert z.is_even == False assert z.is_odd == False assert z.is_bounded == True assert z.is_unbounded == False assert z.is_finite == True assert z.is_infinitesimal == False assert z.is_comparable == True assert z.is_prime == False assert z.is_composite == False def test_I(): z = S.ImaginaryUnit assert z.is_commutative == True assert z.is_integer == False assert z.is_rational == False assert z.is_real == False assert z.is_complex == True assert z.is_noninteger == False assert z.is_irrational == False assert z.is_imaginary == True assert z.is_positive == False assert z.is_negative == False assert z.is_nonpositive == False assert z.is_nonnegative == False assert z.is_even == False assert z.is_odd == False assert z.is_bounded == True assert z.is_unbounded == False assert z.is_finite == True assert z.is_infinitesimal == False assert z.is_comparable == None assert z.is_prime == False assert z.is_composite == False def test_symbol_zero(): x = Symbol('x',zero=True) assert x.is_positive == False assert x.is_nonpositive == True assert x.is_negative == False assert x.is_nonnegative == True assert x.is_zero == True assert x.is_nonzero == False def test_symbol_positive(): x = Symbol('x',positive=True) assert x.is_positive == True assert x.is_nonpositive == False assert x.is_negative == False assert x.is_nonnegative == True assert x.is_zero == False assert x.is_nonzero == True def test_neg_symbol_positive(): x = -Symbol('x',positive=True) assert x.is_positive == False assert x.is_nonpositive == True assert x.is_negative == True assert x.is_nonnegative == False def test_neg_symbol_positive2(): x = -Symbol('x',positive=True) assert x.is_zero == False assert x.is_nonzero == True def test_symbol_nonpositive(): x = Symbol('x',nonpositive=True) assert x.is_positive == False assert x.is_nonpositive == True assert x.is_negative == None assert x.is_nonnegative == None assert x.is_zero == None assert x.is_nonzero == None def test_neg_symbol_nonpositive(): x = -Symbol('x',nonpositive=True) assert x.is_positive == None assert x.is_nonpositive == None assert x.is_negative == False assert x.is_nonnegative == True assert x.is_zero == None assert x.is_nonzero == None def test_prime_symbol(): x = Symbol('x', prime=True) assert x.is_prime == True assert x.is_integer == True assert x.is_positive == True assert x.is_negative == False assert x.is_nonpositive == False assert x.is_nonnegative == True x = Symbol('x', prime=False) assert x.is_prime == False assert x.is_integer == None assert x.is_positive == None assert x.is_negative == None assert x.is_nonpositive == None assert x.is_nonnegative == None def test_other_symbol(): x = Symbol('x', integer=True) assert x.is_integer == True assert x.is_real == True x = Symbol('x', integer=True, nonnegative=True) assert x.is_integer == True assert x.is_nonnegative == True assert x.is_negative == False assert x.is_positive == None x = Symbol('x', integer=True, nonpositive=True) assert x.is_integer == True assert x.is_nonpositive == True assert x.is_positive == False assert x.is_negative == None x = Symbol('x', odd=True) assert x.is_odd == True assert x.is_even == False assert x.is_integer == True x = Symbol('x', even=True) assert x.is_even == True assert x.is_odd == False assert x.is_integer == True x = Symbol('x', integer=True, nonnegative=True) assert x.is_integer == True assert x.is_nonnegative == True x = Symbol('x', integer=True, nonpositive=True) assert x.is_integer == True assert x.is_nonpositive == True raises(AttributeError, "x.is_real = False") def test_other_symbol_fail1(): # XXX x.is_even currently will be True x = Symbol('x', odd=False) assert x.is_odd == False assert x.is_even == None assert x.is_integer == None def test_other_symbol_fail2(): # XXX x.is_odd currently will be True x = Symbol('x', even=False) assert x.is_even == False assert x.is_odd == None assert x.is_integer == None def test_issue749(): a = Symbol('a', real=False) assert a.is_real == False assert a.is_integer == False assert a.is_negative == False assert a.is_positive == False assert a.is_nonnegative == False assert a.is_nonpositive == False assert a.is_zero == False def test_issue726(): """catch: hash instability""" x = Symbol("x") y = Symbol("y") a1 = x+y a2 = y+x a2.is_comparable h1 = hash(a1) h2 = hash(a2) assert h1 == h2 def test_issue1723(): z = (-1)**Rational(1,3)*(1-I*sqrt(3)) assert z.is_real in [True, None] def test_hash_vs_typeinfo(): """seemingly different typeinfo, but in fact equal""" x = Symbol('x') # the following two are semantically equal x1= Symbol('x', even=True) x2= Symbol('x', integer=True, odd=False) assert hash(x1) == hash(x2) assert x1 == x2 def test_hash_vs_typeinfo_2(): """different typeinfo should mean !eq""" # the following two are semantically different x = Symbol('x') x1= Symbol('x', even=True) assert x != x1 def test_hash_vs_eq(): """catch: different hash for equal objects""" a = 1 + S.Pi # important: do not fold it into a Number instance ha= hash(a) # it should be Add/Mul/... to trigger the bug a.is_positive # this uses .evalf() and deduces it is positive assert a.is_positive == True # be sure that hash stayed the same assert ha == hash(a) # now b should be the same expression b = a.expand(trig=True) hb= hash(b) assert a == b assert ha== hb wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_logic.py0000644000175000017500000000677212014170666025713 0ustar georgeskgeorgeskfrom sympy.core.logic import fuzzy_not, name_not, Logic, And, Or, Not, \ fuzzy_and from sympy.utilities.pytest import raises from sympy.core.compatibility import cmp T = True F = False U = None def test_fuzzy_not(): assert fuzzy_not(T) == F assert fuzzy_not(F) == T assert fuzzy_not(U) == U def test_fuzzy_and(): assert fuzzy_and(*[T, T]) == T assert fuzzy_and(*[T, F]) == F assert fuzzy_and(*[T, U]) == U assert fuzzy_and(*[F, F]) == F assert fuzzy_and(*[F, U]) == F assert fuzzy_and(*[U, U]) == U assert fuzzy_and([T, T]) == T assert fuzzy_and([T, F]) == F assert fuzzy_and([T, U]) == U assert fuzzy_and([F, F]) == F assert fuzzy_and([F, U]) == F assert fuzzy_and([U, U]) == U def test_name_not(): assert name_not('zero') == '!zero' assert name_not('!zero') == 'zero' def test_logic_cmp(): l1 = And('a', Not('b')) l2 = And('a', Not('b')) assert hash(l1) == hash(l2) assert (l1==l2) == T assert (l1!=l2) == F assert cmp(l1, l2) == 0 assert And('a','b','c') == And('b','a','c') assert And('a','b','c') == And('c','b','a') assert And('a','b','c') == And('c','a','b') def test_logic_onearg(): raises(TypeError, 'And()') raises(TypeError, 'Or ()') assert And(T) == T assert And(F) == F assert Or (T) == T assert Or (F) == F assert And('a') == 'a' assert Or ('a') == 'a' def test_logic_xnotx(): assert And('a', '!a') == F assert Or ('a', '!a') == T def test_logic_eval_TF(): assert And(F, F) == F assert And(F, T) == F assert And(T, F) == F assert And(T, T) == T assert Or (F, F) == F assert Or (F, T) == T assert Or (T, F) == T assert Or (T, T) == T assert And('a', T) == 'a' assert And('a', F) == F assert Or ('a', T) == T assert Or ('a', F) == 'a' def test_logic_combine_args(): assert And('a', 'b', 'a') == And('a', 'b') assert Or ('a', 'b', 'a') == Or ('a', 'b') assert And( And('a','b'), And('c','d') ) == And('a','b','c','d') assert Or ( Or ('a','b'), Or ('c','d') ) == Or ('a','b','c','d') assert Or( 't', And('n','p','r'), And('n','r'), And('n','p','r'), 't', And('n','r') ) == \ Or('t', And('n','p','r'), And('n','r')) def test_logic_expand(): t = And(Or('a','b'), 'c') assert t.expand() == Or(And('a','c'), And('b','c')) t = And(Or('a','!b'),'b') assert t.expand() == And('a','b') t = And(Or('a','b'), Or('c','d')) assert t.expand() == Or(And('a','c'), And('a','d'), And('b','c'), And('b','d')) def test_logic_fromstring(): S = Logic.fromstring assert S('a') == 'a' assert S('!a') == '!a' assert S('a & b') == And('a','b') assert S('a | b') == Or ('a','b') assert S('a | b & c') == And(Or ('a','b'), 'c') assert S('a & b | c') == Or (And('a','b'), 'c') assert S('a & b & c') == And('a','b','c') assert S('a | b | c') == Or ('a','b','c') raises(ValueError, "S('| a')") raises(ValueError, "S('& a')") raises(ValueError, "S('a | | b')") raises(ValueError, "S('a | & b')") raises(ValueError, "S('a & & b')") raises(ValueError, "S('a |')") def test_logic_not(): assert Not('a') == '!a' assert Not('!a') == 'a' # NOTE: we may want to change default Not behaviour and put this # functionality into some method. assert Not(And('a','b')) == Or ('!a','!b') assert Not(Or ('a','b')) == And('!a','!b') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_eval.py0000644000175000017500000000402512014170666025532 0ustar georgeskgeorgeskfrom sympy import Symbol, Function, exp, sqrt, Rational, I, cos, tan from sympy.utilities.pytest import XFAIL def test_add_eval(): a = Symbol("a") b = Symbol("b") c = Rational(1) p = Rational(5) assert a*b+c+p == a*b+6 assert c+a+p == a+6 assert c+a-p == a+(-4) assert a+a == 2*a assert a+p+a == 2*a+5 assert c+p == Rational(6) assert b+a-b == a def test_addmul_eval(): a = Symbol("a") b = Symbol("b") c = Rational(1) p = Rational(5) assert c+a+b*c+a-p == 2*a+b+(-4) assert a*2+p+a == a*2+5+a assert a*2+p+a == 3*a+5 assert a*2+a == 3*a def test_pow_eval(): # XXX Pow does not fully support conversion of negative numbers # to their complex equivalent assert (-1)**Rational(1,2) == I assert sqrt(-4) == 2*I assert sqrt( 4) == 2 assert (8)**Rational(1,3) == 2 assert (-8)**Rational(1,3) == 2*((-1)**Rational(1,3)) assert sqrt(-2) == I*sqrt(2) assert (-1)**Rational(1,3) != I assert (-10)**Rational(1,3) != I*((10)**Rational(1,3)) assert (-2)**Rational(1,4) != (2)**Rational(1,4) assert 64**(Rational(1)/3) == 4 assert 64**(Rational(2)/3) == 16 assert 24*64**(-Rational(1)/2) == 3 assert (-27)**Rational(1,3) == 3*(-1)**Rational(1,3) assert (cos(2) / tan(2))**2 == (cos(2) / tan(2))**2 @XFAIL def test_pow_eval_X1(): assert (-1)**Rational(1,3) == Rational(1,2)+Rational(1,2)*I*3**Rational(1,2) def test_mulpow_eval(): x = Symbol('x') assert sqrt(50)/(sqrt(2)*x) == 5/x assert sqrt(27)/sqrt(3) == 3 def test_evalpow_bug(): x = Symbol("x") assert 1/(1/x) == x assert 1/(-1/x) == -x def test_symbol_expand(): x = Symbol('x') y = Symbol('y') f = x**4*y**4 assert f == x**4*y**4 assert f == f.expand() g = (x*y)**4 assert g == f assert g.expand() == f assert g.expand() == g.expand().expand() def test_function(): f = Function('f') l,x = map(Symbol, 'lx') assert exp(l(x))*l(x)/exp(l(x)) == l(x) assert exp(f(x))*f(x)/exp(f(x)) == f(x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_equal.py0000644000175000017500000000310112014170666025704 0ustar georgeskgeorgeskfrom sympy import Symbol, Dummy, Rational, exp def test_equal(): b = Symbol("b") a = Symbol("a") c = Symbol("c") e1 = a+b e2 = 2*a*b e3 = a**3*b**2 e4 = a*b+b*a assert not e1 == e2 assert not e1 == e2 assert e1 != e2 assert e2 == e4 assert e2 != e3 assert not e2 == e3 x = Symbol("x") e1 = exp(x+1/x) y = Symbol("x") e2 = exp(y+1/y) assert e1 == e2 assert not e1 != e2 y = Symbol("y") e2 = exp(y+1/y) assert not e1 == e2 assert e1 != e2 e5 = Rational(3)+2*x-x-x assert e5 == 3 assert 3 == e5 assert e5 != 4 assert 4 != e5 assert e5 != 3+x assert 3+x != e5 def test_expevalbug(): x = Symbol("x") e1 = exp(1*x) e3 = exp(x) assert e1 == e3 def test_cmp_bug1(): class T(object): pass t = T() x = Symbol("x") assert not (x == t) assert (x != t) def test_cmp_bug2(): class T(object): pass t = T() assert not (Symbol == t) assert (Symbol != t) def test_cmp_bug1258(): """ Check that Basic subclasses can be compared with sympifiable objects. http://code.google.com/p/sympy/issues/detail?id=1258 """ assert not (Symbol == 1) assert (Symbol != 1) assert not (Symbol == 'x') assert (Symbol != 'x') def test_dummy_eq(): x = Symbol('x') y = Symbol('y') u = Dummy('u') assert (u**2 + 1).dummy_eq(x**2 + 1) == True assert ((u**2 + 1) == (x**2 + 1)) == False assert (u**2 + y).dummy_eq(x**2 + y, x) == True assert (u**2 + y).dummy_eq(x**2 + y, y) == False wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_var.py0000644000175000017500000000301012014170666025364 0ustar georgeskgeorgesk# Tests for var are in their own file, because var pollutes global namespace. from sympy import Symbol, var, Function, FunctionClass from sympy.utilities.pytest import raises # make z1 with call-depth = 1 def make_z1(): var("z1") # make z2 with call-depth = 2 def __make_z2(): var("z2") def make_z2(): __make_z2() def test_var(): var("a") assert a == Symbol("a") var("b bb cc zz _x") assert b == Symbol("b") assert bb == Symbol("bb") assert cc == Symbol("cc") assert zz == Symbol("zz") assert _x == Symbol("_x") v = var(['d','e','fg']) assert d == Symbol('d') assert e == Symbol('e') assert fg == Symbol('fg') # check return value assert v == [d, e, fg] # see if var() really injects into global namespace raises(NameError, "z1") make_z1() assert z1 == Symbol("z1") raises(NameError, "z2") make_z2() assert z2 == Symbol("z2") def test_var_return(): raises(ValueError, "var('')") v2 = var('q') v3 = var('q p') assert v2 == Symbol('q') assert v3 == (Symbol('q'), Symbol('p')) def test_var_accepts_comma(): v1 = var('x y z') v2 = var('x,y,z') v3 = var('x,y z') assert v1 == v2 assert v1 == v3 def test_var_keywords(): var('x y', real=True) assert x.is_real and y.is_real def test_var_cls(): f = var('f', cls=Function) assert isinstance(f, FunctionClass) g, h = var('g,h', cls=Function) assert isinstance(g, FunctionClass) assert isinstance(h, FunctionClass) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_expand.py0000644000175000017500000000414312014170666026063 0ustar georgeskgeorgeskfrom sympy import Symbol, log, sqrt, Rational as R from sympy.utilities.pytest import raises from sympy.abc import x, y def test_expand_no_log(): x = Symbol('x') assert ((1+log(x**4))**2).expand(log=False) == 1 + 2*log(x**4) + log(x**4)**2 assert ((1+log(x**4))*(1+log(x**3))).expand(log=False) == 1 + log(x**4) + log(x**3) + log(x**4)*log(x**3) def test_expand_no_multinomial(): x = Symbol('x') assert ((1+x)*(1+(1+x)**4)).expand(multinomial=False) == 1 + x + (1+x)**4 + x*(1+x)**4 def test_expand_negative_integer_powers(): x = Symbol('x') y = Symbol('y') expr = (x+y)**(-2) assert expr.expand() == 1 / (2*x*y + x**2 + y**2) assert expr.expand(multinomial=False) == (x+y)**(-2) expr = (x+y)**(-3) assert expr.expand() == 1 / (3*x*x*y + 3*x*y*y + x**3 + y**3) assert expr.expand(multinomial=False) == (x+y)**(-3) expr = (x+y)**(2) * (x+y)**(-4) assert expr.expand() == 1 / (2*x*y + x**2 + y**2) assert expr.expand(multinomial=False) == (x+y)**(-2) def test_expand_non_commutative_multinomial(): x = Symbol('x', commutative=False) y = Symbol('y', commutative=False) assert ((x+y)**2).expand() == x*y + y*x + x**2 + y**2 assert ((x+y)**3).expand() == x**2*y + y**2*x + x*y**2 + y*x**2 + x**3 + y**3 + x*y*x + y*x*y def test_expand_radicals(): a = (x + y)**R(1,2) assert (a**1).expand() == a assert (a**3).expand() == x*a + y*a assert (a**5).expand() == x**2*a + 2*x*y*a + y**2*a assert (1/a**1).expand() == 1/a assert (1/a**3).expand() == 1/(x*a + y*a) assert (1/a**5).expand() == 1/(x**2*a + 2*x*y*a + y**2*a) a = (x + y)**R(1,3) assert (a**1).expand() == a assert (a**2).expand() == a**2 assert (a**4).expand() == x*a + y*a assert (a**5).expand() == x*a**2 + y*a**2 assert (a**7).expand() == x**2*a + 2*x*y*a + y**2*a def test_expand_modulus(): assert ((x + y)**11).expand(modulus=11) == x**11 + y**11 assert ((x + sqrt(2)*y)**11).expand(modulus=11) == x**11 + 10*sqrt(2)*y**11 raises(ValueError, "((x + y)**11).expand(modulus=0)") raises(ValueError, "((x + y)**11).expand(modulus=x)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_numbers.py0000644000175000017500000006645012014170666026270 0ustar georgeskgeorgeskfrom sympy import (Rational, Symbol, Float, I, sqrt, oo, nan, pi, E, Integer, S, factorial, Catalan, EulerGamma, GoldenRatio, cos, exp, Number, zoo, log, Mul, Pow) from sympy.core.power import integer_nthroot from sympy.core.numbers import igcd, ilcm, igcdex, seterr, _intcache from sympy.utilities.pytest import raises from sympy import mpmath def test_integers_cache(): python_int = 2**65 + 3175259 while python_int in _intcache or hash(python_int) in _intcache: python_int += 1 sympy_int = Integer(python_int) assert python_int in _intcache assert hash(python_int) not in _intcache assert sympy_int not in _intcache sympy_int_int = Integer(sympy_int) assert python_int in _intcache assert hash(python_int) not in _intcache assert sympy_int_int not in _intcache sympy_hash_int = Integer(hash(python_int)) assert python_int in _intcache assert hash(python_int) in _intcache assert sympy_hash_int not in _intcache def test_seterr(): seterr(divide = True) raises(ValueError,"S.Zero/S.Zero") seterr(divide = False) S.Zero / S.Zero == S.NaN def test_mod(): x = Rational(1, 2) y = Rational(3, 4) z = Rational(5, 18043) assert x % x == 0 assert x % y == 1/S(2) assert x % z == 3/S(36086) assert y % x == 1/S(4) assert y % y == 0 assert y % z == 9/S(72172) assert z % x == 5/S(18043) assert z % y == 5/S(18043) assert z % z == 0 a = Float('2.6') #FIXME-py3k: TypeError: type Float doesn't define __round__ method assert round(a % Float('0.2'), 15) == 0.2 assert round(a % 2, 15) == 0.6 assert round(a % 0.5, 15) == 0.1 assert Rational(3,4) % Float(1.1) == 0.75 assert Float(1.5) % Rational(5, 4) == 0.25 assert Rational(5,4).__rmod__(Float('1.5')) == 0.25 # No rounding required since these numbers can be represented # exactly. assert Float('1.5').__rmod__(Float('2.75')) == Float('1.25') assert 2.75 % Float('1.5') == Float('1.25') a = Integer(7) b = Integer(4) assert type(a % b) == Integer assert a % b == Integer(3) assert Integer(1) % Rational(2, 3) == Rational(1, 3) assert Rational(7,5) % Integer(1) == Rational(2,5) assert Integer(2) % 1.5 == 0.5 assert Integer(3).__rmod__(Integer(10)) == Integer(1) assert Integer(10) % 4 == Integer(2) assert 15 % Integer(4) == Integer(3) def test_divmod(): assert divmod(S(12), S(8)) == (1, 4) assert divmod(-S(12), S(8)) == (-2, 4) assert divmod(S(0), S(1)) == (0, 0) raises(ZeroDivisionError, "divmod(S(0), S(0))") raises(ZeroDivisionError, "divmod(S(1), S(0))") def test_igcd(): assert igcd(0, 0) == 0 assert igcd(0, 1) == 1 assert igcd(1, 0) == 1 assert igcd(0, 7) == 7 assert igcd(7, 0) == 7 assert igcd(7, 1) == 1 assert igcd(1, 7) == 1 assert igcd(-1, 0) == 1 assert igcd(0, -1) == 1 assert igcd(-1, -1) == 1 assert igcd(-1, 7) == 1 assert igcd(7, -1) == 1 assert igcd(8, 2) == 2 assert igcd(4, 8) == 4 assert igcd(8, 16) == 8 assert igcd(7, -3) == 1 assert igcd(-7, 3) == 1 assert igcd(-7, -3) == 1 def test_ilcm(): assert ilcm(0, 0) == 0 assert ilcm(1, 0) == 0 assert ilcm(0, 1) == 0 assert ilcm(1, 1) == 1 assert ilcm(2, 1) == 2 assert ilcm(8, 2) == 8 assert ilcm(8, 6) == 24 assert ilcm(8, 7) == 56 def test_igcdex(): assert igcdex(2, 3) == (-1, 1, 1) assert igcdex(10, 12) == (-1, 1, 2) assert igcdex(100, 2004) == (-20, 1, 4) def _strictly_equal(a, b): return (a.p, a.q, type(a.p), type(a.q)) == \ (b.p, b.q, type(b.p), type(b.q)) def _test_rational_new(cls): """ Tests that are common between Integer and Rational. """ assert cls(0) is S.Zero assert cls(1) is S.One assert cls(-1) is S.NegativeOne # These look odd, but are similar to int(): assert cls(0.9) is S.Zero assert cls('1') is S.One assert cls(u'-1') is S.NegativeOne i = Integer(10) assert _strictly_equal(i, cls('10')) assert _strictly_equal(i, cls(u'10')) assert _strictly_equal(i, cls(10L)) assert _strictly_equal(i, cls(10.5)) assert _strictly_equal(i, cls(i)) raises(TypeError, "cls(Symbol('x'))") def test_Integer_new(): """ Test for Integer constructor """ _test_rational_new(Integer) raises(ValueError, 'Integer("10.5")') def test_Rational_new(): """" Test for Rational constructor """ _test_rational_new(Rational) n1 = Rational(1, 2) assert n1 == Rational(Integer(1), 2) assert n1 == Rational(Integer(1), Integer(2)) assert n1 == Rational(1, Integer(2)) assert n1 == Rational(Rational(1, 2)) assert n1 == Rational(1.2, 2) assert n1 == Rational('.5') assert 1 == Rational(n1, n1) assert Rational(3, 2) == Rational(Rational(1,2),Rational(1,3)) assert Rational(3, 1) == Rational(1,Rational(1,3)) n3_4 = Rational(3, 4) assert Rational('3/4') == n3_4 assert -Rational('-3/4') == n3_4 assert Rational('.76').limit_denominator(4) == n3_4 assert Rational(19, 25).limit_denominator(4) == n3_4 assert Rational('19/25').limit_denominator(4) == n3_4 raises(ValueError, "Rational('1/2 + 2/3')") # handle fractions.Fraction instances try: import fractions assert Rational(fractions.Fraction(1, 2)) == Rational(1, 2) except ImportError: pass def test_Number_new(): """" Test for Number constructor """ # Expected behavior on numbers and strings assert Number(1) is S.One assert Number(2).__class__ is Integer assert Number(-622).__class__ is Integer assert Number(5,3).__class__ is Rational assert Number(5.3).__class__ is Float assert Number('1') is S.One assert Number('2').__class__ is Integer assert Number('-622').__class__ is Integer assert Number('5/3').__class__ is Rational assert Number('5.3').__class__ is Float raises(ValueError, "Number('cos')") raises(TypeError, "Number(cos)") a = Rational(3,5) assert Number(a) is a # Check idempotence on Numbers def test_Rational_cmp(): n1 = Rational(1,4) n2 = Rational(1,3) n3 = Rational(2,4) n4 = Rational(2,-4) n5 = Rational(0) n6 = Rational(1) n7 = Rational(3) n8 = Rational(-3) assert n8n8 assert (n1+1)**n2 < 2 assert ((n1+n6)/n7) < 1 assert n4n1 assert not n3 0) assert Rational(-1) < 0 def test_Float(): def eq(a, b): t = Float("1.0E-15") return (-t < a-b < t) a = Float(2) ** Float(3) assert eq(a.evalf(), Float(8)) assert eq((pi ** -1).evalf(), Float("0.31830988618379067")) a = Float(2) ** Float(4) assert eq(a.evalf(), Float(16)) assert (S(.3) == S(.5)) is False x_str = Float((0, '13333333333333', -52, 53)) x2_str = Float((0, '26666666666666', -53, 53)) x_hex = Float((0, 0x13333333333333L, -52, 53)) x_dec = Float((0, 5404319552844595L, -52, 53)) x2_hex = Float((0, 0x13333333333333L*2, -53, 53)) assert x_str == x_hex == x_dec == x2_hex == Float(1.2) # x2_str and 1.2 are superficially the same assert str(x2_str) == str(Float(1.2)) # but are different at the mpf level assert Float(1.2)._mpf_ == (0, 5404319552844595L, -52, 53) assert x2_str._mpf_ == (0, 10808639105689190L, -53, 53) # do not automatically evalf def teq(a): assert (a.evalf () == a) is False assert (a.evalf () != a) is True assert (a == a.evalf()) is False assert (a != a.evalf()) is True teq(pi) teq(2*pi) teq(cos(0.1, evaluate=False)) assert Float(1) is S.One assert Float(0) is S.Zero def test_Float_eval(): a = Float(3.2) assert (a**2).is_Float def test_Infinity(): assert oo != 1 assert 1*oo == oo assert 1 != oo assert oo != -oo assert oo != Symbol("x")**3 assert oo + 1 == oo assert 2 + oo == oo assert 3*oo + 2 == oo assert -oo*3 == -oo assert oo + oo == oo assert -oo + oo*(-5) == -oo assert 1/oo == 0 assert 1/(-oo) == 0 assert 8/oo == 0 assert oo % 2 == nan assert 2 % oo == nan def test_Infinity_2(): x = Symbol('x') assert oo*x != oo assert oo*(pi-1) == oo assert oo*(1-pi) == -oo assert (-oo)*x != -oo assert (-oo)*(pi-1) == -oo assert (-oo)*(1-pi) == oo def test_Infinity_inequations(): assert oo > pi assert not (oo < pi) assert exp(-3) < oo def test_NaN(): assert nan == nan assert nan != 1 assert 1*nan == nan assert 1 != nan assert nan == -nan assert oo != Symbol("x")**3 assert nan + 1 == nan assert 2 + nan == nan assert 3*nan + 2 == nan assert -nan*3 == nan assert nan + nan == nan assert -nan + nan*(-5) == nan assert 1/nan == nan assert 1/(-nan) == nan assert 8/nan == nan def test_powers(): assert integer_nthroot(1, 2) == (1, True) assert integer_nthroot(1, 5) == (1, True) assert integer_nthroot(2, 1) == (2, True) assert integer_nthroot(2, 2) == (1, False) assert integer_nthroot(2, 5) == (1, False) assert integer_nthroot(4, 2) == (2, True) assert integer_nthroot(123**25, 25) == (123, True) assert integer_nthroot(123**25+1, 25) == (123, False) assert integer_nthroot(123**25-1, 25) == (122, False) assert integer_nthroot(1,1) == (1, True) assert integer_nthroot(0,1) == (0, True) assert integer_nthroot(0,3) == (0, True) assert integer_nthroot(10000, 1) == (10000, True) assert integer_nthroot(4,2) == (2, True) assert integer_nthroot(16,2) == (4, True) assert integer_nthroot(26,2) == (5, False) assert integer_nthroot(1234567**7, 7) == (1234567, True) assert integer_nthroot(1234567**7+1, 7) == (1234567, False) assert integer_nthroot(1234567**7-1, 7) == (1234566, False) b = 25**1000 assert integer_nthroot(b, 1000) == (25, True) assert integer_nthroot(b+1, 1000) == (25, False) assert integer_nthroot(b-1, 1000) == (24, False) c = 10**400 c2 = c**2 assert integer_nthroot(c2, 2) == (c, True) assert integer_nthroot(c2+1, 2) == (c, False) assert integer_nthroot(c2-1, 2) == (c-1, False) assert integer_nthroot(2,10**10) == (1, False) p, r = integer_nthroot(int(factorial(10000)), 100) assert p % (10**10) == 5322420655 assert not r # Test that this is fast assert integer_nthroot(2,10**10) == (1, False) def test_powers_Integer(): """Test Integer._eval_power""" # check infinity assert S(1) ** S.Infinity == 1 assert S(-1)** S.Infinity == S.NaN assert S(2) ** S.Infinity == S.Infinity assert S(-2)** S.Infinity == S.Infinity + S.Infinity * S.ImaginaryUnit assert S(0) ** S.Infinity == 0 # check Nan assert S(1) ** S.NaN == S.NaN assert S(-1) ** S.NaN == S.NaN # check for exact roots assert S(-1) ** Rational(6, 5) == - (-1)**(S(1)/5) assert S(4) ** Rational(1, 2) == 2 assert S(-4) ** Rational(1, 2) == I * 2 assert S(16) ** Rational(1, 4) == 2 assert S(-16) ** Rational(1, 4) == 2 * (-1)**Rational(1,4) assert S(9) ** Rational(3, 2) == 27 assert S(-9) ** Rational(3, 2) == -27*I assert S(27) ** Rational(2, 3) == 9 assert S(-27) ** Rational(2, 3) == 9 * (S(-1) ** Rational(2, 3)) assert (-2) ** Rational(-2, 1) == Rational(1, 4) # not exact roots assert (-3) ** (S(1)/2) == sqrt(-3) assert (3) ** (S(3)/2) == 3 * sqrt(3) assert (-3) ** (S(3)/2) == - 3 * sqrt(-3) assert (-3) ** (S(5)/2) == 9 * I * sqrt(3) assert (-3) ** (S(7)/2) == - I * 27 * sqrt(3) assert (2) ** (S(3)/2) == 2 * sqrt(2) assert (2) ** (S(-3)/2) == sqrt(2) / 4 assert (81) ** (S(2)/3) == 9 * (S(3) ** (S(2)/3)) assert (-81) ** (S(2)/3) == 9 * (S(-3) ** (S(2)/3)) assert (-3) ** Rational(-7, 3) == -(-1)**Rational(2, 3)*3**Rational(2, 3)/27 assert (-3) ** Rational(-2, 3) == -(-1)**Rational(1, 3)*3**Rational(1, 3)/3 # join roots assert sqrt(6) + sqrt(24) == 3*sqrt(6) assert sqrt(2) * sqrt(3) == sqrt(6) # separate symbols & constansts x = Symbol("x") assert sqrt(49 * x) == 7 * sqrt(x) assert sqrt((3 - sqrt(pi)) ** 2) == 3 - sqrt(pi) # check that it is fast for big numbers assert (2**64+1) ** Rational(4, 3) assert (2**64+1) ** Rational(17,25) # negative rational power and negative base assert (-3) ** Rational(-7, 3) == -(-1)**Rational(2, 3)*3**Rational(2, 3)/27 assert (-3) ** Rational(-2, 3) == -(-1)**Rational(1, 3)*3**Rational(1, 3)/3 assert S(1234).factors() == {617: 1, 2: 1} assert Rational(2*3, 3*5*7).factors() == {2: 1, 5: -1, 7: -1} # test that eval_power factors numbers bigger than limit (2**15) from sympy import nextprime n = nextprime(2**15) # bigger than the current limit in factor_trial_division assert sqrt(n**2) == n assert sqrt(n**3) == n*sqrt(n) assert sqrt(4*n) == 2*sqrt(n) # check that factors of base with powers sharing gcd with power are removed assert (2**4*3)**Rational(1, 6) == 2**Rational(2, 3)*3**Rational(1, 6) assert (2**4*3)**Rational(5, 6) == 8*2**Rational(1, 3)*3**Rational(5, 6) # check that bases sharing a gcd are exptracted assert 2**Rational(1, 3)*3**Rational(1, 4)*6**Rational(1, 5) == \ 2**Rational(8, 15)*3**Rational(9, 20) assert 8**Rational(1, 2)*24**Rational(1, 3)*6**Rational(1, 5) == \ 4*2**Rational(7, 10)*3**Rational(8, 15) assert 8**Rational(1, 2)*(-24)**Rational(1, 3)*(-6)**Rational(1, 5) == \ 4*(-3)**Rational(8, 15)*2**Rational(7, 10) assert 2**Rational(1, 3)*2**Rational(8, 9) == 2*2**Rational(2, 9) assert 2**Rational(2, 3)*6**Rational(1, 3) == 2*3**Rational(1, 3) assert 2**Rational(2, 3)*6**Rational(8, 9) == 2*2**Rational(5, 9)*3**Rational(8, 9) assert (-2)**Rational(2, S(3))*(-4)**Rational(1, S(3)) == -2*2**Rational(1, 3) assert 3*Pow(3, 2, evaluate=False) == 3**3 assert 3*Pow(3, -1/S(3), evaluate=False) == 3**(2/S(3)) assert (-2)**(1/S(3))*(-3)**(1/S(4))*(-5)**(5/S(6)) == \ -(-1)**Rational(5, 12)*2**Rational(1, 3)*3**Rational(1, 4)*5**Rational(5, 6) def test_powers_Rational(): """Test Rational._eval_power""" # check infinity assert Rational(1,2) ** S.Infinity == 0 assert Rational(3,2) ** S.Infinity == S.Infinity assert Rational(-1,2) ** S.Infinity == 0 assert Rational(-3,2)** S.Infinity == S.Infinity + S.Infinity * S.ImaginaryUnit # check Nan assert Rational(3,4) ** S.NaN == S.NaN assert Rational(-2,3) ** S.NaN == S.NaN # exact roots on numerator assert Rational(4,3) ** Rational(1,2) == 2 * sqrt(3) / 3 assert Rational(4,3) ** Rational(3,2) == 8 * sqrt(3) / 9 assert Rational(-4,3) ** Rational(1,2) == I * 2 * sqrt(3) / 3 assert Rational(-4,3) ** Rational(3,2) == - I * 8 * sqrt(3) / 9 assert Rational(27,2) ** Rational(1,3) == 3 * (2 ** Rational(2,3)) / 2 assert Rational(5**3, 8**3) ** Rational(4,3) == Rational(5**4, 8**4) # exact root on denominator assert Rational(1,4) ** Rational(1,2) == Rational(1,2) assert Rational(1,-4) ** Rational(1,2) == I * Rational(1,2) assert Rational(3,4) ** Rational(1,2) == sqrt(3) / 2 assert Rational(3,-4) ** Rational(1,2) == I * sqrt(3) / 2 assert Rational(5,27) ** Rational(1,3) == (5 ** Rational(1,3)) / 3 # not exact roots assert Rational(1,2) ** Rational(1,2) == sqrt(2) / 2 assert Rational(-4,7) ** Rational(1,2) == I * Rational(4,7) ** Rational(1,2) assert Rational(-3, 2)**Rational(-7, 3) == \ -4*(-1)**Rational(2, 3)*2**Rational(1, 3)*3**Rational(2, 3)/27 assert Rational(-3, 2)**Rational(-2, 3) == \ -(-1)**Rational(1, 3)*2**Rational(2, 3)*3**Rational(1, 3)/3 # negative integer power and negative rational base assert Rational(-2, 3) ** Rational(-2, 1) == Rational(9, 4) def test_abs1(): assert Rational(1,6) != Rational(-1,6) assert abs(Rational(1,6)) == abs(Rational(-1,6)) def test_accept_int(): assert Float(4) == 4 def test_dont_accept_str(): assert Float("0.2") != "0.2" assert not (Float("0.2") == "0.2") def test_int(): a = Rational(5) assert int(a) == 5 a = Rational(9, 10) assert int(a) == int(-a) == 0 assert 1/(-1)**Rational(2, 3) == -(-1)**Rational(1, 3) assert int(pi) == 3 assert int(E) == 2 assert int(GoldenRatio) == 1 def test_real_bug(): x = Symbol("x") assert str(2.0*x*x) in ["(2.0*x)*x","2.0*x**2","2.00000000000000*x**2"] assert str(2.1*x*x)!="(2.0*x)*x" def test_bug_sqrt(): assert ((sqrt(Rational(2))+1)*(sqrt(Rational(2))-1)).expand() == 1 def test_pi_Pi(): "Test, that pi (instance) is imported, but Pi (class) is not" from sympy import pi raises(ImportError, "from sympy import Pi") def test_no_len(): # there should be no len for numbers raises(TypeError, "len(Rational(2))") raises(TypeError, "len(Rational(2,3))") raises(TypeError, "len(Integer(2))") def test_issue222(): assert sqrt(Rational(1, 5)) == Rational(1, 5)**S.Half assert 5 * Rational(1,5)**Rational(1,2) == 5 * sqrt(Rational(1,5)) def test_issue593(): assert ((-1)**Rational(1,6)).expand(complex=True) == I/2 + sqrt(3)/2 assert ((-5)**Rational(1,6)).expand(complex=True) == \ 5**Rational(1,6)*I/2 + 5**Rational(1,6)*sqrt(3)/2 assert ((-64)**Rational(1,6)).expand(complex=True) == I + sqrt(3) def test_issue324(): x = Symbol("x") assert sqrt(x-1) == (x-1)**Rational(1,2) assert sqrt(x-1) != I*(1-x)**Rational(1,2) def test_issue350(): x = Symbol("x", real=True) assert sqrt(x**2) == abs(x) assert sqrt(x-1).subs(x,5) == 2 def test_Integer_factors(): def F(i): return Integer(i).factors() assert F(1) == { 1:1} assert F(2) == { 2:1} assert F(3) == { 3:1} assert F(4) == { 2:2} assert F(5) == { 5:1} assert F(6) == { 2:1, 3:1} assert F(7) == { 7:1} assert F(8) == { 2:3} assert F(9) == { 3:2} assert F(10) == { 2:1, 5:1} assert F(11) == {11:1} assert F(12) == { 2:2, 3:1} assert F(13) == {13:1} assert F(14) == { 2:1, 7:1} assert F(15) == { 3:1, 5:1} assert F(16) == { 2:4} assert F(17) == {17:1} assert F(18) == { 2:1, 3:2} assert F(19) == {19:1} assert F(20) == { 2:2, 5:1} assert F(21) == { 3:1, 7:1} assert F(22) == { 2:1, 11:1} assert F(23) == {23:1} assert F(24) == { 2:3, 3:1} assert F(25) == { 5:2} assert F(26) == { 2:1, 13:1} assert F(27) == { 3:3} assert F(28) == { 2:2, 7:1} assert F(29) == {29:1} assert F(30) == { 2:1, 3:1, 5:1} assert F(31) == {31:1} assert F(32) == { 2:5} assert F(33) == { 3:1, 11:1} assert F(34) == { 2:1, 17:1} assert F(35) == { 5:1, 7:1} assert F(36) == { 2:2, 3:2} assert F(37) == {37:1} assert F(38) == { 2:1, 19:1} assert F(39) == { 3:1, 13:1} assert F(40) == { 2:3, 5:1} assert F(41) == {41:1} assert F(42) == { 2:1, 3:1, 7:1} assert F(43) == {43:1} assert F(44) == { 2:2, 11:1} assert F(45) == { 3:2, 5:1} assert F(46) == { 2:1, 23:1} assert F(47) == {47:1} assert F(48) == { 2:4, 3:1} assert F(49) == { 7:2} assert F(50) == { 2:1, 5:2} assert F(51) == { 3:1, 17:1} def test_Rational_factors(): def F(p,q): return Rational(p,q).factors() assert F(2,3) == { 2:1, 3:-1} assert F(2,9) == { 2:1, 3:-2} assert F(2,15) == { 2:1, 3:-1, 5:-1} assert F(6,10) == { 3:1, 5:-1} # TODO write more Rational.factor() tests def test_issue1008(): assert pi*(E + 10) + pi*(-E - 10) != 0 assert pi*(E + 10**10) + pi*(-E - 10**10) != 0 assert pi*(E + 10**20) + pi*(-E - 10**20) != 0 assert pi*(E + 10**80) + pi*(-E - 10**80) != 0 assert (pi*(E + 10) + pi*(-E - 10)).expand() == 0 assert (pi*(E + 10**10) + pi*(-E - 10**10)).expand() == 0 assert (pi*(E + 10**20) + pi*(-E - 10**20)).expand() == 0 assert (pi*(E + 10**80) + pi*(-E - 10**80)).expand() == 0 def test_IntegerInteger(): a = Integer(4) b = Integer(a) assert a == b def test_Integer_methods(): assert Integer(0).factorial() == Integer(1) assert Integer(1).factorial() == Integer(1) assert Integer(10).factorial() == Integer(3628800) assert Integer(100).isqrt() == Integer(10) assert Integer(110).isqrt() == Integer(10) assert Integer(121).isqrt() == Integer(11) assert Integer(100).half_gcdex(2004) == \ (Integer(-20), Integer(4)) assert Integer(100).half_gcdex(Integer(2004)) == \ (Integer(-20), Integer(4)) assert Integer(100).gcdex(2004) == \ (Integer(-20), Integer(1), Integer(4)) assert Integer(100).gcdex(Integer(2004)) == \ (Integer(-20), Integer(1), Integer(4)) raises(ValueError, "Integer(3).half_gcdex(Rational(1,2))") raises(ValueError, "Integer(3).gcdex(Rational(1,2))") assert Integer(3).invert(7) == Integer(5) assert Integer(3).invert(Integer(7)) == Integer(5) def test_Rational_gcd_lcm_cofactors(): assert Integer(4).gcd(2) == Integer(2) assert Integer(4).lcm(2) == Integer(4) assert Integer(4).gcd(Integer(2)) == Integer(2) assert Integer(4).lcm(Integer(2)) == Integer(4) assert Integer(4).gcd(3) == Integer(1) assert Integer(4).lcm(3) == Integer(12) assert Integer(4).gcd(Integer(3)) == Integer(1) assert Integer(4).lcm(Integer(3)) == Integer(12) assert Rational(4,3).gcd(2) == Rational(2,3) assert Rational(4,3).lcm(2) == Integer(4) assert Rational(4,3).gcd(Integer(2)) == Rational(2,3) assert Rational(4,3).lcm(Integer(2)) == Integer(4) assert Integer(4).gcd(Rational(2,9)) == Rational(2,9) assert Integer(4).lcm(Rational(2,9)) == Integer(4) assert Rational(4,3).gcd(Rational(2,9)) == Rational(2,9) assert Rational(4,3).lcm(Rational(2,9)) == Rational(4,3) assert Rational(4,5).gcd(Rational(2,9)) == Rational(2,45) assert Rational(4,5).lcm(Rational(2,9)) == Integer(4) assert Integer(4).cofactors(2) == (Integer(2), Integer(2), Integer(1)) assert Integer(4).cofactors(Integer(2)) == (Integer(2), Integer(2), Integer(1)) assert Integer(4).gcd(Float(2.0)) == S.One assert Integer(4).lcm(Float(2.0)) == Float(8.0) assert Integer(4).cofactors(Float(2.0)) == (S.One, Integer(4), Float(2.0)) assert Rational(1,2).gcd(Float(2.0)) == S.One assert Rational(1,2).lcm(Float(2.0)) == Float(1.0) assert Rational(1,2).cofactors(Float(2.0)) == (S.One, Rational(1,2), Float(2.0)) def test_Float_gcd_lcm_cofactors(): assert Float(2.0).gcd(Integer(4)) == S.One assert Float(2.0).lcm(Integer(4)) == Float(8.0) assert Float(2.0).cofactors(Integer(4)) == (S.One, Float(2.0), Integer(4)) assert Float(2.0).gcd(Rational(1,2)) == S.One assert Float(2.0).lcm(Rational(1,2)) == Float(1.0) assert Float(2.0).cofactors(Rational(1,2)) == (S.One, Float(2.0), Rational(1,2)) def test_issue1512(): assert abs(pi._evalf(50) - 3.14159265358979) < 1e-10 assert abs(E._evalf(50) - 2.71828182845905) < 1e-10 assert abs(Catalan._evalf(50) - 0.915965594177219) < 1e-10 assert abs(EulerGamma._evalf(50) - 0.577215664901533) < 1e-10 assert abs(GoldenRatio._evalf(50) - 1.61803398874989) < 1e-10 x = Symbol("x") assert (pi+x).evalf() == pi.evalf()+x assert (E+x).evalf() == E.evalf()+x assert (Catalan+x).evalf() == Catalan.evalf()+x assert (EulerGamma+x).evalf() == EulerGamma.evalf()+x assert (GoldenRatio+x).evalf() == GoldenRatio.evalf()+x def test_conversion_to_mpmath(): assert mpmath.mpmathify(Integer(1)) == mpmath.mpf(1) assert mpmath.mpmathify(Rational(1, 2)) == mpmath.mpf(0.5) assert mpmath.mpmathify(Float('1.23')) == mpmath.mpf('1.23') def test_relational(): # real x = S(.1) assert (x != cos) is True assert (x == cos) is False # rational x = Rational(1,3) assert (x != cos) is True assert (x == cos) is False # integer defers to rational so these tests are omitted # number symbol x = pi assert (x != cos) is True assert (x == cos) is False def test_Integer_as_index(): if hasattr(int, '__index__'): # Python 2.5+ (PEP 357) assert 'hello'[Integer(2):] == 'llo' def test_Rational_int(): assert int( Rational(7, 5)) == 1 assert int( Rational(1, 2)) == 0 assert int(-Rational(1, 2)) == 0 assert int(-Rational(7, 5)) == -1 def test_zoo(): b = Symbol('b', bounded=True) nz = Symbol('nz', nonzero=True) p = Symbol('p', positive=True) n = Symbol('n', negative=True) im = Symbol('i', imaginary=True) c = Symbol('c', complex=True) pb = Symbol('pb', positive=True, bounded=True) nb = Symbol('nb', negative=True, bounded=True) imb = Symbol('ib', imaginary=True, bounded=True) for i in [I, S.Infinity, S.NegativeInfinity, S.Zero, S.One, S.Pi, S.Half, S(3), log(3), b, nz, p, n, im, pb, nb, imb, c]: if i.is_bounded and (i.is_real or i.is_imaginary): assert i + zoo is zoo assert i - zoo is zoo assert zoo + i is zoo assert zoo - i is zoo elif i.is_bounded is not False: assert (i + zoo).is_Add assert (i - zoo).is_Add assert (zoo + i).is_Add assert (zoo - i).is_Add else: assert (i + zoo) is S.NaN assert (i - zoo) is S.NaN assert (zoo + i) is S.NaN assert (zoo - i) is S.NaN if i.is_nonzero and (i.is_real or i.is_imaginary): assert i*zoo is zoo assert zoo*i is zoo elif i.is_zero: assert i*zoo is S.NaN assert zoo*i is S.NaN else: assert (i*zoo).is_Mul assert (zoo*i).is_Mul if (1/i).is_nonzero and (i.is_real or i.is_imaginary): assert zoo/i is zoo elif (1/i).is_zero: assert zoo/i is S.NaN else: assert (zoo/i).is_Mul assert (I*oo).is_Mul # allow directed infinity assert zoo + zoo is S.NaN assert zoo * zoo is S.NaN assert zoo - zoo is S.NaN assert zoo/zoo is S.NaN assert zoo**zoo is S.NaN assert zoo**0 is S.One assert zoo**2 is zoo assert 1/zoo is S.Zero assert Mul.flatten([S(-1), oo, S(0)]) == ([S.NaN], [], None) def test_issue_1023(): x = Symbol('x', nonpositive=True) assert (oo + x).is_Add x = Symbol('x', bounded=True) assert (oo + x).is_Add # x could be imaginary x = Symbol('x', finite=True) assert (oo + x).is_Add # x could be imaginary x = Symbol('x', infinitesimal=True) assert (oo + x).is_Add # x could be imaginary x = Symbol('x', nonnegative=True) assert oo + x == oo x = Symbol('x', bounded=True, real=True) assert oo + x == oo x = Symbol('x', finite=True, real=True) assert oo + x == oo x = Symbol('x', infinitesimal=True, real=True) assert oo + x == oo # similarily for negative infinity x = Symbol('x', nonnegative=True) assert (-oo + x).is_Add x = Symbol('x', bounded=True) assert (-oo + x).is_Add x = Symbol('x', finite=True) assert (-oo + x).is_Add x = Symbol('x', infinitesimal=True) assert (-oo + x).is_Add x = Symbol('x', nonpositive=True) assert -oo + x == -oo x = Symbol('x', bounded=True, real=True) assert -oo + x == -oo x = Symbol('x', finite=True, real=True) assert -oo + x == -oo x = Symbol('x', infinitesimal=True, real=True) assert -oo + x == -oo wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_arit.py0000644000175000017500000010300412014170666025537 0ustar georgeskgeorgeskfrom __future__ import division from sympy import Symbol, sin, cos, exp, O, sqrt, Rational, Float, re, pi, \ sympify, sqrt, Add, Mul, Pow, I, log, S from sympy.utilities.pytest import XFAIL x = Symbol('x') y = Symbol('y') z = Symbol('z') def test_bug1(): assert re(x) != x x.series(x,0,1) assert re(x) != x a = Symbol("a") b = Symbol("b", positive=True) c = Symbol("c") def test_Symbol(): e=a*b assert e==a*b assert a*b*b==a*b**2 assert a*b*b+c==c+a*b**2 assert a*b*b-c==-c+a*b**2 def test_arit0(): p = Rational(5) e=a*b assert e == a*b e=a*b+b*a assert e == 2*a*b e=a*b+b*a+a*b+p*b*a assert e == 8*a*b e=a*b+b*a+a*b+p*b*a+a assert e == a+8*a*b e=a+a assert e == 2*a e=a+b+a assert e == b+2*a e=a+b*b+a+b*b assert e == 2*a+2*b**2 e=a+Rational(2)+b*b+a+b*b+p assert e == 7+2*a+2*b**2 e=(a+b*b+a+b*b)*p assert e == 5*(2*a+2*b**2) e=(a*b*c+c*b*a+b*a*c)*p assert e == 15*a*b*c e=(a*b*c+c*b*a+b*a*c)*p-Rational(15)*a*b*c assert e == Rational(0) e = Rational(50)*(a-a) assert e == Rational(0) e=b*a-b-a*b+b assert e == Rational(0) e=a*b+c**p assert e == a*b+c**5 e=a/b assert e == a*b**(-1) e=a*2*2 assert e == 4*a e=2+a*2/2 assert e == 2+a e=2-a-2 assert e == -a e=2*a*2 assert e == 4*a e=2/a/2 assert e == a**(-1) e=2**a**2 assert e == 2**(a**2) e = -(1+a) assert e == -1 -a e = Rational(1,2)*(1+a) assert e == Rational(1,2) + a/2 def test_div(): e=a/b assert e == a*b**(-1) e=a/b+c/2 assert e == a*b**(-1)+Rational(1)/2*c e=(1-b)/(b-1) assert e == (1+-b)*((-1)+b)**(-1) def test_pow(): n1 = Rational(1) n2 = Rational(2) n5 = Rational(5) e=a*a assert e == a**2 e=a*a*a assert e == a**3 e=a*a*a*a**Rational(6) assert e == a**9 e=a*a*a*a**Rational(6)-a**Rational(9) assert e == Rational(0) e=a**(b-b) assert e == Rational(1) e=(a-a)**b assert e == Rational(0) e=(a+Rational(1)-a)**b assert e == Rational(1) e=(a+b+c)**n2 assert e == (a+b+c)**2 assert e.expand() == 2*b*c+2*a*c+2*a*b+a**2+c**2+b**2 e=(a+b)**n2 assert e == (a+b)**2 assert e.expand() == 2*a*b+a**2+b**2 e=(a+b)**(n1/n2) assert e == (a+b)**(Rational(1)/2) assert e.expand() == (a+b)**(Rational(1)/2) n=n5**(n1/n2) assert n == Rational(5)**(Rational(1)/2) e=n*a*b-n*b*a assert e == Rational(0) e=n*a*b+n*b*a assert e == 2*a*b*5**(Rational(1)/2) assert e.diff(a) == 2*b*5**(Rational(1)/2) assert e.diff(a) == 2*b*5**(Rational(1)/2) e=a/b**2 assert e == a*b**(-2) assert sqrt(2*(1+sqrt(2))) == (2*(1+2**(Rational(1,2))))**(Rational(1,2)) x = Symbol('x') y = Symbol('y') assert ((x*y)**3).expand() == y**3 * x**3 assert ((x*y)**-3).expand() == y**-3 * x**-3 assert (x**5*(3*x)**(3)).expand() == 27 * x**8 assert (x**5*(-3*x)**(3)).expand() == -27 * x**8 assert (x**5*(3*x)**(-3)).expand() == Rational(1,27) * x**2 assert (x**5*(-3*x)**(-3)).expand() == -Rational(1,27) * x**2 # expand_power_exp assert (x**(y**(x+exp(x+y))+z)).expand(deep=False) == x**z*x**(y**(x + exp(x + y))) assert (x**(y**(x+exp(x+y))+z)).expand() == x**z*x**(y**x*y**(exp(x)*exp(y))) n = Symbol('k', even=False) k = Symbol('k', even=True) assert (-1)**x == (-1)**x assert (-1)**n == (-1)**n assert (-2)**k == 2**k assert (-1)**k == 1 def test_pow2(): # x**(2*y) is always (x**y)**2 but is only (x**2)**y if # x.is_positive or y.is_integer # let x = 1 to see why the following are not true. assert ((-x)**2)**Rational(1,3) != ((-x)**Rational(1,3))**2 assert (-x)**Rational(2,3) != x**Rational(2,3) assert (-x)**Rational(5,7) != -x**Rational(5,7) def test_pow_issue417(): assert 4**Rational(1, 4) == 2**Rational(1, 2) def test_pow3(): assert 2**(Rational(3)/2) == 2 * 2**Rational(1, 2) assert 2**(Rational(3)/2) == sqrt(8) def test_expand(): p = Rational(5) e = (a+b)*c assert e == c*(a+b) assert (e.expand()-a*c-b*c) == Rational(0) e=(a+b)*(a+b) assert e == (a+b)**2 assert e.expand() == 2*a*b+a**2+b**2 e=(a+b)*(a+b)**Rational(2) assert e == (a+b)**3 assert e.expand() == 3*b*a**2+3*a*b**2+a**3+b**3 assert e.expand() == 3*b*a**2+3*a*b**2+a**3+b**3 e=(a+b)*(a+c)*(b+c) assert e == (a+c)*(a+b)*(b+c) assert e.expand() == 2*a*b*c+b*a**2+c*a**2+b*c**2+a*c**2+c*b**2+a*b**2 e=(a+Rational(1))**p assert e == (1+a)**5 assert e.expand() == 1+5*a+10*a**2+10*a**3+5*a**4+a**5 e=(a+b+c)*(a+c+p) assert e == (5+a+c)*(a+b+c) assert e.expand() == 5*a+5*b+5*c+2*a*c+b*c+a*b+a**2+c**2 x=Symbol("x") s=exp(x*x)-1 e=s.nseries(x,0,3)/x**2 assert e.expand() == 1+x**2/2+O(x**4) e = (x*(y+z))**(x*(y+z))*(x+y) assert e.expand(power_exp=False, power_base=False) == x*(x*y + x*z)**(x*y + x*z) + y*(x*y + x*z)**(x*y + x*z) assert e.expand(power_exp=False, power_base=False, deep=False) == x*(x*(y + z))**(x*(y + z)) + y*(x*(y + z))**(x*(y + z)) e = (x*(y+z))**z assert e.expand(power_base=True, mul=True, deep=True) in [x**z*(y + z)**z, (x*y + x*z)**z] assert ((2*y)**z).expand() == 2**z*y**z p=Symbol('p', positive=True) assert sqrt(-x).expand().is_Pow assert sqrt(-x).expand(force=True) == I*sqrt(x) assert ((2*y*p)**z).expand() == 2**z*p**z*y**z assert ((2*y*p*x)**z).expand() == 2**z*p**z*(x*y)**z assert ((2*y*p*x)**z).expand(force=True) == 2**z*p**z*x**z*y**z assert ((2*y*p*-pi)**z).expand() == 2**z*pi**z*p**z*(-y)**z assert ((2*y*p*-pi*x)**z).expand() == 2**z*pi**z*p**z*(-x*y)**z n=Symbol('n', negative=True) m=Symbol('m', negative=True) assert ((-2*x*y*n)**z).expand() == 2**z*(-n)**z*(x*y)**z assert ((-2*x*y*n*m)**z).expand() == 2**z*(-m)**z*(-n)**z*(-x*y)**z # issue 2383 assert sqrt(-2*x*n) == sqrt(2)*sqrt(-n)*sqrt(x) # issue 2506 (2) assert (cos(x+y)**2).expand(trig=True) == \ sin(x)**2*sin(y)**2 - 2*sin(x)*sin(y)*cos(x)*cos(y) + cos(x)**2*cos(y)**2 # Check that this isn't too slow x = Symbol('x') W = 1 for i in range(1, 21): W = W * (x-i) W = W.expand() assert W.has(-1672280820*x**15) def test_power_expand(): """Test for Pow.expand()""" a = Symbol('a') b = Symbol('b') p = (a+b)**2 assert p.expand() == a**2 + b**2 + 2*a*b p = (1+2*(1+a))**2 assert p.expand() == 9 + 4*(a**2) + 12*a p = 2**(a+b) assert p.expand() == 2**a*2**b A = Symbol('A', commutative=False) B = Symbol('B', commutative=False) assert (2**(A+B)).expand() == 2**(A+B) assert (A**(a+b)).expand() != A**(a+b) def test_real_mul(): Float(0) * pi * x == Float(0) Float(1) * pi * x == pi * x len((Float(2) * pi * x).args) == 3 def test_ncmul(): A = Symbol("A", commutative=False) B = Symbol("B", commutative=False) C = Symbol("C", commutative=False) assert A*B != B*A assert A*B*C != C*B*A assert A*b*B*3*C == 3*b*A*B*C assert A*b*B*3*C != 3*b*B*A*C assert A*b*B*3*C == 3*A*B*C*b assert A+B == B+A assert (A+B)*C != C*(A+B) assert C*(A+B)*C != C*C*(A+B) assert (C*(A+B)).expand() == C*A+C*B assert (C*(A+B)).expand() != A*C+B*C assert A*A == A**2 assert (A+B)*(A+B) == (A+B)**2 assert ((A+B)**2).expand() == A**2 + A*B + B*A +B**2 assert A**-1 * A == 1 assert A/A == 1 assert A/(A**2) == 1/A assert A/(1+A) == A/(1+A) def test_ncpow(): x = Symbol('x', commutative=False) y = Symbol('y', commutative=False) z = Symbol('z', commutative=False) a = Symbol('a') b = Symbol('b') c = Symbol('c') assert (x**2)*(y**2) != (y**2)*(x**2) assert (x**-2)*y != y*(x**2) assert 2**x*2**y != 2**(x+y) assert 2**x*2**y*2**z != 2**(x+y+z) assert 2**x*2**(2*x) == 2**(3*x) assert 2**x*2**(2*x)*2**x == 2**(4*x) assert exp(x)*exp(y) != exp(y)*exp(x) assert exp(x)*exp(y)*exp(z) != exp(y)*exp(x)*exp(z) assert exp(x)*exp(y)*exp(z) != exp(x+y+z) assert x**a*x**b != x**(a+b) assert x**a*x**b*x**c != x**(a+b+c) assert x**3*x**4 == x**7 assert x**3*x**4*x**2 == x**9 assert x**a*x**(4*a) == x**(5*a) assert x**a*x**(4*a)*x**a == x**(6*a) def test_powerbug(): x=Symbol("x") assert x**1 != (-x)**1 assert x**2 == (-x)**2 assert x**3 != (-x)**3 assert x**4 == (-x)**4 assert x**5 != (-x)**5 assert x**6 == (-x)**6 assert x**128 == (-x)**128 assert x**129 != (-x)**129 assert (2*x)**2 == (-2*x)**2 def test_Mul_doesnt_expand_exp(): x = Symbol('x') y = Symbol('y') assert exp(x)*exp(y) == exp(x)*exp(y) assert 2**x*2**y == 2**x*2**y assert x**2*x**3 == x**5 assert 2**x*3**x == 6**x assert x**(y)*x**(2*y) == x**(3*y) assert 2**Rational(1,2)*2**Rational(1,2) == 2 assert 2**x*2**(2*x) == 2**(3*x) assert 2**Rational(1,2)*2**Rational(1,4)*5**Rational(3,4) == 10**Rational(3,4) assert (x**(-log(5)/log(3))*x)/(x*x**( - log(5)/log(3))) == sympify(1) def test_Add_Mul_is_integer(): x = Symbol('x') k = Symbol('k', integer=True) n = Symbol('n', integer=True) assert (2*k).is_integer == True assert (-k).is_integer == True assert (k/3).is_integer == False assert (x*k*n).is_integer == None assert (k+n).is_integer == True assert (k+x).is_integer == None assert (k+n*x).is_integer == None assert (k+n/3).is_integer == False def test_Add_Mul_is_bounded(): x = Symbol('x', real=True, bounded=False) assert sin(x).is_bounded == True assert (x*sin(x)).is_bounded == False assert (1024*sin(x)).is_bounded == True assert (sin(x)*exp(x)).is_bounded is not True assert (sin(x)*cos(x)).is_bounded == True assert (x*sin(x)*exp(x)).is_bounded is not True assert (sin(x)-67).is_bounded == True assert (sin(x)+exp(x)).is_bounded is not True def test_Mul_is_even_odd(): x = Symbol('x', integer=True) k = Symbol('k', odd=True) n = Symbol('n', odd=True) m = Symbol('m', even=True) assert (2*x).is_even == True assert (2*x).is_odd == False assert (3*x).is_even == None assert (3*x).is_odd == None assert (k/3).is_integer == False assert (k/3).is_even == False assert (k/3).is_odd == False assert (2*n).is_even == True assert (2*n).is_odd == False assert (2*m).is_even == True assert (2*m).is_odd == False assert (-n).is_even == False assert (-n).is_odd == True assert (k*n).is_even == False assert (k*n).is_odd == True assert (k*m).is_even == True assert (k*m).is_odd == False assert (k*n*m).is_even == True assert (k*n*m).is_odd == False assert (k*m*x).is_even == True assert (k*m*x).is_odd == False def test_Add_is_even_odd(): x = Symbol('x', integer=True) k = Symbol('k', odd=True) n = Symbol('n', even=True) assert (2+k).is_even == False assert (2+k).is_odd == True assert (7-k).is_even == True assert (7-k).is_odd == False assert (11-n).is_even == False assert (11-n).is_odd == True assert (-8+n).is_even == True assert (-8+n).is_odd == False assert (n+k).is_even == False assert (n+k).is_odd == True assert (n-k).is_even == False assert (n-k).is_odd == True assert (n+2*k).is_even == True assert (n+2*k).is_odd == False assert (k+n+x).is_odd == None assert (k+n-x).is_even == None assert (2*k+n*x).is_odd == None assert (2*k+n*x).is_even == None def test_Mul_is_negative_positive(): x = Symbol('x', real=True) y = Symbol('y', real=False) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert k.is_negative == True assert (-k).is_negative == False assert (2*k).is_negative == True assert (2*n)._eval_is_negative() == False assert (2*n).is_negative == False assert n.is_negative == False assert (-n).is_negative == True assert (2*n).is_negative == False assert (n*k).is_negative == True assert (2*n*k).is_negative == True assert (-n*k).is_negative == False assert (n*k*y).is_negative == False # y.is_real=F; !real -> !neg assert u.is_negative == False assert (-u).is_negative == None assert (2*u).is_negative == False assert v.is_negative == None assert (-v).is_negative == False assert (2*v).is_negative == None assert (u*v).is_negative == None assert (k*u).is_negative == None assert (k*v).is_negative == False assert (n*u).is_negative == False assert (n*v).is_negative == None assert (v*k*u).is_negative == False assert (v*n*u).is_negative == None assert (-v*k*u).is_negative == None assert (-v*n*u).is_negative == False assert (17*v*k*u).is_negative == False assert (17*v*n*u).is_negative == None assert (k*v*n*u).is_negative == False assert (x*k).is_negative == None assert (u*v*n*x*k).is_negative == None assert k.is_positive == False assert (-k).is_positive == True assert (2*k).is_positive == False assert n.is_positive == True assert (-n).is_positive == False assert (2*n).is_positive == True assert (n*k).is_positive == False assert (2*n*k).is_positive == False assert (-n*k).is_positive == True assert (-n*k*y).is_positive == False # y.is_real=F; !real -> !neg assert u.is_positive == None assert (-u).is_positive == False assert (2*u).is_positive == None assert v.is_positive == False assert (-v).is_positive == None assert (2*v).is_positive == False assert (u*v).is_positive == False assert (k*u).is_positive == False assert (k*v).is_positive == None assert (n*u).is_positive == None assert (n*v).is_positive == False assert (v*k*u).is_positive == None assert (v*n*u).is_positive == False assert (-v*k*u).is_positive == False assert (-v*n*u).is_positive == None assert (17*v*k*u).is_positive == None assert (17*v*n*u).is_positive == False assert (k*v*n*u).is_positive == None assert (x*k).is_positive == None assert (u*v*n*x*k).is_positive == None def test_Mul_is_negative_positive_2(): a = Symbol('a', nonnegative=True) b = Symbol('b', nonnegative=True) c = Symbol('c', nonpositive=True) d = Symbol('d', nonpositive=True) assert (a*b).is_nonnegative == True assert (a*b).is_negative == False assert (a*b).is_zero == None assert (a*b).is_positive == None assert (c*d).is_nonnegative == True assert (c*d).is_negative == False assert (c*d).is_zero == None assert (c*d).is_positive == None assert (a*c).is_nonpositive == True assert (a*c).is_positive == False assert (a*c).is_zero == None assert (a*c).is_negative == None def test_Mul_is_nonpositive_nonnegative(): x = Symbol('x', real=True) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert k.is_nonpositive == True assert (-k).is_nonpositive == False assert (2*k).is_nonpositive == True assert n.is_nonpositive == False assert (-n).is_nonpositive == True assert (2*n).is_nonpositive == False assert (n*k).is_nonpositive == True assert (2*n*k).is_nonpositive == True assert (-n*k).is_nonpositive == False assert u.is_nonpositive == None assert (-u).is_nonpositive == True assert (2*u).is_nonpositive == None assert v.is_nonpositive == True assert (-v).is_nonpositive == None assert (2*v).is_nonpositive == True assert (u*v).is_nonpositive == True assert (k*u).is_nonpositive == True assert (k*v).is_nonpositive == None assert (n*u).is_nonpositive == None assert (n*v).is_nonpositive == True assert (v*k*u).is_nonpositive == None assert (v*n*u).is_nonpositive == True assert (-v*k*u).is_nonpositive == True assert (-v*n*u).is_nonpositive == None assert (17*v*k*u).is_nonpositive == None assert (17*v*n*u).is_nonpositive == True assert (k*v*n*u).is_nonpositive == None assert (x*k).is_nonpositive == None assert (u*v*n*x*k).is_nonpositive == None assert k.is_nonnegative == False assert (-k).is_nonnegative == True assert (2*k).is_nonnegative == False assert n.is_nonnegative == True assert (-n).is_nonnegative == False assert (2*n).is_nonnegative == True assert (n*k).is_nonnegative == False assert (2*n*k).is_nonnegative == False assert (-n*k).is_nonnegative == True assert u.is_nonnegative == True assert (-u).is_nonnegative == None assert (2*u).is_nonnegative == True assert v.is_nonnegative == None assert (-v).is_nonnegative == True assert (2*v).is_nonnegative == None assert (u*v).is_nonnegative == None assert (k*u).is_nonnegative == None assert (k*v).is_nonnegative == True assert (n*u).is_nonnegative == True assert (n*v).is_nonnegative == None assert (v*k*u).is_nonnegative == True assert (v*n*u).is_nonnegative == None assert (-v*k*u).is_nonnegative == None assert (-v*n*u).is_nonnegative == True assert (17*v*k*u).is_nonnegative == True assert (17*v*n*u).is_nonnegative == None assert (k*v*n*u).is_nonnegative == True assert (x*k).is_nonnegative == None assert (u*v*n*x*k).is_nonnegative == None def test_Add_is_even_odd(): x = Symbol('x', integer=True) k = Symbol('k', odd=True) n = Symbol('n', odd=True) m = Symbol('m', even=True) assert (k+7).is_even == True assert (k+7).is_odd == False assert (-k+7).is_even == True assert (-k+7).is_odd == False assert (k-12).is_even == False assert (k-12).is_odd == True assert (-k-12).is_even == False assert (-k-12).is_odd == True assert (k+n).is_even == True assert (k+n).is_odd == False assert (k+m).is_even == False assert (k+m).is_odd == True assert (k+n+m).is_even == True assert (k+n+m).is_odd == False assert (k+n+x+m).is_even == None assert (k+n+x+m).is_odd == None def test_Add_is_negative_positive(): x = Symbol('x', real=True) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert (k-2).is_negative == True assert (k+17).is_negative == None assert (-k-5).is_negative == None assert (-k+123).is_negative == False assert (k-n).is_negative == True assert (k+n).is_negative == None assert (-k-n).is_negative == None assert (-k+n).is_negative == False assert (k-n-2).is_negative == True assert (k+n+17).is_negative == None assert (-k-n-5).is_negative == None assert (-k+n+123).is_negative == False assert (-2*k+123*n+17).is_negative == False assert (k+u).is_negative == None assert (k+v).is_negative == True assert (n+u).is_negative == False assert (n+v).is_negative == None assert (u-v).is_negative == False assert (u+v).is_negative == None assert (-u-v).is_negative == None assert (-u+v).is_negative == None assert (u-v+n+2).is_negative == False assert (u+v+n+2).is_negative == None assert (-u-v+n+2).is_negative == None assert (-u+v+n+2).is_negative == None assert (k+x).is_negative == None assert (k+x-n).is_negative == None assert (k-2).is_positive == False assert (k+17).is_positive == None assert (-k-5).is_positive == None assert (-k+123).is_positive == True assert (k-n).is_positive == False assert (k+n).is_positive == None assert (-k-n).is_positive == None assert (-k+n).is_positive == True assert (k-n-2).is_positive == False assert (k+n+17).is_positive == None assert (-k-n-5).is_positive == None assert (-k+n+123).is_positive == True assert (-2*k+123*n+17).is_positive == True assert (k+u).is_positive == None assert (k+v).is_positive == False assert (n+u).is_positive == True assert (n+v).is_positive == None assert (u-v).is_positive == None assert (u+v).is_positive == None assert (-u-v).is_positive == None assert (-u+v).is_positive == False assert (u-v-n-2).is_positive == None assert (u+v-n-2).is_positive == None assert (-u-v-n-2).is_positive == None assert (-u+v-n-2).is_positive == False assert (n+x).is_positive == None assert (n+x-k).is_positive == None def test_Add_is_nonpositive_nonnegative(): x = Symbol('x', real=True) k = Symbol('k', negative=True) n = Symbol('n', positive=True) u = Symbol('u', nonnegative=True) v = Symbol('v', nonpositive=True) assert (u-2).is_nonpositive == None assert (u+17).is_nonpositive == False assert (-u-5).is_nonpositive == True assert (-u+123).is_nonpositive == None assert (u-v).is_nonpositive == None assert (u+v).is_nonpositive == None assert (-u-v).is_nonpositive == None assert (-u+v).is_nonpositive == True assert (u-v-2).is_nonpositive == None assert (u+v+17).is_nonpositive == None assert (-u-v-5).is_nonpositive == None assert (-u+v-123).is_nonpositive == True assert (-2*u+123*v-17).is_nonpositive == True assert (k+u).is_nonpositive == None assert (k+v).is_nonpositive == True assert (n+u).is_nonpositive == False assert (n+v).is_nonpositive == None assert (k-n).is_nonpositive == True assert (k+n).is_nonpositive == None assert (-k-n).is_nonpositive == None assert (-k+n).is_nonpositive == False assert (k-n+u+2).is_nonpositive == None assert (k+n+u+2).is_nonpositive == None assert (-k-n+u+2).is_nonpositive == None assert (-k+n+u+2).is_nonpositive == False assert (u+x).is_nonpositive == None assert (v-x-n).is_nonpositive == None assert (u-2).is_nonnegative == None assert (u+17).is_nonnegative == True assert (-u-5).is_nonnegative == False assert (-u+123).is_nonnegative == None assert (u-v).is_nonnegative == True assert (u+v).is_nonnegative == None assert (-u-v).is_nonnegative == None assert (-u+v).is_nonnegative == None assert (u-v+2).is_nonnegative == True assert (u+v+17).is_nonnegative == None assert (-u-v-5).is_nonnegative == None assert (-u+v-123).is_nonnegative == False assert (2*u-123*v+17).is_nonnegative == True assert (k+u).is_nonnegative == None assert (k+v).is_nonnegative == False assert (n+u).is_nonnegative == True assert (n+v).is_nonnegative == None assert (k-n).is_nonnegative == False assert (k+n).is_nonnegative == None assert (-k-n).is_nonnegative == None assert (-k+n).is_nonnegative == True assert (k-n-u-2).is_nonnegative == False assert (k+n-u-2).is_nonnegative == None assert (-k-n-u-2).is_nonnegative == None assert (-k+n-u-2).is_nonnegative == None assert (u-x).is_nonnegative == None assert (v+x+n).is_nonnegative == None def test_Pow_is_integer(): x = Symbol('x') k = Symbol('k', integer=True) n = Symbol('n', integer=True, nonnegative=True) m = Symbol('m', integer=True, positive=True) assert (k**2).is_integer == True assert (k**(-2)).is_integer == False assert (2**k).is_integer == None assert (2**(-k)).is_integer == None assert (2**n).is_integer == True assert (2**(-n)).is_integer == None assert (2**m).is_integer == True assert (2**(-m)).is_integer == False assert (x**2).is_integer == None assert (2**x).is_integer == None assert (k**n).is_integer == True assert (k**(-n)).is_integer == None assert (k**x).is_integer == None assert (x**k).is_integer == None assert (k**(n*m)).is_integer == True assert (k**(-n*m)).is_integer == None def test_Pow_is_real(): x = Symbol('x', real=True) y = Symbol('y', real=True, positive=True) assert (x**2).is_real == True assert (x**3).is_real == True assert (x**x).is_real == None assert (y**x).is_real == True assert (x**Rational(1,3)).is_real == None assert (y**Rational(1,3)).is_real == True assert sqrt(-1 - sqrt(2)).is_real == False i = Symbol('i', imaginary=True) assert (i**i).is_real is None assert (I**i).is_real is None assert ((-I)**i).is_real is None assert (2**i).is_real is None # (2**(pi/log(2) * I)) is real, 2**I is not assert (2**I).is_real is False assert (2**-I).is_real is False assert (i**2).is_real assert (i**3).is_real is False assert (i**x).is_real is None # could be (-I)**(2/3) e = Symbol('e', even=True) o = Symbol('o', odd=True) k = Symbol('k', integer=True) assert (i**e).is_real assert (i**o).is_real is False assert (i**k).is_real is None @XFAIL def test_real_Pow(): """ This test fails perhaps because (pi/log(x)).is_real is True even with no assumptions on x. See issue 2322. """ k = Symbol('k', integer=True, nonzero=True) assert (k**(I*pi/log(k))).is_real def test_Pow_is_bounded(): x = Symbol('x', real=True) p = Symbol('p', positive=True) n = Symbol('n', negative=True) assert (x**2).is_bounded == None # x could be oo assert (x**x).is_bounded == None # ditto assert (p**x).is_bounded == None # ditto assert (n**x).is_bounded == None # ditto assert (1/S.Pi).is_bounded assert (sin(x)**2).is_bounded == True assert (sin(x)**x).is_bounded == None assert (sin(x)**exp(x)).is_bounded == None assert (1/sin(x)).is_bounded == None # if zero, no, otherwise yes assert (1/exp(x)).is_bounded == None # x could be -oo def test_Pow_is_even_odd(): x = Symbol('x') k = Symbol('k', even=True) n = Symbol('n', odd=True) m = Symbol('m', integer=True, nonnegative=True) assert (k**2).is_even == True assert (n**2).is_even == False assert (2**k).is_even == None assert (x**2).is_even == None assert (k**m).is_even == True assert (n**m).is_even == False assert (k**x).is_even == None assert (n**x).is_even == None assert (k**2).is_odd == False assert (n**2).is_odd == True assert (3**k).is_odd == None assert (k**m).is_odd == False assert (n**m).is_odd == True assert (k**x).is_odd == None assert (n**x).is_odd == None def test_Pow_is_negative_positive(): x = Symbol('x', real=True) k = Symbol('k', integer=True, positive=True) n = Symbol('n', even=True) m = Symbol('m', odd=True) z = Symbol('z') assert (2**x).is_positive == True assert ((-2)**x).is_positive == None assert ((-2)**n).is_positive == True assert ((-2)**m).is_positive == False assert (k**2).is_positive == True assert (k**(-2)).is_positive == True assert (k**x).is_positive == True assert ((-k)**x).is_positive == None assert ((-k)**n).is_positive == True assert ((-k)**m).is_positive == False assert (2**x).is_negative == False assert ((-2)**x).is_negative == None assert ((-2)**n).is_negative == False assert ((-2)**m).is_negative == True assert (k**2).is_negative == False assert (k**(-2)).is_negative == False assert (k**x).is_negative == False assert ((-k)**x).is_negative == None assert ((-k)**n).is_negative == False assert ((-k)**m).is_negative == True assert (2**z).is_positive == None assert (2**z).is_negative == None def test_Pow_is_nonpositive_nonnegative(): x = Symbol('x', real=True) k = Symbol('k', integer=True, nonnegative=True) l = Symbol('l', integer=True, positive=True) n = Symbol('n', even=True) m = Symbol('m', odd=True) assert (2**x).is_nonnegative == True assert ((-2)**x).is_nonnegative == None assert ((-2)**n).is_nonnegative == True assert ((-2)**m).is_nonnegative == False assert (k**2).is_nonnegative == True assert (k**(-2)).is_nonnegative == True assert (k**x).is_nonnegative == None # NOTE (0**x).is_real = U assert (l**x).is_nonnegative == True assert (l**x).is_positive == True assert ((-k)**x).is_nonnegative == None assert ((-k)**n).is_nonnegative == True assert ((-k)**m).is_nonnegative == None assert (2**x).is_nonpositive == False assert ((-2)**x).is_nonpositive == None assert ((-2)**n).is_nonpositive == False assert ((-2)**m).is_nonpositive == True assert (k**2).is_nonpositive == None assert (k**(-2)).is_nonpositive == None assert (k**x).is_nonpositive == None assert ((-k)**x).is_nonpositive == None assert ((-k)**n).is_nonpositive == None assert ((-k)**m).is_nonpositive == True def test_Mul_is_imaginary_real(): r = Symbol('r', real=True) i = Symbol('i', imaginary=True) ii= Symbol('ii',imaginary=True) x = Symbol('x') assert I .is_imaginary == True assert I .is_real == False assert (-I) .is_imaginary == True assert (-I) .is_real == False assert (3*I) .is_imaginary == True assert (3*I) .is_real == False assert (I*I) .is_imaginary == False assert (I*I) .is_real == True assert (r*i) .is_imaginary == True assert (r*i) .is_real == False assert (x*i) .is_imaginary == None assert (x*i) .is_real == None assert (i*ii).is_imaginary == False assert (i*ii).is_real == True assert (r*i*ii).is_imaginary == False assert (r*i*ii).is_real == True def test_Add_is_comparable(): assert (x+y).is_comparable == False assert (x+1).is_comparable == False assert (Rational(1,3) - sqrt(8)).is_comparable == True def test_Mul_is_comparable(): assert (x*y).is_comparable == False assert (x*2).is_comparable == False assert (sqrt(2)*Rational(1,3)).is_comparable == True def test_Pow_is_comparable(): assert (x**y).is_comparable == False assert (x**2).is_comparable == False assert (Rational(1,3)**Rational(1,2)).is_comparable == True def test_Add_is_positive_2(): e = Rational(1,3) - sqrt(8) assert e.is_positive == False assert e.is_negative == True e = pi - 1 assert e.is_positive == True assert e.is_negative == False def test_Add_is_irrational(): i = Symbol('i', irrational=True) assert i.is_irrational == True assert i.is_rational == False assert (i+1).is_irrational == True assert (i+1).is_rational == False def test_issue432(): class MightyNumeric(tuple): def __rdiv__(self, other): return "something" def __rtruediv__(self, other): return "something" assert sympify(1)/MightyNumeric((1,2)) == "something" def test_issue432b(): class Foo: def __init__(self): self.field = 1.0 def __mul__(self, other): self.field = self.field * other def __rmul__(self, other): self.field = other * self.field f = Foo() x = Symbol("x") assert f*x == x*f def test_bug3(): a = Symbol("a") b = Symbol("b", positive=True) e = 2*a + b f = b + 2*a assert e == f def test_suppressed_evaluation(): a = Add(1,3,2,evaluate=False) b = Mul(1,3,2,evaluate=False) c = Pow(3,2,evaluate=False) assert a != 6 assert a.func is Add assert a.args == (1,3,2) assert b != 6 assert b.func is Mul assert b.args == (1,3,2) assert c != 9 assert c.func is Pow assert c.args == (3,2) def test_Add_as_coeff_mul(): # Issue 2425. These should all be (1, self) assert (x + 1).as_coeff_mul() == (1, (x + 1,) ) assert (x + 2).as_coeff_mul() == (1, (x + 2,) ) assert (x + 3).as_coeff_mul() == (1, (x + 3,) ) assert (x - 1).as_coeff_mul() == (1, (x - 1,) ) assert (x - 2).as_coeff_mul() == (1, (x - 2,) ) assert (x - 3).as_coeff_mul() == (1, (x - 3,) ) n = Symbol('n', integer=True) assert (n + 1).as_coeff_mul() == (1, (n + 1,) ) assert (n + 2).as_coeff_mul() == (1, (n + 2,) ) assert (n + 3).as_coeff_mul() == (1, (n + 3,) ) assert (n - 1).as_coeff_mul() == (1, (n - 1,) ) assert (n - 2).as_coeff_mul() == (1, (n - 2,) ) assert (n - 3).as_coeff_mul() == (1, (n - 3,) ) def test_Pow_as_coeff_mul_doesnt_expand(): assert exp(x + y).as_coeff_mul() == (1, (exp(x + y),)) assert exp(x + exp(x + y)) != exp(x + exp(x)*exp(y)) def test_issue415(): assert (S.Half)**S.Half * sqrt(6) == 2 * sqrt(3)/2 assert S(1)/2*sqrt(6)*sqrt(2) == sqrt(3) assert sqrt(6)/2*sqrt(2) == sqrt(3) assert sqrt(6)*sqrt(2)/2 == sqrt(3) def test_make_args(): assert Add.make_args(x) == (x,) assert Mul.make_args(x) == (x,) assert Add.make_args(x*y*z) == (x*y*z,) assert Mul.make_args(x*y*z) == (x*y*z).args assert Add.make_args(x+y+z) == (x+y+z).args assert Mul.make_args(x+y+z) == (x+y+z,) assert Add.make_args((x+y)**z) == ((x+y)**z,) assert Mul.make_args((x+y)**z) == ((x+y)**z,) def test_issue2027(): assert (-2)**x*(-3)**x != 6**x i = Symbol('i', integer=1) assert (-2)**i*(-3)**i == 6**i def test_Add_primitive(): (x + 2).primitive() == (1, x + 2) (3*x + 2).primitive() == (1, x + 2) (2*x + 2).primitive() == (2, x + 1) (3*x + 3).primitive() == (3, x + 1) (4*x + 8).primitive() == (4, x + 2) (3*x + 2*y).primitive() == (1, x + 2*y) (2*x + 2*y).primitive() == (2, x + y) (3*x + 3*y).primitive() == (3, x + y) (4*x + 8*y).primitive() == (4, x + 2*y) (3/x + 2*x*y*z**2).primitive() == (1, 1/x + 2*x*y*z**2) (2/x + 2*x*y*z**2).primitive() == (2, 1/x + x*y*z**2) (3/x + 3*x*y*z**2).primitive() == (3, 1/x + x*y*z**2) (4/x + 8*x*y*z**2).primitive() == (4, 1/x + 2*x*y*z**2) (2*x/3 + 4*y/9).primitive() == (2/9, 3*x + 2*y) (2*x/3 + 4.1*y).primitive() == (1, 2*x/3 + 4.1*y) def test_issue2361(): u = Mul(2, (1 + x), evaluate=False) assert 2 + u == 4 + 2*x n = Symbol('n', commutative=False) u = 2*(1 + n) assert u.is_Mul assert (2 + u).args == (S(2), u) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_functions.py0000644000175000017500000003256612014170666026626 0ustar georgeskgeorgeskfrom sympy import (Lambda, Symbol, Function, Derivative, Subs, sqrt, log, exp, Rational, Float, sin, cos, acos, diff, I, re, im, oo, zoo, nan, E, expand, pi, O, Sum, S, polygamma, loggamma, Tuple, Dummy) from sympy.utilities.pytest import XFAIL, raises from sympy.abc import x, y, n from sympy.core.function import PoleError from sympy.utilities.iterables import subsets, variations def test_f_expand_complex(): f = Function('f') x = Symbol('x', real=True) z = Symbol('z') assert f(x).expand(complex=True) == I*im(f(x)) + re(f(x)) assert exp(x).expand(complex=True) == exp(x) assert exp(I*x).expand(complex=True) == cos(x) + I*sin(x) assert exp(z).expand(complex=True) == cos(im(z))*exp(re(z)) + \ I*sin(im(z))*exp(re(z)) def test_bug1(): x = Symbol("x") w = Symbol("w") e = sqrt(-log(w)) assert e.subs(log(w),-x) == sqrt(x) e = sqrt(-5*log(w)) assert e.subs(log(w),-x) == sqrt(5*x) def test_general_function(): nu = Function('nu') x = Symbol("x") y = Symbol("y") e = nu(x) edx = e.diff(x) edy = e.diff(y) edxdx = e.diff(x).diff(x) edxdy = e.diff(x).diff(y) assert e == nu(x) assert edx != nu(x) assert edx == diff(nu(x), x) assert edy == 0 assert edxdx == diff(diff(nu(x), x), x) assert edxdy == 0 def test_derivative_subs_bug(): x = Symbol("x y") l = Function('l') n = Function('n') e = diff(n(x), x) assert e.subs(n(x), l(x)) != e assert e.subs(n(x), l(x)) == Derivative(l(x), x) assert e.subs(n(x), -l(x)) == Derivative(-l(x), x) assert e.subs(x, y) == Derivative(n(y), y) def test_derivative_subs_self_bug(): f = Function('f') d = diff(f(x), x) assert d.subs(d, y) == y def test_derivative_linearity(): x = Symbol("x") y = Symbol("y") n = Function('n') assert diff(-n(x), x) == -diff(n(x), x) assert diff(8*n(x), x) == 8*diff(n(x), x) assert diff(8*n(x), x) != 7*diff(n(x), x) assert diff(8*n(x)*x, x) == 8*n(x) + 8*x*diff(n(x), x) assert diff(8*n(x)*y*x, x) == 8*y*n(x) + 8*y*x*diff(n(x), x) def test_derivative_evaluate(): x = Symbol('x') assert Derivative(sin(x), x) != diff(sin(x), x) assert Derivative(sin(x), x).doit() == diff(sin(x), x) f = Function('f') assert Derivative(Derivative(f(x), x), x) == diff(f(x), x, x) assert Derivative(sin(x), x, 0) == sin(x) def test_diff_symbols(): x = Symbol('x') y = Symbol('y') z = Symbol('z') f = Function('f') assert diff(f(x, y, z), x, y, z) == Derivative(f(x, y, z), x, y, z) assert diff(f(x, y, z), x, x, x) == Derivative(f(x, y, z), x, x, x) assert diff(f(x, y, z), x, 3) == Derivative(f(x, y, z), x, 3) # issue 1929 assert [diff(-z + x/y, sym) for sym in (z, x, y)] == [-1, 1/y, -x/y**2] assert diff(f(x, y, z), x, y, z, 2) == Derivative(f(x, y, z), x, y, z, z) assert diff(f(x, y, z), x, y, z, 2, evaluate=False) == \ Derivative(f(x, y, z), x, y, z, z) assert Derivative(f(x, y, z), x, y, z)._eval_derivative(z) == \ Derivative(f(x, y, z), x, y, z, z) assert Derivative(Derivative(f(x, y, z), x), y)._eval_derivative(z) == \ Derivative(f(x, y, z), x, y, z) def test_Lambda(): e = Lambda(x, x**2) f = Function('f') assert e(4) == 16 assert e(x) == x**2 assert e(y) == y**2 assert Lambda(x, x**2) == Lambda(x, x**2) assert Lambda(x, x**2) == Lambda(y, y**2) assert Lambda(x, x**2) != Lambda(y, y**2+1) assert Lambda((x, y), x**y) == Lambda((y, x), y**x) assert Lambda((x, y), x**y) != Lambda((x, y), y**x) assert Lambda((x, y), x**y)(x, y) == x**y assert Lambda((x, y), x**y)(3, 3) == 3**3 assert Lambda((x, y), x**y)(x, 3) == x**3 assert Lambda((x, y), x**y)(3, y) == 3**y assert Lambda(x, f(x))(x) == f(x) assert Lambda(x, x**2)(e(x)) == x**4 assert e(e(x)) == x**4 assert Lambda((x, y), x+y).nargs == 2 z = Symbol('z') t = Symbol('t') p = x, y, z, t assert Lambda(p, t*(x+y+z))(*p) == t * (x + y + z) assert Lambda(x, 2*x) + Lambda(y, 2*y) == 2*Lambda(x, 2*x) assert Lambda(x, 2*x) not in [ Lambda(x, x) ] def test_IdentityFunction(): assert Lambda(x, x) is Lambda(y, y) is S.IdentityFunction assert Lambda(x, 2*x) is not S.IdentityFunction assert Lambda((x, y), x) is not S.IdentityFunction def test_Lambda_symbols(): assert Lambda(x, 2*x).free_symbols == set() assert Lambda(x, x*y).free_symbols == set([y]) def test_Lambda_arguments(): raises(TypeError, 'Lambda(x, 2*x)(x, y)') raises(TypeError, 'Lambda((x, y), x+y)(x)') def test_Lambda_equality(): assert Lambda(x, 2*x) != Lambda((x,y), 2*x) assert (Lambda(x, 2*x) == Lambda((x,y), 2*x)) is False assert Lambda((x, y), 2*x) == Lambda((x, y), 2*x) assert (Lambda((x, y), 2*x) != Lambda((x, y), 2*x)) is False assert Lambda(x, 2*x) != 2*x assert (Lambda(x, 2*x) == 2*x) is False def test_Subs(): x = Symbol('x') y = Symbol('y') z = Symbol('z') f = Function('f') assert Subs(f(x), x, 0).doit() == f(0) assert Subs(f(x**2), x**2, 0).doit() == f(0) assert Subs(f(x, y), (x, y), (0, 1)).doit() == f(0, 1) assert Subs(Subs(f(x, y), x, 0), y, 1).doit() == f(0, 1) raises(ValueError, 'Subs(f(x, y), (x, y), (0, 0, 1))') raises(ValueError, 'Subs(f(x, y), (x, x, y), (0, 0, 1))') assert len(Subs(f(x, y), (x, y), (0, 1)).variables) == 2 assert all([ isinstance(v, Dummy) for v in Subs(f(x, y), (x, y), (0, 1)).variables ]) assert Subs(f(x, y), (x, y), (0, 1)).point == Tuple(0, 1) assert Subs(f(x), x, 0) == Subs(f(y), y, 0) assert Subs(f(x, y), (x, y), (0, 1)) == Subs(f(x, y), (y, x), (1, 0)) assert Subs(f(x)*y, (x, y), (0, 1)) == Subs(f(y)*x, (y, x), (0, 1)) assert Subs(f(x)*y, (x, y), (1, 1)) == Subs(f(y)*x, (x, y), (1, 1)) assert Subs(f(x), x, 0).subs(x, 1) == Subs(f(x), x, 0) assert Subs(f(x), x, 0).subs(x, 1).doit() == f(0) assert Subs(f(x), x, y).subs(y, 0) == Subs(f(x), x, 0) assert Subs(y*f(x), x, y).subs(y, 2) == Subs(2*f(x), x, 2) assert (2 * Subs(f(x), x, 0)).subs(Subs(f(x), x, 0), y) == 2*y assert Subs(f(x), x, 0).free_symbols == set([]) assert Subs(f(x, y), x, z).free_symbols == set([y, z]) assert Subs(f(x).diff(x), x, 0).doit() == Subs(f(x).diff(x), x, 0) assert Subs(1+f(x).diff(x), x, 0).doit() == 1 + Subs(f(x).diff(x), x, 0) assert Subs(y*f(x, y).diff(x), (x, y), (0, 2)).doit() == \ 2*Subs(Derivative(f(x, 2), x), x, 0) assert Subs(y**2*f(x), x, 0).diff(y) == 2*y*f(0) e = Subs(y**2*f(x), x, y) assert e.diff(y) == e.doit().diff(y) == y**2*Derivative(f(y), y) + 2*y*f(y) assert Subs(f(x), x, 0) + Subs(f(x), x, 0) == 2*Subs(f(x), x, 0) e1 = Subs(z*f(x), x, 1) e2 = Subs(z*f(y), y, 1) assert e1 + e2 == 2*e1 assert e1.__hash__() == e2.__hash__() assert Subs(z*f(x+1), x, 1) not in [ e1, e2 ] @XFAIL def test_Subs2(): x = Symbol('x') f = Function('f') # this reflects a limitation of subs(), probably won't fix assert Subs(f(x), x**2, 0).doit() == f(sqrt(x)) def test_expand_function(): assert expand(x+y) == x + y assert expand(x+y, complex=True) == I*im(x) + I*im(y) + re(x) + re(y) assert expand((x + y)**11, modulus=11) == x**11 + y**11 def test_function_comparable(): x = Symbol('x') assert sin(x).is_comparable == False assert cos(x).is_comparable == False assert sin(Float('0.1')).is_comparable == True assert cos(Float('0.1')).is_comparable == True assert sin(E).is_comparable == True assert cos(E).is_comparable == True assert sin(Rational(1,3)).is_comparable == True assert cos(Rational(1,3)).is_comparable == True @XFAIL def test_function_comparable(): assert sin(oo).is_comparable == False assert sin(-oo).is_comparable == False assert sin(zoo).is_comparable == False assert sin(nan).is_comparable == False def test_deriv1(): # These all requre derivatives evaluated at a point (issue 1620) to work. # See issue 1525 f = Function('f') g = Function('g') x = Symbol('x') assert f(g(x)).diff(x) == Derivative(g(x), x)*Subs(Derivative(f(x), x), Tuple(x), Tuple(g(x))) assert f(2*x).diff(x) == 2*Subs(Derivative(f(x), x), Tuple(x), Tuple(2*x)) assert (f(x)**3).diff(x) == 3*f(x)**2*f(x).diff(x) assert (f(2*x)**3).diff(x) == 6*f(2*x)**2*Subs(Derivative(f(x), x), Tuple(x), Tuple(2*x)) assert f(2+x).diff(x) == Subs(Derivative(f(x), x), Tuple(x), Tuple(x + 2)) assert f(2+3*x).diff(x) == 3*Subs(Derivative(f(x), x), Tuple(x), Tuple(3*x + 2)) assert f(sin(x)).diff(x) == cos(x)*Subs(Derivative(f(x), x), Tuple(x), Tuple(sin(x))) assert f(3*sin(x)).diff(x) == 3*cos(x)*Subs(Derivative(f(x), x), Tuple(x), Tuple(3*sin(x))) def test_deriv2(): x = Symbol('x') assert (x**3).diff(x) == 3*x**2 assert (x**3).diff(x, evaluate=False) != 3*x**2 assert (x**3).diff(x, evaluate=False) == Derivative(x**3, x) assert diff(x**3, x) == 3*x**2 assert diff(x**3, x, evaluate=False) != 3*x**2 assert diff(x**3, x, evaluate=False) == Derivative(x**3, x) def test_func_deriv(): f = Function('f') x = Symbol('x') y = Symbol('y') assert f(x).diff(x) == Derivative(f(x), x) # issue 1435 assert f(x, y).diff(x, y) - f(x, y).diff(y, x) == 0 assert Derivative(f(x, y), x, y).args[1:] == (x, y) assert Derivative(f(x, y), y, x).args[1:] == (y, x) assert (Derivative(f(x, y), x, y) - Derivative(f(x, y), y, x)).doit() == 0 def test_suppressed_evaluation(): a = sin(0, evaluate=False) assert a != 0 assert a.func is sin assert a.args == (0,) def test_function_evalf(): def eq(a,b,eps): return abs(a-b) < eps assert eq(sin(1).evalf(15), Float("0.841470984807897"), 1e-13) assert eq(sin(2).evalf(25), Float("0.9092974268256816953960199",25), 1e-23) assert eq(sin(1+I).evalf(15), Float("1.29845758141598") + Float("0.634963914784736")*I, 1e-13) assert eq(exp(1+I).evalf(15), Float("1.46869393991588") + Float("2.28735528717884239")*I, 1e-13) assert eq(exp(-0.5+1.5*I).evalf(15), Float("0.0429042815937374") + Float("0.605011292285002")*I, 1e-13) assert eq(log(pi+sqrt(2)*I).evalf(15), Float("1.23699044022052") + Float("0.422985442737893")*I, 1e-13) assert eq(cos(100).evalf(15), Float("0.86231887228768"), 1e-13) def test_extensibility_eval(): class MyFunc(Function): @classmethod def eval(cls, *args): return (0,0,0) assert MyFunc(0) == (0,0,0) def test_function_non_commutative(): x = Symbol('x', commutative=False) f = Function('f') assert f(x).is_commutative == False assert sin(x).is_commutative == False assert exp(x).is_commutative == False assert log(x).is_commutative == False def test_function__eval_nseries(): x = Symbol('x') assert sin(x)._eval_nseries(x,2,None) == x + O(x**2) assert sin(x+1)._eval_nseries(x,2,None) == x*cos(1) + sin(1) + O(x**2) assert sin(pi*(1-x))._eval_nseries(x,2,None) == pi*x + O(x**2) assert acos(1-x**2)._eval_nseries(x,2,None) == sqrt(2)*x + O(x**2) assert polygamma(n,x+1)._eval_nseries(x,2,None) == \ polygamma(n,1) + polygamma(n+1,1)*x + O(x**2) raises(PoleError, 'sin(1/x)._eval_nseries(x,2,None)') raises(PoleError, 'acos(1-x)._eval_nseries(x,2,None)') raises(PoleError, 'acos(1+x)._eval_nseries(x,2,None)') assert loggamma(1/x)._eval_nseries(x,0,None) \ == log(x)/2 - log(x)/x - 1/x + O(1, x) l = Symbol('l') assert loggamma(log(1/x)).nseries(x,n=1,logx=y) == loggamma(-y) def test_doit(): n = Symbol('n', integer = True) f = Sum(2 * n * x, (n, 1, 3)) d = Derivative(f, x) assert d.doit() == 12 assert d.doit(deep = False) == Sum(2*n, (n, 1, 3)) def test_evalf_default(): from sympy.functions.special.gamma_functions import polygamma assert type(sin(4.0)) == Float assert type(re(sin(I + 1.0))) == Float assert type(im(sin(I + 1.0))) == Float assert type(sin(4)) == sin assert type(polygamma(2.0,4.0)) == Float assert type(sin(Rational(1,4))) == sin def test_issue2300(): args = [x, y, S(2), S.Half] def ok(a): """Return True if the input args for diff are ok""" if not a: return False if a[0].is_Symbol is False: return False s_at = [i for i in range(len(a)) if a[i].is_Symbol] n_at = [i for i in range(len(a)) if not a[i].is_Symbol] # every symbol is followed by symbol or int # every number is followed by a symbol return (all([a[i+1].is_Symbol or a[i+1].is_Integer for i in s_at if i+1 0 and dneg < 0 and npos > 0 and nneg < 0 # pos or neg integer eq=eqn(npos, dpos, 2);assert eq.is_Pow and eq.as_numer_denom() == (1, dpos**2) eq=eqn(npos, dneg, 2);assert eq.is_Pow and eq.as_numer_denom() == (1, dneg**2) eq=eqn(nneg, dpos, 2);assert eq.is_Pow and eq.as_numer_denom() == (1, dpos**2) eq=eqn(nneg, dneg, 2);assert eq.is_Pow and eq.as_numer_denom() == (1, dneg**2) eq=eqn(npos, dpos, -2);assert eq.is_Pow and eq.as_numer_denom() == (dpos**2, 1) eq=eqn(npos, dneg, -2);assert eq.is_Pow and eq.as_numer_denom() == (dneg**2, 1) eq=eqn(nneg, dpos, -2);assert eq.is_Pow and eq.as_numer_denom() == (dpos**2, 1) eq=eqn(nneg, dneg, -2);assert eq.is_Pow and eq.as_numer_denom() == (dneg**2, 1) # pos or neg rational pow = S.Half eq=eqn(npos, dpos, pow);assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) eq=eqn(npos, dneg, pow);assert eq.is_Pow and eq.as_numer_denom() == ((-npos)**pow, (-dneg)**pow) eq=eqn(nneg, dpos, pow);assert not eq.is_Pow or eq.as_numer_denom() == (nneg**pow, dpos**pow) eq=eqn(nneg, dneg, pow);assert eq.is_Pow and eq.as_numer_denom() == ((-nneg)**pow, (-dneg)**pow) eq=eqn(npos, dpos, -pow);assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, npos**pow) eq=eqn(npos, dneg, -pow);assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-npos)**pow) eq=eqn(nneg, dpos, -pow);assert not eq.is_Pow or eq.as_numer_denom() == (dpos**pow, nneg**pow) eq=eqn(nneg, dneg, -pow);assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) # unknown exponent pow = 2*any eq=eqn(npos, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (npos**pow, dpos**pow) eq=eqn(npos, dneg, pow) assert eq.is_Pow and eq.as_numer_denom() == (eq, 1) eq=eqn(nneg, dpos, pow) assert eq.is_Pow and eq.as_numer_denom() == (nneg**pow, dpos**pow) eq=eqn(nneg, dneg, pow) assert eq.is_Pow and eq.as_numer_denom() == ((-nneg)**pow, (-dneg)**pow) eq=eqn(npos, dpos, -pow) assert eq.as_numer_denom() == (dpos**pow, npos**pow) eq=eqn(npos, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == (1, eq.base**pow) eq=eqn(nneg, dpos, -pow) assert eq.is_Pow and eq.as_numer_denom() == (dpos**pow, nneg**pow) eq=eqn(nneg, dneg, -pow) assert eq.is_Pow and eq.as_numer_denom() == ((-dneg)**pow, (-nneg)**pow) x = Symbol('x') assert ((1/(1 + x/3))**(-S.One)).as_numer_denom() == (3 + x, 3) np = Symbol('np',positive=False) assert (((1 + x/np)**-2)**(-S.One)).as_numer_denom() == ((np + x)**2, np**2) def test_Pow_signs(): """Cf. issues 1496 and 2151""" x = Symbol('x') y = Symbol('y') n = Symbol('n', even=True) assert (3-y)**2 != (y-3)**2 assert (3-y)**n != (y-3)**n assert (-3+y-x)**2 != (3-y+x)**2 assert (y-3)**3 != -(3-y)**3 def test_power_with_noncommutative_mul_as_base(): x = Symbol('x', commutative=False) y = Symbol('y', commutative=False) assert not (x*y)**3 == x**3*y**3 assert (2*x*y)**3 == 8*(x*y)**3 def test_zero(): x = Symbol('x') y = Symbol('y') assert 0**x != 0 assert 0**(2*x) == 0**x assert (0**(2 - x)).as_base_exp() == (0, 2 - x) assert 0**(x - 2) != S.Infinity**(2 - x) assert 0**(2*x*y) == 0**(x*y) assert 0**(-2*x*y) == S.Infinity**(x*y) def test_pow_as_base_exp(): x = Symbol('x') assert (S.Infinity**(2 - x)).as_base_exp() == (S.Infinity, 2 - x) assert (S.Infinity**(x - 2)).as_base_exp() == (S.Infinity, x - 2) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_relational.py0000644000175000017500000000463612014170666026745 0ustar georgeskgeorgeskfrom sympy import symbols, oo from sympy.core.relational import Relational, Equality, StrictInequality, \ Rel, Eq, Lt, Le, Gt, Ge, Ne x,y,z = symbols('x,y,z') def test_rel_ne(): Relational(x, y, '!=') # this used to raise def test_rel_subs(): e = Relational(x, y, '==') e = e.subs(x,z) assert isinstance(e, Equality) assert e.lhs == z assert e.rhs == y e = Relational(x, y, '<') e = e.subs(x,z) assert isinstance(e, StrictInequality) assert e.lhs == z assert e.rhs == y e = Eq(x,0) assert e.subs(x,0) == True assert e.subs(x,1) == False def test_wrappers(): e = x+x**2 res = Relational(y, e, '==') assert Rel(y, x+x**2, '==') == res assert Eq(y, x+x**2) == res res = Relational(y, e, '<') assert Lt(y, x+x**2) == res res = Relational(y, e, '<=') assert Le(y, x+x**2) == res res = Relational(y, e, '>') assert Gt(y, x+x**2) == res res = Relational(y, e, '>=') assert Ge(y, x+x**2) == res res = Relational(y, e, '!=') assert Ne(y, x+x**2) == res def test_Eq(): assert Eq(x**2) == Eq(x**2, 0) assert Eq(x**2) != Eq(x**2, 1) def test_rel_Infinity(): assert (oo > oo) is False assert (oo > -oo) is True assert (oo > 1) is True assert (oo < oo) is False assert (oo < -oo) is False assert (oo < 1) is False assert (oo >= oo) is True assert (oo >= -oo) is True assert (oo >= 1) is True assert (oo <= oo) is True assert (oo <= -oo) is False assert (oo <= 1) is False assert (-oo > oo) is False assert (-oo > -oo) is False assert (-oo > 1) is False assert (-oo < oo) is True assert (-oo < -oo) is False assert (-oo < 1) is True assert (-oo >= oo) is False assert (-oo >= -oo) is True assert (-oo >= 1) is False assert (-oo <= oo) is True assert (-oo <= -oo) is True assert (-oo <= 1) is True def test_bool(): assert Eq(0,0) is True assert Eq(1,0) is False assert Ne(0,0) is False assert Ne(1,0) is True assert Lt(0,1) is True assert Lt(1,0) is False assert Le(0,1) is True assert Le(1,0) is False assert Le(0,0) is True assert Gt(1,0) is True assert Gt(0,1) is False assert Ge(1,0) is True assert Ge(0,1) is False assert Ge(1,1) is True def test_rich_cmp(): assert (xy) == Gt(x,y) assert (x>=y) == Ge(x,y) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_facts.py0000644000175000017500000003021712014170666025705 0ustar georgeskgeorgeskfrom sympy.core.facts import deduce_alpha_implications, apply_beta_to_alpha_route, \ rules_2prereq, split_rules_tt_tf_ft_ff, FactRules from sympy.core.logic import And from sympy.utilities.pytest import XFAIL, raises T = True F = False U = None def test_deduce_alpha_implications(): def D(i): I = deduce_alpha_implications(i) P = rules_2prereq(I) return I,P # transitivity I,P = D([('a','b'), ('b','c')]) assert I == {'a': ['b','c'], 'b': ['c']} assert P == {'b': ['a'], 'c': ['a', 'b']} # XXX a,b order unstable # see if the output does not contain repeated implications I,P = D([('a','b'), ('b','c'), ('b','c')]) assert I == {'a': ['b','c'], 'b': ['c']} assert P == {'b': ['a'], 'c': ['a', 'b']} # XXX a,b order unstable # see if it is tolerant to cycles assert D([('a','a'), ('a','a')]) == ({}, {}) assert D([('a','b'), ('b','a')]) == ({'a': ['b'], 'b': ['a']}, {'a': ['b'], 'b': ['a']}) # see if it catches inconsistency raises(ValueError, "D([('a','!a')])") raises(ValueError, "D([('a','b'), ('b','!a')])") raises(ValueError, "D([('a','b'), ('b','c'), ('b','na'), ('na','!a')])") # something related to real-world I,P = D([('rat','real'), ('int','rat')]) assert I == {'int': ['rat', 'real'], 'rat': ['real']} assert P == {'rat': ['int'], 'real': ['int', 'rat']} # XXX int,rat order unstable # TODO move me to appropriate place def test_apply_beta_to_alpha_route(): APPLY = apply_beta_to_alpha_route # indicates empty alpha-chain with attached beta-rule #bidx def Q(bidx): return ([],[bidx]) # x -> a &(a,b) -> x -- x -> a A = {'x': ['a']}; B = [ (And('a','b'), 'x') ] assert APPLY(A, B) == {'x': (['a'], []), 'a':Q(0), 'b':Q(0)} # x -> a &(a,!x) -> b -- x -> a A = {'x': ['a']}; B = [ (And('a','!x'), 'b') ] assert APPLY(A, B) == {'x': (['a'], []), '!x':Q(0), 'a':Q(0)} # x -> a b &(a,b) -> c -- x -> a b c A = {'x': ['a','b']}; B = [ (And('a','b'), 'c') ] assert APPLY(A, B) == {'x': (['a','b','c'], []), 'a':Q(0), 'b':Q(0)} # x -> a &(a,b) -> y -- x -> a [#0] A = {'x': ['a']}; B = [ (And('a','b'), 'y') ] assert APPLY(A, B) == {'x': (['a'], [0]), 'a':Q(0), 'b':Q(0)} # x -> a b c &(a,b) -> c -- x -> a b c A = {'x': ['a','b','c']} B = [ (And('a','b'), 'c') ] assert APPLY(A, B) == {'x': (['a','b','c'], []), 'a':Q(0), 'b':Q(0)} # x -> a b &(a,b,c) -> y -- x -> a b [#0] A = {'x': ['a','b']}; B = [ (And('a','b','c'), 'y') ] assert APPLY(A, B) == {'x': (['a','b'], [0]), 'a':Q(0), 'b':Q(0), 'c':Q(0)} # x -> a b &(a,b) -> c -- x -> a b c d # c -> d c -> d A = {'x': ['a','b'], 'c': ['d']} B = [ (And('a','b'), 'c') ] assert APPLY(A, B) == {'x': (['a','b','c','d'], []), 'c': (['d'], []), 'a':Q(0), 'b':Q(0)} # x -> a b &(a,b) -> c -- x -> a b c d e # c -> d &(c,d) -> e c -> d e A = {'x': ['a','b'], 'c': ['d']} B = [ (And('a','b'), 'c'), (And('c','d'), 'e') ] assert APPLY(A, B) == {'x': (['a','b','c','d','e'], []), 'c': (['d','e'], []), 'a':Q(0), 'b':Q(0), 'd':Q(1)} # x -> a b &(a,y) -> z -- x -> a b y z # &(a,b) -> y A = {'x': ['a','b']} B = [ (And('a','y'), 'z'), (And('a','b'), 'y') ] assert APPLY(A,B) == {'x': (['a','b','y','z'], []), 'a':([],[0,1]), 'y':Q(0), 'b':Q(1)} # x -> a b &(a,!b) -> c -- x -> a b A = {'x': ['a', 'b']} B = [ (And('a','!b'), 'c') ] assert APPLY(A,B) == {'x': (['a', 'b'], []), 'a':Q(0), '!b':Q(0)} # !x -> !a !b &(!a,b) -> c -- !x -> !a !b A = {'!x': ['!a', '!b']} B = [ (And('!a','b'), 'c') ] assert APPLY(A,B) == {'!x': (['!a', '!b'], []), '!a':Q(0), 'b':Q(0)} # x -> a b &(b,c) -> !a -- x -> a b A = {'x': ['a','b']} B = [ (And('b','c'), '!a') ] assert APPLY(A,B) == {'x': (['a','b'], []), 'b':Q(0), 'c':Q(0)} # x -> a b &(a, b) -> c -- x -> a b c p # c -> p a A = {'x': ['a','b'], 'c': ['p','a']} B = [ (And('a','b'), 'c') ] assert APPLY(A,B) == {'x': (['a','b','c','p'], []), 'c': (['p','a'], []), 'a':Q(0), 'b':Q(0)} # TODO more tests? def test_split_rules_tf(): S = split_rules_tt_tf_ft_ff r = {'a': ['b', '!c', 'd'], 'b': ['e', '!f'] } tt, tf, ft, ff = S(r) assert tt == {'a': ['b', 'd'], 'b': ['e']} assert tf == {'a': ['c'], 'b': ['f']} assert ft == {} assert ff == {'b': ['a'], 'd': ['a'], 'e': ['b']} r = {'!a': ['b', '!c'], 'b' : ['e', '!f'] } tt, tf, ft, ff = S(r) assert tt == {'b': ['e'], 'c': ['a'] } assert tf == {'b': ['f'] } assert ft == {'b': ['a'] } # XXX ok? maybe vice versa? assert ff == {'e': ['b'], 'a': ['c'] } def test_FactRules_parse(): f = FactRules('a -> b') # assert f.negs == {} assert f.rel_tt == {'a': ['b']} assert f.rel_tf == {} assert f.rel_ff == {'b': ['a']} assert f.rel_ft == {} assert f.prereq == {'b': ['a'], 'a': ['b']} f = FactRules('a -> !b') assert f.rel_tt == {} assert f.rel_tf == {'a': ['b'], 'b': ['a']} assert f.rel_ff == {} assert f.rel_ft == {} assert f.prereq == {'b': ['a'], 'a': ['b']} f = FactRules('!a -> b') assert f.rel_tt == {} assert f.rel_tf == {} assert f.rel_ff == {} assert f.rel_ft == {'a': ['b'], 'b': ['a']} assert f.prereq == {'b': ['a'], 'a': ['b']} f = FactRules('!a -> !b') assert f.rel_tt == {'b': ['a']} assert f.rel_tf == {} assert f.rel_ff == {'a': ['b']} assert f.rel_ft == {} assert f.prereq == {'b': ['a'], 'a': ['b']} f = FactRules('!z == nz') assert f.rel_tt == {} assert f.rel_tf == {'nz': ['z'], 'z': ['nz']} assert f.rel_ff == {} assert f.rel_ft == {'nz': ['z'], 'z': ['nz']} assert f.prereq == {'z': ['nz'], 'nz': ['z']} # TODO add parsing with | and & ? def test_FactRules_parse2(): raises(ValueError, "FactRules('a -> !a')") def test_FactRules_deduce(): f = FactRules(['a -> b', 'b -> c', 'b -> d', 'c -> e']) D = f.deduce_all_facts assert D({'a': T}) == {'a': T, 'b': T, 'c': T, 'd': T, 'e': T} assert D({'b': T}) == { 'b': T, 'c': T, 'd': T, 'e': T} assert D({'c': T}) == { 'c': T, 'e': T} assert D({'d': T}) == { 'd': T } assert D({'e': T}) == { 'e': T} assert D({'a': F}) == {'a': F } assert D({'b': F}) == {'a': F, 'b': F } assert D({'c': F}) == {'a': F, 'b': F, 'c': F } assert D({'d': F}) == {'a': F, 'b': F, 'd': F } assert D({'a': U}) == {'a': U} # XXX ok? def test_FactRules_deduce2(): # pos/neg/zero, but the rules are not sufficient to derive all relations f = FactRules(['pos -> !neg', 'pos -> !z']) D = f.deduce_all_facts assert D({'pos':T}) == {'pos': T, 'neg': F, 'z': F} assert D({'pos':F}) == {'pos': F } assert D({'neg':T}) == {'pos': F, 'neg': T } assert D({'neg':F}) == { 'neg': F } assert D({'z': T}) == {'pos': F, 'z': T} assert D({'z': F}) == { 'z': F} # pos/neg/zero. rules are sufficient to derive all relations f = FactRules(['pos -> !neg', 'neg -> !pos', 'pos -> !z', 'neg -> !z']) D = f.deduce_all_facts assert D({'pos':T}) == {'pos': T, 'neg': F, 'z': F} assert D({'pos':F}) == {'pos': F } assert D({'neg':T}) == {'pos': F, 'neg': T, 'z': F} assert D({'neg':F}) == { 'neg': F } assert D({'z': T}) == {'pos': F, 'neg': F, 'z': T} assert D({'z': F}) == { 'z': F} def test_FactRules_deduce_multiple(): # deduction that involves _several_ starting points # TODO add the same check for 'npos == real & !pos' ? f = FactRules(['real == pos | npos']) D = f.deduce_all_facts assert D({'real': T}) == {'real': T} assert D({'real': F}) == {'real': F, 'pos': F, 'npos': F} assert D({'pos' : T}) == {'real': T, 'pos': T} assert D({'npos': T}) == {'real': T, 'npos': T} # --- key tests below --- assert D({'pos': F, 'npos': F}) == {'real': F, 'pos': F, 'npos': F} assert D({'real': T, 'pos': F}) == {'real': T, 'pos': F, 'npos': T} assert D({'real': T, 'npos':F}) == {'real': T, 'pos': T, 'npos': F} assert D({'pos': T, 'npos': F}) == {'real': T, 'pos': T, 'npos': F} assert D({'pos': F, 'npos': T}) == {'real': T, 'pos': F, 'npos': T} def test_FactRules_deduce_multiple2(): f = FactRules(['real == neg | zero | pos']) D = f.deduce_all_facts assert D({'real': T}) == {'real': T} assert D({'real': F}) == {'real': F, 'neg': F, 'zero': F, 'pos': F} assert D({'neg' : T}) == {'real': T, 'neg': T} assert D({'zero': T}) == {'real': T, 'zero': T} assert D({'pos' : T}) == {'real': T, 'pos': T} # --- key tests below --- assert D({'neg': F, 'zero': F, 'pos': F}) == {'real': F, 'neg': F, 'zero': F, 'pos': F} assert D({'real':T, 'neg': F}) == {'real': T, 'neg': F} assert D({'real':T, 'zero':F}) == {'real': T, 'zero':F} assert D({'real':T, 'pos': F}) == {'real': T, 'pos': F} assert D({'real':T, 'zero': F, 'pos': F}) == {'real': T, 'neg': T, 'zero': F, 'pos': F} assert D({'real':T, 'neg': F, 'pos': F}) == {'real': T, 'neg': F, 'zero': T, 'pos': F} assert D({'real':T, 'neg': F, 'zero': F }) == {'real': T, 'neg': F, 'zero': F, 'pos': T} assert D({'neg': T, 'zero': F, 'pos': F}) == {'real': T, 'neg': T, 'zero': F, 'pos': F} assert D({'neg': F, 'zero': T, 'pos': F}) == {'real': T, 'neg': F, 'zero': T, 'pos': F} assert D({'neg': F, 'zero': F, 'pos': T}) == {'real': T, 'neg': F, 'zero': F, 'pos': T} def test_FactRules_deduce_base(): # deduction that starts from base f = FactRules(['real == neg | zero | pos', 'neg -> real & !zero & !pos', 'pos -> real & !zero & !neg']) D = f.deduce_all_facts base = D({'real': T, 'neg': F}) assert base == {'real': T, 'neg': F} X = D({'zero': F}, base=base) assert X is base # base is modified inplace assert base == {'real': T, 'neg': F, 'zero': F, 'pos': T} def test_FactRules_deduce_staticext(): # verify that static beta-extensions deduction takes place f = FactRules(['real == neg | zero | pos', 'neg -> real & !zero & !pos', 'pos -> real & !zero & !neg', 'nneg == real & !neg', 'npos == real & !pos']) assert 'npos' in f.rel_tt['neg'] assert 'nneg' in f.rel_tt['pos'] assert 'nneg' in f.rel_tt['zero'] assert 'npos' in f.rel_tt['zero'] # NOTE: once upon a time there was an idea to intoruce copy-on-write (COW) mode # in deduce_all_facts, and also teach it to return list of newly derived knowledge. # # it turned out not to be better performance wise (or was i wrong ?), so this # mode was removed. # # However disabled test stays here just in case (maybe we'll return to this # idea some day) def X_test_FactRules_deduce_cow(): f = FactRules(['real == neg | zero | pos', 'neg -> real & !zero & !pos', 'pos -> real & !zero & !neg']) D = f.deduce_all_facts base0 = D({'real': T, 'neg': F}) assert base0 == {'real': T, 'neg': F} base = base0.copy() X = D({'zero': F}, base=base, cow=False) assert X is base # base is modified inplace assert base == {'real': T, 'neg': F, 'zero': F, 'pos': T} base = base0.copy() X, new_knowledge = D({'zero': F}, base=base, cow=True) assert X is not base # base should be copied assert base == {'real': T, 'neg': F} assert X == {'real': T, 'neg': F, 'zero': F, 'pos': T} #assert set(new_knowledge) == set([ ('zero',F), ('pos',T) ]) # XXX disabled wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_truediv.py0000644000175000017500000000136712014170666026273 0ustar georgeskgeorgeskfrom __future__ import division #this module tests that sympy works with true division turned on from sympy import Rational, Symbol, Float def test_truediv(): assert 1/2 != 0 assert Rational(1)/2 != 0 def dotest(s): x = Symbol("x") y = Symbol("y") l = [ Rational(2), Float("1.3"), x, y, pow(x,y)*y, 5, 5.5 ] for x in l: for y in l: s(x,y) def test_basic(): def s(a,b): x = a x = +a x = -a x = a+b x = a-b x = a*b x = a/b x = a**b dotest(s) def test_ibasic(): def s(a,b): x = a x += b x = a x -= b x = a x *= b x = a x /= b dotest(s) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_cache.py0000644000175000017500000000033112014170666025642 0ustar georgeskgeorgeskfrom sympy.core.cache import cacheit def test_cacheit_doc(): @cacheit def testfn(): "test docstring" pass assert testfn.__doc__ == "test docstring" assert testfn.__name__ == "testfn" wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_containers.py0000644000175000017500000000563212014170666026755 0ustar georgeskgeorgeskfrom sympy import Matrix, Tuple, symbols, sympify, Basic from sympy.core.containers import tuple_wrapper from sympy.utilities.pytest import raises from sympy.core.compatibility import is_sequence, iterable def test_Tuple(): t = (1, 2, 3, 4) st = Tuple(*t) assert set(sympify(t)) == set(st) assert len(t) == len(st) assert set(sympify(t[:2])) == set(st[:2]) assert isinstance(st[:], Tuple) assert st == Tuple(1, 2, 3, 4) assert st.func(*st.args) == st p, q, r, s = symbols('p q r s') t2 = (p, q, r, s) st2 = Tuple(*t2) assert st2.atoms() == set(t2) assert st == st2.subs({p:1, q:2, r:3, s:4}) # issue 2406 assert all([ isinstance(arg, Basic) for arg in st.args ]) assert Tuple(p, 1).subs(p, 0) == Tuple(0, 1) assert Tuple(p, Tuple(p, 1)).subs(p, 0) == Tuple(0, Tuple(0, 1)) assert Tuple(t2) == Tuple(Tuple(*t2)) assert Tuple.fromiter(t2) == Tuple(*t2) assert Tuple.fromiter(x for x in xrange(4)) == Tuple(0, 1, 2, 3) assert st2.fromiter(st2.args) == st2 def test_Tuple_contains(): t1, t2 = Tuple(1), Tuple(2) assert t1 in Tuple(1, 2, 3, t1, Tuple(t2)) assert t2 not in Tuple(1, 2, 3, t1, Tuple(t2)) def test_Tuple_concatenation(): assert Tuple(1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) assert (1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) assert Tuple(1, 2) + (3, 4) == Tuple(1, 2, 3, 4) raises(TypeError, 'Tuple(1, 2) + 3') raises(TypeError, '1 + Tuple(2, 3)') #the Tuple case in __radd__ is only reached when a subclass is involved class Tuple2(Tuple): def __radd__(self, other): return Tuple.__radd__(self, other + other) assert Tuple(1, 2) + Tuple2(3, 4) == Tuple(1, 2, 1, 2, 3, 4) assert Tuple2(1, 2) + Tuple(3, 4) == Tuple(1, 2, 3, 4) def test_Tuple_equality(): assert Tuple(1, 2) is not (1, 2) assert (Tuple(1, 2) == (1, 2)) is True assert (Tuple(1, 2) != (1, 2)) is False assert (Tuple(1, 2) == (1, 3)) is False assert (Tuple(1, 2) != (1, 3)) is True assert (Tuple(1, 2) == Tuple(1, 2)) is True assert (Tuple(1, 2) != Tuple(1, 2)) is False assert (Tuple(1, 2) == Tuple(1, 3)) is False assert (Tuple(1, 2) != Tuple(1, 3)) is True def test_tuple_wrapper(): @tuple_wrapper def wrap_tuples_and_return(*t): return t p = symbols('p') assert wrap_tuples_and_return(p, 1) == (p, 1) assert wrap_tuples_and_return((p, 1)) == (Tuple(p, 1),) assert wrap_tuples_and_return(1, (p, 2), 3) == (1, Tuple(p, 2), 3) def test_iterable_is_sequence(): ordered = [list(), tuple(), Tuple(), Matrix([[]])] unordered = [set()] not_sympy_iterable = [{}, '', u''] assert all(is_sequence(i) for i in ordered) assert all(not is_sequence(i) for i in unordered) assert all(iterable(i) for i in ordered + unordered) assert all(not iterable(i) for i in not_sympy_iterable) assert all(iterable(i, exclude=None) for i in not_sympy_iterable) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_complex.py0000644000175000017500000004533112014170666026257 0ustar georgeskgeorgeskfrom sympy import Symbol, sqrt, I, Integer, Rational, cos, atan, sin, im, re, \ exp, sinh, cosh, tan, tanh, conjugate, sign, cot, coth, pi, expand_complex def test_complex(): a = Symbol("a", real=True) b = Symbol("b", real=True) e = (a+I*b)*(a-I*b) assert e.expand() == a**2+b**2 assert sqrt(I) == (-1)**Rational(1,4) def test_conjugate(): a = Symbol("a", real=True) b = Symbol("b", real=True) x = Symbol('x') z = a + I*b zc = a - I*b assert conjugate(z) == zc assert conjugate(exp(z)) == exp(zc) assert conjugate(exp(I*x)) == exp(-I*conjugate(x)) assert conjugate(z**5) == zc**5 assert conjugate(abs(x)) == abs(x) assert conjugate(sign(x)) == sign(x) assert conjugate(sin(z)) == sin(zc) assert conjugate(cos(z)) == cos(zc) assert conjugate(tan(z)) == tan(zc) assert conjugate(cot(z)) == cot(zc) assert conjugate(sinh(z)) == sinh(zc) assert conjugate(cosh(z)) == cosh(zc) assert conjugate(tanh(z)) == tanh(zc) assert conjugate(coth(z)) == coth(zc) def test_abs1(): a=Symbol("a", real=True) b=Symbol("b", real=True) assert abs(a) == abs(a) assert abs(-a) == abs(a) assert abs(a+I*b) == sqrt(a**2+b**2) def test_abs2(): a=Symbol("a", real=False) b=Symbol("b", real=False) assert abs(a) != a assert abs(-a) != a assert abs(a+I*b) != sqrt(a**2+b**2) def test_evalc(): x = Symbol("x", real=True) y = Symbol("y", real=True) z = Symbol("z") assert ((x+I*y)**2).expand(complex=True) == x**2+2*I*x*y - y**2 assert expand_complex(z**(2*I)) == I*im(z**(2*I)) + re(z**(2*I)) assert exp(I*x) != cos(x)+I*sin(x) assert exp(I*x).expand(complex=True) == cos(x)+I*sin(x) assert exp(I*x+y).expand(complex=True) == exp(y)*cos(x)+I*sin(x)*exp(y) assert sin(I*x).expand(complex=True) == I * sinh(x) assert sin(x+I*y).expand(complex=True) == sin(x)*cosh(y) + \ I * sinh(y) * cos(x) assert cos(I*x).expand(complex=True) == cosh(x) assert cos(x+I*y).expand(complex=True) == cos(x)*cosh(y) - \ I * sinh(y) * sin(x) assert tan(I*x).expand(complex=True) == tanh(x) * I assert tan(x+I*y).expand(complex=True) == \ ((sin(x)*cos(x) + I*cosh(y)*sinh(y)) / (cos(x)**2 + sinh(y)**2)).expand() assert sinh(I*x).expand(complex=True) == I * sin(x) assert sinh(x+I*y).expand(complex=True) == sinh(x)*cos(y) + \ I * sin(y) * cosh(x) assert cosh(I*x).expand(complex=True) == cos(x) assert cosh(x+I*y).expand(complex=True) == cosh(x)*cos(y) + \ I * sin(y) * sinh(x) assert tanh(I*x).expand(complex=True) == tan(x) * I assert tanh(x+I*y).expand(complex=True) == \ ((sinh(x)*cosh(x) + I*cos(y)*sin(y)) / (sinh(x)**2 + cos(y)**2)).expand() def test_pythoncomplex(): x = Symbol("x") assert 4j*x == 4*x*I assert 4j*x == 4.0*x*I assert 4.1j*x != 4*x*I def test_rootcomplex(): R = Rational assert ((+1+I)**R(1,2)).expand(complex=True) == 2**R(1,4)*cos( pi/8) + 2**R(1,4)*sin( pi/8)*I assert ((-1-I)**R(1,2)).expand(complex=True) == 2**R(1,4)*cos(3*pi/8) - 2**R(1,4)*sin(3*pi/8)*I assert (sqrt(-10)*I).as_real_imag() == (-sqrt(10), 0) def test_expand_inverse(): assert (1/(1+I)).expand(complex=True) == (1-I)/2 assert ((1+2*I)**(-2)).expand(complex=True) == (-3-4*I)/25 assert ((1+I)**(-8)).expand(complex=True) == Rational(1,16) def test_expand_complex(): assert ((2+3*I)**10).expand(complex=True) == -341525 - 145668*I # the following two tests are to ensure the SymPy uses an efficient # algorithm for calculating powers of complex numbers. They should execute # in something like 0.01s. assert ((2+3*I)**1000).expand(complex=True) == \ -81079464736246615951519029367296227340216902563389546989376269312984127074385455204551402940331021387412262494620336565547972162814110386834027871072723273110439771695255662375718498785908345629702081336606863762777939617745464755635193139022811989314881997210583159045854968310911252660312523907616129080027594310008539817935736331124833163907518549408018652090650537035647520296539436440394920287688149200763245475036722326561143851304795139005599209239350981457301460233967137708519975586996623552182807311159141501424576682074392689622074945519232029999 + \ 46938745946789557590804551905243206242164799136976022474337918748798900569942573265747576032611189047943842446167719177749107138603040963603119861476016947257034472364028585381714774667326478071264878108114128915685688115488744955550920239128462489496563930809677159214598114273887061533057125164518549173898349061972857446844052995037423459472376202251620778517659247970283904820245958198842631651569984310559418135975795868314764489884749573052997832686979294085577689571149679540256349988338406458116270429842222666345146926395233040564229555893248370000*I assert ((2+3*I/4)**1000).expand(complex=True) == \ Integer(1)*37079892761199059751745775382463070250205990218394308874593455293485167797989691280095867197640410033222367257278387021789651672598831503296531725827158233077451476545928116965316544607115843772405184272449644892857783761260737279675075819921259597776770965829089907990486964515784097181964312256560561065607846661496055417619388874421218472707497847700629822858068783288579581649321248495739224020822198695759609598745114438265083593711851665996586461937988748911532242908776883696631067311443171682974330675406616373422505939887984366289623091300746049101284856530270685577940283077888955692921951247230006346681086274961362500646889925803654263491848309446197554307105991537357310209426736453173441104334496173618419659521888945605315751089087820455852582920963561495787655250624781448951403353654348109893478206364632640344111022531861683064175862889459084900614967785405977231549003280842218501570429860550379522498497412180001/114813069527425452423283320117768198402231770208869520047764273682576626139237031385665948631650626991844596463898746277344711896086305533142593135616665318539129989145312280000688779148240044871428926990063486244781615463646388363947317026040466353970904996558162398808944629605623311649536164221970332681344168908984458505602379484807914058900934776500429002716706625830522008132236281291761267883317206598995396418127021779858404042159853183251540889433902091920554957783589672039160081957216630582755380425583726015528348786419432054508915275783882625175435528800822842770817965453762184851149029376 + \ I*421638390580169706973991429333213477486930178424989246669892530737775352519112934278994501272111385966211392610029433824534634841747911783746811994443436271013377059560245191441549885048056920190833693041257216263519792201852046825443439142932464031501882145407459174948712992271510309541474392303461939389368955986650538525895866713074543004916049550090364398070215427272240155060576252568700906004691224321432509053286859100920489253598392100207663785243368195857086816912514025693453058403158416856847185079684216151337200057494966741268925263085619240941610301610538225414050394612058339070756009433535451561664522479191267503989904464718368605684297071150902631208673621618217106272361061676184840810762902463998065947687814692402219182668782278472952758690939877465065070481351343206840649517150634973307937551168752642148704904383991876969408056379195860410677814566225456558230131911142229028179902418223009651437985670625/1793954211366022694113801876840128100034871409513586250746316776290259783425578615401030447369541046747571819748417910583511123376348523955353017744010395602173906080395504375010762174191250701116076984219741972574712741619474818186676828531882286780795390571221287481389759837587864244524002565968286448146002639202882164150037179450123657170327105882819203167448541028601906377066191895183769810676831353109303069033234715310287563158747705988305326397404720186258671215368588625611876280581509852855552819149745718992630449787803625851701801184123166018366180137512856918294030710215034138299203584 assert ((2+3*I)**-1000).expand(complex=True) == \ Integer(1)*-81079464736246615951519029367296227340216902563389546989376269312984127074385455204551402940331021387412262494620336565547972162814110386834027871072723273110439771695255662375718498785908345629702081336606863762777939617745464755635193139022811989314881997210583159045854968310911252660312523907616129080027594310008539817935736331124833163907518549408018652090650537035647520296539436440394920287688149200763245475036722326561143851304795139005599209239350981457301460233967137708519975586996623552182807311159141501424576682074392689622074945519232029999/8777125472973511649630750050295188683351430110097915876250894978429797369155961290321829625004920141758416719066805645579710744290541337680113772670040386863849283653078324415471816788604945889094925784900885812724984087843737442111926413818245854362613018058774368703971604921858023116665586358870612944209398056562604561248859926344335598822815885851096698226775053153403320782439987679978321289537645645163767251396759519805603090332694449553371530571613352311006350058217982509738362083094920649452123351717366337410243853659113315547584871655479914439219520157174729130746351059075207407866012574386726064196992865627149566238044625779078186624347183905913357718850537058578084932880569701242598663149911276357125355850792073635533676541250531086757377369962506979378337216411188347761901006460813413505861461267545723590468627854202034450569581626648934062198718362303420281555886394558137408159453103395918783625713213314350531051312551733021627153081075080140680608080529736975658786227362251632725009435866547613598753584705455955419696609282059191031962604169242974038517575645939316377801594539335940001 - Integer(1)*46938745946789557590804551905243206242164799136976022474337918748798900569942573265747576032611189047943842446167719177749107138603040963603119861476016947257034472364028585381714774667326478071264878108114128915685688115488744955550920239128462489496563930809677159214598114273887061533057125164518549173898349061972857446844052995037423459472376202251620778517659247970283904820245958198842631651569984310559418135975795868314764489884749573052997832686979294085577689571149679540256349988338406458116270429842222666345146926395233040564229555893248370000*I/8777125472973511649630750050295188683351430110097915876250894978429797369155961290321829625004920141758416719066805645579710744290541337680113772670040386863849283653078324415471816788604945889094925784900885812724984087843737442111926413818245854362613018058774368703971604921858023116665586358870612944209398056562604561248859926344335598822815885851096698226775053153403320782439987679978321289537645645163767251396759519805603090332694449553371530571613352311006350058217982509738362083094920649452123351717366337410243853659113315547584871655479914439219520157174729130746351059075207407866012574386726064196992865627149566238044625779078186624347183905913357718850537058578084932880569701242598663149911276357125355850792073635533676541250531086757377369962506979378337216411188347761901006460813413505861461267545723590468627854202034450569581626648934062198718362303420281555886394558137408159453103395918783625713213314350531051312551733021627153081075080140680608080529736975658786227362251632725009435866547613598753584705455955419696609282059191031962604169242974038517575645939316377801594539335940001 assert ((2+3*I/4)**-1000).expand(complex=True) == \ Integer(1)*4257256305661027385394552848555894604806501409793288342610746813288539790051927148781268212212078237301273165351052934681382567968787279534591114913777456610214738290619922068269909423637926549603264174216950025398244509039145410016404821694746262142525173737175066432954496592560621330313807235750500564940782099283410261748370262433487444897446779072067625787246390824312580440138770014838135245148574339248259670887549732495841810961088930810608893772914812838358159009303794863047635845688453859317690488124382253918725010358589723156019888846606295866740117645571396817375322724096486161308083462637370825829567578309445855481578518239186117686659177284332344643124760453112513611749309168470605289172320376911472635805822082051716625171429727162039621902266619821870482519063133136820085579315127038372190224739238686708451840610064871885616258831386810233957438253532027049148030157164346719204500373766157143311767338973363806106967439378604898250533766359989107510507493549529158818602327525235240510049484816090584478644771183158342479140194633579061295740839490629457435283873180259847394582069479062820225159699506175855369539201399183443253793905149785994830358114153241481884290274629611529758663543080724574566578220908907477622643689220814376054314972190402285121776593824615083669045183404206291739005554569305329760211752815718335731118664756831942466773261465213581616104242113894521054475516019456867271362053692785300826523328020796670205463390909136593859765912483565093461468865534470710132881677639651348709376/2103100954337624833663208713697737151593634525061637972297915388685604042449504336765884978184588688426595940401280828953096857809292320006227881797146858511436638446932833617514351442216409828605662238790280753075176269765767010004889778647709740770757817960711900340755635772183674511158570690702969774966791073165467918123298694584729211212414462628433370481195120564586361368504153395406845170075275051749019600057116719726628746724489572189061061036426955163696859127711110719502594479795200686212257570291758725259007379710596548777812659422174199194837355646482046783616494013289495563083118517507178847555801163089723056310287760875135196081975602765511153122381201303871673391366630940702817360340900568748719988954847590748960761446218262344767250783946365392689256634180417145926390656439421745644011831124277463643383712803287985472471755648426749842410972650924240795946699346613614779460399530274263580007672855851663196114585312432954432654691485867618908420370875753749297487803461900447407917655296784879220450937110470920633595689721819488638484547259978337741496090602390463594556401615298457456112485536498177883358587125449801777718900375736758266215245325999241624148841915093787519330809347240990363802360596034171167818310322276373120180985148650099673289383722502488957717848531612020897298448601714154586319660314294591620415272119454982220034319689607295960162971300417552364254983071768070124456169427638371140064235083443242844616326538396503937972586505546495649094344512270582463639152160238137952390380581401171977159154009407415523525096743009110916334144716516647041176989758534635251844947906038080852185583742296318878233394998111078843229681030277039104786225656992262073797524057992347971177720807155842376332851559276430280477639539393920006008737472164850104411971830120295750221200029811143140323763349636629725073624360001 - Integer(1)*3098214262599218784594285246258841485430681674561917573155883806818465520660668045042109232930382494608383663464454841313154390741655282039877410154577448327874989496074260116195788919037407420625081798124301494353693248757853222257918294662198297114746822817460991242508743651430439120439020484502408313310689912381846149597061657483084652685283853595100434135149479564507015504022249330340259111426799121454516345905101620532787348293877485702600390665276070250119465888154331218827342488849948540687659846652377277250614246402784754153678374932540789808703029043827352976139228402417432199779415751301480406673762521987999573209628597459357964214510139892316208670927074795773830798600837815329291912002136924506221066071242281626618211060464126372574400100990746934953437169840312584285942093951405864225230033279614235191326102697164613004299868695519642598882914862568516635347204441042798206770888274175592401790040170576311989738272102077819127459014286741435419468254146418098278519775722104890854275995510700298782146199325790002255362719776098816136732897323406228294203133323296591166026338391813696715894870956511298793595675308998014158717167429941371979636895553724830981754579086664608880698350866487717403917070872269853194118364230971216854931998642990452908852258008095741042117326241406479532880476938937997238098399302185675832474590293188864060116934035867037219176916416481757918864533515526389079998129329045569609325290897577497835388451456680707076072624629697883854217331728051953671643278797380171857920000*I/2103100954337624833663208713697737151593634525061637972297915388685604042449504336765884978184588688426595940401280828953096857809292320006227881797146858511436638446932833617514351442216409828605662238790280753075176269765767010004889778647709740770757817960711900340755635772183674511158570690702969774966791073165467918123298694584729211212414462628433370481195120564586361368504153395406845170075275051749019600057116719726628746724489572189061061036426955163696859127711110719502594479795200686212257570291758725259007379710596548777812659422174199194837355646482046783616494013289495563083118517507178847555801163089723056310287760875135196081975602765511153122381201303871673391366630940702817360340900568748719988954847590748960761446218262344767250783946365392689256634180417145926390656439421745644011831124277463643383712803287985472471755648426749842410972650924240795946699346613614779460399530274263580007672855851663196114585312432954432654691485867618908420370875753749297487803461900447407917655296784879220450937110470920633595689721819488638484547259978337741496090602390463594556401615298457456112485536498177883358587125449801777718900375736758266215245325999241624148841915093787519330809347240990363802360596034171167818310322276373120180985148650099673289383722502488957717848531612020897298448601714154586319660314294591620415272119454982220034319689607295960162971300417552364254983071768070124456169427638371140064235083443242844616326538396503937972586505546495649094344512270582463639152160238137952390380581401171977159154009407415523525096743009110916334144716516647041176989758534635251844947906038080852185583742296318878233394998111078843229681030277039104786225656992262073797524057992347971177720807155842376332851559276430280477639539393920006008737472164850104411971830120295750221200029811143140323763349636629725073624360001 def test_expand(): f = (16 - 2*sqrt(29))**2 assert f.expand() == 372 - 64*sqrt(29) f = (Integer(1)/2 + I/2)**10 assert f.expand() == I/32 f = (Integer(1)/2 + I)**10 assert f.expand() == Integer(237)/1024 - 779*I/256 def test_re_im1652(): x = Symbol('x') assert re(x) == re(conjugate(x)) assert im(x) == - im(conjugate(x)) assert im(x)*re(conjugate(x)) + im(conjugate(x)) * re(x) == 0 def test_issue_1985(): x = Symbol('x') assert ((x + x*I)/(1 + I)).as_real_imag() == (re((x + I*x)/(1 + I)), im((x + I*x)/(1 + I))) def test_issue_2137(): assert (cos(1+I)**3).as_real_imag() == (-3*sin(1)**2*sinh(1)**2*cos(1)*cosh(1) + cos(1)**3*cosh(1)**3, -3*cos(1)**2*cosh(1)**2*sin(1)*sinh(1) + sin(1)**3*sinh(1)**3) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_operations.py0000644000175000017500000000174312014170666026772 0ustar georgeskgeorgeskfrom sympy import Integer, S from sympy.core.operations import LatticeOp from sympy.utilities.pytest import raises from sympy.core.sympify import SympifyError # create the simplest possible Lattice class class join(LatticeOp): zero = Integer(0) identity = Integer(1) def test_lattice_simple(): assert join(join(2, 3), 4) == join(2, join(3, 4)) assert join(2, 3) == join(3, 2) assert join(0, 2) == 0 assert join(1, 2) == 2 assert join(2, 2) == 2 assert join(join(2, 3), 4) == join(2, 3, 4) assert join() == 1 assert join(4) == 4 assert join(1, 4, 2, 3, 1, 3, 2) == join(2, 3, 4) def test_lattice_shortcircuit(): raises(SympifyError, 'join(object)') assert join(0, object) == 0 def test_lattice_print(): assert str(join(5, 4, 3, 2)) == 'join(2, 3, 4, 5)' def test_lattice_make_args(): assert join.make_args(0) == set([0]) assert join.make_args(1) == set([1]) assert join.make_args(join(2, 3, 4)) == set([S(2), S(3), S(4)]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_count_ops.py0000644000175000017500000000556512014170666026626 0ustar georgeskgeorgeskfrom sympy import symbols, sin, exp, cos, Derivative, Integral, Basic, \ count_ops, S, And, I, pi, Eq x, y, z = symbols('x,y,z') def test_count_ops_non_visual(): def count(val): return count_ops(val, visual=False) assert count(x) == 0 assert count(x) is not S.Zero assert count(x + y) == 1 assert count(x + y) is not S.One assert count(x + y*x + 2*y) == 4 assert count({x + y: x}) == 1 assert count({x + y: S(2) + x}) is not S.One def test_count_ops_visual(): ADD, MUL, POW, SIN, COS, EXP, AND, D, G = symbols('Add Mul Pow sin cos exp And Derivative Integral'.upper()) DIV, SUB, NEG = symbols('DIV SUB NEG') def count(val): return count_ops(val, visual=True) assert count(7) is S.Zero assert count(S(7)) is S.Zero assert count(-1) == NEG assert count(-2) == NEG assert count(S(2)/3) == DIV assert count(pi/3) == DIV assert count(-pi/3) == DIV + NEG assert count(I - 1) == SUB assert count(1 - I) == SUB assert count(1 - 2*I) == SUB + MUL assert count(x) is S.Zero assert count(-x) == NEG assert count(-2*x/3) == NEG + DIV + MUL assert count(1/x) == DIV assert count(1/(x*y)) == DIV + MUL assert count(-1/x) == NEG + DIV assert count(-2/x) == NEG + DIV assert count(x/y) == DIV assert count(-x/y) == NEG + DIV assert count(x**2) == POW assert count(-x**2) == POW + NEG assert count(-2*x**2) == POW + MUL + NEG assert count(x + pi/3) == ADD + DIV assert count(x + S(1)/3) == ADD + DIV assert count(x + y) == ADD assert count(x - y) == SUB assert count(y - x) == SUB assert count(-1/(x - y)) == DIV + NEG + SUB assert count(-1/(y - x)) == DIV + NEG + SUB assert count(1 + x**y) == ADD + POW assert count(1 + x + y) == 2*ADD assert count(1 + x + y + z) == 3*ADD assert count(1 + x**y + 2*x*y + y**2) == 3*ADD + 2*POW + 2*MUL assert count(2*z + y + x + 1) == 3*ADD + MUL assert count(2*z + y**17 + x + 1) == 3*ADD + MUL + POW assert count(2*z + y**17 + x + sin(x)) == 3*ADD + POW + MUL + SIN assert count(2*z + y**17 + x + sin(x**2)) == 3*ADD + MUL + 2*POW + SIN assert count(2*z + y**17 + x + sin(x**2) + exp(cos(x))) == 4*ADD + MUL + 2*POW + EXP + COS + SIN assert count(Derivative(x, x)) == D assert count(Integral(x, x) + 2*x/(1 + x)) == G + DIV + MUL + 2*ADD assert count(Basic()) is S.Zero assert count({x + 1: sin(x)}) == ADD + SIN assert count([x + 1, sin(x) + y, None]) == ADD + SIN + ADD assert count({x + 1: sin(x), y: cos(x) + 1}) == SIN + COS + 2*ADD assert count({}) is S.Zero assert count([x + 1, sin(x)*y, None]) == SIN + ADD + MUL assert count([]) is S.Zero assert count(And(x, y, z)) == 2*AND assert count(Basic(x, x + y)) == ADD # is this right or should we count the Eq, too...like an Add? assert count(Eq(x + y, S(2))) == ADD wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_basic.py0000644000175000017500000000504412014170666025666 0ustar georgeskgeorgesk"""This tests sympy/core/basic.py with (ideally) no reference to subclasses of Basic or Atom.""" from sympy.core.basic import Basic, Atom from sympy.core.singleton import S, Singleton from sympy.utilities.pytest import raises b1 = Basic(); b2 = Basic(b1); b3 = Basic(b2) b21 = Basic(b2, b1) def test_structure(): assert b21.args == (b2, b1) assert tuple(b21.iter_basic_args()) == b21.args assert b21.func(*b21.args) == b21 assert bool(b1) def test_equality(): instances = [b1, b2, b3, b21, Basic(b1,b1,b1), Basic] for i, b_i in enumerate(instances): for j, b_j in enumerate(instances): assert (b_i == b_j) == (i == j) assert (b_i != b_j) == (i != j) assert Basic() != [] assert not(Basic() == []) assert Basic() != 0 assert not(Basic() == 0) def test_matches_basic(): instances = [Basic(b1,b1,b2), Basic(b1,b2,b1), Basic(b2, b1, b1), Basic(b1, b2), Basic(b2, b1), b2, b1] for i, b_i in enumerate(instances): for j, b_j in enumerate(instances): if i ==j: assert b_i.matches(b_j) == {} else: assert b_i.matches(b_j) is None assert b1.match(b1) == {} def test_has(): assert b21.has(b1) assert b21.has(b3, b1) assert b21.has(Basic) assert not b1.has(b21, b3) assert not b21.has() def test_subs(): assert b21.subs(b2, b1) == Basic(b1, b1) assert b21.subs(b2, b21) == Basic(b21, b1) assert b3.subs(b2, b1) == b2 assert b21.subs([(b2, b1), (b1, b2)]) == Basic(b2, b2) assert b21.subs({b1: b2, b2: b1}) == Basic(b2, b2) raises(TypeError, "b21.subs('bad arg')") raises(TypeError, "b21.subs(b1, b2, b3)") def test_atoms(): assert b21.atoms() == set() def test_free_symbols_empty(): assert b21.free_symbols == set() def test_doit(): assert b21.doit() == b21 assert b21.doit(deep=False) == b21 def test_S(): assert repr(S) == 'S' def test_Singleton(): global instanciated instanciated = 0 class MySingleton(Basic): __metaclass__ = Singleton def __new__(cls): global instanciated instanciated += 1 return Basic.__new__(cls) assert instanciated == 1 assert MySingleton() is not Basic() assert MySingleton() is MySingleton() assert S.MySingleton is MySingleton() assert instanciated == 1 class MySingleton_sub(MySingleton): pass assert instanciated == 2 assert MySingleton_sub() is not MySingleton() assert MySingleton_sub() is MySingleton_sub() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_expr.py0000644000175000017500000010557212014170666025572 0ustar georgeskgeorgeskfrom __future__ import division from sympy import (Add, Basic, S, Symbol, Wild, Float, Integer, Rational, I, sin, cos, tan, exp, log, oo, sqrt, symbols, Integral, sympify, WildFunction, Poly, Function, Derivative, Number, pi, var, NumberSymbol, zoo, Piecewise, Mul, Pow, nsimplify, ratsimp, trigsimp, radsimp, powsimp, simplify, together, separate, collect, factorial, apart, combsimp, factor, refine, cancel, invert, Tuple, default_sort_key, DiracDelta) from sympy.physics.secondquant import FockState from sympy.core.cache import clear_cache from sympy.utilities.pytest import XFAIL, raises class DummyNumber(object): """ Minimal implementation of a number that works with SymPy. If one has a Number class (e.g. Sage Integer, or some other custom class) that one wants to work well with SymPy, one has to implement at least the methods of this class DummyNumber, resp. its subclasses I5 and F1_1. Basically, one just needs to implement either __int__() or __float__() and then one needs to make sure that the class works with Python integers and with itself. """ def __radd__(self, a): if isinstance(a, (int, float)): return a + self.number return NotImplemented def __truediv__(a, b): return a.__div__(b) def __rtruediv__(a, b): return a.__rdiv__(b) def __add__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number + a return NotImplemented def __rsub__(self, a): if isinstance(a, (int, float)): return a - self.number return NotImplemented def __sub__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number - a return NotImplemented def __rmul__(self, a): if isinstance(a, (int, float)): return a * self.number return NotImplemented def __mul__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number * a return NotImplemented def __rdiv__(self, a): if isinstance(a, (int, float)): return a / self.number return NotImplemented def __div__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number / a return NotImplemented def __rpow__(self, a): if isinstance(a, (int, float)): return a ** self.number return NotImplemented def __pow__(self, a): if isinstance(a, (int, float, DummyNumber)): return self.number ** a return NotImplemented def __pos__(self): return self.number def __neg__(self): return - self.number class I5(DummyNumber): number = 5 def __int__(self): return self.number class F1_1(DummyNumber): number = 1.1 def __float__(self): return self.number x,y,z,t,n = symbols('x,y,z,t,n') i5 = I5() f1_1 = F1_1() # basic sympy objects basic_objs = [ Rational(2), Float("1.3"), x, y, pow(x,y)*y, ] # all supported objects all_objs = basic_objs + [ 5, 5.5, i5, f1_1 ] def dotest(s): for x in all_objs: for y in all_objs: s(x,y) def test_basic(): def s(a,b): x = a x = +a x = -a x = a+b x = a-b x = a*b x = a/b x = a**b dotest(s) def test_ibasic(): def s(a,b): x = a x += b x = a x -= b x = a x *= b x = a x /= b dotest(s) def test_relational(): assert (pi < 3) == False assert (pi <= 3) == False assert (pi > 3) == True assert (pi >= 3) == True assert (-pi < 3) == True assert (-pi <= 3) == True assert (-pi > 3) == False assert (-pi >= 3) == False assert (x - 2 < x - 3) == False def test_relational_noncommutative(): from sympy import Lt, Gt, Le, Ge a, b = symbols('a b', commutative=False) assert (a < b) == Lt(a, b) assert (a <= b) == Le(a, b) assert (a > b) == Gt(a, b) assert (a >= b) == Ge(a, b) def test_basic_nostr(): for obj in basic_objs: for op in ['+','-','*','/','**']: if obj == 2 and op == '*': if hasattr(int, '__index__'): # Python 2.5+ (PEP 357) assert obj * '1' == '11' else: raises(TypeError, "obj %s '1'" % op) def test_leadterm(): assert (3+2*x**(log(3)/log(2)-1)).leadterm(x) == (3,0) assert (1/x**2+1+x+x**2).leadterm(x)[1] == -2 assert (1/x+1+x+x**2).leadterm(x)[1] == -1 assert (x**2+1/x).leadterm(x)[1] == -1 assert (1+x**2).leadterm(x)[1] == 0 assert (x+1).leadterm(x)[1] == 0 assert (x+x**2).leadterm(x)[1] == 1 assert (x**2).leadterm(x)[1] == 2 def test_as_leading_term(): assert (3+2*x**(log(3)/log(2)-1)).as_leading_term(x) == 3 assert (1/x**2+1+x+x**2).as_leading_term(x) == 1/x**2 assert (1/x+1+x+x**2).as_leading_term(x) == 1/x assert (x**2+1/x).as_leading_term(x) == 1/x assert (1+x**2).as_leading_term(x) == 1 assert (x+1).as_leading_term(x) == 1 assert (x+x**2).as_leading_term(x) == x assert (x**2).as_leading_term(x) == x**2 def test_leadterm2(): assert (x*cos(1)*cos(1 + sin(1)) + sin(1 + sin(1))).leadterm(x) == \ (sin(1 + sin(1)), 0) def test_leadterm3(): assert (y+z+x).leadterm(x) == (y+z, 0) def test_as_leading_term2(): assert (x*cos(1)*cos(1 + sin(1)) + sin(1 + sin(1))).as_leading_term(x) == \ sin(1 + sin(1)) def test_as_leading_term3(): assert (2+pi+x).as_leading_term(x) == 2 + pi assert (2*x+pi*x+x**2).as_leading_term(x) == (2+pi)*x def test_atoms(): assert sorted(list(x.atoms())) == [x] assert sorted(list((1+x).atoms())) == sorted([1, x]) assert sorted(list((1+2*cos(x)).atoms(Symbol))) == [x] assert sorted(list((1+2*cos(x)).atoms(Symbol,Number))) == sorted([1, 2, x]) assert sorted(list((2*(x**(y**x))).atoms())) == sorted([2, x, y]) assert sorted(list(Rational(1,2).atoms())) == [S.Half] assert sorted(list(Rational(1,2).atoms(Symbol))) == [] assert sorted(list(sin(oo).atoms(oo))) == [oo] assert sorted(list(Poly(0, x).atoms())) == [S.Zero] assert sorted(list(Poly(1, x).atoms())) == [S.One] assert sorted(list(Poly(x, x).atoms())) == [x] assert sorted(list(Poly(x, x, y).atoms())) == [x] assert sorted(list(Poly(x + y, x, y).atoms())) == sorted([x, y]) assert sorted(list(Poly(x + y, x, y, z).atoms())) == sorted([x, y]) assert sorted(list(Poly(x + y*t, x, y, z).atoms())) == sorted([t, x, y]) I = S.ImaginaryUnit assert list((I*pi).atoms(NumberSymbol)) == [pi] assert sorted((I*pi).atoms(NumberSymbol, I)) == \ sorted((I*pi).atoms(I,NumberSymbol)) == [pi, I] assert exp(exp(x)).atoms(exp) == set([exp(exp(x)), exp(x)]) assert (1 + x*(2 + y)+exp(3 + z)).atoms(Add) == set( [1 + x*(2 + y)+exp(3 + z), 2 + y, 3 + z]) def test_is_polynomial(): z = Symbol('z') k = Symbol('k', nonnegative=True, integer=True) assert Rational(2).is_polynomial(x, y, z) == True assert (S.Pi).is_polynomial(x, y, z) == True assert x.is_polynomial(x) == True assert x.is_polynomial(y) == True assert (x**2).is_polynomial(x) == True assert (x**2).is_polynomial(y) == True assert (x**(-2)).is_polynomial(x) == False assert (x**(-2)).is_polynomial(y) == True assert (2**x).is_polynomial(x) == False assert (2**x).is_polynomial(y) == True assert (x**k).is_polynomial(x) == False assert (x**k).is_polynomial(k) == False assert (x**x).is_polynomial(x) == False assert (k**k).is_polynomial(k) == False assert (k**x).is_polynomial(k) == False assert (x**(-k)).is_polynomial(x) == False assert ((2*x)**k).is_polynomial(x) == False assert (x**2 + 3*x - 8).is_polynomial(x) == True assert (x**2 + 3*x - 8).is_polynomial(y) == True assert (x**2 + 3*x - 8).is_polynomial() == True assert sqrt(x).is_polynomial(x) == False assert (x**S.Half).is_polynomial(x) == False assert (x**Rational(3,2)).is_polynomial(x) == False assert (x**2 + 3*x*sqrt(y) - 8).is_polynomial(x) == True assert (x**2 + 3*x*sqrt(y) - 8).is_polynomial(y) == False assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(2)).is_polynomial() == True assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(x)).is_polynomial() == False assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(2)).is_polynomial(x, y) == True assert ((x**2)*(y**2) + x*(y**2) + y*x + exp(x)).is_polynomial(x, y) == False def test_is_rational_function(): x,y = symbols('x y') assert Integer(1).is_rational_function() == True assert Integer(1).is_rational_function(x) == True assert Rational(17,54).is_rational_function() == True assert Rational(17,54).is_rational_function(x) == True assert (12/x).is_rational_function() == True assert (12/x).is_rational_function(x) == True assert (x/y).is_rational_function() == True assert (x/y).is_rational_function(x) == True assert (x/y).is_rational_function(x, y) == True assert (x**2+1/x/y).is_rational_function() == True assert (x**2+1/x/y).is_rational_function(x) == True assert (x**2+1/x/y).is_rational_function(x, y) == True assert (sin(y)/x).is_rational_function() == False assert (sin(y)/x).is_rational_function(y) == False assert (sin(y)/x).is_rational_function(x) == True assert (sin(y)/x).is_rational_function(x, y) == False def test_SAGE1(): #see http://code.google.com/p/sympy/issues/detail?id=247 class MyInt: def _sympy_(self): return Integer(5) m = MyInt() e = Rational(2)*m assert e == 10 raises(TypeError, "Rational(2)*MyInt") def test_SAGE2(): class MyInt(object): def __int__(self): return 5 assert sympify(MyInt()) == 5 e = Rational(2)*MyInt() assert e == 10 raises(TypeError, "Rational(2)*MyInt") def test_SAGE3(): class MySymbol: def __rmul__(self, other): return ('mys', other, self) o = MySymbol() e = x*o assert e == ('mys', x, o) def test_len(): x, y, z = symbols("x y z") e = x*y assert len(e.args) == 2 e = x+y+z assert len(e.args) == 3 def test_doit(): a = Integral(x**2, x) assert isinstance(a.doit(), Integral) == False assert isinstance(a.doit(integrals=True), Integral) == False assert isinstance(a.doit(integrals=False), Integral) == True assert (2*Integral(x, x)).doit() == x**2 def test_attribute_error(): raises(AttributeError, "x.cos()") raises(AttributeError, "x.sin()") raises(AttributeError, "x.exp()") def test_args(): assert (x*y).args in ((x, y), (y, x)) assert (x+y).args in ((x, y), (y, x)) assert (x*y+1).args in ((x*y, 1), (1, x*y)) assert sin(x*y).args == (x*y,) assert sin(x*y).args[0] == x*y assert (x**y).args == (x,y) assert (x**y).args[0] == x assert (x**y).args[1] == y def test_iter_basic_args(): assert list(sin(x*y).iter_basic_args()) == [x*y] assert list((x**y).iter_basic_args()) == [x, y] def test_noncommutative_expand_issue658(): A, B, C = symbols('A,B,C', commutative=False) assert A*B - B*A != 0 assert (A*(A+B)*B).expand() == A**2*B + A*B**2 assert (A*(A+B+C)*B).expand() == A**2*B + A*B**2 + A*C*B def test_as_numer_denom(): assert oo.as_numer_denom() == (1, 0) assert (-oo).as_numer_denom() == (-1, 0) assert zoo.as_numer_denom() == (zoo, 1) assert (-zoo).as_numer_denom() == (zoo, 1) assert x.as_numer_denom() == (x, 1) assert (1/x).as_numer_denom() == (1, x) assert (x/y).as_numer_denom() == (x, y) assert (x/2).as_numer_denom() == (x, 2) assert (x*y/z).as_numer_denom() == (x*y, z) assert (x/(y*z)).as_numer_denom() == (x, y*z) assert Rational(1, 2).as_numer_denom() == (1, 2) assert (1/y**2).as_numer_denom() == (1, y**2) assert (x/y**2).as_numer_denom() == (x, y**2) assert ((x**2+1)/y).as_numer_denom() == (x**2+1, y) assert (x*(y+1)/y**7).as_numer_denom() == (x*(y+1), y**7) assert (x**-2).as_numer_denom() == (1, x**2) n = symbols('n', negative=True) assert (x**n).as_numer_denom() == (x**n, 1) assert sqrt(1/n).as_numer_denom() == (I, sqrt(-n)) n = Symbol('0 or neg', nonpositive=True) assert ((x/n)**-S.Half).as_numer_denom() == (1, (x/n)**S.Half) def test_as_independent(): assert (2*x*sin(x)+y+x).as_independent(x) == (y, x + 2*x*sin(x)) assert (2*x*sin(x)+y+x).as_independent(y) == (x + 2*x*sin(x), y) assert (2*x*sin(x)+y+x).as_independent(x, y) == (0, y + x + 2*x*sin(x)) assert (x*sin(x)*cos(y)).as_independent(x) == (cos(y), x*sin(x)) assert (x*sin(x)*cos(y)).as_independent(y) == (x*sin(x), cos(y)) assert (x*sin(x)*cos(y)).as_independent(x, y) == (1, x*sin(x)*cos(y)) assert (sin(x)).as_independent(x) == (1, sin(x)) assert (sin(x)).as_independent(y) == (sin(x), 1) assert (2*sin(x)).as_independent(x) == (2, sin(x)) assert (2*sin(x)).as_independent(y) == (2*sin(x), 1) # issue 1804 = 1766b n1, n2, n3 = symbols('n1 n2 n3', commutative=False) assert (n1 + n1*n2).as_independent(n2) == (n1, n1*n2) assert (n2*n1 + n1*n2).as_independent(n2) == (0, n1*n2 + n2*n1) assert (n1*n2*n1).as_independent(n2) == (n1, n2*n1) assert (n1*n2*n1).as_independent(n1) == (1, n1*n2*n1) assert (3*x).as_independent(x, as_Add=True) == (0, 3*x) assert (3*x).as_independent(x, as_Add=False) == (3, x) assert (3+x).as_independent(x, as_Add=True) == (3, x) assert (3+x).as_independent(x, as_Add=False) == (1, 3 + x) # issue 2380 assert (3*x).as_independent(Symbol) == (3, x) # issue 2549 assert (n1*x*y).as_independent(x) == (n1*y, x) assert ((x + n1)*(x - y)).as_independent(x) == (1, (x + n1)*(x - y)) assert ((x + n1)*(x - y)).as_independent(y) == (x + n1, x - y) assert (DiracDelta(x - n1)*DiracDelta(x - y)).as_independent(x) == (1, DiracDelta(x - n1)*DiracDelta(x - y)) assert (x*y*n1*n2*n3).as_independent(n2) == (x*y*n1, n2*n3) assert (x*y*n1*n2*n3).as_independent(n1) == (x*y, n1*n2*n3) assert (x*y*n1*n2*n3).as_independent(n3) == (x*y*n1*n2, n3) assert (DiracDelta(x - n1)*DiracDelta(y - n1)*DiracDelta(x - n2)).as_independent(y) == \ (DiracDelta(x - n1), DiracDelta(y - n1)*DiracDelta(x - n2)) def test_subs_dict(): a,b,c,d,e = symbols('a,b,c,d,e') assert (sin(x))._subs_dict({ x : 1, sin(x) : 2}) == 2 assert (sin(x))._subs_dict([(x, 1), (sin(x), 2)]) == 2 expr = sqrt(sin(2*x))*sin(exp(x)*x)*cos(2*x) + sin(2*x) seq = [ (sqrt(sin(2*x)),a), (cos(2*x),b), (sin(2*x),c), (x,d), (exp(x),e) ] assert expr._subs_dict(seq) == c + a*b*sin(d*e) seq = [ (sqrt(sin(2*x)),a), (sin(2*x),c), (cos(2*x),b), (x,d), (exp(x),e) ] assert expr._subs_dict(seq) == c + a*b*sin(d*e) def test_subs_list(): assert (sin(x))._subs_list([(sin(x), 2), (x, 1)]) == 2 assert (sin(x))._subs_list([(x, 1), (sin(x), 2)]) == sin(1) assert (x+y)._subs_list([(x, 3), (y, x**2)]) == 3 + x**2 assert (x+y)._subs_list([(y, x**2), (x, 3)]) == 12 def test_call(): # Unlike what used to be the case, the following should NOT work. # See issue 1927. raises(TypeError, "sin(x)({ x : 1, sin(x) : 2})") raises(TypeError, "sin(x)(1)") def test_replace(): f = log(sin(x)) + tan(sin(x**2)) assert f.replace(sin, cos) == log(cos(x)) + tan(cos(x**2)) assert f.replace(sin, lambda a: sin(2*a)) == log(sin(2*x)) + tan(sin(2*x**2)) a = Wild('a') assert f.replace(sin(a), cos(a)) == log(cos(x)) + tan(cos(x**2)) assert f.replace(sin(a), lambda a: sin(2*a)) == log(sin(2*x)) + tan(sin(2*x**2)) g = 2*sin(x**3) assert g.replace(lambda expr: expr.is_Number, lambda expr: expr**2) == 4*sin(x**9) assert cos(x).replace(cos, sin, map=True) == (sin(x), {cos(x): sin(x)}) assert sin(x).replace(cos, sin) == sin(x) assert (y*sin(x)).replace(sin, lambda expr: sin(expr)/y) == sin(x) def test_find(): expr = (x + y + 2 + sin(3*x)) assert expr.find(lambda u: u.is_Integer) == set([S(2), S(3)]) assert expr.find(lambda u: u.is_Symbol) == set([x, y]) assert expr.find(lambda u: u.is_Integer, group=True) == {S(2): 1, S(3): 1} assert expr.find(lambda u: u.is_Symbol, group=True) == {x: 2, y: 1} assert expr.find(Integer) == set([S(2), S(3)]) assert expr.find(Symbol) == set([x, y]) assert expr.find(Integer, group=True) == {S(2): 1, S(3): 1} assert expr.find(Symbol, group=True) == {x: 2, y: 1} a = Wild('a') expr = sin(sin(x)) + sin(x) + cos(x) + x assert expr.find(lambda u: type(u) is sin) == set([sin(x), sin(sin(x))]) assert expr.find(lambda u: type(u) is sin, group=True) == {sin(x): 2, sin(sin(x)): 1} assert expr.find(sin(a)) == set([sin(x), sin(sin(x))]) assert expr.find(sin(a), group=True) == {sin(x): 2, sin(sin(x)): 1} assert expr.find(sin) == set([sin(x), sin(sin(x))]) assert expr.find(sin, group=True) == {sin(x): 2, sin(sin(x)): 1} def test_count(): expr = (x + y + 2 + sin(3*x)) assert expr.count(lambda u: u.is_Integer) == 2 assert expr.count(lambda u: u.is_Symbol) == 3 assert expr.count(Integer) == 2 assert expr.count(Symbol) == 3 a = Wild('a') assert expr.count(sin) == 1 assert expr.count(sin(a)) == 1 assert expr.count(lambda u: type(u) is sin) == 1 def test_has_any(): x,y,z,t,u = symbols('x y z t u') f = Function("f") g = Function("g") p = Wild('p') assert sin(x).has(x) assert sin(x).has(sin) assert not sin(x).has(y) assert not sin(x).has(cos) assert f(x).has(x) assert f(x).has(f) assert not f(x).has(y) assert not f(x).has(g) assert f(x).diff(x).has(x) assert f(x).diff(x).has(f) assert f(x).diff(x).has(Derivative) assert not f(x).diff(x).has(y) assert not f(x).diff(x).has(g) assert not f(x).diff(x).has(sin) assert (x**2).has(Symbol) assert not (x**2).has(Wild) assert (2*p).has(Wild) i = Integer(4400) assert i.has(x) is False assert (i*x**i).has(x) assert (i*y**i).has(x) is False assert (i*y**i).has(x, y) expr = x**2*y + sin(2**t + log(z)) assert expr.has(u) is False assert expr.has(x) assert expr.has(y) assert expr.has(z) assert expr.has(t) assert expr.has(x, y, z, t) assert expr.has(x, y, z, t, u) from sympy.physics.units import m, s assert (x*m/s).has(x) assert (x*m/s).has(y, z) is False poly = Poly(x**2 + x*y*sin(z), x, y, t) assert poly.has(x) assert poly.has(x, y, z) assert poly.has(x, y, z, t) assert FockState((x, y)).has(x) def test_as_poly_as_expr(): f = x**2 + 2*x*y assert f.as_poly().as_expr() == f assert f.as_poly(x, y).as_expr() == f assert (f + sin(x)).as_poly(x, y) is None p = Poly(f, x, y) assert p.as_poly() == p def test_nonzero(): assert bool(S.Zero) == False assert bool(S.One) == True assert bool(x) == True assert bool(x+y) == True assert bool(x-x) == False assert bool(x*y) == True assert bool(x*1) == True assert bool(x*0) == False def test_is_number(): assert Float(3.14).is_number == True assert Integer(737).is_number == True assert Rational(3, 2).is_number == True assert Rational(8).is_number == True assert x.is_number == False assert (2*x).is_number == False assert (x + y).is_number == False assert log(2).is_number == True assert log(x).is_number == False assert (2 + log(2)).is_number == True assert (8+log(2)).is_number == True assert (2 + log(x)).is_number == False assert (8+log(2)+x).is_number == False assert (1+x**2/x-x).is_number == True assert Tuple(Integer(1)).is_number == False assert Add(2, x).is_number == False assert Mul(3, 4).is_number == True assert Pow(log(2), 2).is_number == True assert oo.is_number == True g = WildFunction('g') assert g.is_number == False assert (2*g).is_number == False assert (x**2).subs(x, 3).is_number == True # test extensibility of .is_number # on subinstances of Basic class A(Basic): pass a = A() assert a.is_number == False def test_as_coeff_add(): x = Symbol('x') y = Symbol('y') assert S(2).as_coeff_add() == (2, ()) assert S(3.0).as_coeff_add() == (0, (S(3.0),)) assert S(-3.0).as_coeff_add() == (0, (S(-3.0),)) assert x .as_coeff_add() == ( 0, (x,)) assert (-1+x).as_coeff_add() == (-1, (x,)) assert ( 2+x).as_coeff_add() == ( 2, (x,)) assert ( 1+x).as_coeff_add() == ( 1, (x,)) assert (x + y).as_coeff_add(y) == (x, (y,)) assert (3*x).as_coeff_add(y) == (3*x, ()) # don't do expansion e = (x + y)**2 assert e.as_coeff_add(y) == (0, (e,)) def test_as_coeff_mul(): x = Symbol('x') y = Symbol('y') assert S(2).as_coeff_mul() == (2, ()) assert S(3.0).as_coeff_mul() == (1, (S(3.0),)) assert S(-3.0).as_coeff_mul() == (-1, (S(3.0),)) assert x .as_coeff_mul() == ( 1, (x,)) assert (-x).as_coeff_mul() == (-1, (x,)) assert (2*x).as_coeff_mul() == (2, (x,)) assert (x*y).as_coeff_mul(y) == (x, (y,)) assert (3 + x).as_coeff_mul(y) == (3 + x, ()) # don't do expansion e = exp(x + y) assert e.as_coeff_mul(y) == (1, (e,)) e = 2**(x + y) assert e.as_coeff_mul(y) == (1, (e,)) def test_as_coeff_exponent(): assert (3*x**4).as_coeff_exponent(x) == (3, 4) assert (2*x**3).as_coeff_exponent(x) == (2, 3) assert (4*x**2).as_coeff_exponent(x) == (4, 2) assert (6*x**1).as_coeff_exponent(x) == (6, 1) assert (3*x**0).as_coeff_exponent(x) == (3, 0) assert (2*x**0).as_coeff_exponent(x) == (2, 0) assert (1*x**0).as_coeff_exponent(x) == (1, 0) assert (0*x**0).as_coeff_exponent(x) == (0, 0) assert (-1*x**0).as_coeff_exponent(x) == (-1, 0) assert (-2*x**0).as_coeff_exponent(x) == (-2, 0) assert (2*x**3+pi*x**3).as_coeff_exponent(x) == (2+pi, 3) assert (x*log(2)/(2*x + pi*x)).as_coeff_exponent(x) == \ (log(2)/(2+pi), 0) # 1685 D = Derivative f = Function('f') fx = D(f(x), x) assert fx.as_coeff_exponent(f(x)) == (fx ,0) def test_extractions(): n = Symbol("n", integer=True) assert ((x*y)**3).extract_multiplicatively(x**2 * y) == x*y**2 assert ((x*y)**3).extract_multiplicatively(x**4 * y) == None assert (2*x).extract_multiplicatively(2) == x assert (2*x).extract_multiplicatively(3) == None assert (2*x).extract_multiplicatively(-1) == None assert (Rational(1,2)*x).extract_multiplicatively(3) == x/6 assert (x**(Rational(1,2))).extract_multiplicatively(x) == None assert (x**(Rational(1,2))).extract_multiplicatively(1/x) == x**(Rational(3,2)) assert ((x*y)**3).extract_additively(1) == None assert (x+1).extract_additively(x) == 1 assert (x+1).extract_additively(2*x) == None assert (x+1).extract_additively(-x) == 1+2*x assert (-x+1).extract_additively(2*x) == 1-3*x assert (Integer(-3)).could_extract_minus_sign() == True assert (-n*x+x).could_extract_minus_sign() != (n*x-x).could_extract_minus_sign() assert (x-y).could_extract_minus_sign() != (-x+y).could_extract_minus_sign() assert (1-x-y).could_extract_minus_sign() == True assert (1-x+y).could_extract_minus_sign() == False assert ((-x-x*y)/y).could_extract_minus_sign() == True assert (-(x+x*y)/y).could_extract_minus_sign() == True assert ((x+x*y)/(-y)).could_extract_minus_sign() == True assert ((x+x*y)/y).could_extract_minus_sign() == False assert (x*(-x-x**3)).could_extract_minus_sign() == True # used to give inf recurs assert ((-x-y)/(x+y)).could_extract_minus_sign() == True # is_Mul odd case # The results of each of these will vary on different machines, e.g. # the first one might be False and the other (then) is true or vice versa, # so both are included. assert ((-x-y)/(x-y)).could_extract_minus_sign() == False or\ ((-x-y)/(y-x)).could_extract_minus_sign() == False # is_Mul even case def test_coeff(): from sympy.abc import x, y, z from sympy import sqrt assert (x+1).coeff(x+1) == 1 assert (3*x).coeff(0) == None assert (z*(1+x)*x**2).coeff(1+x) == z*x**2 assert (1+2*x*x**(1+x)).coeff(x*x**(1+x)) == 2 assert (1+2*x**(y+z)).coeff(x**(y+z)) == 2 assert (3+2*x+4*x**2).coeff(1) == None assert (3+2*x+4*x**2).coeff(-1) == None assert (3+2*x+4*x**2).coeff(x) == 2 assert (3+2*x+4*x**2).coeff(x**2) == 4 assert (3+2*x+4*x**2).coeff(x**3) == None assert (-x/8 + x*y).coeff(x) == -S(1)/8 + y assert (-x/8 + x*y).coeff(-x) == S(1)/8 assert (4*x).coeff(2*x) == None assert (2*x).coeff(2*x) == 1 n1, n2 = symbols('n1 n2', commutative=False) assert (n1*n2).coeff(n1) == 1 assert (n1*n2).coeff(n2) == n1 assert (n1*n2 + x*n1).coeff(n1) == 1 # 1*n1*(n2+x) assert (n2*n1 + x*n1).coeff(n1) == n2 + x assert (n2*n1 + x*n1**2).coeff(n1) == n2 assert (n1**x).coeff(n1) == None assert (n1*n2 + n2*n1).coeff(n1) == None assert (2*(n1+n2)*n2).coeff(n1+n2, right=1) == n2 assert (2*(n1+n2)*n2).coeff(n1+n2, right=0) == 2 f = Function('f') assert (2*f(x) + 3*f(x).diff(x)).coeff(f(x)) == 2 expr = z*(x+y)**2 expr2 = z*(x+y)**2 + z*(2*x + 2*y)**2 assert expr.coeff(z) == (x+y)**2 assert expr.coeff(x+y) == None assert expr2.coeff(z) == (x+y)**2 + (2*x + 2*y)**2 assert (x + y + 3*z).coeff(1) == x + y assert (-x + 2*y).coeff(-1) == x assert (x - 2*y).coeff(-1) == 2*y assert (3 + 2*x + 4*x**2).coeff(1) == None assert (-x - 2*y).coeff(2) == -y assert (x + sqrt(2)*x).coeff(sqrt(2)) == x assert (3 + 2*x + 4*x**2).coeff(x) == 2 assert (3 + 2*x + 4*x**2).coeff(x**2) == 4 assert (3 + 2*x + 4*x**2).coeff(x**3) == None assert (z*(x + y)**2).coeff((x+y)**2) == z assert (z*(x + y)**2).coeff(x+y) == None assert (2 + 2*x + (x+1)*y).coeff(x+1) == y n, m, o, l = symbols('n m o l', commutative=False) assert n.coeff(n) == 1 assert y.coeff(n) == None assert (3*n).coeff(n) == 3 assert (2 + n).coeff(x*m) == None assert (2*x*n*m).coeff(x) == 2*n*m assert (2 + n).coeff(x*m*n + y) == None assert (2*x*n*m).coeff(3*n) == None assert (n*m + m*n*m).coeff(n) == 1 + m assert (n*m + m*n*m).coeff(n, right=True) == m # = (1 + m)*n*m assert (n*m + m*n).coeff(n) == None assert (n*m + o*m*n).coeff(m*n) == o assert (n*m + o*m*n).coeff(m*n, right=1) == 1 assert (n*m + n*m*n).coeff(n*m, right=1) == 1 + n # = n*m*(n + 1) def test_coeff2(): r, kappa = symbols('r, kappa') psi = Function("psi") g = 1/r**2 * (2*r*psi(r).diff(r, 1) + r**2 * psi(r).diff(r, 2)) g = g.expand() assert g.coeff((psi(r).diff(r))) == 2/r def test_coeff2_0(): r, kappa = symbols('r, kappa') psi = Function("psi") g = 1/r**2 * (2*r*psi(r).diff(r, 1) + r**2 * psi(r).diff(r, 2)) g = g.expand() assert g.coeff(psi(r).diff(r, 2)) == 1 def test_coeff_expand(): expr = z*(x+y)**2 expr2 = z*(x+y)**2 + z*(2*x + 2*y)**2 assert expr.coeff(z) == (x+y)**2 assert expr2.coeff(z) == (x+y)**2 + (2*x + 2*y)**2 def test_integrate(): assert x.integrate(x) == x**2/2 assert x.integrate((x, 0, 1)) == S(1)/2 def test_contains(): f = (x*y + 3/y)**(3 + 2) g = Function('g') h = Function('h') p = Piecewise( (g, x<-1), (1, x<=1), (f, True)) assert x in p assert y in p assert not z in p assert 1 in p assert 3 in p assert not 4 in p assert f in p assert g in p assert not h in p def test_as_base_exp(): assert x.as_base_exp() == (x, S.One) assert (x*y*z).as_base_exp() == (x*y*z, S.One) assert (x+y+z).as_base_exp() == (x+y+z, S.One) assert ((x+y)**z).as_base_exp() == (x+y, z) def test_issue1864(): assert hasattr(Mul(x, y), "is_commutative") assert hasattr(Mul(x, y, evaluate=False), "is_commutative") assert hasattr(Pow(x, y), "is_commutative") assert hasattr(Pow(x, y, evaluate=False), "is_commutative") expr = Mul(Pow(2, 2, evaluate=False), 3, evaluate=False) + 1 assert hasattr(expr, "is_commutative") def test_action_verbs(): a,b,c,d = symbols('a,b,c,d') assert nsimplify((1/(exp(3*pi*x/5)+1))) == (1/(exp(3*pi*x/5)+1)).nsimplify() assert ratsimp(1/x + 1/y) == (1/x + 1/y).ratsimp() assert trigsimp(log(x), deep=True) == (log(x)).trigsimp(deep = True) assert radsimp(1/(2+sqrt(2))) == (1/(2+sqrt(2))).radsimp() assert powsimp(x**y*x**z*y**z, combine='all') == (x**y*x**z*y**z).powsimp(combine='all') assert simplify(x**y*x**z*y**z) == (x**y*x**z*y**z).simplify() assert together(1/x + 1/y) == (1/x + 1/y).together() assert separate((x*(y*z)**3)**2) == ((x*(y*z)**3)**2).separate() assert collect(a*x**2 + b*x**2 + a*x - b*x + c, x) == (a*x**2 + b*x**2 + a*x - b*x + c).collect(x) assert apart(y/(y+2)/(y+1), y) == (y/(y+2)/(y+1)).apart(y) assert combsimp(y/(x+2)/(x+1)) == (y/(x+2)/(x+1)).combsimp() assert factor(x**2+5*x+6) == (x**2+5*x+6).factor() assert refine(sqrt(x**2)) == sqrt(x**2).refine() assert cancel((x**2+5*x+6)/(x+2)) == ((x**2+5*x+6)/(x+2)).cancel() def test_as_powers_dict(): assert x.as_powers_dict() == {x: 1} assert (x**y*z).as_powers_dict() == {x: y, z: 1} assert Mul(2, 2, **dict(evaluate=False)).as_powers_dict() == {S(2): S(2)} def test_new_rawargs(): x, y = symbols('x,y') n = Symbol('n', commutative=False) a = object.__new__(Add) assert 2 + x == a._new_rawargs(*[S(2), x]) assert x == a._new_rawargs(*[x]) assert 0 == a._new_rawargs() assert 0 == a._new_rawargs(*[]) assert a._new_rawargs(x).is_commutative assert a._new_rawargs(x, y).is_commutative assert a._new_rawargs(x, n).is_commutative is False assert a._new_rawargs(x, y, n).is_commutative is False a = x + n assert a.is_commutative is False assert a._new_rawargs(x).is_commutative assert a._new_rawargs(x, y).is_commutative assert a._new_rawargs(x, n).is_commutative is False assert a._new_rawargs(x, y, n).is_commutative is False m = object.__new__(Mul) assert 2*x == m._new_rawargs(*[S(2), x]) assert x == m._new_rawargs(*[x]) assert 1 == m._new_rawargs() assert 1 == m._new_rawargs(*[]) assert m._new_rawargs(x).is_commutative assert m._new_rawargs(x, y).is_commutative assert m._new_rawargs(x, n).is_commutative is False assert m._new_rawargs(x, y, n).is_commutative is False m = x*n assert m.is_commutative is False assert m._new_rawargs(x).is_commutative assert m._new_rawargs(n).is_commutative is False assert m._new_rawargs(x, y).is_commutative assert m._new_rawargs(x, n).is_commutative is False assert m._new_rawargs(x, y, n).is_commutative is False assert m._new_rawargs(x, n, reeval=False).is_commutative is False assert m._new_rawargs(S.One) is S.One def test_2127(): assert Add(evaluate=False) == 0 assert Mul(evaluate=False) == 1 assert Mul(x+y, evaluate=False).is_Add def test_symbols(): # symbols should return the free symbols of an object assert S(1).free_symbols == set() assert (x).free_symbols == set([x]) assert Integral(x, (x, 1, y)).free_symbols == set([y]) assert (-Integral(x, (x, 1, y))).free_symbols == set([y]) def test_issue2201(): x = Symbol('x', commutative=False) assert x*sqrt(2)/sqrt(6) == x*sqrt(3)/3 def test_issue_2061(): assert sqrt(-1.0*x) == 1.0*I*sqrt(x) assert sqrt(1.0*x) == 1.0*sqrt(x) def test_as_coeff_Mul(): Integer(3).as_coeff_Mul() == (Integer(3), Integer(1)) Rational(3, 4).as_coeff_Mul() == (Rational(3, 4), Integer(1)) Float(5.0).as_coeff_Mul() == (Float(5.0), Integer(1)) (Integer(3)*x).as_coeff_Mul() == (Integer(3), x) (Rational(3, 4)*x).as_coeff_Mul() == (Rational(3, 4), x) (Float(5.0)*x).as_coeff_Mul() == (Float(5.0), x) (Integer(3)*x*y).as_coeff_Mul() == (Integer(3), x*y) (Rational(3, 4)*x*y).as_coeff_Mul() == (Rational(3, 4), x*y) (Float(5.0)*x*y).as_coeff_Mul() == (Float(5.0), x*y) (x).as_coeff_Mul() == (S.One, x) (x*y).as_coeff_Mul() == (S.One, x*y) def test_expr_sorting(): f, g = symbols('f,g', cls=Function) exprs = [1/x**2, 1/x, sqrt(sqrt(x)), sqrt(x), x, x**Rational(3,2), x**2] assert sorted(exprs, key=default_sort_key) == exprs exprs = [x, 2*x, 2*x**2, 2*x**3, x**n, 2*x**n, sin(x), sin(x)**n, sin(x**2), cos(x), cos(x**2), tan(x)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [x + 1, x**2 + x + 1, x**3 + x**2 + x + 1] assert sorted(exprs, key=default_sort_key) == exprs exprs = [S(4), x - 3*I/2, x + 3*I/2, x - 4*I + 1, x + 4*I + 1] assert sorted(exprs, key=default_sort_key) == exprs exprs = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [f(x), g(x), exp(x), sin(x), cos(x), factorial(x)] assert sorted(exprs, key=default_sort_key) == exprs exprs = [Tuple(x, y), Tuple(x, z), Tuple(x, y, z)] assert sorted(exprs, key=default_sort_key) == exprs def test_as_ordered_factors(): f, g = symbols('f,g', cls=Function) assert x.as_ordered_factors() == [x] assert (2*x*x**n*sin(x)*cos(x)).as_ordered_factors() == [Integer(2), x, x**n, sin(x), cos(x)] args = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] expr = Mul(*args) assert expr.as_ordered_factors() == args A, B = symbols('A,B', commutative=False) assert (A*B).as_ordered_factors() == [A, B] assert (B*A).as_ordered_factors() == [B, A] def test_as_ordered_terms(): f, g = symbols('f,g', cls=Function) assert x.as_ordered_terms() == [x] assert (sin(x)**2*cos(x) + sin(x)*cos(x)**2 + 1).as_ordered_terms() == [sin(x)**2*cos(x), sin(x)*cos(x)**2, 1] args = [f(1), f(2), f(3), f(1, 2, 3), g(1), g(2), g(3), g(1, 2, 3)] expr = Add(*args) assert expr.as_ordered_terms() == args assert (1 + 4*sqrt(3)*pi*x).as_ordered_terms() == [4*pi*x*sqrt(3), 1] assert ( 2 + 3*I).as_ordered_terms() == [ 2, 3*I] assert (-2 + 3*I).as_ordered_terms() == [-2, 3*I] assert ( 2 - 3*I).as_ordered_terms() == [ 2, -3*I] assert (-2 - 3*I).as_ordered_terms() == [-2, -3*I] assert ( 4 + 3*I).as_ordered_terms() == [ 4, 3*I] assert (-4 + 3*I).as_ordered_terms() == [-4, 3*I] assert ( 4 - 3*I).as_ordered_terms() == [ 4, -3*I] assert (-4 - 3*I).as_ordered_terms() == [-4, -3*I] f = x**2*y**2 + x*y**4 + y + 2 assert f.as_ordered_terms(order="lex") == [x**2*y**2, x*y**4, y, 2] assert f.as_ordered_terms(order="grlex") == [x*y**4, x**2*y**2, y, 2] assert f.as_ordered_terms(order="rev-lex") == [2, y, x*y**4, x**2*y**2] assert f.as_ordered_terms(order="rev-grlex") == [2, y, x**2*y**2, x*y**4] def test_issue_1100(): # first subs and limit gives NaN a = x/y assert a._eval_interval(x, 0, oo)._eval_interval(y, oo, 0) is S.NaN # second subs and limit gives NaN assert a._eval_interval(x, 0, oo)._eval_interval(y, 0, oo) is S.NaN # difference gives S.NaN a = x - y assert a._eval_interval(x, 1, oo)._eval_interval(y, oo, 1) is S.NaN raises(ValueError, 'x._eval_interval(x, None, None)') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_priority.py0000644000175000017500000000404512014170666026466 0ustar georgeskgeorgeskfrom sympy import Expr, Symbol from sympy.core.decorators import call_highest_priority class Higher(Expr): _op_priority = 20.0 result = 'high' @call_highest_priority('__rmul__') def __mul__(self, other): return self.result @call_highest_priority('__mul__') def __rmul__(self, other): return self.result @call_highest_priority('__radd__') def __add__(self, other): return self.result @call_highest_priority('__add__') def __radd__(self, other): return self.result @call_highest_priority('__rsub__') def __sub__(self, other): return self.result @call_highest_priority('__sub__') def __rsub__(self, other): return self.result @call_highest_priority('__rpow__') def __pow__(self, other): return self.result @call_highest_priority('__pow__') def __rpow__(self, other): return self.result @call_highest_priority('__rdiv__') def __div__(self, other): return self.result @call_highest_priority('__div__') def __rdiv__(self, other): return self.result class Lower(Higher): _op_priority = 5.0 result = 'low' def test_mul(): x = Symbol('x') h = Higher() l = Lower() assert l*h == h*l == 'high' assert x*h == h*x == 'high' assert l*x == x*l != 'low' def test_add(): x = Symbol('x') h = Higher() l = Lower() assert l+h == h+l == 'high' assert x+h == h+x == 'high' assert l+x == x+l != 'low' def test_sub(): x = Symbol('x') h = Higher() l = Lower() assert l-h == h-l == 'high' assert x-h == h-x == 'high' assert l-x == -(x-l) != 'low' def test_pow(): x = Symbol('x') h = Higher() l = Lower() assert l**h == h**l == 'high' assert x**h == h**x == 'high' assert l**x != 'low' assert x**l != 'low' def test_div(): x = Symbol('x') h = Higher() l = Lower() #FIXME-py3k: AssertionError assert l/h == h/l == 'high' assert x/h == h/x == 'high' assert l/x != 'low' assert x/l != 'low' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_sympify.py0000644000175000017500000002551412014170666026311 0ustar georgeskgeorgeskfrom sympy import Symbol, exp, Integer, Float, sin, cos, log, Poly, Lambda, \ Function, I, S, sqrt, srepr, Rational, Tuple from sympy.abc import x, y from sympy.core.sympify import sympify, _sympify, SympifyError from sympy.core.decorators import _sympifyit from sympy.utilities.pytest import XFAIL, raises from sympy.geometry import Point, Line from sympy import mpmath def test_439(): v = sympify("exp(x)") x = Symbol("x") assert v == exp(x) assert type(v) == type(exp(x)) assert str(type(v)) == str(type(exp(x))) def test_sympify1(): assert sympify("x") == Symbol("x") assert sympify(" x") == Symbol("x") assert sympify(" x ") == Symbol("x") # 1778 n1 = Rational(1, 2) assert sympify('--.5') == n1 assert sympify('-1/2') == -n1 assert sympify('-+--.5') == -n1 assert sympify('-.[3]') == Rational(-1, 3) assert sympify('.[3]') == Rational(1, 3) assert sympify('+.[3]') == Rational(1, 3) assert sympify('+0.[3]*10**-2') == Rational(1, 300) assert sympify('.[052631578947368421]') == Rational(1, 19) assert sympify('.0[526315789473684210]') == Rational(1, 19) # options to make reals into rationals assert sympify('1.22[345]', rational=1) == \ 1 + Rational(22, 100) + Rational(345, 99900) assert sympify('2/2.6', rational=1) == Rational(10, 13) assert sympify('2.6/2', rational=1) == Rational(13, 10) assert sympify('2.6e2/17', rational=1) == Rational(260, 17) assert sympify('2.6e+2/17', rational=1) == Rational(260, 17) assert sympify('2.6e-2/17', rational=1) == Rational(26, 17000) assert sympify('2.1+3/4', rational=1) == Rational(21, 10) + Rational(3, 4) assert sympify('2.234456', rational=1) == Rational(279307, 125000) assert sympify('2.234456e23', rational=1) == 223445600000000000000000 assert sympify('2.234456e-23', rational=1) == Rational(279307, 12500000000000000000000000000) assert sympify('-2.234456e-23', rational=1) == Rational(-279307, 12500000000000000000000000000) assert sympify('12345678901/17', rational=1) == Rational(12345678901, 17) assert sympify('1/.3 + x', rational=1) == Rational(10, 3) + x # make sure longs in fractions work assert sympify('222222222222/11111111111') == Rational(222222222222, 11111111111) # ... even if they come from repetend notation assert sympify('1/.2[123456789012]') == Rational(333333333333, 70781892967) # ... or from high precision reals assert sympify('.1234567890123456', rational=1) == Rational(19290123283179, 156250000000000) def test_sympify_Fraction(): try: import fractions except ImportError: pass else: value = sympify(fractions.Fraction(101, 127)) assert value == Rational(101, 127) and type(value) is Rational def test_sympify_gmpy(): try: import gmpy except ImportError: pass else: value = sympify(gmpy.mpz(1000001)) assert value == Integer(1000001) and type(value) is Integer value = sympify(gmpy.mpq(101, 127)) assert value == Rational(101, 127) and type(value) is Rational def test_sympify_mpmath(): value = sympify(mpmath.mpf(1.0)) assert value == Float(1.0) and type(value) is Float dps = mpmath.mp.dps try: mpmath.mp.dps = 12 assert sympify(mpmath.pi).epsilon_eq(Float("3.14159265359"), Float("1e-12")) is True assert sympify(mpmath.pi).epsilon_eq(Float("3.14159265359"), Float("1e-13")) is False mpmath.mp.dps = 6 assert sympify(mpmath.pi).epsilon_eq(Float("3.14159"), Float("1e-5")) is True assert sympify(mpmath.pi).epsilon_eq(Float("3.14159"), Float("1e-6")) is False finally: mpmath.mp.dps = dps assert sympify(mpmath.mpc(1.0 + 2.0j)) == Float(1.0) + Float(2.0)*I def test_sympify2(): class A: def _sympy_(self): return Symbol("x")**3 a = A() assert _sympify(a)== x**3 assert sympify(a) == x**3 assert a == x**3 def test_sympify3(): assert sympify("x**3") == x**3 assert sympify("x^3") == x**3 assert sympify("1/2") == Integer(1)/2 raises(SympifyError, "_sympify('x**3')") raises(SympifyError, "_sympify('1/2')") def test_sympify_keywords(): raises(SympifyError, "sympify('if')") raises(SympifyError, "sympify('for')") raises(SympifyError, "sympify('while')") raises(SympifyError, "sympify('lambda')") def test_sympify_float(): assert sympify("1e-64") != 0 assert sympify("1e-20000") != 0 def test_sympify_bool(): """Test that sympify accepts boolean values and that output leaves them unchanged""" assert sympify(True) == True assert sympify(False)== False def test_sympyify_iterables(): ans = [Rational(3, 10), Rational(1, 5)] assert sympify(['.3', '.2'], rational=1) == ans assert sympify(set(['.3', '.2']), rational=1) == set(ans) assert sympify(tuple(['.3', '.2']), rational=1) == Tuple(*ans) assert sympify(['1', '2', ['3', '4']]) == [S(1), S(2), [S(3), S(4)]] def test_sympify4(): class A: def _sympy_(self): return Symbol("x") a = A() assert _sympify(a)**3== x**3 assert sympify(a)**3 == x**3 assert a == x def test_sympify_text(): assert sympify('some') == Symbol('some') assert sympify('core') == Symbol('core') assert sympify('True') == True assert sympify('False') == False assert sympify('Poly') == Poly assert sympify('sin') == sin def test_sympify_function(): assert sympify('factor(x**2-1, x)') == -(1-x)*(x+1) assert sympify('sin(pi/2)*cos(pi)') == -Integer(1) def test_sympify_poly(): p = Poly(x**2+x+1, x) assert _sympify(p) is p assert sympify(p) is p def test_sage(): # how to effectivelly test for the _sage_() method without having SAGE # installed? assert hasattr(x, "_sage_") assert hasattr(Integer(3), "_sage_") assert hasattr(sin(x), "_sage_") assert hasattr(cos(x), "_sage_") assert hasattr(x**2, "_sage_") assert hasattr(x+y, "_sage_") assert hasattr(exp(x), "_sage_") assert hasattr(log(x), "_sage_") def test_bug496(): a_ = sympify("a_") _a = sympify("_a") @XFAIL def test_lambda(): x = Symbol('x') assert sympify('lambda : 1') == Lambda((), 1) assert sympify('lambda x: 2*x') == Lambda(x, 2*x) assert sympify('lambda x, y: 2*x+y') == Lambda([x, y], 2*x+y) def test_lambda_raises(): raises(SympifyError, "_sympify('lambda : 1')") def test_sympify_raises(): raises(SympifyError, 'sympify("fx)")') def test__sympify(): x = Symbol('x') f = Function('f') # positive _sympify assert _sympify(x) is x assert _sympify(f) is f assert _sympify(1) == Integer(1) assert _sympify(0.5) == Float("0.5") assert _sympify(1+1j) == 1.0 + I*1.0 class A: def _sympy_(self): return Integer(5) a = A() assert _sympify(a) == Integer(5) # negative _sympify raises(SympifyError, "_sympify('1')") raises(SympifyError, "_sympify([1,2,3])") def test_sympifyit(): x = Symbol('x') y = Symbol('y') @_sympifyit('b', NotImplemented) def add(a, b): return a+b assert add(x, 1) == x + 1 assert add(x, 0.5) == x + Float('0.5') assert add(x, y) == x + y assert add(x, '1') == NotImplemented @_sympifyit('b') def add_raises(a, b): return a+b assert add_raises(x, 1) == x + 1 assert add_raises(x, 0.5) == x + Float('0.5') assert add_raises(x, y) == x + y raises(SympifyError, "add_raises(x, '1')") def test_int_float(): class F1_1(object): def __float__(self): return 1.1 class F1_1b(object): """ This class is still a float, even though it also implements __int__(). """ def __float__(self): return 1.1 def __int__(self): return 1 class F1_1c(object): """ This class is still a float, because it implements _sympy_() """ def __float__(self): return 1.1 def __int__(self): return 1 def _sympy_(self): return Float(1.1) class I5(object): def __int__(self): return 5 class I5b(object): """ This class implements both __int__() and __float__(), so it will be treated as Float in SymPy. One could change this behavior, by using float(a) == int(a), but deciding that integer-valued floats represent exact numbers is arbitrary and often not correct, so we do not do it. If, in the future, we decide to do it anyway, the tests for I5b need to be changed. """ def __float__(self): return 5.0 def __int__(self): return 5 class I5c(object): """ This class implements both __int__() and __float__(), but also a _sympy_() method, so it will be Integer. """ def __float__(self): return 5.0 def __int__(self): return 5 def _sympy_(self): return Integer(5) i5 = I5() i5b = I5b() i5c = I5c() f1_1 = F1_1() f1_1b = F1_1b() f1_1c = F1_1c() assert sympify(i5) == 5 assert isinstance(sympify(i5), Integer) assert sympify(i5b) == 5 assert isinstance(sympify(i5b), Float) assert sympify(i5c) == 5 assert isinstance(sympify(i5c), Integer) assert abs(sympify(f1_1) - 1.1) < 1e-5 assert abs(sympify(f1_1b) - 1.1) < 1e-5 assert abs(sympify(f1_1c) - 1.1) < 1e-5 assert _sympify(i5) == 5 assert isinstance(_sympify(i5), Integer) assert _sympify(i5b) == 5 assert isinstance(_sympify(i5b), Float) assert _sympify(i5c) == 5 assert isinstance(_sympify(i5c), Integer) assert abs(_sympify(f1_1) - 1.1) < 1e-5 assert abs(_sympify(f1_1b) - 1.1) < 1e-5 assert abs(_sympify(f1_1c) - 1.1) < 1e-5 def test_issue1034(): a = sympify('Integer(4)') assert a == Integer(4) assert a.is_Integer def test_issue883(): a = [3, 2.0] assert sympify(a) == [Integer(3), Float(2.0)] assert sympify(tuple(a)) == Tuple(Integer(3), Float(2.0)) assert sympify(set(a)) == set([Integer(3), Float(2.0)]) def test_S_sympify(): assert S(1)/2 == sympify(1)/2 assert (-2)**(S(1)/2) == sqrt(2)*I def test_issue1689(): assert srepr(S(1.0+0J)) == srepr(S(1.0)) == srepr(Float(1.0)) assert srepr(Float(1)) != srepr(Float(1.0)) def test_issue1699_None(): assert S(None) is None def test_issue1889_builtins(): C = Symbol('C') vars = {} vars['C'] = C exp1 = sympify('C') assert exp1 == C # Make sure it did not get mixed up with sympy.C exp2 = sympify('C', vars) assert exp2 == C # Make sure it did not get mixed up with sympy.C def test_geometry(): p = sympify(Point(0, 1)) assert p == Point(0, 1) and type(p) == Point L = sympify(Line(p, (1, 0))) assert L == Line((0, 1), (1, 0)) and type(L) == Line wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_symbol.py0000644000175000017500000001737412014170666026123 0ustar georgeskgeorgeskfrom sympy import (Symbol, Wild, Inequality, StrictInequality, pi, I, Rational, sympify, symbols, Dummy, S, Function, flatten) from sympy.utilities.pytest import raises, XFAIL def test_Symbol(): a = Symbol("a") x1 = Symbol("x") x2 = Symbol("x") xdummy1 = Dummy("x") xdummy2 = Dummy("x") assert a != x1 assert a != x2 assert x1 == x2 assert x1 != xdummy1 assert xdummy1 != xdummy2 assert Symbol("x") == Symbol("x") assert Dummy("x") != Dummy("x") d = symbols('d', cls=Dummy) assert isinstance(d, Dummy) c,d = symbols('c,d', cls=Dummy) assert isinstance(c, Dummy) assert isinstance(d, Dummy) raises(TypeError, 'Symbol()') def test_Dummy(): assert Dummy() != Dummy() Dummy._count = 0 d1 = Dummy() Dummy._count = 0 assert d1 == Dummy() def test_as_dummy_nondummy(): x = Symbol('x') x1 = x.as_dummy() assert x1 != x assert x1 != x.as_dummy() # assert x == x1.as_nondummy() x = Symbol('x', commutative = False) x1 = x.as_dummy() assert x1 != x assert x1.is_commutative == False # assert x == x1.as_nondummy() # issue 2446 x = Symbol('x', real=True, commutative=False) assert x.as_dummy().assumptions0 == x.assumptions0 def test_lt_gt(): x, y = Symbol('x'), Symbol('y') assert (x <= y) == Inequality(x, y) assert (x >= y) == Inequality(y, x) assert (x <= 0) == Inequality(x, 0) assert (x >= 0) == Inequality(0, x) assert (x < y) == StrictInequality(x, y) assert (x > y) == StrictInequality(y, x) assert (x < 0) == StrictInequality(x, 0) assert (x > 0) == StrictInequality(0, x) assert (x**2+4*x+1 > 0) == StrictInequality(0, x**2+4*x+1) def test_no_len(): # there should be no len for numbers x = Symbol('x') xxl = Symbol('xxl') raises(TypeError, "len(x)") raises(TypeError, "len(xxl)") def test_Wild_properties(): # these tests only include Atoms x = Symbol("x") y = Symbol("y") p = Symbol("p", positive=True) k = Symbol("k", integer=True) r = Symbol("r", real=True) n = Symbol("n", integer=True, positive=True) given_patterns = [ x, y, p, k, -k, n, -n, sympify(-3), sympify(3), pi, Rational(3,2), I ] integerp = lambda k : k.is_integer positivep = lambda k : k.is_positive symbolp = lambda k : k.is_Symbol realp = lambda k : k.is_real S = Wild("S", properties=[symbolp]) R = Wild("R", properties=[realp]) Y = Wild("Y", exclude=[x,p,k,n]) P = Wild("P", properties=[positivep]) K = Wild("K", properties=[integerp]) N = Wild("N", properties=[positivep, integerp]) given_wildcards = [ S, R, Y, P, K, N ] goodmatch = { S : (x,y,p,k,n), R : (p,k,-k,n,-n,-3,3,pi,Rational(3,2)), Y : (y,-3,3,pi,Rational(3,2),I ), P : (p, n,3,pi, Rational(3,2)), K : (k,-k,n,-n,-3,3), N : (n,3)} for A in given_wildcards: for pat in given_patterns: d = pat.match(A) if pat in goodmatch[A]: assert d[A] in goodmatch[A] else: assert d == None @XFAIL def test_symbols_each_char(): # XXX: Because of the way the warnings filters work, this will fail if it's # run more than once in the same session. See issue 2492. import warnings # each_char is deprecated and emits a warning. w = Symbol('w') x = Symbol('x') y = Symbol('y') z = Symbol('z') # First, test the warning warnings.filterwarnings("error", "The each_char option to symbols\(\) and var\(\) is " "deprecated. Separate symbol names by spaces or commas instead.") raises(DeprecationWarning, "symbols('xyz', each_char=True)") raises(DeprecationWarning, "symbols('xyz', each_char=False)") # now test the actual output warnings.filterwarnings("ignore", "The each_char option to symbols\(\) and var\(\) is " "deprecated. Separate symbol names by spaces or commas instead.") assert symbols(['wx', 'yz'], each_char=True) == [(w, x), (y, z)] assert all(w.is_Function for w in flatten(symbols(['wx', 'yz'], each_char=True, cls=Function))) assert symbols('xyz', each_char=True) == (x, y, z) assert symbols('x,', each_char=True) == (x,) assert symbols('x y z', each_char=True) == symbols('x,y,z', each_char=True) == (x, y, z) assert symbols('xyz', each_char=False) == Symbol('xyz') a, b = symbols('x y', each_char=False, real=True) assert a.is_real and b.is_real assert 'each_char' not in a.assumptions0 assert symbols('x0:0', each_char=False) == () assert symbols('x0:1', each_char=False) == (Symbol('x0'),) assert symbols('x0:3', each_char=False) == (Symbol('x0'), Symbol('x1'), Symbol('x2')) assert symbols('x:0', each_char=False) == () assert symbols('x:1', each_char=False) == (Symbol('x0'),) assert symbols('x:3', each_char=False) == (Symbol('x0'), Symbol('x1'), Symbol('x2')) assert symbols('x1:1', each_char=False) == () assert symbols('x1:2', each_char=False) == (Symbol('x1'),) assert symbols('x1:3', each_char=False) == (Symbol('x1'), Symbol('x2')) # Keep testing reasonably thread safe, so reset the warning warnings.filterwarnings("default", "The each_char option to symbols\(\) and var\(\) is " "deprecated. Separate symbol names by spaces or commas instead.") # Note, in Python 2.6+, this can be done more nicely using the # warnings.catch_warnings context manager. # See http://docs.python.org/library/warnings#testing-warnings. def test_symbols(): x = Symbol('x') y = Symbol('y') z = Symbol('z') assert symbols('x') == x assert symbols('x ') == x assert symbols(' x ') == x assert symbols('x,') == (x,) assert symbols('x, ') == (x,) assert symbols('x ,') == (x,) assert symbols('x , y') == (x, y) assert symbols('x,y,z') == (x, y, z) assert symbols('x y z') == (x, y, z) assert symbols('x,y,z,') == (x, y, z) assert symbols('x y z ') == (x, y, z) xyz = Symbol('xyz') abc = Symbol('abc') assert symbols('xyz') == xyz assert symbols('xyz,') == (xyz,) assert symbols('xyz,abc') == (xyz, abc) assert symbols(('xyz',)) == (xyz,) assert symbols(('xyz,',)) == ((xyz,),) assert symbols(('x,y,z,',)) == ((x, y, z),) assert symbols(('xyz', 'abc')) == (xyz, abc) assert symbols(('xyz,abc',)) == ((xyz, abc),) assert symbols(('xyz,abc', 'x,y,z')) == ((xyz, abc), (x, y, z)) assert symbols(('x', 'y', 'z')) == (x, y, z) assert symbols(['x', 'y', 'z']) == [x, y, z] assert symbols(set(['x', 'y', 'z'])) == set([x, y, z]) raises(ValueError, "symbols('')") raises(ValueError, "symbols(',')") raises(ValueError, "symbols('x,,y,,z')") raises(ValueError, "symbols(('x', '', 'y', '', 'z'))") a, b = symbols('x,y', real=True) assert a.is_real and b.is_real x0 = Symbol('x0') x1 = Symbol('x1') x2 = Symbol('x2') y0 = Symbol('y0') y1 = Symbol('y1') assert symbols('x0:0') == () assert symbols('x0:1') == (x0,) assert symbols('x0:2') == (x0, x1) assert symbols('x0:3') == (x0, x1, x2) assert symbols('x:0') == () assert symbols('x:1') == (x0,) assert symbols('x:2') == (x0, x1) assert symbols('x:3') == (x0, x1, x2) assert symbols('x1:1') == () assert symbols('x1:2') == (x1,) assert symbols('x1:3') == (x1, x2) assert symbols('x1:3,x,y,z') == (x1, x2, x, y, z) assert symbols('x:3,y:2') == (x0, x1, x2, y0, y1) assert symbols(('x:3', 'y:2')) == ((x0, x1, x2), (y0, y1)) a = Symbol('a') b = Symbol('b') c = Symbol('c') d = Symbol('d') assert symbols('x:z') == (x, y, z) assert symbols('a:d,x:z') == (a, b, c, d, x, y, z) assert symbols(('a:d', 'x:z')) == ((a, b, c, d), (x, y, z)) def test_call(): f = Symbol('f') assert f(2) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_evalf.py0000644000175000017500000002600612014170666025703 0ustar georgeskgeorgeskfrom sympy.core.evalf import PrecisionExhausted, complex_accuracy from sympy import pi, I, Symbol, Add, Rational, exp, sqrt, sin, cos, \ fibonacci, Integral, oo, E, atan, log, integrate, floor, ceiling, \ factorial, binomial, Sum, zeta, Catalan, Pow, GoldenRatio, sympify, \ sstr, Function, Eq, Mul, Pow, Derivative from sympy.mpmath.libmp.libmpf import from_float from sympy.utilities.pytest import raises x = Symbol('x') y = Symbol('y') n = Symbol('n') def NS(e, n=15, **options): return sstr(sympify(e).evalf(n, **options), full_prec=True) def test_evalf_helpers(): assert complex_accuracy((from_float(2.0),None,35,None)) == 35 assert complex_accuracy((from_float(2.0),from_float(10.0),35,100)) == 37 assert complex_accuracy((from_float(2.0),from_float(1000.0),35,100)) == 43 assert complex_accuracy((from_float(2.0),from_float(10.0),100,35)) == 35 assert complex_accuracy((from_float(2.0),from_float(1000.0),100,35)) == 35 def test_evalf_basic(): assert NS('pi',15) == '3.14159265358979' assert NS('2/3',10) == '0.6666666667' assert NS('355/113-pi',6) == '2.66764e-7' assert NS('16*atan(1/5)-4*atan(1/239)', 15) == '3.14159265358979' def test_cancellation(): assert NS(Add(pi,Rational(1,10**1000),-pi,evaluate=False),15,maxn=1200) == '1.00000000000000e-1000' def test_evalf_powers(): assert NS('pi**(10**20)',10) == '1.339148777e+49714987269413385435' assert NS(pi**(10**100),10) == ('4.946362032e+4971498726941338543512682882' '9089887365167832438044244613405349992494711208' '95526746555473864642912223') assert NS('2**(1/10**50)',15) == '1.00000000000000' assert NS('2**(1/10**50)-1',15) == '6.93147180559945e-51' # Evaluation of Rump's ill-conditioned polynomial def test_evalf_rump(): a = 1335*y**6/4+x**2*(11*x**2*y**2-y**6-121*y**4-2)+11*y**8/2+x/(2*y) assert NS(a, 15, subs={x:77617, y:33096}) == '-0.827396059946821' def test_evalf_complex(): assert NS('2*sqrt(pi)*I',10) == '3.544907702*I' assert NS('3+3*I',15) == '3.00000000000000 + 3.00000000000000*I' assert NS('E+pi*I',15) == '2.71828182845905 + 3.14159265358979*I' assert NS('pi * (3+4*I)',15) == '9.42477796076938 + 12.5663706143592*I' assert NS('I*(2+I)',15) == '-1.00000000000000 + 2.00000000000000*I' #assert NS('(pi+E*I)*(E+pi*I)',15) in ('.0e-15 + 17.25866050002*I', '.0e-17 + 17.25866050002*I', '-.0e-17 + 17.25866050002*I') assert NS('(pi+E*I)*(E+pi*I)',15,chop=True) == '17.2586605000200*I' def test_evalf_complex_powers(): assert NS('(E+pi*I)**100000000000000000') == \ '-3.58896782867793e+61850354284995199 + 4.58581754997159e+61850354284995199*I' # XXX: rewrite if a+a*I simplification introduced in sympy #assert NS('(pi + pi*I)**2') in ('.0e-15 + 19.7392088021787*I', '.0e-16 + 19.7392088021787*I') assert NS('(pi + pi*I)**2', chop=True) == '19.7392088021787*I' assert NS('(pi + 1/10**8 + pi*I)**2') == '6.2831853e-8 + 19.7392088650106*I' assert NS('(pi + 1/10**12 + pi*I)**2') == '6.283e-12 + 19.7392088021850*I' #assert NS('(pi + pi*I)**4') == '-389.63636413601 + .0e-14*I' assert NS('(pi + pi*I)**4', chop=True) == '-389.636364136010' assert NS('(pi + 1/10**8 + pi*I)**4') == '-389.636366616512 + 2.4805021e-6*I' assert NS('(pi + 1/10**12 + pi*I)**4') == '-389.636364136258 + 2.481e-10*I' assert NS('(10000*pi + 10000*pi*I)**4', chop=True) == '-3.89636364136010e+18' def test_evalf_exponentiation(): assert NS(sqrt(-pi)) == '1.77245385090552*I' assert NS(Pow(pi*I, Rational(1,2), evaluate=False)) == '1.25331413731550 + 1.25331413731550*I' assert NS(pi**I) == '0.413292116101594 + 0.910598499212615*I' assert NS(pi**(E+I/3)) == '20.8438653991931 + 8.36343473930031*I' assert NS((pi+I/3)**(E+I/3)) == '17.2442906093590 + 13.6839376767037*I' assert NS(exp(pi)) == '23.1406926327793' assert NS(exp(pi+E*I)) == '-21.0981542849657 + 9.50576358282422*I' assert NS(pi**pi) == '36.4621596072079' assert NS((-pi)**pi) == '-32.9138577418939 - 15.6897116534332*I' assert NS((-pi)**(-pi)) == '-0.0247567717232697 + 0.0118013091280262*I' # An example from Smith, "Multiple Precision Complex Arithmetic and Functions" def test_evalf_complex_cancellation(): A = Rational('63287/100000') B = Rational('52498/100000') C = Rational('69301/100000') D = Rational('83542/100000') F = Rational('2231321613/2500000000') # XXX: the number of returned mantissa digits in the real part could # change with the implementation. What matters is that the returned digits are # correct. assert NS((A+B*I)*(C+D*I),6) == '6.44862e-6 + 0.892529*I' assert NS((A+B*I)*(C+D*I),10) == '6.447099821e-6 + 0.8925286452*I' assert NS((A+B*I)*(C+D*I) - F*I, 5) in ('6.4471e-6 - .0e-15*I', '6.4471e-6 + .0e-15*I') def test_evalf_logs(): assert NS("log(3+pi*I)", 15) == '1.46877619736226 + 0.808448792630022*I' assert NS("log(pi*I)", 15) == '1.14472988584940 + 1.57079632679490*I' def test_evalf_trig(): assert NS('sin(1)',15) == '0.841470984807897' assert NS('cos(1)',15) == '0.540302305868140' assert NS('sin(10**-6)',15) == '9.99999999999833e-7' assert NS('cos(10**-6)',15) == '0.999999999999500' assert NS('sin(E*10**100)',15) == '0.409160531722613' # Some input near roots assert NS(sin(exp(pi*sqrt(163))*pi), 15) == '-2.35596641936785e-12' assert NS(sin(pi*10**100 + Rational(7,10**5), evaluate=False), 15, maxn=120) == \ '6.99999999428333e-5' assert NS(sin(Rational(7,10**5), evaluate=False), 15) == \ '6.99999999428333e-5' # Check detection of various false identities def test_evalf_near_integers(): # Binet's formula f = lambda n: ((1+sqrt(5))**n)/(2**n * sqrt(5)) assert NS(f(5000) - fibonacci(5000), 10, maxn=1500) == '5.156009964e-1046' # Some near-integer identities from # http://mathworld.wolfram.com/AlmostInteger.html assert NS('sin(2017*2**(1/5))',15) == '-1.00000000000000' assert NS('sin(2017*2**(1/5))',20) == '-0.99999999999999997857' assert NS('1+sin(2017*2**(1/5))',15) == '2.14322287389390e-17' assert NS('45 - 613*E/37 + 35/991', 15) == '6.03764498766326e-11' def test_evalf_ramanujan(): assert NS(exp(pi*sqrt(163)) - 640320**3 - 744, 10) == '-7.499274028e-13' # A related identity A = 262537412640768744*exp(-pi*sqrt(163)) B = 196884*exp(-2*pi*sqrt(163)) C = 103378831900730205293632*exp(-3*pi*sqrt(163)) assert NS(1-A-B+C,10) == '1.613679005e-59' # Input that for various reasons have failed at some point def test_evalf_bugs(): assert NS(sin(1)+exp(-10**10),10) == NS(sin(1),10) assert NS(exp(10**10)+sin(1),10) == NS(exp(10**10),10) assert NS('log(1+1/10**50)',20) == '1.0000000000000000000e-50' assert NS('log(10**100,10)',10) == '100.0000000' assert NS('log(2)',10) == '0.6931471806' assert NS('(sin(x)-x)/x**3', 15, subs={x:'1/10**50'}) == '-0.166666666666667' assert NS(sin(1)+Rational(1,10**100)*I,15) == '0.841470984807897 + 1.00000000000000e-100*I' assert x.evalf() == x assert NS((1+I)**2*I,6) == '-2.00000 + 2.32831e-10*I' d={n: (-1)**Rational(6,7), y: (-1)**Rational(4,7), x: (-1)**Rational(2,7)} assert NS((x*(1+y*(1 + n))).subs(d).evalf(),6) == '0.346011 + 0.433884*I' assert NS(((-I-sqrt(2)*I)**2).evalf()) == '-5.82842712474619' assert NS((1+I)**2*I,15) == '-2.00000000000000 + 2.16840434497101e-19*I' #1659 (1/2): assert NS(pi.evalf(69) - pi) == '-4.43863937855894e-71' #1659 (2/2): With the bug present, this still only fails if the # terms are in the order given here. This is not generally the case, # because the order depends on the hashes of the terms. assert NS(20 - 5008329267844*n**25 - 477638700*n**37 - 19*n, subs={n:.01}) == '19.8100000000000' def test_evalf_integer_parts(): a = floor(log(8)/log(2) - exp(-1000), evaluate=False) b = floor(log(8)/log(2), evaluate=False) raises(PrecisionExhausted, "a.evalf()") assert a.evalf(chop=True) == 3 assert a.evalf(maxn=500) == 2 raises(PrecisionExhausted, "b.evalf()") raises(PrecisionExhausted, "b.evalf(maxn=500)") assert b.evalf(chop=True) == 3 assert int(floor(factorial(50)/E,evaluate=False).evalf()) == \ 11188719610782480504630258070757734324011354208865721592720336800L assert int(ceiling(factorial(50)/E,evaluate=False).evalf()) == \ 11188719610782480504630258070757734324011354208865721592720336801L assert int(floor((GoldenRatio**999 / sqrt(5) + Rational(1,2))).evalf(1000)) == fibonacci(999) assert int(floor((GoldenRatio**1000 / sqrt(5) + Rational(1,2))).evalf(1000)) == fibonacci(1000) def test_evalf_trig_zero_detection(): a = sin(160*pi, evaluate=False) t = a.evalf(maxn=100) assert abs(t) < 1e-100 assert t._prec < 2 assert a.evalf(chop=True) == 0 raises(PrecisionExhausted, "a.evalf(strict=True)") def test_evalf_divergent_series(): n = Symbol('n', integer=True) raises(ValueError, 'Sum(1/n, (n, 1, oo)).evalf()') raises(ValueError, 'Sum(n/(n**2+1), (n, 1, oo)).evalf()') raises(ValueError, 'Sum((-1)**n, (n, 1, oo)).evalf()') raises(ValueError, 'Sum((-1)**n, (n, 1, oo)).evalf()') raises(ValueError, 'Sum(n**2, (n, 1, oo)).evalf()') raises(ValueError, 'Sum(2**n, (n, 1, oo)).evalf()') raises(ValueError, 'Sum((-2)**n, (n, 1, oo)).evalf()') raises(ValueError, 'Sum((2*n+3)/(3*n**2+4), (n,0, oo)).evalf()') raises(ValueError, 'Sum((0.5*n**3)/(n**4+1),(n,0,oo)).evalf()') def test_evalf_py_methods(): assert abs(float(pi+1) - 4.1415926535897932) < 1e-10 assert abs(complex(pi+1) - 4.1415926535897932) < 1e-10 assert abs(complex(pi+E*I) - (3.1415926535897931+2.7182818284590451j)) < 1e-10 raises(ValueError, "float(pi+x)") raises(ValueError, "complex(pi+x)") def test_evalf_power_subs_bugs(): assert (x**2).evalf(subs={x:0}) == 0 assert sqrt(x).evalf(subs={x:0}) == 0 assert (x**Rational(2,3)).evalf(subs={x:0}) == 0 assert (x**x).evalf(subs={x:0}) == 1 assert (3**x).evalf(subs={x:0}) == 1 assert exp(x).evalf(subs={x:0}) == 1 assert ((2+I)**x).evalf(subs={x:0}) == 1 assert (0**x).evalf(subs={x:0}) == 1 def test_evalf_arguments(): raises(TypeError, 'pi.evalf(method="garbage")') def test_implemented_function_evalf(): from sympy.utilities.lambdify import implemented_function f = Function('f') x = Symbol('x') f = implemented_function(f, lambda x: x + 1) assert str(f(x)) == "f(x)" assert str(f(2)) == "f(2)" assert f(2).evalf() == 3 assert f(x).evalf() == f(x) del f._imp_ # XXX: due to caching _imp_ would influence all other tests def test_evaluate_false(): for no in [[], 0, False, None]: assert Add(3, 2, evaluate=no).is_Add assert Mul(3, 2, evaluate=no).is_Mul assert Pow(3, 2, evaluate=no).is_Pow assert Pow(y, 2, evaluate=True) - Pow(y, 2, evaluate=True) == 0 def test_evalf_relational(): assert Eq(x/5, y/10).evalf() == Eq(0.2*x, 0.1*y) def test_issue_2387(): assert not cos(sqrt(0.5 + I)).n().is_Function def test_issue_2387_bug(): from sympy import I, Expr assert abs(Expr._from_mpmath(I._to_mpmath(15), 15) - I) < 1.0e-15 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/core/tests/test_sets.py0000644000175000017500000004136212014170666025566 0ustar georgeskgeorgeskfrom sympy import ( Symbol, Set, Union, Interval, oo, S, sympify, nan, Inequality, Max, Min, And, Or, Eq, Ge, Le, Gt, Lt, Float, FiniteSet ) from sympy.mpmath import mpi from sympy.utilities.pytest import raises def test_interval_arguments(): assert Interval(0, oo) == Interval(0, oo, False, True) assert Interval(0, oo).right_open == True assert Interval(-oo, 0) == Interval(-oo, 0, True, False) assert Interval(-oo, 0).left_open == True assert isinstance(Interval(1, 1), FiniteSet) assert Interval(1, 0) == S.EmptySet assert Interval(1, 1).measure == 0 assert Interval(1, 1, False, True) == S.EmptySet assert Interval(1, 1, True, False) == S.EmptySet assert Interval(1, 1, True, True) == S.EmptySet raises(ValueError, "Interval(0, S.ImaginaryUnit)") raises(ValueError, "Interval(0, Symbol('z'))") assert isinstance(Interval(1, Symbol('a', real=True)), Interval) def test_interval_symbolic_end_points(): a = Symbol('a', real=True) assert Union(Interval(0, a), Interval(0, 3)).sup == Max(a, 3) assert Union(Interval(a, 0), Interval(-3, 0)).inf == Min(-3, a) assert Interval(0, a).contains(1) == Inequality(1, a) def test_union(): assert Union(Interval(1, 2), Interval(2, 3)) == Interval(1, 3) assert Union(Interval(1, 2), Interval(2, 3, True)) == Interval(1, 3) assert Union(Interval(1, 3), Interval(2, 4)) == Interval(1, 4) assert Union(Interval(1, 2), Interval(1, 3)) == Interval(1, 3) assert Union(Interval(1, 3), Interval(1, 2)) == Interval(1, 3) assert Union(Interval(1, 3, False, True), Interval(1, 2)) == \ Interval(1, 3, False, True) assert Union(Interval(1, 3), Interval(1, 2, False, True)) == Interval(1, 3) assert Union(Interval(1, 2, True), Interval(1, 3)) == Interval(1, 3) assert Union(Interval(1, 2, True), Interval(1, 3, True)) == Interval(1, 3, True) assert Union(Interval(1, 2, True), Interval(1, 3, True, True)) == \ Interval(1, 3, True, True) assert Union(Interval(1, 2, True, True), Interval(1, 3, True)) == \ Interval(1, 3, True) assert Union(Interval(1, 3), Interval(2, 3)) == Interval(1, 3) assert Union(Interval(1, 3, False, True), Interval(2, 3)) == \ Interval(1, 3) assert Union(Interval(1, 2, False, True), Interval(2, 3, True)) != \ Interval(1, 3) assert Union(Interval(1, 2), S.EmptySet) == Interval(1, 2) assert Union(S.EmptySet) == S.EmptySet assert Union(Interval(0,1), [FiniteSet(1.0/n) for n in range(1,10)]) == \ Interval(0,1) assert Interval(1, 2).union(Interval(2, 3)) == \ Interval(1, 2) + Interval(2, 3) assert Interval(1, 2).union(Interval(2, 3)) == Interval(1, 3) assert Union(Set()) == Set() assert FiniteSet(1) + FiniteSet(2) + FiniteSet(3) == FiniteSet(1,2,3) assert FiniteSet(['ham']) + FiniteSet(['eggs']) == FiniteSet('ham', 'eggs') assert FiniteSet(1,2,3) + S.EmptySet == FiniteSet(1,2,3) assert FiniteSet(1,2,3) & FiniteSet(2,3,4) == FiniteSet(2,3) assert FiniteSet(1,2,3) | FiniteSet(2,3,4) == FiniteSet(1,2,3,4) # Test that Intervals and FiniteSets play nicely assert Interval(1,3) + FiniteSet(2) == Interval(1,3) assert Interval(1,3, True,True) + FiniteSet(3) == Interval(1,3, True,False) X = Interval(1,3)+FiniteSet(5) Y = Interval(1,2)+FiniteSet(3) XandY = X.intersect(Y) assert 2 in X and 3 in X and 3 in XandY assert X.subset(XandY) and Y.subset(XandY) raises(TypeError, "Union(1, 2, 3)") def test_difference(): assert Interval(1, 3) - Interval(1, 2) == Interval(2, 3, True) assert Interval(1, 3) - Interval(2, 3) == Interval(1, 2, False, True) assert Interval(1, 3, True) - Interval(2, 3) == Interval(1, 2, True, True) assert Interval(1, 3, True) - Interval(2, 3, True) == \ Interval(1, 2, True, False) assert Interval(0,2) - FiniteSet(1) == \ Union(Interval(0,1,False, True), Interval(1,2, True, False)) assert FiniteSet(1,2,3) - FiniteSet(2) == FiniteSet(1,3) assert FiniteSet('ham', 'eggs') - FiniteSet(['eggs']) == FiniteSet(['ham']) assert FiniteSet(1,2,3,4) - Interval(2,10, True, False) == FiniteSet(1,2) assert FiniteSet(1,2,3,4) - S.EmptySet == FiniteSet(1,2,3,4) assert Union(Interval(0,2), FiniteSet(2,3,4)) - Interval(1,3) == \ Union(Interval(0,1,False, True), FiniteSet(4)) def test_complement(): assert Interval(0, 1).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, oo, True, True)) assert Interval(0, 1, True, False).complement == \ Union(Interval(-oo, 0, True, False), Interval(1, oo, True, True)) assert Interval(0, 1, False, True).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, oo, False, True)) assert Interval(0, 1, True, True).complement == \ Union(Interval(-oo, 0, True, False), Interval(1, oo, False, True)) assert -S.EmptySet == S.EmptySet.complement assert ~S.EmptySet == S.EmptySet.complement assert S.EmptySet.complement == Interval(-oo, oo) assert Union(Interval(0, 1), Interval(2, 3)).complement == \ Union(Interval(-oo, 0, True, True), Interval(1, 2, True, True), Interval(3, oo, True, True)) assert FiniteSet(0).complement == Union(Interval(-oo,0, True,True) , Interval(0,oo, True, True)) assert (FiniteSet(5) + Interval(S.NegativeInfinity, 0)).complement == \ Interval(0, 5, True, True) + Interval(5, S.Infinity, True,True) assert FiniteSet(1,2,3).complement == Interval(S.NegativeInfinity,1, True,True) + Interval(1,2, True,True) + Interval(2,3, True,True) + Interval(3,S.Infinity, True,True) X = Interval(1,3)+FiniteSet(5) assert X.intersect(X.complement) == S.EmptySet square = Interval(0,1) * Interval(0,1) notsquare = square.complement assert all(pt in square for pt in [(0,0), (.5,.5), (1,0), (1,1)]) assert not any(pt in notsquare for pt in [(0,0), (.5,.5), (1,0), (1,1)]) assert not any(pt in square for pt in [(-1,0), (1.5,.5), (10,10)]) assert all(pt in notsquare for pt in [(-1,0), (1.5,.5), (10,10)]) def test_intersect(): x = Symbol('x') assert Interval(0, 2).intersect(Interval(1, 2)) == Interval(1, 2) assert Interval(0, 2).intersect(Interval(1, 2, True)) == \ Interval(1, 2, True) assert Interval(0, 2, True).intersect(Interval(1, 2)) == \ Interval(1, 2, False, False) assert Interval(0, 2, True, True).intersect(Interval(1, 2)) == \ Interval(1, 2, False, True) assert Interval(0, 2).intersect(Union(Interval(0, 1), Interval(2, 3))) == \ Union(Interval(0, 1), Interval(2, 2)) assert FiniteSet(1,2,x).intersect(FiniteSet(x)) == FiniteSet(x) assert FiniteSet('ham', 'eggs').intersect(FiniteSet(['ham'])) == \ FiniteSet(['ham']) assert FiniteSet(1,2,3,4,5).intersect(S.EmptySet) == S.EmptySet assert Interval(0,5).intersect(FiniteSet(1,3)) == FiniteSet(1,3) assert Interval(0,1, True, True).intersect(FiniteSet(1)) == S.EmptySet assert Union(Interval(0, 1), Interval(2, 3)).intersect(Interval(1, 2)) == \ Union(Interval(1, 1), Interval(2, 2)) assert Union(Interval(0, 1), Interval(2, 3)).intersect(Interval(0, 2)) == \ Union(Interval(0, 1), Interval(2, 2)) assert Union(Interval(0, 1), Interval(2, 3)).intersect(Interval(1, 2, True, True)) == \ S.EmptySet assert Union(Interval(0, 1), Interval(2, 3)).intersect(S.EmptySet) == \ S.EmptySet assert Union(Interval(0,5), FiniteSet(['Ham'])).intersect( FiniteSet(2,3,4,5,6)) == FiniteSet(2,3,4,5) def test_interval_subs(): a = Symbol('a', real=True) assert Interval(0, a).subs(a, 2) == Interval(0, 2) assert Interval(a, 0).subs(a, 2) == S.EmptySet def test_interval_to_mpi(): assert Interval(0, 1).to_mpi() == mpi(0, 1) assert Interval(0, 1, True, False).to_mpi() == mpi(0, 1) def test_measure(): a = Symbol('a', real=True) assert Interval(1, 3).measure == 2 assert Interval(0, a).measure == a assert Interval(1, a).measure == a - 1 assert Union(Interval(1, 2), Interval(3, 4)).measure == 2 assert Union(Interval(1, 2), Interval(3, 4), FiniteSet(5,6,7)).measure\ == 2 assert FiniteSet(1,2,oo,a,-oo,-5).measure == 0 assert S.EmptySet.measure == 0 square = Interval(0,10) * Interval(0,10) offsetsquare = Interval(5,15) * Interval(5,15) band = Interval(-oo,oo) * Interval(2,4) assert square.measure == offsetsquare.measure == 100 assert (square + offsetsquare).measure == 175 # there is some overlap assert (square - offsetsquare).measure == 75 assert (square * FiniteSet(1,2,3)).measure == 0 assert (square.intersect(band)).measure == 20 assert (square + band).measure == oo assert (band * FiniteSet(1,2,3)).measure == nan def test_subset(): assert Interval(0, 2).subset(Interval(0, 1)) == True assert Interval(0, 2).subset(Interval(0, 3)) == False assert FiniteSet(1,2,3,4).subset(FiniteSet(1,2)) assert FiniteSet(1,2,3,4).subset(FiniteSet(4,5)) == False assert Interval(0,2).subset(FiniteSet(1)) assert Interval(0,2,True,True).subset(FiniteSet(1,2)) == False assert (Interval(0,2,False,True)+FiniteSet(2,3)).subset( Interval(1,2)+FiniteSet(3)) assert Union(Interval(0, 1), Interval(2, 5)).subset(Interval(3, 4)) == True assert Union(Interval(0, 1), Interval(2, 5)).subset(Interval(3, 6)) == False assert Interval(0,5).subset(FiniteSet(1,2,3,4)) == True assert FiniteSet(1,2,3).subset(S.EmptySet) == True assert S.EmptySet.subset(Interval(0, 1)) == False assert S.EmptySet.subset(S.EmptySet) == True raises(ValueError, "S.EmptySet.subset(1)") def test_contains(): assert Interval(0, 2).contains(1) == True assert Interval(0, 2).contains(3) == False assert Interval(0, 2, True, False).contains(0) == False assert Interval(0, 2, True, False).contains(2) == True assert Interval(0, 2, False, True).contains(0) == True assert Interval(0, 2, False, True).contains(2) == False assert Interval(0, 2, True, True).contains(0) == False assert Interval(0, 2, True, True).contains(2) == False assert FiniteSet(1,2,3).contains(2) assert FiniteSet(1,2,Symbol('x')).contains(Symbol('x')) items = [1,2,S.Infinity, 'ham', -1.1, Interval] assert all(item in FiniteSet(items) for item in items) assert Union(Interval(0, 1), Interval(2, 5)).contains(3) == True assert Union(Interval(0, 1), Interval(2, 5)).contains(6) == False assert Union(Interval(0, 1), FiniteSet(2, 5)).contains(3) == False assert S.EmptySet.contains(1) == False def test_interval_symbolic(): x = Symbol('x') e = Interval(0, 1) assert e.contains(x) == And(0<=x, x<=1) raises(TypeError, "x in e") e = Interval(0, 1, True, True) assert e.contains(x) == And(0>> from sympy.integrals.rationaltools import ratint >>> from sympy.abc import x >>> ratint(36/(x**5 - 2*x**4 - 2*x**3 + 4*x**2 + x - 2), x) (12*x + 6)/(x**2 - 1) + 4*log(x - 2) - 4*log(x + 1) References ========== .. [Bro05] M. Bronstein, Symbolic Integration I: Transcendental Functions, Second Edition, Springer-Verlag, 2005, pp. 35-70 """ if type(f) is not tuple: p, q = f.as_numer_denom() else: p, q = f p, q = Poly(p, x, composite=False), Poly(q, x, composite=False) coeff, p, q = p.cancel(q) poly, p = p.div(q) result = poly.integrate(x).as_expr() if p.is_zero: return coeff*result g, h = ratint_ratpart(p, q, x) P, Q = h.as_numer_denom() P = Poly(P, x) Q = Poly(Q, x) q, r = P.div(Q) result += g + q.integrate(x).as_expr() if not r.is_zero: symbol = flags.get('symbol', 't') if not isinstance(symbol, Symbol): t = Dummy(symbol) else: t = symbol L = ratint_logpart(r, Q, x, t) real = flags.get('real') if real is None: if type(f) is not tuple: atoms = f.atoms() else: p, q = f atoms = p.atoms() \ | q.atoms() for elt in atoms - set([x]): if not elt.is_real: real = False break else: real = True eps = S(0) if not real: for h, q in L: eps += RootSum(q, Lambda(t, t*log(h.as_expr())), quadratic=True) else: for h, q in L: R = log_to_real(h, q, x, t) if R is not None: eps += R else: eps += RootSum(q, Lambda(t, t*log(h.as_expr())), quadratic=True) result += eps return coeff*result def ratint_ratpart(f, g, x): """Horowitz-Ostrogradsky algorithm. Given a field K and polynomials f and g in K[x], such that f and g are coprime and deg(f) < deg(g), returns fractions A and B in K(x), such that f/g = A' + B and B has square-free denominator. """ f = Poly(f, x) g = Poly(g, x) u, v, _ = g.cofactors(g.diff()) n = u.degree() m = v.degree() d = g.degree() A_coeffs = [ Dummy('a' + str(n-i)) for i in xrange(0, n) ] B_coeffs = [ Dummy('b' + str(m-i)) for i in xrange(0, m) ] C_coeffs = A_coeffs + B_coeffs A = Poly(A_coeffs, x, domain=ZZ[C_coeffs]) B = Poly(B_coeffs, x, domain=ZZ[C_coeffs]) H = f - A.diff()*v + A*(u.diff()*v).quo(u) - B*u result = solve(H.coeffs(), C_coeffs) A = A.as_expr().subs(result) B = B.as_expr().subs(result) rat_part = cancel(A/u.as_expr(), x) log_part = cancel(B/v.as_expr(), x) return rat_part, log_part def ratint_logpart(f, g, x, t=None): """Lazard-Rioboo-Trager algorithm. Given a field K and polynomials f and g in K[x], such that f and g are coprime, deg(f) < deg(g) and g is square-free, returns a list of tuples (s_i, q_i) of polynomials, for i = 1..n, such that s_i in K[t, x] and q_i in K[t], and: ___ ___ d f d \ ` \ ` -- - = -- ) ) a log(s_i(a, x)) dx g dx /__, /__, i=1..n a | q_i(a) = 0 """ f, g = Poly(f, x), Poly(g, x) t = t or Dummy('t') a, b = g, f - g.diff()*Poly(t, x) R = subresultants(a, b) res = Poly(resultant(a, b), t, composite=False) R_map, H = {}, [] for r in R: R_map[r.degree()] = r def _include_sign(c, sqf): if c < 0: h, k = sqf[0] sqf[0] = h*c, k C, res_sqf = res.sqf_list() _include_sign(C, res_sqf) for q, i in res_sqf: _, q = q.primitive() if g.degree() == i: H.append((g, q)) else: h = R_map[i] h_lc = Poly(h.LC(), t, field=True) c, h_lc_sqf = h_lc.sqf_list(all=True) _include_sign(c, h_lc_sqf) for a, j in h_lc_sqf: h = h.quo(Poly(a.gcd(q)**j, x)) inv, coeffs = h_lc.invert(q), [S(1)] for coeff in h.coeffs()[1:]: T = (inv*coeff).rem(q) coeffs.append(T.as_expr()) h = Poly(dict(zip(h.monoms(), coeffs)), x) H.append((h, q)) return H def log_to_atan(f, g): """Convert complex logarithms to real arctangents. Given a real field K and polynomials f and g in K[x], with g != 0, returns a sum h of arctangents of polynomials in K[x], such that: df d f + I g -- = -- I log( ------- ) dx dx f - I g """ if f.degree() < g.degree(): f, g = -g, f f = f.to_field() g = g.to_field() p, q = f.div(g) if q.is_zero: return 2*atan(p.as_expr()) else: s, t, h = g.gcdex(-f) u = (f*s+g*t).quo(h) A = 2*atan(u.as_expr()) return A + log_to_atan(s, t) def log_to_real(h, q, x, t): """Convert complex logarithms to real functions. Given real field K and polynomials h in K[t,x] and q in K[t], returns real function f such that: ___ df d \ ` -- = -- ) a log(h(a, x)) dx dx /__, a | q(a) = 0 """ u, v = symbols('u,v') H = h.as_expr().subs({t:u+I*v}).expand() Q = q.as_expr().subs({t:u+I*v}).expand() H_map = collect(H, I, evaluate=False) Q_map = collect(Q, I, evaluate=False) a, b = H_map.get(S(1), S(0)), H_map.get(I, S(0)) c, d = Q_map.get(S(1), S(0)), Q_map.get(I, S(0)) R = Poly(resultant(c, d, v), u) R_u = roots(R, filter='R') if len(R_u) != R.count_roots(): return None result = S(0) for r_u in R_u.iterkeys(): C = Poly(c.subs({u:r_u}), v) R_v = roots(C, filter='R') if len(R_v) != C.count_roots(): return None for r_v in R_v: if not r_v.is_positive: continue D = d.subs({u:r_u, v:r_v}) if D.evalf(chop=True) != 0: continue A = Poly(a.subs({u:r_u, v:r_v}), x) B = Poly(b.subs({u:r_u, v:r_v}), x) AB = (A**2 + B**2).as_expr() result += r_u*log(AB) + r_v*log_to_atan(A, B) R_q = roots(q, filter='R') if len(R_q) != q.count_roots(): return None for r in R_q.iterkeys(): result += r*log(h.as_expr().subs(t, r)) return result wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/trigonometry.py0000644000175000017500000002374312014170666026214 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- import sympy from sympy.core import Dummy, Wild, S from sympy.core.numbers import Rational from sympy.functions import sin, cos, binomial from sympy.core.cache import cacheit # TODO add support for tan^m(x) * sec^n(x) # TODO sin(a*x)*cos(b*x) -> sin((a+b)x) + sin((a-b)x) ? # creating each time Wild's and sin/cos/Mul is expensive. Also, our match & # subs are very slow when not cached, and if we create Wild each time, we # effectively block caching. # # so we cache the pattern @cacheit def _pat_sincos(x): a, n, m = [Wild(s, exclude=[x]) for s in 'anm'] pat = sin(a*x)**n * cos(a*x)**m return pat, a,n,m _u = Dummy('u') def trigintegrate(f, x): """Integrate f = Mul(trig) over x >>> from sympy import Symbol, sin, cos >>> from sympy.integrals.trigonometry import trigintegrate >>> from sympy.abc import x >>> trigintegrate(sin(x)*cos(x), x) sin(x)**2/2 >>> trigintegrate(sin(x)**2, x) x/2 - sin(x)*cos(x)/2 http://en.wikibooks.org/wiki/Calculus/Further_integration_techniques """ pat, a,n,m = _pat_sincos(x) ##m - cos ##n - sin M = f.match(pat) if M is None: return n, m = M[n], M[m] # should always be there if n is S.Zero and m is S.Zero: return x a = M[a] if n.is_integer and m.is_integer: if n.is_odd or m.is_odd: u = _u n_, m_ = n.is_odd, m.is_odd # take smallest n or m -- to choose simplest substitution if n_ and m_: n_ = n_ and (n < m) # NB: careful here, one of the m_ = m_ and not (n < m) # conditions *must* be true # n m u=C (n-1)/2 m # S(x) * C(x) dx --> -(1-u^2) * u du if n_: ff = -(1-u**2)**((n-1)/2) * u**m uu = cos(a*x) # n m u=S n (m-1)/2 # S(x) * C(x) dx --> u * (1-u^2) du elif m_: ff = u**n * (1-u**2)**((m-1)/2) uu = sin(a*x) fi= sympy.integrals.integrate(ff, u) # XXX cyclic deps fx= fi.subs(u, uu) return fx / a # n & m are even else: # 2k 2m 2l 2l # we transform S (x) * C (x) into terms with only S (x) or C (x) # # example: # 100 4 100 2 2 100 4 2 # S (x) * C (x) = S (x) * (1-S (x)) = S (x) * (1 + S (x) - 2*S (x)) # # 104 102 100 # = S (x) - 2*S (x) + S (x) # 2k # then S is integrated with recursive formula # take largest n or m -- to choose simplest substitution n_ = (abs(n) > abs(m)) m_ = (abs(m) > abs(n)) res = S.Zero if n_: # 2k 2 k i 2i # C = (1-S ) = sum(i, (-) * B(k,i) * S ) if m > 0 : for i in range(0,m/2+1): res += (-1)**i * binomial(m/2,i) * sin_pow_integrate(n+2*i, x) elif m == 0: res=sin_pow_integrate(n,x) else: # m < 0 , |n| > |m| # / / # | | # | m n -1 m+1 n-1 n - 1 | m+2 n-2 # | cos (x) sin (x) dx = ________ cos (x) sin (x) + _______ | cos (x) sin (x) dx # | | # | m + 1 m + 1 | #/ / # # res=Rational(-1,m+1)*cos(x)**(m+1)*sin(x)**(n-1) + Rational(n-1,m+1)*trigintegrate(cos(x)**(m+2)*sin(x)**(n-2),x) elif m_: # 2k 2 k i 2i # S = (1 -C ) = sum(i, (-) * B(k,i) * C ) if n > 0: # / / # | | # | m n | -m n # | cos (x)*sin (x) dx or | cos (x) * sin (x) dx # | | # / / # # |m| > |n| ; m,n >0 ; m,n belong to Z - {0} # n 2 # sin (x) term is expanded here interms of cos (x), and then integrated. for i in range(0,n/2+1): res += (-1)**i * binomial(n/2,i) * cos_pow_integrate(m+2*i, x) elif n == 0 : ## / ## | # | 1 # | _ _ _ # | m # | cos (x) # / res= cos_pow_integrate(m,x) else: # n < 0 , |m| > |n| # / / # | | # | m n 1 m-1 n+1 m - 1 | m-2 n+2 # | cos (x) sin (x) dx = _______ cos (x) sin (x) + _______ | cos (x) sin (x) dx # | | # | n + 1 n + 1 | #/ / # # res= Rational(1,(n+1))*cos(x)**(m-1)*sin(x)**(n+1) + Rational(m-1,n+1)*trigintegrate(cos(x)**(m-2)*sin(x)**(n+2),x) else : if m == n: ##Substitute sin(2x)/2 for sin(x)cos(x) and then Integrate. res=sympy.integrals.integrate((Rational(1,2)*sin(2*x))**m,x) elif (m == -n): if n < 0: ##Same as the scheme described above. res= Rational(1,(n+1))*cos(x)**(m-1)*sin(x)**(n+1) + Rational(m-1,n+1)*sympy.integrals.integrate(cos(x)**(m-2)*sin(x)**(n+2),x) ##the function argument to integrate in the end will be 1 , this cannot be integrated by trigintegrate. Hence use sympy.integrals.integrate. else: res=Rational(-1,m+1)*cos(x)**(m+1)*sin(x)**(n-1) + Rational(n-1,m+1)*sympy.integrals.integrate(cos(x)**(m+2)*sin(x)**(n-2),x) return res.subs(x, a*x) / a def sin_pow_integrate(n,x): if n > 0 : if n == 1: #Recursion break return -cos(x) # # n > 0 # / / # | | # | n -1 n-1 n - 1 | n-2 # | sin (x) dx = ______ cos (x) sin (x) + _______ | sin (x) dx # | | # | n n | #/ / # # return Rational(-1,n)*cos(x)*sin(x)**(n-1)+Rational(n-1,n)*sin_pow_integrate(n-2,x) if n < 0: if n == -1: ##Make sure this does not come back here again. ##Recursion breaks here or at n==0. return trigintegrate(1/sin(x),x) # # n < 0 # / / # | | # | n 1 n+1 n + 2 | n+2 # | sin (x) dx = _______ cos (x) sin (x) + _______ | sin (x) dx # | | # | n + 1 n + 1 | #/ / # # return Rational(1,n+1)*cos(x)*sin(x)**(n+1) + Rational(n+2,n+1) * sin_pow_integrate(n+2,x) else: #n == 0 #Recursion break. return x def cos_pow_integrate(n,x): if n > 0 : if n==1: #Recursion break. return sin(x) # n > 0 # / / # | | # | n 1 n-1 n - 1 | n-2 # | sin (x) dx = ______ sin (x) cos (x) + _______ | cos (x) dx # | | # | n n | #/ / # # return Rational(1,n)*sin(x)*cos(x)**(n-1)+Rational(n-1,n)*cos_pow_integrate(n-2,x) if n < 0: if n == -1: ##Recursion break return trigintegrate(1/cos(x),x) # # n < 0 # / / # | | # | n -1 n+1 n + 2 | n+2 # | cos (x) dx = _______ sin (x) cos (x) + _______ | cos (x) dx # | | # | n + 1 n + 1 | #/ / # # return Rational(-1,n+1)*sin(x)*cos(x)**(n+1) + Rational(n+2,n+1) * cos_pow_integrate(n+2,x) else : # n == 0 #Recursion Break. return x wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/deltafunctions.py0000644000175000017500000001153112014170666026464 0ustar georgeskgeorgeskimport sympy from sympy.functions import DiracDelta, Heaviside from sympy.simplify.simplify import simplify from sympy.solvers import solve from sympy.utilities.misc import default_sort_key def change_mul(node, x): """change_mul(node, x) Rearranges the operands of a product, bringing to front any simple DiracDelta expression. If no simple DiracDelta expression was found, then all the DiracDelta expressions are simplified (using DiracDelta.simplify). Return: (dirac, new node) Where: o dirac is either a simple DiracDelta expression or None (if no simple expression was found); o new node is either a simplified DiracDelta expressions or None (if it could not be simplified). Examples -------- >>> from sympy import DiracDelta, cos >>> from sympy.integrals.deltafunctions import change_mul >>> from sympy.abc import x, y >>> change_mul(x*y*DiracDelta(x)*cos(x),x) (DiracDelta(x), x*y*cos(x)) >>> change_mul(x*y*DiracDelta(x**2-1)*cos(x),x) (None, x*y*cos(x)*DiracDelta(x - 1)/2 + x*y*cos(x)*DiracDelta(x + 1)/2) >>> change_mul(x*y*DiracDelta(cos(x))*cos(x),x) (None, None) """ if not node.is_Mul: return node new_args = [] dirac = None sorted_args = list(node.args) sorted_args.sort(key=default_sort_key) for arg in sorted_args: if arg.func == DiracDelta and arg.is_simple(x) \ and (len(arg.args) <= 1 or arg.args[1]==0): if dirac is None: dirac = arg else: new_args.append(arg) else: new_args.append(change_mul(arg, x)) if not dirac: # there was no simple dirac new_args = [] for arg in sorted_args: if arg.func == DiracDelta: new_args.append(arg.simplify(x)) else: new_args.append(change_mul(arg, x)) if new_args != sorted_args: nnode = node.__class__(*new_args).expand() else: #if the node didn't change there is nothing to do nnode = None return (None, nnode) return (dirac, node.func(*new_args)) def deltaintegrate(f, x): """The idea for integration is the following: -If we are dealing with a DiracDelta expression, i.e.: DiracDelta(g(x)), we try to simplify it. If we could simplify it, then we integrate the resulting expression. We already know we can integrate a simplified expression, because only simple DiracDelta expressions are involved. If we couldn't simplify it, there are two cases: 1) The expression is a simple expression, then we return the integral Taking care if we are dealing with a Derivative or with a proper DiracDelta 2) The expression is not simple(i.e. DiracDelta(cos(x))), we can do nothing at all -If the node is a multiplication node having a DiracDelta term First we expand it. If the expansion did work, the we try to integrate the expansion If not, we try to extract a simple DiracDelta term, then we have two cases 1)We have a simple DiracDelta term, so we return the integral 2)We didn't have a simple term, but we do have an expression with simplified DiracDelta terms, so we integrate this expression """ if not f.has(DiracDelta): return None # g(x) = DiracDelta(h(x)) if f.func == DiracDelta: h = f.simplify(x) if h == f:#can't simplify the expression #FIXME: the second term tells whether is DeltaDirac or Derivative #For integrating derivatives of DiracDelta we need the chain rule if f.is_simple(x): if (len(f.args) <= 1 or f.args[1]==0): return Heaviside(f.args[0]) else: return (DiracDelta(f.args[0],f.args[1]-1)/ f.args[0].as_poly().LC()) else:#let's try to integrate the simplified expression fh = sympy.integrals.integrate(h, x) return fh elif f.is_Mul: #g(x)=a*b*c*f(DiracDelta(h(x)))*d*e g = f.expand() if f != g:#the expansion worked fh = sympy.integrals.integrate(g, x) if fh and not isinstance(fh, sympy.integrals.Integral): return fh else:#no expansion performed, try to extract a simple DiracDelta term dg, rest_mult = change_mul(f, x) if not dg: if rest_mult: fh = sympy.integrals.integrate(rest_mult, x) return fh else: dg = dg.simplify(x) if dg.is_Mul: # Take out any extracted factors dg, rest_mult_2 = change_mul(dg, x) rest_mult = rest_mult*rest_mult_2 point = solve(dg.args[0],x)[0] return (rest_mult.subs(x, point)*Heaviside(x - point)) return None wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/risch.py0000644000175000017500000003175212014170666024561 0ustar georgeskgeorgeskfrom sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.symbol import Symbol, Wild, Dummy from sympy.core.basic import C, sympify from sympy.core.numbers import Rational, I, pi from sympy.core.singleton import S from sympy.functions import exp, sin , cos , tan , cot , asin, acos, atan from sympy.functions import log, sinh, cosh, tanh, coth, asinh, acosh from sympy.functions import sqrt, erf from sympy.solvers import solve from sympy.polys import quo, gcd, lcm, \ monomials, factor, cancel, PolynomialError from sympy.polys.polyroots import root_factors from sympy.core.compatibility import reduce def components(f, x): """Returns a set of all functional components of the given expression which includes symbols, function applications and compositions and non-integer powers. Fractional powers are collected with with minimal, positive exponents. >>> from sympy import cos, sin >>> from sympy.abc import x, y >>> from sympy.integrals.risch import components >>> components(sin(x)*cos(x)**2, x) set([x, sin(x), cos(x)]) """ result = set() if f.has(x): if f.is_Symbol: result.add(f) elif f.is_Function or f.is_Derivative: for g in f.args: result |= components(g, x) result.add(f) elif f.is_Pow: result |= components(f.base, x) if not f.exp.is_Integer: if f.exp.is_Rational: result.add(f.base**Rational(1, f.exp.q)) else: result |= components(f.exp, x) | set([f]) else: for g in f.args: result |= components(g, x) return result # name -> [] of symbols _symbols_cache = {} # NB @cacheit is not convenient here def _symbols(name, n): """get vector of symbols local to this module""" try: lsyms = _symbols_cache[name] except KeyError: lsyms = [] _symbols_cache[name] = lsyms while len(lsyms) < n: lsyms.append( Dummy('%s%i' % (name, len(lsyms))) ) return lsyms[:n] def heurisch(f, x, **kwargs): """Compute indefinite integral using heuristic Risch algorithm. This is a heuristic approach to indefinite integration in finite terms using the extended heuristic (parallel) Risch algorithm, based on Manuel Bronstein's "Poor Man's Integrator". The algorithm supports various classes of functions including transcendental elementary or special functions like Airy, Bessel, Whittaker and Lambert. Note that this algorithm is not a decision procedure. If it isn't able to compute the antiderivative for a given function, then this is not a proof that such a functions does not exist. One should use recursive Risch algorithm in such case. It's an open question if this algorithm can be made a full decision procedure. This is an internal integrator procedure. You should use toplevel 'integrate' function in most cases, as this procedure needs some preprocessing steps and otherwise may fail. Specification ============ heurisch(f, x, rewrite=False, hints=None) where f : expression x : symbol rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh' hints -> a list of functions that may appear in anti-derivate - hints = None --> no suggestions at all - hints = [ ] --> try to figure out - hints = [f1, ..., fn] --> we know better Examples ======== >>> from sympy import tan >>> from sympy.integrals.risch import heurisch >>> from sympy.abc import x, y >>> heurisch(y*tan(x), x) y*log(tan(x)**2 + 1)/2 See Manuel Bronstein's "Poor Man's Integrator": [1] http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html For more information on the implemented algorithm refer to: [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration Method and its Implementation in Maple, Proceedings of ISSAC'89, ACM Press, 212-217. [3] J. H. Davenport, On the Parallel Risch Algorithm (I), Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157. [4] J. H. Davenport, On the Parallel Risch Algorithm (III): Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch Algorithm (II), ACM Transactions on Mathematical Software 11 (1985), 356-362. """ f = sympify(f) if not f.is_Add: indep, f = f.as_independent(x) else: indep = S.One if not f.has(x): return indep * f * x rewritables = { (sin, cos, cot) : tan, (sinh, cosh, coth) : tanh, } rewrite = kwargs.pop('rewrite', False) if rewrite: for candidates, rule in rewritables.iteritems(): f = f.rewrite(candidates, rule) else: for candidates in rewritables.iterkeys(): if f.has(*candidates): break else: rewrite = True terms = components(f, x) hints = kwargs.get('hints', None) if hints is not None: if not hints: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) c = Wild('c', exclude=[x]) for g in set(terms): if g.is_Function: if g.func is exp: M = g.args[0].match(a*x**2) if M is not None: terms.add(erf(sqrt(-M[a])*x)) M = g.args[0].match(a*x**2 + b*x + c) if M is not None: if M[a].is_positive: terms.add(sqrt(pi/4*(-M[a]))*exp(M[c]-M[b]**2/(4*M[a]))* \ erf(-sqrt(-M[a])*x + M[b]/(2*sqrt(-M[a])))) elif M[a].is_negative: terms.add(sqrt(pi/4*(-M[a]))*exp(M[c]-M[b]**2/(4*M[a]))* \ erf(sqrt(-M[a])*x - M[b]/(2*sqrt(-M[a])))) M = g.args[0].match(a*log(x)**2) if M is not None: if M[a].is_positive: terms.add(-I*erf(I*(sqrt(M[a])*log(x)+1/(2*sqrt(M[a]))))) if M[a].is_negative: terms.add(erf(sqrt(-M[a])*log(x)-1/(2*sqrt(-M[a])))) elif g.is_Pow: if g.exp.is_Rational and g.exp.q == 2: M = g.base.match(a*x**2 + b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(asinh(sqrt(M[a]/M[b])*x)) elif M[a].is_negative: terms.add(asin(sqrt(-M[a]/M[b])*x)) M = g.base.match(a*x**2 - b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(acosh(sqrt(M[a]/M[b])*x)) elif M[a].is_negative: terms.add((-M[b]/2*sqrt(-M[a])*\ atan(sqrt(-M[a])*x/sqrt(M[a]*x**2-M[b])))) else: terms |= set(hints) for g in set(terms): terms |= components(cancel(g.diff(x)), x) V = _symbols('x', len(terms)) mapping = dict(zip(terms, V)) rev_mapping = {} for k, v in mapping.iteritems(): rev_mapping[v] = k def substitute(expr): return expr.subs(mapping) diffs = [ substitute(cancel(g.diff(x))) for g in terms ] denoms = [ g.as_numer_denom()[1] for g in diffs ] try: denom = reduce(lambda p, q: lcm(p, q, *V), denoms) except PolynomialError: # lcm can fail with this. See issue 1418. return None numers = [ cancel(denom * g) for g in diffs ] def derivation(h): return Add(*[ d * h.diff(v) for d, v in zip(numers, V) ]) def deflation(p): for y in V: if not p.has(y): continue if derivation(p) is not S.Zero: c, q = p.as_poly(y).primitive() return deflation(c)*gcd(q, q.diff(y)).as_expr() else: return p def splitter(p): for y in V: if not p.has(y): continue if derivation(y) is not S.Zero: c, q = p.as_poly(y).primitive() q = q.as_expr() h = gcd(q, derivation(q), y) s = quo(h, gcd(q, q.diff(y), y), y) c_split = splitter(c) if s.as_poly(y).degree() == 0: return (c_split[0], q * c_split[1]) q_split = splitter(cancel(q / s)) return (c_split[0]*q_split[0]*s, c_split[1]*q_split[1]) else: return (S.One, p) special = {} for term in terms: if term.is_Function: if term.func is tan: special[1 + substitute(term)**2] = False elif term.func is tanh: special[1 + substitute(term)] = False special[1 - substitute(term)] = False elif term.func is C.LambertW: special[substitute(term)] = True F = substitute(f) P, Q = F.as_numer_denom() u_split = splitter(denom) v_split = splitter(Q) polys = list(v_split) + [ u_split[0] ] + special.keys() s = u_split[0] * Mul(*[ k for k, v in special.iteritems() if v ]) polified = [ p.as_poly(*V) for p in [s, P, Q] ] if None in polified: return a, b, c = [ p.total_degree() for p in polified ] poly_denom = (s * v_split[0] * deflation(v_split[1])).as_expr() def exponent(g): if g.is_Pow: if g.exp.is_Rational and g.exp.q != 1: if g.exp.p > 0: return g.exp.p + g.exp.q - 1 else: return abs(g.exp.p + g.exp.q) else: return 1 elif not g.is_Atom: return max([ exponent(h) for h in g.args ]) else: return 1 A, B = exponent(f), a + max(b, c) if A > 1 and B > 1: monoms = monomials(V, A + B - 1) else: monoms = monomials(V, A + B) poly_coeffs = _symbols('A', len(monoms)) poly_part = Add(*[ poly_coeffs[i]*monomial for i, monomial in enumerate(monoms) ]) reducibles = set() for poly in polys: if poly.has(*V): try: factorization = factor(poly, greedy=True) except PolynomialError: factorization = poly factorization = poly if factorization.is_Mul: reducibles |= set(factorization.args) else: reducibles.add(factorization) def integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.atoms(Symbol): if z in V: break else: continue irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) for i, poly in enumerate(irreducibles): if poly.has(*V): log_coeffs.append(B[i]) log_part.append(log_coeffs[-1] * log(poly)) coeffs = poly_coeffs + log_coeffs candidate = poly_part/poly_denom + Add(*log_part) h = F - derivation(candidate) / denom numer = h.as_numer_denom()[0].expand(force=True) equations = {} for term in Add.make_args(numer): coeff, dependent = term.as_independent(*V) if dependent in equations: equations[dependent] += coeff else: equations[dependent] = coeff solution = solve(equations.values(), *coeffs) if solution is not None: return (solution, candidate, coeffs) else: return None if not (F.atoms(Symbol) - set(V)): result = integrate('Q') if result is None: result = integrate() else: result = integrate() if result is not None: (solution, candidate, coeffs) = result antideriv = candidate.subs(solution) for coeff in coeffs: if coeff not in solution: antideriv = antideriv.subs(coeff, S.Zero) antideriv = antideriv.subs(rev_mapping) antideriv = cancel(antideriv).expand(force=True) if antideriv.is_Add: antideriv = antideriv.as_independent(x)[1] return indep * antideriv else: if not rewrite: result = heurisch(f, x, rewrite=True, **kwargs) if result is not None: return indep * result return None wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/integrals.py0000644000175000017500000007556412014170666025452 0ustar georgeskgeorgeskfrom sympy.core import (Basic, Expr, S, C, Symbol, Wild, Add, sympify, diff, oo, Tuple, Dummy, Equality, Interval) from sympy.core.symbol import Dummy from sympy.core.compatibility import is_sequence from sympy.integrals.trigonometry import trigintegrate from sympy.integrals.deltafunctions import deltaintegrate from sympy.integrals.rationaltools import ratint from sympy.integrals.risch import heurisch from sympy.utilities import xthreaded, flatten from sympy.polys import Poly, PolynomialError from sympy.solvers import solve from sympy.functions import Piecewise, sign from sympy.geometry import Curve from sympy.functions.elementary.piecewise import piecewise_fold from sympy.series import limit def _free_symbols(function, limits): """ Return the symbols that will exist when the function is evaluated as an Integral or a Sum. This is useful if one is trying to determine whether the result is dependent on a certain symbol or not. This is written as a private function so it can be used from Sum as well as from Integral. """ if function.is_zero: return set() isyms = function.free_symbols for xab in limits: if len(xab) == 1: isyms.add(xab[0]) continue # take out the target symbol if xab[0] in isyms: isyms.remove(xab[0]) if len(xab) == 3 and xab[1] == xab[2]: # if two limits are the same the integral is 0 # and there are no symbols return set() # add in the new symbols for i in xab[1:]: isyms.update(i.free_symbols) return isyms def _process_limits(*symbols): """Convert the symbols-related limits into propert limits, storing them as Tuple(symbol, lower, upper). The sign of the function is also returned when the upper limit is missing so (x, 1, None) becomes (x, None, 1) and the sign is changed. """ limits = [] sign = 1 for V in symbols: if isinstance(V, Symbol): limits.append(Tuple(V)) continue elif is_sequence(V, Tuple): V = sympify(flatten(V)) if V[0].is_Symbol: newsymbol = V[0] if len(V) == 2 and isinstance(V[1], Interval): V[1:] = [V[1].start, V[1].end] if len(V) == 3: if V[1] is None and V[2] is not None: nlim = [V[2]] elif V[1] is not None and V[2] is None: sign *= -1 nlim = [V[1]] elif V[1] is None and V[2] is None: nlim = [] else: nlim = V[1:] limits.append(Tuple(newsymbol, *nlim )) continue elif len(V) == 1 or (len(V) == 2 and V[1] is None): limits.append(Tuple(newsymbol)) continue elif len(V) == 2: limits.append(Tuple(newsymbol, V[1])) continue raise ValueError('Invalid limits given: %s' % str(symbols)) return limits, sign class Integral(Expr): """Represents unevaluated integral.""" __slots__ = ['is_commutative'] def __new__(cls, function, *symbols, **assumptions): # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if symbols: limits, sign = _process_limits(*symbols) else: # no symbols provided -- let's compute full anti-derivative limits, sign = [Tuple(s) for s in function.free_symbols], 1 if len(limits) != 1: raise ValueError("specify integration variables to integrate %s" % function) while isinstance(function, Integral): # denest the integrand limits = list(function.limits) + limits function = function.function obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) obj.is_commutative = all(s.is_commutative for s in obj.free_symbols) return obj def __getnewargs__(self): return (self.function,) + tuple([tuple(xab) for xab in self.limits]) @property def function(self): return self._args[0] @property def limits(self): return self._args[1:] @property def variables(self): """Return a list of the integration variables. >>> from sympy import Integral >>> from sympy.abc import x, i >>> Integral(x**i, (i, 1, 3)).variables [i] """ return [l[0] for l in self.limits] @property def free_symbols(self): """ This method returns the symbols that will exist when the integral is evaluated. This is useful if one is trying to determine whether an integral is dependent on a certain symbol or not. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x, (x, y, 1)).free_symbols set([y]) """ return _free_symbols(self.function, self.limits) @property def is_zero(self): """Since Integral doesn't autosimplify it it useful to see if it would simplify to zero or not in a trivial manner, i.e. when the function is 0 or two limits of a definite integral are the same. This is a very naive and quick test, not intended to check for special patterns like Integral(sin(m*x)*cos(n*x), (x, 0, 2*pi)) == 0. """ if (self.function.is_zero or any(len(xab) == 3 and xab[1] == xab[2] for xab in self.limits)): return True if not self.free_symbols and self.function.is_number: # the integrand is a number and the limits are numerical return False @property def is_number(self): """ Return True if the Integral will result in a number, else False. sympy considers anything that will result in a number to have is_number == True. >>> from sympy import log >>> log(2).is_number True Integrals are a special case since they contain symbols that can be replaced with numbers. Whether the integral can be done or not is another issue. But answering whether the final result is a number is not difficult. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x).is_number False >>> Integral(x, y).is_number False >>> Integral(x, (y, 1, x)).is_number False >>> Integral(x, (y, 1, 2)).is_number False >>> Integral(x, (y, 1, 1)).is_number True >>> Integral(x, (x, 1, 2)).is_number True >>> Integral(x*y, (x, 1, 2), (y, 1, 3)).is_number True >>> Integral(1, x, (x, 1, 2)).is_number True """ integrand, limits = self.function, self.limits isyms = integrand.atoms(Symbol) for xab in limits: if len(xab) == 1: isyms.add(xab[0]) continue # it may be removed later elif len(xab) == 3 and xab[1] == xab[2]: # XXX naive equality test return True # integral collapsed if xab[0] in isyms: # take it out of the symbols since it will be replace # with whatever the limits of the integral are isyms.remove(xab[0]) # add in the new symbols for i in xab[1:]: isyms.update(i.free_symbols) # if there are no surviving symbols then the result is a number return len(isyms) == 0 def as_dummy(self): """ Replace instances of the integration variables with their dummy counterparts to make clear what are dummy variables and what are real-world symbols in an Integral. The "integral at" limit that has a length of 1 will be explicated with its length-2 equivalent. >>> from sympy import Integral >>> from sympy.abc import x, y >>> Integral(x).as_dummy() Integral(_x, (_x, x)) >>> Integral(x, (x, x, y), (y, x, y)).as_dummy() Integral(_x, (_x, x, _y), (_y, x, y)) If there were no dummies in the original expression, then the output of this function will show which symbols cannot be changed by subs(), those with an underscore prefix. """ reps = {} f = self.function limits = list(self.limits) for i in xrange(-1, -len(limits) - 1, -1): xab = list(limits[i]) if len(xab) == 1: xab = xab*2 x = xab[0] xab[0] = x.as_dummy() for j in range(1, len(xab)): xab[j] = xab[j].subs(reps) reps[x] = xab[0] limits[i] = xab f = f.subs(reps) return Integral(f, *limits) def transform(self, x, mapping, inverse=False): """ Replace the integration variable x in the integrand with the expression given by `mapping`, e.g. 2*x or 1/x. The integrand and endpoints are rescaled to preserve the value of the original integral. In effect, this performs a variable substitution (although the symbol remains unchanged; follow up with subs to obtain a new symbol.) With inverse=True, the inverse transformation is performed. The mapping must be uniquely invertible (e.g. a linear or linear fractional transformation). """ if x not in self.variables: return self limits = self.limits function = self.function y = Dummy('y') inverse_mapping = solve(mapping.subs(x, y) - x, y) if len(inverse_mapping) != 1 or x not in inverse_mapping[0].free_symbols: raise ValueError("The mapping must be uniquely invertible") inverse_mapping = inverse_mapping[0] if inverse: mapping, inverse_mapping = inverse_mapping, mapping function = function.subs(x, mapping) * mapping.diff(x) def calc_limit(a, b): """replace x with a, using subs if possible, otherwise limit where sign of b is considered""" wok = inverse_mapping.subs(x, a) if wok is S.NaN or wok.is_bounded is False and a.is_bounded: return limit(sign(b)*inverse_mapping, x, a) return wok newlimits = [] for xab in limits: sym = xab[0] if sym == x and len(xab) == 3: a, b = xab[1:] a, b = calc_limit(a, b), calc_limit(b, a) if a == b: raise ValueError("The mapping must transform the " "endpoints into separate points") if a > b: a, b = b, a function = -function newlimits.append((sym, a, b)) else: newlimits.append(xab) return Integral(function, *newlimits) def doit(self, **hints): if not hints.get('integrals', True): return self deep = hints.get('deep', True) # check for the trivial case of equal upper and lower limits if self.is_zero: return S.Zero # now compute and check the function function = self.function if deep: function = function.doit(**hints) if function.is_zero: return S.Zero # There is no trivial answer, so continue undone_limits = [] ulj = set() # free symbols of any undone limits' upper and lower limits for xab in self.limits: # compute uli, the free symbols in the # Upper and Lower limits of limit I if len(xab) == 1: uli = set(xab[:1]) elif len(xab) == 2: uli = xab[1].free_symbols elif len(xab) == 3: uli = xab[1].free_symbols.union(xab[2].free_symbols) # this integral can be done as long as there is no blocking # limit that has been undone. An undone limit is blocking if # it contains an integration variable that is in this limit's # upper or lower free symbols or vice versa if xab[0] in ulj or any(v[0] in uli for v in undone_limits): undone_limits.append(xab) ulj.update(uli) continue antideriv = self._eval_integral(function, xab[0]) if antideriv is None: undone_limits.append(xab) else: if len(xab) == 1: function = antideriv else: if len(xab) == 3: x, a, b = xab if len(xab) == 2: x, b = xab a = None if deep: if isinstance(a, Basic): a = a.doit(**hints) if isinstance(b, Basic): b = b.doit(**hints) if antideriv.is_Poly: gens = list(antideriv.gens) gens.remove(x) antideriv = antideriv.as_expr() function = antideriv._eval_interval(x, a, b) function = Poly(function, *gens) else: function = antideriv._eval_interval(x, a, b) if undone_limits: return self.func(*([function] + undone_limits)) return function def _eval_expand_basic(self, deep=True, **hints): from sympy import flatten if not deep: return self else: return Integral(self.function.expand(deep=deep, **hints),\ flatten(*self.limits)) def _eval_derivative(self, sym): """Evaluate the derivative of the current Integral object by differentiating under the integral sign [1], using the Fundamental Theorem of Calculus [2] when possible. Whenever an Integral is encountered that is equivalent to zero or has an integrand that is independent of the variable of integration those integrals are performed. All others are returned as Integral instances which can be resolved with doit() (provided they are integrable). References: [1] http://en.wikipedia.org/wiki/Differentiation_under_the_integral_sign [2] http://en.wikipedia.org/wiki/Fundamental_theorem_of_calculus >>> from sympy import Integral >>> from sympy.abc import x, y >>> i = Integral(x + y, y, (y, 1, x)) >>> i.diff(x) Integral(x + y, (y, x)) + Integral(1, (y, y), (y, 1, x)) >>> i.doit().diff(x) == i.diff(x).doit() True >>> i.diff(y) 0 The previous must be true since there is no y in the evaluated integral: >>> i.free_symbols set([x]) >>> i.doit() 2*x**3/3 - x/2 - 1/6 """ # differentiate under the integral sign; we do not # check for regularity conditions (TODO), see issue 1116 # get limits and the function f, limits = self.function, list(self.limits) # the order matters if variables of integration appear in the limits # so work our way in from the outside to the inside. limit = limits.pop(-1) if len(limit) == 3: x, a, b = limit elif len(limit) == 2: x, b = limit a = None else: a = b = None x = limit[0] if limits: # f is the argument to an integral f = Integral(f, *tuple(limits)) # assemble the pieces rv = 0 if b is not None: rv += f.subs(x, b)*diff(b, sym) if a is not None: rv -= f.subs(x, a)*diff(a, sym) if len(limit) == 1 and sym == x: # the dummy variable *is* also the real-world variable arg = f rv += arg else: # the dummy variable might match sym but it's # only a dummy and the actual variable is determined # by the limits, so mask off the variable of integration # while differentiating u = Dummy('u') arg = f.subs(x, u).diff(sym).subs(u, x) rv += Integral(arg, Tuple(x, a, b)) return rv def _eval_integral(self, f, x): """Calculate the anti-derivative to the function f(x). This is a powerful function that should in theory be able to integrate everything that can be integrated. If you find something, that it doesn't, it is easy to implement it. (1) Simple heuristics (based on pattern matching and integral table): - most frequently used functions (e.g. polynomials) - functions non-integrable by any of the following algorithms (e.g. exp(-x**2)) (2) Integration of rational functions: (a) using apart() - apart() is full partial fraction decomposition procedure based on Bronstein-Salvy algorithm. It gives formal decomposition with no polynomial factorization at all (so it's fast and gives the most general results). However it needs much better implementation of RootsOf class (if fact any implementation). (b) using Trager's algorithm - possibly faster than (a) but needs implementation :) (3) Whichever implementation of pmInt (Mateusz, Kirill's or a combination of both). - this way we can handle efficiently huge class of elementary and special functions (4) Recursive Risch algorithm as described in Bronstein's integration tutorial. - this way we can handle those integrable functions for which (3) fails (5) Powerful heuristics based mostly on user defined rules. - handle complicated, rarely used cases """ # if it is a poly(x) then let the polynomial integrate itself (fast) # # It is important to make this check first, otherwise the other code # will return a sympy expression instead of a Polynomial. # # see Polynomial for details. if isinstance(f, Poly): return f.integrate(x) # Piecewise antiderivatives need to call special integrate. if f.func is Piecewise: return f._eval_integral(x) # let's cut it short if `f` does not depend on `x` if not f.has(x): return f*x # try to convert to poly(x) and then integrate if successful (fast) poly = f.as_poly(x) if poly is not None: return poly.integrate().as_expr() # since Integral(f=g1+g2+...) == Integral(g1) + Integral(g2) + ... # we are going to handle Add terms separately, # if `f` is not Add -- we only have one term parts = [] args = Add.make_args(f) for g in args: coeff, g = g.as_independent(x) # g(x) = const if g is S.One: parts.append(coeff*x) continue # c # g(x) = (a*x+b) if g.is_Pow and not g.exp.has(x): a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) M = g.base.match(a*x + b) if M is not None: if g.exp == -1: h = C.log(g.base) else: h = g.base**(g.exp + 1) / (g.exp + 1) parts.append(coeff * h / M[a]) continue # poly(x) # g(x) = ------- # poly(x) if g.is_rational_function(x): parts.append(coeff * ratint(g, x)) continue # g(x) = Mul(trig) h = trigintegrate(g, x) if h is not None: parts.append(coeff * h) continue # g(x) has at least a DiracDelta term h = deltaintegrate(g, x) if h is not None: parts.append(coeff * h) continue # fall back to the more general algorithm try: h = heurisch(g, x, hints=[]) except PolynomialError: # XXX: this exception means there is a bug in the # implementation of heuristic Risch integration # algorithm. h = None # if we failed maybe it was because we had # a product that could have been expanded, # so let's try an expansion of the whole # thing before giving up; we don't try this # out the outset because there are things # that cannot be solved unless they are # NOT expanded e.g., x**x*(1+log(x)). There # should probably be a checker somewhere in this # routine to look for such cases and try to do # collection on the expressions if they are already # in an expanded form if not h and len(args) == 1: f = f.expand(mul=True, deep=False) if f.is_Add: return self._eval_integral(f, x) if h is not None: parts.append(coeff * h) else: return None return Add(*parts) def _eval_lseries(self, x): for term in self.function.lseries(x): yield integrate(term, *self.limits) def _eval_nseries(self, x, n, logx): terms, order = self.function.nseries(x, n=n, logx=logx).as_coeff_add(C.Order) return integrate(terms, *self.limits) + Add(*order)*x def _eval_subs(self, old, new): """ Substitute old with new in the integrand and the limits, but don't change anything that is (or corresponds to) a variable of integration. The normal substitution semantics -- traversing all arguments looking for matching patterns -- should not be applied to the Integrals since changing the integration variables should also entail a change in the integration limits (which should be done with the transform method). So this method just makes changes in the integrand and the limits. Not all instances of a given variable are conceptually the same: the first argument of the limit tuple and any corresponding variable in the integrand are dummy variables while every other symbol is a symbol that will be unchanged when the integral is evaluated. For example, in Integral(x + a, (a, a, b)) the dummy variables are shown below with angle-brackets around them and will not be changed by this function: Integral(x + , (, a, b)) If you want to change the lower limit to 1 there is no reason to prohibit this since it is not conceptually related to the integration variable, . Nor is there reason to disallow changing the b to 1. If a second limit were added, however, as in: Integral(x + a, (a, a, b), (b, 1, 2)) the dummy variables become: Integral(x + , (, a, ), (, a, b)) Note that the `b` of the first limit is now a dummy variable since `b` is a dummy variable in the second limit. Summary: no variable of the integrand or limit can be the target of substitution if it appears as a variable of integration in a limit positioned to the right of it. >>> from sympy import Integral >>> from sympy.abc import a, b, c, x, y >>> i = Integral(a + x, (a, a, 3), (b, x, c)) >>> list(i.free_symbols) # only these can be changed [x, a, c] >>> i.subs(a, c) # note that the variable of integration is unchanged Integral(a + x, (a, c, 3), (b, x, c)) >>> i.subs(a + x, b) == i # there is no x + a, only x + True >>> i.subs(x, y - c) Integral(a - c + y, (a, a, 3), (b, -c + y, c)) """ if self == old: return new integrand, limits = self.function, self.limits old_atoms = old.free_symbols limits = list(limits) # make limits explicit if they are to be targeted by old: # Integral(x, x) -> Integral(x, (x, x)) if old = x if old.is_Symbol: for i, l in enumerate(limits): if len(l) == 1 and l[0] == old: limits[i] = Tuple(l[0], l[0]) dummies = set() for i in xrange(-1, -len(limits) - 1, -1): xab = limits[i] if not dummies.intersection(old_atoms): limits[i] = Tuple(xab[0], *[l.subs(old, new) for l in xab[1:]]) dummies.add(xab[0]) if not dummies.intersection(old_atoms): integrand = integrand.subs(old, new) return Integral(integrand, *limits) def as_sum(self, n, method="midpoint"): """ Approximates the integral by a sum. method ... one of: left, right, midpoint This is basically just the rectangle method [1], the only difference is where the function value is taken in each interval. [1] http://en.wikipedia.org/wiki/Rectangle_method **method = midpoint**: Uses the n-order midpoint rule to evaluate the integral. Midpoint rule uses rectangles approximation for the given area (e.g. definite integral) of the function with heights equal to the point on the curve exactly in the middle of each interval (thus midpoint method). See [1] for more information. Examples: >>> from sympy import sqrt >>> from sympy.abc import x >>> from sympy.integrals import Integral >>> e = Integral(sqrt(x**3+1), (x, 2, 10)) >>> e Integral((x**3 + 1)**(1/2), (x, 2, 10)) >>> e.as_sum(4, method="midpoint") 4*7**(1/2) + 6*14**(1/2) + 4*86**(1/2) + 2*730**(1/2) >>> e.as_sum(4, method="midpoint").n() 124.164447891310 >>> e.n() 124.616199194723 **method=left**: Uses the n-order rectangle rule to evaluate the integral, at each interval the function value is taken at the left hand side of the interval. Examples: >>> from sympy import sqrt >>> from sympy.abc import x >>> e = Integral(sqrt(x**3+1), (x, 2, 10)) >>> e Integral((x**3 + 1)**(1/2), (x, 2, 10)) >>> e.as_sum(4, method="left") 6 + 2*65**(1/2) + 2*217**(1/2) + 6*57**(1/2) >>> e.as_sum(4, method="left").n() 96.8853618335341 >>> e.n() 124.616199194723 """ limits = self.limits if len(limits) > 1: raise NotImplementedError("Multidimensional midpoint rule not implemented yet") else: limit = limits[0] if n <= 0: raise ValueError("n must be > 0") if n == oo: raise NotImplementedError("Infinite summation not yet implemented") sym, lower_limit, upper_limit = limit dx = (upper_limit - lower_limit)/n result = 0. for i in range(n): if method == "midpoint": xi = lower_limit + i*dx + dx/2 elif method == "left": xi = lower_limit + i*dx elif method == "right": xi = lower_limit + i*dx + dx else: raise NotImplementedError("Unknown method %s" % method) result += self.function.subs(sym, xi) return result*dx @xthreaded def integrate(*args, **kwargs): """integrate(f, var, ...) Compute definite or indefinite integral of one or more variables using Risch-Norman algorithm and table lookup. This procedure is able to handle elementary algebraic and transcendental functions and also a huge class of special functions, including Airy, Bessel, Whittaker and Lambert. var can be: - a symbol -- indefinite integration - a tuple (symbol, a, b) -- definite integration Several variables can be specified, in which case the result is multiple integration. Also, if no var is specified at all, then the full anti-derivative of f is returned. This is equivalent to integrating f over all its variables. **Examples** >>> from sympy import integrate, log >>> from sympy.abc import a, x, y >>> integrate(x*y, x) x**2*y/2 >>> integrate(log(x), x) x*log(x) - x >>> integrate(log(x), (x, 1, a)) a*log(a) - a + 1 >>> integrate(x) x**2/2 >>> integrate(x*y) Traceback (most recent call last): ... ValueError: specify integration variables to integrate x*y Note that ``integrate(x)`` syntax is meant only for convenience in interactive sessions and should be avoided in library code. See also the doctest of Integral._eval_integral(), which explains thoroughly the strategy that SymPy uses for integration. """ integral = Integral(*args, **kwargs) if isinstance(integral, Integral): return integral.doit(deep = False) else: return integral @xthreaded def line_integrate(field, curve, vars): """line_integrate(field, Curve, variables) Compute the line integral. Examples -------- >>> from sympy import Curve, line_integrate, E, ln >>> from sympy.abc import x, y, t >>> C = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) >>> line_integrate(x + y, C, [x, y]) 3*2**(1/2) """ F = sympify(field) if not F: raise ValueError("Expecting function specifying field as first argument.") if not isinstance(curve, Curve): raise ValueError("Expecting Curve entity as second argument.") if not is_sequence(vars): raise ValueError("Expecting ordered iterable for variables.") if len(curve.functions) != len(vars): raise ValueError("Field variable size does not match curve dimension.") if curve.parameter in vars: raise ValueError("Curve parameter clashes with field parameters.") # Calculate derivatives for line parameter functions # F(r) -> F(r(t)) and finally F(r(t)*r'(t)) Ft = F dldt = 0 for i, var in enumerate(vars): _f = curve.functions[i] _dn = diff(_f, curve.parameter) # ...arc length dldt = dldt + (_dn * _dn) Ft = Ft.subs(var, _f) Ft = Ft * dldt**(S(1)/2) integral = Integral(Ft, curve.limits).doit(deep = False) return integral wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/heurisch2.py0000644000175000017500000003167512014170666025351 0ustar georgeskgeorgeskfrom sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.symbol import Symbol, Wild, Dummy from sympy.core.basic import C, sympify from sympy.core.numbers import Rational, I, pi from sympy.core.singleton import S from sympy.functions import exp, sin , cos , tan , cot , asin, acos, atan from sympy.functions import log, sinh, cosh, tanh, coth, asinh, acosh from sympy.functions import sqrt, erf from sympy.solvers import solve from sympy.polys import quo, gcd, lcm, \ monomials, factor, cancel, PolynomialError from sympy.polys.polyroots import root_factors def components(f, x): """Returns a set of all functional components of the given expression which includes symbols, function applications and compositions and non-integer powers. Fractional powers are collected with with minimal, positive exponents. >>> from sympy import cos, sin >>> from sympy.abc import x, y >>> from sympy.integrals.risch import components >>> components(sin(x)*cos(x)**2, x) set([x, sin(x), cos(x)]) """ result = set() if f.has(x): if f.is_Symbol: result.add(f) elif f.is_Function or f.is_Derivative: for g in f.args: result |= components(g, x) result.add(f) elif f.is_Pow: result |= components(f.base, x) if not f.exp.is_Integer: if f.exp.is_Rational: result.add(f.base**Rational(1, f.exp.q)) else: result |= components(f.exp, x) | set([f]) else: for g in f.args: result |= components(g, x) return result # name -> [] of symbols _symbols_cache = {} # NB @cacheit is not convenient here def _symbols(name, n): """get vector of symbols local to this module""" try: lsyms = _symbols_cache[name] except KeyError: lsyms = [] _symbols_cache[name] = lsyms while len(lsyms) < n: lsyms.append( Dummy('%s%i' % (name, len(lsyms))) ) return lsyms[:n] def heurisch(f, x, **kwargs): """Compute indefinite integral using heuristic Risch algorithm. This is a heuristic approach to indefinite integration in finite terms using the extended heuristic (parallel) Risch algorithm, based on Manuel Bronstein's "Poor Man's Integrator". The algorithm supports various classes of functions including transcendental elementary or special functions like Airy, Bessel, Whittaker and Lambert. Note that this algorithm is not a decision procedure. If it isn't able to compute the antiderivative for a given function, then this is not a proof that such a functions does not exist. One should use recursive Risch algorithm in such case. It's an open question if this algorithm can be made a full decision procedure. This is an internal integrator procedure. You should use toplevel 'integrate' function in most cases, as this procedure needs some preprocessing steps and otherwise may fail. Specification ============ heurisch(f, x, rewrite=False, hints=None) where f : expression x : symbol rewrite -> force rewrite 'f' in terms of 'tan' and 'tanh' hints -> a list of functions that may appear in anti-derivate - hints = None --> no suggestions at all - hints = [ ] --> try to figure out - hints = [f1, ..., fn] --> we know better Examples ======== >>> from sympy import tan >>> from sympy.integrals.risch import heurisch >>> from sympy.abc import x, y >>> heurisch(y*tan(x), x) y*log(tan(x)**2 + 1)/2 See Manuel Bronstein's "Poor Man's Integrator": [1] http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/index.html For more information on the implemented algorithm refer to: [2] K. Geddes, L. Stefanus, On the Risch-Norman Integration Method and its Implementation in Maple, Proceedings of ISSAC'89, ACM Press, 212-217. [3] J. H. Davenport, On the Parallel Risch Algorithm (I), Proceedings of EUROCAM'82, LNCS 144, Springer, 144-157. [4] J. H. Davenport, On the Parallel Risch Algorithm (III): Use of Tangents, SIGSAM Bulletin 16 (1982), 3-6. [5] J. H. Davenport, B. M. Trager, On the Parallel Risch Algorithm (II), ACM Transactions on Mathematical Software 11 (1985), 356-362. """ f = sympify(f) if not f.is_Add: indep, f = f.as_independent(x) else: indep = S.One if not f.has(x): return indep * f * x rewritables = { (sin, cos, cot) : tan, (sinh, cosh, coth) : tanh, } rewrite = kwargs.pop('rewrite', False) if rewrite: for candidates, rule in rewritables.iteritems(): f = f.rewrite(candidates, rule) else: for candidates in rewritables.iterkeys(): if f.has(*candidates): break else: rewrite = True terms = components(f, x) hints = kwargs.get('hints', None) if hints is not None: if not hints: a = Wild('a', exclude=[x]) b = Wild('b', exclude=[x]) c = Wild('c', exclude=[x]) for g in set(terms): if g.is_Function: if g.func is exp: M = g.args[0].match(a*x**2) if M is not None: terms.add(erf(sqrt(-M[a])*x)) M = g.args[0].match(a*x**2 + b*x + c) if M is not None: if M[a].is_positive: terms.add(sqrt(pi/4*(-M[a]))*exp(M[c]-M[b]**2/(4*M[a]))* \ erf(-sqrt(-M[a])*x + M[b]/(2*sqrt(-M[a])))) elif M[a].is_negative: terms.add(sqrt(pi/4*(-M[a]))*exp(M[c]-M[b]**2/(4*M[a]))* \ erf(sqrt(-M[a])*x - M[b]/(2*sqrt(-M[a])))) M = g.args[0].match(a*log(x)**2) if M is not None: if M[a].is_positive: terms.add(-I*erf(I*(sqrt(M[a])*log(x)+1/(2*sqrt(M[a]))))) if M[a].is_negative: terms.add(erf(sqrt(-M[a])*log(x)-1/(2*sqrt(-M[a])))) elif g.is_Pow: if g.exp.is_Rational and g.exp.q == 2: M = g.base.match(a*x**2 + b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(asinh(sqrt(M[a]/M[b])*x)) elif M[a].is_negative: terms.add(asin(sqrt(-M[a]/M[b])*x)) M = g.base.match(a*x**2 - b) if M is not None and M[b].is_positive: if M[a].is_positive: terms.add(acosh(sqrt(M[a]/M[b])*x)) elif M[a].is_negative: terms.add((-M[b]/2*sqrt(-M[a])*\ atan(sqrt(-M[a])*x/sqrt(M[a]*x**2-M[b])))) else: terms |= set(hints) for g in set(terms): terms |= components(cancel(g.diff(x)), x) V = _symbols('x', len(terms)) mapping = dict(zip(terms, V)) rev_mapping = {} for k, v in mapping.iteritems(): rev_mapping[v] = k def substitute(expr): return expr.subs(mapping) diffs = [ substitute(cancel(g.diff(x))) for g in terms ] denoms = [ g.as_numer_denom()[1] for g in diffs ] try: denom = reduce(lambda p, q: lcm(p, q, *V), denoms) except PolynomialError: # lcm can fail with this. See issue 1418. return None numers = [ cancel(denom * g) for g in diffs ] def derivation(h): return Add(*[ d * h.diff(v) for d, v in zip(numers, V) ]) def deflation(p): for y in V: if not p.has(y): continue if derivation(p) is not S.Zero: c, q = p.as_poly(y).primitive() return deflation(c)*gcd(q, q.diff(y)).as_expr() else: return p def splitter(p): for y in V: if not p.has(y): continue if derivation(y) is not S.Zero: c, q = p.as_poly(y).primitive() q = q.as_expr() h = gcd(q, derivation(q), y) s = quo(h, gcd(q, q.diff(y), y), y) c_split = splitter(c) if s.as_poly(y).degree() == 0: return (c_split[0], q * c_split[1]) q_split = splitter(cancel(q / s)) return (c_split[0]*q_split[0]*s, c_split[1]*q_split[1]) else: return (S.One, p) special = {} for term in terms: if term.is_Function: if term.func is tan: special[1 + substitute(term)**2] = False elif term.func is tanh: special[1 + substitute(term)] = False special[1 - substitute(term)] = False elif term.func is C.LambertW: special[substitute(term)] = True F = substitute(f) P, Q = F.as_numer_denom() u_split = splitter(denom) v_split = splitter(Q) polys = list(v_split) + [ u_split[0] ] + special.keys() s = u_split[0] * Mul(*[ k for k, v in special.iteritems() if v ]) polified = [ p.as_poly(*V) for p in [s, P, Q] ] if None in polified: return a, b, c = [ p.total_degree() for p in polified ] poly_denom = (s * v_split[0] * deflation(v_split[1])).as_expr() def exponent(g): if g.is_Pow: if g.exp.is_Rational and g.exp.q != 1: if g.exp.p > 0: return g.exp.p + g.exp.q - 1 else: return abs(g.exp.p + g.exp.q) else: return 1 elif not g.is_Atom: return max([ exponent(h) for h in g.args ]) else: return 1 A, B = exponent(f), a + max(b, c) if A > 1 and B > 1: monoms = monomials(V, A + B - 1) else: monoms = monomials(V, A + B) poly_coeffs = _symbols('A', len(monoms)) poly_part = Add(*[ poly_coeffs[i]*monomial for i, monomial in enumerate(monoms) ]) reducibles = set() for poly in polys: if poly.has(*V): try: factorization = factor(poly, greedy=True) except PolynomialError: factorization = poly factorization = poly if factorization.is_Mul: reducibles |= set(factorization.args) else: reducibles.add(factorization) def integrate(field=None): irreducibles = set() for poly in reducibles: for z in poly.atoms(Symbol): if z in V: break else: continue irreducibles |= set(root_factors(poly, z, filter=field)) log_coeffs, log_part = [], [] B = _symbols('B', len(irreducibles)) for i, poly in enumerate(irreducibles): if poly.has(*V): log_coeffs.append(B[i]) log_part.append(log_coeffs[-1] * log(poly)) coeffs = poly_coeffs + log_coeffs candidate = poly_part/poly_denom + Add(*log_part) h = F - derivation(candidate) / denom numer = h.as_numer_denom()[0].expand(force=True) equations = {} for term in Add.make_args(numer): coeff, dependent = term.as_independent(*V) if dependent in equations: equations[dependent] += coeff else: equations[dependent] = coeff solution = solve(equations.values(), *coeffs) if solution is not None: return (solution, candidate, coeffs) else: return None if not (F.atoms(Symbol) - set(V)): result = integrate('Q') if result is None: result = integrate() else: result = integrate() if result is not None: (solution, candidate, coeffs) = result antideriv = candidate.subs(solution) for coeff in coeffs: if coeff not in solution: antideriv = antideriv.subs(coeff, S.Zero) antideriv = antideriv.subs(rev_mapping) antideriv = cancel(antideriv).expand(force=True) if antideriv.is_Add: antideriv = antideriv.as_independent(x)[1] return indep * antideriv else: if not rewrite: result = heurisch(f, x, rewrite=True, **kwargs) if result is not None: return indep * result return None wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/tests/0000755000175000017500000000000012014170666024231 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/tests/test_lineintegrals.py0000644000175000017500000000044612014170666030506 0ustar georgeskgeorgeskfrom sympy import (symbols, integrate, Integral, diff, sin, cos, pi, E, ln, sympify, Curve, line_integrate, sqrt) s, t, x, y, z = symbols('s,t,x,y,z') def test_lineintegral(): c = Curve([E**t + 1, E**t - 1], (t, 0, ln(2))) assert line_integrate(x + y, c, [x, y]) == 3*sqrt(2) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/tests/test_trigonometry.py0000644000175000017500000000454412014170666030413 0ustar georgeskgeorgeskfrom sympy import Symbol, Rational, sin, cos from sympy.integrals.trigonometry import trigintegrate x = Symbol('x') y = Symbol('y') def test_trigintegrate_odd(): assert trigintegrate(Rational(1), x) == x assert trigintegrate(x, x) is None assert trigintegrate(x**2, x) is None assert trigintegrate(sin(x), x) == -cos(x) assert trigintegrate(cos(x), x) == sin(x) assert trigintegrate(sin(3*x), x) == -cos(3*x)/3 assert trigintegrate(cos(3*x), x) == sin(3*x)/3 assert trigintegrate(sin(y*x), x) == -cos(y*x)/y assert trigintegrate(cos(y*x), x) == sin(y*x)/y assert trigintegrate(sin(x)*cos(x), x) == sin(x)**2/2 assert trigintegrate(sin(x)*cos(x)**2, x) == -cos(x)**3/3 assert trigintegrate(sin(x)**2*cos(x), x) == sin(x)**3/3 # check if it selects right function to substitute, # so the result is kept simple assert trigintegrate(sin(x)**7 * cos(x), x) == sin(x)**8/8 assert trigintegrate(sin(x) * cos(x)**7, x) == -cos(x)**8/8 assert trigintegrate(sin(x)**7 * cos(x)**3, x) == -sin(x)**10/10 + sin(x)**8/8 assert trigintegrate(sin(x)**3 * cos(x)**7, x) == cos(x)**10/10 - cos(x)**8/8 def test_trigintegrate_even(): assert trigintegrate(sin(x)**2, x) == x/2 - cos(x)*sin(x)/2 assert trigintegrate(cos(x)**2, x) == x/2 + cos(x)*sin(x)/2 assert trigintegrate(sin(3*x)**2, x)== x/2 - cos(3*x)*sin(3*x)/6 assert trigintegrate(cos(3*x)**2, x)== x/2 + cos(3*x)*sin(3*x)/6 assert trigintegrate(sin(x)**2 * cos(x)**2, x) == x/8 - cos(2*x)*sin(2*x)/16 assert trigintegrate(sin(x)**4 * cos(x)**2, x) == x/16- sin(x) *cos(x)/16 \ - sin(x)**3*cos(x)/24 \ + sin(x)**5*cos(x)/6 assert trigintegrate(sin(x)**2 * cos(x)**4, x) == x/16+ cos(x) *sin(x)/16 \ + cos(x)**3*sin(x)/24 \ - cos(x)**5*sin(x)/6 assert trigintegrate(sin(x)**(-4),x) == -2*cos(x)/(3*sin(x)) \ - cos(x)/(3*sin(x)**3) assert trigintegrate(cos(x)**(-6),x) == sin(x)/(5*cos(x)**5)\ + 4*sin(x)/(15*cos(x)**3)\ + 8*sin(x)/(15*cos(x)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/tests/test_risch.py0000644000175000017500000001754312014170666026764 0ustar georgeskgeorgeskfrom sympy import Rational, sqrt, symbols, sin, exp, log, sinh, cosh, cos, pi, \ I, S, erf, tan, asin, asinh, acos, acosh, Function, Derivative, diff, simplify, \ LambertW from sympy.integrals.risch import heurisch, components from sympy.utilities.pytest import XFAIL, skip x, y, z = symbols('x,y,z') f = Function('f') def test_components(): assert components(x*y, x) == set([x]) assert components(1/(x+y), x) == set([x]) assert components(sin(x), x) == set([sin(x), x]) assert components(sin(x)*sqrt(log(x)), x) == \ set([log(x), sin(x), sqrt(log(x)), x]) assert components(x*sin(exp(x)*y), x) == \ set([sin(y*exp(x)), x, exp(x)]) assert components(x**Rational(17,54)/sqrt(sin(x)), x) == \ set([sin(x), x**Rational(1,54), sqrt(sin(x)), x]) assert components(f(x), x) == \ set([x, f(x)]) assert components(Derivative(f(x),x), x) == \ set([x, f(x), Derivative(f(x), x)]) assert components(f(x)*diff(f(x), x), x) == \ set([x, f(x), Derivative(f(x), x), Derivative(f(x), x)]) def test_heurisch_polynomials(): assert heurisch(1, x) == x assert heurisch(x, x) == x**2/2 assert heurisch(x**17, x) == x**18/18 def test_heurisch_fractions(): assert heurisch(1/x, x) == log(x) assert heurisch(1/(2 + x), x) == log(x + 2) assert heurisch(1/(x+sin(y)), x) == log(x+sin(y)) # Up to a constant, where C = 5*pi*I/12, Mathematica gives identical # result in the first case. The difference is because sympy changes # signs of expressions without any care. # XXX ^ ^ ^ is this still correct? assert heurisch(5*x**5/(2*x**6 - 5), x) in [5*log(2*x**6 - 5) / 12, 5*log(-2*x**6 + 5) / 12] assert heurisch(5*x**5/(2*x**6 + 5), x) == 5*log(2*x**6 + 5) / 12 assert heurisch(1/x**2, x) == -1/x assert heurisch(-1/x**5, x) == 1/(4*x**4) def test_heurisch_log(): assert heurisch(log(x), x) == x*log(x) - x assert heurisch(log(3*x), x) == -x + x*log(3) + x*log(x) assert heurisch(log(x**2), x) in [x*log(x**2) - 2*x, 2*x*log(x) - 2*x] def test_heurisch_exp(): assert heurisch(exp(x), x) == exp(x) assert heurisch(exp(-x), x) == -exp(-x) assert heurisch(exp(17*x), x) == exp(17*x) / 17 assert heurisch(x*exp(x), x) == x*exp(x) - exp(x) assert heurisch(x*exp(x**2), x) == exp(x**2) / 2 assert heurisch(exp(-x**2), x) is None assert heurisch(2**x, x) == 2**x/log(2) assert heurisch(x*2**x, x) == x*2**x/log(2) - 2**x*log(2)**(-2) def test_heurisch_trigonometric(): assert heurisch(sin(x), x) == -cos(x) assert heurisch(pi*sin(x)+1, x) == x-pi*cos(x) assert heurisch(cos(x), x) == sin(x) assert heurisch(tan(x), x) in [ log(1 + tan(x)**2)/2, log(tan(x) + I) + I*x, log(tan(x) - I) - I*x, ] assert heurisch(sin(x)*sin(y), x) == -cos(x)*sin(y) assert heurisch(sin(x)*sin(y), y) == -cos(y)*sin(x) # gives sin(x) in answer when run via setup.py and cos(x) when run via py.test assert heurisch(sin(x)*cos(x), x) in [sin(x)**2 / 2, -cos(x)**2 / 2] assert heurisch(cos(x)/sin(x), x) == log(sin(x)) assert heurisch(x*sin(7*x), x) == sin(7*x) / 49 - x*cos(7*x) / 7 assert heurisch(1/pi/4 * x**2*cos(x), x) == 1/pi/4*(x**2*sin(x) - 2*sin(x) + 2*x*cos(x)) assert heurisch(acos(x/4) * asin(x/4), x) == 2*x - ((16-x**2)**Rational(1,2))*asin(x/4) \ + ((16 - x**2)**Rational(1,2))*acos(x/4) + x*asin(x/4)*acos(x/4) def test_heurisch_hyperbolic(): assert heurisch(sinh(x), x) == cosh(x) assert heurisch(cosh(x), x) == sinh(x) assert heurisch(x*sinh(x), x) == x*cosh(x) - sinh(x) assert heurisch(x*cosh(x), x) == x*sinh(x) - cosh(x) assert heurisch(x*asinh(x/2), x) == x**2*asinh(x/2)/2 + asinh(x/2) - x*(4+x**2)**Rational(1,2)/4 def test_heurisch_mixed(): assert heurisch(sin(x)*exp(x), x) == exp(x)*sin(x)/2 - exp(x)*cos(x)/2 def test_heurisch_radicals(): assert heurisch(x**Rational(-1,2), x) == 2*x**Rational(1,2) assert heurisch(x**Rational(-3,2), x) == -2*x**Rational(-1,2) assert heurisch(x**Rational(3,2), x) == 2*x**Rational(5,2) / 5 assert heurisch(sin(x)*sqrt(cos(x)), x) == -2*cos(x)**Rational(3,2) / 3 assert heurisch(sin(y*sqrt(x)), x) == 2*y**(-2)*sin(y*x**S.Half) - \ 2*x**S.Half*cos(y*x**S.Half)/y def test_heurisch_special(): assert heurisch(erf(x), x) == x*erf(x) + exp(-x**2)/sqrt(pi) assert heurisch(exp(-x**2)*erf(x), x) == sqrt(pi)*erf(x)**2 / 4 def test_heurisch_symbolic_coeffs(): assert heurisch(1/(x+y), x) == log(x+y) assert heurisch(1/(x+sqrt(2)), x) == log(x+sqrt(2)) assert simplify(diff(heurisch(log(x+y+z), y), y)) == log(x+y+z) def test_heurisch_symbolic_coeffs_1130(): assert heurisch(1/(x**2+y), x) in [I*y**(-S.Half)*log(x + (-y)**S.Half)/2 - \ I*y**(-S.Half)*log(x - (-y)**S.Half)/2, I*log(x + I*y**Rational(1,2)) / \ (2*y**Rational(1,2)) - I*log(x - I*y**Rational(1,2))/(2*y**Rational(1,2))] def test_heurisch_hacking(): assert heurisch(sqrt(1 + 7*x**2), x, hints=[]) == \ x*sqrt(1+7*x**2)/2 + sqrt(7)*asinh(sqrt(7)*x)/14 assert heurisch(sqrt(1 - 7*x**2), x, hints=[]) == \ x*sqrt(1-7*x**2)/2 + sqrt(7)*asin(sqrt(7)*x)/14 assert heurisch(1/sqrt(1 + 7*x**2), x, hints=[]) == \ sqrt(7)*asinh(sqrt(7)*x)/7 assert heurisch(1/sqrt(1 - 7*x**2), x, hints=[]) == \ sqrt(7)*asin(sqrt(7)*x)/7 assert heurisch(exp(-7*x**2),x,hints=[]) == \ sqrt(7*pi)*erf(sqrt(7)*x)/14 assert heurisch(1/sqrt(9 - 4*x**2), x, hints=[]) == \ asin(2*x/3)/2 assert heurisch(1/sqrt(9 + 4*x**2), x, hints=[]) == \ asinh(2*x/3)/2 def test_heurisch_function(): df = diff(f(x), x) assert heurisch(f(x), x) == None assert heurisch(f(x)*df, x) == f(x)**2/2 assert heurisch(f(x)**2 * df, x) == f(x)**3/3 assert heurisch(df / f(x), x) == log(f(x)) def test_issue510(): assert heurisch(1/(x * (1 + log(x)**2)), x) == I*log(log(x) + I)/2 - \ I*log(log(x) - I)/2 ### These are examples from the Poor Man's Integrator ### http://www-sop.inria.fr/cafe/Manuel.Bronstein/pmint/examples/ # # NB: correctness assured as ratsimp(diff(g,x) - f) == 0 in maxima # SymPy is unable to do it :( # Besides, they are skipped(), because they take too much time to execute. @XFAIL def test_pmint_rat(): skip('takes too much time') f = (x**7-24*x**4-4*x**2+8*x-8) / (x**8+6*x**6+12*x**4+8*x**2) g = (4 + 8*x**2 + 6*x + 3*x**3) / (x*(x**4 + 4*x**2 + 4)) + log(x) assert heurisch(f, x) == g @XFAIL def test_pmint_trig(): skip('takes too much time') f = (x-tan(x)) / tan(x)**2 + tan(x) g = (-x - tan(x)*x**2 / 2) / tan(x) + log(1+tan(x)**2) / 2 assert heurisch(f, x) == g @XFAIL def test_pmint_logexp(): skip('takes too much time') f = (1+x+x*exp(x))*(x+log(x)+exp(x)-1)/(x+log(x)+exp(x))**2/x g = 1/(x+log(x)+exp(x)) + log(x + log(x) + exp(x)) assert heurisch(f, x) == g @XFAIL def test_pmint_erf(): skip('takes too much time') f = exp(-x**2)*erf(x)/(erf(x)**3-erf(x)**2-erf(x)+1) g = sqrt(pi)/4 * (-1/(erf(x)-1) - log(erf(x)+1)/2 + log(erf(x)-1)/2) assert heurisch(f, x) == g def test_pmint_lambertw(): g = (x**2 + (LambertW(x)*x)**2 - LambertW(x)*x**2)/(x*LambertW(x)) assert simplify(heurisch(LambertW(x), x) - g) == 0 # TODO: convert the rest of PMINT tests: # Airy functions # f = (x - AiryAi(x)*AiryAi(1, x)) / (x**2 - AiryAi(x)**2) # g = Rational(1,2)*ln(x + AiryAi(x)) + Rational(1,2)*ln(x - AiryAi(x)) # f = x**2 * AiryAi(x) # g = -AiryAi(x) + AiryAi(1, x)*x # Bessel functions # f = BesselJ(nu + 1, x) / BesselJ(nu, x) # g = nu*ln(x) - ln(BesselJ(nu, x)) # f = (nu * BesselJ(nu, x) / x) - BesselJ(nu + 1, x) # g = BesselJ(nu, x) # Whittaker functions # f = WhittakerW(mu + 1, nu, x) / (WhittakerW(mu, nu, x) * x) # g = x/2 - mu*ln(x) - ln(WhittakerW(mu, nu, x)) # - Wright omega wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/tests/test_rationaltools.py0000644000175000017500000000631112014170666030535 0ustar georgeskgeorgeskfrom sympy import symbols, S, I, atan, log, Poly, sqrt from sympy.integrals.rationaltools import ratint, \ ratint_ratpart, ratint_logpart, log_to_atan, log_to_real from sympy.abc import a, b, x, t half = S(1)/2 def test_ratint(): assert ratint(S(0), x) == 0 assert ratint(S(7), x) == 7*x assert ratint(x, x) == x**2/2 assert ratint(2*x, x) == x**2 assert ratint(-2*x, x) == -x**2 assert ratint(8*x**7+2*x+1, x) == x**8+x**2+x f = S(1) g = x + 1 assert ratint(f / g, x) == log(x + 1) assert ratint((f,g), x) == log(x + 1) f = x**3 - x g = x - 1 assert ratint(f/g, x) == x**3/3 + x**2/2 f = x g = (x - a)*(x + a) assert ratint(f/g, x) == log(x**2 - a**2)/2 f = S(1) g = x**2 + 1 assert ratint(f/g, x, real=None) == atan(x) assert ratint(f/g, x, real=True) == atan(x) assert ratint(f/g, x, real=False) == I*log(x + I)/2 - I*log(x - I)/2 f = S(36) g = x**5-2*x**4-2*x**3+4*x**2+x-2 assert ratint(f/g, x) == \ -4*log(x + 1) + 4*log(x - 2) + (12*x + 6)/(x**2 - 1) f = x**4-3*x**2+6 g = x**6-5*x**4+5*x**2+4 assert ratint(f/g, x) == \ atan(x) + atan(x**3) + atan(x/2 - 3*x**S(3)/2 + S(1)/2*x**5) f = x**7-24*x**4-4*x**2+8*x-8 g = x**8+6*x**6+12*x**4+8*x**2 assert ratint(f/g, x) == \ (4 + 6*x + 8*x**2 + 3*x**3)/(4*x + 4*x**3 + x**5) + log(x) assert ratint((x**3*f)/(x*g), x) == \ -(12 - 16*x + 6*x**2 - 14*x**3)/(4 + 4*x**2 + x**4) - \ 5*2**(S(1)/2)*atan(x*2**(S(1)/2)/2) + S(1)/2*x**2 - 3*log(2 + x**2) f = x**5-x**4+4*x**3+x**2-x+5 g = x**4-2*x**3+5*x**2-4*x+4 assert ratint(f/g, x) == \ x + S(1)/2*x**2 + S(1)/2*log(2-x+x**2) - (4*x-9)/(14-7*x+7*x**2) + \ 13*7**(S(1)/2)*atan(-S(1)/7*7**(S(1)/2) + 2*x*7**(S(1)/2)/7)/49 assert ratint(1/(x**2+x+1), x) == \ 2*3**(S(1)/2)*atan(3**(S(1)/2)/3 + 2*x*3**(S(1)/2)/3)/3 assert ratint(1/(x**3+1), x) == \ -log(1 - x + x**2)/6 + log(1 + x)/3 + 3**(S(1)/2)*atan(-3**(S(1)/2)/3 + 2*x*3**(S(1)/2)/3)/3 assert ratint(1/(x**2+x+1), x, real=False) == \ -I*3**half*log(half + x - half*I*3**half)/3 + \ I*3**half*log(half + x + half*I*3**half)/3 assert ratint(1/(x**3+1), x, real=False) == log(1 + x)/3 + \ (-S(1)/6 + I*3**half/6)*log(-half + x + I*3**half/2) + \ (-S(1)/6 - I*3**half/6)*log(-half + x - I*3**half/2) # Issue 1892 assert ratint(1/(x*(a+b*x)**3), x) == \ (sqrt(a**(-6))*log(x + (a - a**4*sqrt(a**(-6)))/(2*b)) + (3*a + 2*b*x)/(2*a**2*b**2*x**2 + 4*b*x*a**3 + 2*a**4) - sqrt(a**(-6))*log(x + (a + a**4*sqrt(a**(-6)))/(2*b))) assert ratint(x/(1 - x**2), x) == -log(x**2 - 1)/2 assert ratint(-x/(1 - x**2), x) == log(x**2 - 1)/2 def test_ratint_logpart(): assert ratint_logpart(x, x**2-9, x, t) == \ [(Poly(x**2 - 9, x), Poly(-2*t + 1, t))] assert ratint_logpart(x**2, x**3-5, x, t) == \ [(Poly(x**3 - 5, x), Poly(-3*t + 1, t))] def test_issue_2315(): assert ratint(1/(x**2 + 16), x) == atan(x/4)/4 def test_issue_2150(): assert ratint(1/(x**2 + a**2), x) == \ sqrt(-1/a**2)*log(x + a**2*sqrt(-1/a**2))/2 - sqrt(-1/a**2)*log(x - a**2*sqrt(-1/a**2))/2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/tests/test_integrals.py0000644000175000017500000006614312014170666027644 0ustar georgeskgeorgeskfrom sympy import (S, symbols, integrate, Integral, Derivative, exp, erf, oo, Symbol, Function, Rational, log, sin, cos, pi, E, I, Poly, LambertW, diff, Matrix, sympify, sqrt, atan, asin, acos, asinh, acosh, DiracDelta, Heaviside, Lambda, sstr, Add, Tuple, Eq, Interval, Sum, factor, trigsimp) from sympy.utilities.pytest import XFAIL, skip, raises from sympy.physics.units import m, s x,y,a,t,x_1,x_2,z = symbols('x,y,a,t,x_1,x_2,z') n = Symbol('n', integer=True) f = Function('f') def diff_test(i): """Return the set of symbols, s, which were used in testing that i.diff(s) agrees with i.doit().diff(s). If there is an error then the assertion will fail, causing the test to fail.""" syms = i.free_symbols for s in syms: assert (i.diff(s).doit() - i.doit().diff(s)).expand() == 0 return syms def test_improper_integral(): assert integrate(log(x), (x, 0, 1)) == -1 assert integrate(x**(-2), (x, 1, oo)) == 1 def test_constructor(): # this is shared by Sum, so testing Integral's constructor # is equivalent to testing Sum's s1 = Integral(n, n) assert s1.limits == (Tuple(n),) s2 = Integral(n, (n,)) assert s2.limits == (Tuple(n),) s3 = Integral(Sum(x, (x, 1, y))) assert s3.limits == (Tuple(y),) s4 = Integral(n, Tuple(n,)) assert s4.limits == (Tuple(n),) s5 = Integral(n, (n, Interval(1, 2))) assert s5.limits == (Tuple(n, 1, 2),) def test_basics(): assert Integral(0, x) != 0 assert Integral(x, (x, 1, 1)) != 0 assert Integral(oo, x) != oo assert Integral(S.NaN, x) == S.NaN assert diff(Integral(y, y), x) == 0 assert diff(Integral(x, (x,0,1)), x) == 0 assert diff(Integral(x, x), x) == x assert diff(Integral(t, (t,0,x)), x) == x + Integral(0, (t, 0, x)) e=(t+1)**2 assert diff(integrate(e, (t,0,x)), x) == \ diff(Integral(e, (t, 0, x)), x).doit().expand() == \ ((1+x)**2).expand() assert diff(integrate(e, (t,0,x)), t) == \ diff(Integral(e, (t,0,x)), t) == 0 assert diff(integrate(e, (t,0,x)), a) == \ diff(Integral(e, (t, 0, x)), a) == 0 assert diff(integrate(e, t), a) == diff(Integral(e, t), a) == 0 assert integrate(e, (t,a,x)).diff(x) == \ Integral(e, (t, a, x)).diff(x).doit().expand() assert Integral(e, (t, a, x)).diff(x).doit() == ((1+x)**2) assert integrate(e, (t,x,a)).diff(x).doit() == (-(1+x)**2).expand() assert integrate(t**2, (t,x,2*x)).diff(x) == 7*x**2 assert Integral(x, x).atoms() == set([x]) assert Integral(f(x), (x, 0, 1)).atoms() == set([S(0), S(1), x]) assert diff_test(Integral(x, (x, 3*y))) == set([y]) assert diff_test(Integral(x, (a, 3*y))) == set([x, y]) # sum integral of terms assert integrate(y + x + exp(x), x) == x*y + x**2/2 + exp(x) assert Integral(x).is_commutative n = Symbol('n', commutative=False) assert Integral(x, (x, n)).is_commutative is False assert Integral(n + x, x).is_commutative is False def test_basics_multiple(): assert diff_test(Integral(x, (x, 3*x, 5*y), (y, x, 2*x))) == set([x]) assert diff_test(Integral(x, (x, 5*y), (y, x, 2*x))) == set([x]) assert diff_test(Integral(x, (x, 5*y), (y, y, 2*x))) == set([x, y]) assert diff_test(Integral(y, y, x)) == set([x, y]) assert diff_test(Integral(y*x, x, y)) == set([x, y]) assert diff_test(Integral(x + y, y, (y, 1, x))) == set([x]) assert diff_test(Integral(x + y, (x, x, y), (y, y, x))) == set([x, y]) def test_integration(): assert integrate(0, (t,0,x)) == 0 assert integrate(3, (t,0,x)) == 3*x assert integrate(t, (t,0,x)) == x**2/2 assert integrate(3*t, (t,0,x))== 3*x**2/2 assert integrate(3*t**2, (t,0,x)) == x**3 assert integrate(1/t, (t,1,x)) == log(x) assert integrate(-1/t**2, (t,1,x)) == 1/x-1 assert integrate(t**2+5*t-8, (t,0,x)) == x**3/3+5*x**2/2-8*x assert integrate(x**2, x) == x**3/3 assert integrate((3*t*x)**5, x) == (3*t)**5 * x**6 / 6 b = Symbol("b") c = Symbol("c") assert integrate(a*t, (t,0,x))==a*x**2/2 assert integrate(a*t**4, (t,0,x))==a*x**5/5 assert integrate(a*t**2+b*t+c, (t,0,x))==a*x**3/3+b*x**2/2+c*x def test_multiple_integration(): assert integrate((x**2)*(y**2), (x,0,1), (y,-1,2)) == Rational(1) assert integrate((y**2)*(x**2), x, y) == Rational(1,9)*(x**3)*(y**3) assert integrate(1/(x+3)/(1+x)**3, x) == -S(1)/8*log(3 + x) + S(1)/8*log(1 + x) + x/(4 + 8*x + 4*x**2) def test_issue433(): assert integrate(exp(-x), (x,0,oo)) == 1 def test_issue461(): assert integrate(x**Rational(3,2), x) == 2*x**Rational(5,2)/5 assert integrate(x**Rational(1,2), x) == 2*x**Rational(3,2)/3 assert integrate(x**Rational(-3,2), x) == -2*x**Rational(-1,2) def test_integrate_poly(): p = Poly(x + x**2*y + y**3, x, y) qx = integrate(p, x) qy = integrate(p, y) assert isinstance(qx, Poly) == True assert isinstance(qy, Poly) == True assert qx.gens == (x, y) assert qy.gens == (x, y) assert qx.as_expr() == x**2/2 + x**3*y/3 + x*y**3 assert qy.as_expr() == x*y + x**2*y**2/2 + y**4/4 def test_integrate_poly_defined(): p = Poly(x + x**2*y + y**3, x, y) Qx = integrate(p, (x, 0, 1)) Qy = integrate(p, (y, 0, pi)) assert isinstance(Qx, Poly) == True assert isinstance(Qy, Poly) == True assert Qx.gens == (y,) assert Qy.gens == (x,) assert Qx.as_expr() == Rational(1,2) + y/3 + y**3 assert Qy.as_expr() == pi**4/4 + pi*x + pi**2*x**2/2 def test_integrate_omit_var(): y = Symbol('y') assert integrate(x) == x**2/2 raises(ValueError, "integrate(2)") raises(ValueError, "integrate(x*y)") def test_integrate_poly_accurately(): y = Symbol('y') assert integrate(x*sin(y), x) == x**2*sin(y)/2 # when passed to risch_norman, this will be a CPU hog, so this really # checks, that integrated function is recognized as polynomial assert integrate(x**1000*sin(y), x) == x**1001*sin(y)/1001 def test_issue536(): y = Symbol('y') assert integrate(x**2, y) == x**2*y assert integrate(x**2, (y, -1, 1)) == 2*x**2 # works in sympy and py.test but hangs in `setup.py test` def test_integrate_linearterm_pow(): # check integrate((a*x+b)^c, x) -- #400 y = Symbol('y') assert integrate(x**y, x) == x**(y+1)/(y+1) assert integrate((exp(y)*x + 1/y)**(1+sin(y)), x) == exp(-y)*(exp(y)*x + 1/y)**(2+sin(y)) / (2+sin(y)) def test_issue519(): assert integrate(pi*x**Rational(1,2),x) == 2*pi*x**Rational(3,2)/3 assert integrate(pi*x**Rational(1,2) + E*x**Rational(3,2),x) == \ 2*pi*x**Rational(3,2)/3 + \ 2*E *x**Rational(5,2)/5 def test_issue524(): assert integrate(cos((n+1) * x), x) == sin(x*(n+1)) / (n+1) assert integrate(cos((n-1) * x), x) == sin(x*(n-1)) / (n-1) assert integrate(cos((n+1) * x) + cos((n-1) * x), x) == \ sin(x*(n+1)) / (n+1) + \ sin(x*(n-1)) / (n-1) def test_issue565(): assert integrate(-1./2 * x * sin(n * pi * x/2), [x, -2, 0]) == 2*cos(pi*n)/(pi*n) assert integrate(-Rational(1)/2 * x * sin(n * pi * x/2), [x, -2, 0]) \ == 2*cos(pi*n)/(pi*n) def test_issue580(): # definite integration of rational functions gives wrong answers assert NS(Integral(1/(x**2-8*x+17), (x, 2, 4))) == '1.10714871779409' def test_issue587(): # remove this when fresnel itegrals are implemented assert integrate(sin(x**2), x) == Integral(sin(x**2), x) def test_integrate_units(): assert integrate(x * m/s, (x, 1*s, 5*s)) == 12*m*s def test_transcendental_functions(): assert integrate(LambertW(2*x), x) == -x + x*LambertW(2*x) + x/LambertW(2*x) def test_issue641(): f=4*log(x)-2*log(x)**2 fid=diff(integrate(f,x),x) assert abs(f.subs(x,42).evalf() - fid.subs(x,42).evalf()) < 1e-10 def test_issue689(): assert integrate(1/(1+x**2), x) == atan(x) def test_issue853(): f = sin(x) assert integrate(f, x) == -cos(x) raises(ValueError, "integrate(f, 2*x)") def test_issue1417(): assert integrate(2**x - 2*x, x) == 2**x/log(2) - x**2 def test_matrices(): M = Matrix(2, 2, lambda i, j: (i+j+1)*sin((i+j+1)*x)) assert integrate(M, x) == Matrix([ [-cos(x), -cos(2*x)], [-cos(2*x), -cos(3*x)], ]) # issue1012 def test_integrate_functions(): assert integrate(f(x), x) == Integral(f(x), x) assert integrate(f(x), (x,0,1)) == Integral(f(x), (x,0,1)) assert integrate(f(x)*diff(f(x), x), x) == f(x)**2/2 assert integrate(diff(f(x),x) / f(x),x) == log(f(x)) def test_integrate_derivatives(): assert integrate(Derivative(f(x), x), x) == f(x) assert integrate(Derivative(f(y), y), x) == x*Derivative(f(y), y) def test_transform(): a = Integral(x**2+1, (x, -1, 2)) assert a.doit() == a.transform(x, 3*x+1).doit() assert a.transform(x, 3*x+1).transform(x, 3*x+1, inverse=True) == a assert a.transform(x, 3*x+1, inverse=True).transform(x, 3*x+1) == a a = Integral(sin(1/x), (x, 0, 1)) assert a.transform(x, 1/x) == Integral(sin(x)/x**2, (x, 1, oo)) assert a.transform(x, 1/x).transform(x, 1/x) == a a = Integral(exp(-x**2), (x, -oo, oo)) assert a.transform(x, 2*x) == Integral(2*exp(-4*x**2), (x, -oo, oo)) # < 3 arg limit handled properly assert Integral(x, x).transform(x, a*x) == Integral(x*a**2, x) raises(ValueError, "a.transform(x, 1/x)") raises(ValueError, "a.transform(x, 1/x)") _3 = S(3) assert Integral(x, (x, 0, -_3)).transform(x, 1/x) == \ Integral(-1/x**3, (x, -oo, -1/_3)) assert Integral(x, (x, 0, _3)).transform(x, 1/x) == \ Integral(x**(-3), (x, 1/_3, oo)) def test_issue953(): f = S(1)/2*asin(x) + x*(1 - x**2)**(S(1)/2)/2 assert integrate(cos(asin(x)), x) == f assert integrate(sin(acos(x)), x) == f def NS(e, n=15, **options): return sstr(sympify(e).evalf(n, **options), full_prec=True) def test_evalf_integrals(): assert NS(Integral(x, (x, 2, 5)), 15) == '10.5000000000000' gauss = Integral(exp(-x**2), (x, -oo, oo)) assert NS(gauss, 15) == '1.77245385090552' assert NS(gauss**2 - pi + E*Rational(1,10**20), 15) in ('2.71828182845904e-20', '2.71828182845905e-20') # A monster of an integral from http://mathworld.wolfram.com/DefiniteIntegral.html t = Symbol('t') a = 8*sqrt(3)/(1+3*t**2) b = 16*sqrt(2)*(3*t+1)*(4*t**2+t+1)**Rational(3,2) c = (3*t**2+1)*(11*t**2+2*t+3)**2 d = sqrt(2)*(249*t**2+54*t+65)/(11*t**2+2*t+3)**2 f = a - b/c - d assert NS(Integral(f, (t, 0, 1)), 50) == NS((3*sqrt(2)-49*pi+162*atan(sqrt(2)))/12,50) # http://mathworld.wolfram.com/VardisIntegral.html assert NS(Integral(log(log(1/x))/(1+x+x**2), (x, 0, 1)), 15) == NS('pi/sqrt(3) * log(2*pi**(5/6) / gamma(1/6))', 15) # http://mathworld.wolfram.com/AhmedsIntegral.html assert NS(Integral(atan(sqrt(x**2+2))/(sqrt(x**2+2)*(x**2+1)), (x, 0, 1)), 15) == NS(5*pi**2/96, 15) # http://mathworld.wolfram.com/AbelsIntegral.html assert NS(Integral(x/((exp(pi*x)-exp(-pi*x))*(x**2+1)), (x, 0, oo)), 15) == NS('log(2)/2-1/4',15) # Complex part trimming # http://mathworld.wolfram.com/VardisIntegral.html assert NS(Integral(log(log(sin(x)/cos(x))), (x, pi/4, pi/2)), 15, chop=True) == \ NS('pi/4*log(4*pi**3/gamma(1/4)**4)', 15) # # Endpoints causing trouble (rounding error in integration points -> complex log) assert NS(2+Integral(log(2*cos(x/2)), (x, -pi, pi)), 17, chop=True) == NS(2, 17) assert NS(2+Integral(log(2*cos(x/2)), (x, -pi, pi)), 20, chop=True) == NS(2, 20) assert NS(2+Integral(log(2*cos(x/2)), (x, -pi, pi)), 22, chop=True) == NS(2, 22) # Needs zero handling assert NS(pi - 4*Integral('sqrt(1-x**2)', (x, 0, 1)), 15, maxn=30, chop=True) in ('0.0', '0') # Oscillatory quadrature a = Integral(sin(x)/x**2, (x, 1, oo)).evalf(maxn=15) assert 0.49 < a < 0.51 assert NS(Integral(sin(x)/x**2, (x, 1, oo)), quad='osc') == '0.504067061906928' assert NS(Integral(cos(pi*x+1)/x, (x, -oo, -1)), quad='osc') == '0.276374705640365' @XFAIL def test_evalf_issue_939(): # http://code.google.com/p/sympy/issues/detail?id=939 # The output form of an integral may differ by a step function between # revisions, making this test a bit useless. This can't be said about # other two tests. For now, all values of this evaluation are used here, # but in future this should be reconsidered. assert NS(integrate(1/(x**5+1), x).subs(x, 4), chop=True) in \ ['-0.000976138910649103', '0.965906660135753', '1.93278945918216'] assert NS(Integral(1/(x**5+1), (x, 2, 4))) == '0.0144361088886740' assert NS(integrate(1/(x**5+1), (x, 2, 4)), chop=True) == '0.0144361088886740' def xtest_failing_integrals(): #--- # Double integrals not implemented assert NS(Integral(sqrt(x)+x*y, (x, 1, 2), (y, -1, 1)), 15) == '2.43790283299492' # double integral + zero detection assert NS(Integral(sin(x+x*y), (x, -1, 1), (y, -1, 1)), 15) == '0.0' def test_integrate_DiracDelta(): assert integrate(DiracDelta(x),x) == Heaviside(x) assert integrate(DiracDelta(x) * f(x),x) == f(0) * Heaviside(x) assert integrate(DiracDelta(x) * f(x),(x,-oo,oo)) == f(0) assert integrate(DiracDelta(-x) * f(x),(x,-oo,oo)) == f(0) assert integrate(DiracDelta(-(x-1)) * f(x),(x,-oo,oo)) == f(1) assert integrate(DiracDelta(x) * f(x),(x,0,oo)) == f(0)/2 assert integrate(DiracDelta(x**2+x-2),x) - \ (Heaviside(-1 + x)/3 + Heaviside(2 + x)/3) == 0 assert integrate(cos(x)*(DiracDelta(x)+DiracDelta(x**2-1))*sin(x)*(x-pi),x) - \ (-pi*(cos(1)*Heaviside(-1 + x)*sin(1)/2 - cos(1)*Heaviside(1 + x)*sin(1)/2) + \ cos(1)*Heaviside(1 + x)*sin(1)/2 + cos(1)*Heaviside(-1 + x)*sin(1)/2) == 0 assert integrate(x_2*DiracDelta(x - x_2)*DiracDelta(x_2 - x_1), (x_2, -oo, oo)) == \ x*DiracDelta(x - x_1) assert integrate(x*y**2*z*DiracDelta(y - x)*DiracDelta(y - z)*DiracDelta(x - z), (y, -oo, oo)) \ == x**3*z*DiracDelta(x - z)**2 assert integrate((x+1)*DiracDelta(2*x), (x, -oo, oo)) == S(1)/2 assert integrate((x+1)*DiracDelta(2*x/3 + 4/S(9)), (x, -oo, oo)) == S(1)/2 def test_subs1(): e = Integral(exp(x-y), x) assert e.subs(y, 3) == Integral(exp(x-3), x) e = Integral(exp(x-y), (x, 0, 1)) assert e.subs(y, 3) == Integral(exp(x-3), (x, 0, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(x-y)*f(y), (y, -oo, oo)) assert conv.subs({x:0}) == Integral(exp(-2*y**2), (y, -oo, oo)) def test_subs2(): e = Integral(exp(x-y), x, t) assert e.subs(y, 3) == Integral(exp(x-3), x, t) e = Integral(exp(x-y), (x, 0, 1), (t, 0, 1)) assert e.subs(y, 3) == Integral(exp(x-3), (x, 0, 1), (t, 0, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(x-y)*f(y), (y, -oo, oo), (t, 0, 1)) assert conv.subs({x:0}) == Integral(exp(-2*y**2), (y, -oo, oo), (t, 0, 1)) def test_subs3(): e = Integral(exp(x-y), (x, 0, y), (t, y, 1)) assert e.subs(y, 3) == Integral(exp(x-3), (x, 0, 3), (t, 3, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(x-y)*f(y), (y, -oo, oo), (t, x, 1)) assert conv.subs({x:0}) == Integral(exp(-2*y**2), (y, -oo, oo), (t, 0, 1)) def test_subs4(): e = Integral(exp(x), (x, 0, y), (t, y, 1)) assert e.subs(y, 3) == Integral(exp(x), (x, 0, 3), (t, 3, 1)) f = Lambda(x, exp(-x**2)) conv = Integral(f(y)*f(y), (y, -oo, oo), (t, x, 1)) assert conv.subs({x:0}) == Integral(exp(-2*y**2), (y, -oo, oo), (t, 0, 1)) def test_subs5(): e = Integral(exp(-x**2), x) assert e.subs(x, 5) == Integral(exp(-x**2), (x, 5)) e = Integral(exp(-x**2), (x, -oo, oo)) assert e.subs(x, 5) == e e = Integral(exp(-x**2+y), x) assert e.subs(x, 5) == Integral(exp(y - x**2), (x, 5)) assert e.subs(y, 5) == Integral(exp(-x**2+5), x) e = Integral(exp(-x**2+y), (y, -oo, oo), (x, -oo, oo)) assert e.subs(x, 5) == e assert e.subs(y, 5) == e def test_subs6(): a, b = symbols('a b') e = Integral(x*y, (x, f(x), f(y))) assert e.subs(x, 1) == Integral(x*y, (x, f(1), f(y))) assert e.subs(y, 1) == Integral(x, (x, f(x), f(1))) e = Integral(x*y, (x, f(x), f(y)), (y, f(x), f(y))) assert e.subs(x, 1) == Integral(x*y, (x, f(1), f(y)), (y, f(1), f(y))) assert e.subs(y, 1) == Integral(x*y, (x, f(x), f(y)), (y, f(x), f(1))) e = Integral(x*y, (x, f(x), f(a)), (y, f(x), f(a))) assert e.subs(a, 1) == Integral(x*y, (x, f(x), f(1)), (y, f(x), f(1))) def test_subs7(): e = Integral(x, (x, 1, y), (y, 1, 2)) assert e.subs({x:1, y:2}) == e e = Integral(sin(x) + sin(y), (x, sin(x), sin(y)), (y, 1, 2)) assert e._eval_subs(sin(y), 1) == e assert e._eval_subs(sin(x), 1) == Integral(sin(x) + sin(y), (x, 1, sin(y)), (y, 1, 2)) def test_integration_variable(): raises(ValueError, "Integral(exp(-x**2), 3)") raises(ValueError, "Integral(exp(-x**2), (3, -oo, oo))") def test_expand_integral(): assert Integral(cos(x**2)*(sin(x**2)+1),(x, 0, 1)).expand() == Integral(cos(x**2)*sin(x**2) + cos(x**2), (x, 0, 1)) assert Integral(cos(x**2)*(sin(x**2)+1),x).expand() == Integral(cos(x**2)*sin(x**2) + cos(x**2), x) def test_as_sum_midpoint1(): e = Integral(sqrt(x**3+1), (x, 2, 10)) assert e.as_sum(1, method="midpoint") == 8*217**(S(1)/2) assert e.as_sum(2, method="midpoint") == 4*65**(S(1)/2) + 12*57**(S(1)/2) assert e.as_sum(3, method="midpoint") == 8*217**(S(1)/2)/3 + \ 8*3081**(S(1)/2)/27 + 8*52809**(S(1)/2)/27 assert e.as_sum(4, method="midpoint") == 2*730**(S(1)/2) + \ 4*7**(S(1)/2) + 4*86**(S(1)/2) + 6*14**(S(1)/2) assert abs(e.as_sum(4, method="midpoint").n() - e.n()) < 0.5 e = Integral(sqrt(x**3+y**3), (x, 2, 10), (y, 0, 10)) raises(NotImplementedError, "e.as_sum(4)") def test_as_sum_midpoint2(): e = Integral((x+y)**2, (x, 0, 1)) assert e.as_sum(1, method="midpoint").expand() == S(1)/4 + y + y**2 assert e.as_sum(2, method="midpoint").expand() == S(5)/16 + y + y**2 assert e.as_sum(3, method="midpoint").expand() == S(35)/108 + y + y**2 assert e.as_sum(4, method="midpoint").expand() == S(21)/64 + y + y**2 def test_as_sum_left(): e = Integral((x+y)**2, (x, 0, 1)) assert e.as_sum(1, method="left").expand() == y**2 assert e.as_sum(2, method="left").expand() == S(1)/8 + y/2 + y**2 assert e.as_sum(3, method="left").expand() == S(5)/27 + 2*y/3 + y**2 assert e.as_sum(4, method="left").expand() == S(7)/32 + 3*y/4 + y**2 def test_as_sum_right(): e = Integral((x+y)**2, (x, 0, 1)) assert e.as_sum(1, method="right").expand() == 1 + 2*y + y**2 assert e.as_sum(2, method="right").expand() == S(5)/8 + 3*y/2 + y**2 assert e.as_sum(3, method="right").expand() == S(14)/27 + 4*y/3 + y**2 assert e.as_sum(4, method="right").expand() == S(15)/32 + 5*y/4 + y**2 def test_as_sum_raises(): e = Integral((x+y)**2, (x, 0, 1)) raises(ValueError, "e.as_sum(-1)") raises(ValueError, "e.as_sum(0)") raises(NotImplementedError, "e.as_sum(oo)") raises(NotImplementedError, "e.as_sum(3, method='xxxx2')") def test_nested_doit(): e = Integral(Integral(x, x), x) f = Integral(x, x, x) assert e.doit() == f.doit() def test_issue1566(): # Allow only upper or lower limit evaluation e = Integral(x**2, (x, None, 1)) f = Integral(x**2, (x, 1, None)) assert e.doit() == Rational(1, 3) assert f.doit() == Rational(-1, 3) assert Integral(x*y, (x, None, y)).subs(y, t) == Integral(x*t, (x, None, t)) assert Integral(x*y, (x, y, None)).subs(y, t) == Integral(x*t, (x, t, None)) #FIXME-py3k: This fails somewhere down the line with: #FIXME-py3k: TypeError: type Float doesn't define __round__ method assert integrate(x**2, (x, None, 1)) == Rational(1, 3) assert integrate(x**2, (x, 1, None)) == Rational(-1, 3) assert integrate("x**2", ("x", "1", None)) == Rational(-1, 3) def test_integral_reconstruct(): e = Integral(x**2, (x, -1, 1)) assert e == Integral(*e.args) def test_doit(): e = Integral(Integral(2*x), (x, 0, 1)) assert e.doit() == Rational(1, 3) assert e.doit(deep=False) == Rational(1, 3) f = Function('f') # doesn't matter if the integral can't be performed assert Integral(f(x), (x, 1, 1)).doit() == 0 # doesn't matter if the limits can't be evaluated assert Integral(0, (x, 1, Integral(f(x), x))).doit() == 0 def issue_1785(): assert integrate(sqrt(x)*(1+x)) == 2*x**Rational(3, 2)/3 + 2*x**Rational(5, 2)/5 assert integrate(x**x*(1+log(x))) == x**x def test_is_number(): from sympy.abc import x, y, z from sympy import cos, sin assert Integral(x).is_number == False assert Integral(1, x).is_number == False assert Integral(1, (x, 1)).is_number == True assert Integral(1, (x, 1, 2)).is_number == True assert Integral(1, (x, 1, y)).is_number == False assert Integral(x, y).is_number == False assert Integral(x, (y, 1, x)).is_number == False assert Integral(x, (y, 1, 2)).is_number == False assert Integral(x, (x, 1, 2)).is_number == True assert Integral(x, (y, 1, 1)).is_number == True assert Integral(x*y, (x, 1, 2), (y, 1, 3)).is_number == True assert Integral(x*y, (x, 1, 2), (y, 1, z)).is_number == False assert Integral(x, (x, 1)).is_number == True assert Integral(x, (x, 1, Integral(y, (y, 1, 2)))).is_number == True # it is possible to get a false negative if the integrand is # actually an unsimplified zero, but this is true of is_number in general. assert Integral(sin(x)**2 + cos(x)**2 - 1, x).is_number == False def test_symbols(): from sympy.abc import x, y, z assert Integral(0, x).free_symbols == set() assert Integral(x).free_symbols == set([x]) assert Integral(x, (x, None, y)).free_symbols == set([y]) assert Integral(x, (x, y, None)).free_symbols == set([y]) assert Integral(x, (x, 1, y)).free_symbols == set([y]) assert Integral(x, (x, y, 1)).free_symbols == set([y]) assert Integral(x, (x, x, y)).free_symbols == set([x, y]) assert Integral(x, x, y).free_symbols == set([x, y]) assert Integral(x, (x, 1, 2)).free_symbols == set() assert Integral(x, (y, 1, 2)).free_symbols == set([x]) assert Integral(x, (y, z, z)).free_symbols == set() assert Integral(x, (y, 1, 2), (y, None, None)).free_symbols == set([x, y]) assert Integral(x, (y, 1, 2), (x, 1, y)).free_symbols == set([y]) assert Integral(2, (y, 1, 2), (y, 1, x), (x, 1, 2)).free_symbols == set() assert Integral(2, (y, x, 2), (y, 1, x), (x, 1, 2)).free_symbols == set() assert Integral(2, (x, 1, 2), (y, x, 2), (y, 1, 2)).free_symbols == set([x]) def test_is_zero(): from sympy.abc import x, m, n assert Integral(0, (x, 1, x)).is_zero assert Integral(1, (x, 1, 1)).is_zero assert Integral(1, (x, 1, 2)).is_zero is False assert Integral(sin(m*x)*cos(n*x), (x, 0, 2*pi)).is_zero is None def test_series(): from sympy.abc import x i = Integral(cos(x)) e = i.lseries(x) assert i.nseries(x, n=8).removeO() == Add(*[e.next() for j in range(4)]) def test_issue_1304(): z = Symbol('z', positive=True) assert integrate(sqrt(x**2 + z**2),x) == z**2*asinh(x/z)/2 + x*(x**2 + z**2)**(S(1)/2)/2 assert integrate(sqrt(x**2 - z**2),x) == -z**2*acosh(x/z)/2 + x*(x**2 - z**2)**(S(1)/2)/2 @XFAIL def test_issue_1304_2(): assert integrate(sqrt(-x**2 - 4), x) == -2*atan(x/(-4 - x**2)**(S(1)/2)) + x*(-4 - x**2)**(S(1)/2)/2 def tets_issue_1001(): R = Symbol('R', positive=True) assert integrate(sqrt(R**2 - x**2), (x, 0, R)) == pi*R**2/4 def test_issue2068(): from sympy.abc import w, x, y, z f = Function('f') assert Integral(Integral(f(x), x), x) == Integral(f(x), x, x) assert Integral(f(x)).args == (f(x), Tuple(x)) assert Integral(Integral(f(x))).args == (f(x), Tuple(x), Tuple(x)) assert Integral(Integral(f(x)), y).args == (f(x), Tuple(x), Tuple(y)) assert Integral(Integral(f(x), z), y).args == (f(x), Tuple(z), Tuple(y)) assert Integral(Integral(Integral(f(x), x), y), z).args == \ (f(x), Tuple(x), Tuple(y), Tuple(z)) assert integrate(Integral(f(x), x), x) == Integral(f(x), x, x) assert integrate(Integral(f(x), y), x) == Integral(y*f(x), x) assert integrate(Integral(f(x), x), y) == Integral(y*f(x), x) assert integrate(Integral(2, x), x) == x**2 assert integrate(Integral(2, x), y) == 2*x*y # don't re-order given limits assert Integral(1, x, y).args != Integral(1, y, x).args # do as many as possibble assert Integral(f(x), y, x, y, x).doit() == Integral(y**2*f(x)/2, x, x) assert Integral(f(x), (x, 1, 2), (w, 1, x), (z, 1, y)).doit() == \ Integral(-f(x) + y*f(x), (x, 1, 2), (w, 1, x)) def test_issue_1791(): z = Symbol('z', positive=True) assert integrate(exp(-log(x)**2),x) == pi**(S(1)/2)*erf(-S(1)/2 + log(x))*exp(S(1)/4)/2 assert integrate(exp(log(x)**2),x) == -I*pi**(S(1)/2)*erf(I*log(x) + I/2)*exp(-S(1)/4)/2 assert integrate(exp(-z*log(x)**2),x) == \ pi**(S(1)/2)*erf(z**(S(1)/2)*log(x) - 1/(2*z**(S(1)/2)))*exp(S(1)/(4*z))/(2*z**(S(1)/2)) def test_issue_1277(): from sympy import simplify assert (simplify(integrate(n*(x**(1/n)-1), (x, 0, S.Half))) == simplify((n**2 - 2**(S(1)/n)*n**2 - n*2**(S(1)/n)) / (2**(1 + S(1)/n) + n*2**(1 + S(1)/n)))) def test_issue_1418(): assert integrate((x**Rational(1,2) - x**3)/x**Rational(1,3), x) == \ 6*x**(Rational(7,6))/7 - 3*x**(Rational(11,3))/11 def test_issue_1100(): assert integrate(exp(-I*2*pi*y*x)*x, (x, -oo, oo)) is S.NaN def test_issue_841(): from sympy import simplify a = Symbol('a', positive = True) b = Symbol('b') c = Symbol('c') d = Symbol('d', positive = True) assert integrate(exp(-x**2 + I*c*x), x) == pi**(S(1)/2)*erf(x - I*c/2)*exp(-c**S(2)/4)/2 assert integrate(exp(a*x**2 + b*x + c), x) == \ I*pi**(S(1)/2)*erf(-I*x*a**(S(1)/2) - I*b/(2*a**(S(1)/2)))*exp(c)*exp(-b**2/(4*a))/(2*a**(S(1)/2)) assert simplify(integrate(exp(-a*x**2 + 2*d*x), (x, -oo, oo))) == pi**(S(1)/2)*(1 + erf(oo - d/a**(S(1)/2))) \ *exp(d**2/a)/(2*a**(S(1)/2)) def test_issue_2314(): # Note that this is not the same as testing ratint() becuase integrate() # pulls out the coefficient. a = Symbol('a') assert integrate(-a/(a**2+x**2), x) == \ -a*(sqrt(-1/a**2)*log(x + a**2*sqrt(-1/a**2))/2 - sqrt(-1/a**2)*log(x - a**2*sqrt(-1/a**2))/2) def test_issue_1793a(): A, z, c = symbols('A z c') P1 = -A*exp(-z) P2 = -A/(c*t)*(sin(x)**2 + cos(y)**2) assert integrate(c*(P2 - P1), t) == \ c*(A*(-cos(y)**2 - sin(x)**2)*log(c*t)/c + A*t*exp(-z)) def test_issue_1793b(): # Issues relating to issue 1497 are making the actual result of this hard # to test. The answer should be something like # # (-sin(y) + sqrt(-72 + 48*cos(y) - 8*cos(y)**2)/2)*log(x + sqrt(-72 + # 48*cos(y) - 8*cos(y)**2)/(2*(3 - cos(y)))) + (-sin(y) - sqrt(-72 + # 48*cos(y) - 8*cos(y)**2)/2)*log(x - sqrt(-72 + 48*cos(y) - # 8*cos(y)**2)/(2*(3 - cos(y)))) + x**2*sin(y)/2 + 2*x*cos(y) expr = (sin(y)*x**3 + 2*cos(y)*x**2 + 12)/(x**2 + 2) assert trigsimp(factor(integrate(expr, x).diff(x) - expr)) == 0 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/integrals/__init__.py0000644000175000017500000000045312014170666025202 0ustar georgeskgeorgesk"""Integration functions that integrates a sympy expression. Examples -------- >>> from sympy import integrate, sin >>> from sympy.abc import x >>> integrate(1/x,x) log(x) >>> integrate(sin(x),x) -cos(x) """ from integrals import integrate, Integral, line_integrate wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/0000755000175000017500000000000012014170666022701 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/summations.py0000644000175000017500000003522112014170666025455 0ustar georgeskgeorgeskfrom sympy.core import (Expr, S, C, sympify, Wild, Dummy, Derivative) from sympy.functions.elementary.piecewise import piecewise_fold from sympy.concrete.gosper import gosper_sum from sympy.polys import apart, PolynomialError from sympy.solvers import solve class Sum(Expr): """Represents unevaluated summation.""" def __new__(cls, function, *symbols, **assumptions): from sympy.integrals.integrals import _process_limits # Any embedded piecewise functions need to be brought out to the # top level so that integration can go into piecewise mode at the # earliest possible moment. function = piecewise_fold(sympify(function)) if function is S.NaN: return S.NaN if not symbols: raise ValueError("Summation variables must be given") limits, sign = _process_limits(*symbols) # Only limits with lower and upper bounds are supported; the indefinite Sum # is not supported if any(len(l) != 3 or None in l for l in limits): raise ValueError('Sum requires values for lower and upper bounds.') obj = Expr.__new__(cls, **assumptions) arglist = [sign*function] arglist.extend(limits) obj._args = tuple(arglist) return obj @property def function(self): return self._args[0] @property def limits(self): return self._args[1:] @property def variables(self): """Return a list of the summation variables >>> from sympy import Sum >>> from sympy.abc import x, i >>> Sum(x**i, (i, 1, 3)).variables [i] """ return [l[0] for l in self.limits] @property def free_symbols(self): """ This method returns the symbols that will exist when the summation is evaluated. This is useful if one is trying to determine whether a sum is dependent on a certain symbol or not. >>> from sympy import Sum >>> from sympy.abc import x, y >>> Sum(x, (x, y, 1)).free_symbols set([y]) """ from sympy.integrals.integrals import _free_symbols return _free_symbols(self.function, self.limits) def doit(self, **hints): #if not hints.get('sums', True): # return self f = self.function for limit in self.limits: f = eval_sum(f, limit) if f is None: return self if hints.get('deep', True): return f.doit(**hints) else: return f def _eval_summation(self, f, x): return def _eval_derivative(self, x): """ Differentiate wrt x as long as x is not in the free symbols of any of the upper or lower limits. Sum(a*b*x, (x, 1, a)) can be differentiated wrt x or b but not `a` since the value of the sum is discontinuous in `a`. In a case involving a limit variable, the unevaluated derivative is returned. """ # diff already confirmed that x is in the free symbols of self, but we # don't want to differentiate wrt any free symbol in the upper or lower # limits # XXX remove this test for free_symbols when the default _eval_derivative is in if x not in self.free_symbols: return S.Zero # get limits and the function f, limits = self.function, list(self.limits) limit = limits.pop(-1) if limits: # f is the argument to a Sum f = Sum(f, *limits) if len(limit) == 3: _, a, b = limit if x in a.free_symbols or x in b.free_symbols: return None df = Derivative(f, x, **{'evaluate': True}) rv = Sum(df, limit) if limit[0] not in df.free_symbols: rv = rv.doit() return rv else: return NotImplementedError('Lower and upper bound expected.') def euler_maclaurin(self, m=0, n=0, eps=0, eval_integral=True): """ Return an Euler-Maclaurin approximation of self, where m is the number of leading terms to sum directly and n is the number of terms in the tail. With m = n = 0, this is simply the corresponding integral plus a first-order endpoint correction. Returns (s, e) where s is the Euler-Maclaurin approximation and e is the estimated error (taken to be the magnitude of the first omitted term in the tail): >>> from sympy.abc import k, a, b >>> from sympy import Sum >>> Sum(1/k, (k, 2, 5)).doit().evalf() 1.28333333333333 >>> s, e = Sum(1/k, (k, 2, 5)).euler_maclaurin() >>> s -log(2) + 7/20 + log(5) >>> from sympy import sstr >>> print sstr((s.evalf(), e.evalf()), full_prec=True) (1.26629073187416, 0.0175000000000000) The endpoints may be symbolic: >>> s, e = Sum(1/k, (k, a, b)).euler_maclaurin() >>> s -log(a) + log(b) + 1/(2*b) + 1/(2*a) >>> e Abs(-1/(12*b**2) + 1/(12*a**2)) If the function is a polynomial of degree at most 2n+1, the Euler-Maclaurin formula becomes exact (and e = 0 is returned): >>> Sum(k, (k, 2, b)).euler_maclaurin() (b**2/2 + b/2 - 1, 0) >>> Sum(k, (k, 2, b)).doit() b**2/2 + b/2 - 1 With a nonzero eps specified, the summation is ended as soon as the remainder term is less than the epsilon. """ m = int(m) n = int(n) f = self.function assert len(self.limits) == 1 i, a, b = self.limits[0] s = S.Zero if m: for k in range(m): term = f.subs(i, a+k) if (eps and term and abs(term.evalf(3)) < eps): return s, abs(term) s += term a += m x = Dummy('x') I = C.Integral(f.subs(i, x), (x, a, b)) if eval_integral: I = I.doit() s += I def fpoint(expr): if b is S.Infinity: return expr.subs(i, a), 0 return expr.subs(i, a), expr.subs(i, b) fa, fb = fpoint(f) iterm = (fa + fb)/2 g = f.diff(i) for k in xrange(1, n+2): ga, gb = fpoint(g) term = C.bernoulli(2*k)/C.factorial(2*k)*(gb-ga) if (eps and term and abs(term.evalf(3)) < eps) or (k > n): break s += term g = g.diff(i, 2) return s + iterm, abs(term) def _eval_subs(self, old, new): if self == old: return new newlimits = [] for lim in self.limits: if lim[0] == old: return self newlimits.append( (lim[0],lim[1].subs(old,new),lim[2].subs(old,new)) ) return Sum(self.args[0].subs(old, new), *newlimits) def summation(f, *symbols, **kwargs): """ Compute the summation of f with respect to symbols. The notation for symbols is similar to the notation used in Integral. summation(f, (i, a, b)) computes the sum of f with respect to i from a to b, i.e., b ____ \ ` summation(f, (i, a, b)) = ) f /___, i = a If it cannot compute the sum, it returns an unevaluated Sum object. Repeated sums can be computed by introducing additional symbols tuples:: >>> from sympy import summation, oo, symbols, log >>> i, n, m = symbols('i n m', integer=True) >>> summation(2*i - 1, (i, 1, n)) n**2 >>> summation(1/2**i, (i, 0, oo)) 2 >>> summation(1/log(n)**n, (n, 2, oo)) Sum(log(n)**(-n), (n, 2, oo)) >>> summation(i, (i, 0, n), (n, 0, m)) m**3/6 + m**2/2 + m/3 >>> from sympy.abc import x >>> from sympy import factorial >>> summation(x**n/factorial(n), (n, 0, oo)) exp(x) """ return Sum(f, *symbols, **kwargs).doit(deep=False) def telescopic_direct(L, R, n, limits): """Returns the direct summation of the terms of a telescopic sum L is the term with lower index R is the term with higher index n difference between the indexes of L and R For example: >>> from sympy.concrete.summations import telescopic_direct >>> from sympy.abc import k, a, b >>> telescopic_direct(1/k, -1/(k+2), 2, (k, a, b)) -1/(b + 2) - 1/(b + 1) + 1/(a + 1) + 1/a """ (i, a, b) = limits s = 0 for m in xrange(n): s += L.subs(i,a+m) + R.subs(i,b-m) return s def telescopic(L, R, limits): '''Tries to perform the summation using the telescopic property return None if not possible ''' (i, a, b) = limits if L.is_Add or R.is_Add: return None # We want to solve(L.subs(i, i + m) + R, m) # First we try a simple match since this does things that # solve doesn't do, e.g. solve(f(k+m)-f(k), m) fails k = Wild("k") sol = (-R).match(L.subs(i, i + k)) s = None if sol and k in sol: s = sol[k] if not (s.is_Integer and L.subs(i,i + s) == -R): #sometimes match fail(f(x+2).match(-f(x+k))->{k: -2 - 2x})) s = None # But there are things that match doesn't do that solve # can do, e.g. determine that 1/(x + m) = 1/(1 - x) when m = 1 if s is None: m = Dummy('m') try: sol = solve(L.subs(i, i + m) + R, m) or [] except NotImplementedError: return None sol = [si for si in sol if si.is_Integer and (L.subs(i,i + si) + R).expand().is_zero] if len(sol) != 1: return None s = sol[0] if s < 0: return telescopic_direct(R, L, abs(s), (i, a, b)) elif s > 0: return telescopic_direct(L, R, s, (i, a, b)) def eval_sum(f, limits): (i, a, b) = limits if f is S.Zero: return S.Zero if i not in f.free_symbols: return f*(b - a + 1) definite = a.is_Integer and b.is_Integer # Doing it directly may be faster if there are very few terms. if definite and (b-a < 100): return eval_sum_direct(f, (i, a, b)) # Try to do it symbolically. Even when the number of terms is known, # this can save time when b-a is big. # We should try to transform to partial fractions value = eval_sum_symbolic(f.expand(), (i, a, b)) if value is not None: return value # Do it directly if definite: return eval_sum_direct(f, (i, a, b)) def eval_sum_symbolic(f, limits): (i, a, b) = limits if not f.has(i): return f*(b-a+1) # Linearity if f.is_Mul: L, R = f.as_two_terms() if not L.has(i): sR = eval_sum_symbolic(R, (i, a, b)) if sR: return L*sR if not R.has(i): sL = eval_sum_symbolic(L, (i, a, b)) if sL: return R*sL try: f = apart(f, i) # see if it becomes an Add except PolynomialError: pass if f.is_Add: L, R = f.as_two_terms() lrsum = telescopic(L, R, (i, a, b)) if lrsum: return lrsum lsum = eval_sum_symbolic(L, (i, a, b)) rsum = eval_sum_symbolic(R, (i, a, b)) if None not in (lsum, rsum): return lsum + rsum # Polynomial terms with Faulhaber's formula n = Wild('n') result = f.match(i**n) if result is not None: n = result[n] if n.is_Integer: if n >= 0: return ((C.bernoulli(n+1, b+1) - C.bernoulli(n+1, a))/(n+1)).expand() elif a.is_Integer and a >= 1: if n == -1: return C.harmonic(b) - C.harmonic(a - 1) else: return C.harmonic(b, abs(n)) - C.harmonic(a - 1, abs(n)) # Geometric terms c1 = C.Wild('c1', exclude=[i]) c2 = C.Wild('c2', exclude=[i]) c3 = C.Wild('c3', exclude=[i]) e = f.match(c1**(c2*i+c3)) if e is not None: c1 = c1.subs(e) c2 = c2.subs(e) c3 = c3.subs(e) # TODO: more general limit handling return c1**c3 * (c1**(a*c2) - c1**(c2+b*c2)) / (1 - c1**c2) r = gosper_sum(f, (i, a, b)) if not r in (None, S.NaN): return r return eval_sum_hyper(f, (i, a, b)) def _eval_sum_hyper(f, i, a): """ Returns (res, cond). Sums from a to oo. """ from sympy.functions import hyper from sympy.simplify import hyperexpand, hypersimp, fraction from sympy.polys.polytools import Poly, factor if a != 0: return _eval_sum_hyper(f.subs(i, i + a), i, 0) if f.subs(i, 0) == 0: return _eval_sum_hyper(f.subs(i, i + 1), i, 0) hs = hypersimp(f, i) if hs is None: return None numer, denom = fraction(factor(hs)) top, topl = numer.as_coeff_mul(i) bot, botl = denom.as_coeff_mul(i) ab = [top, bot] factors = [topl, botl] params = [[], []] for k in range(2): for fac in factors[k]: mul = 1 if fac.is_Pow: mul = fac.exp fac = fac.base if not mul.is_Integer: return None p = Poly(fac, i) if p.degree() != 1: return None m, n = p.all_coeffs() ab[k] *= m**mul params[k] += [n/m]*mul # Add "1" to numerator parameters, to account for implicit n! in # hypergeometric series. ap = params[0] + [1] bq = params[1] x = ab[0]/ab[1] h = hyper(ap, bq, x) return f.subs(i, 0)*hyperexpand(h), h.convergence_statement def eval_sum_hyper(f, (i, a, b)): from sympy.functions import Piecewise from sympy import oo, And if b != oo: if a == -oo: res = _eval_sum_hyper(f.subs(i, -i), i, -b) if res is not None: return Piecewise(res, (Sum(f, (i, a, b)), True)) else: return None if a == -oo: res1 = _eval_sum_hyper(f.subs(i, -i), i, 1) res2 = _eval_sum_hyper(f, i, 0) if res1 is None or res2 is None: return None res1, cond1 = res1 res2, cond2 = res2 cond = And(cond1, cond2) if cond is False: return None return Piecewise((res1 + res2, cond), (Sum(f, (i, a, b)), True)) # Now b == oo, a != -oo res = _eval_sum_hyper(f, i, a) if res is not None: return Piecewise(res, (Sum(f, (i, a, b)), True)) def eval_sum_direct(expr, limits): (i, a, b) = limits s = S.Zero if i in expr.free_symbols: for j in xrange(a, b+1): s += expr.subs(i, j) else: for j in xrange(a, b+1): s += expr return s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/products.py0000644000175000017500000000737512014170666025132 0ustar georgeskgeorgeskfrom sympy.core import Expr, S, C, Mul, sympify from sympy.core.compatibility import is_sequence from sympy.polys import quo, roots from sympy.simplify import powsimp class Product(Expr): """Represents unevaluated product. """ def __new__(cls, term, *symbols, **assumptions): term = sympify(term) if term.is_Number: if term is S.NaN: return S.NaN elif term is S.Infinity: return S.NaN elif term is S.NegativeInfinity: return S.NaN elif term is S.Zero: return S.Zero elif term is S.One: return S.One if len(symbols) == 1: symbol = symbols[0] if isinstance(symbol, C.Equality): k = symbol.lhs a = symbol.rhs.start n = symbol.rhs.end elif is_sequence(symbol): k, a, n = symbol else: raise ValueError("Invalid arguments") k, a, n = map(sympify, (k, a, n)) if isinstance(a, C.Number) and isinstance(n, C.Number): return Mul(*[term.subs(k, i) for i in xrange(int(a), int(n)+1)]) else: raise NotImplementedError obj = Expr.__new__(cls, **assumptions) obj._args = (term, k, a, n) return obj @property def term(self): return self._args[0] @property def index(self): return self._args[1] @property def lower(self): return self._args[2] @property def upper(self): return self._args[3] def doit(self, **hints): term = self.term lower = self.lower upper = self.upper if hints.get('deep', True): term = term.doit(**hints) lower = lower.doit(**hints) upper = upper.doit(**hints) prod = self._eval_product(lower, upper, term) if prod is not None: return powsimp(prod) else: return self def _eval_product(self, a, n, term): from sympy import summation, Sum k = self.index if not term.has(k): return term**(n-a+1) elif term.is_polynomial(k): poly = term.as_poly(k) A = B = Q = S.One all_roots = roots(poly, multiple=True) for r in all_roots: A *= C.RisingFactorial(a-r, n-a+1) Q *= n - r if len(all_roots) < poly.degree(): B = Product(quo(poly, Q.as_poly(k)), (k, a, n)) return poly.LC()**(n-a+1) * A * B elif term.is_Add: p, q = term.as_numer_denom() p = self._eval_product(a, n, p) q = self._eval_product(a, n, q) return p / q elif term.is_Mul: exclude, include = [], [] for t in term.args: p = self._eval_product(a, n, t) if p is not None: exclude.append(p) else: include.append(t) if not exclude: return None else: A, B = Mul(*exclude), term._new_rawargs(*include) return A * Product(B, (k, a, n)) elif term.is_Pow: if not term.base.has(k): s = summation(term.exp, (k, a, n)) if not isinstance(s, Sum): return term.base**s elif not term.exp.has(k): p = self._eval_product(a, n, term.base) if p is not None: return p**term.exp def product(*args, **kwargs): prod = Product(*args, **kwargs) if isinstance(prod, Product): return prod.doit(deep=False) else: return prod wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/gosper.py0000644000175000017500000001153712014170666024561 0ustar georgeskgeorgesk"""Gosper's algorithm for hypergeometric summation. """ from sympy.core import S, Dummy, symbols from sympy.core.compatibility import is_sequence from sympy.polys import Poly, parallel_poly_from_expr, factor from sympy.solvers import solve from sympy.simplify import hypersimp def gosper_normal(f, g, n, polys=True): r""" Compute the Gosper's normal form of ``f`` and ``g``. Given relatively prime univariate polynomials ``f`` and ``g``, rewrite their quotient to a normal form defined as follows:: .. math:: \frac{f(n)}{g(n)} = Z \cdot \frac{A(n) C(n+1)}{B(n) C(n)} where ``Z`` is an arbitrary constant and ``A``, ``B``, ``C`` are monic polynomials in ``n`` with the following properties: 1. `\gcd(A(n), B(n+h)) = 1 \forall h \in \mathbb{N}` 2. `\gcd(B(n), C(n+1)) = 1` 3. `\gcd(A(n), C(n)) = 1` This normal form, or rational factorization in other words, is a crucial step in Gosper's algorithm and in solving of difference equations. It can be also used to decide if two hypergeometric terms are similar or not. This procedure will return a tuple containing elements of this factorization in the form ``(Z*A, B, C)``. **Examples** >>> from sympy.concrete.gosper import gosper_normal >>> from sympy.abc import n >>> gosper_normal(4*n+5, 2*(4*n+1)*(2*n+3), n, polys=False) (1/4, n + 3/2, n + 1/4) """ (p, q), opt = parallel_poly_from_expr((f, g), n, field=True, extension=True) a, A = p.LC(), p.monic() b, B = q.LC(), q.monic() C, Z = A.one, a/b h = Dummy('h') D = Poly(n + h, n, h, domain=opt.domain) R = A.resultant(B.compose(D)) roots = set(R.ground_roots().keys()) for r in set(roots): if not r.is_Integer or r < 0: roots.remove(r) for i in sorted(roots): d = A.gcd(B.shift(+i)) A = A.quo(d) B = B.quo(d.shift(-i)) for j in xrange(1, i+1): C *= d.shift(-j) A = A.mul_ground(Z) if not polys: A = A.as_expr() B = B.as_expr() C = C.as_expr() return A, B, C def gosper_term(f, n): """ Compute Gosper's hypergeometric term for ``f``. Suppose ``f`` is a hypergeometric term such that:: .. math:: s_n = \sum_{k=0}^{n-1} f_k and `f_k` doesn't depend on `n`. Returns a hypergeometric term `g_n` such that `g_{n+1} - g_n = f_n`. **Examples** >>> from sympy.concrete.gosper import gosper_term >>> from sympy.functions import factorial >>> from sympy.abc import n >>> gosper_term((4*n + 1)*factorial(n)/factorial(2*n + 1), n) (-n - 1/2)/(n + 1/4) """ r = hypersimp(f, n) if r is None: return None # 'f' is *not* a hypergeometric term p, q = r.as_numer_denom() A, B, C = gosper_normal(p, q, n) B = B.shift(-1) N = S(A.degree()) M = S(B.degree()) K = S(C.degree()) if (N != M) or (A.LC() != B.LC()): D = set([K - max(N, M)]) elif not N: D = set([K - N + 1, S(0)]) else: D = set([K - N + 1, (B.nth(N - 1) - A.nth(N - 1))/A.LC()]) for d in set(D): if not d.is_Integer or d < 0: D.remove(d) if not D: return None # 'f(n)' is *not* Gosper-summable d = max(D) coeffs = symbols('c:%s' % (d + 1), cls=Dummy) domain = A.get_domain().inject(*coeffs) x = Poly(coeffs, n, domain=domain) H = A*x.shift(1) - B*x - C solution = solve(H.coeffs(), coeffs) if solution is None: return None # 'f(n)' is *not* Gosper-summable x = x.as_expr().subs(solution) for coeff in coeffs: if coeff not in solution: x = x.subs(coeff, 0) if x is S.Zero: return None # 'f(n)' is *not* Gosper-summable else: return B.as_expr()*x/C.as_expr() def gosper_sum(f, k): """ Gosper's hypergeometric summation algorithm. Given a hypergeometric term ``f`` such that:: .. math:: s_n = \sum_{k=0}^{n-1} f_k and `f(n)` doesn't depend on `n`, returns `g_{n} - g(0)` where `g_{n+1} - g_n = f_n`, or ``None`` if `s_n` can not be expressed in closed form as a sum of hypergeometric terms. **Examples** >>> from sympy.concrete.gosper import gosper_sum >>> from sympy.functions import factorial >>> from sympy.abc import n, k >>> gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) (-n! + 2*(2*n + 1)!)/(2*n + 1)! **References** .. [1] Marko Petkovsek, Herbert S. Wilf, Doron Zeilberger, A = B, AK Peters, Ltd., Wellesley, MA, USA, 1997, pp. 73--100 """ indefinite = False if is_sequence(k): k, a, b = k else: indefinite = True g = gosper_term(f, k) if g is None: return None if indefinite: result = f*g else: result = (f*(g+1)).subs(k, b) - (f*g).subs(k, a) return factor(result) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/tests/0000755000175000017500000000000012014170666024043 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/tests/test_sums_products.py0000644000175000017500000002454712014170666030402 0ustar georgeskgeorgeskfrom sympy import (S, Symbol, Sum, oo, Float, Rational, summation, pi, cos, zeta, exp, log, factorial, sqrt, E, sympify, binomial, harmonic, Catalan, EulerGamma, Function, Integral, Product, product, nan, diff, Derivative) from sympy.concrete.summations import telescopic from sympy.utilities.pytest import XFAIL, raises a, b, c, d, m, k, x, y = map(Symbol, 'abcdmkxy') n = Symbol('n', integer=True) def test_arithmetic_sums(): assert summation(1, (n, a, b)) == b-a+1 assert summation(1, (n, 1, 10)) == 10 assert summation(2*n, (n, 0, 10**10)) == 100000000010000000000 assert summation(4*n*m, (n, a, 1), (m, 1, d)).expand() == \ 2*d + 2*d**2 + a*d + a*d**2 - d*a**2 - a**2*d**2 assert summation(cos(n), (n, -2, 1)) == cos(-2)+cos(-1)+cos(0)+cos(1) def test_polynomial_sums(): assert summation(n**2, (n, 3, 8)) == 199 assert summation(n, (n, a, b)) == \ ((a+b)*(b-a+1)/2).expand() assert summation(n**2, (n, 1, b)) == \ ((2*b**3+3*b**2+b)/6).expand() assert summation(n**3, (n, 1, b)) == \ ((b**4+2*b**3+b**2)/4).expand() assert summation(n**6, (n, 1, b)) == \ ((6*b**7+21*b**6+21*b**5-7*b**3+b)/42).expand() def test_geometric_sums(): assert summation(pi**n, (n, 0, b)) == (1-pi**(b+1)) / (1-pi) assert summation(2 * 3**n, (n, 0, b)) == 3**(b+1) - 1 assert summation(Rational(1,2)**n, (n, 1, oo)) == 1 assert summation(2**n, (n, 0, b)) == 2**(b+1) - 1 assert summation(2**n, (n, 1, oo)) == oo assert summation(2**(-n), (n, 1, oo)) == 1 assert summation(3**(-n), (n, 4, oo)) == Rational(1,54) assert summation(2**(-4*n+3), (n, 1, oo)) == Rational(8,15) assert summation(2**(n+1), (n, 1, b)).expand() == 4*(2**b-1) def test_harmonic_sums(): assert summation(1/k, (k, 0, n)) == Sum(1/k, (k, 0, n)) assert summation(1/k, (k, 1, n)) == harmonic(n) assert summation(n/k, (k, 1, n)) == n*harmonic(n) assert summation(1/k, (k, 5, n)) == harmonic(n) - harmonic(4) def test_composite_sums(): f = Rational(1,2)*(7 - 6*n + Rational(1,7)*n**3) s = summation(f, (n, a, b)) assert not isinstance(s, Sum) A = 0 for i in range(-3, 5): A += f.subs(n, i) B = s.subs(a,-3).subs(b,4) assert A == B def test_hypergeometric_sums(): assert summation(binomial(2*k, k)/4**k, (k, 0, n)) == (1 + 2*n)*binomial(2*n, n)/4**n fac = factorial def NS(e, n=15, **options): return str(sympify(e).evalf(n, **options)) def test_evalf_fast_series(): # Euler transformed series for sqrt(1+x) assert NS(Sum(fac(2*n+1)/fac(n)**2/2**(3*n+1), (n, 0, oo)), 100) == NS(sqrt(2), 100) # Some series for exp(1) estr = NS(E, 100) assert NS(Sum(1/fac(n), (n, 0, oo)), 100) == estr assert NS(1/Sum((1-2*n)/fac(2*n), (n, 0, oo)), 100) == estr assert NS(Sum((2*n+1)/fac(2*n), (n, 0, oo)), 100) == estr assert NS(Sum((4*n+3)/2**(2*n+1)/fac(2*n+1), (n, 0, oo))**2, 100) == estr pistr = NS(pi, 100) # Ramanujan series for pi assert NS(9801/sqrt(8)/Sum(fac(4*n)*(1103+26390*n)/fac(n)**4/396**(4*n), (n, 0, oo)), 100) == pistr assert NS(1/Sum(binomial(2*n,n)**3 * (42*n+5)/2**(12*n+4), (n, 0, oo)), 100) == pistr # Machin's formula for pi assert NS(16*Sum((-1)**n/(2*n+1)/5**(2*n+1), (n, 0, oo)) - \ 4*Sum((-1)**n/(2*n+1)/239**(2*n+1), (n, 0, oo)), 100) == pistr # Apery's constant astr = NS(zeta(3), 100) P = 126392*n**5 + 412708*n**4 + 531578*n**3 + 336367*n**2 + 104000*n + 12463 assert NS(Sum((-1)**n * P / 24 * (fac(2*n+1)*fac(2*n)*fac(n))**3 / fac(3*n+2) / fac(4*n+3)**3, (n, 0, oo)), 100) == astr assert NS(Sum((-1)**n * (205*n**2 + 250*n + 77)/64 * fac(n)**10 / fac(2*n+1)**5, (n, 0, oo)), 100) == astr def test_evalf_fast_series_issue998(): # Catalan's constant assert NS(Sum((-1)**(n-1)*2**(8*n)*(40*n**2-24*n+3)*fac(2*n)**3*\ fac(n)**2/n**3/(2*n-1)/fac(4*n)**2, (n, 1, oo))/64, 100) == \ NS(Catalan, 100) astr = NS(zeta(3), 100) assert NS(5*Sum((-1)**(n-1)*fac(n)**2 / n**3 / fac(2*n), (n, 1, oo))/2, 100) == astr assert NS(Sum((-1)**(n-1)*(56*n**2-32*n+5) / (2*n-1)**2 * fac(n-1)**3 / fac(3*n), (n, 1, oo))/4, 100) == astr def test_evalf_slow_series(): assert NS(Sum((-1)**n / n, (n, 1, oo)), 15) == NS(-log(2), 15) assert NS(Sum((-1)**n / n, (n, 1, oo)), 50) == NS(-log(2), 50) assert NS(Sum(1/n**2, (n, 1, oo)), 15) == NS(pi**2/6, 15) assert NS(Sum(1/n**2, (n, 1, oo)), 100) == NS(pi**2/6, 100) assert NS(Sum(1/n**2, (n, 1, oo)), 500) == NS(pi**2/6, 500) assert NS(Sum((-1)**n / (2*n+1)**3, (n, 0, oo)), 15) == NS(pi**3/32, 15) assert NS(Sum((-1)**n / (2*n+1)**3, (n, 0, oo)), 50) == NS(pi**3/32, 50) def test_euler_maclaurin(): # Exact polynomial sums with E-M def check_exact(f, a, b, m, n): A = Sum(f, (k, a, b)) s, e = A.euler_maclaurin(m, n) assert (e == 0) and (s.expand() == A.doit()) check_exact(k**4, a, b, 0, 2) check_exact(k**4 + 2*k, a, b, 1, 2) check_exact(k**4 + k**2, a, b, 1, 5) check_exact(k**5, 2, 6, 1, 2) check_exact(k**5, 2, 6, 1, 3) # Not exact assert Sum(k**6, (k, a, b)).euler_maclaurin(0, 2)[1] != 0 # Numerical test for m, n in [(2, 4), (2, 20), (10, 20), (18, 20)]: A = Sum(1/k**3, (k, 1, oo)) s, e = A.euler_maclaurin(m, n) assert abs((s-zeta(3)).evalf()) < e.evalf() def test_evalf_euler_maclaurin(): assert NS(Sum(1/k**k, (k, 1, oo)), 15) == '1.29128599706266' assert NS(Sum(1/k**k, (k, 1, oo)), 50) == '1.2912859970626635404072825905956005414986193682745' assert NS(Sum(1/k-log(1+1/k), (k, 1, oo)), 15) == NS(EulerGamma, 15) assert NS(Sum(1/k-log(1+1/k), (k, 1, oo)), 50) == NS(EulerGamma, 50) assert NS(Sum(log(k)/k**2, (k, 1, oo)), 15) == '0.937548254315844' assert NS(Sum(log(k)/k**2, (k, 1, oo)), 50) == '0.93754825431584375370257409456786497789786028861483' assert NS(Sum(1/k, (k, 1000000, 2000000)), 15) == '0.693147930560008' assert NS(Sum(1/k, (k, 1000000, 2000000)), 50) == '0.69314793056000780941723211364567656807940638436025' @XFAIL def test_simple_products(): assert Product(2, (n, a, b)) == 2**(b-a+1) assert Product(n, (n, 1, b)) == factorial(b) assert Product(n**3, (n, 1, b)) == factorial(b)**3 assert Product(3**(2+n), (n, a, b)) \ == 3**(2*(1-a+b)+b/2+(b**2)/2+a/2-(a**2)/2) assert Product(cos(n), (n, 3, 5)) == cos(3)*cos(4)*cos(5) # If Product managed to evaluate this one, it most likely got it wrong! assert isinstance(Product(n**n, (n, 1, b)), Product) @XFAIL def test_rational_products(): assert Product(1+1/n, (n, a, b)) == (1+b)/a assert Product(n+1, (n, a, b)) == factorial(1+b)/factorial(a) assert Product((n+1)/(n-1), (n, a, b)) == b*(1+b)/(a*(a-1)) assert Product(n/(n+1)/(n+2), (n, a, b)) \ == a*factorial(a+1)/(b+1)/factorial(b+2) assert Product(n*(n+1)/(n-1)/(n-2), (n, a, b)) \ == b**2*(b-1)*(1+b)/(a-1)**2/(a*(a-2)) @XFAIL def test_wallis_product(): # Wallis product, given in two different forms to ensure that Product # can factor simple rational expressions A = Product(4*n**2 / (4*n**2-1), (n, 1, b)) B = Product((2*n)*(2*n)/(2*n-1)/(2*n+1), (n, 1, b)) half = Rational(1,2) R = pi/2 * factorial(b)**2 / factorial(b-half) / factorial(b+half) assert A == R assert B == R # This one should eventually also be doable (Euler's product formula for sin) # assert Product(1+x/n**2, (n, 1, b)) == ... def test_telescopic_sums(): #checks also input 2 of comment 1 issue 1028 assert Sum(1/k - 1/(k+1),(k,1,n)).doit() == 1 - 1/(1 + n) f = Function("f") assert Sum(f(k)-f(k+2),(k,m,n)).doit() == -f(1+n) - f(2+n) + f(m) + f(1+m) assert Sum(cos(k)-cos(k+3),(k,1,n)).doit() == -cos(1 + n) - cos(2 + n) - \ cos(3 + n) + cos(1) + cos(2) + cos(3) # dummy variable shouldn't matter assert telescopic(1/m, -m/(1+m),(m, n-1, n)) == \ telescopic(1/k, -k/(1+k),(k, n-1, n)) assert Sum(1/x/(x - 1), (x, a, b)).doit() == 1/(-1 + a) - 1/b def test_sum_reconstruct(): s = Sum(n**2, (n, -1, 1)) assert s == Sum(*s.args) raises(ValueError, 'Sum(x, x)') raises(ValueError, 'Sum(x, (x, 1))') def test_Sum_limit_subs(): assert Sum(a*exp(a), (a, -2, 2)) == Sum(a*exp(a), (a, -b, b)).subs(b, 2) assert Sum(a, (a, Sum(b, (b, 1, 2)), 4)).subs(Sum(b, (b, 1, 2)), c) == \ Sum(a, (a, c, 4)) @XFAIL def test_issue2166(): assert Sum(x, (x, 1, x)).subs(x, a) == Sum(x, (x, 1, a)) def test_Sum_doit(): assert Sum(n*Integral(a**2), (n, 0, 2)).doit() == a**3 assert Sum(n*Integral(a**2), (n, 0, 2)).doit(deep = False) == \ 3*Integral(a**2) assert summation(n*Integral(a**2), (n, 0, 2)) == 3*Integral(a**2) def test_Product_doit(): assert Product(n*Integral(a**2), (n, 1, 3)).doit() == 2 * a**9 / 9 assert Product(n*Integral(a**2), (n, 1, 3)).doit(deep = False) == \ 6*Integral(a**2)**3 assert product(n*Integral(a**2), (n, 1, 3)) == 6*Integral(a**2)**3 def test_Sum_interface(): assert isinstance(Sum(0, (n, 0, 2)), Sum) assert Sum(nan, (n, 0, 2)) is nan assert Sum(nan, (n, 0, oo)) is nan assert Sum(0, (n, 0, 2)).doit() == 0 assert isinstance(Sum(0, (n, 0, oo)), Sum) assert Sum(0, (n, 0, oo)).doit() == 0 raises(ValueError, "Sum(1)") raises(ValueError, "summation(1)") def test_eval_diff(): assert Sum(x, (x, 1, 2)).diff(x) == 0 assert Sum(x*y, (x, 1, 2)).diff(x) == 0 assert Sum(x*y, (y, 1, 2)).diff(x) == Sum(y, (y, 1, 2)) e = Sum(x*y, (x, 1, a)) assert e.diff(a) == Derivative(e, a) assert Sum(x*y, (x, 1, 3), (a, 2, 5)).diff(y) == \ Sum(x*y, (x, 1, 3), (a, 2, 5)).doit().diff(y) == \ 24 def test_hypersum(): from sympy import simplify, sin, hyper assert simplify(summation(x**n/fac(n), (n, 1, oo))) == -1 + exp(x) assert summation((-1)**n * x**(2*n) / fac(2*n), (n, 0, oo)) == cos(x) assert simplify(summation((-1)**n*x**(2*n+1)/factorial(2*n+1), (n, 3, oo))) \ == -x + sin(x) + x**3/6 - x**5/120 # TODO to get this without hyper need to improve hyperexpand assert summation(1/(n+2)**3, (n, 1, oo)) == \ hyper([3, 3, 3, 1], [4, 4, 4], 1)/27 s = summation(x**n*n, (n, -oo, 0)) assert s.is_Piecewise assert s.args[0].args[0] == -1/(x*(1-1/x)**2) assert s.args[0].args[1] == (abs(1/x) < 1) def test_issue_1071(): assert summation(1/factorial(k), (k, 0, oo)) == E wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/tests/test_products.py0000644000175000017500000000252212014170666027320 0ustar georgeskgeorgeskfrom sympy import (symbols, product, factorial, rf, sqrt, cos, Function, Product, Rational) a, k, n = symbols('a,k,n', integer=True) def test_simple_products(): assert product(2, (k, a, n)) == 2**(n-a+1) assert product(k, (k, 1, n)) == factorial(n) assert product(k**3, (k, 1, n)) == factorial(n)**3 assert product(k+1, (k, 0, n-1)) == factorial(n) assert product(k+1, (k, a, n-1)) == rf(1+a, n-a) assert product(cos(k), (k, 0, 5)) == cos(1)*cos(2)*cos(3)*cos(4)*cos(5) assert product(cos(k), (k, 3, 5)) == cos(3)*cos(4)*cos(5) assert product(cos(k), (k, 1, Rational(5, 2))) == cos(1)*cos(2) assert isinstance(product(k**k, (k, 1, n)), Product) def test_rational_products(): assert product(1+1/k, (k, 1, n)) == rf(2, n)/factorial(n) def test_special_products(): # Wallis product assert product((4*k)**2 / (4*k**2-1), (k, 1, n)) == \ 4**n*factorial(n)**2/rf(Rational(1, 2), n)/rf(Rational(3, 2), n) # Euler's product formula for sin assert product(1 + a/k**2, (k, 1, n)) == \ rf(1 - sqrt(-a), n)*rf(1 + sqrt(-a), n)/factorial(n)**2 def test__eval_product(): from sympy.abc import i, n # 1710 a = Function('a') assert product(2*a(i), (i, 1, n)) == 2**n * Product(a(i), (i, 1, n)) # 1711 assert product(2**i, (i, 1, n)) == 2**(n/2 + n**2/2) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/tests/test_gosper.py0000644000175000017500000001501012014170666026750 0ustar georgeskgeorgesk"""Tests for Gosper's algorithm for hypergeometric summation. """ from sympy.concrete.gosper import gosper_normal, gosper_term, gosper_sum from sympy import S, Poly, symbols, factorial, binomial, gamma, sqrt, simplify a, b, n, m, k, i, j, r, x = symbols('a,b,n,m,k,i,j,r,x') def test_gosper_normal(): assert gosper_normal(4*n + 5, 2*(4*n + 1)*(2*n + 3), n) == \ (Poly(S(1)/4, n), Poly(n + S(3)/2), Poly(n + S(1)/4)) def test_gosper_term(): assert gosper_term((4*k + 1)*factorial(k)/factorial(2*k + 1), k) == (-k - S(1)/2)/(k + S(1)/4) def test_gosper_sum(): assert gosper_sum(1, (k, 0, n)) == 1 + n assert gosper_sum(k, (k, 0, n)) == n*(1 + n)/2 assert gosper_sum(k**2, (k, 0, n)) == n*(1 + n)*(1 + 2*n)/6 assert gosper_sum(k**3, (k, 0, n)) == n**2*(1 + n)**2/4 assert gosper_sum(2**k, (k, 0, n)) == 2*2**n - 1 assert gosper_sum(factorial(k), (k, 0, n)) is None assert gosper_sum(binomial(n, k), (k, 0, n)) is None assert gosper_sum(factorial(k)/k**2, (k, 0, n)) is None assert gosper_sum((k - 3)*factorial(k), (k, 0, n)) is None assert gosper_sum(k*factorial(k), k) == factorial(k) assert gosper_sum(k*factorial(k), (k, 0, n)) == n*factorial(n) + factorial(n) - 1 assert gosper_sum((-1)**k*binomial(n, k), (k, 0, n)) == 0 assert gosper_sum((-1)**k*binomial(n, k), (k, 0, m)) == -(-1)**m*(m - n)*binomial(n, m)/n assert gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) == \ (2*factorial(2*n + 1) - factorial(n))/factorial(2*n + 1) def test_gosper_sum_indefinite(): assert gosper_sum(k, k) == k*(k - 1)/2 assert gosper_sum(k**2, k) == k*(k - 1)*(2*k - 1)/6 assert gosper_sum(1/(k*(k + 1)), k) == -1/k assert gosper_sum(-(27*k**4 + 158*k**3 + 430*k**2 + 678*k + 445)*gamma(2*k + 4)/(3*(3*k + 7)*gamma(3*k + 6)), k) == \ (3*k + 5)*(k**2 + 2*k + 5)*gamma(2*k + 4)/gamma(3*k + 6) def test_gosper_sum_parametric(): assert gosper_sum(binomial(S(1)/2, m - j + 1)*binomial(S(1)/2, m + j), (j, 1, n)) == \ n*(1 + m - n)*(-1 + 2*m + 2*n)*binomial(S(1)/2, 1 + m - n)*binomial(S(1)/2, m + n)/(m*(1 + 2*m)) def test_gosper_sum_algebraic(): assert gosper_sum(n**2 + sqrt(2), (n, 0, m)) == (m + 1)*(2*m**2 + m + 6*sqrt(2))/6 def test_gosper_sum_iterated(): f1 = binomial(2*k, k)/4**k f2 = (1 + 2*n)*binomial(2*n, n)/4**n f3 = (1 + 2*n)*(3 + 2*n)*binomial(2*n, n)/(3*4**n) f4 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*binomial(2*n, n)/(15*4**n) f5 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*(7 + 2*n)*binomial(2*n, n)/(105*4**n) assert gosper_sum(f1, (k, 0, n)) == f2 assert gosper_sum(f2, (n, 0, n)) == f3 assert gosper_sum(f3, (n, 0, n)) == f4 assert gosper_sum(f4, (n, 0, n)) == f5 def test_gosper_sum_AeqB_part1(): f1a = n**4 f1b = n**3*2**n f1c = 1/(n**2 + sqrt(5)*n - 1) f1d = n**4*4**n/binomial(2*n, n) f1e = factorial(3*n)/(factorial(n)*factorial(n + 1)*factorial(n + 2)*27**n) f1f = binomial(2*n, n)**2/((n + 1)*4**(2*n)) f1g = (4*n - 1)*binomial(2*n, n)**2/((2*n - 1)**2*4**(2*n)) f1h = n*factorial(n - S(1)/2)**2/factorial(n + 1)**2 g1a = m*(m + 1)*(2*m + 1)*(3*m**2 + 3*m - 1)/30 g1b = 26 + 2**(m + 1)*(m**3 - 3*m**2 + 9*m - 13) g1c = (m + 1)*(m*(m**2 - 7*m + 3)*sqrt(5) - (3*m**3 - 7*m**2 + 19*m - 6))/(2*m**3*sqrt(5) + m**4 + m**2 - 1)/6 g1d = -S(2)/231 + 24**m*(m + 1)*(63*m**4 + 112*m**3 + 18*m**2 - 22*m + 3)/(693*binomial(2*m, m)) g1e = -S(9)/2 + (81*m**2 + 261*m + 200)*factorial(3*m + 2)/(40*27**m*factorial(m)*factorial(m + 1)*factorial(m + 2)) g1f = (2*m + 1)**2*binomial(2*m, m)**2/(4**(2*m)*(m + 1)) g1g = -binomial(2*m, m)**2/4**(2*m) g1h = -(2*m + 1)**2*(3*m + 4)*factorial(m - S(1)/2)**2/factorial(m + 1)**2 g = gosper_sum(f1a, (n, 0, m)) assert g is not None and simplify(g - g1a) == 0 g = gosper_sum(f1b, (n, 0, m)) assert g is not None and simplify(g - g1b) == 0 g = gosper_sum(f1c, (n, 0, m)) assert g is not None # and simplify(g - g1c) == 0 g = gosper_sum(f1d, (n, 0, m)) assert g is not None # and simplify(g - g1d) == 0 g = gosper_sum(f1e, (n, 0, m)) assert g is not None # and simplify(g - g1e) == 0 g = gosper_sum(f1f, (n, 0, m)) assert g is not None # and simplify(g - g1f) == 0 g = gosper_sum(f1g, (n, 0, m)) assert g is not None # and simplify(g - g1g) == 0 g = gosper_sum(f1h, (n, 0, m)) assert g is not None # and simplify(g - g1h) == 0 def test_gosper_sum_AeqB_part2(): f2a = n**2*a**n f2b = (n - r/2)*binomial(r, n) f2c = factorial(n - 1)**2/(factorial(n - x)*factorial(n + x)) f2d = n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)) g2a = -a*(a + 1)/(a - 1)**3 + a**(m + 1)*(a**2*m**2 - 2*a*m*22 + m**2 - 2*a*m + 2*m + a + 1)/(a - 1)**3 g2b = -((-m + r)*binomial(r, m)) g2c = 1/(factorial(1 - x)*factorial(1 + x)) - 1/(x**2*factorial(1 - x)*factorial(1 + x)) + factorial(m)**2/(x**2*factorial(1 - x)*factorial(1 + x)) g2d = 1/(factorial(a - 1)*factorial(b - 1)) - a**(m + 1)*b**(m + 1)/(factorial(a + m)*factorial(b + m)) g = gosper_sum(f2a, (n, 0, m)) assert g is not None # and simplify(g - g2a) == 0 g = gosper_sum(f2b, (n, 0, m)) assert g is not None # and simplify(g - g2b) == 0 g = gosper_sum(f2c, (n, 1, m)) assert g is not None # and simplify(g - g2c) == 0 g = gosper_sum(f2d, (n, 0, m)) assert g is not None # and simplify(g - g2d) == 0 def test_gosper_sum_AeqB_part3(): f3a = 1/n**4 f3b = (6*n + 3)/(4*n**4 + 8*n**3 + 8*n**2 + 4*n + 3) f3c = 2**n*(n**2 - 2*n - 1)/(n**2*(n + 1)**2) f3d = n**2*4**n/((n + 1)*(n + 2)) f3e = 2**n/(n + 1) f3f = 4*(n - 1)*(n**2 - 2*n - 1)/(n**2*(n + 1)**2*(n - 2)**2*(n - 3)**2) f3g = (n**4 - 14*n**2 - 24*n - 9)*2**n/(n**2*(n + 1)**2*(n + 2)**2*(n + 3)**2) # g3a -> no closed form g3b = m*(m + 2)/(2*m**2 + 4*m + 3) g3c = 2**m/m**2 - 2 g3d = S(2)/3 + 4**(m + 1)*(m - 1)/(m + 2)/3 # g3e -> no closed form g3f = -S(1)/16 + 1/((m - 2)**2*(m + 1)**2) g3g = -S(2)/9 + 2**(m + 1)/((m + 1)**2*(m + 3)**2) g = gosper_sum(f3a, (n, 1, m)) assert g is None g = gosper_sum(f3b, (n, 1, m)) assert g is not None # and simplify(g - g3b) == 0 g = gosper_sum(f3c, (n, 1, m-1)) assert g is not None # and simplify(g - g3c) == 0 g = gosper_sum(f3d, (n, 1, m)) assert g is not None # and simplify(g - g3d) == 0 g = gosper_sum(f3e, (n, 0, m-1)) assert g is None g = gosper_sum(f3f, (n, 4, m)) assert g is not None # and simplify(g - g3f) == 0 g = gosper_sum(f3g, (n, 1, m)) assert g is not None # and simplify(g - g3g) == 0 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/concrete/__init__.py0000644000175000017500000000011412014170666025006 0ustar georgeskgeorgeskfrom products import product, Product from summations import summation, Sum wxgeometrie-0.133.2.orig/wxgeometrie/sympy/galgebra/0000755000175000017500000000000012014170666022643 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/galgebra/GA.py0000644000175000017500000025120112014170666023505 0ustar georgeskgeorgesk#!/usr/bin/python """ The module symbolicGA implements symbolic Geometric Algebra in python. The relevant references for this module are: 1. "Geometric Algebra for Physicists" by C. Doran and A. Lazenby, Cambridge University Press, 2003. 2. "Geometric Algebra for Computer Science" by Leo Dorst, Daniel Fontijne, and Stephen Mann, Morgan Kaufmann Publishers, 2007 3. Sympy Tutorial, http://docs.sympy.org/ """ import sys import types import numpy, sympy import re as regrep import sympy.galgebra.latex_ex NUMPAT = regrep.compile( '([\-0-9])|([\-0-9]/[0-9])') """Re pattern for rational number""" ZERO = sympy.Rational(0) ONE = sympy.Rational(1) TWO = sympy.Rational(2) HALF = sympy.Rational(1,2) from sympy.core import Symbol as sym_type from sympy.core import Pow as pow_type from sympy import Abs as abs_type from sympy.core import Mul as mul_type from sympy.core import Add as add_type global MAIN_PROGRAM MAIN_PROGRAM = '' @sympy.vectorize(0) def substitute_array(array,*args): return(array.subs(*args)) def is_quasi_unit_numpy_array(array): """ Determine if a array is square and diagonal with entries of +1 or -1. """ shape = numpy.shape(array) if len(shape) == 2 and (shape[0] == shape[1]): n = shape[0] ix = 0 while ix < n: iy = 0 while iy < ix: if array[ix][iy] != ZERO: return(False) iy += 1 if sympy.Abs(array[ix][ix]) != ONE: return(False) ix += 1 return(True) else: return(False) def set_main(main_program): global MAIN_PROGRAM MAIN_PROGRAM = main_program return def plist(lst): if type(lst) == types.ListType: for x in lst: plist(x) else: sys.stderr.write(lst+'\n') return def numeric(num_str): """ Returns rational numbers compatible with symbols. Input is a string representing a fraction or integer or a simple integer. """ if type(num_str) == types.IntType: a = num_str b = 1 else: tmp = num_str.split('/') if len(tmp) == 1: a = int(tmp[0]) b = 1 else: a = int(tmp[0]) b = int(tmp[1]) return(sympy.Rational(a,b)) def collect(expr,lst): """ Wrapper for sympy.collect. """ lst = MV.scalar_to_symbol(lst) return(sympy.collect(expr,lst)) def sqrt(expr): return(sympy.sqrt(expr)) def isint(a): """ Test for integer. """ return(type(a) == types.IntType) def make_null_array(n): """ Return list of n empty lists. """ a = [] for i in range(n): a.append([]) return(a) def test_int_flgs(lst): """ Test if all elements in list are 0. """ for i in lst: if i: return(1) return(0) def comb(N,P): """ Calculates the combinations of the integers [0,N-1] taken P at a time. The output is a list of lists of integers where the inner lists are the different combinations. Each combination is sorted in ascending order. """ def rloop(n,p,combs,comb): if p: for i in range(n): newcomb = comb+[i] np = p-1 rloop(i,np,combs,newcomb) else: combs.append(comb) combs = [] rloop(N,P,combs,[]) for comb in combs: comb.sort() return(combs) def diagpq(p,q=0): """ Return string equivalent metric tensor for signature (p,q). """ n = p+q D = '' rn = range(n) for i in rn: for j in rn: if i ==j: if i < p: D += '1 ' else: D += '-1 ' else: D += '0 ' D = D[:-1]+',' return(D) def make_scalars(symnamelst): """ make_symbols takes a string of symbol names separated by blanks and converts them to MV scalars separately accessible by the main program and addition returns a list of the symbols. """ global MAIN_PROGRAM if type(symnamelst) == types.StringType: symnamelst = symnamelst.split() scalar_lst = [] isym = 0 for name in symnamelst: tmp = sympy.Symbol(name) tmp = MV(tmp,'scalar') scalar_lst.append(tmp) setattr(MAIN_PROGRAM,name,tmp) isym += 1 return(scalar_lst) def make_symbols(symnamelst): """ make_symbols takes a string of symbol names separated by blanks and converts them to MV scalars separately accessible by the main program and addition returns a list of the symbols. """ global MAIN_PROGRAM if type(symnamelst) == types.StringType: symnamelst = symnamelst.split() sym_lst = [] isym = 0 for name in symnamelst: tmp = sympy.Symbol(name) sym_lst.append(tmp) setattr(MAIN_PROGRAM,name,tmp) isym += 1 return(sym_lst) def israt(numstr): """ Test if string represents a rational number. """ global NUMPAT if NUMPAT.match(numstr): return(1) return(0) def dualsort(lst1, lst2): """ Inplace dual sort of lst1 and lst2 keyed on sorted lst1. """ _indices = range(len(lst1)) _indices.sort(key=lst2.__getitem__) lst1[:] = map(lst1.__getitem__, _indices) lst2[:] = map(lst2.__getitem__, _indices) return def cp(A,B): """ Calculates the commutator product (A*B-B*A)/2 for the objects A and B. """ return(HALF*(A*B-B*A)) def reduce_base(k,base): """ If base is a list of sorted integers [i_1,...,i_R] then reduce_base sorts the list [k,i_1,...,i_R] and calculates whether an odd or even number of permutations is required to sort the list. The sorted list is returned and +1 for even permutations or -1 for odd permutations. """ if k in base: return(0,base) grade = len(base) if grade == 1: if k < base[0]: return(1,[k,base[0]]) else: return(-1,[base[0],k]) ilo = 0 ihi = grade-1 if k < base[0]: return(1,[k]+base) if k > base[ihi]: if grade%2 == 0: return(1,base+[k]) else: return(-1,base+[k]) imid = ihi+ilo if grade == 2: return(-1,[base[0],k,base[1]]) while True: if ihi-ilo == 1: break if base[imid] > k: ihi = imid else: ilo = imid imid = (ilo+ihi)/2 if ilo%2 == 1: return(1,base[:ihi]+[k]+base[ihi:]) else: return(-1,base[:ihi]+[k]+base[ihi:]) def sub_base(k,base): """ If base is a list of sorted integers [i_1,...,i_R] then sub_base returns a list with the k^th element removed. Note that k=0 removes the first element. There is no test to see if k is in the range of the list. """ n = len(base) if n == 1: return([]) if n == 2: if k == base[0]: return([base[1]]) else: return([base[0]]) return(base[:k]+base[k+1:]) def magnitude(vector): """ Calculate magnitude of vector containing trig expressions and simplify. This is a hack because of way he sign of magsq is determined and because of the way that absolute values are removed. """ magsq = sympy.expand((vector|vector)()) magsq = sympy.trigsimp(magsq,deep=True,recursive=True) #print magsq magsq_str = sympy.galgebra.latex_ex.LatexPrinter()._print(magsq) if magsq_str[0] == '-': magsq = -magsq mag = unabs(sqrt(magsq)) #print mag return(mag) def LaTeX_lst(lst,title=''): """ Output a list in LaTeX format. """ if title != '': LaTeX(title) for x in lst: LaTeX(x) return def unabs(x): """ Remove absolute values from expressions so a = sqrt(a**2). This is a hack. """ if type(x) == mul_type: y = unabs(x.args[0]) for yi in x.args[1:]: y *= unabs(yi) return(y) if type(x) == pow_type: if x.args[1] == HALF and type(x.args[0]) == add_type: return(x) y = 1/unabs(x.args[0]) return(y) if len(x.args) == 0: return(x) if type(x) == abs_type: return(x.args[0]) return(x) def function_lst(fstr,xtuple): sys.stderr.write(fstr+'\n') fct_lst = [] for xstr in fstr.split(): f = sympy.Function(xstr)(*xtuple) fct_lst.append(f) return(fct_lst) def vector_fct(Fstr,x): """ Create a list of functions of arguments x. One function is created for each variable in x. Fstr is a string that is the base name of each function while each function in the list is given the name Fstr+'__'+str(x[ix]) so that if Fstr = 'f' and str(x[1]) = 'theta' then the LaTeX output of the second element in the output list would be 'f^{\theta}'. """ nx = len(x) Fvec = [] for ix in range(nx): ftmp = sympy.Function(Fstr+'__'+sympy.galgebra.latex_ex.LatexPrinter.str_basic(x[ix]))(*tuple(x)) Fvec.append(ftmp) return(Fvec) def print_lst(lst): for x in lst: print x return def normalize(elst,nname_lst): """ Normalize a list of vectors and rename the normalized vectors. 'elist' is the list (or array) of vectors to be normalized and nname_lst is a list of the names for the normalized vectors. The function returns the numpy arrays enlst and mags containing the normalized vectors (enlst) and the magnitudes of the original vectors (mags). """ i = 0 mags = numpy.array(MV.n*[ZERO],dtype=numpy.object) enlst= numpy.array(MV.n*[ZERO],dtype=numpy.object) for (e,nname) in zip(elst,nname_lst): emag = magnitude(e) emaginv = 1/emag mags[i] = emag enorm = emaginv*e enorm.name = nname enlst[i] = enorm i += 1 return(enlst,mags) def build_base(base_index,base_vectors,reverse=False): base = base_vectors[base_index[0]] if len(base_index) > 1: for i in base_index[1:]: base = base^base_vectors[i] if reverse: base = base.rev() return(base) class MV(object): is_setup = False basislabel_lst = 0 curvilinear_flg = False coords = None @staticmethod def pad_zeros(value,n): """ Pad list with zeros to length n. If length is > n truncate list. Return padded list. """ nvalue = len(value) if nvalue < n: value = value+(n-nvalue)*[ZERO] if nvalue > n: value = value[:n] return(value) @staticmethod def define_basis(basis): """ Calculates all the MV static variables needed for basis operations. See reference 5 section 2. """ MV.vbasis = basis MV.vsyms = make_symbols(MV.vbasis) MV.n = len(MV.vbasis) MV.nrg = range(MV.n) MV.n1 = MV.n+1 MV.n1rg = range(MV.n1) MV.npow = 2**MV.n MV.index = range(MV.n) MV.gabasis = [[]] MV.basis = (MV.n+1)*[0] MV.basislabel = (MV.n+1)*[0] MV.basis[0] = [] MV.basislabel[0] = '1' MV.basislabel_lst = [['1']] MV.nbasis = numpy.array((MV.n+1)*[1],dtype=numpy.object) for igrade in range(1,MV.n+1): tmp = comb(MV.n,igrade) MV.gabasis += [tmp] ntmp = len(tmp) MV.nbasis[igrade] = ntmp MV.basis[igrade] = tmp gradelabels = [] gradelabel_lst = [] for i in range(ntmp): tmp_lst = [] bstr = '' for j in tmp[i]: bstr += MV.vbasis[j] tmp_lst.append(MV.vbasis[j]) gradelabel_lst.append(tmp_lst) gradelabels.append(bstr) MV.basislabel_lst.append(gradelabel_lst) MV.basislabel[igrade] = gradelabels MV.basis_map = [{'':0}] igrade = 1 while igrade <= MV.n: tmpdict = {} bases = MV.gabasis[igrade] nbases = len(bases) ibases = 0 for base in bases: tmpdict[str(base)] = ibases ibases += 1 MV.basis_map.append(tmpdict) igrade += 1 if MV.debug: print 'basis strings =',MV.vbasis print 'basis symbols =',MV.vsyms print 'basis labels =',MV.basislabel print 'basis =',MV.basis print 'grades =',MV.nbasis print 'index =',MV.index return @staticmethod def define_metric(metric): """ Calculates all the MV static variables needed for metric operations. See reference 5 section 2. """ if MV.metric_str: name_flg = False MV.g = [] MV.metric = numpy.array(MV.n*[MV.n*[ZERO]],dtype=numpy.object) if metric == '': metric = numpy.array(MV.n*[MV.n*['#']],dtype=numpy.object) for i in MV.index: for j in MV.index: gij = metric[i][j] if israt(gij): MV.metric[i][j] = numeric(gij) else: if gij == '#': name_flg = True if i == j: gij = '('+MV.vbasis[j]+'**2)' name = MV.vbasis[j]+'sq' else: gij = '('+MV.vbasis[min(i,j)]+'.'+MV.vbasis[max(i,j)]+')' name = MV.vbasis[min(i,j)]+'dot'+MV.vbasis[max(i,j)] tmp = sympy.Symbol(gij) MV.metric[i][j] = tmp if i <= j: MV.g.append(tmp) if name_flg: setattr(MAIN_PROGRAM,name,tmp) name_flg = False else: MV.metric = metric MV.g = [] for row in metric: g_row = [] for col in metric: g_row.append(col) MV.g.append(g_row) if MV.debug: print 'metric =',MV.metric return @staticmethod def define_reciprocal_frame(): """ Calculates unscaled reciprocal vectors (MV.brecp) and scale factor (MV.Esq). The ith scaled reciprocal vector is (1/MV.Esq)*MV.brecp[i]. The pseudoscalar for the set of basis vectors is MV.E. """ if MV.tables_flg: MV.E = MV.bvec[0] MV.brecp = [] for i in range(1,MV.n): MV.E = MV.E^MV.bvec[i] for i in range(MV.n): tmp = ONE if i%2 != 0: tmp = -ONE for j in range(MV.n): if i != j: tmp = tmp^MV.bvec[j] tmp = tmp*MV.E MV.brecp.append(tmp) MV.Esq = (MV.E*MV.E)() MV.Esq_inv = ONE/MV.Esq for i in range(MV.n): MV.brecp[i] = MV.brecp[i]*MV.Esq_inv return @staticmethod def reduce_basis_loop(blst): """ Makes one pass through basis product representation for reduction of representation to normal form. See reference 5 section 3. """ nblst = len(blst) if nblst <= 1: return(1) jstep = 1 while jstep 2: blst = blst[:istep]+blst[jstep+1:] else: blst = [] if len(blst) <= 1 or jstep == nblst-1: blst_flg = 0 else: blst_flg = 1 return(MV.metric[i][i],blst,blst_flg) if blst[istep] > blst[jstep]: blst1 = blst[:istep]+blst[jstep+1:] a1 = TWO*MV.metric[blst[jstep]][blst[istep]] blst = blst[:istep]+[blst[jstep]]+[blst[istep]]+blst[jstep+1:] if len(blst1) <= 1: blst1_flg = 0 else: blst1_flg = 1 return(a1,blst1,blst1_flg,blst) jstep +=1 return(1) @staticmethod def reduce_basis(blst): """ Repetitively applies reduce_basis_loop to basis product representation until normal form is realized. See reference 5 section 3. """ if blst == []: blst_coef = [] blst_expand = [] for i in MV.n1rg: blst_coef.append([]) blst_expand.append([]) blst_expand[0].append([]) blst_coef[0].append(ONE) return(blst_coef,blst_expand) blst_expand = [blst] blst_coef = [ONE] blst_flg = [1] while test_int_flgs(blst_flg): for i in range(len(blst_flg)): if blst_flg[i]: tmp = MV.reduce_basis_loop(blst_expand[i]) if tmp ==1: blst_flg[i] = 0 else: if len(tmp) == 3: blst_coef[i] = tmp[0]*blst_coef[i] blst_expand[i] = tmp[1] blst_flg[i] = tmp[2] else: blst_coef[i] = -blst_coef[i] blst_flg[i] = 1 blst_expand[i] = tmp[3] blst_coef.append(-blst_coef[i]*tmp[0]) blst_expand.append(tmp[1]) blst_flg.append(tmp[2]) (blst_coef,blst_expand) = MV.combine_common_factors(blst_coef,blst_expand) return(blst_coef,blst_expand) @staticmethod def combine_common_factors(blst_coef,blst_expand): new_blst_coef = [] new_blst_expand = [] for i in range(MV.n1): new_blst_coef.append([]) new_blst_expand.append([]) nfac = len(blst_coef) for ifac in range(nfac): blen = len(blst_expand[ifac]) new_blst_coef[blen].append(blst_coef[ifac]) new_blst_expand[blen].append(blst_expand[ifac]) for i in range(MV.n1): if len(new_blst_coef[i]) > 1: MV.contract(new_blst_coef[i],new_blst_expand[i]) return(new_blst_coef,new_blst_expand) @staticmethod def contract(coefs,bases): dualsort(coefs,bases) n = len(bases)-1 i = 0 while i < n: j = i+1 if bases[i] == bases[j]: coefs[i] += coefs[j] bases.pop(j) coefs.pop(j) n -= 1 else: i += 1 n = len(coefs) i = 0 while i < n: if coefs[i] == ZERO: coefs.pop(i) bases.pop(i) n -= 1 else: i +=1 return @staticmethod def convert(coefs,bases): mv = MV() mv.bladeflg = 0 for igrade in MV.n1rg: coef = coefs[igrade] base = bases[igrade] if len(coef) > 0: nbases = MV.nbasis[igrade] mv.mv[igrade] = numpy.array(nbases*[ZERO],dtype=numpy.object) nbaserg = range(len(base)) for ibase in nbaserg: if igrade > 0: k = MV.basis[igrade].index(base[ibase]) mv.mv[igrade][k] = coef[ibase] else: mv.mv[igrade] = numpy.array([coef[0]],dtype=numpy.object) return(mv) @staticmethod def set_str_format(str_mode=0): MV.str_mode = str_mode return @staticmethod def str_rep(mv): """ Converts internal representation of a multivector to a string for outputting. If lst_mode = 1, str_rep outputs a list of strings where each string contains one multivector coefficient concatenated with the corresponding base or blade symbol. MV.str_mode Effect 0 Print entire multivector on one line (default) 1 Print each grade on a single line 2 Print each base on a single line """ if MV.bladeprint: mv.convert_to_blades() labels = MV.bladelabel else: if not mv.bladeflg: labels = MV.basislabel else: labels = MV.bladelabel mv.compact() if isinstance(mv.mv[0],types.IntType): value = '' else: value = (mv.mv[0][0]).__str__() value = value.replace(' ','') dummy = sympy.Dummy('dummy') for igrade in MV.n1rg[1:]: if isinstance(mv.mv[igrade],numpy.ndarray): j = 0 for x in mv.mv[igrade]: if x != ZERO: xstr = (x*dummy).__str__() xstr = xstr.replace(' ','') if xstr[0] != '-' and len(value) > 0: xstr = '+'+xstr if xstr.find('_dummy') < 2 and xstr[-5:] != '_dummy': xstr = xstr.replace('_dummy*','')+'*'+labels[igrade][j] else: xstr = xstr.replace('_dummy',labels[igrade][j]) if MV.str_mode == 2: xstr += '\n' value += xstr j += 1 if MV.str_mode == 1: value += '\n' if value == '': value = '0' #value = value.replace(' ','') value = value.replace('dummy','1') return(value) @staticmethod def xstr_rep(mv,lst_mode=0): """ Converts internal representation of a multivector to a string for outputing. If lst_mode = 1, str_rep outputs a list of strings where each string contains one multivector coefficient concatenated with the corresponding base or blade symbol. """ if lst_mode: outlst = [] if MV.bladeprint: mv.convert_to_blades() labels = MV.bladelabel else: if not mv.bladeflg: labels = MV.basislabel else: labels = MV.bladelabel value = '' for igrade in MV.n1rg: tmp = [] if isinstance(mv.mv[igrade],numpy.ndarray): j = 0 xsum = 0 for x in mv.mv[igrade]: if x != ZERO: xstr = x.__str__() if xstr == '+1' or xstr == '1' or xstr == '-1': if xstr == '+1' or xstr == '1': xstr = '+' else: xstr = '-' else: if xstr[0] != '+': xstr = '+('+xstr+')' else: xstr = '+('+xstr[1:]+')' value += xstr+labels[igrade][j] if MV.str_mode and not lst_mode: value += value+'\n' if lst_mode: tmp.append(value) j += 1 if lst_mode: if len(tmp) > 0: outlst.append(tmp) value = '' if not lst_mode: if len(value) > 1 and value[0] == '+': value = value[1:] if len(value) == 0: value = '0' else: value = outlst return(value) @staticmethod def setup(basis,metric='',rframe=False,coords=None,debug=False,offset=0): """ MV.setup initializes the MV class by calculating the static multivector tables required for geometric algebra operations on multivectors. See reference 5 section 2 for details on basis and metric arguments. """ global MAIN_PROGRAM MV.is_setup = True MV.metric_str = False MV.debug = debug MV.bladeprint = 0 MV.tables_flg = 0 MV.str_mode = 0 MV.basisroot = '' MV.index_offset = offset if coords == None: MV.coords = None else: MV.coords = tuple(coords) rframe= True if type(basis) == types.StringType: basislst = basis.split() if len(basislst) == 1: MV.basisroot = basislst[0] basislst = [] for coord in coords: basislst.append(MV.basisroot+'_'+str(coord)) MV.define_basis(basislst) if type(metric) == types.StringType: MV.metric_str = True if len(metric) > 0: if metric[0] == '[' and metric[-1] == ']': tmps = metric[1:-1].split(',') N = len(tmps) metric = [] itmp = 0 for tmp in tmps: xtmp = N*['0'] xtmp[itmp] = tmp itmp += 1 metric.append(xtmp) else: tmps = metric.split(',') metric = [] for tmp in tmps: xlst = tmp.split() xtmp = [] for x in xlst: xtmp.append(x) metric.append(xtmp) MV.define_metric(metric) MV.multiplication_table() MV.blade_table() MV.inverse_blade_table() MV.tables_flg = 1 isym = 0 MV.bvec = [] for name in MV.vbasis: bvar = MV(value=isym,mvtype='basisvector',mvname=name) bvar.bladeflg = 1 MV.bvec.append(bvar) setattr(MAIN_PROGRAM,name,bvar) isym += 1 if rframe: MV.define_reciprocal_frame() MV.I = MV(ONE,'pseudo','I') MV.ZERO = MV() Isq = (MV.I*MV.I)() MV.Iinv = (1/Isq)*MV.I return('Setup of '+basis+' complete!') @staticmethod def set_coords(coords): MV.coords = coords return @staticmethod def scalar_fct(fct_name): """ Create multivector scalar function with name fct_name (string) and independent variables coords (list of variable). Default variables are those associated with each dimension of vector space. """ phi = sympy.Function(fct_name)(*MV.coords) Phi = MV(phi,'scalar') Phi.name = fct_name return(Phi) @staticmethod def vector_fct(fct_name,vars=''): """ Create multivector vector function with name fct_name (string) and independent variables coords (list of variable). Default variables are those associated with each dimension of vector space. """ if isinstance(vars,types.StringType): Acoefs = vector_fct(fct_name,MV.coords) else: Acoefs =numpy.array(MV.n*[ZERO],dtype=numpy.object) x = MV.coords if isinstance(vars,sympy.core.symbol.Symbol): for icoef in MV.nrg: Acoefs[icoef] = sympy.Function(fct_name+'__'+\ sympy.galgebra.latex_ex.LatexPrinter.str_basic(x[icoef]))(vars) else: for icoef in MV.nrg: Acoefs[icoef] = sympy.Function(fct_name+'__'+\ sympy.galgebra.latex_ex.LatexPrinter.str_basic(x[icoef]))(*tuple(vars)) A = MV(Acoefs,'vector',fct_name) return(A) @staticmethod def rebase(x,coords,base_name='',debug=False,debug_level=0): """ Define curvilinear coordinates for previously defined vector (multivector) space (MV.setup has been run) with position vector, x, that is a vector function of the independent coordinates, coords (list of sympy variables equal in length to dimension of vector space), and calculate: 1. Frame (basis) vectors 2. Normalized frame (basis) vectors. 3. Metric tensor 4. Reciprocal frame vectors 5. Reciprocal metric tensor 6. Connection multivectors The basis vectors are named with the base_name (string) and a subscript derived from the name of each coordinate. So that if the base name is 'e' and the coordinated are [r,theta,z] the variable names of the frame vectors would be e_r, e_theta, and e_z. For LaTeX output the names of the frame vectors would be e_{r}, e_{\theta}, and e_{z}. Everything needed to compute the geometric, outer, and inner derivatives of multivector functions in curvilinear coordinates is calculated. If debug is True all the quantities in the above list are output in LaTeX format. Currently rebase works with cylindrical and spherical coordinates in any dimension. The limitation is the ability to automatically simplify complex sympy expressions generated while calculating the quantities in the above list. This is why the debug option is included. The debug_level can equal 0,1,2, or 3 and determines how far in the list to calculate (input 0 to do the entire list) while debugging. """ global MAIN_PROGRAM #Form root names for basis, reciprocal basis, normalized basis, and normalized reciprocal basis if base_name == '': base_name = MV.basisroot+'prm' LaTeX_base = sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(base_name) bm = '\\bm{'+LaTeX_base+'}' bmhat = '\\hat{'+bm+'}' bstr = bmhat+'_{[i_{1},\dots, i_{R}]}' base_name += 'bm' base_name_hat = base_name+'hat' base_name_lst = [] nbase_name_lst = [] rbase_name_lst = [] rnbase_name_lst = [] coords_lst = [] for coord in coords: coord_str = sympy.galgebra.latex_ex.LatexPrinter.str_basic(coord) coords_lst.append(coord_str) base_name_lst.append(base_name+'_'+coord_str) rbase_name_lst.append(base_name+'__'+coord_str) nbase_name_lst.append(base_name_hat+'_'+coord_str) rnbase_name_lst.append(base_name_hat+'__'+coord_str) if not (MV.n == len(coords) == len(base_name_lst)): print 'rebaseMV inputs not congruent:' print 'MV.n =',MV.n print 'coords =',coords print 'bases =',base_name sys.exit(1) if isinstance(x,MV): #Calculate basis vectors from derivatives of position vector x bases = numpy.array(MV.n*[ZERO],dtype=numpy.object) i = 0 for coord in coords: ei = x.diff(coords[i]) ei.set_name(base_name_lst[i]) bases[i] = ei i += 1 #Calculate normalizee basis vectors and basis vector magnitudes if debug: print 'Coordinate Generating Vector' print x print 'Basis Vectors' for base in bases: print base else: #Input basis vectors as N vector fields bases = x for (base,name) in zip(bases,base_name_lst): base.set_name(name) if debug: print 'Basis Vectors' for base in bases: print base if debug_level == 1: return if debug_level == 1: return #Calculate normalized basis vectors and magnitudes of #unormalized basis vectors (nbases,mags) = normalize(bases,nbase_name_lst) if debug: print 'Magnitudes' print '\\abs{'+LaTeX_base+'_{i}} = ',mags print 'Normalized Basis Vectors' for nbase in nbases: print nbase g = numpy.array(MV.n*[MV.n*[ZERO]],dtype=numpy.object) for irow in MV.nrg: for icol in MV.nrg: magsq = sympy.expand((nbases[irow]|nbases[icol])()) g[irow][icol] = sympy.simplify(sympy.trigsimp(magsq,deep=True,recursive=True)) if debug: print 'Metric $\\hat{g}_{ij} = \\hat{'+LaTeX_base+'}_{i}\\cdot \\hat{'+\ LaTeX_base+'}_{j}$' print r'\hat{g}_{ij} =',sympy.galgebra.latex_ex.LaTeX(g) if debug_level == 2: return #Calculate reciprocal normalized basis vectors rnbases = [] if is_quasi_unit_numpy_array(g): ibasis = 0 while ibasis < MV.n: base = g[ibasis][ibasis]*nbases[ibasis] base.set_name(rnbase_name_lst[ibasis]) base.simplify() base.trigsimp() rnbases.append(base) ibasis += 1 else: rnbases = reciprocal_frame(nbases,rnbase_name_lst) ibase = 0 for base in rnbases: base.simplify() base.trigsimp() rnbases[ibase] = base ibase += 1 if debug: if debug_level != 0: sympy.galgebra.latex_ex.MV_format(1) print 'Reciprocal Normalized Basis Vectors' for rnbase in rnbases: print rnbase if debug_level == 3: return #Calculate components of inverse vectors Acoef = [] for ibasis in MV.nrg: evec = numpy.array(MV.n*[ZERO],dtype=numpy.object) for jbasis in MV.nrg: evec[jbasis] = (MV.bvec[ibasis]|rnbases[jbasis])() Acoef.append(evec) #Calculat metric tensors gr = numpy.array(MV.n*[MV.n*[ZERO]],dtype=numpy.object) for irow in MV.nrg: for icol in MV.nrg: magsq = sympy.expand((rnbases[irow]|rnbases[icol])()) gr[irow][icol] = sympy.simplify(sympy.trigsimp(magsq,deep=True,recursive=True)) if debug: print 'Metric $\\hat{g}^{ij} = \\hat{'+LaTeX_base+'}^{i}\\cdot \\hat{'+\ LaTeX_base+'}^{j}$' print r'\hat{g}^{ij} =',sympy.galgebra.latex_ex.LaTeX(gr) if debug_level == 4: return #Calculate bases and reciprocal bases for curvilinear mulitvectors MV_bases = [[ONE]] MV_rbases = [[ONE]] igrade = 1 while igrade <= MV.n: base_index = MV.gabasis[igrade] nbase_index = len(base_index) grade_bases = [] rgrade_bases = [] for index in base_index: base = build_base(index,nbases) base.simplify() base.trigsimp() rbase = build_base(index,rnbases,True) rbase.simplify() rbase.trigsimp() grade_bases.append(base) rgrade_bases.append(rbase) igrade += 1 MV_bases.append(grade_bases) MV_rbases.append(rgrade_bases) #Calculate connection multivectors for geometric derivative MV_connect = [[ZERO]] igrade = 1 while igrade <= MV.n: grade_connect = [] ibase = 0 for base in MV_bases[igrade]: sum = MV() itheta = 0 for (theta,etheta) in zip(coords,rnbases): psum = (1/mags[itheta])*etheta*base.diff(theta) psum.trigsimp() sum += psum itheta += 1 sum.simplify() sum.trigsimp() grade_connect.append(sum) ibase += 1 MV_connect.append(grade_connect) igrade += 1 if debug: print 'Curvilinear Bases: $'+bstr+' = '+bmhat+'_{i_{1}}\\W\\dots\\W'+bmhat+'_{i_{R}}$' igrade = 1 for grade in MV_bases[1:]: ibase = 0 for base in grade: index = MV.gabasis[igrade][ibase] sub_str = '' for i in index: sub_str += sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(coords_lst[i]) base_str = bmhat+'_{['+sub_str+']} = ' print base_str,base ibase += 1 igrade += 1 if debug_level == 5: return #Calculate representation of connection multivectors in curvilinear system MV_Connect = [[ZERO]] igrade = 1 while igrade <= MV.n: grade_connect = [] ibase = 0 nbase = len(MV_bases[igrade]) if igrade < MV.n: ibase = 0 p1base = len(MV_bases[igrade+1]) m1base = len(MV_bases[igrade-1]) while ibase < nbase: Cm1 = numpy.array(m1base*[ZERO],dtype=numpy.object) Cp1 = numpy.array(p1base*[ZERO],dtype=numpy.object) C = MV_connect[igrade][ibase] if igrade == 1: X = C(0) else: X = MV() jbase = 0 while jbase < m1base: Cm1[jbase] = sympy.trigsimp((MV.inner_product(MV_rbases[igrade-1][jbase],C))(),deep=True,recursive=True) jbase += 1 jbase = 0 while jbase < p1base: Cp1[jbase] = sympy.trigsimp((MV.inner_product(MV_rbases[igrade+1][jbase],C))(),deep=True,recursive=True) jbase += 1 X += MV((igrade-1,Cm1),'grade')+MV((igrade+1,Cp1),'grade') X.simplify() X.trigsimp() grade_connect.append(X) ibase += 1 else: ibase = 0 m1base = len(MV_bases[igrade-1]) while ibase < nbase: Cm1 = numpy.array(m1base*[ZERO],dtype=numpy.object) C = MV_connect[igrade][ibase] jbase = 0 while jbase < m1base: Cm1[jbase] = sympy.trigsimp((MV.inner_product(MV_rbases[igrade-1][jbase],C))(),deep=True,recursive=True) jbase += 1 X = MV() X.mv[MV.n-1] = Cm1 X.simplify() X.trigsimp() grade_connect.append(X) ibase += 1 MV_Connect.append(grade_connect) igrade += 1 base_str = '' for coord in coords: base_str += base_name+'_'+sympy.galgebra.latex_ex.LatexPrinter.str_basic(coord)+' ' base_str = base_str[:-1] old_names = MV.vbasis MV.setup(base_str,g,True,coords) MV.curvilinear_flg = True MV.Connect = MV_Connect sympy.galgebra.latex_ex.LatexPrinter.latex_bases() MV.Rframe = numpy.array(MV.n*[ZERO],dtype=numpy.object) ibasis = 0 while ibasis < MV.n: base = MV() jbasis = 0 while jbasis < MV.n: base.add_in_place(gr[ibasis][jbasis]*MV.bvec[jbasis]) jbasis += 1 base.scalar_mul_inplace(1/mags[ibasis]) MV.Rframe[ibasis] = base ibasis += 1 MV.org_basis = [] for ibasis in MV.nrg: evec = MV(Acoef[ibasis],'vector',old_names[ibasis]) setattr(MAIN_PROGRAM,evec.name,evec) MV.org_basis.append(evec) if MV.coords[0] == sympy.Symbol('t'): MV.dedt = [] for coef in dedt_coef: MV.dedt.append(MV(coef,'vector')) else: MV.dedt = None if debug: print 'Representation of Original Basis Vectors' for evec in MV.org_basis: print evec print 'Renormalized Reciprocal Vectors '+\ '$\\bfrac{'+bmhat+'^{k}}{\\abs{\\bm{'+LaTeX_base+'}_{k}}}$' ibasis = 0 while ibasis < MV.n: c_str = sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(coords_lst[ibasis]) print '\\bfrac{\\bm{\\hat{'+LaTeX_base+\ '}}^{'+c_str+'}}{\\abs{\\bm{'+LaTeX_base+\ '}_{'+c_str+'}}} =',MV.Rframe[ibasis] ibasis += 1 title_str = 'Connection Multivectors: $C\\lbrc'+bstr+'\\rbrc = '+\ '\\bfrac{'+bmhat+'^{k}}{\\abs{'+bmhat+\ '_{k}}}\\pdiff{'+bstr+'}{\\theta^{k}}$' print title_str igrade = 1 for grade in MV.Connect[1:]: ibase = 0 for base in grade: index = MV.gabasis[igrade][ibase] sub_str = '' for i in index: sub_str += sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(coords_lst[i]) base_str = 'C\\lbrc\\hat{'+LaTeX_base+'}_{['+sub_str+']}\\rbrc = ' print base_str,base ibase += 1 igrade += 1 return @staticmethod def print_blades(): """ Set multivector output to blade representation. """ MV.bladeprint = 1 return @staticmethod def print_bases(): """ Set multivector output to base representation. """ MV.bladeprint = 0 return @staticmethod def multiplication_table(): """ Calculate geometric product base multiplication table. See reference 5 section 3 for details. """ MV.mtable = [] for igrade in MV.n1rg: MV.mtable.append([]) for ibase in range(MV.nbasis[igrade]): MV.mtable[igrade].append([]) if igrade == 0: base1 = [] else: base1 = MV.basis[igrade][ibase] for jgrade in MV.n1rg: MV.mtable[igrade][ibase].append([]) for jbase in range(MV.nbasis[jgrade]): if jgrade == 0: base2 = [] else: base2 = MV.basis[jgrade][jbase] base = base1+base2 (coefs,bases) = MV.reduce_basis(base) product = MV.convert(coefs,bases) product.name = '('+MV.basislabel[igrade][ibase]+')('+\ MV.basislabel[jgrade][jbase]+')' MV.mtable[igrade][ibase][jgrade].append(product) if MV.debug: print 'Multiplication Table:' for level1 in MV.mtable: for level2 in level1: for level3 in level2: for mv in level3: mv.printmv() return @staticmethod def geometric_product(mv1,mv2): """ MV.geometric_product(mv1,mv2) calculates the geometric product the multivectors mv1 and mv2 (mv1*mv2). See reference 5 section 3. """ product = MV() if isinstance(mv1,MV) and isinstance(mv2,MV): bladeflg1 = mv1.bladeflg bladeflg2 = mv2.bladeflg if bladeflg1: mv1.convert_from_blades() if bladeflg2: mv2.convert_from_blades() mul = MV() for igrade in MV.n1rg: gradei = mv1.mv[igrade] if isinstance(gradei,numpy.ndarray): for ibase in range(MV.nbasis[igrade]): xi = gradei[ibase] if xi != ZERO: for jgrade in MV.n1rg: gradej = mv2.mv[jgrade] if isinstance(gradej,numpy.ndarray): for jbase in range(MV.nbasis[jgrade]): xj = gradej[jbase] if xj != ZERO: xixj = MV.mtable[igrade][ibase][jgrade][jbase].scalar_mul(xi*xj) product.add_in_place(xixj) product.bladeflg = 0 if bladeflg1: mv1.convert_to_blades() if bladeflg2: mv1.convert_to_blades() if bladeflg1 and bladeflg2: product.convert_to_blades() else: if isinstance(mv1,MV): product = mv1.scalar_mul(mv2) else: product = mv2.scalar_mul(mv1) return(product) @staticmethod def wedge(igrade1,blade1,vector2,name=''): """ Calculate the outer product of a multivector blade and a vector. See reference 5 section 5 for details. """ w12 = blade1*vector2 w21 = vector2*blade1 if igrade1%2 != 0: w = w12-w21 else: w = w12+w21 w.name = name return(w*HALF) @staticmethod def blade_table(): """ Calculate basis blades in terms of bases. See reference 5 section 5 for details. Used to convert from blade to base representation. """ MV.btable = [] MV.bladelabel = [] basis_str = MV.basislabel[1] for igrade in MV.n1rg: MV.bladelabel.append([]) if igrade == 0: MV.bladelabel[0].append('1') tmp = [MV(value=ONE,mvtype='scalar',mvname='1')] if igrade == 1: tmp = [] for ibase in range(MV.nbasis[1]): MV.bladelabel[1].append(basis_str[ibase]) tmp.append(MV(value=ibase,mvtype='basisvector',mvname=basis_str[ibase])) if igrade >= 2: tmp = [] basis = MV.basis[igrade] nblades = MV.nbasis[igrade] iblade = 0 for blade in basis: name = '' for i in blade: name += basis_str[i]+'^' name = name[:-1] MV.bladelabel[igrade].append(name) lblade = MV.basis[igrade-1].index(blade[:-1]) rblade = blade[-1] igrade1 = igrade-1 blade1 = MV.btable[igrade1][lblade] vector2 = MV.btable[1][rblade] b1Wv2 = MV.wedge(igrade1,blade1,vector2,name) tmp.append(b1Wv2) MV.btable.append(tmp) if MV.debug: print 'Blade Tabel:' for grade in MV.btable: for mv in grade: print mv print 'Blade Labels:' print MV.bladelabel return @staticmethod def inverse_blade_table(): """ Calculate bases in terms of basis blades. See reference 5 section 5 for details. Used to convert from base to blade representation. """ MV.ibtable = [] for igrade in MV.n1rg: if igrade == 0: tmp = [MV(value=ONE,mvtype='scalar',mvname='1')] if igrade == 1: tmp = [] for ibase in range(MV.nbasis[1]): tmp.append(MV(value=ibase,mvtype='basisvector')) if igrade >= 2: tmp = [] iblade = 0 for blade in MV.btable[igrade]: invblade = MV() for i in range(igrade-1): invblade.mv[i] = -blade.mv[i] invblade.mv[igrade] = +blade.mv[igrade] invblade.bladeflg = 1 if igrade >= 4: jgrade = igrade-2 while jgrade > 1: for ibase in range(MV.nbasis[jgrade]): invblade.substitute_base(jgrade,ibase,MV.ibtable[jgrade][ibase]) jgrade -= 2 invblade.name = MV.basislabel[igrade][iblade] iblade += 1 tmp.append(invblade) MV.ibtable.append(tmp) if MV.debug: print 'Inverse Blade Tabel:' for grade in MV.ibtable: for mv in grade: mv.printmv() return @staticmethod def outer_product(mv1,mv2): """ MV.outer_product(mv1,mv2) calculates the outer (exterior,wedge) product of the multivectors mv1 and mv2 (mv1^mv2). See reference 5 section 6. """ if type(mv1) == type(MV) and type(mv2) == type(MV): if mv1.is_scalar() and mv2.is_scalar(): return(mv1*mv2) if isinstance(mv1,MV) and isinstance(mv2,MV): product = MV() product.bladeflg = 1 mv1.convert_to_blades() mv2.convert_to_blades() for igrade1 in MV.n1rg: if isinstance(mv1.mv[igrade1],numpy.ndarray): pg1 = mv1.project(igrade1) for igrade2 in MV.n1rg: igrade = igrade1+igrade2 if igrade <= MV.n: if isinstance(mv2.mv[igrade2],numpy.ndarray): pg2 = mv2.project(igrade2) pg1pg2 = pg1*pg2 product.add_in_place(pg1pg2.project(igrade)) else: if isinstance(mv1,MV): product = mv1.scalar_mul(mv2) if isinstance(mv2,MV): product = mv2.scalar_mul(mv1) return(product) @staticmethod def inner_product(mv1,mv2,mode='s'): """ MV.inner_product(mv1,mv2) calculates the inner mode = 's' - symmetric (Doran & Lasenby) mode = 'l' - left contraction (Dorst) mode = 'r' - right contraction (Dorst) """ if type(mv1) == type(MV) and type(mv2) == type(MV): if mv1.is_scalar() and mv2.is_scalar(): return(mv1*mv2) if isinstance(mv1,MV) and isinstance(mv2,MV): product = MV() product.bladeflg = 1 mv1.convert_to_blades() mv2.convert_to_blades() for igrade1 in range(MV.n1): if isinstance(mv1.mv[igrade1],numpy.ndarray): pg1 = mv1.project(igrade1) for igrade2 in range(MV.n1): igrade = igrade1-igrade2 if mode == 's': igrade = igrade.__abs__() else: if mode == 'l': igrade = -igrade if igrade >= 0: if isinstance(mv2.mv[igrade2],numpy.ndarray): pg2 = mv2.project(igrade2) pg1pg2 = pg1*pg2 product.add_in_place(pg1pg2.project(igrade)) return(product) else: if mode == 's': if isinstance(mv1,MV): product = mv1.scalar_mul(mv2) if isinstance(mv2,MV): product = mv2.scalar_mul(mv1) else: product = None return(product) @staticmethod def addition(mv1,mv2): """ MV.addition(mv1,mv2) calculates the sum of the multivectors mv1 and mv2 (mv1+mv2). """ sum = MV() if isinstance(mv1,MV) and isinstance(mv2,MV): if mv1.bladeflg or mv2.bladeflg: mv1.convert_to_blades() mv2.convert_to_blades() sum.bladeflg = 1 for i in MV.n1rg: if isinstance(mv1.mv[i],numpy.ndarray) and isinstance(mv2.mv[i],numpy.ndarray): sum.mv[i] = mv1.mv[i]+mv2.mv[i] else: if isinstance(mv1.mv[i],numpy.ndarray) and not isinstance(mv2.mv[i],numpy.ndarray): sum.mv[i] = +mv1.mv[i] else: if isinstance(mv2.mv[i],numpy.ndarray) and not isinstance(mv1.mv[i],numpy.ndarray): sum.mv[i] = +mv2.mv[i] return(sum) else: if isinstance(mv1,MV): return(mv1+MV(mv2,'scalar')) else: return(MV(mv1,'scalar')+mv2) @staticmethod def subtraction(mv1,mv2): """ MV.subtraction(mv1,mv2) calculates the difference of the multivectors mv1 and mv2 (mv1-mv2). """ diff = MV() if isinstance(mv1,MV) and isinstance(mv2,MV): if mv1.bladeflg or mv2.bladeflg: mv1.convert_to_blades() mv2.convert_to_blades() diff.bladeflg = 1 for i in MV.n1rg: if isinstance(mv1.mv[i],numpy.ndarray) and isinstance(mv2.mv[i],numpy.ndarray): diff.mv[i] = mv1.mv[i]-mv2.mv[i] else: if isinstance(mv1.mv[i],numpy.ndarray) and not isinstance(mv2.mv[i],numpy.ndarray): diff.mv[i] = +mv1.mv[i] else: if not isinstance(mv1.mv[i],numpy.ndarray) and isinstance(mv2.mv[i],numpy.ndarray): diff.mv[i] = -mv2.mv[i] return(diff) else: if isinstance(mv1,MV): return(mv1-MV(mv2,'scalar')) else: return(MV(mv1,'scalar')-mv2) @staticmethod def vdiff(vec,x): dvec = numpy.array(len(vec)*[ZERO]) ivec = 0 for veci in vec: dvec[ivec] = sympy.diff(veci,x) ivec += 1 return(dvec) @staticmethod def scalar_to_symbol(scalar): if isinstance(scalar,MV): return(scalar.mv[0][0]) if type(scalar) == types.ListType: sym = [] for x in scalar: sym.append(MV.scalar_to_symbol(x)) return(sym) return(scalar) def __init__(self,value='',mvtype='',mvname='',fct=False,vars=None): """ Initialization of multivector X. Inputs are as follows mvtype value result default default Zero multivector 'basisvector' int i ith basis vector 'basisbivector' int i ith basis bivector 'scalar' symbol x scalar of value x string s 'grade' [int i, symbol array A] X.grade(i) = A [int i, string s] 'vector' symbol array A X.grade(1) = A string s 'grade2' symbol array A X.grade(2) = A string s 'pseudo' symbol x X.grade(n) = x string s 'spinor' string s spinor with coefficients s__indices and name sbm mvname is name of multivector. If fct is 'True' and MV.coords is defined in MV.setup then a multivector field of MV.coords is instantiated. """ self.name = mvname self.mv = MV.n1*[0] self.bladeflg = 0 #1 for blade expansion self.puregrade = 1 if mvtype == 'basisvector': self.mv[1] = numpy.array(MV.nbasis[1]*[ZERO],dtype=numpy.object) self.mv[1][value] = ONE if mvtype == 'basisbivector': self.mv[2] = numpy.array(MV.nbasis[2]*[ZERO],dtype=numpy.object) self.mv[2][value] = ONE if mvtype == 'scalar': if isinstance(value,types.StringType): value = sympy.Symbol(value) if isinstance(value,types.IntType): value = sympy.Rational(value) self.mv[0] = numpy.array([value],dtype=numpy.object) if mvtype == 'pseudo': if isinstance(value,types.StringType): value = sympy.Symbol(value) self.mv[MV.n] = numpy.array([value],dtype=numpy.object) if mvtype == 'vector': if isinstance(value,types.StringType): #Most general vector symbol_str = '' for ibase in MV.nrg: if MV.coords == None: symbol = value+'__'+str(ibase+MV.index_offset) symbol_str += symbol+' ' else: symbol = value+'__'+(MV.coords[ibase]).name symbol_str += symbol+' ' symbol_lst = make_symbols(symbol_str) self.mv[1] = numpy.array(symbol_lst,dtype=numpy.object) self.name = value else: value = MV.pad_zeros(value,MV.nbasis[1]) self.mv[1] = numpy.array(value,dtype=numpy.object) if mvtype == 'grade2': if isinstance(value,types.StringType): #Most general grade-2 multivector if value != '': symbol_str = '' for base in MV.basis[2]: symbol = value+MV.construct_index(base) symbol_str += symbol+' ' symbol_lst = make_symbols(symbol_str) self.mv[2] = numpy.array(symbol_lst,dtype=numpy.object) else: value = MV.pad_zeros(value,MV.nbasis[2]) self.mv[2] = numpy.array(value,dtype=numpy.object) if mvtype == 'grade': igrade = value[0] coefs = value[1] if isinstance(coefs,types.StringType): #Most general pure grade multivector base_symbol = coefs coefs = [] bases = MV.basis[igrade] if igrade == 0: self.mv[0] = numpy.array([sympy.Symbol(base_symbol)],dtype=numpy.object) else: for base in bases: coef = base_symbol+MV.construct_index(base) coef = sympy.Symbol(coef) coefs.append(coef) self.mv[igrade] = numpy.array(coefs,dtype=numpy.object) else: self.mv[igrade] = coefs if mvtype == 'base': self.mv[value[0]] = numpy.array(MV.nbasis[value[0]]*[ZERO],dtype=numpy.object) self.mv[value[0]][value[1]] = ONE if mvtype == 'spinor': if isinstance(value,types.StringType): #Most general spinor for grade in MV.n1rg: if grade%2 == 0: symbol_str = '' if grade != 0: for base in MV.basis[grade]: symbol = value+MV.construct_index(base) symbol_str += symbol+' ' symbol_lst = make_symbols(symbol_str) self.mv[grade] = numpy.array(symbol_lst,dtype=numpy.object) else: self.mv[0] = numpy.array([sympy.Symbol(value)],dtype=numpy.object) self.name = value+'bm' if isinstance(value,types.StringType) and mvtype == '': #Most general multivector if value != '': for grade in MV.n1rg: symbol_str = '' if grade != 0: for base in MV.basis[grade]: symbol = value+MV.construct_index(base) symbol_str += symbol+' ' symbol_lst = make_symbols(symbol_str) self.mv[grade] = numpy.array(symbol_lst,dtype=numpy.object) else: self.mv[0] = numpy.array([sympy.Symbol(value)],dtype=numpy.object) self.name = value+'bm' if fct: if vars is not None: vars = tuple(vars) for grade in MV.n1rg: if not isinstance(self.mv[grade],types.IntType): if grade == 0: coef = sympy.galgebra.latex_ex.LatexPrinter.str_basic(self.mv[0][0]) if vars == None and MV.coords is not None: self.mv[0]= numpy.array([sympy.Function(coef)(*MV.coords)],dtype=numpy.object) else: self.mv[0]= numpy.array([sympy.Function(coef)(*vars)],dtype=numpy.object) else: for base in range(MV.nbasis[grade]): coef = sympy.galgebra.latex_ex.LatexPrinter.str_basic(self.mv[grade][base]) if vars == None and MV.coords is not None: self.mv[grade][base] = sympy.Function(coef)(*MV.coords) else: self.mv[grade][base] = sympy.Function(coef)(*vars) @staticmethod def construct_index(base): index_str = '' if len(base) == 0: return('') if MV.coords == None: for ix in base: index_str += str(ix+MV.index_offset) else: for ix in base: index_str += (MV.coords[ix]).name return('__'+index_str) def set_name(self,namestr): self.name = namestr return def max_grade(self): """ X.max_grade() is maximum grade of non-zero grades of X. """ for i in range(MV.n,-1,-1): if isinstance(self.mv[i],numpy.ndarray): return(i) return(-1) @staticmethod def coord(xname,offset=0): xi_str = '' for i in MV.nrg: xi_str += xname+str(i+offset)+' ' xi = make_symbols(xi_str) x = MV(xi,'vector') return(x) def x(self,i): if isint(self.mv[1]): return(ZERO) return(self.mv[1][i]) def set_coef(self,grade,base,value): if isinstance(self.mv[grade],types.IntType): self.mv[grade] = numpy.array(MV.nbasis[grade]*[ZERO],dtype=numpy.object) self.mv[grade][base] = value return @staticmethod def named(mvname,value='',mvtype=''): name = mvname tmp = MV(value=value,mvtype=mvtype,mvname=name) setattr(sys.modules[__name__],name,tmp) return @staticmethod def printnm(tpl): for a in tpl: print a.name,' =',a.mv return def __str__(self): return(MV.str_rep(self)) def printmv(self,name=''): title = '' if name: title += name+' = ' else: if self.name: title += self.name+' = ' print title+MV.str_rep(self) return def set_value(self,igrade,ibase,value): if isinstance(self.mv[igrade],numpy.ndarray): self.mv[igrade][ibase] = value else: self.mv[igrade] = numpy.array(MV.nbasis[igrade]*[ZERO],dtype=numpy.object) self.mv[igrade][ibase] = value return def add_in_place(self,mv): """ X.add_in_place(mv) increments multivector X by multivector mv. """ if self.bladeflg or mv.bladeflg: self.convert_to_blades() mv.convert_to_blades() for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray) and isinstance(mv.mv[i],numpy.ndarray): self.mv[i] += mv.mv[i] else: if not isinstance(self.mv[i],numpy.ndarray) and isinstance(mv.mv[i],numpy.ndarray): self.mv[i] = +mv.mv[i] return def sub_in_place(self,mv): """ X.sub_in_place(mv) decrements multivector X by multivector mv. """ if self.bladeflg or mv.bladeflg: self.convert_to_blades() mv.convert_to_blades() for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray) and isinstance(mv.mv[i],numpy.ndarray): self.mv[i] -= mv.mv[i] else: if not isinstance(self.mv[i],numpy.ndarray) and isinstance(mv.mv[i],numpy.ndarray): self.mv[i] = +mv.mv[i] return def __pos__(self): p = MV() p.bladeflg = self.bladeflg for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): p.mv[i] = +self.mv[i] return(p) def __neg__(self): n = MV() n.bladeflg = self.bladeflg for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): n.mv[i] = -self.mv[i] return(n) def __add_ab__(self,mv): self.add_in_place(mv) return def __sub_ab__(self,mv): self.sub_in_place(mv) return def __add__(self,mv): """See MV.addition(self,mv)""" return(MV.addition(self,mv)) def __radd__(self,mv): """See MV.addition(mv,self)""" return(MV.addition(mv,self)) def __sub__(self,mv): """See MV.subtraction(self,mv)""" return(MV.subtraction(self,mv)) def __rsub__(self,mv): """See MV.subtraction(mv,self)""" return(MV.subtraction(mv,self)) def __xor__(self,mv): """See MV.outer_product(self,mv)""" return(MV.outer_product(self,mv)) def __pow__(self,mv): """See MV.outer_product(self,mv)""" return(MV.outer_product(self,mv)) def __rxor__(self,mv): """See MV.outer_product(mv,self)""" return(MV.outer_product(mv,self)) def __or__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(self,mv)) def __ror__(self,mv): """See MV.inner_product(mv,self)""" return(MV.inner_product(mv,self)) def __lt__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(self,mv,'l')) def __lshift__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(self,mv,'l')) def __rlshift__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(mv,self,'l')) def lc(self,mv): return(MV.inner_product(self,mv,'l')) def __gt__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(self,mv,'r')) def __rshift__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(self,mv,'r')) def __rrshift__(self,mv): """See MV.inner_product(self,mv)""" return(MV.inner_product(mv,self,'r')) def rc(self,mv): return(MV.inner_product(self,mv,'r')) def scalar_mul(self,c): """ Y = X.scalar_mul(c), multiply multivector X by scalar c and return result. """ mv = MV() mv.bladeflg = self.bladeflg mv.puregrade = self.puregrade for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): #print self.mv[i] #print c,type(c) mv.mv[i] = self.mv[i]*c return(mv) def scalar_mul_inplace(self,c): """ X.scalar_mul_inplace(c), multiply multivector X by scalar c and save result in X. """ for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): self.mv[i] = self.mv[i]*c return def __mul__(self,mv): """See MV.geometric_product(self,mv)""" return(MV.geometric_product(self,mv)) def __rmul__(self,mv): """See MV.geometric_product(mv,self)""" return(MV.geometric_product(mv,self)) def __div__(self,scalar): div = MV() div.bladeflg = self.bladeflg for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): div.mv[i] = self.mv[i]/scalar return(div) def __div_ab__(self,scalar): for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): self.mv[i] /= scalar return def __call__(self,igrade=0,ibase=0): """ X(i,j) returns symbol in ith grade and jth base or blade of multivector X. """ if not isinstance(self.mv[igrade],numpy.ndarray): return(ZERO) return(self.mv[igrade][ibase]) @staticmethod def equal(mv1,mv2): mv1.compact() if isinstance(mv2,MV): mv2.compact() pure_grade = mv1.is_pure() if not isinstance(mv2,MV) and pure_grade != 0: return(False) if not isinstance(mv2,MV) and pure_grade == 0: if isinstance(mv1.mv[0],types.IntType): return(mv2 == 0) else: return(mv1.mv[0][0] == mv2) for (mvi,mvj) in zip(mv1.mv,mv2.mv): if isint(mvi) ^ isint(mvj): return(False) if isinstance(mvi,numpy.ndarray) and isinstance(mvj,numpy.ndarray): for (x,y) in zip(mvi,mvj): if x != y: return(False) return(True) def __eq__(self,mv): return(MV.equal(self,mv)) def copy(self,sub=0): """ Y = X.copy(), make a deep copy of multivector X in multivector Y so that Y can be modified without affecting X. """ cpy = MV() cpy.name = self.name cpy.bladeflg = self.bladeflg cpy.puregrade = self.puregrade for i in MV.n1rg: if sub: if isinstance(self.mv[i],numpy.ndarray): cpy.mv[i] = -self.mv[i] else: if isinstance(self.mv[i],numpy.ndarray): cpy.mv[i] = +self.mv[i] return(cpy) def substitute_base(self,igrade,base,mv): if not isinstance(self.mv[igrade],numpy.ndarray): return if isinstance(base,numpy.ndarray): ibase = MV.basis[igrade].index(base) else: ibase = base coef = self.mv[igrade][ibase] if coef == ZERO: return self.mv[igrade][ibase] = ZERO self.add_in_place(mv*coef) return def convert_to_blades(self): """ X.convert_to_blades(), inplace convert base representation to blade representation. See reference 5 section 5. """ if self.bladeflg: return self.bladeflg = 1 for igrade in range(2,MV.n1): if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): coef = self.mv[igrade][ibase] if (not coef == ZERO): self.mv[igrade][ibase] = ZERO self.add_in_place(MV.ibtable[igrade][ibase]*coef) return def convert_from_blades(self): """ X.convert_from_blades(), inplace convert blade representation to base representation. See reference 5 section 5. """ if not self.bladeflg: return self.bladeflg = 0 for igrade in range(2,MV.n1): if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): coef = self.mv[igrade][ibase] if (not coef == ZERO): self.mv[igrade][ibase] = ZERO self.add_in_place(MV.btable[igrade][ibase]*coef) return def project(self,r): """ Grade projection operator. For multivector X, X.project(r) returns multivector of grade r components of X if r is an integer. If r is a multivector X.project(r) returns a multivector consisting of the grade of X for which r has non- zero grades. For example if X is a general multivector and r is a general spinor then X.project(r) will return the even grades of X. """ if isinstance(r,types.IntType): grade_r = MV() if r > MV.n: return(grade_r) self.convert_to_blades() if not isinstance(self.mv[r],numpy.ndarray): return(grade_r) grade_r.bladeflg = 1 grade_r.puregrade = 1 grade_r.mv[r] = +self.mv[r] return(grade_r) if isinstance(r,MV): self.convert_to_blades() r.convert_to_blades() proj = MV() for i in MV.n1rg: if not isinstance(r.mv[i],types.IntType): proj.mv[i] = self.mv[i] proj.bladeflg = self.bladeflg return(proj) return(None) def even(self): """ Even grade projection operator. For multivector X, X.even() returns multivector of even grade components of X. """ egrades = MV() self.convert_to_blades() egrades.bladeflg = self.bladeflg egrades.puregrade = self.puregrade for igrade in range(0,MV.n1,2): egrades.mv[igrade] = +self.mv[igrade] return(egrades) def odd(self): """ Odd grade projection operator. For multivector X, X.odd() returns multivector of odd grade components of X. """ ogrades = MV() self.convert_to_blades() ogrades.bladeflg = self.bladeflg ogrades.puregrade = self.puregrade for igrade in range(1,MV.n1,2): ogrades.mv[igrade] = +self.mv[igrade] return(ogrades) def rev(self): """ Revision operator. For multivector X, X.rev() returns reversed multivector of X. """ revmv = MV() self.convert_to_blades() revmv.bladeflg = self.bladeflg revmv.puregrade = self.puregrade for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): if igrade < 2 or (not (((igrade*(igrade-1))/2)%2)): revmv.mv[igrade] = +self.mv[igrade] else: revmv.mv[igrade] = -self.mv[igrade] return(revmv) def cse(self,grade): cse_lst = [] if isinstance(self.mv[grade],numpy.ndarray): for ibase in range(MV.nbasis[grade]): if self.mv[grade][ibase] != ZERO: cse_lst.append(sympy.cse(self.mv[grade][ibase])) return(cse_lst) def div(self,grade,divisor): div_lst = [] if isinstance(self.mv[grade],numpy.ndarray): for ibase in range(MV.nbasis[grade]): if self.mv[grade][ibase] != ZERO: div_lst.append(self.mv[grade][ibase].as_coefficient(divisor)) return(div_lst) def collect(self,lst): """ Applies sympy collect function to each component of multivector. """ for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): if self.mv[igrade][ibase] != ZERO: self.mv[igrade][ibase] = \ sympy.collect(self.mv[igrade][ibase],lst) return def sqrfree(self,lst): """ Applies sympy sqrfree function to each component of multivector. """ for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): if self.mv[igrade][ibase] != ZERO: self.mv[igrade][ibase] = \ sympy.sqrfree(self.mv[igrade][ibase],lst) return def flatten(self): flst = [] for igrade in MV.n1rg: if isinstance(self.mv[igrade],types.IntType): flst += MV.nbasis[igrade]*[ZERO] else: for coef in self.mv[igrade]: flst.append(coef) return(flst) def subs(self,*args): X = MV() X.bladeflg = self.bladeflg X.puregrade = self.puregrade for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): X.mv[igrade] = numpy.array(substitute_array(self.mv[igrade],*args)) return(X) def sub_mv(self,mv1,mv2): mv1_flat = mv1.flatten() mv2_flat = mv2.flatten() self.sub_scalar(mv1_flat,mv2_flat) return def sub_scalar(self,expr1,expr2): if (isinstance(expr1,types.ListType) and isinstance(expr2,types.ListType)) or \ (isinstance(expr1,types.TupleType) and isinstance(expr2,types.TupleType)): for (var1,var2) in zip(expr1,expr2): self.sub_scalar(var1,var2) else: for igrade in MV.n1rg: if not isinstance(self.mv[igrade],types.IntType): for ibase in range(MV.nbasis[igrade]): if expr1 != ZERO: self.mv[igrade][ibase] = self.mv[igrade][ibase].subs(expr1,expr2) return def simplify(self): """ Applies sympy simplify function to each component of multivector. """ for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): if self.mv[igrade][ibase] != ZERO: self.mv[igrade][ibase] = \ sympy.simplify(self.mv[igrade][ibase]) return def trigsimp(self): """ Applies sympy trigsimp function to each component of multivector. """ for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): if self.mv[igrade][ibase] != ZERO: self.mv[igrade][ibase] = \ sympy.trigsimp(self.mv[igrade][ibase],deep=True,recursive=True) return def cancel(self): """ Applies sympy cancel function to each component of multivector. """ for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): if self.mv[igrade][ibase] != ZERO: self.mv[igrade][ibase] = \ sympy.cancel(self.mv[igrade][ibase]) return def expand(self): """ Applies sympy expand function to each component of multivector. """ for igrade in MV.n1rg: if isinstance(self.mv[igrade],numpy.ndarray): for ibase in range(MV.nbasis[igrade]): if self.mv[igrade][ibase] != ZERO: self.mv[igrade][ibase] = \ self.mv[igrade][ibase].expand() return def is_pure(self): igrade = -1 ngrade = 0 self.compact() for i in MV.n1rg: if isinstance(self.mv[i],numpy.ndarray): for base in self.mv[i]: if base != 0: igrade = i ngrade += 1 break if ngrade > 1: return(-1) if igrade == -1: return(0) return(igrade) def is_scalar(self): if self.is_pure() == 0: return(True) return(False) def compact(self): """ Convert zero numpy arrays to single integer zero place holder in grade list for instantiated multivector. For example if numpy array of grade one components is a zero array then replace with single integer equal to zero. """ for i in MV.n1rg: if not isint(self.mv[i]): zero_flg = True for x in self.mv[i]: if x != 0: zero_flg = False break if zero_flg: self.mv[i] = 0 return def diff(self,x): """ Calculate partial derivative of multivector with respect to argument x. """ D = MV() igrade = 0 for grade in self.mv: if not isinstance(grade,types.IntType): D.mv[igrade] = MV.vdiff(grade,x) igrade += 1 return(D) def ddt(self): if MV.coords[0] != sympy.Symbol('t'): return(MV()) dxdt = self.diff(MV.coords[0]) for ibase in MV.nrg: dxdt += self.mv[1][ibase]*MV.dedt[ibase] #dxdt.simplify() #dxdt.trigsimp() return(dxdt) def grad(self): """ Calculate geometric (grad) derivative of multivector function """ D = [] dD = MV() for theta in MV.coords: D.append(self.diff(theta)) if MV.curvilinear_flg: recp = MV.Rframe else: recp = MV.brecp for (rbase,iD) in zip(recp,D): dD.add_in_place(rbase*iD) if MV.curvilinear_flg: #Add Connection igrade = 1 while igrade <= MV.n: coefs = self.mv[igrade] if type(coefs) != types.IntType: for (coef,connect) in zip(coefs,MV.Connect[igrade]): dD.add_in_place(coef*connect) igrade += 1 return(dD) def grad_ext(self): """ Calculate outer (exterior,curl) derivative of multivector function. """ D = [] dD = MV() for ix in MV.coords: D.append(self.diff(ix)) if MV.curvilinear_flg: recp = MV.Rframe else: recp = MV.brecp for (irbase,iD) in zip(recp,D): dD.add_in_place(irbase^iD) if MV.curvilinear_flg: #Add Connection igrade = 1 while igrade <= MV.n: coefs = self.mv[igrade] if type(coefs) != types.IntType: for (coef,connect) in zip(coefs,MV.Connect[igrade]): if igrade < MV.n: dD.add_in_place(coef*connect.project(igrade+1)) igrade += 1 return(dD) def curl(self): return(self.grad_ext()) def grad_int(self): """ Calculate inner (interior,div) derivative of multivector function. """ D = [] dD = MV() for ix in MV.coords: D.append(self.diff(ix)) if MV.curvilinear_flg: recp = MV.Rframe else: recp = MV.brecp for (irbase,iD) in zip(recp,D): dD.add_in_place(irbase|iD) if MV.curvilinear_flg: #Add Connection igrade = 1 while igrade <= MV.n: coefs = self.mv[igrade] if type(coefs) != types.IntType: for (coef,connect) in zip(coefs,MV.Connect[igrade]): dD.add_in_place(coef*connect.project(igrade-1)) igrade += 1 return(dD) def div(self): return(self.grad_int()) def mag2(self): """ Calculate scalar component of square of multivector. """ return((self|self)()) def Name(self): """ Get LaTeX name of multivector. """ return(sympy.galgebra.latex_ex.LatexPrinter.extended_symbol(self.name)) def set_names(var_lst,var_str): """ Set the names of a list of multivectors (var_lst) for a space delimited string (var_str) containing the names. """ var_str_lst = var_str.split() if len(var_lst) == len(var_str_lst): for (var,var_name) in zip(var_lst,var_str_lst): var.set_name(var_name) return sys.stderr.write('Error in set_names. Lists incongruent!\n') sys.exit() return def reciprocal_frame(vlst,names=''): """ Calculate reciprocal frame of list (vlst) of vectors. If desired name each vector in list of reciprocal vectors with names in space delimited string (names). """ E = vlst[0] recp = [] if type(names) != types.StringType: name_lst = names else: if names != '': name_lst = names.split() for i in range(1,MV.n): E = E^vlst[i] for i in range(MV.n): tmp = ONE if i%2 != 0: tmp = -ONE for j in range(MV.n): if i != j: tmp = tmp^vlst[j] tmp = tmp*E recp.append(tmp) Esq = sympy.trigsimp(E.mag2(),deep=True,recursive=True) print Esq print sympy.simplify(Esq) Esq_inv = ONE/Esq i = 0 for i in range(MV.n): recp[i].trigsimp() recp[i] = recp[i]*Esq_inv if names != '': recp[i].set_name(name_lst[i]) i += 1 return(recp) def S(value): return(MV(value,'scalar')) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/galgebra/latex_ex.py0000644000175000017500000012356312014170666025040 0ustar georgeskgeorgesk#latex_ex.py import sys #if sys.version.find('Stackless') >= 0: # sys.path.append('/usr/lib/python2.5/site-packages') import os,types,StringIO from sympy.core import S, C, Basic, Symbol, Mul from sympy.printing.printer import Printer from sympy.simplify import fraction import re as regrep import sympy.galgebra.GA #import sympy.galgebra.OGA import numpy from sympy.core.compatibility import cmp_to_key, cmp def debug(txt): sys.stderr.write(txt+'\n') return def find_executable(executable, path=None): """Try to find 'executable' in the directories listed in 'path' (a string listing directories separated by 'os.pathsep'; defaults to os.environ['PATH']). Returns the complete filename or None if not found """ if path is None: path = os.environ['PATH'] paths = path.split(os.pathsep) extlist = [''] if os.name == 'os2': (base, ext) = os.path.splitext(executable) # executable files on OS/2 can have an arbitrary extension, but # .exe is automatically appended if no dot is present in the name if not ext: executable = executable + ".exe" elif sys.platform == 'win32': pathext = os.environ['PATHEXT'].lower().split(os.pathsep) (base, ext) = os.path.splitext(executable) if ext.lower() not in pathext: extlist = pathext for ext in extlist: execname = executable + ext if os.path.isfile(execname): return execname else: for p in paths: f = os.path.join(p, execname) if os.path.isfile(f): return f else: return None def debug(tstr): return def len_cmp(str1,str2): return(len(str2)-len(str1)) def process_equals(xstr): eq1 = xstr.find('=') eq2 = xstr.rfind('=') if eq1 == eq2: return(xstr) xstr = xstr[:eq1]+xstr[eq2:] return(xstr) class LatexPrinter(Printer): """ A printer class which converts an expression into its LaTeX equivalent. This class extends the LatexPrinter class currently in sympy in the following ways: 1. Variable and function names can now encode multiple Greek symbols, number, Greek, and roman super and subscripts and accents plus bold math in an alphanumeric ASCII string consisting of [A-Za-z0-9_] symbols 1 - Accents and bold math are implemented in reverse notation. For example if you wished the LaTeX output to be '\bm{\hat{\sigma}}' you would give the variable the name sigmahatbm. 2 - Subscripts are denoted by a single underscore and superscripts by a double underscore so that A_{\rho\beta}^{25} would be input as A_rhobeta__25. 2. Some standard function names have been improved such as asin is now denoted by Sin^{-1} and log by ln. 3. Several LaTeX formats for multivectors are available: 1 - Print multivector on one line 2 - Print each grade of multivector on one line 3 - Print each base of multivector on one line 4. A LaTeX output for numpy arrays containing sympy expressions is implemented for up to a three dimensional array. 5. LaTeX formatting for raw LaTeX, eqnarray, and array is available in simple output strings. 1 - The delimiter for raw LaTeX input is '%'. The raw input starts on the line where '%' is first encountered and continues until the next line where '%' is encountered. It does not matter where '%' is in the line. 2 - The delimiter for eqnarray input is '@'. The rules are the same as for raw input except that '=' in the first line is replaced be '&=&' and '\begin{eqnarray*}' is added before the first line and '\end{eqnarray*}' to after the last line in the group of lines. 3 - The delimiter for array input is '#'. The rules are the same as for raw input except that '\begin{equation*}' is added before the first line and '\end{equation*}' to after the last line in the group of lines. 6. Additional formats for partial derivatives: 0 - Same as sympy latex module 1 - Use subscript notation with partial symbol to indicate which variable the differentiation is with respect to. Symbol is of form \partial_{differentiation variable} """ #printmethod ='_latex_ex' sym_fmt = 0 fct_fmt = 0 pdiff_fmt = 0 mv_fmt = 0 str_fmt = 1 LaTeX_flg = False mode = ('_','^') fmt_dict = {'sym':0,'fct':0,'pdiff':0,'mv':0,'str':1} fct_dict = {'sin':'sin','cos':'cos','tan':'tan','cot':'cot',\ 'asin':'Sin^{-1}','acos':'Cos^{-1}',\ 'atan':'Tan^{-1}','acot':'Cot^{-1}',\ 'sinh':'sinh','cosh':'cosh','tanh':'tanh','coth':'coth',\ 'asinh':'Sinh^{-1}','acosh':'Cosh^{-1}', 'atanh':'Tanh^{-1}','acoth':'Coth^{-1}',\ 'sqrt':'sqrt','exp':'exp','log':'ln'} fct_dict_keys = fct_dict.keys() greek_keys = sorted(('alpha','beta','gamma','delta','varepsilon','epsilon','zeta',\ 'vartheta','theta','iota','kappa','lambda','mu','nu','xi',\ 'varpi','pi','rho','varrho','varsigma','sigma','tau','upsilon',\ 'varphi','phi','chi','psi','omega','Gamma','Delta','Theta',\ 'Lambda','Xi','Pi','Sigma','Upsilon','Phi','Psi','Omega','partial',\ 'nabla','eta'),key=cmp_to_key(len_cmp)) accent_keys = sorted(('hat','check','dot','breve','acute','ddot','grave','tilde',\ 'mathring','bar','vec','bm','prm','abs'),key=cmp_to_key(len_cmp)) greek_cnt = 0 greek_dict = {} accent_cnt = 0 accent_dict = {} preamble = '\\documentclass[10pt,letter,fleqn]{report}\n'+\ '\\pagestyle{empty}\n'+\ '\\usepackage[latin1]{inputenc}\n'+\ '\\usepackage[dvips,landscape,top=1cm,nohead,nofoot]{geometry}\n'+\ '\\usepackage{amsmath}\n'+\ '\\usepackage{bm}\n'+\ '\\usepackage{amsfonts}\n'+\ '\\usepackage{amssymb}\n'+\ '\\setlength{\\parindent}{0pt}\n'+\ '\\newcommand{\\bfrac}[2]{\\displaystyle\\frac{#1}{#2}}\n'+\ '\\newcommand{\\lp}{\\left (}\n'+\ '\\newcommand{\\rp}{\\right )}\n'+\ '\\newcommand{\\half}{\\frac{1}{2}}\n'+\ '\\newcommand{\\llt}{\\left <}\n'+\ '\\newcommand{\\rgt}{\\right >}\n'+\ '\\newcommand{\\abs}[1]{\\left |{#1}\\right | }\n'+\ '\\newcommand{\\pdiff}[2]{\\bfrac{\\partial {#1}}{\\partial {#2}}}\n'+\ '\\newcommand{\\lbrc}{\\left \\{}\n'+\ '\\newcommand{\\rbrc}{\\right \\}}\n'+\ '\\newcommand{\\W}{\\wedge}\n'+\ "\\newcommand{\\prm}[1]{{#1}'}\n"+\ '\\newcommand{\\ddt}[1]{\\bfrac{d{#1}}{dt}}\n'+\ '\\newcommand{\\R}{\\dagger}\n'+\ '\\begin{document}\n' postscript = '\\end{document}\n' @staticmethod def latex_bases(): """ Generate LaTeX strings for multivector bases """ if type(sympy.galgebra.GA.MV.basislabel_lst) == types.IntType: sys.stderr.write('MV.setup() must be executed before LatexPrinter.format()!\n') sys.exit(1) LatexPrinter.latexbasis_lst = [['']] for grades in sympy.galgebra.GA.MV.basislabel_lst[1:]: grades_lst = [] for grade in grades: grade_lst = [] for base in grade: latex_base = LatexPrinter.extended_symbol(base) grade_lst.append(latex_base) grades_lst.append(grade_lst) LatexPrinter.latexbasis_lst.append(grades_lst) return @staticmethod def build_base(igrade,iblade,bld_flg): if igrade == 0: return('') base_lst = LatexPrinter.latexbasis_lst[igrade][iblade] if len(base_lst) == 1: return(base_lst[0]) base_str = '' for base in base_lst[:-1]: if bld_flg: base_str += base+'\\W ' else: base_str += base base_str += base_lst[-1] return(base_str) @staticmethod def format(sym=0,fct=0,pdiff=0,mv=0): LatexPrinter.LaTeX_flg = True LatexPrinter.fmt_dict['sym'] = sym LatexPrinter.fmt_dict['fct'] = fct LatexPrinter.fmt_dict['pdiff'] = pdiff LatexPrinter.fmt_dict['mv'] = mv LatexPrinter.fmt_dict['str'] = 1 if sympy.galgebra.GA.MV.is_setup: LatexPrinter.latex_bases() LatexPrinter.redirect() return @staticmethod def str_basic(in_str): if not LatexPrinter.LaTeX_flg: return(str(in_str)) Basic.__str__ = LatexPrinter.Basic__str__ out_str = str(in_str) Basic.__str__ = LaTeX return(out_str) @staticmethod def redirect(): LatexPrinter.Basic__str__ = Basic.__str__ LatexPrinter.MV__str__ = sympy.galgebra.GA.MV.__str__ LatexPrinter.stdout = sys.stdout sys.stdout = StringIO.StringIO() Basic.__str__ = LaTeX sympy.galgebra.GA.MV.__str__ = LaTeX return @staticmethod def restore(): LatexPrinter_stdout = sys.stdout LatexPrinter_Basic__str__ = Basic.__str__ LatexPrinter_MV__str__ = sympy.galgebra.GA.MV.__str__ sys.stdout = LatexPrinter.stdout Basic.__str__ = LatexPrinter.Basic__str__ sympy.galgebra.GA.MV.__str__ = LatexPrinter.MV__str__ LatexPrinter.stdout = LatexPrinter_stdout LatexPrinter.Basic__str__ = LatexPrinter_Basic__str__ LatexPrinter.MV__str__ = LatexPrinter_MV__str__ return @staticmethod def format_str(fmt='0 0 0 0'): fmt_lst = fmt.split() if '=' not in fmt: LatexPrinter.fmt_dict['sym'] = int(fmt_lst[0]) LatexPrinter.fmt_dict['fct'] = int(fmt_lst[1]) LatexPrinter.fmt_dict['pdiff'] = int(fmt_lst[2]) LatexPrinter.fmt_dict['mv'] = int(fmt_lst[3]) else: for fmt in fmt_lst: x = fmt.split('=') LatexPrinter.fmt_dict[x[0]] = int(x[1]) if LatexPrinter.LaTeX_flg == False: if sympy.galgebra.GA.MV.is_setup: LatexPrinter.latex_bases() LatexPrinter.redirect() LatexPrinter.LaTeX_flg = True return @staticmethod def append_body(xstr): if LatexPrinter.body_flg: LatexPrinter.body += xstr return('') else: return(xstr[:-1]) @staticmethod def tokenize_greek(name_str): for sym in LatexPrinter.greek_keys: isym = name_str.find(sym) if isym > -1: keystr = '@'+str(LatexPrinter.greek_cnt) LatexPrinter.greek_cnt += 1 LatexPrinter.greek_dict[keystr] = sym name_str = name_str.replace(sym,keystr) return(name_str) @staticmethod def tokenize_accents(name_str): for sym in LatexPrinter.accent_keys: if name_str.find(sym) > -1: keystr = '#'+str(LatexPrinter.accent_cnt)+'#' LatexPrinter.accent_cnt += 1 LatexPrinter.accent_dict[keystr] = '\\'+sym name_str = name_str.replace(sym,keystr) return(name_str) @staticmethod def replace_greek_tokens(name_str): if name_str.find('@') == -1: return(name_str) for token in LatexPrinter.greek_dict.keys(): name_str = name_str.replace(token,'{\\'+LatexPrinter.greek_dict[token]+'}') LatexPrinter.greek_cnt = 0 LatexPrinter.greek_dict = {} return(name_str) @staticmethod def replace_accent_tokens(name_str): tmp_lst = name_str.split('#') name_str = tmp_lst[0] if len(tmp_lst) == 1: return(name_str) for x in tmp_lst[1:]: if x != '': name_str = '{}'+LatexPrinter.accent_dict['#'+x+'#']+'{'+name_str+'}' LatexPrinter.accent_cnt = 0 LatexPrinter.accent_dict = {} return(name_str) @staticmethod def extended_symbol(name_str): name_str = LatexPrinter.tokenize_greek(name_str) tmp_lst = name_str.split('_') subsup_str = '' sym_str = tmp_lst[0] sym_str = LatexPrinter.tokenize_accents(sym_str) sym_str = LatexPrinter.replace_accent_tokens(sym_str) if len(tmp_lst) > 1: imode = 0 for x in tmp_lst[1:]: if x == '': imode = (imode+1)%2 else: subsup_str += LatexPrinter.mode[imode]+'{'+x+'}' #subsup_str += LatexPrinter.mode[imode]+x+' ' imode = (imode+1)%2 name_str = sym_str+subsup_str name_str = LatexPrinter.replace_greek_tokens(name_str) return(name_str) def coefficient(self,coef,first_flg): if isinstance(coef, C.AssocOp) and isinstance(-coef, C.AssocOp): coef_str = r"\lp %s\rp " % self._print(coef) else: coef_str = self._print(coef) if first_flg: first_flg = False if coef_str[0] == '+': coef_str = coef_str[1:] else: if coef_str[0] != '-': if coef_str[0] != '+': coef_str = '+'+coef_str if coef_str in ('1','+1','-1'): if coef_str == '1': coef_str = '' else: coef_str = coef_str[0] return(coef_str,first_flg) def __init__(self,inline=True): Printer.__init__(self) self._inline = inline def doprint(self, expr): tex = Printer.doprint(self, expr) xstr = '' if self._inline: if LatexPrinter.fmt_dict['fct'] == 1: xstr = r"%s" % tex else: xstr = r"$%s$" % tex else: xstr = r"\begin{equation*}%s\end{equation*}" % tex return(xstr) def _needs_brackets(self, expr): return not ((expr.is_Integer and expr.is_nonnegative) or expr.is_Atom) def _do_exponent(self, expr, exp): if exp is not None: return r"\left(%s\right)^{%s}" % (expr, exp) else: return expr def _print_Add(self, expr): tex = str(self._print(expr.args[0])) for term in expr.args[1:]: coeff = term.as_coeff_mul()[0] if coeff.is_negative: tex += r" %s" % self._print(term) else: tex += r" + %s" % self._print(term) return tex def _print_Mul(self, expr): coeff, tail = expr.as_two_terms() if not coeff.is_negative: tex = "" else: coeff = -coeff tex = "- " numer, denom = fraction(tail) def convert(terms): product = [] if not terms.is_Mul: return str(self._print(terms)) else: for term in terms.args: pretty = self._print(term) if term.is_Add: product.append(r"\left(%s\right)" % pretty) else: product.append(str(pretty)) return r" ".join(product) if denom is S.One: if coeff is not S.One: tex += str(self._print(coeff)) + " " if numer.is_Add: tex += r"\left(%s\right)" % convert(numer) else: tex += r"%s" % convert(numer) else: if numer is S.One: if coeff.is_Integer: numer *= coeff.p elif coeff.is_Rational: if coeff.p != 1: numer *= coeff.p denom *= coeff.q elif coeff is not S.One: tex += str(self._print(coeff)) + " " else: if coeff.is_Rational and coeff.p == 1: denom *= coeff.q elif coeff is not S.One: tex += str(self._print(coeff)) + " " tex += r"\frac{%s}{%s}" % \ (convert(numer), convert(denom)) return tex def _print_Pow(self, expr): if expr.exp.is_Rational and expr.exp.q == 2: base, exp = self._print(expr.base), abs(expr.exp.p) if exp == 1: tex = r"\sqrt{%s}" % base else: tex = r"\sqrt[%s]{%s}" % (exp, base) if expr.exp.is_negative: return r"\frac{1}{%s}" % tex else: return tex else: if expr.base.is_Function: return self._print(expr.base, self._print(expr.exp)) else: if expr.exp == S.NegativeOne: #solves issue 1030 #As Mul always simplify 1/x to x**-1 #The objective is achieved with this hack #first we get the latex for -1 * expr, #which is a Mul expression tex = self._print(S.NegativeOne * expr).strip() #the result comes with a minus and a space, so we remove if tex[:1] == "-": return tex[1:].strip() if self._needs_brackets(expr.base): tex = r"\left(%s\right)^{%s}" else: tex = r"{%s}^{%s}" return tex % (self._print(expr.base), self._print(expr.exp)) def _print_Derivative(self, expr): dim = len(expr.variables) if dim == 1: if LatexPrinter.fmt_dict['pdiff'] == 1: tex = r'\partial_{%s}' % self._print(expr.variables[0]) else: tex = r"\frac{\partial}{\partial %s}" % self._print(expr.variables[0]) else: multiplicity, i, tex = [], 1, "" current = expr.variables[0] for symbol in expr.variables[1:]: if symbol == current: i = i + 1 else: multiplicity.append((current, i)) current, i = symbol, 1 else: multiplicity.append((current, i)) if LatexPrinter.fmt_dict['pdiff'] == 1: for x, i in multiplicity: if i == 1: tex += r"\partial_{%s}" % self._print(x) else: tex += r"\partial^{%s}_{%s}" % (i, self._print(x)) else: for x, i in multiplicity: if i == 1: tex += r"\partial %s" % self._print(x) else: tex += r"\partial^{%s} %s" % (i, self._print(x)) tex = r"\frac{\partial^{%s}}{%s} " % (dim, tex) if isinstance(expr.expr, C.AssocOp): return r"%s\left(%s\right)" % (tex, self._print(expr.expr)) else: return r"%s %s" % (tex, self._print(expr.expr)) def _print_Integral(self, expr): tex, symbols = "", [] for symbol, limits in reversed(expr.limits): tex += r"\int" if limits is not None: if not self._inline: tex += r"\limits" tex += "_{%s}^{%s}" % (self._print(limits[0]), self._print(limits[1])) symbols.insert(0, "d%s" % self._print(symbol)) return r"%s %s\,%s" % (tex, str(self._print(expr.function)), " ".join(symbols)) def _print_Limit(self, expr): tex = r"\lim_{%s \to %s}" % (self._print(expr.var), self._print(expr.varlim)) if isinstance(expr.expr, C.AssocOp): return r"%s\left(%s\right)" % (tex, self._print(expr.expr)) else: return r"%s %s" % (tex, self._print(expr.expr)) def _print_Function(self, expr, exp=None): func = expr.func.__name__ if hasattr(self, '_print_' + func): return getattr(self, '_print_' + func)(expr, exp) else: args = [ str(self._print(arg)) for arg in expr.args ] if LatexPrinter.fmt_dict['fct'] == 1: if func in LatexPrinter.fct_dict_keys: if exp is not None: name = r"\operatorname{%s}^{%s}" % (LatexPrinter.fct_dict[func], exp) else: name = r"\operatorname{%s}" % LatexPrinter.fct_dict[func] name += r"\left(%s\right)" % ",".join(args) return name else: func = self.print_Symbol_name(func) if exp is not None: name = r"{%s}^{%s}" % (func, exp) else: name = r"{%s}" % func return name else: if exp is not None: name = r"\operatorname{%s}^{%s}" % (func, exp) else: name = r"\operatorname{%s}" % func return name + r"\left(%s\right)" % ",".join(args) def _print_floor(self, expr, exp=None): tex = r"\lfloor{%s}\rfloor" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_ceiling(self, expr, exp=None): tex = r"\lceil{%s}\rceil" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_abs(self, expr, exp=None): tex = r"\lvert{%s}\rvert" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_re(self, expr, exp=None): if self._needs_brackets(expr.args[0]): tex = r"\Re\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\Re{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_im(self, expr, exp=None): if self._needs_brackets(expr.args[0]): tex = r"\Im\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\Im{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_conjugate(self, expr, exp=None): tex = r"\overline{%s}" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_exp(self, expr, exp=None): tex = r"{e}^{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_gamma(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\operatorname{\Gamma}^{%s}%s" % (exp, tex) else: return r"\operatorname{\Gamma}%s" % tex def _print_factorial(self, expr, exp=None): x = expr.args[0] if self._needs_brackets(x): tex = r"\left(%s\right)!" % self._print(x) else: tex = self._print(x) + "!" if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_binomial(self, expr, exp=None): tex = r"{{%s}\choose{%s}}" % (self._print(expr[0]), self._print(expr[1])) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_RisingFactorial(self, expr, exp=None): tex = r"{\left(%s\right)}^{\left(%s\right)}" % \ (self._print(expr[0]), self._print(expr[1])) return self._do_exponent(tex, exp) def _print_FallingFactorial(self, expr, exp=None): tex = r"{\left(%s\right)}_{\left(%s\right)}" % \ (self._print(expr[0]), self._print(expr[1])) return self._do_exponent(tex, exp) def _print_Rational(self, expr): if expr.q != 1: sign = "" p = expr.p if expr.p < 0: sign = "- " p = -p return r"%s\frac{%d}{%d}" % (sign, p, expr.q) else: return self._print(expr.p) def _print_Infinity(self, expr): return r"\infty" def _print_NegativeInfinity(self, expr): return r"-\infty" def _print_ComplexInfinity(self, expr): return r"\tilde{\infty}" def _print_ImaginaryUnit(self, expr): return r"\mathbf{\imath}" def _print_NaN(self, expr): return r"\bot" def _print_Pi(self, expr): return r"\pi" def _print_Exp1(self, expr): return r"e" def _print_EulerGamma(self, expr): return r"\gamma" def _print_Order(self, expr): return r"\operatorname{\mathcal{O}}\left(%s\right)" % \ self._print(expr.args[0]) @staticmethod def print_Symbol_name(name_str): if len(name_str) == 1: return (name_str) if LatexPrinter.fmt_dict['sym'] == 1: return LatexPrinter.extended_symbol(name_str) else: return(name_str) #convert trailing digits to subscript m = regrep.match('(^[a-zA-Z]+)([0-9]+)$',name_str) if m is not None: name, sub=m.groups() tex=self._print_Symbol(Symbol(name)) tex="%s_{%s}" %(tex, sub) return tex # insert braces to expresions containing '_' or '^' m = regrep.match('(^[a-zA-Z0-9]+)([_\^]{1})([a-zA-Z0-9]+)$',name_str) if m is not None: name, sep, rest=m.groups() tex=self._print_Symbol(Symbol(name)) tex="%s%s{%s}" %(tex, sep, rest) return tex greek = set([ 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega' ]) other = set( ['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar', 'hslash', 'mho' ]) if name_str.lower() in greek: return "\\" + name_str elif name_str in other: return "\\" + name_str else: return name_str def _print_Symbol(self, expr): return LatexPrinter.print_Symbol_name(expr.name) def _print_str(self,expr): if LatexPrinter.fmt_dict['str'] > 0: expr = expr.replace('^','{\\wedge}') expr = expr.replace('|','{\\cdot}') expr = expr.replace('__','^') return(expr) def _print_ndarray(self,expr): shape = numpy.shape(expr) ndim = len(shape) expr_str = '' if ndim == 1: expr_str += '#\\left [ \\begin{array}{'+shape[0]*'c'+'} \n' for col in expr: expr_str += self._print(col)+' & ' expr_str = expr_str[:-2]+'\n\\end{array}\\right ]#\n' return(expr_str) if ndim == 2: expr_str += '#\\left [ \\begin{array}{'+shape[1]*'c'+'} \n' for row in expr[:-1]: for xij in row[:-1]: expr_str += self._print(xij) + ' & ' expr_str += self._print(row[-1]) + ' \\\\ \n' for xij in expr[-1][:-1]: expr_str += self._print(xij) + ' & ' expr_str += self._print(expr[-1][-1]) + '\n \\end{array} \\right ] #\n' return(expr_str) if ndim == 3: expr_str = '#\\left \\{ \\begin{array}{'+shape[0]*'c'+'} \n' for x in expr[:-1]: xstr = self._print(x).replace('#','') expr_str += xstr + ' , & ' xstr = self._print(expr[-1]).replace('#','') expr_str += xstr+'\n\\end{array} \\right \\}#\n' return(expr_str) def _print_MV(self,expr): igrade = 0 MV_str = '' line_lst = [] first_flg = True for grade in expr.mv: if type(grade) != types.IntType: if type(grade) != types.IntType: ibase = 0 for base in grade: if base != 0: tmp = Symbol('XYZW') base_str = str(base*tmp) if base_str[0] != '-': base_str = '+'+base_str base_str = base_str.replace('- ','-') if base_str[1:5] == 'XYZW': base_str = base_str.replace('XYZW','') else: base_str = base_str.replace('XYZW','1') MV_str += base_str+\ LatexPrinter.build_base(igrade,ibase,expr.bladeflg) if LatexPrinter.fmt_dict['mv'] == 3: line_lst.append(MV_str) MV_str = '' ibase += 1 if LatexPrinter.fmt_dict['mv'] == 2: if MV_str != '': line_lst.append(MV_str) MV_str = '' igrade += 1 n_lines = len(line_lst) if MV_str == '': if n_lines > 0 and line_lst[0][0] == '+': line_lst[0] = line_lst[0][1:] else: if MV_str[0] == '+': MV_str = MV_str[1:] if n_lines == 1: MV_str = line_lst[0] n_lines = 0 if LatexPrinter.fmt_dict['mv'] >= 2: MV_str = '@'+line_lst[0]+' \\\\ \n' for line in line_lst[1:-1]: MV_str += '& '+line+' \\\\ \n' MV_str += '& '+line_lst[-1]+'@\n' if MV_str == '': MV_str = '0' if expr.name != '': MV_str = LatexPrinter.extended_symbol(expr.name)+' = '+MV_str return(MV_str) def _print_OMV(self,expr): igrade = 0 MV_str = '' line_lst = [] first_flg = True for grade in expr.mv: if type(grade) is not None: if type(grade) is not None: ibase = 0 for base in grade: if base != 0: tmp = Symbol('XYZW') base_str = str(base*tmp) if base_str[0] != '-': base_str = '+'+base_str base_str = base_str.replace('- ','-') if base_str[1:5] == 'XYZW': base_str = base_str.replace('XYZW','') else: base_str = base_str.replace('XYZW','1') MV_str += base_str+\ LatexPrinter.build_base(igrade,ibase,expr.bladeflg) if LatexPrinter.fmt_dict['mv'] == 3: line_lst.append(MV_str) MV_str = '' ibase += 1 if LatexPrinter.fmt_dict['mv'] == 2: if MV_str != '': line_lst.append(MV_str) MV_str = '' igrade += 1 n_lines = len(line_lst) if MV_str == '': if n_lines > 0 and line_lst[0][0] == '+': line_lst[0] = line_lst[0][1:] else: if MV_str[0] == '+': MV_str = MV_str[1:] if n_lines == 1: MV_str = line_lst[0] n_lines = 0 if LatexPrinter.fmt_dict['mv'] >= 2: MV_str = '@'+line_lst[0]+' \\\\ \n' for line in line_lst[1:-1]: MV_str += '& '+line+' \\\\ \n' MV_str += '& '+line_lst[-1]+'@\n' if MV_str == '': MV_str = '0' if expr.name != '': MV_str = LatexPrinter.extended_symbol(expr.name)+' = '+MV_str return(MV_str) def _print_Relational(self, expr): charmap = { "==" : "=", "<" : "<", "<=" : r"\leq", "!=" : r"\neq", } return "%s %s %s" % (self._print(expr.lhs), charmap[expr.rel_op], self._print(expr.rhs)) def _print_Matrix(self, expr): lines = [] for line in range(expr.lines): # horrible, should be 'rows' lines.append(" & ".join([ self._print(i) for i in expr[line,:] ])) if self._inline: tex = r"\left(\begin{smallmatrix}%s\end{smallmatrix}\right)" else: tex = r"\begin{pmatrix}%s\end{pmatrix}" return tex % r"\\".join(lines) def _print_tuple(self, expr): return r"\begin{pmatrix}%s\end{pmatrix}" % \ r", & ".join([ self._print(i) for i in expr ]) def _print_list(self, expr): return r"\begin{bmatrix}%s\end{bmatrix}" % \ r", & ".join([ self._print(i) for i in expr ]) def _print_dict(self, expr): items = [] keys = expr.keys() keys.sort(Basic.compare_pretty) for key in keys: val = expr[key] items.append("%s : %s" % (self._print(key), self._print(val))) return r"\begin{Bmatrix}%s\end{Bmatrix}" % r", & ".join(items) def _print_DiracDelta(self, expr): if len(expr.args) == 1 or expr.args[1] == 0: tex = r"\delta\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\delta^{\left( %s \right)}\left( %s \right)" % (\ self._print(expr.args[1]), self._print(expr.args[0])) return tex def LaTeX(expr, inline=True): """ Convert the given expression to LaTeX representation. You can specify how the generated code will be delimited. If the 'inline' keyword is set then inline LaTeX $ $ will be used. Otherwise the resulting code will be enclosed in 'equation*' environment (remember to import 'amsmath'). >>> from sympy import Rational >>> from sympy.abc import tau, mu >>> latex((2*tau)**Rational(7,2)) '$8 \\\\sqrt{2} \\\\sqrt[7]{\\\\tau}$' >>> latex((2*mu)**Rational(7,2), inline=False) '\\\\begin{equation*}8 \\\\sqrt{2} \\\\sqrt[7]{\\\\mu}\\\\end{equation*}' Besides all Basic based expressions, you can recursively convert Python containers (lists, tuples and dicts) and also SymPy matrices: >>> latex([2/x, y]) '$\\\\begin{bmatrix}\\\\frac{2}{x}, & y\\\\end{bmatrix}$' The extended latex printer will also append the output to a string (LatexPrinter.body) that will be processed by xdvi() for immediate display one xdvi() is called. """ xstr = LatexPrinter(inline).doprint(expr) return (xstr) def print_LaTeX(expr): """Prints LaTeX representation of the given expression.""" print LaTeX(expr) def Format(fmt='1 1 1 1'): LatexPrinter.format_str(fmt) return def xdvi(filename='tmplatex.tex',debug=False): """ Post processes LaTeX output (see comments below), adds preamble and postscript, generates tex file, inputs file to latex, displays resulting dvi file with xdvi or yap. """ if not LatexPrinter.LaTeX_flg: return body = sys.stdout.getvalue() LatexPrinter.restore() body_lst = body.split('\n') body = '' array_flg = False eqnarray_flg = False raw_flg = False nline = len(body_lst) iline = 0 i = iter(body_lst) line = i.next() while True: if '$' in line: #Inline math expression(s) if len(line) > 0: line += '\\newline \n' body += line try: line = i.next() except StopIteration: break elif '%' in line: #Raw LaTeX input """ If % in line assume line is beginning of raw LaTeX input and stop post processing """ line = line.replace('%','') raw_flg = True while raw_flg: if '%' in line: """ If % in line assume line is end of LaTeX input and begin post processing """ raw_flg = False line = line.replace('%','')+'\n' else: line += '\n' line = process_equals(line) body += line try: line = i.next() except StopIteration: break elif '#' in line: #Array input """ If # in line assume line is beginning of array input and contains \begin{array} statement """ line = line.replace('#','') array_flg = True line = '\\begin{equation*}\n'+line while array_flg: if '#' in line: """ If # in line assume line is end of array input and contains \end{array} statement """ array_flg = False line = line.replace('#','') line += '\\end{equation*}\n' else: line += '\n' line = process_equals(line) body += line try: line = i.next() except StopIteration: break elif '@' in line: #Align input """ If @ in line assume line is beginning of align input """ line = line.replace('@','') line = line.replace('=','& = ') eqnarray_flg = True line = '\\begin{align*}\n'+line line = process_equals(line) body += line try: line = i.next() except StopIteration: break while eqnarray_flg: if '@' in line: """ If @ in line assume line is end of align input """ eqnarray_flg = False line = line.replace('@','') line += '\\end{align*}\n' else: line+'\n' line = process_equals(line) body += line try: line = i.next() except StopIteration: break else: if '=' in line: #Single line equation line = '\\begin{equation*}\n'+line+'\n\\end{equation*}' else: #Text with no math expression(s)unless \ or _ in line if '\\' in line or '_' in line or '^' in line: line = '\\begin{equation*}\n'+line+'\n\\end{equation*}' else: if len(line) > 0: line += '\\newline \n' line = process_equals(line) body += line try: line = i.next() except StopIteration: break body = LatexPrinter.preamble+body+LatexPrinter.postscript latex_file = open(filename,'w') latex_file.write(body) latex_file.close() latex_str = None xdvi_str = None if find_executable('latex') is not None: latex_str = 'latex' if find_executable('xdvi') is not None: xdvi_str = 'xdvi' if find_executable('yap') is not None: xdvi_str = 'yap' if latex_str is not None and xdvi_str is not None: if debug: #Display latex excution output for debugging purposes os.system(latex_str+' '+filename[:-4]) else: #Works for Linux don't know about Windows if sys.platform.startswith('linux'): os.system(latex_str+' '+filename[:-4]+' > /dev/null') else: os.system(latex_str+' '+filename[:-4]+' > NUL') os.system(xdvi_str+' '+filename[:-4]+' &') LatexPrinter.LaTeX_flg = False return def MV_format(mv_fmt): """ 0 or 1 - Print multivector on one line 2 - Print each multivector grade on one line 3 - Print each multivector base on one line """ if LatexPrinter.LaTeX_flg: LatexPrinter.fmt_dict['mv'] = mv_fmt return def fct_format(fct_fmt): """ 0 - Default sympy latex format 1 - Do not print arguments of arbitrary functions. Use symbol font for arbitrary functions. Use enhanced symbol naming for arbitrary functions. Use new names for standard functions (acos -> Cos^{-1}) """ if LatexPrinter.LaTeX_flg: LatexPrinter.fct = fct_fmt return def pdiff_format(pdiff_fmt): """ 0 - Use default sympy partial derivative format 1 - Contracted derivative format (no fraction symbols) """ if LatexPrinter.LaTeX_flg: LatexPrinter.fmt_dict['pdiff'] = pdiff_fmt return def sym_format(sym_fmt): """ 0 - Use default sympy format 1 - Use extended symbol format including multiple Greek letters in basic symbol (symbol preceding sub and superscripts)and in sub and superscripts of basic symbol and accents in basic symbol """ if LatexPrinter.LaTeX_flg: LatexPrinter.fmt_dict['sym'] = sym_fmt return def str_format(str_fmt): """ 0 - Use default sympy format 1 - Use extended symbol format including multiple Greek letters in basic symbol (symbol preceding sub and superscripts)and in sub and superscripts of basic symbol and accents in basic symbol """ if LatexPrinter.LaTeX_flg: LatexPrinter.fmt_dict['str'] = str_fmt return def ext_str(xstr): return(LatexPrinter.extended_symbol(xstr)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/galgebra/tests/0000755000175000017500000000000012014170666024005 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/galgebra/tests/test_GA.py0000644000175000017500000001710212014170666025706 0ustar georgeskgeorgesk#!/usr/bin/python #test_GA.py """ The reference D&L is "Geometric Algebra for Physicists" by Doran and Lasenby """ import sys try: import numpy disabled = False except ImportError: #py.test will not execute any tests now disabled = True if not disabled: sys.path.append('../') from sympy.galgebra.GA import set_main, MV, make_symbols, types, ZERO, ONE, HALF, S import sympy from sympy import collect, sympify set_main(sys.modules[__name__]) def F(x): """ Conformal Mapping Function from 3D Euclidean space to 5D conformal space where the images of all maps are null vectors. """ Fx = HALF*((x*x)*n+2*x-nbar) #print 'F(x) =',Fx return(Fx) def make_vector(a,n = 3): if type(a) == types.StringType: sym_str = '' for i in range(n): sym_str += a+str(i)+' ' sym_lst = make_symbols(sym_str) sym_lst.append(ZERO) sym_lst.append(ZERO) a = MV(sym_lst,'vector') return(F(a)) def test_rmul(): """ Test for commutative scalar multiplication. Leftover from when sympy and numpy were not working together and __mul__ and __rmul__ would not give the same answer. """ MV.setup('x y z') make_symbols('a b c') assert 5*x == x*5 assert HALF*x == x*HALF assert a*x == x*a def test_contraction(): """ Test for inner product and left and right contraction """ MV.setup('e_1 e_2 e_3','1 0 0, 0 1 0, 0 0 1',offset=1) assert ((e_1^e_3)|e_1) == -e_3 assert ((e_1^e_3)>e_1) == -e_3 assert (e_1|(e_1^e_3)) == e_3 assert (e_1<(e_1^e_3)) == e_3 assert ((e_1^e_3)(e_1^e_3)) == 0 def test_substitution(): MV.setup('e_x e_y e_z','1 0 0, 0 1 0, 0 0 1',offset=1) make_symbols('x y z') X = x*e_x+y*e_y+z*e_z Y = X.subs([(x,2),(y,3),(z,4)]) assert Y == 2*e_x+3*e_y+4*e_z def test_vector_extraction(): """ Show that conformal bivector encodes two points. See D&L Section 10.4.1 """ metric = ' 0 -1 #,'+ \ '-1 0 #,'+ \ ' # # #,' MV.setup('P1 P2 a',metric) """ P1 and P2 are null vectors and hence encode points in conformal space. Show that P1 and P2 can be extracted from the bivector B = P1^P2. a is a third vector in the conformal space with a.B not 0. """ ZERO_MV = MV() B = P1^P2 Bsq = B*B ap = a-(a^B)*B Ap = ap+ap*B Am = ap-ap*B Ap_test = (-2*P2dota)*P1 Am_test = (-2*P1dota)*P2 Ap.compact() Am.compact() Ap_test.compact() Am_test.compact() assert Ap == Ap_test assert Am == Am_test Ap2 = Ap*Ap Am2 = Am*Am Ap2.compact() Am2.compact() assert Ap2 == ZERO_MV assert Am2 == ZERO_MV def test_geometry(): """ Test conformal geometric description of circles, lines, spheres, and planes. """ metric = '1 0 0 0 0,'+ \ '0 1 0 0 0,'+ \ '0 0 1 0 0,'+ \ '0 0 0 0 2,'+ \ '0 0 0 2 0' MV.setup('e0 e1 e2 n nbar',metric,debug=0) e = n+nbar #conformal representation of points ZERO_MV = MV() A = make_vector(e0) # point a = (1,0,0) A = F(a) B = make_vector(e1) # point b = (0,1,0) B = F(b) C = make_vector(-1*e0) # point c = (-1,0,0) C = F(c) D = make_vector(e2) # point d = (0,0,1) D = F(d) X = make_vector('x',3) Circle = A^B^C^X Line = A^B^n^X Sphere = A^B^C^D^X Plane = A^B^n^D^X #Circle through a, b, and c Circle_test = -x2*(e0^e1^e2^n)+x2*(e0^e1^e2^nbar)+HALF*(-1+x0**2+x1**2+x2**2)*(e0^e1^n^nbar) diff = Circle-Circle_test diff.compact() assert diff == ZERO_MV #Line through a and b Line_test = -x2*(e0^e1^e2^n)+HALF*(-1+x0+x1)*(e0^e1^n^nbar)+(HALF*x2)*(e0^e2^n^nbar)+\ (-HALF*x2)*(e1^e2^n^nbar) diff = Line-Line_test diff.compact() assert diff == ZERO_MV #Sphere through a, b, c, and d Sphere_test = HALF*(1-x0**2-x1**2-x2**2)*(e0^e1^e2^n^nbar) diff = Sphere-Sphere_test diff.compact() assert diff == ZERO_MV #Plane through a, b, and d Plane_test = HALF*(1-x0-x1-x2)*(e0^e1^e2^n^nbar) diff = Plane-Plane_test diff.compact() assert diff == ZERO_MV def test_extract_plane_and_line(): """ Show that conformal trivector encodes planes and lines. See D&L section 10.4.2 """ metric = '# # # 0 0,'+ \ '# # # 0 0,'+ \ '# # # 0 0,'+ \ '0 0 0 0 2,'+ \ '0 0 0 2 0' MV.setup('p1 p2 p3 n nbar',metric,debug=0) MV.set_str_format(1) ZERO_MV = MV() P1 = F(p1) P2 = F(p2) P3 = F(p3) #Line through p1 and p2 L = P1^P2^n delta = (L|n)|nbar delta_test = 2*p1-2*p2 diff = delta-delta_test diff.compact() assert diff == ZERO_MV #Plane through p1, p2, and p3 C = P1^P2^P3 delta = ((C^n)|n)|nbar delta_test = 2*(p1^p2)-2*(p1^p3)+2*(p2^p3) diff = delta-delta_test diff.compact() assert diff == ZERO_MV def test_reciprocal_frame(): """ Test of formula for general reciprocal frame of three vectors. Let three independent vectors be e1, e2, and e3. The reciprocal vectors E1, E2, and E3 obey the relations: e_i.E_j = delta_ij*(e1^e2^e3)**2 """ metric = '1 # #,'+ \ '# 1 #,'+ \ '# # 1,' MV.setup('e1 e2 e3',metric) E = e1^e2^e3 Esq = (E*E)() Esq_inv = 1/Esq E1 = (e2^e3)*E E2 = (-1)*(e1^e3)*E E3 = (e1^e2)*E w = (E1|e2) w.collect(MV.g) w = w().expand() w = (E1|e3) w.collect(MV.g) w = w().expand() assert w == 0 w = (E2|e1) w.collect(MV.g) w = w().expand() assert w == 0 w = (E2|e3) w.collect(MV.g) w = w().expand() assert w == 0 w = (E3|e1) w.collect(MV.g) w = w().expand() assert w == 0 w = (E3|e2) w.collect(MV.g) w = w().expand() assert w == 0 w = (E1|e1) w = w().expand() Esq = Esq.expand() assert w/Esq == 1 w = (E2|e2) w = w().expand() assert w/Esq == 1 w = (E3|e3) w = w().expand() assert w/Esq == 1 def test_derivative(): coords = make_symbols('x y z') MV.setup('e','1 0 0, 0 1 0, 0 0 1',coords=coords) X = x*e_x+y*e_y+z*e_z a = MV('a','vector') assert ((X|a).grad()) == a assert ((X*X).grad()) == 2*X assert (X*X*X).grad() == 5*X*X assert X.grad_int() == 3 def test_str(): MV.setup('e_1 e_2 e_3','1 0 0, 0 1 0, 0 0 1') X = MV('x') assert str(X) == 'x+x__0*e_1+x__1*e_2+x__2*e_3+x__01*e_1e_2+x__02*e_1e_3+x__12*e_2e_3+x__012*e_1e_2e_3' Y = MV('y','spinor') assert str(Y) == 'y+y__01*e_1e_2+y__02*e_1e_3+y__12*e_2e_3' Z = X+Y assert str(Z) == 'x+y+x__0*e_1+x__1*e_2+x__2*e_3+(x__01+y__01)*e_1e_2+(x__02+y__02)*e_1e_3+(x__12+y__12)*e_2e_3+x__012*e_1e_2e_3' assert str(e_1|e_1) == '1' def test_metric(): MV.setup('e_1 e_2 e_3','[1,1,1]') assert str(MV.metric) == '[[1 0 0]\n [0 1 0]\n [0 0 1]]' def test_constructor(): """ Test various multivector constructors """ MV.setup('e_1 e_2 e_3','[1,1,1]') make_symbols('x') assert str(S(1)) == '1' assert str(S(x)) == 'x' assert str(MV('a','scalar')) == 'a' assert str(MV('a','vector')) == 'a__0*e_1+a__1*e_2+a__2*e_3' assert str(MV('a','pseudo')) == 'a*e_1e_2e_3' assert str(MV('a','spinor')) == 'a+a__01*e_1e_2+a__02*e_1e_3+a__12*e_2e_3' assert str(MV('a')) == 'a+a__0*e_1+a__1*e_2+a__2*e_3+a__01*e_1e_2+a__02*e_1e_3+a__12*e_2e_3+a__012*e_1e_2e_3' assert str(MV([2,'a'],'grade')) == 'a__01*e_1e_2+a__02*e_1e_3+a__12*e_2e_3' assert str(MV('a','grade2')) == 'a__01*e_1e_2+a__02*e_1e_3+a__12*e_2e_3' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/galgebra/__init__.py0000644000175000017500000000000012014170666024742 0ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/0000755000175000017500000000000012014170666022542 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/mathematica.py0000644000175000017500000000264612014170666025401 0ustar georgeskgeorgeskfrom re import match from sympy import sympify def mathematica (s): return sympify(parse(s)) def parse (s): s = s.strip() #Begin rules rules = ( (r"\A(\w+)\[([^\]]+[^\[]*)\]\Z", #Function call lambda m: translateFunction(m.group(1)) + "(" + parse(m.group(2)) + ")" ), (r"\((.+)\)\((.+)\)", #Parenthesized implied multiplication lambda m: "(" + parse(m.group(1)) + ")*(" + parse(m.group(2)) + ")" ), (r"\A\((.+)\)\Z", #Parenthesized expression lambda m: "(" + parse(m.group(1)) + ")" ), (r"\A(.*[\w\.])\((.+)\)\Z", #Implied multiplication - a(b) lambda m: parse(m.group(1)) + "*(" + parse(m.group(2)) + ")" ), (r"\A\((.+)\)([\w\.].*)\Z", #Implied multiplication - (a)b lambda m: "(" + parse(m.group(1)) + ")*" + parse(m.group(2)) ), (r"\A([\d\.]+)([a-zA-Z].*)\Z", #Implied multiplicatin - 2a lambda m: parse(m.group(1)) + "*" + parse(m.group(2)) ), (r"\A([^=]+)([\^\-\*/\+=]=?)(.+)\Z", #Infix operator lambda m: parse(m.group(1)) + translateOperator(m.group(2)) + parse(m.group(3)) )) #End rules for rule, action in rules: m = match(rule, s) if m: return action(m) return s def translateFunction (s): if s[0:3] == "Arc": return "a" + s[3:] return s.lower() def translateOperator (s): dictionary = {'^':'**'} if s in dictionary: return dictionary[s] return s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/maxima.py0000644000175000017500000000333512014170666024374 0ustar georgeskgeorgeskimport re from sympy import sympify, Sum, product, sin, cos class MaximaHelpers: def maxima_expand(expr): return expr.expand() def maxima_float(expr): return expr.evalf() def maxima_trigexpand(expr): return expr.expand(trig=True) def maxima_sum(a1, a2, a3, a4): return Sum(a1, (a2, a3, a4)).doit() def maxima_product(a1,a2,a3,a4): return product(a1, (a2,a3,a4)) def maxima_csc(expr): return 1/sin(expr) def maxima_sec(expr): return 1/cos(expr) sub_dict = { 'pi' : re.compile('%pi'), 'E' : re.compile('%e'), 'I' : re.compile('%i'), '**': re.compile('\^'), 'oo': re.compile(r'\binf\b'), '-oo': re.compile(r'\bminf\b'), "'-'" : re.compile(r'\bminus\b'), 'maxima_expand' : re.compile(r'\bexpand\b'), 'maxima_float' : re.compile(r'\bfloat\b'), 'maxima_trigexpand' : re.compile(r'\btrigexpand'), 'maxima_sum' : re.compile(r'\bsum\b'), 'maxima_product' : re.compile(r'\bproduct\b'), 'cancel' : re.compile(r'\bratsimp\b'), 'maxima_csc' : re.compile(r'\bcsc\b'), 'maxima_sec' : re.compile(r'\bsec\b') } var_name = re.compile('^\s*(\w+)\s*:') def parse_maxima(str, globals=None, name_dict={}): str = str.strip() str = str.rstrip('; ') for k,v in sub_dict.items(): str = v.sub(k, str) assign_var = None var_match = var_name.search(str) if var_match: assign_var = var_match.group(1) str = str[var_match.end():].strip() dct = MaximaHelpers.__dict__.copy() dct.update(name_dict) obj = sympify(str, locals= dct) if assign_var and globals: globals[assign_var] = obj return obj wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/ast_parser_python25.py0000644000175000017500000000621112014170666027027 0ustar georgeskgeorgeskimport compiler import parser from compiler.transformer import Transformer from compiler.ast import CallFunc, Name, Const from compiler.pycodegen import ExpressionCodeGenerator import re #this is python stdlib symbol, not SymPy symbol: import symbol from sympy.core.basic import Basic from sympy.core.symbol import Symbol from sympy.core.compatibility import callable _is_integer = re.compile(r'\A\d+(l|L)?\Z').match class SymPyTransformer(Transformer): def __init__(self, local_dict, global_dict): Transformer.__init__(self) self.symbol_class = 'Symbol' self.local_dict = local_dict self.global_dict = global_dict def atom_number(self, nodelist): n = Transformer.atom_number(self, nodelist) number, lineno = nodelist[0][1:] if _is_integer(number): n = Const(long(number), lineno) return CallFunc(Name('Integer'), [n]) if number.endswith('j'): n = Const(complex(number), lineno) return CallFunc(Name('sympify'), [n]) n = Const(number, lineno) return CallFunc(Name('Float'), [n]) def atom_name(self, nodelist): name, lineno = nodelist[0][1:] if name in self.local_dict: name_obj = self.local_dict[name] return Const(name_obj, lineno=lineno) elif name in self.global_dict: name_obj = self.global_dict[name] if isinstance(name_obj, (Basic, type)) or callable(name_obj): return Const(name_obj, lineno=lineno) elif name in ['True', 'False']: return Const(eval(name), lineno=lineno) symbol_obj = Symbol(name) self.local_dict[name] = symbol_obj return Const(symbol_obj, lineno=lineno) def lambdef(self, nodelist): if nodelist[2][0] == symbol.varargslist: names, defaults, flags = self.com_arglist(nodelist[2][1:]) else: names = defaults = () flags = 0 lineno = nodelist[1][2] code = self.com_node(nodelist[-1]) assert not defaults,`defaults` # sympy.Lambda does not support optional arguments def convert(x): return CallFunc(Name('sympify'), [Const(x)]) argument = [convert(arg) for arg in names] return CallFunc(Name('Lambda'), [CallFunc(Name('Tuple'), argument), code]) class SymPyParser: def __init__(self, local_dict={}): #Contents of local_dict change, but it has proper effect only in global scope global_dict = {} exec 'from sympy import *' in global_dict self.r_transformer = SymPyTransformer(local_dict, global_dict) self.local_dict = local_dict self.global_dict = global_dict def parse_expr(self, ws_expression): expression = ws_expression.strip() #in case of " x" ast_tree = parser.expr(expression) ast_tree = self.r_transformer.transform(ast_tree) compiler.misc.set_filename('', ast_tree) code = ExpressionCodeGenerator(ast_tree).getCode() parsed_expr = eval(code, self.local_dict, self.global_dict) #Changed order to prefer sympy objects to user defined return parsed_expr wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/sympy_tokenize.py0000644000175000017500000003772312014170666026221 0ustar georgeskgeorgesk"""Tokenization help for Python programs. generate_tokens(readline) is a generator that breaks a stream of text into Python tokens. It accepts a readline-like method which is called repeatedly to get the next line of input (or "" for EOF). It generates 5-tuples with these members: the token type (see token.py) the token (a string) the starting (row, column) indices of the token (a 2-tuple of ints) the ending (row, column) indices of the token (a 2-tuple of ints) the original line (string) It is designed to match the working of the Python tokenizer exactly, except that it produces COMMENT tokens for comments and gives type OP for all operators Older entry points tokenize_loop(readline, tokeneater) tokenize(readline, tokeneater=printtoken) are the same, except instead of generating tokens, tokeneater is a callback function to which the 5 fields described above are passed as 5 arguments, each time a new token is found.""" __author__ = 'Ka-Ping Yee ' __credits__ = \ 'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro, Raymond Hettinger' import string, re from token import * import token __all__ = [x for x in dir(token) if x[0] != '_'] + ["COMMENT", "tokenize", "generate_tokens", "NL", "untokenize"] del token COMMENT = N_TOKENS tok_name[COMMENT] = 'COMMENT' NL = N_TOKENS + 1 tok_name[NL] = 'NL' N_TOKENS += 2 def group(*choices): return '(' + '|'.join(choices) + ')' def any(*choices): return group(*choices) + '*' def maybe(*choices): return group(*choices) + '?' Whitespace = r'[ \f\t]*' Comment = r'#[^\r\n]*' Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) Name = r'[a-zA-Z_]\w*' Hexnumber = r'0[xX][\da-fA-F]+[lL]?' Octnumber = r'(0[oO][0-7]+)|(0[0-7]*)[lL]?' Binnumber = r'0[bB][01]+[lL]?' Decnumber = r'[1-9]\d*[lL]?' Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) Exponent = r'[eE][-+]?\d+' Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent) Repeatedfloat = r'\d*\.\d*\[\d+\]' Expfloat = r'\d+' + Exponent Floatnumber = group(Repeatedfloat, Pointfloat, Expfloat) Imagnumber = group(r'\d+[jJ]', Floatnumber + r'[jJ]') Number = group(Imagnumber, Floatnumber, Intnumber) # Tail end of ' string. Single = r"[^'\\]*(?:\\.[^'\\]*)*'" # Tail end of " string. Double = r'[^"\\]*(?:\\.[^"\\]*)*"' # Tail end of ''' string. Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" # Tail end of """ string. Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' Triple = group("[uU]?[rR]?'''", '[uU]?[rR]?"""') # Single-line ' or " string. String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'", r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"') # Because of leftmost-then-longest match semantics, be sure to put the # longest operators first (e.g., if = came before ==, == would get # recognized as two instances of =). Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=", r"//=?", r"[+\-*/%&|^=<>]=?", r"~") Bracket = '[][(){}]' Special = group(r'\r?\n', r'[:;.,`@]') Funny = group(Operator, Bracket, Special) PlainToken = group(Number, Funny, String, Name) Token = Ignore + PlainToken # First (or only) line of ' or " string. ContStr = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" + group("'", r'\\\r?\n'), r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' + group('"', r'\\\r?\n')) PseudoExtras = group(r'\\\r?\n', Comment, Triple) PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) tokenprog, pseudoprog, single3prog, double3prog = map( re.compile, (Token, PseudoToken, Single3, Double3)) endprogs = {"'": re.compile(Single), '"': re.compile(Double), "'''": single3prog, '"""': double3prog, "r'''": single3prog, 'r"""': double3prog, "u'''": single3prog, 'u"""': double3prog, "ur'''": single3prog, 'ur"""': double3prog, "R'''": single3prog, 'R"""': double3prog, "U'''": single3prog, 'U"""': double3prog, "uR'''": single3prog, 'uR"""': double3prog, "Ur'''": single3prog, 'Ur"""': double3prog, "UR'''": single3prog, 'UR"""': double3prog, "b'''": single3prog, 'b"""': double3prog, "br'''": single3prog, 'br"""': double3prog, "B'''": single3prog, 'B"""': double3prog, "bR'''": single3prog, 'bR"""': double3prog, "Br'''": single3prog, 'Br"""': double3prog, "BR'''": single3prog, 'BR"""': double3prog, 'r': None, 'R': None, 'u': None, 'U': None, 'b': None, 'B': None} triple_quoted = {} for t in ("'''", '"""', "r'''", 'r"""', "R'''", 'R"""', "u'''", 'u"""', "U'''", 'U"""', "ur'''", 'ur"""', "Ur'''", 'Ur"""', "uR'''", 'uR"""', "UR'''", 'UR"""', "b'''", 'b"""', "B'''", 'B"""', "br'''", 'br"""', "Br'''", 'Br"""', "bR'''", 'bR"""', "BR'''", 'BR"""'): triple_quoted[t] = t single_quoted = {} for t in ("'", '"', "r'", 'r"', "R'", 'R"', "u'", 'u"', "U'", 'U"', "ur'", 'ur"', "Ur'", 'Ur"', "uR'", 'uR"', "UR'", 'UR"', "b'", 'b"', "B'", 'B"', "br'", 'br"', "Br'", 'Br"', "bR'", 'bR"', "BR'", 'BR"' ): single_quoted[t] = t tabsize = 8 class TokenError(Exception): pass class StopTokenizing(Exception): pass def printtoken(type, token, srow_scol, erow_ecol, line): # for testing srow, scol = srow_scol erow, ecol = erow_ecol print "%d,%d-%d,%d:\t%s\t%s" % \ (srow, scol, erow, ecol, tok_name[type], repr(token)) def tokenize(readline, tokeneater=printtoken): """ The tokenize() function accepts two parameters: one representing the input stream, and one providing an output mechanism for tokenize(). The first parameter, readline, must be a callable object which provides the same interface as the readline() method of built-in file objects. Each call to the function should return one line of input as a string. The second parameter, tokeneater, must also be a callable object. It is called once for each token, with five arguments, corresponding to the tuples generated by generate_tokens(). """ try: tokenize_loop(readline, tokeneater) except StopTokenizing: pass # backwards compatible interface def tokenize_loop(readline, tokeneater): for token_info in generate_tokens(readline): tokeneater(*token_info) class Untokenizer: def __init__(self): self.tokens = [] self.prev_row = 1 self.prev_col = 0 def add_whitespace(self, start): row, col = start assert row <= self.prev_row col_offset = col - self.prev_col if col_offset: self.tokens.append(" " * col_offset) def untokenize(self, iterable): for t in iterable: if len(t) == 2: self.compat(t, iterable) break tok_type, token, start, end, line = t self.add_whitespace(start) self.tokens.append(token) self.prev_row, self.prev_col = end if tok_type in (NEWLINE, NL): self.prev_row += 1 self.prev_col = 0 return "".join(self.tokens) def compat(self, token, iterable): startline = False indents = [] toks_append = self.tokens.append toknum, tokval = token if toknum in (NAME, NUMBER): tokval += ' ' if toknum in (NEWLINE, NL): startline = True prevstring = False for tok in iterable: toknum, tokval = tok[:2] if toknum in (NAME, NUMBER): tokval += ' ' # Insert a space between two consecutive strings if toknum == STRING: if prevstring: tokval = ' ' + tokval prevstring = True else: prevstring = False if toknum == INDENT: indents.append(tokval) continue elif toknum == DEDENT: indents.pop() continue elif toknum in (NEWLINE, NL): startline = True elif startline and indents: toks_append(indents[-1]) startline = False toks_append(tokval) def untokenize(iterable): """Transform tokens back into Python source code. Each element returned by the iterable must be a token sequence with at least two elements, a token number and token value. If only two tokens are passed, the resulting output is poor. Round-trip invariant for full input: Untokenized source will match input source exactly Round-trip invariant for limited intput: # Output text will tokenize the back to the input t1 = [tok[:2] for tok in generate_tokens(f.readline)] newcode = untokenize(t1) readline = iter(newcode.splitlines(1)).next t2 = [tok[:2] for tok in generate_tokens(readline)] assert t1 == t2 """ ut = Untokenizer() return ut.untokenize(iterable) def generate_tokens(readline): """ The generate_tokens() generator requires one argment, readline, which must be a callable object which provides the same interface as the readline() method of built-in file objects. Each call to the function should return one line of input as a string. Alternately, readline can be a callable function terminating with StopIteration: readline = open(myfile).next # Example of alternate readline The generator produces 5-tuples with these members: the token type; the token string; a 2-tuple (srow, scol) of ints specifying the row and column where the token begins in the source; a 2-tuple (erow, ecol) of ints specifying the row and column where the token ends in the source; and the line on which the token was found. The line passed is the logical line; continuation lines are included. """ lnum = parenlev = continued = 0 namechars, numchars = string.ascii_letters + '_', '0123456789' contstr, needcont = '', 0 contline = None indents = [0] while 1: # loop over lines in stream try: line = readline() except StopIteration: line = '' lnum = lnum + 1 pos, max = 0, len(line) if contstr: # continued string if not line: raise TokenError("EOF in multi-line string", strstart) endmatch = endprog.match(line) if endmatch: pos = end = endmatch.end(0) yield (STRING, contstr + line[:end], strstart, (lnum, end), contline + line) contstr, needcont = '', 0 contline = None elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': yield (ERRORTOKEN, contstr + line, strstart, (lnum, len(line)), contline) contstr = '' contline = None continue else: contstr = contstr + line contline = contline + line continue elif parenlev == 0 and not continued: # new statement if not line: break column = 0 while pos < max: # measure leading whitespace if line[pos] == ' ': column = column + 1 elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize elif line[pos] == '\f': column = 0 else: break pos = pos + 1 if pos == max: break if line[pos] in '#\r\n': # skip comments or blank lines if line[pos] == '#': comment_token = line[pos:].rstrip('\r\n') nl_pos = pos + len(comment_token) yield (COMMENT, comment_token, (lnum, pos), (lnum, pos + len(comment_token)), line) yield (NL, line[nl_pos:], (lnum, nl_pos), (lnum, len(line)), line) else: yield ((NL, COMMENT)[line[pos] == '#'], line[pos:], (lnum, pos), (lnum, len(line)), line) continue if column > indents[-1]: # count indents or dedents indents.append(column) yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line) while column < indents[-1]: if column not in indents: raise IndentationError( "unindent does not match any outer indentation level", ("", lnum, pos, line)) indents = indents[:-1] yield (DEDENT, '', (lnum, pos), (lnum, pos), line) else: # continued statement if not line: raise TokenError("EOF in multi-line statement", (lnum, 0)) continued = 0 while pos < max: pseudomatch = pseudoprog.match(line, pos) if pseudomatch: # scan for tokens start, end = pseudomatch.span(1) spos, epos, pos = (lnum, start), (lnum, end), end token, initial = line[start:end], line[start] if initial in numchars or \ (initial == '.' and token != '.'): # ordinary number yield (NUMBER, token, spos, epos, line) elif initial in '\r\n': yield (NL if parenlev > 0 else NEWLINE, token, spos, epos, line) elif initial == '#': assert not token.endswith("\n") yield (COMMENT, token, spos, epos, line) elif token in triple_quoted: endprog = endprogs[token] endmatch = endprog.match(line, pos) if endmatch: # all on one line pos = endmatch.end(0) token = line[start:pos] yield (STRING, token, spos, (lnum, pos), line) else: strstart = (lnum, start) # multiple lines contstr = line[start:] contline = line break elif initial in single_quoted or \ token[:2] in single_quoted or \ token[:3] in single_quoted: if token[-1] == '\n': # continued string strstart = (lnum, start) endprog = (endprogs[initial] or endprogs[token[1]] or endprogs[token[2]]) contstr, needcont = line[start:], 1 contline = line break else: # ordinary string yield (STRING, token, spos, epos, line) elif initial in namechars: # ordinary name yield (NAME, token, spos, epos, line) elif initial == '\\': # continued stmt continued = 1 else: if initial in '([{': parenlev = parenlev + 1 elif initial in ')]}': parenlev = parenlev - 1 yield (OP, token, spos, epos, line) else: yield (ERRORTOKEN, line[pos], (lnum, pos), (lnum, pos+1), line) pos = pos + 1 for indent in indents[1:]: # pop remaining indent levels yield (DEDENT, '', (lnum, 0), (lnum, 0), '') yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '') if __name__ == '__main__': # testing import sys if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline) else: tokenize(sys.stdin.readline) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/ast_parser.py0000644000175000017500000000651412014170666025265 0ustar georgeskgeorgesk""" This module implements the functionality to take any Python expression as a string and fix all numbers and other things before evaluating it, thus 1/2 returns Integer(1)/Integer(2) We use the Python ast module for that, which is in python2.6 and later. It is well documented at docs.python.org. Some tips to understand how this works: use dump() to get a nice representation of any node. Then write a string of what you want to get, e.g. "Integer(1)", parse it, dump it and you'll see that you need to do "Call(Name('Integer', Load()), [node], [], None, None)". You don't need to bother with lineno and col_offset, just call fix_missing_locations() before returning the node. If the ast module is not available (Python 2.5), we use the old compiler module. """ from sympy.core.basic import Basic from sympy.core.sympify import SympifyError try: from ast import parse, NodeTransformer, Call, Name, Load, \ fix_missing_locations, Str, Tuple ast_enabled = True except ImportError: ast_enabled = False if ast_enabled: class Transform(NodeTransformer): def __init__(self, local_dict, global_dict): NodeTransformer.__init__(self) self.local_dict = local_dict self.global_dict = global_dict def visit_Num(self, node): if isinstance(node.n, int): return fix_missing_locations(Call(Name('Integer', Load()), [node], [], None, None)) elif isinstance(node.n, float): return fix_missing_locations(Call(Name('Float', Load()), [node], [], None, None)) return node def visit_Name(self, node): if node.id in self.local_dict: return node elif node.id in self.global_dict: name_obj = self.global_dict[node.id] if isinstance(name_obj, (Basic, type)) or callable(name_obj): return node elif node.id in ['True', 'False']: return node return fix_missing_locations(Call(Name('Symbol', Load()), [Str(node.id)], [], None, None)) def visit_Lambda(self, node): args = [self.visit(arg) for arg in node.args.args] body = self.visit(node.body) n = Call(Name('Lambda', Load()), [Tuple(args, Load()), body], [], None, None) return fix_missing_locations(n) def parse_expr(s, local_dict): """ Converts the string "s" to a SymPy expression, in local_dict. It converts all numbers to Integers before feeding it to Python and automatically creates Symbols. """ if ast_enabled: global_dict = {} exec 'from sympy import *' in global_dict try: a = parse(s.strip(), mode="eval") except SyntaxError: raise SympifyError("Cannot parse %s." %repr(s)) a = Transform(local_dict, global_dict).visit(a) e = compile(a, "", "eval") return eval(e, global_dict, local_dict) else: # in Python 2.5, the "ast" module is not available, so we need # to use our old implementation: from ast_parser_python25 import SymPyParser try: return SymPyParser(local_dict=local_dict).parse_expr(s) except SyntaxError: raise SympifyError("Cannot parse %s." %repr(s)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/sympy_parser.py0000644000175000017500000000730512014170666025656 0ustar georgeskgeorgesk"""Transform a string with Python-like source code into SymPy expression. """ from sympy_tokenize import \ generate_tokens, untokenize, TokenError, NUMBER, STRING, NAME, OP from keyword import iskeyword from StringIO import StringIO import re from sympy.core.basic import Basic _re_repeated = re.compile(r"^(\d*)\.(\d*)\[(\d+)\]$") def _transform(s, local_dict, global_dict, rationalize, convert_xor): g = generate_tokens(StringIO(s).readline) result = [] for toknum, tokval, _, _, _ in g: if toknum == NUMBER: number = tokval postfix = [] if number.endswith('j') or number.endswith('J'): number = number[:-1] postfix = [(OP, '*'), (NAME, 'I')] if '.' in number or (('e' in number or 'E' in number) and not (number.startswith('0x') or number.startswith('0X'))): match = _re_repeated.match(number) if match is not None: # Clear repeating decimals, e.g. 3.4[31] -> (3 + 4/10 + 31/990) pre, post, repetend = match.groups() zeros = '0'*len(post) repetends = repetend.lstrip('0') a = pre or '0' b, c = post or '0', '1' + zeros d, e = repetends, ('9'*len(repetend)) + zeros seq = [ (OP, '('), (NAME, 'Integer'), (OP, '('), (NUMBER, a), (OP, ')'), (OP, '+'), (NAME, 'Rational'), (OP, '('), (NUMBER, b), (OP, ','), (NUMBER, c), (OP, ')'), (OP, '+'), (NAME, 'Rational'), (OP, '('), (NUMBER, d), (OP, ','), (NUMBER, e), (OP, ')'), (OP, ')'), ] elif rationalize: seq = [(NAME, 'Rational'), (OP, '('), (STRING, repr(str(number))), (OP, ')')] else: seq = [(NAME, 'Float'), (OP, '('), (NUMBER, repr(str(number))), (OP, ')')] else: seq = [(NAME, 'Integer'), (OP, '('), (NUMBER, number), (OP, ')')] result.extend(seq + postfix) elif toknum == NAME: name = tokval if name in ['True', 'False', 'None'] or iskeyword(name) or name in local_dict: result.append((NAME, name)) continue elif name in global_dict: obj = global_dict[name] if isinstance(obj, (Basic, type)) or callable(obj): result.append((NAME, name)) continue result.extend([ (NAME, 'Symbol'), (OP, '('), (NAME, repr(str(name))), (OP, ')'), ]) elif toknum == OP: op = tokval if op == '^' and convert_xor: result.append((OP, '**')) else: result.append((OP, op)) else: result.append((toknum, tokval)) return untokenize(result) def parse_expr(s, local_dict=None, rationalize=False, convert_xor=False): """ Converts the string ``s`` to a SymPy expression, in ``local_dict`` **Examples** >>> from sympy.parsing.sympy_parser import parse_expr >>> parse_expr("1/2") 1/2 >>> type(_) """ if local_dict is None: local_dict = {} global_dict = {} exec 'from sympy import *' in global_dict code = _transform(s.strip(), local_dict, global_dict, rationalize, convert_xor) expr = eval(code, global_dict, local_dict) # take local objects in preference return expr wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/tests/0000755000175000017500000000000012014170666023704 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/tests/test_maxima.py0000644000175000017500000000323612014170666026575 0ustar georgeskgeorgeskfrom sympy.parsing.maxima import parse_maxima from sympy import Rational, Abs, Symbol, sin, cos, E, oo, log, factorial from sympy.abc import x n = Symbol('n', integer=True) def test_parser(): assert Abs(parse_maxima('float(1/3)') - 0.333333333) < 10**(-5) assert parse_maxima('13^26') == 91733330193268616658399616009 assert parse_maxima('sin(%pi/2) + cos(%pi/3)') == Rational(3,2) assert parse_maxima('log(%e)') == 1 def test_injection(): parse_maxima('c: x+1', globals=globals()) assert c==x+1 parse_maxima('g: sqrt(81)', globals=globals()) assert g==9 def test_maxima_functions(): assert parse_maxima('expand( (x+1)^2)') == x**2 + 2*x + 1 assert parse_maxima('factor( x**2 + 2*x + 1)') == (x+1)**2 assert parse_maxima('trigsimp(2*cos(x)^2 + sin(x)^2)') == 2 - sin(x)**2 assert parse_maxima('trigexpand(sin(2*x)+cos(2*x))') == (-1) + 2*cos(x)**2 + 2*cos(x)*sin(x) assert parse_maxima('solve(x^2-4,x)') == [2, -2] assert parse_maxima('limit((1+1/x)^x,x,inf)') == E assert parse_maxima('limit(sqrt(-x)/x,x,0,minus)') == -oo assert parse_maxima('diff(x^x, x)') == x**x*(1 + log(x)) assert parse_maxima('sum(k, k, 1, n)', name_dict=dict( n=Symbol('n',integer=True), k=Symbol('k',integer=True) )) == (n**2 +n)/2 assert parse_maxima('product(k, k, 1, n)', name_dict=dict( n=Symbol('n',integer=True), k=Symbol('k',integer=True) ) ) == factorial(n) assert parse_maxima('ratsimp((x^2-1)/(x+1))') == x-1 assert Abs( parse_maxima('float(sec(%pi/3) + csc(%pi/3))') - 3.154700538379252) < 10**(-5) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/tests/test_mathematica.py0000644000175000017500000000111012014170666027563 0ustar georgeskgeorgeskfrom sympy.parsing.mathematica import mathematica from sympy import sympify def test_mathematica(): d = {'Sin[x]^2':'sin(x)**2', '2(x-1)':'2*(x-1)', '3y+8':'3*y+8', 'Arcsin[2x+9(4-x)^2]/x':'asin(2*x+9*(4-x)**2)/x', 'x+y':'x+y', '355/113':'355/113', '2.718281828':'2.718281828', 'Sin[12]':'sin(12)', 'Exp[Log[4]]':'exp(log(4))', '(x+1)(x+3)':'(x+1)*(x+3)', 'Cos[Arccos[3.6]]':'cos(acos(3.6))', 'Cos[x]==Sin[y]':'cos(x)==sin(y)'} for e in d: assert mathematica(e) == sympify(d[e]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/parsing/__init__.py0000644000175000017500000000007612014170666024656 0ustar georgeskgeorgesk"""Used for translating a string into a SymPy expression. """ wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/0000755000175000017500000000000012014170666022737 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/color_scheme.py0000644000175000017500000002723212014170666025761 0ustar georgeskgeorgeskfrom sympy import Basic, Symbol, symbols, lambdify from util import interpolate, rinterpolate, create_bounds, update_bounds from sympy.core.compatibility import callable class ColorGradient(object): colors = [0.4,0.4,0.4], [0.9,0.9,0.9] intervals = 0.0, 1.0 def __init__(self, *args): if len(args) == 2: self.colors = list(args) self.intervals = [0.0, 1.0] elif len(args) > 0: assert len(args) % 2 == 0 self.colors = [args[i] for i in xrange(1, len(args), 2)] self.intervals = [args[i] for i in xrange(0, len(args), 2)] assert len(self.colors) == len(self.intervals) def copy(self): c = ColorGradient() c.colors = [e[::] for e in self.colors] c.intervals = self.intervals[::] return c def _find_interval(self, v): m = len(self.intervals) i = 0 while i < m-1 and self.intervals[i] <= v: i+=1 return i def _interpolate_axis(self, axis, v): i = self._find_interval(v) v = rinterpolate(self.intervals[i-1], self.intervals[i], v) return interpolate(self.colors[i-1][axis], self.colors[i][axis], v) def __call__(self, r, g, b): c = self._interpolate_axis return c(0, r), c(1, g), c(2, b) default_color_schemes = {} # defined at the bottom of this file class ColorScheme(object): def __init__(self, *args, **kwargs): self.args = args self.f, self.gradient = None, ColorGradient() if len(args) == 1 and not isinstance(args[0],Basic) and callable(args[0]): self.f = args[0] elif len(args) == 1 and isinstance(args[0], str): if args[0] in default_color_schemes: cs = default_color_schemes[args[0]] self.f, self.gradient = cs.f, cs.gradient.copy() else: self.f = lambdify('x,y,z,u,v', args[0]) else: self.f, self.gradient = self._interpret_args(args, kwargs) self._test_color_function() if not isinstance(self.gradient, ColorGradient): raise ValueError("Color gradient not properly initialized. " "(Not a ColorGradient instance.)") def _interpret_args(self, args, kwargs): f, gradient = None, self.gradient atoms, lists = self._sort_args(args) s = self._pop_symbol_list(lists) s = self._fill_in_vars(s) # prepare the error message for lambdification failure f_str = ', '.join(str(fa) for fa in atoms) s_str = (str(sa) for sa in s) s_str = ', '.join(sa for sa in s_str if sa.find('unbound') < 0) f_error = ValueError("Could not interpret arguments " "%s as functions of %s." % (f_str, s_str)) # try to lambdify args if len(atoms) == 1: fv = atoms[0] try: f = lambdify(s, [fv,fv,fv]) except: raise f_error elif len(atoms) == 3: fr, fg, fb = atoms try: f = lambdify(s, [fr,fg,fb]) except: raise f_error else: raise ValueError("A ColorScheme must provide 1 or 3 " "functions in x, y, z, u, and/or v.") # try to intrepret any given color information if len(lists) == 0: gargs = [] elif len(lists) == 1: gargs = lists[0] elif len(lists) == 2: try: (r1,g1,b1), (r2,g2,b2) = lists except: raise ValueError("If two color arguments are given, " "they must be given in the format " "(r1,g1,b1), (r2,g2,b2).") gargs = lists elif len(lists) == 3: try: (r1,r2), (g1,g2), (b1,b2) = lists except: raise ValueError("If three color arguments are given, " "they must be given in the format " "(r1,r2), (g1,g2), (b1,b2). To create " "a multi-step gradient, use the syntax " "[0, colorStart, step1, color1, ..., 1, " "colorEnd].") gargs = [[r1,g1,b1], [r2,g2,b2]] else: raise ValueError("Don't know what to do with collection " "arguments %s." % (', '.join(str(l) for l in lists))) if gargs: try: gradient = ColorGradient(*gargs) except Exception, ex: raise ValueError(("Could not initialize a gradient " "with arguments %s. Inner " "exception: %s") % (gargs, str(ex))) return f, gradient def _pop_symbol_list(self, lists): symbol_lists = [] for l in lists: mark = True for s in l: if s is not None and not isinstance(s, Symbol): mark = False break if mark: lists.remove(l) symbol_lists.append(l) if len(symbol_lists) == 1: return symbol_lists[0] elif len(symbol_lists) == 0: return [] else: raise ValueError("Only one list of Symbols " "can be given for a color scheme.") def _fill_in_vars(self, args): defaults = symbols('x,y,z,u,v') if len(args) == 0: return defaults if not isinstance(args, (tuple, list)): raise v_error if len(args) == 0: return defaults for s in args: if s is not None and not isinstance(s, Symbol): raise v_error # when vars are given explicitly, any vars # not given are marked 'unbound' as to not # be accidentally used in an expression vars = [Symbol('unbound%i'%(i)) for i in xrange(1,6)] # interpret as t if len(args) == 1: vars[3] = args[0] # interpret as u,v elif len(args) == 2: if args[0] is not None: vars[3] = args[0] if args[1] is not None: vars[4] = args[1] # interpret as x,y,z elif len(args) >= 3: # allow some of x,y,z to be # left unbound if not given if args[0] is not None: vars[0] = args[0] if args[1] is not None: vars[1] = args[1] if args[2] is not None: vars[2] = args[2] # interpret the rest as t if len(args) >= 4: vars[3] = args[3] # ...or u,v if len(args) >= 5: vars[4] = args[4] return vars def _sort_args(self, args): atoms, lists = [], [] for a in args: if isinstance(a, (tuple, list)): lists.append(a) else: atoms.append(a) return atoms, lists def _test_color_function(self): if not callable(self.f): raise ValueError("Color function is not callable.") try: result = self.f(0,0,0,0,0) assert len(result) == 3 except TypeError, te: raise ValueError("Color function needs to accept x,y,z,u,v, " "as arguments even if it doesn't use all of them.") except AssertionError, ae: raise ValueError("Color function needs to return 3-tuple r,g,b.") except Exception, ie: pass # color function probably not valid at 0,0,0,0,0 def __call__(self, x,y,z,u,v): try: return self.f(x,y,z,u,v) except Exception, e: #print e return None def apply_to_curve(self, verts, u_set, set_len=None, inc_pos=None): """ Apply this color scheme to a set of vertices over a single independent variable u. """ bounds = create_bounds() cverts = list() if callable(set_len): set_len(len(u_set)*2) # calculate f() = r,g,b for each vert # and find the min and max for r,g,b for _u in xrange(len(u_set)): if verts[_u] is None: cverts.append(None) else: x,y,z = verts[_u] u,v = u_set[_u], None c = self(x,y,z,u,v) if c is not None: c = list(c) update_bounds(bounds, c) cverts.append(c) if callable(inc_pos): inc_pos() # scale and apply gradient for _u in xrange(len(u_set)): if cverts[_u] is not None: for _c in xrange(3): # scale from [f_min, f_max] to [0,1] cverts[_u][_c] = rinterpolate(bounds[_c][0], bounds[_c][1], cverts[_u][_c]) # apply gradient cverts[_u] = self.gradient(*cverts[_u]) if callable(inc_pos): inc_pos() return cverts def apply_to_surface(self, verts, u_set, v_set, set_len=None, inc_pos=None): """ Apply this color scheme to a set of vertices over two independent variables u and v. """ bounds = create_bounds() cverts = list() if callable(set_len): set_len(len(u_set)*len(v_set)*2) # calculate f() = r,g,b for each vert # and find the min and max for r,g,b for _u in xrange(len(u_set)): column = list() for _v in xrange(len(v_set)): if verts[_u][_v] is None: column.append(None) else: x,y,z = verts[_u][_v] u,v = u_set[_u], v_set[_v] c = self(x,y,z,u,v) if c is not None: c = list(c) update_bounds(bounds, c) column.append(c) if callable(inc_pos): inc_pos() cverts.append(column) # scale and apply gradient for _u in xrange(len(u_set)): for _v in xrange(len(v_set)): if cverts[_u][_v] is not None: # scale from [f_min, f_max] to [0,1] for _c in xrange(3): cverts[_u][_v][_c] = rinterpolate(bounds[_c][0], bounds[_c][1], cverts[_u][_v][_c]) # apply gradient cverts[_u][_v] = self.gradient(*cverts[_u][_v]) if callable(inc_pos): inc_pos() return cverts def str_base(self): return ", ".join(str(a) for a in self.args) def __repr__(self): return "%s" % (self.str_base()) x,y,z,t,u,v = symbols('x,y,z,t,u,v') default_color_schemes['rainbow'] = ColorScheme( z, y, x ) default_color_schemes['zfade'] = ColorScheme( z, (0.4,0.4,0.97), (0.97,0.4,0.4), (None, None, z) ) default_color_schemes['zfade3'] = ColorScheme( z, (None, None, z), [ 0.00, (0.2,0.2,1.0), 0.35, (0.2,0.8,0.4), 0.50, (0.3,0.9,0.3), 0.65, (0.4,0.8,0.2), 1.00, (1.0,0.2,0.2) ] ) default_color_schemes['zfade4'] = ColorScheme( z, (None, None, z), [ 0.0, (0.3, 0.3, 1.0), 0.30, (0.3, 1.0, 0.3), 0.55, (0.95,1.0, 0.2), 0.65, (1.0,0.95, 0.2), 0.85, (1.0, 0.7, 0.2), 1.0, (1.0, 0.3, 0.2) ] ) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_mode_base.py0000644000175000017500000002573012014170666026274 0ustar georgeskgeorgeskfrom pyglet.gl import * from plot_mode import PlotMode from threading import Thread, Event, RLock from color_scheme import ColorScheme from sympy.core import S from sympy.core.compatibility import is_sequence from time import sleep from sympy.core.compatibility import callable class PlotModeBase(PlotMode): """ Intended parent class for plotting modes. Provides base functionality in conjunction with its parent, PlotMode. """ ## ## Class-Level Attributes ## """ The following attributes are meant to be set at the class level, and serve as parameters to the plot mode registry (in PlotMode). See plot_modes.py for concrete examples. """ """ i_vars 'x' for Cartesian2D 'xy' for Cartesian3D etc. d_vars 'y' for Cartesian2D 'r' for Polar etc. """ i_vars, d_vars = '', '' """ intervals Default intervals for each i_var, and in the same order. Specified [min, max, steps]. No variable can be given (it is bound later). """ intervals = [] """ aliases A list of strings which can be used to access this mode. 'cartesian' for Cartesian2D and Cartesian3D 'polar' for Polar 'cylindrical', 'polar' for Cylindrical Note that _init_mode chooses the first alias in the list as the mode's primary_alias, which will be displayed to the end user in certain contexts. """ aliases = [] """ is_default Whether to set this mode as the default for arguments passed to PlotMode() containing the same number of d_vars as this mode and at most the same number of i_vars. """ is_default = False """ All of the above attributes are defined in PlotMode. The following ones are specific to PlotModeBase. """ """ A list of the render styles. Do not modify. """ styles = {'wireframe':1, 'solid':2, 'both':3} """ style_override Always use this style if not blank. """ style_override = '' """ default_wireframe_color default_solid_color Can be used when color is None or being calculated. Used by PlotCurve and PlotSurface, but not anywhere in PlotModeBase. """ default_wireframe_color = (0.85,0.85,0.85) default_solid_color = (0.6,0.6,0.9) default_rot_preset = 'xy' ## ## Instance-Level Attributes ## ## 'Abstract' member functions def _get_evaluator(self): if self.use_lambda_eval: try: e = self._get_lambda_evaluator() return e except: print ("\nWarning: creating lambda evaluator failed. " "Falling back on sympy subs evaluator.") return self._get_sympy_evaluator() def _get_sympy_evaluator(self): raise NotImplementedError() def _get_lambda_evaluator(self): raise NotImplementedError() def _on_calculate_verts(self): raise NotImplementedError() def _on_calculate_cverts(self): raise NotImplementedError() ## Base member functions def __init__(self, *args, **kwargs): self.verts = [] self.cverts = [] self.bounds = [ [S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0] ] self.cbounds = [ [S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0] ] self._draw_lock = RLock() self._calculating_verts = Event() self._calculating_cverts = Event() self._calculating_verts_pos = 0.0 self._calculating_verts_len = 0.0 self._calculating_cverts_pos = 0.0 self._calculating_cverts_len = 0.0 self._max_render_stack_size = 3 self._draw_wireframe = [-1] self._draw_solid = [-1] self._style = None self._color = None self.predraw = [] self.postdraw = [] self.use_lambda_eval = self.options.pop('use_sympy_eval', None) is None self.style = self.options.pop('style', '') self.color = self.options.pop('color', 'rainbow') self.bounds_callback = kwargs.pop('bounds_callback', None) self._on_calculate() def synchronized(f): def w(self, *args, **kwargs): self._draw_lock.acquire() try: r = f(self, *args, **kwargs) return r finally: self._draw_lock.release() return w @synchronized def push_wireframe(self, function): """ Push a function which performs gl commands used to build a display list. (The list is built outside of the function) """ assert callable(function) self._draw_wireframe.append(function) if len(self._draw_wireframe) > self._max_render_stack_size: del self._draw_wireframe[1] # leave marker element @synchronized def push_solid(self, function): """ Push a function which performs gl commands used to build a display list. (The list is built outside of the function) """ assert callable(function) self._draw_solid.append(function) if len(self._draw_solid) > self._max_render_stack_size: del self._draw_solid[1] # leave marker element def _create_display_list(self, function): dl = glGenLists(1) glNewList(dl, GL_COMPILE) function() glEndList() return dl def _render_stack_top(self, render_stack): top = render_stack[-1] if top == -1: return -1 # nothing to display elif callable(top): dl = self._create_display_list(top) render_stack[-1] = (dl, top) return dl # display newly added list elif len(top) == 2: if GL_TRUE == glIsList(top[0]): return top[0] # display stored list dl = self._create_display_list(top[1]) render_stack[-1] = (dl, top[1]) return dl # display regenerated list def _draw_solid_display_list(self, dl): glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) glCallList(dl) glPopAttrib() def _draw_wireframe_display_list(self, dl): glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glEnable(GL_POLYGON_OFFSET_LINE) glPolygonOffset(-0.005, -50.0) glCallList(dl) glPopAttrib() @synchronized def draw(self): for f in self.predraw: if callable(f): f() if self.style_override: style = self.styles[self.style_override] else: style = self.styles[self._style] # Draw solid component if style includes solid if style & 2: dl = self._render_stack_top(self._draw_solid) if dl > 0 and GL_TRUE == glIsList(dl): self._draw_solid_display_list(dl) # Draw wireframe component if style includes wireframe if style & 1: dl = self._render_stack_top(self._draw_wireframe) if dl > 0 and GL_TRUE == glIsList(dl): self._draw_wireframe_display_list(dl) for f in self.postdraw: if callable(f): f() def _on_change_color(self, color): Thread(target=self._calculate_cverts).start() def _on_calculate(self): Thread(target=self._calculate_all).start() def _calculate_all(self): self._calculate_verts() self._calculate_cverts() def _calculate_verts(self): if self._calculating_verts.isSet(): return self._calculating_verts.set() try: self._on_calculate_verts() finally: self._calculating_verts.clear() if callable(self.bounds_callback): self.bounds_callback() def _calculate_cverts(self): if self._calculating_verts.isSet(): return while self._calculating_cverts.isSet(): sleep(0) # wait for previous calculation self._calculating_cverts.set() try: self._on_calculate_cverts() finally: self._calculating_cverts.clear() def _get_calculating_verts(self): return self._calculating_verts.isSet() def _get_calculating_verts_pos(self): return self._calculating_verts_pos def _get_calculating_verts_len(self): return self._calculating_verts_len def _get_calculating_cverts(self): return self._calculating_cverts.isSet() def _get_calculating_cverts_pos(self): return self._calculating_cverts_pos def _get_calculating_cverts_len(self): return self._calculating_cverts_len ## Property handlers def _get_style(self): return self._style @synchronized def _set_style(self, v): if v is None: return if v is '': step_max = 0 for i in self.intervals: if i.v_steps is None: continue step_max = max([step_max, i.v_steps]) v = ['both', 'solid'][step_max > 40] #try: assert v in self.styles if v == self._style: return self._style = v #except Exception, e: #raise RuntimeError(("Style change failed. " #"Reason: %s is not a valid " #"style. Use one of %s.") % #(str(v), ', '.join(self.styles.iterkeys()))) def _get_color(self): return self._color @synchronized def _set_color(self, v): try: if v is not None: if is_sequence(v): v = ColorScheme(*v) else: v = ColorScheme(v) if repr(v) == repr(self._color): return self._on_change_color(v) self._color = v except Exception, e: raise RuntimeError(("Color change failed. " "Reason: %s" % (str(e)))) style = property(_get_style, _set_style) color = property(_get_color, _set_color) calculating_verts = property(_get_calculating_verts) calculating_verts_pos = property(_get_calculating_verts_pos) calculating_verts_len = property(_get_calculating_verts_len) calculating_cverts = property(_get_calculating_cverts) calculating_cverts_pos = property(_get_calculating_cverts_pos) calculating_cverts_len = property(_get_calculating_cverts_len) ## String representations def __str__(self): f = ", ".join(str(d) for d in self.d_vars) o = "'mode=%s'" % (self.primary_alias) return ", ".join([f, o]) def __repr__(self): f = ", ".join(str(d) for d in self.d_vars) i = ", ".join(str(i) for i in self.intervals) d = [ ( 'mode', self.primary_alias ), ( 'color', str(self.color) ), ( 'style', str(self.style) ) ] o = "'%s'" % (("; ".join("%s=%s" % (k,v) for k,v in d if v != 'None'))) return ", ".join([f, i, o]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_curve.py0000644000175000017500000000453112014170666025476 0ustar georgeskgeorgeskfrom pyglet.gl import * from plot_mode_base import PlotModeBase from sympy.core import S class PlotCurve(PlotModeBase): style_override = 'wireframe' def _on_calculate_verts(self): self.t_interval = self.intervals[0] self.t_set = list(self.t_interval.frange()) self.bounds = [ [S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0] ] evaluate = self._get_evaluator() self._calculating_verts_pos = 0.0 self._calculating_verts_len = float(self.t_interval.v_len) self.verts = list() b = self.bounds for t in self.t_set: try: _e = evaluate(t) # calculate vertex except: _e = None if _e is not None: # update bounding box for axis in xrange(3): b[axis][0] = min([b[axis][0], _e[axis]]) b[axis][1] = max([b[axis][1], _e[axis]]) self.verts.append(_e) self._calculating_verts_pos += 1.0 for axis in xrange(3): b[axis][2] = b[axis][1] - b[axis][0] if b[axis][2] == 0.0: b[axis][2] = 1.0 self.push_wireframe(self.draw_verts(False)) def _on_calculate_cverts(self): if not self.verts or not self.color: return def set_work_len(n): self._calculating_cverts_len = float(n) def inc_work_pos(): self._calculating_cverts_pos += 1.0 set_work_len(1); self._calculating_cverts_pos = 0 self.cverts = self.color.apply_to_curve(self.verts, self.t_set, set_len=set_work_len, inc_pos=inc_work_pos) self.push_wireframe(self.draw_verts(True)) def calculate_one_cvert(self, t): vert = self.verts[t] return self.color(vert[0], vert[1], vert[2], self.t_set[t], None) def draw_verts(self, use_cverts): def f(): glBegin(GL_LINE_STRIP) for t in xrange( len(self.t_set) ): p = self.verts[t] if p is None: glEnd() glBegin(GL_LINE_STRIP) continue if use_cverts: c = self.cverts[t] if c is None: c = (0, 0, 0) glColor3f(*c) else: glColor3f(*self.default_wireframe_color) glVertex3f(*p) glEnd() return f wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_axes.py0000644000175000017500000002006312014170666025310 0ustar georgeskgeorgeskfrom pyglet.gl import * from pyglet import font from plot_object import PlotObject from util import strided_range, billboard_matrix from util import get_direction_vectors from util import dot_product, vec_sub, vec_mag from sympy.core import S from sympy.core.compatibility import is_sequence class PlotAxes(PlotObject): def __init__(self, *args, **kwargs): # initialize style parameter style = kwargs.pop('style', '').lower() # allow alias kwargs to override style kwarg if kwargs.pop('none', None) is not None: style = 'none' if kwargs.pop('frame', None) is not None: style = 'frame' if kwargs.pop('box', None) is not None: style = 'box' if kwargs.pop('ordinate', None) is not None: style = 'ordinate' if style in ['', 'ordinate']: self._render_object = PlotAxesOrdinate(self) elif style in ['frame', 'box']: self._render_object = PlotAxesFrame(self) elif style in ['none']: self._render_object = None else: raise ValueError(("Unrecognized axes " "style %s.") % (style)) # initialize stride parameter stride = kwargs.pop('stride', 0.25) try: stride = eval(stride) except: pass if is_sequence(stride): assert len(stride) == 3 self._stride = stride else: self._stride = [stride, stride, stride] self._tick_length = float(kwargs.pop('tick_length', 0.1)) # setup bounding box and ticks self._origin = [0,0,0] self.reset_bounding_box() def flexible_boolean(input, default): if input in [True, False]: return input if input in ['f','F','false','False']: return False if input in ['t','T','true','True']: return True return default # initialize remaining parameters self.visible = flexible_boolean(kwargs.pop('visible',''), True) self._overlay = flexible_boolean(kwargs.pop('overlay',''), True) self._colored = flexible_boolean(kwargs.pop('colored',''), False) self._label_axes = flexible_boolean(kwargs.pop('label_axes', ''), False) self._label_ticks = flexible_boolean(kwargs.pop('label_ticks', ''), True) # setup label font self.font_face = kwargs.pop('font_face', 'Arial') self.font_size = kwargs.pop('font_size', 28) # this is also used to reinit the # font on window close/reopen self.reset_resources() def reset_resources(self): self.label_font = None def reset_bounding_box(self): self._bounding_box = [[None,None], [None,None], [None,None]] self._axis_ticks = [[],[],[]] def draw(self): if self._render_object: glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT) if self._overlay: glDisable(GL_DEPTH_TEST) self._render_object.draw() glPopAttrib() def adjust_bounds(self, child_bounds): b = self._bounding_box c = child_bounds for i in [0,1,2]: if abs(c[i][0]) is S.Infinity or abs(c[i][1]) is S.Infinity: continue b[i][0] = [ min([b[i][0], c[i][0]]), c[i][0] ][ b[i][0] is None ] b[i][1] = [ max([b[i][1], c[i][1]]), c[i][1] ][ b[i][1] is None ] self._recalculate_axis_ticks(i) def _recalculate_axis_ticks(self, axis): b = self._bounding_box if b[axis][0] is None or b[axis][1] is None: self._axis_ticks[axis] = [] else: self._axis_ticks[axis] = strided_range(b[axis][0], b[axis][1], self._stride[axis]) def toggle_visible(self): self.visible = not self.visible def toggle_colors(self): self._colored = not self._colored class PlotAxesBase(PlotObject): def __init__(self, parent_axes): self._p = parent_axes def draw(self): color = [ ([0.2,0.1,0.3], [0.2,0.1,0.3], [0.2,0.1,0.3]), ([0.9,0.3,0.5], [0.5,1.0,0.5], [0.3,0.3,0.9]) ][ self._p._colored ] self.draw_background(color) self.draw_axis(2, color[2]) self.draw_axis(1, color[1]) self.draw_axis(0, color[0]) def draw_background(self, color): pass # optional def draw_axis(self, axis, color): raise NotImplementedError() def draw_text(self, text, position, color, scale=1.0): if len(color) == 3: color = (color[0], color[1], color[2], 1.0) if self._p.label_font is None: self._p.label_font = font.load(self._p.font_face, self._p.font_size, bold=True, italic=False) label = font.Text(self._p.label_font, text, color=color, valign=font.Text.BASELINE, halign=font.Text.CENTER) glPushMatrix() glTranslatef(*position) billboard_matrix() scale_factor = 0.005*scale glScalef(scale_factor, scale_factor, scale_factor) glColor4f(0,0,0,0) label.draw() glPopMatrix() def draw_line(self, v, color): o = self._p._origin glBegin(GL_LINES) glColor3f(*color) glVertex3f(v[0][0] + o[0], v[0][1] + o[1], v[0][2] + o[2]) glVertex3f(v[1][0] + o[0], v[1][1] + o[1], v[1][2] + o[2]) glEnd() class PlotAxesOrdinate(PlotAxesBase): def __init__(self, parent_axes): super(PlotAxesOrdinate, self).__init__(parent_axes) def draw_axis(self, axis, color): ticks = self._p._axis_ticks[axis] radius = self._p._tick_length / 2.0 if len(ticks) < 2: return # calculate the vector for this axis axis_lines = [[0,0,0], [0,0,0]] axis_lines[0][axis], axis_lines[1][axis] = ticks[0], ticks[-1] axis_vector = vec_sub( axis_lines[1], axis_lines[0] ) # calculate angle to the z direction vector pos_z = get_direction_vectors()[2] d = abs( dot_product(axis_vector, pos_z) ) d = d / vec_mag(axis_vector) # don't draw labels if we're looking down the axis labels_visible = abs(d - 1.0) > 0.02 # draw the ticks and labels for tick in ticks: self.draw_tick_line(axis, color, radius, tick, labels_visible) # draw the axis line and labels self.draw_axis_line(axis, color, ticks[0], ticks[-1], labels_visible) def draw_axis_line(self, axis, color, a_min, a_max, labels_visible): axis_line = [[0,0,0], [0,0,0]] axis_line[0][axis], axis_line[1][axis] = a_min, a_max self.draw_line(axis_line, color) if labels_visible: self.draw_axis_line_labels(axis, color, axis_line) def draw_axis_line_labels(self, axis, color, axis_line): if not self._p._label_axes: return axis_labels = [axis_line[0][::], axis_line[1][::]] axis_labels[0][axis] -= 0.3 axis_labels[1][axis] += 0.3 a_str = ['X', 'Y', 'Z'][axis] self.draw_text("-" + a_str, axis_labels[0], color) self.draw_text("+" + a_str, axis_labels[1], color) def draw_tick_line(self, axis, color, radius, tick, labels_visible): tick_axis = {0: 1, 1: 0, 2: 1}[axis] tick_line = [[0,0,0], [0,0,0]] tick_line[0][axis] = tick_line[1][axis] = tick tick_line[0][tick_axis], tick_line[1][tick_axis] = -radius, radius self.draw_line(tick_line, color) if labels_visible: self.draw_tick_line_label(axis, color, radius, tick) def draw_tick_line_label(self, axis, color, radius, tick): if not self._p._label_axes: return tick_label_vector = [0,0,0] tick_label_vector[axis] = tick tick_label_vector[{0: 1, 1: 0, 2: 1}[axis]] = [-1,1,1][axis]*radius*3.5 self.draw_text(str(tick), tick_label_vector, color, scale=0.5) class PlotAxesFrame(PlotAxesBase): def __init__(self, parent_axes): super(PlotAxesFrame, self).__init__(parent_axes) def draw_background(self, color): pass def draw_axis(self, axis, color): raise NotImplementedError() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_modes.py0000644000175000017500000001230612014170666025460 0ustar georgeskgeorgeskfrom plot_curve import PlotCurve from plot_surface import PlotSurface from sympy import pi, lambdify from sympy.functions import sin, cos from math import sin as p_sin from math import cos as p_cos def float_vec3(f): def inner(*args): v = f(*args) return float(v[0]), float(v[1]), float(v[2]) return inner class Cartesian2D(PlotCurve): i_vars, d_vars = 'x', 'y' intervals = [[-5,5,100]] aliases = ['cartesian'] is_default = True def _get_sympy_evaluator(self): fy = self.d_vars[0] x = self.t_interval.v @float_vec3 def e(_x): return ( _x, fy.subs(x, _x), 0.0 ) return e def _get_lambda_evaluator(self): fy = self.d_vars[0] x = self.t_interval.v return lambdify([x], [x, fy, 0.0]) class Cartesian3D(PlotSurface): i_vars, d_vars = 'xy', 'z' intervals = [[-1,1,40], [-1,1,40]] aliases = ['cartesian', 'monge'] is_default = True def _get_sympy_evaluator(self): fz = self.d_vars[0] x = self.u_interval.v y = self.v_interval.v @float_vec3 def e(_x, _y): return ( _x, _y, fz.subs(x, _x).subs(y, _y) ) return e def _get_lambda_evaluator(self): fz = self.d_vars[0] x = self.u_interval.v y = self.v_interval.v return lambdify([x,y], [x,y,fz]) class ParametricCurve2D(PlotCurve): i_vars, d_vars = 't', 'xy' intervals = [[0,2*pi,100]] aliases = ['parametric'] is_default = True def _get_sympy_evaluator(self): fx, fy = self.d_vars t = self.t_interval.v @float_vec3 def e(_t): return ( fx.subs(t, _t), fy.subs(t, _t), 0.0 ) return e def _get_lambda_evaluator(self): fx, fy = self.d_vars t = self.t_interval.v return lambdify([t], [fx,fy,0.0]) class ParametricCurve3D(PlotCurve): i_vars, d_vars = 't', 'xyz' intervals = [[0,2*pi,100]] aliases = ['parametric'] is_default = True def _get_sympy_evaluator(self): fx, fy, fz = self.d_vars t = self.t_interval.v @float_vec3 def e(_t): return ( fx.subs(t, _t), fy.subs(t, _t), fz.subs(t, _t) ) return e def _get_lambda_evaluator(self): fx, fy, fz = self.d_vars t = self.t_interval.v return lambdify([t], [fx,fy,fz]) class ParametricSurface(PlotSurface): i_vars, d_vars = 'uv', 'xyz' intervals = [[-1,1,40], [-1,1,40]] aliases = ['parametric'] is_default = True def _get_sympy_evaluator(self): fx, fy, fz = self.d_vars u = self.u_interval.v v = self.v_interval.v @float_vec3 def e(_u, _v): return ( fx.subs(u, _u).subs(v, _v), fy.subs(u, _u).subs(v, _v), fz.subs(u, _u).subs(v, _v) ) return e def _get_lambda_evaluator(self): fx, fy, fz = self.d_vars u = self.u_interval.v v = self.v_interval.v return lambdify([u,v], [fx, fy, fz]) class Polar(PlotCurve): i_vars, d_vars = 't', 'r' intervals = [[0,2*pi,100]] aliases = ['polar'] is_default = False def _get_sympy_evaluator(self): fr = self.d_vars[0] t = self.t_interval.v def e(_t): _r = float( fr.subs(t, _t) ) return ( _r*p_cos(_t), _r*p_sin(_t), 0.0 ) return e def _get_lambda_evaluator(self): fr = self.d_vars[0] t = self.t_interval.v fx, fy = fr*cos(t), fr*sin(t) return lambdify([t], [fx,fy,0.0]) class Cylindrical(PlotSurface): i_vars, d_vars = 'th', 'r' intervals = [[0,2*pi,40], [-1,1,20]] aliases = ['cylindrical', 'polar'] is_default = False def _get_sympy_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v h = self.v_interval.v def e(_t, _h): _r = float( fr.subs(t, _t).subs(h, _h) ) return ( _r*p_cos(_t), _r*p_sin(_t), _h ) return e def _get_lambda_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v h = self.v_interval.v fx, fy = fr*cos(t), fr*sin(t) return lambdify([t,h], [fx,fy,h]) class Spherical(PlotSurface): i_vars, d_vars = 'tp', 'r' intervals = [[0,2*pi,40], [0,pi,20]] aliases = ['spherical'] is_default = False def _get_sympy_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v p = self.v_interval.v def e(_t, _p): _r = float( fr.subs(t, _t).subs(p, _p) ) return ( _r*p_cos(_t)*p_sin(_p), _r*p_sin(_t)*p_sin(_p), _r*p_cos(_p) ) return e def _get_lambda_evaluator(self): fr = self.d_vars[0] t = self.u_interval.v p = self.v_interval.v fx = fr*cos(t)*sin(p) fy = fr*sin(t)*sin(p) fz = fr*cos(p) return lambdify([t,p], [fx,fy,fz]) Cartesian2D._register() Cartesian3D._register() ParametricCurve2D._register() ParametricCurve3D._register() ParametricSurface._register() Polar._register() Cylindrical._register() Spherical._register() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_mode.py0000644000175000017500000003364012014170666025301 0ustar georgeskgeorgeskfrom sympy import Symbol, sympify from plot_interval import PlotInterval from plot_object import PlotObject from util import parse_option_string from sympy.geometry.entity import GeometryEntity from sympy.core.compatibility import is_sequence class PlotMode(PlotObject): """ Grandparent class for plotting modes. Serves as interface for registration, lookup, and init of modes. To create a new plot mode, inherit from PlotModeBase or one of its children, such as PlotSurface or PlotCurve. """ ## Class-level attributes ## used to register and lookup ## plot modes. See PlotModeBase ## for descriptions and usage. i_vars, d_vars = '', '' intervals = [] aliases = [] is_default = False ## Draw is the only method here which ## is meant to be overridden in child ## classes, and PlotModeBase provides ## a base implementation. def draw(self): raise NotImplementedError() ## Everything else in this file has to ## do with registration and retrieval ## of plot modes. This is where I've ## hidden much of the ugliness of automatic ## plot mode divination... ## Plot mode registry data structures _mode_alias_list = [] _mode_map = { 1: {1: {}, 2: {}}, 2: {1: {}, 2: {}}, 3: {1: {}, 2: {}}, } # [d][i][alias_str]: class _mode_default_map = { 1: {}, 2: {}, 3: {}, } # [d][i]: class _i_var_max, _d_var_max = 2, 3 def __new__(cls, *args, **kwargs): """ This is the function which interprets arguments given to Plot.__init__ and Plot.__setattr__. Returns an initialized instance of the appropriate child class. """ nargs, nkwargs = PlotMode._extract_options(args, kwargs) mode_arg = nkwargs.get('mode', '') # Interpret the arguments d_vars, intervals = PlotMode._interpret_args(nargs) i_vars = PlotMode._find_i_vars(d_vars, intervals) i, d = max([len(i_vars), len(intervals)]), len(d_vars) # Find the appropriate mode subcls = PlotMode._get_mode(mode_arg, i, d) # Create the object o = object.__new__(subcls) # Do some setup for the mode instance o.d_vars = d_vars o._fill_i_vars(i_vars) o._fill_intervals(intervals) o.options = nkwargs return o @staticmethod def _get_mode(mode_arg, i_var_count, d_var_count): """ Tries to return an appropriate mode class. Intended to be called only by __new__. mode_arg Can be a string or a class. If it is a PlotMode subclass, it is simply returned. If it is a string, it can an alias for a mode or an empty string. In the latter case, we try to find a default mode for the i_var_count and d_var_count. i_var_count The number of independent variables needed to evaluate the d_vars. d_var_count The number of dependent variables; usually the number of functions to be evaluated in plotting. For example, a Cartesian function y = f(x) has one i_var (x) and one d_var (y). A parametric form x,y,z = f(u,v), f(u,v), f(u,v) has two two i_vars (u,v) and three d_vars (x,y,z). """ # if the mode_arg is simply a PlotMode class, # check that the mode supports the numbers # of independent and dependent vars, then # return it try: m = None if issubclass(mode_arg, PlotMode): m = mode_arg except: pass if m: if not m._was_initialized: raise ValueError(("To use unregistered plot mode %s " "you must first call %s._init_mode().") % (m.__name__, m.__name__)) if d_var_count != m.d_var_count: raise ValueError(("%s can only plot functions " "with %i dependent variables.") % (m.__name__, m.d_var_count)) if i_var_count > m.i_var_count: raise ValueError(("%s cannot plot functions " "with more than %i independent " "variables.") % (m.__name__, m.i_var_count)) return m # If it is a string, there are two possibilities. if isinstance(mode_arg, str): i, d = i_var_count, d_var_count if i > PlotMode._i_var_max: raise ValueError(var_count_error(True, True)) if d > PlotMode._d_var_max: raise ValueError(var_count_error(False, True)) # If the string is '', try to find a suitable # default mode if not mode_arg: return PlotMode._get_default_mode(i, d) # Otherwise, interpret the string as a mode # alias (e.g. 'cartesian', 'parametric', etc) else: return PlotMode._get_aliased_mode(mode_arg, i, d) else: raise ValueError("PlotMode argument must be " "a class or a string") @staticmethod def _get_default_mode(i, d, i_vars=-1): if i_vars == -1: i_vars = i try: return PlotMode._mode_default_map[d][i] except: # Keep looking for modes in higher i var counts # which support the given d var count until we # reach the max i_var count. if i < PlotMode._i_var_max: return PlotMode._get_default_mode(i+1, d, i_vars) else: raise ValueError(("Couldn't find a default mode " "for %i independent and %i " "dependent variables.") % (i_vars, d)) @staticmethod def _get_aliased_mode(alias, i, d, i_vars=-1): if i_vars == -1: i_vars = i if alias not in PlotMode._mode_alias_list: raise ValueError(("Couldn't find a mode called" " %s. Known modes: %s.") % (alias, ", ".join(PlotMode._mode_alias_list))) try: return PlotMode._mode_map[d][i][alias] except: # Keep looking for modes in higher i var counts # which support the given d var count and alias # until we reach the max i_var count. if i < PlotMode._i_var_max: return PlotMode._get_aliased_mode(alias, i+1, d, i_vars) else: raise ValueError(("Couldn't find a %s mode " "for %i independent and %i " "dependent variables.") % (alias, i_vars, d)) @classmethod def _register(cls): """ Called once for each user-usable plot mode. For Cartesian2D, it is invoked after the class definition: Cartesian2D._register() """ name = cls.__name__ #try: cls._init_mode() #except Exception, e: # raise RuntimeError( ("Failed to initialize " # "plot mode %s. Reason: %s") # % (name, (str(e))) ) try: i, d = cls.i_var_count, cls.d_var_count # Add the mode to _mode_map under all # given aliases for a in cls.aliases: if a not in PlotMode._mode_alias_list: # Also track valid aliases, so # we can quickly know when given # an invalid one in _get_mode. PlotMode._mode_alias_list.append(a) PlotMode._mode_map[d][i][a] = cls if cls.is_default: # If this mode was marked as the # default for this d,i combination, # also set that. PlotMode._mode_default_map[d][i] = cls except Exception, e: raise RuntimeError( ("Failed to register " "plot mode %s. Reason: %s") % (name, (str(e))) ) @classmethod def _init_mode(cls): """ Initializes the plot mode based on the 'mode-specific parameters' above. Only intended to be called by PlotMode._register(). To use a mode without registering it, you can directly call ModeSubclass._init_mode(). """ def symbols_list(symbol_str): return [ Symbol(s) for s in symbol_str ] # Convert the vars strs into # lists of symbols. cls.i_vars = symbols_list(cls.i_vars) cls.d_vars = symbols_list(cls.d_vars) # Var count is used often, calculate # it once here cls.i_var_count = len(cls.i_vars) cls.d_var_count = len(cls.d_vars) if cls.i_var_count > PlotMode._i_var_max: raise ValueError(var_count_error(True, False)) if cls.d_var_count > PlotMode._d_var_max: raise ValueError(var_count_error(False, False)) # Try to use first alias as primary_alias if len(cls.aliases) > 0: cls.primary_alias = cls.aliases[0] else: cls.primary_alias = cls.__name__ di = cls.intervals if len(di) != cls.i_var_count: raise ValueError("Plot mode must provide a " "default interval for each i_var.") for i in range(cls.i_var_count): assert len(di[i]) == 3 # default intervals # must be given # [min,max,steps] # # (no var, but they # must be in the same # order as i_vars) # Initialize an incomplete interval, # to later be filled with a var when # the mode is instantiated. di[i] = PlotInterval(None, *di[i]) # To prevent people from using modes # without these required fields set up. cls._was_initialized = True _was_initialized = False ## Initializer Helper Methods @staticmethod def _find_i_vars(functions, intervals): i_vars = [] # First, collect i_vars in the # order they are given in any # intervals. for i in intervals: if i.v is None: continue elif i.v in i_vars: raise ValueError(("Multiple intervals given " "for %s.") % (str(i.v))) i_vars.append(i.v) # Then, find any remaining # i_vars in given functions # (aka d_vars) for f in functions: for a in f.atoms(Symbol): if a not in i_vars: i_vars.append(a) return i_vars def _fill_i_vars(self, i_vars): # copy default i_vars self.i_vars = [Symbol(str(i)) for i in self.i_vars] # replace with given i_vars for i in range(len(i_vars)): self.i_vars[i] = i_vars[i] def _fill_intervals(self, intervals): # copy default intervals self.intervals = [PlotInterval(i) for i in self.intervals] # track i_vars used so far v_used = [] # fill copy of default # intervals with given info for i in range(len(intervals)): self.intervals[i].fill_from(intervals[i]) if self.intervals[i].v is not None: v_used.append(self.intervals[i].v) # Find any orphan intervals and # assign them i_vars for i in range(len(self.intervals)): if self.intervals[i].v is None: u = [v for v in self.i_vars if v not in v_used] assert len(u) != 0 self.intervals[i].v = u[0] v_used.append(u[0]) @staticmethod def _interpret_args(args): interval_wrong_order = "PlotInterval %s was given before any function(s)." interpret_error = "Could not interpret %s as a function or interval." functions, intervals = [], [] if isinstance(args[0], GeometryEntity): for coords in list(args[0].arbitrary_point()): functions.append(coords) intervals.append(PlotInterval.try_parse(args[0].plot_interval())) else: for a in args: i = PlotInterval.try_parse(a) if i is not None: if len(functions) == 0: raise ValueError(interval_wrong_order % (str(i))) else: intervals.append(i) else: if is_sequence(a, include=str): raise ValueError(interpret_error % (str(a))) try: f = sympify(a) functions.append(f) except: raise ValueError(interpret_error % str(a)) return functions, intervals @staticmethod def _extract_options(args, kwargs): nkwargs, nargs = {}, [] for a in args: if isinstance(a, str): nkwargs = dict(nkwargs, **parse_option_string(a)) else: nargs.append(a) nkwargs = dict(nkwargs, **kwargs) return nargs, nkwargs def var_count_error(is_independent, is_plotting): """ Used to format an error message which differs slightly in 4 places. """ if is_plotting: v = "Plotting" else: v = "Registering plot modes" if is_independent: n, s = PlotMode._i_var_max, "independent" else: n, s = PlotMode._d_var_max, "dependent" return ("%s with more than %i %s variables " "is not supported.") % (v, n, s) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/util.py0000644000175000017500000001011512014170666024264 0ustar georgeskgeorgeskfrom pyglet.gl import * from sympy.core import S def get_model_matrix(array_type=c_float, glGetMethod=glGetFloatv): """ Returns the current modelview matrix. """ m = (array_type*16)() glGetMethod(GL_MODELVIEW_MATRIX, m) return m def get_projection_matrix(array_type=c_float, glGetMethod=glGetFloatv): """ Returns the current modelview matrix. """ m = (array_type*16)() glGetMethod(GL_PROJECTION_MATRIX, m) return m def get_viewport(): """ Returns the current viewport. """ m = (c_int*4)() glGetIntegerv(GL_VIEWPORT, m) return m def get_direction_vectors(): m = get_model_matrix() return ((m[0], m[4], m[8]), (m[1], m[5], m[9]), (m[2], m[6], m[10])) def get_view_direction_vectors(): m = get_model_matrix() return ((m[0], m[1], m[2]), (m[4], m[5], m[6]), (m[8], m[9], m[10])) def get_basis_vectors(): return ((1,0,0), (0,1,0), (0,0,1)) def screen_to_model(x,y,z): m = get_model_matrix(c_double, glGetDoublev) p = get_projection_matrix(c_double, glGetDoublev) w = get_viewport() mx,my,mz = c_double(),c_double(),c_double() gluUnProject(x,y,z,m,p,w,mx,my,mz) return float(mx.value),float(my.value),float(mz.value) def model_to_screen(x,y,z): m = get_model_matrix(c_double, glGetDoublev) p = get_projection_matrix(c_double, glGetDoublev) w = get_viewport() mx,my,mz = c_double(),c_double(),c_double() gluProject(x,y,z,m,p,w,mx,my,mz) return float(mx.value),float(my.value),float(mz.value) def vec_subs(a,b): return tuple(a[i]-b[i] for i in xrange(len(a))) def billboard_matrix(): """ Removes rotational components of current matrix so that primitives are always drawn facing the viewer. |1|0|0|x| |0|1|0|x| |0|0|1|x| (x means left unchanged) |x|x|x|x| """ m = get_model_matrix() m[0] =1;m[1] =0;m[2] =0 m[4] =0;m[5] =1;m[6] =0 m[8] =0;m[9] =0;m[10]=1 glLoadMatrixf(m) def create_bounds(): return [ [S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0] ] def update_bounds(b, v): if v is None: return for axis in xrange(3): b[axis][0] = min([b[axis][0], v[axis]]) b[axis][1] = max([b[axis][1], v[axis]]) def interpolate(a_min, a_max, a_ratio): return a_min + a_ratio * (a_max - a_min) def rinterpolate(a_min, a_max, a_value): a_range = a_max-a_min if a_range == 0: a_range = 1.0 return (a_value - a_min) / float(a_range) def interpolate_color(color1, color2, ratio): return tuple(interpolate(color1[i], color2[i], ratio) for i in xrange(3)) def scale_value(v, v_min, v_len): return (v-v_min)/v_len def scale_value_list(flist): v_min, v_max = min(flist), max(flist) v_len = v_max-v_min return list(scale_value(f,v_min,v_len) for f in flist) def strided_range(r_min, r_max, stride, max_steps=50): o_min, o_max = r_min, r_max if abs(r_min-r_max) < 0.001: return [] try: xrange(int(r_min-r_max)) except: return [] assert r_min < r_max r_min_s = (r_min % stride) r_max_s = stride - (r_max % stride) if abs(r_max_s-stride) < 0.001: r_max_s = 0.0 r_min -= r_min_s r_max += r_max_s r_steps = int( (r_max-r_min) / stride ) if max_steps and r_steps > max_steps: return strided_range(o_min, o_max, stride*2) return [r_min] + list( r_min+e*stride for e in xrange(1, r_steps+1) ) + [r_max] def parse_option_string(s): if not isinstance(s, str): return None options = {} for token in s.split(';'): pieces = token.split('=') if len(pieces) == 1: option, value = pieces[0], "" elif len(pieces) == 2: option, value = pieces else: raise ValueError("Plot option string '%s' is malformed." % (s)) options[option.strip()] = value.strip() return options def dot_product(v1, v2): return sum(v1[i]*v2[i] for i in xrange(3)) def vec_sub(v1, v2): return tuple(v1[i]-v2[i] for i in xrange(3)) def vec_mag(v): return sum(v[i]**2 for i in xrange(3))**(0.5) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/textplot.py0000644000175000017500000000274112014170666025200 0ustar georgeskgeorgeskfrom sympy import * def textplot(expr, a, b, W=55, H=18): """ Print a crude ASCII art plot of the SymPy expression 'expr' (which should contain a single symbol, e.g. x or something else) over the interval [a, b]. Example: textplot(sin(t)*t, 0, 15) """ f = None for x in expr.atoms(): if isinstance(x, Symbol): f = lambdify([x], expr) break assert f is not None a = float(a) b = float(b) # Calculate function values y = [0] * W for x in range(W): try: y[x] = f(a + (b-a)/float(W)*x) except: y[x] = 0 # Normalize height to screen space ma = max(y) mi = min(y) for x in range(W): y[x] = int(float(H) * (y[x] - mi) / (ma - mi)) margin = 7 print for h in range(H-1, -1, -1): s = [' '] * W for x in range(W): if y[x] == h: s[x] = '.' # Print y values if h == H-1: prefix = ("%g" % ma).rjust(margin)[:margin] elif h == H//2: prefix = ("%g" % ((mi+ma)/2)).rjust(margin)[:margin] elif h == 0: prefix = ("%g" % mi).rjust(margin)[:margin] else: prefix = " "*margin s = "".join(s) if h == H//2: s = s.replace(" ", "-") print prefix + " | " + s # Print x values bottom = " " * (margin + 3) bottom += ("%g" % a).ljust(W//2-4) bottom += ("%g" % ((a+b)/2)).ljust(W//2) bottom += "%g" % b print bottom wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_interval.py0000644000175000017500000001210312014170666026170 0ustar georgeskgeorgeskfrom sympy import Symbol, Integer, sympify class PlotInterval(object): """ """ _v, _v_min, _v_max, _v_steps = None, None, None, None def require_all_args(f): def check(self, *args, **kwargs): for g in [self._v, self._v_min, self._v_max, self._v_steps]: if g is None: raise ValueError("PlotInterval is incomplete.") return f(self, *args, **kwargs) return check def __init__(self, *args): if len(args) == 1: if isinstance(args[0], PlotInterval): self.fill_from(args[0]) return elif isinstance(args[0], str): try: args = eval(args[0]) except: s_eval_error = "Could not interpret string %s." raise ValueError(s_eval_error % (args[0])) elif isinstance(args[0], (tuple, list)): args = args[0] else: raise ValueError("Not an interval.") if not isinstance(args, (tuple, list)) or len(args) > 4: f_error = "PlotInterval must be a tuple or list of length 4 or less." raise ValueError(f_error) args = list(args) if len(args) > 0 and (args[0] is None or isinstance(args[0], Symbol)): self.v = args.pop(0) if len(args) in [2,3]: self.v_min = args.pop(0) self.v_max = args.pop(0) if len(args) == 1: self.v_steps = args.pop(0) elif len(args) == 1: self.v_steps = args.pop(0) def get_v(self): return self._v def set_v(self, v): if v is None: self._v = None return if not isinstance(v, Symbol): raise ValueError("v must be a sympy Symbol.") self._v = v def get_v_min(self): return self._v_min def set_v_min(self, v_min): if v_min is None: self._v_min = None return try: self._v_min = sympify(v_min) float(self._v_min.evalf()) except: raise ValueError("v_min could not be interpreted as a number.") def get_v_max(self): return self._v_max def set_v_max(self, v_max): if v_max is None: self._v_max = None return try: self._v_max = sympify(v_max) float(self._v_max.evalf()) except: raise ValueError("v_max could not be interpreted as a number.") def get_v_steps(self): return self._v_steps def set_v_steps(self, v_steps): if v_steps is None: self._v_steps = None return if isinstance(v_steps, int): v_steps = Integer(v_steps) elif not isinstance(v_steps, Integer): raise ValueError("v_steps must be an int or sympy Integer.") if v_steps <= Integer(0): raise ValueError("v_steps must be positive.") self._v_steps = v_steps @require_all_args def get_v_len(self): return self.v_steps+1 v = property(get_v, set_v) v_min = property(get_v_min, set_v_min) v_max = property(get_v_max, set_v_max) v_steps = property(get_v_steps, set_v_steps) v_len = property(get_v_len) def fill_from(self, b): if b.v is not None: self.v = b.v if b.v_min is not None: self.v_min = b.v_min if b.v_max is not None: self.v_max = b.v_max if b.v_steps is not None: self.v_steps = b.v_steps @staticmethod def try_parse(*args): """ Returns a PlotInterval if args can be interpreted as such, otherwise None. """ if len(args) == 1 and isinstance(args[0], PlotInterval): return args[0] try: return PlotInterval(*args) except: return None def _str_base(self): return ",".join([str(self.v),str(self.v_min), str(self.v_max),str(self.v_steps)]) def __repr__(self): """ A string representing the interval in class constructor form. """ return "PlotInterval(%s)" % (self._str_base()) def __str__(self): """ A string representing the interval in list form. """ return "[%s]" % (self._str_base()) @require_all_args def assert_complete(self): pass @require_all_args def vrange(self): """ Yields v_steps+1 sympy numbers ranging from v_min to v_max. """ d = (self.v_max - self.v_min) / self.v_steps for i in xrange(self.v_steps+1): a = self.v_min + (d * Integer(i)) yield a @require_all_args def vrange2(self): """ Yields v_steps pairs of sympy numbers ranging from (v_min, v_min + step) to (v_max - step, v_max). """ d = (self.v_max - self.v_min) / self.v_steps a = self.v_min + (d * Integer(0)) for i in xrange(self.v_steps): b = self.v_min + (d * Integer(i+1)) yield a, b a = b def frange(self): for i in self.vrange(): yield float(i.evalf()) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_surface.py0000644000175000017500000000640312014170666026002 0ustar georgeskgeorgeskfrom pyglet.gl import * from plot_mode_base import PlotModeBase from sympy.core import S class PlotSurface(PlotModeBase): default_rot_preset = 'perspective' def _on_calculate_verts(self): self.u_interval = self.intervals[0] self.u_set = list(self.u_interval.frange()) self.v_interval = self.intervals[1] self.v_set = list(self.v_interval.frange()) self.bounds = [ [S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0],[S.Infinity,-S.Infinity,0] ] evaluate = self._get_evaluator() self._calculating_verts_pos = 0.0 self._calculating_verts_len = float(self.u_interval.v_len*self.v_interval.v_len) verts = list() b = self.bounds for u in self.u_set: column = list() for v in self.v_set: try: _e = evaluate(u, v) # calculate vertex except: _e = None if _e is not None: # update bounding box for axis in xrange(3): b[axis][0] = min([b[axis][0], _e[axis]]) b[axis][1] = max([b[axis][1], _e[axis]]) column.append(_e) self._calculating_verts_pos += 1.0 verts.append(column) for axis in xrange(3): b[axis][2] = b[axis][1] - b[axis][0] if b[axis][2] == 0.0: b[axis][2] = 1.0 self.verts = verts self.push_wireframe(self.draw_verts(False, False)) self.push_solid(self.draw_verts(False, True)) def _on_calculate_cverts(self): if not self.verts or not self.color: return def set_work_len(n): self._calculating_cverts_len = float(n) def inc_work_pos(): self._calculating_cverts_pos += 1.0 set_work_len(1); self._calculating_cverts_pos = 0 self.cverts = self.color.apply_to_surface(self.verts, self.u_set, self.v_set, set_len=set_work_len, inc_pos=inc_work_pos) self.push_solid(self.draw_verts(True, True)) def calculate_one_cvert(self, u, v): vert = self.verts[u][v] return self.color(vert[0], vert[1], vert[2], self.u_set[u], self.v_set[v]) def draw_verts(self, use_cverts, use_solid_color): def f(): for u in xrange( 1, len(self.u_set) ): glBegin(GL_QUAD_STRIP) for v in xrange( len(self.v_set) ): pa = self.verts[u-1][v] pb = self.verts[u][v] if pa is None or pb is None: glEnd() glBegin(GL_QUAD_STRIP) continue if use_cverts: ca = self.cverts[u-1][v] cb = self.cverts[u][v] if ca is None: ca = (0,0,0) if cb is None: cb = (0,0,0) else: if use_solid_color: ca = cb = self.default_solid_color else: ca = cb = self.default_wireframe_color glColor3f(*ca) glVertex3f(*pa) glColor3f(*cb) glVertex3f(*pb) glEnd() return f wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/managed_window.py0000644000175000017500000000566512014170666026310 0ustar georgeskgeorgeskfrom pyglet.gl import * from pyglet.window import Window from pyglet.clock import Clock from threading import Thread, Lock gl_lock = Lock() class ManagedWindow(Window): """ A pyglet window with an event loop which executes automatically in a separate thread. Behavior is added by creating a subclass which overrides setup, update, and/or draw. """ fps_limit = 30 default_win_args = dict(width=600, height=500, vsync=False, resizable=True) def __init__(self, **win_args): """ It is best not to override this function in the child class, unless you need to take additional arguments. Do any OpenGL initialization calls in setup(). """ self.win_args = dict(self.default_win_args, **win_args) self.Thread=Thread(target=self.__event_loop__) self.Thread.start() def __event_loop__(self, **win_args): """ The event loop thread function. Do not override or call directly (it is called by __init__). """ gl_lock.acquire() try: try: super(ManagedWindow, self).__init__(**self.win_args) self.switch_to() self.setup() except Exception, e: print "Window initialization failed: %s" % (str(e)) self.has_exit = True finally: gl_lock.release() clock = Clock() clock.set_fps_limit(self.fps_limit) while not self.has_exit: dt = clock.tick() gl_lock.acquire() try: try: self.switch_to() self.dispatch_events() self.clear() self.update(dt) self.draw() self.flip() except Exception, e: print "Uncaught exception in event loop: %s" % str(e) self.has_exit = True finally: gl_lock.release() super(ManagedWindow, self).close() def close(self): """ Closes the window. """ self.has_exit = True def setup(self): """ Called once before the event loop begins. Override this method in a child class. This is the best place to put things like OpenGL initialization calls. """ pass def update(self, dt): """ Called before draw during each iteration of the event loop. dt is the elapsed time in seconds since the last update. OpenGL rendering calls are best put in draw() rather than here. """ pass def draw(self): """ Called after update during each iteration of the event loop. Put OpenGL rendering calls here. """ pass if __name__ == '__main__': ManagedWindow() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_rotation.py0000644000175000017500000000251712014170666026213 0ustar georgeskgeorgeskfrom pyglet.gl import * from math import sqrt, acos def cross(a, b): return (a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0]) def dot(a, b): return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] def mag(a): return sqrt(a[0]**2 + a[1]**2 + a[2]**2) def norm(a): m = mag(a) return (a[0]/m, a[1]/m, a[2]/m) def get_sphere_mapping(x, y, width, height): x = min([max([x,0]), width]) y = min([max([y,0]), height]) sr = sqrt( (width/2)**2 + (height/2)**2 ) #sr *= 1.5 sx = ( (x - width/2) / sr ) sy = ( (y - height/2) / sr ) sz = 1.0 - sx**2 - sy**2 if sz > 0.0: sz = sqrt(sz) return (sx, sy, sz) else: sz = 0 return norm( (sx, sy, sz) ) rad2deg = 180.0/3.141592 def get_spherical_rotatation(p1, p2, width, height, theta_multiplier): v1 = get_sphere_mapping(p1[0], p1[1], width, height) v2 = get_sphere_mapping(p2[0], p2[1], width, height) d = min(max([dot(v1, v2), -1]), 1) if abs(d - 1.0) < 0.000001: return None raxis = norm( cross(v1, v2) ) rtheta = theta_multiplier * rad2deg * acos(d) #rtheta = 2.0 * rad2deg * acos(d) glPushMatrix() glLoadIdentity() glRotatef(rtheta, *raxis) mat = (c_float*16)() glGetFloatv(GL_MODELVIEW_MATRIX, mat) glPopMatrix() return mat wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_object.py0000644000175000017500000000052212014170666025614 0ustar georgeskgeorgeskclass PlotObject(object): """ Base class for objects which can be displayed in a Plot. """ visible = True def _draw(self): if self.visible: self.draw() def draw(self): """ OpenGL rendering code for the plot object. Override in base class. """ pass wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_camera.py0000644000175000017500000000713212014170666025602 0ustar georgeskgeorgeskfrom pyglet.gl import * from plot_rotation import get_spherical_rotatation from util import get_model_matrix from util import screen_to_model, model_to_screen from util import vec_subs class PlotCamera(object): min_dist = 0.05 max_dist = 500.0 min_ortho_dist = 100.0 max_ortho_dist = 10000.0 _default_dist = 6.0 _default_ortho_dist = 600.0 rot_presets = { 'xy':(0,0,0), 'xz':(-90,0,0), 'yz':(0,90,0), 'perspective':(-45,0,-45) } def __init__(self, window, ortho = False): self.window = window self.axes = self.window.plot.axes self.ortho = ortho self.reset() def init_rot_matrix(self): glPushMatrix() glLoadIdentity() self._rot = get_model_matrix() glPopMatrix() def set_rot_preset(self, preset_name): self.init_rot_matrix() try: r = self.rot_presets[preset_name] except: raise ValueError("%s is not a valid rotation preset." % preset_name) try: self.euler_rotate(r[0], 1, 0, 0) self.euler_rotate(r[1], 0, 1, 0) self.euler_rotate(r[2], 0, 0, 1) except: pass def reset(self): self._dist = 0.0 self._x, self._y = 0.0, 0.0 self._rot = None if self.ortho: self._dist = self._default_ortho_dist else: self._dist = self._default_dist self.init_rot_matrix() def mult_rot_matrix(self, rot): glPushMatrix() glLoadMatrixf(rot) glMultMatrixf(self._rot) self._rot = get_model_matrix() glPopMatrix() def setup_projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() if self.ortho: # yep, this is pseudo ortho (don't tell anyone) gluPerspective(0.3, float(self.window.width)/float(self.window.height), self.min_ortho_dist-0.01, self.max_ortho_dist+0.01) else: gluPerspective(30.0, float(self.window.width)/float(self.window.height), self.min_dist-0.01, self.max_dist+0.01) glMatrixMode(GL_MODELVIEW) def _get_scale(self): return 1.0, 1.0, 1.0 def apply_transformation(self): glLoadIdentity() glTranslatef(self._x, self._y, -self._dist) if self._rot is not None: glMultMatrixf(self._rot) glScalef(*self._get_scale()) def spherical_rotate(self, p1, p2, sensitivity=1.0): mat = get_spherical_rotatation(p1, p2, self.window.width, self.window.height, sensitivity) if mat is not None: self.mult_rot_matrix(mat) def euler_rotate(self, angle, x, y, z): glPushMatrix() glLoadMatrixf(self._rot) glRotatef(angle, x, y, z) self._rot = get_model_matrix() glPopMatrix() def zoom_relative(self, clicks, sensitivity): if self.ortho: dist_d = clicks * sensitivity * 50.0 min_dist = self.min_ortho_dist max_dist = self.max_ortho_dist else: dist_d = clicks * sensitivity min_dist = self.min_dist max_dist = self.max_dist new_dist = (self._dist - dist_d) if (clicks < 0 and new_dist < max_dist) or new_dist > min_dist: self._dist = new_dist def mouse_translate(self, x, y, dx, dy): glPushMatrix() glLoadIdentity() glTranslatef(0,0,-self._dist) z = model_to_screen(0,0,0)[2] d = vec_subs(screen_to_model(x,y,z), screen_to_model(x-dx,y-dy,z)) glPopMatrix() self._x += d[0] self._y += d[1] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_controller.py0000644000175000017500000001526212014170666026540 0ustar georgeskgeorgeskfrom pyglet.window import key from pyglet.window.mouse import LEFT, RIGHT, MIDDLE from util import get_direction_vectors, get_basis_vectors class PlotController(object): normal_mouse_sensitivity = 4.0 modified_mouse_sensitivity = 1.0 normal_key_sensitivity = 160.0 modified_key_sensitivity = 40.0 keymap = { key.LEFT:'left', key.A:'left', key.NUM_4:'left', key.RIGHT:'right', key.D:'right', key.NUM_6:'right', key.UP:'up', key.W:'up', key.NUM_8:'up', key.DOWN:'down', key.S:'down', key.NUM_2:'down', key.Z:'rotate_z_neg', key.NUM_1:'rotate_z_neg', key.C:'rotate_z_pos', key.NUM_3:'rotate_z_pos', key.Q:'spin_left', key.NUM_7:'spin_left', key.E:'spin_right', key.NUM_9:'spin_right', key.X:'reset_camera', key.NUM_5:'reset_camera', key.NUM_ADD:'zoom_in', key.PAGEUP:'zoom_in', key.R:'zoom_in', key.NUM_SUBTRACT:'zoom_out', key.PAGEDOWN:'zoom_out', key.F:'zoom_out', key.RSHIFT:'modify_sensitivity', key.LSHIFT:'modify_sensitivity', key.F1:'rot_preset_xy', key.F2:'rot_preset_xz', key.F3:'rot_preset_yz', key.F4:'rot_preset_perspective', key.F5:'toggle_axes', key.F6:'toggle_axe_colors', key.F8:'save_image' } def __init__(self, window, **kwargs): self.invert_mouse_zoom = kwargs.pop('invert_mouse_zoom', False) self.window = window self.camera = window.camera self.action = { # Rotation around the view Y (up) vector 'left':False, 'right':False, # Rotation around the view X vector 'up':False, 'down':False, # Rotation around the view Z vector 'spin_left':False, 'spin_right':False, # Rotation around the model Z vector 'rotate_z_neg':False, 'rotate_z_pos':False, # Reset to the default rotation 'reset_camera':False, # Performs camera z-translation 'zoom_in':False, 'zoom_out':False, # Use alternative sensitivity (speed) 'modify_sensitivity':False, # Rotation presets 'rot_preset_xy':False, 'rot_preset_xz':False, 'rot_preset_yz':False, 'rot_preset_perspective':False, # axes 'toggle_axes':False, 'toggle_axe_colors':False, # screenshot 'save_image':False } def update(self, dt): z = 0 if self.action['zoom_out']: z -= 1 if self.action['zoom_in']: z += 1 if z != 0: self.camera.zoom_relative(z/10.0, self.get_key_sensitivity()/10.0) dx, dy, dz = 0, 0, 0 if self.action['left']: dx -= 1 if self.action['right']: dx += 1 if self.action['up']: dy -= 1 if self.action['down']: dy += 1 if self.action['spin_left']: dz += 1 if self.action['spin_right']: dz -= 1 if not self.is_2D(): if dx != 0: self.camera.euler_rotate(dx*dt*self.get_key_sensitivity(), *(get_direction_vectors()[1])) if dy != 0: self.camera.euler_rotate(dy*dt*self.get_key_sensitivity(), *(get_direction_vectors()[0])) if dz != 0: self.camera.euler_rotate(dz*dt*self.get_key_sensitivity(), *(get_direction_vectors()[2])) else: self.camera.mouse_translate(0, 0, dx*dt*self.get_key_sensitivity(), -dy*dt*self.get_key_sensitivity()) rz = 0 if self.action['rotate_z_neg'] and not self.is_2D(): rz -= 1 if self.action['rotate_z_pos'] and not self.is_2D(): rz += 1 if rz != 0: self.camera.euler_rotate(rz*dt*self.get_key_sensitivity(), *(get_basis_vectors()[2])) if self.action['reset_camera']: self.camera.reset() if self.action['rot_preset_xy']: self.camera.set_rot_preset('xy') if self.action['rot_preset_xz']: self.camera.set_rot_preset('xz') if self.action['rot_preset_yz']: self.camera.set_rot_preset('yz') if self.action['rot_preset_perspective']: self.camera.set_rot_preset('perspective') if self.action['toggle_axes']: self.action['toggle_axes'] = False self.camera.axes.toggle_visible() if self.action['toggle_axe_colors']: self.action['toggle_axe_colors'] = False self.camera.axes.toggle_colors() if self.action['save_image']: self.action['save_image'] = False self.window.plot.saveimage() return True def get_mouse_sensitivity(self): if self.action['modify_sensitivity']: return self.modified_mouse_sensitivity else: return self.normal_mouse_sensitivity def get_key_sensitivity(self): if self.action['modify_sensitivity']: return self.modified_key_sensitivity else: return self.normal_key_sensitivity def on_key_press(self, symbol, modifiers): if symbol in self.keymap: self.action[self.keymap[symbol]] = True def on_key_release(self, symbol, modifiers): if symbol in self.keymap: self.action[self.keymap[symbol]] = False def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): if buttons & LEFT: if self.is_2D(): self.camera.mouse_translate(x, y, dx, dy) else: self.camera.spherical_rotate((x-dx,y-dy),(x,y), self.get_mouse_sensitivity()) if buttons & MIDDLE: self.camera.zoom_relative([1,-1][self.invert_mouse_zoom]*dy, self.get_mouse_sensitivity()/20.0) if buttons & RIGHT: self.camera.mouse_translate(x, y, dx, dy) def on_mouse_scroll(self, x, y, dx, dy): self.camera.zoom_relative([1,-1][self.invert_mouse_zoom]*dy, self.get_mouse_sensitivity()) def is_2D(self): functions = self.window.plot._functions for i in functions: if len(functions[i].i_vars)>1 or len(functions[i].d_vars)>2: return False return True wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot.py0000644000175000017500000003015112014170666024267 0ustar georgeskgeorgeskfrom sympy import Integer from sympy.core.compatibility import is_sequence from threading import RLock # it is sufficient to import "pyglet" here once try: from pyglet.gl import * except ImportError: raise ImportError("pyglet is required for plotting.\n visit http://www.pyglet.org/") from plot_object import PlotObject from plot_axes import PlotAxes from plot_window import PlotWindow from plot_mode import PlotMode import plot_modes from time import sleep from os import getcwd, listdir from util import parse_option_string from sympy.geometry.entity import GeometryEntity class Plot(object): """ Plot Examples ============= See examples/plotting.py for many more examples. >>> from sympy import Plot >>> from sympy.abc import x, y, z >>> Plot(x*y**3-y*x**3) >>> p = Plot() >>> p[1] = x*y >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) >>> p = Plot() >>> p[1] = x**2+y**2 >>> p[2] = -x**2-y**2 Variable Intervals ================== The basic format is [var, min, max, steps], but the syntax is flexible and arguments left out are taken from the defaults for the current coordinate mode: >>> Plot(x**2) # implies [x,-5,5,100] >>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40] >>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100] >>> Plot(x**2, [x,-13,13,100]) >>> Plot(x**2, [-13,13]) # [x,-13,13,100] >>> Plot(x**2, [x,-13,13]) # [x,-13,13,100] >>> Plot(1*x, [], [x], mode='cylindrical') ... # [unbound_theta,0,2*Pi,40], [x,-1,1,20] Coordinate Modes ================ Plot supports several curvilinear coordinate modes, and they independent for each plotted function. You can specify a coordinate mode explicitly with the 'mode' named argument, but it can be automatically determined for Cartesian or parametric plots, and therefore must only be specified for polar, cylindrical, and spherical modes. Specifically, Plot(function arguments) and Plot[n] = (function arguments) will interpret your arguments as a Cartesian plot if you provide one function and a parametric plot if you provide two or three functions. Similarly, the arguments will be interpreted as a curve is one variable is used, and a surface if two are used. Supported mode names by number of variables: 1: parametric, cartesian, polar 2: parametric, cartesian, cylindrical = polar, spherical >>> Plot(1, mode='spherical') Calculator-like Interface ========================= >>> p = Plot(visible=False) >>> f = x**2 >>> p[1] = f >>> p[2] = f.diff(x) >>> p[3] = f.diff(x).diff(x) >>> p [1]: x**2, 'mode=cartesian' [2]: 2*x, 'mode=cartesian' [3]: 2, 'mode=cartesian' >>> p.show() >>> p.clear() >>> p >>> p[1] = x**2+y**2 >>> p[1].style = 'solid' >>> p[2] = -x**2-y**2 >>> p[2].style = 'wireframe' >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) >>> p[1].style = 'both' >>> p[2].style = 'both' >>> p.close() Plot Window Keyboard Controls ============================= Screen Rotation: X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2 Z axis Q,E, Numpad 7,9 Model Rotation: Z axis Z,C, Numpad 1,3 Zoom: R,F, PgUp,PgDn, Numpad +,- Reset Camera: X, Numpad 5 Camera Presets: XY F1 XZ F2 YZ F3 Perspective F4 Sensitivity Modifier: SHIFT Axes Toggle: Visible F5 Colors F6 Close Window: ESCAPE ============================= """ def __init__(self, *fargs, **win_args): """ Positional Arguments ==================== Any given positional arguments are used to initialize a plot function at index 1. In other words... >>> from sympy.core import Symbol >>> from sympy.abc import x >>> p = Plot(x**2, visible=False) ...is equivalent to... >>> p = Plot(visible=False) >>> p[1] = x**2 Note that in earlier versions of the plotting module, you were able to specify multiple functions in the initializer. This functionality has been dropped in favor of better automatic plot plot_mode detection. Named Arguments =============== axes An option string of the form "key1=value1; key2 = value2" which can use the following options: style = ordinate none OR frame OR box OR ordinate stride = 0.25 val OR (val_x, val_y, val_z) overlay = True (draw on top of plot) True OR False colored = False (False uses Black, True uses colors R,G,B = X,Y,Z) True OR False label_axes = False (display axis names at endpoints) True OR False visible = True (show immediately True OR False The following named arguments are passed as arguments to window initialization: antialiasing = True True OR False ortho = False True OR False invert_mouse_zoom = False True OR False """ self._win_args = win_args self._window = None self._render_lock = RLock() self._functions = {} self._pobjects = [] self._screenshot = ScreenShot(self) axe_options = parse_option_string(win_args.pop('axes', '')) self.axes = PlotAxes(**axe_options) self._pobjects.append(self.axes) self[0] = fargs if win_args.get('visible', True): self.show() ## Window Interfaces def show(self): """ Creates and displays a plot window, or activates it (gives it focus) if it has already been created. """ if self._window and not self._window.has_exit: self._window.activate() else: self._win_args['visible'] = True self.axes.reset_resources() self._window = PlotWindow(self, **self._win_args) def close(self): """ Closes the plot window. """ if self._window: self._window.close() def saveimage(self, outfile=None, format='', size=(600, 500)): """ Saves a screen capture of the plot window to an image file. If outfile is given, it can either be a path or a file object. Otherwise a png image will be saved to the current working directory. If the format is omitted, it is determined from the filename extension. """ self._screenshot.save(outfile, format, size) ## Function List Interfaces def clear(self): """ Clears the function list of this plot. """ self._render_lock.acquire() self._functions = {} self.adjust_all_bounds() self._render_lock.release() def __getitem__(self, i): """ Returns the function at position i in the function list. """ return self._functions[i] def __setitem__(self, i, args): """ Parses and adds a PlotMode to the function list. """ if not (isinstance(i, (int, Integer)) and i >= 0): raise ValueError("Function index must " "be an integer >= 0.") if isinstance(args, PlotObject): f = args else: if (not is_sequence(args)) or isinstance(args, GeometryEntity): args = [args] if len(args) == 0: return # no arguments given kwargs = dict(bounds_callback=self.adjust_all_bounds) f = PlotMode(*args, **kwargs) if f: self._render_lock.acquire() self._functions[i] = f self._render_lock.release() else: raise ValueError("Failed to parse '%s'." % ', '.join(str(a) for a in args)) def __delitem__(self, i): """ Removes the function in the function list at position i. """ self._render_lock.acquire() del self._functions[i] self.adjust_all_bounds() self._render_lock.release() def firstavailableindex(self): """ Returns the first unused index in the function list. """ i = 0 self._render_lock.acquire() while i in self._functions: i += 1 self._render_lock.release() return i def append(self, *args): """ Parses and adds a PlotMode to the function list at the first available index. """ self.__setitem__(self.firstavailableindex(), args) def __len__(self): """ Returns the number of functions in the function list. """ return len(self._functions) def __iter__(self): """ Allows iteration of the function list. """ return self._functions.itervalues() def __repr__(self): return str(self) def __str__(self): """ Returns a string containing a new-line separated list of the functions in the function list. """ s = "" if len(self._functions) == 0: s += "" else: self._render_lock.acquire() s += "\n".join(["%s[%i]: %s" % ("", i, str(self._functions[i])) for i in self._functions]) self._render_lock.release() return s def adjust_all_bounds(self): self._render_lock.acquire() self.axes.reset_bounding_box() for f in self._functions: self.axes.adjust_bounds(self._functions[f].bounds) self._render_lock.release() def wait_for_calculations(self): sleep(0) self._render_lock.acquire() for f in self._functions: a = self._functions[f]._get_calculating_verts b = self._functions[f]._get_calculating_cverts while a() or b(): sleep(0) self._render_lock.release() class ScreenShot: def __init__(self, plot): self._plot = plot self.screenshot_requested = False self.outfile = None self.format = '' self.invisibleMode = False self.flag = 0 def __nonzero__(self): if self.screenshot_requested: return 1 return 0 def _execute_saving(self): if self.flag <3: self.flag += 1 return size_x, size_y = self._plot._window.get_size() size = size_x*size_y*4*sizeof(c_ubyte) image = create_string_buffer(size) glReadPixels(0,0,size_x,size_y, GL_RGBA, GL_UNSIGNED_BYTE, image) from PIL import Image im = Image.frombuffer('RGBA',(size_x,size_y),image.raw, 'raw', 'RGBA', 0, 1) im.transpose(Image.FLIP_TOP_BOTTOM).save(self.outfile, self.format) self.flag = 0 self.screenshot_requested = False if self.invisibleMode: self._plot._window.close() def save(self, outfile=None, format='', size=(600, 500)): self.outfile = outfile self.format = format self.size = size self.screenshot_requested = True if not self._plot._window or self._plot._window.has_exit: self._plot._win_args['visible'] = False self._plot._win_args['width'] = size[0] self._plot._win_args['height'] = size[1] self._plot.axes.reset_resources() self._plot._window = PlotWindow(self._plot, **self._plot._win_args) self.invisibleMode = True if self.outfile is None: self.outfile=self._create_unique_path() print self.outfile def _create_unique_path(self): cwd = getcwd() l = listdir(cwd) path = '' i=0 while True: if not 'plot_%s.png'%i in l: path = cwd+'/plot_%s.png'%i break i+=1 return path wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/plot_window.py0000644000175000017500000001063712014170666025665 0ustar georgeskgeorgeskfrom pyglet.gl import * from managed_window import ManagedWindow from plot_camera import PlotCamera from plot_controller import PlotController from time import clock class PlotWindow(ManagedWindow): def __init__(self, plot, **kwargs): """ Named Arguments =============== antialiasing = True True OR False ortho = False True OR False invert_mouse_zoom = False True OR False """ self.plot = plot self.camera = None self._calculating = False self.antialiasing = kwargs.pop('antialiasing', True) self.ortho = kwargs.pop('ortho', False) self.invert_mouse_zoom = kwargs.pop('invert_mouse_zoom', False) self.linewidth = kwargs.pop('linewidth', 1.5) self.title = kwargs.setdefault('caption', "SymPy Plot") self.last_caption_update = 0 self.caption_update_interval = 0.2 self.drawing_first_object = True super(PlotWindow, self).__init__(**kwargs) def setup(self): self.camera = PlotCamera(self, ortho = self.ortho) self.controller = PlotController(self, invert_mouse_zoom=self.invert_mouse_zoom) self.push_handlers(self.controller) glClearColor(1.0, 1.0, 1.0, 0.0) #glClearColor(0.95, 0.95, 0.95, 0.0) glClearDepth(1.0) glDepthFunc(GL_LESS) glEnable(GL_DEPTH_TEST) glEnable(GL_LINE_SMOOTH) glShadeModel(GL_SMOOTH) glLineWidth(self.linewidth) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) if self.antialiasing: glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST) #glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE) #glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE) self.camera.setup_projection() def on_resize(self, w, h): super(PlotWindow, self).on_resize(w, h) if self.camera is not None: self.camera.setup_projection() def update(self, dt): self.controller.update(dt) def draw(self): self.plot._render_lock.acquire() self.camera.apply_transformation() calc_verts_pos, calc_verts_len = 0, 0 calc_cverts_pos, calc_cverts_len = 0, 0 should_update_caption = (clock()-self.last_caption_update > self.caption_update_interval) if len(self.plot._functions.values()) == 0: self.drawing_first_object = True for r in self.plot._functions.itervalues(): if self.drawing_first_object: self.camera.set_rot_preset(r.default_rot_preset) self.drawing_first_object = False glPushMatrix() r._draw() glPopMatrix() # might as well do this while we are # iterating and have the lock rather # than locking and iterating twice # per frame: if should_update_caption: try: if r.calculating_verts: calc_verts_pos += r.calculating_verts_pos calc_verts_len += r.calculating_verts_len if r.calculating_cverts: calc_cverts_pos += r.calculating_cverts_pos calc_cverts_len += r.calculating_cverts_len except: pass for r in self.plot._pobjects: glPushMatrix() r._draw() glPopMatrix() if should_update_caption: self.update_caption(calc_verts_pos, calc_verts_len, calc_cverts_pos, calc_cverts_len) self.last_caption_update = clock() if self.plot._screenshot: self.plot._screenshot._execute_saving() self.plot._render_lock.release() def update_caption(self, calc_verts_pos, calc_verts_len, calc_cverts_pos, calc_cverts_len): caption = self.title if calc_verts_len or calc_cverts_len: caption += " (calculating" if calc_verts_len > 0: p = (calc_verts_pos/calc_verts_len)*100 caption += " vertices %i%%" % (p) if calc_cverts_len > 0: p = (calc_cverts_pos/calc_cverts_len)*100 caption += " colors %i%%" % (p) caption += ")" if self.caption != caption: self.set_caption(caption) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/tests/0000755000175000017500000000000012014170666024101 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/tests/test_plotting.py0000644000175000017500000000366212014170666027361 0ustar georgeskgeorgeskdisabled = False try: # pyglet requires ctypes > 1.0.0 import ctypes ctypes_major = int(ctypes.__version__.split('.')[0]) if ctypes_major < 1: disabled = True except: disabled = True try: # if pyglet.gl fails to import, e.g. opengl is missing, we disable the tests import pyglet.gl import pyglet.window except: disabled = True from sympy import symbols, sin, cos x,y = symbols('x,y') def test_import(): from sympy import Plot def test_plot_2d(): from sympy import Plot p=Plot(x, [x, -5, 5, 4], visible=False) p.wait_for_calculations() def test_plot_2d_discontinuous(): from sympy import Plot p=Plot(1/x, [x, -1, 1, 2], visible=False) p.wait_for_calculations() def test_plot_3d(): from sympy import Plot p=Plot(x*y, [x, -5, 5, 5], [y, -5, 5, 5], visible=False) p.wait_for_calculations() def test_plot_3d_discontinuous(): from sympy import Plot p=Plot(1/x, [x, -3, 3, 6], [y, -1, 1, 1], visible=False) p.wait_for_calculations() def test_plot_2d_polar(): from sympy import Plot p=Plot(1/x, [x,-1,1,4], 'mode=polar', visible=False) p.wait_for_calculations() def test_plot_3d_cylinder(): from sympy import Plot p=Plot(1/y, [x,0,6.282,4], [y,-1,1,4], 'mode=polar;style=solid', visible=False) p.wait_for_calculations() def test_plot_3d_spherical(): from sympy import Plot p=Plot(1, [x,0,6.282,4], [y,0,3.141,4], 'mode=spherical;style=wireframe', visible=False) p.wait_for_calculations() def test_plot_2d_parametric(): from sympy import Plot p=Plot(sin(x), cos(x), [x, 0, 6.282, 4], visible=False) p.wait_for_calculations() def test_plot_3d_parametric(): from sympy import Plot p=Plot(sin(x), cos(x), x/5.0, [x, 0, 6.282, 4], visible=False) p.wait_for_calculations() def _test_plot_log(): from sympy import Plot p=Plot(log(x), [x,0,6.282,4], 'mode=polar', visible=False) p.wait_for_calculations() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/plotting/__init__.py0000644000175000017500000000752312014170666025057 0ustar georgeskgeorgesk"""Plotting module that can plot 2D and 3D functions """ try: try: from ctypes import * except: raise ImportError("ctypes is required for plotting.\n'easy_install ctypes' or visit http://sourceforge.net/projects/ctypes/") def Plot(*args, **kwargs): """ Plot Examples ============= See examples/plotting.py for many more examples. >>> from sympy import symbols, Plot >>> from sympy.abc import x, y, z >>> Plot(x*y**3-y*x**3) >>> p = Plot() >>> p[1] = x*y >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) >>> p = Plot() >>> p[1] = x**2+y**2 >>> p[2] = -x**2-y**2 Variable Intervals ================== The basic format is [var, min, max, steps], but the syntax is flexible and arguments left out are taken from the defaults for the current coordinate mode: >>> Plot(x**2) # implies [x,-5,5,100] >>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40] >>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100] >>> Plot(x**2, [x,-13,13,100]) >>> Plot(x**2, [-13,13]) # [x,-13,13,100] >>> Plot(x**2, [x,-13,13]) # [x,-13,13,100] >>> Plot(1*x, [], [x], mode='cylindrical') ... # [unbound_theta,0,2*Pi,40], [x,-1,1,20] Coordinate Modes ================ Plot supports several curvilinear coordinate modes, and they independent for each plotted function. You can specify a coordinate mode explicitly with the 'mode' named argument, but it can be automatically determined for Cartesian or parametric plots, and therefore must only be specified for polar, cylindrical, and spherical modes. Specifically, Plot(function arguments) and Plot[n] = (function arguments) will interpret your arguments as a Cartesian plot if you provide one function and a parametric plot if you provide two or three functions. Similarly, the arguments will be interpreted as a curve is one variable is used, and a surface if two are used. Supported mode names by number of variables: 1: parametric, cartesian, polar 2: parametric, cartesian, cylindrical = polar, spherical >>> Plot(1, mode='spherical') Calculator-like Interface ========================= >>> p = Plot(visible=False) >>> f = x**2 >>> p[1] = f >>> p[2] = f.diff(x) >>> p[3] = f.diff(x).diff(x) >>> p [1]: x**2, 'mode=cartesian' [2]: 2*x, 'mode=cartesian' [3]: 2, 'mode=cartesian' >>> p.show() >>> p.clear() >>> p >>> p[1] = x**2+y**2 >>> p[1].style = 'solid' >>> p[2] = -x**2-y**2 >>> p[2].style = 'wireframe' >>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4) >>> p[1].style = 'both' >>> p[2].style = 'both' >>> p.close() Plot Window Keyboard Controls ============================= Screen Rotation: X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2 Z axis Q,E, Numpad 7,9 Model Rotation: Z axis Z,C, Numpad 1,3 Zoom: R,F, PgUp,PgDn, Numpad +,- Reset Camera: X, Numpad 5 Camera Presets: XY F1 XZ F2 YZ F3 Perspective F4 Sensitivity Modifier: SHIFT Axes Toggle: Visible F5 Colors F6 Close Window: ESCAPE ============================= """ import plot return plot.Plot(*args, **kwargs) except Exception, e: def Plot(*args, **kwargs): raise e from textplot import textplot wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/0000755000175000017500000000000012014170666022731 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/0000755000175000017500000000000012014170666024260 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/stringpict.py0000644000175000017500000004043612014170666027027 0ustar georgeskgeorgesk"""Prettyprinter by Jurjen Bos. (I hate spammers: mail me at pietjepuk314 at the reverse of ku.oc.oohay). All objects have a method that create a "stringPict", that can be used in the str method for pretty printing. Updates by Jason gedge (email at cs mun ca) - terminal_string() method - minor fixes and changes (mostly to prettyForm) TODO: - Allow left/center/right alignment options for above/below and top/center/bottom alignment options for left/right """ from pretty_symbology import hobj, vobj, xsym, pretty_use_unicode class stringPict(object): """An ASCII picture. The pictures are represented as a list of equal length strings. """ #special value for stringPict.below LINE = 'line' def __init__(self, s, baseline=0): """Initialize from string. Multiline strings are centered. """ #picture is a string that just can be printed self.picture = stringPict.equalLengths(s.splitlines()) #baseline is the line number of the "base line" self.baseline = baseline self.binding = None @staticmethod def equalLengths(lines): # empty lines if not lines: return [''] width = max(len(line) for line in lines) return [line.center(width) for line in lines] def height(self): return len(self.picture) def width(self): return len(self.picture[0]) @staticmethod def next(*args): """Put a string of stringPicts next to each other. Returns string, baseline arguments for stringPict. """ #convert everything to stringPicts objects = [] for arg in args: if isinstance(arg, basestring): arg = stringPict(arg) objects.append(arg) #make a list of pictures, with equal height and baseline newBaseline = max(obj.baseline for obj in objects) newHeightBelowBaseline = max( obj.height()-obj.baseline for obj in objects) newHeight = newBaseline + newHeightBelowBaseline pictures = [] for obj in objects: oneEmptyLine = [' '*obj.width()] basePadding = newBaseline-obj.baseline totalPadding = newHeight-obj.height() pictures.append( oneEmptyLine * basePadding + obj.picture + oneEmptyLine * (totalPadding-basePadding)) result = [''.join(lines) for lines in zip(*pictures)] return '\n'.join(result), newBaseline def right(self, *args): r"""Put pictures next to this one. Returns string, baseline arguments for stringPict. (Multiline) strings are allowed, and are given a baseline of 0. >>> from sympy.printing.pretty.stringpict import stringPict >>> print stringPict("10").right(" + ",stringPict("1\r-\r2",1))[0] 1 10 + - 2 """ return stringPict.next(self, *args) def left(self, *args): """Put pictures (left to right) at left. Returns string, baseline arguments for stringPict. """ return stringPict.next(*(args+(self,))) @staticmethod def stack(*args): """Put pictures on top of each other, from top to bottom. Returns string, baseline arguments for stringPict. The baseline is the baseline of the second picture. Everything is centered. Baseline is the baseline of the second picture. Strings are allowed. The special value stringPict.LINE is a row of '-' extended to the width. """ #convert everything to stringPicts; keep LINE objects = [] for arg in args: if arg is not stringPict.LINE and isinstance(arg, basestring): arg = stringPict(arg) objects.append(arg) #compute new width newWidth = max( obj.width() for obj in objects if obj is not stringPict.LINE) lineObj = stringPict(hobj('-', newWidth)) #replace LINE with proper lines for i, obj in enumerate(objects): if obj is stringPict.LINE: objects[i] = lineObj #stack the pictures, and center the result newPicture = [] for obj in objects: newPicture.extend(obj.picture) newPicture = [line.center(newWidth) for line in newPicture] newBaseline = objects[0].height()+objects[1].baseline return '\n'.join(newPicture), newBaseline def below(self, *args): """Put pictures under this picture. Returns string, baseline arguments for stringPict. Baseline is baseline of top picture >>> from sympy.printing.pretty.stringpict import stringPict >>> print stringPict("x+3").below(stringPict.LINE, '3')[0] #doctest: +NORMALIZE_WHITESPACE x+3 --- 3 """ s, baseline = stringPict.stack(self, *args) return s, self.baseline def above(self, *args): """Put pictures above this picture. Returns string, baseline arguments for stringPict. Baseline is baseline of bottom picture. """ string, baseline = stringPict.stack(*(args+(self,))) baseline = len(string.splitlines())-self.height()+self.baseline return string, baseline def parens(self, left='(', right=')', ifascii_nougly=False): """Put parentheses around self. Returns string, baseline arguments for stringPict. left or right can be None or empty string which means 'no paren from that side' """ h = self.height() b = self.baseline # XXX this is a hack -- ascii parens are ugly! if ifascii_nougly and not pretty_use_unicode(): h = 1 b = 0 res = self if left: lparen = stringPict(vobj(left, h), baseline=b) res = stringPict(*lparen.right(self)) if right: rparen = stringPict(vobj(right, h), baseline=b) res = stringPict(*res.right(rparen)) return ('\n'.join(res.picture), res.baseline) def leftslash(self): """Precede object by a slash of the proper size. """ # XXX not used anywhere ? height = max( self.baseline, self.height()-1-self.baseline)*2 + 1 slash = '\n'.join( ' '*(height-i-1)+xobj('/',1)+' '*i for i in range(height) ) return self.left(stringPict(slash, height//2)) def root(self, n=None): """Produce a nice root symbol. Produces ugly results for big n inserts. """ # XXX not used anywhere # XXX duplicate of root drawing in pretty.py #put line over expression result = self.above('_'*self.width()) #construct right half of root symbol height = self.height() slash = '\n'.join( ' ' * (height-i-1) + '/' + ' ' * i for i in range(height) ) slash = stringPict(slash, height-1) #left half of root symbol if height > 2: downline = stringPict('\\ \n \\',1) else: downline = stringPict('\\') #put n on top, as low as possible if n is not None and n.width()>downline.width(): downline = downline.left(' '*(n.width()-downline.width())) downline = downline.above(n) #build root symbol root = downline.right(slash) #glue it on at the proper height #normally, the root symbel is as high as self #which is one less than result #this moves the root symbol one down #if the root became higher, the baseline has to grow too root.baseline = result.baseline-result.height()+root.height() return result.left(root) def render(self, * args, **kwargs): """Return the string form of self. Unless the argument line_break is set to False, it will break the expression in a form that can be printed on the terminal without being broken up. """ if kwargs["wrap_line"] is False: return "\n".join(self.picture) # Attempt to get a terminal width ncols = self.terminal_width() ncols -= 2 if ncols <= 0: ncols = 78 # If smaller than the terminal width, no need to correct if self.width() <= ncols: return type(self.picture[0])(self) # for one-line pictures we don't need v-spacers. on the other hand, for # multiline-pictures, we need v-spacers between blocks, compare: # # 2 2 3 | a*c*e + a*c*f + a*d | a*c*e + a*c*f + a*d | 3.14159265358979323 # 6*x *y + 4*x*y + | | *e + a*d*f + b*c*e | 84626433832795 # | *e + a*d*f + b*c*e | + b*c*f + b*d*e + b | # 3 4 4 | | *d*f | # 4*y*x + x + y | + b*c*f + b*d*e + b | | # | | | # | *d*f do_vspacers = (self.height() > 1) i = 0 svals = [] while i < self.width(): svals.extend([ sval[i:i+ncols] for sval in self.picture ]) if (self.height() > 1): svals.append("") # a vertical spacer i += ncols if svals[-1] == '': del svals[-1] # Get rid of the last spacer return "\n".join(svals) def terminal_width(self): """Return the terminal width if possible, otherwise return 0. """ ncols = 0 try: import curses try: curses.setupterm() ncols = curses.tigetnum('cols') except AttributeError: # windows curses doesn't implement setupterm or tigetnum # code below from # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440694 from ctypes import windll, create_string_buffer # stdin handle is -10 # stdout handle is -11 # stderr handle is -12 h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) if res: import struct (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) ncols = right - left + 1 except curses.error: pass except (ImportError, TypeError): pass return ncols def __eq__(self, o): if isinstance(o, str): return '\n'.join(self.picture) == o elif isinstance(o, stringPict): return o.picture == self.picture return False def __hash__(self): return super(stringPict, self).__hash__() def __str__(self): return str.join('\n', self.picture) def __unicode__(self): return unicode.join(u'\n', self.picture) def __repr__(self): return "stringPict(%r,%d)"%('\n'.join(self.picture), self.baseline) def __getitem__(self, index): return self.picture[index] def __len__(self): return len(self.s) class prettyForm(stringPict): """Extension of the stringPict class that knows about basic math applications, optimizing double minus signs. "Binding" is interpreted as follows: ATOM this is an atom: never needs to be parenthesized FUNC this is a function application: parenthesize if added (?) DIV this is a division: make wider division if divided POW this is a power: only parenthesize if exponent MUL this is a multiplication: parenthesize if powered ADD this is an addition: parenthesize if multiplied or powered NEG this is a negative number: optimize if added, parenthesize if multiplied or powered """ ATOM, FUNC, DIV, POW, MUL, ADD, NEG = range(7) def __init__(self, s, baseline=0, binding=0, unicode=None): """Initialize from stringPict and binding power.""" stringPict.__init__(self, s, baseline) self.binding = binding self.unicode = unicode or s def __add__(self, *others): """Make a pretty addition. Addition of negative numbers is simplified. """ arg = self if arg.binding > prettyForm.NEG: arg = stringPict(*arg.parens()) result = [arg] for arg in others: #add parentheses for weak binders if arg.binding > prettyForm.NEG: arg = stringPict(*arg.parens()) #use existing minus sign if available if arg.binding != prettyForm.NEG: result.append(' + ') result.append(arg) return prettyForm(binding=prettyForm.ADD, *stringPict.next(*result)) def __div__(self, den, slashed = False): """Make a pretty division; stacked or slashed. """ if slashed: raise NotImplementedError("Can't do slashed fraction yet") num = self if num.binding==prettyForm.DIV: num = stringPict(*num.parens()) if den.binding==prettyForm.DIV: den = stringPict(*den.parens()) return prettyForm(binding=prettyForm.DIV, *stringPict.stack( num, stringPict.LINE, den)) def __truediv__(self, o): return self.__div__(o) def __mul__(self, *others): """Make a pretty multiplication. Parentheses are needed around +, - and neg. """ args = self if args.binding > prettyForm.MUL: arg = stringPict(*args.parens()) result = [args] for arg in others: result.append(xsym('*')) #add parentheses for weak binders if arg.binding > prettyForm.MUL: arg = stringPict(*arg.parens()) result.append(arg) len_res = len(result) for i in xrange(len_res): if i < len_res-1 and result[i] == '-1' and result[i+1] == xsym('*'): # substitute -1 by -, like in -1*x -> -x result.pop(i) result.pop(i) result.insert(i, '-') if result[0][0] == '-': # if there is a - sign in front of all bin = prettyForm.NEG else: bin = prettyForm.MUL return prettyForm(binding=bin, *stringPict.next(*result)) def __repr__(self): return "prettyForm(%r,%d,%d)"%( '\n'.join(self.picture), self.baseline, self.binding) def __pow__(self, b): """Make a pretty power. """ a = self if a.binding > prettyForm.FUNC: a = stringPict(*a.parens()) if b.binding == prettyForm.POW: b = stringPict(*b.parens()) if a.binding == prettyForm.FUNC: # 2 <-- top # sin (x) <-- bot top = stringPict(*b.left(' '*a.prettyFunc.width())) top = stringPict(*top.right(' '*a.prettyArgs.width())) bot = stringPict(*a.prettyFunc.right(' '*b.width())) bot = stringPict(*bot.right(a.prettyArgs)) else: # 2 <-- top # (x+y) <-- bot top = stringPict(*b.left(' '*a.width())) bot = stringPict(*a.right(' '*b.width())) return prettyForm(binding=prettyForm.POW, *bot.above(top)) simpleFunctions = ["sin", "cos", "tan"] @staticmethod def apply(function, *args): """Functions of one or more variables. """ if function in prettyForm.simpleFunctions: #simple function: use only space if possible assert len(args)==1, "Simple function %s must have 1 argument"%function arg = args[0].__pretty__() if arg.binding <= prettyForm.DIV: #optimization: no parentheses necessary return prettyForm(binding=prettyForm.FUNC, *arg.left(function+' ')) argumentList = [] for arg in args: argumentList.append(',') argumentList.append(arg.__pretty__()) argumentList = stringPict(*stringPict.next(*argumentList[1:])) argumentList = stringPict(*argumentList.parens()) return prettyForm(binding=prettyForm.ATOM, *argumentList.left(function)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/pretty_symbology.py0000644000175000017500000003011612014170666030266 0ustar georgeskgeorgesk"""Symbolic primitives + unicode/ASCII abstraction for pretty.py""" import sys warnings = '' # first, setup unicodedate environment try: import unicodedata def U(name): """unicode character by name or None if not found""" try: u = unicodedata.lookup(name) except KeyError: u = None global warnings warnings += 'W: no \'%s\' in unocodedata\n' % name return u except ImportError: warnings += 'W: no unicodedata available\n' U = lambda name: None from sympy.printing.conventions import split_super_sub # prefix conventions when constructing tables # L - LATIN i # G - GREEK beta # D - DIGIT 0 # S - SYMBOL + __all__ = ['greek','sub','sup','xsym','vobj','hobj','pretty_symbol'] _use_unicode = False def pretty_use_unicode(flag = None): """Set whether pretty-printer should use unicode by default""" global _use_unicode global warnings if flag is None: return _use_unicode if flag and warnings: # print warnings (if any) on first unicode usage print "I: pprint -- we are going to use unicode, but there are following problems:" print warnings warnings = '' use_unicode_prev = _use_unicode _use_unicode = flag return use_unicode_prev def pretty_try_use_unicode(): """See if unicode output is available and leverage it if possible""" try: symbols = [] # see, if we can represent greek alphabet for g,G in greek.itervalues(): symbols.append(g) symbols.append(G) # and atoms symbols += atoms_table.values() for s in symbols: if s is None: return # common symbols not present! encoding = getattr(sys.stdout, 'encoding', None) # this happens when e.g. stdout is redirected through a pipe, or is # e.g. a cStringIO.StringO if encoding is None: return # sys.stdout has no encoding # try to encode s.encode(encoding) except UnicodeEncodeError: pass else: pretty_use_unicode(True) def xstr(*args): """call str or unicode depending on current mode""" if _use_unicode: return unicode(*args) else: return str(*args) # GREEK g = lambda l: U('GREEK SMALL LETTER %s' % l.upper()) G = lambda l: U('GREEK CAPITAL LETTER %s' % l.upper()) greek_letters = [ 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lamda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega' ] # {} greek letter -> (g,G) greek = dict([(l, (g(l), G(l))) for l in greek_letters]) # aliases greek['lambda'] = greek['lamda'] digit_2txt = { '0' : 'ZERO', '1' : 'ONE', '2' : 'TWO', '3' : 'THREE', '4' : 'FOUR', '5' : 'FIVE', '6' : 'SIX', '7' : 'SEVEN', '8' : 'EIGHT', '9' : 'NINE', } symb_2txt = { '+' : 'PLUS SIGN', '-' : 'MINUS', '=' : 'EQUALS SIGN', '(' : 'LEFT PARENTHESIS', ')' : 'RIGHT PARENTHESIS', '[' : 'LEFT SQUARE BRACKET', ']' : 'RIGHT SQUARE BRACKET', '{' : 'LEFT CURLY BRACKET', '}' : 'RIGHT CURLY BRACKET', # non-std '{}' : 'CURLY BRACKET', 'sum': 'SUMMATION', 'int': 'INTEGRAL', } # SUBSCRIPT & SUPERSCRIPT LSUB = lambda letter: U('LATIN SUBSCRIPT SMALL LETTER %s' % letter.upper()) GSUB = lambda letter: U('GREEK SUBSCRIPT SMALL LETTER %s' % letter.upper()) DSUB = lambda digit: U('SUBSCRIPT %s' % digit_2txt[digit]) SSUB = lambda symb: U('SUBSCRIPT %s' % symb_2txt[symb]) LSUP = lambda letter: U('SUPERSCRIPT LATIN SMALL LETTER %s' % letter.upper()) DSUP = lambda digit: U('SUPERSCRIPT %s' % digit_2txt[digit]) SSUP = lambda symb: U('SUPERSCRIPT %s' % symb_2txt[symb]) sub = {} # symb -> subscript symbol sup = {} # symb -> superscript symbol # latin subscripts for l in 'aeioruvx': sub[l] = LSUB(l) for l in 'in': sup[l] = LSUP(l) for g in ['beta', 'gamma', 'rho', 'phi', 'chi']: sub[g] = GSUB(g) for d in [str(i) for i in range(10)]: sub[d] = DSUB(d) sup[d] = DSUP(d) for s in '+-=()': sub[s] = SSUB(s) sup[s] = SSUP(s) # VERTICAL OBJECTS HUP = lambda symb: U('%s UPPER HOOK' % symb_2txt[symb]) CUP = lambda symb: U('%s UPPER CORNER' % symb_2txt[symb]) MID = lambda symb: U('%s MIDDLE PIECE' % symb_2txt[symb]) EXT = lambda symb: U('%s EXTENSION' % symb_2txt[symb]) HLO = lambda symb: U('%s LOWER HOOK' % symb_2txt[symb]) CLO = lambda symb: U('%s LOWER CORNER' % symb_2txt[symb]) TOP = lambda symb: U('%s TOP' % symb_2txt[symb]) BOT = lambda symb: U('%s BOTTOM' % symb_2txt[symb]) # {} '(' -> (extension, start, end, middle) 1-character _xobj_unicode = { # vertical symbols # ext top bot mid c1 '(' : (( EXT('('), HUP('('), HLO('(') ), '('), ')' : (( EXT(')'), HUP(')'), HLO(')') ), ')'), '[' : (( EXT('['), CUP('['), CLO('[') ), '['), ']' : (( EXT(']'), CUP(']'), CLO(']') ), ']'), '{' : (( EXT('{}'), HUP('{'), HLO('{'), MID('{') ), '{'), '}' : (( EXT('{}'), HUP('}'), HLO('}'), MID('}') ), '}'), '|' : U('BOX DRAWINGS LIGHT VERTICAL'), 'lfloor' : (( EXT('['), EXT('['), CLO('[') ), U('LEFT FLOOR')), 'rfloor' : (( EXT(']'), EXT(']'), CLO(']') ), U('RIGHT FLOOR')), 'lceil' : (( EXT('['), CUP('['), EXT('[') ), U('LEFT CEILING')), 'rceil' : (( EXT(']'), CUP(']'), EXT(']') ), U('RIGHT CEILING')), 'int': (( EXT('int'), U('TOP HALF INTEGRAL'), U('BOTTOM HALF INTEGRAL') ), U('INTEGRAL')), #'sum': ( U('N-ARY SUMMATION'), TOP('sum'), None, None, BOT('sum') ), # horizontal objects #'-' : '-', '-' : U('BOX DRAWINGS LIGHT HORIZONTAL'), '_' : U('HORIZONTAL SCAN LINE-9'), # XXX symbol ok? # diagonal objects '\' & '/' ? '/' : U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'), '\\': U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), } _xobj_ascii = { # vertical symbols # ext top bot mid c1 '(' : (( '|', '/', '\\' ), '('), ')' : (( '|', '\\', '/' ), ')'), # XXX this looks ugly # '[' : (( '|', '-', '-' ), '['), # ']' : (( '|', '-', '-' ), ']'), # XXX not so ugly :( '[' : (( '[', '[', '[' ), '['), ']' : (( ']', ']', ']' ), ']'), '{' : (( '|', '/', '\\', '<' ), '{'), '}' : (( '|', '\\', '/', '>' ), '}'), '|' : '|', 'int': ( ' | ', ' /', '/ ' ), # horizontal objects '-' : '-', '_' : '_', # diagonal objects '\' & '/' ? '/' : '/', '\\': '\\', } def xobj(symb, length): """Construct spatial object of given length. return: [] of equal-length strings """ assert length > 0 # TODO robustify when no unicodedat available if _use_unicode: _xobj = _xobj_unicode else: _xobj = _xobj_ascii vinfo = _xobj[symb] c1 = top = bot = mid = None if not isinstance(vinfo, tuple): # 1 entry ext = vinfo else: if isinstance(vinfo[0], tuple): # (vlong), c1 vlong = vinfo[0] c1 = vinfo[1] else: # (vlong), c1 vlong = vinfo ext = vlong[0] try: top = vlong[1] bot = vlong[2] mid = vlong[3] except IndexError: pass if c1 is None: c1 = ext if top is None: top = ext if bot is None: bot = ext if mid is not None: if (length % 2) == 0: # even height, but we have to print it somehow anyway... # XXX is it ok? length += 1 else: mid = ext if length == 1: return c1 res = [] next= (length-2)//2 nmid= (length-2) - next*2 res += [top] res += [ext]*next res += [mid]*nmid res += [ext]*next res += [bot] return res def vobj(symb, height): """Construct vertical object of a given height see: xobj """ return '\n'.join( xobj(symb, height) ) def hobj(symb, width): """Construct horizontal object of a given width see: xobj """ return ''.join( xobj(symb, width) ) # RADICAL # n -> symbol root = { 2 : U('SQUARE ROOT'), # U('RADICAL SYMBOL BOTTOM') 3 : U('CUBE ROOT'), 4 : U('FOURTH ROOT'), } # RATIONAL VF = lambda txt: U('VULGAR FRACTION %s' % txt) # (p,q) -> symbol frac = { (1,2) : VF('ONE HALF'), (1,3) : VF('ONE THIRD'), (2,3) : VF('TWO THIRDS'), (1,4) : VF('ONE QUARTER'), (3,4) : VF('THREE QUARTERS'), (1,5) : VF('ONE FIFTH'), (2,5) : VF('TWO FIFTHS'), (3,5) : VF('THREE FIFTHS'), (4,5) : VF('FOUR FIFTHS'), (1,6) : VF('ONE SIXTH'), (5,6) : VF('FIVE SIXTHS'), (1,8) : VF('ONE EIGHTH'), (3,8) : VF('THREE EIGHTHS'), (5,8) : VF('FIVE EIGHTHS'), (7,8) : VF('SEVEN EIGHTHS'), } # atom symbols _xsym = { '==' : ( '=', '='), '<' : ( '<', '<'), '<=' : ('<=', U('LESS-THAN OR EQUAL TO')), '>=' : ('>=', U('GREATER-THAN OR EQUAL TO')), '!=' : ('!=', U('NOT EQUAL TO')), '*' : ('*', U('DOT OPERATOR')), } def xsym(sym): """get symbology for a 'character'""" op = _xsym[sym] if _use_unicode: return op[1] else: return op[0] # SYMBOLS atoms_table = { # class how-to-display 'Exp1' : U('SCRIPT SMALL E'), 'Pi' : U('GREEK SMALL LETTER PI'), 'Infinity' : U('INFINITY'), 'NegativeInfinity' : U('INFINITY') and ('-'+U('INFINITY')), # XXX what to do here #'ImaginaryUnit' : U('GREEK SMALL LETTER IOTA'), #'ImaginaryUnit' : U('MATHEMATICAL ITALIC SMALL I'), 'ImaginaryUnit' : U('DOUBLE-STRUCK ITALIC SMALL I'), 'EmptySet' : U('EMPTY SET'), 'Union' : U('UNION') } def pretty_atom(atom_name, default=None): """return pretty representation of an atom""" if _use_unicode: return atoms_table[atom_name] else: if default is not None: return default raise KeyError('only unicode') # send it default printer def pretty_symbol(symb_name): """return pretty representation of a symbol""" # let's split symb_name into symbol + index # UC: beta1 # UC: f_beta if not _use_unicode: return symb_name name, sups, subs = split_super_sub(symb_name) # let's prettify name gG = greek.get(name.lower()) if gG is not None: if name.islower(): greek_name = greek.get(name.lower())[0] else: greek_name = greek.get(name.lower())[1] # some letters may not be available if greek_name is not None: name = greek_name # Let's prettify sups/subs. If it fails at one of them, pretty sups/subs are # not used at all. def pretty_list(l, mapping): result = [] for s in l: pretty = mapping.get(s) if pretty is None: try: # match by separate characters pretty = ''.join([mapping[c] for c in s]) except KeyError: return None result.append(pretty) return result pretty_sups = pretty_list(sups, sup) if pretty_sups is not None: pretty_subs = pretty_list(subs, sub) else: pretty_subs = None # glue the results into one string if pretty_subs is None: # nice formatting of sups/subs did not work if len(sups) > 0: sups_result = '^' + '^'.join(sups) else: sups_result = '' if len(subs) > 0: subs_result = '_' + '_'.join(subs) else: subs_result = '' else: sups_result = ' '.join(pretty_sups) subs_result = ' '.join(pretty_subs) return ''.join([name, sups_result, subs_result]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/pretty.py0000644000175000017500000011121112014170666026156 0ustar georgeskgeorgeskfrom sympy.core import S, C, Basic, Interval from sympy.utilities import group from sympy.printing.printer import Printer from sympy.printing.str import sstr from stringpict import prettyForm, stringPict from pretty_symbology import xstr, hobj, vobj, xobj, xsym, pretty_symbol,\ pretty_atom, pretty_use_unicode, pretty_try_use_unicode, greek, U from sympy.core.compatibility import cmp_to_key # rename for usage from outside pprint_use_unicode = pretty_use_unicode pprint_try_use_unicode = pretty_try_use_unicode class PrettyPrinter(Printer): """Printer, which converts an expression into 2D ASCII-art figure.""" printmethod = "_pretty" _default_settings = { "order": None, "full_prec": "auto", "use_unicode": None, "wrap_line": True, } def __init__(self, settings=None): Printer.__init__(self, settings) self.emptyPrinter = lambda x: prettyForm(xstr(x)) @property def _use_unicode(self): if self._settings['use_unicode']: return True else: return pretty_use_unicode() def doprint(self, expr): return self._print(expr).render(**self._settings) # empty op so _print(stringPict) returns the same def _print_stringPict(self, e): return e def _print_basestring(self, e): return prettyForm(e) def _print_Symbol(self, e): symb = pretty_symbol(e.name) return prettyForm(symb) def _print_Float(self, e): # we will use StrPrinter's Float printer, but we need to handle the # full_prec ourselves, according to the self._print_level full_prec = self._settings["full_prec"] if full_prec == "auto": full_prec = self._print_level == 1 return prettyForm(sstr(e, full_prec=full_prec)) def _print_Atom(self, e): try: # print atoms like Exp1 or Pi return prettyForm(pretty_atom(e.__class__.__name__)) except KeyError: return self.emptyPrinter(e) # Infinity inherits from Rational, so we have to override _print_XXX order _print_Infinity = _print_Atom _print_NegativeInfinity = _print_Atom _print_EmptySet = _print_Atom def _print_factorial(self, e): x = e.args[0] pform = self._print(x) # Add parentheses if needed if not ((x.is_Integer and x.is_nonnegative) or x.is_Symbol): pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.right('!')) return pform def _print_binomial(self, e): n, k = e.args n_pform = self._print(n) k_pform = self._print(k) bar = ' '*max(n_pform.width(), k_pform.width()) pform = prettyForm(*k_pform.above(bar)) pform = prettyForm(*pform.above(n_pform)) pform = prettyForm(*pform.parens('(', ')')) pform.baseline = (pform.baseline + 1)//2 return pform def _print_Relational(self, e): op = prettyForm(' ' + xsym(e.rel_op) + ' ') l = self._print(e.lhs) r = self._print(e.rhs) pform = prettyForm(*stringPict.next(l, op, r)) return pform def _print_Not(self, e): if self._use_unicode: arg = e.args[0] pform = self._print(arg) if arg.is_Boolean and not arg.is_Not: pform = prettyForm(*pform.parens()) return prettyForm(*pform.left(u"\u00ac ")) else: return self._print_Function(e) def __print_Boolean(self, e, char): arg = e.args[0] pform = self._print(arg) if arg.is_Boolean and not arg.is_Not: pform = prettyForm(*pform.parens()) for arg in e.args[1:]: pform_arg = self._print(arg) if arg.is_Boolean and not arg.is_Not: pform_arg = prettyForm(*pform_arg.parens()) pform = prettyForm(*pform.right(u' %s ' % char)) pform = prettyForm(*pform.right(pform_arg)) return pform def _print_And(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u2227") else: return self._print_Function(e) def _print_Or(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u2228") else: return self._print_Function(e) def _print_Xor(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u22bb") else: return self._print_Function(e) def _print_Nand(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u22bc") else: return self._print_Function(e) def _print_Nor(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u22bd") else: return self._print_Function(e) def _print_Implies(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u2192") else: return self._print_Function(e) def _print_Equivalent(self, e): if self._use_unicode: return self.__print_Boolean(e, u"\u2261") else: return self._print_Function(e) def _print_conjugate(self, e): pform = self._print(e.args[0]) return prettyForm( *pform.above( hobj('_',pform.width())) ) def _print_Abs(self, e): pform = self._print(e.args[0]) pform = prettyForm(*pform.parens('|', '|')) return pform def _print_floor(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.parens('lfloor', 'rfloor')) return pform else: return self._print_Function(e) def _print_ceiling(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.parens('lceil', 'rceil')) return pform else: return self._print_Function(e) def _print_Derivative(self, deriv): # XXX use U('PARTIAL DIFFERENTIAL') here ? syms = list(reversed(deriv.variables)) x = None for sym, num in group(syms, multiple=False): s = self._print(sym) ds = prettyForm(*s.left('d')) if num > 1: ds = ds**prettyForm(str(num)) if x is None: x = ds else: x = prettyForm(*x.right(' ')) x = prettyForm(*x.right(ds)) f = prettyForm(binding=prettyForm.FUNC, *self._print(deriv.expr).parens()) pform = prettyForm('d') if len(syms) > 1: pform = pform**prettyForm(str(len(syms))) pform = prettyForm(*pform.below(stringPict.LINE, x)) pform.baseline = pform.baseline + 1 pform = prettyForm(*stringPict.next(pform, f)) return pform def _print_PDF(self, pdf): lim = self._print(pdf.pdf.args[0]) lim = prettyForm(*lim.right(', ')) lim = prettyForm(*lim.right(self._print(pdf.domain[0]))) lim = prettyForm(*lim.right(', ')) lim = prettyForm(*lim.right(self._print(pdf.domain[1]))) lim = prettyForm(*lim.parens()) f = self._print(pdf.pdf.args[1]) f = prettyForm(*f.right(', ')) f = prettyForm(*f.right(lim)) f = prettyForm(*f.parens()) pform = prettyForm('PDF') pform = prettyForm(*pform.right(f)) return pform def _print_Integral(self, integral): f = integral.function # Add parentheses if arg involves addition of terms and # create a pretty form for the argument prettyF = self._print(f) # XXX generalize parens if f.is_Add: prettyF = prettyForm(*prettyF.parens()) # dx dy dz ... arg = prettyF for x in integral.limits: prettyArg = self._print(x[0]) # XXX qparens (parens if needs-parens) if prettyArg.width() > 1: prettyArg = prettyForm(*prettyArg.parens()) arg = prettyForm(*arg.right(' d', prettyArg)) # \int \int \int ... firstterm = True s = None for lim in integral.limits: x = lim[0] # Create bar based on the height of the argument h = arg.height() H = h+2 # XXX hack! ascii_mode = not self._use_unicode if ascii_mode: H += 2 vint= vobj('int', H) # Construct the pretty form with the integral sign and the argument pform = prettyForm(vint) #pform.baseline = pform.height()//2 # vcenter pform.baseline = arg.baseline + (H-h)//2 # covering the whole argument if len(lim) > 1: # Create pretty forms for endpoints, if definite integral. # Do not print empty endpoints. if len(lim) == 2: prettyA = prettyForm("") prettyB = self._print(lim[1]) if len(lim) == 3: prettyA = self._print(lim[1]) prettyB = self._print(lim[2]) if ascii_mode: # XXX hack # Add spacing so that endpoint can more easily be # identified with the correct integral sign spc = max(1, 3 - prettyB.width()) prettyB = prettyForm(*prettyB.left(' ' * spc)) spc = max(1, 4 - prettyA.width()) prettyA = prettyForm(*prettyA.right(' ' * spc)) pform = prettyForm(*pform.above(prettyB)) pform = prettyForm(*pform.below(prettyA)) #if ascii_mode: # XXX hack # # too much vspace beetween \int and argument # # but I left it as is # pform = prettyForm(*pform.right(' ')) if not ascii_mode: # XXX hack pform = prettyForm(*pform.right(' ')) if firstterm: s = pform # first term firstterm = False else: s = prettyForm(*s.left(pform)) pform = prettyForm(*arg.left(s)) return pform def _print_Sum(self, expr): def asum(hrequired, lower, upper): def adjust(s, wid=None, how='<^>'): if not wid or len(s)>wid: return s need = wid - len(s) if how == '<^>' or how == "<" or how not in list('<^>'): return s + ' '*need half = need//2 lead = ' '*half if how == ">": return " "*need + s return lead + s + ' '*(need - len(lead)) h = max(hrequired, 2) d = h//2 wrequired = max(lower, upper) w = d + 1 more = hrequired % 2 lines = [] lines.append("_"*(w) + ' ') lines.append("\%s`" % (' '*(w - 1))) for i in range(1, d): lines.append('%s\\%s' % (' '*i, ' '*(w - i))) if more: lines.append('%s)%s' % (' '*(d), ' '*(w - d))) for i in reversed(range(1, d)): lines.append('%s/%s' % (' '*i, ' '*(w - i))) lines.append("/" + "_"*(w - 1) + ',') return d, h + more, lines f = expr.function prettyF = self._print(f) if f.is_Add: # add parens prettyF = prettyForm(*prettyF.parens()) H = prettyF.height() + 2 # \sum \sum \sum ... first = True max_upper = 0 sign_height = 0 for lim in expr.limits: if len(lim) == 3: prettyUpper = self._print(lim[2]) prettyLower = self._print(C.Equality(lim[0], lim[1])) elif len(lim) == 2: prettyUpper = self._print("") prettyLower = self._print(C.Equality(lim[0], lim[1])) elif len(lim) == 1: prettyUpper = self._print("") prettyLower = self._print(lim[0]) max_upper = max(max_upper, prettyUpper.height()) # Create sum sign based on the height of the argument d, h, slines = asum(H, prettyLower.width(), prettyUpper.width()) prettySign = stringPict('') prettySign = prettyForm(*prettySign.stack(*slines)) if first: sign_height = prettySign.height() prettySign = prettyForm(*prettySign.above(prettyUpper)) prettySign = prettyForm(*prettySign.below(prettyLower)) if first: # change F baseline so it centers on the sign prettyF.baseline -= d - (prettyF.height()//2 - prettyF.baseline) first = False # put padding to the right pad = stringPict('') pad = prettyForm(*pad.stack(*[' ']*h)) prettySign = prettyForm(*prettySign.right(pad)) # put the present prettyF to the right prettyF = prettyForm(*prettySign.right(prettyF)) prettyF.baseline = max_upper + sign_height//2 return prettyF def _print_Limit(self, l): # XXX we do not print dir ... e, z, z0, dir = l.args E = self._print(e) Lim = prettyForm('lim') LimArg = self._print(z) LimArg = prettyForm(*LimArg.right('->')) LimArg = prettyForm(*LimArg.right(self._print(z0))) Lim = prettyForm(*Lim.below(LimArg)) Lim = prettyForm(*Lim.right(E)) return Lim # Matrix is special: # # it can exist in SymPy in two forms: # - as Matrix # - as _MatrixAsBasic # # see _MatrixAsBasic docstring, and #420 def _print__MatrixAsBasic(self, e): return self._print_Matrix(e.m) def _print_Matrix(self, e): M = e # matrix Ms = {} # i,j -> pretty(M[i,j]) for i in range(M.rows): for j in range(M.cols): Ms[i,j] = self._print(M[i,j]) # h- and v- spacers hsep = 2 vsep = 1 # max width for columns maxw = [-1] * M.cols for j in range(M.cols): maxw[j] = max([Ms[i,j].width() for i in range(M.rows)]) # drawing result D = None for i in range(M.rows): D_row = None for j in range(M.cols): s = Ms[i,j] # reshape s to maxw # XXX this should be generalized, and go to stringPict.reshape ? assert s.width() <= maxw[j] # hcenter it, +0.5 to the right 2 # ( it's better to align formula starts for say 0 and r ) # XXX this is not good in all cases -- maybe introduce vbaseline? wdelta = maxw[j] - s.width() wleft = wdelta // 2 wright = wdelta - wleft s = prettyForm(*s.right(' '*wright)) s = prettyForm(*s.left (' '*wleft)) # we don't need vcenter cells -- this is automatically done in # a pretty way because when their baselines are taking into # account in .right() if D_row is None: D_row = s # first box in a row continue D_row = prettyForm(*D_row.right(' '*hsep)) # h-spacer D_row = prettyForm(*D_row.right(s)) if D is None: D = D_row # first row in a picture continue # v-spacer for _ in range(vsep): D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) if D is None: D = prettyForm('') # Empty Matrix D = prettyForm(*D.parens('[',']')) return D def _print_Piecewise(self, pexpr): P = {} for n, ec in enumerate(pexpr.args): P[n,0] = self._print(ec.expr) if ec.cond == True: P[n,1] = prettyForm('otherwise') else: P[n,1] = prettyForm(*prettyForm('for ').right(self._print(ec.cond))) hsep = 2 vsep = 1 len_args = len(pexpr.args) # max widths maxw = [max([P[i,j].width() for i in xrange(len_args)]) \ for j in xrange(2)] # FIXME: Refactor this code and matrix into some tabular environment. # drawing result D = None for i in xrange(len_args): D_row = None for j in xrange(2): p = P[i,j] assert p.width() <= maxw[j] wdelta = maxw[j] - p.width() wleft = wdelta // 2 wright = wdelta - wleft p = prettyForm(*p.right(' '*wright)) p = prettyForm(*p.left (' '*wleft)) if D_row is None: D_row = p continue D_row = prettyForm(*D_row.right(' '*hsep)) # h-spacer D_row = prettyForm(*D_row.right(p)) if D is None: D = D_row # first row in a picture continue # v-spacer for _ in range(vsep): D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) D = prettyForm(*D.parens('{','')) return D def _hprint_vec(self, v): D = None for a in v: p = a if D is None: D = p else: D = prettyForm(*D.right(', ')) D = prettyForm(*D.right(p)) if D is None: D = stringPict(' ') return D def _hprint_vseparator(self, p1, p2): tmp = prettyForm(*p1.right(p2)) sep = stringPict(vobj('|', tmp.height()), baseline=tmp.baseline) return prettyForm(*p1.right(sep, p2)) def _print_hyper(self, e): # FIXME refactor Matrix, Piecewise, and this into a tabular environment ap = [self._print(a) for a in e.ap] bq = [self._print(b) for b in e.bq] P = self._print(e.argument) # Drawing result - first create the ap, bq vectors D = None for v in [ap, bq]: D_row = self._hprint_vec(v) if D is None: D = D_row # first row in a picture else: D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) # make sure that the argument `z' is centred vertically D.baseline = D.height()/2 # insert horizontal separator P = prettyForm(*P.left(' ')) D = prettyForm(*D.right(' ')) # insert separating `|` D = self._hprint_vseparator(D, P) # add parens D = prettyForm(*D.parens('(', ')')) # create the F symbol above = D.height()/2 - 1 below = D.height() - above - 1 if self._use_unicode: pic = (2, 0, 2, u'\u250c\u2500\n\u251c\u2500\n\u2575') else: pic = ((3, 0, 3, ' _\n|_\n|\n')) add = 0 sz, t, b, img = pic F = prettyForm('\n' * (above - t) + img + '\n' * (below - b), baseline = above + sz) add = (sz+1)/2 F = prettyForm(*F.left(self._print(len(e.ap)))) F = prettyForm(*F.right(self._print(len(e.bq)))) F.baseline = above + add D = prettyForm(*F.right(' ', D)) return D def _print_meijerg(self, e): # FIXME refactor Matrix, Piecewise, and this into a tabular environment v = {} v[(0, 0)] = [self._print(a) for a in e.an] v[(0, 1)] = [self._print(a) for a in e.aother] v[(1, 0)] = [self._print(b) for b in e.bm] v[(1, 1)] = [self._print(b) for b in e.bother] P = self._print(e.argument) vp = {} for idx in v: vp[idx] = self._hprint_vec(v[idx]) for i in range(2): maxw = max(vp[(0, i)].width(), vp[(1, i)].width()) for j in range(2): s = vp[(j, i)] left = (maxw - s.width()) // 2 right = maxw - left - s.width() s = prettyForm(*s.left(' ' * left)) s = prettyForm(*s.right(' ' * right)) vp[(j, i)] = s D1 = prettyForm(*vp[(0, 0)].right(' ', vp[(0, 1)])) D1 = prettyForm(*D1.below(' ')) D2 = prettyForm(*vp[(1, 0)].right(' ', vp[(1, 1)])) D = prettyForm(*D1.below(D2)) # make sure that the argument `z' is centred vertically D.baseline = D.height()/2 # insert horizontal separator P = prettyForm(*P.left(' ')) D = prettyForm(*D.right(' ')) # insert separating `|` D = self._hprint_vseparator(D, P) # add parens D = prettyForm(*D.parens('(', ')')) # create the G symbol above = D.height()/2 - 1 below = D.height() - above - 1 if self._use_unicode: pic = (3, 0, 3, 1, u'\u256d\u2500\u256e\n\u2502\u2576\u2510\n\u2570\u2500\u256f') else: pic = (3, 0, 3, 1, ' __\n/__\n\_|') add = 0 sz, t, b, add, img = pic F = prettyForm('\n' * (above - t) + img + '\n' * (below - b), baseline = above + sz) pp = self._print(len(e.ap)) pq = self._print(len(e.bq)) pm = self._print(len(e.bm)) pn = self._print(len(e.an)) pu = prettyForm(*pm.right(', ', pn)) pl = prettyForm(*pp.right(', ', pq)) ht = F.baseline - above - 2 if ht > 0: pu = prettyForm(*pu.below('\n'*ht)) p = prettyForm(*pu.below(pl)) F.baseline = above F = prettyForm(*F.right(p)) F.baseline = above + add D = prettyForm(*F.right(' ', D)) return D def _print_exp(self, e): base = prettyForm(pretty_atom('Exp1', 'e')) return base ** self._print(e.args[0]) def _print_Function(self, e): # XXX works only for applied functions func = e.func args = e.args n = len(args) func_name = func.__name__ prettyFunc = self._print(C.Symbol(func_name)) prettyArgs = prettyForm(*self._print_seq(args).parens()) pform = prettyForm(binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyArgs return pform def _print_Lambda(self, e): symbols, expr = e.args if len(symbols) == 1: symbols = self._print(symbols[0]) else: symbols = self._print(tuple(symbols)) args = (symbols, self._print(expr)) prettyFunc = self._print(C.Symbol("Lambda")) prettyArgs = prettyForm(*self._print_seq(args).parens()) pform = prettyForm(binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)) # store pform parts so it can be reassembled e.g. when powered pform.prettyFunc = prettyFunc pform.prettyArgs = prettyArgs return pform def _print_gamma(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(greek['gamma'][1])) return pform else: return self._print_Function(e) def _print_uppergamma(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.right(', ', self._print(e.args[1]))) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(greek['gamma'][1])) return pform else: return self._print_Function(e) def _print_lowergamma(self, e): if self._use_unicode: pform = self._print(e.args[0]) pform = prettyForm(*pform.right(', ', self._print(e.args[1]))) pform = prettyForm(*pform.parens()) pform = prettyForm(*pform.left(greek['gamma'][0])) return pform else: return self._print_Function(e) def _print_Add(self, expr, order=None): terms = self._as_ordered_terms(expr, order=order) pforms, indices = [], [] def pretty_negative(pform, index): """Prepend a minus sign to a pretty form. """ if index == 0: if pform.height() > 1: pform_neg = '- ' else: pform_neg = '-' else: pform_neg = ' - ' pform = stringPict.next(pform_neg, pform) return prettyForm(binding=prettyForm.NEG, *pform) for i, term in enumerate(terms): if term.is_Mul and term.as_coeff_mul()[0] < 0: pform = self._print(-term) pforms.append(pretty_negative(pform, i)) elif term.is_Rational and term.q > 1: pforms.append(None) indices.append(i) elif term.is_Number and term < 0: pform = self._print(-term) pforms.append(pretty_negative(pform, i)) else: pforms.append(self._print(term)) if indices: large = True for pform in pforms: if pform is not None and pform.height() > 1: break else: large = False for i in indices: term, negative = terms[i], False if term < 0: term, negative = -term, True if large: pform = prettyForm(str(term.p))/prettyForm(str(term.q)) else: pform = self._print(term) if negative: pform = pretty_negative(pform, i) pforms[i] = pform return prettyForm.__add__(*pforms) def _print_Mul(self, product): a = [] # items in the numerator b = [] # items that are in the denominator (if any) if self.order != 'old': args = product.as_ordered_factors() else: args = product.args # Gather terms for numerator/denominator for item in args: if item.is_Pow and item.exp.is_Rational and item.exp.is_negative: b.append(C.Pow(item.base, -item.exp)) elif item.is_Rational and item is not S.Infinity: if item.p != 1: a.append( C.Rational(item.p) ) if item.q != 1: b.append( C.Rational(item.q) ) else: a.append(item) # Convert to pretty forms. Add parens to Add instances if there # is more than one term in the numer/denom for i in xrange(0, len(a)): if a[i].is_Add and len(a) > 1: a[i] = prettyForm(*self._print(a[i]).parens()) else: a[i] = self._print(a[i]) for i in xrange(0, len(b)): if b[i].is_Add and len(b) > 1: b[i] = prettyForm(*self._print(b[i]).parens()) else: b[i] = self._print(b[i]) # Construct a pretty form if len(b) == 0: return prettyForm.__mul__(*a) else: if len(a) == 0: a.append( self._print(S.One) ) return prettyForm.__mul__(*a) / prettyForm.__mul__(*b) def _print_Pow(self, power): # square roots, other roots or n-th roots #test for fraction 1/n or power x**-1 if (isinstance(power.exp, C.Rational) and power.exp.p==1 and power.exp.q !=1) or \ ( isinstance(power.exp, C.Pow) and isinstance(power.exp.args[0], C.Symbol) and power.exp.args[1]==S.NegativeOne): bpretty = self._print(power.base) #construct root sign, start with the \/ shape _zZ= xobj('/',1) rootsign = xobj('\\',1)+_zZ #make exponent number to put above it if isinstance(power.exp, C.Rational): exp = str(power.exp.q) if exp=='2': exp = '' else: exp = str(power.exp.args[0]) exp = exp.ljust(2) if len(exp)>2: rootsign = ' '*(len(exp)-2)+rootsign #stack the exponent rootsign = stringPict(exp+'\n'+rootsign) rootsign.baseline = 0 #diagonal: length is one less than height of base linelength = bpretty.height()-1 diagonal = stringPict('\n'.join( ' '*(linelength-i-1)+_zZ+' '*i for i in range(linelength) )) #put baseline just below lowest line: next to exp diagonal.baseline = linelength-1 #make the root symbol rootsign = prettyForm(*rootsign.right(diagonal)) #set the baseline to match contents to fix the height #but if the height of bpretty is one, the rootsign must be one higher rootsign.baseline = max(1, bpretty.baseline) #build result s = prettyForm(hobj('_', 2+ bpretty.width())) s = prettyForm(*bpretty.above(s)) s = prettyForm(*s.left(rootsign)) return s elif power.exp.is_Rational and power.exp.is_negative: # Things like 1/x return prettyForm("1") / self._print(C.Pow(power.base, -power.exp)) # None of the above special forms, do a standard power b,e = power.as_base_exp() return self._print(b)**self._print(e) def __print_numer_denom(self, p, q): if q == 1: if p < 0: return prettyForm(str(p),binding=prettyForm.NEG) else: return prettyForm(str(p)) elif abs(p) >= 10 and abs(q) >= 10: # If more than one digit in numer and denom, print larger fraction if p < 0: pform = prettyForm(str(-p))/prettyForm(str(q)) return prettyForm(binding=prettyForm.NEG, *pform.left('- ')) else: return prettyForm(str(p))/prettyForm(str(q)) else: return None def _print_Rational(self, expr): result = self.__print_numer_denom(expr.p, expr.q) if result is not None: return result else: return self.emptyPrinter(expr) def _print_Fraction(self, expr): result = self.__print_numer_denom(expr.numerator, expr.denominator) if result is not None: return result else: return self.emptyPrinter(expr) def _print_ProductSet(self, p): prod_char = u'\xd7' return self._print_seq(p.sets, None, None, ' %s '%prod_char, parenthesize = lambda set:set.is_Union ) def _print_FiniteSet(self, s): if len(s) > 10: # Take ten elements from the set at random q = iter(s) printset = [q.next() for i in xrange(10)] printset.append('...') else: printset = s try: printset = sorted(printset) except: pass return self._print_seq(printset, '{', '}', ', ' ) def _print_Interval(self, i): if i.start == i.end: return self._print_seq(i.args[:1], '{', '}') else: if i.left_open: left = '(' else: left = '[' if i.right_open: right = ')' else: right = ']' return self._print_seq(i.args[:2], left, right) def _print_Union(self, u): union_delimiter = ' %s ' % pretty_atom('Union') return self._print_seq(u.args, None, None, union_delimiter, parenthesize = lambda set:set.is_ProductSet) def _print_seq(self, seq, left=None, right=None, delimiter=', ', parenthesize = lambda x:False): s = None for item in seq: pform = self._print(item) if parenthesize(item): pform = prettyForm(*pform.parens()) if s is None: # first element s = pform else: s = prettyForm(*stringPict.next(s, delimiter)) s = prettyForm(*stringPict.next(s, pform)) if s is None: s = stringPict('') s = prettyForm(*s.parens(left, right, ifascii_nougly=True)) return s def _print_list(self, l): return self._print_seq(l, '[', ']') def _print_tuple(self, t): if len(t) == 1: ptuple = prettyForm(*stringPict.next(self._print(t[0]), ',')) return prettyForm(*ptuple.parens('(', ')', ifascii_nougly=True)) else: return self._print_seq(t, '(', ')') def _print_dict(self, d): items = [] keys = d.keys() keys.sort( key=cmp_to_key(Basic.compare_pretty) ) for k in keys: K = self._print(k) V = self._print(d[k]) s = prettyForm(*stringPict.next(K, ': ', V)) items.append(s) return self._print_seq(items, '{', '}') def __print_set(self, set_): items = list(set_) items.sort( key=cmp_to_key(Basic.compare_pretty) ) s = self._print_seq(items, '(', ')') s = prettyForm(*stringPict.next(type(set_).__name__, s)) return s _print_set = __print_set _print_frozenset = __print_set def _print_AlgebraicNumber(self, expr): if expr.is_aliased: return self._print(expr.as_poly().as_expr()) else: return self._print(expr.as_expr()) def _print_RootOf(self, expr): args = [self._print_Add(expr.expr, order='lex'), expr.index] pform = prettyForm(*self._print_seq(args).parens()) pform = prettyForm(*pform.left('RootOf')) return pform def _print_RootSum(self, expr): args = [self._print_Add(expr.expr, order='lex')] if expr.fun is not S.IdentityFunction: args.append(self._print(expr.fun)) pform = prettyForm(*self._print_seq(args).parens()) pform = prettyForm(*pform.left('RootSum')) return pform def _print_FiniteField(self, expr): if self._use_unicode: form = u'\u2124_%d' else: form = 'GF(%d)' return prettyForm(pretty_symbol(form % expr.mod)) def _print_IntegerRing(self, expr): if self._use_unicode: return prettyForm(u'\u2124') else: return prettyForm('ZZ') def _print_RationalField(self, expr): if self._use_unicode: return prettyForm(u'\u211A') else: return prettyForm('QQ') def _print_RealDomain(self, expr): if self._use_unicode: return prettyForm(u'\u211D') else: return prettyForm('RR') def _print_ComplexDomain(self, expr): if self._use_unicode: return prettyForm(u'\u2102') else: return prettyForm('CC') def _print_PolynomialRing(self, expr): pform = self._print_seq(expr.gens, '[', ']') pform = prettyForm(*pform.left(self._print(expr.dom))) return pform def _print_FractionField(self, expr): pform = self._print_seq(expr.gens, '(', ')') pform = prettyForm(*pform.left(self._print(expr.dom))) return pform def _print_Subs(self, e): pform = self._print(e.expr) pform = prettyForm(*pform.parens()) h = pform.height() if pform.height() > 1 else 2 rvert = stringPict(vobj('|', h), baseline=pform.baseline) pform = prettyForm(*pform.right(rvert)) b = pform.baseline pform.baseline = pform.height() - 1 pform = prettyForm(*pform.right(self._print_seq([ self._print_seq((self._print(v[0]), xsym('=='), self._print(v[1])), delimiter='') for v in zip(e.variables, e.point) ]))) pform.baseline = b return pform def pretty(expr, **settings): """ Returns a string containing the prettified form of expr. Arguments --------- expr: the expression to print wrap_line: line wrapping enabled/disabled, should be a boolean value (default to True) use_unicode: use unicode characters, such as the Greek letter pi instead of the string pi. Values should be boolean or None full_prec: use full precision. Default to "auto" """ pp = PrettyPrinter(settings) # XXX: this is an ugly hack, but at least it works use_unicode = pp._settings['use_unicode'] uflag = pretty_use_unicode(use_unicode) try: return pp.doprint(expr) finally: pretty_use_unicode(uflag) def pretty_print(expr, **settings): """ Prints expr in pretty form. pprint is just a shortcut for this function """ print pretty(expr, **settings) pprint = pretty_print wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/tests/0000755000175000017500000000000012014170666025422 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/tests/test_pretty.py0000644000175000017500000014200312014170666030362 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from sympy import (Basic, Matrix, Piecewise, Ne, symbols, sqrt, Function, Rational, conjugate, Derivative, tan, Function, log, floor, Symbol, pprint, sqrt, factorial, binomial, pi, sin, ceiling, pprint_use_unicode, I, S, Limit, oo, cos, Pow, Integral, exp, Eq, Lt, Gt, Ge, Le, gamma, Abs, RootOf, RootSum, Lambda, Not, And, Or, Xor, Nand, Nor, Implies, Equivalent, Sum, Subs, FF, ZZ, QQ, RR, O, uppergamma, lowergamma, hyper, meijerg) from sympy.printing.pretty import pretty as xpretty from sympy.printing.pretty import pprint from sympy.physics.units import joule from sympy.utilities.pytest import raises a, b, x, y, z, k = symbols('a,b,x,y,z,k') th = Symbol('theta') ph = Symbol('phi') """ Expressions whose pretty-printing is tested here: (A '#' to the right of an expression indicates that its various acceptable orderings are accounted for by the tests.) BASIC EXPRESSIONS: oo (x**2) 1/x y*x**-2 x**Rational(-5,2) (-2)**x Pow(3, 1, evaluate=False) (x**2 + x + 1) # 1-x # 1-2*x # x/y -x/y (x+2)/y # (1+x)*y #3 -5*x/(x+10) # correct placement of negative sign 1 - Rational(3,2)*(x+1) -(-x + 5)*(-x - 2*2**(1/S(2)) + 5) - (-y + 5)*(-y + 5) # Issue 2425 ORDERING: x**2 + x + 1 1 - x 1 - 2*x 2*x**4 + y**2 - x**2 + y**3 RELATIONAL: Eq(x, y) Lt(x, y) Gt(x, y) Le(x, y) Ge(x, y) Ne(x/(y+1), y**2) # RATIONAL NUMBERS: y*x**-2 y**Rational(3,2) * x**Rational(-5,2) sin(x)**3/tan(x)**2 FUNCTIONS (ABS, CONJ, EXP, FUNCTION BRACES, FACTORIAL, FLOOR, CEILING): (2*x + exp(x)) # Abs(x) Abs(x/(x**2+1)) # Abs(1 / (y - Abs(x))) factorial(n) factorial(2*n) factorial(factorial(factorial(n))) factorial(n+1) # conjugate(x) conjugate(f(x+1)) # f(x) f(x, y) f(x/(y+1), y) # sin(x)**2 conjugate(a+b*I) conjugate(exp(a+b*I)) conjugate( f(1 + conjugate(f(x))) ) # f(x/(y+1), y) # denom of first arg floor(1 / (y - floor(x))) ceiling(1 / (y - ceiling(x))) SQRT: sqrt(2) 2**Rational(1,3) 2**Rational(1,1000) sqrt(x**2 + 1) (1 + sqrt(5))**Rational(1,3) 2**(1/x) sqrt(2+pi) (2+(1+x**2)/(2+x))**Rational(1,4)+(1+x**Rational(1,1000))/sqrt(3+x**2) DERIVATIVES: Derivative(log(x), x, evaluate=False) Derivative(log(x), x, evaluate=False) + x # Derivative(log(x) + x**2, x, y, evaluate=False) Derivative(2*x*y, y, x, evaluate=False) + x**2 # beta(alpha).diff(alpha) INTEGRALS: Integral(log(x), x) Integral(x**2, x) Integral((sin(x))**2 / (tan(x))**2) Integral(x**(2**x), x) Integral(x**2, (x,1,2)) Integral(x**2, (x,Rational(1,2),10)) Integral(x**2*y**2, x,y) Integral(x**2, (x, None, 1)) Integral(x**2, (x, 1, None)) Integral(sin(th)/cos(ph), (th,0,pi), (ph, 0, 2*pi)) MATRICES: Matrix([[x**2+1, 1], [y, x+y]]) # Matrix([[x/y, y, th], [0, exp(I*k*ph), 1]]) PIECEWISE: Piecewise((x,x<1),(x**2,True)) SEQUENCES (TUPLES, LISTS, DICTIONARIES): () [] {} (1/x,) [x**2, 1/x, x, y, sin(th)**2/cos(ph)**2] (x**2, 1/x, x, y, sin(th)**2/cos(ph)**2) {x: sin(x)} {1/x: 1/y, x: sin(x)**2} # [x**2] (x**2,) {x**2: 1} LIMITS: Limit(x, x, oo) Limit(x**2, x, 0) Limit(1/x, x, 0) Limit(sin(x)/x, x, 0) UNITS: joule => kg*m**2/s SUBS: Subs(f(x), x, ph**2) Subs(f(x).diff(x), x, 0) Subs(f(x).diff(x)/y, (x, y), (0, Rational(1, 2))) """ def pretty(expr, order=None): """ASCII pretty-printing""" return xpretty(expr, order=order, use_unicode=False) def upretty(expr, order=None): """Unicode pretty-printing""" return xpretty(expr, order=order, use_unicode=True) def test_pretty_ascii_str(): """Different acceptable outputs are listed here because of a disagreement between Python 2.4 and 2.6 on the output of repr('xxx'). """ assert pretty( 'xxx' ) in ["'xxx'", 'xxx'] assert pretty( "xxx" ) in ["'xxx'", 'xxx'] assert pretty( 'xxx\'xxx' ) in ['"xxx\'xxx"', 'xxx\'xxx'] assert pretty( 'xxx"xxx' ) in ["'xxx\"xxx'", 'xxx\"xxx'] assert pretty( 'xxx\"xxx' ) in ["'xxx\"xxx'", 'xxx\"xxx'] assert pretty( "xxx'xxx" ) in ['"xxx\'xxx"', 'xxx\'xxx'] assert pretty( "xxx\'xxx" ) in ['"xxx\'xxx"', 'xxx\'xxx'] assert pretty( "xxx\"xxx" ) in ["'xxx\"xxx'", 'xxx\"xxx'] assert pretty( "xxx\"xxx\'xxx" ) in ['\'xxx"xxx\\\'xxx\'', 'xxx"xxx\'xxx'] assert pretty( "xxx\nxxx" ) in ["'xxx\nxxx'", 'xxx\nxxx'] def test_pretty_unicode_str(): """Different acceptable outputs are listed here because of a disagreement between Python 2.4 and 2.6 on the output of repr(u'xxx'). """ assert pretty( u'xxx' ) in [u"'xxx'", u'xxx'] assert pretty( u"xxx" ) in [u"'xxx'", u'xxx'] assert pretty( u'xxx\'xxx' ) in [u'"xxx\'xxx"', u'xxx\'xxx'] assert pretty( u'xxx"xxx' ) in [u"'xxx\"xxx'", u'xxx\"xxx'] assert pretty( u'xxx\"xxx' ) in [u"'xxx\"xxx'", u'xxx\"xxx'] assert pretty( u"xxx'xxx" ) in [u'"xxx\'xxx"', u'xxx\'xxx'] assert pretty( u"xxx\'xxx" ) in [u'"xxx\'xxx"', u'xxx\'xxx'] assert pretty( u"xxx\"xxx" ) in [u"'xxx\"xxx'", u'xxx\"xxx'] assert pretty( u"xxx\"xxx\'xxx" ) in [u'\'xxx"xxx\\\'xxx\'', u'xxx"xxx\'xxx'] assert pretty( u"xxx\nxxx" ) in [u"'xxx\nxxx'", u'xxx\nxxx'] def test_upretty_greek(): assert upretty( oo ) == u'∞' assert upretty( Symbol('alpha^+_1') ) == u'α⁺₁' assert upretty( Symbol('beta') ) == u'β' assert upretty(Symbol('lambda')) == u'λ' def test_upretty_multiindex(): assert upretty( Symbol('beta12') ) == u'β₁₂' assert upretty( Symbol('Y00') ) == u'Y₀₀' assert upretty( Symbol('Y_00') ) == u'Y₀₀' assert upretty( Symbol('F^+-') ) == u'F⁺⁻' def test_upretty_sub_super(): assert upretty( Symbol('beta_1_2') ) == u'β₁ ₂' assert upretty( Symbol('beta^1^2') ) == u'β¹ ²' assert upretty( Symbol('beta_1^2') ) == u'β²₁' assert upretty( Symbol('beta_10_20') ) == u'β₁₀ ₂₀' assert upretty( Symbol('beta_ax_gamma^i') ) == u'βⁱₐₓ ᵧ' assert upretty( Symbol("F^1^2_3_4") ) == u'F¹ ²₃ ₄' assert upretty( Symbol("F_1_2^3^4") ) == u'F³ ⁴₁ ₂' assert upretty( Symbol("F_1_2_3_4") ) == u'F₁ ₂ ₃ ₄' assert upretty( Symbol("F^1^2^3^4") ) == u'F¹ ² ³ ⁴' def test_upretty_subs_missingin_24(): assert upretty( Symbol('F_beta') ) == u'Fᵦ' assert upretty( Symbol('F_gamma') ) == u'Fᵧ' assert upretty( Symbol('F_rho') ) == u'Fᵨ' assert upretty( Symbol('F_phi') ) == u'Fᵩ' assert upretty( Symbol('F_chi') ) == u'Fᵪ' assert upretty( Symbol('F_a') ) == u'Fₐ' assert upretty( Symbol('F_e') ) == u'Fₑ' assert upretty( Symbol('F_i') ) == u'Fᵢ' assert upretty( Symbol('F_o') ) == u'Fₒ' assert upretty( Symbol('F_u') ) == u'Fᵤ' assert upretty( Symbol('F_r') ) == u'Fᵣ' assert upretty( Symbol('F_v') ) == u'Fᵥ' assert upretty( Symbol('F_x') ) == u'Fₓ' def test_pretty_basic(): assert pretty( -Rational(1)/2 ) == '-1/2' assert pretty( -Rational(13)/22 ) == \ """\ 13\n\ - --\n\ 22\ """ expr = oo ascii_str = \ """\ oo\ """ ucode_str = \ u"""\ ∞\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2) ascii_str = \ """\ 2\n\ x \ """ ucode_str = \ u"""\ 2\n\ x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 1/x ascii_str = \ """\ 1\n\ -\n\ x\ """ ucode_str = \ u"""\ 1\n\ ─\n\ x\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = y*x**-2 ascii_str = \ """\ y \n\ --\n\ 2\n\ x \ """ ucode_str = \ u"""\ y \n\ ──\n\ 2\n\ x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = x**Rational(-5,2) ascii_str = \ """\ 1 \n\ ----\n\ 5/2\n\ x \ """ ucode_str = \ u"""\ 1 \n\ ────\n\ 5/2\n\ x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (-2)**x ascii_str = \ """\ x\n\ (-2) \ """ ucode_str = \ u"""\ x\n\ (-2) \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str # See issue 1824 expr = Pow(3, 1, evaluate=False) ascii_str = \ """\ 1\n\ 3 \ """ ucode_str = \ u"""\ 1\n\ 3 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2 + x + 1) ascii_str_1 = \ """\ 2\n\ 1 + x + x \ """ ascii_str_2 = \ """\ 2 \n\ x + x + 1\ """ ascii_str_3 = \ """\ 2 \n\ x + 1 + x\ """ ucode_str_1 = \ u"""\ 2\n\ 1 + x + x \ """ ucode_str_2 = \ u"""\ 2 \n\ x + x + 1\ """ ucode_str_3 = \ u"""\ 2 \n\ x + 1 + x\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2, ascii_str_3] assert upretty(expr) in [ucode_str_1, ucode_str_2, ucode_str_3] expr = 1-x ascii_str_1 = \ """\ 1 - x\ """ ascii_str_2 = \ """\ -x + 1\ """ ucode_str_1 = \ u"""\ 1 - x\ """ ucode_str_2 = \ u"""\ -x + 1\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = 1-2*x ascii_str_1 = \ """\ 1 - 2*x\ """ ascii_str_2 = \ """\ -2*x + 1\ """ ucode_str_1 = \ u"""\ 1 - 2⋅x\ """ ucode_str_2 = \ u"""\ -2⋅x + 1\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = x/y ascii_str = \ """\ x\n\ -\n\ y\ """ ucode_str = \ u"""\ x\n\ ─\n\ y\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -x/y ascii_str = \ """\ -x\n\ --\n\ y \ """ ucode_str = \ u"""\ -x\n\ ──\n\ y \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x+2)/y ascii_str_1 = \ """\ 2 + x\n\ -----\n\ y \ """ ascii_str_2 = \ """\ x + 2\n\ -----\n\ y \ """ ucode_str_1 = \ u"""\ 2 + x\n\ ─────\n\ y \ """ ucode_str_2 = \ u"""\ x + 2\n\ ─────\n\ y \ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = (1+x)*y ascii_str_1 = \ """\ y*(1 + x)\ """ ascii_str_2 = \ """\ (1 + x)*y\ """ ascii_str_3 = \ """\ y*(x + 1)\ """ ucode_str_1 = \ u"""\ y⋅(1 + x)\ """ ucode_str_2 = \ u"""\ (1 + x)⋅y\ """ ucode_str_3 = \ u"""\ y⋅(x + 1)\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2, ascii_str_3] assert upretty(expr) in [ucode_str_1, ucode_str_2, ucode_str_3] # Test for correct placement of the negative sign expr = -5*x/(x+10) ascii_str_1 = \ """\ -5*x \n\ ------\n\ 10 + x\ """ ascii_str_2 = \ """\ -5*x \n\ ------\n\ x + 10\ """ ucode_str_1 = \ u"""\ -5⋅x \n\ ──────\n\ 10 + x\ """ ucode_str_2 = \ u"""\ -5⋅x \n\ ──────\n\ x + 10\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = -S(1)/2 - 3*x ascii_str = \ """\ -3*x - 1/2\ """ ucode_str = \ u"""\ -3⋅x - 1/2\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = S(1)/2 - 3*x ascii_str = \ """\ -3*x + 1/2\ """ ucode_str = \ u"""\ -3⋅x + 1/2\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = -S(1)/2 - 3*x/2 ascii_str = \ """\ 3*x 1\n\ - --- - -\n\ 2 2\ """ ucode_str = \ u"""\ 3⋅x 1\n\ - ─── - ─\n\ 2 2\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = S(1)/2 - 3*x/2 ascii_str = \ """\ 3*x 1\n\ - --- + -\n\ 2 2\ """ ucode_str = \ u"""\ 3⋅x 1\n\ - ─── + ─\n\ 2 2\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_issue_2425(): assert pretty(-(-x + 5)*(-x - 2*2**(1/S(2)) + 5) - (-y + 5)*(-y + 5)) == \ """\ / ___ \\ 2\n\ (x - 5)*\\-x - 2*\\/ 2 + 5/ - (-y + 5) \ """ assert upretty(-(-x + 5)*(-x - 2*2**(1/S(2)) + 5) - (-y + 5)*(-y + 5)) == \ u"""\ ⎛ ⎽⎽⎽ ⎞ 2\n\ (x - 5)⋅⎝-x - 2⋅╲╱ 2 + 5⎠ - (-y + 5) \ """ def test_pretty_ordering(): assert pretty(x**2 + x + 1, order='lex') == \ """\ 2 \n\ x + x + 1\ """ assert pretty(x**2 + x + 1, order='rev-lex') == \ """\ 2\n\ 1 + x + x \ """ assert pretty(1 - x, order='lex') == '-x + 1' assert pretty(1 - x, order='rev-lex') == '1 - x' assert pretty(1 - 2*x, order='lex') == '-2*x + 1' assert pretty(1 - 2*x, order='rev-lex') == '1 - 2*x' f = 2*x**4 + y**2 - x**2 + y**3 assert pretty(f, order=None) == \ """\ 4 2 3 2\n\ 2*x - x + y + y \ """ assert pretty(f, order='lex') == \ """\ 4 2 3 2\n\ 2*x - x + y + y \ """ assert pretty(f, order='rev-lex') == \ """\ 2 3 2 4\n\ y + y - x + 2*x \ """ expr = x - x**3/6 + x**5/120 + O(x**6) ascii_str = \ """\ 3 5 \n\ x x \n\ x - -- + --- + O(x**6)\n\ 6 120 \ """ ucode_str = \ u"""\ 3 5 \n\ x x \n\ x - ── + ─── + O(x**6)\n\ 6 120 \ """ assert pretty(expr, order=None) == ascii_str assert upretty(expr, order=None) == ucode_str assert pretty(expr, order='lex') == ascii_str assert upretty(expr, order='lex') == ucode_str assert pretty(expr, order='rev-lex') == ascii_str assert upretty(expr, order='rev-lex') == ucode_str def test_pretty_relational(): expr = Eq(x, y) ascii_str = \ """\ x = y\ """ ucode_str = \ u"""\ x = y\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lt(x, y) ascii_str = \ """\ x < y\ """ ucode_str = \ u"""\ x < y\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Gt(x, y) ascii_str = \ """\ y < x\ """ ucode_str = \ u"""\ y < x\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Le(x, y) ascii_str = \ """\ x <= y\ """ ucode_str = \ u"""\ x ≤ y\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Ge(x, y) ascii_str = \ """\ y <= x\ """ ucode_str = \ u"""\ y ≤ x\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Ne(x/(y+1), y**2) ascii_str_1 = \ """\ x 2\n\ ----- != y \n\ 1 + y \ """ ascii_str_2 = \ """\ x 2\n\ ----- != y \n\ y + 1 \ """ ucode_str_1 = \ u"""\ x 2\n\ ───── ≠ y \n\ 1 + y \ """ ucode_str_2 = \ u"""\ x 2\n\ ───── ≠ y \n\ y + 1 \ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] def test_pretty_rational(): expr = y*x**-2 ascii_str = \ """\ y \n\ --\n\ 2\n\ x \ """ ucode_str = \ u"""\ y \n\ ──\n\ 2\n\ x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = y**Rational(3,2) * x**Rational(-5,2) ascii_str = \ """\ 3/2\n\ y \n\ ----\n\ 5/2\n\ x \ """ ucode_str = \ u"""\ 3/2\n\ y \n\ ────\n\ 5/2\n\ x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = sin(x)**3/tan(x)**2 ascii_str = \ """\ 3 \n\ sin (x)\n\ -------\n\ 2 \n\ tan (x)\ """ ucode_str = \ u"""\ 3 \n\ sin (x)\n\ ───────\n\ 2 \n\ tan (x)\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_functions(): """Tests for Abs, conjugate, exp, function braces, and factorial.""" expr = (2*x + exp(x)) ascii_str_1 = \ """\ x\n\ 2*x + e \ """ ascii_str_2 = \ """\ x \n\ e + 2*x\ """ ucode_str_1 = \ u"""\ x\n\ 2⋅x + ℯ \ """ ucode_str_2 = \ u"""\ x \n\ ℯ + 2⋅x\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Abs(x) ascii_str = \ """\ |x|\ """ ucode_str = \ u"""\ │x│\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Abs(x/(x**2+1)) ascii_str_1 = \ """\ | x |\n\ |------|\n\ | 2|\n\ |1 + x |\ """ ascii_str_2 = \ """\ | x |\n\ |------|\n\ | 2 |\n\ |x + 1|\ """ ucode_str_1 = \ u"""\ │ x │\n\ │──────│\n\ │ 2│\n\ │1 + x │\ """ ucode_str_2 = \ u"""\ │ x │\n\ │──────│\n\ │ 2 │\n\ │x + 1│\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Abs(1 / (y - Abs(x))) ascii_str = \ """\ | 1 |\n\ |-------|\n\ |y - |x||\ """ ucode_str = \ u"""\ │ 1 │\n\ │───────│\n\ │y - │x││\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str n = Symbol('n', integer=True) expr = factorial(n) ascii_str = \ """\ n!\ """ ucode_str = \ u"""\ n!\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial(2*n) ascii_str = \ """\ (2*n)!\ """ ucode_str = \ u"""\ (2⋅n)!\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial(factorial(factorial(n))) ascii_str = \ """\ ((n!)!)!\ """ ucode_str = \ u"""\ ((n!)!)!\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = factorial(n+1) ascii_str_1 = \ """\ (1 + n)!\ """ ascii_str_2 = \ """\ (n + 1)!\ """ ucode_str_1 = \ u"""\ (1 + n)!\ """ ucode_str_2 = \ u"""\ (n + 1)!\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = 2*binomial(n, k) ascii_str = \ """\ /n\\\n\ 2*| |\n\ \k/\ """ ucode_str = \ u"""\ ⎛n⎞\n\ 2⋅⎜ ⎟\n\ ⎝k⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2*binomial(2*n, k) ascii_str = \ """\ /2*n\\\n\ 2*| |\n\ \ k /\ """ ucode_str = \ u"""\ ⎛2⋅n⎞\n\ 2⋅⎜ ⎟\n\ ⎝ k ⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2*binomial(n**2, k) ascii_str = \ """\ / 2\\\n\ |n |\n\ 2*| |\n\ \k /\ """ ucode_str = \ u"""\ ⎛ 2⎞\n\ ⎜n ⎟\n\ 2⋅⎜ ⎟\n\ ⎝k ⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate(x) ascii_str = \ """\ _\n\ x\ """ ucode_str = \ u"""\ ⎽\n\ x\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str f = Function('f') expr = conjugate(f(x+1)) ascii_str_1 = \ """\ ________\n\ f(1 + x)\ """ ascii_str_2 = \ """\ ________\n\ f(x + 1)\ """ ucode_str_1 = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽\n\ f(1 + x)\ """ ucode_str_2 = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽\n\ f(x + 1)\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = f(x) ascii_str = \ """\ f(x)\ """ ucode_str = \ u"""\ f(x)\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = f(x, y) ascii_str = \ """\ f(x, y)\ """ ucode_str = \ u"""\ f(x, y)\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = f(x/(y+1), y) ascii_str_1 = \ """\ / x \\\n\ f|-----, y|\n\ \\1 + y /\ """ ascii_str_2 = \ """\ / x \\\n\ f|-----, y|\n\ \\y + 1 /\ """ ucode_str_1 = \ u"""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝1 + y ⎠\ """ ucode_str_2 = \ u"""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝y + 1 ⎠\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = sin(x)**2 ascii_str = \ """\ 2 \n\ sin (x)\ """ ucode_str = \ u"""\ 2 \n\ sin (x)\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate(a+b*I) ascii_str = \ """\ _ _\n\ a - I*b\ """ ucode_str = \ u"""\ ⎽ ⎽\n\ a - ⅈ⋅b\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate(exp(a+b*I)) ascii_str = \ """\ _ _\n\ a - I*b\n\ e \ """ ucode_str = \ u"""\ ⎽ ⎽\n\ a - ⅈ⋅b\n\ ℯ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = conjugate( f(1 + conjugate(f(x))) ) ascii_str_1 = \ """\ ___________\n\ / ____\\\n\ f\\1 + f(x)/\ """ ascii_str_2 = \ """\ ___________\n\ /____ \\\n\ f\\f(x) + 1/\ """ ucode_str_1 = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽\n\ ⎛ ⎽⎽⎽⎽⎞\n\ f⎝1 + f(x)⎠\ """ ucode_str_2 = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽\n\ ⎛⎽⎽⎽⎽ ⎞\n\ f⎝f(x) + 1⎠\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = f(x/(y+1), y) ascii_str_1 = \ """\ / x \\\n\ f|-----, y|\n\ \\1 + y /\ """ ascii_str_2 = \ """\ / x \\\n\ f|-----, y|\n\ \\y + 1 /\ """ ucode_str_1 = \ u"""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝1 + y ⎠\ """ ucode_str_2 = \ u"""\ ⎛ x ⎞\n\ f⎜─────, y⎟\n\ ⎝y + 1 ⎠\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = floor(1 / (y - floor(x))) ascii_str = \ """\ / 1 \\\n\ floor|------------|\n\ \y - floor(x)/\ """ ucode_str = \ u"""\ ⎢ 1 ⎥\n\ ⎢───────⎥\n\ ⎣y - ⌊x⌋⎦\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = ceiling(1 / (y - ceiling(x))) ascii_str = \ """\ / 1 \\\n\ ceiling|--------------|\n\ \y - ceiling(x)/\ """ ucode_str = \ u"""\ ⎡ 1 ⎤\n\ ⎢───────⎥\n\ ⎢y - ⌈x⌉⎥\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_sqrt(): expr = sqrt(2) ascii_str = \ """\ ___\n\ \/ 2 \ """ ucode_str = \ u"""\ ⎽⎽⎽\n\ ╲╱ 2 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2**Rational(1,3) ascii_str = \ """\ 3 ___\n\ \/ 2 \ """ ucode_str = \ u"""\ 3 ⎽⎽⎽\n\ ╲╱ 2 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2**Rational(1,1000) ascii_str = \ """\ 1000___\n\ \/ 2 \ """ ucode_str = \ u"""\ 1000⎽⎽⎽\n\ ╲╱ 2 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = sqrt(x**2 + 1) ascii_str = \ """\ ________\n\ / 2 \n\ \/ x + 1 \ """ ucode_str = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽\n\ ╱ 2 \n\ ╲╱ x + 1 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (1 + sqrt(5))**Rational(1,3) ascii_str = \ """\ ___________\n\ 3 / ___ \n\ \/ 1 + \/ 5 \ """ ucode_str = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽\n\ 3 ╱ ⎽⎽⎽ \n\ ╲╱ 1 + ╲╱ 5 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = 2**(1/x) ascii_str = \ """\ x ___\n\ \/ 2 \ """ ucode_str = \ u"""\ x ⎽⎽⎽\n\ ╲╱ 2 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = sqrt(2+pi) ascii_str = \ """\ ________\n\ \/ 2 + pi \ """ ucode_str = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽\n\ ╲╱ 2 + π \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (2+(1+x**2)/(2+x))**Rational(1,4)+(1+x**Rational(1,1000))/sqrt(3+x**2) ascii_str = \ """\ ____________ \n\ / 2 1000___ \n\ / x + 1 \/ x + 1\n\ 4 / 2 + ------ + -----------\n\ \/ x + 2 ________\n\ / 2 \n\ \/ x + 3 \ """ ucode_str = \ u"""\ ⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽ \n\ ╱ 2 1000⎽⎽⎽ \n\ ╱ x + 1 ╲╱ x + 1\n\ 4 ╱ 2 + ────── + ───────────\n\ ╲╱ x + 2 ⎽⎽⎽⎽⎽⎽⎽⎽\n\ ╱ 2 \n\ ╲╱ x + 3 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_lambda(): expr = Lambda(x, x) ascii_str = \ """\ Lambda(x, x)\ """ ucode_str = \ u"""\ Λ(x, x)\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda(x, x**2) ascii_str = \ """\ / 2\\\n\ Lambda\\x, x /\ """ ucode_str = \ u"""\ ⎛ 2⎞\n\ Λ⎝x, x ⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda(x, x**2)**2 ascii_str = \ """\ 2 \n\ / 2\\\n\ Lambda \\x, x /\ """ ucode_str = \ u"""\ 2 \n\ ⎛ 2⎞\n\ Λ ⎝x, x ⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda((x, y), x) ascii_str = \ """\ Lambda((x, y), x)\ """ ucode_str = \ u"""\ Λ((x, y), x)\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Lambda((x, y), x**2) ascii_str = \ """\ / 2\\\n\ Lambda\\(x, y), x /\ """ ucode_str = \ u"""\ ⎛ 2⎞\n\ Λ⎝(x, y), x ⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_derivatives(): # Simple expr = Derivative(log(x), x, evaluate=False) ascii_str = \ """\ d \n\ --(log(x))\n\ dx \ """ ucode_str = \ u"""\ d \n\ ──(log(x))\n\ dx \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Derivative(log(x), x, evaluate=False) + x ascii_str_1 = \ """\ d \n\ x + --(log(x))\n\ dx \ """ ascii_str_2 = \ """\ d \n\ --(log(x)) + x\n\ dx \ """ ucode_str_1 = \ u"""\ d \n\ x + ──(log(x))\n\ dx \ """ ucode_str_2 = \ u"""\ d \n\ ──(log(x)) + x\n\ dx \ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] # Multiple symbols expr = Derivative(log(x) + x**2, x, y) ascii_str_1 = \ """\ 2 \n\ d / 2\\\n\ -----\log(x) + x /\n\ dy dx \ """ ascii_str_2 = \ """\ 2 \n\ d / 2 \\\n\ -----\\x + log(x)/\n\ dy dx \ """ ucode_str_1 = \ u"""\ 2 \n\ d ⎛ 2⎞\n\ ─────⎝log(x) + x ⎠\n\ dy dx \ """ ucode_str_2 = \ u"""\ 2 \n\ d ⎛ 2 ⎞\n\ ─────⎝x + log(x)⎠\n\ dy dx \ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Derivative(2*x*y, y, x) + x**2 ascii_str_1 = \ """\ 2 \n\ d 2\n\ -----(2*x*y) + x \n\ dx dy \ """ ascii_str_2 = \ """\ 2 \n\ 2 d \n\ x + -----(2*x*y)\n\ dx dy \ """ ucode_str_1 = \ u"""\ 2 \n\ d 2\n\ ─────(2⋅x⋅y) + x \n\ dx dy \ """ ucode_str_2 = \ u"""\ 2 \n\ 2 d \n\ x + ─────(2⋅x⋅y)\n\ dx dy \ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Derivative(2*x*y, x, x) ascii_str = \ """\ 2 \n\ d \n\ ---(2*x*y)\n\ 2 \n\ dx \ """ ucode_str = \ u"""\ 2 \n\ d \n\ ───(2⋅x⋅y)\n\ 2 \n\ dx \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Derivative(2*x*y, x, 17) ascii_str = \ """\ 17 \n\ d \n\ ----(2*x*y)\n\ 17 \n\ dx \ """ ucode_str = \ u"""\ 17 \n\ d \n\ ────(2⋅x⋅y)\n\ 17 \n\ dx \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Derivative(2*x*y, x, x, y) ascii_str = \ """\ 3 \n\ d \n\ ------(2*x*y)\n\ 2 \n\ dy dx \ """ ucode_str = \ u"""\ 3 \n\ d \n\ ──────(2⋅x⋅y)\n\ 2 \n\ dy dx \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str # Greek letters alpha = Symbol('alpha') beta = Function('beta') expr = beta(alpha).diff(alpha) ascii_str = \ """\ d \n\ ------(beta(alpha))\n\ dalpha \ """ ucode_str = \ u"""\ d \n\ ──(β(α))\n\ dα \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_integrals(): expr = Integral(log(x), x) ascii_str = \ """\ / \n\ | \n\ | log(x) dx\n\ | \n\ / \ """ ucode_str = \ u"""\ ⌠ \n\ ⎮ log(x) dx\n\ ⌡ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2, x) ascii_str = \ """\ / \n\ | \n\ | 2 \n\ | x dx\n\ | \n\ / \ """ ucode_str = \ u"""\ ⌠ \n\ ⎮ 2 \n\ ⎮ x dx\n\ ⌡ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral((sin(x))**2 / (tan(x))**2) ascii_str = \ """\ / \n\ | \n\ | 2 \n\ | sin (x) \n\ | ------- dx\n\ | 2 \n\ | tan (x) \n\ | \n\ / \ """ ucode_str = \ u"""\ ⌠ \n\ ⎮ 2 \n\ ⎮ sin (x) \n\ ⎮ ─────── dx\n\ ⎮ 2 \n\ ⎮ tan (x) \n\ ⌡ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**(2**x), x) ascii_str = \ """\ / \n\ | \n\ | / x\ \n\ | \\2 / \n\ | x dx\n\ | \n\ / \ """ ucode_str = \ u"""\ ⌠ \n\ ⎮ ⎛ x⎞ \n\ ⎮ ⎝2 ⎠ \n\ ⎮ x dx\n\ ⌡ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2, (x,1,2)) ascii_str = \ """\ 2 \n\ / \n\ | \n\ | 2 \n\ | x dx\n\ | \n\ / \n\ 1 \ """ ucode_str = \ u"""\ 2 \n\ ⌠ \n\ ⎮ 2 \n\ ⎮ x dx\n\ ⌡ \n\ 1 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2, (x,Rational(1,2),10)) ascii_str = \ """\ 10 \n\ / \n\ | \n\ | 2 \n\ | x dx\n\ | \n\ / \n\ 1/2 \ """ ucode_str = \ u"""\ 10 \n\ ⌠ \n\ ⎮ 2 \n\ ⎮ x dx\n\ ⌡ \n\ 1/2 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(x**2*y**2, x,y) ascii_str = \ """\ / / \n\ | | \n\ | | 2 2 \n\ | | x *y dx dy\n\ | | \n\ / / \ """ ucode_str = \ u"""\ ⌠ ⌠ \n\ ⎮ ⎮ 2 2 \n\ ⎮ ⎮ x ⋅y dx dy\n\ ⌡ ⌡ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Integral(sin(th)/cos(ph), (th,0,pi), (ph, 0, 2*pi)) ascii_str = \ """\ 2*pi pi \n\ / / \n\ | | \n\ | | sin(theta) \n\ | | ---------- d(theta) d(phi)\n\ | | cos(phi) \n\ | | \n\ / / \n\ 0 0 \ """ ucode_str = \ u"""\ 2⋅π π \n\ ⌠ ⌠ \n\ ⎮ ⎮ sin(θ) \n\ ⎮ ⎮ ────── dθ dφ\n\ ⎮ ⎮ cos(φ) \n\ ⌡ ⌡ \n\ 0 0 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_matrix(): # Empty Matrix expr = Matrix() ascii_str = "[]" unicode_str = "[]" assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Matrix([[x**2+1, 1], [y, x+y]]) ascii_str_1 = \ """\ [ 2 ] [1 + x 1 ] [ ] [ y x + y]\ """ ascii_str_2 = \ """\ [ 2 ] [x + 1 1 ] [ ] [ y x + y]\ """ ucode_str_1 = \ u"""\ ⎡ 2 ⎤ ⎢1 + x 1 ⎥ ⎢ ⎥ ⎣ y x + y⎦\ """ ucode_str_2 = \ u"""\ ⎡ 2 ⎤ ⎢x + 1 1 ⎥ ⎢ ⎥ ⎣ y x + y⎦\ """ assert pretty(expr) in [ascii_str_1, ascii_str_2] assert upretty(expr) in [ucode_str_1, ucode_str_2] expr = Matrix([[x/y, y, th], [0, exp(I*k*ph), 1]]) ascii_str = \ """\ [x ] [- y theta] [y ] [ ] [ I*k*phi ] [0 e 1 ]\ """ ucode_str = \ u"""\ ⎡x ⎤ ⎢─ y θ⎥ ⎢y ⎥ ⎢ ⎥ ⎢ ⅈ⋅k⋅φ ⎥ ⎣0 ℯ 1⎦\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_piecewise(): expr = Piecewise((x,x<1),(x**2,True)) ascii_str = \ """\ /x for x < 1\n\ | \n\ < 2 \n\ |x otherwise\n\ \ \ """ ucode_str = \ u"""\ ⎧x for x < 1\n\ ⎪ \n\ ⎨ 2 \n\ ⎪x otherwise\n\ ⎩ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_seq(): expr = () ascii_str = \ """\ ()\ """ ucode_str = \ u"""\ ()\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = [] ascii_str = \ """\ []\ """ ucode_str = \ u"""\ []\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {} ascii_str = \ """\ {}\ """ ucode_str = \ u"""\ {}\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (1/x,) ascii_str = \ """\ 1 \n\ (-,)\n\ x \ """ ucode_str = \ u"""\ ⎛1 ⎞\n\ ⎜─,⎟\n\ ⎝x ⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = [x**2, 1/x, x, y, sin(th)**2/cos(ph)**2] ascii_str = \ """\ 2 \n\ 2 1 sin (theta) \n\ [x , -, x, y, -----------]\n\ x 2 \n\ cos (phi) \ """ ucode_str = \ u"""\ ⎡ 2 ⎤\n\ ⎢ 2 1 sin (θ)⎥\n\ ⎢x , ─, x, y, ───────⎥\n\ ⎢ x 2 ⎥\n\ ⎣ cos (φ)⎦\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2, 1/x, x, y, sin(th)**2/cos(ph)**2) ascii_str = \ """\ 2 \n\ 2 1 sin (theta) \n\ (x , -, x, y, -----------)\n\ x 2 \n\ cos (phi) \ """ ucode_str = \ u"""\ ⎛ 2 ⎞\n\ ⎜ 2 1 sin (θ)⎟\n\ ⎜x , ─, x, y, ───────⎟\n\ ⎜ x 2 ⎟\n\ ⎝ cos (φ)⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {x: sin(x)} ascii_str = \ """\ {x: sin(x)}\ """ ucode_str = \ u"""\ {x: sin(x)}\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {1/x: 1/y, x: sin(x)**2} ascii_str = \ """\ 1 1 2 \n\ {-: -, x: sin (x)}\n\ x y \ """ ucode_str = \ u"""\ ⎧1 1 2 ⎫\n\ ⎨─: ─, x: sin (x)⎬\n\ ⎩x y ⎭\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str # There used to be a bug with pretty-printing sequences of even height. expr = [x**2] ascii_str = \ """\ 2 \n\ [x ]\ """ ucode_str = \ u"""\ ⎡ 2⎤\n\ ⎣x ⎦\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = (x**2,) ascii_str = \ """\ 2 \n\ (x ,)\ """ ucode_str = \ u"""\ ⎛ 2 ⎞\n\ ⎝x ,⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = {x**2: 1} ascii_str = \ """\ 2 \n\ {x : 1}\ """ ucode_str = \ u"""\ ⎧ 2 ⎫\n\ ⎨x : 1⎬\n\ ⎩ ⎭\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_any_object_in_sequence(): # Cf. issue 2207 b1 = Basic() b2 = Basic(Basic()) expr = [b2, b1] assert pretty(expr) == "[Basic(Basic()), Basic()]" assert upretty(expr) == u"[Basic(Basic()), Basic()]" expr = set([b2, b1]) assert pretty(expr) == "set(Basic(), Basic(Basic()))" assert upretty(expr) == u"set(Basic(), Basic(Basic()))" expr = {b2:b1, b1:b2} assert pretty(expr) == "{Basic(): Basic(Basic()), Basic(Basic()): Basic()}" assert upretty(expr) == u"{Basic(): Basic(Basic()), Basic(Basic()): Basic()}" def test_pretty_limits(): expr = Limit(x, x, oo) ascii_str = \ """\ lim x\n\ x->oo \ """ ucode_str = \ u"""\ lim x\n\ x->∞ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Limit(x**2, x, 0) ascii_str = \ """\ 2\n\ lim x \n\ x->0 \ """ ucode_str = \ u"""\ 2\n\ lim x \n\ x->0 \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Limit(1/x, x, 0) ascii_str = \ """\ 1\n\ lim -\n\ x->0x\ """ ucode_str = \ u"""\ 1\n\ lim ─\n\ x->0x\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = Limit(sin(x)/x, x, 0) ascii_str = \ """\ sin(x)\n\ lim ------\n\ x->0 x \ """ ucode_str = \ u"""\ sin(x)\n\ lim ──────\n\ x->0 x \ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_RootOf(): expr = RootOf(x**5 + 11*x - 2, 0) ascii_str = \ """\ / 5 \\\n\ RootOf\\x + 11*x - 2, 0/\ """ ucode_str = \ u"""\ ⎛ 5 ⎞\n\ RootOf⎝x + 11⋅x - 2, 0⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_RootSum(): expr = RootSum(x**5 + 11*x - 2, auto=False) ascii_str = \ """\ / 5 \\\n\ RootSum\\x + 11*x - 2/\ """ ucode_str = \ u"""\ ⎛ 5 ⎞\n\ RootSum⎝x + 11⋅x - 2⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = RootSum(x**5 + 11*x - 2, Lambda(z, exp(z))) ascii_str = \ """\ / 5 / z\\\\\n\ RootSum\\x + 11*x - 2, Lambda\\z, e //\ """ ucode_str = \ u"""\ ⎛ 5 ⎛ z⎞⎞\n\ RootSum⎝x + 11⋅x - 2, Λ⎝z, ℯ ⎠⎠\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_pretty_Boolean(): expr = Not(x, evaluate=False) assert pretty(expr) == "Not(x)" assert upretty(expr) == u"¬ x" expr = And(x, y) assert pretty(expr) == "And(x, y)" assert upretty(expr) == u"x ∧ y" expr = Or(x, y) assert pretty(expr) == "Or(x, y)" assert upretty(expr) == u"x ∨ y" expr = Xor(x, y, evaluate=False) assert pretty(expr) == "Xor(x, y)" assert upretty(expr) == u"x ⊻ y" expr = Nand(x, y, evaluate=False) assert pretty(expr) == "Nand(x, y)" assert upretty(expr) == u"x ⊼ y" expr = Nor(x, y, evaluate=False) assert pretty(expr) == "Nor(x, y)" assert upretty(expr) == u"x ⊽ y" expr = Implies(x, y, evaluate=False) assert pretty(expr) == "Implies(x, y)" assert upretty(expr) == u"x → y" expr = Equivalent(x, y, evaluate=False) assert pretty(expr) == "Equivalent(x, y)" assert upretty(expr) == u"x ≡ y" def test_pretty_Domain(): expr = FF(23) assert pretty(expr) == "GF(23)" assert upretty(expr) == u"ℤ₂₃" expr = ZZ assert pretty(expr) == "ZZ" assert upretty(expr) == u"ℤ" expr = QQ assert pretty(expr) == "QQ" assert upretty(expr) == u"ℚ" expr = RR assert pretty(expr) == "RR" assert upretty(expr) == u"ℝ" expr = QQ[x] assert pretty(expr) == "QQ[x]" assert upretty(expr) == u"ℚ[x]" expr = QQ[x, y] assert pretty(expr) == "QQ[x, y]" assert upretty(expr) == u"ℚ[x, y]" expr = ZZ.frac_field(x) assert pretty(expr) == "ZZ(x)" assert upretty(expr) == u"ℤ(x)" expr = ZZ.frac_field(x, y) assert pretty(expr) == "ZZ(x, y)" assert upretty(expr) == u"ℤ(x, y)" def test_pretty_prec(): assert xpretty(S("0.3"), full_prec=True) == "0.300000000000000" assert xpretty(S("0.3"), full_prec="auto") == "0.300000000000000" assert xpretty(S("0.3"), full_prec=False) == "0.3" assert xpretty(S("0.3")*x, full_prec=True, use_unicode=False) in [ "0.300000000000000*x", "x*0.300000000000000" ] assert xpretty(S("0.3")*x, full_prec="auto", use_unicode=False) in [ "0.3*x", "x*0.3" ] assert xpretty(S("0.3")*x, full_prec=False, use_unicode=False) in [ "0.3*x", "x*0.3" ] def test_pprint(): import StringIO, sys fd = StringIO.StringIO() sso = sys.stdout sys.stdout = fd try: #FIXME-py3k: sympy/printing/pretty/stringpict.py", line 286, in terminal_width #FIXME-py3k: curses.setupterm() #FIXME-py3k: io.UnsupportedOperation: fileno pprint(pi, use_unicode=False) finally: sys.stdout = sso assert fd.getvalue() == 'pi\n' def test_pretty_class(): """Test that the printer dispatcher correctly handles classes.""" class C: pass # C has no .__class__ and this was causing problems class D(object): pass assert pretty( C ) == str( C ) assert pretty( D ) == str( D ) def test_pretty_no_wrap_line(): huge_expr = 0 for i in range(20): huge_expr += i*sin(i+x) assert xpretty(huge_expr ).find('\n') != -1 assert xpretty(huge_expr, wrap_line=False).find('\n') == -1 def test_settings(): raises(TypeError, 'pretty(S(4), method="garbage")') def test_pretty_sum(): from sympy.abc import x, a, b, k, m, n expr = Sum(x, (x, 0, oo)) ascii_str = \ """\ oo \n\ __ \n\ \\ ` \n\ ) x\n\ /_, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(x**2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ___ \n\ \\ ` \n\ \\ 2\n\ / x \n\ /__, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(x/2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ___ \n\ \\ ` \n\ \\ x\n\ ) -\n\ / 2\n\ /__, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(x**3/2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ 3\n\ \\ x \n\ / --\n\ / 2 \n\ /___, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum((x**3*y**(x/2))**n, (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ n\n\ \\ / x\\ \n\ ) | -| \n\ / | 3 2| \n\ / \\x *y / \n\ /___, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(1/x**2, (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ 1 \n\ \\ --\n\ / 2\n\ / x \n\ /___, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(1/y**(a/b), (x, 0, oo)) ascii_str = \ """\ oo \n\ ____ \n\ \\ ` \n\ \\ -a\n\ \\ --\n\ / b \n\ / y \n\ /___, \n\ x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(1/y**(a/b), (x, 0, oo), (y,1,2)) ascii_str = \ """\ 2 oo \n\ ____ ____ \n\ \\ ` \\ ` \n\ \\ \\ -a\n\ \\ \\ --\n\ / / b \n\ / / y \n\ /___, /___, \n\ y = 1 x = 0 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str expr = Sum(1/(1 + 1/(1 + 1/k)) + 1, (k, 111, 1 + 1/n), (k, 1/(1+m), oo)) + 1/(1 + 1/k) ascii_str = \ """\ 1 \n\ 1 + - \n\ oo n \n\ _____ _____ \n\ \\ ` \\ ` \n\ \\ \\ / 1 \\ \n\ \\ \\ |1 + ---------| \n\ \\ \\ | 1 | 1 \n\ ) ) | 1 + -----| + -----\n\ / / | 1| 1\n\ / / | 1 + -| 1 + -\n\ / / \\ k/ k\n\ /____, /____, \n\ 1 k = 111 \n\ k = ----- \n\ m + 1 \ """ assert pretty(expr) == ascii_str #assert upretty(expr) == ucode_str def test_units(): # issue 2461 expr = joule ascii_str = \ """\ 2\n\ kg*m \n\ -----\n\ 2 \n\ s \ """ unicode_str = \ u"""\ 2\n\ kg⋅m \n\ ─────\n\ 2 \n\ s \ """ assert upretty(expr) == unicode_str assert pretty(expr) == ascii_str def test_pretty_Subs(): f = Function('f') expr = Subs(f(x), x, ph**2) ascii_str = \ """\ (f(x))| 2\n\ |x=phi \ """ unicode_str = \ u"""\ (f(x))│ 2\n\ │x=φ \ """ assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Subs(f(x).diff(x), x, 0) ascii_str = \ """\ /d \\| \n\ |--(f(x))|| \n\ \\dx /|x=0\ """ unicode_str = \ u"""\ ⎛d ⎞│ \n\ ⎜──(f(x))⎟│ \n\ ⎝dx ⎠│x=0\ """ assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str expr = Subs(f(x).diff(x)/y, (x, y), (0, Rational(1, 2))) ascii_str = \ """\ /d \\| \n\ |--(f(x))|| \n\ |dx || \n\ |--------|| \n\ \\ y /|x=0, y=1/2\ """ unicode_str = \ u"""\ ⎛d ⎞│ \n\ ⎜──(f(x))⎟│ \n\ ⎜dx ⎟│ \n\ ⎜────────⎟│ \n\ ⎝ y ⎠│x=0, y=1/2\ """ assert pretty(expr) == ascii_str assert upretty(expr) == unicode_str def test_gammas(): assert upretty(lowergamma(x, y)) == u"γ(x, y)" assert upretty(uppergamma(x, y)) == u"Γ(x, y)" def test_hyper(): expr = hyper((), (), z) ucode_str = \ u"""\ ┌─ ⎛ │ ⎞\n\ ├─ ⎜ │ z⎟\n\ 0╵ 0 ⎝ │ ⎠\ """ ascii_str = \ """\ _ \n\ |_ / | \\\n\ | | | z|\n\ 0 0 \\ | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper((), (1,), x) ucode_str = \ u"""\ ┌─ ⎛ │ ⎞\n\ ├─ ⎜ │ x⎟\n\ 0╵ 1 ⎝1 │ ⎠\ """ ascii_str = \ """\ _ \n\ |_ / | \\\n\ | | | x|\n\ 0 1 \\1 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper([2], [1], x) ucode_str = \ u"""\ ┌─ ⎛2 │ ⎞\n\ ├─ ⎜ │ x⎟\n\ 1╵ 1 ⎝1 │ ⎠\ """ ascii_str = \ """\ _ \n\ |_ /2 | \\\n\ | | | x|\n\ 1 1 \\1 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper((pi/3, -2*k), (3, 4, 5, -3), x) ucode_str = \ u"""\ ⎛ π │ ⎞\n\ ┌─ ⎜ ─, -2⋅k │ ⎟\n\ ├─ ⎜ 3 │ x⎟\n\ 2╵ 4 ⎜ │ ⎟\n\ ⎝3, 4, 5, -3 │ ⎠\ """ ascii_str = \ """\ \n\ _ / pi | \\\n\ |_ | --, -2*k | |\n\ | | 3 | x|\n\ 2 4 | | |\n\ \\3, 4, 5, -3 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = hyper((pi,S('2/3'),-2*k), (3,4,5,-3), x**2) ucode_str = \ u"""\ ┌─ ⎛π, 2/3, -2⋅k │ 2⎞\n\ ├─ ⎜ │ x ⎟\n\ 3╵ 4 ⎝3, 4, 5, -3 │ ⎠\ """ ascii_str = \ """\ _ \n\ |_ /pi, 2/3, -2*k | 2\\\n\ | | | x |\n\ 3 4 \\ 3, 4, 5, -3 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str def test_meijerg(): expr = meijerg([pi, pi, x], [1], [0, 1], [1, 2, 3], z) ucode_str = \ u"""\ ╭─╮2, 3 ⎛π, π, x 1 │ ⎞\n\ │╶┐ ⎜ │ z⎟\n\ ╰─╯4, 5 ⎝ 0, 1 1, 2, 3 │ ⎠\ """ ascii_str = \ """\ __2, 3 /pi, pi, x 1 | \\\n\ /__ | | z|\n\ \_|4, 5 \\ 0, 1 1, 2, 3 | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str expr = meijerg([1, pi/7], [2, pi, 5], [], [], z**2) ucode_str = \ u"""\ ⎛ π │ ⎞\n\ ╭─╮0, 2 ⎜1, ─ 2, π, 5 │ 2⎟\n\ │╶┐ ⎜ 7 │ z ⎟\n\ ╰─╯5, 0 ⎜ │ ⎟\n\ ⎝ │ ⎠\ """ ascii_str = \ """\ / pi | \\\n\ __0, 2 |1, -- 2, pi, 5 | 2|\n\ /__ | 7 | z |\n\ \_|5, 0 | | |\n\ \\ | /\ """ assert pretty(expr) == ascii_str assert upretty(expr) == ucode_str wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/pretty/__init__.py0000644000175000017500000000031212014170666026365 0ustar georgeskgeorgesk"""ASCII-ART 2D pretty-printer""" from pretty import pretty, pretty_print, pprint, pprint_use_unicode, pprint_try_use_unicode # if unicode output is available -- let's use it pprint_try_use_unicode() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/codeprinter.py0000644000175000017500000001502512014170666025624 0ustar georgeskgeorgeskfrom sympy.core import S, C, Add from sympy.printing.str import StrPrinter from sympy.tensor import get_indices, get_contraction_structure class AssignmentError(Exception): pass class CodePrinter(StrPrinter): def _doprint_a_piece(self, expr, assign_to=None): # Here we print an expression that may contain Indexed objects, they # correspond to arrays in the generated code. The low-level implementation # involves looping over array elements and possibly storing results in temporary # variables or accumulate it in the assign_to object. lhs_printed = self._print(assign_to) lines = [] # Setup loops over non-dummy indices -- all terms need these indices = self.get_expression_indices(expr, assign_to) openloop, closeloop = self._get_loop_opening_ending(indices) # Setup loops over dummy indices -- each term needs separate treatment d = get_contraction_structure(expr) # terms with no summations first if None in d: text = CodePrinter.doprint(self, Add(*d[None])) else: # If all terms have summations we must initialize array to Zero text = CodePrinter.doprint(self, 0) # skip redundant assignments if text != lhs_printed: lines.extend(openloop) if assign_to is not None: text = self._get_statement("%s = %s" % (lhs_printed, text)) lines.append(text) lines.extend(closeloop) for dummies in d: # then terms with summations if isinstance(dummies, tuple): indices = self._sort_optimized(dummies, expr) openloop_d, closeloop_d = self._get_loop_opening_ending(indices) for term in d[dummies]: if term in d and not ([f.keys() for f in d[term]] == [[None] for f in d[term]]): # If one factor in the term has it's own internal # contractions, those must be computed first. # (temporary variables?) raise NotImplementedError( "FIXME: no support for contractions in factor yet") else: # We need the lhs expression as an accumulator for # the loops, i.e # # for (int d=0; d < dim; d++){ # lhs[] = lhs[] + term[][d] # } ^.................. the accumulator # # We check if the expression already contains the # lhs, and raise an exception if it does, as that # syntax is currently undefined. FIXME: What would be # a good interpretation? if assign_to is None: raise AssignmentError("need assignment variable for loops") if term.has(assign_to): raise(ValueError("FIXME: lhs present in rhs,\ this is undefined in CCodePrinter")) lines.extend(openloop) lines.extend(openloop_d) text = "%s = %s" % (lhs_printed, CodePrinter.doprint(self, assign_to + term)) lines.append(self._get_statement(text)) lines.extend(closeloop_d) lines.extend(closeloop) return lines def get_expression_indices(self, expr, assign_to): rinds, junk = get_indices(expr) linds, junk = get_indices(assign_to) # support broadcast of scalar if linds and not rinds: rinds = linds if rinds != linds: raise ValueError("lhs indices must match non-dummy" " rhs indices in %s" % expr) return self._sort_optimized(rinds, assign_to) def _sort_optimized(self, indices, expr): if not indices: return [] # determine optimized loop order by giving a score to each index # the index with the highest score are put in the innermost loop. score_table = {} for i in indices: score_table[i] = 0 arrays = expr.atoms(C.Indexed) for arr in arrays: for p, ind in enumerate(arr.indices): try: score_table[ind] += self._rate_index_position(p) except KeyError: pass return sorted(indices, key=lambda x: score_table[x]) def _print_NumberSymbol(self, expr): # A Number symbol that is not implemented here or with _printmethod # is registered and evaluated self._number_symbols.add((expr, self._print(expr.evalf(self._settings["precision"])))) return str(expr) def _print_Dummy(self, expr): # dummies must be printed as unique symbols return "%s_%i" %(expr.name, expr.dummy_index) # Dummy _print_Catalan = _print_NumberSymbol _print_EulerGamma = _print_NumberSymbol _print_GoldenRatio = _print_NumberSymbol def _print_not_supported(self, expr): self._not_supported.add(expr) return self.emptyPrinter(expr) # The following can not be simply translated into C or Fortran _print_Basic = _print_not_supported _print_ComplexInfinity = _print_not_supported _print_Derivative = _print_not_supported _print_dict = _print_not_supported _print_ExprCondPair = _print_not_supported _print_GeometryEntity = _print_not_supported _print_Infinity = _print_not_supported _print_Integral = _print_not_supported _print_Interval = _print_not_supported _print_Limit = _print_not_supported _print_list = _print_not_supported _print_Matrix = _print_not_supported _print_DeferredVector = _print_not_supported _print_NaN = _print_not_supported _print_NegativeInfinity = _print_not_supported _print_Normal = _print_not_supported _print_Order = _print_not_supported _print_PDF = _print_not_supported _print_RootOf = _print_not_supported _print_RootsOf = _print_not_supported _print_RootSum = _print_not_supported _print_Sample = _print_not_supported _print_SparseMatrix = _print_not_supported _print_tuple = _print_not_supported _print_Uniform = _print_not_supported _print_Unit = _print_not_supported _print_Wild = _print_not_supported _print_WildFunction = _print_not_supported wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/latex.py0000644000175000017500000007006212014170666024425 0ustar georgeskgeorgesk""" A Printer which converts an expression into its LaTeX equivalent. """ from sympy.core import S, C, Basic, Add, Mul, Wild, var from printer import Printer from conventions import split_super_sub from sympy.simplify import fraction from sympy import Interval import sympy.mpmath.libmp as mlib from sympy.mpmath.libmp import prec_to_dps from sympy.core.compatibility import cmp_to_key import re, warnings class LatexPrinter(Printer): printmethod = "_latex" _default_settings = { "order": None, "mode": "plain", "itex": False, "fold_frac_powers": False, "fold_func_brackets": False, "mul_symbol": None, "inv_trig_style": "abbreviated", "mat_str": "smallmatrix", "mat_delim": "(", } def __init__(self, settings=None): if settings is not None and 'inline' in settings and not settings['inline']: # Change to "good" defaults for inline=False settings['mat_str'] = 'bmatrix' settings['mat_delim'] = None Printer.__init__(self, settings) if ('inline') in self._settings: warnings.warn("'inline' is deprecated, please use 'mode'. " "'mode' can be one of 'inline', 'plain', 'equation', or " "'equation*'.") if self._settings['inline']: self._settings['mode'] = 'inline' else: self._settings['mode'] = 'equation*' if 'mode' in self._settings: valid_modes = ['inline', 'plain', 'equation', \ 'equation*'] if self._settings['mode'] not in valid_modes: raise ValueError("'mode' must be one of 'inline', 'plain', " \ "'equation' or 'equation*'") mul_symbol_table = { None : r" ", "ldot" : r" \,.\, ", "dot" : r" \cdot ", "times" : r" \times " } self._settings['mul_symbol_latex'] = \ mul_symbol_table[self._settings['mul_symbol']] self._delim_dict = {'(':')','[':']'} def doprint(self, expr): tex = Printer.doprint(self, expr) if self._settings['mode'] == 'plain': return tex elif self._settings['mode'] == 'inline': return r"$%s$" % tex elif self._settings['itex']: return r"$$%s$$" % tex else: env_str = self._settings['mode'] return r"\begin{%s}%s\end{%s}" % (env_str, tex, env_str) def _needs_brackets(self, expr): """ Returns True if the expression needs to be wrapped in brackets when printed, False otherwise. For example: a + b => True; a => False; 10 => False; -10 => True. """ return not ((expr.is_Integer and expr.is_nonnegative) or (expr.is_Atom and expr is not S.NegativeOne)) def _needs_function_brackets(self, expr): """ Returns True if the expression needs to be wrapped in brackets when passed as an argument to a function, False otherwise. This is a more liberal version of _needs_brackets, in that many expressions which need to be wrapped in brackets when added/subtracted/raised to a power do not need them when passed to a function. Such an example is a*b. """ if not self._needs_brackets(expr): return False else: # Muls of the form a*b*c... can be folded if expr.is_Mul and not self._mul_is_clean(expr): return True # Pows which don't need brackets can be folded elif expr.is_Pow and not self._pow_is_clean(expr): return True # Add and Function always need brackets elif expr.is_Add or expr.is_Function: return True else: return False def _mul_is_clean(self, expr): for arg in expr.args: if arg.is_Function: return False return True def _pow_is_clean(self, expr): return not self._needs_brackets(expr.base) def _do_exponent(self, expr, exp): if exp is not None: return r"\left(%s\right)^{%s}" % (expr, exp) else: return expr def _print_Add(self, expr, order=None): terms = self._as_ordered_terms(expr, order=order) tex = self._print(terms[0]) for term in terms[1:]: coeff = term.as_coeff_mul()[0] if coeff >= 0: tex += " +" tex += " " + self._print(term) return tex def _print_Float(self, expr): # Based off of that in StrPrinter dps = prec_to_dps(expr._prec) str_real = mlib.to_str(expr._mpf_, dps, strip_zeros=True) # Must always have a mul symbol (as 2.5 10^{20} just looks odd) separator = r" \times " if self._settings['mul_symbol'] is not None: separator = self._settings['mul_symbol_latex'] if 'e' in str_real: (mant, exp) = str_real.split('e') if exp[0] == '+': exp = exp[1:] return r"%s%s10^{%s}" % (mant, separator, exp) elif str_real == "+inf": return r"\infty" elif str_real == "-inf": return r"- \infty" else: return str_real def _print_Mul(self, expr): coeff, tail = expr.as_coeff_Mul() if not coeff.is_negative: tex = "" else: coeff = -coeff tex = "- " numer, denom = fraction(tail) separator = self._settings['mul_symbol_latex'] def convert(expr): if not expr.is_Mul: return str(self._print(expr)) else: _tex = last_term_tex = "" if self.order != 'old': args = expr.as_ordered_factors() else: args = expr.args for term in args: pretty = self._print(term) if term.is_Add: term_tex = (r"\left(%s\right)" % pretty) else: term_tex = str(pretty) # between two digits, \times must always be used, # to avoid confusion if separator == " " and \ re.search("[0-9][} ]*$", last_term_tex) and \ re.match("[{ ]*[-+0-9]", term_tex): _tex += r" \times " elif _tex: _tex += separator _tex += term_tex last_term_tex = term_tex return _tex if denom is S.One: if numer.is_Add: _tex = r"\left(%s\right)" % convert(numer) else: _tex = r"%s" % convert(numer) if coeff is not S.One: tex += str(self._print(coeff)) # between two digits, \times must always be used, to avoid # confusion if separator == " " and re.search("[0-9][} ]*$", tex) and \ re.match("[{ ]*[-+0-9]", _tex): tex += r" \times " + _tex else: tex += separator + _tex else: tex += _tex else: if numer is S.One: if coeff.is_Integer: numer *= coeff.p elif coeff.is_Rational: if coeff.p != 1: numer *= coeff.p denom *= coeff.q elif coeff is not S.One: tex += str(self._print(coeff)) + " " else: if coeff.is_Rational and coeff.p == 1: denom *= coeff.q elif coeff is not S.One: tex += str(self._print(coeff)) + " " tex += r"\frac{%s}{%s}" % \ (convert(numer), convert(denom)) return tex def _print_Pow(self, expr): # Treat x**(Rational(1,n)) as special case if expr.exp.is_Rational\ and abs(expr.exp.p) == 1\ and expr.exp.q != 1: base = self._print(expr.base) expq = expr.exp.q if expq == 2: tex = r"\sqrt{%s}" % base elif self._settings['itex']: tex = r"\root{%d}{%s}" % (expq,base) else: tex = r"\sqrt[%d]{%s}" % (expq,base) if expr.exp.is_negative: return r"\frac{1}{%s}" % tex else: return tex elif self._settings['fold_frac_powers'] \ and expr.exp.is_Rational \ and expr.exp.q != 1: base, p, q = self._print(expr.base), expr.exp.p, expr.exp.q return r"%s^{%s/%s}" % (base, p, q) else: if expr.base.is_Function: return self._print(expr.base, self._print(expr.exp)) else: if expr.exp == S.NegativeOne: #solves issue 1030 #As Mul always simplify 1/x to x**-1 #The objective is achieved with this hack #first we get the latex for -1 * expr, #which is a Mul expression tex = self._print(S.NegativeOne * expr).strip() #the result comes with a minus and a space, so we remove if tex[:1] == "-": return tex[1:].strip() if self._needs_brackets(expr.base): tex = r"\left(%s\right)^{%s}" else: tex = r"%s^{%s}" return tex % (self._print(expr.base), self._print(expr.exp)) def _print_Sum(self, expr): if len(expr.limits) == 1: tex = r"\sum_{%s=%s}^{%s} " % \ tuple([ self._print(i) for i in expr.limits[0] ]) else: def _format_ineq(l): return r"%s \leq %s \leq %s" % \ tuple([self._print(s) for s in l[1], l[0], l[2]]) tex = r"\sum_{\substack{%s}} " % \ str.join('\\\\', [ _format_ineq(l) for l in expr.limits ]) if isinstance(expr.function, Add): tex += r"\left(%s\right)" % self._print(expr.function) else: tex += self._print(expr.function) return tex def _print_Derivative(self, expr): dim = len(expr.variables) if dim == 1: tex = r"\frac{\partial}{\partial %s}" % \ self._print(expr.variables[0]) else: multiplicity, i, tex = [], 1, "" current = expr.variables[0] for symbol in expr.variables[1:]: if symbol == current: i = i + 1 else: multiplicity.append((current, i)) current, i = symbol, 1 else: multiplicity.append((current, i)) for x, i in multiplicity: if i == 1: tex += r"\partial %s" % self._print(x) else: tex += r"\partial^{%s} %s" % (i, self._print(x)) tex = r"\frac{\partial^{%s}}{%s} " % (dim, tex) if isinstance(expr.expr, C.AssocOp): return r"%s\left(%s\right)" % (tex, self._print(expr.expr)) else: return r"%s %s" % (tex, self._print(expr.expr)) def _print_Integral(self, expr): tex, symbols = "", [] for lim in reversed(expr.limits): symbol = lim[0] tex += r"\int" if len(lim) > 1: if self._settings['mode'] in ['equation','equation*'] \ and not self._settings['itex']: tex += r"\limits" if len(lim) == 3: tex += "_{%s}^{%s}" % (self._print(lim[1]), self._print(lim[2])) if len(lim) == 2: tex += "^{%s}" % (self._print(lim[1])) symbols.insert(0, "d%s" % self._print(symbol)) return r"%s %s\,%s" % (tex, str(self._print(expr.function)), " ".join(symbols)) def _print_Limit(self, expr): e, z, z0, dir = expr.args tex = r"\lim_{%s \to %s}" % (self._print(z), self._print(z0)) if isinstance(e, C.AssocOp): return r"%s\left(%s\right)" % (tex, self._print(e)) else: return r"%s %s" % (tex, self._print(e)) def _print_Function(self, expr, exp=None): func = expr.func.__name__ if hasattr(self, '_print_' + func): return getattr(self, '_print_' + func)(expr, exp) else: args = [ str(self._print(arg)) for arg in expr.args ] # How inverse trig functions should be displayed, formats are: # abbreviated: asin, full: arcsin, power: sin^-1 inv_trig_style = self._settings['inv_trig_style'] # If we are dealing with a power-style inverse trig function inv_trig_power_case = False # If it is applicable to fold the argument brackets can_fold_brackets = self._settings['fold_func_brackets'] and \ len(args) == 1 and \ not self._needs_function_brackets(expr.args[0]) inv_trig_table = ["asin", "acos", "atan", "acot"] # If the function is an inverse trig function, handle the style if func in inv_trig_table: if inv_trig_style == "abbreviated": func = func elif inv_trig_style == "full": func = "arc" + func[1:] elif inv_trig_style == "power": func = func[1:] inv_trig_power_case = True # Can never fold brackets if we're raised to a power if exp is not None: can_fold_brackets = False if inv_trig_power_case: name = r"\operatorname{%s}^{-1}" % func elif exp is not None: name = r"\operatorname{%s}^{%s}" % (func, exp) else: name = r"\operatorname{%s}" % func if can_fold_brackets: name += r"%s" else: name += r"\left(%s\right)" if inv_trig_power_case and exp is not None: name += r"^{%s}" % exp return name % ",".join(args) def _print_Poly(self, expr): return self._print(expr.as_expr()) def _print_floor(self, expr, exp=None): tex = r"\lfloor{%s}\rfloor" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_ceiling(self, expr, exp=None): tex = r"\lceil{%s}\rceil" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_Abs(self, expr, exp=None): tex = r"\lvert{%s}\rvert" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_re(self, expr, exp=None): if self._needs_brackets(expr.args[0]): tex = r"\Re\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\Re{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_im(self, expr, exp=None): if self._needs_brackets(expr.args[0]): tex = r"\Im\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\Im{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_conjugate(self, expr, exp=None): tex = r"\overline{%s}" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_exp(self, expr, exp=None): tex = r"e^{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_gamma(self, expr, exp=None): tex = r"\left(%s\right)" % self._print(expr.args[0]) if exp is not None: return r"\operatorname{\Gamma}^{%s}%s" % (exp, tex) else: return r"\operatorname{\Gamma}%s" % tex def _print_uppergamma(self, expr, exp=None): tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\operatorname{\Gamma}^{%s}%s" % (exp, tex) else: return r"\operatorname{\Gamma}%s" % tex def _print_lowergamma(self, expr, exp=None): tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), self._print(expr.args[1])) if exp is not None: return r"\operatorname{\gamma}^{%s}%s" % (exp, tex) else: return r"\operatorname{\gamma}%s" % tex def _print_factorial(self, expr, exp=None): x = expr.args[0] if self._needs_brackets(x): tex = r"\left(%s\right)!" % self._print(x) else: tex = self._print(x) + "!" if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_binomial(self, expr, exp=None): tex = r"{{%s}\choose{%s}}" % (self._print(expr[0]), self._print(expr[1])) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_RisingFactorial(self, expr, exp=None): tex = r"{\left(%s\right)}^{\left(%s\right)}" % \ (self._print(expr[0]), self._print(expr[1])) return self._do_exponent(tex, exp) def _print_FallingFactorial(self, expr, exp=None): tex = r"{\left(%s\right)}_{\left(%s\right)}" % \ (self._print(expr[0]), self._print(expr[1])) return self._do_exponent(tex, exp) def _hprint_BesselBase(self, expr, exp, sym): tex = r"%s" % (sym) need_exp = False if exp is not None: if tex.find('^') == -1: tex = r"%s^{%s}" % (tex, self._print(exp)) else: need_exp = True tex = r"%s_{%s}\left(%s\right)" % (tex, self._print(expr.order), self._print(expr.argument)) if need_exp: tex = self._do_exponent(tex, exp) return tex def _hprint_vec(self, vec): if len(vec) == 0: return "" s = "" for i in vec[:-1]: s += "%s, " % self._print(i) s += self._print(vec[-1]) return s def _print_besselj(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'J') def _print_besseli(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'I') def _print_besselk(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'K') def _print_bessely(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'Y') def _print_yn(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'y') def _print_jn(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'j') def _print_hankel1(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'H^{(1)}') def _print_hankel2(self, expr, exp=None): return self._hprint_BesselBase(expr, exp, 'H^{(2)}') def _print_hyper(self, expr, exp=None): tex = r"{{}_{%s}F_{%s}\left.\left(\begin{matrix} %s \\ %s \end{matrix}" \ r"\right| {%s} \right)}" % \ (self._print(len(expr.ap)), self._print(len(expr.bq)), self._hprint_vec(expr.ap), self._hprint_vec(expr.bq), self._print(expr.argument)) if exp is not None: tex = r"{%s}^{%s}" % (tex, self._print(exp)) return tex def _print_meijerg(self, expr, exp=None): tex = r"{G_{%s, %s}^{%s, %s}\left.\left(\begin{matrix} %s & %s \\" \ r"%s & %s \end{matrix} \right| {%s} \right)}" % \ (self._print(len(expr.ap)), self._print(len(expr.bq)), self._print(len(expr.bm)), self._print(len(expr.an)), self._hprint_vec(expr.an), self._hprint_vec(expr.aother), self._hprint_vec(expr.bm), self._hprint_vec(expr.bother), self._print(expr.argument)) if exp is not None: tex = r"{%s}^{%s}" % (tex, self._print(exp)) return tex def _print_Rational(self, expr): if expr.q != 1: sign = "" p = expr.p if expr.p < 0: sign = "- " p = -p return r"%s\frac{%d}{%d}" % (sign, p, expr.q) else: return self._print(expr.p) def _print_Infinity(self, expr): return r"\infty" def _print_NegativeInfinity(self, expr): return r"-\infty" def _print_ComplexInfinity(self, expr): return r"\tilde{\infty}" def _print_ImaginaryUnit(self, expr): return r"\mathbf{\imath}" def _print_NaN(self, expr): return r"\bot" def _print_Pi(self, expr): return r"\pi" def _print_Exp1(self, expr): return r"e" def _print_EulerGamma(self, expr): return r"\gamma" def _print_Order(self, expr): return r"\operatorname{\mathcal{O}}\left(%s\right)" % \ self._print(expr.args[0]) def _print_Symbol(self, expr): name, supers, subs = split_super_sub(expr.name) # translate name, supers and subs to tex keywords greek = set([ 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega' ]) other = set( ['aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar', 'hslash', 'mho' ]) def translate(s): tmp = s.lower() if tmp in greek or tmp in other: return "\\" + s else: return s name = translate(name) supers = [translate(sup) for sup in supers] subs = [translate(sub) for sub in subs] # glue all items together: if len(supers) > 0: name += "^{%s}" % " ".join(supers) if len(subs) > 0: name += "_{%s}" % " ".join(subs) return name def _print_Relational(self, expr): if self._settings['itex']: lt = r"\lt" else: lt = "<" charmap = { "==" : "=", "<" : lt, "<=" : r"\leq", "!=" : r"\neq", } return "%s %s %s" % (self._print(expr.lhs), charmap[expr.rel_op], self._print(expr.rhs)) def _print_Piecewise(self, expr): ecpairs = [r"%s & \text{for}\: %s" % (self._print(e), self._print(c)) \ for e, c in expr.args[:-1]] if expr.args[-1].cond == True: ecpairs.append(r"%s & \text{otherwise}" % \ self._print(expr.args[-1].expr)) else: ecpairs.append(r"%s & \text{for}\: %s" % \ (self._print(expr.args[-1].cond), self._print(expr.args[-1].expr))) tex = r"\begin{cases} %s \end{cases}" return tex % r" \\".join(ecpairs) def _print_Matrix(self, expr): lines = [] for line in range(expr.rows): # horrible, should be 'rows' lines.append(" & ".join([ self._print(i) for i in expr[line,:] ])) out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' out_str = out_str.replace('%MATSTR%', self._settings['mat_str']) if self._settings['mat_delim']: left_delim = self._settings['mat_delim'] right_delim = self._delim_dict[left_delim] out_str = r'\left' + left_delim + out_str + \ r'\right' + right_delim return out_str % r"\\".join(lines) def _print_tuple(self, expr): return r"\begin{pmatrix}%s\end{pmatrix}" % \ r", & ".join([ self._print(i) for i in expr ]) def _print_list(self, expr): return r"\begin{bmatrix}%s\end{bmatrix}" % \ r", & ".join([ self._print(i) for i in expr ]) def _print_dict(self, expr): items = [] keys = expr.keys() keys.sort(key=cmp_to_key(Basic.compare_pretty)) for key in keys: val = expr[key] items.append("%s : %s" % (self._print(key), self._print(val))) return r"\begin{Bmatrix}%s\end{Bmatrix}" % r", & ".join(items) def _print_DiracDelta(self, expr): if len(expr.args) == 1 or expr.args[1] == 0: tex = r"\delta\left(%s\right)" % self._print(expr.args[0]) else: tex = r"\delta^{\left( %s \right)}\left( %s \right)" % (\ self._print(expr.args[1]), self._print(expr.args[0])) return tex def _print_ProductSet(self, p): return r" \cross ".join(self._print(set) for set in p.sets) def _print_FiniteSet(self, s): if len(s) > 10: #take ten elements from the set at random q = iter(s) printset = [q.next() for i in xrange(10)] else: printset = s try: printset.sort() except: pass return r"\left\{" + r", ".join(self._print(el) for el in printset) + r"\right\}" def _print_Interval(self, i): if i.start == i.end: return r"\left{%s\right}" % self._print(i.start) else: if i.left_open: left = '(' else: left = '[' if i.right_open: right = ')' else: right = ']' return r"\left%s%s, %s\right%s" % \ (left, self._print(i.start), self._print(i.end), right) def _print_Union(self, u): return r" \cup ".join([self._print(i) for i in u.args]) def _print_EmptySet(self, e): return r"\emptyset" def latex(expr, **settings): r"""Convert the given expression to LaTeX representation. You can specify how the generated code will be delimited using the 'mode' keyword. 'mode' can be one of 'plain', 'inline', 'equation' or 'equation*'. If 'mode' is set to 'plain', then the resulting code will not be delimited at all (this is the default). If 'mode' is set to 'inline' then inline LaTeX $ $ will be used. If 'mode' is set to 'equation' or 'equation*', the resulting code will be enclosed in the 'equation' or 'equation*' environment (remember to import 'amsmath' for 'equation*'), unless the 'itex' option is set. In the latter case, the $$ $$ syntax is used. >>> from sympy import latex, Rational >>> from sympy.abc import x, y, mu, tau >>> latex((2*tau)**Rational(7,2)) '8 \\sqrt{2} \\tau^{\\frac{7}{2}}' >>> latex((2*mu)**Rational(7,2), mode='plain') '8 \\sqrt{2} \\mu^{\\frac{7}{2}}' >>> latex((2*tau)**Rational(7,2), mode='inline') '$8 \\sqrt{2} \\tau^{\\frac{7}{2}}$' >>> latex((2*mu)**Rational(7,2), mode='equation*') '\\begin{equation*}8 \\sqrt{2} \\mu^{\\frac{7}{2}}\\end{equation*}' >>> latex((2*mu)**Rational(7,2), mode='equation') '\\begin{equation}8 \\sqrt{2} \\mu^{\\frac{7}{2}}\\end{equation}' >>> latex((2*mu)**Rational(7,2), mode='equation', itex=True) '$$8 \\sqrt{2} \\mu^{\\frac{7}{2}}$$' Besides all Basic based expressions, you can recursively convert Python containers (lists, tuples and dicts) and also SymPy matrices: >>> latex([2/x, y], mode='inline') '$\\begin{bmatrix}\\frac{2}{x}, & y\\end{bmatrix}$' """ return LatexPrinter(settings).doprint(expr) def print_latex(expr, **settings): """Prints LaTeX representation of the given expression.""" print latex(expr, **settings) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tree.py0000644000175000017500000000361312014170666024245 0ustar georgeskgeorgesk def pprint_nodes(subtrees): """ Prettyprints systems of nodes. Example: >> print pprint_nodes(["a", "b1\nb2", "c"]) +-a +-b1 | b2 +-c >> """ def indent(s,type=1): x = s.split("\n") r = "+-%s\n"%x[0] for a in x[1:]: if a=="": continue if type==1: r += "| %s\n"%a else: r += " %s\n"%a return r if len(subtrees)==0: return "" f=""; for a in subtrees[:-1]: f += indent(a) f += indent(subtrees[-1],2) return f def print_node(node): """ Returns an information about the "node". This includes class name, string representation and assumptions. """ s = "%s: %s\n" % (node.__class__.__name__, str(node)) if len(node._assumptions) > 0: for a in node._assumptions: s += "%s: %s\n" % (a, node._assumptions[a]) return s def tree(node): """ Returns a tree representation of "node" as a string. It uses print_node() together with pprint_nodes() on node.args recursively. See also: print_tree() """ subtrees = [] for arg in node.args: subtrees.append(tree(arg)) s = print_node(node)+pprint_nodes(subtrees) return s def print_tree(node): """ Prints a tree representation of "node". >>> from sympy.printing import print_tree >>> from sympy.abc import x >>> print_tree(x**2) # doctest: +SKIP Pow: x**2 +-Symbol: x | comparable: False +-Integer: 2 real: True nonzero: True comparable: True commutative: True infinitesimal: False unbounded: False noninteger: False zero: False complex: True bounded: True rational: True integer: True imaginary: False finite: True irrational: False See also: tree() """ print tree(node) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/conventions.py0000644000175000017500000000336112014170666025653 0ustar georgeskgeorgesk""" A few practical conventions common to all printers. """ import re def split_super_sub(text): """Split a symbol name into a name, superscripts and subscripts The first part of the symbol name is considered to be its actual 'name', followed by super- and subscripts. Each superscript is preceded with a "^" character or by "__". Each subscript is preceded by a "_" character. The three return values are the actual name, a list with superscripts and a list with subscripts. >>> from sympy.printing.conventions import split_super_sub >>> split_super_sub('a_x^1') ('a', ['1'], ['x']) >>> split_super_sub('var_sub1__sup_sub2') ('var', ['sup'], ['sub1', 'sub2']) """ pos = 0 name = None supers = [] subs = [] while pos < len(text): start = pos+1 if text[pos:pos+2] == "__": start += 1 pos_hat = text.find("^", start) if pos_hat < 0: pos_hat = len(text) pos_usc = text.find("_", start) if pos_usc < 0: pos_usc = len(text) pos_next = min(pos_hat, pos_usc) part = text[pos:pos_next] pos = pos_next if name is None: name = part elif part.startswith("^"): supers.append(part[1:]) elif part.startswith("__"): supers.append(part[2:]) elif part.startswith("_"): subs.append(part[1:]) else: raise RuntimeError("This should never happen.") # make a little exception when a name ends with digits, i.e. treat them # as a subscript too. m = re.match('(^[a-zA-Z]+)([0-9]+)$', name) if m is not None: name, sub = m.groups() subs.append(sub) return name, supers, subs wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/fcode.py0000644000175000017500000003615412014170666024374 0ustar georgeskgeorgesk""" Fortran code printer The FCodePrinter converts single sympy expressions into single Fortran expressions, using the functions defined in the Fortran 77 standard where possible. Some useful pointers to Fortran can be found on wikipedia: http://en.wikipedia.org/wiki/Fortran Most of the code below is based on the "Professional Programmer\'s Guide to Fortran77" by Clive G. Page: http://www.star.le.ac.uk/~cgp/prof77.html Fortran is a case-insensitive language. This might cause trouble because sympy is case sensitive. The implementation below does not care and leaves the responsibility for generating properly cased Fortran code to the user. """ from sympy.core import S, C, Add from sympy.printing.codeprinter import CodePrinter from sympy.printing.precedence import precedence from sympy.functions import sin, cos, tan, asin, acos, atan, atan2, sinh, \ cosh, tanh, sqrt, log, exp, Abs, sign, conjugate, Piecewise implicit_functions = set([ sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, sqrt, log, exp, Abs, sign, conjugate ]) class FCodePrinter(CodePrinter): """A printer to convert sympy expressions to strings of Fortran code""" printmethod = "_fcode" _default_settings = { 'order': None, 'full_prec': 'auto', 'assign_to': None, 'precision': 15, 'user_functions': {}, 'human': True, 'source_format': 'fixed', } def __init__(self, settings=None): CodePrinter.__init__(self, settings) self._init_leading_padding() assign_to = self._settings['assign_to'] if isinstance(assign_to, basestring): self._settings['assign_to'] = C.Symbol(assign_to) elif not isinstance(assign_to, (C.Basic, type(None))): raise TypeError("FCodePrinter cannot assign to object of type %s"% type(assign_to)) def _rate_index_position(self, p): """function to calculate score based on position among indices This method is used to sort loops in an optimized order, see CodePrinter._sort_optimized() """ return -p*5 def _get_statement(self, codestring): return codestring def _init_leading_padding(self): # leading columns depend on fixed or free format if self._settings['source_format'] == 'fixed': self._lead_code = " " self._lead_cont = " @ " self._lead_comment = "C " elif self._settings['source_format'] == 'free': self._lead_code = "" self._lead_cont = " " self._lead_comment = "! " else: raise ValueError( "Unknown source format: %s" % self._settings['source_format'] ) def _pad_leading_columns(self, lines): result = [] for line in lines: if line.startswith('!'): result.append(self._lead_comment + line[1:].lstrip()) else: result.append(self._lead_code + line) return result def _get_loop_opening_ending(self, indices): """Returns a tuple (open_lines, close_lines) containing lists of codelines """ open_lines = [] close_lines = [] for i in indices: # fortran arrays start at 1 and end at dimension var, start, stop = map(self._print, [i.label, i.lower+1, i.upper+1]) open_lines.append("do %s = %s, %s" % (var, start, stop)) close_lines.append("end do") return open_lines, close_lines def doprint(self, expr): """Returns Fortran code for expr (as a string)""" # find all number symbols self._number_symbols = set() # keep a set of expressions that are not strictly translatable to # Fortran. self._not_supported = set() lines = [] if isinstance(expr, Piecewise): # support for top-level Piecewise function for i, (e, c) in enumerate(expr.args): if i == 0: lines.append("if (%s) then" % self._print(c)) elif i == len(expr.args)-1 and c == True: lines.append("else") else: lines.append("else if (%s) then" % self._print(c)) lines.extend(self._doprint_a_piece(e, self._settings['assign_to'])) lines.append("end if") else: lines.extend(self._doprint_a_piece(expr, self._settings['assign_to'])) # format the output if self._settings["human"]: frontlines = [] if len(self._not_supported) > 0: frontlines.append("! Not Fortran:") for expr in sorted(self._not_supported, key=self._print): frontlines.append("! %s" % expr) for name, value in sorted(self._number_symbols, key=str): frontlines.append("parameter (%s = %s)" % (str(name), value)) frontlines.extend(lines) lines = frontlines lines = self.indent_code(lines) lines = self._wrap_fortran(lines) result = "\n".join(lines) else: lines = self.indent_code(lines) lines = self._wrap_fortran(lines) result = self._number_symbols, self._not_supported, "\n".join(lines) del self._not_supported del self._number_symbols return result def _print_Add(self, expr): # purpose: print complex numbers nicely in Fortran. # collect the purely real and purely imaginary parts: pure_real = [] pure_imaginary = [] mixed = [] for arg in expr.args: if arg.is_real and arg.is_number: pure_real.append(arg) elif arg.is_imaginary and arg.is_number: pure_imaginary.append(arg) else: mixed.append(arg) if len(pure_imaginary) > 0: if len(mixed) > 0: PREC = precedence(expr) term = Add(*mixed) t = self._print(term) if t.startswith('-'): sign = "-" t = t[1:] else: sign = "+" if precedence(term) < PREC: t = "(%s)" % t return "cmplx(%s,%s) %s %s" % ( self._print(Add(*pure_real)), self._print(-S.ImaginaryUnit*Add(*pure_imaginary)), sign, t, ) else: return "cmplx(%s,%s)" % ( self._print(Add(*pure_real)), self._print(-S.ImaginaryUnit*Add(*pure_imaginary)), ) else: return CodePrinter._print_Add(self, expr) def _print_Function(self, expr): name = self._settings["user_functions"].get(expr.__class__) if name is None: if expr.func == conjugate: name = "conjg" else: name = expr.func.__name__ if hasattr(expr, '_imp_') and isinstance(expr._imp_, C.Lambda): # inlined function. # the expression is printed with _print to avoid loops return self._print(expr._imp_(*expr.args)) if expr.func not in implicit_functions: self._not_supported.add(expr) return "%s(%s)" % (name, self.stringify(expr.args, ", ")) _print_factorial = _print_Function def _print_ImaginaryUnit(self, expr): # purpose: print complex numbers nicely in Fortran. return "cmplx(0,1)" def _print_int(self, expr): return str(expr) def _print_Mul(self, expr): # purpose: print complex numbers nicely in Fortran. if expr.is_imaginary and expr.is_number: return "cmplx(0,%s)" % ( self._print(-S.ImaginaryUnit*expr) ) else: return CodePrinter._print_Mul(self, expr) _print_Exp1 = CodePrinter._print_NumberSymbol _print_Pi = CodePrinter._print_NumberSymbol def _print_Pow(self, expr): PREC = precedence(expr) if expr.exp is S.NegativeOne: return '1.0/%s'%(self.parenthesize(expr.base, PREC)) elif expr.exp == 0.5: if expr.base.is_integer: # Fortan intrinsic sqrt() does not accept integer argument if expr.base.is_Number: return 'sqrt(%s.0d0)' % self._print(expr.base) else: return 'sqrt(dble(%s))' % self._print(expr.base) else: return 'sqrt(%s)' % self._print(expr.base) else: return CodePrinter._print_Pow(self, expr) def _print_Rational(self, expr): p, q = int(expr.p), int(expr.q) return "%d.0d0/%d.0d0" % (p, q) def _print_Float(self, expr): printed = CodePrinter._print_Float(self, expr) e = printed.find('e') if e > -1: return "%sd%s" % (printed[:e], printed[e+1:]) return "%sd0" % printed def _print_Indexed(self, expr): inds = [ self._print(i) for i in expr.indices ] return "%s(%s)" % (self._print(expr.base.label), ", ".join(inds)) def _print_Idx(self, expr): return self._print(expr.label) def _wrap_fortran(self, lines): """Wrap long Fortran lines Argument: lines -- a list of lines (without \\n character) A comment line is split at white space. Code lines are split with a more complex rule to give nice results. """ # routine to find split point in a code line my_alnum = set("_+-.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_") my_white = set(" \t()") def split_pos_code(line, endpos): if len(line) <= endpos: return len(line) pos = endpos split = lambda pos: \ (line[pos] in my_alnum and line[pos-1] not in my_alnum) or \ (line[pos] not in my_alnum and line[pos-1] in my_alnum) or \ (line[pos] in my_white and line[pos-1] not in my_white) or \ (line[pos] not in my_white and line[pos-1] in my_white) while not split(pos): pos -= 1 if pos == 0: return endpos return pos # split line by line and add the splitted lines to result result = [] if self._settings['source_format'] == 'free': trailing = ' &' else: trailing = '' for line in lines: if line.startswith(self._lead_comment): # comment line if len(line) > 72: pos = line.rfind(" ", 6, 72) if pos == -1: pos = 72 hunk = line[:pos] line = line[pos:].lstrip() result.append(hunk) while len(line) > 0: pos = line.rfind(" ", 0, 66) if pos == -1 or len(line) < 66: pos = 66 hunk = line[:pos] line = line[pos:].lstrip() result.append("%s%s" % (self._lead_comment, hunk)) else: result.append(line) elif line.startswith(self._lead_code): # code line pos = split_pos_code(line, 72) hunk = line[:pos].rstrip() line = line[pos:].lstrip() if line: hunk += trailing result.append(hunk) while len(line) > 0: pos = split_pos_code(line, 65) hunk = line[:pos].rstrip() line = line[pos:].lstrip() if line: hunk += trailing result.append("%s%s" % (self._lead_cont, hunk)) else: result.append(line) return result def indent_code(self, code): """Accepts a string of code or a list of code lines""" if isinstance(code, basestring): code_lines = self.indent_code(code.splitlines(True)) return ''.join(code_lines) free = self._settings['source_format'] == 'free' code = [ line.lstrip(' \t') for line in code ] inc_keyword = ('do ', 'if(', 'if ', 'do\n', 'else') dec_keyword = ('end do', 'enddo', 'end if', 'endif', 'else') increase = [ int(any(map(line.startswith, inc_keyword))) for line in code ] decrease = [ int(any(map(line.startswith, dec_keyword))) for line in code ] continuation = [ int(any(map(line.endswith, ['&', '&\n']))) for line in code ] level = 0 cont_padding = 0 tabwidth = 3 new_code = [] for i, line in enumerate(code): if line == '' or line == '\n': new_code.append(line) continue level -= decrease[i] if free: padding = " "*(level*tabwidth + cont_padding) else: padding = " "*level*tabwidth line = "%s%s" % (padding, line) if not free: line = self._pad_leading_columns([line])[0] new_code.append(line) if continuation[i]: cont_padding = 2*tabwidth else: cont_padding = 0 level += increase[i] if not free: return self._wrap_fortran(new_code) return new_code def fcode(expr, **settings): """Converts an expr to a string of Fortran 77 code Arguments: expr -- a sympy expression to be converted Optional arguments: assign_to -- When given, the argument is used as the name of the variable to which the Fortran expression is assigned. (This is helpful in case of line-wrapping.) precision -- the precision for numbers such as pi [default=15] user_functions -- A dictionary where keys are FunctionClass instances and values are there string representations. human -- If True, the result is a single string that may contain some parameter statements for the number symbols. If False, the same information is returned in a more programmer-friendly data structure. source_format -- The source format can be either 'fixed' or 'free'. [default='fixed'] >>> from sympy import fcode, symbols, Rational, pi, sin >>> x, tau = symbols('x,tau') >>> fcode((2*tau)**Rational(7,2)) ' 8*sqrt(2.0d0)*tau**(7.0d0/2.0d0)' >>> fcode(sin(x), assign_to="s") ' s = sin(x)' >>> print fcode(pi) parameter (pi = 3.14159265358979d0) pi """ # run the printer printer = FCodePrinter(settings) return printer.doprint(expr) def print_fcode(expr, **settings): """Prints the Fortran representation of the given expression. See fcode for the meaning of the optional arguments. """ print fcode(expr, **settings) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/str.py0000644000175000017500000003456612014170666024131 0ustar georgeskgeorgesk""" A Printer for generating readable representation of most sympy classes. """ from sympy.core import S, Rational, Pow, Basic from printer import Printer from sympy.printing.precedence import precedence, PRECEDENCE import sympy.mpmath.libmp as mlib from sympy.mpmath.libmp import prec_to_dps from sympy.polys.polyerrors import PolynomialError from sympy.core.compatibility import cmp_to_key class StrPrinter(Printer): printmethod = "_sympystr" _default_settings = { "order": None, "full_prec": "auto", } def parenthesize(self, item, level): if precedence(item) <= level: return "(%s)"%self._print(item) else: return self._print(item) def stringify(self, args, sep, level=0): return sep.join([self.parenthesize(item, level) for item in args]) def emptyPrinter(self, expr): if isinstance(expr, str): return expr elif isinstance(expr, Basic): if hasattr(expr, "args"): return repr(expr) else: raise else: return str(expr) def _print_Add(self, expr, order=None): terms = self._as_ordered_terms(expr, order=order) PREC = precedence(expr) l = [] for term in terms: t = self._print(term) if t.startswith('-'): sign = "-" t = t[1:] else: sign = "+" if precedence(term) < PREC: l.extend([sign, "(%s)"%t]) else: l.extend([sign, t]) sign = l.pop(0) if sign=='+': sign = "" return sign + ' '.join(l) def _print_AppliedPredicate(self, expr): return '%s(%s)' % (expr.func, expr.arg) def _print_Basic(self, expr): l = [self._print(o) for o in expr.args] return expr.__class__.__name__ + "(%s)"%", ".join(l) def _print_Catalan(self, expr): return 'Catalan' def _print_ComplexInfinity(self, expr): return 'zoo' def _print_Derivative(self, expr): return 'Derivative(%s)'%", ".join(map(self._print, expr.args)) def _print_dict(self, expr): keys = expr.keys() keys.sort( key=cmp_to_key(Basic.compare_pretty) ) items = [] for key in keys: item = "%s: %s" % (self._print(key), self._print(expr[key])) items.append(item) return "{%s}"%", ".join(items) def _print_Dummy(self, expr): return '_' + expr.name def _print_EulerGamma(self, expr): return 'EulerGamma' def _print_Exp1(self, expr): return 'E' def _print_ExprCondPair(self, expr): return '(%s, %s)' % (expr.expr, expr.cond) def _print_factorial(self, expr): return "%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Pow"]) def _print_FiniteSet(self, s): if len(s) > 10: #take ten elements from the set at random q = iter(s) printset = [q.next() for i in xrange(10)] else: printset = s try: printset = sorted(printset) except: pass return '{' + ', '.join(self._print(el) for el in printset) + '}' def _print_Function(self, expr): return expr.func.__name__ + "(%s)"%self.stringify(expr.args, ", ") def _print_GeometryEntity(self, expr): # GeometryEntity is special -- it's base is tuple return str(expr) def _print_GoldenRatio(self, expr): return 'GoldenRatio' def _print_ImaginaryUnit(self, expr): return 'I' def _print_Infinity(self, expr): return 'oo' def _print_Integral(self, expr): def _xab_tostr(xab): if len(xab) == 1: return self._print(xab[0]) else: return self._print((xab[0],) + tuple(xab[1:])) L = ', '.join([_xab_tostr(l) for l in expr.limits]) return 'Integral(%s, %s)' % (self._print(expr.function), L) def _print_FiniteSet(self, s): if len(s) > 10: #take ten elements from the set at random q = iter(s) printset = [q.next() for i in xrange(10)] else: printset = s try: printset = sorted(printset) except: pass return '{' + ', '.join(self._print(el) for el in printset) + '}' def _print_Interval(self, i): if i.left_open: left = '(' else: left = '[' if i.right_open: right = ')' else: right = ']' return "%s%s, %s%s" % \ (left, self._print(i.start), self._print(i.end), right) def _print_Lambda(self, obj): args, expr = obj.args if len(args) == 1: return "Lambda(%s, %s)" % (args.args[0], expr) else: arg_string = ", ".join(self._print(arg) for arg in args) return "Lambda((%s), %s" % (arg_string, expr) def _print_LatticeOp(self, expr): args = sorted(expr.args, key=cmp_to_key(expr._compare_pretty)) return expr.func.__name__ + "(%s)"%", ".join(self._print(arg) for arg in args) def _print_Limit(self, expr): e, z, z0, dir = expr.args if dir == "+": return "Limit(%s, %s, %s)" % (e, z, z0) else: return "Limit(%s, %s, %s, dir='%s')" % (e, z, z0, dir) def _print_list(self, expr): return "[%s]"%self.stringify(expr, ", ") def _print_Matrix(self, expr): return expr._format_str(lambda elem: self._print(elem)) def _print_DeferredVector(self, expr): return expr.name def _print_Mul(self, expr): coeff, terms = expr.as_coeff_mul() if coeff.is_negative: coeff = -coeff if coeff is not S.One: terms = (coeff,) + terms sign = "-" else: terms = (coeff,) + terms sign = "" a = [] # items in the numerator b = [] # items that are in the denominator (if any) if self.order != 'old': args = expr._new_rawargs(*terms).as_ordered_factors() else: args = terms # Gather args for numerator/denominator for item in args: if item.is_Pow and item.exp.is_Rational and item.exp.is_negative: b.append(Pow(item.base, -item.exp)) elif item.is_Rational and item is not S.Infinity: if item.p != 1: a.append(Rational(item.p)) if item.q != 1: b.append(Rational(item.q)) else: a.append(item) if len(a)==0: a = [S.One] a_str = map(lambda x:self.parenthesize(x, precedence(expr)), a) b_str = map(lambda x:self.parenthesize(x, precedence(expr)), b) if len(b)==0: return sign + '*'.join(a_str) elif len(b)==1: if len(a)==1 and not (a[0].is_Atom or a[0].is_Add): return sign + "%s/"%a_str[0] + '*'.join(b_str) else: return sign + '*'.join(a_str) + "/%s"%b_str[0] else: return sign + '*'.join(a_str) + "/(%s)"%'*'.join(b_str) def _print_NaN(self, expr): return 'nan' def _print_NegativeInfinity(self, expr): return '-oo' def _print_Normal(self, expr): return "Normal(%s, %s)"%(expr.mu, expr.sigma) def _print_Order(self, expr): if len(expr.variables) <= 1: return 'O(%s)'%self._print(expr.expr) else: return 'O(%s)'%self.stringify(expr.args, ', ', 0) def _print_PDF(self, expr): return 'PDF(%s, (%s, %s, %s))' % \ (self._print(expr.pdf.args[1]), self._print(expr.pdf.args[0]), \ self._print(expr.domain[0]), self._print(expr.domain[1])) def _print_Pi(self, expr): return 'pi' def _print_Poly(self, expr): terms, gens = [], [ self._print(s) for s in expr.gens ] for monom, coeff in expr.terms(): s_monom = [] for i, exp in enumerate(monom): if exp > 0: if exp == 1: s_monom.append(gens[i]) else: s_monom.append(gens[i] + "**%d" % exp) s_monom = "*".join(s_monom) if coeff.is_Add: if s_monom: s_coeff = "(" + self._print(coeff) + ")" else: s_coeff = self._print(coeff) else: if s_monom: if coeff is S.One: terms.extend(['+', s_monom]) continue if coeff is S.NegativeOne: terms.extend(['-', s_monom]) continue s_coeff = self._print(coeff) if not s_monom: s_term = s_coeff else: s_term = s_coeff + "*" + s_monom if s_term.startswith('-'): terms.extend(['-', s_term[1:]]) else: terms.extend(['+', s_term]) if terms[0] in ['-', '+']: modifier = terms.pop(0) if modifier == '-': terms[0] = '-' + terms[0] format = expr.__class__.__name__ + "(%s, %s" try: format += ", modulus=%s" % expr.get_modulus() except PolynomialError: format += ", domain='%s'" % expr.get_domain() format += ")" return format % (' '.join(terms), ', '.join(gens)) def _print_ProductSet(self, p): return ' x '.join(self._print(set) for set in p.sets) def _print_AlgebraicNumber(self, expr): if expr.is_aliased: return self._print(expr.as_poly().as_expr()) else: return self._print(expr.as_expr()) def _print_Pow(self, expr): PREC = precedence(expr) if expr.exp is S.NegativeOne: return '1/%s'%(self.parenthesize(expr.base, PREC)) else: return '%s**%s'%(self.parenthesize(expr.base, PREC), self.parenthesize(expr.exp, PREC)) def _print_Integer(self, expr): return str(expr.p) def _print_int(self, expr): return str(expr) def _print_mpz(self, expr): return str(expr) def _print_Rational(self, expr): return '%s/%s' % (expr.p, expr.q) def _print_Fraction(self, expr): return '%s/%s' % (expr.numerator, expr.denominator) def _print_mpq(self, expr): return '%s/%s' % (expr.numer(), expr.denom()) def _print_Float(self, expr): prec = expr._prec if prec < 5: dps = 0 else: dps = prec_to_dps(expr._prec) if self._settings["full_prec"] == True: strip = False elif self._settings["full_prec"] == False: strip = True elif self._settings["full_prec"] == "auto": strip = self._print_level > 1 return mlib.to_str(expr._mpf_, dps, strip_zeros=strip) def _print_Relational(self, expr): return '%s %s %s'%(self.parenthesize(expr.lhs, precedence(expr)), expr.rel_op, self.parenthesize(expr.rhs, precedence(expr))) def _print_DMP(self, expr): cls = expr.__class__.__name__ rep = self._print(expr.rep) dom = self._print(expr.dom) return "%s(%s, %s)" % (cls, rep, dom) def _print_DMF(self, expr): cls = expr.__class__.__name__ num = self._print(expr.num) den = self._print(expr.den) dom = self._print(expr.dom) return "%s((%s, %s), %s)" % (cls, num, den, dom) def _print_RootOf(self, expr): return "RootOf(%s, %d)" % (self._print_Add(expr.expr, order='lex'), expr.index) def _print_RootSum(self, expr): args = [self._print_Add(expr.expr, order='lex')] if expr.fun is not S.IdentityFunction: args.append(self._print(expr.fun)) return "RootSum(%s)" % ", ".join(args) def _print_Sample(self, expr): return "Sample([%s])"%self.stringify(expr, ", ", 0) def __print_set(self, expr): items = list(expr) items.sort( key=cmp_to_key(Basic.compare_pretty) ) args = ', '.join(self._print(item) for item in items) if args: args = '[%s]' % args return '%s(%s)' % (type(expr).__name__, args) _print_set = __print_set _print_frozenset = __print_set def _print_SparseMatrix(self, expr): return self._print(expr.toMatrix()) def _print_Sum(self, expr): def _xab_tostr(xab): if len(xab) == 1: return self._print(xab[0]) else: return self._print((xab[0],) + tuple(xab[1:])) L = ', '.join([_xab_tostr(l) for l in expr.limits]) return 'Sum(%s, %s)' % (self._print(expr.function), L) def _print_Symbol(self, expr): return expr.name def _print_Predicate(self, expr): return "Q.%s" % expr.name def _print_str(self, expr): return expr def _print_tuple(self, expr): if len(expr)==1: return "(%s,)"%self._print(expr[0]) else: return "(%s)"%self.stringify(expr, ", ") def _print_Tuple(self, expr): return self._print_tuple(expr) def _print_Uniform(self, expr): return "Uniform(%s, %s)"%(expr.a, expr.b) def _print_Union(self, expr): return ' U '.join(self._print(set) for set in expr.args) def _print_Unit(self, expr): return expr.abbrev def _print_Wild(self, expr): return expr.name + '_' def _print_WildFunction(self, expr): return expr.name + '_' def _print_Zero(self, expr): return "0" def sstr(expr, **settings): """Returns the expression as a string. Example: >>> from sympy import symbols, Eq, sstr >>> a, b = symbols('a b') >>> sstr(Eq(a + b, 0)) 'a + b == 0' """ p = StrPrinter(settings) s = p.doprint(expr) return s class StrReprPrinter(StrPrinter): """(internal) -- see sstrrepr""" def _print_str(self, s): return repr(s) def sstrrepr(expr, **settings): """return expr in mixed str/repr form i.e. strings are returned in repr form with quotes, and everything else is returned in str form. This function could be useful for hooking into sys.displayhook """ p = StrReprPrinter(settings) s = p.doprint(expr) return s wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/ccode.py0000644000175000017500000002230612014170666024363 0ustar georgeskgeorgesk""" C code printer The CCodePrinter converts single sympy expressions into single C expressions, using the functions defined in math.h where possible. A complete code generator, which uses ccode extensively, can be found in sympy.utilities.codegen. The codegen module can be used to generate complete source code files that are compilable without further modifications. """ from sympy.core import S, C from sympy.printing.codeprinter import CodePrinter from sympy.printing.precedence import precedence # dictionary mapping sympy function to (argument_conditions, C_function). # Used in CCodePrinter._print_Function(self) known_functions = { "ceiling": [(lambda x: True, "ceil")], "Abs": [(lambda x: not x.is_integer, "fabs")], } class CCodePrinter(CodePrinter): """A printer to convert python expressions to strings of c code""" printmethod = "_ccode" _default_settings = { 'order': None, 'full_prec': 'auto', 'precision': 15, 'user_functions': {}, 'human': True, } def __init__(self, settings={}): """Register function mappings supplied by user""" CodePrinter.__init__(self, settings) self.known_functions = dict(known_functions) userfuncs = settings.get('user_functions', {}) for k,v in userfuncs.items(): if not isinstance(v, tuple): userfuncs[k] = (lambda *x: True, v) self.known_functions.update(userfuncs) def _rate_index_position(self, p): """function to calculate score based on position among indices This method is used to sort loops in an optimized order, see CodePrinter._sort_optimized() """ return p*5 def _get_statement(self, codestring): return "%s;" % codestring def doprint(self, expr, assign_to=None): if isinstance(assign_to, basestring): assign_to = C.Symbol(assign_to) elif not isinstance(assign_to, (C.Basic, type(None))): raise TypeError("CCodePrinter cannot assign to object of type %s"% type(result_variable)) # keep a set of expressions that are not strictly translatable to C # and number constants that must be declared and initialized not_c = self._not_supported = set() self._number_symbols = set() # We treat top level Piecewise here to get if tests outside loops lines = [] if isinstance(expr, C.Piecewise): for i, (e, c) in enumerate(expr.args): if i == 0: lines.append("if (%s) {" % self._print(c)) elif i == len(expr.args)-1 and c == True: lines.append("else {") else: lines.append("else if (%s) {" % self._print(c)) code0 = self._doprint_a_piece(e, assign_to) lines.extend(code0) lines.append("}") else: code0 = self._doprint_a_piece(expr, assign_to) lines.extend(code0) # format the output if self._settings["human"]: frontlines = [] if len(not_c) > 0: frontlines.append("// Not C:") for expr in sorted(not_c, key=str): frontlines.append("// %s" % expr) for name, value in sorted(self._number_symbols, key=str): frontlines.append("double const %s = %s;" % (name, value)) lines = frontlines + lines lines = "\n".join(lines) result = self.indent_code(lines) else: lines = self.indent_code("\n".join(lines)) result = self._number_symbols, not_c, lines del self._not_supported del self._number_symbols return result def _get_loop_opening_ending(self, indices): """Returns a tuple (open_lines, close_lines) containing lists of codelines """ open_lines = [] close_lines = [] loopstart = "for (int %(var)s=%(start)s; %(var)s<%(end)s; %(var)s++){" for i in indices: # C arrays start at 0 and end at dimension-1 open_lines.append(loopstart % { 'var': self._print(i.label), 'start': self._print(i.lower), 'end': self._print(i.upper + 1)}) close_lines.append("}") return open_lines, close_lines def _print_Pow(self, expr): PREC = precedence(expr) if expr.exp is S.NegativeOne: return '1.0/%s'%(self.parenthesize(expr.base, PREC)) elif expr.exp == 0.5: return 'sqrt(%s)' % self._print(expr.base) else: return 'pow(%s, %s)'%(self._print(expr.base), self._print(expr.exp)) def _print_Rational(self, expr): p, q = int(expr.p), int(expr.q) return '%d.0/%d.0' % (p, q) def _print_Indexed(self, expr): # calculate index for 1d array dims = expr.shape inds = [ i.label for i in expr.indices ] elem = S.Zero offset = S.One for i in reversed(range(expr.rank)): elem += offset*inds[i] offset *= dims[i] return "%s[%s]" % (self._print(expr.base.label), self._print(elem)) def _print_Exp1(self, expr): return "M_E" def _print_Pi(self, expr): return 'M_PI' def _print_Infinity(self, expr): return 'HUGE_VAL' def _print_NegativeInfinity(self, expr): return '-HUGE_VAL' def _print_Piecewise(self, expr): # This method is called only for inline if constructs # Top level piecewise is handled in doprint() ecpairs = ["(%s) {\n%s\n}\n" % (self._print(c), self._print(e)) \ for e, c in expr.args[:-1]] last_line = "" if expr.args[-1].cond == True: last_line = "else {\n%s\n}" % self._print(expr.args[-1].expr) else: ecpairs.append("(%s) {\n%s\n" % \ (self._print(expr.args[-1].cond), self._print(expr.args[-1].expr))) code = "if %s" + last_line return code % "else if ".join(ecpairs) def _print_And(self, expr): PREC = precedence(expr) return '&&'.join(self.parenthesize(a, PREC) for a in expr.args) def _print_Or(self, expr): PREC = precedence(expr) return '||'.join(self.parenthesize(a, PREC) for a in expr.args) def _print_Not(self, expr): PREC = precedence(expr) return '!'+self.parenthesize(expr.args[0], PREC) def _print_Function(self, expr): if expr.func.__name__ in self.known_functions: cond_cfunc = self.known_functions[expr.func.__name__] for cond, cfunc in cond_cfunc: if cond(*expr.args): return "%s(%s)" % (cfunc, self.stringify(expr.args, ", ")) if hasattr(expr, '_imp_') and isinstance(expr._imp_, C.Lambda): # inlined function return self._print(expr._imp_(*expr.args)) return CodePrinter._print_Function(self, expr) def indent_code(self, code): """Accepts a string of code or a list of code lines""" if isinstance(code, basestring): code_lines = self.indent_code(code.splitlines(True)) return ''.join(code_lines) tab = " " inc_token = ('{', '(', '{\n', '(\n') dec_token = ('}', ')') code = [ line.lstrip(' \t') for line in code ] increase = [ int(any(map(line.endswith, inc_token))) for line in code ] decrease = [ int(any(map(line.startswith, dec_token))) for line in code ] pretty = [] level = 0 for n, line in enumerate(code): if line == '' or line == '\n': pretty.append(line) continue level -= decrease[n] pretty.append("%s%s" % (tab*level, line)) level += increase[n] return pretty def ccode(expr, assign_to=None, **settings): r"""Converts an expr to a string of c code Arguments: expr -- a sympy expression to be converted Optional arguments: precision -- the precision for numbers such as pi [default=15] user_functions -- A dictionary where keys are FunctionClass instances and values are there string representations. Alternatively, the dictionary value can be a list of tuples i.e. [(argument_test, cfunction_string)]. See below for examples. human -- If True, the result is a single string that may contain some constant declarations for the number symbols. If False, the same information is returned in a more programmer-friendly data structure. >>> from sympy import ccode, symbols, Rational, sin >>> x, tau = symbols(["x", "tau"]) >>> ccode((2*tau)**Rational(7,2)) '8*sqrt(2)*pow(tau, 7.0/2.0)' >>> ccode(sin(x), assign_to="s") 's = sin(x);' """ return CCodePrinter(settings).doprint(expr, assign_to) def print_ccode(expr, **settings): """Prints C representation of the given expression.""" print ccode(expr, **settings) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/mathml.py0000644000175000017500000003124312014170666024570 0ustar georgeskgeorgesk""" A MathML printer. """ from sympy import Basic, sympify, S from sympy.simplify import fraction from printer import Printer from conventions import split_super_sub class MathMLPrinter(Printer): """Prints an expression to the MathML markup language Whenever possible tries to use Content markup and not Presentation markup. References: http://www.w3.org/TR/MathML2/ """ printmethod = "_mathml" _default_settings = { "order": None, "encoding": "utf-8" } def __init__(self, settings=None): Printer.__init__(self, settings) from xml.dom.minidom import Document self.dom = Document() def doprint(self, expr): mathML = Printer._print(self, expr) return mathML.toxml(encoding=self._settings['encoding']) def mathml_tag(self, e): """Returns the MathML tag for an expression.""" translate = { 'Add': 'plus', 'Mul': 'times', 'Derivative': 'diff', 'Number': 'cn', 'int': 'cn', 'Pow': 'power', 'Symbol': 'ci', 'Integral': 'int', 'Sum': 'sum', 'sin': 'sin', 'cos': 'cos', 'tan': 'tan', 'cot': 'cot', 'asin': 'arcsin', 'asinh': 'arcsinh', 'acos': 'arccos', 'acosh': 'arccosh', 'atan': 'arctan', 'atanh': 'arctanh', 'acot': 'arccot', 'atan2': 'arctan', 'log': 'ln', 'Equality': 'eq', 'Unequality': 'neq', 'StrictInequality': 'lt', 'Inequality': 'leq' } for cls in e.__class__.__mro__: n = cls.__name__ if n in translate: return translate[n] # Not found in the MRO set n = e.__class__.__name__ return n.lower() def _print_Mul(self, expr): coeff, terms = expr.as_coeff_mul() if coeff.is_negative: x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('minus')) x.appendChild(self._print_Mul(-expr)) return x numer, denom = fraction(expr) if not denom is S.One: x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('divide')) x.appendChild(self._print(numer)) x.appendChild(self._print(denom)) return x if self.order != 'old': terms = expr._new_rawargs(*terms).as_ordered_factors() if coeff == 1 and len(terms) == 1: return self._print(terms[0]) x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('times')) if(coeff != 1): x.appendChild(self._print(coeff)) for term in terms: x.appendChild(self._print(term)) return x def _print_Add(self, expr, order=None): args = self._as_ordered_terms(expr, order=order) lastProcessed = self._print(args[0]) plusNodes = [] for arg in args[1:]: coeff, _ = arg.as_coeff_mul() if(coeff.is_negative): #use minus x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('minus')) x.appendChild(lastProcessed) x.appendChild(self._print(-arg)) #invert expression since this is now minused lastProcessed = x; if(arg == args[-1]): plusNodes.append(lastProcessed) else: plusNodes.append(lastProcessed) lastProcessed = self._print(arg) if(arg == args[-1]): plusNodes.append(self._print(arg)) if len(plusNodes) == 1: return lastProcessed x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('plus')) while len(plusNodes) > 0: x.appendChild(plusNodes.pop(0)) return x def _print_Matrix(self, m): x = self.dom.createElement('matrix') for i in range(m.lines): x_r = self.dom.createElement('matrixrow') for j in range(m.cols): x_r.appendChild(self._print(m[i,j])) x.appendChild(x_r) return x def _print_Rational(self, e): if e.q == 1: #don't divide x = self.dom.createElement('cn') x.appendChild(self.dom.createTextNode(str(e.p))) return x x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('divide')) #numerator xnum = self.dom.createElement('cn') xnum.appendChild(self.dom.createTextNode(str(e.p))) #denomenator xdenom = self.dom.createElement('cn') xdenom.appendChild(self.dom.createTextNode(str(e.q))) x.appendChild(xnum) x.appendChild(xdenom) return x def _print_Limit(self, e): x = self.dom.createElement('apply') x.appendChild(self.dom.createElement(self.mathml_tag(e))) x_1 = self.dom.createElement('bvar') x_2 = self.dom.createElement('lowlimit') x_1.appendChild(self._print(e.args[1])) x_2.appendChild(self._print(e.args[2])) x.appendChild(x_1) x.appendChild(x_2) x.appendChild(self._print(e.args[0])) return x def _print_ImaginaryUnit(self,e): return self.dom.createElement('imaginaryi') def _print_EulerGamma(self,e): return self.dom.createElement('eulergamma') def _print_GoldenRatio(self,e): """We use unicode #x3c6 for Greek letter phi as defined here http://www.w3.org/Math/characters/""" x = self.dom.createElement('cn') x.appendChild(self.dom.createTextNode(u"\u03c6")) return x def _print_Exp1(self,e): return self.dom.createElement('exponentiale') def _print_Pi(self, e): return self.dom.createElement('pi') def _print_Infinity(self, e): return self.dom.createElement('infinity') def _print_Negative_Infinity(self,e): x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('minus')) x.appendChild(self.dom.createElement('infinity')) return x def _print_Integral(self, e): def lime_recur(limits): x = self.dom.createElement('apply') x.appendChild(self.dom.createElement(self.mathml_tag(e))) bvar_elem = self.dom.createElement('bvar') bvar_elem.appendChild(self._print(limits[0][0])) x.appendChild(bvar_elem) if len(limits[0]) == 3: low_elem = self.dom.createElement('lowlimit') low_elem.appendChild(self._print(limits[0][1])) x.appendChild(low_elem) up_elem = self.dom.createElement('uplimit') up_elem.appendChild(self._print(limits[0][2])) x.appendChild(up_elem) if len(limits[0]) == 2: up_elem = self.dom.createElement('uplimit') up_elem.appendChild(self._print(limits[0][1])) x.appendChild(up_elem) if len(limits) == 1: x.appendChild(self._print(e.function)) else: x.appendChild(lime_recur(limits[1:])) return x limits = list(e.limits) limits.reverse() return lime_recur(limits) def _print_Sum(self, e): # Printer can be shared because Sum and Integral have the # same internal representation. return self._print_Integral(e) def _print_Symbol(self, sym): ci = self.dom.createElement(self.mathml_tag(sym)) def join(items): if len(items) > 1: mrow = self.dom.createElement('mml:mrow') for i, item in enumerate(items): if i>0: mo = self.dom.createElement('mml:mo') mo.appendChild(self.dom.createTextNode(" ")) mrow.appendChild(mo) mi = self.dom.createElement('mml:mi') mi.appendChild(self.dom.createTextNode(item)) mrow.appendChild(mi) return mrow else: mi = self.dom.createElement('mml:mi') mi.appendChild(self.dom.createTextNode(items[0])) return mi name, supers, subs = split_super_sub(sym.name) mname = self.dom.createElement('mml:mi') mname.appendChild(self.dom.createTextNode(name)) if len(supers) == 0: if len(subs) == 0: ci.appendChild(self.dom.createTextNode(name)) else: msub = self.dom.createElement('mml:msub') msub.appendChild(mname) msub.appendChild(join(subs)) ci.appendChild(msub) else: if len(subs) == 0: msup = self.dom.createElement('mml:msup') msup.appendChild(mname) msup.appendChild(join(supers)) ci.appendChild(msup) else: msubsup = self.dom.createElement('mml:msubsup') msubsup.appendChild(mname) msubsup.appendChild(join(subs)) msubsup.appendChild(join(supers)) ci.appendChild(msubsup) return ci def _print_Pow(self, e): #Here we use root instead of power if the exponent is the reciprocal of an integer if e.exp.is_Rational and e.exp.p == 1: x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('root')) if e.exp.q != 2: xmldeg = self.dom.createElement('degree') xmlci = self.dom.createElement('ci') xmlci.appendChild(self.dom.createTextNode(str(e.exp.q))) xmldeg.appendChild(xmlci) x.appendChild(xmldeg) x.appendChild(self._print(e.base)) return x x = self.dom.createElement('apply') x_1 = self.dom.createElement(self.mathml_tag(e)) x.appendChild(x_1) x.appendChild(self._print(e.base)) x.appendChild(self._print(e.exp)) return x def _print_Number(self, e): x = self.dom.createElement(self.mathml_tag(e)) x.appendChild(self.dom.createTextNode(str(e))) return x def _print_Derivative(self, e): x = self.dom.createElement('apply') x.appendChild(self.dom.createElement(self.mathml_tag(e))) x_1 = self.dom.createElement('bvar') for sym in e.variables: x_1.appendChild(self._print(sym)) x.appendChild(x_1) x.appendChild(self._print(e.expr)) return x def _print_Function(self, e): x = self.dom.createElement("apply") x.appendChild(self.dom.createElement(self.mathml_tag(e))) for arg in e.args: x.appendChild(self._print(arg)) return x def _print_Basic(self, e): x = self.dom.createElement(self.mathml_tag(e)) for arg in e: x.appendChild(self._print(arg)) return x def _print_AssocOp(self, e): x = self.dom.createElement('apply') x_1 = self.dom.createElement(self.mathml_tag(e)) x.appendChild(x_1) for arg in e.args: x.appendChild(self._print(arg)) return x def _print_Relational(self, e): x = self.dom.createElement('apply') x.appendChild(self.dom.createElement(self.mathml_tag(e))) x.appendChild(self._print(e.lhs)) x.appendChild(self._print(e.rhs)) return x def _print_list(self, seq): """MathML reference for the element: http://www.w3.org/TR/MathML2/chapter4.html#contm.list""" dom_element = self.dom.createElement('list') for item in seq: dom_element.appendChild(self._print(item)) return dom_element def _print_int(self, p): dom_element = self.dom.createElement(self.mathml_tag(p)) dom_element.appendChild(self.dom.createTextNode(str(p))) return dom_element def mathml(expr, **settings): """Returns the MathML representation of expr""" return MathMLPrinter(settings).doprint(expr) def print_mathml(expr, **settings): """ Prints a pretty representation of the MathML code for expr >>> ## >>> from sympy.printing.mathml import print_mathml >>> from sympy.abc import x >>> print_mathml(x+1) #doctest: +NORMALIZE_WHITESPACE x 1 """ s = MathMLPrinter(settings) print s._print(sympify(expr)).toprettyxml(encoding="utf-8") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/python.py0000644000175000017500000000407612014170666024633 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- import sympy from repr import ReprPrinter from str import StrPrinter # A list of classes that should be printed using StrPrinter STRPRINT = ("Add", "Infinity", "Integer", "Mul", "NegativeInfinity", "Pow", "Zero") class PythonPrinter(ReprPrinter, StrPrinter): """A printer which converts an expression into its Python interpretation.""" def __init__(self, settings=None): ReprPrinter.__init__(self) StrPrinter.__init__(self, settings) self.symbols = [] self.functions = [] # Create print methods for classes that should use StrPrinter instead # of ReprPrinter. for name in STRPRINT: f_name = "_print_%s"%name f = getattr(StrPrinter, f_name) setattr(PythonPrinter, f_name, f) def _print_Function(self, expr): func = expr.func.__name__ if not hasattr(sympy, func) and not func in self.functions: self.functions.append(func) return StrPrinter._print_Function(self, expr) # procedure (!) for defining symbols which have be defined in print_python() def _print_Symbol(self, expr): symbol = self._str(expr) if symbol not in self.symbols: self.symbols.append(symbol) return StrPrinter._print_Symbol(self, expr) def _print_module(self, expr): raise ValueError('Modules in the expression are unacceptable') def python(expr, **settings): """Return Python interpretation of passed expression (can be passed to the exec() function without any modifications)""" printer = PythonPrinter(settings) expr = printer.doprint(expr) result = '' # Returning found symbols and functions for symbol in printer.symbols: result += symbol + ' = Symbol(\'' + symbol + '\')\n' for function in printer.functions: result += function + ' = Function(\'' + function + '\')\n' result += 'e = ' + printer._str(expr) return result def print_python(expr, **settings): """Print output of python() function""" print python(expr, **settings) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/lambdarepr.py0000644000175000017500000000363612014170666025424 0ustar georgeskgeorgeskfrom str import StrPrinter def _find_first_symbol(expr): for atom in expr.atoms(): if atom.is_Symbol: return atom raise ValueError('expression must contain a Symbol: %r' % expr) class LambdaPrinter(StrPrinter): """ This printer converts expressions into strings that can be used by lambdify. """ def _print_Matrix(self, expr): return "Matrix([%s])"%expr._format_str(self._print, ",") def _print_Piecewise(self, expr): from sympy.core.sets import Interval args = expr.args result = [] i = 0 for arg in expr.args: e = arg.expr c = arg.cond result.append('((') result.append(self._print(e)) result.append(') if (') if isinstance(c, Interval): result.append(self._print(c.contains(_find_first_symbol(e)))) else: result.append(self._print(c)) result.append(') else (') i += 1 result = result[:-1] result.append(') else None)') result.append(')'*(2*i - 2)) return ''.join(result) def _print_And(self, expr): result = ['('] for arg in expr.args: result.extend(['(', self._print(arg), ')']) result.append(' and ') result = result[:-1] result.append(')') return ''.join(result) def _print_Or(self, expr): result = ['('] for arg in expr.args: result.extend(['(', self._print(arg), ')']) result.append(' or ') result = result[:-1] result.append(')') return ''.join(result) def _print_Not(self, expr): result = ['(', 'not (', self._print(expr.args[0]), '))'] return ''.join(result) def lambdarepr(expr, **settings): """ Returns a string usable for lambdifying. """ return LambdaPrinter(settings).doprint(expr) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/precedence.py0000644000175000017500000000360612014170666025405 0ustar georgeskgeorgesk"""A module providing information about the necessity of brackets""" # Default precedence values for some basic types PRECEDENCE = { "Lambda":1, "Relational":20, "Or":20, "And":30, "Add":40, "Mul":50, "Pow":60, "Not":100, "Atom":1000 } # A dictionary assigning precedence values to certain classes. These values are # treated like they were inherited, so not every single class has to be named # here. PRECEDENCE_VALUES = { "Or" : PRECEDENCE["Or"], "And" : PRECEDENCE["And"], "Add" : PRECEDENCE["Add"], "Pow" : PRECEDENCE["Pow"], "Relational" : PRECEDENCE["Relational"], "Sub" : PRECEDENCE["Add"], "Not": PRECEDENCE["Not"], } # Sometimes it's not enough to assign a fixed precedence value to a # class. Then a function can be inserted in this dictionary that takes # an instance of this class as argument and returns the appropriate # precedence value. # Precedence functions def precedence_Mul(item): if item.as_coeff_mul()[0].is_negative: return PRECEDENCE["Add"] return PRECEDENCE["Mul"] def precedence_Rational(item): if item.p < 0: return PRECEDENCE["Add"] return PRECEDENCE["Mul"] def precedence_Integer(item): if item.p < 0: return PRECEDENCE["Add"] return PRECEDENCE["Atom"] PRECEDENCE_FUNCTIONS = { "Integer" : precedence_Integer, "Mul" : precedence_Mul, "Rational" : precedence_Rational, } def precedence(item): """ Returns the precedence of a given object. """ if hasattr(item, "precedence"): return item.precedence try: mro = item.__class__.__mro__ except AttributeError: return PRECEDENCE["Atom"] for i in mro: n = i.__name__ if n in PRECEDENCE_FUNCTIONS: return PRECEDENCE_FUNCTIONS[n](item) elif n in PRECEDENCE_VALUES: return PRECEDENCE_VALUES[n] return PRECEDENCE["Atom"] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/repr.py0000644000175000017500000001043312014170666024254 0ustar georgeskgeorgesk""" A Printer for generating executable code. The most important function here is srepr that returns a string so that the relation eval(srepr(expr))=expr holds in an appropriate environment. """ from printer import Printer from sympy.core import Basic import sympy.mpmath.libmp as mlib from sympy.mpmath.libmp import prec_to_dps, repr_dps class ReprPrinter(Printer): printmethod = "_sympyrepr" _default_settings = { "order": None } def reprify(self, args, sep): return sep.join([self.doprint(item) for item in args]) def emptyPrinter(self, expr): if isinstance(expr, str): return expr elif hasattr(expr, "__srepr__"): return expr.__srepr__() elif hasattr(expr, "args") and hasattr(expr.args, "__iter__"): l = [] for o in expr.args: l.append(self._print(o)) return expr.__class__.__name__ + '(%s)' % ', '.join(l) elif hasattr(expr, "__module__") and hasattr(expr, "__name__"): return "<'%s.%s'>" % (expr.__module__, expr.__name__) else: return str(expr) def _print_Add(self, expr, order=None): args = self._as_ordered_terms(expr, order=order) args = map(self._print, args) return "Add(%s)" % ", ".join(args) def _print_Function(self, expr): r = self._print(expr.func) r+= '(%s)' % ', '.join([self._print(a) for a in expr.args]) return r def _print_FunctionClass(self, expr): return 'Function(%r)'%(expr.__name__) def _print_GeometryEntity(self, expr): # GeometryEntity is special -- its base is tuple return type(expr).__name__ + srepr(tuple(expr)) def _print_Half(self, expr): return 'Rational(1, 2)' def _print_RationalConstant(self, expr): return str(expr) def _print_AtomicExpr(self, expr): return str(expr) def _print_NumberSymbol(self, expr): return str(expr) def _print_Integer(self, expr): return 'Integer(%i)' % expr.p def _print_list(self, expr): return "[%s]" % self.reprify(expr, ", ") def _print_Matrix(self, expr): l = [] for i in range(expr.rows): l.append([]) for j in range(expr.cols): l[-1].append(expr[i,j]) return '%s(%s)' % (expr.__class__.__name__, self._print(l)) def _print_NaN(self, expr): return "nan" def _print_Rational(self, expr): return 'Rational(%s, %s)' % (self._print(expr.p), self._print(expr.q)) def _print_Mul(self, expr, order=None): terms = expr.args if self.order != 'old': args = expr._new_rawargs(*terms).as_ordered_factors() else: args = terms args = map(self._print, args) return "Mul(%s)" % ", ".join(args) def _print_Fraction(self, expr): return 'Fraction(%s, %s)' % (self._print(expr.numerator), self._print(expr.denominator)) def _print_Float(self, expr): dps = prec_to_dps(expr._prec) r = mlib.to_str(expr._mpf_, repr_dps(expr._prec)) return "%s('%s', prec=%i)" % (expr.__class__.__name__, r, dps) def _print_Sum2(self, expr): return "Sum2(%s, (%s, %s, %s))" % (self._print(expr.f), self._print(expr.i), self._print(expr.a), self._print(expr.b)) def _print_Symbol(self, expr): return "%s(%s)" % (expr.__class__.__name__, self._print(expr.name)) def _print_Predicate(self, expr): return "%s(%s)" % (expr.__class__.__name__, self._print(expr.name)) def _print_AppliedPredicate(self, expr): return "%s(%s, %s)" % (expr.__class__.__name__, expr.func, expr.arg) def _print_str(self, expr): return repr(expr) def _print_tuple(self, expr): if len(expr)==1: return "(%s,)" % self._print(expr[0]) else: return "(%s)" % self.reprify(expr, ", ") def _print_WildFunction(self, expr): return "%s('%s')" % (expr.__class__.__name__, expr.name) def _print_AlgebraicNumber(self, expr): return "%s(%s, %s)" % (self.__class__.__name__, self._print(self.coeffs()), self._print(expr.root)) def srepr(expr, **settings): """return expr in repr form""" return ReprPrinter(settings).doprint(expr) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/printer.py0000644000175000017500000002172012014170666024770 0ustar georgeskgeorgesk"""Printing subsystem driver SymPy's printing system works the following way: Any expression can be passed to a designated Printer who then is responsible to return a adequate representation of that expression. The basic concept is the following: 1. Let the object print itself if it knows how. 2. Take the best fitting method defined in the printer. 3. As fall-back use the emptyPrinter method for the printer. Some more information how the single concepts work and who should use which: 1. The object prints itself This was the original way of doing printing in sympy. Every class had its own latex, mathml, str and repr methods, but it turned out that it is hard to produce a high quality printer, if all the methods are spread out that far. Therefor all printing code was combined into the different printers, which works great for built-in sympy objects, but not that good for user defined classes where it is inconvenient to patch the printers. To get nevertheless a fitting representation, the printers look for a specific method in every object, that will be called if it's available and is then responsible for the representation. The name of that method depends on the specific printer and is defined under Printer.printmethodname. 2. Take the best fitting method defined in the printer. The printer loops through expr classes (class + its bases), and tries to dispatch the work to _print_ e.g., suppose we have the following class hierarchy:: Basic | Atom | Number | Rational then, for expr=Rational(...), in order to dispatch, we will try calling printer methods as shown in the figure below:: p._print(expr) | |-- p._print_Rational(expr) | |-- p._print_Number(expr) | |-- p._print_Atom(expr) | `-- p._print_Basic(expr) if ._print_Rational method exists in the printer, then it is called, and the result is returned back. otherwise, we proceed with trying Rational bases in the inheritance order. 3. As fall-back use the emptyPrinter method for the printer. As fall-back self.emptyPrinter will be called with the expression. If not defined in the Printer subclass this will be the same as str(expr) """ from sympy import S, Basic, Mul, Add from sympy.core.exprtools import decompose_power from sympy.polys.monomialtools import monomial_key from sympy.core.basic import BasicMeta from sympy.core.compatibility import cmp_to_key class Printer(object): """Generic printer Its job is to provide infrastructure for implementing new printers easily. Basically, if you want to implement a printer, all you have to do is: 1. Subclass Printer. 2. Define Printer.printmethod in your subclass. If a object has a method with that name, this method will be used for printing. 3. In your subclass, define _print_ methods For each class you want to provide printing to, define an appropriate method how to do it. For example if you want a class FOO to be printed in its own way, define _print_FOO: def _print_FOO(self, e): ... this should return how FOO instance e is printed Also, if BAR is a subclass of FOO, _print_FOO(bar) will be called for instance of BAR, if no _print_BAR is provided. Thus, usually, we don't need to provide printing routines for every class we want to support -- only generic routine has to be provided for a set of classes. A good example for this are functions - for example PrettyPrinter only defines _print_Function, and there is no _print_sin, _print_tan, etc... On the other hand, a good printer will probably have to define separate routines for Symbol, Atom, Number, Integral, Limit, etc... 4. If convenient, override self.emptyPrinter This callable will be called to obtain printing result as a last resort, that is when no appropriate print method was found for an expression. Example of overloading StrPrinter:: from sympy import Basic, Function, Symbol from sympy.printing.str import StrPrinter class CustomStrPrinter(StrPrinter): \"\"\" Example of how to customize the StrPrinter for both a Sympy class and a user defined class subclassed from the Sympy Basic class. \"\"\" def _print_Derivative(self, expr): \"\"\" Custom printing of the Sympy Derivative class. Instead of: D(x(t), t) or D(x(t), t, t) We will print: x' or x'' In this example, expr.args == (x(t), t), and expr.args[0] == x(t), and expr.args[0].func == x \"\"\" return str(expr.args[0].func) + "'"*len(expr.args[1:]) def _print_MyClass(self, expr): \"\"\" Print the characters of MyClass.s alternatively lower case and upper case \"\"\" s = "" i = 0 for char in expr.s: if i % 2 == 0: s += char.lower() else: s += char.upper() i += 1 return s # Override the __str__ method of to use CustromStrPrinter Basic.__str__ = lambda self: CustomStrPrinter().doprint(self) # Demonstration of CustomStrPrinter: t = Symbol('t') x = Function('x')(t) dxdt = x.diff(t) # dxdt is a Derivative instance d2xdt2 = dxdt.diff(t) # dxdt2 is a Derivative instance ex = MyClass('I like both lowercase and upper case') print dxdt print d2xdt2 print ex The output of the above code is:: x' x'' i lIkE BoTh lOwErCaSe aNd uPpEr cAsE By overriding Basic.__str__, we can customize the printing of anything that is subclassed from Basic. """ _global_settings = {} _default_settings = {} emptyPrinter = str printmethod = None def __init__(self, settings=None): self._str = str self._settings = self._default_settings.copy() for key, val in self._global_settings.iteritems(): if key in self._default_settings: self._settings[key] = val if settings is not None: self._settings.update(settings) if len(self._settings) > len(self._default_settings): for key in self._settings: if key not in self._default_settings: raise TypeError("Unknown setting '%s'." % key) # _print_level is the number of times self._print() was recursively # called. See StrPrinter._print_Float() for an example of usage self._print_level = 0 @classmethod def set_global_settings(cls, **settings): """Set system-wide printing settings. """ for key, val in settings.iteritems(): if val is not None: cls._global_settings[key] = val @property def order(self): return self._settings['order'] def doprint(self, expr): """Returns printer's representation for expr (as a string)""" return self._str(self._print(expr)) def _print(self, expr, *args): """Internal dispatcher Tries the following concepts to print an expression: 1. Let the object print itself if it knows how. 2. Take the best fitting method defined in the printer. 3. As fall-back use the emptyPrinter method for the printer. """ self._print_level += 1 try: # If the printer defines a name for a printing method # (Printer.printmethod) and the object knows for itself how it # should be printed, use that method. if (self.printmethod and hasattr(expr, self.printmethod) and not isinstance(expr, BasicMeta)): return getattr(expr, self.printmethod)(self, *args) # See if the class of expr is known, or if one of its super # classes is known, and use that print function for cls in type(expr).__mro__: printmethod = '_print_' + cls.__name__ if hasattr(self, printmethod): return getattr(self, printmethod)(expr, *args) # Unknown object, fall back to the emptyPrinter. return self.emptyPrinter(expr) finally: self._print_level -= 1 def _as_ordered_terms(self, expr, order=None): """A compatibility function for ordering terms in Add. """ order = order or self.order if order == 'old': return sorted(Add.make_args(expr), key=cmp_to_key(Basic._compare_pretty)) else: return expr.as_ordered_terms(order=order) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/0000755000175000017500000000000012014170666024073 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_gtk.py0000644000175000017500000000065512014170666026277 0ustar georgeskgeorgeskfrom sympy import print_gtk, sin from sympy.utilities.pytest import XFAIL, raises # this test fails if python-libxml2 isn't installed. We don't want to depend on # anything with SymPy @XFAIL def test_1(): from sympy.abc import x print_gtk(x**2, start_viewer=False) print_gtk(x**2+sin(x)/4, start_viewer=False) def test_settings(): from sympy.abc import x raises(TypeError, 'print_gtk(x, method="garbage")') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_python.py0000644000175000017500000001402612014170666027030 0ustar georgeskgeorgesk# -*- coding: utf-8 -*- from sympy import (Symbol, symbols, oo, limit, Rational, Integral, Derivative, log, exp, sqrt, pi, Function, sin, Eq, Le, Gt, Ne, Abs) from sympy.printing.python import python from sympy.utilities.pytest import raises x, y = symbols('x,y') th = Symbol('theta') ph = Symbol('phi') def test_python_basic(): # Simple numbers/symbols assert python(-Rational(1)/2) == "e = Rational(-1, 2)" assert python(-Rational(13)/22) == "e = Rational(-13, 22)" assert python(oo) == "e = oo" # Powers assert python((x**2)) == "x = Symbol(\'x\')\ne = x**2" assert python(1/x) == "x = Symbol('x')\ne = 1/x" assert python(y*x**-2) == "y = Symbol('y')\nx = Symbol('x')\ne = y/x**2" assert python(x**Rational(-5, 2)) == "x = Symbol('x')\ne = x**(Rational(-5, 2))" # Sums of terms assert python((x**2 + x + 1)) in [ "x = Symbol('x')\ne = 1 + x + x**2", "x = Symbol('x')\ne = x + x**2 + 1", "x = Symbol('x')\ne = x**2 + x + 1",] assert python(1-x) in [ "x = Symbol('x')\ne = 1 - x", "x = Symbol('x')\ne = -x + 1"] assert python(1-2*x) in [ "x = Symbol('x')\ne = 1 - 2*x", "x = Symbol('x')\ne = -2*x + 1"] assert python(1-Rational(3,2)*y/x) in [ "y = Symbol('y')\nx = Symbol('x')\ne = 1 - 3/2*y/x", "y = Symbol('y')\nx = Symbol('x')\ne = -3/2*y/x + 1", "y = Symbol('y')\nx = Symbol('x')\ne = 1 - 3*y/(2*x)"] # Multiplication assert python(x/y) == "x = Symbol('x')\ny = Symbol('y')\ne = x/y" assert python(-x/y) == "x = Symbol('x')\ny = Symbol('y')\ne = -x/y" assert python((x+2)/y) in [ "y = Symbol('y')\nx = Symbol('x')\ne = 1/y*(2 + x)", "y = Symbol('y')\nx = Symbol('x')\ne = 1/y*(x + 2)", "x = Symbol('x')\ny = Symbol('y')\ne = 1/y*(2 + x)", "x = Symbol('x')\ny = Symbol('y')\ne = (2 + x)/y", "x = Symbol('x')\ny = Symbol('y')\ne = (x + 2)/y"] assert python((1+x)*y) in [ "y = Symbol('y')\nx = Symbol('x')\ne = y*(1 + x)", "y = Symbol('y')\nx = Symbol('x')\ne = y*(x + 1)",] # Check for proper placement of negative sign assert python(-5*x/(x+10)) == "x = Symbol('x')\ne = -5*x/(x + 10)" assert python(1 - Rational(3,2)*(x+1)) in [ "x = Symbol('x')\ne = Rational(-3, 2)*x + Rational(-1, 2)", "x = Symbol('x')\ne = -3*x/2 + Rational(-1, 2)", "x = Symbol('x')\ne = -3*x/2 + Rational(-1, 2)" ] def test_python_relational(): assert python(Eq(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x == y" assert python(Le(x, y)) == "x = Symbol('x')\ny = Symbol('y')\ne = x <= y" assert python(Gt(x, y)) == "y = Symbol('y')\nx = Symbol('x')\ne = y < x" assert python(Ne(x/(y+1), y**2)) in [ "x = Symbol('x')\ny = Symbol('y')\ne = x/(1 + y) != y**2", "x = Symbol('x')\ny = Symbol('y')\ne = x/(y + 1) != y**2"] def test_python_functions(): # Simple assert python((2*x + exp(x))) in "x = Symbol('x')\ne = 2*x + exp(x)" assert python(sqrt(2)) == 'e = 2**(Rational(1, 2))' assert python(sqrt(2+pi)) == 'e = (2 + pi)**(Rational(1, 2))' assert python(Abs(x)) == "x = Symbol('x')\ne = Abs(x)" assert python(Abs(x/(x**2+1))) in ["x = Symbol('x')\ne = Abs(x/(1 + x**2))", "x = Symbol('x')\ne = Abs(x/(x**2 + 1))"] # Univariate/Multivariate functions f = Function('f') assert python(f(x)) == "x = Symbol('x')\nf = Function('f')\ne = f(x)" assert python(f(x, y)) == "x = Symbol('x')\ny = Symbol('y')\nf = Function('f')\ne = f(x, y)" assert python(f(x/(y+1), y)) in [ "x = Symbol('x')\ny = Symbol('y')\nf = Function('f')\ne = f(x/(1 + y), y)", "x = Symbol('x')\ny = Symbol('y')\nf = Function('f')\ne = f(x/(y + 1), y)"] # Nesting of square roots assert python(sqrt((sqrt(x+1))+1)) in [ "x = Symbol('x')\ne = (1 + (1 + x)**(Rational(1, 2)))**(Rational(1, 2))", "x = Symbol('x')\ne = ((x + 1)**(Rational(1, 2)) + 1)**(Rational(1, 2))"] # Function powers assert python(sin(x)**2) == "x = Symbol('x')\ne = sin(x)**2" # Conjugates a, b = map(Symbol, 'ab') #assert python( conjugate(a+b*I) ) == '_ _\na - I*b' #assert python( conjugate(exp(a+b*I)) ) == ' _ _\n a - I*b\ne ' def test_python_derivatives(): # Simple f_1 = Derivative(log(x), x, evaluate=False) assert python(f_1) == "x = Symbol('x')\ne = Derivative(log(x), x)" f_2 = Derivative(log(x), x, evaluate=False) + x assert python(f_2) == "x = Symbol('x')\ne = x + Derivative(log(x), x)" # Multiple symbols f_3 = Derivative(log(x) + x**2, x, y, evaluate=False) #assert python(f_3) == f_4 = Derivative(2*x*y, y, x, evaluate=False) + x**2 assert python(f_4) in [ "x = Symbol('x')\ny = Symbol('y')\ne = x**2 + Derivative(2*x*y, y, x)", "x = Symbol('x')\ny = Symbol('y')\ne = Derivative(2*x*y, y, x) + x**2"] def test_python_integrals(): # Simple f_1 = Integral(log(x), x) assert python(f_1) == "x = Symbol('x')\ne = Integral(log(x), x)" f_2 = Integral(x**2, x) assert python(f_2) == "x = Symbol('x')\ne = Integral(x**2, x)" # Double nesting of pow f_3 = Integral(x**(2**x), x) assert python(f_3) == "x = Symbol('x')\ne = Integral(x**(2**x), x)" # Definite integrals f_4 = Integral(x**2, (x,1,2)) assert python(f_4) == "x = Symbol('x')\ne = Integral(x**2, (x, 1, 2))" f_5 = Integral(x**2, (x,Rational(1,2),10)) assert python(f_5) == "x = Symbol('x')\ne = Integral(x**2, (x, Rational(1, 2), 10))" # Nested integrals f_6 = Integral(x**2*y**2, x,y) assert python(f_6) == "x = Symbol('x')\ny = Symbol('y')\ne = Integral(x**2*y**2, x, y)" # Not implemented yet #def test_python_matrix(): # p = python(Matrix([[x**2+1, 1], [y, x+y]])) # s = '' # assert p == s def test_python_limits(): assert python(limit(x, x, oo)) == 'e = oo' assert python(limit(x**2, x, 0)) == 'e = 0' def test_settings(): raises(TypeError, 'python(x, method="garbage")') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_conventions.py0000644000175000017500000000316012014170666030051 0ustar georgeskgeorgeskfrom sympy import symbols from sympy.printing.conventions import split_super_sub def test_super_sub(): assert split_super_sub("beta_13_2") == ("beta", [], ["13","2"]) assert split_super_sub("beta_132_20") == ("beta", [], ["132","20"]) assert split_super_sub("beta_13") == ("beta", [], ["13"]) assert split_super_sub("x_a_b") == ("x", [], ["a","b"]) assert split_super_sub("x_1_2_3") == ("x", [], ["1","2","3"]) assert split_super_sub("x_a_b1") == ("x", [], ["a","b1"]) assert split_super_sub("x_a_1") == ("x", [], ["a","1"]) assert split_super_sub("x_1_a") == ("x", [], ["1","a"]) assert split_super_sub("x_1^aa") == ("x", ["aa"], ["1"]) assert split_super_sub("x_1__aa") == ("x", ["aa"], ["1"]) assert split_super_sub("x_11^a") == ("x", ["a"], ["11"]) assert split_super_sub("x_11__a") == ("x", ["a"], ["11"]) assert split_super_sub("x_a_b_c_d") == ("x", [], ["a","b","c","d"]) assert split_super_sub("x_a_b^c^d") == ("x", ["c","d"], ["a","b"]) assert split_super_sub("x_a_b__c__d") == ("x", ["c","d"], ["a","b"]) assert split_super_sub("x_a^b_c^d") == ("x", ["b","d"], ["a","c"]) assert split_super_sub("x_a__b_c__d") == ("x", ["b","d"], ["a","c"]) assert split_super_sub("x^a^b_c_d") == ("x", ["a","b"], ["c","d"]) assert split_super_sub("x__a__b_c_d") == ("x", ["a","b"], ["c","d"]) assert split_super_sub("x^a^b^c^d") == ("x", ["a","b","c","d"], []) assert split_super_sub("x__a__b__c__d") == ("x", ["a","b","c","d"], []) assert split_super_sub("alpha_11") == ("alpha", [], ["11"]) assert split_super_sub("alpha_11_11") == ("alpha", [], ["11","11"]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_fcode.py0000644000175000017500000004235212014170666026572 0ustar georgeskgeorgeskfrom sympy import sin, cos, atan2, gamma, conjugate, sqrt, factorial, \ Integral, Piecewise, Add, diff, symbols, S, Float, Dummy from sympy import Catalan, EulerGamma, E, GoldenRatio, I, pi from sympy import Function, Rational, Integer, Lambda from sympy.printing.fcode import fcode, FCodePrinter from sympy.tensor import IndexedBase, Idx from sympy.utilities.lambdify import implemented_function from sympy.utilities.pytest import raises def test_printmethod(): x = symbols('x') class nint(Function): def _fcode(self, printer): return "nint(%s)" % printer._print(self.args[0]) assert fcode(nint(x)) == " nint(x)" def test_fcode_Pow(): x, y = symbols('x,y') n = symbols('n', integer=True) assert fcode(x**3) == " x**3" assert fcode(x**(y**3)) == " x**(y**3)" assert fcode(1/(sin(x)*3.5)**(x - y**x)/(x**2 + y)) == \ " (3.5d0*sin(x))**(-x + y**x)/(x**2 + y)" assert fcode(sqrt(x)) == ' sqrt(x)' assert fcode(sqrt(n)) == ' sqrt(dble(n))' assert fcode(x**0.5) == ' sqrt(x)' assert fcode(x**Rational(1,2)) == ' sqrt(x)' assert fcode(sqrt(10)) == ' sqrt(10.0d0)' def test_fcode_Rational(): assert fcode(Rational(3,7)) == " 3.0d0/7.0d0" assert fcode(Rational(18,9)) == " 2" assert fcode(Rational(3,-7)) == " -3.0d0/7.0d0" assert fcode(Rational(-3,-7)) == " 3.0d0/7.0d0" def test_fcode_Integer(): assert fcode(Integer(67)) == " 67" assert fcode(Integer(-1)) == " -1" def test_fcode_Float(): assert fcode(Float(42.0)) == " 42.0000000000000d0" assert fcode(Float(-1e20)) == " -1.00000000000000d+20" def test_fcode_functions(): x, y = symbols('x,y') assert fcode(sin(x) ** cos(y)) == " sin(x)**cos(y)" def test_fcode_NumberSymbol(): p = FCodePrinter() assert fcode(Catalan) == ' parameter (Catalan = 0.915965594177219d0)\n Catalan' assert fcode(EulerGamma) == ' parameter (EulerGamma = 0.577215664901533d0)\n EulerGamma' assert fcode(E) == ' parameter (E = 2.71828182845905d0)\n E' assert fcode(GoldenRatio) == ' parameter (GoldenRatio = 1.61803398874989d0)\n GoldenRatio' assert fcode(pi) == ' parameter (pi = 3.14159265358979d0)\n pi' assert fcode(pi,precision=5) == ' parameter (pi = 3.1416d0)\n pi' assert fcode(Catalan,human=False) == (set([(Catalan, p._print(Catalan.evalf(15)))]), set([]), ' Catalan') assert fcode(EulerGamma,human=False) == (set([(EulerGamma, p._print(EulerGamma.evalf(15)))]), set([]), ' EulerGamma') assert fcode(E,human=False) == (set([(E, p._print(E.evalf(15)))]), set([]), ' E') assert fcode(GoldenRatio,human=False) == (set([(GoldenRatio, p._print(GoldenRatio.evalf(15)))]), set([]), ' GoldenRatio') assert fcode(pi,human=False) == (set([(pi, p._print(pi.evalf(15)))]), set([]), ' pi') assert fcode(pi,precision=5,human=False) == (set([(pi, p._print(pi.evalf(5)))]), set([]), ' pi') def test_fcode_complex(): assert fcode(I) == " cmplx(0,1)" x = symbols('x') assert fcode(4*I) == " cmplx(0,4)" assert fcode(3+4*I) == " cmplx(3,4)" assert fcode(3+4*I+x) == " cmplx(3,4) + x" assert fcode(I*x) == " cmplx(0,1)*x" assert fcode(3+4*I-x) == " cmplx(3,4) - x" x = symbols('x', imaginary=True) assert fcode(5*x) == " 5*x" assert fcode(I*x) == " cmplx(0,1)*x" assert fcode(3+x) == " x + 3" def test_implicit(): x, y = symbols('x,y') assert fcode(sin(x)) == " sin(x)" assert fcode(atan2(x,y)) == " atan2(x, y)" assert fcode(conjugate(x)) == " conjg(x)" def test_not_fortran(): x = symbols('x') g = Function('g') assert fcode(gamma(x)) == "C Not Fortran:\nC gamma(x)\n gamma(x)" assert fcode(Integral(sin(x))) == "C Not Fortran:\nC Integral(sin(x), x)\n Integral(sin(x), x)" assert fcode(g(x)) == "C Not Fortran:\nC g(x)\n g(x)" def test_user_functions(): x = symbols('x') assert fcode(sin(x), user_functions={sin: "zsin"}) == " zsin(x)" x = symbols('x') assert fcode(gamma(x), user_functions={gamma: "mygamma"}) == " mygamma(x)" g = Function('g') assert fcode(g(x), user_functions={g: "great"}) == " great(x)" n = symbols('n', integer=True) assert fcode(factorial(n), user_functions={factorial: "fct"}) == " fct(n)" def test_inline_function(): x = symbols('x') g = implemented_function('g', Lambda(x, 2*x)) assert fcode(g(x)) == " 2*x" g = implemented_function('g', Lambda(x, 2*pi/x)) assert fcode(g(x)) == ( " parameter (pi = 3.14159265358979d0)\n" " 2*pi/x" ) A = IndexedBase('A') i = Idx('i', symbols('n', integer=True)) g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x))) assert fcode(g(A[i]), assign_to=A[i]) == ( " do i = 1, n\n" " A(i) = (1 + A(i))*(2 + A(i))*A(i)\n" " end do" ) def test_assign_to(): x = symbols('x') assert fcode(sin(x), assign_to="s") == " s = sin(x)" def test_line_wrapping(): x, y = symbols('x,y') assert fcode(((x+y)**10).expand(), assign_to="var") == ( " var = x**10 + 10*x**9*y + 45*x**8*y**2 + 120*x**7*y**3 + 210*x**6*\n" " @ y**4 + 252*x**5*y**5 + 210*x**4*y**6 + 120*x**3*y**7 + 45*x**2*y\n" " @ **8 + 10*x*y**9 + y**10" ) e = [x**i for i in range(11)] assert fcode(Add(*e)) == ( " x**10 + x**9 + x**8 + x**7 + x**6 + x**5 + x**4 + x**3 + x**2 + x\n" " @ + 1" ) def test_fcode_Piecewise(): x = symbols('x') code = fcode(Piecewise((x,x<1),(x**2,True))) expected = ( " if (x < 1) then\n" " x\n" " else\n" " x**2\n" " end if" ) assert code == expected assert fcode(Piecewise((x,x<1),(x**2,True)), assign_to="var") == ( " if (x < 1) then\n" " var = x\n" " else\n" " var = x**2\n" " end if" ) a = cos(x)/x b = sin(x)/x for i in xrange(10): a = diff(a, x) b = diff(b, x) expected = ( " if (x < 0) then\n" " weird_name = -cos(x)/x + 10*sin(x)/x**2 + 90*cos(x)/x**3 - 720*\n" " @ sin(x)/x**4 - 5040*cos(x)/x**5 + 30240*sin(x)/x**6 + 151200*cos(x\n" " @ )/x**7 - 604800*sin(x)/x**8 - 1814400*cos(x)/x**9 + 3628800*sin(x\n" " @ )/x**10 + 3628800*cos(x)/x**11\n" " else\n" " weird_name = -sin(x)/x - 10*cos(x)/x**2 + 90*sin(x)/x**3 + 720*\n" " @ cos(x)/x**4 - 5040*sin(x)/x**5 - 30240*cos(x)/x**6 + 151200*sin(x\n" " @ )/x**7 + 604800*cos(x)/x**8 - 1814400*sin(x)/x**9 - 3628800*cos(x\n" " @ )/x**10 + 3628800*sin(x)/x**11\n" " end if" ) code = fcode(Piecewise((a,x<0),(b,True)), assign_to="weird_name") assert code == expected assert fcode(Piecewise((x,x<1),(x**2,x>1),(sin(x),True))) == ( " if (x < 1) then\n" " x\n" " else if (1 < x) then\n" " x**2\n" " else\n" " sin(x)\n" " end if" ) assert fcode(Piecewise((x,x<1),(x**2,x>1),(sin(x),x>0))) == ( " if (x < 1) then\n" " x\n" " else if (1 < x) then\n" " x**2\n" " else if (0 < x) then\n" " sin(x)\n" " end if" ) def test_wrap_fortran(): # "########################################################################" printer = FCodePrinter() lines = [ "C This is a long comment on a single line that must be wrapped properly to produce nice output", " this = is + a + long + and + nasty + fortran + statement + that * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)/must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)/must + be + wrapped + properly", ] wrapped_lines = printer._wrap_fortran(lines) expected_lines = [ "C This is a long comment on a single line that must be wrapped", "C properly to produce nice output", " this = is + a + long + and + nasty + fortran + statement + that *", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that *", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ * must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that*", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ *must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement +", " @ that*must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that**", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ **must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement + that", " @ **must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement +", " @ that**must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)/", " @ must + be + wrapped + properly", " this = is + a + long + and + nasty + fortran + statement(that)", " @ /must + be + wrapped + properly", ] for line in wrapped_lines: assert len(line) <= 72 for w, e in zip(wrapped_lines, expected_lines): assert w == e assert len(wrapped_lines) == len(expected_lines) def test_wrap_fortran_keep_d0(): printer = FCodePrinter() lines = [ ' this_variable_is_very_long_because_we_try_to_test_line_break=1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break = 10.0d0' ] expected = [ ' this_variable_is_very_long_because_we_try_to_test_line_break=1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 1.0d0', ' this_variable_is_very_long_because_we_try_to_test_line_break =', ' @ 10.0d0' ] assert printer._wrap_fortran(lines) == expected def test_settings(): raises(TypeError, 'fcode(S(4), method="garbage")') def test_free_form_code_line(): x, y = symbols('x,y') assert fcode(cos(x) + sin(y), source_format='free') == "sin(y) + cos(x)" def test_free_form_continuation_line(): x, y = symbols('x,y') result = fcode(((cos(x) + sin(y))**(7)).expand(), source_format='free') expected = ( 'sin(y)**7 + 7*sin(y)**6*cos(x) + 21*sin(y)**5*cos(x)**2 + 35*sin(y)**4* &\n' ' cos(x)**3 + 35*sin(y)**3*cos(x)**4 + 21*sin(y)**2*cos(x)**5 + 7* &\n' ' sin(y)*cos(x)**6 + cos(x)**7' ) assert result == expected def test_free_form_comment_line(): printer = FCodePrinter({ 'source_format': 'free'}) lines = [ "! This is a long comment on a single line that must be wrapped properly to produce nice output"] expected = [ '! This is a long comment on a single line that must be wrapped properly', '! to produce nice output'] assert printer._wrap_fortran(lines) == expected def test_loops(): n, m = symbols('n,m', integer=True) A = IndexedBase('A') x = IndexedBase('x') y = IndexedBase('y') i = Idx('i', m) j = Idx('j', n) expected = ( 'do i = 1, m\n' ' y(i) = 0\n' 'end do\n' 'do i = 1, m\n' ' do j = 1, n\n' ' y(i) = %(rhs)s\n' ' end do\n' 'end do' ) code = fcode(A[i, j]*x[j], assign_to=y[i], source_format='free') assert (code == expected % {'rhs': 'y(i) + A(i, j)*x(j)'} or code == expected % {'rhs': 'y(i) + x(j)*A(i, j)'}) def test_dummy_loops(): # the following line could also be # [Dummy(s, integer=True) for s in 'im'] # or [Dummy(integer=True) for s in 'im'] i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( 'do i_%(icount)i = 1, m_%(mcount)i\n' ' y(i_%(icount)i) = x(i_%(icount)i)\n' 'end do' ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index} code = fcode(x[i], assign_to=y[i], source_format='free') assert code == expected def test_derived_classes(): class MyFancyFCodePrinter(FCodePrinter): _default_settings = FCodePrinter._default_settings.copy() _default_settings['assign_to'] = "bork" printer = MyFancyFCodePrinter() x = symbols('x') assert printer.doprint(sin(x)) == " bork = sin(x)" def test_indent(): codelines = ( 'subroutine test(a)\n' 'integer :: a, i, j\n' '\n' 'do\n' 'do \n' 'do j = 1, 5\n' 'if (a>b) then\n' 'if(b>0) then\n' 'a = 3\n' 'donot_indent_me = 2\n' 'do_not_indent_me_either = 2\n' 'ifIam_indented_something_went_wrong = 2\n' 'if_I_am_indented_something_went_wrong = 2\n' 'end should not be unindented here\n' 'end if\n' 'endif\n' 'end do\n' 'end do\n' 'enddo\n' 'end subroutine\n' '\n' 'subroutine test2(a)\n' 'integer :: a\n' 'do\n' 'a = a + 1\n' 'end do \n' 'end subroutine\n' ) expected = ( 'subroutine test(a)\n' 'integer :: a, i, j\n' '\n' 'do\n' ' do \n' ' do j = 1, 5\n' ' if (a>b) then\n' ' if(b>0) then\n' ' a = 3\n' ' donot_indent_me = 2\n' ' do_not_indent_me_either = 2\n' ' ifIam_indented_something_went_wrong = 2\n' ' if_I_am_indented_something_went_wrong = 2\n' ' end should not be unindented here\n' ' end if\n' ' endif\n' ' end do\n' ' end do\n' 'enddo\n' 'end subroutine\n' '\n' 'subroutine test2(a)\n' 'integer :: a\n' 'do\n' ' a = a + 1\n' 'end do \n' 'end subroutine\n' ) p = FCodePrinter({'source_format':'free'}) result = p.indent_code(codelines) assert result == expected wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_mathml.py0000644000175000017500000003147312014170666026776 0ustar georgeskgeorgeskfrom sympy import diff, Integral, Limit, sin, Symbol, Integer, Rational, cos, \ tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, E, I, oo, \ pi, GoldenRatio, EulerGamma, Sum, Eq, Ne, Ge, Lt from sympy.printing.mathml import mathml, MathMLPrinter from xml.dom.minidom import parseString from sympy.utilities.pytest import raises x = Symbol('x') y = Symbol('y') mp = MathMLPrinter() def test_printmethod(): #FIXME-py3k: AssertionError assert mp.doprint(1+x) == 'x1' def test_mathml_core(): mml_1 = mp._print(1+x) assert mml_1.nodeName == 'apply' nodes = mml_1.childNodes assert len(nodes) == 3 assert nodes[0].nodeName == 'plus' assert nodes[0].hasChildNodes() == False assert nodes[0].nodeValue is None assert nodes[1].nodeName in ['cn', 'ci'] if nodes[1].nodeName == 'cn': assert nodes[1].childNodes[0].nodeValue == '1' assert nodes[2].childNodes[0].nodeValue == 'x' else: assert nodes[1].childNodes[0].nodeValue == 'x' assert nodes[2].childNodes[0].nodeValue == '1' mml_2 = mp._print(x**2) assert mml_2.nodeName == 'apply' nodes = mml_2.childNodes assert nodes[1].childNodes[0].nodeValue == 'x' assert nodes[2].childNodes[0].nodeValue == '2' mml_3 = mp._print(2*x) assert mml_3.nodeName == 'apply' nodes = mml_3.childNodes assert nodes[0].nodeName == 'times' assert nodes[1].childNodes[0].nodeValue == '2' assert nodes[2].childNodes[0].nodeValue == 'x' def test_mathml_functions(): mml_1 = mp._print(sin(x)) assert mml_1.nodeName == 'apply' assert mml_1.childNodes[0].nodeName == 'sin' assert mml_1.childNodes[1].nodeName == 'ci' mml_2 = mp._print(diff(sin(x), x, evaluate=False)) assert mml_2.nodeName == 'apply' assert mml_2.childNodes[0].nodeName == 'diff' assert mml_2.childNodes[1].nodeName == 'bvar' assert mml_2.childNodes[1].childNodes[0].nodeName == 'ci' # below bvar there's x/ci> def test_mathml_limits(): # XXX No unevaluated limits lim_fun = sin(x)/x mml_1 = mp._print(Limit(lim_fun, x, 0)) assert mml_1.childNodes[0].nodeName == 'limit' assert mml_1.childNodes[1].nodeName == 'bvar' assert mml_1.childNodes[2].nodeName == 'lowlimit' assert mml_1.childNodes[3].toxml() == mp._print(lim_fun).toxml() def test_mathml_integrals(): integrand = x mml_1 = mp._print(Integral(integrand, (x, 0, 1))) assert mml_1.childNodes[0].nodeName == 'int' assert mml_1.childNodes[1].nodeName == 'bvar' assert mml_1.childNodes[2].nodeName == 'lowlimit' assert mml_1.childNodes[3].nodeName == 'uplimit' assert mml_1.childNodes[4].toxml() == mp._print(integrand).toxml() def test_mathml_sums(): summand = x mml_1 = mp._print(Sum(summand, (x, 1, 10))) assert mml_1.childNodes[0].nodeName == 'sum' assert mml_1.childNodes[1].nodeName == 'bvar' assert mml_1.childNodes[2].nodeName == 'lowlimit' assert mml_1.childNodes[3].nodeName == 'uplimit' assert mml_1.childNodes[4].toxml() == mp._print(summand).toxml() def test_mathml_tuples(): mml_1 = mp._print([2]) assert mml_1.nodeName == 'list' assert mml_1.childNodes[0].nodeName == 'cn' assert len(mml_1.childNodes) == 1 mml_2 = mp._print([2, Integer(1)]) assert mml_2.nodeName == 'list' assert mml_2.childNodes[0].nodeName == 'cn' assert mml_2.childNodes[1].nodeName == 'cn' assert len(mml_2.childNodes) == 2 def test_mathml_matrices(): pass #TODO def test_mathml_add(): mml = mp._print(x**5 - x**4 + x) assert mml.childNodes[0].nodeName == 'plus' assert mml.childNodes[1].childNodes[0].nodeName == 'minus' assert mml.childNodes[1].childNodes[1].nodeName == 'apply' def test_mathml_Rational(): mml_1 = mp._print(Rational(1,1)) """should just return a number""" assert mml_1.nodeName == 'cn' mml_2 = mp._print(Rational(2,5)) assert mml_2.childNodes[0].nodeName == 'divide' def test_mathml_constants(): mml = mp._print(I) assert mml.nodeName == 'imaginaryi' mml = mp._print(E) assert mml.nodeName == 'exponentiale' mml = mp._print(oo) assert mml.nodeName == 'infinity' mml = mp._print(pi) assert mml.nodeName == 'pi' #FIXME-py3k: AssertionError assert mathml(GoldenRatio) == '\xcf\x86' mml = mathml(EulerGamma) assert mml == '' def test_mathml_trig(): mml = mp._print(sin(x)) assert mml.childNodes[0].nodeName == 'sin' mml = mp._print(cos(x)) assert mml.childNodes[0].nodeName == 'cos' mml = mp._print(tan(x)) assert mml.childNodes[0].nodeName == 'tan' mml = mp._print(asin(x)) assert mml.childNodes[0].nodeName == 'arcsin' mml = mp._print(acos(x)) assert mml.childNodes[0].nodeName == 'arccos' mml = mp._print(atan(x)) assert mml.childNodes[0].nodeName == 'arctan' mml = mp._print(sinh(x)) assert mml.childNodes[0].nodeName == 'sinh' mml = mp._print(cosh(x)) assert mml.childNodes[0].nodeName == 'cosh' mml = mp._print(tanh(x)) assert mml.childNodes[0].nodeName == 'tanh' mml = mp._print(asinh(x)) assert mml.childNodes[0].nodeName == 'arcsinh' mml = mp._print(atanh(x)) assert mml.childNodes[0].nodeName == 'arctanh' mml = mp._print(acosh(x)) assert mml.childNodes[0].nodeName == 'arccosh' def test_mathml_relational(): mml_1 = mp._print(Eq(x,1)) assert mml_1.nodeName == 'apply' assert mml_1.childNodes[0].nodeName == 'eq' assert mml_1.childNodes[1].nodeName == 'ci' assert mml_1.childNodes[1].childNodes[0].nodeValue == 'x' assert mml_1.childNodes[2].nodeName == 'cn' assert mml_1.childNodes[2].childNodes[0].nodeValue == '1' mml_2 = mp._print(Ne(1,x)) assert mml_2.nodeName == 'apply' assert mml_2.childNodes[0].nodeName == 'neq' assert mml_2.childNodes[1].nodeName == 'cn' assert mml_2.childNodes[1].childNodes[0].nodeValue == '1' assert mml_2.childNodes[2].nodeName == 'ci' assert mml_2.childNodes[2].childNodes[0].nodeValue == 'x' mml_3 = mp._print(Ge(1,x)) assert mml_3.nodeName == 'apply' assert mml_3.childNodes[0].nodeName == 'leq' assert mml_3.childNodes[1].nodeName == 'ci' assert mml_3.childNodes[1].childNodes[0].nodeValue == 'x' assert mml_3.childNodes[2].nodeName == 'cn' assert mml_3.childNodes[2].childNodes[0].nodeValue == '1' mml_4 = mp._print(Lt(1,x)) assert mml_4.nodeName == 'apply' assert mml_4.childNodes[0].nodeName == 'lt' assert mml_4.childNodes[1].nodeName == 'cn' assert mml_4.childNodes[1].childNodes[0].nodeValue == '1' assert mml_4.childNodes[2].nodeName == 'ci' assert mml_4.childNodes[2].childNodes[0].nodeValue == 'x' def test_c2p(): """This tests some optional routines that depend on libxslt1 (which is optional)""" try: from sympy.modules.mathml import c2p #assert c2p(f.mathml) == result except ImportError: pass def test_symbol(): mml = mp._print(Symbol("x")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeValue == 'x' del mml mml = mp._print(Symbol("x^2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' del mml mml = mp._print(Symbol("x__2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' del mml mml = mp._print(Symbol("x_2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msub' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' del mml mml = mp._print(Symbol("x^3_2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msubsup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[2].childNodes[0].nodeValue == '3' del mml mml = mp._print(Symbol("x__3_2")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msubsup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[2].childNodes[0].nodeValue == '3' del mml mml = mp._print(Symbol("x_2_a")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msub' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mrow' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[1].childNodes[1].nodeName == 'mml:mo' assert mml.childNodes[0].childNodes[1].childNodes[1].childNodes[0].nodeValue == ' ' assert mml.childNodes[0].childNodes[1].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[2].childNodes[0].nodeValue == 'a' del mml mml = mp._print(Symbol("x^2^a")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mrow' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[1].childNodes[1].nodeName == 'mml:mo' assert mml.childNodes[0].childNodes[1].childNodes[1].childNodes[0].nodeValue == ' ' assert mml.childNodes[0].childNodes[1].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[2].childNodes[0].nodeValue == 'a' del mml mml = mp._print(Symbol("x__2__a")) assert mml.nodeName == 'ci' assert mml.childNodes[0].nodeName == 'mml:msup' assert mml.childNodes[0].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[0].childNodes[0].nodeValue == 'x' assert mml.childNodes[0].childNodes[1].nodeName == 'mml:mrow' assert mml.childNodes[0].childNodes[1].childNodes[0].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[0].childNodes[0].nodeValue == '2' assert mml.childNodes[0].childNodes[1].childNodes[1].nodeName == 'mml:mo' assert mml.childNodes[0].childNodes[1].childNodes[1].childNodes[0].nodeValue == ' ' assert mml.childNodes[0].childNodes[1].childNodes[2].nodeName == 'mml:mi' assert mml.childNodes[0].childNodes[1].childNodes[2].childNodes[0].nodeValue == 'a' del mml def test_mathml_order(): expr = x**3 + x**2*y + 3*x*y**3 + y**4 mp = MathMLPrinter({'order': 'lex'}) mml = mp._print(expr) assert mml.childNodes[1].childNodes[0].nodeName == 'power' assert mml.childNodes[1].childNodes[1].childNodes[0].data == 'x' assert mml.childNodes[1].childNodes[2].childNodes[0].data == '3' assert mml.childNodes[4].childNodes[0].nodeName == 'power' assert mml.childNodes[4].childNodes[1].childNodes[0].data == 'y' assert mml.childNodes[4].childNodes[2].childNodes[0].data == '4' mp = MathMLPrinter({'order': 'rev-lex'}) mml = mp._print(expr) assert mml.childNodes[1].childNodes[0].nodeName == 'power' assert mml.childNodes[1].childNodes[1].childNodes[0].data == 'y' assert mml.childNodes[1].childNodes[2].childNodes[0].data == '4' assert mml.childNodes[4].childNodes[0].nodeName == 'power' assert mml.childNodes[4].childNodes[1].childNodes[0].data == 'x' assert mml.childNodes[4].childNodes[2].childNodes[0].data == '3' def test_settings(): raises(TypeError, 'mathml(Symbol("x"), method="garbage")') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_lambdarepr.py0000644000175000017500000000506012014170666027616 0ustar georgeskgeorgeskfrom sympy import symbols, sin, Matrix, Interval, Piecewise from sympy.utilities.pytest import raises from sympy.printing.lambdarepr import lambdarepr x,y,z = symbols("x,y,z") def test_basic(): assert lambdarepr(x*y)=="x*y" assert lambdarepr(x+y) in ["y + x", "x + y"] assert lambdarepr(x**y)=="x**y" def test_matrix(): A = Matrix([[x,y],[y*x,z**2]]) assert lambdarepr(A)=="Matrix([[ x, y],[x*y, z**2]])" def test_piecewise(): # In each case, test eval() the lambdarepr() to make sure there are a # correct number of parentheses. It will give a SyntaxError if there aren't. h = "lambda x: " p = Piecewise((x, True), evaluate=False) l = lambdarepr(p) eval(h + l) assert l == "((x) if (True) else None)" p = Piecewise((x, x<0)) l = lambdarepr(p) eval(h + l) assert l == "((x) if (x < 0) else None)" p = Piecewise( (1, x < 1), (2, x < 2), (0, True) ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x < 1) else (((2) if (x < 2) else (((0) if (True) "\ "else None)))))" p = Piecewise( (1, x < 1), (2, x < 2), ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x < 1) else (((2) if (x < 2) else None)))" p = Piecewise( (x, x < 1), (x**2, Interval(3, 4, True, False)), (0, True), ) l = lambdarepr(p) eval(h + l) assert l == "((x) if (x < 1) else (((x**2) if (((x <= 4) "\ "and (3 < x))) else (((0) if (True) else None)))))" p = Piecewise( (x**2, x < 0), (x, Interval(0, 1, False, True)), (2 - x, x >= 1), (0, True) ) l = lambdarepr(p) eval(h + l) assert l == "((x**2) if (x < 0) else (((x) if (((x < 1) and (0 <= x))) "\ "else (((-x + 2) if (1 <= x) else (((0) if (True) else None)))))))" p = Piecewise( (x**2, x < 0), (x, Interval(0, 1, False, True)), (2 - x, x >= 1), ) l = lambdarepr(p) eval(h + l) assert l == "((x**2) if (x < 0) else (((x) if (((x < 1) and "\ "(0 <= x))) else (((-x + 2) if (1 <= x) else None)))))" p = Piecewise( (1, x < 1), (2, x < 2), (3, x < 3), (4, x < 4), (5, x < 5), (6, True) ) l = lambdarepr(p) eval(h + l) assert l == "((1) if (x < 1) else (((2) if (x < 2) else (((3) if "\ "(x < 3) else (((4) if (x < 4) else (((5) if (x < 5) else (((6) if "\ "(True) else None)))))))))))" def test_settings(): raises(TypeError, 'lambdarepr(sin(x),method="garbage")') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_str.py0000644000175000017500000003277212014170666026327 0ustar georgeskgeorgeskfrom sympy import (Abs, Catalan, cos, Derivative, E, EulerGamma, exp, factorial, Function, GoldenRatio, I, Integer, Integral, Interval, Lambda, Limit, log, Matrix, nan, O, oo, pi, Rational, Float, Rel, S, sin, SparseMatrix, sqrt, summation, Sum, Symbol, symbols, Wild, WildFunction, zeta, zoo, Dummy) from sympy.core import Expr from sympy.physics.units import second, joule from sympy.polys import Poly, RootOf, RootSum from sympy.statistics.distributions import Normal, Sample, Uniform from sympy.geometry import Point, Circle from sympy.utilities.pytest import XFAIL, raises from sympy.printing import sstr, sstrrepr, StrPrinter x, y, z, w = symbols('x,y,z,w') d = Dummy('d') def test_printmethod(): class R(Abs): def _sympystr(self, printer): return "foo(%s)" % printer._print(self.args[0]) assert sstr(R(x)) == "foo(x)" class R(Abs): def _sympystr(self, printer): return "foo" assert sstr(R(x)) == "foo" def test_Abs(): assert str(Abs(x)) == "Abs(x)" assert str(Abs(Rational(1,6))) == "1/6" assert str(Abs(Rational(-1,6))) == "1/6" def test_Add(): assert str(x+y) == "x + y" assert str(x+1) == "x + 1" assert str(x+x**2) == "x**2 + x" assert str(5+x+y+x*y+x**2+y**2) == "x**2 + x*y + x + y**2 + y + 5" assert str(1+x+x**2/2+x**3/3) == "x**3/3 + x**2/2 + x + 1" assert str(2*x-7*x**2 + 2 + 3*y) == "-7*x**2 + 2*x + 3*y + 2" assert str(x-y) == "x - y" assert str(2-x) == "-x + 2" assert str(x-2) == "x - 2" assert str(x-y-z-w) == "-w + x - y - z" assert str(x-z*y**2*z*w) == "-w*y**2*z**2 + x" assert str(x-1*y*x*y) == "-x*y**2 + x" assert str(sin(x).series(x, 0, 15)) == "x - x**3/6 + x**5/120 - x**7/5040 + x**9/362880 - x**11/39916800 + x**13/6227020800 + O(x**15)" def test_Catalan(): assert str(Catalan) == "Catalan" def test_ComplexInfinity(): assert str(zoo) == "zoo" def test_Derivative(): assert str(Derivative(x, y)) == "Derivative(x, y)" assert str(Derivative(x**2, x, evaluate=False)) == "Derivative(x**2, x)" assert str(Derivative(x**2/y, x, y, evaluate=False)) == "Derivative(x**2/y, x, y)" def test_dict(): assert str({1: 1+x}) == sstr({1: 1+x}) == "{1: x + 1}" assert str({1: x**2, 2: y*x}) in ("{1: x**2, 2: x*y}", "{2: x*y, 1: x**2}") assert sstr({1: x**2, 2: y*x}) == "{1: x**2, 2: x*y}" def test_Dummy(): assert str(d) == "_d" assert str(d+x) == "_d + x" def test_EulerGamma(): assert str(EulerGamma) == "EulerGamma" def test_Exp(): assert str(E) == "E" def test_factorial(): n = Symbol('n', integer=True) assert str(factorial(-2)) == "0" assert str(factorial(0)) == "1" assert str(factorial(7)) == "5040" assert str(factorial(n)) == "n!" assert str(factorial(2*n)) == "(2*n)!" def test_Function(): f = Function('f') fx = f(x) w = WildFunction('w') assert str(f) == "f" assert str(fx) == "f(x)" assert str(w) == "w_" def test_Geometry(): assert sstr(Point(0,0)) == 'Point(0, 0)' assert sstr(Circle(Point(0,0), 3)) == 'Circle(Point(0, 0), 3)' # TODO test other Geometry entities def test_GoldenRatio(): assert str(GoldenRatio) == "GoldenRatio" def test_ImaginaryUnit(): assert str(I) == "I" def test_Infinity(): assert str(oo) == "oo" assert str(I * oo) == "(oo)*I" def test_Integer(): assert str(Integer(-1)) == "-1" assert str(Integer(1)) == "1" assert str(Integer(-3)) == "-3" assert str(Integer(0)) == "0" assert str(Integer(25)) == "25" def test_Integral(): assert str(Integral(sin(x), y)) == "Integral(sin(x), y)" assert str(Integral(sin(x), (y, 0, 1))) == "Integral(sin(x), (y, 0, 1))" def test_Interval(): a = Symbol('a', real=True) assert str(Interval(0, a)) == "[0, a]" assert str(Interval(0, a, False, False)) == "[0, a]" assert str(Interval(0, a, True, False)) == "(0, a]" assert str(Interval(0, a, False, True)) == "[0, a)" assert str(Interval(0, a, True, True)) == "(0, a)" def test_Lambda(): assert str(Lambda(d, d**2)) == "Lambda(_d, _d**2)" def test_Limit(): assert str(Limit(sin(x)/x, x, y)) == "Limit(sin(x)/x, x, y)" assert str(Limit(1/x, x, 0)) == "Limit(1/x, x, 0)" assert str(Limit(sin(x)/x, x, y, dir="-")) == "Limit(sin(x)/x, x, y, dir='-')" def test_list(): assert str([x]) == sstr([x]) == "[x]" assert str([x**2, x*y+1]) == sstr([x**2, x*y+1]) == "[x**2, x*y + 1]" assert str([x**2, [y+x]]) == sstr([x**2, [y+x]]) == "[x**2, [x + y]]" def test_Matrix(): M = Matrix([[x**+1, 1], [y, x+y]]) assert str(M) == sstr(M) == "[x, 1]\n[y, x + y]" M = Matrix() assert str(M) == sstr(M) == "[]" M = Matrix(0, 1, lambda i, j: 0) assert str(M) == sstr(M) == "[]" def test_Mul(): assert str(x/y) == "x/y" assert str(y/x) == "y/x" assert str(x/y/z) == "x/(y*z)" assert str((x+1)/(y+2)) == "(x + 1)/(y + 2)" assert str(2*x/3) == '2*x/3' assert str(-2*x/3) == '-2*x/3' class CustomClass1(Expr): pass class CustomClass2(Expr): pass cc1 = CustomClass1(commutative=True) cc2 = CustomClass2(commutative=True) assert str(Rational(2)*cc1) == '2*CustomClass1()' assert str(cc1*Rational(2)) == '2*CustomClass1()' assert str(cc1*Float("1.5")) == '1.5*CustomClass1()' assert str(cc2*Rational(2)) == '2*CustomClass2()' assert str(cc2*Rational(2)*cc1) == '2*CustomClass1()*CustomClass2()' assert str(cc1*Rational(2)*cc2) == '2*CustomClass1()*CustomClass2()' def test_NaN(): assert str(nan) == "nan" def test_NegativeInfinity(): assert str(-oo) == "-oo" def test_Normal(): assert str(Normal(x+y, z)) == "Normal(x + y, z)" def test_Order(): assert str(O(x)) == "O(x)" assert str(O(x**2)) == "O(x**2)" assert str(O(x*y)) == "O(x*y, x, y)" def test_Pi(): assert str(pi) == "pi" def test_Poly(): assert str(Poly(0, x)) == "Poly(0, x, domain='ZZ')" assert str(Poly(1, x)) == "Poly(1, x, domain='ZZ')" assert str(Poly(x, x)) == "Poly(x, x, domain='ZZ')" assert str(Poly(2*x + 1, x)) == "Poly(2*x + 1, x, domain='ZZ')" assert str(Poly(2*x - 1, x)) == "Poly(2*x - 1, x, domain='ZZ')" assert str(Poly(-1, x)) == "Poly(-1, x, domain='ZZ')" assert str(Poly(-x, x)) == "Poly(-x, x, domain='ZZ')" assert str(Poly(-2*x + 1, x)) == "Poly(-2*x + 1, x, domain='ZZ')" assert str(Poly(-2*x - 1, x)) == "Poly(-2*x - 1, x, domain='ZZ')" assert str(Poly(x - 1, x)) == "Poly(x - 1, x, domain='ZZ')" assert str(Poly(x**2 + 1 + y, x)) == "Poly(x**2 + y + 1, x, domain='ZZ[y]')" assert str(Poly(x**2 - 1 + y, x)) == "Poly(x**2 + y - 1, x, domain='ZZ[y]')" assert str(Poly(x**2 + I*x, x)) == "Poly(x**2 + I*x, x, domain='EX')" assert str(Poly(x**2 - I*x, x)) == "Poly(x**2 - I*x, x, domain='EX')" assert str(Poly(-x*y*z + x*y - 1, x, y, z)) == "Poly(-x*y*z + x*y - 1, x, y, z, domain='ZZ')" assert str(Poly(-w*x**21*y**7*z + (1 + w)*z**3 - 2*x*z + 1, x, y, z)) == \ "Poly(-w*x**21*y**7*z - 2*x*z + (w + 1)*z**3 + 1, x, y, z, domain='ZZ[w]')" assert str(Poly(x**2 + 1, x, modulus=2)) == "Poly(x**2 + 1, x, modulus=2)" assert str(Poly(2*x**2 + 3*x + 4, x, modulus=17)) == "Poly(2*x**2 + 3*x + 4, x, modulus=17)" def test_Pow(): assert str(x**-1) == "1/x" assert str(x**-2) == "x**(-2)" assert str(x**2) == "x**2" assert str((x+y)**-1) == "1/(x + y)" assert str((x+y)**-2) == "(x + y)**(-2)" assert str((x+y)**2) == "(x + y)**2" assert str((x+y)**(1+x)) == "(x + y)**(x + 1)" def test_Rational(): n1 = Rational(1,4) n2 = Rational(1,3) n3 = Rational(2,4) n4 = Rational(2,-4) n5 = Rational(0) n6 = Rational(1) n7 = Rational(3) n8 = Rational(-3) assert str(n1*n2) == "1/12" assert str(n1*n2) == "1/12" assert str(n3) == "1/2" assert str(n1*n3) == "1/8" assert str(n1+n3) == "3/4" assert str(n1+n2) == "7/12" assert str(n1+n4) == "-1/4" assert str(n4*n4) == "1/4" assert str(n4+n2) == "-1/6" assert str(n4+n5) == "-1/2" assert str(n4*n5) == "0" assert str(n3+n4) == "0" assert str(n1**n7) == "1/64" assert str(n2**n7) == "1/27" assert str(n2**n8) == "27" assert str(n7**n8) == "1/27" assert str(Rational("-25")) == "-25" assert str(Rational("1.25")) == "5/4" assert str(Rational("-2.6e-2")) == "-13/500" assert str(S("25/7")) == "25/7" assert str(S("-123/569")) == "-123/569" assert str(S("0.1[23]", rational=1)) == "61/495" assert str(S("5.1[666]", rational=1)) == "31/6" assert str(S("-5.1[666]", rational=1)) == "-31/6" assert str(S("0.[9]", rational=1)) == "1" assert str(S("-0.[9]", rational=1)) == "-1" assert str(Rational(1,4) ** Rational(1,2)) == "1/2" assert str(Rational(1,36) ** Rational(1,2)) == "1/6" assert str((123**25) ** Rational(1,25)) == "123" assert str((123**25+1)**Rational(1,25)) != "123" assert str((123**25-1)**Rational(1,25)) != "123" assert str((123**25-1)**Rational(1,25)) != "122" assert str(Rational(81,36)**(Rational(3,2))) == "27/8" assert str(Rational(81,36)**(-Rational(3,2))) == "8/27" assert str((-4)**Rational(1,2)) == str(2*I) assert str(2**Rational(1,10**10)) == "2**(1/10000000000)" def test_Float(): # NOTE prec is the whole number of decimal digits assert str(Float('1.23', prec=1+2)) == '1.23' assert str(Float('1.23456789', prec=1+8)) == '1.23456789' assert str(Float('1.234567890123456789', prec=1+18)) == '1.234567890123456789' assert str(pi.evalf(1+2)) == '3.14' assert str(pi.evalf(1+14)) == '3.14159265358979' assert str(pi.evalf(1+64)) == '3.1415926535897932384626433832795028841971693993751058209749445923' def test_Relational(): assert str(Rel(x, y, "<")) == "x < y" assert str(Rel(x+y, y, "==")) == "x + y == y" def test_RootOf(): assert str(RootOf(x**5 + 2*x - 1, 0)) == "RootOf(x**5 + 2*x - 1, 0)" def test_RootSum(): f = x**5 + 2*x - 1 assert str(RootSum(f, Lambda(z, z), auto=False)) == "RootSum(x**5 + 2*x - 1)" assert str(RootSum(f, Lambda(z, z**2), auto=False)) == "RootSum(x**5 + 2*x - 1, Lambda(_z, _z**2))" def test_Sample(): assert str(Sample([x, y, 1])) in [ "Sample([x, y, 1])", "Sample([y, 1, x])", "Sample([1, x, y])", "Sample([y, x, 1])", "Sample([x, 1, y])", "Sample([1, y, x])", ] def test_set(): assert sstr(set()) == 'set()' assert sstr(frozenset()) == 'frozenset()' assert sstr(set([1,2,3]))== 'set([1, 2, 3])' assert sstr(set([1,x,x**2,x**3,x**4])) == 'set([1, x, x**2, x**3, x**4])' def test_SparseMatrix(): M = SparseMatrix([[x**+1, 1], [y, x+y]]) assert str(M) == sstr(M) == "[x, 1]\n[y, x + y]" def test_Sum(): assert str(summation(cos(3*z), (z, x, y))) == "Sum(cos(3*z), (z, x, y))" assert str(Sum(x*y**2, (x, -2, 2), (y, -5, 5))) == \ "Sum(x*y**2, (x, -2, 2), (y, -5, 5))" def test_Symbol(): assert str(y) == "y" assert str(x) == "x" e = x assert str(e) == "x" def test_tuple(): assert str((x,)) == sstr((x,)) == "(x,)" assert str((x+y, 1+x)) == sstr((x+y, 1+x)) == "(x + y, x + 1)" assert str((x+y, (1+x, x**2))) == sstr((x+y, (1+x, x**2))) == "(x + y, (x + 1, x**2))" def test_Uniform(): assert str(Uniform(x, y)) == "Uniform(x, y)" assert str(Uniform(x+y, y)) == "Uniform(x + y, y)" def test_Unit(): assert str(second) == "s" assert str(joule) == "kg*m**2/s**2" # issue 2461 def test_wild_str(): # Check expressions containing Wild not causing infinite recursion w = Wild('x') assert str(w + 1) == 'x_ + 1' assert str(exp(2**w) + 5) == 'exp(2**x_) + 5' assert str(3*w + 1) == '3*x_ + 1' assert str(1/w + 1) == '1 + 1/x_' assert str(w**2 + 1) == 'x_**2 + 1' assert str(1/(1-w)) == '1/(-x_ + 1)' def test_zeta(): assert str(zeta(3)) == "zeta(3)" def test_bug2(): e = x-y a = str(e) b = str(e) assert a == b def test_bug3(): e = sqrt(x) assert str(e) == "x**(1/2)" def test_bug4(): e = -2*sqrt(x)-y/sqrt(x)/2 assert str(e) not in ["(-2)*x**1/2(-1/2)*x**(-1/2)*y", "-2*x**1/2(-1/2)*x**(-1/2)*y","-2*x**1/2-1/2*x**-1/2*w"] assert str(e) in ["-2*x**(1/2) - 1/2*x**(-1/2)*y", "-2*x**(1/2) - 1/2*y*x**(-1/2)", "-1/2*x**(-1/2)*y - 2*x**(1/2)", "-1/2*y*x**(-1/2) - 2*x**(1/2)", "-2*x**(1/2) - y/(2*x**(1/2))"] def test_issue922(): e = Integral(x,x) + 1 assert str(e) == 'Integral(x, x) + 1' def test_sstrrepr(): assert sstr('abc') == 'abc' assert sstrrepr('abc') == "'abc'" e = ['a', 'b', 'c', x] assert sstr(e) == "[a, b, c, x]" assert sstrrepr(e) == "['a', 'b', 'c', x]" def test_infinity(): assert sstr(I*oo) == "(oo)*I" def test_full_prec(): assert sstr(S("0.3"), full_prec=True) == "0.300000000000000" assert sstr(S("0.3"), full_prec="auto") == "0.300000000000000" assert sstr(S("0.3"), full_prec=False) == "0.3" assert sstr(S("0.3")*x, full_prec=True) in [ "0.300000000000000*x", "x*0.300000000000000" ] assert sstr(S("0.3")*x, full_prec="auto") in [ "0.3*x", "x*0.3" ] assert sstr(S("0.3")*x, full_prec=False) in [ "0.3*x", "x*0.3" ] def test_empty_printer(): str_printer = StrPrinter() assert str_printer.emptyPrinter("foo") == "foo" assert str_printer.emptyPrinter(x*y) == "x*y" assert str_printer.emptyPrinter(32) == "32" def test_settings(): raises(TypeError, 'sstr(S(4), method="garbage")') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_codeprinter.py0000644000175000017500000000054512014170666030026 0ustar georgeskgeorgeskfrom sympy.printing.codeprinter import CodePrinter from sympy.core import C def setup_test_printer(*args, **kwargs): p = CodePrinter(*args, **kwargs) p._not_supported = set() p._number_symbols = set() return p def test_print_Dummy(): d = C.Dummy('d') p = setup_test_printer() assert p._print_Dummy(d) == "d_%i" % d.dummy_index wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/tests/test_ccode.py0000644000175000017500000002020512014170666026560 0ustar georgeskgeorgeskfrom sympy.core import pi, oo, symbols, Function, Rational, Integer, GoldenRatio, EulerGamma, Catalan, Lambda, Dummy from sympy.functions import Piecewise, sin, cos, Abs, exp, ceiling, sqrt from sympy.utilities.pytest import XFAIL, raises from sympy.printing.ccode import CCodePrinter from sympy.utilities.lambdify import implemented_function from sympy.tensor import IndexedBase, Idx # import test from sympy import ccode x, y, z = symbols('x,y,z') g = Function('g') def test_printmethod(): class fabs(Abs): def _ccode(self, printer): return "fabs(%s)" % printer._print(self.args[0]) assert ccode(fabs(x)) == "fabs(x)" def test_ccode_sqrt(): assert ccode(sqrt(x)) == "sqrt(x)" assert ccode(x**0.5) == "sqrt(x)" assert ccode(x**Rational(1,2)) == "sqrt(x)" def test_ccode_Pow(): assert ccode(x**3) == "pow(x, 3)" assert ccode(x**(y**3)) == "pow(x, pow(y, 3))" assert ccode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \ "pow(3.5*g(x), -x + pow(y, x))/(pow(x, 2) + y)" def test_ccode_constants_mathh(): assert ccode(exp(1)) == "M_E" assert ccode(pi) == "M_PI" assert ccode(oo) == "HUGE_VAL" assert ccode(-oo) == "-HUGE_VAL" def test_ccode_constants_other(): assert ccode(2*GoldenRatio) == "double const GoldenRatio = 1.61803398874989;\n2*GoldenRatio" assert ccode(2*Catalan) == "double const Catalan = 0.915965594177219;\n2*Catalan" assert ccode(2*EulerGamma) == "double const EulerGamma = 0.577215664901533;\n2*EulerGamma" def test_ccode_Rational(): assert ccode(Rational(3,7)) == "3.0/7.0" assert ccode(Rational(18,9)) == "2" assert ccode(Rational(3,-7)) == "-3.0/7.0" assert ccode(Rational(-3,-7)) == "3.0/7.0" def test_ccode_Integer(): assert ccode(Integer(67)) == "67" assert ccode(Integer(-1)) == "-1" def test_ccode_functions(): assert ccode(sin(x) ** cos(x)) == "pow(sin(x), cos(x))" def test_ccode_inline_function(): x = symbols('x') g = implemented_function('g', Lambda(x, 2*x)) assert ccode(g(x)) == "2*x" g = implemented_function('g', Lambda(x, 2*x/Catalan)) assert ccode(g(x)) == "double const Catalan = %s;\n2*x/Catalan" %Catalan.n() A = IndexedBase('A') i = Idx('i', symbols('n', integer=True)) g = implemented_function('g', Lambda(x, x*(1 + x)*(2 + x))) assert ccode(g(A[i]), assign_to=A[i]) == ( "for (int i=0; i precedence(x|y) assert precedence(~y) > precedence(x&y) # ... and with other operators (cfr. other programming languages) assert precedence(x+y) > precedence(x|y) assert precedence(x+y) > precedence(x&y) assert precedence(x*y) > precedence(x|y) assert precedence(x*y) > precedence(x&y) assert precedence(~y) > precedence(x*y) assert precedence(~y) > precedence(x-y) # double checks assert precedence(x&y) == PRECEDENCE["And"] assert precedence(x|y) == PRECEDENCE["Or"] assert precedence(~y) == PRECEDENCE["Not"] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/gtk.py0000644000175000017500000000073312014170666024073 0ustar georgeskgeorgesk from sympy.printing.mathml import mathml import tempfile import os def print_gtk(x, start_viewer=True): """Print to Gtkmathview, a gtk widget capable of rendering MathML. Needs libgtkmathview-bin""" from sympy.utilities.mathml import c2p tmp = tempfile.mktemp() # create a temp file to store the result file = open(tmp, 'wb') file.write( c2p(mathml(x), simple=True) ) file.close() if start_viewer: os.system("mathmlviewer " + tmp) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/preview.py0000644000175000017500000001444012014170666024767 0ustar georgeskgeorgeskimport os import time import tempfile from latex import latex def preview(expr, output='png', viewer=None, euler=True): """View expression in PNG, DVI, PostScript or PDF form. This will generate LaTeX representation of the given expression and compile it using available TeX distribution. Then it will run appropriate viewer for the given output format or use the user defined one. By default png output is generated. By default pretty Euler fonts are used for typesetting (they were used to typeset the well known "Concrete Mathematics" book). For that to work, you need the 'eulervm.sty' LaTeX style (in Debian/Ubuntu, install the texlive-fonts-extra package). If you prefer default AMS fonts or your system lacks 'eulervm' LaTeX package then unset the 'euler' keyword argument. To use viewer auto-detection, lets say for 'png' output, issue:: >> from sympy import * >> x, y = symbols("x,y") >> preview(x + y, output='png') This will choose 'pyglet by default. To select different one:: >> preview(x + y, output='png', viewer='gimp') The 'png' format is considered special. For all other formats the rules are slightly different. As an example we will take 'dvi' output format. If you would run:: >> preview(x + y, output='dvi') then 'view' will look for available 'dvi' viewers on your system (predefined in the function, so it will try evince, first, then kdvi and xdvi). If nothing is found you will need to set the viewer explicitly:: >> preview(x + y, output='dvi', viewer='superior-dvi-viewer') This will skip auto-detection and will run user specified 'superior-dvi-viewer'. If 'view' fails to find it on your system it will gracefully raise an exception. Currently this depends on pexpect, which is not available for windows. """ # we don't want to depend on anything not in the # standard library with SymPy by default import pexpect special = [ 'pyglet' ] if viewer is None: if output == "png": viewer = "pyglet" else: # sorted in order from most pretty to most ugly # very discussable, but indeed 'gv' looks awful :) candidates = { "dvi" : [ "evince", "okular", "kdvi", "xdvi" ], "ps" : [ "evince", "okular", "gsview", "gv" ], "pdf" : [ "evince", "okular", "kpdf", "acroread", "xpdf", "gv" ], } try: for candidate in candidates[output]: if pexpect.which(candidate): viewer = candidate break else: raise SystemError("No viewers found for '%s' output format." % output) except KeyError: raise SystemError("Invalid output format: %s" % output) else: if viewer not in special and not pexpect.which(viewer): raise SystemError("Unrecognized viewer: %s" % viewer) if not euler: format = r"""\documentclass[12pt]{article} \usepackage{amsmath} \begin{document} \pagestyle{empty} %s \vfill \end{document} """ else: format = r"""\documentclass[12pt]{article} \usepackage{amsmath} \usepackage{eulervm} \begin{document} \pagestyle{empty} %s \vfill \end{document} """ tmp = tempfile.mktemp() tex = open(tmp + ".tex", "w") tex.write(format % latex(expr, mode='inline')) tex.close() cwd = os.getcwd() os.chdir(tempfile.gettempdir()) if os.system("latex -halt-on-error %s.tex" % tmp) != 0: raise SystemError("Failed to generate DVI output.") os.remove(tmp + ".tex") os.remove(tmp + ".aux") os.remove(tmp + ".log") if output != "dvi": command = { "ps" : "dvips -o %s.ps %s.dvi", "pdf" : "dvipdf %s.dvi %s.pdf", "png" : "dvipng -T tight -z 9 " + \ "--truecolor -o %s.png %s.dvi", } try: if os.system(command[output] % (tmp, tmp)) != 0: raise SystemError("Failed to generate '%s' output." % output) else: os.remove(tmp + ".dvi") except KeyError: raise SystemError("Invalid output format: %s" % output) src = "%s.%s" % (tmp, output) if viewer == "pyglet": try: from pyglet import window, image, gl from pyglet.window import key except: raise ImportError("pyglet is required for plotting.\n visit http://www.pyglet.org/") if output == "png": from pyglet.image.codecs.png import PNGImageDecoder img = image.load(src, decoder=PNGImageDecoder()) else: raise SystemError("pyglet preview works only for 'png' files.") offset = 25 win = window.Window( width = img.width + 2*offset, height = img.height + 2*offset, caption = "sympy", resizable = False ) win.set_vsync(False) try: def on_close(): win.has_exit = True win.on_close = on_close def on_key_press(symbol, modifiers): if symbol in [key.Q, key.ESCAPE]: on_close() win.on_key_press = on_key_press def on_expose(): gl.glClearColor(1.0, 1.0, 1.0, 1.0) gl.glClear(gl.GL_COLOR_BUFFER_BIT) img.blit( (win.width - img.width) / 2, (win.height - img.height) / 2 ) win.on_expose = on_expose while not win.has_exit: win.dispatch_events() win.flip() except KeyboardInterrupt: pass win.close() else: os.system("%s %s &> /dev/null &" % (viewer, src)) time.sleep(2) # wait for the viewer to read data os.remove(src) os.chdir(cwd) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/printing/__init__.py0000644000175000017500000000057312014170666025047 0ustar georgeskgeorgesk"""Printing subsystem""" from pretty import * from latex import latex, print_latex from mathml import mathml, print_mathml from python import python, print_python from ccode import ccode, print_ccode from fcode import fcode, print_fcode from gtk import * from preview import preview from repr import srepr from tree import print_tree from str import StrPrinter, sstr, sstrrepr wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/0000755000175000017500000000000012014170666022561 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/hydrogen.py0000644000175000017500000001050512014170666024753 0ustar georgeskgeorgeskfrom sympy import factorial, sqrt, exp, S, laguerre_l, Float def R_nl(n, l, r, Z=1): """ Returns the Hydrogen radial wavefunction R_{nl}. n, l .... quantum numbers 'n' and 'l' r .... radial coordinate Z .... atomic number (1 for Hydrogen, 2 for Helium, ...) Everything is in Hartree atomic units. Examples:: >>> from sympy.physics.hydrogen import R_nl >>> from sympy import var >>> var("r Z") (r, Z) >>> R_nl(1, 0, r, Z) 2*(Z**3)**(1/2)*exp(-Z*r) >>> R_nl(2, 0, r, Z) 2**(1/2)*(-Z*r + 2)*(Z**3)**(1/2)*exp(-Z*r/2)/4 >>> R_nl(2, 1, r, Z) 6**(1/2)*Z*r*(Z**3)**(1/2)*exp(-Z*r/2)/12 For Hydrogen atom, you can just use the default value of Z=1:: >>> R_nl(1, 0, r) 2*exp(-r) >>> R_nl(2, 0, r) 2**(1/2)*(-r + 2)*exp(-r/2)/4 >>> R_nl(3, 0, r) 2*3**(1/2)*(2*r**2/9 - 2*r + 3)*exp(-r/3)/27 For Silver atom, you would use Z=47:: >>> R_nl(1, 0, r, Z=47) 94*47**(1/2)*exp(-47*r) >>> R_nl(2, 0, r, Z=47) 47*94**(1/2)*(-47*r + 2)*exp(-47*r/2)/4 >>> R_nl(3, 0, r, Z=47) 94*141**(1/2)*(4418*r**2/9 - 94*r + 3)*exp(-47*r/3)/27 The normalization of the radial wavefunction is:: >>> from sympy import integrate, oo >>> integrate(R_nl(1, 0, r)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 0, r)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 1, r)**2 * r**2, (r, 0, oo)) 1 It holds for any atomic number: >>> integrate(R_nl(1, 0, r, Z=2)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 0, r, Z=3)**2 * r**2, (r, 0, oo)) 1 >>> integrate(R_nl(2, 1, r, Z=4)**2 * r**2, (r, 0, oo)) 1 """ # sympify arguments n, l, r, Z = S(n), S(l), S(r), S(Z) # radial quantum number n_r = n - l - 1 # rescaled "r" a = 1/Z # Bohr radius r0 = 2 * r / (n * a) # normalization coefficient C = sqrt((S(2)/(n*a))**3 * factorial(n_r) / (2*n*factorial(n+l))) # This is an equivalent normalization coefficient, that can be found in # some books. Both coefficients seem to be the same fast: # C = S(2)/n**2 * sqrt(1/a**3 * factorial(n_r) / (factorial(n+l))) return C * r0**l * laguerre_l(n_r, 2*l+1, r0).expand() * exp(-r0/2) def E_nl(n, Z=1): """ Returns the energy of the state (n, l) in Hartree atomic units. The energy doesn't depend on "l". Examples:: >>> from sympy import var >>> from sympy.physics.hydrogen import E_nl >>> var("n Z") (n, Z) >>> E_nl(n, Z) -Z**2/(2*n**2) >>> E_nl(1) -1/2 >>> E_nl(2) -1/8 >>> E_nl(3) -1/18 >>> E_nl(3, 47) -2209/18 """ n, Z = S(n), S(Z) if n.is_integer and (n < 1): raise ValueError("'n' must be positive integer") return -Z**2/(2*n**2) def E_nl_dirac(n, l, spin_up=True, Z=1, c=Float("137.035999037")): """ Returns the relativistic energy of the state (n, l, spin) in Hartree atomic units. The energy is calculated from the Dirac equation. The rest mass energy is *not* included. n, l ...... quantum numbers 'n' and 'l' spin_up ... True if the electron spin is up (default), otherwise down Z ...... atomic number (1 for Hydrogen, 2 for Helium, ...) c ...... speed of light in atomic units. Default value is 137.035999037, taken from: http://arxiv.org/abs/1012.3627 Examples:: >>> from sympy.physics.hydrogen import E_nl_dirac >>> E_nl_dirac(1, 0) -0.500006656595360 >>> E_nl_dirac(2, 0) -0.125002080189006 >>> E_nl_dirac(2, 1) -0.125000416024704 >>> E_nl_dirac(2, 1, False) -0.125002080189006 >>> E_nl_dirac(3, 0) -0.0555562951740285 >>> E_nl_dirac(3, 1) -0.0555558020932949 >>> E_nl_dirac(3, 1, False) -0.0555562951740285 >>> E_nl_dirac(3, 2) -0.0555556377366884 >>> E_nl_dirac(3, 2, False) -0.0555558020932949 """ if not (l >= 0): raise ValueError("'l' must be positive or zero") if not (n > l): raise ValueError("'n' must be greater than 'l'") if (l==0 and spin_up is False): raise ValueError("Spin must be up for l==0.") # skappa is sign*kappa, where sign contains the correct sign if spin_up: skappa = -l - 1 else: skappa = -l c = S(c) beta = sqrt(skappa**2 - Z**2/c**2) return c**2/sqrt(1+Z**2/(n + skappa + beta)**2/c**2) - c**2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/wigner.py0000644000175000017500000005633312014170666024440 0ustar georgeskgeorgeskr""" Wigner, Clebsch-Gordan, Racah, and Gaunt coefficients Collection of functions for calculating Wigner 3j, 6j, 9j, Clebsch-Gordan, Racah as well as Gaunt coefficients exactly, all evaluating to a rational number times the square root of a rational number [Rasch03]_. Please see the description of the individual functions for further details and examples. REFERENCES: .. [Rasch03] J. Rasch and A. C. H. Yu, 'Efficient Storage Scheme for Pre-calculated Wigner 3j, 6j and Gaunt Coefficients', SIAM J. Sci. Comput. Volume 25, Issue 4, pp. 1416-1428 (2003) This code was taken from Sage with the permission of all authors: http://groups.google.com/group/sage-devel/browse_thread/thread/33835976efbb3b7f AUTHORS: - Jens Rasch (2009-03-24): initial version for Sage - Jens Rasch (2009-05-31): updated to sage-4.0 Copyright (C) 2008 Jens Rasch """ from sympy import Integer, pi, sqrt #from sage.rings.complex_number import ComplexNumber #from sage.rings.finite_rings.integer_mod import Mod # This list of precomputed factorials is needed to massively # accelerate future calculations of the various coefficients _Factlist=[1] def _calc_factlist(nn): r""" Function calculates a list of precomputed factorials in order to massively accelerate future calculations of the various coefficients. INPUT: - ``nn`` - integer, highest factorial to be computed OUTPUT: list of integers -- the list of precomputed factorials EXAMPLES: Calculate list of factorials:: sage: from sage.functions.wigner import _calc_factlist sage: _calc_factlist(10) [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] """ if nn >= len(_Factlist): for ii in range(len(_Factlist), nn + 1): _Factlist.append(_Factlist[ii - 1] * ii) return _Factlist[:int(nn) + 1] def wigner_3j(j_1, j_2, j_3, m_1, m_2, m_3, prec=None): r""" Calculate the Wigner 3j symbol `Wigner3j(j_1,j_2,j_3,m_1,m_2,m_3)`. INPUT: - ``j_1``, ``j_2``, ``j_3``, ``m_1``, ``m_2``, ``m_3`` - integer or half integer - ``prec`` - precision, default: ``None``. Providing a precision can drastically speed up the calculation. OUTPUT: Rational number times the square root of a rational number (if ``prec=None``), or real number if a precision is given. EXAMPLES:: sage: wigner_3j(2, 6, 4, 0, 0, 0) sqrt(5/143) sage: wigner_3j(2, 6, 4, 0, 0, 1) 0 sage: wigner_3j(0.5, 0.5, 1, 0.5, -0.5, 0) sqrt(1/6) sage: wigner_3j(40, 100, 60, -10, 60, -50) 95608/18702538494885*sqrt(21082735836735314343364163310/220491455010479533763) sage: wigner_3j(2500, 2500, 5000, 2488, 2400, -4888, prec=64) 7.60424456883448589e-12 It is an error to have arguments that are not integer or half integer values:: sage: wigner_3j(2.1, 6, 4, 0, 0, 0) Traceback (most recent call last): ... ValueError: j values must be integer or half integer sage: wigner_3j(2, 6, 4, 1, 0, -1.1) Traceback (most recent call last): ... ValueError: m values must be integer or half integer NOTES: The Wigner 3j symbol obeys the following symmetry rules: - invariant under any permutation of the columns (with the exception of a sign change where `J:=j_1+j_2+j_3`): .. math:: Wigner3j(j_1,j_2,j_3,m_1,m_2,m_3) =Wigner3j(j_3,j_1,j_2,m_3,m_1,m_2) =Wigner3j(j_2,j_3,j_1,m_2,m_3,m_1) =(-1)^J Wigner3j(j_3,j_2,j_1,m_3,m_2,m_1) =(-1)^J Wigner3j(j_1,j_3,j_2,m_1,m_3,m_2) =(-1)^J Wigner3j(j_2,j_1,j_3,m_2,m_1,m_3) - invariant under space inflection, i.e. .. math:: Wigner3j(j_1,j_2,j_3,m_1,m_2,m_3) =(-1)^J Wigner3j(j_1,j_2,j_3,-m_1,-m_2,-m_3) - symmetric with respect to the 72 additional symmetries based on the work by [Regge58]_ - zero for `j_1`, `j_2`, `j_3` not fulfilling triangle relation - zero for `m_1 + m_2 + m_3 \neq 0` - zero for violating any one of the conditions `j_1 \ge |m_1|`, `j_2 \ge |m_2|`, `j_3 \ge |m_3|` ALGORITHM: This function uses the algorithm of [Edmonds74]_ to calculate the value of the 3j symbol exactly. Note that the formula contains alternating sums over large factorials and is therefore unsuitable for finite precision arithmetic and only useful for a computer algebra system [Rasch03]_. REFERENCES: .. [Regge58] 'Symmetry Properties of Clebsch-Gordan Coefficients', T. Regge, Nuovo Cimento, Volume 10, pp. 544 (1958) .. [Edmonds74] 'Angular Momentum in Quantum Mechanics', A. R. Edmonds, Princeton University Press (1974) AUTHORS: - Jens Rasch (2009-03-24): initial version """ if int(j_1 * 2) != j_1 * 2 or int(j_2 * 2) != j_2 * 2 or \ int(j_3 * 2) != j_3 * 2: raise ValueError("j values must be integer or half integer") if int(m_1 * 2) != m_1 * 2 or int(m_2 * 2) != m_2 * 2 or \ int(m_3 * 2) != m_3 * 2: raise ValueError("m values must be integer or half integer") if m_1 + m_2 + m_3 != 0: return 0 prefid = Integer((-1) ** int(j_1 - j_2 - m_3)) m_3 = -m_3 a1 = j_1 + j_2 - j_3 if a1 < 0: return 0 a2 = j_1 - j_2 + j_3 if a2 < 0: return 0 a3 = -j_1 + j_2 + j_3 if a3 < 0: return 0 if (abs(m_1) > j_1) or (abs(m_2) > j_2) or (abs(m_3) > j_3): return 0 maxfact = max(j_1 + j_2 + j_3 + 1, j_1 + abs(m_1), j_2 + abs(m_2), \ j_3 + abs(m_3)) _calc_factlist(maxfact) argsqrt = Integer(_Factlist[int(j_1 + j_2 - j_3)] * \ _Factlist[int(j_1 - j_2 + j_3)] * \ _Factlist[int(-j_1 + j_2 + j_3)] * \ _Factlist[int(j_1 - m_1)] * \ _Factlist[int(j_1 + m_1)] * \ _Factlist[int(j_2 - m_2)] * \ _Factlist[int(j_2 + m_2)] * \ _Factlist[int(j_3 - m_3)] * \ _Factlist[int(j_3 + m_3)]) / \ _Factlist[int(j_1 + j_2 + j_3 + 1)] ressqrt = sqrt(argsqrt) if ressqrt.is_complex: ressqrt = ressqrt.as_real_imag()[0] imin = max(-j_3 + j_1 + m_2, -j_3 + j_2 - m_1, 0) imax = min(j_2 + m_2, j_1 - m_1, j_1 + j_2 - j_3) sumres = 0 for ii in range(imin, imax + 1): den = _Factlist[ii] * \ _Factlist[int(ii + j_3 - j_1 - m_2)] * \ _Factlist[int(j_2 + m_2 - ii)] * \ _Factlist[int(j_1 - ii - m_1)] * \ _Factlist[int(ii + j_3 - j_2 + m_1)] * \ _Factlist[int(j_1 + j_2 - j_3 - ii)] sumres = sumres + Integer((-1) ** ii) / den res = ressqrt * sumres * prefid return res def clebsch_gordan(j_1, j_2, j_3, m_1, m_2, m_3, prec=None): r""" Calculates the Clebsch-Gordan coefficient `\langle j_1 m_1 \; j_2 m_2 | j_3 m_3 \rangle`. The reference for this function is [Edmonds74]_. INPUT: - ``j_1``, ``j_2``, ``j_3``, ``m_1``, ``m_2``, ``m_3`` - integer or half integer - ``prec`` - precision, default: ``None``. Providing a precision can drastically speed up the calculation. OUTPUT: Rational number times the square root of a rational number (if ``prec=None``), or real number if a precision is given. EXAMPLES:: >>> from sympy import S >>> from sympy.physics.wigner import clebsch_gordan >>> clebsch_gordan(S(3)/2, S(1)/2, 2, S(3)/2, S(1)/2, 2) 1 >>> clebsch_gordan(S(3)/2, S(1)/2, 1, S(3)/2, -S(1)/2, 1) 3**(1/2)/2 >>> clebsch_gordan(S(3)/2, S(1)/2, 1, -S(1)/2, S(1)/2, 0) -2**(1/2)/2 NOTES: The Clebsch-Gordan coefficient will be evaluated via its relation to Wigner 3j symbols: .. math:: \langle j_1 m_1 \; j_2 m_2 | j_3 m_3 \rangle =(-1)^{j_1-j_2+m_3} \sqrt{2j_3+1} \; Wigner3j(j_1,j_2,j_3,m_1,m_2,-m_3) See also the documentation on Wigner 3j symbols which exhibit much higher symmetry relations than the Clebsch-Gordan coefficient. AUTHORS: - Jens Rasch (2009-03-24): initial version """ res = (-1) ** int(j_1 - j_2 + m_3) * sqrt(2 * j_3 + 1) * \ wigner_3j(j_1, j_2, j_3, m_1, m_2, -m_3, prec) return res def _big_delta_coeff(aa, bb, cc, prec=None): r""" Calculates the Delta coefficient of the 3 angular momenta for Racah symbols. Also checks that the differences are of integer value. INPUT: - ``aa`` - first angular momentum, integer or half integer - ``bb`` - second angular momentum, integer or half integer - ``cc`` - third angular momentum, integer or half integer - ``prec`` - precision of the ``sqrt()`` calculation OUTPUT: double - Value of the Delta coefficient EXAMPLES:: sage: from sage.functions.wigner import _big_delta_coeff sage: _big_delta_coeff(1,1,1) 1/2*sqrt(1/6) """ if int(aa + bb - cc) != (aa + bb - cc): raise ValueError("j values must be integer or half integer and fulfill the triangle relation") if int(aa + cc - bb) != (aa + cc - bb): raise ValueError("j values must be integer or half integer and fulfill the triangle relation") if int(bb + cc - aa) != (bb + cc - aa): raise ValueError("j values must be integer or half integer and fulfill the triangle relation") if (aa + bb - cc) < 0: return 0 if (aa + cc - bb) < 0: return 0 if (bb + cc - aa) < 0: return 0 maxfact = max(aa + bb - cc, aa + cc - bb, bb + cc - aa, aa + bb + cc + 1) _calc_factlist(maxfact) argsqrt = Integer(_Factlist[int(aa + bb - cc)] * \ _Factlist[int(aa + cc - bb)] * \ _Factlist[int(bb + cc - aa)]) / \ Integer(_Factlist[int(aa + bb + cc + 1)]) ressqrt = sqrt(argsqrt) if prec: ressqrt = ressqrt.evalf(prec).as_real_imag()[0] return ressqrt def racah(aa, bb, cc, dd, ee, ff, prec=None): r""" Calculate the Racah symbol `W(a,b,c,d;e,f)`. INPUT: - ``a``, ..., ``f`` - integer or half integer - ``prec`` - precision, default: ``None``. Providing a precision can drastically speed up the calculation. OUTPUT: Rational number times the square root of a rational number (if ``prec=None``), or real number if a precision is given. EXAMPLES:: sage: racah(3,3,3,3,3,3) -1/14 NOTES: The Racah symbol is related to the Wigner 6j symbol: .. math:: Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) =(-1)^{j_1+j_2+j_4+j_5} W(j_1,j_2,j_5,j_4,j_3,j_6) Please see the 6j symbol for its much richer symmetries and for additional properties. ALGORITHM: This function uses the algorithm of [Edmonds74]_ to calculate the value of the 6j symbol exactly. Note that the formula contains alternating sums over large factorials and is therefore unsuitable for finite precision arithmetic and only useful for a computer algebra system [Rasch03]_. AUTHORS: - Jens Rasch (2009-03-24): initial version """ prefac = _big_delta_coeff(aa, bb, ee, prec) * \ _big_delta_coeff(cc, dd, ee, prec) * \ _big_delta_coeff(aa, cc, ff, prec) * \ _big_delta_coeff(bb, dd, ff, prec) if prefac == 0: return 0 imin = max(aa + bb + ee, cc + dd + ee, aa + cc + ff, bb + dd + ff) imax = min(aa + bb + cc + dd, aa + dd + ee + ff, bb + cc + ee + ff) maxfact = max(imax + 1, aa + bb + cc + dd, aa + dd + ee + ff, \ bb + cc + ee + ff) _calc_factlist(maxfact) sumres = 0 for kk in range(imin, imax + 1): den = _Factlist[int(kk - aa - bb - ee)] * \ _Factlist[int(kk - cc - dd - ee)] * \ _Factlist[int(kk - aa - cc - ff)] * \ _Factlist[int(kk - bb - dd - ff)] * \ _Factlist[int(aa + bb + cc + dd - kk)] * \ _Factlist[int(aa + dd + ee + ff - kk)] * \ _Factlist[int(bb + cc + ee + ff - kk)] sumres = sumres + Integer((-1) ** kk * _Factlist[kk + 1]) / den res = prefac * sumres * (-1) ** int(aa + bb + cc + dd) return res def wigner_6j(j_1, j_2, j_3, j_4, j_5, j_6, prec=None): r""" Calculate the Wigner 6j symbol `Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6)`. INPUT: - ``j_1``, ..., ``j_6`` - integer or half integer - ``prec`` - precision, default: ``None``. Providing a precision can drastically speed up the calculation. OUTPUT: Rational number times the square root of a rational number (if ``prec=None``), or real number if a precision is given. EXAMPLES:: sage: wigner_6j(3,3,3,3,3,3) -1/14 sage: wigner_6j(5,5,5,5,5,5) 1/52 sage: wigner_6j(6,6,6,6,6,6) 309/10868 sage: wigner_6j(8,8,8,8,8,8) -12219/965770 sage: wigner_6j(30,30,30,30,30,30) 36082186869033479581/87954851694828981714124 sage: wigner_6j(0.5,0.5,1,0.5,0.5,1) 1/6 sage: wigner_6j(200,200,200,200,200,200, prec=1000)*1.0 0.000155903212413242 It is an error to have arguments that are not integer or half integer values or do not fulfill the triangle relation:: sage: wigner_6j(2.5,2.5,2.5,2.5,2.5,2.5) Traceback (most recent call last): ... ValueError: j values must be integer or half integer and fulfill the triangle relation sage: wigner_6j(0.5,0.5,1.1,0.5,0.5,1.1) Traceback (most recent call last): ... ValueError: j values must be integer or half integer and fulfill the triangle relation NOTES: The Wigner 6j symbol is related to the Racah symbol but exhibits more symmetries as detailed below. .. math:: Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) =(-1)^{j_1+j_2+j_4+j_5} W(j_1,j_2,j_5,j_4,j_3,j_6) The Wigner 6j symbol obeys the following symmetry rules: - Wigner 6j symbols are left invariant under any permutation of the columns: .. math:: Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) =Wigner6j(j_3,j_1,j_2,j_6,j_4,j_5) =Wigner6j(j_2,j_3,j_1,j_5,j_6,j_4) =Wigner6j(j_3,j_2,j_1,j_6,j_5,j_4) =Wigner6j(j_1,j_3,j_2,j_4,j_6,j_5) =Wigner6j(j_2,j_1,j_3,j_5,j_4,j_6) - They are invariant under the exchange of the upper and lower arguments in each of any two columns, i.e. .. math:: Wigner6j(j_1,j_2,j_3,j_4,j_5,j_6) =Wigner6j(j_1,j_5,j_6,j_4,j_2,j_3) =Wigner6j(j_4,j_2,j_6,j_1,j_5,j_3) =Wigner6j(j_4,j_5,j_3,j_1,j_2,j_6) - additional 6 symmetries [Regge59]_ giving rise to 144 symmetries in total - only non-zero if any triple of `j`'s fulfill a triangle relation ALGORITHM: This function uses the algorithm of [Edmonds74]_ to calculate the value of the 6j symbol exactly. Note that the formula contains alternating sums over large factorials and is therefore unsuitable for finite precision arithmetic and only useful for a computer algebra system [Rasch03]_. REFERENCES: .. [Regge59] 'Symmetry Properties of Racah Coefficients', T. Regge, Nuovo Cimento, Volume 11, pp. 116 (1959) """ res = (-1) ** int(j_1 + j_2 + j_4 + j_5) * \ racah(j_1, j_2, j_5, j_4, j_3, j_6, prec) return res def wigner_9j(j_1, j_2, j_3, j_4, j_5, j_6, j_7, j_8, j_9, prec=None): r""" Calculate the Wigner 9j symbol `Wigner9j(j_1,j_2,j_3,j_4,j_5,j_6,j_7,j_8,j_9)`. INPUT: - ``j_1``, ..., ``j_9`` - integer or half integer - ``prec`` - precision, default: ``None``. Providing a precision can drastically speed up the calculation. OUTPUT: Rational number times the square root of a rational number (if ``prec=None``), or real number if a precision is given. EXAMPLES: A couple of examples and test cases, note that for speed reasons a precision is given:: sage: wigner_9j(1,1,1, 1,1,1, 1,1,0 ,prec=64) # ==1/18 0.0555555555555555555 sage: wigner_9j(1,1,1, 1,1,1, 1,1,1) 0 sage: wigner_9j(1,1,1, 1,1,1, 1,1,2 ,prec=64) # ==1/18 0.0555555555555555556 sage: wigner_9j(1,2,1, 2,2,2, 1,2,1 ,prec=64) # ==-1/150 -0.00666666666666666667 sage: wigner_9j(3,3,2, 2,2,2, 3,3,2 ,prec=64) # ==157/14700 0.0106802721088435374 sage: wigner_9j(3,3,2, 3,3,2, 3,3,2 ,prec=64) # ==3221*sqrt(70)/(246960*sqrt(105)) - 365/(3528*sqrt(70)*sqrt(105)) 0.00944247746651111739 sage: wigner_9j(3,3,1, 3.5,3.5,2, 3.5,3.5,1 ,prec=64) # ==3221*sqrt(70)/(246960*sqrt(105)) - 365/(3528*sqrt(70)*sqrt(105)) 0.0110216678544351364 sage: wigner_9j(100,80,50, 50,100,70, 60,50,100 ,prec=1000)*1.0 1.05597798065761e-7 sage: wigner_9j(30,30,10, 30.5,30.5,20, 30.5,30.5,10 ,prec=1000)*1.0 # ==(80944680186359968990/95103769817469)*sqrt(1/682288158959699477295) 0.0000325841699408828 sage: wigner_9j(64,62.5,114.5, 61.5,61,112.5, 113.5,110.5,60, prec=1000)*1.0 -3.41407910055520e-39 sage: wigner_9j(15,15,15, 15,3,15, 15,18,10, prec=1000)*1.0 -0.0000778324615309539 sage: wigner_9j(1.5,1,1.5, 1,1,1, 1.5,1,1.5) 0 It is an error to have arguments that are not integer or half integer values or do not fulfill the triangle relation:: sage: wigner_9j(0.5,0.5,0.5, 0.5,0.5,0.5, 0.5,0.5,0.5,prec=64) Traceback (most recent call last): ... ValueError: j values must be integer or half integer and fulfill the triangle relation sage: wigner_9j(1,1,1, 0.5,1,1.5, 0.5,1,2.5,prec=64) Traceback (most recent call last): ... ValueError: j values must be integer or half integer and fulfill the triangle relation ALGORITHM: This function uses the algorithm of [Edmonds74]_ to calculate the value of the 3j symbol exactly. Note that the formula contains alternating sums over large factorials and is therefore unsuitable for finite precision arithmetic and only useful for a computer algebra system [Rasch03]_. """ imin = 0 imax = min(j_1 + j_9, j_2 + j_6, j_4 + j_8) sumres = 0 for kk in range(imin, imax + 1): sumres = sumres + (2 * kk + 1) * \ racah(j_1, j_2, j_9, j_6, j_3, kk, prec) * \ racah(j_4, j_6, j_8, j_2, j_5, kk, prec) * \ racah(j_1, j_4, j_9, j_8, j_7, kk, prec) return sumres def gaunt(l_1, l_2, l_3, m_1, m_2, m_3, prec=None): r""" Calculate the Gaunt coefficient. The Gaunt coefficient is defined as the integral over three spherical harmonics: .. math:: Y(j_1,j_2,j_3,m_1,m_2,m_3) =\int Y_{l_1,m_1}(\Omega) Y_{l_2,m_2}(\Omega) Y_{l_3,m_3}(\Omega) d\Omega =\sqrt{(2l_1+1)(2l_2+1)(2l_3+1)/(4\pi)} \; Y(j_1,j_2,j_3,0,0,0) \; Y(j_1,j_2,j_3,m_1,m_2,m_3) INPUT: - ``l_1``, ``l_2``, ``l_3``, ``m_1``, ``m_2``, ``m_3`` - integer - ``prec`` - precision, default: ``None``. Providing a precision can drastically speed up the calculation. OUTPUT: Rational number times the square root of a rational number (if ``prec=None``), or real number if a precision is given. EXAMPLES:: sage: gaunt(1,0,1,1,0,-1) -1/2/sqrt(pi) sage: gaunt(1,0,1,1,0,0) 0 sage: gaunt(29,29,34,10,-5,-5) 1821867940156/215552371055153321*sqrt(22134)/sqrt(pi) sage: gaunt(20,20,40,1,-1,0) 28384503878959800/74029560764440771/sqrt(pi) sage: gaunt(12,15,5,2,3,-5) 91/124062*sqrt(36890)/sqrt(pi) sage: gaunt(10,10,12,9,3,-12) -98/62031*sqrt(6279)/sqrt(pi) sage: gaunt(1000,1000,1200,9,3,-12).n(64) 0.00689500421922113448 It is an error to use non-integer values for `l` and `m`:: sage: gaunt(1.2,0,1.2,0,0,0) Traceback (most recent call last): ... ValueError: l values must be integer sage: gaunt(1,0,1,1.1,0,-1.1) Traceback (most recent call last): ... ValueError: m values must be integer NOTES: The Gaunt coefficient obeys the following symmetry rules: - invariant under any permutation of the columns .. math:: Y(j_1,j_2,j_3,m_1,m_2,m_3) =Y(j_3,j_1,j_2,m_3,m_1,m_2) =Y(j_2,j_3,j_1,m_2,m_3,m_1) =Y(j_3,j_2,j_1,m_3,m_2,m_1) =Y(j_1,j_3,j_2,m_1,m_3,m_2) =Y(j_2,j_1,j_3,m_2,m_1,m_3) - invariant under space inflection, i.e. .. math:: Y(j_1,j_2,j_3,m_1,m_2,m_3) =Y(j_1,j_2,j_3,-m_1,-m_2,-m_3) - symmetric with respect to the 72 Regge symmetries as inherited for the `3j` symbols [Regge58]_ - zero for `l_1`, `l_2`, `l_3` not fulfilling triangle relation - zero for violating any one of the conditions: `l_1 \ge |m_1|`, `l_2 \ge |m_2|`, `l_3 \ge |m_3|` - non-zero only for an even sum of the `l_i`, i.e. `J=l_1+l_2+l_3=2n` for `n` in `\Bold{N}` ALGORITHM: This function uses the algorithm of [Liberatodebrito82]_ to calculate the value of the Gaunt coefficient exactly. Note that the formula contains alternating sums over large factorials and is therefore unsuitable for finite precision arithmetic and only useful for a computer algebra system [Rasch03]_. REFERENCES: .. [Liberatodebrito82] 'FORTRAN program for the integral of three spherical harmonics', A. Liberato de Brito, Comput. Phys. Commun., Volume 25, pp. 81-85 (1982) AUTHORS: - Jens Rasch (2009-03-24): initial version for Sage """ if int(l_1) != l_1 or int(l_2) != l_2 or int(l_3) != l_3: raise ValueError("l values must be integer") if int(m_1) != m_1 or int(m_2) != m_2 or int(m_3) != m_3: raise ValueError("m values must be integer") bigL = (l_1 + l_2 + l_3) // 2 a1 = l_1 + l_2 - l_3 if a1 < 0: return 0 a2 = l_1 - l_2 + l_3 if a2 < 0: return 0 a3 = -l_1 + l_2 + l_3 if a3 < 0: return 0 if (2 * bigL) % 2 != 0: return 0 if (m_1 + m_2 + m_3) != 0: return 0 if (abs(m_1) > l_1) or (abs(m_2) > l_2) or (abs(m_3) > l_3): return 0 imin = max(-l_3 + l_1 + m_2, -l_3 + l_2 - m_1, 0) imax = min(l_2 + m_2, l_1 - m_1, l_1 + l_2 - l_3) maxfact = max(l_1 + l_2 + l_3 + 1, imax + 1) _calc_factlist(maxfact) argsqrt = (2 * l_1 + 1) * (2 * l_2 + 1) * (2 * l_3 + 1) * \ _Factlist[l_1 - m_1] * _Factlist[l_1 + m_1] * _Factlist[l_2 - m_2] * \ _Factlist[l_2 + m_2] * _Factlist[l_3 - m_3] * _Factlist[l_3 + m_3] / \ (4*pi) ressqrt = sqrt(argsqrt) prefac = Integer(_Factlist[bigL] * _Factlist[l_2 - l_1 + l_3] * \ _Factlist[l_1 - l_2 + l_3] * _Factlist[l_1 + l_2 - l_3])/ \ _Factlist[2 * bigL+1]/ \ (_Factlist[bigL - l_1] * _Factlist[bigL - l_2] * _Factlist[bigL - l_3]) sumres = 0 for ii in range(imin, imax + 1): den = _Factlist[ii] * _Factlist[ii + l_3 - l_1 - m_2] * \ _Factlist[l_2 + m_2 - ii] * _Factlist[l_1 - ii - m_1] * \ _Factlist[ii + l_3 - l_2 + m_1] * _Factlist[l_1 + l_2 - l_3 - ii] sumres = sumres + Integer((-1) ** ii) / den res = ressqrt * prefac * sumres * (-1) ** (bigL + l_3 + m_1 - m_2) if prec != None: res = res.n(prec) return res wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/0000755000175000017500000000000012014170666024253 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/cg.py0000644000175000017500000004250112014170666025220 0ustar georgeskgeorgesk#TODO: # -Implement Clebsch-Gordan symmetries # -Improve simplification method # -Implement new simpifications """Clebsch-Gordon Coefficients.""" from sympy import Add, expand, Eq, Expr, Function, Mul, Piecewise, Pow, sqrt, Sum, symbols, sympify, Wild from sympy.printing.pretty.stringpict import prettyForm, stringPict from sympy.physics.quantum.kronecker import KroneckerDelta from sympy.physics.wigner import wigner_3j, clebsch_gordan __all__ = [ 'Wigner3j', 'CG', 'cg_simp' ] #----------------------------------------------------------------------------- # CG Coefficients #----------------------------------------------------------------------------- class Wigner3j(Expr): """Class for the Wigner-3j symbols Wigner 3j-symbols are coefficients determined by the coupling of two angular momenta. When created, they are expressed as symbolic quantities that can be evaluated using the doit() method. Parameters ========== j1, m1, j2, m2, j3, m3 : Number, Symbol Terms determining the angular momentum of coupled angular momentum systems. Examples ======== Declare a Wigner-3j coefficient and calcualte its value >>> from sympy.physics.quantum.cg import Wigner3j >>> w3j = Wigner3j(6,0,4,0,2,0) >>> w3j (6, 4, 2) (0, 0, 0) >>> w3j.doit() 715**(1/2)/143 References ========== [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. """ def __new__(cls, j1, m1, j2, m2, j3, m3): j1,m1,j2,m2,j3,m3 = map(sympify, (j1,m1,j2,m2,j3,m3)) return Expr.__new__(cls, j1, m1, j2, m2, j3, m3) @property def j1(self): return self.args[0] @property def m1(self): return self.args[1] @property def j2(self): return self.args[2] @property def m2(self): return self.args[3] @property def j3(self): return self.args[4] @property def m3(self): return self.args[5] @property def is_symbolic(self): return not (self.j1.is_number and self.j2.is_number and self.j3.is_number and self.m1.is_number and self.m2.is_number and self.m3.is_number) # This is modified from the _print_Matrix method def _sympystr(self, printer, *args): res = [[printer._print(self.j1), printer._print(self.j2), printer._print(self.j3)], \ [printer._print(self.m1), printer._print(self.m2), printer._print(self.m3)]] maxw = [-1] * 3 for j in range(3): maxw[j] = max([ len(res[i][j]) for i in range(2) ]) for i, row in enumerate(res): for j, elem in enumerate(row): row[j] = elem.rjust(maxw[j]) res[i] = "(" + ", ".join(row) + ")" return '\n'.join(res) # This is modified from the _print_Matrix method def _pretty(self, printer, *args): m = ((printer._print(self.j1), printer._print(self.m1)), \ (printer._print(self.j2), printer._print(self.m2)), \ (printer._print(self.j3), printer._print(self.m3))) hsep = 2 vsep = 1 maxw = [-1] * 3 for j in range(3): maxw[j] = max([ m[j][i].width() for i in range(2) ]) D = None for i in range(2): D_row = None for j in range(3): s = m[j][i] wdelta = maxw[j] - s.width() wleft = wdelta //2 wright = wdelta - wleft s = prettyForm(*s.right(' '*wright)) s = prettyForm(*s.left(' '*wleft)) if D_row is None: D_row = s continue D_row = prettyForm(*D_row.right(' '*hsep)) D_row = prettyForm(*D_row.right(s)) if D is None: D = D_row continue for _ in range(vsep): D = prettyForm(*D.below(' ')) D = prettyForm(*D.below(D_row)) D = prettyForm(*D.parens()) return D def _latex(self, printer, *args): return r'\left(\begin{array}{ccc} %s & %s & %s \\ %s & %s & %s \end{array}\right)' % \ (printer._print(self.j1), printer._print(self.j2), printer._print(self.j3), \ printer._print(self.m1), printer._print(self.m2), printer._print(self.m3)) def doit(self, **hints): if self.is_symbolic: raise ValueError("Coefficients must be numerical") return wigner_3j(self.j1, self.j2, self.j3, self.m1, self.m2, self.m3) class CG(Wigner3j): """Class for Clebsch-Gordan coefficient Clebsch-Gordan coefficients describe the angular momentum coupling between two systems. The coefficients give the expansion of a coupled total angular momentum state and an uncoupled tensor product state. The Clebsch-Gordan coefficients are defined as: CG(j1,m1,j2,m2,j3,m3) = Parameters ========== j1, m1, j2, m2, j3, m3 : Number, Symbol Terms determining the angular momentum of coupled angular momentum systems. Examples ======== Define a Clebsch-Gordan coefficient and evaluate its value >>> from sympy.physics.quantum.cg import CG >>> from sympy import S >>> cg = CG(S(3)/2, S(3)/2, S(1)/2, -S(1)/2, 1, 1) >>> cg CG(3/2, 3/2, 1/2, -1/2, 1, 1) >>> cg.doit() 3**(1/2)/2 References ========== [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. """ def doit(self, **hints): if self.is_symbolic: raise ValueError("Coefficients must be numerical") return clebsch_gordan(self.j1,self.j2, self.j3, self.m1, self.m2, self.m3) def _sympystr(self, printer, *args): return 'CG(%s, %s, %s, %s, %s, %s)' % \ (printer._print(self.j1), printer._print(self.m1), printer._print(self.j2), \ printer._print(self.m2), printer._print(self.j3), printer._print(self.m3)) def _pretty(self, printer, *args): bot = printer._print(self.j1) bot = prettyForm(*bot.right(',')) bot = prettyForm(*bot.right(printer._print(self.m1))) bot = prettyForm(*bot.right(',')) bot = prettyForm(*bot.right(printer._print(self.j2))) bot = prettyForm(*bot.right(',')) bot = prettyForm(*bot.right(printer._print(self.m2))) top = printer._print(self.j3) top = prettyForm(*top.right(',')) top = prettyForm(*top.right(printer._print(self.m3))) pad = max(top.width(), bot.width()) bot = prettyForm(*bot.left(' ')) top = prettyForm(*top.left(' ')) if not pad == bot.width(): bot = prettyForm(*bot.right(' ' * (pad-bot.width()))) if not pad == top.width(): top = prettyForm(*top.right(' ' * (pad-top.width()))) s = stringPict('C' + ' '*pad) s = prettyForm(*s.below(bot)) s = prettyForm(*s.above(top)) return s def _latex(self, printer, *args): return r'C^{%s,%s}_{%s,%s,%s,%s}' % \ (printer._print(self.j3), printer._print(self.m3), printer._print(self.j1), printer._print(self.m1), printer._print(self.j2), printer._print(self.m2)) def cg_simp(e): """Simplify and combine CG coefficients This function uses various symmetry and properties of sums and products of Clebsch-Gordan coefficients to simplify statements involving these terms Examples ======== Simplify the sum over CG(a,alpha,0,0,a,alpha) for all alpha to 2*a+1 >>> from sympy.physics.quantum.cg import CG, cg_simp >>> a = CG(1,1,0,0,1,1) >>> b = CG(1,0,0,0,1,0) >>> c = CG(1,-1,0,0,1,-1) >>> cg_simp(a+b+c) 3 References ========== [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. """ if isinstance(e, Add): return _cg_simp_add(e) elif isinstance(e, Sum): return _cg_simp_sum(e) elif isinstance(e, Mul): return Mul(*[cg_simp(arg) for arg in e.args]) elif isinstance(e, Pow): return Pow(cg_simp(e.base), e.exp) else: return e def _cg_simp_add(e): #TODO: Improve simplification method """Takes a sum of terms involving Clebsch-Gordan coefficients and simplifies the terms. First, we create two lists, cg_part, which is all the terms involving CG coefficients, and other_part, which is all other terms. The cg_part list is then passed to the simplification methods, which return the new cg_part and any additional terms that are added to other_part """ cg_part = [] other_part = [] e = expand(e) for arg in e.args: if arg.has(CG): if isinstance(arg, Sum): other_part.append(_cg_simp_sum(arg)) elif isinstance(arg, Mul): terms = 1 for term in arg.args: if isinstance(term, Sum): terms *= _cg_simp_sum(term) else: terms *= term if terms.has(CG): cg_part.append(terms) else: other_part.append(terms) else: cg_part.append(arg) else: other_part.append(arg) cg_part, other = _check_varsh_871_1(cg_part) other_part.append(other) cg_part, other = _check_varsh_871_2(cg_part) other_part.append(other) cg_part, other = _check_varsh_872_9(cg_part) other_part.append(other) return Add(*cg_part)+Add(*other_part) def _check_varsh_871_1(term_list): # Sum( CG(a,alpha,b,0,a,alpha), (alpha, -a, a)) == KroneckerDelta(b,0) a,alpha,b,lt = map(Wild,('a','alpha','b','lt')) expr = lt*CG(a,alpha,b,0,a,alpha) simp = (2*a+1)*KroneckerDelta(b,0) sign = lt/abs(lt) build_expr = 2*a+1 index_expr = a+alpha return _check_cg_simp(expr, simp, sign, lt, term_list, (a,alpha,b,lt), (a,b), build_expr, index_expr) def _check_varsh_871_2(term_list): # Sum((-1)**(a-alpha)*CG(a,alpha,a,-alpha,c,0),(alpha,-a,a)) a,alpha,c,lt = map(Wild,('a','alpha','c','lt')) expr = lt*CG(a,alpha,a,-alpha,c,0) simp = sqrt(2*a+1)*KroneckerDelta(c,0) sign = (-1)**(a-alpha)*lt/abs(lt) build_expr = 2*a+1 index_expr = a+alpha return _check_cg_simp(expr, simp, sign, lt, term_list, (a,alpha,c,lt), (a,c), build_expr, index_expr) def _check_varsh_872_9(term_list): # Sum( CG(a,alpha,b,beta,c,gamma)*CG(a,alpha',b,beta',c,gamma), (gamma, -c, c), (c, abs(a-b), a+b)) a,alpha,alphap,b,beta,betap,c,gamma,lt = map(Wild, ('a','alpha','alphap','b','beta','betap','c','gamma','lt')) # Case alpha==alphap, beta==betap # For numerical alpha,beta expr = lt*CG(a,alpha,b,beta,c,gamma)**2 simp = 1 sign = lt/abs(lt) x = abs(a-b) y = abs(alpha+beta) build_expr = a+b+1-Piecewise((x,x>y),(0,Eq(x,y)),(y,y>x)) index_expr = a+b-c term_list, other1 = _check_cg_simp(expr, simp, sign, lt, term_list, (a,alpha,b,beta,c,gamma,lt), (a,alpha,b,beta), build_expr, index_expr) # For symbolic alpha,beta x = abs(a-b) y = a+b build_expr = (y+1-x)*(x+y+1) index_expr = (c-x)*(x+c)+c+gamma term_list, other2 = _check_cg_simp(expr, simp, sign, lt, term_list, (a,alpha,b,beta,c,gamma,lt), (a,alpha,b,beta), build_expr, index_expr) # Case alpha!=alphap or beta!=betap # Note: this only works with leading term of 1, pattern matching is unable to match when there is a Wild leading term # For numerical alpha,alphap,beta,betap expr = CG(a,alpha,b,beta,c,gamma)*CG(a,alphap,b,betap,c,gamma) simp = KroneckerDelta(alpha,alphap)*KroneckerDelta(beta,betap) sign = sympify(1) x = abs(a-b) y = abs(alpha+beta) build_expr = a+b+1-Piecewise((x,x>y),(0,Eq(x,y)),(y,y>x)) index_expr = a+b-c term_list, other3 = _check_cg_simp(expr, simp, sign, sympify(1), term_list, (a,alpha,alphap,b,beta,betap,c,gamma), (a,alpha,alphap,b,beta,betap), build_expr, index_expr) # For symbolic alpha,alphap,beta,betap x = abs(a-b) y = a+b build_expr = (y+1-x)*(x+y+1) index_expr = (c-x)*(x+c)+c+gamma term_list, other4 = _check_cg_simp(expr, simp, sign, sympify(1), term_list, (a,alpha,alphap,b,beta,betap,c,gamma), (a,alpha,alphap,b,beta,betap), build_expr, index_expr) return term_list, other1+other2+other4 def _check_cg_simp(expr, simp, sign, lt, term_list, variables, dep_variables, build_index_expr, index_expr): """ Checks for simplifications that can be made, returning a tuple of the simplified list of terms and any terms generated by simplification. Parameters ========== expr: expression The expression with Wild terms that will be matched to the terms in the sum simp: expression The expression with Wild terms that is substituted in place of the CG terms in the case of simplification sign: expression The expression with Wild terms denoting the sign that is on expr that must match lt: expression The expression with Wild terms that gives the leading term of the matched expr term_list: list A list of all of the terms is the sum to be simplified variables: list A list of all the variables that appears in expr dep_variables: list A list of the variables that must match for all the terms in the sum, i.e. the dependant variables build_index_expr: expression Expression with Wild terms giving the number of elements in cg_index index_expr: expression Expression with Wild terms giving the index terms have when storing them to cg_index """ other_part = 0 i = 0 while i < len(term_list): sub_1 = _check_cg(term_list[i], expr, len(variables)) if sub_1 is None: i += 1 continue if not sympify(build_index_expr.subs(sub_1)).is_number: i += 1 continue sub_dep = [(x,sub_1[x]) for x in dep_variables] cg_index = [None] * build_index_expr.subs(sub_1) for j in range(i,len(term_list)): sub_2 = _check_cg(term_list[j], expr.subs(sub_dep), len(variables)-len(dep_variables), sign=(sign.subs(sub_1),sign.subs(sub_dep))) if sub_2 is None: continue if not sympify(index_expr.subs(sub_dep).subs(sub_2)).is_number: continue cg_index[index_expr.subs(sub_dep).subs(sub_2)] = j, expr.subs(lt,1).subs(sub_dep).subs(sub_2), lt.subs(sub_2), sign.subs(sub_dep).subs(sub_2) if cg_index.count(None) == 0: min_lt = min(*[ abs(term[2]) for term in cg_index ]) indicies = [ term[0] for term in cg_index] indicies.sort() indicies.reverse() [ term_list.pop(i) for i in indicies ] for term in cg_index: if abs(term[2]) > min_lt: term_list.append( (term[2]-min_lt*term[3]) * term[1] ) other_part += min_lt * (sign*simp).subs(sub_1) else: i += 1 return term_list, other_part def _check_cg(cg_term, expr, length, sign=None): """Checks whether a term matches the given expression""" # TODO: Check for symmetries matches = cg_term.match(expr) if matches is None: return if not sign is None: if not isinstance(sign, tuple): raise TypeError('sign must be a tuple') if not sign[0] == (sign[1]).subs(matches): return if len(matches) == length: return matches def _cg_simp_sum(e): e = _check_varsh_sum_871_1(e) e = _check_varsh_sum_871_2(e) e = _check_varsh_sum_872_4(e) return e def _check_varsh_sum_871_1(e): a = Wild('a') alpha = symbols('alpha') b = Wild('b') match = e.match(Sum(CG(a,alpha,b,0,a,alpha),(alpha,-a,a))) if not match is None and len(match) == 2: return ((2*a+1)*KroneckerDelta(b,0)).subs(match) return e def _check_varsh_sum_871_2(e): a = Wild('a') alpha = symbols('alpha') c = Wild('c') match = e.match(Sum((-1)**(a-alpha)*CG(a,alpha,a,-alpha,c,0),(alpha,-a,a))) if not match is None and len(match) == 2: return (sqrt(2*a+1)*KroneckerDelta(c,0)).subs(match) return e def _check_varsh_sum_872_4(e): a = Wild('a') alpha = Wild('alpha') b = Wild('b') beta = Wild('beta') c = Wild('c') cp = Wild('cp') gamma = Wild('gamma') gammap = Wild('gammap') match1 = e.match(Sum(CG(a,alpha,b,beta,c,gamma)*CG(a,alpha,b,beta,cp,gammap),(alpha,-a,a),(beta,-b,b))) if not match1 is None and len(match1) == 8: return (KroneckerDelta(c,cp)*KroneckerDelta(gamma,gammap)).subs(match1) match2 = e.match(Sum(CG(a,alpha,b,beta,c,gamma)**2,(alpha,-a,a),(beta,-b,b))) if not match2 is None and len(match2) == 6: return 1 return e def _cg_list(term): if isinstance(term, CG): return (term,), 1, 1 cg = [] coeff = 1 if not (isinstance(term, Mul) or isinstance(term, Pow)): raise NotImplementedError('term must be CG, Add, Mul or Pow') if isinstance(term, Pow) and sympify(term.exp).is_number: if sympify(term.exp).is_number: [ cg.append(term.base) for _ in range(term.exp) ] else: return (term,), 1, 1 if isinstance(term, Mul): for arg in term.args: if isinstance(arg, CG): cg.append(arg) else: coeff *= arg return cg, coeff, coeff/abs(coeff) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/circuitplot.py0000644000175000017500000001573612014170666027202 0ustar georgeskgeorgesk"""Matplotlib based plotting of quantum circuits. Todo: * Optimize printing of large circuits. * Get this to work with single gates. * Do a better job checking the form of circuits to make sure it is a Mul of Gates. * Get multi-target gates plotting. * Get initial and final states to plot. * Get measurements to plot. Might need to rethink measurement as a gate issue. * Get scale and figsize to be handled in a better way. * Write some tests/examples! """ from sympy import Mul from sympy.physics.quantum.gate import Gate from sympy.external import import_module __all__ = [ 'CircuitPlot', 'circuit_plot' ] np = import_module('numpy', min_python_version=(2, 6)) matplotlib = import_module('matplotlib', __import__kwargs={'fromlist':['pyplot']}) if not np or not matplotlib: class CircuitPlot(object): def __init__(*args, **kwargs): raise ImportError('numpy or matplotlib not available.') def circuit_plot(*args, **kwargs): raise ImportError('numpy or matplotlib not available.') else: pyplot = matplotlib.pyplot Line2D = matplotlib.lines.Line2D Circle = matplotlib.patches.Circle class CircuitPlot(object): """A class for managing a circuit plot.""" scale = 1.0 fontsize = 20.0 linewidth = 1.0 control_radius = 0.05 not_radius = 0.15 swap_delta = 0.05 def __init__(self, c, nqubits, **kwargs): self.circuit = c self.ngates = len(self.circuit.args) self.nqubits = nqubits self.update(kwargs) self._create_grid() self._create_figure() self._plot_wires() self._plot_gates() self._finish() def update(self, kwargs): """Load the kwargs into the instance dict.""" self.__dict__.update(kwargs) def _create_grid(self): """Create the grid of wires.""" scale = self.scale wire_grid = np.arange(0.0,self.nqubits*scale,scale,dtype=float) gate_grid = np.arange(0.0,self.ngates*scale,scale,dtype=float) self._wire_grid = wire_grid self._gate_grid = gate_grid def _create_figure(self): """Create the main matplotlib figure.""" self._figure = pyplot.figure( figsize=(self.ngates*self.scale,self.nqubits*self.scale), facecolor='w', edgecolor='w' ) ax = self._figure.add_subplot( 1,1,1, frameon=True ) ax.set_axis_off() offset = 0.5*self.scale ax.set_xlim(self._gate_grid[0]-offset,self._gate_grid[-1]+offset) ax.set_ylim(self._wire_grid[0]-offset,self._wire_grid[-1]+offset) ax.set_aspect('equal') self._axes = ax def _plot_wires(self): """Plot the wires of the circuit diagram.""" xstart = self._gate_grid[0] xstop = self._gate_grid[-1] xdata = (xstart-self.scale, xstop+self.scale) for i in range(self.nqubits): ydata = (self._wire_grid[i], self._wire_grid[i]) line = Line2D( xdata,ydata, color='k', lw=self.linewidth ) self._axes.add_line(line) def _plot_gates(self): """Iterate through the gates and plot each of them.""" gates = [] if isinstance(self.circuit, Mul): for g in reversed(self.circuit.args): if isinstance(g, Gate): gates.append(g) elif isinstance(self.circuit, Gate): gates.append(self.circuit) for i, gate in enumerate(gates): gate.plot_gate(self, i) def _finish(self): # Disable clipping to make panning work well for large circuits. for o in self._figure.findobj(): o.set_clip_on(False) def one_qubit_box(self, t, gate_idx, wire_idx): """Draw a box for a single qubit gate.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] self._axes.text( x, y, t, color='k', ha='center', va='center', bbox = dict(ec='k',fc='w',fill=True,lw=self.linewidth), size=self.fontsize ) def control_line(self, gate_idx, min_wire, max_wire): """Draw a vertical control line.""" xdata = (self._gate_grid[gate_idx], self._gate_grid[gate_idx]) ydata = (self._wire_grid[min_wire], self._wire_grid[max_wire]) line = Line2D( xdata,ydata, color='k', lw=self.linewidth ) self._axes.add_line(line) def control_point(self, gate_idx, wire_idx): """Draw a control point.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] radius = self.control_radius c = Circle( (x,y), radius*self.scale, ec='k', fc='k', fill=True, lw=self.linewidth ) self._axes.add_patch(c) def not_point(self, gate_idx, wire_idx): """Draw a NOT gates as the circle with plus in the middle.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] radius = self.not_radius c = Circle( (x,y), radius, ec='k', fc='w', fill=False, lw=self.linewidth ) self._axes.add_patch(c) l = Line2D( (x,x),(y-radius,y+radius), color='k', lw=self.linewidth ) self._axes.add_line(l) def swap_point(self, gate_idx, wire_idx): """Draw a swap point as a cross.""" x = self._gate_grid[gate_idx] y = self._wire_grid[wire_idx] d = self.swap_delta l1 = Line2D( (x-d,x+d), (y-d,y+d), color='k', lw=self.linewidth ) l2 = Line2D( (x-d,x+d), (y+d,y-d), color='k', lw=self.linewidth ) self._axes.add_line(l1) self._axes.add_line(l2) def circuit_plot(c, nqubits, **kwargs): """Draw the circuit diagram for the circuit with nqubits. Parameters ========== c : circuit The circuit to plot. Should be a product of Gate instances. nqubits : int The number of qubits to include in the circuit. Must be at least as big as the largest `min_qubits`` of the gates. """ return CircuitPlot(c, nqubits, **kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/qubit.py0000644000175000017500000005176412014170666025766 0ustar georgeskgeorgesk"""Qubits for quantum computing. Todo: * Finish implementing measurement logic. This should include POVM. * Update docstrings. * Update tests. """ import math from sympy import Integer, log, Mul, Add, Pow, conjugate from sympy.core.basic import sympify from sympy.matrices.matrices import Matrix, zeros from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.hilbert import ComplexSpace from sympy.physics.quantum.state import Ket, Bra, State from sympy.physics.quantum.qexpr import QuantumError from sympy.physics.quantum.represent import represent from sympy.physics.quantum.matrixutils import ( numpy_ndarray, scipy_sparse_matrix ) __all__ = [ 'Qubit', 'QubitBra', 'IntQubit', 'IntQubitBra', 'qubit_to_matrix', 'matrix_to_qubit', 'measure_all', 'measure_partial', 'measure_partial_oneshot', 'measure_all_oneshot' ] #----------------------------------------------------------------------------- # Qubit Classes #----------------------------------------------------------------------------- class QubitState(State): """Base class for Qubit and QubitBra.""" #------------------------------------------------------------------------- # Initialization/creation #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): # If we are passed a QubitState or subclass, we just take its qubit # values directly. if len(args) == 1 and isinstance(args[0], QubitState): return args[0].qubit_values # Turn strings into tuple of strings if len(args) == 1 and isinstance(args[0], basestring): args = tuple(args[0]) args = sympify(args) # Validate input (must have 0 or 1 input) for element in args: if not (element == 1 or element == 0): raise ValueError("Qubit values must be 0 or 1, got: %r" % element) return args @classmethod def _eval_hilbert_space(cls, args): return ComplexSpace(2)**len(args) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def dimension(self): """The number of Qubits in the state.""" return len(self.qubit_values) @property def nqubits(self): return self.dimension @property def qubit_values(self): """Returns the values of the qubits as a tuple.""" return self.label #------------------------------------------------------------------------- # Special methods #------------------------------------------------------------------------- def __len__(self): return self.dimension def __getitem__(self, bit): return self.qubit_values[int(self.dimension-bit-1)] #------------------------------------------------------------------------- # Utility methods #------------------------------------------------------------------------- def flip(self, *bits): """Flip the bit(s) given.""" newargs = list(self.qubit_values) for i in bits: bit = int(self.dimension-i-1) if newargs[bit] == 1: newargs[bit] = 0 else: newargs[bit] = 1 return self.__class__(*tuple(newargs)) class Qubit(QubitState, Ket): """A multi-qubit ket in the computational (z) basis. We use the normal convention that the least significant qubit is on the right, so |00001> has a 1 in the least significant qubit. Parameters ========== values : list, str The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011'). Examples ======== Create a qubit in a couple of different ways and look at their attributes: >>> from sympy.physics.quantum.qubit import Qubit >>> Qubit(0,0,0) |000> >>> q = Qubit('0101') >>> q |0101> >>> q.nqubits 4 >>> len(q) 4 >>> q.dimension 4 >>> q.qubit_values (0, 1, 0, 1) We can flip the value of an individual qubit: >>> q.flip(1) |0111> We can take the dagger of a Qubit to get a bra: >>> from sympy.physics.quantum.dagger import Dagger >>> Dagger(q) <0101| >>> type(Dagger(q)) Inner products work as expected: >>> ip = Dagger(q)*q >>> ip <0101|0101> >>> ip.doit() 1 """ @property def dual_class(self): return QubitBra def _eval_innerproduct_QubitBra(self, bra, **hints): if self.label == bra.label: return Integer(1) else: return Integer(0) def _represent_default_basis(self, **options): return self._represent_ZGate(None, **options) def _represent_ZGate(self, basis, **options): """Represent this qubits in the computational basis (ZGate). """ format = options.get('format', 'sympy') n = 1 definite_state = 0 for it in reversed(self.qubit_values): definite_state += n*it n = n*2 result = [0]*(2**self.dimension) result[int(definite_state)] = 1 if format == 'sympy': return Matrix(result) elif format == 'numpy': import numpy as np return np.matrix(result, dtype='complex').transpose() elif format == 'scipy.sparse': from scipy import sparse return sparse.csr_matrix(result, dtype='complex').transpose() class QubitBra(QubitState, Bra): """A multi-qubit bra in the computational (z) basis. We use the normal convention that the least significant qubit is on the right, so |00001> has a 1 in the least significant qubit. Parameters ========== values : list, str The qubit values as a list of ints ([0,0,0,1,1,]) or a string ('011'). Examples ======== See ``Qubit`` for examples. """ @property def dual_class(self): return Qubit class IntQubitState(QubitState): """A base class for qubits that work with binary representations.""" @classmethod def _eval_args(cls, args): # The case of a QubitState instance if len(args) == 1 and isinstance(args[0], QubitState): return QubitState._eval_args(args) # For a single argument, we construct the binary representation of # that integer with the minimal number of bits. if len(args) == 1 and args[0] > 1: #rvalues is the minimum number of bits needed to express the number rvalues = reversed( range(int(math.ceil(math.log(args[0], 2)+.01)+.001)) ) qubit_values = [(args[0]>>i)&1 for i in rvalues] return QubitState._eval_args(qubit_values) # For two numbers, the second number is the number of bits # on which it is expressed, so IntQubit(0,5) == |00000>. elif len(args) == 2 and args[1] > 1: #TODO Raise error if there are not enough bits qubit_values = [(args[0]>>i)&1 for i in reversed(range(args[1]))] return QubitState._eval_args(qubit_values) else: return QubitState._eval_args(args) def as_int(self): """Return the numerical value of the qubit.""" number = 0 n = 1 for i in reversed(self.qubit_values): number += n*i n = n<<1 return number def _print_label(self, printer, *args): return str(self.as_int()) def _print_label_pretty(self, printer, *args): label = self._print_label(printer, *args) return prettyForm(label) _print_label_repr = _print_label _print_label_latex = _print_label class IntQubit(IntQubitState, Qubit): """A qubit ket that store integers as binary numbers in qubit values. The differences between this class and ``Qubit`` are: * The form of the constructor. * The qubit values are printed as their corresponding integer, rather than the raw qubit values. The internal storage format of the qubit values in the same as ``Qubit``. Parameters ========== values : int, tuple If a single argument, the integer we want to represent in the qubit values. This integer will be represented using the fewest possible number of qubits. If a pair of integers, the first integer gives the integer to represent in binary form and the second integer gives the number of qubits to use. Examples ======== Create a qubit for the integer 5: >>> from sympy.physics.quantum.qubit import IntQubit >>> from sympy.physics.quantum.qubit import Qubit >>> q = IntQubit(5) >>> q |5> We can also create an ``IntQubit`` by passing a ``Qubit`` instance. >>> q = IntQubit(Qubit('101')) >>> q |5> >>> q.as_int() 5 >>> q.nqubits 3 >>> q.qubit_values (1, 0, 1) We can go back to the regular qubit form. >>> Qubit(q) |101> """ @property def dual_class(self): return IntQubitBra class IntQubitBra(IntQubitState, QubitBra): """A qubit bra that store integers as binary numbers in qubit values.""" @property def dual_class(self): return IntQubit #----------------------------------------------------------------------------- # Qubit <---> Matrix conversion functions #----------------------------------------------------------------------------- def matrix_to_qubit(matrix): """Convert from the matrix repr. to a sum of Qubit objects. Parameters ---------- matrix : Matrix, numpy.matrix, scipy.sparse The matrix to build the Qubit representation of. This works with sympy matrices, numpy matrices and scipy.sparse sparse matrices. Examples -------- Represent a state and then go back to its qubit form: >>> from sympy.physics.quantum.qubit import matrix_to_qubit, Qubit >>> from sympy.physics.quantum.gate import Z >>> from sympy.physics.quantum.represent import represent >>> q = Qubit('01') >>> matrix_to_qubit(represent(q)) |01> """ # Determine the format based on the type of the input matrix format = 'sympy' if isinstance(matrix, numpy_ndarray): format = 'numpy' if isinstance(matrix, scipy_sparse_matrix): format = 'scipy.sparse' # Make sure it is of correct dimensions for a Qubit-matrix representation. # This logic should work with sympy, numpy or scipy.sparse matrices. if matrix.shape[0] == 1: mlistlen = matrix.shape[1] nqubits = log(mlistlen, 2) ket = False cls = QubitBra elif matrix.shape[1] == 1: mlistlen = matrix.shape[0] nqubits = log(mlistlen, 2) ket = True cls = Qubit else: raise QuantumError( 'Matrix must be a row/column vector, got %r' % matrix ) if not isinstance(nqubits, Integer): raise QuantumError('Matrix must be a row/column vector of size ' '2**nqubits, got: %r' % matrix) # Go through each item in matrix, if element is non-zero, make it into a # Qubit item times the element. result = 0 for i in range(mlistlen): if ket: element = matrix[i,0] else: element = matrix[0,i] if format == 'numpy' or format == 'scipy.sparse': element = complex(element) if element != 0.0: # Form Qubit array; 0 in bit-locations where i is 0, 1 in # bit-locations where i is 1 qubit_array = [int(i & (1<>> from sympy.physics.quantum.qubit import Qubit, measure_all >>> from sympy.physics.quantum.gate import H, X, Y, Z >>> from sympy.physics.quantum.qapply import qapply >>> c = H(0)*H(1)*Qubit('00') >>> c H(0)*H(1)*|00> >>> q = qapply(c) >>> measure_all(q) [(|00>, 1/4), (|01>, 1/4), (|10>, 1/4), (|11>, 1/4)] """ m = qubit_to_matrix(qubit, format) if format == 'sympy': results = [] if normalize: m = m.normalized() size = max(m.shape) # Max of shape to account for bra or ket nqubits = int(math.log(size)/math.log(2)) for i in range(size): if m[i] != 0.0: results.append( (Qubit(IntQubit(i, nqubits)), m[i]*conjugate(m[i])) ) return results else: raise NotImplementedError( "This function can't handle non-sympy matrix formats yet" ) def measure_partial(qubit, bits, format='sympy', normalize=True): """Perform a partial ensemble measure on the specifed qubits. Parameters ========== qubits : Qubit The qubit to measure. This can be any Qubit or a linear combination of them. bits : tuple The qubits to measure. format : str The format of the intermediate matrices to use. Possible values are ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is implemented. Returns ======= result : list A list that consists of primitive states and their probabilities. Examples ======== >>> from sympy.physics.quantum.qubit import Qubit, measure_partial >>> from sympy.physics.quantum.gate import H, X, Y, Z >>> from sympy.physics.quantum.qapply import qapply >>> c = H(0)*H(1)*Qubit('00') >>> c H(0)*H(1)*|00> >>> q = qapply(c) >>> measure_partial(q, (0,)) [(2**(1/2)*|00>/2 + 2**(1/2)*|10>/2, 1/2), (2**(1/2)*|01>/2 + 2**(1/2)*|11>/2, 1/2)] """ m = qubit_to_matrix(qubit, format) if isinstance(bits, (int, Integer)): bits = (int(bits),) if format == 'sympy': if normalize: m = m.normalized() possible_outcomes = _get_possible_outcomes(m, bits) # Form output from function. output = [] for outcome in possible_outcomes: # Calculate probability of finding the specified bits with # given values. prob_of_outcome = 0 prob_of_outcome += (outcome.H*outcome)[0] # If the output has a chance, append it to output with found # probability. if prob_of_outcome != 0: if normalize: next_matrix = matrix_to_qubit(outcome.normalized()) else: next_matrix = matrix_to_qubit(outcome) output.append(( next_matrix, prob_of_outcome )) return output else: raise NotImplementedError( "This function can't handle non-sympy matrix formats yet" ) def measure_partial_oneshot(qubit, bits, format='sympy'): """Perform a partial oneshot measurement on the specified qubits. A oneshot measurement is equivalent to performing a measurement on a quantum system. This type of measurement does not return the probabilities like an ensemble measurement does, but rather returns *one* of the possible resulting states. The exact state that is returned is determined by picking a state randomly according to the ensemble probabilities. Parameters ---------- qubits : Qubit The qubit to measure. This can be any Qubit or a linear combination of them. bits : tuple The qubits to measure. format : str The format of the intermediate matrices to use. Possible values are ('sympy','numpy','scipy.sparse'). Currently only 'sympy' is implemented. Returns ------- result : Qubit The qubit that the system collapsed to upon measurement. """ import random m = qubit_to_matrix(qubit, format) if format == 'sympy': m = m.normalized() possible_outcomes = _get_possible_outcomes(m, bits) # Form output from function random_number = random.random() total_prob = 0 for outcome in possible_outcomes: # Calculate probability of finding the specified bits # with given values total_prob += (outcome.H*outcome)[0] if total_prob >= random_number: return matrix_to_qubit(outcome.normalized()) else: raise NotImplementedError( "This function can't handle non-sympy matrix formats yet" ) def _get_possible_outcomes(m, bits): """Get the possible states that can be produced in a measurement. Parameters ---------- m : Matrix The matrix representing the state of the system. bits : tuple, list Which bits will be measured. Returns ------- result : list The list of possible states which can occur given this measurement. These are un-normalized so we can derive the probability of finding this state by taking the inner product with itself """ # This is filled with loads of dirty binary tricks...You have been warned size = max(m.shape) # Max of shape to account for bra or ket nqubits = int(math.log(size,2)+.1) # Number of qubits possible # Make the output states and put in output_matrices, nothing in them now. # Each state will represent a possible outcome of the measurement # Thus, output_matrices[0] is the matrix which we get when all measured # bits return 0. and output_matrices[1] is the matrix for only the 0th # bit being true output_matrices = [] for i in range(1< random_number: break result += 1 return Qubit(IntQubit(result, int(math.log(max(m.shape),2)+.1))) else: raise NotImplementedError( "This function can't handle non-sympy matrix formats yet" ) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tensorproduct.py0000644000175000017500000002462712014170666027553 0ustar georgeskgeorgesk"""Abstract tensor product.""" from sympy import Expr, Add, Mul, Matrix, Pow from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.qexpr import QuantumError, split_commutative_parts from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.matrixutils import ( numpy_ndarray, scipy_sparse_matrix, matrix_tensor_product ) __all__ = [ 'TensorProduct', 'tensor_product_simp' ] #----------------------------------------------------------------------------- # Tensor product #----------------------------------------------------------------------------- class TensorProduct(Expr): """The tensor product of two or more arguments. For matrices, this uses ``matrix_tensor_product`` to compute the Kronecker or tensor product matrix. For other objects a symbolic ``TensorProduct`` instance is returned. The tensor product is a non-commutative multiplication that is used primarily with operators and states in quantum mechanics. Currently, the tensor product distinguishes between commutative and non- commutative arguments. Commutative arguments are assumed to be scalars and are pulled out in front of the ``TensorProduct``. Non-commutative arguments remain in the resulting ``TensorProduct``. Parameters ========== args : tuple A sequence of the objects to take the tensor product of. Examples ======== Start with a simple tensor product of sympy matrices:: >>> from sympy import I, Matrix, symbols >>> from sympy.physics.quantum import TensorProduct >>> m1 = Matrix([[1,2],[3,4]]) >>> m2 = Matrix([[1,0],[0,1]]) >>> TensorProduct(m1, m2) [1, 0, 2, 0] [0, 1, 0, 2] [3, 0, 4, 0] [0, 3, 0, 4] >>> TensorProduct(m2, m1) [1, 2, 0, 0] [3, 4, 0, 0] [0, 0, 1, 2] [0, 0, 3, 4] We can also construct tensor products of non-commutative symbols:: >>> from sympy import Symbol >>> A = Symbol('A',commutative=False) >>> B = Symbol('B',commutative=False) >>> tp = TensorProduct(A, B) >>> tp AxB We can take the dagger of a tensor product (note the order does NOT reverse like the dagger of a normal product):: >>> from sympy.physics.quantum import Dagger >>> Dagger(tp) Dagger(A)xDagger(B) Expand can be used to distribute a tensor product across addition:: >>> C = Symbol('C',commutative=False) >>> tp = TensorProduct(A+B,C) >>> tp (A + B)xC >>> tp.expand(tensorproduct=True) AxC + BxC """ def __new__(cls, *args, **assumptions): if isinstance(args[0], (Matrix, numpy_ndarray, scipy_sparse_matrix)): return matrix_tensor_product(*args) c_part, new_args = cls.flatten(args) c_part = Mul(*c_part) if len(new_args) == 0: return c_part elif len(new_args) == 1: return c_part*new_args[0] else: tp = Expr.__new__(cls, *new_args, **{'commutative': False}) return c_part*tp @classmethod def flatten(cls, args): # TODO: disallow nested TensorProducts. c_part = [] nc_parts = [] for arg in args: if isinstance(arg, Mul): cp, ncp = split_commutative_parts(arg) ncp = Mul(*ncp) else: if arg.is_commutative: cp = [arg]; ncp = 1 else: cp = []; ncp = arg c_part.extend(cp) nc_parts.append(ncp) return c_part, nc_parts def _eval_dagger(self): return TensorProduct(*[Dagger(i) for i in self.args]) def _sympystr(self, printer, *args): from sympy.printing.str import sstr length = len(self.args) s = '' for i in range(length): if isinstance(self.args[i], (Add, Pow, Mul)): s = s + '(' s = s + sstr(self.args[i]) if isinstance(self.args[i], (Add, Pow, Mul)): s = s + ')' if i != length-1: s = s + 'x' return s def _pretty(self, printer, *args): length = len(self.args) pform = printer._print('', *args) for i in range(length): next_pform = printer._print(self.args[i], *args) if isinstance(self.args[i], (Add, Mul)): next_pform = prettyForm( *next_pform.parens(left='(', right=')') ) pform = prettyForm(*pform.right(next_pform)) if i != length-1: pform = prettyForm(*pform.right(u'\u2a02' + u' ')) return pform def _latex(self, printer, *args): length = len(self.args) s = '' for i in range(length): if isinstance(self.args[i], (Add, Mul)): s = s + '\\left(' # The extra {} brackets are needed to get matplotlib's latex # rendered to render this properly. s = s + '{' + printer._print(self.args[i], *args) + '}' if isinstance(self.args[i], (Add, Mul)): s = s + '\\right)' if i != length-1: s = s + '\\otimes ' return s def doit(self, **hints): return TensorProduct(*[item.doit(**hints) for item in self.args]) def _eval_expand_tensorproduct(self, **hints): """Distribute TensorProducts across addition.""" args = self.args add_args = [] stop = False for i in range(len(args)): if isinstance(args[i], Add): for aa in args[i].args: add_args.append( TensorProduct( *args[:i]+(aa,)+args[i+1:] ).expand(**hints) ) stop = True if stop: break if add_args: return Add(*add_args).expand(**hints) else: return self def expand(self, **hints): tp = TensorProduct(*[item.expand(**hints) for item in self.args]) return Expr.expand(tp, **hints) def tensor_product_simp_Mul(e): """Simplify a Mul with TensorProducts. Current the main use of this is to simplify a ``Mul`` of ``TensorProduct``s to a ``TensorProduct`` of ``Muls``. It currently only works for relatively simple cases where the initial ``Mul`` only has scalars and raw ``TensorProduct``s, not ``Add``, ``Pow``, ``Commutator``s of ``TensorProduct``s. Parameters ========== e : Expr A ``Mul`` of ``TensorProduct``s to be simplified. Returns ======= e : Expr A ``TensorProduct`` of ``Mul``s. Examples ======== This is an example of the type of simplification that this function performs:: >>> from sympy.physics.quantum.tensorproduct import tensor_product_simp_Mul, TensorProduct >>> from sympy import Symbol >>> A = Symbol('A',commutative=False) >>> B = Symbol('B',commutative=False) >>> C = Symbol('C',commutative=False) >>> D = Symbol('D',commutative=False) >>> e = TensorProduct(A,B)*TensorProduct(C,D) >>> e AxB*CxD >>> tensor_product_simp_Mul(e) (A*C)x(B*D) """ # TODO: This won't work with Muls that have other composites of # TensorProducts, like an Add, Pow, Commutator, etc. # TODO: This only works for the equivalent of single Qbit gates. if not isinstance(e, Mul): return e c_part, nc_part = split_commutative_parts(e) n_nc = len(nc_part) if n_nc == 0 or n_nc == 1: return e elif e.has(TensorProduct): current = nc_part[0] if not isinstance(current, TensorProduct): raise TypeError('TensorProduct expected, got: %r' % current) n_terms = len(current.args) new_args = list(current.args) for next in nc_part[1:]: # TODO: check the hilbert spaces of next and current here. if isinstance(next, TensorProduct): if n_terms != len(next.args): raise QuantumError( 'TensorProducts of different lengths: %r and %r' % \ (current, next) ) for i in range(len(new_args)): new_args[i] = new_args[i]*next.args[i] else: # this won't quite work as we don't want next in the TensorProduct for i in range(len(new_args)): new_args[i] = new_args[i]*next current = next return Mul(*c_part)*TensorProduct(*new_args) else: return e def tensor_product_simp(e, **hints): """Try to simplify and combine TensorProducts. In general this will try to pull expressions inside of ``TensorProducts``. It currently only works for relatively simple cases where the products have only scalars, raw ``TensorProduct``s, not ``Add``, ``Pow``, ``Commutator``s of ``TensorProduct``s. It is best to see what it does by showing examples. Examples ======== >>> from sympy.physics.quantum import tensor_product_simp >>> from sympy.physics.quantum import TensorProduct >>> from sympy import Symbol >>> A = Symbol('A',commutative=False) >>> B = Symbol('B',commutative=False) >>> C = Symbol('C',commutative=False) >>> D = Symbol('D',commutative=False) First see what happens to products of tensor products:: >>> e = TensorProduct(A,B)*TensorProduct(C,D) >>> e AxB*CxD >>> tensor_product_simp(e) (A*C)x(B*D) This is the core logic of this function, and it works inside, powers, sums, commutators and anticommutators as well:: >>> tensor_product_simp(e**2) (A*C)x(B*D)**2 """ if isinstance(e, Add): return Add(*[tensor_product_simp(arg) for arg in e.args]) elif isinstance(e, Pow): return tensor_product_simp(e.base)**e.exp elif isinstance(e, Mul): return tensor_product_simp_Mul(e) elif isinstance(e, Commutator): return Commutator(*[tensor_product_simp(arg) for arg in e.args]) elif isinstance(e, AntiCommutator): return AntiCommutator(*[tensor_product_simp(arg) for arg in e.args]) else: return e wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/operator.py0000644000175000017500000002363612014170666026472 0ustar georgeskgeorgesk"""Quantum mechanical operators. TODO: * Fix early 0 in apply_operators. * Debug and test apply_operators. * Get cse working with classes in this file. * Doctests and documentation of special methods for InnerProduct, Commutator, AntiCommutator, represent, apply_operators. """ from sympy import Expr from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.qexpr import ( QExpr, dispatch_method ) __all__ = [ 'Operator', 'HermitianOperator', 'UnitaryOperator', 'OuterProduct' ] #----------------------------------------------------------------------------- # Operators and outer products #----------------------------------------------------------------------------- class Operator(QExpr): """Base class for non-commuting quantum operators. An operator maps one ket to another [1]. In quantum mechanics, Hermitian operators correspond to observables [2]. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. For time-dependent operators, this will include the time. Examples ======== Create an operator and examine its attributes:: >>> from sympy.physics.quantum import Operator >>> from sympy import symbols, I >>> A = Operator('A') >>> A A >>> A.hilbert_space H >>> A.label (A,) >>> A.is_commutative False Create another operator and do some arithmetic operations:: >>> B = Operator('B') >>> C = 2*A*A + I*B >>> C 2*A**2 + I*B Operators don't commute:: >>> A.is_commutative False >>> B.is_commutative False >>> A*B == B*A False Polymonials of operators respect the commutation properties:: >>> e = (A+B)**3 >>> e.expand() A*B*A + A*B**2 + A**2*B + A**3 + B*A*B + B*A**2 + B**2*A + B**3 Operator inverses are handle symbolically:: >>> A.inv() 1/A >>> A*A.inv() 1 References ========== [1] http://en.wikipedia.org/wiki/Operator [2] http://en.wikipedia.org/wiki/Observable """ #------------------------------------------------------------------------- # Printing #------------------------------------------------------------------------- _label_separator = ',' def _print_operator_name(self, printer, *args): return printer._print(self.__class__.__name__, *args) _print_operator_name_latex = _print_operator_name def _print_operator_name_pretty(self, printer, *args): return prettyForm(self.__class__.__name__) def _print_contents(self, printer, *args): if len(self.label) == 1: return self._print_label(printer, *args) else: return '%s(%s)' % ( self._print_operator_name(printer, *args), self._print_label(printer, *args) ) def _print_contents_pretty(self, printer, *args): if len(self.label) == 1: return self._print_label_pretty(printer, *args) else: pform = self._print_operator_name_pretty(printer, *args) label_pform = self._print_label_pretty(printer, *args) label_pform = prettyForm( *label_pform.parens(left='(', right=')') ) pform = prettyForm(*pform.right((label_pform))) return pform def _print_contents_latex(self, printer, *args): if len(self.label) == 1: return self._print_label_latex(printer, *args) else: return '%s(%s)' % ( self._print_operator_name_latex(printer, *args), self._print_label_latex(printer, *args) ) #------------------------------------------------------------------------- # _eval_* methods #------------------------------------------------------------------------- def _eval_commutator(self, other, **options): """Evaluate [self, other] if known, return None if not known.""" return dispatch_method(self, '_eval_commutator', other, **options) def _eval_anticommutator(self, other, **options): """Evaluate [self, other] if known.""" return dispatch_method(self, '_eval_anticommutator', other, **options) #------------------------------------------------------------------------- # Operator application #------------------------------------------------------------------------- def _apply_operator(self, ket, **options): return dispatch_method(self, '_apply_operator', ket, **options) def matrix_element(self, *args): raise NotImplementedError('matrix_elements is not defined') #------------------------------------------------------------------------- # Printing #------------------------------------------------------------------------- def inverse(self): return self._eval_inverse() inv = inverse def _eval_inverse(self): # TODO: make non-commutative Exprs print powers using A**-1, not 1/A. return self**(-1) class HermitianOperator(Operator): """A Hermitian operator that satisfies H == Dagger(H). Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. For time-dependent operators, this will include the time. Examples ======== >>> from sympy.physics.quantum import Dagger, HermitianOperator >>> H = HermitianOperator('H') >>> Dagger(H) H """ def _eval_dagger(self): return self def _eval_inverse(self): if isinstance(self, UnitaryOperator): return self else: return Operator._eval_inverse(self) def _eval_power(self, exp): if isinstance(self, UnitaryOperator): if exp == -1: return Operator._eval_power(self, exp) elif abs(exp) % 2 == 0: return self*(Operator._eval_inverse(self)) else: return self else: return Operator._eval_power(self, exp) class UnitaryOperator(Operator): """A unitary operator that satisfies U*Dagger(U) == 1. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the operator. For time-dependent operators, this will include the time. Examples ======== >>> from sympy.physics.quantum import Dagger, UnitaryOperator >>> U = UnitaryOperator('U') >>> U*Dagger(U) 1 """ def _eval_dagger(self): return self._eval_inverse() class OuterProduct(Operator): """An unevaluated outer product between a ket and kra. This constructs an outer product between any subclass of KetBase and BraBase as |a>>> from sympy.physics.quantum import Ket, Bra, OuterProduct, Dagger >>> from sympy.physics.quantum import Operator >>> k = Ket('k') >>> b = Bra('b') >>> op = OuterProduct(k, b) >>> op |k>>> op.hilbert_space H >>> op.ket |k> >>> op.bra >> Dagger(op) |b>>> k*b |k>>> A = Operator('A') >>> A*k*b A*|k>*>> A*(k*b) A*|k>>> from sympy.physics.quantum.grover import superposition_basis >>> superposition_basis(2) |0>/2 + |1>/2 + |2>/2 + |3>/2 """ amp = 1/sqrt(2**nqubits) return sum([amp*IntQubit(n, nqubits) for n in range(2**nqubits)]) class OracleGate(Gate): """A black box gate. The gate marks the desired qubits of an unknown function by flipping the sign of the qubits. The unknown function returns true when it finds its desired qubits and false otherwise. Parameters ========== qubits : int Number of qubits. oracle : callable A callable function that returns a boolean on a computational basis. Examples ======== Apply an Oracle gate that flips the sign of |2> on different qubits:: >>> from sympy.physics.quantum.qubit import IntQubit >>> from sympy.physics.quantum.qapply import qapply >>> from sympy.physics.quantum.grover import OracleGate >>> f = lambda qubits: qubits == IntQubit(2) >>> v = OracleGate(2, f) >>> qapply(v*IntQubit(2)) -|2> >>> qapply(v*IntQubit(3)) |3> """ gate_name = u'V' gate_name_latex = u'V' #------------------------------------------------------------------------- # Initialization/creation #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): if len(args) != 2: raise QuantumError( 'Insufficient/excessive arguments to Oracle. Please ' + 'supply the number of qubits and an unknown function.' ) sub_args = args[0], sub_args = UnitaryOperator._eval_args(sub_args) if not sub_args[0].is_Integer: raise TypeError('Integer expected, got: %r' % sub_args[0]) if not callable(args[1]): raise TypeError('Callable expected, got: %r' % args[1]) sub_args = UnitaryOperator._eval_args(tuple(range(args[0]))) return (sub_args, args[1]) @classmethod def _eval_hilbert_space(cls, args): """This returns the smallest possible Hilbert space.""" return ComplexSpace(2)**(max(args[0])+1) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def search_function(self): """The unknown function that helps find the sought after qubits.""" return self.label[1] @property def targets(self): """A tuple of target qubits.""" return self.label[0] #------------------------------------------------------------------------- # Apply #------------------------------------------------------------------------- def _apply_operator_Qubit(self, qubits, **options): """Apply this operator to a Qubit subclass. Parameters ========== qubits : Qubit The qubit subclass to apply this operator to. Returns ======= state : Expr The resulting quantum state. """ if qubits.nqubits != self.nqubits: raise QuantumError( 'OracleGate operates on %r qubits, got: %r' (self.nqubits, qubits.nqubits) ) # If function returns 1 on qubits # return the negative of the qubits (flip the sign) if self.search_function(qubits): return -qubits else: return qubits #------------------------------------------------------------------------- # Represent #------------------------------------------------------------------------- def _represent_ZGate(self, basis, **options): raise NotImplementedError( "Represent for the Oracle has not been implemented yet" ) class WGate(Gate): """General n qubit W Gate in Grover's algorithm. The gate performs the operation 2|phi> = (tensor product of n Hadamards)*(|0> with n qubits) Parameters ========== nqubits : int The number of qubits to operate on """ gate_name = u'W' gate_name_latex = u'W' @classmethod def _eval_args(cls, args): if len(args) != 1: raise QuantumError( 'Insufficient/excessive arguments to W gate. Please ' + 'supply the number of qubits to operate on.' ) args = UnitaryOperator._eval_args(args) if not args[0].is_Integer: raise TypeError('Integer expected, got: %r' % args[0]) return tuple(reversed(range(args[0]))) #------------------------------------------------------------------------- # Apply #------------------------------------------------------------------------- def _apply_operator_Qubit(self, qubits, **options): """ qubits: a set of qubits (Qubit) Returns: quantum object (quantum expression - QExpr) """ if qubits.nqubits != self.nqubits: raise QuantumError( 'WGate operates on %r qubits, got: %r' (self.nqubits, qubits.nqubits) ) # See 'Quantum Computer Science' by David Mermin p.92 -> W|a> result # Return (2/(sqrt(2^n)))|phi> - |a> where |a> is the current basis # state and phi is the superposition of basis states (see function # create_computational_basis above) basis_states = superposition_basis(self.nqubits) change_to_basis = (2/sqrt(2**self.nqubits))*basis_states return change_to_basis - qubits def grover_iteration(qstate, oracle): """Applies one application of the Oracle and W Gate, WV. Parameters ========== qstate : Qubit A superposition of qubits. oracle : OracleGate The black box operator that flips the sign of the desired basis qubits. Returns ======= Qubit : The qubits after applying the Oracle and W gate. Examples ======== Perform one iteration of grover's algorithm to see a phase change:: >>> from sympy.physics.quantum.qapply import qapply >>> from sympy.physics.quantum.qubit import IntQubit >>> from sympy.physics.quantum.grover import OracleGate >>> from sympy.physics.quantum.grover import superposition_basis >>> from sympy.physics.quantum.grover import grover_iteration >>> numqubits = 2 >>> basis_states = superposition_basis(numqubits) >>> f = lambda qubits: qubits == IntQubit(2) >>> v = OracleGate(numqubits, f) >>> qapply(grover_iteration(basis_states, v)) |2> """ wgate = WGate(oracle.nqubits) return wgate*oracle*qstate def apply_grover(oracle, nqubits, iterations=None): """Applies grover's algorithm. Parameters ========== oracle : callable The unknown callable function that returns true when applied to the desired qubits and false otherwise. Returns ======= state : Expr The resulting state after Grover's algorithm has been iterated. Examples ======== Apply grover's algorithm to an even superposition of 2 qubits:: >>> from sympy.physics.quantum.qapply import qapply >>> from sympy.physics.quantum.qubit import IntQubit >>> from sympy.physics.quantum.grover import apply_grover >>> f = lambda qubits: qubits == IntQubit(2) >>> qapply(apply_grover(f, 2)) |2> """ if nqubits <= 0: raise QuantumError( 'Grover\'s algorithm needs nqubits > 0, received %r qubits' % nqubits ) if iterations is None: iterations = floor(sqrt(2**nqubits)*(pi/4)) v = OracleGate(nqubits, oracle) iterated = superposition_basis(nqubits) for iter in range(iterations): iterated = grover_iteration(iterated, v) iterated = qapply(iterated) return iterated wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/hilbert.py0000644000175000017500000004516612014170666026272 0ustar georgeskgeorgesk"""Hilbert spaces for quantum mechanics. Authors: * Brian Granger * Matt Curry """ from sympy import Basic, Interval, oo, sympify from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.qexpr import QuantumError from sympy.core.compatibility import reduce __all__ = [ 'HilbertSpaceError', 'HilbertSpace', 'ComplexSpace', 'L2', 'FockSpace' ] #----------------------------------------------------------------------------- # Main objects #----------------------------------------------------------------------------- class HilbertSpaceError(QuantumError): pass #----------------------------------------------------------------------------- # Main objects #----------------------------------------------------------------------------- class HilbertSpace(Basic): """An abstract Hilbert space for quantum mechanics. In short, a Hilbert space is an abstract vector space that is complete with inner products defined [1]. Examples ======== >>> from sympy.physics.quantum.hilbert import HilbertSpace >>> hs = HilbertSpace() >>> hs H References ========== [1] http://en.wikipedia.org/wiki/Hilbert_space """ def __new__(cls): obj = Basic.__new__(cls, **{'commutative': False}) return obj @property def dimension(self): """Return the Hilbert dimension of the space.""" raise NotImplementedError('This Hilbert space has no dimension.') def __add__(self, other): return DirectSumHilbertSpace(self, other) def __radd__(self, other): return DirectSumHilbertSpace(other, self) def __mul__(self, other): return TensorProductHilbertSpace(self, other) def __rmul__(self, other): return TensorProductHilbertSpace(other, self) def __pow__(self, other, mod=None): if mod is not None: raise ValueError('The third argument to __pow__ is not supported\ for Hilbert spaces.') return TensorPowerHilbertSpace(self, other) def __contains__(self, other): """Is the operator or state in this Hilbert space. This is checked by comparing the classes of the Hilbert spaces, not the instances. This is to allow Hilbert Spaces with symbolic dimensions. """ if other.hilbert_space.__class__ == self.__class__: return True else: return False def _sympystr(self, printer, *args): return u'H' def _pretty(self, printer, *args): # u = u'\u2108' # script u = u'\u0048' return prettyForm(u) def _latex(self, printer, *args): return r'\mathcal{H}' class ComplexSpace(HilbertSpace): """Finite dimensional Hilbert space of complex vectors. The elements of this Hilbert space are n-dimensional complex valued vectors with the usual inner product that takes the complex conjugate of the vector on the right. A classic example of this type of Hilbert space is spin-1/2, which is ComplexSpace(2). Likewise, for spin-s, the space is ComplexSpace(2*s+1). Quantum computing with N qubits is done with the direct product space ComplexSpace(2)**N. Examples ======== >>> from sympy import symbols >>> from sympy.physics.quantum.hilbert import ComplexSpace >>> c1 = ComplexSpace(2) >>> c1 C(2) >>> c1.dimension 2 >>> n = symbols('n') >>> c2 = ComplexSpace(n) >>> c2 C(n) >>> c2.dimension n """ def __new__(cls, dimension): dimension = sympify(dimension) r = cls.eval(dimension) if isinstance(r, Basic): return r obj = Basic.__new__(cls, dimension, **{'commutative': False}) return obj @classmethod def eval(cls, dimension): if len(dimension.atoms()) == 1: if not (dimension.is_Integer and dimension > 0 or dimension is oo\ or dimension.is_Symbol): raise TypeError('The dimension of a ComplexSpace can only' 'be a positive integer, oo, or a Symbol: %r' \ % dimension) else: for dim in dimension.atoms(): if not (dim.is_Integer or dim is oo or dim.is_Symbol): raise TypeError('The dimension of a ComplexSpace can only' ' contain integers, oo, or a Symbol: %r' \ % dim) @property def dimension(self): return self.args[0] def _sympyrepr(self, printer, *args): return "%s(%s)" % (self.__class__.__name__, printer._print(self.dimension, *args)) def _sympystr(self, printer, *args): return "C(%s)" % printer._print(self.dimension, *args) def _pretty(self, printer, *args): # u = u'\u2102' # script u = u'\u0043' pform_exp = printer._print(self.dimension, *args) pform_base = prettyForm(u) return pform_base**pform_exp def _latex(self, printer, *args): return r'\mathcal{C}^{%s}' % printer._print(self.dimension, *args) class L2(HilbertSpace): """The Hilbert space of square integrable functions on an interval. An L2 object takes in a single sympy Interval argument which represents the interval its functions (vectors) are defined on. Examples ======== >>> from sympy import Interval, oo >>> from sympy.physics.quantum.hilbert import L2 >>> hs = L2(Interval(0,oo)) >>> hs L2([0, oo)) >>> hs.dimension oo >>> hs.interval [0, oo) """ def __new__(cls, interval): if not isinstance(interval, Interval): raise TypeError('L2 interval must be an Interval instance: %r'\ % interval) obj = Basic.__new__(cls, interval, **{'commutative': False}) return obj @property def dimension(self): return oo @property def interval(self): return self.args[0] def _sympyrepr(self, printer, *args): return "L2(%s)" % printer._print(self.interval, *args) def _sympystr(self, printer, *args): return "L2(%s)" % printer._print(self.interval, *args) def _pretty(self, printer, *args): pform_exp = prettyForm(u"2") pform_base = prettyForm(u"L") return pform_base**pform_exp def _latex(self, printer, *args): interval = printer._print(self.interval, *args) return r'{\mathcal{L}^2}\left( %s \right)' % interval class FockSpace(HilbertSpace): """The Hilbert space for second quantization. Technically, this Hilbert space is a infinite direct sum of direct products of single particle Hilbert spaces [1]. This is a mess, so we have a class to represent it directly. Examples ======== >>> from sympy.physics.quantum.hilbert import FockSpace >>> hs = FockSpace() >>> hs F >>> hs.dimension oo References ========== [1] http://en.wikipedia.org/wiki/Fock_space """ def __new__(cls): obj = Basic.__new__(cls, **{'commutative': False}) return obj @property def dimension(self): return oo def _sympyrepr(self, printer, *args): return "FockSpace()" def _sympystr(self, printer, *args): return "F" def _pretty(self, printer, *args): # u = u'\u2131' # script u = u'\u0046' return prettyForm(u) def _latex(self, printer, *args): return r'\mathcal{F}' class TensorProductHilbertSpace(HilbertSpace): """A tensor product of Hilbert spaces [1]. The tensor product between Hilbert spaces is represented by the operator "*" Products of the same Hilbert space will be combined into tensor powers. A TensorProductHilbertSpace object takes in an indefinite number of HilbertSpace objects as its arguments. In addition, multiplication of HilbertSpace objects will automatically return a Tensor product object. Examples ======== >>> from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace >>> from sympy import symbols >>> c = ComplexSpace(2) >>> f = FockSpace() >>> hs = c*f >>> hs C(2)*F >>> hs.dimension oo >>> list(hs.spaces) [C(2), F] >>> c1 = ComplexSpace(2) >>> n = symbols('n') >>> c2 = ComplexSpace(n) >>> hs = c1*c2 >>> hs C(2)*C(n) >>> hs.dimension 2*n References ========== [1] http://en.wikipedia.org/wiki/Hilbert_space#Tensor_products """ def __new__(cls, *args): r = cls.eval(args) if isinstance(r, Basic): return r obj = Basic.__new__(cls, *args, **{'commutative': False}) return obj @classmethod def eval(cls, args): """Evaluates the direct product.""" new_args = [] recall = False #flatten arguments for arg in args: if isinstance(arg, TensorProductHilbertSpace): new_args.extend(arg.args) recall = True elif isinstance(arg, (HilbertSpace, TensorPowerHilbertSpace)): new_args.append(arg) else: raise TypeError('Hilbert spaces can only be multiplied by\ other Hilbert spaces: %r' % arg) #combine like arguments into direct powers comb_args = [] prev_arg = None for new_arg in new_args: if prev_arg != None: if isinstance(new_arg, TensorPowerHilbertSpace) and\ isinstance(prev_arg, TensorPowerHilbertSpace) and\ new_arg.base == prev_arg.base: prev_arg = new_arg.base**(new_arg.exp+prev_arg.exp) elif isinstance(new_arg, TensorPowerHilbertSpace) and\ new_arg.base == prev_arg: prev_arg = prev_arg**(new_arg.exp+1) elif isinstance(prev_arg, TensorPowerHilbertSpace) and\ new_arg == prev_arg.base: prev_arg = new_arg**(prev_arg.exp+1) elif new_arg == prev_arg: prev_arg = new_arg**2 else: comb_args.append(prev_arg) prev_arg = new_arg elif prev_arg == None: prev_arg = new_arg comb_args.append(prev_arg) if recall: return TensorProductHilbertSpace(*comb_args) elif len(comb_args) == 1: return TensorPowerHilbertSpace(comb_args[0].base, comb_args[0].exp) else: return None @property def dimension(self): arg_list = [arg.dimension for arg in self.args] if oo in arg_list: return oo else: return reduce(lambda x,y: x*y, arg_list) @property def spaces(self): """A tuple of the Hilbert spaces in this tensor product.""" return set(self.args) def _spaces_printer(self, printer, *args): spaces_strs = [] for arg in self.args: s = printer._print(arg, *args) if isinstance(arg, DirectSumHilbertSpace): s = '(%s)' % s spaces_strs.append(s) return spaces_strs def _sympyrepr(self, printer, *args): spaces_reprs = self._spaces_printer(printer, *args) return "TensorProductHilbertSpace(%s)" % ','.join(spaces_reprs) def _sympystr(self, printer, *args): spaces_strs = self._spaces_printer(printer, *args) return '*'.join(spaces_strs) def _pretty(self, printer, *args): length = len(self.args) pform = printer._print('', *args) for i in range(length): next_pform = printer._print(self.args[i], *args) if isinstance(self.args[i], (DirectSumHilbertSpace, TensorProductHilbertSpace)): next_pform = prettyForm( *next_pform.parens(left='(', right=')') ) pform = prettyForm(*pform.right(next_pform)) if i != length-1: pform = prettyForm(*pform.right(u' ' + u'\u2a02' + u' ')) return pform def _latex(self, printer, *args): length = len(self.args) s = '' for i in range(length): arg_s = printer._print(self.args[i], *args) if isinstance(self.args[i], (DirectSumHilbertSpace, TensorProductHilbertSpace)): arg_s = r'\left(%s\right)' % arg_s s = s + arg_s if i != length-1: s = s + r'\otimes ' return s class DirectSumHilbertSpace(HilbertSpace): """A direct sum of Hilbert spaces [1]. This class uses the "+" operator to represent direct sums between different Hilbert spaces. A DirectSumHilbertSpace object takes in an indefinite number of HilbertSpace objects as its arguments. Also, addition of HilbertSpace objects will automatically return a direct sum object. Examples ======== >>> from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace >>> from sympy import symbols >>> c = ComplexSpace(2) >>> f = FockSpace() >>> hs = c+f >>> hs C(2)+F >>> hs.dimension oo >>> list(hs.spaces) [C(2), F] References ========== [1] http://en.wikipedia.org/wiki/Hilbert_space#Direct_sums """ def __new__(cls, *args): r = cls.eval(args) if isinstance(r, Basic): return r obj = Basic.__new__(cls, *args, **{'commutative': True}) return obj @classmethod def eval(cls, args): """Evaluates the direct product.""" new_args = [] recall = False #flatten arguments for arg in args: if isinstance(arg, DirectSumHilbertSpace): new_args.extend(arg.args) recall = True elif isinstance(arg, HilbertSpace): new_args.append(arg) else: raise TypeError('Hilbert spaces can only be summed with other\ Hilbert spaces: %r' % arg) if recall: return DirectSumHilbertSpace(*new_args) else: return None @property def dimension(self): arg_list = [arg.dimension for arg in self.args] if oo in arg_list: return oo else: return reduce(lambda x,y: x+y, arg_list) @property def spaces(self): """A tuple of the Hilbert spaces in this direct sum.""" return set(self.args) def _sympyrepr(self, printer, *args): spaces_reprs = [printer._print(arg, *args) for arg in self.args] return "DirectSumHilbertSpace(%s)" % ','.join(spaces_reprs) def _sympystr(self, printer, *args): spaces_strs = [printer._print(arg, *args) for arg in self.args] return '+'.join(spaces_strs) def _pretty(self, printer, *args): length = len(self.args) pform = printer._print('', *args) for i in range(length): next_pform = printer._print(self.args[i], *args) if isinstance(self.args[i], (DirectSumHilbertSpace, TensorProductHilbertSpace)): next_pform = prettyForm( *next_pform.parens(left='(', right=')') ) pform = prettyForm(*pform.right(next_pform)) if i != length-1: pform = prettyForm(*pform.right(u' ' + u'\u2295' + u' ')) return pform def _latex(self, printer, *args): length = len(self.args) s = '' for i in range(length): arg_s = printer._print(self.args[i], *args) if isinstance(self.args[i], (DirectSumHilbertSpace, TensorProductHilbertSpace)): arg_s = r'\left(%s\right)' % arg_s s = s + arg_s if i != length-1: s = s + r'\oplus ' return s class TensorPowerHilbertSpace(HilbertSpace): """An exponentiated Hilbert space [1]. Tensor powers (repeated tensor products) are represented by the operator "**" Identical Hilbert spaces that are multiplied together will be automatically combined into a single tensor power object. Any Hilbert space, product, or sum may be raised to a tensor power. The TensorPowerHilbertSpace takes two arguments: the Hilbert space; and the tensor power (number). Examples ======== >>> from sympy.physics.quantum.hilbert import ComplexSpace, FockSpace >>> from sympy import symbols >>> n = symbols('n') >>> c = ComplexSpace(2) >>> hs = c**n >>> hs C(2)**n >>> hs.dimension 2**n >>> c = ComplexSpace(2) >>> c*c C(2)**2 >>> f = FockSpace() >>> c*f*f C(2)*F**2 References ========== [1] http://en.wikipedia.org/wiki/Hilbert_space#Tensor_products """ def __new__(cls, *args): r = cls.eval(args) if isinstance(r, Basic): return r return Basic.__new__(cls, *r, **{'commutative': False}) @classmethod def eval(cls, args): new_args = args[0], sympify(args[1]) exp = new_args[1] #simplify hs**1 -> hs if exp == 1: return args[0] #simplify hs**0 -> 1 if exp == 0: return sympify(1) #check (and allow) for hs**(x+42+y...) case if len(exp.atoms()) == 1: if not (exp.is_Integer and exp >= 0 or exp.is_Symbol): raise ValueError('Hilbert spaces can only be raised to\ positive integers or Symbols: %r' % exp) else: for power in exp.atoms(): if not (power.is_Integer or power.is_Symbol): raise ValueError('Tensor powers can only contain integers\ or Symbols: %r' % power) return new_args @property def base(self): return self.args[0] @property def exp(self): return self.args[1] @property def dimension(self): if self.base.dimension == oo: return oo else: return self.base.dimension**self.exp def _sympyrepr(self, printer, *args): return "TensorPowerHilbertSpace(%s,%s)" % (printer._print(self.base,\ *args), printer._print(self.exp, *args)) def _sympystr(self, printer, *args): return "%s**%s" % (printer._print(self.base, *args),\ printer._print(self.exp, *args)) def _pretty(self, printer, *args): pform_exp = printer._print(self.exp, *args) pform_exp = prettyForm(*pform_exp.left(prettyForm(u'\u2a02'))) pform_base = printer._print(self.base, *args) return pform_base**pform_exp def _latex(self, printer, *args): base = printer._print(self.base, *args) exp = printer._print(self.exp, *args) return r'{%s}^{\otimes %s}' % (base, exp) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/shor.py0000644000175000017500000001444012014170666025603 0ustar georgeskgeorgesk"""Shor's algorithm and helper functions. Todo: * Get the CMod gate working again using the new Gate API. * Fix everything. * Update docstrings and reformat. * Remove print statements. We may want to think about a better API for this. """ import math import random from sympy import Mul from sympy import log, sqrt from sympy.core.numbers import igcd from sympy.physics.quantum.gate import Gate from sympy.physics.quantum.qubit import Qubit, measure_partial_oneshot from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qft import QFT from sympy.physics.quantum.qexpr import QuantumError class OrderFindingException(QuantumError): pass class CMod(Gate): """A controlled mod gate. This is black box controlled Mod function for use by shor's algorithm. TODO implement a decompose property that returns how to do this in terms of elementary gates """ @classmethod def _eval_args(cls, args): # t = args[0] # a = args[1] # N = args[2] raise NotImplementedError('The CMod gate has not been completed.') @property def t(self): """Size of 1/2 input register. First 1/2 holds output.""" return self.label[0] @property def a(self): """Base of the controlled mod function.""" return self.label[1] @property def N(self): """N is the type of modular arithmetic we are doing.""" return self.label[2] def _apply_operator_Qubit(self, qubits, **options): """ This directly calculates the controlled mod of the second half of the register and puts it in the second This will look pretty when we get Tensor Symbolically working """ n = 1 k = 0 # Determine the value stored in high memory. for i in range(self.t): k = k + n*qubits[self.t+i] n = n*2 # The value to go in low memory will be out. out = int(self.a**k%self.N) # Create array for new qbit-ket which will have high memory unaffected outarray = list(qubits.args[0][0:self.t]) # Place out in low memory for i in reversed(range(self.t)): outarray.append((out>>i)&1) return Qubit(*outarray) def shor(N): """This function implements Shor's factoring algorithm on the Integer N The algorithm starts by picking a random number (a) and seeing if it is coprime with N. If it isn't, then the gcd of the two numbers is a factor and we are done. Otherwise, it begins the period_finding subroutine which finds the period of a in modulo N arithmetic. This period, if even, can be used to calculate factors by taking a**(r/2)-1 and a**(r/2)+1. These values are returned. """ a = random.randrange(N-2)+2 if igcd(N,a) != 1: print "got lucky with rand" return igcd(N,a) print "a= ",a print "N= ",N r = period_find(a,N) print "r= ",r if r%2 == 1: print "r is not even, begin again" shor(N) answer = (igcd(a**(r/2)-1, N), igcd(a**(r/2)+1, N)) return answer def arr(num, t): """This function returns num as an array in binary It does this with the 0th digit being on the right >>> from sympy.physics.quantum.shor import arr >>> arr(5, 4) [0, 1, 0, 1] """ binary_array = [] for i in reversed(range(t)): binary_array.append((num>>i)&1) return binary_array def getr(x, y, N): fraction = continued_fraction(x,y) # Now convert into r total = ratioize(fraction, N) return total def ratioize(list, N): if list[0] > N: return 0 if len(list) == 1: return list[0] return list[0] + ratioize(list[1:], N) def continued_fraction(x, y): """This applies the continued fraction expansion to two numbers x/y x is the numerator and y is the denominator >>> from sympy.physics.quantum.shor import continued_fraction >>> continued_fraction(3, 8) [0, 2, 1, 2] """ x = int(x) y = int(y) temp = x//y if temp*y == x: return [temp,] list = continued_fraction(y, x-temp*y) list.insert(0, temp) return list def period_find(a, N): """Finds the period of a in modulo N arithmetic This is quantum part of Shor's algorithm.It takes two registers, puts first in superposition of states with Hadamards so: |k>|0> with k being all possible choices. It then does a controlled mod and a QFT to determine the order of a. """ epsilon = .5 #picks out t's such that maintains accuracy within epsilon t = int(2*math.ceil(log(N,2))) # make the first half of register be 0's |000...000> start = [0 for x in range(t)] #Put second half into superposition of states so we have |1>x|0> + |2>x|0> + ... |k>x>|0> + ... + |2**n-1>x|0> factor = 1/sqrt(2**t) qubits = 0 for i in range(2**t): qbitArray = arr(i, t) + start qubits = qubits + Qubit(*qbitArray) circuit = (factor*qubits).expand() #Controlled second half of register so that we have: # |1>x|a**1 %N> + |2>x|a**2 %N> + ... + |k>x|a**k %N >+ ... + |2**n-1=k>x|a**k % n> circuit = CMod(t,a,N)*circuit #will measure first half of register giving one of the a**k%N's circuit = qapply(circuit) print "controlled Mod'd" for i in range(t): circuit = measure_partial_oneshot(circuit, i) # circuit = measure(i)*circuit # circuit = qapply(circuit) print "measured 1" #Now apply Inverse Quantum Fourier Transform on the second half of the register circuit = qapply(QFT(t, t*2).decompose()*circuit, floatingPoint = True) print "QFT'd" for i in range(t): circuit = measure_partial_oneshot(circuit, i+t) # circuit = measure(i+t)*circuit # circuit = qapply(circuit) print circuit if isinstance(circuit, Qubit): register = circuit elif isinstance(circuit, Mul): register = circuit.args[-1] else: register = circuit.args[-1].args[-1] print register n = 1 answer = 0 for i in range(len(register)/2): answer += n*register[i+t] n = n<<1 if answer == 0: raise OrderFindingException("Order finder returned 0. Happens with chance %f" % epsilon) #turn answer into r using continued fractions g = getr(answer, 2**t, N) print g return g wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/state.py0000644000175000017500000003471512014170666025757 0ustar georgeskgeorgesk"""Dirac notation for states.""" from sympy import Expr from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.qexpr import ( QExpr, dispatch_method ) __all__ = [ 'KetBase', 'BraBase', 'StateBase', 'State', 'Ket', 'Bra', 'TimeDepState', 'TimeDepBra', 'TimeDepKet' ] #----------------------------------------------------------------------------- # States, bras and kets. #----------------------------------------------------------------------------- # LIGHT VERTICAL BAR _straight_bracket = u"\u2758" # MATHEMATICAL LEFT ANGLE BRACKET _lbracket = u"\u27E8" _rbracket = u"\u27E9" # Other options for unicode printing of <, > and | for Dirac notation. # VERTICAL LINE # _straight_bracket = u"\u007C" # LEFT-POINTING ANGLE BRACKET # _lbracket = u"\u2329" # _rbracket = u"\u232A" # LEFT ANGLE BRACKET # _lbracket = u"\u3008" # _rbracket = u"\u3009" class StateBase(QExpr): """Abstract base class for general abstract states in quantum mechanics. All other state classes defined will need to inherit from this class. It carries the basic structure for all other states such as dual, _eval_dagger and label. This is an abstract base class and you should not instantiate it directly, instead use State. """ #------------------------------------------------------------------------- # Dagger/dual #------------------------------------------------------------------------- @property def dual(self): """Return the dual state of this one.""" return self.dual_class._new_rawargs(self.hilbert_space, *self.args) @property def dual_class(self): """Return the class used to construt the dual.""" raise NotImplementedError( 'dual_class must be implemented in a subclass' ) def _eval_dagger(self): """Compute the dagger of this state using the dual.""" return self.dual #------------------------------------------------------------------------- # Printing #------------------------------------------------------------------------- def _print_contents(self, printer, *args): label = self._print_label(printer, *args) return '%s%s%s' % (self.lbracket, label, self.rbracket) def _print_contents_pretty(self, printer, *args): from sympy.printing.pretty.stringpict import prettyForm pform = self._print_label_pretty(printer, *args) pform = prettyForm(*pform.left((self.lbracket_pretty))) pform = prettyForm(*pform.right((self.rbracket_pretty))) return pform def _print_contents_latex(self, printer, *args): label = self._print_label_latex(printer, *args) # The extra {} brackets are needed to get matplotlib's latex # rendered to render this properly. return '{%s%s%s}' % (self.lbracket_latex, label, self.rbracket_latex) class KetBase(StateBase): """Base class for Kets. This class defines the dual property and the brackets for printing. This is an abstract base class and you should not instantiate it directly, instead use Ket. """ lbracket = '|' rbracket = '>' lbracket_pretty = prettyForm(_straight_bracket) rbracket_pretty = prettyForm(_rbracket) lbracket_latex = r'\left|' rbracket_latex = r'\right\rangle ' @property def dual_class(self): return BraBase def __mul__(self, other): """KetBase*other""" from sympy.physics.quantum.operator import OuterProduct if isinstance(other, BraBase): return OuterProduct(self, other) else: return Expr.__mul__(self, other) def __rmul__(self, other): """other*KetBase""" from sympy.physics.quantum.innerproduct import InnerProduct if isinstance(other, BraBase): return InnerProduct(other, self) else: return Expr.__rmul__(self, other) #------------------------------------------------------------------------- # _eval_* methods #------------------------------------------------------------------------- def _eval_innerproduct(self, bra, **hints): """Evaluate the inner product betweeen this ket and a bra. This is called to compute , where the ket is ``self``. This method will dispatch to sub-methods having the format:: def _eval_innerproduct_BraClass(self, **hints): Subclasses should define these methods (one for each BraClass) to teach the ket how to take inner products with bras. """ return dispatch_method(self, '_eval_innerproduct', bra, **hints) def _apply_operator(self, op, **options): """Apply an Operator to this Ket. This method will dispatch to methods having the format:: def _apply_operator_OperatorName(op, **options): Subclasses should define these methods (one for each OperatorName) to teach the Ket how operators act on it. Parameters ========== op : Operator The Operator that is acting on the Ket. options : dict A dict of key/value pairs that control how the operator is applied to the Ket. """ return dispatch_method(self, '_apply_operator', op, **options) class BraBase(StateBase): """Base class for Bras. This class defines the dual property and the brackets for printing. This is an abstract base class and you should not instantiate it directly, instead use Bra. """ lbracket = '<' rbracket = '|' lbracket_pretty = prettyForm(_lbracket) rbracket_pretty = prettyForm(_straight_bracket) lbracket_latex = r'\left\langle ' rbracket_latex = r'\right|' @property def dual_class(self): return KetBase def __mul__(self, other): """BraBase*other""" from sympy.physics.quantum.innerproduct import InnerProduct if isinstance(other, KetBase): return InnerProduct(self, other) else: return Expr.__mul__(self, other) def __rmul__(self, other): """other*BraBase""" from sympy.physics.quantum.operator import OuterProduct if isinstance(other, KetBase): return OuterProduct(other, self) else: return Expr.__rmul__(self, other) def _represent(self, **options): """A default represent that uses the Ket's version.""" from sympy.physics.quantum.dagger import Dagger return Dagger(self.dual._represent(**options)) class State(StateBase): """General abstract quantum state used as a base class for Ket and Bra.""" pass class Ket(State, KetBase): """A general time-independent Ket in quantum mechanics. Inherits from State and KetBase. This class should be used as the base class for all physical, time-independent Kets in a system. This class and its subclasses will be the main classes that users will use for expressing Kets in Dirac notation. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the ket. This will usually be its symbol or its quantum numbers. For time-dependent state, this will include the time. Examples ======== Create a simple Ket and looking at its properties:: >>> from sympy.physics.quantum import Ket, Bra >>> from sympy import symbols, I >>> k = Ket('psi') >>> k |psi> >>> k.hilbert_space H >>> k.is_commutative False >>> k.label (psi,) Ket's know about their associated bra:: >>> k.dual >> k.dual_class Take a linear combination of two kets:: >>> k0 = Ket(0) >>> k1 = Ket(1) >>> 2*I*k0 - 4*k1 2*I*|0> - 4*|1> Compound labels are passed as tuples:: >>> n, m = symbols('n,m') >>> k = Ket(n,m) >>> k |nm> References ========== [1] http://en.wikipedia.org/wiki/Bra-ket_notation """ @property def dual_class(self): return Bra class Bra(State, BraBase): """A general time-independent Bra in quantum mechanics. Inherits from State and BraBase. A Bra is the dual of a Ket [1]. This class and its subclasses will be the main classes that users will use for expressing Bras in Dirac notation. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the ket. This will usually be its symbol or its quantum numbers. For time-dependent state, this will include the time. Examples ======== Create a simple Bra and look at its properties:: >>> from sympy.physics.quantum import Ket, Bra >>> from sympy import symbols, I >>> b = Bra('psi') >>> b >> b.hilbert_space H >>> b.is_commutative False Bra's know about their dual Ket's:: >>> b.dual |psi> >>> b.dual_class Like Kets, Bras can have compound labels and be manipulated in a similar manner:: >>> n, m = symbols('n,m') >>> b = Bra(n,m) - I*Bra(m,n) >>> b -I*>> b.subs(n,m) >> from sympy.physics.quantum import TimeDepKet >>> k = TimeDepKet('psi', 't') >>> k |psi;t> >>> k.time t >>> k.label (psi,) >>> k.hilbert_space H TimeDepKets know about their dual bra:: >>> k.dual >> k.dual_class """ @property def dual_class(self): return TimeDepBra class TimeDepBra(TimeDepState, BraBase): """General time-dependent Bra in quantum mechanics. This inherits from TimeDepState and BraBase and is the main class that should be used for Bras that vary with time. Its dual is a TimeDepBra. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the ket. This will usually be its symbol or its quantum numbers. For time-dependent state, this will include the time as the final argument. Examples ======== >>> from sympy.physics.quantum import TimeDepBra >>> from sympy import symbols, I >>> b = TimeDepBra('psi', 't') >>> b >> b.time t >>> b.label (psi,) >>> b.hilbert_space H >>> b.dual |psi;t> """ @property def dual_class(self): return TimeDepKet wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/dagger.py0000644000175000017500000001056412014170666026064 0ustar georgeskgeorgesk"""Hermitian conjugation.""" from sympy import Expr, sympify, Add, Mul, Matrix, Pow from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.matrixutils import ( numpy_ndarray, scipy_sparse_matrix, matrix_dagger ) __all__ = [ 'Dagger' ] class Dagger(Expr): """General Hermitian conjugate operation. For matrices this operation is equivalent to transpose and complex conjugate [1]. Parameters ========== arg : Expr The sympy expression that we want to take the dagger of. Examples ======== Daggering various quantum objects: >>> from sympy.physics.quantum.dagger import Dagger >>> from sympy.physics.quantum.state import Ket, Bra >>> from sympy.physics.quantum.operator import Operator >>> Dagger(Ket('psi')) >> Dagger(Bra('phi')) |phi> >>> Dagger(Operator('A')) Dagger(A) Inner and outer products:: >>> from sympy.physics.quantum import InnerProduct, OuterProduct >>> Dagger(InnerProduct(Bra('a'), Ket('b'))) >>> Dagger(OuterProduct(Ket('a'), Bra('b'))) |b>>> A = Operator('A') >>> B = Operator('B') >>> Dagger(A*B) Dagger(B)*Dagger(A) >>> Dagger(A+B) Dagger(A) + Dagger(B) >>> Dagger(A**2) Dagger(A)**2 Dagger also seamlessly handles complex numbers and matrices:: >>> from sympy import Matrix, I >>> m = Matrix([[1,I],[2,I]]) >>> m [1, I] [2, I] >>> Dagger(m) [ 1, 2] [-I, -I] References ========== [1] http://en.wikipedia.org/wiki/Hermitian_transpose """ def __new__(cls, arg, **old_assumptions): # Return the dagger of a sympy Matrix immediately. if isinstance(arg, (Matrix, numpy_ndarray, scipy_sparse_matrix)): return matrix_dagger(arg) arg = sympify(arg) r = cls.eval(arg) if isinstance(r, Expr): return r #make unevaluated dagger commutative or non-commutative depending on arg if arg.is_commutative: obj = Expr.__new__(cls, arg, **{'commutative':True}) else: obj = Expr.__new__(cls, arg, **{'commutative':False}) if isinstance(obj, QExpr): obj.hilbert_space = arg.hilbert_space return obj @classmethod def eval(cls, arg): """Evaluates the Dagger instance.""" from sympy.physics.quantum.operator import Operator try: d = arg._eval_dagger() except (NotImplementedError, AttributeError): if isinstance(arg, Expr): if isinstance(arg, Operator): # Operator without _eval_dagger return None if arg.is_Add: return Add(*[Dagger(i) for i in arg.args]) if arg.is_Mul: return Mul(*[Dagger(i) for i in reversed(arg.args)]) if arg.is_Pow: return Pow(Dagger(arg.args[0]),arg.args[1]) else: if arg.is_Number or arg.is_Function or arg.is_Derivative\ or arg.is_Integer or arg.is_NumberSymbol\ or arg.is_complex or arg.is_integer\ or arg.is_real or arg.is_number: return arg.conjugate() else: return None else: return None else: return d def _eval_subs(self, old, new): r = Dagger(self.args[0].subs(old, new)) return r def _eval_dagger(self): return self.args[0] def _sympyrepr(self, printer, *args): arg0 = printer._print(self.args[0], *args) return '%s(%s)' % (self.__class__.__name__, arg0) def _sympystr(self, printer, *args): arg0 = printer._print(self.args[0], *args) return '%s(%s)' % (self.__class__.__name__, arg0) def _pretty(self, printer, *args): from sympy.printing.pretty.stringpict import prettyForm pform = printer._print(self.args[0], *args) pform = pform**prettyForm(u'\u2020') return pform def _latex(self, printer, *args): arg = printer._print(self.args[0]) return '%s^{\\dag}' % arg wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/qapply.py0000644000175000017500000001223012014170666026131 0ustar georgeskgeorgesk"""Logic for applying operators to states. Todo: * Sometimes the final result needs to be expanded, we should do this by hand. """ from sympy import Add, Mul, Pow from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.operator import OuterProduct from sympy.physics.quantum.state import KetBase, BraBase from sympy.physics.quantum.tensorproduct import TensorProduct __all__ = [ 'qapply' ] #----------------------------------------------------------------------------- # Main code #----------------------------------------------------------------------------- def qapply(e, **options): """Apply operators to states in a quantum expression. Parameters ========== e : Expr The expression containing operators and states. This expression tree will be walked to find operators acting on states symbolically. options : dict A dict of key/value pairs that determine how the operator actions are carried out. The following options are valid: * ``dagger``: try to apply Dagger operators to the left (default: False). * ``ip_doit``: call ``.doit()`` in inner products when they are encountered (default: True). Returns ======= e : Expr The original expression, but with the operators applied to states. """ dagger = options.get('dagger', False) if e == 0: return 0 # This may be a bit aggressive but ensures that everything gets expanded # to its simplest form before trying to apply operators. This includes # things like (A+B+C)*|a> and A*(|a>+|b>) and all Commutators and # TensorProducts. The only problem with this is that if we can't apply # all the Operators, we have just expanded everything. # TODO: don't expand the scalars in front of each Mul. e = e.expand(commutator=True, tensorproduct=True) # If we just have a raw ket, return it. if isinstance(e, KetBase): return e # We have an Add(a, b, c, ...) and compute # Add(qapply(a), qapply(b), ...) elif isinstance(e, Add): result = 0 for arg in e.args: result += qapply(arg, **options) return result # For a raw TensorProduct, call qapply on its args. elif isinstance(e, TensorProduct): return TensorProduct(*[qapply(t, **options) for t in e.args]) # For a Pow, call qapply on its base. elif isinstance(e, Pow): return qapply(e.base, **options)**e.exp # We have a Mul where there might be actual operators to apply to kets. elif isinstance(e, Mul): result = qapply_Mul(e, **options) if result == e and dagger: return Dagger(qapply_Mul(Dagger(e), **options)) else: return result # In all other cases (State, Operator, Pow, Commutator, InnerProduct, # OuterProduct) we won't ever have operators to apply to kets. else: return e def qapply_Mul(e, **options): ip_doit = options.get('ip_doit', True) args = list(e.args) # If we only have 0 or 1 args, we have nothing to do and return. if len(args) <= 1: return e rhs = args.pop() lhs = args.pop() # Make sure we have two non-commutative objects before proceeding. if rhs.is_commutative or lhs.is_commutative: return e # For a Pow with an integer exponent, apply one of them and reduce the # exponent by one. if isinstance(lhs, Pow) and lhs.exp.is_Integer: args.append(lhs.base**(lhs.exp-1)) lhs = lhs.base # Pull OuterProduct apart if isinstance(lhs, OuterProduct): args.append(lhs.ket) lhs = lhs.bra # Call .doit() on Commutator/AntiCommutator. if isinstance(lhs, (Commutator, AntiCommutator)): comm = lhs.doit() if isinstance(comm, Add): return qapply( e._new_rawargs(*(args + [comm.args[0], rhs])) +\ e._new_rawargs(*(args + [comm.args[1], rhs])), **options ) else: return qapply(e._new_rawargs(*args)*comm*rhs, **options) # Now try to actually apply the operator and build an inner product. try: result = lhs._apply_operator(rhs, **options) except (NotImplementedError, AttributeError): try: result = rhs._apply_operator(lhs, **options) except (NotImplementedError, AttributeError): if isinstance(lhs, BraBase) and isinstance(rhs, KetBase): result = InnerProduct(lhs, rhs) if ip_doit: result = result.doit() else: result = None # TODO: I may need to expand before returning the final result. if result == 0: return 0 elif result is None: return qapply_Mul(e._new_rawargs(*(args+[lhs])), **options)*rhs elif isinstance(result, InnerProduct): return result*qapply_Mul(e._new_rawargs(*args), **options) else: # result is a scalar times a Mul, Add or TensorProduct return qapply(e._new_rawargs(*args)*result, **options) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/represent.py0000644000175000017500000001310112014170666026630 0ustar georgeskgeorgesk"""Logic for representing operators in state in various bases. TODO: * Get represent working with continuous hilbert spaces. * Document default basis functionality. """ from sympy import Add, Mul, Pow, I, Expr from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.matrixutils import flatten_scalar __all__ = [ 'represent' ] #----------------------------------------------------------------------------- # Represent #----------------------------------------------------------------------------- def _sympy_to_scalar(e): """Convert from a sympy scalar to a Python scalar.""" if isinstance(e, Expr): if e.is_Integer: return int(e) elif e.is_Float: return float(e) elif e.is_Rational: return float(e) elif e.is_Number or e.is_NumberSymbol or e == I: return complex(e) raise TypeError('Expected number, got: %r' % e) def represent(expr, **options): """Represent the quantum expression in the given basis. In quantum mechanics abstract states and operators can be represented in various basis sets. Under this operation the follow transforms happen: * Ket -> column vector or function * Bra -> row vector of function * Operator -> matrix or differential operator This function is the top-level interface for this action. This function walks the sympy expression tree looking for ``QExpr`` instances that have a ``_represent`` method. This method is then called and the object is replaced by the representation returned by this method. By default, the ``_represent`` method will dispatch to other methods that handle the representation logic for a particular basis set. The naming convention for these methods is the following:: def _represent_FooBasis(self, e, basis, **options) This function will have the logic for representing instances of its class in the basis set having a class named ``FooBasis``. Parameters ========== expr : Expr The expression to represent. basis : Operator, basis set An object that contains the information about the basis set. If an operator is used, the basis is assumed to be the orthonormal eigenvectors of that operator. In general though, the basis argument can be any object that contains the basis set information. options : dict Key/value pairs of options that are passed to the underlying method that does finds the representation. These options can be used to control how the representation is done. For example, this is where the size of the basis set would be set. Returns ======= e : Expr The sympy expression of the represented quantum expression. Examples ======== Here we subclass ``Operator`` and ``Ket`` to create the z-spin operator and its spin 1/2 up eigenstate. By definining the ``_represent_SzOp`` method, the ket can be represented in the z-spin basis. >>> from sympy.physics.quantum import Operator, represent, Ket >>> from sympy import Matrix >>> class SzUpKet(Ket): ... def _represent_SzOp(self, basis, **options): ... return Matrix([1,0]) ... >>> class SzOp(Operator): ... pass ... >>> sz = SzOp('Sz') >>> up = SzUpKet('up') >>> represent(up, basis=sz) [1] [0] """ format = options.get('format', 'sympy') if isinstance(expr, QExpr): return expr._represent(**options) elif isinstance(expr, Add): result = represent(expr.args[0], **options) for args in expr.args[1:]: # scipy.sparse doesn't support += so we use plain = here. result = result + represent(args, **options) return result elif isinstance(expr, Pow): exp = expr.exp if format == 'numpy' or format == 'scipy.sparse': exp = _sympy_to_scalar(exp) return represent(expr.base, **options)**exp elif isinstance(expr, TensorProduct): new_args = [represent(arg, **options) for arg in expr.args] return TensorProduct(*new_args) elif isinstance(expr, Dagger): return Dagger(represent(expr.args[0], **options)) elif isinstance(expr, Commutator): A = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A*B - B*A elif isinstance(expr, AntiCommutator): A = represent(expr.args[0], **options) B = represent(expr.args[1], **options) return A*B + B*A elif isinstance(expr, InnerProduct): return represent(Mul(expr.bra,expr.ket), **options) elif not isinstance(expr, Mul): # For numpy and scipy.sparse, we can only handle numerical prefactors. if format == 'numpy' or format == 'scipy.sparse': return _sympy_to_scalar(expr) return expr if not isinstance(expr, Mul): raise TypeError('Mul expected, got: %r' % expr) result = represent(expr.args[-1], **options) for arg in reversed(expr.args[:-1]): result = represent(arg, **options)*result # All three matrix formats create 1 by 1 matrices when inner products of # vectors are taken. In these cases, we simply return a scalar. result = flatten_scalar(result) return result wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/gate.py0000644000175000017500000011427612014170666025560 0ustar georgeskgeorgesk"""An implementation of gates that act on qubits. Gates are unitary operators that act on the space of qubits. Medium Term Todo: * Optimize Gate._apply_operators_Qubit to remove the creation of many intermediate Qubit objects. * Add commutation relationships to all operators and use this in gate_sort. * Fix gate_sort and gate_simp. * Get multi-target UGates plotting properly. * Get UGate to work with either sympy/numpy matrices and output either format. This should also use the matrix slots. """ from itertools import chain import random from sympy import Mul, Pow, Integer, Matrix, Rational, Tuple, I, sqrt, Add from sympy.core.numbers import Number from sympy.core.compatibility import is_sequence from sympy.printing.pretty.stringpict import prettyForm, stringPict from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.qexpr import QuantumError from sympy.physics.quantum.hilbert import ComplexSpace from sympy.physics.quantum.operator import UnitaryOperator, Operator, HermitianOperator from sympy.physics.quantum.matrixutils import ( matrix_tensor_product, matrix_eye ) from sympy.physics.quantum.matrixcache import matrix_cache __all__ = [ 'Gate', 'CGate', 'UGate', 'OneQubitGate', 'TwoQubitGate', 'IdentityGate', 'HadamardGate', 'XGate', 'YGate', 'ZGate', 'TGate', 'PhaseGate', 'SwapGate', 'CNotGate', # Aliased gate names 'CNOT', 'SWAP', 'H', 'X', 'Y', 'Z', 'T', 'S', 'Phase', 'normalized', 'gate_sort', 'gate_simp', 'random_circuit', ] sqrt2_inv = Pow(2, Rational(-1,2), evaluate=False) #----------------------------------------------------------------------------- # Gate Super-Classes #----------------------------------------------------------------------------- _normalized = True def normalized(normalize): """Should Hadamard gates be normalized by a 1/sqrt(2). This is a global setting that can be used to simplify the look of various expressions, by leaving of the leading 1/sqrt(2) of the Hadamard gate. Parameters ---------- normalize : bool Should the Hadamard gate include the 1/sqrt(2) normalization factor? When True, the Hadamard gate will have the 1/sqrt(2). When False, the Hadamard gate will not have this factor. """ global _normalized _normalized = normalize def _validate_targets_controls(tandc): tandc = list(tandc) # Check for integers for bit in tandc: if not bit.is_Integer: raise TypeError('Integer expected, got: %r' % tandc[bit]) # Detect duplicates if len(list(set(tandc))) != len(tandc): raise QuantumError( 'Target/control qubits in a gate cannot be duplicated' ) class Gate(UnitaryOperator): """Non-controlled unitary gate operator that acts on qubits. This is a general abstract gate that needs to be subclassed to do anything useful. Parameters ---------- label : tuple, int A list of the target qubits (as ints) that the gate will apply to. Examples -------- """ _label_separator = ',' gate_name = u'G' gate_name_latex = u'G' #------------------------------------------------------------------------- # Initialization/creation #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): args = UnitaryOperator._eval_args(args) _validate_targets_controls(args) return args @classmethod def _eval_hilbert_space(cls, args): """This returns the smallest possible Hilbert space.""" return ComplexSpace(2)**(max(args)+1) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def nqubits(self): """The total number of qubits this gate acts on. For controlled gate subclasses this includes both target and control qubits, so that, for examples the CNOT gate acts on 2 qubits. """ return len(self.targets) @property def min_qubits(self): """The minimum number of qubits this gate needs to act on.""" return max(self.targets)+1 @property def targets(self): """A tuple of target qubits.""" return self.label @property def gate_name_plot(self): return r'$%s$' % self.gate_name_latex #------------------------------------------------------------------------- # Gate methods #------------------------------------------------------------------------- def get_target_matrix(self, format='sympy'): """The matrix rep. of the target part of the gate. Parameters ---------- format : str The format string ('sympy','numpy', etc.) """ raise NotImplementedError('get_target_matrix is not implemented in Gate.') #------------------------------------------------------------------------- # Apply #------------------------------------------------------------------------- def _apply_operator_IntQubit(self, qubits, **options): """Redirect an apply from IntQubit to Qubit""" return self._apply_operator_Qubit(qubits, **options) def _apply_operator_Qubit(self, qubits, **options): """Apply this gate to a Qubit.""" # Check number of qubits this gate acts on. if qubits.nqubits < self.min_qubits: raise QuantumError( 'Gate needs a minimum of %r qubits to act on, got: %r' %\ (self.min_qubits, qubits.nqubits) ) # If the controls are not met, just return if isinstance(self, CGate): if not self.eval_controls(qubits): return qubits targets = self.targets target_matrix = self.get_target_matrix(format='sympy') # Find which column of the target matrix this applies to. column_index = 0 n = 1 for target in targets: column_index += n*qubits[target] n = n<<1 column = target_matrix[:,int(column_index)] # Now apply each column element to the qubit. result = 0 for index in range(column.rows): # TODO: This can be optimized to reduce the number of Qubit # creations. We should simply manipulate the raw list of qubit # values and then build the new Qubit object once. # Make a copy of the incoming qubits. new_qubit = qubits.__class__(*qubits.args) # Flip the bits that need to be flipped. for bit in range(len(targets)): if new_qubit[targets[bit]] != (index>>bit)&1: new_qubit = new_qubit.flip(targets[bit]) # The value in that row and column times the flipped-bit qubit # is the result for that part. result += column[index]*new_qubit return result #------------------------------------------------------------------------- # Represent #------------------------------------------------------------------------- def _represent_default_basis(self, **options): return self._represent_ZGate(None, **options) def _represent_ZGate(self, basis, **options): format = options.get('format','sympy') nqubits = options.get('nqubits',0) if nqubits == 0: raise QuantumError('The number of qubits must be given as nqubits.') # Make sure we have enough qubits for the gate. if nqubits < self.min_qubits: raise QuantumError( 'The number of qubits %r is too small for the gate.' % nqubits ) target_matrix = self.get_target_matrix(format) targets = self.targets if isinstance(self, CGate): controls = self.controls else: controls = [] m = represent_zbasis( controls, targets, target_matrix, nqubits, format ) return m #------------------------------------------------------------------------- # Print methods #------------------------------------------------------------------------- def _print_contents(self, printer, *args): label = self._print_label(printer, *args) return '%s(%s)' % (self.gate_name, label) def _print_contents_pretty(self, printer, *args): a = stringPict(unicode(self.gate_name)) b = self._print_label_pretty(printer, *args) return self._print_subscript_pretty(a, b) def _print_contents_latex(self, printer, *args): label = self._print_label(printer, *args) return '%s_{%s}' % (self.gate_name_latex, label) def plot_gate(self, axes, gate_idx, gate_grid, wire_grid): raise NotImplementedError('plot_gate is not implemented.') class CGate(Gate): """A general unitary gate with control qubits. A general control gate applies a target gate to a set of targets if all of the control qubits have a particular values (set by ``CGate.control_value``). Parameters ---------- label : tuple The label in this case has the form (controls, gate), where controls is a tuple/list of control qubits (as ints) and gate is a ``Gate`` instance that is the target operator. Examples -------- """ gate_name = u'C' gate_name_latex = u'C' # The values this class controls for. control_value = Integer(1) #------------------------------------------------------------------------- # Initialization #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): # _eval_args has the right logic for the controls argument. controls = args[0] gate = args[1] if not is_sequence(controls): controls = (controls,) controls = UnitaryOperator._eval_args(controls) _validate_targets_controls(chain(controls,gate.targets)) return (controls, gate) @classmethod def _eval_hilbert_space(cls, args): """This returns the smallest possible Hilbert space.""" return ComplexSpace(2)**max(max(args[0])+1,args[1].min_qubits) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def nqubits(self): """The total number of qubits this gate acts on. For controlled gate subclasses this includes both target and control qubits, so that, for examples the CNOT gate acts on 2 qubits. """ return len(self.targets)+len(self.controls) @property def min_qubits(self): """The minimum number of qubits this gate needs to act on.""" return max(max(self.controls),max(self.targets))+1 @property def targets(self): """A tuple of target qubits.""" return self.gate.targets @property def controls(self): """A tuple of control qubits.""" return tuple(self.label[0]) @property def gate(self): """The non-controlled gate that will be applied to the targets.""" return self.label[1] #------------------------------------------------------------------------- # Gate methods #------------------------------------------------------------------------- def get_target_matrix(self, format='sympy'): return self.gate.get_target_matrix(format) def eval_controls(self, qubit): """Return True/False to indicate if the controls are satisfied.""" return all([qubit[bit]==self.control_value for bit in self.controls]) def decompose(self, **options): """Decompose the controlled gate into CNOT and single qubits gates.""" if len(self.controls) == 1: c = self.controls[0] t = self.gate.targets[0] if isinstance(self.gate, YGate): g1 = PhaseGate(t) g2 = CNotGate(c, t) g3 = PhaseGate(t) g4 = ZGate(t) return g1*g2*g3*g4 if isinstance(self.gate, ZGate): g1 = HadamardGate(t) g2 = CNotGate(c, t) g3 = HadamardGate(t) return g1*g2*g3 else: return self #------------------------------------------------------------------------- # Print methods #------------------------------------------------------------------------- def _print_contents(self, printer, *args): controls = self._print_sequence(self.controls, ',', printer, *args) gate = printer._print(self.gate, *args) return '%s((%s),%s)' %\ (self.gate_name, controls, gate) def _print_contents_pretty(self, printer, *args): controls = self._print_sequence_pretty(self.controls, ',', printer, *args) gate = printer._print(self.gate) gate_name = stringPict(unicode(self.gate_name)) first = self._print_subscript_pretty(gate_name, controls) gate = self._print_parens_pretty(gate) final = prettyForm(*first.right((gate))) return final def _print_contents_latex(self, printer, *args): controls = self._print_sequence(self.controls, ',', printer, *args) gate = printer._print(self.gate, *args) return r'%s_{%s}{\left(%s\right)}' %\ (self.gate_name_latex, controls, gate) def plot_gate(self, circ_plot, gate_idx): min_wire = int(min(chain(self.controls, self.targets))) max_wire = int(max(chain(self.controls, self.targets))) circ_plot.control_line(gate_idx, min_wire, max_wire) for c in self.controls: circ_plot.control_point(gate_idx, int(c)) self.gate.plot_gate(circ_plot, gate_idx) class UGate(Gate): """General gate specified by a set of targets and a target matrix. Parameters ---------- label : tuple A tuple of the form (targets, U), where targets is a tuple of the target qubits and U is a unitary matrix with dimension of len(targets). """ gate_name = u'U' gate_name_latex = u'U' #------------------------------------------------------------------------- # Initialization #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): targets = args[0] if not is_sequence(targets): targets = (targets,) targets = Gate._eval_args(targets) _validate_targets_controls(targets) mat = args[1] if not isinstance(mat, Matrix): raise TypeError('Matrix expected, got: %r' % mat) dim = 2**len(targets) if not all([dim == shape for shape in mat.shape]): raise IndexError( 'Number of targets must match the matrix size: %r %r' %\ (targets, mat) ) return (targets, mat) @classmethod def _eval_hilbert_space(cls, args): """This returns the smallest possible Hilbert space.""" return ComplexSpace(2)**(max(args[0])+1) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def targets(self): """A tuple of target qubits.""" return tuple(self.label[0]) #------------------------------------------------------------------------- # Gate methods #------------------------------------------------------------------------- def get_target_matrix(self, format='sympy'): """The matrix rep. of the target part of the gate. Parameters ---------- format : str The format string ('sympy','numpy', etc.) """ return self.label[1] #------------------------------------------------------------------------- # Print methods #------------------------------------------------------------------------- def _print_contents(self, printer, *args): targets = self._print_targets(printer, *args) return '%s(%s)' % (self.gate_name, targets) def _print_contents_pretty(self, printer, *args): targets = self._print_sequence_pretty(self.targets, ',', printer, *args) gate_name = stringPict(unicode(self.gate_name)) return self._print_subscript_pretty(gate_name, targets) def _print_contents_latex(self, printer, *args): targets = self._print_sequence(self.targets, ',', printer, *args) return r'%s_{%s}' % (self.gate_name_latex, targets) def plot_gate(self, circ_plot, gate_idx): circ_plot.one_qubit_box( self.gate_name_plot, gate_idx, int(self.targets[0]) ) class OneQubitGate(Gate): """A single qubit unitary gate base class.""" nqubits = Integer(1) def plot_gate(self, circ_plot, gate_idx): circ_plot.one_qubit_box( self.gate_name_plot, gate_idx, int(self.targets[0]) ) def _eval_commutator(self, other, **hints): if isinstance(other, OneQubitGate): if self.targets != other.targets or self.__class__ == other.__class__: return Integer(0) return Operator._eval_commutator(self, other, **hints) def _eval_anticommutator(self, other, **hints): if isinstance(other, OneQubitGate): if self.targets != other.targets or self.__class__ == other.__class__: return Integer(2)*self*other return Operator._eval_anticommutator(self, other, **hints) class TwoQubitGate(Gate): """A two qubit unitary gate base class.""" nqubits = Integer(2) #----------------------------------------------------------------------------- # Single Qubit Gates #----------------------------------------------------------------------------- class IdentityGate(OneQubitGate): """The single qubit identity gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'1' gate_name_latex = u'1' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('eye2', format) def _eval_commutator(self, other, **hints): return Integer(0) def _eval_anticommutator(self, other, **hints): return Integer(2)*other class HadamardGate(OneQubitGate): """The single qubit Hadamard gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'H' gate_name_latex = u'H' def get_target_matrix(self, format='sympy'): if _normalized: return matrix_cache.get_matrix('H', format) else: return matrix_cache.get_matrix('Hsqrt2', format) def _eval_commutator_XGate(self, other, **hints): return I*sqrt(2)*YGate(self.targets[0]) def _eval_commutator_YGate(self, other, **hints): return I*sqrt(2)*(ZGate(self.targets[0])-XGate(self.targets[0])) def _eval_commutator_ZGate(self, other, **hints): return -I*sqrt(2)*YGate(self.targets[0]) def _eval_anticommutator_XGate(self, other, **hints): return sqrt(2)*IdentityGate(self.targets[0]) def _eval_anticommutator_YGate(self, other, **hints): return Integer(0) def _eval_anticommutator_ZGate(self, other, **hints): return sqrt(2)*IdentityGate(self.targets[0]) class XGate(HermitianOperator, OneQubitGate): """The single qubit X, or NOT, gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'X' gate_name_latex = u'X' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('X', format) def plot_gate(self, circ_plot, gate_idx): circ_plot.not_point( gate_idx, int(self.label[0]) ) def _eval_commutator_YGate(self, other, **hints): return Integer(2)*I*ZGate(self.targets[0]) def _eval_anticommutator_XGate(self, other, **hints): return Integer(2)*IdentityGate(self.targets[0]) def _eval_anticommutator_YGate(self, other, **hints): return Integer(0) def _eval_anticommutator_ZGate(self, other, **hints): return Integer(0) class YGate(HermitianOperator, OneQubitGate): """The single qubit Y gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'Y' gate_name_latex = u'Y' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('Y', format) def _eval_commutator_ZGate(self, other, **hints): return Integer(2)*I*XGate(self.targets[0]) def _eval_anticommutator_YGate(self, other, **hints): return Integer(2)*IdentityGate(self.targets[0]) def _eval_anticommutator_ZGate(self, other, **hints): return Integer(0) class ZGate(HermitianOperator, OneQubitGate): """The single qubit Z gate. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'Z' gate_name_latex = u'Z' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('Z', format) def _eval_commutator_XGate(self, other, **hints): return Integer(2)*I*YGate(self.targets[0]) def _eval_anticommutator_YGate(self, other, **hints): return Integer(0) class PhaseGate(OneQubitGate): """The single qubit phase, or S, gate. This gate rotates the phase of the state by pi/2 if the state is |1> and does nothing if the state is |0>. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'S' gate_name_latex = u'S' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('S', format) def _eval_commutator_ZGate(self, other, **hints): return Integer(0) def _eval_commutator_TGate(self, other, **hints): return Integer(0) class TGate(OneQubitGate): """The single qubit pi/8 gate. This gate rotates the phase of the state by pi/4 if the state is |1> and does nothing if the state is |0>. Parameters ---------- target : int The target qubit this gate will apply to. Examples -------- """ gate_name = u'T' gate_name_latex = u'T' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('T', format) def _eval_commutator_ZGate(self, other, **hints): return Integer(0) def _eval_commutator_PhaseGate(self, other, **hints): return Integer(0) # Aliases for gate names. H = HadamardGate X = XGate Y = YGate Z = ZGate T = TGate Phase = S = PhaseGate #----------------------------------------------------------------------------- # 2 Qubit Gates #----------------------------------------------------------------------------- class CNotGate(CGate, TwoQubitGate): """Two qubit controlled-NOT. This gate performs the NOT or X gate on the target qubit if the control qubits all have the value 1. Parameters ---------- label : tuple A tuple of the form (control, target). Examples -------- """ gate_name = 'CNOT' gate_name_latex = u'CNOT' #------------------------------------------------------------------------- # Initialization #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): args = Gate._eval_args(args) return args @classmethod def _eval_hilbert_space(cls, args): """This returns the smallest possible Hilbert space.""" return ComplexSpace(2)**(max(args)+1) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def min_qubits(self): """The minimum number of qubits this gate needs to act on.""" return max(self.label)+1 @property def targets(self): """A tuple of target qubits.""" return (self.label[1],) @property def controls(self): """A tuple of control qubits.""" return (self.label[0],) @property def gate(self): """The non-controlled gate that will be applied to the targets.""" return XGate(self.label[1]) #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- # The default printing of Gate works better than those of CGate, so we # go around the overridden methods in CGate. def _print_contents(self, printer, *args): return Gate._print_contents(self, printer, *args) def _print_contents_pretty(self, printer, *args): return Gate._print_contents_pretty(self, printer, *args) def _print_contents_latex(self, printer, *args): return Gate._print_contents_latex(self, printer, *args) #------------------------------------------------------------------------- # Commutator/AntiCommutator #------------------------------------------------------------------------- def _eval_commutator_ZGate(self, other, **hints): """[CNOT(i, j), Z(i)] == 0.""" if self.controls[0] == other.targets[0]: return Integer(0) else: raise NotImplementedError('Commutator not implemented: %r' % other) def _eval_commutator_TGate(self, other, **hints): """[CNOT(i, j), T(i)] == 0.""" return self._eval_commutator_ZGate(other, **hints) def _eval_commutator_PhaseGate(self, other, **hints): """[CNOT(i, j), S(i)] == 0.""" return self._eval_commutator_ZGate(other, **hints) def _eval_commutator_XGate(self, other, **hints): """[CNOT(i, j), X(j)] == 0.""" if self.targets[0] == other.targets[0]: return Integer(0) else: raise NotImplementedError('Commutator not implemented: %r' % other) def _eval_commutator_CNotGate(self, other, **hints): """[CNOT(i, j), CNOT(i,k)] == 0.""" if self.controls[0] == other.controls[0]: return Integer(0) else: raise NotImplementedError('Commutator not implemented: %r' % other) class SwapGate(TwoQubitGate): """Two qubit SWAP gate. This gate swap the values of the two qubits. Parameters ---------- label : tuple A tuple of the form (target1, target2). Examples -------- """ gate_name = 'SWAP' gate_name_latex = u'SWAP' def get_target_matrix(self, format='sympy'): return matrix_cache.get_matrix('SWAP', format) def decompose(self, **options): """Decompose the SWAP gate into CNOT gates.""" i,j = self.targets[0], self.targets[1] g1 = CNotGate(i, j) g2 = CNotGate(j, i) return g1*g2*g1 def plot_gate(self, circ_plot, gate_idx): min_wire = int(min(self.targets)) max_wire = int(max(self.targets)) circ_plot.control_line(gate_idx, min_wire, max_wire) circ_plot.swap_point(gate_idx, min_wire) circ_plot.swap_point(gate_idx, max_wire) def _represent_ZGate(self, basis, **options): """Represent the SWAP gate in the computational basis. The following representation is used to compute this: SWAP = |1><1|x|1><1| + |0><0|x|0><0| + |1><0|x|0><1| + |0><1|x|1><0| """ format = options.get('format', 'sympy') targets = [int(t) for t in self.targets] min_target = min(targets) max_target = max(targets) nqubits = options.get('nqubits',self.min_qubits) op01 = matrix_cache.get_matrix('op01', format) op10 = matrix_cache.get_matrix('op10', format) op11 = matrix_cache.get_matrix('op11', format) op00 = matrix_cache.get_matrix('op00', format) eye2 = matrix_cache.get_matrix('eye2', format) result = None for i, j in ((op01,op10),(op10,op01),(op00,op00),(op11,op11)): product = nqubits*[eye2] product[nqubits-min_target-1] = i product[nqubits-max_target-1] = j new_result = matrix_tensor_product(*product) if result is None: result = new_result else: result = result + new_result return result # Aliases for gate names. CNOT = CNotGate SWAP = SwapGate #----------------------------------------------------------------------------- # Represent #----------------------------------------------------------------------------- def represent_zbasis(controls, targets, target_matrix, nqubits, format='sympy'): """Represent a gate with controls, targets and target_matrix. This function does the low-level work of representing gates as matrices in the standard computational basis (ZGate). Currently, we support two main cases: 1. One target qubit and no control qubits. 2. One target qubits and multiple control qubits. For the base of multiple controls, we use the following expression [1]: 1_{2**n} + (|1><1|)^{(n-1)} x (target-matrix - 1_{2}) Parameters ---------- controls : list, tuple A sequence of control qubits. targets : list, tuple A sequence of target qubits. target_matrix : sympy.Matrix, numpy.matrix, scipy.sparse The matrix form of the transformation to be performed on the target qubits. The format of this matrix must match that passed into the `format` argument. nqubits : int The total number of qubits used for the representation. format : str The format of the final matrix ('sympy', 'numpy', 'scipy.sparse'). Examples -------- References ---------- [1] http://www.johnlapeyre.com/qinf/qinf_html/node6.html. """ controls = [int(x) for x in controls] targets = [int(x) for x in targets] nqubits = int(nqubits) # This checks for the format as well. op11 = matrix_cache.get_matrix('op11', format) eye2 = matrix_cache.get_matrix('eye2', format) # Plain single qubit case if len(controls) == 0 and len(targets) == 1: product = [] bit = targets[0] # Fill product with [I1,Gate,I2] such that the unitaries, # I, cause the gate to be applied to the correct Qubit if bit != nqubits-1: product.append(matrix_eye(2**(nqubits-bit-1), format=format)) product.append(target_matrix) if bit != 0: product.append(matrix_eye(2**bit, format=format)) return matrix_tensor_product(*product) # Single target, multiple controls. elif len(targets) == 1 and len(controls) >= 1: target = targets[0] # Build the non-trivial part. product2 = [] for i in range(nqubits): product2.append(matrix_eye(2, format=format)) for control in controls: product2[nqubits-1-control] = op11 product2[nqubits-1-target] = target_matrix - eye2 return matrix_eye(2**nqubits, format=format) +\ matrix_tensor_product(*product2) # Multi-target, multi-control is not yet implemented. else: raise NotImplementedError( 'The representation of multi-target, multi-control gates ' 'is not implemented.' ) #----------------------------------------------------------------------------- # Gate manipulation functions. #----------------------------------------------------------------------------- def gate_simp(circuit): """Simplifies gates symbolically It first sorts gates using gate_sort. It then applies basic simplification rules to the circuit, e.g., XGate**2 = Identity """ # Bubble sort out gates that commute. circuit = gate_sort(circuit) # Do simplifications by subing a simplification into the first element # which can be simplified. We recursively call gate_simp with new circuit # as input more simplifications exist. if isinstance(circuit, Add): return sum(gate_simp(t) for t in circuit.args) elif isinstance(circuit, Mul): circuit_args = circuit.args elif isinstance(circuit, Pow): b, e = circuit.as_base_exp() circuit_args = (gate_simp(b)**e,) else: return circuit # Iterate through each element in circuit, simplify if possible. for i in xrange(len(circuit_args)): # H,X,Y or Z squared is 1. # T**2 = S, S**2 = Z if isinstance(circuit_args[i], Pow): if isinstance(circuit_args[i].base, (HadamardGate, XGate, YGate, ZGate))\ and isinstance(circuit_args[i].exp, Number): # Build a new circuit taking replacing the # H,X,Y,Z squared with one. newargs = (circuit_args[:i] +\ (circuit_args[i].base**(circuit_args[i].exp % 2),) +\ circuit_args[i+1:]) # Recursively simplify the new circuit. circuit = gate_simp(Mul(*newargs)) break elif isinstance(circuit_args[i].base, PhaseGate): # Build a new circuit taking old circuit but splicing # in simplification. newargs = circuit_args[:i] # Replace PhaseGate**2 with ZGate. newargs = newargs + (ZGate(circuit_args[i].base.args[0])**\ (Integer(circuit_args[i].exp/2)), circuit_args[i].base**\ (circuit_args[i].exp % 2)) # Append the last elements. newargs = newargs + circuit_args[i+1:] # Recursively simplify the new circuit. circuit = gate_simp(Mul(*newargs)) break elif isinstance(circuit_args[i].base, TGate): # Build a new circuit taking all the old elements. newargs = circuit_args[:i] # Put an Phasegate in place of any TGate**2. newargs = newargs + (PhaseGate(circuit_args[i].base.args[0])**\ Integer(circuit_args[i].exp/2), circuit_args[i].base**\ (circuit_args[i].exp % 2)) # Append the last elements. newargs = newargs + circuit_args[i+1:] # Recursively simplify the new circuit. circuit = gate_simp(Mul(*newargs)) break return circuit def gate_sort(circuit): """Sorts the gates while keeping track of commutation relations This function uses a bubble sort to rearrange the order of gate application. Keeps track of Quantum computations special commutation relations (e.g. things that apply to the same Qubit do not commute with each other) circuit is the Mul of gates that are to be sorted. """ # Make sure we have an Add or Mul. if isinstance(circuit, Add): return sum(gate_sort(t) for t in circuit.args) if isinstance(circuit, Pow): return gate_sort(circuit.base)**circuit.exp elif isinstance(circuit, Gate): return circuit if not isinstance(circuit, Mul): return circuit changes = True while changes: changes = False circ_array = circuit.args for i in xrange(len(circ_array)-1): # Go through each element and switch ones that are in wrong order if isinstance(circ_array[i], (Gate, Pow)) and\ isinstance(circ_array[i+1], (Gate, Pow)): # If we have a Pow object, look at only the base first_base, first_exp = circ_array[i].as_base_exp() second_base, second_exp = circ_array[i+1].as_base_exp() # Use sympy's hash based sorting. This is not mathematical # sorting, but is rather based on comparing hashes of objects. # See Basic.compare for details. if first_base.compare(second_base) > 0: if Commutator(first_base, second_base).doit() == 0: new_args = (circuit.args[:i] + (circuit.args[i+1],) +\ (circuit.args[i],) + circuit.args[i+2:]) circuit = Mul(*new_args) circ_array = circuit.args changes = True break if AntiCommutator(first_base, second_base).doit() == 0: new_args = (circuit.args[:i] + (circuit.args[i+1],) +\ (circuit.args[i],) + circuit.args[i+2:]) sign = Integer(-1)**(first_exp*second_exp) circuit = sign*Mul(*new_args) circ_array = circuit.args changes = True break return circuit #----------------------------------------------------------------------------- # Utility functions #----------------------------------------------------------------------------- def random_circuit(ngates, nqubits, gate_space=(X, Y, Z, S, T, H, CNOT, SWAP)): """Return a random circuit of ngates and nqubits. This uses an equally weighted sample of (X, Y, Z, S, T, H, CNOT, SWAP) gates. Parameters ---------- ngates : int The number of gates in the circuit. nqubits : int The number of qubits in the circuit. gate_space : tuple A tuple of the gate classes that will be used in the circuit. Repeating gate classes multiple times in this tuple will increase the frequency they appear in the random circuit. """ qubit_space = range(nqubits) result = [] for i in xrange(ngates): g = random.choice(gate_space) if g == CNotGate or g == SwapGate: qubits = random.sample(qubit_space,2) g = g(*qubits) else: qubit = random.choice(qubit_space) g = g(qubit) result.append(g) return Mul(*result) def zx_basis_transform(self, format='sympy'): """Transformation matrix from Z to X basis.""" return matrix_cache.get_matrix('ZX', format) def zy_basis_transform(self, format='sympy'): """Transformation matrix from Z to Y basis.""" return matrix_cache.get_matrix('ZY', format) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/piab.py0000644000175000017500000000323112014170666025537 0ustar georgeskgeorgesk"""1D quantum particle in a box.""" from sympy import Symbol, pi, sqrt, sin, Interval, S from sympy.physics.quantum.operator import HermitianOperator from sympy.physics.quantum.state import Ket, Bra from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.kronecker import KroneckerDelta from sympy.physics.quantum.hilbert import L2 m = Symbol('m') L = Symbol('L') __all__ = [ 'PIABHamiltonian', 'PIABKet', 'PIABBra' ] class PIABHamiltonian(HermitianOperator): """Particle in a box Hamiltonian operator.""" @classmethod def _eval_hilbert_space(cls, label): return L2(Interval(S.NegativeInfinity,S.Infinity)) def _apply_operator_PIABKet(self, ket, **options): n = ket.label[0] return (n**2*pi**2*hbar**2)/(2*m*L**2)*ket class PIABKet(Ket): """Particle in a box eigenket.""" @classmethod def _eval_hilbert_space(cls, args): return L2(Interval(S.NegativeInfinity,S.Infinity)) @property def dual_class(self): return PIABBra def _represent_default_basis(self, **options): return self._represent_XOp(None, **options) def _represent_XOp(self, basis, **options): x = Symbol('x') n = Symbol('n') subs_info = options.get('subs',{}) return sqrt(2/L)*sin(n*pi*x/L).subs(subs_info) def _eval_innerproduct_PIABBra(self, bra): return KroneckerDelta(bra.label[0], self.label[0]) class PIABBra(Bra): """Particle in a box eigenbra.""" @classmethod def _eval_hilbert_space(cls, label): return L2(Interval(S.NegativeInfinity,S.Infinity)) @property def dual_class(self): return PIABKet wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/spin.py0000644000175000017500000006425512014170666025612 0ustar georgeskgeorgesk"""Quantum mechanical angular momemtum.""" from sympy import ( AtomicExpr, binomial, cos, diff, exp, factorial, I, Integer, Matrix, N, pi, Rational, S, sin, sqrt, Symbol, sympify, simplify ) from sympy.matrices.matrices import zeros from sympy.printing.pretty.stringpict import prettyForm, stringPict from sympy.physics.quantum.represent import represent from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.operator import ( HermitianOperator, Operator, UnitaryOperator ) from sympy.physics.quantum.state import Bra, Ket, State from sympy.physics.quantum.kronecker import KroneckerDelta from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.hilbert import ComplexSpace __all__ = [ 'm_values', 'Jplus', 'Jminus', 'Jx', 'Jy', 'Jz', 'J2', 'JzKet', 'JzBra', 'JxKet', 'JxBra', 'JyKet', 'JyBra', 'Rotation', 'WignerD' ] def m_values(j): j = sympify(j) size = 2*j + 1 if not size.is_Integer or not size > 0: raise ValueError( 'Only integer or half-integer values allowed for j, got: : %r' % j ) return size, [j-i for i in range(int(2*j+1))] #----------------------------------------------------------------------------- # SpinOperators #----------------------------------------------------------------------------- class SpinOpBase(object): """Base class for spin operators.""" @classmethod def _eval_hilbert_space(cls, label): # We consider all j values so our space is infinite. return ComplexSpace(S.Infinity) @property def name(self): return self.args[0] def _print_contents(self, printer, *args): return '%s%s' % (unicode(self.name), self._coord) # def _sympyrepr(self, printer, *args): # return '%s(%s)' % ( # self.__class__.__name__, printer._print(self.label,*args) # def _print_contents_pretty(self, printer, *args): a = stringPict(unicode(self.name)) b = stringPict(self._coord) return self._print_subscript_pretty(a, b) def _print_contents_latex(self, printer, *args): return r'%s_%s' % ((unicode(self.name), self._coord)) def _represent_base(self, basis, **options): j = options.get('j', Rational(1,2)) size, mvals = m_values(j) result = zeros((size, size)) for p in range(size): for q in range(size): me = self.matrix_element(j, mvals[p], j, mvals[q]) result[p, q] = me return result class JplusOp(SpinOpBase, Operator): """The J+ operator.""" _coord = '+' def _eval_commutator_JminusOp(self, other): return 2*hbar*JzOp(self.name) def _apply_operator_JzKet(self, ket, **options): j = ket.j m = ket.m if m.is_Number and j.is_Number: if m >= j: return S.Zero return hbar*sqrt(j*(j+S.One)-m*(m+S.One))*JzKet(j, m+S.One) def matrix_element(self, j, m, jp, mp): result = hbar*sqrt(j*(j+S.One)-mp*(mp+S.One)) result *= KroneckerDelta(m, mp+1) result *= KroneckerDelta(j, jp) return result def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): return self._represent_base(basis, **options) def _eval_rewrite_as_xyz(self, *args): return JxOp(args[0]) + I*JyOp(args[0]) class JminusOp(SpinOpBase, Operator): """The J- operator.""" _coord = '-' def _apply_operator_JzKet(self, ket, **options): j = ket.j m = ket.m if m.is_Number and j.is_Number: if m <= -j: return S.Zero return hbar*sqrt(j*(j+S.One)-m*(m-S.One))*JzKet(j, m-S.One) def matrix_element(self, j, m, jp, mp): result = hbar*sqrt(j*(j+S.One)-mp*(mp-S.One)) result *= KroneckerDelta(m, mp-1) result *= KroneckerDelta(j, jp) return result def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): return self._represent_base(basis, **options) def _eval_rewrite_as_xyz(self, *args): return JxOp(args[0]) - I*JyOp(args[0]) class JxOp(SpinOpBase, HermitianOperator): """The Jx operator.""" _coord = 'x' def _eval_commutator_JyOp(self, other): return I*hbar*JzOp(self.name) def _eval_commutator_JzOp(self, other): return -I*hbar*JyOp(self.name) def _apply_operator_JzKet(self, ket, **options): jp = JplusOp(self.name)._apply_operator_JzKet(ket, **options) jm = JminusOp(self.name)._apply_operator_JzKet(ket, **options) return (jp + jm)/Integer(2) def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): jp = JplusOp(self.name)._represent_JzOp(basis, **options) jm = JminusOp(self.name)._represent_JzOp(basis, **options) return (jp + jm)/Integer(2) def _eval_rewrite_as_plusminus(self, *args): return (JplusOp(args[0]) + JminusOp(args[0]))/2 class JyOp(SpinOpBase, HermitianOperator): """The Jy operator.""" _coord = 'y' def _eval_commutator_JzOp(self, other): return I*hbar*JxOp(self.name) def _eval_commutator_JxOp(self, other): return -I*hbar*J2Op(self.name) def _apply_operator_JzKet(self, ket, **options): jp = JplusOp(self.name)._apply_operator_JzKet(ket, **options) jm = JminusOp(self.name)._apply_operator_JzKet(ket, **options) return (jp - jm)/(Integer(2)*I) def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): jp = JplusOp(self.name)._represent_JzOp(basis, **options) jm = JminusOp(self.name)._represent_JzOp(basis, **options) return (jp - jm)/(Integer(2)*I) def _eval_rewrite_as_plusminus(self, *args): return (JplusOp(args[0]) - JminusOp(args[0]))/(2*I) class JzOp(SpinOpBase, HermitianOperator): """The Jz operator.""" _coord = 'z' def _eval_commutator_JxOp(self, other): return I*hbar*JyOp(self.name) def _eval_commutator_JyOp(self, other): return -I*hbar*JxOp(self.name) def _eval_commutator_JplusOp(self, other): return hbar*JplusOp(self.name) def _eval_commutator_JminusOp(self, other): return -hbar*JminusOp(self.name) def _apply_operator_JzKet(self, ket, **options): return (hbar*ket.m)*ket def matrix_element(self, j, m, jp, mp): result = hbar*mp result *= KroneckerDelta(m, mp) result *= KroneckerDelta(j, jp) return result def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): return self._represent_base(basis, **options) class J2Op(SpinOpBase, HermitianOperator): """The J^2 operator.""" _coord = '2' def _eval_commutator_JxOp(self, other): return S.Zero def _eval_commutator_JyOp(self, other): return S.Zero def _eval_commutator_JzOp(self, other): return S.Zero def _eval_commutator_JplusOp(self, other): return S.Zero def _eval_commutator_JminusOp(self, other): return S.Zero def _apply_operator_JzKet(self, ket, **options): j = ket.j return hbar**2*j*(j+1)*ket def matrix_element(self, j, m, jp, mp): result = (hbar**2)*j*(j+1) result *= KroneckerDelta(m, mp) result *= KroneckerDelta(j, jp) return result def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): return self._represent_base(basis, **options) def _pretty(self, printer, *args): a = stringPict('J') b = stringPict('2') top = stringPict(*b.left(' '*a.width())) bot = stringPict(*a.right(' '*b.width())) return prettyForm(binding=prettyForm.POW, *bot.above(top)) def _latex(self, printer, *args): return r'%s^2' % str(self.name) def _eval_rewrite_as_xyz(self, *args): return JxOp(args[0])**2 + JyOp(args[0])**2 + JzOp(args[0])**2 def _eval_rewrite_as_plusminus(self, *args): a = args[0] return JzOp(a)**2 +\ Rational(1,2)*(JplusOp(a)*JminusOp(a) + JminusOp(a)*JplusOp(a)) class Rotation(UnitaryOperator): """Wigner D operator in terms of Euler angles. Defines the rotation operator in terms of the Euler angles defined by the z-y-z convention for a passive transformation. That is the coordinate axes are rotated first about the z-axis, giving the new x'-y'-z' axes. Then this new coordinate system is rotated about the new y'-axis, giving new x''-y''-z'' axes. Then this new coordinate system is rotated about the z''-axis. Conventions follow those laid out in [1]. See the Wigner D-function, Rotation.D, and the Wigner small-d matrix for the evaluation of the rotation operator on spin states. Parameters ========== alpha : Number, Symbol First Euler Angle beta : Number, Symbol Second Euler angle gamma : Number, Symbol Third Euler angle Examples ======== A simple example rotation operator: >>> from sympy import pi >>> from sympy.physics.quantum.spin import Rotation >>> Rotation(pi, 0, pi/2) 'R'(pi,0,pi/2) With symbolic Euler angles and calculating the inverse rotation operator: >>> from sympy import symbols >>> a, b, c = symbols('a b c') >>> Rotation(a, b, c) 'R'(a,b,c) >>> Rotation(a, b, c).inverse() 'R'(-c,-b,-a) References ========== [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. """ @classmethod def _eval_args(cls, args): args = QExpr._eval_args(args) if len(args) != 3: raise ValueError('3 Euler angles required, got: %r' % args) return args @classmethod def _eval_hilbert_space(cls, label): # We consider all j values so our space is infinite. return ComplexSpace(S.Infinity) @property def alpha(self): return self.label[0] @property def beta(self): return self.label[1] @property def gamma(self): return self.label[2] def _print_operator_name(self, printer, *args): return printer._print('R', *args) def _print_operator_name_pretty(self, printer, *args): return prettyForm(u"\u211B" + u" ") def _eval_inverse(self): return Rotation(-self.gamma, -self.beta, -self.alpha) @classmethod def D(cls, j, m, mp, alpha, beta, gamma): """Wigner D-function. Returns an instance of the WignerD class. See the corresponding docstring for more information on the Wigner-D matrix. Parameters =========== j : Number Total angular momentum m : Number Eigenvalue of angular momentum along axis after rotation mp : Number Eigenvalue of angular momentum along rotated axis alpha : Number, Symbol First Euler angle of rotation beta : Number, Symbol Second Euler angle of rotation gamma : Number, Symbol Third Euler angle of rotation Examples ======== Return the Wigner-D matrix element for a defined rotation, both numerical and symbolic: >>> from sympy.physics.quantum.spin import Rotation >>> from sympy import pi, symbols >>> alpha, beta, gamma = symbols('alpha beta gamma') >>> Rotation.D(1, 1, 0,pi, pi/2,-pi) WignerD(1, 1, 0, pi, pi/2, -pi) """ return WignerD(j,m,mp,alpha,beta,gamma) @classmethod def d(cls, j, m, mp, beta): """Wigner small-d function. Roturns an instance of cthe WignerD class with the alpha and gamma angles given as 0. See the corresponding docstring for more information on the Wigner small-d matrix. Parameters =========== j : Number Total angular momentum m : Number Eigenvalue of angular momentum along axis after rotation mp : Number Eigenvalue of angular momentum along rotated axis beta : Number, Symbol Second Euler angle of rotation Examples ======== Return the Wigner-D matrix element for a defined rotation, both numerical and symbolic: >>> from sympy.physics.quantum.spin import Rotation >>> from sympy import pi, symbols >>> beta = symbols('beta') >>> Rotation.d(1, 1, 0, pi/2) WignerD(1, 1, 0, 0, pi/2, 0) """ return WignerD(j,m,mp,0,beta,0) def matrix_element(self, j, m, jp, mp): result = self.__class__.D( jp, m, mp, self.alpha, self.beta, self.gamma ) result *= KroneckerDelta(j,jp) return result def _represent_base(self, basis, **options): j = sympify(options.get('j', Rational(1,2))) size, mvals = m_values(j) result = zeros((size, size)) for p in range(size): for q in range(size): me = self.matrix_element(j, mvals[p], j, mvals[q]) result[p, q] = me return result def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JzOp(self, basis, **options): return self._represent_base(basis, **options) class WignerD(AtomicExpr): """Wigner-D function The Wigner D-function gives the matrix elements of the rotation operator in the jm-representation. For the Euler angles alpha, beta, gamma, the the D-function is defined such that: = delta_jj' * D(j, m, m', alpha, beta, gamma) Where the rotation operator is as defined by the Rotation class. The Wigner D-function defined in this way gives: D(j, m, m', alpha, beta, gamma) = exp(-i*m*alpha) * d(j, m, m', beta) * exp(-i*m'*gamma) Where d is the Wigner small-d function, which is given by Rotation.d. The Wigner small-d function gives the component of the Wigner D-function that is determined by the second Euler angle. That is the Wigner D-function is: D(j, m, m', alpha, beta, gamma) = exp(-i*m*alpha) * d(j, m, m', beta) * exp(-i*m'*gamma) Where d is the small-d function. The Wigner D-function is given by Rotation.D. Note that to evaluate the D-function, the j, m and mp parameters must be integer or half integer numbers. Parameters ========== j : Number Total angular momentum m : Number Eigenvalue of angular momentum along axis after rotation mp : Number Eigenvalue of angular momentum along rotated axis alpha : Number, Symbol First Euler angle of rotation beta : Number, Symbol Second Euler angle of rotation gamma : Number, Symbol Third Euler angle of rotation Examples ======== Evaluate the Wigner-D matrix elements of a simple rotation: >>> from sympy.physics.quantum.spin import Rotation >>> from sympy import pi >>> rot = Rotation.D(1, 1, 0, pi, pi/2, 0) >>> rot WignerD(1, 1, 0, pi, pi/2, 0) >>> rot.doit() 2**(1/2)/2 Evaluate the Wigner-d matrix elements of a simple rotation >>> rot = Rotation.d(1, 1, 0, pi/2) >>> rot WignerD(1, 1, 0, 0, pi/2, 0) >>> rot.doit() -2**(1/2)/2 References ========== [1] Varshalovich, D A, Quantum Theory of Angular Momentum. 1988. """ def __new__(cls, *args, **hints): if not len(args) == 6: raise ValueError('6 parameters expected, got %s' % args) evaluate = hints.get('evaluate', False) if evaluate: return AtomicExpr.__new__(cls, *args)._eval_wignerd() return AtomicExpr.__new__(cls, *args, **{'evaluate': False}) @property def j(self): return self.args[0] @property def m(self): return self.args[1] @property def mp(self): return self.args[2] @property def alpha(self): return self.args[3] @property def beta(self): return self.args[4] @property def gamma(self): return self.args[5] def _latex(self, printer, *args): if self.alpha == 0 and self.gamma == 0: return r'd^{%s}_{%s,%s}\left(%s\right)' % \ ( printer._print(self.j), printer._print(self.m), printer._print(self.mp), printer._print(self.beta) ) return r'D^{%s}_{%s,%s}\left(%s,%s,%s\right)' % \ ( printer._print(self.j), printer._print(self.m), printer._print(self.mp), printer._print(self.alpha), printer._print(self.beta), printer._print(self.gamma) ) def _pretty(self, printer, *args): top = printer._print(self.j) bot = printer._print(self.m) bot = prettyForm(*bot.right(',')) bot = prettyForm(*bot.right(printer._print(self.mp))) pad = max(top.width(), bot.width()) top = prettyForm(*top.left(' ')) bot = prettyForm(*bot.left(' ')) if pad > top.width(): top = prettyForm(*top.right(' ' * (pad-top.width()))) if pad > bot.width(): bot = prettyForm(*bot.right(' ' * (pad-bot.width()))) if self.alpha == 0 and self.gamma == 0: args = printer._print(self.beta) s = stringPict('d' + ' '*pad) else: args = printer._print(self.alpha) args = prettyForm(*args.right(',')) args = prettyForm(*args.right(printer._print(self.beta))) args = prettyForm(*args.right(',')) args = prettyForm(*args.right(printer._print(self.gamma))) s = stringPict('D' + ' '*pad) args = prettyForm(*args.parens()) s = prettyForm(*s.above(top)) s = prettyForm(*s.below(bot)) s = prettyForm(*s.right(args)) return s def doit(self, **hints): hints['evaluate'] = True return WignerD(*self.args, **hints) def _eval_wignerd(self): j = sympify(self.j) m = sympify(self.m) mp = sympify(self.mp) alpha = sympify(self.alpha) beta = sympify(self.beta) gamma = sympify(self.gamma) if not j.is_number: raise ValueError("j parameter must be numerical to evaluate, got %s", j) r = 0 if beta == pi/2: # Varshalovich Equation (5), Section 4.16, page 113, setting # alpha=gamma=0. for k in range(2*j+1): if k > j+mp or k > j-m or k < mp-m: continue r += (-S(1))**k * binomial(j+mp, k) * binomial(j-mp, k+m-mp) r *= (-S(1))**(m-mp) / 2**j * sqrt(factorial(j+m) * \ factorial(j-m) / (factorial(j+mp) * factorial(j-mp))) else: # Varshalovich Equation(5), Section 4.7.2, page 87, where we set # beta1=beta2=pi/2, and we get alpha=gamma=pi/2 and beta=phi+pi, # then we use the Eq. (1), Section 4.4. page 79, to simplify: # d(j, m, mp, beta+pi) = (-1)**(j-mp) * d(j, m, -mp, beta) # This happens to be almost the same as in Eq.(10), Section 4.16, # except that we need to substitute -mp for mp. size, mvals = m_values(j) for mpp in mvals: r += Rotation.d(j, m, mpp, pi/2).doit() * (cos(-mpp*beta)+I*sin(-mpp*beta)) * \ Rotation.d(j, mpp, -mp, pi/2).doit() # Empirical normalization factor so results match Varshalovich # Tables 4.3-4.12 # Note that this exact normalization does not follow from the # above equations r = r * I**(2*j-m-mp) * (-1)**(2*m) # Finally, simplify the whole expression r = simplify(r) r *= exp(-I*m*alpha)*exp(-I*mp*gamma) return r Jx = JxOp('J') Jy = JyOp('J') Jz = JzOp('J') J2 = J2Op('J') Jplus = JplusOp('J') Jminus = JminusOp('J') #----------------------------------------------------------------------------- # Spin States #----------------------------------------------------------------------------- class SpinState(State): """Base class for angular momentum states.""" _label_separator = ',' @property def j(self): return self.label[0] @property def m(self): return self.label[1] @classmethod def _eval_hilbert_space(cls, label): return ComplexSpace(2*label[0]+1) def _represent_base(self, **options): j = self.j alpha = options.get('alpha', 0) beta = options.get('beta', 0) gamma = options.get('gamma', 0) size, mvals = m_values(j) result = zeros((size,1)) for p in range(size): result[p,0] = Rotation.D(self.j, mvals[p], self.m, alpha, beta, gamma).doit() return result def _eval_rewrite_as_Jx(self, *args): if isinstance(self,Bra): return self._rewrite_basis(Jx, JxBra, *args) return self._rewrite_basis(Jx, JxKet, *args) def _eval_rewrite_as_Jy(self, *args): if isinstance(self,Bra): return self._rewrite_basis(Jy, JyBra, *args) return self._rewrite_basis(Jy, JyKet, *args) def _eval_rewrite_as_Jz(self, *args): return self._rewrite_basis(Jz, JzKet, *args) def _rewrite_basis(self, basis, evect_cls, *args): j = self.j size, mvals = m_values(j) result = 0 vect = represent(self, basis=basis) for p in range(size): me = vect[p] me *= evect_cls.__new__(evect_cls, j, mvals[p]) result += me return result def _eval_innerproduct_JxBra(self, bra, **hints): result = KroneckerDelta(self.j, bra.j) if not isinstance(bra, self.__class__): result *= self._represent_JxOp(None)[bra.j-bra.m] else: result *= KroneckerDelta(self.m, bra.m) return result def _eval_innerproduct_JyBra(self, bra, **hints): result = KroneckerDelta(self.j, bra.j) if not isinstance(bra, self.__class__): result *= self._represent_JyOp(None)[bra.j-bra.m] else: result *= KroneckerDelta(self.m, bra.m) return result def _eval_innerproduct_JzBra(self, bra, **hints): result = KroneckerDelta(self.j, bra.j) if not issubclass(bra.dual_class, self.__class__): result *= self._represent_JzOp(None)[bra.j-bra.m] else: result *= KroneckerDelta(self.m, bra.m) return result class JxKet(SpinState, Ket): """Eigenket of Jx. See JzKet for the usage of spin eigenstates. """ @property def dual_class(self): return JxBra def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JxOp(self, basis, **options): return self._represent_base() def _represent_JyOp(self, basis, **options): return self._represent_base(alpha=3*pi/2) def _represent_JzOp(self, basis, **options): return self._represent_base(beta=pi/2) class JxBra(SpinState, Bra): """Eigenbra of Jx. See JzKet for the usage of spin eigenstates. """ @property def dual_class(self): return JxKet class JyKet(SpinState, Ket): """Eigenket of Jy. See JzKet for the usage of spin eigenstates. """ @property def dual_class(self): return JyBra def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JxOp(self, basis, **options): return self._represent_base(gamma=pi/2) def _represent_JyOp(self, basis, **options): return self._represent_base() def _represent_JzOp(self, basis, **options): return self._represent_base(alpha=3*pi/2,beta=-pi/2,gamma=pi/2) class JyBra(SpinState, Bra): """Eigenbra of Jy. See JzKet for the usage of spin eigenstates. """ @property def dual_class(self): return JyKet class JzKet(SpinState, Ket): """Eigenket of Jz. Spin state which is an eigenstate of the Jz operator Parameters ========== j : Number, Symbol Total spin angular momentum m : Number, Symbol Eigenvalue of the Jz spin operator Examples ======== Defining simple spin states, both numerical and symbolic: >>> from sympy.physics.quantum.spin import JzKet >>> from sympy import symbols >>> JzKet(1, 0) |1,0> >>> j, m = symbols('j m') >>> JzKet(j, m) |j,m> Rewriting the JzKet in terms of eigenkets of the Jx operator: Note: that the resulting eigenstates are JxKet's >>> JzKet(1,1).rewrite("Jx") |1,-1>/2 - 2**(1/2)*|1,0>/2 + |1,1>/2 Get the vector representation of a state in terms of the basis elements of the Jx operator: >>> from sympy.physics.quantum.represent import represent >>> from sympy.physics.quantum.spin import Jx >>> represent(JzKet(1,-1), basis=Jx) [ 1/2] [2**(1/2)/2] [ 1/2] Apply innerproducts between states: >>> from sympy.physics.quantum.innerproduct import InnerProduct >>> from sympy.physics.quantum.spin import JxBra >>> i = InnerProduct(JxBra(1,1), JzKet(1,1)) >>> i <1,1|1,1> >>> i.doit() 1/2 """ @property def dual_class(self): return JzBra def _represent_default_basis(self, **options): return self._represent_JzOp(None, **options) def _represent_JxOp(self, basis, **options): return self._represent_base(beta=3*pi/2) def _represent_JyOp(self, basis, **options): return self._represent_base(alpha=3*pi/2,beta=pi/2,gamma=pi/2) def _represent_JzOp(self, basis, **options): return self._represent_base() class JzBra(SpinState, Bra): """Eigenbra of Jz. See the JzKet for the usage of spin eigenstates. """ @property def dual_class(self): return JzKet wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/matrixutils.py0000644000175000017500000002172512014170666027221 0ustar georgeskgeorgesk"""Utilities to deal with sympy.Matrix, numpy and scipy.sparse.""" from sympy import Matrix, I, Expr, Integer from sympy.matrices import matrices from sympy.external import import_module __all__ = [ 'numpy_ndarray', 'scipy_sparse_matrix', 'sympy_to_numpy', 'sympy_to_scipy_sparse', 'numpy_to_sympy', 'scipy_sparse_to_sympy', 'flatten_scalar', 'matrix_dagger', 'to_sympy', 'to_numpy', 'to_scipy_sparse', 'matrix_tensor_product' ] # Conditionally define the base classes for numpy and scipy.sparse arrays # for use in isinstance tests. np = import_module('numpy', min_python_version=(2, 6)) if not np: class numpy_ndarray(object): pass else: numpy_ndarray = np.ndarray scipy = import_module('scipy', __import__kwargs={'fromlist':['sparse']}) if not scipy: class scipy_sparse_matrix(object): pass sparse = None else: sparse = scipy.sparse # Try to find spmatrix. if hasattr(sparse, 'base'): # Newer versions have it under scipy.sparse.base. scipy_sparse_matrix = sparse.base.spmatrix elif hasattr(sparse, 'sparse'): # Older versions have it under scipy.sparse.sparse. scipy_sparse_matrix = sparse.sparse.spmatrix def sympy_to_numpy(m, **options): """Convert a sympy Matrix/complex number to a numpy matrix or scalar.""" if not np: raise ImportError dtype = options.get('dtype','complex') if isinstance(m, Matrix): return np.matrix(m.tolist(), dtype=dtype) elif isinstance(m, Expr): if m.is_Number or m.is_NumberSymbol or m == I: return complex(m) raise TypeError('Expected Matrix or complex scalar, got: %r' % m) def sympy_to_scipy_sparse(m, **options): """Convert a sympy Matrix/complex number to a numpy matrix or scalar.""" if not np or not sparse: raise ImportError dtype = options.get('dtype','complex') if isinstance(m, Matrix): return sparse.csr_matrix(np.matrix(m.tolist(), dtype=dtype)) elif isinstance(m, Expr): if m.is_Number or m.is_NumberSymbol or m == I: return complex(m) raise TypeError('Expected Matrix or complex scalar, got: %r' % m) def scipy_sparse_to_sympy(m, **options): """Convert a scipy.sparse matrix to a sympy matrix.""" return Matrix(m.todense()) def numpy_to_sympy(m, **options): """Convert a numpy matrix to a sympy matrix.""" return Matrix(m) def to_sympy(m, **options): """Convert a numpy/scipy.sparse matrix to a sympy matrix.""" if isinstance(m, Matrix): return m elif isinstance(m, numpy_ndarray): return numpy_to_sympy(m) elif isinstance(m, scipy_sparse_matrix): return scipy_sparse_to_sympy(m) elif isinstance(m, Expr): return m raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m) def to_numpy(m, **options): """Convert a sympy/scipy.sparse matrix to a numpy matrix.""" dtype = options.get('dtype','complex') if isinstance(m, (Matrix, Expr)): return sympy_to_numpy(m, dtype=dtype) elif isinstance(m, numpy_ndarray): return m elif isinstance(m, scipy_sparse_matrix): return m.todense() raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m) def to_scipy_sparse(m, **options): """Convert a sympy/numpy matrix to a scipy.sparse matrix.""" dtype = options.get('dtype','complex') if isinstance(m, (Matrix, Expr)): return sympy_to_scipy_sparse(m, dtype=dtype) elif isinstance(m, numpy_ndarray): if not sparse: raise ImportError return sparse.csr_matrix(m) elif isinstance(m, scipy_sparse_matrix): return m raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m) def flatten_scalar(e): """Flatten a 1x1 matrix to a scalar, return larger matrices unchanged.""" if isinstance(e, Matrix): if e.shape == (1,1): e = e[0] if isinstance(e, (numpy_ndarray, scipy_sparse_matrix)): if e.shape == (1,1): e = complex(e[0,0]) return e def matrix_dagger(e): """Return the dagger of a sympy/numpy/scipy.sparse matrix.""" if isinstance(e, Matrix): return e.H elif isinstance(e, (numpy_ndarray, scipy_sparse_matrix)): return e.conjugate().transpose() raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % e) # TODO: Move this into sympy.matricies. def _sympy_tensor_product(*matrices): """Compute the tensor product of a sequence of sympy Matrices. This is the standard Kronecker product of matrices [1]. Parameters ========== matrices : tuple of Matrix instances The matrices to take the tensor product of. Returns ======= matrix : Matrix The tensor product matrix. Examples ======== >>> from sympy import I, Matrix, symbols >>> from sympy.physics.quantum.matrixutils import _sympy_tensor_product >>> m1 = Matrix([[1,2],[3,4]]) >>> m2 = Matrix([[1,0],[0,1]]) >>> _sympy_tensor_product(m1, m2) [1, 0, 2, 0] [0, 1, 0, 2] [3, 0, 4, 0] [0, 3, 0, 4] >>> _sympy_tensor_product(m2, m1) [1, 2, 0, 0] [3, 4, 0, 0] [0, 0, 1, 2] [0, 0, 3, 4] References ========== [1] http://en.wikipedia.org/wiki/Kronecker_product """ # Make sure we have a sequence of Matrices testmat = [isinstance(m, Matrix) for m in matrices] if not all(testmat): raise TypeError( 'Sequence of Matrices expected, got: %s' % repr(matrices) ) # Pull out the first element in the product. matrix_expansion = matrices[-1] # Do the tensor product working from right to left. for mat in reversed(matrices[:-1]): rows = mat.rows cols = mat.cols # Go through each row appending tensor product to. # running matrix_expansion. for i in range(rows): start = matrix_expansion*mat[i*cols] # Go through each column joining each item for j in range(cols-1): start = start.row_join( matrix_expansion*mat[i*cols+j+1] ) # If this is the first element, make it the start of the # new row. if i == 0: next = start else: next = next.col_join(start) matrix_expansion = next return matrix_expansion def _numpy_tensor_product(*product): """numpy version of tensor product of multiple arguments.""" if not np: raise ImportError answer = product[0] for item in product[1:]: answer = np.kron(answer, item) return answer def _scipy_sparse_tensor_product(*product): """scipy.sparse version of tensor product of multiple arguments.""" if not sparse: raise ImportError answer = product[0] for item in product[1:]: answer = sparse.kron(answer, item) # The final matrices will just be multiplied, so csr is a good final # sparse format. return sparse.csr_matrix(answer) def matrix_tensor_product(*product): """Compute the matrix tensor product of sympy/numpy/scipy.sparse matrices.""" if isinstance(product[0], Matrix): return _sympy_tensor_product(*product) elif isinstance(product[0], numpy_ndarray): return _numpy_tensor_product(*product) elif isinstance(product[0], scipy_sparse_matrix): return _scipy_sparse_tensor_product(*product) def _numpy_eye(n): """numpy version of complex eye.""" if not np: raise ImportError return np.matrix(np.eye(n, dtype='complex')) def _scipy_sparse_eye(n): """scipy.sparse version of complex eye.""" if not sparse: raise ImportError return sparse.eye(n, n, dtype='complex') def matrix_eye(n, **options): """Get the version of eye and tensor_product for a given format.""" format = options.get('format','sympy') if format == 'sympy': return matrices.eye(n) elif format == 'numpy': return _numpy_eye(n) elif format == 'scipy.sparse': return _scipy_sparse_eye(n) raise NotImplementedError('Invalid format: %r' % format) def _numpy_matrix_to_zero(e): """Convert a numpy zero matrix to the zero scalar.""" if not np: raise ImportError test = np.zeros_like(e) if np.allclose(e, test): return 0.0 else: return e def _scipy_sparse_matrix_to_zero(e): """Convert a scipy.sparse zero matrix to the zero scalar.""" if not np: raise ImportError edense = e.todense() test = np.zeros_like(edense) if np.allclose(edense, test): return 0.0 else: return e def matrix_to_zero(e): """Convert a zero matrix to the scalar zero.""" if isinstance(e, Matrix): if matrices.zeros(e.shape) == e: e = Integer(0) elif isinstance(e, numpy_ndarray): e = _numpy_matrix_to_zero(e) elif isinstance(e, scipy_sparse_matrix): e = _scipy_sparse_matrix_to_zero(e) return e wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/matrixcache.py0000644000175000017500000000654312014170666027125 0ustar georgeskgeorgesk"""A cache for storing small matrices in multiple formats.""" from sympy import Matrix, I, Pow, Rational, exp, pi from sympy.physics.quantum.matrixutils import ( to_sympy, to_numpy, to_scipy_sparse ) class MatrixCache(object): """A cache for small matrices in different formats. This class takes small matrices in the standard ``sympy.Matrix`` format, and then converts these to both ``numpy.matrix`` and ``scipy.sparse.csr_matrix`` matrices. These matrices are then stored for future recovery. """ def __init__(self, dtype='complex'): self._cache = {} self.dtype = dtype def cache_matrix(self, name, m): """Cache a matrix by its name. Parameters ---------- name : str A descriptive name for the matrix, like "identity2". m : list of lists The raw matrix data as a sympy Matrix. """ try: self._sympy_matrix(name, m) except ImportError: pass try: self._numpy_matrix(name, m) except ImportError: pass try: self._scipy_sparse_matrix(name, m) except ImportError: pass def get_matrix(self, name, format): """Get a cached matrix by name and format. Parameters ---------- name : str A descriptive name for the matrix, like "identity2". format : str The format desired ('sympy', 'numpy', 'scipy.sparse') """ m = self._cache.get((name, format)) if m is not None: return m raise NotImplementedError( 'Matrix with name %s and format %s is not available.' %\ (name, format) ) def _store_matrix(self, name, format, m): self._cache[(name, format)] = m def _sympy_matrix(self, name, m): self._store_matrix(name, 'sympy', to_sympy(m)) def _numpy_matrix(self, name, m): m = to_numpy(m, dtype=self.dtype) self._store_matrix(name, 'numpy', m) def _scipy_sparse_matrix(self, name, m): # TODO: explore different sparse formats. But sparse.kron will use # coo in most cases, so we use that here. m = to_scipy_sparse(m, dtype=self.dtype) self._store_matrix(name, 'scipy.sparse', m) sqrt2_inv = Pow(2, Rational(-1,2), evaluate=False) # Save the common matrices that we will need matrix_cache = MatrixCache() matrix_cache.cache_matrix('eye2', Matrix([[1,0],[0,1]])) matrix_cache.cache_matrix('op11', Matrix([[0,0],[0,1]])) # |1><1| matrix_cache.cache_matrix('op00', Matrix([[1,0],[0,0]])) # |0><0| matrix_cache.cache_matrix('op10', Matrix([[0,0],[1,0]])) # |1><0| matrix_cache.cache_matrix('op01', Matrix([[0,1],[0,0]])) # |0><1| matrix_cache.cache_matrix('X', Matrix([[0, 1], [1, 0]])) matrix_cache.cache_matrix('Y', Matrix([[0, -I], [I, 0]])) matrix_cache.cache_matrix('Z', Matrix([[1, 0], [0, -1]])) matrix_cache.cache_matrix('S', Matrix([[1, 0], [0, I]])) matrix_cache.cache_matrix('T', Matrix([[1, 0], [0, exp(I*pi/4)]])) matrix_cache.cache_matrix('H', sqrt2_inv*Matrix([[1, 1], [1, -1]])) matrix_cache.cache_matrix('Hsqrt2', Matrix([[1, 1], [1, -1]])) matrix_cache.cache_matrix('SWAP',Matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])) matrix_cache.cache_matrix('ZX', sqrt2_inv*Matrix([[1,1],[1,-1]])) matrix_cache.cache_matrix('ZY', Matrix([[I,0],[0,-I]])) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/innerproduct.py0000644000175000017500000000737112014170666027351 0ustar georgeskgeorgesk"""Symbolic inner product.""" from sympy import Expr, conjugate from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.state import KetBase, BraBase, _lbracket __all__ = [ 'InnerProduct' ] # InnerProduct is not an QExpr because it is really just a regular commutative # number. We have gone back and forth about this, but we gain a lot by having # it subclass Expr. The main challenges were getting Dagger to work # (we use _eval_conjugate) and represent (we can use atoms and subs). Having # it be an Expr, mean that there are no commutative QExpr subclasses, # which simplifies the design of everything. class InnerProduct(Expr): """An unevaluated inner product between a Bra and a Ket. Parameters ========== bra : BraBase or subclass The bra on the left side of the inner product. ket : KetBase or subclass The ket on the right side of the inner product. Examples ======== Create an InnerProduct and check its properties: >>> from sympy.physics.quantum import Bra, Ket, InnerProduct >>> b = Bra('b') >>> k = Ket('k') >>> ip = b*k >>> ip >>> ip.bra >> ip.ket |k> In simple products of kets and bras inner products will be automatically identified and created:: >>> b*k But in more complex expressions, there is ambiguity in whether inner or outer products should be created:: >>> k*b*k*b |k>*>> k*(b*k)*b *|k>* moved to the left of the expression because inner products are commutative complex numbers. References ========== http://en.wikipedia.org/wiki/Inner_product """ def __new__(cls, bra, ket, **old_assumptions): if not isinstance(ket, KetBase): raise TypeError('KetBase subclass expected, got: %r' % ket) if not isinstance(bra, BraBase): raise TypeError('BraBase subclass expected, got: %r' % ket) obj = Expr.__new__(cls, *(bra, ket), **{'commutative':True}) return obj @property def bra(self): return self.args[0] @property def ket(self): return self.args[1] def _eval_dagger(self): return InnerProduct(Dagger(self.ket), Dagger(self.bra)) def _eval_conjugate(self): return self._eval_dagger() def _sympyrepr(self, printer, *args): return '%s(%s,%s)' % (self.__class__.__name__, printer._print(self.bra, *args), printer._print(self.ket, *args)) def _sympystr(self, printer, *args): sbra = str(self.bra) sket = str(self.ket) return '%s|%s' % (sbra[:-1], sket[1:]) def _pretty(self, printer, *args): pform = prettyForm(_lbracket) pform = prettyForm(*pform.right(self.bra._print_label_pretty(printer, *args))) return prettyForm(*pform.right(self.ket._pretty(printer, *args))) def _latex(self, printer, *args): bra_label = self.bra._print_label_latex(printer, *args) ket = printer._print(self.ket, *args) return r'\left\langle %s \right. %s' % (bra_label, ket) def doit(self, **hints): try: r = self.ket._eval_innerproduct(self.bra, **hints) except NotImplementedError: try: r = conjugate( self.bra.dual._eval_innerproduct(self.ket.dual, **hints) ) except NotImplementedError: r = None if r is not None: return r return self wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/commutator.py0000644000175000017500000001372412014170666027026 0ustar georgeskgeorgesk"""The commutator: [A,B] = A*B - B*A.""" from sympy import S, Expr, Mul, Add from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.qexpr import split_commutative_parts from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.operator import Operator __all__ = [ 'Commutator' ] #----------------------------------------------------------------------------- # Commutator #----------------------------------------------------------------------------- class Commutator(Expr): """The standard commutator, in an unevaluated state. The commutator is defined [1] as: [A, B] = A*B - B*A, but in this class the commutator is initially unevaluated. To expand the commutator out, use the ``doit`` method. The arguments of the commutator are put into canonical order using ``__cmp__``, so that [B,A] becomes -[A,B]. Parameters ========== A : Expr The first argument of the commutator [A,B]. B : Expr The second argument of the commutator [A,B]. Examples ======== >>> from sympy import symbols >>> from sympy.physics.quantum import Commutator, Dagger >>> x, y = symbols('x,y') >>> A, B, C = symbols('A,B,C', commutative=False) Create some commutators and use ``doit`` to multiply them out. >>> comm = Commutator(A,B); comm [A,B] >>> comm.doit() A*B - B*A The commutator orders it arguments in canonical order:: >>> comm = Commutator(B,A); comm -[A,B] Scalar constants are factored out:: >>> Commutator(3*x*A,x*y*B) 3*x**2*y*[A,B] Using ``expand(commutator=True)``, the standard commutator expansion rules can be applied:: >>> Commutator(A+B,C).expand(commutator=True) [A,C] + [B,C] >>> Commutator(A,B+C).expand(commutator=True) [A,B] + [A,C] >>> Commutator(A*B,C).expand(commutator=True) A*[B,C] + [A,C]*B >>> Commutator(A,B*C).expand(commutator=True) B*[A,C] + [A,B]*C Commutator works with Dagger:: >>> Dagger(Commutator(A,B)) -[Dagger(A),Dagger(B)] References ========== [1] http://en.wikipedia.org/wiki/Commutator """ def __new__(cls, A, B, **old_assumptions): r = cls.eval(A, B) if r is not None: return r obj = Expr.__new__(cls, *(A, B), **{'commutative': False}) return obj @classmethod def eval(cls, a, b): """The Commutator [A,B] is on canonical form if A < B. """ if not (a and b): return S.Zero if a == b: return S.Zero if a.is_commutative or b.is_commutative: return S.Zero # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul c_part = c_part2 = [] nc_part = nc_part2 = [] if isinstance(a, Mul): c_part, nc_part = split_commutative_parts(a) if isinstance(b, Mul): c_part2, nc_part2 = split_commutative_parts(b) c_part.extend(c_part2) if c_part: a = nc_part or [a] b = nc_part2 or [b] return Mul(*c_part)*cls(Mul(*a),Mul(*b)) # Canonical ordering of arguments if a.compare(b) == 1: return S.NegativeOne*cls(b,a) def _eval_expand_commutator(self, **hints): A = self.args[0].expand(**hints) B = self.args[1].expand(**hints) result = None if isinstance(A, Add): # [A+B,C] -> [A,C] + [B,C] result = Add( *[Commutator(term,B).expand(**hints)\ for term in A.args] ) elif isinstance(B, Add): # [A,B+C] -> [A,B] + [A,C] result = Add( *[Commutator(A,term).expand(**hints)\ for term in B.args] ) elif isinstance(A, Mul): # [A*B,C] -> A*[B,C] + [A,C]*B a = A.args[0] b = Mul(*A.args[1:]) c = B comm1 = Commutator(b,c).expand(**hints) comm2 = Commutator(a,c).expand(**hints) first = Mul(a, comm1) second = Mul(comm2, b) result = Add(first, second) elif isinstance(B, Mul): # [A,B*C] -> [A,B]*C + B*[A,C] a = A b = B.args[0] c = Mul(*B.args[1:]) comm1 = Commutator(a,b).expand(**hints) comm2 = Commutator(a,c).expand(**hints) first = Mul(comm1, c) second = Mul(b, comm2) result = Add(first, second) if result is None: # No changes, so return self return self else: return result def doit(self, **hints): A = self.args[0] B = self.args[1] if isinstance(A, Operator) and isinstance(B, Operator): try: comm = A._eval_commutator(B, **hints) except NotImplementedError: try: comm = -1*B._eval_commutator(A, **hints) except NotImplementedError: comm = None if comm is not None: return comm.doit(**hints) return (A*B - B*A).doit(**hints) def _eval_dagger(self): return Commutator(Dagger(self.args[1]), Dagger(self.args[0])) def _sympyrepr(self, printer, *args): return "%s(%s,%s)" % (self.__class__.__name__, self.args[0],\ self.args[1]) def _sympystr(self, printer, *args): return "[%s,%s]" % (self.args[0], self.args[1]) def _pretty(self, printer, *args): pform = printer._print(self.args[0], *args) pform = prettyForm(*pform.right((prettyForm(u',')))) pform = prettyForm(*pform.right((printer._print(self.args[1], *args)))) pform = prettyForm(*pform.parens(left='[', right=']')) return pform def _latex(self, printer, *args): return "\\left[%s,%s\\right]" % tuple([ printer._print(arg, *args) for arg in self.args]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/anticommutator.py0000644000175000017500000001061212014170666027673 0ustar georgeskgeorgesk"""The anti-commutator: {A,B} = A*B + B*A.""" from sympy import S, Expr, Mul, Integer from sympy.printing.pretty.stringpict import prettyForm from sympy.physics.quantum.qexpr import split_commutative_parts from sympy.physics.quantum.operator import Operator from sympy.physics.quantum.dagger import Dagger __all__ = [ 'AntiCommutator' ] #----------------------------------------------------------------------------- # Anti-commutator #----------------------------------------------------------------------------- class AntiCommutator(Expr): """The standard anticommutator, in an unevaluated state. The commutator is defined [1] as: {A, B} = A*B + B*A, but in this class the anticommutator is initially unevaluated. To expand the anticommutator out, use the ``doit`` method. The arguments of the anticommutator are put into canonical order using ``__cmp__``, so that {B,A} becomes {A,B}. Parameters ========== A : Expr The first argument of the anticommutator {A,B}. B : Expr The second argument of the anticommutator {A,B}. Examples ======== >>> from sympy import symbols >>> from sympy.physics.quantum import AntiCommutator >>> from sympy.physics.quantum import Operator, Dagger >>> x, y = symbols('x,y') >>> A = Operator('A') >>> B = Operator('B') Create an anticommutator and use ``doit`` to multiply them out. >>> ac = AntiCommutator(A,B); ac {A,B} >>> ac.doit() A*B + B*A The commutator orders it arguments in canonical order:: >>> ac = AntiCommutator(B,A); ac {A,B} Scalar constants are factored out:: >>> AntiCommutator(3*x*A,x*y*B) 3*x**2*y*{A,B} Dagger is alto handled:: >>> Dagger(AntiCommutator(A,B)) {Dagger(A),Dagger(B)} References ========== [1] http://en.wikipedia.org/wiki/Commutator """ def __new__(cls, A, B, **old_assumptions): r = cls.eval(A, B) if r is not None: return r obj = Expr.__new__(cls, *(A, B), **{'commutative': False}) return obj @classmethod def eval(cls, a, b): """The Commutator [A,B] is on canonical form if A < B. """ if not (a and b): return S.Zero if a == b: return Integer(2)*a**2 if a.is_commutative or b.is_commutative: return Integer(2)*a*b # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul c_part = [] nc_part = [] nc_part2 = [] if isinstance(a, Mul): c_part, nc_part = split_commutative_parts(a) if isinstance(b, Mul): c_part2, nc_part2 = split_commutative_parts(b) c_part.extend(c_part2) if c_part: a = nc_part or [a] b = nc_part2 or [b] return Mul(Mul(*c_part), cls(Mul(*a), Mul(*b))) # Canonical ordering of arguments if a.compare(b) == 1: return cls(b,a) def _eval_expand_anticommutator(self, **hints): # No changes, so return self return self def doit(self, **hints): A = self.args[0] B = self.args[1] if isinstance(A, Operator) and isinstance(B, Operator): try: comm = A._eval_anticommutator(B, **hints) except NotImplementedError: try: comm = B._eval_anticommutator(A, **hints) except NotImplementedError: comm = None if comm is not None: return comm.doit(**hints) return (A*B + B*A).doit(**hints) def _eval_dagger(self): return AntiCommutator(Dagger(self.args[0]), Dagger(self.args[1])) def _sympyrepr(self, printer, *args): return "%s(%s,%s)" % (self.__class__.__name__, self.args[0],\ self.args[1]) def _sympystr(self, printer, *args): return "{%s,%s}" % (self.args[0], self.args[1]) def _pretty(self, printer, *args): pform = printer._print(self.args[0], *args) pform = prettyForm(*pform.right((prettyForm(u',')))) pform = prettyForm(*pform.right((printer._print(self.args[1], *args)))) pform = prettyForm(*pform.parens(left='{', right='}')) return pform def _latex(self, printer, *args): return "\\left{}%s,%s\\right}" % tuple([ printer._print(arg, *args) for arg in self.args]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/constants.py0000644000175000017500000000234012014170666026640 0ustar georgeskgeorgesk"""Constants (like hbar) related to quantum mechanics.""" from sympy.core.numbers import NumberSymbol from sympy.printing.pretty.stringpict import prettyForm import sympy.mpmath.libmp as mlib #----------------------------------------------------------------------------- # Constants #----------------------------------------------------------------------------- __all__ = [ 'hbar' ] class HBar(NumberSymbol): """Reduced Plank's constant in numerical and symbolic form [1]. Examples ======== >>> from sympy.physics.quantum.constants import hbar >>> hbar.evalf() 1.05457162000000e-34 References ========== [1] http://en.wikipedia.org/wiki/Planck_constant """ is_real = True is_positive = True is_negative = False is_irrational = True __slots__ = [] def _as_mpf_val(self, prec): return mlib.from_float(1.05457162e-34, prec) def _sympyrepr(self, printer, *args): return 'HBar()' def _sympystr(self, printer, *args): return 'hbar' def _pretty(self, printer, *args): return prettyForm(u'\u210f') def _latex(self, printer, *args): return r'\hbar' # Create an instance for everyone to use. hbar = HBar() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/qft.py0000644000175000017500000001414012014170666025417 0ustar georgeskgeorgesk"""An implementation of qubits and gates acting on them. Todo: * Update docstrings. * Update tests. * Implement apply using decompose. * Implement represent using decompose or something smarter. For this to work we first have to implement represent for SWAP. * Decide if we want upper index to be inclusive in the constructor. * Fix the printing of Rk gates in plotting. """ from sympy import Expr, Matrix, exp, I, pi, Integer, Symbol from sympy.functions import sqrt from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qexpr import QuantumError, QExpr from sympy.matrices.matrices import eye from sympy.physics.quantum.tensorproduct import matrix_tensor_product from sympy.physics.quantum.gate import ( Gate, HadamardGate, SwapGate, OneQubitGate, CGate, PhaseGate, TGate, ZGate ) __all__ = [ 'QFT', 'IQFT', 'RkGate', 'Rk' ] #----------------------------------------------------------------------------- # Fourier stuff #----------------------------------------------------------------------------- class RkGate(OneQubitGate): """This is the R_k gate of the QTF.""" gate_name = u'Rk' gate_name_latex = u'R' def __new__(cls, *args, **old_assumptions): if len(args) != 2: raise QuantumError( 'Rk gates only take two arguments, got: %r' % args ) # For small k, Rk gates simplify to other gates, using these # substitutions give us familiar results for the QFT for small numbers # of qubits. target = args[0] k = args[1] if k == 1: return ZGate(target) elif k == 2: return PhaseGate(target) elif k == 3: return TGate(target) args = cls._eval_args(args) inst = Expr.__new__(cls, *args, **{'commutative':False}) inst.hilbert_space = cls._eval_hilbert_space(args) return inst @classmethod def _eval_args(cls, args): # Fall back to this, because Gate._eval_args assumes that args is # all targets and can't contain duplicates. return QExpr._eval_args(args) @property def k(self): return self.label[1] @property def targets(self): return self.label[:1] @property def gate_name_plot(self): return r'$%s_%s$' % (self.gate_name_latex, str(self.k)) def get_target_matrix(self, format='sympy'): if format == 'sympy': return Matrix([[1,0],[0,exp(Integer(2)*pi*I/(Integer(2)**self.k))]]) raise NotImplementedError('Invalid format for the R_k gate: %r' % format) Rk = RkGate class Fourier(Gate): """Superclass of Quantum Fourier and Inverse Quantum Fourier Gates.""" @classmethod def _eval_args(self, args): if len(args) != 2: raise QuantumError( 'QFT/IQFT only takes two arguments, got: %r' % args ) if args[0] >= args[1]: raise QuantumError("Start must be smaller than finish") return Gate._eval_args(args) def _represent_default_basis(self, **options): return self._represent_ZGate(None, **options) def _represent_ZGate(self, basis, **options): """ Represents the (I)QFT In the Z Basis """ nqubits = options.get('nqubits',0) if nqubits == 0: raise QuantumError('The number of qubits must be given as nqubits.') if nqubits < self.min_qubits: raise QuantumError( 'The number of qubits %r is too small for the gate.' % nqubits ) size = self.size omega = self.omega #Make a matrix that has the basic Fourier Transform Matrix arrayFT = [[omega**(i*j%size)/sqrt(size) for i in range(size)] for j in range(size)] matrixFT = Matrix(arrayFT) #Embed the FT Matrix in a higher space, if necessary if self.label[0] != 0: matrixFT = matrix_tensor_product(eye(2**self.label[0]), matrixFT) if self.min_qubits < nqubits: matrixFT = matrix_tensor_product(matrixFT, eye(2**(nqubits-self.min_qubits))) return matrixFT @property def targets(self): return range(self.label[0],self.label[1]) @property def min_qubits(self): return self.label[1] @property def size(self): """Size is the size of the QFT matrix""" return 2**(self.label[1]-self.label[0]) @property def omega(self): return Symbol('omega') class QFT(Fourier): """The forward quantum Fourier transform.""" gate_name = u'QFT' gate_name_latex = u'QFT' def decompose(self): """Decomposes QFT into elementary gates.""" start = self.label[0] finish = self.label[1] circuit = 1 for level in reversed(range(start, finish)): circuit = HadamardGate(level)*circuit for i in range(level-start): circuit = CGate(level-i-1, RkGate(level, i+2))*circuit #FIXME-py3k: TypeError: 'Rational' object cannot be interpreted as an integer for i in range((finish-start)/2): circuit = SwapGate(i+start, finish-i-1)*circuit return circuit def _apply_operator_Qubit(self, qubits, **options): return qapply(self.decompose()*qubits) def _eval_inverse(self): return IQFT(*self.args) @property def omega(self): return exp(2*pi*I/self.size) class IQFT(Fourier): """The inverse quantum Fourier transform.""" gate_name = u'IQFT' gate_name_latex = u'{QFT^{-1}}' def decompose(self): """Decomposes IQFT into elementary gates.""" start = self.args[0] finish = self.args[1] circuit = 1 for i in range((finish-start)/2): circuit = SwapGate(i+start, finish-i-1)*circuit for level in range(start, finish): for i in reversed(range(level-start)): circuit = CGate(level-i-1, RkGate(level, -i-2))*circuit circuit = HadamardGate(level)*circuit return circuit def _eval_inverse(self): return QFT(*self.args) @property def omega(self): return exp(-2*pi*I/self.size) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/kronecker.py0000644000175000017500000000512612014170666026614 0ustar georgeskgeorgesk"""Discrete Kronecker delta function.""" from sympy import S, Function from sympy.printing.pretty.stringpict import prettyForm, stringPict __all__ = [ 'KroneckerDelta' ] class KroneckerDelta(Function): """The discrete, or Kronecker, delta function. A function that takes in two integers i and j. It returns 0 if i and j are not equal or it returns 1 if i and j are equal. Parameters ========== i : Number, Symbol The first index of the delta function. j : Number, Symbol The second index of the delta function. Examples ======== A simple example with integer indices:: >>> from sympy.physics.quantum import KroneckerDelta >>> KroneckerDelta(1,2) 0 >>> KroneckerDelta(3,3) 1 Symbolic indices:: >>> from sympy import symbols >>> i, j, k = symbols('i j k') >>> KroneckerDelta(i, j) d(i,j) >>> KroneckerDelta(i, i) 1 >>> KroneckerDelta(i, i+1) 0 >>> KroneckerDelta(i, i+1+k) d(i,i + k + 1) References ========== http://en.wikipedia.org/wiki/Kronecker_delta """ nargs = 2 is_commutative=True @classmethod def eval(cls, i, j): """ Evaluates the discrete delta function. """ if i > j: return cls(j,i) diff = i-j if diff == 0: return S.One elif diff.is_number: return S.Zero def _eval_subs(self, old, new): r = KroneckerDelta(self.args[0].subs(old, new), self.args[1].subs(old,\ new)) return r def _eval_dagger(self): return self def _latex_(self,printer): return "\\delta_{%s%s}"% (self.args[0].name,self.args[1].name) def _sympyrepr(self, printer, *args): return "%s(%s,%s)"% (self.__class__.__name__, self.args[0],\ self.args[1]) def _sympystr(self, printer, *args): return 'd(%s,%s)'% (self.args[0],self.args[1]) def _pretty(self, printer, *args): pform = printer._print(self.args[0], *args) pform = prettyForm(*pform.right((prettyForm(',')))) pform = prettyForm(*pform.right((printer._print(self.args[1], *args)))) a = stringPict(u'\u03b4') b = pform top = stringPict(*b.left(' '*a.width())) bot = stringPict(*a.right(' '*b.width())) return prettyForm(binding=prettyForm.POW, *bot.below(top)) def _latex(self, printer, *args): i = printer._print(self.args[0], *args) j = printer._print(self.args[1], *args) return '\\delta_{%s %s}' % (i,j) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/cartesian.py0000644000175000017500000000546412014170666026607 0ustar georgeskgeorgesk"""Operators and states for 1D cartesian position and momentum.""" from sympy import I, S, sqrt, pi from sympy import exp from sympy import Interval, DiracDelta from sympy.physics.quantum.operator import HermitianOperator from sympy.physics.quantum.state import Ket, Bra from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.hilbert import L2 __all__ = [ 'XOp', 'PxOp', 'X', 'Px', 'XKet', 'XBra', 'PxKet', 'PxBra' ] class XOp(HermitianOperator): """1D cartesian position operator.""" @classmethod def _eval_hilbert_space(self, args): return L2(Interval(S.NegativeInfinity, S.Infinity)) def _eval_commutator_PxOp(self, other): return I*hbar def _apply_operator_XKet(self, ket): return ket.position*ket class PxOp(HermitianOperator): """1D cartesian momentum operator.""" @classmethod def _eval_hilbert_space(self, args): return L2(Interval(S.NegativeInfinity, S.Infinity)) def _apply_operator_PxKet(self, ket): return ket.momentum*ket X = XOp('X') Px = PxOp('Px') class XKet(Ket): """1D cartesian position eigenket.""" @property def dual_class(self): return XBra @property def position(self): """The position of the state.""" return self.label[0] def _eval_innerproduct_XBra(self, bra, **hints): return DiracDelta(self.position-bra.position) def _eval_innerproduct_PxBra(self, bra, **hints): return exp(-I*self.position*bra.momentum/hbar)/sqrt(2*pi*hbar) def _represent_default_basis(self, **options): return self._represent_XOp(None, **options) def _represent_XOp(self, basis, **options): return self.position class XBra(Bra): """1D cartesian position eigenbra.""" @property def dual_class(self): return XKet @property def position(self): """The position of the state.""" return self.label[0] class PxKet(Ket): """1D cartesian momentum eigenket.""" @property def dual_class(self): return PxBra @property def momentum(self): """The momentum of the state.""" return self.label[0] def _eval_innerproduct_XBra(self, bra, **hints): return exp(I*self.momentum*bra.position/hbar)/sqrt(2*pi*hbar) def _eval_innerproduct_PxBra(self, bra, **hints): return DiracDelta(self.momentum-bra.momentum) def _represent_default_basis(self, **options): return self._represent_PxOp(None, **options) def _represent_PxOp(self, basis, **options): return self.momentum class PxBra(Bra): """1D cartesian momentum eigenbra.""" @property def dual_class(self): return PxKet @property def momentum(self): """The momentum of the state.""" return self.label[0] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/0000755000175000017500000000000012014170666025415 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_gate.py0000644000175000017500000002407512014170666027756 0ustar georgeskgeorgeskfrom sympy import exp, symbols, sqrt, I, pi, Mul, Integer from sympy.matrices.matrices import Matrix from sympy.physics.quantum.gate import (XGate, YGate, ZGate, random_circuit, CNOT, IdentityGate, H, X, Y, S, T, Z, SwapGate, gate_simp, gate_sort, CNotGate, TGate, HadamardGate, PhaseGate, UGate, CGate) from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.represent import represent from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import Qubit, IntQubit, qubit_to_matrix,\ matrix_to_qubit from sympy.physics.quantum.matrixutils import matrix_to_zero from sympy.physics.quantum import Dagger def test_gate(): """Test a basic gate.""" h = HadamardGate(1) assert h.min_qubits == 2 assert h.nqubits == 1 def test_UGate(): a,b,c,d = symbols('a,b,c,d') uMat = Matrix([[a,b],[c,d]]) # Test basic case where gate exists in 1-qubit space u1 = UGate((0,), uMat) assert represent(u1, nqubits = 1) == uMat assert qapply(u1*Qubit('0')) == a*Qubit('0') + c*Qubit('1') assert qapply(u1*Qubit('1')) == b*Qubit('0') + d*Qubit('1') # Test case where gate exists in a larger space u2 = UGate((1,), uMat) u2Rep = represent(u2, nqubits=2) for i in range(4): assert u2Rep*qubit_to_matrix(IntQubit(i,2)) ==\ qubit_to_matrix(qapply(u2*IntQubit(i,2))) def test_cgate(): """Test the general CGate.""" # Test single control functionality CNOTMatrix = Matrix([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]) assert represent(CGate(1, XGate(0)), nqubits=2) == CNOTMatrix # Test multiple control bit functionality ToffoliGate = CGate((1,2), XGate(0)) assert represent(ToffoliGate, nqubits=3) == \ Matrix([[1,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0],[0,0,1,0,0,0,0,0],\ [0,0,0,1,0,0,0,0],[0,0,0,0,1,0,0,0],[0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1],\ [0,0,0,0,0,0,1,0]]) ToffoliGate = CGate((3,0), XGate(1)) assert qapply(ToffoliGate*Qubit('1001')) == \ matrix_to_qubit(represent(ToffoliGate*Qubit('1001'), nqubits=4)) assert qapply(ToffoliGate*Qubit('0000')) == \ matrix_to_qubit(represent(ToffoliGate*Qubit('0000'), nqubits=4)) CYGate = CGate(1, YGate(0)) CYGate_matrix = Matrix(((1,0,0,0),(0,1,0,0),(0,0,0,-I),(0,0,I,0))) # Test 2 qubit controlled-Y gate decompose method. assert represent(CYGate.decompose(), nqubits=2) == CYGate_matrix CZGate = CGate(0, ZGate(1)) CZGate_matrix = Matrix(((1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,-1))) assert qapply(CZGate*Qubit('11')) == -Qubit('11') assert matrix_to_qubit(represent(CZGate*Qubit('11'),nqubits=2)) ==\ -Qubit('11') # Test 2 qubit controlled-Z gate decompose method. assert represent(CZGate.decompose(), nqubits=2) == CZGate_matrix CPhaseGate = CGate(0, PhaseGate(1)) assert qapply(CPhaseGate*Qubit('11')) ==\ I*Qubit('11') assert matrix_to_qubit(represent(CPhaseGate*Qubit('11'), nqubits=2)) == \ I*Qubit('11') def test_UGate_CGate_combo(): a,b,c,d = symbols('a,b,c,d') uMat = Matrix([[a,b],[c,d]]) cMat = Matrix([[1,0,0,0],[0,1,0,0],[0,0,a,b],[0,0,c,d]]) # Test basic case where gate exists in 1-qubit space. u1 = UGate((0,), uMat) cu1 = CGate(1, u1) assert represent(cu1, nqubits = 2) == cMat assert qapply(cu1*Qubit('10')) == a*Qubit('10') + c*Qubit('11') assert qapply(cu1*Qubit('11')) == b*Qubit('10') + d*Qubit('11') assert qapply(cu1*Qubit('01')) == Qubit('01') assert qapply(cu1*Qubit('00')) == Qubit('00') # Test case where gate exists in a larger space. u2 = UGate((1,), uMat) u2Rep = represent(u2, nqubits=2) for i in range(4): assert u2Rep*qubit_to_matrix(IntQubit(i,2)) ==\ qubit_to_matrix(qapply(u2*IntQubit(i,2))) def test_represent_hadamard(): """Test the representation of the hadamard gate.""" circuit = HadamardGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) # Check that the answers are same to within an epsilon. assert answer == Matrix([1/sqrt(2),1/sqrt(2), 0, 0]) def test_represent_xgate(): """Test the representation of the X gate.""" circuit = XGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert Matrix([0, 1, 0, 0]) == answer def test_represent_ygate(): """Test the representation of the Y gate.""" circuit = YGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert answer[0] == 0 and answer[1] == I and \ answer[2] == 0 and answer[3] == 0 def test_represent_zgate(): """Test the representation of the Z gate.""" circuit = ZGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert Matrix([1, 0, 0, 0]) == answer def test_represent_phasegate(): """Test the representation of the S gate.""" circuit = PhaseGate(0)*Qubit('01') answer = represent(circuit, nqubits=2) assert Matrix([0, I,0,0]) == answer def test_represent_tgate(): """Test the representation of the T gate.""" circuit = TGate(0)*Qubit('01') assert Matrix([0, exp(I*pi/4), 0, 0]) == represent(circuit, nqubits=2) def test_compound_gates(): """Test a compound gate representation.""" circuit = YGate(0)*ZGate(0)*XGate(0)*HadamardGate(0)*Qubit('00') answer = represent(circuit, nqubits=2) assert Matrix([I/sqrt(2),I/sqrt(2), 0, 0]) == answer def test_cnot_gate(): """Test the CNOT gate.""" circuit = CNotGate(1,0) assert represent(circuit, nqubits=2) ==\ Matrix([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]) circuit = circuit*Qubit('111') assert matrix_to_qubit(represent(circuit, nqubits=3)) ==\ qapply(circuit) def test_gate_sort(): """Test gate_sort.""" for g in (X, Y, Z, H, S, T): assert gate_sort(g(2)*g(1)*g(0)) == g(0)*g(1)*g(2) e = gate_sort(X(1)*H(0)**2*CNOT(0,1)*X(1)*X(0)) assert e == H(0)**2*CNOT(0,1)*X(0)*X(1)**2 assert gate_sort(Z(0)*X(0)) == -X(0)*Z(0) assert gate_sort(Z(0)*X(0)**2) == X(0)**2*Z(0) assert gate_sort(Y(0)*H(0)) == -H(0)*Y(0) assert gate_sort(Y(0)*X(0)) == -X(0)*Y(0) assert gate_sort(Z(0)*Y(0)) == -Y(0)*Z(0) assert gate_sort(T(0)*S(0)) == S(0)*T(0) assert gate_sort(Z(0)*S(0)) == S(0)*Z(0) assert gate_sort(Z(0)*T(0)) == T(0)*Z(0) assert gate_sort(Z(0)*CNOT(0,1)) == CNOT(0,1)*Z(0) assert gate_sort(S(0)*CNOT(0,1)) == CNOT(0,1)*S(0) assert gate_sort(T(0)*CNOT(0,1)) == CNOT(0,1)*T(0) assert gate_sort(X(1)*CNOT(0,1)) == CNOT(0,1)*X(1) # This takes a long time and should only be uncommented once in a while. # nqubits = 5 # ngates = 10 # trials = 10 # for i in range(trials): # c = random_circuit(ngates, nqubits) # assert represent(c, nqubits=nqubits) ==\ # represent(gate_sort(c), nqubits=nqubits) def test_gate_simp(): """Test gate_simp.""" e = H(0)*X(1)*H(0)**2*CNOT(0,1)*X(1)**3*X(0)*Z(3)**2*S(4)**3 assert gate_simp(e) == H(0)*CNOT(0,1)*S(4)*X(0)*Z(4) assert gate_simp(X(0)*X(0)) == 1 assert gate_simp(Y(0)*Y(0)) == 1 assert gate_simp(Z(0)*Z(0)) == 1 assert gate_simp(H(0)*H(0)) == 1 assert gate_simp(T(0)*T(0)) == S(0) assert gate_simp(S(0)*S(0)) == Z(0) assert gate_simp(Integer(1)) == Integer(1) assert gate_simp(X(0)**2 + Y(0)**2) == Integer(2) def test_swap_gate(): """Test the SWAP gate.""" swap_gate_matrix = Matrix(((1,0,0,0),(0,0,1,0),(0,1,0,0),(0,0,0,1))) assert represent(SwapGate(1,0).decompose(), nqubits=2) == swap_gate_matrix assert qapply(SwapGate(1,3)*Qubit('0010')) == Qubit('1000') nqubits = 4 for i in range(nqubits): for j in range(i): assert represent(SwapGate(i,j), nqubits=nqubits) ==\ represent(SwapGate(i,j).decompose(), nqubits=nqubits) def test_one_qubit_commutators(): """Test single qubit gate commutation relations.""" for g1 in (IdentityGate, X, Y, Z, H, T, S): for g2 in (IdentityGate, X, Y, Z, H, T, S): e = Commutator(g1(0),g2(0)) a = matrix_to_zero(represent(e, nqubits=1, format='sympy')) b = matrix_to_zero(represent(e.doit(), nqubits=1, format='sympy')) assert a == b e = Commutator(g1(0),g2(1)) assert e.doit() == 0 def test_one_qubit_anticommutators(): """Test single qubit gate anticommutation relations.""" for g1 in (IdentityGate, X, Y, Z, H): for g2 in (IdentityGate, X, Y, Z, H): e = AntiCommutator(g1(0),g2(0)) a = matrix_to_zero(represent(e, nqubits=1, format='sympy')) b = matrix_to_zero(represent(e.doit(), nqubits=1, format='sympy')) assert a == b e = AntiCommutator(g1(0),g2(1)) a = matrix_to_zero(represent(e, nqubits=2, format='sympy')) b = matrix_to_zero(represent(e.doit(), nqubits=2, format='sympy')) assert a == b def test_cnot_commutators(): """Test commutators of involving CNOT gates.""" assert Commutator(CNOT(0,1),Z(0)).doit() == 0 assert Commutator(CNOT(0,1),T(0)).doit() == 0 assert Commutator(CNOT(0,1),S(0)).doit() == 0 assert Commutator(CNOT(0,1),X(1)).doit() == 0 assert Commutator(CNOT(0,1),CNOT(0,1)).doit() == 0 assert Commutator(CNOT(0,1),CNOT(0,2)).doit() == 0 assert Commutator(CNOT(0,2),CNOT(0,1)).doit() == 0 assert Commutator(CNOT(1,2),CNOT(1,0)).doit() == 0 def test_random_circuit(): c = random_circuit(10,3) assert isinstance(c, Mul) m = represent(c, nqubits=3) assert m.shape == (8,8) assert isinstance(m, Matrix) def test_hermitian_XGate(): x = XGate(1, 2) x_dagger = Dagger(x) assert (x == x_dagger) def test_hermitian_YGate(): y = YGate(1, 2) y_dagger = Dagger(y) assert (y == y_dagger) def test_hermitian_ZGate(): z = ZGate(1, 2) z_dagger = Dagger(z) assert (z == z_dagger) def test_unitary_XGate(): x = XGate(1, 2) x_dagger = Dagger(x) assert (x*x_dagger == 1) def test_unitary_YGate(): y = YGate(1, 2) y_dagger = Dagger(y) assert (y*y_dagger == 1) def test_unitary_ZGate(): z = ZGate(1, 2) z_dagger = Dagger(z) assert (z*z_dagger == 1) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_piab.py0000644000175000017500000000156712014170666027752 0ustar georgeskgeorgesk"""Tests for piab.py""" from sympy import S, Interval, symbols, I, DiracDelta, exp, sqrt, pi, sin from sympy.physics.quantum import L2, qapply, hbar, represent from sympy.physics.quantum import KroneckerDelta from sympy.physics.quantum.piab import PIABHamiltonian, PIABKet, PIABBra, m, L x, n = symbols('x,n') i, j = symbols('i,j') def test_H(): assert PIABHamiltonian('H').hilbert_space ==\ L2(Interval(S.NegativeInfinity,S.Infinity)) assert qapply(PIABHamiltonian('H')*PIABKet(n)) ==\ (n**2*pi**2*hbar**2)/(2*m*L**2)*PIABKet(n) def test_states(): assert PIABKet(n).dual_class == PIABBra assert PIABKet(n).hilbert_space ==\ L2(Interval(S.NegativeInfinity,S.Infinity)) assert represent(PIABKet(n)) == sqrt(2/L)*sin(n*pi*x/L) assert (PIABBra(i)*PIABKet(j)).doit() == KroneckerDelta(i, j) assert PIABBra(n).dual_class == PIABKet wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_constants.py0000644000175000017500000000050412014170666031041 0ustar georgeskgeorgeskfrom sympy import Float from sympy.physics.quantum.constants import hbar def test_hbar(): assert hbar.is_commutative == True assert hbar.is_real == True assert hbar.is_positive == True assert hbar.is_negative == False assert hbar.is_irrational == True assert hbar.evalf() == Float(1.05457162e-34) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_represent.py0000644000175000017500000000777312014170666031053 0ustar georgeskgeorgeskfrom sympy import Matrix, I, Float, Integer from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.represent import represent from sympy.physics.quantum.state import Bra, Ket from sympy.physics.quantum.operator import Operator, OuterProduct from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.tensorproduct import matrix_tensor_product from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.matrixutils import ( to_sympy, to_numpy, to_scipy_sparse, numpy_ndarray, scipy_sparse_matrix ) from sympy.external import import_module from sympy.utilities.pytest import skip Amat = Matrix([[1,I],[-I,1]]) Bmat = Matrix([[1,2],[3,4]]) Avec = Matrix([[1],[I]]) class AKet(Ket): @property def dual_class(self): return ABra def _represent_default_basis(self, **options): return self._represent_AOp(None, **options) def _represent_AOp(self, basis, **options): return Avec class ABra(Bra): @property def dual_class(self): return AKet class AOp(Operator): def _represent_default_basis(self, **options): return self._represent_AOp(None, **options) def _represent_AOp(self, basis, **options): return Amat class BOp(Operator): def _represent_default_basis(self, **options): return self._represent_AOp(None, **options) def _represent_AOp(self, basis, **options): return Bmat k = AKet('a') b = ABra('a') A = AOp('A') B = BOp('B') _tests = [ # Bra (b, Dagger(Avec)), (Dagger(b), Avec), # Ket (k, Avec), (Dagger(k), Dagger(Avec)), # Operator (A, Amat), (Dagger(A), Dagger(Amat)), # OuterProduct (OuterProduct(k,b), Avec*Avec.H), # TensorProduct (TensorProduct(A,B), matrix_tensor_product(Amat,Bmat)), # Pow (A**2, Amat**2), # Add/Mul (A*B + 2*A, Amat*Bmat + 2*Amat), # Commutator (Commutator(A,B), Amat*Bmat - Bmat*Amat), # AntiCommutator (AntiCommutator(A,B), Amat*Bmat + Bmat*Amat), # InnerProduct (InnerProduct(b,k), (Avec.H*Avec)[0]) ] def test_format_sympy(): for test in _tests: lhs = represent(test[0], basis=A, format='sympy') rhs = to_sympy(test[1]) assert lhs == rhs def test_scalar_sympy(): assert represent(Integer(1)) == Integer(1) assert represent(Float(1.0)) == Float(1.0) assert represent(1.0+I) == 1.0+I np = import_module('numpy', min_python_version=(2, 6)) def test_format_numpy(): if not np: skip("numpy not installed or Python too old.") for test in _tests: lhs = represent(test[0], basis=A, format='numpy') rhs = to_numpy(test[1]) if isinstance(lhs, numpy_ndarray): assert (lhs == rhs).all() else: assert lhs == rhs def test_scalar_numpy(): if not np: skip("numpy not installed or Python too old.") assert represent(Integer(1), format='numpy') == 1 assert represent(Float(1.0), format='numpy') == 1.0 assert represent(1.0+I, format='numpy') == 1.0+1.0j scipy = import_module('scipy', __import__kwargs={'fromlist':['sparse']}) def test_format_scipy_sparse(): if not np: skip("numpy not installed or Python too old.") if not scipy: skip("scipy not installed.") for test in _tests: lhs = represent(test[0], basis=A, format='scipy.sparse') rhs = to_scipy_sparse(test[1]) if isinstance(lhs, scipy_sparse_matrix): assert np.linalg.norm((lhs-rhs).todense()) == 0.0 else: assert lhs == rhs def test_scalar_scipy_sparse(): if not np: skip("numpy not installed or Python too old.") if not scipy: skip("scipy not installed.") assert represent(Integer(1), format='scipy.sparse') == 1 assert represent(Float(1.0), format='scipy.sparse') == 1.0 assert represent(1.0+I, format='scipy.sparse') == 1.0+1.0j wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_innerproduct.py0000644000175000017500000000323712014170666031547 0ustar georgeskgeorgeskfrom sympy import I, Integer, srepr, latex, pretty from sympy.physics.quantum.innerproduct import InnerProduct from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.state import Bra, Ket, StateBase def test_innerproduct(): k = Ket('k') b = Bra('b') ip = InnerProduct(b,k) assert isinstance(ip, InnerProduct) assert ip.bra == b assert ip.ket == k assert b*k == InnerProduct(b,k) assert k*(b*k)*b == k*InnerProduct(b,k)*b assert InnerProduct(b,k).subs(b,Dagger(k)) == Dagger(k)*k def test_innerproduct_dagger(): k = Ket('k') b = Bra('b') ip = b*k assert Dagger(ip) == Dagger(k)*Dagger(b) class FooState(StateBase): pass class FooKet(Ket, FooState): @property def dual_class(self): return FooBra def _eval_innerproduct_FooBra(self, bra): return Integer(1) def _eval_innerproduct_BarBra(self, bra): return I class FooBra(Bra, FooState): @property def dual_class(self): return FooKet class BarState(StateBase): pass class BarKet(Ket, BarState): @property def dual_class(self): return BarBra class BarBra(Bra, BarState): @property def dual_class(self): return BarKet def test_doit(): f = FooKet('foo') b = BarBra('bar') assert InnerProduct(b,f).doit() == I assert InnerProduct(Dagger(f),Dagger(b)).doit() == -I assert InnerProduct(Dagger(f),f).doit() == Integer(1) def test_printing(): psi = Ket('psi') ip = Dagger(psi)*psi assert pretty(ip, use_unicode=True) == u'\u27e8\u03c8\u2758\u03c8\u27e9' assert latex(ip) == r"\left\langle \psi \right. {\left|\psi\right\rangle }" wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_operator.py0000644000175000017500000000445712014170666030673 0ustar georgeskgeorgeskfrom sympy import Symbol, Integer, Mul from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.hilbert import HilbertSpace from sympy.physics.quantum.operator import ( Operator, UnitaryOperator, HermitianOperator, OuterProduct ) from sympy.physics.quantum.state import Ket, Bra def test_operator(): A = Operator('A') B = Operator('B') C = Operator('C') assert isinstance(A, Operator) assert isinstance(A, QExpr) assert A.label == (Symbol('A'),) assert A.is_commutative == False assert A.hilbert_space == HilbertSpace() assert A*B != B*A assert (A*(B+C)).expand() == A*B + A*C assert ((A+B)**2).expand() == A**2 + A*B + B*A + B**2 def test_operator_inv(): A = Operator('A') assert A*A.inv() == 1 assert A.inv()*A == 1 def test_hermitian(): H = HermitianOperator('H') assert isinstance(H, HermitianOperator) assert isinstance(H, Operator) assert Dagger(H) == H assert H.inv() != H assert H.is_commutative == False assert Dagger(H).is_commutative == False def test_unitary(): U = UnitaryOperator('U') assert isinstance(U, UnitaryOperator) assert isinstance(U, Operator) assert U.inv() == Dagger(U) assert U*Dagger(U) == 1 assert Dagger(U)*U == 1 assert U.is_commutative == False assert Dagger(U).is_commutative == False def test_outer_product(): k = Ket('k') b = Bra('b') op = OuterProduct(k, b) assert isinstance(op, OuterProduct) assert isinstance(op, Operator) assert op.ket == k assert op.bra == b assert op.label == (k, b) assert op.is_commutative == False op = k*b assert isinstance(op, OuterProduct) assert isinstance(op, Operator) assert op.ket == k assert op.bra == b assert op.label == (k, b) assert op.is_commutative == False op = 2*k*b assert op == Mul(Integer(2), k, b) op = 2*(k*b) assert op == Mul(Integer(2), OuterProduct(k, b)) assert Dagger(k*b) == OuterProduct(Dagger(b),Dagger(k)) assert Dagger(k*b).is_commutative == False def test_operator_dagger(): A = Operator('A') B = Operator('B') assert Dagger(A*B) == Dagger(B)*Dagger(A) assert Dagger(A+B) == Dagger(A) + Dagger(B) assert Dagger(A**2) == Dagger(A)**2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_cartesian.py0000644000175000017500000000233012014170666030775 0ustar georgeskgeorgesk"""Tests for cartesian.py""" from sympy import S, Interval, symbols, I, DiracDelta, exp, sqrt, pi from sympy.physics.quantum import qapply, represent, L2, Dagger from sympy.physics.quantum import Commutator, hbar from sympy.physics.quantum.cartesian import ( XOp, PxOp, X, Px, XKet, XBra, PxKet, PxBra ) x, y = symbols('x,y') px, py = symbols('px py') def test_x(): assert X.hilbert_space == L2(Interval(S.NegativeInfinity, S.Infinity)) assert Commutator(X, Px).doit() == I*hbar assert qapply(X*XKet(x)) == x*XKet(x) assert XKet(x).dual_class == XBra assert XBra(x).dual_class == XKet assert (Dagger(XKet(y))*XKet(x)).doit() == DiracDelta(x-y) assert (PxBra(px)*XKet(x)).doit() ==\ exp(-I*x*px/hbar)/sqrt(2*pi*hbar) assert represent(XKet(x)) == x assert XBra(x).position == x def test_p(): assert Px.hilbert_space == L2(Interval(S.NegativeInfinity, S.Infinity)) assert qapply(Px*PxKet(px)) == px*PxKet(px) assert PxKet(px).dual_class == PxBra assert PxBra(x).dual_class == PxKet assert (Dagger(PxKet(py))*PxKet(px)).doit() == DiracDelta(px-py) assert (XBra(x)*PxKet(px)).doit() ==\ exp(I*x*px/hbar)/sqrt(2*pi*hbar) assert represent(PxKet(px)) == px wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_qft.py0000644000175000017500000000320712014170666027622 0ustar georgeskgeorgeskfrom sympy.physics.quantum.qft import QFT, IQFT, RkGate from sympy.physics.quantum.gate import (ZGate, SwapGate, HadamardGate, CGate, PhaseGate, TGate) from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.represent import represent from sympy.physics.quantum.qapply import qapply from sympy.matrices.matrices import Matrix, eye from sympy.core.symbol import Symbol from sympy import exp, I, pi, sqrt def test_RkGate(): x = Symbol('x') assert RkGate(1,x).k == x assert RkGate(1,x).targets == (1,) assert RkGate(1,1) == ZGate(1) assert RkGate(2,2) == PhaseGate(2) assert RkGate(3,3) == TGate(3) assert represent(RkGate(0,x), nqubits =1) ==\ Matrix([[1,0],[0,exp(2*I*pi/2**x)]]) def test_RkGate_controled(): pass def test_quantum_fourier(): assert QFT(0,3).decompose() == SwapGate(0,2)*HadamardGate(0)*CGate((0,), PhaseGate(1))\ *HadamardGate(1)*CGate((0,), TGate(2))*CGate((1,), PhaseGate(2))*HadamardGate(2) assert IQFT(0,3).decompose() == HadamardGate(2)*CGate((1,), RkGate(2,-2))*CGate((0,),RkGate(2,-3))\ *HadamardGate(1)*CGate((0,), RkGate(1,-2))*HadamardGate(0)*SwapGate(0,2) assert represent(QFT(0,3), nqubits=3)\ == Matrix([[exp(2*pi*I/8)**(i*j%8)/sqrt(8) for i in range(8)] for j in range(8)]) assert QFT(0,4).decompose() #non-trivial decomposition assert qapply(QFT(0,3).decompose()*Qubit(0,0,0)).expand() ==\ qapply(HadamardGate(0)*HadamardGate(1)*HadamardGate(2)*Qubit(0,0,0)).expand() def test_qft_represent(): c = QFT(0,3) a = represent(c,nqubits=3) b = represent(c.decompose(),nqubits=3) assert a.evalf(prec=10) == b.evalf(prec=10) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_commutator.py0000644000175000017500000000324012014170666031217 0ustar georgeskgeorgeskfrom sympy import symbols, Integer from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.commutator import Commutator as Comm from sympy.physics.quantum.operator import Operator a, b, c = symbols('a,b,c') A, B, C, D = symbols('A,B,C,D', commutative=False) def test_commutator(): c = Comm(A,B) assert c.is_commutative == False assert isinstance(c, Comm) assert c.subs(A,C) == Comm(C,B) def test_commutator_identities(): assert Comm(a*A,b*B) == a*b*Comm(A,B) assert Comm(A, A) == 0 assert Comm(a, b) == 0 assert Comm(A,B) == -Comm(B,A) assert Comm(A,B).doit() == A*B - B*A assert Comm(A,B*C).expand(commutator=True) == Comm(A,B)*C + B*Comm(A,C) assert Comm(A*B,C).expand(commutator=True) == A*Comm(B,C) + Comm(A,C)*B assert Comm(A+B,C).expand(commutator=True) == Comm(A,C) + Comm(B,C) assert Comm(A,B+C).expand(commutator=True) == Comm(A,B) + Comm(A,C) e = Comm(A,Comm(B,C))+Comm(B,Comm(C,A))+Comm(C,Comm(A,B)) assert e.doit().expand() == 0 def test_commutator_dagger(): comm = Comm(A*B,C) assert Dagger(comm).expand(commutator=True) ==\ - Comm(Dagger(B),Dagger(C))*Dagger(A) -\ Dagger(B)*Comm(Dagger(A),Dagger(C)) class Foo(Operator): def _eval_commutator_Bar(self, bar): return Integer(0) class Bar(Operator): pass class Tam(Operator): def _eval_commutator_Foo(self, foo): return Integer(1) def test_eval_commutator(): F = Foo('F') B = Bar('B') T = Tam('T') assert Comm(F,B).doit() == 0 assert Comm(B,F).doit() == 0 assert Comm(F,T).doit() == -1 assert Comm(T,F).doit() == 1 assert Comm(B,T).doit() == B*T - T*B wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_state.py0000644000175000017500000000654512014170666030160 0ustar georgeskgeorgeskfrom sympy import I, symbols, sqrt, Add, Mul, Rational, Pow, Symbol, sympify from sympy import Integer, conjugate, pretty, latex from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.qexpr import QExpr from sympy.physics.quantum.state import ( Ket, Bra, TimeDepKet, TimeDepBra, KetBase, BraBase, StateBase ) from sympy.physics.quantum.hilbert import HilbertSpace x,y,t = symbols('x,y,t') def test_ket(): k = Ket('0') assert isinstance(k, Ket) assert isinstance(k, KetBase) assert isinstance(k, StateBase) assert isinstance(k, QExpr) assert k.label == (Symbol('0'),) assert k.hilbert_space == HilbertSpace() assert k.is_commutative == False # Make sure this doesn't get converted to the number pi. k = Ket('pi') assert k.label == (Symbol('pi'),) k = Ket(x,y) assert k.label == (x,y) assert k.hilbert_space == HilbertSpace() assert k.is_commutative == False assert k.dual_class == Bra assert k.dual == Bra(x,y) assert k.subs(x,y) == Ket(y,y) def test_bra(): b = Bra('0') assert isinstance(b, Bra) assert isinstance(b, BraBase) assert isinstance(b, StateBase) assert isinstance(b, QExpr) assert b.label == (Symbol('0'),) assert b.hilbert_space == HilbertSpace() assert b.is_commutative == False # Make sure this doesn't get converted to the number pi. b = Bra('pi') assert b.label == (Symbol('pi'),) b = Bra(x,y) assert b.label == (x,y) assert b.hilbert_space == HilbertSpace() assert b.is_commutative == False assert b.dual_class == Ket assert b.dual == Ket(x,y) assert b.subs(x,y) == Bra(y,y) def test_ops(): k0 = Ket(0) k1 = Ket(1) k = 2*I*k0 - (x/sqrt(2))*k1 assert k == Add(Mul(2, I, k0), Mul(Rational(-1, 2), x, Pow(2, Rational(1, 2)), k1)) def test_time_dep_ket(): k = TimeDepKet(0,t) assert isinstance(k, TimeDepKet) assert isinstance(k, KetBase) assert isinstance(k, StateBase) assert isinstance(k, QExpr) assert k.label == (Integer(0),) assert k.args == (Integer(0),t) assert k.time == t assert k.dual_class == TimeDepBra assert k.dual == TimeDepBra(0,t) assert k.subs(t,2) == TimeDepKet(0,2) k = TimeDepKet(x, 0.5) assert k.label == (x,) assert k.args == (x,sympify(0.5)) def test_time_dep_bra(): b = TimeDepBra(0,t) assert isinstance(b, TimeDepBra) assert isinstance(b, BraBase) assert isinstance(b, StateBase) assert isinstance(b, QExpr) assert b.label == (Integer(0),) assert b.args == (Integer(0),t) assert b.time == t assert b.dual_class == TimeDepKet assert b.dual == TimeDepKet(0,t) k = TimeDepBra(x, 0.5) assert k.label == (x,) assert k.args == (x,sympify(0.5)) def test_bra_ket_dagger(): x = symbols('x',complex=True) k = Ket('k') b = Bra('b') assert Dagger(k) == Bra('k') assert Dagger(b) == Ket('b') assert Dagger(k).is_commutative == False k2 = Ket('k2') e = 2*I*k + x*k2 assert Dagger(e) == conjugate(x)*Dagger(k2) - 2*I*Dagger(k) def test_printing(): psi = Ket('psi') assert pretty(psi, use_unicode=True) == u'\u2758\u03c8\u27e9' assert pretty(Dagger(psi), use_unicode=True) == u'\u27e8\u03c8\u2758' assert latex(psi) == r"{\left|\psi\right\rangle }" assert latex(Dagger(psi)) == r"{\left\langle \psi\right|}" wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_anticommutator.py0000644000175000017500000000234112014170666032074 0ustar georgeskgeorgeskfrom sympy import symbols, Integer from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.anticommutator import AntiCommutator as AComm from sympy.physics.quantum.operator import Operator a, b, c = symbols('a,b,c') A, B, C, D = symbols('A,B,C,D', commutative=False) def test_anticommutator(): ac = AComm(A,B) assert isinstance(ac, AComm) assert ac.is_commutative == False assert ac.subs(A,C) == AComm(C,B) def test_commutator_identities(): assert AComm(a*A,b*B) == a*b*AComm(A,B) assert AComm(A, A) == 2*A**2 assert AComm(A, B) == AComm(B, A) assert AComm(a, b) == 2*a*b assert AComm(A,B).doit() == A*B + B*A def test_anticommutator_dagger(): assert Dagger(AComm(A, B)) == AComm(Dagger(A),Dagger(B)) class Foo(Operator): def _eval_anticommutator_Bar(self, bar): return Integer(0) class Bar(Operator): pass class Tam(Operator): def _eval_anticommutator_Foo(self, foo): return Integer(1) def test_eval_commutator(): F = Foo('F') B = Bar('B') T = Tam('T') assert AComm(F,B).doit() == 0 assert AComm(B,F).doit() == 0 assert AComm(F,T).doit() == 1 assert AComm(T,F).doit() == 1 assert AComm(B,T).doit() == B*T + T*B wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_matrixutils.py0000644000175000017500000000672312014170666031423 0ustar georgeskgeorgeskfrom sympy import Matrix, zeros, ones, Integer from sympy.physics.quantum.matrixutils import ( to_sympy, to_numpy, to_scipy_sparse, matrix_tensor_product, matrix_to_zero ) from sympy.external import import_module from sympy.utilities.pytest import skip m = Matrix([[1,2],[3,4]]) def test_sympy_to_sympy(): assert to_sympy(m) == m def test_matrix_to_zero(): assert matrix_to_zero(m) == m assert matrix_to_zero(Matrix([[0,0],[0,0]])) == Integer(0) np = import_module('numpy', min_python_version=(2, 6)) def test_to_numpy(): if not np: skip("numpy not installed or Python too old.") result = np.matrix([[1,2],[3,4]], dtype='complex') assert (to_numpy(m) == result).all() def test_matrix_tensor_product(): if not np: skip("numpy not installed or Python too old.") l1 = zeros(4) for i in range(16): l1[i] = 2**i l2 = zeros(4) for i in range(16): l2[i] = i l3 = zeros(2) for i in range(4): l3[i] = i vec = Matrix([1,2,3]) #test for Matrix known 4x4 matricies numpyl1 = np.matrix(l1.tolist()) numpyl2 = np.matrix(l2.tolist()) numpy_product = np.kron(numpyl1,numpyl2) args = [l1, l2] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() numpy_product = np.kron(numpyl2,numpyl1) args = [l2, l1] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() #test for other known matrix of different dimensions numpyl2 = np.matrix(l3.tolist()) numpy_product = np.kron(numpyl1,numpyl2) args = [l1, l3] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() numpy_product = np.kron(numpyl2,numpyl1) args = [l3, l1] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() #test for non square matrix numpyl2 = np.matrix(vec.tolist()) numpy_product = np.kron(numpyl1,numpyl2) args = [l1, vec] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() numpy_product = np.kron(numpyl2,numpyl1) args = [vec, l1] sympy_product = matrix_tensor_product(*args) assert numpy_product.tolist() == sympy_product.tolist() #test for random matrix with random values that are floats random_matrix1 = np.random.rand(np.random.rand()*5+1,np.random.rand()*5+1) random_matrix2 = np.random.rand(np.random.rand()*5+1,np.random.rand()*5+1) numpy_product = np.kron(random_matrix1,random_matrix2) args = [Matrix(random_matrix1.tolist()),Matrix(random_matrix2.tolist())] sympy_product = matrix_tensor_product(*args) assert not (sympy_product - Matrix(numpy_product.tolist())).tolist() > \ (ones((sympy_product.rows,sympy_product.cols))*epsilon).tolist() #test for three matrix kronecker sympy_product = matrix_tensor_product(l1,vec,l2) numpy_product = np.kron(l1,np.kron(vec,l2)) assert numpy_product.tolist() == sympy_product.tolist() scipy = import_module('scipy', __import__kwargs={'fromlist':['sparse']}) def test_to_scipy_sparse(): if not np: skip("numpy not installed or Python too old.") if not scipy: skip("scipy not installed.") else: sparse = scipy.sparse result = sparse.csr_matrix([[1,2],[3,4]], dtype='complex') assert np.linalg.norm((to_scipy_sparse(m) - result).todense()) == 0.0 epsilon = .000001 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_grover.py0000644000175000017500000000633612014170666030342 0ustar georgeskgeorgeskfrom sympy import sqrt from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import Qubit, IntQubit from sympy.physics.quantum.grover import (apply_grover, superposition_basis, OracleGate, grover_iteration, WGate) def return_one_on_two(qubits): return qubits == IntQubit(2, qubits.nqubits) def return_one_on_one(qubits): return qubits == IntQubit(1, qubits.nqubits) def test_superposition_basis(): nbits = 2 first_half_state = IntQubit(0, nbits)/2 + IntQubit(1, nbits)/2 second_half_state = IntQubit(2, nbits)/2 + IntQubit(3, nbits)/2 assert first_half_state + second_half_state == superposition_basis(nbits) nbits = 3 firstq = (1/sqrt(8))*IntQubit(0, nbits) + (1/sqrt(8))*IntQubit(1, nbits) secondq = (1/sqrt(8))*IntQubit(2, nbits) + (1/sqrt(8))*IntQubit(3, nbits) thirdq = (1/sqrt(8))*IntQubit(4, nbits) + (1/sqrt(8))*IntQubit(5, nbits) fourthq = (1/sqrt(8))*IntQubit(6, nbits) + (1/sqrt(8))*IntQubit(7, nbits) assert firstq + secondq + thirdq + fourthq == superposition_basis(nbits) def test_OracleGate(): v = OracleGate(1, lambda qubits: qubits == IntQubit(0)) assert qapply(v*IntQubit(0)) == -IntQubit(0) assert qapply(v*IntQubit(1)) == IntQubit(1) nbits = 2 v = OracleGate(2, return_one_on_two) assert qapply(v*IntQubit(0, nbits)) == IntQubit(0, nbits) assert qapply(v*IntQubit(1, nbits)) == IntQubit(1, nbits) assert qapply(v*IntQubit(2, nbits)) == -IntQubit(2, nbits) assert qapply(v*IntQubit(3, nbits)) == IntQubit(3, nbits) def test_WGate(): nqubits = 2 basis_states = superposition_basis(nqubits) assert qapply(WGate(nqubits)*basis_states) == basis_states expected = ((2/sqrt(pow(2, nqubits)))*basis_states) - IntQubit(1, nqubits) assert qapply(WGate(nqubits)*IntQubit(1, nqubits)) == expected def test_grover_iteration_1(): numqubits = 2 basis_states = superposition_basis(numqubits) v = OracleGate(numqubits, return_one_on_one) expected = IntQubit(1, numqubits) assert qapply(grover_iteration(basis_states, v)) == expected def test_grover_iteration_2(): numqubits = 4 basis_states = superposition_basis(numqubits) v = OracleGate(numqubits, return_one_on_two) # After (pi/4)sqrt(pow(2, n)), IntQubit(2) should have highest prob # In this case, after around pi times (3 or 4) # print '' # print basis_states iterated = grover_iteration(basis_states, v) iterated = qapply(iterated) # print iterated iterated = grover_iteration(iterated, v) iterated = qapply(iterated) # print iterated iterated = grover_iteration(iterated, v) iterated = qapply(iterated) # print iterated # In this case, probability was highest after 3 iterations # Probability of Qubit('0010') was 251/256 (3) vs 781/1024 (4) # Ask about measurement expected = (-13*basis_states)/64 + 264*IntQubit(2, numqubits)/256 assert qapply(expected) == iterated def test_grover(): nqubits = 2 assert apply_grover(return_one_on_one, nqubits) == IntQubit(1, nqubits) nqubits = 4 basis_states = superposition_basis(nqubits) expected = (-13*basis_states)/64 + 264*IntQubit(2, nqubits)/256 assert apply_grover(return_one_on_two, 4) == qapply(expected) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_kronecker.py0000644000175000017500000000071712014170666031016 0ustar georgeskgeorgeskfrom sympy import symbols from sympy.physics.quantum.kronecker import KroneckerDelta def test_kronecker_delta(): i, j, k = symbols('i j k') D = KroneckerDelta assert D(i, i) == 1 assert D(i, i + 1) == 0 assert D(0, 0) == 1 assert D(0, 1) == 0 # assert D(i, i + k) == D(0, k) assert D(i + k, i + k) == 1 assert D(i + k, i + 1 + k) == 0 assert D(i, j).subs(dict(i=1, j=0)) == 0 assert D(i, j).subs(dict(i=3, j=3)) == 1 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_hilbert.py0000644000175000017500000000472212014170666030464 0ustar georgeskgeorgeskfrom sympy.physics.quantum.hilbert import ( HilbertSpace, ComplexSpace, L2, FockSpace, TensorProductHilbertSpace, DirectSumHilbertSpace, TensorPowerHilbertSpace ) from sympy import Interval, oo, Symbol, sstr, srepr def test_hilbert_space(): hs = HilbertSpace() assert isinstance(hs, HilbertSpace) assert sstr(hs) == 'H' assert srepr(hs) == 'HilbertSpace()' def test_complex_space(): c1 = ComplexSpace(2) assert isinstance(c1, ComplexSpace) assert c1.dimension == 2 assert sstr(c1) == 'C(2)' assert srepr(c1) == 'ComplexSpace(Integer(2))' n = Symbol('n') c2 = ComplexSpace(n) assert isinstance(c2, ComplexSpace) assert c2.dimension == n assert sstr(c2) == 'C(n)' assert srepr(c2) == "ComplexSpace(Symbol('n'))" assert c2.subs(n,2) == ComplexSpace(2) def test_L2(): b1 = L2(Interval(-oo, 1)) assert isinstance(b1, L2) assert b1.dimension == oo assert b1.interval == Interval(-oo, 1) x = Symbol('x', real=True) y = Symbol('y', real=True) b2 = L2(Interval(x,y)) assert b2.dimension == oo assert b2.interval == Interval(x,y) assert b2.subs(x,-1) == L2(Interval(-1,y)) def test_fock_space(): f1 = FockSpace() f2 = FockSpace() assert isinstance(f1, FockSpace) assert f1.dimension == oo assert f1 == f2 def test_tensor_product(): n = Symbol('n') hs1 = ComplexSpace(2) hs2 = ComplexSpace(n) h = hs1*hs2 assert isinstance(h, TensorProductHilbertSpace) assert h.dimension == 2*n assert h.spaces == set([hs1, hs2]) h = hs2*hs2 assert isinstance(h, TensorPowerHilbertSpace) assert h.base == hs2 assert h.exp == 2 assert h.dimension == n**2 f = FockSpace() h = hs1*hs2*f assert h.dimension == oo def test_tensor_power(): n = Symbol('n') hs1 = ComplexSpace(2) hs2 = ComplexSpace(n) h = hs1**2 assert isinstance(h, TensorPowerHilbertSpace) assert h.base == hs1 assert h.exp == 2 assert h.dimension == 4 h = hs2**3 assert isinstance(h, TensorPowerHilbertSpace) assert h.base == hs2 assert h.exp == 3 assert h.dimension == n**3 def test_direct_sum(): n = Symbol('n') hs1 = ComplexSpace(2) hs2 = ComplexSpace(n) h = hs1+hs2 assert isinstance(h, DirectSumHilbertSpace) assert h.dimension == 2+n assert h.spaces == set([hs1, hs2]) f = FockSpace() h = hs1 + f + hs2 assert h.dimension == oo assert h.spaces == set([hs1, hs2, f]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_spin.py0000644000175000017500000007023412014170666030005 0ustar georgeskgeorgeskfrom __future__ import division from sympy import cos, exp, I, Matrix, pi, S, sin, sqrt, symbols from sympy.physics.quantum import hbar, represent, Commutator, InnerProduct from sympy.physics.quantum import qapply from sympy.physics.quantum.spin import ( Jx, Jy, Jz, Jplus, Jminus, J2, JxBra, JyBra, JzBra, JxKet, JyKet, JzKet, Rotation, WignerD ) from sympy.utilities.pytest import XFAIL def test_represent(): # Spin operators assert represent(Jx) == hbar*Matrix([[0,1],[1,0]])/2 assert represent(Jx, j=1) == hbar*sqrt(2)*Matrix([[0,1,0],[1,0,1],[0,1,0]])/2 assert represent(Jy) == hbar*I*Matrix([[0,-1],[1,0]])/2 assert represent(Jy, j=1) == hbar*I*sqrt(2)*Matrix([[0,-1,0],[1,0,-1],[0,1,0]])/2 assert represent(Jz) == hbar*Matrix([[1,0],[0,-1]])/2 assert represent(Jz, j=1) == hbar*Matrix([[1,0,0],[0,0,0],[0,0,-1]]) # Spin states # Jx basis assert represent(JxKet(S(1)/2,S(1)/2), basis=Jx) == Matrix([1,0]) assert represent(JxKet(S(1)/2,-S(1)/2), basis=Jx) == Matrix([0,1]) assert represent(JxKet(1,1), basis=Jx) == Matrix([1,0,0]) assert represent(JxKet(1,0), basis=Jx) == Matrix([0,1,0]) assert represent(JxKet(1,-1), basis=Jx) == Matrix([0,0,1]) assert represent(JyKet(S(1)/2,S(1)/2), basis=Jx) == Matrix([exp(-I*pi/4),0]) assert represent(JyKet(S(1)/2,-S(1)/2), basis=Jx) == Matrix([0,exp(I*pi/4)]) assert represent(JyKet(1,1), basis=Jx) == Matrix([-I,0,0]) assert represent(JyKet(1,0), basis=Jx) == Matrix([0,1,0]) assert represent(JyKet(1,-1), basis=Jx) == Matrix([0,0,I]) assert represent(JzKet(S(1)/2,S(1)/2), basis=Jx) == sqrt(2)*Matrix([-1,1])/2 assert represent(JzKet(S(1)/2,-S(1)/2), basis=Jx) == sqrt(2)*Matrix([-1,-1])/2 assert represent(JzKet(1,1), basis=Jx) == Matrix([1,-sqrt(2),1])/2 assert represent(JzKet(1,0), basis=Jx) == sqrt(2)*Matrix([1,0,-1])/2 assert represent(JzKet(1,-1), basis=Jx) == Matrix([1,sqrt(2),1])/2 # Jy basis assert represent(JxKet(S(1)/2,S(1)/2), basis=Jy) == Matrix([exp(-3*I*pi/4),0]) assert represent(JxKet(S(1)/2,-S(1)/2), basis=Jy) == Matrix([0,exp(3*I*pi/4)]) assert represent(JxKet(1,1), basis=Jy) == Matrix([I,0,0]) assert represent(JxKet(1,0), basis=Jy) == Matrix([0,1,0]) assert represent(JxKet(1,-1), basis=Jy) == Matrix([0,0,-I]) assert represent(JyKet(S(1)/2,S(1)/2), basis=Jy) == Matrix([1,0]) assert represent(JyKet(S(1)/2,-S(1)/2), basis=Jy) == Matrix([0,1]) assert represent(JyKet(1,1), basis=Jy) == Matrix([1,0,0]) assert represent(JyKet(1,0), basis=Jy) == Matrix([0,1,0]) assert represent(JyKet(1,-1), basis=Jy) == Matrix([0,0,1]) assert represent(JzKet(S(1)/2,S(1)/2), basis=Jy) == sqrt(2)*Matrix([-1,I])/2 assert represent(JzKet(S(1)/2,-S(1)/2), basis=Jy) == sqrt(2)*Matrix([I,-1])/2 assert represent(JzKet(1,1), basis=Jy) == Matrix([1,-I*sqrt(2),-1])/2 assert represent(JzKet(1,0), basis=Jy) == Matrix([-sqrt(2)*I,0,-sqrt(2)*I])/2 assert represent(JzKet(1,-1), basis=Jy) == Matrix([-1,-sqrt(2)*I,1])/2 # Jz basis assert represent(JxKet(S(1)/2,S(1)/2)) == sqrt(2)*Matrix([1,1])/2 assert represent(JxKet(S(1)/2,-S(1)/2)) == sqrt(2)*Matrix([-1,1])/2 assert represent(JxKet(1,1)) == Matrix([1,sqrt(2),1])/2 assert represent(JxKet(1,0)) == sqrt(2)*Matrix([-1,0,1])/2 assert represent(JxKet(1,-1)) == Matrix([1,-sqrt(2),1])/2 assert represent(JyKet(S(1)/2,S(1)/2)) == sqrt(2)*Matrix([-1,-I])/2 assert represent(JyKet(S(1)/2,-S(1)/2)) == sqrt(2)*Matrix([-I,-1])/2 assert represent(JyKet(1,1)) == Matrix([1,sqrt(2)*I,-1])/2 assert represent(JyKet(1,0)) == sqrt(2)*Matrix([I,0,I])/2 assert represent(JyKet(1,-1)) == Matrix([-1,sqrt(2)*I,1])/2 assert represent(JzKet(S(1)/2,S(1)/2)) == Matrix([1,0]) assert represent(JzKet(S(1)/2,-S(1)/2)) == Matrix([0,1]) assert represent(JzKet(1,1)) == Matrix([1,0,0]) assert represent(JzKet(1,0)) == Matrix([0,1,0]) assert represent(JzKet(1,-1)) == Matrix([0,0,1]) def test_rewrite(): # Rewrite to same basis assert JxBra(1,1).rewrite('Jx') == JxBra(1,1) assert JxKet(1,1).rewrite('Jx') == JxKet(1,1) # Rewriting to different basis assert JxKet(1,1).rewrite('Jy') == I*JyKet(1,1) assert JxKet(1,0).rewrite('Jy') == JyKet(1,0) assert JxKet(1,-1).rewrite('Jy') == -I*JyKet(1,-1) assert JxKet(1,1).rewrite('Jz') == JzKet(1,1)/2+JzKet(1,0)/sqrt(2)+JzKet(1,-1)/2 assert JxKet(1,0).rewrite('Jz') == -sqrt(2)*JzKet(1,1)/2+sqrt(2)*JzKet(1,-1)/2 assert JxKet(1,-1).rewrite('Jz') == JzKet(1,1)/2-JzKet(1,0)/sqrt(2)+JzKet(1,-1)/2 assert JyKet(1,1).rewrite('Jx') == -I*JxKet(1,1) assert JyKet(1,0).rewrite('Jx') == JxKet(1,0) assert JyKet(1,-1).rewrite('Jx') == I*JxKet(1,-1) assert JyKet(1,1).rewrite('Jz') == JzKet(1,1)/2+sqrt(2)*I*JzKet(1,0)/2-JzKet(1,-1)/2 assert JyKet(1,0).rewrite('Jz') == sqrt(2)*I*JzKet(1,1)/2+sqrt(2)*I*JzKet(1,-1)/2 assert JyKet(1,-1).rewrite('Jz') == -JzKet(1,1)/2+sqrt(2)*I*JzKet(1,0)/2+JzKet(1,-1)/2 assert JzKet(1,1).rewrite('Jx') == JxKet(1,1)/2-sqrt(2)*JxKet(1,0)/2+JxKet(1,-1)/2 assert JzKet(1,0).rewrite('Jx') == sqrt(2)*JxKet(1,1)/2-sqrt(2)*JxKet(1,-1)/2 assert JzKet(1,-1).rewrite('Jx') == JxKet(1,1)/2+sqrt(2)*JxKet(1,0)/2+JxKet(1,-1)/2 assert JzKet(1,1).rewrite('Jy') == JyKet(1,1)/2-sqrt(2)*I*JyKet(1,0)/2-JyKet(1,-1)/2 assert JzKet(1,0).rewrite('Jy') == -sqrt(2)*I*JyKet(1,1)/2-sqrt(2)*I*JyKet(1,-1)/2 assert JzKet(1,-1).rewrite('Jy') == -JyKet(1,1)/2-sqrt(2)*I*JyKet(1,0)/2+JyKet(1,-1)/2 # Innerproducts of rewritten states assert qapply(JxBra(1,1)*JxKet(1,1).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1,0)*JxKet(1,0).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1,-1)*JxKet(1,-1).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1,1)*JxKet(1,1).rewrite('Jz')).doit() == 1 assert qapply(JxBra(1,0)*JxKet(1,0).rewrite('Jz')).doit() == 1 assert qapply(JxBra(1,-1)*JxKet(1,-1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1,1)*JyKet(1,1).rewrite('Jx')).doit() == 1 assert qapply(JyBra(1,0)*JyKet(1,0).rewrite('Jx')).doit() == 1 assert qapply(JyBra(1,-1)*JyKet(1,-1).rewrite('Jx')).doit() == 1 assert qapply(JyBra(1,1)*JyKet(1,1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1,0)*JyKet(1,0).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1,-1)*JyKet(1,-1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1,1)*JyKet(1,1).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1,0)*JyKet(1,0).rewrite('Jz')).doit() == 1 assert qapply(JyBra(1,-1)*JyKet(1,-1).rewrite('Jz')).doit() == 1 assert qapply(JzBra(1,1)*JzKet(1,1).rewrite('Jy')).doit() == 1 assert qapply(JzBra(1,0)*JzKet(1,0).rewrite('Jy')).doit() == 1 assert qapply(JzBra(1,-1)*JzKet(1,-1).rewrite('Jy')).doit() == 1 assert qapply(JxBra(1,1)*JxKet(1,0).rewrite('Jy')).doit() == 0 assert qapply(JxBra(1,1)*JxKet(1,-1).rewrite('Jy')) == 0 assert qapply(JxBra(1,1)*JxKet(1,0).rewrite('Jz')).doit() == 0 assert qapply(JxBra(1,1)*JxKet(1,-1).rewrite('Jz')) == 0 assert qapply(JyBra(1,1)*JyKet(1,0).rewrite('Jx')).doit() == 0 assert qapply(JyBra(1,1)*JyKet(1,-1).rewrite('Jx')) == 0 assert qapply(JyBra(1,1)*JyKet(1,0).rewrite('Jz')).doit() == 0 assert qapply(JyBra(1,1)*JyKet(1,-1).rewrite('Jz')) == 0 assert qapply(JzBra(1,1)*JzKet(1,0).rewrite('Jx')).doit() == 0 assert qapply(JzBra(1,1)*JzKet(1,-1).rewrite('Jx')) == 0 assert qapply(JzBra(1,1)*JzKet(1,0).rewrite('Jy')).doit() == 0 assert qapply(JzBra(1,1)*JzKet(1,-1).rewrite('Jy')) == 0 assert qapply(JxBra(1,0)*JxKet(1,1).rewrite('Jy')) == 0 assert qapply(JxBra(1,0)*JxKet(1,-1).rewrite('Jy')) == 0 assert qapply(JxBra(1,0)*JxKet(1,1).rewrite('Jz')) == 0 assert qapply(JxBra(1,0)*JxKet(1,-1).rewrite('Jz')) == 0 assert qapply(JyBra(1,0)*JyKet(1,1).rewrite('Jx')) == 0 assert qapply(JyBra(1,0)*JyKet(1,-1).rewrite('Jx')) == 0 assert qapply(JyBra(1,0)*JyKet(1,1).rewrite('Jz')) == 0 assert qapply(JyBra(1,0)*JyKet(1,-1).rewrite('Jz')) == 0 assert qapply(JzBra(1,0)*JzKet(1,1).rewrite('Jx')) == 0 assert qapply(JzBra(1,0)*JzKet(1,-1).rewrite('Jx')) == 0 assert qapply(JzBra(1,0)*JzKet(1,1).rewrite('Jy')) == 0 assert qapply(JzBra(1,0)*JzKet(1,-1).rewrite('Jy')) == 0 assert qapply(JxBra(1,-1)*JxKet(1,1).rewrite('Jy')) == 0 assert qapply(JxBra(1,-1)*JxKet(1,0).rewrite('Jy')).doit() == 0 assert qapply(JxBra(1,-1)*JxKet(1,1).rewrite('Jz')) == 0 assert qapply(JxBra(1,-1)*JxKet(1,0).rewrite('Jz')).doit() == 0 assert qapply(JyBra(1,-1)*JyKet(1,1).rewrite('Jx')) == 0 assert qapply(JyBra(1,-1)*JyKet(1,0).rewrite('Jx')).doit() == 0 assert qapply(JyBra(1,-1)*JyKet(1,1).rewrite('Jz')) == 0 assert qapply(JyBra(1,-1)*JyKet(1,0).rewrite('Jz')).doit() == 0 assert qapply(JzBra(1,-1)*JzKet(1,1).rewrite('Jx')) == 0 assert qapply(JzBra(1,-1)*JzKet(1,0).rewrite('Jx')).doit() == 0 assert qapply(JzBra(1,-1)*JzKet(1,1).rewrite('Jy')) == 0 assert qapply(JzBra(1,-1)*JzKet(1,0).rewrite('Jy')).doit() == 0 def test_innerproduct(): j,m = symbols("j m") assert InnerProduct(JzBra(1,1), JzKet(1,1)).doit() == 1 assert InnerProduct(JzBra(S(1)/2,S(1)/2), JzKet(S(1)/2,-S(1)/2)).doit() == 0 assert InnerProduct(JzBra(j,m), JzKet(j,m)).doit() == 1 assert InnerProduct(JzBra(1,0), JyKet(1,1)).doit() == I/sqrt(2) assert InnerProduct(JxBra(S(1)/2,S(1)/2), JzKet(S(1)/2,S(1)/2)).doit() == -sqrt(2)/2 assert InnerProduct(JyBra(1,1), JzKet(1,1)).doit() == S(1)/2 assert InnerProduct(JxBra(1,-1), JyKet(1,1)).doit() == 0 def test_rotation_small_d(): # Symbolic tests beta = symbols('beta') # j = 1/2 assert Rotation.d(S(1)/2,S(1)/2,S(1)/2,beta).doit() == cos(beta/2) assert Rotation.d(S(1)/2,S(1)/2,-S(1)/2,beta).doit() == -sin(beta/2) assert Rotation.d(S(1)/2,-S(1)/2,S(1)/2,beta).doit() == sin(beta/2) assert Rotation.d(S(1)/2,-S(1)/2,-S(1)/2,beta).doit() == cos(beta/2) # j = 1 assert Rotation.d(1,1,1,beta).doit() == (1+cos(beta))/2 assert Rotation.d(1,1,0,beta).doit() == -sin(beta)/sqrt(2) assert Rotation.d(1,1,-1,beta).doit() == (1-cos(beta))/2 assert Rotation.d(1,0,1,beta).doit() == sin(beta)/sqrt(2) assert Rotation.d(1,0,0,beta).doit() == cos(beta) assert Rotation.d(1,0,-1,beta).doit() == -sin(beta)/sqrt(2) assert Rotation.d(1,-1,1,beta).doit() == (1-cos(beta))/2 assert Rotation.d(1,-1,0,beta).doit() == sin(beta)/sqrt(2) assert Rotation.d(1,-1,-1,beta).doit() == (1+cos(beta))/2 # j = 3/2 assert Rotation.d(S(3)/2,S(3)/2,S(3)/2,beta).doit() == (3*cos(beta/2)+cos(3*beta/2))/4 assert Rotation.d(S(3)/2,S(3)/2,S(1)/2,beta).doit() == sqrt(3)*(-sin(beta/2)-sin(3*beta/2))/4 assert Rotation.d(S(3)/2,S(3)/2,-S(1)/2,beta).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4 assert Rotation.d(S(3)/2,S(3)/2,-S(3)/2,beta).doit() == (-3*sin(beta/2)+sin(3*beta/2))/4 assert Rotation.d(S(3)/2,S(1)/2,S(3)/2,beta).doit() == sqrt(3)*(sin(beta/2)+sin(3*beta/2))/4 assert Rotation.d(S(3)/2,S(1)/2,S(1)/2,beta).doit() == (cos(beta/2)+3*cos(3*beta/2))/4 assert Rotation.d(S(3)/2,S(1)/2,-S(1)/2,beta).doit() == (sin(beta/2)-3*sin(3*beta/2))/4 assert Rotation.d(S(3)/2,S(1)/2,-S(3)/2,beta).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(1)/2,S(3)/2,beta).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(1)/2,S(1)/2,beta).doit() == (-sin(beta/2)+3*sin(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(1)/2,-S(1)/2,beta).doit() == (cos(beta/2)+3*cos(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(1)/2,-S(3)/2,beta).doit() == sqrt(3)*(-sin(beta/2)-sin(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(3)/2,S(3)/2,beta).doit() == (3*sin(beta/2)-sin(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(3)/2,S(1)/2,beta).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(3)/2,-S(1)/2,beta).doit() == sqrt(3)*(sin(beta/2)+sin(3*beta/2))/4 assert Rotation.d(S(3)/2,-S(3)/2,-S(3)/2,beta).doit() == (3*cos(beta/2)+cos(3*beta/2))/4 # j = 2 assert Rotation.d(2,2,2,beta).doit() == (3+4*cos(beta)+cos(2*beta))/8 assert Rotation.d(2,2,1,beta).doit() == (-2*sin(beta)-sin(2*beta))/4 assert Rotation.d(2,2,0,beta).doit() == sqrt(6)*(1-cos(2*beta))/8 assert Rotation.d(2,2,-1,beta).doit() == (-2*sin(beta)+sin(2*beta))/4 assert Rotation.d(2,2,-2,beta).doit() == (3-4*cos(beta)+cos(2*beta))/8 assert Rotation.d(2,1,2,beta).doit() == (2*sin(beta)+sin(2*beta))/4 assert Rotation.d(2,1,1,beta).doit() == (cos(beta)+cos(2*beta))/2 assert Rotation.d(2,1,0,beta).doit() == -sqrt(6)*sin(2*beta)/4 assert Rotation.d(2,1,-1,beta).doit() == (cos(beta)-cos(2*beta))/2 assert Rotation.d(2,1,-2,beta).doit() == (-2*sin(beta)+sin(2*beta))/4 assert Rotation.d(2,0,2,beta).doit() == sqrt(6)*(1-cos(2*beta))/8 assert Rotation.d(2,0,1,beta).doit() == sqrt(6)*sin(2*beta)/4 assert Rotation.d(2,0,0,beta).doit() == (1+3*cos(2*beta))/4 assert Rotation.d(2,0,-1,beta).doit() == -sqrt(6)*sin(2*beta)/4 assert Rotation.d(2,0,-2,beta).doit() == sqrt(6)*(1-cos(2*beta))/8 assert Rotation.d(2,-1,2,beta).doit() == (2*sin(beta)-sin(2*beta))/4 assert Rotation.d(2,-1,1,beta).doit() == (cos(beta)-cos(2*beta))/2 assert Rotation.d(2,-1,0,beta).doit() == sqrt(6)*sin(2*beta)/4 assert Rotation.d(2,-1,-1,beta).doit() == (cos(beta)+cos(2*beta))/2 assert Rotation.d(2,-1,-2,beta).doit() == (-2*sin(beta)-sin(2*beta))/4 assert Rotation.d(2,-2,2,beta).doit() == (3-4*cos(beta)+cos(2*beta))/8 assert Rotation.d(2,-2,1,beta).doit() == (2*sin(beta)-sin(2*beta))/4 assert Rotation.d(2,-2,0,beta).doit() == sqrt(6)*(1-cos(2*beta))/8 assert Rotation.d(2,-2,-1,beta).doit() == (2*sin(beta)+sin(2*beta))/4 assert Rotation.d(2,-2,-2,beta).doit() == (3+4*cos(beta)+cos(2*beta))/8 # Numerical tests # j = 1/2 assert Rotation.d(S(1)/2,S(1)/2,S(1)/2,pi/2).doit() == sqrt(2)/2 assert Rotation.d(S(1)/2,S(1)/2,-S(1)/2,pi/2).doit() == -sqrt(2)/2 assert Rotation.d(S(1)/2,-S(1)/2,S(1)/2,pi/2).doit() == sqrt(2)/2 assert Rotation.d(S(1)/2,-S(1)/2,-S(1)/2,pi/2).doit() == sqrt(2)/2 # j = 1 assert Rotation.d(1,1,1,pi/2).doit() == 1/2 assert Rotation.d(1,1,0,pi/2).doit() == -sqrt(2)/2 assert Rotation.d(1,1,-1,pi/2).doit() == 1/2 assert Rotation.d(1,0,1,pi/2).doit() == sqrt(2)/2 assert Rotation.d(1,0,0,pi/2).doit() == 0 assert Rotation.d(1,0,-1,pi/2).doit() == -sqrt(2)/2 assert Rotation.d(1,-1,1,pi/2).doit() == 1/2 assert Rotation.d(1,-1,0,pi/2).doit() == sqrt(2)/2 assert Rotation.d(1,-1,-1,pi/2).doit() == 1/2 # j = 3/2 assert Rotation.d(S(3)/2,S(3)/2,S(3)/2,pi/2).doit() == sqrt(2)/4 assert Rotation.d(S(3)/2,S(3)/2,S(1)/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.d(S(3)/2,S(3)/2,-S(1)/2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2,S(3)/2,-S(3)/2,pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2,S(1)/2,S(3)/2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2,S(1)/2,S(1)/2,pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2,S(1)/2,-S(1)/2,pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2,S(1)/2,-S(3)/2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2,-S(1)/2,S(3)/2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2,-S(1)/2,S(1)/2,pi/2).doit() == sqrt(2)/4 assert Rotation.d(S(3)/2,-S(1)/2,-S(1)/2,pi/2).doit() == -sqrt(2)/4 assert Rotation.d(S(3)/2,-S(1)/2,-S(3)/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.d(S(3)/2,-S(3)/2,S(3)/2,pi/2).doit() == sqrt(2)/4 assert Rotation.d(S(3)/2,-S(3)/2,S(1)/2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2,-S(3)/2,-S(1)/2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(S(3)/2,-S(3)/2,-S(3)/2,pi/2).doit() == sqrt(2)/4 # j = 2 assert Rotation.d(2,2,2,pi/2).doit() == 1/4 assert Rotation.d(2,2,1,pi/2).doit() == -1/2 assert Rotation.d(2,2,0,pi/2).doit() == sqrt(6)/4 assert Rotation.d(2,2,-1,pi/2).doit() == -1/2 assert Rotation.d(2,2,-2,pi/2).doit() == 1/4 assert Rotation.d(2,1,2,pi/2).doit() == 1/2 assert Rotation.d(2,1,1,pi/2).doit() == -1/2 assert Rotation.d(2,1,0,pi/2).doit() == 0 assert Rotation.d(2,1,-1,pi/2).doit() == 1/2 assert Rotation.d(2,1,-2,pi/2).doit() == -1/2 assert Rotation.d(2,0,2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(2,0,1,pi/2).doit() == 0 assert Rotation.d(2,0,0,pi/2).doit() == -1/2 assert Rotation.d(2,0,-1,pi/2).doit() == 0 assert Rotation.d(2,0,-2,pi/2).doit() == sqrt(6)/4 assert Rotation.d(2,-1,2,pi/2).doit() == 1/2 assert Rotation.d(2,-1,1,pi/2).doit() == 1/2 assert Rotation.d(2,-1,0,pi/2).doit() == 0 assert Rotation.d(2,-1,-1,pi/2).doit() == -1/2 assert Rotation.d(2,-1,-2,pi/2).doit() == -1/2 assert Rotation.d(2,-2,2,pi/2).doit() == 1/4 assert Rotation.d(2,-2,1,pi/2).doit() == 1/2 assert Rotation.d(2,-2,0,pi/2).doit() == sqrt(6)/4 assert Rotation.d(2,-2,-1,pi/2).doit() == 1/2 assert Rotation.d(2,-2,-2,pi/2).doit() == 1/4 def test_rotation_d(): # Symbolic tests alpha, beta, gamma = symbols('alpha beta gamma') # j = 1/2 assert Rotation.D(S(1)/2,S(1)/2,S(1)/2,alpha,beta,gamma).doit() == cos(beta/2)*exp(-I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(1)/2,S(1)/2,-S(1)/2,alpha,beta,gamma).doit() == -sin(beta/2)*exp(-I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(1)/2,-S(1)/2,S(1)/2,alpha,beta,gamma).doit() == sin(beta/2)*exp(I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(1)/2,-S(1)/2,-S(1)/2,alpha,beta,gamma).doit() == cos(beta/2)*exp(I*alpha/2)*exp(I*gamma/2) # j = 1 assert Rotation.D(1,1,1,alpha,beta,gamma).doit() == (1+cos(beta))/2*exp(-I*alpha)*exp(-I*gamma) assert Rotation.D(1,1,0,alpha,beta,gamma).doit() == -sin(beta)/sqrt(2)*exp(-I*alpha) assert Rotation.D(1,1,-1,alpha,beta,gamma).doit() == (1-cos(beta))/2*exp(-I*alpha)*exp(I*gamma) assert Rotation.D(1,0,1,alpha,beta,gamma).doit() == sin(beta)/sqrt(2)*exp(-I*gamma) assert Rotation.D(1,0,0,alpha,beta,gamma).doit() == cos(beta) assert Rotation.D(1,0,-1,alpha,beta,gamma).doit() == -sin(beta)/sqrt(2)*exp(I*gamma) assert Rotation.D(1,-1,1,alpha,beta,gamma).doit() == (1-cos(beta))/2*exp(I*alpha)*exp(-I*gamma) assert Rotation.D(1,-1,0,alpha,beta,gamma).doit() == sin(beta)/sqrt(2)*exp(I*alpha) assert Rotation.D(1,-1,-1,alpha,beta,gamma).doit() == (1+cos(beta))/2*exp(I*alpha)*exp(I*gamma) # j = 3/2 assert Rotation.D(S(3)/2,S(3)/2,S(3)/2,alpha,beta,gamma).doit() == (3*cos(beta/2)+cos(3*beta/2))/4*exp(-3*I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2,S(3)/2,S(1)/2,alpha,beta,gamma).doit() == sqrt(3)*(-sin(beta/2)-sin(3*beta/2))/4*exp(-3*I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2,S(3)/2,-S(1)/2,alpha,beta,gamma).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4*exp(-3*I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2,S(3)/2,-S(3)/2,alpha,beta,gamma).doit() == (-3*sin(beta/2)+sin(3*beta/2))/4*exp(-3*I*alpha/2)*exp(3*I*gamma/2) assert Rotation.D(S(3)/2,S(1)/2,S(3)/2,alpha,beta,gamma).doit() == sqrt(3)*(sin(beta/2)+sin(3*beta/2))/4*exp(-I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2,S(1)/2,S(1)/2,alpha,beta,gamma).doit() == (cos(beta/2)+3*cos(3*beta/2))/4*exp(-I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2,S(1)/2,-S(1)/2,alpha,beta,gamma).doit() == (sin(beta/2)-3*sin(3*beta/2))/4*exp(-I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2,S(1)/2,-S(3)/2,alpha,beta,gamma).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4*exp(-I*alpha/2)*exp(3*I*gamma/2) assert Rotation.D(S(3)/2,-S(1)/2,S(3)/2,alpha,beta,gamma).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4*exp(I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2,-S(1)/2,S(1)/2,alpha,beta,gamma).doit() == (-sin(beta/2)+3*sin(3*beta/2))/4*exp(I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2,-S(1)/2,-S(1)/2,alpha,beta,gamma).doit() == (cos(beta/2)+3*cos(3*beta/2))/4*exp(I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2,-S(1)/2,-S(3)/2,alpha,beta,gamma).doit() == sqrt(3)*(-sin(beta/2)-sin(3*beta/2))/4*exp(I*alpha/2)*exp(3*I*gamma/2) assert Rotation.D(S(3)/2,-S(3)/2,S(3)/2,alpha,beta,gamma).doit() == (3*sin(beta/2)-sin(3*beta/2))/4*exp(3*I*alpha/2)*exp(-3*I*gamma/2) assert Rotation.D(S(3)/2,-S(3)/2,S(1)/2,alpha,beta,gamma).doit() == sqrt(3)*(cos(beta/2)-cos(3*beta/2))/4*exp(3*I*alpha/2)*exp(-I*gamma/2) assert Rotation.D(S(3)/2,-S(3)/2,-S(1)/2,alpha,beta,gamma).doit() == sqrt(3)*(sin(beta/2)+sin(3*beta/2))/4*exp(3*I*alpha/2)*exp(I*gamma/2) assert Rotation.D(S(3)/2,-S(3)/2,-S(3)/2,alpha,beta,gamma).doit() == (3*cos(beta/2)+cos(3*beta/2))/4*exp(3*I*alpha/2)*exp(3*I*gamma/2) # j = 2 assert Rotation.D(2,2,2,alpha,beta,gamma).doit() == (3+4*cos(beta)+cos(2*beta))/8*exp(-2*I*alpha)*exp(-2*I*gamma) assert Rotation.D(2,2,1,alpha,beta,gamma).doit() == (-2*sin(beta)-sin(2*beta))/4*exp(-2*I*alpha)*exp(-I*gamma) assert Rotation.D(2,2,0,alpha,beta,gamma).doit() == sqrt(6)*(1-cos(2*beta))/8*exp(-2*I*alpha) assert Rotation.D(2,2,-1,alpha,beta,gamma).doit() == (-2*sin(beta)+sin(2*beta))/4*exp(-2*I*alpha)*exp(I*gamma) assert Rotation.D(2,2,-2,alpha,beta,gamma).doit() == (3-4*cos(beta)+cos(2*beta))/8*exp(-2*I*alpha)*exp(2*I*gamma) assert Rotation.D(2,1,2,alpha,beta,gamma).doit() == (2*sin(beta)+sin(2*beta))/4*exp(-I*alpha)*exp(-2*I*gamma) assert Rotation.D(2,1,1,alpha,beta,gamma).doit() == (cos(beta)+cos(2*beta))/2*exp(-I*alpha)*exp(-I*gamma) assert Rotation.D(2,1,0,alpha,beta,gamma).doit() == -sqrt(6)*sin(2*beta)/4*exp(-I*alpha) assert Rotation.D(2,1,-1,alpha,beta,gamma).doit() == (cos(beta)-cos(2*beta))/2*exp(-I*alpha)*exp(I*gamma) assert Rotation.D(2,1,-2,alpha,beta,gamma).doit() == (-2*sin(beta)+sin(2*beta))/4*exp(-I*alpha)*exp(2*I*gamma) assert Rotation.D(2,0,2,alpha,beta,gamma).doit() == sqrt(6)*(1-cos(2*beta))/8*exp(-2*I*gamma) assert Rotation.D(2,0,1,alpha,beta,gamma).doit() == sqrt(6)*sin(2*beta)/4*exp(-I*gamma) assert Rotation.D(2,0,0,alpha,beta,gamma).doit() == (1+3*cos(2*beta))/4 assert Rotation.D(2,0,-1,alpha,beta,gamma).doit() == -sqrt(6)*sin(2*beta)/4*exp(I*gamma) assert Rotation.D(2,0,-2,alpha,beta,gamma).doit() == sqrt(6)*(1-cos(2*beta))/8*exp(2*I*gamma) assert Rotation.D(2,-1,2,alpha,beta,gamma).doit() == (2*sin(beta)-sin(2*beta))/4*exp(I*alpha)*exp(-2*I*gamma) assert Rotation.D(2,-1,1,alpha,beta,gamma).doit() == (cos(beta)-cos(2*beta))/2*exp(I*alpha)*exp(-I*gamma) assert Rotation.D(2,-1,0,alpha,beta,gamma).doit() == sqrt(6)*sin(2*beta)/4*exp(I*alpha) assert Rotation.D(2,-1,-1,alpha,beta,gamma).doit() == (cos(beta)+cos(2*beta))/2*exp(I*alpha)*exp(I*gamma) assert Rotation.D(2,-1,-2,alpha,beta,gamma).doit() == (-2*sin(beta)-sin(2*beta))/4*exp(I*alpha)*exp(2*I*gamma) assert Rotation.D(2,-2,2,alpha,beta,gamma).doit() == (3-4*cos(beta)+cos(2*beta))/8*exp(2*I*alpha)*exp(-2*I*gamma) assert Rotation.D(2,-2,1,alpha,beta,gamma).doit() == (2*sin(beta)-sin(2*beta))/4*exp(2*I*alpha)*exp(-I*gamma) assert Rotation.D(2,-2,0,alpha,beta,gamma).doit() == sqrt(6)*(1-cos(2*beta))/8*exp(2*I*alpha) assert Rotation.D(2,-2,-1,alpha,beta,gamma).doit() == (2*sin(beta)+sin(2*beta))/4*exp(2*I*alpha)*exp(I*gamma) assert Rotation.D(2,-2,-2,alpha,beta,gamma).doit() == (3+4*cos(beta)+cos(2*beta))/8*exp(2*I*alpha)*exp(2*I*gamma) # Numerical tests # j = 1/2 assert Rotation.D(S(1)/2,S(1)/2,S(1)/2,pi/2,pi/2,pi/2).doit() == -I*sqrt(2)/2 assert Rotation.D(S(1)/2,S(1)/2,-S(1)/2,pi/2,pi/2,pi/2).doit() == -sqrt(2)/2 assert Rotation.D(S(1)/2,-S(1)/2,S(1)/2,pi/2,pi/2,pi/2).doit() == sqrt(2)/2 assert Rotation.D(S(1)/2,-S(1)/2,-S(1)/2,pi/2,pi/2,pi/2).doit() == I*sqrt(2)/2 # j = 1 assert Rotation.D(1,1,1,pi/2,pi/2,pi/2).doit() == -1/2 assert Rotation.D(1,1,0,pi/2,pi/2,pi/2).doit() == I*sqrt(2)/2 assert Rotation.D(1,1,-1,pi/2,pi/2,pi/2).doit() == 1/2 assert Rotation.D(1,0,1,pi/2,pi/2,pi/2).doit() == -I*sqrt(2)/2 assert Rotation.D(1,0,0,pi/2,pi/2,pi/2).doit() == 0 assert Rotation.D(1,0,-1,pi/2,pi/2,pi/2).doit() == -I*sqrt(2)/2 assert Rotation.D(1,-1,1,pi/2,pi/2,pi/2).doit() == 1/2 assert Rotation.D(1,-1,0,pi/2,pi/2,pi/2).doit() == I*sqrt(2)/2 assert Rotation.D(1,-1,-1,pi/2,pi/2,pi/2).doit() == -1/2 # j = 3/2 assert Rotation.D(S(3)/2,S(3)/2,S(3)/2,pi/2,pi/2,pi/2).doit() == I*sqrt(2)/4 assert Rotation.D(S(3)/2,S(3)/2,S(1)/2,pi/2,pi/2,pi/2).doit() == sqrt(6)/4 assert Rotation.D(S(3)/2,S(3)/2,-S(1)/2,pi/2,pi/2,pi/2).doit() == -I*sqrt(6)/4 assert Rotation.D(S(3)/2,S(3)/2,-S(3)/2,pi/2,pi/2,pi/2).doit() == -sqrt(2)/4 assert Rotation.D(S(3)/2,S(1)/2,S(3)/2,pi/2,pi/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.D(S(3)/2,S(1)/2,S(1)/2,pi/2,pi/2,pi/2).doit() == I*sqrt(2)/4 assert Rotation.D(S(3)/2,S(1)/2,-S(1)/2,pi/2,pi/2,pi/2).doit() == -sqrt(2)/4 assert Rotation.D(S(3)/2,S(1)/2,-S(3)/2,pi/2,pi/2,pi/2).doit() == I*sqrt(6)/4 assert Rotation.D(S(3)/2,-S(1)/2,S(3)/2,pi/2,pi/2,pi/2).doit() == -I*sqrt(6)/4 assert Rotation.D(S(3)/2,-S(1)/2,S(1)/2,pi/2,pi/2,pi/2).doit() == sqrt(2)/4 assert Rotation.D(S(3)/2,-S(1)/2,-S(1)/2,pi/2,pi/2,pi/2).doit() == -I*sqrt(2)/4 assert Rotation.D(S(3)/2,-S(1)/2,-S(3)/2,pi/2,pi/2,pi/2).doit() == sqrt(6)/4 assert Rotation.D(S(3)/2,-S(3)/2,S(3)/2,pi/2,pi/2,pi/2).doit() == sqrt(2)/4 assert Rotation.D(S(3)/2,-S(3)/2,S(1)/2,pi/2,pi/2,pi/2).doit() == I*sqrt(6)/4 assert Rotation.D(S(3)/2,-S(3)/2,-S(1)/2,pi/2,pi/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.D(S(3)/2,-S(3)/2,-S(3)/2,pi/2,pi/2,pi/2).doit() == -I*sqrt(2)/4 # j = 2 assert Rotation.D(2,2,2,pi/2,pi/2,pi/2).doit() == 1/4 assert Rotation.D(2,2,1,pi/2,pi/2,pi/2).doit() == -I/2 assert Rotation.D(2,2,0,pi/2,pi/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2,2,-1,pi/2,pi/2,pi/2).doit() == I/2 assert Rotation.D(2,2,-2,pi/2,pi/2,pi/2).doit() == 1/4 assert Rotation.D(2,1,2,pi/2,pi/2,pi/2).doit() == I/2 assert Rotation.D(2,1,1,pi/2,pi/2,pi/2).doit() == 1/2 assert Rotation.D(2,1,0,pi/2,pi/2,pi/2).doit() == 0 assert Rotation.D(2,1,-1,pi/2,pi/2,pi/2).doit() == 1/2 assert Rotation.D(2,1,-2,pi/2,pi/2,pi/2).doit() == -I/2 assert Rotation.D(2,0,2,pi/2,pi/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2,0,1,pi/2,pi/2,pi/2).doit() == 0 assert Rotation.D(2,0,0,pi/2,pi/2,pi/2).doit() == -1/2 assert Rotation.D(2,0,-1,pi/2,pi/2,pi/2).doit() == 0 assert Rotation.D(2,0,-2,pi/2,pi/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2,-1,2,pi/2,pi/2,pi/2).doit() == -I/2 assert Rotation.D(2,-1,1,pi/2,pi/2,pi/2).doit() == 1/2 assert Rotation.D(2,-1,0,pi/2,pi/2,pi/2).doit() == 0 assert Rotation.D(2,-1,-1,pi/2,pi/2,pi/2).doit() == 1/2 assert Rotation.D(2,-1,-2,pi/2,pi/2,pi/2).doit() == I/2 assert Rotation.D(2,-2,2,pi/2,pi/2,pi/2).doit() == 1/4 assert Rotation.D(2,-2,1,pi/2,pi/2,pi/2).doit() == I/2 assert Rotation.D(2,-2,0,pi/2,pi/2,pi/2).doit() == -sqrt(6)/4 assert Rotation.D(2,-2,-1,pi/2,pi/2,pi/2).doit() == -I/2 assert Rotation.D(2,-2,-2,pi/2,pi/2,pi/2).doit() == 1/4 def test_wignerd(): j, m, mp, alpha, beta, gamma = symbols('j m mp alpha beta gamma') assert Rotation.D(j, m, mp, alpha, beta, gamma) == WignerD(j, m, mp, alpha, beta, gamma) assert Rotation.d(j, m, mp, beta) == WignerD(j, m, mp, 0, beta, 0) def test_jplus(): assert Commutator(Jplus, Jminus).doit() == 2*hbar*Jz assert qapply(Jplus*JzKet(1,1)) == 0 assert Jplus.matrix_element(1,1,1,1) == 0 assert Jplus.rewrite('xyz') == Jx + I*Jy def test_jminus(): assert qapply(Jminus*JzKet(1,-1)) == 0 assert Jminus.matrix_element(1,0,1,1) == sqrt(2)*hbar assert Jminus.rewrite('xyz') == Jx - I*Jy def test_j2(): j, m = symbols('j m') assert Commutator(J2, Jz).doit() == 0 assert qapply(J2*JzKet(1,1)) == 2*hbar**2*JzKet(1,1) assert qapply(J2*JzKet(j,m)) == j**2*hbar**2*JzKet(j,m)+j*hbar**2*JzKet(j,m) assert J2.matrix_element(1,1,1,1) == 2*hbar**2 def test_jx(): assert Commutator(Jx, Jz).doit() == -I*hbar*Jy assert qapply(Jx*JzKet(1,1)) == sqrt(2)*hbar*JzKet(1,0)/2 assert Jx.rewrite('plusminus') == (Jminus + Jplus)/2 assert represent(Jx, basis=Jz, j=1) == (represent(Jplus, basis=Jz, j=1)+represent(Jminus, basis=Jz, j=1))/2 def test_jy(): assert Commutator(Jy, Jz).doit() == I*hbar*Jx assert qapply(Jy*JzKet(1,1)) == I*sqrt(2)*hbar*JzKet(1,0)/2 assert Jy.rewrite('plusminus') == (Jplus - Jminus)/(2*I) assert represent(Jy, basis=Jz) == (represent(Jplus, basis=Jz) - represent(Jminus, basis=Jz))/(2*I) def test_jz(): assert Commutator(Jz, Jminus).doit() == -hbar*Jminus assert qapply(Jz*JzKet(2,1)) == hbar*JzKet(2,1) assert Jz.rewrite('plusminus') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_qapply.py0000644000175000017500000000541712014170666030343 0ustar georgeskgeorgeskfrom sympy import I, symbols, Symbol, sqrt, expand, Integer, srepr from sympy.physics.quantum.anticommutator import AntiCommutator from sympy.physics.quantum.commutator import Commutator from sympy.physics.quantum.dagger import Dagger from sympy.physics.quantum.operator import Operator from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.constants import hbar from sympy.physics.quantum.spin import ( Jx, Jy, Jz, Jplus, Jminus, J2, JzKet, JzBra, JxKet, JxBra ) from sympy.physics.quantum.gate import H from sympy.physics.quantum.qubit import Qubit j = Symbol('j') m = Symbol('m') jp = Symbol("j'") mp = Symbol("m'") z = JzKet(1,0) po = JzKet(1,1) mo = JzKet(1,-1) A = Operator('A') class Foo(Operator): def _apply_operator_JzKet(self, ket, **options): return ket def test_basic(): assert qapply(Jz*po) == hbar*po assert qapply(Jx*z) == hbar*po/sqrt(2) + hbar*mo/sqrt(2) assert qapply((Jplus + Jminus)*z/sqrt(2)) == hbar*po + hbar*mo assert qapply(Jz*(po + mo)) == hbar*po - hbar*mo assert qapply(Jz*po + Jz*mo) == hbar*po - hbar*mo assert qapply(Jminus*Jminus*po) == 2*hbar**2*mo assert qapply(Jplus**2*mo) == 2*hbar**2*po assert qapply(Jplus**2*Jminus**2*po) == 4*hbar**4*po def test_extra(): extra = z.dual*A*z assert qapply(Jz*po*extra) == hbar*po*extra assert qapply(Jx*z*extra) == (hbar*po/sqrt(2) + hbar*mo/sqrt(2))*extra assert qapply((Jplus + Jminus)*z/sqrt(2)*extra) == hbar*po*extra + hbar*mo*extra assert qapply(Jz*(po + mo)*extra) == hbar*po*extra - hbar*mo*extra assert qapply(Jz*po*extra + Jz*mo*extra) == hbar*po*extra - hbar*mo*extra assert qapply(Jminus*Jminus*po*extra) == 2*hbar**2*mo*extra assert qapply(Jplus**2*mo*extra) == 2*hbar**2*po*extra assert qapply(Jplus**2*Jminus**2*po*extra) == 4*hbar**4*po*extra def test_innerproduct(): assert qapply(po.dual*Jz*po, ip_doit=False) == hbar*(po.dual*po) assert qapply(po.dual*Jz*po) == hbar def test_zero(): assert qapply(0) == 0 assert qapply(Integer(0)) == 0 def test_commutator(): assert qapply(Commutator(Jx,Jy)*Jz*po) == I*hbar**3*po assert qapply(Commutator(J2, Jz)*Jz*po) == 0 assert qapply(Commutator(Jz, Foo('F'))*po) == 0 assert qapply(Commutator(Foo('F'), Jz)*po) == 0 def test_anticommutator(): assert qapply(AntiCommutator(Jz, Foo('F'))*po) == 2*hbar*po assert qapply(AntiCommutator(Foo('F'), Jz)*po) == 2*hbar*po def test_outerproduct(): e = Jz*(mo*po.dual)*Jz*po assert qapply(e) == -hbar**2*mo assert qapply(e, ip_doit=False) == -hbar**2*(po.dual*po)*mo assert qapply(e).doit() == -hbar**2*mo def test_dagger(): lhs = Dagger(Qubit(0))*Dagger(H(0)) rhs = Dagger(Qubit(1))/sqrt(2) + Dagger(Qubit(0))/sqrt(2) assert qapply(lhs, dagger=True) == rhs wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_tensorproduct.py0000644000175000017500000000254612014170666031750 0ustar georgeskgeorgeskfrom sympy import I, symbols, Matrix from sympy.physics.quantum.commutator import Commutator as Comm from sympy.physics.quantum.tensorproduct import TensorProduct from sympy.physics.quantum.tensorproduct import TensorProduct as TP from sympy.physics.quantum.tensorproduct import tensor_product_simp from sympy.physics.quantum.dagger import Dagger A,B,C = symbols('A,B,C', commutative=False) x = symbols('x') mat1 = Matrix([[1,2*I],[1+I,3]]) mat2 = Matrix([[2*I,3],[4*I,2]]) def test_tensor_product_dagger(): assert Dagger(TensorProduct(I*A, B)) ==\ -I*TensorProduct(Dagger(A),Dagger(B)) assert Dagger(TensorProduct(mat1,mat2)) ==\ TensorProduct(Dagger(mat1),Dagger(mat2)) def test_tensor_product_abstract(): assert TP(x*A,2*B) == x*2*TP(A,B) assert TP(A,B) != TP(B,A) assert TP(A,B).is_commutative == False assert isinstance(TP(A,B), TP) assert TP(A,B).subs(A,C) == TP(C,B) def test_tensor_product_expand(): assert TP(A+B,B+C).expand(tensorproduct=True) ==\ TP(A,B) + TP(A,C) + TP(B,B) + TP(B,C) def test_tensor_product_commutator(): assert TP(Comm(A,B),C).doit().expand(tensorproduct=True) ==\ TP(A*B,C) - TP(B*A,C) assert Comm(TP(A,B),TP(B,C)).doit() ==\ TP(A,B)*TP(B,C) - TP(B,C)*TP(A,B) def test_tensor_product_simp(): assert tensor_product_simp(TP(A,B)*TP(B,C)) == TP(A*B,B*C) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_dagger.py0000644000175000017500000000266312014170666030266 0ustar georgeskgeorgeskfrom sympy import I, Matrix, symbols, conjugate, Expr, Integer from sympy.physics.quantum.dagger import Dagger from sympy.external import import_module from sympy.utilities.pytest import skip def test_scalars(): x = symbols('x',complex=True) assert Dagger(x) == conjugate(x) assert Dagger(I*x) == -I*conjugate(x) i = symbols('i',real=True) assert Dagger(i) == i p = symbols('p') assert isinstance(Dagger(p), Dagger) i = Integer(3) assert Dagger(i) == i def test_matrix(): x = symbols('x') m = Matrix([[I,x*I],[2,4]]) assert Dagger(m) == m.H class Foo(Expr): def _eval_dagger(self): return I def test_eval_dagger(): f = Foo() d = Dagger(f) assert d == I np = import_module('numpy', min_python_version=(2, 6)) def test_numpy_dagger(): if not np: skip("numpy not installed or Python too old.") a = np.matrix([[1.0,2.0j],[-1.0j,2.0]]) adag = a.copy().transpose().conjugate() assert (Dagger(a) == adag).all() scipy = import_module('scipy', __import__kwargs={'fromlist':['sparse']}) def test_scipy_sparse_dagger(): if not np: skip("numpy not installed or Python too old.") if not scipy: skip("scipy not installed.") else: sparse = scipy.sparse a = sparse.csr_matrix([[1.0+0.0j,2.0j],[-1.0j,2.0+0.0j]]) adag = a.copy().transpose().conjugate() assert np.linalg.norm((Dagger(a) - adag).todense()) == 0.0 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_cg.py0000644000175000017500000001626112014170666027425 0ustar georgeskgeorgeskfrom __future__ import division from sympy import S, sqrt, Sum, symbols from sympy.physics.quantum.cg import Wigner3j, CG, cg_simp from sympy.physics.quantum.kronecker import KroneckerDelta def test_cg_simp_add(): j, m1, m1p, m2, m2p = symbols('j m1 m1p m2 m2p') # Test Varshalovich 8.7.1 Eq 1 a = CG(S(1)/2,S(1)/2,0,0,S(1)/2,S(1)/2) b = CG(S(1)/2,-S(1)/2,0,0,S(1)/2,-S(1)/2) c = CG(1,1,0,0,1,1) d = CG(1,0,0,0,1,0) e = CG(1,-1,0,0,1,-1) assert cg_simp(a+b) == 2 assert cg_simp(c+d+e) == 3 assert cg_simp(a+b+c+d+e) == 5 assert cg_simp(a+b+c) == 2+c assert cg_simp(2*a+b) == 2+a assert cg_simp(2*c+d+e) == 3+c assert cg_simp(5*a+5*b) == 10 assert cg_simp(5*c+5*d+5*e) == 15 assert cg_simp(-a-b) == -2 assert cg_simp(-c-d-e) == -3 assert cg_simp(-6*a-6*b) == -12 assert cg_simp(-4*c-4*d-4*e) == -12 a = CG(S(1)/2,S(1)/2,j,0,S(1)/2,S(1)/2) b = CG(S(1)/2,-S(1)/2,j,0,S(1)/2,-S(1)/2) c = CG(1,1,j,0,1,1) d = CG(1,0,j,0,1,0) e = CG(1,-1,j,0,1,-1) assert cg_simp(a+b) == 2*KroneckerDelta(j,0) assert cg_simp(c+d+e) == 3*KroneckerDelta(j,0) assert cg_simp(a+b+c+d+e) == 5*KroneckerDelta(j,0) assert cg_simp(a+b+c) == 2*KroneckerDelta(j,0)+c assert cg_simp(2*a+b) == 2*KroneckerDelta(j,0)+a assert cg_simp(2*c+d+e) == 3*KroneckerDelta(j,0)+c assert cg_simp(5*a+5*b) == 10*KroneckerDelta(j,0) assert cg_simp(5*c+5*d+5*e) == 15*KroneckerDelta(j,0) assert cg_simp(-a-b) == -2*KroneckerDelta(j,0) assert cg_simp(-c-d-e) == -3*KroneckerDelta(j,0) assert cg_simp(-6*a-6*b) == -12*KroneckerDelta(j,0) assert cg_simp(-4*c-4*d-4*e) == -12*KroneckerDelta(j,0) # Test Varshalovich 8.7.1 Eq 2 a = CG(S(1)/2,S(1)/2,S(1)/2,-S(1)/2,0,0) b = CG(S(1)/2,-S(1)/2,S(1)/2,S(1)/2,0,0) c = CG(1,1,1,-1,0,0) d = CG(1,0,1,0,0,0) e = CG(1,-1,1,1,0,0) assert cg_simp(a-b) == sqrt(2) assert cg_simp(c-d+e) == sqrt(3) assert cg_simp(a-b+c-d+e) == sqrt(2)+sqrt(3) assert cg_simp(a-b+c) == sqrt(2)+c assert cg_simp(2*a-b) == sqrt(2)+a assert cg_simp(2*c-d+e) == sqrt(3)+c assert cg_simp(5*a-5*b) == 5*sqrt(2) assert cg_simp(5*c-5*d+5*e) == 5*sqrt(3) assert cg_simp(-a+b) == -sqrt(2) assert cg_simp(-c+d-e) == -sqrt(3) assert cg_simp(-6*a+6*b) == -6*sqrt(2) assert cg_simp(-4*c+4*d-4*e) == -4*sqrt(3) a = CG(S(1)/2,S(1)/2,S(1)/2,-S(1)/2,j,0) b = CG(S(1)/2,-S(1)/2,S(1)/2,S(1)/2,j,0) c = CG(1,1,1,-1,j,0) d = CG(1,0,1,0,j,0) e = CG(1,-1,1,1,j,0) assert cg_simp(a-b) == sqrt(2)*KroneckerDelta(j,0) assert cg_simp(c-d+e) == sqrt(3)*KroneckerDelta(j,0) assert cg_simp(a-b+c-d+e) == sqrt(2)*KroneckerDelta(j,0)+sqrt(3)*KroneckerDelta(j,0) assert cg_simp(a-b+c) == sqrt(2)*KroneckerDelta(j,0)+c assert cg_simp(2*a-b) == sqrt(2)*KroneckerDelta(j,0)+a assert cg_simp(2*c-d+e) == sqrt(3)*KroneckerDelta(j,0)+c assert cg_simp(5*a-5*b) == 5*sqrt(2)*KroneckerDelta(j,0) assert cg_simp(5*c-5*d+5*e) == 5*sqrt(3)*KroneckerDelta(j,0) assert cg_simp(-a+b) == -sqrt(2)*KroneckerDelta(j,0) assert cg_simp(-c+d-e) == -sqrt(3)*KroneckerDelta(j,0) assert cg_simp(-6*a+6*b) == -6*sqrt(2)*KroneckerDelta(j,0) assert cg_simp(-4*c+4*d-4*e) == -4*sqrt(3)*KroneckerDelta(j,0) # Test Varshalovich 8.7.2 Eq 9 # alpha=alphap,beta=betap case # numerical a = CG(S(1)/2,S(1)/2,S(1)/2,-S(1)/2,1,0)**2 b = CG(S(1)/2,S(1)/2,S(1)/2,-S(1)/2,0,0)**2 c = CG(1,0,1,1,1,1)**2 d = CG(1,0,1,1,2,1)**2 assert cg_simp(a+b) == 1 assert cg_simp(c+d) == 1 assert cg_simp(a+b+c+d) == 2 assert cg_simp(4*a+4*b) == 4 assert cg_simp(4*c+4*d) == 4 assert cg_simp(5*a+3*b) == 3+2*a assert cg_simp(5*c+3*d) == 3+2*c assert cg_simp(-a-b) == -1 assert cg_simp(-c-d) == -1 # symbolic a = CG(S(1)/2,m1,S(1)/2,m2,1,1)**2 b = CG(S(1)/2,m1,S(1)/2,m2,1,0)**2 c = CG(S(1)/2,m1,S(1)/2,m2,1,-1)**2 d = CG(S(1)/2,m1,S(1)/2,m2,0,0)**2 assert cg_simp(a+b+c+d) == 1 assert cg_simp(4*a+4*b+4*c+4*d) == 4 assert cg_simp(3*a+5*b+3*c+4*d) == 3+2*b+d assert cg_simp(-a-b-c-d) == -1 a = CG(1,m1,1,m2,2,2)**2 b = CG(1,m1,1,m2,2,1)**2 c = CG(1,m1,1,m2,2,0)**2 d = CG(1,m1,1,m2,2,-1)**2 e = CG(1,m1,1,m2,2,-2)**2 f = CG(1,m1,1,m2,1,1)**2 g = CG(1,m1,1,m2,1,0)**2 h = CG(1,m1,1,m2,1,-1)**2 i = CG(1,m1,1,m2,0,0)**2 assert cg_simp(a+b+c+d+e+f+g+h+i) == 1 assert cg_simp(4*(a+b+c+d+e+f+g+h+i)) == 4 assert cg_simp(a+b+2*c+d+4*e+f+g+h+i) == 1+c+3*e assert cg_simp(-a-b-c-d-e-f-g-h-i) == -1 # alpha!=alphap or beta!=betap case # numerical a = CG(S(1)/2,S(1)/2,S(1)/2,-S(1)/2,1,0)*CG(S(1)/2,-S(1)/2,S(1)/2,S(1)/2,1,0) b = CG(S(1)/2,S(1)/2,S(1)/2,-S(1)/2,0,0)*CG(S(1)/2,-S(1)/2,S(1)/2,S(1)/2,0,0) c = CG(1,1,1,0,2,1)*CG(1,0,1,1,2,1) d = CG(1,1,1,0,1,1)*CG(1,0,1,1,1,1) assert cg_simp(a+b) == 0 assert cg_simp(c+d) == 0 # symbolic a = CG(S(1)/2,m1,S(1)/2,m2,1,1)*CG(S(1)/2,m1p,S(1)/2,m2p,1,1) b = CG(S(1)/2,m1,S(1)/2,m2,1,0)*CG(S(1)/2,m1p,S(1)/2,m2p,1,0) c = CG(S(1)/2,m1,S(1)/2,m2,1,-1)*CG(S(1)/2,m1p,S(1)/2,m2p,1,-1) d = CG(S(1)/2,m1,S(1)/2,m2,0,0)*CG(S(1)/2,m1p,S(1)/2,m2p,0,0) assert cg_simp(a+b+c+d) == KroneckerDelta(m1,m1p)*KroneckerDelta(m2,m2p) a = CG(1,m1,1,m2,2,2)*CG(1,m1p,1,m2p,2,2) b = CG(1,m1,1,m2,2,1)*CG(1,m1p,1,m2p,2,1) c = CG(1,m1,1,m2,2,0)*CG(1,m1p,1,m2p,2,0) d = CG(1,m1,1,m2,2,-1)*CG(1,m1p,1,m2p,2,-1) e = CG(1,m1,1,m2,2,-2)*CG(1,m1p,1,m2p,2,-2) f = CG(1,m1,1,m2,1,1)*CG(1,m1p,1,m2p,1,1) g = CG(1,m1,1,m2,1,0)*CG(1,m1p,1,m2p,1,0) h = CG(1,m1,1,m2,1,-1)*CG(1,m1p,1,m2p,1,-1) i = CG(1,m1,1,m2,0,0)*CG(1,m1p,1,m2p,0,0) assert cg_simp(a+b+c+d+e+f+g+h+i) == KroneckerDelta(m1,m1p)*KroneckerDelta(m2,m2p) def test_cg_simp_sum(): x, a, b, c, cp, alpha, beta, gamma, gammap = symbols('x a b c cp alpha beta gamma gammap') # Varshalovich 8.7.1 Eq 1 assert cg_simp(x * Sum(CG(a,alpha,b,0,a,alpha), (alpha,-a,a))) == x*(2*a+1)*KroneckerDelta(b,0) assert cg_simp(x * Sum(CG(a,alpha,b,0,a,alpha), (alpha,-a,a))+CG(1,0,1,0,1,0)) == x*(2*a+1)*KroneckerDelta(b,0)+CG(1,0,1,0,1,0) assert cg_simp(2 * Sum(CG(1,alpha,0,0,1,alpha), (alpha,-1,1))) == 6 # Varshalovich 8.7.1 Eq 2 assert cg_simp(x*Sum((-1)**(a-alpha) * CG(a,alpha,a,-alpha,c,0), (alpha,-a,a))) == x*sqrt(2*a+1)*KroneckerDelta(c,0) assert cg_simp(3*Sum((-1)**(2-alpha) * CG(2,alpha,2,-alpha,0,0), (alpha,-2,2))) == 3*sqrt(5) # Varshalovich 8.7.2 Eq 4 assert cg_simp(Sum(CG(a,alpha,b,beta,c,gamma)*CG(a,alpha,b,beta,cp,gammap),(alpha,-a,a),(beta,-b,b))) == KroneckerDelta(c,cp)*KroneckerDelta(gamma,gammap) assert cg_simp(Sum(CG(a,alpha,b,beta,c,gamma)*CG(a,alpha,b,beta,c,gammap),(alpha,-a,a),(beta,-b,b))) == KroneckerDelta(gamma,gammap) assert cg_simp(Sum(CG(a,alpha,b,beta,c,gamma)*CG(a,alpha,b,beta,cp,gamma),(alpha,-a,a),(beta,-b,b))) == KroneckerDelta(c,cp) assert cg_simp(Sum(CG(a,alpha,b,beta,c,gamma)**2,(alpha,-a,a),(beta,-b,b))) == 1 assert cg_simp(Sum(CG(2,alpha,1,beta,2,gamma)*CG(2,alpha,1,beta,2,gammap), (alpha,-2,2), (beta,-1,1))) == KroneckerDelta(gamma,gammap) def test_doit(): assert Wigner3j(1/2,-1/2,1/2,1/2,0,0).doit() == -sqrt(2)/2 assert CG(1/2,1/2,1/2,-1/2,1,0).doit() == sqrt(2)/2 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_qubit.py0000644000175000017500000001272012014170666030154 0ustar georgeskgeorgeskfrom sympy.physics.quantum.qubit import (measure_all, measure_partial, matrix_to_qubit, qubit_to_matrix, IntQubit, IntQubitBra, QubitBra) from sympy.physics.quantum.gate import (HadamardGate, CNOT, XGate, ZGate, YGate, PhaseGate) from sympy.physics.quantum.represent import represent from sympy.physics.quantum.qapply import qapply from sympy import symbols, Rational, sqrt from sympy.core.numbers import Integer from sympy.physics.quantum.shor import Qubit from sympy.core.containers import Tuple from sympy.matrices.matrices import Matrix import random x, y = symbols('x,y') epsilon = .000001 def test_Qubit(): array = [0,0,1,1,0] qb = Qubit('00110') assert qb.flip(0) == Qubit('00111') assert qb.flip(1) == Qubit('00100') assert qb.flip(4) == Qubit('10110') assert qb.dimension == 5 for i in range(5): assert qb[i] == array[4-i] assert len(qb) == 5 qb = Qubit('110') def test_QubitBra(): assert Qubit(0).dual_class == QubitBra assert QubitBra(0).dual_class == Qubit assert represent(Qubit(1,1,0), nqubits=3).H ==\ represent(QubitBra(1,1,0), nqubits=3) assert Qubit(0,1)._eval_innerproduct_QubitBra(QubitBra(1,0)) == Integer(0) assert Qubit(0,1)._eval_innerproduct_QubitBra(QubitBra(0,1)) == Integer(1) def test_IntQubit(): assert IntQubit(8).as_int() == 8 assert IntQubit(8).qubit_values == (1,0,0,0) assert IntQubit(7, 4).qubit_values == (0,1,1,1) assert IntQubit(3) == IntQubit(3,2) #test Dual Classes assert IntQubit(3).dual_class == IntQubitBra assert IntQubitBra(3).dual_class == IntQubit def test_superposition_of_states(): assert qapply(CNOT(0,1)*HadamardGate(0)*(1/sqrt(2)*Qubit('01') + 1/sqrt(2)*Qubit('10'))).expand() == (Qubit('01')/2 + Qubit('00')/2 - Qubit('11')/2 +\ Qubit('10')/2) assert matrix_to_qubit(represent(CNOT(0,1)*HadamardGate(0)\ *(1/sqrt(2)*Qubit('01') + 1/sqrt(2)*Qubit('10')), nqubits=2))\ == (Qubit('01')/2 + Qubit('00')/2 - Qubit('11')/2 + Qubit('10')/2) #test apply methods def test_apply_represent_equality(): gates = [HadamardGate(int(3*random.random())),\ XGate(int(3*random.random())), ZGate(int(3*random.random())),\ YGate(int(3*random.random())), ZGate(int(3*random.random())),\ PhaseGate(int(3*random.random()))] circuit = Qubit(int(random.random()*2),int(random.random()*2),\ int(random.random()*2),int(random.random()*2),int(random.random()*2),\ int(random.random()*2)) for i in range(int(random.random()*6)): circuit = gates[int(random.random()*6)]*circuit mat = represent(circuit, nqubits=6) states = qapply(circuit) state_rep = matrix_to_qubit(mat) states = states.expand() state_rep = state_rep.expand() assert state_rep == states def test_matrix_to_qubits(): assert matrix_to_qubit(Matrix([1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]))\ == Qubit(0,0,0,0) assert qubit_to_matrix(Qubit(0,0,0,0)) ==\ Matrix([1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]) assert matrix_to_qubit(sqrt(2)*2*Matrix([1,1,1,1,1,1,1,1])) ==\ (2*sqrt(2)*(Qubit(0,0,0) + Qubit(0,0,1) + Qubit(0,1,0) + Qubit(0,1,1)\ + Qubit(1,0,0) + Qubit(1,0,1) + Qubit(1,1,0) + Qubit(1,1,1))).expand() assert qubit_to_matrix(2*sqrt(2)*(Qubit(0,0,0) + Qubit(0,0,1) + Qubit(0,1,0)\ + Qubit(0,1,1) + Qubit(1,0,0) + Qubit(1,0,1) + Qubit(1,1,0) + Qubit(1,1,1)))\ == sqrt(2)*2*Matrix([1,1,1,1,1,1,1,1]) def test_measure_normalize(): a,b = symbols('a b') state = a*Qubit('110') + b*Qubit('111') assert measure_partial(state, (0,), normalize=False) ==\ [(a*Qubit('110'), a*a.conjugate()), (b*Qubit('111'),b*b.conjugate())] assert measure_all(state, normalize=False) ==\ [(Qubit('110'), a*a.conjugate()),(Qubit('111'), b*b.conjugate())] def test_measure_partial(): #Basic test of collapse of entangled two qubits (Bell States) state = Qubit('01') + Qubit('10') assert sorted(measure_partial(state, (0,))) ==\ [(Qubit('01'), Rational(1,2)), (Qubit('10'), Rational(1,2))] assert sorted(measure_partial(state, (0,))) ==\ sorted(measure_partial(state, (1,))) #Test of more complex collapse and probability calculation state1 = sqrt(2)/sqrt(3)*Qubit('00001') + 1/sqrt(3)*Qubit('11111') assert measure_partial(state1, (0,)) ==\ [(sqrt(2)/sqrt(3)*Qubit('00001') + 1/sqrt(3)*Qubit('11111'), 1)] assert measure_partial(state1, (1,2)) == measure_partial(state1, (3,4)) assert measure_partial(state1, (1,2,3)) ==\ [(Qubit('00001'), Rational(2,3)), (Qubit('11111'), Rational(1,3))] #test of measuring multiple bits at once state2 = Qubit('1111') + Qubit('1101') + Qubit('1011') + Qubit('1000') assert sorted(measure_partial(state2, (0,1,3))) ==\ sorted([(Qubit('1011')/sqrt(2) + Qubit('1111')/sqrt(2), Rational(1,2)), \ (Qubit('1101'), Rational(1,4)), (Qubit('1000'), Rational(1,4))]) assert sorted(measure_partial(state2, (0,))) ==\ sorted([(Qubit('1111')/sqrt(3) + Qubit('1101')/sqrt(3) + Qubit('1011')/sqrt(3), Rational(3,4)),\ (Qubit('1000'), Rational(1,4))]) def test_measure_all(): assert measure_all(Qubit('11')) == [(Qubit('11'), 1)] state = Qubit('11') + Qubit('10') assert sorted(measure_all(state)) == sorted([(Qubit('11'), Rational(1,2)),\ (Qubit('10'), Rational(1,2))]) state2 = Qubit('11')/sqrt(5) + 2*Qubit('00')/sqrt(5) assert sorted(measure_all(state2)) == sorted([(Qubit('11'), Rational(1,5)), \ (Qubit('00'), Rational(4,5))]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_qexpr.py0000644000175000017500000000232012014170666030162 0ustar georgeskgeorgeskfrom sympy import Symbol, Integer from sympy.physics.quantum.qexpr import QExpr, _qsympify_sequence from sympy.physics.quantum.hilbert import HilbertSpace from sympy.core.containers import Tuple x = Symbol('x') y = Symbol('y') def test_qexpr_new(): q = QExpr(0) assert q.label == (0,) assert q.hilbert_space == HilbertSpace() assert q.is_commutative == False q = QExpr(0,1) assert q.label == (Integer(0),Integer(1)) q = QExpr._new_rawargs(HilbertSpace(), Integer(0), Integer(1)) assert q.label == (Integer(0),Integer(1)) assert q.hilbert_space == HilbertSpace() def test_qexpr_commutative(): q1 = QExpr(x) q2 = QExpr(y) assert q1.is_commutative == False assert q2.is_commutative == False assert q1*q2 != q2*q1 q = QExpr._new_rawargs(0,1,HilbertSpace()) assert q.is_commutative == False def test_qexpr_subs(): q1 = QExpr(x,y) assert q1.subs(x, y) == QExpr(y,y) assert q1.subs({x:1,y:2}) == QExpr(1,2) def test_qsympify(): assert _qsympify_sequence([[1,2], [1,3]]) == (Tuple(1,2), Tuple(1,3)) assert _qsympify_sequence(([1,2,[3,4,[2,]],1],3)) ==\ (Tuple(1,2,Tuple(3,4,Tuple(2,)),1),3) assert _qsympify_sequence((1,)) == (1,) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/tests/test_shor.py0000644000175000017500000000165112014170666030004 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import XFAIL from sympy.physics.quantum.qapply import qapply from sympy.physics.quantum.qubit import Qubit from sympy.physics.quantum.shor import ( CMod, continued_fraction, getr, arr ) @XFAIL def test_CMod(): assert qapply(CMod(4, 2, 2)*Qubit(0,0,1,0,0,0,0,0)) ==\ Qubit(0,0,1,0,0,0,0,0) assert qapply(CMod(5, 5, 7)*Qubit(0,0,1,0,0,0,0,0,0,0)) ==\ Qubit(0,0,1,0,0,0,0,0,1,0) assert qapply(CMod(3, 2, 3)*Qubit(0,1,0,0,0,0)) ==\ Qubit(0,1,0,0,0,1) def test_continued_frac(): assert continued_fraction(3245, 10000) == [0,3,12,4,13] assert continued_fraction(1932, 2568) == [0, 1, 3, 26, 2] assert continued_fraction(6589, 2569) == [2, 1, 1, 3, 2, 1, 3, 1, 23] assert getr(513, 1024, 10) == 2 assert getr(169, 1024, 11) == 6 assert getr(314, 4096, 16) == 13 assert arr(5,3) == [1,0,1] assert arr(4,3) == [1,0,0] assert arr(8,5) == [0,1,0,0,0] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/__init__.py0000644000175000017500000000271712014170666026373 0ustar georgeskgeorgesk__all__ = [] # The following pattern is used below for importing sub-modules: # # 1. "from foo import *". This imports all the names from foo.__all__ into # this module. But, this does not put those names into the __all__ of # this module. This enables "from sympy.physics.quantum import State" to # work. # 2. "import foo; __all__.extend(foo.__all__)". This adds all the names in # foo.__all__ to the __all__ of this module. The names in __all__ # determine which names are imported when # "from sympy.physics.quantum import *" is done. import anticommutator from anticommutator import * __all__.extend(anticommutator.__all__) import qapply as qapmod from qapply import * __all__.extend(qapmod.__all__) import commutator from commutator import * __all__.extend(commutator.__all__) import dagger from dagger import * __all__.extend(dagger.__all__) import hilbert from hilbert import * __all__.extend(hilbert.__all__) import innerproduct from innerproduct import * __all__.extend(innerproduct.__all__) import kronecker from kronecker import * __all__.extend(kronecker.__all__) import operator from operator import * __all__.extend(operator.__all__) import represent as repmod from represent import * __all__.extend(repmod.__all__) import state from state import * __all__.extend(state.__all__) import tensorproduct from tensorproduct import * __all__.extend(tensorproduct.__all__) import constants from constants import * __all__.extend(constants.__all__) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/quantum/qexpr.py0000644000175000017500000003060612014170666025771 0ustar georgeskgeorgesk from sympy import Expr, sympify, Symbol, Matrix from sympy.printing.pretty.stringpict import prettyForm from sympy.core.containers import Tuple from sympy.core.compatibility import is_sequence from sympy.physics.quantum.matrixutils import ( numpy_ndarray, scipy_sparse_matrix, to_sympy, to_numpy, to_scipy_sparse ) __all__ = [ 'QuantumError', 'QExpr' ] #----------------------------------------------------------------------------- # Error handling #----------------------------------------------------------------------------- class QuantumError(Exception): pass def _qsympify_sequence(seq): """Convert elements of a sequence to standard form. This is like sympify, but it performs special logic for arguments passed to QExpr. The following conversions are done: * (list, tuple, Tuple) => _qsympify_sequence each element and convert sequence to a Tuple. * basestring => Symbol * Matrix => Matrix * other => sympify Strings are passed to Symbol, not sympify to make sure that variables like 'pi' are kept as Symbols, not the Sympy built-in number subclasses. Examples ======== >>> from sympy.physics.quantum.qexpr import _qsympify_sequence >>> _qsympify_sequence((1,2,[3,4,[1,]])) (1, 2, (3, 4, (1,))) """ return tuple(__qsympify_sequence_helper(seq)) def __qsympify_sequence_helper(seq): """ Helper function for _qsympify_sequence This function does the actual work. """ #base case. If not a list, do Sympification if not is_sequence(seq): if isinstance(seq, Matrix): return seq elif isinstance(seq, basestring): return Symbol(seq) else: return sympify(seq) #if list, recurse on each item in the list result = [__qsympify_sequence_helper(item) for item in seq] return Tuple(*result) #----------------------------------------------------------------------------- # Basic Quantum Expression from which all objects descend #----------------------------------------------------------------------------- class QExpr(Expr): """A base class for all quantum object like operators and states.""" # In sympy, slots are for instance attributes that are computed # dynamically by the __new__ method. They are not part of args, but they # derive from args. # The Hilbert space a quantum Object belongs to. __slots__ = ['hilbert_space'] # The separator used in printing the label. _label_separator = u'' def __new__(cls, *args, **old_assumptions): """Construct a new quantum object. Parameters ========== args : tuple The list of numbers or parameters that uniquely specify the quantum object. For a state, this will be its symbol or its set of quantum numbers. Examples ======== >>> from sympy.physics.quantum.qexpr import QExpr >>> q = QExpr(0) >>> q 0 >>> q.label (0,) >>> q.hilbert_space H >>> q.args (0,) >>> q.is_commutative False """ # First compute args and call Expr.__new__ to create the instance args = cls._eval_args(args) inst = Expr.__new__(cls, *args, **{'commutative':False}) # Now set the slots on the instance inst.hilbert_space = cls._eval_hilbert_space(args) return inst @classmethod def _new_rawargs(cls, hilbert_space, *args): """Create new instance of this class with hilbert_space and args. This is used to bypass the more complex logic in the ``__new__`` method in cases where you already have the exact ``hilbert_space`` and ``args``. This should be used when you are positive these arguments are valid, in their final, proper form and want to optimize the creation of the object. """ obj = Expr.__new__(cls, *args, **{'commutative':False}) obj.hilbert_space = hilbert_space return obj #------------------------------------------------------------------------- # Properties #------------------------------------------------------------------------- @property def label(self): """The label is the unique set of identifiers for the object. Usually, this will include all of the information about the state *except* the time (in the case of time-dependent objects). This must be a tuple, rather than a Tuple. """ return self.args @property def is_symbolic(self): return True #------------------------------------------------------------------------- # _eval_* methods #------------------------------------------------------------------------- @classmethod def _eval_args(cls, args): """Process the args passed to the __new__ method. This simply runs args through _qsympify_sequence. """ return _qsympify_sequence(args) @classmethod def _eval_hilbert_space(cls, args): """Compute the Hilbert space instance from the args. """ from sympy.physics.quantum.hilbert import HilbertSpace return HilbertSpace() def _eval_dagger(self): """Compute the Dagger of this state.""" raise NotImplementedError('_eval_dagger not defined on: %r' % self) #------------------------------------------------------------------------- # Printing #------------------------------------------------------------------------- # Utilities for printing: these operate on raw sympy objects def _print_sequence(self, seq, sep, printer, *args): result = [] for item in seq: result.append(printer._print(item, *args)) return sep.join(result) def _print_sequence_pretty(self, seq, sep, printer, *args): pform = printer._print(seq[0], *args) for item in seq[1:]: pform = prettyForm(*pform.right((sep))) pform = prettyForm(*pform.right((printer._print(item, *args)))) return pform # Utilities for printing: these operate prettyForm objects def _print_subscript_pretty(self, a, b): top = prettyForm(*b.left(' '*a.width())) bot = prettyForm(*a.right(' '*b.width())) return prettyForm(binding=prettyForm.POW, *bot.below(top)) def _print_superscript_pretty(self, a, b): return a**b def _print_parens_pretty(self, pform, left='(', right=')'): return prettyForm(*pform.parens(left=left, right=right)) # Printing of labels def _print_label(self, printer, *args): return self._print_sequence( self.label, self._label_separator, printer, *args ) def _print_label_repr(self, printer, *args): return self._print_sequence( self.label, ',', printer, *args ) def _print_label_pretty(self, printer, *args): return self._print_sequence_pretty( self.label, self._label_separator, printer, *args ) def _print_label_latex(self, printer, *args): return self._print_sequence( self.label, self._label_separator, printer, *args ) # Printing of contents def _print_contents(self, printer, *args): return self._print_label(printer, *args) def _print_contents_repr(self, printer, *args): return self._print_label_repr(printer, *args) def _print_contents_pretty(self, printer, *args): return self._print_label_pretty(printer, *args) def _print_contents_latex(self, printer, *args): return self._print_label_latex(printer, *args) # Main methods def _sympystr(self, printer, *args): return self._print_contents(printer, *args) def _sympyrepr(self, printer, *args): classname = self.__class__.__name__ contents = self._print_contents_repr(printer, *args) return '%s(%s)' % (classname, contents) def _pretty(self, printer, *args): pform = self._print_contents_pretty(printer, *args) return pform def _latex(self, printer, *args): return self._print_contents_latex(printer, *args) #------------------------------------------------------------------------- # Methods from Basic and Expr #------------------------------------------------------------------------- def doit(self, **kw_args): return self def _eval_rewrite(self, pattern, rule, **hints): # TODO: Make Basic.rewrite get the rule using the class name rather # than str(). See L1072 of basic.py. # This will call self.rule(*self.args) for rewriting. if hints.get('deep', False): args = [ a._eval_rewrite(pattern, rule, **hints) for a in self ] else: args = self.args if pattern is None or isinstance(self, pattern): if hasattr(self, rule): rewritten = getattr(self, rule)(*args) if rewritten is not None: return rewritten return self #------------------------------------------------------------------------- # Represent #------------------------------------------------------------------------- def _represent_default_basis(self, **options): raise NotImplementedError('This object does not have a default basis') def _represent(self, **options): """Represent this object in a given basis. This method dispatches to the actual methods that perform the representation. Subclases of QExpr should define various methods to determine how the object will be represented in various bases. The format of these methods is:: def _represent_BasisName(self, basis, **options): Thus to define how a quantum object is represented in the basis of the operator Position, you would define:: def _represent_Position(self, basis, **options): Usually, basis object will be instances of Operator subclasses, but there is a chance we will relax this in the future to accomodate other types of basis sets that are not associated with an operator. If the ``format`` option is given it can be ("sympy", "numpy", "scipy.sparse"). This will ensure that any matrices that result from representing the object are returned in the appropriate matrix format. Parameters ========== basis : Operator The Operator whose basis functions will be used as the basis for representation. options : dict A dictionary of key/value pairs that give options and hints for the representation, such as the number of basis functions to be used. """ basis = options.pop('basis',None) if basis is None: result = self._represent_default_basis(**options) else: result = dispatch_method(self, '_represent', basis, **options) # If we get a matrix representation, convert it to the right format. format = options.get('format', 'sympy') if format == 'sympy' and not isinstance(result, Matrix): result = to_sympy(result) elif format == 'numpy' and not isinstance(result, numpy_ndarray): result = to_numpy(result) elif format == 'scipy.sparse' and\ not isinstance(result, scipy_sparse_matrix): result = to_scipy_sparse(result) return result def split_commutative_parts(e): """Split into commutative and non-commutative parts.""" c_part = [p for p in e.args if p.is_commutative] nc_part = [p for p in e.args if not p.is_commutative] return c_part, nc_part def split_qexpr_parts(e): """Split an expression into Expr and noncommutative QExpr parts.""" expr_part = [] qexpr_part = [] for arg in e.args: if not isinstance(arg, QExpr): expr_part.append(arg) else: qexpr_part.append(arg) return expr_part, qexpr_part def dispatch_method(self, basename, arg, **options): """Dispatch a method to the proper handlers.""" method_name = '%s_%s' % (basename, arg.__class__.__name__) if hasattr(self, method_name): f = getattr(self, method_name) # This can raise and we will allow it to propagate. result = f(arg, **options) if result is not None: return result raise NotImplementedError( "%s.%s can't handle: %r" % \ (self.__class__.__name__, basename, arg) ) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/sho.py0000644000175000017500000000425212014170666023727 0ustar georgeskgeorgeskfrom sympy.core import S, pi, Rational from sympy.functions import laguerre_l, sqrt, exp, factorial, factorial2 def R_nl(n, l, nu, r): """ Returns the radial wavefunction R_{nl} for a 3d isotropic harmonic oscillator. ``n`` the "nodal" quantum number. Corresponds to the number of nodes in the wavefunction. n >= 0 ``l`` the quantum number for orbital angular momentum ``nu`` mass-scaled frequency: nu = m*omega/(2*hbar) where `m' is the mass and `omega' the frequency of the oscillator. (in atomic units nu == omega/2) ``r`` Radial coordinate :Examples: >>> from sympy.physics.sho import R_nl >>> from sympy import var >>> var("r nu l") (r, nu, l) >>> R_nl(0, 0, 1, r) 2*2**(3/4)*exp(-r**2)/pi**(1/4) >>> R_nl(1, 0, 1, r) 4*2**(1/4)*3**(1/2)*(-2*r**2 + 3/2)*exp(-r**2)/(3*pi**(1/4)) l, nu and r may be symbolic: >>> R_nl(0, 0, nu, r) 2*2**(3/4)*(nu**(3/2))**(1/2)*exp(-nu*r**2)/pi**(1/4) >>> R_nl(0, l, 1, r) r**l*(2**(l + 3/2)*2**(l + 2)/(2*l + 1)!!)**(1/2)*exp(-r**2)/pi**(1/4) The normalization of the radial wavefunction is:: >>> from sympy import Integral, oo >>> Integral(R_nl(0, 0, 1, r)**2 * r**2, (r, 0, oo)).n() 1.00000000000000 >>> Integral(R_nl(1, 0, 1, r)**2 * r**2, (r, 0, oo)).n() 1.00000000000000 >>> Integral(R_nl(1, 1, 1, r)**2 * r**2, (r, 0, oo)).n() 1.00000000000000 """ n, l, nu, r = map(S, [n, l, nu, r]) # formula uses n >= 1 (instead of nodal n >= 0) n = n + 1 C = sqrt( ((2*nu)**(l + Rational(3, 2))*2**(n+l+1)*factorial(n-1))/ (sqrt(pi)*(factorial2(2*n + 2*l - 1))) ) return C*r**(l)*exp(-nu*r**2)*laguerre_l(n-1, l + S(1)/2, 2*nu*r**2) def E_nl(n, l, hw): """ Returns the Energy of an isotropic harmonic oscillator ``n`` the "nodal" quantum number ``l`` the orbital angular momentum ``hw`` the harmonic oscillator parameter. The unit of the returned value matches the unit of hw, since the energy is calculated as: E_nl = (2*n + l + 3/2)*hw """ return (2*n + l + Rational(3, 2))*hw wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/paulialgebra.py0000644000175000017500000000307312014170666025566 0ustar georgeskgeorgesk""" This module implements Pauli algebra by subclassing Symbol. Only algebraic properties of Pauli matrices are used (we don't use the Matrix class). See the documentation to the class Pauli for examples. See also: http://en.wikipedia.org/wiki/Pauli_matrices """ from sympy import Symbol, I def delta(i,j): if i==j: return 1 else: return 0 def epsilon(i,j,k): if (i,j,k) in [(1,2,3), (2,3,1), (3,1,2)]: return 1 elif (i,j,k) in [(1,3,2), (3,2,1), (2,1,3)]: return -1 else: return 0 class Pauli(Symbol): """ >>> from sympy.physics.paulialgebra import Pauli >>> Pauli(1) sigma1 >>> Pauli(1)*Pauli(2) I*sigma3 >>> Pauli(1)*Pauli(1) 1 >>> Pauli(3)**4 1 >>> Pauli(1)*Pauli(2)*Pauli(3) I """ __slots__ = ["i"] def __new__(cls, i): if not i in [1,2,3]: raise IndexError("Invalid Pauli index") obj = Symbol.__new__(cls, "sigma%d"%i, commutative=False) obj.i=i return obj def __getnewargs__(self): return (self.i,) # FIXME don't work for -I*Pauli(2)*Pauli(3) def __mul__(self, other): if isinstance(other, Pauli): j=self.i k=other.i return delta(j,k) \ +I*epsilon(j,k,1)*Pauli(1) \ +I*epsilon(j,k,2)*Pauli(2) \ +I*epsilon(j,k,3)*Pauli(3) return super(Pauli, self).__mul__(other) def _eval_power(b, e): if e.is_Integer and e.is_positive: return super(Pauli, b).__pow__(int(e) % 2) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/secondquant.py0000644000175000017500000026354612014170666025477 0ustar georgeskgeorgesk""" Second quantization operators and states for bosons. This follow the formulation of Fetter and Welecka, "Quantum Theory of Many-Particle Systems." """ from sympy import ( Basic, Expr, Function, Mul, sympify, Integer, Add, sqrt, zeros, Pow, I, S, Symbol, Tuple, Dummy ) from sympy.core.sympify import sympify from sympy.core.cache import cacheit from sympy.core.symbol import Dummy from sympy.printing.str import StrPrinter from sympy.core.compatibility import reduce __all__ = [ 'Dagger', 'KroneckerDelta', 'BosonicOperator', 'AnnihilateBoson', 'CreateBoson', 'AnnihilateFermion', 'CreateFermion', 'FockState', 'FockStateBra', 'FockStateKet', 'BBra', 'BKet', 'FBra', 'FKet', 'F', 'Fd', 'B', 'Bd', 'apply_operators', 'InnerProduct', 'BosonicBasis', 'VarBosonicBasis', 'FixedBosonicBasis', 'Commutator', 'matrix_rep', 'contraction', 'wicks', 'NO', 'evaluate_deltas', 'AntiSymmetricTensor', 'substitute_dummies', 'PermutationOperator', 'simplify_index_permutations', ] class SecondQuantizationError(Exception): pass class AppliesOnlyToSymbolicIndex(SecondQuantizationError): pass class ContractionAppliesOnlyToFermions(SecondQuantizationError): pass class ViolationOfPauliPrinciple(SecondQuantizationError): pass class SubstitutionOfAmbigousOperatorFailed(SecondQuantizationError): pass class WicksTheoremDoesNotApply(SecondQuantizationError): pass class Dagger(Expr): """ Hermitian conjugate of creation/annihilation operators. Example: >>> from sympy import I >>> from sympy.physics.secondquant import Dagger, B, Bd >>> Dagger(2*I) -2*I >>> Dagger(B(0)) CreateBoson(0) >>> Dagger(Bd(0)) AnnihilateBoson(0) """ def __new__(cls, arg): arg = sympify(arg) r = cls.eval(arg) if isinstance(r, Basic): return r obj = Basic.__new__(cls, arg) return obj @classmethod def eval(cls, arg): """ Evaluates the Dagger instance. Example: >>> from sympy import I >>> from sympy.physics.secondquant import Dagger, B, Bd >>> Dagger(2*I) -2*I >>> Dagger(B(0)) CreateBoson(0) >>> Dagger(Bd(0)) AnnihilateBoson(0) The eval() method is called automatically. """ try: d = arg._dagger_() except: if isinstance(arg, Basic): if arg.is_Add: return Add(*tuple(map(Dagger, arg.args))) if arg.is_Mul: return Mul(*tuple(map(Dagger, reversed(arg.args)))) if arg.is_Number: return arg if arg.is_Pow: return Pow(Dagger(arg.args[0]),arg.args[1]) if arg == I: return -arg else: return None else: return d def _eval_subs(self, old, new): if self == old: return new r = Dagger(self.args[0].subs(old, new)) return r def _dagger_(self): return self.args[0] class TensorSymbol(Expr): is_commutative = True class AntiSymmetricTensor(TensorSymbol): """Stores upper and lower indices in separate Tuple's. Each group of indices is assumed to be antisymmetric. Examples: >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i j', below_fermi=True) >>> a, b = symbols('a b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (i, a), (b, j)) -AntiSymmetricTensor(v, (a, i), (b, j)) As you can see, the indices are automatically sorted to a canonical form. """ nargs = 3 def __new__(cls, symbol, upper, lower): try: upper, signu = _sort_anticommuting_fermions(upper, key=cls._sortkey) lower, signl = _sort_anticommuting_fermions(lower, key=cls._sortkey) except ViolationOfPauliPrinciple: return S.Zero symbol = sympify(symbol) upper = Tuple(*upper) lower = Tuple(*lower) if (signu + signl) % 2: return -TensorSymbol.__new__(cls, symbol, upper, lower) else: return TensorSymbol.__new__(cls, symbol, upper, lower) @classmethod def _sortkey(cls, index): """Key for sorting of indices. particle < hole < general FIXME: This is a bottle-neck, can we do it faster? """ h = hash(index) if isinstance(index, Dummy): if index.assumptions0.get('above_fermi'): return (20, h) elif index.assumptions0.get('below_fermi'): return (21, h) else: return (22, h) if index.assumptions0.get('above_fermi'): return (10, h) elif index.assumptions0.get('below_fermi'): return (11, h) else: return (12, h) def _latex(self,printer): return "%s^{%s}_{%s}" %( self.symbol, "".join([ i.name for i in self.args[1]]), "".join([ i.name for i in self.args[2]]) ) @property def symbol(self): """ Returns the symbol of the tensor. Example: >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (a, i), (b, j)).symbol v """ return self.args[0] @property def upper(self): """ Returns the upper indices. Example: >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (a, i), (b, j)).upper (a, i) """ return self.args[1] @property def lower(self): """ Returns the lower indices. Example: >>> from sympy import symbols >>> from sympy.physics.secondquant import AntiSymmetricTensor >>> i, j = symbols('i,j', below_fermi=True) >>> a, b = symbols('a,b', above_fermi=True) >>> AntiSymmetricTensor('v', (a, i), (b, j)) AntiSymmetricTensor(v, (a, i), (b, j)) >>> AntiSymmetricTensor('v', (a, i), (b, j)).lower (b, j) """ return self.args[2] def __str__(self): return "%s(%s,%s)" %self.args def doit(self, **kw_args): return self class KroneckerDelta(Function): """ Discrete delta function. >>> from sympy import symbols >>> from sympy.physics.secondquant import KroneckerDelta >>> i, j, k = symbols('i,j,k') >>> KroneckerDelta(i, j) KroneckerDelta(i, j) >>> KroneckerDelta(i, i) 1 >>> KroneckerDelta(i, i+1) 0 >>> KroneckerDelta(i, i+1+k) KroneckerDelta(i, i + k + 1) """ nargs = 2 is_commutative=True @classmethod def eval(cls, i, j): """ Evaluates the discrete delta function. >>> from sympy import symbols >>> from sympy.physics.secondquant import KroneckerDelta >>> i, j, k = symbols('i,j,k') >>> KroneckerDelta(i, j) KroneckerDelta(i, j) >>> KroneckerDelta(i, i) 1 >>> KroneckerDelta(i, i+1) 0 >>> KroneckerDelta(i, i+1+k) KroneckerDelta(i, i + k + 1) # indirect doctest """ if i > j: return cls(j,i) diff = i-j if diff == 0: return S.One elif diff.is_number: return S.Zero if i.assumptions0.get("below_fermi") and j.assumptions0.get("above_fermi"): return S.Zero if j.assumptions0.get("below_fermi") and i.assumptions0.get("above_fermi"): return S.Zero def _eval_subs(self, old, new): if self == old: return new r = KroneckerDelta(self.args[0].subs(old, new), self.args[1].subs(old, new)) return r @property def is_above_fermi(self): """ True if Delta can be non-zero above fermi >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> q = Symbol('q') >>> KroneckerDelta(p,a).is_above_fermi True >>> KroneckerDelta(p,i).is_above_fermi False >>> KroneckerDelta(p,q).is_above_fermi True """ if self.args[0].assumptions0.get("below_fermi"): return False if self.args[1].assumptions0.get("below_fermi"): return False return True @property def is_below_fermi(self): """ True if Delta can be non-zero below fermi >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> q = Symbol('q') >>> KroneckerDelta(p,a).is_below_fermi False >>> KroneckerDelta(p,i).is_below_fermi True >>> KroneckerDelta(p,q).is_below_fermi True """ if self.args[0].assumptions0.get("above_fermi"): return False if self.args[1].assumptions0.get("above_fermi"): return False return True @property def is_only_above_fermi(self): """ True if Delta is restricted to above fermi >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> q = Symbol('q') >>> KroneckerDelta(p,a).is_only_above_fermi True >>> KroneckerDelta(p,q).is_only_above_fermi False >>> KroneckerDelta(p,i).is_only_above_fermi False """ return ( self.args[0].assumptions0.get("above_fermi") or self.args[1].assumptions0.get("above_fermi") ) or False @property def is_only_below_fermi(self): """ True if Delta is restricted to below fermi >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> q = Symbol('q') >>> KroneckerDelta(p,i).is_only_below_fermi True >>> KroneckerDelta(p,q).is_only_below_fermi False >>> KroneckerDelta(p,a).is_only_below_fermi False """ return ( self.args[0].assumptions0.get("below_fermi") or self.args[1].assumptions0.get("below_fermi") ) or False @property def indices_contain_equal_information(self): """ Returns True if indices are either both above or below fermi. Example: >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> q = Symbol('q') >>> KroneckerDelta(p, q).indices_contain_equal_information True >>> KroneckerDelta(p, q+1).indices_contain_equal_information True >>> KroneckerDelta(i, p).indices_contain_equal_information False """ if (self.args[0].assumptions0.get("below_fermi") and self.args[1].assumptions0.get("below_fermi")): return True if (self.args[0].assumptions0.get("above_fermi") and self.args[1].assumptions0.get("above_fermi")): return True # if both indices are general we are True, else false return self.is_below_fermi and self.is_above_fermi @property def preferred_index(self): """ Returns the index which is preferred to keep in the final expression. The preferred index is the index with more information regarding fermi level. If indices contain same information, 'a' is preferred before 'b'. >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> j = Symbol('j',below_fermi=True) >>> p = Symbol('p') >>> KroneckerDelta(p,i).preferred_index i >>> KroneckerDelta(p,a).preferred_index a >>> KroneckerDelta(i,j).preferred_index i """ if self._get_preferred_index(): return self.args[1] else: return self.args[0] @property def killable_index(self): """ Returns the index which is preferred to substitute in the final expression. The index to substitute is the index with less information regarding fermi level. If indices contain same information, 'a' is preferred before 'b'. >>> from sympy.physics.secondquant import KroneckerDelta >>> from sympy import Symbol >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> j = Symbol('j',below_fermi=True) >>> p = Symbol('p') >>> KroneckerDelta(p,i).killable_index p >>> KroneckerDelta(p,a).killable_index p >>> KroneckerDelta(i,j).killable_index j """ if self._get_preferred_index(): return self.args[0] else: return self.args[1] def _get_preferred_index(self): """ Returns the index which is preferred to keep in the final expression. The preferred index is the index with more information regarding fermi level. If indices contain same information, index 0 is returned. """ if not self.is_above_fermi: if self.args[0].assumptions0.get("below_fermi"): return 0 else: return 1 elif not self.is_below_fermi: if self.args[0].assumptions0.get("above_fermi"): return 0 else: return 1 else: return 0 def _dagger_(self): return self def _latex(self,printer): return "\\delta_{%s%s}"% (self.args[0].name,self.args[1].name) def __repr__(self): return "KroneckerDelta(%s,%s)"% (self.args[0],self.args[1]) def __str__(self): if not self.is_above_fermi: return 'd<(%s,%s)'% (self.args[0],self.args[1]) elif not self.is_below_fermi: return 'd>(%s,%s)'% (self.args[0],self.args[1]) else: return 'd(%s,%s)'% (self.args[0],self.args[1]) class SqOperator(Expr): """ Base class for Second Quantization operators. """ op_symbol = 'sq' def __new__(cls, k): obj = Basic.__new__(cls, sympify(k), commutative=False) return obj def _eval_subs(self, old, new): if self == old: return new r = self.__class__(self.args[0].subs(old, new)) return r @property def state(self): """ Returns the state index related to this operator. >>> from sympy import Symbol >>> from sympy.physics.secondquant import F, Fd, B, Bd >>> p = Symbol('p') >>> F(p).state p >>> Fd(p).state p >>> B(p).state p >>> Bd(p).state p """ return self.args[0] @property def is_symbolic(self): """ Returns True if the state is a symbol (as opposed to a number). >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> p = Symbol('p') >>> F(p).is_symbolic True >>> F(1).is_symbolic False """ if self.state.is_Integer: return False else: return True def doit(self,**kw_args): """ FIXME: hack to prevent crash further up... """ return self def __repr__(self): return NotImplemented def __str__(self): return "%s(%r)" % (self.op_symbol, self.state) def apply_operator(self, state): """ Applies an operator to itself. """ raise NotImplementedError('implement apply_operator in a subclass') class BosonicOperator(SqOperator): pass class Annihilator(SqOperator): pass class Creator(SqOperator): pass class AnnihilateBoson(BosonicOperator, Annihilator): """ Bosonic annihilation operator """ op_symbol = 'b' def _dagger_(self): return CreateBoson(self.state) def apply_operator(self, state): if not self.is_symbolic and isinstance(state, FockStateKet): element = self.state amp = sqrt(state[element]) return amp*state.down(element) else: return Mul(self,state) def __repr__(self): return "AnnihilateBoson(%s)"%self.state class CreateBoson(BosonicOperator, Creator): """ Bosonic creation operator """ op_symbol = 'b+' def _dagger_(self): return AnnihilateBoson(self.state) def apply_operator(self, state): if not self.is_symbolic and isinstance(state, FockStateKet): element = self.state amp = sqrt(state[element] + 1) return amp*state.up(element) else: return Mul(self,state) def __repr__(self): return "CreateBoson(%s)"%self.state B = AnnihilateBoson Bd = CreateBoson class FermionicOperator(SqOperator): @property def is_restricted(self): """ Is this FermionicOperator restricted with respect to fermi level? Return values: 1 : restricted to orbits above fermi 0 : no restriction -1 : restricted to orbits below fermi >>> from sympy import Symbol >>> from sympy.physics.secondquant import F, Fd >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_restricted 1 >>> Fd(a).is_restricted 1 >>> F(i).is_restricted -1 >>> Fd(i).is_restricted -1 >>> F(p).is_restricted 0 >>> Fd(p).is_restricted 0 """ ass = self.args[0].assumptions0 if ass.get("below_fermi"): return -1 if ass.get("above_fermi"): return 1 return 0 @property def is_above_fermi(self): """ Does the index of this FermionicOperator allow values above fermi? >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_above_fermi True >>> F(i).is_above_fermi False >>> F(p).is_above_fermi True The same applies to creation operators Fd """ return not self.args[0].assumptions0.get("below_fermi") @property def is_below_fermi(self): """ Does the index of this FermionicOperator allow values below fermi? >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_below_fermi False >>> F(i).is_below_fermi True >>> F(p).is_below_fermi True The same applies to creation operators Fd """ return not self.args[0].assumptions0.get("above_fermi") @property def is_only_below_fermi(self): """ Is the index of this FermionicOperator restricted to values below fermi? >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_below_fermi False >>> F(i).is_only_below_fermi True >>> F(p).is_only_below_fermi False The same applies to creation operators Fd """ return self.is_below_fermi and not self.is_above_fermi @property def is_only_above_fermi(self): """ Is the index of this FermionicOperator restricted to values above fermi? >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_above_fermi True >>> F(i).is_only_above_fermi False >>> F(p).is_only_above_fermi False The same applies to creation operators Fd """ return self.is_above_fermi and not self.is_below_fermi def _sortkey(self): h = hash(self) if self.is_only_q_creator: return 1, h if self.is_only_q_annihilator: return 4, h if isinstance(self, Annihilator): return 3, h if isinstance(self, Creator): return 2, h class AnnihilateFermion(FermionicOperator, Annihilator): """ Fermionic annihilation operator """ op_symbol = 'f' def _dagger_(self): return CreateFermion(self.state) def apply_operator(self, state): if isinstance(state, FockStateFermionKet): element = self.state return state.down(element) elif isinstance(state, Mul): c_part, nc_part = split_commutative_parts(state) if isinstance(nc_part[0], FockStateFermionKet): element = self.state return Mul(*(c_part+[nc_part[0].down(element)]+nc_part[1:])) else: return Mul(self,state) else: return Mul(self,state) @property def is_q_creator(self): """ Can we create a quasi-particle? (create hole or create particle) If so, would that be above or below the fermi surface? >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_q_creator 0 >>> F(i).is_q_creator -1 >>> F(p).is_q_creator -1 """ if self.is_below_fermi: return -1 return 0 @property def is_q_annihilator(self): """ Can we destroy a quasi-particle? (annihilate hole or annihilate particle) If so, would that be above or below the fermi surface? >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=1) >>> i = Symbol('i',below_fermi=1) >>> p = Symbol('p') >>> F(a).is_q_annihilator 1 >>> F(i).is_q_annihilator 0 >>> F(p).is_q_annihilator 1 """ if self.is_above_fermi: return 1 return 0 @property def is_only_q_creator(self): """ Always create a quasi-particle? (create hole or create particle) >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_q_creator False >>> F(i).is_only_q_creator True >>> F(p).is_only_q_creator False """ return self.is_only_below_fermi @property def is_only_q_annihilator(self): """ Always destroy a quasi-particle? (annihilate hole or annihilate particle) >>> from sympy import Symbol >>> from sympy.physics.secondquant import F >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> F(a).is_only_q_annihilator True >>> F(i).is_only_q_annihilator False >>> F(p).is_only_q_annihilator False """ return self.is_only_above_fermi def __repr__(self): return "AnnihilateFermion(%s)"%self.state def _latex(self,printer): return "a_{%s}"%self.state.name class CreateFermion(FermionicOperator, Creator): """ Fermionic creation operator. """ op_symbol = 'f+' def _dagger_(self): return AnnihilateFermion(self.state) def apply_operator(self, state): if isinstance(state, FockStateFermionKet): element = self.state return state.up(element) elif isinstance(state, Mul): c_part, nc_part = split_commutative_parts(state) if isinstance(nc_part[0], FockStateFermionKet): element = self.state return Mul(*(c_part+[nc_part[0].up(element)]+nc_part[1:])) else: return Mul(self,state) else: return Mul(self,state) @property def is_q_creator(self): """ Can we create a quasi-particle? (create hole or create particle) If so, would that be above or below the fermi surface? >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> Fd(a).is_q_creator 1 >>> Fd(i).is_q_creator 0 >>> Fd(p).is_q_creator 1 """ if self.is_above_fermi: return 1 return 0 @property def is_q_annihilator(self): """ Can we destroy a quasi-particle? (annihilate hole or annihilate particle) If so, would that be above or below the fermi surface? >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a',above_fermi=1) >>> i = Symbol('i',below_fermi=1) >>> p = Symbol('p') >>> Fd(a).is_q_annihilator 0 >>> Fd(i).is_q_annihilator -1 >>> Fd(p).is_q_annihilator -1 """ if self.is_below_fermi: return -1 return 0 @property def is_only_q_creator(self): """ Always create a quasi-particle? (create hole or create particle) >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> Fd(a).is_only_q_creator True >>> Fd(i).is_only_q_creator False >>> Fd(p).is_only_q_creator False """ return self.is_only_above_fermi @property def is_only_q_annihilator(self): """ Always destroy a quasi-particle? (annihilate hole or annihilate particle) >>> from sympy import Symbol >>> from sympy.physics.secondquant import Fd >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> Fd(a).is_only_q_annihilator False >>> Fd(i).is_only_q_annihilator True >>> Fd(p).is_only_q_annihilator False """ return self.is_only_below_fermi def __repr__(self): return "CreateFermion(%s)"%self.state def _latex(self,printer): return "a^\\dagger_{%s}"%self.state.name Fd = CreateFermion F = AnnihilateFermion class FockState(Expr): """ Many particle Fock state with a sequence of occupation numbers. Anywhere you can have a FockState, you can also have S.Zero. All code must check for this! """ def __new__(cls, occupations): """ occupations is a list with two possible meanings: - For bosons it is a list of occupation numbers. Element i is the number of particles in state i. - For fermions it is a list of occupied orbits. Element 0 is the state that was occupied first, element i is the i'th occupied state. """ occupations = map(sympify, occupations) obj = Basic.__new__(cls, Tuple(*occupations), commutative=False) return obj def _eval_subs(self, old, new): if self == old: return new r = self.__class__([o.subs(old, new) for o in self.args[0]]) return r def __getitem__(self, i): i = int(i) return self.args[0][i] def __repr__(self): return ("FockState(%r)") % (self.args) def __str__(self): return "%s%r%s" % (self.lbracket,self._labels(),self.rbracket) def _labels(self): return self.args[0] def __len__(self): return len(self.args[0]) class BosonState(FockState): """ Many particle Fock state with a sequence of occupation numbers. occupation numbers can be any integer >= 0 """ def up(self, i): i = int(i) new_occs = list(self.args[0]) new_occs[i] = new_occs[i]+S.One return self.__class__(new_occs) def down(self, i): i = int(i) new_occs = list(self.args[0]) if new_occs[i]==S.Zero: return S.Zero else: new_occs[i] = new_occs[i]-S.One return self.__class__(new_occs) class FermionState(FockState): """ Many particle Fock state with a sequence of occupied orbits Each state can only have one particle, so we choose to store a list of occupied orbits rather than a tuple with occupation numbers (zeros and ones). states below fermi level are holes, and are represented by negative labels in the occupation list For symbolic state labels, the fermi_level caps the number of allowed hole- states """ fermi_level=0 def __new__(cls, occupations, fermi_level=0): occupations = map(sympify,occupations) if len(occupations) >1: try: (occupations,sign) = _sort_anticommuting_fermions(occupations, key=hash) except ViolationOfPauliPrinciple: return S.Zero else: sign = 0 cls.fermi_level = fermi_level if cls._count_holes(occupations) > fermi_level: return S.Zero if sign%2: return S.NegativeOne*FockState.__new__(cls,occupations) else: return FockState.__new__(cls,occupations) def up(self, i): """ Performs the action of a creation operator. If below fermi we try to remove a hole, if above fermi we try to create a particle. if general index p we return Kronecker(p,i)*self where i is a new symbol with restriction above or below. >>> from sympy import Symbol >>> from sympy.physics.secondquant import FKet >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') >>> FKet([]).up(a) FockStateFermionKet((a,)) A creator acting on vacuum below fermi vanishes >>> FKet([]).up(i) 0 """ present = i in self.args[0] if self._only_above_fermi(i): if present: return S.Zero else: return self._add_orbit(i) elif self._only_below_fermi(i): if present: return self._remove_orbit(i) else: return S.Zero else: if present: hole = Dummy("i",below_fermi=True) return KroneckerDelta(i,hole)*self._remove_orbit(i) else: particle = Dummy("a",above_fermi=True) return KroneckerDelta(i,particle)*self._add_orbit(i) def down(self, i): """ Performs the action of an annihilation operator. If below fermi we try to create a hole, if above fermi we try to remove a particle. if general index p we return Kronecker(p,i)*self where i is a new symbol with restriction above or below. >>> from sympy import Symbol >>> from sympy.physics.secondquant import FKet >>> a = Symbol('a',above_fermi=True) >>> i = Symbol('i',below_fermi=True) >>> p = Symbol('p') An annihilator acting on vacuum above fermi vanishes >>> FKet([]).down(a) 0 Also below fermi, it vanishes, unless we specify a fermi level > 0 >>> FKet([]).down(i) 0 >>> FKet([],4).down(i) FockStateFermionKet((i,)) """ present = i in self.args[0] if self._only_above_fermi(i): if present: return self._remove_orbit(i) else: return S.Zero elif self._only_below_fermi(i): if present: return S.Zero else: return self._add_orbit(i) else: if present: hole = Dummy("i",below_fermi=True) return KroneckerDelta(i,hole)*self._add_orbit(i) else: particle = Dummy("a",above_fermi=True) return KroneckerDelta(i,particle)*self._remove_orbit(i) @classmethod def _only_below_fermi(cls,i): """ Tests if given orbit is only below fermi surface. If nothing can be concluded we return a conservative False. """ if i.is_number: return i<= cls.fermi_level if i.assumptions0.get('below_fermi'): return True return False @classmethod def _only_above_fermi(cls,i): """ Tests if given orbit is only above fermi surface. If fermi level has not been set we return True. If nothing can be concluded we return a conservative False. """ if i.is_number: return i> cls.fermi_level if i.assumptions0.get('above_fermi'): return True return not cls.fermi_level def _remove_orbit(self,i): """ Removes particle/fills hole in orbit i. No input tests performed here. """ new_occs = list(self.args[0]) pos = new_occs.index(i) del new_occs[pos] if (pos)%2: return S.NegativeOne*self.__class__(new_occs,self.fermi_level) else: return self.__class__(new_occs, self.fermi_level) def _add_orbit(self,i): """ Adds particle/creates hole in orbit i. No input tests performed here. """ return self.__class__((i,)+self.args[0], self.fermi_level) @classmethod def _count_holes(cls,list): """ returns number of identified hole states in list. """ return len([ i for i in list if cls._only_below_fermi(i)]) def _negate_holes(self,list): return tuple([ -i if i<=self.fermi_level else i for i in list ]) def __repr__(self): if self.fermi_level: return "FockStateKet(%r, fermi_level=%s)"%(self.args[0],self.fermi_level) else: return "FockStateKet(%r)"%(self.args[0],) def _labels(self): return self._negate_holes(self.args[0]) class FockStateKet(FockState): lbracket = '|' rbracket = '>' class FockStateBra(FockState): lbracket = '<' rbracket = '|' def __mul__(self, other): if isinstance(other, FockStateKet): return InnerProduct(self, other) else: return Expr.__mul__(self, other) class FockStateBosonKet(BosonState,FockStateKet): def _dagger_(self): return FockStateBosonBra(*self.args) class FockStateBosonBra(BosonState,FockStateBra): def _dagger_(self): return FockStateBosonKet(*self.args) class FockStateFermionKet(FermionState,FockStateKet): def _dagger_(self): return FockStateFermionBra(*self.args) class FockStateFermionBra(FermionState,FockStateBra): def _dagger_(self): return FockStateFermionKet(*self.args) BBra = FockStateBosonBra BKet = FockStateBosonKet FBra = FockStateFermionBra FKet = FockStateFermionKet def split_commutative_parts(m): c_part = [p for p in m.args if p.is_commutative] nc_part = [p for p in m.args if not p.is_commutative] return c_part, nc_part def apply_Mul(m): """ Take a Mul instance with operators and apply them to states. This method applies all operators with integer state labels to the actual states. For symbolic state labels, nothing is done. When inner products of FockStates are encountered (like ), the are converted to instances of InnerProduct. This does not currently work on double inner products like, . If the argument is not a Mul, it is simply returned as is. """ if not isinstance(m, Mul): return m c_part, nc_part = split_commutative_parts(m) n_nc = len(nc_part) if n_nc == 0 or n_nc == 1: return m else: last = nc_part[-1] next_to_last = nc_part[-2] if isinstance(last, FockStateKet): if isinstance(next_to_last, SqOperator): if next_to_last.is_symbolic: return m else: result = next_to_last.apply_operator(last) if result == 0: return 0 else: return apply_Mul(Mul(*(c_part+nc_part[:-2]+[result]))) elif isinstance(next_to_last, Pow): if isinstance(next_to_last.base, SqOperator) and \ next_to_last.exp.is_Integer: if next_to_last.base.is_symbolic: return m else: result = last for i in range(next_to_last.exp): result = next_to_last.base.apply_operator(result) if result == 0: break if result == 0: return 0 else: return apply_Mul(Mul(*(c_part+nc_part[:-2]+[result]))) else: return m elif isinstance(next_to_last, FockStateBra): result = InnerProduct(next_to_last, last) if result == 0: return 0 else: return apply_Mul(Mul(*(c_part+nc_part[:-2]+[result]))) else: return m else: return m def apply_operators(e): """ Take a sympy expression with operators and states and apply the operators. """ e = e.expand() muls = e.atoms(Mul) subs_list = [(m,apply_Mul(m)) for m in iter(muls)] return e.subs(subs_list) class InnerProduct(Basic): """ An unevaluated inner product between a bra and ket. Currently this class just reduces things to a product of Kronecker Deltas. In the future, we could introduce abstract states like |a> and |b>, and leave the inner product unevaluated as . """ def __new__(cls, bra, ket): assert isinstance(bra, FockStateBra), 'must be a bra' assert isinstance(ket, FockStateKet), 'must be a key' r = cls.eval(bra, ket) if isinstance(r, Basic): return r obj = Basic.__new__(cls, *(bra, ket), **dict(commutative=True)) return obj @classmethod def eval(cls, bra, ket): result = S.One for i,j in zip(bra.args[0], ket.args[0]): result *= KroneckerDelta(i,j) if result == 0: break return result @property def bra(self): return self.args[0] @property def ket(self): return self.args[1] def _eval_subs(self, old, new): if self == old: return new r = self.__class__(self.bra.subs(old,new), self.ket.subs(old,new)) return r def __repr__(self): sbra = repr(self.bra) sket = repr(self.ket) return "%s|%s" % (sbra[:-1], sket[1:]) def __str__(self): return self.__repr__() def matrix_rep(op, basis): """ Find the representation of an operator in a basis. """ a = zeros((len(basis), len(basis))) for i in range(len(basis)): for j in range(len(basis)): a[i,j] = apply_operators(Dagger(basis[i])*op*basis[j]) return a class BosonicBasis(object): """ Base class for a basis set of bosonic Fock states. """ pass class VarBosonicBasis(object): """ A single state, variable particle number basis set. """ def __init__(self, n_max): self.n_max = n_max self._build_states() def _build_states(self): self.basis = [] for i in range(self.n_max): self.basis.append(FockStateBosonKet([i])) self.n_basis = len(self.basis) def index(self, state): return self.basis.index(state) def state(self, i): return self.basis[i] def __getitem__(self, i): return self.state(i) def __len__(self): return len(self.basis) def __repr__(self): return repr(self.basis) class FixedBosonicBasis(BosonicBasis): """ Fixed particle number basis set. """ def __init__(self, n_particles, n_levels): self.n_particles = n_particles self.n_levels = n_levels self._build_particle_locations() self._build_states() def _build_particle_locations(self): tup = ["i%i" % i for i in range(self.n_particles)] first_loop = "for i0 in range(%i)" % self.n_levels other_loops = '' for cur, prev in zip(tup[1:], tup): temp = "for %s in range(%s + 1) " % (cur, prev) other_loops = other_loops + temp tup_string = "(%s)" % ", ".join(tup) list_comp = "[%s %s %s]" % (tup_string, first_loop, other_loops) result = eval(list_comp) if self.n_particles == 1: result = [(item,) for item in result] self.particle_locations = result def _build_states(self): self.basis = [] for tuple_of_indices in self.particle_locations: occ_numbers = self.n_levels*[0] for level in tuple_of_indices: occ_numbers[level] += 1 self.basis.append(FockStateBosonKet(occ_numbers)) self.n_basis = len(self.basis) def index(self, state): return self.basis.index(state) def state(self, i): return self.basis[i] def __getitem__(self, i): return self.state(i) def __len__(self): return len(self.basis) def __repr__(self): return repr(self.basis) # def move(e, i, d): # """ # Takes the expression "e" and moves the operator at the position i by "d". # """ # if e.is_Mul: # if d == 1: # # e = a*b*c*d # a = Mul(*e.args[:i]) # b = e.args[i] # c = e.args[i+1] # d = Mul(*e.args[i+2:]) # if isinstance(b, Dagger) and not isinstance(c, Dagger): # i, j = b.args[0].args[0], c.args[0] # return a*c*b*d-a*KroneckerDelta(i, j)*d # elif not isinstance(b, Dagger) and isinstance(c, Dagger): # i, j = b.args[0], c.args[0].args[0] # return a*c*b*d-a*KroneckerDelta(i, j)*d # else: # return a*c*b*d # elif d == -1: # # e = a*b*c*d # a = Mul(*e.args[:i-1]) # b = e.args[i-1] # c = e.args[i] # d = Mul(*e.args[i+1:]) # if isinstance(b, Dagger) and not isinstance(c, Dagger): # i, j = b.args[0].args[0], c.args[0] # return a*c*b*d-a*KroneckerDelta(i, j)*d # elif not isinstance(b, Dagger) and isinstance(c, Dagger): # i, j = b.args[0], c.args[0].args[0] # return a*c*b*d-a*KroneckerDelta(i, j)*d # else: # return a*c*b*d # else: # if d > 1: # while d >= 1: # e = move(e, i, 1) # d -= 1 # i += 1 # return e # elif d < -1: # while d <= -1: # e = move(e, i, -1) # d += 1 # i -= 1 # return e # elif isinstance(e, Add): # a, b = e.as_two_terms() # return move(a, i, d) + move(b, i, d) # raise NotImplementedError() class Commutator(Function): """ The Commutator: [A, B] = A*B - B*A The arguments are ordered according to .__cmp__() >>> from sympy import symbols >>> from sympy.physics.secondquant import Commutator >>> A, B = symbols('A,B', commutative=False) >>> Commutator(B, A) -Commutator(A, B) Evaluate the commutator with .doit() >>> comm = Commutator(A,B); comm Commutator(A, B) >>> comm.doit() A*B - B*A For two second quantization operators the commutator is evaluated immediately: >>> from sympy.physics.secondquant import Fd, F >>> a = symbols('a',above_fermi=True) >>> i = symbols('i',below_fermi=True) >>> p,q = symbols('p,q') >>> Commutator(Fd(a),Fd(i)) 2*NO(CreateFermion(a)*CreateFermion(i)) But for more complicated expressions, the evaluation is triggered by a call to .doit() >>> comm = Commutator(Fd(p)*Fd(q),F(i)); comm Commutator(CreateFermion(p)*CreateFermion(q), AnnihilateFermion(i)) >>> comm.doit(wicks=True) -KroneckerDelta(i, p)*CreateFermion(q) + KroneckerDelta(i, q)*CreateFermion(p) """ is_commutative = False nargs = 2 @classmethod def eval(cls, a,b): """ The Commutator [A,B] is on canonical form if A < B """ if not (a and b): return S.Zero if a == b: return S.Zero if a.is_commutative or b.is_commutative: return S.Zero # # [A+B,C] -> [A,C] + [B,C] # a = a.expand() if isinstance(a,Add): return Add(*[cls(term,b) for term in a.args]) b = b.expand() if isinstance(b,Add): return Add(*[cls(a,term) for term in b.args]) # # [xA,yB] -> xy*[A,B] # c_part = [] nc_part = [] nc_part2 = [] if isinstance(a,Mul): c_part,nc_part = split_commutative_parts(a) if isinstance(b,Mul): c_part2,nc_part2 = split_commutative_parts(b) c_part.extend(c_part2) if c_part: a = nc_part or [a] b = nc_part2 or [b] return Mul(*c_part)*cls(Mul(*a),Mul(*b)) # # single second quantization operators # if isinstance(a, BosonicOperator) and isinstance(b, BosonicOperator): if isinstance(b,CreateBoson) and isinstance(a,AnnihilateBoson): return KroneckerDelta(a.state,b.state) if isinstance(a,CreateBoson) and isinstance(b,AnnihilateBoson): return S.NegativeOne*KroneckerDelta(a.state,b.state) else: return S.Zero if isinstance(a, FermionicOperator) and isinstance(b, FermionicOperator): return wicks(a*b)- wicks(b*a) # # Canonical ordering of arguments # if a > b: return S.NegativeOne*cls(b, a) def doit(self,**hints): a = self.args[0] b = self.args[1] if hints.get("wicks"): a = a.doit(**hints) b = b.doit(**hints) try: return wicks(a*b) - wicks(b*a) except ContractionAppliesOnlyToFermions: pass except WicksTheoremDoesNotApply: pass return (a*b - b*a).doit(**hints) def __repr__(self): return "Commutator(%s,%s)" %(self.args[0],self.args[1]) def __str__(self): return "[%s,%s]" %(self.args[0],self.args[1]) def _latex(self,printer): return "\\left[%s,%s\\right]"%tuple([ printer._print(arg) for arg in self.args]) class NO(Expr): """ This Object is used to represent normal ordering brackets. i.e. {abcd} sometimes written :abcd: Applying the function NO(arg) to an argument means that all operators in the argument will be assumed to anticommute, and have vanishing contractions. This allows an immediate reordering to canonical form upon object creation. >>> from sympy import symbols >>> from sympy.physics.secondquant import NO, F, Fd >>> p,q = symbols('p,q') >>> NO(Fd(p)*F(q)) NO(CreateFermion(p)*AnnihilateFermion(q)) >>> NO(F(q)*Fd(p)) -NO(CreateFermion(p)*AnnihilateFermion(q)) Note: If you want to generate a normal ordered equivalent of an expression, you should use the function wicks(). This class only indicates that all operators inside the brackets anticommute, and have vanishing contractions. Nothing more, nothing less. """ nargs = 1 is_commutative = False def __new__(cls,arg): """ Use anticommutation to get canonical form of operators. Employ associativity of normal ordered product: {ab{cd}} = {abcd} but note that {ab}{cd} /= {abcd} We also employ distributivity: {ab + cd} = {ab} + {cd} Canonical form also implies expand() {ab(c+d)} = {abc} + {abd} """ # {ab + cd} = {ab} + {cd} arg = sympify(arg) arg = arg.expand() if arg.is_Add: return Add(*[ cls(term) for term in arg.args]) if arg.is_Mul: # take coefficient outside of normal ordering brackets c_part, seq = split_commutative_parts(arg) if c_part: coeff = Mul(*c_part) if not seq: return coeff else: coeff = S.One # {ab{cd}} = {abcd} newseq = [] foundit = False for fac in seq: if isinstance(fac,NO): newseq.extend(fac.args) foundit = True else: newseq.append(fac) if foundit: return coeff*cls(Mul(*newseq)) # We assume that the user don't mix B and F operators if isinstance(seq[0], BosonicOperator): raise NotImplementedError try: newseq,sign = _sort_anticommuting_fermions(seq) except ViolationOfPauliPrinciple: return S.Zero if sign%2: return (S.NegativeOne*coeff)*cls(Mul(*newseq)) elif sign: return coeff*cls(Mul(*newseq)) else: pass #since sign==0, no permutations was necessary # if we couldn't do anything with Mul object, we just # mark it as normal ordered if coeff != S.One: return coeff*cls(Mul(*newseq)) return Expr.__new__(cls, Mul(*newseq)) if isinstance(arg,NO): return arg # if object was not Mul or Add, normal ordering does not apply return arg @property def has_q_creators(self): """ Returns yes or no, fast Also, in case of yes, we indicate whether leftmost operator is a quasi creator above or below fermi. >>> from sympy import symbols >>> from sympy.physics.secondquant import NO, F, Fd >>> a = symbols('a',above_fermi=True) >>> i = symbols('i',below_fermi=True) >>> NO(Fd(a)*Fd(i)).has_q_creators 1 >>> NO(F(i)*F(a)).has_q_creators -1 >>> NO(Fd(i)*F(a)).has_q_creators #doctest: +SKIP 0 """ return self.args[0].args[0].is_q_creator @property def has_q_annihilators(self): """ Returns yes or no, fast Also, in case of yes, we indicate whether rightmost operator is an annihilator above or below fermi. >>> from sympy import symbols >>> from sympy.physics.secondquant import NO, F, Fd >>> a = symbols('a',above_fermi=True) >>> i = symbols('i',below_fermi=True) >>> NO(Fd(a)*Fd(i)).has_q_annihilators -1 >>> NO(F(i)*F(a)).has_q_annihilators 1 >>> NO(Fd(a)*F(i)).has_q_annihilators #doctest: +SKIP 0 """ return self.args[0].args[-1].is_q_annihilator def doit(self, **kw_args): if kw_args.get("remove_brackets", True): return self._remove_brackets() else: return self.__new__(type(self),self.args[0].doit(**kw_args)) def _remove_brackets(self): """ Returns the sorted string without normal order brackets. The returned string have the property that no nonzero contractions exist. """ # check if any creator is also an annihilator subslist=[] for i in self.iter_q_creators(): if self[i].is_q_annihilator: assume = self[i].state.assumptions0 # only operators with a dummy index can be split in two terms if isinstance(self[i].state, Dummy): # create indices with fermi restriction assume.pop("above_fermi", None) assume["below_fermi"]=True below = Dummy('i',**assume) assume.pop("below_fermi", None) assume["above_fermi"]=True above = Dummy('a',**assume) cls = type(self[i]) split = ( self[i].__new__(cls,below) * KroneckerDelta(below,self[i].state) + self[i].__new__(cls,above) * KroneckerDelta(above,self[i].state) ) subslist.append((self[i],split)) else: raise SubstitutionOfAmbigousOperatorFailed(self[i]) if subslist: result = NO(self.subs(subslist)) if isinstance(result, Add): return Add(*[term.doit() for term in result.args]) else: return self.args[0] def _expand_operators(self): """ Returns a sum of NO objects that contain no ambiguous q-operators. If an index q has range both above and below fermi, the operator F(q) is ambiguous in the sense that it can be both a q-creator and a q-annihilator. If q is dummy, it is assumed to be a summation variable and this method rewrites it into a sum of NO terms with unambiguous operators: {Fd(p)*F(q)} = {Fd(a)*F(b)} + {Fd(a)*F(i)} + {Fd(j)*F(b)} -{F(i)*Fd(j)} where a,b are above and i,j are below fermi level. """ return NO(self._remove_brackets) def _eval_subs(self,old,new): if self == old: return new ops = self.args[0].args for i in range(len(ops)): if ops[i] == old: l1 = ops[:i]+(new,)+ops[i+1:] return self.__class__(Mul(*l1)) return Expr._eval_subs(self,old,new) def __getitem__(self,i): if isinstance(i,slice): indices = i.indices(len(self)) return [self.args[0].args[i] for i in range(*indices)] else: return self.args[0].args[i] def __len__(self): return len(self.args[0].args) def iter_q_annihilators(self): """ Iterates over the annihilation operators. >>> from sympy import symbols, Dummy >>> i,j,k,l = symbols('i j k l', below_fermi=True) >>> p,q,r,s = symbols('p q r s', cls=Dummy) >>> a,b,c,d = symbols('a b c d', above_fermi=True) >>> from sympy.physics.secondquant import NO, F, Fd >>> no = NO(Fd(a)*F(i)*Fd(j)*F(b)) >>> no.iter_q_creators() >>> list(no.iter_q_creators()) [0, 1] >>> list(no.iter_q_annihilators()) [3, 2] """ ops = self.args[0].args iter = xrange(len(ops)-1, -1, -1) for i in iter: if ops[i].is_q_annihilator: yield i else: break def iter_q_creators(self): """ Iterates over the creation operators. >>> from sympy import symbols, Dummy >>> i,j,k,l = symbols('i j k l',below_fermi=True) >>> p,q,r,s = symbols('p q r s', cls=Dummy) >>> a,b,c,d = symbols('a b c d',above_fermi=True) >>> from sympy.physics.secondquant import NO, F, Fd >>> no = NO(Fd(a)*F(i)*Fd(j)*F(b)) >>> no.iter_q_creators() >>> list(no.iter_q_creators()) [0, 1] >>> list(no.iter_q_annihilators()) [3, 2] """ ops = self.args[0].args iter = xrange(0, len(ops)) for i in iter: if ops[i].is_q_creator: yield i else: break def get_subNO(self, i): """ Returns a NO() without FermionicOperator at index i >>> from sympy import symbols >>> from sympy.physics.secondquant import F, NO >>> p,q,r = symbols('p,q,r') >>> NO(F(p)*F(q)*F(r)).get_subNO(1) # doctest: +SKIP NO(AnnihilateFermion(p)*AnnihilateFermion(r)) """ arg0 = self.args[0] # it's a Mul by definition of how it's created mul = Mul._new_rawargs(arg0, Mul._new_rawargs(arg0, arg0.args[:i]), Mul._new_rawargs(arg0, arg0.args[i + 1:])) return NO(mul) def _latex(self,printer): return "\\left\\{%s\\right\\}"%printer._print(self.args[0]) def __repr__(self): return "NO(%s)"%self.args[0] def __str__(self): return ":%s:" % self.args[0] # @cacheit def contraction(a,b): """ Calculates contraction of Fermionic operators ab >>> from sympy import symbols >>> from sympy.physics.secondquant import F, Fd, contraction >>> p,q = symbols('p,q') >>> a,b = symbols('a,b', above_fermi=True) >>> i,j = symbols('i,j', below_fermi=True) A contraction is non-zero only if a quasi-creator is to the right of a quasi-annihilator: >>> contraction(F(a),Fd(b)) KroneckerDelta(a, b) >>> contraction(Fd(i),F(j)) KroneckerDelta(i, j) For general indices a non-zero result restricts the indices to below/above the fermi surface: >>> contraction(Fd(p),F(q)) KroneckerDelta(p, q)*KroneckerDelta(q, _i) >>> contraction(F(p),Fd(q)) KroneckerDelta(p, q)*KroneckerDelta(q, _a) Two creators or two annihilators always vanishes: >>> contraction(F(p),F(q)) 0 >>> contraction(Fd(p),Fd(q)) 0 """ if isinstance(b,FermionicOperator) and isinstance(a,FermionicOperator): if isinstance(a,AnnihilateFermion) and isinstance(b,CreateFermion): if b.state.assumptions0.get("below_fermi"): return S.Zero if a.state.assumptions0.get("below_fermi"): return S.Zero if b.state.assumptions0.get("above_fermi"): return KroneckerDelta(a.state,b.state) if a.state.assumptions0.get("above_fermi"): return KroneckerDelta(a.state,b.state) return (KroneckerDelta(a.state,b.state)* KroneckerDelta(b.state,Dummy('a',above_fermi=True))) if isinstance(b,AnnihilateFermion) and isinstance(a,CreateFermion): if b.state.assumptions0.get("above_fermi"): return S.Zero if a.state.assumptions0.get("above_fermi"): return S.Zero if b.state.assumptions0.get("below_fermi"): return KroneckerDelta(a.state,b.state) if a.state.assumptions0.get("below_fermi"): return KroneckerDelta(a.state,b.state) return (KroneckerDelta(a.state,b.state)* KroneckerDelta(b.state,Dummy('i',below_fermi=True))) # vanish if 2xAnnihilator or 2xCreator return S.Zero else: #not fermion operators t = ( isinstance(i,FermionicOperator) for i in (a,b) ) raise ContractionAppliesOnlyToFermions(*t) def sqkey(sq_operator): """Generates key for canonical sorting of SQ operators""" return sq_operator._sortkey() def _sort_anticommuting_fermions(string1, key=sqkey): """Sort fermionic operators to canonical order, assuming all pairs anticommute. Uses a bidirectional bubble sort. Items in string1 are not referenced so in principle they may be any comparable objects. The sorting depends on the operators '>' and '=='. If the Pauli principle is violated, an exception is raised. returns a tuple (sorted_str, sign) sorted_str -- list containing the sorted operators sign -- int telling how many times the sign should be changed (if sign==0 the string was already sorted) """ verified = False sign = 0 rng = range(len(string1)-1) rev = range(len(string1)-3,-1,-1) keys = list(map(key, string1)) key_val = dict(zip(keys, string1)) while not verified: verified = True for i in rng: left = keys[i] right = keys[i+1] if left == right: raise ViolationOfPauliPrinciple([left,right]) if left > right: verified = False keys[i:i+2] = [right, left] sign = sign+1 if verified: break for i in rev: left = keys[i] right = keys[i+1] if left == right: raise ViolationOfPauliPrinciple([left,right]) if left > right: verified = False keys[i:i+2] = [right, left] sign = sign+1 string1 = [ key_val[k] for k in keys ] return (string1,sign) def evaluate_deltas(e): """ We evaluate KroneckerDelta symbols in the expression assuming Einstein summation. If one index is repeated it is summed over and in effect substituted with the other one. If both indices are repeated we substitute according to what is the preferred index. this is determined by KroneckerDelta.preferred_index and KroneckerDelta.killable_index. In case there are no possible substitutions or if a substitution would imply a loss of information, nothing is done. In case an index appears in more than one KroneckerDelta, the resulting substitution depends on the order of the factors. Since the ordering is platform dependent, the literal expression resulting from this function may be hard to predict. Examples: ========= We assume that >>> from sympy import symbols, Function, Dummy >>> from sympy.physics.secondquant import evaluate_deltas, KroneckerDelta >>> i,j = symbols('i j', below_fermi=True, cls=Dummy) >>> a,b = symbols('a b', above_fermi=True, cls=Dummy) >>> p,q = symbols('p q', cls=Dummy) >>> f = Function('f') >>> t = Function('t') The order of preference for these indices according to KroneckerDelta is (a,b,i,j,p,q). Trivial cases: >>> evaluate_deltas(KroneckerDelta(i,j)*f(i)) # d_ij f(i) -> f(j) f(_j) >>> evaluate_deltas(KroneckerDelta(i,j)*f(j)) # d_ij f(j) -> f(i) f(_i) >>> evaluate_deltas(KroneckerDelta(i,p)*f(p)) # d_ip f(p) -> f(i) f(_i) >>> evaluate_deltas(KroneckerDelta(q,p)*f(p)) # d_qp f(p) -> f(q) f(_q) >>> evaluate_deltas(KroneckerDelta(q,p)*f(q)) # d_qp f(q) -> f(p) f(_p) More interesting cases: >>> evaluate_deltas(KroneckerDelta(i,p)*t(a,i)*f(p,q)) f(_i, _q)*t(_a, _i) >>> evaluate_deltas(KroneckerDelta(a,p)*t(a,i)*f(p,q)) f(_a, _q)*t(_a, _i) >>> evaluate_deltas(KroneckerDelta(p,q)*f(p,q)) f(_p, _p) Finally, here are some cases where nothing is done, because that would imply a loss of information: >>> evaluate_deltas(KroneckerDelta(i,p)*f(q)) f(_q)*KroneckerDelta(_i, _p) >>> evaluate_deltas(KroneckerDelta(i,p)*f(i)) f(_i)*KroneckerDelta(_i, _p) """ # We treat Deltas only in mul objects # for general function objects we don't evaluate KroneckerDeltas in arguments, # but here we hard code exceptions to this rule accepted_functions = ( Add, ) if isinstance(e, accepted_functions): return e.func(*[evaluate_deltas(arg) for arg in e.args]) elif isinstance(e,Mul): # find all occurences of delta function and count each index present in # expression. deltas = [] indices = {} for i in e.args: for s in i.atoms(): if s in indices: indices[s] += 1 else: indices[s] = 0 # geek counting simplifies logic below if isinstance(i, KroneckerDelta): deltas.append(i) for d in deltas: # If we do something, and there are more deltas, we should recurse # to treat the resulting expression properly if indices[d.killable_index]: e = e.subs(d.killable_index,d.preferred_index) if len(deltas)>1: return evaluate_deltas(e) elif indices[d.preferred_index] and d.indices_contain_equal_information: e = e.subs(d.preferred_index,d.killable_index) if len(deltas)>1: return evaluate_deltas(e) else: pass return e # nothing to do, maybe we hit a Symbol or a number else: return e def substitute_dummies(expr, new_indices=False, pretty_indices={}): """ Collect terms by substitution of dummy variables. This routine allows simplification of Add expressions containing terms which differ only due to dummy variables. The idea is to substitute all dummy variables consistently depending on the structure of the term. For each term, we obtain a sequence of all dummy variables, where the order is determined by the index range, what factors the index belongs to and its position in each factor. See _get_ordered_dummies() for more inforation about the sorting of dummies. The index sequence is then substituted consistently in each term. Examples -------- >>> from sympy import symbols, Function, Dummy >>> from sympy.physics.secondquant import substitute_dummies >>> a,b,c,d = symbols('a b c d', above_fermi=True, cls=Dummy) >>> i,j = symbols('i j', below_fermi=True, cls=Dummy) >>> f = Function('f') >>> expr = f(a,b) + f(c,d); expr f(_a, _b) + f(_c, _d) Since a, b, c and d are equivalent summation indices, the expression can be simplified to a single term (for which the dummy indices are still summed over) >>> substitute_dummies(expr) 2*f(_a, _b) Controlling output ------------------ By default the dummy symbols that are already present in the expression will be reused in a different permuation. However, if new_indices=True, new dummies will be generated and inserted. The keyword 'pretty_indices' can be used to control this generation of new symbols. By default the new dummies will be generated on the form i_1, i_2, a_1, etc. If you supply a dictionary with key:value pairs in the form: { index_group: string_of_letters } The letters will be used as labels for the new dummy symbols. The index_groups must be one of 'above', 'below' or 'general'. >>> expr = f(a,b,i,j) >>> my_dummies = { 'above':'st','below':'uv' } >>> substitute_dummies(expr, new_indices=True, pretty_indices=my_dummies) f(_s, _t, _u, _v) If we run out of letters, or if there is no keyword for some index_group the default dummy generator will be used as a fallback: >>> p,q = symbols('p q', cls=Dummy) # general indices >>> expr = f(p,q) >>> substitute_dummies(expr, new_indices=True, pretty_indices=my_dummies) f(_p_0, _p_1) """ # setup the replacing dummies if new_indices: letters_above = pretty_indices.get('above',"") letters_below = pretty_indices.get('below',"") letters_general= pretty_indices.get('general',"") len_above = len(letters_above) len_below = len(letters_below) len_general= len(letters_general) def _i(number): try: return letters_below[number] except IndexError: return 'i_'+str(number-len_below) def _a(number): try: return letters_above[number] except IndexError: return 'a_'+str(number-len_above) def _p(number): try: return letters_general[number] except IndexError: return 'p_'+str(number-len_general) aboves = [] belows = [] generals = [] dummies = expr.atoms(Dummy) if not new_indices: dummies = sorted(dummies) # generate lists with the dummies we will insert a = i = p = 0 for d in dummies: assum = d.assumptions0 if assum.get("above_fermi"): if new_indices: sym = _a(a); a +=1 l1 = aboves elif assum.get("below_fermi"): if new_indices: sym = _i(i); i +=1 l1 = belows else: if new_indices: sym = _p(p); p +=1 l1 = generals if new_indices: l1.append(Dummy(sym, **assum)) else: l1.append(d) expr = expr.expand() terms = Add.make_args(expr) new_terms = [] for term in terms: i = iter(belows) a = iter(aboves) p = iter(generals) ordered = _get_ordered_dummies(term) subsdict = {} for d in ordered: if d.assumptions0.get('below_fermi'): subsdict[d] = i.next() elif d.assumptions0.get('above_fermi'): subsdict[d] = a.next() else: subsdict[d] = p.next() subslist = [] final_subs = [] for k, v in subsdict.iteritems(): if k == v: continue if v in subsdict: # We check if the sequence of substitutions end quickly. In # that case, we can avoid temporary symbols if we ensure the # correct substitution order. if subsdict[v] in subsdict: # (x, y) -> (y, x), we need a temporary variable x = Dummy('x') subslist.append((k, x)) final_subs.append((x, v)) else: # (x, y) -> (y, a), x->y must be done last # but before temporary variables are resolved final_subs.insert(0, (k, v)) else: subslist.append((k, v)) subslist.extend(final_subs) new_terms.append(term.subs(subslist)) return Add(*new_terms) class KeyPrinter(StrPrinter): """Printer for which only equal objects are equal in print""" def _print_Dummy(self, expr): return "(%s_%i)" % (expr.name, expr.dummy_index) def __kprint(expr): p = KeyPrinter() return p.doprint(expr) def _get_ordered_dummies(mul, verbose = False): """Returns all dummies in the mul sorted in canonical order The purpose of the canonical ordering is that dummies can be substituted consistently accross terms with the result that equivalent terms can be simplified. It is not possible to determine if two terms are equivalent based solely on the dummy order. However, a consistent substitution guided by the ordered dummies should lead to trivially (non-)equivalent terms, thereby revealing the equivalence. This also means that if two terms have identical sequences of dummies, the (non-)equivalence should already be apparent. Strategy -------- The canoncial order is given by an arbitrary sorting rule. A sort key is determined for each dummy as a tuple that depends on all factors where the index is present. The dummies are thereby sorted according to the contraction structure of the term, instead of sorting based solely on the dummy symbol itself. After all dummies in the term has been assigned a key, we check for identical keys, i.e. unorderable dummies. If any are found, we call a specialized method, _determine_ambiguous(), that will determine a unique order based on recursive calls to _get_ordered_dummies(). Key description --------------- A high level description of the sort key: 1. Range of the dummy index 2. Relation to external (non-dummy) indices 3. Position of the index in the first factor 4. Position of the index in the second factor The sort key is a tuple with the following components: 1. A single character indicating the range of the dummy (above, below or general.) 2. A list of strings with fully masked string representations of all factors where the dummy is present. By masked, we mean that dummies are represented by a symbol to indicate either below fermi, above or general. No other information is displayed about the dummies at this point. The list is sorted stringwise. 3. An integer number indicating the position of the index, in the first factor as sorted in 2. 4. An integer number indicating the position of the index, in the second factor as sorted in 2. If a factor is either of type AntiSymmetricTensor or SqOperator, the index position in items 3 and 4 is indicated as 'upper' or 'lower' only. (Creation operators are considered upper and annihilation operators lower.) If the masked factors are identical, the two factors cannot be ordered unambiguously in item 2. In this case, items 3, 4 are left out. If several indices are contracted between the unorderable factors, it will be handled by _determine_ambiguous() """ # setup dicts to avoid repeated calculations in key() args = Mul.make_args(mul) fac_dum = dict([ (fac, fac.atoms(Dummy)) for fac in args] ) fac_repr = dict([ (fac, __kprint(fac)) for fac in args] ) all_dums = list(reduce( lambda x, y: x | y, fac_dum.values(), set())) mask = {} for d in all_dums: if d.assumptions0.get('below_fermi'): mask[d] = '0' elif d.assumptions0.get('above_fermi'): mask[d] = '1' else: mask[d] = '2' dum_repr = dict([ (d, __kprint(d)) for d in all_dums ]) def key(d): dumstruct = [ fac for fac in fac_dum if d in fac_dum[fac] ] other_dums = reduce(lambda x, y: x | y, [ fac_dum[fac] for fac in dumstruct ]) fac = dumstruct[-1] if other_dums is fac_dum[fac]: other_dums = fac_dum[fac].copy() other_dums.remove(d) masked_facs = [ fac_repr[fac] for fac in dumstruct ] for d2 in other_dums: masked_facs = [ fac.replace(dum_repr[d2], mask[d2]) for fac in masked_facs ] all_masked = [ fac.replace(dum_repr[d], mask[d]) for fac in masked_facs ] masked_facs = dict(zip(dumstruct, masked_facs)) # dummies for which the ordering cannot be determined if len(set(all_masked)) < len(all_masked): all_masked.sort() return mask[d], tuple(all_masked) # positions are ambiguous # sort factors according to fully masked strings keydict = dict(zip(dumstruct, all_masked)) dumstruct.sort(key=lambda x: keydict[x]) all_masked.sort() pos_val = [] for fac in dumstruct: if isinstance(fac,AntiSymmetricTensor): if d in fac.upper: pos_val.append('u') if d in fac.lower: pos_val.append('l') elif isinstance(fac, Creator): pos_val.append('u') elif isinstance(fac, Annihilator): pos_val.append('l') elif isinstance(fac, NO): ops = [ op for op in fac if op.has(d) ] for op in ops: if isinstance(op, Creator): pos_val.append('u') else: pos_val.append('l') else: # fallback to position in string representation facpos = -1 while 1: facpos = masked_facs[fac].find(dum_repr[d], facpos+1) if facpos == -1: break pos_val.append(facpos) return (mask[d], tuple(all_masked), pos_val[0], pos_val[-1]) dumkey = dict(zip(all_dums, map(key, all_dums))) result = sorted(all_dums, key=lambda x: dumkey[x]) if len(set(dumkey.itervalues())) < len(dumkey): # We have ambiguities unordered = {} for d, k in dumkey.iteritems(): if k in unordered: unordered[k].add(d) else: unordered[k] = set([d]) for k in [ k for k in unordered if len(unordered[k]) < 2 ]: del unordered[k] unordered = [ unordered[k] for k in sorted(unordered) ] result = _determine_ambiguous(mul, result, unordered) return result def _determine_ambiguous(term, ordered, ambiguous_groups): # We encountered a term for which the dummy substitution is ambiguous. # This happens for terms with 2 or more contractions between factors that # cannot be uniquely ordered independent of summation indices. For # example: # # Sum(p, q) v^{p, .}_{q, .}v^{q, .}_{p, .} # # Assuming that the indices represented by . are dummies with the # same range, the factors cannot be ordered, and there is no # way to determine a consistent ordering of p and q. # # The strategy employed here, is to relabel all unambiguous dummies with # non-dummy symbols and call _get_ordered_dummies again. This procedure is # applied to the entire term so there is a possibility that # _determine_ambiguous() is called again from a deeper recursion level. # break recursion if there are no ordered dummies all_ambiguous = set() for dummies in ambiguous_groups: all_ambiguous |= dummies all_ordered = set(ordered) - all_ambiguous if not all_ordered: # FIXME: If we arrive here, there are no ordered dummies. A method to # handle this needs to be implemented. In order to return something # useful nevertheless, we choose arbitrarily the first dummy and # determine the rest from this one. This method is dependent on the # actual dummy labels which violates an assumption for the canonization # procedure. A better implementation is needed. group = [ d for d in ordered if d in ambiguous_groups[0] ] d = group[0] all_ordered.add(d) ambiguous_groups[0].remove(d) stored_counter = __symbol_factory.counter subslist = [] for d in [ d for d in ordered if d in all_ordered ]: nondum = __symbol_factory.next() subslist.append((d, nondum)) newterm = term.subs(subslist) neworder = _get_ordered_dummies(newterm) __symbol_factory.set_counter(stored_counter) # update ordered list with new information for group in ambiguous_groups: ordered_group = [ d for d in neworder if d in group ] ordered_group.reverse() result = [] for d in ordered: if d in group: result.append(ordered_group.pop()) else: result.append(d) ordered = result return ordered class _SymbolFactory(object): def __init__(self, label): self._counter = 0 self._label = label def set_counter(self, value): self._counter = value @property def counter(self): return self._counter def next(self): s = Symbol("%s%i" % (self._label, self._counter)) self._counter += 1 return s __symbol_factory = _SymbolFactory('_]"]_') # most certainly a unique label @cacheit def _get_contractions(string1, keep_only_fully_contracted=False): """ Uses recursion to find all contractions. -- Internal helper function -- Will find nonzero contractions in string1 between indices given in leftrange and rightrange. returns Add-object with contracted terms. """ # Should we store current level of contraction? if keep_only_fully_contracted and string1: result = [] else: result = [NO(Mul(*string1))] for i in range(len(string1)-1): for j in range(i+1,len(string1)): c = contraction(string1[i],string1[j]) if c: # print "found contraction",c sign = (j-i+1) %2 if sign: coeff = S.NegativeOne*c else: coeff = c # # Call next level of recursion # ============================ # # We now need to find more contractions among operators # # oplist = string1[:i]+ string1[i+1:j] + string1[j+1:] # # To prevent overcounting, we don't allow contractions # we have already encountered. i.e. contractions between # string1[:i] <---> string1[i+1:j] # and string1[:i] <---> string1[j+1:]. # # This leaves the case: oplist = string1[i+1:j] + string1[j+1:] if oplist: result.append(coeff*NO( Mul(*string1[:i])*_get_contractions( oplist, keep_only_fully_contracted=keep_only_fully_contracted))) else: result.append(coeff*NO( Mul(*string1[:i]))) if keep_only_fully_contracted: break # next iteration over i leaves leftmost operator string1[0] uncontracted return Add(*result) # @cacheit def wicks(e, **kw_args): """ Returns the normal ordered equivalent of an expression using Wicks Theorem. >>> from sympy import symbols, Function, Dummy >>> from sympy.physics.secondquant import wicks, F, Fd, NO >>> p,q,r = symbols('p,q,r') >>> wicks(Fd(p)*F(q)) # doctest: +SKIP KroneckerDelta(p, q)*KroneckerDelta(q, _i) + NO(CreateFermion(p)*AnnihilateFermion(q)) By default, the expression is expanded: >>> wicks(F(p)*(F(q)+F(r))) # doctest: +SKIP NO(AnnihilateFermion(p)*AnnihilateFermion(q)) + NO(AnnihilateFermion(p)*AnnihilateFermion(r)) With the keyword 'keep_only_fully_contracted=True', only fully contracted terms are returned. By request, the result can be simplified in the following order: -- KroneckerDelta functions are evaluated -- Dummy variables are substituted consistently across terms >>> p,q,r = symbols('p q r', cls=Dummy) >>> wicks(Fd(p)*(F(q)+F(r)), keep_only_fully_contracted=True) # doctest: +SKIP KroneckerDelta(_i, _q)*KroneckerDelta(_p, _q) + KroneckerDelta(_i, _r)*KroneckerDelta(_p, _r) """ if not e: return S.Zero opts={ 'simplify_kronecker_deltas':False, 'expand':True, 'simplify_dummies':False, 'keep_only_fully_contracted':False } opts.update(kw_args) # check if we are already normally ordered if isinstance(e,NO): if opts['keep_only_fully_contracted']: return S.Zero else: return e elif isinstance(e,FermionicOperator): if opts['keep_only_fully_contracted']: return S.Zero else: return e # break up any NO-objects, and evaluate commutators e = e.doit(wicks=True) # make sure we have only one term to consider e = e.expand() if isinstance(e, Add): if opts['simplify_dummies']: return substitute_dummies(Add(*[ wicks(term, **kw_args) for term in e.args])) else: return Add(*[ wicks(term, **kw_args) for term in e.args]) # For Mul-objects we can actually do something if isinstance(e, Mul): # we dont want to mess around with commuting part of Mul # so we factorize it out before starting recursion c_part = [] string1 = [] for factor in e.args: if factor.is_commutative: c_part.append(factor) else: string1.append(factor) n = len(string1) # catch trivial cases if n == 0: result= e elif n==1: if opts['keep_only_fully_contracted']: return S.Zero else: result = e else: # non-trivial if isinstance(string1[0], BosonicOperator): raise NotImplementedError string1 = tuple(string1) # recursion over higher order contractions result = _get_contractions(string1, keep_only_fully_contracted=opts['keep_only_fully_contracted'] ) result = Mul(*c_part)*result if opts['expand']: result = result.expand() if opts['simplify_kronecker_deltas']: result = evaluate_deltas(result) return result # there was nothing to do return e class PermutationOperator(Expr): """ Represents the index permutation operator P(ij) P(ij)*f(i)*g(j) = f(i)*g(j) - f(j)*g(i) """ is_commutative = True def __new__(cls, i,j): i,j = map(sympify,(i,j)) if (i>j): obj = Basic.__new__(cls,j,i) else: obj = Basic.__new__(cls,i,j) return obj def get_permuted(self,expr): """ Returns -expr with permuted indices. >>> from sympy import symbols, Function >>> from sympy.physics.secondquant import PermutationOperator >>> p,q = symbols('p,q') >>> f = Function('f') >>> PermutationOperator(p,q).get_permuted(f(p,q)) -f(q, p) """ i = self.args[0] j = self.args[1] if expr.has(i) and expr.has(j): tmp = Dummy() expr = expr.subs(i,tmp) expr = expr.subs(j,i) expr = expr.subs(tmp,j) return S.NegativeOne*expr else: return expr def _latex(self, printer): return "P(%s%s)"%self.args def simplify_index_permutations(expr, permutation_operators): """ Performs simplification by introducing PermutationOperators where appropriate. Schematically: [abij] - [abji] - [baij] + [baji] -> P(ab)*P(ij)*[abij] permutation_operators is a list of PermutationOperators to consider. If permutation_operators=[P(ab),P(ij)] we will try to introduce the permutation operators P(ij) and P(ab) in the expression. If there are other possible simplifications, we ignore them. >>> from sympy import symbols, Function >>> from sympy.physics.secondquant import simplify_index_permutations >>> from sympy.physics.secondquant import PermutationOperator >>> p,q,r,s = symbols('p,q,r,s') >>> f = Function('f') >>> g = Function('g') >>> expr = f(p)*g(q) - f(q)*g(p); expr f(p)*g(q) - f(q)*g(p) >>> simplify_index_permutations(expr,[PermutationOperator(p,q)]) f(p)*g(q)*PermutationOperator(p, q) >>> PermutList = [PermutationOperator(p,q),PermutationOperator(r,s)] >>> expr = f(p,r)*g(q,s) - f(q,r)*g(p,s) + f(q,s)*g(p,r) - f(p,s)*g(q,r) >>> simplify_index_permutations(expr,PermutList) f(p, r)*g(q, s)*PermutationOperator(p, q)*PermutationOperator(r, s) """ def _get_indices(expr, ind): """ Collects indices recursively in predictable order. """ result = [] for arg in expr.args: if arg in ind: result.append(arg) else: if arg.args: result.extend(_get_indices(arg,ind)) return result def _choose_one_to_keep(a,b,ind): # we keep the one where indices in ind are in order ind[0] < ind[1] if _get_indices(a,ind) < _get_indices(b,ind): return a else: return b expr = expr.expand() if isinstance(expr,Add): terms = set(expr.args) for P in permutation_operators: new_terms = set([]) on_hold = set([]) while terms: term = terms.pop() permuted = P.get_permuted(term) if permuted in terms | on_hold: try: terms.remove(permuted) except KeyError: on_hold.remove(permuted) keep = _choose_one_to_keep(term, permuted, P.args) new_terms.add(P*keep) else: # Some terms must get a second chance because the permuted # term may already have canonical dummy ordering. Then # substitute_dummies() does nothing. However, the other # term, if it exists, will be able to match with us. permuted1 = permuted permuted = substitute_dummies(permuted) if permuted1 == permuted: on_hold.add(term) elif permuted in terms | on_hold: try: terms.remove(permuted) except KeyError: on_hold.remove(permuted) keep = _choose_one_to_keep(term, permuted, P.args) new_terms.add(P*keep) else: new_terms.add(term) terms = new_terms | on_hold return Add(*terms) return expr wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/units.py0000644000175000017500000001224612014170666024302 0ustar georgeskgeorgesk""" Physical units and dimensions. The base class is Unit, where all here defined units (~200) inherit from. """ from sympy import Rational, pi from sympy.core import AtomicExpr class Unit(AtomicExpr): """ Base class for all physical units. Create own units like: m = Unit("meter", "m") """ is_positive = True # make (m**2)**Rational(1,2) --> m is_commutative = True __slots__ = ["name", "abbrev"] def __new__(cls, name, abbrev, **assumptions): obj = AtomicExpr.__new__(cls, **assumptions) assert isinstance(name, str),repr(type(name)) assert isinstance(abbrev, str),repr(type(abbrev)) obj.name = name obj.abbrev = abbrev return obj def __getnewargs__(self): return (self.name, self.abbrev) def __eq__(self, other): return isinstance(other, Unit) and self.name == other.name def __hash__(self): return super(Unit, self).__hash__() def _hashable_content(self): return (self.name,self.abbrev) def defunit(value, *names): u = value g = globals() for name in names: g[name] = u # Dimensionless percent = percents = Rational(1,100) permille = permille = Rational(1,1000) ten = Rational(10) yotta = ten**24 zetta = ten**21 exa = ten**18 peta = ten**15 tera = ten**12 giga = ten**9 mega = ten**6 kilo = ten**3 deca = ten**1 deci = ten**-1 centi = ten**-2 milli = ten**-3 micro = ten**-6 nano = ten**-9 pico = ten**-12 femto = ten**-15 atto = ten**-18 zepto = ten**-21 yocto = ten**-24 rad = radian = radians = 1 deg = degree = degrees = pi/180 # Base units defunit(Unit('meter', 'm'), 'm', 'meter', 'meters') defunit(Unit('kilogram', 'kg'), 'kg', 'kilogram', 'kilograms') defunit(Unit('second', 's'), 's', 'second', 'seconds') defunit(Unit('ampere', 'A'), 'A', 'ampere', 'amperes') defunit(Unit('kelvin', 'K'), 'K', 'kelvin', 'kelvins') defunit(Unit('mole', 'mol'), 'mol', 'mole', 'moles') defunit(Unit('candela', 'cd'), 'cd', 'candela', 'candelas') # Derived units defunit(1/s, 'Hz', 'hz', 'hertz') defunit(m*kg/s**2, 'N', 'newton', 'newtons') defunit(N*m, 'J', 'joule', 'joules') defunit(J/s, 'W', 'watt', 'watts') defunit(N/m**2, 'Pa', 'pa', 'pascal', 'pascals') defunit(s*A, 'C', 'coulomb', 'coulombs') defunit(W/A, 'v', 'V', 'volt', 'volts') defunit(V/A, 'ohm', 'ohms') defunit(A/V, 'S', 'siemens', 'mho', 'mhos') defunit(C/V, 'F', 'farad', 'farads') defunit(J/A, 'Wb', 'wb', 'weber', 'webers') defunit(V*s/m**2, 'T', 'tesla', 'teslas') defunit(V*s/A, 'H', 'henry', 'henrys') # Common length units defunit(kilo*m, 'km', 'kilometer', 'kilometers') defunit(deci*m, 'dm', 'decimeter', 'decimeters') defunit(centi*m, 'cm', 'centimeter', 'centimeters') defunit(milli*m, 'mm', 'millimeter', 'millimeters') defunit(micro*m, 'um', 'micrometer', 'micrometers', 'micron', 'microns') defunit(nano*m, 'nm', 'nanometer', 'nanometers') defunit(pico*m, 'pm', 'picometer', 'picometers') defunit(Rational('0.3048')*m, 'ft', 'foot', 'feet') defunit(Rational('25.4')*mm, 'inch', 'inches') defunit(3*ft, 'yd', 'yard', 'yards') defunit(5280*ft, 'mi', 'mile', 'miles') # Common volume and area units defunit(m**3 / 1000, 'l', 'liter', 'liters') defunit(deci*l, 'dl', 'deciliter', 'deciliters') defunit(centi*l, 'cl', 'centiliter', 'centiliters') defunit(milli*l, 'ml', 'milliliter', 'milliliters') # Common time units defunit(milli*s, 'ms', 'millisecond', 'milliseconds') defunit(micro*s, 'us', 'microsecond', 'microseconds') defunit(nano*s, 'ns', 'nanosecond', 'nanoseconds') defunit(pico*s, 'ps', 'picosecond', 'picoseconds') defunit(60*s, 'minute', 'minutes') defunit(60*minute, 'h', 'hour', 'hours') defunit(24*hour, 'day', 'days') defunit(Rational('31558149.540')*s, 'sidereal_year', 'sidereal_years') defunit(Rational('365.24219')*day, 'tropical_year', 'tropical_years') defunit(Rational('365')*day, 'common_year', 'common_years') defunit(Rational('365.25')*day, 'julian_year', 'julian_years') year = years = tropical_year # Common mass units defunit(kilogram / kilo, 'g', 'gram', 'grams') defunit(milli * g, 'mg', 'milligram', 'milligrams') defunit(micro * g, 'ug', 'microgram', 'micrograms') #---------------------------------------------------------------------------- # Physical constants # c = speed_of_light = 299792458 * m/s G = gravitational_constant = Rational('6.67428') * ten**-11 * m**3 / kg / s**2 u0 = magnetic_constant = 4*pi * ten**-7 * N/A**2 e0 = electric_constant = 1/(u0 * c**2) Z0 = vacuum_impedance = u0 * c planck = Rational('6.62606896') * ten**-34 * J*s hbar = planck / (2*pi) avogadro = (Rational('6.02214179') * 10**23) / mol boltzmann = Rational('1.3806505') * ten**-23 * J / K gee = gees = Rational('9.80665') * m/s**2 atmosphere = atmospheres = atm = 101325 * pascal kPa = kilo*Pa bar = bars = 100*kPa pound = pounds = 0.45359237 * kg * gee #exact psi = pound / inch ** 2 dHg0 = 13.5951 # approx value at 0 C mmHg = dHg0 * 9.80665 * Pa amu = amus = gram / avogadro quart = quarts = 231 * inch**3 eV = 1.602176487e-19 * J # Other convenient units and magnitudes defunit(c*julian_year, 'ly', 'lightyear', 'lightyears') defunit(149597870691*m, 'au', 'astronomical_unit', 'astronomical_units') # Delete this so it doesn't pollute the namespace del Rational, pi wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/qho_1d.py0000644000175000017500000000257012014170666024312 0ustar georgeskgeorgeskfrom sympy.core import S, pi, Rational from sympy.functions import hermite, sqrt, exp, factorial from sympy.physics.quantum.constants import hbar def psi_n(n, x, m, omega): """ Returns the wavefunction psi_{n} for the One-dimensional harmonic oscillator. ``n`` the "nodal" quantum number. Corresponds to the number of nodes in the wavefunction. n >= 0 ``x`` x coordinate ``m`` mass of the particle ``omega`` angular frequency of the oscillator :Example: >>> from sympy.physics.qho_1d import psi_n >>> from sympy import var >>> var("x m omega") (x, m, omega) >>> psi_n(0, x, m, omega) (m*omega)**(1/4)*exp(-m*omega*x**2/(2*hbar))/(hbar**(1/4)*pi**(1/4)) """ # sympify arguments n, x, m, omega = map(S, [n, x, m, omega]) nu = m * omega / hbar # normalization coefficient C = (nu/pi)**(S(1)/4) * sqrt(1/(2**n*factorial(n))) return C * exp(-nu* x**2 /2) * hermite(n, sqrt(nu)*x) def E_n(n,omega): """ Returns the Energy of the One-dimensional harmonic oscillator ``n`` the "nodal" quantum number ``omega`` the harmonic oscillator angular frequency The unit of the returned value matches the unit of hw, since the energy is calculated as: E_n = hbar * omega*(n + 1/2) """ return hbar * omega*(n + Rational(1,2)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/0000755000175000017500000000000012014170666023723 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_clebsch_gordan.py0000644000175000017500000000545512014170666030302 0ustar georgeskgeorgeskfrom sympy import S, sqrt, pi from sympy.physics.wigner import clebsch_gordan, wigner_9j, wigner_6j, gaunt from sympy.core.numbers import Rational # Todo: more tests should be added from: # http://en.wikipedia.org/wiki/Table_of_Clebsch-Gordan_coefficients def test_clebsch_gordan_docs(): assert clebsch_gordan(S(3)/2,S(1)/2,2, S(3)/2,S(1)/2,2) == 1 assert clebsch_gordan(S(3)/2,S(1)/2,1, S(3)/2,-S(1)/2,1) == sqrt(3)/2 assert clebsch_gordan(S(3)/2,S(1)/2,1, -S(1)/2,S(1)/2,0) == -sqrt(2)/2 def test_clebsch_gordan1(): j_1 = S(1)/2 j_2 = S(1)/2 m = 1 j = 1 m_1 = S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 1 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 1 m_1 = S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 0 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 1 m_1 = S(1)/2 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/2 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 0 m_1 = S(1)/2 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/2 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 1 m_1 = -S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/2 j_1 = S(1)/2 j_2 = S(1)/2 m = 0 j = 0 m_1 = -S(1)/2 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == -sqrt(2)/2 def test_clebsch_gordan2(): j_1 = S(1) j_2 = S(1)/2 m = S(3)/2 j = S(3)/2 m_1 = 1 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 1 j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(3)/2 m_1 = 1 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == 1/sqrt(3) j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(1)/2 m_1 = 1 m_2 = -S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/sqrt(3) j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(1)/2 m_1 = 0 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == -1/sqrt(3) j_1 = S(1) j_2 = S(1)/2 m = S(1)/2 j = S(3)/2 m_1 = 0 m_2 = S(1)/2 assert clebsch_gordan(j_1, j_2, j, m_1, m_2, m) == sqrt(2)/sqrt(3) def test_wigner(): def tn(a, b): return abs((a - b).n(64) < S('1e-64')) assert tn(wigner_9j(1,1,1, 1,1,1, 1,1,0 ,prec=64), S(1)/18) assert wigner_9j(3,3,2, 3,3,2, 3,3,2) == 3221*sqrt(70)/(246960*sqrt(105)) - 365/(3528*sqrt(70)*sqrt(105)) assert wigner_6j(5,5,5,5,5,5) == Rational(1,52) assert tn(wigner_6j(8,8,8,8,8,8, prec=64), -S(12219)/965770) def test_gaunt(): def tn(a, b): return abs((a - b).n(64) < S('1e-64')) assert gaunt(1,0,1,1,0,-1) == -1/(2*sqrt(pi)) assert tn(gaunt(10,10,12,9,3,-12, prec=64), (-S(98)/62031) * sqrt(6279)/sqrt(pi)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_qho_1d.py0000644000175000017500000000213312014170666026506 0ustar georgeskgeorgeskfrom sympy.physics.qho_1d import psi_n, E_n from sympy.physics.quantum.constants import hbar from sympy import var, simplify, integrate, oo from sympy.core import S, pi, Rational from sympy.functions import sqrt, exp var("x m omega") nu = m * omega / hbar def test_wavefunction(): Psi = { 0: (nu/pi)**(S(1)/4) * exp(-nu * x**2 /2), 1: (nu/pi)**(S(1)/4) * sqrt(2*nu) * x * exp(-nu * x**2 /2), 2: (nu/pi)**(S(1)/4) * (2 * nu * x**2 - 1)/sqrt(2) * exp(-nu * x**2 /2), 3: (nu/pi)**(S(1)/4) * sqrt(nu/3) * (2 * nu * x**3 - 3 * x) * exp(-nu * x**2 /2) } for n in Psi: assert simplify(psi_n(n, x, m, omega) - Psi[n]) == 0 def test_norm(n=1): # Maximum "n" which is tested: for i in range(n+1): assert integrate(psi_n(i, x, 1, 1)**2, (x,-oo,oo)) == 1 def test_orthogonality(n=1): # Maximum "n" which is tested: for i in range(n+1): for j in range(i+1,n+1): assert integrate(psi_n(i, x, 1, 1)*psi_n(j, x, 1, 1), (x,-oo,oo)) == 0 def test_energies(n=1): # Maximum "n" which is tested: for i in range(n+1): assert E_n(i,omega) == hbar * omega * (i + Rational(1,2)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_secondquant.py0000644000175000017500000013406012014170666027664 0ustar georgeskgeorgeskfrom sympy.physics.secondquant import ( Dagger, Bd, VarBosonicBasis, BBra, B, BKet, FixedBosonicBasis, matrix_rep, apply_operators, InnerProduct, Commutator, KroneckerDelta, FockState, AnnihilateBoson, CreateBoson, BosonicOperator, F, Fd, FKet, FBra, BosonState, CreateFermion, AnnihilateFermion, evaluate_deltas, AntiSymmetricTensor, contraction, NO, wicks, PermutationOperator, simplify_index_permutations, _sort_anticommuting_fermions, _get_ordered_dummies, substitute_dummies ) from sympy import ( symbols, Symbol, sympify, sqrt, Rational, Sum, I, simplify, expand, Function, Dummy ) def test_PermutationOperator(): p,q,r,s = symbols('p,q,r,s') f,g,h,i = map(Function, 'fghi') P = PermutationOperator assert P(p,q).get_permuted(f(p)*g(q)) == -f(q)*g(p) assert P(p,q).get_permuted(f(p, q)) == -f(q, p) assert P(p,q).get_permuted(f(p)) == f(p) expr = (f(p)*g(q)*h(r)*i(s) - f(q)*g(p)*h(r)*i(s) - f(p)*g(q)*h(s)*i(r) + f(q)*g(p)*h(s)*i(r)) perms = [P(p,q),P(r,s)] assert (simplify_index_permutations(expr,perms) == P(p,q)*P(r,s)*f(p)*g(q)*h(r)*i(s)) def test_index_permutations_with_dummies(): a,b,c,d = symbols('a b c d') p,q,r,s = symbols('p q r s', cls=Dummy) f,g = map(Function, 'fg') P = PermutationOperator # No dummy substitution necessary expr = f(a, b, p, q) - f(b, a, p, q) assert simplify_index_permutations( expr, [P(a, b)]) == P(a, b)*f(a, b, p, q) # Cases where dummy substitution is needed expected = P(a, b)*substitute_dummies(f(a, b, p, q)) expr = f(a, b, p, q) - f(b, a, q, p) result = simplify_index_permutations(expr, [P(a, b)]) assert expected == substitute_dummies(result) expr = f(a, b, q, p) - f(b, a, p, q) result = simplify_index_permutations(expr, [P(a, b)]) assert expected == substitute_dummies(result) # A case where nothing can be done expr = f(a, b, q, p) - g(b, a, p, q) result = simplify_index_permutations(expr, [P(a, b)]) assert expr == result def test_dagger(): i, j, n, m = symbols('i,j,n,m') assert Dagger(1) == 1 assert Dagger(1.0) == 1.0 assert Dagger(2*I) == -2*I assert Dagger(Rational(1,2)*I/3.0) == -Rational(1,2)*I/3.0 assert Dagger(BKet([n])) == BBra([n]) assert Dagger(B(0)) == Bd(0) assert Dagger(Bd(0)) == B(0) assert Dagger(B(n)) == Bd(n) assert Dagger(Bd(n)) == B(n) assert Dagger(B(0)+B(1)) == Bd(0) + Bd(1) assert Dagger(n*m) == Dagger(n)*Dagger(m) # n, m commute assert Dagger(B(n)*B(m)) == Bd(m)*Bd(n) assert Dagger(B(n)**10) == Dagger(B(n))**10 def test_operator(): i, j = symbols('i,j') o = BosonicOperator(i) assert o.state == i assert o.is_symbolic o = BosonicOperator(1) assert o.state == 1 assert not o.is_symbolic def test_create(): i, j, n, m = symbols('i,j,n,m') o = Bd(i) assert isinstance(o, CreateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = Bd(0) assert o.apply_operator(BKet([n])) == sqrt(n+1)*BKet([n+1]) o = Bd(n) assert o.apply_operator(BKet([n])) == o*BKet([n]) def test_annihilate(): i, j, n, m = symbols('i,j,n,m') o = B(i) assert isinstance(o, AnnihilateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = B(0) assert o.apply_operator(BKet([n])) == sqrt(n)*BKet([n-1]) o = B(n) assert o.apply_operator(BKet([n])) == o*BKet([n]) def test_basic_state(): i, j, n, m = symbols('i,j,n,m') s = BosonState([0,1,2,3,4]) assert len(s) == 5 assert s.args[0] == tuple(range(5)) assert s.up(0) == BosonState([1,1,2,3,4]) assert s.down(4) == BosonState([0,1,2,3,3]) for i in range(5): assert s.up(i).down(i) == s assert s.down(0) == 0 for i in range(5): assert s[i] == i s = BosonState([n,m]) assert s.down(0) == BosonState([n-1,m]) assert s.up(0) == BosonState([n+1,m]) def test_kronecker_delta(): i, j, k = symbols('i,j,k') D = KroneckerDelta assert D(i, i) == 1 assert D(i, i + 1) == 0 assert D(0, 0) == 1 assert D(0, 1) == 0 # assert D(i, i + k) == D(0, k) assert D(i + k, i + k) == 1 assert D(i + k, i + 1 + k) == 0 assert D(i, j).subs(dict(i=1, j=0)) == 0 assert D(i, j).subs(dict(i=3, j=3)) == 1 i,j,k,l = symbols('i j k l',below_fermi=True,cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True, cls=Dummy) p,q,r,s = symbols('p q r s',dumy=True) assert D(i,a) == 0 assert D(i,j).is_above_fermi == False assert D(a,b).is_above_fermi == True assert D(p,q).is_above_fermi == True assert D(i,q).is_above_fermi == False assert D(a,q).is_above_fermi == True assert D(i,j).is_below_fermi == True assert D(a,b).is_below_fermi == False assert D(p,q).is_below_fermi == True assert D(p,j).is_below_fermi == True assert D(q,b).is_below_fermi == False assert not D(i,q).indices_contain_equal_information assert not D(a,q).indices_contain_equal_information assert D(p,q).indices_contain_equal_information assert D(a,b).indices_contain_equal_information assert D(i,j).indices_contain_equal_information assert D(q,b).preferred_index == b assert D(q,b).killable_index == q assert D(q,i).preferred_index == i assert D(q,i).killable_index == q assert D(q,p).preferred_index == p assert D(q,p).killable_index == q EV = evaluate_deltas assert EV(D(a,q)*F(q)) == F(a) assert EV(D(i,q)*F(q)) == F(i) assert EV(D(a,q)*F(a)) == D(a,q)*F(a) assert EV(D(i,q)*F(i)) == D(i,q)*F(i) assert EV(D(a,b)*F(a)) == F(b) assert EV(D(a,b)*F(b)) == F(a) assert EV(D(i,j)*F(i)) == F(j) assert EV(D(i,j)*F(j)) == F(i) assert EV(D(p,q)*F(q)) == F(p) assert EV(D(p,q)*F(p)) == F(q) assert EV(D(p,j)*D(p,i)*F(i)) == F(j) assert EV(D(p,j)*D(p,i)*F(j)) == F(i) assert EV(D(p,q)*D(p,i))*F(i) == D(q,i)*F(i) # def Xtest_move1(): # i, j = symbols('i,j') # o = A(i)*C(j) # # This almost works, but has a minus sign wrong # assert move(o, 0, 1) == KroneckerDelta(i, j) + C(j)*A(i) # # def Xtest_move2(): # i, j = symbols('i,j') # o = C(j)*A(i) # # This almost works, but has a minus sign wrong # assert move(o, 0, 1) == -KroneckerDelta(i, j) + A(i)*C(j) def test_basic_apply(): n = symbols("n") e = B(0)*BKet([n]) assert apply_operators(e) == sqrt(n)*BKet([n-1]) e = Bd(0)*BKet([n]) assert apply_operators(e) == sqrt(n+1)*BKet([n+1]) def test_complex_apply(): n, m = symbols("n,m") o = Bd(0)*B(0)*Bd(1)*B(0) e = apply_operators(o*BKet([n,m])) answer = sqrt(n)*sqrt(m+1)*(-1+n)*BKet([-1+n,1+m]) assert expand(e) == expand(answer) def test_number_operator(): n = symbols("n") o = Bd(0)*B(0) e = apply_operators(o*BKet([n])) assert e == n*BKet([n]) def test_inner_product(): i, j, k, l = symbols('i,j,k,l') s1 = BBra([0]) s2 = BKet([1]) assert InnerProduct(s1, Dagger(s1)) == 1 assert InnerProduct(s1, s2) == 0 s1 = BBra([i, j]) s2 = BKet([k, l]) r = InnerProduct(s1, s2) assert r == KroneckerDelta(i, k)*KroneckerDelta(j, l) def test_symbolic_matrix_elements(): n, m = symbols('n,m') s1 = BBra([n]) s2 = BKet([m]) o = B(0) e = apply_operators(s1*o*s2) assert e == sqrt(m)*KroneckerDelta(n, m-1) def test_matrix_elements(): b = VarBosonicBasis(5) o = B(0) m = matrix_rep(o, b) for i in range(4): assert m[i, i+1] == sqrt(i+1) o = Bd(0) m = matrix_rep(o, b) for i in range(4): assert m[i+1, i] == sqrt(i+1) def test_sho(): n, m = symbols('n,m') h_n = Bd(n)*B(n)*(n + Rational(1, 2)) H = Sum(h_n, (n, 0, 5)) o = H.doit(deep = False) b = FixedBosonicBasis(2, 6) m = matrix_rep(o, b) # We need to double check these energy values to make sure that they # are correct and have the proper degeneracies! diag = [1, 2, 3, 3, 4, 5, 4, 5, 6, 7, 5, 6, 7, 8, 9, 6, 7, 8, 9, 10, 11] for i in range(len(diag)): assert diag[i] == m[i, i] def test_commutation(): n, m = symbols("n,m", above_fermi=True) c = Commutator(B(0), Bd(0)) assert c == 1 c = Commutator(Bd(0), B(0)) assert c == -1 c = Commutator(B(n), Bd(0)) assert c == KroneckerDelta(n,0) c = Commutator(B(0), Bd(0)) e = simplify(apply_operators(c*BKet([n]))) assert e == BKet([n]) c = Commutator(B(0), B(1)) e = simplify(apply_operators(c*BKet([n,m]))) assert e == 0 c = Commutator(F(m), Fd(m)) assert c == +1 - 2*NO(Fd(m)*F(m)) c = Commutator(Fd(m), F(m)) assert c == -1 + 2*NO(Fd(m)*F(m)) C = Commutator X,Y,Z = symbols('X,Y,Z',commutative=False) assert C(C(X,Y),Z) != 0 assert C(C(X,Z),Y) != 0 assert C(Y,C(X,Z)) != 0 # assert (C(C(Y,Z),X).eval_nested() + C(C(Z,X),Y).eval_nested() + C(C(X,Y),Z).eval_nested()) == 0 # assert (C(X,C(Y,Z)).eval_nested() + C(Y,C(Z,X)).eval_nested() + C(Z,C(X,Y)).eval_nested()) == 0 i,j,k,l = symbols('i,j,k,l',below_fermi=True) a,b,c,d = symbols('a,b,c,d',above_fermi=True) p,q,r,s = symbols('p,q,r,s') D=KroneckerDelta assert C(Fd(a),F(i)) == -2*NO(F(i)*Fd(a)) assert C(Fd(j),NO(Fd(a)*F(i))).doit(wicks=True) == -D(j,i)*Fd(a) assert C(Fd(a)*F(i),Fd(b)*F(j)).doit(wicks=True) == 0 def test_create_f(): i, j, n, m = symbols('i,j,n,m') o = Fd(i) assert isinstance(o, CreateFermion) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = Fd(1) assert o.apply_operator(FKet([n])) == FKet([1,n]) assert o.apply_operator(FKet([n])) ==-FKet([n,1]) o = Fd(n) assert o.apply_operator(FKet([])) == FKet([n]) vacuum = FKet([],fermi_level=4) assert vacuum == FKet([],fermi_level=4) i,j,k,l = symbols('i,j,k,l',below_fermi=True) a,b,c,d = symbols('a,b,c,d',above_fermi=True) p,q,r,s = symbols('p,q,r,s') assert Fd(i).apply_operator(FKet([i,j,k],4)) == FKet([j,k],4) assert Fd(a).apply_operator(FKet([i,b,k],4)) == FKet([a,i,b,k],4) def test_annihilate_f(): i, j, n, m = symbols('i,j,n,m') o = F(i) assert isinstance(o, AnnihilateFermion) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = F(1) assert o.apply_operator(FKet([1,n])) == FKet([n]) assert o.apply_operator(FKet([n,1])) ==-FKet([n]) o = F(n) assert o.apply_operator(FKet([n])) == FKet([]) i,j,k,l = symbols('i,j,k,l',below_fermi=True) a,b,c,d = symbols('a,b,c,d',above_fermi=True) p,q,r,s = symbols('p,q,r,s') assert F(i).apply_operator(FKet([i,j,k],4)) == 0 assert F(a).apply_operator(FKet([i,b,k],4)) == 0 assert F(l).apply_operator(FKet([i,j,k],3)) == 0 assert F(l).apply_operator(FKet([i,j,k],4)) == FKet([l,i,j,k],4) def test_create_b(): i, j, n, m = symbols('i,j,n,m') o = Bd(i) assert isinstance(o, CreateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = Bd(0) assert o.apply_operator(BKet([n])) == sqrt(n+1)*BKet([n+1]) o = Bd(n) assert o.apply_operator(BKet([n])) == o*BKet([n]) def test_annihilate_b(): i, j, n, m = symbols('i,j,n,m') o = B(i) assert isinstance(o, AnnihilateBoson) o = o.subs(i, j) assert o.atoms(Symbol) == set([j]) o = B(0) def test_wicks(): p,q,r,s = symbols('p,q,r,s',above_fermi=True) # Testing for particles only str = F(p)*Fd(q) assert wicks(str) == NO(F(p)*Fd(q)) + KroneckerDelta(p,q) str = Fd(p)*F(q) assert wicks(str) == NO(Fd(p)*F(q)) str = F(p)*Fd(q)*F(r)*Fd(s) nstr= wicks(str) fasit = NO( KroneckerDelta(p, q)*KroneckerDelta(r, s) + KroneckerDelta(p, q)*AnnihilateFermion(r)*CreateFermion(s) + KroneckerDelta(r, s)*AnnihilateFermion(p)*CreateFermion(q) - KroneckerDelta(p, s)*AnnihilateFermion(r)*CreateFermion(q) - AnnihilateFermion(p)*AnnihilateFermion(r)*CreateFermion(q)*CreateFermion(s)) assert nstr == fasit assert (p*q*nstr).expand() == wicks(p*q*str) assert (nstr*p*q*2).expand() == wicks(str*p*q*2) # Testing CC equations particles and holes i,j,k,l = symbols('i j k l',below_fermi=True,cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True,cls=Dummy) p,q,r,s = symbols('p q r s',cls=Dummy) assert (wicks(F(a)*NO(F(i)*F(j))*Fd(b)) == NO(F(a)*F(i)*F(j)*Fd(b)) + KroneckerDelta(a,b)*NO(F(i)*F(j))) assert (wicks(F(a)*NO(F(i)*F(j)*F(k))*Fd(b)) == NO(F(a)*F(i)*F(j)*F(k)*Fd(b)) - KroneckerDelta(a,b)*NO(F(i)*F(j)*F(k))) expr = wicks(Fd(i)*NO(Fd(j)*F(k))*F(l)) assert (expr == -KroneckerDelta(i,k)*NO(Fd(j)*F(l)) - KroneckerDelta(j,l)*NO(Fd(i)*F(k)) - KroneckerDelta(i,k)*KroneckerDelta(j,l)+ KroneckerDelta(i,l)*NO(Fd(j)*F(k)) + NO(Fd(i)*Fd(j)*F(k)*F(l))) expr = wicks(F(a)*NO(F(b)*Fd(c))*Fd(d)) assert (expr == -KroneckerDelta(a,c)*NO(F(b)*Fd(d)) - KroneckerDelta(b,d)*NO(F(a)*Fd(c)) - KroneckerDelta(a,c)*KroneckerDelta(b,d)+ KroneckerDelta(a,d)*NO(F(b)*Fd(c)) + NO(F(a)*F(b)*Fd(c)*Fd(d))) def test_NO(): i,j,k,l = symbols('i j k l',below_fermi=True) a,b,c,d = symbols('a b c d',above_fermi=True) p,q,r,s = symbols('p q r s', cls=Dummy) assert (NO(Fd(p)*F(q) + Fd(a)*F(b))== NO(Fd(p)*F(q)) + NO(Fd(a)*F(b))) assert (NO(Fd(i)*NO(F(j)*Fd(a))) == NO(Fd(i)*F(j)*Fd(a))) assert NO(1) == 1 assert NO(i) == i assert (NO(Fd(a)*Fd(b)*(F(c)+F(d))) == NO(Fd(a)*Fd(b)*F(c)) + NO(Fd(a)*Fd(b)*F(d))) assert NO(Fd(a)*F(b))._remove_brackets()==Fd(a)*F(b) assert NO(F(j)*Fd(i))._remove_brackets()==F(j)*Fd(i) assert (NO(Fd(p)*F(q)).subs(Fd(p),Fd(a)+Fd(i)) == NO(Fd(a)*F(q)) + NO(Fd(i)*F(q))) assert (NO(Fd(p)*F(q)).subs(F(q),F(a)+F(i)) == NO(Fd(p)*F(a)) + NO(Fd(p)*F(i))) expr = NO(Fd(p)*F(q))._remove_brackets() assert wicks(expr) == NO(expr) assert NO(Fd(a)*F(b)) == - NO(F(b)*Fd(a)) no = NO(Fd(a)*F(i)*Fd(j)*F(b)) l1 = [ ind for ind in no.iter_q_creators() ] assert l1 == [0,1] l2 = [ ind for ind in no.iter_q_annihilators() ] assert l2 == [3,2] def test_sorting(): i,j = symbols('i,j',below_fermi=True) a,b = symbols('a,b',above_fermi=True) p,q = symbols('p,q') # p, q assert _sort_anticommuting_fermions([Fd(p), F(q)]) == ([Fd(p), F(q)], 0) assert _sort_anticommuting_fermions([F(p), Fd(q)]) == ([Fd(q), F(p)], 1) # i, p assert _sort_anticommuting_fermions([F(p), Fd(i)]) == ([F(p), Fd(i)], 0) assert _sort_anticommuting_fermions([Fd(i), F(p)]) == ([F(p), Fd(i)], 1) assert _sort_anticommuting_fermions([Fd(p), Fd(i)]) == ([Fd(p), Fd(i)], 0) assert _sort_anticommuting_fermions([Fd(i), Fd(p)]) == ([Fd(p), Fd(i)], 1) assert _sort_anticommuting_fermions([F(p), F(i)]) == ([F(i), F(p)], 1) assert _sort_anticommuting_fermions([F(i), F(p)]) == ([F(i), F(p)], 0) assert _sort_anticommuting_fermions([Fd(p), F(i)]) == ([F(i), Fd(p)], 1) assert _sort_anticommuting_fermions([F(i), Fd(p)]) == ([F(i), Fd(p)], 0) # a, p assert _sort_anticommuting_fermions([F(p), Fd(a)]) == ([Fd(a), F(p)], 1) assert _sort_anticommuting_fermions([Fd(a), F(p)]) == ([Fd(a), F(p)], 0) assert _sort_anticommuting_fermions([Fd(p), Fd(a)]) == ([Fd(a), Fd(p)], 1) assert _sort_anticommuting_fermions([Fd(a), Fd(p)]) == ([Fd(a), Fd(p)], 0) assert _sort_anticommuting_fermions([F(p), F(a)]) == ([F(p), F(a)], 0) assert _sort_anticommuting_fermions([F(a), F(p)]) == ([F(p), F(a)], 1) assert _sort_anticommuting_fermions([Fd(p), F(a)]) == ([Fd(p), F(a)], 0) assert _sort_anticommuting_fermions([F(a), Fd(p)]) == ([Fd(p), F(a)], 1) # i, a assert _sort_anticommuting_fermions([F(i), Fd(j)]) == ([F(i), Fd(j)], 0) assert _sort_anticommuting_fermions([Fd(j), F(i)]) == ([F(i), Fd(j)], 1) assert _sort_anticommuting_fermions([Fd(a), Fd(i)]) == ([Fd(a), Fd(i)], 0) assert _sort_anticommuting_fermions([Fd(i), Fd(a)]) == ([Fd(a), Fd(i)], 1) assert _sort_anticommuting_fermions([F(a), F(i)]) == ([F(i), F(a)], 1) assert _sort_anticommuting_fermions([F(i), F(a)]) == ([F(i), F(a)], 0) def test_contraction(): i,j,k,l = symbols('i,j,k,l',below_fermi=True) a,b,c,d = symbols('a,b,c,d',above_fermi=True) p,q,r,s = symbols('p,q,r,s') assert contraction(Fd(i),F(j)) == KroneckerDelta(i,j) assert contraction(F(a),Fd(b)) == KroneckerDelta(a,b) assert contraction(F(a),Fd(i)) == 0 assert contraction(Fd(a),F(i)) == 0 assert contraction(F(i),Fd(a)) == 0 assert contraction(Fd(i),F(a)) == 0 assert contraction(Fd(i),F(p)) == KroneckerDelta(p,i) restr = evaluate_deltas(contraction(Fd(p),F(q))) assert restr.is_only_below_fermi restr = evaluate_deltas(contraction(F(p),Fd(q))) assert restr.is_only_above_fermi def test_Tensors(): i,j,k,l = symbols('i j k l',below_fermi=True,cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True,cls=Dummy) p,q,r,s = symbols('p q r s') AT= AntiSymmetricTensor assert AT('t',(a,b),(i,j)) == -AT('t',(b,a),(i,j)) assert AT('t',(a,b),(i,j)) == AT('t',(b,a),(j,i)) assert AT('t',(a,b),(i,j)) == -AT('t',(a,b),(j,i)) assert AT('t',(a,a),(i,j)) == 0 assert AT('t',(a,b),(i,i)) == 0 assert AT('t',(a,b,c),(i,j)) == -AT('t',(b,a,c),(i,j)) assert AT('t',(a,b,c),(i,j,k)) == AT('t',(b,a,c),(i,k,j)) tabij = AT('t',(a,b),(i,j)) assert a in tabij assert b in tabij assert i in tabij assert j in tabij assert tabij.subs(b,c) == AT('t',(a,c),(i,j)) assert (2*tabij).subs(i,c) == 2*AT('t',(a,b),(c,j)) assert AT('t', (a, a), (i, j)).subs(a, b) == AT('t', (b, b), (i, j)) assert AT('t', (a, i), (a, j)).subs(a, b) == AT('t', (b, i), (b, j)) def test_fully_contracted(): i,j,k,l = symbols('i j k l',below_fermi=True) a,b,c,d = symbols('a b c d',above_fermi=True) p,q,r,s = symbols('p q r s', cls=Dummy) Fock = (AntiSymmetricTensor('f',(p,),(q,))* NO(Fd(p)*F(q))) V = (AntiSymmetricTensor('v',(p,q),(r,s))* NO(Fd(p)*Fd(q)*F(s)*F(r)))/4 Fai=wicks(NO(Fd(i)*F(a))*Fock, keep_only_fully_contracted=True, simplify_kronecker_deltas=True) assert Fai == AntiSymmetricTensor('f',(a,),(i,)) Vabij=wicks(NO(Fd(i)*Fd(j)*F(b)*F(a))*V, keep_only_fully_contracted=True, simplify_kronecker_deltas=True) assert Vabij==AntiSymmetricTensor('v',(a,b),(i,j)) def test_substitute_dummies_without_dummies(): i,j = symbols('i,j') assert substitute_dummies(att(i, j) + 2) == att(i, j) + 2 assert substitute_dummies(att(i, j) + 1) == att(i, j) + 1 def test_substitute_dummies_NO_operator(): i,j = symbols('i j', cls=Dummy) assert substitute_dummies(att(i, j)*NO(Fd(i)*F(j)) - att(j, i)*NO(Fd(j)*F(i))) == 0 def test_substitute_dummies_SQ_operator(): i,j = symbols('i j', cls=Dummy) assert substitute_dummies(att(i, j)*Fd(i)*F(j) - att(j, i)*Fd(j)*F(i)) == 0 def test_substitute_dummies_new_indices(): i,j = symbols('i j',below_fermi=True, cls=Dummy) a,b = symbols('a b',above_fermi=True, cls=Dummy) p,q = symbols('p q', cls=Dummy) f = Function('f') assert substitute_dummies(f(i,a,p) - f(j,b,q), new_indices=True) == 0 def test_substitute_dummies_substitution_order(): i,j,k,l = symbols('i j k l',below_fermi=True, cls=Dummy) f = Function('f') from sympy.utilities.iterables import variations for permut in variations([i,j,k,l], 4): assert substitute_dummies(f(*permut) - f(i,j,k,l)) == 0 def test_dummy_order_inner_outer_lines_VT1T1T1(): ii = symbols('i',below_fermi=True) aa = symbols('a',above_fermi=True) k, l = symbols('k l',below_fermi=True, cls=Dummy) c, d = symbols('c d',above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies # Coupled-Cluster T1 terms with V*T1*T1*T1 # t^{a}_{k} t^{c}_{i} t^{d}_{l} v^{lk}_{dc} exprs = [ # permut v and t <=> swapping internal lines, equivalent # irrespective of symmetries in v v(k, l, c, d)*t(c, ii)*t(d, l)*t(aa, k), v(l, k, c, d)*t(c, ii)*t(d, k)*t(aa, l), v(k, l, d, c)*t(d, ii)*t(c, l)*t(aa, k), v(l, k, d, c)*t(d, ii)*t(c, k)*t(aa, l), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_dummy_order_inner_outer_lines_VT1T1T1T1(): ii,jj = symbols('i j',below_fermi=True) aa,bb = symbols('a b',above_fermi=True) k, l = symbols('k l',below_fermi=True, cls=Dummy) c, d = symbols('c d',above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies # Coupled-Cluster T2 terms with V*T1*T1*T1*T1 exprs = [ # permut t <=> swapping external lines, not equivalent # except if v has certain symmetries. v(k, l, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(k, l, c, d)*t(c, jj)*t(d, ii)*t(aa, k)*t(bb, l), v(k, l, c, d)*t(c, ii)*t(d, jj)*t(bb, k)*t(aa, l), v(k, l, c, d)*t(c, jj)*t(d, ii)*t(bb, k)*t(aa, l), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permut v <=> swapping external lines, not equivalent # except if v has certain symmetries. # # Note that in contrast to above, these permutations have identical # dummy order. That is because the proximity to external indices # has higher influence on the canonical dummy ordering than the # position of a dummy on the factors. In fact, the terms here are # similar in structure as the result of the dummy substitions above. v(k, l, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(l, k, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(k, l, d, c)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(l, k, d, c)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), ] for permut in exprs[1:]: assert dums(exprs[0]) == dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permut t and v <=> swapping internal lines, equivalent. # Canonical dummy order is different, and a consistent # substitution reveals the equivalence. v(k, l, c, d)*t(c, ii)*t(d, jj)*t(aa, k)*t(bb, l), v(k, l, d, c)*t(c, jj)*t(d, ii)*t(aa, k)*t(bb, l), v(l, k, c, d)*t(c, ii)*t(d, jj)*t(bb, k)*t(aa, l), v(l, k, d, c)*t(c, jj)*t(d, ii)*t(bb, k)*t(aa, l), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT1T1(): i,j,k,l = symbols('i j k l',below_fermi=True, cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True, cls=Dummy) f = Function('f') v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ # permute v. Different dummy order. Not equivalent. v(i, j, a, b)*t(a, i)*t(b, j), v(j, i, a, b)*t(a, i)*t(b, j), v(i, j, b, a)*t(a, i)*t(b, j), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v. Different dummy order. Equivalent v(i, j, a, b)*t(a, i)*t(b, j), v(j, i, b, a)*t(a, i)*t(b, j), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ # permute t. Same dummy order, not equivalent. v(i, j, a, b)*t(a, i)*t(b, j), v(i, j, a, b)*t(b, i)*t(a, j), ] for permut in exprs[1:]: assert dums(exprs[0]) == dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Different dummy order, equivalent v(i, j, a, b)*t(a, i)*t(b, j), v(j, i, a, b)*t(a, j)*t(b, i), v(i, j, b, a)*t(b, i)*t(a, j), v(j, i, b, a)*t(b, j)*t(a, i), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT2conjT2(): # this diagram requires special handling in TCE i,j,k,l,m,n = symbols('i j k l m n',below_fermi=True, cls=Dummy) a,b,c,d,e,f = symbols('a b c d e f',above_fermi=True, cls=Dummy) p1,p2,p3,p4 = symbols('p1 p2 p3 p4',above_fermi=True, cls=Dummy) h1,h2,h3,h4 = symbols('h1 h2 h3 h4',below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations v = Function('v') t = Function('t') dums = _get_ordered_dummies # v(abcd)t(abij)t(ijcd) template = v(p1, p2, p3, p4)*t(p1, p2, i, j)*t(i, j, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) template = v(p1, p2, p3, p4)*t(p1, p2, j, i)*t(j, i, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) # v(abcd)t(abij)t(jicd) template = v(p1, p2, p3, p4)*t(p1, p2, i, j)*t(j, i, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) template = v(p1, p2, p3, p4)*t(p1, p2, j, i)*t(i, j, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2conjT2_ambiguous_order(): # These diagrams invokes _determine_ambiguous() because the # dummies can not be ordered unambiguously by the key alone i,j,k,l,m,n = symbols('i j k l m n',below_fermi=True, cls=Dummy) a,b,c,d,e,f = symbols('a b c d e f',above_fermi=True, cls=Dummy) p1,p2,p3,p4 = symbols('p1 p2 p3 p4',above_fermi=True, cls=Dummy) h1,h2,h3,h4 = symbols('h1 h2 h3 h4',below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations v = Function('v') t = Function('t') dums = _get_ordered_dummies # v(abcd)t(abij)t(cdij) template = v(p1, p2, p3, p4)*t(p1, p2, i, j)*t(p3, p4, i, j) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) template = v(p1, p2, p3, p4)*t(p1, p2, j, i)*t(p3, p4, i, j) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert dums(base) != dums(expr) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2(): i,j,k,l = symbols('i j k l',below_fermi=True, cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True, cls=Dummy) f = Function('f') v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ # permute v. Same dummy order, not equivalent. # # This test show that the dummy order may not be sensitive to all # index permutations. The following expressions have identical # structure as the resulting terms from of the dummy subsitutions # in the test above. Here, all expressions have the same dummy # order, so they cannot be simplified by means of dummy # substitution. In order to simplify further, it is necessary to # exploit symmetries in the objects, for instance if t or v is # antisymmetric. v(i, j, a, b)*t(a, b, i, j), v(j, i, a, b)*t(a, b, i, j), v(i, j, b, a)*t(a, b, i, j), v(j, i, b, a)*t(a, b, i, j), ] for permut in exprs[1:]: assert dums(exprs[0]) == dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute t. v(i, j, a, b)*t(a, b, i, j), v(i, j, a, b)*t(b, a, i, j), v(i, j, a, b)*t(a, b, j, i), v(i, j, a, b)*t(b, a, j, i), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Relabelling of dummies should be equivalent. v(i, j, a, b)*t(a, b, i, j), v(j, i, a, b)*t(a, b, j, i), v(i, j, b, a)*t(b, a, i, j), v(j, i, b, a)*t(b, a, j, i), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_VT2T2(): ii, jj = symbols('i j',below_fermi=True) aa, bb = symbols('a b',above_fermi=True) k, l = symbols('k l' ,below_fermi=True, cls=Dummy) c, d = symbols('c d' ,above_fermi=True, cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ v(k,l,c,d)*t(aa, c, ii, k)*t(bb, d, jj, l), v(l,k,c,d)*t(aa, c, ii, l)*t(bb, d, jj, k), v(k,l,d,c)*t(aa, d, ii, k)*t(bb, c, jj, l), v(l,k,d,c)*t(aa, d, ii, l)*t(bb, c, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ v(k,l,c,d)*t(aa, c, ii, k)*t(d, bb, jj, l), v(l,k,c,d)*t(aa, c, ii, l)*t(d, bb, jj, k), v(k,l,d,c)*t(aa, d, ii, k)*t(c, bb, jj, l), v(l,k,d,c)*t(aa, d, ii, l)*t(c, bb, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ v(k,l,c,d)*t(c, aa, ii, k)*t(bb, d, jj, l), v(l,k,c,d)*t(c, aa, ii, l)*t(bb, d, jj, k), v(k,l,d,c)*t(d, aa, ii, k)*t(bb, c, jj, l), v(l,k,d,c)*t(d, aa, ii, l)*t(bb, c, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_pqrs(): ii, jj = symbols('i j') aa, bb = symbols('a b') k, l = symbols('k l' , cls=Dummy) c, d = symbols('c d' , cls=Dummy) v = Function('v') t = Function('t') dums = _get_ordered_dummies exprs = [ v(k,l,c,d)*t(aa, c, ii, k)*t(bb, d, jj, l), v(l,k,c,d)*t(aa, c, ii, l)*t(bb, d, jj, k), v(k,l,d,c)*t(aa, d, ii, k)*t(bb, c, jj, l), v(l,k,d,c)*t(aa, d, ii, l)*t(bb, c, jj, k), ] for permut in exprs[1:]: assert dums(exprs[0]) != dums(permut) assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_dummy_order_well_defined(): aa, bb = symbols('a b', above_fermi=True) k, l, m = symbols('k l m', below_fermi=True, cls=Dummy) c, d = symbols('c d', above_fermi=True, cls=Dummy) p, q = symbols('p q', cls=Dummy) A = Function('A') B = Function('B') C = Function('C') dums = _get_ordered_dummies # We go through all key components in the order of increasing priority, # and consider only fully orderable expressions. Non-orderable expressions # are tested elsewhere. # pos in first factor determines sort order assert dums(A(k, l)*B(l, k)) == [k, l] assert dums(A(l, k)*B(l, k)) == [l, k] assert dums(A(k, l)*B(k, l)) == [k, l] assert dums(A(l, k)*B(k, l)) == [l, k] # factors involving the index assert dums(A(k, l)*B(l, m)*C(k, m)) == [l, k, m] assert dums(A(k, l)*B(l, m)*C(m, k)) == [l, k, m] assert dums(A(l, k)*B(l, m)*C(k, m)) == [l, k, m] assert dums(A(l, k)*B(l, m)*C(m, k)) == [l, k, m] assert dums(A(k, l)*B(m, l)*C(k, m)) == [l, k, m] assert dums(A(k, l)*B(m, l)*C(m, k)) == [l, k, m] assert dums(A(l, k)*B(m, l)*C(k, m)) == [l, k, m] assert dums(A(l, k)*B(m, l)*C(m, k)) == [l, k, m] # same, but with factor order determined by non-dummies assert dums(A(k, aa, l)*A(l, bb, m)*A(bb, k, m)) == [l, k, m] assert dums(A(k, aa, l)*A(l, bb, m)*A(bb, m, k)) == [l, k, m] assert dums(A(k, aa, l)*A(m, bb, l)*A(bb, k, m)) == [l, k, m] assert dums(A(k, aa, l)*A(m, bb, l)*A(bb, m, k)) == [l, k, m] assert dums(A(l, aa, k)*A(l, bb, m)*A(bb, k, m)) == [l, k, m] assert dums(A(l, aa, k)*A(l, bb, m)*A(bb, m, k)) == [l, k, m] assert dums(A(l, aa, k)*A(m, bb, l)*A(bb, k, m)) == [l, k, m] assert dums(A(l, aa, k)*A(m, bb, l)*A(bb, m, k)) == [l, k, m] # index range assert dums(A(p, c, k)*B(p, c, k)) == [k, c, p] assert dums(A(p, k, c)*B(p, c, k)) == [k, c, p] assert dums(A(c, k, p)*B(p, c, k)) == [k, c, p] assert dums(A(c, p, k)*B(p, c, k)) == [k, c, p] assert dums(A(k, c, p)*B(p, c, k)) == [k, c, p] assert dums(A(k, p, c)*B(p, c, k)) == [k, c, p] assert dums(B(p, c, k)*A(p, c, k)) == [k, c, p] assert dums(B(p, k, c)*A(p, c, k)) == [k, c, p] assert dums(B(c, k, p)*A(p, c, k)) == [k, c, p] assert dums(B(c, p, k)*A(p, c, k)) == [k, c, p] assert dums(B(k, c, p)*A(p, c, k)) == [k, c, p] assert dums(B(k, p, c)*A(p, c, k)) == [k, c, p] def test_dummy_order_ambiguous(): aa, bb = symbols('a b', above_fermi=True) i, j, k, l, m = symbols('i j k l m', below_fermi=True, cls=Dummy) a, b, c, d, e = symbols('a b c d e', above_fermi=True, cls=Dummy) p, q = symbols('p q', cls=Dummy) p1,p2,p3,p4 = symbols('p1 p2 p3 p4',above_fermi=True, cls=Dummy) p5,p6,p7,p8 = symbols('p5 p6 p7 p8',above_fermi=True, cls=Dummy) h1,h2,h3,h4 = symbols('h1 h2 h3 h4',below_fermi=True, cls=Dummy) h5,h6,h7,h8 = symbols('h5 h6 h7 h8',below_fermi=True, cls=Dummy) A = Function('A') B = Function('B') dums = _get_ordered_dummies from sympy.utilities.iterables import variations # A*A*A*A*B -- ordering of p5 and p4 is used to figure out the rest template = A(p1, p2)*A(p4, p1)*A(p2, p3)*A(p3, p5)*B(p5, p4) permutator = variations([a,b,c,d,e], 5) base = template.subs(zip([p1, p2, p3, p4, p5], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4, p5], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) # A*A*A*A*A -- an arbitrary index is assigned and the rest are figured out template = A(p1, p2)*A(p4, p1)*A(p2, p3)*A(p3, p5)*A(p5, p4) permutator = variations([a,b,c,d,e], 5) base = template.subs(zip([p1, p2, p3, p4, p5], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4, p5], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) # A*A*A -- ordering of p5 and p4 is used to figure out the rest template = A(p1, p2, p4, p1)*A(p2, p3, p3, p5)*A(p5, p4) permutator = variations([a,b,c,d,e], 5) base = template.subs(zip([p1, p2, p3, p4, p5], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4, p5], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) def atv(*args): return AntiSymmetricTensor('v', args[:2], args[2:] ) def att(*args): if len(args) == 4: return AntiSymmetricTensor('t', args[:2], args[2:] ) elif len(args) == 2: return AntiSymmetricTensor('t', (args[0],), (args[1],)) def test_dummy_order_inner_outer_lines_VT1T1T1_AT(): ii = symbols('i',below_fermi=True) aa = symbols('a',above_fermi=True) k, l = symbols('k l',below_fermi=True, cls=Dummy) c, d = symbols('c d',above_fermi=True, cls=Dummy) # Coupled-Cluster T1 terms with V*T1*T1*T1 # t^{a}_{k} t^{c}_{i} t^{d}_{l} v^{lk}_{dc} exprs = [ # permut v and t <=> swapping internal lines, equivalent # irrespective of symmetries in v atv(k, l, c, d)*att(c, ii)*att(d, l)*att(aa, k), atv(l, k, c, d)*att(c, ii)*att(d, k)*att(aa, l), atv(k, l, d, c)*att(d, ii)*att(c, l)*att(aa, k), atv(l, k, d, c)*att(d, ii)*att(c, k)*att(aa, l), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_dummy_order_inner_outer_lines_VT1T1T1T1_AT(): ii,jj = symbols('i j',below_fermi=True) aa,bb = symbols('a b',above_fermi=True) k, l = symbols('k l',below_fermi=True, cls=Dummy) c, d = symbols('c d',above_fermi=True, cls=Dummy) # Coupled-Cluster T2 terms with V*T1*T1*T1*T1 # non-equivalent substitutions (change of sign) exprs = [ # permut t <=> swapping external lines atv(k, l, c, d)*att(c, ii)*att(d, jj)*att(aa, k)*att(bb, l), atv(k, l, c, d)*att(c, jj)*att(d, ii)*att(aa, k)*att(bb, l), atv(k, l, c, d)*att(c, ii)*att(d, jj)*att(bb, k)*att(aa, l), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == -substitute_dummies(permut) # equivalent substitutions exprs = [ atv(k, l, c, d)*att(c, ii)*att(d, jj)*att(aa, k)*att(bb, l), # permut t <=> swapping external lines atv(k, l, c, d)*att(c, jj)*att(d, ii)*att(bb, k)*att(aa, l), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT1T1_AT(): i,j,k,l = symbols('i j k l',below_fermi=True, cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True, cls=Dummy) exprs = [ # permute v. Different dummy order. Not equivalent. atv(i, j, a, b)*att(a, i)*att(b, j), atv(j, i, a, b)*att(a, i)*att(b, j), atv(i, j, b, a)*att(a, i)*att(b, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v. Different dummy order. Equivalent atv(i, j, a, b)*att(a, i)*att(b, j), atv(j, i, b, a)*att(a, i)*att(b, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ # permute t. Same dummy order, not equivalent. atv(i, j, a, b)*att(a, i)*att(b, j), atv(i, j, a, b)*att(b, i)*att(a, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Different dummy order, equivalent atv(i, j, a, b)*att(a, i)*att(b, j), atv(j, i, a, b)*att(a, j)*att(b, i), atv(i, j, b, a)*att(b, i)*att(a, j), atv(j, i, b, a)*att(b, j)*att(a, i), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_equivalent_internal_lines_VT2conjT2_AT(): # this diagram requires special handling in TCE i,j,k,l,m,n = symbols('i j k l m n',below_fermi=True, cls=Dummy) a,b,c,d,e,f = symbols('a b c d e f',above_fermi=True, cls=Dummy) p1,p2,p3,p4 = symbols('p1 p2 p3 p4',above_fermi=True, cls=Dummy) h1,h2,h3,h4 = symbols('h1 h2 h3 h4',below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations # atv(abcd)att(abij)att(ijcd) template = atv(p1, p2, p3, p4)*att(p1, p2, i, j)*att(i, j, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) template = atv(p1, p2, p3, p4)*att(p1, p2, j, i)*att(j, i, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) # atv(abcd)att(abij)att(jicd) template = atv(p1, p2, p3, p4)*att(p1, p2, i, j)*att(j, i, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) template = atv(p1, p2, p3, p4)*att(p1, p2, j, i)*att(i, j, p3, p4) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2conjT2_ambiguous_order_AT(): # These diagrams invokes _determine_ambiguous() because the # dummies can not be ordered unambiguously by the key alone i,j,k,l,m,n = symbols('i j k l m n',below_fermi=True, cls=Dummy) a,b,c,d,e,f = symbols('a b c d e f',above_fermi=True, cls=Dummy) p1,p2,p3,p4 = symbols('p1 p2 p3 p4',above_fermi=True, cls=Dummy) h1,h2,h3,h4 = symbols('h1 h2 h3 h4',below_fermi=True, cls=Dummy) from sympy.utilities.iterables import variations # atv(abcd)att(abij)att(cdij) template = atv(p1, p2, p3, p4)*att(p1, p2, i, j)*att(p3, p4, i, j) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) template = atv(p1, p2, p3, p4)*att(p1, p2, j, i)*att(p3, p4, i, j) permutator = variations([a,b,c,d], 4) base = template.subs(zip([p1, p2, p3, p4], permutator.next())) for permut in permutator: subslist = zip([p1, p2, p3, p4], permut) expr = template.subs(subslist) assert substitute_dummies(expr) == substitute_dummies(base) def test_equivalent_internal_lines_VT2_AT(): i,j,k,l = symbols('i j k l',below_fermi=True, cls=Dummy) a,b,c,d = symbols('a b c d',above_fermi=True, cls=Dummy) exprs = [ # permute v. Same dummy order, not equivalent. atv(i, j, a, b)*att(a, b, i, j), atv(j, i, a, b)*att(a, b, i, j), atv(i, j, b, a)*att(a, b, i, j), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute t. atv(i, j, a, b)*att(a, b, i, j), atv(i, j, a, b)*att(b, a, i, j), atv(i, j, a, b)*att(a, b, j, i), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) != substitute_dummies(permut) exprs = [ # permute v and t. Relabelling of dummies should be equivalent. atv(i, j, a, b)*att(a, b, i, j), atv(j, i, a, b)*att(a, b, j, i), atv(i, j, b, a)*att(b, a, i, j), atv(j, i, b, a)*att(b, a, j, i), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_VT2T2_AT(): ii, jj = symbols('i j',below_fermi=True) aa, bb = symbols('a b',above_fermi=True) k, l = symbols('k l' ,below_fermi=True, cls=Dummy) c, d = symbols('c d' ,above_fermi=True, cls=Dummy) dums = _get_ordered_dummies exprs = [ atv(k,l,c,d)*att(aa, c, ii, k)*att(bb, d, jj, l), atv(l,k,c,d)*att(aa, c, ii, l)*att(bb, d, jj, k), atv(k,l,d,c)*att(aa, d, ii, k)*att(bb, c, jj, l), atv(l,k,d,c)*att(aa, d, ii, l)*att(bb, c, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ atv(k,l,c,d)*att(aa, c, ii, k)*att(d, bb, jj, l), atv(l,k,c,d)*att(aa, c, ii, l)*att(d, bb, jj, k), atv(k,l,d,c)*att(aa, d, ii, k)*att(c, bb, jj, l), atv(l,k,d,c)*att(aa, d, ii, l)*att(c, bb, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) exprs = [ atv(k,l,c,d)*att(c, aa, ii, k)*att(bb, d, jj, l), atv(l,k,c,d)*att(c, aa, ii, l)*att(bb, d, jj, k), atv(k,l,d,c)*att(d, aa, ii, k)*att(bb, c, jj, l), atv(l,k,d,c)*att(d, aa, ii, l)*att(bb, c, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) def test_internal_external_pqrs_AT(): ii, jj = symbols('i j') aa, bb = symbols('a b') k, l = symbols('k l' , cls=Dummy) c, d = symbols('c d' , cls=Dummy) exprs = [ atv(k,l,c,d)*att(aa, c, ii, k)*att(bb, d, jj, l), atv(l,k,c,d)*att(aa, c, ii, l)*att(bb, d, jj, k), atv(k,l,d,c)*att(aa, d, ii, k)*att(bb, c, jj, l), atv(l,k,d,c)*att(aa, d, ii, l)*att(bb, c, jj, k), ] for permut in exprs[1:]: assert substitute_dummies(exprs[0]) == substitute_dummies(permut) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_physics_matrices.py0000644000175000017500000000244012014170666030705 0ustar georgeskgeorgeskfrom sympy.physics.matrices import msigma, mgamma, minkowski_tensor from sympy import zeros, eye, I def test_Pauli(): #this and the following test are testing both Pauli and Dirac matrices #and also that the general Matrix class works correctly in a real world #situation sigma1=msigma(1) sigma2=msigma(2) sigma3=msigma(3) assert sigma1 == sigma1 assert sigma1 != sigma2 # sigma*I -> I*sigma (see #354) assert sigma1*sigma2 == sigma3*I assert sigma3*sigma1 == sigma2*I assert sigma2*sigma3 == sigma1*I assert sigma1*sigma1 == eye(2) assert sigma2*sigma2 == eye(2) assert sigma3*sigma3 == eye(2) assert sigma1*2*sigma1 == 2*eye(2) assert sigma1*sigma3*sigma1 == -sigma3 def test_Dirac(): gamma0=mgamma(0) gamma1=mgamma(1) gamma2=mgamma(2) gamma3=mgamma(3) gamma5=mgamma(5) # gamma*I -> I*gamma (see #354) assert gamma5 == gamma0 * gamma1 * gamma2 * gamma3 * I assert gamma1 * gamma2 + gamma2 * gamma1 == zeros(4) assert gamma0 * gamma0 == eye(4) * minkowski_tensor[0,0] assert gamma2 * gamma2 != eye(4) * minkowski_tensor[0,0] assert gamma2 * gamma2 == eye(4) * minkowski_tensor[2,2] assert mgamma(5,True) == \ mgamma(0,True)*mgamma(1,True)*mgamma(2,True)*mgamma(3,True)*I wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_sho.py0000644000175000017500000000124012014170666026122 0ustar georgeskgeorgeskfrom sympy.core import symbols, Rational, Function, diff from sympy.physics.sho import R_nl, E_nl from sympy import simplify def test_sho_R_nl(): omega, r = symbols('omega r') l = symbols('l', integer=True) u = Function('u') # check that it obeys the Schrodinger equation for n in range(5): schreq = ( -diff(u(r), r, 2)/2 + ((l*(l+1))/(2*r**2) + omega**2*r**2/2 - E_nl(n, l, omega))*u(r) ) result = schreq.subs(u(r), r*R_nl(n, l, omega/2, r)) assert simplify(result.doit()) == 0 def test_energy(): n, l, hw = symbols('n l hw') assert simplify(E_nl(n, l, hw) - (2*n + l + Rational(3, 2))*hw) == 0 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_hydrogen.py0000644000175000017500000001023512014170666027154 0ustar georgeskgeorgeskfrom sympy import var, sqrt, exp, simplify, S, integrate, oo, Symbol from sympy.physics.hydrogen import R_nl, E_nl, E_nl_dirac from sympy.utilities.pytest import raises var("r Z") def feq(a, b, max_relative_error=1e-12, max_absolute_error=1e-12): a = float(a) b = float(b) # if the numbers are close enough (absolutely), then they are equal if abs(a-b) < max_absolute_error: return True # if not, they can still be equal if their relative error is small if abs(b) > abs(a): relative_error = abs((a-b)/b) else: relative_error = abs((a-b)/a) return relative_error <= max_relative_error def test_wavefunction(): a = 1/Z R = { (1, 0): 2*sqrt(1/a**3) * exp(-r/a), (2, 0): sqrt(1/(2*a**3)) * exp(-r/(2*a)) * (1-r/(2*a)), (2, 1): S(1)/2 * sqrt(1/(6*a**3)) * exp(-r/(2*a)) * r/a, (3, 0): S(2)/3 * sqrt(1/(3*a**3)) * exp(-r/(3*a)) * \ (1-2*r/(3*a) + S(2)/27 * (r/a)**2), (3, 1): S(4)/27 * sqrt(2/(3*a**3)) * exp(-r/(3*a)) * \ (1-r/(6*a)) * r/a, (3, 2): S(2)/81 * sqrt(2/(15*a**3)) * exp(-r/(3*a)) * (r/a)**2, (4, 0): S(1)/4 * sqrt(1/a**3) * exp(-r/(4*a)) * \ (1-3*r/(4*a)+S(1)/8 * (r/a)**2-S(1)/192 * (r/a)**3), (4, 1): S(1)/16 * sqrt(5/(3*a**3)) * exp(-r/(4*a)) * \ (1-r/(4*a)+S(1)/80 * (r/a)**2) * (r/a), (4, 2): S(1)/64 * sqrt(1/(5*a**3)) * exp(-r/(4*a)) * \ (1-r/(12*a)) * (r/a)**2, (4, 3): S(1)/768 * sqrt(1/(35*a**3)) * exp(-r/(4*a)) * (r/a)**3, } for n, l in R: assert simplify(R_nl(n, l, r, Z) - R[(n, l)]) == 0 def test_norm(): # Maximum "n" which is tested: n_max = 2 # you can test any n and it works, but it's slow, so it's commented out: #n_max = 4 for n in range(n_max+1): for l in range(n): assert integrate(R_nl(n, l, r)**2 * r**2, (r, 0, oo)) == 1 def test_hydrogen_energies(): n = Symbol("n") assert E_nl(n, Z) == -Z**2/(2*n**2) assert E_nl(n) == -1/(2*n**2) assert E_nl(1, 47) == -S(47)**2/(2*1**2) assert E_nl(2, 47) == -S(47)**2/(2*2**2) assert E_nl(1) == -S(1)/(2*1**2) assert E_nl(2) == -S(1)/(2*2**2) assert E_nl(3) == -S(1)/(2*3**2) assert E_nl(4) == -S(1)/(2*4**2) assert E_nl(100) == -S(1)/(2*100**2) raises(ValueError, "E_nl(0)") def test_hydrogen_energies_relat(): # First test exact formulas for small "c" so that we get nice expressions: assert E_nl_dirac(2, 0, Z=1, c=1) == 1/sqrt(2) - 1 assert simplify(E_nl_dirac(2, 0, Z=1, c=2) - ( (8*sqrt(3) + 16) \ / sqrt(16*sqrt(3) + 32) - 4)) == 0 assert simplify(E_nl_dirac(2, 0, Z=1, c=3) - ( (54*sqrt(2) + 81) \ / sqrt(108*sqrt(2) + 162) - 9)) == 0 # Now test for almost the correct speed of light, without floating point # numbers: assert simplify(E_nl_dirac(2, 0, Z=1, c=137) - ( (352275361 + 10285412 * \ sqrt(1173)) / sqrt(704550722 + 20570824 * sqrt(1173)) - \ 18769)) == 0 assert simplify(E_nl_dirac(2, 0, Z=82, c=137) - ( (352275361 + \ 2571353*sqrt(12045)) / sqrt(704550722 + 5142706*sqrt(12045)) \ - 18769)) == 0 # Test using exact speed of light, and compare against the nonrelativistic # energies: for n in range(1, 5): for l in range(n): assert feq(E_nl_dirac(n, l), E_nl(n), 1e-5, 1e-5) if l > 0: assert feq(E_nl_dirac(n, l, False), E_nl(n), 1e-5, 1e-5) Z = 2 for n in range(1, 5): for l in range(n): assert feq(E_nl_dirac(n, l, Z=Z), E_nl(n, Z), 1e-4, 1e-4) if l > 0: assert feq(E_nl_dirac(n, l, False, Z), E_nl(n, Z), 1e-4, 1e-4) Z = 3 for n in range(1, 5): for l in range(n): assert feq(E_nl_dirac(n, l, Z=Z), E_nl(n, Z), 1e-3, 1e-3) if l > 0: assert feq(E_nl_dirac(n, l, False, Z), E_nl(n, Z), 1e-3, 1e-3) # Test the exceptions: raises(ValueError, "E_nl_dirac(0, 0)") raises(ValueError, "E_nl_dirac(1, -1)") raises(ValueError, "E_nl_dirac(1, 0, False)") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_units.py0000644000175000017500000000123412014170666026476 0ustar georgeskgeorgeskfrom sympy import Rational, Symbol, integrate, pprint from sympy.physics.units import m, s, day, km, foot, meter, au, \ speed_of_light, minute, joule def test_units(): assert (5*m/s * day) / km == 432 assert foot / meter == Rational('0.3048') # Light from the sun needs about 8.3 minutes to reach earth t = (1*au / speed_of_light).evalf() / minute assert abs(t - 8.31) < 0.1 assert (m**2)**Rational(1,2) == m assert (m**Rational(1,2))**2 == m t = Symbol('t') assert integrate(t*m/s,(t, 1*s, 5*s)) == 12*m*s assert (t * m/s).integrate((t, 1*s, 5*s)) == 12*m*s def test_issue_2466(): assert (m < s).is_Relational wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/tests/test_paulialgebra.py0000644000175000017500000000123612014170666027766 0ustar georgeskgeorgeskfrom sympy import I from sympy.physics.paulialgebra import Pauli def test_Pauli(): sigma1=Pauli(1) sigma2=Pauli(2) sigma3=Pauli(3) assert sigma1 == sigma1 assert sigma1 != sigma2 assert sigma1*sigma2 == I*sigma3 assert sigma3*sigma1 == I*sigma2 assert sigma2*sigma3 == I*sigma1 assert sigma1*sigma1 == 1 assert sigma2*sigma2 == 1 assert sigma3*sigma3 == 1 assert sigma1**0 == 1 assert sigma1**1 == sigma1 assert sigma1**2 == 1 assert sigma1**3 == sigma1 assert sigma1**4 == 1 assert sigma3**2 == 1 assert sigma1*2*sigma1 == 2 #assert sigma1*sigma3*sigma1 == -sigma3 XXX should work wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/__init__.py0000644000175000017500000000017412014170666024674 0ustar georgeskgeorgesk""" A module that helps solving problems in physics """ import units from matrices import mgamma, msigma, minkowski_tensor wxgeometrie-0.133.2.orig/wxgeometrie/sympy/physics/matrices.py0000644000175000017500000000404612014170666024746 0ustar georgeskgeorgesk"""Known matrices related to physics""" from sympy import Matrix, I def msigma(i): """Returns a Pauli matrix sigma_i. i=1,2,3 See also: http://en.wikipedia.org/wiki/Pauli_matrices """ if i==1: mat=( ( (0, 1), (1, 0) ) ) elif i==2: mat=( ( (0, -I), (I, 0) ) ) elif i==3: mat=( ( (1, 0), (0, -1) ) ) else: raise IndexError("Invalid Pauli index") return Matrix(mat) def mgamma(mu,lower=False): """Returns a Dirac gamma matrix gamma^mu in the standard (Dirac) representation. If you want gamma_mu, use gamma(mu, True). We use a convention: gamma^5 = I * gamma^0 * gamma^1 * gamma^2 * gamma^3 gamma_5 = I * gamma_0 * gamma_1 * gamma_2 * gamma_3 = - gamma^5 See also: http://en.wikipedia.org/wiki/Gamma_matrices """ if not mu in [0,1,2,3,5]: raise IndexError("Invalid Dirac index") if mu == 0: mat = ( (1,0,0,0), (0,1,0,0), (0,0,-1,0), (0,0,0,-1) ) elif mu == 1: mat = ( (0,0,0,1), (0,0,1,0), (0,-1,0,0), (-1,0,0,0) ) elif mu == 2: mat = ( (0,0,0,-I), (0,0,I,0), (0,I,0,0), (-I,0,0,0) ) elif mu == 3: mat = ( (0,0,1,0), (0,0,0,-1), (-1,0,0,0), (0,1,0,0) ) elif mu == 5: mat = ( (0,0,1,0), (0,0,0,1), (1,0,0,0), (0,1,0,0) ) m= Matrix(mat) if lower: if mu in [1,2,3,5]: m = - m return m #Minkowski tensor using the convention (+,-,-,-) used in the Quantum Field #Theory minkowski_tensor = Matrix( ( (1,0,0,0), (0,-1,0,0), (0,0,-1,0), (0,0,0,-1) )) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/README0000644000175000017500000000714112014170666021762 0ustar georgeskgeorgeskSymPy ===== A Python library for symbolic mathematics. http://sympy.org/ See the AUTHORS file for the list of authors. And many more people helped on the SymPy mailinglist, reported bugs, helped organize SymPy's participation in the Google Summer of Code, the Google Highly Open Participation Contest, wrote and blogged about SymPy... License: New BSD License (see the LICENSE file for details) covers all files in the sympy repository unless stated otherwise. 0. Download ----------- $ git clone git://github.com/sympy/sympy.git For other options (tarballs, debs, etc.), see the web page of SymPy. 1. Documentation and usage -------------------------- Everything is at: http://docs.sympy.org/ You can generate everything at the above site in your local copy of SymPy by: $ cd doc $ make html $ epiphany _build/html/index.html If you don't want to read that, here is a short usage: From this directory, start python and: >>> from sympy import Symbol, cos >>> x = Symbol('x') >>> e = 1/cos(x) >>> print e.series(x, 0, 10) 1 + (1/2)*x**2 + (5/24)*x**4 + (61/720)*x**6 + (277/8064)*x**8 + O(x**10) SymPy also comes with a console that is a simple wrapper around the classic python console (or ipython when available) that loads the sympy namespace and executes some common commands for you. To start it, issue: $ bin/isympy from this directory if SymPy is not installed or simply $ isympy if SymPy is installed somewhere in your PATH. 3. Tests -------- To execute all tests, run ./setup.py test in the current directory. For more fine-grained running of tests, use bin/test or respectively bin/doctest. 4. Clean -------- To clean everything (thus getting the same tree as in the repository): ./setup.py clean 5. Brief History ---------------- SymPy was started by Ondrej Certik in 2005, he wrote some code during the summer, then he wrote some more code during the summer 2006. In February 2007, Fabian Pedregosa joined the project and helped fixed many things, contributed documentation and made it alive again. 5 students (Mateusz Paprocki, Brian Jorgensen, Jason Gedge, Robert Schwarz and Chris Wu) improved SymPy incredibly during the summer 2007 as part of the Google Summer of Code. Pearu Peterson joined the development during the summer 2007 and he has made SymPy much more competitive by rewriting the core from scratch, that has made it from 10x to 100x faster. Jurjen N.E. Bos has contributed pretty printing and other patches. Fredrik Johansson has wrote mpmath and contributed a lot of patches. Since then, a lot more people have joined the development and some people have also left. You can see the full list in doc/src/aboutus.txt, or online at: http://docs.sympy.org/aboutus.html#sympy-development-team For people that don't want to be listed there, see the git history. 6. Citation ----------- To cite SymPy in publications use:: SymPy Development Team (2011). SymPy: Python library for symbolic mathematics URL http://www.sympy.org. A BibTeX entry for LaTeX users is:: @Manual{, title = {SymPy: Python library for symbolic mathematics}, author = {{SymPy Development Team}}, year = {2011}, url = {http://www.sympy.org}, } SymPy is BSD licensed, so you are free to use it whatever you like, be it academic, commercial, creating forks or derivatives, as long as you copy the BSD statement if you redistribute it (see the LICENSE file for details). That said, although not required by the SymPy license, if it is convenient for you, please cite SymPy when using it in your work and also consider contributing all your changes back, so that we can incorporate it and all of us will benefit in the end. wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/0000755000175000017500000000000012014170666022733 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/epathtools.py0000644000175000017500000002351112014170666025471 0ustar georgeskgeorgesk"""Tools for manipulation of expressions using paths. """ from sympy.core import Basic class EPath(object): """ Manipulate expressions using paths. EPath grammar in EBNF notation:: literal ::= /[A-Za-z_][A-Za-z_0-9]*/ number ::= /-?\d+/ type ::= literal attribute ::= literal "?" all ::= "*" slice ::= "[" number? (":" number? (":" number?)?)? "]" range ::= all | slice query ::= (type | attribute) ("|" (type | attribute))* selector ::= range | query range? path ::= "/" selector ("/" selector)* See the docstring of the epath() function. """ __slots__ = ["_path", "_epath"] def __new__(cls, path): """Construct new EPath. """ if isinstance(path, EPath): return path if not path: raise ValueError("empty EPath") _path = path if path[0] == '/': path = path[1:] else: raise NotImplementedError("non-root EPath") epath = [] for selector in path.split('/'): selector = selector.strip() if not selector: raise ValueError("empty selector") index = 0 for c in selector: if c.isalnum() or c == '_' or c == '|' or c == '?': index += 1 else: break attrs = [] types = [] if index: elements = selector[:index] selector = selector[index:] for element in elements.split('|'): element = element.strip() if not element: raise ValueError("empty element") if element.endswith('?'): attrs.append(element[:-1]) else: types.append(element) span = None if selector == '*': pass else: if selector.startswith('['): try: i = selector.index(']') except ValueError: raise ValueError("expected ']', got EOL") _span, span = selector[1:i], [] if ':' not in _span: span = int(_span) else: for elt in _span.split(':', 3): if not elt: span.append(None) else: span.append(int(elt)) span = slice(*span) selector = selector[i+1:] if selector: raise ValueError("trailing characters in selector") epath.append((attrs, types, span)) obj = object.__new__(cls) obj._path = _path obj._epath = epath return obj def __repr__(self): return "%s(%r)" % (self.__class__.__name__, self._path) def _get_ordered_args(self, expr): """Sort ``expr.args`` using printing order. """ if expr.is_Add: return expr.as_ordered_terms() elif expr.is_Mul: return expr.as_ordered_factors() else: return expr.args def _hasattrs(self, expr, attrs): """Check if ``expr`` has any of ``attrs``. """ for attr in attrs: if not hasattr(expr, attr): return False return True def _hastypes(self, expr, types): """Check if ``expr`` is any of ``types``. """ _types = [ cls.__name__ for cls in expr.__class__.mro() ] return bool(set(_types).intersection(types)) def _has(self, expr, attrs, types): """Apply ``_hasattrs`` and ``_hastypes`` to ``expr``. """ if not (attrs or types): return True if attrs and self._hasattrs(expr, attrs): return True if types and self._hastypes(expr, types): return True return False def apply(self, expr, func, args=None, kwargs=None): """ Modify parts of an expression selected by a path. **Examples** >>> from sympy.simplify.epathtools import EPath >>> from sympy import sin, cos, E >>> from sympy.abc import x, y, z, t >>> path = EPath("/*/[0]/Symbol") >>> expr = [((x, 1), 2), ((3, y), z)] >>> path.apply(expr, lambda expr: expr**2) [((x**2, 1), 2), ((3, y**2), z)] >>> path = EPath("/*/*/Symbol") >>> expr = t + sin(x + 1) + cos(x + y + E) >>> path.apply(expr, lambda expr: 2*expr) t + sin(2*x + 1) + cos(2*x + 2*y + E) """ def _apply(path, expr, func): if not path: return func(expr) else: selector, path = path[0], path[1:] attrs, types, span = selector if isinstance(expr, Basic): if not expr.is_Atom: args, basic = self._get_ordered_args(expr), True else: return expr elif hasattr(expr, '__iter__'): args, basic = expr, False else: return expr args = list(args) if span is not None: if type(span) == slice: indices = range(*span.indices(len(args))) else: indices = [span] else: indices = xrange(len(args)) for i in indices: try: arg = args[i] except IndexError: continue if self._has(arg, attrs, types): args[i] = _apply(path, arg, func) if basic: return expr.func(*args) else: return expr.__class__(args) _args, _kwargs = args or (), kwargs or {} _func = lambda expr: func(expr, *_args, **_kwargs) return _apply(self._epath, expr, _func) def select(self, expr): """ Retrieve parts of an expression selected by a path. **Examples** >>> from sympy.simplify.epathtools import EPath >>> from sympy import sin, cos, E >>> from sympy.abc import x, y, z, t >>> path = EPath("/*/[0]/Symbol") >>> expr = [((x, 1), 2), ((3, y), z)] >>> path.select(expr) [x, y] >>> path = EPath("/*/*/Symbol") >>> expr = t + sin(x + 1) + cos(x + y + E) >>> path.select(expr) [x, x, y] """ result = [] def _select(path, expr): if not path: result.append(expr) else: selector, path = path[0], path[1:] attrs, types, span = selector if isinstance(expr, Basic): args = self._get_ordered_args(expr) elif hasattr(expr, '__iter__'): args = expr else: return if span is not None: if type(span) == slice: args = args[span] else: try: args = [args[span]] except IndexError: return for arg in args: if self._has(arg, attrs, types): _select(path, arg) _select(self._epath, expr) return result def epath(path, expr=None, func=None, args=None, kwargs=None): """ Manipulate parts of an expression selected by a path. This function allows to manipulate large nested expressions in single line of code, utilizing techniques to those applied in XML processing standards (e.g. XPath). If ``func`` is ``None``, :func:`epath` retrieves elements selected by the ``path``. Otherwise it applies ``func`` to each matching element. Note that it is more efficient to create an EPath object and use the select and apply methods of that object, since this will compile the path string only once. This function should only be used as a convenient shortcut for interactive use. **Syntax** select all : "/*" Equivalent of ``for arg in args:``. select slice : "/[0]" | "/[1:5]" | "/[1:5:2]" Supports standard Python's slice syntax. select by type : "/list" | "/list|tuple" Emulates :func:`isinstance`. select by attribute : "/__iter__?" Emulates :func:`hasattr`. **Parameters** path : str | EPath A path as a string or a compiled EPath. expr : Basic | iterable An expression or a container of expressions. func : callable (optional) A callable that will be applied to matching parts. args : tuple (optional) Additional positional arguments to ``func``. kwargs : dict (optional) Additional keyword arguments to ``func``. **Examples** >>> from sympy.simplify.epathtools import epath >>> from sympy import sin, cos, E >>> from sympy.abc import x, y, z, t >>> path = "/*/[0]/Symbol" >>> expr = [((x, 1), 2), ((3, y), z)] >>> epath(path, expr) [x, y] >>> epath(path, expr, lambda expr: expr**2) [((x**2, 1), 2), ((3, y**2), z)] >>> path = "/*/*/Symbol" >>> expr = t + sin(x + 1) + cos(x + y + E) >>> epath(path, expr) [x, x, y] >>> epath(path, expr, lambda expr: 2*expr) t + sin(2*x + 1) + cos(2*x + 2*y + E) """ _epath = EPath(path) if expr is None: return _epath if func is None: return _epath.select(expr) else: return _epath.apply(expr, func, args, kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/cse_main.py0000644000175000017500000002175612014170666025076 0ustar georgeskgeorgesk""" Tools for doing common subexpression elimination. """ import bisect import difflib from sympy import Basic, Mul, Add from sympy.utilities.iterables import preorder_traversal, numbered_symbols import cse_opts # (preprocessor, postprocessor) pairs which are commonly useful. They should # each take a sympy expression and return a possibly transformed expression. # When used in the function ``cse()``, the target expressions will be transformed # by each of the preprocessor functions in order. After the common # subexpressions are eliminated, each resulting expression will have the # postprocessor functions transform them in *reverse* order in order to undo the # transformation if necessary. This allows the algorithm to operate on # a representation of the expressions that allows for more optimization # opportunities. # ``None`` can be used to specify no transformation for either the preprocessor or # postprocessor. cse_optimizations = list(cse_opts.default_optimizations) def preprocess_for_cse(expr, optimizations): """ Preprocess an expression to optimize for common subexpression elimination. Parameters ---------- expr : sympy expression The target expression to optimize. optimizations : list of (callable, callable) pairs The (preprocessor, postprocessor) pairs. Returns ------- expr : sympy expression The transformed expression. """ for pre, post in optimizations: if pre is not None: expr = pre(expr) return expr def postprocess_for_cse(expr, optimizations): """ Postprocess an expression after common subexpression elimination to return the expression to canonical sympy form. Parameters ---------- expr : sympy expression The target expression to transform. optimizations : list of (callable, callable) pairs, optional The (preprocessor, postprocessor) pairs. The postprocessors will be applied in reversed order to undo the effects of the preprocessors correctly. Returns ------- expr : sympy expression The transformed expression. """ if optimizations is None: optimizations = cse_optimizations for pre, post in reversed(optimizations): if post is not None: expr = post(expr) return expr def cse(exprs, symbols=None, optimizations=None): """ Perform common subexpression elimination on an expression. Parameters: exprs : list of sympy expressions, or a single sympy expression The expressions to reduce. symbols : infinite iterator yielding unique Symbols The symbols used to label the common subexpressions which are pulled out. The ``numbered_symbols`` generator is useful. The default is a stream of symbols of the form "x0", "x1", etc. This must be an infinite iterator. optimizations : list of (callable, callable) pairs, optional The (preprocessor, postprocessor) pairs. If not provided, ``sympy.simplify.cse.cse_optimizations`` is used. Returns: replacements : list of (Symbol, expression) pairs All of the common subexpressions that were replaced. Subexpressions earlier in this list might show up in subexpressions later in this list. reduced_exprs : list of sympy expressions The reduced expressions with all of the replacements above. """ if symbols is None: symbols = numbered_symbols() else: # In case we get passed an iterable with an __iter__ method instead of # an actual iterator. symbols = iter(symbols) seen_subexp = set() muls = set() adds = set() to_eliminate = [] to_eliminate_ops_count = [] if optimizations is None: # Pull out the default here just in case there are some weird # manipulations of the module-level list in some other thread. optimizations = list(cse_optimizations) # Handle the case if just one expression was passed. if isinstance(exprs, Basic): exprs = [exprs] # Preprocess the expressions to give us better optimization opportunities. exprs = [preprocess_for_cse(e, optimizations) for e in exprs] # Find all of the repeated subexpressions. def insert(subtree): '''This helper will insert the subtree into to_eliminate while maintaining the ordering by op count and will skip the insertion if subtree is already present.''' ops_count = subtree.count_ops() index_to_insert = bisect.bisect(to_eliminate_ops_count, ops_count) # all i up to this index have op count <= the current op count # so check that subtree is not yet present from this index down # (if necessary) to zero. for i in xrange(index_to_insert - 1, -1, -1): if to_eliminate_ops_count[i] == ops_count and \ subtree == to_eliminate[i]: return # already have it to_eliminate_ops_count.insert(index_to_insert, ops_count) to_eliminate.insert(index_to_insert, subtree) for expr in exprs: pt = preorder_traversal(expr) for subtree in pt: if subtree.is_Atom: # Exclude atoms, since there is no point in renaming them. continue if subtree in seen_subexp: insert(subtree) pt.skip() continue if subtree.is_Mul: muls.add(subtree) elif subtree.is_Add: adds.add(subtree) seen_subexp.add(subtree) # process adds - any adds that weren't repeated might contain # subpatterns that are repeated, e.g. x+y+z and x+y have x+y in common adds = [set(a.args) for a in adds] for i in xrange(len(adds)): for j in xrange(i + 1, len(adds)): com = adds[i].intersection(adds[j]) if len(com) > 1: insert(Add(*com)) # remove this set of symbols so it doesn't appear again adds[i] = adds[i].difference(com) adds[j] = adds[j].difference(com) for k in xrange(j + 1, len(adds)): if not com.difference(adds[k]): adds[k] = adds[k].difference(com) # process muls - any muls that weren't repeated might contain # subpatterns that are repeated, e.g. x*y*z and x*y have x*y in common # use SequenceMatcher on the nc part to find the longest common expression # in common between the two nc parts sm = difflib.SequenceMatcher() muls = [a.args_cnc() for a in muls] for i in xrange(len(muls)): if muls[i][1]: sm.set_seq1(muls[i][1]) for j in xrange(i + 1, len(muls)): # the commutative part in common ccom = muls[i][0].intersection(muls[j][0]) # the non-commutative part in common if muls[i][1] and muls[j][1]: # see if there is any chance of an nc match ncom = set(muls[i][1]).intersection(set(muls[j][1])) if len(ccom) + len(ncom) < 2: continue # now work harder to find the match sm.set_seq2(muls[j][1]) i1, _, n = sm.find_longest_match(0, len(muls[i][1]), 0, len(muls[j][1])) ncom = muls[i][1][i1:i1 + n] else: ncom = [] com = list(ccom) + ncom if len(com) < 2: continue insert(Mul(*com)) # remove ccom from all if there was no ncom; to update the nc part # would require finding the subexpr and then replacing it with a # dummy to keep bounding nc symbols from being identified as a # subexpr, e.g. removing B*C from A*B*C*D might allow A*D to be # identified as a subexpr which would not be right. if not ncom: muls[i][0] = muls[i][0].difference(ccom) for k in xrange(j, len(muls)): if not ccom.difference(muls[k][0]): muls[k][0] = muls[k][0].difference(ccom) # Substitute symbols for all of the repeated subexpressions. replacements = [] reduced_exprs = list(exprs) for i, subtree in enumerate(to_eliminate): sym = symbols.next() replacements.append((sym, subtree)) # Make the substitution in all of the target expressions. for j, expr in enumerate(reduced_exprs): reduced_exprs[j] = expr.subs(subtree, sym) # Make the substitution in all of the subsequent substitutions. for j in range(i+1, len(to_eliminate)): to_eliminate[j] = to_eliminate[j].subs(subtree, sym) # Postprocess the expressions to return the expressions to canonical form. for i, (sym, subtree) in enumerate(replacements): subtree = postprocess_for_cse(subtree, optimizations) replacements[i] = (sym, subtree) reduced_exprs = [postprocess_for_cse(e, optimizations) for e in reduced_exprs] return replacements, reduced_exprs wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/cse_opts.py0000644000175000017500000000424612014170666025132 0ustar georgeskgeorgesk""" Optimizations of the expression tree representation for better CSE opportunities. """ from sympy import Add, Mul from sympy.core.operations import AssocOp from sympy.utilities.iterables import preorder_traversal def assumed(e, name): """ Return True if the given assumption is true about the sympy expression. Examples -------- >>> from sympy import symbols >>> from sympy.simplify.cse_opts import assumed >>> from sympy.abc import x, y >>> assumed(x+y, 'is_Add') True >>> assumed(x+y, 'is_Mul') False """ return getattr(e, name, False) class Sub(AssocOp): """ Stub of a Sub operator to replace Add(x, Mul(NegativeOne(-1), y)). """ __slots__ = [] is_Add = False is_Sub = True def _eval_subs(self, old, new): if self == old: return new else: return self.__class__(*[s._eval_subs(old, new) for s in self.args ]) def sub_pre(e): """ Replace Add(x, Mul(NegativeOne(-1), y)) with Sub(x, y). """ replacements = [] for node in preorder_traversal(e): if assumed(node, 'is_Add'): positives = [] negatives = [] for arg in node.args: if assumed(arg, 'is_Mul'): a, b = arg.as_two_terms() if (assumed(a, 'is_number') and assumed(a, 'is_negative')): negatives.append(Mul(-a, b)) continue positives.append(arg) if len(negatives) > 0: replacement = Sub(Add(*positives), Add(*negatives)) replacements.append((node, replacement)) for node, replacement in replacements: e = e.subs(node, replacement) return e def sub_post(e): """ Replace Sub(x,y) with the canonical form Add(x, Mul(NegativeOne(-1), y)). """ replacements = [] for node in preorder_traversal(e): if assumed(node, 'is_Sub'): replacements.append((node, Add(node.args[0], Mul(-1, node.args[1])))) for node, replacement in replacements: e = e.subs(node, replacement) return e default_optimizations = [ (sub_pre, sub_post), ] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/traversaltools.py0000644000175000017500000000157212014170666026376 0ustar georgeskgeorgesk"""Tools for applying functions to specified parts of expressions. """ from sympy.core import sympify def use(expr, func, level=0, args=(), kwargs={}): """ Use ``func`` to transform ``expr`` at the given level. Example ======= >>> from sympy import use, expand >>> from sympy.abc import x, y >>> f = (x + y)**2*x + 1 >>> use(f, expand, level=2) x*(x**2 + 2*x*y + y**2) + 1 >>> expand(f) x**3 + 2*x**2*y + x*y**2 + 1 """ def _use(expr, level): if not level: return func(expr, *args, **kwargs) else: if expr.is_Atom: return expr else: level -= 1 _args = [] for arg in expr.args: _args.append(_use(arg, level)) return expr.__class__(*_args) return _use(sympify(expr), level) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/simplify.py0000644000175000017500000017671212014170666025157 0ustar georgeskgeorgeskfrom sympy import SYMPY_DEBUG from sympy.core import (Basic, S, C, Add, Mul, Pow, Rational, Integer, Derivative, Wild, Symbol, sympify, expand, expand_mul, expand_func, Function, Equality, Dummy, Atom, count_ops) from sympy.core.compatibility import iterable from sympy.core.numbers import igcd from sympy.core.function import expand_log from sympy.utilities import flatten from sympy.functions import gamma, exp, sqrt, log from sympy.simplify.cse_main import cse from sympy.polys import (Poly, together, reduced, cancel, factor, ComputationFailed, terms_gcd) from sympy.core.compatibility import reduce import sympy.mpmath as mpmath def fraction(expr, exact=False): """Returns a pair with expression's numerator and denominator. If the given expression is not a fraction then this function will return the tuple (expr, 1). This function will not make any attempt to simplify nested fractions or to do any term rewriting at all. If only one of the numerator/denominator pair is needed then use numer(expr) or denom(expr) functions respectively. >>> from sympy import fraction, Rational, Symbol >>> from sympy.abc import x, y >>> fraction(x/y) (x, y) >>> fraction(x) (x, 1) >>> fraction(1/y**2) (1, y**2) >>> fraction(x*y/2) (x*y, 2) >>> fraction(Rational(1, 2)) (1, 2) This function will also work fine with assumptions: >>> k = Symbol('k', negative=True) >>> fraction(x * y**k) (x, y**(-k)) If we know nothing about sign of some exponent and 'exact' flag is unset, then structure this exponent's structure will be analyzed and pretty fraction will be returned: >>> from sympy import exp >>> fraction(2*x**(-y)) (2, x**y) >>> fraction(exp(-x)) (1, exp(x)) >>> fraction(exp(-x), exact=True) (exp(-x), 1) """ expr = sympify(expr) numer, denom = [], [] for term in Mul.make_args(expr): if term.is_Pow or term.func is exp: b, ex = term.as_base_exp() if ex.is_negative: if ex is S.NegativeOne: denom.append(b) else: denom.append(Pow(b, -ex)) elif not exact and ex.is_Mul: n, d = term.as_numer_denom() numer.append(n) denom.append(d) else: numer.append(term) elif term.is_Rational: n, d = term.as_numer_denom() numer.append(n) denom.append(d) else: numer.append(term) return Mul(*numer), Mul(*denom) def numer(expr): return fraction(expr)[0] def denom(expr): return fraction(expr)[1] def fraction_expand(expr): a, b = fraction(expr) return a.expand() / b.expand() def numer_expand(expr): a, b = fraction(expr) return a.expand() / b def denom_expand(expr): a, b = fraction(expr) return a / b.expand() def separate(expr, deep=False, force=False): """A wrapper to expand(power_base=True) which separates a power with a base that is a Mul into a product of powers, without performing any other expansions, provided that assumptions about the power's base and exponent allow. deep=True (default is False) will do separations inside functions. force=True (default is False) will cause the expansion to ignore assumptions about the base and exponent. When False, the expansion will only happen if the base is non-negative or the exponent is an integer. >>> from sympy.abc import x, y, z >>> from sympy import separate, sin, cos, exp >>> (x*y)**2 x**2*y**2 >>> (2*x)**y (2*x)**y >>> separate(_) 2**y*x**y >>> separate((x*y)**z) (x*y)**z >>> separate((x*y)**z, force=True) x**z*y**z >>> separate(sin((x*y)**z)) sin((x*y)**z) >>> separate(sin((x*y)**z), deep=True, force=True) sin(x**z*y**z) >>> separate((2*sin(x))**y + (2*cos(x))**y) 2**y*sin(x)**y + 2**y*cos(x)**y >>> separate((2*exp(y))**x) 2**x*exp(x*y) >>> separate((2*cos(x))**y) 2**y*cos(x)**y Notice that summations are left untouched. If this is not the desired behavior, apply 'expand' to the expression: >>> separate(((x+y)*z)**2) z**2*(x + y)**2 >>> (((x+y)*z)**2).expand() x**2*z**2 + 2*x*y*z**2 + y**2*z**2 >>> separate((2*y)**(1+z)) 2**(z + 1)*y**(z + 1) >>> ((2*y)**(1+z)).expand() 2*2**z*y*y**z """ return sympify(expr).expand(deep=deep, mul=False, power_exp=False,\ power_base=True, basic=False, multinomial=False, log=False, force=force) def collect(expr, syms, evaluate=True, exact=False): """ Collect additive terms with respect to a list of symbols up to powers with rational exponents. By the term symbol here are meant arbitrary expressions, which can contain powers, products, sums etc. In other words symbol is a pattern which will be searched for in the expression's terms. This function will not apply any redundant expanding to the input expression, so user is assumed to enter expression in final form. This makes 'collect' more predictable as there is no magic behind the scenes. However it is important to note, that powers of products are converted to products of powers using 'separate' function. There are two possible types of output. First, if 'evaluate' flag is set, this function will return a single expression or else it will return a dictionary with separated symbols up to rational powers as keys and collected sub-expressions as values respectively. >>> from sympy import collect, sympify, Wild >>> from sympy.abc import a, b, c, x, y, z This function can collect symbolic coefficients in polynomial or rational expressions. It will manage to find all integer or rational powers of collection variable: >>> collect(a*x**2 + b*x**2 + a*x - b*x + c, x) c + x**2*(a + b) + x*(a - b) The same result can be achieved in dictionary form: >>> d = collect(a*x**2 + b*x**2 + a*x - b*x + c, x, evaluate=False) >>> d[x**2] a + b >>> d[x] a - b >>> d[sympify(1)] c You can also work with multi-variate polynomials. However remember that this function is greedy so it will care only about a single symbol at time, in specification order: >>> collect(x**2 + y*x**2 + x*y + y + a*y, [x, y]) x**2*(y + 1) + x*y + y*(a + 1) Also more complicated expressions can be used as patterns: >>> from sympy import sin, log >>> collect(a*sin(2*x) + b*sin(2*x), sin(2*x)) (a + b)*sin(2*x) >>> collect(a*x*log(x) + b*(x*log(x)), x*log(x)) x*(a + b)*log(x) You can use wildcards in the pattern >>> w = Wild('w1') >>> collect(a*x**y - b*x**y, w**y) x**y*(a - b) It is also possible to work with symbolic powers, although it has more complicated behavior, because in this case power's base and symbolic part of the exponent are treated as a single symbol: >>> collect(a*x**c + b*x**c, x) a*x**c + b*x**c >>> collect(a*x**c + b*x**c, x**c) x**c*(a + b) However if you incorporate rationals to the exponents, then you will get well known behavior: >>> collect(a*x**(2*c) + b*x**(2*c), x**c) (a + b)*(x**2)**c Note also that all previously stated facts about 'collect' function apply to the exponential function, so you can get: >>> from sympy import exp >>> collect(a*exp(2*x) + b*exp(2*x), exp(x)) (a + b)*exp(2*x) If you are interested only in collecting specific powers of some symbols then set 'exact' flag in arguments: >>> collect(a*x**7 + b*x**7, x, exact=True) a*x**7 + b*x**7 >>> collect(a*x**7 + b*x**7, x**7, exact=True) x**7*(a + b) You can also apply this function to differential equations, where derivatives of arbitrary order can be collected. Note that if you collect with respect to a function or a derivative of a function, all derivatives of that function will also be collected. Use exact=True to prevent this from happening: >>> from sympy import Derivative as D, collect, Function >>> f = Function('f') (x) >>> collect(a*D(f,x) + b*D(f,x), D(f,x)) (a + b)*Derivative(f(x), x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), f) (a + b)*Derivative(f(x), x, x) >>> collect(a*D(D(f,x),x) + b*D(D(f,x),x), D(f,x), exact=True) a*Derivative(f(x), x, x) + b*Derivative(f(x), x, x) >>> collect(a*D(f,x) + b*D(f,x) + a*f + b*f, f,x) (a + b)*f(x) + (a + b)*Derivative(f(x), x) Or you can even match both derivative order and exponent at the same time. >>> collect(a*D(D(f,x),x)**2 + b*D(D(f,x),x)**2, D(f,x)) (a + b)*Derivative(f(x), x, x)**2 Note: arguments are expected to be in expanded form, so you might have to call expand() prior to calling this function. """ def make_expression(terms): product = [] for term, rat, sym, deriv in terms: if deriv is not None: var, order = deriv while order > 0: term, order = Derivative(term, var), order-1 if sym is None: if rat is S.One: product.append(term) else: product.append(Pow(term, rat)) else: product.append(Pow(term, rat*sym)) return Mul(*product) def parse_derivative(deriv): # scan derivatives tower in the input expression and return # underlying function and maximal differentiation order expr, sym, order = deriv.expr, deriv.variables[0], 1 for s in deriv.variables[1:]: if s == sym: order += 1 else: raise NotImplementedError('Improve MV Derivative support in collect') while isinstance(expr, Derivative): s0 = expr.variables[0] for s in expr.variables: if s != s0: raise NotImplementedError('Improve MV Derivative support in collect') if s0 == sym: expr, order = expr.expr, order+len(expr.variables) else: break return expr, (sym, Rational(order)) def parse_term(expr): """Parses expression expr and outputs tuple (sexpr, rat_expo, sym_expo, deriv) where: - sexpr is the base expression - rat_expo is the rational exponent that sexpr is raised to - sym_expo is the symbolic exponent that sexpr is raised to - deriv contains the derivatives the the expression for example, the output of x would be (x, 1, None, None) the output of 2**x would be (2, 1, x, None) """ rat_expo, sym_expo = S.One, None sexpr, deriv = expr, None if expr.is_Pow: if isinstance(expr.base, Derivative): sexpr, deriv = parse_derivative(expr.base) else: sexpr = expr.base if expr.exp.is_Rational: rat_expo = expr.exp elif expr.exp.is_Mul: coeff, tail = expr.exp.as_coeff_mul() if coeff.is_Rational: rat_expo, sym_expo = coeff, expr.exp._new_rawargs(*tail) else: sym_expo = expr.exp else: sym_expo = expr.exp elif expr.func is C.exp: arg = expr.args[0] if arg.is_Rational: sexpr, rat_expo = S.Exp1, arg elif arg.is_Mul: coeff, tail = arg.as_coeff_mul() if coeff.is_Rational: sexpr, rat_expo = C.exp(arg._new_rawargs(*tail)), coeff elif isinstance(expr, Derivative): sexpr, deriv = parse_derivative(expr) return sexpr, rat_expo, sym_expo, deriv def parse_expression(terms, pattern): """Parse terms searching for a pattern. terms is a list of tuples as returned by parse_terms; pattern is an expression treated as a product of factors """ pattern = Mul.make_args(pattern) if len(terms) < len(pattern): # pattern is longer than matched product # so no chance for positive parsing result return None else: pattern = [parse_term(elem) for elem in pattern] terms = terms[:] # need a copy elems, common_expo, has_deriv = [], None, False for elem, e_rat, e_sym, e_ord in pattern: if elem.is_Number: # a constant is a match for everything continue for j in range(len(terms)): if terms[j] is None: continue term, t_rat, t_sym, t_ord = terms[j] # keeping track of whether one of the terms had # a derivative or not as this will require rebuilding # the expression later if t_ord is not None: has_deriv= True if (term.match(elem) is not None and \ (t_sym == e_sym or t_sym is not None and \ e_sym is not None and \ t_sym.match(e_sym) is not None)): if exact == False: # we don't have to be exact so find common exponent # for both expression's term and pattern's element expo = t_rat / e_rat if common_expo is None: # first time common_expo = expo else: # common exponent was negotiated before so # there is no chance for a pattern match unless # common and current exponents are equal if common_expo != expo: common_expo = 1 else: # we ought to be exact so all fields of # interest must match in every details if e_rat != t_rat or e_ord != t_ord: continue # found common term so remove it from the expression # and try to match next element in the pattern elems.append(terms[j]) terms[j] = None break else: # pattern element not found return None return filter(None, terms), elems, common_expo, has_deriv if evaluate: if expr.is_Mul: ret = 1 for term in expr.args: ret *= collect(term, syms, True, exact) return ret elif expr.is_Pow: b = collect(expr.base, syms, True, exact) return Pow(b, expr.exp) summa = [separate(i) for i in Add.make_args(sympify(expr))] if hasattr(syms, '__iter__') or hasattr(syms, '__getitem__'): syms = [separate(s) for s in syms] else: syms = [separate(syms)] collected, disliked = {}, S.Zero for product in summa: terms = [parse_term(i) for i in Mul.make_args(product)] for symbol in syms: if SYMPY_DEBUG: print "DEBUG: parsing of expression %s with symbol %s " % (str(terms), str(symbol)) result = parse_expression(terms, symbol) if SYMPY_DEBUG: print "DEBUG: returned %s" % str(result) if result is not None: terms, elems, common_expo, has_deriv = result # when there was derivative in current pattern we # will need to rebuild its expression from scratch if not has_deriv: index = 1 for elem in elems: index *= Pow(elem[0], elem[1]) if elem[2] is not None: index **= elem[2] else: index = make_expression(elems) terms = separate(make_expression(terms)) index = separate(index) if index in collected.keys(): collected[index] += terms else: collected[index] = terms break else: # none of the patterns matched disliked += product if disliked is not S.Zero: collected[S.One] = disliked if evaluate: return Add(*[a*b for a, b in collected.iteritems()]) else: return collected def rcollect(expr, *vars): """ Recursively collect sums in an expression. Example ======= >>> from sympy.simplify import rcollect >>> from sympy.abc import x, y >>> expr = (x**2*y + x*y + x + y)/(x + y) >>> rcollect(expr, y) (x + y*(x**2 + x + 1))/(x + y) """ if expr.is_Atom or not expr.has(*vars): return expr else: expr = expr.__class__(*[ rcollect(arg, *vars) for arg in expr.args ]) if expr.is_Add: return collect(expr, vars) else: return expr def separatevars(expr, symbols=[], dict=False, force=False): """ Separates variables in an expression, if possible. By default, it separates with respect to all symbols in an expression and collects constant coefficients that are independent of symbols. If dict=True then the separated terms will be returned in a dictionary keyed to their corresponding symbols. By default, all symbols in the expression will appear as keys; if symbols are provided, then all those symbols will be used as keys, and any terms in the expression containing other symbols or non-symbols will be returned keyed to the string 'coeff'. If force=True, then power bases will only be separated if assumptions allow. Note: the order of the factors is determined by Mul, so that the separated expressions may not necessarily be grouped together. Examples: >>> from sympy.abc import x, y, z, alpha >>> from sympy import separatevars, sin >>> separatevars((x*y)**y) (x*y)**y >>> separatevars((x*y)**y, force=True) x**y*y**y >>> separatevars(2*x**2*z*sin(y)+2*z*x**2) 2*x**2*z*(sin(y) + 1) >>> separatevars(2*x+y*sin(x)) 2*x + y*sin(x) >>> separatevars(2*x**2*z*sin(y)+2*z*x**2, symbols=(x, y), dict=True) {'coeff': 2*z, x: x**2, y: sin(y) + 1} >>> separatevars(2*x**2*z*sin(y)+2*z*x**2, [x, y, alpha], dict=True) {'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1} If the expression is not really separable, or is only partially separable, separatevars will do the best it can to separate it. >>> separatevars(x+x*y-3*(x**2)) -x*(3*x - y - 1) If the expression is not separable then expr is returned unchanged or (if dict=True) then None is returned. >>> eq = 2*x+y*sin(x) >>> separatevars(eq) == eq True >>> separatevars(2*x+y*sin(x), symbols=(x, y), dict=True) == None True """ if dict: return _separatevars_dict(_separatevars(expr, force), *symbols) else: return _separatevars(expr, force) def _separatevars(expr, force): # get a Pow ready for expansion if expr.is_Pow: expr = Pow(separatevars(expr.base, force=force), expr.exp) # First try other expansion methods expr = expr.expand(mul=False, multinomial=False, force=force) _expr = expr.expand(power_exp=False, deep=False, force=force) if not force: # factor will expand bases so we mask them off now pows = [p for p in _expr.atoms(Pow) if p.base.is_Mul] dums = [Dummy(str(i)) for i in xrange(len(pows))] _expr = _expr.subs(dict(zip(pows, dums))) _expr = factor(_expr, expand=False) if not force: # and retore them _expr = _expr.subs(dict(zip(dums, pows))) if not _expr.is_Add: expr = _expr if expr.is_Add: nonsepar = sympify(0) # Find any common coefficients to pull out commoncsetlist = [] for i in expr.args: if i.is_Mul: commoncsetlist.append(set(i.args)) else: commoncsetlist.append(set((i,))) commoncset = set(flatten(commoncsetlist)) commonc = sympify(1) for i in commoncsetlist: commoncset = commoncset.intersection(i) commonc = Mul(*commoncset) for i in expr.args: coe = i.extract_multiplicatively(commonc) if coe == None: nonsepar += sympify(1) else: nonsepar += coe if nonsepar == 0: return commonc else: return commonc*nonsepar else: return expr def _separatevars_dict(expr, *symbols): if symbols: assert all((t.is_Atom for t in symbols)), "symbols must be Atoms." ret = dict(((i, S.One) for i in symbols + ('coeff',))) for i in Mul.make_args(expr): expsym = i.free_symbols intersection = set(symbols).intersection(expsym) if len(intersection) > 1: return None if len(intersection) == 0: # There are no symbols, so it is part of the coefficient ret['coeff'] *= i else: ret[intersection.pop()] *= i return ret def ratsimp(expr): """Put an expression over a common denominator, cancel and reduce. == Examples == >>> from sympy import ratsimp >>> from sympy.abc import x, y >>> ratsimp(1/x + 1/y) (x + y)/(x*y) """ f, g = cancel(expr).as_numer_denom() try: Q, r = reduced(f, [g], field=True, expand=False) except ComputationFailed: return f/g return Add(*Q) + cancel(r/g) def trigsimp(expr, deep=False, recursive=False): """ == Usage == trigsimp(expr) -> reduces expression by using known trig identities == Notes == deep: - Apply trigsimp inside functions recursive: - Use common subexpression elimination (cse()) and apply trigsimp recursively (recursively==True is quite expensive operation if the expression is large) == Examples == >>> from sympy import trigsimp, sin, cos, log >>> from sympy.abc import x, y >>> e = 2*sin(x)**2 + 2*cos(x)**2 >>> trigsimp(e) 2 >>> trigsimp(log(e)) log(2*sin(x)**2 + 2*cos(x)**2) >>> trigsimp(log(e), deep=True) log(2) """ sin, cos, tan, cot = C.sin, C.cos, C.tan, C.cot if not expr.has(sin, cos, tan, cot): return expr if recursive: w, g = cse(expr) g = trigsimp_nonrecursive(g[0]) for sub in reversed(w): g = g.subs(sub[0], sub[1]) g = trigsimp_nonrecursive(g) result = g else: result = trigsimp_nonrecursive(expr, deep) return result def trigsimp_nonrecursive(expr, deep=False): """ A nonrecursive trig simplifier, used from trigsimp. == Usage == trigsimp_nonrecursive(expr) -> reduces expression by using known trig identities == Notes == deep ........ apply trigsimp inside functions == Examples == >>> from sympy import cos, sin, log >>> from sympy.simplify.simplify import trigsimp, trigsimp_nonrecursive >>> from sympy.abc import x, y >>> e = 2*sin(x)**2 + 2*cos(x)**2 >>> trigsimp(e) 2 >>> trigsimp_nonrecursive(log(e)) log(2*sin(x)**2 + 2*cos(x)**2) >>> trigsimp_nonrecursive(log(e), deep=True) log(2) """ sin, cos, tan, cot = C.sin, C.cos, C.tan, C.cot if expr.is_Function: if deep: return expr.func(trigsimp_nonrecursive(expr.args[0], deep)) elif expr.is_Mul: # do some simplifications like sin/cos -> tan: a,b,c = map(Wild, 'abc') matchers = ( (a*sin(b)**c/cos(b)**c, a*tan(b)**c), (a*tan(b)**c*cos(b)**c, a*sin(b)**c), (a*cot(b)**c*sin(b)**c, a*cos(b)**c), (a*tan(b)**c/sin(b)**c, a/cos(b)**c), (a*cot(b)**c/cos(b)**c, a/sin(b)**c), ) for pattern, simp in matchers: res = expr.match(pattern) if res is not None: # if c is missing or zero, do nothing: if (not c in res) or res[c] == 0: continue # if "a" contains any of sin("b"), cos("b"), tan("b") or cot("b), # skip the simplification: if res[a].has(cos(res[b]), sin(res[b]), tan(res[b]), cot(res[b])): continue # simplify and finish: expr = simp.subs(res) break if not expr.is_Mul: return trigsimp_nonrecursive(expr, deep) ret = S.One for x in expr.args: ret *= trigsimp_nonrecursive(x, deep) return ret elif expr.is_Pow: return Pow(trigsimp_nonrecursive(expr.base, deep), trigsimp_nonrecursive(expr.exp, deep)) elif expr.is_Add: # TODO this needs to be faster # The types of trig functions we are looking for a,b,c = map(Wild, 'abc') matchers = ( (a*sin(b)**2, a - a*cos(b)**2), (a*tan(b)**2, a*(1/cos(b))**2 - a), (a*cot(b)**2, a*(1/sin(b))**2 - a) ) # Scan for the terms we need ret = S.Zero for term in expr.args: term = trigsimp_nonrecursive(term, deep) res = None for pattern, result in matchers: res = term.match(pattern) if res is not None: ret += result.subs(res) break if res is None: ret += term # Reduce any lingering artifacts, such as sin(x)**2 changing # to 1-cos(x)**2 when sin(x)**2 was "simpler" artifacts = ( (a - a*cos(b)**2 + c, a*sin(b)**2 + c, cos), (a - a*(1/cos(b))**2 + c, -a*tan(b)**2 + c, cos), (a - a*(1/sin(b))**2 + c, -a*cot(b)**2 + c, sin) ) expr = ret for pattern, result, ex in artifacts: # Substitute a new wild that excludes some function(s) # to help influence a better match. This is because # sometimes, for example, 'a' would match sec(x)**2 a_t = Wild('a', exclude=[ex]) pattern = pattern.subs(a, a_t) result = result.subs(a, a_t) if expr.is_number: continue m = expr.match(pattern) while m is not None: if m[a_t] == 0 or -m[a_t] in m[c].args or m[a_t] + m[c] == 0: break expr = result.subs(m) m = expr.match(pattern) return expr return expr def radsimp(expr): """ Rationalize the denominator. Examples: >>> from sympy import radsimp, sqrt, Symbol >>> radsimp(1/(2+sqrt(2))) -2**(1/2)/2 + 1 >>> x,y = map(Symbol, 'xy') >>> e = ((2+2*sqrt(2))*x+(2+sqrt(8))*y)/(2+sqrt(2)) >>> radsimp(e) 2**(1/2)*x + 2**(1/2)*y """ n,d = fraction(expr) a,b,c = map(Wild, 'abc') r = d.match(a+b*sqrt(c)) if r is not None: a = r[a] if r[b] == 0: b,c = 0,0 else: b,c = r[b],r[c] syms = list(n.atoms(Symbol)) n = collect((n*(a-b*sqrt(c))).expand(), syms) d = a**2 - c*b**2 return n/d def posify(eq): """Return eq (with generic symbols made positive) and a restore dictionary. Any symbol that has positive=None will be replaced with a positive dummy symbol having the same name. This replacement will allow more symbolic processing of expressions, especially those involving powers and logarithms. A dictionary that can be sent to subs to restore eq to its original symbols is also returned. >>> from sympy import posify, Symbol, log >>> from sympy.abc import x >>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True)) (_x + n + p, {_x: x}) >> log(1/x).expand() # should be log(1/x) but it comes back as -log(x) log(1/x) >>> log(posify(1/x)[0]).expand() # take [0] and ignore replacements -log(_x) >>> eq, rep = posify(1/x) >>> log(eq).expand().subs(rep) -log(x) >>> posify([x, 1 + x]) ([_x, _x + 1], {_x: x}) """ eq = sympify(eq) if iterable(eq): f = type(eq) eq = list(eq) syms = set() for e in eq: syms = syms.union(e.atoms(C.Symbol)) reps = {} for s in syms: reps.update(dict((v, k) for k, v in posify(s)[1].items())) for i, e in enumerate(eq): eq[i] = e.subs(reps) return f(eq), dict([(r,s) for s, r in reps.iteritems()]) reps = dict([(s, Dummy(s.name, positive=True)) for s in eq.atoms(Symbol) if s.is_positive is None]) eq = eq.subs(reps) return eq, dict([(r,s) for s, r in reps.iteritems()]) def powdenest(eq, force=False): """ Collect exponents on powers as assumptions allow. Given (bb**be)**e, this can be simplified as follows: o if bb is positive or e is an integer, bb**(be*e) o if be has an integer in the denominatory, then all integers from its numerator can be joined with e Given a product of powers raised to a power, (bb1**be1 * bb2**be2...)**e, simplification can be done as follows: o if e is positive, the gcd of all bei can be joined with e; o all non-negative bb can be separated from those that are negative and their gcd can be joined with e; autosimplification already handles this separation. o integer factors from powers that have integers in the denominator of the exponent can be removed from any term and the gcd of such integers can be joined with e Setting ``force`` to True will make symbols that are not explicitly negative behave as though they are positive, resulting in more denesting. When there are sums of logs in exp() then a product of powers may be obtained e.g. exp(3*(log(a) + 2*log(b))) - > a**3*b**6. Examples: >>> from sympy.abc import a, b, x, y, z >>> from sympy import Symbol, exp, log, sqrt, symbols, powdenest >>> powdenest((x**(2*a/3))**(3*x)) (x**(a/3))**(6*x) >>> powdenest(exp(3*x*log(2))) 2**(3*x) Assumptions may prevent expansion: >> powdenest(sqrt(x**2)) # activate when log rules are fixed (x**2)**(1/2) >>> p = symbols('p', positive=True) >>> powdenest(sqrt(p**2)) p No other expansion is done. >>> i, j = symbols('i,j', integer=1) >>> powdenest((x**x)**(i + j)) # -X-> (x**x)**i*(x**x)**j x**(x*(i + j)) But exp() will be denested by moving all non-log terms outside of the function; this may result in the collapsing of the exp to a power with a different base: >>> powdenest(exp(3*y*log(x))) x**(3*y) >>> powdenest(exp(y*(log(a) + log(b)))) (a*b)**y >>> powdenest(exp(3*(log(a) + log(b)))) a**3*b**3 If assumptions allow, symbols can also be moved to the outermost exponent: >>> i = Symbol('i', integer=True) >>> p = Symbol('p', positive=True) >>> powdenest(((x**(2*i))**(3*y))**x) ((x**(2*i))**(3*y))**x >>> powdenest(((x**(2*i))**(3*y))**x, force=1) x**(6*i*x*y) >> powdenest(((p**(2*a))**(3*y))**x) # activate when log rules are fixed p**(6*a*x*y) >>> powdenest(((x**(2*a/3))**(3*y/i))**x) ((x**(a/3))**(y/i))**(6*x) >>> powdenest((x**(2*i)*y**(4*i))**z,1) (x*y**2)**(2*i*z) >>> n = Symbol('n', negative=1) >> powdenest((x**i)**y, force=1) # activate when log rules are fixed x**(i*y) >> powdenest((n**i)**x, force=1) # activate when log rules are fixed (n**i)**x """ if force: eq, rep = posify(eq) return powdenest(eq, force=0).subs(rep) eq = S(eq) if eq.is_Atom: return eq # handle everything that is not a power # if subs would work then one could replace the following with # return eq.subs(dict([(p, powdenest(p)) for p in eq.atoms(Pow)])) # but subs expands (3**x)**2 to 3**x * 3**x so the 3**(5*x) # is not recognized; in addition, that would take 2 passes through # the expression (once to find Pows and again to replace them). The # following does it in one pass. Which is more important, efficiency # or simplicity? On the other hand, this only does a shallow replacement # and doesn't enter Integrals or functions, etc... so perhaps the subs # approach (or adding a deep flag) is the thing to do. if not eq.is_Pow and not eq.func is exp: args = list(Add.make_args(eq)) rebuild = False for i, arg in enumerate(args): margs = list(Mul.make_args(arg)) changed = False for j, m in enumerate(margs): if not m.is_Pow: continue m = powdenest(m, force=force) if m != margs[j]: changed = True margs[j] = m if changed: rebuild = True args[i] = C.Mul(*margs) if rebuild: eq = eq.func(*args) return eq b, e = eq.as_base_exp() # denest exp with log terms in exponent if b is S.Exp1 and e.is_Mul: logs = [] other = [] efunc = C.Mul for ei in Mul.make_args(e): if any(aj.func is C.log for a in Mul.make_args(ei) for ai in Add.make_args(a) for aj in Mul.make_args(ai)): logs.append(ei) else: other.append(ei) logs = logcombine(efunc(*logs), force=force) return Pow(C.exp(logs), efunc(*other)) bb, be = b.as_base_exp() if be is S.One and not (b.is_Mul or b.is_Rational): return eq # denest eq which is either Pow**e or Mul**e if force or e.is_integer: # replace all non-explicitly negative symbols with positive dummies syms = eq.atoms(Symbol) rep = [(s, C.Dummy(s.name, positive=True)) for s in syms if not s.is_negative] sub = eq.subs(rep) else: rep = [] sub = eq # if any factor is a bare symbol then there is nothing to be done b, e = sub.as_base_exp() if e is S.One or any(s.is_Symbol for s in Mul.make_args(b)): return sub.subs([(new, old) for old, new in rep]) # let log handle the case of the base of the argument being a mul, e.g. # sqrt(x**(2*i)*y**(6*i)) -> x**i*y**(3**i) gcd = terms_gcd(log(b).expand(log=1)) if gcd.func is C.log or not gcd.is_Mul: if hasattr(gcd.args[0], 'exp'): gcd = powdenest(gcd.args[0]) c, _ = gcd.exp.as_coeff_mul() ok = c.p != 1 if ok: ok = c.q != 1 if not ok: n, d = gcd.exp.as_numer_denom() ok = d is not S.One and any(di.is_integer for di in Mul.make_args(d)) if ok: return Pow(Pow(gcd.base, gcd.exp/c.p), c.p*e) elif e.is_Mul: return Pow(b, e).subs([(new, old) for old, new in rep]) return eq else: add= [] other = [] for g in gcd.args: if g.is_Add: add.append(g) else: other.append(g) return powdenest(Pow(exp(logcombine(Mul(*add))), e*Mul(*other))).subs([(new, old) for old, new in rep]) def powsimp(expr, deep=False, combine='all', force=False): """ == Usage == powsimp(expr, deep) -> reduces expression by combining powers with similar bases and exponents. == Notes == If deep is True then powsimp() will also simplify arguments of functions. By default deep is set to False. If force is True then bases will be combined without checking for assumptions, e.g. sqrt(x)*sqrt(y) -> sqrt(x*y) which is not true if x and y are both negative. You can make powsimp() only combine bases or only combine exponents by changing combine='base' or combine='exp'. By default, combine='all', which does both. combine='base' will only combine:: a a a 2x x x * y => (x*y) as well as things like 2 => 4 and combine='exp' will only combine :: a b (a + b) x * x => x combine='exp' will strictly only combine exponents in the way that used to be automatic. Also use deep=True if you need the old behavior. When combine='all', 'exp' is evaluated first. Consider the first example below for when there could be an ambiguity relating to this. This is done so things like the second example can be completely combined. If you want 'base' combined first, do something like powsimp(powsimp(expr, combine='base'), combine='exp'). == Examples == >>> from sympy import powsimp, exp, log, symbols >>> from sympy.abc import x, y, z, n >>> powsimp(x**y*x**z*y**z, combine='all') x**(y + z)*y**z >>> powsimp(x**y*x**z*y**z, combine='exp') x**(y + z)*y**z >>> powsimp(x**y*x**z*y**z, combine='base', force=True) x**y*(x*y)**z >>> powsimp(x**z*x**y*n**z*n**y, combine='all', force=True) (n*x)**(y + z) >>> powsimp(x**z*x**y*n**z*n**y, combine='exp') n**(y + z)*x**(y + z) >>> powsimp(x**z*x**y*n**z*n**y, combine='base', force=True) (n*x)**y*(n*x)**z >>> x, y = symbols('x y', positive=True) >>> powsimp(log(exp(x)*exp(y))) log(exp(x)*exp(y)) >>> powsimp(log(exp(x)*exp(y)), deep=True) x + y """ if combine not in ['all', 'exp', 'base']: raise ValueError("combine must be one of ('all', 'exp', 'base').") y = Dummy('y') if expr.is_Pow: if deep: return powsimp(y*powsimp(expr.base, deep, combine, force)**powsimp(\ expr.exp, deep, combine, force), deep, combine, force)/y else: return powsimp(y*expr, deep, combine, force)/y # Trick it into being a Mul elif expr.is_Function: if expr.func is exp and deep: # Exp should really be like Pow return powsimp(y*exp(powsimp(expr.args[0], deep, combine, force)), deep, combine, force)/y elif expr.func is exp and not deep: return powsimp(y*expr, deep, combine, force)/y elif deep: return expr.func(*[powsimp(t, deep, combine, force) for t in expr.args]) else: return expr elif expr.is_Add: return Add(*[powsimp(t, deep, combine, force) for t in expr.args]) elif expr.is_Mul: if combine in ('exp', 'all'): # Collect base/exp data, while maintaining order in the # non-commutative parts of the product if combine is 'all' and deep and any((t.is_Add for t in expr.args)): # Once we get to 'base', there is no more 'exp', so we need to # distribute here. return powsimp(expand_mul(expr, deep=False), deep, combine, force) c_powers = {} nc_part = [] newexpr = sympify(1) for term in expr.args: if term.is_Add and deep: newexpr *= powsimp(term, deep, combine, force) else: if term.is_commutative: b, e = term.as_base_exp() if deep: b, e = [powsimp(i, deep, combine, force) for i in [b, e]] c_powers.setdefault(b, []).append(e) else: # This is the logic that combines exponents for equal, # but non-commutative bases: A**x*A**y == A**(x+y). if nc_part: b1, e1 = nc_part[-1].as_base_exp() b2, e2 = term.as_base_exp() if (b1 == b2 and e1.is_commutative and e2.is_commutative): nc_part[-1] = Pow(b1, Add(e1, e2)) continue nc_part.append(term) # add up exponents of common bases for b, e in c_powers.iteritems(): c_powers[b] = Add(*e) # check for base and inverted base pairs be = c_powers.items() skip = set() # skip if we already saw them for b, e in be: if b in skip: continue bpos = b.is_positive if bpos: binv = 1/b if b != binv and binv in c_powers: if b.as_numer_denom()[0] is S.One: c_powers.pop(b) c_powers[binv] -= e else: skip.add(binv) e = c_powers.pop(binv) c_powers[b] -= e newexpr = Mul(*([newexpr] + [Pow(b, e) for b, e in c_powers.iteritems()])) if combine is 'exp': return Mul(newexpr, Mul(*nc_part)) else: # combine is 'all', get stuff ready for 'base' if deep: newexpr = expand_mul(newexpr, deep=False) if newexpr.is_Add: return powsimp(Mul(*nc_part), deep, combine='base', force=force) * \ Add(*[powsimp(i, deep, combine='base', force=force) for i in newexpr.args]) else: return powsimp(Mul(*nc_part), deep, combine='base', force=force)*\ powsimp(newexpr, deep, combine='base', force=force) else: # combine is 'base' if deep: expr = expand_mul(expr, deep=False) if expr.is_Add: return Add(*[powsimp(i, deep, combine, force) for i in expr.args]) else: # Build c_powers and nc_part. These must both be lists not # dicts because exp's are not combined. c_powers = [] nc_part = [] for term in expr.args: if term.is_commutative: c_powers.append(list(term.as_base_exp())) else: # This is the logic that combines bases that are # different and non-commutative, but with equal and # commutative exponents: A**x*B**x == (A*B)**x. if nc_part: b1, e1 = nc_part[-1].as_base_exp() b2, e2 = term.as_base_exp() if (e1 == e2 and e2.is_commutative): nc_part[-1] = Pow(Mul(b1, b2), e1) continue nc_part.append(term) # Pull out numerical coefficients from exponent if assumptions allow # e.g., 2**(2*x) => 4**x for i in xrange(len(c_powers)): b, e = c_powers[i] if not (b.is_nonnegative or e.is_integer or force): continue exp_c, exp_t = e.as_coeff_mul() if not (exp_c is S.One) and exp_t: c_powers[i] = [Pow(b, exp_c), e._new_rawargs(*exp_t)] # Combine bases whenever they have the same exponent and # assumptions allow # first gather the potential bases under the common exponent c_exp = {} for b, e in c_powers: if deep: e = powsimp(e, deep, combine, force) c_exp.setdefault(e, []).append(b) del c_powers # Merge back in the results of the above to form a new product c_powers = {} for e in c_exp: bases = c_exp[e] # calculate the new base for e if len(bases) == 1: new_base = bases[0] elif e.is_integer or force: new_base = Mul(*bases) else: # see which ones can be joined unk=[] nonneg=[] neg=[] for bi in bases: if not bi.is_negative is None: #then we know the sign if bi.is_negative: neg.append(bi) else: nonneg.append(bi) else: unk.append(bi) if len(unk) == 1 and not neg or len(neg) == 1 and not unk: # a single neg or a single unk can join the rest nonneg.extend(unk + neg) unk = neg = [] elif neg: # their negative signs cancel in pairs neg = [-w for w in neg] if len(neg) % 2: unk.append(S.NegativeOne) # these shouldn't be joined for b in unk: c_powers.setdefault(b, []).append(e) # here is a new joined base new_base = Mul(*(nonneg + neg)) c_powers.setdefault(new_base, []).append(e) # break out the powers from c_powers now c_part = [] if combine == 'all': #...joining the exponents for b, e in c_powers.iteritems(): c_part.append(Pow(b, Add(*e))) else: #...joining nothing for b, e in c_powers.iteritems(): for ei in e: c_part.append(Pow(b, ei)) # we're done return Mul(*(c_part + nc_part)) else: return expr def hypersimp(f, k): """Given combinatorial term f(k) simplify its consecutive term ratio i.e. f(k+1)/f(k). The input term can be composed of functions and integer sequences which have equivalent representation in terms of gamma special function. The algorithm performs three basic steps: (1) Rewrite all functions in terms of gamma, if possible. (2) Rewrite all occurrences of gamma in terms of products of gamma and rising factorial with integer, absolute constant exponent. (3) Perform simplification of nested fractions, powers and if the resulting expression is a quotient of polynomials, reduce their total degree. If f(k) is hypergeometric then as result we arrive with a quotient of polynomials of minimal degree. Otherwise None is returned. For more information on the implemented algorithm refer to: [1] W. Koepf, Algorithms for m-fold Hypergeometric Summation, Journal of Symbolic Computation (1995) 20, 399-417 """ f = sympify(f) g = f.subs(k, k+1) / f g = g.rewrite(gamma) g = expand_func(g) g = powsimp(g, deep=True, combine='exp') if g.is_rational_function(k): return simplify(g) else: return None def hypersimilar(f, g, k): """Returns True if 'f' and 'g' are hyper-similar. Similarity in hypergeometric sense means that a quotient of f(k) and g(k) is a rational function in k. This procedure is useful in solving recurrence relations. For more information see hypersimp(). """ f, g = map(sympify, (f, g)) h = (f/g).rewrite(gamma) h = h.expand(func=True, basic=False) return h.is_rational_function(k) def combsimp(expr): r""" Simplify combinatorial expressions. This function takes as input an expression containing factorials, binomials, Pochhammer symbol and other "combinatorial" functions, and tries to minimize the number of those functions and reduce the size of their arguments. The result is be given in terms of binomials and factorials. The algorithm works by rewriting all combinatorial functions as expressions involving rising factorials (Pochhammer symbols) and applies recurrence relations and other transformations applicable to rising factorials, to reduce their arguments, possibly letting the resulting rising factorial to cancel. Rising factorials with the second argument being an integer are expanded into polynomial forms and finally all other rising factorial are rewritten in terms more familiar binomials and factorials. All transformation rules can be found (or was derived from) here: 1. http://functions.wolfram.com/GammaBetaErf/Pochhammer/17/01/02/ 2. http://functions.wolfram.com/GammaBetaErf/Pochhammer/27/01/0005/ **Examples** >>> from sympy.simplify import combsimp >>> from sympy import factorial, binomial >>> from sympy.abc import n, k >>> combsimp(factorial(n)/factorial(n - 3)) n*(n - 2)*(n - 1) >>> combsimp(binomial(n+1, k+1)/binomial(n, k)) (n + 1)/(k + 1) """ factorial = C.factorial binomial = C.binomial gamma = C.gamma def as_coeff_Add(expr): if expr.is_Add: coeff, args = expr.args[0], expr.args[1:] if coeff.is_Number: if len(args) == 1: return coeff, args[0] else: return coeff, expr._new_rawargs(*args) return S.Zero, expr class rf(Function): @classmethod def eval(cls, a, b): if b.is_Integer: if not b: return S.Zero n, result = int(b), S.One if n > 0: for i in xrange(0, n): result *= a + i return result else: for i in xrange(1, -n+1): result *= a - i return 1/result else: c, _b = as_coeff_Add(b) if c.is_Integer: if c > 0: return rf(a, _b)*rf(a+_b, c) elif c < 0: return rf(a, _b)/rf(a+_b+c, -c) c, _a = as_coeff_Add(a) if c.is_Integer: if c > 0: return rf(_a, b)*rf(_a+b, c)/rf(_a, c) elif c < 0: return rf(_a, b)*rf(_a+c, -c)/rf(_a+b+c, -c) expr = expr.replace(binomial, lambda n, k: rf((n-k+1).expand(), k.expand())/rf(1, k.expand())) expr = expr.replace(factorial, lambda n: rf(1, n.expand())) expr = expr.replace(gamma, lambda n: rf(1, (n-1).expand())) expr = expr.replace(rf, lambda a, b: binomial(a+b-1, b)*factorial(b)) def rule(n, k): coeff, rewrite = S.One, False cn, _n = as_coeff_Add(n) ck, _k = as_coeff_Add(k) if cn.is_Integer and cn: coeff *= rf(_n + 1, cn)/rf(_n - k + 1, cn) rewrite = True n = _n if ck.is_Integer and ck: coeff *= rf(n - ck - _k + 1, ck)/rf(_k + 1, ck) rewrite = True k = _k if rewrite: return coeff*binomial(n, k) expr = expr.replace(binomial, rule) return factor(expr) def simplify(expr, ratio=1.7): """Naively simplifies the given expression. Simplification is not a well defined term and the exact strategies this function tries can change in the future versions of SymPy. If your algorithm relies on "simplification" (whatever it is), try to determine what you need exactly - is it powsimp()?, radsimp()?, together()?, logcombine()?, or something else? And use this particular function directly, because those are well defined and thus your algorithm will be robust. In some cases, applying :func:`simplify` may actually result in some more complicated expression. By default ``ratio=1.7`` prevents more extreme cases: if (result length)/(input length) > ratio, then input is returned unmodified (:func:`count_ops` is used to measure length). For example, if ``ratio=1``, ``simplify`` output can't be longer than input. :: >>> from sympy import S, simplify, count_ops, oo >>> root = S("(5/2 + 21**(1/2)/2)**(1/3)*(1/2 - I*3**(1/2)/2)" ... "+ 1/((1/2 - I*3**(1/2)/2)*(5/2 + 21**(1/2)/2)**(1/3))") Since ``simplify(root)`` would result in a slightly longer expression, root is returned inchanged instead:: >>> simplify(root, ratio=1) is root True If ``ratio=oo``, simplify will be applied anyway:: >>> count_ops(simplify(root, ratio=oo)) > count_ops(root) True Note that the shortest expression is not necessary the simplest, so setting ``ratio`` to 1 may not be a good idea. Heuristically, default value ``ratio=1.7`` seems like a reasonable choice. """ expr = sympify(expr) if not isinstance(expr, Basic): # XXX: temporary hack return expr if isinstance(expr, Atom): return expr if isinstance(expr, C.Relational): return expr.__class__(simplify(expr.lhs, ratio=ratio), simplify(expr.rhs, ratio=ratio)) # TODO: Apply different strategies, considering expression pattern: # is it a purely rational function? Is there any trigonometric function?... # See also https://github.com/sympy/sympy/pull/185. original_expr = expr if expr.is_commutative is False: return together(powsimp(expr)) expr = together(cancel(powsimp(expr)).expand()) if not isinstance(expr, Basic): # XXX: temporary hack return expr if expr.has(C.TrigonometricFunction): expr = trigsimp(expr) if expr.has(C.log): expr = min([expand_log(expr, deep=True), logcombine(expr)], key=count_ops) if expr.has(C.CombinatorialFunction, gamma): expr = combsimp(expr) expr = powsimp(expr, combine='exp', deep=True) numer, denom = expr.as_numer_denom() if denom.is_Add: a, b, c = map(Wild, 'abc') r = denom.match(a + b*c**S.Half) if r is not None and r[b]: a, b, c = r[a], r[b], r[c] numer *= a-b*c**S.Half numer = numer.expand() denom = a**2 - c*b**2 expr = numer/denom if expr.could_extract_minus_sign(): n, d = expr.as_numer_denom() if d != 0: expr = -n/(-d) if count_ops(expr) > ratio*count_ops(original_expr): return original_expr return expr def _real_to_rational(expr): """ Replace all reals in expr with rationals. >>> from sympy import nsimplify >>> from sympy.abc import x >>> nsimplify(.76 + .1*x**.5, rational=1) x**(1/2)/10 + 19/25 """ p = sympify(expr) for r in p.atoms(C.Float): newr = nsimplify(r) if not newr.is_Rational or \ r.is_finite and not newr.is_finite: newr = r if newr < 0: s = -1 newr *= s else: s = 1 d = Pow(10, int((mpmath.log(newr)/mpmath.log(10)))) newr = s*Rational(str(newr/d))*d p = p.subs(r, newr) return p def nsimplify(expr, constants=[], tolerance=None, full=False, rational=False): """ Replace numbers with simple representations. If rational=True then numbers are simply replaced with their rational equivalents. If rational=False, a simple formula that numerically matches the given expression is sought (and the input should be possible to evalf to a precision of at least 30 digits). Optionally, a list of (rationally independent) constants to include in the formula may be given. A lower tolerance may be set to find less exact matches. With full=True, a more extensive search is performed (this is useful to find simpler numbers when the tolerance is set low). Examples: >>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, exp, pi >>> nsimplify(4/(1+sqrt(5)), [GoldenRatio]) -2 + 2*GoldenRatio >>> nsimplify((1/(exp(3*pi*I/5)+1))) 1/2 - I*(5**(1/2)/10 + 1/4)**(1/2) >>> nsimplify(I**I, [pi]) exp(-pi/2) >>> nsimplify(pi, tolerance=0.01) 22/7 """ if rational: return _real_to_rational(expr) expr = sympify(expr) prec = 30 bprec = int(prec*3.33) constants_dict = {} for constant in constants: constant = sympify(constant) v = constant.evalf(prec) if not v.is_Float: raise ValueError("constants must be real-valued") constants_dict[str(constant)] = v._to_mpmath(bprec) exprval = expr.evalf(prec, chop=True) re, im = exprval.as_real_imag() # Must be numerical if not ((re.is_Float or re.is_Integer) and (im.is_Float or im.is_Integer)): return expr def nsimplify_real(x): orig = mpmath.mp.dps xv = x._to_mpmath(bprec) try: # We'll be happy with low precision if a simple fraction if not (tolerance or full): mpmath.mp.dps = 15 rat = mpmath.findpoly(xv, 1) if rat is not None: return Rational(-int(rat[1]), int(rat[0])) mpmath.mp.dps = prec newexpr = mpmath.identify(xv, constants=constants_dict, tol=tolerance, full=full) if not newexpr: raise ValueError if full: newexpr = newexpr[0] return sympify(newexpr) finally: mpmath.mp.dps = orig try: if re: re = nsimplify_real(re) if im: im = nsimplify_real(im) except ValueError: return expr return re + im*S.ImaginaryUnit def logcombine(expr, force=False): """ Takes logarithms and combines them using the following rules: - log(x)+log(y) == log(x*y) - a*log(x) == log(x**a) These identities are only valid if x and y are positive and if a is real, so the function will not combine the terms unless the arguments have the proper assumptions on them. Use logcombine(func, force=True) to automatically assume that the arguments of logs are positive and that coefficients are real. Note that this will not change any assumptions already in place, so if the coefficient is imaginary or the argument negative, combine will still not combine the equations. Change the assumptions on the variables to make them combine. Examples: >>> from sympy import Symbol, symbols, log, logcombine >>> from sympy.abc import a, x, y, z >>> logcombine(a*log(x)+log(y)-log(z)) a*log(x) + log(y) - log(z) >>> logcombine(a*log(x)+log(y)-log(z), force=True) log(x**a*y/z) >>> x,y,z = symbols('x,y,z', positive=True) >>> a = Symbol('a', real=True) >>> logcombine(a*log(x)+log(y)-log(z)) log(x**a*y/z) """ # Try to make (a+bi)*log(x) == a*log(x)+bi*log(x). This needs to be a # separate function call to avoid infinite recursion. expr = expand_mul(expr, deep=False) return _logcombine(expr, force) def _logcombine(expr, force=False): """ Does the main work for logcombine, it's a separate function to avoid an infinite recursion. See the docstrings of logcombine() for help. """ def _getlogargs(expr): """ Returns the arguments of the logarithm in an expression. Example: _getlogargs(a*log(x*y)) x*y """ if expr.func is log: return [expr.args[0]] else: args = [] for i in expr.args: if i.func is log: args.append(_getlogargs(i)) return flatten(args) return None if type(expr) in (int, float) or expr.is_Number or expr.is_Rational or \ expr.is_NumberSymbol or type(expr) == C.Integral: return expr if isinstance(expr, Equality): retval = Equality(_logcombine(expr.lhs-expr.rhs, force),\ Integer(0)) # If logcombine couldn't do much with the equality, try to make it like # it was. Hopefully extract_additively won't become smart enought to # take logs apart :) right = retval.lhs.extract_additively(expr.lhs) if right: return Equality(expr.lhs, _logcombine(-right, force)) else: return retval if expr.is_Add: argslist = 1 notlogs = 0 coeflogs = 0 for i in expr.args: if i.func is log: if (i.args[0].is_positive or (force and not \ i.args[0].is_nonpositive)): argslist *= _logcombine(i.args[0], force) else: notlogs += i elif i.is_Mul and any(map(lambda t: getattr(t,'func', False)==log,\ i.args)): largs = _getlogargs(i) assert len(largs) != 0 loglargs = 1 for j in largs: loglargs *= log(j) if all(getattr(t,'is_positive') for t in largs)\ and getattr(i.extract_multiplicatively(loglargs),'is_real', False)\ or (force\ and not all(getattr(t,'is_nonpositive') for t in largs)\ and not getattr(i.extract_multiplicatively(loglargs),\ 'is_real')==False): coeflogs += _logcombine(i, force) else: notlogs += i elif i.has(log): notlogs += _logcombine(i, force) else: notlogs += i if notlogs + log(argslist) + coeflogs == expr: return expr else: alllogs = _logcombine(log(argslist) + coeflogs, force) return notlogs + alllogs if expr.is_Mul: a = Wild('a') x = Wild('x') coef = expr.match(a*log(x)) if coef\ and (coef[a].is_real\ or expr.is_Number\ or expr.is_NumberSymbol\ or type(coef[a]) in (int, float)\ or (force\ and not coef[a].is_imaginary))\ and (coef[a].func != log\ or force\ or (not getattr(coef[a],'is_real')==False\ and getattr(x, 'is_positive'))): return log(coef[x]**coef[a]) else: return _logcombine(expr.args[0], force)*reduce(lambda x, y:\ _logcombine(x, force)*_logcombine(y, force),\ expr.args[1:], 1) if expr.is_Function: return expr.func(*map(lambda t: _logcombine(t, force), expr.args)) if expr.is_Pow: return _logcombine(expr.args[0], force)**\ _logcombine(expr.args[1], force) return expr wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/hyperexpand_doc.py0000644000175000017500000000062212014170666026461 0ustar georgeskgeorgesk""" This module cooks up a docstring when imported. Its only purpose is to be displayed in the sphinx documentation. """ from sympy.simplify.hyperexpand import FormulaCollection from sympy import latex, Eq, hyper c = FormulaCollection() doc = "" for f in c.formulae: obj = Eq(hyper(f.indices.ap, f.indices.bq, f.z), f.closed_form) doc += ".. math::\n %s\n" % latex(obj) __doc__ = doc wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/sqrtdenest.py0000644000175000017500000000704312014170666025505 0ustar georgeskgeorgeskfrom sympy.functions import sqrt, sign from sympy.core import S, Wild, Rational, sympify def sqrtdenest (expr): """ Denests an expression that contains nested square roots. This algorithm is based on . """ expr = sympify(expr) if expr.is_Pow and expr.exp is S.Half: #If expr is a square root return denester([expr])[0] return expr def denester (nested): """ Denests a list of expressions that contain nested square roots. This method should not be called directly - use 'denest' instead. This algorithm is based on . It is assumed that all of the elements of 'nested' share the same bottom-level radicand. (This is stated in the paper, on page 177, in the paragraph immediately preceding the algorithm.) When evaluating all of the arguments in parallel, the bottom-level radicand only needs to be denested once. This means that calling denester with x arguments results in a recursive invocation with x+1 arguments; hence denester has polynomial complexity. However, if the arguments were evaluated separately, each call would result in two recursive invocations, and the algorithm would have exponential complexity. This is discussed in the paper in the middle paragraph of page 179. """ if all((n**2).is_Number for n in nested): #If none of the arguments are nested for f in subsets(len(nested)): #Test subset 'f' of nested p = prod(nested[i]**2 for i in range(len(f)) if f[i]).expand() if 1 in f and f.count(1) > 1 and f[-1]: p = -p if sqrt(p).is_Number: return sqrt(p), f #If we got a perfect square, return its square root. return nested[-1], [0]*len(nested) #Otherwise, return the radicand from the previous invocation. else: a, b, r, R = Wild('a'), Wild('b'), Wild('r'), None values = [expr.match(sqrt(a + b * sqrt(r))) for expr in nested] for v in values: if r in v: #Since if b=0, r is not defined if R is not None: assert R == v[r] #All the 'r's should be the same. else: R = v[r] d, f = denester([sqrt((v[a]**2).expand()-(R*v[b]**2).expand()) for v in values] + [sqrt(R)]) if not any([f[i] for i in range(len(nested))]): #If f[i]=0 for all i < len(nested) v = values[-1] return sqrt(v[a] + v[b]*d), f else: v = prod(nested[i]**2 for i in range(len(nested)) if f[i]).expand().match(a+b*sqrt(r)) if 1 in f and f.index(1) < len(nested) - 1 and f[len(nested)-1]: v[a] = -1 * v[a] v[b] = -1 * v[b] if not f[len(nested)]: #Solution denests with square roots return (sqrt((v[a]+d).expand()/2)+sign(v[b])*sqrt((v[b]**2*R/(2*(v[a]+d))).expand())).expand(), f else: #Solution requires a fourth root FR, s = (R.expand()**Rational(1,4)), sqrt((v[b]*R).expand()+d) return (s/(sqrt(2)*FR) + v[a]*FR/(sqrt(2)*s)).expand(), f def subsets(n): """ Returns all possible subsets of the set (0, 1, ..., n-1) except the empty set. """ binary = lambda x: x>0 and binary(x>>1) + [x&1] or [] pad = lambda l: [0]*(n-len(l)) + l #Always returns a list of length 'n' return [pad(binary(i)) for i in range(1, 2**n)] def prod(n): """ Returns the product of all elements of n, as a Rational. """ product = S.One for i in n: product = product * i return product wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/hyperexpand.py0000644000175000017500000015002412014170666025636 0ustar georgeskgeorgesk""" Expand Hypergeometric (and Meijer G) functions into named special functions. The algorithm for doing this uses a collection of lookup tables of hypergeometric functions, and various of their properties, to expand many hypergeometric functions in terms of special functions. It is based on the following paper: Kelly B. Roach. Meijer G Function Representations. In: Proceedings of the 1997 International Symposium on Symbolic and Algebraic Computation, pages 205-211, New York, 1997. ACM. It is described in great(er) detail in the Sphinx documentation. """ from sympy.core import S, Dummy, symbols, sympify, Tuple, expand, I, Mul from sympy import SYMPY_DEBUG def add_formulae(formulae): """ Create our knowledge base. Leave this at the top for easy reference. """ z = Dummy('z') a, b, c = symbols('a b c', cls=Dummy) def add(ap, bq, res): formulae.append(Formula(ap, bq, z, res, (a, b, c))) def addb(ap, bq, B, C, M): formulae.append(Formula(ap, bq, z, None, (a, b, c), B, C, M)) from sympy.matrices import diag, Matrix # Luke, Y. L. (1969), The Special Functions and Their Approximations, # Volume 1, section 6.2 from sympy import (exp, sqrt, cosh, log, asin, atan, I, lowergamma, cos, atanh, besseli, gamma, erf, pi, sin, besselj) # 0F0 add((), (), exp(z)) # 1F0 add((-a, ), (), (1-z)**a) # 2F1 addb((a, a - S.Half), (2*a,), Matrix([2**(2*a-1)*(1 + sqrt(1-z))**(1-2*a), 2**(2*a-1)*(1 + sqrt(1-z))**(-2*a)]), Matrix([[1, 0]]), Matrix([[(a-S.Half)*z/(1-z), (S.Half-a)*z/(1-z)], [a/(1-z), a*(z-2)/(1-z)]])) addb((1, 1), (2,), Matrix([log(1 - z), 1]), Matrix([[-1/z, 0]]), Matrix([[0, z/(z - 1)], [0, 0]])) addb((S.Half, 1), (S('3/2'),), Matrix([log((1 + sqrt(z))/(1 - sqrt(z)))/sqrt(z), 1]), Matrix([[S(1)/2, 0]]), Matrix([[-S(1)/2, 1/(1 - z)], [0, 0]])) addb((S.Half, S.Half), (S('3/2'),), Matrix([asin(sqrt(z))/sqrt(z), 1/sqrt(1 - z)]), Matrix([[1, 0]]), Matrix([[-S(1)/2, S(1)/2], [0, z/(1 - z)/2]])) addb((-a, S.Half - a), (S.Half,), Matrix([(1 + sqrt(z))**(2*a) + (1 - sqrt(z))**(2*a), sqrt(z)*(1 + sqrt(z))**(2*a-1) - sqrt(z)*(1 - sqrt(z))**(2*a-1)]), Matrix([[S.Half, 0]]), Matrix([[0, a], [z*(2*a-1)/2/(1-z), S.Half - z*(2*a-1)/(1-z)]])) # A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990). # Integrals and Series: More Special Functions, Vol. 3,. # Gordon and Breach Science Publisher add([a, -a], [S.Half], cos(2*a*asin(sqrt(z)))) addb([1, 1], [3*S.Half], Matrix([asin(sqrt(z))/sqrt(z*(1-z)), 1]), Matrix([[1, 0]]), Matrix([[(z - S.Half)/(1 - z), 1/(1 - z)/2], [0, 0]])) # 3F2 addb([-S.Half, 1, 1], [S.Half, 2], Matrix([sqrt(z)*atanh(sqrt(z)), log(1 - z), 1]), Matrix([[-S(2)/3, -S(1)/(3*z), S(2)/3]]), Matrix([[S(1)/2, 0, z/(1 - z)/2], [0, 0, z/(z - 1)], [0, 0, 0]])) # actually the formula for 3/2 is much nicer ... addb([-S.Half, 1, 1], [2, 2], Matrix([sqrt(1 - z), log(sqrt(1 - z)/2 + S.Half), 1]), Matrix([[S(4)/9 - 16/(9*z), 4/(3*z), 16/(9*z)]]), Matrix([[z/2/(z - 1), 0, 0], [1/(2*(z - 1)), 0, S.Half], [0, 0, 0]])) # 1F1 addb([1], [b], Matrix([z**(1 - b) * exp(z) * lowergamma(b - 1, z), 1]), Matrix([[b - 1, 0]]),Matrix([[1 - b + z, 1], [0, 0]])) addb([a], [2*a], Matrix([z**(S.Half - a)*exp(z/2)*besseli(a - S.Half, z/2) * gamma(a + S.Half)/4**(S.Half - a), z**(S.Half - a)*exp(z/2)*besseli(a + S.Half, z/2) * gamma(a + S.Half)/4**(S.Half - a)]), Matrix([[1, 0]]), Matrix([[z/2, z/2], [z/2, (z/2 - 2*a)]])) add([-S.Half], [S.Half], exp(z) - sqrt(pi*z)*(-I)*erf(I*sqrt(z))) # 2F2 addb([S.Half, a], [S(3)/2, a + 1], Matrix([a/(2*a - 1)*(-I)*sqrt(pi/z)*erf(I*sqrt(z)), a/(2*a - 1)*(-z)**(-a)*lowergamma(a, -z), a/(2*a - 1)*exp(z)]), Matrix([[1, -1, 0]]), Matrix([[-S.Half, 0, 1], [0, -a, 1], [0, 0, z]])) # 0F1 add((), (S.Half,), cosh(2*sqrt(z))) addb([], [b], Matrix([gamma(b)*z**((1-b)/2)*besseli(b-1, 2*sqrt(z)), gamma(b)*z**(1 - b/2)*besseli(b , 2*sqrt(z))]), Matrix([[1, 0]]), Matrix([[0, 1], [z, (1-b)]])) # 0F3 x = 4*z**(S(1)/4) def fp(a,z): return besseli(a, x) + besselj(a, x) def fm(a,z): return besseli(a, x) - besselj(a, x) addb([], [S.Half, a, a+S.Half], Matrix([fp(2*a - 1, z), fm(2*a, z)*z**(S(1)/4), fm(2*a - 1, z)*z**(S(1)/2), fp(2*a, z)*z**(S(3)/4)]) * 2**(-2*a)*gamma(2*a)*z**((1-2*a)/4), Matrix([[1, 0, 0, 0]]), Matrix([[0, 1, 0, 0], [0, S(1)/2 - a, 1, 0], [0, 0, S(1)/2, 1], [z, 0, 0, 1 - a]])) x = 2*(-4*z)**(S(1)/4) addb([], [a, a + S.Half, 2*a], (2*sqrt(-z))**(1-2*a)*gamma(2*a)**2 * Matrix([besselj(2*a-1, x)*besseli(2*a-1, x), x*(besseli(2*a, x)*besselj(2*a-1, x) - besseli(2*a-1, x)*besselj(2*a, x)), x**2*besseli(2*a, x)*besselj(2*a, x), x**3*(besseli(2*a,x)*besselj(2*a-1,x) + besseli(2*a-1, x)*besselj(2*a, x))]), Matrix([[1, 0, 0, 0]]), Matrix([[0, S(1)/4, 0, 0], [0, (1-2*a)/2, -S(1)/2, 0], [0, 0, 1-2*a, S(1)/4], [-32*z, 0, 0, 1-a]])) # 1F2 addb([a], [a - S.Half, 2*a], Matrix([z**(S.Half - a)*besseli(a-S.Half, sqrt(z))**2, z**(1-a)*besseli(a-S.Half, sqrt(z)) *besseli(a-S(3)/2, sqrt(z)), z**(S(3)/2-a)*besseli(a-S(3)/2, sqrt(z))**2]), Matrix([[-gamma(a + S.Half)**2/4**(S.Half - a), 2*gamma(a - S.Half)*gamma(a + S.Half)/4**(1 - a), 0]]), Matrix([[1 - 2*a, 1, 0], [z/2, S.Half - a, S.Half], [0, z, 0]])) addb([S.Half], [b, 2 - b], pi*(1-b)/sin(pi*b) * Matrix([besseli(1-b, sqrt(z))*besseli(b-1, sqrt(z)), sqrt(z)*(besseli(-b, sqrt(z))*besseli(b-1, sqrt(z)) + besseli(1-b, sqrt(z))*besseli(b, sqrt(z))), besseli(-b, sqrt(z))*besseli(b, sqrt(z))]), Matrix([[1, 0, 0]]), Matrix([[b-1, S(1)/2, 0], [z, 0, z], [0, S(1)/2, -b]])) # 2F3 # XXX with this five-parameter formula is pretty slow with the current # Formula.find_instantiations (creates 2!*3!*3**(2+3) ~ 3000 # instantiations ... But it's not too bad. addb([a, a + S.Half], [2*a, b, 2*a - b + 1], gamma(b)*gamma(2*a - b + 1) * (sqrt(z)/2)**(1-2*a) * Matrix([besseli(b-1, sqrt(z))*besseli(2*a-b, sqrt(z)), sqrt(z)*besseli(b, sqrt(z))*besseli(2*a-b, sqrt(z)), sqrt(z)*besseli(b-1, sqrt(z))*besseli(2*a-b+1, sqrt(z)), besseli(b, sqrt(z))*besseli(2*a-b+1, sqrt(z))]), Matrix([[1, 0, 0, 0]]), Matrix([[0, S(1)/2, S(1)/2, 0], [z/2, 1-b, 0, z/2], [z/2, 0, b-2*a, z/2], [0, S(1)/2, S(1)/2, -2*a]])) def make_simp(z): """ Create a function that simplifies rational functions in `z`. """ def simp(expr): """ Efficiently simplify the rational function `expr`. """ from sympy import poly numer, denom = expr.as_numer_denom() c, numer, denom = poly(numer, z).cancel(poly(denom, z)) return c * numer.as_expr() / denom.as_expr() return simp def debug(*args): if SYMPY_DEBUG: for a in args: print a, print class Mod1(object): """ Represent an expression 'mod 1'. Beware: __eq__ and the hash are NOT compatible. (by design) This means that m1 == m2 does not imply hash(m1) == hash(m2). Code that creates Mod1 objects (like compute_buckets below) should be careful only to produce one instance of Mod1 for each class. """ # TODO this should be backported to any implementation of a Mod object # (c/f issue 2490) def __new__(cls, r): if r.is_Rational and not r.free_symbols: return r - r.p//r.q res = object.__new__(cls) res.expr = r return res def __repr__(self): return str(self.expr) + ' % 1' def __eq__(self, other): from sympy import simplify if not isinstance(other, Mod1): return False if simplify(self.expr - other.expr).is_integer is True: return True return False class IndexPair(object): """ Holds a pair of indices, and methods to compute their invariants. """ def __init__(self, ap, bq): from sympy import expand, Tuple self.ap = Tuple(*[expand(x) for x in sympify(ap)]) self.bq = Tuple(*[expand(x) for x in sympify(bq)]) @property def sizes(self): return (len(self.ap), len(self.bq)) def __str__(self): return 'IndexPair(%s, %s)' % (self.ap, self.bq) def compute_buckets(self, oabuckets=None, obbuckets=None): """ Partition parameters `ap`, `bq` into buckets, that is return two dicts abuckets, bbuckets such that every key in [ab]buckets is a rational in range [0, 1) and the corresponding items are items of ap/bq congruent to the key mod 1. If oabuckets, obbuckets is specified, try to use the same Mod1 objects for parameters where possible. >>> from sympy.simplify.hyperexpand import IndexPair >>> from sympy import S >>> ap = (S(1)/2, S(1)/3, S(-1)/2, -2) >>> bq = (1, 2) >>> IndexPair(ap, bq).compute_buckets() ({0: (-2,), 1/3: (1/3,), 1/2: (1/2, -1/2)}, {0: (1, 2)}) """ # TODO this should probably be cached somewhere abuckets = {} bbuckets = {} oaparametric = [] obparametric = [] if oabuckets is not None: for parametric, buckets in [(oaparametric, oabuckets), (obparametric, obbuckets)]: parametric += filter(lambda x: isinstance(x, Mod1), buckets.keys()) for params, bucket, oparametric in [(self.ap, abuckets, oaparametric), (self.bq, bbuckets, obparametric)]: parametric = [] for p in params: res = Mod1(p) if isinstance(res, Mod1): parametric.append(p) continue if res in bucket: bucket[res] += (p,) else: bucket[res] = (p,) while parametric: p0 = parametric[0] p0mod1 = Mod1(p0) if oparametric.count(p0mod1): i = oparametric.index(p0mod1) p0mod1 = oparametric.pop(i) bucket[p0mod1] = (p0,) pos = [] for po in parametric[1:]: if Mod1(po) == p0mod1: bucket[p0mod1] += (po,) else: pos.append(po) parametric = pos return abuckets, bbuckets def build_invariants(self): """ Compute the invariant vector of (`ap`, `bq`), that is: (gamma, ((s1, n1), ..., (sk, nk)), ((t1, m1), ..., (tr, mr))) where gamma is the number of integer a < 0, s1 < ... < sk nl is the number of parameters a_i congruent to sl mod 1 t1 < ... < tr ml is the number of parameters b_i congruent to tl mod 1 If the index pair contains parameters, then this is not truly an invariant, since the parameters cannot be sorted uniquely mod1. >>> from sympy.simplify.hyperexpand import IndexPair >>> from sympy import S >>> ap = (S(1)/2, S(1)/3, S(-1)/2, -2) >>> bq = (1, 2) Here gamma = 1, k = 3, s1 = 0, s2 = 1/3, s3 = 1/2 n1 = 1, n2 = 1, n2 = 2 r = 1, t1 = 0 m1 = 2: >>> IndexPair(ap, bq).build_invariants() (1, ((0, 1), (1/3, 1), (1/2, 2)), ((0, 2),)) """ abuckets, bbuckets = self.compute_buckets() gamma = 0 if S(0) in abuckets: gamma = len(filter(lambda x: x < 0, abuckets[S(0)])) def tr(bucket): bucket = bucket.items() if not any(isinstance(x[0], Mod1) for x in bucket): bucket.sort(key=lambda x: x[0]) bucket = tuple(map(lambda x: (x[0], len(x[1])), bucket)) return bucket return (gamma, tr(abuckets), tr(bbuckets)) def difficulty(self, ip): """ Estimate how many steps it takes to reach `ip` from self. Return -1 if impossible. """ oabuckets, obbuckets = self.compute_buckets() abuckets, bbuckets = ip.compute_buckets(oabuckets, obbuckets) gt0 = lambda x: (x > 0) is True if S(0) in abuckets and (not S(0) in oabuckets or len(filter(gt0, abuckets[S(0)])) != len(filter(gt0, oabuckets[S(0)]))): return -1 diff = 0 for bucket, obucket in [(abuckets, oabuckets), (bbuckets, obbuckets)]: for mod in set(bucket.keys() + obucket.keys()): if (not mod in bucket) or (not mod in obucket) \ or len(bucket[mod]) != len(obucket[mod]): return -1 l1 = list(bucket[mod]) l2 = list(obucket[mod]) l1.sort() l2.sort() for i, j in zip(l1, l2): diff += abs(i - j) return diff class IndexQuadruple(object): """ Holds a quadruple of indices. """ def __init__(self, an, ap, bm, bq): from sympy import expand, Tuple def tr(l): return Tuple(*[expand(x) for x in sympify(l)]) self.an = tr(an) self.ap = tr(ap) self.bm = tr(bm) self.bq = tr(bq) def compute_buckets(self): """ Compute buckets for the fours sets of parameters. We guarantee that any two equal Mod1 objects returned are actually the same, and that the buckets are sorted by real part (an and bq descendending, bm and ap ascending). >>> from sympy.simplify.hyperexpand import IndexQuadruple >>> from sympy.abc import y >>> from sympy import S >>> IndexQuadruple([1, 3, 2, S(3)/2], [1 + y, y, 2, y + 3], [2], [y]).compute_buckets() ({0: [3, 2, 1], 1/2: [3/2]}, {y + 1 % 1: [y, y + 1, y + 3], 0: [2]}, {0: [2]}, {y + 1 % 1: [y]}) """ mod1s = [] pan, pap, pbm, pbq = {}, {}, {}, {} for dic, lis in [(pan, self.an), (pap, self.ap), (pbm, self.bm), (pbq, self.bq)]: for x in lis: m = Mod1(x) if mod1s.count(m): i = mod1s.index(m) m = mod1s[i] else: mod1s.append(m) dic.setdefault(m, []).append(x) for dic, flip in [(pan, True), (pap, False), (pbm, False), (pbq, True)]: l = dic.items() dic.clear() for m, items in l: x0 = items[0] items.sort(key=lambda x: x-x0) if flip: items.reverse() dic[m] = items return pan, pap, pbm, pbq def __str__(self): return 'IndexQuadruple(%s, %s, %s, %s)' % (self.an, self.ap, self.bm, self.bq) # Dummy generator x = Dummy('x') class Formula(object): """ This class represents hypergeometric formulae. Its data members are: - z, the argument - closed_form, the closed form expression - symbols, the free symbols (parameters) in the formula - indices, the parameters - B, C, M (see _compute_basis) - lcms, a dictionary which maps symbol -> lcm of denominators - isolation, a dictonary which maps symbol -> (num, coeff) pairs >>> from sympy.abc import a, b, z >>> from sympy.simplify.hyperexpand import Formula >>> f = Formula((a/2, a/3 + b, (1+a)/2), (a, b, (a+b)/7), z, None, [a, b]) The lcm of all denominators of coefficients of a is 2*3*7 >>> f.lcms[a] 42 for b it is just 7: >>> f.lcms[b] 7 We can isolate a in the (1+a)/2 term, with denominator 2: >>> f.isolation[a] (2, 2, 1) b is isolated in the b term, with coefficient one: >>> f.isolation[b] (4, 1, 1) """ def _compute_basis(self, closed_form): """ Compute a set of functions B=(f1, ..., fn), a nxn matrix M and a 1xn matrix C such that: closed_form = C B z d/dz B = M B. """ from sympy.matrices import Matrix, eye, zeros afactors = map(lambda a: x + a, self.indices.ap) bfactors = map(lambda b: x + b - 1, self.indices.bq) expr = x*Mul(*bfactors) - self.z*Mul(*afactors) poly = Poly(expr, x) n = poly.degree() - 1 b = [closed_form] for _ in xrange(n): b.append(self.z*b[-1].diff(self.z)) self.B = Matrix(b) self.C = Matrix([[1] + [0]*n]) m = eye(n) m = m.col_insert(0, zeros((n, 1))) l = poly.all_coeffs()[1:] l.reverse() self.M = m.row_insert(n, -Matrix([l])/poly.all_coeffs()[0]) def __init__(self, ap, bq, z, res, symbols, B=None, C=None, M=None): ap = Tuple(*map(expand, sympify(ap))) bq = Tuple(*map(expand, sympify(bq))) z = sympify(z) res = sympify(res) symbols = filter(lambda x: ap.has(x) or bq.has(x), sympify(symbols)) self.z = z self.symbols = symbols self.B = B self.C = C self.M = M params = list(ap) + list(bq) lcms = {} isolation = {} for a in symbols: from sympy import ilcm l = 1 isolating = [] others = list(symbols[:]) others.remove(a) i = 0 for p in params: if p.has(a): c, m = None, None if p.is_Add: c, m = p.as_independent(a)[1].as_coeff_mul(a) else: c, m = p.as_coeff_mul(a) if m != (a,) or not c.is_Rational: raise NotImplementedError('?') l = ilcm(l, c.q) if not p.has(*others): isolating.append((i, c.q, c.p)) lcms[a] = l i += 1 if len(isolating) == 0: raise NotImplementedError('parameter is not isolated') isolating.sort(key=lambda x:x[1]) isolating.sort(key=lambda x:-x[2]) isolation[a] = isolating[-1] self.lcms = lcms self.isolation = isolation self.indices = IndexPair(ap, bq) # TODO with symbolic parameters, it could be advantageous # (for prettier answers) to compute a basis only *after* # instantiation if res is not None: self._compute_basis(res) @property def closed_form(self): return (self.C*self.B)[0] def find_instantiations(self, ip): """ Try to find instantiations of the free symbols that match `ip.ap`, `ip.bq`. Return the instantiated formulae as a list. Note that the returned instantiations need not actually match, or be valid! """ ap = ip.ap bq = ip.bq if len(ap) != len(self.indices.ap) or len(bq) != len(self.indices.bq): raise TypeError('Cannot instantiate other number of parameters') from sympy import solve from sympy.core.compatibility import permutations, product res = [] our_params = list(self.indices.ap) + list(self.indices.bq) for na in permutations(ap): for nb in permutations(bq): all_params = list(na) + list(nb) repl = {} for a in self.symbols: i, d, _ = self.isolation[a] repl[a] = (solve(our_params[i] - all_params[i], a)[0], d) for change in product(*[(-1, 0, 1)]*len(self.symbols)): rep = {} for i, a in zip(change, repl.keys()): rep[a] = repl[a][0] + i*repl[a][1] res.append(Formula(self.indices.ap.subs(rep), self.indices.bq.subs(rep), self.z, None, [], self.B.subs(rep), self.C.subs(rep), self.M.subs(rep))) # if say a = -1/2, and there is 2*a in the formula, then # there will be a negative integer. But this origin is also # reachable from a = 1/2 ... # So throw this in as well. # The code is not as general as it could be, but good enough. if len(self.symbols) == 1: a = self.symbols[0] aval, d = repl[a] if aval < 0 and d == 1: from sympy import ceiling aval -= ceiling(aval) - 1 res.append(Formula(self.indices.ap.subs(a, aval), self.indices.bq.subs(a, aval), self.z, None, [], self.B.subs(a, aval), self.C.subs(rep), self.M.subs(a, aval))) return res def is_suitable(self): """ Decide if `self` is a suitable origin. >>> from sympy.simplify.hyperexpand import Formula >>> from sympy import S If ai - bq in Z and bq >= ai this is fine: >>> Formula((S(1)/2,), (S(3)/2,), None, None, []).is_suitable() True but ai = bq is not: >>> Formula((S(1)/2,), (S(1)/2,), None, None, []).is_suitable() False and ai > bq is not either: >>> Formula((S(1)/2,), (-S(1)/2,), None, None, []).is_suitable() False None of the bj can be a non-positive integer: >>> Formula((S(1)/2,), (0,), None, None, []).is_suitable() False >>> Formula((S(1)/2,), (-1, 1,), None, None, []).is_suitable() False None of the ai can be zero: >>> Formula((S(1)/2, 0), (1,), None, None, []).is_suitable() False More complicated examples: >>> Formula((S(1)/2, 1), (2, -S(2)/3), None, None, []).is_suitable() True >>> Formula((S(1)/2, 1), (2, -S(2)/3, S(3)/2), None, None, []).is_suitable() True """ from sympy import oo, zoo if len(self.symbols) > 0: return None for a in self.indices.ap: for b in self.indices.bq: if (a-b).is_integer and not a < b: return False for a in self.indices.ap: if a == 0: return False for b in self.indices.bq: if b <= 0 and b.is_integer: return False for e in [self.B, self.M, self.C]: if e is None: continue if e.has(S.NaN) or e.has(oo) or e.has(-oo) or e.has(zoo): return False return True class FormulaCollection(object): """ A collection of formulae to use as origins. """ def __init__(self): """ Doing this globally at module init time is a pain ... """ self.symbolic_formulae = {} self.concrete_formulae = {} self.formulae = [] add_formulae(self.formulae) # Now process the formulae into a helpful form. # These dicts are indexed by (p, q). for f in self.formulae: sizes = f.indices.sizes if len(f.symbols) > 0: self.symbolic_formulae.setdefault(sizes, []).append(f) else: inv = f.indices.build_invariants() self.concrete_formulae.setdefault(sizes, {})[inv] = f def lookup_origin(self, ip): """ Given the suitable parameters `ip.ap`, `ip.bq`, try to find an origin in our knowledge base. >>> from sympy.simplify.hyperexpand import FormulaCollection, IndexPair >>> f = FormulaCollection() >>> f.lookup_origin(IndexPair((), ())).closed_form exp(_z) >>> f.lookup_origin(IndexPair([1], ())).closed_form 1/(-_z + 1) >>> from sympy import S >>> f.lookup_origin(IndexPair([S('1/4'), S('3/4 + 4')], [S.Half])).closed_form 1/(2*(_z**(1/2) + 1)**(17/2)) + 1/(2*(-_z**(1/2) + 1)**(17/2)) """ inv = ip.build_invariants() sizes = ip.sizes if sizes in self.concrete_formulae and \ inv in self.concrete_formulae[sizes]: return self.concrete_formulae[sizes][inv] # We don't have a concrete formula. Try to instantiate. if not sizes in self.symbolic_formulae: return None # Too bad... possible = [] for f in self.symbolic_formulae[sizes]: l = f.find_instantiations(ip) for f2 in l: if not f2.is_suitable(): continue diff = f2.indices.difficulty(ip) if diff != -1: possible.append((diff, f2)) if not possible: # Give up. return None # find the nearest origin possible.sort(key=lambda x:x[0]) return possible[0][1] class Operator(object): """ Base class for operators to be applied to our functions. These operators are differential operators. They are by convention expressed in the variable D = z*d/dz (although this base class does not actually care). Note that when the operator is applied to an object, we typically do *not* blindly differentiate but instead use a different representation of the z*d/dz operator (see make_derivative_operator). To subclass from this, define a __init__ method that initalises a self._poly variable. This variable stores a polynomial. By convention the generator is z*d/dz, and acts to the right of all coefficients. Thus this poly x**2 + 2*z*x + 1 represents the differential operator (z*d/dz)**2 + 2*z**2*d/dz. This class is used only in the implementation of the hypergeometric function expansion algorithm. """ def apply(self, obj, op): """ Apply `self` to the object `obj`, where the generator is given by `op`. >>> from sympy.simplify.hyperexpand import Operator >>> from sympy.polys.polytools import Poly >>> from sympy.abc import x, y, z >>> op = Operator() >>> op._poly = Poly(x**2 + z*x + y, x) >>> op.apply(z**7, lambda f: f.diff(z)) y*z**7 + 7*z**7 + 42*z**5 """ coeffs = self._poly.all_coeffs() coeffs.reverse() diffs = [obj] for c in coeffs[1:]: diffs.append(op(diffs[-1])) r = coeffs[0]*diffs[0] for c, d in zip(coeffs[1:], diffs[1:]): r += c*d return r class MultOperator(Operator): """ Simply multiply by a "constant" """ def __init__(self, p): self._poly = Poly(p, x) class ShiftA(Operator): """ Increment an upper index. """ def __init__(self, ai): ai = sympify(ai) if ai == 0: raise ValueError('Cannot increment zero upper index.') self._poly = Poly(x/ai + 1, x) def __str__(self): return '' % (1/self._poly.all_coeffs()[0]) class ShiftB(Operator): """ Decrement a lower index. """ def __init__(self, bi): bi = sympify(bi) if bi == 1: raise ValueError('Cannot decrement unit lower index.') self._poly = Poly(x/(bi - 1) + 1, x) def __str__(self): return '' % (1/self._poly.all_coeffs()[0] + 1) class UnShiftA(Operator): """ Decrement an upper index. """ def __init__(self, ap, bq, i, z): """ Note: i counts from zero! """ ap, bq, i = map(sympify, [ap, bq, i]) self._ap = ap self._bq = bq self._i = i ap = list(ap) bq = list(bq) ai = ap.pop(i) - 1 if ai == 0: raise ValueError('Cannot decrement unit upper index.') m = Poly(z*ai, x) for a in ap: m *= Poly(x + a, x) #print m A = Dummy('A') D = Poly(ai*A - ai, A) n = 1*D for b in bq: n *= (D + b - 1) #print n b0 = -n.all_coeffs()[-1] if b0 == 0: raise ValueError('Cannot decrement upper index: ' \ 'cancels with lower') #print b0 n = Poly(Poly(n.all_coeffs()[:-1], A).as_expr().subs(A, x/ai + 1), x) self._poly = Poly((n-m)/b0, x) def __str__(self): return '' % (self._i, self._ap, self._bq) class UnShiftB(Operator): """ Increment a lower index. """ def __init__(self, ap, bq, i, z): """ Note: i counts from zero! """ ap, bq, i = map(sympify, [ap, bq, i]) self._ap = ap self._bq = bq self._i = i ap = list(ap) bq = list(bq) bi = bq.pop(i) + 1 if bi == 0: raise ValueError('Cannot increment -1 lower index.') m = Poly(x*(bi-1), x) for b in bq: m *= Poly(x + b - 1, x) #print m B = Dummy('B') D = Poly((bi-1)*B - bi + 1, B) n = Poly(z, B) for a in ap: n *= (D + a) #print n b0 = n.all_coeffs()[-1] #print b0 if b0 == 0: raise ValueError('Cannot increment index: ' \ 'cancels with upper') #print b0 n = Poly(Poly(n.all_coeffs()[:-1], B).as_expr().subs(B, x/(bi-1) + 1), x) #print n self._poly = Poly((m-n)/b0, x) def __str__(self): return '' % (self._i, self._ap, self._bq) class ReduceOrder(Operator): """ Reduce Order by cancelling an upper and a lower index. """ def __new__(cls, ai, bj): """ For convenience if reduction is not possible, return None. """ ai = sympify(ai) bj = sympify(bj) n = ai - bj if n < 0 or not n.is_Integer: return None if bj.is_integer and bj <= 0 and bj + n - 1 >= 0: return None self = Operator.__new__(cls) p = S(1) for k in xrange(n): p *= (x + bj + k)/(bj + k) self._poly = Poly(p, x) self._a = ai self._b = bj return self @classmethod def _meijer(cls, b, a, sign): """ Cancel b + sign*s and a + sign*s This is for meijer G functions. """ from sympy import Add b = sympify(b) a = sympify(a) n = b - a if n < 0 or not n.is_Integer: return None self = Operator.__new__(cls) p = S(1) for k in xrange(n): p *= (sign*x + a + k) self._poly = Poly(p, x) if sign == -1: self._a = b self._b = a else: self._b = Add(1, a - 1, evaluate=False) self._a = Add(1, b - 1, evaluate=False) return self @classmethod def meijer_minus(cls, b, a): return cls._meijer(b, a, -1) @classmethod def meijer_plus(cls, a, b): return cls._meijer(1 - a, 1 - b, 1) def __str__(self): return '' % \ (self._a, self._b) def _reduce_order(ap, bq, gen, key): """ Order reduction algorithm common to both Hypergeometric and Meijer G """ ap = list(ap) bq = list(bq) ap.sort(key=key) bq.sort(key=key) nap = [] # we will edit bq in place operators = [] for a in ap: op = None for i in xrange(len(bq)): op = gen(a, bq[i]) if op is not None: bq.pop(i) break if op is None: nap.append(a) else: operators.append(op) return nap, bq, operators def reduce_order(ip): """ Given the hypergeometric parameters `ip.ap`, `ip.bq`, find a sequence of operators to reduces order as much as possible. Return (nip, [operators]), where applying the operators to the hypergeometric function specified by nip.ap, nip.bq yields ap, bq. Examples: >>> from sympy.simplify.hyperexpand import reduce_order, IndexPair >>> reduce_order(IndexPair((1, 2), (3, 4))) (IndexPair((1, 2), (3, 4)), []) >>> reduce_order(IndexPair((1,), (1,))) (IndexPair((), ()), []) >>> reduce_order(IndexPair((2, 4), (3, 3))) (IndexPair((2,), (3,)), []) """ nap, nbq, operators = _reduce_order(ip.ap, ip.bq, ReduceOrder, lambda x: x) return IndexPair(Tuple(*nap), Tuple(*nbq)), operators def reduce_order_meijer(iq): """ Given the Meijer G function parameters, `iq.am`, `iq.ap`, `iq.bm`, `iq.bq`, find a sequence of operators that reduces order as much as possible. Return niq, [operators]. Examples: >>> from sympy.simplify.hyperexpand import reduce_order_meijer, IndexQuadruple >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [3, 4], [1, 2]))[0] IndexQuadruple((4, 3), (5, 6), (3, 4), (2, 1)) >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [3, 4], [1, 8]))[0] IndexQuadruple((3,), (5, 6), (3, 4), (1,)) >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [7, 5], [1, 5]))[0] IndexQuadruple((3,), (), (), (1,)) >>> reduce_order_meijer(IndexQuadruple([3, 4], [5, 6], [7, 5], [5, 3]))[0] IndexQuadruple((), (), (), ()) """ nan, nbq, ops1 = _reduce_order(iq.an, iq.bq, ReduceOrder.meijer_plus, lambda x: -x) nbm, nap, ops2 = _reduce_order(iq.bm, iq.ap, ReduceOrder.meijer_minus, lambda x: x) return IndexQuadruple(Tuple(*nan), Tuple(*nap), Tuple(*nbm), Tuple(*nbq)), \ ops1 + ops2 def make_derivative_operator(M, z): """ Create a derivative operator, to be passed to Operator.apply. """ from sympy import poly def doit(C): r = z*C.diff(z) + C*M r = r.applyfunc(make_simp(z)) return r return doit def apply_operators(obj, ops, op): """ Apply the list of operators `ops` to object `obj`, substituting `op` for the generator. """ res = obj for o in reversed(ops): res = o.apply(res, op) return res def devise_plan(ip, nip, z): """ Devise a plan (consisting of shift and un-shift operators) to be applied to the hypergeometric function (`nip.ap`, `nip.bq`) to yield (`ip.ap`, `ip.bq`). Returns a list of operators. >>> from sympy.simplify.hyperexpand import devise_plan, IndexPair >>> from sympy.abc import z Nothing to do: >>> devise_plan(IndexPair((1, 2), ()), IndexPair((1, 2), ()), z) [] >>> devise_plan(IndexPair((), (1, 2)), IndexPair((), (1, 2)), z) [] Very simple plans: >>> devise_plan(IndexPair((2,), ()), IndexPair((1,), ()), z) [] >>> devise_plan(IndexPair((), (2,)), IndexPair((), (1,)), z) [] Several buckets: >>> from sympy import S >>> devise_plan(IndexPair((1, S.Half), ()), IndexPair((2, S('3/2')), ()), z) [, ] A slightly more complicated plan: >>> devise_plan(IndexPair((1, 3), ()), IndexPair((2, 2), ()), z) [, ] Another more complicated plan: (note that the ap have to be shifted first!) >>> devise_plan(IndexPair((1, -1), (2,)), IndexPair((3, -2), (4,)), z) [, , , , ] """ abuckets, bbuckets = ip.compute_buckets() nabuckets, nbbuckets = nip.compute_buckets(abuckets, bbuckets) if len(abuckets.keys()) != len(nabuckets.keys()) or \ len(bbuckets.keys()) != len(nbbuckets.keys()): raise ValueError('%s not reachable from %s' % (ip, nip)) ops = [] def do_shifts(fro, to, inc, dec): ops = [] for i in xrange(len(fro)): if to[i] - fro[i] > 0: sh = inc ch = 1 else: sh = dec ch = -1 while to[i] != fro[i]: ops += [sh(fro, i)] fro[i] += ch return ops def do_shifts_a(nal, nbk, al, aother, bother): """ Shift us from (nal, nbk) to (al, nbk). """ return do_shifts(nal, al, lambda p, i: ShiftA(p[i]), lambda p, i: UnShiftA(p + aother, nbk + bother, i, z)) def do_shifts_b(nal, nbk, bk, aother, bother): """ Shift us from (nal, nbk) to (nal, bk). """ return do_shifts(nbk, bk, lambda p, i: UnShiftB(nal + aother, p + bother, i, z), lambda p, i: ShiftB(p[i])) for r in set(abuckets.keys() + bbuckets.keys()): al = () nal = () bk = () nbk = () if r in abuckets: al = abuckets[r] nal = nabuckets[r] if r in bbuckets: bk = bbuckets[r] nbk = nbbuckets[r] if len(al) != len(nal) or len(bk) != len(nbk): raise ValueError('%s not reachable from %s' % ((ap, bq), (nap, nbq))) al = sorted(list(al)) nal = sorted(list(nal)) bk = sorted(list(bk)) nbk = sorted(list(nbk)) def others(dic, key): l = [] for k, value in dic.iteritems(): if k != key: l += list(dic[k]) return l aother = others(nabuckets, r) bother = others(nbbuckets, r) if len(al) == 0: # there can be no complications, just shift the bs as we please ops += do_shifts_b([], nbk, bk, aother, bother) elif len(bk) == 0: # there can be no complications, just shift the as as we please ops += do_shifts_a(nal, [], al, aother, bother) else: namax = nal[-1] amax = al[-1] if nbk[0] <= namax or bk[0] <= amax: raise ValueError('Non-suitable parameters.') if namax > amax: # we are going to shift down - first do the as, then the bs ops += do_shifts_a(nal, nbk, al, aother, bother) ops += do_shifts_b(al, nbk, bk, aother, bother) else: # we are going to shift up - first do the bs, then the as ops += do_shifts_b(nal, nbk, bk, aother, bother) ops += do_shifts_a(nal, bk, al, aother, bother) nabuckets[r] = al nbbuckets[r] = bk ops.reverse() return ops def try_shifted_sum(ip, z): """ Try to recognise a hypergeometric sum that starts from k > 0. """ from sympy.functions import rf, factorial abuckets, bbuckets = ip.compute_buckets() if not S(0) in abuckets or len(abuckets[S(0)]) != 1: return None r = abuckets[S(0)][0] if r <= 0: return None if not S(0) in bbuckets: return None l = list(bbuckets[S(0)]) l.sort() k = l[0] if k <= 0: return None nap = list(ip.ap) nap.remove(r) nbq = list(ip.bq) nbq.remove(k) k -= 1 nap = map(lambda x: x - k, nap) nbq = map(lambda x: x - k, nbq) ops = [] for n in xrange(r - 1): ops.append(ShiftA(n + 1)) ops.reverse() fac = factorial(k)/z**k for a in nap: fac /= rf(a, k) for b in nbq: fac *= rf(b, k) ops += [MultOperator(fac)] p = 0 for n in xrange(k): m = z**n/factorial(n) for a in nap: m *= rf(a, n) for b in nbq: m /= rf(b, n) p += m return IndexPair(nap, nbq), ops, -p def try_polynomial(ip, z): """ Recognise polynomial cases. Returns None if not such a case. Requires order to be fully reduced. """ from sympy import oo, factorial, rf abuckets, bbuckets = ip.compute_buckets() a0 = list(abuckets.get(S(0), [])) b0 = list(bbuckets.get(S(0), [])) a0.sort() b0.sort() al0 = filter(lambda x: x <= 0, a0) bl0 = filter(lambda x: x <= 0, b0) if bl0: return oo if not al0: return None a = al0[-1] fac = 1 res = S(1) for n in xrange(-a): fac *= z fac /= n + 1 for a in ip.ap: fac *= a + n for b in ip.bq: fac /= b + n res += fac return res collection = None def _hyperexpand(ip, z, ops0=[], z0=Dummy('z0'), premult=1, chainmult=1): """ Try to find an expression for the hypergeometric function `ip.ap`, `ip.bq`. The result is expressed in terms of a dummy variable z0. Then it is multiplied by premult. Then ops0 is applied, using chainmult*t*d/dt for the operator. These latter parameters are all trickery to make _meijergexpand short. """ from sympy.simplify import powdenest, simplify # TODO # The following would be possible: # 1) Partial simplification (i.e. return a simpler hypergeometric function, # even if we cannot express it in terms of named special functions). # 2) PFD Duplication (see Kelly Roach's paper) # 3) If the coefficients are a rational function of n (numerator parameters # k, a1, ..., an, denominator parameters a1+k1, a2+k2, ..., an+kn, where # k, k1, ..., kn are integers) then result can be expressed using Lerch # transcendent. Under certain conditions, this simplifies to polylogs # or even zeta functions. C/f Kelly Roach's paper. global collection if collection is None: collection = FormulaCollection() debug('Trying to expand hypergeometric function corresponding to', ip) # First reduce order as much as possible. nip, ops = reduce_order(ip) if ops: debug(' Reduced order to', nip) else: debug(' Could not reduce order.') # Now try polynomial cases res = try_polynomial(nip, z0) if res is not None: debug(' Recognised polynomial.') p = apply_operators(res, ops, lambda f: z0*f.diff(z0)) p = apply_operators(p*premult, ops0, lambda f: chainmult*z0*f.diff(z0)) return simplify(p).subs(z0, z) # Try to recognise a shifted sum. p = S(0) res = try_shifted_sum(nip, z0) if res != None: nip, nops, p = res debug(' Recognised shifted sum, reducerd order to', nip) ops += nops # apply the plan for poly p = apply_operators(p, ops, lambda f: z0*f.diff(z0)) p = apply_operators(p*premult, ops0, lambda f: chainmult*z0*f.diff(z0)) p = simplify(p).subs(z0, z) # Now try to find a formula f = collection.lookup_origin(nip) if f is None: debug(' Could not find an origin.') # There is nothing we can do. return None # We need to find the operators that convert f into (nap, nbq). ops += devise_plan(nip, f.indices, z0) # Now carry out the plan. C = apply_operators(f.C.subs(f.z, z0), ops, make_derivative_operator(f.M.subs(f.z, z0), z0)) C = apply_operators(C*premult, ops0, make_derivative_operator(f.M.subs(f.z, z0)*chainmult, z0)) if premult == 1: C = C.applyfunc(make_simp(z0)) r = C*f.B.subs(f.z, z0) r = r[0].subs(z0, z) + p # This will simpliy things like sqrt(-z**2) to i*z. # It would be wrong under certain choices of branch, but all results we # return are under an "implicit suitable choice of branch" anyway. return powdenest(r, force=True) def _meijergexpand(iq, z0, allow_hyper=False): """ Try to find an expression for the Meijer G function specified by the IndexQuadruple `iq`. If `allow_hyper` is True, then returning an expression in terms of hypergeometric functions is allowed. Currently this just does slater's theorem. """ from sympy import hyper, Piecewise, meijerg, powdenest iq_ = iq debug('Try to expand meijer G function corresponding to', iq) # We will play games with analytic continuation - rather use a fresh symbol z = Dummy('z') iq, ops = reduce_order_meijer(iq) if ops: debug(' Reduced order to', iq) else: debug(' Could not reduce order.') # TODO the following would be possible: # 1) Set up a collection of meijer g formulae. # This handles some cases that cannot be done using Slater's theorem, # and also yields nicer looking results. # 2) Paired Index Theorems # 3) PFD Duplication # (See Kelly Roach's paper for (2) and (3).) # # TODO Also, we tend to create combinations of gamma functions that can be # simplified. def can_do(pbm, pap): """ Test if slater applies. """ for i in pbm: if len(pbm[i]) > 1: l = 0 if i in pap: l = len(pap[i]) if l + 1 < len(pbm[i]): return False return True def do_slater(an, bm, ap, bq, z, t, chainmult, realz): from sympy import gamma, residue, factorial, rf, expand_func iq = IndexQuadruple(an, bm, ap, bq) _, pbm, pap, _ = iq.compute_buckets() if not can_do(pbm, pap): return S(0), False res = S(0) for m in pbm: if len(pbm[m]) == 1: bh = pbm[m][0] fac = 1 bo = list(bm) bo.remove(bh) for bj in bo: fac *= gamma(bj - bh) for aj in an: fac *= gamma(1 + bh - aj) for bj in bq: fac /= gamma(1 + bh - bj) for aj in ap: fac /= gamma(aj - bh) nap = [1 + bh - a for a in list(an) + list(ap)] nbq = [1 + bh - b for b in list(bo) + list(bq)] k = S(-1)**(len(ap) - len(bm)) harg = k*z premult = (k*t)**bh hyp = _hyperexpand(IndexPair(nap, nbq), harg, ops, t, premult, chainmult) if hyp is None: hyp = apply_operators(premult*hyper(nap, nbq, t), ops, lambda f: chainmult*t*f.diff(t)).subs(t, harg) res += fac * hyp else: b_ = pbm[m][0] ki = [bi - b_ for bi in pbm[m][1:]] u = len(ki) li = [ai - b_ for ai in pap[m][0:u+1]] bo = list(bm) for b in pbm[m]: bo.remove(b) ao = list(ap) for a in pap[m][:u]: ao.remove(a) lu = li[-1] di = [l - k for (l, k) in zip(li, ki)] # We first work out the integrand: s = Dummy('s') integrand = z**s for b in bm: integrand *= gamma(b - s) for a in an: integrand *= gamma(1 - a + s) for b in bq: integrand /= gamma(1 - b + s) for a in ap: integrand /= gamma(a - s) # Now sum the finitely many residues: # XXX This speeds up some cases - is it a good idea? integrand = expand_func(integrand) for r in range(lu): resid = residue(integrand, s, b_ + r) resid = apply_operators(resid, ops, lambda f: realz*f.diff(realz)) res -= resid # Now the hypergeometric term. au = b_ + lu k = S(-1)**(len(ao) + len(bo) + 1) harg = k*z premult = (k*t)**au nap = [1 + au - a for a in list(an) + list(ap)] + [1] nbq = [1 + au - b for b in list(bm) + list(bq)] hyp = _hyperexpand(IndexPair(nap, nbq), harg, ops, t, premult, chainmult) if hyp is None: hyp = apply_operators(premult*hyper(nap, nbq, t), ops, lambda f: chainmult*t*f.diff(t)).subs(t, harg) C = S(-1)**(lu)/factorial(lu) for i in range(u): C *= S(-1)**di[i]/rf(lu - li[i] + 1, di[i]) for a in an: C *= gamma(1 - a + au) for b in bo: C *= gamma(b - au) for a in ao: C /= gamma(a - au) for b in bq: C /= gamma(1 - b + au) res += C*hyp cond = len(an) + len(ap) < len(bm) + len(bq) if len(an) + len(ap) == len(bm) + len(bq): cond = abs(z) < 1 return res, cond t = Dummy('t') slater1, cond1 = do_slater(iq.an, iq.bm, iq.ap, iq.bq, z, t, 1, z) def tr(l): return [1 - x for x in l] for op in ops: op._poly = Poly(op._poly.subs(z, S(-1)**(len(iq.an) - len(iq.bq))/t), x) slater2, cond2 = do_slater(tr(iq.bm), tr(iq.an), tr(iq.bq), tr(iq.ap), 1/z, t, -1, z) slater1 = powdenest(slater1.subs(z, z0), force=True) slater2 = powdenest(slater2.subs(z, z0), force=True) if meijerg(iq.an, iq.ap, iq.bm, iq.bq, z).delta > 0: # The above condition means that the convergence region is connected. # Any expression we find can be continued analytically to the entire # convergence region. if cond1 is not False: cond1 = True if cond2 is not False: cond2 = True if not isinstance(cond1, bool): cond1 = cond1.subs(z, z0) if not isinstance(cond2, bool): cond2 = cond2.subs(z, z0) if cond1 is True and not slater1.has(hyper): return slater1 if cond2 is True and not slater2.has(hyper): return slater2 # We couldn't find an expression without hypergeometric functions. # TODO it would be helpful to give conditions under which the integral # is known to diverge. r = Piecewise((slater1, cond1), (slater2, cond2), (meijerg(iq_.an, iq_.ap, iq_.bm, iq_.bq, z0), True)) if r.has(hyper) and not allow_hyper: debug(' Could express using hypergeometric functions, but not allowed.') if not r.has(hyper) or allow_hyper: return r return meijerg(iq_.an, iq_.ap, iq_.bm, iq_.bq, z0) def hyperexpand(f, allow_hyper=False): """ Expand hypergeometric functions. If allow_hyper is True, allow partial simplification (that is a result different from input, but still containing hypergeometric functions). Examples: >>> from sympy.simplify.hyperexpand import hyperexpand >>> from sympy.functions import hyper >>> from sympy.abc import z >>> hyperexpand(hyper([], [], z)) exp(z) Non-hyperegeometric parts of the expression and hypergeometric expressions that are not recognised are left unchanged: >>> hyperexpand(1 + hyper([1, 1, 1], [], z)) 1 + hyper((1, 1, 1), (), z) """ from sympy.functions import hyper, meijerg from sympy import nan, zoo, oo f = sympify(f) def do_replace(ap, bq, z): r = _hyperexpand(IndexPair(ap, bq), z) if r is None: return hyper(ap, bq, z) else: return r def do_meijer(ap, bq, z): r = _meijergexpand(IndexQuadruple(ap[0], ap[1], bq[0], bq[1]), z, allow_hyper) if not r.has(nan, zoo, oo, -oo): return r return f.replace(hyper, do_replace).replace(meijerg, do_meijer) from sympy.polys.polytools import Poly wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/0000755000175000017500000000000012014170666024075 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_epathtools.py0000644000175000017500000000645112014170666027676 0ustar georgeskgeorgesk"""Tests for tools for manipulation of expressions using paths. """ from sympy.simplify.epathtools import epath, EPath from sympy.utilities.pytest import raises from sympy import sin, cos, E from sympy.abc import x, y, z, t def test_epath_select(): expr = [((x, 1, t), 2), ((3, y, 4), z)] assert epath("/*", expr) == [((x, 1, t), 2), ((3, y, 4), z)] assert epath("/*/*", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/*/*", expr) == [x, 1, t, 3, y, 4] assert epath("/*/*/*/*", expr) == [] assert epath("/[:]", expr) == [((x, 1, t), 2), ((3, y, 4), z)] assert epath("/[:]/[:]", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/[:]/[:]/[:]", expr) == [x, 1, t, 3, y, 4] assert epath("/[:]/[:]/[:]/[:]", expr) == [] assert epath("/*/[:]", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/[0]", expr) == [(x, 1, t), (3, y, 4)] assert epath("/*/[1]", expr) == [2, z] assert epath("/*/[2]", expr) == [] assert epath("/*/int", expr) == [2] assert epath("/*/Symbol", expr) == [z] assert epath("/*/tuple", expr) == [(x, 1, t), (3, y, 4)] assert epath("/*/__iter__?", expr) == [(x, 1, t), (3, y, 4)] assert epath("/*/int|tuple", expr) == [(x, 1, t), 2, (3, y, 4)] assert epath("/*/Symbol|tuple", expr) == [(x, 1, t), (3, y, 4), z] assert epath("/*/int|Symbol|tuple", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/int|__iter__?", expr) == [(x, 1, t), 2, (3, y, 4)] assert epath("/*/Symbol|__iter__?", expr) == [(x, 1, t), (3, y, 4), z] assert epath("/*/int|Symbol|__iter__?", expr) == [(x, 1, t), 2, (3, y, 4), z] assert epath("/*/[0]/int", expr) == [1, 3, 4] assert epath("/*/[0]/Symbol", expr) == [x, t, y] assert epath("/*/[0]/int[1:]", expr) == [1, 4] assert epath("/*/[0]/Symbol[1:]", expr) == [t, y] assert epath("/Symbol", x + y + z + 1) == [x, y, z] assert epath("/*/*/Symbol", t + sin(x + 1) + cos(x + y + E)) == [x, x, y] def test_epath_apply(): expr = [((x, 1, t), 2), ((3, y, 4), z)] func = lambda expr: expr**2 assert epath("/*", expr, list) == [[(x, 1, t), 2], [(3, y, 4), z]] assert epath("/*/[0]", expr, list) == [([x, 1, t], 2), ([3, y, 4], z)] assert epath("/*/[1]", expr, func) == [((x, 1, t), 4), ((3, y, 4), z**2)] assert epath("/*/[2]", expr, list) == expr assert epath("/*/[0]/int", expr, func) == [((x, 1, t), 2), ((9, y, 16), z)] assert epath("/*/[0]/Symbol", expr, func) == [((x**2, 1, t**2), 2), ((3, y**2, 4), z)] assert epath("/*/[0]/int[1:]", expr, func) == [((x, 1, t), 2), ((3, y, 16), z)] assert epath("/*/[0]/Symbol[1:]", expr, func) == [((x, 1, t**2), 2), ((3, y**2, 4), z)] assert epath("/Symbol", x + y + z + 1, func) == x**2 + y**2 + z**2 + 1 assert epath("/*/*/Symbol", t + sin(x + 1) + cos(x + y + E), func) == \ t + sin(x**2 + 1) + cos(x**2 + y**2 + E) def test_EPath(): assert EPath("/*/[0]")._path == "/*/[0]" assert EPath(EPath("/*/[0]"))._path == "/*/[0]" assert isinstance(epath("/*/[0]"), EPath) is True assert repr(EPath("/*/[0]")) == "EPath('/*/[0]')" raises(ValueError, 'EPath("")') raises(ValueError, 'EPath("/")') raises(ValueError, 'EPath("/|x")') raises(ValueError, 'EPath("/[")') raises(ValueError, 'EPath("/[0]%")') raises(NotImplementedError, 'EPath("Symbol")') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_rewrite.py0000644000175000017500000000175212014170666027174 0ustar georgeskgeorgeskfrom sympy import (sin, cos, exp, cot, sqrt, S, I, E, pi, symbols, Function, Matrix, Eq, RootSum, Lambda) from sympy.integrals import integrate from sympy.utilities.pytest import XFAIL, raises x,y,z,n = symbols('x,y,z,n') def test_has(): assert cot(x).has(x) assert cot(x).has(cot) assert not cot(x).has(sin) assert sin(x).has(x) assert sin(x).has(sin) assert not sin(x).has(cot) def test_sin_exp_rewrite(): assert sin(x).rewrite(sin, exp) == -I/2*(exp(I*x)-exp(-I*x)) assert sin(x).rewrite(sin, exp).rewrite(exp, sin) == sin(x) assert cos(x).rewrite(cos, exp).rewrite(exp, cos) == cos(x) assert (sin(5*y) - sin(2*x)).rewrite(sin, exp).rewrite(exp, sin) == sin(5*y) - sin(2*x) assert sin(x+y).rewrite(sin, exp).rewrite(exp, sin) == sin(x+y) assert cos(x+y).rewrite(cos, exp).rewrite(exp, cos) == cos(x+y) # This next test currently passes... not clear whether it should or not? assert cos(x).rewrite(cos, exp).rewrite(exp, sin) == cos(x) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_hyperexpand.py0000644000175000017500000006617512014170666030054 0ustar georgeskgeorgeskfrom sympy.simplify.hyperexpand import (ShiftA, ShiftB, UnShiftA, UnShiftB, ReduceOrder, reduce_order, apply_operators, devise_plan, make_derivative_operator, Formula, hyperexpand, IndexPair, IndexQuadruple, reduce_order_meijer) from sympy import hyper, I, S, meijerg, Piecewise from sympy.utilities.pytest import raises from sympy.abc import z, a, b, c from sympy.utilities.randtest import test_numerically as tn from sympy.utilities.pytest import XFAIL, skip from random import randrange from sympy import (cos, sin, log, exp, asin, lowergamma, atanh, besseli, gamma, sqrt, pi) # whether to veryify that we can indeed do everything in the tables # beware: this takes a *long* time do_tables = False def test_hyperexpand(): # Luke, Y. L. (1969), The Special Functions and Their Approximations, # Volume 1, section 6.2 assert hyperexpand(hyper([], [], z)) == exp(z) assert hyperexpand(hyper([1, 1], [2], -z)*z) == log(1 + z) assert hyperexpand(hyper([], [S.Half], -z**2/4)) == cos(z) assert hyperexpand(z*hyper([], [S('3/2')], -z**2/4)) == sin(z) assert hyperexpand(hyper([S('1/2'), S('1/2')], [S('3/2')], z**2)*z) \ == asin(z) def tables(fn): def wrapper(): skip("This is too slow.") wrapper.__name__ = fn.__name__ if do_tables: return fn else: return wrapper def can_do(ap, bq, numerical=True): r = hyperexpand(hyper(ap, bq, z)) if r.has(hyper): return False if not numerical: return True repl = {} for n, a in enumerate(r.free_symbols - set([z])): repl[a] = randcplx(n) return tn(hyper(ap, bq, z).subs(repl), r.subs(repl), z) def test_roach(): # Kelly B. Roach. Meijer G Function Representations. # Section "Gallery" assert can_do([S(1)/2], [S(9)/2]) assert can_do([], [1, S(5)/2, 4]) assert can_do([-S.Half, 1, 2], [3, 4]) assert can_do([S(1)/3], [-S(2)/3, -S(1)/2, S(1)/2, 1]) assert can_do([-S(3)/2, -S(1)/2], [-S(5)/2, 1]) @XFAIL def test_roach_fail(): assert can_do([-S(3)/2,], [-S(1)/2, S(1)/2]) # shine-integral assert can_do([-S(3)/2, -S(1)/2], [2]) # elliptic integrals assert can_do([-S(1)/2, 1], [S(1)/4, S(1)/2, S(3)/4]) # PFDD assert can_do([S(3)/2], [S(5)/2, 5]) # polylog assert can_do([-S(1)/2, S(1)/2, 1], [S(3)/2, S(5)/2]) # polylog, pfdd assert can_do([1, 2, 3], [S(1)/2, 4]) # XXX ? assert can_do([S(1)/2], [-S(1)/3, -S(1)/2, -S(2)/3]) # PFDD ? # For the long table tests, see end of file def test_polynomial(): from sympy import oo assert hyperexpand(hyper([], [-1], z)) == oo assert hyperexpand(hyper([-2], [-1], z)) == oo assert hyperexpand(hyper([0, 0], [-1], z)) == 1 assert can_do([-5, -2, randcplx(), randcplx()], [-10, randcplx()]) def test_hyperexpand_bases(): assert hyperexpand(hyper([2], [a], z)) == \ a + z**(-a + 1)*(-a**2 + 3*a + z*(a - 1) - 2)*exp(z)*lowergamma(a - 1, z) - 1 # TODO [a+1, a-S.Half], [2*a] assert hyperexpand(hyper([1, 2], [3], z)) == -2/z - 2*log(-z + 1)/z**2 assert hyperexpand(hyper([S.Half, 2], [S(3)/2], z)) == \ -1/(2*z - 2) + log((z**(S(1)/2) + 1)/(-z**(S(1)/2) + 1))/(4*z**(S(1)/2)) assert hyperexpand(hyper([S(1)/2, S(1)/2], [S(5)/2], z)) == \ (-3*z + 3)/(4*z*(-z + 1)**(S(1)/2)) \ + (6*z - 3)*asin(z**(S(1)/2))/(4*z**(S(3)/2)) assert hyperexpand(hyper([1, 2], [S(3)/2], z)) == -1/(2*z - 2) \ - asin(z**(S(1)/2))/(z**(S(1)/2)*(2*z - 2)*(-z + 1)**(S(1)/2)) assert hyperexpand(hyper([-S.Half - 1, 1, 2], [S.Half, 3], z)) == \ z**(S(1)/2)*(6*z/7 - S(6)/5)*atanh(z**(S(1)/2)) \ + (-30*z**2 + 32*z - 6)/(35*z) - 6*log(-z + 1)/(35*z**2) assert hyperexpand(hyper([1+S.Half, 1, 1], [2, 2], z)) == \ -4*log((-z + 1)**(S(1)/2)/2 + S(1)/2)/z # TODO hyperexpand(hyper([a], [2*a + 1], z)) # TODO [S.Half, a], [S(3)/2, a+1] assert hyperexpand(hyper([2], [b, 1], z)) == \ z**(-b/2 + S(1)/2)*besseli(b - 1, 2*z**(S(1)/2))*gamma(b) \ + z**(-b/2 + 1)*besseli(b, 2*z**(S(1)/2))*gamma(b) # TODO [a], [a - S.Half, 2*a] def test_hyperexpand_parametric(): assert hyperexpand(hyper([a, S(1)/2 + a], [S(1)/2], z)) \ == (1 + z**(S(1)/2))**(-2*a)/2 + (1 - z**(S(1)/2))**(-2*a)/2 assert hyperexpand(hyper([a, -S(1)/2 + a], [2*a], z)) \ == 2**(2*a - 1)*((-z + 1)**(S(1)/2) + 1)**(-2*a + 1) def test_shifted_sum(): from sympy import simplify assert simplify(hyperexpand(z**4*hyper([2], [3, S('3/2')], -z**2))) \ == -S(1)/2 + cos(2*z)/2 + z*sin(2*z) - z**2*cos(2*z) def randrat(): """ Steer clear of integers. """ return S(randrange(25) + 10)/50 def randcplx(offset=-1): """ Polys is not good with real coefficients. """ return randrat() + I*randrat() + I*(1 + offset) def test_formulae(): from sympy.simplify.hyperexpand import FormulaCollection formulae = FormulaCollection().formulae for formula in formulae: h = hyper(formula.indices.ap, formula.indices.bq, formula.z) rep = {} for n, sym in enumerate(formula.symbols): rep[sym] = randcplx(n) #print h, closed_form # first test if the closed-form is actually correct h = h.subs(rep) closed_form = formula.closed_form.subs(rep) z = formula.z assert tn(h, closed_form, z) # now test the computed matrix cl = (formula.C * formula.B)[0].subs(rep) assert tn(closed_form, cl, z) deriv1 = z*formula.B.diff(z) deriv2 = formula.M * formula.B for d1, d2 in zip(deriv1, deriv2): assert tn(d1.subs(rep), d2.subs(rep), z) def op(f): return z*f.diff(z) def test_plan(): assert devise_plan(IndexPair([0], ()), IndexPair([0], ()), z) == [] raises(ValueError, 'devise_plan(IndexPair([1], ()), IndexPair((), ()), z)') raises(ValueError, 'devise_plan(IndexPair([2], [1]), IndexPair([2], [2]), z)') raises(KeyError, 'devise_plan(IndexPair([2], []), IndexPair([S("1/2")], []), z)') # We cannot use pi/(10000 + n) because polys is insanely slow. a1, a2, b1 = map(lambda n: randcplx(n), range(3)) b1 += 2*I h = hyper([a1, a2], [b1], z) h2 = hyper((a1 + 1, a2), [b1], z) assert tn(apply_operators(h, devise_plan(IndexPair((a1 + 1, a2), [b1]), IndexPair((a1, a2), [b1]), z), op), h2, z) h2 = hyper((a1 + 1, a2 - 1), [b1], z) assert tn(apply_operators(h, devise_plan(IndexPair((a1 + 1, a2 - 1), [b1]), IndexPair((a1, a2), [b1]), z), op), h2, z) def test_plan_derivatives(): a1, a2, a3 = 1, 2, S('1/2') b1, b2 = 3, S('5/2') h = hyper((a1, a2, a3), (b1, b2), z) h2 = hyper((a1 + 1, a2 + 1, a3 + 2), (b1 + 1, b2 + 1), z) ops = devise_plan(IndexPair((a1 + 1, a2 + 1, a3 + 2), (b1 + 1, b2 + 1)), IndexPair((a1, a2, a3), (b1, b2)), z) f = Formula((a1, a2, a3), (b1, b2), z, h, []) deriv = make_derivative_operator(f.M, z) assert tn((apply_operators(f.C, ops, deriv)*f.B)[0], h2, z) h2 = hyper((a1, a2 - 1, a3 - 2), (b1 - 1, b2 - 1), z) ops = devise_plan(IndexPair((a1, a2 - 1, a3 - 2), (b1 - 1, b2 - 1)), IndexPair((a1, a2, a3), (b1, b2)), z) assert tn((apply_operators(f.C, ops, deriv)*f.B)[0], h2, z) def test_reduction_operators(): a1, a2, b1 = map(lambda n: randcplx(n), range(3)) h = hyper([a1], [b1], z) assert ReduceOrder(2, 0) is None assert ReduceOrder(2, -1) is None assert ReduceOrder(1, S('1/2')) is None h2 = hyper((a1, a2), (b1, a2), z) assert tn(ReduceOrder(a2, a2).apply(h, op), h2, z) h2 = hyper((a1, a2 + 1), (b1, a2), z) assert tn(ReduceOrder(a2 + 1, a2).apply(h, op), h2, z) h2 = hyper((a2 + 4, a1), (b1, a2), z) assert tn(ReduceOrder(a2 + 4, a2).apply(h, op), h2, z) # test several step order reduction ap = (a2 + 4, a1, b1 + 1) bq = (a2, b1, b1) nip, ops = reduce_order(IndexPair(ap, bq)) assert nip.ap == (a1,) assert nip.bq == (b1,) assert tn(apply_operators(h, ops, op), hyper(ap, bq, z), z) def test_shift_operators(): a1, a2, b1, b2, b3 = map(lambda n: randcplx(n), range(5)) h = hyper((a1, a2), (b1, b2, b3), z) raises(ValueError, 'ShiftA(0)') raises(ValueError, 'ShiftB(1)') assert tn(ShiftA(a1).apply(h, op), hyper((a1 + 1, a2), (b1, b2, b3), z), z) assert tn(ShiftA(a2).apply(h, op), hyper((a1, a2 + 1), (b1, b2, b3), z), z) assert tn(ShiftB(b1).apply(h, op), hyper((a1, a2), (b1 - 1, b2, b3), z), z) assert tn(ShiftB(b2).apply(h, op), hyper((a1, a2), (b1, b2 - 1, b3), z), z) assert tn(ShiftB(b3).apply(h, op), hyper((a1, a2), (b1, b2, b3 - 1), z), z) def test_ushift_operators(): a1, a2, b1, b2, b3 = map(lambda n: randcplx(n), range(5)) h = hyper((a1, a2), (b1, b2, b3), z) raises(ValueError, 'UnShiftA((1,), (), 0, z)') raises(ValueError, 'UnShiftB((), (-1,), 0, z)') raises(ValueError, 'UnShiftA((1,), (0, -1, 1), 0, z)') raises(ValueError, 'UnShiftB((0, 1), (1,), 0, z)') s = UnShiftA((a1, a2), (b1, b2, b3), 0, z) assert tn(s.apply(h, op), hyper((a1 - 1, a2), (b1, b2, b3), z), z) s = UnShiftA((a1, a2), (b1, b2, b3), 1, z) assert tn(s.apply(h, op), hyper((a1, a2 - 1), (b1, b2, b3), z), z) s = UnShiftB((a1, a2), (b1, b2, b3), 0, z) assert tn(s.apply(h, op), hyper((a1, a2), (b1 + 1, b2, b3), z), z) s = UnShiftB((a1, a2), (b1, b2, b3), 1, z) assert tn(s.apply(h, op), hyper((a1, a2), (b1, b2 + 1, b3), z), z) s = UnShiftB((a1, a2), (b1, b2, b3), 2, z) assert tn(s.apply(h, op), hyper((a1, a2), (b1, b2, b3 + 1), z), z) def can_do_meijer(a1, a2, b1, b2, numeric=True): """ This helper function tries to hyperexpand() the meijer g-function corresponding to the parameters a1, a2, b1, b2. It returns False if this expansion still contains g-functions. If numeric is True, it also tests the so-obtained formula numerically (at random values) and returns False if the test fails. Else it returns True. """ r = hyperexpand(meijerg(a1, a2, b1, b2, z)) if r.has(meijerg): return False if not numeric: return True repl = {} for n, a in enumerate(meijerg(a1, a2, b1, b2, z).free_symbols - set([z])): repl[a] = randcplx(n) return tn(meijerg(a1, a2, b1, b2, z).subs(repl), r.subs(repl), z) def test_meijerg_expand(): # from mpmath docs assert hyperexpand(meijerg([[],[]], [[0],[]], -z)) == exp(z) assert hyperexpand(meijerg([[1,1],[]], [[1],[0]], z)) == \ log(z + 1) assert hyperexpand(meijerg([[1,1],[]], [[1],[1]], z)) == \ z/(z + 1) assert hyperexpand(meijerg([[],[]], [[S(1)/2],[0]], (z/2)**2)) \ == sin(z)/sqrt(pi) assert hyperexpand(meijerg([[],[]], [[0], [S(1)/2]], (z/2)**2)) \ == cos(z)/sqrt(pi) assert can_do_meijer([], [a], [a-1, a-S.Half], []) assert can_do_meijer([], [], [a/2], [-a/2], False) # branches... assert can_do_meijer([a], [b], [a], [b, a - 1]) # wikipedia assert hyperexpand(meijerg([1], [], [], [0], z)) == \ Piecewise((0, abs(z) < 1), (1, abs(1/z) < 1), (meijerg([1], [], [], [0], z), True)) assert hyperexpand(meijerg([], [1], [0], [], z)) == \ Piecewise((1, abs(z) < 1), (0, abs(1/z) < 1), (meijerg([], [1], [0], [], z), True)) # The Special Functions and their Approximations assert can_do_meijer([], [], [a + b/2], [a, a - b/2, a + S.Half]) assert can_do_meijer([], [], [a], [b], False) # branches only agree for small z assert can_do_meijer([], [S.Half], [a], [-a]) assert can_do_meijer([], [], [a, b], []) assert can_do_meijer([], [], [a, b], []) assert can_do_meijer([], [], [a, a+S.Half], [b, b+S.Half]) assert can_do_meijer([], [], [a, -a], [0, S.Half], False) # dito assert can_do_meijer([], [], [a, a+S.Half, b, b+S.Half], []) assert can_do_meijer([S.Half], [], [0], [a, -a]) assert can_do_meijer([S.Half], [], [a], [0, -a], False) # dito assert can_do_meijer([], [a - S.Half], [a, b], [a - S.Half], False) assert can_do_meijer([], [a+S.Half], [a+b, a-b, a], [], False) assert can_do_meijer([a+S.Half], [], [b, 2*a-b, a], [], False) # This for example is actually zero. assert can_do_meijer([], [], [], [a, b]) # Testing a bug: assert hyperexpand(meijerg([0, 2], [], [], [-1, 1], z)) == \ Piecewise((0, abs(z) < 1), (z*(1 - 1/z**2)/2, abs(1/z) < 1), (meijerg([0, 2], [], [], [-1, 1], z), True)) @XFAIL def test_meijerg_expand_fail(): # These basically test hyper([], [1/2 - a, 1/2 + 1, 1/2], z), # which is *very* messy. But since the meijer g actually yields a # sum of bessel functions, things can sometimes be simplified a lot and # are then put into tables... assert can_do_meijer([], [], [a + S.Half], [a, a - b/2, a + b/2]) assert can_do_meijer([], [], [0, S.Half], [a, -a]) assert can_do_meijer([], [], [3*a - S.Half, a, -a - S.Half], [a - S.Half]) assert can_do_meijer([], [], [0, a - S.Half, -a - S.Half], [S.Half]) assert can_do_meijer([], [], [a, b + S(1)/2, b], [2*b - a]) assert can_do_meijer([], [], [a, b + S(1)/2, b, 2*b - a]) assert can_do_meijer([S.Half], [], [-a, a], [0]) def test_meijerg(): # carefully set up the parameters. # NOTE: this used to fail sometimes. I believe it is fixed, but if you # hit an inexplicable test failure here, please let me know the seed. a1, a2 = map(lambda n: randcplx() - 5*I - n*I, range(2)) b1, b2 = map(lambda n: randcplx() + 5*I + n*I, range(2)) b3, b4, b5, a3, a4, a5 = map(lambda n: randcplx(), range(6)) g = meijerg([a1], [a3, a4], [b1], [b3, b4], z) assert ReduceOrder.meijer_minus(3, 4) is None assert ReduceOrder.meijer_plus(4, 3) is None g2 = meijerg([a1, a2], [a3, a4], [b1], [b3, b4, a2], z) assert tn(ReduceOrder.meijer_plus(a2, a2).apply(g, op), g2, z) g2 = meijerg([a1, a2], [a3, a4], [b1], [b3, b4, a2 + 1], z) assert tn(ReduceOrder.meijer_plus(a2, a2 + 1).apply(g, op), g2, z) g2 = meijerg([a1, a2 - 1], [a3, a4], [b1], [b3, b4, a2 + 2], z) assert tn(ReduceOrder.meijer_plus(a2 - 1, a2 + 2).apply(g, op), g2, z) g2 = meijerg([a1], [a3, a4, b2 - 1], [b1, b2 + 2], [b3, b4], z) assert tn(ReduceOrder.meijer_minus(b2 + 2, b2 - 1).apply(g, op), g2, z, tol=1e-6) # test several-step reduction an = [a1, a2] bq = [b3, b4, a2 + 1] ap = [a3, a4, b2 - 1] bm = [b1, b2 + 1] niq, ops = reduce_order_meijer(IndexQuadruple(an, ap, bm, bq)) assert niq.an == (a1,) assert set(niq.ap) == set([a3, a4]) assert niq.bm == (b1,) assert set(niq.bq) == set([b3, b4]) assert tn(apply_operators(g, ops, op), meijerg(an, ap, bm, bq, z), z) def test_meijerg_confluence(): def t(m, a, b): from sympy import sympify, Piecewise a, b = sympify([a, b]) m_ = m m = hyperexpand(m) if not m == Piecewise((a, abs(z) < 1), (b, abs(1/z) < 1), (m_, True)): return False if not (m.args[0].args[0] == a and m.args[1].args[0] == b): return False z0 = randcplx()/10 if abs(m.subs(z, z0).n() - a.subs(z, z0).n()).n() > 1e-10: return False if abs(m.subs(z, 1/z0).n() - b.subs(z, 1/z0).n()).n() > 1e-10: return False return True assert t(meijerg([], [1, 1], [0, 0], [], z), -log(z), 0) assert t(meijerg([], [3, 1], [0, 0], [], z), -z**2/4 + z - log(z)/2 - S(3)/4, 0) assert t(meijerg([], [3, 1], [-1, 0], [], z), z**2/12 -z/2 + log(z)/2 + S(1)/4 + 1/(6*z), 0) assert t(meijerg([], [1, 1, 1, 1], [0, 0, 0, 0], [], z), -log(z)**3/6, 0) assert t(meijerg([1, 1], [], [], [0, 0], z), 0, -log(1/z)) assert t(meijerg([1, 1], [2, 2], [1, 1], [0, 0], z), -z*log(z) + 2*z, -log(1/z) + 2) assert t(meijerg([S(1)/2], [1, 1], [0, 0], [S(3)/2], z), log(z)/2 - 1, 0) def u(an, ap, bm, bq): m = meijerg(an, ap, bm, bq, z) m2 = hyperexpand(m, allow_hyper=True) if m2.has(meijerg) and not (m2.is_Piecewise and len(m2.args) == 3): return False return tn(m, m2, z) assert u([], [1], [0, 0], []) assert u([1, 1], [], [], [0]) assert u([1, 1], [2, 2, 5], [1, 1, 6], [0, 0]) assert u([1, 1], [2, 2, 5], [1, 1, 6], [0]) @tables def test_prudnikov_misc(): assert can_do([1, (3 + I)/2, (3 - I)/2], [S(3)/2, 2]) assert can_do([S.Half, a - 1], [S(3)/2, a + 1]) assert can_do([], [b + 1]) assert can_do([a], [a - 1, b + 1]) assert can_do([a], [a - S.Half, 2*a]) assert can_do([a], [a - S.Half, 2*a + 1]) assert can_do([a], [a - S.Half, 2*a - 1]) assert can_do([a], [a + S.Half, 2*a]) assert can_do([a], [a + S.Half, 2*a + 1]) assert can_do([a], [a + S.Half, 2*a - 1]) assert can_do([S.Half], [b, 2-b]) assert can_do([S.Half], [b, 3-b]) assert can_do([1], [2, b]) assert can_do([a, a+S.Half], [2*a, b, 2*a - b + 1]) assert can_do([a, a+S.Half], [S.Half, 2*a, 2*a + S.Half]) @tables def test_prudnikov_1(): # A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990). # Integrals and Series: More Special Functions, Vol. 3,. # Gordon and Breach Science Publisher # 7.3.1 assert can_do([a, -a], [S.Half]) assert can_do([a, 1 - a], [S.Half]) assert can_do([a, 1 - a], [S(3)/2]) assert can_do([a, 2 - a], [S.Half]) assert can_do([a, 2 - a], [S(3)/2]) assert can_do([a, 2 - a], [S(3)/2]) assert can_do([a, a + S(1)/2], [2*a - 1]) assert can_do([a, a + S(1)/2], [2*a]) assert can_do([a, a + S(1)/2], [2*a + 1]) assert can_do([a, a + S(1)/2], [S(1)/2]) assert can_do([a, a + S(1)/2], [S(3)/2]) assert can_do([a, a/2 + 1], [a/2]) assert can_do([1, b], [2]) assert can_do([a], [2*a]) assert can_do([a], [2*a + 1]) assert can_do([a], [2*a - 1]) @tables def test_prudnikov_2(): h = S.Half assert can_do([-h, -h], [h]) assert can_do([-h, h], [3*h]) assert can_do([-h, h], [5*h]) assert can_do([-h, h], [7*h]) assert can_do([-h, 1], [h]) for p in [-h, h]: for n in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: for m in [-h, h, 3*h, 5*h, 7*h]: assert can_do([p, n], [m]) for n in [1, 2, 3, 4]: for m in [1, 2, 3, 4]: assert can_do([p, n], [m]) @tables def test_prudnikov_3(): h = S.Half assert can_do([S(1)/4, S(3)/4], [h]) assert can_do([S(1)/4, S(3)/4], [3*h]) assert can_do([S(1)/3, S(2)/3], [3*h]) assert can_do([S(3)/4, S(5)/4], [h]) assert can_do([S(3)/4, S(5)/4], [3*h]) for p in [1, 2, 3, 4]: for n in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4, 9*h]: for m in [1, 3*h, 2, 5*h, 3, 7*h, 4]: assert can_do([p, m], [n]) @tables def test_prudnikov_4(): h = S.Half for p in [3*h, 5*h, 7*h]: for n in [-h, h, 3*h, 5*h, 7*h]: for m in [3*h, 2, 5*h, 3, 7*h, 4]: assert can_do([p, m], [n]) for n in [1, 2, 3, 4]: for m in [2, 3, 4]: assert can_do([p, m], [n]) @tables def test_prudnikov_5(): h = S.Half for p in [1, 2, 3]: for q in range(p, 4): for r in [1, 2, 3]: for s in range(r, 4): assert can_do([-h, p, q], [r, s]) for p in [h, 1, 3*h, 2, 5*h, 3]: for q in [h, 3*h, 5*h]: for r in [h, 3*h, 5*h]: for s in [h, 3*h, 5*h]: if s <= q and s <= r: assert can_do([-h, p, q], [r, s]) for p in [h, 1, 3*h, 2, 5*h, 3]: for q in [1, 2, 3]: for r in [h, 3*h, 5*h]: for s in [1, 2, 3]: assert can_do([-h, p, q], [r, s]) @tables def test_prudnikov_6(): h = S.Half for m in [3*h, 5*h]: for n in [1, 2, 3]: for q in [h, 1, 2]: for p in [1, 2, 3]: assert can_do([h, q, p], [m, n]) for q in [1, 2, 3]: for p in [3*h, 5*h]: assert can_do([h, q, p], [m, n]) for q in [1, 2]: for p in [1, 2, 3]: for m in [1, 2, 3]: for n in [1, 2, 3]: assert can_do([h, q, p], [m, n]) assert can_do([h, h, 5*h], [3*h, 3*h]) assert can_do([h, 1, 5*h], [3*h, 3*h]) assert can_do([h, 2, 2], [1, 3]) # pages 435 to 457 contain more PFDD and stuff like this @tables def test_prudnikov_7(): assert can_do([3], [6]) h = S.Half for n in [h, 3*h, 5*h, 7*h]: assert can_do([-h], [n]) for m in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: # HERE for n in [-h, h, 3*h, 5*h, 7*h, 1, 2, 3, 4]: assert can_do([m], [n]) @tables def test_prudnikov_8(): h = S.Half # 7.12.2 for a in [1, 2, 3]: for b in [1, 2, 3]: for c in range(1, a+1): for d in [h, 1, 3*h, 2, 5*h, 3]: assert can_do([a, b], [c, d]) for b in [3*h, 5*h]: for c in [h, 1, 3*h, 2, 5*h, 3]: for d in [1, 2, 3]: assert can_do([a, b], [c, d]) for a in [-h, h, 3*h, 5*h]: for b in [1, 2, 3]: for c in [h, 1, 3*h, 2, 5*h, 3]: for d in [1, 2, 3]: assert can_do([a, b], [c, d]) for b in [h, 3*h, 5*h]: for c in [h, 3*h, 5*h, 3]: for d in [h, 1, 3*h, 2, 5*h, 3]: if c <= b: assert can_do([a, b], [c, d]) @tables def test_prudnikov_9(): # 7.13.1 [we have a general formula ... so this is a bit pointless] for i in range(9): assert can_do([], [(S(i) + 1)/2]) for i in range(5): assert can_do([], [-(2*S(i) + 1)/2]) @tables def test_prudnikov_10(): # 7.14.2 h = S.Half for p in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: for m in [1, 2, 3, 4]: for n in range(m, 5): assert can_do([p], [m, n]) for p in [1, 2, 3, 4]: for n in [h, 3*h, 5*h, 7*h]: for m in [1, 2, 3, 4]: assert can_do([p], [n, m]) for p in [3*h, 5*h, 7*h]: for m in [h, 1, 2, 5*h, 3, 7*h, 4]: assert can_do([p], [h, m]) assert can_do([p], [3*h, m]) for m in [h, 1, 2, 5*h, 3, 7*h, 4]: assert can_do([7*h], [5*h, m]) @tables def test_prudnikov_11(): # 7.15 assert can_do([a, a+S.Half], [2*a, b, 2*a - b]) assert can_do([a, a+S.Half], [S(3)/2, 2*a, 2*a - S(1)/2]) assert can_do([S(1)/4, S(3)/4], [S(1)/2, S(1)/2, 1]) assert can_do([S(5)/4, S(3)/4], [S(3)/2, S(1)/2, 2]) assert can_do([S(5)/4, S(3)/4], [S(3)/2, S(3)/2, 1]) assert can_do([S(5)/4, S(7)/4], [S(3)/2, S(5)/2, 2]) @tables def test_prudnikov_12(): # 7.16 assert can_do([], [a, a + S.Half, 2*a], False) # branches only agree for some z! assert can_do([], [a, a + S.Half, 2*a+1], False) # dito assert can_do([], [S.Half, a, a+S.Half]) assert can_do([], [S(3)/2, a, a+S.Half]) assert can_do([], [S(1)/4, S(1)/2, S(3)/4]) assert can_do([], [S(1)/2, S(1)/2, 1]) assert can_do([], [S(1)/2, S(3)/2, 1]) assert can_do([], [S(3)/4, S(3)/2, S(5)/4]) assert can_do([], [1, 1, S(3)/2]) assert can_do([], [1, 2, S(3)/2]) assert can_do([], [1, S(3)/2, S(3)/2]) assert can_do([], [S(5)/4, S(3)/2, S(7)/4]) assert can_do([], [2, S(3)/2, S(3)/2]) @XFAIL def test_prudnikov_fail_2F1(): assert can_do([a, b], [b + 1]) # incomplete beta function assert can_do([1, b], [b + 1]) # Lerch Phi assert can_do([-1, b], [c]) # Poly. also -2, -3 etc # TODO polys # Legendre functions: assert can_do([a, b], [a + b + S.Half]) assert can_do([a, b], [a + b - S.Half]) assert can_do([a, b], [a + b + S(3)/2]) assert can_do([a, b], [(a + b + 1)/2]) assert can_do([a, b], [(a + b)/2 + 1]) assert can_do([a, b], [a - b + 1]) assert can_do([a, b], [a - b + 2]) assert can_do([a, b], [2*b]) assert can_do([a, b], [S.Half]) assert can_do([a, b], [S(3)/2]) assert can_do([a, 1 - a], [c]) assert can_do([a, 2 - a], [c]) assert can_do([a, 3 - a], [c]) assert can_do([a, a + S(1)/2], [c]) assert can_do([1, b], [c]) assert can_do([1, b], [S(3)/2]) h = S.Half # Elliptic integrals for p in [-h, h]: for m in [h, 3*h, 5*h, 7*h]: for n in [1, 2, 3, 4]: assert can_do([p, m], [n]) assert can_do([S(1)/4, S(3)/4], [1]) # PFDD o = S(1) assert can_do([o/8, 1], [o/8*9]) assert can_do([o/6, 1], [o/6*7]) assert can_do([o/6, 1], [o/6*13]) assert can_do([o/5, 1], [o/5*6]) assert can_do([o/5, 1], [o/5*11]) assert can_do([o/4, 1], [o/4*5]) assert can_do([o/4, 1], [o/4*9]) assert can_do([o/3, 1], [o/3*4]) assert can_do([o/3, 1], [o/3*7]) assert can_do([o/8*3, 1], [o/8*11]) assert can_do([o/5*2, 1], [o/5*7]) assert can_do([o/5*2, 1], [o/5*12]) assert can_do([o/5*3, 1], [o/5*8]) assert can_do([o/5*3, 1], [o/5*13]) assert can_do([o/8*5, 1], [o/8*13]) assert can_do([o/4*3, 1], [o/4*7]) assert can_do([o/4*3, 1], [o/4*11]) assert can_do([o/3*2, 1], [o/3*5]) assert can_do([o/3*2, 1], [o/3*8]) assert can_do([o/5*4, 1], [o/5*9]) assert can_do([o/5*4, 1], [o/5*14]) assert can_do([o/6*5, 1], [o/6*11]) assert can_do([o/6*5, 1], [o/6*17]) assert can_do([o/8*7, 1], [o/8*15]) @XFAIL def test_prudnikov_fail_3F2(): assert can_do([a, a + S(1)/3, a + S(2)/3], [S(1)/3, S(2)/3]) assert can_do([a, a + S(1)/3, a + S(2)/3], [S(2)/3, S(4)/3]) assert can_do([a, a + S(1)/3, a + S(2)/3], [S(4)/3, S(5)/3]) # page 421 assert can_do([a, a + S(1)/3, a + S(2)/3], [3*a/2, (3*a+1)/2]) # pages 422 ... assert can_do([-S.Half, S.Half, S.Half], [1, 1]) # elliptic integrals assert can_do([-S.Half, S.Half, 1], [S(3)/2, S(3)/2]) # TODO LOTS more # PFDD assert can_do([S(1)/8, S(3)/8, 1], [S(9)/8, S(11)/8]) assert can_do([S(1)/8, S(5)/8, 1], [S(9)/8, S(13)/8]) assert can_do([S(1)/8, S(7)/8, 1], [S(9)/8, S(15)/8]) assert can_do([S(1)/6, S(1)/3, 1], [S(7)/6, S(4)/3]) assert can_do([S(1)/6, S(2)/3, 1], [S(7)/6, S(5)/3]) assert can_do([S(1)/6, S(2)/3, 1], [S(5)/3, S(13)/6]) assert can_do([S.Half, 1, 1], [S(1)/4, S(3)/4]) # LOTS more @XFAIL def test_prudnikov_fail_other(): # 7.11.2 assert can_do([a], [a+1]) # lowergamma ... why?? # 7.12.1 assert can_do([1, a], [b, 1 - 2*a + b]) # ??? # 7.14.2 assert can_do([-S(1)/2], [S(1)/2, S(1)/2]) # shine-integral shi assert can_do([-S(1)/2], [S(1)/2, 1]) # poly-log assert can_do([1], [S(1)/2, S(1)/2]) # poly-log assert can_do([S(1)/4], [S(1)/2, S(5)/4]) # PFDD assert can_do([S(3)/4], [S(3)/2, S(7)/4]) # PFDD assert can_do([1], [S(1)/4, S(3)/4]) # PFDD assert can_do([1], [S(3)/4, S(5)/4]) # PFDD assert can_do([1], [S(5)/4, S(7)/4]) # PFDD # TODO LOTS more # 7.15.2 assert can_do([S(1)/2, 1], [S(3)/4, S(5)/4, S(3)/2]) # PFDD assert can_do([S(1)/2, 1], [S(7)/4, S(5)/4, S(3)/2]) # PFDD assert can_do([1, 1], [S(3)/2, 2, 2]) # cosh-integral chi # 7.16.1 assert can_do([], [S(1)/3, S(2/3)]) # PFDD assert can_do([], [S(2)/3, S(4/3)]) # PFDD assert can_do([], [S(5)/3, S(4/3)]) # PFDD # XXX this does not *evaluate* right?? assert can_do([], [a, a + S.Half, 2*a-1]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_cse.py0000644000175000017500000001014112014170666026255 0ustar georgeskgeorgeskimport itertools from sympy import Add, Mul, Pow, Symbol, exp, sqrt, symbols, sympify, cse from sympy.simplify import cse_main, cse_opts from sympy.utilities.pytest import XFAIL w,x,y,z = symbols('w,x,y,z') x0,x1,x2 = list(itertools.islice(cse_main.numbered_symbols(), 0, 3)) negone = sympify(-1) def test_numbered_symbols(): ns = cse_main.numbered_symbols(prefix='y') assert list(itertools.islice(ns, 0, 10)) == [Symbol('y%s'%i) for i in range(0, 10)] ns = cse_main.numbered_symbols(prefix='y') assert list(itertools.islice(ns, 10, 20)) == [Symbol('y%s'%i) for i in range(10, 20)] ns = cse_main.numbered_symbols() assert list(itertools.islice(ns, 0, 10)) == [Symbol('x%s'%i) for i in range(0, 10)] # Dummy "optimization" functions for testing. def opt1(expr): return expr+y def opt2(expr): return expr*z def test_preprocess_for_cse(): assert cse_main.preprocess_for_cse(x, [(opt1, None)]) == x+y assert cse_main.preprocess_for_cse(x, [(None, opt1)]) == x assert cse_main.preprocess_for_cse(x, [(None, None)]) == x assert cse_main.preprocess_for_cse(x, [(opt1, opt2)]) == x+y assert cse_main.preprocess_for_cse(x, [(opt1, None), (opt2, None)]) == (x+y)*z def test_postprocess_for_cse(): assert cse_main.postprocess_for_cse(x, [(opt1, None)]) == x assert cse_main.postprocess_for_cse(x, [(None, opt1)]) == x+y assert cse_main.postprocess_for_cse(x, [(None, None)]) == x assert cse_main.postprocess_for_cse(x, [(opt1, opt2)]) == x*z # Note the reverse order of application. assert cse_main.postprocess_for_cse(x, [(None, opt1), (None, opt2)]) == x*z+y def test_cse_single(): # Simple substitution. e = Add(Pow(x+y,2), sqrt(x+y)) substs, reduced = cse([e], optimizations=[]) assert substs == [(x0, x+y)] assert reduced == [sqrt(x0) + x0**2] def test_cse_single2(): # Simple substitution, test for being able to pass the expression directly e = Add(Pow(x+y,2), sqrt(x+y)) substs, reduced = cse(e, optimizations=[]) assert substs == [(x0, x+y)] assert reduced == [sqrt(x0) + x0**2] def test_cse_not_possible(): # No substitution possible. e = Add(x,y) substs, reduced = cse([e], optimizations=[]) assert substs == [] assert reduced == [x+y] def test_nested_substitution(): # Substitution within a substitution. e = Add(Pow(w*x+y,2), sqrt(w*x+y)) substs, reduced = cse([e], optimizations=[]) assert substs == [(x0, w*x+y)] assert reduced == [sqrt(x0) + x0**2] def test_subtraction_opt(): # Make sure subtraction is optimized. e = (x-y)*(z-y) + exp((x-y)*(z-y)) substs, reduced = cse([e], optimizations=[(cse_opts.sub_pre,cse_opts.sub_post)]) assert substs == [(x0, (x - y)*(z - y))] assert reduced == [x0 + exp(x0)] def test_multiple_expressions(): e1 = (x+y)*z e2 = (x+y)*w substs, reduced = cse([e1, e2], optimizations=[]) assert substs == [(x0, x+y)] assert reduced == [x0*z, x0*w] l = [w*x*y + z, w*y] substs, reduced = cse(l) rsubsts, _ = cse(reversed(l)) assert substs == rsubsts assert reduced == [z + x*x0, x0] l = [w*x*y, w*x*y + z, w*y] substs, reduced = cse(l) rsubsts, _ = cse(reversed(l)) assert substs == rsubsts assert reduced == [x1, x1 + z, x0] l = [(x - z)*(y - z), x - z, y - z] substs, reduced = cse(l) rsubsts, _ = cse(reversed(l)) substitutions = [ [(x0, x - z), (x1, y - z)], [(x0, y - z), (x1, x - z)], ] assert substs in substitutions assert rsubsts in substitutions assert reduced == [x0*x1, x0, x1] l = [w*y + w + x + y + z, w*x*y] assert cse(l) == ([(x0, w*y)], [w + x + x0 + y + z, x*x0]) assert cse([x + y, x + y + z]) == ([(x0, x + y)], [x0, z + x0]) assert cse([x + y, x + z]) == ([], [x + y, x + z]) assert cse([x*y, x + x*y , x*y*z + 3]) == \ ([(x0, x*y)], [x0, x + x0, 3 + x0*z]) A, B, C = symbols('A B C', commutative=False) l = [A*B*C, A*C] assert cse(l) == ([], l) l = [A*B*C, A*B] assert cse(l) == ([(x0, A*B)], [x0*C, x0]) @XFAIL def test_powers(): assert cse(x*y**2 + x*y) == ([(x0, x*y)], [x0*y + x0]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_sqrtdenest.py0000644000175000017500000000135112014170666027702 0ustar georgeskgeorgeskfrom sympy import sqrt, Rational, sqrtdenest from sympy.utilities.pytest import XFAIL def test_sqrtdenest(): d = {sqrt(5 + 2 * sqrt(6)): sqrt(2) + sqrt(3), sqrt(sqrt(2)): sqrt(sqrt(2)), sqrt(5+sqrt(7)): sqrt(5+sqrt(7)), sqrt(3+sqrt(5+2*sqrt(7))): sqrt(6+3*sqrt(7))/(sqrt(2)*(5+2*sqrt(7))**Rational(1,4)) + 3*(5+2*sqrt(7))**Rational(1,4)/(sqrt(2)*sqrt(6+3*sqrt(7))), sqrt(3+2*sqrt(3)): 3**Rational(1,4)/sqrt(2)+3/(sqrt(2)*3**Rational(1,4))} for i in d: assert sqrtdenest(i) == d[i] # more complex example: @XFAIL # this fails on amd64 def test_sqrtdenest2(): assert sqrtdenest(sqrt(16-2*sqrt(29)+2*sqrt(55-10*sqrt(29)))) == \ sqrt(5) + sqrt(11-2*sqrt(29)) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_simplify.py0000644000175000017500000006453512014170666027357 0ustar georgeskgeorgeskfrom sympy import (Symbol, symbols, hypersimp, factorial, binomial, collect, Function, powsimp, separate, sin, exp, Rational, fraction, simplify, trigsimp, cos, tan, cot, log, ratsimp, Matrix, pi, integrate, solve, nsimplify, GoldenRatio, sqrt, E, I, sympify, atan, Derivative, S, diff, oo, Eq, Integer, gamma, acos, Integral, logcombine, Wild, separatevars, erf, rcollect, count_ops, combsimp, posify) from sympy.utilities.pytest import XFAIL from sympy.abc import x, y, z, t, a, b, c, d, e def test_ratsimp(): f, g = 1/x + 1/y, (x + y)/(x*y) assert f != g and ratsimp(f) == g f, g = 1/(1 + 1/x), 1 - 1/(x + 1) assert f != g and ratsimp(f) == g f, g = x/(x + y) + y/(x + y), 1 assert f != g and ratsimp(f) == g f, g = -x - y - y**2/(x + y) + x**2/(x + y), -2*y assert f != g and ratsimp(f) == g f = (a*c*x*y + a*c*z - b*d*x*y - b*d*z - b*t*x*y - b*t*x - b*t*z + e*x)/(x*y + z) G = [a*c - b*d - b*t + (-b*t*x + e*x)/(x*y + z), a*c - b*d - b*t - ( b*t*x - e*x)/(x*y + z)] assert f != g and ratsimp(f) in G A = sqrt(pi) B = log(erf(x) - 1) C = log(erf(x) + 1) D = 8 - 8*erf(x) f = A*B/D - A*C/D + A*C*erf(x)/D - A*B*erf(x)/D + 2*A/D assert ratsimp(f) == A*B/8 - A*C/8 - A/(4*erf(x) - 4) def test_trigsimp1(): x, y = symbols('x,y') assert trigsimp(1 - sin(x)**2) == cos(x)**2 assert trigsimp(1 - cos(x)**2) == sin(x)**2 assert trigsimp(sin(x)**2 + cos(x)**2) == 1 assert trigsimp(1 + tan(x)**2) == 1/cos(x)**2 assert trigsimp(1/cos(x)**2 - 1) == tan(x)**2 assert trigsimp(1/cos(x)**2 - tan(x)**2) == 1 assert trigsimp(1 + cot(x)**2) == 1/sin(x)**2 assert trigsimp(1/sin(x)**2 - 1) == cot(x)**2 assert trigsimp(1/sin(x)**2 - cot(x)**2) == 1 assert trigsimp(5*cos(x)**2 + 5*sin(x)**2) == 5 assert trigsimp(5*cos(x/2)**2 + 2*sin(x/2)**2) in \ [2 + 3*cos(x/2)**2, 5 - 3*sin(x/2)**2] assert trigsimp(sin(x)/cos(x)) == tan(x) assert trigsimp(2*tan(x)*cos(x)) == 2*sin(x) assert trigsimp(cot(x)**3*sin(x)**3) == cos(x)**3 assert trigsimp(y*tan(x)**2/sin(x)**2) == y/cos(x)**2 assert trigsimp(cot(x)/cos(x)) == 1/sin(x) assert trigsimp(cos(0.12345)**2 + sin(0.12345)**2) == 1 e = 2*sin(x)**2 + 2*cos(x)**2 assert trigsimp(log(e), deep=True) == log(2) def test_trigsimp2(): x, y = symbols('x,y') assert trigsimp(cos(x)**2*sin(y)**2 + cos(x)**2*cos(y)**2 + sin(x)**2, recursive=True) == 1 assert trigsimp(sin(x)**2*sin(y)**2 + sin(x)**2*cos(y)**2 + cos(x)**2, recursive=True) == 1 def test_issue1274(): x = Symbol("x") assert abs(trigsimp(2.0*sin(x)**2+2.0*cos(x)**2)-2.0) < 1e-10 def test_trigsimp3(): x, y = symbols('x,y') assert trigsimp(sin(x)/cos(x)) == tan(x) assert trigsimp(sin(x)**2/cos(x)**2) == tan(x)**2 assert trigsimp(sin(x)**3/cos(x)**3) == tan(x)**3 assert trigsimp(sin(x)**10/cos(x)**10) == tan(x)**10 assert trigsimp(cos(x)/sin(x)) == 1/tan(x) assert trigsimp(cos(x)**2/sin(x)**2) == 1/tan(x)**2 assert trigsimp(cos(x)**10/sin(x)**10) == 1/tan(x)**10 assert trigsimp(tan(x)) == trigsimp(sin(x)/cos(x)) def test_trigsimp_issue_2515(): x = Symbol('x') assert trigsimp(x*cos(x)*tan(x)) == x*sin(x) assert trigsimp(-sin(x)+cos(x)*tan(x)) == 0 @XFAIL def test_factorial_simplify(): # There are more tests in test_factorials.py. These are just to # ensure that simplify() calls factorial_simplify correctly from sympy.specfun.factorials import factorial x = Symbol('x') assert simplify(factorial(x)/x) == factorial(x-1) assert simplify(factorial(factorial(x))) == factorial(factorial(x)) def test_simplify(): x, y, z, k, n, m, w, f, s, A = symbols('x,y,z,k,n,m,w,f,s,A') assert all(simplify(tmp) == tmp for tmp in [I, E, oo, x, -x, -oo, -E, -I]) e = 1/x + 1/y assert e != (x+y)/(x*y) assert simplify(e) == (x+y)/(x*y) e = A**2*s**4/(4*pi*k*m**3) assert simplify(e) == e e = (4+4*x-2*(2+2*x))/(2+2*x) assert simplify(e) == 0 e = (-4*x*y**2-2*y**3-2*x**2*y)/(x+y)**2 assert simplify(e) == -2*y e = -x-y-(x+y)**(-1)*y**2+(x+y)**(-1)*x**2 assert simplify(e) == -2*y e = (x+x*y)/x assert simplify(e) == 1 + y e = (f(x)+y*f(x))/f(x) assert simplify(e) == 1 + y e = (2 * (1/n - cos(n * pi)/n))/pi assert simplify(e) == 2*((1 - 1*cos(pi*n))/(pi*n)) e = integrate(1/(x**3+1), x).diff(x) assert simplify(e) == 1/(x**3+1) e = integrate(x/(x**2+3*x+1), x).diff(x) assert simplify(e) == x/(x**2+3*x+1) A = Matrix([[2*k-m*w**2, -k], [-k, k-m*w**2]]).inv() assert simplify((A*Matrix([0,f]))[1]) == \ (f*(2*k - m*w**2))/(k**2 - 3*k*m*w**2 + m**2*w**4) a, b, c, d, e, f, g, h, i = symbols('a,b,c,d,e,f,g,h,i') f_1 = x*a + y*b + z*c - 1 f_2 = x*d + y*e + z*f - 1 f_3 = x*g + y*h + z*i - 1 solutions = solve([f_1, f_2, f_3], x, y, z, simplified=False) assert simplify(solutions[y]) == \ (a*i+c*d+f*g-a*f-c*g-d*i)/(a*e*i+b*f*g+c*d*h-a*f*h-b*d*i-c*e*g) f = -x + y/(z + t) + z*x/(z + t) + z*a/(z + t) + t*x/(z + t) assert simplify(f) == (y + a*z)/(z + t) A, B = symbols('A,B', commutative=False) assert simplify(A*B - B*A) == A*B - B*A assert simplify(log(2) + log(3)) == log(6) assert simplify(log(2*x) - log(2)) == log(x) def test_simplify_other(): assert simplify(sin(x)**2 + cos(x)**2) == 1 assert simplify(gamma(x + 1)/gamma(x)) == x assert simplify(sin(x)**2 + cos(x)**2 + factorial(x)/gamma(x)) == 1 + x assert simplify(Eq(sin(x)**2 + cos(x)**2, factorial(x)/gamma(x))) == Eq(1, x) def test_simplify_ratio(): # roots of x**3-3*x+5 roots = ['(5/2 + 21**(1/2)/2)**(1/3)*(1/2 - I*3**(1/2)/2)' ' + 1/((1/2 - I*3**(1/2)/2)*(5/2 + 21**(1/2)/2)**(1/3))', '(5/2 + 21**(1/2)/2)**(1/3)*(1/2 + I*3**(1/2)/2)' ' + 1/((1/2 + I*3**(1/2)/2)*(5/2 + 21**(1/2)/2)**(1/3))', '-1/(5/2 + 21**(1/2)/2)**(1/3) - (5/2 + 21**(1/2)/2)**(1/3)'] for r in roots: r = S(r) assert count_ops(simplify(r, ratio=1)) <= count_ops(r) # If ratio=oo, simplify() is always applied: assert simplify(r, ratio=oo) is not r def test_simplify_issue_1308(): assert simplify(exp(-Rational(1, 2)) + exp(-Rational(3, 2))) == \ (1 + E)*exp(-Rational(3, 2)) assert simplify(exp(1)+exp(-exp(1))) == (1 + exp(1 + E))*exp(-E) def test_simplify_fail1(): x = Symbol('x') y = Symbol('y') e = (x+y)**2/(-4*x*y**2-2*y**3-2*x**2*y) assert simplify(e) == 1 / (-2*y) def test_fraction(): x, y, z = map(Symbol, 'xyz') assert fraction(Rational(1, 2)) == (1, 2) assert fraction(x) == (x, 1) assert fraction(1/x) == (1, x) assert fraction(x/y) == (x, y) assert fraction(x/2) == (x, 2) assert fraction(x*y/z) == (x*y, z) assert fraction(x/(y*z)) == (x, y*z) assert fraction(1/y**2) == (1, y**2) assert fraction(x/y**2) == (x, y**2) assert fraction((x**2+1)/y) == (x**2+1, y) assert fraction(x*(y+1)/y**7) == (x*(y+1), y**7) assert fraction(exp(-x), exact=True) == (exp(-x), 1) def test_separate(): x, y, z = symbols('x,y,z') assert separate((x*y*z)**4) == x**4*y**4*z**4 assert separate((x*y*z)**x).is_Pow assert separate((x*y*z)**x, force=True) == x**x*y**x*z**x assert separate((x*(y*z)**2)**3) == x**3*y**6*z**6 assert separate((sin((x*y)**2)*y)**z).is_Pow assert separate((sin((x*y)**2)*y)**z, force=True) == sin((x*y)**2)**z*y**z assert separate((sin((x*y)**2)*y)**z, deep=True) == (sin(x**2*y**2)*y)**z assert separate(exp(x)**2) == exp(2*x) assert separate((exp(x)*exp(y))**2) == exp(2*x)*exp(2*y) assert separate((exp((x*y)**z)*exp(y))**2) == exp(2*(x*y)**z)*exp(2*y) assert separate((exp((x*y)**z)*exp(y))**2, deep=True, force=True) == exp(2*x**z*y**z)*exp(2*y) assert separate((exp(x)*exp(y))**z).is_Pow assert separate((exp(x)*exp(y))**z, force=True) == exp(x*z)*exp(y*z) def test_powsimp(): x, y, z, n = symbols('x,y,z,n') f = Function('f') assert powsimp( 4**x * 2**(-x) * 2**(-x) ) == 1 assert powsimp( (-4)**x * (-2)**(-x) * 2**(-x) ) == 1 assert powsimp( f(4**x * 2**(-x) * 2**(-x)) ) == f(4**x * 2**(-x) * 2**(-x)) assert powsimp( f(4**x * 2**(-x) * 2**(-x)), deep = True ) == f(1) assert exp(x)*exp(y) == exp(x)*exp(y) assert powsimp(exp(x)*exp(y)) == exp(x+y) assert powsimp(exp(x)*exp(y)*2**x*2**y) == (2*E)**(x + y) assert powsimp(exp(x)*exp(y)*2**x*2**y, combine='exp') == exp(x+y)*2**(x+y) assert powsimp(exp(x)*exp(y)*exp(2)*sin(x)+sin(y)+2**x*2**y) == exp(2+x+y)*sin(x)+sin(y)+2**(x+y) assert powsimp(sin(exp(x)*exp(y))) == sin(exp(x)*exp(y)) assert powsimp(sin(exp(x)*exp(y)), deep=True) == sin(exp(x+y)) assert powsimp(x**2*x**y) == x**(2+y) # This should remain factored, because 'exp' with deep=True is supposed # to act like old automatic exponent combining. assert powsimp((1 + E*exp(E))*exp(-E), combine='exp', deep=True) == (1 + exp(1 + E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E), deep=True) == exp(1) + exp(-E) # This should not change without deep. Otherwise, simplify() will fail. assert powsimp((1 + E*exp(E))*exp(-E)) == (1 + E*exp(E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E), combine='exp') == (1 + E*exp(E))*exp(-E) assert powsimp((1 + E*exp(E))*exp(-E), combine='base') == (1 + E*exp(E))*exp(-E) x,y = symbols('x,y', nonnegative=True) n = Symbol('n', real=True) assert powsimp( y**n * (y/x)**(-n) ) == x**n assert powsimp(x**(x**(x*y)*y**(x*y))*y**(x**(x*y)*y**(x*y)),deep=True) == (x*y)**(x*y)**(x*y) assert powsimp(2**(2**(2*x)*x), deep=False) == 2**(2**(2*x)*x) assert powsimp(2**(2**(2*x)*x), deep=True) == 2**(x*4**x) assert powsimp(exp(-x + exp(-x)*exp(-x*log(x))), deep=False, combine='exp') == exp(-x + exp(-x)*exp(-x*log(x))) assert powsimp(exp(-x + exp(-x)*exp(-x*log(x))), deep=False, combine='exp') == exp(-x + exp(-x)*exp(-x*log(x))) assert powsimp((x+y)/(3*z), deep=False, combine='exp') == (x+y)/(3*z) assert powsimp((x/3+y/3)/z, deep=True, combine='exp') == (x/3+y/3)/z assert powsimp(exp(x)/(1 + exp(x)*exp(y)), deep=True) == exp(x)/(1 + exp(x + y)) assert powsimp(x*y**(z**x*z**y), deep=True) == x*y**(z**(x + y)) assert powsimp((z**x*z**y)**x, deep=True) == (z**(x + y))**x assert powsimp(x*(z**x*z**y)**x, deep=True) == x*(z**(x + y))**x p = symbols('p', positive=True) assert powsimp((1/x)**log(2)/x) == (1/x)**(1 + log(2)) assert powsimp((1/p)**log(2)/p) == p**(-1 - log(2)) # coefficient of exponent can only be simplified for positive bases assert powsimp(2**(2*x)) == 4**x assert powsimp((-1)**(2*x)) == (-1)**(2*x) i = symbols('i', integer=True) assert powsimp((-1)**(2*i)) == 1 assert powsimp((-1)**(-x)) != (-1)**x # could be 1/((-1)**x), but is not # force=True overrides assumptions assert powsimp((-1)**(2*x), force=True) == 1 def test_powsimp_nc(): x, y, z = symbols('x,y,z') A, B, C = symbols('A B C', commutative=False) assert powsimp(A**x*A**y, combine='all') == A**(x+y) assert powsimp(A**x*A**y, combine='base') == A**x*A**y assert powsimp(A**x*A**y, combine='exp') == A**(x+y) assert powsimp(A**x*B**x, combine='all') == (A*B)**x assert powsimp(A**x*B**x, combine='base') == (A*B)**x assert powsimp(A**x*B**x, combine='exp') == A**x*B**x assert powsimp(B**x*A**x, combine='all') == (B*A)**x assert powsimp(B**x*A**x, combine='base') == (B*A)**x assert powsimp(B**x*A**x, combine='exp') == B**x*A**x assert powsimp(A**x*A**y*A**z, combine='all') == A**(x+y+z) assert powsimp(A**x*A**y*A**z, combine='base') == A**x*A**y*A**z assert powsimp(A**x*A**y*A**z, combine='exp') == A**(x+y+z) assert powsimp(A**x*B**x*C**x, combine='all') == (A*B*C)**x assert powsimp(A**x*B**x*C**x, combine='base') == (A*B*C)**x assert powsimp(A**x*B**x*C**x, combine='exp') == A**x*B**x*C**x assert powsimp(B**x*A**x*C**x, combine='all') == (B*A*C)**x assert powsimp(B**x*A**x*C**x, combine='base') == (B*A*C)**x assert powsimp(B**x*A**x*C**x, combine='exp') == B**x*A**x*C**x def test_collect_1(): """Collect with respect to a Symbol""" x, y, z, n = symbols('x,y,z,n') assert collect( x + y*x, x ) == x * (1 + y) assert collect( x + x**2, x ) == x + x**2 assert collect( x**2 + y*x**2, x ) == (x**2)*(1+y) assert collect( x**2 + y*x, x ) == x*y + x**2 assert collect( 2*x**2 + y*x**2 + 3*x*y, [x] ) == x**2*(2+y) + 3*x*y assert collect( 2*x**2 + y*x**2 + 3*x*y, [y] ) == 2*x**2 + y*(x**2+3*x) assert collect( ((1 + y + x)**4).expand(), x) == ((1 + y)**4).expand() + \ x*(4*(1 + y)**3).expand() + x**2*(6*(1 + y)**2).expand() + \ x**3*(4*(1 + y)).expand() + x**4 # symbols can be given as any iterable expr = x + y assert collect(expr, expr.free_symbols) == expr def test_collect_2(): """Collect with respect to a sum""" a, b, x = symbols('a,b,x') assert collect(a*(cos(x)+sin(x)) + b*(cos(x)+sin(x)), sin(x)+cos(x)) == (a + b)*(cos(x) + sin(x)) def test_collect_3(): """Collect with respect to a product""" a, b, c = symbols('a,b,c') f = Function('f') x, y, z, n = symbols('x,y,z,n') assert collect(-x/8 + x*y, -x) == x*(y - S(1)/8) assert collect( 1 + x*(y**2), x*y ) == 1 + x*(y**2) assert collect( x*y + a*x*y, x*y) == x*y*(1 + a) assert collect( 1 + x*y + a*x*y, x*y) == 1 + x*y*(1 + a) assert collect(a*x*f(x) + b*(x*f(x)), x*f(x)) == x*(a + b)*f(x) assert collect(a*x*log(x) + b*(x*log(x)), x*log(x)) == x*(a + b)*log(x) assert collect(a*x**2*log(x)**2 + b*(x*log(x))**2, x*log(x)) == x**2*log(x)**2*(a + b) # with respect to a product of three symbols assert collect(y*x*z+a*x*y*z, x*y*z) == (1 + a)*x*y*z def test_collect_4(): """Collect with respect to a power""" a, b, c, x = symbols('a,b,c,x') assert collect(a*x**c + b*x**c, x**c) == x**c*(a + b) assert collect(a*x**(2*c) + b*x**(2*c), x**c) == (x**2)**c*(a + b) def test_collect_5(): """Collect with respect to a tuple""" a, x, y, z, n = symbols('a,x,y,z,n') assert collect(x**2*y**4 + z*(x*y**2)**2 + z + a*z, [x*y**2, z]) in [ z*(1 + a + x**2*y**4) + x**2*y**4, z*(1 + a) + x**2*y**4*(1 + z) ] assert collect((1+ (x+y) + (x+y)**2).expand(), [x, y]) == 1 + y + x*(1 + 2*y) + x**2 + y**2 def test_collect_D(): D = Derivative f = Function('f') x, a, b = symbols('x,a,b') fx = D(f(x), x) fxx = D(f(x), x, x) assert collect(a*fx + b*fx, fx) == (a + b)*fx assert collect(a*D(fx, x) + b*D(fx, x), fx) == (a + b)*D(fx, x) assert collect(a*fxx + b*fxx , fx) == (a + b)*D(fx, x) # 1685 assert collect(5*f(x)+3*fx, fx) == 5*f(x) + 3*fx assert collect(f(x) + f(x)*diff(f(x), x) + x*diff(f(x), x)*f(x), f(x).diff(x)) ==\ (x*f(x) + f(x))*D(f(x), x) + f(x) assert collect(f(x) + f(x)*diff(f(x), x) + x*diff(f(x), x)*f(x), f(x).diff(x), exact=True) ==\ (x*f(x) + f(x))*D(f(x), x) + f(x) assert collect(1/f(x) + 1/f(x)*diff(f(x), x) + x*diff(f(x), x)/f(x), f(x).diff(x), exact=True) ==\ (1/f(x) + x/f(x))*D(f(x), x) + 1/f(x) @XFAIL def collect_issues(): assert collect(1/f(x) + 1/f(x)*diff(f(x), x) + x*diff(f(x), x)/f(x), f(x).diff(x)) !=\ (1 + x*D(f(x), x) + D(f(x), x))/f(x) def test_collect_D_0(): D = Derivative f = Function('f') x, a, b = symbols('x,a,b') fxx = D(f(x), x, x) # collect does not distinguish nested derivatives, so it returns # -- (a + b)*D(D(f, x), x) assert collect(a*fxx + b*fxx , fxx) == (a + b)*fxx def test_collect_Wild(): """Collect with respect to functions with Wild argument""" a, b, x, y = symbols('a b x y') f = Function('f') w1 = Wild('.1') w2 = Wild('.2') assert collect(f(x) + a*f(x), f(w1)) == (1 + a)*f(x) assert collect(f(x, y) + a*f(x, y), f(w1)) == f(x, y) + a*f(x, y) assert collect(f(x, y) + a*f(x, y), f(w1, w2)) == (1 + a)*f(x, y) assert collect(f(x, y) + a*f(x, y), f(w1, w1)) == f(x, y) + a*f(x, y) assert collect(f(x, x) + a*f(x, x), f(w1, w1)) == (1 + a)*f(x, x) assert collect(a*(x + 1)**y + (x + 1)**y, w1**y) == (1 + a)*(x + 1)**y assert collect(a*(x + 1)**y + (x + 1)**y, w1**b) == a*(x + 1)**y + (x + 1)**y assert collect(a*(x + 1)**y + (x + 1)**y, (x + 1)**w2) == (1 + a)*(x + 1)**y assert collect(a*(x + 1)**y + (x + 1)**y, w1**w2) == (1 + a)*(x + 1)**y def test_rcollect(): assert rcollect((x**2*y + x*y + x + y)/(x + y), y) == (x + y*(1 + x + x**2))/(x + y) assert rcollect(sqrt(-((x + 1)*(y + 1))), z) == sqrt(-((x + 1)*(y + 1))) def test_separatevars(): x,y,z,n = symbols('x,y,z,n') assert separatevars(2*n*x*z+2*x*y*z) == 2*x*z*(n+y) assert separatevars(x*z+x*y*z) == x*z*(1+y) assert separatevars(pi*x*z+pi*x*y*z) == pi*x*z*(1+y) assert separatevars(x*y**2*sin(x) + x*sin(x)*sin(y)) == x*(sin(y) + y**2)*sin(x) assert separatevars(x*exp(x+y)+x*exp(x)) == x*(1 + exp(y))*exp(x) assert separatevars((x*(y+1))**z) == x**z*(1 + y)**z assert separatevars(1+x+y+x*y) == (x+1)*(y+1) assert separatevars(y / pi * exp(-(z - x) / cos(n))) == y * exp((x - z) / cos(n)) / pi # 1759 p=Symbol('p',positive=True) assert separatevars(sqrt(p**2 + x*p**2)) == p*sqrt(1 + x) assert separatevars(sqrt(y*(p**2 + x*p**2))) == p*sqrt(y*(1 + x)) assert separatevars(sqrt(y*(p**2 + x*p**2)), force=True) == p*sqrt(y)*sqrt(1 + x) # 1766 assert separatevars(sqrt(x*y)).is_Pow assert separatevars(sqrt(x*y), force=True) == sqrt(x)*sqrt(y) @XFAIL def test_separation_by_factor(): x,y = symbols('x,y') assert factor(sqrt(x*y), expand=False).is_Pow def test_separatevars_advanced_factor(): x,y,z = symbols('x,y,z') assert separatevars(1 + log(x)*log(y) + log(x) + log(y)) == (log(x) + 1)*(log(y) + 1) assert separatevars(1 + x - log(z) - x*log(z) - exp(y)*log(z) - x*exp(y)*log(z) + x*exp(y) + exp(y)) == \ -((x + 1)*(log(z) - 1)*(exp(y) + 1)) x, y = symbols('x,y', positive=True) assert separatevars(1 + log(x**log(y)) + log(x*y)) == (log(x) + 1)*(log(y) + 1) def test_hypersimp(): n, k = symbols('n,k', integer=True) assert hypersimp(factorial(k), k) == k + 1 assert hypersimp(factorial(k**2), k) is None assert hypersimp(1/factorial(k), k) == 1/(k + 1) assert hypersimp(2**k/factorial(k)**2, k) == 2/(k**2+2*k+1) assert hypersimp(binomial(n, k), k) == (n-k)/(k+1) assert hypersimp(binomial(n+1, k), k) == (n-k+1)/(k+1) term = (4*k+1)*factorial(k)/factorial(2*k+1) assert hypersimp(term, k) == (S(1)/2)*((4*k + 5)/(3 + 14*k + 8*k**2)) term = 1/((2*k-1)*factorial(2*k+1)) assert hypersimp(term, k) == (2*k - 1)/(3 + 11*k + 12*k**2 + 4*k**3)/2 term = binomial(n, k)*(-1)**k/factorial(k) assert hypersimp(term, k) == (k - n)/(k**2+2*k+1) def test_nsimplify(): x = Symbol("x") assert nsimplify(0) == 0 assert nsimplify(-1) == -1 assert nsimplify(1) == 1 assert nsimplify(1+x) == 1+x assert nsimplify(2.7) == Rational(27, 10) assert nsimplify(1-GoldenRatio) == (1-sqrt(5))/2 assert nsimplify((1+sqrt(5))/4, [GoldenRatio]) == GoldenRatio/2 assert nsimplify(2/GoldenRatio, [GoldenRatio]) == 2*GoldenRatio - 2 assert nsimplify(exp(5*pi*I/3, evaluate=False)) == sympify('1/2 - I*3**(1/2)/2') assert nsimplify(sin(3*pi/5, evaluate=False)) == sympify('(5/8 + 1/8*5**(1/2))**(1/2)') assert nsimplify(sqrt(atan('1', evaluate=False))*(2+I), [pi]) == sqrt(pi) + sqrt(pi)/2*I assert nsimplify(2 + exp(2*atan('1/4')*I)) == sympify('49/17 + 8*I/17') assert nsimplify(pi, tolerance=0.01) == Rational(22, 7) assert nsimplify(pi, tolerance=0.001) == Rational(355, 113) assert nsimplify(0.33333, tolerance=1e-4) == Rational(1, 3) assert nsimplify(2.0**(1/3.), tolerance=0.001) == Rational(635, 504) assert nsimplify(2.0**(1/3.), tolerance=0.001, full=True) == 2**Rational(1, 3) assert nsimplify(x + .5, rational=True) == Rational(1, 2) + x assert nsimplify(1/.3 + x, rational=True) == Rational(10, 3) + x assert nsimplify(log(3).n(), rational=True) == \ sympify('109861228866811/100000000000000') def test_extract_minus_sign(): x = Symbol("x") y = Symbol("y") a = Symbol("a") b = Symbol("b") assert simplify(-x/-y) == x/y assert simplify(-x/y) == -x/y assert simplify(x/y) == x/y assert simplify(x/-y) == -x/y assert simplify(-x/0) == -oo*x assert simplify(S(-5)/0) == -oo assert simplify(-a*x/(-y-b)) == a*x/(b + y) def test_diff(): x = Symbol("x") y = Symbol("y") f = Function("f") g = Function("g") assert simplify(g(x).diff(x)*f(x).diff(x)-f(x).diff(x)*g(x).diff(x)) == 0 assert simplify(2*f(x)*f(x).diff(x)-diff(f(x)**2, x)) == 0 assert simplify(diff(1/f(x), x)+f(x).diff(x)/f(x)**2) == 0 assert simplify(f(x).diff(x, y)-f(x).diff(y, x)) == 0 def test_logcombine_1(): x, y = symbols("x,y") a = Symbol("a") z, w = symbols("z,w", positive=True) b = Symbol("b", real=True) assert logcombine(log(x)+2*log(y)) == log(x) + 2*log(y) assert logcombine(log(x)+2*log(y), force=True) == log(x*y**2) assert logcombine(a*log(w)+log(z)) == a*log(w) + log(z) assert logcombine(b*log(z)+b*log(x)) == log(z**b) + b*log(x) assert logcombine(b*log(z)-log(w)) == log(z**b/w) assert logcombine(log(x)*log(z)) == log(x)*log(z) assert logcombine(log(w)*log(x)) == log(w)*log(x) assert logcombine(cos(-2*log(z)+b*log(w))) == cos(log(w**b/z**2)) assert logcombine(log(log(x)-log(y))-log(z), force=True) == \ log(log((x/y)**(1/z))) assert logcombine((2+I)*log(x), force=True) == I*log(x)+log(x**2) assert logcombine((x**2+log(x)-log(y))/(x*y), force=True) == \ log(x**(1/(x*y))*y**(-1/(x*y)))+x/y assert logcombine(log(x)*2*log(y)+log(z), force=True) == \ log(z*y**log(x**2)) assert logcombine((x*y+sqrt(x**4+y**4)+log(x)-log(y))/(pi*x**Rational(2, 3)*\ y**Rational(3, 2)), force=True) == \ log(x**(1/(pi*x**Rational(2, 3)*y**Rational(3, 2)))*y**(-1/(pi*\ x**Rational(2, 3)*y**Rational(3, 2)))) + (x**4 + y**4)**Rational(1, 2)/(pi*\ x**Rational(2, 3)*y**Rational(3, 2)) + x**Rational(1, 3)/(pi*y**Rational(1, 2)) assert logcombine(Eq(log(x), -2*log(y)), force=True) == \ Eq(log(x*y**2), Integer(0)) assert logcombine(Eq(y, x*acos(-log(x/y))), force=True) == \ Eq(y, x*acos(log(y/x))) assert logcombine(gamma(-log(x/y))*acos(-log(x/y)), force=True) == \ acos(log(y/x))*gamma(log(y/x)) assert logcombine((2+3*I)*log(x), force=True) == \ log(x**2)+3*I*log(x) assert logcombine(Eq(y, -log(x)), force=True) == Eq(y, log(1/x)) assert logcombine(Integral((sin(x**2)+cos(x**3))/x, x), force=True) == \ Integral((sin(x**2)+cos(x**3))/x, x) assert logcombine(Integral((sin(x**2)+cos(x**3))/x, x)+ (2+3*I)*log(x), \ force=True) == log(x**2)+3*I*log(x) + \ Integral((sin(x**2)+cos(x**3))/x, x) def test_posify(): from sympy.abc import x assert str(posify( x + Symbol('p', positive=True) + Symbol('n', negative=True))) == '(_x + n + p, {_x: x})' # log(1/x).expand() should be log(1/x) but it comes back as -log(x) # when it is corrected, posify will allow the change to be made. The # force=True option can do so as well when it is implemented. eq, rep = posify(1/x) assert log(eq).expand().subs(rep) == -log(x) assert str(posify([x, 1 + x])) == '([_x, _x + 1], {_x: x})' x = symbols('x') p = symbols('p', positive=True) n = symbols('n', negative=True) orig = [x, n, p] modified, reps = posify(orig) assert str(modified) == '[_x, n, p]' assert [w.subs(reps) for w in modified] == orig def test_powdenest(): from sympy import powdenest from sympy.abc import x, y, z, a, b p = symbols('p', positive=True) i, j = symbols('i,j', integer=1) assert powdenest(x) == x assert powdenest(x + 2*(x**(2*a/3))**(3*x)) == x + 2*(x**(a/3))**(6*x) assert powdenest((exp(2*a/3))**(3*x)) == (exp(a/3))**(6*x) assert powdenest((x**(2*a/3))**(3*x)) == (x**(a/3))**(6*x) assert powdenest(exp(3*x*log(2))) == 2**(3*x) assert powdenest(sqrt(p**2)) == p i, j = symbols('i,j', integer=1) assert powdenest((x**x)**(i + j)) # -X-> (x**x)**i*(x**x)**j == x**(x*(i + j)) assert powdenest(exp(3*y*log(x))) == x**(3*y) assert powdenest(exp(y*(log(a) + log(b)))) == (a*b)**y assert powdenest(exp(3*(log(a) + log(b)))) == a**3*b**3 assert powdenest(((x**(2*i))**(3*y))**x) == ((x**(2*i))**(3*y))**x assert powdenest(((x**(2*i))**(3*y))**x, force=True) == x**(6*i*x*y) assert powdenest(((x**(2*a/3))**(3*y/i))**x) == ((x**(a/3))**(y/i))**(6*x) assert powdenest((x**(2*i)*y**(4*i))**z, force=True) == (x*y**2)**(2*i*z) e = ((x**2*y**4)**a)**(x*y) assert powdenest(e) == e e = (((x**2*y**4)**a)**(x*y))**3 assert powdenest(e) == ((x**2*y**4)**a)**(3*x*y) @XFAIL def test_powdenest_fail_in_polys(): from sympy import powdenest from sympy.abc import x, y, z, a, b assert powdenest((((x**2*y**4)**a)**(x*y)), force=True) == (x**2*y**4)**(a*x*y) assert powdenest((((x**2*y**4)**a)**(x*y))**3, force=True) == (x**2*y**4)**(3*a*x*y) def test_issue_1095(): # simplify should call cancel from sympy.abc import x, y f = Function('f') assert simplify((4*x+6*f(y))/(2*x+3*f(y))) == 2 def test_combsimp(): from sympy.abc import n, k assert combsimp(factorial(n)) == factorial(n) assert combsimp(binomial(n, k)) == binomial(n, k) assert combsimp(factorial(n)/factorial(n - 3)) == n*(-1 + n)*(-2 + n) assert combsimp(binomial(n + 1, k + 1)/binomial(n, k)) == (1 + n)/(1 + k) assert combsimp(binomial(3*n + 4, n + 1)/binomial(3*n + 1, n)) == \ S(3)/2*((3*n + 2)*(3*n + 4)/((n + 1)*(2*n + 3))) assert combsimp(factorial(n)**2/factorial(n - 3)) == factorial(n)*n*(-1 + n)*(-2 + n) assert combsimp(factorial(n)*binomial(n+1, k+1)/binomial(n, k)) == factorial(n)*(1 + n)/(1 + k) assert combsimp(binomial(n - 1, k)) == -((-n + k)*binomial(n, k))/n assert combsimp(binomial(n + 2, k + S(1)/2)) == \ 4*((n + 1)*(n + 2)*binomial(n, k + S(1)/2))/((2*k - 2*n - 1)*(2*k - 2*n - 3)) assert combsimp(binomial(n + 2, k + 2.0)) == \ -((1.0*n + 2.0)*binomial(n + 1.0, k + 2.0))/(k - n) def test_issue_2516(): aA, Re, a, b, D = symbols('aA Re a b D') e=((D**3*a + b*aA**3)/Re).expand() assert collect(e, [aA**3/Re, a]) == e wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/tests/test_traversaltools.py0000644000175000017500000000155212014170666030575 0ustar georgeskgeorgesk"""Tools for applying functions to specified parts of expressions. """ from sympy.simplify.traversaltools import use from sympy import expand, factor, I from sympy.abc import x, y def test_use(): assert use(0, expand) == 0 f = (x + y)**2*x + 1 assert use(f, expand, level=0) == x**3 + 2*x**2*y + x*y**2 + + 1 assert use(f, expand, level=1) == x**3 + 2*x**2*y + x*y**2 + + 1 assert use(f, expand, level=2) == 1 + x*(2*x*y + x**2 + y**2) assert use(f, expand, level=3) == (x + y)**2*x + 1 f = (x**2 + 1)**2 - 1 kwargs = {'gaussian': True} assert use(f, factor, level=0, kwargs=kwargs) == x**2*(x**2 + 2) assert use(f, factor, level=1, kwargs=kwargs) == (x + I)**2*(x - I)**2 - 1 assert use(f, factor, level=2, kwargs=kwargs) == (x + I)**2*(x - I)**2 - 1 assert use(f, factor, level=3, kwargs=kwargs) == (x**2 + 1)**2 - 1 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/simplify/__init__.py0000644000175000017500000000111612014170666025043 0ustar georgeskgeorgesk"""The module helps converting sympy expressions into shorter forms of them. for example: the expression E**(pi*I) will be converted into -1 the expression (x+x)**2 will be converted into 4*x**2 """ from simplify import collect, rcollect, separate, radsimp, ratsimp, fraction, \ simplify, trigsimp, powsimp, combsimp, hypersimp, hypersimilar, nsimplify, \ logcombine, separatevars, numer, denom, powdenest, posify from sqrtdenest import sqrtdenest from cse_main import cse from traversaltools import use from epathtools import epath, EPath from hyperexpand import hyperexpand wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/0000755000175000017500000000000012014170666023112 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/cythonutils.py0000644000175000017500000000056312014170666026055 0ustar georgeskgeorgesk"""Helper module for cooperation with Cython. """ from sympy.external import import_module cython = import_module('cython') if cython: def cythonized(specs): arg_types = {} for spec in specs.split(','): arg_types[spec] = cython.int return cython.locals(**arg_types) else: def cythonized(specs): return lambda f: f wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/misc.py0000644000175000017500000000304112014170666024415 0ustar georgeskgeorgesk"""Miscellaneous stuff that doesn't really fit anywhere else.""" from sympy.core import sympify def default_sort_key(item): """ A default sort key for lists of SymPy objects to pass to functions like sorted(). This uses the default ordering. If you want a nonstandard ordering, you will have to create your own sort key using the sort_key() method of the object. **Examples** >>> from sympy import Basic, S, I, default_sort_key >>> from sympy.abc import x >>> sorted([S(1)/2, I, -I], key=default_sort_key) [1/2, -I, I] >>> a = [S(1)/2, I, -I] >>> a.sort(key=default_sort_key) >>> a [1/2, -I, I] >>> b = S("[x, 1/x, 1/x**2, x**2, x**(1/2), x**(1/4), x**(3/2)]") >>> b.sort(key=default_sort_key) The built-in functions min() and max() also take a key function (in Python 2.5 or higher), that this can be used for. """ #XXX: The following should also be in the docstring, but orders do not # actually work at the moment. # To use a nonstandard order, you must create your own sort key. The default # order is lex. # >>> from sympy import sympify # >>> mykey = lambda item: sympify(item).sort_key(order='rev-lex') # >>> sorted([x, x**2, 1], key=default_sort_key) # [x**2, x, 1] # >>> sorted([x, x**2, 1], key=mykey) # [1, x, x**2] return sympify(item).sort_key() import sys size = getattr(sys, "maxint", None) if size is None: #Python 3 doesn't have maxint size = sys.maxsize if size > 2**32: ARCH = "64-bit" else: ARCH = "32-bit" wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/source.py0000644000175000017500000000251012014170666024762 0ustar georgeskgeorgesk""" This module adds several functions for interactive source code inspection. """ import inspect from sympy.core.compatibility import callable def source(object): """ Prints the source code of a given object. """ print 'In file: %s' % inspect.getsourcefile(object) print inspect.getsource(object) def get_class(lookup_view): """ Convert a string version of a class name to the object. For example, get_class('sympy.core.Basic') will return class Basic located in module sympy.core """ if isinstance(lookup_view, str): lookup_view = lookup_view mod_name, func_name = get_mod_func(lookup_view) if func_name != '': lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name) if not callable(lookup_view): raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name)) return lookup_view def get_mod_func(callback): """ splits the string path to a class into a string path to the module and the name of the class. For example: >>> from sympy.utilities.source import get_mod_func >>> get_mod_func('sympy.core.basic.Basic') ('sympy.core.basic', 'Basic') """ dot = callback.rfind('.') if dot == -1: return callback, '' return callback[:dot], callback[dot+1:] wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/compilef.py0000644000175000017500000004344312014170666025272 0ustar georgeskgeorgesk# needs access to libtcc and math.h # TODO: *get tcc errors (currently something like 'Unknown error 3217941984', # this makes debugging painful) # *currently the compiled function accepts too many arguments silently # *implement multi-dimensional functions for frange # *list comprehension syntax for frange? # *configuration of path to libtcc.so # *add gcc support again (easier to set up than tcc) # *fix compiler warnings # heavily inspired by http://www.cs.tut.fi/~ask/cinpy/ """ Experimental module for compiling functions to machine code. Can also be used to generate C code from SymPy expressions. Depends on libtcc. This code is experimental. It may have severe bugs. Due to the use of C, it's able to crash your Python interpreter/debugger with obscure error messages. 64 bit floats (double) are used. Overview ======== clambdify: compile a function to machine code (only useful for big functions) frange: evaluate a function on a range of numbers using machine code cexpr: translate a Python expression to a C expression genfcode: generate C code from a lambda string evanonarray: evaluate a function on an array using machine code Performance =========== Python functions using the math module are *quite* fast. For simple functions they are faster than functions compiled to machine code. So you should test to see whether lambdify is fast enough for you. Iterating is slow in Python (it's probably the biggest bottle neck). frange allows you to iterate using machine code. This can result in huge speedups. You might want to use NumPy: http://numpy.org/ For simple functions it's faster, but for big ones frange can be several times more efficient. You should experiment to see which solution is best for your application. You can run the included benchmarks to see the real performance on your machine. Configuration ============= You will probably need to compile libtcc on your own. Get the sources of tcc: http://bellard.org/tcc/ Currently it only works for a recent development version. So you might want to run the following commands (you have to use your own paths of course): $ cvs -z3 -d:pserver:anonymous@cvs.savannah.nongnu.org:/sources/tinycc co tinycc $ cd tinycc $ ./configure $ make $ gcc -shared -Wl,-soname,libtcc.so -o libtcc.so libtcc.o $ cd sympy/utilities/ $ ln -s tinycc/libtcc.so # or change libtccpath in compilef.py You might try to run libtcc_test. If something went wrong there will be bad low level Python errors probably crashing the interpreter. The error output will be printed to stdout or stderr, which might be different to your Python shell. Make sure that this module knows the path to libtcc. If everything went right, all the tests will pass. Run this file to do so and to see the results of some benchmarks. """ import os import ctypes from sympy import Symbol, cse, sympify from sympy.utilities.lambdify import lambdastr as getlambdastr from sympy.external import import_module numpy = import_module('numpy') libtccpath = './libtcc.so' dps = 17 # decimal places of float precision # load libtcc TODO: better Windows support libtcc = ctypes.cdll.LoadLibrary(libtccpath) if not libtcc: raise ImportError('Could not load libtcc') def __getLeftRight(expr, index, oplength=1, stopchar='+-'): """ Gets the expressions to the left and right of an operator. >>> __getLeftRight('1/(g(x)*3.5)**(x - a**x)/(x**2 + a)', 12, ... oplength=2, stopchar='+-*/') ('(g(x)*3.5)', '(x - a**x)') """ # assumes correct syntax # TODO: never repeat yourself # get left expression left = '' openbraces = 0 for char in reversed(expr[:index]): if char == ' ': # skip whitespaces but keep them left = char + left continue elif char == ')': openbraces += 1 left = char + left elif char == '(': if not openbraces: # happens when operator is in braces break openbraces -= 1 left = char + left elif char in stopchar: if openbraces: left = char + left continue else: break else: left = char + left # get right expression right = '' openbraces = 0 for char in expr[index+oplength:]: if char == ' ': # skip whitespaces but keep them right += char continue elif char == '(': openbraces += 1 right += char elif char == ')': if not openbraces: # happens when operator is in braces break openbraces -= 1 right += char elif char in stopchar: if openbraces: right += char continue else: break else: right += char return (left, right) def cexpr(pyexpr): """ Python math expression string -> C expression string """ # TODO: better spacing # replace 'a**b' with 'pow(a, b)' while True: index = pyexpr.find('**') if index != -1: left, right = __getLeftRight(pyexpr, index, 2, '+-*/') pyexpr = pyexpr.replace(left + '**' + right, ' pow(%s, %s) ' % (left.lstrip(), right.rstrip())) else: break # TODO: convert 'x**n' to 'x*x*...*x' # TODO: avoid integer division return pyexpr def _gentmpvars(): """ Generate symbols tmp1, tmp2, ... infinitely. """ i = 0 while True: i += 1 yield Symbol('tmp' + str(i)) def genfcode(lambdastr, use_cse=False): """ Python lambda string -> C function code Optionally cse() is used to eliminate common subexpressions. """ # TODO: verify lambda string # interpret lambda string varstr, fstr = lambdastr.split(': ') varstr = varstr.lstrip('lambda ') # generate C variable string cvars = varstr.split(',') cvarstr = '' for v in cvars: cvarstr += 'double %s, ' % v cvarstr = cvarstr.rstrip(', ') # convert function string to C syntax if not use_cse: cfstr = '' finalexpr = cexpr(fstr) else: # eliminate common subexpressions subs, finalexpr = cse(sympify(fstr), _gentmpvars()) assert len(finalexpr) == 1 vardec = '' cfstr = '' for symbol, expr in subs: vardec += ' double %s;\n' % symbol.name cfstr += ' %s = %s;\n' % (symbol.name, cexpr(str(expr.evalf(dps)))) cfstr = vardec + cfstr finalexpr = cexpr(str(finalexpr[0].evalf(dps))) # generate C code code = """ inline double f(%s) { %s return %s; } """ % (cvarstr, cfstr, finalexpr) return code def __run(cmd): """ Checks the exit code of a ran command. """ if not cmd == 0: raise RuntimeError('could not run libtcc command') def _compile(code, argcount=None, fname='f', fprototype=None): """ C code with function -> compiled function Supports all standard C math functions, pi and e. Function is assumed to get and return 'double' only. Uses libtcc. """ # returned type and all arguments are double if fprototype: fprototype = ctypes.CFUNCTYPE(*fprototype) else: assert argcount, 'need argcount if no prototype is specified' fprototype = ctypes.CFUNCTYPE(*[ctypes.c_double]*(argcount+1)) # see libtcc.h for API documentation tccstate = libtcc.tcc_new() __run(libtcc.tcc_set_output_type(tccstate, 0)) # output to memory ##print libtcc.tcc_add_library_path(tccstate, mathh) # could be dropped __run(libtcc.tcc_add_library(tccstate, 'm')) # use math.h FIXME: Windows # compile string __run(libtcc.tcc_compile_string(tccstate, code)) __run(libtcc.tcc_relocate(tccstate)) # fails if link error # create C variable to get result symbol = ctypes.c_long() __run(libtcc.tcc_get_symbol(tccstate, ctypes.byref(symbol), fname)) # return reference to C function return fprototype(symbol.value) # expr needs to work with lambdastr def clambdify(args, expr, **kwargs): """ SymPy expression -> compiled function Supports all standard C math functions, pi and e. >>> from sympy import symbols, sqrt >>> from sympy.abc import x, y >>> cf = clambdify((x,y), sqrt(x*y)) >>> cf(0.5, 4) 1.4142135623730951 """ # convert function to lambda string s = getlambdastr(args, expr.evalf(21)) # generate code code = """ # include # define pi M_PI # define e M_E %s """ % genfcode(s, **kwargs) # compile code return _compile(code, len(args)) def frange(*args, **kwargs): """ frange(lambdastr, [start,] stop[, step]) -> ctypes double array Evaluates function on range using machine code. Currently only one-dimensional functions are supported. For simple functions it's somewhat slower than NumPy. For big functions it can be several times faster. lambdastr has the same restrictions as in clambdify. >>> frange('lambda x: sqrt(x)', 1, 4) # doctest: +ELLIPSIS <__main__.c_double_Array_3 object at ...> >>> for i in _: ... print i ... 1.0 1.41421356237 1.73205080757 """ if len(args) > 4: raise TypeError('expected at most 4 arguments, got %i' % len(args)) if len(args) < 2: raise TypeError('expected at least 2 argument, got %i' % len(args)) # interpret arguments lambdastr = args[0] start = 0 step = 1 if len(args) == 2: stop = args[1] elif len(args) >= 3: start = args[1] stop = args[2] if len(args) == 4: step = args[3] assert start + step != start, \ 'step is too small and would cause an infinite loop' # determine length of resulting array # TODO: do this better length = stop - start if length % step == 0: length = length/step - 1 # exclude last one else: length = length//step if step > 0: if start < stop: length += 1 # include first one else: if start > stop: length += 1 # include first one if length < 0: length = 0 assert length == int(length) length = int(length) # create array a = (ctypes.c_double * length)() # generate code vardef = 'double* MAX; double x = %f;' % start loopbody = '*result = f(x); x += %f;' % step code = """ # include # define pi M_PI # define e M_E %s void evalonrange(double *result, int n) { %s for (MAX = result + n; result < MAX; result++) { %s } } """ % (genfcode(lambdastr, **kwargs), vardef, loopbody) # compile and run evalonrange = _compile(code, fname='evalonrange', fprototype=[None, ctypes.c_void_p, ctypes.c_int]) evalonrange(ctypes.byref(a), ctypes.c_int(length)) # return ctypes array with results return a def evalonarray(lambdastr, array, length=None, **kwargs): """ Evaluates a function on an array using machine code. array can be a numpy array, a ctypes array or a pointer to an array. In the latter case, the correct length must be specified. array will be overwritten! Make a copy before to avoid this. """ # interpret arguments if hasattr(array, 'ctypes'): # numpy array pointer = array.ctypes.get_as_parameter() length = len(array) elif isinstance(array, ctypes.Array): # ctypes array pointer = ctypes.byref(array) length = len(array) elif isinstance(array, ctypes.c_void_p): # ctypes pointer FIXME pointer = array assert isinstance(length, int) and not length < 0 else: raise ValueError('array type not recognized') # generate code code = """ # include # define pi M_PI # define e M_E %s void evalonarray(double *array, int length) { double* MAX; for (MAX = array + length; array < MAX; array++) { *array = f(*array); } } """ % genfcode(lambdastr, **kwargs) # compile an run on array run = _compile(code, fname='evalonarray', fprototype=[None, ctypes.c_void_p, ctypes.c_int]) run(pointer, length) ######### # TESTS # ######### from sympy import sqrt, pi, lambdify from math import exp, cos, sin def test_cexpr(): expr = '1/(g(x)*3.5)**(x - a**x)/(x**2 + a)' assert cexpr(expr).replace(' ', '') == \ '1/pow((g(x)*3.5),(x-pow(a,x)))/(pow(x,2)+a)' def test_clambdify(): x = Symbol('x') y = Symbol('y') z = Symbol('z') f1 = sqrt(x*y) pf1 = lambdify((x, y), f1, 'math') cf1 = clambdify((x, y), f1) for i in xrange(10): assert cf1(i, 10 - i) == pf1(i, 10 - i) f2 = (x - y) / z * pi pf2 = lambdify((x, y, z), f2, 'math') cf2 = clambdify((x, y, z), f2) assert round(pf2(1, 2, 3), 14) == round(cf2(1, 2, 3), 14) # FIXME: slight difference in precision def test_frange(): fstr = 'lambda x: exp(x)*cos(x)**x' f = eval(fstr) a = frange(fstr, 30, 168, 3) args = range(30, 168, 3) assert len(a) == len(args) for i in xrange(len(a)): assert a[i] == f(args[i]) assert len(frange('lambda x: x', 0, -10000)) == 0 assert len(frange('lambda x: x', -1, -1, 0.0001)) == 0 a = frange('lambda x: x', -5, 5, 0.1) b = range(-50, 50) assert len(a) == len(b) for i in xrange(len(a)): assert int(round(a[i]*10)) == b[i] a = frange('lambda x: x', 17, -9, -3) b = range(17, -9, -3) assert len(a) == len(b) for i in xrange(len(a)): assert a[i] == b[i] a = frange('lambda x: x', 2.7, -3.1, -1.01) b = range(270, -310, -101) assert len(a) == len(b) for i in xrange(len(a)): assert int(round(a[i]*100)) == b[i] assert frange('lambda x: x', 0.2, 0.1, -0.1)[0] == 0.2 assert len(frange('lambda x: x', 0)) == 0 assert len(frange('lambda x: x', 1000, -1)) == 0 assert len(frange('lambda x: x', -1.23, 3.21, -0.0000001)) == 0 try: frange() assert False except TypeError: pass try: frange(1, 2, 3, 4, 5) assert False except TypeError: pass def test_evalonarray_ctypes(): a = frange('lambda x: x', 10) evalonarray('lambda x: sin(x)', a) for i, j in enumerate(a): assert sin(i) == j # TODO: test for ctypes pointers ## evalonarray('lambda x: asin(x)', ctypes.byref(a), len(a)) ## for i, j in enumerater(a): ## print j def test_evalonarray_numpy(): a = numpy.arange(10, dtype=float) evalonarray('lambda x: x + 1', a) for i, j in enumerate(a): assert float(i + 1) == j def test_use_cse(): args = ('lambda x: sqrt(x + 1)**sqrt(x + 1)', 1, 10) a = frange(*args) kwargs = {} kwargs['use_cse'] = True b = frange(*args, **kwargs) assert len(a) == len(b) for i in xrange(len(a)): assert a[i] == b[i] def benchmark(): """ Run some benchmarks for clambdify and frange. NumPy and Psyco are used as reference if available. """ from time import time from timeit import Timer def fbenchmark(f, var=[Symbol('x')]): """ Do some benchmarks with f using clambdify, lambdify and psyco. """ global cf, pf, psyf start = time() cf = clambdify(var, f) print 'compile time (including sympy overhead): %f s' % (time() - start) pf = lambdify(var, f, 'math') psyf = None psyco = import_module('psyco') if psyco: psyf = lambdify(var, f, 'math') psyco.bind(psyf) code = '''for x in (i/1000. for i in range(1000)): f(%s)''' % ('x,'*len(var)).rstrip(',') t1 = Timer(code, 'from __main__ import cf as f') t2 = Timer(code, 'from __main__ import pf as f') if psyf: t3 = Timer(code, 'from __main__ import psyf as f') else: t3 = None print 'for x = (0, 1, 2, ..., 999)/1000' print '20 times in 3 runs' print 'compiled: %.4f %.4f %.4f' % tuple(t1.repeat(3, 20)) print 'Python lambda: %.4f %.4f %.4f' % tuple(t2.repeat(3, 20)) if t3: print 'Psyco lambda: %.4f %.4f %.4f' % tuple(t3.repeat(3, 20)) print 'big function:' from sympy import diff, exp, sin, cos, pi, lambdify x = Symbol('x') ## f1 = diff(exp(x)**2 - sin(x)**pi, x) \ ## * x**12-2*x**3+2*exp(x**2)-3*x**7+4*exp(123+x-x**5+2*x**4) \ ## * ((x + pi)**5).expand() f1 = 2*exp(x**2) + x**12*(-pi*sin(x)**((-1) + pi)*cos(x) + 2*exp(2*x)) \ + 4*(10*pi**3*x**2 + 10*pi**2*x**3 + 5*pi*x**4 + 5*x*pi**4 + pi**5 \ + x**5)*exp(123 + x + 2*x**4 - x**5) - 2*x**3 - 3*x**7 fbenchmark(f1) print print 'simple function:' y = Symbol('y') f2 = sqrt(x*y)+x*5 fbenchmark(f2, [x,y]) times = 100000 fstr = 'exp(sin(exp(-x**2)) + sqrt(pi)*cos(x**5/(x**3-x**2+pi*x)))' print print 'frange with f(x) =' print fstr print 'for x=1, ..., %i' % times print 'in 3 runs including full compile time' t4 = Timer("frange('lambda x: %s', 0, %i)" % (fstr, times), 'from __main__ import frange') numpy = import_module('numpy') print 'frange: %.4f %.4f %.4f' % tuple(t4.repeat(3, 1)) if numpy: t5 = Timer('x = arange(%i); result = %s' % (times, fstr), 'from numpy import arange, sqrt, exp, sin, cos, exp, pi') print 'numpy: %.4f %.4f %.4f' % tuple(t5.repeat(3, 1)) # TODO: integration into fbenchmark if __name__ == '__main__': if __debug__: print 'Running tests...', test_cexpr() test_clambdify() test_frange() test_evalonarray_ctypes() if numpy: test_evalonarray_numpy() test_use_cse() import doctest doctest.testmod() print 'OK' print print 'Running benchmark...' benchmark() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/benchmarking.py0000644000175000017500000001435012014170666026117 0ustar georgeskgeorgesk"""benchmarking through py.test""" import py from py.__.test.item import Item from py.__.test.terminal.terminal import TerminalSession from math import ceil, floor, log10 from time import time import timeit from inspect import getsource # from IPython.Magic.magic_timeit #units = ["s", "ms", "\xc2\xb5s", "ns"] units = ["s", "ms", "us", "ns"] scaling = [1, 1e3, 1e6, 1e9] unitn = dict((s,i) for i,s in enumerate(units)) precision = 3 # like py.test Directory but scan for 'bench_.py' class Directory(py.test.collect.Directory): def filefilter(self, path): b = path.purebasename ext = path.ext return b.startswith('bench_') and ext == '.py' # like py.test Module but scane for 'bench_' and 'timeit_' class Module(py.test.collect.Module): def funcnamefilter(self, name): return name.startswith('bench_') or name.startswith('timeit_') # Function level benchmarking driver class Timer(timeit.Timer): def __init__(self, stmt, setup='pass', timer=timeit.default_timer, globals=globals()): # copy of timeit.Timer.__init__ # similarity index 95% self.timer = timer stmt = timeit.reindent(stmt, 8) setup = timeit.reindent(setup, 4) src = timeit.template % {'stmt': stmt, 'setup': setup} self.src = src # Save for traceback display code = compile(src, timeit.dummy_src_name, "exec") ns = {} #exec code in globals(), ns -- original timeit code exec code in globals, ns # -- we use caller-provided globals instead self.inner = ns["inner"] class Function(py.__.test.item.Function): def __init__(self, *args, **kw): super(Function, self).__init__(*args, **kw) self.benchtime = None self.benchtitle = None def execute(self, target, *args): # get func source without first 'def func(...):' line src = getsource(target) src = '\n'.join( src.splitlines()[1:] ) # extract benchmark title if target.func_doc is not None: self.benchtitle = target.func_doc else: self.benchtitle = src.splitlines()[0].strip() # XXX we ignore args timer = Timer(src, globals=target.func_globals) if self.name.startswith('timeit_'): # from IPython.Magic.magic_timeit repeat = 3 number = 1 for i in range(1,10): t = timer.timeit(number) if t >= 0.2: number *= (0.2 / t) number = int(ceil(number)) break if t <= 0.02: # we are not close enough to that 0.2s number *= 10 else: # since we are very close to be > 0.2s we'd better adjust number # so that timing time is not too high number *= (0.2 / t) number = int(ceil(number)) break self.benchtime = min(timer.repeat(repeat, number)) / number # 'bench_' else: self.benchtime = timer.timeit(1) class BenchSession(TerminalSession): def header(self, colitems): #self.out.sep("-", "benchmarking starts") super(BenchSession, self).header(colitems) def footer(self, colitems): super(BenchSession, self).footer(colitems) #self.out.sep("-", "benchmarking ends") self.out.write('\n') self.print_bench_results() def print_bench_results(self): self.out.write('==============================\n') self.out.write(' *** BENCHMARKING RESULTS *** \n') self.out.write('==============================\n') self.out.write('\n') # benchname, time, benchtitle results = [] for item, outcome in self._memo: if isinstance(item, Item): best = item.benchtime if best is None: # skipped or failed benchmarks tstr = '---' else: # from IPython.Magic.magic_timeit if best > 0.0: order = min(-int(floor(log10(best)) // 3), 3) else: order = 3 tstr = "%.*g %s" % (precision, best * scaling[order], units[order]) results.append( [item.name, tstr, item.benchtitle] ) # dot/unit align second column # FIXME simpler? this is crappy -- shame on me... wm = [0]*len(units) we = [0]*len(units) for s in results: tstr = s[1] n,u = tstr.split() # unit n un = unitn[u] try: m,e = n.split('.') except ValueError: m,e = n,'' wm[un] = max(len(m), wm[un]) we[un] = max(len(e), we[un]) for s in results: tstr = s[1] n,u = tstr.split() un = unitn[u] try: m,e = n.split('.') except ValueError: m,e = n,'' m = m.rjust(wm[un]) e = e.ljust(we[un]) if e.strip(): n = '.'.join((m,e)) else: n = ' '.join((m,e)) # let's put the number into the right place txt = '' for i in range(len(units)): if i == un: txt += n else: txt += ' '*(wm[i]+we[i]+1) s[1] = '%s %s' % (txt, u) # align all columns besides the last one for i in range(2): w = max(len(s[i]) for s in results) for s in results: s[i] = s[i].ljust(w) # show results for s in results: self.out.write('%s | %s | %s\n' % tuple(s)) def main(args=None): # hook our Directory/Module/Function as defaults from py.__.test import defaultconftest defaultconftest.Directory = Directory defaultconftest.Module = Module defaultconftest.Function = Function # hook BenchSession as py.test session config = py.test.config config._getsessionclass = lambda : BenchSession py.test.cmdline.main(args) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/autowrap.py0000644000175000017500000003772212014170666025341 0ustar georgeskgeorgesk"""Module for compiling codegen output, and wrap the binary for use in python. .. note:: To use the autowrap module it must first be imported >>> from sympy.utilities.autowrap import autowrap This module provides a common interface for different external backends, such as f2py, fwrap, Cython, SWIG(?) etc. (Currently only f2py and Cython are implemented) The goal is to provide access to compiled binaries of acceptable performance with a one-button user interface, i.e. >>> from sympy.abc import x,y >>> expr = ((x - y)**(25)).expand() >>> binary_callable = autowrap(expr) # doctest: +SKIP >>> binary_callable(1, 2) # doctest: +SKIP -1.0 The callable returned from autowrap() is a binary python function, not a Sympy object. If it is desired to use the compiled function in symbolic expressions, it is better to use binary_function() which returns a Sympy Function object. The binary callable is attached as the _imp_ attribute and invoked when a numerical evaluation is requested with evalf(), or with lambdify(). >>> from sympy.utilities.autowrap import binary_function >>> f = binary_function('f', expr) # doctest: +SKIP >>> 2*f(x, y) + y # doctest: +SKIP y + 2*f(x, y) >>> (2*f(x, y) + y).evalf(2, subs={x: 1, y:2}) # doctest: +SKIP 0.0 The idea is that a SymPy user will primarily be interested in working with mathematical expressions, and should not have to learn details about wrapping tools in order to evaluate expressions numerically, even if they are computationally expensive. When is this useful? 1) For computations on large arrays, Python iterations may be too slow, and depending on the mathematical expression, it may be difficult to exploit the advanced index operations provided by NumPy. 2) For *really* long expressions that will be called repeatedly, the compiled binary should be significantly faster than SymPy's .evalf() 3) If you are generating code with the codegen utility in order to use it in another project, the automatic python wrappers let you test the binaries immediately from within SymPy. 4) To create customized ufuncs for use with numpy arrays. See :ref:`ufuncify` When is this module NOT the best approach? 1) If you are really concerned about speed or memory optimizations, you will probably get better results by working directly with the wrapper tools and the low level code. However, the files generated by this utility may provide a useful starting point and reference code. Temporary files will be left intact if you supply the keyword tempdir="path/to/files/". 2) If the array computation can be handled easily by numpy, and you don't need the binaries for another project. """ from __future__ import with_statement import sys import os import shutil import tempfile import subprocess from sympy.utilities.codegen import ( codegen, get_code_generator, Routine, OutputArgument, InOutArgument, CodeGenArgumentListError, Result ) from sympy.utilities.lambdify import implemented_function from sympy import C class CodeWrapError(Exception): pass class CodeWrapper: """Base Class for code wrappers""" _filename = "wrapped_code" _module_basename = "wrapper_module" _module_counter = 0 @property def filename(self): return "%s_%s" % (self._filename, CodeWrapper._module_counter) @property def module_name(self): return "%s_%s" % (self._module_basename, CodeWrapper._module_counter) def __init__(self, generator, filepath=None, flags=[], verbose=False): """ generator -- the code generator to use """ self.generator = generator self.filepath = filepath self.flags = flags self.quiet = not verbose @property def include_header(self): return bool(self.filepath) @property def include_empty(self): return bool(self.filepath) def _generate_code(self, main_routine, routines): routines.append(main_routine) self.generator.write(routines, self.filename, True, self.include_header, self.include_empty) def wrap_code(self, routine, helpers=[]): workdir = self.filepath or tempfile.mkdtemp("_sympy_compile") if not os.access(workdir, os.F_OK): os.mkdir(workdir) oldwork = os.getcwd() os.chdir(workdir) try: sys.path.append(workdir) self._generate_code(routine, helpers) self._prepare_files(routine) self._process_files(routine) mod = __import__(self.module_name) finally: sys.path.remove(workdir) CodeWrapper._module_counter +=1 os.chdir(oldwork) if not self.filepath: shutil.rmtree(workdir) return self._get_wrapped_function(mod) def _process_files(self, routine): command = self.command command.extend(self.flags) null = open(os.devnull, 'w') try: if self.quiet: retcode = subprocess.call(command, stdout=null, stderr=subprocess.STDOUT) else: retcode = subprocess.call(command) except OSError: retcode = 1 if retcode: raise CodeWrapError( "Error while executing command: %s" % " ".join(command)) class DummyWrapper(CodeWrapper): """Class used for testing independent of backends """ template = """# dummy module for testing of Sympy def %(name)s(): return "%(expr)s" %(name)s.args = "%(args)s" %(name)s.returns = "%(retvals)s" """ def _prepare_files(self, routine): return def _generate_code(self, routine, helpers): with open('%s.py' % self.module_name, 'w') as f: printed = ", ".join([str(res.expr) for res in routine.result_variables]) # convert OutputArguments to return value like f2py inargs = filter(lambda x: not isinstance(x, OutputArgument), routine.arguments) retvals = [] for val in routine.result_variables: if isinstance(val, Result): retvals.append('nameless') else: retvals.append(val.result_var) print >> f, DummyWrapper.template % { 'name': routine.name, 'expr': printed, 'args': ", ".join([str(arg.name) for arg in inargs]), 'retvals': ", ".join([str(val) for val in retvals]) } def _process_files(self, routine): return @classmethod def _get_wrapped_function(cls, mod): return mod.autofunc class CythonCodeWrapper(CodeWrapper): """Wrapper that uses Cython""" setup_template = """ from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext setup( cmdclass = {'build_ext': build_ext}, ext_modules = [Extension(%(args)s)] ) """ @property def command(self): command = [sys.executable, "setup.py", "build_ext", "--inplace"] return command def _prepare_files(self, routine): pyxfilename = self.module_name + '.pyx' codefilename = "%s.%s" % (self.filename, self.generator.code_extension) # pyx with open(pyxfilename, 'w') as f: self.dump_pyx([routine], f, self.filename, self.include_header, self.include_empty) # setup.py ext_args = [repr(self.module_name), repr([pyxfilename, codefilename])] with open('setup.py', 'w') as f: print >> f, CythonCodeWrapper.setup_template % {'args': ", ".join(ext_args)} @classmethod def _get_wrapped_function(cls, mod): return mod.autofunc_c def dump_pyx(self, routines, f, prefix, header=True, empty=True): """Write a Cython file with python wrappers This file contains all the definitions of the routines in c code and refers to the header file. :Arguments: routines List of Routine instances f File-like object to write the file to prefix The filename prefix, used to refer to the proper header file. Only the basename of the prefix is used. empty Optional. When True, empty lines are included to structure the source files. [DEFAULT=True] """ for routine in routines: prototype = self.generator.get_prototype(routine) origname = routine.name routine.name = "%s_c" % origname prototype_c = self.generator.get_prototype(routine) routine.name = origname # declare print >> f, 'cdef extern from "%s.h":' % prefix print >> f, ' %s' % prototype if empty: print >> f # wrap ret, args_py = self._split_retvals_inargs(routine.arguments) args_c = ", ".join([str(a.name) for a in routine.arguments]) print >> f, "def %s_c(%s):" % (routine.name, ", ".join(self._declare_arg(arg) for arg in args_py)) for r in ret: if not r in args_py: print >> f, " cdef %s" % self._declare_arg(r) rets = ", ".join([str(r.name) for r in ret]) if routine.results: call = ' return %s(%s)' % (routine.name, args_c) if rets: print >> f, call + ', ' + rets else: print >> f, call else: print >> f, ' %s(%s)' % (routine.name, args_c) print >> f, ' return %s' % rets if empty: print >> f dump_pyx.extension = "pyx" def _split_retvals_inargs(self, args): """Determines arguments and return values for python wrapper""" py_args = [] py_returns = [] for arg in args: if isinstance(arg, OutputArgument): py_returns.append(arg) elif isinstance(arg, InOutArgument): py_returns.append(arg) py_args.append(arg) else: py_args.append(arg) return py_returns, py_args def _declare_arg(self, arg): t = arg.get_datatype('c') if arg.dimensions: return "%s *%s"%(t, str(arg.name)) else: return "%s %s"%(t, str(arg.name)) class F2PyCodeWrapper(CodeWrapper): """Wrapper that uses f2py""" @property def command(self): filename = self.filename + '.' + self.generator.code_extension command = ["f2py", "-m", self.module_name, "-c" , filename] return command def _prepare_files(self, routine): pass @classmethod def _get_wrapped_function(cls, mod): return mod.autofunc def _get_code_wrapper_class(backend): wrappers = { 'F2PY': F2PyCodeWrapper, 'CYTHON': CythonCodeWrapper, 'DUMMY': DummyWrapper} return wrappers[backend.upper()] def autowrap(expr, language='F95', backend='f2py', tempdir=None, args=None, flags=[], verbose=False, helpers=[]): """Generates python callable binaries based on the math expression. expr The SymPy expression that should be wrapped as a binary routine :Optional arguments: language The programming language to use, currently 'C' or 'F95' backend The wrapper backend to use, currently f2py or Cython tempdir Path to directory for temporary files. If this argument is supplied, the generated code and the wrapper input files are left intact in the specified path. args Sequence of the formal parameters of the generated code, if ommited the function signature is determined by the code generator. flags Additional option flags that will be passed to the backend verbose If True, autowrap will not mute the command line backends. This can be helpful for debugging. helpers Used to define auxillary expressions needed for the main expr. If the main expression need to do call a specialized function it should be put in the `helpers' list. Autowrap will then make sure that the compiled main expression can link to the helper routine. Items should be tuples with (, , ). It is mandatory to supply an argument sequence to helper routines. >>> from sympy.abc import x, y, z >>> from sympy.utilities.autowrap import autowrap >>> expr = ((x - y + z)**(13)).expand() >>> binary_func = autowrap(expr) # doctest: +SKIP >>> binary_func(1, 4, 2) # doctest: +SKIP -1.0 """ code_generator = get_code_generator(language, "autowrap") CodeWrapperClass = _get_code_wrapper_class(backend) code_wrapper = CodeWrapperClass(code_generator, tempdir, flags, verbose) try: routine = Routine('autofunc', expr, args) except CodeGenArgumentListError, e: # if all missing arguments are for pure output, we simply attach them # at the end and try again, because the wrappers will silently convert # them to return values anyway. new_args = [] for missing in e.missing_args: if not isinstance(missing, OutputArgument): raise new_args.append(missing.name) routine = Routine('autofunc', expr, args + new_args) helps = [] for name, expr, args in helpers: helps.append(Routine(name, expr, args)) return code_wrapper.wrap_code(routine, helpers=helps) def binary_function(symfunc, expr, **kwargs): """Returns a sympy function with expr as binary implementation This is a convenience function that automates the steps needed to autowrap the Sympy expression and attaching it to a Function object with implemented_function(). >>> from sympy.abc import x, y, z >>> from sympy.utilities.autowrap import binary_function >>> expr = ((x - y)**(25)).expand() >>> f = binary_function('f', expr) # doctest: +SKIP >>> type(f) # doctest: +SKIP >>> 2*f(x, y) # doctest: +SKIP 2*f(x, y) >>> f(x, y).evalf(2, subs={x: 1, y: 2}) # doctest: +SKIP -1.0 """ binary = autowrap(expr, **kwargs) return implemented_function(symfunc, binary) def ufuncify(args, expr, **kwargs): """Generates a binary ufunc-like lambda function for numpy arrays ``args`` Either a Symbol or a tuple of symbols. Specifies the argument sequence for the ufunc-like function. ``expr`` A Sympy expression that defines the element wise operation ``kwargs`` Optional keyword arguments are forwarded to autowrap(). The returned function can only act on one array at a time, as only the first argument accept arrays as input. .. Note:: a *proper* numpy ufunc is required to support broadcasting, type casting and more. The function returned here, may not qualify for numpy's definition of a ufunc. That why we use the term ufunc-like. See http://docs.scipy.org/doc/numpy/reference/ufuncs.html :Examples: >>> from sympy.utilities.autowrap import ufuncify >>> from sympy.abc import x, y, z >>> f = ufuncify([x, y], y + x**2) # doctest: +SKIP >>> f([1, 2, 3], 2) # doctest: +SKIP [2. 5. 10.] """ y = C.IndexedBase(C.Dummy('y')) x = C.IndexedBase(C.Dummy('x')) m = C.Dummy('m', integer=True) i = C.Dummy('i', integer=True) i = C.Idx(i, m) l = C.Lambda(args, expr) f = implemented_function('f', l) if isinstance(args, C.Symbol): args = [args] else: args = list(args) # first argument accepts an array args[0] = x[i] return autowrap(C.Equality(y[i], f(*args)), **kwargs) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/pytest.py0000644000175000017500000001052212014170666025014 0ustar georgeskgeorgesk"""py.test hacks to support XFAIL/XPASS""" # XXX this should be integrated into py.test # XXX but we can't force everyone to install py-lib trunk import sys try: # functools is not available in Python 2.4 import functools except ImportError: has_functools = False else: has_functools = True try: # tested with py-lib 0.9.0 from py.__.test.outcome import Outcome, Passed, Failed, Skipped from py.__.test.terminal.terminal import TerminalSession from py.test import skip USE_PYTEST = True except ImportError: USE_PYTEST = False def raises(ExpectedException, code): """ Tests that ``code`` raises the exception ``ExpectedException``. Does nothing if the right exception is raised, otherwise raises an AssertionError. Example: >>> from sympy.utilities.pytest import raises >>> raises(ZeroDivisionError, "1/0") >>> raises(ZeroDivisionError, "1/2") Traceback (most recent call last): ... AssertionError: DID NOT RAISE """ if not isinstance(code, str): raise TypeError('raises() expects a code string for the 2nd argument.') frame = sys._getframe(1) loc = frame.f_locals.copy() try: exec code in frame.f_globals, loc except ExpectedException: return raise AssertionError("DID NOT RAISE") if not USE_PYTEST: class XFail(Exception): pass class XPass(Exception): pass class Skipped(Exception): pass def XFAIL(func): def wrapper(): try: func() except Exception: raise XFail() raise XPass() if has_functools: wrapper = functools.update_wrapper(wrapper, func) return wrapper def skip(str): raise Skipped(str) else: from time import time as now __all__ = ['XFAIL'] class XFail(Outcome): pass class XPass(Outcome): pass TerminalSession.typemap[XFail] = 'f' TerminalSession.typemap[XPass] = 'X' TerminalSession.namemap[XFail] = 'XFAIL' TerminalSession.namemap[XPass] = '*** XPASS ***' def footer(self, colitems): super(TerminalSession, self).footer(colitems) self.endtime = now() self.out.line() self.skippedreasons() self.failures() self.xpasses() self.summaryline() def xpasses(self): """report unexpectedly passed tests""" texts = {} for colitem, outcome in self.getitemoutcomepairs(XPass): raisingtb = self.getlastvisible(outcome.excinfo.traceback) fn = raisingtb.frame.code.path lineno = raisingtb.lineno #d = texts.setdefault(outcome.excinfo.exconly(), {}) d = texts.setdefault(outcome.msg, {}) d[(fn,lineno)] = outcome if texts: self.out.line() self.out.sep('_', '*** XPASS ***') for text, dict in texts.items(): #for (fn, lineno), outcome in dict.items(): # self.out.line('Skipped in %s:%d' %(fn, lineno+1)) #self.out.line("reason: %s" % text) self.out.line("%s" % text) self.out.line() def summaryline(self): outlist = [] sum = 0 for typ in Passed, XPass, XFail, Failed, Skipped: l = self.getitemoutcomepairs(typ) if l: outlist.append('%d %s' % (len(l), typ.__name__.lower())) sum += len(l) elapsed = self.endtime-self.starttime status = "%s" % ", ".join(outlist) self.out.sep('=', 'tests finished: %s in %4.2f seconds' % (status, elapsed)) # SymPy specific if self.getitemoutcomepairs(Failed): self.out.line('DO *NOT* COMMIT!') TerminalSession.footer = footer TerminalSession.xpasses = xpasses TerminalSession.summaryline = summaryline def XFAIL(func): """XFAIL decorator""" def func_wrapper(): try: func() except Outcome: raise # pass-through test outcome except: raise XFail('XFAIL: %s' % func.func_name) else: raise XPass('XPASS: %s' % func.func_name) if has_functools: func_wrapper = functools.update_wrapper(func_wrapper, func) return func_wrapper wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/runtests.py0000644000175000017500000013211412014170666025355 0ustar georgeskgeorgesk""" This is our testing framework. Goals: * it should be compatible with py.test and operate very similarly (or identically) * doesn't require any external dependencies * preferably all the functionality should be in this file only * no magic, just import the test file and execute the test functions, that's it * portable """ import os import sys import inspect import traceback import pdb import re import linecache from fnmatch import fnmatch from timeit import default_timer as clock import doctest as pdoctest # avoid clashing with our doctest() function from doctest import DocTestFinder, DocTestRunner import re as pre import random # Use sys.stdout encoding for ouput. # This was only added to Python's doctest in Python 2.6, so we must duplicate # it here to make utf8 files work in Python 2.5. pdoctest._encoding = getattr(sys.__stdout__, 'encoding', None) or 'utf-8' def _indent(s, indent=4): """ Add the given number of space characters to the beginning of every non-blank line in `s`, and return the result. If the string `s` is Unicode, it is encoded using the stdout encoding and the `backslashreplace` error handler. """ if isinstance(s, unicode): s = s.encode(pdoctest._encoding, 'backslashreplace') # This regexp matches the start of non-blank lines: return re.sub('(?m)^(?!$)', indent*' ', s) pdoctest._indent = _indent def sys_normcase(f): if sys_case_insensitive: return f.lower() return f def convert_to_native_paths(lst): """ Converts a list of '/' separated paths into a list of native (os.sep separated) paths and converts to lowercase if the system is case insensitive. """ newlst = [] for i, rv in enumerate(lst): rv = os.path.join(*rv.split("/")) # on windows the slash after the colon is dropped if sys.platform == "win32": pos = rv.find(':') if pos != -1: if rv[pos+1] != '\\': rv = rv[:pos+1] + '\\' + rv[pos+1:] newlst.append(sys_normcase(rv)) return newlst def get_sympy_dir(): """ Returns the root sympy directory and set the global value indicating whether the system is case sensitive or not. """ global sys_case_insensitive this_file = os.path.abspath(__file__) sympy_dir = os.path.join(os.path.dirname(this_file), "..", "..") sympy_dir = os.path.normpath(sympy_dir) sys_case_insensitive = (os.path.isdir(sympy_dir) and os.path.isdir(sympy_dir.lower()) and os.path.isdir(sympy_dir.upper())) return sys_normcase(sympy_dir) def isgeneratorfunction(object): """ Return true if the object is a user-defined generator function. Generator function objects provides same attributes as functions. See isfunction.__doc__ for attributes listing. Adapted from Python 2.6. """ CO_GENERATOR = 0x20 if (inspect.isfunction(object) or inspect.ismethod(object)) and \ object.func_code.co_flags & CO_GENERATOR: return True return False def setup_pprint(): from sympy import pprint_use_unicode, init_printing # force pprint to be in ascii mode in doctests pprint_use_unicode(False) # hook our nice, hash-stable strprinter init_printing(pretty_print=False) def test(*paths, **kwargs): """ Run all tests in test_*.py files which match any of the given strings in `paths` or all tests if paths=[]. Notes: o if sort=False, tests are run in random order (not default). o paths can be entered in native system format or in unix, forward-slash format. Examples: >> import sympy Run all tests: >> sympy.test() Run one file: >> sympy.test("sympy/core/tests/test_basic.py") >> sympy.test("_basic") Run all tests in sympy/functions/ and some particular file: >> sympy.test("sympy/core/tests/test_basic.py", "sympy/functions") Run all tests in sympy/core and sympy/utilities: >> sympy.test("/core", "/util") Run specific test from a file: >> sympy.test("sympy/core/tests/test_basic.py", kw="test_equality") Run the tests with verbose mode on: >> sympy.test(verbose=True) Don't sort the test output: >> sympy.test(sort=False) Turn on post-mortem pdb: >> sympy.test(pdb=True) Turn off colors: >> sympy.test(colors=False) The traceback verboseness can be set to "short" or "no" (default is "short") >> sympy.test(tb='no') """ verbose = kwargs.get("verbose", False) tb = kwargs.get("tb", "short") kw = kwargs.get("kw", "") post_mortem = kwargs.get("pdb", False) colors = kwargs.get("colors", True) sort = kwargs.get("sort", True) seed = kwargs.get("seed", None) if seed is None: seed = random.randrange(100000000) r = PyTestReporter(verbose, tb, colors) t = SymPyTests(r, kw, post_mortem, seed) # Disable warnings for external modules import sympy.external sympy.external.importtools.WARN_OLD_VERSION = False sympy.external.importtools.WARN_NOT_INSTALLED = False test_files = t.get_test_files('sympy') if len(paths) == 0: t._testfiles.extend(test_files) else: paths = convert_to_native_paths(paths) matched = [] for f in test_files: basename = os.path.basename(f) for p in paths: if p in f or fnmatch(basename, p): matched.append(f) break t._testfiles.extend(matched) return t.test(sort=sort) def doctest(*paths, **kwargs): """ Runs doctests in all *py files in the sympy directory which match any of the given strings in `paths` or all tests if paths=[]. Note: o paths can be entered in native system format or in unix, forward-slash format. o files that are on the blacklist can be tested by providing their path; they are only excluded if no paths are given. Examples: >> import sympy Run all tests: >> sympy.doctest() Run one file: >> sympy.doctest("sympy/core/basic.py") >> sympy.doctest("polynomial.txt") Run all tests in sympy/functions/ and some particular file: >> sympy.doctest("/functions", "basic.py") Run any file having polynomial in its name, doc/src/modules/polynomial.txt, sympy\functions\special\polynomials.py, and sympy\polys\polynomial.py: >> sympy.doctest("polynomial") """ normal = kwargs.get("normal", False) verbose = kwargs.get("verbose", False) blacklist = kwargs.get("blacklist", []) blacklist.extend([ "doc/src/modules/mpmath", # needs to be fixed upstream "sympy/mpmath", # needs to be fixed upstream "doc/src/modules/plotting.txt", # generates live plots "sympy/plotting", # generates live plots "sympy/utilities/compilef.py", # needs tcc "sympy/utilities/autowrap.py", # needs installed compiler "sympy/galgebra/GA.py", # needs numpy "sympy/galgebra/latex_ex.py", # needs numpy "sympy/conftest.py", # needs py.test "sympy/utilities/benchmarking.py", # needs py.test ]) blacklist = convert_to_native_paths(blacklist) # Disable warnings for external modules import sympy.external sympy.external.importtools.WARN_OLD_VERSION = False sympy.external.importtools.WARN_NOT_INSTALLED = False r = PyTestReporter(verbose) t = SymPyDocTests(r, normal) test_files = t.get_test_files('sympy') not_blacklisted = [f for f in test_files if not any(b in f for b in blacklist)] if len(paths) == 0: t._testfiles.extend(not_blacklisted) else: # take only what was requested...but not blacklisted items # and allow for partial match anywhere or fnmatch of name paths = convert_to_native_paths(paths) matched = [] for f in not_blacklisted: basename = os.path.basename(f) for p in paths: if p in f or fnmatch(basename, p): matched.append(f) break t._testfiles.extend(matched) # run the tests and record the result for this *py portion of the tests if t._testfiles: failed = not t.test() else: failed = False # test *txt files only if we are running python newer than 2.4 if sys.version_info[:2] > (2,4): # N.B. # -------------------------------------------------------------------- # Here we test *.txt files at or below doc/src. Code from these must # be self supporting in terms of imports since there is no importing # of necessary modules by doctest.testfile. If you try to pass *.py # files through this they might fail because they will lack the needed # imports and smarter parsing that can be done with source code. # test_files = t.get_test_files('doc/src', '*.txt', init_only=False) test_files.sort() not_blacklisted = [f for f in test_files if not any(b in f for b in blacklist)] if len(paths) == 0: matched = not_blacklisted else: # Take only what was requested as long as it's not on the blacklist. # Paths were already made native in *py tests so don't repeat here. # There's no chance of having a *py file slip through since we # only have *txt files in test_files. matched = [] for f in not_blacklisted: basename = os.path.basename(f) for p in paths: if p in f or fnmatch(basename, p): matched.append(f) break setup_pprint() first_report = True for txt_file in matched: if not os.path.isfile(txt_file): continue old_displayhook = sys.displayhook try: # out = pdoctest.testfile(txt_file, module_relative=False, encoding='utf-8', # optionflags=pdoctest.ELLIPSIS | pdoctest.NORMALIZE_WHITESPACE) out = sympytestfile(txt_file, module_relative=False, encoding='utf-8', optionflags=pdoctest.ELLIPSIS | pdoctest.NORMALIZE_WHITESPACE) finally: # make sure we return to the original displayhook in case some # doctest has changed that sys.displayhook = old_displayhook txtfailed, tested = out if tested: failed = txtfailed or failed if first_report: first_report = False msg = 'txt doctests start' lhead = '='*((80 - len(msg))//2 - 1) rhead = '='*(79 - len(msg) - len(lhead) - 1) print ' '.join([lhead, msg, rhead]) print # use as the id, everything past the first 'sympy' file_id = txt_file[txt_file.find('sympy') + len('sympy') + 1:] print file_id, # get at least the name out so it is know who is being tested wid = 80 - len(file_id) - 1 #update width test_file = '[%s]' % (tested) report = '[%s]' % (txtfailed or 'OK') print ''.join([test_file,' '*(wid-len(test_file)-len(report)), report]) # the doctests for *py will have printed this message already if there was # a failure, so now only print it if there was intervening reporting by # testing the *txt as evidenced by first_report no longer being True. if not first_report and failed: print print("DO *NOT* COMMIT!") return not failed # The Python 2.5 doctest runner uses a tuple, but in 2.6+, it uses a namedtuple # (which doesn't exist in 2.5-) if sys.version_info[:2] > (2,5): from collections import namedtuple SymPyTestResults = namedtuple('TestResults', 'failed attempted') else: SymPyTestResults = lambda a, b: (a, b) def sympytestfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=pdoctest.DocTestParser(), encoding=None): """ Test examples in the given file. Return (#failures, #tests). Optional keyword arg "module_relative" specifies how filenames should be interpreted: - If "module_relative" is True (the default), then "filename" specifies a module-relative path. By default, this path is relative to the calling module's directory; but if the "package" argument is specified, then it is relative to that package. To ensure os-independence, "filename" should use "/" characters to separate path segments, and should not be an absolute path (i.e., it may not begin with "/"). - If "module_relative" is False, then "filename" specifies an os-specific path. The path may be absolute or relative (to the current working directory). Optional keyword arg "name" gives the name of the test; by default use the file's basename. Optional keyword argument "package" is a Python package or the name of a Python package whose directory should be used as the base directory for a module relative filename. If no package is specified, then the calling module's directory is used as the base directory for module relative filenames. It is an error to specify "package" if "module_relative" is False. Optional keyword arg "globs" gives a dict to be used as the globals when executing examples; by default, use {}. A copy of this dict is actually used for each docstring, so that each docstring's examples start with a clean slate. Optional keyword arg "extraglobs" gives a dictionary that should be merged into the globals that are used to execute examples. By default, no extra globals are used. Optional keyword arg "verbose" prints lots of stuff if true, prints only failures if false; by default, it's true iff "-v" is in sys.argv. Optional keyword arg "report" prints a summary at the end when true, else prints nothing at the end. In verbose mode, the summary is detailed, else very brief (in fact, empty if all tests passed). Optional keyword arg "optionflags" or's together module constants, and defaults to 0. Possible values (see the docs for details): DONT_ACCEPT_TRUE_FOR_1 DONT_ACCEPT_BLANKLINE NORMALIZE_WHITESPACE ELLIPSIS SKIP IGNORE_EXCEPTION_DETAIL REPORT_UDIFF REPORT_CDIFF REPORT_NDIFF REPORT_ONLY_FIRST_FAILURE Optional keyword arg "raise_on_error" raises an exception on the first unexpected exception or failure. This allows failures to be post-mortem debugged. Optional keyword arg "parser" specifies a DocTestParser (or subclass) that should be used to extract tests from the files. Optional keyword arg "encoding" specifies an encoding that should be used to convert the file to unicode. Advanced tomfoolery: testmod runs methods of a local instance of class doctest.Tester, then merges the results into (or creates) global Tester instance doctest.master. Methods of doctest.master can be called directly too, if you want to do something unusual. Passing report=0 to testmod is especially useful then, to delay displaying a summary. Invoke doctest.master.summarize(verbose) when you're done fiddling. """ if package and not module_relative: raise ValueError("Package may only be specified for module-" "relative paths.") # Relativize the path text, filename = pdoctest._load_testfile(filename, package, module_relative) # If no name was given, then use the file's name. if name is None: name = os.path.basename(filename) # Assemble the globals. if globs is None: globs = {} else: globs = globs.copy() if extraglobs is not None: globs.update(extraglobs) if '__name__' not in globs: globs['__name__'] = '__main__' if raise_on_error: runner = pdoctest.DebugRunner(verbose=verbose, optionflags=optionflags) else: runner = SymPyDocTestRunner(verbose=verbose, optionflags=optionflags) if encoding is not None: text = text.decode(encoding) # Read the file, convert it to a test, and run it. test = parser.get_doctest(text, globs, name, filename, 0) runner.run(test) if report: runner.summarize() if pdoctest.master is None: pdoctest.master = runner else: pdoctest.master.merge(runner) return SymPyTestResults(runner.failures, runner.tries) class SymPyTests(object): def __init__(self, reporter, kw="", post_mortem=False, seed=random.random()): self._post_mortem = post_mortem self._kw = kw self._count = 0 self._root_dir = sympy_dir self._reporter = reporter self._reporter.root_dir(self._root_dir) self._testfiles = [] self._seed = seed def test(self, sort=False): """ Runs the tests returning True if all tests pass, otherwise False. If sort=False run tests in random order. """ if sort: self._testfiles.sort() else: from random import shuffle random.seed(self._seed) shuffle(self._testfiles) self._reporter.start(self._seed) for f in self._testfiles: try: self.test_file(f) except KeyboardInterrupt: print " interrupted by user" break return self._reporter.finish() def test_file(self, filename): name = "test%d" % self._count name = os.path.splitext(os.path.basename(filename))[0] self._count += 1 gl = {'__file__':filename} random.seed(self._seed) try: execfile(filename, gl) except (ImportError, SyntaxError): self._reporter.import_error(filename, sys.exc_info()) return pytestfile = "" if "XFAIL" in gl: pytestfile = inspect.getsourcefile(gl["XFAIL"]) disabled = gl.get("disabled", False) if disabled: funcs = [] else: # we need to filter only those functions that begin with 'test_' # that are defined in the testing file or in the file where # is defined the XFAIL decorator funcs = [gl[f] for f in gl.keys() if f.startswith("test_") and (inspect.isfunction(gl[f]) or inspect.ismethod(gl[f])) and (inspect.getsourcefile(gl[f]) == filename or inspect.getsourcefile(gl[f]) == pytestfile)] # Sorting of XFAILed functions isn't fixed yet :-( funcs.sort(key=lambda x: inspect.getsourcelines(x)[1]) i = 0 while i < len(funcs): if isgeneratorfunction(funcs[i]): # some tests can be generators, that return the actual # test functions. We unpack it below: f = funcs.pop(i) for fg in f(): func = fg[0] args = fg[1:] fgw = lambda: func(*args) funcs.insert(i, fgw) i += 1 else: i += 1 # drop functions that are not selected with the keyword expression: funcs = [x for x in funcs if self.matches(x)] if not funcs: return self._reporter.entering_filename(filename, len(funcs)) for f in funcs: self._reporter.entering_test(f) try: f() except KeyboardInterrupt: raise except: t, v, tr = sys.exc_info() if t is AssertionError: self._reporter.test_fail((t, v, tr)) if self._post_mortem: pdb.post_mortem(tr) elif t.__name__ == "Skipped": self._reporter.test_skip(v) elif t.__name__ == "XFail": self._reporter.test_xfail() elif t.__name__ == "XPass": self._reporter.test_xpass(v) else: self._reporter.test_exception((t, v, tr)) if self._post_mortem: pdb.post_mortem(tr) else: self._reporter.test_pass() self._reporter.leaving_filename() def matches(self, x): """ Does the keyword expression self._kw match "x"? Returns True/False. Always returns True if self._kw is "". """ if self._kw == "": return True return x.__name__.find(self._kw) != -1 def get_test_files(self, dir, pat = 'test_*.py'): """ Returns the list of test_*.py (default) files at or below directory `dir` relative to the sympy home directory. """ dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0]) g = [] for path, folders, files in os.walk(dir): g.extend([os.path.join(path, f) for f in files if fnmatch(f, pat)]) return [sys_normcase(gi) for gi in g] class SymPyDocTests(object): def __init__(self, reporter, normal): self._count = 0 self._root_dir = sympy_dir self._reporter = reporter self._reporter.root_dir(self._root_dir) self._normal = normal self._testfiles = [] def test(self): """ Runs the tests and returns True if all tests pass, otherwise False. """ self._reporter.start() for f in self._testfiles: try: self.test_file(f) except KeyboardInterrupt: print " interrupted by user" break return self._reporter.finish() def test_file(self, filename): import unittest from StringIO import StringIO rel_name = filename[len(self._root_dir)+1:] module = rel_name.replace(os.sep, '.')[:-3] setup_pprint() try: module = pdoctest._normalize_module(module) tests = SymPyDocTestFinder().find(module) except: self._reporter.import_error(filename, sys.exc_info()) return tests = [test for test in tests if len(test.examples) > 0] # By default (except for python 2.4 in which it was broken) tests # are sorted by alphabetical order by function name. We sort by line number # so one can edit the file sequentially from bottom to top...HOWEVER # if there are decorated functions, their line numbers will be too large # and for now one must just search for these by text and function name. tests.sort(key=lambda x: -x.lineno) if not tests: return self._reporter.entering_filename(filename, len(tests)) for test in tests: assert len(test.examples) != 0 runner = SymPyDocTestRunner(optionflags=pdoctest.ELLIPSIS | \ pdoctest.NORMALIZE_WHITESPACE) old = sys.stdout new = StringIO() sys.stdout = new # If the testing is normal, the doctests get importing magic to # provide the global namespace. If not normal (the default) then # then must run on their own; all imports must be explicit within # a function's docstring. Once imported that import will be # available to the rest of the tests in a given function's # docstring (unless clear_globs=True below). if not self._normal: test.globs = {} # if this is uncommented then all the test would get is what # comes by default with a "from sympy import *" #exec('from sympy import *') in test.globs try: f, t = runner.run(test, out=new.write, clear_globs=False) finally: sys.stdout = old if f > 0: self._reporter.doctest_fail(test.name, new.getvalue()) else: self._reporter.test_pass() self._reporter.leaving_filename() def get_test_files(self, dir, pat='*.py', init_only=True): """ Returns the list of *py files (default) from which docstrings will be tested which are at or below directory `dir`. By default, only those that have an __init__.py in their parent directory and do not start with `test_` will be included. """ def importable(x): """ Checks if given pathname x is an importable module by checking for __init__.py file. Returns True/False. Currently we only test if the __init__.py file exists in the directory with the file "x" (in theory we should also test all the parent dirs). """ init_py = os.path.join(os.path.dirname(x), "__init__.py") return os.path.exists(init_py) dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0]) g = [] for path, folders, files in os.walk(dir): g.extend([os.path.join(path, f) for f in files if not f.startswith('test_') and fnmatch(f, pat)]) if init_only: # skip files that are not importable (i.e. missing __init__.py) g = [x for x in g if importable(x)] return [sys_normcase(gi) for gi in g] class SymPyDocTestFinder(DocTestFinder): """ A class used to extract the DocTests that are relevant to a given object, from its docstring and the docstrings of its contained objects. Doctests can currently be extracted from the following object types: modules, functions, classes, methods, staticmethods, classmethods, and properties. Modified from doctest's version by looking harder for code in the case that it looks like the the code comes from a different module. In the case of decorated functions (e.g. @vectorize) they appear to come from a different module (e.g. multidemensional) even though their code is not there. """ def _find(self, tests, obj, name, module, source_lines, globs, seen): """ Find tests for the given object and any contained objects, and add them to `tests`. """ if self._verbose: print 'Finding tests in %s' % name # If we've already processed this object, then ignore it. if id(obj) in seen: return seen[id(obj)] = 1 # Make sure we don't run doctests for classes outside of sympy, such # as in numpy or scipy. if inspect.isclass(obj): if obj.__module__.split('.')[0] != 'sympy': return # Find a test for this object, and add it to the list of tests. test = self._get_test(obj, name, module, globs, source_lines) if test is not None: tests.append(test) # Look for tests in a module's contained objects. if inspect.ismodule(obj) and self._recurse: for rawname, val in obj.__dict__.items(): # Recurse to functions & classes. if inspect.isfunction(val) or inspect.isclass(val): in_module = self._from_module(module, val) if not in_module: # double check in case this function is decorated # and just appears to come from a different module. pat = r'\s*(def|class)\s+%s\s*\(' % rawname PAT = pre.compile(pat) in_module = any(PAT.match(line) for line in source_lines) if in_module: try: valname = '%s.%s' % (name, rawname) self._find(tests, val, valname, module, source_lines, globs, seen) except ValueError, msg: raise except: pass # Look for tests in a module's __test__ dictionary. if inspect.ismodule(obj) and self._recurse: for valname, val in getattr(obj, '__test__', {}).items(): if not isinstance(valname, basestring): raise ValueError("SymPyDocTestFinder.find: __test__ keys " "must be strings: %r" % (type(valname),)) if not (inspect.isfunction(val) or inspect.isclass(val) or inspect.ismethod(val) or inspect.ismodule(val) or isinstance(val, basestring)): raise ValueError("SymPyDocTestFinder.find: __test__ values " "must be strings, functions, methods, " "classes, or modules: %r" % (type(val),)) valname = '%s.__test__.%s' % (name, valname) self._find(tests, val, valname, module, source_lines, globs, seen) # Look for tests in a class's contained objects. if inspect.isclass(obj) and self._recurse: for valname, val in obj.__dict__.items(): # Special handling for staticmethod/classmethod. if isinstance(val, staticmethod): val = getattr(obj, valname) if isinstance(val, classmethod): val = getattr(obj, valname).im_func # Recurse to methods, properties, and nested classes. if (inspect.isfunction(val) or inspect.isclass(val) or isinstance(val, property)): in_module = self._from_module(module, val) if not in_module: # "double check" again pat = r'\s*(def|class)\s+%s\s*\(' % valname PAT = pre.compile(pat) in_module = any(PAT.match(line) for line in source_lines) if in_module: valname = '%s.%s' % (name, valname) self._find(tests, val, valname, module, source_lines, globs, seen) def _get_test(self, obj, name, module, globs, source_lines): """ Return a DocTest for the given object, if it defines a docstring; otherwise, return None. """ # Extract the object's docstring. If it doesn't have one, # then return None (no test for this object). if isinstance(obj, basestring): docstring = obj else: try: if obj.__doc__ is None: docstring = '' else: docstring = obj.__doc__ if not isinstance(docstring, basestring): docstring = str(docstring) except (TypeError, AttributeError): docstring = '' # Find the docstring's location in the file. lineno = self._find_lineno(obj, source_lines) if lineno is None: # if None, then _find_lineno couldn't find the docstring. # But IT IS STILL THERE. Likely it was decorated or something # (i.e., @property docstrings have lineno == None) # TODO: Write our own _find_lineno that is smarter in this regard # Until then, just give it a dummy lineno. This is just used for # sorting the tests, so the only bad effect is that they will appear # last instead of the order that they really are in the file. # lineno is also used to report the offending line of a failing # doctest, which is another reason to fix this. See issue 1947. lineno = 0 # Don't bother if the docstring is empty. if self._exclude_empty and not docstring: return None # Return a DocTest for this object. if module is None: filename = None else: filename = getattr(module, '__file__', module.__name__) if filename[-4:] in (".pyc", ".pyo"): filename = filename[:-1] return self._parser.get_doctest(docstring, globs, name, filename, lineno) class SymPyDocTestRunner(DocTestRunner): """ A class used to run DocTest test cases, and accumulate statistics. The `run` method is used to process a single DocTest case. It returns a tuple `(f, t)`, where `t` is the number of test cases tried, and `f` is the number of test cases that failed. Modified from the doctest version to not reset the sys.displayhook (see issue 2041). See the docstring of the original DocTestRunner for more information. """ def run(self, test, compileflags=None, out=None, clear_globs=True): """ Run the examples in `test`, and display the results using the writer function `out`. The examples are run in the namespace `test.globs`. If `clear_globs` is true (the default), then this namespace will be cleared after the test runs, to help with garbage collection. If you would like to examine the namespace after the test completes, then use `clear_globs=False`. `compileflags` gives the set of flags that should be used by the Python compiler when running the examples. If not specified, then it will default to the set of future-import flags that apply to `globs`. The output of each example is checked using `SymPyDocTestRunner.check_output`, and the results are formatted by the `SymPyDocTestRunner.report_*` methods. """ self.test = test if compileflags is None: compileflags = pdoctest._extract_future_flags(test.globs) save_stdout = sys.stdout if out is None: out = save_stdout.write sys.stdout = self._fakeout # Patch pdb.set_trace to restore sys.stdout during interactive # debugging (so it's not still redirected to self._fakeout). # Note that the interactive output will go to *our* # save_stdout, even if that's not the real sys.stdout; this # allows us to write test cases for the set_trace behavior. save_set_trace = pdb.set_trace self.debugger = pdoctest._OutputRedirectingPdb(save_stdout) self.debugger.reset() pdb.set_trace = self.debugger.set_trace # Patch linecache.getlines, so we can see the example's source # when we're inside the debugger. self.save_linecache_getlines = pdoctest.linecache.getlines linecache.getlines = self.__patched_linecache_getlines try: return self.__run(test, compileflags, out) finally: sys.stdout = save_stdout pdb.set_trace = save_set_trace linecache.getlines = self.save_linecache_getlines if clear_globs: test.globs.clear() # We have to override the name mangled methods. SymPyDocTestRunner._SymPyDocTestRunner__patched_linecache_getlines = \ DocTestRunner._DocTestRunner__patched_linecache_getlines SymPyDocTestRunner._SymPyDocTestRunner__run = DocTestRunner._DocTestRunner__run SymPyDocTestRunner._SymPyDocTestRunner__record_outcome = \ DocTestRunner._DocTestRunner__record_outcome class Reporter(object): """ Parent class for all reporters. """ pass class PyTestReporter(Reporter): """ Py.test like reporter. Should produce output identical to py.test. """ def __init__(self, verbose=False, tb="short", colors=True): self._verbose = verbose self._tb_style = tb self._colors = colors self._xfailed = 0 self._xpassed = [] self._failed = [] self._failed_doctest = [] self._passed = 0 self._skipped = 0 self._exceptions = [] # this tracks the x-position of the cursor (useful for positioning # things on the screen), without the need for any readline library: self._write_pos = 0 self._line_wrap = False def root_dir(self, dir): self._root_dir = dir def write(self, text, color="", align="left", width=80): """ Prints a text on the screen. It uses sys.stdout.write(), so no readline library is necessary. color ... choose from the colors below, "" means default color align ... left/right, left is a normal print, right is aligned on the right hand side of the screen, filled with " " if necessary width ... the screen width """ color_templates = ( ("Black" , "0;30"), ("Red" , "0;31"), ("Green" , "0;32"), ("Brown" , "0;33"), ("Blue" , "0;34"), ("Purple" , "0;35"), ("Cyan" , "0;36"), ("LightGray" , "0;37"), ("DarkGray" , "1;30"), ("LightRed" , "1;31"), ("LightGreen" , "1;32"), ("Yellow" , "1;33"), ("LightBlue" , "1;34"), ("LightPurple" , "1;35"), ("LightCyan" , "1;36"), ("White" , "1;37"), ) colors = {} for name, value in color_templates: colors[name] = value c_normal = '\033[0m' c_color = '\033[%sm' if align == "right": if self._write_pos+len(text) > width: # we don't fit on the current line, create a new line self.write("\n") self.write(" "*(width-self._write_pos-len(text))) if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty(): # the stdout is not a terminal, this for example happens if the # output is piped to less, e.g. "bin/test | less". In this case, # the terminal control sequences would be printed verbatim, so # don't use any colors. color = "" if sys.platform == "win32": # Windows consoles don't support ANSI escape sequences color = "" if self._line_wrap: if text[0] != "\n": sys.stdout.write("\n") if color == "": sys.stdout.write(text) else: sys.stdout.write("%s%s%s" % (c_color % colors[color], text, c_normal)) sys.stdout.flush() l = text.rfind("\n") if l == -1: self._write_pos += len(text) else: self._write_pos = len(text)-l-1 self._line_wrap = self._write_pos >= width self._write_pos %= width def write_center(self, text, delim="="): width = 80 if text != "": text = " %s " % text idx = (width-len(text)) // 2 t = delim*idx + text + delim*(width-idx-len(text)) self.write(t+"\n") def write_exception(self, e, val, tb): t = traceback.extract_tb(tb) # remove the first item, as that is always runtests.py t = t[1:] t = traceback.format_list(t) self.write("".join(t)) t = traceback.format_exception_only(e, val) self.write("".join(t)) def start(self, seed=None): self.write_center("test process starts") executable = sys.executable v = tuple(sys.version_info) python_version = "%s.%s.%s-%s-%s" % v self.write("executable: %s (%s)\n" % (executable, python_version)) from .misc import ARCH self.write("architecture: %s\n" % ARCH) from sympy.polys.domains import GROUND_TYPES self.write("ground types: %s\n" % GROUND_TYPES) if seed is not None: self.write("random seed: %d\n\n" % seed) self._t_start = clock() def finish(self): self._t_end = clock() self.write("\n") global text, linelen text = "tests finished: %d passed, " % self._passed linelen = len(text) def add_text(mytext): global text, linelen """Break new text if too long.""" if linelen + len(mytext) > 80: text += '\n' linelen = 0 text += mytext linelen += len(mytext) if len(self._failed) > 0: add_text("%d failed, " % len(self._failed)) if len(self._failed_doctest) > 0: add_text("%d failed, " % len(self._failed_doctest)) if self._skipped > 0: add_text("%d skipped, " % self._skipped) if self._xfailed > 0: add_text("%d expected to fail, " % self._xfailed) if len(self._xpassed) > 0: add_text("%d expected to fail but passed, " % len(self._xpassed)) if len(self._exceptions) > 0: add_text("%d exceptions, " % len(self._exceptions)) add_text("in %.2f seconds" % (self._t_end - self._t_start)) if len(self._xpassed) > 0: self.write_center("xpassed tests", "_") for e in self._xpassed: self.write("%s:%s\n" % (e[0], e[1])) self.write("\n") if self._tb_style != "no" and len(self._exceptions) > 0: #self.write_center("These tests raised an exception", "_") for e in self._exceptions: filename, f, (t, val, tb) = e self.write_center("", "_") if f is None: s = "%s" % filename else: s = "%s:%s" % (filename, f.__name__) self.write_center(s, "_") self.write_exception(t, val, tb) self.write("\n") if self._tb_style != "no" and len(self._failed) > 0: #self.write_center("Failed", "_") for e in self._failed: filename, f, (t, val, tb) = e self.write_center("", "_") self.write_center("%s:%s" % (filename, f.__name__), "_") self.write_exception(t, val, tb) self.write("\n") if self._tb_style != "no" and len(self._failed_doctest) > 0: #self.write_center("Failed", "_") for e in self._failed_doctest: filename, msg = e self.write_center("", "_") self.write_center("%s" % filename, "_") self.write(msg) self.write("\n") self.write_center(text) ok = len(self._failed) == 0 and len(self._exceptions) == 0 and \ len(self._failed_doctest) == 0 if not ok: self.write("DO *NOT* COMMIT!\n") return ok def entering_filename(self, filename, n): rel_name = filename[len(self._root_dir)+1:] self._active_file = rel_name self._active_file_error = False self.write(rel_name) self.write("[%d] " % n) def leaving_filename(self): if self._colors: self.write(" ") if self._active_file_error: self.write("[FAIL]", "Red", align="right") else: self.write("[OK]", "Green", align="right") self.write("\n") if self._verbose: self.write("\n") def entering_test(self, f): self._active_f = f if self._verbose: self.write("\n"+f.__name__+" ") def test_xfail(self): self._xfailed += 1 self.write("f", "Green") def test_xpass(self, fname): self._xpassed.append((self._active_file, fname)) self.write("X", "Green") def test_fail(self, exc_info): self._failed.append((self._active_file, self._active_f, exc_info)) self.write("F", "Red") self._active_file_error = True def doctest_fail(self, name, error_msg): # the first line contains "******", remove it: error_msg = "\n".join(error_msg.split("\n")[1:]) self._failed_doctest.append((name, error_msg)) self.write("F", "Red") self._active_file_error = True def test_pass(self): self._passed += 1 if self._verbose: self.write("ok", "Green") else: self.write(".", "Green") def test_skip(self, v): self._skipped += 1 self.write("s", "Green") if self._verbose: self.write(" - ", "Green") self.write(v.message, "Green") def test_exception(self, exc_info): self._exceptions.append((self._active_file, self._active_f, exc_info)) self.write("E", "Red") self._active_file_error = True def import_error(self, filename, exc_info): self._exceptions.append((filename, None, exc_info)) rel_name = filename[len(self._root_dir)+1:] self.write(rel_name) self.write("[?] Failed to import", "Red") if self._colors: self.write(" ") self.write("[FAIL]", "Red", align="right") self.write("\n") sympy_dir = get_sympy_dir() wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/timeutils.py0000644000175000017500000000123612014170666025505 0ustar georgeskgeorgesk"""Simple tools for timing functions execution, when IPython is not available. """ import timeit, math _scales = [1e0, 1e3, 1e6, 1e9] _units = [u's', u'ms', u'\u03bcs', u'ns'] def timed(func): """Adaptively measure execution time of a function. """ timer = timeit.Timer(func) repeat, number = 3, 1 for i in range(1, 10): if timer.timeit(number) >= 0.2: break else: number *= 10 time = min(timer.repeat(repeat, number)) / number if time > 0.0: order = min(-int(math.floor(math.log10(time)) // 3), 3) else: order = 3 return (number, time, time*_scales[order], _units[order]) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/decorator.py0000644000175000017500000000444312014170666025453 0ustar georgeskgeorgeskfrom sympy.core.decorators import wraps def threaded_factory(func, use_add): """A factory for ``threaded`` decorators. """ from sympy.core import sympify, Add from sympy.matrices import Matrix @wraps(func) def threaded_func(expr, *args, **kwargs): if isinstance(expr, Matrix): return expr.applyfunc(lambda f: func(f, *args, **kwargs)) elif hasattr(expr, '__iter__'): return expr.__class__([ func(f, *args, **kwargs) for f in expr ]) else: expr = sympify(expr) if use_add and expr.is_Add: return expr.__class__(*[ func(f, *args, **kwargs) for f in expr.args ]) elif expr.is_Relational: return expr.__class__(func(expr.lhs, *args, **kwargs), func(expr.rhs, *args, **kwargs)) else: return func(expr, *args, **kwargs) return threaded_func def threaded(func): """Apply ``func`` to sub--elements of an object, including :class:`Add`. This decorator is intended to make it uniformly possible to apply a function to all elements of composite objects, e.g. matrices, lists, tuples and other iterable containers, or just expressions. This version of :func:`threaded` decorator allows threading over elements of :class:`Add` class. If this behavior is not desirable use :func:`xthreaded` decorator. Functions using this decorator must have the following signature:: @threaded def function(expr, *args, **kwargs): """ return threaded_factory(func, True) def xthreaded(func): """Apply ``func`` to sub--elements of an object, excluding :class:`Add`. This decorator is intended to make it uniformly possible to apply a function to all elements of composite objects, e.g. matrices, lists, tuples and other iterable containers, or just expressions. This version of :func:`threaded` decorator disallows threading over elements of :class:`Add` class. If this behavior is not desirable use :func:`threaded` decorator. Functions using this decorator must have the following signature:: @xthreaded def function(expr, *args, **kwargs): """ return threaded_factory(func, False) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/codegen.py0000644000175000017500000010361212014170666025073 0ustar georgeskgeorgesk""" module for generating C, C++, Fortran77, Fortran90 and python routines that evaluate sympy expressions. This module is work in progress. Only the milestones with a '+' character in the list below have been completed. --- How is sympy.utilities.codegen different from sympy.printing.ccode? --- We considered the idea to extend the printing routines for sympy functions in such a way that it prints complete compilable code, but this leads to a few unsurmountable issues that can only be tackled with dedicated code generator: - For C, one needs both a code and a header file, while the printing routines generate just one string. This code generator can be extended to support .pyf files for f2py. - Sympy functions are not concerned with programming-technical issues, such as input, output and input-output arguments. Other examples are contiguous or non-contiguous arrays, including headers of other libraries such as gsl or others. - It is highly interesting to evaluate several sympy functions in one C routine, eventually sharing common intermediate results with the help of the cse routine. This is more than just printing. - From the programming perspective, expressions with constants should be evaluated in the code generator as much as possible. This is different for printing. --- Basic assumptions --- * A generic Routine data structure describes the routine that must be translated into C/Fortran/... code. This data structure covers all features present in one or more of the supported languages. * Descendants from the CodeGen class transform multiple Routine instances into compilable code. Each derived class translates into a specific language. * In many cases, one wants a simple workflow. The friendly functions in the last part are a simple api on top of the Routine/CodeGen stuff. They are easier to use, but are less powerful. --- Milestones --- + First working version with scalar input arguments, generating C code, tests + Friendly functions that are easier to use than the rigorous Routine/CodeGen workflow. + Integer and Real numbers as input and output - Optional extra include lines for libraries/objects that can eval special functions - Test other C compilers and libraries: gcc, tcc, libtcc, gcc+gsl, ... + Output arguments + InputOutput arguments + Sort input/output arguments properly + Contiguous array arguments (numpy matrices) - Contiguous array arguments (sympy matrices) - Non-contiguous array arguments (sympy matrices) - ccode must raise an error when it encounters something that can not be translated into c. ccode(integrate(sin(x)/x, x)) does not make sense. - Complex numbers as input and output + Also generate .pyf code for f2py (in autowrap module) - A default complex datatype - Include extra information in the header: date, user, hostname, sha1 hash, ... + Isolate constants and evaluate them beforehand in double precision - Common Subexpression Elimination - User defined comments in the generated code - Fortran 77 + Fortran 90 - C++ - Python - ... """ from __future__ import with_statement import os from StringIO import StringIO from sympy import __version__ as sympy_version from sympy.core import Symbol, S, Expr, Tuple, Equality, Function from sympy.core.compatibility import is_sequence from sympy.printing.codeprinter import AssignmentError from sympy.printing.ccode import ccode, CCodePrinter from sympy.printing.fcode import fcode, FCodePrinter from sympy.tensor import Idx, Indexed, IndexedBase from sympy.utilities import flatten __all__ = [ # description of routines "Routine", "DataType", "default_datatypes", "get_default_datatype", "Argument", "InputArgument", "Result", # routines -> code "CodeGen", "CCodeGen", "FCodeGen", # friendly functions "codegen", ] # # Description of routines # class Routine(object): """Generic description of an evaluation routine for a set of sympy expressions. A CodeGen class can translate instances of this class into C/Fortran/... code. The routine specification covers all the features present in these languages. The CodeGen part must raise an exception when certain features are not present in the target language. For example, multiple return values are possible in Python, but not in C or Fortran. Another example: Fortran and Python support complex numbers, while C does not. """ def __init__(self, name, expr, argument_sequence=None): """Initialize a Routine instance. ``name`` A string with the name of this routine in the generated code ``expr`` The sympy expression that the Routine instance will represent. If given a list or tuple of expressions, the routine will be considered to have multiple return values. ``argument_sequence`` Optional list/tuple containing arguments for the routine in a preferred order. If omitted, arguments will be ordered alphabetically, but with all input aguments first, and then output or in-out arguments. A decision about whether to use output arguments or return values, is made depending on the mathematical expressions. For an expression of type Equality, the left hand side is made into an OutputArgument (or an InOutArgument if appropriate). Else, the calculated expression is the return values of the routine. A tuple of exressions can be used to create a routine with both return value(s) and output argument(s). """ arg_list = [] if is_sequence(expr): if not expr: raise ValueError("No expression given") expressions = Tuple(*expr) else: expressions = Tuple(expr) # local variables local_vars = set([i.label for i in expressions.atoms(Idx)]) # symbols that should be arguments symbols = expressions.atoms(Symbol) - local_vars # Decide whether to use output argument or return value return_val = [] output_args = [] for expr in expressions: if isinstance(expr, Equality): out_arg = expr.lhs expr = expr.rhs if isinstance(out_arg, Indexed): dims = tuple([ (S.Zero, dim-1) for dim in out_arg.shape]) symbol = out_arg.base.label elif isinstance(out_arg, Symbol): dims = [] symbol = out_arg else: raise CodeGenError("Only Indexed or Symbol can define output arguments") if expr.has(symbol): output_args.append(InOutArgument(symbol, out_arg, expr, dimensions=dims)) else: output_args.append(OutputArgument(symbol, out_arg, expr, dimensions=dims)) # avoid duplicate arguments symbols.remove(symbol) else: return_val.append(Result(expr)) # setup input argument list array_symbols = {} for array in expressions.atoms(Indexed): array_symbols[array.base.label] = array for symbol in sorted(symbols, key=str): if symbol in array_symbols: dims = [] array = array_symbols[symbol] for dim in array.shape: dims.append((S.Zero, dim - 1)) metadata = {'dimensions': dims} else: metadata = {} arg_list.append(InputArgument(symbol, **metadata)) output_args.sort(key=lambda x:str(x.name)) arg_list.extend(output_args) if argument_sequence is not None: # if the user has supplied IndexedBase instances, we'll accept that new_sequence = [] for arg in argument_sequence: if isinstance(arg, IndexedBase): new_sequence.append(arg.label) else: new_sequence.append(arg) argument_sequence = new_sequence missing = filter(lambda x: x.name not in argument_sequence, arg_list) if missing: raise CodeGenArgumentListError("Argument list didn't specify: %s" % ", ".join([str(m.name) for m in missing]), missing) # create redundant arguments to produce the requested sequence name_arg_dict = dict([(x.name, x) for x in arg_list]) new_args = [] for symbol in argument_sequence: try: new_args.append(name_arg_dict[symbol]) except KeyError: new_args.append(InputArgument(symbol)) arg_list = new_args self.name = name self.arguments = arg_list self.results = return_val self.local_vars = local_vars @property def variables(self): """Returns a set containing all variables possibly used in this routine. For routines with unnamed return values, the dummies that may or may not be used will be included in the set. """ v = set(self.local_vars) for arg in self.arguments: v.add(arg.name) for res in self.results: v.add(res.result_var) return v @property def result_variables(self): """Returns a list of OutputArgument, InOutArgument and Result. If return values are present, they are at the end ot the list. """ args = [arg for arg in self.arguments if isinstance(arg, (OutputArgument, InOutArgument))] args.extend(self.results) return args class DataType(object): """Holds strings for a certain datatype in different programming languages.""" def __init__(self, cname, fname, pyname): self.cname = cname self.fname = fname self.pyname = pyname default_datatypes = { "int": DataType("int", "INTEGER*4", "int"), "float": DataType("double", "REAL*8", "float") } def get_default_datatype(expr): """Derives a decent data type based on the assumptions on the expression.""" if expr.is_integer: return default_datatypes["int"] else: return default_datatypes["float"] class Variable(object): """Represents a typed variable.""" def __init__(self, name, datatype=None, dimensions=None, precision=None): """Initializes a Variable instance name -- must be of class Symbol datatype -- When not given, the data type will be guessed based on the assumptions on the symbol argument. dimension -- If present, the argument is interpreted as an array. Dimensions must be a sequence containing tuples, i.e. (lower, upper) bounds for each index of the array precision -- FIXME """ if not isinstance(name, Symbol): raise TypeError("The first argument must be a sympy symbol.") if datatype is None: datatype = get_default_datatype(name) elif not isinstance(datatype, DataType): raise TypeError("The (optional) `datatype' argument must be an instance of the DataType class.") if dimensions and not isinstance(dimensions, (tuple, list)): raise TypeError("The dimension argument must be a sequence of tuples") self._name = name self._datatype = { 'C': datatype.cname, 'FORTRAN': datatype.fname, 'PYTHON': datatype.pyname } self.dimensions = dimensions self.precision = precision @property def name(self): return self._name def get_datatype(self, language): """Returns the datatype string for the requested langage. >>> from sympy import Symbol >>> from sympy.utilities.codegen import Variable >>> x = Variable(Symbol('x')) >>> x.get_datatype('c') 'double' >>> x.get_datatype('fortran') 'REAL*8' """ try: return self._datatype[language.upper()] except KeyError: raise CodeGenError("Has datatypes for languages: %s" % ", ".join(self._datatype)) class Argument(Variable): """An abstract Argument data structure: a name and a data type. This structure is refined in the descendants below. """ def __init__(self, name, datatype=None, dimensions=None, precision=None): """ See docstring of Variable.__init__ """ Variable.__init__(self, name, datatype, dimensions, precision) class InputArgument(Argument): pass class ResultBase(object): """Base class for all ``outgoing'' information from a routine Objects of this class stores a sympy expression, and a sympy object representing a result variable that will be used in the generated code only if necessary. """ def __init__(self, expr, result_var): self.expr = expr self.result_var = result_var class OutputArgument(Argument, ResultBase): """OutputArgument are always initialized in the routine """ def __init__(self, name, result_var, expr, datatype=None, dimensions=None, precision=None): """ See docstring of Variable.__init__ """ Argument.__init__(self, name, datatype, dimensions, precision) ResultBase.__init__(self, expr, result_var) class InOutArgument(Argument, ResultBase): """InOutArgument are never initialized in the routine """ def __init__(self, name, result_var, expr, datatype=None, dimensions=None, precision=None): """ See docstring of Variable.__init__ """ Argument.__init__(self, name, datatype, dimensions, precision) ResultBase.__init__(self, expr, result_var) class Result(ResultBase): """An expression for a scalar return value. The name result is used to avoid conflicts with the reserved word 'return' in the python language. It is also shorter than ReturnValue. """ def __init__(self, expr, datatype=None, precision=None): """Initialize a (scalar) return value. The second argument is optional. When not given, the data type will be guessed based on the assumptions on the expression argument. """ if not isinstance(expr, Expr): raise TypeError("The first argument must be a sympy expression.") temp_var = Variable(Symbol('result_%s'%hash(expr)), datatype=datatype, dimensions=None, precision=precision) ResultBase.__init__(self, expr, temp_var.name) self._temp_variable = temp_var def get_datatype(self, language): return self._temp_variable.get_datatype(language) # # Transformation of routine objects into code # class CodeGen(object): """Abstract class for the code generators.""" def __init__(self, project="project"): """Initialize a code generator. Derived classes will offer more options that affect the generated code. """ self.project = project def write(self, routines, prefix, to_files=False, header=True, empty=True): """Writes all the source code files for the given routines. The generate source is returned as a list of (filename, contents) tuples, or is written to files (see options). Each filename consists of the given prefix, appended with an appropriate extension. ``routines`` A list of Routine instances to be written ``prefix`` The prefix for the output files ``to_files`` When True, the output is effectively written to files. [DEFAULT=False] Otherwise, a list of (filename, contents) tuples is returned. ``header`` When True, a header comment is included on top of each source file. [DEFAULT=True] ``empty`` When True, empty lines are included to structure the source files. [DEFAULT=True] """ if to_files: for dump_fn in self.dump_fns: filename = "%s.%s" % (prefix, dump_fn.extension) with open(filename, "w") as f: dump_fn(self, routines, f, prefix, header, empty) else: result = [] for dump_fn in self.dump_fns: filename = "%s.%s" % (prefix, dump_fn.extension) contents = StringIO() dump_fn(self, routines, contents, prefix, header, empty) result.append((filename, contents.getvalue())) return result def dump_code(self, routines, f, prefix, header=True, empty=True): """Write the code file by calling language specific methods in correct order The generated file contains all the definitions of the routines in low-level code and refers to the header file if appropriate. :Arguments: routines A list of Routine instances f A file-like object to write the file to prefix The filename prefix, used to refer to the proper header file. Only the basename of the prefix is used. :Optional arguments: header When True, a header comment is included on top of each source file. [DEFAULT=True] empty When True, empty lines are included to structure the source files. [DEFAULT=True] """ code_lines = self._preprosessor_statements(prefix) for routine in routines: if empty: code_lines.append("\n") code_lines.extend(self._get_routine_opening(routine)) code_lines.extend(self._declare_arguments(routine)) code_lines.extend(self._declare_locals(routine)) if empty: code_lines.append("\n") code_lines.extend(self._call_printer(routine)) if empty: code_lines.append("\n") code_lines.extend(self._get_routine_ending(routine)) code_lines = self._indent_code(''.join(code_lines)) if header: code_lines = ''.join(self._get_header() + [code_lines]) if code_lines: print >> f, code_lines, class CodeGenError(Exception): pass class CodeGenArgumentListError(Exception): @property def missing_args(self): return self.args[1] header_comment = """Code generated with sympy %(version)s See http://www.sympy.org/ for more information. This file is part of '%(project)s' """ class CCodeGen(CodeGen): """ Generator for C code The .write() method inherited from CodeGen will output a code file and an inteface file, .c and .h respectively. """ code_extension = "c" interface_extension = "h" def _get_header(self): """Writes a common header for the generated files.""" code_lines = [] code_lines.append("/" + "*"*78 + '\n') tmp = header_comment % {"version": sympy_version, "project": self.project} for line in tmp.splitlines(): code_lines.append(" *%s*\n" % line.center(76)) code_lines.append(" " + "*"*78 + "/\n") return code_lines def get_prototype(self, routine): """Returns a string for the function prototype for the given routine. If the routine has multiple result objects, an CodeGenError is raised. See: http://en.wikipedia.org/wiki/Function_prototype """ if len(routine.results) > 1: raise CodeGenError("C only supports a single or no return value.") elif len(routine.results) == 1: ctype = routine.results[0].get_datatype('C') else: ctype = "void" type_args = [] for arg in routine.arguments: name = ccode(arg.name) if arg.dimensions: type_args.append((arg.get_datatype('C'), "*%s" % name)) elif isinstance(arg, ResultBase): type_args.append((arg.get_datatype('C'), "&%s" % name)) else: type_args.append((arg.get_datatype('C'), name)) arguments = ", ".join([ "%s %s" % t for t in type_args]) return "%s %s(%s)" % (ctype, routine.name, arguments) def _preprosessor_statements(self, prefix): code_lines = [] code_lines.append("#include \"%s.h\"\n" % os.path.basename(prefix)) code_lines.append("#include \n") return code_lines def _get_routine_opening(self, routine): prototype = self.get_prototype(routine) return ["%s {\n" % prototype] def _declare_arguments(self, routine): # arguments are declared in prototype return [] def _declare_locals(self, routine): # loop variables are declared in loop statement return [] def _call_printer(self, routine): code_lines = [] for result in routine.result_variables: if isinstance(result, Result): assign_to = None elif isinstance(result, (OutputArgument, InOutArgument)): assign_to = result.result_var try: constants, not_c, c_expr = ccode(result.expr, assign_to=assign_to, human=False) except AssignmentError: assign_to = result.result_var code_lines.append("%s %s;\n" % (result.get_datatype('c'), str(assign_to))) constants, not_c, c_expr = ccode(result.expr, assign_to=assign_to, human=False) for name, value in sorted(constants, key=str): code_lines.append("double const %s = %s;\n" % (name, value)) if assign_to: code_lines.append("%s\n" % c_expr) else: code_lines.append(" return %s;\n" % c_expr) return code_lines def _indent_code(self, codelines): p = CCodePrinter() return p.indent_code(codelines) def _get_routine_ending(self, routine): return ["}\n"] def dump_c(self, routines, f, prefix, header=True, empty=True): self.dump_code(routines, f, prefix, header, empty) dump_c.extension = code_extension dump_c.__doc__ = CodeGen.dump_code.__doc__ def dump_h(self, routines, f, prefix, header=True, empty=True): """Writes the C header file. This file contains all the function declarations. :Arguments: routines A list of Routine instances f A file-like object to write the file to prefix The filename prefix, used to construct the include guards. :Optional arguments: header When True, a header comment is included on top of each source file. [DEFAULT=True] empty When True, empty lines are included to structure the source files. [DEFAULT=True] """ if header: print >> f, ''.join(self._get_header()) guard_name = "%s__%s__H" % (self.project.replace(" ", "_").upper(), prefix.replace("/", "_").upper()) # include guards if empty: print >> f print >> f, "#ifndef %s" % guard_name print >> f, "#define %s" % guard_name if empty: print >> f # declaration of the function prototypes for routine in routines: prototype = self.get_prototype(routine) print >> f, "%s;" % prototype # end if include guards if empty: print >> f print >> f, "#endif" if empty: print >> f dump_h.extension = interface_extension # This list of dump functions is used by CodeGen.write to know which dump # functions it has to call. dump_fns = [dump_c, dump_h] class FCodeGen(CodeGen): """ Generator for Fortran 95 code The .write() method inherited from CodeGen will output a code file and an inteface file, .f90 and .h respectively. """ code_extension = "f90" interface_extension = "h" def __init__(self, project='project'): CodeGen.__init__(self, project) def _get_symbol(self, s): """returns the symbol as fcode print it""" return fcode(s).strip() def _get_header(self): """Writes a common header for the generated files.""" code_lines = [] code_lines.append("!" + "*"*78 + '\n') tmp = header_comment % {"version": sympy_version, "project": self.project} for line in tmp.splitlines(): code_lines.append("!*%s*\n" % line.center(76)) code_lines.append("!" + "*"*78 + '\n') return code_lines def _preprosessor_statements(self, prefix): return [] def _get_routine_opening(self, routine): """ Returns the opening statements of the fortran routine """ code_list = [] if len(routine.results) > 1: raise CodeGenError("Fortran only supports a single or no return value.") elif len(routine.results) == 1: result = routine.results[0] code_list.append(result.get_datatype('fortran')) code_list.append("function") else: code_list.append("subroutine") args = ", ".join("%s" % self._get_symbol(arg.name) for arg in routine.arguments) # name of the routine + arguments code_list.append("%s(%s)\n" % (routine.name, args)) code_list = [ " ".join(code_list) ] code_list.append('implicit none\n') return code_list def _declare_arguments(self, routine): # argument type declarations code_list = [] array_list = [] scalar_list = [] for arg in routine.arguments: if isinstance(arg, InputArgument): typeinfo = "%s, intent(in)" % arg.get_datatype('fortran') elif isinstance(arg, InOutArgument): typeinfo = "%s, intent(inout)" % arg.get_datatype('fortran') elif isinstance(arg, OutputArgument): typeinfo = "%s, intent(out)" % arg.get_datatype('fortran') else: raise CodeGenError("Unkown Argument type: %s"%type(arg)) fprint = self._get_symbol if arg.dimensions: # fortran arrays start at 1 dimstr = ", ".join(["%s:%s"%( fprint(dim[0]+1), fprint(dim[1]+1)) for dim in arg.dimensions]) typeinfo += ", dimension(%s)" % dimstr array_list.append("%s :: %s\n" % (typeinfo, fprint(arg.name))) else: scalar_list.append("%s :: %s\n" % (typeinfo, fprint(arg.name))) # scalars first, because they can be used in array declarations code_list.extend(scalar_list) code_list.extend(array_list) return code_list def _declare_locals(self, routine): code_list = [] for var in sorted(routine.local_vars, key=str): typeinfo = get_default_datatype(var) code_list.append("%s :: %s\n" % ( typeinfo.fname, self._get_symbol(var))) return code_list def _get_routine_ending(self, routine): """ Returns the closing statements of the fortran routine """ if len(routine.results) == 1: return ["end function\n"] else: return ["end subroutine\n"] def get_interface(self, routine): """Returns a string for the function interface for the given routine and a single result object, which can be None. If the routine has multiple result objects, a CodeGenError is raised. See: http://en.wikipedia.org/wiki/Function_prototype """ prototype = [ "interface\n" ] prototype.extend(self._get_routine_opening(routine)) prototype.extend(self._declare_arguments(routine)) prototype.extend(self._get_routine_ending(routine)) prototype.append("end interface\n") return "".join(prototype) def _call_printer(self, routine): declarations = [] code_lines = [] for result in routine.result_variables: if isinstance(result, Result): assign_to = routine.name elif isinstance(result, (OutputArgument, InOutArgument)): assign_to = result.result_var constants, not_fortran, f_expr = fcode(result.expr, assign_to=assign_to, source_format='free', human=False) for obj, v in sorted(constants, key=str): t = get_default_datatype(obj) declarations.append("%s, parameter :: %s = %s\n" % (t.fname, obj, v)) for obj in sorted(not_fortran, key=str): t = get_default_datatype(obj) if isinstance(obj, Function): name = obj.func else: name = obj declarations.append("%s :: %s\n" % (t.fname, name)) code_lines.append("%s\n" % f_expr) return declarations + code_lines def _indent_code(self, codelines): p = FCodePrinter({'source_format': 'free', 'human': False}) return p.indent_code(codelines) def dump_f95(self, routines, f, prefix, header=True, empty=True): # check that symbols are unique with ignorecase for r in routines: lowercase = set(map(lambda x: str(x).lower(), r.variables)) orig_case = set(map(lambda x: str(x), r.variables)) if len(lowercase) < len(orig_case): raise CodeGenError("Fortran ignores case. Got symbols: %s"% (", ".join([str(var) for var in r.variables]))) self.dump_code(routines, f, prefix, header, empty) dump_f95.extension = code_extension dump_f95.__doc__ = CodeGen.dump_code.__doc__ def dump_h(self, routines, f, prefix, header=True, empty=True): """Writes the interface to a header file. This file contains all the function declarations. :Arguments: routines A list of Routine instances f A file-like object to write the file to prefix The filename prefix :Optional arguments: header When True, a header comment is included on top of each source file. [DEFAULT=True] empty When True, empty lines are included to structure the source files. [DEFAULT=True] """ if header: print >> f, ''.join(self._get_header()) if empty: print >> f # declaration of the function prototypes for routine in routines: prototype = self.get_interface(routine) print >> f, prototype, if empty: print >> f dump_h.extension = interface_extension # This list of dump functions is used by CodeGen.write to know which dump # functions it has to call. dump_fns = [dump_f95, dump_h] def get_code_generator(language, project): CodeGenClass = {"C": CCodeGen, "F95": FCodeGen}.get(language.upper()) if CodeGenClass is None: raise ValueError("Language '%s' is not supported." % language) return CodeGenClass(project) # # Friendly functions # def codegen(name_expr, language, prefix, project="project", to_files=False, header=True, empty=True, argument_sequence=None): """Write source code for the given expressions in the given language. :Mandatory Arguments: ``name_expr`` A single (name, expression) tuple or a list of (name, expression) tuples. Each tuple corresponds to a routine. If the expression is an equality (an instance of class Equality) the left hand side is considered an output argument. ``language`` A string that indicates the source code language. This is case insensitive. For the moment, only 'C' and 'F95' is supported. ``prefix`` A prefix for the names of the files that contain the source code. Proper (language dependent) suffixes will be appended. :Optional Arguments: ``project`` A project name, used for making unique preprocessor instructions. [DEFAULT="project"] ``to_files`` When True, the code will be written to one or more files with the given prefix, otherwise strings with the names and contents of these files are returned. [DEFAULT=False] ``header`` When True, a header is written on top of each source file. [DEFAULT=True] ``empty`` When True, empty lines are used to structure the code. [DEFAULT=True] ``argument_sequence`` sequence of arguments for the routine in a preferred order. A CodeGenError is raised if required arguments are missing. Redundant arguments are used without warning. If omitted, arguments will be ordered alphabetically, but with all input aguments first, and then output or in-out arguments. >>> from sympy import symbols >>> from sympy.utilities.codegen import codegen >>> from sympy.abc import x, y, z >>> [(c_name, c_code), (h_name, c_header)] = \\ ... codegen(("f", x+y*z), "C", "test", header=False, empty=False) >>> print c_name test.c >>> print c_code, #include "test.h" #include double f(double x, double y, double z) { return x + y*z; } >>> print h_name test.h >>> print c_header, #ifndef PROJECT__TEST__H #define PROJECT__TEST__H double f(double x, double y, double z); #endif """ # Initialize the code generator. code_gen = get_code_generator(language, project) # Construct the routines based on the name_expression pairs. # mainly the input arguments require some work routines = [] if isinstance(name_expr[0], basestring): # single tuple is given, turn it into a singleton list with a tuple. name_expr = [name_expr] for name, expr in name_expr: routines.append(Routine(name, expr, argument_sequence)) # Write the code. return code_gen.write(routines, prefix, to_files, header, empty) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/lambdify.py0000644000175000017500000003130312014170666025253 0ustar georgeskgeorgesk""" This module provides convenient functions to transform sympy expressions to lambda functions which can be used to calculate numerical values very fast. """ from __future__ import division from sympy.core.sympify import sympify from sympy.core.compatibility import is_sequence import inspect # These are the namespaces the lambda functions will use. MATH = {} MPMATH = {} NUMPY = {} SYMPY = {} # Default namespaces, letting us define translations that can't be defined # by simple variable maps, like I => 1j # These are separate from the names above because the above names are modified # throughout this file, whereas these should remain unmodified. MATH_DEFAULT = {} MPMATH_DEFAULT = {} NUMPY_DEFAULT = {"I": 1j} SYMPY_DEFAULT = {} # Mappings between sympy and other modules function names. MATH_TRANSLATIONS = { "Abs":"fabs", "ceiling":"ceil", "E":"e", "ln":"log", } MPMATH_TRANSLATIONS = { "ceiling":"ceil", "chebyshevt":"chebyt", "chebyshevu":"chebyu", "E":"e", "I":"j", "ln":"log", #"lowergamma":"lower_gamma", "oo":"inf", #"uppergamma":"upper_gamma", "LambertW":"lambertw", "Matrix":"matrix", "conjugate":"conj", } NUMPY_TRANSLATIONS = { "acos":"arccos", "acosh":"arccosh", "arg":"angle", "asin":"arcsin", "asinh":"arcsinh", "atan":"arctan", "atan2":"arctan2", "atanh":"arctanh", "ceiling":"ceil", "E":"e", "im":"imag", "ln":"log", "Matrix":"matrix", "Max":"amax", "Min":"amin", "oo":"inf", "re":"real", } # Available modules: MODULES = { "math":(MATH, MATH_DEFAULT, MATH_TRANSLATIONS, ("from math import *",)), "mpmath":(MPMATH, MPMATH_DEFAULT, MPMATH_TRANSLATIONS, ("from sympy.mpmath import *",)), "numpy":(NUMPY, NUMPY_DEFAULT, NUMPY_TRANSLATIONS, ("from numpy import *",)), "sympy":(SYMPY, SYMPY_DEFAULT, {}, ("from sympy.functions import *", "from sympy.matrices import Matrix", "from sympy import Integral, pi, oo, nan, zoo, E, I",)), } def _import(module, reload="False"): """ Creates a global translation dictionary for module. The argument module has to be one of the following strings: "math", "mpmath", "numpy", "sympy". These dictionaries map names of python functions to their equivalent in other modules. """ # TODO: rewrite this using import_module from sympy.external if not module in MODULES: raise NameError("This module can't be used for lambdification.") namespace, namespace_default, translations, import_commands = MODULES[module] # Clear namespace or exit if namespace != namespace_default: # The namespace was already generated, don't do it again if not forced. if reload: namespace.clear() namespace.update(namespace_default) else: return # It's possible that numpy is not available. for import_command in import_commands: try: exec import_command in {}, namespace except ImportError: raise ImportError("Can't import %s with command %s" % (module, import_command)) # Add translated names to namespace for sympyname, translation in translations.iteritems(): namespace[sympyname] = namespace[translation] def lambdify(args, expr, modules=None, printer=None, use_imps=True): """ Returns a lambda function for fast calculation of numerical values. Usage: >>> from sympy import sqrt, sin >>> from sympy.utilities.lambdify import lambdify >>> from sympy.abc import x, y, z >>> f = lambdify(x, x**2) >>> f(2) 4 >>> f = lambdify((x,y,z), [z,y,x]) >>> f(1,2,3) [3, 2, 1] >>> f = lambdify(x, sqrt(x)) >>> f(4) 2.0 >>> f = lambdify((x,y), sin(x*y)**2) >>> f(0, 5) 0.0 If not specified differently by the user, Sympy functions are replaced as far as possible by either python-math, numpy (if available) or mpmath functions - exactly in this order. To change this behavior, the "modules" argument can be used. It accepts: - the strings "math", "mpmath", "numpy", "sympy" - any modules (e.g. math) - dictionaries that map names of sympy functions to arbitrary functions - lists that contain a mix of the arguments above. (Entries that are first in the list have higher priority) Examples: (1) Use one of the provided modules: >> f = lambdify(x, sin(x), "math") Attention: Functions that are not in the math module will throw a name error when the lambda function is evaluated! So this would be better: >> f = lambdify(x, sin(x)*gamma(x), ("math", "mpmath", "sympy")) (2) Use some other module: >> import numpy >> f = lambdify((x,y), tan(x*y), numpy) Attention: There are naming differences between numpy and sympy. So if you simply take the numpy module, e.g. sympy.atan will not be translated to numpy.arctan. Use the modified module instead by passing the string "numpy": >> f = lambdify((x,y), tan(x*y), "numpy") >> f(1, 2) -2.18503986326 >> from numpy import array >> f(array([1, 2, 3]), array([2, 3, 5])) [-2.18503986 -0.29100619 -0.8559934 ] (3) Use own dictionaries: >> def my_cool_function(x): ... >> dic = {"sin" : my_cool_function} >> f = lambdify(x, sin(x), dic) Now f would look like: >> lambda x: my_cool_function(x) Functions present in `expr` can also carry their own numerical implementations, in a callable attached to the ``_imp_`` attribute. Usually you attach this using the ``implemented_function`` factory: >>> from sympy.abc import x, y, z >>> from sympy.utilities.lambdify import lambdify, implemented_function >>> from sympy import Function >>> f = implemented_function(Function('f'), lambda x : x+1) >>> func = lambdify(x, f(x)) >>> func(4) 5 ``lambdify`` always prefers ``_imp_`` implementations to implementations in other namespaces, unless the ``use_imps`` input parameter is False. """ from sympy.core.symbol import Symbol # If the user hasn't specified any modules, use what is available. if modules is None: # Use either numpy (if available) or python.math where possible. # XXX: This leads to different behaviour on different systems and # might be the reason for irreproducible errors. try: _import("numpy") modules = ("math", "numpy", "mpmath", "sympy") except ImportError: modules = ("math", "mpmath", "sympy") # Get the needed namespaces. namespaces = [] # First find any function implementations if use_imps: namespaces.append(_imp_namespace(expr)) # Check for dict before iterating if isinstance(modules, (dict, str)) or not hasattr(modules, '__iter__'): namespaces.append(modules) else: namespaces += list(modules) # fill namespace with first having highest priority namespace = {} for m in namespaces[::-1]: buf = _get_namespace(m) namespace.update(buf) if hasattr(expr, "atoms"): #Try if you can extract symbols from the expression. #Move on if expr.atoms in not implemented. syms = expr.atoms(Symbol) for term in syms: namespace.update({str(term): term}) # Create lambda function. lstr = lambdastr(args, expr, printer=printer) return eval(lstr, namespace) def _get_namespace(m): """ This is used by _lambdify to parse its arguments. """ if isinstance(m, str): _import(m) return MODULES[m][0] elif isinstance(m, dict): return m elif hasattr(m, "__dict__"): return m.__dict__ else: raise TypeError("Argument must be either a string, dict or module but it is: %s" % m) def lambdastr(args, expr, printer=None): """ Returns a string that can be evaluated to a lambda function. >>> from sympy.abc import x, y, z >>> from sympy.utilities.lambdify import lambdastr >>> lambdastr(x, x**2) 'lambda x: (x**2)' >>> lambdastr((x,y,z), [z,y,x]) 'lambda x,y,z: ([z, y, x])' """ if printer is not None: if inspect.isfunction(printer): lambdarepr = printer else: if inspect.isclass(printer): lambdarepr = lambda expr: printer().doprint(expr) else: lambdarepr = lambda expr: printer.doprint(expr) else: #XXX: This has to be done here because of circular imports from sympy.printing.lambdarepr import lambdarepr # Transform everything to strings. expr = lambdarepr(expr) if isinstance(args, str): pass elif hasattr(args, "__iter__"): args = ",".join(str(a) for a in args) else: args = str(args) return "lambda %s: (%s)" % (args, expr) def _imp_namespace(expr, namespace=None): """ Return namespace dict with function implementations We need to search for functions in anything that can be thrown at us - that is - anything that could be passed as `expr`. Examples include sympy expressions, as well as tuples, lists and dicts that may contain sympy expressions. Parameters ---------- expr : object Something passed to lambdify, that will generate valid code from ``str(expr)``. namespace : None or mapping Namespace to fill. None results in new empty dict Returns ------- namespace : dict dict with keys of implemented function names within `expr` and corresponding values being the numerical implementation of function Examples -------- >>> from sympy.abc import x, y, z >>> from sympy.utilities.lambdify import implemented_function, _imp_namespace >>> from sympy import Function >>> f = implemented_function(Function('f'), lambda x : x+1) >>> g = implemented_function(Function('g'), lambda x : x*10) >>> namespace = _imp_namespace(f(g(x))) >>> sorted(namespace.keys()) ['f', 'g'] """ # Delayed import to avoid circular imports from sympy.core.function import FunctionClass if namespace is None: namespace = {} # tuples, lists, dicts are valid expressions if is_sequence(expr): for arg in expr: _imp_namespace(arg, namespace) return namespace elif isinstance(expr, dict): for key, val in expr.items(): # functions can be in dictionary keys _imp_namespace(key, namespace) _imp_namespace(val, namespace) return namespace # sympy expressions may be Functions themselves func = getattr(expr, 'func', None) if isinstance(func, FunctionClass): imp = getattr(func, '_imp_', None) if not imp is None: name = expr.func.__name__ if name in namespace and namespace[name] != imp: raise ValueError('We found more than one ' 'implementation with name ' '"%s"' % name) namespace[name] = imp # and / or they may take Functions as arguments if hasattr(expr, 'args'): for arg in expr.args: _imp_namespace(arg, namespace) return namespace def implemented_function(symfunc, implementation): """ Add numerical `implementation` to function `symfunc` `symfunc` can by a Function, or a name, in which case we make an anonymous function with this name. The function is anonymous in the sense that the name is not unique in the sympy namespace. Parameters ---------- symfunc : str or ``sympy.FunctionClass`` instance If str, then create new anonymous sympy function with this as name. If `symfunc` is a sympy function, attach implementation to function implementation : callable numerical implementation of function for use in ``lambdify`` Returns ------- afunc : sympy.FunctionClass instance function with attached implementation Examples -------- >>> from sympy.abc import x, y, z >>> from sympy.utilities.lambdify import lambdify, implemented_function >>> from sympy import Function >>> f = implemented_function(Function('f'), lambda x : x+1) >>> lam_f = lambdify(x, f(x)) >>> lam_f(4) 5 """ # Delayed import to avoid circular imports from sympy.core.function import UndefinedFunction # if name, create anonymous function to hold implementation if isinstance(symfunc, basestring): symfunc = UndefinedFunction(symfunc) # We need to attach as a method because symfunc will be a class symfunc._imp_ = staticmethod(implementation) return symfunc wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/mathml/0000755000175000017500000000000012014170666024374 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/mathml/data/0000755000175000017500000000000012014170666025305 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/mathml/data/simple_mmlctop.xsl0000644000175000017500000033740112014170666031071 0ustar georgeskgeorgesk e - + &#x2062; &#x2148; - + &#x2062; &#x2148; Polar &#x2062; Polar &#x2062; &#x2061; [ ] -1 &#x03BB; &#x2061; &#x2218; &#x2218; id id domain codomain image &#x2061; { if otherwise &#x230A; &#x230B; e ! max min max min | - - - + &#x2062; gcd lcm gcd lcm &#x2061; &#x2227; &#x2061; &#x2228; &#x2061; &#x22BB; &#x2061; &#x00AC; &#x2061; &#x00AC; &#x2061; &#x2200; : , &#x2203; : , &#x00AF; &#x211C; &#x2111; &#x2061; &#x230A; &#x2308; &#x230B; &#x2309; &#x2260; &#x2248; &#x2223; &#x2198; &#x2197; &#x2192; &#x21D2; &#x2208; &#x2209; &#x2284; &#x2288; &#x2286; &#x2282; &#x2265; &#x2264; &#x2261; ln ln log log log log d d d d &#x2032; &#x2145; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2207; 2 &#x2061; | &#x222A; &#x2229; \ &#x00D7; &#x2211; &#x220F; = &#x2211; &#x220F; &#x2211; &#x220F; &#x222B; &#x222B; &#x222B; &#x222B; &#x222B; d lim &#x2192; &#x03C3; &#x03C3; 2 median mode det T &#x00D7; &#x22C5; &#x2297; &#x2124; &#x211D; &#x211A; &#x2115; &#x2102; &#x2119; e &#x2148; NaN true false &#x2205; &#x03C0; &#x213D; &#x221E; wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/mathml/data/mmltex.xsl0000644000175000017500000041415512014170666027355 0ustar georgeskgeorgesk $ $ + i / _{} e^{i } E \mathrm{} ( , ) () \left( \left[ , \right) \right] \left\{\right\} ^{(-1)} \mathrm{lambda}\: .\: \circ \mathrm{id} \mathop{\mathrm{ }} \begin{cases} \end{cases} & \text{if $ $} \\ & \text{otherwise} \left\lfloor\frac{ }{ }\right\rfloor ! \left( \frac{ }{ } \right) \ \{ , , \} - - ( - + ) ^{ } \mod ( \times ) \sqrt [ ] { } \gcd \land \lor \mathop{\mathrm{xor}} \neg \implies \ , \colon \left| \right| \overline{} \Re \Im \lfloor \rfloor \lceil \rceil = \neq > < \ge \le \equiv \approx | \int _{ } ^{ } \,d ^\prime \frac{ d^{ } }{d ^{ } d }{d } } D_{ , } \frac{\partial^{ + + } }{ \partial ^{ } } , \mathop{\mathrm{div}} \nabla^2 \{\} \left[\right] \colon , \cup \cap \in \notin \subseteq \subset \nsubseteq \not\subset \setminus | | \times ^{ } \sum \prod _{ = } ^{ } \lim_{ } \to \searrow \nearrow \rightarrow \to \ \ \mathrm{ \,} \mathrm{ } e^{} \lg \log_{ } \langle , \rangle \sigma \sigma( )^2 \langle ^{ }\rangle _{ } \left(\begin{array}{c} \\ \end{array}\right) \begin{pmatrix} \end{pmatrix} & \\ \det \begin{vmatrix} \end{vmatrix} ^T _{ , } \dot \mathbb{Z} \mathbb{R} \mathbb{Q} \mathbb{N} \mathbb{C} \mathbb{P} e i NaN \mbox{true} \mbox{false} \emptyset \pi \gamma \infty ( ) ( ) \multicolumn{ }{c}{ } & \hfill \hfill & \\ \begin{array}{ | | } \hline \\ \hline \end{array} \overline{ } \overbrace{ } \underline{ } \underbrace{ } _{ }^{ } \underset{ }{\overset{ }{ }} \overline{ } \overbrace{ } ^{ } \stackrel{ }{ } \underline{ } \underbrace{ } _{ } \underset{ }{ } { }_{ }^{ } { }^{ } { }_{ } {}_{ } {}^{ } {} _{ } ^{ } {} _{ } ^{ } \genfrac{}{}{ ex .05ex .2ex }{}{ \frac{ \hfill \hfill }{ \hfill \hfill } \sqrt[ ]{ } exception 25: \text{exception 25:} \sqrt{ } \left \ \left( , \right \ \right) \phantom{ } \overline{ \hspace{.2em}|} \sqrt{ } \overline{) } \colorbox[rgb]{ }{$ \textcolor[rgb]{ }{ } $} \mathrm{ } \text{ } \phantom{\rule [- ] { 0ex }{ 0ex }} " " \colorbox[rgb]{ }{$ \textcolor[rgb]{ }{ \mathrm{ \mathbf{ \mathit{ \mathbit{ \mathbb{ { \mathcal{ \mathsc{ \mathfrak{ \mathsf{ \mathbsf{ \mathsfit{ \mathbsfit{ \mathtt{ { } } $} , , , , 0,1,1 0,0,0 0,0,1 1,0,1 .5,.5,.5 0,.5,0 0,1,0 .5,0,0 0,0,.5 .5,.5,0 .5,0,.5 1,0,0 .75,.75,.75 0,.5,.5 1,1,1 1,1,0 Exception at color template Exception at Hex2Decimal template wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/mathml/data/mmlctop.xsl0000644000175000017500000033741412014170666027524 0ustar georgeskgeorgesk e - + &#x2062; &#x2148; - + &#x2062; &#x2148; Polar &#x2062; Polar &#x2062; &#x2061; [ ] -1 &#x03BB; &#x2061; &#x2218; &#x2218; id id domain codomain image &#x2061; { if otherwise &#x230A; &#x230B; &#x2147; ! max min max min | - - - + &#x2062; gcd lcm gcd lcm &#x2061; &#x2227; &#x2061; &#x2228; &#x2061; &#x22BB; &#x2061; &#x00AC; &#x2061; &#x00AC; &#x2061; &#x2200; : , &#x2203; : , &#x00AF; &#x211C; &#x2111; &#x2061; &#x230A; &#x2308; &#x230B; &#x2309; &#x2260; &#x2248; &#x2223; &#x2198; &#x2197; &#x2192; &#x21D2; &#x2208; &#x2209; &#x2284; &#x2288; &#x2286; &#x2282; &#x2265; &#x2264; &#x2261; ln ln log log log log &#x2146; &#x2146; &#x2146; &#x2146; &#x2032; &#x2145; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2202; &#x2207; 2 &#x2061; | &#x222A; &#x2229; \ &#x00D7; &#x2211; &#x220F; = &#x2211; &#x220F; &#x2211; &#x220F; &#x222B; &#x222B; &#x222B; &#x222B; &#x222B; &#x2146; lim &#x2192; &#x03C3; &#x03C3; 2 median mode det T &#x00D7; &#x22C5; &#x2297; &#x2124; &#x211D; &#x211A; &#x2115; &#x2102; &#x2119; &#x2147; &#x2148; NaN true false &#x2205; &#x03C0; &#x213D; &#x221E; wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/mathml/__init__.py0000644000175000017500000000311012014170666026500 0ustar georgeskgeorgesk"""Module with some functions for MathML, like transforming MathML content in MathML presentation. To use this module, you will need libxml2 and libxslt, with its respective python bindings. """ from sympy.utilities.pkgdata import get_resource import xml.dom.minidom def add_mathml_headers(s): return """""" + s + "" def apply_xsl(mml, xsl): """Apply a xsl to a MathML string @param mml: a string with MathML code @param xsl: a string representing a path to a xsl (xml stylesheet) file. This file name is relative to the PYTHONPATH """ import libxml2 import libxslt s = get_resource(xsl).read() styledoc = libxml2.parseDoc(s) style = libxslt.parseStylesheetDoc(styledoc) doc = libxml2.parseDoc(mml) result = style.applyStylesheet(doc, None) sourceDoc = result s = style.saveResultToString(result) style.freeStylesheet() sourceDoc.freeDoc() return s def c2p(mml, simple=False): """Transforms a document in MathML content (like the one that sympy produces) in one document in MathML presentation, more suitable for printing, and more widely accepted """ if not mml.startswith(' 1: return diff/abs(z1) <= tol else: return diff <= tol def test_numerically(f, g, z, tol=1.0e-6, a=2, b=-1, c=3, d=1): """ Test numerically that f and g agree when evaluated in the argument z. Examples: >>> from sympy import sin, cos, S >>> from sympy.abc import x >>> from sympy.utilities.randtest import test_numerically as tn >>> tn(sin(x)**2 + cos(x)**2, S(1), x) True """ z0 = random_complex_number(a, b, c, d) z1 = f.subs(z, z0).n() z2 = g.subs(z, z0).n() return comp(z1, z2, tol) def test_derivative_numerically(f, z, tol=1.0e-6, a=2, b=-1, c=3, d=1): """ Test numerically that the symbolically computed derivative of f with respect to z is correct. Examples: >>> from sympy import sin, cos >>> from sympy.abc import x >>> from sympy.utilities.randtest import test_derivative_numerically as td >>> td(sin(x), x) True """ from sympy.core.function import Derivative z0 = random_complex_number(a, b, c, d) f1 = f.diff(z).subs(z, z0) f2 = Derivative(f, z).doit_numerically(z0) return comp(f1.n(), f2.n(), tol) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/iterables.py0000644000175000017500000010375112014170666025445 0ustar georgeskgeorgeskfrom sympy.core import Basic, C from sympy.core.compatibility import is_sequence, iterable #logically, they belong here import random def flatten(iterable, levels=None, cls=None): """ Recursively denest iterable containers. >>> from sympy.utilities.iterables import flatten >>> flatten([1, 2, 3]) [1, 2, 3] >>> flatten([1, 2, [3]]) [1, 2, 3] >>> flatten([1, [2, 3], [4, 5]]) [1, 2, 3, 4, 5] >>> flatten([1.0, 2, (1, None)]) [1.0, 2, 1, None] If you want to denest only a specified number of levels of nested containers, then set ``levels`` flag to the desired number of levels:: >>> ls = [[(-2, -1), (1, 2)], [(0, 0)]] >>> flatten(ls, levels=1) [(-2, -1), (1, 2), (0, 0)] If cls argument is specified, it will only flatten instances of that class, for example: >>> from sympy.core import Basic >>> class MyOp(Basic): ... pass ... >>> flatten([MyOp(1, MyOp(2, 3))], cls=MyOp) [1, 2, 3] adapted from http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks """ if levels is not None: if not levels: return iterable elif levels > 0: levels -= 1 else: raise ValueError("expected non-negative number of levels, got %s" % levels) if cls is None: reducible = lambda x: hasattr(x, "__iter__") and not isinstance(x, basestring) else: reducible = lambda x: isinstance(x, cls) result = [] for el in iterable: if reducible(el): if hasattr(el, 'args'): el = el.args result.extend(flatten(el, levels=levels, cls=cls)) else: result.append(el) return result def group(container, multiple=True): """ Splits a container into a list of lists of equal, adjacent elements. >>> from sympy.utilities.iterables import group >>> group([1, 1, 1, 2, 2, 3]) [[1, 1, 1], [2, 2], [3]] >>> group([1, 1, 1, 2, 2, 3], multiple=False) [(1, 3), (2, 2), (3, 1)] """ if not container: return [] current, groups = [container[0]], [] for elem in container[1:]: if elem == current[-1]: current.append(elem) else: groups.append(current) current = [elem] groups.append(current) if multiple: return groups for i, current in enumerate(groups): groups[i] = (current[0], len(current)) return groups def postorder_traversal(node): """ Do a postorder traversal of a tree. This generator recursively yields nodes that it has visited in a postorder fashion. That is, it descends through the tree depth-first to yield all of a node's children's postorder traversal before yielding the node itself. Parameters ---------- node : sympy expression The expression to traverse. Yields ------ subtree : sympy expression All of the subtrees in the tree. Examples -------- >>> from sympy import symbols >>> from sympy.utilities.iterables import postorder_traversal >>> from sympy.abc import x, y, z >>> set(postorder_traversal((x+y)*z)) == set([z, y, x, x + y, z*(x + y)]) True """ if isinstance(node, Basic): for arg in node.args: for subtree in postorder_traversal(arg): yield subtree elif iterable(node): for item in node: for subtree in postorder_traversal(item): yield subtree yield node class preorder_traversal(object): """ Do a pre-order traversal of a tree. This iterator recursively yields nodes that it has visited in a pre-order fashion. That is, it yields the current node then descends through the tree breadth-first to yield all of a node's children's pre-order traversal. Parameters ---------- node : sympy expression The expression to traverse. Yields ------ subtree : sympy expression All of the subtrees in the tree. Examples -------- >>> from sympy import symbols >>> from sympy.utilities.iterables import preorder_traversal >>> from sympy.abc import x, y, z >>> set(preorder_traversal((x+y)*z)) == set([z, x + y, z*(x + y), x, y]) True """ def __init__(self, node): self._skip_flag = False self._pt = self._preorder_traversal(node) def _preorder_traversal(self, node): yield node if self._skip_flag: self._skip_flag = False return if isinstance(node, Basic): for arg in node.args: for subtree in self._preorder_traversal(arg): yield subtree elif iterable(node): for item in node: for subtree in self._preorder_traversal(item): yield subtree def skip(self): """ Skip yielding current node's (last yielded node's) subtrees. Examples -------- >>> from sympy import symbols >>> from sympy.utilities.iterables import preorder_traversal >>> from sympy.abc import x, y, z >>> pt = preorder_traversal((x+y*z)*z) >>> for i in pt: ... print i ... if i == x+y*z: ... pt.skip() z*(x + y*z) z x + y*z """ self._skip_flag = True def next(self): return self._pt.next() def __iter__(self): return self def interactive_traversal(expr): """Traverse a tree asking a user which branch to choose. """ from sympy.printing import pprint RED, BRED = '\033[0;31m', '\033[1;31m' GREEN, BGREEN = '\033[0;32m', '\033[1;32m' YELLOW, BYELLOW = '\033[0;33m', '\033[1;33m' BLUE, BBLUE = '\033[0;34m', '\033[1;34m' MAGENTA, BMAGENTA = '\033[0;35m', '\033[1;35m' CYAN, BCYAN = '\033[0;36m', '\033[1;36m' END = '\033[0m' def cprint(*args): print "".join(map(str, args)) + END def _interactive_traversal(expr, stage): if stage > 0: print cprint("Current expression (stage ", BYELLOW, stage, END, "):") print BCYAN pprint(expr) print END if isinstance(expr, Basic): if expr.is_Add: args = expr.as_ordered_terms() elif expr.is_Mul: args = expr.as_ordered_factors() else: args = expr.args elif hasattr(expr, "__iter__"): args = list(expr) else: return expr n_args = len(args) if not n_args: return expr for i, arg in enumerate(args): cprint(GREEN, "[", BGREEN, i, GREEN, "] ", BLUE, type(arg), END) pprint(arg) print if n_args == 1: choices = '0' else: choices = '0-%d' % (n_args-1) try: choice = raw_input("Your choice [%s,f,l,r,d,?]: " % choices) except EOFError: result = expr print else: if choice == '?': cprint(RED, "%s - select subexpression with the given index" % choices) cprint(RED, "f - select the first subexpression") cprint(RED, "l - select the last subexpression") cprint(RED, "r - select a random subexpression") cprint(RED, "d - done\n") result = _interactive_traversal(expr, stage) elif choice in ['d', '']: result = expr elif choice == 'f': result = _interactive_traversal(args[0], stage+1) elif choice == 'l': result = _interactive_traversal(args[-1], stage+1) elif choice == 'r': result = _interactive_traversal(random.choice(args), stage+1) else: try: choice = int(choice) except ValueError: cprint(BRED, "Choice must be a number in %s range\n" % choices) result = _interactive_traversal(expr, stage) else: if choice < 0 or choice >= n_args: cprint(BRED, "Choice must be in %s range\n" % choices) result = _interactive_traversal(expr, stage) else: result = _interactive_traversal(args[choice], stage+1) return result return _interactive_traversal(expr, 0) def cartes(*seqs): """Return Cartesian product (combinations) of items from iterable sequences, seqs, as a generator. Examples:: >>> from sympy import Add, Mul >>> from sympy.abc import x, y >>> from sympy.utilities.iterables import cartes >>> do=list(cartes([Mul, Add], [x, y], [2])) >>> for di in do: ... print di[0](*di[1:]) ... 2*x 2*y x + 2 y + 2 >>> >>> list(cartes([1, 2], [3, 4, 5])) [[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5]] """ if not seqs: yield [] else: for item in seqs[0]: for subitem in cartes(*seqs[1:]): yield [item] + subitem def variations(seq, n, repetition=False): """Returns a generator of the variations (size n) of the list `seq` (size N). `repetition` controls whether items in seq can appear more than once; Examples: variations(seq, n) will return N! / (N - n)! permutations without repetition of seq's elements: >>> from sympy.utilities.iterables import variations >>> list(variations([1, 2], 2)) [[1, 2], [2, 1]] variations(seq, n, True) will return the N**n permutations obtained by allowing repetition of elements: >>> list(variations([1, 2], 2, repetition=True)) [[1, 1], [1, 2], [2, 1], [2, 2]] If you ask for more items than are in the set you get the empty set unless you allow repetitions: >>> list(variations([0, 1], 3, repetition=False)) [] >>> list(variations([0, 1], 3, repetition=True))[:4] [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1]] Reference: http://code.activestate.com/recipes/190465/ """ if n == 0: yield [] else: if not repetition: for i in xrange(len(seq)): for cc in variations(seq[:i] + seq[i + 1:], n - 1, False): yield [seq[i]] + cc else: for i in xrange(len(seq)): for cc in variations(seq, n - 1, True): yield [seq[i]] + cc def subsets(seq, k=None, repetition=False): """Generates all k-subsets (combinations) from an n-element set, seq. A k-subset of an n-element set is any subset of length exactly k. The number of k-subsets of an n-element set is given by binomial(n, k), whereas there are 2**n subsets all together. If k is None then all 2**n subsets will be returned from shortest to longest. Examples: >>> from sympy.utilities.iterables import subsets subsets(seq, k) will return the n!/k!/(n - k)! k-subsets (combinations) without repetition, i.e. once an item has been removed, it can no longer be "taken": >>> list(subsets([1, 2], 2)) [[1, 2]] >>> list(subsets([1, 2])) [[], [1], [2], [1, 2]] >>> list(subsets([1, 2, 3], 2)) [[1, 2], [1, 3], [2, 3]] subsets(seq, k, repetition=True) will return the (n - 1 + k)!/k!/(n - 1)! combinations *with* repetition: >>> list(subsets([1, 2], 2, repetition=True)) [[1, 1], [1, 2], [2, 2]] If you ask for more items than are in the set you get the empty set unless you allow repetitions: >>> list(subsets([0, 1], 3, repetition=False)) [] >>> list(subsets([0, 1], 3, repetition=True)) [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]] """ if type(seq) is not list: seq = list(seq) if k == 0: yield [] elif k is None: yield [] for k in range(1, len(seq) + 1): for s in subsets(seq, k, repetition=repetition): yield list(s) else: if not repetition: for i in xrange(len(seq)): for cc in subsets(seq[i + 1:], k - 1, False): yield [seq[i]] + cc else: nmax = len(seq) - 1 indices = [0] * k yield seq[:1] * k while 1: indices[-1] += 1 if indices[-1] > nmax: #find first digit that can be incremented for j in range(-2, -k - 1, -1): if indices[j] < nmax: indices[j:] = [indices[j] + 1] * -j break # increment and copy to the right else: break # we didn't for-break so we are done yield [seq[li] for li in indices] def numbered_symbols(prefix='x', cls=None, start=0, *args, **assumptions): """ Generate an infinite stream of Symbols consisting of a prefix and increasing subscripts. Parameters ---------- prefix : str, optional The prefix to use. By default, this function will generate symbols of the form "x0", "x1", etc. cls : class, optional The class to use. By default, it uses Symbol, but you can also use Wild. start : int, optional The start number. By default, it is 0. Yields ------ sym : Symbol The subscripted symbols. """ if cls is None: if 'dummy' in assumptions and assumptions.pop('dummy'): import warnings warnings.warn("\nuse cls=Dummy to create dummy symbols", DeprecationWarning) cls = C.Dummy else: cls = C.Symbol while True: name = '%s%s' % (prefix, start) yield cls(name, *args, **assumptions) start += 1 def capture(func): """Return the printed output of func(). `func` should be an argumentless function that produces output with print statements. >>> from sympy.utilities.iterables import capture >>> def foo(): ... print 'hello world!' ... >>> 'hello' in capture(foo) # foo, not foo() True """ import StringIO import sys stdout = sys.stdout sys.stdout = file = StringIO.StringIO() func() sys.stdout = stdout return file.getvalue() def sift(expr, keyfunc): """Sift the arguments of expr into a dictionary according to keyfunc. INPUT: expr may be an expression or iterable; if it is an expr then it is converted to a list of expr's args or [expr] if there are no args. OUTPUT: each element in expr is stored in a list keyed to the value of keyfunc for the element. EXAMPLES: >>> from sympy.utilities import sift >>> from sympy.abc import x, y >>> from sympy import sqrt, exp >>> sift(range(5), lambda x: x%2) {0: [0, 2, 4], 1: [1, 3]} It is possible that some keys are not present, in which case you should used dict's .get() method: >>> sift(x+y, lambda x: x.is_commutative) {True: [y, x]} >>> _.get(False, []) [] Sometimes you won't know how many keys you will get: >>> sift(sqrt(x) + x**2 + exp(x) + (y**x)**2, ... lambda x: x.as_base_exp()[0]) {E: [exp(x)], x: [x**(1/2), x**2], y: [y**(2*x)]} >>> _.keys() [E, x, y] """ d = {} if hasattr(expr, 'args'): expr = expr.args or [expr] for e in expr: d.setdefault(keyfunc(e), []).append(e) return d def take(iter, n): """Return ``n`` items from ``iter`` iterator. """ return [ value for _, value in zip(xrange(n), iter) ] def dict_merge(*dicts): """Merge dictionaries into a single dictionary. """ merged = {} for dict in dicts: merged.update(dict) return merged def prefixes(seq): """ Generate all prefixes of a sequence. Example ======= >>> from sympy.utilities.iterables import prefixes >>> list(prefixes([1,2,3,4])) [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]] """ n = len(seq) for i in xrange(n): yield seq[:i+1] def postfixes(seq): """ Generate all postfixes of a sequence. Example ======= >>> from sympy.utilities.iterables import postfixes >>> list(postfixes([1,2,3,4])) [[4], [3, 4], [2, 3, 4], [1, 2, 3, 4]] """ n = len(seq) for i in xrange(n): yield seq[n-i-1:] def topological_sort(graph, key=None): r""" Topological sort of graph's vertices. **Parameters** ``graph`` : ``tuple[list, list[tuple[T, T]]`` A tuple consisting of a list of vertices and a list of edges of a graph to be sorted topologically. ``key`` : ``callable[T]`` (optional) Ordering key for vertices on the same level. By default the natural (e.g. lexicographic) ordering is used (in this case the base type must implement ordering relations). **Examples** Consider a graph:: +---+ +---+ +---+ | 7 |\ | 5 | | 3 | +---+ \ +---+ +---+ | _\___/ ____ _/ | | / \___/ \ / | V V V V | +----+ +---+ | | 11 | | 8 | | +----+ +---+ | | | \____ ___/ _ | | \ \ / / \ | V \ V V / V V +---+ \ +---+ | +----+ | 2 | | | 9 | | | 10 | +---+ | +---+ | +----+ \________/ where vertices are integers. This graph can be encoded using elementary Python's data structures as follows:: >>> V = [2, 3, 5, 7, 8, 9, 10, 11] >>> E = [(7, 11), (7, 8), (5, 11), (3, 8), (3, 10), ... (11, 2), (11, 9), (11, 10), (8, 9)] To compute a topological sort for graph ``(V, E)`` issue:: >>> from sympy.utilities.iterables import topological_sort >>> topological_sort((V, E)) [3, 5, 7, 8, 11, 2, 9, 10] If specific tie breaking approach is needed, use ``key`` parameter:: >>> topological_sort((V, E), key=lambda v: -v) [7, 5, 11, 3, 10, 8, 9, 2] Only acyclic graphs can be sorted. If the input graph has a cycle, then :py:exc:`ValueError` will be raised:: >>> topological_sort((V, E + [(10, 7)])) Traceback (most recent call last): ... ValueError: cycle detected .. seealso:: http://en.wikipedia.org/wiki/Topological_sorting """ V, E = graph L = [] S = set(V) E = list(E) for v, u in E: S.discard(u) if key is None: key = lambda value: value S = sorted(S, key=key, reverse=True) while S: node = S.pop() L.append(node) for u, v in list(E): if u == node: E.remove((u, v)) for _u, _v in E: if v == _v: break else: kv = key(v) for i, s in enumerate(S): ks = key(s) if kv > ks: S.insert(i, v) break else: S.append(v) if E: raise ValueError("cycle detected") else: return L def rotate_left(x, y): """ Left rotates a list x by the number of steps specified in y. Examples: >>> from sympy.utilities.iterables import rotate_left >>> a = [0, 1, 2] >>> rotate_left(a, 1) [1, 2, 0] """ if len(x) == 0: return x y = y % len(x) return x[y:] + x[:y] def rotate_right(x, y): """ Left rotates a list x by the number of steps specified in y. Examples: >>> from sympy.utilities.iterables import rotate_right >>> a = [0, 1, 2] >>> rotate_right(a, 1) [2, 0, 1] """ if len(x) == 0: return x y = len(x) - y % len(x) return x[y:] + x[:y] def multiset_partitions(multiset, m): """ This is the algorithm for generating multiset partitions as described by Knuth in TAOCP Vol 4. Given a multiset, this algorithm visits all of its m-partitions, that is, all partitions having exactly size m using auxiliary arrays as described in the book. Examples: >>> from sympy.utilities.iterables import multiset_partitions >>> list(multiset_partitions([1,2,3,4], 2)) [[[1, 2, 3], [4]], [[1, 3], [2, 4]], [[1], [2, 3, 4]], [[1, 2], \ [3, 4]], [[1, 2, 4], [3]], [[1, 4], [2, 3]], [[1, 3, 4], [2]]] >>> list(multiset_partitions([1,2,3,4], 1)) [[[1, 2, 3, 4]]] >>> list(multiset_partitions([1,2,3,4], 4)) [[[1], [2], [3], [4]]] """ cache = {} def visit(n, a): ps = [[] for i in xrange(m)] for j in xrange(n): ps[a[j + 1]].append(multiset[j]) canonical = tuple(tuple(j) for j in ps) if not canonical in cache: cache[canonical] = 1 return ps def f(m_arr, n_arr, sigma, n, a): if m_arr <= 2: v = visit(n, a) if not v is None: yield v else: for v in f(m_arr - 1, n_arr - 1, (m_arr + sigma) % 2, n, a): yield v if n_arr == m_arr + 1: a[m_arr] = m_arr - 1 v = visit(n, a) if not v is None: yield v while a[n_arr] > 0: a[n_arr] = a[n_arr] - 1 v = visit(n, a) if not v is None: yield v elif n_arr > m_arr + 1: if (m_arr + sigma) % 2 == 1: a[n_arr - 1] = m_arr - 1 else: a[m_arr] = m_arr - 1 func = [f, b][(a[n_arr] + sigma) % 2] for v in func(m_arr, n_arr - 1, 0, n, a): if v is not None: yield v while a[n_arr] > 0: a[n_arr] = a[n_arr] - 1 func = [f, b][(a[n_arr] + sigma) % 2] for v in func(m_arr, n_arr - 1, 0, n, a): if v is not None: yield v def b(m_arr, n_arr, sigma, n, a): if n_arr == m_arr + 1: v = visit(n, a) if not v is None: yield v while a[n_arr] < m_arr - 1: a[n_arr] = a[n_arr] + 1 v = visit(n, a) if not v is None: yield v a[m_arr] = 0 v = visit(n, a) if not v is None: yield v elif n_arr > m_arr + 1: func = [f, b][(a[n_arr] + sigma) % 2] for v in func(m_arr, n_arr - 1, 0, n, a): if v is not None: yield v while a[n_arr] < m_arr - 1: a[n_arr] = a[n_arr] + 1 func = [f, b][(a[n_arr] + sigma) % 2] for v in func(m_arr, n_arr - 1, 0, n, a): if v is not None: yield v if (m_arr + sigma) % 2 == 1: a[n_arr - 1] = 0 else: a[m_arr] = 0 if m_arr <= 2: v = visit(n, a) if not v is None: yield v else: for v in b(m_arr - 1, n_arr - 1, (m_arr + sigma) % 2, n, a): if v is not None: yield v n = len(multiset) a = [0] * (n + 1) for j in xrange(1, m + 1): a[n - m + j] = j - 1 return f(m, n, 0, n, a) def partitions(n, m=None, k=None): """Generate all partitions of integer n (>= 0). 'm' limits the number of parts in the partition, e.g. if m=2 then partitions will contain no more than 2 numbers, while 'k' limits the numbers which may appear in the partition, e.g. k=2 will return partitions with no element greater than 2. Each partition is represented as a dictionary, mapping an integer to the number of copies of that integer in the partition. For example, the first partition of 4 returned is {4: 1}: a single 4. >>> from sympy.utilities.iterables import partitions Maximum key (number in partition) limited with k (in this case, 2): >>> for p in partitions(6, k=2): ... print p {2: 3} {1: 2, 2: 2} {1: 4, 2: 1} {1: 6} Maximum number of parts in partion limited with m (in this case, 2): >>> for p in partitions(6, m=2): ... print p ... {6: 1} {1: 1, 5: 1} {2: 1, 4: 1} {3: 2} Note that the _same_ dictionary object is returned each time. This is for speed: generating each partition goes quickly, taking constant time independent of n. >>> [p for p in partitions(6, k=2)] [{1: 6}, {1: 6}, {1: 6}, {1: 6}] If you want to build a list of the returned dictionaries then make a copy of them: >>> [p.copy() for p in partitions(6, k=2)] [{2: 3}, {1: 2, 2: 2}, {1: 4, 2: 1}, {1: 6}] Reference: modified from Tim Peter's version to allow for k and m values: code.activestate.com/recipes/218332-generator-for-integer-partitions/ """ if n < 0: raise ValueError("n must be >= 0") m = min(m or n, n) if m < 1: raise ValueError("maximum numbers in partition, m, must be > 0") k = min(k or n, n) if k < 1: raise ValueError("maximum value in partition, k, must be > 0") if m*k < n: return q, r = divmod(n, k) ms = {k: q} keys = [k] # ms.keys(), from largest to smallest if r: ms[r] = 1 keys.append(r) room = m - q - bool(r) yield ms while keys != [1]: # Reuse any 1's. if keys[-1] == 1: del keys[-1] reuse = ms.pop(1) room += reuse else: reuse = 0 while 1: # Let i be the smallest key larger than 1. Reuse one # instance of i. i = keys[-1] newcount = ms[i] = ms[i] - 1 reuse += i if newcount == 0: del keys[-1], ms[i] room += 1 # Break the remainder into pieces of size i-1. i -= 1 q, r = divmod(reuse, i) need = q + bool(r) if need > room: if not keys: return continue ms[i] = q keys.append(i) if r: ms[r] = 1 keys.append(r) break room -= need yield ms def binary_partitions(n): """ Generates the binary partition of n. A binary partition consists only of numbers that are powers of two. Each step reduces a 2**(k+1) to 2**k and 2**k. Thus 16 is converted to 8 and 8. Reference: TAOCP 4, section 7.2.1.5, problem 64 Examples: >>> from sympy.utilities.iterables import binary_partitions >>> for i in binary_partitions(5): ... print i ... [4, 1] [2, 2, 1] [2, 1, 1, 1] [1, 1, 1, 1, 1] """ from math import ceil, log pow = int(2**(ceil(log(n, 2)))) sum = 0 partition = [] while pow: if sum + pow <= n: partition.append(pow) sum += pow pow >>= 1 last_num = len(partition) - 1 - (n & 1) while last_num >= 0: yield partition if partition[last_num] == 2: partition[last_num] = 1 partition.append(1) last_num -= 1 continue partition.append(1) partition[last_num] >>= 1 x = partition[last_num + 1] = partition[last_num] last_num += 1 while x > 1: if x <= len(partition) - last_num - 1: del partition[-x + 1:] last_num += 1 partition[last_num] = x else: x >>= 1 yield [1]*n def uniq(seq): ''' Remove repeated elements from an iterable, preserving order of first appearance. Returns a sequence of the same type of the input, or a list if the input was not a sequence. Examples: -------- >>> from sympy.utilities.iterables import uniq >>> uniq([1,4,1,5,4,2,1,2]) [1, 4, 5, 2] >>> uniq((1,4,1,5,4,2,1,2)) (1, 4, 5, 2) >>> uniq(x for x in (1,4,1,5,4,2,1,2)) [1, 4, 5, 2] ''' from sympy.core.function import Tuple seen = set() result = (s for s in seq if not (s in seen or seen.add(s))) if not hasattr(seq, '__getitem__'): return list(result) if isinstance(seq, Tuple): return Tuple(*tuple(result)) return type(seq)(result) def generate_bell(n): """ Generates the bell permutations. In a Bell permutation, each cycle is a decreasing sequence of integers. Reference: [1] Generating involutions, derangements, and relatives by ECO Vincent Vajnovszki, DMTCS vol 1 issue 12, 2010 Examples: >>> from sympy.utilities.iterables import generate_bell >>> list(generate_bell(3)) [(0, 1, 2), (0, 2, 1), (1, 0, 2), (2, 0, 1), (2, 1, 0)] """ P = [i for i in xrange(n)] T = [0] cache = set() def gen(P, T, t): if t == (n - 1): cache.add(tuple(P)) else: for i in T: P[i], P[t+1] = P[t+1], P[i] if tuple(P) not in cache: cache.add(tuple(P)) gen(P, T, t + 1) P[i], P[t+1] = P[t+1], P[i] T.append(t + 1) cache.add(tuple(P)) gen(P, T, t + 1) T.remove(t + 1) gen(P, T, 0) return sorted(cache) def generate_involutions(n): """ Generates involutions. An involution is a permutation that when multiplied by itself equals the identity permutation. In this implementation the involutions are generated using Fixed Points. Alternatively, an involution can be considered as a permutation that does not contain any cycles with a length that is greater than two. Reference: http://mathworld.wolfram.com/PermutationInvolution.html Examples: >>> from sympy.utilities.iterables import \ generate_involutions >>> generate_involutions(3) [(0, 1, 2), (0, 2, 1), (1, 0, 2), (2, 1, 0)] >>> len(generate_involutions(4)) 10 """ P = range(n) # the items of the permutation F = [0] # the fixed points {is this right??} cache = set() def gen(P, F, t): if t == n: cache.add(tuple(P)) else: for j in xrange(len(F)): P[j], P[t] = P[t], P[j] if tuple(P) not in cache: cache.add(tuple(P)) Fj = F.pop(j) gen(P, F, t + 1) F.insert(j, Fj) P[j], P[t] = P[t], P[j] t += 1 F.append(t) cache.add(tuple(P)) gen(P, F, t) F.pop() gen(P, F, 1) return sorted(cache) def generate_derangements(perm): """ Routine to generate derangements. TODO: This will be rewritten to use the ECO operator approach once the permutations branch is in master. Examples: >>> from sympy.utilities.iterables import generate_derangements >>> list(generate_derangements([0,1,2])) [[1, 2, 0], [2, 0, 1]] >>> list(generate_derangements([0,1,2,3])) [[1, 0, 3, 2], [1, 2, 3, 0], [1, 3, 0, 2], [2, 0, 3, 1], \ [2, 3, 0, 1], [2, 3, 1, 0], [3, 0, 1, 2], [3, 2, 0, 1], \ [3, 2, 1, 0]] >>> list(generate_derangements([0,1,1])) [] """ indices = range(len(perm)) p = variations(indices, len(indices)) for rv in \ uniq(tuple(perm[i] for i in idx) \ for idx in p if all(perm[k] != \ perm[idx[k]] for k in xrange(len(perm)))): yield list(rv) def unrestricted_necklace(n, k): """ A routine to generate unrestriced necklaces. Here n is the length of the necklace and k - 1 is the maximum permissible element in the generated necklaces. Reference: http://mathworld.wolfram.com/Necklace.html Examples: >>> from sympy.utilities.iterables import unrestricted_necklace >>> [i[:] for i in unrestricted_necklace(3, 2)] [[0, 0, 0], [0, 1, 1]] >>> [i[:] for i in unrestricted_necklace(4, 4)] [[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 2, 0], [0, 0, 3, 0], \ [0, 1, 1, 1], [0, 1, 2, 1], [0, 1, 3, 1], [0, 2, 2, 2], \ [0, 2, 3, 2], [0, 3, 3, 3]] """ a = [0] * n def gen(t, p): if (t > n - 1): if (n % p == 0): yield a else: a[t] = a[t - p] for necklace in gen(t + 1, p): yield necklace for j in xrange(a[t - p] + 1, k): a[t] = j for necklace in gen(t + 1, t): yield necklace return gen(1, 1) def generate_oriented_forest(n): """ This algorithm generates oriented forests. An oriented graph is a directed graph having no symmetric pair of directed edges. A forest is an acyclic graph, i.e., it has no cycles. A forest can also be described as a disjoint union of trees, which are graphs in which any two vertices are connected by exactly one simple path. Reference: [1] T. Beyer and S.M. Hedetniemi: constant time generation of \ rooted trees, SIAM J. Computing Vol. 9, No. 4, November 1980 [2] http://stackoverflow.com/questions/1633833/ oriented-forest-taocp-algorithm-in-python Examples: >>> from sympy.utilities.iterables import generate_oriented_forest >>> list(generate_oriented_forest(4)) [[0, 1, 2, 3], [0, 1, 2, 2], [0, 1, 2, 1], [0, 1, 2, 0], \ [0, 1, 1, 1], [0, 1, 1, 0], [0, 1, 0, 1], [0, 1, 0, 0], [0, 0, 0, 0]] """ P = range(-1, n) while True: yield P[1:] if P[n] > 0: P[n] = P[P[n]] else: for p in xrange(n - 1, 0, -1): if P[p] != 0: target = P[p] - 1 for q in xrange(p - 1, 0, -1): if P[q] == target: break offset = p - q for i in xrange(p, n + 1): P[i] = P[i - offset] break else: break wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/pkgdata.py0000644000175000017500000000333712014170666025105 0ustar georgeskgeorgesk""" pkgdata is a simple, extensible way for a package to acquire data file resources. The getResource function is equivalent to the standard idioms, such as the following minimal implementation:: import sys, os def getResource(identifier, pkgname=__name__): pkgpath = os.path.dirname(sys.modules[pkgname].__file__) path = os.path.join(pkgpath, identifier) return file(os.path.normpath(path), mode='rb') When a __loader__ is present on the module given by __name__, it will defer getResource to its get_data implementation and return it as a file-like object (such as StringIO). """ import sys import os from cStringIO import StringIO def get_resource(identifier, pkgname=__name__): """ Acquire a readable object for a given package name and identifier. An IOError will be raised if the resource can not be found. For example:: mydata = get_esource('mypkgdata.jpg').read() Note that the package name must be fully qualified, if given, such that it would be found in sys.modules. In some cases, getResource will return a real file object. In that case, it may be useful to use its name attribute to get the path rather than use it as a file-like object. For example, you may be handing data off to a C API. """ mod = sys.modules[pkgname] fn = getattr(mod, '__file__', None) if fn is None: raise IOError("%r has no __file__!") path = os.path.join(os.path.dirname(fn), identifier) loader = getattr(mod, '__loader__', None) if loader is not None: try: data = loader.get_data(path) except IOError: pass else: return StringIO(data) return file(os.path.normpath(path), 'rb') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/0000755000175000017500000000000012014170666024254 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_pytest.py0000644000175000017500000000073612014170666027223 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import raises def test_raises(): class My(Exception): pass class My2(Exception): pass raises(My, "raise My()") try: raises(My, "1+1") assert False except Exception, e: assert str(e) == "DID NOT RAISE" try: raises(My, "raise My2('my text123')") assert False except My2, e: assert str(e) == "my text123" raises(TypeError, 'raises("oops", ValueError)') wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_decorator.py0000644000175000017500000000232512014170666027651 0ustar georgeskgeorgeskfrom sympy.utilities.decorator import threaded, xthreaded from sympy import symbols, Eq, Matrix from sympy.abc import x, y from sympy.core.decorators import wraps def test_threaded(): @threaded def function(expr, *args): return 2*expr + sum(args) assert function(Matrix([[x, y], [1, x]]), 1, 2) == \ Matrix([[2*x+3, 2*y+3], [5, 2*x+3]]) assert function(Eq(x, y), 1, 2) == Eq(2*x+3, 2*y+3) assert function([x, y], 1, 2) == [2*x+3, 2*y+3] assert function((x, y), 1, 2) == (2*x+3, 2*y+3) assert function(set([x, y]), 1, 2) == set([2*x+3, 2*y+3]) @threaded def function(expr, n): return expr**n assert function(x + y, 2) == x**2 + y**2 assert function(x, 2) == x**2 def test_xthreaded(): @xthreaded def function(expr, n): return expr**n assert function(x + y, 2) == (x + y)**2 def test_wraps(): def my_func(x): """My function. """ my_func.is_my_func = True new_my_func = threaded(my_func) new_my_func = wraps(my_func)(new_my_func) assert new_my_func.__name__ == 'my_func' assert new_my_func.__doc__ == 'My function. ' assert hasattr(new_my_func, 'is_my_func') assert new_my_func.is_my_func is True wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_autowrap.py0000644000175000017500000001004712014170666027531 0ustar georgeskgeorgesk# Tests that require installed backends go into # sympy/test_external/test_autowrap import os import tempfile import shutil from StringIO import StringIO from sympy.utilities.autowrap import autowrap, binary_function, CythonCodeWrapper,\ ufuncify from sympy.utilities.codegen import Routine, CCodeGen, CodeGenArgumentListError from sympy.utilities.pytest import raises from sympy.core import symbols, Eq def get_string(dump_fn, routines, prefix="file", header=False, empty=False): """Wrapper for dump_fn. dump_fn writes its results to a stream object and this wrapper returns the contents of that stream as a string. This auxiliary function is used by many tests below. The header and the empty lines are not generator to facilitate the testing of the output. """ output = StringIO() dump_fn(routines, output, prefix, header, empty) source = output.getvalue() output.close() return source def test_cython_wrapper_scalar_function(): x,y,z = symbols('x,y,z') expr = (x+y)*z routine = Routine("test", expr) code_gen = CythonCodeWrapper(CCodeGen()) source = get_string(code_gen.dump_pyx, [routine]) expected = ( 'cdef extern from "file.h":\n' ' double test(double x, double y, double z)\n' 'def test_c(double x, double y, double z):\n' ' return test(x, y, z)\n' ) assert source == expected def test_cython_wrapper_outarg(): from sympy import Equality x,y,z = symbols('x,y,z') code_gen = CythonCodeWrapper(CCodeGen()) routine = Routine("test", Equality(z, x + y)) source = get_string(code_gen.dump_pyx, [routine]) expected = ( 'cdef extern from "file.h":\n' ' void test(double x, double y, double &z)\n' 'def test_c(double x, double y):\n' ' cdef double z\n' ' test(x, y, z)\n' ' return z\n' ) assert source == expected def test_cython_wrapper_inoutarg(): from sympy import Equality x,y,z = symbols('x,y,z') code_gen = CythonCodeWrapper(CCodeGen()) routine = Routine("test", Equality(z, x + y + z)) source = get_string(code_gen.dump_pyx, [routine]) expected = ( 'cdef extern from "file.h":\n' ' void test(double x, double y, double &z)\n' 'def test_c(double x, double y, double z):\n' ' test(x, y, z)\n' ' return z\n' ) assert source == expected def test_autowrap_dummy(): x, y, z = symbols('x y z') # Uses DummyWrapper to test that codegen works as expected f = autowrap(x + y, backend='dummy') assert f() == str(x + y) assert f.args == "x, y" assert f.returns == "nameless" f = autowrap(Eq(z, x + y), backend='dummy') assert f() == str(x + y) assert f.args == "x, y" assert f.returns == "z" f = autowrap(Eq(z, x + y + z), backend='dummy') assert f() == str(x + y + z) assert f.args == "x, y, z" assert f.returns == "z" def test_autowrap_args(): x, y, z = symbols('x y z') raises(CodeGenArgumentListError, "autowrap(Eq(z, x + y), backend='dummy', args=[x])") f = autowrap(Eq(z, x + y), backend='dummy', args=[y, x]) assert f() == str(x + y) assert f.args == "y, x" assert f.returns == "z" raises(CodeGenArgumentListError, "autowrap(Eq(z, x + y + z), backend='dummy', args=[x, y])") f = autowrap(Eq(z, x + y + z), backend='dummy', args=[y, x, z]) assert f() == str(x + y + z) assert f.args == "y, x, z" assert f.returns == "z" def test_autowrap_store_files(): x, y = symbols('x y') tmp = tempfile.mkdtemp() try: f = autowrap(x + y, backend='dummy', tempdir=tmp) assert f() == str(x + y) assert os.access(tmp, os.F_OK) finally: shutil.rmtree(tmp) def test_binary_function(): x, y = symbols('x y') f = binary_function('f', x + y, backend='dummy') assert f._imp_() == str(x + y) def test_ufuncify(): x, y = symbols('x y') f = ufuncify((x, y), x + y, backend='dummy') assert f() == "f(_x[_i], y)" wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_pickling.py0000644000175000017500000003352012014170666027470 0ustar georgeskgeorgeskimport copy import pickle import types from sympy.utilities.pytest import XFAIL from sympy.core.assumptions import AssumeMeths from sympy.core.basic import Atom, Basic from sympy.core.core import BasicMeta, BasicType, ClassRegistry from sympy.core.singleton import SingletonRegistry from sympy.core.symbol import Dummy, Symbol, Wild from sympy.core.numbers import Catalan, ComplexInfinity, EulerGamma, Exp1,\ GoldenRatio, Half, ImaginaryUnit, Infinity, Integer, NaN,\ NegativeInfinity, NegativeOne, Number, NumberSymbol, One, Pi,\ Rational, Float, Zero from sympy.core.relational import Equality, Inequality, Relational,\ StrictInequality, Unequality from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.power import Pow from sympy.core.function import Derivative, Function, FunctionClass, Lambda,\ WildFunction from sympy.core.sets import Interval from sympy.core.multidimensional import vectorize from sympy.core.cache import Memoizer #from sympy.core.ast_parser import SymPyParser, SymPyTransformer from sympy.core.compatibility import callable from sympy import symbols def check(a, check_attr = True): """ Check that pickling and copying round-trips. """ #FIXME-py3k: Add support for protocol 3. for protocol in [0, 1, 2, copy.copy, copy.deepcopy]: if callable(protocol): if isinstance(a, BasicType): # Classes can't be copied, but that's okay. return b = protocol(a) else: b = pickle.loads(pickle.dumps(a, protocol)) d1 = dir(a) d2 = dir(b) assert d1==d2 if not check_attr: continue def c(a,b,d): for i in d: if not hasattr(a,i): continue attr = getattr(a,i) if not hasattr(attr, "__call__"): assert hasattr(b,i), i assert getattr(b,i)==attr c(a,b,d1) c(b,a,d2) #================== core ========================= def test_core_assumptions(): for c in (AssumeMeths, AssumeMeths()): check(c) def test_core_basic(): for c in (Atom, Atom(), Basic, Basic(), BasicMeta, BasicMeta("test"), BasicType, BasicType("test"), ClassRegistry, ClassRegistry(), SingletonRegistry, SingletonRegistry()): check(c) def test_core_symbol(): for c in (Dummy, Dummy("x", False), Symbol, Symbol("x", False), Wild, Wild("x")): check(c) def test_core_numbers(): for c in (Catalan, Catalan(), ComplexInfinity, ComplexInfinity(), EulerGamma, EulerGamma(), Exp1, Exp1(), GoldenRatio, GoldenRatio(), Half, Half(), ImaginaryUnit, ImaginaryUnit(), Infinity, Infinity(), Integer, Integer(2), NaN, NaN(), NegativeInfinity, NegativeInfinity(), NegativeOne, NegativeOne(), Number, Number(15), NumberSymbol, NumberSymbol(), One, One(), Pi, Pi(), Rational, Rational(1,2), Float, Float("1.2"), Zero, Zero()): check(c) def test_core_relational(): x = Symbol("x") y = Symbol("y") for c in (Equality, Equality(x,y), Inequality, Inequality(x,y), Relational, Relational(x,y), StrictInequality, StrictInequality(x,y), Unequality, Unequality(x,y)): check(c) def test_core_add(): x = Symbol("x") for c in (Add, Add(x,4)): check(c) def test_core_mul(): x = Symbol("x") for c in (Mul, Mul(x,4)): check(c) def test_core_power(): x = Symbol("x") for c in (Pow, Pow(x,4)): check(c) def test_core_function(): x = Symbol("x") for f in (Derivative, Derivative(x), Function, FunctionClass, Lambda,\ WildFunction): check(f) @XFAIL def test_core_dynamicfunctions(): # This fails because f is assumed to be a class at sympy.basic.function.f f = Function("f") check(f) def test_core_interval(): for c in (Interval, Interval(0,2)): check(c) def test_core_multidimensional(): for c in (vectorize, vectorize(0)): check(c) @XFAIL def test_core_cache(): for c in (Memoizer, Memoizer()): check(c) # This doesn't have to be pickable. #@XFAIL #def test_core_astparser(): # # This probably fails because of importing the global sympy scope. # for c in (SymPyParser, SymPyParser(), SymPyTransformer, # SymPyTransformer({},{})): # check(c) #================== functions =================== from sympy.functions import (Piecewise, lowergamma, acosh, chebyshevu, chebyshevt, ln, chebyshevt_root, binomial, legendre, Heaviside, Dij, factorial, bernoulli, coth, tanh, assoc_legendre, sign, arg, asin, DiracDelta, re, rf, Abs, uppergamma, binomial, sinh, Ylm, cos, cot, acos, acot, gamma, bell, hermite, harmonic, LambertW, zeta, log, factorial, asinh, acoth, Zlm, cosh, dirichlet_eta, Eijk, loggamma, erf, ceiling, im, fibonacci, conjugate, tan, chebyshevu_root, floor, atanh, sqrt, RisingFactorial, sin, atan, ff, FallingFactorial, lucas, atan2, polygamma, exp) from sympy.core import pi, oo, nan, zoo, E, I def test_functions(): zero_var = (pi, oo, nan, zoo, E, I) one_var = (acosh, ln, Heaviside, Dij, factorial, bernoulli, coth, tanh, sign, arg, asin, DiracDelta, re, Abs, sinh, cos, cot, acos, acot, gamma, bell, harmonic, LambertW, zeta, log, factorial, asinh, acoth, cosh, dirichlet_eta, loggamma, erf, ceiling, im, fibonacci, conjugate, tan, floor, atanh, sin, atan, lucas, exp) two_var = (rf, ff, lowergamma, chebyshevu, chebyshevt, binomial, atan2, polygamma, hermite, legendre, uppergamma) x, y, z = symbols("x,y,z") others = (chebyshevt_root, chebyshevu_root, Eijk(x, y, z), Piecewise( (0, x<-1), (x**2, x<=1), (x**3, True)), assoc_legendre) for a in zero_var: check(a) for cls in one_var: check(cls) c = cls(x) check(c) for cls in two_var: check(cls) c = cls(x, y) check(c) for cls in others: check(cls) #================== geometry ==================== from sympy.geometry.entity import GeometryEntity from sympy.geometry.point import Point from sympy.geometry.ellipse import Circle, Ellipse from sympy.geometry.line import Line, LinearEntity, Ray, Segment from sympy.geometry.polygon import Polygon, RegularPolygon, Triangle def test_geometry(): p1 = Point(1,2) p2 = Point(2,3) p3 = Point(0,0) p4 = Point(0,1) for c in (GeometryEntity, GeometryEntity(), Point, p1, Circle, Circle(p1,2), Ellipse, Ellipse(p1,3,4), Line, Line(p1,p2), LinearEntity, LinearEntity(p1,p2), Ray, Ray(p1,p2), Segment, Segment(p1,p2), Polygon, Polygon(p1,p2,p3,p4), RegularPolygon, RegularPolygon(p1,4,5), Triangle, Triangle(p1,p2,p3)): check(c, check_attr = False) #================== integrals ==================== from sympy.integrals.integrals import Integral def test_integrals(): x = Symbol("x") for c in (Integral, Integral(x)): check(c) #================== matrices ==================== from sympy.matrices.matrices import Matrix, SparseMatrix def test_matrices(): for c in (Matrix, Matrix([1,2,3]), SparseMatrix, SparseMatrix([[1,2],[3,4]])): #FIXME-py3k: This raises sympy.matrices.matrices.ShapeError check(c) #================== ntheory ===================== from sympy.ntheory.generate import Sieve def test_ntheory(): for c in (Sieve, Sieve()): check(c) #================== physics ===================== from sympy.physics.paulialgebra import Pauli from sympy.physics.units import Unit def test_physics(): for c in (Unit, Unit("meter", "m"), Pauli, Pauli(1)): check(c) #================== plotting ==================== # XXX: These tests are not complete. # these depend on ctypes, that are not in python2.4 by default, so XFAIled @XFAIL def test_plotting(): from sympy.plotting.color_scheme import ColorGradient, ColorScheme from sympy.plotting.managed_window import ManagedWindow from sympy.plotting.plot import Plot, ScreenShot from sympy.plotting.plot_axes import PlotAxes, PlotAxesBase, PlotAxesFrame, PlotAxesOrdinate from sympy.plotting.plot_camera import PlotCamera from sympy.plotting.plot_controller import PlotController from sympy.plotting.plot_curve import PlotCurve from sympy.plotting.plot_interval import PlotInterval from sympy.plotting.plot_mode import PlotMode from sympy.plotting.plot_modes import Cartesian2D, Cartesian3D, Cylindrical,\ ParametricCurve2D, ParametricCurve3D, ParametricSurface, Polar, Spherical from sympy.plotting.plot_object import PlotObject from sympy.plotting.plot_surface import PlotSurface from sympy.plotting.plot_window import PlotWindow for c in (ColorGradient, ColorGradient(0.2,0.4), ColorScheme, ManagedWindow, ManagedWindow, Plot, ScreenShot, PlotAxes, PlotAxesBase, PlotAxesFrame, PlotAxesOrdinate, PlotCamera, PlotController, PlotCurve, PlotInterval, PlotMode, Cartesian2D, Cartesian3D, Cylindrical, ParametricCurve2D, ParametricCurve3D, ParametricSurface, Polar, Spherical, PlotObject, PlotSurface, PlotWindow): check(c) @XFAIL def test_plotting2(): from sympy.plotting.color_scheme import ColorGradient, ColorScheme from sympy.plotting.managed_window import ManagedWindow from sympy.plotting.plot import Plot, ScreenShot from sympy.plotting.plot_axes import PlotAxes, PlotAxesBase, PlotAxesFrame, PlotAxesOrdinate from sympy.plotting.plot_camera import PlotCamera from sympy.plotting.plot_controller import PlotController from sympy.plotting.plot_curve import PlotCurve from sympy.plotting.plot_interval import PlotInterval from sympy.plotting.plot_mode import PlotMode from sympy.plotting.plot_modes import Cartesian2D, Cartesian3D, Cylindrical,\ ParametricCurve2D, ParametricCurve3D, ParametricSurface, Polar, Spherical from sympy.plotting.plot_object import PlotObject from sympy.plotting.plot_surface import PlotSurface from sympy.plotting.plot_window import PlotWindow check(ColorScheme("rainbow")) check(Plot(1,visible=False)) check(PlotAxes()) #================== polys ======================= from sympy.polys.polytools import Poly from sympy.polys.polyclasses import DMP, DMF, ANP from sympy.polys.rootoftools import RootOf, RootSum from sympy.polys.domains import ( PythonIntegerRing, SymPyIntegerRing, SymPyRationalField, PolynomialRing, FractionField, ExpressionDomain, ) @XFAIL def test_polys(): x = Symbol("x") ZZ = PythonIntegerRing() QQ = SymPyRationalField() for c in (Poly, Poly(x, x)): check(c) for c in (GFP, GFP([ZZ(1),ZZ(2),ZZ(3)], ZZ(7), ZZ)): check(c) for c in (DMP, DMP([ZZ(1),ZZ(2),ZZ(3)], 0, ZZ)): check(c) for c in (DMF, DMF(([ZZ(1),ZZ(2)],[ZZ(1),ZZ(3)], ZZ))): check(c) for c in (ANP, ANP([QQ(1),QQ(2)], [QQ(1),QQ(2),QQ(3)], QQ)): check(c) for c in (PythonIntegerRing, PythonIntegerRing()): check(c) for c in (SymPyIntegerRing, SymPyIntegerRing()): check(c) for c in (SymPyRationalField, SymPyRationalField()): check(c) for c in (PolynomialRing, PolynomialRing(ZZ, 'x', 'y')): check(c) for c in (FractionField, FractionField(ZZ, 'x', 'y')): check(c) for c in (ExpressionDomain, ExpressionDomain()): check(c) from sympy.polys.domains import HAS_FRACTION, HAS_GMPY if HAS_FRACTION: from sympy.polys.domains import PythonRationalField for c in (PythonRationalField, PythonRationalField()): check(c) if HAS_GMPY: from sympy.polys.domains import GMPYIntegerRing, GMPYRationalField for c in (GMPYIntegerRing, GMPYIntegerRing()): check(c) for c in (GMPYRationalField, GMPYRationalField()): check(c) f = x**3 + x + 3 g = lambda x: x for c in (RootOf, RootOf(f, 0), RootSum, RootSum(f, g)): check(c) #================== printing ==================== from sympy.printing.latex import LatexPrinter from sympy.printing.mathml import MathMLPrinter from sympy.printing.pretty.pretty import PrettyPrinter from sympy.printing.pretty.stringpict import prettyForm, stringPict from sympy.printing.printer import Printer from sympy.printing.python import PythonPrinter def test_printing(): for c in (LatexPrinter, LatexPrinter(), MathMLPrinter, PrettyPrinter, prettyForm, stringPict, stringPict("a"), Printer, Printer(), PythonPrinter, PythonPrinter()): #FIXME-py3k: sympy/printing/printer.py", line 220, in order #FIXME-py3k: return self._settings['order'] #FIXME-py3k: KeyError: 'order' check(c) @XFAIL def test_printing1(): check(MathMLPrinter()) @XFAIL def test_printing2(): check(PrettyPrinter()) #================== series ====================== from sympy.series.limits import Limit from sympy.series.order import Order def test_series(): e = Symbol("e") x = Symbol("x") for c in (Limit, Limit(e, x, 1), Order, Order(e)): check(c) #================== statistics ================== from sympy.statistics.distributions import ContinuousProbability, Normal, Sample, Uniform def test_statistics(): x = Symbol("x") y = Symbol("y") for c in (ContinuousProbability, ContinuousProbability(), Normal, Normal(x,y), Sample, Sample([1,3,4]), Uniform, Uniform(x,y)): check(c) #================== concrete ================== from sympy.concrete.products import Product from sympy.concrete.summations import Sum def test_concrete(): x = Symbol("x") for c in (Product, Product(1,2), Sum, Sum(x, (x, 2, 4))): check(c) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_codegen.py0000644000175000017500000011437712014170666027306 0ustar georgeskgeorgeskfrom StringIO import StringIO from sympy.core import symbols, Eq, pi, Catalan, Lambda, Dummy from sympy.utilities.codegen import CCodeGen, Routine, InputArgument, Result, \ CodeGenError, FCodeGen, codegen, CodeGenArgumentListError, OutputArgument, \ InOutArgument from sympy.utilities.pytest import XFAIL, raises from sympy.utilities.lambdify import implemented_function # import test: #FIXME: Fails due to circular import in with core # from sympy import codegen #FIXME-py3k: Many AssertionErrors here, perhaps related to unicode; #FIXME-py3k: some are just due to an extra space at the end of the string def get_string(dump_fn, routines, prefix="file", header=False, empty=False): """Wrapper for dump_fn. dump_fn writes its results to a stream object and this wrapper returns the contents of that stream as a string. This auxiliary function is used by many tests below. The header and the empty lines are not generator to facilitate the testing of the output. """ output = StringIO() dump_fn(routines, output, prefix, header, empty) source = output.getvalue() output.close() return source def test_Routine_argument_order(): a, x, y, z = symbols('a x y z') expr = (x+y)*z raises(CodeGenArgumentListError, 'Routine("test", expr, argument_sequence=[z, x])') raises(CodeGenArgumentListError, 'Routine("test", Eq(a, expr), argument_sequence=[z, x, y])') r = Routine('test', Eq(a, expr), argument_sequence=[z, x, a, y]) assert [ arg.name for arg in r.arguments ] == [z, x, a, y] assert [ type(arg) for arg in r.arguments ] == [ InputArgument, InputArgument, OutputArgument, InputArgument ] r = Routine('test', Eq(z, expr), argument_sequence=[z, x, y]) assert [ type(arg) for arg in r.arguments ] == [ InOutArgument, InputArgument, InputArgument ] from sympy.tensor import IndexedBase, Idx A, B = map(IndexedBase, ['A', 'B']) m = symbols('m', integer=True) i = Idx('i', m) r = Routine('test', Eq(A[i], B[i]), argument_sequence=[B, A, m]) assert [ arg.name for arg in r.arguments ] == [B.label, A.label, m] def test_empty_c_code(): code_gen = CCodeGen() source = get_string(code_gen.dump_c, []) assert source == "#include \"file.h\"\n#include \n" def test_empty_c_code_with_comment(): code_gen = CCodeGen() source = get_string(code_gen.dump_c, [], header=True) assert source[:82] == ( "/******************************************************************************\n *" ) # " Code generated with sympy 0.7.1 " assert source[158:] == ( "*\n" " * *\n" " * See http://www.sympy.org/ for more information. *\n" " * *\n" " * This file is part of 'project' *\n" " ******************************************************************************/\n" "#include \"file.h\"\n" "#include \n" ) def test_empty_c_header(): code_gen = CCodeGen() source = get_string(code_gen.dump_h, []) assert source == "#ifndef PROJECT__FILE__H\n#define PROJECT__FILE__H\n#endif\n" def test_simple_c_code(): x,y,z = symbols('x,y,z') expr = (x+y)*z routine = Routine("test", expr) code_gen = CCodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include \n" "double test(double x, double y, double z) {\n" " return z*(x + y);\n" "}\n" ) assert source == expected def test_numbersymbol_c_code(): routine = Routine("test", pi**Catalan) code_gen = CCodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include \n" "double test() {\n" " double const Catalan = 0.915965594177219;\n" " return pow(M_PI, Catalan);\n" "}\n" ) assert source == expected def test_c_code_argument_order(): x,y,z = symbols('x,y,z') expr = x + y routine = Routine("test", expr, argument_sequence=[z, x, y]) code_gen = CCodeGen() source = get_string(code_gen.dump_c, [routine]) expected = ( "#include \"file.h\"\n" "#include \n" "double test(double z, double x, double y) {\n" " return x + y;\n" "}\n" ) assert source == expected def test_simple_c_header(): x,y,z = symbols('x,y,z') expr = (x+y)*z routine = Routine("test", expr) code_gen = CCodeGen() source = get_string(code_gen.dump_h, [routine]) expected = ( "#ifndef PROJECT__FILE__H\n" "#define PROJECT__FILE__H\n" "double test(double x, double y, double z);\n" "#endif\n" ) assert source == expected def test_simple_c_codegen(): x,y,z = symbols('x,y,z') expr = (x+y)*z result = codegen(("test", (x+y)*z), "C", "file", header=False, empty=False) expected = [ ("file.c", "#include \"file.h\"\n" "#include \n" "double test(double x, double y, double z) {\n" " return z*(x + y);\n" "}\n"), ("file.h", "#ifndef PROJECT__FILE__H\n" "#define PROJECT__FILE__H\n" "double test(double x, double y, double z);\n" "#endif\n") ] assert result == expected def test_multiple_results_c(): x,y,z = symbols('x,y,z') expr1 = (x+y)*z expr2 = (x-y)*z routine = Routine( "test", [expr1,expr2] ) code_gen = CCodeGen() raises(CodeGenError, 'get_string(code_gen.dump_h, [routine])') def test_no_results_c(): raises(ValueError, 'Routine("test", [])') def test_ansi_math1_codegen(): # not included: log10 from sympy import (acos, asin, atan, ceiling, cos, cosh, floor, log, ln, sin, sinh, sqrt, tan, tanh, N, Abs) x = symbols('x') name_expr = [ ("test_fabs", Abs(x)), ("test_acos", acos(x)), ("test_asin", asin(x)), ("test_atan", atan(x)), ("test_ceil", ceiling(x)), ("test_cos", cos(x)), ("test_cosh", cosh(x)), ("test_floor", floor(x)), ("test_log", log(x)), ("test_ln", ln(x)), ("test_sin", sin(x)), ("test_sinh", sinh(x)), ("test_sqrt", sqrt(x)), ("test_tan", tan(x)), ("test_tanh", tanh(x)), ] result = codegen(name_expr, "C", "file", header=False, empty=False) assert result[0][0] == "file.c" assert result[0][1] == ( '#include "file.h"\n#include \n' 'double test_fabs(double x) {\n return fabs(x);\n}\n' 'double test_acos(double x) {\n return acos(x);\n}\n' 'double test_asin(double x) {\n return asin(x);\n}\n' 'double test_atan(double x) {\n return atan(x);\n}\n' 'double test_ceil(double x) {\n return ceil(x);\n}\n' 'double test_cos(double x) {\n return cos(x);\n}\n' 'double test_cosh(double x) {\n return cosh(x);\n}\n' 'double test_floor(double x) {\n return floor(x);\n}\n' 'double test_log(double x) {\n return log(x);\n}\n' 'double test_ln(double x) {\n return log(x);\n}\n' 'double test_sin(double x) {\n return sin(x);\n}\n' 'double test_sinh(double x) {\n return sinh(x);\n}\n' 'double test_sqrt(double x) {\n return sqrt(x);\n}\n' 'double test_tan(double x) {\n return tan(x);\n}\n' 'double test_tanh(double x) {\n return tanh(x);\n}\n' ) assert result[1][0] == "file.h" assert result[1][1] == ( '#ifndef PROJECT__FILE__H\n#define PROJECT__FILE__H\n' 'double test_fabs(double x);\ndouble test_acos(double x);\n' 'double test_asin(double x);\ndouble test_atan(double x);\n' 'double test_ceil(double x);\ndouble test_cos(double x);\n' 'double test_cosh(double x);\ndouble test_floor(double x);\n' 'double test_log(double x);\ndouble test_ln(double x);\n' 'double test_sin(double x);\ndouble test_sinh(double x);\n' 'double test_sqrt(double x);\ndouble test_tan(double x);\n' 'double test_tanh(double x);\n#endif\n' ) def test_ansi_math2_codegen(): # not included: frexp, ldexp, modf, fmod from sympy import atan2, N x, y = symbols('x,y') name_expr = [ ("test_atan2", atan2(x,y)), ("test_pow", x**y), ] result = codegen(name_expr, "C", "file", header=False, empty=False) assert result[0][0] == "file.c" assert result[0][1] == ( '#include "file.h"\n#include \n' 'double test_atan2(double x, double y) {\n return atan2(x, y);\n}\n' 'double test_pow(double x, double y) {\n return pow(x, y);\n}\n' ) assert result[1][0] == "file.h" assert result[1][1] == ( '#ifndef PROJECT__FILE__H\n#define PROJECT__FILE__H\n' 'double test_atan2(double x, double y);\n' 'double test_pow(double x, double y);\n' '#endif\n' ) def test_complicated_codegen(): from sympy import sin, cos, tan, N x,y,z = symbols('x,y,z') name_expr = [ ("test1", ((sin(x)+cos(y)+tan(z))**7).expand()), ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x+y+z))))))))), ] result = codegen(name_expr, "C", "file", header=False, empty=False) assert result[0][0] == "file.c" assert result[0][1] == ( '#include "file.h"\n#include \n' 'double test1(double x, double y, double z) {\n' ' return ' 'pow(sin(x), 7) + ' '7*pow(sin(x), 6)*cos(y) + ' '7*pow(sin(x), 6)*tan(z) + ' '21*pow(sin(x), 5)*pow(cos(y), 2) + ' '42*pow(sin(x), 5)*cos(y)*tan(z) + ' '21*pow(sin(x), 5)*pow(tan(z), 2) + ' '35*pow(sin(x), 4)*pow(cos(y), 3) + ' '105*pow(sin(x), 4)*pow(cos(y), 2)*tan(z) + ' '105*pow(sin(x), 4)*cos(y)*pow(tan(z), 2) + ' '35*pow(sin(x), 4)*pow(tan(z), 3) + ' '35*pow(sin(x), 3)*pow(cos(y), 4) + ' '140*pow(sin(x), 3)*pow(cos(y), 3)*tan(z) + ' '210*pow(sin(x), 3)*pow(cos(y), 2)*pow(tan(z), 2) + ' '140*pow(sin(x), 3)*cos(y)*pow(tan(z), 3) + ' '35*pow(sin(x), 3)*pow(tan(z), 4) + ' '21*pow(sin(x), 2)*pow(cos(y), 5) + ' '105*pow(sin(x), 2)*pow(cos(y), 4)*tan(z) + ' '210*pow(sin(x), 2)*pow(cos(y), 3)*pow(tan(z), 2) + ' '210*pow(sin(x), 2)*pow(cos(y), 2)*pow(tan(z), 3) + ' '105*pow(sin(x), 2)*cos(y)*pow(tan(z), 4) + ' '21*pow(sin(x), 2)*pow(tan(z), 5) + ' '7*sin(x)*pow(cos(y), 6) + ' '42*sin(x)*pow(cos(y), 5)*tan(z) + ' '105*sin(x)*pow(cos(y), 4)*pow(tan(z), 2) + ' '140*sin(x)*pow(cos(y), 3)*pow(tan(z), 3) + ' '105*sin(x)*pow(cos(y), 2)*pow(tan(z), 4) + ' '42*sin(x)*cos(y)*pow(tan(z), 5) + ' '7*sin(x)*pow(tan(z), 6) + ' 'pow(cos(y), 7) + ' '7*pow(cos(y), 6)*tan(z) + ' '21*pow(cos(y), 5)*pow(tan(z), 2) + ' '35*pow(cos(y), 4)*pow(tan(z), 3) + ' '35*pow(cos(y), 3)*pow(tan(z), 4) + ' '21*pow(cos(y), 2)*pow(tan(z), 5) + ' '7*cos(y)*pow(tan(z), 6) + ' 'pow(tan(z), 7);\n' '}\n' 'double test2(double x, double y, double z) {\n' ' return cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))));\n' '}\n' ) assert result[1][0] == "file.h" assert result[1][1] == ( '#ifndef PROJECT__FILE__H\n' '#define PROJECT__FILE__H\n' 'double test1(double x, double y, double z);\n' 'double test2(double x, double y, double z);\n' '#endif\n' ) def test_loops_c(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n,m = symbols('n m', integer=True) A = IndexedBase('A') x = IndexedBase('x') y = IndexedBase('y') i = Idx('i', m) j = Idx('j', n) (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y[i], A[i, j]*x[j])), "C", "file", header=False, empty=False) assert f1 == 'file.c' expected = ( '#include "file.h"\n' '#include \n' 'void matrix_vector(double *A, int m, int n, double *x, double *y) {\n' ' for (int i=0; i\n' 'void test_dummies(int m_%(mno)i, double *x, double *y) {\n' ' for (int i_%(ino)i=0; i_%(ino)i\n' 'void matrix_vector(double *A, int m, int n, int o, int p, double *x, double *y) {\n' ' for (int i=o; i<%(upperi)s; i++){\n' ' y[i] = 0;\n' ' }\n' ' for (int i=o; i<%(upperi)s; i++){\n' ' for (int j=0; j\n' 'double foo(double x, double &y) {\n' ' y = sin(x);\n' ' return cos(x);\n' '}\n' ) assert result[0][1] == expected def test_empty_f_code(): code_gen = FCodeGen() source = get_string(code_gen.dump_f95, []) assert source == "" def test_empty_f_code_with_header(): code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [], header=True) assert source[:82] == ( "!******************************************************************************\n!*" ) # " Code generated with sympy 0.7.1 " assert source[158:] == ( "*\n" "!* *\n" "!* See http://www.sympy.org/ for more information. *\n" "!* *\n" "!* This file is part of 'project' *\n" "!******************************************************************************\n" ) def test_empty_f_header(): code_gen = FCodeGen() source = get_string(code_gen.dump_h, []) assert source == "" def test_simple_f_code(): x,y,z = symbols('x,y,z') expr = (x+y)*z routine = Routine("test", expr) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "test = z*(x + y)\n" "end function\n" ) assert source == expected def test_numbersymbol_f_code(): routine = Routine("test", pi**Catalan) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test()\n" "implicit none\n" "REAL*8, parameter :: Catalan = 0.915965594177219d0\n" "REAL*8, parameter :: pi = 3.14159265358979d0\n" "test = pi**Catalan\n" "end function\n" ) assert source == expected def test_f_code_argument_order(): x,y,z = symbols('x,y,z') expr = x + y routine = Routine("test", expr, argument_sequence=[z, x, y]) code_gen = FCodeGen() source = get_string(code_gen.dump_f95, [routine]) expected = ( "REAL*8 function test(z, x, y)\n" "implicit none\n" "REAL*8, intent(in) :: z\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "test = x + y\n" "end function\n" ) assert source == expected def test_simple_f_header(): x,y,z = symbols('x,y,z') expr = (x+y)*z routine = Routine("test", expr) code_gen = FCodeGen() source = get_string(code_gen.dump_h, [routine]) expected = ( "interface\n" "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "end function\n" "end interface\n" ) assert source == expected def test_simple_f_codegen(): x,y,z = symbols('x,y,z') expr = (x+y)*z result = codegen(("test", (x+y)*z), "F95", "file", header=False, empty=False) expected = [ ("file.f90", "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "test = z*(x + y)\n" "end function\n"), ("file.h", "interface\n" "REAL*8 function test(x, y, z)\n" "implicit none\n" "REAL*8, intent(in) :: x\n" "REAL*8, intent(in) :: y\n" "REAL*8, intent(in) :: z\n" "end function\n" "end interface\n") ] assert result == expected def test_multiple_results_f(): x,y,z = symbols('x,y,z') expr1 = (x+y)*z expr2 = (x-y)*z routine = Routine( "test", [expr1,expr2] ) code_gen = FCodeGen() raises(CodeGenError, 'get_string(code_gen.dump_h, [routine])') def test_no_results_f(): raises(ValueError, 'Routine("test", [])') def test_intrinsic_math_codegen(): # not included: log10 from sympy import (acos, asin, atan, ceiling, cos, cosh, floor, log, ln, sin, sinh, sqrt, tan, tanh, N, Abs) x = symbols('x') name_expr = [ ("test_abs", Abs(x)), ("test_acos", acos(x)), ("test_asin", asin(x)), ("test_atan", atan(x)), # ("test_ceil", ceiling(x)), ("test_cos", cos(x)), ("test_cosh", cosh(x)), # ("test_floor", floor(x)), ("test_log", log(x)), ("test_ln", ln(x)), ("test_sin", sin(x)), ("test_sinh", sinh(x)), ("test_sqrt", sqrt(x)), ("test_tan", tan(x)), ("test_tanh", tanh(x)), ] result = codegen(name_expr, "F95", "file", header=False, empty=False) assert result[0][0] == "file.f90" expected = ( 'REAL*8 function test_abs(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_abs = Abs(x)\n' 'end function\n' 'REAL*8 function test_acos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_acos = acos(x)\n' 'end function\n' 'REAL*8 function test_asin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_asin = asin(x)\n' 'end function\n' 'REAL*8 function test_atan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_atan = atan(x)\n' 'end function\n' 'REAL*8 function test_cos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_cos = cos(x)\n' 'end function\n' 'REAL*8 function test_cosh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_cosh = cosh(x)\n' 'end function\n' 'REAL*8 function test_log(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_log = log(x)\n' 'end function\n' 'REAL*8 function test_ln(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_ln = log(x)\n' 'end function\n' 'REAL*8 function test_sin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_sin = sin(x)\n' 'end function\n' 'REAL*8 function test_sinh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_sinh = sinh(x)\n' 'end function\n' 'REAL*8 function test_sqrt(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_sqrt = sqrt(x)\n' 'end function\n' 'REAL*8 function test_tan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_tan = tan(x)\n' 'end function\n' 'REAL*8 function test_tanh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'test_tanh = tanh(x)\n' 'end function\n' ) assert result[0][1] == expected assert result[1][0] == "file.h" expected = ( 'interface\n' 'REAL*8 function test_abs(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_acos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_asin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_atan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_cos(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_cosh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_log(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_ln(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_sin(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_sinh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_sqrt(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_tan(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_tanh(x)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'end function\n' 'end interface\n' ) assert result[1][1] == expected def test_intrinsic_math2_codegen(): # not included: frexp, ldexp, modf, fmod from sympy import atan2, N x, y = symbols('x,y') name_expr = [ ("test_atan2", atan2(x,y)), ("test_pow", x**y), ] result = codegen(name_expr, "F95", "file", header=False, empty=False) assert result[0][0] == "file.f90" expected = ( 'REAL*8 function test_atan2(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'test_atan2 = atan2(x, y)\n' 'end function\n' 'REAL*8 function test_pow(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'test_pow = x**y\n' 'end function\n' ) assert result[0][1] == expected assert result[1][0] == "file.h" expected = ( 'interface\n' 'REAL*8 function test_atan2(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test_pow(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'end function\n' 'end interface\n' ) assert result[1][1] == expected def test_complicated_codegen_f95(): from sympy import sin, cos, tan, N x,y,z = symbols('x,y,z') name_expr = [ ("test1", ((sin(x)+cos(y)+tan(z))**7).expand()), ("test2", cos(cos(cos(cos(cos(cos(cos(cos(x+y+z))))))))), ] result = codegen(name_expr, "F95", "file", header=False, empty=False) assert result[0][0] == "file.f90" expected = ( 'REAL*8 function test1(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'test1 = sin(x)**7 + 7*sin(x)**6*cos(y) + 7*sin(x)**6*tan(z) + 21*sin(x) &\n' ' **5*cos(y)**2 + 42*sin(x)**5*cos(y)*tan(z) + 21*sin(x)**5*tan(z) &\n' ' **2 + 35*sin(x)**4*cos(y)**3 + 105*sin(x)**4*cos(y)**2*tan(z) + &\n' ' 105*sin(x)**4*cos(y)*tan(z)**2 + 35*sin(x)**4*tan(z)**3 + 35*sin( &\n' ' x)**3*cos(y)**4 + 140*sin(x)**3*cos(y)**3*tan(z) + 210*sin(x)**3* &\n' ' cos(y)**2*tan(z)**2 + 140*sin(x)**3*cos(y)*tan(z)**3 + 35*sin(x) &\n' ' **3*tan(z)**4 + 21*sin(x)**2*cos(y)**5 + 105*sin(x)**2*cos(y)**4* &\n' ' tan(z) + 210*sin(x)**2*cos(y)**3*tan(z)**2 + 210*sin(x)**2*cos(y) &\n' ' **2*tan(z)**3 + 105*sin(x)**2*cos(y)*tan(z)**4 + 21*sin(x)**2*tan &\n' ' (z)**5 + 7*sin(x)*cos(y)**6 + 42*sin(x)*cos(y)**5*tan(z) + 105* &\n' ' sin(x)*cos(y)**4*tan(z)**2 + 140*sin(x)*cos(y)**3*tan(z)**3 + 105 &\n' ' *sin(x)*cos(y)**2*tan(z)**4 + 42*sin(x)*cos(y)*tan(z)**5 + 7*sin( &\n' ' x)*tan(z)**6 + cos(y)**7 + 7*cos(y)**6*tan(z) + 21*cos(y)**5*tan( &\n' ' z)**2 + 35*cos(y)**4*tan(z)**3 + 35*cos(y)**3*tan(z)**4 + 21*cos( &\n' ' y)**2*tan(z)**5 + 7*cos(y)*tan(z)**6 + tan(z)**7\n' 'end function\n' 'REAL*8 function test2(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'test2 = cos(cos(cos(cos(cos(cos(cos(cos(x + y + z))))))))\n' 'end function\n' ) assert result[0][1] == expected assert result[1][0] == "file.h" expected = ( 'interface\n' 'REAL*8 function test1(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'end function\n' 'end interface\n' 'interface\n' 'REAL*8 function test2(x, y, z)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(in) :: y\n' 'REAL*8, intent(in) :: z\n' 'end function\n' 'end interface\n' ) assert result[1][1] == expected def test_loops(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n, m = symbols('n,m', integer=True) A, x, y = map(IndexedBase, 'Axy') i = Idx('i', m) j = Idx('j', n) (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y[i], A[i, j]*x[j])), "F95", "file", header=False, empty=False) assert f1 == 'file.f90' expected = ( 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'INTEGER*4 :: j\n' 'do i = 1, m\n' ' y(i) = 0\n' 'end do\n' 'do i = 1, m\n' ' do j = 1, n\n' ' y(i) = y(i) + %(rhs)s\n' ' end do\n' 'end do\n' 'end subroutine\n' ) % {'rhs': 'A(i, j)*x(j)'} assert expected == code assert f2 == 'file.h' assert interface == ( 'interface\n' 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'end subroutine\n' 'end interface\n' ) def test_dummy_loops_f95(): from sympy.tensor import IndexedBase, Idx # the following line could also be # [Dummy(s, integer=True) for s in 'im'] # or [Dummy(integer=True) for s in 'im'] i, m = symbols('i m', integer=True, cls=Dummy) x = IndexedBase('x') y = IndexedBase('y') i = Idx(i, m) expected = ( 'subroutine test_dummies(m_%(mcount)i, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m_%(mcount)i\n' 'REAL*8, intent(in), dimension(1:m_%(mcount)i) :: x\n' 'REAL*8, intent(out), dimension(1:m_%(mcount)i) :: y\n' 'INTEGER*4 :: i_%(icount)i\n' 'do i_%(icount)i = 1, m_%(mcount)i\n' ' y(i_%(icount)i) = x(i_%(icount)i)\n' 'end do\n' 'end subroutine\n' ) % {'icount': i.label.dummy_index, 'mcount': m.dummy_index} r = Routine('test_dummies', Eq(y[i], x[i])) c = FCodeGen() code = get_string(c.dump_f95, [r]) assert code == expected def test_loops_InOut(): from sympy.tensor import IndexedBase, Idx from sympy import symbols i,j,n,m = symbols('i,j,n,m', integer=True) A,x,y = symbols('A,x,y') A = IndexedBase(A)[Idx(i, m), Idx(j, n)] x = IndexedBase(x)[Idx(j, n)] y = IndexedBase(y)[Idx(i, m)] (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y, y + A*x)), "F95", "file", header=False, empty=False) assert f1 == 'file.f90' expected = ( 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(inout), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'INTEGER*4 :: j\n' 'do i = 1, m\n' ' do j = 1, n\n' ' y(i) = y(i) + %(rhs)s\n' ' end do\n' 'end do\n' 'end subroutine\n' ) assert (code == expected % {'rhs': 'A(i, j)*x(j)'} or code == expected % {'rhs': 'x(j)*A(i, j)'}) assert f2 == 'file.h' assert interface == ( 'interface\n' 'subroutine matrix_vector(A, m, n, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'REAL*8, intent(in), dimension(1:m, 1:n) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(inout), dimension(1:m) :: y\n' 'end subroutine\n' 'end interface\n' ) def test_partial_loops_f(): # check that loop boundaries are determined by Idx, and array strides # determined by shape of IndexedBase object. from sympy.tensor import IndexedBase, Idx from sympy import symbols n,m,o,p = symbols('n m o p', integer=True) A = IndexedBase('A', shape=(m, p)) x = IndexedBase('x') y = IndexedBase('y') i = Idx('i', (o, m - 5)) # Note: bounds are inclusive j = Idx('j', n) # dimension n corresponds to bounds (0, n - 1) (f1, code), (f2, interface) = codegen( ('matrix_vector', Eq(y[i], A[i, j]*x[j])), "F95", "file", header=False, empty=False) expected = ( 'subroutine matrix_vector(A, m, n, o, p, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'INTEGER*4, intent(in) :: n\n' 'INTEGER*4, intent(in) :: o\n' 'INTEGER*4, intent(in) :: p\n' 'REAL*8, intent(in), dimension(1:m, 1:p) :: A\n' 'REAL*8, intent(in), dimension(1:n) :: x\n' 'REAL*8, intent(out), dimension(1:%(iup-ilow)s) :: y\n' 'INTEGER*4 :: i\n' 'INTEGER*4 :: j\n' 'do i = %(ilow)s, %(iup)s\n' ' y(i) = 0\n' 'end do\n' 'do i = %(ilow)s, %(iup)s\n' ' do j = 1, n\n' ' y(i) = y(i) + %(rhs)s\n' ' end do\n' 'end do\n' 'end subroutine\n' ) % { 'rhs': 'A(i, j)*x(j)', 'iup': str(m - 4), 'ilow': str(1+o), 'iup-ilow': str(m - 4 -o) } assert expected == code def test_output_arg_f(): from sympy import sin, cos, Equality x, y, z = symbols("x,y,z") r = Routine("foo", [Equality(y, sin(x)), cos(x)]) c = FCodeGen() result = c.write([r], "test", header=False, empty=False) assert result[0][0] == "test.f90" assert result[0][1] == ( 'REAL*8 function foo(x, y)\n' 'implicit none\n' 'REAL*8, intent(in) :: x\n' 'REAL*8, intent(out) :: y\n' 'y = sin(x)\n' 'foo = cos(x)\n' 'end function\n' ) def test_inline_function(): from sympy.tensor import IndexedBase, Idx from sympy import symbols n,m = symbols('n m', integer=True) A, x, y = map(IndexedBase, 'Axy') i = Idx('i', m) j = Idx('j', n) p = FCodeGen() func = implemented_function('func', Lambda(n, n*(n+1))) routine = Routine('test_inline', Eq(y[i], func(x[i]))) code = get_string(p.dump_f95, [routine]) expected = ( 'subroutine test_inline(m, x, y)\n' 'implicit none\n' 'INTEGER*4, intent(in) :: m\n' 'REAL*8, intent(in), dimension(1:m) :: x\n' 'REAL*8, intent(out), dimension(1:m) :: y\n' 'INTEGER*4 :: i\n' 'do i = 1, m\n' ' y(i) = (1 + x(i))*x(i)\n' 'end do\n' 'end subroutine\n' ) assert code == expected def test_check_case(): x, X = symbols('x,X') raises(CodeGenError, "codegen(('test', x*X), 'f95', 'prefix')") def test_check_case_false_positive(): # The upper case/lower case exception should not be triggered by Sympy # objects that differ only because of assumptions. (It may be useful to # have a check for that as well, but here we only want to test against # false positives with respect to case checking.) x1 = symbols('x') x2 = symbols('x', my_assumption=True) try: codegen(('test', x1*x2), 'f95', 'prefix') except CodeGenError, e: if e.args[0][0:21] == "Fortran ignores case.": raise AssertionError("This exception should not be raised!") wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_source.py0000644000175000017500000000042612014170666027167 0ustar georgeskgeorgeskfrom sympy.utilities.source import get_mod_func, get_class def test_get_mod_func(): assert get_mod_func('sympy.core.basic.Basic') == ('sympy.core.basic', 'Basic') def test_get_class(): _basic = get_class('sympy.core.basic.Basic') assert _basic.__name__ == 'Basic' wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_code_quality.py0000644000175000017500000001346112014170666030354 0ustar georgeskgeorgeskfrom sympy.core.compatibility import reduce from os import walk, sep, chdir, pardir from os.path import split, join, abspath, exists from glob import glob import re # System path separator (usually slash or backslash) to be # used with excluded files, e.g. # exclude = set([ # "%(sep)smpmath%(sep)s" % sepd, # ]) sepd = {"sep": sep} # path and sympy_path PATH = reduce(join, [split(__file__)[0], pardir, pardir]) # go to sympy/ SYMPY_PATH = abspath(PATH) assert exists(SYMPY_PATH) # Tests can be executed when examples are not installed # (e.g. after "./setup.py install") so set the examples path # to null so it will be skipped by the checker if it is not # there. EXAMPLES_PATH = abspath(reduce(join, [PATH, pardir, "examples"])) if not exists(EXAMPLES_PATH): EXAMPLES_PATH = "" # Error messages message_space = "File contains trailing whitespace: %s, line %s." message_implicit = "File contains an implicit import: %s, line %s." message_tabs = "File contains tabs instead of spaces: %s, line %s." message_carriage = "File contains carriage returns at end of line: %s, line %s" message_str_raise = "File contains string exception: %s, line %s" message_gen_raise = "File contains generic exception: %s, line %s" message_old_raise = "File contains old style raise statement: %s, line %s, \"%s\"" message_eof = "File does not end with a newline: %s, line %s" implicit_test_re = re.compile('^\s*(>>> )?from .* import .*\*') def tab_in_leading(s): """Returns True if there are tabs in the leading whitespace of a line, including the whitespace of docstring code samples.""" n = len(s)-len(s.lstrip()) if not s[n:n+3] in ['...', '>>>']: check = s[:n] else: smore = s[n+3:] check = s[:n] + smore[:len(smore)-len(smore.lstrip())] return not (check.expandtabs() == check) def check_directory_tree(base_path, file_check, exclusions=set()): """ Checks all files in the directory tree (with base_path as starting point) with the file_check function provided, skipping files that contain any of the strings in the set provided by exclusions. """ if not base_path: return for root, dirs, files in walk(base_path): for fname in glob(join(root, "*.py")): if filter(lambda ex: ex in fname, exclusions): continue file_check(fname) def test_whitespace_and_exceptions(): """ This test tests all files in sympy and checks that: o no lines contains a trailing whitespace o no lines end with \r\n o no line uses tabs instead of spaces o that the file ends with a newline o there are no general or string exceptions o there are no old style raise statements """ strRaise = re.compile(r'raise(\s+(\'|\")|\s*(\(\s*)+(\'|\"))') genRaise = re.compile(r'raise(\s+Exception|\s*(\(\s*)+Exception)') oldRaise = re.compile(r'raise(\s+\w+\s*,)') def test(fname): file = open(fname, "rt") # without "t" the lines from all systems may appear to be \n terminated try: line = None # to flag the case where there were no lines in file for idx, line in enumerate(file): if line.endswith(" \n"): assert False, message_space % (fname, idx+1) if line.endswith("\r\n"): assert False, message_carriage % (fname, idx+1) if tab_in_leading(line): assert False, message_tabs % (fname, idx+1) if strRaise.search(line): assert False, message_str_raise % (fname, idx+1) if genRaise.search(line): assert False, message_gen_raise % (fname, idx+1) result = oldRaise.search(line) if result is not None: assert False, message_old_raise % (fname, idx+1, result.group()) finally: if line != None: # eof newline check if not line.endswith('\n'): assert False, message_eof % (fname, idx+1) file.close() exclude = set([ "%(sep)smpmath%(sep)s" % sepd, ]) check_directory_tree(SYMPY_PATH, test, exclude) check_directory_tree(EXAMPLES_PATH, test, exclude) def test_implicit_imports_regular_expression(): candidates_ok = [ "from sympy import something", ">>> from sympy import something", "from sympy.somewhere import something", ">>> from sympy.somewhere import something", "import sympy", ">>> import sympy", "import sympy.something.something", ] candidates_fail = [ "from sympy import *", ">>> from sympy import *", "from sympy.somewhere import *", ">>> from sympy.somewhere import *", ] for c in candidates_ok: assert implicit_test_re.search(c) is None for c in candidates_fail: assert implicit_test_re.search(c) is not None def test_implicit_imports(): """ Tests that all files except __init__.py use explicit imports, even in the docstrings. """ def test(fname): file = open(fname, "r") try: for idx, line in enumerate(file): if implicit_test_re.search(line): assert False, message_implicit % (fname, idx+1) finally: file.close() exclude = set([ "%(sep)s__init__.py" % sepd, "%(sep)sinteractive%(sep)ssession.py" % sepd, # Taken from Python stdlib: "%(sep)sparsing%(sep)ssympy_tokenize.py" % sepd, # these two should be fixed: "%(sep)smpmath%(sep)s" % sepd, "%(sep)splotting%(sep)s" % sepd, ]) check_directory_tree(SYMPY_PATH, test, exclude) check_directory_tree(EXAMPLES_PATH, test, exclude) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_iterables.py0000644000175000017500000003414112014170666027642 0ustar georgeskgeorgeskfrom sympy import symbols, Integral, Tuple, Dummy, Basic from sympy.utilities.iterables import (postorder_traversal, preorder_traversal, flatten, group, take, subsets, variations, cartes, numbered_symbols, dict_merge, prefixes, postfixes, sift, topological_sort, rotate_left, rotate_right, multiset_partitions, partitions, binary_partitions, generate_bell, generate_involutions, generate_derangements, unrestricted_necklace, generate_oriented_forest) from sympy.core.singleton import S from sympy.functions.elementary.piecewise import Piecewise, ExprCondPair from sympy.utilities.pytest import raises w,x,y,z= symbols('w,x,y,z') def test_postorder_traversal(): expr = z+w*(x+y) expected1 = [z, w, y, x, x + y, w*(x + y), z + w*(x + y)] expected2 = [z, w, x, y, x + y, w*(x + y), z + w*(x + y)] expected3 = [w, y, x, x + y, w*(x + y), z, z + w*(x + y)] assert list(postorder_traversal(expr)) in [expected1, expected2, expected3] expr = Piecewise((x,x<1),(x**2,True)) assert list(postorder_traversal(expr)) == [ x, x, 1, x < 1, ExprCondPair(x, x < 1), x, 2, x**2, True, ExprCondPair(x**2, True), Piecewise((x, x < 1), (x**2, True)) ] assert list(preorder_traversal(Integral(x**2, (x, 0, 1)))) == [ Integral(x**2, (x, 0, 1)), x**2, x, 2, Tuple(x, 0, 1), x, 0, 1 ] assert list(preorder_traversal(('abc', ('d', 'ef')))) == [ ('abc', ('d', 'ef')), 'abc', ('d', 'ef'), 'd', 'ef'] def test_preorder_traversal(): expr = z+w*(x+y) expected1 = [z + w*(x + y), z, w*(x + y), w, x + y, y, x] expected2 = [z + w*(x + y), z, w*(x + y), w, x + y, x, y] expected3 = [z + w*(x + y), w*(x + y), w, x + y, y, x, z] assert list(preorder_traversal(expr)) in [expected1, expected2, expected3] expr = Piecewise((x,x<1),(x**2,True)) assert list(preorder_traversal(expr)) == [ Piecewise((x, x < 1), (x**2, True)), ExprCondPair(x, x < 1), x, x < 1, x, 1, ExprCondPair(x**2, True), x**2, x, 2, True ] assert list(postorder_traversal(Integral(x**2, (x, 0, 1)))) == [ x, 2, x**2, x, 0, 1, Tuple(x, 0, 1), Integral(x**2, Tuple(x, 0, 1)) ] assert list(postorder_traversal(('abc', ('d', 'ef')))) == [ 'abc', 'd', 'ef', ('d', 'ef'), ('abc', ('d', 'ef'))] expr = (x**(y**z)) ** (x**(y**z)) expected = [(x**(y**z))**(x**(y**z)), x**(y**z), x**(y**z)] result = [] pt = preorder_traversal(expr) for i in pt: result.append(i) if i == x**(y**z): pt.skip() assert result == expected def test_flatten(): assert flatten((1, (1,))) == [1, 1] assert flatten((x, (x,))) == [x, x] ls = [[(-2, -1), (1, 2)], [(0, 0)]] assert flatten(ls, levels=0) == ls assert flatten(ls, levels=1) == [(-2, -1), (1, 2), (0, 0)] assert flatten(ls, levels=2) == [-2, -1, 1, 2, 0, 0] assert flatten(ls, levels=3) == [-2, -1, 1, 2, 0, 0] raises(ValueError, "flatten(ls, levels=-1)") class MyOp(Basic): pass assert flatten([MyOp(x, y), z]) == [MyOp(x, y), z] assert flatten([MyOp(x, y), z], cls=MyOp) == [x, y, z] def test_group(): assert group([]) == [] assert group([], multiple=False) == [] assert group([1]) == [[1]] assert group([1], multiple=False) == [(1, 1)] assert group([1,1]) == [[1,1]] assert group([1,1], multiple=False) == [(1, 2)] assert group([1,1,1]) == [[1,1,1]] assert group([1,1,1], multiple=False) == [(1, 3)] assert group([1,2,1]) == [[1],[2],[1]] assert group([1,2,1], multiple=False) == [(1, 1), (2, 1), (1, 1)] assert group([1,1,2,2,2,1,3,3]) == [[1,1], [2,2,2], [1], [3,3]] assert group([1,1,2,2,2,1,3,3], multiple=False) == [(1, 2), (2, 3), (1, 1), (3, 2)] def test_subsets(): # combinations assert list(subsets([1, 2, 3], 0)) == [[]] assert list(subsets([1, 2, 3], 1)) == [[1], [2], [3]] assert list(subsets([1, 2, 3], 2)) == [[1, 2], [1,3], [2, 3]] assert list(subsets([1, 2, 3], 3)) == [[1, 2, 3]] l = range(4) assert list(subsets(l, 0, repetition=True)) == [[]] assert list(subsets(l, 1, repetition=True)) == [[0], [1], [2], [3]] assert list(subsets(l, 2, repetition=True)) == [[0, 0], [0, 1], [0, 2], [0, 3], [1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]] assert list(subsets(l, 3, repetition=True)) == [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 2, 2], [0, 2, 3], [0, 3, 3], [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 2], [2, 2, 3], [2, 3, 3], [3, 3, 3]] assert len(list(subsets(l, 4, repetition=True))) == 35 assert list(subsets(l[:2], 3, repetition=False)) == [] assert list(subsets(l[:2], 3, repetition=True)) == [[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 1, 1]] assert list(subsets([1, 2], repetition=True)) == \ [[], [1], [2], [1, 1], [1, 2], [2, 2]] assert list(subsets([1, 2], repetition=False)) == \ [[], [1], [2], [1, 2]] assert list(subsets([1, 2, 3], 2)) == \ [[1, 2], [1, 3], [2, 3]] assert list(subsets([1, 2, 3], 2, repetition=True)) == \ [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]] def test_variations(): # permutations l = range(4) assert list(variations(l, 0, repetition=False)) == [[]] assert list(variations(l, 1, repetition=False)) == [[0], [1], [2], [3]] assert list(variations(l, 2, repetition=False)) == [[0, 1], [0, 2], [0, 3], [1, 0], [1, 2], [1, 3], [2, 0], [2, 1], [2, 3], [3, 0], [3, 1], [3, 2]] assert list(variations(l, 3, repetition=False)) == [[0, 1, 2], [0, 1, 3], [0, 2, 1], [0, 2, 3], [0, 3, 1], [0, 3, 2], [1, 0, 2], [1, 0, 3], [1, 2, 0], [1, 2, 3], [1, 3, 0], [1, 3, 2], [2, 0, 1], [2, 0, 3], [2, 1, 0], [2, 1, 3], [2, 3, 0], [2, 3, 1], [3, 0, 1], [3, 0, 2], [3, 1, 0], [3, 1, 2], [3, 2, 0], [3, 2, 1]] assert list(variations(l, 0, repetition=True)) == [[]] assert list(variations(l, 1, repetition=True)) == [[0], [1], [2], [3]] assert list(variations(l, 2, repetition=True)) == [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3]] assert len(list(variations(l, 3, repetition=True))) == 64 assert len(list(variations(l, 4, repetition=True))) == 256 assert list(variations(l[:2], 3, repetition=False)) == [] assert list(variations(l[:2], 3, repetition=True)) == [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]] def test_cartes(): assert list(cartes([1, 2], [3, 4, 5])) == \ [[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5]] assert list(cartes()) == [[]] def test_numbered_symbols(): s = numbered_symbols(cls=Dummy) assert isinstance(s.next(), Dummy) def test_sift(): assert sift(range(5), lambda _: _%2) == {1: [1, 3], 0: [0, 2, 4]} assert sift(x + y, lambda _: _.has(x)) == {False: [y], True: [x]} assert sift(x*y, lambda _: _.has(x)) == {False: [y], True: [x]} assert sift(S.One, lambda _: _.has(x)) == {False: [1]} def test_take(): X = numbered_symbols() assert take(X, 5) == list(symbols('x0:5')) assert take(X, 5) == list(symbols('x5:10')) assert take([1,2,3,4,5], 5) == [1,2,3,4,5] def test_dict_merge(): assert dict_merge({}, {1: x, y: z}) == {1: x, y: z} assert dict_merge({1: x, y: z}, {}) == {1: x, y: z} assert dict_merge({2: z}, {1: x, y: z}) == {1: x, 2: z, y: z} assert dict_merge({1: x, y: z}, {2: z}) == {1: x, 2: z, y: z} assert dict_merge({1: y, 2: z}, {1: x, y: z}) == {1: x, 2: z, y: z} assert dict_merge({1: x, y: z}, {1: y, 2: z}) == {1: y, 2: z, y: z} def test_prefixes(): assert list(prefixes([])) == [] assert list(prefixes([1])) == [[1]] assert list(prefixes([1, 2])) == [[1], [1, 2]] assert list(prefixes([1,2,3,4,5])) == \ [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]] def test_postfixes(): assert list(postfixes([])) == [] assert list(postfixes([1])) == [[1]] assert list(postfixes([1, 2])) == [[2], [1, 2]] assert list(postfixes([1,2,3,4,5])) == \ [[5], [4, 5], [3, 4, 5], [2, 3, 4, 5], [1, 2, 3, 4, 5]] def test_topological_sort(): V = [2, 3, 5, 7, 8, 9, 10, 11] E = [(7, 11), (7, 8), (5, 11), (3, 8), (3, 10), (11, 2), (11, 9), (11, 10), (8, 9)] assert topological_sort((V, E)) == [3, 5, 7, 8, 11, 2, 9, 10] assert topological_sort((V, E), key=lambda v: -v) == [7, 5, 11, 3, 10, 8, 9, 2] raises(ValueError, "topological_sort((V, E + [(10, 7)]))") def test_rotate(): A = [0, 1, 2, 3, 4] assert rotate_left(A, 2) == [2, 3, 4, 0, 1] assert rotate_right(A, 1) == [4, 0, 1, 2, 3] def test_multiset_partitions(): A = [0, 1, 2, 3, 4] assert list(multiset_partitions(A, 5)) == [[[0], [1], [2], [3], [4]]] assert len(list(multiset_partitions(A, 4))) == 10 assert len(list(multiset_partitions(A, 3))) == 25 assert list(multiset_partitions([1,1,1,2,2], 2)) == [[[1, 1, 1, 2], [2]],\ [[1, 1, 2], [1, 2]], [[1, 1], [1, 2, 2]], [[1], [1, 1, 2, 2]], [[1, 2],\ [1, 1, 2]], [[1, 1, 2, 2], [1]], [[1, 2, 2], [1, 1]]] assert list(multiset_partitions([1,1,2,2], 2)) == [[[1, 1, 2], [2]], \ [[1, 2], [1, 2]], [[1], [1, 2, 2]], [[1, 1], [2, 2]], [[1, 2, 2], [1]]] assert list(multiset_partitions([1,2,3,4], 2)) == [[[1, 2, 3], [4]], [[1, 3], \ [2, 4]], [[1], [2, 3, 4]], [[1, 2], [3, 4]], [[1, 2, 4], [3]], \ [[1, 4], [2, 3]], [[1, 3, 4], [2]]] assert list(multiset_partitions([1,2,2], 2)) == [[[1, 2], [2]], [[1], [2, 2]]] def test_partitions(): assert [p.copy() for p in partitions(6, k=2)] == [{2: 3}, \ {1: 2, 2: 2}, {1: 4, 2: 1}, {1: 6}] assert [p.copy() for p in partitions(6, k=3)] == [{3: 2}, \ {1: 1, 2: 1, 3: 1}, {1: 3, 3: 1}, {2: 3}, {1: 2, 2: 2}, \ {1: 4, 2: 1}, {1: 6}] assert [p.copy() for p in partitions(6, k=2, m=2)] == [] assert [p.copy() for p in partitions(8, k=4, m=3)] == [{4: 2},\ {1: 1, 3: 1, 4: 1}, {2: 2, 4: 1}, {2: 1, 3: 2}] def test_binary_partitions(): assert [i[:] for i in binary_partitions(10)] == [[8, 2], [8, 1, 1], \ [4, 4, 2], [4, 4, 1, 1], [4, 2, 2, 2], [4, 2, 2, 1, 1], [4, 2, 1, 1, 1, 1], \ [4, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [2, 2, 2, 2, 1, 1], \ [2, 2, 2, 1, 1, 1, 1], [2, 2, 1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1, 1, 1, 1, 1], \ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] assert len([j[:] for j in binary_partitions(16)]) == 36 def test_bell_perm(): assert [len(generate_bell(i)) for i in xrange(1, 7)] == [1, 2, 5, 15, 52, 203] assert list(generate_bell(4)) == [(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 3, 1, 2), (0, 3, 2, 1), (1, 0, 2, 3), (1, 0, 3, 2), (2, 0, 1, 3), (2, 1, 0, 3), (2, 3, 0, 1), (3, 0, 1, 2), (3, 0, 2, 1), (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 1, 0)] def test_involutions(): assert [len(generate_involutions(n)) for n in range(1, 7)] == [1, 2, 4, 10, 26, 76] assert generate_involutions(4) == [(0, 1, 2, 3), (0, 1, 3, 2), (0, 2, 1, 3), (0, 3, 2, 1), (1, 0, 2, 3), (2, 1, 0, 3), (3, 0, 2, 1), (3, 1, 0, 2), (3, 1, 2, 0), (3, 2, 1, 0)] def test_derangements(): assert len(list(generate_derangements([0, 1, 2, 3, 4, 5]))) == 265 assert list(generate_derangements([0, 1, 2, 3])) == [[1, 0, 3, 2], \ [1, 2, 3, 0], [1, 3, 0, 2], [2, 0, 3, 1], [2, 3, 0, 1], [2, 3, 1, 0], \ [3, 0, 1, 2], [3, 2, 0, 1], [3, 2, 1, 0]] assert list(generate_derangements([0, 1, 2, 2])) == [[2, 2, 0, 1], \ [2, 2, 1, 0]] def test_unrestricted_necklaces(): assert [i[:] for i in unrestricted_necklace(4, 5)] == [[0, 0, 0, 0], \ [0, 0, 1, 0], [0, 0, 2, 0], [0, 0, 3, 0], [0, 0, 4, 0], [0, 1, 1, 1], \ [0, 1, 2, 1], [0, 1, 3, 1], [0, 1, 4, 1], [0, 2, 2, 2], [0, 2, 3, 2], \ [0, 2, 4, 2], [0, 3, 3, 3], [0, 3, 4, 3], [0, 4, 4, 4]] assert [i[:] for i in unrestricted_necklace(6, 3)] == [[0, 0, 0, 0, 0, 0],\ [0, 0, 0, 1, 0, 0], [0, 0, 0, 2, 0, 0], [0, 0, 1, 0, 1, 0], \ [0, 0, 1, 1, 0, 1], [0, 0, 1, 2, 0, 1], [0, 0, 2, 0, 2, 0], \ [0, 0, 2, 1, 0, 2], [0, 0, 2, 2, 0, 2], [0, 1, 1, 1, 1, 1], \ [0, 1, 1, 2, 1, 1], [0, 1, 2, 1, 2, 1], [0, 1, 2, 2, 1, 2], \ [0, 2, 2, 2, 2, 2]] assert len(list(unrestricted_necklace(20, 2))) == 111 def test_generate_oriented_forest(): assert list(generate_oriented_forest(5)) == [[0, 1, 2, 3, 4], \ [0, 1, 2, 3, 3], [0, 1, 2, 3, 2], [0, 1, 2, 3, 1], [0, 1, 2, 3, 0], \ [0, 1, 2, 2, 2], [0, 1, 2, 2, 1], [0, 1, 2, 2, 0], [0, 1, 2, 1, 2], \ [0, 1, 2, 1, 1], [0, 1, 2, 1, 0], [0, 1, 2, 0, 1], [0, 1, 2, 0, 0], \ [0, 1, 1, 1, 1], [0, 1, 1, 1, 0], [0, 1, 1, 0, 1], [0, 1, 1, 0, 0], \ [0, 1, 0, 1, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 0]] assert len(list(generate_oriented_forest(10))) == 1842 wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/tests/test_lambdify.py0000644000175000017500000002577612014170666027475 0ustar georgeskgeorgeskfrom sympy.utilities.pytest import XFAIL, raises from sympy import (symbols, lambdify, sqrt, sin, cos, pi, atan, Rational, Float, Matrix, Lambda, exp, Integral, oo, I) from sympy.printing.lambdarepr import LambdaPrinter from sympy import mpmath from sympy.utilities.lambdify import implemented_function import math, sympy x,y,z = symbols('x,y,z') #================== Test different arguments ============== def test_no_args(): f = lambdify([], 1) try: f(-1) assert False except TypeError: pass assert f() == 1 def test_single_arg(): f = lambdify(x, 2*x) assert f(1) == 2 def test_list_args(): f = lambdify([x,y], x+y) assert f(1,2) == 3 def test_str_args(): f = lambdify('x,y,z', 'z,y,x') assert f(3,2,1) == (1,2,3) assert f(1.0,2.0,3.0) == (3.0,2.0,1.0) # make sure correct number of args required try: f(0) assert False except TypeError: pass def test_own_namespace(): myfunc = lambda x:1 f = lambdify(x, sin(x), {"sin":myfunc}) assert f(0.1) == 1 assert f(100) == 1 def test_own_module(): f = lambdify(x, sin(x), math) assert f(0)==0.0 f = lambdify(x, sympy.ceiling(x), math) try: f(4.5) assert False except NameError: pass def test_bad_args(): try: # no vargs given f = lambdify(1) assert False except TypeError: pass try: # same with vector exprs f = lambdify([1,2]) assert False except TypeError: pass def test_atoms(): # Non-Symbol atoms should not be pulled out from the expression namespace f = lambdify(x, pi + x, {"pi": 3.14}) assert f(0) == 3.14 f = lambdify(x, I + x, {"I": 1j}) assert f(1) == 1 + 1j #================== Test different modules ================ # high precision output of sin(0.2*pi) is used to detect if precision is lost unwanted def test_sympy_lambda(): dps = mpmath.mp.dps mpmath.mp.dps = 50 try: sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "sympy") assert f(x) == sin(x) prec = 1e-15 assert -prec < f(Rational(1,5)).evalf() - Float(str(sin02)) < prec try: # arctan is in numpy module and should not be available f = lambdify(x, arctan(x), "sympy") assert False except NameError: pass finally: mpmath.mp.dps = dps def test_math_lambda(): dps = mpmath.mp.dps mpmath.mp.dps = 50 try: sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "math") prec = 1e-15 assert -prec < f(0.2) - sin02 < prec try: f(x) # if this succeeds, it can't be a python math function assert False except ValueError: pass finally: mpmath.mp.dps = dps def test_mpmath_lambda(): dps = mpmath.mp.dps mpmath.mp.dps = 50 try: sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin(x), "mpmath") prec = 1e-49 # mpmath precision is around 50 decimal places assert -prec < f(mpmath.mpf("0.2")) - sin02 < prec try: f(x) # if this succeeds, it can't be a mpmath function assert False except TypeError: pass finally: mpmath.mp.dps = dps @XFAIL def test_number_precision(): dps = mpmath.mp.dps mpmath.mp.dps = 50 try: sin02 = mpmath.mpf("0.19866933079506121545941262711838975037020672954020") f = lambdify(x, sin02, "mpmath") prec = 1e-49 # mpmath precision is around 50 decimal places assert -prec < f(0) - sin02 < prec finally: mpmath.mp.dps = dps #================== Test Translations ===================== # We can only check if all translated functions are valid. It has to be checked # by hand if they are complete. def test_math_transl(): from sympy.utilities.lambdify import MATH_TRANSLATIONS for sym, mat in MATH_TRANSLATIONS.iteritems(): assert sym in sympy.__dict__ assert mat in math.__dict__ def test_mpmath_transl(): from sympy.utilities.lambdify import MPMATH_TRANSLATIONS for sym, mat in MPMATH_TRANSLATIONS.iteritems(): assert sym in sympy.__dict__ or sym == 'Matrix' assert mat in mpmath.__dict__ #================== Test some functions =================== def test_exponentiation(): f = lambdify(x, x**2) assert f(-1) == 1 assert f(0) == 0 assert f(1) == 1 assert f(-2) == 4 assert f(2) == 4 assert f(2.5) == 6.25 def test_sqrt(): f = lambdify(x, sqrt(x)) assert f(0) == 0.0 assert f(1) == 1.0 assert f(4) == 2.0 assert abs(f(2) - 1.414) < 0.001 assert f(6.25) == 2.5 try: #FIXME-py3k: In Python 3, sqrt(-1) is a ValueError but (-1)**(1/2) isn't #FIXME-py3k: (previously both were). Change the test, or check Py version? f(-1) assert False except ValueError: pass def test_trig(): f = lambdify([x], [cos(x),sin(x)]) d = f(pi) prec = 1e-11 assert -prec < d[0]+1 < prec assert -prec < d[1] < prec d = f(3.14159) prec = 1e-5 assert -prec < d[0]+1 < prec assert -prec < d[1] < prec #================== Test vectors ========================== def test_vector_simple(): f = lambdify((x,y,z), (z,y,x)) assert f(3,2,1) == (1,2,3) assert f(1.0,2.0,3.0) == (3.0,2.0,1.0) # make sure correct number of args required try: f(0) assert False except TypeError: pass def test_vector_discontinuous(): f = lambdify(x, (-1/x, 1/x)) try: f(0) assert False except ZeroDivisionError: pass assert f(1) == (-1.0, 1.0) assert f(2) == (-0.5, 0.5) assert f(-2) == (0.5, -0.5) def test_trig_symbolic(): f = lambdify([x], [cos(x),sin(x)]) d = f(pi) assert abs(d[0]+1) < 0.0001 assert abs(d[1]-0) < 0.0001 def test_trig_float(): f = lambdify([x], [cos(x),sin(x)]) d = f(3.14159) assert abs(d[0]+1) < 0.0001 assert abs(d[1]-0) < 0.0001 def test_docs(): f = lambdify(x, x**2) assert f(2) == 4 f = lambdify([x,y,z], [z,y,x]) assert f(1, 2, 3) == [3, 2, 1] f = lambdify(x, sqrt(x)) assert f(4) == 2.0 f = lambdify((x,y), sin(x*y)**2) assert f(0, 5) == 0 def test_math(): f = lambdify((x, y), sin(x), modules="math") assert f(0, 5) == 0 def test_sin(): f = lambdify(x, sin(x)**2) assert isinstance(f(2), float) f = lambdify(x, sin(x)**2, modules="math") assert isinstance(f(2), float) def test_matrix(): A = Matrix([[x, x*y], [sin(z)+4, x**z]]) sol = Matrix([[1, 2], [sin(3)+4, 1]]) f = lambdify((x,y,z), A, modules="sympy") assert f(1,2,3) == sol f = lambdify((x,y,z), (A, [A]), modules="sympy") assert f(1,2,3) == (sol,[sol]) def test_integral(): f = Lambda(x, exp(-x**2)) l = lambdify(x, Integral(f(x), (x, -oo, oo)), modules="sympy") assert l(x) == Integral(exp(-x**2), (x, -oo, oo)) #########Test Symbolic########### def test_sym_single_arg(): f = lambdify(x, x * y) assert f(z) == z * y def test_sym_list_args(): f = lambdify([x,y], x + y + z) assert f(1,2) == 3 + z def test_sym_integral(): f = Lambda(x, exp(-x**2)) l = lambdify(x, Integral(f(x), (x, -oo, oo)), modules="sympy") assert l(y).doit() == sqrt(pi) def test_namespace_order(): # lambdify had a bug, such that module dictionaries or cached module # dictionaries would pull earlier namespaces into themselves. # Because the module dictionaries form the namespace of the # generated lambda, this meant that the behavior of a previously # generated lambda function could change as a result of later calls # to lambdify. n1 = {'f': lambda x:'first f'} n2 = {'f': lambda x:'second f', 'g': lambda x:'function g'} f = sympy.Function('f') g = sympy.Function('g') if1 = lambdify(x, f(x), modules=(n1, "sympy")) assert if1(1) == 'first f' if2 = lambdify(x, g(x), modules=(n2, "sympy")) # previously gave 'second f' assert if1(1) == 'first f' def test_imps(): # Here we check if the default returned functions are anonymous - in # the sense that we can have more than one function with the same name f = implemented_function('f', lambda x: 2*x) g = implemented_function('f', lambda x: math.sqrt(x)) l1 = lambdify(x, f(x)) l2 = lambdify(x, g(x)) assert str(f(x)) == str(g(x)) assert l1(3) == 6 assert l2(3) == math.sqrt(3) # check that we can pass in a Function as input func = sympy.Function('myfunc') assert not hasattr(func, '_imp_') my_f = implemented_function(func, lambda x: 2*x) assert hasattr(func, '_imp_') # Error for functions with same name and different implementation f2 = implemented_function("f", lambda x : x+101) raises(ValueError, 'lambdify(x, f(f2(x)))') def test_lambdify_imps(): # Test lambdify with implemented functions # first test basic (sympy) lambdify f = sympy.cos assert lambdify(x, f(x))(0) == 1 assert lambdify(x, 1 + f(x))(0) == 2 assert lambdify((x, y), y + f(x))(0, 1) == 2 # make an implemented function and test f = implemented_function("f", lambda x : x+100) assert lambdify(x, f(x))(0) == 100 assert lambdify(x, 1 + f(x))(0) == 101 assert lambdify((x, y), y + f(x))(0, 1) == 101 # Can also handle tuples, lists, dicts as expressions lam = lambdify(x, (f(x), x)) assert lam(3) == (103, 3) lam = lambdify(x, [f(x), x]) assert lam(3) == [103, 3] lam = lambdify(x, [f(x), (f(x), x)]) assert lam(3) == [103, (103, 3)] lam = lambdify(x, {f(x): x}) assert lam(3) == {103: 3} lam = lambdify(x, {f(x): x}) assert lam(3) == {103: 3} lam = lambdify(x, {x: f(x)}) assert lam(3) == {3: 103} # Check that imp preferred to other namespaces by default d = {'f': lambda x : x + 99} lam = lambdify(x, f(x), d) assert lam(3) == 103 # Unless flag passed lam = lambdify(x, f(x), d, use_imps=False) assert lam(3) == 102 #================== Test special printers ========================== def test_special_printers(): class IntervalPrinter(LambdaPrinter): """Use ``lambda`` printer but print numbers as ``mpi`` intervals. """ def _print_Integer(self, expr): return "mpi('%s')" % super(IntervalPrinter, self)._print_Integer(expr) def _print_Rational(self, expr): return "mpi('%s')" % super(IntervalPrinter, self)._print_Rational(expr) def intervalrepr(expr): return IntervalPrinter().doprint(expr) expr = sympy.sqrt(sympy.sqrt(2) + sympy.sqrt(3)) + sympy.S(1)/2 func0 = lambdify((), expr, modules="mpmath", printer=intervalrepr) func1 = lambdify((), expr, modules="mpmath", printer=IntervalPrinter) func2 = lambdify((), expr, modules="mpmath", printer=IntervalPrinter()) mpi = type(mpmath.mpi(1, 2)) assert isinstance(func0(), mpi) assert isinstance(func1(), mpi) assert isinstance(func2(), mpi) wxgeometrie-0.133.2.orig/wxgeometrie/sympy/utilities/__init__.py0000644000175000017500000000077212014170666025231 0ustar georgeskgeorgesk"""Some utilities that may help. """ from iterables import (flatten, group, take, subsets, variations, numbered_symbols, cartes, capture, dict_merge, postorder_traversal, preorder_traversal, interactive_traversal, prefixes, postfixes, sift, topological_sort) from lambdify import lambdify from source import source from decorator import threaded, xthreaded from runtests import test, doctest from cythonutils import cythonized from timeutils import timed from misc import default_sort_key wxgeometrie-0.133.2.orig/wxgeometrie/sympy/__init__.py0000644000175000017500000000265012014170666023213 0ustar georgeskgeorgesk""" SymPy is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS) while keeping the code as simple as possible in order to be comprehensible and easily extensible. SymPy is written entirely in Python and does not require any external libraries, except optionally for plotting support. See the webpage for more information and documentation: http://code.google.com/p/sympy/""" __version__ = "0.7.1" def __sympy_debug(): # helper function so we don't import os globally import os return eval(os.getenv('SYMPY_DEBUG', 'False')) SYMPY_DEBUG = __sympy_debug() from sympy.core import * from logic import * from assumptions import * from polys import * from series import * from functions import * from ntheory import * from concrete import * from simplify import * from solvers import * from matrices import * from geometry import * from utilities import * from integrals import * from tensor import * from parsing import * # This module is slow to import: #from physics import units from plotting import Plot, textplot from printing import pretty, pretty_print, pprint, pprint_use_unicode, \ pprint_try_use_unicode, print_gtk, print_tree from printing import ccode, fcode, latex, preview from printing import python, print_python, srepr, sstr, sstrrepr from interactive import init_session, init_printing evalf._create_evalf_table() # This is slow to import: #import abc wxgeometrie-0.133.2.orig/wxgeometrie/doc/0000755000175000017500000000000012014170666020463 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/doc/license.txt0000644000175000017500000003544612014170666022662 0ustar georgeskgeorgesk GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS wxgeometrie-0.133.2.orig/wxgeometrie/doc/help.htm0000644000175000017500000007243112014170666022134 0ustar georgeskgeorgesk Aide de WxGéométrie

WxGéométrie

Dynamic geometry, graph plotter, and more for french mathematic teachers.
Copyright © 2005-2011 Nicolas Pourcelot

http://wxgeo.free.fr

Sommaire

  1. Licence

  2. Installation

  3. Premiers pas

    1. Le module de géométrie dynamique
    2. Le traceur de courbes
    3. La calculatrice
    4. Le module de statistiques
    5. Le générateur d'arbres de probabilités
  4. Utilisation avancée

    1. Le fichier param.py
    2. Débogage
    3. La ligne de commande
  5. Comment contribuer ?

  6. Remerciements

I. LICENCE

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301

Ce programme est un logiciel libre; vous pouvez le redistribuer et/ou le modifier selon les termes de la GNU General Public Licence telle qu'elle a été publiée par la Free Software Foundation; soit la version 2 de la licence, ou (au choix) toute version ultérieure.
Ce programme est distribué dans l'espoir qu'il puisse être utile, mais sans aucune garantie, pas même la garantie implicite qu'il puisse etre adapté à un usage donné. Pour plus de précisions, lisez la GNU General Public License.
Vous avez recu en principe une copie de la GNU General Public License en même temps que ce programme. Si ce n'est pas le cas, écrivez a l'adresse suivante : Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301


II. INSTALLATION

Pour fonctionner, ce programme nécessite la configuration logicielle suivante :
  • Windows 98 ou supérieur, ou une distribution Linux assez récente.
  • Le logiciel Python, version 2.4 ou supérieure.
    (Logiciel libre, disponible gratuitement sur http://www.python.org )
    Sous Linux, une version récente est en principe déjà présente.
  • La librairie graphique WxPython, version 2.6 (des problèmes subsistent avec la version 2.8 sous Windows).
    (Logiciel libre, disponible gratuitement sur http://www.wxpython.org)
    Sous Linux, une version est en principe deja présente, mais elle n'est pas toujours assez récente.
  • La librairie mathématique numpy
    (Logiciel libre, disponible gratuitement sur http://sourceforge.net/project/showfiles.php?group_id=1369&package_id=175103)
  • La librairie mathématique Matplotlib, version 0.91.2 ou supérieure
    (Logiciel libre, disponible gratuitement sur http://www.sourceforge.net/projects/matplotlib)


Remarque
: ces logiciels doivent être installés dans l'ordre mentionné.

Sous Windows, un redémarrage du système est conseillé après ces installations.

Après avoir téléchargé la dernière version de WxGeometrie (http://www.sourceforge.net/projects/wxgeometrie), dézippez l'archive dans un répertoire sur lequel vous avez les permissions nécessaires.
Vous pouvez lancer le programme en double-cliquant sur le fichier wxgeometrie.pyw .

Sous Windows :
Il existe désormais un programme d'installation de WxGéométrie.
Ce programme nécessite un accès à internet, car il télécharge les dernières versions de Python, et des autres librairies nécessaires (afin de ne pas alourdir inutilement le programme d'installation en incluant systématiquement tout).

Pour désinstaller WxGéométrie, il suffit de supprimer le répertoire d'installation du programme. En effet, pour l'instant, WxGéométrie n'écrit rien dans la base de registre.

Il existe également une version sans installation, qui ne nécessite pas la présence de Python.
Cette dernière version est surtout livrée à des fins de démonstration (ou pour être utilisée sur une clef USB, style Framakey) ; elle est  probablement moins stable (si quelqu'un veut reprendre et améliorer le projet ?).



III. PREMIERS PAS

Certaines options de WxGéométrie ne sont pas encore fonctionnelles.
Il ne s'agit pas de bugs en general, mais, simplement, du fait que ces options ne sont pas encore complètement écrites. Ainsi, un certain nombre de boutons et d'entrées du menu ne provoquent aucune action dans les versions actuelles. Ceci correspond à des fonctionnalités qui seront implementées dans les prochaines versions.

WxGéométrie est composée de plusieurs modules ; les 5 principaux sont :
  • Un module de géométrie dynamique.
  • Un traceur de courbes.
  • Une calculatrice formelle.
  • Un module graphique de statistiques.
  • Un générateur d'arbres de probabilités.
Les autres modules présents sont fournis à titre purement expérimental, et ne sont donc pas documentés ici.

Note :
Toutes les fonctionnalités du module de géométrie dynamique sont utilisables dans le traceur de courbes ; elles sont aussi (en partie) utilisables dans le module de statistiques.

1. Le module de géométrie dynamique


Pilotage avec la souris
Vous pouvez pour l'instant faire les actions suivantes :
  • créer une nouvelle feuille
  • créer différents types de points, de droites, de cercles, des vecteurs, des intersections...
  • modifier les propriétés de ces différents objets (changer leur couleur, les masquer...)
  • régler la fenêtre d'affichage
  • orthonormaliser le repère
  • annuler une ou plusieurs actions
  • exporter et sauvegarder la figure
Utilisation de la souris pour piloter le logiciel :
  • Laissez enfoncé le bouton droit de la souris pour déplacer la figure.
  • La molette de la souris permet de zoomer sur la figure.
  • En laissant enfoncée la touche [Ctrl], la molette de la souris permet d'ajuster la taille des textes de la figure.
  • Laissez enfoncée la touche [Ctrl], et le bouton gauche de la souris, pour sélectionner une zone et zoomer dessus.
  • Vous pouvez déplacer les points libres de la figure avec la souris.
  • Placez-vous sur un point, ou un texte, et appuyez sur la touche [Entrée], pour le renommer.
  • Placez-vous sur un objet, et faites un clic droit pour éditer ses propriétés
  • Placez-vous sur un objet, et faites [Suppr] pour le supprimer, ou [Maj] + [Suppr] pour le masquer .
  • Si vous créez un point libre en laissant la touche [Maj] enfoncée, le point se placera sur le quadrillage.
  • Vous pouvez déplacer le nom d'un point autour de celui-ci en cliquant dessus, la touche [Alt] étant enfoncée.
Note:
Sous Ubuntu, la touche [Alt] est déjà utilisée pour déplacer la fenêtre. Il est conseillé de modifier ce comportement : dans Système>Préférence>Fenêtres, choisir "Super" comme "touche de mouvement".

Création d'objets via le menu Créer
Pour la création des objets géométriques, il existe une abondante aide contextuelle dans chaque fenêtre de création d'objet.
Cliquez sur le point d'interrogation en haut de la fenêtre, puis sur un champ, pour obtenir une aide detaillée.
En cliquant avec le bouton du milieu de la souris dans un champ, vous ferez également apparaitre diverses propositions.

Vous pouvez utiliser les notations suivantes : [A B] pour le segment [AB], (A B) pour la droite (AB), (A>B) pour le vecteur A->B, ||A>B|| pour sa norme.
Remarquez l'espace entre les lettres "A" et "B" dans les deux premiers cas.


Choisir le mode d'affichage de l'étiquette d'un objet
Vous remarquerez que chaque objet possède quatre modes d'affichage : nom, texte, formule, ou aucun.
"Fenêtre de propriété"

Que signifient ces 4 modes ?
  • Mode « Nom » : le nom de l'objet est affiché.
    Un nom d'objet doit commencer par une lettre (non accentuée), suivie de lettres (non accentuées) et de chiffres.
    Il est forcément unique.
    Certains noms sont réservés (e, pi, x, y, t, i, ... )
  • Mode « Texte » : un texte est affiché à la place du nom.
    Ce mode permet d'associer n'importe quel type de texte à un objet.
    La syntaxe LATEX est également partiellement supportée, dans ce cas, le texte doit être entre $.
    Exemple :
    "$\alpha_{n}$"
  • Mode « Formule » : le texte affiché est interprété comme une formule.
    On peut aussi mélanger du texte interprété et non interprété, en mettant les blocs à interpréter entre { }.
    Exemple :
    "Le point A a pour abscisse {A.x} et pour ordonnée {A.y}."
  • Mode « Aucun » : rien n'est affiché.



La ligne de commande
Elle permet de créer rapidement des objet géométriques. (Personnellement, je l'utilise fréquemment).
Elle fonctionne dans le module géometrie, dans le module traceur, et dans le module statistiques.

Utilisation :
Voici quelques exemples qui parleront mieux qu'un long discours...

Exemples
  1. "A=Point(1,2)" ou "A=(1,2)"
    Création d'un point A de coordonnées (1 ; 2).
  2. "u=Vecteur(1,2)" ou "u=(1,2)"
    Création d'un vecteur u de coordonnées (1 ; 2).
  3. "AB=Segment(A,B)" ou "AB=[A B]"
    Création du segment [AB].
    Attention à l'espace entre les lettres dans le deuxième cas.
  4. "d=Droite(A,B)" ou "d=(A B)"
    Création de la droite (AB).
    Attention à l'espace entre les lettres dans le deuxième cas.
  5. "u=Vecteur(A,B)" ou "u=A>B"
    Création du vecteur A->B.
  6. "M=C+2*(A>B)"
    Création du point M vérifiant C->M = 2 A->B.
    Les parenthèses sont obligatoires.
  7. "fenetre=(-10,10,-5,5)"
    Changement de la fenetre d'affichage (xmin, xmax, ymin, ymax).
    Si l'affichage est en mode orthonormé, la fenêtre sera élargie pour respecter cette contrainte.

2. Le traceur de courbes

Vous pouvez pour l'instant faire les actions suivantes :
  • régler la fenêtre d'affichage
  • orthonormaliser le repère
  • utiliser des objets géométriques
  • représenter des suites

Le traceur de courbes supporte les fonctions définies par morceau, continues ou non.

Exemples :
1) Soit la fonction f, définie sur R-{0} par f(x)=1/x
Remplissez les champs de la manière suivante :
 [V] Y1= [ 1/x ] sur [ R-{0} ]

Notes : vous remplissez le premier champ avec 1/x (la fonction), le deuxième avec l'intervalle de définition, c'est-à-dire R-{0}.
Graphe de la fonction inverse.

2) Soit la fonction  f, définie sur [-2;0[ par f(x) = -1, et sur [0;1[ U ]2;3] par f(x) = x
Remplissez les champs de la manière suivante :
[V] Y2= [ -1|x ] sur [ [-2;0[|[0;1[U]2;3] ]

Notes : Vous remplissez le premier champ avec la fonction.
Comme elle est définie par morceaux, on utilise le symbole "|" comme séparateur: ce qui donne -1|x.
Vous remplissez le 2ème champ avec l'intervalle de définition.
Comme elle est définie par morceaux, on utilise le symbole "|" comme séparateur: ce qui donne [-2;0[|[0;1[U]2;3].
Graphe d'une fonction affine par morceaux.

3. La calculatrice

Son fonctionnement sera assez familier pour quiconque a déjà utilisé une calculatrice scientifique.
Elle permet de travailler sur des nombres réels ou complexes, et de faire du calcul formel de niveau lycée.


En particulier, on peut développer, factoriser, dériver, intégrer, et résoudre des (in)équations ou des systèmes linéaires :
  • developpe((x-3)(x+sin(x)-4))
  • factorise(x*exp(x)+exp(x))
  • derive(x^2+x+1)
  • integre(x^2+x+1)
  • resous(x*exp(x)+exp(x)=0)
  • resous(x+3>2-x ou (x-4<=3x+7 et x>0))
  • resous(x+3-y=2-x et x-4y=3x+7y-1)



Nota 1 : Pour obtenir le résultat d'un calcul sous forme décimale (calcul approché), appuyez sur MAJ+ENTRÉE au lieu de ENTRÉE.
Nota 2 : On peut faire apparaitre la liste des fonctions par un clic-droit dans la zone de saisie, en laissant enoncée la touche CTRL.

Depuis la version 0.120, elle utilise la librairie de calcul formel sympy (© 2006-2011 SymPy Development Team).

Des variables peuvent être utilisées pour mémoriser des valeurs, ou définir des fonctions.

Exemples:
  • a = 25+7/4
  • f(x)=2x+17
  • g=f'
    Ici, la fonction g est définie comme la dérivée de la fonction f.


Notez que certaines variables sont protégées (i, e ou E, pi, ...).

4. Le module statistiques

Ce module sert essentiellement à tracer des diagrammes, qui n'existent pas toujours sur tableur, ou qui y sont incorrectement définis.
A l'origine, ma motivation était essentiellement de pouvoir tracer des histogrammes, qu'OpenOffice.org® ou Excel® confondent avec les diagrammes en barre.
Il fait aussi la différence entre diagrammes en barres et en bâtons (contrairement à ce qu'on peut lire souvent, la différence essentielle n'est pas esthétique).

Voici une présentation des principaux types de graphiques :

Diagrammes en barres :
Utilisés en particulier pour des séries à caractère qualitatif.
Exemple : la répartition des voyelles dans l'alphabet.

On sélectionne le mode :


On ajoute les valeurs et les effectifs (ou fréquences) qui leur correspondent.
La syntaxe est la suivante : « effectif * valeur » (valeur doit être entre guillemets, pour des valeurs non numériques).

Il ne reste plus qu'à compléter la légende :


Et à appuyer sur [Entrée] dans un des champs.
Le résultat est le suivant :



Diagrammes en bâtons :
Utilisés pour des séries à caractère quantitatif discret.
Exemple : la répartition des pointures de chaussures chez les femmes françaises adultes (2005).

On sélectionne le mode :


On ajoute les valeurs et les effectifs (ou fréquences) qui leur correspondent, et on complète la légende :


On presse la touche [Entrée] dans un des champs.
Le résultat est le suivant :




Histogrammes :
Utilisés pour des séries à caractère quantitatif continu.
On va reprendre l'exemple précédent, en regroupant les pointures par classe.

On sélectionne le mode :


On complète la rubrique « Regroupement par classes ».


Pour les histogrammes, il n'y a pas d'ordonnée, mais il faut préciser la nature de l'unité d'aire.


Et on appuye sur [Entrée].




Pour aller plus loin :
  • A la place des valeurs numériques, on peut tout à fait insérer des formules.
    Exemple :
  • La génération de listes est également possible. La syntaxe est celle de Python (cf. list comprehensions dans la documentation de Python).
    Essayez par exemple de rentrer cette formule : [(rand(),i) for i in range(4)].
  • Dans Outils, trois sous-menus permettent respectivement de créer des expériences.
    En particulier, à titre d'exemple, il est possible de simuler des lancers de dés, et des sondages simples.

    N'hésitez pas à éditer le fichier experience.py dans modules/statistiques/, et à y ajouter de nouvelles fonctions.
    Vous pourrez ensuite réaliser vos propres expériences, depuis le menu « Experience ».

    Entrez votre formule dans le champ « Experience » (ici, un lancer de dé), et le nombre d'expériences.
    Eventuellement, entrez aussi les valeurs possibles . Pour un lancer de dé par exemple, cela permet d'afficher en légende 1, 2, 3, 4, 5 et 6,
    quand bien même il n'y aurait aucun « 4 » par exemple.

    Notes : 
    - Pour simuler des lancers de dés, mieux vaut utiliser le menu spécialement dédié (quelques optimisations y ont été faites).
    - La case « lancer une animation » n'a pas d'effet pour l'instant.


4. Le générateur d'arbres de probabilités


Les arbres de probabilité sont codés de la manière suivante :
  • La première ligne (optionnelle) correspond à la légende.

    Exemple :
    ||Premier tirage|Deuxième tirage

    (Note : l'ajout de barres verticales supplémentaires (AltGr+6) décale la légende vers la droite.)
  • Les lignes suivantes correspondent à l'arbre proprement dit.
    • Le nombre de > correspond au niveau dans l'arbre.
    • La syntaxe est la suivante :
      « Nom de l'évènement » : « Probabilité de l'évènement »

    Exemple :
    omega
    > A:1/4
    >> B:1/5
    >> J:2/5
    >> V:...
    > &A :3/4
    >> B:...
    >> J:...
    >> V:...

    Le symbole & indique qu'il s'agit de l'évènement contraire : &A est ainsi l'évènement «A barre».
    (Note : la syntaxe LaTeX est également acceptée).

IV. UTILISATION AVANCÉE

1. Le fichier param.py

Un grand nombre de paramètres peuvent être modifiés dans le fichier param.py avec un simple éditeur de textes.
Exemple:
Remplacez "affiche_axes = True" par "affiche_axes = False" pour que les axes ne soient plus affichés par defaut.

Note : il peut être parfois nécessaire d'effacer le dossier /preferences (qui contient les paramètres de la session précédente) pour que les changements soient pris en compte.

2. Débogage

Dans le menu Avancé>Déboguer, sélectionner « Déboguer » pour faire apparaître une fenêtre contenant entre autres tous les rapports d'erreurs. Par ailleurs, le répertoire /log contient les fichiers .log générés lors de la dernière exécution (actions effectuées, messages d'erreurs, etc.)

3. La ligne de commande

Introduction:
La ligne de commande sert essentiellement à débuguer le programme.
(Ou à réaliser certaines opérations internes, etc...)
La ligne de commande permet d'exécuter des instructions Python.

Précédé du symbole &, le résultat de la commande sera affiché dans la console. (NB : assurez-vous au prélable que l'option « Déboguer » soit cochée, dans le menu Avancé>Déboguer).


Les raccourcis suivants sont disponibles :
  • !p. pour panel.
  • !c. pour canvas.
  • !f. pour feuille.
  • !o. pour objets.
  • !g. pour moteur_graphique.

Leur maniement nécessite évidemment de bien connaître l'API de WxGéométrie, et donc de faire un tour dans le code source.

Exemples :
1) "print 'hello world !'"
Ceci va afficher 'hello wold !' sur la console.
NB : « & 'hello world !' » produirait le même resultat.
2) "print objets.A"
Affiche, s'il existe, l'objet A dans la console.
NB : Cette commande s'abrège de même en « & !o.A ».
3) "panel.exporter('test.png')"
Exporte la figure courante en un fichier test.png.
NB : Forme abrégée : « !p.exporter('test.png') ».
4) "feuille.fenetre = (-5,2,-7,3)"
Change la fenêtre d'affichage en (-5, 2, -7, 3).
NB : Forme abrégée : « !f.fenetre = (-5,2,-7,3) ».

V. COMMENT CONTRIBUER ?

Vous pouvez par exemple :
  • m'envoyer un mail à l'adresse suivante : wxgeo@users.sourceforge.net, en me donnant vos impressions générales.
  • corriger les éventuelles fautes d'orthographe.
  • me signaler des bugs existants, et non répertoriés sur le tracker http://wxgeo.free.fr/tracker .
  • me proposer des corrections de bug :)
  • ajouter des fonctions mathématiques à la calculatrice
  • implémenter la gestion des coniques
  • commencer de géométrie dynamique dans l'espace
  • compléter cette documentation ou créer un tutoriel (je manque de temps pour tout faire !)


Je suis également ouvert à toute autre contribution, et je suis prêt à travailler en équipe... :-)


Je recherche en particulier (liste non exhaustive) :
  1. des personnes pour m'aider à maintenir et à améliorer :
    • le fonctionnement sous Linux :
      tests, création de scripts bash d'installation, de paquetages .deb ou .rpm, guides utilisateurs, etc..., chose que je n'ai pas le temps de faire aussi bien que je le souhaiterais.
    • le fonctionnement sous MacOs X :
      théoriquement, ça devrait tourner assez facilement,  mais je n'ai jamais eu la possibilité de le tester.
  2. des personnes intéressées par la construction de nouveaux modules pour WxGéométrie.
    Je pense en particulier à des professeurs de mathématiques, de sciences-physiques, de technologie... qui auraient un peu d'expérience en programmation objet (mais pas nécessairement en python : python en lui-même s'apprend en une semaine).
    Une première expérience fructueuse a déjà commencée, en collaboration avec Christophe Vrignaud.
  3. des personnes pour me faire remonter des rapports de bugs, ou des suggestions. A quelques exceptions près, les seuls échos que j'ai pu avoir, ce sont les statistiques de sourceforge. Je sais que le projet manque encore de maturité, mais au fil des versions, il y a désormais un peu de matière. Toutes les critiques sufisamment précises sont bonnes à prendre. ;-)


Note :
 une documentation spécifique pour développeurs se trouve dans le répertoire doc/developpeurs/.



VI. REMERCIEMENTS


Sans prétention d'exhaustivité, je voudrais remercier :
  • Boris Mauricette, pour avoir contribué au module de Statistiques (tracé des quartiles).
  • Christophe Bal pour ses commentaires, et pour avoir proposé la syntaxe du module de Probabilités.
  • Les développeurs de sympy, avec qui j'ai toujours eu des échanges cordiaux et constructifs, y compris de code.
    Remerciements plus particuliers à Chris Smith, Vinzent Steinberg et Aaron Meurer.
  • Christophe Vrignaud, qui a développé et maintenu quelques temps le module Scicalc pour Wxgéométrie.
  • Stéphane Clément a mis a disposition de Wxgéométrie le wiki de l'académie d'Aix-Marseille.
  • Tous ceux qui ont pris le temps de faire quelques commentaires sur ce programme, et m'ont encouragé à continuer (en particulier dans les premiers temps : mon frère Thomas, Enzo, Rhydwen Volsik, Robert Setif...).
  • François Lermigeaux, pour les coups de pub occasionnels.
  • Georges Khaznadar, pour ses nombreux conseils concernant Debian.
  • Tous ceux qui ont pris le temps de faire des rapports de bugs et des retours.


Merci enfin à Sophie pour sa patience !
wxgeometrie-0.133.2.orig/wxgeometrie/doc/notes.txt0000644000175000017500000006351212014170666022363 0ustar georgeskgeorgeskWxGeometrie _________________________ Copyright 2005-2010 by Nicolas Pourcelot Ce programme est distribué selon les termes de la GNU Public License version 2. This program is distributed under the terms of the GNU Public License version 2. DESCRIPTION ______________________ Geometrie est un petit programme de géométrie dynamique écrit en WxPython (à partir de la version 0.3). Il a été testé avec les configurations suivantes (2008): Logicielles : Matplotlib version 0.99.1.1 WxPython version 2.8.10.1 Python version 2.6.5 Windows XP SP2 ou Linux Ubuntu Lucid Numpy 1.3 Il fonctionnera sans problème avec des versions ultérieures de Python. Attention, pour WxPython, certains bugs subsistent sous Windows avec les versions 2.7.x et 2.8.x ! Materielles : Processeur AMD Sempron 3000+ (2 GHz) NVidia Geforce FX5200 500 Mo RAM Il fonctionnera très probablement avec des configurations moindres. Je n'ai pas eu l'occasion d'essayer, veuillez donc m'en informer. Configuration minimale requise : Processeur : AMD Athlon 1,5 GHz ou équivalent RAM : 256 Mo Système d'exploitation : Windows 98, 2000, XP ou Linux ou tout autre "Unix-like" assez recent. Python : 2.5.x WxPython : 2.6.x Matplotlib : 0.99 Numpy 1.2 Non testé sous Windows Seven, ni sous MacOs X FEUILLE DE ROUTE ______________________ Versions 0.12x.x : ------------------ Buts de cette série : - intégration des courbes à geolib -> en cours - intégration de sympy -> ok - gestion complète des variables -> ok - refactorisation du code de geolib -> ok - refactorisation du code de wxgéométrie -> en cours - implémentation de tests unitaires -> en cours Éventuellement : - gestion des macros -> non - création d'un nouveau moteur graphique -> en cours Ameliorations : Majeures : version 0.120: Intégration de sympy dans la calculatrice Changements importants dans l'API de geolib (les méthodes orientées utilisateur-final on été remplacées par des attributs spéciaux). Par exemple, pour changer l'abscisse d'un point A, on écrit désormais "A.x = 3" au lieu de "A.x(3)", ce qui est plus lisible :-) Améliorations dans la console La gestion des évènements est (enfin) correcte sous Linux Implémentation de tests unitaires dans geolib... à terme, cela devrait réduire le nombre de bugs Support de matplotlib 0.98+ Ajout d'une fonction 'resoudre' dans la calculatrice (équations, inéquations et systèmes) version 0.125: Résolution de plusieurs nouveaux types d'inéquations Numeric et numarray ne sont plus supportés, et sont remplacés par numpy (C'était déjà le cas pour les versions récentes de matplotlib) version 0.126: Support expérimental du calcul formel dans geolib Python 2.4 n'est plus supporté, il faut impérativement utiliser Python 2.5 Mineures : version 0.120: Correction d'un petit bug dans la boîte de dialogue d'animation. Inclusion d'un fichier .sh en guise de lanceur. La méthode __call__ n'est plus systématiquement présente. Ceci permet de tester si les objets sont vraiment appelables ("callables"). Correction de bugs pour les classes Polygone_regulier et Polygone_regulier_centre Le gestionnaire d'objets de la feuille utilise désormais un dictionnaire personnalisé. Ceci permet d'alléger le travail de la console en effectuant le travail en amont, et de manière (enfin !) systématique, lorsqu'on crée ou supprime des objets. Plus d'ajout de __self__ au début de chaque commande (cela n'offrait que peu d'avantage au niveau sécurité, et bloquait des commandes type "for [] in []:" par exemple...) La plupart des méthodes orientées utilisateur des objets ont été remplacées par des attributs spéciaux. Ceci rend la syntaxe plus intuitive dans la console (ex: 'A.x = 3' au lieu de 'A.x(3)', 'A.coordonnees = (1,2)' au lieu de 'A.coordonnees(1,2)'). Jusqu'à présent, 'A.x = 3' était susceptible de faire planter wxgéométrie. Refonte du système de formules pour corriger plusieurs bugs. ex: 'o.B.label("{A.x}", formule = True);o.A(1,2);o.B.supprimer();o.A(1,3)' Correction du comportement du focus. Construction de points à partir de leur affixe (complexe). Correction de bugs lorsqu'on reliait un point aux axes. La gestion des options du programme adopte une syntaxe Unix Changements dans le moteur d'affichage Améliorations diverses et correction de bugs dans Tablatex Les bases pour la gestion de filtres d'import sont posées Définition dynamique des classes de geolib, suivant le type d'argument L'environnement d'excéution du gestionnaire d'objets de la feuille s'enrichit progressivement (mots-clefs 'fenetre', 'objets'...) Amélioration de l'historique de la ligne de commande Support des styles de texte "famille", "epaisseur" et "style" quand param.latex == True. Correction de bugs avec WxPython 2.8 sous Windows (ajout de style=TE_PROCESS_ENTER) Correction de regression: 'del b' fonctionne de nouveau dans la calculatrice Correction de regression: centre de gravité dans geometre Meilleur gestion des arguments de WxGéométrie (utilisation de optparse) Nouvelle gestion de la mise en valeur lors de la selection (modifications notamment pour le texte) Factorisation dans R ou dans C des polynomes Correction de regression: détection des objets Texte près de la souris Droite_equation accepte des arguments style '2x+3y=0' (à améliorer) Meilleur support de LaTeX dans la calculatrice (0,2) créer un point dont le nom n'apparaît pas (cf Géogébra) Regression corrigée: quand on crée (graphiquement au moins) une intersection, le nom apparaît par défaut. Regression corrigée: construction d'un triangle avec l'icône spécifique triangle (idem parallelogramme). Affichage des noms d'objets en mode LaTeX. Régression corrigée: zoom automatique. Régression corrigée: mauvais chargement des paramètres lorsque les préférences sont désactivées (option -d). Régression corrigée: ans() renvoyait une chaine de caractère Calculatrice: ~ est désormais interprété comme un espace en mode LaTeX Calculatrie: amélioration de la précision par défaut des calculs approchés Correction de bugs unicode : utilisation de unichr au lieu de chr Correction d'un bug pour les étiquettes (attribut parametre manquant) Support de numpy 1.1.* et 1.2.* (contournement d'un bug de numpy) Il n'est plus possible de rentrer un code LaTeX erroné via la boite de dialogue dans l'onglet 'geometre'. (Ceci évite de bloquer malencontreusement l'affichage). Ajout d'une entrée dans les menus pour le zoom des textes. Simplification des racines imbriquées lors des factorisations de polynomes. Correction de plusieurs régressions liées à la création d'objets sans nom par ligne de commande (mais il en reste !). Bug de geolib (detection des angles) Ajout d'une vérification pour les textes LaTeX entrés, afin de ne pas bloquer l'affichage. Correction de plusieurs bugs concernant le codage des segments, des angles et des côtés. Correction d'une importante régression (dans une commande, '1,2' était converti systématiquement en 'Point(1,2)') Correction d'un bug lorsque wxgeometrie était lancé avec le parametre -d Calculatrice: possibilité de copier le code LaTeX automatiquement dans le presse-papier Correction d'une régression (dans une commande, '(1,2)' était converti systématiquement en 'Point(1,2)') Mise à jour de l'aide (help.htm) Désactivation du module "surfaces" pour les versions récentes de matplotlib. Correction d'une régression mineure (style par défaut des côtés) version 0.120.1 Correction d'un bug pour la fenetre de dialogue Polygone_regulier_centre Correction d'un bug dans le nommage automatique des cercles version 0.120.2 Correction d'un bug dans l'utilitaire de rapport de bugs (sic !) Correction d'un bug empêchant certaines sauvegardes de se charger (Sauvegardes contenant des barycentres, des interpolations ou des polygones de plus de 9 côtés) Amélioration de la version py2exe (le module param est cherché de préférence dans le répertoire de wxgeometrie.exe) Correction de bugs pour la classe Interpolation (extémités de la courbe) Amélioration du parser de la calculatrice (i sqrt(3) -> i*sqrt(3)) version 0.120.3 Calc. : Affichage des nombres complexes sous forme algébrique par défaut Calc. : Ajout des commandes LaTeX \exp, \ln et \log version 0.120.4 Correction d'un bug sur la copie de styles Corrections de bugs sur les interpolations version 0.120.5 Double-clic sur les boutons dans le module 'Geometre' Correction d'un bug lors de la construction des parallèles et perpendiculaires Calculatrice : conversion de 'resous' et 'solve' en 'resoudre' version 0.120.6 Correction d'un bug concernant les intersections droites-cercles et les tangentes. Contournement (partiel ?) d'un bug sous numpy 1.1 et 1.2 (cf. http://scipy.org/scipy/numpy/ticket/884) En cas de bug lors d'une annulation/restauration, l'affichage n'est plus gelé Ajout de boutons annuler et rétablir (pratiques pour le TBI) Patch provisoire pour le TNI Correction de bug: style des intersections version 0.120.7 Correction de bugs: label des angles, conversion Variable -> Angle Patch TNI version 0.121 Support partiel du logarithme dans les inéquations Calculatrice : gestion de 'NotImplementedError' Refonte de la classe 'Intervalle', pour contourner un bug de sympy Ajout du mot clef 'z' pour les points, vecteurs libres, et textes Calculatrice: ajout de 'conjug()' (conjugué d'un nombre complexe) Ajout des objets Triangle_rectangle, Triangle_isocele, et Triangle_isocele_rectangle Meilleur gestion de la construction des tangentes Construction graphique des translations, des homothéties, des rotations et des symétries (centrales ou axiales) Correction d'un bug se produisant parfois lors de la recherche de mises à jour Construction graphique des triangles isocèles, rectangles et équilatéraux Construction graphique des carrés, rectangles et losanges Meilleure prise en charge de LaTeX dans la calculatrice Calculatrice : conversion j - h - min - s version 0.122 Correction d'un bug sur les représentants de vecteurs. Correction d'un bug sur certains cas particuliers d'intersections. Correction de bug : conflit de nom de variable (objet.py-l150). Réécriture plus souple des transformations. La calculatrice accepte désormais f' et f" au lieu f` et de f`` version 0.123.1 Ajouts de nouvelles méthodes aux polygones (en interne pour l'instant) Optimisations 'derive(f)' accepté à la place de 'derive(f(x))' Syntaxe de ans() modifiée Extension des options de zoom de l'affichage Passage à sympy 0.6.4 Correction d'un bug sur les angles orientés Calcul matriciel Correction d'un bug dans la fonction 'point_dans_polygone' Représentations en perspective cavalière (en ligne de commande pour l'instant) Correction d'un bug dans l'affichage des demi-droites La plupart des objets géométriques peuvent être construits sans arguments (cela permet des constructions et des tests plus rapides) Permutation des classes Cercle et Cercle_rayon 0.123 -> 0.123.1 (nécessaire du fait du changement d'API précédent) Les noms d'objets peuvent être utilisés sans majuscule, et sans parenthèses en l'absence d'argument. Nommage intelligent (ex: ABCD = carre -> les sommets s'appeleront A, B, C et D) Correction d'un bug lors de l'utilisation de fonctions mathématiques dans geolib Correction d'un bug dans l'objet Droite_equation Génération d'une liste d'objets via la ligne de commande Regression corrigée: Détection incorrecte des arcs de cercle Bug corrigé: labels contenant des formules commençant par un chiffre Bug corrigé: intersection entre une droite et un segment presque vertical Orthonormalisation par "zoom out" plutôt que par "zoom in" Régression corrigée: le zoom "intelligent" fonctionne de nouveau pour un repère orthonormé Polygones par défaut non croisés Régression corrigée: exécution d'un historique contenant un déplacement d'étiquette Régression corrigée: chargement d'une feuille contenant un représentant de vecteur Contournement d'un bug de WxGtk (titres de pop-up non affichés) Changement du style par défaut des angles Codage automatique des angles droits (désactivable) Version 0.124 Correction d'un bug dans les perspectives cavalières Les noms de vecteurs apparaissent avec une flêche. Bug corrigé: une formule incorrecte entrée comme légende d'un objet géométrique est refusée proprement Changement des polices par défaut en Stix Serif, et des polices mathématiques en Stix Nom des cercles et des droites en caligraphie. Changement du codage des ' dans les noms d'objets pour corriger une régression. Régression corrigée: affichage incorrect des droites dans un repère orthonormé. Bug corrigé: 'Relier aux axes' ne fonctionnait que pour les points libres. Bug corrigé: résolution impossible de certains systèmes. La copie de style ne copie plus le mode de légende (formule, nom, texte, etc.). Régression corrigée: problèmes lors de la redéfinition des objets Régression corrigée: un objet ayant mêmes coordonnées ou valeur qu'un objet en gras était affiché en gras lui aussi Version 0.125 Régression corrigée: union d'un intervalle et d'un singleton L'objet Courbe_fonction de geolib est désormais fonctionnel (reste à l'intégrer) Bug corrigé: redéfinition d'un objet ayant un seul argument Amélioration du système de déboguage Patch provisoire de sympy, pour contourner plusieurs bugs (Les patchs ont été soumis à l'équipe de développement de sympy) Amélioration de la résolution des inéquations contenant des logarithmes Amélioration de la factorisation dans R des polynômes de degrés 3 et 4 Factorisation en mode approché Possibilité d'afficher les objets cachés Mise en place d'un tracker de bugs (cf. http://wxgeo.free.fr/tracker) Calculatrice: conversion décimal -> fraction Nouveau système de gestion des feuilles (via un "classeur") Calculatrice: développement décimal infini périodique Geolib ne dépend plus de pylab, mais de numpy directement Correction d'un bug de sympy sur les matrices Correction d'un bug interne sur les dépendances d'objets Version 0.126 Modification de l'API de geolib concernant les objets Variable et tous les angles Les angles sont tous en radian en interne désormais Nouvel algorithme (bien plus rapide) de détection des cercles en repère quelconque Correction d'un bug de sympy sur l'évaluation de E Affichage d'informations dans la barre de statut pour de nombreux objets Régression corrigée: étiquettes impossibles à déplacer Modifications internes dans le dictionnaire LocalDict Gestionnaire de contextes La classe Polygone_regulier est implémentée de manière bien plus efficace Réécriture des intersections d'objets de manière plus concise et compatible avec sympy Début de changement de l'API d'affichage Versions 0.13x.x : ------------------ Buts de cette série : - intégration des courbes à geolib -> ok - refactorisation du code de wxgéométrie -> en cours - implémentation de tests unitaires -> en cours - création d'un nouveau moteur graphique -> ok - intégration du calcul formel dans geolib -> en cours - réécriture de Tablatex -> partielle Améliorations : Majeures : Version 0.130 Réécriture de l'API d'affichage Optimisation très importante de l'affichage Réécriture du module Traceur en utilisant directement geolib Contournement d'un vieux bug de WxGtk sous Linux Contournement d'un bug de WxPython 2.8 sous Windows (enfin !) Barre d'outils affichable pour le traceur Nouvelle console dédiée aux objets géométriques Réécriture complète de l'ancienne console Réécriture de larges parties de TablaTex. Nouvelle syntaxe. Génération automatique du tableau de signes/variations d'une fonction donnée. Version 0.131 Support des sessions Nécessite Matplotlib version 0.91 ou supérieure Nouveau module "Surfaces" Ajout d'une interface graphique pour gérer les options Modules actifs modifiables sans redémarrer l'application version 0.132 Nouveau style "hâchures" pour les polygones Nécessite Matplotlib 0.99 ou supérieure Le zoom est de nouveau fluide Amélioration du module Statistiques Nouveau module sur les graphes (très incomplet) Support (encore expérimental) du calcul formel dans geolib. Mineures : Version 0.130 Utilisation de blit() et de deux buffers pour accélérer l'affichage Gestion sécurisée du gel d'affichage (via 'with') Régression corrigée: F5 exécute de nouveau l'historique Bug corrigé: un objet renommé avec un nom réservé en python disparaissait Amélioration et unification des lignes de commandes Mise en cache de 4 listes des objets de la feuille selon différent critères de sélection Suppression de l'historique des commandes Nouvelle gestion des logs -> Beta 1 Régression critique corrigée: export de la figure Bug corrigé: bouton inactif dans le widget "LigneCommande" Passage à sympy 0.6.5 Calculatrice: nouvelle fonction Rassemble() pour mettre sous une seule fraction. Calculatrice: syntaxe LaTeX et OpenOffice.org des matrices acceptée Calculatrice: copie du code LaTeX vers le presse-papier améliorée Calculatrice: la syntaxe sin', cos', tan' etc. est maintenant acceptée. Affichage: désélection lors de wx.EVT_LEAVE_WINDOW Un grand nombre de paramètres graphiques sont désormais gérés directement par la feuille de travail Geolib indique directement au moteur d'affichage le type de rafraichissement requis En interne, les figures ne sont recrées qu'au moment où on en a besoin Moteur d'affichage: optimisation en cas de rafraichissements rapides [sympy 0.6.5] Bug corrigé pour solve(1/x,x) [sympy 0.6.5] Bug corrigé pour solve(-(1 + x)/(2 + x)**2 + 1/(2 + x), x) Calculatrice: simplification de l'affichage des racines et des produits Régression corrigée: zoom automatique Tests unitaires pour tablatex Régression corrigée: affichage temporaire (zoomBox, etc.) Amélioration du mode calcul approché (racines des polynômes) Réintroduction d'une protection contre les plantages de l'affichage. Bug corrigé: cercle ou droite nommé par une lettre grecque Réflexion: possibilité d'utiliser un segment comme axe de symétrie Correction d'un bug pour les ensembles de définition de fonctions rationnelles Compression en temps réel de l'historique d'annulation Régression corrigée: annulations parfois impossibles Équation de droite plus lisible lorsque les coeffs sont petits. Simplification du code des étiquettes (coordonnées en particulier) Meilleur gestion de l'affichage des textes (moteur graphique) Régression corrigée: impossible d'écrire dans un champs de texte sous Windows Vista Gestion du focus améliorée lors des changements d'onglets Calculatrice: [Maj] + [Entrée] pour une valeur approchée Geolib: les noms "droites", "points", etc. permettent de traiter globalement les objets dans la ligne de commande. Régression corrigée: axes actualisé lors du changement de feuille Adaptation du module de statistiques au nouveau moteur graphique (au passage, les expériences sont désormais plus rapides) Refonte partielle du système de log (stdout/stderr personnalisés) Nouvelle fenêtre de contact non bloquante Correction d'un bug d'affichage lors du redimensionnement de la fenêtre Passage à Sympy 0.6.6 Documentation des arbres de probabilité Mise à jour partielle de la documentation Pointeur de la souris modifié lorsqu'on charge une feuille Nouveau système de paquetage Modification légère de l'initialisation, pour s'assurer que le fichier .log fonctionne. Contournement d'un bug de wx.MiniFrame (Gtk) lorsque Compiz est activé. Version 0.130.1 Correction d'un bug sous Windows Vista (export impossible) Version 0.131 Changement de la précision de calcul pour la détection du cercle Optimisation de l'affichage des cercles en cas de zoom important Optimisation de la détection des arcs de cercle Meilleur gestion des formules LaTeX incorrectes ou en cours d'édition Correction d'un bug lorsque la légende du repère était vide Optimisation de l'affichage des arcs de cercles en cas de zoom important Nouvel objet : Demi-plan Ajout d'un parametre '--modules' en ligne de commande Mise à jour du code d'import des modules Autre action: Supprimer les objets inutiles. Calculatrice: meilleur gestion des matrices en LaTeX Meilleur support des équations de droites Calculatrice: ajout d'un menu 'Statistiques' Calculatrice: ajout d'une fonction 'aide()' Calculatrice: réorganisation des menus Tablatex : support élargi des tableaux automatiques de variations/signes Mathlib: meilleur factorisation des exponentielles Calculatrice: support des mots-clés Python (if, for, else, etc.) Nouvel éditeur interne de feuilles Editeur de l'état interne de la calculatrice version 0.131.1 Correction de regressions pour la sauvegarde des modules statistiques et probabilités Sauvegarde automatique et récupération en cas de crash Meilleur support des mots-clés Python dans la calculatrice version 0.131.2 Amélioration notable de la sauvegarde de l'état de la calculatrice (quelques bugs subsistent cependant) Ajout d'une fonction 'arrondir' Correction d'un segfault à la fermeture lorsque le premier onglet n'est pas actif (sic !) version 0.132 Matplotlib version 0.99+ est nécessaire Support correct de l'export d'une partie de la figure Les formats d'export supportés sont désormais ceux de matplotlib -> JPEG, BMP et XPM ne sont plus suppportés (le support était trop mauvais) -> EMF, SVGZ et RAW supportés désormais Refactoring du code de la ligne de commande de débogage Refactoring des librairies GUI et API Ajout de nouveaux paramètres: echelle_cm et dpi_ecran Nouvelles options d'export (non encore documentées) Problème d'encodage corrigé lors de la sauvegarde de la session (Régressions corrigées: export en image, boîtes de dialogue) Bug corrigé: fonction dépendant de paramètres Correction d'un bug de l'affichage des formules dans la partie géométrie Patch de sympy.sympify Bug corrigé dans l'interprète de la feuille (annulation impossible) Plusieurs bugs (mineurs) corrigés concernant l'icône de sauvegarde Optimisation de la sauvegarde automatique Correction d'un bug dans le module de probas Mise à jour de sympy Mise à jour des filtres d'import pour le traceur (l'ouverture d'anciens fichiers .geo ne fonctionnait plus) Meilleur support de la syntaxe LaTeX dans le traceur Patch de sympy concernant les limites Adoption partielle du framework de test de Sympy Réorganisation du code du module tablatex Corrections de plusieurs bugs mineurs concernant l'édition de noms d'objets Bug concernant le sablier (pointeur souris) sous Windows (module surfaces) Correction d'un bug dans les menus de création d'objets géométriques Détection des librairies manquantes dès le démarrage du logiciel Fonction LambertW ajoutée dans la calculatrice. Tablatex : limites optionnelles. Calculatrice : amélioration du pretty print. Ajout de fichiers README, INSTALL et LICENSE. Amélioration de l'annulation après la suppression simultanée d'objets. Régression corrigée : l'éditeur interne de code ne fonctionnait plus. Bug corrigé concernant certaines configurations de wxgéométrie. Calculatrice : _59 peut être utilisé au lieu de ans(59). Bug corrigé concernant la fermeture de wxgéométrie dans certains cas (rares). Fusion des boutons "Start" et "Stop" dans la fenêtre d'animation. Formatage amélioré des équations dans geolib. Correction de bugs concernant la fonction Intersection() (console de geolib). Amélioration du parser de la calculatrice ("a b" est toujours converti en "a*b") Améliorations mineures de la console de geolib. Système de test refactorisé. Toutes les docstrings passent maintenant. Meilleure gestion des guillemets (calculatrice, traceur, géométrie...) Très nombreuses corrections de bugs et améliorations pour le traceur. version 0.132.4 Correction d'une regression importante avec matplotlib 1.0 (points non affichés) Affichage du sablier lors d'un certain nombre d'opérations potentiellement longues Module expérimental de cryptographie (attention, ne fonctionne *PAS* sous Windows pour l'instant !) **Bugs connus / améliorations prévues** http://wxgeo.free.fr/tracker version 1.0 : ------------- Mise a jour automatique style Firefox :) Ajout d'une vraie rubrique d'aide. Passage à PyQt ? version 2.0 : --------------- Geometrie 3-D Autres pistes de travail : -------------------------- Tirer partie de l'eventuelle presence de ImageMagick. Outil pour calculer les moyennes trimestrielles et annuelles ? Etendre les possibilites de l'API afin de creer des animations. (système de diapos, export en gif anime ou mng, lecture de bande sonore, ...) Support du format .ggb (Geogebra) en import et/ou export Corriger les copies. Faire le thé. Sans sucre pour moi svp. wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/0000755000175000017500000000000012014170666021730 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/doc/images/logo.png0000644000175000017500000034076312014170666023413 0ustar georgeskgeorgeskPNG  IHDRn4 sBIT|dtEXtSoftwarewww.inkscape.org< IDATx]wE}磊4ZB B@(HB@@HQRUTP@J @ %~w9g w,ywwΙUDDn붏ytM8ꨣ{tjl>z#"5 ӧOg?i{mٸ#D=Vv n붏~~SL6l{k_~F{G^ɽZMt[ +lC=z⬽oc=ŋWbn붏{ܾcǮXҗ}9s&~󟯤ެ= ݶ\6w\3oF  &ά@;pFn[ ݶ\vW_ƃ>]w/*kiދn[zL4 > ݇뮻s#Uݺ''mlذa &GY=Zumڴi5jTדּ%\\ףJ)p 8WD7?-\.ƌ$IVvwhfe{"Sެe+6f6/W 8ƍJ+_0`ƌO}S?~nOT{gTg߾}W '@WH}[;\z+ޕitS^rnϞ=i„ tuQSSJnb :_:utt.9{ ڲ=.]m:Q}ƍ[gmc=n!vNJ?騣>nf2T,o .ӻ[s;mmm4~CɓW魖F+f7{?q =h :3sϥ;XX?rݻw郞>xYu跸khХ^J>?Gy3fyG>`F:ꨣH){ojll,3$'|s);l;c뵦*?iذatҴiӖロPCC\:&Zo}Z/~1w744+蠻>G஻mߌڽ袋>t[ vwN{} _'{쑻‰p4iR):oiiN0pxbѣ+&PsssW+nGuԇrF^ZzN?/~:^~MrW,WV}#z?:ꨣW^pnii>!ꫯ.w]k-[8q"M0&L@rqtwВ%K}n1ٮꪨ·~8#q5WivqE:zIӔrotWwMwy']tE{Fk[T*?Odذa4s.o;T*c=֥:14`~]GaC _ڇG}xZ+M7ݔ?|jooXnΜ9[<3t%\Bz+,W^yh`޽餓NٳgCoi}?d6mZĘmR\:thUIcѢE]irڴi<v>/z*ܭ96;tyoq&k_:j.QYуsϭnvGkryΜ9v`ۏz表̬Y3L2~_u?mmm }o9W… iP$ ?QÇ;n8o[=,<#8"*wVF/εwWWO=TTOX3ln"^{(| oN8KWZӲe˺TGaR)wn6oU7x^4qWD /?_xo|u ~U?&ݖ(n_|q6x` O<&Je1bDz=H^ì#"b-Fdjc---4f̘Q:)[%;<]]=53}]\> ƹcewimFF]M7ݔ.N; @vԩU_25cEv߫W/vmi+9FO>$/n+J5\SOv j }-ZTSs̡={J)я~TQmݖ=ºjiC#G?Qva=\mRt޼ytqt~Dm};ߩ/W%M1br<|&pmذawZ. N:養O}**׿vu\W_}u!ەW^IDDk?lذ(D%oUO [偛'臺:=7MՇy厭 /}|WKgΜ. ʎ>.eի=3UۺKmYlh~:*[f n&mg'sTc9&*[m7o޼~g"IEa^M2YiҤIt7 i+)S-&?;&kϙ3:WJ:ê\s+%СC ٳ#v$ ⋅}K:>r˖-]v٥vvԔ믿^ӱQaT:~FSisT_A{„ Dm p-آU²q;sH&lRJc}k˾9v5˟vihaХ}a 3gҹE|_XwQ=X*ϕ6mZ-dT9眊}#Ic]v)mUQҥKӟta 8~x]͟??{UNn+W-\V+^dI.£=3;m.e]s€ %-2w7ɕknnα{VJ; j":\oVzz6l͟??W&?p\?Szn7:BVr;3&~9 ':KD)h_TYʹzc7xº#裏^Vmn"F6nXhw1bkBuYӾQ:0okVxf>E լz)N:vyg5jmt'By䑨$IN 566;*i_|q;HE~…4hР\aّ?Ϫܐ!Ch…ra[$nT ?%ܰtM5og?Q3<3 /]wݵp/3&xy睚XtA?_X{͕ƸŲm{ケ2EKVZ ^,whgClqQo%+Z 3Έ̢X<*۷oߊ_;찊K~VB]xKk]qRN*R\rccc.g̙ nidZ[[iذaQ2q倶cR0]{=XU~kQ[f%,X@suݻ0I(M\dvŚi 6~Zge˖EOZX6zElٲߟmfbC'OM}+_vzg jkti{\nUPnÇf%ƘnVu߯? g6&NX3gFsoyUsE6lsʶ3swi.;C"˺Tź/iVX_l'|2z;WG[maF}}}aHVv)jן*eˣ?w}7瞅Yd',jqM:&IڶGuf n]{{;M8b]rHǨ[lQumkkСCs---K$tBjX6g?Y}kkk1ݴ+f-+l5K0YX)ʧ+xhֳgϊj7ho)lZ~Vk&ʳcB@ՠ$gP~ӟ^>?_)n? okg[g$]bΜ93fg?[]wݵ \lg;bYF6W&|oW%۷ܹs>}B4kS___qzE{]Ve˖E{+UaYXe}Y-7M2vxMQYkkkŃ~+_q/X 4h͙3'λᄏnW^y%fZw|Wߟ((#J#GŋRmw9m;PM+؝={vt=W5n[~_F,4nܸL>}4_"C5\S=\QjNʾtEC:G)B]v]}tgӄ *?q/Q܋/BΒ$Meר=zt}vXa[ى3r6t>Xocn 3x\yY]O,+1-*_Sؒ%K"]X~8W&Mg{'*9ó>X&N\#?jԨQUe_}V4")KFNJb?ϻ/\6nW5黲mn" 6aĨQ"&]"VkcZ( V_s-1{k5k6.>}P='?IUhmm1c9&Wnر>('N\ 0/t\vOȑ#^>v۪ٕ b5_ Xe}+GmnF5Y*$e94uԪ<Ngk7Q>9\!z駣}߿~Ȑ!9ɤ!ŋ+};ߩyų /0:uRmmm5Cvcr-Uo~\V.i FFZt4kv3C'?7V$>|xե|-ZӬRU#IJc׿"˺CF֝wyA^{m C=KqU5 ڢaR17x#cf^v lEW2Rd vA$3,]4joU__s"?~|a?n[lʔ)=`$lhQc=9k֬(V|РAbNԧO\Dkkk{-ǎKW\qM>ݕMӔ^}U:s*-ګ6۬frXJ&WZ%+tGM#G̍C_ (&"zXÇScc#crﱬz]?ySfemvء⛿C+>O.lME駟F ZY\ho}/|H9k,z'?#ooA7QT&VJ~뭷X{GD6r! p ?T_t}7A&ЫZr[%TeCP3:~ 6jRͲΖ=hmm&KRՄ\mќ1;5֕7z뭝:J g|j>VёO<1ڷ[V#{ ~+k/ [2e =L>`,߿7cCi*[Ji|PLnj\n;WN)Egy&-[DmԒZ궽'jnjjn9siƌZ>( I qZjjnu[s/4aҚ;V޲[o=[_j蠧z> ]I&uydwI{.J%Zm+H&a믟K-m4-jUWQކS͊&Ls3jlSKKKrJrY:5eʹdY3-YDK6Q&9b[o -\.ZB xmo}:/yo%o$mG;SŴsG4{h۝GTvРA4m;4w"-nvZn.ۢKsN'?kdi-]LK5[Bwute>|8M.߻U5nhmnpE /&NIziӦ@Y+Z9/… ^m%c޴tR([uk[e-m4cƻZk7!5.iK٭q-Z.^J /~.69wtmw~4c&-8n95gf^@S_vݭrOwOf4s|9{{Q۳.@Oޚ[~ނ4b眞b$ <[HF{,%̗-kޟIv;+_9җhĉ^jjjvjoﰿkL5Y]mncLB0 IDATŷv뭷V Y_lѢEm#v1l|HӧOփ4cG@bezᅗ*Ȏia =/hy Ӽ^7Sv?fͣmn^?yd~Xh!1vƸQ__O:)ӎ83qun]:m7zao?\m`o{zxveSlIu/76 xN:?ߎwaysϲ $k?]{W.c7h?Uu]N8}w_yH$  vO_W?1ڷ;gz-܂c9nߖ[n. x8@8pu)aĦA)#cI$q'7~|Ejnk>~}-P|۝K/F54 _ӠJA>>c;8p('yw{~MMMsq"qJ)W|V>۠8g%gJ+=mh te_@mm5ޛ?`,Xӛ?.䒊߿hjϾx zj!<381{l 7?~lo+~i|KG`ܹ5/9? ]zv{]Xtian6_⡇A UA9'(q ,kS_ŲeKѷo?fi\~>v!r B ./qa!Z& m;y(aG;.T\Wfm5=k,3 ,}7uT=?~<~b?^z)8hߴi0r\Yc<>x /rxA[<fϞo}dEveW᏷GNڈ܆*c <,`oS_ESSL:`-Ĉ#j*hCշ)F;1FØFeFiSUh q{<9`wTmI֐C ey®:p"|7.7q8CsJ|ɝ/cwDsss+rJ}ikkg};^}Uy,X M8I_{8h0|!=?O?UR }y٨}g8YFe)1rr@/9BvG=;>?&K< J|\GİI X$ 1cvt lݳq3xZ0i$xѾ >`MOĉsO9\q]ҥKq;ą^?>bR Dvꗿ]x)/zg[o> ^x W^6tm8@ .㔴 9'>{ '9X>* 0z6k >kFm<'˫ZZK g!סdp$ %*Ѻ=sWQnjZK.رc;Do&y5qꩧ.9ͺ+f yV7bf6#$+^#`Y!t8fבUqLz]ˍXsЦv 0e{fz2f䏓+}2Y>ˮ}uxl3i;]ҙYx7|[x~у{r]v=۷fa]G¬ÈGM8)L}Db !z{'uL$b0 YmF}#^>2 eFpYC9RG("mLpHPo*.Ki؋Tmtk݁a?ओNBcc#N VR8#||`;] &ԳlĞgva3Hi lXgIN9cZ)( vPV}V2n%KGw}:x g}W-+!ϒ=&B"د=h{;þ).'iȸ2!vlϾ"Ed%,zm>$aI8+*SD-\_c|W#g!dٻ˂;HZi(vTش;ǀ}C c#2 2RQ32ąw(qY`gt *lB%,@2rD1dB5!&*c} kIKD׭C?N`JFm{fvM3J N2bHv _<+8A&JCf҂da?DP\9  8mB4!X H2{돶B/lm̶kFVnmȌ]P"tBpغ;Fduu :S +wZ7p9mc1\2jp;tW})my w[V ;rdꕿ 2&I]dhGf sAY9^+LW dxޛs|uxiE@k2;=8 RĒYx?ʳs!΀i#rr262l@s@, =TeǓwbdgRiI?*oI.rWD0J$i"; a 2)+cDT\HbiQ7eɝ3HyD3e"'8Ʌ> Yw >xK3F)<0xpn2h`մk `/? eb##n %$NZi݌{X([t@;ˢBPI.~_n349 e 9`)opD wOc1|UnlKMR:rB* f{&<"E7a`X%|aAD`J&P0IL8#; 1q 8wg*~F)*Ӿmo!MɌ;ԐUJ ),8Ve [BIz0Q   `y2~+k8/@aP$C`2T``'v"yV`Rw;/E!1x.ǚɕ keBEecܱk L9Ixwmy,x"t,b11V1@c#[/]*mf\=PC! ^@^p|LH!{vLWr#RpvӉ딺4IlItDvD}⿼um9Z:=GIU%|Oذh.YcҦ8 `m'3c -tݺV4A8`c 4"~g[10Lr\2dlܲʮ_58H16kgl!(ZahF43qnrn5Em_UZg@1p`^.xQ@;`%¦Up M!~Mk3[m:xڵ>^.ߊl:dD!έkc9HPM|Ek%c2J&``RL=^ǀSϬ@~ᙨP0QJUܸ8%!@\]`mÊJA+G sg.i&C[J,߱`>SœIEU[ 0ZHxnniqLq(@LV pNS; 寓-8P {oug[L𻭲uw pzr,sֳ!{6퇲x@*tOceٷ&> 5{XHk56B5] cR^6F^Z,ȸF@#Ppuk=@sdHCQ(DPa #-y#{S+e"r4/:qpJvHH>ѡ# F*ypsQƻwU*c!Vi^N7H&(SͰ_h4 $xi$5X1KZAByEt4M4AbVuK5Zó06\1yn*v8}D>䜀~fᄤo5>D[.4 D65ݝOl~ ,#{ 8a xIVȱ"muw+XE ;#a8fD&`%lώ9K_IX|•b,2kEzah>bzP%_AGF( 8 -i;Dr-<G#I`Qʝhd<# Wߞ]jrAQY@:`ʕsT}YwN:cpg@:tI{\P/c/ʸ Eeo9W0&YbgWsQ.} q.6a\ɗÐ2㫭c`lB b=2W|XlHPd i:gܽ$+- AM nd%fr}#F쓓4Gh W$\rJ{H\Ng?7?k־͸s+18vk:Clh 7m۞ea,7NAJx&8c]4 k9WOTpŐy>((5 Nnۛrgq;wVJ#%FRrZƁI]t u--6W$iz']}Y%/M,!tK|(Q (oWD צԙXugxHݺ?ښ6VwhX3u`ʟCM[50PfCbN²cpƓ13) tg#@Je"F-8MC!l_TmPI_( p:$6x)cC>):@ }8hpdKV.X-[̗2U,܃ Bb'15 ъR|]5\H Ȃ4 ˫5iVm0ז; >:"sb*rE)\5)ĬSq4/KS/M(ǛuBBGøVw4"I/8v'x:M4VO*2sYa45 y$LK%JEI+4Lj8:n%H 7,0Xi@]`jmxCKW)_"-D+N+ 4H.c:坂M9F.QjjRhhSB,ȥPmRp]YdB,;(@1'ы!(7hHFP;) Pɳa0y( )W =+|JF~nH1~=Àl 70`wRQH'kTuV'PAԵC✀/Cq:L ìI~*I lKF0=V>>8pu.v`^_kV\.XȾ7@& DpIC dd'1 3Z G}ƉO2Af䨰zDk/鋕!$OI+ $K te&n!`P 𵍴e>(\˹%`3Zt -jBVIN5[:u=n _A1ٺH$Ɩ(5 P*½'/`EΉq(5鐄ͥH5r+!x55@̡vZN]xL 'Bz5M)\n\gWR(xG"ZRpč\~%ԺI;Q T,Τ4f* ) k:]ߣ ;Rpe /%>: H宯Ri v@|s?Db֊H[ď>Cr)5_C{Pώ5jYszs>\c_dِa*y_0$B IDAT7#бRg@1}(h+hHIX3݄Q,e) h1CvfIN5{'\G1u[2 4v27V†yD۾/0HtYư0 @;4'C O)m'c LIkJvT!?sYd}E`Qo.Y!WIX ɔMn^lX&/-ɬfȽ+SP2_=tywd{--UvaeHtEMc]{t{LX|7چA"pmAY-1}>.l(J#44ЧO/ieho@RRhIS*g]g 3|Icae\$%KɃ(`LFi2ou6HmTMzzʏx,c8rB)dSNF0L"#+ӲaFi(<*;T()ɶ#2)qLʎ.XYECD$aE ^Sp}$p!wozZi(u[$>Ǐsn{=[if5 A̕VsrW.W/+7JYaA,ۂe}8$OO"hLz'PcFAȃW @%P_ MFlAցR@{[;-Ys0{<`Y3:R(| *^\*ux&xdp,eg\ٴs`I/v"ԪTCKX ӹJUVT9v,HkVT^CYەet@.ێBDP GRӊJX]Ư$r}yF"7HPIgڸnʓŷN@/f1`ltV5Ϡ6ektDiَFڵRI7ѢNax}.ȭP 1H:Gb-`[t(FXDfLc-+!hh Qo"I4 R~Bڑqq#ޙޞ>[ )A Bk7̞!NMF*r ZjJ"Hehy21  CWo ̘Ǔ Dd}KA-g?}S Ȁn: ɳ\*%%\P+NI?wFw2N":) 1$4p3JQg $/Hwaw>(+d4x= :с|P.T7/ޜ4: T56Ry$$M(ˁY׉0EcK "3Lgj`@8set+u)Y Q1U  'vJ uuhh(hQ,iGDV N# ʕ"TT&  `:4P/a t*tq?(& -:k(s2d aGI)nӲ;-S -u2) =`A?VmW:3. ;dP/A x2;XP, {$9Vl `$QKP_ѿol޺Xwӧꐖh\/M/]$aߔcR آ1( %ުWv?-S;I+bDtDNn, idAMx[ȍv! LTR( -12?>/вph>s ȳl ϬZw!h`jB/̐ T :c$ ȃdǚA N$eleNI3SJ /1JH;fkoByC(%0i&-[i%R_J77٧ER탦ѳwOvk +P$X$,H붰rij)˰z ؁8o`$DcO8ȉgtIK,k,"7(u62ll( icS2I X[n  l$'s> {_hmmHI:#{°J$"J:/! U.I&R4lrR Vy #s |R0C+;9ƍӽ +,cכe4N ;][CuV9EdYry睷;˃6VEhÁnQ9&KQ9˚8R*6.'dv% <~mkaryr8V$L #1VW7ojYN 6` Z P _qiDnfC 6}zヌЖZ֕(p]:ou{Skc5@~[p(3EŝiV וjܲ n`z"Bxle)%dJZG]c~l.weYQ z@VxݷP dbB@keœV˘ GZW^KVpBu*%őq'ΩsL(ׂ?ŔC`u} A8£u>|[{b܅Ik 7 "^ËXs)DDFeoOp ,CB_q  7a v2&F y/I9pL7Ж+R4Euݫm(ܚ0MӓaO4 ;Ŧxxבh $I=Z;f8#D04H;}UKh5GJEY" 3Im5= ZCם'r" eR~,J/Rh($B 4pF0j 8 hik0 RjZ{IT]Q%9!K|bp&^z5eɎUuɹoHä6IH~QIy WҀ1"HdJF.\-c|uaޫ p .["$IFd}o1[ uוgn&0#  $1<>LMkٌ@r`E&(EIȔ (P00i(~{G [oZڡ (2E6Mxm ˀEk ۮd#FOkv!{NSld_l lߔkŴDx>IPJJ(iu ׳.ĔP&`i@Cz& vt"N['6B$UvbXav`ɅV'u0v$da8l!&sE mc&2&G!]7.i؅)K7X$^:#(Rc cmf?`3) F}$J!-22P 8A9eRz0{BJCmm<в>h+|f^x_ba c@e(a{z ~$GW֧qI_@D :4F} X |=P.#)Cwd (:T]K FMv:,f܊y$cz~R6 2w8]Fa)@Ű ~Z 9뎸0?ͮ#| g`-Q&n W+xF{n!-]HPF03q )Cda>L) Q{ q@+M]a~aVN TɂDېK!f%f)ճH)B1#3\uRNmfĉ9uuu7n,{ $H`0)\8hb+_k :%V3m>ʆy6kAƃT D^&-,9_&(yTxLUrf'THˢ :.k(A*\&b0/H,\pJ%&$ Ke^DЧ__XotRrrg( H>ٽ] iʲ#[ND%X0 a!4`٤\J')K6V))HF.agz(Ja:u9fYk܆%褄`4٥d<Չ"/$~YISOhhe%kp YXEJ<`R Ⱊs9Ujm @{}Y1Jo+0k./: xsMXlU Ax 's#\)zW9w@,l CUEI)u8Qq'-,+Ifce!8>}Mb'JG=ҴZ IS:U ƔجJ hc]v{Fk9E]rrj124\_8ޱV@#F\*)hXv*P!Wv6c99H%n)hkJvҺD1r&pe`N)2Ljcԙ"I[{ 퀡-˩"%e^ (ˆ2,1LJv]^`SeSb%a'/ ;2Y?UU?Z D,+q$O&IUc)xiLHf-܆׏Cr" ]\ {|v^.q{-`xgx;g!\dwyTi[6xR\&7dʞ-`,[B"$ (JAGB z.!5P0()ꔂh&BYyD%$IgVBJee5"M|p`u('6j@{9ICBJe. !p浴*~v*Ȕ0r2B.ىRɊy:Ρlڑ%qЈc $ u1bC H6uÜfS%$dGN(Ô`nc;aB:6ԳF3\XVZti8hQ3:Q64ʾ Iɺ4"NfY"Ev$2CP|V< 咮R($ޛ=[g~?̳ܥnm] z$@HH6#y?jdI1Dp8@9zpȼA kTչ%/>=$Sug#צ~ MJ !uՃ"%E<3zZP./??J;+d[w{ olRوu wm}8dsWYɲ>K3YhoAIѸVJ&P% [[LNoZ_da;bcZͪZ#R Pfщj+jE)Hٹ{)B`]: ま:jFm\'A ?c^]efi 9DDHcA8l9>:f!pq~+TH%B 4:t"G\](#1՞dE- Ӂ؞lwOwu.(C' }4N|/>}~,H|k-Quo=wߦ-ChIzVF'(BdYwX&T߁7&g`To/^ @K~W5*>`m>)%d6'Mv|nYpǶ&Bufh63k8lU2[@>wƞD'?9C_\ IDATjѼʍlQ;.½{+}7xƤ kw?g~3z-azrHBu$_?3;'?/)uOg|799=,fdlu8v" ')ˋsqB%s2!I-9 ևZVbGm#xuE~~ wQޚPE}#4WP  BX^J3ʘlLtj%@ CVIQ/mO}65G38]3(.m`b[g,cMfݜ$ta˲ U.Oi"}~ʉR ]4nZ*`;I]-($dvlU;U R)XXA+H%}9IE;%Bݦ몞42u!=:w軍- )&|V:2]T(]`5oxpv**U Ct]sv'?_ ; ͣB$Hh]{ \j1 vM07,VO*B u;~Yݐl ?/G#={j(Ϊ +B;}xhz^d2gHQ=ZfC~O?Vmר&͜"@% Iby:zx)=)cez?-3ݮeiv.#œ nQh%JQ̩F3Ej}d5­F T.92vhXߵC@{ޭ-S4"d77&]z_-*%)b<ѴHs.+>Y;|R :ǺO/w* eP/3uRb "YϷF1gRPhI.9XEZ%zmhaќ$YB%gBL?TMOcĔζDžΉPŘyJ$UXΝ݃k[Y()?xu}R0=&O;jcWie5xbR`%_oLӇ.Q+3~TxS0nx3WSJ3Rux9yeMeuZ4kB4@+.qS:RZW:e538[Ԭúcg/X e4;$ߞۃ:IU-($*aK~᯸HbnGY =}[7q|2 Ld "gRiē@@cg S6 ^]_%t  %R@Yi|J׫V t]wQ/3̚훱~f5ls}Ph,GX/2G,YJ6:o+EPI6 9 J]S,#WbGpjz]ek1S!n j^obZjVSi8}̷=+X*ۂNDW*x2u[}Wm 6hdw-~omu]4vB \v3pVOY+DZK1Lj׈ӻƴb1u=[!R} ; WכiDJ#AV-C5s$HF}RWF.بzoI)7xo}}~Clja7{OyYRQ`+jT hԂ0C5/_ vdq ŷX-1R Njo`2ffcY4!!2^mkAX'LV(&$[$C7ԁgG<0SZaP{wx-\ha^b)1z>^r\v G;ǯOΎ ي5h,GiBd =HTbO> 8"$@ Ȁ20@GwL+,4[:m Vq71Y/=Ֆ@%D >JqZJ^d55Bʈe'EAxSV5dZA=.>k`Mɇ3S]̎`$Z4H9xm&ױ}_Y ZEgL?||u5'Q8wu'IVY{=-\~f.`<r WeXK}_- ~D;g-pvA*Qv1%sɥ@o"V Gk<}|>7?=SYoV|' Lhm~nVDM믙iJtrsh]G?gՉާ^P4ڂ#`^[sSe Ȝ#[A"P17lX /!돻_;}K\q&r1v7[w Z=-JԲ=VJ!k d^~^֭_V2ĭu.Y/tY-[S=].ށ{ٽQR. m@n8w[nI0<,V׻^N+ WV+3_bQJV%z;(=@3ԀVڡW,yۣ#ώ89;aX)3M#3%+](k`lL$z[m :vΉg=>?Cwz;o?a@Q5gK(ƲW! aAPgȑOq# L9M)Y(zYьkZ`Գƽc?q xtzfS &2ig'룊#*x9= e_5PZ:aT%½vsu1[#@XsWUf+&@GA0:MoMP3r'f~.UսR[p߫M,xYW6ZWWӻuH* vE @ϭlM!_J,7wԪ. bAgEiX'ૼx b0 ~x@tzUXVȺ#5B0z^1C%&< UK2m;Kչ(qy[ӏxou'h9MHrsqy"xMV۪sRe3%GV>/>fM %E'켋P;ϵ4-FT/P_W2 YuA =OhȞc5&Y*C,aZݻ{*^*֤qbc%Jp 66%/ _ݝ @vP1D(ЬDg+4iKZKlwS\jsfw]2y(ͼe-}B:Ů8^׍6֭ȬC)s fR`Bi('k~)+k֫-[{Cϟ嗌΢ 3IVh ɣ"d>fVCw'yN&s,XUQW-fTK4k e!m($'Og?;B&ϬRޮXWgmqteXA ).MkS[gL=q$7L~ό܋#~!?.y_+eaճgruqOwcy޺21ΘK5ٷxIeXwW\4o;@Ilmw.LB6}W1K5v˧}p܂j1t-aäs/RRPն]Uӳ><1P2! ۣJgie-HWMze&+נt݊iwMXEFDzaf{@UmtcLR{KLV@섔3~]&7^v:_ ח^HP,3ƺQii1v~w l65U:jk^}17a?ڽ((&„npv; '00e"<}ִ4DVUxᪧO$HF$[I -bSjϯPA5꼧e~SuA ͆톗_u J@X1P80lZR@B-e?0MVVh+U N/xp\b{Ef&/ӗ[g,bgA4!uc>y *-}p#kf+5]ν\ZDML oוﮤ`fMf^n?Â\& *U 2SYh2M V u5˄~oI3˝TccvϚi[@_ 4>K<|xSOgޙ=D 0ΪMi΂f w1BsT8ڜS>W^xJIirOmRU y"v-sMdߝx^ʙ%Ԓ0{‚mmҪ/UMb)P\HՆmuD֮ יoQ?ZHzꚜM54OqfřR E *,f~ O,*tRj^/XAN" A3㙙 8 sfs(jVLWlF7b\KOp>Ћj]ZVRےdjŷuYXhF)l{1A-kxf)$,խww@Ͽ&0(=%%מ~!#@ڽAA2]Ύ׼.F_˿׿@ (!Z^ɅjMK9%tɳ);pW3ϞMJW/xpy,cºмBh &A֫Ϊ J$F1 u< 5Z`c:JIô|D{H߯9~X7hV+-3Uyh ېJ^t2?{Q#7W76BY.nhc.S;%ӭgxVa?r" \ڮ:Hl.g_ h՝JR/7[0CMR&M:Z@K5mw"TuQbiuuh|O:#—YֲH"d_|IÆ5үvg\]ǿa~#2i`8f;Vkv)OB1BagߪywP-\^/q),9ץ8<|Uձ@'jS%3VjjWg3@ Z&[Kyp"w\]]" 1xG'6H PJ:2xM[:{]N XO:$ vMgB jى#W9DJkaϸ {~׶U]oOݕEӼ;ksys`Lȸ۳ޱg~?qq}@g̝1)m2 >;ZXAZq&Vrw?XwTRy-$,w\7U,dהjFd~}ɼ8(BҶ]w,vP51/6,* e]RwƸ¦C[xsI̴%3EY|⤬ H8,ٚ[PK}\lٖT6Uy TR0KB%* IDAT4k"T3hh,12Q"~f&7n݊?o _|ruuM:n+7#aXӍ(HZ54Y{zϘ! &YV1+T}%Dsb.{=B:'vp~)>P(i$E_/ qC$1H JbbGׯYz.$ʄꄔHdM.%-. Z\bʅ1Od˗!%K XC"vDq^aSS;[,,BP ,Ѷ Tk!ds' s+u.rk-] D)sAei jM.-Œ}UԤ bFm ME'u-9j4HUu|e=K^leݿSணvș V.&}-֔KY|t̕4hg'P#&Yz-i)ZeUbęjߘSv~ԅ~A׊yk2զWny7\BKi7+tX1 ],,`.b@b{\\r8uAK1bWDw__P=+ bis%#:"zII OP6وv߃4ЇK9ST=~p͎n 1r||lkv.iA*$S[KО.PÒZZ4i"LJ&Tz[|3 t޹1@9L1RK6lw-bIc4]+,:FXY'1L= ]L^@zO-JK8 ѥlܓ{s`]DaH|A#Z@V ֎#:I|za}W |W^juHqowXybqK]{Bzh`p^\:)\jt<Z }XaH~ڮX4YUP\2A35Ǜ5]}d% 1]߳=ru婚Xڳ,ďЙ6zc@9[{gyu^xjEH ntI-k,4ߐ4{[ C_6GG|k.3S6&i_A/>D d{1+:V+6ۭu29ڄaFわc3@mBQ4[2eWWLED욻kcۉ7χS\sB"ݣ"rӏ”͕QD[B|tdOlARlnDj,rec7ˎaq-^2:B<V1ŮE4 /ѥRB[Њŭj).b[IE+uY~g 3Wv4mZW}o ̟k4}j.ts,.ӑAA 5&db7{J&vUYaL P9HevfƤlb0讍 Q(YY8(a|4!@2 zz4W8ztBV <lrWKBKm q: r7t.9dMkLIx4ހ^Xz=ֵF ~D9 Gh}`կڡR3%´A>|z*DCaÊuޣVd@;przЯ|&aQ욢OU`As?mSl(Վ!AX35L]gdo-->1 -h!YerK!{W$x((Ӂ#ru}GOVl6\ukJnKJA]׵2A t*#vcǧDQA9>=fe $q4@ڽ P(#}c's֫Uwx9mXo߲d,U[ԒGEwt7~K5}<&+>xJX s= Nth4yQ{ mku' TkF΂iQ ]*fUQ]gF{iV.UH9ڏa,bO".1j=)!^BDPeUg9,s6$1vםn|B%Cb kgٸb%Q Xic&Jl]ZF0ҹg*~]?_+p/m96۞ka/퀥N1[B5Tj <+;3H3NNV $8+qj cw'IK~<~ 9#~-V)ZW΁:aXRu茒q5:%WL#Jx5>W=xJ+s})ͭ%=KG΂J )Q #k6D^?S0G'Fz6LD)h\)2nb7$~02z#m=&+`Gߙ_m!= _J=|~/ [>p(1|@+$?B,pYgVkZ1د(XERF⩱V1?$7WRBKbVX 5;`Ĥ5p2Hkg p[ctj#:6}X\&Ak2bb?Pb= }ZQ+%YUj_BK{adeAaFNVN6hzgĥ3:IU7K^ UK($cK,O57WHy].V8ЧY#a%UD&̟ѱZt#E',})wMFgt1z(@VÁxX ;7]XfJٓ\w{$n=XXy:Za}AG!l7. V$QdtC5}"9%_VͯHƸ2j(PvCH&tm<n<͋scf%7s?_Ŏ4@o2LKlvÔIcjO05bM|]X׺,4%,-5`dgrAu4yQ0 3j⎏/&2mͱ%bQE++f>]BdbsVz$桶o<E-uON\^g*n%TjRZ@oROpiW/ y k~^j*F8D%w@~7,TEo}2ֻ21[Y[޳=9"pt|jཻ.#9bf`؋&)oW'WSK^"D(-K../nЬh&kS@ FXF?a)qݻ߱9x8#WD){$l8Zs d񆾙tqAwv1KU+o]lrNdG`$0Hy%]҅}P$pz6`4]u؋O:@'({Oӷָ@A—~N>\EaԪ GO| n| !'ĎyRv(zS>/(eqy*oA9 q֌ 6kt4EȚvWl !Hפq5r$hB]BvNyR`KQ)SanȺFKZ^Z)qH@Ĕ3ۣYH:/>BC)ÉЂE+5S~[CL rR]~5 ݷoėFYVhy(i`h<ն-6hQz,j^mG㋥.ۘ ^iP$xeE();!5PY]evR7H`.Skѱ%q0ق$9YHQKu姕[&u"{R#i7Pʞ۠@+؊:kKf]s ْ -x)ZϹO^my/_>4`IHa^23|>b@{crvVQ$DJrw,:~g\ X^OT Vuutb-@' QX ӞOjBa\}vX꟢grx8>gurgtjOwvpgXu+V!ͱ2qCg:^[2Qk81D5)wqq'h11340^qwz$= 6<|"KƛLP-)y|K E$ `Ѡ 2oתbYc'Y"!v q5.C6{38۵sn؝Ƽn5{Bu B.%mz7$5vvv{_}w>/U%CAf \6.ޛ/Ђ"qm|"qJTr62¹n,dE1ّ5WiY24S\ejG; ._ IDAT4j+ U'Re{jG-˳%(+Yw=Dj_p̎ц_ŧGzNs21Z pG6.Ki\F`*ļ4d\Mإ7'>?gsevxXPqttOˊn>v8 )Z1A>uPPMO;^So8?BD{kK$"VxN.&SJ ]'s);U[)YuDu|)`=: REY_;M_'H7д3G9.<]jb)5,\Ԥ$ h k§þ%Сr =rFxFX’: 1GR"vB̯V.Ͼn sS.gcRڣf- FnG_78.dNP7C{B~~*''5nwk'HH9=dyׄ1'88>\rsrr{?O>Ԍ*;|`ǖnݺ$/ i'"{|X"{|X . Z&^cu9OZ~G?c^?ׇ'TY,@&"u$u\1Xs 5x@kcho: s3D_d6I6iW¼q޺rg07JIIf<6|.>LLΗwcm}Y=TD\>S| 쾂K( 3lW$We/@z&|reөI~6(:7h;g)L:@1)UxssѕPnݾC5\\wV}W5@:gRe|M@ sܿ!?鯸8ߚ|Vțg8'5'8Wa1֔ phKSWZؓ:> CVRK\+՞+)BdN{rkg(1^#BsVY4,ZRx攕bBD?vdI} q C DSό)޵h2}߁4T dGrҒ_ ]zɭ%pUML x3.nAO 9#SpKmd'SdQ7P4HE΋9 +OU{8<\spd^0g{qq~Ç9Uj9k=Hθ!,2( hTP"qB^V(\X;(6' Q M ) &Mxub Hx:HdkIʗ6Wmyb@8/RcB.ㆀ hxW3LSah|#7LYPy.]׳\RJsheHHL׊%7ZCcWU6GG )u JUUh"j,u==Uhr8$,Q) : 8*n*3I/ʤfS ֚QMc f]U/hSq'~⪀5KH BeAL3WcytAbK1b~@{fC~iE.5^*kޝ"PDI~w#Q,VǬ7_m#'׮Ӵ!HE✬(E36똡hdc+ϷM4'Ah6cJHi,$xghcD d]xl EߤO=<l4|{}_zL4l:WϿP*:K8)^ٳ3> ¢bbݻ|#f?Pڦ1m 2[EƑ] Бl]}F۬ϯ|G=_RK>}m|ްp5"b:')͉̩kUCq+RrVᓕ*{.q, hum7ﳌh-[5:!%O@әmiȂ0!=CcCڜq{j#Oxs餢WV2KtiQQʷKLkZ .'iyX>g]g4K~m'G$Lf>V<}_=o҈v} of0L˺xdG3g3 W\ٛ|GtԄP5nP`I;{)I~NsDTJ-w_J8C :psAPKmϩ\-7ja'8߁Ơ먫{ bgG[t" -q,.I<"iIvԤ[E^!"*oLt;}=vЦ脨;i򮪤 WD%!5s.:R P*,r9cu#߿7qrbfF$aߺٖ|+7wܸN,Ov6֨ r'P-Y橱F];C Y#SgM2Rƨp JCFyۣ9XPiW'C GbP "Xlf<2kږ=g΋g>HT3:`S/p||NawIL,iS!1qs~_};gFAp57nUgh]=HHyjnFw1; >>:b|2$$uMY|蟓[VPj #Ljp\4!QffR#1%T_G$Ii͚[AmOLi ΢ I&e: juˇiAs3sB3"IEZО$=)f 2"=]h%VoneD겾yDU- {Hx79$QϏf |%K>>{NbM"Kjt%:LۺEI9СL+`*9G$!3ܺ{̽WrMV989DQ*Fˣk)\h I_νG-Uģ1RfȜ#phR1Z GE$Ɯ= f\l:9׸&MG{èKRbK>J33K^x)Ý [chG 3ž5O{U#^7nG]%:5@%M+8}Ikn޸G^! $5}IT!{zdfU+0eU&͑%hj̖y<*0'{ H}c=8yo^GmAzTLom+.I>WAy:AJRkՄv-H0n66ݞ^@H9C:@ ?ɤ\d>kĉ @@3 0iF3Ԉ_sx혽s#bo3 TkP߁1WGJbMF*)$[OwuaYI/ńwb6.3iR׊ )J14|Q~N1D'z1`[7}&wp||HUL pđrScT8׳;}= 1~)6O1L<FyL"}sT0k6(k v9.|e<B{ KfH2]<겶v2=cqfTUR$aBG:& c8`{oaN ԟzdrJM ޞ:/&C ţD]\M$O_'Xr8}n. ù?ek[H}P)UM۔0fo,=%SX5s jiȌ}"<8f**59 WDlÏ8}Q+VMk)]M8k4<(L ccؼrXv"ۆ*)^1Szx:s0c=2_0V4>1}z ޔҋT+so#)~o& %ύg|'4 )uM"8DG%T+'aRfU!gS2o֚hݑ 13 6;4.ɜ6Og٣lYNЮKn{p4h6!!n]. JmhD ru#9 eO9D=قsyqYږ멂U!>$L$-k7TEcb[>>9ti1`?$G;J{ɐTUdk'=^}sZB."N4hx/رn^n}?Drم #J I-3ܛ+:! H t~5̳A9M ס*4ZK()xIV}SHxM{L.t "+p/}b|[/kbo$\+ 'WrAX\O\p%ww:r~ v#;:]\6J^%BnE&B-fkh1Z{F=|}MTf>C|rf+ճK_;#=@LGM_K9L(t^*+qNkOUY^>|U$RMZ׼T[b2D{kuO]KC|bϦHh(MdfH6 Y*Y(yJޢK~'yz%Hy>!iM 1喔ͳ<;۝8{ll 뿋;]ϓW3S2橁އs1z3eȔ%m,ЙKO"YiLBhv.{ޕ8$E>3ꛯ 4A ˔|Ƀ2ys:4NT tY~|a@L ɮ%3"O&*B߁B= ^U'7c4g&*Ԡ1!0sEj\$H:fbOڮH<ů%u"O]\EDkq:"UwCd~(K3ݗJ9xٔdMë+E<&,RiSzm *kcPhH4}b~6G1j4YHYׁ1Wܹsnr5V"wA0뺌Wצj׾oĥc4mKvl)wDMVf;`}@J $Ur%,xW+wqUׁ ԛ#!%</@h^$D墶dMv%O]"W?='=)Yך_/I@JtTI)8b3x?k ]u[1C2C9g9z;`8ijVf;^URvL r@ A+Ò41DX0h=lbb23fcJc6M*"lbW>P}4,M} '!ЊfCdqG{hR. 9Å;2| f:0/ףb 5+ÌD5sk8H`F&Dn̜o?}s5mɅsLJl@BP ؓ0UV3^yps *xRq .WZ(G-yM!xe1LTX:S"au/tU(d̒ ]wȐZn3 +(Yڢ,2eY]4=RrSdԣR5Y#y} 2Ƿ2W0멱2PŘ*/ }s-2%\KiXR60c㸝x}C<5JjѣONI }nKڞ ?>Õyes8z+Tf*NIM`U3nE𛼄LQB\ddl'?b-jWC〠ܒ;&]ZߡK+b~/vbD vH(.Ըz#ڃB QQlf&K-l}c0 IDATAL8";=_}|WovI⭬IxW  :qZa9e[orIO:Ŕ{!X@" :8/0zqX]װlq>puML!KڦC 1ZJ9 Cµ@*[Jd4¤ca1H`ٛa\緦MMگ.RO¼x}+9p~0tM`A/D,<-_ +%QJ|M򕐓q&A89ƅCbCc@^LIoi=zś?cP+m۬և=9}v 5x:+#Tm !0 k$Y'd;jifw)F磵9Ed(%F0$P`3$ bv'disE=MH fϾݙCtVyJ.h$ XBZ^a@i~/R9 7?@wPYxҰ ^KSa,~y Z]Yx=H u}!]^_O6tmt"f6i1vSsu|{wo2=U"'d㯓uwVgelto8,+9]Kv%}\^Q_w"Lŕ t4[h賀B" :t`u PiZͽ#EA1(Sؠʒ^ ݦ9@^lP?"3q'#0I䀡EW^zACLfqcfE*2 y>D)gÏ>wo2?\N]Ղ3^e8TUoyvf#>7Tޮz"mڑ:$!Pe<\TI*]466QMZ)tG0dΙՁzn[`I*ToS~1ks[? )9q@21/eP%;0CDM! nݠ]jWMg'g5ѳw.'%qhf6fb)Wp{e>*Ph^r}N:]y2?Zᔁ;9e}cf]"Bps|#dܹ}嬢tx 4H1+bb+giF/lģ8Ż,Mg9JKWFb?p) j˜%aEgOŻidT6%{/em圕+no+| &v/)Ff;+k4yCbS;8f`PG8eK[2/FHEQb9qyV@z ɞ2AJb6n]'cAõA X{_˯|?~?#$cRlBT*Ԭl.Nm/i=+If I) g<ּ"*1fra,ذXG3eTeie-xqx99mTQY=gХ~>fIb޺ Rh<B\?7y{LI2q sfHh|VnН\!Şw?NZ*9}z>!]aQ$wQ)p$&i(N slʵhn2V83Js¼dQ4ͪ"IlvZ_6,iRQJ71۟VéAǷ0ܣ ȕI]WpO~N k.DW;4+F̰zk1 BEt#{ v8{g[NO6[_ ~)Oli[ BM0Aל`'q>K[b_4\/y)ͅySu4BjI}KpS<ތA"Ajd0U̗tS4趧 5%kƘRKq79y}?-]_QG U|qu <LCdєH81&T< .H6'Dw>)1BΛHX-5N 'ky-^!#Ek"uRd;|Uz@J[]q$wGgϿ6jb[l.:ěRo &O#8m=: Q2ǻ$,KqUP\W ejyJD5Rfc2Y&OC&E|U!}q~[%'./?rZN@1`'%{Nu`#ۜ & q@Ȳ~t[H0txG ̑jIlHd@NDcg<>kfdN/ޤSc5t_8GwvФN-+sDgA`)6co 3.lRvs3j}x~{>"[]SK,d;7%1mog*1BO@qFrTͲ&6 B)`-s` k8PH$dJ6Uk ?A\\ %AnRl5KO 39441g=ċg?0C&1Dbse+IN r e(ҏWAj9,D/cV^i{ ◿A%7,OHBRܟ:`Zgz6g\X+mx _#,_Ʃ V@\ݙT=YȪwڡ,ojpsيt'TRYU$3vJeFUIzDlt0suؼ2Rrn:Rgu]qGqfH RH ]CZbLix͝w_Ï,W DRqj_&L+OGxgL#H[v~wv{zcG~_~J w_l6c{ZKxk0(rC汯 &ܒ9KI&L%XNlXX|QeP赿H-4Dx<]f3:N2g cֺ`#1O7ɗj1ɹ P wy %yZ IAE5#ƣMTt{i5ڮʯ{[~oϵ <4+j:9T_ޜ0ڍ{l.Ooqc; \]&Ӡv39plZqSk W/,#s_\IaϫkZ[$,̏q՜`}m4zC _skKKv'-ywy滯w~[5CC_-in8{sjɚ4Ohr46qgoX2A.FA^,0V/<Ô_ 2!J7+q &*26FL>YEiXlEf W2j9y:~~U#*_^}.Ti"Ŷ97_{Άxe2JdHj$vE23ޥݼѧ2j5&|%$]jȽѠvM'|-ze2/zW;o5^'rA`4eЬ㭒q.sR@K4/,Ch!U㖖aHp5jVWwIЂ'x8gY%iCmYR WmsT;Rۜ/~W_> 6 -iņj8<9dywƛooy8ђMMY `ΨT!@2)4Rl$R&@-:OԩZGm 5Z=N]!k-Uڀ_U`UC $ u5ܭ{y!*`!5XC++9αFiӦ=XH!z^k*刔)VAIK0L.9Y/C^<xptJ 60̋J IDAT6kьL[8Q$h l6^]su&ѡʃfG] HyDj.kN#j"EclG@/Hݙ9<±BJIX6mn J@3 6U[KdZ}xi{fuȫfT'Ȧb0׉'?ų//&#u͆:o6||xx7IBT!L88Zm͋͜ö|];RR0q4kZt߃?xH*W~r2T--&[1p{#H$Dx!6Tag/N+9b’.T/LEChuºAo]<x"m虳װMֶёwn|߷3k9ͻ(,[ٰ.~fT\!7GUYz޺(fMm9ݢXN.8EW<%:v+8|R)yjaj+'| ~tgU\epW|AT,YD!t/4kis=.n2; %tl9R,a" ɋ%D 7y]%Ȥ.;Xwḧ́Ztr|=lոEh+nAz&dR v5J]\{O?fɛ{tN8}o<[o#rw,j~`6b13n!&C1.̊ }4Qde^L)@:5gJ-IIt'n9olzm۬(L 6;M׉0I¨=-c7f n."ݞtsVKܘgg!,X- /ZEUAxX~_x~_'fVbݘv[d`9銹Ko`/SA 9&h2b)ֶ]jB=9АٖVr)TLKv~ /_.'H0 \-+ѯpO)jhl 0SiDm))nhT u!0!֊ W ydv2 -u=ͷ+* I~\%YPP4L茡W"jr~%ۢ Z1ei1| }{l{[<~1g6ó9;rG5cd ֘`-KN[}-|UjH=QJ&[gsoĉ_/?ҥt$n}( h7qvvf6J-(H)fdl]2)W0%AY!`C)o燘,FP!r0. lݤh"W-S a^zYr3xt/9ݾ{ɘ+5{~`a{3deR=冦yZZZ(~ֺ[ZK2OtXߨd5p y;CܥdtKG9H!"\Gvn'C~w!lX D@13ZFpf5T)n8nT$(y3^=NE"ր#N^簻z0aZZ'BR"U"asհ^pÍT^SKLjD'DX͂ߵNFG=Ԏvbs!.s[N<;Ơ QYnj|\hAH.bT`1 O?~98~=R/曈qq=h:عB`Ӹh\&oꐐlzyGlx*@(!SͨbS ey/>wMo{_wO?j98ko!]ynWNpyV [[+2K d^<ax[#ڌ&J&Eib#֞}sΗHC*Ӈ 7R>pƎXX^6'AX'Ȇ@>-P B&$1Hd.)ӕ u)  B ֑2<#)Җ*=X9>{ YLFR$:NGlU7n״U5 ]g6Ȱ5r`esm_΀ȣ֦_r~ӣ3\ S:c<ǣ~u87w?ǿC6ϟ|&M=|HHuXO5F1%/S^op8xl'v;HH|q)<zCU!IPdFͤ yŧcDD]#?a9e:{bU*XQ I. aJEDzn{ئxEȅČgE UAMld尫X8\&[ךʁq e45iaC.JJSDc.IM=ABE/^r1S75;Z+C]p17hsf_)\':$R꒟ٟ'|'?꼰'9GGd_;}y8>$/wzħ_G׼}a{rJl@ bS}4_[_]0G>Oqsh'K v;wZ:TL kJlJ=tO(jm3ݓivn7JretVz9pj*&w0M3zSoio{WS)tՕCpy}}F'2_PElf$ECZ<r) 5[{rQO8Rj6qyX+P!0N٦g#7#W%Rܱ;oN=Ih:=t8p~UkVk2EJL‹q1GM.(qCs3H1NA6q>n0!TD'Н`HK 5faWCMX(:tO<\F/ڵ93ϩ7O͈( 55߲ !d8PǽOhjH4#d~æt!uiw 3I8TeROuSw/[۞qD0d`@K0wh(l"l~xqhxtW?G٘!QwX NO99yn4#م5Zn ʩ$FD䒽Hj8FM>{'⌓9O=no1R4̜k͙o˟nݹs_7ܳ^g쯳rWyUgFtA[Cj3:_3 s'z;Z*`̝ jpTʙb6hT)mYY7Sk8xE)%5Bysہ |?G\Lȇkc|9!2; a{ 蕞*zX glZ(:0Ƃb"=Z,B 657\Tw&bUk:[D+(Zoa RPޞY)f 7kMfvF !rh6)q9 NEz7xx,H-|w"i91'jBЉ2J) どpѧ?cΟ_q;:#rޣKϳ'L__\ᅦG!WO/ (u9}xW_r~Ԛ5 a{x;m=R.q|Vѝǒܰi[cw-+P5Xkm\%-ʻ՝YC׉ ~ʟQ;eIJOM=G1R[:G?=k>'h\nrY.t<8 /{?8C99U}ZL0JL- oE$̜P.u=,b6 as.TΟwº*,deS^ AAKib5 b-y!x/f Bd#=n?O~œ/xw>g<&v7!r(BeߢO * .u VK؅ XGx(?8TZ:l-B JM'0L|v4˗ARGJBw~paw4\ 29G{hi@O [3s;`MFl-Z_Cs^p ! կ+Q}U]Ttnc>Gp}yšDlRE)LIX骘GyņkUmv]`kG%eZ TIh8~ϞsN?5 6n$kudJ>nQъи@&"f {4W%9g{=a]SZ-v덯_ѯ ݭooпឳŤuzg~^'=24#{a֝Y7[O,}BWiM }df3T$Νc]&D- iM5 -Xe횤ER (cX+=?9?{n^> Mn؜t`w!tLbaL&ΟBsz2YrCܵTKJEMN*S-Dq:hHtnR )ՍMGaÌ BHDܳѐ 9#՝MJ.;+~&6NX6; .ܘbDb̈u#g`y>kȋ2틗|OOnGrQ.儫1v[ ^ HZHm3;m,s;1QTHռP+) c.hJm)fe*)&R\`%!PkZ$h̜ BJjujPZ-wbfZBBkY'ZJdwF0 IDATζ;ݹ3\nC߹彅12zku~w.(o?F ;uõ`r|{#1nfPmFѲBP3]lŖ(]BE"%OLo63} ٤Jeakɠ< '?g?o=ǿj_>34B<ޢ- }Rtl 7iGM`1@Zz @)!&_;jӸELܓ5 T!C#rQ켤ZD zxI}J?#.5-HQH4Nٜ܇3W8*7]<4^A1"_.3{e5ZRK%U::p77!\\OJvC:OH"A5`"Tgw ~oƢ"}crGl'#ǂ dҼѺ%mjZ)Dŵ,)M1qe"V(^h kE!2xt7}B3{{mXۿmC1!_+.C8\PD In`6l{I@كAKSp|,QSDŽkݔ.yt+=chtF _x ]HAp·gDIAMs*r6IfGȟ|ӟpT07C7Wǜ* Ӵ'vs$GS@TRF <,7ӼP JN9IԐ'no1` ' 'fj^|z#A >$( sõQOާ79?g 4KgOzwo?1H5{T *] Z2Ĝӧ͏t\:Pu`k9t ^quxh5)V[^[j{·BOuCJv-^ꊵE.hTTv|ψhLK9yѹZ*1ť`9_g7gWy:g\lb^]$ݲw*ڒ"~gqc+e6]4krkJY=ܖn:j~ ;P  xHf:R>--6 Rm"Ge^⨢TȊfU U-lKa_s3Pj/9;SN1{n/ι0!BlNz9xCf, PdT&B͊{![.S9!Cdu:"uPrLݿd<\q]0׌ӭqS5U((8=:!lꉀF͡f YjDIW7Ow5>nwU%(e [ 7|S>GOyrK ٍ+ 5f)n512^ +Is0BG;?mK6GVsmVOχHLJ" n(jO+j}xOu 7X7$h)3xͤT2ELB,Y~w44]2'#<2("fZ7MխIg];[/zf=-S-BjphtF׊VDKk՞[} Pcýy4p\Ic"#ҜbXNkSk2Q%Csn9.q !RFG1&oN9{%O|K.[c-/?g_3N\m6eT͚fX1@J,mP>],D10R5S٢J4ۮ{p>g01yxA)#S3LXmVnsW+DVUf$bDRU[#wjlLq:ڄ먴40@p)WOϟ-$qџr[ 9HGz*ٍ7!*۵2q21׹) ]ocUcI'Z>lVmNî:Ā!H_vČ`u CBUjb1n/wqT̬h@KA{Mߴ\bJډ[*,QsW1k'Akz'CAZb 8[l!ZV[ا@ \xʍM H4FI[TߤJBmāO2ٰrkSDD f$fPʞ @6O6G2e`Hbᴨ2&?7 \ps#gx{\䗿i7uFAK#vڡiݨ5LN ly* $56k%@\ OL嚢\Wђ,rV "G//쫵l8y$O#p _sG<rm\m""I0Vfb[Afsۧs,hmk?!R50Z K#< 34עҔncs\A›N]נ75+C3w!f.>⮱+7yf~i_ZYm }oWGbV_Aw?^՚a~ro[–}p`)`:!(!]2` ޶Dq&B\ .;31!d8o(br~8??p8po /c=.vg"|ek5F3%;Mb[F()xz[cјOB@ u^$P4#D4gÍubcҰBjubBIfu n\}BtsAbo:U7;gY,%)52Ái8'q'\'tuZ"T-}S *&ͯEGI:HR k-\ZSZ@3Ҋ#-m{nLJ@Gu2W^[îin2PaxuGGf(>'5wG虜Z<|ueYh</WypDǷ}TIn`VY.Aڟk^eοL^,(X2{o^DIXRv,11EC)O܈B.Bƍ7E3JLTgT9PUC~_SG )9#,T,(al $W!ۗ WGiAwsg5K_jC  W4*Q;Ag:j޵:A/R3*杓%ژ7p2#u!kKYNV )VD#JbQA߆-.!dR 6 UR|mTXrxq˗E'褄w1ݧK“쇁 6=q!KkT_Dj(i42P'r2u(.J6&X7x%ĩo&foѥw@Kv8dE6pzF%@dG6ԘѢ$)=5T4*Gg.2@ j&8I7/gq"ӇQz>-DA62b$N:H`ӓw OJNvC][Hj^fNuRv !P@7Zj KnT7 a.7Z]Hbr1GT)-1saV3g~=YfFln}qw0}t A ^THcz?o*nnvllUSg7YbZg<]bi;6yw?q`F׵s_qV|TCtF1E7΅E|0w1-j"]ԙ1';pEk֢#Ág_|7E4;# 8ћ0+߭;%Ek 5 Î<t/N $+ZJ17 HI11Dېى6B̈́1DKCzDūFFa. JŊִZlT8r2ia_sj{ki->1 iv޿֒~7*NyNl7mN{gwZAVxmԅk7Om/܉ţА!q2tXm7P>!zY,mH =N;421B ]8=e*U dDz)w9L\_=g}\khL#M ?)نoIVո#Ndj"jHn.]? / 7tu1 0b]"6Bjt׶т g5V@Bk6cEZpNwt\iG W - $9 (dcM:KDǴb$[!4KF7ՎjCAWHs&a>v_G<s߾!dQAZYgYF4٣A+pLSowmc3ʜnU?Z؄h>DnR6-2GA #ʆ0쏡-%ېnTKV*Dzj=+L^qfΠ6P]F(YIɻNMw΋}B+厫s]^!7L!>vv1pPa2 ]67X*rC-%|HPV3iif$H3h<;}Ţb|!Id@8eXHhrn̩|54/ɇ*^(DYXIk^,Z5;7 \x;okrL gIb[b׬mbTf]z;L>Y?:/Ջ1ۈ#P V=Oup1cqPxU]#\́)Ư6]4U”3A3IO~Oaؤ1q|"]l6-)o]_9잡zA%ng[HinV0Gq t%JS>OwrFwoü8MvA^ Fu iMET2Q-%F u-DAcLpIns|&RiXZ:Kb`mPˈ8L*"+@r|\)D? 7W;^<9g50V8ކʤ1I+cZPK/E\ZLuclx!iC "S^+H$粪9XDj6h"z7j -"^1LV1zwPE !4DIktunĎ)7=֙DسV{if ѭn BYq۱5[W]3߮8ٌ#uxռݠ[D6C!vA[:7ay?ӪOixZkk,^S};ҮV`zҪeE4Sy*qN#cv82o\0M#'=GX )o6!.(t4S؆rdwɻ9ьN#>9>SΨ6|gCnPe;"<"jJ>dL¤FPS15<'n [oQ˄ɾqWhJ3PeF5XÄmݓ~pcx~57 h {M^MtFmD &JuGeX7o*Վ~b)Pq抈N3\;  1^[Z˲zDw,ʛn@_^ ; K^8jbi ͸.C3YFHUe"!27(,[fN4c zA[WlWR_7.J`'=W>93 oM:~#YuƋZņ|f;q Cx.؊I-6WՋ*Vt1i6K\BEB4F65I:͖x-ΎQOReT3}gM?U+3Z,GZ`r.xS!PZmE;B-Z)v^\֯ӟM'wW:g&V1MnFA5jLH)4w#1SiAW%R*:U(\ G=vW;ǚ7Q[k=.3y3s 3)>K þp{pg}pDz YfE$!rLp88Rj!6Pz̓EkM *%0-Q|-5t}NEqEA%Bd2U,\gt>o,beԚ现c4]Vs@i\  ֪|EV=!33E[fidm׃ƻ *_{cŮB#fRB ey#v`JD۫7O_qzQZ&* nigQs : `f^+żxY*_d];!UD%O]Pm ?rm% YĀl6Ęɖ'd&m"3w5Ϟqj&& # 5ZQ)=$Yb IDATv[bZ{gأ!%RI}IS W@9䐠8-32]qRdW(ˋrع{Dm,¶- 5FRHT j2W_O7g*:)+HHkM-MѲkp&Cׂ2 Ovw,OVخȲj t֗\ U6V`Zm\$ 'ER 躎%nEI]$DE 9\ޚNZ?G< vCkL)Nmx19cƛb&kL)4a1zB]?gY _|v7 /\^xUն/b=l 6U+yxzxdQC@Hf! ǣalicFjm;BH;J{*;!&$&Ֆh{՘lz:>0ϷY)UXmVkB" "J,E*)X#V.Nd#]j8t݊R ,fUgŎg :itu$Lf{k@gi:FNsRLs3B@5O NRd pY6 օ̝e YUKϳXN "So4㚸^B[CH`Ou !ׅH:b['כKǘ'G!ڐ:Vx$.&dDA"K2f+Mz_m(y!%kc-vMD ^%Kg- Ӟ7F-u\[2gDOqjD d ,8fPs.Xl5$5Ĥ}/%ZvN:@;H`QO\h{~b;)+虑..'ێHݩZZn k׍Xs T6g=o~J^"G q^MKXRe~]aToUg.زǔOhnon(=}PD:rILy JQLR0'拘}ʡ4E%s11Wqӟo3H"i._Ek˭pn"buPD(e|N|oc~?f xҊ:/! ]+j+B ʜ!R=n^ λDaز9$D unBY=BW_|=~ǧGhDwK]QK k', ࢞O#Xg 2N_[xǿc.^]!IlW Tŋvc-% :q?t X-vKM،4M'4et:*))n;c1[BBد'>y0`h1Y;0l\H,iZ*A!IBpvvAF-H:#WLSaG@sHE 5ǎ)t ƎpF<>`-Ӧp6KuGVxkjd;BaASEbhzOq,RFh5(vy,N{7o]-~ʼI|3/d^No`X1ae}o[߼#a8="0M{B[K ̓ˁzifйd˸J@ERaB,:g Ϗyyϟşsɧ5:bB5CHXe6yHdjÁܼ=YVly";L-,S ߆vc~w~h#,EJN!U/? d7&,xCs1~mm΂Tex|7GƧaٯ!_ mdToGNsP|m/WeXOg?Ç잟y~qx2.yG`f官IجAMew3s>-۫kfiE{@ԇ5WJqwLe`2""F=2pd=x󁇛52h1V-Ѳ`B&d"E퀚T[_)UA<%*D1tKIbT*)$;v%OH ~o͊Mlpy!X_Y1LBН^Y`Y85T=:.~mQlYTus6~XPWu Pl@%.i<3<մxhNfk`\*Od{͒Ta! Ɔ[#Ŏ)ҚqtSk><ﲨ4b O-beknJ?*3Kc''/Ou mWyHBr_?#B'TT#1xU~Nx9¶]Th˕5Z jP迆hsrQbZsvݱ$ys<[>[JJAYaю+TdaU>! bpr}}_|'Db!JgVy[Go8#2QNjAXx\kUx<|OwHXu(}3۟Z"]Lj=& 9dfCVlBE1 㞚'4L b!Ŏp&>s@o/qM&QH-FID9*b%t7LfC{]@M+7DJ.>+L fEl(טv}r#7pQs`5 Ų𢨪횢> f -%q4;xk!=pp^[1 >?p7rKdO\,?A  Z, 0C$3_~Ǧy9q{DAU^vΗ"i.Ikx ޶ h;JcM&=O;'$WR5הle%o.B --!YٷhXa;pv\I5Tfw~߼9#}efXL~=?p|~Bs!2銴ZHH6-G@U"JsvJDH& 'RZѭ/HgW\~}s랯[}=SůO=YqrB@Zw(hؐ $K U|+ 2 ʪ%S<=yx! 9A΃cL %3M٦]P "hi"m"{ɀƯ wxYtrlZVZ:MCFyNHc̊4OֺnAX2m=wc2ȡex/,6, K #?93B>׆Q Ѝ' o51pnCh%c3Nv!^%o \9_-G}s  {f$36MPqa1sۆ6$jsH:s6=xw<l[0 |;7n?F*Ch\T'$8Lgaoǧ'0]CRFQYYf2_*cUVێZ=ƶK $鈡CCv)]4-4E6 |O_#ןXwh!WcA&47]$D!y̨*vUș3 Sķ~lϮyuM!2 rq‰a kmo-2=_F34Ju-6Lxn[^HL~c,}Ѫ~ߪwD]y[fn݊lK-u4{ 3{vx{r5Uh% {sq͸-ecdgnewb]7[`V omWdaf=%LLS&'p??I+? BmsHÇ*d-{:7za(&#w<߱qqX'4XWVw DkS ЋR/vnBɾy$l7Va'qbҊJw.V p4_#Za_K~ss֫n{`Qm @Ѷ0T: K #g#LS!<ҴL5ǂYO{HJߙC5-ZT\22cDk6"-*7'߾z Id1+JMvmԳ7'VV=ΆuXO/?R!Tvc6qY^:"4i겳 ޵(88(sHH[]v_;^F6YK%ui~`n-\ !p`^0N.x^X,uFZkrӺga@ p2`F>KpyS]H{stj4Ak?7 <BRu0'-gg+^f}bcLe]GY*"EX"IF͛RhL/HT.i`I}vVE23<(X]QGDWO/5w !쏓uo*GzR2%#h." dو㍫g:vFc6'HtavXivvE=g[2S =4hG8qk}sYB|W)ボĻ<cH Ҿr>_eaq % S^^nXIۦcμ8ݒؖ;x@-07Oykү z[|u 'A@jY<=a̰ߑ@L"L2TujP;jW%Bɕ<*9WI#`HJrzriE!},8eR|4}d4(r~AK[y";Hu}gP8'v MY(B:\I7PR#Zsubʅͺ#wVH 0 Jq23PW>#BAy8cW^4k|x|+_FP u\ 1nj}7ov{A:2 H|lOnݬ^kUZ[*`hSA9J9Dw>GÝc3I3b2M 1lH)z{ǶZg7,#&Ĵ xȣ44V\wkp,{-PӅm{쓔ﶉ8h,ѴoN~/HV%ͬ}3*SJfUvynyp% 7ZQCrOqd.U(-7︿e<H¤1gF4V@U&eGрfa򬑎Ke*JdfWB2ݔdB" ÞawZ1{1 I=8"ƂMǁ\&)[Ctɳ`rAr<10JZAeǧE+],`ϏܾJdzTd_Oj_:J'MnqݭI'rY;'|JX^];3$ʠ!:Y+Xſe^qvvή9{tvAXoА@l`ɑ;?h (7<~ w<0L41a4N 8P';R EKa7 #= blK^%`)V)|gn ѝ@eYJ{aw9 L%Z (xZKp'H(F*Z469ĉf9t;Dw}e?})^)u[8?2h^QOjZUCH~ wӞ1Րhyzf#UJiִXY\f{!u:_䊫`?9\*d\Jr^㲂t[B0m&"2y1떋vSS+h2 Jd" {7w\\9`s~v>$,Z9q)uMn[~G?gvwIo,o#Yh4sgR`55לot+”Z*U|拟o{{]ב MQ:KkģM & f0rSf ALk7v Fƪe`RYBkd#bQՋP4UQf[|)V̵7JNq--PO?;a-,eA?M[ X HsG6&~4zͼ"One .:; j-liyjű=Ebp@ц 7?,d-V,TaXdՂg ԂTGiuq;U q̌ϏO. )%>Q<`8pCBZ;wyoETB+ bUV\Jy񉋋K.|%|l 9(HjϓQ k>ɟ$[ LHZbQf"-poܮBBet \XZxdKPvA=<@cT;R(v߄hf'an:ŠYUS?]JvʼnA >&dv@L 7f~”OwbX~gf߃/{ƛ/\~f@|ª%оcfN16it#0nh+`7ת춒}.. K\ROm]K:v}Ƣ &ݘ%ܪrd{m 1 :*}D%`'*+c. eb(&QƢh hRET(BTIՆD{c*YK*^|aB`nqjdu__ə^*{#]0SH ggcyqrh``}(4McM]wPX(d:rN.EsoLw#}bO_\gk4#U'H=6SB]s~Ox/2dTGXt>=eR/nMz3...8b=3R2x8H@B9{կy{}ڒR4G(IV$v>Kͨ&  ֿ &,Zd6Y#FB9" ^ଷI 0v`ݰb,0'.`{SGiqQf_䥏=d1>~)b_=#Ʉ&AʉlBt;y >ͨƗUFhR 2#ChMV+BybEq"ǮVb+I4Z40Jjs1bPP  (Bī I!^g4FRݱiR3#ԉ uUjfY~f89 wNeaԲ4/B;.S_dRm|*1Z,V@3ϚЍZ,b҄®GnnyoyLw2IF!电PJڸ`(~u'M-=fW^s~~f}f-Pꄐz$L}W_w7ՊaqI%5DbLb硪x`hfhRޘ &&L<֊Ox7?P5@QMck3'w.O̺1p-ȼ5:0N%8o [?pY|*x[m0T׺'"S٭LvBrj hii.-8h ?Շ(S4жPsSƎCTTy4 S(LԃPRB@R&kN U &|S®b5*RGVe܃)L"jl{5gaiP|1uIlQUkf7{ n+ ,z^i7mk| ,ʷ,u_#u-GW 8y;㞟?`ꚴYúc*9;!%-h@ٔ ,@GlYm9₳K~cK1үL͊N37_k対W Mt -w[/9&5{g]]C;I ڀ'Q-LnmSd14 =I <,x{E33w6.'@{loQ~Ǐ$,`=K$MƷ;$2kޣoٴ.'_VNt-'XxU\YWk˜Ƕ}]Hb]u0b:-"7KNBT}Nލ`Ba @u-^uov()b;ѵ0+`,7lҀ Q+yr|Y*A{S_#3mM|wb@כ ,yXZv\*6 % %Ȳ;hذ ɤٷ8˛lrn`qaE6amÏ -tJdX,oM˖9Cz>xp \KfoDgw؄d4%F aBRibqrQ-R)͹Z:lV"s6¦?DLv]m Zh6F0VVXXvf Zm2a XPҪgݲ9;ngoRNLmh|#iBXJ:M T7"6Qѷ7~Ґ|aJ"=Hd W@zYK+nno_ӿ%Wz֬bgWl{8qd4׶ۂ'65ׯ^o6Ĕ,O j=偒oy_5 wߎ$F |$yϱ!j"{\HPb\o!d{\ڜŹmE@GaT_8Aa8RPgp5މ)5pÙpx'&4;ĶGnxf0ߒ5ZΒd4T=NlB*5|D{|CjZesJTb1mLT+!]kc) zHk lMOs[Ђu;e&tTq(!%bJ5ۋ gWg3a@=(>UKrlvkʰdZϼ#ۊhTB]tV<+ةE ~ ρDU*7ХĦI"søoml_q 'o8{fC@EʀRZuѨKXbND+}wr =|>z977\xqX3w~@ɬv:< h9\]]# ԊgZ|(B͕sA:MvMGYtי] /EGӘ,Wʾn;;N`r s1L:lz zfO7~|~  m>~Tn֦g,Yd>Ɩh@Vc1-"RRKӴ9yKuقwp֭;dfb҉zn>1\Tt%~"@jM/t@hZףf޲JSnVЉ %ח\f-y\qʶqm;̡FmJ򶋸sZBL'8K` ڧ )E4;Es tNb$blr anR!F˯R9A'u$J"Egn}3=r*-WK7pɧt/6PVu"razf:>svvrxep3{?7=8on_1 #挲-PJ|x6 U#~O>䭲|MْKuT ".T?jY x:qt`9dM. v{WZnFc%dJOX 'li/v/0'ʼ{8S>uИx+$?j<}`"'&m~9`4|PukYlf%fBBЯ7s H~}"J敶.X׵zXi~5Թ1D=OR >;N՛x\b,s d} m,. ؜˚'b@-6$JLj;B/ j~vi, >`-oB$Z)AQ?w.oxw!<>Yml/In>[.?yMI+^Z@ S9R)?1 ᙻAyxxp'<cuvAL:j afH2AmaeDX[[[]_b>gtʮՁ. #eYWnչ[kEs)-*#q(p:wTs߳,Df.Je>P-=)>ȯKM=4Z>a5s.<, +v|17_4/4l^~~uϾKilӘc_cZQMf 1V+UU?Yba~K6ch|7}=٢!U-cCL@5`H`ќbRI7i'%[^toe tg&:ȉΓCA ȊGv f b56P(4<*\]_ryuj%Yoi%K%k݉Wj}q%7i"aڽzh; m!jdI0ؐ)Q^r6X}tZdnP\*I!(XQv-ԢXf `*" @@.k-ԘCI /yrpղVXXC dPү:{IdILT="2rrX>7A Ă윙WUfFAT=2=}TU^11QQQYI|,SE=?ຮrcVXD'@{,}d#RMy;:h!}xX;n;Cbd87o 5ٵ:zf.)5~7oW{|O`nbVzG N!hG>{݌;* ^|z)f7J.I 2vIZUʠ;V2|⎍PW6{R%^XGx~lhsWzRIǢQp(p2|~q#JC3m-Xue!pƖpd.Q<@*1kjy!X*J>搇_8MorvESQx:|>p8P?4 Z酝Q U{_vliGC.! ͢؊=sJ$,k>LaN_*B`0U.Oe!w)ݬk`2h:o!f񬇖03͋X ,+F]poOC\{–Q[7cZIF i/(*_BQ(*|\ 0F*}%l}{V\xhᣖ!"P#dƅ2cV@Pc,u+dl@@2lIv~϶ F{3Lgs_;?o #SlsT:"1PuQ 9"+p0=| xˋ͂)Di( s ;h}eagbmr`>mjΡ:z$r:b9@;NwVt-5dug!7Pi퀙J1MSDֿ]#8.d{Tw J)Ϗq&,JhD}$EqciS"ڮj̏ۊ4C8Nb]?;n(V}A/wK~9;ˊ~hSAU)4SkXԞ < eA_<Oᅤ[@ ҋ4 6]p/El eE@ ĉ0'A?Co?BF\ZI>*ЎWA <(RI&q"5`NN>yۑ7g Zo u_M}ET+!P" @|N@2^dKi[(yq+X/V anO, * { y>BdSXwd6Y[ptt`)3tN[;`)9;h`rݺEK*fh3(bMK4MXn8Q,KF<=@_?O__=Kb+Zzm! J&I-na4g\>y`$b;@mQ0kŚᦣoAR\Bƺuݞ{ PcՉz >~ l黓1)dY()GVIF`"Ԓɨ3؟6AYrVhgRzBê<Qn#Rֻ8VXzxm Y(B{XҹyCB顃;u4yB!K=~q}oZŵ-hĝ hlHNfR+;}-D}xkQe>3J29Z:y @+E h Ut>s|}FQ؉4p}@@K x: Oڱp)%N |?kؗ/ ?^xP󫠔3I~2Z3S OGPihD0/  ',&'rcQ?3O• nXBU)O0w^~{O}xu} 7Jڿe.}*Юl: &ie+Z5kUy) ?ߏm" 64]ʪ3cEV@I܀!3s4I #pTԿKtPXc 6(.-=(Nmo[-S4PU.WW=`:f]Z ?J )CyS(9SvKJto*rBZ8S:Z\-QpJN&|jF4J 8czڠ60c+./\Pc~ +z͛a) G!LJ $ׇ.Mգ(i9nQcv%T)~xv|X z(SnL^Yvv8&@ sV7n[?z⫿% 3 !2$|80+Hz9/n}{.[ {YgNur]S!(S*3ָ`X aA씴,|t1Oockx7~v,譣6T1|͗8=o헊0]:%u.|,lbx^E+W<͏N(kzYǿ'.:)Gθ<OkEeGQd?==c-JP:} yLi5`0O zS&`fQ8/0n*)<,8 Nx\?w?~h lʑýL]ަ#[~kjPe+uV[B&} hM頳BK btd~F`Qg(#:ڹpGGWCmnu_#`켵-ҒЁb'; u]Kp\;pҸM@ !i]:p]ь +#dGL]F;rD s!'ӏiQJŻߡ-о;L37)p( )wZ`2 tGM= ƩXKy%6lM<{>j.ooƝ#H$QݿD"QpF kx;N!WE>~?[GX=N³N{m }omy!48( SdTmS^:.VܙjeѪU.X+֥ ]V,mkb)X0ϸ=_Зo-u<]j>}&&wjXn!4we۬D ;-u\kyZQJY 5wG/6# Gys:! b=tgRD1r8PU. ju9;Y`s;ѥG;Gs;. m;G `Oq8`XlA  k ݗätc{#wأ{>r~Lpd"qsdl qljeI{I﹒ω`lfpS2u7Πdl^ԝs*7iPӸ0+ly9-ؔeEmG^3iK1 R lPXYE3(-% "KИȲpWQaWهgp.̎3x{fLdf9-m[7+9 |;{N'ǢcK$7ۭD !E([J:+ Zv;[K.@>x)YuS6T]vy6?g=ZCmtr'زEkE=؞;E] )w4"OM#s3&(ڣ8Y#4Sܱ8M,KI:X,^w[qh .@ydEoըݲjɺ(G"lMfuº m KMZ wmt…rQ +2-<=3|w;Ԝ_iӯ;YFdȴ?6}?AtE$Y6gngDO %=3-1~#Iqxߘu)[12Y;?khhLV^;ǘ& ]Pĺ_BA@9A|).j(PD9})EW|ͷn2C8~즪)\0'83jYuA-5o4a^P~G19(c1bLORDikmhkC.`feYW#hX +RXQOx=J1#QUуq ,u%H G, wPޣ?=qE-Q+(pW捀S8i]J].5֖`8&p^cI~}b=r~Z5@9t+%wqRـu;N2X"I%8Xuo;7NHpjQ 㗭AcICv!&Y`#/[ۜLɀ46{ U;VȰq c0QUwLQqf%35f jE-wh>(s|:"MAc##^>AJs0k^ZDuZx8v\_.h+^*Y5,%WXN8|<=`*lYZ;t*TeEu.YW|qxo_z;x@.M)Bjwo%,#T Pܚ+L }?9:b&%w6/]3NJLRPFȚ$p<_q\Pn T1_bW,/7LVV a1d?A~Lt3K%H!6!qdn`5>olf bT;w86sIåmDzY^:#<;!up,U@g},91nHLm ^*,$ۈtzh27$2{i,9,-ߜox-aD+['=*" q,#-ː `U<0N8Nʮ;mz1~t,.afEu498 Áq8G{ =./6X;.m.\`<(3~} PB}s4vEAp8]|kvߺ6d=ڼ;Bwm0)5KUE1M!AԂOϘ3j3hhLl-:(D p>_l}qPc0b[IQxkEUC-dUC&pz/+>Ȩm仳cnFq4wV:vŨ4ĸhŗ ;7Zd8\.~ׂ:oS3#qQ@xȂ;ϵ.-,{ga]Hي"Pp<͘:=x3ư 7JH i.JfɲwO!?PDA 7gjLfI Desw :nh^ 1O']:"T`X ڹ9t#.IɡP6m߬WR :(0Y9]%ٺuaH \R0fY{(Cx6-5P'~>{ m]q }es=Sp|xg8~vr#"bg.wZ8IV2Ծm V+w-E@MQhKX5ÌU#', *(V Q aR\o(]kwEPoo0g| 7k3h9-Ώocks+Nб.pp$d0#e~Mg^O[[ VQe߀*9 ϟ3{2৭af'g};\G)=~1KO:e|klrl'yn=A4q8=}U9]u)=4c>{VH-~x8ҩz,"x Oow8btS̠ }K4 1۲`Y; aQU4hFQ4t#2Å;j%^޻hAߜF:cm'Z\1MqFI﮲at Rٛ+` u ;IJD/>0\34är94Ot)qi~|.ML7=tzU~h hVLzӇ\`Ƿa>1p2 B6T@ݔQ@v=A5:{0`>0OhKa7ܳl8}~n@B:Ab9LV3$Sܑh4vL8'x.=_5> vd, C=01OKXcC &qJv ;޸I%3MRo,Z;6 ϊb6A[< ?@)#oDG-l1e7ZXEJJt21zבhW X/úR iM`B=8†Q֊Goݺ.RY&Q|Mb? 2Zo;jeԑ0H| aVQh8Ot}afnvq$-ǽ.yǽ>O2ap/;dWZ'7pфJSnn.i8c8BB [$oVpcTdP)ɰ5g%7pcQEH (S$8&P%ȘqW2,5"5M0GRĠa#3&$s(>EQtybZg n+$DY T+*rY`ZiӋF*"d&Z'BJ~,vr p`\Z4^%uKIE$xMpw$#2P+J/34#v}v yjX޹@Xåx4QaNunI;rw ul̷Dw,op'-:Bʄg|.O/8·V?"m{}:,N۟{)&g?{`]B<]S#5h^8bY#%A ŝگY0{5  mIl:Lkh%ʒR L91=ߣFq+QvQeɚ%S`-@-,Pj JYowj(LחsqӑM>ӌ]OeH4%/@p<ME!)yMz[yL &u4WtTQ44xD)B߹Xr((ӑ]EYV*ep8e2tJHsCw൓k ~:;c`8t8%hk g\1M0qx͸-ƟikLJlS.lKtpF`;c3`sQe~z?~ctH8KK\=P)syMkrz$mܐ7ZQ&u=yh w_rmC(J.t@k)zhÁB=?ZșB ],n;EW)QK(h/7L V ص7Mܾ i,\Cɠ7\^O0,ߥd4xuϹw=w2B^LTB3}hWox S`{`J-: a®A\4}2<p\`g\6͙ݍ> Ȥ\[\rAi1b([v/w @ }=t_$vV)h?3?7$ZÕ  ʑ7 Qp$q,{4K {J$n2n ;MƸ{Ԣ( j #w3؜ՂeеOyL. Rw9: F]?*le,dP~)P8|Y=u#;3Zh,J X:@ܥŊ`MqPpjzŹpX f{l`[sChs,@hRN10QTPDTE ,k#mxC͢*g&ayT_53IHa"_^/ !Dž!g@1*!O t^v }_d1} ;F}WhK#w`E~7wSxw0Uopn175 {0ӴL/>K>{g],5U bQءúz mqfRְ{wZd`b!8Yw=mX AN9 OPk#G/ JZ7M(ҎRPQGD. K C@RLQ#Ҡƈ5 A! b]חsVzұ,+Zs-Ei2w@V@Cde,y=! Ld6akjym̜qYױsa02w縷=K!=)h?S>!DEr!â;$C!t` >8ʇMthtC'τAD!, D Q1Z"= %< _fR'lm )ޝrQS%>۾I,2{ƶ+?=SwD)K9p{dkҹӾB++GBi}DĢ:p{-.h6q^@6eќΏNc$>[~nU^2FF]1ˣd>Aa9Z9<=-0R2wEM 1Eo(bTSjs,200^`ָ"`}T-r0{hyjk\y 1=0]@5.,yshzQL}cJ36Ht4[ @,~]ٰ= -0cᶸe$Z5Y7RKi%=~.{'goH#tIs!ك ~.@=/_pDGvH)\3pc ofS+7W2H5|G^F䴂99=ٱ.[rNڻMX K?u= bdɧBPP>7.5P|>厂^`4AW T=BMFƤ mR"`œ/ 4dd'hkC-Ѱ5G"cEpL$، &B2+BGqjCf4Phlr`:pJyx'`&Ѱ )5!;gsbА*Ӷ+3 L?v.{%6 nC4`5a!SУs5]9 رVl0&P;6$\ŢM%wC <^c,;A]brO<_+I{%<~1 4DMj}`Z~me-:|}J59B--dhO`Q`ۼKJi VX2udɦ1U}(Á%c0\{y=x~SVגw~I_p؝(R`H׽ SMƎ zg}3l "=[h-Za9L1U]$tt B %+v3{3 {`jO: @lg8)hxRG4_=F7{ ؝g/ IDAT T8zWDd#  :(J$,G_0p9jo+ 8A wlk{:S-[L; ^w:}A\c` {}zxXDDARBo#ܩ!$D$r)aIxR-ۄtN.HQ/KyEw>igv1kD{+;ZdQA{D<P-%MmK-,rcG鼐?= Ya7-8,:fQuZ"5Y3G.y<8t#&Fiq~Ev$"}9.ȦEx ēaN~ y?l:Ș:3@7~ʳ9;$p Qx/{wW\ȸ+r9G>޿\~ .Z~BFꡑ*smQ(ާ"ds+ &4UtHݑj]6ȓrU Úl:pǔ i/2"f\+)3X؊HNpϤ7 u^_| wճ?U7 M;3M@IM>,IG3Y9M3Щ)J#P=g$6![}G$#MjB踊QcQ̰DG_(@< u،LM65l: ##a1ry``E,oȾZw.e p] V besQFx:m7pA2H΃8?#lOuapJh@2 mw'l\<8A±+^&I*;ֺ{Nd {pe݂_w -_ pcӽ5G o:x2Iȩ޷ = LJPy(EOD|0mL#i Vzg c<5{◀Plc=$d"hEÔ:m R`wQ{w6VD,@DX+g2$A3*qRe`Q%w&q݆ۃ8eֈM*c؁Q)rE 8䉼ޅ e89گ;sjVa5WM{x׌zbp7 M:aQq7`!ԇZlM6E.4y*p#SՂs0nwdC/΋Q4C' 0zKR c|~ ,",jJ;X6gO>3rĜPRU[HDh"-|& 歄V?,-ȖpPqY!mYw>$v?lݭq.u^޹LEYd}ǿ? ^KyU7)dз}gX(Ec.v>GCgγ*wA`q' '(zo#4B`!yp(C̘F;kz4Q HisQJe4&B2jp%敻эlPMo +m,8;޶K@I/\O>>@>O^_;h|:qkd0^><$3 %jB|z>KpZ3 j ͝oSǍ7vC .mRH !i gIxO^B7pThѕȦ"*j1Qu';J M7|fUңU<lS7bctfjnS k5Ar초C>vM+~ƶsmZmeT79"(4c﷎cGoKE,cL𮋓|giU'^;]6pUD)[ٷ@oim]Gej-Nؐ-6AY|\5?D"B'O5NPGL0?VcB%JBOPo"<|_4_.й'f0qF&bZ<ll{roÓPYyK7߀jbo23x.d̥uok6e27E0no Aol칀1h% fV7WYrq_V=OWf6p/ .~36 Bi396OpeOWt#s`FM'cC1'dm'` sT#ݭi;A͍LbG7 %|շ5A $Hm8`0l#?^8|K|׉psS vM=K !_bjnVwza"S6T'`O,=sr /tceWF 1 Lz^мͲ߷ 7o Il.^J4]MR/&7ec;N=Y^jt ӃM}%͍>h G` =Y-<⑊5q6OF44<>=63pp5mԌ0rzEئȓtƮS󪨁c<sɉ*)0w?_tA?v6= H`8j:$ peteu q ٬I2޹Rqe5\#0dzL pڂti,l;v}4HѿBݝ1HB5g^ÓbQ獅RL22.u8`E%٨;f^ ngKte{Tïn{E ¦tidZhHy:uH9f"E0\"Uznzǩ/A"ko'Dg- @iubyOLZT9ΎSn ަbfw!"&e\D  8OrIIqf6p* Wrz-Y`Hwo2tSgsi" ) 7̩b{ ? |l]|ZYoY䯘(i2/e]T^u@: m1i"=(p1Vpgnp ȩ S>1Q{zLJPˁ}D[v#7f1l'vX(Na&0:y gTiaR$7 yZq>2etluc ?6p=F"U&=ۜd.}KjP(E#') ()Vr2 J,- #מ|hb˷Ja|k\"D_fHa ydv:o&iJQMw3}/RGgucepON& ~q@xx6Ϻ\jU,u.lYw:z=5q zHg:b,pރ6z2*S4<n.LS'q&Hr{p2Qg ][)d7/^V[8hw빋-S/6`یZe8=nZ, U ֞4C< % m̑L8?T3GiMFa#B"O{wI &iF*jOqFmO0syҥ4ݣ$mI pxj<i=NL6X*"}ĽV@G-32o U'M:G^+[ܨ-On`iaԭYo]LF_l2kRZwe۔?z(3)$tq˴[!FS jLNk)?(!vBO~F+PVqNΘ,a%3_?1虭{io[* o%]jo*d_ FgMm''=oү{-f.xPb.Ä1o56l~}NcOx,4 JXuhWXְ[xԫ]u*llOjp$`_?GV&YaT`Fɱ1QL@jr'N`c%zBZn{bԋ2aq{jƷ/QxB̄R"kayXDظRo+,Y IwO;?P$lv!lOno*cLweX湹 CǕ}HR}ru(8 $W2tvX.x@0y#sCcx*d]OgeL9^6٠&Oyaq0W,8[ݺֻͮ^lf᳔oJZ&{@ЫY '(9#?~Sk/;UijY(sz'`N})$ԅq#+:*N23g$k`b?}W9? Xt ܟ/*Mא2dшL.YR@޹rn5mm۶m`m۶m۶m`{۶m>m޶m۶fm۶m۶m`{۶m>m޶m۶fm۶&`T~IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/diag_barres.png0000644000175000017500000010132412014170666024701 0ustar georgeskgeorgeskPNG  IHDRXA$5FE pHYs  tIME+&?tEXtCommentCreated with The GIMPd%n IDATxg\TG0kQ"XP!`Eņ%HԈ%j4C[lHD XPQPQTJﰰ.ag^5Vs~8eΜ9^9sfh/?b9)Z-^ B]VX9'_׭[~z.B;L塡%%%iiiEa#BLv^zYXX\zk!п"Rh"3yǏ@ZWW׭͏ze˖@[[P($CCC;z.]m۶tٳg_/B`v9#""L fffӧO?|[)--so֣G{!dG! ǎИ7ox{{w?~|xfwEu@(HnݺEQgKK 8zK.EDD,\q.$$$'''&&fɷoӧӧO fccsΝleeecccVQQq޽jqƺ͛7mmm ޽[VVfdd4n8[n)((|{{{gggSPPMMX[[@nnSLfYY͛7|j755%&&khhHRPSSSTT?C yyy>|Դ8::2'NXXXdggy{{kii@JJǏe2ӧ#''boo;w())544|yyyMMM...׮]+))ɡ(㵫(ӧ=RPPpssjmm? rss'L@ &۷oK$իWnřwD\111FFF"N䤦x<}}@\\\aa!ɴwrrbX3glnnNJJWSS4hP^O!w}[ՉJJJWQ C[[[AAa)))l6ݻwUTT,((ommmhh+++SUU;u>ݻ\.WII)$$b)+++++kkkg***111R(++SRRyf\\kBjjիW9Nuuٳgx<ޮ]222;!JJJ C]]]CCɓ'nllr򆆆ӧOx.[[[+{F9ϩ9%%/^xyy B$͛7zTzzٳɲ]]]T*(j̙yyyrڴiO>ݰaCmmT*MOO2e EQ>>>e˖%%%-^F"dffN4˗_pA(m޼9**J,6^& N:uĉֶC;v_|Q,vohh ۿy2ɉ۷8qB(>}>}K$QFu?Ǐ555dǶ_ŋŅ~oiiB}֨˛ E׍V:]~hĉ ܸh---mmmt:h4L&{}}Ύlf(H]]]Fq8߷o߁!V~)00pǎEHR555IbH˗/_tիW<lWSSc \./ &muuŋ޽[^^^YY9i$rTcYYN֮%Ǵ"CH&:gҰ#-<]]]y{Ϩ</66͛eee555$bX Ci4DDDf< ^^^˗/7 !eggl2[[[TK b~eejZZIr̪HGJ6=}k?FzUZZjbbښb``aÆӧ+++X,066>w\qq_YY12 == !Y(_I L0loiiH$YC!d2>TTTDܻw/55\B\\\NNΟ~Lf[[D"a2ݻwnny">=Y̙A?~%+++Wz)&EEE}}*}}}W\\ܯ_0p1=ݸqƍ:u׮]1b?F[>@ڵ .0 &innnff֣G={hjjCϞ={ %%r а_~om}Ι3'""b:2tׯ۷O,+**zzz={hAnrk߾}4MQQc d2}yIH%---{ f###sY,--#""v)W_:uNd2__ߎ1]~=99FXs㎠/^ro6=崴<==|D"ׯmRRұcTTTrss>|x=r`ȑЕ+W7mڤtҥ+V5?0|w}9}}'Z~sޝ4ZVVV@@@ll_hC -++c2&&&eSSSK&b~稨f]]v+++ ּ:KKK--l===  ssskjjLiϞ=&??_ уfeeUTTtsss׾MMM/^ FFFnEc5`ceeKlllN8!tuu{pBa~~~ee*ݻwIMM۷/nnnUUU\"ddd׫rܪKKKhllӧk)-F322233d/^ 7')z_;={TQQhoo'( +/_2ۣ^.6OڵkϜ9q޽5kL6ɓ'.]7o$JJJ---d5##COcce\\{nJQԂ /^Ҳlٲ>}ۯ]~׮]BS,;wp޽-[Sxzz Ÿ~~Ȑ!<O~EagnLIII ѣj#|܅PejjknnNFX`Azz:7tǎdյ`@"7ƍ***;vϟ?t|<Çvjll ;z(ܹsGGG'000**<'spphoo?㯭 >>>w!vg [lv&1_?+u;~~~7o|_&$Yo߾}B[[999SN%:~qBCQQQYv{&vpp "}-&&;((hѢEcǎ ڹs'ϗo_1"Hq?4e+++2Ӌ/LMMGEV &?$44I>z0vڣG:99\l:uӓ'OHXYY 4_WW⢧GVRW[[ɓG555o{|FB{d4M>x2Y20hEU`2UWŋwJ'cHN5kƍmllt:NJ$9L&c0ɀ@CXOMut L"ﵷl F{Ǫ\PP@ @bVZiӦ3gΰl21:Buwd|mG;&S]]!()))))X, |!ؿCCçF]Bs% E"ñ*:T*dw"#Bz9{l'k#BBhVVTJ$R !yʘL'[Z@`jjyTL*Dp|!>2'(!!̙3Ia!>":Z(_USU 9[!;u߾}CQԍ7\]]]]] 22N:֭[֭x9 |֭S.X:a„dWW7n@NNIŘ5k-[`ʕ$ )Ç]]]GAVGR*8~xbbY?gϞ={V*xY}'R_x +7|l"P0aÇ<E9;;յ?}IhѢ==ȹsnݺ3̝;w$&&.\P__Μ9Ooŋ`___KK˫W|2""B__s!]xڵky<ިQ__7d̙wNHH >\zjϞ=[s?^YY922ٳg`]!Bغuk@@@[[[aaի@d2/^pu֝9sf_yA!>@Hb!Nw쏋Bu-}!BB!B!!@Ba D!0"BBM۶mdm۶& !olݺUn܌i'Br!&~KƏObĉ-[r=<<(JNN'N899]zky戈>}mۂ g.--={x{{]p?$舁!>h{\tMLL8NII[|ѣG`޼yΝۻw˗|~yy99\$EGG;;;1o߾Ǐ=z,Y?&zjViiiB[!yo߾cǎsEFF@UUթS`СZZZz '-Ȏh"JMMMCC1co#F0*@`6W^E{G-ZhРA"DEE@PPPqq+0b DOƨQȲ…  gbٲedWFFF?N?~ԨQcƌXBuajdd$155%]]\.F|2())13fo*++;w,dGAA!""(̮[BT*$ EQbN3LB賤qUII,7v\f0W;q m R4..ѣGuuu;wd0˖-366!i4uyy9իp8X!Ndgg߹sϏf8 BG $BE!>Ba D!0"BB! !BB!B!!ЇEQW^upp}6sppصk;H$2 d2T*H$'N1c|8;wfwSWW%GD7n;655IGÇ$T*%ݽ5440" +W$;NZ[[Is=22իW"b;@ <<99D>@ ϜVH^]]$HHJPH^kkkccSQ@lٲ+W\v277744$eobX=zh$SBI@CCrŊ$ ]v bcc}}}Nٳg3r$G]),\w7h IDATre GGǴZsss>djjj``PPPRQMMMӦM?޳g899ijj2<occ3n87oQX,BBpƏO0Cn9… ɂcǒmmmgϞ AAA"hɒ%!!!?aX ڽ{7Xzrrr}}}}}}wܼphRMMMSSS3k,y&FFF/>{,/'OruȐ!L&W~-_|E~ӈD"DwEFFlyJq`p)nl޵1 %=tRO_ZZP(8p`bݻ۫h4A!Ե:Mb֯_r޽{7%%cg'Bt3B6f g!B! !BB!B! RMMMߧѺGҒ`ǁ@:[II ) w@a D=z:q6B+TJ>R)Fd2{[nOK.h4ڔ)S777\ɓS-_ 4EZIPhlld2dPVV>}zgfʕd6;n|,srr:/JEB===UUռ\\o.))=zÇuuuKJJcbb_]] }߿ll,ggg[[[@ C577oll:t(9Keeehh<(/OBBBYY)iii&LR2k֬:2+[tuu===޽K!C ""B(999.22R ڎ9RWW㑽\.yyy1"P7fmmcǎOr8ʶYf>{ɓ'L&D&O#GTTTtF۴i)7""bgϞUQQ'O?~\:WTTۂ{XyfO>q ؼysHHѣGN~MM͢pSSS==RTBIȼ}6* ?`0–/_.Jȑ#_}$%%ݾ}VWW:ujܹBۛ8qqrrINN>s̊+~'k׮YᆱPRR*//WWW ݻw|N@@@@IIɉ' 33sԩׯwaRSSSRR\bggs$;wmXXؓ'O._|9pWWǏMX,֥K ,, 7gΝ;gddfhh!>&&&$ucccYY@ w#kaar}IJ]]#LLLL|/f0@O{׮]+W~BBB'\۷zrʈSE͞=["D"hllTUU\a DN/@nn.XZZ_vbccHիWMMMVVVӧb >N9rO>````hhXWWӼ}ؘOn~",뜏ڵk~~~L>J$jb^綧:"$&tUQ?xؐazbk 55uR*SR`0ˬeeoΞ=kffF 6ʕ+/^ӧϤIBX,X,655USS4hD"KKK__SN͚5V&)((tƦ499N=jmm>|xϞ=ǎYYY͞=;44HGGӓP(tuup8T4M]]<Ԭ2eʈ#"""^|9p@// .<jW^}%KD"MKKKaaaQQҥK̤RY***b |\UU%H_I3YYY4LtT90ޠQHSXzMJJJK;CիWIno͟t]9C׫lSN_v٣ª+((888(++Ӵ?5Y(Jjw---O!U,,,oMMMc;#H۷o'$$4559r`ܹc (/2kpT*!BtF d2/׮]{ f 6аk! 5B?V,El6y!r86B! !BB_XC݉D"b bձ_BEEE7oHeaaad,D>۷o,[chڴid÷JKKrƍ?=zt5k֐k׮-\!BSYm"uIj|TUeEcӺfpA)H9 9"4ZZZ|́3&**ꭉ+++׬Y|r33漯^;w.w>>>|Ƕ}} :uҥK+**:VQMM k.*W&]tɞa%Ůh=aQQQ5j٘XVVC\~ɓ'>>>0rHeee2<7NMM0e6(ٳgN%gϞ(ёDAajj*;6##L{GM4)## cȑ ݻwTT&̬ի驯_YYy-9y@NNΙ3g`̙sĴ=G3@P7 N2]r*҅tvb666wܑPG)((@vv6lhhPVV>p]TTT_~]H \lYXXk޽7n|<;wnTTTd<2'baaٳg pƍN;997((HMM-77wܹ֭#wqO8!5558t_믿X,& 7n jii!Wlhh'O2!B}>|||}L0<4001c|҅Xspp(--mjjJMM۷1c!+3uTƸqΝo]|gϞ+W$F۹s'驭=qDÜ9sΝ;rr?֭[@UUȑ#k׮J={3g}ssŋ`Ϟ=/ ___CC/䣪:accc}}}By? }!̙cnnN 9~̙3Ԕ$8vXzzy<СCnjCͧ2C=ߴiC[|}=>t^zqP_HJ'&޺uo[*.ރ:DGoφ1 }pv᣶>?{!?[L&J2(T*J)oB}lllu;L&{ANNNEEEyyӧY,ք ttt BEWdjkk˙L&!!3fwvvv^^l6G!@t:hL&6B}(B! !BB!Bx1 !x V"3fxGb2K{!:EQJJJR˵=== !!.55UQQ\܁׮]xupwwJ@B4[[[}}[ngy F^^^KK ddd={={F|R" 4hݺu@(ɂH|w}GfGB5~_uɒ%dw:99$''9scbb\niiƠA<(J&2.GUUU%%%VVVPXX(H233ֶ022sΥh,f/ ~ݻw+((`#aYYYnnOVo4АF)++ڒB}V0 !P7UBa D![lyΞ={??+JMMݿ|ƍN@B'^x1k֬CvvD"-//www''}moFF;^oS655˷WTTtGO Pg(o߾w!у %!_~汱$ 8ȨP[[+ 7o1bDqqqjj*7fr8SSSy;~xLLLI.^HQT>}ɖ41bDvvId_B]]}YYYt:}ovtt#Ą%$$СCuuuH$[pss[d EGG@ɯ\",,,^v !Buo^ڲe ,Z_xaggLւ VXpBlٲÇ3 hkk۽{w\\^:>>ɓdp5kox7 RiFFիW${FEE>,?fϞ=NNN/^MKKc0[r,0Y=rMqAAA/^Jmmm8sLAAdffJR''' !PVV߿?pܶ6[[~Əokk ϟ?9r:>uukVVVJP\\@f $۔)Sƍ7|𔔔+VUWW=y4'O|ȑyܹo߾m۶u:p@rÆ ܹsbXQQȨBH$h4f21"!˵l6;88B{rqq!YB!h9D"'L`0;uuu>|ӻwش;wX[[?~G"HGGZ[[\nǎu͞4iUTTBDSSSrr2Y666NNNqtt$DbbbHINlYmkkrFFFÇ9sfoرcL&SUUttt*++tzKK IpHR#hoo'hjjr\KKKynt:յbUTTWl mmmy<ܿzNOOիJJJQQQէOBH84hЙ3g/^dɒ'NZ ;m4fԨQw&mA\<ݻ3N> ǎ#DMMyݻaÆ&/CCC/_.J̦MHc 6䣢2zh2p&,X`aa/޽6lfɮyYXXݻ&M:m4cccMMMɓO>ڴiSCC@^! feeyxx+**r8#Ա O.@Ok{:8t$FIX;x`ݝZܜ,hkk~ĺu!JCC!&&&Wl\f- ;wX`y|$=fEEEFD"#g4IlT*(J"PE"P7"J +**d2{rpp@?,--_z% B:~?BKkk:pĈgΜٵkP(ѱE[LȐd{w&1ťgϞvFUUU7o?ILL0aKV¿hΜ9377WAAZ__n-..n+2KKw͸g|]gBPWW2eJoܸqȑׯ/YއS H$*;5552Tuuuo| $k*ZZujj.LQ"\/LVԤw?ztѓ\YZIIo]S kjjVA*[] M=50]_PTTUUޟѣmll.\0iҤիWw?uwZ ())EQյAM. M}'"H*++Guҥ9zҥK1|҄BaǷMEhna9"##9ΛoRX,JRk&t+|.w` I g2{"Hϊ)| +nRUSSfɠĤoftEEſ!L&uVTT;vkõ*b PG%%%̜1cgV4n9k IDATaáC,'NHPQQ3f Y=vX߾}ܙ3#ill>!B֖y ++KEE/֭[LoRF[[[0rHPXZZ &&&˜:>~b2Q 55\MM=33f{{{'OtSNu瓩 @6s\[[[>߻woD`mmm``㊊ 33!CpׯcJIIɳgۛfw}||JKK>} s}k[EE/ +WBœ Z?v:y򤊊J[[?tn#ubPTTtwwH$L&㑐=tUV3f~mzzzee%[SSCQTtt4ϧ͛EhhhĐdfڷoڵkymvɎ% $紴yG\BQ޼y@ ذaìY>zɓ'zzzZZZt:]&͜9sڵ~~~p“'Oa>>>gΜ!r߾}l2vbR2>|𫯾222@>Et:?ciiikk{u__Gq\-b2ͤ4tP /R~]]݋/d2Y||Eƌ|e˖ 2zƌcee% SRRf͚bŊwy߿?i$HII166ꫯ… y-+Vx!!!!!!&Mի׶ma̘1^"O>gϞ 6l54iҼy󲲲vAO8x ӧG؁yFzjCu-nnnn~~~/_^.HȟP7իŋcii3leeC`0~'O lo۶r޼y111D"0#b ߹y^6B:+222===a4ǂ }_155}HMMaȒyUGGG~~p ! ''uodSPPػw yyyg޷o.fddcߑ Ν;gccs ~OзhѢ1&&FIII\\|XdfΜ9K50RQ@p1»F!XvmMMMnn.~ j x\>5{{6sǏ׿{‹-RTT @OOJ?ݐihhؘd2 h4ZIIԩSJ^|I i4ڋ/Ǝ;qD3x<^vvvII H8q2)))yҚ4i\h6`0L&e0 W ֭[L&3))ݻw)))MMM7o,,,d%cǎh֛7o^x&\hiiNJJ555 555iiiP̙3=<<444KE"ƏyeEEYzq޽:yׯ#GehhXYYYZZb ???'O:::Y̶Yf[O=[rkNRiiigΜ-BAAA!!!u<%u)AKKd...222 ^^^)))SL>`Gq 駟<<<-Zۛ?&Lذa~?|A7n\DDĵkB~~~qqqvieee``ӓj*TNN'@V:~8. &:u rYKK/::ɻwB/_~%b٦#0~]+?0ydjSiiٳg466***!$$$/333"XZZ*,,};ŋf򮮮|Ʈ3gΜELL4( wnۉ vpuu .GOCCtCCÇjoo/~ m @ rrr,XTGGO2,##gǎ+""ݍ2==999uuu___/$$3~ &L:ƦN555DNsϞ=/_>tP߮ 6Q|zzz8zqqq"TPPӸ?sl$HijӧO211Y`7 zVooowwwZZnb@(--=k,AAx` ͈:v옏_"##ك233ߡLJJ:u]~ȑpP__! ƪ.]AǏO"f~ ?8uꔯ/cƌqrr266vpp>|xڵ3f6ndZZZx\t ee宮.vll,nH$jŊzzz޽ .2eѣ$%%QT0o޼͛7755M>!_ FpܘDP]]o߾= [laF[[{ӦMxL&a~1B JBBB__O~o!B!@@ >t 8vp&Mrx(t@@  B!kCBIIIss3`Xё@E 55@st:Pa5sƍr SMasxXf$koo)uVCCË/mzΝA<[Dnnnmm-ѣG]]]srrB魭}7`0/...++߻wwtTVV~X zUuu5B)33ŋ ߿|ӧ~P~^\}X !LHHXfͿi*zܹ˗khhYYY9::~#~~~C´]zu%===KKKsuuEEDDtkky:tÝ[nr効yݣv>{,޾}[PP_~A}/WSSdّZZZ{W^hihhwĝ;wKa̙}j͚5)))W\JHH ϟIOO qLMMqKNNVTTܸq#B(11xر/_ܺu1cbcc?Z]ޯ@-իW~*uAXU]]}I[[[999???[[[)**^gΜ3fL`0o,]ʕ+˗ggg ~m6[[ۂ{YXXY&55ŋ9UVVVjji֭[w޽[[[m۶3ٳǏBx}QQɓVP|ݝYYY! ?c˖-zzz蹹l6իxFkooe2x6]XXDјL{555h4:z7o655׻<{utt3̇\. wUyy9bXx x r8\ 799ɩ{{{~ .:x`m۝>> .Ufgff<ܺuf777wvvvwwhի_x3f455]rرc&Mzu:::7l؀雫\l˗/.aqqqvnf5k֬ׯ_999Xv&9^t)Jŗl߾}z왏n ٱcǣG ;ƯWO>}4ǫU0 Zc?k,''GAAAJJJRR-\!TSS'%%z }}}8"QPPo߾G>~XHH;v鱴\nBwXgÞ&&&Ϟ=sssrsssrrٳgO]]ƍtzxxx{{Bhǎzzz...9997nٲ%&&&''G^^ߢrnnn!UVeffFEE!6o,!!?#wennnݺ+V;w.:::-- `ӧO_v !t̙s{>E^^?mkk{w!++]vܹeeeUUU&L"Ho޼xbUUyHH?)QQQoookkkoo'OD"BhӦMo޼ 0a?4yd~@ N:zh|:S뤥kjjٺu3r2={= V___=v옖nU |||T*BhΜ9&LPRRjjj?eddBCC6 ?zCNYYL&/\!͛ &Dbuuu[[mjj"\QF)++j KKK!pA-]TKKѣIII^^^!~_w $$$꧟~B&edd^~֖;fqqqahw ,pvvkmmC9r (*//kii 333O:ôÇWmڴiڴi$ѣGGݻw}޽-[ܺukƌ.\@-eeeMLL󓒒rrrp̚5k~7+++D7o$&&;uu }6cc㠠 |hΜ9JJJQQQwލy0Bܹs?lll|ĉǏO8Rnҿ`477]NNNccٳsN:^\\{շmaϞ='.' IDAT2p7KVV|Lߍ'N|ء})t?~<&ן\DDG$%%E&g̘19iӦ9::߿?000447t"##qNFFFxx8N믿0Ng׮]jjjolllpvv޴iӁ\]]Gہpk),,lʕSL 8p`ɒ%ݟ={|ӧO?ӷol8p@Psk۷oϜ9w׳lݻmmm>ͯtK?z떖;?.&&ƿaΝ;yyy ^||ӦM{rrG0ZWݕBg簉'zxxzNUUã߳d/?}ׯ?KII~4e{{{ gee}ikkkkkOx 666 ]r(33o\\\pp666 e޽xԔBǯY.++ J<}}pa.p=!Ÿr6O[Q8ݲf^mj>'կ8}|f} vPJs%j\r'? VR޽P?`܇eĨT*B;??¢&##!dooO"rrrB$ĤN#f̘A"rrrBw577wNё@ ,Zٳg߹sGGG!t^###}}7ovww8vؖdCCCPSSÇq ..***((rvv.//r555VVVx۷otMMM<`0nܸTWWooosBhԩ222ׯ_GYXXhhhźv?Wwޝ5kT5ށ游8?Ϟ=+""JJJ_?jiiPAAҥK݋apppbb޽{.\:tЂ .\NJJ.(( Ž%Klܸzyyo*++L8p`o";&,,|e++I&o D W^}|.?Ù3gl6BHOOȑ#}sd2B:::L!8X[[h85c vPrr2BHUU[RRΝ;a7}t~R(m۶!pkRRnUUUSԨeeebsss111wmm,333XSSs.f111kBCCq=zbX8yyy· D9rdٲe˗ѣG/^{nltt43pԨQ111.\h&LAuuuP_JJJ_z7q̙!!!!&m:$$$`0(w0e˖! d,^8>>!OMM=qӧOp,웫ǏST}}0WWWSSSg0-[~G !tر[HǏ#\n߽ruuug񽽆c ;;;;;ھ}{kki!Hu8]O(--ܹsӧ}.111ϝ;\q.]:իWF&Z(AJ}ZCC!WW'Nlڴi@?=իWPY aaa dFFF[EEEE\2@:oFh4ee;w%&&Μ9!jkki4i4366.,,lll|y~~~ee۷o %2}k׮!JKKBVVVG=y˗BBBd2.o狟7o^ggg^^^EEŒ%K򄄄VZj*!!! !iӦ<]]]{{{"2eaTTT^^^^^ބ 455ۇg=<<4449s&L #P:G;v۶m[IIχ@_:u V^`0H$..@kB!@@  Kx$ ihh:0iii4ΈwuBL&iXO=66(VtssI'yᖖ d=ڣGaŋ(0A!##caaI;N ɓ'%()Q #sDV aqh ~TKӈ:ՅhBF͙ٙ3g8d@2`0(j1tbq  ^1ӇǏHNۏ;vڒS>AAA^ Vս}!p+o$==Nggg G-**]222>g30t|rݻw57Dr[VVVPPbll|xmPss޽{BѮ!!!IIIquuuuuųΝsuuNHiiŋǍ7nܸ-[ <lڴJ;wWWX/!!!\.!tܮ.Çe˖Yܠ)))%%%4 O?-..g+\tx'O \*J#mnn...NLL{1o޼:<{ׯ;;;^r_G555.gY,֍7ƏOPMfO:ehhHPl6Yf Bzɒ% % Q(Lٜ wvv*))-[aӦM7nܠR^^^[lILL455*$$͛]J{xx~ vww-===_~ի⮮.Ǐ_|/dXx/ooׯ_,YRXX8qD3gnjCPI<axVMMݻwcƌٻwRRRT*B޽{%%%`9soܸӳhٲe˗/ ={v``vޝfpBRRRzzz!yy3g jkkCBBpݻwJJJ yxpʕxzբ86&؈gBYYY-.]ۗASSSOO݁ PEEźuNH$<[WWsEܱK,Yv-?)yyy==Pkk+N@ (**Z[h_Yf͚uA|qCBBBwQSSr K,tBcÆ >L2EDDDUUkaaիDbwwSRR,YOSS3%%%::: `ԨQ\?/_lmm}}CCCFFF lllnݺҢflllii9gΜ!t%O?$,,|[ZZ={2ann.""ډ'Ν;FFFW7[lپ}BW^rrrnMddW>z !dkkѣ+W'ndddnܸA&O= w~ċ/BO>hSQQ[>|PQQŋ?8rȴi>yHKKÇ>>\KKKqT-,,Tj}}ulvpppvvɓ:K .]b,X~.| DȰW\NNNKKo:axW\𴫫+/~h-MZ<;}tII7oQVV' 6ȑaaaa//xxxxyy566޼ysRRR,,,\\\> << ++ ۇo0!{\~=B(==۴iӎ;TTTBCC###w1i$b˗WVVlܸ177WKKkժU,kΝ  eƍ7o;v,,p† >?"6l؀x;ãRPPpԩ˗H$&..?hjjxuuuOOr9Ѐ;{ Ϙ1!}3f1^fnng޽{߯m6CC>}TMMƝ+V@oJΞ=[VVKQQQ__LAAblߨ񜜜>}=z4?uuUV#++㓙?~l M䣟 ?>|b G u5MuRb2dFTǩuug1eF뒥Iҳ,q hB T^^>D\ׁ.~I2IHHxy|g!bW|7щbΝF2LUV%%%EGGϜ9ri om6[ttԩS ZmGk9p8˵ԩSL&ɓo~;Gl6n /x5U+EUUo4UU}z뭰O~'7m :v-̙3EEE%I ]?lذ+W.[, ?I5Ǐ///onnBlܸӵO?M6)g)f͚@ ~'jGqĉ{z<ŋj+͛WQQаaÆYvG>8p`֬Y]U+iiieeeMMM{5jԫ-t>гOl2m9tMII)))@!첪ׯ_זkkk -UWWF m|ϧ6oj[ 2ƍlfrrrmmVZ9p1c Czzǎk׮VW4555ىsՅUUO:H0Y%I>~$N[Ϗ 7wvߟi0Əo߾i6y[Vݻw; zZHk;~RUU|ڵrwi;bĈm/--MLL ]ݮggzՖ9s愽s\)))zZB-..z?v>QUPPp9sw8~˗/e;oII ǎ>}zl޼ҥKf9)))...lǏꂂ͛7 !FgϞݻw>رcL%(!秤 0`ʔ);v+2k,({6@a%#%]/9rrFp;GFMF (}VLB` DH "@$0>r !PUUme% 544YZ?f;卒j0J@d;W!ž}` DHܭ:|yYUU-PEݪSeI((DP$nժ?7aYnw:ZX,  $i18"@$ DH "@$ DH "@$ }/PUv !EѾ*eY$  njv(,"(dYvtjI`X&DV$He>_./ DH "@$ DH "@$ D2vn!(WUU,KDA "v[VnEe@E,n;N- , ȊI @e a)"VSSh$(P;vZifP @䊍!>NSKnZ  $i18"@$ DH "@$ DH "pg)]f>:)**J%^)  )!v3>H"D!~ @UUnBQBY%IAvVnk ȲLA "Ae}|`۝Nj@dE$IZE s "@$ DH "@$ DH {F$)>::>:ZCQcw.ןnS>!PDB5ӅHB1l :# auZn[(UUU!,˒$QPHpVUh۵EQdYA ˲>>NSKB5 "A$-"?py@$ Dpznժ{RR31cIIyW z .=˗)⿢  BI!S!n# L DH "Ѝp %$$̞=2@F† +**&NS&ư}yҥ>,e]cc;qD $i ΝL #UU |VB˪RM蟣!DBBSO=jժЕnjv(,(aԩ~ccڶm۔)SB+˲6,rtj- 6JY~#GO^ZZ]Iр6S$2跑Ki -DHeUTT|0SSS) zڧ~ԢQhIRf ep}'&M2 B?`hqpSׯbSޥ& RB\cDz(HIgcR܌k "@$@#/^x,ϛ7R&H(,,=w n: _rssm6f^VVF \'55ŋ @+νFQ!?op8(K?-|>2﷾%ƍB7nP fY) #bx!DoP>ia%3F… $nGnݺ5t8?\ڭ[N6n,hwQ;#;{߅z5w)S\rŋǏa8 <}K/4p[{7^hnn0`|}ԖO<9nܸ;u%WK.{^ɤwBM6%''3f\~]2h"YeY^hQcc|}^s8&p[sZ|BܹsԨQf XjURRRtt̙3].G>&f͟??VիWl6CoV|SNMHH0L'O޾}{ۨQM&ӄ >݄+{Vrcco梏wU6l:o|3f?t\G^brLNN^z/3o޼ a7v'js}6QQ`PeLn߾}_<)!p/|ڵ!ChÆ Ӗ vyBg0¶od}R[[;h myȐ!EEE7nhg+FY__[f+/Vv4&] n8+((TWz<;sꕦp…vhb4eztP#g333 v+FЖjJIt.[{lgc~Mj#·^Kϵ'Nߩ;W9s\h trޡH=FEE|[^SSV?KKK~whM݈#žYss=w mmi;je/Ԫ;%L:ollt\۶m2ew^-XwijjpBmm6O>D?vmV1gΜ"zxopCQ\\ϟ?|^[\\|UرYfi7o|%ٜy겲v/_޲͛ߺcǎM> z衇222ZZZJJJna7!^Uw^KxWEGG'&&?՞_bLVwiWX&Ol6s3GmPoٲ%..nra4322{m!秤 0`ʔ);v/Xf &9rcTVVl6m m SSSM&Ӹq֭[6 5kҰve/Jo? ӺȸDQ Y#"@$ &CKIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/diag_barres2.png0000644000175000017500000000230412014170666024761 0ustar georgeskgeorgeskPNG  IHDRJA$ pHYs  tIME.>ñtEXtCommentCreated with The GIMPd%n:IDATx1@P˥{Sd>C>JhA .-V3 n]}s @n7']M`/vepN0cKuτY@ndtAb%/Y:rsQn\"ɛLlտ)HZђ}q Mfow|Ocyr6N2h LR=K56-9$ޤᯖ @x  7o7_MmLoieTڡ"Sί?eTڡ:Ul2Sf*Pgܽ7NH v{ v塪ouRY1ymT0ZQ ʼa0(J(KR/V?J:SE oif*]}=`͸D65lM.}S!8,{~ǮIxbff6[ɖĮ0Ϟ!$(Kej0Qn]f NfK._ %寞mA[h 7!㈳|3ZX2^>G׉/2vu} eYjUVkY/ a*ZF}TfG{s6p#W5̳0_ f{Y>+ZYt 6sDQeYUl좷JfEΓY]O.Ӓi%G2g|X dًdxDa'?v#fl!Q 6tۏun1J(iwW50~@(P1%SIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/morceaux.png0000644000175000017500000000126312014170666024263 0ustar georgeskgeorgeskPNG  IHDRG0K pHYs  tIME   ~etEXtCommentCreated with The GIMPd%n)IDATx휻r0Esm\ha赫5s*bYګ+$4M?7BW\ Qx<е6A+JqDz_O=(}+GzuuJUcYeѪƾR:?dP8y&HڏjuuJ3{,uyk~]w?z GV\-7UC[֦pF[ޘimͲ' 6%RbBŭla;7LɼwlNY0/`y3`uOVwʵN]- 6ݲQu 1Kʤ: X+W_mabj\#~Rp٧R)fM)(T>͹'W<06v -H6 (/<:DT3G~/hfjeJJ aR$W_OGM̥vp˄Ln@mSǸ7KLÒem _#5 Zg/oW%ӮUzIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/histo_3.png0000644000175000017500000000760212014170666024013 0ustar georgeskgeorgeskPNG  IHDR _S pHYs  tIME*LtEXtCommentCreated with The GIMPd%nIDATxmL񫴅2| $tDDșdcfllz`[`-qpbt3lFȔ8}>-(/m.]P&I /@'t@'t@'t@'t@'pHN& eVFD߼#HLVy;xn˫Q9B< NNNNN@'t@'t@'t@'t@':::: NNNNN@'t@'t@'t@'t@'::::EVde2/SOMaaKz{o dJe>Z1N en@P·%<6NNNNN@'t0=D>Ɠ_H FCLL?NȰ-{|$NECyq~8a 18Xx8Y+Dn+-֮];888d2: Pjab [~oPPPzzzWWë555(覦&ƣGfff d;wLNNqF\\RNWRR+xw;F'^xSSSU*UZZZ{{ovHHOhϝ;DFF644L?8$$$==ׇӧd0=F@@ӧsssO8j|A~~֭[W\YTTtCEGGGEEUUUmذNx w8ʒ#+UTT=z4))lSRR 5͛rss\B' ~2T&p͛7۷~\[[u^xA.m߾SNﲲVի_Vj+?^uԩ{>22Ng:~ƾEh4 !*++233*++x p/~9))ի>|.@'t@'t@'t@'t@':::: NNNNt@'t@'t@'t@'t@':::: NNNN ^{Gܱ`$-[ÇY :<0 we)9pK/Z pju':::㛚v>z~Æ j}|$B^Z;Q1S#$)88xΗd2/Yvٻw]nݺe04MmmsB\vmxxXڵ+++ki:k6jbĦ}?566>l7Զ6qpw;w-Zdt~K ~a\>'~䓡Jr5q8]Vs yjkk\8&jㆱZvٲeԉO^^^Ó>-[?p6NNW[[_z_b[|S\\Ңj-}N0q]V.=?iӦh4jO?499ٶ=++o5Lׯ_볽6 x`UU$bǎCCCCCC۷o; knn566bwv۷o[,K.۶>>>/sx۴'OZ,7oNz~隚^oZ/]e˖'ӓ=22RSS3LNFKOTUU=3**$$dϞ=}}}A@7n +..~>{ȑ#~~~677*jg`Z"""?3PB?:@ZZϊ+***~}3::ZVVc)իW[ϱ!22R.GFFط;)8肵&%7ʟ) @9p<::xHga%tSBIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/ptes_objets.png0000644000175000017500000001547212014170666024770 0ustar georgeskgeorgeskPNG  IHDR? pHYs  tIME pIDATx[$w]I\ F.GL ;d& 5YD`=/> dfHB'|RXqǣ3r B&8"CuIz_]߿ݝc'N:B8zĶicc#p('E޺&8 B0AHƅnLra]^zzyY\f$ͬk'^\jSL Q|*d75]ۊ]S_ST{]Zp8Ϸ. ^pݩi^t{ۣ/ 7'߼;po~W˝˟#a,r;Y*ϸW߾}gn~B3/ 䉿=p͛} %p[>on;k +kW' ' gjK]͟{a!`!\m]7`ow'J_{(poW?vo!詏Ə>k#ɧBxշ=w$=xd_/pB@|A|/7o+]_'W'慲߼O~栘]{BO|Ϩ*|SS-o?g'CG. !WG>~z {^ adAW?W>Bx<|2ý !n}U;D ¨0/ȝrި* o?·?‹_sK^sK^sq~v#_}KoOo7pkwpwV^?B_?WBOB؃<z ̧^>YaUg\qS7p ''^ soOmWz>z]k(+|:8[?|p9T_ϿdןcŧM~a]ٹ7A~ Jƒrb+o~v9|x[Mv_/޷y_):mj5o~K-A-?Oޒ{hZWs vw8o3Ͼ84:|͛PAVft[J/Is7nz9|&ӺF:ȹ[aП\Лc}[A0\wm(O,'O=s K!oS[[[]zUBt7} /{vt@\IM繠{Kx]V=$oNi[] Gq{IꗊE9X^-p;$ʸTg_|;9e2SSoܧ4Qι(N 5D4`-}Se"uO6dnRGD/6c-[f?$J=[(.yKbe_Qh5v5`Cuj@Q̳d%Ups]ť;(봐#s{VYJ][Fo8:a(l$P;\1iO2c2ws P%(Mޞhl'*-n # _p͞ٳS+{^ʜ*Zv07mW%FmmmFr L Bg.ݺ\Tih4jOKǏ=~+՝; D!(@BQв("}KN`pz`j5$wy^r!9\fB8~h:f^4MQg؛anh4u&)}Չ}/|yNz-LmccFX(,ϾGj0SN#M4={s:VibY8*,S&h)@ddԒE V.5  r @BQ D!zWX}4[Oi; ]3zLQ8&*j!VZY7 Ե 0nf&Ƚ?z.ߨ`0fVmզn{ WUaJy2u2ʫJ6̒/Fa*T>HS2O;g9Xo3KFI>}l{ul ˕qRrgai$ )npKUJ}z4s0N97J-nҊW9wf\.̫"GKf - 8N[Kx5E+90Zu+6|}qFQ|G`vI;rR6p塪p4톔:<:07'yzpn,m*Kg!*m 16vL {;WD߉od;YSs7;ZO7Y4 ۡS4񷷷EE G܎+-POAn%ѓf.|;H(@BQp`nll^@ Q@9V D!(@BQ D!(uGz_tOY\|3$|X29 ;w~3{cG ݝ( e]4ɇ?SQ7F 7w6R`*3dMvm0<:E5u̝'!0(lZHuQKL3cy0uJ$:+.>tʬ{|ro+DaneϟdOdX~Omnv: :@BQ D!(@BQ ڬXnڍ78=sndQB޶qK6j%֖K- E+@B @l(OhYcB] >d ?R1Y04˪B肨GNݨRK+E!t$ B->t}H=( \^”=S a饾IGK/#:Mޙ* ZA@BFD!rkX3]nuF#[{Ycк2p813@BQ D!(@BQ D!(ogoNΫXGq>6@O;rlFNANSg]ikE0U fɩtnZX(Ҭҹ@+0%d0+FPV no ELMPhK@BQ D!(@BQ D!(1X;>~hKZεҴF+ ]y{{;ubwv5wQxf+ommwh4zGkZui3y׈v{sgѮ  k+~Ƽ4Uf?Њ(>y0CW<R7c*ywW{Ӗ!0yAr0y$YG& *u4^`ӒkomgK "*l^dFLѨ!y6qG.'hϳ!Kc60]dJaت^k̭*ҴYQ}a8F}!=JZUϔ)gM[lC+tLcgϜNUސG V uK뭪޴E5$:P]ǎ^=ډTZ)*u@-^ܔ7$9ۓ6y >ݢ>ؐ*/Aӟ,N(F;9[UEJskՆY;7x2OU{Ϟ9ߞiv*W:4gƕijXBQ D!(@BQ D!4(F6(t8+@BQ D!(@,8 WmwU2dظ@P}s7w?vNLUЌk{r 4\9tUaBѫ]*։و̭.]="FhQKهO Zʆfgf_{N9uګ,_U aIe0f8,M&WLqϢS+r@*Q D!(@BQ D!(X&l9`UQRY3mmX(h$:jϚ;q}Z|f#U&38$+ [|Y_[#x`Z2M&Yr$G*8;Ks(_de3elvGd*J&;=ږO 黜ٜ ColDځ5rD3I-avs-/XtI-Ua4!q䭛{1؉NVe)W{!"r O*VDـ^FU\,4 '){ Ϝc%N)CCv=@. g1 p} ]hUAvh4@ B9 D!(@BQ D!(@BQ D!@c#p80Dq@s7mOXfMP4Yk: WXrt'Nݝmy T<,e`\=E&'K=ehY[v:ng'ή̞T>WXT6M1v(yN :sj*S>(I!D8F }ɹ_,YW4LkĬ>jKN]="ȱVrVayU\d^w;7c@Q D!(@BQ -e|P {8 n8 6,c@BQ D!(@BQ D!(@0 hԓN'OF4mrD;zS666l7Q+k{;\z]֖7LT=8iyb]2ZDui,OݙJM8̭+ԦnGB[:4IHx__g*-晛>Uj +mKV*yvSx[.fcfߨ7snm56ªTd\Lf)*]VC|/ɜl`?gt#y'{:U/T;|6vAhS,=Ν#V;3l,"tVRَat;g]o?'asMnҐoɈڒ%Gfޣh'=Z./<jԉlQ p'}V|J:ٙTiKKf]>ZtT,MS͟_}{rcU)cW&K,_,gI4T֛f)!E:ݽJ%(lu\XɜIELɳ(N?D6$ (4i:¥[q5~%QKBrJΞ9hu%# hd#TwnRNZAKDZBQ D!(@BQ D!(@BQ rۤΝߴFr)#BP.hWTy>EamVvwVx:ϙ׻ĭ-{T=h7v_4sKԲW0ӣ (,kUeVyxͭ`ְ,Q~uH* 4E;bvV%S*ZnneW;v&G-矃6(D*ڥ*kR,7ؕ}(dN97`0n݃^6Qz01g8ҳ]!Աoߙ {7j)o?'a-KϝIY ߒmL4|g(]^x u)A6ʹ4}r+d(8ZNLw6tPw_o쟩uAZ&N)y|YJH) R:÷7DaӰW\{ =L;QH/dN" BcIt+1I-D!t< ]Xa/=s)|KFAFD!ݤzȱBQ D!(@BQ D!@cp"QBO(t'+@BQ D!(@BQpC=opxi|u ojk2(@&+k6^Gz/_ ՜XW+:5n jtNv=ݶmKO0)O ;{Oc7K/R>)>8 olG  @@&'G~@n7'/@/kK@>3Ma/9^_g#t/?/ŽyJ=0h眄ؘ*yq.=qKJ)@7[R8ɌAN?W#.ػ|7@ eY/qVwޛ0KA*{^>Д#EeJݬ92~2[FK[ W*wyׯSDtIl O&nL98~5KT84#>=?I@@/@/  @WC q/߿z^m?#=,za,蕨p^rJV\!rc$޵Rj'Ht]hFW]@7RNtFOg~N"r@}DnKme{e=[ýIv8WRu9[3WQsHl4=_~~__{c9OW9cJ@ Tݾ8>qa63̖uva /JqMu;<0uPo>{sldmF;E*A~³8==JPz˔:sSh_ǬSIEz==gEgW"0pҐy~m8Eee!L@vSVX(zWeK0msLL g";ΰXڐ]N~:?-Gq չ~o {1z^*a~Ȉ^4+3cPHE`QC/Jt}G"{RZXgXuG"E֍{qrPn(Nreqi}?h^VpS51$B@pPefV[ĪWƅ |o~ 眱l۶,+ &o5}:eiD|g fVf3XAYAfe12 bƘ bvH}}l,6A̬ f2122x ùA h4Xe۶$ 0n 3b@aY b@`3 bFy \l:w11YA`e1A f31Ҳ>1ČYb6׫b6_z fbv.Nb1 f'/?5 0#b6_nA f #Hqp8F"˲lۖCPb9aB fC$m۲`0 6(?A̎ӧ| f qM?spJ3 8 b fMS1wv2#6YAlf2@x40@1#uƮ f3Ɯ׫^b6ԩjkc f󕔨1YAHIbf3Č f 03@`q1cv fGPs3c f'fSVh13W^'1|d GOSp1h$,˶mI~? e)^),ѭmۖe,q5"f6 13WUE b1#uL@b#jjb ͚#G1|g!@0_~b1@pN f8gqb ~1|55[@0_i)o"GpU @0^e%W$f8w/1|ժg ݜ;EpR0b/as9t f﮻׌aKUWU,q G *bC,[>b 婧t>13L`ψ_ e ax<k*/˜{q6c fo i&A0SO/a UUwq>+!-GH$bYCХ z</ȃ45\ %m۲`08쫽nMM͏?H >8r&f6RLQQQzzzIIwuۯ]ZʌqC/,Tu0_[y}M>a3 ͛%i$fo ^PH1 b*+[jiѳ aNӴr~+z 1XձcڰAzi3bfP8Fmܨ =Og* cUUiwܬ_RKf0 3uoY#O˖i<]卮@HmZTK*'%K4\4nvT-W_)ܹZ 2׿K_|eY .ܴiܹsGM;7W˗kr A|ݚ;W pbƵ9r$ ޽[Ү]/^Ϛ5k-e4(qAA& Jϧ*UU)Pq1[s…D)zzبF55]Rۭ2U^B"g١ ۿ2wttL<ğܹZZw:;'{TIIcTM*oN{ٳjժKٽ{}ִi6m/5wjkKٲɓUTBMB*7Wێ!fu^G,kϞ=6m&\K:uv*Mih(Y}L)/O?rrIL 6gΜ_|qՒϟo̙3GaN1Lee׾b1bV,V=3gݭg_)-M^|ξ,ee|fT>lbxQѹsQos._,,SQVVLefǏK;#)X"==)>zΟW?ut_yɓ{<瓿) Ư֭[0ʻ70w뮻&?a /k߿ \,(>P?˖-[jΝ;/)sA?H3g|뭷^ʕ+%l۾;G"}ܿ~ܹ@ _Pӛ5v?ofKK˺-˲F*(>x}8}۷OMgz!G˲nP(4=Q}ܿ{]]݌3&?='|c544 `~4ZFyq/..?|Ç?N4b%7o Bٗ>A#1;=ϗ_q5hR/:De{>_u͚" ;N bNmᑟ:F:>J^R ٳ5gb6eHEE?'Ԥ#Gb1IWujjTZ3b6ۭ[n-׿N~K z=ʲTQ T]{)MSPEhQr<՗_j. T#d@nWVR>*I--ڿ_/IkɒZmsE… k}USeR3N+u¥ pq8Zhr1$i< Njfb6ۭCqmۦ~=~ b6ִiӟiiժS yyݭpX}}ZVLl\_mؠnʕ7R)*/+a f=mӶmCb6ܤIںU֮|V&@̦ZB|R lٳy֬Ѩ V\;ac f|ھ]7a 7кu?>{׷A̎?2 ѭr< }T|@̎+zqi&rr|w&b6_0zuu1 ^xA73\\PQ!Gǎ]}0Gh$,˶mI~? 1U u&֯׶m?\7lfb f()Qk+c1Flo'f#r fĜRrs1ё#lٳuc1/!A̎vbv^z{|UUjld fb.F >p4D"eٶ-B!bNE?rDm[ ?6CG@̎P^'|~|FǍ E1A f ` fS f|$c1oT131"xfC|a fY1bM 8')b6_Q::|\olb12A f<>@KOc1 3b@̩.#C3@̎9c a.FjrspX8FHIJ,۶%P(Ġ0z)f^efjѢ|Ҷm˲ &3 'Gg0#Vfb1;def3&2f3@1 fl1ČTbY^10|>b1;%^bf3 6AK:w1Čg03+3 f21,1;YI)3@cϲ  f nK h4Xe۶$ ٘9e)ѭmۖeA&6 Ab6_V|1 b1ccb# fVf3Rf"3ANy`1g1 b1A 31A f3n)MIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/arbre.png0000644000175000017500000007345712014170666023551 0ustar georgeskgeorgeskPNG  IHDRXϦsRGBbKGD pHYs  tIME *w=5 IDATxy\N?ǹHD0l(K$%d&e>6jcj%E (Ph_߮KRhyonyy^u9s@Dc1VcDc1Nc18b11cqc1 c1'X1c,c1NcYf c|De<w"1c,c FfptZZZ0`"##ッѷo_hiik׮NWg5k;v͛7rK~@m۶UX`ڴi6m޾wŋqcU(44HHHѣ,7{l,Y?ƩSp5LR_/"##R_~=ݻK.!""O}Z_Iw^"00[جYmC ajj kkklٲ^^;;y$455q:+V@CCnFXM>(,,X:NMM ߯pغu+rssS,\Pfɓ'O 33K.2Ə%K -- iiiXx1llljd'^|I H$)IWW6mJ'_Djٲ%9::~eI$)۵kӧU]I$QLL9Bm۶%UUUڿmgF***tСZ\zLҥKICCn޼ɍPWW/ePϞ=EԧO ۴ijժEDDЀEԥKڷoLZx1k׎i^Fnn.999鑓U;VիN" 믿bΜ9DpppIWZmёI}}}<~<@6mdedd`smۆs툏Co4m48::bcu?'iٮ];޾bժU?͛5w}Wk.K/jlذ^^^܈c`%gϞʂ7,--q%,Z***Xl׷.^ӧu&_%$$~-͑ɓ'Yf޽{;wDΝc"77W:}AAN<)]^ Dc4h:vH߿+WĠAc͛7͛7Kmoll,<D{bԩc5k }}}yyy` 6ZfHH D"hkkc|r>011aÐ ___iG0sL١s>|31aD"b>\f D"N x~Gi?cǎٳgF.]p*?\fa044Drr2̙---KÇGPRR€Juum888`ĉС&NO+Wĉ^?hԩ{.ظq#.^(..F`` a@[[ xNNNpssѳg2xcֆH$B۶m1x`U껀1ީ^]|Vvv6رА}L Ǐ~#Ao߾N?#Rvv6͟?TTT8{, @'Oݸq޽{DDDԱcJoc]Ѷm[Dr[AI&Qrr2uЁܹ#a``@:::ADDD"!Ahԩ2oI:z(Se.o;#"ѣG 4k,)++,X@ ̙3? !999rvvΝ;dhhHO>%"TRSS#SSS*,,,7f^^^$]xQZG2q>mܸAaÆѯJNNNdaaADDVVVdbb"ݻI$۷e)))Qff&H$#FHW]cR,A_~ԫW/ر#YXXЏ?H/_ۿ? @ԪU+'N dgg'-spp HDDZv1[SĉeZj%sz*5nܘe$GZ6`RSSYRl" }`փHZnŅD"K&MD U`ٳG͝;L"SՕA]vs`P]vB~~~e+V 27HDDD l2etޝ>3Q~cccC Й3gʍفJ%Xo͸3fرciȑ>o&ƍ֭[L׻wo.`Ի` @֭e/K/ɔ\&077|KUUUWRs[J-qprr M4Yn͡y{oz;) py%5!66FFF>>m۶6uuu7o\]\g{bȐ!|2+JkEiӦҾf ˗/e˗077ǫW>x{?uinΖn={V+C?OwcwϞ=eʋpBwPr% J[t{=2OEO~W^F=EJ.o>>-%%k֬Ahh(6oތ+V:R6}.""̛7知/ׯN<)͸\.Ǖ\^2sXx1~gHSvV} SSS߿ݓS̚jwޅS*zFGG?kbʕ2UE...(,,/L;՛3XōZuK2}txzzbŊСCK`PVV~ɾ|֒$oϳAlX1cƠ[nؿ??~qơ5)..t@pp0FK.F:}^^^(..!((.\ ~:o?3BCCrJiAЦMBAAm|Leݻw\"p5 | LMMCرc^֭[ѭ[7jjj@tt3gΔ>o6o Y>(!ʶ+WbĉׯΝvݻƹsʍkI2zt/^KcǎEAAZзo_,]w˥gD"ܪ軠d}_u^PP988H0n8ڷo_uO8A=z HD9ȌqƑ*)))Qٳ>>DD+IIID"Qa\HQQ,--ѣGti=z4D"244$9s A-6l 555j֬͘1iذaNk֬!"8277N:5jHzGz6v8D"266ÇӠAܜJ.ԧORTT$uuuŤC***dooOiiidaaA+VׯS\\;:w,633I$SttU?~:wL"\\\޽{t=Zp! @OcRVV&999ھ};={Lz^׮]qƤISNwƷ>sRRRSFF>|K"zEݸqlmmI$QvH,Ӌ/dzUVV&mmmZh={HKKKȅׯ1uЁTTTdګC c'՟WOǐ!C0|p4,Yk׮}rEEE҇vx)ϟcڸ8pTr033C-8PV_p- x}I[WW 1jEȪ%''Ϟ=Ô)S0{li>Ο?;;;Ddz^ñ`L<ݻwq%g1Xu%BVmv¯wB$cƌ 5kbFQQ6l؀~ qqqPTTDǎakkyAII#<9ctgNc_UA1l0c0Nc18b1ڲk:eԅuxOEC cj`Kakk5}@/+mICIE"Э[7,^,_=z􀫫+'X1XuD(((o|K$>hڴ)1rHJͅ#4i-[bÆ UZwiҤ u_~ӧO˫b0g8p@f1gΜ{Ukub cz+W.}qڵkqu#%%JJJpss&88/^aooQFԩS8w^x'b֬YeCe:}Vw]|{VVVpvvFjj*RRR`ll J/_HHH@dd$BCCޕ1c |xyyb[lyWuTL>:~#c!ۇ7jj*9::Ҙ1cdƗpD۶m޽{d֖&==]:]f\QNo+vW4lԔjՊ߯z+o*(( yy*]ۚLڵŋSvd^^ոWuV&&QncMJTUUƦp 999 >Wl/_& jҤImԨI ޕM>|H:::5+VڴiS>~LLjcŗc233mmwAbbt:kʇylmm䄤$H$dff\bÇowD޻w/>JWNN999gϞ/""~}Y]`1fϞYf!..EEE]:4Mnn.#fggWWW#==]Vu;''7n܀3ۇUVUzfffشirrrٳgKegg㫯¡C0n8ܹ(C1%ª^j+..kR۶mI^^LMMϯ.߶zjRQQ)we?$jԨiӆl"S/;;KRRR"---ZfL_ֻ˳ O)))Uwxx8u֍m۶crh2swwSH+*LLj%B~3c2B[Ad.u=Ͻ{0j(Wz}l/2c ?dbرz,@vZ4n"ݺu_ÇW^ؾ};ׇ .Wqc۷ҥKA///ϯ3y_z ,@ZZ^zCAEEZqc NRRRRRеkWH  1 c(y'߸qdʯ_ԩzHCcΙ3gdlx{{c?~<֬YAb,c%H x"""ЩS'\z&&&uvQ_"dիx9L̙3>ƌ:L+8bOAe:u*n:c`13g@ 8Pk׮[nm(9{%̙֭[C$z}X56m EEE9irss&Me˖ذac'O` ٬>}e(ccc4puuſcԩ&2&M ==]:>99m۶|ڱcGɖ-[\bd3X1VGq()k ++ +VÇ!6n܈;wwޅ`kk[dkk͛7cĈPUUūW&Jn 3_jՊ1Tu1gDŽkA d/`ժUꈎ9W\(**BQQ<ҥKe{Y0{l̚5 [lA۶mq]Y~~~ Yc E 5p<88#5bX,\W-g}Vʳo>,\M4ѯ_??TTTЦMO>_x1,,,0d(++޾1w]{r ^^uEu89 .Gybt珲{aԨQg/2I8x b$L$I8fw ZYV5...XbꊱcrPX cA-{G!n0 XBY^yyyW1`y"pa6Յ#Z#[`,X``1X]CxGx7<.UzK1Nc qB1pVFVPS18bʐ.B!?7oG3GB[E1*+6#1~ 99bi0oiaqrssQXXUUZ>y8|0b= h2r Fv Fc?ɝ1NDΖ0 IDAT7nR,)F`l rsK ;S;yWמ^豻e>>}xn dٲex ߿&M*SSSCaa!Oq 1 %%fffHNNҥK000rrrgGl wrg}rO)kHII&:```:%(++YVTT$NJJBǎeq 099m۶7n ;;}ا =`EsV[ƃҪ|P: 3-DL cA/G@T}箇1ɮò%D-6&MwJMMł `ggWyΞ=fB\\)3O;;;"==WBQQxL.8b{D0;5>{|b 1_ M9Pؚ5k:Cի5ŋC 21n8~ ***hӦ LLLЧO… ѤI <jD"BBB0pJM2 o8Bėd: f׺#*ӑcwFx1N|6;cҰgCe˖;3+/ n7,؎c,dU+V ??;v,`1ISSK.ҥK+=E lzz@addg9ϰv=vG6j;DƍݻCWWl޼0225PPPPO>f͚a3gJթY :T:|]Ņw1xq.|n`fx6r-**BFF<<<йsgL8fȐ!Fbb"Əm۶AWWÕ+W*~ǎprr̝;۷o 44ҲEaժUPTT8b}87o%[:9<2fuE !'''Oӧٳgsp(| ǏO>&>>aaacƌAvv69w̝;IIIرcz]tyyyx:!>>^^^˫+22됓-aO 7͔[Gn[n^^y40pttƍUлwo˶v>GGGiBT˄ϟ?Gtt4+yxSe'X1@$u1_&b$|Z /_wbڴiDعs'~ ͛7k۷cժUe3g~'i7sϟy0Nc/0GWB`pX6`v eό ,_5j:wNaV8+++XYYI>T/^1Nc .? qnS]89b>}ӧOyZ#((~-q/;޷,&wS'we @\\ |!cXVA7, í* YO}vj&WgΜ10p@<|wcU`1 HH  # *훷#l]kӧtL>pD!6#1#[ . r}믿BEEj*L>w$cqاw+ מ\h:r+Fv FVJfyh׮TXR #8z(LLhvǡQg|18bJ8B"~|K\y &Z&u~۲q-f7scРApww7+Xw܁5ʼa>jz ۮnC==cw<|c#l^$W۷QFsssxzz"55F떥}ܹ3&LPiaÆ_tqGVX\as틙gSbD՟{쁽=455_oa!7dj}A˗Ν;UEe'X}B7o%[:9<2fuEz^^^PRRBaa!ݻ>>>!C?~ףq.]"===auk֬ݾ}R[/\@{.U^TTD4x`rvvjQ`zoՈABBB/ʇ?|ڣ5NĜK$&C<==!n߾ }}w~= 8B H_4SjG3GlLVmq6mAՙ>wq1s΅U"͚5֭[WkƍDH$8Y3X^0gQo빧'>8>8bb\| :Nݍ sssĉ0aB SUV$336mLye;?۷lɸ}6SVx"c`zh߾=Ǝ OOOܾ}=Fr%-A;6l>ikk#::35=ZU+UUd.ل]1{.Э[7lْ?APPP:3 cox;&?111رc%.Lei% GA``1VS\]]!0zhiYll,ח KTT9x yyy(+WcYکSԽWwzܹsիWqi韛N՗V#6#X D"D"+˗ NJE `…ؼys;wwd ,[`=Q~:k׮ڵk8<EEE}0uTc`1Vbm趫V^ 333׾}{իW˝!CHZ;S;ߍ[>(4GxzzqPVVFݡ 6>FFXm ųg跿w_dԻ}WŻ2dc۶mżyd[պ$炂RZƍ֭dd݄ҞD"2%qHihOyyur?Ҟ={~':|07PgB.]"==JmlnܸAڵhiم wޥQDD W+zlD"K htmS괭{=%f&guWzpp0988=9ByyeJݪ#]]] +5W^trMII!UUUީ`! .VC<}9pJ6 mc^$/JƲp97X sssĉ0a]2>L-'kɸ}6Sb„ 3G/{f$|ym,v++þ=#e":UVaٰ5#Wg.͛nU<*+?#[Ə)S ** ڰŚ5kxg2NXuyu4t&NHhx'STb1jU#İ:mra BVV4i"Sfw],ĉ !h݅>M'=L2A&Wozc;) Y+IW`Ń!)FVA7 Voc߾}޽{MϿbb\jv*~#<ݦ#v~,4hr`ʠ '`B HJO6N̓Mg89bqԩk8"5yEyBa`ہ tlёSI-UZba߅p 1&O2h;pXe`1V [ % t 'AMtơq:4nzd > _} [O c` Q(.ēWOvZ|KUHIm?ߍo|C 1>!> 6yh$3?f\}@-[fkhӎMC6p vCTZ18buQFn8.Pl2(+sp>"-l\a><|{瞞~u;< 1'X/ʇh=C0ڍ*-98H#:/>3s9lQX\ȁbJLL tttW/^k׮x9q'"?:7g<S-SN-Y=fˈN-:ZK; sssxzz"55F0228P]vwh֬ $^A1|,X90uV[Fs&!#7.sx끔+͛###X[[# ծ%KD۷ow}) ?^$ޖlgcF|Z] "Xxɮp1ڣ5&O2 2HLLm6b޼yr{}Sa``r___ FFF#XByswZtǙ9(ԃh9L去tQe#B={)ҥ ]rw3|RX\mWÖ.O ^SSpp){Qz-cSVVb1,--1n8XYYv BF zguq6ǣ.vu9sM矡M6U?Ν#s")) ;v@޽߻nY,Y 66m777,Vw? %o{92 6m *戉'0a(((TnY~ iii1b:"qj\BUU81~8mob ӎ18;g`bD7nܨye痡(!j^+VmYYV-_~% K%W%ߋfcKOOȑ#Xc$E}}7 Bw8sY0bǣ8<$$0$9AY;{.B^Qv|: Ɲ4nzJJp0sCWjrpc jna`g j^w [s'&aȭJNSƋ$'Xz)f{{D¹37ja|#yX[#6O>d:u09`2ĞA Xuq E DΉpw4Wjaz9!lfͼ֪14h `7DEqc`ЭC0fSO!@CVkh` xǣG豻zmWY3cN̺uVv00/^GzF#;|#yc3XD6>ڬk3)c.O9ԢN9{+fMRv 044YƍƍSNaPTT쐔$SΝ;h߾LYzz:ڴiߝ -- ׯ 8t+FQN4# #\MCFn]fp)Y)HMM}mmm1{ldff?ıcrΝ]?2+M IDATkӦMpvvFrr2ХKL8QNPP &SrJ̙3w69c˼t5Wht+|@zQpp0}WFVVVO&ݽ{Ru/޽;ٓNUP?Hād D"M3 uq[GZ:JL|_.]"== xxx!yzzH=yZh!#MMM%"\Ԥ*;@ZZZt}i wgR^H"Tx61>UH$jY",X`cH^Ou$u4 3҃ht˫\7eeeztӧԷo_ڲe Ջnܸ!s2˜ժRPPՋ.^XLrkx" Rm7 7D͍BWAhܨ1}a.B‹R1118q&LJM;aL4 7n@qq1${x}V,`bbذFѨE%bd%רQVĂ1  xu ;gvnr2͚5c;v… ?SfMS_lYWΒ%K7nݺq ?VZrឮ_NI/,`ܹk 0 (Y-fs Od8t%)ʐ!C(Zh~լY6m`eeEՉG0{leOvoժ:un޼RζnJڵɕK^+wIU=S۵k' h4Rn]"W㮲"9s *×135-7=g۷/*TH)u"/&s豪SܼyݻwӢE &AcH! cN,_lț ) "/ +vmoWsY?uMC9z(돦w2OۻWʖ-E%ɉl>cU'WV __u{Bz|z긹1*Ub3ӭwv;]U>7%EFuY&\DeafA3f,n+~W,>SKć`$=xd>wwwrMTTcqFlmm3eQ@wqBbr"gbʯpDV]7;Gŵ+ Ԓ|‚ *p].]DFɷ~PETqw8tw-¾}sH&Tȉ rsǾDDDD3f  T ͹s`4Or@e&>9^s}5JP0"9e2 ԩS)._̢EϲU\ӧsڵ 8&99*VH "j8M4ڋSG_g#",X~~~L<9ǰdf:tgggZn͊+HHHHا1 -Z/cǎ=r V6sz5q-A9KsK#"pssH9DehРAYFDtt4>>>̚5%J0`BBBܺu3h =$˱POnJXѵ suuڵ/2.ܲ V^M:u߿?Z"w, dĈL0A"QN\?Mya'A5aea`D$M*Vsɒ___&OLѢE3l̚5˧n0Rj|"*Xص}CӳJO淙O1g3vX>>̚5%J0`BBB>/j dr:tgggZn͊+HHHHؿO:u*ɩ'$$0{l ݀ ֭[ٽ{7 fɒ%9r_{aXXgΜaܹɓkײuVmLQFf͢D 04UR\9VXjŋQ dy888HfOJc֭ԩSGGGʕ+ǻロf-""Iн{wmʕ+ 3cOϢJGI#"""*Xa0hܒ 9=iԈCWi Vz2E7szijAkʗ?%EDDD+=[gbpI(?<_l;wEED:DT^}{[6mlڎL';ɉڲ"""5J`Gä'Q*?^[WDDDT`0SӳJO:LM9r`%=szi\R71q1"""ֶLm:{/Nm,=K[]DDDTց,g}\L'CI6%k니 Vz[&>jUc빭z Vz :u'y]xjq+N8G`遧) U€~"""EaVY %hg:2qD'#CD̙3?KKK̨R ݻwiӦԨQٳg+$|UW2*Ϣ#t"#GPB VXA`` 6laÆ 8S*(A⦅ 2|p<dׅ] FDҥK\zwwwʕ+2nݺlذA!`@ ftƩh]56vuZ ۸q#ީGaơV޿\rs^!+U*_|| g|||ѣɦ,A2 tnIS|{zĭMjW5M|י7o իn_7Xtd0#'7ƶFmyʸ8&MҥK1 L\`be aBn4Ӊ?q Ws̙3J ʝ+L=wί˝;,밌30hl<7"|j;Xyr%cwGǟZzu}ʳKv?>Ìl::e0T!FDTOZ:T"d"R,9"×^sy= HDT:KsK \"?1ոttȚkUA VvcfS III So&eɕÌlNrS ˷:,99Y!dɔ̮  de___m[B :{ ‚#:W6mTC"%"aeÌ칸No⏗q,yd5jĨQW-)9mQ0Yq|.]eYeST }5\aH31Y}r5W×5`kmDDKg|/4O]UNd)GfdᑅѕоvQ8"%￧K.XZ;L&3+0և^Kv,:cO]A4,s3s$"Y"ȼJ*X[[cmmiҤ :uz|4q3L3x*FBRN{7%KrWO,mUD$Lj߾}-[Hݺu9[Q7uY3q(fb^DdpaCY|t166tƔ`렀DDK&M80C3h Lǿ?<W\aᑅÌ=G,밌ebftQ,̜^U{ɭwOQP#Z9bB -XVIx`aF6D2up|*2,<Ν;m,m]4TϷ}W>!#댤U,y3VVz2k.]H}9L|%f>;q#3Cf ‘ 2">೺\ ,U쪰f2溱(.\i5Kԛ)DDK\- ko5 '믱gʔ)/!o F$Y6o:zV"ixnhU{.4]Д 'KjͫGo&+P8""*X*Y[GtJ*Q ŕ;WNȵt\;;Cލ~Ya)^^XsEDT_Qк M9(L'6 wN&tnJi(W}Ck/臭BQցe \,gORr3BfPj&1q1 Lh4. ID%1I^==R> ߄|kp^Ffdͩ5xtp@ d5ni҆!3i_'SHL&&04GcciC7nLy{  HD_CEXIDATa,0N:Mقe]UϵNr SvO=zKcytG\`IvTl>>\s L9{)4xǖȋ2˰z9~l#afYD$!Byi.N~ `|[]`2B`h K-kEe\^Q}}ς<"D:e(ǸE~ cqW։YDDTD`F7n؞i{bQ .6_7BN9> wYyr%0#EFrS ˷:@""Y"W\YψIs3szVIGL=?5sK&6HقezD܈fx#WUF!td:66?S\QqNEm3}KjͫGo&+菣dʕ HP2_I淙Ϟ{8t32I:߅ (Xc/vY;boG`h \KrL;EDr"Lrlki(bRI.'—*Ucǎ=vYŋO9r0# ,̂] Kam,K}\s0Mc^>̌%Jb|*WLׯ_g̙*T333YrQ2E790ZkQg~JO>w]\\^%$%*xSrjI_΀734> xm̵Q)RR|y ӦM[`d3N 8A)WدSNL&]9Ġu(9$C6 '`G_}Χ ir=>L޼y_tdie a*d =5#dQt؁SL z"c۶mӲeK͵S$;_Æ _~L'泛pbb͏Գr%j͚5hBa`dM888rѧ7]cG񿏻H4yua0h޼tPxƍƍIJJzNLI963s9u믿O?eҤI :C'N ***UdDž +7i$uF(V$''?H\B\҆оّ7o^Rni9=fÃ^^^zdR \pBBBRN$nܸ1[nijذ!۷ogxzzflt:'`I燙-[Lvi TŞR_*-Yǟɮ]2JDG'Ke߿Ͼ}Xn]eĈO}'aSw~]~<#0%iժӦM`ӦM$&&ңG#%$$$m+V~OҮB;fE)ytfII W^۷YfMW,]EpDTD }=q"yGsR̦]Wv{FnIč,ϥpL2/ҦMbbbظq# T8"*X"YǏ… l۶-m!!!|WDGG3d^sǿ?G@HދUT1Νco+D"yNb%ÌƄҺ|k|=|i 3_DP$ yNֶ{{{%o(oJO+ЍC9rB,r)„F8?zVɎ;81=a%K0h ^>NyQ4ru`tќt .!3eܹDDTD$MO0caYz=e,= EC}^ʮc-#DDTD$܋ğ菣 xx'DDTD$-,,rbi\/βwXsj ɉ JDDKDږ~oco ց>Ciq(GQH""*X"V.]h'm1q1&Uʌ\{]!`HZ,Y@1ǗSrjIܖU'WDDTD$-GϪ=c',- &oQ e`H… yi""*X""_cooϔ)S^x`cّ7o^^xd2ӲysIsC{D$C_ӟߢ~#ٔ0D$DzP"Cȗ;<ͣ S0Y8" Sk0YRtܝۓ/w>$"*X""qu]L`h 'oħ4,s3s$"*X""qaF/ y.w×+QID6ހ1HpD0W{ttHA HDTDD#^,K-!04ИPZo/Mbaለ Hq ~ DTꂯ/UR8"%"IIl=c_NKaWλnR$O$"*X""g,?@F聾S WN X[* QIg fN:Wꌯ/Ub0`U)wb 32 KJ]˫/,tp_N1Ȏ;h\1+wuXYX) QI \`ACu:vR:y]0/G@2s J,Mfpu@TDD^>Ì|g×])zg"#9ڶHY'-dARb"Dj:u녃 AKDx'Wc 3fꖩKv&e 3IؗsTPl\_z襈dkVVttHG\sG2qDGN:cD_T2xTt/FqLFQD$ǰk7rn҆vK8ÑQGq6BD-,t^ H`0V3e,ίˏ?C&."^Hfinwo+xsu]pT×ebnfs#.KD+j Bqt]oe$7"rj%3_DKD$TSL!hm-o#< 8@اF:u033ʊѣGsTc(Rcř_%Jɔz?Qr,,hԂr +wfߎwÚSkHL~$c/)]:gpۗŋ3m4}l+9)ei+=X*X""MAޗ==7 G[GKiq(ǮK5ݝܹsmܸ[[[z왩I{N]Χ盵wpb%tƖm;zWxxGӴ&e|>Ö=X9ř"yq g|m0mQ QP#b/X *T@XX.]dɒ)%&&2rH,XW1cbݮ=:g/V8:̏?Ұ~TG'.ywt)3M{DD̜gzvWWWL&O1c^^^d|Ԕt܉teAhܨ 槌KJJdįZ.p wƥ^LY~Z7kSљͽ8v xf-JۗKcyb^~Sk0R-:wf@1zujccw|ї)Su.]B![ۧ| y`Օ(ƍG^beh13g%%w/v=<]}ϾŅIJJTd$wK||㿞̭[܊Ÿh+]K=X""/+`׮]|Y&>2dXNܺK^T8ﻔy#]kg@>㓞3ǍaI\tbEһgv3]Zݻh֔! ̀uL1-""/ɓ/ )Kz0qڈџxF&=X""/|I+W)ĵDDKD$i޼9͛7k%%""[ z? d(33,,P,aDDDD2`[X\dFI6*X""Ք*mOty^/SNadB)Y *Y>hTDDLdyؗ)B:uhվ3N+k),*fˆr)I $0Dxnk~K, ..;w(Lkkklll HiQQQ,,,QQÂ# IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/stats_avance.png0000644000175000017500000000154312014170666025114 0ustar georgeskgeorgeskPNG  IHDR?J pHYs  tIME,tEXtCommentCreated with The GIMPd%nIDATx휻v E,[m 0^{Wax{ٶ],ŨzE>6&8%ly%eg?ktC|J>U}\(v@sM\i5I8;Db64e f"~YCX| ^^EӜTbcQu -G$vYŹhT5o-+_e,zqj1ѬQY~UT:c+w&d!$j-[ YURFjvq+O='{\߯,SXj6UYLsث AI;z<=cFٲؘ)vumb^"-cXO!V(3;z52]KפGtҤ(Qh%{ebU⋿?Fea4h?O^i Ț^gxi4mzn;KTcvijBiuKfЛPgnƀ0u|1V4A?{gf>5 a3V""  |D&p %33|_IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/stats_experience.png0000644000175000017500000002007412014170666026006 0ustar georgeskgeorgeskPNG  IHDR2 pHYs  tIME7숬tEXtCommentCreated with The GIMPd%nIDATx}xU_Ziz{#ueպ,UQˋ+E- (W."u/$*6ʪl s@LǤa&Niz2~MLs/g$C[zW^y"|~ivv}#xƊ4Ih:evm#3|ass3@D{aȰBH+UUU8 :N@ Ч_9Q.֙L&"2L&_xAxQ_8gu٪ۑz霥>BɒяA{e\qڋ ^~`ٮbuɎs ص9TǠ򿯆"A8- "%Nn#B88f svm2P/AF=wMbx\..ȪB#k\Vj&}.Y̝~ڃD"9|7w46"., "^!yihR~m%Uzn~qW=ND?iY:/?1JnBz#ZwnUN.5Dd4Wz!wHp! p"ZZNMέB A"\/9t~ޏ9, w."[e-gq? eD4KV"qvtkܶBv͹~J?@D7E"OhtɚY҅|Ypg:6߬Gz"ʩDt DԸbfkOKךe3K*JDuUED4ɷVNiq³ы ~.o?7řVm!sGsYIT{_?UVE|a/":OՏՏѡϞҲQOPy OѷJq1AcJls?FDCov^ '}=sю7KWz9܈U1w̽cf{D4lͳѦ5I$/L#[笿mz"jZu'?Q"Zl"ADK}rl7ѬeKD_8V|OoGѪFD|G}pͯ7/|LDO?4yT{^0G%8s!:_8w WZKi8/+^=Y翦~ަ9ɷa:K+\VotG @o":\aW=[BD7TGO2~ƕ=s:g9оeso7.MH$o,q·7pN }% \DdC1Ru y`޿-9:`Oc!㖵ku-ۏ?s˕QwRG7?̔^?;<^?455lm&.dcYl'ތnRW⛥׸꽈K>g?{<4>oՖWmyQk\ѾJz|b"{^`JP~/~ @ ͤG6 q* ]r4~ @3JK;gϞ-ޓiBcvSQQ8g$eg'X9LSO"eǺ~oUޕ+.}y_1+{ȆĥcFѥ&ZO"eSΠ(њnrX1!@/䒈%i42z%-݋d{ C#|]c~ 1s KbUYdKWYY??rIB# KK]hs3iNVTƒn߾l@ T8vX٬DiS{;G] 0/q:v.~ĉ'M}&N0=aHn7zF3.Wjrqf6dX!x<.KZdUC644Ԕmum#XDy\"yhUTP~>uu( g!̬^TPp8RĆqYVՊ@V &]FhDnb!d6֙;$N[pb5+8u%i$! RUU\^qTz$[`K:|Nr @D̈́d9uV*9dFܐ :R&`%$}\?Ҳy`*p/@֝[!kp|i(L+nPJwA}KhYK`4nG^/IH~V6_Pٵ2%(wqG{xݮ̑;@J}[IYsG&"2qHZX hvO|e`0=L(6YZHolxgM`ضѶOD68=UeDFvIo֢sI+ }Li4c _"m+іTNԥ\E:m]DZLăI1\k;ʉC%Z.esZpo1E% 5+K~l0(6~"XJ$ ;0@FB?u) eg.Hr r r r r >8:jlr$J(ry<or ӚG&r rIC`ʣJqLh1msE&D3Xm*$EK JDִڋ{|4-bg"s'X/ke!. LKiBi}[비:Գ_/ђK @w;Cݶ3P9*x=9Ĭ {I:\K G݅q~ϗHDq~ؐӦ)[RKͺt8)'zx6V?a,%Nb#x[@00ia(8Nw#޺sO'1}82lX7%;DDDVGKyiq1^oda QS=#rZK𖩾˥K38lܶ]ۆ +T -׈ԯ\MR`m;]4^jlry<oD5^U71 cwf"?YՐ 5ee::l][8"B/D^777WaԒW38O< <_DО竨|x0zQAH C%VjWX}y_~qK-vk >.--~7 DfL6DV6B[%)Dd!,,YKlDdBh2u)`85ˆW* wLۉlD}uP4% YX:YWYY%H =aKKKK\.SYYo x<*֬t tOHs)HMh|ۍcSNe.o`T ObKda'uS(H'.$)`$ .LZ%Nn"<KxK$ [İ r] Hn7Lg\.]  8l6xjȰˆW\GrݡGȚQ0_]\22bغslP\-t`͊Z[<; Kȸ?Jv  #|>Y]Pj'b1D:۩] `$_S{x0y^&- iюzxIHxI%-RDEbXOi.$[zF3..LZh Dqq6M۶kېa7&>^/#^<0% ! j6utغqD,^^onnJÒ%fSq6exBy܃;Sd=/WQA< afuÑHZV5 443hSq7jA# adKde#["YIb NDXBEYp ND Y,,&Y&-䊊g#U^?"4a 8җiSYTі\Bv5LZ%~\ҳͫ!d 5QbxlV^zqK2%7ڎ".rI2Q4~o^Lqm"mSeG? Nv鹵Hz\=wv0{|iA8>r$LKd=V28nSA(rFQt-;cf.`\}͔ʡc;-ʭVZ\Wď?$~K r r (yx\9@d|ҼaLi^|0^%\%~tzx6V?a,%Nb#x[@00ia(8Nݎv@Vn|>xygi?|x"<گ~h" ԩO{ .'2X~uN2@pO0 r r r r ?''fM6ԩS8%Yby~w\pҥKqKbqƲ.袋.*--}7qKb{.L߿޽{qKbsԩ} }=}42rIl;@ L>}:++ g u% طo0gz>+Xd}_|رcG}Ǎ@kzkРA6ld:g뮻n֭n{<5k֜7iUe.33b$,fs>}tv}f3_/ZhΝRNhCm6fr-T@zUUU/ٳsssy_zW^7|b"!>8An@`ݺuǏW^;El߾;1cFKK˄ "™zkvχK, 'N^RBԘL&D^$|'h#G۷n;on޼y̙ٹSL9t!ߢ~{$eҵr-6- 4%V}Xoq߾}{ꪫ&Md.[(,,:u0v J\{8qY9OlJd1͝;O>#>BxxׯqF*T[[]w4h ? \f3fɓ)a >`ذaF-ߴiSaa uҦ͜7w(`/L۳fzgo߾vڵkTٳgեC"1}5j_|ar>}9RPPPZZqdz>v-L8ѨǏn޼v)G*g]֬YnݺV*++~; /LQX~5\3h ÿeO>m 6^u\3&~w/Rt^zѣG-̘1vsgϞGo}ر~رc֭GO-7tӖ-[8;qDcc T˗:t^{Nǿq8q< E3XK/4f̘ /{=zhРՍ7.+++//HfOywӧ%\%%D.>O!BjC7WVV$K.\"D!BДaĹI\rȑ$!B⻊w@.@.@.@.sly~5IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/diag_barres1.png0000644000175000017500000000177712014170666024775 0ustar georgeskgeorgeskPNG  IHDR'9' pHYs  tIME-tEXtCommentCreated with The GIMPd%nuIDATx흽 E_]yM"h]@B90,pz.p*~nn7Bwwx>x<7@&z&ן%k~)<{9\L/~XlkovUh'WGOmE .s=5H35^7,5!-tⲯ" ;>}VAzr시p ]r)q\qG9!ᤗnqP^.ެXܹ9]t鵚/P\ ˓aa^ެʢK, n3=6ɂfv4",6DER^&U q(wXKپlbK>[6;7d0|ͷ e/גeNoEff2=jDi= C}$m9WYY6cfܛ*Z Ib -2^NmaI=-#C90Nux(oi:׾5b2ĵ&5y]܋eqKW~|Pp6 6zSǀsu:P AnujzO\SՕ plYcmQ8t|u^Jb8i2 P1Dmf$o`_ߥXrtM?x?}4k'#4ԡkWq#G{vaưxqBxCZ^~ 烜 x7x7w0C$IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/histo_1.png0000644000175000017500000000153712014170666024012 0ustar georgeskgeorgeskPNG  IHDRԧq pHYs  tIME'7tEXtCommentCreated with The GIMPd%nIDATxr0 EC-$.LeK`3eY$eyt!y S2y>S; y zz1<BiZp'23YܻQ(,ٽ */\t$CO =Txφ_ϩGd{1P6ONiC=0zߦ4 l?/*5 2o L>EZ'[SBi-qi/qrf_mrj X/@.B$[MZIa1j'mHI;3e)wwͅHy ? k$W䆨Ejѭ c~6|Q\Əƿ \scLz\.c;mѶݬ9FNo=<|r^PO0<0t[[=" T'˰Jx?L=GI:,\Flsc{FC[F!&L~s-UCvh6XƭIkVNN~V<a{\N=6-24/Ϟӓ̚ScT-V`NdgP=ďz3ڿlb߉ S$R7qW}y SW!H=IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/diag_baton_0.png0000644000175000017500000000136412014170666024750 0ustar georgeskgeorgeskPNG  IHDR4#e3 pHYs  tIME:5,X5tEXtCommentCreated with The GIMPd%njIDATx=N0cm@@I1(nЁٯ8fcy xǯ's<; Z(ƘSxK@P1?W֣\c3x!wNe@\­( {w-U틎)B y}a UwnE%r(PN7%+A7%#IMT Yi2;ӳX=(vV;f nkxݬAb^_grQ41$Y @(P P#}S`%upS[5Q=jO}/[4͝X$@(V=](lш@vڹw?_s#C6a-aHyQ2i^1I}ExrkՄF% {2`!%AQh+vb< ĵ(xiZQdG3hfι Q4 Ыds܃EF뜩|@eEI(zNogZϥ].'Jjw .N(ixgdm@Adm@ن:yIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/diag_barres_source.png0000644000175000017500000002623012014170666026263 0ustar georgeskgeorgeskPNG  IHDRꫯ:uC=$3O~ ttt={\uuu H$+m#<{Ơ}xOH$ivK./˵^+㈪aoҤI8ahL.>a͚5O#G|-Z/>Ϯ+2vX}ХeeezkKK>Weˆ^VVv7;~7գ͛Ǐ_ZZ:~w}Wk׮o/--|w鸤uuuÇ4hwǑcǖ^q5k֌5jРA?Z[[-SE?~EEEEE󻺺f_dOi,u*G~BJ|y7%K9s楗^̮@O$'{655uww+Ovww8p555K,ˬ^:}zذa1c|g,O10`xȐ!gϞ7խ#F444  /4iiEgg~JR?1bxL=n79X---#GdwI.nbʬӤȬ\WA3{(j8/cC[=n勊Mu779` &뎋Y\\l~mnn^hW\QYYYRRP2Hms|iA\ힶ N#rfEy9SUUu)c!S?3g8**씯ȑ#|=7p?|roooM7D6o|AXlĈ2+/qJkkkUUUFV_Fx]Lu 5%;wMN8ӳ{iӦӧNjժs۷oƌFM6:tHZul׷{}{z֮]{сVUUwyuN:}OOeO?tҤIƍݲe_vbׯ4ix7ÇojjO1cFCCC{{{{{{CC32bӤȬ\#@P^S雾+W5JĶmXlڴiØxܸq6m26߿rѣG?3gΜ/wq뜩S80^~bcƌ0`1cVXa>p8puuuzܺWyWQQaWggy4M4m޼y旖9=m^m&Gfz㬎Oe5,/ |,DԕRȕ1cGdyc@% =Y1ǘ [^>cy5[aB g؏)efO(3ŭ植gJu|wѭ#" xjdf9{~AP6 .d0/h2[!%Y# +P=uuutJD<W%qH%#4bi%Yi1FF,R^^ΊP=)8k {B J%P@(B J9BUU-OA(ȪbuuuD">xߺ2E/lLTtvA+cJ:qsIU#J!$%0;D(:؟QC&[9; ]! f1D#;)7XK<5HavJ)_S<=EpB#%D"+z4P=x<OѨ iƺP%c$hY"%٣B6 PJ%P@(B JPGw7?N4ba~H$TUox,SeFuzzEe172E\P@8$g0/Dr",q7.^bTQP#@.Eq|vNp;;c?;uʼd[40 j *0I_8 Pp%S~ȧ _ kod_u.J)FhXO{s1A&y)/8l_:V˥+?iY)|&-+j4P=x<OѨ iƺP%c$hY"%٣B6 PJ0 JP%nydùs-^ԟo&_y啬) uuumܰQ}o+uJP19dȉ!̭$ݶ@a5%|-OCҼe -o) CO ~W>B(D"!2}}.˙MWn%6;.2% ;J!2I,3f=yK͖Wtk@f*B 7KT,&w !y.i&ks_p㤏=>}ȇx!PIsq$aq|H 2Jҋ1eʤ3%oKK m?u'~#?|MqҊPQq͂#98!0{0f) m߾}%EazWeС)B4uC_[[vNQ]ov~uuٳ2^(Xr7%>%Rm& ¶[9P OvCƏP@b_3fmo+o<z0rCIknױqI i-TF,̑1Ӏx^GeySS1A6[^ѭI[B 7K?$[ͬ5^d)%L*^ӂncgC>Oi kVmϠdG5=r/X.tK 2I^LI+(BV/z^ Z{P(6E/qIX6&ɿS),L8}BP@OJ@D"Eb_= j+@x<DhT4Mc@(Mӌ1h4ܬgrV QUUO!YH(X\ %@( B ~7 %d>^)%( H$TU5է;Qk/B RY!^F*P3K]U Cp+ O(b #%(SSHVB m_EB7[n"ȴiӚq@(9sɓ?#GL0o@d+V:tC=k.zJx [ouex{祗^@Dz[nҤID"+%Кeq%u֛oyɒ%H$bVeq Sa2bcK=-Ow|Q{Y$mcdZK(@9;ݍǖ)2I?R '! sY.-$mm'z% 3usx%a^ B ?#KfW'},O֓i+==={oEEifjoo-c 8lB@·~z}ѡC x:Fd`y,8Oa.#3E|7AnRSz@nW^yeܹw}y?'Z ed$cHZm/g_q ]a{%ѨxG=%eee㲲zOrJݭ?4hO?/OLJ6Nr{7f̘+VmJ^(я~̙3O޸qM7D@+׿_^~9x =[nY~== {֬OO.** a2c̙3CҞo}[[lYxu}ٯlBȁPRRR@G}啞WƎ c^xo~3T-***;MO^wuӧO/~1b6$d b`7  F;;;[Z'N?\IYQ]]],`<WEBQJ¸xzYps_tJw)TWwWoAz:h׮wԎ|;G=#V{@B>hn a)F͚ Etvv +))).NlfoooOOOpРAɢ/Kj8_Q*...)) g<ٳgٲe2+"Y z3oI~+**x7`>iii_~_}աC'HS]𤷷;v͚5$dD1]m/7 CN^ܒxw}wڴisٷo-w"1/ZIHO wg#%YW^yѣGC{& W9󻌔 W1hٰaÔ)SSLgdj2J?0u,cp+#ɸ%>F1,AP%O'yQPfϞ>|XzСٳg3͍7~|e,SYt)絼ڌe!ByjmPQZZpµkO׮]{*S/Svi@CرcJKKwy'eeezkKKKrϟ_QQQQQ1^qy͛kxjԨ'Ox`{{{-]UUթSrn [[[lB~w,@(Iƍۻe˖<[snڴĉ===w6m[yn1cFCCC{{{{{{CClkkk7msСx Ǐ9r5װ1# G 1l޼yܹ k;gK#_{C Ytw9Xѣ+l7l_z'Lkp͚5?[2u&ۈX,Dbw|U___WWG?@%i %P@((|'@(a/&x<(J,4@ r$G"H$RSS(J4՟={)xGY~H1G3[@}8p@__(۷oPz޼yZ<۶m-UUUU .q+/>Æ Kx,,U)izۜj0lż UUU_~yii |g޸qc".}zUUݻn%on]wuԩ,,xn݈NUUw?)0s̗_~yӦMs>EpC nܸ3_8" qk׮=z;qS?8`L7טbp"HN5fY46u[&] B 궶SN]x +++'O|饗^tED[ܳgUW]5xKKAEx IDATKc:/Qq^x͘ݨ3i-րw kӤl{pb> X;15Xn1B0Rboc#n`z${ -kJ ?~ȑ#J$cC:/-b@%ׁ8}f͚5? @Up Omd05|Y#%ozꩼY˹UCxELII[iAN> ~S~1mC+k"7ni@M{}tH}(37@&v8iOF֚/ \0y涖V%ŒtLZw۾5Owa@)Gq?VWų6[ĉG~k,s鋏|)L g:,;- B %$ᶗܷ?%w{ 3I2jO'p.5_qB3+R8$7 G2mFd&TMrf-$.]%ŧӌ)%)RO:.}tk,%Scom o ;tNd:'_ mH/X)a $wqxwaRNAIŭ5EJڗxJ.tw SN_%$H>D)w|:@qTlWjѧH"C  c9!Jz`sS;K&=:.}b/ft.A ,/6?㩅}$S-K+=@B'\a.; ;% >-@(HU1]=&~=M#@8}%@( B J]Y}}=P\sN:úgVJߎ;y/3)B ?UU<|pH$r:7t(Pb"@đ1b,'#XʛQ1Y2/S^Ɯ$_Լ w[?ȼ:.PH~X5'qcKIK:ND"S[P";$ U ^jjJ~A&%dzH>sLq'y]H$,OͶ _`e~ QxHd8&AHҺĸPOçdyALII_+_jDry(P)~|E\^~xYTO/dFJS>E(I3eO{P( ۺgVD?OݿtoP%@( B J%P@(JPBbJ28izĺ:I"L1&Z (Nc0)M2r2ȯQUU͉!HX8 ɹS򟫑ۡDU&]8vb%P(14$|h@B G鎔6/0cU V (`[0?5',4@(B Y]P%@( B J%PUq!/|}}=[ X Hٹs'=N`3c3cKcP"kǎtOmX5%P@(񕪪_q ɹ̓@n_!3WH$kMz`B %i~ - SZrer|l؀E}gG#山0&-1Eqތ $ }e.)D)2IIˁ3i^ nq9wMCy" vIA d25 ~o*n0ߛyF7=oMN~4 JNtw߇sm-䓜d0eBI? $9MLyZkXF< xd/G9B7K)}\NmJZA0BIV?˔?M:]NqYju0Wۤ|"O0L"޿$vrK2>K,e>KwW&ڠlLSr sMf-AJf2"%wm2>KY?#YU)ì e i3sT웽x&*MUޏJ.f>[ƧSp @(Ikm縻:KwXK,#:I+Dj0#4LpH0`#Ib=}poM;G20 4OI.OA$ם쌧)o|%i}vc3cK#$c|fb@ouJJ s |=&- -lf`KB7P@(B JP%@( $uzz JݿtoP%@( P(ʝfHIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/histo_2.png0000644000175000017500000000071712014170666024012 0ustar georgeskgeorgeskPNG  IHDR3l pHYs  tIME1tEXtCommentCreated with The GIMPd%nEIDATx1 EqŽl3>dءq$HiMk #1@t TP!@Ҳ6c? a1f].Kz9cO͊P(A ۅYȷoI܈Mk33Jg#Η{!wlDdo6M= #Ĩ Gi &3lL<7lfZCvY:zw"Fu7mxiT[]Vc+%J$;bTQr1[+ Y[.-um/< t,@EBi~P< D(:~!@T(]ݡ#$IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/morceaux_graphe.png0000644000175000017500000000665512014170666025623 0ustar georgeskgeorgeskPNG  IHDR  pHYs  tIME  >ZtEXtCommentCreated with The GIMPd%n #IDATxmPTq]yZAX>a63w4DSd k1݌IY;>aRVq6䎘1Z ȃíF -AC]`Py,i e.\'-g9jq3$Yo"a_|HH8q$v$ %I:rwjnnN+((`Ã#ɭdY^|yTTѣ{$"]ZZ$IVb<3l~pծɓ .l2222##"i$IF"B `OLLLNNfGRTT{t:]@@@qq1;:wK߮Ff$IϿ[nuH" H" D @$D @$-xҥ2IzBdzH-? p @$D @$ H" H"D @$D @$ H" H"Dm|ec،ȨH@#jjj&NXۿ:*=N;  GKz;V[kULbh64oذp$J7ߢtʼnnQ}"5*_A=HL[Nzj__xvڭ\{{'v~C e2Ing 8CgT*!$kK./.4h̙3j5c!ZF g\@1˸.|s (+`(FA$P#wǪ@祟O9̓Q e',fDeсѪ~B$Plk>^z|HlO1$1 "uH$p`[5k@YUCuq~@ٖ3[D.]Ъ=3 "ëu,#+cucܽ@,˻vNeDe@]L&,bDe"ӳD%VN=Q ;%2 [뿫nS@ٶmk2"~|c젱H2 :}^2 "2cqm8W#Dr. ⳌDe,۽b FA$Pf`~lcQ g?(07YF"2Kt鹣2 '憃KKi,BdYv>z^t}TIO7ydI.99̉ gt[Z-Ƭ7nwn05p4{K^zˆ=Q>*+*~́HPFiә)ܗHБ11 H"D @$D @$ H" H"D @$éH\+GO[.Y;Q#g0("㊄bDW3w!8fo؝[[v|Bw-VzUӷqv---6-g:lҲѾ?_Zys=ssl-?'TK 0hKGzd">nuuuZ6""СC]w%F6 kE$D " g IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/doc/images/calc_exact.png0000644000175000017500000000074012014170666024525 0ustar georgeskgeorgeskPNG  IHDR% pHYs  tIME  -:qZtEXtCommentCreated with The GIMPd%nVIDATxX;0 MP:v}ڱCC$ى|A'hMm3O&KƤ@<-"jl);0ιu]a0ιcNy0y5 ] pRkt@NSݗyw喪-eqip&Z=A0s,Pm젼~h 1B:0Ia'XuK_#z"@琬pÖa]Ō:R@^gbl y Mk,KeE(M%Q/m_h$ǒ~HB'R@gS$E-%~qF:+{dKqs}t6IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/0000755000175000017500000000000012014170666021336 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/mathlib/intervalles.py0000644000175000017500000005035212014170666024245 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Intervalles # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re, math import numpy from sympy import oo, sympify from sympy.core.sympify import SympifyError import sympy from ..pylib import print_error, str2 from .. import param from .parsers import _convertir_latex_frac class Ensemble(object): def __new__(cls, *args, **kw): if len(args) == 1 and isinstance(args[0], basestring): return conversion_chaine_ensemble(args[0], utiliser_sympy = True) instance = object.__new__(cls) instance._initialiser(*args, **kw) return instance class Union(Ensemble): u"""Une union finie d'intervalles rels. Les valeurs numriques isoles sont converties en singletons. Lors de l'initialisation, elle est reecrite sous forme d'union disjointe et ordonne. Les opration sur les unions d'intervalles sont : - l'union : symbole "A+B" - l'intersection : symbole "A*B" - la diffrence : symbole "A-B" - le complmentaire : symbole -A Note: l'inclusion et l'appartenance sont notes de manire identique: "y in A" "A in B". Note: pour des raisons pratiques, l'ensemble vide est considr comme un intervalle (sic). """ def _initialiser(self, *intervalles): self.intervalles = [] if intervalles: for val in intervalles: if isinstance(val, Intervalle): self.intervalles.append(val.__copy__()) # important (ne pas travailler sur les originaux) ! elif hasattr(val, "__iter__"): self.intervalles += Union(*val).intervalles else: self.intervalles.append(Intervalle(val, val)) self.simplifier() if len(self.intervalles) == 1: self.inf = self.intervalles[0].inf self.sup = self.intervalles[0].sup self._inf_inclus = self.intervalles[0]._inf_inclus self._sup_inclus = self.intervalles[0]._sup_inclus # /!\ faire le changement de classe en dernier ( cause de 'self.intervalles') self.__class__ = Intervalle else: # ensemble vide self.inf = 0 self.sup = 0 self._inf_inclus = False self._sup_inclus = False self.__class__ = Intervalle @property def vide(self): return len(self.intervalles) == 0 def __iter__(self): return iter(self.intervalles) def __add__(self, y): "union" if not isinstance(y, Union): y = Union(y) return Union(*(self.intervalles + y.intervalles)) def __radd__(self, y): return self.__add__(y) def __mul__(self, y): "intersection" if not isinstance(y, Union): y = Union(y) if y.vide: return y else: return Union(*[i*j for i in self.intervalles for j in y.intervalles]) def __pow__(self, y): # alias pour l'intersection (le symbole '^' rappelle vagument le symbole 'inter') return self.__add__(y) def __rmul__(self, y): self.__mul__(y) def __rpow__(self, y): self.__radd__(y) def simplifier(self): ints = [intervalle for intervalle in self.intervalles if not intervalle.vide] ints.sort() #print ints #print self.intervalles for i in range(len(ints) - 1): if float(ints[i].sup) > float(ints[i + 1].inf): # les intervalles se coupent if ints[i + 1].inf == ints[i].inf: ints[i + 1]._inf_inclus = ints[i + 1].inf_inclus or ints[i].inf_inclus else: ints[i + 1]._inf_inclus = ints[i].inf_inclus if ints[i + 1].sup == ints[i].sup: ints[i + 1]._sup_inclus = ints[i + 1].sup_inclus or ints[i].sup_inclus elif float(ints[i + 1].sup) < float(ints[i].sup): ints[i + 1].sup = ints[i].sup ints[i + 1]._sup_inclus = ints[i].sup_inclus ints[i + 1].inf = ints[i].inf ints[i] = None elif ints[i].sup == ints[i + 1].inf and (ints[i].sup_inclus or ints[i + 1].inf_inclus): ints[i + 1].inf = ints[i].inf ints[i + 1]._inf_inclus = ints[i].inf_inclus ints[i] = None self.intervalles = [intervalle for intervalle in ints if intervalle is not None] def __str__(self): #from .custom_functions import custom_str as str return "U".join(str(intervalle) for intervalle in self.intervalles).replace("}U{", " ; ") def __repr__(self): return "Ensemble(%s)" %repr(str(self)) def __nonzero__(self): return not self.vide def __neg__(self): u"complmentaire" return reduce(lambda x, y: x*y, [-intervalle for intervalle in self.intervalles], Intervalle()) def __pos__(self): return self def __sub__(self, y): return self*(-y) # "soustraction" peu naturelle ! ;) def __rsub(self, y): return (-self)*y def __eq__(self, y): if not isinstance(y, Union): if hasattr(y, "__iter__"): try: return self == Union(*y) except Exception: print_error() return False elif len(self.intervalles) != len(y.intervalles): return False elif self.vide: return y.vide else: test = [i.inf == j.inf and i.sup == j.sup and i.inf_inclus == j.inf_inclus and i.sup_inclus == j.sup_inclus for (i, j) in zip(self.intervalles, y.intervalles)] return not (False in test) def __ne__(self, y): return not (self == y) def __contains__(self, y): if isinstance(y, self.__class__) and not isinstance(y, Intervalle): return reduce(lambda x, y: x and y, ((intervalle in self) for intervalle in y.intervalles), True) return reduce(lambda x, y: x or y, ((y in intervalle) for intervalle in self.intervalles)) def extremites(self, _min, _max): u"""Retourne les extrmits de chaque intervalle. Chaque extrmit est donne sous la forme de couples (_float, _str), o _str peut prendre les valeurs ".", ")", "(" ou "o". Exemple : >>> from wxgeometrie.mathlib.intervalles import conversion_chaine_ensemble >>> E = conversion_chaine_ensemble("]-oo;3[U]3;4]U]5;+oo[") >>> E.extremites(0, 10) [(3, 'o'), (4, '.'), (5, ')')] """ extremites = [] for intervalle in self.intervalles: if _min <= intervalle.inf <= _max: if extremites and extremites[-1][0] == intervalle.inf: extremites[-1] = (intervalle.inf, "o") else: extremites.append((intervalle.inf, intervalle.inf_inclus and "." or ")")) if _min <= intervalle.sup <= _max: extremites.append((intervalle.sup, intervalle.sup_inclus and "." or "(")) return extremites @property def adherence(self): u"L'adhrence de l'ensemble (ie. le plus petit ensemble ferm qui le contienne)." return Union(Intervalle(intervalle.inf, intervalle.sup, True, True) for intervalle in self.intervalles) def asarray(self, _min, _max, pas): u"""Gnre une liste d'objets 'array', correspondant chaque intervalle. On se limite des valeurs comprises entre '_min' et '_max', avec le pas 'pas'.""" arrays = [] for intervalle in self.intervalles: inf = max(intervalle.inf, _min) sup = min(intervalle.sup, _max) a = numpy.arange(float(inf), float(sup), pas) # Il faut convertir inf et sup en float du fait de bugs de sympy. # En particulier, 1/0 == +oo (au lieu de NaN ou zoo) provoque des bugs # dans l'affichage de courbes style 1/x sur ]-oo;0[. if inf < sup or (intervalle.inf_inclus and intervalle.sup_inclus): a = numpy.append(a, float(sup)) arrays.append(a) return arrays def evalf(self, n = 15, **options): u"Convertit les bornes de chaque intervalle en float." union = vide def evalf(nbr): if nbr in (-oo, +oo): return nbr elif hasattr(nbr, 'evalf'): return nbr.evalf(n, **options) return float(nbr) for intervalle in self.intervalles: union += Intervalle(evalf(intervalle.inf), evalf(intervalle.sup), inf_inclus = intervalle.inf_inclus, sup_inclus = intervalle.sup_inclus ) return union class Intervalle(Union): u"""Un intervalle rel non vide. Les opration sur les intervalles sont : - l'union : symbole "A+B" - l'intersection : symbole "A*B" - la diffrence : symbole "A-B" - le complmentaire : symbole -A """ def _initialiser(self, inf = -oo, sup = oo, inf_inclus = True, sup_inclus = True): self.inf = inf self.sup = sup self._inf_inclus = inf_inclus self._sup_inclus = sup_inclus @property def inf_inclus(self): if self.inf == -oo: return False return self._inf_inclus @property def sup_inclus(self): if self.sup == oo: return False return self._sup_inclus @property def singleton(self): return self.sup == self.inf and self.inf_inclus and self.sup_inclus @property def intervalles(self): # La conversion en 'float' est due un bug de sympy 0.6.3 if float(self.sup) < float(self.inf) or (self.sup == self.inf and not (self.inf_inclus and self.sup_inclus)): return [] else: return [self] def __cmp__(self, y): c = cmp(float(self.inf), float(y.inf)) if c: return c else: return cmp(float(self.sup), float(y.sup)) def __mul__(self, y): u"intersection" if not isinstance(y, Union): y = Union(y) if y.vide: return y elif isinstance(y, Intervalle): # il s'agit de 2 intervalles if float(self.sup) < float(y.inf) or float(y.sup) < float(self.inf): return Union() # vide elif self.sup == y.inf and self.sup_inclus and y.inf_inclus: return Union(self.sup) elif self.inf == y.sup and self.inf_inclus and y.sup_inclus: return Union(self.inf) else: # les intervalles se coupent if float(self.inf) < float(y.inf): inf = y.inf inf_inclus = y.inf_inclus elif float(self.inf) > float(y.inf): inf = self.inf inf_inclus = self.inf_inclus else: inf = self.inf inf_inclus = self.inf_inclus and y.inf_inclus if float(y.sup) < float(self.sup): sup = y.sup sup_inclus = y.sup_inclus elif float(y.sup) > float(self.sup): sup = self.sup sup_inclus = self.sup_inclus else: sup = self.sup sup_inclus = self.sup_inclus and y.sup_inclus return Intervalle(inf, sup, inf_inclus, sup_inclus) else: return Union(*[self*i for i in y.intervalles]) def __neg__(self): if self.vide: return Intervalle() return Intervalle(-oo, self.inf, False, not self.inf_inclus) + Intervalle(self.sup, oo, not self.sup_inclus) def __str__(self): from .custom_functions import custom_str as str if self.vide: return "{}" elif self.inf == self.sup: return "{%s}" %str(self.inf) return (self.inf_inclus and "[" or "]") + str(self.inf) + ";" + str(self.sup) + (self.sup_inclus and "]" or "[") def __unicode__(self): if self.vide: return u"\u00D8" # "" ; u"\u2205" ne fonctionne pas sous Windows XP else: return unicode(str(self)) def __copy__(self): return Intervalle(self.inf, self.sup, self._inf_inclus, self._sup_inclus) def __contains__(self, y): if self.vide: return isinstance(y, Union) and y.vide elif isinstance(y, self.__class__): condition_inf = float(self.inf) < float(y.inf) or (self.inf == y.inf and (self.inf_inclus or not y.inf_inclus)) condition_sup = float(self.sup) > y.sup or (self.sup == y.sup and (self.sup_inclus or not y.sup_inclus)) return condition_inf and condition_sup elif isinstance(y, Union): return reduce(lambda x, y: x and y, ((intervalle in self) for intervalle in y.intervalles), True) else: return float(self.inf) < float(y) < float(self.sup) or (y == self.inf and self.inf_inclus) or (y == self.sup and self.sup_inclus) def _remplacer_virgule(chaine): u"""Remplacement intelligent de la virgule par un point ou un point-virgule. Dans la mesure du possible, essaie de deviner si la virgule est utilise comme sparateur dcimal, ou entre deux valeurs. Dans la majorit des cas, cela permet de corriger une utilisation incorrecte de la virgule comme sparateur dcimal.""" if ',' not in chaine: return chaine elif ';' in chaine and '.' not in chaine: if param.debug: print(u"Warning (autocorrection): '" + chaine + "'\n" "Utilisation incorrecte d'une virgule.\n" u"Utilisez le point comme sparateur dcimal, ou modifiez les options.\n" u"Enfin, ne mlangez pas les virgules et les points virgules.") return chaine.replace(',', '.') else: return chaine.replace(',', ';') def preformatage_ensemble(chaine): u"""Formatage lger (qui reste humainement lisible).""" chaine = chaine.replace(" ", "") chaine = _remplacer_virgule(chaine) # Traduction du LaTeX # On enlve le \ devant les commandes latex mathmatiques usuelles chaine = re.sub(r"\\(infty|e|pi|sin|cos|tan|ln|exp)", lambda m:m.group()[1:], chaine) # conversion de \frac, \dfrac et \tfrac chaine = _convertir_latex_frac(chaine) # TODO: tests unitaires pour le LaTeX chaine = chaine.replace('infty', 'oo').replace('inf', 'oo') chaine = re.sub(r"(? afficher les extrmits de la courbe en 1 (arc) et en 2 (point). 1;2] -> afficher l'extrmit en 2 (point), mais pas en 1. ]1;2 -> afficher l'extrmit en 1 (arc), mais pas en 2. """ chaine = re.sub('[ ]+', ' ', chaine) chaine = _remplacer_virgule(chaine) chaine = chaine.replace('\\', '-') chaine = re.sub(r"(?<=[][])\+(?=[][])", 'U', chaine) extremites_cachees = [] parties = chaine.split('|') for i, partie in enumerate(parties): extremites_cachees.append([]) if re.match(r"R-[^][{}]+$", partie): partie = 'R-{' + partie[2:] + '}' intervalles = re.split(r'(?<=[][{} ])[U](?=[][{} ])', partie) for j, intervalle in enumerate(intervalles): intervalle = intervalle.replace(' ', '') appendice = '' if not intervalle.startswith('{'): # ie. intervalle != {1;2;3} if intervalle.startswith(';'): # ]3; signifie ]3;+oo[ intervalle = ']' + intervalle if intervalle.endswith(';'): # ;3[ signifie ]-oo;3[ intervalle += '[' k = intervalle.find('-{') if k != -1: appendice = intervalle[k:] intervalle = intervalle[:k] # ]-oo;+oo[-{1;2} est dcoup en ]-oo;+oo[ (intervalle) et -{1;2} (appendice) if ';' in intervalle: deb, fin = intervalle.split(';') if deb and deb[0] not in '[]': extremites_cachees[-1].append(deb) intervalle = ']' + intervalle if fin and fin[-1] not in '[]': extremites_cachees[-1].append(fin) intervalle += '[' intervalles[j] = intervalle + appendice parties[i] = 'U'.join(intervalles) chaine = preformatage_ensemble('|'.join(parties)) #print 'chaine, extremites cachees', chaine, extremites_cachees return chaine or "]-oo;+oo[", tuple(extremites_cachees) def formatage_ensemble(chaine, preformatage = True, utiliser_sympy = False): u"""Les symboles utiliser sont 'U' pour l'union, '^' pour l'intersection, '-' ou '\\' pour la soustraction. R, R+, R*+, R-, R*- sont aussi accepts.""" if preformatage: chaine = preformatage_ensemble(chaine) def f1(matchobject): # conversion ]-2;sqrt(3)] -> Intervalle(-2, sqrt(3), False, True) chaine = matchobject.group() sep = chaine.find(";") return "Intervalle(%s,%s,%s,%s)" %(chaine[1:sep],chaine[sep+1:-1],chaine[0]=="[",chaine[-1]=="]") chaine = re.sub("[][][-+*/0-9.A-Za-z_)( ]+[;][-+*/0-9.A-Za-z_)( ]+[][]", f1, chaine) def f2(matchobject): # conversion {-1;2;sqrt(3)} -> Union(-1, 2, sqrt(3)) chaine = matchobject.group() liste = chaine[1:-1].split(";") return "Union(%s)" %",".join(liste) chaine = re.sub("[{][-+*/0-9.A-Za-z)(;]+[}]", f2, chaine) return chaine def conversion_chaine_ensemble(chaine, utiliser_sympy = False): chaine = formatage_ensemble(chaine) dico = math.__dict__.copy() if utiliser_sympy: dico.update(sympy.__dict__) # faire en dernier (remplace sympy.Union par intervalles.Union). dico.update({"__builtins__": None, "Intervalle": Intervalle, "Union": Union, "oo": oo, "False": False, "True": True, }) if utiliser_sympy: try: #print str2(chaine), dico return sympify(str2(chaine), dico) except (SympifyError, TypeError) as e: print "Warning: %s in %s." %(e, str2(chaine)) print_error() return eval(str2(chaine), dico, dico) IR = R = Intervalle() O = vide = Union() wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/graphes.py0000644000175000017500000003754612014170666023360 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Graphes # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Lexicon # http://www.apprendre-en-ligne.net/graphes/lexique/index.html # http://en.wikipedia.org/wiki/Glossary_of_graph_theory import collections, copy from sympy import oo, Matrix from ..pylib import OrderedDict, advanced_split class GraphError(StandardError): pass # http://www.chansonsdewallonie.be/PAGES/COULEURS/CouleursTeintes.htm # http://www.w3.org/TR/css3-color/#svg-color (less usefull) colors_dict = OrderedDict(( ('bleu', (0, 0, 255)), ('rouge', (255, 0, 0)), ('vert', (0, 128, 0)), ('orange', (255, 69, 0)), ('violet', (148, 0, 211)), ('jaune', (255, 255, 0)), ('marron', (210, 105, 30)), ('rose', (255, 20, 147)), ('turquoise', (64, 224, 208)), ('indigo', (75, 0, 130)), ('magenta', (255, 0, 255)), ('ocre', (184, 134, 11)), ('olive', (184, 134, 11)), ('pomme', (127, 255, 0)), ('gris', (128, 128, 128)), ('noir', (0, 0, 0)) )) def colors(): for color in colors_dict: yield color num = 0 while True: yield 'color %s' %num num += 1 class Graph(dict): u"""A graph representation. Graph are stored as a dictionary: >>> from wxgeometrie.mathlib.graphes import Graph >>> g = Graph({'A': {'B':[1],'C':[2],'D':[3]}, 'B': {'A':[4],'C':[2]}, 'C':{}, 'D':{}}, oriented=True) For convenience, they may be entered as a string: >>> h = Graph("A>(B:1,C:2,D:3), B>(A:4,C:2), C, D", oriented=True) >>> g == h True If the graph is not oriented (default), the graph must be symetric: >>> k = Graph("A>(B:1,C:2,D:3), B>(A:4,C:2), C>(A:2,B:2), D>(A:3)") >>> k.symetric True """ #XXX: doctest fails !!! # teintes = ('bleu', 'rouge', 'vert', 'jaune', 'orange', 'violet', # 'marron', 'rose', 'turquoise', 'cyan', 'magenta', 'ocre', # 'marine', 'indigo', 'pourpre') def __init__(self, dictionary = (), oriented = False): # Ex: {"A": {"B":[1], "C":[2, 5]}, "B": {}, "C": {"A": [2], "C": [1]}} if isinstance(dictionary, basestring): dictionary = self._convert_input(dictionary) elif not isinstance(dictionary, dict): dictionary = dict.fromkeys(dictionary, {}) for key, val in dictionary.items(): if not isinstance(val, dict): dictionary[key] = dict.fromkeys(val, [1]) elif isinstance(val, collections.defaultdict): dictionary[key] = dict(val) for node in dictionary[key]: if node not in dictionary: raise GraphError("Edge contain a unknown node: %s." %repr(node)) self.oriented = oriented dict.__init__(self, dictionary) if not oriented and not self.symetric: raise GraphError("Unoriented graph must be symetric.") @staticmethod def _convert_input(chaine): # Support nicer input format : "A>(B:1,C:2,D:3), B>(A:4, ...) ..." # -> {"A":{"B":1,"C":2,"D":3},"B":{"A":4, ...} ...} dic = {} nodes = advanced_split(chaine, ',') for node_stuff in nodes: if '>' in node_stuff: node, edges = node_stuff.split('>', 1) else: node, edges = node_stuff, '()' node = node.strip() edges = edges.strip()[1:-1] dic[node] = {} for edge in edges.split(','): if edge: if ':' in edge: node2, distance = edge.split(':', 1) else: node2, distance = edge, '1' node2 = node2.strip() if not dic[node].has_key(node2): dic[node][node2] = [] dic[node][node2].append(float(distance) if '.' in distance else int(distance)) #TODO: support exact calculus return dic @property def order(self): return len(self) ## @property ## def ordered_nodes(self): ## return sorted(self) nodes = property(dict.keys) @property def matrix(self): n = self.order nodes = sorted(self) def f(i, j): # In an unoriented graph, loops are counted twice. k = (1 if self.oriented or i != j else 2) return k*len(self[nodes[i]].get(nodes[j], ())) return Matrix(n, n, f) def degree(self, node): if self.oriented: return sum(len(edges) for node2, edges in self[node].items() if node2) else: # In an unoriented graph, loops are counted twice. return sum(len(edges) for node2, edges in self[node].items() if node2 != node) \ + 2*len(self[node].get(node, ())) @property def degrees(self): return dict((node, self.degree(node)) for node in self.nodes) @property def to_dict(self): return copy.deepcopy(dict(self)) def adjacents(self, node1, node2): return self[node1].has_key(node2) or self[node2].has_key(node1) @property def connected(self): def adjacent(node, new_nodes): return any(self.adjacents(node, new) for new in new_nodes) remaining_nodes = set(self) new_nodes = [remaining_nodes.pop()] while new_nodes: new_nodes = [node for node in remaining_nodes if adjacent(node, new_nodes)] remaining_nodes.difference_update(new_nodes) return not remaining_nodes @property def symetric(self): M = self.matrix return M.transpose() == M @property def eulerian(self): odds = sum(self.degree(node)%2 for node in self.nodes) return odds in (0, 2) def _nodes_sorted_by_degree(self, *first_nodes): return list(first_nodes) \ + sorted(set(self).difference(first_nodes), key = self.degree, reverse = True) def eulerian_trail(self, walk=None): if walk is None: #TODO: return an (arbitrary) eulerian trail if any, else None raise NotImplementedError # return trail # Convert input string to a list of nodes. for sep in ('-', ',', ';'): if sep in walk: walk_lst = walk.split(sep) break else: if ' ' in walk: walk_lst = walk.split() else: walk_lst = list(walk) # if not walk_lst: # raise SyntaxError, repr(walk) + " format is incorrect. Nodes should be separated by '-'." graph = self.to_dict previous = None # previous node def remove_edge(A, B): "Remove edge from graph" endpoints = graph.get(A, {}) edges = endpoints.get(B, []) if edges: edges.pop() else: return False if not edges: endpoints.pop(B) if not endpoints: graph.pop(A) return True # We remove edges one by one from graph. for node in walk_lst: node = node.strip() if previous is not None: if not remove_edge(previous, node): print("%s-%s edge does not exist, or was already used !" %(previous, node)) return False # Edge did not exist, or was already removed. if not self.oriented: if not remove_edge(node, previous): return False previous = node if graph: print("Following edges were never used:") for node in graph: for endpoint in graph[node]: print ('%s-%s' %(node, endpoint)) return not graph def coloring(self, *first_nodes): u"""Graph colorization using Welsh & Powell algorithm. By default, nodes are sorted according to their degrees, but you can also choose manually the first nodes to be visited. """ coloring = [] uncolored = self._nodes_sorted_by_degree(*first_nodes) while uncolored: coloring.append([]) for node in uncolored: if not any(self.adjacents(node, s) for s in coloring[-1]): coloring[-1].append(node) assert coloring[-1] uncolored = [s for s in uncolored if s not in coloring[-1]] return coloring def latex_WelshPowell(self, *first_nodes): ordered_nodes = self._nodes_sorted_by_degree(*first_nodes) dico = {} for nodes, color in zip(self.coloring(*ordered_nodes), colors()): for node in nodes: dico[node] = color # Gnration du code LaTeX nodes_line = r'Sommets ' degrees_line = r'Degr\'es' colors_line = r'Couleurs' for node in ordered_nodes: nodes_line += ' & $%s$ ' %node degrees_line += ' & $%s$ ' %self.degree(node) colors_line += ' & %s ' %dico[node] code = r'\begin{tabular}{|l||*{%s}{c|}}' % len(dico) code += '\n\\hline\n' code += nodes_line + '\\\\\n\\hline\n' code += degrees_line + '\\\\\n\\hline\n' code += colors_line + '\\\\\n\\hline\n' code += '\\end{tabular}\n' return code def shortest_path(self, start, end): u"Implementation of Dijkstra-Moore algorithm." # current node current = start # Nodes which have been already visited, but still not archived: visited = {start: [0, [start]]} # format: {node: [distance from start, [previous node, alternative previous node, ...]]} # Nodes which will not change anymore: archived = {} while current != end and visited: archived[current] = visited.pop(current) # We select the node having the smallest distance for neighbor in self[current]: if neighbor not in archived: distance = visited.get(neighbor, [oo])[0] new_distance = archived[current][0] + min(self[current][neighbor]) if new_distance < distance: visited[neighbor] = [new_distance, [current]] elif new_distance == distance: visited[neighbor][1].append(current) current = min(visited.items(), key = lambda x:x[1][0])[0] if visited.has_key(current): archived[current] = visited.pop(current) in_progress = set([(current,)]) final_paths = set() while in_progress: for path in set(in_progress): # print path, in_progress, final_paths in_progress.remove(path) node1 = path[0] for previous in archived[node1][1]: if previous == start: final_paths.add((previous,) + path) else: in_progress.add((previous,) + path) return archived[current][0], final_paths else: return oo, [] def latex_Dijkstra(self, start, end, nodes=None): if nodes is None: nodes = sorted(self) else: if set(nodes) != set(self): raise ValueError, "Nodes do not match." code = u"On applique l'algorithme de Moore-Dijkstra~:\n\n" code += r'\begin{tabular}{|*{%s}{c|}}\hline' %len(self) code += '\n' + '&'.join(('$%s$' %node) for node in nodes) + r'\\\hline\hline' + '\n' # current node current = start # Nodes which have been already visited, but still not archived: visited = {start: [0, [start]]} # format: {node: [distance from start, [previous node, alternative previous node, ...]]} # Nodes which will not change anymore (shorter path from start has been found): archived = {} while current != end and visited: def str2(val): # 2.0 -> "2" ; 2.3 -> "2,3" val = str(val) if '.' in val: val = val.rstrip('0').rstrip('.').replace('.', ',') return val def format(node): def _format(node): previous_nodes = ','.join(str(prev) for prev in visited[node][1]) return str2(visited[node][0]) + ' $(%s)$' %previous_nodes if node == current: return r'\textbf{%s}' %_format(node) elif node in archived: return '' elif node in visited: return _format(node) else: return r'$+\infty$' code += '&'.join(format(node) for node in nodes) + r'\\\hline' + '\n' archived[current] = visited.pop(current) # We select the node having the smallest distance for neighbor in self[current]: if neighbor not in archived: # best distance found until now between neighbor and start distance = visited.get(neighbor, [oo])[0] # new distance found using current node: # distance(start, current) + distance(current, neighbor) new_distance = archived[current][0] + min(self[current][neighbor]) # replace with new distance only if better if new_distance < distance: visited[neighbor] = [new_distance, [current]] elif new_distance == distance: visited[neighbor][1].append(current) current = min(visited.items(), key = lambda x:x[1][0])[0] code += '&'.join(format(node) for node in nodes) + r'\\\hline' + '\n' code += '\\end{tabular}\n' if visited.has_key(current): archived[current] = visited.pop(current) in_progress = set([(current,)]) final_paths = set() while in_progress: for path in set(in_progress): # print path, in_progress, final_paths in_progress.remove(path) node1 = path[0] for previous in archived[node1][1]: if previous == start: final_paths.add((previous,) + path) else: in_progress.add((previous,) + path) distance = str2(archived[current][0]) paths = ', '.join('$' + '-'.join(path) + '$' for path in final_paths) plur1 = ('x' if len(final_paths) > 1 else '') plur2 = ('s' if len(final_paths) > 1 else '') code += u""" La distance minimale entre le sommet $%(start)s$ et le sommet $%(end)s$ est de $%(distance)s$. Cela correspond au%(plur1)s chemin%(plur2)s %(paths)s. """ %locals() return code wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/end_user_functions.py0000644000175000017500000002122712014170666025610 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### #WxGeometrie #Dynamic geometry, graph plotter, and more for french mathematic teachers. #Copyright (C) 2005-2010 Nicolas Pourcelot # #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation; either version 2 of the License, or #(at your option) any later version. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode ## Les fonctions qui doivent tre accessibles par l'utilisateur final ## "from end_user_functions import *" doit donner un rsultat propre. from ..pylib import OrderedDict __classement__ = OrderedDict(( (u"Algbre", []), (u"Analyse", []), (u"Symboles", []), (u"Arithmtique", []), (u"Statistiques", []), (u"Divers", []), ## (u"LaTeX", []), )) del OrderedDict from .universal_functions import abs, acos, asin, atan, ceil, cos, cosh, exp, floor,\ ln, log, sin, sinh, sqrt, tan, tanh, arg from sympy import LambertW, gamma, Sum, Integral # Alias arcsin = asin arccos = acos arctan = atan from .custom_functions import derivee, moyenne, variance, ecart_type, covariance, linreg, aide __classement__[u"Statistiques"].append((u"Moyenne", "moyenne", u"Moyenne de la srie, ventuellement coefficiente." u" Ex: moyenne((12, 13, 15), (1, 1, 2))")) __classement__[u"Statistiques"].append((u"Variance", "variance", u"Variance de la srie, ventuellement coefficiente." u" Ex: variance((12, 13, 15), (1, 1, 2))")) __classement__[u"Statistiques"].append((u"cart-type", "ecart_type", u"cart-type de la srie, ventuellement coefficiente." u" Ex: ecart_type((12, 13, 15), (1, 1, 2))")) __classement__[u"Statistiques"].append((u"Covariance", "covariance", u"Covariance de deux sries." u" Ex: covariance((1,2,3),(2,5,7))")) __classement__[u"Statistiques"].append((u"Droite de rgression", "linreg", u"Retourne (a, b) pour une droite d'ajustement d'quation y=ax+b" u" (moindres carrs). Ex: linreg((1,2,3),(2,5,7))")) from .sympy_functions import expand developpe = expand __classement__[u"Algbre"].append((u"Dveloppe", "developpe", u"Dvelopper une expression.")) from .sympy_functions import factor factorise = factor __classement__[u"Algbre"].append((u"Factorise", "factorise", u"Factoriser une expression.")) from .sympy_functions import together rassemble = together __classement__[u"Algbre"].append((u"Une seule fraction", "rassemble", u"Mettre sur le mme dnominateur.")) from .sympy_functions import evalf evalue = evalf __classement__[u"Algbre"].append((u"value", "evalue", u"Calculer une valeur approche.")) from .custom_functions import resoudre solve = resous = resoudre __classement__[u"Algbre"].append((u"Rsous", "resous", u"Rsoudre une (in)quation ou un systme dans R. ex: resous(2x-3>0 et 3x+1<=0")) __classement__[u"Algbre"].append(None) from .sympy_functions import cfactor cfactorise = cfactor __classement__[u"Algbre"].append((u"Factorise dans C", "cfactorise", u"Factoriser une expression dans le corps des complexes.")) from sympy import conjugate conjug = conjugue = conjuguer = conjugate __classement__[u"Algbre"].append((u"Conjuguer", "conjug", u"Calculer le conjugu d'un nombre complexe.")) __classement__[u"Algbre"].append(None) from .sympy_functions import sum somme = sum __classement__[u"Algbre"].append((u"Somme", "somme", u"Calculer une somme.")) from .sympy_functions import product produit = product __classement__[u"Algbre"].append((u"Produit", "produit", u"Calculer un produit.")) __classement__[u"Algbre"].append(None) from .sympy_functions import mat Matrice = Matrix = Mat = matrix = matrice = mat __classement__[u"Algbre"].append((u"Matrice", "mat", u"Gnre une matrice. ex: mat([[1,2],[3,4]]), mat(4,4,0), mat(4,4,2*li+3*co)")) from sympy import isprime premier = isprime __classement__[u"Arithmtique"].append((u"Premier ?", "premier", u"Tester si un nombre est premier.")) from .sympy_functions import divisors diviseurs = divisors __classement__[u"Arithmtique"].append((u"Diviseurs", "diviseurs", u"Chercher les diviseurs d'un nombre.")) # Alias facteurs = factors = factor __classement__[u"Arithmtique"].append((u"Facteurs premiers", "facteurs", u"Dcomposer un nombre en facteurs premiers.")) from .custom_functions import pgcd gcd = pgcd __classement__[u"Arithmtique"].append((u"PGCD", "pgcd", u"Calculer le PGCD de plusieurs entiers.")) from .custom_functions import ppcm lcm = ppcm __classement__[u"Arithmtique"].append((u"PPCM", "ppcm", u"Calculer le PPCM de plusieurs entiers.")) __classement__[u"Arithmtique"].append(None) from .custom_functions import jhms hms = jhms __classement__[u"Divers"].append((u"Conversion j-h-min-s", "jhms", u"Convertir un temps en secondes en jours, heures, minutes et secondes.")) from .custom_functions import deg __classement__[u"Divers"].append((u"Conversion en degrs", "deg", u"Convertir un angle de radians en degrs. (Ex: deg(pi/3), ou pi/3>>deg).")) from .custom_functions import frac __classement__[u"Divers"].append((u"Convertir en fraction", "frac", u"Convertir un nombre dcimal en fraction.")) ent = floor __classement__[u"Divers"].append((u"Partie entire", "ent", u"Partie entire d'un nombre (ex: -2,4 -> -3).")) from .custom_functions import arrondir round = arrondi = arrondis = arrondir __classement__[u"Divers"].append((u"Arrondi", "arrondi", u"Arrondit n chiffres aprs la virgule-3).")) from sympy import factorial factoriel = factorial __classement__[u"Arithmtique"].append((u"Factoriel", "factoriel", u"factoriel(n) vaut 1*2*3*4*5*...*n.")) from sympy import ff arrangements = nAr = nA = ff __classement__[u"Arithmtique"].append((u"Arrangements", "nAr", u"Nombre d'arrangements.")) from sympy import binomial combinaisons = binome = nCr = nC = Binomial = binomial __classement__[u"Arithmtique"].append((u"Combinaisons", "nCr", u"Nombre de combinaisons.")) from .sympy_functions import limit lim = limite = limit __classement__[u"Analyse"].append((u"Limite", "limite", u"Calculer une limite. Ex: 'limite(1/x,x,0+)', 'limite(x^2,x,-oo)'")) from .sympy_functions import diff derive = diff __classement__[u"Analyse"].append((u"Drive", "derive", u"Driver une expression. Ex: 'derive(2x+7,x)' et 'derive(2x+7,x,2)' (drive seconde)")) from .sympy_functions import integrate integrale = integral = integre = integrate __classement__[u"Analyse"].append((u"Intgre", "integre", u"Intgrer une expression. Ex: 'integre(x+1,x)' et 'integre(x+1,(x,-1,1))")) from .sympy_functions import series taylor = series __classement__[u"Analyse"].append((u"Taylor", "taylor", u"Dveloppement limit. Ex: 'series(cos(x),x,0,5)'")) from sympy import pi __classement__[u"Symboles"].append((u"pi", "pi", u"Le nombre pi (3,14...).")) from sympy import oo __classement__[u"Symboles"].append((u"oo", "oo", u"L'infini positif.")) from sympy import E e = E __classement__[u"Symboles"].append((u"e", "e", u"Le nombre e = exp(1).")) from sympy import I i = I __classement__[u"Symboles"].append((u"i", "i", u"Le nombre imaginaire i.")) ##from custom_functions import pstfunc ##__classement__[u"LaTeX"].append((u"Fonction pst-plot", "pstfunc", u"Conversion d'une formule pst-plot en fonction.")) __classement__[u"Divers"].append(None) from .custom_functions import pstfunc __classement__[u"Divers"].append((u"Import d'une fonction \\psplot", "pstfunc", u'Pour utilisateurs de LaTeX. Ex: pstfunc("4 1 2.71818 0.039 x mul neg exp sub div")')) __classement__[u"Divers"].append(None) __classement__[u"Divers"].append((u"Aide", "aide", u"Fournit un complment d'aide sur certaines fonctions. Ex: aide(linreg)")) help = aide from .graphes import Graph Graphe = Graph #~ class Exp1_bis(sympy.core.numbers.Exp1): #~ def tostr(self, level=0): #~ return 'e' #~ e = Exp1_bis() #~ del Exp1_bis #~ class ImaginaryUnit_bis(sympy.core.numbers.ImaginaryUnit): #~ def tostr(self, level=0): #~ return 'i' #~ i = ImaginaryUnit_bis() #~ del ImaginaryUnit_bis wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/custom_objects.py0000644000175000017500000002561712014170666024746 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### #WxGeometrie #Dynamic geometry, graph plotter, and more for french mathematic teachers. #Copyright (C) 2005-2010 Nicolas Pourcelot # #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation; either version 2 of the License, or #(at your option) any later version. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## Objets complmentaires ceux de sympy from sympy import Symbol, Integer, Matrix from sympy.printing.latex import LatexPrinter from sympy.printing.str import StrPrinter from sympy.printing.precedence import precedence from .internal_objects import ObjetMathematique from ..pylib import GenericWrapper class Fonction(ObjetMathematique): def __init__(self, variables, expression): if not isinstance(variables, (list, tuple)): variables = (variables,) self.variables = variables self.expression = expression @classmethod def _substituer(cls, expression, dico): if hasattr(expression, "__iter__"): return expression.__class__(cls._substituer(elt, dico) for elt in expression) return expression.subs(dico) def __call__(self, *args, **kw): if kw: if args: raise TypeError, "les arguments sont entres de deux facons differentes." return self._substituer(self.expression, [(Symbol(key), value) for key, value in kw.iteritems()]) if len(args) > len(self.variables): raise TypeError, "il y a plus d'arguments que de variables." return self._substituer(self.expression, zip(self.variables[:len(args)], args)) def _variables(self): return tuple(str(arg) for arg in self.variables) def __str__(self): return ", ".join(self._variables()) + " -> " + str(self.expression) def __repr__(self): return "Fonction(%s, %s)" %(repr(self.variables), repr(self.expression)) for op in ("add", "mul", "div", "rdiv", "pow", "rpow"): op = "__" + op + "__" def __op__(self, y, op = op): if isinstance(y, Fonction): if self.variables == y.variables or not y.variables: return Fonction(self.variables, getattr(self.expression, op)(y.expression),) elif not self.variables: return Fonction(y.variables, getattr(self.expression, op)(y.expression),) else: raise ValueError, "les deux fonctions n'ont pas les memes variables." else: return Fonction(self.variables, getattr(self.expression, op)(y),) exec("%s=__op__" %op) del __op__ def __ne__(self): return Fonction(self.variables, -self.expression) def __abs__(self): return Fonction(self.variables, abs(self.expression)) def __eq__(self, y): if isinstance(y, Fonction): return self.expression == y.expression else: return self.expression == y def __gt__(self, y): if isinstance(y, Fonction): return self.expression > y.expression else: return self.expression > y class Matrice(Matrix): def __repr__(self): return "Matrice(%s)" %repr(self.mat) class ProduitEntiers(long): u"""Usage interne : destin tre utilis avec sympy.factorint.""" def __new__(cls, *couples): val = 1 for (m, p) in couples: val *= m**p self = long.__new__(cls, val) self.couples = couples return self # __slots__ = ["couples"] def __str__(self): def formater(exposant): if exposant == 1: return "" return "**" + str(exposant) return "*".join((str(entier) + formater(exposant)) for entier, exposant in self.couples) def __repr__(self): return "ProduitEntiers(*%s)" %repr(self.couples) #TODO: crer une classe Wrapper, dont MesureDegres doit hriter, # Note: this must wrap all special methods # http://docs.python.org/reference/datamodel.html#more-attribute-access-for-new-style-classes class MesureDegres(GenericWrapper): u"""Usage interne : destin tre utilis avec deg.""" __slots__ = ('__val',) def __str__(self): return str(self.__val) + '' def __repr__(self): return repr(self.__val) + '' def __unicode__(self): return unicode(self.__val) + u'' class Temps(object): def __init__(self, secondes = 0, **kw): self.secondes = secondes + kw.get("s", 0) \ + 60*kw.get("m", 0) + 60*kw.get("min", 0) \ + 3600*kw.get("h", 0) \ + 86400*kw.get("j", 0) + 86400*kw.get("d", 0) def jhms(self): s = float(self.secondes) s, dec = int(s), s-int(s) j, s = s//86400, s%86400 h, s = s//3600, s%3600 m, s = s//60, s%60 return j, h, m, s + dec def __str__(self): return "%s j %s h %s min %s s" %self.jhms() def __repr__(self): return "Temps(%s)" %self.secondes class CustomStrPrinter(StrPrinter): def _print_str(self, expr): return '"%s"' %expr.replace('"', r'\"') def _print_unicode(self, expr): return '"%s"' %expr.replace('"', r'\"') def _print_Exp1(self, expr): return 'e' def _print_Abs(self, expr): return 'abs(%s)'%self._print(expr.args[0]) def _print_ImaginaryUnit(self, expr): return 'i' def _print_Infinity(self, expr): return '+oo' def _print_log(self, expr): return "ln(%s)"%self.stringify(expr.args, ", ") def _print_Pow(self, expr): PREC = precedence(expr) if expr.exp.is_Rational and expr.exp.p == 1 and expr.exp.q == 2: return 'sqrt(%s)' % self._print(expr.base) if expr.exp.is_Rational and expr.exp.is_negative: return '1/%s'%self._print(expr.base**abs(expr.exp)) else: return '%s^%s'%(self.parenthesize(expr.base, PREC), self.parenthesize(expr.exp, PREC)) def _print_Float(self, expr): string = StrPrinter._print_Float(self, expr) return string.replace('e+', '*10^').replace('e-', '*10^-') def _print_Union(self, expr): return expr.__str__() def doprint(self, expr): return StrPrinter.doprint(self, expr) if not isinstance(expr, unicode) else expr class CustomLatexPrinter(LatexPrinter): def __init__(self, profile = None): _profile = { "mat_str" : "pmatrix", "mat_delim" : "", "mode": "inline", } if profile is not None: _profile.update(profile) LatexPrinter.__init__(self, _profile) def _print_Temps(self, expr): return r"%s \mathrm{j}\, %s \mathrm{h}\, %s \mathrm{min}\, %s \mathrm{s}" %expr.jhms() def _print_exp(self, expr, exp=None): tex = r"\mathrm{e}^{%s}" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_Exp1(self, expr): return r"\mathrm{e}" def _print_Abs(self, expr): return r'\left|{%s}\right|'%self._print(expr.args[0]) def _print_ImaginaryUnit(self, expr): return r"\mathrm{i}" def _print_Fonction(self, expr): return ", ".join(expr._variables()) + "\\mapsto " + self._print(expr.expression) def _print_Function(self, expr, exp=None): func = expr.func.__name__ if hasattr(self, '_print_' + func): return getattr(self, '_print_' + func)(expr, exp) else: if exp is not None: name = r"\mathrm{%s}^{%s}" % (func, exp) else: name = r"\mathrm{%s}" % func if len(expr.args) == 1 and isinstance(expr.args[0], (Symbol, Integer)): return name + "(" + str(self._print(expr.args[0])) +")" else: args = [ str(self._print(arg)) for arg in expr.args ] return name + r"\left(%s\right)" % ",".join(args) def _print_ProduitEntiers(self, expr): def formater(exposant): if exposant == 1: return "" return "^" + str(exposant) return "\\times ".join((str(entier) + formater(exposant)) for entier, exposant in expr.couples) def _print_Float(self, expr): s = str(expr) if "e" in s: nombre, exposant = s.split("e") return nombre + "\\times 10^{" + exposant.lstrip("+") + "}" else: return s def _print_Infinity(self, expr): return r"+\infty" def _print_Order(self, expr): return r"\mathcal{O}\left(%s\right)" % \ self._print(expr.args[0]) def _print_abs(self, expr, exp=None): tex = r"\left|{%s}\right|" % self._print(expr.args[0]) if exp is not None: return r"%s^{%s}" % (tex, exp) else: return tex def _print_Union(self, expr): tex = r"\cup".join(self._print(intervalle) for intervalle in expr.intervalles) tex = tex.replace(r"\}\cup\{", "\,;\, ") return tex def _print_Intervalle(self, expr): if expr.vide: return r"\varnothing" elif expr.inf_inclus and expr.sup_inclus and expr.inf == expr.sup: return r"\{%s\}" % self._print(expr.inf) if expr.inf_inclus: left = "[" else: left = "]" if expr.sup_inclus: right = "]" else: right = "[" return r"%s%s;%s%s" % (left, self._print(expr.inf), self._print(expr.sup), right) def _print_Singleton(self, expr): return r"\{%s\}" % self._print(expr.inf) def _print_tuple(self, expr): return "(" + ",\,".join(self._print(item) for item in expr) + ")" def _print_log(self, expr, exp=None): if len(expr.args) == 1 and isinstance(expr.args[0], (Symbol, Integer)): tex = r"\ln(%s)" % self._print(expr.args[0]) else: tex = r"\ln\left(%s\right)" % self._print(expr.args[0]) return self._do_exponent(tex, exp) def _print_function(self, expr): return r"\mathrm{Fonction}\, " + expr.func_name def doprint(self, expr): tex = LatexPrinter.doprint(self, expr) return tex.replace(r'\operatorname{', r'\mathrm{') wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/internal_objects.py0000644000175000017500000000540412014170666025240 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets mathlib # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import numpy UfuncType = type(numpy.absolute) fonctions_numpy = [key for key, val in numpy.__dict__.items() if type(val) == UfuncType] class ObjetMathematique(object): u"Classe mre de la plupart des objets mathmatiques." def __lt__(self, y): return not self >= y def __ge__(self, y): return self > y or self == y def __le__(self, y): return not self > y def __ne__(self, y): return not self.__eq__(y) def __nonzero__(self): # la valeur logique de l'objet (la doc officielle est fausse ??) return self != 0 # utilise dans un test "if" par exemple. def __truediv__(self, y): return self.__div__(y) def __rtruediv__(self, y): return self.__rdiv__(y) def __sub__(self, y): return self + (- y) def __rsub__(self, y): return y + (- self) def __radd__(self,y): return self.__add__(y) def __rmul__(self, y): return self.__mul__(y) def __pos__(self): return self def __cmp__(self, y): if hasattr(self, "__gt__") and isinstance(y, (ObjetMathematique, float, int, long)): if self > y: return 1 elif self < y: return -1 else: return 0 return NotImplemented class Reel(ObjetMathematique): u"""Classe mre pour les objets mathmatiques supportant les fonctions usuelles. Permet de leur appliquer les oprations mathmatiques de pylab.""" for nom in fonctions_numpy: exec(""" def %s(self, *args): arguments = [] for arg in args: arguments.append(float(arg)) return numpy.%s(float(self), *arguments)""" %(nom, nom)) del nom wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/interprete.py0000644000175000017500000004770412014170666024105 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### #WxGeometrie #Dynamic geometry, graph plotter, and more for french mathematic teachers. #Copyright (C) 2005-2010 Nicolas Pourcelot # #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation; either version 2 of the License, or #(at your option) any later version. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re, math, types import numpy import sympy from sympy import Symbol, Basic, Float, sympify from .intervalles import Ensemble from .custom_functions import custom_str, custom_latex from .custom_objects import Temps, Fonction, Matrice, ProduitEntiers from . import sympy_functions from ..mathlib import end_user_functions from ..pylib import print_error, split_around_parenthesis, regsub,\ securite from .parsers import simplifier_ecriture, NBR, traduire_formule from .. import param class LocalDict(dict): globals = {} ## def __getitem__(self, name): ## #~ print "Nom de clef: ", name ## #~ print "local: ", self.has_key(name) ## #~ print "global: ", self.globals.has_key(name) ## if self.has_key(name) or self.globals.has_key(name): # doit renvoyer une KeyError si la cl est dans le dictionnaire global, pour que Python y aille chercher ensuite la valeur associe la cl ## return dict.__getitem__(self, name) ## return Symbol(name) def __missing__(self, key): # _59 is an alias for ans(59) if key.startswith('_'): if key[1:].isalnum(): return self.globals['ans'](int(key[1:])) else: if key == len(key)*'_': return self.globals['ans'](-len(key)) return self.globals.get(key, sympy.__dict__.get(key, Symbol(key))) def __setitem__(self, name, value): # Pour viter que l'utilisateur redfinisse pi, i, e, etc. par mgarde. if self.globals.has_key(name) or (name.startswith('_') and name[1:].isalnum()): raise NameError, "%s est un nom reserve" %name if isinstance(value, str): # exec/eval encodent les chanes cres en utf8. value = value.decode("utf8").encode(param.encodage) # [[1,2],[3,4]] est converti en une matrice if isinstance(value, list) and len(value): if isinstance(value[0], list): n = len(value[0]) test = True for elt in value[1:]: if not isinstance(elt, list) or len(elt) != n: test = False break if test: value = sympy_functions.mat(value) dict.__setitem__(self, name, value) class Interprete(object): def __init__(self, calcul_exact=True, ecriture_scientifique=False, forme_algebrique=True, simplifier_ecriture_resultat=True, changer_separateurs=False, separateurs_personnels=(",", ";"), copie_automatique=False, formatage_OOo=True, formatage_LaTeX=True, ecriture_scientifique_decimales=2, precision_calcul=60, precision_affichage=18, simpify=True, verbose=None, appliquer_au_resultat=None, adapter_separateur=None, ): # Dictionnaire local (qui contiendra toutes les variables dfinies par l'utilisateur). self.locals = LocalDict() # Dictionnaire global (qui contient les fonctions, variables et constantes prdfinies). self.globals = vars(end_user_functions).copy() self.globals.update({ "__builtins__": None, "Fonction": Fonction, "Matrice": Matrice, "Temps": Temps, "ProduitEntiers": ProduitEntiers, "Ensemble": Ensemble, "__sympify__": sympify, "ans": self.ans, "rep": self.ans, # alias en franais :) "__vars__": self.vars, # "__decimal__": Decimal, "__decimal__": self._decimal, "__local_dict__": self.locals, "range": numpy.arange, "arange": numpy.arange, }) # pour viter que les procdures de rcriture des formules ne touchent au mots clefs, # on les rfrence comme fonctions (elles seront inaccessibles, mais ce n'est pas grave). # ainsi, "c and(a or b)" ne deviendra pas "c and*(a or b)" ! # self.globals.update({}.fromkeys(securite.keywords_autorises, lambda:None)) # On import les fonctions python qui peuvent avoir une utilit ventuel (et ne prsentent pas de problme de scurit) a_importer = ['all', 'unicode', 'isinstance', 'dict', 'oct', 'sorted', 'list', 'iter', 'set', 'reduce', 'issubclass', 'getattr', 'hash', 'len', 'frozenset', 'ord', 'filter', 'pow', 'float', 'divmod', 'enumerate', 'basestring', 'zip', 'hex', 'chr', 'type', 'tuple', 'reversed', 'hasattr', 'delattr', 'setattr', 'str', 'int', 'unichr', 'any', 'min', 'complex', 'bool', 'max', 'True', 'False'] for nom in a_importer: self.globals[nom] = __builtins__[nom] self.locals.globals = self.globals # gerer les fractions et les racines de maniere exacte si possible. self.calcul_exact = calcul_exact # afficher les resultats en ecriture scientifique. self.ecriture_scientifique = ecriture_scientifique # mettre les rsultats complexes sous forme algbrique self.forme_algebrique = forme_algebrique # crire le rsultat sous une forme plus agrable lire # (suppression des '*' dans '2*x', etc.) self.simplifier_ecriture_resultat = simplifier_ecriture_resultat # appliquer les sparateurs personnaliss self.changer_separateurs = changer_separateurs # sparateurs personnaliss (sparateur dcimal, sparateur de listes) self.separateurs_personnels = separateurs_personnels # d'autres choix sont possibles, mais pas forcment heureux... # copie automatique de chaque rsultat dans le presse-papier self.copie_automatique = copie_automatique self.formatage_OOo = formatage_OOo self.formatage_LaTeX = formatage_LaTeX self.ecriture_scientifique_decimales = ecriture_scientifique_decimales self.precision_calcul = precision_calcul self.precision_affichage = precision_affichage self.verbose = verbose self.simpify = simpify # une fonction appliquer tous les rsultats self.appliquer_au_resultat = appliquer_au_resultat self.adapter_separateur = (param.adapter_separateur if adapter_separateur is None else adapter_separateur) self.latex_dernier_resultat = '' self.initialiser() def _decimal(self, nbr, prec = None): if prec is None: prec = self.precision_calcul return Float(nbr, prec) def initialiser(self): self.locals.clear() self.derniers_resultats = [] def evaluer(self, calcul = ""): self.warning = "" # calcul = re.sub("[_]+", "_", calcul.strip()) # par mesure de scurit, les "__" sont interdits. # Cela permet ventuellement d'interdire l'accs des fonctions. # Warning: inefficace. Cf. "_import _builtins_import _ # Ferme automatiquement les parentheses. parentheses = "({[", ")}]" for i in range(3): difference = calcul.count(parentheses[0][i])-calcul.count(parentheses[1][i]) if difference > 0: calcul += difference*parentheses[1][i] self.warning += u" Attention, il manque des parenthses \"" + parentheses[1][i] + "\"." elif difference < 0: self.warning += u" Attention, il y a des parenthses \"" + parentheses[1][i] + "\" superflues." if calcul.endswith(abs(difference)*parentheses[1][i]): calcul = calcul[:difference] ## # Transforme les ' en ` et les " en `` ## calcul = calcul.replace("'", "`").replace('"', '``') if self.verbose: print "Traitement ({[]}) : ", calcul if calcul and calcul[0] in ">>evalf" if ">>" in calcul: liste = calcul.split(">>") calcul = liste[0] for s in liste[1:]: calcul = s + "(" + calcul + ")" if self.verbose: print "Traitement >> : ", calcul if calcul.startswith('?'): calcul = 'aide(%s)' %calcul[1:] elif calcul.endswith('?'): calcul = 'aide(%s)' %calcul[:-1] try: param.calcul_approche = not self.calcul_exact # utilis en particulier dans la factorisation des polynmes self._executer(calcul) except Exception as err: if not self.adapter_separateur: raise # Si le calcul choue, c'est peut-tre que l'utilisateur a utilis une virgule pour les dcimaux sep = self.separateurs_personnels[0] _raise = True if not self.changer_separateurs and re.search(r'\d[' + sep + r']\d', calcul): self.changer_separateurs = True try: # On retente le calcul aprs avoir dfini la virgule comme sparateur dcimal self._executer(calcul) self.warning += u" Attention: sparateur dcimal incorrect." _raise = False except Exception: # C'est l'erreur **initiale** qui nous intresse, pas celle-ci ! pass finally: self.changer_separateurs = False if _raise: raise err # Erreur initiale (c--d. avant de changer les sparateurs). finally: param.calcul_approche = False self.derniers_resultats.append(self.locals["_"]) if not self.calcul_exact: return self._formater(sympy_functions.evalf(self.locals["_"], self.precision_calcul)) return self._formater(self.locals["_"]) def _ecriture_scientifique(self, chaine): valeur = float(chaine) mantisse = int(math.floor(math.log10(abs(valeur)))) chaine = str(round(valeur/10.**mantisse, self.ecriture_scientifique_decimales)) if mantisse: chaine += "*10^"+str(mantisse) return chaine def _ecriture_scientifique_latex(self, chaine): valeur = float(chaine) mantisse = int(math.floor(math.log10(abs(valeur)))) chaine = str(round(valeur/10.**mantisse, self.ecriture_scientifique_decimales)) if mantisse: chaine += "\\times 10^{%s}" %mantisse return chaine def _formater_decimaux(self, chaine): if "." in chaine: chaine = str(Float(str(chaine), self.precision_calcul).evalf(self.precision_affichage)) chaine = chaine.rstrip('0') if chaine.endswith("."): chaine = chaine[:-1] return chaine def _formater(self, valeur): ## resultat = self._formatage_simple(valeur) resultat = custom_str(valeur) if valeur is None: latex = "" else: try: latex = custom_latex(valeur) except Exception: print_error() latex = '' if self.ecriture_scientifique and not self.calcul_exact: resultat = regsub(NBR, resultat, self._ecriture_scientifique) latex = regsub(NBR, latex, self._ecriture_scientifique_latex) else: ## print "initial", resultat resultat = regsub(NBR, resultat, self._formater_decimaux) ## print "final", resultat latex = regsub(NBR, latex, self._formater_decimaux) ## if re.match("[0-9]*[.][0-9]+$", resultat): ## resultat = resultat.rstrip('0') ## if resultat.endswith("."): ## resultat += "0" ## latex = "$" + resultat + "$" if self.changer_separateurs and not isinstance(valeur, basestring): resultat = resultat.replace(",", self.separateurs_personnels[1]).replace(".", self.separateurs_personnels[0]) latex = latex.replace(",", self.separateurs_personnels[1]).replace(".", self.separateurs_personnels[0]) # TODO: utiliser un parser, pour dtecter les chanes, et ne pas remplacer l'intrieur. if isinstance(valeur, basestring): latex = u'\u201C%s\u201D' %valeur self.latex_dernier_resultat = latex if self.simplifier_ecriture_resultat: resultat = simplifier_ecriture(resultat) return resultat, latex def _traduire(self, formule): variables = self.globals.copy() variables.update(self.locals) #callable_types = ( types.BuiltinFunctionType, #types.BuiltinMethodType, #types.FunctionType, #types.UfuncType, #types.MethodType, #types.TypeType, #types.ClassType) #fonctions = [key for key, val in variables.items() if isinstance(val, callable_types)] ## fonctions = [key for key, val in variables.items() if hasattr(val, "__call__") and not isinstance(val, sympy.Atom)] #print fonctions # La fonction traduire_formule de la librairie formatage permet d'effectuer un certain nombre de conversions. formule = traduire_formule(formule, fonctions = variables, OOo = self.formatage_OOo, LaTeX = self.formatage_LaTeX, changer_separateurs = self.changer_separateurs, separateurs_personnels = self.separateurs_personnels, simpify = self.simpify, verbose = self.verbose, ) formule = re.sub("(?= 0: n = int(n-1) else: n = int(n) if self.derniers_resultats: return self.derniers_resultats[n] self.warning += u" Ans(): aucun calcul antrieur." return 0 def clear_state(self): self.locals.clear() def save_state(self): def repr2(expr): if isinstance(expr, (types.BuiltinFunctionType, types.TypeType, types.FunctionType)): return expr.__name__ return repr(expr) return '\n'.join(k + ' = ' + repr2(v) for k, v in self.locals.items()) \ + '\n\n@derniers_resultats = [\n ' + '\n '.join(repr(repr2(res)) +',' for res in self.derniers_resultats) + '\n ]' def load_state(self, state): def evaltry(expr): u"Evalue l'expression. En cas d'erreur, intercepte l'erreur et retourne None." try: return eval(expr, self.globals, self.locals) except Exception: print("Error: l'expression suivante n'a pu tre value par l'interprte: %s." %repr(expr)) print_error() self.clear_state() etat_brut, derniers_resultats = state.split('@derniers_resultats = ', 1) etat = (l.split(' = ', 1) for l in etat_brut.split('\n') if l) self.locals.update((k, evaltry(v)) for k, v in etat) liste_repr = eval(derniers_resultats, self.globals, self.locals) self.derniers_resultats = [evaltry(s) for s in liste_repr] wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/sympy_functions.py0000644000175000017500000002465312014170666025173 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode ## Cette librairie est une interface pour sympy : ## Elle modifie si ncessaire le comportement de fonctions sympy. from types import FunctionType from sympy import Basic, expand as expand_, apart, Function, Integer, factorint, Symbol,\ diff as diff_, divisors as divisors_, cancel, together as together_,\ limit as limit_, factor as factor_, integrate as integrate_, Sum,\ sqrtdenest, solve as solve_, product as product_ from .custom_objects import Matrice, Fonction, ProduitEntiers from .internal_functions import extract_var, poly_factor, syms from .custom_functions import auto_collect, derivee from ..pylib import print_error from .. import param def expand(expression, variable = None): expression = expand_(expression) if isinstance(expression, Basic) and expression.is_rational_function(): if variable is None: variable = extract_var(expression) return apart(expression, variable) return expression def evalf(expression, precision = 60): u"""Evalue l'expression en respectant sa structure. Par exemple, un polynme factoris reste factoris aprs valuation. """ if not isinstance(expression, Basic): if hasattr(expression, 'evalf'): return expression.evalf(precision) elif hasattr(expression, '__float__'): return float(expression) return expression elif expression.is_Atom or isinstance(expression, Function): return expression.evalf(precision) else: return expression.func(*(evalf(arg) for arg in expression.args)) def factor(expression, variable = None, ensemble = None, decomposer_entiers = True): if isinstance(expression, (int, long, Integer)): if decomposer_entiers: return ProduitEntiers(*factorint(expression).iteritems()) else: return expression elif isinstance(expression, Basic) and expression.is_polynomial(): if variable is None: variable = extract_var(expression) if variable is None: # polynme plusieurs variables return factor_(expression) else: try: return poly_factor(expression, variable, ensemble) except NotImplementedError: if param.debug: print_error() return expression resultat = together_(expression) if resultat.is_rational_function(): num, den = resultat.as_numer_denom() if den != 1: return factor(num, variable, ensemble, decomposer_entiers)/factor(den, variable, ensemble, decomposer_entiers) else: resultat = auto_collect(resultat) if resultat.is_Mul: produit = 1 for facteur in resultat.args: if facteur != 1: produit *= factor(facteur, variable, ensemble, decomposer_entiers) return produit return resultat def cfactor(expression, variable = None): return factor(expression, variable, ensemble = "C") def series(expression, variable = None, point = 0, ordre = 5): if variable is None: if syms(expression): variable = syms(expression)[0] else: variable = Symbol("x") return getattr(expression, "series")(variable, point, ordre) def diff(expression, variable = None, n = 1): if isinstance(expression, Fonction): return derivee(expression) if variable is None and hasattr(expression, "atoms") and len(expression.atoms(Symbol)) == 1: variable = expression.atoms(Symbol).pop() return diff_(expression, variable, n) def divisors(n): return divisors_(int(n)) def limit(expression, *args): args = list(args) if args[-1] == "-" or args[-1] == "+": dir = args.pop() else: dir = None if len(args) == 1 and len(expression.atoms(Symbol)) == 1: args = [expression.atoms(Symbol).pop()] + args if dir is None: limite_gauche = limit_(expression, args[0], args[1], "-") limite_droite = limit_(expression, args[0], args[1], "+") if limite_gauche is limite_droite: return limite_gauche return limite_gauche, limite_droite else: return limit_(expression, args[0], args[1], dir) def integrate(expression, *args): if len(args) == 3 and isinstance(args[0], Symbol) \ and not isinstance(args[1], Symbol) \ and isinstance(args[1], Basic) \ and not isinstance(args[2], Symbol) \ and isinstance(args[2], Basic): return integrate_(expression, (args[0], args[1], args[2])) elif len(args) == 2 and len(syms(expression)) <= 1 \ and not isinstance(args[0], Symbol) \ and isinstance(args[0], Basic) \ and not isinstance(args[1], Symbol) \ and isinstance(args[1], Basic): if syms(expression): return integrate_(expression, (syms(expression)[0], args[0], args[1])) else: return integrate_(expression, (Symbol("x"), args[0], args[1])) return integrate_(expression, *args) def sum(expression, *args): if len(args) == 3 and isinstance(args[0], Symbol) \ and not isinstance(args[1], Symbol) \ and isinstance(args[1], Basic) \ and not isinstance(args[2], Symbol) \ and isinstance(args[2], Basic): args = (args[0], args[1], args[2]) elif len(args) == 2 and len(syms(expression)) <= 1 \ and not isinstance(args[0], Symbol) \ and isinstance(args[0], Basic) \ and not isinstance(args[1], Symbol) \ and isinstance(args[1], Basic): if syms(expression): args = (syms(expression)[0], args[0], args[1]) else: args = (Symbol("x"), args[0], args[1]) return Sum(expression, args).doit() def product(expression, *args): if len(args) == 3 and isinstance(args[0], Symbol) \ and not isinstance(args[1], Symbol) \ and isinstance(args[1], Basic) \ and not isinstance(args[2], Symbol) \ and isinstance(args[2], Basic): return product_(expression, (args[0], args[1], args[2])) elif len(args) == 2 and len(syms(expression)) <= 1 \ and not isinstance(args[0], Symbol) \ and isinstance(args[0], Basic) \ and not isinstance(args[1], Symbol) \ and isinstance(args[1], Basic): if syms(expression): return product_(expression, (syms(expression)[0], args[0], args[1])) else: return product_(expression, (Symbol("x"), args[0], args[1])) return product_(expression, *args) def solve(expression, *variables, **kw): ensemble = kw.get("ensemble", "R") if not variables: variables = syms(expression) if len(variables) == 1: solutions = solve_(expression, variables[0]) if ensemble == "R": return tuple(solution for solution in solutions if solution.is_real) elif ensemble == "Q": return tuple(solution for solution in solutions if solution.is_rational) elif ensemble == "N": return tuple(solution for solution in solutions if solution.is_integer) else: return solutions else: return solve_(expression, variables) def csolve(expression, *variables): return solve(expression, *variables, **{'ensemble': "C"}) def qsolve(expression, *variables): return solve(expression, *variables, **{'ensemble': "Q"}) def nsolve(expression, *variables): return solve(expression, *variables, **{'ensemble': "N"}) def simplifier_racines(expression): try: if getattr(expression, "is_Pow", False): return sqrtdenest(expression) elif getattr(expression, "is_Mul", False): return reduce(lambda x,y:x*y, [simplifier_racines(fact) for fact in expression.args], 1) elif getattr(expression, "is_Add", False): return reduce(lambda x,y:x+y, [simplifier_racines(term) for term in expression.args], 0) return expression except TypeError: if param.debug: print "Warning: error occured during expression denesting:", expression return expression def mat(*args): if len(args) >= 2: args = list(args) args[0] = int(args[0]) args[1] = int(args[1]) if len(args) == 2: args.append(lambda i,j:i==j,) elif not isinstance(args[2], FunctionType): if isinstance(args[2], Basic): li = Symbol("li") co = Symbol("co") args = [[args[2].subs({co: j+1, li: i+1}) for j in xrange(args[1])] for i in xrange(args[0])] else: args[2] = lambda i, j, val = args[2]: val # print args, [type(arg) for arg in args], args[2](1, 1), type(args[2](1, 1)) return Matrice(*args) def together(expression): return cancel(together_(expression)) wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/custom_functions.py0000644000175000017500000007104612014170666025322 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## Cette librairie contient les fonctions de haut niveau non inclues dans sympy. import math from types import FunctionType from sympy import exp, ln, tan, pi, E, Rational, Symbol, oo, diff, log, floor,\ Add, Mul, sqrt, solve, Wild, sympify, FunctionClass from .intervalles import Intervalle, vide, Union, R from .custom_objects import Temps, CustomLatexPrinter, CustomStrPrinter, Fonction from .internal_functions import extract_var, count_syms from ..pylib import msplit, split_around_parenthesis from .. import param #def deg(x): # u'Conversion radians -> degrs.' # return .MesureDegres(x) def deg(x): u'Conversion radians -> degrs.' return x*180/pi #def rad(x): # u'Conversion degrs -> radians.' # return x*sympy.pi/180 def jhms(s): u"Convertit un temps en secondes en jours-heures-minutes-secondes." return Temps(s = s) def cbrt(x): u"Racine cubique de x." return cmp(x, 0)*math.exp(math.log(abs(x))/3) def root(x, n): u"""Racine nime de x. N'est dfinie pour x ngatif que si n est pair.""" if n%2: return cmp(x, 0)*math.exp(math.log(abs(x))/n) return math.exp(math.log(x)/n) def prod(facteurs): return reduce(lambda x,y:x*y, facteurs, 1) def gcd(a, b): u"pgcd de a et de b" # algorithme d'Euclide a, b = max(abs(a),abs(b)), min(abs(a),abs(b)) while b: a, b = b, a%b return a def lcm(a, b): u"ppcm de a et de b" return a*b//gcd(a,b) def pgcd(*termes): u"Le plus grand dnominateur commun un nombre quelconque d'entiers." return reduce(lambda x,y:gcd(x,y), termes) def ppcm(*termes): u"Le plus petit multiple commun un nombre quelconque d'entiers." return reduce(lambda x,y:lcm(x,y), termes) def n_premiers(n = 100, maximum = 50000): # securite face aux erreurs de frappe...! u"Donne la liste des n premiers nombres premiers." liste = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] m = liste[-1] + 2 if n > maximum: raise OverflowError while len(liste) < n: r = math.sqrt(m) for k in liste: if k > r: # pas de diviseurs inferieurs sa racine, donc le nombre est premier liste.append(m) break if not m%k: break m += 2 return liste # Approximation rationnelle par fractions continues # cf. http://fr.wikipedia.org/wiki/Fraction_continue def frac(valeur, n = 20, epsilon = 1e-15): u"Donne une fraction approximativement gale la valeur." assert epsilon > 0 p_ = 0 p = 1 q_ = 1 q = 0 x = valeur for i in xrange(n): a = int(x) p, p_ = a*p + p_, p q, q_ = a*q + q_, q delta = x - a if abs(delta) < epsilon or abs(valeur - p/q) < epsilon: return Rational(p, q) x = 1/delta return valeur def bin(n): u"Conversion en binaire." s = "" while n: s = str(n%2) + s n //= 2 return s def custom_latex(expr, profile = None): return CustomLatexPrinter(profile).doprint(expr) def custom_str(expr, profile = None): return CustomStrPrinter(profile).doprint(expr) def _Pow2list(expression): u"""On dcompose une puissance en liste de facteurs.""" base, puissance = expression.as_base_exp() if puissance.is_integer: return int(puissance)*[base] elif base == E: coeff = puissance.as_coeff_mul()[0] if coeff.is_integer: return int(abs(coeff))*[base**(puissance/abs(coeff))] return [expression] def _Mul2list(expression): u"""On dcompose un produit en une liste de facteurs ; les puissances sont converties pralablement en produits.""" liste = [] if expression.is_Mul: for facteur in expression.args: liste += _Pow2list(facteur) return liste return _Pow2list(expression) def auto_collect(expression): u"""Factorise une expression en utilisant sympy.collect, sans prciser manuellement ce par quoi factoriser.""" if expression.is_Add: dico = {} liste0 = _Mul2list(expression.args[0]) for elt in liste0: dico[elt] = liste0.count(elt) for terme in expression.args[1:]: liste = _Mul2list(terme) for elt in liste0: dico[elt] = min(dico[elt], liste.count(elt)) produit = 1 for key in dico: produit *= key**dico[key] return produit*sum(arg/produit for arg in expression.args) return expression def derivee(f): if isinstance(f, FunctionType): x = Symbol('x') return Fonction(x, diff(f(x), x)) return Fonction(f.variables[0], diff(f.expression, f.variables[0])) def nul(expression, variable = None, intervalle = True): u"""Retourne l'ensemble sur lequel une expression variable relle est nulle.""" from .sympy_functions import factor, solve if variable is None: variable = extract_var(expression) expression = factor(expression, variable, "R", decomposer_entiers = False) if expression.is_Mul: facteurs = expression.args else: facteurs = [expression] solutions = (vide if intervalle else set()) for facteur in facteurs: liste_solutions = solve(facteur, variable, ensemble = "R") if intervalle: solutions += Union(*liste_solutions) else: solutions.update(liste_solutions) return solutions def ensemble_definition(expression, variable = None): ## print expression, variable if variable is None: variable = extract_var(expression) ens_def = R if hasattr(expression, "is_Add") and expression.is_Add: for terme in expression.args: ens_def *= ensemble_definition(terme, variable) return ens_def ## try: ## expression = sympy_functions.factor(expression, variable, "R", decomposer_entiers = False) ## except (NotImplementedError, TypeError): ## if param.debug: ## print "Warning: Factorisation impossible de ", expression if hasattr(expression, "subs"): old_variable = variable variable = Symbol("_tmp",real=True) expression = expression.subs({old_variable:variable}) if hasattr(expression, "is_Pow") and expression.is_Pow: base, p = expression.as_base_exp() if p.is_integer: if p >= 0: return ensemble_definition(base, variable) else: return ensemble_definition(base, variable) - nul(base, variable) elif p.is_rational: n, d = p.as_numer_denom() if abs(n) == 1 and not d.is_even: if p >= 0: return ensemble_definition(base, variable) else: return ensemble_definition(base, variable) - nul(base, variable) else: if p >= 0: return positif(base, variable) else: return positif(base, variable, strict = True) else: if p >= 0: return positif(base, variable) else: return positif(base, variable, strict = True) elif hasattr(expression, "is_Mul") and expression.is_Mul: for facteur in expression.args: ens_def *= ensemble_definition(facteur, variable) return ens_def elif isinstance(expression, tan): arg = expression.args[0] # -pi/2 < arg < pi/2 return positif(arg + pi/2, variable, strict = True)*positif(pi/2-arg, variable, strict = True) elif isinstance(expression, ln): arg = expression.args[0] # 0 < arg return positif(arg, variable, strict = True) return ens_def def _is_pos(expr): return getattr(expr, "is_positive", float(expr) > 0) def _is_neg(expr): return getattr(expr, "is_negative", float(expr) < 0) def _is_var(expression, variable): return hasattr(expression, "has_any_symbols") and expression.has(variable) def positif(expression, variable = None, strict = False): u"""Retourne l'ensemble sur lequel une expression variable relle est positive (resp. strictement positive).""" from .sympy_functions import factor # L'tude du signe se fait dans R, on indique donc sympy que la variable est relle. if variable is None: variable = extract_var(expression) if hasattr(expression, "subs"): old_variable = variable variable = Symbol("_tmp",real=True) expression = expression.subs({old_variable:variable}) ens_def = ensemble_definition(expression, variable) try: expression = factor(expression, variable, "R", decomposer_entiers = False) except NotImplementedError: if param.debug: print "Warning: Factorisation impossible de ", expression ## print "T455451", expression, variable if hasattr(expression, "is_Pow") and expression.is_Pow and expression.as_base_exp()[1].is_rational: base, p = expression.as_base_exp() # Le dnominateur ne doit pas s'annuler : if p < 0: strict = True if p.is_integer and p.is_even: if strict: return ens_def*(R - (positif(base, variable, strict = False) - positif(base, variable, strict = True))) else: return ens_def else: return ens_def*positif(base, variable, strict = strict) if hasattr(expression, "is_Mul") and expression.is_Mul: posit = R posit_nul = R for facteur in expression.args: # pos : ensemble des valeurs pour lequelles l'expression est positive # pos_nul : ensemble des valeurs pour lequelles l'expression est positive ou nulle pos = positif(facteur, variable, strict = True) pos_nul = positif(facteur, variable, strict = False) # posit : les deux sont strictements positifs, ou les deux sont strictements ngatifs # posit_nul : les deux sont positifs ou nuls, ou les deux sont ngatifs ou nuls posit, posit_nul = (posit*pos + (-posit_nul)*(-pos_nul))*ens_def, (posit_nul*pos_nul + (-posit)*(-pos))*ens_def ## print "resultat", facteur, res #### if res is NotImplemented: #### return NotImplemented ## # le rsultat est positif si les deux facteurs sont positifs, ou si les deux facteurs sont ngatifs: ## resultat = resultat*res + (-resultat)*(-res) if strict: return posit else: return posit_nul if getattr(expression, "is_positive", None) is True: # > 0 return ens_def if getattr(expression, "is_negative", None) is True: # < 0 return vide if getattr(expression, "is_positive", None) is False and strict: # <= 0 return vide if getattr(expression, "is_negative", None) is False and not strict: # >= 0 return ens_def if getattr(expression, "is_zero", None) is True and not strict: # == 0 return ens_def if isinstance(expression, (int, float, long)): if expression > 0 or (expression == 0 and not strict): return ens_def else: return vide # pas besoin de l'ensemble de dfinition pour les fonctions polynomiales if hasattr(expression, "is_polynomial") and expression.is_polynomial(): P = expression.as_poly(variable) if P.degree() == 1: a, b = P.all_coeffs() if a > 0: return Intervalle(-b/a, +oo, inf_inclus = not strict) else: # a<0 (car a != 0) return Intervalle(-oo, -b/a, sup_inclus = not strict) elif P.degree() == 2: a, b, c = P.all_coeffs() d = b**2 - 4*a*c if d > 0: x1 = (-b - sqrt(d))/(2*a) x2 = (-b + sqrt(d))/(2*a) x1, x2 = min(x1, x2), max(x1, x2) if a > 0: return Intervalle(-oo, x1, sup_inclus = not strict) + Intervalle(x2, +oo, inf_inclus = not strict) else: # a<0 (car a != 0) return Intervalle(x1, x2, inf_inclus = not strict, sup_inclus = not strict) elif d == 0: x0 = -b/(2*a) if a > 0: return Intervalle(-oo, x0, sup_inclus = not strict) + Intervalle(x0, +oo, inf_inclus = not strict) else: return Intervalle(x0, x0, sup_inclus = not strict) else: # d < 0 if a > 0: return R else: return vide # a*f(x)+b > 0 <=> f(x)+b/a > 0 pour a > 0, -f(x) - b/a > 0 pour a < 0 if getattr(expression, "is_Add", False): args = expression.args if len(args) == 2: liste_constantes = [] liste_autres = [] for arg in args: if _is_var(arg, variable): liste_autres.append(arg) else: liste_constantes.append(arg) if len(liste_autres) == 1: partie_constante = Add(*liste_constantes) partie_variable = liste_autres[0] if getattr(partie_variable, "is_Mul", False): liste_facteurs_constants = [] liste_autres_facteurs = [] for facteur in partie_variable.args: if _is_var(facteur, variable): liste_autres_facteurs.append(facteur) else: liste_facteurs_constants.append(facteur) if liste_facteurs_constants: facteur_constant = Mul(*liste_facteurs_constants) autre_facteur = Mul(*liste_autres_facteurs) if _is_pos(facteur_constant): return positif(autre_facteur + partie_constante/facteur_constant, variable, strict = strict) elif _is_neg(facteur_constant): return ens_def*(R - positif(autre_facteur + partie_constante/facteur_constant, variable, strict = not strict)) # Logarithme : if isinstance(expression, ln): return positif(expression.args[0] - 1, variable, strict = strict) # Rsolution de ln(X1) + ln(X2) + ... + b > 0, o X1=f1(x), X2 = f2(x) ... if getattr(expression, "is_Add", False): args = expression.args liste_constantes = [] liste_ln = [] # Premier passage : on remplace a*ln(X1) par ln(X1**a) for arg in args: if getattr(arg, "is_Mul", False): liste_constantes = [] liste_ln = [] for facteur in arg.args: if isinstance(facteur, ln) and _is_var(facteur, variable): liste_ln.append(facteur) elif not _is_var(facteur, variable): liste_constantes.append(facteur) ## print facteur, liste_constantes, liste_ln if len(liste_constantes) == len(arg.args) - 1 and len(liste_ln) == 1: expression += ln(liste_ln[0].args[0]**Add(*liste_constantes)) - arg ## print "Resultat 1er passage:", expression # Deuxime passage : ln(X1)+ln(X2)+b>0 <=> X1*X2-exp(-b)>0 for arg in args: if isinstance(arg, ln) and hasattr(arg, "has_any_symbols") and arg.has(variable): liste_ln.append(arg) elif not hasattr(arg, "has_any_symbols") or not arg.has(variable): liste_constantes.append(arg) if liste_ln and len(liste_ln) + len(liste_constantes) == len(args): # ln(X1)+ln(X2)+b>0 <=> X1*X2-exp(-b)>0 contenu_log = Mul(*(logarithme.args[0] for logarithme in liste_ln)) contenu_cst = exp(- Add(*liste_constantes)) return ens_def*positif(contenu_log - contenu_cst, variable, strict = strict) # Exponentielle # Rsolution de a*exp(f(x)) + b > 0 if getattr(expression, "is_Add", False): a_ = Wild('a') b_ = Wild('b') X_ = Wild('X') match = expression.match(a_*exp(X_) + b_) if match is not None: a = match[a_] b = match[b_] X = match[X_] if a != 0 and not a.has(variable) and not b.has(variable): if _is_pos(b): if _is_pos(a): return ens_def elif _is_neg(a): # l'ensemble de dfinition ne change pas return positif(- X + ln(-b/a), variable, strict = strict) elif _is_neg(b): if _is_pos(a): return positif(X - ln(-b/a), variable, strict = strict) elif _is_neg(a): return vide # Cas trs particulier : on utilise le fait que exp(x)>=x+1 sur R if getattr(expression, "is_Add", False): expr = expression changements = False for arg in expr.args: if isinstance(arg, exp): changements = True expr += arg.args[0] + 1 - arg if changements and (ens_def - positif(expr, variable, strict = strict) == vide): return ens_def # Sommes contenant des logarithmes : if getattr(expression, "is_Add", False): # Cas trs particulier : on utilise le fait que ln(x)<=x-1 sur ]0;+oo[ expr = expression changements = False for arg in expr.args: if isinstance(arg, ln): changements = True expr += arg.args[0] + 1 - arg if changements: try: ## print "S458475", -expr non_positif = positif(-expr, variable, strict = not strict) # complementaire (ens_def - positif(expr, variable, strict = not strict) == vide) if (ens_def - non_positif == vide): return vide except NotImplementedError: pass # Somme contenant des logarithmes : si aucune autre mthode n'a fonctionn, on tente ln(a)+ln(b)>0 <=> a*b>1 (pour a>0 et b>0) expr = Mul(*(exp(arg) for arg in expression.args)) - 1 try: return ens_def*positif(expr, variable, strict = strict) except NotImplementedError: pass ## print "Changement de variable." # En dernier recours, on tente un changement de variable : tmp2 = Symbol("_tmp2", real=True) # changements de variables courants : x, exp(x), ln(x), sqrt(x), x : for X in (variable**2, variable**3, exp(variable), ln(variable), sqrt(variable)): expr = expression.subs(X, tmp2) # Si la nouvelle variable apparait une seule fois, # le changement de variable produirait une rcurrence infinie ! if variable not in expr.atoms() and count_syms(expr, X) > 1: ## print "nouvelle variable:", X solution_temp = positif(expr, tmp2, strict = strict) solution = vide for intervalle in solution_temp.intervalles: sol = R a = intervalle.inf b = intervalle.sup if a != - oo: sol *= positif(X - a, variable, strict = strict) if b != oo: sol *= positif(b - X, variable, strict = strict) solution += sol return ens_def*solution raise NotImplementedError def resoudre(chaine, variables = (), local_dict = None): if local_dict is None: evaluer = sympify else: def evaluer(expression, local_dict = local_dict): return eval(expression, local_dict.globals, local_dict) # Prformatage: chaine = chaine.replace(')et', ') et').replace(')ou', ') ou').replace('et(', 'et (').replace('ou(', 'ou (') chaine = chaine.replace("==", "=").replace("<>", "!=").replace("=>", ">=").replace("=<", "<=") if not variables: # Dtection des variables dans l'expression arguments = chaine.split(',') variables = [Symbol(s.strip()) for s in arguments[1:]] if not variables: # Les variables ne sont pas explicitement indiques. # On tente de les dtecter. variables = set() chaine2tuple = arguments[0] for s in (' et ', ' ou ', '>=', '<=', '==', '!=', '=', '<', '>'): chaine2tuple = chaine2tuple.replace(s, ',') def find_all_symbols(e): s = set() if isinstance(e, tuple): s.update(*(find_all_symbols(elt) for elt in e)) else: s.update(e.atoms(Symbol)) return s variables = find_all_symbols(evaluer(chaine2tuple)) chaine = arguments[0] ##print 'variables:', variables if len(variables) > 1: return systeme(chaine, local_dict = local_dict) # fin = ",".join(arguments[1:]) # if fin: # fin = "," + fin debut = '' while chaine: l = [s for s in split_around_parenthesis(chaine)] if len(l) == 3: if l[0].strip() == l[2].strip() == '': return resoudre(chaine[1:-1], variables = variables, local_dict = local_dict) if ' et ' in l[0]: retour = chaine.split(' et ', 1) retour[0] = debut + retour[0] return resoudre(retour[0], variables = variables, local_dict = local_dict)*resoudre(retour[1], variables = variables, local_dict = local_dict) elif ' ou ' in l[0]: retour = chaine.split(' ou ', 1) retour[0] = debut + retour[0] return resoudre(retour[0], variables = variables, local_dict = local_dict) + resoudre(retour[1], variables = variables, local_dict = local_dict) chaine = l[2] debut += l[0] + l[1] else: if ' et ' in chaine: retour = chaine.split(' et ', 1) retour[0] = debut + retour[0] return resoudre(retour[0], variables = variables, local_dict = local_dict)*resoudre(retour[1], variables = variables, local_dict = local_dict) elif ' ou ' in chaine: retour = chaine.split(' ou ', 1) retour[0] = debut + retour[0] return resoudre(retour[0], variables = variables, local_dict = local_dict) + resoudre(retour[1], variables = variables, local_dict = local_dict) else: break chaine = debut + chaine if ">=" in chaine: gauche, droite = chaine.split(">=") return positif(evaluer(gauche + "-(" + droite + ")"), *variables) elif "<=" in chaine: gauche, droite = chaine.split("<=") return positif(evaluer(droite + "-(" + gauche + ")"), *variables) if ">" in chaine: gauche, droite = chaine.split(">") return positif(evaluer(gauche + "-(" + droite + ")"), *variables, **{"strict": True}) elif "<" in chaine: gauche, droite = chaine.split("<") return positif(evaluer(droite + "-(" + gauche + ")"), *variables, **{"strict": True}) elif "!=" in chaine: gauche, droite = chaine.split("!=") expression = evaluer(gauche + "-(" + droite + ")") return ensemble_definition(expression, *variables) - nul(expression, *variables) elif "=" in chaine: gauche, droite = chaine.split("=") expression = evaluer(gauche + "-(" + droite + ")") return nul(expression, *variables) else: raise TypeError, "'" + chaine + "' must be an (in)equation." def systeme(chaine, variables = (), local_dict = None): chaine = chaine.replace("==", "=") if local_dict is None: evaluer = sympify else: def evaluer(expression, local_dict = local_dict): return eval(expression, local_dict.globals, local_dict) def transformer(eq): gauche, droite = eq.split("=") return evaluer(gauche + "-(" + droite + ")") if not variables: arguments = chaine.split(',') variables = tuple(Symbol(s.strip()) for s in arguments[1:]) chaine = arguments[0] eqs = tuple(transformer(eq) for eq in chaine.split("et")) if not variables: variables = set() for eq in eqs: variables.update(eq.atoms(Symbol)) return solve(eqs, *variables) def _convertir_frequences(frequences, serie): if frequences is None: n = len(serie) return n*[Rational(1, n)] else: total = sum(frequences) if total != 1: return [val/total for val in frequences] return frequences def moyenne(serie, coeffs = None): u"Calcule l'esprance de la srie des (xi, fi)." frequences = _convertir_frequences(coeffs, serie) return sum(xi*fi for xi, fi in zip(serie, frequences)) def variance(serie, coeffs = None): u"Calcule la variance de la serie des (xi, fi)" frequences = _convertir_frequences(coeffs, serie) M = moyenne(serie, frequences) return sum(fi*(xi - M)**2 for xi, fi in zip(serie, frequences)) def ecart_type(serie, coeffs = None): u"Retourne l'cart-type de la srie des (xi, fi)." return sqrt(variance(serie, coeffs)) def covariance(serie1, serie2, coeffs = None): u"Retourne la covariance des deux sries." assert len(serie1) == len(serie2), u"Les deux sries doivent avoir le mme nombre de valeurs." frequences = _convertir_frequences(coeffs, serie1) x_ = moyenne(serie1, frequences) y_ = moyenne(serie2, frequences) return sum(fi*(xi - x_)*(yi - y_) for xi, yi, fi in zip(serie1, serie2, frequences)) def linreg(serie1, serie2, coeffs = None): u"""Droite de rgression par la mthode des moindres carrs. Retourne les coefficients a et b de l'quation y=ax+b de la droite de rgression par la mthode des moindres carrs. >>> from wxgeometrie.mathlib.custom_functions import linreg >>> linreg((85.6,84.5,81,80.2,72.8,71.2,73,48.1),(78.7,77.6,75.2,71.1,67.7,66.3,59.1,46.8)) (0.849191825268073, 4.50524942626518) """ a = covariance(serie1, serie2)/variance(serie1) b = moyenne(serie2) - a*moyenne(serie1) return a, b def pstfunc(chaine): u"Convertit une chaine reprsentant une fonction pst-trick en une fonction " args = [] dict_op = {'mul':'*','add':'+','exp':'**','div':'/','sub':'-'} dict_fn = {'ln':'ln'} def code_arg(s): return '(' + str(sympify(s)) + ')' for s in chaine.split(' '): if s in dict_op: assert len(args) >= 2, 'Il faut deux arguments pour ' + s args[-2] = code_arg(dict_op[s].join(args[-2:])) args.pop() elif s in dict_fn: args[-1] = code_arg(dict_fn[s] + '(' + args[-1] + ')') elif s: args.append(code_arg(s)) assert len(args) == 1, 'Il doit rester un seul argument a la fin.' return custom_str(sympify(args[0])) def aide(fonction): u"Retourne (si possible) de l'aide sur la fonction saisie." if getattr(fonction, '__name__', None) and getattr(fonction, '__doc__', None): hlp = "\n== Aide sur %s ==" %fonction.__name__ for ligne in fonction.__doc__.split('\n'): hlp += '\n' + ligne.lstrip() return hlp else: from end_user_functions import __classement__ for val in __classement__.itervalues(): if val[1] == getattr(fonction, '__name__', str(fonction)): hlp = "\n== Aide sur %s ==\n" %fonction.__name__ hlp += val[2] return hlp return "Pas d'aide disponible." def arrondir(valeur, chiffres = 0): # Nombre de chiffres de la partie entire : n = floor(log(valeur, 10)) + 1 return sympify(valeur).evalf(chiffres + n) wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/universal_functions.py0000644000175000017500000002361212014170666026014 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### #WxGeometrie #Dynamic geometry, graph plotter, and more for french mathematic teachers. #Copyright (C) 2005-2010 Nicolas Pourcelot # #This program is free software; you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation; either version 2 of the License, or #(at your option) any later version. # #This program is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with this program; if not, write to the Free Software #Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # version unicode __doc__ = """ Cette librairie contient des versions modifies des fonctions courantes, pour qu'elles puissent tre appliques aussi bien des nombres qu' des variables formelles ou encore des listes... Le dictionnaire fonctions_mathematiques contient les infos suivantes : {: [, , ]} Il est gnre via le code suivant : import math liste = math.__dict__.items() dico = {} ignore_liste = ("ldexp", "frexp", "pow", "degrees", "radians", "atan2", "hypot", "modf", "fmod", "fabs") dico.update([(nom,[nom,nom,nom]) for nom, objet in liste if not nom.startswith("_") and hasattr(objet, "__call__") and not nom in ignore_liste]) # fonctions ayant un nom diffrent dans numpy : dico["asin"][1] = "arcsin"; dico["acos"][1] = "arccos"; dico["atan"][1] = "arctan" # fonctions ayant un nom diffrent dans sympy : dico["ceil"][2] = "ceiling" # fonctions renommer : dico["ln"] = dico.pop("log") dico["log"] = dico.pop("log10") liste = dico.items() liste.sort() s = "_fonctions_mathematiques = " + repr(liste).replace("[(", "{\n").replace(")]", "}").replace(", [", ": [").replace("), (", ",\n") print s exec(s) # Vrification du contenu du dictionnaire : import math, sympy, numpy sympy.log10 = lambda x: sympy.log(x, 10) for elt in _fonctions_mathematiques.items(): m, n, s = elt[1] getattr(math, m) getattr(numpy, n) getattr(sympy, s) # Gnration du code : _fonctions_mathematiques = { 'acos': ['acos', 'acos', 'acos'], 'asin': ['asin', 'asin', 'asin'], 'atan': ['atan', 'atan', 'atan'], 'ceil': ['ceil', 'ceil', 'ceiling'], 'cos': ['cos', 'cos', 'cos'], 'cosh': ['cosh', 'cosh', 'cosh'], 'exp': ['exp', 'exp', 'exp'], 'floor': ['floor', 'floor', 'floor'], 'ln': ['log', 'log', 'log'], 'log': ['log10', 'log10', 'log10'], 'sin': ['sin', 'sin', 'sin'], 'sinh': ['sinh', 'sinh', 'sinh'], 'sqrt': ['sqrt', 'sqrt', 'sqrt'], 'tan': ['tan', 'tan', 'tan'], 'tanh': ['tanh', 'tanh', 'tanh']} for _nom, _noms in _fonctions_mathematiques.items(): _nom_math, _nom_numpy, _nom_sympy = _noms print '''\n\ndef %s(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.%s(*args,**kw) elif isinstance(arg0, complex): return _cmath.%s(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.%s(*args,**kw) else: return _numpy.%s(*args,**kw)''' %(_nom, _nom_math, _nom_math, _nom_sympy, _nom_numpy) """ _fonctions_mathematiques = { 'abs':['abs', 'abs', 'abs'], 'acos': ['acos', 'acos', 'acos'], 'asin': ['asin', 'asin', 'asin'], 'atan': ['atan', 'atan', 'atan'], 'ceil': ['ceil', 'ceil', 'ceiling'], 'cos': ['cos', 'cos', 'cos'], 'cosh': ['cosh', 'cosh', 'cosh'], 'exp': ['exp', 'exp', 'exp'], 'floor': ['floor', 'floor', 'floor'], 'ln': ['log', 'log', 'log'], 'log': ['log10', 'log10', 'log10'], 'sin': ['sin', 'sin', 'sin'], 'sinh': ['sinh', 'sinh', 'sinh'], 'sqrt': ['sqrt', 'sqrt', 'sqrt'], 'tan': ['tan', 'tan', 'tan'], 'tanh': ['tanh', 'tanh', 'tanh']} import numpy as _numpy, sympy as _sympy, math as _math, cmath as _cmath _sympy.log10 = lambda x: _sympy.log(x, 10) _cmath.floor = _math.floor # afin de renvoyer une erreur si floor est appel avec un complexe _math.abs = _cmath.abs = _sympy.abs = abs # Le code suivant est gnr automatiquement (voir plus haut) def sinh(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.sinh(*args,**kw) elif isinstance(arg0, complex): return _cmath.sinh(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.sinh(*args,**kw) else: return _numpy.sinh(*args,**kw) def asin(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.asin(*args,**kw) elif isinstance(arg0, complex): return _cmath.asin(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.asin(*args,**kw) else: return _numpy.asin(*args,**kw) def cos(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.cos(*args,**kw) elif isinstance(arg0, complex): return _cmath.cos(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.cos(*args,**kw) else: return _numpy.cos(*args,**kw) def log(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.log10(*args,**kw) elif isinstance(arg0, complex): return _cmath.log10(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.log10(*args,**kw) else: return _numpy.log10(*args,**kw) def atan(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.atan(*args,**kw) elif isinstance(arg0, complex): return _cmath.atan(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.atan(*args,**kw) else: return _numpy.atan(*args,**kw) def floor(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.floor(*args,**kw) elif isinstance(arg0, complex): return _cmath.floor(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.floor(*args,**kw) else: return _numpy.floor(*args,**kw) def ln(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.log(*args,**kw) elif isinstance(arg0, complex): return _cmath.log(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.log(*args,**kw) else: return _numpy.log(*args,**kw) def tanh(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.tanh(*args,**kw) elif isinstance(arg0, complex): return _cmath.tanh(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.tanh(*args,**kw) else: return _numpy.tanh(*args,**kw) def sqrt(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.sqrt(*args,**kw) elif isinstance(arg0, complex): return _cmath.sqrt(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.sqrt(*args,**kw) else: return _numpy.sqrt(*args,**kw) def cosh(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.cosh(*args,**kw) elif isinstance(arg0, complex): return _cmath.cosh(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.cosh(*args,**kw) else: return _numpy.cosh(*args,**kw) def exp(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.exp(*args,**kw) elif isinstance(arg0, complex): return _cmath.exp(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.exp(*args,**kw) else: return _numpy.exp(*args,**kw) def acos(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.acos(*args,**kw) elif isinstance(arg0, complex): return _cmath.acos(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.acos(*args,**kw) else: return _numpy.acos(*args,**kw) def ceil(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.ceil(*args,**kw) elif isinstance(arg0, complex): return _cmath.ceil(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.ceil(*args,**kw) else: return _numpy.ceiling(*args,**kw) def sin(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.sin(*args,**kw) elif isinstance(arg0, complex): return _cmath.sin(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.sin(*args,**kw) else: return _numpy.sin(*args,**kw) def tan(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.tan(*args,**kw) elif isinstance(arg0, complex): return _cmath.tan(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.tan(*args,**kw) else: return _numpy.tan(*args,**kw) def abs(*args, **kw): arg0 = args[0] if isinstance(arg0, (int, float, long)): return _math.abs(*args,**kw) elif isinstance(arg0, complex): return _cmath.abs(*args,**kw) elif isinstance(arg0, _sympy.Basic): return _sympy.abs(*args,**kw) else: return _numpy.abs(*args,**kw) # Code crit la main (ne pas effacer donc !) def arg(complexe): if isinstance(complexe, (int, complex, long, float)): return _cmath.log(complexe).imag elif isinstance(complexe, _sympy.Basic): return _sympy.arg(complexe) else: return _numpy.imag(_numpy.log(complex)) wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/internal_functions.py0000644000175000017500000001652012014170666025620 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## Cette librairie contient des fonctions mathmatiques usage interne from numpy import roots as nroots from sympy import Mul, Float, Basic, Poly, together, expand, powsimp, roots,\ sympify, div, Symbol from ..pylib import warning from .. import param def _simplify(expr): u"""Simplifie une expression. Alias de simplify (sympy 0.6.4). Mais simplify n'est pas garanti d'tre stable dans le temps. (Cf. simplify.__doc__).""" return together(expand(Poly.cancel(powsimp(expr)))) #TODO: Mettre jour simplify def _is_num(val): return isinstance(val, (float, Float)) def poly_factor(polynome, variable, corps = None, approchee = None): u"""Factorise un polynome une variable. Le corps peut tre R ou C. Par dfaut, le corps de factorisation est celui des coefficients.""" from .sympy_functions import simplifier_racines if approchee is None: # Paramtre utilis en interne par 'l'interpreteur' de commandes # (cf. mth. evaluer() de la classe Interprete(), dans custom_objects.py) approchee = getattr(param, 'calcul_approche', False) if polynome.is_Mul: return reduce(lambda x,y:x*y, [poly_factor(fact, variable, corps = corps) for fact in polynome.args], 1) sym_poly = polynome.as_poly(variable) coeffs = sym_poly.all_coeffs() if any(_is_num(coeff) for coeff in coeffs): approchee = True racines_brutes = {}.fromkeys(nroots(coeffs), 1) else: if corps == "R": if not all(coeff.is_real for coeff in coeffs): raise ValueError, "factorisation dans 'R' impossible." elif corps is None: if all(coeff.is_real for coeff in coeffs): corps = "R" else: corps = "C" racines_brutes = roots(polynome, variable, cubics=True, quartics=True) racines = list((simplifier_racines(racine), mult) for racine, mult in racines_brutes.iteritems()) if approchee: nbr_racines = sum(multiplicite for racine, multiplicite in racines) if nbr_racines < sym_poly.degree(): # On cherche une approximation des racines manquantes sol_approchees = list(nroots(coeffs)) # On associe chaque racine l'approximation qui lui correspond for racine, multiplicite in racines: distances = [(sol, abs(complex(racine) - sol)) for sol in sol_approchees] distances.sort(key = lambda x:x[1]) for i in range(multiplicite): distances.pop(0) # Les racines approches qui restent ne correspondent aucune racine exacte sol_approchees = [sol for sol, distance in distances] racines.extend((sympify(sol), sol_approchees.count(sol)) for sol in set(sol_approchees)) coefficient = coeffs[0] produit = 1 if corps == "R": racines_en_stock = [] multiplicites_en_stock = [] for racine, multiplicite in racines: if not isinstance(racine, Basic): racine = sympify(racine) reel = racine.is_real if not reel: # is_real n'est pas fiable (26/11/2009) # cf. ((54*6**(1/3)*93**(1/2) - 162*I*6**(1/3)*31**(1/2) - 522*6**(1/3) + 6*6**(2/3)*(-522 + 54*93**(1/2))**(1/3) + 522*I*3**(1/2)*6**(1/3) + 6*I*3**(1/2)*6**(2/3)*(-522 + 54*93**(1/2))**(1/3) - 24*(-522 + 54*93**(1/2))**(2/3))/(36*(-522 + 54*93**(1/2))**(2/3))).is_real re, im = racine.expand(complex=True).as_real_imag() reel = im.is_zero or im.evalf(80).epsilon_eq(0,'10e-80') if reel: racine = re # Approximation utile (?) pour la factorisation de certains polynmes de degrs 3 et 4 # De toute manire, une vrification de la factorisation par division euclidienne # a lieu la fin de l'algorithme. if reel: produit *= (variable - racine)**multiplicite else: conjuguee = racine.conjugate() if conjuguee in racines_en_stock: produit *= (variable**2 - 2*re*variable + re**2 + im**2)**multiplicite i = racines_en_stock.index(conjuguee) racines_en_stock.pop(i) multiplicites_en_stock.pop(i) else: racines_en_stock.append(racine) multiplicites_en_stock.append(multiplicite) if racines_en_stock: # Il reste des racines qu'on n'a pas russi appareiller. P = 1 for racine, multiplicite in zip(racines_en_stock, multiplicites_en_stock): P *= (variable - racine)**multiplicite produit *= P.expand() else: for racine, multiplicite in racines: produit *= (variable - racine)**multiplicite # print produit quotient, reste = div(polynome, coefficient*produit, variable) if reste != 0 and not approchee: raise NotImplementedError poly_factorise = coefficient*produit*quotient if isinstance(poly_factorise, Mul) and poly_factorise.args[0] == 1.: poly_factorise = Mul(*poly_factorise.args[1:]) # sinon, poly_factor(x**2+2.5*x+1,x) donne 1.0*(x + 0.5)*(x + 2.0) return poly_factorise def syms(expression): u"""Retourne la liste des symboles utiliss par l'expression.""" return tuple(expression.atoms(Symbol)) def extract_var(expression): u"""Retourne la variable de l'expression (renvoie un avertissement s'il y en a plusieurs, et 'x' par dfaut s'il n'y en a pas).""" if hasattr(expression, "atoms"): symboles = expression.atoms(Symbol) if len(symboles) == 0: return Symbol("x") else: if len(symboles) > 1 and param.debug: warning("l'expression possede plusieurs variables.") return expression.atoms(Symbol).pop() else: return Symbol("x") def count_syms(expression, symbole): u"""Compte le nombre d'occurence de la variable dans l'expression.""" if expression.has(symbole): if expression.is_Atom: return 1 else: return sum(count_syms(arg, symbole) for arg in expression.args) else: return 0 wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/parsers.py0000644000175000017500000004130012014170666023365 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Objets CALC # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import keyword, re from itertools import chain, izip_longest from sympy import Expr from ..pylib import regsub, split_around_parenthesis, debug # Commandes LaTeX caractres uniquement alphabtiques (sans le \ prliminaire) dictionnaire_latex_commandes = { "nombre" : "", "nb": "", "left": "", "right": "", "sqrt": " sqrt", "sin": " sin", "cos": " cos", "tan": " tan", "ln":" ln", "log": " log", "exp": " exp", "times": "*", "infty": "oo", "oo": "oo", "i": " i", "e": " e", "pi": " pi", "quad": " ", "qquad": " ", "text": "", "mathrm": "", "bar": "conjug", "le": "<=", "leqslant": "<=", "ge": ">=", "geqslant": ">=", "displaystyle": "", } # Commandes LaTeX caractres NON uniquement alphabtiques dictionnaire_latex_special = { "~": " ", "\\,": " ", "\\!": " ", "\\;": " ", "\\:": " ", "\\\\": " ", } # Nom de variable VAR = "(?:[_A-Za-z][_A-Za-z0-9]*)" # Nom de variable, mais pas d'attribut VAR_NOT_ATTR = "(?:(?>> from wxgeometrie.mathlib.parsers import _arguments_latex >>> _arguments_latex('2{x+1}+4', 2) ['2', '{x+1}', '+4'] >>> _arguments_latex('{x+2}5+4x-17^{2+x}', 2) ['{x+2}', '5', '+4x-17^{2+x}'] """ liste = [] while len(liste) < nbr_arguments: if not chaine: raise TypeError, "Il manque des arguments." if chaine[0] != "{": liste.append(chaine[0]) chaine = chaine[1:] else: l = split_around_parenthesis(chaine, 0, "{") if len(l) != 3: raise TypeError, "Arguments mal formes: il manque des '}'." liste.append(l[1]) chaine = l[2] liste.append(chaine) return liste def _convertir_latex_frac(chaine): u"""Convertit \frac{a}{b}, \dfrac{a}{b} et \tfrac{a}{b} en ((a)/(b)). >>> from wxgeometrie.mathlib.parsers import _convertir_latex_frac >>> _convertir_latex_frac('3+\dfrac{1}{2x+1}+5x+1') '3+((1)/(2x+1))+5x+1' """ for substr in (r"\frac", r"\dfrac", r"\tfrac"): i = chaine.find(substr) while i != -1: arg1, arg2, reste = _arguments_latex(chaine[i + len(substr):], 2) if arg1[0] == '{' and arg1[-1] == '}': arg1 = '(' + arg1[1:-1] + ')' if arg2[0] == '{' and arg2[-1] == '}': arg2 = '(' + arg2[1:-1] + ')' chaine = chaine[:i] + "(" + arg1 + "/" + arg2 + ")" + reste i = chaine.find(substr, i) return chaine def _ajouter_mult_manquants(formule, fonctions = (), verbose = None, mots_cles = ()): if isinstance(fonctions, dict): # On rcupre les fonctions de l'espace des noms # (tout objet 'callable' sauf certains objets Sympy). fonctions = [key for key, val in fonctions.items() if hasattr(val, "__call__") and not isinstance(val, Expr)] if verbose: print '1', formule # Le code qui suit remplace les expressions style 3x ou 2.5cos(x) par 3*x et 2.5*cos(x). formule = regsub(NBR + "[ ]?(?=[a-zA-Z_])", formule, lambda s: s.rstrip() + '*') # TODO: traiter le cas des mots-cls # De meme, on rajoute les * entre deux parentheses... formule = formule.replace(")(",")*(") if verbose: print '2', formule # Si a, b, c... ne sont pas des fonctions, on remplace "a(" par "a*(", etc... def f1(s): s = s.strip() if s in fonctions:# or s in mots_cles: return s elif s[0] == '.' and not s[1:].isdigit(): # Probablement une mthode # TODO: amliorer dtection en remontant avant le point # (pour distinguer entre 2.x(1+x) et a2.x(1+x)) return s else: return s + "*" formule = regsub("[.]?" + NBR_OR_VAR + "[ ]?(?=[(])", formule, f1) if verbose: print '3', formule # "f x" devient "f(x)" si f est une fonction, "f*x" sinon ; # de mme, "f 2.5" devient "f(2.5)" si f est une fonction, et "f*2.5" sinon. # (Si f est un mot-cl (if, then, else, for...), on n'y touche pas.) def f2(s): l = s.split() if l[0] in mots_cles: return s elif l[0] in fonctions: return l[0] + "(" + l[1] + ")" else: return l[0] + "*" + l[1] formule_initiale = "" i = 0 # scurit sans doute inutile... while formule != formule_initiale and i < 1000: formule_initiale = formule formule = regsub(VAR + "[ ]" + NBR + "?[*/]?" + NBR_OR_VAR , formule, f2) i += 1 if verbose: print '4', formule # On remplace ")x" par ")*x" formule = regsub("[)][ ]?\w", formule, lambda s: s[0] + '*' + s[-1]) # TODO: traiter le cas des mots-cls # Cas des mots-cls: on supprime les '*' introduits tort. mc = '|'.join(mots_cles) formule = regsub("(? is a marker for substring emplacements. s = s.replace('@', '@@') start = end = None morceaux = [] subs = [] pos = 0 while pos < len(s): if start is None: # Start of a new substring. start = s.find('"', pos) if start == -1: start = None morceaux.append(s[end:start]) if start is None: break # Two types of substrings are recognised: "type 1", and """type 2""". marker = ('"""' if s.startswith('"""', start) else '"') n = len(marker) pos = start + n else: # End of the substring. end = s.find(marker, pos) if s.endswith('\\', 0, end): # Use \" to escape " caracter. pos = end + 1 # '"""\"+1\" ici, et non \"+n\""""' continue if end == -1: raise SyntaxError, "String unclosed !" end += n subs.append(s[start:end]) pos = end start = None return '<@>'.join(morceaux), subs def _inject_inner_str(s, str_list): s_list = s.split('<@>') # Combine alternativement les 2 listes i = filter(None, chain.from_iterable(izip_longest(s_list, str_list))) return ''.join(i).replace('@@', '@') def traduire_formule(formule = "", fonctions = (), OOo = True, LaTeX = True, changer_separateurs = False, separateurs_personnels = (",", ";"), simpify = False, verbose = None, mots_cles = tuple(keyword.kwlist)): # Les chanes internes ne doivent pas tre modifies # http://wxgeo.free.fr/tracker/index.php?do=details&task_id=129&project=1 # Algorithme pour le parser de mathlib: # - remplacer @ par @@ # - dtection des chanes de textes, remplaces par <@> # -> gnration d'une liste de chanes (ex: ["texte 1", """texte 2""", "texte 3", "texte 4"]) # On applique le parser... #- remplacer <@> par les chanes prlablement enregistres #-remplacer @@ par @ formule, substrings_list = _extract_inner_str(formule) # On peut choisir comme separateur decimal la virgule (convention francaise en particulier) if changer_separateurs: formule = formule.replace(separateurs_personnels[0], ".").replace(separateurs_personnels[1], ",") formule = _simplifier(formule) if verbose: print '0', formule # Diffrentes faons de rentrer les puissances : formule = formule.replace("^", "**").replace(u'',"**2").replace(u'',"**3") formule = formule.replace(u'\u2074',"**4").replace(u'\u2075',"**5").replace(u'\u2076',"**6") formule = formule.replace(u'\u2077',"**7").replace(u'\u2078',"**8").replace(u'\u2079',"**9") # Caractres unicode # remplace le tiret long en '-' formule = formule.replace(u'\u2013', "-").replace(u'\u2212', "-") # Conversion criture dcimale infinie priodique -> fraction def to_frac(reg): p_entiere, p_decimale, periode = reg.groups() chaine = '((' + p_entiere + p_decimale chaine += '+' + (periode.lstrip('0') or '0') + '/' + len(periode)*'9' chaine += ')/1' + len(p_decimale)*'0' + ')' return chaine formule = re.sub(r"(\d+)[.](\d*)\[(\d+)\]", to_frac, formule) # exemple: 17.03[45] -> ((1703+45/99)/100) # Aprs calcul, on on obtiendra bien 17.03454545... = 9369/550 # il est plus intuitif d'utiliser le symbole % pour les pourcentages, et mod pour le modulo. if LaTeX: formule = formule.replace("\\%", "/100 ") formule = formule.replace("%", "/100 ") formule = _simplifier(formule) formule = formule.replace(" mod ", "%").replace(" modulo ", "%") formule = formule.replace(")mod ", ")%").replace(")modulo ", ")%") formule = formule.replace("}mod ", "}%").replace("}modulo ", "}%") # interprtation de 0+ et de 0- (entre autres) formule = formule.replace("+)", ",'+')").replace("-)", ",'-')") # conversion degrs -> radians formule = formule.replace(u'', '*pi/180') if OOo: # Gestion des matrices. # NB: faire en premier, en tout cas avant de remplacer '{}' par '()'. deb = formule.find("matrix{") while deb != -1: matrice, reste = _arguments_latex(formule[deb + 6:], 1) matrice = 'mat([[' + matrice[1:-1].replace(r'##', '],[').replace('#', ',') + ']])' formule = formule[:deb] + matrice + reste deb = formule.find("matrix{", deb) #Conversion de quelques formules latex ultra-frquentes (comme \frac, \dfrac, \tfrac, \sqrt, suppression de \nombre, etc.). if LaTeX: # Gestion des matrices. # NB: faire en premier, en tout cas avant de remplacer '\\'. for substr in (r"matrix", r"pmatrix", r"bmatrix", r"vmatrix", r"Vmatrix", r"smallmatrix", ): deb = formule.find(r"\begin{" + substr + "}") while deb != -1: fin = formule.find(r"\end{" + substr + "}", deb) avant = formule[:deb] coeur = formule[deb + len(substr) + 8:fin].replace('\n', '').rstrip('\\') apres = formule[fin + len(substr) + 6:] coeur = 'mat([[' + coeur.replace(r'\\', '],[').replace('&', ',') + ']])' formule = avant + coeur + apres deb = formule.find(substr, deb) # Suppression ou remplacement de commandes courantes for pattern, repl in dictionnaire_latex_commandes.items(): formule = re.sub("\\\\" + pattern + "(?![A-Za-z])", repl, formule) for substr, repl in dictionnaire_latex_special.items(): formule = formule.replace(substr, repl) formule = _simplifier(formule) # '\dfrac{a}{b}' devient '(a)/(b)' (idem pour \frac et \tfrac) formule = _convertir_latex_frac(formule) formule = formule.replace("{", "(").replace("}", ")") if OOo: # transforme les accolades en parentheses (utile par exemple pour les fonctions issues d'OpenOffice.org). formule = formule.replace("{", "(").replace("}", ")") formule = regsub("[ ]?(left|right)[])([]", formule, lambda s: s[-1]) # De mme, les notations "times", "over" et "sup" d'OpenOffice.org sont converties. formule = regsub("\Wtimes\W", formule, lambda s: (s[0] + '*' + s[-1]).strip()) formule = regsub("\Wover\W", formule, lambda s: (s[0] + '/' + s[-1]).strip()) formule = regsub("\Wsup\W", formule, lambda s: (s[0] + '**' + s[-1]).strip()) formule = formule.replace('infinity', 'oo') formule = _ajouter_mult_manquants(formule, fonctions = fonctions, verbose = verbose, mots_cles = mots_cles) if verbose: print '5', formule # n! devient factoriel(n). formule = regsub("\w+[!]", formule, lambda s: 'factoriel(' + s[:-1] + ')') # (5 2) devient binomial(5, 2) formule = regsub("[(]" + NBR + "[ ]+" + NBR + "[)]", formule, lambda s: 'binomial(' + ",".join(s[1:-1].split()) + ')') if verbose: print '6', formule # f' devient derivee(f), f'' devient derivee(derivee(f)), etc. def prime2derivee(s): n = s.count("'") # nombre de ' return n*"derivee(" + s.rstrip("'") + n*")" formule = regsub(VAR + "[']+", formule, prime2derivee) formule = formule.replace("`", "'") if verbose: print '7', formule if simpify: def transformer(chaine): if "." in chaine: return "__decimal__('" + chaine + "')" else: return "__sympify__(" + chaine + ")" formule = regsub(NBR, formule, transformer) formule = _inject_inner_str(formule, substrings_list) if verbose is not False: debug(formule, "[formule transformee]") return formule def simplifier_ecriture(formule): formule = formule.replace('**', '^') formule = formule.replace('*(', '(') formule = formule.replace(')*', ')') formule = re.sub(r'[*](?![-+.0-9])', ' ', formule) return formule ##def simplifier_ecriture(formule): ## # Simplification de l'criture des puissances ## formule = formule.replace('**', '^') ## ## # Simplification de l'criture des racines ## def simp_sqrt(m): ## return 'sqrt(' + m.group(1) + ')' ## formule = re.sub(r'\(([^()]+)\)\^\(1/2\)', simp_sqrt, formule) ## formule = re.sub(r'([A-Za-z_][A-Za-z_0-9]*|[0-9]+)\^\(1/2\)', simp_sqrt, formule) ## # Simplification de l'criture des produits ## formule = formule.replace(')*(', ')(') ## def simp_mul(m): ## return m.group(0).replace('*', ' ') ## formule = re.sub(r'(?=1<=3") assert_latex(r"100\left(\left(1+\dfrac{50}{100}\right)^\frac{1}{10}-1\right)", "100*((1+((50)/(100)))**((1)/(10))-1)") assert_latex("M = \\begin{pmatrix}\n0,6 & 0,4\\\\\n0,75& 0,25\\\\\n\\end{pmatrix}", 'M=mat([[0,6,0,4],[0,75,0,25]])') def test_NBR(): assert_NBR_SIGNE("-2.56") assert_NBR_SIGNE("-.56") assert_NBR_SIGNE("+5.") assert_NBR_SIGNE("+5.056") assert_NBR("56") assert_NBR(".46") assert_NBR(".015") assert_NBR("752.") assert_NBR("740.54") assert_not_NBR("5-6") assert_not_NBR(".") def test_VAR(): assert_VAR("Arertytre") assert_VAR("a") assert_VAR("_") assert_VAR("_45ui") assert_VAR("A13") assert_not_VAR("1A") assert_not_VAR("2") def test_search_VAR_NOT_ATTR(): assert_find_VAR_NOT_ATTR("a") assert_find_VAR_NOT_ATTR("1+_arrt9876") assert_find_VAR_NOT_ATTR("5*t_566") assert_find_VAR_NOT_ATTR("(_.t)/3") assert_not_find_VAR_NOT_ATTR(".aert") assert_not_find_VAR_NOT_ATTR("4.tyu+4") assert_not_find_VAR_NOT_ATTR("89eeazt") assert_not_find_VAR_NOT_ATTR("2-._ez") def test_arguments_LaTeX(): assert_arg_latex('2{x+1}+4', '2', '{x+1}', '+4') assert_arg_latex('{x+2}5+4x-17^{2+x}', '{x+2}', '5', '+4x-17^{2+x}') wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/tests/test_internal_functions.py0000644000175000017500000000256712014170666030027 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from wxgeometrie.mathlib.internal_functions import poly_factor from sympy import Symbol, sqrt, S, I as i, expand, Integer x = Symbol('x') def assert_factor(poly, *args, **kw): def convert(obj): if isinstance(obj, int): return Integer(obj) return obj var = kw.get("var", x) facteurs = poly_factor(poly, var) poly_args = facteurs.args s = set() s.update(convert(arg) for arg in poly_args) s.update(convert(-arg) for arg in poly_args) TEST1 = set(convert(arg) for arg in args).issubset(s) if not TEST1: print "ERREUR: ", set(args), " n'est pas inclus dans ", s print "Difference: ", set(args).difference(s) print [type(obj) for obj in set(args).difference(s)] TEST2 = (expand(facteurs) == expand(poly)) if not TEST2: print "ERREUR: ", expand(facteurs), " != ", expand(poly) assert(TEST1) assert(TEST2) def test_factor(): assert_factor(x**2 - 1, x-1, x+1) assert_factor(x**2 - 2, x - sqrt(2), x + sqrt(2)) assert_factor(2*x**2 - 2, 2, x-1, x+1) assert_factor(1 + 2*x + 2*x**2 + x**3, x+1, x**2+x+1) assert_factor(2*x, 2, x) # assert_factor(x**2-i, x+sqrt(2)/2+i*sqrt(2)/2, x-sqrt(2)/2-i*sqrt(2)/2) assert_factor(x**2-i, x + (-1)**(S(1)/4), x - (-1)**(S(1)/4)) wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/tests/test_interprete.py0000644000175000017500000002431512014170666026277 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from pytest import XFAIL from tools.testlib import assertRaises, assertAlmostEqual from wxgeometrie.mathlib.interprete import Interprete from sympy import S VERBOSE = False def assert_resultat(s, resultat, latex = None, **parametres): i = Interprete(verbose = VERBOSE, **parametres) r, l = i.evaluer(s) if r != resultat: i = Interprete(verbose = True, **parametres) r, l = i.evaluer(s) print "ERREUR (" + s + "): ", r, " != ", resultat assert(r == resultat) if latex is not None: latex = "$" + latex + "$" if l != latex: print "ERREUR (" + s + "): ", l, " != ", latex assert(l == latex) def assert_resoudre(s, *args, **kw): assert_resultat("resoudre(" + s + ")", *args, **kw) def assert_approche(s, resultat, latex = None, **parametres): assert_resultat(s, resultat, latex, calcul_exact = False, **parametres) def assertEqual(x, y): if x != y: print "ERREUR:", repr(x), "!=", repr(y) assert(x == y) def assertDernier(i, s): assertEqual(str(i.derniers_resultats[-1]), s) def test_exemples_de_base(): # Nombres assert_resultat('2+2', '4', '4') # Symboles assert_resultat('pi+1+2pi', '1 + 3 pi', '1 + 3 \\pi') assert_resultat('oo+5*oo', '+oo') assert_resultat('i**2-i', '-1 - i', '-1 - \\mathrm{i}') assert_resultat('5e-3', '-3 + 5 e', '-3 + 5 \\mathrm{e}') # Analyse assert_resultat('limite(x^2-x+3,+oo)', '+oo', '+\\infty') assert_resultat('derive(x^2+2x-3)', '2 x + 2', '2 x + 2') assert_resultat('integre(2x+7)', 'x^2 + 7 x', 'x^{2} + 7 x') assert_resultat('integre(x+1,(x,-1,1))', '2', '2') assert_resultat('integre(x+1,x,-1,1)', '2', '2') assert_resultat('taylor(sin x,x,0,4)', 'x - x^3/6 + O(x^4)', \ 'x - \\frac{1}{6} x^{3} + \\mathcal{O}\\left(x^{4}\\right)') assert_resultat('cos x>>taylor', \ '1 - x^2/2 + x^4/24 + O(x^5)', \ '1 - \\frac{1}{2} x^{2} + \\frac{1}{24} x^{4} + \\mathcal{O}\\left(x^{5}\\right)') # Algbre assert_resultat('developpe((x-3)(x+7)(2y+x+5))', \ 'x^3 + 2 x^2 y + 9 x^2 + 8 x y - x - 42 y - 105', \ 'x^{3} + 2 x^{2} y + 9 x^{2} + 8 x y - x - 42 y -105') assert_resultat('factorise(x^2-7x+3)', \ '(x - 7/2 - sqrt(37)/2)(x - 7/2 + sqrt(37)/2)', r'\left(x - \frac{7}{2} - \frac{1}{2} \sqrt{37}\right) \left(x - \frac{7}{2} + \frac{1}{2} \sqrt{37}\right)') assert_resultat('factorise(x^2+x)', 'x(x + 1)', 'x \\left(x + 1\\right)') assert_resultat('factor(exp(x)x^2+5/2x*exp(x)+exp(x))', '(x + 1/2)(x + 2)exp(x)') assert_resultat('factor(exp(x)x^2+2.5x*exp(x)+exp(x))', '(x + 0.5)(x + 2)exp(x)') assert_resultat('factorise(exp(2x)*x^2+x*exp(x))', \ 'x(x exp(x) + 1)exp(x)', \ 'x \\left(x \\mathrm{e}^{x} + 1\\right) \\mathrm{e}^{x}') assert_resultat('factorise(x^2+7x+53)', 'x^2 + 7 x + 53', 'x^{2} + 7 x + 53') assert_resultat('factor(exp(x)x^2+2x*exp(x)+exp(x))', \ '(x + 1)^2 exp(x)', \ '\left(x + 1\\right)^{2} \\mathrm{e}^{x}') assert_resultat('cfactorise(x^2+7x+53)', \ '(x + 7/2 - sqrt(163)i/2)(x + 7/2 + sqrt(163)i/2)', \ r'\left(x + \frac{7}{2} - \frac{1}{2} \sqrt{163} \mathrm{i}\right) \left(x + \frac{7}{2} + \frac{1}{2} \sqrt{163} \mathrm{i}\right)') assert_resultat('evalue(pi-1)', '2.14159265358979324', '2.14159265358979324') assert_resultat('somme(x^2,(x,1,7))', '140', '140') assert_resultat('somme(x^2,x,1,7)', '140', '140') assert_resultat('product(x^2,(x,1,7))', '25401600', '25401600') assert_resultat('product(x^2,x,1,7)', '25401600', '25401600') assert_resultat('limit(x^2-x,oo)', '+oo', '+\infty') assert_resultat('abs(pi-5)', '-pi + 5', r'- \pi + 5') assert_resultat('abs(x-5)', 'abs(x - 5)', r'\left|{x -5}\right|') assert_resultat('i(1+i)', r'-1 + i', r'-1 + \mathrm{i}') assert_resultat('i sqrt(3)', r'sqrt(3)i', r'\sqrt{3} \mathrm{i}') assert_resultat('pi sqrt(3)', r'sqrt(3)pi', r'\sqrt{3} \pi') assert_resultat('sqrt(1+e)', r'sqrt(1 + e)', r'\sqrt{1 + \mathrm{e}}') assert_resultat('(5-2i)(5+2i)', r'29', r'29') assert_resultat('resous(2x=1)', r'{1/2}', r'\{\frac{1}{2}\}') assert_resultat('jhms(250000)', r'2 j 21 h 26 min 40 s', r'2 \mathrm{j}\, 21 \mathrm{h}\, 26 \mathrm{min}\, 40 \mathrm{s}') assert_resultat(r'pi\approx', r'3.14159265358979324', r'3.14159265358979324', formatage_LaTeX = True) assert_resultat('rassemble(1/x+1/(x*(x+1)))', '(x + 2)/(x^2 + x)', r'\frac{x + 2}{x^{2} + x}') assert_resultat('factorise(-2 exp(-x) - (3 - 2 x)exp(-x))', '(2 x - 5)exp(-x)', r'\frac{2 x -5}{\mathrm{e}^{x}}') assert_resultat('-x^2+2x-3>>factor', '-x^2 + 2 x - 3') assert_resultat('abs(-24/5 - 2 i/5)', '2 sqrt(145)/5') @XFAIL def test_resolution_complexe(): assert_resultat('resoudre(2+\i=\dfrac{2\i z}{z-1})', '3/5 + 4*i/5', r'\frac{3}{5} + \frac{4}{5} \mathrm{i}') def test_fonctions_avances(): pass def test_resoudre(): assert_resoudre('2x+3>5x-4 et 3x+1>=4x-4', r']-oo;7/3[') assert_resoudre('2=-a+b et -1=3a+b', r'{a: -3/4, b: 5/4}') assert_resoudre(r'3-x\ge 1+2x\\\text{et}\\4x<2+10x', ']-1/3;2/3]', r']- \frac{1}{3};\frac{2}{3}]', formatage_LaTeX = True) assert_resoudre('2exp(x)>3', ']-ln(2) + ln(3);+oo[') #TODO: Rassembler les ln: ]ln(3/2);+oo[ assert_resoudre('x^3-30x^2+112=0', '{-6 sqrt(7) + 14 ; 2 ; 14 + 6 sqrt(7)}', r'\{- 6 \sqrt{7} + 14\,;\, 2\,;\, 14 + 6 \sqrt{7}\}') # assert_resoudre(r'ln(x^2)-ln(x+1)>1', ']-1;e/2 - sqrt(4 e + exp(2))/2[U]e/2 + sqrt(4 e + exp(2))/2;+oo[') assert_resoudre(r'ln(x^2)-ln(x+1)>1', ']-1;-sqrt(e + 4)exp(1/2)/2 + e/2[U]e/2 + sqrt(e + 4)exp(1/2)/2;+oo[') #TODO: @SLOW wrapper should be defined, and the test only run in some circonstances # (for ex, a 'slow' keyword in tools/tests.py arguments) def test_longs(): # NB: Test trs long (15-20 secondes) ! pass def test_approches(): assert_approche('pi-1', '2.14159265358979324', '2.14159265358979324') assert_approche('factor(x^2+2.5x+1)', '(x + 0.5)(x + 2)') assert_approche('factor(exp(x)x^2+2.5x*exp(x)+exp(x))', '(x + 0.5)(x + 2)exp(x)') assert_approche('ln(2.5)', '0.916290731874155065') assert_approche('resoudre(x^3-30x^2+112=0)', '{-1.87450786638754354 ; 2 ; 29.8745078663875435}', r'\{-1.87450786638754354\,;\, 2\,;\, 29.8745078663875435\}') def test_session(): i = Interprete(verbose = VERBOSE) i.evaluer("1+7") i.evaluer("x-3") i.evaluer("ans()+ans(1)") assertDernier(i, "x + 5") i.evaluer("f(x, y, z)=2x+3y-z") i.evaluer("f(-1, 5, a)") assertDernier(i, "-a + 13") i.evaluer("f(x)=x^2-7x+3") i.evaluer("f'(x)") assertDernier(i, "2*x - 7") # Noms rservs assertRaises(NameError, i.evaluer, "e=3") assertRaises(NameError, i.evaluer, "pi=3") assertRaises(NameError, i.evaluer, "i=3") assertRaises(NameError, i.evaluer, "oo=3") assertRaises(NameError, i.evaluer, "factorise=3") # Etc. # Test des gnrateurs i.evaluer('f(x)=x+3') i.evaluer('[f(j) for j in range(1,11)]') assertDernier(i, '[4, 5, 6, 7, 8, 9, 10, 11, 12, 13]') i.evaluer('tuple(i for i in range(7))') assertDernier(i, '(0, 1, 2, 3, 4, 5, 6)') i.evaluer('[j for j in range(7)]') assertDernier(i, '[0, 1, 2, 3, 4, 5, 6]') # _11 is an alias for ans(11) i.evaluer('_11 == _') assertDernier(i, 'True') i.evaluer('_7') assertDernier(i, "2*x - 7") # _ is an alias for ans(-1), __ is an alias for ans(-2), and so on. i.evaluer('_ == -7 + 2*x') assertDernier(i, 'True') i.evaluer('__') assertDernier(i, "2*x - 7") i.evaluer('______') # ans(-6) assertDernier(i, '(0, 1, 2, 3, 4, 5, 6)') # Affichage des chanes en mode text (et non math) i.evaluer('"Bonjour !"') assert i.latex_dernier_resultat == u'\u201CBonjour !\u201D' i.changer_separateurs = True resultat, latex = i.evaluer('1,2') assert resultat == '1,2' assertAlmostEqual(i.derniers_resultats[-1], 1.2) resultat, latex = i.evaluer('"1,2"') assert resultat == '"1,2"' i.evaluer('?aide') i.evaluer('aide?') i.evaluer('aide(aide)') msg_aide = u"\n== Aide sur aide ==\nRetourne (si possible) de l'aide sur la fonction saisie." resultats = i.derniers_resultats assert resultats[-3:] == [msg_aide, msg_aide, msg_aide] # LaTeX latex = i.evaluer("gamma(x)")[1] assert latex == r'$\mathrm{\Gamma}\left(x\right)$' def test_issue_129(): assert_resultat('"x(x+1)" + """x!"""', '"x(x+1)x!"') assert_resultat(r'""" "" """ + " \"\"\" "', r'" \"\" \"\"\" "') def test_issue_185(): i = Interprete(verbose = VERBOSE) i.evaluer("a=1+I") i.evaluer("a z") assertDernier(i, 'z*(1 + I)') def test_issue_206(): i = Interprete(verbose = VERBOSE) etat_interne = \ u"""_ = 0 @derniers_resultats = [ 're(x)', ]""" i.load_state(etat_interne) i.evaluer("-1+\i\sqrt{3}") assertDernier(i, '-1 + 3**(1/2)*I') i.evaluer('-x**2 + 2*x - 3>>factor') assertDernier(i, '-x**2 + 2*x - 3') def test_issue_206_bis(): i = Interprete(verbose = VERBOSE) etat_interne = \ u"""_ = 0 @derniers_resultats = [ 'Abs(x)', ]""" i.load_state(etat_interne) i.evaluer('abs(-24/5 - 2 i/5)') assertDernier(i, '2*145**(1/2)/5') def test_issue_206_ter(): i = Interprete(verbose = VERBOSE) etat_interne = \ u"""_ = 0 @derniers_resultats = [ 'atan2(x, y)', ]""" i.load_state(etat_interne) i.evaluer('ln(9)-2ln(3)') assertDernier(i, '0') def test_systeme(): i = Interprete(verbose = VERBOSE, adapter_separateur=False) i.evaluer("g(x)=a x^3+b x^2 + c x + d") i.evaluer("resoudre(g(-3)=2 et g(1)=6 et g(5)=3 et g'(1)=0)") res = i.derniers_resultats[-1] assert isinstance(res, dict) assertEqual(res, {S('a'): S(1)/128, S('b'): -S(31)/128, S('c'): S(59)/128, S('d'): S(739)/128}) wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/tests/test_custom_functions.py0000644000175000017500000000321512014170666027514 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from pytest import XFAIL from sympy import exp, sqrt, Symbol #from tools.testlib import * from wxgeometrie.mathlib.custom_functions import resoudre, positif, ensemble_definition #VERBOSE = False def assertEqual(x, y): if x != y: print "ERREUR:", repr(x), "!=", repr(y) assert(x == y) def assert_resoudre(x, y): assertEqual(str(resoudre(x)), y) def assert_positif(x, y): assertEqual(unicode(positif(x)), y) def assert_ens_def(x, y): assertEqual(unicode(ensemble_definition(x)), y) def test_resoudre(): assert_resoudre("2*x=0", "{0}") assert_resoudre("2*x>0", u"]0;+oo[") assert_resoudre("(x>4 et x<7) ou (x/2-1>=3)", u"]4;7[U[8;+oo[") assert_resoudre("(2)*x>0", u"]0;+oo[") assert_resoudre("2*x+3>5*x-4 et 3*x+1>=4*x-4", u']-oo;7/3[') assert_resoudre("2*x+3*y=4 et 4*x-2*y=1", u"{x: 11/16, y: 7/8}") assert_resoudre("ln(x)=0", "]-oo;+oo[") def test_positif(): x = Symbol("x") assert_positif(x**7, "[0;+oo[") assert_positif((x + 1)**5, "[-1;+oo[") assert_positif(sqrt(x + 1), "[-1;+oo[") assert_positif((x + 1)**6, "]-oo;+oo[") assert_positif(-x**2 - x, "[-1;0]") assert_positif(sqrt(5), "]-oo;+oo[") assert_positif((x - 1)/(x + 1), ']-oo;-1[U[1;+oo[') assert_positif(x*exp(3) - 1, '[exp(-3);+oo[') @XFAIL def test_positif2(): x = Symbol("x") assert_positif(x - 1 + exp(x), "[0;+oo[") def test_ensemble_definition(): x = Symbol("x") assert_ens_def((2 - x)/(6 - 5*x + x**2), ']-oo;2[U]2;3[U]3;+oo[') wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/tests/tests_parsers.py~0000644000175000017500000001576012014170666026162 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) import re from customtest import * from mathlib import traduire_formule from mathlib.parsers import NBR, NBR_SIGNE, VAR, VAR_NOT_ATTR, NBR_OR_VAR, _arguments_latex liste_fonctions = [key for key in mathlib.universal_functions.__dict__.keys() if "_" not in key] liste_fonctions.append("limite") liste_fonctions.append("log10") liste_fonctions.append("mat") class TestMathlibParsers(CustomTest): def assert_formule(self, x, y, OOo, LaTeX): y_ = traduire_formule(x, fonctions = liste_fonctions, OOo = OOo, LaTeX = LaTeX, verbose = False) if y_ != y: print "/!\\ Formule: ", x traduire_formule(x, fonctions = liste_fonctions, OOo = OOo, LaTeX = LaTeX, verbose = True) print "ERREUR: ", y_, " != ", y self.assert_(y_ == y) def assert_arg_latex(self, x, *y): x = _arguments_latex(x, 2) y = list(y) if x != y: print "ERREUR (_arguments_latex): ", x, " != ", y self.assert_(x == y) def assert_all(self, x, y): self.assert_formule(x, y, OOo = True, LaTeX = False) self.assert_formule(x, y, OOo = True, LaTeX = False) self.assert_formule(x, y, OOo = False, LaTeX = True) self.assert_formule(x, y, OOo = True, LaTeX = True) def assert_OOo(self, x, y): self.assert_formule(x, y, OOo = True, LaTeX = False) self.assert_formule(x, y, OOo = True, LaTeX = True) def assert_latex(self, x, y): self.assert_formule(x, y, OOo = False, LaTeX = True) self.assert_formule(x, y, OOo = True, LaTeX = True) def assert_match(self, pattern, chaine): u"""Teste si la chaine correspond entirement au pattern.""" self.assert_(re.match(pattern + "$", chaine)) def assert_not_match(self, pattern, chaine): u"""Teste si la chaine ne correspond pas entirement au pattern.""" self.assert_(not re.match(pattern + "$", chaine)) def assert_VAR(self, chaine): self.assert_match(VAR, chaine) self.assert_match(NBR_OR_VAR, chaine) def assert_not_VAR(self, chaine): self.assert_not_match(VAR, chaine) def assert_NBR(self, chaine): self.assert_match(NBR, chaine) self.assert_match(NBR_OR_VAR, chaine) def assert_NBR(self, chaine): self.assert_match(NBR, chaine) self.assert_match(NBR_SIGNE, chaine) self.assert_match(NBR_OR_VAR, chaine) def assert_NBR_SIGNE(self, chaine): self.assert_match(NBR_SIGNE, chaine) self.assert_not_match(NBR_OR_VAR, chaine) def assert_find_VAR_NOT_ATTR(self, chaine): self.assert_(re.search(VAR_NOT_ATTR, chaine)) def assert_not_find_VAR_NOT_ATTR(self, chaine): self.assert_(not re.search(VAR_NOT_ATTR, chaine)) def assert_not_NBR(self, chaine): self.assert_not_match(NBR, chaine) def test_tous_modes(self): self.assert_all("2x+3", "2*x+3") self.assert_all("2(x+3)", "2*(x+3)") self.assert_all("(x+1)x(x+3)", "(x+1)*x*(x+3)") self.assert_all("sin(x+1)x(x+3)", "sin(x+1)*x*(x+3)") self.assert_all("(x+1)cos(x+3)", "(x+1)*cos(x+3)") self.assert_all("-1.5x^(-2)+ab+3ab(2x+y)+x(y(z+1)))2(x)", "-1.5*x**(-2)+ab+3*ab*(2*x+y)+x*(y*(z+1)))*2*(x)") self.assert_all(u"3x-2xy-2x==5y", "3*x**3-2*x**2*y-2*x==5*y") self.assert_all(u"25%*12 mod 5", "25/100*12%5") self.assert_all(u"(25%*12)mod 5", "(25/100*12)%5") self.assert_all(u"limite(1/x^3,x,1+)", "limite(1/x**3,x,1,'+')") self.assert_all(u"limite(1/x^3,x, 1- )", "limite(1/x**3,x,1,'-')") self.assert_all(u"x sin x+1", "x*sin(x)+1") self.assert_all(u"log10 ab y sin 2x+1", "log10(ab)*y*sin(2*x)+1") self.assert_all(u"cos 3.5x(x+1)", "cos(3.5*x)*(x+1)") self.assert_all(u"cos 2", "cos(2)") # Cas particulier : self.assert_all(u"cos -3", "cos-3") # Dveloppement dcimal infini priodique self.assert_all(u"17.03[45]", u"((1703+45/99)/100)") self.assert_all(u"17.[045]", u"((17+45/999)/1)") self.assert_all(u"17.1[0]", u"((171+0/9)/10)") # Ne pas rajouter de * devant les parenthses d'une mthode self.assert_all(u"A.transpose()", u"A.transpose()") def test_mode_OOo(self): self.assert_OOo("2 times 3", "2*3") self.assert_OOo("2 over 3", "2/3") self.assert_OOo("{2+5x} over {3-x}", "(2+5*x)/(3-x)") self.assert_OOo("{2-.5x}over{3-x}", "(2-.5*x)/(3-x)") self.assert_OOo("0.85 sup {1 over 7} - 1", "0.85**(1/7)-1") def test_mode_LaTeX(self): self.assert_latex("2\\times3", "2*3") self.assert_latex("\\cos x\\sin x\\exp x", "cos(x)*sin(x)*exp(x)") self.assert_latex("\\frac{2}{3}", "((2)/(3))") self.assert_latex("\\frac{2+x}{3}", "((2+x)/(3))") self.assert_latex("\\dfrac{2+x}{1-3}", "((2+x)/(1-3))") self.assert_latex("\\tfrac{2+x}{3}", "((2+x)/(3))") self.assert_latex("\\dfrac{2x^2+x-7}{6-4x}", "((2*x**2+x-7)/(6-4*x))") self.assert_latex("-\\frac12-\\dfrac4{(6-4x)^2}", "-(1/2)-(4/((6-4*x)**2))") self.assert_latex("\\left((1+10~\\%)(1+5~\\%)(1-7~\\%)\\right)^{\\frac{1}{3} }", "((1+10/100)*(1+5/100)*(1-7/100))**(((1)/(3)))") self.assert_latex("\\text{0.7}\\times (-50)^2-9\\times (-50)+200", "(0.7)*(-50)**2-9*(-50)+200") self.assert_latex("\\ln(2)+\\exp(3)+\\log(\\pi+1)", "ln(2)+exp(3)+log(pi+1)") self.assert_latex("x\ge1\le3", "x>=1<=3") self.assert_latex(r"100\left(\left(1+\dfrac{50}{100}\right)^\frac{1}{10}-1\right)", "100*((1+((50)/(100)))**((1)/(10))-1)") self.assert_latex(r"M = \begin{pmatrix}\n0,6 & 0,4\\\n0,75& 0,25\\\n\end{pmatrix}", 'M=mat([[0,6,0,4],[0,75,0,25]])') def test_NBR(self): self.assert_NBR_SIGNE("-2.56") self.assert_NBR_SIGNE("-.56") self.assert_NBR_SIGNE("+5.") self.assert_NBR_SIGNE("+5.056") self.assert_NBR("56") self.assert_NBR(".46") self.assert_NBR("752.") self.assert_NBR("740.54") self.assert_not_NBR("5-6") self.assert_not_NBR(".") def test_VAR(self): self.assert_VAR("Arertytre") self.assert_VAR("a") self.assert_VAR("_") self.assert_VAR("_45ui") self.assert_VAR("A13") self.assert_not_VAR("1A") self.assert_not_VAR("2") def test_search_VAR_NOT_ATTR(self): self.assert_find_VAR_NOT_ATTR("a") self.assert_find_VAR_NOT_ATTR("1+_arrt9876") self.assert_find_VAR_NOT_ATTR("5*t_566") self.assert_find_VAR_NOT_ATTR("(_.t)/3") self.assert_not_find_VAR_NOT_ATTR(".aert") self.assert_not_find_VAR_NOT_ATTR("4.tyu+4") self.assert_not_find_VAR_NOT_ATTR("89eeazt") self.assert_not_find_VAR_NOT_ATTR("2-._ez") def test_arguments_LaTeX(self): self.assert_arg_latex('2{x+1}+4', '2', '{x+1}', '+4') self.assert_arg_latex('{x+2}5+4x-17^{2+x}', '{x+2}', '5', '+4x-17^{2+x}') if __name__ == '__main__': unittest.main() wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/tests/tests_custom_objects.py~0000644000175000017500000001760512014170666027526 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) import re from customtest import * from mathlib import traduire_formule from mathlib.custom_objects import Interprete VERBOSE = False class TestMathlibCustomObjects(CustomTest): def assert_resultat(self, s, resultat, latex = None, **parametres): i = Interprete(verbose = VERBOSE, **parametres) r, l = i.evaluer(s) if r != resultat: i = Interprete(verbose = True, **parametres) r, l = i.evaluer(s) print "ERREUR (" + s + "): ", r, " != ", resultat self.assert_(r == resultat) if latex is not None: latex = "$" + latex + "$" if l != latex: print "ERREUR (" + s + "): ", l, " != ", latex self.assert_(l == latex) def assert_resoudre(self, s, *args, **kw): self.assert_resultat("resoudre(" + s + ")", *args, **kw) def assert_approche(self, s, resultat, latex = None, **parametres): self.assert_resultat(s, resultat, latex, calcul_exact = False, **parametres) def assertEqual(self, x, y): if x != y: print "ERREUR:", repr(x), "!=", repr(y) self.assert_(x == y) def assertDernier(self, i, s): self.assertEqual(str(i.derniers_resultats[-1]), s) def test_exemples_de_base(self): # Nombres self.assert_resultat('2+2', '4', '4') # Symboles self.assert_resultat('pi+1+2pi', '1 + 3 pi', '1 + 3 \\pi') self.assert_resultat('oo+5*oo', '+oo') self.assert_resultat('i**2-i', '-1 - i', '-1 - \\mathrm{i}') self.assert_resultat('5e-3', '-3 + 5 e', '-3 + 5 \\mathrm{e}') # Analyse self.assert_resultat('limite(x^2-x+3,+oo)', '+oo', '+\\infty') self.assert_resultat('derive(x^2+2x-3)', '2 + 2 x', '2 + 2 x') self.assert_resultat('integre(2x+7)', '7 x + x^2', '7 x + x^{2}') self.assert_resultat('integre(x+1,(x,-1,1))', '2', '2') self.assert_resultat('integre(x+1,x,-1,1)', '2', '2') self.assert_resultat('taylor(sin x,x,0,4)', 'x - x^3/6 + O(x^4)', \ 'x - \\frac{1}{6} x^{3} + \\mathcal{O}\\left(x^{4}\\right)') self.assert_resultat('cos x>>taylor', \ '1 - x^2/2 + x^4/24 + O(x^5)', \ '1 - \\frac{1}{2} x^{2} + \\frac{1}{24} x^{4} + \\mathcal{O}\\left(x^{5}\\right)') # Algbre self.assert_resultat('developpe((x-3)(x+7)(2y+x+5))', \ '-105 - x - 42 y + 8 x y + 9 x^2 + 2 y x^2 + x^3', \ '-105 - x - 42 y + 8 x y + 9 x^{2} + 2 y x^{2} + x^{3}') self.assert_resultat('factorise(x^2-7x+3)', \ '(7/2 - x + sqrt(37)/2)(7/2 - x - sqrt(37)/2)', '\\left(\\frac{7}{2} - x + \\frac{1}{2} \\sqrt{37}\\right) \\left(\\frac{7}{2} - x - \\frac{1}{2} \\sqrt{37}\\right)') self.assert_resultat('factorise(x^2+x)', 'x(1 + x)', 'x \\left(1 + x\\right)') self.assert_resultat('factor(exp(x)x^2+5/2x*exp(x)+exp(x))', '(1/2 + x)(2 + x)exp(x)') self.assert_resultat('factor(exp(x)x^2+2.5x*exp(x)+exp(x))', '(0.5 + x)(2 + x)exp(x)') self.assert_resultat('factorise(exp(2x)*x^2+x*exp(x))', \ 'x(1 + x exp(x))exp(x)', \ 'x \\left(1 + x \\mathrm{e}^{x}\\right) \\mathrm{e}^{x}') self.assert_resultat('factorise(x^2+7x+53)', '53 + 7 x + x^2', '53 + 7 x + x^{2}') self.assert_resultat('factor(exp(x)x^2+2x*exp(x)+exp(x))', \ '(1 + x)^2 exp(x)', \ '\left(1 + x\\right)^{2} \\mathrm{e}^{x}') self.assert_resultat('cfactorise(x^2+7x+53)', \ '(7/2 + x + i sqrt(163)/2)(7/2 + x - i sqrt(163)/2)', \ '\\left(\\frac{7}{2} + x + \\frac{1}{2} \\mathrm{i} \\sqrt{163}\\right) \\left(\\frac{7}{2} + x - \\frac{1}{2} \\mathrm{i} \\sqrt{163}\\right)') self.assert_resultat('evalue(pi-1)', '2.14159265358979324', '2.14159265358979324') self.assert_resultat('somme(x^2,(x,1,7))', '140', '140') self.assert_resultat('somme(x^2,x,1,7)', '140', '140') self.assert_resultat('product(x^2,(x,1,7))', '25401600', '25401600') self.assert_resultat('product(x^2,x,1,7)', '25401600', '25401600') self.assert_resultat('limit(x^2-x,oo)', '+oo', '+\infty') self.assert_resultat('abs(pi-5)', '5 - pi', r'5 - \pi') self.assert_resultat('abs(x-5)', 'abs(5 - x)', r'\left|{5 - x}\right|') self.assert_resultat('i(1+i)', r'-1 + i', r'-1 + \mathrm{i}') self.assert_resultat('i sqrt(3)', r'i sqrt(3)', r'\mathrm{i} \sqrt{3}') self.assert_resultat('pi sqrt(3)', r'pi sqrt(3)', r'\pi \sqrt{3}') self.assert_resultat('sqrt(1+e)', r'sqrt(1 + e)', r'\sqrt{1 + \mathrm{e}}') self.assert_resultat('(5-2i)(5+2i)', r'29', r'29') self.assert_resultat('resous(2x=1)', r'{1/2}', r'\{\frac{1}{2}\}') self.assert_resultat('jhms(250000)', r'2 j 21 h 26 min 40 s', r'2 \mathrm{j}\, 21 \mathrm{h}\, 26 \mathrm{min}\, 40 \mathrm{s}') self.assert_resultat(r'pi\approx', r'3.14159265358979324', r'3.14159265358979324', formatage_LaTeX = True) self.assert_resultat('rassemble(1/x+1/(x*(x+1)))', '(2 + x)/(x + x^2)', r'\frac{2 + x}{x + x^{2}}') self.assert_resultat('factorise(-2 exp(-x) - (3 - 2 x)exp(-x))', '-(5 - 2 x)exp(-x)', r'- \left(5 - 2 x\right) \mathrm{e}^{- x}') # self.assert_resultat('resoudre(2+\i=\dfrac{2\i z}{z-1})', '3/5 + 4*i/5', r'\frac{3}{5} + \frac{4}{5} \mathrm{i}') def test_fonctions_avances(self): def test_resoudre(self): self.assert_resoudre('2x+3>5x-4 et 3x+1>=4x-4', r']-oo;7/3[') self.assert_resoudre('2=-a+b et -1=3a+b', r'{a: -3/4, b: 5/4}') self.assert_resoudre(r'3-x\ge 1+2x\\\text{et}\\4x<2+10x', ']-1/3;2/3]', r']- \frac{1}{3};\frac{2}{3}]', formatage_LaTeX = True) self.assert_resoudre('2exp(x)>3', ']ln(3/2);+oo[') self.assert_resoudre('x^3-30x^2+112=0', '{14 - 6 sqrt(7) ; 2 ; 14 + 6 sqrt(7)}', r'\{14 - 6 \sqrt{7}\,;\, 2\,;\, 14 + 6 \sqrt{7}\}') # NB: Test trs long (15-20 secondes) ! self.assert_resoudre(r'ln(x^2)-ln(x+1)>1', ']-1;e/2 - sqrt(4 e + exp(2))/2[U]e/2 + sqrt(4 e + exp(2))/2;+oo[') def test_approches(self): self.assert_approche('pi-1', '2.14159265358979324', '2.14159265358979324') self.assert_approche('factor(x^2+2.5x+1)', '(0.5 + x)(2 + x)') self.assert_approche('factor(exp(x)x^2+2.5x*exp(x)+exp(x))', '(0.5 + x)(2 + x)exp(x)') self.assert_approche('ln(2.5)', '0.916290731874155065') self.assert_approche('resoudre(x^3-30x^2+112=0)', '{-1.87450786638754354 ; 2 ; 29.8745078663875435}', r'\{-1.87450786638754354\,;\, 2\,;\, 29.8745078663875435\}') def test_session(self): i = Interprete(verbose = VERBOSE) i.evaluer("1+7") i.evaluer("x-3") i.evaluer("ans()+ans(1)") self.assertDernier(i, "5 + x") i.evaluer("f(x, y, z)=2x+3y-z") i.evaluer("f(-1, 5, a)") self.assertDernier(i, "13 - a") i.evaluer("f(x)=x^2-7x+3") i.evaluer("f'(x)") self.assertDernier(i, "-7 + 2*x") # Noms rservs self.assertRaises(NameError, i.evaluer, "e=3") self.assertRaises(NameError, i.evaluer, "pi=3") self.assertRaises(NameError, i.evaluer, "i=3") self.assertRaises(NameError, i.evaluer, "oo=3") self.assertRaises(NameError, i.evaluer, "factorise=3") i.evaluer('f(x)=x+3') i.evaluer('[f(j) for j in range(1,11)]') self.assertDernier(i, '[4, 5, 6, 7, 8, 9, 10, 11, 12, 13]') # Etc. if __name__ == '__main__': unittest.main() wxgeometrie-0.133.2.orig/wxgeometrie/mathlib/__init__.py0000644000175000017500000000256512014170666023457 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Mathlib 2 (sympy powered) # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #import sympy_functions #import custom_functions #import universal_functions #import custom_objects #import end_user_functions #import intervalles #import parsers #import graphes #from parsers import traduire_formule #from intervalles import Intervalle wxgeometrie-0.133.2.orig/wxgeometrie/initialisation.py0000644000175000017500000004606412014170666023322 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import sys, time, os, optparse, itertools, traceback, imp from os.path import dirname, realpath t0 = time.time() from . import param # Attention, les paramtres imports explicitement ici dans l'espace des noms # du module `initialisation` ne pourront pas tre modifi en ligne de commande : # en effet, pour modifier les paramtres via la ligne de commande, # on met jour l'espace des noms du module param. # En particulier, il ne faut *PAS* crire ``from .param import debug``, # car alors, ``$ wxgeometrie -b`` ne prendrait pas en compte le ``-b`` # lors de l'initialisation. from .param import dependances, NOMPROG, NOMPROG2, plateforme, GUIlib nomprog = NOMPROG2.lower() if param.py2exe: # cf. py2exe/boot_common.py # Par dfaut dans py2exe, sys.stdout redirige nul part, # et sys.stderr redirige vers un fichier .log via un mcanisme assez labor sys._py2exe_stderr = sys.stderr sys._py2exe_stdout = sys.stdout def msgbox(titre='Message', texte='', MB=sys._py2exe_stderr.write.func_defaults[0]): MB(0, texte.encode(param.encodage), titre.encode(param.encodage)) # Outil de dbogage avec py2exe def _test(condition = True): msgbox('** Test **', ('Success !' if condition else 'Failure.')) else: # Ne pas faire ces tests avec py2exe (non seulement inutiles, mais en plus ils chouent). # Make sure I have the right Python version. if sys.version_info[:2] < param.python_min: print(u" ** Erreur fatale **") print(NOMPROG + u" ncessite Python %d.%d au minimum.") print(u"Python %d.%d dtect." % (param.python_min + sys.version_info[:2])) sys.exit(-1) # Test for dependencies: for module in dependances: try: imp.find_module(module) except ImportError: print(u'** Erreur fatale ** : le module %s doit tre install !' %module) if plateforme == 'Linux': print("Sous Ubuntu/Debian, tapez 'sudo apt-get install %s' pour \ installer le module manquant." %dependances[module]) sys.exit(-1) def gerer_arguments(): u"""On rcupre les options ventuelles passes au programme. -a ou --all : essaie de dtecter tous les modules possibles pour les intgrer au dmarrage ex: python wxgeometrie.pyw --all monfichier1.geo monfichier2.geo -p ou --param ou --parametres : modifier le contenu du module 'param'. Les parametres sont spars par des points-virgules. ex: python wxgeometrie.pyw --param version='0.1';tolerance=0.01 monfichier1.geo monfichier2.geo -d ou --defaut : ne pas charger les prfrences""" parametres_additionnels = {} parser = optparse.OptionParser(prog = NOMPROG2, usage = "usage: %prog [options] [fichiers...]", version = "%prog " + param.version, description = NOMPROG2 + """ est un logiciel de mathematiques de niveau lycee. Il permet notamment de faire de la geometrie dynamique et du calcul formel.""") parser.add_option("-a", "--all", action = "store_true", help="detecter tous les modules presents et les integrer au demarrage") parser.add_option("-m", "--modules", help="specifier les modules a charger. ex: %s -m traceur,calculatrice" %nomprog) parser.add_option("-d", "--defaut", action = "store_true", help="utiliser les parametres par defaut, sans tenir compte des preferences") parser.add_option("-n", "--nouveau", action = "store_true", help="ouvrir une nouvelle session vierge") parser.add_option("-b", "--debug", action = "store_true", help="afficher les eventuels messages d'erreurs lors de l'execution") parser.add_option("--nodebug", action = "store_true", help="ne pas afficher les messages d'erreurs lors de l'execution") parser.add_option("-w", "--warning", action = "store_true", help="afficher les eventuels avertissements lors de l'execution") parser.add_option("--nowarning", action = "store_true", help="ne pas afficher les avertissements lors de l'execution") parser.add_option("-p", "--parametres", help="modifier les parametres. ex: %s -p 'tolerance=0.001;version=\"1.0\"'" %nomprog) parser.add_option("-r", "--recompile", action = "store_true", help="supprimer recursivement tous les fichiers .pyc, pour obliger python a recompiler tous les fichiers sources au demarrage.") parser.add_option("-s", "--script", action = "store_true", help="passer en mode script (ie. sans interface graphique). Ex: %s -s -i mon_script.txt -o mon_image.png" %nomprog) parser.add_option("-i", "--input", help="(mode script) fichier contenant le script de construction de figure, ou fichier .geo.") parser.add_option("-o", "--output", help="(mode script) fichier image. L'extension determine le type de fichier.") # parser.set_defaults() (options, args) = parser.parse_args() if options.defaut: param.charger_preferences = False if options.modules: # Les sparateurs accepts entre les noms de modules sont , et ; if ',' in options.modules: a_activer = options.modules.split(',') else: a_activer = options.modules.split(';') parametres_additionnels["modules_actifs"] = dict((module, (module in a_activer)) for module in param.modules) if options.all: parametres_additionnels["modules_actifs"] = dict.fromkeys(param.modules, True) if options.debug: parametres_additionnels["debug"] = True if options.nodebug: parametres_additionnels["debug"] = False if options.warning: parametres_additionnels["warning"] = True if options.nowarning: parametres_additionnels["warning"] = False if options.parametres: for parametre in options.parametres.split(";"): try: nom, valeur = parametre.split("=", 1) parametres_additionnels[nom] = eval(valeur) # pas sensass question scurit... :-( (?) except Exception: #raise print "Erreur: Parametre incorrect :", parametre print sys.exc_info()[0].__name__, ": ", sys.exc_info()[1] if options.recompile: for root, dirs, files in os.walk(os.getcwdu()): for file in files: if file.endswith(".pyc"): os.remove(os.path.join(root,file)) if (options.input or options.output) and not options.script: print("Warning: options --input et --output incorrectes en dehors du mode script.\n" "Exemple d'utilisation correcte : %s -s -i mon_script.txt -o mon_image.png." %nomprog) arguments = [] # Sous les Unix-like, les espaces dans les noms de fichiers sont mal grs par python semble-t-il. # Par exemple, "mon fichier.geo" est coup en "mon" et "fichier.geo" complet = True for arg in args: if complet: arguments.append(arg) else: arguments[-1] += " " + arg complet = arg.endswith(".geo") or arg.endswith(".geoz") for nom in parametres_additionnels: if not hasattr(param, nom): print(u"Attention: Paramtre inconnu : " + nom) return parametres_additionnels, arguments, options def universal_unicode(chaine): if not isinstance(chaine, basestring): chaine = str(chaine) if not isinstance(chaine, unicode): try: chaine = chaine.decode(param.encodage) except UnicodeError: try: chaine = chaine.decode('utf8') except UnicodeError: chaine = chaine.decode('iso-8859-1') return chaine uu = universal_unicode #def my_excepthook(exc_type, exc_obj, exc_tb): # u"""Affiche l'erreur sans interrompre le programme. # C'est un alias de sys.excepthook, mais qui est plus souple avec les encodages. # """ # tb = traceback.extract_tb(exc_tb) # print 'Traceback (most recent call last !)' # for fichier, ligne, fonction, code in tb: # print ' File "' + uu(fichier) +'", line ' + unicode(ligne) + ', in ' + uu(fonction) # if code is not None: # print ' ' + uu(code) # print uu(exc_type.__name__) + ": " + uu(exc_obj) #sys.excepthook = my_excepthook class SortieTemporaire(list): def write(self, chaine): self.append(uu(chaine).encode(param.encodage)) class SortiesMultiples(object): softspace = 0 def __init__(self, obligatoires = (), facultatives = ()): self.obligatoires = list(obligatoires) self.facultatives = list(facultatives) self.total = 0 def write(self, chaine): uni = uu(chaine) chaine = uni.encode(param.encodage) # default_out = (sys.__stdout__ if not param.py2exe else sys.py2exe_stderr) # Sous Windows, l'encodage se fait en cp1252, sauf dans console o cp850 est utilis ! # default_out.write(chaine if plateforme != 'Windows' else uni.encode('cp850')) # Sous Windows, l'encodage se fait en cp1252, sauf dans console o cp850 est utilis ! if not param.py2exe: sys.__stdout__.write(chaine if plateforme != 'Windows' else uni.encode('cp850')) self.total += len(chaine) if self.total - len(chaine) < param.taille_max_log <= self.total: chaine = u"Sortie sature !".encode(param.encodage) for sortie in self.obligatoires: sortie.write(chaine) if param.debug: for sortie in self.facultatives: sortie.write(chaine) def flush(self): for sortie in itertools.chain(self.obligatoires, self.facultatives): if hasattr(sortie, 'flush'): sortie.flush() def __del__(self): self.close() def close(self): for sortie in self.obligatoires: if hasattr(sortie, 'close'): sortie.close() for sortie in self.facultatives: if hasattr(sortie, 'close'): sortie.close() # Emplacement du module python nomm wxgeometrie param.EMPLACEMENT = dirname(realpath(sys._getframe().f_code.co_filename)) def path2(chemin): u"""Transforme le chemin en remplaant les / et \\ selon le sparateur utilis par le systme. % est remplac par l'emplacement du programme (contenu dans param.EMPLACEMENT). Exemple : path2("%/images/archives/old.png"). ~ fait rfrence au rpertoire personnel de l'utilisateur (ex: /home/SteveB/ sous Linux. """ return os.path.normpath(os.path.expanduser(uu(chemin).replace("%", uu(param.EMPLACEMENT)))) # S'assurer que les dossiers log/, session/, etc. existent: for emplacement in param.emplacements.values(): emplacement = path2(emplacement) try: if not os.path.isdir(emplacement): os.makedirs(emplacement) print(u'Cration du rpertoire : ' + emplacement) except IOError: print(u"Impossible de crer le rpertoire %s !" %emplacement) # PARTIE CRITIQUE (redirection des messages d'erreur) # Attention avant de modifier, c'est trs difficile dboguer ensuite (et pour cause !) # Rduire la taille de cette partie au minimum possible. try: sorties = sys.stdout = sys.stderr = SortiesMultiples() # Tester sys.stdout/stderr (les plantages de sys.stderr sont trs pnibles tracer !) sorties.write('') except: if param.py2exe: sys.stderr = sys._py2exe_stderr sys.stdout = sys._py2exe_stdout else: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ raise log_filename = path2(param.emplacements['log'] + u"/messages.log") if param.enregistrer_messages and isinstance(sys.stdout, SortiesMultiples): try: sys.stdout.facultatives.append(SortieTemporaire()) fichier_log = open(log_filename, 'w') fichier_log.write(NOMPROG.encode(param.encodage) + " version " + param.version + '\n') fichier_log.write(time.strftime("%d/%m/%Y - %H:%M:%S") + '\n') sys.stdout.obligatoires.append(fichier_log) except IOError: fichier_log = None param.enregistrer_messages = param.historique_log = False if param.py2exe: sys.stderr = sys._py2exe_stderr sys.stdout = sys._py2exe_stdout else: print(traceback.format_exc(sys.exc_info())) print('Warning: This exception was not raised.') else: fichier_log = None # FIN DE PARTIE CRITIQUE # On enclt tout dans un try/except. # En effet, le sys.stderr personnalis se comporte mal en cas d'erreur non intercepte # (il semble qu'une partie de l'espace des noms ne soit dj plus accessible au moment o l'erreur # est traite...??) try: # faire avant d'importer API et LIB parametres_additionnels, arguments, options = gerer_arguments() if param.verbose: print u'Arguments de la ligne de commande :', parametres_additionnels, arguments if options.script: print u"--- Mode script activ. ---" if param.py2exe: print sys.path sys.path.extend(('library.zip\\matplotlib', 'library.zip\\' + GUIlib)) ## #Test des imports ## from . import GUI ## import wx # aprs GUI (wxversion.select() must be called before wxPython is imported) if param.charger_psyco is not False: try: import psyco if param.charger_psyco is True: psyco.full() else: psyco.profile() except ImportError: pass def initialiser(): from .API.parametres import actualiser_module from .pylib import print_error from .geolib import contexte # Rcupration d'un crash ventuel path_lock = path2(param.emplacements['session'] + "/lock") crash = os.path.isfile(path_lock) try: open(path_lock, 'w').close() param.ecriture_possible = True except IOError: print(u"Warning: impossible de crer le fichier '%s'." %path_lock) param.ecriture_possible = False # Mise jour des paramtres en fonction des prfrences de l'utilisateur # (NB: faire avant d'importer modules.py, qui lui-mme utilise param.modules_actifs) path = path2(param.emplacements['preferences'] + "/parametres.xml") try: if os.path.exists(path): if param.charger_preferences: if param.verbose: print(u"Chargement des prfrences...") # On charge les prfrences de l'utilisateur depuis parametres.xml. a_verifier = dict((dicname, getattr(param, dicname)) for dicname in param.a_mettre_a_jour) actualiser_module(param, path) # Certains paramtres peuvent avoir besoin d'une mise jour # (en cas de changement de version du programme par exemple). # Cela concerne en particulier les dictionnaires, qui peuvent gagner de nouvelles cls. for dicname in param.a_mettre_a_jour: for key, val in a_verifier[dicname].iteritems(): getattr(param, dicname).setdefault(key, val) # Mise jour du contexte de geolib: for parametre in ('decimales', 'unite_angle', 'tolerance'): contexte[parametre] = getattr(param, parametre) else: actualiser_module(param, None) except: sys.excepthook(*sys.exc_info()) param.__dict__.update(parametres_additionnels) if options.script: from .GUI.mode_script import mode_script msg = mode_script(options.input, options.output) if msg: print msg else: from .GUI.app import app app.nom(NOMPROG) from .GUI.fenetre_principale import FenetrePrincipale if param.debug: print("Temps d'initialisation: %f s" % (time.time() - t0)) frame = FenetrePrincipale(app, fichier_log = fichier_log) frame.SetTitle(u"WxGomtrie - Chargement en cours, patientez...") #XXX: impossible de modifier le curseur (BusyCursor ou SetCursor # n'ont aucun effet)... frame.Show() if param.debug: print('Temps avant affichage de la fentre principale: %f s' % (time.time() - t0)) frame.onglets.terminer_initialisation() if isinstance(sys.stdout, SortiesMultiples): if param.debug: for msg in sys.stdout.facultatives[0]: frame.fenetre_sortie.write(msg) sys.stdout.facultatives[0] = frame.fenetre_sortie if arguments: try: for arg in arguments: frame.onglets.ouvrir(arg) # ouvre le fichier pass en paramtre except: print_error() # affiche l'erreur intercepte, titre informatif print(arg) elif (param.sauver_session or crash) and not options.nouveau: try: if crash: print(NOMPROG + u" n'a pas t ferm correctement.\n" "Tentative de restauration de la session en cours...") frame.gestion.charger_session() except: print(u"Warning: La session n'a pas pu tre restaure.") print_error() if param.debug: print('Temps de chargement complet: %f s' % (time.time() - t0)) app.boucle() sorties.close() os.remove(path_lock) except Exception: # do *NOT* catch SystemExit ! ("wxgeometrie -h" use it) if param.py2exe: details = u"Dtails de l'erreur :\n" # 25 lignes maxi dans la fenetre l = uu(traceback.format_exc(sys.exc_info())).split('\n')[-25:] details += '\n'.join(l) + '\n\n' if param.enregistrer_messages: details += u"Pour plus de dtails, voir \n'%s'." %log_filename else: details += u"Par ailleurs, impossible de gnrer le fichier\n'%s'." %log_filename msgbox(u"Erreur fatale lors de l'initialisation.", details) sys.excepthook(*sys.exc_info()) sorties.close() sys.exit("Erreur fatale lors de l'initialisation.") wxgeometrie-0.133.2.orig/wxgeometrie/API/0000755000175000017500000000000012014170666020327 5ustar georgeskgeorgeskwxgeometrie-0.133.2.orig/wxgeometrie/API/canvas.py0000644000175000017500000006572412014170666022172 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import numpy from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.figure import Figure from .moteur_graphique import Moteur_graphique from ..pylib import decorator, property2, print_error, WeakList, str2, no_argument from ..geolib import Feuille from .. import param class GelAffichage(object): def __init__(self, canvas, geler=True, actualiser=False, seulement_en_apparence=False, sablier=False): self.sablier = sablier self.canvas = canvas self.geler = geler self.actualiser = actualiser self.attribut = ('_affichage_gele_en_apparence' if seulement_en_apparence else '_affichage_gele') def __enter__(self): self._ancienne_valeur = getattr(self.canvas, self.attribut) if self.geler is not None: setattr(self.canvas, self.attribut, self.geler) if self.sablier: self.canvas._curseur(sablier=True) def __exit__(self, type, value, traceback): setattr(self.canvas, self.attribut, self._ancienne_valeur) if self.actualiser: self.canvas.rafraichir_affichage() if self.sablier: self.canvas._curseur(sablier=False) # Garde une trace dans les logs de chaque appel de la mthode pour dbogage. @decorator def track(meth, self, *args, **kw): if param.debug: s = "%s - Args: %s, %s" % (meth.func_name, args, kw) self.parent.action_effectuee(s) return meth(self, *args, **kw) # Garde une trace dans les logs de chaque appel *isol* de la mthode pour dbogage. # Par exemple, en cas de zoom avec la roulette de souris, seul le dernier zoom # sera enregistr, pour ne pas saturer le fichier .log @decorator def partial_track(meth, self, *args, **kw): if param.debug: s = "%s - Args: %s, %s" % (meth.func_name, args, kw) self.parent.action_effectuee(s, signature = meth.func_name) return meth(self, *args, **kw) class Canvas(FigureCanvasAgg): u'Partie du canvas indpendante de la librairie graphique (Wx actuellement).' def __init__(self, couleur_fond = 'w', dimensions = None, feuille = None): self.figure = Figure(dpi = param.dpi_ecran, frameon=True, facecolor = couleur_fond) FigureCanvasAgg.__init__(self, self.figure) self.axes = self.figure.add_axes([0, 0, 1, 1], frameon=False) self._dimensions = dimensions self.__feuille_actuelle = feuille self.axes.set_xticks([]) self.axes.set_yticks([]) # Ces paramtres ne sont utiles que pour les sur-classes s'intgrant dans un GUI. self.editeur = None self.select = None # objet couramment slectionn #if self.param("transformation_affine") is not None: # self.transformation = matplotlib.transforms.Affine(*self.param("transformation_affine")) # self.figure.set_transform(self.transformation) # self.axes.set_transform(self.transformation) #else: # self.transformation = None if self.param("transformation") is not None: a, b, c, d = self.param("transformation") # CODE RCRIRE et ADAPTER self.transformation = numpy.matrix([[a, b], [c, d]]) else: self.transformation = None self._affichage_gele = False # Ne pas utiliser directement. # Si on met la valeur a True, self.rafraichir_affichage() ne fait plus rien. # Cela permet d'effectuer un certain nombre d'actions rapidement, # et de n'actualiser qu' la fin. # En particulier, cela sert pour charger une figure depuis un fichier .geo self._affichage_gele_en_apparence = False # Ne pas utiliser directement. # Si on met la valeur a True, self.rafraichir_affichage() fonctionne toujours, # mais les changements ne s'affichent pas l'cran. # Cela permet d'effectuer un certain nombre d'actions sans que l'utilisateur s'en apercoive # (pas de clignotement de l'affichage). # Par contre, le gain de temps est ngligeable par rapport un vrai gel de l'affichage. # En particulier, cela sert pour exporter une figure. self.graph = Moteur_graphique(self) self.parametres = [u"taille", u"gradu", u"afficher_axes", u"afficher_quadrillage", u"afficher_fleches", u"repere", u"resolution", u"origine_axes", u"utiliser_repere", u"quadrillages", u"couleur_papier_millimetre", u"liste_axes", u"orthonorme", u"grille_aimantee", u"zoom_texte", "zoom_ligne"] self.liste_objets_en_gras = WeakList() self.initialiser() @property2 def feuille_actuelle(self, val = None): if val is None: return self.__feuille_actuelle self.__feuille_actuelle = val def initialiser(self): # actions a effectuer avant de rxecuter l'historique for parametre in self.parametres: setattr(self, parametre, self.param(parametre)) def exporter(self, nom, dpi = None, zone = None, echelle = None): u"""Export de la feuille sous forme d'un fichier (png, eps, ...). dpi : rsolution souhaite (en dot par inch) zone = (xmin, xmax, ymin, ymax) : pour n'exporter qu'une partie de la feuille (png seulement). echelle = (x, y) : nombre de cm pour une unit en abscisse ; en ordonne""" nom = str2(nom) # la mthode savefig ne gre pas l'unicode dpi = dpi or param.dpi_export if self.editeur is not None: # Evite d'exporter la feuille avec un nom d'objet en cours d'dition self.editeur.close() # De mme, aucun objet ne doit tre en gras self.feuille_actuelle.objets_en_gras() # Les objets invisibles ne doivent pas apparaitre afficher_objets_caches = self.afficher_objets_caches self.afficher_objets_caches = False self.graph.exporter(nom = nom, dpi = dpi, zone = zone, echelle = echelle) self.afficher_objets_caches = afficher_objets_caches self.selection_en_gras() def selection_en_gras(self): self.feuille_actuelle.objets_en_gras(self.select, *self.liste_objets_en_gras) # Alias ###################### def dessiner_ligne(self, *args, **kw): return self.graph.ajouter_ligne(*args, **kw) def dessiner_polygone(self, *args, **kw): return self.graph.ajouter_polygone(*args, **kw) def dessiner_texte(self, *args, **kw): return self.graph.ajouter_texte(*args, **kw) def dessiner_arc(self, *args, **kw): return self.graph.ajouter_arc(*args, **kw) def dessiner_point(self, *args, **kw): return self.graph.ajouter_point(*args, **kw) def ligne(self, *args, **kw): return self.graph.ligne(*args, **kw) def polygone(self, *args, **kw): return self.graph.polygone(*args, **kw) def texte(self, *args, **kw): return self.graph.texte(*args, **kw) def arc(self, *args, **kw): return self.graph.arc(*args, **kw) def point(self, *args, **kw): return self.graph.point(*args, **kw) def fleche(self, *args, **kw): return self.graph.fleche(*args, **kw) def fleche_courbe(self, **kw): return self.graph.fleche_courbe(**kw) def codage(self, **kw): return self.graph.codage(**kw) def angle(self, **kw): return self.graph.angle(**kw) def codage_angle(self, **kw): return self.graph.codage_angle(**kw) def rectangle(self, **kw): return self.graph.rectangle(**kw) def dessiner(self, objet): self.graph.ajouter(objet) ################################## # Les fonctions suivantes assurent la conversion pixel <-> coordonnees ################################## # en multipliant m par coeff(0), on convertit un ecart en abcisses de m pixels en coordonnees. # en multipliant n par coeff(1), on convertit un ecart en ordonnees de n pixels en coordonnees. # Les fonctions qui suivent permettent la conversion d'un couple de coordonnees en pixels, et reciproquement. # Si le mode est fixe a 1, le pixel (0,0) sera le coin inferieur gauche (plus intuitif). # Si le mode est fixe a -1, le pixel (0,0) sera le coin superieur gauche (convention, respectee par WxPython). @property def dimensions(self): return self._dimensions def pas(self): #print 'pas:', self.fenetre, self.resolution return (self.fenetre[1] - self.fenetre[0])/(self.resolution) ## ## def coeff(self, i): # DESUET # warning("Desuet. Utiliser dpix2coo (*coeff) et dcoo2pix (/coeff)") return (self.fenetre[1+2*i] - self.fenetre[2*i])/self.dimensions[i] # Rq: une ZeroDivisionError se produit juste aprs avoir beaucoup rduit une fentre, # wxpython renvoit parfois (0,0) pour la taille. # le plus simple serait de laisser l'erreur, mais a innonde le dbugueur de messages... :-/ def coeffs(self): # DESUET # warning("Desuet. Utiliser dpix2coo et dcoo2pix") return self.coeff(0), self.coeff(1) ## ## def coo2pix(self, x, y): u"""Convertit des coordonnes en pixel.""" if isinstance(x, (list, tuple)): x = numpy.array(x) if isinstance(y, (list, tuple)): y = numpy.array(y) l, h = self.dimensions px = l*(x - self.fenetre[0])/(self.fenetre[1] - self.fenetre[0]) py = h*(self.fenetre[3] - y)/(self.fenetre[3] - self.fenetre[2]) return px, py def pix2coo(self, px, py): u"""Convertit un pixel en coordonnes.""" if isinstance(px, (list, tuple)): px = numpy.array(px) if isinstance(py, (list, tuple)): py = numpy.array(py) l, h = self.dimensions x = px*(self.fenetre[1] - self.fenetre[0])/l + self.fenetre[0] y = py*(self.fenetre[2] - self.fenetre[3])/h + self.fenetre[3] # print x, y, -x, -y return x, y def dcoo2pix(self, dx, dy): u"""Convertit un dplacement exprim en coordonnes en un dplacement en pixels.""" l, h = self.dimensions dpx = l*dx/(self.fenetre[1] - self.fenetre[0]) dpy = h*dy/(self.fenetre[2] - self.fenetre[3]) return dpx, dpy def dpix2coo(self, dpx, dpy): u"""Convertit un dplacement exprim en pixels en un dplacement exprim en coordonnes.""" l, h = self.dimensions dx = dpx*(self.fenetre[1] - self.fenetre[0])/l dy = dpy*(self.fenetre[3] - self.fenetre[2])/h return dx, dy def _affiche_module(self): u"Affichage spcifique au module en cours. ( surclasser.)" pass def geler_affichage(self, geler=True, actualiser=False, seulement_en_apparence=False, sablier=False): u""" utiliser au sein d'un contexte 'with': with self.geler_affichage(): ... Si actualiser = True, l'affichage est rafraichi au dgel. Si seulement_en_apparence = True, l'affichage n'est pas gel en interne, mais les modifications ne s'affichent pas l'cran (le gain de vitesse est alors ngligeable, mais esthtiquement a vite que des modifications successives apparaissent l'cran). Si sablier = True, le pointeur de la souris est remplac temporairement par un sablier. """ return GelAffichage(self, geler=geler, actualiser=actualiser, sablier=sablier) def _curseur(self, sablier): u"""Changer le curseur en sablier. surclasser.""" raise NotImplementedError @property def affichage_gele(self): return self._affichage_gele @property def affichage_gele_en_apparence(self): return self._affichage_gele_en_apparence def saturation(self, i): return self.zoom_ligne*self.coeff(i)/self.gradu[i] # Reglage des parametres d'affichage ########################################## # Gestion des variables d'environnement lies l'affichage. # A standardiser. ## _liste_parametres_repere = ("quadrillages", "affiche_quadrillage", "affiche_axes", ## "affiche_fleches", "repere", "gradu", "utiliser_repere", ## "liste_axes", "orthonorme", "fenetre", ## ) # Parametres boolens grs par une entre du menu for _nom_, _doc_ in ( ('afficher_axes', u"Afficher ou non les axes."), ('afficher_quadrillage', u"Afficher ou non le(s) quadrillage(s)."), ('orthonorme', u"Afficher la figure dans un repre toujours orthonorm."), ('afficher_objets_caches', u"Indique si les objets cachs sont affichs ou non."), ('grille_aimantee', u"Indique si les points doivent se placer sur le quadrillage."), ): exec('''@track def gerer_parametre_%(_nom_)s(self, afficher = None): """%(_doc_)s""" if afficher is not None: if isinstance(afficher, bool): self.%(_nom_)s = afficher else: self.%(_nom_)s = not self.%(_nom_)s self.rafraichir_affichage() assert isinstance(self.%(_nom_)s, bool), '%(_nom_)s: ' + repr(self.%(_nom_)s) return self.%(_nom_)s''' %locals(), globals(), locals()) # Paramtres grs directement par la feuille for _nom_ in Feuille._parametres_repere: exec('''assert "%(_nom_)s" not in locals(), "Erreur: %(_nom_)s est deja defini !" @property2 def %(_nom_)s(self, valeur = no_argument): if valeur is no_argument: return self.feuille_actuelle.%(_nom_)s self.feuille_actuelle.%(_nom_)s = valeur''' %locals(), globals(), locals()) del _nom_, _doc_ # Gestion du zoom, etc... ######################################## def _get_fenetre(self): if self.orthonorme or getattr(self, 'ratio', None) is not None: if self.orthonorme: rat = 1 else: rat = self.ratio # x:y -> x/y # ratio est le rapport "unit en abscisse/unit en ordonne" w, h = self.dimensions fenetre = self.feuille_actuelle.fenetre coeff0 = rat*(fenetre[1] - fenetre[0])/w coeff1 = (fenetre[3] - fenetre[2])/h xmin, xmax, ymin, ymax = fenetre xcoeff = (coeff1/coeff0 if coeff0 < coeff1 else 1) ycoeff = (1 if coeff0 < coeff1 else coeff0/coeff1) x, y, rx, ry = (xmin+xmax)/2., (ymin+ymax)/2., (xmax-xmin)/2., (ymax-ymin)/2. return x - xcoeff*rx, x + xcoeff*rx, y - ycoeff*ry, y + ycoeff*ry return self.feuille_actuelle.fenetre def _set_fenetre(self, xmin_xmax_ymin_ymax): self.feuille_actuelle.fenetre = xmin_xmax_ymin_ymax fenetre = property(_get_fenetre, _set_fenetre) @property def dimensions_fenetre(self): xmin, xmax, ymin, ymax = self.fenetre return xmax - xmin, ymax - ymin def synchroniser_fenetre(self): u"""Dtecte la fentre d'affichage et l'enregistre. Ce peut tre utile si l'on utilise une commande de haut niveau de matplolib, qui calcule automatiquement la meilleure fentre d'affichage.""" xmin, xmax = self.axes.viewLim.intervalx ymin, ymax = self.axes.viewLim.intervaly self.fenetre = xmin, xmax, ymin, ymax def zoomer(self, coeff): xmin, xmax, ymin, ymax = self.fenetre x, y, rx, ry = (xmin+xmax)/2., (ymin+ymax)/2., (xmax-xmin)/2., (ymax-ymin)/2. self.fenetre = x - rx/coeff, x + rx/coeff, y - ry/coeff, y + ry/coeff def zoom_in(self, event = None): self.zoomer(param.zoom_in) def zoom_out(self, event = None): self.zoomer(param.zoom_out) @track def zoom_auto(self, event = None): fenetre_initiale = a = self.fenetre compteur = 0 condition = True while condition: self.graph._regler_fenetre() compteur += 1 self._zoom_auto() # cause du texte dont la taille est indpendante de l'chelle, les calculs sont fausss. # Mais en ritrant le procd, l'affichage converge rapidement (A OPTIMISER ?). b = a a = self.fenetre erreur_x = abs(a[0] - b[0]) + abs(a[1] - b[1]) erreur_y = abs(a[2] - b[2]) + abs(a[3] - b[3]) # l'erreur doit tre infrieure 1 pixel: condition = erreur_x/self.coeff(0) > 1 or erreur_y/self.coeff(1) > 1 if compteur > 25: self.message(u"chec du zoom automatique.") self.fenetre = fenetre_initiale break def _zoom_auto(self): actuelle = self.feuille_actuelle xxyy = zip(*([obj.espace_vital for obj in actuelle.liste_objets(False) if obj.espace_vital is not None] + [obj.etiquette.espace_vital for obj in actuelle.liste_objets(False) if obj.etiquette is not None and obj.etiquette.espace_vital is not None])) print 'xxyy', xxyy if xxyy: # 'None' indique que l'objet ne fournit pas d'indication de dimension # pour les abscisses ou pour les ordonnes. _ajuster_xmin = _ajuster_xmax = True xmins = [x for x in xxyy[0] if x is not None] if xmins: xmin = min(xmins) else: xmin = self.fenetre[0] _ajuster_xmin = False xmaxs = [x for x in xxyy[1] if x is not None] if xmaxs: xmax = max(xmaxs) else: xmax = self.fenetre[1] _ajuster_xmax = False ymin = min(y for y in xxyy[2] if y is not None) ymax = max(y for y in xxyy[3] if y is not None) dx = xmax - xmin dy = ymax - ymin # des valeurs trop proches pour xmin et xmax risquent de faire planter l'affichage. if dx < 100*param.tolerance: r = (self.fenetre[1] - self.fenetre[0])/2 xmin -= r xmax += r else: if _ajuster_xmin: xmin -= .05*dx if _ajuster_xmax: xmax += .05*dx if dy < 100*param.tolerance: # idem pour ymin et ymax r = abs(self.fenetre[3] - self.fenetre[2])/2 ymin -= r ymax += r else: ymin -= .05*dy ymax += .05*dy self.fenetre = xmin, xmax, ymin, ymax if param.debug: print "ZOOM AUTO :", xmin, xmax, ymin, ymax, xxyy @track def orthonormer(self, event = None, mode = 1): u""" mode 0 : on orthonormalise le repre en restreignant la vue. mode 1 : on orthonormalise le repre en largissant la vue.""" if mode: xcoeff = self.coeff(1)/self.coeff(0) if self.coeff(0) < self.coeff(1) else 1 ycoeff = 1 if self.coeff(0) < self.coeff(1) else self.coeff(0)/self.coeff(1) else: xcoeff = self.coeff(1)/self.coeff(0) if self.coeff(0) > self.coeff(1) else 1 ycoeff = 1 if self.coeff(0) > self.coeff(1) else self.coeff(0)/self.coeff(1) xmin, xmax, ymin, ymax = self.fenetre x, y, rx, ry = (xmin+xmax)/2., (ymin+ymax)/2., (xmax-xmin)/2., (ymax-ymin)/2. self.fenetre = x - xcoeff*rx, x + xcoeff*rx, y - ycoeff*ry, y + ycoeff*ry # < Zooms concernant uniquement la taille des objets > def zoom_text(self, event = None, valeur = 100): self.zoom_texte = valeur/100 ## self.rafraichir_affichage() def zoom_line(self, event = None, valeur = 100): self.zoom_ligne = valeur/100 ## self.rafraichir_affichage() def zoom_normal(self, event = None): self.zoom_texte = 1 self.zoom_ligne = 1 ## self.rafraichir_affichage() def zoom_large(self, event = None): self.zoom_texte = 1.2 self.zoom_ligne = 1.4 ## self.rafraichir_affichage() def zoom_videoprojecteur(self, event = None): self.zoom_texte = 1.6 self.zoom_ligne = 2 ## self.rafraichir_affichage() def zoom_videoprojecteur_large(self, event = None): self.zoom_texte = 2.2 self.zoom_ligne = 3 ## self.rafraichir_affichage() # @track def repere_Oij(self, event = None): self.repere = ('O', 'i', 'j') self.gerer_parametre_afficher_axes(True) @track def repere_OIJ(self, event = None): self.repere = ('O', 'I', 'J') self.gerer_parametre_afficher_axes(True) @track def repere_011(self, event = None): ux, uy = self.gradu self.repere = ('0', str(ux), str(uy)) self.gerer_parametre_afficher_axes(True) @track def quadrillage_millimetre(self, event = None): self.quadrillages = ( ((1, 1), ':', 1, 'k'), ((0.5, 0.5), '-', 0.25, 'darkgray'), ((0.1, 0.1), '-', 0.1, 'gray'), ) self.gerer_parametre_afficher_quadrillage(True) @track def quadrillage_millimetre_colore(self, event = None, couleur = None): if couleur is None: couleur = self.couleur_papier_millimetre self.quadrillages = ( ((1, 1), ':', 1, couleur), ((0.5, 0.5), '-', 0.25, couleur), ((0.1, 0.1), '-', 0.1, couleur), ) self.gerer_parametre_afficher_quadrillage(True) @track def quadrillage_demigraduation(self, event = None): ux, uy = self.gradu self.quadrillages = ( ((ux, uy), ':', 1, 'k'), ((ux/2, uy/2), '-', 0.25, 'darkgray'), ) self.gerer_parametre_afficher_quadrillage(True) @track def quadrillage_demigraduation_colore(self, event = None, couleur = None): ux, uy = self.gradu if couleur is None: couleur = self.couleur_papier_millimetre self.quadrillages = ( ((ux, uy), ':', 1, couleur), ((ux/2, uy/2), '-', 0.25, couleur), ) self.gerer_parametre_afficher_quadrillage(True) @track def quadrillage_defaut(self, event = None): self.quadrillages = self.param("quadrillages", defaut = True) self.gerer_parametre_afficher_quadrillage(True) # Slection d'une zone ###################### def gestion_zoombox(self, pixel): x, y = pixel xmax, ymax = self.dimensions x = max(min(x, xmax), 0) y = max(min(y, ymax), 0) self.fin_zoom = self.pix2coo(x, y) self.debut_zoom = self.debut_zoom or self.fin_zoom (x0, y0), (x1, y1) = self.debut_zoom, self.fin_zoom if self.orthonorme or getattr(self, 'ratio', None) is not None: rymax = (ymax if self.orthonorme else ymax*self.ratio) if rymax*abs(x0 - x1) > xmax*abs(y0 - y1): y1 = y0 + rymax/xmax*abs(x0 - x1)*cmp(y1, y0) else: x1 = x0 + xmax/rymax*abs(y0 - y1)*cmp(x1, x0) self.fin_zoom = (x1, y1) #if param.bouger_curseur: # ou comment rendre fou l'utilisateur... ;) # self.WarpPointer(*self.XYcoo2pix((x1, y1), -1)) self.dessiner_polygone([x0,x0,x1,x1], [y0,y1,y1,y0], facecolor='c', edgecolor='c', alpha = .1) self.dessiner_ligne([x0,x0,x1,x1,x0], [y0,y1,y1,y0,y0], 'c', alpha = 1) self.rafraichir_affichage(dessin_temporaire = True) # pour ne pas tout rafraichir def selection_zone(self, pixel): x, y = pixel xmax, ymax = self.dimensions x = max(min(x, xmax), 0) y = max(min(y, ymax), 0) self.fin_select = self.pix2coo(x, y) self.debut_select = self.debut_select or self.fin_select (x0, y0), (x1, y1) = self.debut_select, self.fin_select self.dessiner_polygone([x0,x0,x1,x1], [y0,y1,y1,y0], facecolor='y', edgecolor='y',alpha = .1) self.dessiner_ligne([x0,x0,x1,x1,x0], [y0,y1,y1,y0,y0], 'g', linestyle = ":", alpha = 1) self.rafraichir_affichage(dessin_temporaire = True) # pour ne pas tout rafraichir # Evenements concernant directement la feuille ################################ def coder(self, event): self.executer(u"coder()") def decoder(self, event): self.executer(u"effacer_codage()") def nettoyer_feuille(self, event): self.executer(u"nettoyer()") def effacer_traces(self, event): self.feuille_actuelle.effacer_traces() def executer(self, commande, parser = False): u"""Excute une commande dans la feuille. NB: le parser n'est *PAS* activ par dfaut, par souci de rapidit.""" self.feuille_actuelle.executer(commande, parser = parser) # Gestion de l'affichage ################################# def rafraichir_affichage(self, dessin_temporaire = False, rafraichir_axes = None): if rafraichir_axes is not None: self.feuille_actuelle._repere_modifie = rafraichir_axes self.feuille_actuelle.affichage_perime() self._dessin_temporaire = dessin_temporaire def _actualiser_si_necessaire(self, event = None, _n=[0]): # _n[0] += 1 if self.feuille_actuelle._affichage_a_actualiser: # print _n[0], u"Affichage actualis." self._actualiser() def _actualiser(self, _n = [0]): # Le code suivant est activer uniquement pour le dbogage de l'affichage: # if param.debug: s = "Actualisation" if self.feuille_actuelle._repere_modifie: s += " complete" print s + str(_n) + ": " + self.parent.__titre__ _n[0] += 1 # try: self.graph.dessiner(dessin_temporaire = self._dessin_temporaire, rafraichir_axes = self.feuille_actuelle._repere_modifie) except Exception: # Ne pas bloquer le logiciel par des rafraichissements successifs en cas de problme. print_error() self.feuille_actuelle._repere_modifie = False self.feuille_actuelle._affichage_a_actualiser = False self._dessin_temporaire = False def param(self, key, **kw): return getattr(param, key) wxgeometrie-0.133.2.orig/wxgeometrie/API/macros_construction.py0000644000175000017500000000531012014170666024776 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Macros de construction # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import re from .sauvegarde import FichierGEO class Macro_construction(object): u"""Lecteur de macros. Lit et interprte le fichier de macro de construction. """ def __init__(self, nom = None): self.nom = nom self.fichier = FichierGEO(type = "Macro de construction WxGeometrie", nom = nom or "") self.figure = "" pass def ouvrir(self, path): self.fichier.ouvrir(path) if self.fichier.has_key("Figure"): self.figure = self.fichier.contenu["Figure"][0] # code python correspondant la figure else: self.figure = "" if self.fichier.has_key("Parametres_macro"): self.parametres = self.fichier.contenu["Parametres_macro"][0] # self.nom = self.parametres["nom"][0].strip() self.arguments = self.parametres["arguments"][0].strip().split(",") # arguments de la macro (ex: 3 points pour un triangle) # self.decoration = self.parametres["decoration"][0].strip().split(",") # lments ne devant pas tre construits (texte de commentaire par exemple). else: # par dfaut, les arguments sont tous les points libres self.arguments = [] for ligne in self.figure.split("\n"): re_match = re.match("[A-Za-z_][A-Za-z0-9_]*[ ]*=[ ]*Point(", ligne) if re_match: self.arguments.append(re_match.group().split("=")[0].strip()) # on rajoute le nom du point # self.decoration = () def enregistrer(self, path): pass # self.ouvrir(fichier) # def ouvrir(self, path): # pass wxgeometrie-0.133.2.orig/wxgeometrie/API/parametres.py0000644000175000017500000000615612014170666023054 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ########################################################################## # # Sauvegarde et chargement des paramtres # ########################################################################## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import time from types import NoneType from . import sauvegarde from ..pylib import print_error, eval_safe from .. import param types_supportes = (int, long, str, unicode, float, bool, NoneType, list, tuple, dict) # TO DO (?) : # - rajouter le support des types array et complex dans securite.eval_safe # - grer correctement l'encodage dans save.py # (tout convertir en unicode, puis en utf-8) def sauvegarder_module(module, nom = "main"): u"""Renvoie le contenu d'un module sous forme d'un fichier XML. Au lieu du module lui-mme, 'module' peut tre un dictionnaire correspondant au dictionnaire du module (ventuellement modifi). """ dico = module.__dict__.copy() if not isinstance(module, dict) else module for key in param.valeurs_a_ne_pas_sauver: dico.pop(key, None) f = sauvegarde.FichierGEO(type = 'Options WxGeometrie', module = nom) m = f.ajouter("Meta") f.ajouter("date", m, time.strftime("%d/%m/%Y - %H:%M:%S",time.localtime())) p = f.ajouter("Parametres") for key, value in dico.items(): if not key.startswith("_") and isinstance(value, types_supportes): f.ajouter(key, p, repr(value)) return f def actualiser_module(module, fichier): u"Rafraichit le contenu d'un module partir d'un fichier XML." if fichier: fgeo, msg = sauvegarde.ouvrir_fichierGEO(fichier) else: fgeo = None copie = module.__dict__.copy() copie.pop("__builtins__", None) setattr(module, "_parametres_par_defaut", copie) if fgeo is not None: parametres = fgeo.contenu["Parametres"][-1] try: for key in parametres: setattr(module, key, eval_safe(parametres[key][-1])) except: print module, key print_error() #def extraire_parametre(fichier, parametre): # fgeo, msg = sauvegarde.ouvrir_fichierGEO(fichier) # parametres = fgeo.contenu["Parametres"][-1] # return parametres[parametre][-1] wxgeometrie-0.133.2.orig/wxgeometrie/API/sauvegarde.py0000644000175000017500000002620112014170666023030 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Sauvegarde # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from xml.dom.minidom import parseString from xml.parsers.expat import ExpatError import tarfile, os, zipfile, re from cStringIO import StringIO from .filtres import filtre_versions_anterieures from ..pylib import print_error, eval_safe, removeend from .. import param #---------------------------------- # DOM pour lire un fichier #---------------------------------- class FichierGEO(object): u"""Classe utilise pour manipuler un fichier .geo. On peut aussi utiliser le DOM XML, mais les spcifications du format de fichier .geo sont plus restreintes, et cette classe offre une surcouche au DOM XML pour un accs plus simple.""" defaut = {"type": "Fichier WxGeometrie", "version": param.version, "module": '', "nom": '', "repertoire": ''} def __init__(self, texte = "", encoding = "utf-8", **args): self.infos = self.defaut.copy() self.infos.update(args) self.encoding = encoding self.contenu = {} if texte: self.importer(texte) @property def nom(self): return self.infos['nom'] @property def repertoire(self): return self.infos['repertoire'] @property def module(self): return self.infos['module'] @property def type(self): return self.infos['type'] @property def version(self): return self.infos['version'] @property def data(self): return self.exporter().encode(self.encoding) def importer(self, texte): def contenu_node(node): # Le contenu d'une node (contenu entre 2 balises) est soit du texte, soit (ou exclusif!) d'autres balises. # Le contenu autorise est ainsi passablement plus restreint que dans la specification XML. # Pour plus de detail, lire la doc sur le format de fichier. # 1er cas: contenu vide -> assimile a du texte. if not node.childNodes: return "" # 2eme cas: le contenu est purement du texte. if not "Element" in [subnode.__class__.__name__ for subnode in node.childNodes]: return node.childNodes[0].nodeValue.strip("\n").replace("<", "<").replace(">", ">").replace("&", "&").encode(param.encodage) # un certain nombres de fonctions python supportent encore mal l'unicode (par exemple, f(**{u"a":3}) ne fonctionne pas) # 3eme cas: le contenu est un ensemble de balises -> regroupees dans un dictionnaire. # ce qui n'est pas entre deux balises est ignore. dico = {} for elt in node.childNodes: if elt.__class__.__name__ == "Element": contenu = contenu_node(elt) if dico.has_key(elt.nodeName): dico[elt.nodeName] += [contenu] else: dico[elt.nodeName] = [contenu] return dico xml = parseString(texte) self.encoding = xml.encoding self.document = xml.childNodes[0] self.infos = self.defaut.copy() # contient tous les attributs du document for attribut in self.document._attrs.keys(): self.infos[attribut] = self.document._attrs[attribut].value self.contenu = contenu_node(self.document) return self def exporter(self): def convertir_contenu(dictionnaire): texte = "" for balise in dictionnaire.keys(): for elt in dictionnaire[balise]: texte += "<%s>\n" %balise if isinstance(elt, (str, unicode)): texte += elt.replace("&", "&").replace("<", "<").replace(">", ">").strip("\n") + "\n" elif isinstance(elt, dict): texte += convertir_contenu(elt) texte += "\n" %balise return texte texte = u"\n" %self.encoding texte += u"\n" %(self.type, self.version, self.module) texte += convertir_contenu(self.contenu) texte += u"" return texte def ajouter(self, nom, racine = None, contenu = None): u"""Ajoute une ou plusieurs nouvelle(s) node(s) nomme(s) 'nom' 'racine', leur contenu tant donn par 'contenu'. Renvoie le contenu, ou la nouvelle racine cre (si le contenu tait vide).""" if racine is None: racine = self.contenu if contenu is None: contenu = {} if not racine.has_key(nom): racine[nom] = [] racine[nom].append(contenu) return contenu def ecrire(self, path, zip = False): u"""Ecrit dans un fichier dont l'adresse est donne par 'path'. L'encodage est fix par 'self.encoding'. Eventuellement, le contenu peut-tre compress au format zip.""" contenu = self.data f = None rep = os.path.split(path)[0] if not os.path.exists(rep): os.makedirs(rep) try: if zip: f = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) f.writestr("content.geo", contenu) else: f = open(path, "w") f.write(contenu) finally: if f is not None: f.close() def ouvrir(self, path, zip = None): u"""Retourne un objet FichierGEO partir du fichier dont l'adresse est donne par 'path'. Si l'attribut 'zip' n'est pas fix, la dtection est automatique.""" f = None if not os.path.exists(path): return None, u"Le fichier n'existe pas." try: f = open(path, "rU") except IOError: print_error() return None, u"L'accs au fichier a t refus." except UnicodeError: print_error() return None, u"Caractres non reconnus." except Exception: print_error() return None, u"Impossible d'ouvrir le fichier." try: texte = f.read() finally: f.close() try: parseString(texte) except ExpatError: try: f = zipfile.ZipFile(path, "r") texte = f.read("content.geo") finally: f.close() self.importer(texte) # Filtre d'import pour les versions antrieures if self.version_interne() < self.version_interne(param.version): filtre_versions_anterieures(self) rep, fich = os.path.split(path) self.infos['repertoire'] = rep self.infos['nom'] = removeend(fich, ".geo", ".geoz") # nom sans l'extension return self, u"Le fichier %s a bien t ouvert." %path def version_interne(self, version = None): if version is None: version = self.version version = version.strip().lower() version = version.replace("alpha", "a").replace("beta", "b").replace("release candidate", "rc").replace("releasecandidate", "rc") version = version.replace("a", " -3 ").replace("b", " -2 ").replace("rc", " -1 ").replace(".", " ") return [int(n) for n in re.split("[ ]+", version)] ##def __old__ouvrir_fichierGEO(path): ## u"Dsuet. Utiliser plutt FichierGEO().ouvrir(path)." ## try: ## f=open(path,"rU") ## except IOError: ## return None, u"Le fichier n'existe pas, ou est inaccessible." ## except UnicodeError: ## return None, u"Caractres non reconnus." ## except: ## return None, u"Impossible d'ouvrir le fichier." ## try: ## texte = f.read() ## finally: ## f.close() ## fgeo = FichierGEO(texte) ## return fgeo, u"Le fichier %s a bien t ouvert." %path def ouvrir_fichierGEO(path): u"Alias de 'FichierGEO().ouvrir(path)'." return FichierGEO().ouvrir(path) class FichierSession(object): # cf. http://www.doughellmann.com/PyMOTW/tarfile/ def __init__(self, *fichiers, **infos): # {nom_module : [fichierGEO,...], ...} self.fichiers = {} self.infos = infos self.ajouter(*fichiers) def ajouter(self, *fichiers): for fichier in fichiers: self._ajouter_fichier(fichier) def _ajouter_fichier(self, fichier): if fichier.module in self.fichiers: self.fichiers[fichier.module].append(fichier) else: self.fichiers[fichier.module] = [fichier] def ecrire(self, path, compresser = True): rep = os.path.split(path)[0] if not os.path.exists(rep): os.makedirs(rep) tar = tarfile.open(path, mode = 'w:' + ('gz' if compresser else '')) def _ajouter(titre, data): info = tarfile.TarInfo(titre) info.size = len(data) tar.addfile(info, StringIO(data)) try: fichier_info = FichierGEO(type = 'Session WxGeometrie', module = 'main') for key, val in self.infos.items(): fichier_info.ajouter(key, None, repr(val)) _ajouter('session.info', fichier_info.data) for module, fichiers in self.fichiers.items(): for i, fichier in enumerate(fichiers): _ajouter(module + str(i) + '.geo', fichier.data) finally: tar.close() def ouvrir(self, path): self.fichiers = {} self.infos = {} tar = tarfile.open(path, mode = 'r') try: for member_info in tar.getmembers(): nom = member_info.name f = tar.extractfile(member_info) data = f.read() f.close() fichier = FichierGEO().importer(data) if nom == 'session.info': for key in fichier.contenu: self.infos[key] = eval_safe(fichier.contenu[key][0]) else: self._ajouter_fichier(fichier) finally: tar.close() return self def __iter__(self): def gen_fichiers(): for fichiers in self.fichiers.values(): for fichier in fichiers: yield fichier return gen_fichiers() wxgeometrie-0.133.2.orig/wxgeometrie/API/console.py0000644000175000017500000000733212014170666022350 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement ########################################## # CONSOLE ########################################## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # NOTES : # Un fichier .geo est essentiellement un fichier python (structure par du XML). # C'est potentiellement dangereux (en particulier sous Windows), car certains modules python (comme os, sys, ...), et certaines fonctions (write...) peuvent endommager le systeme d'exploitation. # Une bonne partie du travail de ce module consiste donc a securiser l'environnement d'execution. # Comme je suis loin d'etre un expert, il est possible (voire probable) que des failles subsistent neanmoins. # Merci de m'en informer. # Il faudra donner a l'utilisateur la possibilite d'autoriser la macro ponctuellement, ou meme de changer le parametre de securite from .. import param class Console: def __init__(self, parent): self.parent = parent self.locals = {'main': self.parent, 'onglets': self.parent.onglets, 'param': param} def executer(self, commande): u"La commande est excute dans l'espace de nom du panel." commande = commande.strip() # & est un raccourci pour print if commande[0] == '&': commande = 'print(' + commande[1:] + ')' # Les racourcis suivants sont utilisables : # Panel actuel : commande = commande.replace(u"!p.", u"panel.") # Canvas actuel : commande = commande.replace(u"!c.", u"canvas.") # Feuille utilise actuellement : commande = commande.replace(u"!f.", u"feuille.") # Objets de la feuille : commande = commande.replace(u"!o.", u"objets.") commande = commande.replace(u"!g.", u"moteur_graphique.") # Fentre principale : commande = commande.replace(u"!m.", u"main.") # if param.debug: # self.parent.onglets.onglet_actuel.action_effectuee(u"REQUETE CONSOLE:" + commande) # print u"REQUETE CONSOLE:" + commande print(u"REQUETE CONSOLE [" + self.parent.onglets.onglet_actuel.__titre__ + "]:\n>>> " + commande) self.locals.update({'panel': self.parent.onglets.onglet_actuel, 'canvas': self.parent.onglets.onglet_actuel.canvas, 'feuille': self.parent.onglets.onglet_actuel.feuille_actuelle, }) for nom in param.modules: if param.modules_actifs[nom]: self.locals[nom] = getattr(self.parent.onglets, nom) if self.parent.onglets.onglet_actuel.canvas is not None: self.locals['objets'] = self.parent.onglets.onglet_actuel.feuille_actuelle.objets self.locals['moteur_graphique'] = self.parent.onglets.onglet_actuel.canvas.graph exec(commande, self.parent.__dict__, self.locals) wxgeometrie-0.133.2.orig/wxgeometrie/API/moteur_graphique.py0000644000175000017500000014536412014170666024276 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) from __future__ import with_statement # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA from itertools import chain from operator import attrgetter from string import ascii_lowercase from matplotlib.colors import colorConverter from matplotlib.transforms import Bbox from matplotlib.backends.backend_wxagg import FigureCanvasAgg from matplotlib.lines import Line2D from matplotlib.collections import LineCollection from matplotlib.patches import Polygon, Circle, FancyArrowPatch, FancyBboxPatch from matplotlib.text import Text from matplotlib.axes import Axes from numpy import array, arange, concatenate, cos as ncos, sin as nsin from math import cos, sin, atan2, pi, hypot, sqrt, atan from ..pylib import fullrange, is_in, uu, warning, print_error, tex from .. import param class LigneDecoree(LineCollection): taille = NotImplemented def __init__(self, canvas, **kw): LineCollection.__init__(self, ()) self.canvas = canvas if kw: self.set(**kw) # XXX: matplotlib LineCollection.set_alpha() seems to be broken # (Matplotlib 0.99.1.1) def set_alpha(self, alpha): r, v, b, a = self.get_color()[0] self.set_color((r, v, b, alpha)) def set(self, **kw): maj = kw.pop('maj', True) for nom in self._parametres: if kw.has_key(nom): setattr(self, nom, kw.pop(nom)) if kw: LineCollection.set(self, **kw) if maj: self._maj_data() class FlecheGenerique(LigneDecoree): angle = 60 taille = 10 double = False position = 1 angle = 60 def _maj(self, lignes): self.set_segments(lignes) # Le style de ligne (pointills, etc.) ne doit pas s'appliquer # aux dcorations (pointes de flches, etc.) ls = self.get_linestyles()[0] self.set_linestyles([ls, '-', '-']) def _pointe(self, xy, dxdy): a = self.angle*pi/360 # angle/2, puis degrs -> radians taille = self.taille # M + # C \ # Schma: A +--------------+-----+ B --> direction # / # N + # On calcule les coordonnes de C, puis par rotation autour de B, celles de M et N. xB, yB = self.canvas.coo2pix(*xy) dpx, dpy = self.canvas.dcoo2pix(*dxdy) alpha = atan2(dpy, dpx) xM = xB + taille*cos(alpha - a) yM = yB + taille*sin(alpha - a) xN = xB + taille*cos(alpha + a) yN = yB + taille*sin(alpha + a) return self.canvas.pix2coo((xM, xB, xN), (yM, yB, yN)) class Fleche(FlecheGenerique): _parametres = ('xy0', 'xy1', 'taille', 'double', 'position', 'angle') xy0 = (0, 0) xy1 = (1, 1) def __init__(self, canvas, **kw): u"""Une flche (ventuellement double). En plus des styles de matplotlib.collections.LineCollection, les styles suivants sont dfinis: - taille: la longueur de la pointe (en pixels) ; - double: flche double ou non ; - position: position (entre 0 et 1) de la pointe sur la flche. ex: 1 pour l'extrmit finale, 0.5 pour le milieu, 0 pour le dbut. - angle: l'ouverture de la pointe (en degrs) - xy0: dbut de la flche (tuple) - xy1: fin de la flche (tuple) """ FlecheGenerique.__init__(self, canvas, **kw) def _maj_data(self): xy0 = array(self.xy0) xy1 = array(self.xy1) lignes = [(xy0, xy1)] k = self.position dxdy = xy1 - xy0 lignes.append(zip(*self._pointe((1 - k)*xy0 + k*xy1, -dxdy))) if self.double: lignes.append(zip(*self._pointe(k*xy0 + (1 - k)*xy1, dxdy))) self._maj(lignes) class FlecheCourbe(FlecheGenerique): u"""Une flche (ventuellement double) en forme d'arc de cercle. En plus des styles de matplotlib.collections.LineCollection, les styles suivants sont dfinis: - taille: la longueur de la pointe (en pixels) ; - double: flche double ou non ; - position: position (entre 0 et 1) de la pointe sur la flche. ex: 1 pour l'extrmit finale, 0.5 pour le milieu, 0 pour le dbut. - angle: l'ouverture de la pointe (en degrs) ; - intervalle: angle de dbut et de fin de l'arc (radians) ; - centre: centre du cercle contenant l'arc ; - rayon: rayon du cercle contenant l'arc ; - sens: orientation de l'arc (1 ou -1). """ intervalle = (0, pi) centre = (0, 0) sens = 1 rayon = 1 _parametres = ('taille', 'double', 'position', 'angle', 'intervalle', 'centre', 'rayon', 'sens') def _maj_data(self): x, y = self.centre r = self.rayon a, b = self.intervalle t = fullrange(a, b, self.canvas.pas()) lignes = [zip(x + r*ncos(t), y + r*nsin(t))] k = self.position sens = self.sens if sens == -1: a, b = b, a c = k*b + (1 - k)*a # Point de l'arc x0 = x + r*cos(c) y0 = y + r*sin(c) # Vecteur normal au rayon dxdy = ((y0 - y, x - x0) if sens == 1 else (y - y0, x0 - x)) lignes.append(zip(*self._pointe((x0, y0), dxdy))) if self.double: c = k*a + (1 - k)*b # Point de l'arc x0 = x + r*cos(c) y0 = y + r*sin(c) # Vecteur normal au rayon dxdy = ((y - y0, x0 - x) if sens == 1 else (y0 - y, x - x0)) lignes.append(zip(*self._pointe((x0, y0), dxdy))) self._maj(lignes) class Codage(LigneDecoree): u'''Objet graphique servant coder les segments et arcs de mme longueur. Paramtres: - taille: taille du symbole (en pixels). - angle: angle relatif au segment, en degrs, pour les symboles / et X. - position: position du symbole (couple de coordonnes). - direction: couple (dx, dy) donnant l'orientation du segment. Ce couple est exprim en coordonnes (et non en pixels). - marge: si la longueur en pixels correspondant (dx, dy) est infrieure la marge + la taille du codage, le codage n'est pas affich. ''' style = '/' taille = 10 position = (0, 0) direction = (1, 0) marge = 0 angle = 60 _parametres = ('style', 'taille', 'position', 'direction', 'angle') def set(self, **kw): pixel = kw.pop('pixel', False) if 'position' in kw: pos = kw.pop('position') self.position = (pos if pixel else self.canvas.coo2pix(*pos)) if 'direction' in kw: dir = kw.pop('direction') self.direction = (dir if pixel else self.canvas.dcoo2pix(*dir)) self.hyp = hypot(*self.direction) LigneDecoree.set(self, **kw) def _maj_data(self): self.set_segments(self._codage()) def _codage(self): lignes = [] style = self.style if style and self.taille: if style == 'o': # On vrifie qu'il y a assez de place sur le segment pour afficher le cercle if self.hyp > self.taille + self.marge: x, y = self.position t = fullrange(0, 2*pi, 2./self.taille) r = .7*self.taille lignes.append(zip(*self.canvas.pix2coo(x + r*ncos(t), y + r*nsin(t)))) elif style.count('/') == len(style): lignes.extend(self._oblique(self.position, self.direction, n=len(style))) elif style.count('|') == len(style): lignes.extend(self._oblique(self.position, self.direction, n=len(style), angle=pi/2)) elif style.count('\\') == len(style): lignes.extend(self._oblique(self.position, self.direction, n=len(style), sens=-1)) elif style in ('x', 'X'): lignes.extend(self._oblique(self.position, self.direction)) lignes.extend(self._oblique(self.position, self.direction, sens=-1)) return lignes def _oblique(self, xy, dxdy, n=1, sens=1, angle=None): u'''Retourne les coordonnes de n barres obliques, la position xy. dxdy dfinit l'orientation des barres. S'il y a plusieurs barres, elles seront espaces de 1 paisseur de ligne. ''' a = (sens*self.angle*pi/180 if angle is None else angle)# degrs -> radians r = self.taille # + M # I / # Schma: A +--------+--------+ B --> direction # / # N + # On calcule les coordonnes de C, puis par rotation autour de B, celles de M et N. dx, dy = dxdy lw = self.get_linewidth()[0] # S'il n'y a pas assez d'espace sur le segment, on n'affiche pas le codage. # TODO: affiner l'algorithme (pour l'instant, on fait comme si les lignes # taient verticales, et non obliques). if self.hyp < self.marge + lw*(2*n + 2): return [] alpha = atan2(dy, dx) xI, yI = xy xM = xI + r*cos(alpha - a) yM = yI + r*sin(alpha - a) xN = 2*xI - xM yN = 2*yI - yM obliques = [] xu = dx/self.hyp yu = dy/self.hyp for i in xrange(-n + 1, n, 2): k = (.5*lw + 1.5)*i M = self.canvas.pix2coo(xM + k*xu, yM + k*yu) N = self.canvas.pix2coo(xN + k*xu, yN + k*yu) obliques.append((M, N)) return obliques class Angle(Polygon): _parametres = ('intervalle', 'style', 'taille', 'rayon', 'angle', 'position') def __init__(self, canvas, **kw): Polygon.__init__(self, ((0,0),)) self.canvas = canvas if kw: self.set(**kw) def set(self, **kw): maj = kw.pop('maj', True) for nom in self._parametres: if kw.has_key(nom): setattr(self, nom, kw.pop(nom)) if kw: Polygon.set(self, **kw) if maj: self._maj_data() def _maj_data(self): xy = [self.position] x0, y0 = self.P = self.canvas.coo2pix(*self.position) a, b = self.intervalle t = fullrange(a, b, .3*(b - a)/self.taille) if self.style == '^': r = self.rayon # P M Angle droit au point P(x0, y0) # +-----+------- # | | # | | # N +-----+ I On a donc PM-> + PN-> = PI-> # | D'o "I = M + N - P" # | M = x0 + .5*r*cos(a), y0 + .5*r*sin(a) N = x0 + .5*r*cos(b), y0 + .5*r*sin(b) I = M[0] + N[0] - x0, M[1] + N[1] - y0 # Pixels -> coordonnes xy.append(self.canvas.pix2coo(*M)) xy.append(self.canvas.pix2coo(*I)) xy.append(self.canvas.pix2coo(*N)) else: # cost et sint sont mis en cache pour tre rutiliss par CodageAngle() self.cost = ncos(t) self.sint = nsin(t) xy.extend(self.arc_angle(i=max(0, self.style.count(')') - 1))) self.xy = xy def arc_angle(self, i=0): r = self.rayon x0, y0 = self.P return zip(*self.canvas.pix2coo(x0 + (r - 3*i)*self.cost, y0 + (r - 3*i)*self.sint)) class CodageAngle(Codage): u'''Objet graphique servant coder des angles de mme mesure. L'objet CodageAngle doit tre associ un objet graphique Angle prexistant. Il en partage les proprits. ''' def __init__(self, canvas, angle_associe, **kw): self.angle_associe = angle_associe Codage.__init__(self, canvas, **kw) def _maj_data(self): lignes = [self.angle_associe.xy[1:]] a, b = self.angle_associe.intervalle r = self.angle_associe.rayon x0, y0 = self.angle_associe.P # pixels c = .5*(a + b) self.set(direction=(.5*r*(cos(b) - cos(a)), .5*r*(sin(b) - sin(a))), position=(x0 + r*cos(c), y0 + r*sin(c)), taille=.8*self.angle_associe.taille, angle=self.angle_associe.angle, style=self.angle_associe.style.replace('/', '|'), # patch temporaire maj=False, pixel=True, ) n = self.style.count(')') if n > 1: for i in xrange(0, n - 1): lignes.append(self.angle_associe.arc_angle(i=i)) lignes.extend(self._codage()) self.set_segments(lignes) class ZoomArtistes(object): def __init__(self, axes, zoom_texte, zoom_ligne): self.zoom_texte = zoom_texte self.zoom_ligne = zoom_ligne self.size = {} self.linewidth = {} self.markersize = {} self.markeredgewidth = {} self.taille = {} self.rayon = {} self.artistes = list(chain(axes.artists, axes.patches, axes.lines, axes.texts, axes.tables, axes.collections)) def __enter__(self): self.regler_textes = abs(self.zoom_texte - 1) > 0.05 self.regler_lignes = abs(self.zoom_ligne - 1) > 0.05 if self.regler_textes or self.regler_lignes: for artiste in self.artistes: if artiste._visible: ID = id(artiste) if self.regler_textes: if isinstance(artiste, Text): size = self.size[ID] = artiste.get_size() artiste.set_size(size*self.zoom_texte) if self.regler_lignes: if isinstance(artiste, Line2D): lw = self.linewidth[ID] = artiste.get_linewidth() artiste.set_linewidth(lw*self.zoom_ligne) ms = self.markersize[ID] = artiste.get_markersize() artiste.set_markersize(ms*self.zoom_ligne) mw = self.markeredgewidth[ID] = artiste.get_markeredgewidth() artiste.set_markeredgewidth(mw*self.zoom_ligne) elif isinstance(artiste, LineCollection): lws = self.linewidth[ID] = artiste.get_linewidth() artiste.set_linewidth(tuple(lw*self.zoom_ligne for lw in lws)) if isinstance(artiste, CodageAngle): angle = artiste.angle_associe taille = self.taille[ID] = angle.taille rayon = self.rayon[ID] = angle.rayon angle.set(taille=self.zoom_ligne*taille, rayon=self.zoom_ligne*rayon) artiste._maj_data() elif isinstance(artiste, LigneDecoree): taille = self.taille[ID] = artiste.taille artiste.set(taille=self.zoom_ligne*taille) return self.artistes def __exit__(self, type, value, traceback): if self.regler_textes or self.regler_lignes: for artiste in self.artistes: if artiste._visible: ID = id(artiste) if self.regler_textes: if isinstance(artiste, Text): artiste.set_size(self.size[ID]) if self.regler_lignes: if isinstance(artiste, Line2D): artiste.set_linewidth(self.linewidth[ID]) artiste.set_markersize(self.markersize[ID]) artiste.set_markeredgewidth(self.markeredgewidth[ID]) elif isinstance(artiste, LineCollection): artiste.set_linewidth(self.linewidth[ID]) if isinstance(artiste, CodageAngle): angle = artiste.angle_associe angle.set(taille=self.taille[ID], rayon=self.rayon[ID]) artiste._maj_data() elif isinstance(artiste, LigneDecoree): artiste.set(taille=self.taille[ID]) class CollecterArtistes(object): def __init__(self, moteur_graphique): self.moteur_graphique = moteur_graphique def __enter__(self): m = self.moteur_graphique # On rerfrence tous les artistes. self.dict_artistes = m._effacer_artistes() m.canvas._affiche_module() m._dessine_axes() artistes = m.canvas.feuille_actuelle.lister_figures()[0] m.axes.artists.extend(artistes) def __exit__(self, type, value, traceback): m = self.moteur_graphique # On remet la liste des artistes dans son tat d'origine (vide en gnral) m._restaurer_artistes(self.dict_artistes) class AjusterEchelle(object): def __init__(self, moteur_graphique, echelle): self.moteur_graphique = moteur_graphique self.echelle = echelle def __enter__(self): if self.echelle is not None: m = self.moteur_graphique xe, ye = self.echelle xmin, xmax, ymin, ymax = m.canvas.fenetre l, h = m.canvas.dimensions_fenetre # Conversion en inches : 1 inch = 2.54 cm x = xe*l/2.54 y = ye*h/2.54 self.taille_precedente = tuple(m.canvas.figure.get_size_inches()) # faire une copie !! m.canvas.figure.set_size_inches(x, y) # on redessine cause du changement d'chelle (le ratio a pu tre modifi) if m.canvas.dimensions is None: m.canvas._dimensions = 850*xe, 850*h/l*ye else: L, H = m.canvas.dimensions # on conserve la plus grande des dimensions if H < L: m.canvas._dimensions = L, L*h/l else: m.canvas._dimensions = H*l/h, H m.canvas.feuille_actuelle._rafraichir_figures() m.dessiner(rafraichir_axes = True) def __exit__(self, type, value, traceback): if self.echelle is not None: m = self.moteur_graphique m.canvas._dimensions = None m.canvas.feuille_actuelle._rafraichir_figures() m.dessiner(rafraichir_axes = True) # On remet la figure dans son tat d'origine m.canvas.figure.set_size_inches(self.taille_precedente) class Moteur_graphique(object): def __init__(self, canvas): self.canvas = canvas self._dernier_objet_deplace = None self.axes = self.canvas.axes self._effacer_artistes() # Buffer contenant la dernire image. self._dernier_dessin = None # +---------------------+ # | Fonctions de dessin | # +---------------------+ @property def zoom_ligne(self): return self.canvas.zoom_ligne @property def zoom_texte(self): return self.canvas.zoom_texte def _ajouter_objet(self, artiste): self.axes.add_artist(artiste) def _ajouter_objets(self, liste_artistes): for artiste in liste_artistes: self.axes.add_artist(artiste) def ajouter(self, artiste): u"""Ajoute un artiste (objet graphique de matplotlib) dessiner. NB: 'artiste' peut aussi tre une liste d'artistes, ou une liste de listes...""" if isinstance(artiste, (list, tuple)): for elt in artiste: self.ajouter(elt) else: self._ajouter_objet(artiste) def _temp_warning_color(self, kw): u" supprimer aprs quelques temps (01/05/2011)." if 'couleur' in kw: kw['color'] = kw.pop('couleur') warning("Utiliser desormais 'color' au lieu de 'couleur'.", level=1) def ligne(self, x = (0, 1), y = (1, 0), pixel = False, **kw): assert isinstance(pixel, bool), str(type(pixel)) # Changement d'API self._temp_warning_color(kw) if pixel: x, y = self.canvas.pix2coo(x, y) # if self.canvas.transformation is not None: # x, y = zip(*(zip(x, y)*self.canvas.transformation).array) ligne = Line2D(x, y, **kw) return ligne def ajouter_ligne(self, x = (0, 1), y = (1, 0), color = 'b', pixel = False, **kw): self._ajouter_objet(self.ligne(x, y, pixel, color=color, **kw)) def polygone(self, x = (0, 1, 1, 0), y = (1, 0, 0, 1), pixel = False, **kw): self._temp_warning_color(kw) if pixel: x, y = self.canvas.pix2coo(x, y) polygone = Polygon(zip(x, y), **kw) # if self.canvas.transformation is not None: # x, y = zip(*(zip(x, y)*self.canvas.transformation).array) return polygone def ajouter_polygone(self, x = (0, 1, 1, 0), y = (1, 0, 0, 1), facecolor = 'b', pixel = False, **kw): ## if kw.pop('color', None): ## warning("Utiliser desormais 'facecolor' ou 'edgecolor' au lieu de 'couleur'.") ## facecolor = color self._ajouter_objet(self.polygone(x, y, pixel, facecolor=facecolor, **kw)) def texte(self, x=0, y=0, txt=u'hello !', pixel=False, **kw): if pixel: x, y = self.canvas.pix2coo(x, y) if not isinstance(txt, unicode): txt = unicode(txt, param.encodage) kw.setdefault('family', 'serif') texte = Text(x, y, txt, **kw) texte.figure = self.canvas.figure # texte must have access to figure.dpi # if self.canvas.transformation is not None: # x, y = ((x, y)*self.canvas.transformation).array[0] return texte def ajouter_texte(self, x=0, y=0, txt=u'hello !', pixel=False, **kw): self._ajouter_objet(self.texte(x, y, txt, pixel, **kw)) def arc(self, x, y, vecteur, **kw): u"""Affiche un petit demi-cercle d'orientation choisie. Par ex, pour le vecteur (1,0), ie. ->, l'arc est orient comme ceci: ( Pour le vecteur (-1,0), ie. <-, l'arc est orient comme ceci: ). On travaille sur les pixels pour faire de la trigonometrie plus simplement (le repere devient ainsi orthonorme)""" self._temp_warning_color(kw) vecteur = self.canvas.dcoo2pix(*vecteur) if vecteur[0] == 0: if vecteur[1] > 0: angle = pi/2 else: angle = -pi/2 else: angle = atan(vecteur[1]/vecteur[0]) if vecteur[0] < 0: angle += pi # donne l'angle d'incidence l'extrmit t = arange(angle - pi/2, angle + pi/2, 0.05) R = self.canvas.taille["("]*self.zoom_ligne x, y = self.canvas.coo2pix(x, y) return self.ligne(x + R*(ncos(t) - cos(angle)), y + R*(nsin(t) - sin(angle)), pixel=True, **kw) def ajouter_arc(self, x, y, vecteur, color='k', **kw): self._ajouter_objet(self.arc(x, y, vecteur, color, **kw)) def point(self, x, y, plein=True, **kw): u"Un petit cercle, vide ou plein." assert isinstance(plein, bool), str(type(plein)) # Changement d'API self._temp_warning_color(kw) couleur = kw.pop('color', 'k') kw.setdefault('zorder', 2.1) kw.setdefault('markeredgecolor', couleur) kw.setdefault('markerfacecolor', couleur if plein else 'w') kw.setdefault('markeredgewidth', 1) kw.setdefault('markersize', 2*self.canvas.taille["o"]) kw.setdefault('marker', 'o') return self.ligne([x], [y], **kw) # Le zorder doit tre suprieur celui d'une courbe (2 par dfaut), # et la largeur du trait doit tre celle d'une courbe (1 par dfaut). def ajouter_point(self, x, y, plein=True, **kw): self._ajouter_objet(self.point(x, y, plein, **kw)) # def _fleche_matplotlib(self, x0=0, y0=0, x1=1, y1=1, **kw): # kw.setdefault(mutation_scale=25) # arrow = FancyArrowPatch((x0, y0), (x1, y1), **kw) # # cf. matplotlib.patches.ArrowStyle.get_styles() pour les # # styles de flches. # # Changer coord. avec FancyArrowPatch.set_position(). # # FIXME: il n'y a aucun moyen de changer la position de la flche # # sur la ligne (au milieu, au dbut, au bout...) # return arrow def fleche(self, xy0=(0, 0), xy1=(1, 1), **kw): return Fleche(xy0=xy0, xy1=xy1, canvas=self.canvas, **kw) def ajouter_fleche(self, x0=0, y0=0, x1=1, y1=1, **kw): self._ajouter_objet(self.fleche(x0, y0, x1, y1, **kw)) def fleche_courbe(self, **kw): return FlecheCourbe(canvas=self.canvas, **kw) def ajouter_fleche_courbe(self, **kw): self._ajouter_objet(self.fleche_courbe(**kw)) def codage(self, **kw): return Codage(canvas=self.canvas, **kw) def ajouter_codage(self, **kw): self._ajouter_objet(self.codage(**kw)) def cercle(self, xy=(0, 0), r=1, **kw): return Circle(xy, r, **kw) def ajouter_cercle(self, xy=(0, 0), r=1, **kw): self._ajouter_objet(self.cercle(xy, r, **kw)) def rectangle(self, xy=(0, 0), w=1, h=1, **kw): return FancyBboxPatch(xy, w, h, **kw) def ajouter_rectangle(self, xy=(0, 0), w=1, h=1, **kw): self._ajouter_objet(self.rectangle(xy, w, h, **kw)) def angle(self, **kw): return Angle(canvas=self.canvas, **kw) def ajouter_angle(self, **kw): self._ajouter_objet(self.angle(**kw)) def codage_angle(self, **kw): return CodageAngle(canvas=self.canvas, **kw) def ajouter_codage_angle(self, **kw): self._ajouter_objet(self.codage_angle(**kw)) # +------------------------+ # | Gestion de l'affichage | # +------------------------+ def _creer_arriere_plan(self, rafraichir_axes): if rafraichir_axes: dict_artistes = self._effacer_artistes() self.canvas._affiche_module() self._dessine_axes() # Les axes et lgendes de matplotlib ne conviennent pas, on les dsactive. self.axes.legend_ = None self.axes.axison = False # Synchronisation de la fentre de matplotlib avec celle de la feuille self._regler_fenetre() # Attention, la synchronisation de la fentre doit avoir lieu : # - APRS self.canvas._affiche_module(), qui peut encore modifier la fentre # - avant FigureCanvasAgg.draw(), qui dessine la figure with ZoomArtistes(self.axes, self.zoom_texte, self.zoom_ligne): FigureCanvasAgg.draw(self.canvas) ## self.canvas.draw(repaint = False) ## self._dessiner_artistes() self._mise_en_cache_axes = self._en_cache() # _artistes_repere sert pour dboguer. Cf. '.infos()' self._artistes_repere = self._restaurer_artistes(dict_artistes) else: self._restaurer(self._mise_en_cache_axes) def _regler_fenetre(self): xmin, xmax, ymin, ymax = self.canvas.fenetre self.axes.viewLim.set_points(array([[xmin, ymin], [xmax, ymax]])) def dessiner(self, dessin_temporaire = False, rafraichir_axes = False): # Affichage bloqu if self.canvas.affichage_gele: return ## print 'rafraichissement' # Quand un objet est dplac, on optimise en ne redessinant # que l'objet, et ce qui en dpend. objet_deplace = self.canvas.feuille_actuelle._objet_deplace if dessin_temporaire: self._restaurer(self._dernier_dessin) else: self._objets_fixes, self._objets_mobiles = self.canvas.feuille_actuelle.lister_figures() if objet_deplace is None or rafraichir_axes: # Cas par dfaut : on dessine tout. self._creer_arriere_plan(rafraichir_axes) self._ajouter_objets(self._objets_fixes) elif objet_deplace is self._dernier_objet_deplace: # Un cache spcifique existe dj : on l'utilise. ## print "Optimisation." self._restaurer(self._mise_en_cache_objets_fixes) else: # On gnre un cache (spcifique l'objet dplac). self._creer_arriere_plan(rafraichir_axes) dict_artistes = self._effacer_artistes() self._ajouter_objets(self._objets_fixes) self._dessiner_artistes() self._mise_en_cache_objets_fixes = self._en_cache() self._restaurer_artistes(dict_artistes) self._ajouter_objets(self._objets_mobiles) # Rsum du bloc de code prcdent : # Les objets fixes ne doivent pas tre redessins, # mais restitus partir d'un cache. # On gnre le cache la premire fois que objet_deplace is not None # et on utilise le cache les autres fois si objet_deplace # correspond toujours au mme objet. # On dessine dans le buffer self._dessiner_artistes() # Affichage proprement dit (copie du buffer l'cran) if not self.canvas.affichage_gele_en_apparence: self.canvas.blit() # Garde en mmoire l'affichage pour viter de redessiner la fentre # dans certains cas (fentre masque, etc.) if not dessin_temporaire: self._dernier_dessin = self._en_cache() self._dernier_objet_deplace = objet_deplace self.canvas.feuille_actuelle._objet_deplace = None # _artistes_dessin sert pour le dbogage self._artistes_dessin = self._effacer_artistes() def _convertir_zone(self, zone): u"Conversion de la zone: coordonnes -> inches" x0, x1, y0, y1 = zone l, h = self.canvas.figure.get_size_inches() fenetre = self.canvas.fenetre def coo2inches(x, y): return (l*(x - fenetre[0])/(fenetre[1] - fenetre[0]), h*(y - fenetre[2])/(fenetre[3] - fenetre[2])) x0, y0 = coo2inches(x0, y0) x1, y1 = coo2inches(x1, y1) x0, x1 = max(0, min(x0, x1)), min(l, max(x0, x1)) y0, y1 = max(0, min(y0, y1)), min(h, max(y0, y1)) if x1 <= x0 or y1 <= y0: raise RuntimeError, "Erreur: image de dimensions nulles !" return Bbox(((x0, y0), (x1, y1))) def exporter(self, nom, dpi = None, zone = None, echelle = None): u"Exporter la figure." with self.canvas.geler_affichage(seulement_en_apparence = True, actualiser = False): # (Nota: le rglage de la fentre ne sert en fait qu'en l'absence de GUI.) self._regler_fenetre() # NB: ajuster l'chelle *avant* de convertir la zone with AjusterEchelle(self, echelle): if zone: zone = self._convertir_zone(zone) with CollecterArtistes(self): with ZoomArtistes(self.axes, self.zoom_texte, self.zoom_ligne): # Export proprement dit: self.canvas.figure.savefig(nom, dpi = dpi, bbox_inches = zone) def exporter_tikz(self, chiffres_significatifs = 4): conv_tikz = ConvertisseurTikz(cs = chiffres_significatifs) code = '\\begin{tikzpicture}\n' #TODO: rgler fentre d'affichage with CollecterArtistes(self): code += '\n'.join(conv_tikz.convertir(artiste) for artiste in self.axes.artists) code += '\n\\end{tikzpicture}' return code def restaurer_dessin(self): u'''Restaure le dernier dessin.''' self._restaurer(self._dernier_dessin) self.canvas.blit() def _dico_artistes(self): dico = {} for rubrique in ("lines", "patches", "texts", "tables", "artists", "images", "collections"): dico[rubrique] = getattr(self.axes, rubrique) return dico def _effacer_artistes(self): u"Supprime tous les artistes (objets graphiques de matplotlib)." dico = self._dico_artistes() self.axes.artists = [] self.axes.lines = [] self.axes.patches = [] self.axes.texts = [] self.axes.tables = [] self.axes.images = [] self.axes.collections = [] # collection.Collection instances return dico def _restaurer_artistes(self, dictionnaire): u"Restaure les artistes prcdemment supprims." dico = self._dico_artistes() self.axes.__dict__.update(dictionnaire) return dico def _dessiner_artistes(self): with ZoomArtistes(self.axes, self.zoom_texte, self.zoom_ligne) as artistes: artistes.sort(key=attrgetter('zorder')) for artiste in artistes: if artiste._visible: try: self.axes.draw_artist(artiste) except: print_error() def _en_cache(self): return self.canvas.copy_from_bbox(self.axes.bbox) def _restaurer(self, buffer): if buffer is not None: self.canvas.restore_region(buffer) def _dessine_axe(self, num = 0, nbr_axes = 1): u"Dessine l'axe des abscisses (num = 0) ou des ordonnes (num = 1)." #un = nbr_axes == 1 # un seul axe #x = num == 0; y = not x # axe des abscisses / des ordonnees #rep = self.utiliser_repere # repere ou non vect = self.canvas.utiliser_repere and self.canvas.repere[1 + num] in ascii_lowercase # repere defini ou non par des vecteurs correspondance = {"b" : "bottom", "c": "center", "l": "left", "r": "right", "t": "top"} def f(a, b): return a if num == 0 else b signe = f(-1, 1) origine = self.canvas.origine_axes[1 - num] fenetre = self.canvas.fenetre repere = self.canvas.repere # On cree l'axe proprement dit: self.ajouter_ligne(f([fenetre[0], fenetre[1]], [origine, origine]), f([origine, origine], [fenetre[2], fenetre[3]]), "k") if self.canvas.afficher_fleches: # On cree la fleche au bout de l'axe: x, y = self.canvas.coo2pix(f(fenetre[1], origine), f(origine, fenetre[3])) dx, dy = .5*self.canvas.taille[">"]*self.zoom_ligne*array([f(sqrt(3), 1), f(1, sqrt(3))]) self.ajouter_ligne([x - dx, x, x + signe*dx], [y + dy, y, y + signe*dy], "k", pixel = True) # FORMAT (_origine_ et _graduation_) : [texte, x, y, alignement horizontal, alignement vertical] # _origine_ : infos sur l'origine de l'axe # _graduation_ : infos sur le 2eme point de l'axe ox, oy = self.canvas.origine_axes ux, uy = self.canvas.gradu # TODO: tendre un repre non orthogonal kx, ky = self.canvas.dpix2coo(1, 1) x, y = self.canvas.coo2pix(f(ox + ux, ox), f(oy, oy + uy)) if self.canvas.gradu[num] and self.canvas.saturation(num) < param.saturation: if vect: self.ajouter_ligne([x - dx, x, x + signe*dx], [y + dy, y, y + signe*dy], "k", pixel = True) elif self.canvas.afficher_quadrillage: d = .5*self.canvas.taille["|"]*self.zoom_ligne self.ajouter_ligne([f(x, x - d), f(x, x + d)], [f(y - d, y), f(y + d, y)], "k", pixel = True) def str_(val): s = str(val) if s.endswith('.0'): s = s[:-2] #TODO: remplacer le point par une virgule (selon les paramtres actifs) return s if nbr_axes == 1: # un seul axe if num: # axe des ordonnees if self.canvas.utiliser_repere: # utiliser un repere (meme origine sur chaque axe...) _origine_ = [repere[0], ox - 8*kx, oy, "r", "c"] if vect: # reperage par des vecteurs _graduation_ = [r"$\tt{\vec{" + repere[2] + "}}$", ox - 8*kx, oy + .5*uy, "r", "c"] else: # reperage par des points _graduation_ = [repere[2], ox - 8*kx, oy + uy, "r", "c"] else: # pas un repere _origine_ = [str_(oy), ox - 8*kx, oy, "r", "c"] _graduation_ = [str_(oy + uy), ox - 8*kx, oy + uy, "r", "c"] else: # axe des abscisses if self.canvas.utiliser_repere: # utiliser un repere (meme origine sur chaque axe...) _origine_ = [repere[0], ox, oy - 8*ky, "c", "t"] if vect: # reperage par des vecteurs _graduation_ = [r"$\tt{\vec{" + repere[1] + "}}$", ox + .5*ux, oy - 8*ky, "c", "t"] else: # reperage par des points _graduation_ = [repere[1], ox + ux, oy - 8*ky, "c", "t"] else: # pas un repere _origine_ = [str_(ox), ox, oy - 8*ky, "c", "t"] _graduation_ = [str_(ox + ux), ox + ux, oy - 8*ky, "c", "t"] else: # 2 axes if num: # axe des ordonnees if self.canvas.utiliser_repere: _origine_ = None if vect: # reperage par des vecteurs _graduation_ = [r"\vec{" + repere[2] + "}", ox - 8*kx, oy + .5*uy, "r", "c"] else: # reperage par des points _graduation_ = [repere[2], ox - 8*kx, oy + uy, "r", "c"] else: # pas un repere _origine_ = [str_(oy), ox - 8*kx, oy + 3*ky, "r", "b"] _graduation_ = [str_(oy + uy), ox - 8*kx, oy + uy, "r", "b"] else: # axe des abscisses if self.canvas.utiliser_repere: _origine_ = [repere[0], ox - 8*kx, oy - 8*ky, "r", "t"] if vect: # reperage par des vecteurs _graduation_ = [r"\vec{" + repere[1] + "}", ox + .5*ux, oy - 8*ky, "c", "t"] else: # reperage par des points _graduation_ = [repere[1], ox + ux, oy - 8*ky, "c", "t"] else: # pas un repere _origine_ = [str_(ox), ox + 3*kx, oy - 8*ky, "l", "t"] _graduation_ = [str_(ox + ux), ox + ux, oy - 8*ky, "l", "t"] if _origine_ and self.canvas.gradu[num]: self.ajouter_texte(_origine_[1], _origine_[2], tex(_origine_[0]), \ horizontalalignment = correspondance[_origine_[3]], \ verticalalignment = correspondance[_origine_[4]], size = 14) if _graduation_ and self.canvas.gradu[num] and self.canvas.saturation(num) < param.saturation: self.ajouter_texte(_graduation_[1], _graduation_[2], tex(_graduation_[0]), \ horizontalalignment = correspondance[_graduation_[3]], \ verticalalignment = correspondance[_graduation_[4]], size = 14) def _quadriller(self, hauteur = 3, pas = (None, None), style = None, epaisseur = 1, couleur = "k"): u"""Cre des graduations ou un quadrillage (si hauteur = None). Si pas[0] = 0 (respectivement, pas[1] = 0), les graduations seront uniquement horizontales (respectivement verticales).""" origine = self.canvas.origine_axes fenetre = self.canvas.fenetre pas = list(pas) # Je n'utilise pas des listes directement, mais des tuples. # Ceci afin de n'avoir jamais la MEME liste. if hauteur == None: # quadrillage xmin, xmax, ymin, ymax = fenetre _min = ymin, xmin ; _max = ymax, xmax if not style: style = ":" else: # graduations ## _min = [origine[i] - hauteur*self.canvas.coeff(i) for i in (1, 0)] # ymin, xmin ## _max = [origine[i] + hauteur*self.canvas.coeff(i) for i in (1, 0)] # ymax, xmax # ymin, xmin: _min = [origine[1] - self.canvas.dpix2coo(0, hauteur)[1], origine[0] - self.canvas.dpix2coo(hauteur, 0)[0]] # ymax, xmax: _max = [origine[1] + self.canvas.dpix2coo(0, hauteur)[1], origine[0] + self.canvas.dpix2coo(hauteur, 0)[0]] if not style: style = "-" ## #type_ligne = couleur + style # Format de 'segments': [[(x0,y0), (x1,y1), ...], [...], [...], ...] # cf. LineCollection segments = [] for n in (0, 1): if pas[n] == None: pas[n] = self.canvas.gradu[n] pas[n] = 1.*abs(pas[n]) # graduations sur l'axe: if pas[n] and self.canvas.coeff(n)/pas[n] < param.saturation: maxi = fenetre[2*n + 1] if self.canvas.afficher_axes and (n in self.canvas.liste_axes) and hauteur: # les graduations qui chevauchent les fleches, c'est pas joli maxi -= .7*self.canvas.taille[">"]*self.zoom_ligne*sqrt(3)*self.canvas.coeff(n) valeurs = concatenate((arange(origine[n], fenetre[2*n], -pas[n]), arange(origine[n] + pas[n], maxi, pas[n]))) for val in valeurs: # si on affiche le quadrillage ou les graduations sur les axes, ca fait pas net: if val != origine[n] or not self.canvas.afficher_axes or not (1 - n in self.canvas.liste_axes): if n: segments.append([(_min[n], val), (_max[n], val)]) else: segments.append([(val, _min[n]), (val, _max[n])]) ## m = len(segments) ## width = m*[epaisseur] ## color = m*[matplotlib.colors.colorConverter.to_rgba(couleur)] couleur = colorConverter.to_rgba(couleur) style_dict = {':': 'dotted', '--': 'dashed', '-.': 'dashdot', '-': 'solid'} style = style_dict.get(style, style) collection = LineCollection(segments, linewidths = epaisseur, colors = couleur, linestyle = style) self.axes.add_collection(collection) def _dessine_axes(self): if self.canvas.afficher_axes: for axe in self.canvas.liste_axes: self._dessine_axe(axe, len(self.canvas.liste_axes)) if self.canvas.afficher_quadrillage: # quadrillage for quadrillage in self.canvas.quadrillages: self._quadriller(None, *quadrillage) elif self.canvas.afficher_axes: self._quadriller() # simples graduations # +---------------------+ # | Aides au dbogage | # +---------------------+ def _info_artiste(self, artiste): u"""Retourne des informations permettant ventuellement de reprer visuellement l'artiste.""" infos = {} infos['parent'] = getattr(getattr(artiste, '_cree_par', None), 'info', None) if isinstance(artiste, Text): infos['text'] = artiste.get_text() infos['color'] = artiste.get_color() infos['size'] = artiste.get_size() elif isinstance(artiste, Line2D): infos['xy'] = zip(artiste.get_xdata(), artiste.get_ydata()) if len(infos['xy']) > 1: infos['color'] = artiste.get_color() else: infos['edge-color'] = artiste.get_markeredgecolor() infos['face-color'] = artiste.get_markerfacecolor() elif isinstance(artiste, LineCollection): infos['color'] = artiste.get_color() elif isinstance(artiste, Polygon): infos['xy'] = artiste.get_verts() infos['edge-color'] = artiste.get_edgecolor() infos['face-color'] = artiste.get_edgecolor() infos_as_str = ', '.join([key + '=' + uu(val) for key, val in infos.iteritems()]) return artiste.__class__.__name__ + ' (' + str(id(artiste)) + '):\n' + infos_as_str def infos(self): u"Informations utiles pour le dbogage." print "---------------" print("+ Repere") for rubrique in self._artistes_repere: print " -> " + rubrique for artiste in self._artistes_repere[rubrique]: print ' * ' + self._info_artiste(artiste) print("+ Objet deplace ?") print getattr(self._dernier_objet_deplace, 'info', 'None') print "+ Objets fixes:" for artiste in self._objets_fixes: print ' * ' + self._info_artiste(artiste) print "+ Objets mobiles:" for artiste in self._objets_mobiles: print ' * ' + self._info_artiste(artiste) print "+ Autres artistes:" for rubrique in self._artistes_dessin: print " -> " + rubrique for artiste in self._artistes_dessin[rubrique]: if not is_in(artiste, self._objets_mobiles) \ and not is_in(artiste, self._objets_fixes)\ and not any(is_in(artiste, liste) for liste in self._artistes_repere.itervalues()): print ' * ' + self._info_artiste(artiste) print "---------------" # TESTS pour dbogage # TODO: avoir une image de rfrence pour comparer def _test(self): u"""Test 1. Barre rouge en travers reliant 2 croix (+).""" self.axes.viewLim.set_points(array([[0, 0], [1, 1]])) Axes.clear(self.canvas.axes) Axes.plot(self.canvas.axes, [0.1, 0.9], [0.1, 0.9], 'r+-') self.canvas.draw() def _test2(self): u"""Test 2. Barre bleue en travers. Un point vert et un rouge (croix +). Un point vert en forme de rond (plein). """ self.axes.viewLim.set_points(array([[0, 0], [1, 1]])) Axes.clear(self.canvas.axes) pt = self.point(.5, .5, couleur="g") pt2 = self.ligne([.4], [.5], couleur="g") pt2._marker = "+" pt3 = self.ligne([.4], [.6], couleur="r") pt3.set_marker("+") l = self.ligne([0.1, 0.9], [0.1, 0.9], couleur="b") self.canvas.axes.lines.extend([pt, l, pt2, pt3]) self.canvas.draw() def _test3(self): u"""Test les flches de matplotlib. Une flche verte, et une flche double rouge, croises. """ self.canvas.fenetre = 0, 1, 0, 1 #self.axes.viewLim.set_points(array([[0, 0], [1, 1]])) Axes.clear(self.canvas.axes) f1 = self.fleche((0.2, 0.2), (0.6, 0.6), double=False, color='g') f2 = FancyArrowPatch((0.6, 0.2), (0.2, 0.6), arrowstyle='<->', mutation_scale=25, color='r') #f2.set_figure(self.canvas.figure) #f2.set_axes(self.canvas.axes) #f2.set_clip_path(self.axes.patch) self.canvas.axes.add_artist(f2) #self.canvas.axes.add_patch(f2) #self.canvas.axes.collections.extend([f1]) self.axes.add_collection(f1) self.canvas.draw() class ConvertisseurBase(object): u"""Classe gnrique dont drivent tous les convertisseurs d'objets matplotlib.""" def convertir(self, obj, chiffres_significatifs = None): u"Appelle la mthode spcifique l'objet matplotlib." return getattr(self, obj.__class__.__name__)(obj) class ConvertisseurTikz(ConvertisseurBase): u"""Convertit un objet matplotlib en instruction Tikz. 'cs': nombre de chiffres significatifs afficher.""" def __init__(self, fenetre, resolution = 1000): xmin, xmax, ymin, ymax = fenetre self.nx = (xmax - xmin)/resolution# number of digits for x self.ny = (ymax - ymin)/resolution# number of digits for y def Text(self, t): x, y = t.get_position() x = self._xstr(x) y = self._ystr(y) txt = t.get_text() pos = self._position(t) if pos: pos += ',' chaine = self._couleur(t) chaine += '\\draw (%(x)s,%(y)s) node[%(pos)scolor=currentcol]{%(txt)s};' %locals() return chaine def Line2D(self, l): def _strc(x, y): x = self._xstr(x) y = self._ystr(y) return '(%(x)s,%(y)s)' %locals() chaine = self._couleur(l) ls = self._ls(l) if ls: ls += ',' chaine += '\\draw [%(col)s] plot [smooth] coordinates {' %locals() chaine += ' '.join(_strc(*couple) for couple in l.get_xydata()) return chaine + '};' def LineCollection(self, lc): _str = self._str chaine = '' return chaine def Polygon(self, p): _str = self._str chaine = '' return chaine def _couleur(self, obj): _colstr = self._colstr color = colorConverter.to_rgb(obj.get_color()) #TODO: conversion incorrecte. Se documenter sur la question. R = _colstr(color[0]) V = _colstr(color[1]) B = _colstr(color[2]) return "\\definecolor{currentcol}{rgb}{%(R)s,%(V)s,%(B)s};\n" %locals() _ha_tikz = {'left': 'left', 'right': 'right', 'center': ''} _va_tikz = {'top': 'above', 'bottom': 'below', 'center': ''} def _position(self, t): ha = self._ha_tikz[t.get_horizontalalignment()] va = self._va_tikz[t.get_verticalalignment()] return (va + ' ' + ha) if (va or ha) else '' def _lw(self, obj): lw = obj.get_linewidth() return "line width=%(lw)spt" %locals() _ls_tikz = {'--': (2, 2), '-.': (2, 2, 1, 2), ':': (1, 2)} def _ls(self, obj): ls = _ls_tikz.get(obj.get_linestyle(), None) if ls: on_off = '' for i, val in enumerate(ls): on_off += 'off' if i%2 else 'on' on_off += ' %(val)s pt ' %locals() return 'dash pattern=' + on_off return '' _mk_tikz = {'+': '+', '*': '$\\star$', '.': '$\\bullet$', 'o': '$\\circle$', 'x': '$\\mul$', '<': '$\\blacktriangleleft$', '>': '$\\blacktriangleright', '^': '$\\blacktriangle', 'v': '$\\blacktriangledown'} def _mk(self, obj): pass def _xstr(self, val): return self._str(val, self.nx) def _ystr(self, val): return self._str(val, self.ny) def _colstr(self, val): return self._str(val, 1) def _str(self, val, n): val = round(val, n) s = str(val) if s.endswith('.0'): s = s[:-2] return s wxgeometrie-0.133.2.orig/wxgeometrie/API/__init__.py0000644000175000017500000000172112014170666022441 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA wxgeometrie-0.133.2.orig/wxgeometrie/API/filtres.py0000644000175000017500000002767412014170666022371 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------####### # Filtres # ##--------------------------------------####### # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA __doc__ = u"""Gre l'import de fichiers de versions antrieures de WxGomtrie. terme, on devrait aussi pouvoir importer ou exporter des fichiers Gogbra par exemple. """ import re def filtre_versions_anterieures(fgeo): version = fgeo.version_interne() if fgeo.contenu.has_key("Affichage"): if fgeo.contenu["Affichage"]: parametres = fgeo.contenu["Affichage"][0] # 0.109 if version < [0, 109]: parametres["taille"][0] = parametres["taille"][0][:-1] + ', "|":8}' if fgeo.contenu.has_key("Figure"): figures = fgeo.contenu["Figure"] for i in xrange(len(figures)): # 0.106 if version < [0, 106]: figures[i] = figures[i].replace("'label': None, ", "'label': '', 'legende': 1, ") figures[i] = figures[i].replace("creer_feuille()", "") # 0.108 if version < [0, 108]: lignes = figures[i].split("\n") for j in range(len(lignes)): if not re.match(u"[A-Za-z_][A-Za-z_0-9]*[=](Point|Intersection|Glisseur|Projete|Barycentre|Milieu|Centre|Orthocentre)", lignes[j]): lignes[j] = lignes[j].replace(u"'legende': 1", u"'legende': 2") figures[i] = "\n".join(lignes) # 0.120 alpha 1 if version < [0, 120, -3, 1]: figures[i] = figures[i].replace("_prime", "_").replace("_seconde", "__").replace("_tierce", "___") figures[i] = figures[i].replace("set_fenetre(", "fenetre = (") figures[i] = figures[i].replace(" 'noms': {'point_final'", " '_noms_': {'extremite'") figures[i] = figures[i].replace(" 'noms': {'", " '_noms_': {'") def corrige1(match_obj): pt = match_obj.group("pt") dte = match_obj.group("dte") cer = match_obj.group("cer") deb = match_obj.group("deb") return deb + dte + "," + cer + "," + dte + ".point1 is " + pt figures[i] = re.sub("(?PIntersection_droite_cercle[(])(?P[a-zA-Z_]\w*)[,](?P[A-Za-z_]\w*)[,](?P[a-zA-Z_]\w*)", corrige1, figures[i]) figures[i] = figures[i].replace(".ordonnee()", ".ordonnee")\ .replace(".abscisse()", ".abscisse")\ .replace(".x()", ".x")\ .replace(".y()", ".y")\ .replace(".rayon()", ".rayon")\ .replace(".longueur()", ".longueur")\ .replace(".equation()", ".equation")\ .replace(".val()", ".val")\ .replace(".norme()", ".norme")\ .replace(".coordonnees()", ".coordonnees") def corrige2(match_obj): #Droite_vectorielle deb = match_obj.group("deb") vec = match_obj.group("vec") pt = match_obj.group("pt") return deb + pt + "," + vec figures[i] = re.sub("(?PDroite_vectorielle[(])(?P[a-zA-Z_]\w*)[,](?P[A-Za-z_]\w*)", corrige2, figures[i]) def corrige3(match_obj): #Mediatrice return match_obj.group().replace("objet1=", "point1=")\ .replace("objet2=", "point2=")\ .replace("objet1 =", "point1 =")\ .replace("objet2 =", "point2 =") figures[i] = re.sub("Mediatrice[(].*$", corrige3, figures[i]) def corrige4(match_obj): #Bissectrice return match_obj.group().replace("pt1==", "point1=")\ .replace("pt2=", "point2=")\ .replace("pt3=", "point3=")\ .replace("pt1 =", "pt1 =")\ .replace("pt2 =", "pt2 =")\ .replace("pt3 =", "p3 =") figures[i] = re.sub("Bissectrice[(].*$", corrige4, figures[i]) def corrige5(match_obj): #Barycentre s = match_obj.group().replace(" ", "") if "points=" in s: s = s.replace("points=", "'points':").replace("coeffs=", "'coeffs':") m = re.search("(?PBarycentre[()])(?P.*)(?P[,][*][*].*)", s) return m.group("deb") + "*zip({" + m.group("milieu") + "}['points'],{" + m.group("milieu") + "}['coeffs'])" + m.group("fin") else: m = re.search("(?PBarycentre[()])(?P.*)(?P[,][*][*].*)", s) return m.group("deb") + "*zip(" + m.group("milieu") + ")" + m.group("fin") figures[i] = re.sub("Barycentre[(].*", corrige5, figures[i]) # 0.120 beta 6 # t, x, y sont maintenant des noms rservs pour un usage futur. if version < [0, 120, -2, 6]: lignes = figures[i].split("\n") for num_ligne in xrange(len(lignes)): if len(lignes[num_ligne]) > 1 and lignes[num_ligne][0] in "txy" and lignes[num_ligne][1] in "=.": lignes[num_ligne] = "Objet_" + lignes[num_ligne] figures[i] = "\n".join(lignes) # 0.123.1 # Les noms Cercle et Cercle_rayon permutent. if version < [0, 123, 1]: figures[i] = figures[i].replace("Cercle_rayon(", "_Gtftqsff45ezytaezfehge(").replace("Cercle(", "Cercle_rayon(").replace("_Gtftqsff45ezytaezfehge(", "Cercle(") # version 0.124 # Nouveau codage des ' et autres " dans les noms d'objets # Le filtre n'est pas parfait, et ne rgle que les cas les plus courants if version < [0, 124]: def transformer(match_obj): chaine = match_obj.group(0) if chaine in ['Label_segment', 'Glisseur_arc_cercle', 'Objet_avec_coordonnees_modifiables', 'Secteur_angulaire', 'Label_angle', 'Widget', 'Perpendiculaire', 'Label_vecteur', 'Centre_cercle_inscrit', 'Texte_translation', 'Pentagone', 'Orthocentre', 'Demidroite', 'Cercle_equation', 'Cercle_points', 'Point_pondere', 'Variable', 'Point_homothetie', 'Extremite', 'Vecteur', 'Point_equidistant', 'Rotation', 'Centre_cercle_circonscrit', 'Point_final', 'Segment', 'Courbe_generique', 'Label_arc_cercle', 'Glisseur_ligne_generique', 'Courbe', 'Projete_arc_cercle', 'Tangente', 'Triangle_equilateral', 'Disque', 'Label_point', 'Somme_vecteurs', 'Label_generique', 'Cube', 'Label_demidroite', 'Vecteur_libre', 'Angle_libre', 'Ligne_generique', 'Cote', 'Glisseur_segment', 'Projete_droite', 'Glisseur_droite', 'Centre_polygone_generique', 'Sommet_polyedre', 'Demicercle', 'Heptagone', 'Quadrilatere', 'Label_polygone', 'Sommet_triangle_isocele', 'Sommet_rectangle', 'Homothetie', 'Parallelogramme', 'Transformation_generique', 'Cercle_rayon', 'Point_reflexion', 'Polygone_regulier_centre', 'Point_rotation', 'Point_droite', 'Interpolation_cubique', 'Interpolation_lineaire', 'Hexagone', 'Sommet', 'Objet_numerique', 'Centre_gravite', 'Glisseur_vecteur', 'Intersection_generique', 'PointTangence', 'Mediatrice', 'Rectangle', 'Objet', 'Bissectrice', 'Barycentre', 'Angle_oriente', 'Point_translation', 'Tetraedre', 'Projete_generique', 'Vecteur_generique', 'Representant', 'Glisseur_demidroite', 'Arete', 'Texte_transformation_generique', 'Objet_avec_valeur', 'Texte_homothetie', 'Label_cercle', 'Translation', 'Triangle_isocele_rectangle', 'Cercle_generique', 'Triangle_equilateral_centre', 'Cercle', 'Centre', 'Angle_generique', 'Octogone', 'Point_generique', 'Objet_avec_equation', 'Polygone', 'Interpolation_generique', 'Angle', 'Label_droite', 'Texte_generique', 'Milieu', 'Losange', 'Projete_segment', 'Polygone_generique', 'Glisseur_generique', 'Arc_points', 'Cercle_diametre', 'Triangle', 'Droite_vectorielle', 'Intersection_droite_cercle', 'Glisseur_cercle', 'Polyedre_generique', 'Triangle_isocele', 'Arc_oriente', 'Projete_demidroite', 'Projete_cercle', 'Parallele', 'Texte_rotation', 'Polygone_regulier', 'Droite_equation', 'Droite_generique', 'Arc_generique', 'Sommet_triangle_rectangle', 'Objet_avec_coordonnees', 'Fonction', 'Arc_cercle', 'PrevisualisationPolygone', 'Carre_centre', 'Angle_vectoriel', 'Carre', 'Interpolation_quadratique', 'Intersection_droites', 'Triangle_rectangle', 'Intersection_cercles', 'Texte', 'Texte_reflexion', 'Reflexion', 'Point', 'Droite', 'Sommet_cube', 'Symetrie_centrale', 'Vecteur_unitaire', 'creer_feuille', 'rafraichir_feuille']: return chaine return chaine.replace("_", "_prime") figures[i] = re.sub("""(?轹$7 hQ\y<ro5vf/sͳsC"31)s' :rmQ`՞[7~0}ԉ{%x%/^m{x%sB^Dܓt[}:`i쎱}GUxovkJ7LY‘M.K<襁4 *KO/ e_H'H,hF#E4TgD5 ׇ0cFtiam32"f$ήlO.*]SnXLJCEc?F zl&n&q;C܌88ɷC`YJ.mǑ!:޿ah 6y~ӸQ~ZoGC+NqK _N8B$h-C) ,8zTST!VN"]ɔ5i ́k /!A嫉[w51AA=!]8wpO5/E4ڷn`R73iɝD]&߈\B YΜ#R㵍ٵ5;oBҾ|ZT|Ytq|턺O3p$#ɎI Iaq 6f5BйphmzA&}SꖾwBǔyug2t Koo%" BH pmt*|QTN!(MPhD0XK,rO vWRRUb ▁c # ۲(W[h(@&-@/'*3_t_y/RU7wXFnYt5ǡ{NEAtS^9؏ThE!noN9ČcW7YHW3#]݄tIꗭs6pe#H0X';!0s⮆Gb9eegvdL<upZ Y%(oxvJNy%yAf7?q29H(~_`"p$AhE%]D M(Ɓ2e)`m"^naҗU8 W3!4X`-ɤN]:@I&ttH1^Qj;4HTb\a`Ž;86߲'u4ytT%:\ TmCE leT9s(-#K6=6n"=mU șrn۔ŋ444\|QȸcXQl{4VdȊF@'A Fj3=aa#j8Coo/yi,MЯ?7}oc))=6z i49quߤ8C1z{{ ~> 4%ڎFgjϪc1x H$r@.FGa#q{5%wa~ě[`G{zz2,hQ׽;I.^ wܹJ{.2|k"imb:_Yl ԟu^ 3/e>hvIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/logo.png0000644000175000017500000034076312014170666022646 0ustar georgeskgeorgeskPNG  IHDRn4 sBIT|dtEXtSoftwarewww.inkscape.org< IDATx]wE}磊4ZB B@(HB@@HQRUTP@J @ %~w9g w,ywwΙUDDn붏ytM8ꨣ{tjl>z#"5 ӧOg?i{mٸ#D=Vv n붏~~SL6l{k_~F{G^ɽZMt[ +lC=z⬽oc=ŋWbn붏{ܾcǮXҗ}9s&~󟯤ެ= ݶ\6w\3oF  &ά@;pFn[ ݶ\vW_ƃ>]w/*kiދn[zL4 > ݇뮻s#Uݺ''mlذa &GY=Zumڴi5jTדּ%\\ףJ)p 8WD7?-\.ƌ$IVvwhfe{"Sެe+6f6/W 8ƍJ+_0`ƌO}S?~nOT{gTg߾}W '@WH}[;\z+ޕitS^rnϞ=i„ tuQSSJnb :_:utt.9{ ڲ=.]m:Q}ƍ[gmc=n!vNJ?騣>nf2T,o .ӻ[s;mmm4~CɓW魖F+f7{?q =h :3sϥ;XX?rݻw郞>xYu跸khХ^J>?Gy3fyG>`F:ꨣH){ojll,3$'|s);l;c뵦*?iذatҴiӖロPCC\:&Zo}Z/~1w744+蠻>G஻mߌڽ袋>t[ vwN{} _'{쑻‰p4iR):oiiN0pxbѣ+&PsssW+nGuԇrF^ZzN?/~:^~MrW,WV}#z?:ꨣW^pnii>!ꫯ.w]k-[8q"M0&L@rqtwВ%K}n1ٮꪨ·~8#q5WivqE:zIӔrotWwMwy']tE{Fk[T*?Odذa4s.o;T*c=֥:14`~]GaC _ڇG}xZ+M7ݔ?|jooXnΜ9[<3t%\Bz+,W^yh`޽餓NٳgCoi}?d6mZĘmR\:thUIcѢE]irڴi<v>/z*ܭ96;tyoq&k_:j.QYуsϭnvGkryΜ9v`ۏz表̬Y3L2~_u?mmm }o9W… iP$ ?QÇ;n8o[=,<#8"*wVF/εwWWO=TTOX3ln"^{(| oN8KWZӲe˺TGaR)wn6oU7x^4qWD /?_xo|u ~U?&ݖ(n_|q6x` O<&Je1bDz=H^ì#"b-Fdjc---4f̘Q:)[%;<]]=53}]\> ƹcewimFF]M7ݔ.N; @vԩU_25cEv߫W/vmi+9FO>$/n+J5\SOv j }-ZTSs̡={J)я~TQmݖ=ºjiC#G?Qva=\mRt޼ytqt~Dm};ߩ/W%M1br<|&pmذawZ. N:養O}**׿vu\W_}u!ەW^IDDk?lذ(D%oUO [偛'臺:=7MՇy厭 /}|WKgΜ. ʎ>.eի=3UۺKmYlh~:*[f n&mg'sTc9&*[m7o޼~g"IEa^M2YiҤIt7 i+)S-&?;&kϙ3:WJ:ê\s+%СC ٳ#v$ ⋅}K:>r˖-]v٥vvԔ믿^ӱQaT:~FSisT_A{„ Dm p-آU²q;sH&lRJc}k˾9v5˟vihaХ}a 3gҹE|_XwQ=X*ϕ6mZ-dT9眊}#Ic]v)mUQҥKӟta 8~x]͟??{UNn+W-\V+^dI.£=3;m.e]s€ %-2w7ɕknnα{VJ; j":\oVzz6l͟??W&?p\?Szn7:BVr;3&~9 ':KD)h_TYʹzc7xº#裏^Vmn"F6nXhw1bkBuYӾQ:0okVxf>E լz)N:vyg5jmt'By䑨$IN 566;*i_|q;HE~…4hР\aّ?Ϫܐ!Ch…ra[$nT ?%ܰtM5og?Q3<3 /]wݵp/3&xy睚XtA?_X{͕ƸŲm{ケ2EKVZ ^,whgClqQo%+Z 3Έ̢X<*۷oߊ_;찊K~VB]xKk]qRN*R\rccc.g̙ nidZ[[iذaQ2q倶cR0]{=XU~kQ[f%,X@suݻ0I(M\dvŚi 6~Zge˖EOZX6zElٲߟmfbC'OM}+_vzg jkti{\nUPnÇf%ƘnVu߯? g6&NX3gFsoyUsE6lsʶ3swi.;C"˺Tź/iVX_l'|2z;WG[maF}}}aHVv)jן*eˣ?w}7瞅Yd',jqM:&IڶGuf n]{{;M8b]rHǨ[lQumkkСCs---K$tBjX6g?Y}kkk1ݴ+f-+l5K0YX)ʧ+xhֳgϊj7ho)lZ~Vk&ʳcB@ՠ$gP~ӟ^>?_)n? okg[g$]bΜ93fg?[]wݵ \lg;bYF6W&|oW%۷ܹs>}B4kS___qzE{]Ve˖E{+UaYXe}Y-7M2vxMQYkkkŃ~+_q/X 4h͙3'λᄏnW^y%fZw|Wߟ((#J#GŋRmw9m;PM+؝={vt=W5n[~_F,4nܸL>}4_"C5\S=\QjNʾtEC:G)B]v]}tgӄ *?q/Q܋/BΒ$Meר=zt}vXa[ى3r6t>Xocn 3x\yY]O,+1-*_Sؒ%K"]X~8W&Mg{'*9ó>X&N\#?jԨQUe_}V4")KFNJb?ϻ/\6nW5黲mn" 6aĨQ"&]"VkcZ( V_s-1{k5k6.>}P='?IUhmm1c9&Wnر>('N\ 0/t\vOȑ#^>v۪ٕ b5_ Xe}+GmnF5Y*$e94uԪ<Ngk7Q>9\!z駣}߿~Ȑ!9ɤ!ŋ+};ߩyų /0:uRmmm5Cvcr-Uo~\V.i FFZt4kv3C'?7V$>|xե|-ZӬRU#IJc׿"˺CF֝wyA^{m C=KqU5 ڢaR17x#cf^v lEW2Rd vA$3,]4joU__s"?~|a?n[lʔ)=`$lhQc=9k֬(V|РAbNԧO\Dkkk{-ǎKW\qM>ݕMӔ^}U:s*-ګ6۬frXJ&WZ%+tGM#G̍C_ (&"zXÇScc#crﱬz]?ySfemvء⛿C+>O.lME駟F ZY\ho}/|H9k,z'?#ooA7QT&VJ~뭷X{GD6r! p ?T_t}7A&ЫZr[%TeCP3:~ 6jRͲΖ=hmm&KRՄ\mќ1;5֕7z뭝:J g|j>VёO<1ڷ[V#{ ~+k/ [2e =L>`,߿7cCi*[Ji|PLnj\n;WN)Egy&-[DmԒZ궽'jnjjn9siƌZ>( I qZjjnu[s/4aҚ;V޲[o=[_j蠧z> ]I&uydwI{.J%Zm+H&a믟K-m4-jUWQކS͊&Ls3jlSKKKrJrY:5eʹdY3-YDK6Q&9b[o -\.ZB xmo}:/yo%o$mG;SŴsG4{h۝GTvРA4m;4w"-nvZn.ۢKsN'?kdi-]LK5[Bwute>|8M.߻U5nhmnpE /&NIziӦ@Y+Z9/… ^m%c޴tR([uk[e-m4cƻZk7!5.iK٭q-Z.^J /~.69wtmw~4c&-8n95gf^@S_vݭrOwOf4s|9{{Q۳.@Oޚ[~ނ4b眞b$ <[HF{,%̗-kޟIv;+_9җhĉ^jjjvjoﰿkL5Y]mncLB0 IDATŷv뭷V Y_lѢEm#v1l|HӧOփ4cG@bezᅗ*Ȏia =/hy Ӽ^7Sv?fͣmn^?yd~Xh!1vƸQ__O:)ӎ83qun]:m7zao?\m`o{zxveSlIu/76 xN:?ߎwaysϲ $k?]{W.c7h?Uu]N8}w_yH$  vO_W?1ڷ;gz-܂c9nߖ[n. x8@8pu)aĦA)#cI$q'7~|Ejnk>~}-P|۝K/F54 _ӠJA>>c;8p('yw{~MMMsq"qJ)W|V>۠8g%gJ+=mh te_@mm5ޛ?`,Xӛ?.䒊߿hjϾx zj!<381{l 7?~lo+~i|KG`ܹ5/9? ]zv{]Xtian6_⡇A UA9'(q ,kS_ŲeKѷo?fi\~>v!r B ./qa!Z& m;y(aG;.T\Wfm5=k,3 ,}7uT=?~<~b?^z)8hߴi0r\Yc<>x /rxA[<fϞo}dEveW᏷GNڈ܆*c <,`oS_ESSL:`-Ĉ#j*hCշ)F;1FØFeFiSUh q{<9`wTmI֐C ey®:p"|7.7q8CsJ|ɝ/cwDsss+rJ}ikkg};^}Uy,X M8I_{8h0|!=?O?UR }y٨}g8YFe)1rr@/9BvG=;>?&K< J|\GİI X$ 1cvt lݳq3xZ0i$xѾ >`MOĉsO9\q]ҥKq;ą^?>bR Dvꗿ]x)/zg[o> ^x W^6tm8@ .㔴 9'>{ '9X>* 0z6k >kFm<'˫ZZK g!סdp$ %*Ѻ=sWQnjZK.رc;Do&y5qꩧ.9ͺ+f yV7bf6#$+^#`Y!t8fבUqLz]ˍXsЦv 0e{fz2f䏓+}2Y>ˮ}uxl3i;]ҙYx7|[x~у{r]v=۷fa]G¬ÈGM8)L}Db !z{'uL$b0 YmF}#^>2 eFpYC9RG("mLpHPo*.Ki؋Tmtk݁a?ओNBcc#N VR8#||`;] &ԳlĞgva3Hi lXgIN9cZ)( vPV}V2n%KGw}:x g}W-+!ϒ=&B"د=h{;þ).'iȸ2!vlϾ"Ed%,zm>$aI8+*SD-\_c|W#g!dٻ˂;HZi(vTش;ǀ}C c#2 2RQ32ąw(qY`gt *lB%,@2rD1dB5!&*c} kIKD׭C?N`JFm{fvM3J N2bHv _<+8A&JCf҂da?DP\9  8mB4!X H2{돶B/lm̶kFVnmȌ]P"tBpغ;Fduu :S +wZ7p9mc1\2jp;tW})my w[V ;rdꕿ 2&I]dhGf sAY9^+LW dxޛs|uxiE@k2;=8 RĒYx?ʳs!΀i#rr262l@s@, =TeǓwbdgRiI?*oI.rWD0J$i"; a 2)+cDT\HbiQ7eɝ3HyD3e"'8Ʌ> Yw >xK3F)<0xpn2h`մk `/? eb##n %$NZi݌{X([t@;ˢBPI.~_n349 e 9`)opD wOc1|UnlKMR:rB* f{&<"E7a`X%|aAD`J&P0IL8#; 1q 8wg*~F)*Ӿmo!MɌ;ԐUJ ),8Ve [BIz0Q   `y2~+k8/@aP$C`2T``'v"yV`Rw;/E!1x.ǚɕ keBEecܱk L9Ixwmy,x"t,b11V1@c#[/]*mf\=PC! ^@^p|LH!{vLWr#RpvӉ딺4IlItDvD}⿼um9Z:=GIU%|Oذh.YcҦ8 `m'3c -tݺV4A8`c 4"~g[10Lr\2dlܲʮ_58H16kgl!(ZahF43qnrn5Em_UZg@1p`^.xQ@;`%¦Up M!~Mk3[m:xڵ>^.ߊl:dD!έkc9HPM|Ek%c2J&``RL=^ǀSϬ@~ᙨP0QJUܸ8%!@\]`mÊJA+G sg.i&C[J,߱`>SœIEU[ 0ZHxnniqLq(@LV pNS; 寓-8P {oug[L𻭲uw pzr,sֳ!{6퇲x@*tOceٷ&> 5{XHk56B5] cR^6F^Z,ȸF@#Ppuk=@sdHCQ(DPa #-y#{S+e"r4/:qpJvHH>ѡ# F*ypsQƻwU*c!Vi^N7H&(SͰ_h4 $xi$5X1KZAByEt4M4AbVuK5Zó06\1yn*v8}D>䜀~fᄤo5>D[.4 D65ݝOl~ ,#{ 8a xIVȱ"muw+XE ;#a8fD&`%lώ9K_IX|•b,2kEzah>bzP%_AGF( 8 -i;Dr-<G#I`Qʝhd<# Wߞ]jrAQY@:`ʕsT}YwN:cpg@:tI{\P/c/ʸ Eeo9W0&YbgWsQ.} q.6a\ɗÐ2㫭c`lB b=2W|XlHPd i:gܽ$+- AM nd%fr}#F쓓4Gh W$\rJ{H\Ng?7?k־͸s+18vk:Clh 7m۞ea,7NAJx&8c]4 k9WOTpŐy>((5 Nnۛrgq;wVJ#%FRrZƁI]t u--6W$iz']}Y%/M,!tK|(Q (oWD צԙXugxHݺ?ښ6VwhX3u`ʟCM[50PfCbN²cpƓ13) tg#@Je"F-8MC!l_TmPI_( p:$6x)cC>):@ }8hpdKV.X-[̗2U,܃ Bb'15 ъR|]5\H Ȃ4 ˫5iVm0ז; >:"sb*rE)\5)ĬSq4/KS/M(ǛuBBGøVw4"I/8v'x:M4VO*2sYa45 y$LK%JEI+4Lj8:n%H 7,0Xi@]`jmxCKW)_"-D+N+ 4H.c:坂M9F.QjjRhhSB,ȥPmRp]YdB,;(@1'ы!(7hHFP;) Pɳa0y( )W =+|JF~nH1~=Àl 70`wRQH'kTuV'PAԵC✀/Cq:L ìI~*I lKF0=V>>8pu.v`^_kV\.XȾ7@& DpIC dd'1 3Z G}ƉO2Af䨰zDk/鋕!$OI+ $K te&n!`P 𵍴e>(\˹%`3Zt -jBVIN5[:u=n _A1ٺH$Ɩ(5 P*½'/`EΉq(5鐄ͥH5r+!x55@̡vZN]xL 'Bz5M)\n\gWR(xG"ZRpč\~%ԺI;Q T,Τ4f* ) k:]ߣ ;Rpe /%>: H宯Ri v@|s?Db֊H[ď>Cr)5_C{Pώ5jYszs>\c_dِa*y_0$B IDAT7#бRg@1}(h+hHIX3݄Q,e) h1CvfIN5{'\G1u[2 4v27V†yD۾/0HtYư0 @;4'C O)m'c LIkJvT!?sYd}E`Qo.Y!WIX ɔMn^lX&/-ɬfȽ+SP2_=tywd{--UvaeHtEMc]{t{LX|7چA"pmAY-1}>.l(J#44ЧO/ieho@RRhIS*g]g 3|Icae\$%KɃ(`LFi2ou6HmTMzzʏx,c8rB)dSNF0L"#+ӲaFi(<*;T()ɶ#2)qLʎ.XYECD$aE ^Sp}$p!wozZi(u[$>Ǐsn{=[if5 A̕VsrW.W/+7JYaA,ۂe}8$OO"hLz'PcFAȃW @%P_ MFlAցR@{[;-Ys0{<`Y3:R(| *^\*ux&xdp,eg\ٴs`I/v"ԪTCKX ӹJUVT9v,HkVT^CYەet@.ێBDP GRӊJX]Ư$r}yF"7HPIgڸnʓŷN@/f1`ltV5Ϡ6ektDiَFڵRI7ѢNax}.ȭP 1H:Gb-`[t(FXDfLc-+!hh Qo"I4 R~Bڑqq#ޙޞ>[ )A Bk7̞!NMF*r ZjJ"Hehy21  CWo ̘Ǔ Dd}KA-g?}S Ȁn: ɳ\*%%\P+NI?wFw2N":) 1$4p3JQg $/Hwaw>(+d4x= :с|P.T7/ޜ4: T56Ry$$M(ˁY׉0EcK "3Lgj`@8set+u)Y Q1U  'vJ uuhh(hQ,iGDV N# ʕ"TT&  `:4P/a t*tq?(& -:k(s2d aGI)nӲ;-S -u2) =`A?VmW:3. ;dP/A x2;XP, {$9Vl `$QKP_ѿol޺Xwӧꐖh\/M/]$aߔcR آ1( %ުWv?-S;I+bDtDNn, idAMx[ȍv! LTR( -12?>/вph>s ȳl ϬZw!h`jB/̐ T :c$ ȃdǚA N$eleNI3SJ /1JH;fkoByC(%0i&-[i%R_J77٧ER탦ѳwOvk +P$X$,H붰rij)˰z ؁8o`$DcO8ȉgtIK,k,"7(u62ll( icS2I X[n  l$'s> {_hmmHI:#{°J$"J:/! U.I&R4lrR Vy #s |R0C+;9ƍӽ +,cכe4N ;][CuV9EdYry睷;˃6VEhÁnQ9&KQ9˚8R*6.'dv% <~mkaryr8V$L #1VW7ojYN 6` Z P _qiDnfC 6}zヌЖZ֕(p]:ou{Skc5@~[p(3EŝiV וjܲ n`z"Bxle)%dJZG]c~l.weYQ z@VxݷP dbB@keœV˘ GZW^KVpBu*%őq'ΩsL(ׂ?ŔC`u} A8£u>|[{b܅Ik 7 "^ËXs)DDFeoOp ,CB_q  7a v2&F y/I9pL7Ж+R4Euݫm(ܚ0MӓaO4 ;Ŧxxבh $I=Z;f8#D04H;}UKh5GJEY" 3Im5= ZCם'r" eR~,J/Rh($B 4pF0j 8 hik0 RjZ{IT]Q%9!K|bp&^z5eɎUuɹoHä6IH~QIy WҀ1"HdJF.\-c|uaޫ p .["$IFd}o1[ uוgn&0#  $1<>LMkٌ@r`E&(EIȔ (P00i(~{G [oZڡ (2E6Mxm ˀEk ۮd#FOkv!{NSld_l lߔkŴDx>IPJJ(iu ׳.ĔP&`i@Cz& vt"N['6B$UvbXav`ɅV'u0v$da8l!&sE mc&2&G!]7.i؅)K7X$^:#(Rc cmf?`3) F}$J!-22P 8A9eRz0{BJCmm<в>h+|f^x_ba c@e(a{z ~$GW֧qI_@D :4F} X |=P.#)Cwd (:T]K FMv:,f܊y$cz~R6 2w8]Fa)@Ű ~Z 9뎸0?ͮ#| g`-Q&n W+xF{n!-]HPF03q )Cda>L) Q{ q@+M]a~aVN TɂDېK!f%f)ճH)B1#3\uRNmfĉ9uuu7n,{ $H`0)\8hb+_k :%V3m>ʆy6kAƃT D^&-,9_&(yTxLUrf'THˢ :.k(A*\&b0/H,\pJ%&$ Ke^DЧ__XotRrrg( H>ٽ] iʲ#[ND%X0 a!4`٤\J')K6V))HF.agz(Ja:u9fYk܆%褄`4٥d<Չ"/$~YISOhhe%kp YXEJ<`R Ⱊs9Ujm @{}Y1Jo+0k./: xsMXlU Ax 's#\)zW9w@,l CUEI)u8Qq'-,+Ifce!8>}Mb'JG=ҴZ IS:U ƔجJ hc]v{Fk9E]rrj124\_8ޱV@#F\*)hXv*P!Wv6c99H%n)hkJvҺD1r&pe`N)2Ljcԙ"I[{ 퀡-˩"%e^ (ˆ2,1LJv]^`SeSb%a'/ ;2Y?UU?Z D,+q$O&IUc)xiLHf-܆׏Cr" ]\ {|v^.q{-`xgx;g!\dwyTi[6xR\&7dʞ-`,[B"$ (JAGB z.!5P0()ꔂh&BYyD%$IgVBJee5"M|p`u('6j@{9ICBJe. !p浴*~v*Ȕ0r2B.ىRɊy:Ρlڑ%qЈc $ u1bC H6uÜfS%$dGN(Ô`nc;aB:6ԳF3\XVZti8hQ3:Q64ʾ Iɺ4"NfY"Ev$2CP|V< 咮R($ޛ=[g~?̳ܥnm] z$@HH6#y?jdI1Dp8@9zpȼA kTչ%/>=$Sug#צ~ MJ !uՃ"%E<3zZP./??J;+d[w{ olRوu wm}8dsWYɲ>K3YhoAIѸVJ&P% [[LNoZ_da;bcZͪZ#R Pfщj+jE)Hٹ{)B`]: ま:jFm\'A ?c^]efi 9DDHcA8l9>:f!pq~+TH%B 4:t"G\](#1՞dE- Ӂ؞lwOwu.(C' }4N|/>}~,H|k-Quo=wߦ-ChIzVF'(BdYwX&T߁7&g`To/^ @K~W5*>`m>)%d6'Mv|nYpǶ&Bufh63k8lU2[@>wƞD'?9C_\ IDATjѼʍlQ;.½{+}7xƤ kw?g~3z-azrHBu$_?3;'?/)uOg|799=,fdlu8v" ')ˋsqB%s2!I-9 ևZVbGm#xuE~~ wQޚPE}#4WP  BX^J3ʘlLtj%@ CVIQ/mO}65G38]3(.m`b[g,cMfݜ$ta˲ U.Oi"}~ʉR ]4nZ*`;I]-($dvlU;U R)XXA+H%}9IE;%Bݦ몞42u!=:w軍- )&|V:2]T(]`5oxpv**U Ct]sv'?_ ; ͣB$Hh]{ \j1 vM07,VO*B u;~Yݐl ?/G#={j(Ϊ +B;}xhz^d2gHQ=ZfC~O?Vmר&͜"@% Iby:zx)=)cez?-3ݮeiv.#œ nQh%JQ̩F3Ej}d5­F T.92vhXߵC@{ޭ-S4"d77&]z_-*%)b<ѴHs.+>Y;|R :ǺO/w* eP/3uRb "YϷF1gRPhI.9XEZ%zmhaќ$YB%gBL?TMOcĔζDžΉPŘyJ$UXΝ݃k[Y()?xu}R0=&O;jcWie5xbR`%_oLӇ.Q+3~TxS0nx3WSJ3Rux9yeMeuZ4kB4@+.qS:RZW:e538[Ԭúcg/X e4;$ߞۃ:IU-($*aK~᯸HbnGY =}[7q|2 Ld "gRiē@@cg S6 ^]_%t  %R@Yi|J׫V t]wQ/3̚훱~f5ls}Ph,GX/2G,YJ6:o+EPI6 9 J]S,#WbGpjz]ek1S!n j^obZjVSi8}̷=+X*ۂNDW*x2u[}Wm 6hdw-~omu]4vB \v3pVOY+DZK1Lj׈ӻƴb1u=[!R} ; WכiDJ#AV-C5s$HF}RWF.بzoI)7xo}}~Clja7{OyYRQ`+jT hԂ0C5/_ vdq ŷX-1R Njo`2ffcY4!!2^mkAX'LV(&$[$C7ԁgG<0SZaP{wx-\ha^b)1z>^r\v G;ǯOΎ ي5h,GiBd =HTbO> 8"$@ Ȁ20@GwL+,4[:m Vq71Y/=Ֆ@%D >JqZJ^d55Bʈe'EAxSV5dZA=.>k`Mɇ3S]̎`$Z4H9xm&ױ}_Y ZEgL?||u5'Q8wu'IVY{=-\~f.`<r WeXK}_- ~D;g-pvA*Qv1%sɥ@o"V Gk<}|>7?=SYoV|' Lhm~nVDM믙iJtrsh]G?gՉާ^P4ڂ#`^[sSe Ȝ#[A"P17lX /!돻_;}K\q&r1v7[w Z=-JԲ=VJ!k d^~^֭_V2ĭu.Y/tY-[S=].ށ{ٽQR. m@n8w[nI0<,V׻^N+ WV+3_bQJV%z;(=@3ԀVڡW,yۣ#ώ89;aX)3M#3%+](k`lL$z[m :vΉg=>?Cwz;o?a@Q5gK(ƲW! aAPgȑOq# L9M)Y(zYьkZ`Գƽc?q xtzfS &2ig'룊#*x9= e_5PZ:aT%½vsu1[#@XsWUf+&@GA0:MoMP3r'f~.UսR[p߫M,xYW6ZWWӻuH* vE @ϭlM!_J,7wԪ. bAgEiX'ૼx b0 ~x@tzUXVȺ#5B0z^1C%&< UK2m;Kչ(qy[ӏxou'h9MHrsqy"xMV۪sRe3%GV>/>fM %E'켋P;ϵ4-FT/P_W2 YuA =OhȞc5&Y*C,aZݻ{*^*֤qbc%Jp 66%/ _ݝ @vP1D(ЬDg+4iKZKlwS\jsfw]2y(ͼe-}B:Ů8^׍6֭ȬC)s fR`Bi('k~)+k֫-[{Cϟ嗌΢ 3IVh ɣ"d>fVCw'yN&s,XUQW-fTK4k e!m($'Og?;B&ϬRޮXWgmqteXA ).MkS[gL=q$7L~ό܋#~!?.y_+eaճgruqOwcy޺21ΘK5ٷxIeXwW\4o;@Ilmw.LB6}W1K5v˧}p܂j1t-aäs/RRPն]Uӳ><1P2! ۣJgie-HWMze&+נt݊iwMXEFDzaf{@UmtcLR{KLV@섔3~]&7^v:_ ח^HP,3ƺQii1v~w l65U:jk^}17a?ڽ((&„npv; '00e"<}ִ4DVUxᪧO$HF$[I -bSjϯPA5꼧e~SuA ͆톗_u J@X1P80lZR@B-e?0MVVh+U N/xp\b{Ef&/ӗ[g,bgA4!uc>y *-}p#kf+5]ν\ZDML oוﮤ`fMf^n?Â\& *U 2SYh2M V u5˄~oI3˝TccvϚi[@_ 4>K<|xSOgޙ=D 0ΪMi΂f w1BsT8ڜS>W^xJIirOmRU y"v-sMdߝx^ʙ%Ԓ0{‚mmҪ/UMb)P\HՆmuD֮ יoQ?ZHzꚜM54OqfřR E *,f~ O,*tRj^/XAN" A3㙙 8 sfs(jVLWlF7b\KOp>Ћj]ZVRےdjŷuYXhF)l{1A-kxf)$,խww@Ͽ&0(=%%מ~!#@ڽAA2]Ύ׼.F_˿׿@ (!Z^ɅjMK9%tɳ);pW3ϞMJW/xpy,cºмBh &A֫Ϊ J$F1 u< 5Z`c:JIô|D{H߯9~X7hV+-3Uyh ېJ^t2?{Q#7W76BY.nhc.S;%ӭgxVa?r" \ڮ:Hl.g_ h՝JR/7[0CMR&M:Z@K5mw"TuQbiuuh|O:#—YֲH"d_|IÆ5үvg\]ǿa~#2i`8f;Vkv)OB1BagߪywP-\^/q),9ץ8<|Uձ@'jS%3VjjWg3@ Z&[Kyp"w\]]" 1xG'6H PJ:2xM[:{]N XO:$ vMgB jى#W9DJkaϸ {~׶U]oOݕEӼ;ksys`Lȸ۳ޱg~?qq}@g̝1)m2 >;ZXAZq&Vrw?XwTRy-$,w\7U,dהjFd~}ɼ8(BҶ]w,vP51/6,* e]RwƸ¦C[xsI̴%3EY|⤬ H8,ٚ[PK}\lٖT6Uy TR0KB%* IDAT4k"T3hh,12Q"~f&7n݊?o _|ruuM:n+7#aXӍ(HZ54Y{zϘ! &YV1+T}%Dsb.{=B:'vp~)>P(i$E_/ qC$1H JbbGׯYz.$ʄꄔHdM.%-. Z\bʅ1Od˗!%K XC"vDq^aSS;[,,BP ,Ѷ Tk!ds' s+u.rk-] D)sAei jM.-Œ}UԤ bFm ME'u-9j4HUu|e=K^leݿSணvș V.&}-֔KY|t̕4hg'P#&Yz-i)ZeUbęjߘSv~ԅ~A׊yk2զWny7\BKi7+tX1 ],,`.b@b{\\r8uAK1bWDw__P=+ bis%#:"zII OP6وv߃4ЇK9ST=~p͎n 1r||lkv.iA*$S[KО.PÒZZ4i"LJ&Tz[|3 t޹1@9L1RK6lw-bIc4]+,:FXY'1L= ]L^@zO-JK8 ѥlܓ{s`]DaH|A#Z@V ֎#:I|za}W |W^juHqowXybqK]{Bzh`p^\:)\jt<Z }XaH~ڮX4YUP\2A35Ǜ5]}d% 1]߳=ru婚Xڳ,ďЙ6zc@9[{gyu^xjEH ntI-k,4ߐ4{[ C_6GG|k.3S6&i_A/>D d{1+:V+6ۭu29ڄaFわc3@mBQ4[2eWWLED욻kcۉ7χS\sB"ݣ"rӏ”͕QD[B|tdOlARlnDj,rec7ˎaq-^2:B<V1ŮE4 /ѥRB[Њŭj).b[IE+uY~g 3Wv4mZW}o ̟k4}j.ts,.ӑAA 5&db7{J&vUYaL P9HevfƤlb0讍 Q(YY8(a|4!@2 zz4W8ztBV <lrWKBKm q: r7t.9dMkLIx4ހ^Xz=ֵF ~D9 Gh}`կڡR3%´A>|z*DCaÊuޣVd@;przЯ|&aQ욢OU`As?mSl(Վ!AX35L]gdo-->1 -h!YerK!{W$x((Ӂ#ru}GOVl6\ukJnKJA]׵2A t*#vcǧDQA9>=fe $q4@ڽ P(#}c's֫Uwx9mXo߲d,U[ԒGEwt7~K5}<&+>xJX s= Nth4yQ{ mku' TkF΂iQ ]*fUQ]gF{iV.UH9ڏa,bO".1j=)!^BDPeUg9,s6$1vםn|B%Cb kgٸb%Q Xic&Jl]ZF0ҹg*~]?_+p/m96۞ka/퀥N1[B5Tj <+;3H3NNV $8+qj cw'IK~<~ 9#~-V)ZW΁:aXRu茒q5:%WL#Jx5>W=xJ+s})ͭ%=KG΂J )Q #k6D^?S0G'Fz6LD)h\)2nb7$~02z#m=&+`Gߙ_m!= _J=|~/ [>p(1|@+$?B,pYgVkZ1د(XERF⩱V1?$7WRBKbVX 5;`Ĥ5p2Hkg p[ctj#:6}X\&Ak2bb?Pb= }ZQ+%YUj_BK{adeAaFNVN6hzgĥ3:IU7K^ UK($cK,O57WHy].V8ЧY#a%UD&̟ѱZt#E',})wMFgt1z(@VÁxX ;7]XfJٓ\w{$n=XXy:Za}AG!l7. V$QdtC5}"9%_VͯHƸ2j(PvCH&tm<n<͋scf%7s?_Ŏ4@o2LKlvÔIcjO05bM|]X׺,4%,-5`dgrAu4yQ0 3j⎏/&2mͱ%bQE++f>]BdbsVz$桶o<E-uON\^g*n%TjRZ@oROpiW/ y k~^j*F8D%w@~7,TEo}2ֻ21[Y[޳=9"pt|jཻ.#9bf`؋&)oW'WSK^"D(-K../nЬh&kS@ FXF?a)qݻ߱9x8#WD){$l8Zs d񆾙tqAwv1KU+o]lrNdG`$0Hy%]҅}P$pz6`4]u؋O:@'({Oӷָ@A—~N>\EaԪ GO| n| !'ĎyRv(zS>/(eqy*oA9 q֌ 6kt4EȚvWl !Hפq5r$hB]BvNyR`KQ)SanȺFKZ^Z)qH@Ĕ3ۣYH:/>BC)ÉЂE+5S~[CL rR]~5 ݷoėFYVhy(i`h<ն-6hQz,j^mG㋥.ۘ ^iP$xeE();!5PY]evR7H`.Skѱ%q0ق$9YHQKu姕[&u"{R#i7Pʞ۠@+؊:kKf]s ْ -x)ZϹO^my/_>4`IHa^23|>b@{crvVQ$DJrw,:~g\ X^OT Vuutb-@' QX ӞOjBa\}vX꟢grx8>gurgtjOwvpgXu+V!ͱ2qCg:^[2Qk81D5)wqq'h11340^qwz$= 6<|"KƛLP-)y|K E$ `Ѡ 2oתbYc'Y"!v q5.C6{38۵sn؝Ƽn5{Bu B.%mz7$5vvv{_}w>/U%CAf \6.ޛ/Ђ"qm|"qJTr62¹n,dE1ّ5WiY24S\ejG; ._ IDAT4j+ U'Re{jG-˳%(+Yw=Dj_p̎ц_ŧGzNs21Z pG6.Ki\F`*ļ4d\Mإ7'>?gsevxXPqttOˊn>v8 )Z1A>uPPMO;^So8?BD{kK$"VxN.&SJ ]'s);U[)YuDu|)`=: REY_;M_'H7д3G9.<]jb)5,\Ԥ$ h k§þ%Сr =rFxFX’: 1GR"vB̯V.Ͼn sS.gcRڣf- FnG_78.dNP7C{B~~*''5nwk'HH9=dyׄ1'88>\rsrr{?O>Ԍ*;|`ǖnݺ$/ i'"{|X"{|X . Z&^cu9OZ~G?c^?ׇ'TY,@&"u$u\1Xs 5x@kcho: s3D_d6I6iW¼q޺rg07JIIf<6|.>LLΗwcm}Y=TD\>S| 쾂K( 3lW$We/@z&|reөI~6(:7h;g)L:@1)UxssѕPnݾC5\\wV}W5@:gRe|M@ sܿ!?鯸8ߚ|Vțg8'5'8Wa1֔ phKSWZؓ:> CVRK\+՞+)BdN{rkg(1^#BsVY4,ZRx攕bBD?vdI} q C DSό)޵h2}߁4T dGrҒ_ ]zɭ%pUML x3.nAO 9#SpKmd'SdQ7P4HE΋9 +OU{8<\spd^0g{qq~Ç9Uj9k=Hθ!,2( hTP"qB^V(\X;(6' Q M ) &Mxub Hx:HdkIʗ6Wmyb@8/RcB.ㆀ hxW3LSah|#7LYPy.]׳\RJsheHHL׊%7ZCcWU6GG )u JUUh"j,u==Uhr8$,Q) : 8*n*3I/ʤfS ֚QMc f]U/hSq'~⪀5KH BeAL3WcytAbK1b~@{fC~iE.5^*kޝ"PDI~w#Q,VǬ7_m#'׮Ӵ!HE✬(E36똡hdc+ϷM4'Ah6cJHi,$xghcD d]xl EߤO=<l4|{}_zL4l:WϿP*:K8)^ٳ3> ¢bbݻ|#f?Pڦ1m 2[EƑ] Бl]}F۬ϯ|G=_RK>}m|ްp5"b:')͉̩kUCq+RrVᓕ*{.q, hum7ﳌh-[5:!%O@әmiȂ0!=CcCڜq{j#Oxs餢WV2KtiQQʷKLkZ .'iyX>g]g4K~m'G$Lf>V<}_=o҈v} of0L˺xdG3g3 W\ٛ|GtԄP5nP`I;{)I~NsDTJ-w_J8C :psAPKmϩ\-7ja'8߁Ơ먫{ bgG[t" -q,.I<"iIvԤ[E^!"*oLt;}=vЦ脨;i򮪤 WD%!5s.:R P*,r9cu#߿7qrbfF$aߺٖ|+7wܸN,Ov6֨ r'P-Y橱F];C Y#SgM2Rƨp JCFyۣ9XPiW'C GbP "Xlf<2kږ=g΋g>HT3:`S/p||NawIL,iS!1qs~_};gFAp57nUgh]=HHyjnFw1; >>:b|2$$uMY|蟓[VPj #Ljp\4!QffR#1%T_G$Ii͚[AmOLi ΢ I&e: juˇiAs3sB3"IEZО$=)f 2"=]h%VoneD겾yDU- {Hx79$QϏf |%K>>{NbM"Kjt%:LۺEI9СL+`*9G$!3ܺ{̽WrMV989DQ*Fˣk)\h I_νG-Uģ1RfȜ#phR1Z GE$Ɯ= f\l:9׸&MG{èKRbK>J33K^x)Ý [chG 3ž5O{U#^7nG]%:5@%M+8}Ikn޸G^! $5}IT!{zdfU+0eU&͑%hj̖y<*0'{ H}c=8yo^GmAzTLom+.I>WAy:AJRkՄv-H0n66ݞ^@H9C:@ ?ɤ\d>kĉ @@3 0iF3Ԉ_sx혽s#bo3 TkP߁1WGJbMF*)$[OwuaYI/ńwb6.3iR׊ )J14|Q~N1D'z1`[7}&wp||HUL pđrScT8׳;}= 1~)6O1L<FyL"}sT0k6(k v9.|e<B{ KfH2]<겶v2=cqfTUR$aBG:& c8`{oaN ԟzdrJM ޞ:/&C ţD]\M$O_'Xr8}n. ù?ek[H}P)UM۔0fo,=%SX5s jiȌ}"<8f**59 WDlÏ8}Q+VMk)]M8k4<(L ccؼrXv"ۆ*)^1Szx:s0c=2_0V4>1}z ޔҋT+so#)~o& %ύg|'4 )uM"8DG%T+'aRfU!gS2o֚hݑ 13 6;4.ɜ6Og٣lYNЮKn{p4h6!!n]. JmhD ru#9 eO9D=قsyqYږ멂U!>$L$-k7TEcb[>>9ti1`?$G;J{ɐTUdk'=^}sZB."N4hx/رn^n}?Drم #J I-3ܛ+:! H t~5̳A9M ס*4ZK()xIV}SHxM{L.t "+p/}b|[/kbo$\+ 'WrAX\O\p%ww:r~ v#;:]\6J^%BnE&B-fkh1Z{F=|}MTf>C|rf+ճK_;#=@LGM_K9L(t^*+qNkOUY^>|U$RMZ׼T[b2D{kuO]KC|bϦHh(MdfH6 Y*Y(yJޢK~'yz%Hy>!iM 1喔ͳ<;۝8{ll 뿋;]ϓW3S2橁އs1z3eȔ%m,ЙKO"YiLBhv.{ޕ8$E>3ꛯ 4A ˔|Ƀ2ys:4NT tY~|a@L ɮ%3"O&*B߁B= ^U'7c4g&*Ԡ1!0sEj\$H:fbOڮH<ů%u"O]\EDkq:"UwCd~(K3ݗJ9xٔdMë+E<&,RiSzm *kcPhH4}b~6G1j4YHYׁ1Wܹsnr5V"wA0뺌Wצj׾oĥc4mKvl)wDMVf;`}@J $Ur%,xW+wqUׁ ԛ#!%</@h^$D墶dMv%O]"W?='=)Yך_/I@JtTI)8b3x?k ]u[1C2C9g9z;`8ijVf;^URvL r@ A+Ò41DX0h=lbb23fcJc6M*"lbW>P}4,M} '!ЊfCdqG{hR. 9Å;2| f:0/ףb 5+ÌD5sk8H`F&Dn̜o?}s5mɅsLJl@BP ؓ0UV3^yps *xRq .WZ(G-yM!xe1LTX:S"au/tU(d̒ ]wȐZn3 +(Yڢ,2eY]4=RrSdԣR5Y#y} 2Ƿ2W0멱2PŘ*/ }s-2%\KiXR60c㸝x}C<5JjѣONI }nKڞ ?>Õyes8z+Tf*NIM`U3nE𛼄LQB\ddl'?b-jWC〠ܒ;&]ZߡK+b~/vbD vH(.Ըz#ڃB QQlf&K-l}c0 IDATAL8";=_}|WovI⭬IxW  :qZa9e[orIO:Ŕ{!X@" :8/0zqX]װlq>puML!KڦC 1ZJ9 Cµ@*[Jd4¤ca1H`ٛa\緦MMگ.RO¼x}+9p~0tM`A/D,<-_ +%QJ|M򕐓q&A89ƅCbCc@^LIoi=zś?cP+m۬և=9}v 5x:+#Tm !0 k$Y'd;jifw)F磵9Ed(%F0$P`3$ bv'disE=MH fϾݙCtVyJ.h$ XBZ^a@i~/R9 7?@wPYxҰ ^KSa,~y Z]Yx=H u}!]^_O6tmt"f6i1vSsu|{wo2=U"'d㯓uwVgelto8,+9]Kv%}\^Q_w"Lŕ t4[h賀B" :t`u PiZͽ#EA1(Sؠʒ^ ݦ9@^lP?"3q'#0I䀡EW^zACLfqcfE*2 y>D)gÏ>wo2?\N]Ղ3^e8TUoyvf#>7Tޮz"mڑ:$!Pe<\TI*]466QMZ)tG0dΙՁzn[`I*ToS~1ks[? )9q@21/eP%;0CDM! nݠ]jWMg'g5ѳw.'%qhf6fb)Wp{e>*Ph^r}N:]y2?Zᔁ;9e}cf]"Bps|#dܹ}嬢tx 4H1+bb+giF/lģ8Ż,Mg9JKWFb?p) j˜%aEgOŻidT6%{/em圕+no+| &v/)Ff;+k4yCbS;8f`PG8eK[2/FHEQb9qyV@z ɞ2AJb6n]'cAõA X{_˯|?~?#$cRlBT*Ԭl.Nm/i=+If I) g<ּ"*1fra,ذXG3eTeie-xqx99mTQY=gХ~>fIb޺ Rh<B\?7y{LI2q sfHh|VnН\!Şw?NZ*9}z>!]aQ$wQ)p$&i(N slʵhn2V83Js¼dQ4ͪ"IlvZ_6,iRQJ71۟VéAǷ0ܣ ȕI]WpO~N k.DW;4+F̰zk1 BEt#{ v8{g[NO6[_ ~)Oli[ BM0Aל`'q>K[b_4\/y)ͅySu4BjI}KpS<ތA"Ajd0U̗tS4趧 5%kƘRKq79y}?-]_QG U|qu <LCdєH81&T< .H6'Dw>)1BΛHX-5N 'ky-^!#Ek"uRd;|Uz@J[]q$wGgϿ6jb[l.:ěRo &O#8m=: Q2ǻ$,KqUP\W ejyJD5Rfc2Y&OC&E|U!}q~[%'./?rZN@1`'%{Nu`#ۜ & q@Ȳ~t[H0txG ̑jIlHd@NDcg<>kfdN/ޤSc5t_8GwvФN-+sDgA`)6co 3.lRvs3j}x~{>"[]SK,d;7%1mog*1BO@qFrTͲ&6 B)`-s` k8PH$dJ6Uk ?A\\ %AnRl5KO 39441g=ċg?0C&1Dbse+IN r e(ҏWAj9,D/cV^i{ ◿A%7,OHBRܟ:`Zgz6g\X+mx _#,_Ʃ V@\ݙT=YȪwڡ,ojpsيt'TRYU$3vJeFUIzDlt0suؼ2Rrn:Rgu]qGqfH RH ]CZbLix͝w_Ï,W DRqj_&L+OGxgL#H[v~wv{zcG~_~J w_l6c{ZKxk0(rC汯 &ܒ9KI&L%XNlXX|QeP赿H-4Dx<]f3:N2g cֺ`#1O7ɗj1ɹ P wy %yZ IAE5#ƣMTt{i5ڮʯ{[~oϵ <4+j:9T_ޜ0ڍ{l.Ooqc; \]&Ӡv39plZqSk W/,#s_\IaϫkZ[$,̏q՜`}m4zC _skKKv'-ywy滯w~[5CC_-in8{sjɚ4Ohr46qgoX2A.FA^,0V/<Ô_ 2!J7+q &*26FL>YEiXlEf W2j9y:~~U#*_^}.Ti"Ŷ97_{Άxe2JdHj$vE23ޥݼѧ2j5&|%$]jȽѠvM'|-ze2/zW;o5^'rA`4eЬ㭒q.sR@K4/,Ch!U㖖aHp5jVWwIЂ'x8gY%iCmYR WmsT;Rۜ/~W_> 6 -iņj8<9dywƛooy8ђMMY `ΨT!@2)4Rl$R&@-:OԩZGm 5Z=N]!k-Uڀ_U`UC $ u5ܭ{y!*`!5XC++9αFiӦ=XH!z^k*刔)VAIK0L.9Y/C^<xptJ 60̋J IDAT6kьL[8Q$h l6^]su&ѡʃfG] HyDj.kN#j"EclG@/Hݙ9<±BJIX6mn J@3 6U[KdZ}xi{fuȫfT'Ȧb0׉'?ų//&#u͆:o6||xx7IBT!L88Zm͋͜ö|];RR0q4kZt߃?xH*W~r2T--&[1p{#H$Dx!6Tag/N+9b’.T/LEChuºAo]<x"m虳װMֶёwn|߷3k9ͻ(,[ٰ.~fT\!7GUYz޺(fMm9ݢXN.8EW<%:v+8|R)yjaj+'| ~tgU\epW|AT,YD!t/4kis=.n2; %tl9R,a" ɋ%D 7y]%Ȥ.;Xwḧ́Ztr|=lոEh+nAz&dR v5J]\{O?fɛ{tN8}o<[o#rw,j~`6b13n!&C1.̊ }4Qde^L)@:5gJ-IIt'n9olzm۬(L 6;M׉0I¨=-c7f n."ݞtsVKܘgg!,X- /ZEUAxX~_x~_'fVbݘv[d`9銹Ko`/SA 9&h2b)ֶ]jB=9АٖVr)TLKv~ /_.'H0 \-+ѯpO)jhl 0SiDm))nhT u!0!֊ W ydv2 -u=ͷ+* I~\%YPP4L茡W"jr~%ۢ Z1ei1| }{l{[<~1g6ó9;rG5cd ֘`-KN[}-|UjH=QJ&[gsoĉ_/?ҥt$n}( h7qvvf6J-(H)fdl]2)W0%AY!`C)o燘,FP!r0. lݤh"W-S a^zYr3xt/9ݾ{ɘ+5{~`a{3deR=冦yZZZ(~ֺ[ZK2OtXߨd5p y;CܥdtKG9H!"\Gvn'C~w!lX D@13ZFpf5T)n8nT$(y3^=NE"ր#N^簻z0aZZ'BR"U"asհ^pÍT^SKLjD'DX͂ߵNFG=Ԏvbs!.s[N<;Ơ QYnj|\hAH.bT`1 O?~98~=R/曈qq=h:عB`Ӹh\&oꐐlzyGlx*@(!SͨbS ey/>wMo{_wO?j98ko!]ynWNpyV [[+2K d^<ax[#ڌ&J&Eib#֞}sΗHC*Ӈ 7R>pƎXX^6'AX'Ȇ@>-P B&$1Hd.)ӕ u)  B ֑2<#)Җ*=X9>{ YLFR$:NGlU7n״U5 ]g6Ȱ5r`esm_΀ȣ֦_r~ӣ3\ S:c<ǣ~u87w?ǿC6ϟ|&M=|HHuXO5F1%/S^op8xl'v;HH|q)<zCU!IPdFͤ yŧcDD]#?a9e:{bU*XQ I. aJEDzn{ئxEȅČgE UAMld尫X8\&[ךʁq e45iaC.JJSDc.IM=ABE/^r1S75;Z+C]p17hsf_)\':$R꒟ٟ'|'?꼰'9GGd_;}y8>$/wzħ_G׼}a{rJl@ bS}4_[_]0G>Oqsh'K v;wZ:TL kJlJ=tO(jm3ݓivn7JretVz9pj*&w0M3zSoio{WS)tՕCpy}}F'2_PElf$ECZ<r) 5[{rQO8Rj6qyX+P!0N٦g#7#W%Rܱ;oN=Ih:=t8p~UkVk2EJL‹q1GM.(qCs3H1NA6q>n0!TD'Н`HK 5faWCMX(:tO<\F/ڵ93ϩ7O͈( 55߲ !d8PǽOhjH4#d~æt!uiw 3I8TeROuSw/[۞qD0d`@K0wh(l"l~xqhxtW?G٘!QwX NO99yn4#م5Zn ʩ$FD䒽Hj8FM>{'⌓9O=no1R4̜k͙o˟nݹs_7ܳ^g쯳rWyUgFtA[Cj3:_3 s'z;Z*`̝ jpTʙb6hT)mYY7Sk8xE)%5Bysہ |?G\Lȇkc|9!2; a{ 蕞*zX glZ(:0Ƃb"=Z,B 657\Tw&bUk:[D+(Zoa RPޞY)f 7kMfvF !rh6)q9 NEz7xx,H-|w"i91'jBЉ2J) どpѧ?cΟ_q;:#rޣKϳ'L__\ᅦG!WO/ (u9}xW_r~Ԛ5 a{x;m=R.q|Vѝǒܰi[cw-+P5Xkm\%-ʻ՝YC׉ ~ʟQ;eIJOM=G1R[:G?=k>'h\nrY.t<8 /{?8C99U}ZL0JL- oE$̜P.u=,b6 as.TΟwº*,deS^ AAKib5 b-y!x/f Bd#=n?O~œ/xw>g<&v7!r(BeߢO * .u VK؅ XGx(?8TZ:l-B JM'0L|v4˗ARGJBw~paw4\ 29G{hi@O [3s;`MFl-Z_Cs^p ! կ+Q}U]Ttnc>Gp}yšDlRE)LIX骘GyņkUmv]`kG%eZ TIh8~ϞsN?5 6n$kudJ>nQъи@&"f {4W%9g{=a]SZ-v덯_ѯ ݭooпឳŤuzg~^'=24#{a֝Y7[O,}BWiM }df3T$Νc]&D- iM5 -Xe횤ER (cX+=?9?{n^> Mn؜t`w!tLbaL&ΟBsz2YrCܵTKJEMN*S-Dq:hHtnR )ՍMGaÌ BHDܳѐ 9#՝MJ.;+~&6NX6; .ܘbDb̈u#g`y>kȋ2틗|OOnGrQ.儫1v[ ^ HZHm3;m,s;1QTHռP+) c.hJm)fe*)&R\`%!PkZ$h̜ BJjujPZ-wbfZBBkY'ZJdwF0 IDATζ;ݹ3\nC߹彅12zku~w.(o?F ;uõ`r|{#1nfPmFѲBP3]lŖ(]BE"%OLo63} ٤Jeakɠ< '?g?o=ǿj_>34B<ޢ- }Rtl 7iGM`1@Zz @)!&_;jӸELܓ5 T!C#rQ켤ZD zxI}J?#.5-HQH4Nٜ܇3W8*7]<4^A1"_.3{e5ZRK%U::p77!\\OJvC:OH"A5`"Tgw ~oƢ"}crGl'#ǂ dҼѺ%mjZ)Dŵ,)M1qe"V(^h kE!2xt7}B3{{mXۿmC1!_+.C8\PD In`6l{I@كAKSp|,QSDŽkݔ.yt+=chtF _x ]HAp·gDIAMs*r6IfGȟ|ӟpT07C7Wǜ* Ӵ'vs$GS@TRF <,7ӼP JN9IԐ'no1` ' 'fj^|z#A >$( sõQOާ79?g 4KgOzwo?1H5{T *] Z2Ĝӧ͏t\:Pu`k9t ^quxh5)V[^[j{·BOuCJv-^ꊵE.hTTv|ψhLK9yѹZ*1ť`9_g7gWy:g\lb^]$ݲw*ڒ"~gqc+e6]4krkJY=ܖn:j~ ;P  xHf:R>--6 Rm"Ge^⨢TȊfU U-lKa_s3Pj/9;SN1{n/ι0!BlNz9xCf, PdT&B͊{![.S9!Cdu:"uPrLݿd<\q]0׌ӭqS5U((8=:!lꉀF͡f YjDIW7Ow5>nwU%(e [ 7|S>GOyrK ٍ+ 5f)n512^ +Is0BG;?mK6GVsmVOχHLJ" n(jO+j}xOu 7X7$h)3xͤT2ELB,Y~w44]2'#<2("fZ7MխIg];[/zf=-S-BjphtF׊VDKk՞[} Pcýy4p\Ic"#ҜbXNkSk2Q%Csn9.q !RFG1&oN9{%O|K.[c-/?g_3N\m6eT͚fX1@J,mP>],D10R5S٢J4ۮ{p>g01yxA)#S3LXmVnsW+DVUf$bDRU[#wjlLq:ڄ먴40@p)WOϟ-$qџr[ 9HGz*ٍ7!*۵2q21׹) ]ocUcI'Z>lVmNî:Ā!H_vČ`u CBUjb1n/wqT̬h@KA{Mߴ\bJډ[*,QsW1k'Akz'CAZb 8[l!ZV[ا@ \xʍM H4FI[TߤJBmāO2ٰrkSDD f$fPʞ @6O6G2e`Hbᴨ2&?7 \ps#gx{\䗿i7uFAK#vڡiݨ5LN ly* $56k%@\ OL嚢\Wђ,rV "G//쫵l8y$O#p _sG<rm\m""I0Vfb[Afsۧs,hmk?!R50Z K#< 34עҔncs\A›N]נ75+C3w!f.>⮱+7yf~i_ZYm }oWGbV_Aw?^՚a~ro[–}p`)`:!(!]2` ޶Dq&B\ .;31!d8o(br~8??p8po /c=.vg"|ek5F3%;Mb[F()xz[cјOB@ u^$P4#D4gÍubcҰBjubBIfu n\}BtsAbo:U7;gY,%)52Ái8'q'\'tuZ"T-}S *&ͯEGI:HR k-\ZSZ@3Ҋ#-m{nLJ@Gu2W^[îin2PaxuGGf(>'5wG虜Z<|ueYh</WypDǷ}TIn`VY.Aڟk^eοL^,(X2{o^DIXRv,11EC)O܈B.Bƍ7E3JLTgT9PUC~_SG )9#,T,(al $W!ۗ WGiAwsg5K_jC  W4*Q;Ag:j޵:A/R3*杓%ژ7p2#u!kKYNV )VD#JbQA߆-.!dR 6 UR|mTXrxq˗E'褄w1ݧK“쇁 6=q!KkT_Dj(i42P'r2u(.J6&X7x%ĩo&foѥw@Kv8dE6pzF%@dG6ԘѢ$)=5T4*Gg.2@ j&8I7/gq"ӇQz>-DA62b$N:H`ӓw OJNvC][Hj^fNuRv !P@7Zj KnT7 a.7Z]Hbr1GT)-1saV3g~=YfFln}qw0}t A ^THcz?o*nnvllUSg7YbZg<]bi;6yw?q`F׵s_qV|TCtF1E7΅E|0w1-j"]ԙ1';pEk֢#Ág_|7E4;# 8ћ0+߭;%Ek 5 Î<t/N $+ZJ17 HI11Dېى6B̈́1DKCzDūFFa. JŊִZlT8r2ia_sj{ki->1 iv޿֒~7*NyNl7mN{gwZAVxmԅk7Om/܉ţА!q2tXm7P>!zY,mH =N;421B ]8=e*U dDz)w9L\_=g}\khL#M ?)نoIVո#Ndj"jHn.]? / 7tu1 0b]"6Bjt׶т g5V@Bk6cEZpNwt\iG W - $9 (dcM:KDǴb$[!4KF7ՎjCAWHs&a>v_G<s߾!dQAZYgYF4٣A+pLSowmc3ʜnU?Z؄h>DnR6-2GA #ʆ0쏡-%ېnTKV*Dzj=+L^qfΠ6P]F(YIɻNMw΋}B+厫s]^!7L!>vv1pPa2 ]67X*rC-%|HPV3iif$H3h<;}Ţb|!Id@8eXHhrn̩|54/ɇ*^(DYXIk^,Z5;7 \x;okrL gIb[b׬mbTf]z;L>Y?:/Ջ1ۈ#P V=Oup1cqPxU]#\́)Ư6]4U”3A3IO~Oaؤ1q|"]l6-)o]_9잡zA%ng[HinV0Gq t%JS>OwrFwoü8MvA^ Fu iMET2Q-%F u-DAcLpIns|&RiXZ:Kb`mPˈ8L*"+@r|\)D? 7W;^<9g50V8ކʤ1I+cZPK/E\ZLuclx!iC "S^+H$粪9XDj6h"z7j -"^1LV1zwPE !4DIktunĎ)7=֙DسV{if ѭn BYq۱5[W]3߮8ٌ#uxռݠ[D6C!vA[:7ay?ӪOixZkk,^S};ҮV`zҪeE4Sy*qN#cv82o\0M#'=GX )o6!.(t4S؆rdwɻ9ьN#>9>SΨ6|gCnPe;"<"jJ>dL¤FPS15<'n [oQ˄ɾqWhJ3PeF5XÄmݓ~pcx~57 h {M^MtFmD &JuGeX7o*Վ~b)Pq抈N3\;  1^[Z˲zDw,ʛn@_^ ; K^8jbi ͸.C3YFHUe"!27(,[fN4c zA[WlWR_7.J`'=W>93 oM:~#YuƋZņ|f;q Cx.؊I-6WՋ*Vt1i6K\BEB4F65I:͖x-ΎQOReT3}gM?U+3Z,GZ`r.xS!PZmE;B-Z)v^\֯ӟM'wW:g&V1MnFA5jLH)4w#1SiAW%R*:U(\ G=vW;ǚ7Q[k=.3y3s 3)>K þp{pg}pDz YfE$!rLp88Rj!6Pz̓EkM *%0-Q|-5t}NEqEA%Bd2U,\gt>o,beԚ现c4]Vs@i\  ֪|EV=!33E[fidm׃ƻ *_{cŮB#fRB ey#v`JD۫7O_qzQZ&* nigQs : `f^+żxY*_d];!UD%O]Pm ?rm% YĀl6Ęɖ'd&m"3w5Ϟqj&& # 5ZQ)=$Yb IDATv[bZ{gأ!%RI}IS W@9䐠8-32]qRdW(ˋrع{Dm,¶- 5FRHT j2W_O7g*:)+HHkM-MѲkp&Cׂ2 Ovw,OVخȲj t֗\ U6V`Zm\$ 'ER 躎%nEI]$DE 9\ޚNZ?G< vCkL)Nmx19cƛb&kL)4a1zB]?gY _|v7 /\^xUն/b=l 6U+yxzxdQC@Hf! ǣalicFjm;BH;J{*;!&$&Ֆh{՘lz:>0ϷY)UXmVkB" "J,E*)X#V.Nd#]j8t݊R ,fUgŎg :itu$Lf{k@gi:FNsRLs3B@5O NRd pY6 օ̝e YUKϳXN "So4㚸^B[CH`Ou !ׅH:b['כKǘ'G!ڐ:Vx$.&dDA"K2f+Mz_m(y!%kc-vMD ^%Kg- Ӟ7F-u\[2gDOqjD d ,8fPs.Xl5$5Ĥ}/%ZvN:@;H`QO\h{~b;)+虑..'ێHݩZZn k׍Xs T6g=o~J^"G q^MKXRe~]aToUg.زǔOhnon(=}PD:rILy JQLR0'拘}ʡ4E%s11Wqӟo3H"i._Ek˭pn"buPD(e|N|oc~?f xҊ:/! ]+j+B ʜ!R=n^ λDaز9$D unBY=BW_|=~ǧGhDwK]QK k', ࢞O#Xg 2N_[xǿc.^]!IlW Tŋvc-% :q?t X-vKM،4M'4et:*))n;c1[BBد'>y0`h1Y;0l\H,iZ*A!IBpvvAF-H:#WLSaG@sHE 5ǎ)t ƎpF<>`-Ӧp6KuGVxkjd;BaASEbhzOq,RFh5(vy,N{7o]-~ʼI|3/d^No`X1ae}o[߼#a8="0M{B[K ̓ˁzifйd˸J@ERaB,:g Ϗyyϟşsɧ5:bB5CHXe6yHdjÁܼ=YVly";L-,S ߆vc~w~h#,EJN!U/? d7&,xCs1~mm΂Tex|7GƧaٯ!_ mdToGNsP|m/WeXOg?Ç잟y~qx2.yG`f官IجAMew3s>-۫kfiE{@ԇ5WJqwLe`2""F=2pd=x󁇛52h1V-Ѳ`B&d"E퀚T[_)UA<%*D1tKIbT*)$;v%OH ~o͊Mlpy!X_Y1LBН^Y`Y85T=:.~mQlYTus6~XPWu Pl@%.i<3<մxhNfk`\*Od{͒Ta! Ɔ[#Ŏ)ҚqtSk><ﲨ4b O-beknJ?*3Kc''/Ou mWyHBr_?#B'TT#1xU~Nx9¶]Th˕5Z jP迆hsrQbZsvݱ$ys<[>[JJAYaю+TdaU>! bpr}}_|'Db!JgVy[Go8#2QNjAXx\kUx<|OwHXu(}3۟Z"]Lj=& 9dfCVlBE1 㞚'4L b!Ŏp&>s@o/qM&QH-FID9*b%t7LfC{]@M+7DJ.>+L fEl(טv}r#7pQs`5 Ų𢨪횢> f -%q4;xk!=pp^[1 >?p7rKdO\,?A  Z, 0C$3_~Ǧy9q{DAU^vΗ"i.Ikx ޶ h;JcM&=O;'$WR5הle%o.B --!YٷhXa;pv\I5Tfw~߼9#}efXL~=?p|~Bs!2銴ZHH6-G@U"JsvJDH& 'RZѭ/HgW\~}s랯[}=SůO=YqrB@Zw(hؐ $K U|+ 2 ʪ%S<=yx! 9A΃cL %3M٦]P "hi"m"{ɀƯ wxYtrlZVZ:MCFyNHc̊4OֺnAX2m=wc2ȡex/,6, K #?93B>׆Q Ѝ' o51pnCh%c3Nv!^%o \9_-G}s  {f$36MPqa1sۆ6$jsH:s6=xw<l[0 |;7n?F*Ch\T'$8Lgaoǧ'0]CRFQYYf2_*cUVێZ=ƶK $鈡CCv)]4-4E6 |O_#ןXwh!WcA&47]$D!y̨*vUș3 Sķ~lϮyuM!2 rq‰a kmo-2=_F34Ju-6Lxn[^HL~c,}Ѫ~ߪwD]y[fn݊lK-u4{ 3{vx{r5Uh% {sq͸-ecdgnewb]7[`V omWdaf=%LLS&'p??I+? BmsHÇ*d-{:7za(&#w<߱qqX'4XWVw DkS ЋR/vnBɾy$l7Va'qbҊJw.V p4_#Za_K~ss֫n{`Qm @Ѷ0T: K #g#LS!<ҴL5ǂYO{HJߙC5-ZT\22cDk6"-*7'߾z Id1+JMvmԳ7'VV=ΆuXO/?R!Tvc6qY^:"4i겳 ޵(88(sHH[]v_;^F6YK%ui~`n-\ !p`^0N.x^X,uFZkrӺga@ p2`F>KpyS]H{stj4Ak?7 <BRu0'-gg+^f}bcLe]GY*"EX"IF͛RhL/HT.i`I}vVE23<(X]QGDWO/5w !쏓uo*GzR2%#h." dو㍫g:vFc6'HtavXivvE=g[2S =4hG8qk}sYB|W)ボĻ<cH Ҿr>_eaq % S^^nXIۦcμ8ݒؖ;x@-07Oykү z[|u 'A@jY<=a̰ߑ@L"L2TujP;jW%Bɕ<*9WI#`HJrzriE!},8eR|4}d4(r~AK[y";Hu}gP8'v MY(B:\I7PR#Zsubʅͺ#wVH 0 Jq23PW>#BAy8cW^4k|x|+_FP u\ 1nj}7ov{A:2 H|lOnݬ^kUZ[*`hSA9J9Dw>GÝc3I3b2M 1lH)z{ǶZg7,#&Ĵ xȣ44V\wkp,{-PӅm{쓔ﶉ8h,ѴoN~/HV%ͬ}3*SJfUvynyp% 7ZQCrOqd.U(-7︿e<H¤1gF4V@U&eGрfa򬑎Ke*JdfWB2ݔdB" ÞawZ1{1 I=8"ƂMǁ\&)[Ctɳ`rAr<10JZAeǧE+],`ϏܾJdzTd_Oj_:J'MnqݭI'rY;'|JX^];3$ʠ!:Y+Xſe^qvvή9{tvAXoА@l`ɑ;?h (7<~ w<0L41a4N 8P';R EKa7 #= blK^%`)V)|gn ѝ@eYJ{aw9 L%Z (xZKp'H(F*Z469ĉf9t;Dw}e?})^)u[8?2h^QOjZUCH~ wӞ1Րhyzf#UJiִXY\f{!u:_䊫`?9\*d\Jr^㲂t[B0m&"2y1떋vSS+h2 Jd" {7w\\9`s~v>$,Z9q)uMn[~G?gvwIo,o#Yh4sgR`55לot+”Z*U|拟o{{]ב MQ:KkģM & f0rSf ALk7v Fƪe`RYBkd#bQՋP4UQf[|)V̵7JNq--PO?;a-,eA?M[ X HsG6&~4zͼ"One .:; j-liyjű=Ebp@ц 7?,d-V,TaXdՂg ԂTGiuq;U q̌ϏO. )%>Q<`8pCBZ;wyoETB+ bUV\Jy񉋋K.|%|l 9(HjϓQ k>ɟ$[ LHZbQf"-poܮBBet \XZxdKPvA=<@cT;R(v߄hf'an:ŠYUS?]JvʼnA >&dv@L 7f~”OwbX~gf߃/{ƛ/\~f@|ª%оcfN16it#0nh+`7ת춒}.. K\ROm]K:v}Ƣ &ݘ%ܪrd{m 1 :*}D%`'*+c. eb(&QƢh hRET(BTIՆD{c*YK*^|aB`nqjdu__ə^*{#]0SH ggcyqrh``}(4McM]wPX(d:rN.EsoLw#}bO_\gk4#U'H=6SB]s~Ox/2dTGXt>=eR/nMz3...8b=3R2x8H@B9{կy{}ڒR4G(IV$v>Kͨ&  ֿ &,Zd6Y#FB9" ^ଷI 0v`ݰb,0'.`{SGiqQf_䥏=d1>~)b_=#Ʉ&AʉlBt;y >ͨƗUFhR 2#ChMV+BybEq"ǮVb+I4Z40Jjs1bPP  (Bī I!^g4FRݱiR3#ԉ uUjfY~f89 wNeaԲ4/B;.S_dRm|*1Z,V@3ϚЍZ,b҄®GnnyoyLw2IF!电PJڸ`(~u'M-=fW^s~~f}f-Pꄐz$L}W_w7ՊaqI%5DbLb硪x`hfhRޘ &&L<֊Ox7?P5@QMck3'w.O̺1p-ȼ5:0N%8o [?pY|*x[m0T׺'"S٭LvBrj hii.-8h ?Շ(S4жPsSƎCTTy4 S(LԃPRB@R&kN U &|S®b5*RGVe܃)L"jl{5gaiP|1uIlQUkf7{ n+ ,z^i7mk| ,ʷ,u_#u-GW 8y;㞟?`ꚴYúc*9;!%-h@ٔ ,@GlYm9₳K~cK1үL͊N37_k対W Mt -w[/9&5{g]]C;I ڀ'Q-LnmSd14 =I <,x{E33w6.'@{loQ~Ǐ$,`=K$MƷ;$2kޣoٴ.'_VNt-'XxU\YWk˜Ƕ}]Hb]u0b:-"7KNBT}Nލ`Ba @u-^uov()b;ѵ0+`,7lҀ Q+yr|Y*A{S_#3mM|wb@כ ,yXZv\*6 % %Ȳ;hذ ɤٷ8˛lrn`qaE6amÏ -tJdX,oM˖9Cz>xp \KfoDgw؄d4%F aBRibqrQ-R)͹Z:lV"s6¦?DLv]m Zh6F0VVXXvf Zm2a XPҪgݲ9;ngoRNLmh|#iBXJ:M T7"6Qѷ7~Ґ|aJ"=Hd W@zYK+nno_ӿ%Wz֬bgWl{8qd4׶ۂ'65ׯ^o6Ĕ,O j=偒oy_5 wߎ$F |$yϱ!j"{\HPb\o!d{\ڜŹmE@GaT_8Aa8RPgp5މ)5pÙpx'&4;ĶGnxf0ߒ5ZΒd4T=NlB*5|D{|CjZesJTb1mLT+!]kc) zHk lMOs[Ђu;e&tTq(!%bJ5ۋ gWg3a@=(>UKrlvkʰdZϼ#ۊhTB]tV<+ةE ~ ρDU*7ХĦI"søoml_q 'o8{fC@EʀRZuѨKXbND+}wr =|>z977\xqX3w~@ɬv:< h9\]]# ԊgZ|(B͕sA:MvMGYtי] /EGӘ,Wʾn;;N`r s1L:lz zfO7~|~  m>~Tn֦g,Yd>Ɩh@Vc1-"RRKӴ9yKuقwp֭;dfb҉zn>1\Tt%~"@jM/t@hZףf޲JSnVЉ %ח\f-y\qʶqm;̡FmJ򶋸sZBL'8K` ڧ )E4;Es tNb$blr anR!F˯R9A'u$J"Egn}3=r*-WK7pɧt/6PVu"razf:>svvrxep3{?7=8on_1 #挲-PJ|x6 U#~O>䭲|MْKuT ".T?jY x:qt`9dM. v{WZnFc%dJOX 'li/v/0'ʼ{8S>uИx+$?j<}`"'&m~9`4|PukYlf%fBBЯ7s H~}"J敶.X׵zXi~5Թ1D=OR >;N՛x\b,s d} m,. ؜˚'b@-6$JLj;B/ j~vi, >`-oB$Z)AQ?w.oxw!<>Yml/In>[.?yMI+^Z@ S9R)?1 ᙻAyxxp'<cuvAL:j afH2AmaeDX[[[]_b>gtʮՁ. #eYWnչ[kEs)-*#q(p:wTs߳,Df.Je>P-=)>ȯKM=4Z>a5s.<, +v|17_4/4l^~~uϾKilӘc_cZQMf 1V+UU?Yba~K6ch|7}=٢!U-cCL@5`H`ќbRI7i'%[^toe tg&:ȉΓCA ȊGv f b56P(4<*\]_ryuj%Yoi%K%k݉Wj}q%7i"aڽzh; m!jdI0ؐ)Q^r6X}tZdnP\*I!(XQv-ԢXf `*" @@.k-ԘCI /yrpղVXXC dPү:{IdILT="2rrX>7A Ă윙WUfFAT=2=}TU^11QQQYI|,SE=?ຮrcVXD'@{,}d#RMy;:h!}xX;n;Cbd87o 5ٵ:zf.)5~7oW{|O`nbVzG N!hG>{݌;* ^|z)f7J.I 2vIZUʠ;V2|⎍PW6{R%^XGx~lhsWzRIǢQp(p2|~q#JC3m-Xue!pƖpd.Q<@*1kjy!X*J>搇_8MorvESQx:|>p8P?4 Z酝Q U{_vliGC.! ͢؊=sJ$,k>LaN_*B`0U.Oe!w)ݬk`2h:o!f񬇖03͋X ,+F]poOC\{–Q[7cZIF i/(*_BQ(*|\ 0F*}%l}{V\xhᣖ!"P#dƅ2cV@Pc,u+dl@@2lIv~϶ F{3Lgs_;?o #SlsT:"1PuQ 9"+p0=| xˋ͂)Di( s ;h}eagbmr`>mjΡ:z$r:b9@;NwVt-5dug!7Pi퀙J1MSDֿ]#8.d{Tw J)Ϗq&,JhD}$EqciS"ڮj̏ۊ4C8Nb]?;n(V}A/wK~9;ˊ~hSAU)4SkXԞ < eA_<Oᅤ[@ ҋ4 6]p/El eE@ ĉ0'A?Co?BF\ZI>*ЎWA <(RI&q"5`NN>yۑ7g Zo u_M}ET+!P" @|N@2^dKi[(yq+X/V anO, * { y>BdSXwd6Y[ptt`)3tN[;`)9;h`rݺEK*fh3(bMK4MXn8Q,KF<=@_?O__=Kb+Zzm! J&I-na4g\>y`$b;@mQ0kŚᦣoAR\Bƺuݞ{ PcՉz >~ l黓1)dY()GVIF`"Ԓɨ3؟6AYrVhgRzBê<Qn#Rֻ8VXzxm Y(B{XҹyCB顃;u4yB!K=~q}oZŵ-hĝ hlHNfR+;}-D}xkQe>3J29Z:y @+E h Ut>s|}FQ؉4p}@@K x: Oڱp)%N |?kؗ/ ?^xP󫠔3I~2Z3S OGPihD0/  ',&'rcQ?3O• nXBU)O0w^~{O}xu} 7Jڿe.}*Юl: &ie+Z5kUy) ?ߏm" 64]ʪ3cEV@I܀!3s4I #pTԿKtPXc 6(.-=(Nmo[-S4PU.WW=`:f]Z ?J )CyS(9SvKJto*rBZ8S:Z\-QpJN&|jF4J 8czڠ60c+./\Pc~ +z͛a) G!LJ $ׇ.Mգ(i9nQcv%T)~xv|X z(SnL^Yvv8&@ sV7n[?z⫿% 3 !2$|80+Hz9/n}{.[ {YgNur]S!(S*3ָ`X aA씴,|t1Oockx7~v,譣6T1|͗8=o헊0]:%u.|,lbx^E+W<͏N(kzYǿ'.:)Gθ<OkEeGQd?==c-JP:} yLi5`0O zS&`fQ8/0n*)<,8 Nx\?w?~h lʑýL]ަ#[~kjPe+uV[B&} hM頳BK btd~F`Qg(#:ڹpGGWCmnu_#`켵-ҒЁb'; u]Kp\;pҸM@ !i]:p]ь +#dGL]F;rD s!'ӏiQJŻߡ-о;L37)p( )wZ`2 tGM= ƩXKy%6lM<{>j.ooƝ#H$QݿD"QpF kx;N!WE>~?[GX=N³N{m }omy!48( SdTmS^:.VܙjeѪU.X+֥ ]V,mkb)X0ϸ=_Зo-u<]j>}&&wjXn!4we۬D ;-u\kyZQJY 5wG/6# Gys:! b=tgRD1r8PU. ju9;Y`s;ѥG;Gs;. m;G `Oq8`XlA  k ݗätc{#wأ{>r~Lpd"qsdl qljeI{I﹒ω`lfpS2u7Πdl^ԝs*7iPӸ0+ly9-ؔeEmG^3iK1 R lPXYE3(-% "KИȲpWQaWهgp.̎3x{fLdf9-m[7+9 |;{N'ǢcK$7ۭD !E([J:+ Zv;[K.@>x)YuS6T]vy6?g=ZCmtr'زEkE=؞;E] )w4"OM#s3&(ڣ8Y#4Sܱ8M,KI:X,^w[qh .@ydEoըݲjɺ(G"lMfuº m KMZ wmt…rQ +2-<=3|w;Ԝ_iӯ;YFdȴ?6}?AtE$Y6gngDO %=3-1~#Iqxߘu)[12Y;?khhLV^;ǘ& ]Pĺ_BA@9A|).j(PD9})EW|ͷn2C8~즪)\0'83jYuA-5o4a^P~G19(c1bLORDikmhkC.`feYW#hX +RXQOx=J1#QUуq ,u%H G, wPޣ?=qE-Q+(pW捀S8i]J].5֖`8&p^cI~}b=r~Z5@9t+%wqRـu;N2X"I%8Xuo;7NHpjQ 㗭AcICv!&Y`#/[ۜLɀ46{ U;VȰq c0QUwLQqf%35f jE-wh>(s|:"MAc##^>AJs0k^ZDuZx8v\_.h+^*Y5,%WXN8|<=`*lYZ;t*TeEu.YW|qxo_z;x@.M)Bjwo%,#T Pܚ+L }?9:b&%w6/]3NJLRPFȚ$p<_q\Pn T1_bW,/7LVV a1d?A~Lt3K%H!6!qdn`5>olf bT;w86sIåmDzY^:#<;!up,U@g},91nHLm ^*,$ۈtzh27$2{i,9,-ߜox-aD+['=*" q,#-ː `U<0N8Nʮ;mz1~t,.afEu498 Áq8G{ =./6X;.m.\`<(3~} PB}s4vEAp8]|kvߺ6d=ڼ;Bwm0)5KUE1M!AԂOϘ3j3hhLl-:(D p>_l}qPc0b[IQxkEUC-dUC&pz/+>Ȩm仳cnFq4wV:vŨ4ĸhŗ ;7Zd8\.~ׂ:oS3#qQ@xȂ;ϵ.-,{ga]Hي"Pp<͘:=x3ư 7JH i.JfɲwO!?PDA 7gjLfI Desw :nh^ 1O']:"T`X ڹ9t#.IɡP6m߬WR :(0Y9]%ٺuaH \R0fY{(Cx6-5P'~>{ m]q }es=Sp|xg8~vr#"bg.wZ8IV2Ծm V+w-E@MQhKX5ÌU#', *(V Q aR\o(]kwEPoo0g| 7k3h9-Ώocks+Nб.pp$d0#e~Mg^O[[ VQe߀*9 ϟ3{2৭af'g};\G)=~1KO:e|klrl'yn=A4q8=}U9]u)=4c>{VH-~x8ҩz,"x Oow8btS̠ }K4 1۲`Y; aQU4hFQ4t#2Å;j%^޻hAߜF:cm'Z\1MqFI﮲at Rٛ+` u ;IJD/>0\34är94Ot)qi~|.ML7=tzU~h hVLzӇ\`Ƿa>1p2 B6T@ݔQ@v=A5:{0`>0OhKa7ܳl8}~n@B:Ab9LV3$Sܑh4vL8'x.=_5> vd, C=01OKXcC &qJv ;޸I%3MRo,Z;6 ϊb6A[< ?@)#oDG-l1e7ZXEJJt21zבhW X/úR iM`B=8†Q֊Goݺ.RY&Q|Mb? 2Zo;jeԑ0H| aVQh8Ot}afnvq$-ǽ.yǽ>O2ap/;dWZ'7pфJSnn.i8c8BB [$oVpcTdP)ɰ5g%7pcQEH (S$8&P%ȘqW2,5"5M0GRĠa#3&$s(>EQtybZg n+$DY T+*rY`ZiӋF*"d&Z'BJ~,vr p`\Z4^%uKIE$xMpw$#2P+J/34#v}v yjX޹@Xåx4QaNunI;rw ul̷Dw,op'-:Bʄg|.O/8·V?"m{}:,N۟{)&g?{`]B<]S#5h^8bY#%A ŝگY0{5  mIl:Lkh%ʒR L91=ߣFq+QvQeɚ%S`-@-,Pj JYowj(LחsqӑM>ӌ]OeH4%/@p<ME!)yMz[yL &u4WtTQ44xD)B߹Xr((ӑ]EYV*ep8e2tJHsCw൓k ~:;c`8t8%hk g\1M0qx͸-ƟikLJlS.lKtpF`;c3`sQe~z?~ctH8KK\=P)syMkrz$mܐ7ZQ&u=yh w_rmC(J.t@k)zhÁB=?ZșB ],n;EW)QK(h/7L V ص7Mܾ i,\Cɠ7\^O0,ߥd4xuϹw=w2B^LTB3}hWox S`{`J-: a®A\4}2<p\`g\6͙ݍ> Ȥ\[\rAi1b([v/w @ }=t_$vV)h?3?7$ZÕ  ʑ7 Qp$q,{4K {J$n2n ;MƸ{Ԣ( j #w3؜ՂeеOyL. Rw9: F]?*le,dP~)P8|Y=u#;3Zh,J X:@ܥŊ`MqPpjzŹpX f{l`[sChs,@hRN10QTPDTE ,k#mxC͢*g&ayT_53IHa"_^/ !Dž!g@1*!O t^v }_d1} ;F}WhK#w`E~7wSxw0Uopn175 {0ӴL/>K>{g],5U bQءúz mqfRְ{wZd`b!8Yw=mX AN9 OPk#G/ JZ7M(ҎRPQGD. K C@RLQ#Ҡƈ5 A! b]חsVzұ,+Zs-Ei2w@V@Cde,y=! Ld6akjym̜qYױsa02w縷=K!=)h?S>!DEr!â;$C!t` >8ʇMthtC'τAD!, D Q1Z"= %< _fR'lm )ޝrQS%>۾I,2{ƶ+?=SwD)K9p{dkҹӾB++GBi}DĢ:p{-.h6q^@6eќΏNc$>[~nU^2FF]1ˣd>Aa9Z9<=-0R2wEM 1Eo(bTSjs,200^`ָ"`}T-r0{hyjk\y 1=0]@5.,yshzQL}cJ36Ht4[ @,~]ٰ= -0cᶸe$Z5Y7RKi%=~.{'goH#tIs!ك ~.@=/_pDGvH)\3pc ofS+7W2H5|G^F䴂99=ٱ.[rNڻMX K?u= bdɧBPP>7.5P|>厂^`4AW T=BMFƤ mR"`œ/ 4dd'hkC-Ѱ5G"cEpL$، &B2+BGqjCf4Phlr`:pJyx'`&Ѱ )5!;gsbА*Ӷ+3 L?v.{%6 nC4`5a!SУs5]9 رVl0&P;6$\ŢM%wC <^c,;A]brO<_+I{%<~1 4DMj}`Z~me-:|}J59B--dhO`Q`ۼKJi VX2udɦ1U}(Á%c0\{y=x~SVגw~I_p؝(R`H׽ SMƎ zg}3l "=[h-Za9L1U]$tt B %+v3{3 {`jO: @lg8)hxRG4_=F7{ ؝g/ IDAT T8zWDd#  :(J$,G_0p9jo+ 8A wlk{:S-[L; ^w:}A\c` {}zxXDDARBo#ܩ!$D$r)aIxR-ۄtN.HQ/KyEw>igv1kD{+;ZdQA{D<P-%MmK-,rcG鼐?= Ya7-8,:fQuZ"5Y3G.y<8t#&Fiq~Ev$"}9.ȦEx ēaN~ y?l:Ș:3@7~ʳ9;$p Qx/{wW\ȸ+r9G>޿\~ .Z~BFꡑ*smQ(ާ"ds+ &4UtHݑj]6ȓrU Úl:pǔ i/2"f\+)3X؊HNpϤ7 u^_| wճ?U7 M;3M@IM>,IG3Y9M3Щ)J#P=g$6![}G$#MjB踊QcQ̰DG_(@< u،LM65l: ##a1ry``E,oȾZw.e p] V besQFx:m7pA2H΃8?#lOuapJh@2 mw'l\<8A±+^&I*;ֺ{Nd {pe݂_w -_ pcӽ5G o:x2Iȩ޷ = LJPy(EOD|0mL#i Vzg c<5{◀Plc=$d"hEÔ:m R`wQ{w6VD,@DX+g2$A3*qRe`Q%w&q݆ۃ8eֈM*c؁Q)rE 8䉼ޅ e89گ;sjVa5WM{x׌zbp7 M:aQq7`!ԇZlM6E.4y*p#SՂs0nwdC/΋Q4C' 0zKR c|~ ,",jJ;X6gO>3rĜPRU[HDh"-|& 歄V?,-ȖpPqY!mYw>$v?lݭq.u^޹LEYd}ǿ? ^KyU7)dз}gX(Ec.v>GCgγ*wA`q' '(zo#4B`!yp(C̘F;kz4Q HisQJe4&B2jp%敻эlPMo +m,8;޶K@I/\O>>@>O^_;h|:qkd0^><$3 %jB|z>KpZ3 j ͝oSǍ7vC .mRH !i gIxO^B7pThѕȦ"*j1Qu';J M7|fUңU<lS7bctfjnS k5Ar초C>vM+~ƶsmZmeT79"(4c﷎cGoKE,cL𮋓|giU'^;]6pUD)[ٷ@oim]Gej-Nؐ-6AY|\5?D"B'O5NPGL0?VcB%JBOPo"<|_4_.й'f0qF&bZ<ll{roÓPYyK7߀jbo23x.d̥uok6e27E0no Aol칀1h% fV7WYrq_V=OWf6p/ .~36 Bi396OpeOWt#s`FM'cC1'dm'` sT#ݭi;A͍LbG7 %|շ5A $Hm8`0l#?^8|K|׉psS vM=K !_bjnVwza"S6T'`O,=sr /tceWF 1 Lz^мͲ߷ 7o Il.^J4]MR/&7ec;N=Y^jt ӃM}%͍>h G` =Y-<⑊5q6OF44<>=63pp5mԌ0rzEئȓtƮS󪨁c<sɉ*)0w?_tA?v6= H`8j:$ peteu q ٬I2޹Rqe5\#0dzL pڂti,l;v}4HѿBݝ1HB5g^ÓbQ獅RL22.u8`E%٨;f^ ngKte{Tïn{E ¦tidZhHy:uH9f"E0\"Uznzǩ/A"ko'Dg- @iubyOLZT9ΎSn ަbfw!"&e\D  8OrIIqf6p* Wrz-Y`Hwo2tSgsi" ) 7̩b{ ? |l]|ZYoY䯘(i2/e]T^u@: m1i"=(p1Vpgnp ȩ S>1Q{zLJPˁ}D[v#7f1l'vX(Na&0:y gTiaR$7 yZq>2etluc ?6p=F"U&=ۜd.}KjP(E#') ()Vr2 J,- #מ|hb˷Ja|k\"D_fHa ydv:o&iJQMw3}/RGgucepON& ~q@xx6Ϻ\jU,u.lYw:z=5q zHg:b,pރ6z2*S4<n.LS'q&Hr{p2Qg ][)d7/^V[8hw빋-S/6`یZe8=nZ, U ֞4C< % m̑L8?T3GiMFa#B"O{wI &iF*jOqFmO0syҥ4ݣ$mI pxj<i=NL6X*"}ĽV@G-32o U'M:G^+[ܨ-On`iaԭYo]LF_l2kRZwe۔?z(3)$tq˴[!FS jLNk)?(!vBO~F+PVqNΘ,a%3_?1虭{io[* o%]jo*d_ FgMm''=oү{-f.xPb.Ä1o56l~}NcOx,4 JXuhWXְ[xԫ]u*llOjp$`_?GV&YaT`Fɱ1QL@jr'N`c%zBZn{bԋ2aq{jƷ/QxB̄R"kayXDظRo+,Y IwO;?P$lv!lOno*cLweX湹 CǕ}HR}ru(8 $W2tvX.x@0y#sCcx*d]OgeL9^6٠&Oyaq0W,8[ݺֻͮ^lf᳔oJZ&{@ЫY '(9#?~Sk/;UijY(sz'`N})$ԅq#+:*N23g$k`b?}W9? Xt ܟ/*Mא2dшL.YR@޹rn5mm۶m`m۶m۶m`{۶m>m޶m۶fm۶m۶m`{۶m>m޶m۶fm۶&`T~IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_egal.png0000644000175000017500000000165312014170666023451 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<=IDATH1D7Da5w='. ]E)U@:]ADuBopלt&dwGKmbJr]b* OSJ\8ֈ" .PAЗۓ3&•<@dY>VwؔEWUF/a?Ji+[TX^$@QW5\8~^3IӕUxi xm=A`u2m !ƣBsi7%Voiˤل 8U!?;(lS波 >ǧ~q8_~O\wmۢz>{_npcIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_e.png0000644000175000017500000000305412014170666022762 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHYGs1;Y{Y%MVƁ8ED␥HQ)(,/DPJ&( o56l"^kzzkUWS7%RJ P4Ѵ0%JM|[/R4JǎMonlPkqyN=QE\fpϖXZ5ݬ<}b :k )|:q=TJZE8Cɴc'B{%d4zo^\vKQ5dkmf `8Y)oPֿ;׺TC?y%dT u$RJh-8iRUׇ뺁n蚞y-}{ pQ17kq'=fJάHX_t@OTZwns?3ofL ʿIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cercle2_.png0000644000175000017500000000315112014170666023347 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH[lW}&8;\LL -J/ЗE-xC/֌;TJ$m2jzU98u$^u]7H9Nm9բ \?&^FI`2XeX`Xl}1z%*nLӁCӉ(!U>?vnv2$G1F*beWrYa8:j${ts99DŌ_Ddכ,\: s|}m}wWBJ 7&y9h9\љfZ;\β kIjl4lˡp{l1af֦naq$3X4M.!!{~g0SF/bsෘf)hE \#[ ccﵑm!on}`rHv|kS D3 _/YM- zƕO)T VKDQy2͕"n'Da|:a,E0 :yaQ-l1(soGIozH)c\y9n +"%`&ߨ-Uϓ,0:˼տ0\e1ك.Ndk&rQW1PE e[nD0ϱ]>&Šym,T9OA !tplͫO rD̬9βvUmZn#ht' bç+~MJ$Ii^fM[q/iH!h|#3B)ɗ-]rr)?z&%~I`xkZHk);o6 QٳuIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/axe_.png0000644000175000017500000000311712014170666022607 0ustar georgeskgeorgeskPNG  IHDR$usBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IDATX{UE?sνD).e0D-ށR$D* ""Ђ-!ʨA%mVdkCs]+_J5w87|g!$[BЭen^it]7[ - LIݰP8n5%u'nձV׵uu/?t 4)Jְms@K'P `0йm(34oѓ%0; :_L"HB v@"AJ$)R"@UUJj?pqS,P)~8_8"$h]J֪3QD2wxʎ-Y B E!dPG2¶Vscs% te6Q*clT撪w~s)@4@'@E߹#Dc339WsR=Y @(j!O*\I7 Zu,=@BJ+E (jKVK Àۿ BRʫkb DU d!H]'}} f0v9i Ţ 3 18 !N)l]ű|FMJ% g-23;k ]koqtXV3ox]l^y7FRQ|,q*6~qxCG* CA$$%׳i}j { z{&΍KW3a碋Í4R岖DAW_ڵx1&2eͧƓ|@TǏ;${lc͚GoMb4Ig8c&;@M )=LYx×}=R !3fJUAQCjϠuTvw>XoR4n_4'n5#2^BxUl8׸hbU#̸x"+HE fSX̪3h5p4׳/4 daD:Q(T6ThqP}k u9[4l^n/(״IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/intersection_.png0000644000175000017500000000340712014170666024542 0ustar georgeskgeorgeskPNG  IHDR!$\sBIT|dtEXtSoftwarewww.inkscape.org<IDATHpT?}(?4L+XKg`PBtЖt*:hiѶRG,B;#?+)L%416 x8=3wyw~yR+][<&홥iUÈtXy87q׽>$Z9K-,<۶/sOGkM$#+ ə@$i2iQdz48n̓*x<("Ց;&Kf4.1a!#t4zjYKW桬"5xܵMÒ6dv؋񥎷XٞPKCwOwc.=gI qV*. B2 3ALp85ݎW۟PL/: ﴡZ&B^} W8zw|_@Qh:ASce 11`PUgM-t W}&Aqfs3؏bJO|4Pw=~EQq!h R8x)gZp&{%pkhUU)Ü, 8uv1Ų-rq6NH63DzU{V{=u3Je>ix Bpn-iri39.[& ;U/W,j˧ B\eiH3xuBZD5Vm$T.LK/òO9qno1 t86>߇T.|{Vc9,$pHM@_z8\m ;ߤcpV8baRPwBJ ݌1m.yd,:Y}@>;|eSh]G^C?u-!ܴ@CtL8AG5GTTwt1Nt6COPM{9uhCsac]bQ,ƨLF(zI&~6\\fbE,#̑Oqxr(mg` sғċ0=(FE І05+M \ڬ010-#@r>F{=W1 \݃#OFU W:\nsӑ5ֆ #}f!,AW\|C*oKЂPQ,M'阺xa1ީȬQ&Կ@F~ ErbY@HUiRb f1 WoQEcD"LRNy!ұ$ px䕗6xwj`BO]4y 0"DQI93n57>v'Ӗ&>e!ןjEcĆ^IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_5.png0000644000175000017500000000177512014170666022712 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHk$E{; L"$ u ,(?@(ȂAVXً EPWY&D4Kd(V$RqII"Mk"5qG|gI3CV{N3O3ef7ޓ#pk*z@Kuؿ߫FqG'^ jHa;@*\nBQK43kxLPjnO7O\X`ܲ&4G)Eu\u|@G#M-z@ >)笘OyjOu KAJ-<[vBU0CȲӹ9D뙁 '[6RDsce^Czibב߯" []M[-R=Ax_n}`U+cT36J0%97\{'2[k]}BvW_B,/3';"y,Ctjryid0`s7/p4)4/Ñi4t} &ߚڢ}^u{]}Q=COԾ~ ģ(r KoN&h\.w4wxG-[/g{yk`|G΋|ܻ,쨦 D`̶ =w5G8zwm_~kwʹL{5uwR`tekKo?9r-o.ݘD/L'ONhV"mto\\3W&獼_GKGabztYO^6Ӡl,8K݂$vtng},c>Ӊh~k)^yn.:e/]cvж 7]JN<ɫ{\ =7lΩDzMcwGnyn̵c뻮{|}+/FWc[WLF+<4Wxjyઑʥi5~hUX/׬}C{bϝoo}Ɂ󽹉h _GaGܽAvtlR?#JϷ 3mk~\+l|9K"OyءϽVO.{ Uv ׽% 8QVv= Lī Xxmū^w|w#?>=9Vs1>+z^rLtZAf>pU~W6 rd(N{ tzgliWX[Y#qwo*m2JP3Ǔ >ASp^-,1A %Ivwu;ca?Ɲm=smnf쭟(L~w)h-?vy֟ko ϽC}ws>WڜԫLJaʆlٱG^:v2g6\ƻM9u60%YʮYߒ-9:!]p}KG9G ׾㻖o~[^]~EmS"_m^<6|ӱA+/lxs_>0l z< ![yw~-=rjYQ*11@Gk:`<t3̂sԺ9+R Qy)ə!7 Q =Vhb+5Wm]s5S3++"Jֈb2nw "8X 9OQQmU 0hGͳb 9ue@8e'PbJ-ߖxLk,<KfR8YSR A)7mwO$S5S?+_}o|`)oDgt H c-TvT\ެ tr .+T?c &VX=Ċ ^Eցs|8V [s57PO+ M%ĝ3&֍uo9+]? ȝ.CFJm!i)i:ܹb H!bkU8?hi#\*s90+ r1?/gf8'8hm$_yx&֎v;r9O0_ 6K5HtL2` ٓ$ )3!p`߲ı sҒXyl+-r}RHYu f{΁yrD\͔$b@N{YcĐ T8o\YYn!˨JLDXT&p`g~<&'bbuv:ܦ7SŦ}{`{ŭ;/>uɖ#v4z*֑s̄Qt?=5:cЩ912;ADf(W`r-HP ϤV@1)s&֎خ>KGN G Wſ6~켃ODʓ\)僭982 m 2 }"~ sXt 6L:1:G53{`w|+\}ɆPuEy(f{˃UxU;"t$rd+N(mw^>fs_9@sxBXXɳ5G ҽv0g;v&`XEQIkGZ[#ں" _B 渰LN9L,y ~ |=LNFI37$I3{ǡ1п}#wl'1DY5!"RG+ $Nn 4W(rh-/Cv$ӷbdN1'[;7k_ҷyQrU1"sXsh HIyD9$I 9>޺롃r.]UoNWy{ml/~P|,HW|MB plK8Roaѥ'KDnH1 S@9 !`/MJK$WX!CT2 gImNm62Ic"5ZSۻ]}0I56u~Vbf ~-{z~-{Ojhm`RYi7mEQ2nL/ @,@$1xu3^=Sh3X"#!SgoBæqIf#6` -]{`t6UCL} 89};WEJF˜Hh #RmgXFMlopgeWbȚ(޲ L $X?5p.'uU253e打 ͿԲ bD[ଃB*Jㄠ|`XIBhɘ-o,w ޞvIc4^= h[Wwއl0t%?>iYo.i v«I+c Ss=NuB@\Cf-_~A,W 0GIAB:I5DB[cmDj :v L !}S+5 =gowm}>n9;љL~Uj"AІYݩD|-A%9e @Ic4u(!`t+I"8p3b$.gGh@u6gj uX)34]^YR׊"Ξ .z!4?xx܎'tЦ/ʰxnUU>Rz0\I]R7<|k`-XP? 9qr_q}-T@B̸.8"ib ZkhV.sDS=rS̪iwL>o: ny`]+tS_ <壻o.[ fрva)y~Z{azl#vgӻc2w{\zsiEJ*hg3BR2bk H=5]n9!r4쯭N0}2N;be깓X! cR n,V2txOyR<RҴ7ԸN>l>L^S.Ҵ 6sv.8 sKȲoٱ ߳"MCe_V5윓쬄HPl`򩹪;^'L-ckvtJhVހn>6 RBnn;nkY_휲ڃNt7>Hrل_Xw?^i$g2'Y2+8?WY?Sݕ65&1`+Ui`3y;K=E?_lR3KcnI( L󓕞q vg.}! lV@"\GN?ǫp MB(! HH ԼzRzhG('S2N0ATX)ޕ_I:#I7A˰Qw?_ TbD %ñ5\:ZhG)JJXkL$*uxwV$\;@X@B @ "% RǂK/ %@QBJ VI ˳I_9ljCE937\DARo>/y'E( #X^;}Ŀksn5\n?̥q}5.'Wu!@IK]m!`UF[IdGUHk >JhŚ:W^zV UYCX',dp)<>P--u.vM2+hz9o.LKWf'Ĥ)bx=„ګK_E2UVv!3$8Àҹ%_ b P8XbyO3,TsCe{೴J |`r~k+&-/䱇v&Uع)ɠ\{ 2Bd˔X]ڰXit8dCS#K֘trC~68"C AYDL R 5bEpb'3R 1Fcg׊IIb@ \FS:>jw/KUKKKO%*Fb#K]\cN/03QJĂA:D`E9]N X pJY5pn^>ubơ>}r0 HHHQ T_mD0dDv<"ڑD2t(GBrŦdS+ <%ȞW].%zs-'5VTeih۸#"K lxCbgdԷXh!ϝiԙL00P L5|Cpr|V~{%1$@3Mr?mcՇ'Nm#n|y{z翹&35+tJihRRo~һXr[g gApA8yۿhֺ*@h)-7D mD][P`th _(pzIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/selection_.png0000644000175000017500000000377712014170666024033 0ustar georgeskgeorgeskPNG  IHDR!$\sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<|IDATHklwf3~6c* ! AV%&j4P!EZh@BhICHx ($wlc;;G"W:99s )%ۂ)t.VҴ.Kq5\Q4\nӉbX7|׶Mo\wkά-is{%$x8?co הf'[#W:57AG7~[QQ 8-B=J0T" R6 sPUJN,wW]ƓhT>XpyjꞋZqxe.evQ/gjȉl,2}cn#JQiڼfrږJ2xqqxt-`|SWZU$0yx: ؜V)A禢%>_J#EE(⻭/ٿWӎe66 4Οf @QES6UAL NZKPqmiu׷ DxB> aQ0"kt5aU6wrj&}d4 .)kgg7O#r6uTi~v->URۛx߸xlD>5AtG㴘kDڏ>Υz\kC+R{h=ֳm2j-ȌTRI$@ŝ VB(*Ocy:}l}{ 2>>#! .IA_Rq"<\;%Į B3H]L˥Z^j>;yWrEz..cyi/N"?/ҩضI*!RmIK du/n4.VQ߿sE?;^y+Z e%AZE9?YQ t]wDBv 2Ey;xųs'AfΡu:Zw5Rs>3w1 M= )e&q@)??!5w'ڦNvop-o;yQE|ۨ~%ZqJRb6o7g+fݯ0AOecF2{%MJgiv?vI?!P@JڔNო`}ءP KT)8V Z3sLvˋ˖2&wu2X1χ"4@ux ,+)$[cP`BrS7izZL첲2~)-Z-A ?y殧2 m)ѦlH縉HM:2}:-jzOFcYr{X\\Gk: b"[Z:G87g #dzk؊*]dp"d "zk+~BvIq=ܙ cc#O}MsPtRăMC3RqD9. 1<2z3رc=sJ3g~MV90B<vc&є*~~u/322œ ,1M_b䀏| Z&RJYt@vn jyQ} wc[@KL"!#JS AX0䥼@}v&٧ج-uWC!Px?ADMApPc(#r $D" GGQy .UVu Xw?1\K 6wz70B\)egB{~ÏU%Yn6pT6ݿ^BJ Vs6IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_minus.png0000644000175000017500000000154312014170666023672 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHkA?fP$D!'iN *`@ !VDl-mKARܛݱؽٻ z7ogG8WmZ7FXuQ0`5=bmg؊amzn١ف{2pk@K̵}s@}6(6Hfn8q #pU9()%--"E~vڳيRpCp/Q sHz)H5pkNap7nk`zGEuc^am)dfF#қsp @GQSލm<̈Rb\] !VK#D77!T(UW><4 g Ί7LXX)㦮0_6|Pθx?U}<$ ZX5?Z\D%($C!~2] c Ȓ+L|PL(_JMl d@A,D$%iq@Di}*3|nzjQMGK$ ̈́@2ՒU#dYkX=P77i PEp!ASOXp̚?>9Hpv` ?PG\P9LJݓبƨnJO}_?ұx1)r^V66fON{IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle_rectangle_.png0000644000175000017500000000345212014170666025665 0ustar georgeskgeorgeskPNG  IHDRVgsBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHkpUW}ryW IH yP bQ@Vg8u:>KK0:㴟cSUhA^H ! y{=gȽf=kZg-aHξLiXSgia[Fqǵ㼛Ǎ ǵ:k Gx|^x'qI:WyqWR a;~4r^6 EQB!! !I);>O׈S"I2**qhÇ9&u?a Y0s!+ܪZo!KNK^a&S?Ɯ: YaSo5L~kOB,fN&e!%̄(j^ڛoPlX(A@ ap!caV`F`k DE*ډT/C0WZ.WLOjɁ|b>"q;<RRi3[IEVm4X}ƞ'[R"RkiɕeƖ^YQ2Hɇ1).sv{99jM髑[kwHP,Ne GRodUgug0ar\Zfڬu%i.HE0-C+Wo]J ŠN=FMc3Yb/j:yjƔ4s5Unj*VQYU QF6 kUrJi/!;S4`B3BYh}GSbcʽhJ;ǐ=;I}^U*jr ,qa:#=RaU#5=KˑVWQUQ"+(N%>rTUQrEEQT4U*5ƌdl?;11k\)\B ǵRF 6=WO!@]'l-iᡒ#P>1Bx$hO=%hL9.vp|" "2p=&|K<.^♊5n"]|:6gvoӚ<~j5krF8v'9s:Z܌3ɻӗfZ[44=_:\tƈcʫ>~-z#}S- _~ӗzxiD(%N6䴡67ۺ@/fq'JMQYXhnr"2ݵ ) hVآwb1E"Xf/SurN i;XA1YhZ&Lf, Q^BaB)d:<I ASxhjv8?G&mrx~,*p9h$eY/z]%w&LO1YZl+ 3bn ޵Z.ecl>MmM \2rLa0 \S^>e|Čj$ O@cCUL#AG_'ҟ~d#hv)ǯٮ-tbn{= q_y{'׳#\^&.StIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/masquer_.png0000644000175000017500000000331312014170666023505 0ustar georgeskgeorgeskPNG  IHDR')BusBIT|dtEXtSoftwarewww.inkscape.org<]IDATXklW;٭i״jCHcqKhB? PC+T^*?J KEH@D>HE"ZHChڦn)y#qPJ\+40-ee *MGC$*il>}p硉G-4](epc}#@R`:&sI\xr_X=,m\%4M`c;pqZ\#$ˤiBR \NpqYiZZ=Z%}SJ _ ڮ $ĉT# maj$2/Y L/)#[gB B8 ~ Cʕ3,vVtY{3ncNC%BTDt-"+uC.% sLַ5vs8n`6Z5o|jQM4F]1/Q\kUQ"RgNw྇>䢒ƎKӄSE%8,va7/iZKp + -;Іn݅řI{jAxEgCƒR˒P"$Y'}M$w0Po*WnC)U?Y ض댮يER 9^|8 rL-Okf€}]Ti4팋EaM5_u;׮(G?[S Rbe^re4qGgs 6K]R~tGwQ򧳀=a\;;Րu KJMS-M@pAw0!(Rqie:ͩYI+c&myj6mk+ˑeǽN>;j[-IoϗnՒB5P"+Z;ɲ%dJQ)@Rgۚs Cosaa>n#RwY p__ ࿢ 5Љ)IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle_equilateral.png0000644000175000017500000000345112014170666026071 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHilΌmu1%MڀRTԐҴ"HE@*IP*TQRE$PPVAqj`\;ƼfV/|z̙{2̽B)ovmLmYԚ-Sla%qSRc>D?ێ|i  %d&{]MRR,~l}JشR2Cl,4 @@\"Ca^ajtj%ee4)R ^7X? ]P1E ӱ3IR!| yYץ2*dԎI}Pف/u+J4ByxQ?6l 3k5!'>RXPTh`bWQL܆%bXWtI0c&^]y6a* wH 3dzyFZ"غ~1oȝ[6K #L i-Or71g?=v3J( k@Y8uV|?9 B4I־ϊ-NzAF۱g=qW?JuzRdZ1,}cazNw{$I糧qQ<\`Ec3!(,({ Z=;25Mgd)5 &Ũ0|6K+XXY9vˑ>ynEΣA#@3ʉ: |߸~~ۇ?B4M`i9B[fu7BQ% 9!/?וSiv @%.Fhyef- KعIJKiFӤDRГF0j"aKfh`0B)(̷Xa-SܸH g$> '$qg}BGuuꥼ* y %u.vzJ֬]B= 7qMptǜkgi=M7Ƕmsrպ58{]t\%) L4vzH33梣xB3p sx_ok}lǟ48A'@NE"+=>9eq8&Ng7O uڏ~H?&WW7S8^u /ͬ1]H^9q½y{| m;\\O ˩k* PR(v8L+J[G{"@)<RI1 @]ÙٔLXq$eDv% 0wtstn%c%zC='q}_^;IHOSzd‚g91ƨƸ>'f4!&$RHލ)TO"\_K8 ,P݋=Mց[((!wn6Oahdj5UR !~i7uͧ ;ED}bӶK?L+$)` RN|[ fN0/MBd8nx xR)ҩ$@$=$9*K L$}ǁ]B)Ŗ])j>y?g]_ ӧfπcDz¢ .IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_3.png0000644000175000017500000000205312014170666022676 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHMh$EnY%"e$D QQA= ,Ed! "{OK`񪐬Ú==3ݙ<驪^9n|Z }r.U3Eؐ"h( 5K818IbĬ~,u 4943_ׁ/o>  P 9s <"3K ;@>]o,h%=EXnlj ΟH nGS}GAIZ&'=R40xZև_=@DEql4Y<VMJ3Z7Ͱ'9P{ %ŰE$A,boMM%sXH 0p =u qpL|{bzJpєN![o o~R*Sa 0W~C>k-tV 7Oo/TMO/hc胏1Z G2*"0O@ZʋzE{2J~SPQdg8b ՗}A}^ңؖl`7sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<\IDATXMhUSJ?~B AR]sV JA7,Ji-YZlTm4I{ysf^Rs̝s{#UWQZ*UPs";j]_}%'^Z@"4 z} UQQEED4RP bkO4>h4&kA߀Ӂ/F`o{Ј^EtsSDS6jI?QnMWxxƈC{.a[6ؖ -xТ>4`Q5 [Q!l1ELOvS &_8 "ZT*V .h-GJ-V.İmS{|? Z7 E׏-&P$/DUQNF&t"/  "U巃/Vk:eu ۤ{m{wE)}G+ȵ<2>+ j^xD(WA' BX`2ì8a#{rlͽw#ƀN}GHTur:j0?UFRA6":]RyŸoEx/x,8΀u/t:rfʥF76-L"w*܈%6yN2)VX"5uf_|<ZADzNs.ΊNήvG:%ՉLDDG{j3` +i(RCCԢ mDy&xD]|ZYQO;;Y]͍rx0":@oWYW4M!:^a$ilMHDh~M//.Q(:hantwi>,kwEif!y )h6><݇uI!M-^.P0Cc[/~/Ϟx=s19v,SǟeuڳPT,_[(6h;^0w{8,^eeK-1T.3>=_߸pvJݵ.b)\zb8ҐPd*W78ևAl׃}",%4+~?=__y3sqCJlò Ucl#EΠ!MJ){hdϮg0r 4BF*R9Rm` \8O-e ]BeE̔4i^Pe9wPY`J6WRӴm!LHN.=|I0خQR(,(fl4^0UNϮvrvCdڍhd~UD`y" |`eg哻нǒ?t'&X*WX|AVٿԛҨDŽÁ!eN09qjP@!H YZ58q.?E\\Lˌu¥Ꭽis@-B[L0;?JB8-t lH^}c AXu`{©36]j5QBQBFA-kk?:PW1 ΍m >6[4)qQc$ I  [ڙ[Qo0 X[J;oyј3̗oOXY()~$ \DoDgIxRQD/&؜QS4"T:%MS:FtϦmtsR*RHCk8{1g*R!4_%(߰yPU^cX;MB(bw?@$SPzmM<\$i݁,ߘom;b8̻V PQƚ:NJۉ̛|m1`P{Y&!ݒ|c3}ufsx4;Or݄b@yhie+0Qn*)u|3DV܊ I R;Mo7 |E~| -/sXY{(a?"OGQA=!~;^Y$ɔP:bݨDe}Js$¨qYomk@ԢҊ* !U3 Dw7;a͙0 W$n,`•>1 Q 2/ۻ-]}o jR-Kd6TבYQѶgƝR-еihrO -C iTWRHn`eKOc[:Man7-WZTwHl0ob U /_%ZIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle_equilateral_.png0000644000175000017500000000344512014170666026233 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHkl3;;ewŀm 66(4A EM"ڪM RVQSnJjҴ$ Tp[)J6R+vM k^۳ؙ%G:3sϽνGBhzV׌GCQ] kBCXeso2ee~lYNr?d}/mځ6 $y.RKT;@dnhjZN |y$$Abbg^c4^ e~n,؎e-e~U~I$IFQd%HZV J>d c,`l00jP-xN-c 9F39W/2mP-!#B XRI}FWTUdRU+4cqɁSc[ ̦ujg+g&7áנ/Rd̋JKBQϡZ i\.C+8Y$ }127h>Ƞ7*uS6/$˳*jjY/>611AI j|V:3|9J|6`KU){Nt}c$U NBro\Uӹt\l;/ndC/rsj^?EfkE* jI:C < */l΍PL (~9})gvV`P#T U `{nj=--E \aPzc;}($02jPC j^O,RRth"JPɱ)5xp*}9_x!H87~6zehE54ZNj4ƲӜmFOʖd%p]7 $AOnn=q,\]il;,oΙ.:TGqp];رk7M|8tGNzamb.RӞ死gJ@z:(uG {=Fe8")Mu)Q4h帮S`698umT+HǞYR NQqء&˿ab|ǵ m5j n^e^jb7l:/cy6LK>R'q2RyK]t0?D Do8&]S|,Ń?"<~mIIޖ^4scc5nܠX0ȚqlmܟLSYGaAŸIŸI_u } YH\zu%8zQ2n!)*$=sG&VWsڮ^{f6?U{'5%7E}N)Z..}G|/ L֞L$2'm2MPz:`lYks5C'c\"(H TC8f^&zC Z-ӑ(HX#+XjNZ t5e(o !S0s,+wEQP$ S p\Bи淚j6PRhǭmi>+9MG@j~?瀟6h? /xQIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/sauvegarde_.png0000644000175000017500000000205112014170666024154 0ustar georgeskgeorgeskPNG  IHDRVgsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHnEU=쮃H>%p !P8r8@% +Hxqtۑ TL_U;bfӾsn[Uo]q- sEépNWU: N{>vm[c7u{qK\Ne=q"r], y&A:Ed rKzA}IUX[[[:-"-/"Da( ^]BE64P;MtK͗AtuM]U\)n7PX0Ҡ sMJ]POKC?4Nhk6:߻4kmBO]ukzbq4ÃKsm[Hp. ?=U[+W, =IGo~!fYiQw5Ss]ko9iuy>eOu0\PYB !dd!0u3#% 3c61-t&W_`8`o4&tE![9y7~B9ϴ,S3rtVr3vGǕSFX4?D$P %đā:rrJ9UN1RHj[:An%ɔd!ir)}!VUbERlD1(88ׁ;j*w̷*fJs R:eYX?[>@J eePUBvp)`jզOn6'f}?p?4/z^N)z=|^sf+,F"Ĭ,?s8>drxtj:R}3o஽;IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_pow.png0000644000175000017500000000206412014170666023343 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHOhU?̾Is󠘭\`J R ނT#FVLj,zЋ`i%р@ I=Uzsqavgggg@~0{~{ k-Q9㩺1 cS=0ADЍ A7 h+H`X:Gz/*{3b;QQ oL2E+l), Q֭6 UaLЍ:A4%<:i"!qdGck;!* 40$` &ME +3YW-+8A05ؘF&ԫ˙A)-BO1q+~4Gti./3O 0a8h݇ї!Č|Nt&N<7RB&wH< {H@7֗pr~1% W(Rudhp8RߏSY`j&PoCGV(l@ hj`XhxX8:_1;Z.`B:5Q BX4 FJɾ=Luv5#xNslk-3Q?s^Gzg]&fd|r|IJ] CT .27>2dZ𘑢Fha 2 e@% è|3{+ ppQ1Ϝgyn;G84@+7+=fk f=Y,)~e"i=n y9{ &HaJ%:DFPOh6\xp +yKb @ϕgЖ.R(pa2;M-\(,kNJ'M&;뷣K1idfv~CͯTd51xkxp_ -}ϾbÿPI\wt=]L9 TEMzn8ws?JZ 4-W I5l[Rqw@\7ɧָ&S$[kc!=H"V5/ܝIn[Oُz {wo@İD%ld YskDU&dgX''է;x("]9;Mk)4?I,[&_<6ʞj:|1QwvarK ׸Dzu\NidT5 #řEk%]9o0@ծ36fдsv?*&Ͳ[ܺ,)&C#[,Vc)r9Zv$ :8P5##_3vZ+U$ӧ0:vEr-X0㥗I$wߪ%q 㯾b&^,ղ 20ȇ!0G> Y%$*U-j^{Yx (GBk:t+tA{~<uׁ.L!,}]E YIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/polygone_.png0000644000175000017500000000325112014170666023665 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<;IDATH]lWwfvl+MMhlH iKj\>\ڒTBDJHU ^ <(M~M*+Ulj׎zwvfw^vv5HW9sݻBk ضt-m`KvőNtwm5_<@!~w0^||=޷ubJ)Q[ɄM9,e(&$\TѧTf4(iZ$NQ*Y?6[@7R!V546T蛈MGB^D"IxvI+,6z$$[8ӽ%Nhke'iZH]``lK<0<[4- :Y/tUx- u 4-޹w*e0ylJ5d< Rʥ=Ъi 3u 08gj2`HxI}t-#zvpLRLii7<WGKu3u+ċɥ@[`3 <W8mCt@ (rL9&< vR+ܖe복(ê(ҥp#;D^2<m5ˤYn%YN&^|1КuCvuV8?$]OUҪ|_yap~.M^Rihp90qO-˹X?ѭii ` Wa4+l{zao]݋d o00w\~b0ҍߩԌ5ġ!.N_(qc~<^{WzY2}P+_v2Sð(iX"˛mr^Co]o> @7 T-|>"abE#H0sa 3f(ӘV@x~ eϤ8f?\.52,l/21?0vZ0K}+[(b@))Td~\4V5 TKw7ٺ4<=52R ~ZX"IEc8~d[Z:) ͞8<[j |ðj/6/LĽ IF.q*wo-#S_D-Z 7M ia0^ӂ 9<]7G@bVUR%Ci?Byk ~L# W/^̪ ]>ZkrE=p hysȄ\#a~'hJɰ5kȮW Ձ) Y$6hˇCD{fO_c.hH@MAZ p!Y~FP,0mSk] <,?7[_I˲LHVW_`lJ?JڔaTR*g:DJJ)b?(s?]-}HXI(oKtTWN?G}hx>=>g鱥{mow2뤵M IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/rotation.png0000644000175000017500000000272012014170666023531 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<MIDATHklUw[  %Тbx( !MTDb$4*4!o> FPA@"4$Zvgfg;;n'9;޹{9gp]_]Ѓ,uA] rA@2A]&=D@,lO,m.ӲO[sȴ &3$À3@#0(qcxws3A]#('PeAC39LZH@Vƴrj`0Xw@J R~a+-f$H!hzڻ>˗Px*TBS%T&4SNj {؝~ ܄9t{RL,AU$pWmKAh^BrcD= *%ii0AX--HՐ+ Y_ `Z=gǏ|[}?t¹gIII1TUI<äGXb_Ţ+ya֮zak4=3:룘f4N6[uToX=4TM`(!K"kwDi^|y%e,_2mw )kv?ZtY

]ߐQif&oݼ '+tHe)RD-]w"#y}< nS 詇- 5lI[h}#P^ٟ2;eE3H܀1I3; ;. ?WwKO@׭k,ڳ E|l@ðpg-}r.]lZN DـmY΋i:g"?5#vT"j8RS8 iǶ<=UK2c^*"~~+_.N0]d >w^y7 '׋]#{]@XOLq9ʓfP޷JbXw5݄pS] px;j3v2CFL3rˑVjG͠fwNwCW>}O+!`a^G1f6}jx:7{]섛CҀ&]ⶃeQ$:#)TU%u] Π.N_ei5-e?;`5pD"ݯtyV5s 8CIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/representant_.png0000644000175000017500000000331512014170666024544 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<JIDATHklWwgsw֏؎?$ŵ\nIHICDFM6Tj*UP? "R@iQ+J[! HETĊ!uvn8w|]ۑr={={%y )ҴvK*՝6-i!eyL :\=I׳>{1đ*)= G\?39dw0X@(~/Nڴ5Lt H(JT_Gua%)xn `֤I N@)X4є̅*6̇)%`$XJ^DUS8N`N1Oɖk rXt9`fc:dEJūziR iUTO86.VE=})ֵD5)ˍ`}77Q"F7/}7]8@stW|.aS BMӫ-Tqa"ŗȝ;P(fYF%bfBb 5v>&hl6M Ge,c ~@(uSHOO>Ku|!`}ru+BQ|:Eϳ' K.9'E0RFZ!zk{(=yjf-#6Û˄rEl9!P`W{?9>3>=>=f/%pɧ~OkQi5\y0^[gWQ;x?B-Dw7T:\6q̏> j^mk(0Qװ, N&hv.Yri x-x-kY3|[NFؙ a Ye.1 >-=Pmn"n0޻?& C"VBFV֢\_mgO$e HXZ@,; qqȵ?~v Í:I7"tVe5.֍gOc1'K_ -&o#"]7*W};xTpo hp6Y)j\b0u{`/ ~8zmY+$ҁR Y""_˴_5\imP:2@>Sǥ *zߓ:px*P*+x*:T0"r8zJ) TAP|AdS(+Ғ=ܟ6?HieYKӢ!ڱifZq^|TʼnAٛ׼_LZ]Ƌ%2ErvIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/disque.png0000644000175000017500000000322212014170666023162 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH_lWޙM;cuHMHӴ) B*dP< DSPZJm4--"(u9vmV^_{3;3/t]un*INcjm>y}N ԀJ ]۫anM_cPلHg6IS'<ƥ~]Ue$)ఖ K!\~yaBKmOنE$C´^ buSxߠbܚtEHS{0V現c(%єDS2%8~Oė6ޟ P$1@^+k4QVZ1utpe3TERA߿^2<|hRJBhhp }7_Fж&8Q*:ĎeGtٝ>p324l2pbqXv>Ak\RrG}ÆAGwal!R۲>y-.Sw]y(-KS::wh ]˥Wi"~B:.Y;<\ H%)GY?#]}oU{|6FZK 18M{.G򹯂P7Xq2^Y[K K o}n4ڣ]-L.Ʊ҈ ޲匀dẃv½UY±(t̝/\`ڌ%Fh^ʮe5[htf׏`;i`Jd<˵Ye k*Mv[-A\K(%bY',8۰=XbSwR”Rod\g~=QDl6\j{ʹ.Bi[|iFnS |{&{KGJQǿó&M3<χΚ㬦n2|{X ](\ᤘ^zu>]??Eb>!zxoP'jv "}62ڈ\j|,UsjY9^KՀM/&xO6HlN/"QBl~KJS2}ҍ%ϥ!ߛf58,$Rc[-(aSPKTυWǻ-Cn3+>e[6ö~T8ApxX)}ﱒu8sum IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/fleche4_.png0000644000175000017500000000261312014170666023344 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATHmTU}}KPTei?[}hmq!ڴDQeW0WK2d1 ,BՇT3&,Q(Ī{g3wޝO><<9BJ @ ò 2L=eYicXiaXv7- B",)Y\7MNfϹNfſw{ @ǂV`tܻ4| g2 6LE7R(9 @ ّѯhUʘjŦixnɎzNMw"DUTͪtɭŇ2 th&(]QyDǃS n.ХmdE˒Oò[ &ok6CA HK|?cyӰdFF-f:u•(9/'h]_ નGs2(ʀN8V!G[s#<޲E=6/>w]OG4l%0h6Z] #ټyGYWFc I0cS94O{mS tM@]7PP 4 #Yj%m֛ҳ4 b=I֫tDdIw XI__7k^:ESجs-=U&&&cbb/l|yb).` e&5%$8J*ki7ҟAC|t]m}\s0~K\tuk{Yd1?9Mz+"u^VFȻSiy!À0 p^֮L#{F+Wۻ7ϒ{N*q0 Yb5?~wζC,{v:[` @UNq,jsA`2gƣPZ㌌˾Ohr >QRu9:u MOž,}>/$ue6g =r~p&Be_0 4lVI?B~6O<w6ؙ<&52^6'KHcn,I.) \w2\րs@guwT$QJ$ŔfoF_4`?EQU "q?b'#ҍ-PBB )% Z :iXvKOY/nɌNv؁ o7^ڀ7ҽdmxUpv}IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_ans.png0000644000175000017500000000243412014170666023320 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH_UU{s>w:3QACc8C)c(ļ̃PRT >AOE4 EB0@hÄ 3qf9w=&p]{ou:9TTTAAH cF(„&ۄQцф&mz@ G#.K!u8}ZTA/^@eǔ6v9Q{FAq yH[Qtфa K8@ʬ]bJ 6>Z"adq2RuȵN[}Q3-6 |DF]M[9(Eh9lK CW3V=)ƒauuM 8\Ue /xRDbhVʼnhd.^3xZSuC`(=폾}w3AXG8>E'1;|Wwf-Jg.@lM|:fL4{ܧMJcMqm^/^g黳%B'~WYYgR'~S @gT#F=I*S03sH:vj+<5]Wfp3EԺ4u{H.@l{dMf@* '~bN@V&u(tr>Ik~xP6UMB$h/֏MkC)AK{~q/8_E|*yRe4Ba0"Zl9!UbUU+Q !-BبR`noC1q0[Ca!Κ*s\ 2 v<|l{st 'T\= z遀- q3{ܦUcY#}tB8c,& k'zA̽Sk:sY< ZJ]9lNˎ%jXk `[)`m+a}i-@~.]hξS.SZ@|v˿2SMx 8 D]"k,f1QZ>p ¹^*e\YhEq* qp69GI|-U[Oz o c$tRL} A'$GIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/tangente.png0000644000175000017500000000340212014170666023475 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH[lT06EBAB@D4ETJ}hچJ B - mч6Mڪ}H i˵{ 1`u9g}xcԇlY׿:kZpkvVժB[A[H}mIV^ >n$z9ڶMKhGY];{B)_@ßwV=vV A[$|4Bd<wvF2\INR4 Zׯ.PP4O0+(-eN4%!M7~1 i)SPJ"@pʁ]`a `@a/`GX:q/-|hi_Nvr%XmQLC\'ac|aJ,wdΪjT3)K Wٷo`|e<1^ډ'/("qG󽇿xw}\'N5Tk?wWP[آ"9]G8 uD '5`dZāxZ4g/̍OCl~1 OMh{pT9d2 %V5碬r!Tڶл^D; ٩#9 c"DsS:iwc)5*=XY%Z -Flx%yKvn)*ۜ,Č&ꀭذj>J \?AtOƵ؍v<ʣjA)( nscǑ-{ 6Ḿ)q) Y>sJؾ F14F@!4p¼T,P1?B唆J1?㘏n"t@iqCe26 P}\le֡vnsz?u"<4g $. z-b"4|D&3 h7DEq2u, /wDU_5_' ֜B||e< <o[$; " 8$^'@/9ja3o_E %>](1㝒%Ay^x;8[oTpPcT-.I"d55]wUuFp Q=WI:ui,i,ePgY3 @_Dsj҂30B -^*xVlcQu^`| */ Z\/?4`0(?rpm/hvO=ǿwP,7Z>U.( Cb_ԁ,k%g@VU?wd y4'@> owj:V'7\"RΨ[ :>W(AAbmn`f2w}n@5h[JJ* w 9p*ݾ՚{vxzNȝ,"GX앍+SDZ~KUZsw)uUp-IF?(p ꫇2&ktav=v@GhqxrFx*@wʿk\әDĊmK?́<{;]/HK2T8V_ZώvC曹L+Fo$.9)i˦JL. +ȂuCWˀTS>S͋^Q$ĺF=:|xotE6w~#wQzglbK)#K\ewVh%⛙;u;K[p;ogK{3=čM); zs:8=&IPMS 7O^t6$;TޛVZ.uv'[{~M\i~GF՞FlGG!Fn/aՎ\z,@훋]V{Xf.pb%WoM]}#qlN9y#nܟo]MJƒIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cercle3points.png0000644000175000017500000000330312014170666024445 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<@IDATHklTs{k;19HMKhRJiBѯ$J*V*5Q-R6RVB%"%6yB)Xc8kc{ؽv7Μ99CA'2 5l†1 6TP0 !u}Wj;9r]n_n(mm6 H r/G:z P qY"!N $XHeSVq\~5*K)l8qn P|=s}?li@+@yi9'}JBN]2nN 3fM2fMry:ʚ8_:C^*&\ YVSLxާttd̊+Y:MI.}f f: &KI[ L{7 @D LC"TDT?Y3" *?\}m\؋dn8!O2\ SBM!|پj6GY T:8wgGYH{u]E9ޠETBzFu]Ik(j*!MEד;e\;~KwF~:>ET]Kihξb}ツ)ty Rڷ/`}Gѫ2!ļ% v35[kNp{Vo-*3X`X7:o#xn#Iزo ݴeN;@}q {!H}U'ٷhu3,Fo+ +M%9w,bKPsGnFfLJRN0]>-q_@G)$cs*lI"K-G)^: ~ s$l*ZX[@̙N▷ KChǎo0c=ڥ ll!]RGxY75{Xw_ "ljOT  G"`R5M(_Ó ЅA]A-}~ƣGh"_(E#>n1e^ڄ׿vpE|t~8;ijDSżⰤř\uE*o5Cc<Ÿc-[xyqΧwhBS~@zUR૬_CU H=+}d5a] 3RX潏?lhIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle_rectangle_isocele_.png0000644000175000017500000000322612014170666027367 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH]l3sggf%ڤJBSخdD%%H "V"業*oR+UmU UP#ը8``cBbBD)6]l;;}x׻~Hg{hqoٶsжҽntƱ,+c,Nc[yCJ<<"YHer'F0 ^JKn5Tr.{\D*Pcշֆ=o' E h+P)]7\DUX;" :AttPBF\ P*Qg-q#\ߋ4w-S\O 9əA0ĪM#}]ӓn6`:yP@uD"`,AЖGBb Ғ\R\=5̩_s~O7bA>3_gq  y¨e c{?[X0|*җH_A#}Ǹ%br?zO-.(\EB}m{㿴,Ko~șև9:DMȏnQ-%O lhFAhh豞aZٷ ; ÛN]eZP\U\vtOStɋ~e=sxwM{ ]/# O 5u2ԽF*wT#@$(D*e"R)[0?XWIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/mediatrice_.png0000644000175000017500000000250412014170666024137 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH_TU?̹ή˚T nm"}4!{ !1}z )E$J[â5vAEY]%Wםֹs̽szns~~94o,{%mppZmF[8ac[(} 'sx9r ?œ'ݴ9=w%gP @+>ZX2ڮΈ%!j*N*/n`&a"tx5@OV0uӌE3Ι\Rb1=~`[ D\TNڈ"fpjt. g a,jhYvii^`12 s}&I 4Mo2j!N~bxZp&5(0l#_X!Bh ]כ#MTB#:5^(lhS!.Bmˊmڠ7ƅaVUx-_ջX7^ڍ"2bFyYe,!q "13^%jњYhaS't1 3||.rW׋zQ5J2\ן|LM]+0ҭ0[w “.ߙmyqZ;8 ",]̎njnMK0ON^cT_x^5'$9=,M_saIB۵wP̖4 EcRY>!Y ><] `=jʘS}Jm25"%cq2m|eu55$;֒Y{H+W3lȘ@zu^`<3;68]K[{ `S(Siܟ/=5)~xUgfAi;yiƶj#sȸͮUK$+[  ~KfX|@,nѵ}LW0$Y x\P _ZJWӸrlДF/t)}b%!˲{Jk>,yO{zAi?2eAf?iGIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle.png0000644000175000017500000000321612014170666023500 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org< IDATHklw>mZi/Nh9KGz@KM|@8|RS_Z?h(-8٢Cɶ R4AF٘:kZv';׀ "(^ʕxBýNiD I)~HM~OG\6--Q-R Xe;Խ|oun-JLY=|C ysrO`:IoY-Y[Gv-i- 1B:Xd(Í?ek4J!?;)T>xQq^؇?o]n/c55Zv"je\,#a)O]fY$JDocӣ$G90bfֵvnIhm !&{6[J݋\J,fz b7wWox gyC4"*=9;#W ~!M1~x},Q>jGk-i̶CG'HSmg7lF Cd/Fx{2<{UxB׍`iCc:#-C,`e[رr-(,T6ZJSa6lLa&awƀ7sp|*cj{~L8xeTcne|G?=OYQTt*04j(,ݵ %u N'8_;/Yʰćr*w fL83;!eVW2T!9LEҹ[)L3E$=-qBQOfĮB>vSKQ(+ȚA\e] _f >ie~s7,fikT.`y ^YۙkXDڰI>tߥnKX7ذcᅜx[LR*HP y]{$z|WպZ}^R}jo]_\4-G.ZgIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/polygone.png0000644000175000017500000000330312014170666023524 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<UIDATH}lU?s9-k R-C+Zu2E2YԹe.:e&eSX)Zh7{o{_oeyy~{>s}^1MC ueMCY2BT!efm7+iӶM޿l=ί%$/@+PeY[{Cz=/3̐;vdǵa~&\fǶ'h͎T򛙃I.bvNGWjFѲ \NkNoM#@MzN76&bssMI4%Д@e9yS[jpmqưRȇv2r5T(M_ZH7شlǏFI7|n'odt_}y+vPt TN!W'YQw&%TݏS;wc<ޟq<{[:-5+xUPh*c#^{ ڛL>5F`~IO!)i3&n1iDe!*V,'m{w\T׾9DLLEqa??2Ü.5w6H\R3RR0PF?@"P<, (f HH%s%,M` ۛeu$R9SgL:{l]9ݱ6ZkNO"c P6ȱ43Rrlu:s{f@ j`ct¡*xUrK~+zF9S$[Mz.~\0LWnÜрC .$fē bTNg5%pH uD#\s"JGdٳ?d..CIL}|/̽\ #$[8i9(تkːU[NEɴuٶ{|}#9- WIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/projete.png0000644000175000017500000000274212014170666023346 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<_IDATHolEw׫\Q[` b6P?`$ K4BZ"VQ bTh -JiP(%-]?h5L27oޛ7Frw?zTQbGU4Uƣ <@zTMHaZnc&F2L4탆im?-[*_ qU4~YG>򜪩ѣy^Mp IJ "E$ɲ롐aZ-i(@994.Զ˘#"l3yl<\PT@oXi3@`Y`t(+n@?rvc2 ziux?8ݿ}Xab֝ۉD%K KL]4krz)/_ߟ;!m9bx eʯw"OK-R@v*;X2elm>Mq*T@EF)IyL>ڼgf܁.%!%T7;U̞;/%f^%CQt>AV[såL e !@K 77-nʕVZ!Y2o0BP A"[bݦTnn-ZΞHm-A&h݌wӴ 0h3$!77ejհcϨ{9| !DW+kG~Ī=ZIgoanLT vyO'." vW!ut_}_Guvb:,ŗ^ǣyyd,۶llzոv#bz,̅ 0emwPRƍ1s  PR0*~gw{xf|ndeHQҹ>d1b T ,ޒ蹉~ cx <-كn $JG@I8ѩ.hJzC/6s]d|a<^Ϡ}Fgyy'P-ԓa ũn.1 p( 3y;%iۭ"I/d-(j4#C4+\l*F x:nn% kFz8P5u+Mb2`ٴ%q~!ߣʻ<ȋ?0ô-$|> (V '@Y9MZ`0IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_plus_bleu.png0000644000175000017500000000170712014170666024533 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<YIDATHkG?3{k .?@\ KWHa7FEpa2!nT ҥ2R#\|Y͌ݽݝ3μy}9O&j'KVȍ4Q,Qd"M@tBsS,:7XD ;)p6!au8|5EaхT2!DV$`6waj)DJĊ3cѹ=ֹY`)S`k٩T`D(DcUk[0\#?|Z[A2Us^ L; .z6i1L]|}0m/~g~US4IKt$Y@5鉮4S;;E˛_]mDǺy/hyGC&V _îe)#}L_{;'p<9+ꍋvg'plTvzh VWD>)쯽l1EGsڢ9-"!Kk[H%΁1{9ǽ/ײD=M5*㪏m3:7]:?XQ4yĺ o_t)dM[ IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/translation_.png0000644000175000017500000000253512014170666024373 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATH[lUgΙ ʥ L􁐘D$1dH0^h Pb!ĤhH ̞3gpfNL0^%er;m,ޱ,ű\,]lj4*Q#|CůP_Pw!~_yǾp :] =6{#]=@xn˶r[-FkM9,Q K@O,j4N90LL@ xWTm0#@ra0\]#alv+p:%jȄ+yQS󛨟h)hpG*̚L0F؆YSh3uBUY&HMrC0W01ZȂxyKĻ[Rxa.0M+xFPYsSVgGٞoA.a3zVEJyX MX %hXi.Y 0,V8ы|Ggi%Ӕ0EE}X3Rse )M9sbQ+^ޥf\ EXd8X?Zil|[;gq9bPs"MNᓏ_sLLܮ;xrHod33_$Hv2]s+CkMcc OAJ߈a4{/Kx$CCAk gkEY&&u& Ҩ7oͻ좣c?]?lܴ)1 ɕ}z5}MNvF^ 唪: P )eTqXx9}V=Ʋe(1Vg|UիMܾEΛ[_A4͡>-^.lx9\sV\_wPz2ra6VN<0P!X ;p_:&lP)!%OK~8@WȚLi1 ,?a޷U,Ǜ(qCVjya) &pIrfm[8߶ߝAm=?W|EtLV ח4@|qL3]," [ԥY?:ՐC YУTh?pIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/zoombox.png0000644000175000017500000000240312014170666023365 0ustar georgeskgeorgeskPNG  IHDR!$\sBIT|dtEXtSoftwarewww.inkscape.org<IDATHe?;{sG!ݝQȑI"QtfYQDX$AQ DADIfXieX׉m3?fݽ=ewyf}RF2R:#-1RZ) #lժcOJmXfwMtMǵ)tڦ]?7Ɠ򮳩U={ "IxB)mj\ Mk?>U-q*8 A;U1~Xr4sqwMg~踼l˝Z Ւ"iѮ^_E D)Er2DI )TЬ?|'%JCVwzC8hRckZղoF̛$w b !@χRi֘RwB'!$FS 7!%5p&HQ#=YSJH) Ԭ QMj2.:uѪ4Ut+ MQk3?~%k*LՔhbtr-z8oVW.^&P3A 3  þ5:) j`f&O .*,l6C_rcwV*Cᚨ!XP TL\{Đ_$ >uF3N9޸Zݻ6Adʕ?.mT&!Ġ遖om`{Xy\R._{eYJn1Ճ#C:z[A8Blp-7M)5)˶QOUIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_pi.png0000644000175000017500000000304312014170666023144 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHMl\W}ͼn(qӴI9JHlh%$>Pt"[w@]*JӴ )q\\%nv<ޝ7{Y̼7w{{Ι+=gׯZpxi[HI;Ih'&2ȲGQ<ϐQC!vjX pbks//tɯ~3W366$U[ʜ8jrMxOՈ#qː%Fs~Ύ1Q*޾l/AKFŅ~LZӴPZGmѿmP~t4-L4̢7'_x xM1Yu㪏<灗V^J KDu}덛i(IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_2.png0000644000175000017500000000201312014170666022671 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHOEU5ݽM",a`!YYrѠ/^",H +! m EƋ. O!ٝ*=gU'sgj%rʥ(T8Tġ"qKGLf8,IjH IjzIjl%Y{{ .m^v+@hsti;_~xaԒ;q:3dhbh ;@n\ B % b`,Ij, ~D `ScĠ$`%;BF<~Khm9ڳ:/D-9, d6VpN:2p6׶WK ?7p$9aJ*$–fNۇ9ܩS#ĉi zXMZK ~i<`7IV{R+UwJ0:"D MY "I=DW| !Iak |e)\yuދ0ŵsceQ&9ž#-ngvo{p89CC"dLvP*ql"u"T =Qd˼2f{᥿w([ZҾiw?M$%On@K!Dzm!+a/L/ UVU+#++g4Nt3UQuU⊢*Dy8/$+u?O}\ (73y Mc7'=eׇogwmDIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_div.png0000644000175000017500000000164112014170666023320 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<3IDATHk$E?USî+ vŽ$]PoA㮸nqYؓ$ aw^"t=3UU}JDafZamf33`fkl0v9IknR:W8￳@<|8vu`۠ʵ e %+Iqb֭c 7U`xF.N\;[eeKOCIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/carre.png0000644000175000017500000000275412014170666022775 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<iIDATH]]GuݴIiLdɂd1V*(RRD,Z",Tm,""X &֏I{9sܻ033 #ō0ՙ(TsqfPMD$q>%Q-ɭZ­-Z[^̭;>Hk30&wlK| ΫW(TKq(9&0#y.lj5)R ‘[w%1 B#$0%ZIhYJtoђ7‘Oe94pFsLi@3zh<5MܺY_% `Ncc#Q{OhZy Ҳ--Coͳ?{UDO)ygFg𭭅;ѝ6aKK#nҎ %%6$0D$P!cA@C&`L4*.z~s?}8ž UO^Ż/~(0;r{js M21&xP>|)`"1қչ㜘\cf!^1!G?Fg5 ,':{ Vpp9CbD'X*F 4}/>Gt,j"p"_g>FiN.$҃( +ye pˇ/ǀBWYl=ye]ۃIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/fleche2.png0000644000175000017500000000255712014170666023212 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH{Tu?{b)Ұl+ShBF-DFku̴3s?wvyps/.s0,Ӱy6,R1R*_o䲎$o;l\og-Om7uz0{n:in\>N DKn*}6't ]%@pozcҟ b`X*CIޖgs3C:f,A<G5cpʐ840 nh 6\8ҭti@+5BC)u`v 1[p#ɲKhV]v/rSc.mWjב҅ʐaVa*w3g'8}a )0-Ӕaj-H@hU@KAB~74\?HK"4SA5loKZ8$u'T &H#i)fZʾR4}+yl>b1*+68N0JhZ &ATdM,]AH$*z0 KE0hBg׭0 xQY Pde#uKEBK6 Cq~Fn,oURSEleXe˖b:F6;Q )PZ\ L&Y~]8:&2$VO^a%000@&X,`}lv/wa*cdDӆ NP(*x$52j8Ddr<XsMh;* ?ʶ&O̡Cxj_|+ BN\2DX=9i!iޖ8bes,{pgU>)uP mK<Α?h[|31Z6mk7Xq;`k %hEL;-|]SNѱݭLu-zZְHT%;\v)k5OLP*K.F|"?'=zƆ)K~)Q*h )Oڀ";1;g0pNb ^.cQ|l-ߦTΒ=  BvOh@o@Q"O*)򶬱u]RՀ͕4wEIJqL(7]w, &p\=̴Yc->B4'u=?UO䥦!ߧf]at2cu3t_+]TIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/masquer.png0000644000175000017500000000330012014170666023342 0ustar georgeskgeorgeskPNG  IHDR')BusBIT|dtEXtSoftwarewww.inkscape.org<RIDATX]W;?цMd6mW%J!RBHҪUBJT- R 5ᡨ(mBhæ4V[UR l쮽cܹ<cÑFs;|̽;B)E;&m%m[I['i$l>W.B>H꽡r#OSS7lk6\ | XmGҙgwmتhDJNziiP.+j֡ͅ P ( Ɋ]WQpD>'wM +6#< (!(Kq *fUTU&]]`:5rzPuA]cAWh}& AAkuavVNtWs(Hr59oK1u2(K m>̼08p/[Êң Q B< ^E!а~qvGEJ sx^;E9j4Pa*,  @Ptه{]+2AС;&Xew`B1(((%:3t ðu!Von3.x'0}9wE|s#(D;[*R '}@Vih]\Ɓﶅ[g𹯬 RCYYsy4{R)o+Ĕ|xRjs(0U7Lg&[-XVgџ8XzǔR +z vYwIW*/1qL q8g6a39(n8kX> ?S~dgާ[Wh:495}]:i @}ILLY #JH%G;|/CoB%&86id}-' %\+f$맘cM6DM27-xǺTnye@,>8K,y3/rд86ӄg^f ˑR΄02(Bu[_"պ&QdFUxji:B1u K7޽J0TDO=rThb"NM~xi$!%W߮8©U$@\$Z]e~]8sM„MN.-1_zF<ϫuD ѣfPA ߄0g qD2^ ^/3d |_ Uh|w u* ag JoAvP*iSQNԩ@ vRUcNW߼ð^/T^SR nhv)QkAS4*ZOx[Xh4J@ݺ_Oԍ3qh`tJ h6 MK >GkA;bfǾ7Ɗ)d9n_p+L\Wٕ+Gཽ# ?P,ɰ]}y{? ݉t_x5IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/logo192x192.png0000644000175000017500000014625712014170666023530 0ustar georgeskgeorgeskPNG  IHDRRlsRGBbKGD pHYs  tIME *'zΉ IDATxw]e>̼k{zOI'$@!TA,(ŀXP^E頨AED; !! IHo}nkىx*hV>{gf}gfmۿomۿok3?uk?q;[쟳]0E_q`SΘP}͗Lp]LSwoŽ/ݡ8lW@sO }vY݊,/8V7mGzpƟΘB=a%j#>HYle2TXT Quɓg#Wf 麔BVo2\qጥfS( hVWě69:h{^~O7>KiK{~U|aU7wt_>clg.+߮pYɿmpڣuiҹ s 腬ۈ (9本0FU)|gf97)Sz_ flaUZnϛ7Z U%Fm g.0)UYYrBj.Bϵ1~r-pOyã/)l$8mON+Y=Z kO}ۘ\}]>PE02 ^ͭ⮱&uG qs@g\ml)ݡզYAxU{0lvAoŠ QM9=5 le{/FUF!,* Ls5K_|2c/awBk=z3squ}[2ԅ{ 5 5&Sk_y U~~c2O3N?{i Pe]0L5h %G%;sܽo__xN|p5`&̥3;?QlS"_?Cku=jcUfVQ Z3:h3꯻ %k\7_s(wЯpͼ6( 8L9uGvl<ÇC1V QV*j;t؅m?qOTU5q>x.l7yy? ץw_t֍+cw(;K~?yc跼+}d03.`#?:Y1N]ss̫ıO{4%'}]-ja'Z` drTho>Box,}5s>-T+Ĩ*uN`\-. gs\ ؟v-Uj] !5MEm@)Z`Ysԓv՝xS&b6Aȣjy^Qސɟ6|?3S~}kj-^Aݓ7Ϯ긿fc`Imbd𞶟^zG=#[uoXOq[ܗ촦TUu/7ˇl@Б3om6xDQV]sH1tʇx:ިl̑;/Ww}tҨoleÎͭbgx47A#y?o(81})hkW-ַvI"$G@@]}H1D`cj;:>yC[7(9Jy<#U5 İ+u_;ЧCjfx=L&ִ_]%MA2[^:XdkӉz9G$A|ĨO'&]a݅?KdVSZ6#YՉaܫaUZsg64 [?Ym~a5&noLM(1=~xʔ%B8 ZK%]! :|ɱE qW}+td#V`G|Tƅ9UӿT͆pT͡+33I7WI u7}ɪ(y|+_>mk߳ӇIqcb$߿z |kޗ qRß+wMmɟ  =21$ؑyw?}gL/ܺ*t=u?w^0m9 " Xzsn_F /VovkDw^ ۣ揮g0)T{n~uaKX ^9 <&U'O敤W,nҿp}㮆R{Coa[*UJۮߙ0=n^KnꨚC?wޟ5'W/}}j(þF>'L;: .۞y1{="R'P8 %!O,iU2eUhy}> O\=nO-eCu g5C3޳3הj]О[8Ʃ5ih_Ў3~9U'ɟ7\|lXRw\ PSp[oan=Eۖl%ڲo@Hdp{Oۚy ĔO^80n׊bDFQq3sd#_̮O =Н|vmrWWɵfM-3g^}gnb80)cy۹-b%@[6+S#B€Ugצ^¨:*ĤVz* &`Þ"TsU ?RJ05kV1U9~]Wrw;S'7yxT{n-V_ЎoQl}" hlHm}*F{;LŃ8y9K7~4aÁ➓q_S/qKw-k:]y0[ECxW+k|5?Е}=9/"5@q'^HvRCzϜqǏz[Gїy#k6x#$a#;L B5), &@K=ۛe)0~`{@Z4PWeɣd«BxJɹ|g̾:f+w^iJokm/^vSv9FGff&o޴ڀIU(炞־g*'6п^̡gn%0%~>{թcHw:XٟRXv,:x֯.nob9Yȑ{WfeUe9qVUL\6uwͽM:zoSܚHs.Omy׆|ߙ,W|HNZgP'pb;+s$ǥHipWco(Cr]N"+$Ov\֛1/[Clkc:s绊ݳ&\Bͭ/-|ļڍ3/14=>J1SW?6spį~=e8Г۔ 57.{w٢\y7h1hcpAf?wTm?~Ek\C5LqWssMDںlj:DՊIU0&~-RAUl ; 1{,%Gz^S@ɾ6Ps@zC]8cM^@t{oCIzqeUZG^j՗=c_Ю9{k@T@aVyVxrU)ݺ/@CBPЎ=Ӛt䷼Mm /_8&EV荒ܻ0^a9u칕]3uU_I*Dœ1;&x;9!Bis`WpMo7 meN}{PVf.]L ?!: :ŠwL ~L ^:=2fUgs65\Н ^2|ۗ2dzԨ_I/˽/"[ZZg}X%X0QUY ,, )"^UrH)kJoQJ\o.$"®{:MsfR [jZ*zzJX+mmjT#ɈY, }WVZZ2 ) '/ h7S еk% dYM6}^ԶkٿO^+c7M?=h!ްtz}j~qSJʢ!L '~xH7~8`șO߄Jhv]VƤ{:G}wmp7lK*5|'2qX=59 LB1(L}a|Ug|%_ `ӺSQ5I" E"sW#GRö\=#kTRTPC!:vc+V)Tg6bejaX9Cz(#XTA¬ aQP(:*1,(o[ ( -&w.SG%0Ac~((ΉvZ߳!~p΄WeQ1c޾}OV'ݟn>!V]51 #j(j+tcd}{Tͬi^5eҕw<2V$_rwR$QР D8qTa1 m0C%1ł_z5Q e0H=w+ k\J*<(L2h)^wOFIx@ S%gc 9W{uvJ2p[GVF%e(8KϺe"XDʢf,F%a,j]^\%T `$Y6K!1N>=җ?=s^g\ӧ;vAM9y=ᙄABC+gXLiŽVQ @P  R!7jvGxHe="X254b'?LG9#GkգGTaAb LV-7vIrBŔ_Z޺x1HRAc"D  ?@U@TBN;oz=@m,L5BFhU P^ڨI=r](CH(B J ;C؜D( ARu%ն ֥o dbQи#7Yx'%?#t܊·*T1ޥ@iwGg0lTwljX7{5!ZՙߐVR,ƴOc2a5D OcTM6A(6mmS,:i獟0οb~߼iɳWׇWql)SQ4 XWkr/o aPTc}IX㖱"ւ?V}2y!PC(ݑl2] k=} *8-= Uʤ+@ dC,lR  2)l8r!%VP(AL !0 @ C$EmKco-7~(H]h f_@[-xbĢ l0XANd#\&1v=L ӟ]U0򮆾΍SCCn ( M:ੳ;\obE߲x" 0}s^{O;ym7.$-6Z3>P 7l;Ѳ(?&5 IDATQ7eB:l&#GFfJi[UoWG&s>=QgWwWxd"Fk GnۧM u2 D87`!r&0AY~BčB#m~QrA(%w9mY{`W j{ʊλ]E e61\l'ZO8ُWܙPbGj>[j8ݩ K<֐=z TNys. U=Wy/tޗgWA&y1A(;0(m\ٓw}ēzj%FXPj FbER|o&a]1a:gRgmTaB#iXG֦}>ޡ͕UIHNG׮"ИKaf!LZP 8 *[P!@H ^#'(-j{$oT* Ecd-75OcBviqgncjohJ| B.CMm瞰g"iMTW\٧ï?Yz*rݯxenzӪӇ4(XTUw-;![Ad4}Jt/IacDj4dXĮd.a-"'|>ұʊxE<tRIOEZ T !(ΰ{HW I1 X%^gKhP&Hj)%A"L@X*"EӍkthœu*)/n]"A~P;yUk4B~ĽJR{!+v~5 ɸk/Ch"^.Jz4,Fܳݛ \cȴ/NMGߘ\|c jFm;N>!zTrzJQQ6;7XRJX!6TBO G,'m!k"W`@hБTL ~tEmW\4᷆.4AD ;JE[%pSDZ 0[%*}R4cjBAsMqch4U{"P#!3~A(pF4ZcQ{_ԉc((lJrM+ӏ>TMuʐ:+0vkI"6 DT]6=CEsպT⨃7=yͻ;2IG4_#Fa(}**) {:2PKVB35$3zl%;a'5lz&b_LN`v+\ V!ehŞDL vgNJ&T% [dGrD%ǪXUaDy Q C3Ӳ}bPa"חQj@sB2dZ(ZbT1`ATJ _W/Eí%Wݛӻ~_IodD  1hK'zW^uԖ]U49EyI'Oۻb1U%1~>Twɶ³Ny@[R=fPFE1KVA' ٝW )@EY[A:|vSi TJOʶu׼UTGv̄ܚT&KOGg{rbX!c0CD6dXTӕ E)q^%ŢP(ԥ:8 Ui<6{@=>"(TL]~ S "M;ڭ PP`E_) LVMĽh/+ !DĶ ǖ!8Wq9sn 4:vJĉtRLy+®aaG̺fl\!`kiT0Ħ%F=$1W@3`5 'ITFoi8nIakw{&4=a蓄T<5rJ7X|>WVUtn[oo>=ϧq&+S{R Bx pN!"m9AEat:Vmψcn;\Ӌ]BaBm*" $AX&F쇖\ XJTLaՆ y¿G,*CyῨ殣eTV1G[)ЫT㾸5B4=1 '")+TX N$OR4 ,TR9ZjMO|t '}fO~LGueED*XҭCj{#J<7rC7>~q}LOؒC?;=ׁx _ `&&2|%tt+@B*f@-xfl(¥WVA "H;71>A'kN"PEVEXiHYB(CfVI)\oe6mT`v n^-k¢֥UT@Qvg>޺IX\HkpIHB"I-lk* I f3 b~L"H&e+O]bk4"{{`PgU}s1f 4z{K0ez0k ?Жϖ2H)]/\_O̾f p")tVvdv7wU\WrCIlˏ+`;p@)UJ _~EWPR .k/XrDmh5' \3LYXvLĚkS,,# ׯwM#gBJYr^ ǘ::ayKcnEXή G'V A1 $%+u>|M0â>Wn+As"2cB*iX)Y(!'Pc @ T<BRׇtǃ*j`Da5'3+,V!!%*B,j Hq&'M!"pl7Y S$U:Vw-;I"d]RPVI{`lB 3[1va1wOm% Ʊ~ShŹ*Dn`B0?j=̉xC}JE6)]4|?|C12LESCeoQJ㗊m [#Vjh銘y=؝DIE >"N.Ԇ_..bI7#wr" Bm%v4jWrpmD$Au݉\g޿@ݠ _QHV$PJgb~:8fBܘlw~LaE{Ef{{Ӌқ\)&47T ~@d"-}z}_>1VfDD,#$j\ݻᗯSf>|?DeE|E}ުUOb&c BɒVza+`k#\ \H aJm)b:IcRkr(81ET]Vn&7E(R;9?6 u1ۖ8mb ]s FQ3?q] j!_K~ o\]U.翹)ٛa任QFcK*CY'!0nGBT o &c hW#(Q!40XBI~1a1P("j*8zhH@F'w9͌5 %w) V}Su"<%itDȣ"g2YB6b&o| YSHΎ}*` Z#7OwVKb,&mJ !Ő ŀ ԮyfF*b}dت|> V'Z}P9,qS;am.rW LjkjiVPMU"B20PM{C20Pdۗ'H=Q;tۂy_`ex$"_ݕL M銒*DJh{ [;lg@|U5**^Խ6mwWJQѶ* .+G EH;dI6jԫ6~uۧp UhTmڬH]^@j!RGzC(IBqc|(6KOa뺊$ڂvUfdO(B(Z¯Z4xʷ(yLQm.M$pX!r"DT<EOoP#ưj8_DX,ɨHBE$Wd@ Aana@zJ魉Kݛ'ž@߫v@V?/~?!TVRb!RhEv%p*OWO1NC 0 8^4':FMB:Yl5D(vYPu_[lP(2{s֮%2u,9@45 K2e)3w?@Jb=̓Z\{=}nW3E(֖-O|a/yL&߇'sXJ4weDi]}J0Nk)ؽ52¨ Zpv1gE_@]} h:*o ͗.PRwqQOo>t[r|> Q'|b(d k e(ʫ1Epq]y>ܩƞVKIB @`0$19q ɋg Ë;m @` $?4@Vkh9gsoUK`+O_uݪ{V46j1s646egu IMN80%`P`WY5$E )Iҵ2ٮl|+n>zݣ JXۍ1*-N0BB=DLj(3N&[9 }D\k=7\8 l`.1>Fpn2ybl2hlG7mۤ[/ݭ<3gup? P0_+8 4hߟM H X#AX.ñ3nn0X RkU^%{*S3@:ޝh> IDATsBM% iV2ZhmS?lmf +IIMH#h J^C'Cq0:%%UjzqӱAU=#m?() ;1զfwtgo2HR ʙ + ß}ˮ5`E۷iz/pŞӢ-',rŜ,n?4(Ρ/Sg6x/z?ca@ZPB=v`A3mDDK IY`qO}[D&yn IL{ǶM?+G~{=xD$J5[j_v4B4#bw ¯)(bvC4F,lV?dYV(>h`>p .!eW"whRCP '[+voVCib/?w視xTP̐RBphBp W>#O n[:ap}p+]@uu=!:P nz[*+u.yf6*.jXlB#/>/uꚬcyJ3 ㏞/ ^} S^X|=?vggxd%)C cFUWt>@] EtǖlT@QDHawʟT1(% 0PCd߀n7G/a=;i2J7'*֚Mﺤ(S.O(hIXhȎbz=a z9HpO*e2 j i!0Cu;&& JX'_"A`*5ٹyC'>JƓ*O^,F#I/?C݋K=@$OS{ ӿkcEd PbK H/Z#+Aj@ވR$Y#A@B%vZ+ Hè||T_(Na ^ ӛ!!/oWlv[)~bnpRTh# a? %O gN:\?æXBBF@JgVbԳY5y!fL[xV.Qx3wʝe6yblnޠĹ~܉^`쩮rNL׽GO^p7|֡M*״O )Eap(5'9h@q+H(71QM?D."8{ p$&%IX1%Llhb&'D 0+s(geOW!ܺ׽`Te|[?|ԏ\ZSDA%Y9({8oO&Y"r!(wA ) ;6@{3JooK]YO?GUM$v;)I޲nhR2Wy8H't+ q~ - (ħ cWX7ԢcL1\5 9t0(P<8r*WPLbK83$Pc.*h659QãP)PnLN4TEqNfj0]H$"7rͯ;g~/_ dZQEG&V[sjPId#VR7WzHl(ԡffF_gi"ԑ㰥2P($i M%,+'V֟CQ Ba 1^3ua_-nwΉgVZF#Wÿ??PQ ؟?ۤ<}3|N)^>I !z>AH8+`%BL@ӷ^OCPU_7H-عvof~buw)Wr*؃B^/LO6hK~Ks՗ 2PEȤU""~ЅF~Tz>[ACQRoP冗sP K>iP~7Rhz \rO8Kk)^h@.qāשIy3Ź+fC5Z{7,0a7o@n/'+ (!T;?}]JڈXm0~>fc왟,R0d*QzEU66kCTL5ů_ۮ \8˸LR:.52gZ'N:!b|]ǘ8F(wBS3z2V"+n k&^0Tq􍙰ط#)$)LCTl5 Yf8<ᅾ9E@<$XhX2ٟʌVoc=ol+hzyQa3X(q(@$ 1 (o;$),"]x C*D S%tʺ:pXbXD2̴\9=yvo{]?]@8mt&}p3"!1ޱ 9- bɟ%m7='̐#mEšR-#4h$rDzD+j5Tw_u:rlĆ۴3=xva41O8Nޣ <._̻ڿXaP`Hq)|6DN-- o[<*E˅rn5Wqac >B~xEY_n.3xn_~n7U2k#ݺzKPͶpa?Bb6'Md  7-iV&zLM (B$F5S8C,6&8p&$'޸vvl[<[0ءJb5c@ykI}NaM_>wь^W(Ȥ) {&'@]" ͰW%):)pv:8ג-4 bA>+ =#hbK?iȈH$ձ>ikETA$u ا,iӄy_/T]Dǚ>ja< JoO}M&tkOKM񘂅JAO"uh8`cIÆj.Vpu{ϭ(l^oO}[ t2f# pDc)H*fmY #g\(!++P1@y.P];DG{NM o;|VPgE5ԡnoi*&X y/x)m弋]!tphpd&V7F4!9S (0~өo}杻Vy|ҢRљ$6[H@4Z\@lS] WAI 1iKUӬZ? -@#ώ Wva-7:YNrVx}_@/{!Uًxg 15_}Vjb9,Jy̹V8e &p{FgA=(58Apqr8@ @;Cu ЖZAWnMn#a[#li^ud2^'eUwQAdaElrDADV^:"9ui(BJ7sb33'ċ #U%HڭʏgK` N.۫) pyS5^8*Hh@Icz(tnؚ\wYg{DwЫ%Dn #k]0:NJ8~ǾVYmLc5{bcxH\@XVop 9?a֊7fxip mhS^7 ӡk^F Kld'T6:0,| ;%BQF(}sIU$cZ(@=WW h@|y+G#W֙ uJqAA-ژ8ĎCOnZ%gkYE :gW#Pri~o B&Py) WSAiC]M_)Q LKr+7B6x;Eű ld",DL"^=PXCW*KsZ10<}q#ZK>4im~3ښ3jvЇĺBК\!TO4MA-+ĺ"V/Q`)oߏ}[vEGNz> 3)'ߍOR33?QVl @^\V81x0D.喕J`(,D:zrY vHT`JhpShSQU a0OM'G$PM!~9~ !Th`B+U x !! w+r%ZC)1}x?8:wj6˒C**0AHZ-4'&*r#' Lr(Sk&a #idZmEuܐMLx69\xձ=v E"ſlgF Ϲ ^ݛv /P ?:&x ,*M7b&ۺ*V6pNK 0Н̴"$$a ,3J=Bҙx!#+d)BuT!P &S;gӯ 9=E\7bhǁ5 (LUC$ P?$ >>_޼|v~]+"h%&iEv0P SۋUfsM&'Q, 0C IDATiLL"2Uق-V0Bz FgkEUˢ2pZ\OJ,lL}͸dspC!GdDEQ9J(Y]]heVVR=^6=XB>Q}EH%ts= 7сt"푷QAU^*bOޤ䕢6ySq`3rț09C#$8yh$Sp 4 sbPb<1'+`*\Y kwAlRli]mS4'f`Nƈ`8)\s"[1S?_Ҟ5úEJJ$xkT_:uV{f9k<?~w.|02reS9'RUeZk̨J2LLD*ҥg7<A8te* '5%eyWRsׅ?u/:2Us}f\?Q?OQRPDp~ gW1uK adCJ1ܗO+~r; -FdF´٠.0& 4Q_"iE;UA&+0ZUb8l҈+{P,# G6tAPo)$B`h2Oc'ӧ Bݽ˴p|kl]:ә:?(W;rsNI56M;%Ya0Sn JKxb˱nk>qIdU|v czht?w]6(z&t'd: $@^;Nk*SE%vˡu#,zea\4,ӫg77DXqƲKPLG_M -vs+F?SJL'* ;pvǖP?/ 𨒟{ ¡& iP]/,ψG:K_P: dg"Kl[``WPĵ5R(ax4+Vi :b?v Y2iijH:l$* eiH^sNP|^yyNϥd1nׅF?*_ " 5Ap)(]!~ѝyZN@)q$RI,ϜY:ЄIjM/p[ٳedv\%C]kn՚r h L5eV :-36kÌ^xӍx\HYOW:Db8h5ρ@( FY<+񍍄B#ez+iBdg*}&,K:yaŰ\bO0~UOVX)dsY+ VD8iGv]83ܮ`Ŷ^;"_~>H,ZOCJJv2s8Lt4'|O-J%=ʅ^ ъKڊQ:ԭd9,~t 6ua3Ik VǪ5i/'7D9W87iUFA}De^A7}Nf1%;R'N N d l(3HS_׏~%Vv{J? 6Ƨ]e}=7?u;Wڏ@mS0u$w7=2ڨT^Yʯ_;}ک 8[1ؐP=[@fmRuDN:d'R%PII{%I6ie59- >pP+04.>Aաg2_^KMQ5tezNTK#8y0'#TҁSL3|W~+:5р!3&#j{C!ĪȩnikHCoIìs4 %Ԛ\X~nnI܎p)6r +g`bHĤbB*};[1JdTq paN*0qC%~NN&*Hm"e26?6xztV2b\ޠ/ mYb` cL3޼ /xKq``d^_b̬m yڹIM2Hy.yifx^W<%Z/O2C b?Z-9Â=G<+XSN!54.WrH P%%"Iy1 /*=]_R%x]zd$ )H4GS}oyjӻ/{$^4 )٢[@5J<TĀȈI N" !0BG7@&0VsCٳF%0C%D];k.{\zEQ䐲E(fG4J~:>g,%GĻZ՗>?387[m'&U}Dcst8"O(TTtk~DjAWBTwF6 ;"X"%,L΃'NmXi/:VLVXY w?OpV,Y| C_O;fe^~#/DU=S4c6Ëtu/fCISǶqcՏT#9ʘ%FU"Xg[?jtFڰ W{.ݰ ) ~~$anAH50Jρ,un=_HJ}N{`-NϟŅ9/|Y4E\5N˩:=]ЃuHVtku۰Mqv\̦OdCDj.c <ֈUu@+!U¼wv2IPQA ^MRX\Zè_Yfi+c;Hi%.$B9Th$)ڍ&xxh6L&Jذk?y,΢%Z: ÕT CJ$U''{juwQVX̎ґ5{Ʈ;pٮ}x/׎|~_L G6;-?čXFǒ5^$5 RQCu0L*g,4vYt|*Zk`H*@y~MLyNU?o Y/9/|hUgMISTU g"Lռ}^2Ď0VK%Pؿer)n؅m[h03 [t˴"X{hиfUCks9ʲ]X0C ؽכcg`:8n>HIUQ]W%-\_~p/h[zZrb ђΙ @B:ڪpNfh:*UKl\oJY(7@5#CZ@E[" q+\E+Y527@";?HˍDDzNV JTRMLwOd'a,,*a"pH,bԣ s杗Cּ}#3Z"xs#+8O>ٹ;0FW`*g,sHInQvs̀aoWRXp$ˆ:s!qZU׭iV/"ekZb$8dK*ePmWM5嚒a߻'BqV{XT5}Btzvq5҉IĐ|LG6yazYbxe5{"mᵊi92b,2.ΎڒH!8;tbҺj>Eh$TmB8XҮ3E"^{[ޜ_iH!@(PC}PJaY6QLUgETHnFv<%D3(۴0Z[ Fန5ZR`)gly%pׅ. -tMW)m2؄lz %W@af/pWaDz_dR*aKu̢㇗Cwu+sX^e߻-XR.َ^4l=>{ȋڔ~5zRtPGX_뉝ۦ)txLuxWQeZqƁ꺱` NVy|%JJcg6dॣsc7?蓕10O'O\&wy V*(;^篥؎[t%>tZl}m9BHOk5 tv*aᱯv:O2Ilv}1|᭯yzyMC/4LۯurU(==srʐhp 8n۾7>l$Jzf*!WJ>(<=UH!`R( H}qfOm& !Md!ks X%ePfduӛp&n.r0XTѩ~ \ "R -ܨ7SߓVqS϶N@(񃊑&QoXOF|T]u:?MZ(0R@.Q@0bÆR8k,gl ,E}Sֆ͸Q|Oӏ°Aj E'pע[Z?btLMc}vl܆|o@mSWL0eW91r"ekkl0H8%_}2%! *aG~~cx?63 B ,|ԆF3 Z͓<JNsW$=Bv6n~@pKinxY̒aS]_:6 ZjeEA1@3mphAAԂwqq -2ZNˆY 3§7orC ~v>(o-5TɐzC8#!<ݐބ"R/*W"t> ˤۜh< ԪX>"9]$]u$yo!WpA<nwt$MZGhXF2%Ի"8wȅo7}O%>u#PqZJ!Qe)҅%_IrvJqLw5"d:=ݝSֲH4I㿾g~ϝ8a_^'AqwIor%8f/9X 9@!IZNC`~x],d=H #p +^\OnۨxQ1GI緞ڰp*R@F?>ħ@fx2›Q%@ ҄LVa'<()?kfăAn'ϯR$!jf)g)yOS8gr'h0=rEg흹!0"7ttkoF{Y#it>3q^b# 9s쎭brwl/'(3nnA|真g(EwwMOeEUX0?I'M2bRf #%ɳOlyORyߣB~¦nGb}G_#}1abpXTR5Bzr•.574鈩tlGBѶ)!#J˜4dJ6RiHD;'gC'˓V IDATܙ$:CT@+ Nƙ:L<. &8x?'Fkm0=6 pƧxp1`8ts cr2cRYraDXKsMETcFdZEu$1B"_{}uREkAX 2`f&lU01y 7 ľs{/ګh,).*GC#S d;a)F#4"xb4#YcLY"E7$^}*3{8j@wQ4*+;.|["'[y*6BMO@6% *6%Xa|x*$s/nF V`'=7@H(&ReU Q9gEBBSdvEYxmm>z ^AjU于e';ls%"{.%$7ХMsisnQ3?荽0v[zݾֱF:4V"B`+1?O~tl|\XY;)I?9_ "QTj(Q =^"wg'/v OA$l_:mir)oc|Xnb y"``H$*}1]Z.JN*vKFԣp$%R6OGo"M0?Udh2j2L7l˙7j\^p@c 26֪Z<4fW+iuݖԅ-(zVY>L#&#,W&fQ m& 1npex +C2~++*8~9F+l 7oߏ79ZyUp 8w=syL&[!G-F蝟I/lUeU$" A+ &PU'ePL#}f> IlT(ݙV6Ӣ|-x /w"=`/N9#U߇Odfw Ik7B.?&a@DYQPd!EvMOvۧZn~ϻ+gxx3׋X_/#z%JHDHTc^{wC/^vqr-sW^bH5mJbR q;$r͂M6oF'0mY7I|Zȳ ["(Z[mmQ͵֭}opvu-AkH-**ni,H%Q 4$5[4YA-Mر8uunp}T zS>z! nF{ dqDc֣gY+,`tYHBc, (ȰkLqm]ۧ;ok~-oiks^_^ ZAZbTG1Cie''{q{#}BJ*wyzۋkvexF˭T@<눲"X_//׫j*Tƨk!7iJAm#^ sۓwnߛ;"=USS3y&h֧ x38YO^&J-!Nsx]̍cxC Ԭ[)h́*禉te\V334J8u񈜺pD痯"u_.U Va~?bX-/.!lpfgnd3&0Q ['%'}2H޴_,)~7@z<9x$fV_}ngI Dh2 t~zr\nkc(ˈ,XvPaP֨* ^ZבZTD}IM@ӧg}s4u:A=Ou=HtvLj-P.nI8 Hf9)\1}JMՐ<iS !SO,2zDGeiqΦf1,X5` A5庮/ae}e TZוF]!Z]\w>q[B&Lp ]刴/MgfVwOu$mztzwyJ9X!%=sdc}\[:VۃN||Grݷ@i^oq&XނB%ݺ^*@DqPES_ǢkLzj!(b|Ϭm𱄕wMY`zIŤ58(&ZF7}~͸K :c ZYtj6AܳE0BN7AJ-[UeU1-:BE[{`4pc#U89TUY(J-QjR#ZԐXCRb]<7yGoF͚P?Q 3PCIw`!:$DpR*B(a.c#Qz{@S 5\3~,( Vۤ5B*0b,?ߝjLNS) QQ*ZAEEEjsuMUuN.HL&gVya(:$aGq'Ϊw,[4$řÞ<H3 5]3J*`(Z[&5kMέt'@6`y=D9C%XSN Sy1A{U~Hc%U@ol V1({ZW1V*@zn 5g,&q]LδZg ]-wQ,ܞ ,W(b< BA8i<#] K:Lv$o~<|A^Zu@ ]^cT%bfrçq! cQjR{+U#JE#U*cm=X`z|~҇`%vg:'B֙Q3QZz[Z7g\n*f[zM: ?]#G\j$k~>m!ڷW(SL +y$,`ɍUErGqOZb*u%2JvSܸ])]6!$Ơ_wb:Zh@1,`H1Q B:7sb \)=! ڞ'ńk} 422]-A{k$CJ.hVke.?>b@Z}Rc]jTYYV3ak7[)ݳF'IƮNVخwuV5(a~TBthx G?tyQu*U1_δoOB2/&E ܨI&!Uo5@.Kmzӄ+mkISH ͋iDhe\H;GT\! DM*~6^$ݳr֮i!V 1fǍJ8qFo/%q֗_Fk6JR4oUjWVץVuGE\\TbCNh[Gz5;ovg.΄%yo( _|@,?衇?r~z|n_񾻿gTumNkN(Оn5 2UTDlaU1w({+&Q bƸXGnnD!"D⠒XI=?fC9l(ek4Vs!qNfKD۹Ov_izk u薋 ]@:/2!EvMdG3NޤMƛ^B3xZVNj 1C7X&;25-oK-A|_/5 U jǏ6}r#miX$fĢ J$ 2t{ۿ}Ǚ]c֕8ʢi@=w"Ͻg_,?ɕ@<}XQPxu_u]Iݤ#Gn^ә("1{Kj1cש8_(:5a41 Q+@:}A?1e  ˡe%QukZY&FyCU&X4m@E dlC\ 5+q~(cNaYQ }}7\_!`G$)rъTAE?]ˋH:19c 9On<|%`݀H6ZE@7Z+`Oi @SJ0-j<6u6ڳϝcC }Wtrz-ׁ!&I` "blP M^V"-:Cic5oi" Z;g?#HREm+=ke%[$Y4u Pi*j"՟J.нeR0^soTyt DU!_xӹ/gܚS3afMN&0;QV\]弄I,@] u)c^"Q&RikF@7^}ͬ\q՟9m7@ĕcs>v{quJ_ܒirϴ TAg ɛ%#ܨ7IW+oF>G\/ 6nKuC҄jvjfdZ1U9~yދlΐMƷpiv(~d:N q\CJ t'!%=g[_tH=Q%HAXƴⷂ`c}}w7AC,P24&Nj%u}\]kZ8RV IDAT Ij׭kjү%2Uz _443D$"X`6ɔFpb`rb=tRr.ѸFOa5N]o[U$.=/`uD/DoFcFԨͰT`'1gGUdyA+n֒Ǐ- wyPfYXDz+[$YKlR@1mN j3EVJ\hA8)fξ㹃_MbXX aB[On)"D \1SNX̴|(Qè0Jz"fN02hDՠMe 48c@-=fg4c ̜(eRZG^w^ Hw^|Ov*7EKRM&d;v-{zQN_Gl|ӳF!<@U.$*J* j T"|f׊lsS=cOaty/aNl6en,o}ddNnM5AD*[v4DsEك ܞj-{+;F S*WIa} Ě[o??K'7sk>3֜v"eJGӍenGB[̅[rտDh8]괛\vM@=o"4VQ\+<@gM& 85r &AC-4`qwqm(FBNd+iLR?qZ[el7G~ ;i&INժ'W_|ա{Ol|&Κ 9DJ*#ho J !eё430&׍vsc1¬L9ZV-50Rٹ-ٻ.B S-Oi@cu67A4?LBkjR@Z|eN7_iͲ,XjAVH pgXlኚ1eͯÛ讬jbPV«H{@IӍEK ?臦B ᅂ]e٭SGw]zTˍbHHLC /-ҩwZ%0$Y2BD6$geU23 [NUͦA4rԎ 299&awgY^%yGnnMrU ;(j|'W_Ngj{=d~Pvn=l oY5A#>vd"*vєfhϥd'TtZ1z%nrvȑz?.'V]PҩNksdYsS]h{Ei衪lyV/H V)ƖIGL os!$F׾-6iM^yv oS!PNhUjn&CR[yM["" !󾣌8Fn8ۈLPYNY,ӟzndA."}۽*~JQE]BB60o}nV$^ !$T(Q١a"tfN [aÅ#7< Sq4'p $98T _sI"W.st_ !(+,3KnW𠑓)STu) ԝOu<1,={]"(ЙHK. B*_`^BM0jʺ2Va""/O|΅۶-._nѣfW`TkW2}æ%i2,68Yt+zu(->85v:D-)@4_g7w GST&;e{p@x#[^\<$8 .ܘ}qpW0#jf޿@H!Uij|F|6璯2XnBwTb XD5i=AؘBi7_I=QSQ Զ=&Q+4Rm=(Ke=lΛK/ ڧGEm^@$: Q mM p杰]!ɢ"B<ζvXv=QQީ4/ؙ7<ڐť ] 4M)C58(Or^ƻgEz&䑏I7h#b%TBikj Ü#)L[4HLM'UGa ,+&''$s9jucaoW ~rQ ;ǯߴ4<.Yx)m83W$) &"툼0D(kgړSrh* h+vK8uц:O`We14$#'fIK̆иx ^vDbmC9Y>d#g ܚ55!t}ab93 iIzO$4-ruHX;^CEAhg3y'jTVffa e~uvml]<>=ө비 $!;ś[ACG Ƈ? B߼K4gX9 ]aƍ$q9*n~]bيNSݞ8HC; Z7s$tP:wbjOʼnޕX o?y۲>#nM^]65X_kDZ ]ѵqMn}9bwIRUYT'26OHiZlͲ0kMɤZJ&4"gvXa*d{u~mv{:K YYIYXbpc{E|tϒ|cx?ֵ? j}]/y}9XZe'_EcQltHak>~`j>^\ζ_bD$LTת~I= K$UP#02Z*iH<>y+]iq6VDf?.E݀ԓnzx _ľp"X$$7E2j>HZyYig4tS]$ak2zVڞ(YmV!(EpYHUkf1(yE) UbI^#ؗOf&/#G<ᇎɅwjz U7>*i7#M' t+mrFH &fX=,scH=Qt>`ő#d=ņvD1kS VP3k%7΅(h=%P̅7y.- ]FC` rKe1.>'2ҰA7%KO/Ʀ`kwcej2 };0DP+,Fm4_E  D9+:|yz;Z1͊Ui&(1rZpf1kf?MI0o㋛3;}z۲lJC%ZY|;4 v,MEG-%;{ 1l3yܺξld-fWVy }#[K=Lmk"AC NFL7śvt/~> n'LzaJ$./|_ ?ǣ![M 3(M7)1Sw[h=9mEXQZ-U"9׼ o8+hEouyq\Mo:,sI&p1%F[AȄ7ϟ_eEjGG4Bn2OC˗=I˞֓ P4DJh c~Qg֩)trgj{;h=Kt;ff*v0CHY9bq?Ro޻ $x=#!YZD7Tv[/"s0huhgT;) oCG]~ם9vlB,/ ?M(b8~|g?Y]XE:܉TˤmX4˄NرzSM!ڍzMà,ӷ^wE(S928 e&g*+FLud1AbEEE+;?  :I$4J !a<|KoqM ,(0VxHAn& agojFnMBRM{`Qp'4YpȆ%6kӘNzםo4yFf#HCP(iW>xIZo&=g ;ο=kg E',h椺$W^TƓ1!a";nP5̖槯cJT$\tnwv][G{裮32>HQZgx)6Z/թz#)"Q 0TC$iwu b-epsmS nchh/~vo;X-ԛ__TE>\ qVH`R 5oݏm5,JJU[.N(Rj;+H&԰g3*Ȳv6E])5*R{<\}t`4:IJaXHPL(Z/[lʙTn;&4^GJD'#hh6ESk9]iD6OYg1@9J7n?Nm̆auxi$uU:߭,h;~BֲVXKI.):ҕ[Q3Au4#dkUss~ae(sns["6J"XZL~IF̰yPGl! ,zݳ{. +\0[K9}ӎ)8). $VOs f?W6TYWˬ U! , [/5߽/2δxJ,U椪wgULTe/Z'4qR(*܏\Yߍ F,| 4.酧UO_Uu;&.)$wO>(VrRWqҙ'(h )Ok8[n;1Ԅ\r~$ cdqC|z;<]>t r~Yo!E#Xc%Px]z()e2O;gl9`@T@ۓ)GI1Q\4 {9**kE76CXp} :PMk0N>yp$fEˆ"aŹw+y;l ' k4f C! \ȇ`1-"'6˛ =_dy]^@9Ok,n/+V:cq\,!p$g_,T+$3]JNi> JFJ_!O-sg|GyXU6)NjFhèԤ*"֡`/B$yj*UrwZtE֮ͥІUTљjbUՍ-l=+ (1<1߆F( c9W$}?pp(l-P|U x|U T^wm(c"e y01vaf}ly55?b*wS^/lI55 &wxSJE'_+w8:\;M큺(8 M>= ff30b MF5ȲԔ2t;ps>mP5^\- z) Ut[/$Owul}rc6a C;LxIp ,|cy~ yg>|q΂xll̀h&A Wou/7pۓ[&t1q$3:۝m˺ټuNH^x9"\dqO6 DaѸJ`Df+uʈH#i1 "h;" ):ty5N3UMG -:z>k9y!(Kuy='Yoyŵh>홽'ۭRuQU7|Ğ\x)\6U GMsbKS͠Pb<FV?Խyok+/mYfW9I`ާ$D@ѝiKiCY;8`OsnB*T)؜Hpcߍ#{=Kl|d].P(НG6ZUkb˧OԠ:(4P4Ns6L˫HBlTS0_8o?nb /(7,( h,nk-m"([fA*(F YRU*uD4.pMEOx74V@lҐS$fݪ'?v~8z/G{Ebï;43^ZmU Did[XΫh4<@=B H8Y Ζяs(tA Uq Pdk`q&goOTI6HY{DTr.t!#E6O`A#ͩxRLi㷟 O5CV&fCZE3&?x߫>r:^ "]8u˖kA^ؗ+iWzy͔|鸦d90yk&F(:3εbݫg^ :+Mg)QRCS5_eZZrgn3:c &5G6 g ').*);9a< ~a )ލW#{PjDj"84ӔD*-Ɯ#'W.#X1I1Dsq:4_NPc3 Bc)S@bE4H"fTLxU[c5XվxǕGKda)!/fN3$dqޜACT%J}c#m IDAT,f6$.67Z۸rez\ɀd||.G@ʬ淜o|:`I!G 4&Ƞ f_*iVZSy" \U(^=u,*0H.תTsݝma"dQYUqB U*kRhd[@.FcڨfXF"K$ ) p|o%j,Ŀ+v,lozneO^JQTv@v)aq_yЁq$w]; (dzO@\TG~3ңiu?-6U(]vqi ]̶Ot9PhHq6j06u*l"L5xv>U\yUϬ-k 5#$H!ْ0$QT&a^FE uӻ4 ~{{ =G (,|'cP% ~Yb^oe3C>r޵x|{pc^>n]~_\~a<9D|TՔ* <sKW`#_y_I/6-~X Jyg7jkwP4 E1{yaK11x]@z#$ g X"peX>">V^9cs]Zȱ*9&,7E+Omt+KcGw_<ϫY'{ "R][OTW:0j@3\X# TPԴ>mE)O6XfjpΔiBѸ]߿#*6LL*~ŠVA(i\b! CGz~?nM)"ֽIw^}ݱ+b]}1h^c&5FW,Թ\Q蟊%%zMniq%Gb~%;G*Nѧ# ygcM;BCտ"E 973 PB䲖!(ƛn "ˡZ @J9L %F#z\wB C3zaJnh fl/:I LL潦1!:(+"5+l2J Xt؝6$&YQzšm(zqrgřDr8R׳M„\P};Iʖ?4Wxw,`((\v9y[ Cѧ;'i:Y>,-HXϊC11=_o1O,|QP(,27?-Ut0Q̳H"N`UHM 8㻂Uˆm8=Vr!,HV}=QKQ̵$ÚV,SZ--0 I >=qͺOʴY-I;%ъHa2?E`[JMB3WRntΎAHco:l3 pn  ֏dNH[ⅼ#@u(ƺ v:Kd?ILs L1Leج& E6`}tݍt`Ep>u( XM -j[wग़UxChhR{>70E J_O?X#Bl֎) U7wo\2Qb@kM#C gqIĺ|(n;@"P<ՑyxMwnݙɭ~t:}i  8i3Rgx[Zŵ6:BgxBn Z@p:G<S~{EZv5+["IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_1.png0000644000175000017500000000167312014170666022703 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<MIDATHkG󱷻ɱEb)A_ ,i\T nI R$U\ [}\#L3;vfyggGxUj7v$V4V"IH&dHb9Ln&S<&w>$G/ׁc!K_H\I$k)eXрzQcjmh%#:LNMn74 LRVȾ|O!.EI&nHb*c8y_mk``N9z.{qR +sQK]=;(1}xܷPG %E?"8Xdh( %Y*ą v%#Y@}!HIꋦEGoVe* wn'3QBrr0@ʕĖ1A΄sAgK-A@C@lACt!6ZsR j%He830q-5-33*<7* Ï?nkY¬Mؤħ!kFXBV"s-BQkkȍl\} *l}:J*a&+ 7 оCS`|V|̋j©7og^!%b:-UG87+${/:lC _z$3{Y5'h4ʷ{S`_xyzߒXM$)crWS/:~ O)tYX{ݝ[' m=W9IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_pt.png0000644000175000017500000000161612014170666023163 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org< IDATH;G=` .BG‘c0J 90 pv{vT(rD1nCN p03XGQ_%s^)tnyY=_L0ݺM][+VSb216Nrت l#iN[#̈́`H0A8(fPOgPj4T舓j%\.=+34tKnf` q<9jGH?1[W~Ij/}x/h z<{ YD4|qIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/pinceau_.png0000644000175000017500000000405212014170666023455 0ustar georgeskgeorgeskPNG  IHDR%-sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATX[l\G9{_[&miBBFPѴTԇ @pyD+@TFZ  E(H 4ipim7qfw~fx%n܎tvf̙7ˌPJ*=>3zL&3f27)D3d\=XxN=5z5:Yϩ3~hc?@ RA(<z6W[=8'D5%gK&p8Qn4p$%P^=D3n#HR1] ( EJzN 1E𼐊Q(ٔ*˭@>~TCh`׏Am)*aSݞI Z~׵Ƿ@n ZZl62T{hD`| " FfJ$ J7AFUhp%>wNJAJ Zkv3N-LYhKmcLsb\ p m[}H٤%|;EgG+55Q\*UH*ӧgsk %HpV§Q/5M[2BFlBd/(K6ŲP Rȷhդ<Pbf)o\k՛B LЛ1kX23dv2*|\Ca7E:L:~F7oѴ~w'}B'GBx33l֪_`Acع4np(AH?PJb'2z7 p&rpORx|yE"4vK($4ŋ-"\UG3sq}CHoMQ֕c!s#>չV[CIb?K\<+ -3tk7]IRj.oLf*#=u<>SgAi![X?TD<Ҝm}$:ӓܲD]!CF:1@k[ ulyoYbbKum@! vlTܢ2IS s)u1ͨT#+̹~ #G}9*ڥ yI qjVu€m*Y@}}կlys:wyoZZMM>R r׮^F_:b6˘gG@i)ƈO4nXH:>9Gs۷YG'҆7ZuUHhX~N\&@Å0O ៨=;U#ᑊ E:ŌOtV#R)~a;I|n|.qjJrns ƶCk4cEc[YGbm^X͟k6bͭ[DruԤ(;<͋ԙEKO3`ݟ0;H9Wϼv"MXq'v4rn`ZYὢ-4 M7+ޥ%5JGz1'/m 1`R`j@һZӳ-IزSSV; 7t[@'. Vceϕ)IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/perpendiculaire.png0000644000175000017500000000233412014170666025046 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<YIDATHME=5|@#損Q7Bx}A$xZ% /A ы%f1&78:0==;=4WUM~{j$V3IXĊ$V$b\u>2ܐmi\v:w$ٛ"us`MgǵX-&I%3~sO @ IXjR_af]v@V֞~݇#f09[4X_j~A"c@as?1F"l[Li6d5`F'DRDi4>ЍEkZB=TL[QH!`nVΝ֋ϑ*٧LD ј:{ H4Z"(:{f:9=zna (pyGF*2`3r%FFt95YVDsɬ d͈_%R4%T 22:B#KKKkgiRT.,K*dC!D)C*`Uofg4O՟G18b"\"ll_o@=⥣q{d_}=3UF%V}i8DQc=iǗ*֎0픁_'_rpTY~uvgE9U@{ в]RB " T)iR\ ˃WDEZκ;-gI;~0e*lDb.Lk0gXW˝L_,_1>sCI)=6gVH !h M b9xWW՝~oRR~`?? eWRIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_minus_bleu.png0000644000175000017500000000156312014170666024703 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHkA?3{䇂ABJ 5"M إ$&! 1ݽ3w<}3c _~aQ֢@D" aZ(T-Ef8I?M$8Iq$=tUoa ϖc`no\k8>{@l~-y{'ܝ_ȗe@5M1\5 B <% |(ĉ>t@S@BٔdgS8EI@Ws@iLck i{t_~AD)Xz ҎԤbco+Zh4s =_h(:[,g2kf<0ϰ3NmZjJTi1T&@ jL'zKK'HKo6q]}eKϹ&Y,|b5ppr0><懦>?]D'V1f7Y(=A7y@$ôc]馺5Io^iuܞ ,N+]q[O¶j#QB;hjVU (}tt.F߲1.!g\} xлRV_^u@(^hi,8=1tF)1k !%*[|O{m1{roa^[\8ѯ7֗N$wO` AHAl/JD3PIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_mul.png0000644000175000017500000000220512014170666023330 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHOhU?yɛvQ(IP\\ <PĄBl-.AizC%$MBovwvfٝ] c~޼yOc՜jBkkTBkvZ[4]ЮEezyo-$ҥa`XƁBkXm0q^OVV%W{̠#L[~c ֚TؖqՃXPR ^<= MOX( T]u04x^0j 2jtX:Ƶ8Bc0:>GN1&$B>t31_ui[sw'dlT?2`0[Զ6;9`t(S&YiX=ZG9= ð0Oɓfg3 AyX~pOXkVJl/~irRT_%(<^~( ߜH TaA^/.ff/c]èC=ayqoLaOMǸd'ZՏ"m`fc<|6`-#ngzbl20Ob>敡L҆aCyAvJ Պ"ĝ$Ұu#;- 9D>cޡV}ppc۲#(C#r Iž:E7{Zk8,`TA j|- ,CB4 @вn5}ZG BEaa氫mU)[PWՃg mHO?>~"0rY IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_7.png0000644000175000017500000000171412014170666022705 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<^IDATHϋE?g&a`w/1 Kѣ9H r@BHn.Š!@.2vT ݩzޫU%s|syj;Vˍ$V4V"IiO&HR䅡h+ f q6Au?kOc5'sg R!*ӹm @+I)&ƒ0/̺Vy5k.ױS3ܠ$C`[[oHbj9Cu^`Ei9{&9lhYIլb^B gځGhFtr2Z:R()=<$D{l8=0aa3eh>{#G)Ra's0iW&a/˗>AxɀmceE [X[FTFw Q$K8;}h BO~鵏Phx 2s>^~ںL~m BIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/representant.png0000644000175000017500000000322212014170666024402 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATHklw޳kowmq18%PTRKR-jBTEiUX _HJMVj(!jHhqBHmĬ{wfgn?ש#]9s=#úm*;,SM6&TMRLB_p]q\/BqN;wk)! /jնԣY?G&\W'>κ{m=BkMmm*X ttMݛ7˦gt& ό!vngMr9/dq]XyA{c6գ`*C@±3aܪتrg4M) [WػNvm. K 4Dj*F4U L]#¹"t$Ȣ6`t ,PQfe|8Iz[8>6=/ՎQd/Y+t;4*bZEz[:COȿ^O>@*v #7[1L4!en+@@R1t隂+ʔ;O V=k̍ū1t ]W1KSHIUEDa]G%?ɡsk0b8C%iJRc}gҴ]k>Kc $VI(.B@Ew%rK^ON wo;:K_exrWP^SՅC; d~{FٿpCu2-)9fQ]럐{O% KcUoɩBrd is 'j2tgO>eF|)yj#=Ǻ%-eǑCD/pmZ=+[̲1!~aR}V ?p^;747078 Gwv3YьWq6x կ˖2`{$ۻr- Bn4pEn^~OV:":RC.rqp"`Γg3nP(|7RT RKNBt_y^pi|F>)#;tq2uUP/^DS [)e$T#A1;K{1NV1ys#<\TYJbNee(A6Wwe3Z:dgNN0WU!hk7hd[iW~?hNP$3o0KJ e [5hʺ>",ta/'Ɵ'#ݹ\ԿyuG:0a9рCRLCS*;XE|EW|Qߎse5ՊWi 8 tmRwd{$^}.e5|r,MI4*ئ «-ePGL}&E5<%9tOCC~e]z˕w$}]"RT /Ey1IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_abs.png0000644000175000017500000000341512014170666023304 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHmTg}2;,eaK1PjRi*h~(Iը D`)McbM4ֶH!0()el]vewgv}w?.ۘ؛;?=sϙ#4`/d iwɴò1<~ݪI\:xҩt%u\t/@Xӿ8 >Q:]v:awl}ִ}YÄ$!B8H)HV)I*%gt2YӴ}O:lT3zfo&C3EjAtt-h'ĩhiU[9i:#4M EmQxr={/Ư<4Gkk\q$uyltfοX 09t|($Itf6ij+( /䛏~=K7ϼʍ醣mxLrÔLFg߷̺+-K;&g L& jXgX>2,aŊe GC{fϮ!Ln_8Ί.}.Nƒ_gn._Ee(ƉԆe(*F?:؈o#c!x3NK'xPhogYbdUo&0gQZfger:յ`rk8)tw/|[o8qI7Jkec6_u yׯ/2]90jVu3,Sa ޹ô͏0mTURtrd Y:ٴ r*VvxMU#St|Kw:7iJԪjbF^8%gJ}N^ Qu6w3U>XC`q-P5dUP Ni=mL8?ij;).QUqS9DQ: W"?sCy Qݖݱaۻ{ `k& R Œ:?7nɹ*xs\K)RRD͘VJ͞q!Msãpx$M>⡪I$ R/k޴^ӖSWj *j69IOCBa.~#kƮ F`?pQR~`㕋_/s}:)IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/perpendiculaire_.png0000644000175000017500000000236412014170666025210 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<qIDATHOU?̙A E!;3Iي~V ]whou2|jA51۟Ww\#ebm`3Lq0кb Asy ~SN5y'æVyFӦ\1'lm jHh'zӿs (?@WHGyޣ#ۦ D!}:wl=5|0w,i {R^loDrbz17 I5^)*5zwd9|ڼz͋uz HWn{v'cl[ԋ+D쌊Zz\9`+-׶,s/Ƃcs`r=\<=܅SF\#iu`EHI=?`BXʁ&C@~ )!zԮdkI$}~c]յG/KG/cyAG:9ܤ(IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/parallelogramme_.png0000644000175000017500000000320612014170666025175 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHYlTܙ{ܙaK`B0667@W(P&RZ64(ܽ7yɇз?(1_Yl)g:4}2}_{o wsD753h@H! R"D &1qQ1HEQaa2y ,/|ݖ_i*#*Z<Λ_RUg(J2p[]1gY749&L&ؗҩ,DQ`Mef场϶3c}y-ouP!9|Nˋܐd1&`dʆ&t*ST_Z4(3amU5sVqQcbp4I>x} Fܷ 99Pt`OhI,J@ ! 5q59o}ʏs&F-672\Z TF-'w`K u.Tlζa-}*=,al7S,@+! Р5r{6#CGR45dhZ,, Շ[-ؖ]. L(Ξ`ٯ{ʍvfǹa<c]˔ sjU<`cܼ6V̄b䬻Njz6>HӖD&5$5- elۓ\3-F{$+0n@^`DDɿhk[@w@ϩ%!Gm a>` GeWF7ȭ\ &+K pi#N;xi~8\w:'q.tK%1^Via%VZs` \~4$1N6s l4''АuChӼ&r=xylnN#B?pBWٙgB-蠙㵨(I[pS}!]O@I8F'xX2CnXg"Ga%*6t8lߓM~P^|PT$U}CqLߪD$[ +Pv!^hyקLSz#JAr6 tm^>00 .=BkW:-eβR:k.<fv]`7sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<\IDATXMhUSJ?~B AR]sV JA7,Ji-YZlTm4I{ysf^Rs̝s{#UWQZ*UPs";j]_}%'^Z@"4 z} UQQEED4RP bkO4>h4&kA߀Ӂ/F`o{Ј^EtsSDS6jI?QnMWxxƈC{.a[6ؖ -xТ>4`Q5 [Q!l1ELOvS &_8 "ZT*V .h-GJ-V.İmS{|? Z7 E׏-&P$/DUQNF&t"/  "U巃/Vk:eu ۤ{m{wE)}G+ȵ<2>+ j^xD(WA' BX`2ì8a#{rlͽw#ƀN}GHTur:j0?UFRA6":]RyŸoEx/x,8΀u/t:rfʥF76-L"w*܈%6yN2)VX"5uf_|<ZADzNs.ΊNήvG:%ՉLDDG{j3` +i(RCCԢ mDy&xD]|ZYQO;;Y]͍rx0":@oWomߺ%<{='9GDQ@BOmyjEzҡ>H@iTR\UP=oԛPJroqu<=-YGOmarnaCz79nR(šWxO:Oмnm>pb?^9N0~Çׯdi%jM37}0%7S1Z0 ,Ol{=R0&B눉Wj16⹳o~tqS "S䯡TahlDm[Bc{4mo6\(s R It}ۆk-7n`Ɋ.͎S1!]: 4?``PBd2VwsNs39TclK~ Ah}V#BD"R٦t[eޣc4_Bҥ|cbKזyAh arwV#:"oƯI~Hoɀ?$hF(IC;9yu<ӺD@<'5 _b(ɻO1WD[/F1O^ھf>] gh I BPvkCWx`ϸ8B׋[EEQpmsתū^s@*%]UV@r0Mdaba2$@dUq%e.!5^/K5 )1eA&%HS\,.Pd{1=-2%!y4/W P{m^/5 1S`>>J@R%Gb1AȲGAmhˆF{lD}m]- Go6WȦ)~{8|~SqaXk!{n h9xx zu]oUHyWJ*sIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/print.png0000644000175000017500000000161712014170666023032 0ustar georgeskgeorgeskPNG  IHDR%%Ş sBIT|dtEXtSoftwarewww.inkscape.org<!IDATXkel$EFV!4 ETK9H? 5Qؘgb=H4ں&%PR!L6x:H_}Q#:pуfKǐщ/(8r( |&MK)?J,,bkMN@?} ;{ᑒӡ vJ>i_Lq|82x Ρ&#{ZЄl;&4߳~᭣B-xq])yӤ~Q-|s6ͅ 쥼gź-nado*߼Z~7zXxnpnwRWX͜-Jݍ GOVؕ>'t=IY+?-EʿgNjDlR]zR rƑj|-)|/}EKD_ 'Y3#!}{h۵i#uj,XTx~V)P7#EM2NFD|;ۼɛ/fuſn[\Kȿ$O_d,u.a(+a5@OYY8 $K-S7N>?*' $?m{(1_$T+;r4-,cu%O qXP ﭮtZg|qTBX(⒩^?O(ZKՏZYh!,L'o3=H$S *hșO%X `<5Ͽ'$R `IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/reflexion.png0000644000175000017500000000274112014170666023670 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<^IDATH]lEٟZ/V-TlZcHE(hb4bhbi bc$B` FD4 FOiB]N2ٙ93sfx󣓦eu!SXLTX"Z$d*7Y<wl=8^q7._tc @uG6sNjG hm1C:b2Zh)t#Hx.HH!27LդH%B]lVQMA$LDd(jKDuh곭B+0dѳ%zVy(}CK M-}۞i2נ&3G,\fϩ\ fi@XO[aݻq&|tcǹJMN w6-F zn6ҩ A2R]~Nz8Meu 4B HmYq8vK_<~Hx:JfRPRRvج^&Yۊ٧48o,1_prӢ4>.\]>?xGsYX, G4fwWS3CS }P%JMMp['œ2e2袢"BYEiգ*aǻyu=1<͠4tAKBXи""J|GPf62ȾR:oj.aXa.˦ /04xղ, HVH=-G^繈+ K|P;펥 ]zYLŬB{a?QbjNՁU~ӫOqYf/v<4`3yhAWd@PPzK i%kpHybɀP[K/NӔN.lҀ@svq/<6K nbjFaJ Op=?XLYf嗟t]`ItuX2U4W\SqScmǽŒc`ǀZ`=E ] %mjW,:#FanIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/arc_oriente_.png0000644000175000017500000000305612014170666024326 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHklwfvgvlc]MG*ZH!JJRFʧJiT)kRU*R_RURJB+Ui % 6&ؼسsgw>I?p=?={R ^iYiVӴcV Ӵ6#fXfQ(\uLnq\N2}<:%1`ȽKc>+XӇL˴,N ~g " PD3B7M1t]7++-pHi5iFV 2˅Q2[3U5n*{)e&#?wP*C$jVoj>H$tVnyd-0Q>9MIӷAy1;joXBhUA| :̄ZCk9fpp?Uk ZE4FQMeW߿xQrz˼Elb-bLAV|& 0w|q|ggZ&9~YF$7EvS'bߗ(+*VQB`aA o13u驳\ 7QK?yc+=-!>eu{v'm6mq`933'gklhmg3/r}l5aԴu (aGEbw 6]j*sIFYι(;MtuU/^%~Zw}x >^zOSI6'B[H%;VT.lu;3F6T~u=ol¥[MTJ "m epG?'~ j2yड़u@'YBTb\3}ku/SM4Y?QėUjO[Ya BMljfJnuWfrğÕ5tnj.X9rH 'O.QvVJ pyBQv-Kv`/]'i^|oK_e S@^EB5J*"5`Kݽ ޡy*s=@rJ-e.'B$kJ_>6ɂvI-NXWZEmtGx7!q8 <$>Y?K %UVJTM{K$pX(͖iX G{{qe:IW:p'-!}rt:ls?UkB^IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/rectangle.png0000644000175000017500000000314612014170666023641 0ustar georgeskgeorgeskPNG  IHDRVgsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATH]E?s߷۷t-U[!ݮ Bnnmt+"1ј?HM,J%i#@bKRmwk3qߏ޶ 73s=gdFhǎ]s皟]swMs |L<'}LP! *w@_@6?q&w /M86uz:5 vIjd B|hbodԆZk:=˘\eK*1ׄ e X{µA!`ea,㷍r* -`#@gܳhbw5gږn-FJS&h]e` MOЎܱ@ jÛ(jC3YFIǣ'rϲ{1iV1#\ދWwQ5+I#[S LG!ӕb|EIC*fw7pSpq;\8 tϗQI}*$/ yr@V@,z(ř"\B d(UUD32PlW+`8 p֛|sHtuvIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/vecteur.png0000644000175000017500000000253712014170666023355 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IDATH_lE?{BO*Aj h)@ &(֐[Cb /EBP0DIU+ mPPݽwb$o#|`fؔ)[æl2baS6%V7ljXDp]UN;)w\qU=Uoh]> h"~d.4w>ôL6h)G鹏d)6MٞJy89פ@T0wV!#9UĤ>0 @+@MeҚoR U"3t C+^6m~PJ ЬIlg-v2P>ӏ>9 mJ~Ihx`B YkO0 sGf[ps>RK ,,1&K ^f0~DZCj]CE@? N ?zbH-ʻmQ:"6Bj5liHאRL{+#93k.pb I22Dd Dԧ6uQ3 b`IPS^ {rhfCAY2$lbKo|>>seuQͩS j@-^bW5ɶxaɓ7:>}ϝa=r"i+>uU؎W*sm5'!lyygx7.߿c/'xJDs@98Lّ/k[q<*jyїI;&m [BCbU; *mI[a?GٞgcCK1P`sY 'zg٬0`qMIJ O 40(Ru >: [t//FpD! eW¶NeLyT^u .[qPYQQB?Wq 2P2R  s/n̼& ɟ4uhT*_4%bR`#{dOZ+AT|Txׁ]f Hy>JJ9=' –`V(KOWư}6e4wUU>1U6m:^{h:H'xFhڸa? ͺߣIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/droite.png0000644000175000017500000000127612014170666023165 0ustar georgeskgeorgeskPNG  IHDR%%Ş sBIT|dtEXtSoftwarewww.inkscape.org<PIDATXKkQGS'hw4(vUF*\3HhtZA d#!%UE4A`^qBM'N=0pw;?!`A}"d FRBxL=N WOZ =YقtLTI, -YTZͶ[B *1Ao̿TMIRϐX!b!K$ )@C ^U Dž tG'eH}zOK_\k ,-aO6%J@ | 6~³\}sojROEUs Oj 8݆T2֥"\Í06 s_N[Yj|)Wb>π35Jf)W­w {7p.z=R..p]< ǭ/u=ruRݢ-:GKp\Hay$z$#+@X'Tz[RTZTIihhJsljl@-$O7.™]IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/zoombox3_.png0000644000175000017500000000277212014170666023620 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<wIDATHklTE3wߔ*G)m*塨U((|(߆C@jc!>Q䡥҂4PnnNr̝9?3w^aiY4y6,!J(e/*Z/!DMdup4q3O2!ĠR&`8&2jO IQ j7~HǞcs'='5t݉{| co!$aի:Setlz*S/Y%*3fR'^h<7T"$mT6_;Q1Z B!RuK-0ᥘ *4Fwuup?dEH9E= BI˱g⨘ Ƿ8XmN԰:\|=p)5ݝX(_Dӧk7!-R);W.= fw0|L-_|Z2"bCR2´5ԑY8)"o{\1 `ڀ„mѺuo@)o^5c2]5xF^Wv"S0!%P(<αQ+3 3dLCjSOnJ!_'~Et6K&qȚ07V$ؓdJ)VRDml41U*zzPJ%yMu 87U & {N;nkNJ4LgQ[Y%;4P0`,W+NHيh=݇i_7b맟/` {= (,,˚jnq-Ff'nlyW|ճ9w}^0gƨm!N'W$`Xnn.ozMEƒvBbwXvEAR' ]ǒMC&>X d|>e/F"@"H:JoJ)Ѓ C-dv3}>,V)DxNa-sx< [z?}Ѓo-f`F00BɝDGZl4uʠҺ', ,.u/ΟNW4aGPe*26uuk͒AWU0v~u_K.X1Bұ;]NNg~=/}躋aTU}DW@. Y(D^jLmkkϼ aIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/droite2_.png0000644000175000017500000000267112014170666023406 0ustar georgeskgeorgeskPNG  IHDR# ՄsBIT|dtEXtSoftwarewww.inkscape.org<KIDATHklU3m- X(@5`$bD|!4ѤĀZ~ "Z4J$#"D6fgfgcݝ)aL9{oFH) {Wljҵ fZL蚁oIƲX~2le'ڶӍST hn+ra-]Wkfwt(i;mW4& ѐl꥔7<@@ $B B$H| S|_"h4DPZe0@)p'Ͻ3_ӌu-NLnә4<թ9ADTɥ1 a7m 72 15GV; m,DǟW4n=2ܦ6 S@3Br|Q),/:G|mE=ĞG>̅)J\%.ϸtO5^[8:3z aaƳ01U˂8 IFN] nlQM9!y& O'i涮ͼ5he X@abo)vpg(GdE&t[J%ri<0xoakce :gԉ StV2Q\$ŁH+Jl E6ψ̗>{p/L/(U*Ƕ4gY)FLȩCcy_\ /`nc<  .:1kw72펚b/F@2dvE$gΩ3|r`^)+O`6&)kɃ%5,% y|l3 k5 6o9>pa^y="cfF(%`l,D  R~F(Ӊ*,*[Hm4 8ʠҍ[(=M"|/e6y{AkMK*+'0yt,H3G3} =$JJKA!@JX&Ab鲯vq5ʑeo\G##3'W:X$ƥ{&yHpSw0O=&c̊<žZLMqF{¤' wG^awUYf-q +RBZ"95-RbgS$uLIT P74EȗA& )[`' 97&*"|]|Ǘ>RH)@ H]A>?z=\\/dחFf,*;!bUɤz뚽Qv4jivu4?۸bby!`ҨyDŽnk]}.lҿϥ\M[IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/projete_.png0000644000175000017500000000275712014170666023513 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<lIDATH[lUsٝK`ۥ4P6H䡢0ʋ }QA%QHJP!Ţ ]p*K-VhY9=>eI&{o|;^0Mkia]eac6aa6$Ox.pr 7 Gw95tځ*|TX1ei1Ӱa 3t&M:%VNTDJ?%hHzx‰{iց@4*D]7s#RH1z rQ&%Z f^8Ǔ;pa{?[&K1RU'huW.P{OM2 ,<`Q ]9ТHU۵7`#ԟ<.ciHBZ28xիVM ,'4Mϵ az{c<[zkOFl/czhV̓_K0 R=ر}L8?UQB&CnwqABtJ૪ilD2afl1eBe&]vq'++ZRJIҾpzpcϙ׺UL tRwB- PKa3u!-)H]ēgYZ ?MACt  ,BMI/{,r sW2WI "'E+>8C76ڤWy-RHZq uX>gf$g=G9Kfp<J]_k\orzj R[-]kFQ}D)) qogϡax#L@?kjg4SB&dЕ9 0=@biӄRهD9B_=DR#*NPSї^n$"7с@Hz}Hi>3IEҴyQ<e *J 'ܯ@40˹M~Ϥ ڕ`(R vQmj.6*RJ:Wnx4miE~ =tnۼ mS@3l!HXf(dmIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/arc_.png0000644000175000017500000000303412014170666022575 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH[lgfv쬯&Ȇ R%PAJRP*VkU/HU[)ZQUۇ7LBQ %# ,ALj;3;zGs3G(x??iI`^Ry+6yi#eecQ(] \Ww<s~hO ^<~kaL8GsJZҞ]RbEGDaV$].ZQ6}hAP)&04 5C_TÈf8"pҔkR6tVkZ `[1t*TY|YԖd}Aw܉TfEf9K  / Y䪀qf[ӤMСx.~ 4㺨ݨiiAy6g,NѺ袃/qgE%gK l׶%ş9SBRBKѴ'%ޮ\B Fmr?-Z &UTWיp:@AWtMP82/bW~{${.m,j8z%q yzC S`P=e|;P ,)mt],y9e؍alZPH(g1?lm-fFuMP *}5u|I._ѣb yPvHga GsYڱe2~bI4EMB3LrWiѱ>LS|if~}'5La2FZIQ;g6FFS0"r?utS>p %WR-g[[Y_JV3t`ǾT>7)ܙL=9 ûv`3ŝ 6g ?EB/w}~"{qJ1taav .^CEV /Kxa{.'~L.bw^x wB5ڦR.>ZAGU(i׬!Q ܚN`++ZCi:G_NB֞o- 3⛷FpDSBMCʱuM^xx4Higї% EOܧuXăήL@-ƚe٤0=ٽĺ{딶 ZƁ˙LE3R0)[ZYߛK ?+7,W|nDX+ H i%(488XqHn D涧ɽThZ]^N O͒joW}6%?pyk^ $\: 8o`IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/logo3.png0000644000175000017500000054040712014170666022726 0ustar georgeskgeorgeskPNG  IHDR >}sBIT|d pHYs  tEXtSoftwarewww.inkscape.org< IDATx]wNn D&`Wlޞ)"vb v|@nCE]]v^oHro[vA/]nNNf=3gf13 0`[ 0`A 0`? 0`0n ` 0A 0`? 0`0n ` 0A 0`? 0`0n ` n 8 r7`ѝ?r8pnDd!~1p0n 0`H(0cO w "$b- 8L |;⡗n"J>2dF(78 / r7`vCDQ-AB0f^p81pp` DKpaa?05wƘ;+#p?"#>rу<0t@DIj||fn>"80݀)t 9PP@ WNcHDt3ge1pp`07ÔDf`la?:280݀ 78Fo`?@DS e橇S ar屐]F47˜Lˇ/t1```x07˭  3rƶw #_pJADYD4~!"[0 `rKy =4b_,?D`%lp%< `2@z Yހ{U!}p"vb  " )mmU9|a 812)?"r#4h$? 3Hs*BT6n=0?b}t fHK |V .g7 ?fg s2Y #ODp9 t{ۚ< `*&X` κ y  r7`'@<D$7 s6<$2Rf{&l|M걬M#57)KOdA7)!G@J`93ձ {1F:maAܧX6Ho.aA sĶqKMԄ`A!iI9 >A~f?@DYVpYo{"̼OW> RZs{HDMU@{d'År9@ɓF 8qSϥ !jv3#V:`yɊ: Iؠ]VC@ y[iӃ1ZbhgD䀤qnHER~~U'BWoA{ii0AW ]+<מ u0B 8qS%I@@kK~{o>L5qR@ʲGkX&qŌnv#hh"6-'i~+҃ "ʅǓВ(f^z}K @3Q7H&r=yBr;=L3a!M40[tu~0D>t[y"Gn)lXōof !ٻVq[>cB%͐L^׾;i > M9~?b7 bqľ}0 Tϑ4oCJh32d(@ =C#$vs b?2`h A i9P/y5@ s.<:@0/zVU=/p;nK#{6=fy?x4h']\`Ot0>չOG'2l]tH|$Lf^_np,Cb{k@ ,vuLk iQ_rte{Ѧ&}`!k\YEpдCۡ%C2M$C i2$0aR,gg.+ͮqrHb0 m1rQ5`L򂮖LH[%kƑ,?^ЙQ?VPb@}}SιpH%*vx[HGxA `6Dը g/S\ Rۓf2s ִ3G Q#+& b浪aMNeC:2pXa &w_N XY5I8>٪:di R~n#^:@,H`kâO|$@ SU3/uîB#{Zɿ H "|H{AJ?{9?rddlhwojm]DdSRpzO}L`r̃ې'R8afKu>ZOfm]C ј}9A &y2@p|n?k_qܻ_H$N>aY7myPp;3 kHL B b Oùh"̐v@z-wp>tcEIC tL=O|hM;Bd=Hh$Ae F_]1(p(+#Y(V1i 8̂^RopRŃ+C2)E̼K-_'N{GBK?OEIkW}8$4kg wG d@ a¢MuZ9ŒzV +V17ZD^ݏZ  $ ߠTMrO'ݒ^y|m-W |\t9%v=Ǻ BDSFn)iu&W!y6rS0LOxYSMZ|U} ]x]IǩMDt}0ֵWjP3!( @ܷw^~n^6 >HZX;e9fx350\UC;BԦ(T'2Bֿ_y n`rb@D2vI+ ^]ܝ:ossڿ<&`_F ϹA9q5#rmOTCz[-R=b(4N!Śϊk?qDRU8vֵKRl.ZRq5I7gF hVu=.ÕQqBJ uRن1OD@&AA[H,0 R(9\#8}X&eҎB#6smvX"?{˒GWݽ\j|vc2(T~]M?%1FGXS|ڹvl!\}Wt KΣ\^8 )q:Y{gW0 p [; dsR_+ UٗcK-}M譼Ϟ~/2 ) Zv"G#nh'b;xăs!ARYj_uM[+CK?N;ڟGl}I755FWt sڒl*sstgwIk?| ̶„׏λk_<(}1;q*vyɴ1":*mN/xgH=rb'ogӏSwwO4e^k3NdEH.sB o3<@gȀA*&w_.]|@Lo=K~':תr~~q(*oӧU]/'렌K_[RȯQV,z.LvN~+46ޟyڼz@՛qqk 1@ 5xʬv1ٻ2uD\ { Eo uݳ2#֜YO-jvV!v§fCP!I[NQws_:Ҫ9V >T9}wDrȗ.c HD9/n r7pP0r+7v^xub&h&iH=߄ޞs^crU|0D{%#:L7YׯFQyQҼ{.L:wꙕQ5?h؂~<@ޯ6>%|+3S5_{u"o!,YZ?|R^O _<}XMqO:^Jӧ!vR:WvS*B >9qWw!d.7iYǻۯf[p^[|ʵjuDǛU]T2o깴f?}N&ӛOhڿnG渻倕6i9@ZA/'@0akV<);eH=G4yX2W>lkX931f{q ͞U]2`][pjɝyGO/7 eEO̿g~Q9~uE[^ՍٟoY☃.s`Kd\:?>Z[l y_Գ#2$"0.i P*臽sU9ޯύpoVLOWI |^d&?n'@V!"\̜/9 kh""mWNk@qo?:R!Hg88 \84lT؍@86fvr~|!گڀOtgaW)?=w8-kOȢtΟ')aQ]T 3(wr*2I`[ߩ+LA1N>m0'3:b{%WO=ܩNϜw2]T~S0VUy\ oAJv5pr_2˿STf_|,qAh -+A:_\H_2Bu޾)g~|Iio<Ϟ@==.y K!ybs@*U=;  R;u)S&S!Mlna'5pd wBvl*+:C2ۺ$iZz7TKo7+}͎w= #c #roY2F&shd[;2{h8^Ӵ*Dz&x΂n-] {_ѻVSځY3@zWg[|~SogM|pwfZ? ;8v dqZٟhLfw;e^lY#45qw:' ~y+R~ӋTOWU>'{~i<0$ *KV]('ѬH9VDNڵ}dkSɶBɼB; ,Wgm]VR H̼C^#O}Hp"g livgA y=pM~T2e/c6@ ˓=OtȗQywÒyX;R%\vJɏsn[Ⱦ̐䍷fy3I<5pG"2㬙el)FT[$U %w0qد ֺʈ踢XM1"!>ѥ iٗilwQT_ݏ >J]`-> [< &Tb)EZtM} nU}c[KKga"S/O.x4Ggkښ1*/*#7엔:|L=Od_'[.|sNG?@RI]Bg~zN IDAT:<,WLr"?[<_!y+|f. @D2yU[ul{.g 2.*cly )nBWJW$.+=b>`UfՏrT;LaNZ5 g3[,st8|uUu1ԯ® !L?H=BdpbsRENJVߨogf':L@4ڳb{o i!YٰZuA-`C}oOR;9pn9i5JVw4aN䨴UZJOW:&/ R +e CCZsNC{ID yNn$,mga8aQ2_ څ9G:o)[Z}^ sy|]KoBW)--}lLHwtyj# Az vyDO5nݜ̋zZ{.{[3T{їe-s%ILLD}YVlO@&؋M^֜-z 7? T͂}mc׺|A=:87}D=-} d-y-LZaf^KDxϬAUA>)gj "ٮ)g>Xm+D3fgqP>pIoj=%S0eô &3v7p ǐwܚ )KV VOGl9IU.}[%/#ixNK&t+>y,1OǒSyDJRMDwQgtyF4Dj '25͝Rc-Mlp] 6SӻO8Z>Lj*}}Lq+rG~±,?*O7פ~w/cN 9b駄eeOLwF^P&In&O*v4U`fAQKt}62z[ܶ)Sdˎ r@@C;6P;Tos7O蹤zS3HhĚS89o毌ĭ5&Q",ۮ5`tj#E !uB>y4:pnT;Wd_F53yCs+;uʧXu 1Au?~8%US]S2Qjf"y@7GAai3` D $"W~;\-7@dz~h;@+&<)oSuv`L4y*"q`W RT"椥guzS9' cxrߎ2>|M dYp۴d{KGij+؜k?P U{u3#֬BPeVO>[$X+$`([s+?} 8@L>S퐧,D%ug:fn )y,~A wD~'v;RO5>bpgڃV!g8kGQf9)>ڹ]p~] 3kSa3.~!XL1N2^91MƟ/ v.8f?llZ_RЈwi4r>!~`}X9Gshbܓogi@3ޒY'fV2ޯhM۝\@s)v (YGXV_k'YW=fe/tT݁I7@:3y;o=sҨkTJe^u T` ¢=]ҸbyWܳ6S_id{ʭo3/{K= &>u5њ;Nߜ#۵<=u }a"2H1m4Y)sS;BJKn5:6wZY~t}Ukv/`q >ŞM>YKfoLud= G&r}5;l[C![3j^Yr8 Z'`K74G9v.gij,={=p(u'ߢlӪ`lx,;jYv}%y *-<ޖI~u!DZ\u}굌`:vǶEi+_x+ cDޭ*2'EP/1Sn[WI K'I朸N-|x҇Ro+*^՟=i+^P$ ٫dWH<&ހ r?B ;] )OtF;1A6H{/Qc($lq]LHAczWt]O[m wI jvcJ(,c {> }ȼ=DkCMJe@^gQ-5̂4H=9׮G}5W}Ae\ S㐼gֿ@Ebs'jGNbG޹,ҵF6vn3'{`c-qq/K@n37{j]Ҙ5bƗ)Dz~4|∼V>PYx6$ ?m,ٟ ̂m[ ̸DbxKSEq?;6bsU'=0D?5wl`4s r?@D!mx\Zs=uMb1Kց&a._RX  [&g -i\:"8~?oqyJIaǯa#yD"wq^̂}IҘ=f{yHdo@gNML0@bv|g,[}1o"Kň;>׷e0ZՖ-W󓆼Hv@Vu_]O|)\ .Vk0@2V}[yӚclJfXkoaVٲ."WڍКYU9Kk@=ÜɅHwr*RKJ=o':䉳;,{sA'8 ɫ6D, "̃zz"JE~5N~TYKA6,Z`Sݼ5U]'/nN\ti=i~cʚ=pя2wyR6w%PPORd{:j dsz,_m.@N9*ǔ]CeR'voA6j]<$µ_^<{+۰)DO7֩\9H5_ hu[u4մnSʨw7,@zL׵LgŢ m4ɳ"Yd]zSsJ &ޑ yg{vPY/n\q 9~?'u^ $\SLޤ0 "[ #Trm~bU38DȲ߸n깤/n7=at[˟j֏ー+~s%u+a,w!b|n<1yC;ܸQE{[ݭT{|Xe)LFȔv7HhYEH?Ӱ*O[du/htwKN k嫱.-61Sr{uV7:bawu-WUp݋X5qe}Qt|w:ov,Orwpx3YY^]z5o^ihĩWCgD~ї d|r&^.3@ J=$X?sPeŊ_ 2GZ@ "2(umn s*dxҺZ[2? 0`p< 70!/ "`&H).5҆缢s=^e2 voRnֵK7r ̺lzB7VW9KovN&Hvh+:]ueVniTE}hޤ/Nϥ/MRd"25+ E+GͰmskrUa/VkqE愞]}V2duƑJM/zMU_ yD$ νU=P"Rc%ؤq^ъ.uHײimb{ gbwv, ͂uϠ+>~m+\_/RtOe/_8g䎏<gP-^gHd_vqN7pAͽP4=SN_;.~wyRihCUNwYހbq>y]M.AaM&Hr[c%@w;*¯X֖!KE*7Z ':U} w#NΈl[ccI| ~L ԯooZ5ylZqjǴbk`Q0hB_!~^\CN8[5u-^ѕnNqXyNgn֌Qtmazx䞆w3D{5cγ*~Hs=93_ؙ.$6r+*ڐcJ-ɯT^sƙKȈr^Ґj#1{fjCn1W4iR2ƿ"0.`kh;o@ ;+c'uz(S#^AgUj,B̺n9&Mٞuh]#/aP ˜u$ο$/&c@ r? %p@~1*.hoN& f[7kBTP55z-'2C /&F Lutt+O`Uw6{ &r^W8e9Q%wE }ܥ5ؼ/a57Vqߚ9a6sOǪ~}xk,|g'D Nֹ =6ӌ®+dhmJJ/̲I嫈K7%% q{VkHBc5ŭ/ ev?{rZf|3bV;nKR3,+} h#=.G.yzW޵;d/U/R<S>V7鳪r]z$Xz㨬K5m^3R>~[zesRY_ d&z9#ƀO=ɳ<KeҊe|0 C"cӞЍ\8 Σ.nM!NsɶO(d⭙9dg6 u@z(t~Yplby-G[=Dɣ3i"ZdOOjMj}x+U!a._۝^Zuk‚Hp%oaҰ-y*}yOt^O6 w~@D1>D^tZЁ.6uIy5|{9/U7jQZ/j~;ま;OC i7Y`tĻPm7oPiVY>>QaEpwcդ%A;+:5fa/lKk=|}Otծ6Z6ION0<6T*XG27$a֩y.vsE=׳1Lam &ϛM1cD0kQ2jrϋ(H|Y22+^Nd_tb W$uɜVl`%7ImVougصs)vk2r/߽t݂o N[ό3yN3_l2$ ]rSB5!‘|m]>:Y XԦzN Cn3l 0BW\)V=Zc SܳibnNh͕su@޹O6 g=u}MPq&ԟk,0͊鹩کu2ޚj]v?VHz>KOuYr|o50 N޸o9Sb:}lDmZT*9' A_DD:,!uXU}o^9JX^* V!gN٫[!_jL7O<-)V q'; T6om bg`dѝK`BV\<~&zVR*m^(GAM_CҌ$ب&2_osg r!kWʇNH)_:@@gd>znsK_x},GBu;)ΉyE`L5Gg\11 @Z:wIa9> Ā8SφܸA*Z}[r\%]jrP;?J~2},~G7xD F,L:B Q,]Nvt|7-K*^(o\.8,:uC25vNs څyp5S?z"wnikk1e'o@}jf1ؘFVO6 ḙuΒâWY}EPq]f}x g=q֌NY1yD";*2Qp^u$A;T׬˫kKI wBN>WfT_.yb7gCm3}~Yw^d-G@撓[Eij]OkV]c S-cNN;T{'.4AyAzkrm9- ]=Rsw}uF&v[Qڣ6 msѧk 0iX T{߹R/nXk̓󮿯0}6DnLZM$z ,D3W IDATWtGy3m̅o]ج[wz F*I_Kjkn<`&"`cEf/dgH_~[ؔ[灓|b,)4bѱl)Xlβ$k1٫U=E%y K$1݀[;MAw6&8Dֵg?~DLkNm=Hn{t׺y&9:?8*oɅȽ(oɀ5& \tϣDOAv\{ORz!T)/Wg[O ,mTjv}+V[9CiWFۏe9˃s!z6fvNr?֖s ˡw l%Y83ƵJݖ{ǩoL1\sj)쿗08!-Ɏg8f}~^$vaЂP'?yT/f [ܾ&69W!^hDkA;6H{unƘHŐ)]H,eh>鞕Qa#KQH^Ԑ< "ϴIrl>`v6X S_Z|&t}'~Lr7HT Y2_;܏ĎR+:ݝ BkǤ&ȿmtxK$͵<}/]Z"V#x!!SH'DújD }of 1I;OA)k+J U.)_ L~Rsii1]7jXAҽ lhZ?ܵ: X 4Z9YkP1UԘkТC ~No1`29R.:G]H}1}{#ݚUhmVa2dOH}Ung= bkz)$lnQՓL4jAtm>RH^:8\BGTOS&9uZHyTON0@ HWgS͂3(iSXZ"ytS!+hz<( Muj7dP^\^kOlQte1>kSjeB׼}xNTCF\7c,)^-k.Up8,ɿL,,r-[pXu `gzϫ'K_k(W<81גN^HdV bfYK>o90O;k^k,) pz`WhL#ߜ`Iw?52=? =U෗k Mۭd{3^x}m^f%Ife'k74鎀5091Gngjot¦b]4{;޻_<1GDk#iopsYGKeMJJ =θyP۔:!uTݺ-S5:$lVDY2g ZMiePzP>-Io2m[ E~aս_rȾ$uY=om8sݕES5<Ζ϶|H-[k5$XSXYU/;KkNig.]oBȒ}WM1`"KmfB t7/>1 )4b'{1Bn{g٫)LБ\%vOWSĮ|!=WHʚ#ԑlhes$ #e! u}5nl +0 ashwBZ_m-"J&З 5x~@pQs D/~ :Ucѕ"xAW/H[C 5fU$$ Sx$Q [Āɮx48#5-r2}$UY|aמt~ TL J9@֚s1K o!;DwJ{۫kPҰbz|ɾ$XYI=kV%e#Ѓߘ(n!}^>gM[~jZuw.vR%)+Jooۺ{D%Ϙn~f?{o鬿^pz-0>=mHu+A_]tb47e65.teZl7("DIPbӀET])֑݁T3_oVvA@"QB&Į$V0$c{X#ť,gs}eYP}ߙ}=s=Tp턤A&~A·v9t"MrwSQ65j6<ųM[JDq.;;EGLs3roŌm׵{|#Iy澧VP,w Cp?f_duIHzRC6JN Ec]>خV@g*Gفa{RٶXG>~b=~?8Qڃ%{KOOc}Uvv|owᥳO'rkz_{k;u( g7ʱ?\&3ko>`E3Ҧɞۻ:Rb9vu*pM YiL~+؛2ښZ_l%~VTf*71s/׷.ZM Z/f>^ա@aHz $0z(bڕ)K H1GUn0}],o{kJPzvǢ>4Ov[ͶhYteWե3HXv<3{koMkީ '?,J{G^4GoO:?Wp|^,_ +'5~ ?mx.H41)cn״ce'oN8n<[WNHMBq 'ׯ Y7̞){[4m4UFCҴ =,Q[Kl0<HRCrryڼ2?/[ 6_y<OhB pb@G/mS69O{Un X_--#K;;:b|~ЕyrST.vhKx_N2#~;"L.4c_iUǿLׄo[M|vzeb4uo#`6<~qk1;yzz8s_gg'βNͨH}o6'@j;0] Kߛj mv mqTG|prUoNTGApjl!a‰L ND!-idy=Iɂ. +-$k&lsG,|r:* XF'#vX*ȷ8IYiiz]yצL%btT0bHȴULoUn0y],2?2TF|z Eo7^fփeEapkrw~7*GZLf4gnEc`~;/>5cvMN~{Wfd  ^lfA.9srE|rr[f{ vO}g{ͨا<}5WP׫ݤ*@֫Ib8l9đ=K=NjڙO|'E"͔/LmU dEO7٪75zlT˒_wKS 2T$x׋ao] v$Qï~ZpXs3#>m-_B`_FjSj@@@%?Pylڼקc_㝩Bc~S}ʫ ڹDC <Ʈvep@mAr4'Z8˿s|KN><1OP??xG?^sb0~6|䓰断 i}Bz;1z>TQ_w֝ȳ7^)Ni O:p]o{ ?pw|ޫxNuP!j/.M*s￞-v'S$}snLy'ʢ}$%' W>K='ٺ7Kނn{Hoe`:jv ~tąGm&>ή8;¼P"^L4ӃAWoe.eziu[vdt󖎥^O=3k ]ݥn`*zJ?#n;=w?CٖcʏZ?Esc_4|n]Jun Ѣry_y25'(7߼#72u |ȡP[hSd>bP,tAӸ߷;9o 9Q<(%$%cD(KP~?b%P~PyZfQ ϊj +SŶgaU/VI7М~;:ޙp-! bggZVMTa1v/dO12hm]u )nf3qNt\ۺ9SK]np[t@5!Џ 6tlDf_Q\՝7PBR4{*}lzV7gWvQ|A[ R 8YY 9-TOَBhW-z-u:Zw۲Ћ=MYykVP=-fl3Ozgןw߫_0ruomTZ)m4Jiٻg׿c{UNW_cŠ~z'ڎt0X8^m7NSou\߆Ov@iʖF7t9˕gs?e3"aY,nPvuTnWT׸YR֭yѷK6}Cg{Z Rmrг7߰zYߥhJB\4gnyp(@T릢ʚ̒Wmp"ZU5wA>E=8LlX*Cy''Hۃu ?V.w0$RփŒN|g=}!׏A|To;mg@Z@ H)%"m$e<<3(OrdL2'd,]lpwD">ͦ{_[xiwN|͹'P1ǷR7 %9+jM{m/kCg?Q,EM,{K恋Vus{0l׆r geW]'+O}g/UN.mUwuwgE9psֿҀT 059,|0nsv!^`}^e9/ =׭qcNr=6QrjA7 3W"1*>Ti q9Wm}xB[O#ӭ}֮9Ύ3'*#]<'i$-y~&ҳi|=k 1G>j슯 M@9QibUg|4@;@o6jCm+r.`9d*I}~[U?ANgc ;v;q;0@5sQOΚ9rv'}#3jV6Y p=:rfEA8𜈚~өr,,i{%iQ5uksB &e;E9ܵۧSWr,2Kn`@Cfn@z- 74}U^p:vq!`g8qjٜu3gwOxh=Ozi#<,h<}N x?Ij0i۳^1/P3yٮ]c ߻gt/ TYo'[^2Зfv`[A@M.+uZYA`y%q:^ oC=|O/;` m,ldEO񜨍 P׸:o]XDT~(D|K,;TmWJ^H,KNjY&@Ӝ<%p]T@>~B [>64;an R>H2-Sy8Ӟ?}')x~ܷDzR1tcoI~ ?<;`Hq/X?Hl::<~zS] '3i5SŹ~4C&@?;'ۋ/ \AxowC;JGl r|n'`hT̕I%mJ.5md$MIbTj# 8Laz^E9E>w5sa',9C .H&m=OdnΩܡ`zը߷W&Xg #W$ᚅ}PCUF6SZ`C IDAT-J56 kdҤɾȧӰm@^R^)zzi,ǼZ<[bI\o1UQlK.1UL?ݠ\*fg%-ҩr xy:‰7~^-zC3_fnUϬ D51;t}?я z{nwbu6Oz~zާLxlҏe#Qn++ʊ!gϐ=sr6\,774 'M +'Sfv kg찵 %wsOas+8a{-f @nvsPf<[&{Zmp#KDA4RvͬwB酤>Sej]SS'TS̊?vtYVFn7/6JQ/('ddpmHɽwDHhɹyVYwy9mUJlcvw mrK~a>c%?h0m/fK@w_iGnh1o;MQMQg.yDc[jl>' \~}B1`g-ksnw Ѽn5sr|ں.`J+/R8ͬ{*8o>^a  tktbcF( 9E"Z3?D͹l؜ͺn|(Y42D3?]Q>tz8/A.G6UD}$ϔyMl?U9zvgS.?rKA DVH@Lb0e; -ObS6ɓp[YQejGr&r9FԀC8w2ȍhG,ܩ< f':BdzdKaaU>&>lBsv͒hytK\^T:ujm0<z^902tp-]}h῱{e`^T`'2Sw?7 Wy]If@sC-&Z4s^(2nGjL<$.Sؿ}gT#zݼڸʛ0ecp0~\3l9}}u5b>  O ]LzsvL)̖ƪG BPz8oDbs>ˍU3+U;[@_TbQ^*SI#;Pl-7WM4w7O>Lnr@~Q (kle~ $^PKJԥD1 b٨dsg[devb8LJPuo"abGx"QWP7#0"Z=e n߉I[g7= /hIca0ҌS̓xSm[&L&O{&FJy9I ϱ]nMoLKe=I!; 'sꦁz4{Crp̢of,0i?=q3n2.XЀhiL:4؟}pۯ Z;u TXifb$*tm5upn { t~m|ٗԱ1ķ7/g& 1לyM]su׏`vjRb],[RoOlT]5UW(rB kn0*gD$rPӧ)I8ká\wm= GsXl*/'nÀ.7`:@#_0>  7QcfE.1o1ŒvT(SA?1/KkabS}?\vfzy*5;;4x9G?`bPϵ[o ?pyHf46`g~8!.8[w7~)Wi[=s?h!wxf/I3h Jy_4͕*pWaí]'ÙAjmRmamZ6NvWh14â<%L ̈e7pIф=tOQwPxWmO;ptuxOoN7W}z`9S &bnHLݝs"j&is[j)k Ha3MdBOt5Q3pf;G^ಱiu1H8k&8aFS5BGtTY=UW4$/↾(tc4Jy{tyuwrzedu>Pfj8yq:A57=ONz> Snk;n dUZ-xPqp8][/p<,Kz ?m`_:1fV@ۿ~E9= k(qq8.ezrnߺ}箏|b硲lC7',7ZM8 FH׮TA,W+,ZKUhxuX{$;+v ~T$l |(=:ʤ0a'' l@0 U:͙l?qB 8rׇO7ڼu5nf+Q {yHdMIQk>^+vC-Q7(v,55񋞇oh7[ ? *">msI$܏n3D_:C"gqUSi`O4}9t7Atp=ٕ?k[? Օ|]? `='W7h;z&+f̛@KQL5"py"v`SjJd'o'[Yq>לsv:N¼ȫd/h.ڶ?߷p?R"R^U9 Җ Dұ|8D~hJϸH#-P24 bJc1GL }19)AҸ5J AFL)8JSjĂ}hWnwܯ z{OG50&{*Or)pN1@Gj}m^nfz̳\NI_|KkV-SHZvK8؛4Q֖θmx-;s?!e7s*mSamwrO^qh$rqp ڎ@GFӧOcmԤjV\XHM޻|(u$V=s*q ;׵uf rQByxǗ I"zV^SFs:ц,O#]U^ 8s\$( qX vIW I v8}O0|ɾoq1H5[v+V5|-yX$-E\uf1vHp._w<}ۿV`ΞgW>M7[K9[cV 2_` R`90Ÿ;Gafs>ݲdht{9U%Yք9Z:\׹ʮ5;3EnҒ'͒t-&Վ Ltz]*'sSMm@;-@Exr̀{?:kU=zΟ\;iUEa6=ǛNy.(SffxYACbtG&]gPI {}Lީsopd=xjY}_nI3ٕFkoA}<Ώq!U~G!4:ڝ}T_#Z4tѡӋ9ihj梡EvvKm^Ij""yV&sS#HN V] I˄r #]LJA',}8֎>+.efdZimIߍ]B`MD%_qRk Ddl'S}[o٢=$cH[i@M<ayQPf뭝>2IZ%]6B]'ЦS3@x璿su鴿>kUV.dujk`Y4Vl|:٨:brozoN$qfS{]SBjmV!%pcOϺLĶQP̈́͹LQPɈj&O곻V~ C+wylB.JXW"{q@e|pd?pԌnMKDdd锭mj!xBG53A&*1GFTb1;{.7!wVpm+ W4+]N eU0*tl[jFvZ3Q5(˥(]mxxֵ >]{ t cP2*lxC_)`;p+?N]9xڿ!elݜ;Ut[_OvR42M/4F~%7W[8*7ZEVϬ٩n78g+=c3'^9bA-sx~Fq3zszmk)X;W9ZDs ?eQTg2zdzϸ-d#@=N;$BJ>7 Y }9p#ކzutOJa&΄kea{ A{AVAոv' [3xf|/E\.I9\#$[DRaR=L]Hu"%iog z]<@-fz !q>iTmK 2vmYŴVveR;;o ~V7NXRCWfK'2WdgKSچӮLHi.{R$D6F}zJ2(8PXPך8[TvrѳJ<.E.OV;(rE!6ˇ`xу;=޹'a#`¸t`ڷcՏxv~^7d&̗|uD6H`$ŭENDM/TRRcMAAVYZ:/'nF#pD ".&ycj9B xm` ʂL0i˂ƭd 2pj+WFU-3u.YŀF<:mx|ꭷ}6ܯr[@v&੫c[Lv2| ,{A27{pl|CӋ1'zL6@٦ZZuD[UZe yxd2u_CrWssݣs{뉽DŁm.??а0p0_Ԗv{7SleCQ%s|[/>s'4r 4ivj\ ֿ_wa4'ߤ_|8/Hn[tv2٨_>s֌>gN ab /d>ǎ .1 䠐x 1:ǰPV ]*V<{K[U% I-e 8^m]VJ^#Ȗˏ1 7 ܯ۲62Vq%8vJ IDAT)UK ?,i1?*̞S쿯P4'QW>"zDkC hdԁ0}Ɔ=xV_l8X>e_{J4h@nqZi  _+#("F&W{xE[.y@=ghԍDo:NDMj #Jl4`E׾'x?iXZh:q mJF@|lB'[Ʉq"k1.ͺC.䊹3цTBы%У&y5’T/w2S3'v7Y w^wE[uqm7ܯ۲V`OS5l0NK`;cMuY'Ѫi`k:_O~}^DL}nqz<JVRk86G sY߁x6}Xo./4]rlwy63?$d/[s4hnͬ*uDqS XeZm0g?燇G.甓 Dϳ[~m0-Z,h1IHc| I -3521 u73Q7Wuֳ^э/(aY֞hepoC~ݖ9, !r.1=`O{/fc V5+'9+G֭?yT1F[ch¾7`mW|Io;Xa4cl͝R;;׈5w0b9޹u}K_ ٯlVG]MҪdo06&3R՝off6'! 06-]v<(P4<)v^)Jq([[A_rg ĠZ&?4=&$n0O%q #s'=m6Iٚ;$,ߙr8`PpP"9Cf_`VaQԺ}P1?,FQ9k.gD<$>Ku]pYUr3O j5=z`_$o' Pdb#DNhK kim=3hdO 5eb[=P<1mxc'ʮejWmi›ul_#1%`a QLҝX`wHj逰;10;(뚩@](3Sك)9pB L5X$b^Fq MLі鉶D2R*\P}к8,l<ԵkzIy0M]z_$ !hIy.gbهROz< x&ĎtL @7fЦuCAz.zFŧS}I䮃B8PiF.(4:kZK:O W&x2iC >fwxs2-#=pjöI`ǚx̽A\^`>;PS8笃A{֕f:,2QkAaͺԓ?..*5l4x^y$lݤ_(ܿy%#Oi&b,ĥ *K@[{x?m)ԴG׿mi^uRhʞ!yv8?4a !v &6-<%ɃdF9-W$^/Rp !#UF>wն;[JipnG50eJ;1pJ:ZPvS'PO9_\ 4Ŵ  ZdzV;ŀcLzm X֙17WOuxOࣥiAǯg6Se{֜>䕰&^TR@ac3`D_6Vnºp]ZOҙ&&U3u(a|Fia&zkfJnh vZW}uRa;Pxm=J7UMSul:| =;Z;Z%Q+v!*3$x~D~ sUF 2 PGu>42mn'?^[5 #Jb8 SoqcXDSu2 8`aABFS-mGu|ۼgW^aՊEck)TMb!>}\qȪGBׇޤ@9tcI'Ͷ`wO_z{a)':pr%ިMjо]u5G…lЍ<fA->-Led8gbxvo8=ֽ;_uBҝEõA ` 3~D`ͤfk*^L 6C*"Gd Mm^9@xCSroXy$Ue$aCwĆő zP$wHB&| m|~NlҞNz)ó >`\{?ypn 488_I"@J2p8gpz#onJWvRn;%=턭ߩ|XE'N5}&;ԩw7t?:5/yvJSgm_qȧE'+`#yN|Ɠw[/-֟74S F湩kaL+nYZc/`i׭>*j!JgԗD^w4:j 2vRqNk>ky7xIGe|~\ "f#+K ٣*tx3`J'DxV\Ϥ~Si.6֗{qb8&\KfGj iPjFI01HPG2Roi-m^3-}[oyϫl 7og3^˪\sgn}sNnIATڀc} jtPC+FO:HW [R3UrR]z!b;jURrQB,@O  9r20a|GG= ߛ6[qԒZyLmtx]jWFcoi'{~Ȣ"fYX:oy럱]""/>g`ѿ=mcVn)n\7X,-뙏[/o#OjEoO< "P.7AE\/CaFtx#jC0y0>|+٠I7T/['KHTX+dXdDSzw82,vr8Ca޲8#RЈCIG&<Ti4s7@`۲b(sz. u/5ژLX7-c[Oq{4M^s,}rkNȀWzpla_PP#$ˬHf;9B[!/.c:D?yV36HNcbwssyfvVK^|3H;5Յc/Gcq$o_/0].8_]zG\hˮCx衩=c0#"GWDT/c辯Pq mRp xT*c`"VPχ].+*Cֽ cئMc5{qM=oNY.+_\l]>箙[<݊g É1%Nx8"@4fzA`@҄=ng㢉0+"afoҲ=>Rb rtcc3^o+1Xmbman#5UZǿ#5h~k~g~FN8|ozhQ6xYqz'>MWW:;8em<2o_vGĽF#",sTkh$X9:WX/HdF._Xkalpnx:*eG`"@]!=Ym9xkmUYi* ?1ȫr*StD( ENY7(T$PVQVǡxG0{ Oi7Wao~b0<PwJutn+ݙRv R990uZv$[]N&!-[I` 4ܲ~.oLdm}a1 " f7l]Z Smw^DĞuSVPOlGuw;Ta$8 h"j﹬~-<>myX7~z>&=c{ jz₎ \A!hydAȾ#xGYvXSaҊ'm4ԉS0YPyBB@m~%Kx;?eҼ{+oi#LMf`+$;g|Bo{÷vX.١ RlsG`gef3ݐQσˌ!D6LTmdusu6\QԺyQ6_M;)P+vnժe܎gManIFs>SRcn./R{hM>w#j{WWW-=XBB v $c@a g >nj@`{ f-ld VKZ]ۿ53{߫zU}U"Fd{FN}{fͯ>XI|q@ڂ.o_񊹙VxN19Eu,>{fuLoP筡Ƿ]]6 6aVn+/fLg[5t8꒘ #J}4NMnH-%LEzҘ}ݟx"`b<.<K$}U`g!ƝfZ`wMSPZܫ;@_{9L6"=HÛսVl*i[M@g T{"kIּͯ;];Aciq&_z:u%^fz7޾jLkk OL`` +}R Q [](9ܱ"/2(KjAek'"t .RAn|_T1o  iξ5#nU`B4Զ1g6M!%F<D`]2A٘t{=Z`@ڲ(F"RA9J # NYto E_sO LqzyTozŵ 䨹m,7@MH?oe~ݝ Ac~ևo9JYpdjm@^g\If4$"ntۃX[V^h~CvQZ-:+Z|>PZzArvTl^`#gO {#K˥ z*s.ly=w_>Pٸ1H~>6 @tq~\Tg!ҫv{yj4d"n:[ -M2uC/(ݸz|YC'A@E03rDr&S@Q\@?o:aH=KLA)2Xy ogDy\Z;SM ^k~lؚE@@MΡnf_X{nkD|RJ(q|-{< 15W /{p \{HcM[4N B"M E"R5#%5sU%N!Fp pN6m@ 4 C UAeÃjlsRB%tf S/[_fs}/謥,ZB*( pl@x ZCKHa/8e1}BRyؾ YN*O1 @QbqkބG}<xߤ$cj=}}ym@ލ8oۭ.YgʠIb {5-qI(}h%߷AMM,KG3/f o5`k1J't#F;xDKh+/AۗY{(K͕ɼ5?ė`x-4]V%k$Bι$Iex\FLND9.C큝TEZ4*nRA=ɂguWC`|NF2[E j1A'.>s84Qm>'~/x~-3k1LXTR>t)7/(l;Ͼ?]~D?o:nE爀FJ<i} x*"ί:h|BØMP왕ho&vwXYB!4`-C3e͙Ht3^ߦ͢ṴS s_]!#WtHHlsu-:ڜ`Ľ`V.&o/Tdfk :Rz 9}e-}A ,8'r @7F$]sq ;8%) 7J[IGyQ1Xߕy9.GδZtOl'ɨ[(vgUtC7_jԃ:ypWO}ɥNe|;}kn^~kn^~-O<}_kWv6e+&sUY e7w}_蒸M{\aR<0 &Xqz'Cb&ԓ"|)og. n v|~ h76 `ۥ|& 6k؏IoL -J0%n0P)ij`N1IL,"b{GywקCpMW/wpAAĹ 1.8K4.xsM:H/;wXн= ae5H['M"t"O plT`s:cPuۉznr eoAgz{/@`[FSBaZnl[7g_{~zFWTp&5 n)A jJh67<g_f.Yl]@ [CE;n^+Vl9MYt8[ewL'9 @[5зxC>گTIQyXTws@݇2HyD MO,/O0+-~CpMN~)K3m_Bb:Kx*SţߦC="Tl3{ ,`wPtNN7t <ſֽIzwXֵDCr|гKnioh|;ڿqgvi&5%w/츝Ţ22MDlaK& ?mǿݿpsPn i05 w&z@H\ *Qyf5Zmkf<%:4pmnnśʰ/2|/<98ŤJπaa[5{&VvҢ6Q:J  {a <%vXCAq0^ ݔ0?G4N 6@4l9-RćYvst_v3wby,LgGmĹv^ ёѭͅMO^|^(?gؘ ؉!s ˜7&84좜NvhL5Sewu@#M0$u~0ux߇LZ_:~L:u=B@c$4R~jv1َa_*"Z[-,Gh IbV/tp߿,*ن= C;&mg?4閆N0 HĐ{6G^7(oQ#s]^wٿ~Zy ׎/Z>hI)rR9<ߋG:B|{;~ܬ*<([8K֯ /s=4V?`h1aQ:5t2zp6_56o!_zni癳sƞ5{~gF)`v $mDm'}g.GoCz&s>+`COuk{;`NRw{FgQW3皚d'L ַf'c[߼/{ DAsnu&zKծF b 1ZnΙzbiB45H+J#scR+b@L&l.miM&E˃Q[o:d5x h.0WLeo5]Ԥk1e;\ s/|䑷Emh0`׻ 6e (10u}"!"f8{YR<ŻٚZŲm4iޔpf1;h]޻1{ifvGK|O:|;T$8]E[Q=X qi04斢+rfs;82#/] s`m;M"[. @J:4)>:s/]GPlB; S\lq JC:&r lZ 4[Dg5Y,=Izq痟D2sx,nA_MQYehe1 `D`r|{+/[zm@d'c|YAgt Boo*M+,L95i75~{?r M"@df]4z`gP,n)3CV&`jTEyb& pafʽ,D< ,g#?BKQOEz 6HS4%jeiR5@嶏XuX91DkqN7|o~eKi ;Zg}f qU!SZRyjE+ m݌[*W=& Gx쩋\a " T`J8|f/@88g +YsGb.ؗ 0|{書ɝv0}'6 NXqJr66Qf`&ߋf\Edn*rNQH09bkq)[ eǦ 8|w{;U0~K"rt],C] Gg/[@*5XVJg"B,jƕXvE0H.݈A`awt0 JTDl||6ahח>ZPI`Pb\ Oo(bv9k }G%1JCt) zrLastxsg[ٞlUvWN"׳͍NcN7HB/h71&39 a.2̔񭧖;oE3ҙskyuSn|w v~?mB,K$BCgH$|`g/ cX(%-kA$ӽ0ʄY>C{vL}Ʉkj9:G.k 汩8KC$'L6N'oKf^"L~H+$mPsW Ag+Mz+2ss[BՃb]k`=x"-8y4{`}1{Cțn,pt,h2ЂO{]pZ$g iU0b`5c!W%[P @ Һv#sKЍIN&MGx|~L<|߷w7e'G8pEJhE&^vJpf3,lPhL`R(]s?>~SsIĉl( ۗhhpjz'ng#Z!1yfNpŨхnY$HqX\ EeC߯l2"|r$˴=o钜J-N~ 7J.N>`鏀~& > 41+Ntϑ9^A57vbwoՎGb{>a'?:fX5n r1 TLb\>0 gCl[v4 kHIBHUY;UÜ~oS?eDó,+ O9i5yv5uYh} ͉+FCHh wͨ۳:?SZi[Fx { tjL!S۽"*o#3*k@KIG.t Hrv~Gli7VL%*W.`aސvQ mr\TW!e!, >"͵u.o,%z륹२28y,':o{F./n-ٿBwn) RC$vԕ i$۽|T 8fV.?v*=8< 2 ]89y ʲ=?3l2Oa ̃Kj-Z* L3_\67F}8?|8ۡ ,{A'.g~:H(+|J ^'fGD]e3a&?^6M|/2ĈV|ȋN>sLVD1DP-D Jz_/:T|9CɋW]ʶBք7 Ԥ1s] c^cqTQPU3:э h%~C+I-jw}ܡp=7KA t h̚1xb y"G`]W^_n qb`wŕ<\+ƞeә󂡹F"- /tzѧNonFzn93p,׵  ZMYi9q+sW1'v 6R1MRG@j y7^ Vz跞T?"u#E/ē!R *hh"V8 *I IDATwyHˬ EH^؜m¦ 2faw g{H4F z:ib T`jM:by 7W 8@Im_=w g^,{ݷ ׫PDo)E]Uq $EH^<93N5֥Lã&PV쏤ng 5V܄У~.Rh4ʗY_)ZkkEdmj(("se( aAwt>/K'G|)4Ӏ_ٳ@*ƬHs^X=41le( Ҿ+yh|* QPsVO0q!HsC|كzХVc#@S5}3~ #@LV P2J ؽSn=(2x]zC mfaA7҄>7طm\6,EjVKW౒kG"2KeWjV]UC灦V.m{^y- [1td F<zF>'2 cSmݜxS 'Bٵ65Up;Շg>fef!T 9`@O=w9%]8.4<؎\h#<[oK,s?S=SufC!IDUJf(\4AiH> } P @O'Z=kVJF(n?P,+칮sɸ!rI}2~?a8O6_Ozך[ቭnvvo*@1ZkB9qoʲ{MJq y-=[ԛȫo/cax5\/pXkG9L}iA։Τ#ˍc3!* wB{_AYR<}6J*K8.=';3 ;.;!8T,63x (x;Bc0Ȣ7ܾ{Bc~ti YkVkwpMRN>Lr7%ː;y5zfU{]B/YPu GLDj Pȝv*zc@ عP”1B  @ N#^_jhh{`']pd}&$6n[3,Iq҈9QK35 &b h3`\vfe`B,sy_x*0'cG:?`wrO,b~-^{fU34V4{27 ZDBD07Ӏ @e*.zxFv::TTGoU`I-9tkWa8ʩ:v \a0 Z;~Ҵ[!T{5ejo]MrGҰ1nwo'PiEi+ ^ؿ~Cw>wh׎kbzjU.q~I@h2_E{vp] }HkV[bnmv*IMdlv?r~+ky,X`o,%QGGĐ-ve. r80rJ:ӳgVGWRf3tG&Σ<~vtf^<.>n lY4M*&C(6WFBs 4"nAsӓQXk ,Þ'&JZ3FY῍&mv2͐k,#$ 8LoR(?05CU-ǩ>M+14ګ5Jt~ngx@5ZzPk&!CyY 菾CCkܯplIP >CIk/kcHv㺏R'Hpip#ORf|`vuf|C9֬69'~+J]ɞz\w1/>ya/`M;ݘ~3[*f: CG2Hd^@S{ F,j3xy̌0N;8=['wn6J).­RpShe=LcC#USO6+@ztQsugJKEDޫ疛]]~(' M#\S^h$HdF"3ɵ\e?ő+]uB. |2SN260gG|Q-"&˪Pơ8L^!>IF (04BY,f]KɻʚF-m5ZZ-mv6d)BڏDžJ!K9‚6 pniJ uOcqmr9ltQZֽcpu~3O/';n's)r%]E"$cZ[YY`'{ؕhʕ̴|8gfgp♴H Gmw/bMwSXCO,oW` n餜Vhvn(ށj0,j&PbW+_>x)tjp8Qq?ZIfɸΎv /#eM(nh0} m=^po|%33>c. 6 L@Q0\`feL,F- (|慕anb f]ݝϜA%>ĎLx'*T w`#K0c,L56Np><.,o`eb@[!{ dQLW.?(}+ߪ@^߭J_p]MWD:]g   |Ailן$=Qalt;|U66FgbR";n޷}ǗnYr4{(C-~ML e`z`n7*JTwi|ۭH6߻ع;[鳃}_Ty2C`.:/v}; 6_:1טi<>c86Jgy&1do`JO8s~NMcҚڼ]xK^z|m(;\ja'f1P=Ba8[кqa]}R "}0&>iOuК8(DG0%jy W@ e^ ^xiRp1)h.ioYyuwX?dbDhg%yP'+r(n8WG[U#SK;_XWXQs^n@NW%Alt\;Ǎ2xcubܥ[?u閘5HU~_qϝAa`* ``/#M1]-T9:/,Hj9G/Mw+D主Zvhm@^{.!ϊȘ0mv#0-"hH_6ζZ^7wknʠ EPZIRQ w=yo7Lu vpCE=!p7_Tc!"!2K@6Z\NPX,*뎩.xiH)I}c*뤨.$);i8'.y!-H[.AV;1a`x@7ÿ se@w[vD.!cgL?7|g*Ek\Ij1)e9x˱'z ^GDfOxC:Y5PJ %T:aDSP/oLn BX8;˒vphѹhtQ4O7,4_Z=^FHD*:yOxǁsݺN]6+v!|o/?BJl&X\ls`dHemvT%>6FXVV %;_h~QVFe(5՝$y{/{?57/η`! 񲫶`wu=NǦ6͗"lZfmͰ&DeUJEZ` .38عBWwtz*΀' It## ɸbtŴةn0=0c8Q*z; C?Ke^9ٶn'|f`i]+'%Pvw@^EH$%y}o9}?Bpd~iɝ&suybew\;!D0I\r| p*>Nꪴ=8Pen+;/KX#hXj<1bX߿6mʧ\ns3Ene._ZUuZiww>ձNN]';\vЗ3?ÛEqUHgvg#fN34yns-^X߻g53Eʚ 讬ThyT!T9[5xӎQS| IoM1a:`A~-/+dܧvȳ׺V`u:l t k"J7 cE[?99 Du7 &x}9)S?%KD\W%>=7d~)Ot {@;2ҹT_4¹.ثҹ6{r]N~*e }C;7Mw4qR/ޭxP:/:qa ^ ]9jʀ=JWxs_V=[FR\{<^f NyDi1[_~U^\~oㄠ3uPBcGНwn}8MS=|eKZ[H~k6lclQ֦Wlz|a:];ecj]X3V.m_N ~Vt͌4s;ޤ_/Hq>by)Kxod|a) T mqexgh)%&#l̕> {6k1.T:וmKR #_s}AM9EmF'!Sh`\fJl^kFܾ'B u+`Q5ZzٞZ,a gi[noݏaZfVv,}_b|9Z ,.DnG64gf|({/6.F^u-83yxo`۹P &w1ܓu#`ZFhC;C;n:h4TdyxNu!f/n Nx Jr'z* Vxk7H >/diܘqo ☔|쉼U|;Ըz_@b̎wGվ?Oȥȱ]+ 7$0ZBY W5~, NȤmIqǂg({7x2"+fFlo}p2-81o_c3eN$OV_i^_ [_@9J[r[ Na[t^Aa_Z )_]vS,b3&y7&FF#m"m8,BN5o)ut1sh40~@GdF.hFJoG֛mib쓩mQo?+ XhB /<0^ѶG<# 7cLO|!]{n 1hN5_yU0w^GGm|-ǎOX;~w}#|uhkk1!'ո'`E`3߳7ǻcY?ۧ = x/*)_C{|B' ҼYvm%#z={զTD:ކOA VrF2=^Ӟ8IwSMn{ W@:y*ܠ۷N%m]C|QhF ʹوד 2*t}̵`?!;aU=sG?;^]l!7/YGsJ/:ZTm6|,llQ@&{e SJT!B>8ו.|K:ce^&37PXt.\A~41FO(Զocnuy|T̎z=X6\R' ~k#&(w~ݽ?[?zubAX1JF'v4䚤 Hџ!Lxn܊iAU +(;=!wk>TjF'9LM4;j1j7Cӂ_;p@^E-{ܴFbvmo0IN3.Ұi2nO֙ }#}K IDATfgέe`/Ļ'@j5i}z5k앎N :BX4 G2C%j_?'.˙q[~`#s{5X 3}SA5ڻW2%W }I8aĜt 7Kk74~{J\eii/H[T~S-b­G/lz̴t tUPB[QgгgV+DX3|'{lj˳nأ^W.'.^*vZhP' 5$[>vNnXUg#.CiքpY91@{÷Ş{$d/uIn4?-4z` ˾ nIuiw L.S炆_r$ {z%(W|m`^yr\~.@:6|tuDogr z`yߩ[K1\i?5fH $wwjI/sɎ (bccًXl'`IHud,+V(f kWr#-O26ͯg~o}>˪]D*c`73Vz;_BUPw.jی-^ۛ?eKƭ߉3w?bBY_}߄>ha)FO3 s8tQ,m?N=v#@/F3f#Qw~^!4%)+khյqKZ7E?kӃP$JNyD?)茭iO;n#})4Hf7pf+-e庸e u◩%Ԧ'/ Kz`S._Ɛuw6JRFHy'j@MC\w],o-X7}lDSV`QG09ĝ.nif;,_xLsc_gn [\ݕLty{n%fjU=_,GMTŹm֍jńhF]@x$7ߍak$a^0GV^Ѩ@Q8C08Ǎ>Wӝ4ӷ RКuؼTh>y~w|#?p[yYF)biاBZ5ܤ>"ZzrIY]Cj+K*p ]^. _!(0|ye[nܽ2~a:НV{'18SD"MyTrxaI"q !%6~\+ 6tob36nrܐtR(gSvRGg1a^ ~ڹ.0313w/;;R vD.t+#JKQuIB^T@Q#1;4xI7 P窄^O3 kLj[FȲ l6 mnY5pڏy si*v?Hז< (WxX ._ KtPItagǃqD%fVCwO35Nq D;;IjY5qܫ\Cc C`̵KgKgj|3p$`/Vܪ[?\-%:_ ZYvۃAl"x\Y?+zߏOX~6w~'}`ċ¸ RHT4UHSg(0 nvaNz <X0aӮtԃ~ ᵷt\L`4ʩ`H?~5 X7xr}8[=N!5҂bR9<\/mX Le,'!/Cw" 2YO&+/pq P]dI H2@E|~ՃrnWiJ603I܋[KsWl+}tRvPإjȁTk+BmDy͏spRqƇl2ƣNsXC>9jb;D^hxTd::fFjf3" BBU̫]*r,Kzmc7#ks2VKqym|_ ˼\=Gq'|ȵ~L lp捰`!xs)]zVya# ?&9NcQqqZ}Zy^ C?{Fȧ51U`UkR|HLcc%1B]֛{4}ܘʂV¤k;ˆ$A%Ӡ>gO9=rpaDˁ1qk4{O/oϮWc. 7sE 6jv23ߍ[ yGgN-WwL, OFAoh&}A5X )1T F@#5; D@b0<E4v#̀MψFS'v R E]ƒIõCL (Qʃ~,/n~N3t 3`s :2>xo+RЮL f2N'o/a{MN{nU=;c6D (ag@vPAQQw%% A`!NwYuU03?Sν|Ϲ+wZtaX U`pMeљr-Y&Y0$8afADeCG@tFJ~-H`UTd׊ y ԰΋[nɩݫ_{+>J !`GXn҄P{3gqh=ծALg8I"S(f9ˇy& W>_]?kjAk8ۄPӛcpz#&!6gg#b.詼>XE5 Qe}mY2HH)L Mg^~e# ME ba 6W WlXz5T} a]#MU J)ALx]sִ }7>d5z4MT-U)t@WI2:d){9X>G_Kwparae7L^wz[w/&y9uqYdS.zSYtr.ݒRy'@eCN$D7Y<$:&n3m@ʞ TU@)Hj`EXǦKU|32Qmv9T Ֆ9ĔMhE:E-瀾jr٩_UW 3×{`q(Sz]_} [i|NǸusBKhB{ dz!@u-&*!;n0~ǖvC.{+C 4e@)N%%7 tO% k CZvwj0UFW)\:Xr U uѮsw?KIb7LvK\\{gno>yg9]0]vQnLw1 ^ h%hq$h)/n>ceXU5bݴe fD _p #;{]uAtc)辤|'NnXа kD/$.Lƺ6yù YH!:`xxϮaϺg?txc]\🽠˿cؗ\%#M=GXr) (lg@&_܅dQ-Sl%0{y|7$jKb8D /J3Ope'E)5,yPl^?:r{>fk)kΌAZewNo-o֪?\Z[jΛolrQНa#lS]K|+wMixx]¦%Di 6d& iy<W{ 5߹ Ni3dJJu}7q⏾q-ֻ7Orަ͢kJJ偵 ëܔjLш) 5cN3?3p_sW\/Kz FwtuބF@O1db6 zUXBT'2UvCx@" 6':.\S4qzm\?sº3bt?;"=g=w:9}|]b~؉U cauIC!( #>lCmmrrd"E=3>P,4@ƅ!>MaM)G{LWEf\? ѥ+?v+_gT2ko GNP1NpVΥ5tH~C]xO?7̓/޻?Et tC 6T/"@tFTaNNL: Evdjbq4"r>?(|V^x:`@ݗlHvv̞4jQ Q D1@Ž9bfE{Grw+rQ]+tyk{0{S u2A]g |,9֋6lmU[rtQc80c`ty/'#|{tPnvSgdR(0ADېO[ވD5ӫy]*<9S.6=Mdk!Cz%):=' p:BfA -%Զ?^o}%߱6 _R2jIB( 9}^n;4/ynZ9)[{_t`_~lS6- * (KF]ţV ufZ~W#vU~7{v]R僲_̗d"" yV.\TmnA;m}:i^⥿w_-~/Ct"CvO-钱\rM!,sͤlH4CxQưX}hdEfçC&[v?ݶ̈&@^H;y4Ns ^9Pw*28-˅?OD2\P5Ez\x=kX=vM67-} !U'F5@M5!r~5ʑ_!`xwL( vٖ]EvWq#WKKmJYY_t-#0YG޻ wJiP6Je{+k_umyj|WsH_tg\-t#\ oi? K$˗%qG~9QdɽV(p,vwwN?G|a8_q ,ױ,oiG3RE=vH/f5E;J0<8ڟ7ʦ&N2L=И 6I= 3 ط ,pn anĩ 5k#Cu?>%#u:)y~ h_پa\ |2{vAky-z_"JK;gmh7N ?.^ |ps"`8Fo[F8.Nr)WGe;*ŀ݅νӓ n ,pfႹnH~wf׿cw\zYSY%ayH@ V%ŖAZl.6þEK`/, ML(\0m}p``Љ)?g-y~Uߵwd[状8F@Q"8/ g~Rj2NN'`X𝱰SeR)uYK.81j7Ȭ:k*P?sK!3TmQKZt\F %@qb5WU|˶kr|O}#?+bcf`r$ɲ4ي-,p\b֮1n:x ?CoX޵Ku$2"^ pݫh"BMq F V2YJF^Uج:U&ˈ٠]';[Ԝ@W5aw^S06nӔc9$XhG"!.rqFBI 姡w5Wb[6s/<eAKJlgbk+/OUJDFˆY}]tMJ;)=|8q E>4xg} x۟~ȟv<l>27+l:V7}Sw`WNaD5}+M5'4dN2pMwDcϑW9ڀAF9]0hO j6ahrf*T̡{Cn!~;#p٥:ƔQ?!{vܻ 8ԂvY]v<, "m9| [̛*I~d-(L󟡢lڿYLn]~Npg`-2B퍄L muK * ,jՍDEgѳj =- [SL.\&3$n9$BK?GMt8t) U]kR3:L9ٮEٵe-0Ϟw7??ܶe|"aY.{11(f+JqǗxn=7&c;GU5RWgk3Fr=aj tJǾ ? +ؘ,s"yxJmn{ڛ lԐK~9gbx[*$=h^A&CZ`#*h D];H2<ԪRh (>oO9 A3Fh]DS'*eq;>+.>@/jebm5fvHIάnCNPHNwaܟ-ݑQRnѝfEg"TR,$1 )n:y2M;Qg3˓"XFa6]a[p㋴GxQep%G }_.6)Iwfk+Z3zV|-rQys"l, Ok:zk;,QPfs#}qr/2,tu&g)lgn ?2e5<TS5N:+ksyn"A*j,9 HM F~]&3ov /a&4| M1GzLQ_ߩϥY$ 4L_予s,rzUvKWj6ΙlD:0B[3'Caߧ'ؖq ~6S{AIJ z 1B]B(D-I?ljwUfw\!Y,5m {zw,c[аT.[(/fWj(EMԩNHT0A0 ) Xp%M\ittrs\@vYCDER},ޒgϻM!ꖆqwsqs/Q6_%D|S !Exxjæt6= KX$ED%wd=OUT'HM ٴ}q(.|* _͏yI"! \LY&(v(ɈIX*#kÍmڎE z<|ğ[5:I`>u _}ޒVmvEidBB SP= F)Ks1 zM_|񫇫CR8'{|l #J ;Iv RS btN{Ŀ3ɠR:V /߸7O7>V;i=DĪb$Jhڅwv,Unݨ4!-bM] '{O-Nu ^]z9ZK8c$Y_IZ0GM@:BLE`0nX";-xmzKXIY=Df ]E#T\#/'vpv2 @<to#}ciFao#_^L洓n~wӁwTӯ Kxܝ.gGk uM*{_6VwhWӮ_~'{ .ڷ.oZ]?O1G|knئaGtTi|\N"uD%y`wt2qX5&R["eV]٧=̞v5cLt>"gY,U(ˈ'h#ߛcRkD @sE< ۼzޕ/\"ۇڵu-EC[y{KU3cK4 ._%ǟt(v>8WvfPU7^=cB7av--8d"֘RjÆQ&|pVtZZf,ȉB[VOU :kՃ@d%2M]S %z~X}%LP]lRnhX5# ,z }B8 XVwYWCf+Ъu|uxwZ?ؙasVӳ 㕏)7nO-uQNs.Fc)C~H-EVevP5%矬?l<-cx@~Hzg{]="K1صKm.q:XjU}{tu_}w9ZoҐkCN ;lkLzw 򬥅n:9xt+"eHSz}SGw]ŻQuQ0֐hlQMOD\ôu ݭњϫu˿Oӈ-nŗݰ{ 9[Ƙ'Ɵk~솠Nͩtѹ4{|ޯ$RL#PY.Jv`win7`7ۀ ׬v\lV-JqHd>p+_;:vЏ%d>r<\r8JS0FAU9+vFb"%sYX=TMk|ҞT]*[?Ҍ\+ 0RWPTJ. bޏ|In֧٩ ]Z9IR~kL2Ge^ͩF{ḳm?"֟twqɅCaLkyW'oO3RC5 sM]te2FaYTLM h 5 >+?ae'\yxƓ_;|בSedx,eY$]|,$0 $~Pާ:ǣFZ Ѫj\mM.FJ1<ݢsLٸ# Ap q6LʊRVIѤ~0#ٌ yQ*ye/;8#- :Iǎz_COu~.8$yǗǙq 䵨[8aj"K "Bݫ;Dj7G,Y_R 6!qħ3,?dzeVfJF㽳 CwS'NmQa (bT%Xr)s= $Y5pWrViW[!JpAD QMϛ3yVȚN}][}Wڊ۶ZҊ(H]տ1na(qCpЍ7ft]?|/[pw ÌۀGOjYɨ qg4Q,dc"w'vE.{q4٘M<=>w69g/ˢdE2^FvT V<8̰LnыܼJP9&kc89,6b i~ޱ_HrŠkAClvlH-'vfA)_YpQ=ʵGiphxIYob2YlQD:׈B4?s7<'A`qf+y #ڲةax]nj<~W[TKXȘngb}yd"˅FgSrɣno=o,r;tn,;;_ى Iam;|UQ 9yg̷ktgU{mx"$"'ީW]%m,=Ckyjg n+{n7ívϙ2xȼrsS)oB "Buf[>,̒m-dR$)DUIQU=kX+0](NUf>v}" 2g|r6577GSӹZ^zS<Z[#?xq{w- 9Eb.҆#(vȌ"(JRT`,|ǻ?{V;1ӣNOϝlLM֧6OOlyj|d#_"K3I:v)IsmJlUa6t01܉ IDATbc# u0 t/sX$ƈV LƽTH$976P%mVO U="Am@\y߼o0}ov$uSE֡ʂU<% DAalSbuFE |4|pY@H$e1zcp9t/q`ыDH!$q'QId8#狠{kJs|,~/tJ2=˺o'TG>s͎u_g3ȉ¬g HB(p 7#[Ny{}Mݬr35b%w,2V^ ЋL~zMRd9s}{[2:s?tNp=ϚלE!CZ۹vۦ mȹԾ^HNIM'0q"^(ab.NeL HgWxi x@Nonr:PYo>1 uqu?eY杕6PU*?m @j<hu9.޺Xw8K]®pHmΥsI𣏽W=y%!aõtq{C BY`I O kp:MG sVS_~1bxsIrOoW~'N򿆴^>;6^7r|UDaukrXnN+4L6} N*V픿U \ 賓Vg< JčƆӻVyQے )X`f eǙ%.$_1]e/ *DH:݆YF5\߼=凢VPf+ w`Soo$ڟru8Ov}o?wݷQr)Q7BG6#lD~XgWjHRk9Њӥ_iA{ 4B?gbH-]c)Y"z6ॗޘaV|W9ċX"H/+/L󩾉N?=>Y[\S (R6rhF\UGv]t|4DVS-95yf 2z7-?ɯzV Muǂ:DtF uǂik|Wu?SH9ԁd KTQ?>4WPHҷR7Sߔ,dԬu}j*QwH+k?dAKh,w~NqfM9 D$,+Ʉ>socJ6_|STkP[ ۨXtIo7>GSz$Y2I1%ak'5J!9〫.V"kʆG7}Ȍ̅RMA Ae.ڢuҰ6&^ty0l%lx#LvNzUnWY贁zx7 te k{~fp?#gz!(X9x4Yo2ΟC̆Cd2ȵeSE-jc?w3>oHx*رy)ĜJhしdP!A Q *=L/GN{MQRK5nB g ©NxqrbHH.RĞdxJpkZ-?[A;|03 2<ѲD Οg 1Z[uAfU/tf*x9\ [_:ްzzW<-῕tND%!)wIr7Ӗkl(1-6Xd^uw\_kX4 If]9uteʢ86!Qln7&(IPFv>?,]LErGg<%:]r K_~ԇ0~Y/nBx[fx1N-,tjZȅǿMVW!(aˬn}Y<bxU bfA\lh2͍Svͧ; {f3h/*pwCM NKzo9G/͓r;`&J^#vWBM'$4.&D?&|λ1:%bUr&j:6JR/l1(RݑXҬA5EAy^x}ozv?{.h̅7ȱ12B9'.%.0iO!wa_uPL&&Nq>)BNc%2e0XEQxpܲ aP4 PR`09+-?W?hes$߆P͊ =Ә8q'j,%~7&Ӝ@9xjEX5nCSj~@$Tlddmvˇ@k*FؙP!c{m#:ȰWJL?kt끣Ii2nU@&n&"^YY/=YAQתּ͌8c-VD?8J-mwQY]S3o?|(-IhēUyfg`i$ط$s=dQI;]@*QY~c Q8(a3oq ɯbnltc, #JS>0RiGK:kʴ?y'|9`ɴ:EE}Jj[XznD$@%R$@Ώ]$33^5[}l9EA@NcUBzç9.V86bƍ!0֐iG:I(-x8A1- |Geug4~\Bl_Z;6$1M.zr"VkU+ O5e#N~qA qju<4yQϾC' wO V X)Y䘮q9 V!Je9JC6s~2{ Y!YBFV% nD`1ڝhQI,o7umStH]g P>Fx^?(m,cs#&[cƑ-kqYfvىsmqПf-q1x8sq@G_1նݷ1,rZ٤Yϟ؎3 c8?(qcssck:qPn;_iD47֚4 ;uk5Li`|pG; O769_҉4E(,,m5ֻ\đs/ IZfLFNt: p"Jl,WJH*HҔ$ H.] YNQӚ0NG7g.Fgg}ߓ`<҆Z+ [2eb~ŴH:z((@vl?o8~Jg[e x(i/9v;Hp6qr'߸PFcZ=|Rg 7+T-# <*$t$me9yԭONUyW(@*@t@fMTbcئJu қt ҾS9ݬSoVk+ܳxSCuAQrR@NKTWWn,C%}ˇOwձ$ 籍ܭ9qgnH ![v`ݑnJ[JuFྭ/@qtԼnԙ m\?ލĤ;4Z|&()o@X2:Yj -tَ5xj(/Jo^w)o*&>Q Pftc^^2i)r9,Na16w]YH4"B: _wo|k16TfI  9_,Uq ̛:#!,.SOo>ZsIϯ@=i%!W,$I "+*aZx{3.TX4":Yt]cZk]WWBvLEAQ! 6x;+ ^\\~5K^| n~C?"nKKoL ~b֣kuӬכId!&&O9W;Jə NEjJ8M)"佔Ѳ7o?/|3^?GNw^L۪YE7G ٠ָɘp[@P9ܔpP%fE"iG,@]ZYSQgߑ9[HOnv" epP?W؜8ηM"۫ 1F~-VdLG9Hކ58v޷cEg^vHjG;C )%Ӟe壂:˱"k@5KGj˜K(e";nO: pܲ>im0o'iZfE*RȬLd"(zt}{7{g|{ɈH ULηgk;oz I%`550^pcD0^OlʩQM,wC.',/)ͩNG3 eWu'J]$EP>j|)Cv{(&m2@7wi /3ëqzr'$>0SQ  iHtvEY- }l[tgW]}ێc],/3#ߜHnZI^jG>#u&F'M1K# Ȅd Ko+Ӿd-d70*qcm?tte Z،#q l &d8 n1pbCid׹ա;3WVvS1X`lrodTNM6IZ1s*$ ISYnx֖j!oߤ_r0AUa^(3f]KƜ!j?GxiWJ*t(7+^ǵC0r# 9GC5H('cN ZdGpe8 eCN zTkfMkO-Y,!$zpM0( MMH{KPxaS%do/Qm68keܟEg_9kB;\2A"`6Dxoզ,튤XgWz j+.`ZD- `OXeHlꀓX.:I6anw6^>gŐ gwn(0Aw]kïDأl[mlV:1])U;͛Փg EpDDRTL)i gG6M) 9prBnr pҴY!RgKjZGlߴB-ʒڞ$ WW\7tɱ o•XrJʓI0;κ !E)Nn`))xD.u)^E&3ZP ݷp>"qΌL-xmgO+{ʽf+#~Fuw,Ɛ@uA洐Q.,%DLTD%Tc!jIG9hG\Tc(Q"ytk7^O ,k^[v??!:t+: 4^]&E(cy[u\qܻe椽N$)?b:~Ho5hpiIV\ueZ9#T{]F2,#/̌_l)-X.\<ʫ^~+ D eǤ,W~z03$Boi#  wٛ2Xr)E R 9X{ _B1)~z vhQRIN"! ,7r͛fM_8 IDATA[S1*ŗ}oR~dIAۻޛ]\y}kU]]jU]@XHbx&p60CM8&OxacB/ BԢ%$uRKTZUuշ<]߽}_{3O73<@i麰Z|oN~=۪}|Vj{ 2 JH-dj!AuݑeCXEwᢗУMqP.arW?IB{˸xE aw,UG.%~6m#(DHญH9{mXeCl3,pV^Wڄ S\ʖ3(q?0s ۟e%w k75sK|b;?oä.#o@:6V[HVY`& $DBB:Uvj+H;ۃϿ'+b: W84 %jv,H]E"F>pm?Ʒ㧿od+(}%Փ?}Zi+ekw)coAwyc˷-.H6Lh ~,SUYQ^ #ot᯾5t6,DžG{+PZami.PJ>ˆaK: JBimXǧkӯz2NMӵ7 ف.) sQonmK2v JWǿλL"UȂt's_$(2lR4̹uqR_Rr%3<@s!ڦ>.RPP+[veȜke^a Wwuּղd۰Z-\uFףv*lm6}Xa=hoR8O+VKϛ#"ӆ `WS7[-X4z&(vk Tff Uнqo Ơepm-r{ o<}/|>t?ɩV`gk-lvuBoYCka ?| 1 XK}ǓS[6h "blՖW!q^{hX0PXӱ7,[8O"rk8v#,c ~6By(@͵bFb`K1<ɑ=۾r O) Sm赻赺h% ƁahaS^.ricXJbaf=Qc3#,gcNXt;Mf_c鴜4;w];txN[*3\$S)DEJBJM͏-58n`bw:N)*!0g`ҏ5Eqe䔈\aҮ5^ )ES2&q̽+G'_s?}:o9{ªgKAP]vmC:.8iKL6]XF`[[= y&;mjt˔GmhoF3*V^n E;u"dگ~hDMElN^FBY>5܋.${RBu)|4hkĂƈO ~GnV|Ch ftd鱳ॼ 9xGd܅,H˂/i pUPH:]⺰+Ù2_(?Dӛ)G P>EA0^0'%W?DaE)%h-nq{g`[6~_x ğğnkxbhw:*cIc>i '{{,AAmw֗נc;Xpq7:`}i{>;;XjpkvF9]9R$$x<3V7ש؄ b֤-B U!T0g|_ݱlSs;\c*0 kSpqoSN)s&c>-``whuӅ|6Gn^!}fe{#QOUl:*D\9ޅTܗS2\g>|z?rG?vˎ3t$=uI6]Gۡɠ=cnڏ^F@)J{2XN7l H} 0c9K;]Z.s^}Q.ԁVLr< 3iu ĂGQ3*qNKW>qT{oe{y+$%$X[Z%-jw9qXlip{g@Tڰֆ<_Ћ\: 5lAX]v iAks\3^PӲ.}=`ޥ~0/XΠ$;d{t)$s2V4_*)LT$-P,jj&|l1/sU{x%OzNXOS`LHX̀XZv"lɶE$Ȃ6 ǗZVo?!  `ٝXL it Vw N~K,ӌ5< e_ E!*hǽՙ?!I{$7^;'[G_]=¬ڰBtk7A J+1  &`kE139Z@Gl8kS6i[~R* ` CNWN HoZwv^+"?j70$8^*iڣ3ͭ{ ;cksOD<>"3^l9,8D&6&YT|y y/aϲ޳rװړ7b#ySw}cOӣh mTfoQBZQЧo4xpiB4k֖݁>+Yvׇŋ90kLCp7:ٌ̽ ڝխY0E|/ EL ]o,A"<`YV{kpmkhgu]LF.7n'ӯ K7_Սa؟W>vݽZl`t~6+h_l*!xgI"-f|]] =aBR{3`|3 jbiyK\ N&D{t'yxac9ck{V'#%dyUT]U->&Z`rg)%#Trt/Wy(qJ>#_c wT(GDXp *)x٤^@I0=' lTeЖѹᩔ**kY^נY(,Fu0M"!ɿ8oOiC8v?@ \c2܃ۮt2H,!QP>'A Z췛8@GNͬG;X@X}uCaQ<(@)J (:[g/<34 q8 Al1`ז$hX-I0?ukXzav`<6{>4L| &W4+ {)%a?};o7mNa=:.i&!))||sbgMXMVRrӔ zH "ۿ:=)44Òp!^fċ M@$j92 <ť< iFOznpGC_8>ٿ~}e,R4@Bq*`< 21>v /p7R! V2϶g횋( L0NE#;ka79'c_zµ! aa-"_#a@i햍MkB[w^uwzuM[ c#-NF'қx-x6l 'a{+ &fp.?UDBIL͕A0Fs\mG45y6MAi}p!pmb@[KKv 7+D'x~)U,Yh*CGwcNc.7D^yΩ2 r~~ᤫƣ> ef.JڼȰF.P; bkP^, X\>PqN>ǫ |sw:Z2 Aڀْ !5 A|Ͷ-hw{-ں.G`嫫smYk-F,i=hM18 0(XZ^ŋ5YUI ~&P S3#`_==j5i7kߥws&#B R eIR?TJOg9yVr܏SJi9gPș,dU~TX׳|m34?}xѯ}Cjkz4AS}X;/uUU@Y<67εA#V:uny |O (k;\6F Na>$c5K.u!JifdL|pccOۖDgK-~krdC8yKy6mpp 2Ȃr&"]YM}Yj:etI򜎽YP%:|=C_]%4D9/`uI }!l=pr)*^@:i̖f6i٪uX{Hpb*&=F̆>??{P`4C\lv\iK@Q=1kdm < $XE `w'U'Hpw aPiÒ[љH!bjH+-A'0ZD5cSȞ~tUC'#z΍% z[9~Vi;-48u^pXMʱ4toxv a+le)׳T۳UoԚتIFk^@XbZrL M+`nUi^/EOCYH,4d8DyQ3=,l,@_ggk/??z:Aưp;V+ʆ㝿\ NRbY۸>^[ɀOֺeߣp}cOݯ] ,q̌ G` ض$Fcfv$M|o}1qi8 ùz v6ҀxZr0Zw$x)ەvp+bY &-F*wDY[Gy;Y HpEtVbz3qKp};jƜΙ\"A\a0gԦ*{I{fLC)wϤ©m0VϷ_~]?+cOx2 7f2Pؗq8^4StH@&Ĵea{^XvgOq`⩼Z0R$K gZC{ [6 \|ØxH)i8W*jDb{xU4JU@r@rDG#iҴv;]T7 Ո=zW04>w{ޑ㭅w@)\M~h%;J]w&`e :c crIyuّ#"*_SdrrFU% L<-+jcQ:Jks=F%{ԗG|(Λ҇$4fL)0䌅{gw;;3Dәz$v$=N)Ǿ&"bcjo#%:ȥ;ÿG.\2RļDc˂NJBPt KJسXf,"צvY IDATu륥 vݛǵ!1P*@0n ``F$^ 8=!3Š3`4HG%}6d8}kY ei~|ǁ5>Cj\v8Q,0t.iZ:rFVдLv\[{5/&EL{)p'OArd)YFzwTXrdexGlD[DX:y%# Yp)=ХӖ{؂8S.c-s1OˏMO=.;ps3R3ܲsһmO|Uo[K1K&&z т3D>WK-f^EZʀPec돾1<>:Z,@pr ,)X0؍acIAnˆ)^㱯m[4(mKʘ`a6:v0# M(7 ϋ'8̝iY[fL$ÞɁ;'yG<ɜrqek{ Y *kjw͛0 h RKD\#-i2-(d"boCdAVOп+T<3,jQMy˥mK޲O5:}݇ij\닐G)MU>`G^,ֱJQ3i0o0ea`UĉSQ2ɯ&tToN<U\6,``)ZpATG AƄ3(%#U?#A۳o )&!NM4Dń!Oo3]_}M۳;3QdP=ytX{FzI$5(rxCߒpR8m"v¯ˈk?2*a1O,Cvͩ߆ ؘIߡ)U3E,kEYb1(`uW6s.`vҵmkqaj܋e߉fa6$*C&>D`'4G 3k@ Rt^cD ZuNr ם\}3v5y*7@\ BTnQC>rsw&NjׁUw7i\B>ku*UP+pԠFNgJ*$}5GtrrsgөGqБu8h;qW,G#˻1(đ\PPaEBѨ`kaG"zobp?'>DOf.1&dp 3Q'ghe0&c充G[9.%3Qk$H )];l[wZ!8Wa]7 Wh_Ɩ"N6ϝ5>夻[jEg`/n[[ZQoV9HšvȅYxh>Эi `t!wb'/OK}7Pe7 Kj" m%,8ҝbe6UQCM#zO=D!1OO~ol5E9K[$mpx`c;"+ W" .{6ed)d"A\Lё~A~0|oh}}>| {ۻvke΂ v~5s3-J7Gޝ`.*r[QwKkyfUR0?5v&{BF1|{o;iA@ִ{ʇm 1c &W>(maÖR10 8m" rA`e(VL8O̶hj#GD3'I9& Xk qlpNo ēA; Rq/s*SυM\x_hg4-XZ~n)?s3i?.ܙ wsa;ychճDlmM&> FI  AX7࠺eթ4\xL.;e:ra6PLmʀnm^Χ{u„})NeoXWY]e¿5"+BA2iOhY-8ǰ2 1Z kc__.$@Ȃ;YdA (xlac%|bY\4T5}7gniCe[%|f| *cng=Qhp]fP*(4~^q]k0w]>4' ΖʹtYȁ)h9A8LPP R%Sv h(B|ʬܱl݀x 3E1ѲB+> ?B̴_)64NHa$'R)%F3HJ5+>?+{F} W_UZ0xIy~pXhE.Ҏ JVsAU RAX21XݻkyzQvn)xz`s|;މ۫~Mja{y^TUt<s MoVCKc ,_9ŀKM5ep]u"p}_U+m4t xG8`yg<}(83CRKR>1,Ff?gC:kz%nRbg?`NJư%kōcoP7OMEsW|0w~pm۞~;x"r'RǪD9ҟ5q-'*( 3CiM6!2Q/>Z-Й? g'ŝ1|m 2e &j*'>AC+v-c&O' 繁%5Db7?>OԁQHs h ,}*Z2R}rU Kґ#X,2߯lqA{~hpGtS p]3svs :JE(ZnrTsQvE!{-&0`o eyz偳:l`еZEtp0G0vN &8d (P<[+sQ_ hbe3=fW_~J}畯LI"T˃ȷ@5 ֞iI[* NPd-/Ca5Vʱ K ׂeApθ>Q̥,Y6e&,O@"~_CW\~i)D|w ^FVDhUQ𴟲 3&ೂT,O$$O$j'+@AOƸ2YA`Lb\|f|+OzOGDM>{ UCղxy{펽a;r7dW$QrJak-NO}ɓ_9};V졫Fw]?e)U7lQ fΆQܪͯy_J^pYp',dov g,;q(4[{;`fFZDOn^7" țtʬ|>byC0+#펳?CdgWW/%Nm<뉧NݼWI S*oӬ45ؔRt{\ _7y)JS?7UѰ2d{%}KRJ훔nTk.xcw &G pϹ1,{ >/RĄ׏-UE,@}%k:;XQHcA!V3#K/z[uJ$?\/_+/IGY#$:7[/0 Z}0'W}{Igz ,aԿCҨg3lMSUz/Vj2(5tx{cY_(l,ٳDo%(K_H)Z0y DK05ٚMr{gDlud 8>L3L3)e5+|>w—o=ڶ$̟ϴKI8䰔 &zYMtOXbhb(m9r+. 5` ο \>_|CgVf(Kw~S+{grdkBŞb_/홎7TG*mn*#5= }|doDZzۮVw!*rejXd5AqO>wrp*IMloYC`M٭]/_"L'68̿#&e|[O=qM[ {|E͙-\=\FM5I[-ҒR"l+1P2` -XV3[нW][ݸV2ɭ"7e2?+7˘-MʚOznPƷlX@ZG6-KV"ㆎ,E?[;󂲤\=:h}݉칷 Gc uhrp$r'^ٺ:|k}}^to?Ʊ< 8Mb_[p{sȒ/{r%1e51K HK %#BPv欰yw>2G{Wj'`u#YZև>K|8髯*5x0ʸ7ܙ57cRkubΞ#{[v+LK_zX[-Wi=)GDzqǑ8s^s8~kN;HyP_N5= ,^[|r(< Wy#8 d1=&6_~xRsO6{t?"WfG-o,oc'bViZB-WZ7n/=Z_%B{ο{Д bpuZYoz'.vNgxii\FmC%JJƀ{}0hy9X٘Q3*~H\\34IӾ9tS++.?]g1EM| Oy-Գd&b`B e0ڸjr]emSܮouvP>_?Σ̱s0$]>rm}u bkxMoXeSqwJ{eHi-otvDkDZ=x-vN]Nѓ]I[^ \JZL'5i;.J큖N#ZUo1=r ?xb 8 0ߛqo _ aer=Zb2cFtnZme}!Rg!H=7wj?KҨ5G{t̹hf|Agw,1V\dU%6tę{d!>JrJMpkm%Vw@Z5 BӴFDfe2J%+7[KөڕL܍qunl͛yWn]NW>4}^Dr offg/Z+rR~7@MGzx3RusWP,e)Ĩ,z;83+ DPEyA&wkXq63/n,^y`FRp<Kz+zvl zr5.S8S?ELwR^@nmvR$~B ڣm-Žw܃ǰoM\uWo_ uo2hOAu[OQ1yic\d^dzД̫z2Oadz}Q3AE\5]v_}i}=|oLWa—郂|˕;Chq0u>Ca--/+w{C4YGpGq|(/Gq|zn$_ҲaN L6f@iΉgws8,uzO+ztTR[#S ԡ1HuKUJ0t{XܑK Սk b0*KK5P,]{[>S*shӲ;Cܦ>f )jmk Φʀpqd@ w_f11^9~;_%MT*9 bnY{gff!,15ch-OMj`&˗םVw{iPjH5SIDXH, ѥC8|N)e)߃bv@xa29=8rDu^YǛyC`O>x?w6#Kfhv͕} kR 8k_)Z>U9ABθY OBf.Yu-5gAe;(Ш_RdL,}#OP!Amm߽9p{DuQga+:NGzpxGz^Y`B7jxzBNY@~1YR)H=bNSDZe29L]R)km]yE`LzTc mWV7~?`_~A|/a߅돏ڷyt{Mp=<2!MK[y4ns-3wt}QbGKI%& |1 R<"wXpeNZ-̻/0=?qƭݩf"[ t@]Dgw݇[+3cu;]u.zJh}SIJ,9ca L{p=x-tg$' S`7@@Z+賵{=[3*oiR-J̈ 9$q/wa~6O?䍧JY{^ӣj_*S>/aT ݪ,=}`GK*qy?^;cwqCmE]Et@Eܙ oXe7ฝ[).n)@G'$8X4b *V mmT,˖MS~YM! É02 :t'>cW㓏1ܻ^3Jy@'{S#YH5 B_I*p{'NzJ:.Tפ[Hȿ񛓭MA?%U h?Ոy%]=ص 4NbBQ^2-թtmrvl ̖G<#į\6KǨ_ Z)?m]ᕣ䖇-Tb5O?ˤNwJ I&> ib!s@ZDE܋폢0`4֐c-Kwb|>G/^u]ډj [%37(ùPU)uڌtt4GpGطL;I4^('/k'þ xEaB|-q9pUН.D|SVCz R9'A=.&O-p{#,6 42*N"X䔌6lOW)3lO?7޹D?W/; gʀrfKIPDP/#)"hѡ5$DFe;.t׭$90mw,h am_ʎ.Z;KSWk}삌ÞFDI@L~p܃(?NLˑj˕!RӲI4I9>Ġ|04?8Q~M 4I~o "Ǭ&m޶;r+ou@li1jyNw ;~GQX tGGO뎢w;DߛFS .w7r s-dF}/H& ͕;ƶ^,8+ΑgN=Y/1 GPwfN 00G>Q<5ٳZd֒ t;OK- \L}{ "2 Ptw\pvZ翅ן~]*#c8>~4 eY g_03tm` 8i𿽊Gc3=7S:RJؓIڢ2 GOgRJY3GT$,1vqɑA;rsuZ{7rwW]PQ?:]B8ӮN󀷢Cfŗ/=av(iĭpm=:LOnOXZU}BjKg,prRT{>~˿:(Lj̩L)]?* l@62Ւ#3ڧ+|;'#ޏy 24(!.D||+*!Ξ~~G{^9VHSG@x5D[ %u&̬tc "!E )K؎CGW#R1 +ŽC1<15X,Jwz~rZ?8_=lucu3B4+556Z~<'pNڹދ \ Xi|t.x%V[#VΠ ܉uSjy/"yYS\3|#s>Nm^WhBX&SoFl1&:r+j\MyGP['*RE@~{7^Ʒ_>nlݨ!~壟/EAPoaD ] c@܎qk &嫯?'_~@DP2~5u!"&'1OY4r+p6 ҹFuprPYL|͔^%U{;s(bŸU O-T랪\*QTzҵ*Q/-1U`0 QPHIEҞ~J O"ZCV27Y+LTEvHNhs `[m|hE֙zqs=_;0jwe%[;7~2n4èuR_JJZjSߐE{q08h_wExK㘂,5*}p7hTK-u1;kwT^A9`yu`>\ =Ͼ՗q0 %3A|JpGn^t\^ fpwj*V8ԉ^Fq",m>)G˝IF@/J^X@sNB-HaJ gߜԟS{% ?0xFLF!svhse(W:"P2]I<wt*"QVPZJ r0go~{ޛ/cpwfUA|3|@B!2~:a4 K3vo_í)l.owEKܛ}^ p/1vꚢN\w"Rtȷd2Ňu[ׁ|_pdWʖ_0.%8%?y6%` {rIMkr< Upߥ ٞ>D1m>z;L\D ;oMdϞDܯ^Ud%r2uPFKF#w(}z;,[i|c 2{bU woo⍛ohTwfuRFV{+?O> ;U5d!uA I+9~ $?_ "|k5(@FnB ^nr80k :p^5>%Fdw7eX*Tɯt9 G#g5kw kw9k XjD h;\4Vvrp'&W:p9'_ʽt_RgNpGw=pEl\0CdpφɦRWŦ~ً]XK?t~_?г .#P~,zjw%$M7xkg%&:] 賽v0BE"6*" c"ip.cx[895tJL\$x?,},w˾ c0mu : a<1}{kܾ9>3pn6.q57)@ҟ~(@@؎} ,$ֺѲwWGIPb˒sIx3׌騚/wMNSwr=Tq5ι+Y=ܺpw),7܉ /h"4s@ݜ g;~8.Z}x-G4-1llMjBbde#sKo~3aꋾ膟' .W| >gr2򨩒rZ`- ŬU"Z L~κ\^S6Gnzȭُs60A#&$Rnn?1W6߼v:ػvHSU)0 *yNu~zb}i `@c85Y=*Z%m5/+羈k瑴#!Ӄ.;c9>;iC$i,[~#/ *-udwo/wG+7!\ ݩeM]*af>U&4A78u>'*S=3n~HDMuF[ @X' 2Xd5W"Z>A*{+.lx[+g(%W7_^NSGGCIژ+`heyV鼖 hW ZWi]}rTTyJ`F̈́a`kokwڭ~sp J *zb6&Pbށ;` lhw`Zdz.q .Tx)C?L|/c9*@$ IDAT <Nxw"BweIח 38v]La5Ck0lp<#sΡv琯94I˗1p;8uLqEu/D?4׋E3ˣ!'.Z<!$8 hp̓ј8AH8AlY*5Iz+KLB *9]1I4ڰN 6MR74[{#!oxkoww4*iuQ hO胟~`;`k8/ T@t< @O?k?Ǒ֐폛v̥#v +8-?9pp9Y)IDLw6`a%Խkw)ecppgx{Ѕ cz T#$IiSwhQ u$,fzipHix[;}wGf`7{{ߗ{5-™'$ aZ- M!'iT3ħ ^yG֊1Åb4Fi[|VKz{$WfE HT8&6t%]>&j)C3!mpɂ{bg@Io%fӿk8*J@〪Ra{?lv~`Be=w+.} 6 BrG۳%t':P̓;vg,SLgԴ&g'{Grd޻eijk}1ZYI8~pse9JJhc7wt Bnꚕ'ig/qLT%;U$B Ix3 :\'qun xZi =oj WX?]K<( _2|pyL9#]k%zl7|K2VISw `f;g5c$mٝ *EA8d4U0 63!$wdNQ;m5,n]Dj͂} uǞӏ:>=MUTir$HquoO>;#soodfxnU+W.g=*Ң۾|#F}b1xfie/~7q%3p1 䉐+vA?#8 6$A%& X'a;LW%@IJcnQuC.Mgfp/|Y]0Y^Ucݮ\)=匃nBIg|QejG^({bh76YlWt\=bՓ|q$,hg?9 =fYU5ibU8۫"i4e:PVQpc{u2N'8CLN?Y{DD6IAJA m C)8:o C%G~#Q $`9I4II{}g0}Azowo ݡ>.}HyWNq7+ * 3r|#vDAVVY~j XT:~p/<,~5ہn CѸ!Lv}VK}ȝ^飬)Η*N4 %D b &RIhٵ:Qp;! A54 00v"Z N/Bi4kPV,i4o.Yt3w͓J]h2H6U*>%S˨.2^,>|h̿Ii/Wă~!p~|&50JSV|6IJ^p'>3sqo%eLP|.[}T5t!iLBw RJa!@A I "奵(A E`k߻6Z[Md(ISe(de,>}27 0iS62XB)^'0[*9A~uRM3>3@NZ^ ޸c|OS}V߃-Wϊ܎A0,Vׁ8 ~?YꁤuԜh"%2 !82l p2s4"_LV#An3Mnz 50Sť)\0 \,̐8]Mz JQJB$BRB1+Cw^0pv;'^09z0aW_9'zfz˚";n^5+!)6@R eRcIJ "APHʤz{hc@ T`ijRZ/Ą(ZX(Li-5bX^_(@|ج'yiwW}c?$r@6ҍu@ʩv4t.^܊@rZN HH"%Ҥ"3; Ăz@ a (S+j|Yq`T5GN6Iigc݊dR,5À4dɃ75]wٽBâ_f׳ө /d1Ogόd(ZBR 9cRne~JC}M+NկY )z]I1M5"KAHIR6XdF~R!BAq`9SP3S4{|D976؋m Ueg%d^[># z.tEB#?|*~x~j[~_g%Y=Vzuk4݃Xfy t^ԷߌiXrD iC"\k &{? Ơ4&q 1dL|6]H697mu۪)Ridtmפ2,\pxߍle(eQhf92A*GYk"e}/Sշŕ{5 Թ_; ]^,D"Z/tQ bo;C;u.u2e xwQvPVόV=3CQDhHId9v&) ZkWH) ~%F`(M5N @[I Aǚ) Ƙ|j~dc+X9dXgݰ"?+txCWix-߿߄Kg>.yxa h08fmui<O{WlCRʕJ&NmrSv$%#o yGOɹx0LX BHĉ6ƀHRb/`/fqRPd&]L\UYw.\J[v^Vf8J4r>Vbo)91tt1'X=PvUVG͝x7FLψ1W_<]U+J \[w+r!; 4a<J azF-NcUԱAuOZ%NӁ$$܊' RZɩ!wGzlHJAIbtڡHBI!!hFzHJAٞ<@IAAZkVJP&S7* h^^v%rE1mN/U2@뉨Ǣ ^ >ImqsrTȫ%v*^vxyc?V=^4A+-l~hD~J.,4NKo_llVM;mć_Yl%7=J- (I5P1 2Y; OmR2Yw.>_/>SܴX9LJF28?j:LfppRt~8)@<~ڒĸr#9-MFq4Et @4NzCzɖ3m(OP,n(Uޔ& LagDPY:D)bZ#)I:Lx4ye-G A(PFb``HMR:A U H;QZq|J1p[j4}`m~h) nj2ƌ|jG.ӲrSLDUB|o~˛S (zw7 o4XU laDnM?kArs\ʿMk@ĢЦIUQA@o0\|q7IrRh`HJa5ma%% Rdu ]\JU- p03x&JޭD?v{Zs< "O5u}jh4 \4htk.Go<= 'k[@1w.9>wr]tl:(dáHͣ‘MpNq~` H0H$&)W!!@B /Rr`n,w%;0K<<v6GB2 R4M3IbHJ$D"s/?kb< ޾?OS[7|vW:?OzтW7BzZuIY⽂xcYS !?(o ̠8&'2WJ) ߅(BGr85SƧ}9c<Aab1<|΀1;o}x8J6H8,]$nI1-(w6{w3m<p"o~k\(0 /.?ƮY"▶~;rۄ-}d-!W,y1udOߞAH)>32*;#WGKwrs%Trlu`Ks)3 _xX=pnZa .JB?2#7Q'zK2Ǯ; ?smyY$Ȳ@Y YIA6qVZVR k, W磦&7&`[隅|AnzSB/ `>b08`׮׮%$I\<.]|bh" qbY n(Si{%iΟO4MЎ쁞8#wZ0hqS*#04%F(ThR2lL(%pJe*w~cG<=FebL qpUذdfUYSg`WYOOʛ88*\9uM#^$L-m~%72@wW{o:'~$se.bNqp'X<}!ieh&t$^< r%=Ϙ;7ap@@ mR""bE2(P&0.a"%@4ͥqyzi+P|w`{%shKepy\,?z (5{ 5C pXf JH u 9Hhc@jZ&\3G{c6I41,@60)SL;rP>fVS1SU u28,fc~./32Uu7y=pGacw6wNNo2pϹ,7 _E%o_}>YN-_5Nh|vQڅۗ Se;yK8`E@-9pD^DN"y,,knWRU-i#v6 t@rR@*Ib v&!wUjGnh9ĜHKPBe%X/XLq9[@JJJm(󂙹ͬq_ܪCq;*‘ IDAT0X8S >7wW>G>G>Nظ~ginӫRl ZWOwnPRE'2a++Kl` Zo!;;b jRz;|뵇@" % GB<1(mv6/`ws@NJ 3PP:DSb*o獃 )\o熸5ea|T^fK 6wr@1W{/_%:^D'##_uRY' zyYƏ .>#OgZ Tx;dg(S5˜ī4U1@S;60tg3M(Ys^y$c.vx.޼*& !$Ne/q+H}[" 0'贺NkZk`R? $$ZF[ 0RYG{+o={pX$~֍aֆ9lت!/;_!Ýg^R3jx*z߱wn-7q~읩ԡZVNt^;G:zW)GN߾W>M Q֙Mv, 4p{}l@&ٱ\AB=-wZr @Vrq|s2֕i+1P:J53QH$1RljQRzmgp.#yF(`RDSTk*a?O%aa(];t5čjʥLczL?S]$yHjvT%Hji|ͬ<,¥xabEl9#Tl9a[mew1MF bb",3-l@0Ĭu`n'Z3DJъ^$V3KzOI)xlnOn9H5@ڈc0P^ CGc3 ڭvrS$sС!!*[YRy86Y}G3Rlv8-A}WeufYM\R񅙟' xl3ϺCl3 ec4얙F  [0uELDD@RJbc]Jk"Ye{$")V+t8~gF BAh(dvA,̒2Qrb;Z=g|;D(T4$jÆS,5*>a2c"1"tfB3sZڔνqi<^t'(9->S3ܴF.9I8ޫ.mQSf9߯J%A. !.$؊t F`u0f0%dv aeKMF21奶PRDH# %{:-uю$H`49h2`@R>i*bkN"ryg k=007\{Com߹p4'0s+ Dh'ڸ[ I r;f9 :wUֽDOg@Ǧ$ :jG򨲀4+iR`2SJ~!501 &_cxwo/>fE_ZQV]@w{ThQʒL9{@nhW<= 3؀37p0dXXR tۑ: 3heVr޸zOo/:LC([J*Y6wm<}o]ɦ&#U47mr*,3zC.Td l\u0:̧x6!3>%x AX[06Z3H&10E81@H7Hh厒xwhԌF!" 1KA-^Oޛ7[[wbdR˫Kݤ(Q 1(2bGaF{aK Ȗ&$-wp\jw3{sp䲲 gjh/iEI 0'lb&żICvƄ7~  įgdJIG$CY.I(S m)<>S5wk龢 ""{S,j:Q1kEP #@`4F0G(bLGk}Ez=lhf'-Q$3)FǢ| "(ޗ~t<(W"•q88C\pI{A[ٮ Ζ'岺2*&i2|%:&{':q8[L=9?x= }?=p JS 22گ^|j7WyHHy%mh)ʥC7mLbahŃ{'^ x~|!)o3RHd>-Lr5ya "S:TmiKSU"l+YmJv5u9=yé:XdSr8(EEjH]BDY_񮡋SP1=V !AM .4mNvS= ^zRs&z|u >@<+XOrdGSQE֚}RfvAyO D2HҘ޺)H<ťDє[T'f0,+FX=쐎װ\˦`]"S*pd3r(2J3'LwrajHdO\m|z{>3|Oag;[Mu$;SJѯS"$ @.hyo?+l^.eQL3K. "ڷݞ[Mpw?#?O!rQaKqK R)7vrPK=h&UwQPmݵR_6P\L+IQr6rL(ϴ8lKEtUiq'u>غQf[]i`ʉ,#ZJ9VR<=@s ]8Ձqۉ[ AeK@Y(*#`^z6l}rPYgk&8:y <*vq\UNuRױęҭFknwI\SμE{D]x^.\,~gMh hzG ֱ:?y~mIh O<'߼p]|IY_xdk dx?FH*׻G_2Ҿ2n+,^Ccmhx'`fsށrRVFMuu|tOzSǗMś]ŕq\lM7*+ sGwhJ֝39N;<ͳ6k-kS)Ԛhҁa2}8 ͷmY]I9{} C@tE Ҡ8>1PV8oKAGKp=)/u5[rUGw89U3@]H!bgpZebR|Q18 ;+qh0QVk0Rؕk(j-|k *RΗ]`[bu&j-8`v0.~??7 ٠G\8*+cJγ^^L.e \=_ KBsV<-;ʤ6>9i IDATsgP M6u-n {ۙ3Ko9Ul5\? o{7~ηG@ x o2"%DMRU[’SX[u3Hzσy B-:"2 c]i؟YKָ8Yo+n+~z^(UɫM۝8AsUY. Mq p˘%"͑J]ka{B?8ޘVk~S7)H\5u{&9B _@~.Yj>t"^4]d(ne.zCZQ\ #R-2;X;{q,Ƕ\a< t 98vRRvZJ)?yW/j *[lD>x6^>v cKf *,00d4}iU)P/{oUc&Ggߴju?kx-,UD}NLr[ W\V/8NWY0VZJ떔 |ȳ?sO b76(sf~G<6:i+Q4hA2Ч]r a'HXt2%ѐ1J}̬ԇEXZ΁%"犜c&"gZm6Oƅ2;)jU8Wu"(WX1eUF@8q̞p`!Ǿ,'"Zchni#6:,;'a8 PZڿ 2מyTxaѝ1QcBZZ7 quτ&}VgD;vg)gR>^&zV}o=_YH 1[=!?t{5ҵDBqrj":\L E \X("0"(9,+PFb-sf]œVX\bnk*q9/ʳU" hE)S-ƜF/gLm EX^0a֫ǵo*xs%yNn=Ku{k;0#FٞIqzxNvwC~CJ5%V=ľ2V2+Hd: ; l9g`lv`a٭r>)}(|rzP.g-(f cK ֽuF* y5 KYg_.DQƚkh%:뀤 u=b{Mo >gdV-RVR DnX,SUWN}q1Wo|=DlCb%KiV4ZaAmTiNhOy9?revWO\G4mQ+"xkbQV\b=jU$4.6'iN9竚:\o* qpZ1sYYs1 ҭ+' }O껛@e ;vi*< 0: Py6,rO*_ЉWCb^=}7oyx*=sH'WU@ܭo-uIF"e?G9x|e+i6f$W"_Gzo)10 )lo$#I2}ꈀl#-]"V^DޙNg73ȐLU3#<w*P$0TIA H)IPH6BDRV[8g `m)`Lb撷S\a[a9Ȯx|Wc댰g4@c~c5 pݙR%,gu_\Soz"[j>:&I sk%Phz=jg9lZ;T.S,+Ia'ǧ!]'%}T<,s48..,y :ia^xВ=v~yGcQ4F@Z:*MdY㷈^?d^cm3(QORVX"j35 cEu"(S8Y%O'ZK,`0o~za/PDxҊ`cf!T<`~NgyZmw/~h hM^:y),V#\5}gDd!7 G'H'7<|DqDq< ΅3n2W@{Rr>Ev8^HAVwk31!x148 &?9MLg 4#E8a'HZ_Ck * pl]ʸb]q4)gy 3q}mHٲV9qGTϲ5swcF=e|.#%!\ۖm;^Q_^%pY/sdIY.F'͸0+v\8Eb|yOy$2?'(6{Ƴ.L'76ygPq_~l4~j=)I*C*pQm-anyFZ,.RH%muN) ^a9fBqGv”)Eq~oYLRg"k9<:]lʅ}}&aHPgZQu"cƉ~k+1ZP@ny2RIIHg*s=M'y} oG.jwCrԣ 9t qtG"dvA/b/nNbٵGv$|,Hka'(řߙdY2فk)Qd*SG(5 9Vۥwy,Ɣ)V88q~oYp o9v(1F4 }'"=wo)8}>,ɫfl @?iI. B[lk\0Vrq IM(rR.UQ62WVs;R%M>Lί].s]$m2$=޵^p\9P8ß} ;n/ O^Zxz֣W>^8L VReK7{S*>GZ >|Ӵ!}K`( _)pdJ)"αVgZ9"G iTVX "=)E4rc,SȉH3Vcr`h`gd.ߦZ{>7Fʒ,qj7_;8qQ >T<$.a@+or(@Xz{.ݫŸ TwDb?7.Ssz^$0cdDXP}pk /FOWī[5ʠŵx=xu"|y[گ??$oA%Jel2J)!JVYhx\D߷':-W ¨lEJQ$86YOaa!"2εlKy&dM3:{yp~hD1>B:MIk`Th/w 0I.';4Tv KW␠ ³C|B4Ji#Sݟu׿}{op@ׇpCfϮ/?x>X̉Maݺ M+_aIu Rr )F_fbZgߵ3NGW,ILN"V\|:Wkzܖ x}} {=A_FmU׾WIW)[mio_6?as @Dc\KLwA߇w';:@J!nx18aqNU?}ťwJpΉRZ;]v,Zx6+r-֖"VáHal`\Lk1Vžs`qBpe Jt|hz zQZ=Z4Ua]bE^((={([jA]IS;Vre)V~ <' O; ݷhv 7VԊ]jeN؄+k'$TA:6t d@?fRTaQZ㭈`ʼnrwlc)Nϊ[{x*g[ :E{I9fAe֋#2[kai'wO R2&+qB~7hJ`8\IIuŰ^,7_R` TA!=@7mε=yCmrj5~}&;mCPXAH!οD]W y0q~:fN6 OTYnim,۠a] .E9+V9#N ")H{H wD9w5B?^>y6<9[.;{'̩讕)gMV@uh2Xi{*Ao$  j:*nֶL!? SJAG,Cv4Y}G`nߨjl"DcH'aQ&j>}[M|(! x&RILf:_GK0`m%9gQkß-!bl)Ji8qؙ 86~?{0[_\O!ǕhgRFtp6>k< qTܕH'SN*FejCvDUWBI*orv`}E q,JlӞ7>z׿UW>;ڄEti2,vݫO~ a9)F reN$r' uѸ@g'x%isхDLD>`reĶHV2ȊC)R -(J)Eᢰb0Rk1"`n_3"Ea=H^rtׅ3)ƕ>~Ef7a=qg u"N24Mvub>ݜh_$pzfDjWkQ]'"h/q" /{K_%_R7 =ﳃg]y2)sWv2Tc59U cX˳CVDx_㣃mXg$Fc[ \ËڳHxy8v !#)~t׌9//x7JxN :+Rj "5NE-,:Q||9Pӳc)pKYh.llSg4{I'>=@/H_tb7^4MG`K`'clSQ]ZZ_U4{<>(.Ȗ~H&JNwxy [Ǭȟ(Y ߓk_{?\vfXɳչ_{==PN\Fz-֭UγtNOuSx&g@m<[gk\ݐ@QZ>z AZ 0|INٍy_:sEVDv0?XeJ)8)+kyh9g;c^"bmbg68(E`p8O4qI0!s"/3c?Lx.М'\{;5Ur4\m>.:)*SeVu>.ԡ޽_;/߹0WMh`W`M|M7m9u>;|om<@2h:|aN>^l[igJA-H_ŻY~?%OG],"1DZ)9@ LffœN]h@ԉxQ^{K8} l| !֗bu*8|Okh+I&e*p~3?@õSO@zU'iڄk]]@p/nU)ʼnV=R $}GU|6Htާ>ѣ_?up5GXRRp%CvgU#(*'| v~H))\- b3 vBaYa'"%*xDѷsm.7RM{b[CJ싳7hRk˨vݰhC,)n2q3=-#uEYO!6n>RٺD"c 0{mV}`JhPl@JMF:{ͻŴVSVn}X Lh&KjA7{_yj mVB9#ŒLV n$^:PVQXVf5݊RJ+!\Zr/rYﴰ s {q.Z6p5C "دKOg=^, Px m`$JJ'rkS5 IDAT>w^?M}g'B(2H6>PԦƇ{8kŕZ95/ˎ]u ϝ9AY=ggݢNǶDS+zpm*I} mֽ# OUۤ\K1O=pnTJ,OS$FywAx>&P{y p=g[ȶeU\oͩiU<G+m|ML,D"z%$(CP䯁/JD~ytSCdUZ@Jb6dguug?~x\/[/N-+E0Ҋ*"ztgFYm X :#Jibq߅!T[ Ar3h|Ot$ MC\;:H:TӺR-=:\~l=l:[ײsʵ4o@޻y(:iw;O2:wMЕVq? &"х-GU?<.QUwٺHve8M5{w|p ' [:t6מxӖV$+=C[{k.NՁwÜZ]ݹd|pK`q =ĉEN1>N|@t3Q*^?ggIsp&0MA; KfIa(n4_!nEatSgsޯn5a2D>5:`)ɋ=)mXlEy'7_%;+G=]iKJE8WVJTٝׄkŀ͏.st pMT/b\#i\2,:|[k.:tWظR{Fe'B>Hnc]}Us/TB_O5 xz `GIʣW8'>5m׿?_?;<>+ HS%yZVWd\/H$KŸ.O|wLb\zO0Ǝ'u= IKp ?5ܔ) CAQp?K#C0jFFv[ h<`P*LE\+}FY}ھ%Dƻnߣ++{>hBvm&teWj CC=j:,eL 8ݚ7g$z&/j\niLXv0 gg+pD+!/hkV{w[Ji"L)E>&cJ}Z-GNVMݮR^C4Ğ\5)ib;=t2yN)B/&V5kwtYͼ1:]>w9޼GɲWvvZT* c?U6FK4&xZhZ\)pmoHhz?$`H!@;e $B]К M|zB3C*3ՅZ\@Wס)u]rI/1#bQ./i:JOV#$)a'_z4ꮦPLED0>9.H뾩n7d\@X<&kcQ';ۋrU ˭F7aK{O[8qLb\?czGj>nq][R+uqNO~7XpB s>iVx#5 <fUԝ]>S9Rcoyo>caIs0zSJ VFJ; /2U9%^kӸF[v): o 6`%LVH0m|(5|h7yמ31zumgm_cد kMHmO׵-sg/_~D%ȻGT=J_TOErLٸy2k`"S:W]+=6; B@e:f5;vw_zgZCX{rFa2 qH`Kt0IBWP?tuEw_~8'R^&M݉"?9KbL):#sp ^}yMB3 h!v<)l6݊lihRܓ2ҸiZ!&=׍e5|k gbew WmFi$p -E+d̢ ɒ!'r#'\(ژD<klqt0]ӵ%)ˋk:H$u1pV^xgXh&YWli ޙ޻l6 YrUgBWmSseBrLx;AJȘ"W;x3h\`fђ隰ЋوŚx9l#tiãuFsy_\}tm9k ᳕& XvJ-&\AyP{,P̈H~WΕ6'!qlD:FP$1aDF O.2oM~}Sv ;PgNS'r;oG_8;`ZeZZk,:1ϚB]oW]fq_!`Xr˧i1NY$K sw 5{]*$;*Wmaid!jɯe)+Z$֩CnڡoyI'#uېQ|Q+cg^ Vځ]/kߜ󞼻ڿ;D2B +aא6SyzO3W!;J:uI>޿ԑ ּks,|7*Bv<ڜVv i.B9l'L0+^Y#0t$Q;f.$䅥dEDG0:~76@Ag6FcwѮ Yph24:ЙPͻv@3g6>t2[yeNڇmK5i5H7~/\Nv4u(\|D̂{ghh|64WPO ij,$g,#M:ENNni7ikS'qA&\/^-:uY]Vn;P}7D[w:+s(nW)ʾE099.4}Ѿq^ToVMGR3,WQ?n0µ'Ł=ɵ.}u q^OW P@ƒ]_Ԥ,aǫ}^{txL5 ÿ{eN׽L>;RlvJ)U=J< c6O$?O-[i3sVg @jS$# :c4q12Z'0u`'2ڳ^6}c4ZetCݺ،o~՟{z^s)1=$g+Amn}7ǛSZ{\%,%I|M<k#ߚL*tn1npK:K,\ oȴ]XBşV+!7ZU1g5Zr0>T^H괥@|;!@ھc㢾ד>.*)A^E=Ϋ-FRtG؎Xi!Ɂ@vzn0E ݯ4Ȣ@pq6KB|,kV-L D)C-y`?j?OÚ~Ƌ?W["†ir:q+NuXδ(*2q)n|$P%a#I3v;5ZmG;Na:cB/'|W?Ϋ/׾-EhE{_ey[ lnOWwx"^]v|՛֭"9#GG<rfjkZ=^RsDx}wNLOsGH(f =>6Rv<>g4O#G* E )GYԃ*eJ6+R|H)i'|ٝ;j"hs)u tr4嘹)ۡfk|,'{ I3ġ~I?m[wNG:J]>>Ke )p*WM\J+B8}ugGӶ}utQ~W}5 冺/e!oV6%o BJ("}4^߳g<*QD篿LVP:9url5 >ǿ CihKdS%onݷy\/OsK58[zU?*c]"nTQcƝ5("qFD߂'R֒_M$k~̍qWVrTRQ/SI|o5M; "YO/tw'@#0 z$nᒚK}\Cu7CqMCnݹk٠tYTʈhLʆ6zD^ݍ[O7կ4u'PQuyFM6 G7KMvэg\"62uТ@/d+n@MOD]l`/6F}0.Iu(DsOPBܹ"8';K@M(@RW}4(,!<)؄Mvћ{no?ݗNFcª<[0i9}rX>~p{n0l7Iǡg5^/?y{?yt|9iجآz7F}Z:ڸGoύaQzkoK b6H/ɷLn4\;aGEp$Gʴ9K_{t8ps:@E/<-ݼ=##==5c)=ɡ] ;āJ(դ% .pIL |uo'wPh&` of)*9'PZRN딡i6[R2ꐖAyVC> 92!ܲ={*W],> =TFThrf+X&VC׷,d߽Nԍ^ؾ65GblT93GENc"py1W^dwB`5"`Znik!-MRN#,b5ie`kov{EG:3[唹# u'>byo|Ӡ~/n֩n-;zǎPcR;6K@gxR.f{oSjzbIݡ2R ?S>)S' $Iש#u%ID_|4?A-; yAa[u/gLZGҞF~wvSaUQ"G4kyUY4S"\>rT萯E/&CI">HԨ5EI-Е9qEEH$ E_dG4m BrpɐSj{gQ@R6W0<$z{퟼GmL:Z0PJizᨤiaK;მ5am9U~9t'^L[Bko4::@su{olq ~g}A` HZ-SЏilF=q^$n?hiXIBUo5!6sϭ*BudFxxlyxxD e@ARfzQ4Ow ְg>z|BΖM;23lrH[j'Z|z&?5:j W!&cKπa2XpkW-a՟&)t):GsACC؁$ƫIXrׯQtrE #( Ẫ;و'ܠ"G̲og?"jXzf5e~P'0>0C-^>fGo?+Sp\? k4&44kAv04@ʽ'*U/Ǯ>sM{?''!]Mabe~;x'h@O~Ӱ=IO&rag.5{^u 88,ǪŌ~ok!Oгny̋I}\uodw$ءº@< ޡQJk%S\0cRM!d T:6N9*jLO/:~S J!gKv +6-U09,TQکd+W-/% )C+ utuXt98qК/|g6͓Xo_E,ƉeŹ4l\EyԓGto=(gSke g]B G9|]u櫽"֫P5^gVLw:[rlЭdC[g; i; i3qɛmyѸlv8$E)TDQ9& ˫Ew_X?$;aXh^?[+sJ jB|4ڤ`fϷK(I+`s@'?Q=fiF*?+O["?..vj0(0G~ 1ۛgQ;(-k#l{{i?>vj'2 ˺tVSN7=Ja?s-< hu+%&>p^2o/ý֟͟?+ A8SmNɫ3e~vLyI7agskQk|b,OޜjDȋP:4 f x }]<*w /f=qu4%_t; H8$M9w.\cnX8-NW>ꍆv/_T-Cp6l~Nq3\JGͻz|8!y#ǻQ*ʅxz|F,*ZvIƂNCE aFUk2B̪lX*<J\,v2`kc~F'NE ǡ1K!1YV^LN^X8̽.+f潧] z`ũ1e!? . p|kkpkkWJ~},owǧPJ?PYACi"x`42]Nܚ煘0lL9lf,3CT,t ^p,\m7ڰ׾E3ʍϟ|$]{W* &Ť5.ƤO:5B_$(+)4P6#~xBA{dg0 6 )* Wvujq?޹k?y7, {bsaނ.wig[u o#ypfDz H/0寚~pA!.e88S bu^!˰-߻raQ1R#( u$YoiL)%m- ]zfR}MD+F+>imvQw3iTvR2;eTfQifAX $\'7%Pg\^D P<~cRL*2{`,dP:$>F0'Šg4#hWM!vfg͕]#z4<s#XT=sVvo/MF<+npZ`%xQ@P`F X@TjqQC#TRZpu_5Gz(&>@B%UR܏8Ђd*lҨ!HP2TI?"5 Z PpfU@UeFvH2h=E9f&.P VXqgFI|a;3|hL{uPR7?hRX>٧% 2gQSح*Z>F?7r${ȃYb2hGԴ(rviA{B;sٗX7'% ; xU}M;)l=`n<,˱Z₫y@HSOR3 ƒKks\5l\$h/3fʁ,ĚNDs d Sy~u[u[UW 7 m508K%>*´pv>wG_saľs(h=OHf|q4* 43B`f~:`~O`W`HQAe+ ..+ΕBf ;8h4rQi@_zHvP^Bs&T4̓1 苸dOhGxZ㢣Afܰszgޥ۰h˙CE}dkP/ryj58/H9cnO2*bp|%>]m-<}]#>>bp|o6&;hW3*=",GYBHg wæzחG*vwA8%o/s Έ^a&N/L+} Fcġl]ُ)\@VN8w 4ұ wƆ#mPsN lL\ٕHg/MXuvt=; B#&YҧO:7@xX@3@tkR-7/\<쌥ӣLl< iuL:'6Y"Y$@ ZCK51dV1<_kC^*N+zK8.'FKk,c/"&lH&xixqW,g )DH~$W3W^u,@d9fE9QZiwF~ 鿾Ng4˱Aϯዃ; +זO# 9#2B$%h8A5ZŶ|&,S%r=`19; @9p"UK11is8v+T{B/+xN >w+WwϙXrԜ-b֨8Xu/nn-:c h$2 Q>ί^@jI#k~aG n{{) W٢F@EY°+!]7j6x1rҴ6R 3RuU{J-qkRWyR3pϡ/Z Yzxۋ,2s3A1)'`iX_[m+waNHW:ߢPN/ĆOC6ט/_@!P gТnIoj֥[P9"?ꛎ1G2/_m8 a3m^Xm]\_ݝ&Nnq3PPz  x@չ[/Y_h;Û.:V9wUf9b] je$Eĉ;*UrԭxS&GJ&^ =f?6/I|"n͗IASj$QS @Z-5=ݝn~~RGĂ4r}l @-p%Tz6;vhykm]vtzYo><J("n*a$xWG?ns SEC;q/ѦYd ;vr.V \?wk~Tָޙ;Zó8 j?dBj;׼hn6_4Om Tq0aԙss %pq۸BNB:$~N ~SD%}5hOohpP@~ST)75ٓeJZ U{K~޿.V WbT3IAohK'= ؽbvvQIvΈw'|z|:V8ZɁ-Fx&j|_'Ľ8Wכy xXd ZuNtU@ߗO'_SWQ1M600T'=2'ysSt{] Q.}UF0,YgmgL ᢷ !Se}"b3^ݞX{SZ;э{ âLF;!쥹Ҏ "DFO$!]Tkcx .-o\;5e $? Q/fHk *H 1]TVӁ{`Lщ>_-'3MٕWd;yzI%Ǟ/~? PHq#`7} :_,)[Dyo0(heM[;'vΊtf4LJi<:Y?xx'f>%@#fZ4#%l[T\G(2څ(R- C (0eƲ5<`gכhH0,)pok +q3ueQF91Cd|`QevA<7l+EU$Iǔ #/4,D('g{gA3.H5k/PD$\ԺJ87ZJi*p>[ YN2^2ts159, `}3+}UѠ-qt\@g݀px,W&Rף{&vlTvN|N,%}u/xp-wNt; B:8\fNxxqr1];vfmiIVv"/;d$ ĹT& e*kd{H1;.0^G՞:n4GvGUAE_Ils.P: Or̩z` 0\y^S>[ey%;pA<>tV:™'\}]ʲ5!׽] 8=Ne:`MB0m,@D|^$SmJ:|;Ȯ3æh Eس|EʾUL+EZ >}`~\t/l䥉] R(8]/Pe- bvFZj`tg{~r^Xٲ/aG(˜7-c_z)9 *XAU=l2S]>qrUfSFr{2.k\1lK`u1Bi9C-NpF9q]`i2زI: N-/e>OW xpׁDIQ#{Wv'7W~կ~trqQvU#/:3[&^&Ycb^&3rRFcTWpR@Lx_7XS c H.`0IND:w _CQr%=_~@|b $gCu:5._2:fFU[@|d:B[*_v+f\75p2q5*T{ I|liuzU"2o?APU*mpJ|gǍ@>k9c;GtYƓx @>c p'^V(8mnZ_\OccxfV48#%(vqފbV]"kưZa9#~C9x j3%P'0$g技f;;e-&t䰜R%Vux״"} D{k_<~ŭ";W?W&mʚ4Hy#@@~Nt{^Agp4*W:MYaGM q5>lb"ާ|UssGL^ Q[H[b`H* S+/]/UX1>| IDAT]39WX ]a( tc?\%U@xggn^g4Jo}t+ka v*cB3-T5rUe993c7SA^sal8qY9lge9=Ƥ;>mwrv~淦ʌG-<27W^`0AsxU+j-i{ص'o86lQ o2 4O9 kBIvr%PiC X1>`mp5%mO &2˩AJN-dݱL#r"A>c!dmGׇNow3j),W_A ?U~ί~v7k4l7nG_Z{ Pč5_:c' OUOO{x1 <1+.qى"g2!q{`r,h+[oHVq Fn9X|Br'ۂo~c}>͕O ?a`tBZ|6's3?ma"2^kk:P pI}oCKGΈFvZ&҃CYi̋ 6!7zXj׋Ho^󴺺2 cHqZx+4ILr5Pk.c| D@u_yf<*TIpUf‹:$ Wl'QcfTؒ>jXf躓_3;Ճƽ{˃QpR`iK3k L=; yZY&rhX2G/:`2YP8Q[~Cd򠰮>:s[of*h~9޵n7z#I_f9Ġt %n3FwPIk=m`Rgq:ZfT<`I3Hi7W}:3ckR :E3 wo?qryfHSq89ׯz#YsQ /b|Pp&Q0c̥woy#8^)`96D n&yR>D$ YroM`ɤ9ԧh><Ҩh-N^NO|X8;+ .YeI#Ƃ xD捬W4og9 S[#@ E1ɓx`>@ns9a^)Cce=1b_%,֋7~~,Cl8/EXH&\C>3WJ+URA)޷%y@ߌw|p& z>[5Koz I`}ȄL;1+j?+-Y%+/lk/<4Vgɫ_kFvT)%4ݑrdC52Ixe+_g^:ILu+70B2nrZ Dǘ{|ͮ͟'|~} nMvK#v l`F]lͬghr=LV&rA._2(g['|4hɿT7 BQX 2'#*q>IBX_}hBHkQuYiɓm*l:vTkT)M7Kwgf!{b@O A>HpMcvS03Ete7d~ςpA4;O%FRzqH cH#lLǨ\#?X# ,+מ_υAbA^bZY6 $:ܑy [пo M;* 1'y#;,jN@k;|w *.h`dajNS߲33qeY'P@3cRp0%DHG~uYZQW|n)Ui”Ɍk՗RޗBHx\>"SGS7K"p˦+FS晞=h0K{d6`v; }_vWgVص\#'>{-eŲ2.iafs|lwy}1.#i oo2Aok0`l)!M7ϳX!´ّRFEη\:;o{9&/hҜkjtmda7&_g]l`` <"h+-W@#v3qA^"*v-(weEɹWmJ}Hʉ\ O Wvؙ2* \(ְk)t)'VZAtIP`xo;EV!0z-`vO W. 01i `o;Jauq+"W궭<ߋۦ<Ӫ'! vGd&y/ZNV A+ R_{ ֶ &n)Fvy'ҡ'T]c'YM >;9.&$\G\ 61[c<7a^;/' Z1o"kn҆Ad1߃սy^ܮKkv kuSo|K3 '`tdFF^=f")u}ZașM-3ܴ`>n8;1(K%8xBSܺS>Km\ˉ*yQi WwgB8#h:,rƆ󷻝(Bࡅ#;)^W*):fSP9O"E\;P0(hO"1@h;V aa x09PUotupn+"P2и(~QFY*g8Wg*Sy$g)`6=dE{= XtpLa6 +uߓD} osh nNv۷&~hqKa5߸@V''Qy3w+ s ۧ }l2k0hka1QkؘFŤ5.8W2WILkw&㈗1I>v.k]gϥ9^<|ڵ.<{;H[!izmڷNlϕZfmMcЀ0xb [yH-iEb9vpkO.Sɛς3'v.3 Nx>:Ӏ\ "RׂO#hpl!fFv[>o]n?yBefF`֤&1b%Uv5FEYLVR~79v`Ώ^ۙio}} 5'0SgE?+fX=tx9< e΀#l읈ݹiz _)&d8+jѤ3xLv[}A/P(r:Jq"e pĵ∙˥H0jKG^rp_ īty{2)o44T/J^o|9>*O XPB:SyF$.4!hS7hdO| Nv F@sNF!B9K2d4nuͼtI`'|։,ny"QC1'N)"2Nx-gG6E~ 5tC: ߼qwPNrG(Fm+#ѵNH@5[uuWٵt1[׾#ã] f6]%{ /J5F;epNyTvZ7˭;͛vɳjMmzNg=):bD.V:I`h~v{[ħ)|fuo~[oo*@,_ӂіU )4Ӹ)v=g`|7cb%\fas"/SݠŖ7}X@WS])53I~<ֳi}9.jGўg!qy;e3˄aa0hN&H _rl,+y%Ӈ#9R?O ._zmH:=@8b ,Cmt،3ur/qL߶}w,ֻZ5 vi< ~)`W>^rA"`s'--K&Ϋ.dOҁ.hsI:;>O>}S9{G"Xscʃ>c|K/Ǫ3Esyބ# C߼yjnlm/ 2я|kss 3~ϭIiP?. Wq%̏Gzבf |p{@]Ou߇)TFP}!<{E=ɁȺ@^,hKs|&J& r@Zv-'zy^sHT?p$!o{jp bEI^~'פK1'+\Tw|89 B~M.EW1 IDATBBߺ}rЌ0Cmt|O(EzO&Bp~2Ol *38 2xMM-1<Чax.Nϕؙ3$ؤ<{mn"RQdRNY e͵p;b`I&*G`nC8mS&o#&l.6!u"=f@Dƃ)R>o5Fh7n E7_$r0Q76pQ ammG?_rΝ^.P/_:lϯzyw.ər%Q01Q7| >tA)^=NgM]v4ΐ`.GnCԑ!eGD- d'PZ`U} -n,\2}ryu>}Ņm/t;{O3lwv{ Lư1Mf?3'a0+KeOskxPlؼyɊO)hhͼ W]X *سx;'響zG?򍵵>0.`Iڳ_yxWN_ {!sL'G4`r4f_>T,׃?' u˿.Q#`_0tH)mYfDr܍ox,hST<>0;2XŖcv5.'u'zቷ5%~՜]xгÐ^ԡ7Z? 0X1F3,=;2˨<&lw4Lz35'y9QsoqyŅ^.|Fv;h~gһz~#< B֖S.-(6TݝKCC{r/VWH$Bի{C4l*[-R!EIe~f8t8? tL$C5BIq(NSc'{ n9Y`7e`Sy2&P n/}oYo`'Qbo =vcY})< |vA*qQ/{ͧ}ӗ[O# Eyʥw߽gO Z!. ^GI0)gRʉc ^{fa87THSuF#=kA݂ߐHBar/0l3agG@r)@L\`]F@H`̬߳o;22H퍟b7S@;K5^o '\Ze!-E2QrΜޞ?yrgna5<`МܽٙՁe8 f9.ZqNĴؿ{we0I]ul:fN.LUKK?z )@?^~{W]vCd:'-Zq;;͂OV+%$Ey"78g4i-6o6 حSyK7.l3RvQO OfxF`1حyP|83x-G*fP:F<70nIeS[!DړMrc!˚! c&{osPST 7%s2>cC Wƍ7nRvEis]̇8%^_+1G`wwnl~L?mm- Ξc)Kj5ذ[tKv&Xl.]u l"~jn[`֍c_ؙYN)0Kg$c`7+,PLoS3<坓綗)vF73z"< 50.o=ƉX\8Zx` 볲 _5'Y #+^oPfq;&0葧yEĜP8&/r4-k*YZyG 12{9@D*=SJS%HzQzs;s6~𳟽xoggn犞}>r3*SY^ vHL=V{ s!0Kd˴1<ںh| r L2ΦnmO^YW +]]BsCs=_ x?`wvAkuc^kZvSH'Nv@zY&GVH*38 hk͹鳬|)*ChSf>r kGھj{k/uP|Dl yifUv6 i寿TpkB]^9Ga:o~s37w.7^yV q^tǦ$xa_ys]㆏#!@BO(!=Ҝ.)N`{%`F`G԰1^eր =G?v BɓNԅ'3G݀3\833_suׯ3xFQ{ A7>{ƝR0G3xE')yq&ZM H8@#?Œ{kG^ r3LeP:g6N#v@Ƀ=s癭sɠ\('j g9)y#;5^zxh3=>,`32:6z_]lpOUF:Q+չB^ y?ʃ$TK8Xy؍`A5 4@pPol܃9x^Wc`B(;Zd9_m s N"ac =^_Dt}Lxbab`Npn'@-׿3?;˭4b]댛kTZsB^)fvg; )f3I`ݙ2!@>mr'g\ox1(7#:$?}fu׿~fkkk~FaMA;pYfl?#,2 h`z8ϺV0&xAKEŧm~Зe!y.'Eq R6Dc`7%ΪG \eo;V=Okx+5|Q22;g 8?xQ0aFgCKWkGc_әh(fo+(urV(L!^8 i0h5{$ze-0~N&g$P\kЕhߐ/ԭ9ۂzWlԬGΆ8_ 3(*~r=`H9 /I9GixVy7ObH ݷQ|pHa42or$wƍ/|Vs  p vOߛFIY @ )xVr(/y /dA yI%w@? Ӫ)4uw&r9<*8O.Y.ڌQ,_ 8VI%"Bᬟ&.e`ɿGgZ#dA frհϓKb~<~ճ;oe~(nj•c98Y<`|#d}Jx0 0=mԯ9"CngLԉ?MC%6i[(H.lV9+˻2s|qbq`|F' 0 x_-d`0oҧC>t&C+D}q)H\F*+`L#  _[I*7ps.n]89?3(GlRPMQ DJAQS'7#{;X iH>԰! ދ.yK6MC!8Fq|֜w/n\fɅxs;Kk~s"_o[:xer|Nlo|j>Kx2sa*4r'3\= .ܰ#d| F?@#"3C|tƚC^g`W&|!Z'f^j^k/6[6w``+8ʴ.  ^)ԧ/H,{#рn 8 AZIuQSՅ_E@'R35k3{s;t=`r9;akԼ~rExmi6` zn1Ӄ UI'0PΔbۏj`8 L D%on"79dV 4 Prd6.S+4ĴURL/[w~{ ㄚf kg.=>Vy> S)AD3BQ" q潸7]=ܸg$hH3ĉu|&x u<;>cPc, x jyS7M lݦֽ\eŏޣے:vd1E+ $׽@ǽzk'$D@azYOZR뉢AY@ys?6OA< *T~d cǎ]96>@q=W&:9qrmﯜؽd|siX=}2!\[hxLWOLupmmh0p.-=W4nI;zB^+̻t lӚ]waTIa~ BBV2, $$H*g x\uI@O!m zS;p*#34 5}"2W{ެ%hW [RyJ\! Zx*$NoCo[ɆTV\:uɶރ禀LTOQ{ty3˛T 'ޢ|{Ɛ'fx9Xb ;BEwfƻ[V>h T?LfGgIs5c-$s6ZCBcɼ\CID/ʹi>!M_5L=}>7 %! !<\"Gat'>;Mrp=pSI D/"գMS2 rj˦ޟ#u=qKm%e!W]ت&PpI:yBeč!Ğd(Z:}ۨ3T~YV6:cskjT<5R|'{I: !V=jv2R%yO 9-^CX]Ml'B2C6 d"&"ы u؎6G^ ~bϥhs{>?>O\Wõdbqo륿]7:d>JD$ ףA7iUh^*kVV%['I;F. kHSlf˽y%ںG^>ےh^G9;!v]([bOZ<}۟'>}~xHh'Ωٜ4uj5,tkirIWdVQ>~['=^ .̄R7&Di3ի{Ѳv F!_J%y%TyӇևz=DᲗHC IñK/"߼ony8e=P.ؽ5Pf+Q/ֵ\ˏAʴy|K$iv/y)KE1uӘwi¶ ڊ3Gя"~ ľ zEGEwDj03ۻ xh;L`^k7/  VMgP6Җ1Zf|X-0EZ(v\ֽv>rcZnբͬMA H?FL0k)%{@%ɥ?v =kMa9NJsD~qH<Q"Z6Ka8/Gɴ;_Ҍ uc?L|9h7Va4_{̽ܒq=7O6U{z9'^sOܶĞ)Z"A/R0/<fXa@ZBӾÌsC^FO=-f!S"Yz^)5ok%7=MPx"dI3^`sHpfnGEq=Zd ďtq:2?: 9v>H_Pf[ eݔq*,(!Fؐx^CzZԈ֝aL\hR,L?E,Zq-jARِ7G6csh\u 1ClXS}ST~G'P#55qsW3]94{})&(m\͂md*r1FQ"i8J8õx$k )j 㓱x"xlG.(Ee\Yo֪0{" a<.OX <n449ToBlLmQ:kmIb᤮onM;Dz!&"N"Gig0@n[ßwm3ߊ$ANXGF0 6Cub\fx`,FN `sϣEEw 8Q=<`5ۇ5kRvZ<緍U|@hgk'Z</ц y0GI%Q[hm IDAT "@X$+_uY>{OW=b[dy׷堑?w7l_Xώ;iI_KNgPlIZzw-/ER.1ћkd KoCouۻDd,^ѵzsҌW̪&{XoN[7ׄȾVc )TpusH[ <.( z]Rqs߉ w!fи!UuCZKS17Xo0mMX }UYfz_+VB-P-^_#bٴG[s,bMT9br$kBi'y7{ E^6u.ٱf;W#O@v}M/h~l8g'ŷH `l(Ӯ~4 H]zO1Z*e>/6̈ou;U=  JU\2z.1۪A6?H2]=6d$KTZ{D+c uLo0/U;mIhc GW'ZǕ<@^&X<m:lmظݾkcKcic/|H"޼̆1ukɜ. f~k')TM\x[/$? @9ʧ簓vi]5 nt[=өav xkɖ!"s6Gu& HfݿϤҁzؖe%I-{TPu&qE uyȵw_Ҟ\mrZBbs0` 6A7+bQiw4#7+_~U }Z jsz0#)AHvW׺zolљެw2qiD;mLo&~+eٗV;jɲqT20qweb&h<Ėʺw\r}ekF͙]/` º.c>"tN^ZY| gqX^i߼p>(3 M8l,#)z&>p`. a|ֻOdmY[0Ɠ&^6iգ(gVky zF QMl]C:PND9?z?OWҦnIE iF: ˯KJt*!th"dDw} EqҌWe5qӜӞ~"0ԏWL=\kBo+=dCO[HDD]!@7>b˵u':UA 0Fu ιWPϦ/p jz =xX0 fadYF/&x%(}`}ڃ{n78Wqa,@E6M7ψAG[{V>mۇ/s3@sNa=ܿh ~)3#+St7E`bjVǛmx摫QJ[%d pqGǯ>Ij*.cM7׿&vTېZ}B܌8kNNu[9gĂbd;:*sK'?l+ MR޵P0A0]h׵pX^:zACs>ٿ1Ղm|,}'V(ehɛ7.=g=Xd:ΐ< c듼-#m٫a2=hd<(M|jۛc/ZˤǮJ2"Ԇs̙䉋*v g#tzzNx)vi&E'| A3 Iס-"'90Hd"w][n39c~'!P!TG/?zmDWݺi5l^حßN[jx81{`ir8i|G> sOG6=Hk6&&y2C6q`bPH#AyH8UFC}e|ϗ?OK6LJ{(!g%qǎ-N:'{0/d=Դ]KNGgQ Xu:с{pϣܿjֶݭA Zi>x'L#}p|n1 KV'Sµwkbyqj2[`wwF^j50;bdk~ey ZoGӭKL2ɢ͉n^RED6[tf/(-^H2T`4h3'yO:0$3zr<;7YmE2L-Sވ=٫""BdAF"hMDԧR@6ls qfAjif -3O H97 F*t/u"sKz ¾; u{oN'x~eh'uҪGa%ںMBmEξ8l=~ef_~ѽӳξɮv|1\s9rxT"S9YN@l.IE6m`y})R9"4-BA-𮗑~0N6kI` ף;ضvHtf,"J}lJr%vŎ.61emvӞD ζh®RRg_/'u!mSKH}SXJ6ycb,egnI|mO{g^D5*r˰z'?] M~XM&KLox{A\ RK\Xg7cO򦰲-Zu/jO",]2ڶnSa(a "QK84#?q-rHDFDY)/{#{'@@Dԫŋ,ye˦}9A<;xddAZXeHK2<+BpmE!Mڷ@XCI]b{XRZW' %:{w[gZͫ.1h_pQ9fP:},t6X1Rzu+hzme4 rj%~m7$9K (C>N a ݣls/xn&[%z eBY*Y_NȁŚ]+JUtab z4q#Z!K;ޜ0Z3JĢi"ԚRvsJ2 eC>Ȍ!t!`@g{L]1s":m>LH=\i[ܲ=X yT;Ήg#U;ba>BEw)N{ )/e (wwkiÍZ}W쮃>;ecc>$c|:'QOo!ڋDON8>5(-jբVT-qeƣc;;DL}"G8fW◷gr%Q[LVzg]Tz-h._6pݻKf.mCaƓCRg)wݫ+3yI]; uU{Wz6muWQCEw366ف~Z|/I !od N@(Ӡe^Ʉ.=x' Vf D{ iˍ;ݙ6uX^r-D53 W*1'df31ρd "J6D\eu=-QQUD`Se7>QkȁƼ>M^"I$ >(i]41NR-oLnYOWMںt~)|"@ *!xEKp.4%Ű9B>䁈ws7ERHtb.[84$Nb)Cm>O&:H5[Aq,W ŢբVkc,c6SZNP+jr)LfZg}A2 Ac> p}2F(f '0B 㜗4&H9%%jS Av=k3eEZInR!s-"?᪹\dH{L+*|8}Y Ǝ(nW< 74<<ͦ$]i3w^{eC^P06oᅃ7#)eX07OSn-8+|g36 ='{^vMJ=׍vmHPsÛNH궝ޓtZ m!_>Ee# (b.d΂l,=ҩ_h7`lvF^?t$$&QKfEcn[_x&j g%^{by']3M(El4[G@.oAmU$agkMhLqFͭ'|BV)I5"xHj1X +3n2CAE9sy8~hv{ K_SQ eGU&@0Z LȺ$ݦ';DžbתZ'%G>ч86uIEX#q%DoJ6 u3;0j@Z$LbKr2ǦBZC~$ !'3Tz>nu)E够h$0dщrbdB~67us[բEYrb$Ҝ&@Q<+IHn PvtuكKALdǗ:홵}s0q ;tP`ɝoM[ b?-AC&.((Ma7 -9{o^ Lkzlm"ty󒞏TA$XgMIg^aM_B|u:r/i)Tٙi]SڪsVV{\ dRww3oOI9zi(gD$s"D5Ades@drX,bɥz|_cÜ2&x:%G^6VR \=zD>Yբu},b"jɼt2͞;1Da;B]ݳͅde2oioϦ~м?)Km7B^1`򞅖sN`!YjɟkǺpF;۹+8X@ɭ4s#'+o}oL O ]X&-R$H5{u7F5Ѣ.'{wZhLD@E 0$~udxND)H &A[8g +'h?Adm<^nL׮Ǎx"J}&MA-6,jNMǶ?8mN w>Dݗ&uΑ}pu^ɜ`ļrۼX%sнxOkutuB8Aԭ0߫G p-7qwǖ.ߣ?f?{XGH|kZ%vnѾ-%H=jqTP=7.~k]ԧ 8RWsxso{~[.cPnP_4oMAϴosi[R"H򃮘D(xBE;!zPHfsFX=o]6?ryQEe4-xD撽@)5/IC.s;c IXi띐.Z"#{7'.4 migF;n|4v9rE~HuKcge+z{~̋pS5r#yo`4YV[7sTnqm>Yj&&"jצk$D-kr^rw+ _f"hxh<8<y"ՖGꀒ@u-DIbJ'nm\Sl'n6Zz^ť컡JdQw3Hݘp ~HR.v GxSL`N ~"{ _|6`R-Z5SKV:[+бW.Xp\}ݶ3/,"t ~5~[ p ݲdYUݮ7?N>~"dxXHr܈Vx6QTpGh􋇿~AŶ|֌DLml#,q,?@lSd<3M2NNw[GRd:t>b5Y.5SZyFW1<{]kV^W#uX3kÿԉ=T( Z^O84nF/#k95ʤlܝOx>] {绵4C'ǩ:~;Y ~iz[8Ho]rʗP{UZSihofk"A~%i=G@Փ;9. O׮SMOWe/hefZcu4s rکci{#&9ѩ/[ssvk{V PozXl?>>P ƌqugZͅM=(N{·P@E__6^[{o\V5nIGF+,MmN\8zϽ/Wi@!dOy 8%GM,*]@?;5%i{0$)EX[8xhi@. `Gn>읐y.'c01q{v?[tS_:dzwutKҫkd~ kSV:sr><^dϽq[ՠ" ~/ @5rd_|fM/A u =m_/H6p=_LI6_XDRUVV;/| eD,"iуFY2J/ 8>Ƀ< T_1t>r'{SaX8/Sevy⎛wd6=]~5<#sΙGr$.~dOg SKK-{)<)[ThGڇm(H7.=s*rpSG2r`q˧lb=ٴ9d/AT(BmҔM|~ єw&2}#$sv.޳GWfW2_A@@XL{%({_g_(iV9~/jqRa!H_|T^K>8`̌`x O3)6nL;!z?U@q-JJ/͏$jd n%YԩNenNQůWީPN+޿mއz5s6p}ohpj4(;P42ݐz~tPh @IݝYI3x3C}+ܵxC/G>;ի{@2́XsVLFv5_^9P?LoSԮQO|=Md )hޝi7z:hoΣ1L&@P9^Ln[Wҩi]8~J4!Dd,h*z k9{xmfkk?vαv@y̶Ư>v咲zvcΑL~q3qjJؿSWD˧)]n O|wW{8`%Չu~-sy7O"&z g6^L>BE tgډP4^5K_{6ݜ[ז'6g:`sYbr_|}|yB߉0$`g_ Gn?VM+'qSwZ{e? l_. s[|Pzk~5[wBk?OF-xH@ 9)CT^x~T0P[^iޡU7^%Y[nƾ+['8b׆b\0׼٫-ɕguaٮXI wkzOLƦi25v.@:vr$ruxz^ܣ$݃.r2EoP$[&t[90nEfKlMl||d07U׮:ه`Bc7MJ:{Cccx6ߊ\F'ǃv";>o}<<1B|tڣ[aG?Mt( L}zo_Pf ;i=}%-3Xz̋? ?xg]hιwW*|Ƚ=xj4')E8oKV'ݡSoD߯/l;(B:IaO| (}ʟ\*GÝ܎^rSj'$Z2yb)GVfsw|B'* "t@0[*R`B?ҥzƻz7``\鬓[,E[oAi;vLHblxcăFw# t&kZOiC*d8~ O2԰/Yik}zd =aMCWJh{;U` r4 Vz"72Z^ޜoMItk6&ݘ^D"o7D߳9>6Kg[ƭV;ӨCФgM@i:`JGIڍK 5o@7iocuʱ5gjʲvHs<99g+T(AEM|1O"r>^_٘j23j-3kM'`J ,h~fF!e#w~NE1iO_bo*BY{qjx{52c|>RPkm';ޟ3'/ FfH\$3I3 b)ds }7BW'z 4!d/ͱnkavc~c<޾kqIG-3\?+iS9w—coaߛ/;y\=MVre+%fweqjs!A19.EIkA1Gn=~i}gCX̓ ( /̱BWwA\bޖ~gqz7o}h͓IXv^~ +ܻg3w^dM-f!#yS<8MͱfIϑ^yRwۜh} 6!D1>~"g])7¾PCν=ٿy)y,f|#K/o9@18e.wν0t]P!M|ΫA N}pFC 3Ib쬶r3vXJ?궁Z^:%rVFAs&ۖaS1w=V^Bp3(S!_B 1}S>oB `;w^h7%6Gzn] tD7mt؍÷g3 ":T4 m AҔ gV}Jspcǯ=W@9dӯl/䢾1 )ʠm}ctg5O+-azeWޟ,}4rD_Qi|޵=_yW0^+Pq J+oOtԖ[ >+'&N$J.X~ ] NJɆQ{][I5 jHSsA}:_GۮM*Ƚ=7^IgO'55sN~hc(q~s_ޑu#wνrJڇ@y/s/*;hrƓ=h}ϢB{Y?~ pl *orO?|Qm$Uk|f_~-Qo2EWJ?'ᄫy;oHW6Ƚ?~mPksorO;?ߏ+mo{~Sf_~ E[T W|m?޽AA[%o OaBmǯS1k~2푋>W?㞲TU+T `S_8V|eqǯ[-C[ XS8r]gwٔ _T{ )oPÞӺv@!3B7TPa4 cJv(OjI*0 q_ŋ@o*|PyWP O|8svPU T*^B/jC-KcȿWkTs*ToʬsK̩3[220Z_E+TQ+Tp'i70_&U-Ps2WP΁lJ8lvcպ >/*ͽB w?~%/>SyW9Q{ 8~/4\.gwI*|Q+Te`ćA" pT*Tx3x3=G @Tf *|㿬Ciry3Wת ^=MF:IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/maison.png0000644000175000017500000000207512014170666023163 0ustar georgeskgeorgeskPNG  IHDR%%Ş sBIT|dtEXtSoftwarewww.inkscape.org<IDATX͘mlSUvսœEC/$|,('D2&!&1%'`0F4.B4N#A"qƐ0vS\mw=~++]۫9_yRr[}+f 4Z%>!'m~@Cp 5vym!<6r #gyk~ $tSS*CNR]]ƺuSX8&#IXׁF бc}͛PP 4 v!#[F)+{M08ʕˬU6EuC!MPU塦"cb\u%y2-&]|!mKapõ摑qNشimAEczv?'pa.-cÅYu/BO;C_0apu>XRޠ~ |4CI t‡ f]ط5P!v0Vt2 3+܀"Qx(E7vFC56 [guM},jƬ+CS>WQ~~~[wԕ 8 x`a Eb+8_ju=|jq>/[^2Y43{ =lЭ/u݆AIUaimThE4T}QGK-/$raEQ4Hw|iWp S\r4@1>7ЃOG\"x46=!?^g/uC- (1?%Kěf$SOIº^䉻/ZsRIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_4.png0000644000175000017500000000177312014170666022707 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH͋E FP\P쒃Ņ%({Qraqr /xt sXfL񳤙!=9N3efffk ypn@Ku ǣUGaԓ{q" ([>M @+I(ƒf ̚V@2#e&!FAI6^B9%gi+mj` N>z&9kYd:g<Ig+m_O`t|ۼR*JiO"۠Y^ kLHwD:EPW",-ur1>`$JM7T۾ oG TeLWz}vwO?!>%"^r`e'Tsvi'}r1&z4fl6/GD*58} yp[t)[f/0I,c=SIɫ*W?48Jm G]\ثeLb&gXO-Tzы(\ u^C&M` 27{񂪍[m޺ü< :*lON*a"L*Ok@I~z `kJ8?=3/^5 l<=GzTvubç5jWC`q&{텀Ejs Ԣ9-Pŕ:Am\ hA ΁1;9{qB5ȯOBg~xʾ75*EڵK+{7!%IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/image_.png0000644000175000017500000000271312014170666023115 0ustar georgeskgeorgeskPNG  IHDR sBIT|dtEXtSoftwarewww.inkscape.org<]IDATHm\g/3;;;dHM6KCiiĵDD H%eKiiK)IAYADł@AvJIZ#FVmw7mLm290q7F;<>wDU_5_' ֜B||e< <o[$; " 8$^'@/9ja3o_E %>](1㝒%Ay^x;8[oTpPcT-.I"d55]wUuFp Q=WI:ui,i,ePgY3 @_Dsj҂30B -^*xVlcQu^`| */ Z\/?4`0(?rpm/hvO=ǿwP,7Z>U.( Cb_ԁ,k%g@VU?wd y4'@> owj:V'7\"RΨ[ :>W(AAbmn`f2w}n@5h[JJ* w 9p*ݾ՚{vxzNȝ,"GX앍+SDZ~KUZsw)uUp-IF?(p ꫇2&ktav=v@GhqxrFx*@wʿk\әDĊmK?́<{;]/HK2T8V_ZώvC曹L+Fo$.9)i˦JL. +ȂuCWˀTS>S͋^Q$ĺF=:|xotE6w~#wQzglbK)#K\ewVh%⛙;u;K[p;ogK{3=čM); zs:8=&IPMS 7O^t6$;TޛVZ.uv'[{~M\i~GF՞FlGG!Fn/aՎ\z,@훋]V{Xf.pb%WoM]}#qlN9y#nܟo]MJƒIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/annuler_.png0000644000175000017500000000250712014170666023500 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IDATHMd3!; A W#KV9=,QO^=Oo+x@~'IUϞhIUU5=st%eeR5M*A4WM\iT,t֙0? &>rSՁHfUU2N%rzxG;;G!K2J"$+ԪӸQ18AD DQFI].kptEA P!Ri)A{y:,65, 0){rHT.al$R)>X+ >yXha@9{tDQ[q&Pa+VݘEWTk6sJ$\׏}A#>zx޴1GvpDQGk5f.-2}՚v3(ItRk[}xcY#.=y<3O)uIRLxdBFu>wF,Ir[AMCڡ$@9zhc<(6Rl<7d&Rl9s.MʖPU [b ʚ}TRAbh#yfmAێ u pLlnsr 1lf$_)V͍wHPd9!=1*up]"!^',@݅1tq5ˠ)+lޖRA5Sˏs)޵^s0+t҈ V- I>Ǧ`H)[ ʫwdeص%͢eE+z7JL_\{BW",_>&W#BxGwac9b9ؾ2X(Ezۊ9ڴ˃_Jq G+RЁxLMWAp⡚U@ aED)R`~=Q^EY~dNh+<0aWI$S!E;b20L*Vo%8 fG$YƱm؝׷~GTEV$7r<ΊX^yO}j `t'_hogI`lmldѵIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/logo_2_ans.png0000644000175000017500000020357712014170666023731 0ustar georgeskgeorgeskPNG  IHDRSqDQsBIT|d pHYs#ktEXtSoftwarewww.inkscape.org< IDATxwŝvfJʫI$8&p6`~`8 d%ʫM&tWT3a~~RJQDEQ;q"(݀"1 0q$QQ;AL8P &ρc)"q18P6LޚE~E2- 1o{z3U]E Gɴ *V, S 4$$۶]Y\l+[oɼ`HwҞ9t(iVhަydU TPx3>v> 4{_(bHE 7UFUAδd;!5Ԙ AJ$ЁCNvd{35dZ6ѽ9 |\%SݲnGE^H"|5g{ݛ)\M50wߡ2W`aQ/1d#Uoխ3U>?8iu+fb*Tv\׆G{SCsq@xH RA%d:,zwdZ~{+(&*@u yT W=2m @Dʵ:& CV"GRI7,lH!TFPADuK 'f |aݎW]4 *tOVKk9;7~( \Xa>֡>W({&RI( R[L# C/@n/OG+[H$;2mgn #T*]`N.p} ]jȼ_֮ =&%C1pd(Xȑ>WZw"q6)'߳B':vc0L-фM@EX#D=!tt d=:6XqK^JS@G ߉ }Oj׾P?Mz xP;< W@50`AC攦șR_?c- `f:>r^ j^;؍] wnG䷳N4 SvtjuA{ŠhY0HW Y #J!ul!-p7cƄN8z MR$ b@6U૘k{)M~0fN6\_wA5vhj.E&V\Յ8n;&c=!@y fp9 hzJ2<hvqMW DAE@H>=@Z\NJoK7yPE6q*Fa|TdmD_d\ &`ۖ_mP ]h7K\$:wہXBf\0&$SЋ:_]3&jujpTSGb+dz8^B+鄊*زڜnrR{4TN[#҄K^m1ux]B=?eTlY n% o%mR2"N'!ʁ" dv¶gacS{d IFL(qv%v3a6[/ jksvHChhkց@R x}&nX 47h}2Ip}B =ܨ0>7"1PM:,A$RpVoA?:@cR `-U?Ctb#KB.H(8 `(_guwS,|A&|}Nݝнy3 gji4 6 H}{+!༠G~2O12?NWM,c@Ը%:uJ[E+]1᠟{jf;̺l R'3Td监d{>.6\xMYg9 !җY,w28Ĕ5ڌ&tSd_,Sj4ckt_}REez%g2IiՙA,Mv@e*f:ض &AMv k"S~tSd=,_D34Ot35:C:t:}hUj|ݧ*tbM)[Bw|*~7+%A_& ttBfqOe<%:ݩTlrdѯbNKKt=z]Ltr6v6*)U>&we41*x#is20hB%R D=Չ*ݩNVg`j*8h 0A hb ƩEot ء$? ;Cq7vp{nGFq:x{(^45!nX(];ӄzG`sp0sWс'BM>Nh'ȵz ݑYL+0zh7gTLF hdzw9z(gڶVJ 0^5:`UV3t ڿJmzWazPBi{\9 QDkw1';ȴv^Mn4qEntÒ3t%XI'i2 8D֢Bϑ٘tZ* @cс)=ל=Q"=0lw N@;&q4IYK)DD`ȉTѹWz".zA} L(Ywbvm(H}=:TJt.)h⭨l0jLnl@pNԨ(0ױ)YʫT=% I_pBL2YH=25™IKB;J[a4K*)DCGujC 0xL"2}>M <сoN*b,$7uD?BiS0Zp>0HXV"D-Ϋs6"@eL!F`\"1bѿY︝mw@Ue>JC1#T7|`ZC_i?aޱth7cfri{ nQtv<5lݎmT0Q(IqB쀇 "ٿd6@+%DtVftd܈}J'@nd61c(tm^*++Dp2^|vDr 4V(aDdJ녻oaĠ#3 `}T5P~[l?$0^:ФZ*TW3k9E W^JȠWn*̴)cY1T&"h#h#ڹ ?4W!A~t8-Di$E6 l$&@%mD5G6f%BD 5~FLvASS/@)s&I?||yL455q˷-B}}{%Ls-Jv ӷv2!JyAXh4ϑ%zlkjz cjܣًUzGi-T9#.UGMdj y+Wȩdaô|Txq.W^BnB"E)Ɯ4_^x} йc(x}k+,;0\zbiHo%ٿhLQhEA3mS Dλi9zlٲN8f͚ڵk֭[;Hlܸ|#<Ӏ&ou1e|Ab|os Daa!L ! a;D\=D/Q  am #|~sC^$濼3g_AmvC`"tD߭_[I"|?q={]v\>~㩝>کӈͪo2y\GO.>/LGS=ˣa60m0-H-o{$#;T,dI``!13SICn2A/dahO;O6ߺkQZZUJ)N9=z4||kW^y뮻6~_3g~878sXh\r GYt9wо.W W1"E[G}1zⱷ[ IDAT(k4-*OS`ֻPL>\zTtW+EJkj(#XVxo/_1KRtIFQUfcWW:RFI#X^rb]$eY=Z<2]$|{^*eITƍN )Aa7=-?4W3ݿ,{߀Ն1R*CTU2}XLmNN;! ?5/}hb9iDi`C#d5̥zT?&A >8xW]]]I'' Q@J$mV8im ,%CꍍWbΜ9\tE^믿͛7OP[[)++cԩL0;+"k l޼?(7ɔ)S.+dʔ)J@HP,1N8IǺpG.az{!Ԝ/ha?@TOFՂcp :_|] -~jKۛq~+(--u+*TS3H_@Ǟ"s= m8fu;J,,8I w%T̘Θa}yqR-Լ1[2“y)fӨdJGȈ!&7!U%D:^r)6^`6_=lb2ʳ2,@4=uP|F *HGa K`RSJw\v/0Zn?˲t χaܹp-gy&Їd2`ѢEɺu袋;w.L~?L&… y衇Xb~:s+/F]w<B?O$ZMt8Dlf =U n J(uc^FDs36YӘuTlGbg tzk )4'Hl.mԉ MG:&L8A@Џ2P('˺4P ѣ0l ln52mxP,JoKB2Ues]s]/KDj>>X36q`g*D`z*@`P"UA^Ϛć8H47T:T!wZ`;/"xƦ8(.0-2\uٵ$"Qd*rk[ F.>.^_Gz&NHs/6H;;v/ |87tK, /,8;3&nVl8_~9RJ~i8aֆt{Y,y#A@q3Xx Fyb%T96m&HLO/J@ HOcN|/[~'2$] ?~-j):Q oUs~,XȖmy1eҿ~#1eS]NwC+;ZqxS d9HmA30$R%k UJ$0*) Ml PT "i n+{ÎLG9vn#L^mN3Yh)-X? Q8C:M?oLjdܗ¶mf|1_d|tX?甓~˪8~<9s9|䒋/ew_~_ޑU7x#555?/8d2ɭ%\?ܹsY~=3}t>Ͻ{Oy#) H{ уIR8x/$*b#;Z!3s21Մ{YT[Ʉ,ؽG}0w c?'_ܰxC#qs_+{Iuo[O~d$BM'iY4փ#dyjUd|Kw)S?]WPJ0Pt7`.UP!1 b%8Rtb#Csn^{i;J/ N>kMp ?r2b8]i>2iN\|eea~_pBO[ﰧǟ| ^qsra lnO3NK|桇b} 0X|9tq#NI.WD&(/9>Ь7 7!}TCQs'vJ$'x{tv"KC2f~t %OKdOJIImm$AfT%-dIY2O*8߱ҷtE񌯩 ƌM0`e93=O*CNw'3+DCV |(]Cf 1r5)"Mu5(,Jp0Q.K4璘og}%Kd׿,\ _?>_u$T:(8/{v7A&]KMqw· 3O|_~gR\ɪ^3_ ?,;\`z˾~5۶,w O@)֮d`)CӔ]GEE9_kX>wY$*`5„yJɼn}к<l}K/%5kְl2͐J(--3<1CYY~~֕_eY~5?xM8*AJgٲe?Z~sݼ@.a|雟f[qm??_;oSO>^~۹_)?G{x467`֮_Mk˴|䃟dQpsWac{7dz&Sٽ5&FQp{;F W SO= 7F<gL]-'Obzr}a]s y086sgTPe^O:*MMwmjUe/Bu51`)2?^a̜&r)\p~s^z:qҗonno|K2׷.g6!Ek[+_gx}&k^l sE_k(]lOU I?q7m]|dng,N=;#X ajͫ,lǦqc'rǰ+E$")/L4kMrK"; aTe +?>Pa ܴyN'vK-1OưJXP9yR/ö%0Xvٛ/ͼoCC#`5>= šHv^FLQeR t"[o_eXnWҼX*'7bT2<Ï3,\ C؅"H TyBQy(GqI1oY]_HZh+%gm7l7qZw4gg3kzO|T׍\ ^8d}%)VEkJWhU9@!4d%1.l>+Gv >w>*O}Si^},`a3u=A:k]mMM(hσi`LZpWǡgDJo|o_q/錃t L/~2Gm}w8?PP[ Lc08 .cêMy^44Ʃ)SS@E{WT$S6a y@)(XJJ+}#6r 1AߪF 0қTX\gCMz60aah;UÅ#C{ۻ7?g /xF?Xk}Xo7Pga { 7]x&l@&8\5* k4oy +Fqkg/Cn9'/֥p9i/DoOxPY'3}֌!Jli#BAG1*H%Ga\q71 nŋ3fDL)R*d;ɹg^tvvи~)o\~sԑѱ'~Ҳ:i0{fUPtFf{K);b?NB#wk#Q%A@PnxfyYSIv6&Zґ(^!t,/) EO[$3%&%e{s[g$_?6YેKPy)٩}ٚp4G2ك9{ljecdY'|%&r&4%8mvjsO2r[n_=-ٗ8%{L}\B%fwN[?Heq"ڇRvNa]+-+gF̱}϶; UuϭϤ3EK ;vcSOɫj5ۺۛ1^ t1L1Vv-!𜏓cq^~e.R&M]@];saG'HP?i:!䳟gW" ]۸y'}tZ#?ݾ^Rp ;maa2ϰI(!I8LA۱Y! )T>-HܦB.%lOV12 #P0&[r51p$֧H'$e5>Lk hLJhiJ0]%'p:\ =2Aa*%ssdPG8jBJr=Pi>E喕BU.Ѻeh'ގ8C GoCYUΩT:8]Sލtm /5OW7 .5C|n^{^04uo~ [wra $ҪǼ[w. 3|mIT2W}1o¼ky' Hwp{q?sC| tK\k00P2o v4Ei@Gg3,ؽ Yʤ(lGZ,\PK(luOmc::ٱ%(:dǑ8RqTv-(I\D #VҎP6#-2EB:İ KSjHذ4["ԔgB?w󃹩RqI*fN8X~JhhZu` @eUPz&XMtȸLLݭpQ=qp\U\QB]DHI|/mRI*hbp*4"I#[a) MSZ-*tERZ('8RAUYUab}A-Eˮ:u$KfhWi⎣RhTA2R+6Ϝ6UKoil!% u`-(j|\Wn4XBxB ˿~弹cmSFN'^+ZU@.6( _\A};$夲#`{)SC&"mik_£JP劝#`0-?Hz4ec'Qᓌ %:"WRڝpPJ-4lz-q3ij1,Co!f5S ` q*bSCʒj*g+dD 8fLM^}}$1 fQ QV^*7i&%ILChs:#t H9P{ّ<5q$uc+̯f@5FԫR4d  ܅?q4Ÿ]TJSl°[*aI¢r:=@(e|@ Chi*qP^beNJI %g,Ӡ$`b(yϖJPG7C4#m0,pR`Ŀ *ե>(8L3Yzj!F-,ۨ88 JOoA{^$8ݴr ^ Q׶ʖ>$y;Gs} DiiH: JIuw tڵa5hvP5}!.`ƌH$qX `5%5S( 9MIۺXCviˊ''\=qc W.vsPb4ͭxiQ}nږ(t $Q%a2wZ<"IJ)13R+1U%ҼءJqWuz:Bʖ4V%X6Yd-t鸮6ЕJ!WH%7=|j zQu,u'XZ; fb1 ڭB| N˺CUhtL?I,hef?{sC=wm:5rơ"h%4:D WJPf@i#-(!_}9ʧ!:0Df~y+ɯÕ n@H5>H~K54;v'b5 u" q`!A -@hFI5Xߩ6o+SX -Z&8KVSORQ9yȽt oGb۰&jт%;V;dKۉ%Jg޵HHInkFu 42X.v!$&:+dΥ`?=xY|VJ~d3Sw7?@!禛GHR5}&03b~[5r(1?MO37?1ǿYVws]D{>"NaܾOQ]Q( jt?X}e=ejt}kTH|CqACpzE4JA5mnPJ(61/k`3RaHK/897 Lq~'ncl{V^435~ͯNB{<-s"hp@ICFmQ;L8tJ=q$HDDXH[D7~Q-\F UhͬD Y8Yb5We! -fGZ;[5{UzgrooGsAK/?wL9B3\N2sr g_^bϱ6lF"Ӭbj/q #A=?9G1uM8#t#1JiNGsIq[c U`TcmVVV ?{pv'zbѱZq|2`׍&Əba]+ChlM.6l 1NdH}c_=WXL?um%V*L2?˗o w?.R?f V ,.vGE!ұkZO4knvx,4?%Q^s?GV:80]\_v5PY3?&RKkJL\o (Tg߲/=w5B`h55f']w>kEJ+5M0{Yf %J\Ǧ610&bN )p˫Õո#5N.x4HWe{Hf j@67Aj,,G;E- +) `ڦGltwAxʚ&2qM~q;!󐮇t< |F=j_6ܿ֊)*0FYĀ*@j|s@c";h=b(GeT#lUZA2aFoh 8&=j5P`&Lwۼu{u)O Gn[ ]i329ERAh-rMp7kR *ťHn}V{|eiϧLGHRlF<['QzhSQD1(Rpx*5T82upť f4d-5f Fh p;2fqُ QB(G3|#Ué bRjBWUtZtHګI`GBn4g-U-X.< `XkmY?jn . HNwND-$tAk^֯] hl?Gi:= Hm4Sw݇Q'#{wmHtD^iS,Q{mD݀ Ks wt0 !4RX,e\<DZ`@h͒&Sj&ÝJ'A^#yGr_[B)MyeȒ#CdU2|^C.T^l,R6(2f z,, JA<>9FhiK:ZXm 4I$9֊s&GiAi8t6/ F*u*aS14d~?QKOC{otl- Bf+R.U֭N{/ XT V `4cmꄜ.r`fӸ,-IB5KZJ1Hcu%ZQLL@g\:׌och>Z gXSUp2]{ 2@ձJӫ|eC9Ɔ̭t8;$sww۫lmY}BhJYVk FHp0֏y*!r_ᕽ`ڮ+PiDmb A.{ta dq;PBAVH:s!TG)fTLw- *ey_&1FT *߷vGyݛk?@/3ŞyJg5C 䳨ȞaCiĬ]OZ6˸c$uױg 8]W@4[&䳲ʑ%Fj{T2΋ha+zLLSg,=*Cje ^{O%\QYS("Wya|bHΓ ϝe:2ß٢qhz~rdS?hxq 1deht i 8m4(tZ1?%DFCyFcر0QڐsC690;-9#`h<LSm&s$_3FJsTkl+jYFӪ-JxTx4[tW,yF{ISCmLsT4gcn%@)KtH8jQJhElHiLd#_+X dӠU mFGx aK%#k:G{H܎f͜Ð/7sl+\GX^|ή#HJ=yta}#q 8HƇIF"e(\8Rw`/EuaުVkk }EGDD}Nz(t!;i_jćzKO+3i(!F!]x 71w;ǧAG+g`~cfcl^pK78i$ĠjM q;v 'gMyB:ԧO|4|QȺZ!q L2fn|=鲤.ȧxၬ=W)(Ø0.)M$R2R˜v'L]rA0LΡ]6@ԭZX$TjSujջqM7w=#T!ՑjwƇAc7&F)BHґ B8Jܴz)r F+F=6 5cE6 cd}=C;RO`ttI-p h&2%VЌ΁0hT['@ *zo<}'M"9)ٛ)g匆Jsdޭҏwf?<wO3ز@͢2w0?kB๒ ŹH x#h:2pJ?8ѓN #VrUssZM"=\Y @2 P0>eֱHZ'P-"N՛J2IIbQHӽb_fViml#UU(dkj{~_KrV3vK. ]gA( n(/hЪQخ}.,* QIwA pq`ܶ#\0'rs'^c$R: ME*1ZZHms])S4Nl9["%MEG~uL1*J"}I]t1*0Vϔr% -``u~K=Ls{{N; BȅZbϖXr:p{9xj̹ M3bX7:{-@HA>{NkczK焕1"xiV[A\}j#UX-a6!I<CVT QhC[u4oWO V#uA(mTXhAiS9rEᣕ#d4Z!MO?2zn}!>ۯt0=$҂]@wdko/n![8Z[m"݆ Ӵ;3M0x3㹒LdЎ*feh栾@X@&FўtD8GԚdmB Z3ttrd[ &[˕ ihV}_i\iR;`L:ĕR(cP!B~El)0ח[߈4F)) m0Y S P8zgVBq -EOa N{;6:nbT2Q#>0AV$D1mW.8-l'63ā;<'vT-Я5kXƿK9 ѝwB=FÆ5᳃6T Y:e~KhV|ێr7Ws$+O&3TdUWOjXAq6jģL}Dii|Bs07Y.{. )ZuԎҩE:9v ^( .Fmi8ZGD&ROM'TBN{Dܟ=̻%lӵt/48He0 * %m$ww#0(FȟGA;$*Kǻ ۑoC0I@8&&6jTV *_s<}t )2(O$G9>z@\K KC\ Eat\_}u~Vګiv+ް‹)}`BMc5$ fIvZYeѶ-~oB[C ?_] e1JasLD}ZHMjqg̢U Wh6,-hIcN$]m<e&J|y2PP _7ޅTO|YpdfQz1ꠄs$E!"AQ)y1uʪF4ά6ؗSɋcFͳ/Uu|m( .AEf~C/n%"B+sF45TX+m;BҠc4cƮIjՉxݟg'Q [KyOi.=As 0ʻqE@n5$m7τDLʦ48ҺtL8$M4FJ _aht'ML}a(d IDATEDi*Zs1 N>[ Q tA[Q/u)$ ,i൯oMTY0V+_o~|%zv9\qޒZ+#]v :Cv`8=`jP~b #͗~K4Nu(S(OwSٷe XxL~ɕ_f|.@s5%cb>n$+j^jtO.KQ5$T>Ҽw]?w;U0niuP06^L6!|D;p"e+@T{G:.\9wBZ*f$&RG(>3Wh!\!9 c m*@r|4 V ڗqc ++0"S(Pg䞗Sf>g.< }b8Mqdy E;LX.2d&Eߟwe/hg锏306 6өL &"堌aWKnahc0!ILw]R҆B҆ß)|}b&QLƱ7J" ,=)_=iA71H[Ct]K$ 7ef~͟)% WzcC9 =y_L}j͇z'Ôp] bŌgeV*B__^=xE=I:x4e;QEH1j )2puuZ%?4fPcN 8et[z A}'?FᯞI?_ Y|JR4:w0xN3X>u @$'X,0WP_ [p,vr@ Xdi`4s:柦tj}_z_d?H HIfϡۨ1f~4Lۀ,k ὔ8YձY$dze-wWLG?{WUG [g"r:BsGrږ2c*`xWL qvqLiORI -SShlGVhW_vNO"кw?/G?[=sy{:g~^/l\ ٨{CTiֻ]23L.?Qs J~|[b~ϘүJ" ?HqMi@GYl4AǚRb:tll"_Uvj0rp;|Ϟ n9eЏҩq\pS~"roo|+'qyY:E~U\(‚tTB߰5GT;"j -^8dy Ny+"7BR7#6G̜_Y:gO1 =..1kȕw~ O"Llch,-[t$ )m(.r PP)@"CQ %aR( C[4 u\VhN6 c 34ອ87t+VjA0.KHU!D }>x[gk !UK _{{Y! -uS'V2 !F~/ ϕx\)ٚ˳w*ds(ya[d+eIゔGPPxq.EO}P(Gi^ jZC>!SP"tYw;c N0s>3tTI,ͷWw$G1{xXC%JAnrx7.?)b| Qȴ 5:Ҽ[oo0I{dynBmoֈ5ЈtUXChBk!TCB a4N{~R[7~q?myd$1Hݦih[) Oڜ|בdWW01gDǑB"=̓ 8Kz q%Cq((]d.52.MNϲzzigМ_~>YP\Owj6Qh0+dh9(7WQPEH!p`[oY(]mx>R+x9V:sK :-(DIed7yqYjH9O2ɰ\b \Qql>yU7m.?Ac|{a@vl`j4K(9~mq"+7>[oA~}̍u((|z0FdBX]#UL#>n0R(sӄ=!"G$_ԦW32t'{!zs/;Y)x f2c`R&k46{t 7jPiD|#buJATn/W`tϵ_SAj^Y]]|Hyleۉ e> |k,Ι:S Fogpf;c~4+g.wFi+) 1zםs73Ә(jK(nɕd&NMGb#gY*ϤߥbXLycbD_xew؞o7xY7%4LDhaҨ=[qǑw"/#3c9ɞmy\0p PBy$P[vXJ$\X<&*7Xv-/nLsN7)æ7֩3467߄;S[ů4(@"E4 1:Bՙk-W* ^MOЩ. a&^{7/p 0w V(ٍbƜˠ ~lt(D7 D?o =c`_Xyq DٽMP/)LsE/Ǐ"Аt(.̶ڄZcA aT`Nnv$$juU62jeV#~elC߁[ϠJcGMB`2Cw  Dndi׭.ڸ|yۿcx8jfNJ1y]dߍ#jxR jZ`gɥ8LnO8/zp5*,O=`MꥭFS6s $& Whͱgf9t$o{5ZkYɲD+F죻`dWK4IcG' ab$$ V]o^  ?i%r2Ʀ)K* ̳gv&v !l44h7)l6:,VDN; A哏g8J;‘ ݸ:T *Rum$ד8BX.J[kI&C37z@K @O!F.O>Gu54RX^fɧi=KK!ݸA _${s[xOѠAd6Qڡ#d&G;zsfHŜaSoa}c9#VDwl(Jd2x. |i~^P5D|ߙhpj' G)([ lKL24VBkU ++mVxbֽTM} Г})!MB2ad˧^1\?oTd Po|ak::(kz{H\_)m yb-ĀuE;/5ɕ6mcp6gx}'b4KjS9g>N}k!h}SӈWGdK 8* >;6R/^Dn)=ΞeZïcs9=OaiƇ ?7׳k~s>]]LyMVΜ]-k(ݏ22ate<@cF0>Nv 04XaRf)ok6{x r:]ML^cMSz$/FQK])x(/W9QK Xi6CW\G6&TYɳ4jbU-ryAXXYbhMd$ ท6Gd0FS#Odj}hs#i",kXa1NMalcZ#K<&@:7 ; UyHw=.Fv5jҫYveYP;ZDCp'g і'gMЭI  W#?K0pQyx;Fkt3[nbPQUѥA~Sb9&u.c4~rx8~KΞ;FIyH!0j}78<}8>㳧&iN~Mϐ/p5vm4GOmZG2T҉gYBH0" '\cSZOE}vu1( F䗞ݛbL82׿_|+v/,.N7|3lanvj1&6MP.%'{S8:M*%F*7m(F:E/CЫEC:gOcvٳzQ&x+0bͶ&WB?)}t-ps6a0 %t ?Q d$c%&S3/Q& LdBlnwE8#qs02V,}R x!qr Bdj gqiܰ/Yl hv|4')b\]hb8ˎ:wEt!Q?^-;!7{uu=u=Qz/Ys xbd}d2lf+bjiV4ެyNhڣndҟ/3wEv^[sr֙DuzYJ|:xrKDvpr0ϕX s|UD 8޷' Mvrqjc8~I>+w0Aחi4$=eu( Z-4=5D`"P| <ϓppZªB_Ʊ[ܹ#|U9n}&Yp3=G|$f@ ӥJA8ɕo)<{bSDz-q?Q/Wމ@ԙ#sut/^0?ǠgqUs/}Mc`x [Wü =*׭Ѿ79Fz> fBDQ<{ߧur6*$Ot=tYw݄ӝ(L~$;p0Bx R6y[<N`^LsAۇ\[ei\͙z n! LН4NW}oyi֓;A<7_DX+{QE$sGLLni!> 5=!'_v& x.!ÍM$3E>P?ǑSee=(9‚g2N*y*۝d:%<)d=v l*p㣗G}$:`kl}|؝9ۮ;}j_G?y"~à rĽo{kzFȆ }Ǵ/H׺ 65 h&&2 p}|+;lȝKyMΥd@p>ٔ-w`<} 4s͌FYB$D 6k.x/668,\96"Y$4h$MΚϙc VUw9bytW귪~9ub,&ZR4< _2$ fɧ-欀sTfuK!P4O!Кo3|e4[|/0 B|ռ?M3yf4KM6jeh1Y.tVR`) LueKC2 pkey6CRlZ/`z|?;ldYy=͓e_譏| )%Evdnz==7M<3$rL!&fv萠fXZI#dh*;`blKҜ^D4:so|4CAwoZq=Z@hہjyoeg WF-t(W#|SFI*bl)NH)i%MRP?*"$Skpsfh6K_%]w;Չ A@]p˕Ns\ɜ sơfB"RGQsYŪ,N1h$GT}n /YWZ LJ٩&_, X@!V,9Yl%"xme܎oQaw=ămoZ}3W_c;BaiQ.'ڕ"2ل aV\&;y]mTɜ(4kӒLRJb)mEܦJX$eG-k2:MTG. h9EkYRtY>ippάCWU_Gxu &|_Hh)O}C;&b+,l.-S~}Ȉ11s4Nip4l )+܁[ޛcl+nljMk{w|Cﻞ{ e M7/ׇZf߆ /Mrha.4Ho:ۗ [Y/|ϫ3CHƼzFyG:)s-Rad9ie hD~(6+#=[P2N26\Ƣ3u뻙ܮ =i 2Dƿq4饦zKZLj H.`ٺh$F泀7 Gge&ʑ(K1[d)r8ʥ+KMxTGR#JГh?5O(,u$؉5h-=)lGHKC o< XJ!~Ay>R*Q6+IgmF|.΄aB|?uy@ yʹ+:7k%gڒ pJa7Th1N{ OBFZId[#hM7/S8J"-VJ#^ VF[:(XRDIqXRR2X2D)"m4bKJT*$8eF>X9:vpKQ(ɴdvI MteRaTEZ-P H'HLPPp]Uvs{~@{wL7kQ:I^tm!| Br܌ni%z36c/훚RiφDzV˳ӛ/]و 1z>Xgf }<տ8m/ši|Mιz RM{^\mЇ%^}tVB@˴¸{) Ii4U?=]fzδ?nHJg_~ug˦i4 R"R:da&h 0S@&;A&%qE>cqobݲȤX’d\ܬE:addr2 J je όcbZ.q36HcR[9xYmsg!m]87>E $A0?6׾S˛z[?g6 X#\ؤu,ݷ3Ǜ'!QBY"XBPDt7EݥŶ$*jX K :Z0yHڴT+}c BwXN< +fS>euIDco+Kn,"b]WmfA__$) `z/ߗ9>b*~/(¥VR޻e)n秢id29I u0m(" K*LѲzł{dRڛbbjiBep//05ټM!t3y!:LTjYi4? Mo:0.0k4 Xqy ig@udZq%B\6KnI 8RoRO5j DDvd #C8^Bԫ1[İOxJT5jkEP!B* uh-}/@cY$7vѕM!#MZDTӝTIDOK0}sOS ~$^x*1ٸF 9y'e闦Reh6v6*͟27<9W|oRR*Ղz1\5vn&'.ui˻L8Yn8"ʽp*_S;2ѐXp,eF<׬._lL2nZ0Nfչ(LF)Er}"p(B@[oT2Qs%y$/ʁ +t\nĚ92TapF@HT7(HK$Siɨf? T\y+e}% $fBqq+n64滊4?EA>4%I8iˑ8V`YYЉΦp)s9P e*J glZRf*(!2]gW7?_w?dGX;$N' 8K{sSCR/.Lw#F}69%z_ +lykuWc dGw<{yo0 9ޅEI͗wM7o6oֺPwɕ:Ҵ`&ӶjJ{,=˗1;W5惐rO5x9rQfT iGA'DU^3ΌV C5t3fwO@ҘWJV8ǩD^ ,!BT`)v納fG>M5(’[!Ҳqr nGqiH!Fϕ-1.?R0: Z| Wꗩl3-hhBj2$ȆaMziOPzf2]eV^;EkK*J3/͙5s$X䝗'csgJ32x@Ta`;sK4n?{Am^ؒ[Ƶbgx{ihCSN*!i+F׵qZ+#HHW㗎LF.JYj^L$sK$<\~H5B1%IH*CR;[ Z`TX4ZH HHhqhB0K)Q͹ɾeP&o{v.e/ py7ekȚ到-A'@v@ӳӳ88bdriKhGL"ض5iw$ R=gyztr81!(Kfk(KyrD#ځN֝ D+OM׌9o09:u[^zIFz۵<^ph^2K!XcR/ KG7oBw<$iXnżC_WTįM 0~u0'͵Q4D2V5ӿRώy*3UxYk~d(גq$RRDX2JYB"@UJdѾuRpOEAĭְ,߂V=m"nҀb +gҖ9OKfPaRە8 2 jYuwbxl9O׍ݓEPS&`"$Oo')i%q ibLm \K"- 19EP+/ZL ;d]c$W'8m;^iv&6kifgziU]X.m35~1ze}B^-Fx mw;w'A4L3蠒)Ե Ze:}!<3[R``Rn,ǹYfg~HUv62V1)HA(%R8BJlZ%2&?~xUo/4g(>B+bl Bq,bqRhFb t44mM,g +JG2~_i&U9X詳r =Hס >YFKFRw,T W, *ByrM{~fĜ=YUy ϳ'anOMqpRZ$CJW_豣L o^fSߣeR=MJNJi'=inXcMxhN&'F$fq /L5-Y qV}2?M|3ÄAʐNe^Q93CD%Œ }ٴ!" 4G'gtz % ͡LLhB,IN0@,CK/FQ֙lXm L:ۖ%ݕG7&FC'HNޱ+﨏,G%`zԘc螖J* T@karw+Я#J Hg0N:CogO"\)at# IJaH0$0Ssl 2,Z ,>Z ]f XH~k 5DŽZJs%T"K]ZSg)3]6] 8:| v$&-b* XQd}l\kVdVE h%з 2gJIN3})=Bתv {ȱw[DIW>7/2eEMCtpiQ8Gк|!DTk3}hWS|Co"y<"[xޖWvn7)zy#y~~Cj&W[̆n@ A8s ]Y._T0i "<oz'2pq޴ 0dbrl>ǒK7k-BԨay&  (gf3HhS??^8U~Z +pZ:@k+ @XC"Dx23~Ah4ŽLyidNZY~L>1@aƶ7P,p;-.Ѱ@%puD?rWoFx"hixU AD>OLSP dWYqU<0@*$Wf9)zÏoOBőKH A!#U2}v~F9VZ""mTU6}mZ8=\oS^dъ %J}AEj^cKבkm5LRbͪV*m v\o0h_0;I)h۸ 3 BkKevNR B|" 0k*:@KtD%qR4:Ԝ,3SDhBY|aG#Ca*aF?/ 0i1P5'|-( $J8ZƌײlRV3WPq发k -$"*c` 6!xNԊ4Ƀ# Ks,4 t~MU:5^\]Hbk"v(RTV*W/#{&mOP)0qmI׫Eprf/-5[|ůP+eqgR5_.JSznZs93\fN:?{坠hjպeU+@C[G;AP(PCjOf ɤ_2r^5d4ǦJv][}e`ҘXH4EKj>zm\[! [HHqҳSF3UFp 0Skox IDAT m+\O\Ǫe{9q(4JE"$SKyvX))ɩ`RA6X8m yȴhUg/T/fH9R}bҖO(BR%iK";+P+(}lIM'^Ƴ>ٴ]K_HCTs8<7s5+)ȧ/B*L;*݆nA:9p7}tXr @*UX"0 kyuRST/Js&sx!iWC^|v8* 4}(y ;%pJ2\:2甙ƈ;VRhJ 5Zq`(T;8QAK+)HY גR$Q#NrnFܬzj/G,BJYQ Y60eY[;Qny{gr-V:f5J .14Cc.BW6;Б[3Ўm n䷾âW t[N.#ӹ& JD0RS3/δ!^S-lj2uF'/L0+j DZqϵ}9%yՋ(My}@3V(heq)BB3SmVX~H.#cӴX3Ph) 4%c2^HRZ@e;"ԕg_L&ɠ 3&o4G6)(c] ClU|2i_.Mֱۨ8 } $P_KQ}xsqYgb.Brlrظܱnսmu4 -@vjvZY\p٥Ts՛u/1sZ\|nN=uZ~w2ș3tv[!M..,!fG>咾\vpI@ oݖoBio_`O]nv/JѶ.46cPf+H%d#b :5aeS}sZsn~(dQ`*0f 7ra0m\֝TPIeq e5}_{!eki]vc/\5t/}w.6=oXy_t9=׽i-к Halw/黝"mXv$Q6h&hF>R"\FjN}g{/gNqYiH5#XEBj>mY&yE듬 f/) UӶkI0]CDpM_'Ñ #&$0PCOjBm߅Rm2m' CڋJ5J9P &XF4G%$-zzn~dG@cwݱT-Jv-Ʀk̖è@Y:2MTd XMi i&h"Fjtu!?SIYL{(_7~Us ec"RMbҗ(}Zr&Q "ƱU\&`~y]x?l[v7GG\{EuXw{p,;sqӝkh[}'I刎e D\6ru^X!C;nD]^έr/|jmᔶ0 k;hK aɺ6A뱀4V!Ụ̽O7/ضbm.BB/eID`|@(SQ%!-)݊|!żdlʣ@ _݇j(5ZRru&%0ly13Ǘr$8Y$atgr9%iwiͻ*qVבAw6`̑9 O#MWPzJ r 7*o]K;{&J|u+q@F5sq%S<0BIɟ݄g9=ﻂP38SuT.V*W1Y$BJW\6:o];um亗3{E$Qo ӚuY_lNZ.ox?W0ۛ]۲mR￞7S2a18&֧QO-3~.%]E5Ǟ;ϼ(r6R~+3OF(r@QjB3>5=O,R/<9xZ! 4Ԩ02C I҆ PkZƦJLP7hqBdn8 ^U,]J(4%ё##F IwGTF5Qڬ̚ƄY7!6@ݐ$Q4Z쨺qU^{1U8Ժdymn;-/Yr@7Ev= ZWAD_{suk}$ӳeNt;oZ=ʽ|yǢR2]ı.(Z357ö{86tzzgP癘`;qRt+?0-vT|fkip5|o>X $? RY~e*;~XI~ ;GYⱧwܺ!M&U%mg?tԓ??7~=QUm< .š_U)U}X˙j5з+oq'?09 \07c L"?!e :s)֥0Ј@e ! 5A2Jh)SQ+q.HzpJ5R#qfbh\8Ы~TFa؟lHL7E*L+jE#@I3ytX[=GJ3 7;WwlZ@! }Wh3''y(W.﹙|c_yRlX[ !h_uҒEnX?R u vL78<-xz.F'B!Ǎlk 뱣1lk>ɒǿlW@A[OO\w u `{z7rͲu\#9޳;9x|[k<. f gyqjt6Kג~-ŋyi bC+ө*^*h CAUX4d^f(sz>Jmd+Z֚qHE@_iel=*)UCZ .^pB A:R4"eӗL?5sIJO: Df~kO.m󘞞fzz/T>f~|M7^ԩI?6ƕy>zr/_m-u,,z_CO77^ˇsgԢ隉>񅯳inrxڂ,<Ɵݿin*~'5RT|>+ o^Ƶ[Rw㽈]BBR `kQ!: "'$pcM| /!<CwdsS)OBژA<=:S84ye 95<~voS L?DE\GA ޷,e1I>hkIQ_0 ݃#䊤s4h'9s>__Prn2-6350FkCX(G! 3,h`y1N ~j-j if|%gaHh'5zZY iIǪL ZrҴ͘Ѣc(AH2%ߏn@D8椤(G9 SwG(^_ `I*~H{f,_?E>vr򮅲 _x.;n!ְ >Ð彝|yb~N>>[J*0>5o߰q.V-Yu$f|LrW]}wx{@$5_CUo-ka=AkYI0ʃ0 :4y&-@2OfچB70k8/aI5giӜ1۟~i75/;}ze1wʥLaOߵOF&*# Z9p/Wʔ+&ċ|4^")Qr^ՙGdeѱe??0lH/4Q?! cT,WPIlncRU&$$t#$GvJH&%p43 hɲg|uiV3J1R2IukŁW ^^eV秲+ pvfVeu=';E}snU\/΅Io{ə/7m`튥CSLΑ˦<̷7n(3~z9Fx Wu2LÍ@?uϝ=p{52I#kcJY󮏒j6f06gB (i,O6-OZ/+O\rm7Oy26se?u[ڒB>Rf&j_p^WG# "wz-M:9ذ{-cѧ+%%x+^nGX5v:ȿSgG^HtKr'?lLftp I)TV.NϖQfT*KxU@k]6LZS *8Eu­DUN"Q#{_yyr." Ӗ6Q zAZ"fLXF(;><̜0DGC3 X)NB4G,qvx(33秾;l+~lg\ٙ%mR+3e;"Zã{Yd wOm'c;vsUwygO0$0TKsl5gW^민ʵ:$3~5|q3DGeoi@Ӳw0DyRO?ȩgNw|'qȼeCIOisށR̗r_};/\^W?u6~|s|au.K ^orwoW ݝEΎLuYv3]w%'&]iǦx/pnW]?n-82`ޯ1;;ν;'GZHZz_~ɩI؎,/3tyS"ˑfQJ}{'?ø?p6˯FGw?ͣ0x"0:'gVnxEH)ٌ IDATdܘQ|lٶD";6,ٟq[[z05*rBؓ9E=ՔB r'}k1{:x0wz 'ΨB'BC{,=۩*fQ2qQKM8--ݱK7'd2w]>JKKks=9sd9VZESS&LK_sfԙ̈h X}|箿 w᫄C1v'8RO2ObXǎI3ܗ7됏V4FBdۯӲu=7>>~ϟ;OZ:[\+*ħ>ɓW@)Eyy9|_ݽG)HfضO0'Lw-AWē 0 r,렉4@͌?Dc~ ],z#1JU36*t,Iev>`V/ʹc9u\+klbQ(ϼ//SyŔDB(*D mw\x)XTdYxsN;}[Y;M9TvRپ>H@gdu^M*2{ɇ~Milܼ/^cnF4Աr*o?g3/fiim?Xʊ$8?\|<,Cv%|ngS5/G?Ome z.ap5@qΩSr}w{S&r]HϿ1*?_\ eEP9Wt+XfN:tZvOʼn'ij#br~J8;q|d[o}Z*,X>˜9sp]syP هzYf8]ivџb=\yt5T$$wUqC: U> @efoDÜyt40hq/JG(T~ެru7``r?ͫ{i osf|&O%?}ƍӦ]˟o;wm?)s*7GTVѿpqQ~s/LhnK"!c0b2{C,'Wޚ dH>r4y- FֽV0)A0PF!=XMi:hBCIg>5^Z7$0d^z9^_ nC9qvĉGrt޼yEoI'>ȺuX`Aᘉ'cn6,+.'JKN@[.tCT[Y\gf`>5 WJ{;&L>ˉw´$SvP^Y͜)Gi}G?[w].FYt3<ݻ5jU:]w]w]\u r4KICCeOww7Ǐ'|Xf)/NYw@?b<=Py}+LGK䅣z\3=]%l0$dJ\E0i8sH&|Q[[[8O=N9ֶ]ر47n{#fz\}2}衇A*g!0 j0 #F`t"KJJb`ر5ic'}v2iӺ/g(ŷ>|56 JzxC|BҶn4!7[xfc;I51Ё먰7~@$Ih>PE-ZNbզj5UQψ*J1-PWq)͟P6'0wo6RMԷ=Bɮ]lWp9!nkrY30WWeo9 %POI}2h"*?( ڕ?u֮YCss3Ϝxɘ?33B MI.=PpIgW77}, x۶7n~xw yL}}=h@?oC=t3GDN.Y% D"AeeeHg%'ߚrKA>MϱIvJ<M~[z+%Iu9X{l-eZC :s @Zk:wj!RUO11FU!P摧ۋ~ؑ | GNÕK+Jk"f10 yc7LjܷR'-Ivvp=E,d0kl͂@>m.wfOGe1F7Vb,y+ϑ隭J\"%<qމMeS+SZYOfҴrѹX'r@~" e$1yL6\1G62Rr2ydb6cꫯ0w\mD%>OĒ%K())iswm,XpO13ga܄(]]{W/fL fh䖅CqĈ455\<.]ʇ?ᢿ۲e ^{-K.=t:M0ӂ𭤾4d\րxKmmm)%vүVA]4Gs}H) cY'AIȤ$@*:0&ʆ= %wKJc)1#-IGop9,_-;[L;e%1i]> ԼvW?TTCD[tK>n|%vdhML0ـe Ic}UW9{H)y꫉D"C~8p tww[^vŕW^ɯ5up2A0bHd2ɒ%K[4p}K.%s 7e]Ʒ-8oW #F`YRJg@kf=PDG*j_%ullfȔ<<;Y+%%kl(-$ҠAO2gહkXō2WK;lNh K|O05k#4זIFsNSx<)`_n("K9llK0>Gi(ϓis]t;u|r򸦂e|[>i,ʳp19ę~g >6v܉ ׹U~m1}\9Chι͊\D +rUH}{CQ"͟&t m .͉sϠ;xog2zzz(/w0%`Sg}6J),=ԧ5ju…ɓO>… QJ1f_By1O=}}}\Clڴ^.bR}񵷷կ~{キ ڴy=tsf e QJ3aW8䓙;wna~;̟?r.wRN|p4-W""0Lz5ﰨ@8$ٵJrE8t<;tiWE,zYWjm|M6߼>5q8 nFZ*T4r D*df5Jg|e%->m 3Ɣxџ%c{~oC)w/9:KckOkdZqI44gB_عL=j NCJ+lF{G7;{,D(e[?\ =Y3UO!۽E(58ېnYBx7 ԃ_R"@zq<+jhٛH?8h~:d_O("LfYp!GA$K/eѢE8eY||SNa„ ,^n qw… 9r$X3?淿maٹ?twwO~~+H)宻gA\@MM /sx<㔖Ksxb&NիyyH3x_3h2|40M?by\H$H0`>n(ՠ#JYU-R-ʂ<~㧢p;?O*xi;W1]^\W+f0x|dAIDn#ƏByvA)jΌA20}&ғv`UZ,+mIGh4FP3:zzӄY!psh|i;/uT,*vebKJ*E]YqB6@ucэ%_8hBU(ʢCG͌CP躆m X~Kh*- } 9[P)f3,O~6@~sz7uכ.&d2Y(6[;PN2}o>%+T]B1u!&|?C(B!(dmi<(k%9k2^ ^±ž խmO6qh^mLfteB4.0 X0Av/{,'珿+n#Aib\} %=7G3bAP87"UߵWWJ: .XݢDO&ղi 2]$?Ff6e\RL$7SRy`0XxD"B_~9Ph4OiRJ!/i{ԇ*,+u# |k;OCYپHd#Q8ة(W%q36x@8B2_3 6>ʃ0.^0aَ^L]p uV {VR|Q{Hn!Z;zPT_C*o/2(NU%KCuL@9S`n7{>w7Չ O'1t TN~TUBn8ϊj8Њ*Ȣs~A$黜̂u9g'H$an|Qɡ`L_hv\tM`RʡBO ݵeO{wFJvw@< v\ ݷe͔݊ɀ5#qL =ܘ F3/K6 c@]()))M~UпQp  t&=pj&)+(O_"BccI/DzIaC%p_|&oH%mmw;ل UGuO^CH$WiџY!0Qⱕ$l+玢+6YeslvSPCu3 <ږM`'z!\ .XOE B NQ:l6R [I߹tE(o{?)I%j"|<A0,*E󤚷Xe0d:"yU<.%W0Uf5 s["Q4BdFU] =݄C:pYrʸ3.Dȋn(z˻;9{xaC':BR~u jKo"H'k7i_ҥk'TфnyZ4SM8 ӻ T- yٜ(d!ǛJaBCy($Z ,²B2>8N.,Xa2!thH>"+UbjJV#D F8ЌZ(OUG*HJ?¼u"⭻.X}FzUI%>I{*N_UՏfs\'x=@ZUtdRT/24&p3X^"ݵpu3E_S*oV`3#5M R)MsI^j<X"q\` w~i;0fTNCyiX7@ibY EB4OPRBPVVơbLq@!0tMzh^AoN䔑4LCBsJ)q =t!q BW15wfPt.A;zWDwӬӏ|=A(QdX>< vf9=:T7og':ԠVa僚Ay*9)mEsnEHwJ/);op]IґDce\!n#ԄmNm$\>lj3(JE* H9 夻W\^^g H~4e֬YC.<އ}@!4 MPRw@P $* ])\AtJ&<=M\bVNq=5]Sy?2_| B)sABRi(i:RSӲ IDATǑ,Yd8q_'{"UJ B|wp@]wRdZ EvbnpXIx+y@5\AnHU Z2}h#q 6!i&އ&CȆpm&6(lG%M?Wdlɫw,B!@e ={v9TQ}k4O4`gGBD~M'0\w{S> WExZyHCC=#^&f~s=w%eLĕ(Cuq\wkvmLIz(Ѻ1u,ҵd\Z6^n{7Jzs>#H$7@8pEIf⸩dz Ro#ۻ+o(%TLhEw-AL,Sc:Akе evr=݆k@ ^vC /plޞ~KO]IXas% D"fΜɈE.'())9at i8JBw* W.<B \2Jwd10p0L$ ^| /`hbfU_Ƨ>3TI0*q9tnXFϖ~+ݶ1å(N1OzeS~Pl__@Qfzwenc(kb QjHEv^1N'1PwZf)%$VtNz3nzrhFjBC# I~w9H$ڵ@ @(ڇHOy DZRRL`L:R28n/v!0GZXR"p4%pDK9xLuU"#$g9ax M` q=Ŧ'ƧfNt#Jp],.e<;M~ 3>}`hm3#\X_}]_'T9@ +&{WAI$T!vH"c 9 ,֢#i[0"7_"ydz^@ I\r׏nV R膁p/N6Gz!@O$Ř1cF{0KKK1M!xB;' BTTT/s:\N:CJ'-5JCCP (,` ?a D !TietItwF?@`CdYӳm^%WNzV8Jӈn/᷻14/"6/s%UsiZ $=[Yu^_NzP.sfuΏPV Eq  ]ιK:{Hw7=2/ Gt|`/AKr\}w;0Ca*JI&zXp!477iqIyd2NGGGa>bK) [8 Xi\őoW=D ߒxW9uIyY)`I`T)%_0p8*2 g}sn|Od< L?dߚ/!A* $+\]R^^Yy 0 i*N}d6]X5$J*tP۶fN(m[s=a= ؜Rߒ &YL 04~Z6%6gZTN@BgĶt!?OzMlr$ƞ̉Go]LnhfQ2r}RW)yfTGAYض-ܱF@8LEf2TTWU GU GlT*v~( 0B0BӰNSV.B 뒶}_[ `)᧙(0t-$.lG+Ͻ\? k68mayWqtŌcJJd:wQ84%QO!N& B }'7?(,uJr<"LTXC)`xSV!J9r', 7BʦxHMӤ0Cd]& X`g: >ttD*F-J_I_?PP-CyP~K12Ԫ J,4|'}AztMvo T4b."Hs%..t+g]& B~7 `¹sx'>Ajf߀Yk (,/&l=nƯrLD>s T|!OW#j(M PYQ1dZ'mۃH|Űe:ÇRMW{xHiJ =s*q];p?#JbZ{U^:TS=CgwDz%qt1d\YeM%f;`aNYǟ'gi>22Yb ?`bgʢ D=!4*t!L\{3e z3Td=,SC*8#^u H7CDz_P} :<z @  @Gx.m ,(UFib@2+QڀiQQZ]EuH'RL³;a]ik#lx n& B#TQU߀M!d&ŒovfƌøVUSSYeY躎T=if崶9k4o[?y`"W`|ŔGqm'-to]SFNo,qzuLhId߰"&V K{Phɴ/&|]qD@0ygÌrcʑZ0pePF\3@axY<Mg01K@X(B2@Bh(?B&0ytnށ<q\lq5'n ï84n:kg [t+n iiFbW D;w}vnJE:<ۃ_y|+|'j9{K_:)3{ٚ)ZM"4 7py~_)M^ pݴr>L=kXgjD'Ɏ8C&SWAgB=ё4Lt-0tCa0q8>2Q D@I '4ȜԞe4)4tkOA?ttM+K1{~&aƟݽ?qf0,yfŚn`Ku6fFKz=qE{3.oq!Okt I5Iy.>t]/g~;O@d:PCprBQ2&(ְ?tAp"HnTѬ 0Vh_a#a2FyG"$^N$Mש5ӲPcևKOOtuuqʼ8cnRW(fm()N/;Qr:ZEUB#4Rb?Bog"/{jKoazcrnYW*auE ލP&V0, lY>?0. хgp=/ ͩ3(WSexN{MF6AcLq/0aRTẇ׍t(!,)#t2ץ&.BH'<:I4_4)A.aE>ˎ; Y1u_w ix!>{& *V'.lҚځLc_67_7_C1yO<]='x( tǞxLQ Wd{Aeh`BLs [//\G rPd@RٹX u|7FY4F8* ?$kӛ8{!r7>%U+/~!reE$kgIS8N&IhURV8BV,_AU@YUV88Dy0&aNx5E_"PN)SE*شi_k!  3joX[^r̘~ܐ߭ӴDM!]Jfwu!B 0d0QJK98ϩTAPمOGa+$IBP2l#dIS4CA)EhRq],z*?:pr}p1Y=o;']R}fr`2?boR) yRUkpyEծ! ch|ƀ L:iͤ34dt/6C'i>4M:qH=MԷu 2F2w$^wIV.s^< ãgYԥko6?1L"P K(f!r!@iPHρ#ּ{hn\H"<$dže]nᆵt0Tn"i$Y3?oH45D2²LlĴ̫~u5 D$z[TD}-MΜ?ar"KkܽAtK`~}{Y 9 3r4@ Ba4h*N2E,A,VEWxxxGAu]d ձj♹'-Fﱴs9?˹nt0hi_0Bܡs\כ6Hb")d(PQ :(|a`65)qUa,;Lg{˖$Oc[vКjAK)GHk{eR?wD2M(40> sF3myO 0 AP@>ִ>rSxRQ2`XHiԴoPmDT>R)N_bICԤ\1 }#熐NqO~rs,!.3:ᮇh*@.7I]tm C9L4 SB-Ӡ6@T0D@t&]X'% =bϱ n gYq3kxV\̎"qۿҏK3 }hJ-?Oy}.ZI?q{OʝQ %D a&| 0 A(qXI6?/6Uk_q}>.#tu-g^K &? }h15ܹ3$ K<SLY+Lr Pʠtw@ EɗF|FN @*L:e,b``5D<o\(06-_ 082tJRX掘S&1,,|<;|ϡDM-p(n {捾7ٶlzz+FsSᵛ^ ޷(˜FD e:t ){D-u: $s`,`v\qtv-tJp8ŠQzG("0qE;AъѴvƢ^+cʁ! 1j5mX9s3S5%hIHzկa6\ŁWqjӵ;;_zW񫧟dr"Kixtf\=v˻#}34I)y&j=sô5CS$ B?q-(*ZgW^GEKXS)1_o}my]K>{/lF g^~}Xmm*lMТ}f95<ş8مؖ]ein"ϖt uTe;X] iH IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle_.png0000644000175000017500000000323712014170666023642 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH]lWwwv7n%@@ABU>**R RO < JQHDT +!&NS$!qjvz>vx]t{?s9+0о=fcƀ3z@ ⱜi1lĶM,njd1riccy 6R{Ca^Ckc3}K/ Ð/~1 Uynɻ" #/aqJN!$:X9e;e~뀪PUjK~kYNi;''Ѷrw/RʼŮʜǑnzZɺ cQ<:+0I:VrXՖJKO qݦ.#vcOu]\RW4Jɘ){Dtkg98nZ_5"[[j{YJz'= Ce.D#HWBO?Zw [:F1N0NЪ63P05ԫ%J 8J YW1nܠKff25k%KX㤵64[W!zJ@7!1ngr 4=¬̌Usww`f1LJK_j$TU~!BO Rh5m{o3 M$4 kBUwUoky{}-%%B`ߢ/]?b( z̈㩎\ڸ̿y沫ԶG^'џ܆\7^e&s,7Y\ sdsFJ**5Csymy{-HE**R>Jvyi 8ͷtĽ5|r=N ,2 1 Xr7㧙>$7 g(i485B1Q =,2L&Iu.kyej;x?>VE3Y~zҭdgX~Zi.m^flkEes|XAA%HC~SXU6gb3yXcNdnqv* /5|sKz2!V5OG8~uȖtu~W>8v 1q*fK&5;D⟬kLn2\.u|&6J]؎Y]mA-{.N0gZ Q4&\0f{Y e_n,5۔4͛<{ ~} =̒DDvwXT;z)t٤75юUҐI"^VX&B<ǜ7*2,,;os+EQNA<~d 8`;&cEl646*VVRl﷭LZ8q@U*$|{xQǯ:"YrC(TQԂ~ xEJg=ˆzXW+lΗM5p/߯/ԴeO݋WC77|IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_0.png0000644000175000017500000000207212014170666022674 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHKh]U:&pF$Jqp%qSBA;Dt5#AAs$X@jp${s;8{ν`ُk[Xkx֞jwՊ%UU#=(|/$ה0J*aQEf%/.?H7-: t8tru`^|9rwU'9; FYͲc uIA(V($%0J5tgRȶ;F>5 J407\! ͖ڊ:eCxTb% S%M^ $ǐ5Mَ.3 lĴ̓{ ) Q()F-[&:1B8s i!#.ۈ?avO`f*BXQ%dTcd#~<(fH%G 25w7^]Gu$d0Z 9Eh:#,tQ9}i~gyɑ($M4}L9vv>M-Ta "^hH0Fcʿ@LK E#jP#)(Vj` t;ݙ9^v *'39y9sva3m٥#LXvϴ,eH1߫x5\M}߫{!ӾW;5s:08<>Nwys/p\R0-c°I@ "  $N$^)&P;xLe!*T0Z )LʔtnQRU@ր#SX421r'q#0P*z6G qV&4Z ҇(9*[1.ֱWL) a(:S5>X_ЧAr:Q059ٌ֓74!F)vٱm(jd3 wɏ iԛ4è0<¢YBUq148箳fhq(|[~on3Pvpۡ=-U^\m#C,1- Ef&6%Kq376.Ks =EU oCSsw{5enyQYnZryM27!̳0/{xYO?Z_fFFRFYNu g`Ŵ^F7 {6Y0Al0ws40UK_Ok)=zj2&cJjqn'\U5TMCU1 j.'_!Eʽ>$z8}]X}vNg5N_zP*GIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/texte.png0000644000175000017500000000357112014170666023030 0ustar georgeskgeorgeskPNG  IHDR%-sBIT|dtEXtSoftwarewww.inkscape.org< IDATXp?K cfQK D%ai:v,V2eP#ȏVq imBQ*&!4*7BB$K.;@vw}gwwv"[blѠzEl5ɺNt|O%z3O<5sdlO7SEF?PJ|@s9+wK;ʼƠ`  "}H*%E`Q \㞜GCY@E]QOϾ=t"/m?\W!l 1g)ᙽ|_Մ.\j<@Aid 9v]zz޸ aP*KWF%N*& ԓPYs 64&2ڮ %%vSÛ:09-Ûxw/?r˅6"E_}FJKl % hny~+b\ZO+ X j@Īm|g![: 4/%Ka9ef,S˴6X{`]lG_v8Z]wO2u9^Mgm4!P/C*2 9}n fK3Fm1vc|܊Ex feZEXeC0x 1k&;`SsC=G^kӡq އy Ա2uL'<淾hmE͜vtv3chb^ad!iE md{x 궾R'C*|$ RaR64NKBeOjuzL?sƗixaYyW`? hV|mī > RGPlC*{WOSvC#9e7}S0GI`Db:q-B_I .!޶] 0n4{Z(֖4Y=?co>[H5jCUx^uOZO5Tm﷨N]r\3w(1W"% IԵ8  ͮ6cbƜԈdDv̦)s2e X;}Kl.1+0B;*Fr~;!D*ve.]{AIC.|tzҡG?7G[X[h:.>pdr>.hkSOuOUSKN3TcfՏCg Нs |B z#X+IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/point2_.png0000644000175000017500000000200312014170666023236 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH=kGݽ3&(|C@E:T ‰;CbHIa+#R90IBE R#Ptk[3wwRޝ9gs+s\~WilkoilhhUBJU\=NL06ڌ̦sc}c=cӝ}a$k׀ pRa^ >R*>*YE"_P/•:Wr81J,c#cVRdA5tfMJ#/s0-bfC8?Xy\_T- גHoEPQd& \|y\|0=5+a-cp9ph$ Vqg2r_C1Mgu?P*Ğ4vpT.h*}/J[B{^n삮#T/V/V1AL^;nn0&4W~8lf<,οۻ'# {Ǽ{oZ7vOŞ{au/E@xɗ#W+m%DM 5`_~˟{Mtrc 0Yda|w$%A0'JLTRF>JS*?#0qY"q]\+ mI8N5{pG7Ј((mtc}cZhjL3}8t8NN:FM%VEX ZoEE_<\ޅ]?ilj3s{}=0os Ky!r!aw2x9X68*:hx U*zZUzIi7srns(_$ !pVo rEk:6_Zk4oRO*c#EK0@t뺆EDazLFud$%TB]$ [ Xe֠JXPWcH|g$0]y8elI|Gxݫԩ*%؊Aą`Ғ)~ Ho >6%lH >1M<ƝG,Xutw[c,(OaQ 8Q]ȾhkUeuj:0dYׅ I Kk.KX>e+H5,oސ $t룥Ս, dY륥 TrW7=V:hjMO=:ɜIE5VYʶR~"Pۉy7/‚\: YK"̚$Q$$ ÕeXU0p.BtJNWd!$]fsh9::ho'og$(QS6t]3\zמA^^.%Fw@ol99uzZF0ɓ./O00.D͆3ߺh]6LiA6v+ݗqw%lM|ϥz#IvLb=zB0 a "`ӱC7S@=IhPPLNmw|#y7=kzNN()_}5~ajw͛aM\%'d٧"?R5ł9 TdJQD B@8I/ H.q;غmc ̜1{ޭg[5aCƑ`G]d L@\T@ bJC,++/ӧOgJώG=f /ŗ`"xul fOC+XrN0WUtsQ(ְ‚.i:;v4 ~ nVE5rJHww&w̾/i7?;7HB t `dU֒9Mbc[g٧W= ̵;:1 ׮ڻwoBd0"H|4R-l,a )Y^uǺuKEjFzkW[IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/logo_surfaces.png0000644000175000017500000011401712014170666024530 0ustar georgeskgeorgeskPNG  IHDR@fsBIT|d pHYs#ktEXtSoftwarewww.inkscape.org< IDATxwxT?{&@$*`QQQW+pbA@^w%!!B z03gwJ#{u)9kVk+?O>>,^Cٞ$88/2uT{,Y­ފ/ rZyy97o ==&x識Gy7x___x ڵkG-X|9̙3M67 ֭[QJѾ}{9rmΧzMi&رc`3gg}?>˗/|*Ξ=Kƍ={6kFݩvuֱzj _jL 4hx{{si.\Hqq1رJ:ġCh߾=eeetv˗~-[_φ .矱W^-**Xٴi#""2j(yglO#|w_n6~IMM̙3GJDD.]*\s|TVVٻwb Jĉ6e""bX]v+w}'ݺu58@eذa3CDDdڴi+%%%""rʵ^k5k 0@9"{O?T3M7$=d___2dXϞ=e̘1w)G={HϞ=eɒ%sϣy{{Vsr*{>dhGcǎw^FXMxnJ^^wk牌g6)**woرc&M/UUUڵfVTT矓ǒ%KXb{f۷3d38qR?w999>|OZZVb~/P ;vLeر6QD$//O裏͛7_rb7nO>ͱĉmzK9tfoX,""h"d˖-cb0lZ@RߊHZZl2ٸq4;#&&F[blbӔ G;v ׯ=S= Fkذ!/o6c5ŋi۶-Çם/"<쳼 0}iL@@:t>]ٳL8 .?C=j\iIX,~_3g 3g;wYFfϞmV^--bX$ @dڴi2k,[[ny}]dĈ2zh뮻lZPݥC'?L:DygaÆ=#m۶Ǎ'SOɦM$66ViݺL:U $6^zI5jL2ErrrD˳>{Æ MʤIgcǎrwuzwa|_rR ݱLOuICfMF^dDEEѽ{wRnݚX&Mٳquueʔ)DGGSVVFDDwu>(xxx0bٹs'C^^СCm2;;1cжm[W^`4bԨQ̛7|2ey[XX``ҤIjEvv6!!!ꫴiӆ ""2vXΝK@@;T^{98-[Xv-~!۷v駟U]zp} ~~~H^y>OOO6n}Ǽy.Yd\z JKKꫯؽ{72`+}k z ;_KB28<vnnnK&M]V>(Կٳ Nq(9D %?x PS &}JQmQD/=٤ C6#"WhNbͮxb#}0{7A]ݛ!4i7LOOg2}~!CS_B{ՊƏO$:oo#//䫕9Ku^h3#K$IZEj'ůTr(7me9s8n;zN UWˀ`ȟ@jj*Mx>sq70x֬Y5g 2w:_XFօҎxLJeE_vz3cN$g>yMoub/v> odwQ>_Zppo(wg؏P0>.;:^yǐm}CG(D{/X,s_ŧs pso|K5?fzٳM-KPnڵ`ʒ5*9 r<1r!܁VUjG|B=5] 9 =J-jM#++w=o= M6Ӿt,I`/;kb}\91An@ŷv߁4j 9j2Zjgʸt{.b׋#/B]999@'`栉Lw q qC(ВOvkar.ZBWk$Ӽ~o7a+:̽ώd_urrr5j ǎMbYRoהG!AU "zbJBP\;PP*>M͛2ݱ>g%1x5 -,3Luntk_֗ˉ/7DY҉h51||3ho>L\}<E?BΥp rHD9q_!~T 5gVxbހPP)("#*] |85l"NR!U'c~`e;w^=FIV151c7\pͩ[MhW%`v>&D]גMeŗ|90iT\DF+GͺX誘^C^>۷xG2|rQ)u:;pB% 78r1ma]^!'] #Tn J?퍞=womJx gK *Kj(/ؘC^ڶ"\__`ӡxu][r?GljFı)G?~ڊȯ2 1ص\m, @H1a'T#QΚJNar8Q 0jEhOrE9=>y|H,U&&ҦViqk/52`UޏbX7P({$PR/ r mEA9 ZS9TiC T.%pb So4|Zk5WٞX;N&I횶l=Po\ɿqK Jdc4nEB'c@~WF۵k/iR' @؅ pQh^fJעzُ!&Jӑm:tZOWq$! B܍fM@ xZ;7䦕*Jj0X4)'Jq6* ph)F5_/?Sb ָ`ad*J#$ ԍ: h笃FڢaRkJ !N8|j[TI Vpc3hJƌ6ࡌf#clڈW&$}vЄgi4)o'mZW.Zǿn\R Yu8 ~6Nv2qp1oBo!U`BFDb~Fn !Q!-xzK =-(obj6Q7@n\.g_WO?qhӏF-23M54Q1^3ӝW{A !ʁHPCGE$ w5Bm*=*;) /o;k.ިqП%,HO+.$;TAd~{ݧ=U)vWK}kh'2!JǛy8AOuE~Ƿ{q{Rկm;w#_-g]t< ZRRFcfEY^DdJ )'K6<$W${.~O! r&#vD,v޲4dӖD[}C*۞.3"?_Lfw*ݗS F7#VPUa;^Fd?}3%F\;@=.Z{ņR%Oڣ0J9 *J#e^(G!r>}tUgIG?;jz_eI2ODb[-[kƷ^]p&:^w//ҨI2+iK+hb6ѺZך1us_o/h{3*9`(](:uAP5ʯAT{<E$| s t澔*)3#mIo:ذͿZ6DCr䁯i,5V/euM}\iE%G}ڝI?SIh>@QiMÂH_G㯇kAȑp K$  F#PMw4okp -Rn`U嬣lVZ GahYK۷>~9[?BD7VNKQx:&}s,mqk^0&&9%O+X .ժ79xJwZ-11ZR .U X)J= LQjpdhvA` )Vx@SA$cFאTQ܆isoXk[:7#j[$U.S53~*^7KȮ[4Y2q-ǿ: DІ+ hL4Z0 bG#xf2E95."krS+f6hIШ$,PW+ւ_Rو8PiBwsK!`!o:ގ/pWG AcnfBh5iY' IZzVýVˏ%hW ߖ?ٲ0u2r5DkcyDtAy3rޙt^3ԞB[vG/T2a]VV`K_^_@7yH怋ڪ\@Sk6U~)h݁ IDATGPnS X\Pd=OjPj$k`? !>]?q:'#Wћq k)+ d_<9CQtbd#'.Ow=#~vK_P_tq/kA7>ߜ~9x/>^OZUUCZZ'k.F=<#88ƍO:#33kMEa3@DnRGplK͂lʀ4h0!oAS9RJ~P[ 7t o P(gII5Lx\!x7 %]J^nHqZ)^AzrBZkn 4\$O?QQWCCi= Zuu5)))~bcٿcAyyD^f3[,Tc Ņf3"Х ^M(<[o6mKPهU7*E k8ܚpl^BpKfF<@u;)k3 ;`3 "!Dh3cmZ*: P;!ҺUz)xJQN}H#6y 0 ΟB`f|()jb࢏ `e~}ϟfQU[(8U]umxyu׹Z_[aV,[мbtBަ} uZZk @?:4sLo<݌)> N=HDwZVs&DF-^OcLފZ+6#uE1wٶ_eTBR~|(Ͻ<[|W!.]pΙÔC>dZ"N|݁'S"8skD)IZG, Qijׁ҄QӴ9H;z<Pjb lA<0{x^V˓+D֐Tn-_a:݆JheާdOx3)Ďy x>qշsQfM, rw**PAHP#FrL & TmeBP?\7Ն?-h555|G_fmjUSPtN`mGh􎙠k.!|3Dwx^8BO0Ӏ~iCqDk'W1 9/)&hJS-cn柀@muT76RWB"Q-[ dn9GK!{(؟DpWBzEjm?x9P'}.-Ʒ{b5=H-q'BYzO ڡֿa?IJllWN!"hNjY8ᠭܰ %h-A#|@9h4FZځR14.D%*UbO@+<8UiԦ~dSA_|C3' P$aiyN~6W9c:sl1%jˢOx@&MF| 'ΧY@+fj#A+**bɬ2c3 \0(68#Dtfb4G)v;k#p̉8@t8H*C0sb-PP.Dʼ<Q?*o%*c8ub*7Q ~JXxxCEnu_QFs-z#U`9ܺ4õu4~?~DZy>;OIߚ`;i&%U$,ΰ rcKOێ+y-i2((.ͰOKCHՈH~ .IOS DkFgH 8s.*itsLDHuhG1d$!ʾL,Ɉ[oڀ!TV$TEI]'@y !!VۍQEv΃s;NX$i\~ n]syϸ LALo\V_ez+^,s)J)'ܚHT~zfPgL5BKwF8"z{bR| U<@R(7pNHD(EX]XkD)+E'nKtu5CjF@  I(=Q|#dj&@HM x E#>ρ E p ~Wx54DLY =E8*k$~2?2b16F'q.؃T(rJ+cmM~k S5?[؆Fmm˧_:Ոrss~hܥi={cDuQI9%(T f`R` V <⢸Jxw̍J1;A)7'RU5mX}tD uP~e ኁC (ivR eގae.@h9ACRi둡SHYP-:C`KԢ=E! Fj]=Gp5 FB>Cv!:sWLU&S1s^nʼn%Dh Fh'},y G6gj ^$yީ*=ďՆS%9R{N5e3SY}v(?`2@- ]!npKhkT7 uF ZpASFcðF _^ q>aL;"Z]JЊ(vY7lG\fMм’mHC< nDݨ}v)966gP߽;=lgAm]ǘ!?O0ڵGnxRr:GßNHon]X ׵Ih6Ҍ2u ?|rjſm[֘|"> QG{j}k@3ZC`FEPwaСʣ97aEtǶ@J݃Z7X_V4B]I\R &R>>`p9fpw/BЪ7auvPc'_QNFK=[0ف5~/[gs {$k-sc< /aNI#WK"& &bJɂIخ!;4SUIۦjqIAeJ6{#r2睄fOapAX tCk)B'+b)H6(w3{^;Ѻ/#X`#݄U)`8 %W#~m稼m v ڏ_[t%FzݢQ!߃Gn޵k{5Zx-CiU{^k ;#Y[l&^CHs= 9MHOk3ѯ<{=y:A{9D^ .E@*;pNx㢸ӢS,u72vWY40 N6vXmabh d 4DQ |"8o\݀e_ub'į=[i'Ў75; N$89\@[P+R |Knf`l̨=xM`9/(8_(۸{x !YBz;<>|p6O=tW+4矓 bv<;M0[,; aiga^2'^&ns80C)wLŁЄ:[9jTRh9QSWjRC%@ڣR#fTqm:[pS.ַV1hN@ߡ7Oz;_uĜģќ_Gum=J9b(UXv5DP[9C 47".*Mоs*?-3)]?: [Wx_#󅝕?}ਓP0!V 2@GŪ)3F}D؄U"9|L t x/u n`#Xi34s2 Bבס|0ڽ _QAq[?ð{Xo| *'ϭN-BFLwOj_?jYx ^6'qv -wYz㥱i4<9mE*j|%*m.FO=[x:- pG0:0wɯG,\E4QBnwmN ! >R/ d+  %JaVQ{B`tAjuupQXB|ν*Q9B~]"u0O6 QM Jj* P_9OYhօ]G4z`fZ`I1բxC-X;0,i#l-ĵ"\k}U ?!ʕ](t\ം7tXMWP6ZrQA7ǚT|kjEMjUbalK%̀(pWRoW6fHȠ ~Ծe{|S-ɴխnmș Rj7kcpkM0Ke5[d#?x$=M&tv6@Il: IЈnd5J+Kl)+uaDR `[%H_RqY@&FºKt[GefmQ17z}MahcĮq?G~=7P4a?\O{C,DXXK A᏶$ 0 ;F@U6*KtK8Ziy Zwagg^cPoL_?ZvEPEY &d SN:9c.ѣcKr)>m ڀkA3&`1+Щɥzd^0DDDP}xŒͽ(j~))09G+P ekqN?g:@FECTĨq_0|Oj|>FPo7Śk *x !8<ria:!?sPC; " hM?'9CtW; 7zrhowm apU] KOv{3Y_f ܙ%4\ Wi- 'HX2@3.JvFHCiǸ\WǕ}5( ǀPd˱.a5%gb5Gq'0:oB: G@W_ùapGNs-oWwOOObra9KPDTTCE%EA Ds&رG L✣7<߽TU]kz׻pށr{g~y+' ;ڏebKSF;R^PZFT\97_Y5?_vkYyjUV>͉IQRsVa<@D9^b@?F\\}YaV=-I0 & |}o< o+y%﵃Qpb+K~sZJ ׋`*lhQIᩂA9/pVC4dvJ*H~,s⺵O<\i Pn#&&g`j AQ])DX=I*|c}l"S!\(K"(Jg|MZT79g8E@ bC ?o)Iw_} b1c #2wu[ʱ?o]$ 4I" *6 ~O,Vg31;,$+|^!0>$zӚXɈx,~Muat< Uo:Pd3^s:lr .b{nd]` 0hFc7aXubX@w01A{زn@OB4邬~ A\ -!Bܾ全燥~ t O_KO*Y/*Y,x|OFX]Tm՘ƒQCI3SK|{ o<QiK䞏ҫO_~6i_dRTS7e(,1wUб$s IDATՠ+~5$[p ^l&B2Q#Y_6)% J(;g 8VHɺ"D2lAS (K0x> 55_$ x#[G;]lؗ2Atd{rzس ٴktWdӊ- s#J#q8"&WL!YHgِ;) a- h~-*~i'S?gVbNb=EkժqXT\B5MQ(\+. n$ lZq|t .'G쒱Q)/J5g#C)B]چ&8~^U(CDWq7"wB|㐜E(*VFhG 1IOq`j\@GE֨ɍCzǯcT6OV鍱D+D-S4͆WGy|$8GmX~W#h X)Hy!×cF0L3bh/ ,ke"a(U Ie,`ǰ]~TטTJߐMFąE`OP͆5qGC?^ƟѮ:I`пx)7n"""P8Nʄ\Źw,7.s.-b"͑a4l|濥zql",Fb"]+wB#[r ےː$dZ/I2C|{H5E ɀRaz(j<5O)ItOJ<,ѰO?cXH(rob@Fp Au}#u]RIJF\#=+z٠Qc6`8rPpOGFKOFjXdzJjIhWB.! C$ |wr]*DZ(A}1uHZ`,.Dw =>ȕ PNў|IΙ:Ǧ(l+_[n 2Hő͈74,SN > UTF^+B[Xx8ᄝI GC00,ۂA 'n .$caN<>  . fX VAny/R9L@ 8p7B3DV{vդNkG.!?Ae/0ltc/hДI]|=wRlY iYE9XHMB]9m(.sL?"HDh Zzvs:@ȍW,>)ɩn'$"bHO`͊9`6 ߸`tmS].t/< ^]06&^0ײJU-‚Xq\Ʌ%=g@B8XR3z< ۞OO}ɶQOH.Otl.Il_M킧,wF74* ÊǤ՚#`Br7PJ҅Qn %@ż/|=*B5O@ڏAp=PlL}Sxn=.nk xYFwˀΡt(z2cZq}j|eR֋tHELm_vТ_8 ܯII*<eE0T&2{(c/g  "`p+ֿAckix'c׊muo]7Ɔ0xAw^8|mPb͘rx?J+#Ql~bMPgBsP@޼mqp8S?}^,{Wx 8 u2ǝOѹiTP4Fx._~lhɶk'ÿQOJ PUCa??]$ T0[2q]47l9[2G<Qvy`ov.JNg(a %zKca@`GB.P_Pj1c;1a- 7YUu4/Ȝ(ӟ0XB@Lv:5=G0wlS&:)"#^ m:\Cnۊ^8#\gԮȐL@LkKMs uȽui[?/>]B7쇸ngGL I(NgBLhT  iYvOc<0|z\PddntRXvM58S7쁴| \÷||N$ f4P(Qlׇҡe1й\ A󅟊`[ĚjZ`y(엂0c 20evQ0j~Gpa9"t$==ua+; ۯх.B^OW yY*=~-g:a8W9 2JI`E[+%cN@ZDU*Z(W·QP<2}ߟ;O~aЧ2L<Lms8U 0f9 NWBFM0HLaTp8^~%z+(V Bv fL~ݏ 4SP( >=u9`VH=*)oOшʕ ҡC:q.Ҫ @]?<*ık>!Ջawt:-tH)H:6m{A$*|myAQPV; jśں5#pfwÒKgg8~{jU]#UgTHЯL @]\mx|BzTw ÛaP+ ?  |,ŕfh 10?8T d\7V*ZI.wI"k:Ksoφ5"%@/IDjw*@uG ? M 1ˌ/Ev@H*"9AQo&SHI/Nh}Jrfs .d߾}jhc;zf|q/' Ʈ`"IX4,8|oWagn#)͒rH:M1 aE2&Рo 3'c ] oSOPg+glEc?߀$+-$mHR+I7=Jnn.kܼy)%K.EuΝ7oRJRRRصk.O[\OLK`׊_ *y Kr$@l%_v>|Uk$k&Ԏo *rcOY(*&o+j΀  wǯ>Ép#\ {o >*Hnd•tAO F4W:A(d,4KC7Q&ê5kRV@!H  d䒝g'o+RDpBԎ"of,%k6"?a)2s|_r"yamxkǢ"1@F:ڎ0 ߹8uiоو9̌R*|"J Zt%ەL\qre-4ۡvnե9r[ңGΝ;ǦMX,j*?___6mOvm Eʇ; ~9 S֛yo> x‚= *GQ%_"OigĵQ̋jt)%N^Žs|o*td0qqMYxy< L `^8uB|ٶp< >:> `p 9Gip0xG1ɛ1ؓ}18v*1/çv{x[0ټn$0mr!X*ӢV9q=Owyy*PXM\Ƃ_%hh~@iڠ laeX|c䩓ٷߘ+W8ppGxn'&p6n[wd0ٶ""(wj^yň?~+WPF ٱc%X!IO$>y6u +#Q%.Y:󦗝"# FLT 2jKΑR„EԋШU w"K2c-fQ@:_(*r)Smu2ViMc|&gbtB>Wr<[Af|ғUs$uWi(&AvVoFShz6FzểBAHV >=kBx2N Y@zKzd|+41>BaLԜ k)pZPܪ}ˆOCѩ$%P.@Z`&A M@:|o eKrQHRSS9t/^_~8q8Yp! MFZ)Il~ݻ>@&-ѥ ㉿uN۰/?#J#_]N|JfXij/buճi$'!2"^K=yѱۏtړ0?Rο+t*U=/QNCj ukI4 ]țV͢trҲ]IMסJ-_m{Kj7WUg[TpsV/1{)W/Lt WZ~KZ~x-]DQF="uԥcǎSK_NJJ 8p;w5o^ABN!-#a b/q 5plVa60 _r?)%yA@,S>{W:mTeZin<'n"Op Y_BHҸ) cQn܀| }3^yӑ|2 @^b4T}l6C !99<233ڵ+={?Ð!C:t(+WءS(xJTD4s\ohټ-ʵQj 4j{=vi<5}^g& ,b"[LKŤٵ/Bh_'Ԣ|w%eݑtƥf/)>a]eW^Au/4|-:Fi!>-hɰz9fgp&:<';R !=]\R -Eh Rgs}_*E1<7=$>>˖~*N?U rȀApa oh4#X"P ˖ L2h~5+4I{]Yv5teGg{4scNm1WJB?Q c_Cl:b8+^ܒyXz)j& /Cd-G=#GdΜ9zjO=(uu;yO|Peب|+il+Uo-;yo|09F=-'$8wV_+Mꅐg7s'A` )вA63B(w_&A@~+gCJPP͛7t[6t+ MQqc2 eLE:> /z<DAY TcG{0b'tf3$Ig .C9Ջ׸u7fy6|oOc…iKVZ1qwϤ0Eo'5J-/vn4jdKo%صaUl]ƲGe~)%GxtlyΙ0D&5S k:^ܕԋBNv7{V@FPxQzۃ7n̍ 9NMRd5#uś$߂KAJƀI4,*1{WO^:fg_s+8vKns[bzmJ|{ZŜQkLx6BMOڼؕFdW:駒nnXɞ3IOCvp1V^h$00Τ {`98޸0:XaLK+4|RB5^M$L1 '5Ac_{l=ALՏ1n&pm1\*vJ~RhINS%䦣t SLvI'$"ǣJBѱ{MwFuk8=dpzOY7WR§ H1n]NJRž-yl?U Db.1Ҟ ,wPk<LUM.#a͊eL8wK۶mܥ 7Ş_g¼Tȕp[@4h? ZAkq!~iUDRp!ۯ9<T(^}&εZ6?5hbʭ\.܎gߥU3cQBH+\ [xp*&:NMWSQ4 IDATtBߺ.)Sq5TDUutMGS%y'$|:ǓrH.9"\nGУ"c(t]b6Al9h(&h &h$[XB+jF'D݉] Z7XDoF] L/OJXXOsՌzeD˷{\0Gʒ8.fzkRH:bxk>4NFFB`ɿٽW6݀7x6TTp`2 4: ǧv[; :<c^ϩiL쫽F7Ou]Sm!Y Ħue{VB<.bR7hz=/J]qDHtxiZ8]nJd0?*nxft8]Nݭq>)UQ5H T [E}rzW~'>AJI\vvqrv!vgHtrUU]P=U)'.?yxp?;Tyq1έEz̤ XAӹ=u9Ǜ#۾|yi.Ț#'#Lc5QQ^^,'Md'|e.zz'zNأI.W)-Y;3a\fw=1T?'h4|fDUc˻V?c20ELQ%=ÿ2=*v*~GaɊ-#ы\RT!*΋iMFHd $3/!A@)w.L T2/᷽($C)fF )0$J#@R9[y(^jXjtZw4`sbwM,۶.*dJ!Av=2(ąݐx)˩ `W]HMiU5L4flsQđl۟µB\N[ъNaL~(=lAUq.x ]GR/G_cRH*Rti<Yn7˭rs0+i\Q`XaUC@H4UR&'INc(zy~n|`jɀJUcbdlqʑ [BQ FfDr $XLSil۶6mtܙΝ;cR֯K]f~FOi IX:߾z.]3Sݚe L &/>,\.am^Rᦟ{"F.K8/_# AkX<fPL,߿bOH)qiŮ`jf|:dQ@8GUq Ku:4 gƘ=|Mӑ:XPTeQBQNH',6o2|O)R1+ ]!Osb5EAː4C+&5mw픂HdV*.a0p@*a@deΉtdspgq3Yu-NčadUO.v}/deuL{]R!b(?X. ;s$oG̩JXotYl4N~u:ГpcpNz+95!O|JK*UNha$X? =O⽙Wޔ .'LٌˣtazG҈ h]?V+tH 3jDQo#DqDxRGqC]QdW#)A$?Iq{t\.Э\CPKԊk"뮀.ੁP \{RLVPGfdڴ;v~թ Ga_+(O`An+`[Q=ƗV=֛7?S|3wP_u: ȄuI7m(&_+V#řŔryK17yGO2"Tiz:&&,'z}*ѥDPP//<׉myyXZ QxT.ͻ 5cVV ?8QtxTOP0ya$+JUBסȮVunW/h=FӭLjbڽTn4͈ צKPb]Pt T.ԅ>8/a0%H&Mzn-ZcWHGlV"t-<ڋ֝f!z Wg4w1_.x'5>(ӛPvoya7xA0y9CQ:wk\$FY哗Q50/_oZۆ[1Yf /BDe{kKHБtlVmRɊɤ`Je6/E!0܊j(E+5bȐ!tjĮwH? hr'BX z5̎khٲ?_Q7nLÆXq25n';'4jc[/{6/ cgUк?=Itxxqf5tԄWb_7p Z5jC!@t;^lgG0CQاMJ#N͝4;)NU=9{gml@gpkn萼j@g龊~$HvQ(4C)۲{fu}6l<]jdM;S Lq=\zL]čߘ@/J@7PhNeɢzQ(Z68"}̂B >8zyɈ8}:ڹkΦ-b[l900FH M[v@4 VceT44N=JՁfJoq]{s s1ji Bs^dܣ{&8\z:Y˹QCr9hBi1tu0M,TO"Ɍ0aƠKi_it%a#e n20:' rء,(P,uv%2\z.D%:GáqJ.^)c);w i|T<}/ 7]F+ב-@<5N:;V'sN=6l r8~N.]@ bkJ-CoRmow43 $RhZLRVgP(lڝl~W2+$? ?7@.?q|vudJz&Cմ@v ϱ|rfpR 8jC_+ 4n| -ԯJqM21»k^GOC#1w'bz4֮]̱ /oE2қqq.[wغ+E*9@8>!fȄ&`Ke($n \92O0cC{ЙSo>ǖmhJChXufқn<]ÒX4to[- G sÐ92DbDeM;: C2ꮄ[nb*[)%x ?^/\QI.添Xp{ >|FzN'E1bTgf}2飨z; ,ghL9,ٴT֥qk]m2S;$D(;(ӱGn40 hN/ٍۃۍr=-f4 ROy;,~9^&͙6|11t'#u0l.%%1 e,SDz%S`+q%EV`D7HmiJ8F;p 'NZ?3Ni+%V$x&Ȣ8S껰BK%y6*u߸ufc+~zh8?ͿS'RAƮ$ۻI<ۚӸ(S_ɉU J#>7psa5>^o2p|3YYYxY+?pRZ6rYStt0$Vr@¥|硲YpjC1pƖE)4ԐBj]K iH = @2R`X]dc؈P5X#W:A~3Yb0W9yA쯮xŌY|j{uV='DDéq_?og $ڙ#eC[7]YNt8.ҿ(0K8^4D3"H+B6V׷}W^cGWZ1dkK !CF3214Ŋo݆L)c`@mݝ{[&Wzh0%.1Mi L d ,K'd.dqܤfHxC\IDAT( =4CDLqHx1}:gШخ;?/͉dw~P97}NYo6AnHZ;( 7oeǘvtpEw: ;M9d}\viܙ"""c*AsJy{]d\?TNt*sb1X}k6M#]HέHP>|8cƌf͚X@yÔ9zfHY $W>/cקԠd ԇ[hceCh>-p>m mo|||?!3 A&Jxt9H($irf=-DvHi ]OhRCAUXW7 ehL:(>/lZDi}t V.1` ےI,b VRDmnBP0`QA;Lt8GT-?\op΅:/BPyp]~~{z}s];2CڝsS?~7՜~gk[,fr343Bmp)+wpkm,?(-F5%(Wᤝ| `FShbQ=ZSK˲)2l~n[M tuv#lk?A#CBNؑ)czfh aGvqJ@zAW | Νx@wU˃>R <dRƞXFkQ0{Mh `Ld/ZlR18Ͻ(RsgBjX8nǞO,(L"%t05P/%% \0%zCÔ԰t CfS"KVe2tH"H*FL"=@$).PbQZd lKyh{X4440j(ln[5̀Sf*9Oesv.Jrc>%~﹝4zYN}w԰’hAثXAsS2>p9~"]MwHZoJ?mg!1˻D2}κfd7GI)|,?.,sż^ ?,Z\ߧ"(8"AUQ)E ,jk Cۂ(2q(0D,JbB6bG@F٩dc;ګsi~BBcUCxM*}4\uDEQ*CJ9렡}PRR{vroH~=$(:x^4 'MG=nj9?lC )i1馎e )tĢSdA[" )u-;Q)@ uJn:&?Ԃ1fk4upXǒzns~ww 5N>ޜ|N0qD\y|FoUdYȋL~?d\Ј] !0; nNšS90LIE EwҥŴt.|XC?WhB hfŽj )Qj ^̧IUt*~oIVEBbY+7Ú6' =|{z]+XjNj6+\yw0M/"rXmA0h6uU.ztvgi؞uFwKv@Pe#۶?}"ZGFH?Xg};K_!p@Ȝ#PbLSp$"Bi9 fJ3$)*p>hoo'L2xPaZwp\&,7ѝ%v"rNvo MTRfNR^}4|%ْXckt!׾̦nLC Tc)2`MeQ?8:~QTTOVOg{K J-K/HӴX ή66bP{z&a:u~Q"(Ha|ߠ(n + !r-_t )ؚNSUz9-khR-2?3d2I8X  ɡP^ӴC6e͚5L4===DѿuQJa?1.aGޝbKO )|tHt} ,1F<Etzvˠ32T^m2шAHxgyH "`WGTF IIKKQK{B:F!Bjc:ا3u8䓩dĈq{y⋹k7^m~.?~eM 3mK'6ZЁDB:a[b[iPQ&b XR1 6G `:)DN<0a+l+wv0;J#د+xw3 'yCC_/ISS_~yȑAFnFJJJ83jwYW_};;3O>SqΜ9744paQXd4iGqDMMMMm<瑩H$ү͒%K<)%Fʟs]:nv݅-D%yT VRDsc4x#P{ ;$XfYs4((ޝp"aA HrlCRNUA i'f 7q7=ۛ&NǏϷIJKKIR,^3g?k7 }<\ve|_7CLc\t)+W_`ӦMZ_=_x1TӧkNaaa~W]uuuuTWWd>%(5mR?WXf3Fj|ؓ R}҈ruUP"89S A]khhMcbJzS<¦(M=VovCSJ1odƍǽڤiVXݫ<#<\>W{*w?Lyy5F֯_?oS^^ޏ\lRJ~:s჊FLӃi`]M{d[6l(4wː0Bhǃ=h?W1Ï~#U]]QJ)5k,ͷ>}׿ޯ+Rp 8~;̦]ձ3۲췆;wkբE… \0ѵ~|%P[1SSi\TaY9+>nG)3g _~8;6 {A4Yͷ =^vd/#6iZ!;v4Q+SS "z%i?XP^AڋO'[YV%,g@uJ\۞, f'0~!@ٽ 0E(]@ ZS%,Pܿ׊o|ѳ@J4 o{:?ѓGλj[[Z g/ 4k-f`PJ`Ř"!4eVG |WİZ"ֲQ]rxg#< BE 4oUv%'L;q!=쵱ʱ.w픏UIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cercle2.png0000644000175000017500000000321612014170666023212 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org< IDATHol?v8!&i4"V:"nZUlЄix{1 Q$FG'!-(D?&ii#`8qَ^GڏwQwxחR@Iba- q/WiZ`` v3صt7wFj}-=E3PUQC kU~K3%¾T)3_<ʛi5 oޖ^+n"]NiGUҨ3(km!cG#^3tfNLJw8z-s(yac2FcP`0zl!\nk_a6{ S z>vk#2,s;<?Dx8 nűg2fPkN zbe譴K3ͧk,ZHp_(7kZIe;Uppe;קb.wL>R!]C"FG{ho|0BB .NIs5sPRyI+!p=4h1zh L]x^e.1Bb+.^(_b/?FLZ _vOgr']#/XIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/droite2.png0000644000175000017500000000264412014170666023247 0ustar georgeskgeorgeskPNG  IHDR# ՄsBIT|dtEXtSoftwarewww.inkscape.org<6IDATHklUwn ZR,bH(,4ZCB %H0%5Q >b0`U`@(RT MxXZ(m;3; ~8sg2B)WZZ4! ! x_44Ď:i٘Q+xZi9̨l8Օ̶: iGK^АC> |G0CĊZѨ9+&%>Q>(8ׂp@P@ R$B:PҎςJ 8\[ۛK~ CR/w2z.=,iL @P@D)ZĶrcMJ?mU5 `D]IKݻ 厾l;/t x tɗ7K~]Kx8)hld5?oe0*Ê#1-;c'5e <tEy\gUQ>Mw\O[h56Z4> ̞MtFRHWfg{!eRat'=Rn66vµwKy-Y3{wMoH'zr{;-&cB eZV#i!/G98^ǗP2a+0|:smN?M杔n3̆idjIJaO 7n;w^!d˼(OޚVPľ&>Æ&[f{sV:rrC@ʘQEtmGP0̩$=C61ba굨 -ռ~(˲24%qɇӁXvB6+KRg.,4|L*c9u Y>T<206 CGvy׃#w\MDjƦGKhƽ'0:7f14 cȌHSp7\^;#2F uODlwX<.1/k|}f?7|B?@ITAfN wt7X S1# Iuk܊:/sSP])M@dUK[L(b _/e倚(:9wTYd9 vpPJ(R EϓE(lQ 3AGa;jQųMVy&hFVIS_(u.eRTWii-lm]4C>4;->kZq3jP6k9w_tM^IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/angle_.png0000644000175000017500000000237712014170666023127 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<|IDATH_UE?3;uAqR0v%ч\2, E(|G}jՃ "Y/iZ.)`*z=̽ήso7~c oH9QWy#Jy5qQUJj0Ahݠ}>nֺq_WHg(PK- I9/:q7Ć(D6W$u Ƥo@J ۲, ?hv9Rbە26 0-ZqeN:}-F(;RjlǶ9'f.o"^DjؤCY 69ş/ūu#PYW$URt nC-9"8mL9!9\={]d"__A jZ&qG]C*oʪ@Of#5> CmXVQ)7^ª,k]0֙ p7j8gD vXE.vzh(|Cd?_RR ZRo ~:Υ=lEh6/ py O<޷yezӬyeϝdৃ̼zSfn!ͷeŮ/,bדﳸo)J+Vs,rqr,*R%0>eg48y_MCL%ضjQ+$h{sh#˔:;o/J"=l# ,+ Ϝ[?glfi5anzpZIek~i{غ96-܀N0A6$Fcu"q)n ō͜ fsh0=kSS<[@7R`~Y(̣&>RZX;ޛo e oW#H[zeis:uPkvtO*з]u]!51aIh:(-]jau8 T*ՇDz>fSgi8 fPJ&t^j 5`PHIEQ$(l/ yI+y ))`Bc۾o::TJ8pd#=jXp/nKvXIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/angle_oriente.png0000644000175000017500000000241312014170666024504 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHTU?7jiielnIE5,R! QJZY!ZAIZЮʪdCweݙyo13of[\=s]a] [mpl5'a[ [ [ !|\O9z6ӛ}w@n:V`&PK}s@l;k;H2t$IAL(_ (t11)Er>\o @R0zDޠĉPrn `sF pl9s\O̱&ZXLV A #tɷMD!씒\K{MI&T!CEro;U ךzk)d Sq8MSgGq1$),Y0JbqΎxi} 3g8z$PRy5&0PAo?+y8q?ΔQc"WB&F.n͈o2~]~I)TܒcX)Vrڶ0i$}!+SU,rWl@/c=hيks+cR|=e&[@HeYH Oú[i̽lyrƑN\xq^\VAe (Up]z29`qbqVZ1:WrUGU@f>2ee?h 5ǖb$k}|W05O2r2 aKg|y Lǚu ]I# :@DM]şFD<ݧ,4•AUqpJE˶ƱsDIAOo~,_s=z~ fୟ)ʧK{.8Kqq2K\ Ox(+`@@yW/w0N.pѭ;.p-g ^J;8:$iB |@CVAC%ōbhL'`&ҔD&i`ka "s9"f-mmґ`?HBG8P.Uplƿ>"4E7PXfE~͊}e'O731 E~mtոO-ktpx a NM2bޡ/3OPgX1oV\>XnC ;/mGæ7^C[QBP* N)[̭LNRlSc/"Lv2-J~.Oon`ѻCu8[+l'aZqw'=[xaH ; y-857;^z x+Kx6[|+Kx*=r鶢xPCL6`Λ"QmE!aF4_``BU(J^yFa[`M0Kg`u@T)a0+az9v"zL<3b"?"PZ$P=iJg0/2o\K ߰[p*85@}$;qv)K*pW,! .c#LڗEMMrS O&E# Qa^a*u3)_aE.jr $zJv? $Ro0H8I0A*grrbXRS!h4؁0` q 4ݼTT$dfm,yXUꞪ79yhCܰI.+K\1HWΤ$EwzS#v\6?+p{ kCa%X-t"Dڐ+(J%MBAh`1ɫow0,H޷ge`q UL&$ssR L , Jb&SYU6S ܽi  l52 Ź:$_P4/($%e l.)Jؠ@0y*5jX>:$=S)USc]nVqc芹$9e3zPytG5>P!~]ݻ kJ$$i'zφo'tE5ֻټǟ&gO,LIV\UT6#wmܒ%bͺ/lNQ ٽK} Pϻ`L[QO{n]IHȚ2jkRGGet<+&.-BB^>w.CPPnQn7 1ǽ;6cWLƯdtN6rX{׺]``[WuciOoGD\s*0+P, vIʕlN~wb23NiFuf~>yp4}^2Op0v{TOȎPڿ2'UIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/demidroite_.png0000644000175000017500000000250712014170666024161 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH]lU;33;lV6P)P0h#?kQM|E_/‹/hA>(Q$` " $`@(PڲtgٝF/3s ϾX&<`2˲-23XeDes[t1)!9{羻 wÖ%f^Z6 zʘqӴ;LB!A%[19*@X蚎躆{Hҹ^:DũahF*=2TDLRUm7d,tڬFZ!Y3E /z  c7`FQI" 8=˰gs+ `FfBh1s^iy |eLBkZG3mi]kULW§H~zɯ-3i7}@C SQ$%kU꿦hEF#ׅ·\, ѓYrN<_V'COb{o0#}Ut@懙9V*0m>.2*w+ntNj_Ӣ0u1zv6TarV, \JUOppYzYT=HZs9xr& 9iׄ80e"rFFص?:\";]!.R)(=]7*7jbXb]`uf㪟{SbA)]k9(s&% +~.2s ;uf3 7K[;SsB{I@E+33^9m|`0Bm~KH6,1q0+_lĢyl_@&V'C$nCQ;'W&d(=(Cj23 VRXy{CbO4\~l<2x 3ˉ?9b)%X@s^?:YkZKgY-5k@R);C74ssU9"DbnyMs8nǀGN2!ܔ㛵6wb9ZLr;fϓAL\]xҨ<9ƉdX2goOEJA@5E  D쵧:nD`P(ݰei;2x <9w]>$  @}%( IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_z.png0000644000175000017500000000214112014170666023003 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHMhUw̝ɼTօB ĝ 6%4 P(~PPP!]7-f! ABTHb5bqe}Ѿw7s]7/q {3~Ͻsk-/}Ah.™& "V-!(uM6 ƌl3h&ƬkcVOM<֦-`&61l)g8bMdXRM-rp|`bs`D> hhc}`x<$ -fKCa;if.&E8 ˰R/ 9z(M͡m0YM4ҫ$E/%%HѰ^✩~қtX>{{):&/j5nO>,cӘ/Bwl)Yu*4ɠ܊_W|ѵ.67`/,J_@cئE1p2Xrw{)aq&" `B8-$@=l&0RѽB: .Hm/Be) T{FUB˙ovxi7[R?|$,oS`UXk9yam*V8&o}[?~m̎6 =GNl2t= L_ҙIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/demiplan.png0000644000175000017500000000272012014170666023463 0ustar georgeskgeorgeskPNG  IHDR# ՄsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<MIDATH{lE?3w{WzRh+I GF$F`@jTH$bتĠTKc-ܵܵҪf};;ܘZnL1E8ɕ`e)67M+DWBh@t:wB@? _)_6|@ ,Rf+{cj}".:Ʊb̌ts 6sѵw2s*JIWLMhOTǫ3sky3 UbX8x4sw`K+w-8{i8L ([EDവzx Gf\7NGʱ BƲLaJ%Ⱦ^wwpKQW`A] R QHCT[;_q/WlxLv 0c8CYzxH)Bؖ6Rz=9\mt ;oy#ض1{wo?м]2YI% OM\)O`#2u}UpZ-Y)ضAT cZ&W,?I/ͲWy!jfN  BJR >G>Xrڗwmɜ3~\sr, ̀087~ⱍo;τd'S Z^GlPפЉfF^mm 疗qS} 1% "@[1 ÈtRL"A٧y|[;3khT^N@y~))ᡆr:z:qu՜WVJ/4JRD< "`CB"l 1Ha苄ٕvbgf+YS+cVe sꄜC`ưkṟ qG67 I`@}+ƻ(ȃ_$ldž]-J}B?>00'.qMED-0BcL.r?$9$P $R&&؋ow!p8,t׎ƬG!ÒKKbY%6@h3th[Zդ٬Xx cmnZ|ҵ?ltcxL]d |?^V??{KV(qIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/gomme_.png0000644000175000017500000000321612014170666023136 0ustar georgeskgeorgeskPNG  IHDR"ksBIT|dtEXtSoftwarewww.inkscape.org< IDATH[lW9s߫F6 ($i-BEP5/T$ċTU@*t RTHE "(md!4E!I;^;3c?w9)*=9i4?fN7S膉fm4эa]u:muµcO:'׮?3;w*"v=8<rMB\;cˁ-ݚR nlUhx^3D@H=ZRil0S}.q2Vq#Y~Y(a>B7mc&!|d;!!kqb8ka1s B!';Sat#x-k$i+)ƺrv BiasW3$25Bײ]:.%56D7݊$c IQva=_@RYG]+o̭HkW=3,Yj{&놐JtW}P,K<9H8vb255K&[^­^Nn5Hf.d-px }" Eq-h$n[[7!=#(K $)йWB+*?A0j!C>H{t߶] Y@} N `un~V>3OθXtA =x8TC* LYҖ~hGyTQiq J"88< RD׸~ĉx ;NMz;;s0 diZ1jZM.$6Mƒ)ϴt):2j U)zU}J:Is%}F}@PE"y0b](]8A$ 0> Dѧc˲d]H0SQ(_ @ZZ5ֈ!Ie:0۳6d?Iϐ$:wA>o}Iwu gd|ar4[_bFk]@ ( ?̺N[QIѲNnHOn O߼؋ʘtO^T"vԭ2%X_mn5{m?y֣L^ušz$r+::PPYd ܵ*Bx&>VakKnӈ}!?,x&H;$,ª%Wp<WՀXP>v?Eg̿I8U!D#[^Avi}o@mo &*A@nY$Df^~ Lķc;Hǻ-s66=? ۈא$kvyo't:$YtG6B޶gB8E _dVMÜ}QXD͆M53K{a W÷ʥN\U,Vm5r+&i+/08zәVǭ8o5cL8*8]qU|Yŷ%TE9q=Sq&EJvWfUkݯLb>fUGTǃ5rlS3nkz1ՌV*~{Oc(z% H5E-AcE\|9oN?'X$CfwJ`t`j܇#H͟OY1KV[lۑC^o(;4>N'_B3¯8}P(˯bUuHu%w߀mn.xLM>V,[C} ׃*O?&0 leIKV&ᐁ1@%'%ꏙcΘ=B)ى;/I}ksll. ޙ[&]~{h\yGrZ%p1T(Cԁh9=sgFEQ1 _0/s/IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/annuler3_.png0000644000175000017500000000235012014170666023557 0ustar georgeskgeorgeskPNG  IHDR%-sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<eIDATX՘]oUgf;JE ! Q@(@, (1 $paL^ BĂV4Β̜En 9o-Rzн-;"6 )ʏžJ_9K _erGk7ظTl2o}mszne%d Ub6IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/retablir.png0000644000175000017500000000255312014170666023502 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IDATHKoW3sǯMBBcMBRZJ$@i-EY nak|VHBaQ"ށBhI<3#Yx[sy٭_F5#y"'hFjDӍTH"QjzLo,,U᫣N29]SAJB]GQ|5J|ǃ'zY&el|#ζM:勃E98+n 5-,N7?saW3y]e#\n':Bu[\U;%jMS[x*p d7۹F1Ts hLT-T?I5q@efu+dz pl<^\+Lȣ&..jfH{*,Ε::M,#s~j)⨆9H͙i]H0W,*EhsjT#3_kNsֆ;eۛ1]A`+R?_G_M]7rc;z8~=]&ҭT&柵mEﶺc酘%Kܟft#l+w ,+=I-Jf~ŵ[8*v{wmDHa4Ve;S (\l&R=mķz=倔[Bj[fm^Ӧ`Cm9LcvnH4ⰹlثfX)dZX!o7"iֿ0Z _YqM3.1!peR-׌Ⱦ$?B$¯ E<0 ot,˃ړ5FvdR 'u%Wu^^8{h&IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cercle_.png0000644000175000017500000000317712014170666023275 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHmWyۗ/[u_Yb*l(֊oM IScb&J(Դ ƖֲKSe}s9~sdry3s9 UL|+] O\J\KW 7?H2<*HPm,}kwa 0Oy7**r׃1BԪY3\{r? }}lVP&C-,f9.Ue"vm;,A]3>j DXF)%;,o.N.!X9ڃ8S z;(XqC#A#yxSoމby)Ĝ ΰ6_ Tm vzQv4ꖣfy2iGCGWMp y۸D|qwNt#6֚Yr:2VAR>cBy *ׄ=¨9%9%_|cHTK(a!B)Et.`\B8ͪm%|C1i̥QtZE"U(}E8 Ra՜@^l^tmƢsrVb(6~!%qQJQzE4*W%@bU'r_*/G֚Oc{w[}BBr_yC p>#ʅtc>: $y&IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/annuler.png0000644000175000017500000000250712014170666023341 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IDATHMd3!; A W#KV9=,QO^=Oo+x@~'IUϞhIUU5=st%eeR5M*A4WM\iT,t֙0? &>rSՁHfUU2N%rzxG;;G!K2J"$+ԪӸQ18AD DQFI].kptEA P!Ri)A{y:,65, 0){rHT.al$R)>X+ >yXha@9{tDQ[q&Pa+VݘEWTk6sJ$\׏}A#>zx޴1GvpDQGk5f.-2}՚v3(ItRk[}xcY#.=y<3O)uIRLxdBFu>wF,Ir[AMCڡ$@9zhc<(6Rl<7d&Rl9s.MʖPU [b ʚ}TRAbh#yfmAێ u pLlnsr 1lf$_)V͍wHPd9!=1*up]"!^',@݅1tq5ˠ)+lޖRA5Sˏs)޵^s0+t҈ V- I>Ǧ`H)[ ʫwdeص%͢eE+z7JL_\{BW",_>&W#BxGwac9b9ؾ2X(Ezۊ9ڴ˃_Jq G+RЁxLMWAp⡚U@ aED)R`~=Q^EY~dNh+<0aWI$S!E;b20L*Vo%8 fG$YƱm؝׷~GTEV$7r<ΊX^yO}j `t'_hogI`lmldѵIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/triangle_rectangle.png0000644000175000017500000000340212014170666025521 0ustar georgeskgeorgeskPNG  IHDRVgsBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHŗilUs}+86ޗ0("FADEiF$JHHM+UV*" .JKnDT1T8!0čw C4;g?3W#!._m~Jxtoyt%1 )1Lô1Ɔ)1M6,cÔ ~aז8o.^?+=oc%'.f"Cx"aGNIFG Ex޹һwGMP=]iՎi̬\ⲳd~)0-Ѐ @ZGii\=qj~ |]Ӳʁ* X]t%^+jYw44,`:` 4 OJ8L`ۭ_2KaQU%>iC [mتsy>zA W 4޹ xA|9VBfgs (*ֳd-wUTQ$đA{v3uëGCA-+ֳ`9PXH9W?'Zoosoay{9{lO:M 4Wm$#ﮪWIr2KZLTQS_e49sc?6_jcen;j6l5^{X|$%F&-REUiL\gtfƒJܚJ ;U&M(ܜ q-tΡ)nN8-Ũ*8 t1QPX"Ӛ-QEU*X/urq(JZ[Q_Z22# &=_h¢,;ѧO3ȣLb.ha/jXxn<)M(B( "p Yl.dSY%>ŭIƨxo WoH1{_ךԴ;VUnWM#dh|}¶SRWQ~ԱGoPb7<|?pgHkْ#"pDb {x)9}h4e_kdd"ıUnТx-k%G$kSWͅOSY[O~ÿc76[_Cc܇v:K&3@{+vq/Be˗RR^ʳZxӏz`?}uP 0 ,MiKf"v--s,H~q '?Ŕ^MgO6lBSf`VgW)vWlF>Q"oLGHn&KO2¾cAd:%s8M #4o"|͵6,U;)M/`&;*)R©H3!vt4Oǫ)ý$uvW n8اL/\å)(B`Itm|N]!atB9H`K+K8 ;C-GJW.Yaq06x tK< IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/losange_.png0000644000175000017500000000335512014170666023466 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<jIDATH[Lsw]vY am qS+4խ\u*rU塕*)UM_>DQʊURc&JK$Jjp X.3;;}&yh43߷?' cVTU;*z݊5UCQt4EGQ44UGUŴ2eeXf2 L+6-cȲ28_["+~ ׷`@O^TTEV=(*wr ޭ0p($H,IiWM蒁@J $ I.+DdP2 WDh5rP!p\4UL)xcM!7 Fϳkl&c@7mA$1ހw; D_ܡ,ѕeji1\[/]D/ i.C |aR#|\Fr*:x82.-Uoaansw ]ېqEthV,y @AK=y 979f#ldg? n6cupF@\Gb A>wݭZO+ζur%$!z:7*y7)a>+Z?`]-K}ԕ~>>!Eg8u{Ҋ 8%(U`śeQgp %I/w(jV\sl_)hB-4:x4h5D\oʃ/'-{Y9lc )CEG}@m qK-r! ݧƉmgqmw9|2'S8eE+iBj*IH4 iHT=`4=@ZT|{VOQnN(r:4{hYWes68jbGu+[[Ȗ,d9^h>]fe"EQqՉ8N$3tO,~o,!k@@ M@kIŷr_yP;ٜ@s ` pge9Ux %}8mtlu]{ykDR friz5ZĚX2_2;ϝ~ s}T'Tkȥpa[=}sO!i*W;t&iy",\`k=| n4 ccWys3`Oz~|2?+b@0 :{.7زxe]k\$)!?اi| t0?ۺ[e(5i57'i,Xc;*ŵMfPdC2-ދ]H;f~?৿eqr:òܜc;XcXcVj4 ||Tk((R9?N_+ pWc+~ \ 0p e[mecMGDaWTtUT:&) L0LT) P@y&pHa4CG2Ō@{!gYjyQU07݂ғ6ܱ`X3TYtDml 7j_JE<&u0 n}2c >3gS%vePx"/:T?Y:PXa8Hӂ2]9_%# lݬ9drqy@iykSf9c=ݬIU\z+"^˔EZnV[/u%b!s>qVV˺ۖ _,B6#ʧ&0xϩ[fQBeBFÛ_@c 8[4zMO[KL?S=%o%a|쳟"oN7kp )ID, g8bCJ~ y8ZB4Pכ7&mKgoU5o9nS".Vʔ<.WhbgtEsB< c}ވ2oaC0mcy L{\ĴhdK/afj\-ƭIBٞ4*y* Zk6rO۶3`cw@yN'k/ ' TqK!: 'IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_ouv.png0000644000175000017500000000177312014170666023355 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHOhE?gͦ AA0 ,H4zEl (RPP RăA & )7owݷov d`|7ߌpǿQƨucT1 )LQH3 ֦qSM8ݵ6ݱ6z# }2ր>'o `//[Iq(+5Db9(ʁ@KP$vlM`I)8C H%f>_~޾Lx? JEXSWg _שe% =۟]ӭ3ª ~ aH‹Nؿ#lI?Οןo5KGGS@(vdY6KD-)Qpc&iWqRY"jFO@b< N0BDHoQgCi%얛7,^ >]'7jL8k(Xˋ"^~l |jjYG)ÓqM4f7#8؇7_!tPpj'_b$i7qZ^O-6R^ A [hwXj`XZh˛|j[Wyvt4B+ O[f6A)P lαdmZ{Xʮw=#e[9{ˑQwQKs^=/\:/޺>\~$O`¥s綋g#RIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/symetrie_centrale_.png0000644000175000017500000000237412014170666025554 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<yIDATHˏEU}Ucq`  4D1Lx,\&dM$&&Fc\h2D11A'r3rV]nw3*S}wT3|/p Xu1:&B痎Ц3M4eLbGG_~zH}'.fp)@qKz$ы{@-7lr'J:((6mDcp".Pr<%dG]/9;hQ@l6)wG !I@NEAX[W!Mſy{A1ѲX}LJOR!9^>ul*krp[6[G+k5*q=CBLH\Ft| @UV|[P!%&iVߘe3 oO"%=ө X5Uyu(OdaSuTIW.QqyvE.zA<~I)*R6xK۽{&7"k4II͠oe[^tI`ŎJ%㘸'T%1Z:*P2ë2 ~0Gu0ƒ Gm3IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/parallelogramme.png0000644000175000017500000000322412014170666025036 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHklw"򅪭"PQK['U$R) C041&~`lcc|؇xJi4;gΜ9ήPJiYrmu%c!Kö$% e϶$B8=|܂cd⼃v|ks9+%ֶ#]j?w|/xq,ճ l!=$ OJ h R LNRi8UkhȒ/CC3Vi@k޲t^ޛu7brwtAҘ.1˔$S)LOJ@@,cZjF熻x[–fy43L64O7@)@$i3ә>f&R|tB 'o??LʕH!SR4@57D:6v=<MĴCIli70 偆!v /~|(=KVZ†ɑh,_iH JSu:DUT@Y)}=Bg,X~`Ò CM]CߓCZ7r)3mvTf'&J b+%RfPݗQ'J"SVarV;Fd %jy?p~dީz~mDͺY ymE$u)ƴB݊M]Dz(!R[Mm+pdp')Viߟ~Uy ]5^ԆmnסڌS?I`G[h:0 i|PL)H"oR7=a&ӃDk>c?v}S yV/dε!NwTţS~IlJ ˷p O%8ΙϡDm8fjN>aḙҾG[oo~ց{غk[ضj#FG'w'V-ɔN\chjaȚ|<럞#izv<@I(Q]Qx[["PuKhy. P`mY],4t<4M`;M8L?1ʯ+Ef*6 <Wŏ>chd-6lG9A9NDF/{uyťmbLͺ+m:l\ӄN{_$x*#]=L'ӵ@q`Fq>x{\ki y )wz Ymi&$Ǟt8дLI6] !q 7_Rc[ےՇNJ_~:3\t\oC-;D}!ےO,ɶdm";o9{+_l_}fIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_x.png0000644000175000017500000000217712014170666023012 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHME]kŀ",,^J=AzX K0E lЃ=bbȇ35]?jr05WUZc8rzRgZ*h^@Z* |BPHDZպ&fWǫ+ˇg50 αkǿ| @zZɖ KN"0Q1Io&D.D:ވv%`R=ٖH@TEЉ\``\N(Ɔ&}SьPJ s.0p 46EliJ7f\r q2*:Dnƿmw(U"@k,dA+ZrlόLj# <84XM-j Imґ#ŕkOA~3!{c󏠟|iRNAT) q_9q֑plKfe7#Ȋkpxe8!ӈV #ږpԖRrBn9m+o""wԩd+Ƶ)=iֳ@b6J\E[jN.|sąKȉ]CZ$IqT^ªaz} '+bvz1n"y>~hG?8Cﱽ"n/S:cY =y?aKGDoLk{fՋ"K:*M)-/:@/4vd닓C^ /ofTJ3zJRjJÁI-ۋעj;<6'S6+kՋyd#k@zci&QJҤMXfG[Tp3HkaSTم."\u`v3TY*൉Q a `Ԙɨ1&4MX@R# E+ `XΞ UiaYߪ}O|]iQ;tm3: L/_7FEIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/fleche3.png0000644000175000017500000000300712014170666023202 0ustar georgeskgeorgeskPNG  IHDR!$\sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATH]lUwf;; ~*`)"" D&(|7'&~$SD}0b(FiΌ-pg{{}t79iw%7ee2Y-+e.zy.FEyKF0Y69p<z(>?~%.}<χ}H)%p'/r\Q+ {QM"8n4 c6#b[GMaJ*L OZ&S*5PͶ<Ϗg V[OLV:+Ǣt5Z"h*14 ,/+18 4a f`|…K;5٩ B~,Sċ(I,Wݒ;6rzZg'm[nžS"/_}NGٷ 7(H=!i]²K\ɂ4Nz?vY&N 1c3~t>Nے太{%W`GEJTI߇tFF DN8'ik[4l7t(&D!S-1&uuutu䩥+`n(eӡeZOkR% 5Sxf*|aDH XP3鐪i1,M8~N;nhujYH` .YԼWL(ßrsC2@@-$$ö=\PD ;,~::Vm$.^dPc9wh-0Ě`ќ={VYmd2cH+cX0w3S[0~XqI=̜Kxmu.Gkk+M`ѼhZ |rQ<Ŝx"-/r/=];w#2;vl̻AF|m#dC9Qroi_yP@5֭_{ѴD Q\Z5"&ԉi5t =?0m ;/jꁖ|9v'D"]͌>$ΤCww7֭ҕcsz")ɹ8(OGixd gMlNk3Ϯ\Uԏ]chB xM|q=|ŽE2rMާFYMq# 9[Mx5CJPp| Q(܋eD<\ .XOUeV9\[ǖ ܂ݼ-(8Pp-&q|;g=l#ozضO m^[>GXj-7r9}B'h㇮}wr ,-c 566 Ba[X:rSP/0Y./ paIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/fleche2_.png0000644000175000017500000000261012014170666023337 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHkejvÏ [%H3ˢ4/fj(V) \T#"P3 5eC)mvqw=93̹9_|ey?יRJfO9lmZym0-ٰlL;i٘iegIY<7-=lyn/;}e;psj\|dv1yiXEӲ4SPX~Z x#(*KKzYTM0,LۡXuh@7& 7zJJ6Ihtk<ՈL¶ryݝ A'!* i +c5i&zޅ'(78JmFpqT~~iթ#R2@VVZl;vÇh؅J*@CbVJJVEQjŀ8I:mB>V6ְ h#hT/>Έ=oEU$tu*i9qɤڵq^܋Z˫UiiUM[lf#cGJt(T@AU|ըD6l|o&>yl EAa $252uul2K'3&UNpDhY;4ZR믭EiZwB"DeDbktZ`>mnRV.FM1A `sQUn3TO 088HPs8l*Dz%k(u뢪*a0p #Л ̚UÔ)xn#E Zaoo/+Wa{8[?_qIPJ%D`#|\ =u(ؽ{?+o`CZK.fj~7 n=X< n l{ydxr|v;'cINt]gs&~r|ޭ3ui:vA! `3|2k׭ӹeT-ɨRŚ4GA@ʾ e=jQS#bD/׈:؎>Il/lмj l6 8J[M_;шME41AHW x[T.0 tM 8iȃz`@ĠvF Wlb `&T$ ">䅀+DA(kX+omض#f@({ 8J&=RyH#;k!R%pHGvSjO7q%mQNJIc!e#,H!/~ꮻ'o qtoC6*^Fky;u U0y#, ̚pj˫-\gl4KsV2u<,zN/!- ~'2&ʆHDJaT/tKԦI7s,cg3JAhmUVDy)D]>Hi#n5Mށ}f^ӳٺS[R[* -"R. G:l>)@CmU GqQi7v5$b7g!Z›f,Iɼ Rv >¦\_<>AMzR\>TPkoػY5j vXsX)7r@f p`ibCUzd* Y|=W EV 0xZ;;(ޱy/t0ZHYX^-|s'\J D8Oi'C8q7Oa@^{n@ S)uDϜ&o7քj.ܿkBF5]Qsϕ#ҢeiE31>^8 _$!Crǟz;Y-9"G0 #C6@eK)UJ*@) TX)u1e&D7MyFpĘѲ| `͵KwDz.6IL\hۨj-X[-T]K <Dmڄ\`0WJ\7eض?,`xOIOWQ;\`Pyjȶň" ,"A` Aq48_pəmFDV^LI.\7\ Bfw@b-(#4VX>b/O' nQR)Elx3y [+O~^[ūaqt?3@Ԣ%ϼ{k[ʂ8mFޥp >3q*A]Kюl*qV *z9׸ jc#,8a|c̽ L#ۢx8|[Oa'}ӫ>Q{f/K_=䦯?EH?Rq!^\`JO3HV뾉'U v'Yw;G'sf6ޅܺ6`8.jtݙ*k1n<z,vY*}\[*xu8 MzX UWJUK,)׀WP:%B7s'A*Q Ǽv֞u ĝ}?xt8`]q'04ziz8μ~IE%ls+nARuAF( ~e{7׽r=Hb. vS^ۀK4k3ڔ٠s(RD7)_a~E?ca@J 3AfڄHAWöNg'_`0qjDtZoIY6ûTpqe?n%b=Bg`#Ƅ.NmG̊|v׭05ثB:t+8P_~Yz.Sqs۲5PqpJg\`<E%Lz{PdvD{@s=G &$I̕$&Eq# ʫmdNxk1)h[^+841Ki:$ZK#ۓ)1 k-z|SA4Tmm:TH` ~ĢPTJRQI+}!IP+V[!M3;33 =wwΙ3wZpӫcIזm8qmkKG \G$Ӱ{~\{~j3 }5`̕A`  @=4w Of;9cɵ@c 5 5 bM-B Li`[z|U`&S­7 #R-[ O6%^4&M``NcKqR7f{*QɛX9&q:z{4FLɻYb"DDL&VS͔Oz(|K:TkA',UOݤP/xmBgԂvsjV ~E#W.oEY$ʔUѡ,.:^9䞽=NwҀ6z5"X40͚$} :d ˿ ˸s~E6, 6-q Ԃ$A咽sZ%D~Vځk@F ޳/B {B \3EPި:fF7n&!D ,sբ BFL2.a:"AM`xJkjBJplBs+e \[#/0#L2 ǖ㽁qUh J<2ֿl0pF#mwZ'#cf4IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/annuler2.png0000644000175000017500000000227512014170666023425 0ustar georgeskgeorgeskPNG  IHDRRf2sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<:IDATHŗ[hUsKn5J M M`Ci,T J@4UTIkJjTV!NVi&N2!)r;̙3HBe~9`pOI)\u]?NHna2pxi 3rQ8 lʖL snF-p(ulF30H@WwW E, r(5˞9R\OމU=R묌|ǚmp-WNB plxx[UY)yqWJyɶhk*iLbx.+ EzUnϮP>EEQpmsתū^s@*%]UV@r0Mdaba2$@dUq%e.!5^/K5 )1eA&%HS\,.Pd{1=-2%!y4/W P{m^/5 1S`>>J@R%Gb1AȲGAmhˆF{lD}m]- Go6WȦ)~{8|~SqaXk!{n h9xx zu]oUHyWJ*sIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cercle3points_.png0000644000175000017500000000323612014170666024611 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH[lk.81lW.ŸNsBIHUҋUتHyR՗%Bj+(/JCۤI*PbS"l%6`llz̜ݹag^䅑f;ߙvQ0Mi,iv2- 2l 2mLzGDxrqW=OEO9)sLH^W=@1+ṕx;G ӰM1 (€A֊( QwRj蚎騲QO9}:pI`@]O5BG )E:LQ5EpXXfFHv5røCA+h rA3^+*9: d O9t`'m7kZX é17IV-Mh[ls_hxd(v d5!L> gX.M:Q6 ")2!hxݟ={yJ*zIlVIFFH "TS>b5{ĭ~T㥅Nia7aU vOS>[7"5&O" W_8K(L'aưwO4 ZaIItU27ß#e/%ovk/փ SsgZyk zgݨi~Fi&8~]uhƕ<\l1y%f>(PYo2p ݼӰi-䁶Vnmb^h o;kgORɷ3td6s%*w0 pUZ^9wΝG*}heP붰yLLe62vlc/` #7WSO}z$ըR$Z*~RVF8O (3`뉗ύ^o[$*;L>uy.} JQ? ӪTCBoPQ=ֶOr"Qu00DFKaI="d|Ӽ>XY I4˂./ ^{F SRjt\Won$AS 6^8j-OKnO4ȋʖh}Ͻ}[}hT[ ?]QGu>K]Oђ~hSb(1]*H4_# | =EkF<J!'d?ef :PrIF+c07Ϸ?|?yk{gƹf D&gzCQ~3(iSy  )KGz8LJ1yl6 훙 ?<O9A {T[ؾeYSݏ bS;kXҁ!%aPc JZ$AKV*!S z݌*{UuRBJ3v8*(W{M>nV6xsF=<K㯿1NʼnW1:^3JIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/rectangle_.png0000644000175000017500000000312112014170666023771 0ustar georgeskgeorgeskPNG  IHDRVgsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATHmT;2ˮ@HyB*Tbb+nTVcqM$&~MjФ&j4Ac*]lkB+ߥZ+t;6ܙK};ygsaw|xm?-wm;lmŶlۥ7F>aa_F0[O=Η4k:׾'K(zlq2RjZU3m5;@FA*}/0 ]wКBR.64K t`\ `&kdRطh{p֤fgF)H҄)oR躁\IuO7\+$mk **o8]<DqtٸG8ȹgֵ++.xmdoxNa4@f™aSp) p+]ʸAG+}iFOnVO*W3!RBK_Z&eQC')Dl0ZIz,\|>+'n㯿фuW^Mxi / ?"?/{W b(J)^ׇ='޵7`66fykװ0y덿ʥDzYQ$غ+NLWJ "[bb4-[n 3Xx'8*.^C1822 _ԣ>;X3]Ңήsy鹧@Jz7.JǛB2yV M7rm5î>NG<ɋtpJY2$E*(3j\oC?4Mf!M|#9x1^ܴ~A&Z>eD$À=>H\oGgխ@$|8LGe>_:gK$I\$=tj ! 2yX8kScq\FU皈V{@8,Wo&5J&4) G3qEI+-\iq`ѓn#Iy_|F&%0~E0|$)ixA`t_@N,.y=JGkn`Q{!5mٹKI O B<Sb`UPScO5 TyGMRʼn+M0 Wi e*<˛TG[><.9:2F.@OYwSD~[[6tWx|8}Cq\#<bΤ,|EZqHzѡnбJMYd(@E涨P i.MIҘ$I}&-#Aq?hN)◃/(5x"ԉ(Quϙ`Y|dX;ߌtPPw^ˁ?q߂u} 1ԑR4R'^o#HKp݂%  @qdCzHGG~>b2:{[{7 Ր@`` o'гV{[&3j%rQ4]|`>\^?=M ZÜHpU+nI6MUnpN _@v {(4TCjL8a'ɩg'^TdQGR_uӅL 3>Fh.`ִBJjO\I'UV`n@kc]Bk=g:|W\ٞ^ͿV0RE]<FY/>v GH JDk\nTl77IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/zoombox3.png0000644000175000017500000000300512014170666023447 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATHilTU鴝NJe)kAd1bQ!@?HBcQ KP6VR h@R: PfJtiT$$䟻'sSgu ~B9f7㬱2fцP."MDv#qq`>Bsim]܉raZru"L\ˍҽڸv0EHEP^9֒ E,AyH͙e`&Uw1qgLX쥛i;|XeLX,ކYw1DFn_K!*4g zT !s4{V#2.(1m+RkusP0 :>]E1w!|> |B*DLNteBH,i\)ڎ%}XPBXf"0DE?B,iCwJ꒽hZZLQ$ L+vTG15y+^~o0cK1d/^ʼnusr{u"t4 !%e"s7hx_;gO!g義XP 9sĬlqjpW~Yˉf5$`HB(-#q8ZMze#IHI:IG1CQ[;4U%1y^74Лю0H4u6-|׫ńÄI6КNrT~ܵK^U٬P j :}xi4iJ0yHOOP伌6q6I knXPw;HR855c3NFtI 93hIZFL(̵swp;r׏:m"C'&BW9,a+MW O҃L8͞pqNGA jAۦZou]!G 0P1F*xjF̙5 Iɭ~00[IiXk*=bh8իUw pXTDYYGԀNɛ~[ . D" ijbwoᕜwo)wi {@ bL1 X*M nFSRq,;775)*R(((qAW=IrFzRs_p<Ƭ*[of-nUrIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_6.png0000644000175000017500000000211112014170666022674 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHk\U?eLjKdBWPA\TBTnMiE mJ*"(3h RHQB3ケcL!;s{@-D9(P"%Q}EIj:Òf'IZ$5͜$ ` O?9 ܺ\5H(9: r1k  2;]& B e,Ij7Li` N zvE31( 8YG+@{.Nbu;7iƐӲ>"Us$td(Ry9yXEx8I{9`OW mgإ P{ %EE${=Y=ԩ{d=X&8q󘛟@TzJTӔ` mwى쯿Q~˰,}=d>ydn2f'=3|OR ZsHξt}S=;GD٨X"#9ѥyF 46 ײ2@/MK#yrn&lnX8tMN u8شeCGpRܦd,-uS٤+˫9/%Iƞ@&z0+S} ޅw1{q2UґQmDe"lhd7ߡqb]- Wek5adZ%"M$?ˀ,K$;4%IR`sXt$:>#\$iqvA9=VG[ϚˊYm5Uo3$VS` =%ڄ3`2FԠQAk?!m1!3?]ǵso jR-p윞.A) mp\J,|$b_tY;FS2 9ؙ39ou/7\;}1Q,ʬuغy1"#lCIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_t.png0000644000175000017500000000204612014170666023001 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHOE]I$ADL !zЃr2=DQ" $%\}ed"xP̢$ٝ*3`aիWݯZ8q%;J(^F J訄4IUB64dhc3ȠM6l_ٽ[[٥[y2zNFA^G&A.pQM(n- $* Uh$A0o7tT>Dǧ:$ӻ3 K_}8WBnmTXq_K ;7!vp*Z0V1a 0"6۟|wg(Ur9_#Wke[8kc^}ߺBrWT+j?dIS 1f2brftMv.u6_NorVoRQxVTFdZa ptb5ih{6 JNK ~*0 C`XJfJ4$ܽ> \qiǛ$jQ_E$ 0v9!+s\po9Uɗi⳾SžAsMV?k}` |AzVn+[}šIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/bissectrice.png0000644000175000017500000000300012014170666024161 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<}IDATHklTE3svG<+$41E@HԠј(V "5*I1QR#h) [(.-ݽw}tw'93ssΜ93Bk k-YjR~%Y &>KB@4: u#1':h:YnH &~X5@WeuG-} z%B4BdΎ5H4ѽ)ƤI\"Qp$3j `((+$ qto&7Z*?ffs u `< Ӕٳrn*%e$Ч2F* ~Cݞ/#J!Y!I(@ވxq;"6` ,^ҹwp$t xx ޢ'av1(]_{"d j|Z8sPΆNQ,^ &^3q pM!niD/\ȹNjZr#>Bಟ G 2oI^YOtG2Jsaж)Y6Wci;$GNcȩ(JpE'%< ^KQyc~ (_vYM=C?wr@Yr\\YAѴYں#gXձ{{<qMH)v*[s4x 61BLțP9ӕA@ חYUL3e9Pk~F cNl=~2hru E$B=6!C1Y ŝX: EꍞL9. &h'O<͔ JU@98R (j8v5Pg@@ ljHF$`mcc|^AIDkrZkj*|T Wϧ>cs8u̝>X8`)*R`u Ccg+7IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cerclediametre.png0000644000175000017500000000327212014170666024645 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<7IDATH[lsۙ57 `17"RzQ-MCyTx*їjMSD&jQISTXı]eLfR#5gsJpM=+G ] JGXW ]&+u#uCW$p\I1qlG,鉍@8tU|R:FXnJ<˭ e!IxK)l,eɸ؎]u5~elYS|0 zʵ j+pDzVDT ])9f l¦躒#zT\Eg)'ٱ?t,k +pY3|-Q"С,ea%yӏ`L*__U˸.,კtLMl6V59X)39^|h4V(;Lsh%5+jA&9!"K j2W#H ͜"M5u44MAS},ښ""] I W.+LU08tWfƉ܃R}<"R{bqn +" i *78m/J4M&!Kf Z^ɡνf-U"TPI~]\g'[)-kQevigcQlI3ot"ŹJH-MAHe+Xe.O_I@^U%{ot&]S "UsHǼ{E"FVd$`u?SdOlƏg8D{2w ҉S]CRQ6/Eaìz"F~;I$LvDaU?i,Җ m_s (_|LC-ޡ>s`k>~pjX"cId4ߵ=;a-{&blo\kiUj&jݽav[=l @lyҶKv1EJvf!/x8U%mµY% ]jx@Ua ?}r˟n|Mh(o4d۶F;ݸf!!y&&U tLKJXx"Ν4sN[ #Z5zTRقl:.S!۴-}\5ְrEj5+eYצf!!8 Ze 2>l%_nƐ^\"Dؐ<2 <鉍a] JkI9؎X3j;wMd\ځ Y2$_G6]/gw @IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_8.png0000644000175000017500000000211512014170666022702 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH]hU3;3&`c_,,1Q|`*  RCXD%JAȃ*_D&VDT(Bwgf>$8;s9>?r諹З3}E(†$ ACiL-qbHJƉL'fd VoO[`hrtif[|M?hȭWC)ٰ4EσbA(VS%NNi ,m`%2Jn7d.AI}X }rΈaH\mN31e˭Q9G[MV c9Mkx}`OW860=btD A,뙮CX;~z&R".RKln!~>x|S5TLSF,y+*zeUqEXͣwO=ʶ}i/=9b[nXY TtdK&H>"|,aUeq 'BwfiZoѺu3콸*ҩz%FgY:a;ҍ~"οoߢCډ (7nli H7?oL>f8 >BDQjְ?RB.^Ľw w}ykqK>؂ȖyR F2ptusп}Mɓ@IӍlqxQysb&&4/'Q EGX씀tuπC)^_ĐjCTw5 }h**q`P z"tv̙?VIg]X#K8]pv lF[8ac[ȶOf<)3d<Of<H9 |[$HTM5 OzxSqǺn|0LtMEAU5t@ײJi:.}Pw# OSߌ_Q蚎@'$qBuadt LCfXxY6ܻK؍% .]Ԝd"@p*4MJ w3s/[?C܋v XEȄͧvű+QD))]\A&k UZZv`X4c)}g B^zu8_jZ\`i6+V:_`BQԒ ŷIi4YFf V(LUUA+1u1&).uZmbr@K9`ypa:h*T68kqe'[fQвQUlX-؝Uw!9ಝ@KبJg1;;E_[ǣt!Pa>, sԩ45m6GyTf aIgwS:007,abN)E }əqnߤ}#$4A Bwoӟǿ@0fܟ?I>Ak_ 2 )iҗts]1:׮\$cO}L~0e=޾uL-Ld.Hf@a63~*u)ӛTZxy./{ǻ٨W-{)Eeso񵓯?j pHEZ>- ! xiRG\۶QGnL0@ $u c{S>yK8ZF/B@Wy` wJOY_>'wuKM[FG&]ޫygK"˛Aȶ;gs@/LB׃_!"^u//y{~DY.Esњ9 ϲmWdJgd;gO=ϽQ!m[vb:Q#XzΞ>!0BQgIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/retablir_.png0000644000175000017500000000255312014170666023641 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IDATHKoW3sǯMBBcMBRZJ$@i-EY nak|VHBaQ"ށBhI<3#Yx[sy٭_F5#y"'hFjDӍTH"QjzLo,,U᫣N29]SAJB]GQ|5J|ǃ'zY&el|#ζM:勃E98+n 5-,N7?saW3y]e#\n':Bu[\U;%jMS[x*p d7۹F1Ts hLT-T?I5q@efu+dz pl<^\+Lȣ&..jfH{*,Ε::M,#s~j)⨆9H͙i]H0W,*EhsjT#3_kNsֆ;eۛ1]A`+R?_G_M]7rc;z8~=]&ҭT&柵mEﶺc酘%Kܟft#l+w ,+=I-Jf~ŵ[8*v{wmDHa4Ve;S (\l&R=mķz=倔[Bj[fm^Ӧ`Cm9LcvnH4ⰹlثfX)dZX!o7"iֿ0Z _YqM3.1!peR-׌Ⱦ$?B$¯ E<0 ot,˃ړ5FvdR 'u%Wu^^8{h&IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/arc_points.png0000644000175000017500000000254512014170666024040 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH[lEs,z(Ԗh+MC%DH`bcMbK$*FIABԖڦ\l \9g{.{/3̎p醮vtU`@%]!M̴ҎMY1:ovG̴7^m{. Xyp &b]_w=.CuyI0OSB{ǿ DI)gj&fZcݨ:MAi(n'Gdm5` @C]N?cSLWϘKbkf"2TJ264g(q>CaVpvEԆ*Y4+1NZ)tg2{ c<Ό\EZJ& f9 i),k!v OtrqL|[X*ҍ>znpg{669+?vkQ5S[f̰S() j;u Ntu-]|?ɛy[wCXbVU^@;p6<*/CiL)/ˇu&ؗ.uǚ`Km{5RbztPQJ :|gʓ]n݆c?$];OϧPJGLdw5UO?"z/ľ\+gm.[diSz%.%87 ܷڋ8ڜ(@Z-]܈͗ wǸ "-/`$j)ǃ$[6EObܹ1"3H̞^C /}}q%9ˊG]@+}[drfjQ~Qyl:?CMt'\ƏxSg"ɓܵmOZOJ+镡)KĢ!Em0pB.T#a+%cɮ7)F~AE4j!RE~IЉ-@okP2Hh:fǁdBSsyO\eU}5sԼ*fq": ۸V݋X4y"ɡXT M[5t"1+g#h;lz +աVN ,+lt'3~M`8X6v8m{ ]5tU7}W image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/texte_.svg0000644000175000017500000001135712014170666025007 0ustar georgeskgeorgesk image/svg+xml Texte wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/zoombox2_.svg0000644000175000017500000001350112014170666025426 0ustar georgeskgeorgesk image/svg+xml ZOOM wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_sin.svg0000644000175000017500000001240512014170666025146 0ustar georgeskgeorgesk image/svg+xml sin sin -1 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_pow.svg0000644000175000017500000000752612014170666025172 0ustar georgeskgeorgesk image/svg+xml ^ wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/selection.svg0000644000175000017500000001473612014170666025510 0ustar georgeskgeorgesk image/svg+xml SEL. wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/axe_.svg0000644000175000017500000002047612014170666024435 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cercle.svg0000644000175000017500000001250712014170666024752 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_exp.svg0000644000175000017500000001075012014170666025152 0ustar georgeskgeorgesk image/svg+xml exp ln wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/zoombox.svg0000644000175000017500000001125012014170666025204 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche4.svg0000644000175000017500000001164612014170666025032 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_mod.svg0000644000175000017500000001241012014170666025130 0ustar georgeskgeorgesk image/svg+xml mod log 10 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/interpolation_.svg0000644000175000017500000002473012014170666026544 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_ans.svg0000644000175000017500000001077612014170666025147 0ustar georgeskgeorgesk image/svg+xml ans () wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche3_.svg0000644000175000017500000001371612014170666025170 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/maison.svg0000644000175000017500000001260712014170666025004 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_z.svg0000644000175000017500000000745712014170666024641 0ustar georgeskgeorgesk image/svg+xml z wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche2.svg0000644000175000017500000001052312014170666025021 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/logo2.svg0000644000175000017500000017533112014170666024544 0ustar georgeskgeorgesk image/svg+xml WxGéométrie wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_t.svg0000644000175000017500000000745712014170666024633 0ustar georgeskgeorgesk image/svg+xml t wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/milieu2.svg0000644000175000017500000001233712014170666025064 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/droite.svg0000644000175000017500000000667212014170666025011 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/arc_points_.svg0000644000175000017500000001522712014170666026017 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_rac.svg0000644000175000017500000000701412014170666025122 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_mul.svg0000644000175000017500000000755312014170666025162 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/pinceau.svg0000644000175000017500000004246312014170666025145 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche2_.svg0000644000175000017500000001161612014170666025164 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_isocele.svg0000644000175000017500000001660012014170666027023 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/angle_oriente_.svg0000644000175000017500000001707012014170666026467 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_2nde.svg0000644000175000017500000001077612014170666025216 0ustar georgeskgeorgesk image/svg+xml 2 nde wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/demicercle.svg0000644000175000017500000001257312014170666025614 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/axe.svg0000644000175000017500000002046212014170666024271 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/sauvegarde_grise.svg0000644000175000017500000002231712014170666027034 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/arc_points.svg0000644000175000017500000001523012014170666025652 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_ouv.svg0000644000175000017500000000727112014170666025173 0ustar georgeskgeorgesk image/svg+xml ( wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/point2.svg0000644000175000017500000000731512014170666024731 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/polygone.svg0000644000175000017500000001465212014170666025354 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/sauvegarde_.svg0000644000175000017500000002235212014170666026001 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/retablir3.svg0000644000175000017500000002220012014170666025373 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_rectangle.svg0000644000175000017500000001522212014170666027343 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_5.svg0000644000175000017500000000717312014170666024527 0ustar georgeskgeorgesk image/svg+xml 5 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/perpendiculaire.svg0000644000175000017500000001200412014170666026660 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/angle.svg0000644000175000017500000001063412014170666024602 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone-small-no-background-2.svg0000644000175000017500000004751112014170666033146 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_9.svg0000644000175000017500000000717312014170666024533 0ustar georgeskgeorgesk image/svg+xml 9 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/demiplan.svg0000644000175000017500000001751512014170666025312 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cerclediametre.svg0000644000175000017500000001252712014170666026467 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_e.svg0000644000175000017500000001070712014170666024604 0ustar georgeskgeorgesk image/svg+xml cosh e wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/bissectrice_.svg0000644000175000017500000001375112014170666026155 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/demidroite_.svg0000644000175000017500000001203612014170666025776 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cercle2_.svg0000644000175000017500000001200712014170666025166 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/texte.svg0000644000175000017500000000731612014170666024650 0ustar georgeskgeorgesk image/svg+xml Texte wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/masquer.svg0000644000175000017500000001323412014170666025170 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/arc_oriente_.svg0000644000175000017500000001541712014170666026151 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/zoombox3.svg0000644000175000017500000001360712014170666025277 0ustar georgeskgeorgesk image/svg+xml ZOOM wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone-small-no-background.svg0000644000175000017500000004756512014170666033020 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/pinceau_.svg0000644000175000017500000004246512014170666025306 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/angle_oriente.svg0000644000175000017500000002031012014170666026317 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/carre.svg0000644000175000017500000001505412014170666024611 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/arc.svg0000644000175000017500000001400712014170666024257 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/disque_.svg0000644000175000017500000001221112014170666025136 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_egal.svg0000644000175000017500000000735712014170666025277 0ustar georgeskgeorgesk image/svg+xml = wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/annuler0.svg0000644000175000017500000002163012014170666025236 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_ouvr.svg0000644000175000017500000003410412014170666025350 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/gomme_.svg0000644000175000017500000001720112014170666024754 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/arc_.svg0000644000175000017500000001402112014170666024412 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/mediatrice.svg0000644000175000017500000001410312014170666025615 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/demiplan_.svg0000644000175000017500000002424112014170666025443 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/milieu.svg0000644000175000017500000001164212014170666025000 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/sauvegarde.svg0000644000175000017500000002304412014170666025641 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/2_ans.svg0000644000175000017500000106507312014170666024526 0ustar georgeskgeorgesk image/svg+xml WxGéométrie 2005-2007 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/rotation_.svg0000644000175000017500000002164312014170666025514 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/symetrie_centrale_.svg0000644000175000017500000001510312014170666027365 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/milieu2_.svg0000644000175000017500000001471712014170666025227 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone-small-medaillon.svg0000644000175000017500000005104012014170666032212 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_4.svg0000644000175000017500000000717312014170666024526 0ustar georgeskgeorgesk image/svg+xml 4 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_3.svg0000644000175000017500000000717312014170666024525 0ustar georgeskgeorgesk image/svg+xml 3 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/losange.svg0000644000175000017500000002266212014170666025150 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/pinceau0.svg0000644000175000017500000004146112014170666025222 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/tangente_.svg0000644000175000017500000001241412014170666025456 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/homothetie.svg0000644000175000017500000001504012014170666025655 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_div.svg0000644000175000017500000000777312014170666025153 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle.svg0000644000175000017500000001333012014170666025315 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_equilateral.svg0000644000175000017500000002024212014170666027705 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/mediatrice_.svg0000644000175000017500000001410712014170666025760 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/retablir.svg0000644000175000017500000001163012014170666025315 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_6.svg0000644000175000017500000000717312014170666024530 0ustar georgeskgeorgesk image/svg+xml 6 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone-minimal-no-background.svg0000644000175000017500000005672712014170666033336 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_.svg0000644000175000017500000001333112014170666025455 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/zoombox2.svg0000644000175000017500000001347712014170666025303 0ustar georgeskgeorgesk image/svg+xml ZOOM wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_equilateral_.svg0000644000175000017500000002025612014170666030051 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone-minimal.svg0000644000175000017500000005450312014170666030575 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_i.svg0000644000175000017500000001263412014170666024611 0ustar georgeskgeorgesk image/svg+xml i 3 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/point.svg0000644000175000017500000000662012014170666024645 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/droite2.svg0000644000175000017500000001307712014170666025070 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/carre_.svg0000644000175000017500000001710112014170666024743 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_tan.svg0000644000175000017500000001240412014170666025136 0ustar georgeskgeorgesk image/svg+xml tan tan -1 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/zoombox_.svg0000644000175000017500000001361512014170666025352 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche4_.svg0000644000175000017500000001373312014170666025170 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cerclediametre_.svg0000644000175000017500000001253112014170666026621 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_y.svg0000644000175000017500000000745712014170666024640 0ustar georgeskgeorgesk image/svg+xml y wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_minus.svg0000644000175000017500000000657212014170666025520 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/interpolation.svg0000644000175000017500000002337012014170666026404 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/surfaces.svg0000644000175000017500000014322712014170666025334 0ustar georgeskgeorgesk image/svg+xml 2005-2007 Wxgéométrie wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/vecteur_.svg0000644000175000017500000005213512014170666025332 0ustar georgeskgeorgesk style="overflow:visible"> image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/annuler.svg0000644000175000017500000001140312014170666025153 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/intersection.svg0000644000175000017500000001245612014170666026226 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/surface.png0000644000175000017500000015534012014170666025135 0ustar georgeskgeorgeskPNG  IHDR#tbKGD pHYs  tIME ETe IDATxw`UUsKMwBHBB BP"*QqTH^C:@ @z^~В}~o3:3Y{N=gg[kMc^jW\iii訮dfRJJMT4^M{ gΜ)-rj6I2gSGUv5vrj喎R;/$,4.2olU[#>.\},j+nNL|>QG0`kH؞ļg+lZkkW6tu+eL;f1Æ 1Ʃ2?Rۡs^yѱDLg'FDDF_hEEEyׯ@bEgx%ϝ岴 ja3!weJ_VJ&_"0J*|_y9"6r75!!i`˖}W.}ܟ #uQQʴMO 6IJ~,P2p*w58oLww_kÌ*õ>|m{ߍz&N߁;)lǎN}@&W+td!aS|rh!<8];;f;B[,mΩQkP- HE=/_'۵kWO[jo( [hMhq 34縊pvSfJ'.l)uW{q\엖vu֨~vlԜYU4 [8y>>>>g-~0|8f#z?>DeC\S !jtVOj}v7tv^'~e^-Zl翶+W}/;wߥʆd00s#S̉P] yUB*?dMM.C{r|%lj>/eOӏ744"~{ylջm7xr P 9.T^Xy"g/]&Z\bй!ܒ!!JI:,jЇ:?6KWۏkH8p ""~͚`}1-sbVRC )'Ճҁ/2w'hC PJ&sGorOXg6){9=]?i7nܽPi qpna'$r34f8Z4^C2݆**@ROLvVŻJe]&dhҿE>dij=BPE AV36 A8k·U7IIe(i N89{;rGNUE g{r. 0Y?8HΟ?diٲ Yag R55ÅM.9.iCZYz!9˵ͭrȡKCB~J%6EhRħGN;Xis-2dDQT!NX36@+D45吢-wWGkG3ʽfSQ0>I3iҴm1|(ڪ< $?::j^?o/^ ?VX,g{DYEB@ZflaUyP% ½6=N\uw&ߠ@SvMxEpGs7)d|nҴ#3^ҥ I}]/zퟱeiZڰ`HwWP8x!b/PzџU!eBZ*w;6_ߞOV~ҽ!)^ϳ響);Z( 󉽳-?iDYI/(67p`p9kZrB]KZ _|3G)\n]HQ9*z}u8LɕveAJJ?؋^{2Eo 46dT͑Cjfj M4|NȤB,G-V`~ԍrC,g Q&st'Nd /xgEZ[o3mCj;f׀#PϘ *!ԦI{s jl/NGyfi~ r'E'_n j-h2tBӼEbZRQCMqCܾM9vv}'"׺_ݿ_l0xbK%6~=xq`!ɽ$R~Z2ēnx.25Z|Ɔ+AwJYΆR0G{9wJD EZeu\s@SLgϞ?]٦M.|M.m2:7e B*i"HnPg90_䐍-yy|>wUBk%jiZѸ5o2R)S{%FsE}}]-!!_Zq$2]g  ,l wt}A`Is WH6fx7{wI/z ̙3ɴ"B!sPĕC<_זێˬEc ghYU!w#r'*sJ8 PGWD¤czhBUHV(_+ѶSmUd΃L|?H8OSHحh-ӌx,XlaVi3 rȇ}C9|2 9wE-^_9#RJu6blBAS,jTD ƒpuR5_T!("XIgG08/ۂ[ U(-NU;>r =!"CDx'${Jud>ZLKG>r(JtFOoquK>?>QlJNNE=ݻRj*SsGl!P&q6Ew4DZ5qd#BŇT*c5'=\Ĺ#qL+Lǘ.][7Y{r,>(,F{ּRXhG9$@cPΨl>_*III9pqze_n4 if̶U.V;%Op0~P!rfUaE<m)+BNQbXNI}XTghZ|[F\?X=(\*;+xe/Y~hy;$ yYV9~5kydN?aeeeaa/K owX;`ۃ p:Rf\`cqěZq4!U-ᇩb+jo9LTUHmG wC8^⃱VķRC [_> N\i|hQb7⎚)RJU];R8H>ܟO^ۥK R]dPrY(Pk ]ekêBEvیΥhQ_^HVRkYGŐ |.$9}\:ز4yҔ[LVf}p|[TH#ľp**9eW=ﱹHo{oȐmmZ,w§lA)ml`w \NGږq*Dج;լ'NA%;Tr8n^W MCC2Z!H/_tb_iHNa+Qֵ\㨰6~3Og}IHM6R!棋rR[D]@2GQ$HaNf<,!M >-ܸ C#y\WC vP~+Cnw!s超|ēfzEmzc+vSdr#Hn~{N+HivF"Ͷl2sLt׾il݄Dؼa&@E?h@y)K C<ش'q`,a#W˴31ʼ|q$k$NRJ;cHs{ޑB}b{]^WpQ޶jC`QLUYbݍ-ecv(OLۋ[ns 20lrj^tסdW!5 jaNcɢfʺ05U54>4"I&Xk=Ljv\J]11fiz-t/oM7G5ѩMrH ~v^] {ɢ11}"ZZZzo?O  D,@DՌu2`VpAE1=DrGMz}S&έx{k^ sBX,XpE(žpH[ߡk|M(++E\_0_6T<%lnV`0%[`$6!~ԫ`mivaCRc~s2k]g5$koDbIuY{c-P[)ؚDBf{rxxxqqq/#mժU>l~@cC (ӎ v|`:c5@$xz@pUmO"h/@>J:Tд_{t+˔}^âP+/-ۨ 2kX,q3%eZUW:!TdMeq)!L9zؿlH-o}nKX-ҀV|$0 ܠt T]P"Jg.iZ{-oQrp( olѠl"h.\P*CBx HdlmpRJ[[}g/6sβE@b>QoMfηAA!/pHȚ3͌Ƒpu&}D믡䜃iUHX,*nP].{#GÍ J+&j -Z0(K[-boye'ͥޢ]Hi\K&B?ݲ7ƾ0mڴ^VPP`0m3'p3| F6>1۹@ @yfj¥ǀ:/U݇{ξM潳)rHCl˷í̄£ȧ0_mi=2o7 iΓҜƦJ]4~)Γt>NA|b0i4Dk\+uYK¼#sK䩿EO}=|ӎ35qϙupaÆ"'iJeVN BfXJ6ޯ1vTJl ,{[9@ԛj\-8vQRĂC1] CEـP*G\MTz'vmCu1ϋ FG_$2W貴ufOҕod];3WnwkW2G?վѷԚ龍ei߻oρ^Es!68B>`l- BJ( x欉p'L1fa)g$eñ052c3yp J4LׂH>QM&/漍[wUV,fƪ4t豨!`h&t{~@A":-;4! dov9F-*ȇ}ztzmIﲸ{^ p룏=ܫ=ΖunQ,2O:6:듂`5@bX˨u Q>u\pS,OR Yk^ ;8āBNVn|WdxX)$[wz-83뻦nXSV0Bi-jCdžԙ4QTr=O%s R.ԗuoOrY NҌFS,N=U2@3a^nrzm@`N1M>R[CIܳAv\(jeE뙥NM&E"ܷt/X LӎD3!n6\qڸ#s_GC>N.rRm}Gph&-oX;'03#F8z*,-"\ IDATsu\r8 ;3*G7G`"' /m{‹//EcyyyV+>:L6 H" `CFE@dip^_ 8Hs!!i%JҢEd^A*BrbD kΛ_~s. >b ~žR."2"#ǡ* @*xaFPCs/kTN=i?7v[fE`.];uql4@Mc.!( l$2`J}Y܁"DB>s}ąbI|xp "BԕLۂ(C,u|j( .?wm"7Ӣ3Yt kNMs/Kka:ԡ#DIݠiх ,Ov yw6ZXE^=<+T6~n_~a/c^WNd%62f+z]K0k?F!Ẻ炚WBG^b8L p #iH))n炠xv;u ]BsUaElmem6CH3yn9<G5^H,6<Dz1wSPPЋ={$xz%&J9]n9s,k$P l Dzfi!i K4tqEDLB&w$8@e|8[k*Q:㳋: c:uy.N>bFaOOpo| pTBlA <{xⵅZGg"gee7p X^<Hlr֔N$B2r 8 c7.6 ȼa#inEGu!ie,J-'A1p}Wb˸7Ш_Bz?ίgn4ufHcCzxM-|7Vk]=4/hZPO)*Z"dR#` ^GSo%^@|e,r/ˣhC\yXɌ-ѭ!1AKӵ4R| (:dCE׵B]vZyjJR6xuHDIZ{*t(IXhG6lVڷ^)L_}OJ"%5iԑ^v  3BBx"$85>cxBjJ 㮿.a<մ'Wn"/G"ďY{%|RG{veT[W>{@i;iZa#CbBH@Oj*$1K9kfs[#I/HۢE: PtoXar@cgo. klU@x!KT{ZE31Vq)/=  "[a]! 2-RE4)Ie1[u+/Iv߂<{kZHv|}X9f;ݳڛL_l$5mq"ca2h+Z!CZ,M<fXN!2Ë6 T׈Qh$* |xH>q(quLX&M}Ekyn_wCkk++SIiJ\C8fƊ\_~Z&w->fi?n%ה7"$ڑ^anVw9'$ #y[8OyT#c}p9.]3PÕW^}&-"DW`iX!.6Hu#i]kr@ Tq?_vQ5,C4t4=##oA1Zjс5wv=2ÒP@'R@ZpzG؋988t1oC|v!ra.61[oar:kB˘=q/A'-'pNgVTL vdQu:c] Eqt[K jOY5x %w zӿ} kcH{찞nwrst^:nI233{/gϙͶ fB0fkl JHlJI,y̪49݋Oo)V]TiQ@XV'|rrrr7iW\111W!O8^~By`5&ZYbjW7G<1'F,i&{ɩ7jLw 9`8c-t}wJW|^%WH"94bgϞ4h***BBB\pURխZ*oQDr^.xzzzzzzxx%ig-IWHi/`+firlVOFT< S-B𥒾pX _8`Ӈ-yb+R M}-%(9@+qu/2Ո7'Y8  NJ5ADwJV}qXunu͏dQg޿h9{N֎ln"puU%2Idhxi` *s vTBzb[Y?U9|c3CCCpW^8{vˎ7?H<\ qTi*,;@]WgX3`JRV"XFvRi_tttľ}&$$hZ77!;VY()͂c=`-Bx-z6ɘ횎]n"0Uw&fʧ&T洯CNP |xgtL׀NxG#XrL c#f } J^+v,3=jv .=vv'Q}MBì:xWdVǎꙤ9:]A"^|q+>pწvymA =-lS5rۗ\wso_6lCZuuukkkVVVѣ9gϖ)@^/V+uZS; Z,XP9Q biegWi0d䣏NMgez]׹@0&ȬbLœCN1))$B)K!q IXf?dV@ˢG7eɟ+l2 13-ؗvK(z5W0ƸC#\~|CJk͈艤e8 >I>˩+6U'WV'/ECG3{iN085eÆ fDӺnX,Z$Niw 1spP#a~!Pg:JSl|%U9w[:mnHH/$6<պV#xi%B.C/&X [sZ-XA5|-UEYIcO4Y\2-_5h[۫o-V5䐢U"qe>l,gO_c'Ly/^Zw@ڱcƏ1qÇYL}nS&oB*}A8-2z?PXi]"o p@ޱ鶤IHi` MйF I`P  D}(SLy!>{w"f*'"W ɢH5:z |`=lDk?\ۺc!^@Юy%II=̥p]-\”_Tx^4k5TA*L[7YϳzvsXNT.~Ъ+W߇kN3fq'OX޲Z9?v8cţfn4/N+HM*; 9=D#!d[-ݳvGV{5tTw̘1_M{l'!]ypJP( W Oi P7,T0 E\X|p ne\T1f눎!zQ%X'XuP",LP8#HdD7-{#${֒3vS%^7gE/MF-}MK$RiӪm|G|.*OeA{Sv3fP:K_ Y+<(MtsBr^{o;+@JCK\ؘ+ /!GEF/BNjEYD2 ITjϝ'|v*) !1 8k  bJ6AR 8.s5<pxVgijY4W7DJؐjaXAXo7QYLwW~jeBv\`z6VZw tf갵[DxO$QVy:zg®*. mN K߶?++kĈɷ> ) Z@!DQAި.g)岊>!d F0vN)=Ic1`( 78Ņ\LX< H!RZCɟ9dSB[*և uT-j`jd3<AF$v9+>2؉TG.C[1o IkZi{-RDd<:щiF IDAT(#,yie,8C+֞)j\MY}v3Ӄ-+:P4'xj}H̀g,d%9UOio֭[w)J=bO=(@ d~1f;nm>BXyPX+oh"PJio(8σ|P+<+~Xr) (m`CrZ렘f*4+I۳Bc2a: K)}4 *g.S"G7Hg TjGM֛Hc'9\\Ka_$~Nڛ{"i~QNe[%.>&4XnlT[bꜲIZݱB{ 7>;6QnIc&rS$"Q1}F+kZ]DsE^СCUTT{x%Gh|(')x0h@B%$[&jJ""\ S{ ׁ05DtJxD^R(,K+i{5NO:1ca1 Gl\ p!i  KCij} u%,ՈkؼV< Hgeȑc>8)*/W _,> ;>/= GWAߠ艤z5K2sG~7b[Sa2ghk,'>3뱖úWc=I z'NA,f77A#)CxfP Yw S~YtM,E48_8I;41baiOP'']\*Kj/wK ph{a#}œK8;1ʎW ~ǖBCᬸo۬׸٢tT.Җ-[+,vrxIv+{^J#+1؟lfpH;ɤB!^B JokETD삈"*]])4齗{'@:$!$s9gg~ZYZĭ!BK9 `'ਪNR:HYYJW)͐ 9fT-VjFr"uU Qo Gb俻+d?nHxyPDg +_ $em4g)o(_jICD-T'@AVX'o-69xAY<;SMnM+7888x]r$xRsRB$I#A 8mo\~7Nڏ]SCպcmvJ1]"maߥz$uS_4CQ6fŢ/KW R pS/%8~EJJ;7L3XJSP$rX yǞ0{u-Enr UՔ.a/)i-m;a \|EZ~`Jn\dzh`1L:3Z͈SLi?]?;Gto ClNi6vh:.d,aSK7ʿ@ί9XEy)F_jp>h[Mm|&94xRAuPD /vG'c/#H̍ yٷoiQQQNrrvl̳@;U߬ ?EW./|Gxu**^A}YEBҺdd2Zpi>B3j9m bH`mt'M#LFVjm17ʃ4]G2C0dׯl5i_sfmNJ:He귑37ұ i8tk'lu+swpɊosۆޖq0`nZN|zZH|͏߲: ~#6] ؓM!T؉j=iyyI 5Ƽ)pZ?oVo)w+88{ǷbOyK|]jډ,y+7H :LAgkp ~&F.Vʛ:W0%eWrg,Nvk lB.{uN));pR7s1Q@+x8iY\E,6^ХN9<$dQONK\FOz6ևǷmt?sj4_{ܷ-`^3dGӁsR(?swrU w*q˴WY@zLFzLmr4o&OdZ2u*^ĶHyf׷ڿO|7+Aw XSf *H H/\mS=ȐBuKթISQv8} :ܝNgHQ]6%ߩ GH$;Zj&pRF)/QjMq*hyQT:f<aez<00TKILMI=:Gq)I=^Ka;6V?bn3#%g>~34^;͹s:T=>r*}C=8:y!E@ i/Ҿ])ye%AkŶrr|^2;WUڙspʇUB};,ԝCg|`"wJu* qPgRՊRI^8Ab AYX1WhUX :PZQ]Po˲Z&{:fPYc:> )sDLLMum:x/$SNM(ZͱsKN[m,5YdѺjO9]7D q~w-~0ŝkvQٮ{i9>C}ۢܝ1xoȭuw}Yk\g5݊1Rҵo]mN#vRi Á#Gv$(‰r6b[ B+69xEf /t]2]=A;0>|xiE޳x(- ^B ; %k62sZWYk e"~ˑ*,df@}B jzS*8)?_Dƺc|LyAہOq\Q}_o/;n%gq uk+X9G/9wjw׼O\G7t^fEVКtZLJRBi\RvF[z֨]80|u߭ u٘l H3xZ 㤙JQ',c%i&Nx*SuG]ByGGPEtPI[uFnf@2eZxq-Hɒ `xbPKXѮ *t*{b狍J}qzT fD _:]6A '䓗DiKsz6%֙8#>T̓eazHݗ?OUs#{YYi髶;k8=ewkڶc;g'ϡ?=6gj6Q融(U5HOEÇnA&3Y3Ulb+6;Pfys wWW꺩MT蛵 rYW2B-P,p=?`#lpAإ4W8CLinF0.}*؂a5`x2 j&ZUX@__Bwo_h9MwdB=..*K"$Z nnCW-u9.Qq֎Cgo!gO.ܮO8Сn%TG±;}Z4 QxhKWaAa̵~Cus)Ӈ'N o`V(8jmfcl -zdjx+F~׽*Zl̛R 3u4%xfH.Ԅ0& 1 V&xG AmPTy ಔa(|X' i%FBVwN`"t9  xUy_ ,-!߬J-% !EEY(Fʿoɜ8jȬTWC }<-?.k:^YKjȔ!^dQ}If.ؠ)h,)Hx˚h8%SU/EQYh o^ͼw&>ZۊdINܱC4{)Vfg:PUL5JNF:"U[ƺ`:Q`*"T6,E<PlqP4r ȭCZmPSFPWpHKX Y_aEh| fB6~"09!HXCձ(Z+^q6xUm^1#%PSg IQL_ҝ,6BU]wy=}Bb$f-Iqy|Ϗ -ri6V2UN5S^6@8ntav8k1[W S+&vٹAڹH .oϠȎm5D*͛7koZ59gcP߁5op%8gԒ[e~>OW9Nr%9h&MpP6g&9CޮIv#7jq:f<8Y` h^wMKfm=G^8ҞͶ4V(??p>ѐHKes"q!#pQɸz {bev8UĒH֝>ل9X?T3k&6ELpgPRXVF7}+c"ٟ[9£ȻUB s`k¢c?y-gN(3yع^zxq"خQ?ݵƽإܠ{l|<DR_o߮Ɗ1mP/i;gnۓ9U`_+y/MKKm7FKa#60O "K韯xe^>9v8+ \_hTYmX^2DkaK/vɤI{1Tx[pZl8;>/,,DA1 {! 5^&z il4#︻P4IǿRi1KX(G/1-L~A?s)ImiK_Ss V#3zM񪹹^mk/9rTU8}NZx9 ׵UM+H)N|D"毌{١U@5їW'ȈLΌOYȉxVfХ\d_7I$73Y}\^.M6w$/RV,+ W@ګޚ[X_/I1zu)2C몿UMbE),bV7a^y0[! +dfmA!qPιwKYVh5]. IDAT:o9C:hdA/'͖fsɡE=kH}ֶ~ҪN:0Jxʣ[TE3sZr*/K|44wRV@73VUϓLk&<ȖOr(ggRh+zVZ$ϔi_I2s*K'XAn+HSMJ+y8;Kf~]Ff\7R}ݩĨ0Fᤑg +/a Hm O<*B.G,0p˼v]d"b$ ,I?u?1Y3p*GUqq1ɅV!\aa~x6w-lyZFOl&cAKNFn:׾.3Vl-30vYUs3qPbW>ˮuH꾭gKVo/~$H V-u 3 |v^\s< `~CuI7k.F;ǻS`7eDm( v󒵯w 6iN2k8f@Qb Qp1n -2rgaY1q=x`, |?tQI.dpB6'l;>ֳ9bt0Y)ljƔC‹e*RKOZȥMݜ>Voqֱ.sl]=_aP 0qlxn^al}vp1Q2l؁XsZ553QWyvck 2GQ ]G2i%Eeǻ=H+55ׇ$3GuHY8$3eq]`֟3Aa/| {OWD`&C (B6I*U!lU4^6b/xꤤSer8lѼ'Nnxo6I3Dj .Zs=XI~=R|{慻H;pT"k!Ryi4̜훩s+e&묙d28ѧвeKmUΣ*K.3CĞT0Ֆ#e>8ElEںe+Vr h\eHnS_QdlAkg脔.FZ;2[GDQ-# U,%/,@> 9ݢuWW9N !r("WĠT1pS-ҨH2M/Ǧ 5ݖ݊J~)epK{}DU $y+={u&'y5f2?26 Ww$d0߃K*Rv9D jj 0,p_ VHּ|L#h̳B &XAn8kvy8hWy#,ƕ yWG2W\mg^6?І-#U.f<` D vh8\ o@E7;*@*,j:Ŏ#drm qw25`u9jˉacGu| J[| ް…be=ϵ/>6jFm{X:^,k۰絝)OXkO/*}M3,>I oW ,  Ζ7t1f5aG<2!oP8 %jϡu셞Y[QkMoG֥ 6rЂzۥY{Jd vM KN=9prd8$\d6m!ϯsvb}M ؈ɜLZyq,u\ILfj%%yLf+aP օ6sbT;eNB/+M> JkRώB+s,ޗFTc1[_W'( ,wa\E-e;(7lJ;qՎ3U)(֖|LND]N,Z Q 2%VƇ!2)S!syۗ v>ì ͛ qpDY_p;Nwnfe 5 qIۀf?ig̶7%Si٩w"+Mnek]{{혫m+es2cZje iwҡ!'>gcjrFze$VY:rkL65TáLpjaQ{BD~k*IyWNuvV1kK<ٗk칩jX܂p QJںI(Y8(>Ȉw6@/TtM{|#wGbcхFLCEuN5}RqӏJ֔D-:_bwb?IEHw Z|ܸ$H;v9r8ewS ^$e?rA7 !=\t'py姯ՙ61i c6xX'믖 jԯAZ```bb $uFVg~~b6\}^d7eLg5!}٭H VF{kzQQ,ԧp~,W&vt-_TV]c^QJp10@ *S)tf"YC|yRP W L< Qpm ErMpR!C )&,~ b5!κ`uoY<ŋG@f>fQ*mqc5?so[Y{HhԡE<ѓ8G'zTg2D#辚d]Fi왌 "]]kYCU+b&ݦl۷y%`uL9uצq6;Bs[86F5G#N&T~}`WI>}ld8~H?,jǡhN/W UKb#IfS",|ǜ4eQ6Mm.?+'B^L'UTpv!Gy4͡L65|ne  kbԄs0^Ē '[@:q'U ՠ*f7v!M%3̍[Xm#,iq~cJM˼<»r~aޗ mNSkd|CF??Kc>_lתu:c[gշ5gUOqݰkRQ-Ap6SF#}Īޗ?l5V _xh)Zy"蛵jl#VNcDGF<I؀+.BnOA/u៶g˂&T#I^L쪬:\'5&:ۄsPFĨG& 5֪4Q}f{| B>(ï /) Ĺs>G'{nC~2KsP]]RX(LH)Lݒ¢'ky^ٖww91Zjӳ_b|bUZ;N9aI ֱcGv[??3~6lbG5/lɽTy5|$Zb6B"_黩 VŌgԿ#a2 }Q2d߅(8Ģ'Әk8 3mőD4)`3 !8y F#g[!9.YJI֧輛HjH:2:0s)#6EWrn!Vw3qɪ!<gj%%,Z uC8=q=^ތdT؉eOto!ğ. ech]C>{QS\}j9?XhidU/Y"gcOU?Vf$"şغAtiFmx_TZ}ࠨ[W\CMe:cloe 5m} 85# ? mËRC^lP֛/ݗD"zȳ!| ^y15Nn$D~*ٖlLd">kzR+a0:X\X&> ,Bgį K#Suyl^ۥ% XFnIzJÁʫG8_` RŕRn00y=0P?]+ed&HA!($o$!jqBW.Q6d~~ʘp5?.-Dž%r\陙8Oe}6¦lȸI;tݾɪ|`anjuWw? ZjݺtZ-ax"7+3 ˉ@2w;6B>%F]cb[!=BtAmAj\K0( DO/wԑtl$&!W۬UGpxP *h:(L$6k@K &h ,3.%OAx[ 7Gy|!ˢ\@jByŲTa\NV;T".Aj^6D-_XerGxqʼn*>='zep7V/)JU Z˒?/Fx-?:۟ogk'N֜QUC Qa^$Dėx Z+Ҁ'5dJ0k2T+e2";zN}<:M~GX!Wޫ'ϿZJUr0Dandu#GyG 6FRrXތ i 1I[Đݨd ;وe:2R -(O3> KQ-ػ-u\NAtU bL{Two~CgWENmC˱7wBy}7JڲpTE ͚$r>W_^qs/3.W{~0Џ W'cgp1JDYsW;>jZs Z5Mk7EޱP+IL`zR܊$՗k* 8 t&A>&h`eQº?Y lPX) yc݇ѬՋpv|4H> \ѾuAjkD)URq_t|1v\3/ݓyN㭱/ Ÿ~z۴ny(eћ΃ɠ Nҩ45k֜3FYؼfs"%_b V~Ex./EZ6rMmHɑHDa8[FfX|]"?:E+Z ؀p0 + >sܒc +:S+^ m" Zº!=4<w#\Ս@%Va7Fq_ŵ 73'Wo4.-'YşQ<g7N^ɂzrRe*#IAAH۽GVMҗ(iE`SWA# e7Lzǎx0dNN ATaf8]U"+*w3ҀǷi8wWb$\:Aê>km0Ě߿DbAҿ9/1t]ĩI)R2⫛چ= DcCezI"M՛M>1Owj\%]]eA#i,+r\2(gD:̄GRp <Y %΢n |s{Is>=&>5Ey/wӽ!?s)޵JGonjy 6.T6Ownخ_m3fdM5B= յJ~Usaiò~_ۦe d֠y3ĵ7L]ދcedK^b xq8%!a8.dO'c ; *V؀a;F~'+{iz6.q`w/߫vڡ{K;0y0b)8"G[%,(0a/?|e5\Ǽ\tܸW7>}4N#`z_vTbLͭ O]թ4!uU-4wx2x(+qWL%KjNoUa@2ŬVl f9ĺ[enfqOQǻqdtDB7Dq'CmM;?vb^czg?,Dmf2 _⸑DH /(D@{ى\`ř?+5~Kxb}pQPUf[*`3PgoH*xT74~j Nh'DݖK\"2j@Rrju/WHK)h#7Q[4kZϻ@2X[z1蒕q _ 7nT?awWwH8SPͶg.^wg.9ߥo K̫-;vi?Zy}K o5Ƽ0排o8^)F##ɉg.&eBw.3`"dH#j"/!k ;\m!}K"f?a^?g(mVg &i6?csݙD ]‡0QL 8F _eueW )!,B,M{mg_& uIyGUA(NP^5^w IDATL m`3H\783T55CcVf=8O߿0:.Àμۅ!ϑ)CF1Q{wސa]Ũ1֗ڶ |ΈL*է^}Ħc[O?@5k2YCژ1AaO}xBK CN3 a)[PMB[#F\y5rIF#LRr+/OQa 6hT>Xǽ;PKc-lť(-ӨPh@e'uaJ#&Ά}W 3<$+ _(_sSp'mMx!æbOSmy((P C`a22CNmWߓo+U J3T[O8M))Nnk~ؿJ9WL¹UiS"lhayJ~a6q[gjS>`bٺcBq樘R(k ckASB6Ֆ߽tׯOW V> Y?Z|M$]$Y{D/koР:7GA>EYgqPߋ־tۊJr?䪉axʹIGT5\-"ȻGټ[C>F%PdЇ _Z'BlnKd"߅ K vf_0U1.6Xn;zMq˖"Q#*H?W\4Ammj*F!g hF3%G8 ߒB}Je `Z2\=H aD"`F=ux҂u5.ۿHrB-v*{fXԸ;ty)\-|Z:vMkY8"[f_#]Jŋ4$I}W a=C^gYm$_y`eVJrr}wRKG~{kݻwː/Ϯ?3I&Viڏ2F1pN>"Y zRڎ90MG0B\Et݈|e4Pڔe٩ɒO.J?(+X ZE*"1[ `sZ 0 CU |4^NYVLUɚ z,{*BC*enlϟO w>-vUN^kS7m`pNVm > 54//{ȴ5.{sdEcC[8 ;lRi/u1~u_y*xKium]!9Hc3|\05a3Eᄂ@nͦˏSmDޝpE Y"VyDXiZ^Ioan?WBhyL ;%\ls:3.' $8V*e͡7$CYH_E` Z@98!IcmpJ<[oYPiрd3͞eں2`eU u~>5d+b̮SV&MTPQ[аdi)߼ ?6%GY-}fUndm>Mwh*E:?#i41G.[ߎ4Og)R};YxM_1̀uSGwFE<8#+KiUJR46IߜC`ϖZ/d3Տ2nt#3׋]\.ұڱ AMG 5UJw]^*zލh9}Et"J?x6D%z[=@s7,+O"yÂʧDeA 1>wp3I`t*S#o;p"B`|;ly?/Nf.C&{_eeBy"+[5yM3yԫ|(*jn~烥I= %SgG쌨sƍXuIK@{;u2oAARҬ?SKOjp$-Y684`Lއ($*LفGPɟC;C9]o" ľWț3~–ird>R\e⑿gBa6e)xi qy)r Qlg-HN- 1%<*\щS ْ.p_HC  %< #v֦+0^b)Ԇ`Jb@6xh?8+0Ƣ͒߾ ]5[leeP*_9ltc'wԖ۵O$,Σ>sNQi /3l⟘pOXw[nފ~`:ʄ 't^zլYNjq2ydq V(M楬NWhF|uY]k9ѷ%}4mxqQ w;NbggPm<]H q=ڦ(U~cF-pk VԘH/9D>OgńtɅkF%gl՞#](ZD/hv8fi! HQ\Mwsl\';ᤡHBTl ֤eA>I:&IF憟In >BS>[hR[T˶j^+RXؑsl:4*uͲIy <{a߾Kt;kNU^0t)NWNӢy ےПˠ%9#3Z׃JWxOKuΞܱ z+6aHH#U0Cj˄,N҂ЧY * 6ZzU[ x`~[.•=d\=udIIL$z1 |ʗ(B !6τǐ[sLԘHR+h#c8ۻU*(#YŝX~ (̐%Rg+S7/1=<,U6" x0%eeM"F,Kkel`;hM 0B"0n/x.(Q-]J)-mݨ. ܠr[ݷ:,3&C^Ir׵*Ԟ@+]gyh7q_LGCffӧfHJ£K9~8vW? uZMXp$@uS KN7n\PMGd]q5U&K>0URY/8u>.#nȝ\pƭO!Xq6]A[Ӈ_>{# im4%p1yT9L\zbI=+94K׶|{>Й)^9^{9q'u)-Ɗ6"8H}e)2BgY#F'|k"ibGYp!I̾x`-G5,b%+"w b`:dJk@ Q>l^ PZoTzqVj!*.t٢3*h[h J\m5b_1GdirXbvJ:#vT,r:eU I.v>D"O1k|EZw-10"ӧr[l߾}gMdx ȟW׾U?!OeIHb*i Ѳfv,˽[ǿox{ ̓ _y/@@0Kbb*u쭚2W~?D29R7 /۸Ɲ)leJRrR׶߷H_ZdS'Q ncd5K[!'"cá1թS\Wx;2hE qr{gMӺ-+S '2AZy_r={qy"9,tf0rs-%>4yG.*hzy uYB4n^ ˓ۑsX13>&sj \]rf2i?6Ͻ8$/W暑L?^5LC7|IR^Gj/\ť 3} I~xVȇ'~ǽĝ~oǫ/$ /3hT,n̰S|C|<ȻXnW9*G>Rγ"icαd^,)(Uv a$PD ?\%!@ >ͧ~8sM3~W6图_bnbXI G%M tse҅=,|ҕt]YLA!\\D;=~1TK>t\#1?+g=7ofߝlذaQoO$eyFVldY=` g)_ 0z嫫5 $/5I8d'ljB\jbm),7OcX?>y=usɕXtf]vm .Vi4QreUŧ0C zq rܻkh݂AX[Ks'+5"M,cgF݋2YٵHCQ-uLe#&͞>_4PZ~k34)rz'+ct[?3iwy*D?Bp C%}}\\ы 2/m"*Nzfñq|_?v%K){>V?O W"< ST*d * *la%YX1|KqmHN"4XDFbfJY"wvJ8Sޅ}Wyk˖lLT H(Pu3p*ٲ8 %\6}d2ANc]"Ԝҳjj8'qZ<z!)-0Ӡ.ԇh:XӢKzd QhCM ۾p?|miyܗ7[F,v/iD+,үٸνY%U?0Nܼ,zaslNOSDŽ4NbnB\Ag޾ȍ#ʕ+/GںuV,õv6-Ѫͩ&"T'yRYfz.}2uEPrv$|4IPhe=m< u)WqZ w + rBR҃a2.F[KQƄxll+Yn2a/o)F4-WT+}4Â@EF!1 IFBHJD"%Qc&> 6i{i9QwbTpsdTڸ;65'Oqu,*P͖0C8 ̶dA.Yd(-aF _r%D2䗤`D E JmcYXJE~Ww\}Cw̝~Vz=4ɟS" 풔_fR;|硜I]:w)tN(''K;m}*Zp.:S"[=0fWʏ~ndw# (sր/ҪI`1}wULR3sIգg2Ŵ|f.dXdܸA(FiZ]7oYbWEA;h\6O5+iaoo}QGsUnMt4ĸEQlsG,wL# ^ƿ,!:hLeӂ],,YN:%J0 2Z-Cj{sSt,4.OB'X"Jp%-57p]+5@G\jh8`̠Nx5IM`= ¦/0 dh .x %]9|.އW+cطMQk} Urn vr#E IDATTvk#17Dl['|~1UVB[2e lWU&'R]Di۟gfj;<3vmsMH[~%՜Itb1yZƹ"o|Owӗp-8 {Cnrʗ=wmz3-6i=SoU5wzvن8J7r6wb:&\7ֽPK`(.?=D*GU5kbwC"JdNk& B4VZUPbŊTf?~<[lׯ_733suu={mSPVC.[#"1Uѫ }cΒTm<}8F65S6P#t3e_2=p@cB ѡya0l'|Ɩ,u;!T&eNLV05bkkMh2fw5ysݗ54fM.njq ҅DKw? -Zla%`zJ;2`m*#ְ)zF6hQ&.&7'͇f CcHvb@!ҥLߛfSs79GdJ56ydSScn\_}rۢ&ڌGga-F5Z9OŜvm{̋O zӆڵk+W̿`L2e޽J~iJgtjcQ=1v@#-K`*r"+_Yg-=ugC-qNGXKGf@JHb`܁JWutIN:?^NFpUe"D~o±ァهqk ԅJwm)H_$gnS2#tjy2ufc N%)v=aB0˗E!m֭}e:6߻DJB_hI.咿oT{~#[=v{QS ;`ՌFqvWԚ _0[|ҩr2El :}dH*>4"|7fO?wo0h֠:1hL/[hѢǰa֮Ycx}DhPEu9͌Xl+A/y^SL}G'-XgAwS[zj< 6gsv7QB8#2z[Dbӧ0V^qO0F~ěp8a38JQ zx3&#^01a9iݷeh*0b XAJBAt` AwSU(Z0<{rZxk%*I? BQBP?=U75BԪRDʶ-JY-\ kI+ >A~\9$<7sۧ6oi 1cK)&JxĠ Tj7E-3/UͲg˄Xgl\!a}CuVߤ0 |HK[лM>jH]?ϪVk{-"2$-/veֱÏY=Xmm#bΐ&->1ᒥ3g={t-[6444I'_A7?u=M@/cYT% H3* Ԏ- La*H̗C )X׿PT}t ԞTĊ{mqI6ʃnl ;k]u$1ٻp{C蓷 9y|cԭ(vU2j򟬚sA96r;33_B;7mzLjW)V{?4}1b˕*)myU*3ѡ툫ͳe@ƅ'qtrV-9Ⱥe+b_PO[W_'<]o]rAVIEw]1]a%Qj-ǐ,+)'Ϝ9XVρIzLd(+xo0xY"&JI+(*q>"h$QLTؑV:ax}C욽`/M7v9}{"nk|]nH|1o+T\$K+cT,dwi.&HBeD2 WҰw+ϥ ?7o6h~;G7zޟ?==yH %[*84>YU״sU1c]g9 &*ܺRQ{֕*:C7_ly)׍xn!+/^$%%egG? H6`-{)GɯlY#4Zv7IZ^*lK,_)5GyڏdPFr/iJ/ 0#f Ʃ|=w[)F[Z2>1 ]}ev_u*VQat'~4*Y-ݱOF0&يg>&湷Čafߋ1 y/LRre)7W[2 ŋfo .dPxo L} Il/3-!lܑ @mPPt[c9aWZC.s=.p~o0rZQf_N7ԳW+ʠX| *dV ų-^0ǨNҜ8,e\m 1s7qLF8e#:Ѣ?5O>G~kX>%fK Xɥ[Y?Dgl.ץېd۲=6a-j\d$IߍnH"~׬YӠ)v3I Ӕ%JZ&,q͊+D5k00{q-e&oLVFj~fUj鲚[뿙sVFqmV%bѠ V5 8m55qr %<>魱^;. 8[=wU*^c>DL# 4&Tݍ>{I1jƌGNIXL!s 93=4q 6P 48 %`!`؁/l>.qAlkt,1:Yma\lYG,(ڱ9gU5O}䏼LhJxS69L8gCilXP fk]]3VZ,rh }bpڙ"/]Knto³IǼW\r\l(vU&c=؃KO]܃^OtG_jZ^CcRؙ">0Ykggym6in77>ߩff)MK& UQ[ c ^QW Ne"G,gF)Ҝ!! tdC/pB k0&!E>}PB߿w-VԨ巢[Q?@#q4%-p{n*^eXDC # 5`1 Kn(١H U{\gkql;xWХp*7]4&zzt|UJ?[&%y0B Lz~xAݸx26Le4&t(̪7rt/o)h!UotQXd,`4?;.^&$Rnf0} QqMFYΠk4)4Em&J$c4~Eud"0$"+VyfsbR Dz - yVBFͩfI"Y@CiŶ*@"@E CENrەC0+YYJVmVWm:~9`!.6Es$ߺ:Ax~㗨#Wl]V;zTZ-R&؈kLsd:*K#ܹ[˕@GːޱcGzfس`N K *,2ح )K" lBX7@Z2~ VKCn|34)mIu>A~و#7ƍ7c>$s @(Z#`Ny>8 A8'[+'%([^6/+ÐozG]{Rw~1!?<4;$Imq[^>6{!e{M,閭,r^ėOI~s3$1c?+e}$韞oݺҀ})^Ψ3YxHt#l%3%hvD;M(ӗ^/qav"WXp?Bّ݃*λc8]$^Py@[Bɛ/V,9WKZ>)4 PK#ق #yLWuoz n}ʘd貳O$sNjS)$+gG,HWwtn_CIcdLĆ#62nɗ+ ҕ,ˁRGJgٷ*cVm=;;s%l\_gSO4b,ݓ'(X]aʖЭ?6&$i!um!'l|w̜NRmOV?t[wYull;7[?YE'V&CU.Iytʣ_{L?ul(E dU*z|a}: !2*ĽW_'fu>'MJVwW, /.eZxL}0ie'қщ<ԬYs굧oh!2;N@%_a+e '9V|}o0!α]P#%eŻ.tU6f̘޽= GU m(Hkn4Bj TdGu 2Ĭ7>77W,:٘_  Ұ3p8C J>X2#b9anзݵ !w}i){tM7e|g$-]I~}+ {'Pb!JQT;#@otnKBj$e (3L `Ҥqߣ 3%-METYH<$\,F :\X͞骸gaAw~7 hUz 7|Idތ,=L JqSfj&ܲ[B`!1A|1];ߢ'imoв>Io:ت'"ΞPdXЁt5 W4ZLv,Zx2鿉?/-GX-$ )@qf{x+^X徫%-|xbq d2e '}{~} &7ξk"_OWi irSY5zX7v S C'ʾ6q ~ĸӞ<$z]vlk;tʔ)Xos|~=|Qwc@  3X(+ׯyͯrݲ؂w>]8+WKnIxֵIr˔Tm|>$yo>0^˜1Uo"*m'#:@-,p X5W3:Id]ak{-jdC222 X# `./O3jr)XE{Lpo!J~b)dc q?WezZcyx)-:6.h&XUOse&4OɇG׽ZW.;88{i_Y!!!BiUfbf >\ #?Ly{G޵S%gkDL2D'U3rMBq@Ve7ju<%J_ /ĐM,*]w'B~#oܸ@{ UTU/\ k8. d3%R i9ag5LDZVkַl ڱFC:}b~O6k(|^ZWܻ+"UuKe ӛ%5Y븂< ݲʦƓt:,p|htX2ʒRE{qFfӧD'54)%!aj1hmZ*RbgrK2;q|㍭h=,MؙnЄhamIJqYX=;۴ಊ X.n?dM,P^5c1| [MKY޷|W&۝xaZk_V>* #As\~E)⿥ `zLjRlZ`O/UaޥtKHVQ'+q?wb17Wž[$R#F.>XbE#2Ne%8~RwIt@@@njH CtL.=T2YC'L& * wu-O\c*_WGĘ-4rc{?_'+o>1]j@Mi;k>0s֬V"D#le$Ā U$D`יaZ-*LRK4_Jȵ 0Mum@T# 09*iJdM\%q4 Vž5wSsСC]h B\5BQS1tL3y؋V##c߆~,O:XpWū겼nyL%埫=_}A~%>!LF]CV֭ 3Ji$]FCJҤR'/Z۳V \Ji<Ζ0LLb<}S_Eo8j,i7Y-ţx袷H kVG)⟄;6k0,GC|.^0؜\8<}=)i|0g5Qη- AYS˫k^W4*qgMsVJ(y!bP4UFI" xqB &2`jEqG<uo{;'ōׇyR4pH@HT<.1@&cO7m+4 &\&,رqO!`pc\(LY #udR19\EGO$Ir>߃؂Š +zFZX]ғ|kl<27'ҥK&& Srs?2[ۭ<#ጞ!? ]O2@&l?b,5EQ@/UmOk\y Vz\ٚ}B)omm- q] pgc4-DydY{|ֺvAtz*F(x<[yɣ77V}^b8EEٺ/:᭟=0/1zP i9tRZdEN{[6aV{N(!gBn=19 " xwF@[Zt믁,)xjOnAtg8Q螻 6K˪PJŒPrZiG͔ X^K G bJkF]O><7r1e GIEZ ?r5m (9<OJ T 1{.tH VcUdsu4ϭKmToFJany,.!xNț'bXVRſ'Ngp=mW rg4NLmTNnST|F' j֔L隷T=ʯ;4^>[V]+XǷO(ZaÆݼyG^"5T1v8r `iUśje&E, ġ$[K>Ԕ czXZoT$ 7%j)v4(2bnщr 50=qc$sá͇5jsD&Ʈ5γ֑J_ eV`HX? ;۶`ۈ%℄;;j8zhUs$@E}G\D!1>;Lsb䷕ E>e>W.ԍ-/d غݘr\R+D;wc0mpJiJ=n )80#Edn/6M6lL!#b鶧9&qgΈ%U=*nÆSG6k.EE4OK㠡 ,TV8U;w,/]3yڃZ&O^sq;†vֶMFF# PX U.a<x5;^NOopg}cE~p ~ yYwcn}қ3ZƍF&:99诃wJ!@>УwX|H'O^ZF6lg!>! 8ĉPkM/EΔ5i[^3tgH& W뷓fRmLFFggWH ".#ӒI&LMX9Hȉ+@פ2a #d )QsE **j&!Y ݇4hwDB6/)O_}+ᎉIj#5Q=ƍ[fM~pB_LH$7CIdq&~6`2#'QO<2rdt{Y= `eh`- ށ:&*$L]t]3gC@'aRBBBl4v׺uI.q Ȑ+Hږ]Xiwpim(Q/BBB:kkp Eb{ZYrխ.JiiS'w7 Haaٰ<P`bfQN<wOouHNǖI:k c31 uRd;L2_H:7v՘k(++j0޾O>KC(Q|7{{ :Ѷ IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/reflexion_.svg0000644000175000017500000001545712014170666025656 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/tangente.svg0000644000175000017500000001241212014170666025315 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/annuler1.svg0000644000175000017500000002371312014170666025243 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/retablir2.svg0000644000175000017500000002215012014170666025376 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_plus.svg0000644000175000017500000000725512014170666025347 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/representant.svg0000644000175000017500000004100012014170666026215 0ustar georgeskgeorgesk style="overflow:visible"> image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_1.svg0000644000175000017500000000717412014170666024524 0ustar georgeskgeorgesk image/svg+xml 1 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche3.svg0000644000175000017500000001143312014170666025023 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/segment2_.svg0000644000175000017500000001365312014170666025403 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/selection_.svg0000644000175000017500000001352412014170666025641 0ustar georgeskgeorgesk image/svg+xml SEL. wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_pi.svg0000644000175000017500000001324712014170666024772 0ustar georgeskgeorgesk image/svg+xml sinh wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/rectangle_.svg0000644000175000017500000002210112014170666025607 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cercle3points.svg0000644000175000017500000001374212014170666026274 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/projete.svg0000644000175000017500000001361112014170666025162 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/bissectrice.svg0000644000175000017500000001166712014170666026022 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cercle3points_.svg0000644000175000017500000001374412014170666026435 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_rectangle_isocele_.svg0000644000175000017500000001763012014170666031212 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/rotation.svg0000644000175000017500000002164212014170666025354 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/intersection_.svg0000644000175000017500000001477612014170666026374 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone.svg0000644000175000017500000004117212014170666027147 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/vecteur.svg0000644000175000017500000004165012014170666025173 0ustar georgeskgeorgesk style="overflow:visible"> image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/fleche.svg0000644000175000017500000001320312014170666024735 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/pinceau1.svg0000644000175000017500000004232012014170666025216 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/wxgeometrie-icone-small.svg0000644000175000017500000005065212014170666030260 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/rectangle.svg0000644000175000017500000002272712014170666025466 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/parallelogramme_.svg0000644000175000017500000001526212014170666027021 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/translation.svg0000644000175000017500000003004512014170666026050 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/representant_.svg0000644000175000017500000004305612014170666026371 0ustar georgeskgeorgesk style="overflow:visible"> image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/reflexion.svg0000644000175000017500000001337312014170666025512 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_7.svg0000644000175000017500000000717312014170666024531 0ustar georgeskgeorgesk image/svg+xml 7 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_cos.svg0000644000175000017500000001240312014170666025137 0ustar georgeskgeorgesk image/svg+xml cos cos -1 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/parallelogramme.svg0000644000175000017500000001526012014170666026660 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/arc_oriente.svg0000644000175000017500000001663012014170666026010 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_isocele_.svg0000644000175000017500000001661712014170666027172 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/point2_.svg0000644000175000017500000001051412014170666025063 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/losange_.svg0000644000175000017500000002464512014170666025312 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/annuler2.svg0000644000175000017500000002220512014170666025237 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_8.svg0000644000175000017500000000717312014170666024532 0ustar georgeskgeorgesk image/svg+xml 8 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/gomme.svg0000644000175000017500000001467412014170666024630 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_rectangle_isocele.svg0000644000175000017500000001761112014170666031052 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/projete_.svg0000644000175000017500000001361312014170666025323 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/segment.svg0000644000175000017500000001104512014170666025153 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/annuler3.svg0000644000175000017500000002223512014170666025243 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/masquer_.svg0000644000175000017500000001603012014170666025324 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/logo3.svg0000644000175000017500000017642012014170666024545 0ustar georgeskgeorgesk image/svg+xml WxGéométrie wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/gauche.svg0000644000175000017500000000642212014170666024750 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_0.svg0000644000175000017500000000717412014170666024523 0ustar georgeskgeorgesk image/svg+xml 0 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cercle2.svg0000644000175000017500000001037712014170666025037 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/segment2.svg0000644000175000017500000001154212014170666025237 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/cercle_.svg0000644000175000017500000001251112014170666025104 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/translation_.svg0000644000175000017500000003005212014170666026205 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/symetrie_centrale.svg0000644000175000017500000001510212014170666027225 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/parallele.svg0000644000175000017500000001125112014170666025451 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/parallele_.svg0000644000175000017500000001125312014170666025612 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_pt.svg0000644000175000017500000000746412014170666025011 0ustar georgeskgeorgesk image/svg+xml . wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/triangle_rectangle_.svg0000644000175000017500000001525512014170666027510 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/demidroite.svg0000644000175000017500000001203412014170666025635 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/polygone_.svg0000644000175000017500000001623312014170666025510 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/print.svg0000644000175000017500000000746212014170666024655 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_2.svg0000644000175000017500000000717312014170666024524 0ustar georgeskgeorgesk image/svg+xml 2 wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/homothetie_.svg0000644000175000017500000001504212014170666026016 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/image.svg0000644000175000017500000003675212014170666024607 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/zoombox3_.svg0000644000175000017500000001361112014170666025431 0ustar georgeskgeorgesk image/svg+xml ZOOM wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/perpendiculaire_.svg0000644000175000017500000001177612014170666027036 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_ferm.svg0000644000175000017500000000727212014170666025314 0ustar georgeskgeorgesk image/svg+xml ) wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/disque.svg0000644000175000017500000001036012014170666025002 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_x.svg0000644000175000017500000000745712014170666024637 0ustar georgeskgeorgesk image/svg+xml x wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/btn_abs.svg0000644000175000017500000001075612014170666025131 0ustar georgeskgeorgesk image/svg+xml tanh abs wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/droite2_.svg0000644000175000017500000001477512014170666025235 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/archives/demicercle_.svg0000644000175000017500000001257512014170666025755 0ustar georgeskgeorgesk image/svg+xml wxgeometrie-0.133.2.orig/wxgeometrie/images/losange.png0000644000175000017500000000333712014170666023327 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<\IDATHkhڙk-ʱdʼnH6āe=3{q8x䊪MԣWSEtUBW%WWE4UB4m Ju3gl$EôΛ=dց67'D`?0x79tqUS];JtbErp-!7=MDA@DALZ5fv :d j}b@Xg'(sxn\ǨoM/u{D` @M(ʊQ$m;PQ$Ba3IFnNsDchBĎ AZ}2ziXGH| W:O>hf޿%z5RbC|s xhrC/>Ϛ.V!BED!mAh gj5^bl~ V?Z@?iEQt JHi  V @/Tm%Eq< ƱmΝёjzXRtqK`oƈuw#YmLڙ7.7R^|g)gTlb O="Añf8%gBVr}]<6>>9o~=~^{4DJG>2?˗U0yӼg7f|u0Rah+Y.LA 4 T҈GCpnMh a?T r[w M7 vhEEuFOeQdqo']% r HEYRGdLl|Tf&."aE4rZ&kV/ezIt.tn(j_^wؾtOn1\e <W@J7 b?>fI1`-.45N}=TKWf)><%[ʾg)t@Qs!wZ,deG֏M)hRJp #BhhM`Ţ2T4 J0Sa!E"N;sOZd X%+djsymNU3l^Rтyrj-e6ZkcQ]ǢE]_w3W/e:Vt,ZyݫNs}z]eQ-e<@e>ژρ#7Tv>~׮.:o h[HEi]<ϭK=A< DZ3Rױ[47fBy8{>Lvnm[U|uHrIwdU l_}ǶR/ph0ר' Ffg׏;n>Ɠ cl@ Q~ O)upK^9p!˧xSS62QC~M]"=[49H%I!UE੼?N0"e,oƹn@ E6Q]"U7uG0瘸2/SR'T 3 `\TbwFwy,n"rYhNgF`kHw{.?Cы-).GV1Q x ŽF >vn܀q- V1[o[UG_{2^[\A*㘐Ky7Vk+1e 3\9f{k됒p:'Oa_<C 8E6Z"4E_Pp wӑw ܊}TMOoy0GhZdJZ6 ]&HeZڌS[s7"`X8?=0$!x(aXGbܹ!ƒC:(Noo ABn;ryWϧ@=ǞKL:1fSOv^gw4`~}57`FJ1}FS0yۊ-1Q"s\^|mO {SgFm>w.KU Bt v|}QeK=vu^?h- -Y[cwlq닖`ܽ{j_oϱ=@΃aaE&y8\_i /X`mhaoBx;Z8t$jXO-Pd꾗96`ɄTn{Y$$Hp4PEW>{Ƶ@%!HX]~g\ 3+LyDq1C?>lŜBR JI (>٥K ;s Li ׆F5騙3P  ew1!|pG߈~I 3w#N}Ȟz==( P&TWi )0vB clh~e/pg̤w6>Qz)(DU%HUU'Ɔ(m?x?(GkT5pn/)EK֬`Y\^m*Yu+ 7J KWXRg{(,Ib0;a>u/Ժe; !|T 4,jL>yj{D1ZI&hRy8Y# 8n !ET9@0*_d B Lf [m[ڳKmKÎH"$Oy^W=tojN`[BܵSe3;(ZIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/interpolation_.png0000644000175000017500000000331412014170666024720 0ustar georgeskgeorgeskPNG  IHDR"ksBIT|d pHYs  @AtEXtSoftwarewww.inkscape.org<IIDATHklwfw籶,_Ip$qb@R_**jpGBU@iSCdZQ)H "j:h"( hT1رxo?zvw,Qq9sνBJIRC7]]3zt5S4]3uT/mxQE۱a;Gͧ)I{ /{?:`= $th]3hCHH!joWbWika@~q`{dtt '>'JI3x GEM*P?J@xq,h`zjn\M(&h'-*QdTvDϝyS(p,^Ja; +3,pOk|&fJY ̇ɼ(.l݂oU6og,>9< tVGҩ!³S(~{;JΠn׶QPl]/^eKݍ1 mC+'Z"buiάJNRD3j*=cGͿr0^s?%pfK:4YX ɶy[1LºZy氕8O})V SmqS>,n@}mh]}볫 {$=@3l)6/ugnY.mIӌ5_!TdxrK 2Iʵeߖx(jL:Cˬ7Y0m@q5oM@#p ~-7-H P<µeӃia}cGRIdFu}cҊW^/oNNsIwm[1AD`dl5L s3 ]pO?6q.]/L /Aa;ؽTΛs9L@umK.O5qu8,-)@+p~Ռ1~`;V@$e2ku9w=f7 c@y<= SGoTp{[ )bJq9QgdhRs,#h_EʱY?2Z:oZ]c1 <? MeQ⑳&%O ǹoבxYQXfFEXh$nYI.2Y=Gkꂫ '> 5 CCS#L,-~:^>NE^qW^::Zj1uI sq\d|l%ẢqYtڪ|=;;.H D8׻\ \ϥb Rж| T2,[rnޅ5 . ѴxK/ʻokk yg$gN/xl%4W,AJ᣽.J2|mJPI[2vu!4ZC[M=Rh3xdF$ Ӕi}#[. %aҹ5.+ 7ײ?scvx也<ÏnI|m QXI#0a+"n号XzZU'8}tSs?ڑ^V,c[)ǭ)yV֢L.k:=}׷b'VRjsgx[i}tۿ;rŲ\п▋)I{в Ip_BׅcthG 4^6KhZ.။GXQiP]\⢅Lqx֥-Ye{0+*'gFv: E4rTd?TQ@WYU(BK"^.DǚE&=tFsY5~%Fvsoi%vcSO\2ͳ<;K&+Y)[\p5]NDhXl8s w!*V S6|35ǎ&vM}cYm!0q+Lj#ۿ1?KV[H>Γ,ooa;2z-gOWȂUD+ }M 8IWq UkUetk/72E$ImOmƘa_oIf?C1#=ݏ㞷|FhOӈK&6=F iPq~'o$on b<б/kÇIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/point2.png0000644000175000017500000000170712014170666023111 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<YIDATHkG?3{k .?@\ KWHa7FEpa2!nT ҥ2R#\|Y͌ݽݝ3μy}9O&j'KVȍ4Q,Qd"M@tBsS,:7XD ;)p6!au8|5EaхT2!DV$`6waj)DJĊ3cѹ=ֹY`)S`k٩T`D(DcUk[0\#?|Z[A2Us^ L; .z6i1L]|}0m/~g~US4IKt$Y@5鉮4S;;E˛_]mDǺy/hyGC&V _îe)#}L_{;'p<9+ꍋvg'plTvzh VWD>)쯽l1EGsڢ9-"!Kk[H%΁1{9ǽ/ײD=M5*㪏m3:7]:?XQ4yĺ o_t)dM[ IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/angle_oriente_.png0000644000175000017500000000244112014170666024644 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHMEU3:,ȗ$AQ`bd# (bk0nO.V`DE $4./@evpktgbB%3^^Zmؖ6g9=rvrY#g;V]𔋟|_ye_g{;xK @l~`)p|b>˶s-BG0 Db+j,5 ZAJ01 U;+w uX)043iM[f͢(LX XYi;B*eJ׬R+` 8NaLj y,$#VP&@I|@Ҧm-LaҌmF~. Qt/g`Y#K. $s^/=5J+06 ݫjF/[o%Հa|Ę=YVk\'7FMOBcF=S|x5lՊOT:x:"M#n< DžO, P Ȕ-`ou%$]o7fRi%Em'sߟfނ)1TgPA47bXb?l\r5mbwDXR iJE2v$mGI$ٹ{/1S2S*TV_~b҉L^ݣ0fJН.ݿ-8Yz{Cv >ޛۄ&TRF E_ٽkwnwS5)z g| XNzRvL㫯Oq~e_ lݶ/M-`#hp׻h\obԨRa*I/#е0!@os9 Ķpalma8EEbX y*&TMf'pf^HM$~dݖRu!IU41<_%=&O#ňTNJ}DqQ;Ȳך1k,O.G%R) vF=DOXgtvhV=Ib(HFJB@Tp:b!}z_B?q) )zn.ͫ} MW6!ktPOCBW=LCҵ V61c*϶qmz/ # 3|nm=O' VB"Iev~{iu7?-o>% B%Vi6OpI;Ȣi8Tw@}8\jo_Ǽ'ejRb;w~̸aGgsDSglK8 t9^ݿ+̿e[EclIBh[l:*~VFZggip}a'~>LKP:-R,-qJٓf$a{ 44h;k:m)G (tU)GGhe聿A:]5ldchkCL F%dU") ?C?/{Aꅏ}O)'ЈdɍdkiD)A^; b-jtJAU}Pl~zb[QR0t,nye9֕h-QI[/@dF* #']dd"QD%w}pvo?5yPYڌx׉CtWOߌ:k;@EAL[AGvgt" AaF%Ba&w+ 9:;'TQ@]5ohL.J}b'lmDFoDJt(̐8H'y3j021RJK2HI!@mkdlgMU9 4Q;]arx7K]%E8)zᨾ_-ڴ6[YB#Tc#4 J*#onkIrGUR5=9@:[3o:6*Ygr f)J*bQ'Rn 6>H]SkG\)4Ol(i!5zCRTFa"c mElŏZgR50- pdA7ukjE)~·YUow,Fľ 0{,te5T0as!OfK4}s7]Tq>J(CYX|Bf pQ<)m_5K,]LWOUVdQQpb ^t#u6Wm_`h^;LYn^x @;uC>yĮdzM,~e3m?3\jo<9EkgkJmDCB+]^V5xk߷ӏ>;>p'Y g09A%=Gf?xr2~K>ܲk7b]J5w*?$eYru&TY` _=5ns.Wtb?yh$Xyҽ'xW2uI`4IJц7=Xm0#v/>Ғx|ݳ sEO(3YjC×>ՏU{L`nBxG~D{nvV>D_1f@>ZFϩi N9uS|r6̟?#?*,k0 1P&+f"o xŦw4֗k'NNa4r,} ;kMula ejUCDPr8Ⱥܢ~樝=*MR?fr9OtQ>=|F%6s5ULn8ﷴY{rgbr8UG/jU~,csT8PQ|쥭REo |<9 = Ni[:GzBH#RrE^%5T42!`d0@c!)k&=/DoEB\hmPJ6c=vJYVA*R|Cbhz\.MZtdR6?rg?;fW+|ߖ=_ d}&Ov_;zBl4}||#[ojg3Jw𭍻5_j-팾O8q_hD}R)/fy Zu=sg'/suɉX5#!X梦) !Ē\OcXQUDJi:Tj6NbWfT.E\Mc7$/x3 r!IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/homothetie.png0000644000175000017500000000236612014170666024045 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<sIDATH]lU{NY@~jK.Z R[#lhG'5QI1M@[McZE6lwf3bI&s3޹}85aٖZöTmXԒؖ¶۶$QK!v\=^x /jE-uٶd|XH`*A6r!DGVwoI!J hW:z@ENtB| 8ekD#*tҽCn[Uoj5ZjhX4E?PBA^gشPJBh6Hr xLraOﳓddOje,f[X0_U8}$ o_ѵD5}P dyJaw$'.215jYl5)MS!+N)qGFv+_ebj֚8mkdlJ\БD;5XUGU}4%J Tk[jy@GZ/0P`Ĕ(v3 _ghݵ;k:EMTR- {oi۳g!R 2Jk F|z|\ ^eg~SJ9 ",s0'ɡC8%ZE%0a3?ϡGٵ{-:X?_Pa[ʀrl h|]7m%94`yf3JRpKԸf 2Kr<pK>Ά[IuE1CM/P]ぶVmԾyں8`"WsR! rN0ӈ;3sxrmPi by_i|EMu_)*B0 f2>TQ̚i]) U0 쿛Ҙ,6rTRA#K6^`h^uHIKm]s! }2C{~(y)+|"^LS`[K5q>='%OۖWz}^q\u\ #XtW7ȁ<IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/symetrie_centrale.png0000644000175000017500000000236012014170666025410 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<mIDATHMlTU+-7RK& # !1$&$* qƅ +~lXh΅nta&`h4T"RDvBgケ﹘f,8{9s(xuѴ\G ʤ(\G|]GvB@B/ G@8g FNM0dxxTb}[?8vvԸlXH`AC:H%B4~' bGs@DC>q9 k,pV/HAOw~vvnmZh! Xacۢ>~$/3ŻWشt3b0`Q)6WUiLr1,gn_`b7좳mE,cUG)+Y[*~o37/^`R>(1f C;Ȯ曙LϓtgUضlml'ۺF,ۻ\x 6f} S=[_UzmK-=ׇomr̀ UehX1y2m"U='#`ʖ(a]b99^{0B_@=7zQmb@%eP$/X7oBB!O.oi,zKbŶy|}G~y#;h1C )Vt#'j9 znهA>˅˷m͉fA(6ʩ)W^&' PN ^1g * foQh&VlDJCXݦgO|vd30a%[,)J{K,$ 9bJ&<4eSDJVΫc[@Jqڈ4Z[iQU1`Pؖ//.@ Z (HY!hFAE}hCI]^ #J:DQ(yūQ˶#YXZ3 Q1rju箣^O}@Wm&@pp_τ~iP.\%0ྞ g"ĢY IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/milieu2_.png0000644000175000017500000000233012014170666023374 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<jIDATH_U?gf=gfMW$ KAA&=EI҃n=H"!HXXB$h>luwg3w޹8{}w~sc ;JzJ[ztQURzMG<W' ЯiOס?G])  0sϡ!}RJwJIoZQ&"Q$H#L3,&`[.F`&ZB`9+prRb!C =ncieq5g_½$p^'V=,* `?˲E4KRh9I#ת]:w~iL6WFhSy%*0?\Y~,j?EߺDUR+t7C+=F٥-B)=씰^i,wW>`gOz, sG?Jz!.#TŶ.Ge }b(v̸.fgb[z3{=ov6:APRXm\IP^.պ28GFp|~vmEǶ+\|De/Ut%,<߅Pcuz'O~jÛ `e#4,@D;X']&ti6}?ōm7Ka3(Ol2Bz}4 ] D;YQ7e:ׇ铌|6G4/݅a\J7-¨tYܠ^ݐ$-F7 sg=pE>a1Ӱ{-6 ;aac6aa6}CBH$$$ ʳUYU =/6R fi~ja ~ڔ%L1F Vr+0"V $4 )%mtKDu{0M{VYդ ㌅f80=WYhhk˰in#Fíuj[=ԄH#s<~!OpcW]nRoQTyoecv݀;4lt[{E~)4l4M[.,5E(Z-589 ƴaEr/g{\gϩbՌ}[wm[qm^aڇvm\cǜ؆YPZW.1Z E! 6leon<.uF]X,4MϏ8H)J- 25a&ҷRm?CӴb]T`b4ss&`uD~EhxX z=w!b,Ҽ?pb"D0}w Ya \!>}?ۃ2c& Y;O\ZĞDNf>VP,`h+bI,roO#l<}{^ʡ(ȰN}pBH'їUs $ EP8ۇ}Qd j43žg\eru`鸏,sX /b R0tm2ˆ*/e`ZoX%(O#ٕTxYBJ'g%$ +2D"?RKypFa#Lþ`V]m񪏧r{3)gsLJ$sLJ wO:90,!IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/fleche4.png0000644000175000017500000000263312014170666023207 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATHklUwnQ5ȣjL(CB%i""&(V#DH&H$ BIm0JKgtwffݭ9{̜?s`CL[vl6mѰeڴ%-1l Ӗ% :p焹5-;NpsžkG4+sN`)87b^͏)ӖL[6TUNT$R\P*R[QuA"!:%w8@2@ Hdi[Wz^2m-PM¾{ٻgdAnDrc3fXJxkZ>!0   ps$eil mq`ߓ=9]e U<Z@,W 9a *Ae˒zǦ4Nk1MwVZ!,C0Q%VyyjiYatv>o~ɤIwկ8H]./xlR ۗAjjj9`p̆d``AFGGylBjb׶FQG ###H)I&,^aTTjY [xNP1Ka#iGwZ׵QW_Ǒ#_ GӼpKiPn q"ύ0PaȎѺv#O4ĵ?2az.tqCF0Y!9A-⛣XFr&߿7l#Hi 8a؍'+%M#4bcD$̚ M1e7=Lp<۶ݍ4jèXBN^z7ԘxW?3g3lVa1وLz';G"ςU֟!Qv|.JOŘ):5.3ޣ ]oxNq+"f-^mC: ,r|) رL=d;k)1`M/G"_Ku6;*c:Skτ~>>L]"MWf̴eC0bq\'sK>_wA{.sqF] =_wr)ZIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/reflexion_.png0000644000175000017500000000276612014170666024036 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<sIDATH]lUU{s> BCl)!1>TK2DyAH@|0 CH0̌'41 /6O0UAD)m-sy;9kZ,a`WC]k]EkW{h:.>C0̑ }a.ga?;v/@o钮% YG{=Z;cP|S`N\+iAh}0QV.P6* 05=zQ,` \S6 m5Ĕ:WR(m<f,0 0hFN4ly*Z'jTr )23B00=8?{]?ya=kowI2&1l7~w}Kyfoضn8:Hƣ\4+cn{ ^eh!$el{;\]c@DmBNSBTERe5='DQ`0룤*{5\+^N![sFLsD0G4 `v^LM}5]lHGGWժ91 /Fi}5R^:W2Αÿdb*'~Vg$a24vxΕgdy\8hm[AVf}( R[8Ȼaz^מ" B*>-FGc[)⪨TwcL<VSo\Y6I:ylv[|?a @>vSAT<ɽǹp{p+;+,k_3dah5R"]cߏ1ݶOXJ Z'Y~c|5zG=) sYR-Ja>"!U(E픃T i| #~@zbl Bh$uF0 u,g_5|s7]rA%G,8+JEQ!"ulԉ(Uu eH!)UqaaýN:TJ{, \Ko:v[k+a?+F>IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/annuler3.png0000644000175000017500000000235012014170666023420 0ustar georgeskgeorgeskPNG  IHDR%-sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<eIDATX՘]oUgf;JE ! Q@(@, (1 $paL^ BĂV4Β̜En 9o-Rzн-;"6 )ʏžJ_9K _erGk7ظTl2o}mszne%d Ub6IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/demiplan_.png0000644000175000017500000000271212014170666023623 0ustar georgeskgeorgeskPNG  IHDR# ՄsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<GIDATH[leK[Xh F1$FnH0 QBbxyHQ$O4c" JbB$ҀX@Hir^vgv.۽B7ݝ9/9ߙ_H)5;Z-^iKXeư9 qS8N2sw_~u7qֵ'r~[Em]D)Kec%f`p3&7}ևM{R-"(ۀ%d$9D@!@H2$DU54UЁon:8 Eŕm-;`Z6/a%T^M-Yl53^o*R ~+_)J+V#OȀXݱ. P 3 fűL;ިbόFp8j2 ̼Ǽ޿A0uhp2S.a.M%. ]!Ί*` 2THe3U IqMc=N{Ċkr}H&?­Ȼ?sf,Cٽ8&]71 jaQ,hP(2v05S8SHWh-6+eòQBT[Ew]^}'GNGaT6 ,UͻRDip$HD" o x-'42(1̑>M3e# N40nRٙ4cQ M72 +€3>J*ñ%4=I3tξ- S_?oR&"(GnƾHٵ#U>+?Tp^>U`lܼs1pz7_RexXvu]'8:" Q+J]ijh] 2-LB$RRK-`Q5a`Yv Бq0,V:m M8 :zXdČ C!k]d iN;(V2Wl7,kBEu]ڟz5Nz=OD"tJYI$1 Atxj23M**u< Ø"V +4]m^g3 )N4Z @-Ȣ䝳rERV m!5/d~tŘVS)ivp <|7OqlcRU9|ryoǿ}sey2e~"S0ӞBo!&0 PbfS42@0JuˤRzfgrb_4|>"w]|uPr?-l*M@(^aȱo&&feww/6t7=42Z3t߃Q7ğӘH^C6&܇$61wT>eTm7+Gȍ ZzZW[<܃j .Boz>6EDA@|2i2=k /G6?S>Ko X lndZہe|4 IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_tan.png0000644000175000017500000000321112014170666023313 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATH[lWggfIiqRۄG!u7J<6xDy5&oiEaT4N?-;Pњ s6uU>5 soTMXjlkv5WW|flSrB, ':Ҍ9z?l.@2+'&I{+aPez {ś<>s#SczeN^?/-ָ>^ Lh0@XU.S9=`KNa탕QG:f}cM]A^"A2iK c-4PKX4KBtTWy(:i~ė5$t3T-3z 'N'_=98yϺr@AEr{~38okn#0m^B\훈-?ޭa9?? MKOdGrk9s,{_FVv0nV9wp7x,MZ +x,4IWO]}- Z~}Eef]hvQEiQ(=ϱ !5c-,Lsõ71ʋ<$#54KdFdS_%@%b?MmfCm`zb;S5LL]y4` RJ#Xɘ76\{F,W쥘J!?麼ʾd}ڃ+XjĬִ$R2Hp*D"]Uddxk04߽l7S=9Y[yE'yKT*/'>.\9`ًѺo0 lQ$CEZ6sqpm<\NVU`?z"Fw@*dIL;xl hfRMec:b:.ddu,W60|oA3$Vp^4Ag-rbV>xk i`dBY=4@W3)FHp 3?ɱr@x n_c0(ŀH>r@* dB_$aK@SH՜ȓM獦/|ViE:R#FiMjTH4 T4={JwMNu7\G³2ߟx/tT(#9|_V`%<.EC{-7Ŗ IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_mod.png0000644000175000017500000000330212014170666023311 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<TIDATHmTW;/e]\wq|iQKKA[l4[SKkl4~ OMcMLM%MZ A^pم.aw̝?yY̹9y~s-|FO7pif/p9F櫛i/po4 ô110k2kĩ* +x5aTk1{9qfô/U! b~0mM&Qѝ@òR"lX(&JKIiN01t JNgR`X6ila hH*Y'f!d:YӢⅬ)pj0mr9P@ lNk@a1}/m˽%>>>lİB)n-V[=_hz|N*c21{ʋO?@ar2e2VO 3V&C [@anTi}]7Qƫ{]B$Ws-whѡav^]Ovh(\ `,SEL.{.R@TAEu:]tsE] A5m)v,NmÏ˗yf ?^EKٺ(A-~z #][)@ ۾rc_Xn_]&%@ؙ>n/L1n*34f 1밲ǧX΢ gŒٚCu(yjA QJEqRE OHWvLyN r-sX ]X=0˂ 9x$OSF*UcC8=dR\j6{7银R#^`;,oMfIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_y.png0000644000175000017500000000223312014170666023004 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<-IDATHAhE;5*QAj)$RQ z$H !؂"RCX!!E-Ej1"""$ \,,ZAdv펇}o`offYao8fcMA8^SQ="bQSq܎I&AÎ1mYm,kc1s'# p xN+/}1 ;:p%VpMF d /̂6c)"lS*~I@dM3c(65eR -z_L"w˰fKh(坅)"vCnDdPx«uϕ^87u)Utp;V9d0T t!:apl>Ddw6$yH6o^0  *]}b>j}FDASoEŸ~& [Arxғ}Bg0~pVFk Ϟ|6WSNw<,0d7+JaRإN>C9sFJ\7Q  pċx}Y9`-ѻ JC'ćƜZʤ\[E[ e$3o Kl=\u>RZ@ ݾ!y% tBY6:rb+HQ`(f 3<\zPT(]%AsĆޢt7D70m}`X-.=^BAz70ڒJ}xSր9aXEA4_1k$O=ylzNs~WμujaĚSe7 _9k?'[;1e)-î-]#!0 0̼ZE{w_Gj:#'xq!L aV:1Xy])]9hWߢϣHܳe9N&q)>&Ld’D'~%{75QxkC? }4BWP]OeD i\$E c3af a6@9yk0i8mQ5{Ic ϼS_Yz۹Å?QV`1Pl7^*ݸbӹu7c$c 2CjE`l@גVs[7BA){ ke"ٚùma^T#kg Hq9,0&q6ԩ5y`z:o\Yz#뜥MLjģO2b4KЁ>ਟJ/,к*kSXF OU1 w*ޥ!Y($f)E:J"ʻ^I"]g%REJɩ3/5kVY||§>o!wN^x#‹s{'t9`HFXIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/gomme.png0000644000175000017500000000322012014170666022772 0ustar georgeskgeorgeskPNG  IHDR"ksBIT|dtEXtSoftwarewww.inkscape.org<"IDATH]lޙݝ5xm0AnI@ LFQFUBԗT⡊TBBjSZR?j6REUVi$JJ A!u+r`c;;;3}X^i4s9{ϹΈ ؽa$nji'ͤlZ*e&>W)=3S*z8EgڣTNvå#g{j,B`[ՍiU`'Zl(XљߝL9?vbaӦ↤Rj-^f։@@ tyv0^w8)oJgrCaW [ h 16lLHg?ŷ;tg*RZgW2[5,+}O -fb.* ?ر|je[T&?^) H"Ċ~}! / KM.ڽs/a7>Dn&&M$unBH1/ZTW2 D &/EЍǁZ4ma}]\YM}35rtI_ǹ:=T*<_;?Ɗ|rn^_ɐ폀XIb~;yȫ9L@hfW:R(sSq ~ yl 𖗒V22B/%JKUXlj4vnPӻX6<5YŪ41?)*}8zIR'ջ#6LǷF鼪[AvT~L%6I>һx].ULnhb! z5nytݦc~Hdr 2fWEChW(`W>!f܏Ki$mteh{I͡T1bUu{P4)Rv|4m3|KڍYji|onOezHd{ %ε4Ʃi2OvRw{j|7I"u> kJ4NR@,t #3ҍpp.֖x(%c1H|rƕ+X]b_"9[{}?WT2R)}`YEgdb7^* w~W"'7,Lnvyvvi P.8c(}%n,7(7m?lX=~}IXa9 q')8hG_Ӏ) ]׉%'-q*O:kj*T%l#۫O'۫GCכ%e 0:R \8"S,F;b@ 47:kNVe5씤R|JN@y=)?~d1 ? e |? [ڼgIAR &ѧ0opu_B $ x̝u V89< p (,4.( 8lyb0f ~ŖIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/demicercle.png0000644000175000017500000000255512014170666023774 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATH]lUw>w PAڠ-J b$ <4AC|1ڸ%RP#H5CSٷ8C#caE /;a9YZ_ccMD T]pi{P·=N`e(UUzoCה@bm^NKH7q͗aK,DR* At\ρ~+_ \>_w[(bVΩ%jK8AVD; 'o 2>AF2+ `h9[J;# sgh*22f[&"ԁy zJ cRNnau.4@,w?;z KsDִ n:<Vvtګx]`MW<60P{ %E^biA'3>n`;/mp79QPy1E@OIMػo%チ^,YٍZKcťn,rQrk+KJW7pg80_;*M?dWR|h "rvۧ?QlNΟ=%-` JRHǕK\O IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/fleche3_.png0000644000175000017500000000305612014170666023345 0ustar georgeskgeorgeskPNG  IHDR!$\sBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATHYlE==}8[`@ N܇,Z)$D!W BAE 09$ axC X! A 0wb&đQ=Q,(M}k47^Cߺ#Ʋ,t,8 cA{9|E],`!.(H{Tێ4w_?xx>`sahO#4 MP(RzQG/YW5ju:X庑')&G%N^J}.C>H_W:)#L@էTGh80OLѷi$ b r|q)݆c.j 9RU>?ALmki8oc;(;'!C)jqfnC*Lab9sCgö[ٰFv'6*Vh;aa匢)4yYsFE¥YMYѰN (I&Tssj*eV_/zۣnȊyb5::2\ZJ=ΪЋwpjeha4πa;HMK\jbXWѓḟ޷Y}zBIJ٨a@4ɛ{^aQ>*soM+dQhzð1L-ôJ '2SƲXNG˹J=Ի!T3M]G Pֻ$]H ϫ Z.8СCTU*7mem"wwQ(ARn0yHᣌt&T*LLL0qCKWbNlgG0_$PB<Ӽ;յލmUR5$[&Bԃ@155kxmc:;vn哃O4lu%=Gʄ-5̆kVEYTerr͛oa:c TFLD  =,$/XȲR6{ks@7v?sĮfK4EN^G(aqY-7q ffi{^ex|::+v-azϬ})_7E IZ,'R~ڢ'CG?-tX4HDYEX~@#c:Q -#gND] w=mR%+n4j>bGRHl %QG*PQ"DP 5#2>Gsq? w]ί?ecUWz H˸sG DAJ]S;_wܻ:$IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/bissectrice_.png0000644000175000017500000000277112014170666024336 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<vIDATHiE>eX].AŠ Ĩx $Ft"xĈ mbH 1hPDr ( dAvdVܽJ~^W >aRӰgY=0eZeeژFR(<|9x|g[]i} жx$`? |G Ga7 ;m*Ta@σ(ڊBW**;JE@J ]4?Qw&R ]r+z"{EPU#Fai`QiXdUWY:05_Xթ௾?Dj@Β-_sAcyi>' >Nq'((t?@MJDjX'tݳǹ?E=ivٌ0wˠ5Niӈ0`8bx 2$E;֭ᲖRoTa,wpMX=] g =kƃ1Xobט9u Cz필:R1@wR"sYݸ! ,_r+gݟSf݂ 4e&2S*JBԸnFk൹SWyq7K/u|oJs>B50 6M`޳<l9%=Rög,3{1W3nu9@.P$&BץXUT2'Itf V"&Φu{1EL-_Ì.R h#(ScfͿENkto}xmb߬>"<;F5|?B"gߟ]'%7R֫t XGO]x:i\[bjV/r&P {MiC:NtWP2i}?]AFKo#IӜ(߷a~Bh\ןvqb>"}W?Fjk#CW<@`hIc/3l"x-.^ u e52/0c{l\6H| 7 ɠRrn#H)#{}{@=Whxe+؄+6n/u QT!Jq8;~W8s8SN֎] \p,(ehL)U^Ĺs'EF_HI,rV94&L4fo7co.m ox h:9mH9iM_煯G+/Q]`Q6oyhhefl6AMƄ]zziIe+q;B*"{ 6 뼱g;л>Ϥ rF#@T1+1P\!BJprߗ,9͎ㇹ׻?XJZi vݰ݀@OؘC)"X X].%g Z8P?TzI9Rt /Lgę'% 8b!]e<G-vX*IED(NY%? "J*SA Z&v^jW}Iu:iN:J: ~$)N_ SIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/pinceau.png0000644000175000017500000000410112014170666023311 0ustar georgeskgeorgeskPNG  IHDR%-sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATXkl\wz_ql'NiHB?J mD)URJU!QUD*U-HR*R-ʼnIb'k}':0ҕΜ=wη=3B)Evlt+M)dZ&RxU9ab!VMb!v-jLyڪ'b;&@ <~j:W{½mIY%k1pH·vm3cz_(%uǧj?uGFGz:35ؖ%Sz"p]|  j:@2RG A̰zt!uKlSn$/bh8j՝[m7<F?.^h+&G@O걸@<EIJSQK>dқ@^XjH Yj~uIHOTҠx^RW,Xj@$*H j+7ӻ5kds5 qlǶӿ\}&H Zk5޿)F&dQ\KtBN5ɔiq.$sN6WX-K6MR8RZk88dѕγNly!Bdh/YuSӹ@A2q&׃(1D(,?4Cw17?%ʕ:bF !ܿVi %4Hktקgt=K,UknJpbeX? 'WZuAjw {OGOBl\L."eSarAݯ;oaw l.whJ͚b)ߣ^o'XF/UY*I=t :pGp!)e`Әk[K()eiA-g^71"(G ݕHrDyss;>+Q?),VgTPVJWZwf2m(([/Mz"- ĢM]KW,ʥ01Ծ;?@l 2v`}%̊C,AhdgK@;0tTdL &cݞC&ZU<ЖwqB6 9 !}6j,^>lBI ?,rrLv o,v*fWk}?.>^ U1xQǞ$&ܚFh0?DGg(B)I(3%-W5d ex-0 ؎P4x7QI2;s/ݟ!gv*Nb1ɳlJH)5R“J0ʖmKAȥ;G6cDBCI5Ȧd;$vy& 3;€%VۗۗvPFg֯3w % (']B.71Un՝IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/arc_oriente.png0000644000175000017500000000301712014170666024164 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHklUwjY>Ӳ/52 Tol-jq#Ū~@0dSP  ,&]H_'z6ӌi:E3s0-Ŵ* (T%;x!^g Mt_u|X qSP$̞a#hr] eV@5}SoOkF~-fwe3 (!JJT JMf75qt7F-ߦd]U5<3v>cxUBjB5).ST+u{T B;w]#Rq,s?R2oTMX t㟪`A2 (o]uY4 E~@݋ItM?IGi{ತ!4:~n8_Z9e'捆/ii?]5=llDdv #QՕ(K ^VQdU}+Pg"AQtzW+N& X4vܦ,<%}_a0lTb\5㏄>蛧5_bZ%.M?OwthF$=#Ig7cٍst!]yD5M ۨRd|2Q_PI4ʼ  L;CMUn$7_e'|ZL~rN Q'@px#bU+y]6qظIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/icone.ico0000644000175000017500000003144612014170666022764 0ustar georgeskgeorgeskhF 00V@@(( @GXKIIyq,1\mpuZO4]in'CMuy|mVY]jajcb\DQ`'8>zbTP;OQEag{ŻmrsRRRzaYaXdl~i[Xspqedtv``_zdbA[aq_ZC^olwzZRP|}{[]f+>I}ukf[rd_s[U_ilkehcos@QSpfdl_qRYYeacn\Vi\tl$?Nv^Xqhwuzxd\lehvx*7;oa^lczuc_]|d\dXaknv\T)ENl]Xt`YwpoetZSg\Yw^Uy`Ww`Y^jng[tjum{sDaeu^V~d[kcrZTyc[{c[g\i`~u[Tt\Ut_X~e\ujunr[Tm\Vw`Xu`YyaWe\mwzr9a muifQBqo gykH*)2V(KD$ O 63Y}|h!Zx%MXz18 _{:-7e^.4wp`NIl;Wc]#tjd/F'vn=??( @zr<*HOV%he\Vu&+dmfB`hwCx~fZUGHHuppka[aq]A^,8;QJXV¶sZb;9OĿ7SZ9Jp|~tYP17[NKctwbrfsji[x5]qdbcY[^Qkpp$,1v_{:>A=FUseyoQdgGbt6NN{x[fllxaYpZ{%9AW}RORsd_YlkibVXjsY~=DH\\XWj^]tm$17?QS[cffp\VjwyN[_m3L\l_D^b"%+"@N}x(k,1RVZ_oqv|4AECBE}e\/MRbUYH}\q|vlnqsc_hjme98H#=GzuqvkcbMKJwqWJG~cW)DM9W_}tlkHcit\U`p068LTe^cgqts~bwutqwGTXi_ohvr4OTkkkkkkk;嘘Pkkkkk;;쐘kkkNjv;;vkk嘘왲;ƈkkk;j왙N;Pkkkk;NAƈAkkk;7A|AA^^k kvk嘙x&QQ, v솘kjQ44<ƹkv AqTd jk>k魭q%nXT1K\kku 6azh#lk9R b6M5I/epmS5JW^ynjҫtƙSO"r]GYvD{Z` `3Sm yDZ)E$f[DnjDǃ@fSfe׷U*0ҫSf-Ʌ}׷Se=-HB.SFeCH8Ҁeצgs=-S :t2p}'LV()gc~ܮ+?с_i:wDDtϺ=H oB?H =t~H מD=׶D!}SԗDDSDD????(0` 9dWKFyrx0?^UloNgs|3RYv^W@aq}X=#nkMr38?K|oT[]]hf uNq:TBRk{coEn}AIOdghpVw .24tKJcglddXUkvvb,i(CL_FYYertznyiv{)Rh`&4:(FS`ZI?Fb~ĹXeg>Fe}sZROPR2}mnn}@[dwie[zf\LWr\v%,~2HP`Evy!=Hm]Yavz8?D?V\xtqq~f\tm3DdoioPHJZmkoywK^a_nqVw}piq+;@fiysuCORcRYu)2bbavz|hkxzju07m}>=@\WWrb`ȾQpLgmodh~%*fNyE@@E@ơ=@EE˂,a+++++ddE@,a+-a,E-b{Nīݦ,@sa--<||[Ĭ+as@@,N[[||[%,@+[wL|ݦw%+,@+I[[w%[쫬,s@,+I||[[9%-w%[w,|||w[[w  [ Lƚ@a|||[9 ɅHHH%IbE,+I|||<YkYwkkHYN-E@a||kkCC H{,+|wCCCCCSHSlYc<%b,EbN||~CCCCllSSSS c6wY@^B/i7nӵ`{Ia@@˾&_g>1e! P!mr IIa@@ÂsrrrrZ_;D7rYIa@@aIg_.OTtx쬫Ia@@@aIIIL Wyщ MqVIIÂ@@aIIIII(ۢ4ffQ\"___ZIa@@+IIIIIII3p"Q8zK}\\__V(՟Ia@s,IIIW_}zuJ\y__^ 6b@E+y\2u\\A:_#aE,X_y22_\Nց@,aX_\\J_\\___,s@E+X_G$2}_2ۺ,_œG$y_22_Ν_yҕy22j2__p0Z_EŶ$ņϱFj\",@†⇕y__uuR8RJ:+a•__\0]Kz'RKKppߺa,@+Ƶvv\ڋ"yya,EE,+::$v&\\&֕yyb+a,˂p$$y&&yy+a,˂$Gy&&yš,E˂a++=y__=Es++$_̂__$E@,$__++aa,_E@s_aaaaa,_ss@EE@@@@@@E???(@BɡRHI{t0"^,1d[sUkl_w]U$=WXBKukDu^716^q{}9V\|EdW[msJjIgczI/OaDfE`lz8T9DHimd9COe2vqffYRpp3fy lOSX]w|%%*w%Dbgm{3nZS\^owbIP1J`;Q|~f]sumxqO}~ESTEZ_RpxfbVM7FgxzsgsȾw,8>TNQt_Y?atk?O\$*"@KƲjg:Yb^ZXnol--/uZOdx,1DMKOae(GP6LRWJhnfi^]]jlJ0000000\00;//Svv0;/k}Uܥf/;0;/}}ܤfff+/;0;ܤq====ZZ"q}k/;0χZ+qZZZ"Z""""oq}0;/+ZZ""o""""oo}/;00ì}ܤZZZoP""G""""oo^=}00ܤZZZo"o冄Z"""oo^(ɦ=}k00ܤ"ZZo""߆"ZZ"o^GG۪U}k00ܤ=o"Z""oZZZ"o^Goo^ɦU}k0ܤ=Ɇ"Z"oooo^ZZZ"o^o"ooNɦ=U};;}=円"Z"oooo~^""oo"oNɦ۪U};0/}=oZooooo:±ZPoo^u=So""^o^‹:PXN^ =q+f0;ܤɆ""´:#޽P#XN^>WQɄZ+fffS=Ɇ"Ћ´‹޽### WqZZ0;ܤo"XP## W~""ZZo;Ɇɐ鋋鋋郌XX#> 2]"""^q0ܤqqɆ#::> Qڄo:}0;+qqG:؃|,7`u:Ǫk;+^u>>>a4 jp7 uǦ/0qN~~`u(>>>>Rw CCr(~u=}00;qN:d z.((ǃ>>>aӹVVV' Bk;;Y??&s_GGN|3 VVV'bSï;?& ZZ_sT3Jl llV Z=;???{{{{{ΕΕK??'V[lVVPǦ==U}/ìvϟ33333??FLL[3 :ɦ=U}//}+q^oPPP3{?ggA5:Ǧ=U}/0/ܤGqo{?O<<=$ARĒR," f+ uuV=3wa{/woFhزmt7Hw#AtqxHJ7~z8G9 T Re/?(X}_}0^ C@b8e7?RglHרBZ@=?&t=oi 4aS%RTCESX(xòI9VkjQ_e.[!t>XåӮxZóbQ0cz-p;,qqVaA%tbKGz&#ti^BʄƓ823N,l8l(CNgI0:d|('fÔ6nE k3N7b 3bfwe}9E(FK'/ʩ[2vy U.tz4|jy1M&U9|N犃l~ttT DaiV.N/13v7+3X ŵP~ S%0@u2K۳ɿ d {.C(k)UY95-sOazy vdJXWPczR>;uS\]8jH<"K6@,;,tGY /mhiK bnj> x`142RQ(ڐ9+i (wWztD;X!'s a&sB&sR+'=*4&p 0grrbXRS!h4؁0` q 4ݼTT$dfm,yXUꞪ79yhCܰI.+K\1HWΤ$EwzS#v\6?+p{ kCa%X-t"Dڐ+(J%MBAh`1ɫow0,H޷ge`q UL&$ssR L , Jb&SYU6S ܽi  l52 Ź:$_P4/($%e l.)Jؠ@0y*5jX>:$=S)USc]nVqc芹$9e3zPytG5>P!~]ݻ kJ$$i'zφo'tE5ֻټǟ&gO,LIV\UT6#wmܒ%bͺ/lNQ ٽK} Pϻ`L[QO{n]IHȚ2jkRGGet<+&.-BB^>w.CPPnQn7 1ǽ;6cWLƯdtN6rX{׺]``[WuciOoGD\s*0+P, vIʕlN~wb23NiFuf~>yp4}^2Op0v{TOȎPڿ2'UIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_9.png0000644000175000017500000000212512014170666022704 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHMh]E3gW>ҺZA6 &UT\ Bi@*EAYE Jԍ.M$Bjźpab{̸M 9pwsܙQ{|q;#YN"YH"=GH"!8PB IO3UGY{ٝ4sYf̮4d@\Z5lyэu3O~Po$4bQ{@ctY(QDб4s[ifg 4GZ"B D+ ,`cB V{:{s h/"4usn'2sH_ʘzpm^aO&DQEX }OP ^}V8⅑@}}8g\lߖh \Ò ȅwQPǎ~t%74Qj\@w__"4h,,-⿽Nm W!0BhՔv Z\hȀȼB>5_WA Tp,'gsS .ٵ3\R(P"L~y >,sgDU4br :BiNy`x{!R@/[H"q`-[siLWq$zC\I3f̝[<;>|&0 \~ oN7E`v&RdIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/retablir2_.png0000644000175000017500000000237312014170666023723 0ustar georgeskgeorgeskPNG  IHDRRf2sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<xIDATHŗmlSUwmU,j&\? j C/( L|1AS] B/M`""@"Tdunw(/$͹< ?}2aP8RC@~7MFAX6v?`PD|IA| 1ĪhBP1BJعn}&ٙ{9ܻWA{{Likefi+-1MҰLXfz~ b珺:y.hw  ͷlk0p3ĺ͔6`e[T h& qdS @iCRN?Kz6]ԉM+1T ^? La>R ՁN[%fZBP6G.ӺiTcs>›ޚԩ f (ujllF%!I`<}di9a!RF%ʶF )Cgk $c*"/kL^AS JE".=Fo5Cό[SChc 2'NQ!C#$3e.?vX2LTJj-t!{] ¶t0QC0aHpƤ[hZ ṤW:|bݪ"K/ԐR$]Y-6Y\yC˽At=Y1+/D)CV *]i"SHfW#]?6`b.hް-SD6R5u#<xDťkqތf{%xMCtߖ_m̻g-~6D45GQdPqONaA.%5޷6b)h*QMA\P)nA@)0bml#4fc;*fZC0HK5v]FckcD55)8~MJlO 8 EMh&MMb|/ιAEJ\T䋰K\ν?R=DâGxgB$t[se*VJz"LX7?` s"JpVG =`hQ :^HZ>pw_ 9sVE3 do ۃ.Ata~>7wpGgNT,*=:pQ2}IO;ir8VZxCR5ׁ#@XOJL?[A@*ARBB b" yo0tL|Vwi>3-뽆YUS>멧k=,',mE>`y'K;v1IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/parallele.png0000644000175000017500000000305112014170666023631 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<IDATHlU?{}ePpЀ% H4 dMe3EKLR!ƀ, h&ilF%-mjWʨةBu7=~>I9+)G-L9*t$)Gr;HB?Ѕ'"hQ=8;R ÅK`kw'RlO'%V!gZDXVhtH)?~-.R0Y (3Eg&o񷑣lz`]"騩m~`Әw>xF"8;t?-`!@¶d18As8MN#_LLIT!pۼxOD.!NcJ3V"Sգ~_ȑ~0p嬞N жd#81;#Qut-+_k0aj `m>w0m%wX=inW LLB }~σy_|6\u$O w)Lc# @K ??0I`b΢߿B\.@> ADض¶ [ww;fVf:ϭw/-ˏ&ڲ Xw!Ǘq\ZZVEHe>Zc'az$ߙ ۖ%p~C]Js5,R$87~eWsŦu!R{{:1_C}>^ ^ 45Ⱥ:1R\/+s+pۍwn^IݾD *zR!]9-99Oa}F~# Bm}y=4e#ș~püDȋ[ywu(rC'bd҂ybUW2A(hr.!X8&=}ŋLJwf[mRz:?`A~[7Nϛ ud7l)&N+:iTFr^jXrN}t 0 a" UQfѲ{;sq=3.WvB O04(0|&L"ͪDW]1 3X*'ecR?Џvϗ̟Į ,Xu5%i{['Γmv21-ƦrFŠ(T/,u} 9ac岜v݂Nָ({'1i<쩪K[F㜝9ťHWU KP47H)ץÎ';R|)rIR?E~?Ыi1 9ǀ@7t2V ,UliaIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/logo2.png0000644000175000017500000031210112014170666022711 0ustar georgeskgeorgeskPNG  IHDRn4 bKGD pHYs  tIME 83yIM IDATxwU搜a2C" YDQEE". b (`E "t1Ts0ysW ~9y$ !X"7UU9qBիElBRSSYh >/M"Ek~-[z(Y_ȑG#be8q!Cн{w ,ZjH;b wѣ| 2N:Qti$I\pG,bYVV/u֥O>$%%E.J#\.ÇSBJ(X( [laƍ :Ue]AJJ ?3۷o_~tҥKx l6[#qL4_̚5k;v,zd2t=UUq@hɬZp8PUѣG]^GJ"? h,[_|z覠zٶm&LK.7g4M^/k֬K.YGy/nݺ!˲q}L5)h!! eQUcǎ1uT8?L\ZEӑ#Gn:N'AA%33T)_1kXf ѠA*TEX |n/X*c1eB<Wfܹԭ[˗SNX_oAz0`z|~3gΰpBoNZZ;vd$&&" ;;}j*{.]lf75TUe޽3i߾;v EQ 4?sXgUEnWd EzKqOe.]۷ow(3fw} [r0|{JVUX,1eʔ{7ڵkl6EΝEJJ;vC } jժݻwyn[̟?_ iiiiӦ5NT y/pUBd_GNJJbŊ//j*$IdΝ\xӧOsEEaiMӸp ,`ڵ?\cŋ7O>aΜ9$%%3`ٸr tҸ\.ڴic=Fҥٲe ˗/ **R~Br1o<&OLժUywYf27ӴiSf͚U`TLQ47UuVh"1~xѢE !I$IBe+V*5j$:t kcoV3>cV+IIIdff8t)));11DOӧZ*?|vYp!zQFƬ{z1Lٓx F6mxO?Ć 4*U0lذBrjj*'Nٳ$$$пbccYp!6l`֭hт~nݺaZI#tf̘'|BƍykTdŊi܊)/&l:@q+?BY, nd1Y?~m۶qʗ/O^(]-u[oǂ h޼<\`*T00۵ksEUU4iRN'#FaMӘ5k6dx#GҫW/ tKH3qDl׏-Z*L4;vz\ԩS0akצE7:ufZ#P@" x U IB$L $!I$%MuVMƵk%!qt:)^8h`nI={ rJ#R#>>8{Kٜ9sXx1<=e˖5իҺu" .̙3ȲL׮]{oae:ŋQ(z)fϞMnׯmM8tjbРA& mٲ/¨YsѾ}{*T@ _Ԁ4Ur/A% jn0[$Vm r2e VC|GTT g_׵ذT!H|*6m@km}@TL&̖m;v Zs=ϤIxd˗/m6@ h_$aC%IE{eٲeJ 8pjvYlGyر=zOlcǎ1o<GE ,IMMRJ;bŊݒϱjMP|~|y`-`~TRqyPUl&:62Qlx Ul2l Y4mĉ[.}իVRNJA !ii=zW/Rdi}loZķ^%==¥s wTꜜ,͘ر#7TRPI x<ȠA ahu?ŧp0 tʗ+K (VLnn.cǎ5}Ѯ]QUNFF穧;77Yfq z!uV~׮]k |:u*p_c۶m\~;¸#c06U}:`J@j)Q I9z vĕ+p8\8\9dee{ 1H!:{@H $-,qngGҺuk,&)nT_Ѵ Wiz\̕kp8TTѬɝƲ'OYokf+,`R) f 1g0|ڵkaʔ)ƶyjԨQqm߾#GriMFϞ=[Vq9|,[RD FKV>@ EE,ZCrKZp8L2 @tt4G/4J*O1hCZZ%Kd2ؽ|g2`?}p[i^_ aַH:en~a=)ԮYlMN0[(>dB]8Bt"` ^/w ?5X(f,uOQHOOe9rIhذ)Z݇n/qF>x*\Kf3QQ1F2/^gxxq`$]ܹ3L:%cYB*W … IOOL2<3EGa9s#FOx!`wPU7O`3nXBpI-Zf1jԨ[v/e#G2x)>[o,tܙmO>VZmo$'">}sw^yuv-V~@ H!*n"iաP9wxm I+Ɉ xsTlVZރA~0>|ߙKRJ!גЪe;j׬g?g瑾 ;Ƣe YV=uz30n4oژwmǏg…nV+Z߿?gΜ[<7p$9iy^֜,ZdZhe Br!K:~6{"I<=r"8q>ÁjeKYy([tرc!8y$~!͛7}(ƶ,X@:uHLL$55={~z._L߾};v"*4n߫"L:?PK:0Hc?tgDۣy{nbcu<`(._-}q慂sQ,BBHETP4 86m^WkVQ% ae_[_ZQڹ'5FU9ҕro~bzdf8..g t| Fxulp8,X,:$;ҡ]>%Kp>{޽{2g5jz՚ 0qnf}'OtrIɒ\.<}>X@R2d5EQXhqsÆ Aŋ,ZȈ;2d+Vp߳vZ{1L&qsQc?3]vj"|tܙ?*UD#3MS^6"?B:(-H_Uٳo?|=vУ#<7xU+Wi"wd-I˹m0L$ M \勬+իÖr 7D|IB|m3ytK@B/&KLL \!+'QQxzwװsv~$N=Vi=| ~J'`lڴ=j6v3n8222ى|IPq),]_%%J$&&oO6h}=|R6hll?tp >Z4|nb {z Տ*ۑ1hD`NNG!++ wCnx*wrݑؽ{w5kƨQҥK$Iw}lٲ;vp8HJJqEܚq9N:ŁʢTRjՊƍJvv6䐑Arr27i=n[-CZhbAfBBpi͜wPBeƎO%S cQv@RA^.uf՚/Xz 0jkcۯ@!;7Ғ|s>1%6:6 ًgVR+TnPX[d%_ax^a6oąKx\!E\V|O#Iwٖ}?!$}gDIt%I">>bŊQX1jԨA޽y衇VEY2e޽{*,ȳxb .\1~/U#GdذazYjk֬!%% 2331L4nܘvRrJ! _pI{IGՊUyhtG3d9/f0;}}k:us1"q1,mwuI(X JI֍[:KW.p[!N&J0{z?4b >ȒEy2Í+|2S2220<ٻ?%Ⱥ OS||n5N9~*M6`)=y'mx/V:vE6֟dyQUag3gΰx'bXTRC/Ix= &[y70` TZ",;;3g2g|>TLBZѣ~233IHH(wχAL6bccY|9]vXx\3-$tOE-# N5Mc/[x8s իӸa`*{J0)ij;wk?7ߠncNГjOeIIK*IRZ0M_9s^Yf $Yuk-I+W6B$#?ܹsZ,Xzax<|Wk_ҵk[ !ػw/ӧOdɒ̘1exgz*3f >>ɓ'e7Iuk P5 GN&8$Fu[i-&իSS&LAy aeI gaK(d@eɪ|#ڴh߇FICVU?k֭FZ2'{Nz%} 1gp7-^%>xr挞g2x 11(._--#+撖v9bL& +\Nvv&'4l- Wj ˑcXb>MI&wJ/IL72;7uc߁=tlߙq/O"xqp[ٳohFR +Tg,p#ǣa2E37xޝghPٵ{'O뗉a4o#`vd YYq{9gKV-y㖼< J(z?̗߮ӾmGb 3ikݿ\6o@FV$Q^+hñ3,fx32?o`D)TPC_\ y]v)V3 IDAT `1Xv| ##2z}tYKH_8FZlGc?6ꚺI\]ϩ3Xz |0hf H?wx~?OTTMѬѝl<5Ȋ+Vf(X%J fc`%cp "'LfϞ`B׮]Y|97n+%%RSSSyٻw/vЂڵkYh=/ $ [ MG{Iޟ._l 8bz;ѯ>뗱ZXڴ7|5j@"(_p9r=:/N&w1i(QZI233XN=IV1i+]A@VNfMbO$zu9v?| t<kT"9N=3#t/T~ ۭk2|>oVp^Uv1=/]̒DdBWόb-?G:|^Pz^Ms՟t'66>l&dH(6k>ztyt t,ʹYbeC2FJ8#k׮ҢE z񛩸%KlٲpVњ믳~z5j]>N<Ʉ hڴ)nfڵ,[L>5jT(xGřph">7BPdiƿ!\r o'1.GΊ>K B@ffsEUŔq3QN=~0K?MHL,E'RdYW< y_|TdY{^ԭI3YglbÚ=\.fyCGQ* /*UNzKHLHdܣV+&ЩC7f++WX\1aj١>CppZkbv9wN֭oIX,aU7uLٳYdլY3bcc\'X$>11~2zhYNˍ>|Ia)))2elwo=e3v1{*{f xbu(P !pydee9<ׯ_ʕK8N}/x" ~?._-A/P^cdd3=M##+GE_z<} gWT9:$Ig\97{燈MnrǝpƐ6jQF<@r! C{4Y_* !cpWaL<eSGJ"˄xLj6G4%Pru\7Ғ/INO[ֈBkf͙RR5?>㧏r>]8G;̕ԯYsrkkh l޲]={ QP /_!55ώ%i!#Q#FM˖-~S|\[|b^&-vyp{^\.WX… 1b{6,ƚqfiJS5V|ӧȣ?jׁ=usrAnv|l 5!!AOy \9'/8R18ٙ,X2KW$E1ɸnV;w5k߇Z~46lֈJ EEe&p9=&::ߏy׷ETp;^E\jUx\9z|?$ѬݴoY*`D-** q)ʕwO:뎪m@MTUzi^.]uBRW5&SjJ^`pL<$PyRdnGfr)nG)CJ 甔| ĬYnݺ)SgqFIӧQ{AC81Sm:'XN/׳LÏe>C6lXYiHFF_>_.6It zT UUFXwώR*$ !$}ְj1%K&ѩ3Hf+ʹ+.,Mlڲ~bq t0u4NT(W9̏ brˎ-θBvZFSgL`FK9ԫ^hk׮x6'{Lraj@0;)~HR<^g!s0 Eh2t7C Q*iYbrH qK)2qh"e"~c|_>UT [&99#FPbE&NHJJvbо}@FM֭1b^M|. CA $!x<<#>ry #Y3QEixj:~h򉍍n}+Vd*%*Y99?eŋ- <̲ p< $$2X˥E/m͚͋Ͼj\ ^ǥwѬd9<lwVv{tm\OķFz|ʝZDf1:#G~aǎQU5n]I.!P8|*۪qq^~a5צj& 4_AN*mYHd-,[29Ãé"(~Y9><%DFL g8A)%?,Ư;)Sw6K+KsǞXP5-/SXr.\:$IqGkn5jtZ45\ iyo D&ɾCwZ1 [:{>8GUdÞ}(QR`3BpU, W9طo=Vk45j4V6Ԩ%!ʀ\4|ـaϧ5O f qt/Uܞ|J63>M`K۫W4YŨă JXxQLYE&CE,v;piٲeXKe͛GŊx{P[.ʕ c3fڵk̟?ҥKwK3{$=EeV*11@k<j!khĮ};$λ1ض$NȒ d*? q6lXiO[\]Up98~W^~ƭ ^N=╺d䄠`ڼeu괠iz\zy}8/ٌCh&ZCn'62rǦ'#fsjBeÇ/$P-)@z)a)Q8!mLUeO#.&zxhz .gA-<*h/uX%[zwҥi۶Vb,XƍzrSNaG}Ć Xje˖UqfQM|Ş 7MN.)k&>Vw0gzvzC%IB'hµT7%-[%+׏*JXqP}Y?ؚEIk 7DX4JziF̒$!蕈E;̶mzD޽ VꫯңGz?~ܐVׯOJzC]v;+pwqhxe[)̘-=~ `؎^s[x\F] }9vJuv)G6[ m!XT-5%J6ҏذkV;)Wϧ!J%C 8#a;Ln{ֶ}sx%嶪qL! :CoRt7}>d9,x5 z3+٤z ~2s|L\Oi(@%$qEK0P^n I$h+G!.+@hݪ8`Ř0YG;\.6m"-- -[Rn].]i߾=/BX[vv6[n!I-[L28pѣGөS~N([Z%'Ϝ`ɴhڒ{UJu{&Ah2F1,Dtܛre2iYC;uiN2@$IaU+[{;70 h(F*>5lp|k"еFhJH:L/^J*Hvp_Ʉ,c!l>M#-ZlfH)AA 1v3QQB \BJE !F9IFhMA7 #JE37R 2Ja:pdPUALk>-*~P˓.kݭ"'/?#d2ѹsgTUe$&&K/hO?@iҤ 7Ν;ӭK>|H~{Uڱ'];bʴqav{ݨ(HYkM%=3= \/~fGQjm>Yx o!ԩUOI&-eێFt4%:=ʲw>HŤ;z8BԭKz5(^Q+.WE :ha|+ӈ.c&6Zl%=!$lFY&9c 4DA*O%!K^U/ XT ɄMPw ۩rr.5+P'i\Nq浨cEXJ+Z~d`9%Tc` F(7C~|.̈́&M[5$;Xt4]aBu-%ڹs7'N %*Uy̛7K.ZnuΝLRlYWV=6dP e|n5V-Cxgl9יhXL:ڽWΞ;E&]IQ~ci.ڷԫs߾s+{zF#]ENV.:pEn'VΝT:V*!^Yf1a.f KQ%ndxpy$I:T@֙nvT!I)u66 dsL)!(Sܤg)h3BSӽØ{܂-rEXd$¥ZxaanF1iꢄϾ!-*x*~-֌=Jؽ{'ْfM|Lzc 52NΜ9׮]#::NF@F{"*ƍkXb>~o!&NǨ|͊iiq!J(ˤާb;g*ڬIM3jvԫx\.bcIHH$>6- =Yp s ;7Fv}X2uB3.)$Ndp[xbقnBr+iTmʐNØ# &iF gh>eR†]Z"HPq% L2ŒXOA0e=U(hKzN||(xŮ$B4z9,R%? +9NEӈ2c7#d )?hRN_޹(wpf <*΄in#AS*?_jV3gNhܰ $aHn*P( dPLetx8~0pa6oރؾ+/ }fre*=Ĺl-{OSTlwVOwZ,U : rYWoB':Ia~j!kS[el2BpU|~d߄ ӄJ@f7q *(YoμT M:9If Sc$Bhaz yz/Mպ<&kvFm#뇦W4\B#ER)ٰk *%2;/)Iz|[!üIBY5kg䔚$?H2%T3%4+&YB~Oj J<\|.Ȳ\OwԮ,:x><܉~Iax0-pd(DřH'])lQb!<LN9 +pAӣ,ʗ-ϛߣAU M?hj]{4pM@HzI`k!~II胥\tIIJ(n7[ %⭜4״oI{ ˲sνm^ջj@ }@, a`p01811&b{0-ah@Zުkso8.2nd *_|y߹= Fbed&iٷ~3҉YցaD~¨ {M:hЅvUf@5,p*PtVjtyI#xc} H39HQXq9JӒbD{mXM { ٚ:Uc0%p6A;!֮4{4~]M _9VBpU9nf<3ӎ`Bx]n`~}K5J0rZ`20dGo5&̏؋[M2p{6bccCc'Iبc{ns:"-س?9_0?cY;r\г PBsN:3Ű .Dҋͪqx@HL2x~}9 |g>< ?[Pl 7&7$"n ;[e:02uCmW 'F8D 'lD7灗܎T E-c9r[0w^[TUօm1U]HW('uNO%Sjqjy€8";)/jY[m{sIhੇX K ط̼s c{gN[as;Կ o֠#5ՍɿO>p'ONlsFgOUeKm`Rw{PpvvSJ/]I=qn8A+G[|w}qd.N\2>q:x*Nއ(^D)z휓ҎWC|9qGҎs'\y|~VˮƚqͰߢ#+Q8{I.}R64̑X1.(B22}=ÊD܀c|cS lScD $l3H9wAyxyAWZNx{Cȑpm'pkV:70䓙A: Dh0G{v K/i-\|qrXEns>#G?C{=Aw #~6i9ж1R6[;ӢjNh)mϽ戧>|#+WXvѵ:=:et"3C8qQwrYT.Ib Z͖7%G}wv.} F.l r(:Js,+=E!RlE+k"N0 nMڛ+8&-.3Q&҅&\EMpMRv^}E#O߹81;|mvSڭc:,u#H|axߩgNux9Z|?c;aD*VKj2e [Ea=*;.^['WӳΒoR/ ƭ*'?u{lqtU^]^^_[zFFж!P,)=LjM]w(,5&i/q&Rqwp9vף'ZڪXvaUSBj݉x>o<|uOg~o/]>şk R.*Ӭf 2Սd Mvq88 ik!~ibXV-%sWSAT.9îR P.xU!# X[i@>C< ^<HKmkS8]Ҋ&%- K͹̣S=Y]cF\Lq9_u"?+8D}Z7@6a<~?+{?H .Xv -M,so˖zR,2zóǩ寞a4 s*C| QLte:Z`;%Tx%{Y[I(f/h́._Eӗ *愊d 7MIomW"1q=*ROԥ˂duJ0f%HkQ~5>xK&۱AVÂuk;AkQRsR)Hɟ^.zV xj5*݊xUV|/^^}^rCʫ "4Nݵ oGQi**Hڶ!oV.X:SɒgoUi:`_ ^8~8vLY0bo4,UC*H+[kqoֻ!IM1qruڐȋ|-TC%$3}]=Yu XёVfIGx`K>UL4;kY];U ȕ+dLT4ʿG;7wzzqoL”)p- ě\kx5k]|#|$PEIxSN{-Z/=hV+,|zОduYdQqU eۜ4"`S99`NVZjzuYV1JZZ#XX-d&[r]' >~繸rn66 '̥.>)3A rmGՔZT|oBRQu'їpKb^XoEvYT e\x]u:n;Bρvq=[-$sDQđ-.>Ω¸ t伽Sn#9^qH,GuVq N-缔M%ueVC6߯`kNHgU9"إ<3A„[kXiG-GibQQsD.7;)kKp O/ =mՊrs n!/bu`ﻩpn **)u[)Br`j})Vchh/dXe X"uazQV-bxuhٍ gzQ2lE݈v}YIЮ&ĬMaSe] ]&Y\UkBIMgp:zh"|> oC#ZjEkAgu0 r^uL"(7}Ld;=r\)"Bx?9K{VHE$ hMӽA7Dbtu:ղ}8Af+4MZ/Or}V&dQe&<Z/0c2S !w@Z$9G; MA@{f-rz?(%GbTVX7FeZZW`dQpR)cYB.jdYs628Sc@!Ⱦg%aGJ<'_Ɵ}fLDVRE-κrb l{vHRjUP=ra!cEhhŎXǚ%~WwDQ^TNF/ǶlQkF ;Њ5'i%Q#= zJ.w:1$KA{)U]J:2gE% SHaar/9uv?g`1)&QRWi.&>Jdb :m!dadi7+ì ?(֣3S-tyI{g=8{H_jF~K#^`"d fò%,e`gM_WkLđIpz(7 _u*}i,}ď+N=>g!ލ ;|kWc?.mC˗ Ƕk ЪzFλN\հTaUzF4U |om2 V56n.^syHG>j??+{0U&3LHIE3j;XSAfxlٙ\'%:`Kz(e^ _c\֎$YkZӾdëHNĩ#=_lNfXj\ {&0\\9lގVMr:IS+F~_{RX9Fkw[gìM.e+ }mV\H Q1GtLTYE1Ѵltyhֿ5qҀ,$3ίxLK:ľg :eV:iҽ2]hYgȐ4Xq;sgANyf<ڷ Mlkeʗ-" y ND"%3,ɿIεNG3vt^ܪ !A3wy͌97-@9 !bKv NMaL [iN2Eb}[h}V(R\/?"Ny7GqC[E;_8qsm9zRcv1|Kj*6ގ#s_p̙dc8rn$*1׺VvX6]v ;x7[).03z)̅[ڭ[B6d1x7wZgR?Kuiۘ\&h^2 5_et}gPmu5IWk;É?ox.wkK) 6w3^rL0y&t a+p4r?UPtpbB}o,Њ~s$IďreG/@lϘ#mr #^؝nXC/O \iV.].q%I|a`笠–l}sc ByN}a2vHsR8bc^O딖a ɕ /&7C;758dUJ牎mwE˦ ;u y_w9mr<l7䍴6,lFhWDq.GX&f #h 2eE9y%"ȝeZX/j|GOH..4`'ƟF)DCn-p/ Zob|wR 2>k4Fc)~ֈcW=o(/?yQj to)aR*9ceMt̏.N\0`;5רs1a,yȿ֘±u4Y=4_Ng,4;).1ƷX8,剮`YIvAuN˺',ln/ҸGm)jkѮK#7 u±^< $(zjh 8(uf Kjf ;4R0lJF \:+QpDg<:m%^ɊNrCzc=~!^6g'sV3;7s˝Ǘ>#wdơ:1v8{z'9~KsL>_ΘH s@wށ[OTXa2qT].H$!]w2hWq$ V va %^ \CYc|aѸL>ءiXrpucPxIly<=x'I4ݙY5|޷ C)Z.n)6zAu'Co5nyrcrȜݯ9ӿw]Iċd#G?jaEU`Kp8;!4vo!V_(U ;ީLF xMQZ[ ZG O#3_wgw`)x⑳owgV3x*U~5&a"p7N=ݧo[-(J+n yI9QU㐈w~S1$rSek8waw/Ӫ%T)qOWXe.u0.+N[юTillEKNeѴh,ب"je rhD#ñ3[-Mi'Q~&Ut#-wϝe77*-zY9VY^|Sy.-hϮPw'bz?gQHONJ]/^4psIkwq#/hڱԉwzql [ZkΞ^|#C3{́~G[_{WƵ} ))T2T,-SUʥintN3O|}դ\\)ZWϯI+H4u\êfu廉~v4it ܷ,48HnEٵ~~"|k˗\ N`m9]Vc߄R/Phpe\[hOBÙc ]VW](]u5H%6je{dʴY+`VEɾ9M^'x#\?me槿TX&7ǽwózC˭[~/a8Bv8\zƅXA`Jj.gh0J8)I=d; IDAT]UY!6H ~!6eK"ϻUNB/SE{囘LHǖʗn{0Оb @fM[v/Yswm3ck7jO|Y.$Vy˽KlL&|n9 &Yr|Kd .]H-`x)DEs[#:k/\W7⪱aӚ?'|w)Y3-46E ֍Os׷Sŭd#~WD[8';CpoRnrx)c5~$:4BQпq *V]@Tx`e10d~ؕ,wdWs^mzJw鬙o7&%+pi 2i77k+C A-J[#ZC]s([%F#/s܂SPEXĜ궪רXW?1hCZ|sXH'R ¦UV%tx5sRXPւqruDEk[nkECE*upJI;@ iĖVCGdƃ{ <'﹐)+jPȭ&WB:2%.1Rf:5sntեJ4qʤofoOxwqh5oq&sv Cz Bd+D~]gc7 GnVuyu4,hKgFϝesϝ]V:1:s}|-֬=rPĬd.$7 ֤G?:oQkGZmUo+~#:uG(m{")*}'6YM]O>Ŀg:pHu_Z{sWƸJDڷC#~w~lX)XKPp\6dllՈ$TN!Q5 ~ m>pFa`VG@_"uSk' sV:o"7WAy;8 *txلX`h=86d>o"Չм7U~^Yt:"i75f&cb<jEJm[%ֳ;9ZC" 6[M8֧,"`x I$4c(.'v]K 2cr^D+3&`/ƃ!W&YZXqHkWƼ$-4K*bEJ.К^4rq)v>Sd =`+%*Y,d*2F+UȨjy\^tU3}||x\MJ]o7&'乺% R;rmqܐ`!!O+0"hI)s.YJ"v9[1|5ֳ.UVnꜟToRW^|W6"gq 餐J|oD9֔`c! 4b݁ RPN2&H6ir;E0 wZ?)){=\h!Zq*Եm`&]Vtlg&tpZBZ-ͩ_!{V>amǮ.Y `@R>SƳiWpBk#`;7RǓ|oJ=ЖYcRi9[+洓/j'V~o#gϔ͝ WRόc5۟oR!Sx$ Q~193-N 9r̴0g)~y&yFDNJn˲bM)V*玺tTj>2ހnk;0hdz]7؁G5GhpCT],n+Γm^P-i$GB &Pǣ03 i}3]8ň,.Gz>l#S-.ܐϾSƒ}[(h PV{x&_0~6*0:jGoH zkW.s|!R.x׈ 0AcNJRL` K:JдK:[s%)*#s⋟ck[)%d6b[]0P+cצբ첬F;x+w_n5)PYV [io~S֪uc 6b۟Wf^\MrW0 M׎9}Lxr[9J#3&+~s)-`~jX|57 ׄcjw=zwuzqnjlpiq1ѨxɾO92ŕg߱_K: g v1]D6q" jBc% S4ںr`Cd;.mNJ8^4My:SNauV"Z,^lNx/@~vũq{_-Vȃ^Z A29 "ܿqez{ :l.,wbƬoeuI6%¢ E Uf N4(Fx5~N}W o<\\'ݾܕߵPV}-zH8I*ξClq0,le 6Ze1`5x.rUXk@Wۡa M| tb6WnQ|e,*JaC"q Ƶ6_(ӎU )V 7uxtcܧ?yB5h†L2(lѩC k.;}Ni0vT-մܩrF())(Xȧt{7ixNXjGs]ɛsal4z ϭwK6uW6uy]Tg[-e20Lv íYDpǖ-RܯfhW:Unw׊H+Thz7SM7[ՍUogTs"X7cQnǃOӇ{KlF[,bs5'q,s Q)r `l__nB#IWeeFx ΰ-%7bCglL:ټjI/گ+C@di }AN79dDUİ,oor]e7?H8u[-M$c/=I "b3F=^2G{1gr?yb3of wr_lo5l12K#FqSk*gnGV9iU(/n7"95of FS0^KEhEvvfM߱B5qL&IHs! ;N-is,HGOkyÈ}us 7ƣZÑ1~"f+4oO 75/ {x$1{1kMk;W8)߉PZ}֕2KIPYefֳ\.KJblz `x@ ='9k0uy>Sq6dL=׉7TkܲqZ\* ЛHT)F:P Po'Y="#xIy? &>СXRN f^϶NrW-R6T2slCRXȌgR8' k| |O>ş<}wh.Gbv6dvsִc2;m_·[d`n _E90+-=wu%vɐH,? ZOݸ/=fO뗮W[V>ۆU Kh%!6caKYj'VQCFо0"V}bG8/:vyz.DjǙMdQ]yb :}.qcCJ-u=Ls F%hF2q\O.\gz"'G5F拐Jj j Yӳ5J8 & -i큧?ݳ}˥"Yĵ{{%IiSrm+e}3+3G]i_u.IX+r"t _Қ?"K iZ2ӬFbJ*o)zTή@|d+>^/5Bϼ.CgN7 6..#SjSnY#T_ȋ.];F=EN=3MATmIUԃppBҔq [H{J@'WLnE(;I:Rty_=5~M6`obZ˳!fkSk1kAbs^<ձwGH c-롪ϲ9Ю/*[^3Obu *hHSN+?vS;)}hG]ĻLkmZv#?jwKh\RP*m]Z{ӗfS9Kh7M'VϯQmƛLF`(mwm>֗곯/]쳻!2 aFTmO9O԰yŒsz T9cQ/;\c0# An4G'X|n8aC]Yܗ z^}bAaS|]4n)Z݈ĸSC"'[A18bm<^6FvҜ3'mVqTC=e53<:-O_q.EdqTK#~RS9?z磝9+wj8"s7*k: &(Rı/nun ~p9ڠ\َopJY;ҘHm#TTptt.CH~`T/"20J`2K7<5Ng&λddBRt٫^<}tR~˻P ?k 8xH0#Q}k'(+HApʃϻ8em&^4cj2pX?]KMo.הءqX#6w=O5wtyO7^?eKf6l**D_ h&a*1}2/ז'J+Qns.Y$2c8pq8NV^̑ԺXNyct'Ο|2Wv&d֡`r,hS#T@mk9#F8wy^ӫc~n,uff#ox⡣zhb<>cZ! !<܂5a)z-ՁL#]aυi_:I2L*չ/,VI©|R ӕ}1 :ػW/Q1]82p-FHʺs}&c_'>;yi\1!;(J`l)'oA:a#8c{eX"-VO;Z } J):1 Gf Ml[ 7uWF#32YF#-OMyfy]G$QyW7"^ݫ]wg\O_Smc87ZAMuqd_e\[> |ל \"iiP^>I8q:vq+Zf\uuͳf5>[;(%tV*M6D!v^nplݘc"M>=p(3', *2``a!I GcT5薹ݏUAWevGu Fy^O "il$U= -jt𮷮-Usݎ"5s\LE0E4N`QݑӤ1dAwT|.7uo-Lj3dc{L"qELU({ e}<)E|8 qN[;Xb;_bmo?û?ŹKs׹1 6/"Ur o!/H|:{OHcz`탅g;̒#Hj-L[lN58-e6] V9V Zt&HWpܛd5 d[nCp@"Οts(RS k6rg v/Θև%@Rg*FPDԤ ɕIx+[J6)53 IDATC`Vtc^Z,ЏTktN$3׃w [EmL)Mɴe1^[g|l2iS%[/Rg2ʰ3NMdEAn80t9sk<6INOǯ6v \(wS~U*~dbL2D$dNNVs[4%I}WYoPljG\ .g9Tqk0- <6+@ "{R!njw^.I4𨃸vֲ1&-ZǾyEHJ18ƅ>)f*Z!Mm) .x3ߕ EK ̔қR>!o<*toaE{G!5iPXKcg:4"@YDDE|1WvsX\ջ,-*%y$f-b 8to:D Kȉ"VlLMD2B@Pi5зeBN_.ycsngYw=~sv?;_|fAVkiЖha>~P^/zQC1:Imk+NԛB^vދH4WTD@FM= 4o; NNLFhEk4t"Ǻ,;1 op;ֺ|uvY{A|hwpyŃMoIHrt00phK6$R]lnVV} ]5̧$>&Ʊ2-l$\sT9hW΍LnmG|Žy*|S yEAjjSw9ewpdsg{yԢ P LH0WO1ou7ymp(QTۋw{TwUk)b=kE b#,BƵ"'۪QFOtRuDˁ^aºc]~}=˅VV:F¤me9dQ"NJO-Jiomf4GuB46;YOӶY]nĊH`5‰{ǭQhdܸ[Ӵ"EҴ[km%.` (G 3-5JakvϞձ2 )փy Y&q?߷ Y|En5)‹b7':Lbd bPB:tݰݟO9vSn~Èww->G8/PkS EO>{K =爺_m}-B}0y*g}xC#F){5^ҖV!EXl"E{)&VśīΛ[ IZKELF6 n+"}Ѳ~~RmP:8JFN1-jX©cV,wL-ո&C[,u RLn<7% U6348 ?r W6'9ԙ:ޮѳ OO])*ƉET]nV/f'0(,}{%gUHV^Z H?F#pW*6@uS-MU>Ǡs:g!ۮZ98e;" D("Gl2kqd؇. VvJq|k?|׃{eB)Ͼ\U*.^ۻk?48|N '(b x>֖ odV>:!7>8Rjb)2 "OEՈ"joDm^>ޢjD˟SrGE~/}ac*.U|tb<\kع$R:8/%ҳ}#@1"菮9~84tqhKĕcå]9xhg~χ5>6'>˞ҼVnQ S~YϢ܀x۰.tЪSNj~h-}9wnz\JpW _MnwI#9]oe0tЩ-k0hS:6!֙p>xXTs]uo}ιÛzD7AL@3%RD)Y%JJbG8VU$v9IRq*SqTbǎc*)J6%E#Dy"Mw8叽x^<D=wetݷZ@QM4o<^ܙp|}/o57 /|[ϗ RN ex.$,KՆuPLTQX UH7dѰ `^^(- GAd }j g XĔgb4M]tE"ިt:WAË(߈ 7KiIl~(҉%O-N7$UwPe 8h)csRi* rK?~)bB?yh9㭯9;^{a?oemUr7?sƬ{Aw.C}tU'Q.Yb!}J}9ρߗ0BL y izv3rCr#-nz m"wR"iE bIkgً#=+==yun<]wԱu\t%9/~? T#WCr$hO @*zWmJW|;5PAU5B-(֕X/B uUT*(!.7 kNR|3.\HJ}q|3tOD_d]/iTu0Ә\Ґl‹Nn}ÈXagMne~rCy\n=ZT /eԛNoM~Q^Yr8`p 9b"M*de"UA*z=8CsRhOxߗLIݦEy&N~)u "§,)d.Gl8!X)w1 kR98~x0\y`^e(li/Rs2M&~ۮG^mC<*i9z$!{NXqsaiT?~σ{Gz?e]GwzwwGxm"p͆膜{o2E Tz8杯;n?MnpȠnMko]O< (Ļ;4 ~Z_uX4*I'$Q%+ A U괮ps x7crU9Ү gͻRwA[0mYP')̊61 5l&  ({ 0I#nf%8xҏIxLQeR>ԾRZp+6X3U% LZUUW̙ 1gyQ~%/;pyؿyK]Ҫ򿕫|<;ZƓ*IK.l*hH}a{¯} rmk ^!Uen&Q_x'C߶j$-D' ] f[}0s,s v&d%0e: >D٠@;Ob<TCoqr2(7n`vCJ)^d!Hy/Hefl 罭662.I-h;1o<ō9,iL C)8^yo~qvYPUk4)e\T#nn \{^!eս^zqI,VΘ[[ *J[ijۉIJ3ɹ4I8͸2M9Jg|rMP4bBu^W~q~ :Qb ¹a8RW;ۃiЃuo*T֏1M>P!Vz$u9ۻo3GFh!xx.(!IDfnm?z(BQ>NUR =a0 .^0Fq!?77ң'AU@~ˆ4E)EEl(m_˩v@)iww#aIN(?'TQA0ioZe1}tksm% THYmΊ03Ҝ4g/3vܻYR;8{~D?Hmj!ڂ/`~a#m~ԢE u/ƀ2YL.ű,Tù:B\9}H_/k>bJɻ1Wq[d wVX^_77on>܏;SQ'y@)->LT`'<<\U[{FHgE]!!Tޮ[ Uཥ@ҲY[;^s ^\GD:6qo8v͐rtؐ/*c(mtqO}±聵n4r{BP%u@L:++I:G۵c:&,g7g>WЙ[i…ݴJЖ0t ж~f—=GoaVEz[`$zW)ei-އM<aێ549n!jTz=A0QK&>Wd4RRc=$EIeǣ#D8giK8gyhQdVyvhpi I$$a.x_vb !k[k _9{ig9xDSkX֔&]p TIӗ šA?ԡuIkȪ:UYkUY6LŊ8ը@ı-^L0&R o\Y2"RV͒lϤ:w+)=ͼ*CF*0Ā@愋1y2v2^U.9aqfh/?Md4?e7-J,v^0f IíNՓ<?{o; v\L~}s]T| ROBrsh(bG׏y!Q,V*jӔJOJJ- >pNԣsW]m#G[If<< V9{U-¿?}?\D&ta[אs`]swta,aR[B~ac%RA&1=ecpnVY)^m!p5 juܱUr&*JA!7T2//^TnJ/ڞy7$)R)U#5+| |VUTך|b(xjJ(DvZPT+&P%g0) zHU_Hjܷ/]JS*2?qNKo6K fO]5%sXMD)Jͦ%]jo4:MompZW56f}Uki~B7vNaurJV9fMMtO~.0WFalXN4':~~7qLj3|U)m퀺٬xo +By.3_Y;)CrFT .\ΈU)+oQ]k9$ʁԱ60uI9;KU7o"QKA:i\#n஦dݗz:iƾ&&gl]~Ҭ +0S$BvRڧO"%ht{4W~.lOߌva'd`32^a`KÁI5r)jn+/MסJ|%Ya8K9lSYÊVމqX@ #ql Ja4 rKUM;,ZzknkMJ(Q1uFG+fNlu ƥ6{=^xwFE* W"aHF)쑍I*M"u U IJөOXP+I7 [=4゙͚]QjrSaYi/ Q5˧yߐkJS~"v7>CWױy=8ul3RLw} j Jk4yg4=Xժ9+H'S :q+DYR0/)2dg_`(M췞g{{iA]KbEoYK\1k"]%'%/+NVvmtS ˥t@E)>eqWC&ʵF 5ɜ$Oqf#|m}]Nb8s|J&ABmTT(-Vn6ƐuoYF{9n֠@\^rZ{@\_EP8Ճ] bb#(.V8t\ d[i[̠*;z%Ո^'U`ČvvY,\#製Q?yo9EoW$ 5ZO8@@X-QUN"w7XɭV 몺}ԙ/:)F A"?W} 6yAo&` -dMwIٕKul*RշCPԸp47M6nXc/y~o:mKig\V1Wɛt,[ʽ@:ERT%]STRڕӕuML>*n8V΄E/UjSDePEEZc'#@RD{률to2M .4tQx;SRO ?*n J[o#ZW]ff/.X>gy~ZzhE1gר@ƨ8+%8뽈qu'aP㨙ʹ+TZpztױkh:8O$[)!NoJyEo6ԇna !њurG=D[DH" ;i.kEZ! 5@15 n^7'5?p4yWn6y_YɊ]m jƳZ||ii3e .SL? gȣSM(RVS_O0ԕ<^7[=ku.Fꣷ_z?*+.0.\ ;nXz$S{DB->~,yFS!.tki6) r6S+ hKEZHecM[0k3nR6*!"FbB֘TiYh4\ ~[V}?fo B]6Sibk#^˽"ˠ]m׎Pmj|5n0w|8o>Ss{ XwG9:.J72#u1*.;m@gZ2]Ujv9.J&FRoƥbv'ɢ5l9 ;h\:Pp3ߣ^K:T6 G}*X=^?W7t.dNvd<TْKZux"{μ0enQp fejjۡѦ2zB--2#oG}n\_N,M(̓OޤKqPtll\EG3=8 OymwA+殡yWpaYLRj[նhjh lռ@5K}j ԻU%aPِ#M=z.7;_Zn Z[8(a!q%"Oqst7R5LxvN;2Ljt~E>DG>rUz(OdK]}^3DJ,LotlGt;x SY}{n XlNF*rOJv$gcWBn0E]ԆLJ?tuF+NnX47 9֣_fFU~/Je?z\{{fHL%[ƻ{PC7%MRy/ k=;s%K-.AFR^V۪'P 8]]jzu+ϜK0WvR~mJgAYf <9DFwQSmxR޺#MDpy|EeoSqsqemyX4!=h\Hǩ5#:Sf?8RJge܈ok} [~Lmu@"r b^9>g= wp۩x+Zq2az_+M3|rNt %^u4RK6 A6/ pw1`S/fٿ7&Fs&ybo|ӱ.)QUm㼱}:a_ތnHf:gڃj{ôVQm7N_y˜lK5V۬kiIYKI.vUFS(^뛯#bvΝE 9Zn%R$U]:UzmmRJVƗ3g7Q{×K㔋t Zb8NyWۢӉcY?ܢĵ&}k%{uZSjhx4m۟Y/7}-biS<$"Vw67@OF(kWp5MXwTDfzhohryګ7FeW*ϙbC󰤱`-rQx>o?ʹK#8y\Q.u$5Lޫ Ӎ}hR[Mj)S0 w49=Al&֫vDu0Ϊ(i3!]e=r{oӒHLh\&r 'qJFZP܅]\q+0Z*!>L8y%m7AI">^ۧVWnd¯'wx vV"TׯEiT݊䥁t LGu=IV]ͨV*NB2(X!F\Utl;o&G,֧%EO1rD)s\ɰ!ɊŃx"='+[dVN,YmATY]ެm`tvv%sݮ'"c]m=ȍ|-BxꬌԜ>eWkwrS$OqqW:{4dKPo, .6 ,y ׳U[\N]m SG@#YVq]. IW`铢`ī=QZOhOG)|[ýb<[`,euQneʖh2ⷿ|򫟙}.euz{*1qRեK*L$qÄ^?k8+1nAs"Kdq v&{l:־!y΅q^9<4 t ڄjպp :Z/8zq,vEA-º߶YRՋ2R#U# EVeq ɚ}ʘ7gJ *( )A/M¤;')ҋW A(4pYHzX%q>l70~.$rVo\lKiuog.&=9c$/ /O(B0^acgts#]S?0 2}Tצ6|E.0O)N]x+y{s1ùlED Ov. tFnYdEah+9xjȱd.8.5+(箧zR1G O3[L};.;S-MhRJ"9u凈b 1&iQŘ}3[f ;4M=B-{lӱHi~u5I2::MRPgW|g⽴c8Ms>g+xwܸL>OB8SD`2B).J௧[6b;Iq}!X (Bצ(ٖܲBX9enPs它-Y.EʑԭM$Ռ R*BnxH!-\+!UqҶ1خKɹʿ{oockUŒx]c_>S&ѡT ʧ9]_]'},k$Նt  .KPv)9̢μN?!8W),Ty.7-93#qwz( tYt|y|N?MwGsKlb|\W`M HRK$ı T]R℉r\V%&S.>>ຓCz 8bMsesdZ~U#đX5zTڭ=;zzt/ti/3NrZ}hgiX{ǚ#[ gOn BXU%̖[$NT#ΗuMO΂qնBJ7qX黫)Ϝ?|[;&nnUǽ7ҁHz) I5v/Jkq QQ!YVWUYE|:] P d΃Y-ٙe.pI?(wƐAӮ _amづewEWB1ˏ^xW}39+L'iˡ3!Μ?'iwɲ 5qc'^w{:~mGf&m.p6-ʐ&Bs]I'=c,4Ai*\Mx iĩᰙ eYx#RG - 0c#n;6>CͱC 'ج]9 6&zUYזiFE7k#rT1xSV!6~b,̄#)0Y9Zx=>Γ;Oqၿ4]=ix B3ue өVihH|U )O$n>t:T:-$0^mAL죲tb& u]xhb?o@lI'@OɧxGJ9uݯ{+''B7ڼmfuSW z݋{x~ ;0pXkJo&dw0Pj3SsCpH'c?͈BeP)7 ~1[g<--io ʅw4*Ev}憵5rРMs&riJ̌CfeE]tԁ\I4 IDAT֫rR%*;CVZҙ3=zamusmH3݂ѸZHlA6"N_$BM3m]swiv÷cpqz&&I=|믹W]w3s >;7Iҥ*e pnaK>aFLCOLjf+#_yO|7H2}yc:Xg<quo\)#}RI $C\G\?NfGke$Jb!m56 @+j5c- s/\>5ڌa:79u&Y66/cF&YbS#9^ 4^]Z&*:u! [#f<7X6DǑ~1 UZRabMR}YQMk nmǹKGE)D[U(-8%+6m~\N-0s$ʺ^74}3anqiTcTZTt?}{K;WML?DuN;kn[=}<ԣ@Qr1>=;aN㧉vTڢKY~(GMO\1"I1kqL 5wKyuuAE^0޵/@UKyE`5Dh^l8uvq~'#j2 >?Q5]"9-Y|n,TĚ%f5gP&sR%ΜO~?|+}Q'K8 PܢZ+=e'~獩4q /IM'aܻjR0-Git֓9iț\BԮ{%|OO"urы{\su\st?ގoVU2#wˮY*Əjs5io8njd}q(؈cbQQjCڢ5j8 O4]lS:pgv'4<}bk+7PūWVZ7NZ8 MBm^juWvmTjxjWe]Ϗz߻,^|(ͧf2HָUwTi_"p%R!:8+ )sRjX3|"FT*AWa-<|b̝w?pӚOkM !:8;̈́W W4IHsMf-yEv%09 MGC %∵a}7đ{Zs,8>ًʠX+OM/}_.FhX𺲂R}{Υ+(`NN&גN`ꎳ~g/Zs<>و:DOΐ9sd}f>"c߷2~лF]I"X[81D)]Zqzp?`4wʛ>Y(<{/Wo|z|Q⸚(),a<7҈9K 6*͹Z7…㢘z])yOM~Rgc>/o. Q`}Lt-g{6$^ma&miGDݜiz&r\D%O X\!.$5ȴ|Jup[CC!X-ܼaS!-}j1UԲbXjK[G(0X5$nO!{rYu֎1RE H&Xf|r^?ryВwo>xp~4zqq3856MAL%$-.mY#v++b&l+e͙PYE)_}q>?sLz8w<bOs;eex@i(ArMPdh$\Y(ey5;Fn$EM&#OmZ^ 4]NG$la$ [ыn+Q34M02l]opX) CiC5=M+ǖyrw4ۘ4망xYF-9J΃ 6qE8IuI[צo-uϽIESQA]i.hyw{o>y̓{⭷5hx#V,SϏ j׺a^VRQ'fdUИ;~l(:{+Omo9gsIuVĴq!QC9lq0ΐYAeYKd VJ)yM8-)A"kQk훉WP\D1FEGVҝZ^87pbmҝq kY[[ѯLH8r9ʅl{" 0¾UIbD_C VʿAlgWF9O>.4Ѫ\)Ghl ,< Ful!.D/v6匍p۵?Wy+o4Y6&$tW:(L.9AQȫ`?/M^$._*4u<.p|\Iu,cFY}qbi"VpQf"Fދ)]AB4 _')8Zi@EDQ+?uy;#>jH#Ӡ) "ӓ1$bkٲ31$èdL{ժԏC*O`ħ^31Y;LZJG:v/:]V.ަ'IӒR)9Nd߉%f6}r'<N-6|GٝdM!^!,+b0M[ <_^iDObAWK̹.siCjCxo 5L"`6Z'Qi9l&к@C Qe{TBvSR ]G%)vfyxw[8a,q%r,tp2޵#GD}UkAM \9YL^$i`t:+4gqpTJLh({gNpz6H>@ .5}=5⒲ڿr#Էs\er(ңTsmXΥ)9Ř?3:h%EadThhvV&28$'N Ͱя1%¹IG}R-]&/=~:rLGw'ؿb|1xx(UլZ[׼ET4kߣƑ9E/gw߲?ȭ+s qp{1 |IE__):9r Q ݼ57wݛ=rٝ#rn\lI6j%͌lƦ56ym4ƆZ[\bs@j%wȥ*^B`8~|;_DƘ>5@Ape-2L޺ڛ]bS+qւD,ǚ-/J3"+z8Bl_o_~2bd5H 1zw7;ēzD  V;cl.7^.koo%q6zA{J+ ((Ô)E[Kz^ ܶLJ\dmo\pʐٶ^7`e[[3IO,v>w)6|gQ^ ?@Bc&x٬^'a+qggB[K^(FZwIJK5=oװ1f,p:!銟&}ה|dkOçgSs>  rМ"tMXXk]Ɔ{{TmmLgZx'S7R#|0<plqIfsPی1v!Mx8]3P2 iak پ$- fy-CwZKãԏ7}]kgH{Ǎ|WQ|ְ?@٬--uMA%6eOnm+s D(RlQ0(%󩮲$aS09.F(kл cHn %*Ѯ+|1?"O]CN$A.ٽE^ tzDKϹd2K>̪UQfabOD$ _ gSG߅mrf;κETª hb Z[ձ|:O )cV2gStc Z>y|ם鋘.[X5uu`.Z_bmlhDBriPEYG/LJ98K98zeRZsj-}ˆDGj_\SmYÈwO.u/KiAjGnII=4'eځQ=b"N`3朾[.w݃}ZBQ2XJWos=K wP7sqgcx7@o3KV-=ZJ;l e|B<˯"$-GphLJr_hZCppv[>xwdrR](w[>̗~$%voEظb#2mK*sUx8 /h++tz:w;fWU/iy{f;pbʅmUhsjl37bţZ"J;H28ڋ许f=݇[b6!+2$a{w@%ШO;_7'+~Mz"sexY-^kFWTѪ( 'ۍgJр4sO @OOkv ^J%Ifܮ׻ү4񪑧jFוPGET_v.H)&!2FdR^^| Z͐gFm^ػnNsqx 8vjc׉)-R z{Q=4(=fa dE8x޻_ރOSN&KKQ^z~MS?:gwbºsx9xIJ4lIk*J*z[kϵKEEIS`oO/f|x>lmP)wu?|w+!5$:QFiTkcpvuN: (;>c0olt<^Wy"4leL13{1Gh9tKbgM#v[FwHW 6j`> ..h#Zdvh׹/X{|/WW^sy.-޽!lsl\"˹8+k~ptM>}5dbYS^X;[rF6B%ߔhG6 t2Z'W aW'uSn?G1эaOɴZ.-}Ҕߝpq̾D }e2GG.v*iZat0 ',>0{=è IO餇$c9p6c tDc7brsݽ[`CGIl Q%ޖa{.ŇÎOQIt:mWxQuZŻ} IDATB =3[prqƣ~|-~w}Y57L?!Mɦ~êԉ3cr?zu>'hR]t֪k%taX3[;)^CP7ۋnrL]1OO)(9ҫr| }t *ZM?lyWAw^\ūxa#I?EtFèѢ]v7dz۷}Έ~VwHG's6ꨛApnΦ ywKΎt C.gJBEz(fEAJ1=OO?Lf֟z o 4M1x|ICίPoH޿pҝpr[ Xi&z=^i+0OvMKEaTu<^pFwcY~]W4:܆>xig'I y>m< \q< ]Mj-K'n'XEeFN&y@"m7no7ncQϣ+\ ^ FOp~ &^:X]\+i~_qx~'G|#p1ʰ 72/ȦSt4̕u/ý__ xOw_ށ UL_V<U)Ҋ@kdXNyAgW.`щPaUj@XK$|co_qB׳=|lyxIVtئ)kkm] Fu {3GluOu nF>׹a62mo5g^D^͜q.L$"sMG l3%"Q_]uz:=ҝb rW~fT+"_oGv$()i1Nx|zȽÇ<>?ܚUt11B.YvxųWDnCyb[AL:"hb`P<{t^U癦1웅wywLB]k zt(5|uwKbJ%oiaBjB['g>[{}9b7N"]5i ZR5.KeWھZX\,^"aDH]C~pׯ_ym7h柁]2œhStmW&?Js%*)uڃgZo9N9|O9sOn{qpUfʭ1S2ZHЦ&@8UGɃ} \j~|ܽgw36g׬5h(Y9yjV>~o)om vgƍspsvy<6>4KǖÓ̮YĬhk} .fIh*z/ajH4- Wy+KXNla]rr>I7"|U^|~}%Ԡ[Y\]\"1Y/ELi~:ŕm#:Ysھf֕EM5߃1hRLcQ>+{wޗ˯Ϳ~ä[zŮ"u,u{O%\64@\ck]Buʶ<k-^X$R|%lM"-Mun 6kŝAf-`tTcR-o-l,@h|+oį>zyc7c( KҙS8\lzMvX$WW1v}@ ;#Ivyay2n'F3-uj/]jr!ݦZ} uTX_\,-㥃d{)Qnhs0X"K%.zh>WJ Av: /,VQ8# [ܛZ㠕Ƶ~ߵHoK="R()i{ۅU!o}?>ztZEt6|.x7%m vqp:mG9DiG6gJvlѼtOB k_MPp72w$k7*]YI*dp2WrrJE$l 6u9 vGJ/~'_a VnyFV<:ĥ֫`nE^סuOJIAjܶY6O Nr=~_֢vKg\rj+Jѳ;\*І ,5%mo{6a1 q0zИ i=_{A\~ǨZւ[D+MOOy"eͲd2=YgT{ mļ%xbW>_QvsXe_ލѺ_^/$ n-|xSD U~|iwRX,hsgapm.<դxs ءyq񔢚nC@UIůۉeY/8bڠ3R!Ǽ_|sm (pF+Vkvѝ^`6F-Zݔ9w-ÿ [0I_Tio?g~s5[_/7s(ePeTЎ*3Y/?75]W}TY'^k|RyU.^k>>}_y=2W?|u&!IpT⤎Dg5.7p6Rhp zLuJ 27z:'$pbV+wXK+"5pfaRJ@ Zj J0L^zXtl.41EWD-yʽx({f#{sK6-vsE.pqimlS} o"\w<<.8;T5eo`n7`wCiWCUPi7@Us6t>X\*o pzV/!8P<:Շc~c>qt3m=έ+Q^t2tko~/~}RekQ|:S g‚/b. 7W"OZiLO>|UbJLO~ȇ3-X/f;>?|pN1s|l dAc6R MV-;Fנ +QNFꙘտuYw<*v ˀ[+a2̀[D##IDt |Eu }=$Co7qJιW "vl}E|1gq cGn[>v (wa:Nnow+%lk^_/l%5Vi7 P& CxŪ[LW{.߯*^b޿? |8孏|(':pݒ~L=y'E"_ _~eۻÍ۽[ifuي>9c~w]~ϸwhﻹ3H|Η0O(qxk&I!q6=~OS0ȃ_YWK-62lM [ϣu[&gZV`]mRV1O; ].2_*~_d(t4QDjF݄$8+0(W:-}%pqW]V-FX I^O 'eɯ8-#{Kb'1 ZO/f|:a8/7nLzyn-NX穁zK7tܣԧ(JkR=^o?voDġ~̺1[;W6i l{ ,W\w899zfdΤXm}ןr<򻌋K4iD' :=at5TǙٔY>c\Ommר3/?zOyhkQeXpaEBEM.󱼀 ]9x˽G&L;H~*9pb܋]J߮[\nR #`nMB (y" I[AOk=bK==Vu u ,w@߹WrrL-I@S#v2';o' Gyθ +i깱)NKƳ"`ʂdUv, }۸(mslz3;当wF7ʭWóϼHg0eO&)(_PȨ;o)K#P;`*5kWB{J'BI{r` EYɬ(|WnӬƳ|[Bsvrl!v'E=X]CPkrr q٘~ɛ|l)q W ʒ*Ccܔ |woʐ*×oƋ>'Ce?*'[A7 ӫ5GZvxc)h7eh5EqCey6J]؝' 1j\">0ܯdއ9.%xztQV2TnmVXt_,f̙4 ;Ң-$0ȞPO,YIi],Aeem蘟ʿZX-6iݺŷ^xyW F5Y- \^RƱ`\Y ItHʚ9ۢ-,qyYi8I8dZ`8̵r}a[L5{+m"EW7 ꄃGTmPU w6˧)>4]?^ .-bNs%r~v"A.|eɻ888xq2.fA)2O=pd "=*V*ʂ\sD e!_I0տ 0_dZ4w^6_ nw[ Y"4~Ї]x=&9Я |2{#88|l7ܺ͝.IQtږIjW-}*எ]i C*L]h!_w`R|so1-c]M}'+[䴠1[i%p7݋-1p[Of-?|xY}$zK8\q>1= 7އl85ĺ;JOHC:=Ģ"LJ Us{͗y$CaElY3.rg<:-土,2;f!M%&We 튐v.~Lm&f&[Cu qVUҍWן0tv>[m>pk;ϡNX<#K0=:zt%l̏ݗ9<~dҦb1i.jٿ-sG ppWjrm-41 {h}ە⽧5P:Dd5F %6NҨsA|n- YhQ7@6LI[.i1>V'g<εYtη[,ee Ӓǧٴ8 ~^w1ŗ퓯wT;-k<xɺckoyk?|VnT&{AZmP(^R:}[| ՅjLj\|E[8Ya+_~ÿ~ߢPLNNc+(y|8|.P瀄s+WHU\'(uۮ pk07<{CR*.,AT)f (̋z4 ֯ЫH nѡ^8٬RhO:%HЇ/\*Xob2wIbUs{=3yhAP- {41+NrrK8S%cx\62X[Pr ¿]GFkPP0{?cb޺lNʰ0Q#qC*yqߛ0 IDATqvFOĞ^SIU\Lo@9_%xfo9Zn>gcx4A&l5(ӁI Z|r̯\0˂Al>f<㳯s>+I! P4uay߾A씊yFJכbUFp㤑$xEEy4 :"tyq4@NW73WVEp׉`ͨ5l{ ^ U8>84P~KM6)BԲW+VJ IGDTa50D>8 (JpAi 5/,hbעbV«hƎqű {$u{/ _f_kMXHp[=}iWIű| <,3oϬc;|eWxa9#_*B)d0C?yRvWZ;ը:t3[?ӋG$@E[OF?b8F 1Gϩb+;NVeT?$Q0jmWOS" F _xj~Y^-\1QhZfcg̕bJ-^YXqyh?lEnAO}EyC8%8+hiG[ Y07 UŐМKh{Htu'")(őjC1+褚tMXs3GRGZI(zb<'Ksip  rɂRuW[^$JEo XvRN n -1M@ O+c ~yT CaW2z/7GA#EHpܹ"&Etp$vy%N|8?g k$MQjR&Ȑ3G}:[1=v7Ȋ$}m,ZjY*jӒjqta!SM[/Eׇ#=v[Oow.8iEՍ8W^:>pU棳ťJg5n]}2<}{IÓ"K\ k^yu*pG \.J)y1L-κq%eK6h^YeB/UKHnYܬDK}[-ZR .᥯׋ڀ<|VZz |iʟQWXRݽ;̫<7GluwHM7q1Rڧ﵊ɭNȹ{[33!00=pg3i>i'A Ԅ<8<{bS-"MjoTRHܢtlIQtA ֽL􌚻T|LxYpR9MNlաRDP$xC Qw iW]ˇ~%Z0J[[ԝ=1ֹ.t%T9kWDbZ8ay 4]my`tԱ}eQh3U7FΓAG[RcGRd]\`#&,kiPK Qon%2zpB'U|ДvC1W%`n>II$ !6YoܿWL1>|KjRv x"{:IYUX%+%Y:ՠXl{N_eOH]ĥ^4ZHSS,G?I(H7Eou1鄻7~pNܱ5 zmW`_YpG'/3(i}RQZdQN«q"#~^7cqcHzןw%pySti?78:浪z*\Ug+.h^J.i"fMEy>A^4g"&:0Nj2+r7Q yQjPyLdI)yQkځkl<cR7w4e3#Fͯh][>VQW ۽!` Țtt0JdV`-sv YUn\^@(W.AF |,s=7h{ݹˍ n<;1WLcQ|gLֳk哓{~}<< :aO3LUDk$⭥_U's=HAa#O9|@a{1s7{Yx\& 8KsƎݭOϫjc D"K-zF74:0om6uVh IW$_fPB_ uܶ]Fb=#2F6xvt(:7D9GruC0+'+8J+U\,JPF褊T]*QD(&j&cKO< bzf  X"(x-r>lun -(뗴n%qy>s7pίݖ0r|_ ;z+W褡pVuXJqͿkͱr|~X2&幽縳{ CzI~ڧz}+gb삏O?os6;ILj Z'RepSOНjNʐpXoj+ 9=ϐYN2A׳; .G ՀWMѩ{k/:jJ <8AYV@Tз=V5kԲ咅IV U|LSaF2O7vfW6 ׻Gl {mY䲅bA)=ou-)8~xΧ<6hcaF|yV*E=(U@/tMg :ACa^r~a9\(`:љF9K)ᄝ ;*ji6`Y׬{Cdl!yv#5ZJTx7$fgWW Y$nv>YodUQw&-zVxTJפt.CtF:A& FL'o J l1e^>`^PF!^t1+spsrz ݎj+f1oQNV'Y}~ nF.]eIҾbt1pDS~{ƅ{aE\ SG7.>\z㡘3pQ˼" k%^t(,G厣]kJ&9т-ƒoD t&1^7ag`}F<lθ0&<Ǖ<]<3 ,.$b6qd"5UźW0Eɤֻ#XW~%x+IJ]^&5"Z(3V`/ǑƏzOdjmL3 m:TxF cPƐ$IύEF;8W*J Nֶ w%99ʭr28`ȨC/nvث/ l[LX ZmUAAW2N|bGkD+$mWvgv-"=ʹXp6x*PC 0 -Ps¦D{lYmJPhqxbytcmcO.S{FG9G' 7>QIG3쌺6 CŤ*RIZR: 4e6 +6U Vx|Qεn]׎$9 s/u,)LװuOd[ص#s 99^ U{T.~dB~2Bӧ?Oy|w[Zv˦J&Q]#2SnsEMRƐQS,B9RG{ڸGyZF D UhaJtVLۤtހ^ohͰC7 v39S5fU@)K6 ouKJ e-~6q1uvh  - ư :aYVpx4ьn-gN&̊2H7*HraYx_ Dȳ v@X\Ԉbލ{; ,[lXF;HO&y'bts*asDͦ]*r_x*P^΁$ dme VO iH[yY[Nk/s:I0iJvlv^֭F!tn fp aRNggLǔ g4Fq8N/HŶE@dNb@sLk_v"lY<{J;!xAjet#'Yrk5|]6O6(;Y͢ew*@c7ֱyϼOB!&i,7g%B#]\]%o[K* c&Q]cpèՅEl 4'Z Ү y"_jSf9VyjˤITZkU-+X .!(ǧqli .%R`TҏbVU`S:MB9$uL.gV&ܥW:Rԑg%y"̧ӻCm]ԶК~{ȕs5*hmlWdxE-KM[㭧IAmXUa>UZ\E@w 嬼07A5YnAh~$aډrcbXk䩀[D4Z ~^icf9w*~Тa}J,K04;E9r.``MZ{:BlhtB"IBCO[䳒,-P؎b\II266+JcOy~q"ǗSIIs ݥ{EeNAù(hF7Gt ܦ0qqWRIo?-ʆQƠO9 ]M@v*"аKw{@n}||Ѯzt_vP;!N.ژ0bVO>t%۷]<?lrBO(lE+b[0{*ۆab& zvz?gT%s fԴex$d3aM+P%5Lݺs;Y dr\*%h Yea}9U#mwnд ,5`0 toH/oSWD>a[m{clV۠l/k+=?_XvӴ]/pz% IDAT7P9q] 9_ m-Z0ZˮLb r/ )=PN? F+D & W]zwh-Py54rM[)+&M_.U2S`HfhM5dzK0b0yA;C/,w&2NrUljSNr `A]~bJB 9 3PI[,ھdȪnS_-5MI1 ΦeW :Lyg8\żP t3Xp}ê@Gw2Xe2 xcGBb2QF !!c9tF^k /,m D~j[ޙ-hcDل`Ǭ+Mғ6vx28>zç )cf $t.W>+QL(EY~cfWB)G&9{k]pf3,6eUUrbC5 zAT<5-<.P-} *`^{ݮ(sC/0Cx{` t1$%0 H0P$QeO?5 ns.'mL58EՋEM=zZ]Bj01_ !Yj8o4Tu,{;0T&)Upf,/$}2MI׿q%LUäE"۟[NM\w\zdP}^K1#L 993K-Dׁ؇i#,,l֦Yz03ʄ] 0IxgdL@t.Q;t.vZ“o lģ"$Y!QL!3wTfQv}~'v7D=6"~+͵=Ȫ 8~*sESLǯ;$G/bu 6q ry.Ed=kTU;Rw\ZkԵZ҈Gk5ZCx]c-Kc^DNc#Xq++'߅6oTW3Mڮ,(ntVlYEc/YuHᮐ7EՋ56xgootl[dyPGg8>|e$f(& K۶EGS* 2(%ڹg{,e`T)HQTVxZNjdeVg%e^ Ivl/:7bU64 -3&%]qoWyST8@;‘DnjB-V ["MՀ^޼aZ̽p&;Uhu Q xVW-B/3 vWRZd7ȷn]|SwZ ך$W % KM2e(<m!A|0{?k39.Whb؏LjqmJW Ư,p&32 c/k[.9)n9h~o_6vMףa_i jTכ~qB8Z3 ^ ݚ?$['%) >;)r}^Nf.p?KoAY%{<nkW=& \@,!)B2w-Hn, *%`.tUQH <G1#}Ȳijl0qGQ+`!C1bsL <  .]D&)mvEQ@ZC\^\IbFDx,(OĴ͗0[ `m}/Qd%DtsP@ 'u%,M]=7 ~1_KX5hEQmIUZݿհxxNje,dBZhr[X[WNHl!mkkl\6!І %Ek BRM"$3zwZ&vb_Mg΂% C=bK;<!W%^[6]u>+PL;p*1x{);J&UG;lm,O .U0bkSb2]my\V P6֓fu:@Un`9#pFn}v۞!}SLKW'}ܱ6%>{oq3䪉W1J1n{NWˆ;+o5i@$N>K;go.kb7 Y2Z055.M׫'UTj_:p/uԸ݋6m||6ǽ27s&p+%Z]D6VA7p9E` .7V4]&*t[>xm=}m_d\WUB]TN7V`o/@iؒ}:` 3\p+0P & Wf}̒F &{/'&2^&},@ q?vY5pPI5S4%pd[*x2@Z:s=YNث2L'\#y"3 D3hSQ* F-x-y)P`FÌ{K<6Y1 \Gʭ(@"[TR ^eik4-yZ7-Po ֵ;XF~`@ٱ{?ڥwӀy6ޮGlz۟>{O wuϷ$ W9ֺW똤Oa )rKڕknU!ABRp'M  y3zeEŶ~Y"3 %m5Ujxxw]A{{92բ5' X'|}7N'.U.Flqu[A7$ D śX._ NfǐVs) o]lzoHr jC@tݾYBY dE3M% *RQd۱F <$W[귮 ~P)ffYY\7x<_![#> nh=Yȯja6T˄}/oح"zd`晇! m\XM]Ic4H;`i~||R&(*kWm#9!kx؀2WoﻫŠxY筦S"É<9sB@ g}xvIF>vW*t~VїM8HF|0 7|r Z7Ocw܃f}! XĐ!u0A?I6)(鶝(TF^d@)AME^. Qf al{|ji0 B!;`Key`qއDwR7;wө%yχGMFUidx C(q` SUh@'9l.z'%6-e 78oCy B!egVA>W: nW%ޞNӃ앓Ku^/̴@Vq_%X4 >Anv(qTf8R&kȄ4l/оF/c"[6ds;v2׺axqV+zxKv$>#<6$䮤!2PCK$V/Qe f#e;:[֫\A n]&5^ZQA'J-x$ iu8Š  8ZlzVB_&c^v~=יTaW?* !GA{iG6o(3k9򌔭E%ەL{{ "b1?Ǻui?J]mٷD(F-vvfF~_h_p|)S=8@PJC#Zv_>;}v>K;mFMPa.6"h&YcgH\ aWoZGfm2;{SM,CJ}pXdP$pp.@_ Dr/+ZSܑ`/{z7 $R&aڅWEk45f>Ƽlr= jr?0\GX@a@\tmigyЀBԸ}>\sSS(m߉-Gb4*!Ƞr36?z0"úֹѽܕة|%E\q2#d'?+'/w~(Y߶xK?E^4cٽ1z.NJmt iD˕~m_ 6FA۵~ado Xh[DP߬T/;#I$iūNɌAu=ָ x,8Tkc|{k,Mt]74C3=8~>Q ho;MTƘ M$QzU >?dv:;3> /E줫0fXƒMM&kɣVg]^VPmAt>No Q[gؙR_S;x,^g%~ɳ.uI>93(.$7]P-h/\Ǵo%LۯGov ?DօE2ź`iZ<hvhl5u T2 zV1fm#CxqjrGk,69GSUgQNoy%1گnF fގyk6![g rFV6d3S*`{pN^N9MkͰS}V+Zv88d`wPT!QĉٔOuܽN޶WK8|w]ֶ"vk)er (1~v/ZN4ձJ FQPk(.3uğ~L:2CpSjw..TIچܝ#0I rJqIDh`|?4iuedeA_lsbQIkdwǺ6xqY]cWѐi;m׻S4 E!Ql= v##n/i$:N:sc{G`T%? Ą YX 1Q.a75GL'׺'g_5c~_chBCܥCZz/}ڥ$Z&?[R=[~I#úAIL[&AX 2E9T(Fm#$[ ƽߙan99zs@+: to {@ǫ/qttO:6jŽ W:R+ ̠2|?>)]?nrp'HXr_]Dt4}BA;}aȺWANW-'HDb |ܠI\@v!ۦQ$b})qN1V/VXL΅A .Lca- m- h-ŬRXl4jn;mԀat'bL2M abpK@.1(wޚb &2 >C Iz+*_>z>Fs/KywI#)f.AokmxRB`SW uJnc!W­3AfB=hN.JA;uH/'Aa-|XRf>ܚ$d!*~7(u;>_k蒰oqM&dw?X ‡=M;Fe>&h#pa0V HQ6 'Db6UճK?,#_vD}}kĶd":07Ƅ}\( ݔk}8{0I#% o@D-⣿o},3;?G*so3lSWx_Gk8Q]!%PxƖ4 K[\{SXa6Ul* I[hPn>o6syn϶5U.n?{UVpYiqVlK!,-9KV4w¬(I-gZ݁;G9rN@J L'1l ָ۰4/kw0N3f|}9f.?I^ϞEh( /6~~[d6>\~={~ /w:X|Ϳd~NvI26dЁN"pW)Au}y _>yf?:yzY#%a]^P9.8 [-<.r7oK@U1mIp4 uqJPR6ʵΞĴ^dӝ+*7!/ *aŝq^cncvتv:yv +NI^[Nw*t8_!t~=P^%A:vԸxYv:|[$OD4ξH~}SIe*%{QW #WJ (_̣)/ |xT; M8j0N:MۣP6S!L=% Y[!C!+`3|Yu])} ]Ͼ+IcR>AdK$@I5vx;Cyd;O,P }8/_ }|4W_ZoTxV`?!Po[WWIl-].DbYvm_n@n*EwC ,~ޟ-%r%:L 9J5GжɁ[R6٤qPBl[EYV$Ōr+׭S|`/_ tywx!$X - 2 "a}eZ/--j/7}K 2iA>QJ` dӭ 68JR`|2Sq2|5MgxqZcGWK|]~߿#kJS]:0vuH~oo0TWN>H~Vvvbl1YpKKdʟ,<Ɠ"ׂ5vm6) 2jo ?7dS8keM)|5_"D9yQUn+2GxsV5[sMB`k1XhEia9H-q͛l4x7yoԶEz,۴$k]B!Ωˏh2`2wy.A߳iԭɧk/hȻ]=Bε[`@"h;G:]F÷ҏwtOn_j{Q w%>i]cpf5@dXf ChI$?*yBE"|!NBXvmd[*Wl HNz1L:4goSl oc kwˌ^paFr0U,Gэ@{(2Rb"^imnF[*Orle05OZoao6r _E:n3Dg ([ n 05Xm~u,";=xsʾ)}<+jH=qݏB. CiEI'| 9!> Xst$kߠ+_Dn^rPL%E6h7Z^I&IUVU//Ԧˇޱ#v"%G Vg /|,$"cBX,6/7K?vعީ."RRZX.[\lו=$d,O%&)*L۲Iz9]߾Lt ¦Zf)/?f8=zqo4]>N]ç׉N =*x&K¨,ҁ-޶N2Qҳp]H~p7gqH=P2S_Y}?B # 6c7=_ymӃ$R ôGm-1ニVDQ[Y,&XF/!Ireqv^԰Ƣ_ BөĬ̐)* LKby` `bN1"OWk4`G$.1^W8؋掀}X{rkb' ӯ@P̉.7K4遥~ FD0wTRSM=ӦxE)7:ߧH h+%Pd6kܽ7JJb2F[3A?yq/J Rv˨hzI,3\ -شnzEz?}O TR=?ِ2Љ|B)zy$"_`_16❇c;bd:0^):PZǰu")PR-;ձI5/ۉ} d)TFafaԠ2LSz'`$zQ͘097 g LS{Zchc `ےvm9;Ct2uL`V2ɼ k _VtW哉bF BKE8 Fz >cm| oHv?}?#YcCE)`Zz`-"vi2H XE|哜r>XW8Iڻ {W !w rWi)m7;~-1JNKӃ-(Ka/jljmXBl VїNdp%Η5`kBRTU~(Zp{~׹aBun,5A z`mm⢿*b`6wO?ߒSnC@ eGF%Ir5cLE'{ XWLbֻ֧06K"V&َ@صLduM6lr,;>P۠-;Jf lC߳M7f%*z6[$io9GʃdV~ )q{ɲDQ0 zzp V$с#킩M>/V랳l*|HyPZ#NcTpٱonRHKI<`9GӼ.~(|) IDATLJyM<|'x$G]wJ롧* ߬'/_+o=V -/ ]`^N>9Qv` h؁aM;5l1C-%WN c&7B StmV ?*( l!]{D_ o,pqUbX#F가e`l[zwZ5;{4]xT:qF ArR']8@ GF}4AY,{qf[8<{O]6x`ԆxG9^^y aL_ WTҟ=};6LwYS^O=΃Mp z^sF7^ 9ln"D؁J°c~ȩ>( ]k便\I2FCH`ܣ}rh۪f *֘QaٚxIU =% όtB ɹ@|kN26Ȣ f9ŀ}k-tW',7'8__@.c ug%/(w"XYēb<; z(n6ߘ=M&#"ͥ^c½?@.Wx㵟j}Mл\UINZ۩Iٵ2捁't{נo`&IHl!3 %;;m]PTlQ-V8(s{~x@;=)FF7ፃ)kZeAIt3[QzD2> 10 !U[ i/,ڶgBa[> wK~FDbc?ۘ 9rd 5{XOal;<8.P+r7X Own=b~gg?½?nUؾG;ܷ-C79>h;w>]9:'u՚ŕ{|8Xq{>`Ke՘;X=Ka'ZrH b1ᬩdi7T7B7&#=Oe~2ǴX֦LZL>^o DFnK-.V-oN$  WzV9 s1 Qk󡰕 ~?L/2G&inm+*1 kD!Nq6.'؟>5Z.oa<@cJޒCY|Y5sg!XЋB"Џq *V$W p CWUf!MeVWߙ!@2f䩓;ك}Ԏ\%S"~Ro Z y{ ubט[+m78!|]DVSs2N J|0ڰo? b&_ A*³Rc>?D wϾ7p«/shem fكD.8N9β B00UV-VMuo&4+moӬÉK۶L;qX.Ƃލ+(p|W7pqaξѹkn' {v2;K&e8ڶēg%<{)Sw{HnAsIp s lqI7c_W8 Qf[P/PW'd a^%T jr`U b+=K"X6Z88]w7,mf'9]䶕KTJ#z6E;8_ <7“M] RoJ* Є2 Ҫ*3AZ#c?U:1{ ^, &B2pD4>oܵ+6%XT<Ԩg4HYG 1e6?Z*]{'!ϭ4ȤC`ir{ߡ>vk>ӫױMu?BQ|SL 0f"N_#Kxt@sD`SxlgYyMD֛PH#:E( !%l;4p _Ldac/C/ BEnoNJ mtH7m:eФQ?J6 |k) I֝Ӗ^ڷ/>tpm.23p |fQaZ&XLpfSm1 *f3py]ױ,ɱq!+_y"zZ2gy.և`gx#n!`]A2WB+'ӏD%'` 8f(R23TaZpd ȮݢcYk^sR-ۖxnl%qU dnN"pG_f<Ǵm:.7ѿk=tsNm-z J@ryBXLY.UyyS rT9yGOwhjow.dK6-\"Aq su\νld&g&|*"#9>\zcJJ"tbaGB45dp^@ g-iM9ýe".յM' +S&5-7ptPrgnd"do Seku ǽij*}++q8'`!U׋Vfe&H (J# ܞIkneP;xca;Ի =yi⇯ȕ=;d$t1t✡(8\;&n6u.Lj?8KD lߛfXLDW  O/kj[zV#T"AcJ7sHf ]-j jQlAcNr@j=sҖ$k BZΎa&cR1syYU,&j6O}s|sc$!SzELFQ1]=.mDM~ % [\ӽ\?Ommn]=<4\Ὣ%Zޜ]ʠ|;tIl7`u莁'ePoa YiI_WR@zPؾoi x_誾M:@GA9jCcw~4ȶ >yKqx䌠Re-ȒӋi V`kY,ãg5r4UqZ1h-ŀ ?I\99vYei@LtMs#CcN;6T\&Bybð0w u7r,.bTYi|S%>_oɿq_]5)c0KN:p8He"i(a)Yw׎IcڌL?'c&z3Q†웅WCeԹ,>^ȔD.v(RӱfGL,_Enc~c9FGӶVpF?`mwK2WsoJv/o |!?Ew#\qmr۸w "L@ d~ [݋b^5!7 #`yBH<27`&m B\N,b Jk|]-4א طmcet _YʺI2.fRU)FJbܮu@CgsVzPVW*c*5?QH24@%?;b;n;gz9AIB2l]+$ nˢ,D+}{z' _5E$%TGUr0azw;J\½g:6vqpA`pRC?|tdx"PTy# wnKVu_keuom-r;'1#M`:_ox(dup&*;PdA\ۃoWCtŲdlGvo]Y:~]-}Rqc&e7߁`썙0-)cG$Q9.s;z);#l"%0,6פs!c],;!-˫-Vt4oD$4;ϦG9RCZ_2<-4 $n\l r9. %[Wd. N`l ײ 7!ăCW'ᄛHR Kw57Wz44h6p]z_ql mňeC/@r~]-p&$NQ6d0~<qFc 60WÀE`siNgH'i"iBݗFyȔHe(_VŔMo\of (gZj6 D6;oh{WKʵ8qeeFHY Ca_ݱV1e+IiOͮEkv*cwWᚗeƉN9Dj~1kssp~>A`Rs%5vMO`wP2s90(J\D|1X0dk!.!Hd؎=+K h`C$(EuE;+aGBx|5ݵKRCkZPY㵪" 8 S@*?aaCI-rRw{Rg2_V6 C& M"53nYY)I;mݾUd!;_$ E{RJ̆<S0N`P2n4Z^+("fPV*!@snC7bלuqڰh\em!S 4)g`ˮW Ѽ᣽0,Oy4e ͛f #h_ %.]ij,OpQ\`zyj㳯[N %N~в97 0,DrĭᲲYfDXCf "fa NL234S.wXTPv"VjԷSËLF̛vDa뤫#@.u搲m*\Pr? 5́_\{+^x# uVs!;iPpG󵦻ultub~s'cz|gbUGg9.75Ɣ/842 ޘ82$Uuo7CVT6"gI(n͹P2DٻQz*q-(K2=<1m$$-3poQnk,WcQ~&qf. +/8dx|]mx%ٷXNoȋ@7_@]":y'>΋4{?tB2ǽ2H\?e93 E;bN>q݇IJWVk8d7dPRMN?7~{ H*( ՖL|"D!4.@W&+td􅬷{]XG_:_ͮEVck0k 9 s"W]hڸ=3;Aw1%QOZRfDW# z:;I6|iCmo.ʉ8S3겵9&:}CeȾ|+5p ܁sNFa,b᢫S&SzeZ@tZmMT`Zj\$ [h+ I?4"?'<8Kq)Z,7M0^*ҔtT{'\!Gb,7Ӊq#(͍VS;j~V4 $b*P̍¥)ԈYb[dy_ AĮ˰`H"g8opYA` ΐMLf<Ѿ7JҜ(;o> X`֝IPn;pNf`(ai#g [nk!A\tLܱo;#5A nk׹lb> $fy/Z<'6u]M3K#v% sƩ4ЪyQpn ߗz"X'#vj6ۭB'ݘ96W $mw²^- Hc"{$޷3n:k׽!f&@K-{xjR {(23Ӣdmmȇ11mV UYVuk• #R~ i ?Mp(LaV>IDAT.>Uknb=6>İn8#Ly,wE=Nz,sȬ z9HB+kYA·u!i,:8ᕢ*CinwX6I?dkך3y9u/,cHvalfCmg6{夥aoJ B#Mw92S8ԕ -Kb&ˢ8R}p&%~~@',oiL'u0jydu]pqT'E>>;=<)EGFH,?;远]=b㱮۶?(BjXdbtȚݩDtUm>-xom݂1 Nү~Ynk >Xl@P I1HFkƮE^>q (y0~rBRrd y42 9HJn/kHt:8??_a'!qwҹS3Xq]`n6+dݍқEoudHǃuc|=(0:V loҺp 8YL`RFE2ox3@+=hvm(80lDF]dp;9QzF=i)H:ȴ&C΁Re 219͞aXp^EV)l.[DpV޿UK:S:xJR 3h`z*_(QHW`D@nyX%օqTUbn"Ի &+@a>M>[HXE:ka ?>_$;x mARI8RoYxʀeO5vR|qv0ä)CSǷ(pq1 G!![ΐ([@zf`.`M E2C+S͐\jqX5kmGKzYYi<{́)N|D"A@p$NR_K9%#pYtiw@1;&Ғ8 ]ﰷ݊Jis,ac<Ǖܣx>`g9N[`).wX55R13T 2;OqFHvj 7u@n~I, xk~Qpm[wL: w@{ D~E{/n {";p DHK9[488r1c hUפ㘵uZ,.NkV ~ħxfjswkCrưs{:Ъ< -GK"vk v.1p҂e;Qw"V{@'OQ7@Y=;vG]-rߞAQkއ:K_°?,OI^ TԋDG WjV4ʋ3K7H7?Md_{-+uMVM1wfYT~_!wy{?%% ZјdU$ٟ&all!Ua߮ He7m>Lwhà.CgӃw_bi ^ȕuս~8PςjWW}aD`0\9. qC(vFXd)]yd6J>C\(?chVBZ{+wxn@\^$C wL:@&U/9A)CWݰKopnAi ;` -69084'$YzmQoZ;S̳W;XLYE_wBD:]8| !/(zݻ)ϞY$f3$Y^&FT+ā}Ä (|,$OځpPn2ܧFhFK5+"Bwz heFyiL̴{j `A @ FɱrC±Zۍ{_tMOSiIVrǛ^m5u?Wxs19t7 幎ooםꈑ KRȥPB۪U͝Lp1{d*8 4ś->Z6k8"\Q@r$OlTRcop4xnkԙ{t8@7d `59-sDkv}ԷRU'Ѧc0tB25%QUܐUW_X->\!{-8R Zj0=I 겆2?8|v 捪$O/kk\#p_xJn\ $bxl}C` 8K9=Ux\Âcf%ZkV-57Zs*8C95m+|i"9by?Eb ,w ٚz/ L gE6,I#,q}*~_= Ck|bI}46%IB'.UnѶ-`Npj'Z}4|{+%yuhC!pQ@[g v蕅 y<{ȸ֫5[ؖ*E*|.փ7~_7Iw$*ȷ5y$G3]2'NE|z;l@]ϗ{6/d!ж 14~k~5MFȸլU⽛5wuT@ \)d1(ebl,76c c8mY3ClYӄ_9dJ^3 2?_ tkq}uU ص Eqps`Y^5޲B0LVkyz v1+Uq5ky ~~w:5-as[+. N $kLSr'X5jߜ)Z;@2y$IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/retablir2.png0000644000175000017500000000237312014170666023564 0ustar georgeskgeorgeskPNG  IHDRRf2sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<xIDATHŗmlSUwmU,j&\? j C/( L|1AS] B/M`""@"Tdunw(/$͹< ?}2aP8RC@~7MFAX6v"o~xa %Ħ)vcIo@ˑL! -;gWۘ+ϜgrPO)=X7?Ml{`8{i{YLaT"񐴥;piR˻p$$- )f3q?1!g4/H6F4b#̚=IJ(yz5Z0[!Ty&Ôc+_Z71ܮފh|hgc GOȴ\b4.َ8wx{o؋@/׵ɮE~dύGѓ/~a:B_gO}!$:L'mR|W;14Iy0ph4=r)" +} hsc4O(Xת{ 0[d7M0> イ\$akbf@(<;ý }4[+sZUkPR0,lAKc݌IXWK#bH,ie=aLfbz8 1q2ģF"g`E09qIxL XB#~%Bi5}4aZkm_c$i bԗ;Sl=q3b+mTK[h ϯiyd=tOҕ> &{qzIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/cercle.png0000644000175000017500000000334212014170666023130 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|d pHYs  ZtEXtSoftwarewww.inkscape.org<_IDATH]l3^ئcm66(`Dr!4%*Ai-M>JQUnR0UFmQB僬qJcca;{g0ٝ}rsϜ9{<˗4te{\WRn2q]!+7$؎vvd'7a(˷a#_ ]rk!S^"){x@"ELAqT Ъ*P[+&mre~:Zxnmv CWɋF?sי4#sˍe.Mqw6U]9/vvh(Oh1ur|5e1YcuYLLӜ1ԵHNT;9]% %q%H#8ͤ9&`M'__d-;w]OaSv Ύdj_lB^nz)NdoܹSEKJ~ٶEɎ!kxW9_8+V$*HHCM48CNXlMmw&b)hYcEu>fr⥏ʑ*?zaT(*Qc*]| =@f-"i *[{Ji21!8@U{(DlIBUƝ[dڒJi#)JPN I|y*-wTv9FRnMStWG7na Sk1_! cgSWZWIE<  QW\=^3=q啛%4-jCsWjc0Sxl?~1m7rVw$u6jj~03hJMۥLbn~3? 0-QI5 uw>YӈwفkńU~">Ɍ+6J!PGZBRؽswġ0g I7Sk8X:~-wѼsu%y>G"gÝaZط~ӽd.9Ŵ 0-A-ByfnҧxtqlkiB6T63cgȚŰ yfy6T5\`wqJm#ͻxޝPXYLd3U Eg_˃lDPӪ*,yI:KW243D6` ]J +/qb4S4r[0^>"p=_6t<U"&>/J78sߨjgWI؎;i3k DRY[Jt?qn;ӌΏ+eز$w\ %i:M[ E?cM4T7k-0W["$BӦ-,3W<l7Z QI R.Q<޸ _A{xA8X 8,f[5^/؋f8IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_div_bleu.png0000644000175000017500000000165412014170666024333 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<>IDATHOk$EUzY׿A\o,Aݳ_ JJvQ M"{ $@7eM{Cw@ fWuu)=WIJƲz3eB I"#MHHr>/ls䅥h<= wT0/{[gG1l>&}^P}gqD5ģTeV`.{@}z=(Qđ0pya7 Lk/(=wd"` `$Ԝby_m``26DFߔ.QS{O;MC T\ X tj0HizGء ~ o.wFbO׹`g_w}oa^}y2y:#7w܄hz-2,玞wn!d* ]YTӲoշa ܶ^^2E?TWmfm`u4W,ѵ'E懷 Y5ċw/PVSӆRX CP {gw[rxf#`ibVr[6D{Ց'= x\]W Cwg: P Q(߾DFƊc`-s4X^T+\ WӼp\;_| P6UYkعv?SYWfIENDB`wxgeometrie-0.133.2.orig/wxgeometrie/images/btn_i.png0000644000175000017500000000250112014170666022762 0ustar georgeskgeorgeskPNG  IHDRQ 1sBIT|dtEXtSoftwarewww.inkscape.org<IDATHMlTUA[@HT%!Qc\ipCFWj W,\qDtƯ[-*Ҡh}tf{yDnf޻̹s 5>U7-_v{ʶeXK:BW}<x{_9ẕ*00w}4m&pvu-g²m]e ( B#4H?hW4(dR*cYjHx Ry!,X4KC B%21U i(D))M2y덗PR&i" !zT `9.e3mxk/u>6n$I0ຽ(lféK\Ƥ:̳@iaZ/h.\Au-;4s њo561=/E4[fDmE àR,>BĜ7V6 ̰{JEܮ-B#UuEzh$O^;7f"E=VԣQ-3K ^,2xBhN[Ϟ+Y֬_=N#ۘlWK;p N.9JM3!-= )۔Q/+ٹidz%pdҤÇ$.a൚w&岉7y$t&+:6a oK|dJxrǗF*x 1:sƱ/\ylhWé΄Az-]-(`طx CJF-$Dե0+`طpgF4;š!<=)eY t9A$۪K!R*$ F4 ¢E+H54A3E U u*]Bql2caY $IرXzuuik_]8cG^o#/H6"tE(=_d2;s+n$ܐ_n _mP бH}Ooawl޲5iΏ2r ֲFuu=g~ ,jTCYZ C :,-f 73::,cM\_nۣOҼ`9 fA,šl6<.hhlZȬ9̀t7%=?HSYAQȒ\a?OBakfvѢO{|㸾̱ 4HW[&|]6IC IENDB`wxgeometrie-0.133.2.orig/wxgeometrie/__init__.py0000644000175000017500000000057312014170666022034 0ustar georgeskgeorgesk# -*- coding: iso-8859-1 -*- from os.path import dirname, realpath, abspath import sys # XXX: Hack temporaire, permettant de préférer la version locale de sympy. path = abspath(dirname(realpath(sys._getframe().f_code.co_filename))) sys.path.insert(0, path) from .param import version as __version__ from . import GUI # initialisation de la version de wx from .geolib import * wxgeometrie-0.133.2.orig/wxgeometrie.pyw0000755000175000017500000000316512014170666020467 0ustar georgeskgeorgesk#!/usr/bin/env python # -*- coding: iso-8859-1 -*- from __future__ import division # 1/2 == .5 (par defaut, 1/2 == 0) ##--------------------------------------## # WxGeometrie # # main program # ##--------------------------------------## # WxGeometrie # Dynamic geometry, graph plotter, and more for french mathematic teachers. # Copyright (C) 2005-2010 Nicolas Pourcelot # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # choisit comme rpertoire courant le repertoire d'execution du script (pour Linux) from codecs import getwriter import sys, os from os.path import dirname, realpath if sys.platform == 'win32': sys.stdout = getwriter('cp850')(sys.stdout) else: sys.stdout = getwriter('utf8')(sys.stdout) path = dirname(realpath(sys._getframe().f_code.co_filename)) sys.path.insert(0, path) from wxgeometrie.initialisation import initialiser if __name__ == '__main__': initialiser() wxgeometrie-0.133.2.orig/README0000644000175000017500000000461012014170666016240 0ustar georgeskgeorgeskWxGeometrie =========== *Le couteau suisse du prof de maths. :-)* **License :** GNU Public License version 2 or higher **Auteur :** Nicolas Pourcelot (2005-2010) La librairie *Sympy* ci-incluse est distribuée sous license BSD, par la Sympy Development Team: SymPy Development Team (2010). SymPy: Python library for symbolic mathematics URL . Le module *decorator* est écrit et distribué par Michele Simionato (c) 2005. URL . 0. Téléchargement ----------------- La dernière version officiellement publiée se trouve sur : Pour télécharger la version en cours de développement : `$ git clone git://github.com/wxgeo/wxgeometrie.git` Derrière un firewall, vous pouvez également utiliser : `$ git clone http://github.com/wxgeo/wxgeometrie.git` (beaucoup plus lent !!) 1. Documentation ---------------- Pour l'installation, consultez le fichier *INSTALL*. Une documentation (hélas incomplète) se trouve à cette adresse : 2. Tests -------- À venir. 3. Bref historique ------------------ En 2005, souhaitant apprendre la programmation orientée objet, je suis tombé sur un article d'un développeur vantant Python. Les logiciels de géométrie dynamique répandus étaient alors essentiellement propriétaires (hasard du sort, six mois plus tard, les choses avaient déjà bien changés). Après quelques essais de programmation en Python fin décembre 2004, je décidai d'écrire un prototype de logiciel de géométrie dynamique, m'inspirant de Cabri et Geoplan. De fil en aiguille, WxGéométrie a acquis successivement toute une panoplie d'outils au fur et à mesure de mes besoins d'enseignant : traceur de courbes, calculatrice, statistiques, arbre de probabilités, tableaux LaTeX... 4. Remerciements ---------------- Un grand merci à tous ceux qui ont pris la peine de rapporter des bugs, faire des commentaires, suggestions, etc. Remerciements tous particuliers à Christophe Vrignaud, Christophe Bal et Stéphane Clément pour leur aide et encouragements. Mes remerciements vont également à la Sympy Development Team, et aux auteurs de Matplotlib, tant pour la qualité de leurs librairies que pour leur disponibilité.

+4PIF7Gmm̰i}1@HymðZzqv ђXyVe*!;J.F7/m}'/P8h\ S,w h҇?>s͜=22d>U<"֬#*ͦ>w,cgϹ EFjKA%*+AHͭ 6D,ZsMRj,c/({ hCv?I!OD2Gi扺Mh]V1fts>ؚ^x=!9$! S*Øp2U]yAk)5HӪ0hVet+EύnIm-'c&pu yKoS 5g,҅{擶$1jZ֬Τ`ͧ'q5z6`L02F7!66^ ][%ƴmCBL饓wm+ fƥl20A2Zf7q66e+%Z֛',K=/3F5PZ隀M A`RsƊR(QxK8?[> ,7LYڬ KYוNɳm6 Ad=ʏ9Ndf}+јEb-uI@(g|zPR>[|#|p>W>>VDNͱ\`;b/㪕q)ňn%z\g<.A`_uw>q1*.tTR:)D`K:/]tU.R%]-% Ե1]YO D&4'}|f61ح򀚅'K>08m7'Kmm'5JTD:QMPpl7'2m=DV N\~ZJwVY!0қ1ސ͎4AcC;OZ2vnS>^Z/-KR{S66&h>yyJTV7A6>#d될>]2tnWhtvqB͏= S#ߗy_ifSpf9Do7Ld1!3|ц^ŎI:>4"ܜ&~R<9תszԶ~DJ0<Ǫm› ڒ,dC,}RM9E"Qqz(nBm-"i^9_Φ÷׫ŋp Qq"R&^@b%= ͧSWP_}j;pc/}`6 ږ; ߙgܟ 4_ mAP<?NL|?ښHF@]7aqH(gJV76YśK3hf4 ~`~ іg2Y%\Os'EPWoc[' 7PK=zPKL~<Object 10/content.xmlMj0gG)h  ]LcbY{Ŷ ?/|j)b3T}ĶdƖ!|LxWsX k l%euJ1)HV4>@\mAUb<v1b^?=k| /:j2N?]T<댸f]Aalc:ތ膯_-?PK "oPKL~<Object 10/settings.xmlQo0)V#*(}6/ H'Db4Vo}YӮ^Wl SMet~h}ilF=pE0y%@J5DXj:n|iGH.#Wz.ihw|ޛBury]#;zl&C=d3:?G!'l`7ʭ{ڭ$Vc @^֮5mt?(,wQ=Uos c \enUαa& }SH{[/f;"Z5tE/Y|!Dktt+ȕ!P$o.Rb0 |NY 31ʰvY+ڗ\nuya 2xX)Ka Șd=b@e8 ':oavkF| =I|bD{IUI1f thsLư:0&8DmÉEű3x;Ѵ5ͨ`VfE*&1*"հۀFQH]鉑G|h~aI)CKO% =y^V\؃L@R-r^ŝ6.kץBًqwo+f(zfPpYI=6U }i> *#u~5;eAT[舼So9Ģɽ%ua@^.YJګ8#YjjMi)$Eg($.&RKJ_  Y=,WNɨՊQvSPMR4ņ2P;i]'h] hWQX@ % n}Fx83#;G$?PK7lRhPKL~<Object 11/content.xml]Ak@ӹ1HA%Y4B#œFMQH/c}3E 6U1A:Ƹ-FS\9͓bYBil_׫pDZ"h(-RI3dkph="l,e2Ɠ9QuA7blJ=n8o5xLG}Tku}u|Xjbz1^{<PK>ѦmPKL~<Object 11/settings.xmlQo0)25*(}6^_d;gB$)#}ϗsҺ[\Pdm~]-`-}z!0y%@J5DXj:nrmǜH.#!Wz.Fihw K^Rujuj\#_8[g{7!Źd!  =!qf >;9yIvJuleZVHj5;ks.2NMP&NZ8Ҁef^weF錋z_B̉T /k $`cTKຜgWqccTS! N%h^!&{#\SҀpRfHCz*@ߩwn:C6sQ}"@zBvS::No7JuleZVHr5;ks^2NuP&v[8fҀ7Eݻے/2]kԳMg\ p%bNJYN@L'ၿb\y)]" EjB(%Ce[pc k'о*v?kl VrpNIP 8@&TU&C.i&p}0|S3(MKP#fK¬Q5NY@ÞpF5Q1y%jN0/*1iFe$t4+"!U7Q 4*dRFBL_=@'x@*^!`ɋ:zlk~PZ<;m P];l׮G$;2?_W1P>:W0ңU9{Najv\k%<ɷyG 6 Ήe{KÐ2L=tU$KWIFK8 (qS CI΀qD<]M7X1$z YF%QkEçSle!MCwӺ,HFٺ<^S 0 ڱAKq# ^qGv>8yIPKHRhPKL~<Object 13/content.xmlj0Y@0u0U2vѵ4)Iw{di]׌d9{lI9s`˺0-_7p;?`U?f`!7" C%BL5#$!jt4!@%Rd.R*#DZwrl뢌*c@ F>6$߮& _ $ ?/nttO0ɗ˥De)vx|.wP~~vQe!j&HZRvR;>K1|JI9ZʦQJ4tg>ˢd˓p2ơpT;nEFvH#ʰvQڗ\y a!*xX)+a Ȕd=c@X17=O7C0 ҄$>L1"$*yUKe:41 fi?PS؜UgMhfTF 0AL"bQzj8l@B(j$ bb{yZR2zIF8uOׁk{!5OU%p]ӫ1~yg@m SJ.9ipRfHz*Aߩ8YI:PK$ShPKL~< styles.xml[kSJQ"533$ة]] IA9$'IA|%mv*+|3֏f<4?_22N}@9>)x3=Ï\"vĎwK@Qc[{(isӘ`5P``ű : Z×Vqb/7M2v`yXfT_8t2|`O+$AǠ\V>+C <EB=?Hpw%NId*, .C+Ϟzq5>gRjՂpT*su—^E')*? +t&_%5-Z}lDcѳD7:ta D5ҖAɟ8H -]t-YN4-b\d[ˡ晡LПE~6^#zu>BOF% _r3# 8BgFh=tfU BR_'^.J dp7tp᫱H\ ^J=PKJcwPKL~<Object 15/settings.xmlo0WDyoÏj#jHM@g5ESلP!eI'Db圴a`Yۮ_l >e4}:_Z8S\8&/HKMgMn3e$JX:n`ɕu@k^JZ.WK ~{{lC=ds8G!'$l`ZIɽNT~$?TBccm/kڶBoVYw9)FvzGGeZαa. }\fmԚLXf\]dθEK.ĜHz݀.NvX,zD2tm ʲ-̍X(G?/ /[„5BU.$O@T&VCŠ4hapbٝ9 & $ g3O%aVŨMB, aq8Sϣ˜< ghFN42Y fЛĨ`TnGW#y\3 '+ÒRK2B ]y^VH1YnTKຜgWqscT۵S! %ZƷBLo3NdPrYI ue! #u~e5;UAT萼UR/9IJɽ%aH^>DtU$KWIFK$ Q CI΀ID<]MW7 X1" Yf%O,GIڧjRN)6F7 ݙO벨<e=a0qhx%ՎS_Nm_t:sqcE/Zr!Dtt9,Vȵb'ɓHM_Rb8&|Ii%s.ʰvAKjw<֔0aa#@]@@ePz1^fS&t2s<l2牨m8$vFo0vj5Ӭ`T$FE$ШJ㒘z$^9^@: ^1`ʋ:fd6BOȂ7~TZtw>1@uٯ[0/c/ r~1m<bJw=Be͒WΗ0##ulS{N5;uAT2sHj(sbޑ0L/<]5RUE,`34A\Pm3`O`)05m%(.ezl0CV(U èQҢçSjMe!mCwӺ*HFպ<} )a,^ Gc!o9QdtkǍ43J}d>8yJPKBRjPKL~<Object 17/content.xmlQk0}[^T*MclS h"IX7}5*t{ Gzk<>K+=*ՙT[J֫vC8Iƫm J\"f8YM`\ >zc V\ E(%1KXgKw?|oa4tЙ*nbݰ4Rq糫n鸩 }'ڔ}p&9 PKK)PKL~<Object 17/settings.xmlQo0)vFTPV hl&v ݧMe,H|& W"ˆmЧlٱg_6.G/ R!Rәp;E"p As1NsG[XrePұWRF%lZn:CsQC"AzBvh|vs 4M!mXڵ+>jyyR{ #;#"u2iwm'k|#XHf/W6oZj]N3}^,s"Ud: +:B'ɓHM_Rb8&|Ii%S.ʰvAKjw<֔0aa#@]@@ePz1^fS&t2s8l2牨m8$vFo0vj5Ӭ`T$FE$ШJ㒘z$^9^@: ^1`nʋ:fd6BOȂ7~TZtw>1@uٯ[0/c/ M9?61P;fICiKm if{N:yaߩ=l * Oml9#iFs9jrH`RiQd*ɈpEAsq a6I0 2D=C6w!+s] è]WEÇOE5)PK B@Eۆ̧uUTuy1SF84XjB r׎igxř| ptPK^6tPiPKL~<Object 18/content.xml_k0) `"vV{ mZ&w_kQ{ wy:ϵP#cgDHBهO)Y;~Nydd\I""Ke #uzre;UAT莼UP/9IJ%aH^DtU$KWID]K8  ox:.o2A~.)cH YF%(GIڇOI5)'WK!\@IӺ,*OFټ<^S 0 &ʱASzF F^q|} pN}'iPKTRiPKL~<Object 19/content.xmlJ0} a l'!2Db5MxIlWc M;'9ǝ[Jťc1ȔW`g="?_QAu>i\].}ю(x,Yn8@a8KGa;pRbvȺùֻ @UUV5<`x Z*VPy HN F~{Ĩ^FUd:<ѭb|jZ6.=!x5tg PK!} 'xPKL~<Object 19/settings.xmlr0'S<@B3eZ IЧ18[bVݺ] \Pdm~]-`-}z!0y%@JEXj8nܶc\$ +=#`0%Oe/m{)e:j^5/͍mMztq.*}B=HH&5jN߶v"O5!LX#dXO;rJJdJUj`e1$2m7= 7C 03'҄$L1b$*yU e:4!g8)3牨m8 wFo0v:j9Ӭ`T$FE$ШJ 11H q⽀$ oAoL.&PKs QnPKL~<Object 20/settings.xmlMs0C6@h@[0&$VO Ͼzena`Yn7l >e8?jv>p>>zqL R ΄n1g.AH•t{8’+뀲2rgZ.ϑ/͍⣨d!  =!qf h4m<MqHwZ;@sF%:6vm+Ja79Oj|bdw&Rw(vr>nusi32nyݬf;2端s.%bNʀYn@Lgᑿb\GP{d27m_PoԸ2~Pcڝ>"O5!LX#dXO+u9%A- t^RUXYc ; A D I 2a I@=Syz/ J(Fl:eN {Ùz֙ cD6`^T;gM;]ӌHf5iVDB0Bo"Q hT\񉘁x$n8^@: ^`n:fl~TZ<;CWح] Ii~1=bBu=Be͒WΗ0C#ulS{򚝺 *Oml9#is9jrH}`RQd*ɈpEA3q .a6I0  2FD=Cw!+sY ݨ]VE݇OE5)TK#R@EۆO모2Ud0QF84XjB79QdtkǍ4SNxY| ptPKv+SiPKL~<Object 21/content.xml͔J0ǿ1l,Cfau;ďM@&X;,ᓘnN_#2GwLp F'"e|Yt902V~5||9D 0YQ>2PGs P) Q1x`tNRLsrmxԪPSvXBE ΄Jb4Bd${Ҥ*J>\RL&^3 㓵Ň/gVqάr>Ȇ]zjY?z!\XioH֯U\TB YW|DWzoj^3sQͩ[7PKP]vIqPKL~<Object 21/settings.xmlQo0)mmTPV hl&v &2RF=!~?/y  Zvf[<)ō}ٌz!0y!@J5DXj:nreǜH.#!Wz.FihwK^[Buryl\";[gs7!d> w =!qfծmm MnqH巛[@sA%:6ve+Fa79j|`dw:Rw(vtN:4`rgqsUwE7_M\+p%bNʀYN@Lgၿb\y\?/]" Ej0wQJ )˷|fjBn?t}UXv~&!2u8%A% tv24YFvN6."@ݞ@@iB_z1^fS$t2S8 2癨m8 $vFo0vj5Ӭ`T$FE$vШ=J31}1H r⽂<,)Uh $Cqݔu`ڙ=?# Jy~w>1@uo]0/c/ <`|[y Ę6Cyԏ%5K3_NpTАfWfÖTQ׾Kxo`yG 6 ^ Ήe{KÐ2L=Hj(:dq<U5 &):t6\Tb]RƐg8dr>Z%-j>|JI9ZʦQJ4t'>ˢdu8`H `J8 _i\].}ю(x,Yn8@a8KGa;pRbvȺùֻ @UUV5<`x Z*VPy HN F~{Ĩ^FUd:<ѭb|jZ6.=!@\x5tg PKaf'xPKL~<Object 22/settings.xmls0`xoѶCFh9sUs,_"b=jܓ#$fly Zvf[<)7ыC`Bjt&vˎ9s*\FB\\Y셔8ry}|TT2z;8]j7N߶Nn8- Bk{Yֲ}E>69Oj|`dw:Rw(vtN:4`rqn^Q\x}^,s"U|d:3rB%ɣHMߜipHLX(?/ o[c„5DU\STNg9 Uz5p \_t18 1J3ň0bT&Sа8@M`0&8ODmiE%3x;Ѵ5ͨ`VfE$*&1*"հۀFQH鋱GAaIBK_%림HY6/U%p]ΏWq}#T ۵Q! 2@|>B3N=G(YRtRf@C^S~FN^w*۱f*5]“|g;Zee\pN,[ReA"YJ2\vTxF9 d6I0 2D=CVw!+s] ݨ]E݇OI5)PK!B@IħuYTuy )a,^ Gc!k(2F Fg}d8yIPKDkTiPKL~<Object 23/content.xml_k0) `"vV{ mj&w_kQ{ wy:ϵP#cgDHB<Sw:X Iydd\I""D&+K?Fܼ ‰ML ^JdsĢ(brŎg)V$q֪I#"mQ*4q[2%Ufmrn# ;jNhU_Aˍ͢+oX̤Tru@WjP~A\Uyt5ooPKm!ZPKL~<Object 23/settings.xmlQo0)ҭkTPV hl&v &2)K=!b~sҼ[\Pd-~Y-`[}ٌz!0y!@JEXj8nܲc\$ +=#`0w%OVe-{!e:r\6.ϝiMzft~.*齏BHH&]jN߶sMn~H巛[@sA%7ZVHr5;kcU2N[:R-I]k:η:4`re~\wE术dxW5KĜHxΙƒrbd+RWm Sm`hB~T C+"ϱ5&LXCdXO;pJJdBUjai0$N6."@ݞ@@iB_z1^fSt2SuVG癨m8 wFo0v:j9Ӭ`T$FE$vШ=J゘{$Nt9^A*^!`.*z` 6@⏀ȂyTZ<;;A7خ] Iwd/qD!{~F(Ǥ>Ke #uzre;UAT莼UP/9IJ%aH^DtU$KWID]K8  ox:.o2A~.)cH YF%(GIڇOI5)'WK!\@IӺ,*OFټ<^S 0 &ʱASzF F^q|} pN}'iPKTRiPKL~<Object 24/content.xml]K01i> l'!2D5Mx߹_b[oK{Vl+-%abDy"R7^7 q9GEIszq"<vYjrkFh.뀿@5aCֽpyΕM2k#hHtT"%RJQi)_b-Q6hqbjzѾW$wpm%LbB.+O"΅T-ۇ{ߣzOz0?Gk65jpo:3iPK](xPKL~<Object 24/settings.xmlQo0)nmTPV hl^_d;gB )K=!~?/y| Zvf[1_uo[0/e/u1?1P;=BeMlN'8_* h@S۫rʏ Najv\k%<ηEꌼUP/9IJɽ#a@^tU$KWqFK8 JoxHo+A~.)cH3d}~\9J@FQ>%$\-eSl(s%o3eQy2&?Q`xՎo r׎i'xy=NPKsjRiPKL~<Object 25/content.xmluJ0}  am/C5)M0+YÛpHhݜP2a$ oAoL.&PKs QnPKL~<Object 25/settings.xmlMs0C6@h@[0&$VO Ͼzena`Yn7l >e8?jv>p>>zqL R ΄n1g.AH•t{8’+뀲2rgZ.ϑ/͍⣨d!  =!qf h4m<MqHwZ;@sF%:6vm+Ja79Oj|bdw&Rw(vr>nusi32nyݬf;2端s.%bNʀYn@Lgᑿb\GP{d27m_PoԸ2~Pcڝ>"O5!LX#dXO+u9%A- t^RUXYc ; A D I 2a I@=Syz/ J(Fl:eN {Ùz֙ cD6`^T;gM;]ӌHf5iVDB0Bo"Q hT\񉘁x$n8^@: ^`n:fl~TZ<;CWح] Ii~1=bBu=Be͒WΗ0C#ulS{򚝺 *Oml9#is9jrH}`RQd*ɈpEA3q .a6I0  2FD=Cw!+sY ݨ]VE݇OE5)TK#R@EۆO모2Ud0QF84XjB79QdtkǍ4SNxY| ptPKv+SiPKL~<Object 26/content.xml͔J0ǿ1l,Cfa;ďM&X;,ᓘnN_#rh!>v(OErQNhydz> nD+ʣ+e锝5P`3.X* 1=02Ŧ4 d!)& /v♅gZ݇y×j~+xάr>Ȇ]zjY?z!\DioH֯URTB yW|DW-fbfVy?Sd 'oPKʫIqPKL~<Object 26/settings.xmlQo0)mmTPV hl&v &2RF=!~?/y  Zvf[<)ō}ٌz!0y!@J5DXj:nreǜH.#!Wz.FihwK^[Buryl\";[gs7!d> w =!qfծmm MnqH巛[@sA%:6ve+Fa79j|`dw:Rw(vtN:4`rgqsUwE7_M\+p%bNʀYN@Lgၿb\y\?/]" Ej0wQJ )˷|fjBn?t}UXv~&!2u8%A% tv24YFvN6."@ݞ@@iB_z1^fS$t2S8 2癨m8 $vFo0vj5Ӭ`T$FE$vШ=J31}1H r⽂<,)Uh $Cqݔu`ڙ=?# Jy~w>1@uo]0/c/ <`|[y Ę6Cyԏ%5K3_NpTАfWfÖTQ׾Kxo`yG 6 ^ Ήe{KÐ2L=Hj(:dq<U5 &):t6\Tb]RƐg8dr>Z%-j>|JI9ZʦQJ4t'>ˢdu8`H `J8 _$^+:uA%Ū=p} q9>DIvsyvư"6Od:-Be @4N3?cr- :*VRy,>XyJ75idb/t F;qL&T6EwgM6Stk'yτOOӛ-y;\*$PKUQPKL~<Object 27/settings.xmlQo0)ҭkTPV hl&v &2RF=!~?/y  Zvf[<)WыC`Bjt&vˎ9s*\FB\\Y셔8rٸD>w귷n:C6SQ}"@zBvU];:No7JuleZVHr5;os2NuP&vd-nu3i "nq]wE_oM\+p%bNʀYN@Lgၿb\GPtd273m SoԸ2~T C"ϱ5&LXCdXO;pJJdBUiai0$<m] 7E 0=#҄$L1b$*yUIe:41q?PXe* 3QpyQI&ގa4tM3*#ՠkY IHF56Q!{b5gbb:A{yXRzI)/3{G Sj \*|lc`v=*$a^^y@1mީJ.kg:| 3!l)?"P'/;-٩ rM}$3Vl@&!ezzU,^%Qt.R;Vx*|Tr M,iN|ZE([p2ơpT;6y"[_;n`t+ΣwPK4zRiPKL~<Object 28/content.xmlJ0}P am/Cn.Mz;,ᓘV`&!o +ǮsLXx FxJμ 9RTZn3ю(xY8@adi8C d.#\!Tx Eֻ @YN9cx j*BDYT!K;ry`sku4 ͛3+Ie!u=/s}m;JvR?_'= !5զ5)꣦EU tm LW+4)i0֣ m#PK3W6PKL~<Object 28/settings.xmlQo0)mmTPV hl&v &2RF=!~?/y  Zvf[<)ō}ٌz!0y!@J5DXj:nreǜH.#!Wz.FihwK^[BuryD>w귷n:C6SQ}"@zB֨ծmm MnqH巛[@sA%:6ve+Fa79j|`dw:Rw(vtN:4`rgs9߁>_|7sQk\9*e:3rqPtd27ƙQJ )˷|fjBn?t}UXv~&!2u8%A% tv24YFvN6."@ݞ@@iB_z1^fS$t2S8 2癨m8 $vFo0vj5Ӭ`T$FE$vШ=J31}1H r⽂<,)Uh $Cqݔu`ڙ=?# Jy~w>1@uo]0/c/ ęGƷBo3NhPrY>Ke if{UN:yaߩ]0G%rqX(zD2tm! -LH(Gavl_rVssd V2p]N_ 8g@&T&VŠ4h)ٝ9&%g1O%aV;ŨmB', aQ0U˜< >hFN42bY fЛĨ`T~r@W#yTc'+RK20^+؃ F@fR-r^ŝmW] Iwb/b~1=bLw=Be=%LN387a4'۫rʏ OÖTQ׾Gxo`3Vl@&t 2C"Y3"\vTxF5 &):!t6\Vb]RƐg(`r*}JR>|TprMm|ZE([Ǜ`2DsT;1y C[_;n`Xg{dkPKb0mUiPKL~<Object 1/content.xml}J@>ŸoXd "dM6B77I4R,;/y{LU&> ~4% z_.0yެL1o!b\rH=2YBdʰvQkKjw<ք0aaŸoXd "dM6B177I4R,;/>xJd]2Uє,y#3ż}`[?@Fqe"垪d eg4 !,~z$@ZPGZ/RʀƔsĺzrŽg)$pnT#Fڡ*URGWbҷ-MMZ4AsBש+ϯ_gQx= 0c,m KM6ݦ@4/N'8o:PK2,PKL~<Object 2/settings.xmlo0WDyoÏvj#jHM@g")~6!AH|*"kmpJټi֗fw^WTC3ƷvęDP2pb,v7ʧi/ ]Y.5Sw6wGqbg6Zrmk^hv+C"+*!бkM[! ]yyUC;#סC[sl|}IfT.*7wL:_|[NW5/DHxmΙ8SV;D2tB(%甥[05# k'}UXV~&2,eڜp:ˀ*M3,!]ӠMza&>ffPГħ)F$\-ESl(so3EQy2:?Q`xՎ r^׎i^3=s5NPKd@QAc۽^*8/|ˤ\ )}ytѨO_6bpTܰɹ7%)4ǔ\3S;r;, |Ώ3 }ДU-I 3vz ˫CvhqHJ{0ߩpXPK+OQ?PKL~<Object 3/settings.xmlr0'vL$f5#0&B+[oj[wހ mׯk9e˶4}ju>ps ",5 7ݶ#\$ +=C`4xW6>em{%e:z^7/ -\T<;5j'o[{'BS;I\Q ]k QXfg{γ@ ʤݩa! }sʲۨܔ3rf3tEY|!D tt+VȕB#P$F)1,q eX? OKjwL1"$*yUKe:41 fq?PSؤ* 3QpyQq&ށa4tM3*#ՠkY1 IF56Q!Gb5Gbb{yZR2zIF8uS^ԁk`C$192kTKຜgWqccUS! R@|ƷBLo3N=G(iҀ42̐4*A0Tf*5=|[Ze!eRpN,{ReC"Y3\vl&Lx9F5 &):&!t6C\Ub]Rƈg(`r>Zλs)IQSRM)R6ƈP;i]U$l]l#hWQX@ ) n}Fx8s#;NwPKAJUiPKL~<Object 4/content.xmlQK016M67pC|M@&ڊIL&(.wrfZ(Ip;8LjX%B ^.[]<'h-cefӯ4_^Mn"[41T-Fhj Dhj dm-FBI0 k"$xc̮PEPtvׅK52iD=*W7-C!vvJ_]4tA*7,mrn )4ǔ\3S;r;, |Ώ3 }ДU-I 3vz ˫CvhqHJ{0ߩpXPKEƖ?PKL~<Object 4/settings.xmlo0WDyoÏnZ#jHM@g")~6!A$"srNZwހ mׯk9e˶4}ju>ps ",5 7ݶ#\$ +=C`4xW6>em{%e:z^7/ -RT<;5j'o[{'BS;I\Q ]k QXf{γ@ ʤݩs [B0BrfQݔ3rf3tEY|!D tt+Vȕbdj(ӍQJ F/)K\05# k}UXN~Ț&2eຜpȀL*M#1i.pC4|3D; (MHS#bK¬wQ5NX@`G5Y1y&j|0/*;0iFet4+"&U7Q14*䈒F f &=NW% -ds0`\7yXV"̑[~TZ<;7د] Iwf/b~0m<bBw=Be=' \N38_* i@϶W!;-٩ rM}8߆gg2ؐ2x9'M= 2!U,^w.R;6h&jvּ<]dޑHݡLڝF9p!̥ԗ,j],3}Vm:^,s"Uu`: K:B#PJF)1,rԸ2~Pcڝ>"O5!LX#dXOu9%A- tRUXYc +Ӡmqffw$P0$)FDyjګ$#Yjzτi$Eg$".&R`+JP\+ YaPu-wV5VJIڻjRN)6F ݙO모"Ud0QF84XjB79QdtkǍ4SJ}| ptPKʣ,SiPKL~<Object 6/content.xmlMQK0񾯷eh2 v`t hQn.֗rǹ'~g!#D1jHup+ز>/#=fp|?gEme-"=*'gR4m=qFDX:#}eBge蜋M$pjDϕ A %)y caBFRKS,ΕҖ[D{|C_(;gOU_PKIFPKL~<Object 6/settings.xmlQs0S0h봌zsF{7j Y& ާDz rOoͲ: 7"kmЧlѶg7]K s)aLs"T \K ,({mK)#qVi:Pٜ.NE%Q 3Eq$mk^hv'C*3*!Աkm[!7 ]yyV#;##u2iw-V0̾P_.6j],3}6qQk\9*e]0%rrdf(R%m_PmdjBn?t }UXN~ؚ&2ຜp:ρL*M#1i&p0|3;sLHP#fK¬Q5NY@pG5Q1y&j0/*1iFe$t4+"!U7Q 4*dRF$f & =NW%-d>0(ك c >݀?Uu9Ϯ6. kקBK񵜟okf(znPqYIi6̐h{N:yqS{ز *OmlqtFj )sbޒ0L/S<]5RUE,`=g4A\Pm3`O`)0M%(.ez1CV(ЧQFIڧjRN)6F7 ݉O모"U=a0qhx%Վ]0G%rbdf(ӍQJ /(˶\05" kGٱ}UXv~&!2u8%~% t~24YOFV AEM} nω/4/O=Syz/ J>(Fl:aN {z֩ cB6`^T;gM;]ӌʈf5iVDL0Bo"bQ hT%_QAL_=Cxo KJZB/g`n:z`6@2!7Jyvw>1_u[0/e/u1?1P>:=BeMlN'87a4U9G~e5;UAT"uF* (לsbޑz0L/SS*ګ8#%YjzMixѨ$Eg8$.&R`+J_K Y?,WU%OV5VBIڧjN)6B ݙO벨<exLRF(0Xbj79ahtkǍ4 <I} pN}'iPK8TiPKL~<Object 8/content.xmlj@}ۻ s+R'PP&Pt9L&:H2Jq#IMcZꢛ0|ܙc6"IV[Z88=4;8p7cY!`[ 兡ɂh44gR4L'[D'#2Ϣ1QeV>`[n$*p41SFD%:+D:(6l9pNGc e4ZJw]_>_5'05HP+suP$f&=ۯjFNxb" $GctnN) 3ys uUgÒ}I./ۀLb#8wYPz4a^t PK,X9PKL~<Object 8/settings.xmlQo0)nkTPV hl&v &2RF=!~?/y  Zvf[<)WыC`Bjt&vˎ9s*\FB\\Y셔8rٸD>w귷n:C6SQ}"@zBvU];:No7JuleZVHr5;os2NuP&vd-nu3i "n@\o?ιW5K.ĜHxΙydn(RWgF)1>,q eX?%Wa=EckLȰ 6v0Ȅd=aHy8 /oav{F }Ib{IUN1f)thcN&Tg4MhfTF0AL"Qzjm@B(j$#tˉ T/ uS^ԁkg`$,X*.UevzTH¼$7y@1mީJ.kg:| 3!l)?"P'/;-٩ rM}$3Vl@&!ezzU,^%Qt.R;Vx*|Tr M,iN|ZE([p2ơpT;6y"[_;n`t+ΣwPKbRiPKL~<Object 9/content.xmlMj@E}Bt j(}*FdBwM/e/R$l+MHL,]xdq]CSq1<ݭ6O1 bvRյ.v$O Z ^1H7`Y^ŕᨴ9HO ľ~z ^t5)ui|]ԒCt:#IF j`ر.kx5I~8ῥvPK(oPKL~<Object 9/settings.xmlo0WDyotل&v _?H|:w"k˚mpF٢e?O/n&w^TC3Ʒ[vęDP2pb,v7ڧe/ ]YV%Ss6w9]Gqbg6ZډɽvD~?TBccm/kZBVYw8 vrG~enךNV0̾ҙ\fٽ)g;2F=tE/Y|!Dtt+ȕ9%P$δRb0$|AY33!ʰvIkڗ\ny1a"*xX)+aȄd=a@y8 /oav{N| }I|bD{IUI1f thSL&NUMhfTF 0AL"bQzjm@B(j$#>t|ˉT/ q랼Hf# 3dR-r]ŝ6F.kףBŁ8(q@1m٩%5M곙:pVЀW!ÖTQ׾Kxo`yG 6 ^ Ήe{KÀ2L=tU$KWqFK8 0qU CI΀qH<]MW7X1$~@? XF%Qk$EçSle!MCwӺ,HFٺ<)A,^1Gc9ahtkǍ4 xřI} p>PK"ShPKL~<ʋHHmeta.xml OpenOffice.org/3.1$Linux OpenOffice.org_project/310m19$Build-94202006-10-11T17:42:46Nicolas 2010-03-30T19:02:22fr-FR56PT13H45M34SPKL~<Thumbnails/thumbnail.pngջUT\Q- ww@pwh{4i%w|G}{תj͚v$ "!wP+Ck߽y뚒k;aV)qUO$__iK]TM >|Q9>9j7A A[r0cjhGr\ B Z4MT ka,)S:qk:?#[·k:|Eaձky8(]{ai۸{> }fj3"_!{ZGȞ!+#q[:i zKⴈ4Z"{9w ;"i9-3=]h0>.)ښjV^j9OJnT0Gii/}nW4Z.OŵRm7C(v#2in[z5f6=4en+WdSXc7מ"u SI7u!jª>\=Șyve Gs/כ$\)\rphɎG?Z~FfG( ] Pzk .c\r1fuv̮ZZ*=:@ O-\[7l*Z W/哙;iFQI,7>Pn3_h4_o^oə [dϏ)vŘx${z9 I=Q7 R*ZtppȽly΍ biyd˼}$44R 69E=ϵ: ԓHqm㷥A'QwODQ;qy7!SB0sN{cؐ H6TS s9"l*g+9 b?5TwV>gl?]fxhC`Mϡk4mծ(RZâ}ͦ|ζ%aq_7ÙpKI19"VۃZlHt&ipôG7Kv;׀G4E]>w]N('F7G 8|J2EgALɬ~/Ix>M+VR%9߸t㖴6EСT̙At [F5!nMz(o\[ϖXպ/w\vκufZyrgev>kH/Jÿ\Ò:K ۊ֛5o'1)ePIy%/aAF$' YM-+Oi 9޶ՅMIf4U;i>i҆pgʻm{?!!ǗA{eM, v[xi YmUOM0y> Ȩt֢rvxIn >ط$-% NԷ۵?/&GKR$r_2~K! . R5٪Fhr'zʼnݭ#ͫ.&Y(71r,\VWuZoWpsٵd ;LJdwj0kvJRbՆBByvŝStNW]5ͽG70PV>] 5MpBhX0P/_n}LLIT]zH%~ig-PqPݕhmFTYq> # p;j~.h8Z{`Ջ9pfV356A^_"dXQ"Bb >I8xlrmJE\ X=7rωF_f%L0A}fFX}=X'HfjqabD@i; Q$ 괐(2 :ڛΧ/WԭVî2kp+~ l`G0gⰜˈl:;r΄7C:CRM쐞A>h [;( ڑ$61Uqގ{`VPff_}\󻭸hī\ yRX,G?hz$U}ZO QW٩-R)jRgjJ$J6/{u~ò#ȝTrWdgcbÏ b'O⇴P_l wJbpW&NfiT?\B9QHclMZ @T = nў l#q*n'>GCSܔK W/CjSM"WoMcˣrV cRƹ.Lv )ZZB>Hz)j;cuWtB;52?v0״ר8l.?PbyFyTL:h9/JI 1Q T7Ɵ2Ҫtws}YG@z}8 n&:plg꼭G,z8).)U3SnΛob[{LO=٣8Аj^'6z0N]%g?"e "c2 ǜ~[XPBFRM= q?:+, Ә&bo]B*AeU73 .EbN_81MB|-p.`3xbSIOwpQW$Mo mC>,gp@c'H=?~$tI"QUKLH霐$[@|/l5GI},ghKO$d0\[7 ^:q̠ \iOI+݌Ɵ+x}Ǵo?܀>)؄1 y#)1W"ͅohT;tv(߂ΉRG88ԲɊ(%鎿] Tۊ8K-SR)&%RU&9kKGx'YLx]JK/(d;߽?5|flлsm:BAr@%LnɶytU"*B[z'CzDpr4//&#߯.F 5H'v Ѵ*-}2bMټdRuL3?jS㴋 Fj{\9 J i9yϳX8 ⦜S[\5$E488@5*ոXˍьF(5o;K? $Sf-.#D$,w+ZM#қjS尛b{$7.ajʿԋ:%Ϸp1ٝp4j[;3ǝ*|2TqTڤs Y0]SBɽT(]@'XgC}m4x[䥉l l\}!լ@T6_Y1_dt=e2{(Ժ,˝eN]d)ݮܢ)brePo6'q[>)NUΨBOguOpu*1Td1-=D`]׋yOmH_X-Jn MF9si(SZe})rg\ZQ8;^dR`+DpP?JȊݶn…^M:=/+4^Yj=)xrݱ,ᵝ?l]n_;@}d:TĻ|鏦q1G:X]r{罋hw=\ޅY/E~tZ jS{gGBґ䣬)Zw&t4UJUH1 W%juVs!%dX܃օe `R$ByE!Tէ%mA2w5P1^%w@Mx_A¬unloUW@iw~?z&s[ ; F\I6Șƕ.k>Y¡>Y߄)~DV_T͑zu/%CEUy#6 D&sF lDzlul|tTD@:@clhܐχ!Z0_0EpRi~_\R'Ȯ3k7XAIlwփ핸/.H }7 :wO̤R`nz;}Zxj96t&Akp/ AC<ԟqY,݇/#P┭[¾L=,mk{gM'w!IR_<`qѲ>^<2JWxCW9 x,TEqH=,PLQ1"`+c%GP:i4b E, Ow$jEe@28z@Rg$oM m; VBZTliS[Vr}קg_bx3O636L<|w~=s^LG\rB,? ɓ)d91c_o3XO`z3V:1$'Mb.|V igx&pEiߍs;LU:a28'NzkpDWXSqN.>?V k5;6K_36UX<^i<M2Eo>" O}ScKX8*T]^K.94;~ܨBʽ!г@A&Dcft+O(pcOel:>SVJ.hz+LGOyL䥧qҸgm#JEO bGgQ6`4b%Ss GO7 WS5- G/7Z<d!_R` ssD ktC> r63dY(m%|9l)ri#aX,7`~Ry/|-,1\ q(NNP*V\Xx; k`b#S2UlH<W=_- -CK]]o6lh4#By~`Z*NoeD?<ՠR|۰ɗ_/qŪSS!TYA.c& r~yl2:';S HN-!.<6V:Jm QBXX2yo]4ex:$YWYW T9G&lsݑnW9_kӘ&F>& }F>>Q2CR/IyݏGp̼h=˗$v{UxѾW,J9TC2mhC_0OZ|I*`V_x5 ҽO"l7 ͔4O ޭpvɳ(ķTs! q$@F8,zZ2J&礰5PkV`RgYz}9}~ "MqsusJ8+-Ise^<"g62p ^OCwӲ[DƓ{Aihyoڤ3@ʔV>n0ӡ;iKmPգڴT)&,2*eY7w|0wmGy+|H^]~Rs)T @Mth2u]5~u!r'6Apql ǀC <[!jc{%"0%_&Y]Oa)pΊ JZ:'A}}B]5nei'f2G)xr\T7EjЀ#e%_jPR:ԙ=psjW쉈gה#C%t]ӝCBc!s$b~4pȥݰ8J*k,[SVy$2eVHLz6/3vѬ័~~Szo/Ϸ,*TE Q_}+AֻQ1zf@ES AE+j)Yij?|b寥x˩HXG Hq}ё(gÏRzYMS#n16{`@#$T ,5c6jg.1N>3;EZ-] ڄ5aeyoTf(25龥PRD^6. ߕ0nQn7[ʀG89qŦH_n0'Z+Kgo`#}L(BJeB X !Xos'J0 *H*1ur9BlQ^wU+/CCQ6OLl6C^=ϽlLp=]Ά+_LG^ףI)WTlTվ;i8|4Di*F>LV nw8>\.ghޣ `Wچ:MT_tX sEE' H%!^RnR Q:1L+r)>rd Rsac6gWZk*::ޗ T4aZ4.PB /EA=3o-˟b=9]X b;-0YPQRk2SnMCxf{ZN L@ҵ݄3p`ԃ`D)m!*Ż1=PajONDI=H]Onh#{4%-)˭ -sPwA]BKYC0_De6p$J~g|38(BRj Z1fB5P 4Tt@-܏gFu}QIFH!MZ, h svz "*Ll'w1LJSMi[ؾ0#Ro ѱHņ2Y_vnNJ(VL ېX,G ı:ú$3@kNfi(ad-@Ƌ?DPΝsn^4<1gҕ]Nj1&]$ bU̼B/OZdT2&V2-"pXy{hph)CTS&0!x .B*|yliD -Q,XO[g%ȵו xm~~rE! 8Xi b v6m&@Juc,Y3"ߊ30wԐ76*߄$3k)eB6)^=A.n*껅IE#^D >^tj~mJXv꼯csЁ6R^&s+O x/^hKu d˾WRLNj$3ER Dz cg4ٮ7 WuS{1%K]t$"B+=Qu<., *9ҌfS?JTG:Ro][S8Z&Ie~y^cqOZE489iC5uo&N 0A6j&^tTIȀ5bm5˨Ϧcۘq#.zO$+C [i>{짔u ,| K@ [S"uKܾ7L, @6$Bۂ+ӳ*ǜP:]( >mdݷ&[~p=~(BRO{ms鼙©,㚥ң//ϽFjݚA]_(.tYc@zEd5 a_Ү]I[DЃQ+4ɳo*3Vu7leKZ/wt)1fFK%@'WFr_夋/CLW[ceTRg6\2eۃ2~ ueT 6qitz=C&K$hy{Oˏ@8Ut<L2\ N^j!71k?B "t1wM~ Qtլzz42n@g/0u~W.o[(D=0-"^/|%֩i*k&$P^!UgHd!zD3ޟib y7=AnX?2 Ld[^j4KOŘP?,&b=ꭰR(_%IS/J-/"S!eͼ|{P~e(x2aOw25VnL"Bjs.  lD9E c""Lz`ze<+UÛtʀ:$k#uWhuŮI?aEFl o_9t؝k&v{WOd:\)4Zj[gX5l L7DDS#bVFWTQa?f^5̅~M#&45m QAM(%ĆTC38/8"֫^r0)G\ ն]3yPPU#Ly}+R'*F:|[*עcfpųqAS0~ T,ȕo /=.?v !/$FFL{s|%rg4[!c1/zAmh`G ۲.A&mPSBIh u}ټQ>Wϳeܪ, ^I^p洢LL0HsNCdžc}C%1K[c ?r›1ᆪTnI&W9܆]?'*:|fZ&k+ْlL%%ץG9uc*b">+ ;l$J S: 6 )_bZmwD_?jZK->핹yu] =[#v>z?8HP &,tshn78lizYt7'p[M2M=ŝԾoV 0lߎH,E;M,tn謹^tyv +oTmqW&Wse) ֹDNJGݒ+:@_PNr&$>P~&!Xf;؂ [,sAnVè?S9HiBN+W&=(*-;3pfcn,Ɩ$0-VqnzG/o!FsJ+ZL#RyrxOy#qt-d^wSW@Kՠ]vɬp.t.%ABE[u1= gfb}Ń 9ZlvS)OC\*`5#bvŭsۛ6Z&p|<l1}IsޚQe$-gz>3UtY+>SSựo:B(W 01F 4? |o&5GeC56[Apw2c3Ib"{?[l * SDk_@^-" {\lH,"C PZW4hQ[Vws/jkI\t9^g #k]I|Uo2JE/R|#oU ) ]aT$$B%|<݇RLx`6 ^z0X&% dԭ4;\٭Մ^t,Lb̟h?=ve2M/ZdaXZOO?!;?0#t-QdэRi'xރScF:^)rTg11<>Ć9[nÝDu񳏦yp^ >o5g*|k_8}@ilf#m̨>[9pF,`5 H+ lle&=ĹWh$`ʧ5q+R*F%zA|^)>+(IQJ9E`H`(@qTv@UwhSi AN_j P^t7Ӟ,ɤ)h'! gn8+^!VFz&:_7)醢rUUk>t?k,J'sͳ5LGdX_Y[HuƶӬ%NKޫO+~Yw8-߭rYfQo,D vXPt39GEfUZ@81*jӦ='`"Ɍ1m\`wD`Z?円-j]OgdAR#% R2C5&5P$ɪ{w\OS0@ju?AP9֏ҦLvLX?O*7\:92y=\shcQz=jhؤ쟅DƤsm[ּCqpo S>eAg"7>@qvWJgB$ ! p,諸~͢ ^gǜnެHޠP>̷t`W]`id0܀fRDxJ)l2S7Yw6K5hԾ>"ԖtWVF{#T#Cv@)/U,!_xfڥ->+3w!j{#xQ"ZN3 J4`'Վ)f~~r"gyORřjRr{Y8d@Nq<8jQ|i$"d8~ȰN7ԇɛ[qb牓УV5TfBL$o Seʥ@C/,d%}2 ۊ^ :75\1J2OsVb7UHIJB XBL{.Xd,0j_9]/$^t L Y|*S"qg5EOhqIʗ5C=yRM$'bM؊7a2ª cwmGW~}+D֒c|jnUScv sQTI:LIxUEk3xJ_mp܄Sa\H3²}3tÏkDvIƳr%c56C=X 5kΌ^Q4h9ku#a.Ujy\M`VcDʉFt G]5 ]ޯ]Y&R& Ε <O%k@PKL~<Object 30/content.xmlJ0}P am/Cn.Mz;,ᓘV`&!o +ǮsLXx FxJμ 9RTZn3ю(xY8@adi8C d.#\!Tx Eֻ @YN9cx j*BDYT!K;ry`sku4 ͛3+Ie!u=/s}m;JvR?_'= !5զ5)꣦EU tm LW+4)i0֣ m#PK3W6PKL~<Object 30/settings.xmlQo0)mmTPV hl&v &2RF=!~?/y  Zvf[<)ō}ٌz!0y!@J5DXj:nreǜH.#!Wz.FihwK^[BuryD>w귷n:C6SQ}"@zB֨ծmm MnqH巛[@sA%:6ve+Fa79j|`dw:Rw(vtN:4`rgs9߁>_|7sQk\9*e:3rqPtd27ƙQJ )˷|fjBn?t}UXv~&!2u8%A% tv24YFvN6."@ݞ@@iB_z1^fS$t2S8 2癨m8 $vFo0vj5Ӭ`T$FE$vШ=J31}1H r⽂<,)Uh $Cqݔu`ڙ=?# Jy~w>1@uo]0/c/ ęGƷBo3NhPrY>Ke if{UN:yaߩ]0G%rqX(zD2tm! -LH(Gavl_rVssd V2p]N_ 8g@&T&VŠ4h)ٝ9&%g1O%aV;ŨmB', aQ0U˜< >hFN42bY fЛĨ`T~r@W#yTc'+RK20^+؃ F@fR-r^ŝmW] Iwb/b~1=bLw=Be=%LN387a4'۫rʏ OÖTQ׾Gxo`3Vl@&t 2C"Y3"\vTxF5 &):!t6\Vb]RƐg(`r*}JR>|TprMm|ZE([Ǜ`2DsT;1y C[_;n`Xg{dkPKb0mUiPKL~<Object 32/content.xmluQk0}잺z2h0`PgǠI Ԥ0oXɔ]m3䭑Z@*u%)c2_†Ei-iAkB`_ʅ%t{BLdW}q|}=T{22뺠[\pZb/ SVS|)Зzؖ۲A@g>GH@&iĿR2V?H31JPKPKL~<Object 32/settings.xmlQo0)FTPV hl&v &2RF=!~?/y  Zvf[<)WыC`Bjt&vˎ9s*\FB\\Y셔8ry}|TT2z;8]j7N߶Nn8- Bk{Yֲ}EΛg5ÁL0;r;I]k:Y [L0B}ȳhܖ|/s.8}ɒ 1'R%]'s_@<.L"5}um!s-Pۏ]h_rV݃_9Ƅ k icGpNIP 8LL#,ӠMafg$PЗ$)F%դB-eSl( %oeQE2:b0qhx%Վ r׎i'{d'19PK/\RhPKL~< settings.xmlZs8~"^a:^ZZ\߄.#[ɐIԶHliiwo77x9Up2˜mm6|uc% ͥchRw׷Ad#Yc :/WwFpxI;z~~ ղqn~irWTϊyuj;#_tqػ߽)?.bؚvsԂ}?NClic)׭fq,|CX">ج Mnm&Uֻͫr§+\O %sDL)=k _0Z_&,2 *0jCm }|`67dn?S|lUȐjsZ+IQNT |]ItHod@Lf$0V!bf(Du]{q\,2m*i@+9C CjI)9 }P߼@T߾zᘥ)\ա!h ,回x7"P̙:LvxޝԵYg]f`>̧ /!D1O1ot:qO0)2ሖD4m,QD#X:#y3tuIׇ dbHyaa7 V> nʞ{~e7}X%Ι6ZH5q&#*PLǨԔ?iyM(#m-Uu#L):&Զ1)f**Ox)V R}`Xĥ!Í;R)Sl=Gr|p JePhHaA{vig\'ֵeͩrTP}hb Ao~hO f*_+i[[6&3&z أ0GBC}rlOO<^o-s' [QTQd֦**iw= 3y¸,帛}?(Inh~BJl',iqwPKEH!PKL~<Object 33/content.xmluQk0}잺z2h0`PgǠI Ԥ0oXɔ]m3䭑Z@*u%)c2_†Ei-iAkB`_ʅ%t{BLdW}q|}=T{22뺠[\pZb/ SVS|)Зzؖ۲A@g>GH@&iĿR2V?H31JPKPKL~<Object 33/settings.xmlQo0)FTPV hl&v &2RF=!~?/y  Zvf[<)WыC`Bjt&vˎ9s*\FB\\Y셔8ry}|TT2z;8]j7N߶Nn8- Bk{Yֲ}EΛg5ÁL0;r;I]k:Y [L0B}ȳhܖ|/s.8}ɒ 1'R%]'s_@<.L"5}um!s-Pۏ]h_rV݃_9Ƅ k icGpNIP 8LL#,ӠMafg$PЗ$)F%դB-eSl( %oeQE2:b0qhx%Վ r׎i'{d'19PK/\RhPKL~<Object 34/content.xmlJ0}лX6 fvY6)Mz;,,]Z݅7|ϛzaRu0b");oəwFA^|>h](xY8D+8K8@+cX-ppRbHӋP\j4ӌXw:@KgTh(ec%'[<ƾ?NL[v+]3o>h&A* RSmҡ7NPK#`PKL~<Object 34/settings.xmlo0WDyoUCn>/gB$)#}ϗsҼ_\Pd-~Y-`[}ٌz!0y!@J5DXj:nreǜH.#!Wz.FihwKZBuryD>wFMzft~**BHOHj'o[['BSiR\P ]k SXfg{΋@&ʤݮ5c [L0J}Ȳ[k3|l fɅ2_9YxX 8/]" EjL(%Ce[>35b kGо*vKl VrpNIP 8@&T& Ci&p}0|S3(MKP#fK¬Q5NY@ÞpG5Q1y!jN0/*1iFe$t4+"!U7Q 4*dRFLL_=@'xo KJZB/}0`\7EXvf6@⏀ȂR-r]ŝm P];l׮G$;:W]e #u~e5;UAT茼UPIJɽ%aH^Dy*ګ$#%YjjOi$Eg8".&R`KJP\K Y=`Pu%Ov]O>%դB-eSl( %oeQE2:b0qhx%Վ r׎i'3=s5NPK ERiPKL~<Object 35/content.xmlMj@E}Bt j(}*FdBwM/e/R$l+MHL,]xdq]CSq1<ݭ6O1 bvRյ.v$O Z ^1H7`Y^ŕᨴ9HO ľ~z ^t5)ui|]ԒCt:#IF j`ر.kx5I~8ῥvPK(oPKL~<Object 35/settings.xmlo0WDyotل&v _?H|:w"k˚mpF٢e?O/n&w^TC3Ʒ[vęDP2pb,v7ڧe/ ]YV%Ss6w9]Gqbg6ZډɽvD~?TBccm/kZBVYw8 vrG~enךNV0̾ҙ\fٽ)g;2F=tE/Y|!Dtt+ȕ9%P$δRb0$|AY33!ʰvIkڗ\ny1a"*xX)+aȄd=a@y8 /oav{N| }I|bD{IUI1f thSL&NUMhfTF 0AL"bQzjm@B(j$#>t|ˉT/ q랼Hf# 3dR-r]ŝ6F.kףBŁ8(q@1m٩%5M곙:pVЀW!ÖTQ׾Kxo`yG 6 ^ Ήe{KÀ2L=tU$KWqFK8 0qU CI΀qH<]MW7X1$~@? XF%Qk$EçSle!MCwӺ,HFٺ<)A,^1Gc9ahtkǍ4 xřI} p>PK"ShPKL~<Object 36/content.xmlMA @i9IAZdqUr ]ߚ]0ST,5E*.2/n59c?i pl{pB> T*ȏ| #"̃M="` sɢt1"jjf\.%Q뭵(2{dҡ^v;T6%CdzU˃3B PKp@PKL~<Object 36/settings.xmlQo0)UmTPV hl^_d;gB$)K=!~?/y|Zvf[jvڼ缨dܑPݡLZ9p̥t&ivW%-X|}sn:^,q"Uu|`: KzPtGd25髂QJ /(K\p" kGٱ}UXv~&!2e:p:πLLi6p}E8|SDs (MKS#bK¬Q5ۄNX@Þ`F5I1y!jN}0/*3iFet4+"&U7Q14*䀒F /]N7% -}d30`\yXV ̐~TZ<;wح] Iwb/b~1=bL|t=Be=%LN387a4'۫rʏ OÖTQ׾Kxo`#Vl@&ezzfHj;dq4a諌#JoxHo+A~.)cH3d~\9J@FQO>%$\-eSl(s%o3eQy2&?Q`xՎo r׎i'xř9d}&iPKbpShPKL~<Object 37/content.xmlMj@E}Bt j(}*FdBwM/e/R$l+MHL,]xdq]CSq1<ݭ6O1 bvRյ.v$O Z ^1H7`Y^ŕᨴ9HO ľ~z ^t5)ui|]ԒCt:#IF j`ر.kx5I~8ῥvPK(oPKL~<Object 37/settings.xmlo0WDyotل&v _?H|:w"k˚mpF٢e?O/n&w^TC3Ʒ[vęDP2pb,v7ڧe/ ]YV%Ss6w9]Gqbg6ZډɽvD~?TBccm/kZBVYw8 vrG~enךNV0̾ҙ\fٽ)g;2F=tE/Y|!Dtt+ȕ9%P$δRb0$|AY33!ʰvIkڗ\ny1a"*xX)+aȄd=a@y8 /oav{N| }I|bD{IUI1f thSL&NUMhfTF 0AL"bQzjm@B(j$#>t|ˉT/ q랼Hf# 3dR-r]ŝ6F.kףBŁ8(q@1m٩%5M곙:pVЀW!ÖTQ׾Kxo`yG 6 ^ Ήe{KÀ2L=tU$KWqFK8 0qU CI΀qH<]MW7X1$~@? XF%Qk$EçSle!MCwӺ,HFٺ<)A,^1Gc9ahtkǍ4 xřI} p>PK"ShPKL~<Object 38/content.xmlJ0}jM67pCdmd4:Dr>C$Y+d7|s|v% 3$܁u_:p66aاqoz7U;bxxMK--O`>mHFCuayʃΑz.SR6Bq[q#[&JH(!ኹҠ")dX5Sب?-HQ6I )s)r?+5hg *&Y/JEvSX|0hpϩ_)IQi!.¹PDlg<7DID!'laZ۝$Nk\Q {]k SXfw΋@& ʤݩS [\0Jgrfk9?.>4S|'Re,^ק _D<L"1(h`HtS>ʰvI~kۗ\Z̦䲞l/a4'۫rj1U OÖTQ׾Gxo`KVl@\ & 2=@"Y3"[vTxF5 &):!t6\Vb]RƐ3d}~\9J@g,GIçle.mCwi]'l]/ChWQX@ 9 n}FxaoN?F:PK3j_ZPKL~<Object 39/content.xmlj0Y`IE*mNaT&[cvCI*Y;:9vSs'=n{+.kȁ _\L=8WM3Y+0bbgHS.FY00A| %0E@":L먅q$(fSTʢbؐ6,nB91у!+I"uU҅J8vM4jV 5eRއ*XnpW xYz r9[f3!BjXِriL\9as]KN(OAEm 9f PKtPKL~<Object 39/settings.xmlMs0CHd i@Y@HrJ?:e}]V+y |Zvf[, eX~?LKj7ya *xX)+aȘd=b@y8 'oav{F| =I|bD{IUI1f +'~f&xf%(0e T]JԾ'L1.bFet4+"&UQ14*dRFLLOM8{,y(sV2 ]&ֿ$ZXd3~pW|^BoV.)e AqJ2u|u^WE\g6h+]^'7V޾]%﷥],$63Zo|Y:M ³zxx"_'(tݘ 3ceQq]U7kA x ,ʺlfF *6u+ݶE <ƺm3S:h3׈v d8NN쏛l{a&9^exL '^KuHt/XV{%|3hF#e< z5'uQeuj Vg7#2<<$uhp0䷣c4gj>hxvC? kz><~jEo3JRIlUYoϿ4@ƷTߺ{ooliĶ.W)뗔)w߉~)w_RoH2#eF)7Roߴمv2PfʜBQ( e60k36eʬy+37Vn\;@ EM@-IP/¨GpHz["cz#z# ꍺUmjr>I6_2L \&pԛbԛ՛IPoQoWo.A9D=# I@ $ aC0Cp! C!8.zX0Ap A 8k k5Hk5$5 Nt R2&$3I,!c1<Kǘl 1&sXIP,!w{:Ouv$ur8,a1 g*U*cK*qFG6̆6SK6p%cЍ1p|c c8c 1X7E? PK$68PZPKL~<^2 ''mimetypePKL~<MConfigurations2/statusbar/PKL~<'Configurations2/accelerator/current.xmlPKL~<Configurations2/floater/PKL~<Configurations2/popupmenu/PKL~<JConfigurations2/progressbar/PKL~<Configurations2/menubar/PKL~<Configurations2/toolbar/PKL~<Configurations2/images/Bitmaps/PKL~<$-Object 40/Configurations2/statusbar/PKL~<"oObject 40/Configurations2/floater/PKL~<$Object 40/Configurations2/popupmenu/PKL~<&Object 40/Configurations2/progressbar/PKL~<"5Object 40/Configurations2/menubar/PKL~<"uObject 40/Configurations2/toolbar/PKL~<)Object 40/Configurations2/images/Bitmaps/PKL~<2h^Object 40/content.xmlPKL~<Rh+Object 40/settings.xmlPKL~<fkfk-Pictures/10000201000003B4000001985F81C7C9.pngPKL~<[?-rtPictures/10000201000003D0000002707AEE4682.pngPKL~<\ZHEg blayout-cachePKL~<(68ObjectReplacements/Object 40PKL~<*UObjectReplacements/Object 10PKL~<7l)ObjectReplacements/Object 11PKL~<|2ObjectReplacements/Object 12PKL~<̘ObjectReplacements/Object 13PKL~<ObjectReplacements/Object 14PKL~<,r!pObjectReplacements/Object 15PKL~<ц/ ObjectReplacements/Object 16PKL~<|io./"ObjectReplacements/Object 17PKL~<&TXl#ObjectReplacements/Object 18PKL~ObjectReplacements/Object 8PKL~ѦmfObject 11/content.xmlPKL~<$Oh9gObject 11/settings.xmlPKL~k@bThumbnails/thumbnail.pngPKL~<3W6]?Object 30/content.xmlPKL~<&Si@Object 30/settings.xmlPKL~}Ьe?ĵ~xbMbGA# 1д7( A0 ()U6mG/5Z]F8]JSɤS>I4I?.QxPShKeO:9p<2:d#*2Nq&61mvAM`I{ pAUw9 CY ؋VǗPgdB-i]<>*һoCk('h3Vtӭ7I 6m~V0c~LF DnRy ^A؋L%E0B$^WKa-2c20tZ?L,eTiya^i<+aiH*|)%%T7_XwVabrܧgƎR- 7ɘ- ڨ> cIYE #K,?] ˚|@֏flfp^/ "IhuB`@ tw8a1͎x7d yu?'O1jNs0Gokm{X,Yw}H>tH5Zŏ$o)~⨆)ZAB#ٿ1!U+pw_"#4W5q|8qƤc|Uh^f]_ZhHQf(V(^աx*E[G+C}n`Ƹ> 7~uaf\mۜ;'i{r)n9`^\V`eb#sӃ̥IE0q=[]8ty(~PvmM{| :?bTZZ峉_̦ͦ*Qi63L{ek}==)E,T84oaUx})Ə͹AhS=#ǖs*N֋06հĐ:QyQ0 #&):R P #p:js "y:7(:Wj:r%Dp]xm7).E*OA? }/z&꠴Ί{C\0WQ!7^5c04< C*"%L*Qm>u=v/BQSVDS xmSzA"\q")t6]>E9򀂻??|/PKD5 Z .SPKғK5D5 Z .S geogebra.xmlPK: wxgeometrie-0.133.2.orig/wxgeometrie/developpeurs/maths/distance_point_segment.pdf0000644000175000017500000077520012014170666031002 0ustar georgeskgeorgesk%PDF-1.4 %äüöß 2 0 obj <> stream x]ۮ帍}[ 8A{d2@J%$j>dkH(= ^?)ǿ!xڇVB'p¿_W~ǧǽ~ǷC=y`lo7Ÿg{^ȟ?1|sO|L~B*_])v\=/}1"xh^ۀC>Њ !ۼr6s߄]},L(xpA̴iaA0(0}=OƄ,mxZJSRB;3 3:2,0\0q! JUFz@bUgf\g܌_Dg3v`OZg|RcplP77qK wb|NW7P?MV-;]?IIܶ<"-]Q2;yF嘚0͟IBԎ7zS"f5E!70o߷;r2 ۝,2Ql] Dzărl$|?Yrj|>0Mfׯ 悿$ԝG_QQ  zԏzr;ki+|8|˷W+h09$eUs pd ѡ : O`FU"DB-aAn#rp%..3|3<=5<,[.C\/G sSA At6ڈ%Z=h57EB;!"bLc~$ g&8 dzO.M_;H&EP@)d ῀)۱쒏 h33ef~V8|# qt si$ݤO\BKω߈푐 .lW vY3.BPƴ] _F03gg+ha?ѲF24tS3UUF34?% ~6CHGAH')Io2 wUNhQ5Q`xWWQ*CWܛ*lI5ýւʭ.)oblCtbK =o]>1 l%+*(A6*4禐 7WEX9'9UzE%pvVGv?ƽW04 wVTX?D((8&a; 20&keɬ$%s 1g ,)>VN"\BH\$V+3kmZ˘ Z&)RbW&󽺶r9-˟x>S8̜4 M)Ta| @(.: ,t\-B[pVF0u 3==i}omgpXxNt!ןq$Lp"ΚTǕލ%; DyjqT~F<ɞ9QJL5hYĩwA;0ɡb5K-sX2;JOJl 0SK,KEz2g 8fN& 蝯~{g+l?`o%&.$j >먧 >g:E1SI9,Dw{!))ku;$ ƣ|L^׺#T~ku% r18$H0:ڙm>fH}p l*FֹXKkǰ|c /Njt>lOWjZGgUs[yRXf5+_ǯm\'4KCݩmԨRMEJ نFFFqް"?}Jy \p40b!jh:3Oo8ޫS`Tҳ83,H ^9EaQT2OWeGҲ\5j ҆8&є1oE3ZV KjR2󘗠R%-[P;]Z7bZWsWҪ;=yϪ9:"PܱړRGjߣ (p$*w,W?3QP񇴸W#ŷo 3h̢Y%2) ݜC0 cŗ8Γr:*U@emYZ`CZlY)`Sʧ3{ a3d *_ԘEnJ2AP(yZTp/7@mT<1\zڈn#06f+DmЦ#V k#>Q6r2\i#Nj^eln&!U}dIU{=GJH)FFOgK0,_%9O/d-F(f$6^UF8ΖKep)g<]]Ȃj37 J˼ƹ T\x1x q{Z܋ۼ18ҖEb2' \?J)C\]`a!Mh2RCt\/ nx#Lk8^T]"g#Ўf0xc,jA}v3v3gbhQײ.I<={f83>BKKrJ :OEeUg/_ c@%(ge?yULA#\E:E71q8F~@Uu"f0+'34; .q4 v1+y'҂Q͇3H9r2^KgъU-V@fn3$8l'd.dx vdVQxn @ӅKԼ{, UFǞPr^9qa\[}T0/uzƇ_جR -VU0di"7Tn6[F>tU KG~QFlfcz`Xv"-WoYMp"NP.G).z}j#Io bcK+^Ģ5E+I$BTvu'1U1=Ă3P8$)DvȥŅXDA"ˆC&% v8kD(1FXBRMKQ|v {igEX哎$.u\ VF0v$ ʼ},ܬ ŋh#ŗ( %͓ԪL (DƐTfha.(Y2T,0x$bM+HnrwEf\E뮢M3W &;3WL613&jnwe D'5C`–r\ +g;x 5(6SUR*rb}%(IXk^}+,NJ2 isBNDc1愹/C03&`}MasWP計ϬrjmͥUl"evs&%ssf Mr><ϯŢs37=F5űujKгl] ,fn݀/KЏ_n endstream endobj 3 0 obj 6144 endobj 5 0 obj <> stream xk0 D,#ъfkוmPqOA!2v(ͧu8*uiEjӄlA)QEmZPNр7+ٔsș|$~tu}xn4?ߤ/2bjTcM\P>WM7oތK{>Su<|iӦ?v8d\kj*:yd;ܵjܑgyKR_=܅ћjI**رƍp‡n5bull,oHM侽]j?|qꧣ?)#G>NMuSC 7oތ#OTodZ>burr2S兏Tͼ窍CCC_,#|l0wRMTg'_WXT5|ickk\uׯ_2?ԤĊT*u̙!|&{7fzJX=zb'@ ItPcCC\u׮]V+|&76f*#|&%ymGXBOФ-NX/J_ u'||&}T?=Ċ7nD4ԗ1]fbjKKhw@ Idמ݉/?EL޻1{/|^&a?~T|cǎ|> P'eҝ|Sџ,|L&U\6b̙3_5+|F&m ާ+V9R,'jPL^|9b5NrO Z>񫗷%VGUԔt?ͼζsT*e*+(|(&g X-Jџ |"&H=P>WݱcG>z0i~Ċ 6EdTAەXzĉ *>VC_U)|&F J窻vr'|%Rtqݺ###U&|%^vnߖXz̙j>Vn)bVpg^?ilhHXfU |%M֯OXd2џ .|%Yӓ_ĊW_}5+Ϲl0tbb@ rIkgO'VvvvrJ>ޒB~͍"8-)w|GG⧀KRw @ lI̒*;o+RW_}5 X$K޻1{===_*aXRXNo޼#|%U`οs 6X P§WRe6;]{v'~ ̙3џ,k-|t%Ul ÇsbvVRllh(vttro5>*Lb͛Ƣ?_BJf}g[Ot:GTuM\|aÆׯGTrٝ۷USԙ3g?hV^pJ sG.JY P'SRUwPcCC\uǎVԒT_|y汱>jɮ=>jCjoo|W} EIԹӧ+VU-|%Xc#-mmsՖׯGܣ T{r}{?<88X*xZIՎ>b1SMMsN+VKIƯ^~ᭉџ>w*|$|\kn+VTIXR&~bj-mms͛7[ PgLR5;=i*@ 0ICKW...F $+Vwe*@ +Iܲ+VSPI]zĉ>QV[ P gI=+V*S IM\tH9P§Hʛܹ}[\5JY (|$)Bai+V*DHҲ;}*J%V޾};;#IV IX>3-bȑ#V;ɊU("Iw---L&qs"Iw:/LX}WZ>$tW-JX(J @m IΝ>T>WWO>toM\O|aÆCք$s3ᄈsO9s&\)S!IّgIUbP#AsO564U͛7Z> "dU7md* IZש}{XXYc I+^|!JY R@V^b`O$FVяUjwvnf*} HZ s/|cU>]|\sS&|#i v呇&VE@U |#imڳ|_*tIk{7fzz">呴Ɲ=5XcY Q;־kWY p'‡;B*;obOv$EP8|ȊU>֑OOU>Бo>V+V>͑T rٮ=XX*|#BZ(y+V8*_dsSHՊU>TNOe*J>\**9rT*E[k*|j#;{j|k׮|>}t𑍤 ol$sL&}zyowvn?~<X IUBaȳ$ݷoߎ>VWFRtjggg.>VQFRu𥍭###чj Hf'NcǢ3U>Twc\b1TXaISSSJ HXj^DFRg*P1j +V>T3-b5Fs%| #Zv :{>TcY Ԙk===g] H~~'T|N|pw.j8+V>sTNOnK?)^z)SI5GX}v§-ꤥ+V۳l) ,eWF'|"ZvKRq zkb1DXFxER}tjggd>XT-]J2L!SIܲ+VF>RT-bg~~>y$}t:G*/}ӟ*677_p!]E>vٝ)cǎE@] HRy +V55}d(|t"I6~򖶶ĊՑS;sIf}g[j*:qD ԗ$}L +V8}|"|b"IgO764U;::XFD\b5D@ Hv+?ooW⧀oJeSI s\X,F@ HvsMM*P‡#tM\b*6'#tor'N HtWvD$>tƆj:f+P#§!tfZ>b… G,P G!"NO~K;?<44}U/|"I+Ba+VbY T!$l=Xٙ墏[Z?$iŻ~-mmsT*5<<}U)|!Iѭ̾],J.PeJ-JUXJCVϞnnjJXѧ/P5M\Xܜd`:;$i *O<]>WMR'ObDIZ T*bu~~>$*ZCֲ^klh(i+V>57yx nHWe~Î?}$(|!I!-JU8P,f5$)_dbj{{d T$6qJbjKKh Tq$w+?KWJC>ːJh0_HRs՞+VA$UN=ԔX:55}TGTQM\|ܜdOk LB*B.sj*> I sG.buV@ \HRvPcCC\#F YHR%7~jKKh $Uxӓ]{v?aǏ>5>o07pPb3??}.|T!Iҹӧ+Vt>>>*jl$-bg9'T]r}{?<88X*Ot`U'$;sjoooX>>*SMMsN+VO%$zz&VFJ IHRUWebjXz׬X>wZm?pw‡TY 5 | I5bEA!+Vz$NvŊUFSIf'wnf*T$U X*>_:STʊU IƯ^bBXA6+V*$b*_@AtjGGG."%H܊7ѷ`*I:/b*MAA ÇsX 'DNjnj*vvvT@kW>OU7l0::}i@= Hmwvnߖ)3gDPw§ȳ$VZ H>sO564U͛7/I'66V>Wݴi6';oڷ+S'NF $Iw^|!JY k)|@ IΟ{-b#G'P§͊UXKI=4;;ob@\@to-^ƀ‡皛+VI}6q#o-X +%| If'NX=~xTg$I+{7fzJU{{{X>$`gO Y ++_M\͊UX)ܾ͊UX/h07pPbc*ܭgI?jlhbG$iU~GZ>Wmlld2T~IjWebMS$i Z(y+VK֬_dsSpW_%Ik[×6&V׿r?Kָ}{X;/I ϥRjww|'U_SC sխ[Z OF2__>Wmnnd2ѷT|IRl3ᄈs+V/( sG}&1W}v5_%I\.`oK[[+VGFF/+/If'NcǢ+z/I޻1Tbzb}k@{IRevPsSS\}jj*h/IƯ^b_%I侽]VPŸ%IߑgbP/I]f1Xuji.\`u?K(+VC󒤪[} i$Cosm/677_p!O.I O$V;v,&O.IJUWb.Io.]d/ItIV~fޮO.JW%.]o0oMU{{{bm @] B$oknjJX0_$%vŊU*G˹$IK+O<ޝ)A+VX{$}TKW߿~~>?K1]:FcCC\5Ng:Z.I7~hĊ .D_ԋrI>/ig⧀oQB;$IwBa+Vb] @ $${Ċ\.}PŸ%I_~sK[[\5J GߨԬqI[}{?<88X*UjPø$IBan\ۊUV\$IgO775%V.I4qJbjsss&`$g\ǻ窩TɓV"_%IZ T*bu~~>ꅿKR?ZcCC\5N[ } $i~Gj*+([v?~<Z?}K-JU8P,/^O$I/~v2b}rr2ʄxKzM\X2::}PMŸ%IZngx{R} Pߺ%IZ s{T*U>Wb;-IMMSSSW1.[5kڕ-mmsL&}P߷%IZ 窩Tjpp0Br?nK->\bX`Y/ے$tPcCC\#FT7mIzyckk\ett4rL\-IRHӓ]{v?aǏ <@*zegmIBZ( >GT^ ٖ$)sO%V|>}Q_ ֖$)̖ĊׯG۴iS]-IRH\vޮOJ07oܱcG s/ے$t7coooXߟx=ړ_-ڒ$t榦򛱳ӊU:w̙T*Uz?gmIBz&VFDKXmnjtgmIB*]{v[ @+VS}׊UIRf* ˮX}[gmIBbXg'] ֖$)񫗷Y @+V׭{χ?kKǎb-b'~-IRT΋VPnwc6Y[Ο{͊UXݹ}d$I!-buxx8 [[-IRHVT>bUZbuVԳeW>u+?-IRHVԉ'+V??!Y[޿߿ X=ۋß%I e*]P ٖ$)$+VHXvOX*I۬X`eWNgmIBb]Kß%I ɊUZb}΋ڒ$Ee* ˮX}n+V%Iu\. Ҳ+V??kKool"-bul$-IRH3O>c*單XMR?<݅\˶$I!};/&~ї6a{7fß%I 'Vn޼يU:w̙/v~avz2Y[Ƿyxk_* xbp$I!r}{X͛7{/ڒ$EoM܌ߕߘї6aǽ~+?-IRH=P~3nݺujj* PbVJ궱̖ҊU:7>>aÆĊ ֖$)uʊUnܸk׮T*ڒ$E_x6b,..%+)+V%IusY @ɓ'/X*I۬X`Ċ|So _ ֖$)B.s6+V(tbOO(Y[ s/|cUŃ&+kO~ՊUIRv_o*N8}|;Y[&]yᭉol"oڴxݺwb$I!NOvٝ1Ǐ[ Pn޼c%VOŸ%I i0oMU߿+o߾}iT*=|=}ʊUIRvPsSSa*@{W_??!Y[&W[vo?kK{7fg*] ֖$)C߆V|ɯZ*IΟ{mݺXPb;0;=-IRHۏ<ՊUʍoذ!bχ?kKҭמqƎ;+V ֖$)+}}}{ڊUIRMMVPɓ|G_gmIBvsmbr---MMVJ[?Y @|>k׮ĊW_֖$)ď9twwG_)JGMWCVJ^{7f_%I 쩡榦򛱽}jj* ;o˟-IRHW/[ @֭[_׭{χ?kK侽]VP.ϧT*ڒ$EugXY*Iۖ]f/m"9s&bu[Q+V%Iu۲+V/\}cilllӦM[[-IRHVԍ7:;;+Vx mIB7&===ї6aǽ~+?-IRHKW|>}iipp0bkgmIB|[ڬXhKKKbhf8Y[]gRE HARE-j)xEWZ -%E+RA~,tDMʂ+ҕ. Rf{͵LBr>9jܜ"y/\[$I$IG3&rbuG6?_%I$I$IO8%FUIJƌ՟-z0 jdY{*FU^{_o HҗQUҺiӦEvҒfT%_@2JRT^^^5' "d>Qp'}vx$v⋹/VRQ,N~U?wQU2F~Zp_ 8*YlVОG.m6@1JR&U__xboNuYͨJ6۸bH^EO~fi&.y%X=⃿EŨJ{m gU.Ϩ*Iƍ#'VҖM_w oW-/M^*𥌪8p(~WydO?pW Ɍ}wxX >dPXN@2Joߞ*d0*˾m['c[ϸO $QUѣG9 ʨ |buxX}ïVPIfT$5xw8 ɨ zi^="'V4H&$)G}4''ljU0FUmV ՚Gd $)Ν; XLbTSfsV\bWte@V0J;tШQXaTS7[Fȥ-vյھ%cjFUIR;v$rbo_2BXxy}z=V]ʨ*I:I?Yrb59SX fT$-['rbugMu0팪ЉlrEOnɞ]@W0J#G\qNB2BzgSSFrbRQ}wDn[k&jm'Saʔ)NB2B[=hjQfNqNBj2B;*W+O1Dw_OIU%I]Ν;^8`gMu?6FUH*0~g\*2$KK<ȒbT2[F蘖'V':M.VU%I]ݱcȉ9鑃_!KU!,/=~TQMHdT$%+WFN~o}{WBFUHA/}0/M@<$)im߾=rbzo].!d<*}۶OA]5y=SpQUZ=e_!U!u=ri]kھ%̨*IJrX=N'VU!-/n~꿭i' $)P'V'O{2BF2B{y Z=gQU :wU_!U!-ZbW0sԟ~@HFUIRZ=E} cT4R6tdأcZ쪿_o dY˨*I FUH//cO{kO?@v2JRc\]_(!3U!sx^jo#ͨ*IJZ=WVB0B:j>:ole-vM^}?@V1JRC5*~Ws˃\B3B*U:gbt[ {U%I)U'V?>EҗQڪ Xm^ KU%I)ؒ%K"'VfQAFUHwol~h[/;?w]Ψ*IJO-tB:2B[y˾[9ŮdzlFUIRʖxb5'' ?ͨ lV3c_bW]3W?@3JRVOl'V]IV-^?Ǽ-v'ukO?@2JR'\{B0Bi>Z=ܩ92QU%X>W%`T|bu|Zϸ{ ƨ*IJO8U b Ϩ lVi~‰յמsכ?@&1JҨVOΞuprFU`Xmz| cU%IiWɓ&u'dTv f: ¨*IJO7/VadTo)J<ܴO ,Ψ*IJO<_[!U!Kjn 'VXSdT$oǎ"'VsGbTn⼞=~8̉ULFUIRhѢȉѣ.>_d!uU!}|'VU%IPUUUnnn:ڪ-_g!EU!??}+$)3XmꡅT`Ttm7:#95X2J2iӦEv;Jo_p!,*d_؏:$UV=SP~މ'Vw?4$){jlll߼Q8mV xbuǃw: U%IVmmX%U]ʱH8}?@*0J#G\~׮ ]Ĩ ;=c[O_} 8$);kll뮻"'Vsd$*~9[ IfT$x]3C^)2چ='}v#YĨ*IR|۷oˋUVm ¨ tؾm[NxU؊Z4=?@rU%Itȑ/<~WqkXU:̨ {ԉUQUX%US#rbgfT$DUTT望'M|C{UNmݪa'VktbfT$$'~W=oй_ bT:˾m[_=olؖ'Vg\}?@1Jt:4jԨȉuk "mgT:W٬=|I]uyN$}i 3f̈X=O RmaT:ݺe7܉UQU6dɒȮտ]_KUrAQNy$ȉճ*Ϩ t5'7\*Ĩ*IR+**UsrrV-4k:QReJhT]kX sU%Ijo ӦM(;JouH2]m㊊Qd&$I<'''~W=⃿IEFU vT3X U%IpUUU_Gr@rꦯ_;old$IRզ~"K<3|bKZk=7>1JtGvչߟ鑃_衙QHUڞ `T$S*//_{0!4Xmp+'V?w@1JY}U:Κ/`Th>:>jӓ$I؁"'Vs{\d9*P٬ґgu[*̨*IR0mڴȏ=N'V Ȩ *Ψ*IRWdɒȉdTQhN$uQ[lˋU/**+_BFU 4X7܉UҍQUСCkX ƨ XsbTfT$KOZ*i¨*IRrڲeKnnn:|X$QHA*0~\ 'VHyFUIցƌ9¦@*U:WG.m>s$IɬnUsrrYo lFU [ny[O: @*1J,X9z7~o TFU \}񅃿;Ůdۏoi8z8(U%I Rmmm>}w﬩ހdTR_իEOn2'VΨ*IRꊊw~o؏:3U4n⁽NV: kU%I ^yyywn鑃'1@zQh珼>QUT*///~WpHn V`TΉNnG~%*ƨ*IRxb5gO'VFU M*yV-v5zW>S+YŨ*IRP\\9:sXUտ G>$FUIRƅ Fv뮙ɡ6@ZˉՂo/5*aT$)X>y=;ҔQHw{k6OtUͷfxFUIR1cDN>oHGFU 3*+9szSgZ2QUaڴiH;FU c[8̯$Xmw['V:FUIR򜜜]oNubv1O柛xbS?;X)dT$){ssswѣ.vb3 W_qyo*o^ @1J8p0rbu5K@Fj/'vbNgT$)]O<:瞻? 9QTWT\Яwۚ U%IJON4Nq2FU 颂O.a'FUIҮ7FN^8 eT2۾m[_=ʱ-v|#*IR:vȉuk ւdTn5^8UFUIҴȉٳo0H5FU KT>tHQtQUnɒ%'\y𷻃 U.pp՗˦; @U%IJv٧O]u@/mmè dX}9tdT$)+,,U{~A0YG-zbubw&2iǨ*IRfT__WGNtb/@|ly}z&X}w; @U%Iʘ,XUX *vT.?7j==HFUI2J'V0 _tT}O$IW]]]AAgT;'jmgiRQUnpbfFU&x3xbuc9 U%I,Y*FUfտ||Ƞ'V5kg eU%IjkkXŨ pܾm[',&36ɨ*IRWWWWXXj63k>:W,rb+NsbVU%IʆXrFUDc㺿'NaT$){ZpadWub5KUZmݪVOV3sG7$IYUeeYȨ p"{k6O?ole-vg\tW?"$e[uuuEEENf*ͽ=ri]ǘ3XQU,رcSL(n󏝐LFU/j?"w=v_q3J566.X ''ljl`ThX43ݞOdT$)۲eK՝5ߢйmfcG_/M#>ٳ+9U%I XlFU{gUcT$I'Frb5U:*FUt'V2QU$)S8֌kݲ1DG*@F2J/q'V?xwO73Q\= ?jo; y$mٲ%rb!N*@W[xq}'V+_xW?Љ$ٳg̘1jNNk䌪]g>wG.mpĨ*IձcOO Ɔ1tK9_QωU`T$I_N,*@W{y# οkHtW}ş B)2JqȉՋ׃!Q QU{@'VҎQU$uVN,*DjTu=rͨ*I:VOΞuGۓ͌pFU跬@FFUI%X;?$) zbu/%$?O& OY/RQ?vjW%T,g}%rbu3T 2$)/..X}`~Y7WɨJ ԨJ*|lg7܉UgT$I… #'Vf+UkhXĮ*IҺ7'~W=oйo\ FU꩓'[3.o^$)۳gOaaakJFUl^'V:QU$e@ ӦMK<LJJFU,T~#'V_W U%IRfؘxbߘ{ Ψ [5 ߉USgT$ITmmwNU־m[oV8 p $):r_j<*@[2zubW]3D)`T$IWcc]w9:瞻ĪQucvb-$)S{{N4u߀%Q&;*W |]W&wNQU$ep۷oXpH_ ,Ɍ4{oGM_kyc[Xi'aT$IѣGȉ66,[8ge%HMFUI;v4rbudωU*imG_WKՌ}۶9[=895oq;ΐnS'O_le uU%IRVuȑ/}d r0Jl!NUI-mTsbcX_g+w?G_'Vw?4@*0J쬢"'''~'۳`T%-l[*aQ-.XWSVxbvL'V$)kӧO(sQQ_ UI}mM%OĚwD~p*Ӌ7rl3._2JlСCFer{\vM7i˨JsƷZ. bƓ.-ǷlVŮ5XQU$ey 3f̈gsݙtbըJ{ssvZQm쿿_5{6<Wn㊊~ (]q5 $IM-Y$~9jT%ŕ*{|Om";*WA*$IUUUEN_^UIq{];fsWao -ŮZ9O LFUIeމU*lG\N,VߡQ!qeۺU]1fmѨӓXQU$)iӦE~:̨J*[^~_ܣ-ͮ>߲fve6X{k65@WTϹ]vv}jW]E5i:uٞ׷k_1kZ_ҖMz ]v}ozj~}]/H>V?P:u?x*^_#|y^uo2ΓMyʾbT$IJ<'''~WK[6uw5@MXQ5} uc%خ_ԩu5 **IOlnxFU;qKN'Lל}Y޹-+,GENߌd$IҩظpȉҒ)ubըJzǧ~>bhAWT4]on}n#O9ݻϽ'V!O8 1$Iޖ-[rssל SĪQtqE̩ן~in9ݻO*3^UNfs_dWkѷS9]ǨJ0J$uEuuuEEESNO_vM|FU]Ff~*no76?۬QY?Ȯ:uw7B9щտz;A@1$II(jnϞI;jT9Z88߉UlVȳUXO:Q `T$IJN ő9I8jTxտ|4'Yhݲyg~%vb˨*I򜜜5oNFU89ݻ/*7'YO柛xbW&: dT$I ۋ/uqX5—zoG̩Gy_AyzW\xbi )@v2J$׮鬷|FUh~iƱ]>'Yk& {)@2J$B'Vsw 9*?|o;,qEz'X}NIfT$IJON4zBΨ jW/9Z?1Bmx/MwbH&$IRJqȉ OtBΨ n1S&]*$߾m[_=ʱ-vgk_FUITEEEtQ:fُEN~Qol~6'Y}7; $QU$)khh())FUH읛[ǃb*[:Y(~6ljUU%IR%KDN~޷|FUޚol~YNG8G駝X /xbvݯmgT$IJv٧O5碢ݯҮ|FU]٬OU85; w/o^,@2J$xuuuSYg~a3*0rbu#X ;{Y"'VL #U%IR:]r/kU*.EF&֭:^?> U>>=OӻX:QU$)-jll\`AdWmU*.uF&To(0>ӽ9wYhGsOV3O\8eJ<`T$IJ*++#'V+|m'gT%ݥԨߊ!'۶5' _tT'Vݾt**I^O9_zbըJKQÿXyC X{gݜxbu3W /|aT%#U%IҮn!]r'9jT%ݥڤ; 3x('YݟwWOz>'V3$Iiڒ%KrrrלX5RvTmo։.$6X}gu M/2_͚/?A@1$I[mmm[NUIw<6{-o: Oqj& @2$Ii]]]]aaaxbըJKQsswTAi>:W,rb+Nsb **I}U*.-F&;*W?(*zi^= o>9[+*@UFUIh…]U*.]F&{k6_9vt8oowboۺUXg?:7dQ `T$Iʘ*++[=jT%ݥѨȮZ,]p-[瞧O=ىU(۲3L>gUW: ИD*Iw?9'7̽n ZF՜ͫVʼnUh 7'SgW}jDo*|jcT%FUI{''?sٺ)km*V.\0ierٳse/+2W<щU( :e]5V0HyoզuO>}^K'V 匪$IwQֿ{O~w{!m^z):$N>)fCZ'UәN@jU%IKF՜VrxIK9}3_qoY!_܎_|=~Go*Dɨ*I}8߬)Ҹ,siUZ40sbgN٦كXU%IG՜|vy{IN، [T9 MDG)gI~ XT+G*I} 2g5qbuN_'VitFvo8z0'V)xg!\=cXu%G*I} 8q{6qbbpž|(ǨLn쭩YZoˮ_gT%FUIkQ5=;Mx̘}(Ĩ%x`DT3uvb ɨJ$Ijg{%{ﴉQ9 MǚEu?3_?yoB1$IR5ƨZ7?,]yoaU#X1erzgwM;:aAAUQU$)oTUiٙvY2kmaT/yII:ޚ<__ewAgT%FUIkQX];iHKʲQQ['V `BϜ֧muOlVP`FU"`T$IU?Ys8qwvm௵)vFկe[Z);ԓK~ FD*I}UkK_?s2>12~]g7_6؉Uh ٴwOo*FU"`T$I9ūU#[4;ʱ0۲7OUX r?wc/y_|=~G{ jD*I}U?5%N~=Q{yڴU}s RziuO:546*0J$E_G՜q9ѧfM127׫~z~NWo/QU%Ij]kOX/󽱾}ͨzr?t.6/)7)T{bR'Vt1$IRUs˪:4~|h~vVZ"bTmҼĉU3KU\/vSD*I}Gzb4^>,s':WgTm(k=t|˖NBSpi=X}:{/QU%I/쨚6M3nٿ-6MQS:uHX}y R9 ʨJ$I|Tq\Kg3ʷB0fԹ9 AqOn;N11$IR5Q5gU#['N^ܷW i3{ G {?!A8 QU%I錪מXvƕe+zh~ڐ6W<ЮM]_NBPډU ZFU"`T$I&5>x 9~̏*Xͺg(MX})fC'>aV'VrⲪ_d`|=[7 {#%NΘ2ىU(7ܿoo*QU$)bTOn\ڐ*eo\U/z}g[S=ie#:f;;[v@zU%IXF՜-_kCڬ\-wΧm]S *+GFUI+Q5Ё_]uƙ7M NJ;*4,|R9}X0$IW\jKNX? 3A} ; M R'V $IRy[4~|hғ|(mSoh^R*yws;~qv<#ʼng_U%IHG՜W.\6kݪ<8{P`FUI+Q5糏?ztoM7~iyEܟ?Ojݺ8 M% K;KX}jDɯ Q2J$E_Q~جĮ60{ [rmU NXK+v?`;jy(U%I/Q56N;WшUן^\ĉե `Bk=tzK~@q!G*I}1~]/UÛ4w@f̈wo6dٺįs'Vs_r«YwOMGv)gT%FUIoTٻeiؐ̅:լ[6 Ψڔߖv'Vnl6{k NTU}@XFU"`T$I(G՜ڱfˇebݒ_kӰM_C'VQ*1thWR˨J$IZksxaoQ(d]ֳ['V)xŧwh7Ow,?2$IR=v%/{ސ=[7M0 G: M ϝQeRirW<{¡ƨJ$Is.h8zy}heM9 M]2zd/?qb/vRŨJ$IїQ5=;_f`|K >jYpAVX3thؐ:^~?0$IRdT9|@G굥S&ѢfT-F[TӉUh -YxjX@JUQU$)3,2W{o-^F"ډUh"ӳg@UQU$)6q{/ou+(Uڴ&&vU'V!=[7Ms3euv d@܌D*I})Us>h i\Y[ eT-vW<*4׮gU@2$IRsT&2*>Z\Yg.NBSNM~ ʨJ$IїQo.<"bTޚqvbu+(rUW; 4*0J$E_G՜?ؒ8Z18sA7<6_Q5&3LN쪣G ۽ec܍u&ˮ?UQU$)jݛ;Zyvm$N֬[ rڧm9 4.*0J$EQO3k:kmK+fOVc}s[ vNȨJ$IgTWꃃ2cP`{n0WϽF'Vj֭ػǤR'VbT%FUI3&|֧.8zaNg i3ۚ.C~3X}nQU%IϨZߡ}<,=G^Z^ѱIjn]%g|\91z՗;VQU$)O)OxM} f~z&N>)T{buNB>x g7bT$I>K+*ixQnZRG'VgO_r«=:GX $IgTr=1qbox.kmHSobu Ҧ9[ϜX $IgTIC_WeƗzh~ڐ6O_r fOaI: FUIdT޼Q('V۵i)*2J$EQĉog௵!Uxbuu%(u+14yb8 @zU%IϨiǖ:4~|h⾝k֭ ZҦ#9 WFt,ub2J$EQ]k38qbn-.+kmH'V{vyՊ)tOwիv@c3J$EQ(>x {q7̼iC?ںU0H5: wwb1J$EQv#4w@/pb 'V~Px'Vub1J$EQXjĉ {*^\*ޚ11յWrbXU%IϨz>hk%N~KuK ifC[U;zre RhmؐĉՖ~inhpFUI3|s~Z?ªbyIĚE9}X ~FUI36߮]\9$qbub͞zܟ?~h)X«Yr`U_:оv@C1J$EQxjTƏ \ Z;bFU1%9tenhFUI36O\U> q٥ʅ ֎Q3{KD{iu>W:^{&g7\G:VQU$) ML쪷sw}h#7?ڮMX%w9 )w/ܟ?$IRUx_n *0J$EQ>yIuMU11U?zyn`6 q{6g7QU%IϨڨIC_We֎Q'Vk֭ `BpR{iEnD*I}Fv7>=w䎩iWWjV%X׈6FU"`T$I>jaK_/8+󽱣X=vFU#XvD?Px_XƞG8zh?1$IRU xjdĉ=]uj~if/\}}(U9lN.wb 퍫{rW}mh'V nFU"`T$I>j(_XcK+tY>kbT?:_T_}Ovl 4*0J$EQ5O?C[=O|௵Qrvm$Na {gZ$N>3ݯ<h FU"`T$I>jXO~7{ﴋQRjG 'Vw,ˉUQU%IϨzqY9-'VhW * hMc/8z~vV/8krW<{*0J$EQ8VuE'V }s_k7qFUܼ? f쭓X]}ܞ 4*0J$EQ8]Z<_@M3v>V8YPVwΧ}vYZm[<|vǧF8 0$IRU>ƽ'N|qNBĪx>;?;XUQU$)M{T/IXڳuSڐ*l3ĉiMܿ- mr?G b?s?caT%FUI36Mު~=݉U(7]*4ebu݄~W?fT%FUI36Y}ij_/GuK i|ڴUK;yՊ)TqϬ3ڟ:3k $IgTm>xk3'N33Q(w6׫g#s `BW~J'VۜX $IRU߮]8:ofҥ#X]}Sl6;ן3dU]2euv_Gv6FUI3}obTĉKj>hkU+7Vx`^jSO GCό:щUQU$)}9SzdM);Fs<𧿞X-|Zڼӂ?O/3K @4$IgT{8zw W\U*MSn#_|5 fe='V?hWn"`T$I>j|='V/}ZW4e3LNY>AߖurvX_v )orܟ?$IRUɞe+zU*M\t}vYgOHX]*vܟ?$IRUcu^wmii7ݰ4}5V%qbw0HͫVt?3|>>57?}tt2$IRUV/ӉU*EaG sb/<N~BFU"`T$I>jrը/)ќX5R,jO:&v mr?7O?CgbGk|? mD*I}F4d5* g}쌪5:S]i/-`BKuZ ΪPpFU"`T$I>jJ|Įz1X5Rtٴ_ZqϬ)}vY.>;*QU%IϨ*IX+.*FUQn#w_*^ձ'wצ>tFU"`T$I>j?Z18səU*k[ z}=Z!tX]ūiǖ=*0J$EQ5>ٳs!3 EzbըJQ[da6mwS:ux)9;u>>،D*I}Ft:w[FU]ͺg(U̿)yՊ''UәN@1$IRU?MX]pV/(FU"g1MXy۲ #MU̽ƉUh$FU"`T$I>j?؁݊ĪQhĉՁ}z#Dcw_1?໿ 1$IRUd i\YлO/Z0'V;zre Ruo'ό:щUhpFU"`T$I>*8!=7q܏}ԨJd޸_ ^zqWw,ˉUh@FU"`T$I>*3U}4&~bըJ|l4щN.oⳳz}wb?sh? FU"`T$I>*S4^:,S> Uռ$N3d;0H5շN.1ybʼng9 ¨J$IgT%=;הK{owoc}DWXtߜZ?h@O/-`B̽k뒇Ϯ҉UhFU"`T$I>*}Ym[iCڼi}'V+ ^zq.N** *0J$EQg 7iߺxM_kC쭩K'V'̉U(w6-8krW<{pԌD*I}FUľ7_mKLJf&Y>kmHwܖ8:z0'Vo^+G*I}FUO  Zt8}vYzd۶xp|*I}FU#Xn NҦ֭Z-oNziyES*_/3J$EQ՟ LօNBߖ42'V)xgs TU1NQU$)|uO.r@g'V=-XTwe*U}_v @aU%IϨrʉU(W*b Rh}sNkbYNQU$)|]G<þ{!mvVOXm^RRqϬ)Jc]On?w3<FeT$I>*GgenbP`e]71qbu˜X{g==SYVgW1Ёvx$IgT?|X~֬[6w5/)U2ȉU(۲ŀvLJ=:wFbT$I>*=;NҸ,3NBὴ‰Uh"={?XV{iEnQU$)#XnBim .g&Oz48$IRUi~idn|] loM'N:#;ճkrW}c8'V"cT$I>* xjdKe&Y>kmH'׺UĉU?Px{k/?#ʼng_U%IϨJd5%=wzjԫϜVZ?MU+:?)Wd] *սmg=:gAU%IϨJ:|^Ugk4қSofs7tqu^=X >+U#g7Ψ*I}FUî'N.8+󽱣l௩%r?t]*4lܿӓ_*@$IgT|UuLXr@hTh邻'V ҦvLJU\/vP}rή?u? {y(U%IϨJ}bu jڨ _ NXm RJ;7ybNFU"`T$I>*q\K2SƜ 'V0ĉ՛'wb G 4n;N6FU"`T$I>*wĪQ{f5/)qbۿ-;|ܹLtb43$IRU)/N~o|X5Qxŧt*4*mwN^FU"`T$I>*t&&v='wL=o5Y!'V9-A e]ֵcY}X D*I}FU e{buYƜ(^DUXL)8:‘G2n]*3$IRU xꂶ/L(NU?s-[ Φ RhJNͨJ$IgT%W89X5±>,qbuVW< {gif*)bT%FUI36}mbWyWlTz}Lcਭ[;?;v@c0$IRU n׋˪7iۯ+^u4BC?:a{n l6lZ{ΐAWuTUߘ9ΉUcT%FUI3|qbudKe.sWFUhX-٣t뚪izҸ;‰ՏQU%IϨJ];4=<Օ_٨ ڴYdaZtߜ'HX]9gbT%FUI3t:k?<ٯsf*4*4,|R9}X ZFU"`T$I>*M͎w'N|ߛrHT_}NBS}sC4: @D*I}FU/N*qb{im̋s~Z? [tbۿ-{#:fZgW}qY~W?XUQU$)4M7'=Δ>'Ԣ௵!mX3:n,'VQU%IϨJXquGwڐ6lZ[6p[߫S#J~|^n8:FU"`T$I>*MܯU2漽5_kCߖ+X9==SY*1x{9Q0J$EQ5U#['Nug௵!mMyIޚ/t`aNgT$I>*Eӏv0ih⟬^ӫ՚E iӋX&Y;_18ybw/ *FUI3R,ή}X($IRU)FٵcA9ωo ZJ;ҩĢ LXzUnU%IϨJڼ>ЉU(w6-8Uh :IOXrvʨ*I}FUo.^'3_yP`{kR'V)ؾ!wMX}{8 H$IgTx %N^NBU3-Xo 2FUI3d41N}܏6 vbן^ܵcY}'V?z Ȩ*I}FUb?Z5YK2|"WXMk1䮺?ٳ3g7@$IgTؽΝ]+?nV]!qb[:; ;X`qCg=]?~gkrU%IϨJw5?o>h N{<_kCڼS:upb{f tg=z *I}FU]Qw_Ul}ӆygs rbkt?}N_'VQU$)#~vq/ڿ-6ͭ˝X`go ULe4o-UKs灣`T$I>*KF՜?ؒ8Z183qDW=#qbu`~TOwو_\nNėZE*I}FU?'V'lӋֆټjE~!yoӶكX <*0J$EQbU~>՛8'wL NfgKJ{gziyE׎*UQU$)ON0foMuڐ*eKX߸w Wne^=ʻ: @HFU"`T$I>*k;[S؁ݶox.kmH{fn*W-8#ޚ/tDr'VĨJ$IgT}߾`aNBSPneiwvmڬYP=>m=|v_UQU$);{ߔ'V<&> z'V9-A }qb3LX'4**0J$EQbwԿ}HX~(*ތ)'V;=[70H7߫5ݎpbӏv VFU"`T$I>*X~Վz}ڐ68z~?ޚI.+< XUQU$)cо^44=ןyK+ֆbNX MWfrbB0$IRU)v ߒUl}ӆqb{; @3$IRU)v 'V$N \usb7׫.NиD*I}FU][.x}O۾௵!mxbսc(C*ĨJ$IgT5oߟ~kw?Ӌֆb'a {gif Ϊ{bXAUQU$)ٻbuw iSjwܶ[6Aڼ4*0J$EQbH}v#4? 3{8S^\*[rWubcdT%FUI3RvW_s?'V;&NN,;UG('V 'V^ˉUQU%IϨ _ӏvfPKʲwluMUڐ6W8S]5tbxd][<8(qb*0J$EQ~>wR{g)y_ iSjSoPx'VˉUQU%IϨ _o.^8zUr𦔏KX{H'VvV?lpyч}"xW]`$IgT[U&N^g7 i*4;Ճ2g7@U%IϨ _'{v*89PxNBӱ9ڴHX]9zaT$I>*|-hbbWae_ v `B/-zr[H~%w9 QU$)pG7iu:kmH'V]2ډU(,1sg Ш$IgT_'%NUG('V}s Ҧj߶džU_۳?QU$)pIC'V{_, Z'V_Z^ 7',8+ybw/ H$IgTcq7>޻٢ N'VؼjERj6XdT$I>*VrxIƎvb c.z9C]'V g7@2J$EQ~j/)௵!mXܿPxeo\^~W?QU$)P>hךs8v[T iSjV-Y 7kmrnbT$I>*4ۏU.י ize_<ۂ?+'Vzx@$IgTV oxg௵!UoNnbbW3#N6u@$IgT;w߶ܟ?O/N^1q^6߲͢eڳ[ [X]{e?XU%IϨJk=CsxMVT>6U+J;8r);m8̨wTnfT$I>*ŮɎn7gۧ iz}_]f:Nln (~/gT$I>*ŮX~>s8{˂ֆYda'KJx A -]pWO(qb U:FUI3R>ƽ'NN]YiC^?OWϞzPxT>VzZg&On'VR~Ǩ*I}FU]޺%K'Vo:Bbg76H[61՟`̡ݯuPQU$)b[O^!C3W bʁ{dzY2AߖR>uz_Qu`T$I>*ŮH>&{sueڐ6?'節[zy RYg?!qbmgvx:gT$I>*Ůx}8z{f3;mH[6߷)yՊ'b͉UXu7FUI3R߮]\Ico g릫/4u-? Tj/]?hpk|nT$IJAFU]}{&N^^; isiKJwΧe] mo:'V4~$IRU)vq}hvb :?)qb?Тto"qbuVV @:RΨ*I}FU]4}>x {u9 ~h⫀]7qoMug>.?c]ND#_H3$IRU)v޺%+F4OX: yĮ:zİ=[76Hxb0g7._H'$IRU)v͗+/hx̤NBͿ6'V)ؿ-W?vl 1:RȨ*I}FU]}qs8 %_kCڼRX6mw[tbxy۶x:O҉UbuQU$)X>|kqbw6-8 U3orCe]w>57?vbxkbT$I>*ngnvRܟ?O%NBp{kK7܉U(/\j/}?8 FU"`T$I>*4qުjoMuڐ*/4pPxl,8`G8Gv)aT$I>*?nϮ}RK2z: ;XݼjEٿ-{_9^$F0@n>XB*[( BbUSC&E A4kB6Wla0 Vwzy8k\;{@J$*$'ח|[= Xܫ۴s窭 W, *ϺȊ5V9a*I|D8#s=[,[03;mHڭWΞ:ɊUȽ\Q4gC߭ѩWgo `$Ig ɵҪ-7lu;\uAv mk+0V~@ U%IP[V $bUVB-_9\őOOq/Lڐ6U?sՋZZdA)|Z__VMPU$) U!}|U=nwڐ6v1tPS Obr/vEWN6/<+VPU$) U!k3=x-VBۓ)/xŪ!NXuut'$IRBHjͭ/ ̻{g.(ȞيUȽ+VK}v<=}oWn $Ig p~bbsCX]tq)TpNv=s]K7b8/ U%IPGkqtd:W‡'~ iS_G>wFwr@f۰sՖ O |}{Sg*@WON;wgPU$) UI:C/齗~aٗK{VBۓm+~6-kzv=WmbI)Ǻ\zSX:`*I|$9:ՙ/X:!mՌ~cdG rEѬ^O~q#$IgJ9}g\:Z 7#+VGz];b0H5od|\Wi*XG U%IPs>_X]>(X[_Mjk:޲&91wU h0W=/w7@0$IRtNW~G{Wif RrᜫZ_MűJ$*I}~q{_#iCڜq쩓b0HתԾhz觀g3??q<7@9CUI3T%霾B=|p+V!fOZ _yXQWU6fuͱJ$*IDX]9(>Wfs՞];ݶ.9'sߘ lbul $IRtNMY}i\]7EVBY Sgt'KX߷WH(:`*I|$w:ՙ/c{2_kCۭX vEW] bw$c0T$I >CUSCU߼^}SAAyKnZfՒWw wdwmy[yWֲywPJ$*%}r~>|G_kCmZ]ҫG\V6U>A U̟ kbu%ܺCU Y_wF8\W6쩓X=kGis}{:)7C$IRg{o0bX[x^Z\l`[ 7hR$Ig G?XwOKҼk iڐ6{ӽ[d`B,Vx3EWܼ,w7t U%IP8k}|6^tΌ!mՌȧgOA V\ X~xo)J$*p]ld7Y xؑY w ֡9:Wr$CUI3TV|~zQa6sʬ]Aۓ)/}e_XJ$*p^|uE~VB voYӳkja6* *\xȊ+V 0$Ig G5U'zlO&kmHu5#h*4+:]V82CUI3Tί/VϾ4^/7]6͌ 7w w {,CUI3Tλ?T(ʾ4^9(oVBXҺ {ڳk[`BeZ 2T$I >CU)<ڲq&ĊUȽ-{ں`}Œ RٹunXيUH'gs~ ic*4[_ڡm9+V9XG U%IPsNX]ThHȽ2??{zMCXۻm}{}stZ3O?: $IRtN߉s⽷XoH5cֆYteEmXۓCU;NpG~S7^ZҦn];gU/jj}Œ R3:pq^I9CUI3T%霾?z02W}=6͡];F d*4[_zuv~ +h:uPU$) UI:D/*g_/.|MGv~ iчX`ҒQWU61u@$IRtNI}/XSҩvkڐ6k.n]P`*{0o +VTp#$IgJ9}֌-|*ZҦn>ݻEV._CU )~d_/*ΛZR؞b59:W<$CU`*I|@iofȊկw0ʁ6mXۓ)/=(oebw|PU$) Ud/=Ğo_Y6Mݦݮh*4ggbH^;灳`*I|@|~xͬ{"s=[,_8+;mHu5cGYZ^6ڊUȽM\=CUI3T g_??(roIVBsp%}W]j*䔡$IR}V`jŀqC{ccڐ6^YVB];~C/bx+rׯb$IRG׎n|_kCڼccϮ窭 V?(\8䲋o*䈡$IRIwG~ is涛DV|~۽eMW>ˊUCUI3TpƎ<6kmHS'E檣np]MiS_֡:Y MPU$) U`|zDQdꨒkn[6M9 窝;\e*bF孴b$IR!9yub[tpU\V,]A ^uܺCU Tﯫ6bu}k~ ixj+.7bFvÊU8$Ig ֖Ds8WV~ i{˚>ݻY A9}]T+V1T$I >CUne#;߱?ЯLUl|!ma*4WNa*$IgJ=>w̟;y:ʊ-"+V-:B͞:e~~\ud`6[G 4pJ$*Igʗ}"+VʻU{ZfՒW\ΊUŌ x'Z PU$) UI:CUO?:_F9{g\| RrE-pMY /ONY0T$I >CUP'[WV!mXc熪틬XXG U%IPs,UC[f_/*Λ=aQL^z5ѹ@8CUI3T%霾9;޷[EV+vkڐ6*bFWn@r#$IgJ9}s>{J/KճuݦձiCX G9]TȊVVMDZJ$*I͹8WxNwڐ6Gv׎~^V9XG U%IPsC#+V6=+V8bSt'~c0T$I >CUok_Md@Y FWx@H$IRtNߜ/_;O8(rϊUh>*6buK/XG U%IPs<7g~?VScGvsFȽ:/z觀=?ϊUp#$IgJ9}s޽_}i__د!mXV:vkiszWU6z>>H::`*I|$7M?~zȊ1%r }is7bt?ͱJ$*IM9y DW(/?6W>; Rrk.^/b߷W˱J$*IM#sGzQWN7w!vnоhJNVgDZJ$*IMSÆYQ#r X1tЁ̶ Ҧv V@)Np]=wYzߘ|rn3X<+V$IR?lZV5e%yG?6kmH'fNk=W-w!NWZRxcX T$Ig pvՈvKٻm]ڐ6*nb͗~AۓyhBYIaA vfbwyg*I|֍.2kW~ iwۺȊS->%Zx^/ͷbJ$*yG"+V_pVwڐ6jƎ-2W}hBٱ=؟ :_u{PU$) UΣ<_=EdC_p]Mڐ6 Oibc0HCv:_}oPU$) Uί/V/̾4^602bֆȊ6mNcislOlt/.w7;CUI3T8N`=%>P\د!m6Xmi?PY]Yfĥcw92T$I >CUG_ȊGzciCڵcԭ7GVs sCUEW'[ @J$*@/*DW?؞Lڐ6NU]ъUȽڭ7\߿~ӏc*I|M[V|id꽃nZfʊ6mW\b0Hc{2M(kbu'{;w7CUI3T%ׯzکch+V'nm*-kX?P9].0buxbwPU$) UIS` RhՒ]._uPU$) UI:obu5n92W;SAd֮rEcU_nX9CUI3T%霾Io*4 OXӽ;;6`6[hb_㣱&XG U%IPs&юȊՉXTۓCU雤Yl*4k6[ D9W~$:`*I|$7㣯b3cӆ9k7FeX{xyW/:`*I|$7xX?q{3?bgVBڵxgXGcwc0T$I >CU雐iojDK&v;6MW\b0Hc{2M(+)❘^=/w7p^8CUI3T%霾 'ח|pRq[+V!k;{2?#`Bg][tq*w7p$IRtN߄5w|d:g?6ͱ=ёy{;7TuhF߯\p8uPU$) UI:oBҪ-/}wY .j*{Zҫmb0HڭJ]'~8kuPU$) UI:ovW]Yz.VBPYZئ͖`6d&3]eU7z⽷cwgDZJ$*IM>9\ő>P\h*-kzvpY?j}~ W\o+bwgJ$*IM>xg~ isdwؑEˊUȽ=:^9ս?}ԊUH:`*I|$7)w79CUI3T%霾I?U}sKeJ[ ZsW\?`6Wbx+r7$IRtˮ˗v؟'{]8S]j*^}Sg`B mw35x?xc?n]2 U $IR$ΛUgbbplOltd:vmVBPթ}Ѭ^O-ߊUh U $IR$KX}br"+Vtﶿfsis mAc;F5SnCCU`*I| }5X|P^=X۹sXۓo]X;\w[;+V9ՅJ$*@r}r1}#Nl~1kmHmJdU[?1sZ)jɂ.^dIK7ƊPU$) UyEzWiC];yܘȊ_qc6H7_yEo~Tn*MPU$) UƟU m}iEa*ު% ZdUo;;6`6[}wN׃bwCUI3TÉޮQ}i\]Ŋڭ_kCPu"+V m}hBˢ+V7~ $$Ig Oo7 {^VB\ZRSb0Hg>֩u35x?84ߊU#CUI3T̛'F檏_pVwڐB3'D#h*ޛ/G+~ L㣱$IR)bɒF[ .j*{:oo+V!N~StZO U%IP H'{{/ ̛tCO+V!^~s窗TtislOf1Wy՟ @J$*@N}X1tPSsy zvc].7/ @rJ$*@~_}o+cwڐ6dFGyrCm_Y劢Y>u $$IR-7^7bWpNa6VBls՚)DΝO;wWN*n~ is mXj'fN *ι5x?tck7 A U%IP{|>w\]˟-N؞̴ǵ}Ǒݵ?kuj_4GSV%J$*Ig c]d~yfwڐBϋU{vwߏCA =5{FK/|\u*w7̱J$*I }T$;mHёwۓ m2kWx匞_ 7ʱJ$*I /`@=5{EZeUo;;6`6k6ޭStZ3O?:XG U%IPssxAy:+V!2kWtc\MMO`6Gvז~ErlϿ:`*I|$7'ۃ#ǙTܶnد!mQZR=WUg>A U̟ k~|?n]BXG U%IPs(#W~6͑ݵy{d}c:?Mf^;|{S{h/nc0T$I >CUίcݪ!ٗƋVB-_CU^'${)%E^2;mHڭΞvc?Ъ% z_~35x?846VfPJ$*g<92W}G5?Y6ẚY:~Gvl6uV_ߣK;gbCU`*I|_Z9K'KOc*SgDV+`*ޡ];Ǝyե 檯b;CUI3T\x#.˾4^>(o2bֆyWXݾ"=uҀV$IRp>Ɖ7Fsx_6omNfҒEZU.A m_YѷVܼ,w7睡$IRp^d~<%2W}O%?Nڱ#okbp]MiK?9+VX$IRp|eEdGFZ W1EZeUKzbrЮ_~÷;G窯p}nCUI3T<:#"+VЮ_kCl_YѹU[_A -zڷX6\wuLn CUI3T:y=~rwڐ6vV: )g>A -_` Rb.]nbwp U%IPfd:6͑ݵe[[ ۴k;]V$$IRo! V>QrNRhy=v~g Ю#g*I|$;ke;ܷbVBs}eb-h*@J$*Iu|؟Npuu>Կo66M}aXrk.~$b~|$IgJBexd?;O~ islOf1Mv+V?hn6CUI3T% U!v吖ٗO͟8؞Lڐ6O̜2??bn Ю_~CY׿s'cwCӱՅJ$*Ig u⽷RY:+Dr/vEk:fU ۴bbЄE-ފURıJ$*I v/>É}۾ccwڐ6[KK#=uߏC-_<%>^ҍn^c0T$I >CUƼ"sϖ~ islO1wEcG{+vݣ+VO c0T$I >CU°oS/ xQqޏ'NRhy X!Uk&b8CUI3T%霾!'{{字GW,9Ƚc{2e{_Y:}.w7/uPU$) UI:o0${) woY6b؊UXG U%IPsdx$)/6ͱ=&E檷4!6)ZJ$IRtN?~吖ٗOo޳skY{v(-)ʼX%4uPU$) UI:oVR}i\1 o}k~ i}eE+gU[Z 9v;ΰbu$c0T$I >CUG׎}.9Ƚ?| xF Rrk.^pVduPU$) UI:o'̺'2W#Տ~ islOf"sՑobro5];^UՊUXG U%IPs4xwCŪ! ׊UȽfψXӽ U?M}C:\obq#$IgJ9}CJྪڊUh^~eU[~zQ)4|Bq$c0T$I >CUߊUhk;)๏<{ tm{S+Vuyn$IRtNߐ6`|ΊNUǎЮ?-kztSG<+VI:`*I|$7лkbO Xdj+[ wzWa c0T$I >CU{~;ru+oԊUhXm]P`*bFF+V7݊U9CU`*I|$ɣ-+|pbϋNҦvO/>%\8Sŏ[JCUI3T ~_}oCU׃rY[cֆٹ*bM++b0HZ $ U%IP `őקp5iCڜq쩓~rrᜮm/~C/ ,$Ig @z:wib?~ )4|BGv`6;7Tuj_еQ_$IR*mYbHKf'r{W{熪 +Vbw$$IRtϝ_7y.bu ~ i{˚ʞ.(Xtq)T^6y]`̦͟$IRtYGs8힭/~ is3'8^9]_t껫 J$*Ig Oc}/ x~qes6PnЮ? UMibn'2T$I >CUP8^[S5$?buÎ쮍ZҦ>ݻ޲&z]Z3yاM$IRtN9:۫nmbu堼I;lZf5}w˞i?ͱ=&5^V$IRtN;y|>X6͡];X=uR)TpNVuPU$) UI:oggNRhG檷4!^~ڎWZJSs#$IgJ9}TU n}iuyXv=د!m/wQVuV`6݊ c0T$I >CU87^YvZf熪];GVXAۓ՝Zʹp#$IgJ9}MU mY:g!Xٵmb0Hڭ%ōW25:`*I|$7ФvՈvSnV_5kmHתm\uA{dPVR|PbXG U%IPsɣ7 ^VBnmbuFc{2?X}qhs$IRtN@|~'FVbiCڜ\d:vmjb6Hת|3zFWfe*:`*I|$73nxjpȊY'r{ DVc0HCv:m*b/ϱJ$*I ֮^Y ^fX]dAislOltU,:`*I|$7c'%>P\د!md +3`Bg][tq"w7͟c0T$I >CUȽOsX6{iC M\dzMCAPա}Q9ڊU6:`*I|$ /ONGV>Y7g(+V!*i=W*+V!kp}+V__]n-CU`*I|p7ȊՉk~ iZsW\nKc0Hc{2M(kbuW}4OPU$) U\< |pbVBڵc#=uR)TpNK/lb~4CPU$) U̳b&Eyݵ?Mfk;^xc?hn˗>SCUh T[ Ī% ZdUtΎ?M}aX}e\\J$*4ス"+V9ع5Xۓ>VBsp涛X A9gX:4~gPU$) U3>2WݧŊg~ is'rjyh+V!|=:^9Gt'&[ $$IR@xu!-#+VOc*^Y=v޽eMish׎ҒQWGWn+V1T$I >CUȍU˾4^>(oBim_kC4^Zئk`6dPVR;կ]_$IR@Μ<Fs8{jF1{2?8^Y]x껫 %J$*؛|pz4;;mH+Voi{;7TuhF>3cw?J$*Iu˓=S<_FkW 0ywAZ bW?{wU}'|?DY0"BADŋ*ղRŢXT,, (kjCA55{hx)N;Fg2 *o9Ht9sr>?;uit1X?CUIgJ=>cڗWu??t샷(buloY WK̹y͚-`6GvU;6M]So[/ݝ_PU$)w@|}~|pBA}+F >)9M ~0Hoq. J$%>CUPf%O ~ )4Mss3 }U/-vX}ϏN9yJ$%>CUPH.bunGY W65˜v_iuAڜX:\>73k `*I U;o@2e߾8x59wW j6ts͛[ wbj6[;|qTvsy# U%I*qHϏǹVBt?PۜL+V4݋Ovsx# U%I*qHs/yo<ƴGVr; u/kѮOf]_ y# U%I*qH/\ҿYS=s;ҊUȾegl9WЮmjE.U+&`jx# U%I*qHctHK㲢WZ ٷgn;Y U5ykZmx#r6 tx# U%I*qHϏ^k#ڸ26́;Y: ~|o]po%ݜ2u$$IR3T%}FVNӆ9j=c#+V^C.sSo=QljLy# U%I*qH_Y_n^9s}ׯAm(]Тy̹ziIAعcpX>O ZGJ$%>CU7xŃd^*7ֆPߡ}\f>`BS{]&o|#^HCUIgJyR˿W}ͭX;+rS`Bes|\?won>u$$IR3T%}'wSne ~ )4ع# mvo]5ÃWF?ӓ>?z8k `*I U;o@ڼȊ' si*dsk,sߡk^~0H#\eѹ=rp_g7#u$$IR3T%})tdo lyi蚜vb7\G Rh}X]{E y# U%I*qHXD>|qȾ%]|S=+V]WZGJ$%>CU7f#+V=wɬiC ;\u`VBlZݳevb5N֑$IPPsdIWo9ȾZb;X]߾+?:Wdg7 $IPop}K㲢qvjkkmH W^87 &]Sһ\^vD$IP+VmkiC|Xu`ޑOϸBA m(]ߦ_Yn2$IPzӆ9jMss3C!j6.tWFճZxBJ$%>CUH+Vș;{!>^|Q?F\G}@2J$%>CUHctP^Ң/:X]ZU/X Ĵ,\uMmW?0T$IJ|<T~{K}rvo!mVW 4 buJ# Ҧt+Zda{W< ĝ$IR3T9Ds8{ҧiC 122Wbٴk~)]+Vkűn U%I*$ػJkyi< gd_-7b;bK+Vv1e*I U X-+ʙ^VBlZݥc~\E ?͑]USl$bu?] đ$IR3T;^q"9_ЪzCykmH"䱣8d_:8wnvc*I U %DV!&pŪziItX}IV߈$IR3Tm+ͼ4#+r}ϊXֹSͦis`F\]Jq? ą$IR3TT9nXwmW"ٷg®WFV^87 m쪺wy9/ۃ?X0T$IJ|6z+VW۽Wֆ9s>#Rx .6\wڐ6GvUM;inn\uv~6HWV<߮MWDWf]V_PU$)wϘc#Xүi񜂜wڐB/k2sڡ] iani]Z1+V`*I U;CU{iY:۽>Zu;X][2? m*cUXtwUg78J$%>CUP4xEs8?qekmH XФ*eyGV?VPU$)wgDռ#sݚ,wڐBNb-" X=֑$IP p²~s{)Ȯڐ6J\غU\KM  ]"+V"u$$IR3T%}Av/ȼ4^Zs!m7w)buY)1wti]ҐolNu$$IR3T%}Yk?ȮN,l5KiCڜȊ)c8d߲.k"bue߯/ Nu$$IR3T%} U']dӳiC Mo|d>?`6ʻwpFYz֑$IP ptIOw_۶ is`QÇkO+X=^HCUIgJy8{}v٠֙eE9W vo]S̹y͚}, 7U+V u$$IR3T%}U=]}#9o_N#\Y:~oҒ6'Y8ZGJ$%>CU7@6#rWi;mH)cXU/\qiWDW$+V)u$$IR3T%}d6,ZZ꜂W 6-^ײ܁;pKX}1ⵎ0T$IJ|ĝo{o.Yzwv~ iSwX`C 4}zs_z٧?k `*I U;ot5 V]_ ~ isrVBcz-ΝÊS䵎0T$IJ|ĝo{m\urM%siC MoTk~:[z*֑$IP u/buag& ~ )T`NvmXVWqK+Vcݘy# U%I*q ?f .CUׯn{>H/n}xTdhA~ )4C!7t++VkkjC$$IR3TN{//[Ky̚wڐBx֭XVWa]jP0T$IJ|.ƊUh Xߥc~dڒ)4 [Y?0T% U%I*p^g6 ~ isbĐXƠt+Zdat꾵 ?^N9yJ$%>CU̪w*wӳiC ͸y͚Y lZݽs)]Nb󣇃?g*I U3W_Zүi93W --bC5cF|eڿ &CUIg xͲZGV>~EϊUh<&ݻu篶bPU$)gڏ_}u;㺷ڳc};mH;w /sY{>lE)3T$IJ|YUq=NRhMssXvo]ӿ{:GW燾8V CUIg m?d^{GӆZ[2ߊUh 5|KsՊ{Z qd*I U,8K\YMd_N_j*wdW䱣 ZEWˎ_ |#$IPȎ4_q&ȫٴ:6́;0+V1X[2X qg*I UlWwo<[%?}06М&Y > +V! U%I*e{onkri~Eo¹-7X}}9TSy=YZd*Ē$IR3Tve"+V'^i_ڐ65V),Ȝ6-== m쪚zؓXa*Ā$IR3T'_=kڐ6j*G >bog)_0ϩbuJ$%>CU⮼lAO9y8VQnM?P;mHiUqȾM;^ھ'$IP{|ƴ/~~߮|zY9k*d߶%n9WЮ++~0H}/qIΊ>檯s8J$%>CUP sonpU[_kC޺[N_Y7urA M)Sc*Ă$IR3T% UV-GzbPMA"s{Gn*d_[TosYgy݋p u??CUIgJ$懿g*4477sz=wo]`65V_yi+V^bk `*I U;o {36b Z4o9W6ۖ?͉?Ɋտ~?y# U%I*q yj\2e*7ֆٴ:binnG RhJ+V79+V֑$IP HN~}j*ΰȊ;F~6H/;񂤭XZGJ$%>CU7@qNdܟӆjb~Ċ:'jŪ:PU$)w޾m/˖ˍX}ӆڶ֭2ڵziIAڜX:]rVz# U%I*q /e[DV>rS/;էM ~0H)cigbVz# U%I*q FEWNڧG!Nbo NXiدXZGJ$%>CU7@J|qvã"szsOӆjbu`Vm ~0H-жW{Ū:PU$)w޾R__\_K=r~vwڐBۖ\|Q̹jP mNXՊ{eŪ:PU$)w޾YVm ~0H-m]]ōjŪ* `*I U`%f^+<ӆjbCx> mNXIV~}* `*I U${o.&bu>= ٷrsWeU>5mJA 1 r\!{Ն2T% U%I*0_]yGwڵwڐ6']Z|Ht+Z?Gtyݐ$IPH/nwg4YwڐB W^{uO+V!^_CX=xk$IPHX/7xnǎ<*6MnUҒWy`޷]]=_$IPHXuҢ\W Nr25kV:{zAU5DVֶX82T$IJ|@?ъ% Z~EkmHXwVB-?ˊU8 U%I*x_\MCU %߰xy&ʙ9VBȊھ9st_ 1T$IJ|@z|%[FVӯそ;_kCٱKȊՒ?͑]U3p7_RNeg7ą$IR3TR察_qkw'۵ڐ6+Y:yh?ٷz܆+V6Zj?3T$IJ|@|qv#9iAn~E7urdj+V!7wtyOe* U%I*N{sFVNkD;mHMl9Wu? ~0H#\p=Xf*I U:fDVNӇU[_kClZߡ}\inn)4xLa^NYQZ _PU$)w>xׯn? ^Wꭍ+iC;"+V}gP˅YEӬX2T$IJ|3}yVs;_ё>ֽɲُӆq\K[?Mͦ];L]Z+V!CUIgJpF^M"+VywڐBJDVvh׶%is`XVB$IR3T% U8SYd`ȊGnu"6kzw*sz^fes ~0HmbuMgrŪ.$$IR3T% U8~tUNwNȮFf*d_9W>߯/9#_u$$IR3T%}pf}q+ÙM_~wڐB477s:O;w?떟tꯧ<^HCUIgJylس$bœ~<.6Іn9WU/?ꊻo-wvt^HCUIgJy,+ڊUh Un.ze\inSӦ?м{nYnŇ~\u$$IR3T%}px坽X`uŨC#+V# f.}kk `*I U;omEsX`CXW9 mٽקoZGJ$%>CU7YerXGV^|Q? ~0HC5mb>=g֑$IP @vٹdEVBcPKȊӃ RhX}qT~:PU$)w޾ȚϏ^XPMe##+V']iʊ]|3Xw/>5^HCUIgJy 6?\ՊU׬Y\ګ{Z ٷg#]OqŪ:PU$)w޾Ⱦ}[VX Ħ ZX}}9X]qw5\et?^HCUIgJy cVBc~k9WmѼyG RhC^d5]+V֑$IP @(=\~(UuFVIݟ?Ͷ%'Y?wڅ#k `*I U;oeꝑc=Z:?6М&E} >`6۽˃WFW̻+V֑$IP @pmX_K=y{iC mZCsպmiIAU5#.X Wz# U%I*qo/UqYQΔa ~ is`2Mss6% ]saӺgb7_Ǩ:PU$)w{Y^脺:^q"9|%VBt䱣X۶%y W-3P0T$IJ|g#9|{m ~ )Gk,s:O=;?;w%bg&q UICUIg pfݾoO:煇ӆziI~sռ-_[BAڜ.K+V$IP{o&baX;sGWeUk֬lc)4Em,\^/~i'p U%I*/.bPMe##+VփiS`N~s{DW[0N$IR3T8{Np!Jf>ڢy̹ja+Un~0H[t0շ(n85$IPڻ}+VO ~ ).4szEm^Z`6+F8xXWn8$IPlkbu59ϊUȾ;w),Ȝ6͝Фzޭ՛8w*?2T$IJ|Yë|[ wdWIWyAڬ-߱O_:^|:CUIg 5 W>\+V!6͜^{u+;ȶ+V`ofűn U%I*@6ݾէ{#!^Zߡ}\5eM?CqItjX%. U%I*@7_uqYQN+?ZҦ>#qФnyM]SofhOwWvPU$)dGG6 ~ isr𡑹~6Hs.;=W]K?J$%>CUP^w'w]OiC }f2窝.tώiSiu}+b'X13T$IJ|er3/ʙ3GvUֆٶdUcKK #o<\u?7NPU$)\[rskmH+6_9WmG Rhփ ZEWWvCC$IPu?S+>NqVBUu["+VG j*d߲:ȊՕs{1T$IJ|3}yUs)"+V[6wڐBOMYڭs'+V!u0觀yzGv× U%I*qg @4\daδ1#X{m y-[^87 mVWrs"h9/N0T$IJ|ĝ* sdo lyi蚜vb]WE>Lo|&7W]{E 2T$IJAĝ*svb8X]qwECg*A߯%uQ^. CUIgJT W{Y*h̹jV™5uj6)B'+Vit U%I*qg @tOBmZ0eK+VlC㣆.buzݤ$IR3T% UH+Vˊr]ê!m),| x;8Mhz䱣)]o[/;nPU$)w$IW/h9TSpAXq C:Ji1GS__M J$%>CUPhbo!Xֹ bԆ}jA2:W=֊UPU$)wGs{̝B W^غN)U?F\]JqMzJ$%>CUPT97e׵ȼ4^ZE>= ٷr>#Ф9 سuEԛjg7)a*I U;CUV|K}rvoo!mTr}dz[ ~6)\ueܽ+& U%I*qg @:5\:{ҧ_C =5mJdjWY _~WN]Z3/vl$IP3T ]WZַފ993W ^[ɜmKK bL U=ΰ.X2ǟMJ$%>CUP4kb(gҷ{Y ٷrs‚ȧOs<)q'L)p꺛*2T$IJ|ĝ*)w #9_ЪzCyKrH#yK|3>TtA~s{DW߸(D2T$IJ|ĝ*2c\/?D{rHk,buώ~W>9b'Yg*I U;CU8ὗ-$xnϊUȾmKK"+V[4oi, Uعc䍃G\]Jq?MJ$%>CU1?: :(d&?4Bgoz¬'DV8ol&1 U%I*@? "9׽W3[#s;F ~0hTPڒxAtחv $IP y6O?uT279м#+V_1PXay>CUIg HmX\779?o!^_bb Vr U%I*@R}oh`ȊGo*bê^3s47tq.)]nPU$)$ڏ(I=[W>= vdWU##+V4掺?~6HegiY7. & U%I*@m1.2W}閒ӆZ6V*7?M;Nd*g$IR3THwV>oK_} ~ )TҒȊՋ/jc*d߁;F zcEVr U%I*@JەK xiQδ[QȾ+6;biS}znyM"+V_wĈ$IR3THbdwmkiC h=X]`6Ϻut__M\J$%>CUxlld:[ ,?+e̹j{v~0H+VytFYa*I URuK5ͼ4~NRuXݴxaAڜX:œ}U_)o*#CUIg N>xlPK㲢էG!q=#vVVJ$%>CUý~!3inn\w}ۖ9ɊvhJ$%>CU"s9=n{nv;mHegh܊UU/\qiGW$+V9)CUIg ϔͼ4^+gNRzCعc/#+Vrp_g7$IR3T%l u??'ȼ4^'o ~ is`X`CluWכ>]Mb*I UgL:^Q>TkiC Y s;8wn+V*$IP3T3kwD3 l\h*`*4U/-aB'+VoJ$%>CUPθX}1!X% ^_t~~:X]1X2ǟMp$IP3TYv}^d7\ӣ}Wlbdv/ Ngz՛J$%>CUPΒ?Z~땑N(۵wڐBV`1)hO*bu'? PU$)wp|qvã"sMw,~:6вk,sZ} NS;]++VkkjjJ$%>CUPζ^^߹ ϙS|[;mHV`*g_zwoǷb2T$IJAĝ*dxPKE9 .QȾ+6r}d3 ~08eq0 [7W];܊42T$IJ|ĝ*dڏzykwڐBSDV_)= Ο-buڅd$IR3T% U 6M;Ç]wڐB[J2zZfn;IV~~pg7a*I U;CUȲ]^(뛛yidaqUZƊU!C:j*y˰v<~#9/,0T$IJ|ĝ*d']:eqYQ΃ӣ}Wlz]?+V UOCUIg O/qE;S{__m ~ isdWսoUG z`g0T=|iw+VPU$)p6|qv˔F檏8wssiC =;㑦sN_{4TSiuKwb5i U%I*goW>_t %WV<ߡ}\E ~0x}O9yζ}/qIΊ>V&$IR3T}]`6'V6giQ=Xm U%I*}֯^y=X;թ=* m7M:bݜ$IR3T ~+"9|W++V!+V`*d߆Z?觀]MC$IP8V4~ƅiC 5\ڡ][+V!XCUIg @#/.,뛛yiL?=6Py-[-`6j*0խXhjb*I UgLNǧSz]롚ڐ6']: Vr79y:X]sETv$IR3T% U oԢ{e};mHXܿêFmKKuQ+[0; U%I*qg iǷGV? ~ )pj/bazjj6[?"b'8Vٝ6$IP3TxٲNwڐB Wh|YFL+VoiG;6;U U%I*qg _/Ȋ7b+V'm*P ,hU_rWmNCUIgJ@|qvwEs@6 ~ isCw`g#^ UO߶%=.ɛ#buo0T$IJ|ĝ*j7?|G; n/NR.k6~0bPسc}'6XZ={ڏ?PU$)wNシlqߦ?;6PMssז~0PL9bK+V_线~?; U%I*qg Af^/-yVWֆ9)cX0T=f?U놵b2T$IJ|ĝ*ᤞ!mXqۖtmʊl2T$IJ|ĝ*eƸ\ufaW(P2ѦV Wl,NVf$IR3T% U:{X}9)X!Cճm¹yGVm/|'$IR3T% UWѲ>d^yӆڴxᅭ[YW3T͂ ;]pFYzFJ$%>CU⮼lAO9y`߫/;ފ^9VB;]~i\EVP5;VWr+Va3T$IJ|${sѠ #+V}+^/buFP5ꂜamX=M$IPXm|X f=812W1FpYte[<^PEӬX=e$IPd1kB;3 Z A/ӢyȊ FX' j6ߡժFXzj U%I*7-X}΢>= ޺[Nsզes ~0H;w _p,bJ$%>CUo]YM|z+Vg=81 3յ7YJ$%>CURxKĞmX X5|W`%?߯/ CUIg @?O>p=NR^Z`6[~:ҊկPU$)6シlqߦO9}ϱb ZXP`6*7ٽקPU$)r+]/׊Uh ^_72T$IJ|0+V189M ~0HO2b!ԾSJ$%>CUëH̸Bd:|+V!^YIVhnT U%I*|i1+Ի7bBP֭2]:lZ`6{v޹/bJ$%>CUׯn|ƴ~~ mYa*41?bum9X]qw]>2ǟJ$%>CUӾ`9y8eC.bwE>Y ALglN齯rsqj UkҒ+ڵ`yYf*I U;CU k6pg~NRhC邼-#+Vwo]`CXWOIV/vg$IR3T% Ulڻ}UYz+V]ӆz}#+V7-^`|Sqw`\]uLX5T$IJ|ĝ*exͲDVN͊UȾ| xJP53M7W字},0T$IJ|ĝ*}="9½;ӆ\upd kx. > U%I*qg uȊՙM|^;mHsZ4o9W1f0TM[t0շ(PU$)w@@{Z\=0[!T@K[:H*VEe Ţ A"jD BQFQ)EOJ?vY7sݟ}y`T$I;*ܾ]UK*pmdWtϯ Eꉒ]vO8~{w3J$e|FUҝQHX}oڐqGj0fWV}WEO0ax߻[QU$)3@xsO>Xfӆ,yţGuԢbeTll\wE]zb꺛.ȤFUIϨJ3)e_{^ȿ3^\YM'Vxݎ`[hĜ޵?WʃnFUIϨJ3f߮Gω ;: AL}K,77qW=W嫂bbT1_۷GK;OO|FUIϨJ37w]9SubX4ubcIUtè=_w=O_Xt ~bը*IUIw|ַ^;(?CE&>*)Swvbތ5fϯ|tYا֮>ZMfT$Izvyh\Z3Nqb? qWNw{O jxMxW]KzccMcT$IЪZ|u9o{'V!17^9:dPB2fES'~DN>?Sn$IRgT$xÝXDyy'w\dO,kzaAU>I7}#zb߲oWUݍbT$INBxnç~[>/ |qewO;|%3J$e|FUH/9Coxڐm>ظnQQsl3ٵv'/@FUIϨ TIX F^;4 nd'J~S'V<1#0J$e|FUH'Vwӆ,Dv>lyţu6[ZX5J$e|FUsoM勻wjcX`!=> $IRgTP=:|'!_w]t^G|=Bnةy93jX]vV~u_Ĩ*IU zON(eߴ! Xnn:[_l|/Nlni'KﮗQU$)3@puON[`Z7mB+͈X=/A9xbEwՍw_'V$IQRAsz̻oڐ6._#'VwgO ]ؿs>ޝȨ*IU E|av uIO'!>ظn"'Vod=Z}߻1J$e|FUH{/t'V!(uݰȉ_u?76ﻳWڎ?GYzOU%I2>*k8|uON9;ӆ,bތyyj_ݼd/:ykvWmT$IIz܈qEtNƉU⹥=hdu?~<~ȧ{mT$I;*X--I|XUzkٵvեW~6J$e|FUҝQ`{=ظnuB꺛.H‰U$IRgT%Uw0/wXyP_ dTNJNIA_^)oFUIϨJ3Yko9 ;vY7m6;*x][*ES'v;]=j}\r1=5)U%I2>*Ψ d_U'==sr7mBOL{b Kb)ΨJ۸|'w;jT$I;*ˏ<sz̻oڐ6xԉ2>fȠYا֮[ĪQU$)3q/ uIO'!_w9.x[j2zx폈X+U_ݲ߈$IQtgT8hoy9w_ӆ,4y-=O;e?dTU-:_;tfӓ%-]ب*IUIwFUD+ όuo7mB+h9ZX16.__5zbqCZĪQU$)3>onCr掶S@k,yfzQG9aT% Xp\Nia]u͈~-rbը*IUIwFUvŹO|4'}Xx!"'Vo:!FU&rbը*IUIwFUzUʳ"?'Sߴ! L{QG%8zQd’nsb4ۮQU$)3V.}lDH)Ko*NΏ*҇tuUIgWO(=oiU$IRgT H|4~͢#iCzcS=Nwb5¨Jm}~͠???rb| m$IQ2Gl{1ƥ9yǛֆl|:ȨJ(GrJ#UkW}~5J$e|FU{͹No]MPɄ9ƉՃqY%[vGwVsOU%I2>*d_U?;#%ߴ! za _wu;ݛubը*IU lz}sgj3ߴ! 9 )⃍_|:'Vv֧֐oFUIϨ f_=]%|BqbRǔ1KgUtbCNU%I2>*dU/9ӷ;<Mѷ8 dqzꛥQU$)3@v]ǗEO:3iC{bI'n\8'&uף{ב߈X<凇9jT$IEsh<윹 YȉUHl\o dPѥ'FwU$IRfgT%UZ_zy?ԉUhΨjW_tvfU$IRgT%UZms.?=b?;$IUIwFUX6滑z/M%SsLƜXwC7o҄yԮJ=nF]C?I$3IӿX;gÃiCڼў9bMjTm_}y9'9rdMMMg?I$dFUҝQ f9:'!=:ڡi:]}gfׯݻCI$2IoWC#r?g}ub{b}-_kj3Z8huqAdT׹sg}6$IZ&*Ψ Ī;9zYG>=3-(Gg>//qW=ӱ,k *Q5^۶mBI$2tޜ>uNn 4j,7wڸۃb aT%$ (IfeT%U˳;6qW'g7?޼>6d6>Ȕ.?3Gu?<|1ٳ'C$IQtgTfw!XPja3_YU;j_hT"jsڵKbҥKeee@I$51*Ψ Em"'V/Я)}^^*XMQ5? WQ5n˫/գGc۶m-[9P$IMɨJ3מ^<0=zݐ*_ YOL |T|_:~^߉X,VVVuP$I ʨJ3 gubRAin ]fjȨX;v$IQtgTHYUo*97zb+qb/OڄĮJ35pT=޽{ٳ'3$IQtgTHevUͩsbuLNBuO8r͓a?PjݿЮJ5jT[ryjݝX$IJ匪@k[9~D='V) Yb^ȓaGfpPcGϝX$IJ@⹅NN/h`?C{b5ޔ1Bhjrh¨U_:(8v~2$IR4*_4{`ȉ*$ǛߊL9dkUMU{b> j(IZU[m9_n9z_YUOck* (IuUQzFN-3NBuO<+ ˢJX7~'pBjEEEgEIʨ o2rbKg0,Tߔ>˜JX:m}ȉX,VVVeQ$)2ryN8;g! {bu[RGsS GϿرc}Q$)+2)h{!qW]'Rmǁl'V!ɒ0t;#'VwQ$)3چq99@'V_TQNvҥ2+$IRgT%M?'"?Zֲ1ߍzΛЊy3N:ss\0'%sTΛm۶]lYFILΨJ3dן^<m л z9:;bVGոO>|/\b'V%IZ)*Ψ *_{b >޼>C:d6|);V>r͓8/ AS'OX-**ڳgOGI ̨J3d}9;osDɄ-ubu#MMjT[v"'V7mQ$)2YeٸIU'tɽ! ȉ;1qoNsc ~w$Iʨ;*@ÊENE\ؿO171ŽqnVQX$Ij;*@ 3X|'V!vT0tpdʹhT%U'#_EEE;w )I UIwFUTz*Q2alcIrFU2@qV.С}cǎ+++CAJ$}FUҝQ ==Zꄞ1'V!>||cX/ dU㶼)B?CJ$wFUҝQ ˽hȉՂ6s ;d-W+֔[2a;*7._=:g`hU>/0GuЏ$IQtgT࣊fh9omQQ>ڡ)粋΋|=n\îjS_!~$/(FՃ7&uW8*IԴ;*q{'rbcX f}G8n*?mA;FUUBjq{$]/]{$%I/*Ψ !O^fP tbs N+rw!nǛaWuO.Y$$IReT%UHqȉ{̼퐅-_9Z_srəs?✜u RyTtG\X$IjxFUҝQ;8 `GE ~99ksr:K *݂^)>)X/ .`Ϟ='%I#*Ψ @]{ͺd'V!E,:#N9Pߜ2'K_3֛Z^i1nNeս{/$IiQtgTg#XTqZ/Sٓs꡿}^޻嫂rtU圾j۶m׮]R$)3Ƌ?_'V!SQMaNNޡx#r4UCD~ X$I:LFUҝQ3;։UeGEyfmcՃ*"+FՃpgA>Я$I)QtaI)h߮~F΍Ti{oyţQj FNc˖-,%IR1*!qZmO>|('7\X~Q?Qh[55v^<N>ЃӦ4U|QQ+5i$yˍXM@}/ޤ>/ޤ>O~G}ޜ&clԕ ?ݾx5gr)IBU ipFČ}nl26E26}s#e+fh'VUjTSSQC·$HQSjMMMKITɨJfHw2&fTmsc)H~})Gո8UjbFտUT%d?Oȉݻ/%IR"*!]x5j"7UܭI&#ܚ}SjѡU}޽wX,dɒO$I3YGK::ͼۊ?C&)|A>%'@՚/UI+Gc:*I<*>xs%qW--g~y}'z 񯦣:*ah_||cLUzh}t:hР>,[$IR@[mgEN+xbh[5_S~/krU??"'V+**B?gJ$ɨ r'V~~SP$ڨY혛yţ9PWFqURjYYYMIUÊ%sz5!qrYsN-ל]uy_+SGոO>|.X=z$)2,.rb3XfzfXnn&5'/7&8N:U!P UkܘX,%ٻwݻw~ڔ$IJ^FUDvU|f}{s![^NNINΞwTg K/jС}f׮]X$IٓQ]9:glլ)!}(//'Z,''NɹoQNN蟑D?F6q)ߨ_xh۶%KB?pJ$%#*@*/WƉUh7ηUgW=\N:?s8,U>ݾm蕃#_#F )IԺU?8{`{'VeM}q;|UJZȞQ'VĪ$I쌪oWo*ϯxM~[suݰWVWVͿ)cFyѷ̾>QN+-- )I*Ub¹O>Xf U?+/,=z_=%IZ8*@}ʆY"? x|.{>ezSXѣLJ~S$%34ܾ]Uʏ ;: Ͳ|T[#]v}7B}J$XFUZ6ȉ)g: q[^}=wX,VRRS$e243IUgrb KU^98rb&#$IRs34Ͷs9NLSX6FDS'OXݻCJ$5+*@U5'GNݷYŨ;thvyӦMB%IQ`'VQov7GN.X k$IR34%% *@v2ۮȉѣG߿?$IR3m/"'V~ǛUUcqܽ{wgQIeTh)XTUfT=˗v:ȉյk׆~$IjDFUU괳s~oKvjGO6G%IQ{buÃ? m:$6̉UIUZC'V_}zSIͨ d/|^azqb }UT&rbo *I􏌪 k#'VK s_*@:2;~2:rb5??P%IaХ'GN-*@1u+GNvر23$I-ȉ{XH7FՖU/$)3yIɂ6m|#-zȉQF8p {$IҌ{bo*@Z0'Fvբ={~R$I٘Q zKGNGSϨzV/_9ڥKׇ~U$IYQ u,NjK|{_Y-))%IR23M+<"qW^fOn>E.tPdW-.. *I%*@yb"'V_*@j2&ǽ쪽{޽{wVIURm%9:ܓ\2v@Q5iXp#'V+**B?JϨ :rb=|> Q5xyY=z$X4;$IZط։չ}ڔuKCIɇwȏU$elFUWUy>9ӆwb EUkܘX,9*IZ)*@[mvNV0RM6~s$IQ ]<=ȉ G=,gT նmۖ~v$IQ }swٽ<>lfT ˉUI{mw}rf^o7eTMƏsbU$^FUoWՂkENmX|YBFaj'V%IR+eTHSKȮz_^\R|\6FԱ_͉UIU׋ϞWxD:|_*FՔɇ_~ 'V%IRfTHk_4c`]0+ O èrOۣvbU$TFUtoWgF~Oel`TMM+>UX$IMΨ ZgǞ{ĉUVgTMYϗNJgTtVߣwYqq US٧۷ rpQ8iV$SFUL?FO$GEy SUS߃ӦDvU'V%IR2d Xӷ;<|wHFմhvI^voFZIU2ү~6"zbב応|zzeyjiaΔ*@sUݘFb1'V%Ia2d_~jNo։U&2f=ҡC{'V%IeTBO$zb؆E&ґQ53{9*Iͨ ^_m|H;FՌO;t$nFU^=}.ubqT{buΝr%IRȌlo99:o?nE ]U3K;w\ڱcЯ$)XFUn=Gmzlv -U3ҶwuNNq UvV(9Z\\Я$3Po:5VeT0=ZYYaX$5.*-詻9:g|h=FUնmۖ~$IȨ @˪\ĬG&y7_=ZQ/UՑ#G:*IRdTx <.rbu>NɨJ=zl߾=#$I}f}ȉ1}:ۧ-˨J{b N,I$*;PZXkW؆E_@ZQFqbUt̨ @z91'V fTX$)2ڪ:}౑/wb FUբ>,$I'*1whȉձyƉU UizOVTT~6$Iь$ͯ~6baZ=Gч"aT9Xbeee_%IR$[랬{b?4QfΪaDN;vߏ%I˨ @z;& ,8,4FUZĽX߹sg'dI􏌪$߾]U]U1׆U2RXҥKeeeWdIdT c#䂣7 4Q7#'V۶mlٲɒ$e{FUzs&3zHΨJȉb'V%I Q:s`ȉ 4Q0uȉբ={~Q$)K3ܾ]UQtJQVrcyENnڴ)$I٘Qlu $j[^:#\pxFUZ/oU #'VM>=$IQty%GFN>> U nq]dW1bC=KQUH#}3vtbH)FU{pڔX,Ī$IQoWo^ ;UI[q%]tY~}hI2$*hŤ $z*QQ--(9s8p ;$IiQ4Ss s#'V~my6FURvV}wȮZ\\\SS)ZΨ @3QS>ljU {'9Ī$Iɨ @Z۷jU]g}ݲ; %֯Xܹ$59*l UXƨJjkgQ ,&-IRZfT 3l,7@FUR֧۷~$)21vF~ϯ*Ъ9zܹ3$IQ 3uO kWGN9*IR3y~u"'V/mX@HFU»ZػWG~$)=2^^G||2QtOGC'V%IjBFU2Uh̼"'VeT%<4m>:qWݻCUKU`{ͻȉI_V_aaT%T<_nw.]TVV~$)u3VQZXkWһKb`T%UyKj,sbU/ʨ @6Ê%sy|  UISnݡC"'VGĪ$Iu3%%ENN@3UIkƏbwt-IRjeT {U5{ kWx j׮]7mZʨ @YzUw}KKe4eT%l{ȉxs -IRdT U,_8։YXȨJf'գnadW1bDMMMglIgT ;}; 4QL=]_~NJdT kUeg8 4Q ;.qWҥ /1[Ureg ]U<[Q߳%I QXYЦ[/5@Z0I]u-IRME~ϯ,zi}HqFU2آ%=zC?lK쌪pо]UsDv|eHeFU2ۆ#'Vvo~ۖ$)U ;9:ב=R|RQ՗"'VcXIIImIQ"^||>^mO-p&*o;]9Z\\\SS[ddTިxp@]0NuUL'%{޾}{wnIZ=*k߮9:u`g'V*YwWFNvyӦM%Ij݌pU6[zNX]`AnIZ1*KK)9 |*YT(9:z~$U2zb9"'VzkQ5iȉՁ޽;$I-Q<މUJ6[|ik׮ -IR gT{|בN Ur[^})rb5޴i8[˨ Re~|B1§۷}wȮ:l0'V%IQ\s^e¨ mTdWׯCKU =:.?*$QYryw.]lڴ)C$Iͨ M/(lSpO >dTD[z$Xl%IjVFUhWW=9:Oo IcTR}"? xȑNJ7*4S Lf`H*k䉱X,qW8p={B?KԔ|5_ljUNFU"V.tqj׮]}$5:*?>vi|UU0Y=zDNN>=Ӹ$I˨ -USCpb2QT(9z7Ԅ~ $Ue履ĉե{NJQU^ر}iNp.$I$Ym'O;mW]>'†Ç'X3J_҉Uͨ*I$IRlۚN3;÷'(3Jcͷ*cT$I$(;V\~^NJ#QU*֬\6j:T@3J$ITHO_8zgx~K%SFUؽ3rwɓ'߿?k=yͨ*I$IRqԟ]9xjm%MFUSxJۣ?$I$}{7~l]7n Ȩ*bӽn.qbԩSџGFUI$IJ_WAX=T=ĉL&cT$I$tZ_(qbxᛔTUnӧMUN{$I$I%պ/^8z 6<>KIQU*zwWWU6mZIQU*<u1eT$I$I}kO?Z5,߭ͨ*}CXmjjrbtU%I$IRW_8 X̨*HCXX0J$Iӱ~bf5S,)2JӐ'VwQgT$I$IoWk杶oZ7,)2J%Ց.5+񧀝X(zFUI$I}扵UswUsƯ 3?URk7tөS?0Z$I$EVW%N._to3JيIX5kʨ*I$I޵_1qbk󧿶;{QU*ٜX(FUI$It=>9c'-)6T yb%?#̨*I$Iμ]S]u͜O~uI%fTJ<'VJQU$I$UɋkC9ͨ*_::saT$I$Igۛ=˯(qby{υ[gTۆ8z_y~߉UDFUIC6;vD QU$I$HxNU[/i3Jz< p֌$I$i=yyIQU;tY0J$Igߋ+j}O?g/i2Jz^@ٳ{zz'ΔQU$I$lo >z޵5|F#3c'V;;;WΈQU$I$Fn6߿Ϩ* ==:wWMRׯ xwoɷk$I$I#s__?o|d͸ 7̑o]=Ѩ* ;]xKO7448 :W%J$I$I^[n>Hzr]ل j}}ovy}?5%I$Ih |Tݹ#qb#z4`h;6֎LJ$I$IK5/5 cW|b5FO c}$I$I'W}wfƏ|+B>?1Ţ*l;Ϗ;,%>{N -IRTkV.KRt: F6mjjJ쪋noڒ$EO8N;w~e'V%Iʭpshmmf61:::X}uߴ%Ix'vՆ'VJV:^`$q]kل c}}} @l6tĮzUWCߴ%IjO8F۶m[YY$Ց.5˨ [;uT'V%Iz76Y*od2NJHRѯ4b``qߴ%I{ߏhK[[[%3f8*I*gΡC*++'V>T7mIB~GL>׬\Ī$~_MMM]u q[,A \2Jsf_vے$Yѯ1sԩImOܒ$MO1 ,X kNJJwfX$fя0d˖-eee?PW$i~~(0]]]NJJsĉZ'V%I%R @AhnnvbUT ET*8G{¿~K4E 'V%IE\c @{7z'V%IZK @ sn|hpIα7y沲]K%I:XʡC*++'V߾5{$I.u؜8q‰UIR1P|A'V%IQ @ڲeKyyB/E̞=ۉUIRAPN8pB'V%I[[ @IpbUTE 'V%IZ @ qbUTE!O$]я'hƆNJg5#9$)Q @|buiNJĪ$)~-_ĮĪ$)~'lڴɉUIR~HTVV:*Iʷ_HL&Ī$)ߊ~ ɉUIR^0X$Oѯ" ͉UIR$rbUE.X$j/f$B 'V'M:ωUIw}d풤".3'$X~ >:geG.I*ʢ=ĉ>]T|E?z0LLމUIhe;#J[ttoxIRڶmɓsw~?KFC*++swX$Hѯt:]WW8fOB/300ؘUw4$p~`̟/$@~`uttTTT&%IX",\0wWMR2/I*4EK.MR[X$Uѯm۶M<9wWdƌ_ D/I*2uU'NS_%IQ;c!NՍ;݊ P/I1#]sn's$)~`L@e$moĉo h/IϢ.d.\RGW=^EZfҥT*wZ]tuo z/Iʫ+m۶ɓ'̟/$)~ ءCfΜ?e~ މ'ƝnƗ$Cl6ܜU;*I~ ;*I-u]QQ^t{vҗ$E4@d2j*ztC'ؗ$}l6t'V{ÿKƸG ז-['VT9x $i,~ >:iy۟}& $i̊~ ߥk&׬\_46E?DPj9*IPcOC_%IZdՋ.c+drwT*amK7I(@fK,qbUJg U{{{*U?W;*IWlSNUϟ2[ /I_(l}}} ,HX}tC')7ƆNJRq@hkk+++UT9x | $c/ /8wW : $IRE*qbuE@4(6CXm7a@qjmmMRٗ$Ig[Ekԩi^ر=|$U L&S[[MĪ$Vя SN555%N6gIҙ@IX~}Ǯ@E!P*:;;/])S>H޹JH:USԆ-{$~= 444$N.@4dOֲ]ꫮ'|8$ .5%3f8*IyXk׵ܢ (ilvɒ%w|KNJRV~T*VW=#HŨ "wW>mZ]S$)~'&NY,|M/}~o```ƆNJR`яVVV8W TE? :::'V'M:ωUI )M6jĉOo|2|\R+A֐'Vnc$Nѯ.'V_v(|e)]gggi>4HR)g$TjږA8Sl)qbato IE\?pv6o\^^ί>r@ IZ?pֺ'V/=wI*ʢ-çI*af7|4Edիpw!IES/=pXdƌ;gI*NkkkX(7l)JNKo9,|.I[lIX{IB v`utt$N^tNJҰ]F^:OX}tCÄ$b?fwqǸ/OTXE,wW{w/T@Esԩi: )$P!F]___]]]Z6ašw I*f'V4FBp`lڴ)qbꫮ> stream xOLi~'p3L7t 8=d I*Yf¤BfT&MԕI΢U)eղV]]FHA8x%808p|8>:Mׯ*?>V_߷=? p BtttRAj:pr:d⥼_Bw4[\\롣jSSSRDR  hN㥼й&ۉvy]]]sMhJ>}]Dj:dee+^/ sMb6򺻻v.@rT*.(t.I>Ofb1t.IX򺺺VWWCT*Fhm722R.Clnn&4imJCj&6n h.ohhH<h7ۙL&^kqc\@RhNWVVBjDGifii).oxx0t.Ikޭй&rydd$^KR sMNl׸Z4Y^^N(JsMvww򺺺666B塡\.WCG>SקQݬKylP(4) vysM*vy=zJ⥼ᣣй&tZ<hsb]^> hRV'''fffvFFFr\@Dй&B!./J---4j>.B]pT h/5ڸ1t.vyl]TV  hե]L&h:5 sMt711qtt:dmm-.o``P(4) }}}R^wwF\@r<22XczhgLJ:$ϧDй&{{{vytzmm-t.IZM}qV  hD)oxx\.4ꊗlP hrpp000/Rй&j5333@yi*򆆆vhdvwwCDt::Ф^?|0.orrR4im700ppp:d{{;KyݍCKyTٳgsMjd]t&+++]]]RPT h/uuu4)Jvy O>롣333RdZ  hN㥼й&;;;vysMxbܜvynZ卍4Y]]ꊗz{{vS(lݭ]χ$=y]^R h/eй&b?^KKKKsMNl755UBGϧRx)R)t.V]^OOf\@Rt\.WCG>S>|(MNN*@YXXH><< hd⥼й&r9./J-..4SSSvyj5t4r]^___P h/uuu4T*5sssz=t4ɓ'Or:dmm+^d2{{{sMlJB'NMMjD)oddD<h7|]V\@Bhנ]J2>>.Z4ORR^T hӣ]r<88XczhgjT799]\..P(4tй&@V>Lƴv]Dй&ryxxX<hsz}jj*Q)ˡM٬vynl]J\@ۙ<~X<h]]]R^& hR(6WTkljhgl788]|>]>U*vyZ-t4I.KRRPT hhݽ:ФX, KyTjqq1t.IVkm7==]VCG,ˡsM{zzVWWCkxqV  LVJFFFBG'4d2R^WWvyn[h7J:v./J4)###5=.ʉrh@x)P(4omSklۉJ:dnn.Q888/EE<<MX.R˛VDO)kRT]^T kwx]ދNlٳйBb-T>|.RXkNZeB:W0'ξ3%භ3vyǞWS R.oqq1thgZmjj*.o||N{YKkhϞ=K/s]Wh{{{vys]θ3%R.GFFkl=zTCGBѩVGv%N9Δ<Z:z{{ B\Omww]^>뒝:@{T*fggoM3Ͳ3%Ur:%P6R^& Bus|@*l6^KRˡs:RZ}AbÇoh0;;;͓vNW;lV^*:88}6Xv;e-"N8KxgB @x\ZNǷ=d|L&PŊxxՎ*B!9\W{}HX^^&^;ng5O>[ǛdN}jnn.gϞ|*oTX,}\.6Ur \uA.߉j+?y\-TV }[Hvnt:LL^eKwӥǻˢ`|& FUA3S]~z=*LGsq kyyӧuJqkhh(,N[+fwK uRjBxxꢖTX<.UmnnׯqkllLU0ᥫOijb{q!qSSSJ'eͅ>ӪjQIjww7Q|^m01\d2]k3: :`gg*s)yS:;;b u`|5q6&6uD: p=bx__*\No ::i0 8c~xz˙Jb5ގ-:Z++R) r7x/.h51]Ҳ x; w֓'O ] G'Ux/>!ω3O\MK7"1upmXK]!qc#G::M~g~Nٳ v: :&Ŧ:שV=|L%l6WVWeR#^|F$ in'-zzz^.>JMOOE\\\LW~W\7)>;[pss7Hܿ{}}nS'u.BVWWvs_B|mӧO'3wNV_ppU??h}KpjwIM1uPm;eE.񮳵] r.qO4i0 8c: ׬R iypo0 'N_Y/ˉq+*Wؤo0 1/LwѣG;;;PK/Bdp\<8xmS2Ǘ5\^^NT&nMOO{kPh)e;:V;:*?ivt?gRYyp;;;ś7R4h5%j\LRBpIzje9<ݩ?6zUkNl:@HQU'&i: ީHxWqmiMuvvsæ]vpp{ﵾ/--p{ħ o5I nRVS}ىR䩋x_S#ST_@k|G5:u&jbSOSk/2iqk;ǁ)lmmE_cϾl9 l?c1&im}.aD/4hiDUg F&&&?^www#rooo3ܡxi'^|Mf ^IӞrJg.B!!_[__O2r"HtwK^[_7jħ4x3W]7eaa!$EKKK{=S^˾61Cb?p744455R,kT'|j|&^ǧ.WawI+ݻ: ;Mq|[m/Zw~EST*{KWTNl#Ne2ӷ;.Mut|6N.IRWDz}ff%7^YܝC㹺_{l6w=i˲.ggg't4h_LzgJ}p7///..r<::]__=JL&?yRV'&&Z_d2/kzCpZ_{=@@\y"]]]jujj[|-s؏_R9qL'OjRzTwxxߟx윟צP(dRpm{׬V?h}yC.={ֺRyfffy+|޽_vypu*)+xo$Fhp J3&''ѣG rrFbٙKs"^N㱣/σBpL`k ZkɧR 600볼: nr<<<|"^J/4p|bWcl6:=zt|vv6Yw}71GZ:`ƷBkutt444tIջsz8xRIgz'OB_,,,7Y.CGkqki#Ϟ=;>Y;11mh{{7L /7n \\"^oooWY>881t׿9tvvF -vpp9ƖJT*uև&@۪?Z]^c{\#/..T*jyy޽{!W򕃃ϟc\;HPx).7.1d2E |>_\nٹ:\6>>>Najڇ~:裏kgffWىs}pWZ*BG+_9kmhh(z湋x q~Ĉ _Zhpr6=kmss3:7Mz=niWrJh}4l6UjZwwEx ap4F#?Zw+Jtpfgg8͎x B!EnE6-YYYx'''g٭vuu=V3\]fٟ"NollP(VbĚǏT*u:ޓ'OB\h c~u'RT!nwww}R>KKK/5LLL\nÌ|;psǷgr\SSS ]Jo``O|:N4}];Yͽpw?Rv|&"Cwyu:zJhT )9pYZ>:F( hTDx޾?sr:HZoAյw^b!_4} _H:z뭝&&&#T*.--=zhjjjllu'O\֙D׾G?Qhpĺ+zjZ*vwwgW}1&&&(pj}{Ï./^ϐuzz:zBF]YYg鉞T*]S|𚇦go ^luuU<F|h:<<|͕ٻNc{{.kZY~qtto}]ޏ~{[R )ommEƮ?i4N>9f?A={$rajj*HSZ]]w^kkpE={ow{[Dr\cnn.H}뭷/}Kp677Ej*Ldyy9T ;::zAkwhr7p6|{[MLLDa666&8O>3QwSTBGƫ룣fH###Qݰadkk _BKKb1t4n'OKKK/ʣA4p;R^:s[\\ś _uuuEBg8j}'|:7v744TBf}N}߷}8rddOOOؽ-"b1J588:t:]Sׇ{[DT\TX_D)W_.#s\DY]]5rp *vyI>śC}fqq= ٳ0ؘvyۋ&{[F񖖖BLof׸1H  6RVcwwAPI>nmmpկ&Jy[YY  vp=[+D B8V}ᇭ7Z B|ɓЉN,ˡ\]oo@0Qx[ms+t'Jy_Jbwww42myl6t+W*+|>t4Q|jWWW;FQCZǭmPpЉ^dgg':11:YYYw^kR:W.ǁO> %֢?Z/~ۡpvvv{[(|8׭RoF'Xs(J/rsssQf^ǭ&&&jt\ѐ/JݔSSSQ웒*꫉R{W(BGLOO{ V|Wb:@HPk󛛛p #\.:GkZ8]S<mss3e2t:tv:;;ռ~Pr'e2-CAVgqH>OӉR޻ᆱ]SՆ8T*E:@{)^ꫯ$X\\ vwwZ~E.YTx[8)̄ЦNloRś Н^磳h9to$JyX,CGdGGG୻ tsNdee%tvbT*CG ^LJm7no\n\Z@කB'\, 8vyqtt:jmm->T{[qn7|3Qd2{{{uB;e[yk|YnRկ~]jhwWR닆gݷ`jVv:vy= 7nlhh(vPX,F'8qnvy7-fO> rlooG'566: vbzP(pWT* &&&nƩE533:V.o&Jy~b1ER GBjڟٟogm\.ghdJRPinn.:qnW_}5Q.Ň^][|v[Ro:m⃮[tdd$:qnr1t4#gUW?ak>Zp-ttZ+++qױwy^fZmpp0_5F\B!t+Q.d2fb/Q:Mj}}=tLpU*uVpRwaoftccc '.{[FWVV ?(/mmpbOOO|QPWk~~>:߹qwMRFhmm733R8wK^[nVVVB')qxkc[u׆C|yZa:22n8wN˱޻BqщJqJo]??r&FJ?7n  Z-tnnn]ޣG'jCCC)Nu}*J:{b;q3@"R,so'Jy===wR|\433:u؈Nll,t>SVojpDW`jj*t4?nmG+p7-"O>.\8`}}^K~7n`;VDCt::T333uXZZ RoqgDZqf81,a||\{b:677ENLLС볻:~W^yecc#t4hgoVl6Dr9t.M^vy?'nV FcT*eA->_1t.?D)} @eqq1tvT*KdBJ km u!tDmjww7JCCCpU*7vy/:pw׊VСzt&''Cj~sKT=z:ptww׊ڻk533:Wn}}]޿(t4CCCh$Nr\tC: wy'Q  +fffC|>:Q_q&'ٳgAxD7ht677CZvvv&yq^ t4T*CE+pݶ/$Jyzxx:p U*xg%Sd2u/T*ZۡJZRVP7CV_q1~A}:p{LMMϞ= (Ju r\k> ▗cЉnݿ?tz__.ˡ7~WWW4fZ-t$GWoll,tBX_D)_vy ]]]C0KKK vQ]_+t4*B'yr\t|XCs766BU*__O^yǡmmww7NGa{[Pt%6tG}O> hSJ/6R)/"QA8W^y%Q?`H뉽-t4vvvzzzl6:g*<GRncacC$^1"D,x'Jd5)ِ4Yv3QXg1NFRb'kjI]rĖUU ]E_E_d{Ʃ#G"e+~~}MСCO \.tV.7/tZCR;^z)t4)>}:>b޽f[\fJ}|IÕJJ%tW(thh(tZ̉'۽{Rh@j5F 2+bzz:zW:gnnnHmV,CGZ^߻woSӧCjѻzСqhIJ宻J֭[ t4`M=SƛG }+Q;pvy!||pV }F'B9sfy!ž;|XY:-m޼ٳVg[СMkD Vԧ>t4`U8p ~ݟ T*>կ.okm&>G!˅NԆrvqh7gϞ+R:2Ν;Olxx٭bІ'Jy6mzBG.WBdK|r9tT(yhh(tSZݳgOφ\ՆB:Qۚ}@;{駗?j:p)ЉY C͝={vӦMRލ7X,CG.T>66:Qrѻ}qhKKK7tSN277]kZPmntt4zÏ?:ZYfbb"t4---m߾=-,,! {'Q>WTBG~z>22]SٳgCl6zBcR)t4Ż5;v,tNN\.@YZZG?([^  HT*d۷^jZqPccc_<+;tX3b1zl8tǏ_>Qʻ뮻jhrt+CuN777w'Jy^{Nܧ?ӡu|>*p~z /͇ LNNF. ~G].ϴwI- ~#ϧD)oppP?mXK󽽽ѵlPFGG:x?z`m,--m$GfffBJR^wwٳ'~ 6"l6T֓O>Օ=裵Z-t4h[_y'&&B'h~#>fff6mڔ(}]ZZ  ЩS̶hr9:L&txbwW;w.t4h+[l۷o7"ƧpdqjvyW:jcǎ:NYpB!:"###zgzzzռx#t4hyӡQX8pΜ9qD)鍊n*ˡ@ K̶rO&''Z‡?D)ogΜ  Zӧ-Ͷhq .ZC=Օ} ZB___t1w{M>.щ'˻{ TTv]F|P`ttfggCK@{W$Jy7pBh8q"t"~KttJR8pYwܙ(G@SrDD$5>DV^?#}[  ԩS-FFF̶h6r9:@L&tX1/vyCCCJ%t4h.bq˖-rǎ.MqcfCtܹ[&Jy]w]cph,jb ͩP(o VX=ܓ(_^  W|>:lzz::Lccc7]?رcc. irrґ ֭K򆇇jhƹsOwej3T8篿D)o۶mkRl߾= FFFU(BUVD) pt)LRgΜ w1882HDG-ɄkV۷/Q:-JRP\ a<=CZ-t4XI 'O U(722:333sW&Jy7|Rh2-&&&B'"LMMEn||] /:\be˖Ҷc[xtBX8tZ޽;χEbPE⾅>@ٿrvԩЉA H\߽p^:>}thp>:t(t".QtRB}+Qr;E޽uPMvyKKKdC Wжmnxwt:ZKk|䈎`8j>D)oЄ`;v,t".KPh8կ.oOj7@xꩧשN耎^۸qcwV*f[jС\1m:thboLڅh+Q|EooVa||<:ӡ@+jwvy'O  ո6 FWT*uСXё- @yvy_״!`K= 3<<YXyz{{}cj5t4:ȱcW"-L6n\ZUTZ.fffBG#48t: 6/BgVD)gu;X,g[d2sLcFt|Cvs-owhOKPcB) kiӦD)鍊n*JІWЉXy333! G\޹sgyfXYөT*^̡-(mV}s[.vhll6[TBbULNNFq _u6>ϻY˴dK:Ւc===:+2Qʻ[rhz><<:u*t(Vht1S.wڕ(В5l/q=} sxӆa^6xV u%y\s%f[,--Ūz!6~,)m;wjZZZߗeEjm8A*ʿ2Qʻ+^~hj###kT*:O<Օ9r$t.DB'bF}tt4tD/j:ĉf[t|>~ mٲ%QʻKRh4">"Q?99:tJr]w%Jy7n._󌃁Nϝ;:k*E χ}+_@ۿ`EL8N:l{Wx8x2r}ۢo~~>t(H: pnD)oӦMZh\z>88?kF\VF& t/\.رc:?OLLNDs) \/u떷˫j;MOOǛhsJL|L&(p R)t4ށ\|>-qQVƗG[r/_z{{bP4I]@z'ռz*t.~V ӡCѤr\NBV򆆆< gK{FK,chKbq˖-R޶m<t̼g-=nΆJr=$Jy===CGPsss}}}9l UcD \.gyfy|VY-|P4PYUW(D)oVtyl U\L& X ۶m. 8˅ND h\Oajz'JyTJ<Ֆ;<xll,t`9s&nJWv ٳC2&''= R/ʷKwl .xxB¨_җg}6t46mqСЉh1!=ipfff-kZPƲl8@`Νk[ZZ  U-[D'վСh=**JU*l6(mڴ7 $f[4>}:t(ZR:V9C=Օh3τb#GNDK**d2~y 455?-q&U:#NG+tm۶%JyW_}[o:Z >:VEK+Ʉjzw&JyTooBGX;|qN###f[JRCZUvy  1b틋CVfggUqꫯnڴ)Qʻ;[V۵kWt뛟vM:D)ooFhe޽^P6455qvPD);Nh+/1BUe655:>ouuu%yccc̙3X%k{>U233qD)@T  `4f۷oo_Сh[###b h7R^:~CG,j5FgT*ª7c`5}%Jy]]]7=}ęM2V[&[,@;~ӓٳVpўzDЉhse2L8@;wܕW^(]wuovhٳCCCj5t(\T`8@+7tSaÆ'\-[Dg۷Vh8@G=vyO>dhZ޽;:wR3g΄EG(  |>Ty'huȑЉSSS› ,bkI2Bh`rr2~rOkibb"Z{ӡgii#H~zPf3;;N3ծ]̶`-G˯P(tzg'x"t4_[\\NP[l)ˡCYFFFX,C:??ot9opp7e Z8Nf[T*m۶-Qʻϝ;:ÇND'd2W`:WjN֭[סرc3Ҿ}B'CE?t_{'ռz^tDf+JPtro::oW^yew뭷Jcǎ׷:jvv6Z~R͛1t4xmAPB!Z$jx Qw:穧 655ƉqYKOOOGGZ-t4=MOOO8{џ&&&ٸ,;[ܹS<`-[D-hњ, >r#vyZh@T*Il A[l8Gꊗ h{쉟aN>:l6-Kbzz:J%n?ctСl G&Vf,᭷ںuk7  mAjd2\Ɨo=QK?OCGZۣɎ;B_+J R|_^.g h%Zm׮]0GX +6lHܘw}Uрְw $χN&966:+m۶рfw#˅NISSS(6jڽޛ(mذA<1ۂU:55: '5˩q `= sss9ahhl YVwq `k6mJ>OU*р-^ZZ  ~Rl6:*j|mЇ>(]wuk^GT*u̙С]Fvdd$tU(Q[~Q-B'wE;66:Zwӓ=ZcA'8qD*hrqHy~vy{?NG~݆~*r\t'''CX;r#Hy7x#t4`U,--eh...jtt4Z|>t/})QzCVB!t(<33:@>vyjhI̶8|pDpqwqx뭷lْ(}+ˡ+=::j -'Q_GU*[n%Q۴iOрR,sm٬6Zt::@x}:t"\U=33:@@úu~򓟄GxDl_ ~Doz)I laۓd]Bh?ϯčyǗBGNm;vؘZ:@(J[nMΝ;:tjk׮hKͅ+q݉v6 T{'QKR/bhСFGGqzz:t"X1 |IT~a`MMMŷ|>-ﱱqZܦM;CW.X3O6ۂTrqZU\[뮻X,T*E[l DȧBhm=XWWW]CvVׇMgj||t799ݝ1 _BZ  X|mAl8*yGrhn9hV`KR8mRvmR^ook:gϦh 6d^Bhzvyǎ  A\5oW[+?t6tu%n7Ѡ5vPT*uС`hgqSTںukؽCp;=:hж*7߼]OѠ?~<W^PX8^oyѠg[޽l :Ts\8ozz:J%Jy### p!ۣdC011q:BXkm۶JѠhפRB:xOH+`T;#Q۸q/:4:rmq:O?]$r8x`D@XvH8?vyrRhɓ'-߯Mg* FfkF<:MX϶h _X^__okZ ڽb1t(frr2SSS[zzzռGyst={Wɓ'C'ǣP(B inn+\.\.hbb"Љ hGΆ;(]uU/CGUqlZ:688mR:<(uww VXbE&Qh_l4Ǐ/oghZm`` ZީTjff&t(h t:*nߪtM7ݤ]!ĉASh}fC,--|͉Rƍ_yLNNWu. l5FFFBBG}+.  .ٳgcB펱q8w~čyw_Z  .NTھ}{3bPDܪ D)o۶mBݻJΞ=:4hLMMVwuvy:\W,7>>|>:'x2_CwqȑCAL8\֭K^hZw\ F;X,ZXX[χIrl @ mCRJ֯_/QRB:4hYXIՕ=c:$<_ǎ W\6K6 vԩ 6$JywT*||YNMmvv6/ávy]w]T 577/Jh_Ph8j|&Q[nDcǎh)f2r:4hrqXE]?]klϞ=Eh\hLNNz73ww.--F/Љ5G'χT*7|soF+ T*Zxf[L8/~R^OO߄E;[XXd2ђf&ÅklhqX;=vy>vyiN@2pQepa:͹s6oޜ(zVk^mVE'N@J#HwWCGkUYkR^|fC㷡A)J`~3>ss˯Y)ԩS݆5;;mq֯_1K_R\qZ+f[bx8V,&JyЇCGk%\k:^V϶HCAKx8WVR/:Z˸Z\_HN ZU.d84~y\2 qmS;zh| mcll,MӡD^y 6$n?^VCGkvx gΜ϶h-r. \Jvy[n]XXlС5Qb84z>44(g55`|m/^/ˡФo.o|%t&uƗʉ'B'WqhjM7}.oNMOOW"JRCU*}R^&y뭷BGk.Sk"޹sz{{ahg @ <@J|hMkqÿm⭒r}-CZxJic5:::-?ӓ=n:jqˬjWvehrqh%oUW]];Z^_ЉrhMNN@YZZ߂uޖ-[~"T| Nftt4bpi񮮮D>t:na-`4vVfffBU:u*J%n?߱Gg[d2-`5dhqha\sMw-tfUիs^JnU_^jڪ'?D)+|뭷BG[kR{l X%Z-^06ݿKzzzε:wcccA*^f>֭KT:]^OڵRmkff&nzV Rt7&Jy;wl_E\x񣩤kѢM799:mg?W\1ﳟlV mkozz:~9=!hqhgr}{_߿:ڪh:^X_65FLmqhsZ3L~m_/1ۢo~~>t(l6}b1t:7D??o3ۯmqЉhqG/~ 6$nԧ>UVCG[1mVǛz*t" r9}L&t:;m۶52کSTt̶5V,})C'0qW^y%t6ulС h@ՕhwAw|5Zk׮Фs΅gzz:چcccйxčy.Eݻ7~PC'N499m\.:Zz뭉R޵^[oֹ-8:th'NMMzG|>:W'϶jCAڿh?||tޟٟY-[R)t(\8;;:[ozw߭]H̶HR@%h*joN[cccرcAՆ'hB=XJ>tv- jhKf2q?WйSbg!hWfqw/_frhmT*e2޾}w\:>ZoO6mo&̶uhoGꊗ򺻻CjǏY.ڛ\.t ʆ 7} _롣yЉ8|p='''C U,򖖖BGkIg[jС8p@C|8pjڽ޻]#tS.-^\\  -6L8pr\]^ַBjZmxx8 С b:\W_}5N'n'> mqЉwS'7ZW\޾}{њZ|fþ} t:: \Z622(]qњ\___^ T*Сwmq`<݉vy?йNbEoo[дΞ=q`ekqj5t&w1J>}:t"w* ц VL\ޱcGwCGk \.ΘmMnjj*ڰJj{I6l,t-FGGͶ&711q`;v'.8t`bbǍ:pAk4mOOyBmrr2ڵ؂\.ny'OR%ӟn $ڿt:tX#bq֭R'>J:jYXX϶ހкg NK7߼]믿:ʫVLRgϞ  Ν`8~ᮮx);N\+ltt4=:pq|GFFBovyxmz%f[:t(t"?~<@bKn6 wt:V\h#7~:S~_v3Lrn$t(R8p SSS@`_W{gBZmhh(z!Tjff&t(۷/΅B!t̙3ׯOܘr/ <%Jy7xcrG XB!{ W^(_G?Qh1==mhk:t(thv/bPv^.  t:i.D'''Cj0U\޲eKl h'?~x8*@e˖_:z/9Q[n߮}:f=n[X/uռ{l-3Ļ7۷l h?L&J%th=o5\]l___ۦX Z-|8Ъ*ߞ(]}տ/W}hRƮZ׾x)?*p]F}hh(thy?ׯ_1OOVa].W̶6=0CvP*nݚ(tM+۶>JEmm,>@g͛7TbmfͶá@[7h>=:11qȑ?PVl-:Ƣ]q?Æ 7}_\ lR9r@iH&rRO۷/m`dd$gΜ b.I\#Gl߾}y.!Jyr/ X3p+s\(qWVۿVH㏇~eZd2_ZZ vy;wLT?+ouww//]}վCj:6m]|>6;w?|ܹ/X R)۷o:BZЇ>aÆ;swֽ矛%:lC#+r֭[~;>RU]wq={6^.w^T*krr2@oݺ‹xr_ <@jBE݌w^|m& 5::mǏܹsW򓟼:wKC&`5v} B8)?}%k/w^___/Uf>77:tÇG_oK5`/9rHr9tWK5 jZt8A܏\bml6:t 733m:yf :HVw.sg2/XE~r@goݺboƋ}~ :ٳgsQe2Rb(^@[c؅> ovh̄gvv6JE_7lpwiwE6o jՒf[⩧Wzzz>~_^۹sg'^Bww/%VE_BNTO̝uWxN3===ׯOpϞ=CilhN 5߿whϝ;WTH_C&`ŔJhgq9rd˖-ZKRXZZ:j6lK/}9JvwcRLNNe2C-,,,t`_UOOO]׿йKe3;;:|ͫ*qcvyr]\*BVXZ[믿 L&µZ-t`U'? Pd2_Lk?ŶudƦc%5Z'e4ZM7V7SZga[o+VwP7Q`MW6*bBl^fd,j l `$\b&ĢeL`2 ^b?e`EAxv;~.t8E}ݗw\p!t4lnn.~(l8o~3QRsAHZ-~"|8{7mڔhT*Y(/~*~vh@\R:('>nߡ6>>?$}n  Nt^` CCCyԧvhpld`h###*/χW#N `}+_ITy7ntsmܸ1}_ JTkR zd2*h .X,kuzz:tno߾=Qmݺ??CGKB\W'200Ymذg lJm֡_|oLl{B.třdBghy睉*^XX ^n㕙Bw~wUޖ-[^zйz, B8}O{ꩧBbX8,/|!X*JC fJTysqy299/r:@뿆z4>>J:@oy'7lYuX,ƋVs^z饭[&6=Cq=onn.t^nW~%Q|WCGcr[\G}t鸼SNźXuL&t^/wйXvr\8/t\<`\+^Njmtt4tաj}CJTy~ BGcmWZX `5)J*oƍ?OCb T*2`ygnĸ`ټysbc#<:LZOX ` jZ{UG>f:TxJR8k֗De˖s:LNN+gqֲ}{USO=:@TT8k /t\ݻCxTq־{'Q=yBGwxq֋??HTytڸ<~l6/F::rر 6tVy ^N7tuٳ7|sbcooGQ:=h#͆EQ}Dw… +jZ6|8ף>nǡsj0 B8ډ'Q\W.%Q*BXkpp0Q}4.o?v{dd$Q/ŋCG#R/r:FgqF֭Bj:ozgۻwخC|>^Z-t\x|g?nCGd2h4E?D744tѸ~:~,BaÆ~:t.FL&:og?٦MйXqZ-|>tƫ9' l6CGcUv qx{Q=C*o|BGc^Jq\йX^>t7ޘؘ{{sJR|r8\;#Q}#iZ B|j8\JTygϞ e[C*=UW:ˣCF8\-[$6=CQ5i MӡpZwߝmꫯ՛f6 {DiӦйJZ-|>tO?aÆ*'QTX,C`9K7|sbcg?v:W\.wT*2kUwl6CG Ƿorr2tV׿й\ccc񽛚 ORD7>>:P(wZ p­ޚqy/ŷ^ʊh*oxxŋV2L|F8\t\Cv|t8\?Nڼys{C{fCznD~jFZ90tx衇U/:oT*)Ç:}й?`N:iӦƼGy$t.^7>>ߔq^f*oddĸb|G*J8~6Qmٲeff&tumtt4ӡ.9.'kr+jM7ݔؘgϞй֩t:߅v:=ns=*Bh⍈:^466t\й֑z_\.:=? :?? kh8W^yoNl{j*J|͋b8(|ʻ_{ָ*裏wVy6m:~x\kx|'''C`uJεfu~T8/^NTyqy+P(Z*&[^p!t&W^S*U^*zgCZS2L|yF8JNڸqcW:n㫚NC`x;Dя~jŗ4͆7::n喳gφεjzqX  tVy?=z4tURX,q-[$6=ásVr9R)t֎Vu=${aa!tgbb"߇Z/~1Q  s2R)r9t֠G.Oε UqX~mܸ11CZ5||jZ$}}W*?Q}BG[2L|F$W)V~8Q陙йz] D.?ܰaCg1йzWшU& {lhTyW>W_}5tSVKT(†:Nz-JN8:Wo))Ja,KXunƍ彅,~6̵tqz<U… v[bcnW^y%thW Ʉs5u\b'`wt\߇u]s\8( g?qy?p\OZOP(^{L&>ZѮBDZC%͛7 sCZO<700SOεJR|r9tsۛ޼ysbcޗйVPXϴRsu*`]i6{U]wݵV4kZ8֛MTy[l keBǹ.N<U^йY:On={nJl?:ײi4ye2q^*4*o_^}іAVO*χ:=W+_JKk`\^ZϨP(:=⩧ڰaCb\7йI\OT*Z=+r-y<@E]DuEQ|$Qvmt\^TϢ\.l KTy7n%ޢdWYY|fffni.{mZnO{pXf6*_r>_#jwmqIiJ%>rX\sΫޏ(5eIR.ÖJe9& wÌǜpĉT*u=rncccڝ?~hhhYJET̙3P(Vrz5۷/Wm۶_K\.m?>VΝoh׮]2(oxx8>TXU+QW8x>}:N\h%"Xшd`T*cV4L||>ˉ'vرrUۯtPbwڵB']RJTycccWsj߾}V`߷oвWyrcLLL.b ^{^]MEǏY*/˵Zy;wUVWL` 8}]۽{bgy8;;'klXLX9rm_k۶mͭl6ǯ0Rӧ%(v}֒Vul6{uU޶mJjs{*;wFQACFFF:2w5?-߿o:9 Y˯>O\Qf}|-\fybw)}}}}ѾR'O>KX (*### kOWחMTyv2T*oWm;Kycf5;;{t:&5|mx{>-XFTڼysw7sy%_oKRϟ}N6}K_(v_Ilߜ7>>l` j﮽xo|U?d2Oَ֠/]y{ 'Xk<_m7СC ֚oj{7xkxjt:Q\yWydBMccc=+ߌ}sxiffm{LRz=522 5.ě>яFQT`ͪjTwNnyb }6f8p;׷p9/ᮻ:|)BKmaÆ[v5J% n6]R6랰+СCoR{.,,Wv{ll,;;wX^fsrr2]d2ry}j`8c><11qС3gμLOOg2*/Nh&vMLLt丼B`\*oxxӡs]Μ9Jr\@Fcǎ{l(BGEсUΝ;fh@Ǐ'eٙй.Νfys]ݻUޞ={vh@ÇR*/7й.CCCU^&1.zM^rU^*:tPEojb11.oΝf3t4ˑ#GFFFB8q"1.oxxxzz:t.ĸ#Gthll,1.oϞ=V+t4qylvvv6t.KZcB4;v$c(BGEUBh@FFFΟ?:efff۶mU^* l6wܙx}vh(ڻwo۱cG R.tgdfggCfĸCtiZKٳ'7EQT*Uqyk=:88Ym۶mzz:t.˹sFFF^jvڕ⿇)R*odddnn.t4ӧ:aל;w{Rԑ#GBZbg9r$NwVywu׹sBLOOwVyJ%t.h=@i{ITywn6]r\^ rm۶%sL\p?U endstream endobj 7 0 obj 42805 endobj 9 0 obj <> stream x[ێ6 }s:H݀@vviM[ yeK,Yc l!1 ?.F#a /¸_3A?obb>ys1gWeXx ^j^x 2>٧0L7v|) 9~aÏ\e rpgy::꜐lZlq}Z<>C 4o[^ ڸq=cBf`.B({%.-,n]Pū!i32׭kN1Y01Y2Ec[–iLq&Lȱ1Cf@!1qcs˰\rYJ2^vR(I3oj#)pa9}-NܨmK0RMĺR%Q,*|*ͩY]p)sj&oF\9R?I*r퐀PR"CcV*4$XVTٓQ.ţu<] ꪘPg+%-&q$eF؁$FrTdb7zIv;u$y=j d&AGspR\e|‘kTG66wh@x8LP:iZکĈĕ59Ñ=2v_ڄ2z&)W<+GS 륯%޷:[p[}R.ý']Kmưc3OPȴ3Lt3Nkz9LԘVͪӕӘ_?Gt1 sa/)7RIѕt8S6I-fkXNQ|쩎RH{Lڲ#ū,Zv%֯+G757`K6` dl 3fԾFxbVeەSyF;r@Q{N#V/slĚo*}Azن׎O#ޖ ePj|m"җN'pD=#U.٭l3dY-AXݟ Iw*5H2UՊRV}%pu"ܓ ݱm'fm+hK/}m+UKݷ$dm]oSWۊks5+͕.n^;:Q tSK_s-dk}k-WoE3)~L0~pgzia3-cymG endstream endobj 10 0 obj 1882 endobj 12 0 obj <> stream x}{`Tܻ{J_֪|RV6b@+T,*XҊU̽yh}93s̙sΜsf7 PošmCNO'!,Bܲ)"=v!g}Po"قm n[2#BFi;t\5vPv3;#,v!;[ys~T mt7CЖG%C)iOw82v!tI z>D(0 j&R No0 j? ,dG(x{u&R墼.G/a7iiz{nEȂ>'H*FVԀvH=tZct 碕Yl [~}}LbǀLTqjm&VaU(Fw-c/2+_ѭJ<Ȁz;4GɚX'XUE]sπ-uע+a ٣jEG ԉv_6iP~Ŀįs'RUX%9 rZ V(/?RD g6p^L$GWbGk<]~~Gj >;I?b(F2oA#4i,⪹>.ʿ6vb 4B/8dpNgܳǰ*=(/r݄݇Q;:z.0JdEH'䒹\{=ouki1]KƂ h=v]`@!C/wo?7[Gsv _7ooŷx?wy >? GlA\K֒vג`'GQ9INq"7+=p}Q> ]A?JT%2T:lUR_Z}zTdTpp DKS0"pThwnq %/o~#YAn&rTWQZs8L } t5wAO\tw#s1|.GpG'Қyh!y$\_/Uk'|3_cm?܍}p*:A8[0.$3P6$L*Ά6s_/$C'l|.B14zH10&AL59Uc9+5E.$8v?^g8L"3<&HN!w69+{ol)yeVR~Q _A (9N~/ćNεO#nPU8rHHy~鯪I7.G\ ~܁p?GQsz/^q@z`T8?}5h*at" <tBz6-07W9Y̬(/+-Q\4 ?/vgef9%GzZ=f$'%&Mi5ZspnknjYh]sʼnJ M:m_ɘ L,JЬ\%EqI⥍Xj|kCRFf6:wS@ms ]]^.ze6f"H\ES\5Q2k5v)/7[\5'j2T͖[F 륡܃7h]7_Mt ֭Z.?alqsuvnhs``=qꨓ>%s7P)Z >݊4_&E9˚A!)Q9yCJ4rF+&MP"X?bIsGrD,!Q11t -'D 8itF#PZ ^MfE[A y@I*StI#P_+=LcDAjpz<.jP$8Kr7Q ćaZIzFtF-ua+6EI398>l}IQM?P>3f8 -w-QhVd[pNK/S ,|&Hj ,nō2jфFNd9F vehL53(f:47*6ϗM:+ )Fc,VMNS=}9s_>5\<0;gl..i@4mK] mhl`x&X6As\x!ޱ}"ܠv44CYYWYe*_x ZSEt>)jArKfd\&\;{W?x]pץѮn5)B^]2};Ӧ+<2GcVݠ|_~}Jg^1dFp2] su.AM)G9)ƴi2}k%eU Ykszo3Nݙ&CLẙO [Vl:kݠQ.g6zA|BN|xKͼǣX*-K,pnݒs7;jDQZԍH=ڃhi%"6GE"XwYierJ<w3kNʳ0!\d䩶,raytb!5nh|;Akl_b`bf&zT&bC E4tg3Y%3JE=KIdNJTe̮.IJ}>U<|ߓ |_$۶ycm\M$K+_y%[oٖ?3=˝"f_xy/mw9Iu g}-n_+k5cJy. $*wWgs["*Ɋ֪$YeRQL6l;6e?O[«LCCK<>wie66[4hwFfmuzMTO\)I=M S^RUI29TN8ykr2g㒑HPz&\21ޠ3nR2HƭKOgԈ<_YNxLQ8Ȏ4֧gyŶQh%39,&KIV%'D]5S'΄N'.h-8sf TyjFk9Zp:/} 3|536@ ee\} |n˽,ړ]c%\pMw?16Xuə)hm>K xܜ}mʷפdS▙ A#AF( 18J*}b TaSl`Z|#,V4i2Nxj CgN}t |K) P+0U)8NsTk@x#u8"*B 9Eiԃ8jY6 QZZ;Yo;d*X3w-Z0N2`UdOM;+xыjϜ=;nݛ`ȟ]9N)ʸ%BY a{@'}[7kԒ^_|CxOxOO?E]IÚ|eznWX A2p ȷI8-۩9r4?XjP=I'ք- 9R3YpP-c~A<헏3}k|pmx0oc{xСiM [qi[+T@&=  +08Łkw_m846YǥjRtdKJp&f@vY?i3Ch!"'bqJm ͛ ?~;UQk5 -3dirwTI ,a^ ,BzE15iA:%gk FQ#ijNkTjw^ElF+Ibk͸mӤd Oyqlթ3yZj%R+[xlZ'K!Q7F4ӨNM `faEݐ^aQ--B tb--k d:y)YRbW-n }vKcgE3)Fo^{ozm;}}unӦk ꏽj}?̴/$..Lݜ*\iioLOߖ N>E' Gq<>s\8PkKѓsFɀ a][Ocf0AB 5 s;q'CD N 4lQ./kOyNlb@i5f%r:˔, N,&8»N[paKyYa[^p]7b$OB64va\ TR_SFAD 8e ΍p#11|UXA»pɐ|Zzo_:)GEg΀0k)6ʜKU%¦cC3 YBf+.^H(.*AgՊllmR[{RwBCXtXcԈ6bm)q)+ 6C"kMTOOX^"o%r:nR DU $%fӣO Yc}sL(͉FdE(MfI[F3#f0W9@[9(0'xк$kXfb떮0 FּADMBlF^4ue6bخAcRmD5 \&$ULIWq>v\e)Zx=!2z==r-oL͟vE񧟽6x-W9"JGn\?<76ͬg=ҭv=hqq:l8,ko0 ]F2 -gLh3Td0bbdž8pp4J"JE҅Dp{IS8fMLCjh'zM!IEO'"5B%qc2RICN=!w^-~N9XQNƳn3Vc7-n]g;c[]*ԸlB>swNξ]m~[<C)~ߌ?c{:&3/էm;lS""u⢛,JNk_!tZZ}|NWՖQQ{15ԢZR׫OUj/YJũT HCosHa ҂D,BI𜖕gLfe["l|kWw-n} cg_۱ \vEաk7bI:ɢbPO$X,>]U#(ywM =f>Jv^|qq"G82|:xgVwkz .lƉS>"Շol)#\/햫 Hkq|ݴ>˒]8cfdt)7P}=WoV̟g>?Ѫr{m^U^BN6Bqָ<+gӧޖv}!Ŧ.$ 9nNuhš\R^EUmЭ8l^LyLF${Q/9N@~^Џ쥐rܔLI26K}B;`),֬`YfE E>J^ScnCAU d'R rJyΙո z%e9)M;xr'.Ny/?^ѹƺ}#83Rg* =G>Ž Fku.ߺ }B@^"̥\aU&Q#fzS.ʒ_Mx-פם/{NwM}_zqU#y;x<`Ex&S"΃!Ǟ*'cGD&SjZǫxt:'ӐW}e1a(O -Fᴇɯa2CB_b7{xo5Y,^v{^ _L,Q}R͉)W<͡)˲Uc$\GY/W)ՃbdUySnz tcrsc'o[oJ1v|dCÅk-Eu8{ ~=e.s7.X:vSZnQ1+XȢ8 Nz&7p:r^#Bt:熙KA5ԙc'p} GYǞ[kƦa?JRGOjccQď8pΏ%Iu5 <jOh%W@1~..zScP< T間 #U`X GYօ VCGy$Yf@bKAVyC)O*07%ENJ~Sf*0L٩ߔ~SkO*075_ :oQ`ЯsfGʉS`UziP`lfp{= {ϑ1 `yGY T& rDODGٞgDQ`kep2;_omt]úa)c |z?ep\Ire:ߚ۠<`p3wP_0_P`QO'*0?k'`?/l_y L7Q8NߩQ3T`W>A jC~Ҁu.(KۍBӧ CP _R*LBaNoz$m`VCZPBh<[+~he#')33:;Q% )NFP`^οAsWP71\A O|5%Aod"?p" E ճgznc>TG}l&du)O=Xfq~bݠby6:lINb#Af˸#b9hqaR[x˯XlA&VfRZS-Egљ"deKk?[EцF}5PnFK^{_vDHb/EfI%LEe5iʙ+^!8TfoRV *;O*0)6Iv+; 2^֚jYi61{c$ I6(0_))t\flWOOXPa;;w=tÓMe 1t%[dآ?ȴpz͔ٓw2պEOfE3jT;|t֤F__:FI 5wBԶ[&vl[b66.2OdSQ|ŠMzOyiď.ge)6:iiLZ=Ob|.3鸟I"Aϐ,?8fCo"ffB$.ϰz|::b Ve6 k<?B70e &΅R G%K#\x^kg93J`'ڤFO&ŗF򚑯̸N&l:GD9m=O^㧐J,0)zJyFufӐ\IV}b~aEaŇ|T+gI%˘s8a *οsIŮ)m|]IZSHhJ;b%Edz}L6qp Io؏TT<,T(1yϵѤ},`{s/sJ&FZV=I9̲^@yr>45RrS:_e=ҚX[/q R s9 (Ylb8z?BM r nfBV)%EurEҒ|*:v@˟^O H5!_k=I˺8R[~iR,/:)o~) mRk \ߕ+ɂh,;CK-`K`? Zp7,G7-RP!E7v&V:vi"}H0-%&nФL! #0D'9bTc@+, Z{p#0UԐt@30Bo酅dx_;\jtDMN_qH]W==cWK’a3~Z)N`(g,30f`c/e_j-ET$B #`0'4;t Ғ+ =~ {5@i#0%L{Bݝ݌Z~{$3/S1΂HgGAgZ?x>7':7,^Ұ`ꪆKKKJ-]VvQx]:.5*b`va=cL_3[9(_ׇjRLktfFaZg5(.@% D@m I  S